2005-04-17 02:20:36 +04:00
/*
* Copyright ( C ) 1999 - 2002 Andre Hedrick < andre @ linux - ide . org >
2007-05-06 00:03:50 +04:00
* Copyright ( C ) 2007 MontaVista Software , Inc . < source @ mvista . com >
2005-04-17 02:20:36 +04:00
*
*/
# include <linux/module.h>
# include <linux/types.h>
# include <linux/pci.h>
# include <linux/ide.h>
# include <linux/init.h>
# include <asm/io.h>
2008-07-25 00:53:32 +04:00
# define DRV_NAME "aec62xx"
2005-04-17 02:20:36 +04:00
struct chipset_bus_clock_list_entry {
u8 xfer_speed ;
u8 chipset_settings ;
u8 ultra_settings ;
} ;
2006-06-28 15:27:02 +04:00
static const struct chipset_bus_clock_list_entry aec6xxx_33_base [ ] = {
2005-04-17 02:20:36 +04:00
{ XFER_UDMA_6 , 0x31 , 0x07 } ,
{ XFER_UDMA_5 , 0x31 , 0x06 } ,
{ XFER_UDMA_4 , 0x31 , 0x05 } ,
{ XFER_UDMA_3 , 0x31 , 0x04 } ,
{ XFER_UDMA_2 , 0x31 , 0x03 } ,
{ XFER_UDMA_1 , 0x31 , 0x02 } ,
{ XFER_UDMA_0 , 0x31 , 0x01 } ,
{ XFER_MW_DMA_2 , 0x31 , 0x00 } ,
{ XFER_MW_DMA_1 , 0x31 , 0x00 } ,
{ XFER_MW_DMA_0 , 0x0a , 0x00 } ,
{ XFER_PIO_4 , 0x31 , 0x00 } ,
{ XFER_PIO_3 , 0x33 , 0x00 } ,
{ XFER_PIO_2 , 0x08 , 0x00 } ,
{ XFER_PIO_1 , 0x0a , 0x00 } ,
{ XFER_PIO_0 , 0x00 , 0x00 } ,
{ 0 , 0x00 , 0x00 }
} ;
2006-06-28 15:27:02 +04:00
static const struct chipset_bus_clock_list_entry aec6xxx_34_base [ ] = {
2005-04-17 02:20:36 +04:00
{ XFER_UDMA_6 , 0x41 , 0x06 } ,
{ XFER_UDMA_5 , 0x41 , 0x05 } ,
{ XFER_UDMA_4 , 0x41 , 0x04 } ,
{ XFER_UDMA_3 , 0x41 , 0x03 } ,
{ XFER_UDMA_2 , 0x41 , 0x02 } ,
{ XFER_UDMA_1 , 0x41 , 0x01 } ,
{ XFER_UDMA_0 , 0x41 , 0x01 } ,
{ XFER_MW_DMA_2 , 0x41 , 0x00 } ,
{ XFER_MW_DMA_1 , 0x42 , 0x00 } ,
{ XFER_MW_DMA_0 , 0x7a , 0x00 } ,
{ XFER_PIO_4 , 0x41 , 0x00 } ,
{ XFER_PIO_3 , 0x43 , 0x00 } ,
{ XFER_PIO_2 , 0x78 , 0x00 } ,
{ XFER_PIO_1 , 0x7a , 0x00 } ,
{ XFER_PIO_0 , 0x70 , 0x00 } ,
{ 0 , 0x00 , 0x00 }
} ;
/*
* TO DO : active tuning and correction of cards without a bios .
*/
static u8 pci_bus_clock_list ( u8 speed , struct chipset_bus_clock_list_entry * chipset_table )
{
for ( ; chipset_table - > xfer_speed ; chipset_table + + )
if ( chipset_table - > xfer_speed = = speed ) {
return chipset_table - > chipset_settings ;
}
return chipset_table - > chipset_settings ;
}
static u8 pci_bus_clock_list_ultra ( u8 speed , struct chipset_bus_clock_list_entry * chipset_table )
{
for ( ; chipset_table - > xfer_speed ; chipset_table + + )
if ( chipset_table - > xfer_speed = = speed ) {
return chipset_table - > ultra_settings ;
}
return chipset_table - > ultra_settings ;
}
2010-01-19 12:45:29 +03:00
static void aec6210_set_mode ( ide_hwif_t * hwif , ide_drive_t * drive )
2005-04-17 02:20:36 +04:00
{
2008-02-02 01:09:31 +03:00
struct pci_dev * dev = to_pci_dev ( hwif - > dev ) ;
2008-07-25 00:53:15 +04:00
struct ide_host * host = pci_get_drvdata ( dev ) ;
struct chipset_bus_clock_list_entry * bus_clock = host - > host_priv ;
2005-04-17 02:20:36 +04:00
u16 d_conf = 0 ;
u8 ultra = 0 , ultra_conf = 0 ;
u8 tmp0 = 0 , tmp1 = 0 , tmp2 = 0 ;
2010-01-19 12:45:29 +03:00
const u8 speed = drive - > dma_mode ;
2005-04-17 02:20:36 +04:00
unsigned long flags ;
local_irq_save ( flags ) ;
/* 0x40|(2*drive->dn): Active, 0x41|(2*drive->dn): Recovery */
pci_read_config_word ( dev , 0x40 | ( 2 * drive - > dn ) , & d_conf ) ;
2008-07-25 00:53:15 +04:00
tmp0 = pci_bus_clock_list ( speed , bus_clock ) ;
2005-04-17 02:20:36 +04:00
d_conf = ( ( tmp0 & 0xf0 ) < < 4 ) | ( tmp0 & 0xf ) ;
pci_write_config_word ( dev , 0x40 | ( 2 * drive - > dn ) , d_conf ) ;
tmp1 = 0x00 ;
tmp2 = 0x00 ;
pci_read_config_byte ( dev , 0x54 , & ultra ) ;
tmp1 = ( ( 0x00 < < ( 2 * drive - > dn ) ) | ( ultra & ~ ( 3 < < ( 2 * drive - > dn ) ) ) ) ;
2008-07-25 00:53:15 +04:00
ultra_conf = pci_bus_clock_list_ultra ( speed , bus_clock ) ;
2005-04-17 02:20:36 +04:00
tmp2 = ( ( ultra_conf < < ( 2 * drive - > dn ) ) | ( tmp1 & ~ ( 3 < < ( 2 * drive - > dn ) ) ) ) ;
pci_write_config_byte ( dev , 0x54 , tmp2 ) ;
local_irq_restore ( flags ) ;
}
2010-01-19 12:45:29 +03:00
static void aec6260_set_mode ( ide_hwif_t * hwif , ide_drive_t * drive )
2005-04-17 02:20:36 +04:00
{
2008-02-02 01:09:31 +03:00
struct pci_dev * dev = to_pci_dev ( hwif - > dev ) ;
2008-07-25 00:53:15 +04:00
struct ide_host * host = pci_get_drvdata ( dev ) ;
struct chipset_bus_clock_list_entry * bus_clock = host - > host_priv ;
2008-10-13 23:39:40 +04:00
u8 unit = drive - > dn & 1 ;
2005-04-17 02:20:36 +04:00
u8 tmp1 = 0 , tmp2 = 0 ;
u8 ultra = 0 , drive_conf = 0 , ultra_conf = 0 ;
2010-01-19 12:45:29 +03:00
const u8 speed = drive - > dma_mode ;
2005-04-17 02:20:36 +04:00
unsigned long flags ;
local_irq_save ( flags ) ;
/* high 4-bits: Active, low 4-bits: Recovery */
pci_read_config_byte ( dev , 0x40 | drive - > dn , & drive_conf ) ;
2008-07-25 00:53:15 +04:00
drive_conf = pci_bus_clock_list ( speed , bus_clock ) ;
2005-04-17 02:20:36 +04:00
pci_write_config_byte ( dev , 0x40 | drive - > dn , drive_conf ) ;
pci_read_config_byte ( dev , ( 0x44 | hwif - > channel ) , & ultra ) ;
tmp1 = ( ( 0x00 < < ( 4 * unit ) ) | ( ultra & ~ ( 7 < < ( 4 * unit ) ) ) ) ;
2008-07-25 00:53:15 +04:00
ultra_conf = pci_bus_clock_list_ultra ( speed , bus_clock ) ;
2005-04-17 02:20:36 +04:00
tmp2 = ( ( ultra_conf < < ( 4 * unit ) ) | ( tmp1 & ~ ( 7 < < ( 4 * unit ) ) ) ) ;
pci_write_config_byte ( dev , ( 0x44 | hwif - > channel ) , tmp2 ) ;
local_irq_restore ( flags ) ;
}
2010-01-19 12:44:41 +03:00
static void aec_set_pio_mode ( ide_hwif_t * hwif , ide_drive_t * drive )
2005-04-17 02:20:36 +04:00
{
2010-01-19 12:44:41 +03:00
drive - > dma_mode = drive - > pio_mode ;
2010-01-19 12:45:29 +03:00
hwif - > port_ops - > set_dma_mode ( hwif , drive ) ;
2005-04-17 02:20:36 +04:00
}
2009-03-25 01:22:53 +03:00
static int init_chipset_aec62xx ( struct pci_dev * dev )
2005-04-17 02:20:36 +04:00
{
2006-02-03 14:03:48 +03:00
/* These are necessary to get AEC6280 Macintosh cards to work */
if ( ( dev - > device = = PCI_DEVICE_ID_ARTOP_ATP865 ) | |
( dev - > device = = PCI_DEVICE_ID_ARTOP_ATP865R ) ) {
u8 reg49h = 0 , reg4ah = 0 ;
/* Clear reset and test bits. */
pci_read_config_byte ( dev , 0x49 , & reg49h ) ;
pci_write_config_byte ( dev , 0x49 , reg49h & ~ 0x30 ) ;
/* Enable chip interrupt output. */
pci_read_config_byte ( dev , 0x4a , & reg4ah ) ;
pci_write_config_byte ( dev , 0x4a , reg4ah & ~ 0x01 ) ;
/* Enable burst mode. */
pci_read_config_byte ( dev , 0x4a , & reg4ah ) ;
pci_write_config_byte ( dev , 0x4a , reg4ah | 0x80 ) ;
}
2009-03-25 01:22:53 +03:00
return 0 ;
2005-04-17 02:20:36 +04:00
}
2008-08-05 20:17:04 +04:00
static u8 atp86x_cable_detect ( ide_hwif_t * hwif )
2008-02-02 21:56:31 +03:00
{
struct pci_dev * dev = to_pci_dev ( hwif - > dev ) ;
u8 ata66 = 0 , mask = hwif - > channel ? 0x02 : 0x01 ;
pci_read_config_byte ( dev , 0x49 , & ata66 ) ;
return ( ata66 & mask ) ? ATA_CBL_PATA40 : ATA_CBL_PATA80 ;
}
2008-04-27 00:25:14 +04:00
static const struct ide_port_ops atp850_port_ops = {
. set_pio_mode = aec_set_pio_mode ,
. set_dma_mode = aec6210_set_mode ,
} ;
2005-04-17 02:20:36 +04:00
2008-04-27 00:25:14 +04:00
static const struct ide_port_ops atp86x_port_ops = {
. set_pio_mode = aec_set_pio_mode ,
. set_dma_mode = aec6260_set_mode ,
. cable_detect = atp86x_cable_detect ,
} ;
2005-04-17 02:20:36 +04:00
2007-10-20 02:32:34 +04:00
static const struct ide_port_info aec62xx_chipsets [ ] __devinitdata = {
2008-07-25 00:53:32 +04:00
{ /* 0: AEC6210 */
. name = DRV_NAME ,
2005-04-17 02:20:36 +04:00
. init_chipset = init_chipset_aec62xx ,
. enablebits = { { 0x4a , 0x02 , 0x02 } , { 0x4a , 0x04 , 0x04 } } ,
2008-04-27 00:25:14 +04:00
. port_ops = & atp850_port_ops ,
2007-10-19 02:30:10 +04:00
. host_flags = IDE_HFLAG_SERIALIZE |
IDE_HFLAG_NO_ATAPI_DMA |
2008-02-02 01:09:30 +03:00
IDE_HFLAG_NO_DSC |
2007-10-19 02:30:10 +04:00
IDE_HFLAG_OFF_BOARD ,
2007-07-20 03:11:59 +04:00
. pio_mask = ATA_PIO4 ,
2007-10-19 02:30:07 +04:00
. mwdma_mask = ATA_MWDMA2 ,
. udma_mask = ATA_UDMA2 ,
2008-07-25 00:53:32 +04:00
} ,
{ /* 1: AEC6260 */
. name = DRV_NAME ,
2005-04-17 02:20:36 +04:00
. init_chipset = init_chipset_aec62xx ,
2008-04-27 00:25:14 +04:00
. port_ops = & atp86x_port_ops ,
2007-10-19 02:30:06 +04:00
. host_flags = IDE_HFLAG_NO_ATAPI_DMA | IDE_HFLAG_NO_AUTODMA |
IDE_HFLAG_OFF_BOARD ,
2007-07-20 03:11:59 +04:00
. pio_mask = ATA_PIO4 ,
2007-10-19 02:30:07 +04:00
. mwdma_mask = ATA_MWDMA2 ,
. udma_mask = ATA_UDMA4 ,
2008-07-25 00:53:32 +04:00
} ,
{ /* 2: AEC6260R */
. name = DRV_NAME ,
2005-04-17 02:20:36 +04:00
. init_chipset = init_chipset_aec62xx ,
. enablebits = { { 0x4a , 0x02 , 0x02 } , { 0x4a , 0x04 , 0x04 } } ,
2008-04-27 00:25:14 +04:00
. port_ops = & atp86x_port_ops ,
2008-01-26 00:17:18 +03:00
. host_flags = IDE_HFLAG_NO_ATAPI_DMA |
2008-04-26 19:36:35 +04:00
IDE_HFLAG_NON_BOOTABLE ,
2007-07-20 03:11:59 +04:00
. pio_mask = ATA_PIO4 ,
2007-10-19 02:30:07 +04:00
. mwdma_mask = ATA_MWDMA2 ,
. udma_mask = ATA_UDMA4 ,
2008-07-25 00:53:32 +04:00
} ,
{ /* 3: AEC6280 */
. name = DRV_NAME ,
2005-04-17 02:20:36 +04:00
. init_chipset = init_chipset_aec62xx ,
2008-04-27 00:25:14 +04:00
. port_ops = & atp86x_port_ops ,
2008-01-26 00:17:18 +03:00
. host_flags = IDE_HFLAG_NO_ATAPI_DMA |
IDE_HFLAG_OFF_BOARD ,
2007-07-20 03:11:59 +04:00
. pio_mask = ATA_PIO4 ,
2007-10-19 02:30:07 +04:00
. mwdma_mask = ATA_MWDMA2 ,
. udma_mask = ATA_UDMA5 ,
2008-07-25 00:53:32 +04:00
} ,
{ /* 4: AEC6280R */
. name = DRV_NAME ,
2005-04-17 02:20:36 +04:00
. init_chipset = init_chipset_aec62xx ,
. enablebits = { { 0x4a , 0x02 , 0x02 } , { 0x4a , 0x04 , 0x04 } } ,
2008-04-27 00:25:14 +04:00
. port_ops = & atp86x_port_ops ,
2008-01-26 00:17:18 +03:00
. host_flags = IDE_HFLAG_NO_ATAPI_DMA |
IDE_HFLAG_OFF_BOARD ,
2007-07-20 03:11:59 +04:00
. pio_mask = ATA_PIO4 ,
2007-10-19 02:30:07 +04:00
. mwdma_mask = ATA_MWDMA2 ,
. udma_mask = ATA_UDMA5 ,
2005-04-17 02:20:36 +04:00
}
} ;
/**
* aec62xx_init_one - called when a AEC is found
* @ dev : the aec62xx device
* @ id : the matching pci id
*
* Called when the PCI registration layer ( or the IDE initialization )
* finds a device matching our IDE device tables .
2007-07-10 01:17:56 +04:00
*
* NOTE : since we ' re going to modify the ' name ' field for AEC - 6 [ 26 ] 80 [ R ]
2007-10-20 02:32:34 +04:00
* chips , pass a local copy of ' struct ide_port_info ' down the call chain .
2005-04-17 02:20:36 +04:00
*/
2007-10-20 02:32:34 +04:00
2005-04-17 02:20:36 +04:00
static int __devinit aec62xx_init_one ( struct pci_dev * dev , const struct pci_device_id * id )
{
2008-07-25 00:53:15 +04:00
const struct chipset_bus_clock_list_entry * bus_clock ;
2007-10-20 02:32:34 +04:00
struct ide_port_info d ;
2007-10-19 02:30:08 +04:00
u8 idx = id - > driver_data ;
2008-07-25 00:53:15 +04:00
int bus_speed = ide_pci_clk ? ide_pci_clk : 33 ;
2007-11-27 23:35:53 +03:00
int err ;
2008-07-25 00:53:15 +04:00
if ( bus_speed < = 33 )
bus_clock = aec6xxx_33_base ;
else
bus_clock = aec6xxx_34_base ;
2007-11-27 23:35:53 +03:00
err = pci_enable_device ( dev ) ;
if ( err )
return err ;
2007-10-19 02:30:08 +04:00
d = aec62xx_chipsets [ idx ] ;
if ( idx = = 3 | | idx = = 4 ) {
unsigned long dma_base = pci_resource_start ( dev , 4 ) ;
if ( inb ( dma_base + 2 ) & 0x10 ) {
2008-07-25 00:53:32 +04:00
printk ( KERN_INFO DRV_NAME " %s: AEC6880%s card detected "
" \n " , pci_name ( dev ) , ( idx = = 4 ) ? " R " : " " ) ;
2007-10-19 02:30:08 +04:00
d . udma_mask = ATA_UDMA6 ;
}
}
2005-04-17 02:20:36 +04:00
2008-07-25 00:53:15 +04:00
err = ide_pci_init_one ( dev , & d , ( void * ) bus_clock ) ;
2007-11-27 23:35:53 +03:00
if ( err )
pci_disable_device ( dev ) ;
return err ;
2005-04-17 02:20:36 +04:00
}
2008-07-25 00:53:19 +04:00
static void __devexit aec62xx_remove ( struct pci_dev * dev )
{
ide_pci_remove ( dev ) ;
pci_disable_device ( dev ) ;
}
2007-10-17 00:29:56 +04:00
static const struct pci_device_id aec62xx_pci_tbl [ ] = {
{ PCI_VDEVICE ( ARTOP , PCI_DEVICE_ID_ARTOP_ATP850UF ) , 0 } ,
{ PCI_VDEVICE ( ARTOP , PCI_DEVICE_ID_ARTOP_ATP860 ) , 1 } ,
{ PCI_VDEVICE ( ARTOP , PCI_DEVICE_ID_ARTOP_ATP860R ) , 2 } ,
{ PCI_VDEVICE ( ARTOP , PCI_DEVICE_ID_ARTOP_ATP865 ) , 3 } ,
{ PCI_VDEVICE ( ARTOP , PCI_DEVICE_ID_ARTOP_ATP865R ) , 4 } ,
2005-04-17 02:20:36 +04:00
{ 0 , } ,
} ;
MODULE_DEVICE_TABLE ( pci , aec62xx_pci_tbl ) ;
2008-10-13 23:39:41 +04:00
static struct pci_driver aec62xx_pci_driver = {
2005-04-17 02:20:36 +04:00
. name = " AEC62xx_IDE " ,
. id_table = aec62xx_pci_tbl ,
. probe = aec62xx_init_one ,
2008-08-18 23:40:03 +04:00
. remove = __devexit_p ( aec62xx_remove ) ,
2008-10-11 00:39:32 +04:00
. suspend = ide_pci_suspend ,
. resume = ide_pci_resume ,
2005-04-17 02:20:36 +04:00
} ;
2007-01-27 15:46:56 +03:00
static int __init aec62xx_ide_init ( void )
2005-04-17 02:20:36 +04:00
{
2008-10-13 23:39:41 +04:00
return ide_pci_register_driver ( & aec62xx_pci_driver ) ;
2005-04-17 02:20:36 +04:00
}
2008-07-25 00:53:19 +04:00
static void __exit aec62xx_ide_exit ( void )
{
2008-10-13 23:39:41 +04:00
pci_unregister_driver ( & aec62xx_pci_driver ) ;
2008-07-25 00:53:19 +04:00
}
2005-04-17 02:20:36 +04:00
module_init ( aec62xx_ide_init ) ;
2008-07-25 00:53:19 +04:00
module_exit ( aec62xx_ide_exit ) ;
2005-04-17 02:20:36 +04:00
MODULE_AUTHOR ( " Andre Hedrick " ) ;
MODULE_DESCRIPTION ( " PCI driver module for ARTOP AEC62xx IDE " ) ;
MODULE_LICENSE ( " GPL " ) ;