2005-04-17 02:20:36 +04:00
/*
* Copyright ( C ) 1998 - 2002 Andre Hedrick < andre @ linux - ide . org >
2009-06-07 15:52:50 +04:00
* Copyright ( C ) 2006 - 2007 , 2009 MontaVista Software , Inc .
2010-01-18 10:19:14 +03:00
* Copyright ( C ) 2007 - 2010 Bartlomiej Zolnierkiewicz
2005-04-17 02:20:36 +04:00
*
* Portions Copyright ( C ) 1999 Promise Technology , Inc .
* Author : Frank Tiernan ( frankt @ promise . com )
* Released under terms of General Public License
*/
# include <linux/types.h>
# include <linux/module.h>
# include <linux/kernel.h>
# include <linux/delay.h>
# include <linux/blkdev.h>
# include <linux/pci.h>
# include <linux/init.h>
# include <linux/ide.h>
# include <asm/io.h>
2008-07-25 00:53:32 +04:00
# define DRV_NAME "pdc202xx_old"
2010-01-19 12:45:29 +03:00
static void pdc202xx_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 ) ;
2005-04-17 02:20:36 +04:00
u8 drive_pci = 0x60 + ( drive - > dn < < 2 ) ;
2010-01-19 12:45:29 +03:00
const u8 speed = drive - > dma_mode ;
2005-04-17 02:20:36 +04:00
2007-05-16 02:51:41 +04:00
u8 AP = 0 , BP = 0 , CP = 0 ;
2005-04-17 02:20:36 +04:00
u8 TA = 0 , TB = 0 , TC = 0 ;
2007-05-16 02:51:41 +04:00
pci_read_config_byte ( dev , drive_pci , & AP ) ;
pci_read_config_byte ( dev , drive_pci + 1 , & BP ) ;
pci_read_config_byte ( dev , drive_pci + 2 , & CP ) ;
2005-04-17 02:20:36 +04:00
switch ( speed ) {
case XFER_UDMA_5 :
case XFER_UDMA_4 : TB = 0x20 ; TC = 0x01 ; break ;
case XFER_UDMA_2 : TB = 0x20 ; TC = 0x01 ; break ;
case XFER_UDMA_3 :
case XFER_UDMA_1 : TB = 0x40 ; TC = 0x02 ; break ;
case XFER_UDMA_0 :
case XFER_MW_DMA_2 : TB = 0x60 ; TC = 0x03 ; break ;
case XFER_MW_DMA_1 : TB = 0x60 ; TC = 0x04 ; break ;
2007-05-16 02:51:41 +04:00
case XFER_MW_DMA_0 : TB = 0xE0 ; TC = 0x0F ; break ;
2005-04-17 02:20:36 +04:00
case XFER_PIO_4 : TA = 0x01 ; TB = 0x04 ; break ;
case XFER_PIO_3 : TA = 0x02 ; TB = 0x06 ; break ;
case XFER_PIO_2 : TA = 0x03 ; TB = 0x08 ; break ;
case XFER_PIO_1 : TA = 0x05 ; TB = 0x0C ; break ;
case XFER_PIO_0 :
default : TA = 0x09 ; TB = 0x13 ; break ;
}
if ( speed < XFER_SW_DMA_0 ) {
2007-05-16 02:51:41 +04:00
/*
* preserve SYNC_INT / ERDDY_EN bits while clearing
* Prefetch_EN / IORDY_EN / PA [ 3 : 0 ] bits of register A
*/
AP & = ~ 0x3f ;
2009-06-15 20:52:53 +04:00
if ( ide_pio_need_iordy ( drive , speed - XFER_PIO_0 ) )
2007-05-16 02:51:41 +04:00
AP | = 0x20 ; /* set IORDY_EN bit */
if ( drive - > media = = ide_disk )
AP | = 0x10 ; /* set Prefetch_EN bit */
/* clear PB[4:0] bits of register B */
BP & = ~ 0x1f ;
pci_write_config_byte ( dev , drive_pci , AP | TA ) ;
pci_write_config_byte ( dev , drive_pci + 1 , BP | TB ) ;
2005-04-17 02:20:36 +04:00
} else {
2007-05-16 02:51:41 +04:00
/* clear MB[2:0] bits of register B */
BP & = ~ 0xe0 ;
/* clear MC[3:0] bits of register C */
CP & = ~ 0x0f ;
pci_write_config_byte ( dev , drive_pci + 1 , BP | TB ) ;
pci_write_config_byte ( dev , drive_pci + 2 , CP | TC ) ;
2005-04-17 02:20:36 +04:00
}
}
2010-01-19 12:44:41 +03:00
static void pdc202xx_set_pio_mode ( ide_hwif_t * hwif , ide_drive_t * drive )
2005-04-17 02:20:36 +04:00
{
2010-01-19 12:45:29 +03:00
drive - > dma_mode = drive - > pio_mode ;
pdc202xx_set_mode ( hwif , drive ) ;
2005-04-17 02:20:36 +04:00
}
2009-06-15 20:52:59 +04:00
static int pdc202xx_test_irq ( ide_hwif_t * hwif )
{
struct pci_dev * dev = to_pci_dev ( hwif - > dev ) ;
unsigned long high_16 = pci_resource_start ( dev , 4 ) ;
u8 sc1d = inb ( high_16 + 0x1d ) ;
if ( hwif - > channel ) {
/*
* bit 7 : error , bit 6 : interrupting ,
* bit 5 : FIFO full , bit 4 : FIFO empty
*/
2010-04-14 09:52:59 +04:00
return ( sc1d & 0x40 ) ? 1 : 0 ;
2009-06-15 20:52:59 +04:00
} else {
/*
* bit 3 : error , bit 2 : interrupting ,
* bit 1 : FIFO full , bit 0 : FIFO empty
*/
2010-04-14 09:52:59 +04:00
return ( sc1d & 0x04 ) ? 1 : 0 ;
2009-06-15 20:52:59 +04:00
}
}
2008-08-05 20:17:04 +04:00
static u8 pdc2026x_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:38 +03:00
u16 CIS , mask = hwif - > channel ? ( 1 < < 11 ) : ( 1 < < 10 ) ;
2007-07-10 01:17:58 +04:00
2008-02-02 01:09:31 +03:00
pci_read_config_word ( dev , 0x50 , & CIS ) ;
2007-07-10 01:17:58 +04:00
return ( CIS & mask ) ? ATA_CBL_PATA40 : ATA_CBL_PATA80 ;
2005-04-17 02:20:36 +04:00
}
/*
* Set the control register to use the 66 MHz system
* clock for UDMA 3 / 4 / 5 mode operation when necessary .
*
2007-05-16 02:51:41 +04:00
* FIXME : this register is shared by both channels , some locking is needed
*
2005-04-17 02:20:36 +04:00
* It may also be possible to leave the 66 MHz clock on
* and readjust the timing parameters .
*/
static void pdc_old_enable_66MHz_clock ( ide_hwif_t * hwif )
{
2008-01-26 00:17:05 +03:00
unsigned long clock_reg = hwif - > extra_base + 0x01 ;
2007-02-17 04:40:25 +03:00
u8 clock = inb ( clock_reg ) ;
2005-04-17 02:20:36 +04:00
2007-02-17 04:40:25 +03:00
outb ( clock | ( hwif - > channel ? 0x08 : 0x02 ) , clock_reg ) ;
2005-04-17 02:20:36 +04:00
}
static void pdc_old_disable_66MHz_clock ( ide_hwif_t * hwif )
{
2008-01-26 00:17:05 +03:00
unsigned long clock_reg = hwif - > extra_base + 0x01 ;
2007-02-17 04:40:25 +03:00
u8 clock = inb ( clock_reg ) ;
2005-04-17 02:20:36 +04:00
2007-02-17 04:40:25 +03:00
outb ( clock & ~ ( hwif - > channel ? 0x08 : 0x02 ) , clock_reg ) ;
2005-04-17 02:20:36 +04:00
}
2010-01-18 10:19:14 +03:00
static void pdc2026x_init_hwif ( ide_hwif_t * hwif )
{
pdc_old_disable_66MHz_clock ( hwif ) ;
}
2008-04-27 00:25:24 +04:00
static void pdc202xx_dma_start ( ide_drive_t * drive )
2005-04-17 02:20:36 +04:00
{
if ( drive - > current_speed > XFER_UDMA_2 )
pdc_old_enable_66MHz_clock ( drive - > hwif ) ;
2008-10-13 23:39:36 +04:00
if ( drive - > media ! = ide_disk | | ( drive - > dev_flags & IDE_DFLAG_LBA48 ) ) {
2009-01-06 19:20:52 +03:00
ide_hwif_t * hwif = drive - > hwif ;
2009-01-06 19:20:50 +03:00
struct request * rq = hwif - > rq ;
2008-01-26 00:17:05 +03:00
unsigned long high_16 = hwif - > extra_base - 16 ;
2005-04-17 02:20:36 +04:00
unsigned long atapi_reg = high_16 + ( hwif - > channel ? 0x24 : 0x20 ) ;
u32 word_count = 0 ;
2007-02-17 04:40:25 +03:00
u8 clock = inb ( high_16 + 0x11 ) ;
2005-04-17 02:20:36 +04:00
2007-02-17 04:40:25 +03:00
outb ( clock | ( hwif - > channel ? 0x08 : 0x02 ) , high_16 + 0x11 ) ;
2009-05-07 17:24:40 +04:00
word_count = ( blk_rq_sectors ( rq ) < < 8 ) ;
2005-04-17 02:20:36 +04:00
word_count = ( rq_data_dir ( rq ) = = READ ) ?
word_count | 0x05000000 :
word_count | 0x06000000 ;
2007-02-17 04:40:25 +03:00
outl ( word_count , atapi_reg ) ;
2005-04-17 02:20:36 +04:00
}
ide_dma_start ( drive ) ;
}
2008-04-27 00:25:24 +04:00
static int pdc202xx_dma_end ( ide_drive_t * drive )
2005-04-17 02:20:36 +04:00
{
2008-10-13 23:39:36 +04:00
if ( drive - > media ! = ide_disk | | ( drive - > dev_flags & IDE_DFLAG_LBA48 ) ) {
2009-01-06 19:20:52 +03:00
ide_hwif_t * hwif = drive - > hwif ;
2008-01-26 00:17:05 +03:00
unsigned long high_16 = hwif - > extra_base - 16 ;
2005-04-17 02:20:36 +04:00
unsigned long atapi_reg = high_16 + ( hwif - > channel ? 0x24 : 0x20 ) ;
u8 clock = 0 ;
2007-02-17 04:40:25 +03:00
outl ( 0 , atapi_reg ) ; /* zero out extra */
clock = inb ( high_16 + 0x11 ) ;
outb ( clock & ~ ( hwif - > channel ? 0x08 : 0x02 ) , high_16 + 0x11 ) ;
2005-04-17 02:20:36 +04:00
}
if ( drive - > current_speed > XFER_UDMA_2 )
pdc_old_disable_66MHz_clock ( drive - > hwif ) ;
2008-10-13 23:39:46 +04:00
return ide_dma_end ( drive ) ;
2005-04-17 02:20:36 +04:00
}
2009-03-25 01:22:53 +03:00
static int init_chipset_pdc202xx ( struct pci_dev * dev )
2005-04-17 02:20:36 +04:00
{
2008-04-27 00:25:21 +04:00
unsigned long dmabase = pci_resource_start ( dev , 4 ) ;
2005-04-17 02:20:36 +04:00
u8 udma_speed_flag = 0 , primary_mode = 0 , secondary_mode = 0 ;
2008-04-27 00:25:21 +04:00
if ( dmabase = = 0 )
goto out ;
2005-04-17 02:20:36 +04:00
2007-02-17 04:40:25 +03:00
udma_speed_flag = inb ( dmabase | 0x1f ) ;
primary_mode = inb ( dmabase | 0x1a ) ;
secondary_mode = inb ( dmabase | 0x1b ) ;
2005-04-17 02:20:36 +04:00
printk ( KERN_INFO " %s: (U)DMA Burst Bit %sABLED " \
" Primary %s Mode " \
2008-04-27 00:25:20 +04:00
" Secondary %s Mode. \n " , pci_name ( dev ) ,
2005-04-17 02:20:36 +04:00
( udma_speed_flag & 1 ) ? " EN " : " DIS " ,
( primary_mode & 1 ) ? " MASTER " : " PCI " ,
( secondary_mode & 1 ) ? " MASTER " : " PCI " ) ;
if ( ! ( udma_speed_flag & 1 ) ) {
printk ( KERN_INFO " %s: FORCING BURST BIT 0x%02x->0x%02x " ,
2008-04-27 00:25:20 +04:00
pci_name ( dev ) , udma_speed_flag ,
2005-04-17 02:20:36 +04:00
( udma_speed_flag | 1 ) ) ;
2007-02-17 04:40:25 +03:00
outb ( udma_speed_flag | 1 , dmabase | 0x1f ) ;
printk ( " %sACTIVE \n " , ( inb ( dmabase | 0x1f ) & 1 ) ? " " : " IN " ) ;
2005-04-17 02:20:36 +04:00
}
2008-04-27 00:25:21 +04:00
out :
2009-03-25 01:22:53 +03:00
return 0 ;
2005-04-17 02:20:36 +04:00
}
2012-12-22 01:21:03 +04:00
static void pdc202ata4_fixup_irq ( struct pci_dev * dev , const char * name )
2005-04-17 02:20:36 +04:00
{
if ( ( dev - > class > > 8 ) ! = PCI_CLASS_STORAGE_IDE ) {
u8 irq = 0 , irq2 = 0 ;
pci_read_config_byte ( dev , PCI_INTERRUPT_LINE , & irq ) ;
/* 0xbc */
pci_read_config_byte ( dev , ( PCI_INTERRUPT_LINE ) | 0x80 , & irq2 ) ;
if ( irq ! = irq2 ) {
pci_write_config_byte ( dev ,
( PCI_INTERRUPT_LINE ) | 0x80 , irq ) ; /* 0xbc */
2008-07-25 00:53:31 +04:00
printk ( KERN_INFO " %s %s: PCI config space interrupt "
" mirror fixed \n " , name , pci_name ( dev ) ) ;
2005-04-17 02:20:36 +04:00
}
}
}
2008-01-26 00:17:18 +03:00
# define IDE_HFLAGS_PDC202XX \
( IDE_HFLAG_ERROR_STOPS_FIFO | \
IDE_HFLAG_OFF_BOARD )
2008-04-27 00:25:14 +04:00
static const struct ide_port_ops pdc20246_port_ops = {
. set_pio_mode = pdc202xx_set_pio_mode ,
. set_dma_mode = pdc202xx_set_mode ,
2009-06-15 20:52:59 +04:00
. test_irq = pdc202xx_test_irq ,
2008-04-27 00:25:14 +04:00
} ;
static const struct ide_port_ops pdc2026x_port_ops = {
. set_pio_mode = pdc202xx_set_pio_mode ,
. set_dma_mode = pdc202xx_set_mode ,
2010-04-14 09:50:26 +04:00
. test_irq = pdc202xx_test_irq ,
2008-04-27 00:25:14 +04:00
. cable_detect = pdc2026x_cable_detect ,
} ;
2008-04-27 00:25:24 +04:00
static const struct ide_dma_ops pdc2026x_dma_ops = {
. dma_host_set = ide_dma_host_set ,
. dma_setup = ide_dma_setup ,
2008-04-27 00:25:24 +04:00
. dma_start = pdc202xx_dma_start ,
. dma_end = pdc202xx_dma_end ,
2009-06-07 17:37:03 +04:00
. dma_test_irq = ide_dma_test_irq ,
2009-06-07 17:37:04 +04:00
. dma_lost_irq = ide_dma_lost_irq ,
2009-03-27 14:46:47 +03:00
. dma_timer_expiry = ide_dma_sff_timer_expiry ,
2009-01-06 19:21:02 +03:00
. dma_sff_read_status = ide_dma_sff_read_status ,
2008-04-27 00:25:24 +04:00
} ;
2008-12-29 22:27:34 +03:00
# define DECLARE_PDC2026X_DEV(udma, sectors) \
2007-10-19 02:30:10 +04:00
{ \
2008-07-25 00:53:32 +04:00
. name = DRV_NAME , \
2007-10-19 02:30:10 +04:00
. init_chipset = init_chipset_pdc202xx , \
2010-01-18 10:19:14 +03:00
. init_hwif = pdc2026x_init_hwif , \
2008-04-27 00:25:14 +04:00
. port_ops = & pdc2026x_port_ops , \
2008-04-27 00:25:24 +04:00
. dma_ops = & pdc2026x_dma_ops , \
2008-12-29 22:27:34 +03:00
. host_flags = IDE_HFLAGS_PDC202XX , \
2007-10-19 02:30:10 +04:00
. pio_mask = ATA_PIO4 , \
. mwdma_mask = ATA_MWDMA2 , \
. udma_mask = udma , \
2008-12-29 22:27:34 +03:00
. max_sectors = sectors , \
2007-10-19 02:30:10 +04:00
}
2012-12-22 01:21:03 +04:00
static const struct ide_port_info pdc202xx_chipsets [ ] = {
2008-07-25 00:53:32 +04:00
{ /* 0: PDC20246 */
. name = DRV_NAME ,
2005-04-17 02:20:36 +04:00
. init_chipset = init_chipset_pdc202xx ,
2008-04-27 00:25:14 +04:00
. port_ops = & pdc20246_port_ops ,
2009-06-07 17:37:03 +04:00
. dma_ops = & sff_dma_ops ,
2008-01-26 00:17:18 +03:00
. host_flags = IDE_HFLAGS_PDC202XX ,
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 ,
2007-10-19 02:30:10 +04:00
} ,
2008-07-25 00:53:32 +04:00
/* 1: PDC2026{2,3} */
DECLARE_PDC2026X_DEV ( ATA_UDMA4 , 0 ) ,
2008-12-29 22:27:34 +03:00
/* 2: PDC2026{5,7}: UDMA5, limit LBA48 requests to 256 sectors */
DECLARE_PDC2026X_DEV ( ATA_UDMA5 , 256 ) ,
2005-04-17 02:20:36 +04:00
} ;
/**
* pdc202xx_init_one - called when a PDC202xx is found
* @ dev : the pdc202xx device
* @ id : the matching pci id
*
* Called when the PCI registration layer ( or the IDE initialization )
* finds a device matching our IDE device tables .
*/
2012-12-22 01:21:03 +04:00
static int pdc202xx_init_one ( struct pci_dev * dev ,
const struct pci_device_id * id )
2005-04-17 02:20:36 +04:00
{
2007-10-20 02:32:34 +04:00
const struct ide_port_info * d ;
2007-10-19 02:30:09 +04:00
u8 idx = id - > driver_data ;
d = & pdc202xx_chipsets [ idx ] ;
2008-07-25 00:53:32 +04:00
if ( idx < 2 )
2007-10-19 02:30:09 +04:00
pdc202ata4_fixup_irq ( dev , d - > name ) ;
2008-07-25 00:53:32 +04:00
if ( dev - > vendor = = PCI_DEVICE_ID_PROMISE_20265 ) {
2007-10-19 02:30:09 +04:00
struct pci_dev * bridge = dev - > bus - > self ;
2005-04-17 02:20:36 +04:00
2007-10-19 02:30:09 +04:00
if ( bridge & &
bridge - > vendor = = PCI_VENDOR_ID_INTEL & &
( bridge - > device = = PCI_DEVICE_ID_INTEL_I960 | |
bridge - > device = = PCI_DEVICE_ID_INTEL_I960RM ) ) {
2008-07-25 00:53:32 +04:00
printk ( KERN_INFO DRV_NAME " %s: skipping Promise "
2008-07-25 00:53:31 +04:00
" PDC20265 attached to I2O RAID controller \n " ,
pci_name ( dev ) ) ;
2007-10-19 02:30:09 +04:00
return - ENODEV ;
}
}
2008-07-25 00:53:14 +04:00
return ide_pci_init_one ( dev , d , NULL ) ;
2005-04-17 02:20:36 +04:00
}
2007-10-17 00:29:56 +04:00
static const struct pci_device_id pdc202xx_pci_tbl [ ] = {
{ PCI_VDEVICE ( PROMISE , PCI_DEVICE_ID_PROMISE_20246 ) , 0 } ,
{ PCI_VDEVICE ( PROMISE , PCI_DEVICE_ID_PROMISE_20262 ) , 1 } ,
2008-07-25 00:53:32 +04:00
{ PCI_VDEVICE ( PROMISE , PCI_DEVICE_ID_PROMISE_20263 ) , 1 } ,
{ PCI_VDEVICE ( PROMISE , PCI_DEVICE_ID_PROMISE_20265 ) , 2 } ,
{ PCI_VDEVICE ( PROMISE , PCI_DEVICE_ID_PROMISE_20267 ) , 2 } ,
2005-04-17 02:20:36 +04:00
{ 0 , } ,
} ;
MODULE_DEVICE_TABLE ( pci , pdc202xx_pci_tbl ) ;
2008-10-13 23:39:41 +04:00
static struct pci_driver pdc202xx_pci_driver = {
2005-04-17 02:20:36 +04:00
. name = " Promise_Old_IDE " ,
. id_table = pdc202xx_pci_tbl ,
. probe = pdc202xx_init_one ,
2008-07-25 00:53:24 +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 pdc202xx_ide_init ( void )
2005-04-17 02:20:36 +04:00
{
2008-10-13 23:39:41 +04:00
return ide_pci_register_driver ( & pdc202xx_pci_driver ) ;
2005-04-17 02:20:36 +04:00
}
2008-07-25 00:53:24 +04:00
static void __exit pdc202xx_ide_exit ( void )
{
2008-10-13 23:39:41 +04:00
pci_unregister_driver ( & pdc202xx_pci_driver ) ;
2008-07-25 00:53:24 +04:00
}
2005-04-17 02:20:36 +04:00
module_init ( pdc202xx_ide_init ) ;
2008-07-25 00:53:24 +04:00
module_exit ( pdc202xx_ide_exit ) ;
2005-04-17 02:20:36 +04:00
2010-01-18 10:19:14 +03:00
MODULE_AUTHOR ( " Andre Hedrick, Frank Tiernan, Bartlomiej Zolnierkiewicz " ) ;
2005-04-17 02:20:36 +04:00
MODULE_DESCRIPTION ( " PCI driver module for older Promise IDE " ) ;
MODULE_LICENSE ( " GPL " ) ;