2005-04-17 02:20:36 +04:00
/*
* Copyright ( C ) 2000 - 2002 Andre Hedrick < andre @ linux - ide . org >
2007-03-03 19:48:53 +03:00
* Copyright ( C ) 2006 - 2007 MontaVista Software , Inc . < source @ mvista . com >
2005-04-17 02:20:36 +04:00
*
2006-12-30 03:49:26 +03:00
* This is a look - alike variation of the ICH0 PIIX4 Ultra - 66 ,
2005-04-17 02:20:36 +04:00
* but this keeps the ISA - Bridge and slots alive .
*
*/
# include <linux/types.h>
# include <linux/module.h>
# include <linux/kernel.h>
# include <linux/pci.h>
# include <linux/ide.h>
# include <linux/init.h>
2008-07-25 00:53:32 +04:00
# define DRV_NAME "slc90e66"
2007-10-20 02:32:35 +04:00
static DEFINE_SPINLOCK ( slc90e66_lock ) ;
2010-01-19 12:44:41 +03:00
static void slc90e66_set_pio_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 ) ;
2007-02-07 20:18:34 +03:00
int is_slave = drive - > dn & 1 ;
2005-04-17 02:20:36 +04:00
int master_port = hwif - > channel ? 0x42 : 0x40 ;
int slave_port = 0x44 ;
unsigned long flags ;
u16 master_data ;
u8 slave_data ;
2008-04-26 19:36:39 +04:00
int control = 0 ;
2010-01-19 12:44:41 +03:00
const u8 pio = drive - > pio_mode - XFER_PIO_0 ;
2007-02-07 20:18:34 +03:00
/* ISP RTC */
2008-04-26 19:36:39 +04:00
static const u8 timings [ ] [ 2 ] = {
2007-02-07 20:18:34 +03:00
{ 0 , 0 } ,
{ 0 , 0 } ,
{ 1 , 0 } ,
{ 2 , 1 } ,
{ 2 , 3 } , } ;
2005-04-17 02:20:36 +04:00
2007-10-20 02:32:35 +04:00
spin_lock_irqsave ( & slc90e66_lock , flags ) ;
2005-04-17 02:20:36 +04:00
pci_read_config_word ( dev , master_port , & master_data ) ;
2007-02-07 20:18:34 +03:00
if ( pio > 1 )
control | = 1 ; /* Programmable timing on */
if ( drive - > media = = ide_disk )
control | = 4 ; /* Prefetch, post write */
2009-06-15 20:52:53 +04:00
if ( ide_pio_need_iordy ( drive , pio ) )
2007-02-07 20:18:34 +03:00
control | = 2 ; /* IORDY */
2005-04-17 02:20:36 +04:00
if ( is_slave ) {
2007-02-07 20:18:34 +03:00
master_data | = 0x4000 ;
master_data & = ~ 0x0070 ;
if ( pio > 1 ) {
2007-03-03 19:48:53 +03:00
/* Set PPE, IE and TIME */
master_data | = control < < 4 ;
2007-02-07 20:18:34 +03:00
}
2005-04-17 02:20:36 +04:00
pci_read_config_byte ( dev , slave_port , & slave_data ) ;
2007-03-03 19:48:53 +03:00
slave_data & = hwif - > channel ? 0x0f : 0xf0 ;
slave_data | = ( ( timings [ pio ] [ 0 ] < < 2 ) | timings [ pio ] [ 1 ] ) < <
( hwif - > channel ? 4 : 0 ) ;
2005-04-17 02:20:36 +04:00
} else {
2007-02-07 20:18:34 +03:00
master_data & = ~ 0x3307 ;
if ( pio > 1 ) {
2005-04-17 02:20:36 +04:00
/* enable PPE, IE and TIME */
2007-03-03 19:48:53 +03:00
master_data | = control ;
2007-02-07 20:18:34 +03:00
}
2007-03-03 19:48:53 +03:00
master_data | = ( timings [ pio ] [ 0 ] < < 12 ) | ( timings [ pio ] [ 1 ] < < 8 ) ;
2005-04-17 02:20:36 +04:00
}
pci_write_config_word ( dev , master_port , master_data ) ;
if ( is_slave )
pci_write_config_byte ( dev , slave_port , slave_data ) ;
2007-10-20 02:32:35 +04:00
spin_unlock_irqrestore ( & slc90e66_lock , flags ) ;
2005-04-17 02:20:36 +04:00
}
2010-01-19 12:45:29 +03:00
static void slc90e66_set_dma_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 ) ;
2005-04-17 02:20:36 +04:00
u8 maslave = hwif - > channel ? 0x42 : 0x40 ;
int sitre = 0 , a_speed = 7 < < ( drive - > dn * 4 ) ;
int u_speed = 0 , u_flag = 1 < < drive - > dn ;
u16 reg4042 , reg44 , reg48 , reg4a ;
2010-01-19 12:45:29 +03:00
const u8 speed = drive - > dma_mode ;
2005-04-17 02:20:36 +04:00
pci_read_config_word ( dev , maslave , & reg4042 ) ;
sitre = ( reg4042 & 0x4000 ) ? 1 : 0 ;
pci_read_config_word ( dev , 0x44 , & reg44 ) ;
pci_read_config_word ( dev , 0x48 , & reg48 ) ;
pci_read_config_word ( dev , 0x4a , & reg4a ) ;
if ( speed > = XFER_UDMA_0 ) {
2008-01-26 00:17:18 +03:00
u_speed = ( speed - XFER_UDMA_0 ) < < ( drive - > dn * 4 ) ;
2005-04-17 02:20:36 +04:00
if ( ! ( reg48 & u_flag ) )
pci_write_config_word ( dev , 0x48 , reg48 | u_flag ) ;
2009-11-30 11:55:18 +03:00
if ( ( reg4a & a_speed ) ! = u_speed ) {
2005-04-17 02:20:36 +04:00
pci_write_config_word ( dev , 0x4a , reg4a & ~ a_speed ) ;
pci_read_config_word ( dev , 0x4a , & reg4a ) ;
pci_write_config_word ( dev , 0x4a , reg4a | u_speed ) ;
}
} else {
2007-10-17 00:29:54 +04:00
const u8 mwdma_to_pio [ ] = { 0 , 3 , 4 } ;
2005-04-17 02:20:36 +04:00
if ( reg48 & u_flag )
pci_write_config_word ( dev , 0x48 , reg48 & ~ u_flag ) ;
if ( reg4a & a_speed )
pci_write_config_word ( dev , 0x4a , reg4a & ~ a_speed ) ;
2007-10-17 00:29:54 +04:00
if ( speed > = XFER_MW_DMA_0 )
2010-01-19 12:44:41 +03:00
drive - > pio_mode =
mwdma_to_pio [ speed - XFER_MW_DMA_0 ] + XFER_PIO_0 ;
2007-10-17 00:29:54 +04:00
else
2010-01-19 12:44:41 +03:00
drive - > pio_mode = XFER_PIO_2 ; /* for SWDMA2 */
2005-04-17 02:20:36 +04:00
2010-01-19 12:44:41 +03:00
slc90e66_set_pio_mode ( hwif , drive ) ;
2007-10-17 00:29:56 +04:00
}
2005-04-17 02:20:36 +04:00
}
2008-08-05 20:17:04 +04:00
static u8 slc90e66_cable_detect ( ide_hwif_t * hwif )
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-02-02 21:56:31 +03:00
u8 reg47 = 0 , mask = hwif - > channel ? 0x01 : 0x02 ;
2005-04-17 02:20:36 +04:00
2008-02-02 01:09:31 +03:00
pci_read_config_byte ( dev , 0x47 , & reg47 ) ;
2005-04-17 02:20:36 +04:00
2008-02-02 21:56:31 +03:00
/* bit[0(1)]: 0:80, 1:40 */
return ( reg47 & mask ) ? ATA_CBL_PATA40 : ATA_CBL_PATA80 ;
}
2008-04-27 00:25:14 +04:00
static const struct ide_port_ops slc90e66_port_ops = {
. set_pio_mode = slc90e66_set_pio_mode ,
. set_dma_mode = slc90e66_set_dma_mode ,
. cable_detect = slc90e66_cable_detect ,
} ;
2005-04-17 02:20:36 +04:00
2012-12-22 01:21:03 +04:00
static const struct ide_port_info slc90e66_chipset = {
2008-07-25 00:53:32 +04:00
. name = DRV_NAME ,
2008-04-26 19:36:39 +04:00
. enablebits = { { 0x41 , 0x80 , 0x80 } , { 0x43 , 0x80 , 0x80 } } ,
2008-04-27 00:25:14 +04:00
. port_ops = & slc90e66_port_ops ,
2007-07-20 03:11:59 +04:00
. pio_mask = ATA_PIO4 ,
2007-10-19 02:30:07 +04:00
. swdma_mask = ATA_SWDMA2_ONLY ,
. mwdma_mask = ATA_MWDMA12_ONLY ,
. udma_mask = ATA_UDMA4 ,
2005-04-17 02:20:36 +04:00
} ;
2012-12-22 01:21:03 +04:00
static int slc90e66_init_one ( struct pci_dev * dev ,
const struct pci_device_id * id )
2005-04-17 02:20:36 +04:00
{
2008-07-25 00:53:14 +04:00
return ide_pci_init_one ( dev , & slc90e66_chipset , NULL ) ;
2005-04-17 02:20:36 +04:00
}
2007-10-17 00:29:56 +04:00
static const struct pci_device_id slc90e66_pci_tbl [ ] = {
{ PCI_VDEVICE ( EFAR , PCI_DEVICE_ID_EFAR_SLC90E66_1 ) , 0 } ,
2005-04-17 02:20:36 +04:00
{ 0 , } ,
} ;
MODULE_DEVICE_TABLE ( pci , slc90e66_pci_tbl ) ;
2008-10-13 23:39:41 +04:00
static struct pci_driver slc90e66_pci_driver = {
2005-04-17 02:20:36 +04:00
. name = " SLC90e66_IDE " ,
. id_table = slc90e66_pci_tbl ,
. probe = slc90e66_init_one ,
2008-07-25 00:53:25 +04:00
. remove = ide_pci_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 slc90e66_ide_init ( void )
2005-04-17 02:20:36 +04:00
{
2008-10-13 23:39:41 +04:00
return ide_pci_register_driver ( & slc90e66_pci_driver ) ;
2005-04-17 02:20:36 +04:00
}
2008-07-25 00:53:25 +04:00
static void __exit slc90e66_ide_exit ( void )
{
2008-10-13 23:39:41 +04:00
pci_unregister_driver ( & slc90e66_pci_driver ) ;
2008-07-25 00:53:25 +04:00
}
2005-04-17 02:20:36 +04:00
module_init ( slc90e66_ide_init ) ;
2008-07-25 00:53:25 +04:00
module_exit ( slc90e66_ide_exit ) ;
2005-04-17 02:20:36 +04:00
MODULE_AUTHOR ( " Andre Hedrick " ) ;
MODULE_DESCRIPTION ( " PCI driver module for SLC90E66 IDE " ) ;
MODULE_LICENSE ( " GPL " ) ;