2006-08-30 02:12:40 +04:00
/*
* pata_optidma . c - Opti DMA PATA for new ATA layer
* ( C ) 2006 Red Hat Inc
* Alan Cox < alan @ redhat . com >
*
* The Opti DMA controllers are related to the older PIO PCI controllers
* and indeed the VLB ones . The main differences are that the timing
* numbers are now based off PCI clocks not VLB and differ , and that
* MWDMA is supported .
*
* This driver should support Viper - N + , FireStar , FireStar Plus .
*
* These devices support virtual DMA for read ( aka the CS5520 ) . Later
* chips support UDMA33 , but only if the rest of the board logic does ,
* so you have to get this right . We don ' t support the virtual DMA
* but we do handle UDMA .
*
* Bits that are worth knowing
* Most control registers are shadowed into I / O registers
* 0x1F5 bit 0 tells you if the PCI / VLB clock is 33 or 25 Mhz
* Virtual DMA registers * move * between rev 0x02 and rev 0x10
* UDMA requires a 66 MHz FSB
*
*/
# include <linux/kernel.h>
# include <linux/module.h>
# include <linux/pci.h>
# include <linux/init.h>
# include <linux/blkdev.h>
# include <linux/delay.h>
# include <scsi/scsi_host.h>
# include <linux/libata.h>
# define DRV_NAME "pata_optidma"
2006-09-26 20:53:38 +04:00
# define DRV_VERSION "0.2.2"
2006-08-30 02:12:40 +04:00
enum {
READ_REG = 0 , /* index of Read cycle timing register */
WRITE_REG = 1 , /* index of Write cycle timing register */
CNTRL_REG = 3 , /* index of Control register */
STRAP_REG = 5 , /* index of Strap register */
MISC_REG = 6 /* index of Miscellaneous register */
} ;
static int pci_clock ; /* 0 = 33 1 = 25 */
/**
* optidma_pre_reset - probe begin
* @ ap : ATA port
*
* Set up cable type and use generic probe init
*/
2006-08-31 08:03:49 +04:00
2006-08-30 02:12:40 +04:00
static int optidma_pre_reset ( struct ata_port * ap )
{
struct pci_dev * pdev = to_pci_dev ( ap - > host - > dev ) ;
2006-08-31 08:03:49 +04:00
static const struct pci_bits optidma_enable_bits = {
2006-08-30 02:12:40 +04:00
0x40 , 1 , 0x08 , 0x00
} ;
2006-09-26 20:53:38 +04:00
if ( ap - > port_no & & ! pci_test_config_bits ( pdev , & optidma_enable_bits ) )
return - ENOENT ;
2006-08-30 02:12:40 +04:00
ap - > cbl = ATA_CBL_PATA40 ;
return ata_std_prereset ( ap ) ;
}
/**
* optidma_probe_reset - probe reset
* @ ap : ATA port
*
* Perform the ATA probe and bus reset sequence plus specific handling
* for this hardware . The Opti needs little handling - we have no UDMA66
* capability that needs cable detection . All we must do is check the port
* is enabled .
*/
static void optidma_error_handler ( struct ata_port * ap )
{
ata_bmdma_drive_eh ( ap , optidma_pre_reset , ata_std_softreset , NULL , ata_std_postreset ) ;
}
/**
* optidma_unlock - unlock control registers
* @ ap : ATA port
*
* Unlock the control register block for this adapter . Registers must not
* be unlocked in a situation where libata might look at them .
*/
2006-08-31 08:03:49 +04:00
2006-08-30 02:12:40 +04:00
static void optidma_unlock ( struct ata_port * ap )
{
unsigned long regio = ap - > ioaddr . cmd_addr ;
2006-08-31 08:03:49 +04:00
2006-08-30 02:12:40 +04:00
/* These 3 unlock the control register access */
inw ( regio + 1 ) ;
inw ( regio + 1 ) ;
outb ( 3 , regio + 2 ) ;
}
/**
* optidma_lock - issue temporary relock
* @ ap : ATA port
*
* Re - lock the configuration register settings .
*/
2006-08-31 08:03:49 +04:00
2006-08-30 02:12:40 +04:00
static void optidma_lock ( struct ata_port * ap )
{
unsigned long regio = ap - > ioaddr . cmd_addr ;
2006-08-31 08:03:49 +04:00
2006-08-30 02:12:40 +04:00
/* Relock */
outb ( 0x83 , regio + 2 ) ;
}
/**
* optidma_set_mode - set mode data
* @ ap : ATA interface
* @ adev : ATA device
* @ mode : Mode to set
*
* Called to do the DMA or PIO mode setup . Timing numbers are all
* pre computed to keep the code clean . There are two tables depending
* on the hardware clock speed .
*
* WARNING : While we do this the IDE registers vanish . If we take an
* IRQ here we depend on the host set locking to avoid catastrophe .
*/
static void optidma_set_mode ( struct ata_port * ap , struct ata_device * adev , u8 mode )
{
struct ata_device * pair = ata_dev_pair ( adev ) ;
int pio = adev - > pio_mode - XFER_PIO_0 ;
int dma = adev - > dma_mode - XFER_MW_DMA_0 ;
unsigned long regio = ap - > ioaddr . cmd_addr ;
u8 addr ;
/* Address table precomputed with a DCLK of 2 */
static const u8 addr_timing [ 2 ] [ 5 ] = {
{ 0x30 , 0x20 , 0x20 , 0x10 , 0x10 } ,
{ 0x20 , 0x20 , 0x10 , 0x10 , 0x10 }
} ;
static const u8 data_rec_timing [ 2 ] [ 5 ] = {
{ 0x59 , 0x46 , 0x30 , 0x20 , 0x20 } ,
{ 0x46 , 0x32 , 0x20 , 0x20 , 0x10 }
} ;
static const u8 dma_data_rec_timing [ 2 ] [ 3 ] = {
{ 0x76 , 0x20 , 0x20 } ,
{ 0x54 , 0x20 , 0x10 }
} ;
/* Switch from IDE to control mode */
optidma_unlock ( ap ) ;
2006-08-31 08:03:49 +04:00
2006-08-30 02:12:40 +04:00
/*
* As with many controllers the address setup time is shared
* and must suit both devices if present . FIXME : Check if we
* need to look at slowest of PIO / DMA mode of either device
*/
if ( mode > = XFER_MW_DMA_0 )
addr = 0 ;
else
addr = addr_timing [ pci_clock ] [ pio ] ;
2006-08-31 08:03:49 +04:00
2006-08-30 02:12:40 +04:00
if ( pair ) {
u8 pair_addr ;
/* Hardware constraint */
if ( pair - > dma_mode )
pair_addr = 0 ;
else
pair_addr = addr_timing [ pci_clock ] [ pair - > pio_mode - XFER_PIO_0 ] ;
if ( pair_addr > addr )
addr = pair_addr ;
}
2006-08-31 08:03:49 +04:00
2006-08-30 02:12:40 +04:00
/* Commence primary programming sequence */
/* First we load the device number into the timing select */
outb ( adev - > devno , regio + MISC_REG ) ;
/* Now we load the data timings into read data/write data */
if ( mode < XFER_MW_DMA_0 ) {
outb ( data_rec_timing [ pci_clock ] [ pio ] , regio + READ_REG ) ;
outb ( data_rec_timing [ pci_clock ] [ pio ] , regio + WRITE_REG ) ;
} else if ( mode < XFER_UDMA_0 ) {
outb ( dma_data_rec_timing [ pci_clock ] [ dma ] , regio + READ_REG ) ;
outb ( dma_data_rec_timing [ pci_clock ] [ dma ] , regio + WRITE_REG ) ;
}
/* Finally we load the address setup into the misc register */
outb ( addr | adev - > devno , regio + MISC_REG ) ;
/* Programming sequence complete, timing 0 dev 0, timing 1 dev 1 */
outb ( 0x85 , regio + CNTRL_REG ) ;
2006-08-31 08:03:49 +04:00
2006-08-30 02:12:40 +04:00
/* Switch back to IDE mode */
optidma_lock ( ap ) ;
2006-08-31 08:03:49 +04:00
2006-08-30 02:12:40 +04:00
/* Note: at this point our programming is incomplete. We are
not supposed to program PCI 0x43 " things we hacked onto the chip "
until we ' ve done both sets of PIO / DMA timings */
}
/**
* optiplus_set_mode - DMA setup for Firestar Plus
* @ ap : ATA port
* @ adev : device
* @ mode : desired mode
*
* The Firestar plus has additional UDMA functionality for UDMA0 - 2 and
* requires we do some additional work . Because the base work we must do
* is mostly shared we wrap the Firestar setup functionality in this
* one
*/
static void optiplus_set_mode ( struct ata_port * ap , struct ata_device * adev , u8 mode )
{
struct pci_dev * pdev = to_pci_dev ( ap - > host - > dev ) ;
u8 udcfg ;
u8 udslave ;
int dev2 = 2 * adev - > devno ;
int unit = 2 * ap - > port_no + adev - > devno ;
int udma = mode - XFER_UDMA_0 ;
2006-08-31 08:03:49 +04:00
2006-08-30 02:12:40 +04:00
pci_read_config_byte ( pdev , 0x44 , & udcfg ) ;
if ( mode < = XFER_UDMA_0 ) {
udcfg & = ~ ( 1 < < unit ) ;
optidma_set_mode ( ap , adev , adev - > dma_mode ) ;
} else {
udcfg | = ( 1 < < unit ) ;
if ( ap - > port_no ) {
pci_read_config_byte ( pdev , 0x45 , & udslave ) ;
udslave & = ~ ( 0x03 < < dev2 ) ;
udslave | = ( udma < < dev2 ) ;
pci_write_config_byte ( pdev , 0x45 , udslave ) ;
} else {
udcfg & = ~ ( 0x30 < < dev2 ) ;
udcfg | = ( udma < < dev2 ) ;
}
}
pci_write_config_byte ( pdev , 0x44 , udcfg ) ;
}
/**
* optidma_set_pio_mode - PIO setup callback
* @ ap : ATA port
* @ adev : Device
*
* The libata core provides separate functions for handling PIO and
* DMA programming . The architecture of the Firestar makes it easier
* for us to have a common function so we provide wrappers
*/
2006-08-31 08:03:49 +04:00
2006-08-30 02:12:40 +04:00
static void optidma_set_pio_mode ( struct ata_port * ap , struct ata_device * adev )
{
optidma_set_mode ( ap , adev , adev - > pio_mode ) ;
}
/**
* optidma_set_dma_mode - DMA setup callback
* @ ap : ATA port
* @ adev : Device
*
* The libata core provides separate functions for handling PIO and
* DMA programming . The architecture of the Firestar makes it easier
* for us to have a common function so we provide wrappers
*/
2006-08-31 08:03:49 +04:00
2006-08-30 02:12:40 +04:00
static void optidma_set_dma_mode ( struct ata_port * ap , struct ata_device * adev )
{
optidma_set_mode ( ap , adev , adev - > dma_mode ) ;
}
/**
* optiplus_set_pio_mode - PIO setup callback
* @ ap : ATA port
* @ adev : Device
*
* The libata core provides separate functions for handling PIO and
* DMA programming . The architecture of the Firestar makes it easier
* for us to have a common function so we provide wrappers
*/
2006-08-31 08:03:49 +04:00
2006-08-30 02:12:40 +04:00
static void optiplus_set_pio_mode ( struct ata_port * ap , struct ata_device * adev )
{
optiplus_set_mode ( ap , adev , adev - > pio_mode ) ;
}
/**
* optiplus_set_dma_mode - DMA setup callback
* @ ap : ATA port
* @ adev : Device
*
* The libata core provides separate functions for handling PIO and
* DMA programming . The architecture of the Firestar makes it easier
* for us to have a common function so we provide wrappers
*/
2006-08-31 08:03:49 +04:00
2006-08-30 02:12:40 +04:00
static void optiplus_set_dma_mode ( struct ata_port * ap , struct ata_device * adev )
{
optiplus_set_mode ( ap , adev , adev - > dma_mode ) ;
}
/**
* optidma_make_bits - PCI setup helper
* @ adev : ATA device
*
* Turn the ATA device setup into PCI configuration bits
* for register 0x43 and return the two bits needed .
*/
2006-08-31 08:03:49 +04:00
2006-08-30 02:12:40 +04:00
static u8 optidma_make_bits43 ( struct ata_device * adev )
{
static const u8 bits43 [ 5 ] = {
0 , 0 , 0 , 1 , 2
} ;
if ( ! ata_dev_enabled ( adev ) )
return 0 ;
if ( adev - > dma_mode )
return adev - > dma_mode - XFER_MW_DMA_0 ;
return bits43 [ adev - > pio_mode - XFER_PIO_0 ] ;
}
/**
* optidma_post_set_mode - finalize PCI setup
* @ ap : port to set up
*
* Finalise the configuration by writing the nibble of extra bits
* of data into the chip .
*/
2006-08-31 08:03:49 +04:00
2006-08-30 02:12:40 +04:00
static void optidma_post_set_mode ( struct ata_port * ap )
{
u8 r ;
int nybble = 4 * ap - > port_no ;
struct pci_dev * pdev = to_pci_dev ( ap - > host - > dev ) ;
2006-08-31 08:03:49 +04:00
2006-08-30 02:12:40 +04:00
pci_read_config_byte ( pdev , 0x43 , & r ) ;
2006-08-31 08:03:49 +04:00
2006-08-30 02:12:40 +04:00
r & = ( 0x0F < < nybble ) ;
2006-08-31 08:03:49 +04:00
r | = ( optidma_make_bits43 ( & ap - > device [ 0 ] ) +
2006-08-30 02:12:40 +04:00
( optidma_make_bits43 ( & ap - > device [ 0 ] ) < < 2 ) ) < < nybble ;
pci_write_config_byte ( pdev , 0x43 , r ) ;
}
static struct scsi_host_template optidma_sht = {
. module = THIS_MODULE ,
. name = DRV_NAME ,
. ioctl = ata_scsi_ioctl ,
. queuecommand = ata_scsi_queuecmd ,
. can_queue = ATA_DEF_QUEUE ,
. this_id = ATA_SHT_THIS_ID ,
. sg_tablesize = LIBATA_MAX_PRD ,
. max_sectors = ATA_MAX_SECTORS ,
. cmd_per_lun = ATA_SHT_CMD_PER_LUN ,
. emulated = ATA_SHT_EMULATED ,
. use_clustering = ATA_SHT_USE_CLUSTERING ,
. proc_name = DRV_NAME ,
. dma_boundary = ATA_DMA_BOUNDARY ,
. slave_configure = ata_scsi_slave_config ,
2006-11-29 05:26:47 +03:00
. slave_destroy = ata_scsi_slave_destroy ,
2006-08-30 02:12:40 +04:00
. bios_param = ata_std_bios_param ,
} ;
static struct ata_port_operations optidma_port_ops = {
. port_disable = ata_port_disable ,
. set_piomode = optidma_set_pio_mode ,
. set_dmamode = optidma_set_dma_mode ,
. tf_load = ata_tf_load ,
. tf_read = ata_tf_read ,
. check_status = ata_check_status ,
. exec_command = ata_exec_command ,
. dev_select = ata_std_dev_select ,
. freeze = ata_bmdma_freeze ,
. thaw = ata_bmdma_thaw ,
. post_internal_cmd = ata_bmdma_post_internal_cmd ,
. error_handler = optidma_error_handler ,
. post_set_mode = optidma_post_set_mode ,
. bmdma_setup = ata_bmdma_setup ,
. bmdma_start = ata_bmdma_start ,
. bmdma_stop = ata_bmdma_stop ,
. bmdma_status = ata_bmdma_status ,
. qc_prep = ata_qc_prep ,
. qc_issue = ata_qc_issue_prot ,
2006-09-27 13:41:13 +04:00
2006-08-30 02:12:40 +04:00
. data_xfer = ata_pio_data_xfer ,
. irq_handler = ata_interrupt ,
. irq_clear = ata_bmdma_irq_clear ,
. port_start = ata_port_start ,
. port_stop = ata_port_stop ,
. host_stop = ata_host_stop
} ;
static struct ata_port_operations optiplus_port_ops = {
. port_disable = ata_port_disable ,
. set_piomode = optiplus_set_pio_mode ,
. set_dmamode = optiplus_set_dma_mode ,
. tf_load = ata_tf_load ,
. tf_read = ata_tf_read ,
. check_status = ata_check_status ,
. exec_command = ata_exec_command ,
. dev_select = ata_std_dev_select ,
. freeze = ata_bmdma_freeze ,
. thaw = ata_bmdma_thaw ,
. post_internal_cmd = ata_bmdma_post_internal_cmd ,
. error_handler = optidma_error_handler ,
. post_set_mode = optidma_post_set_mode ,
. bmdma_setup = ata_bmdma_setup ,
. bmdma_start = ata_bmdma_start ,
. bmdma_stop = ata_bmdma_stop ,
. bmdma_status = ata_bmdma_status ,
. qc_prep = ata_qc_prep ,
. qc_issue = ata_qc_issue_prot ,
2006-09-27 13:41:13 +04:00
2006-08-30 02:12:40 +04:00
. data_xfer = ata_pio_data_xfer ,
. irq_handler = ata_interrupt ,
. irq_clear = ata_bmdma_irq_clear ,
. port_start = ata_port_start ,
. port_stop = ata_port_stop ,
. host_stop = ata_host_stop
} ;
/**
* optiplus_with_udma - Look for UDMA capable setup
* @ pdev ; ATA controller
*/
2006-08-31 08:03:49 +04:00
2006-08-30 02:12:40 +04:00
static int optiplus_with_udma ( struct pci_dev * pdev )
{
u8 r ;
int ret = 0 ;
int ioport = 0x22 ;
struct pci_dev * dev1 ;
2006-08-31 08:03:49 +04:00
2006-08-30 02:12:40 +04:00
/* Find function 1 */
dev1 = pci_get_device ( 0x1045 , 0xC701 , NULL ) ;
if ( dev1 = = NULL )
return 0 ;
2006-08-31 08:03:49 +04:00
2006-08-30 02:12:40 +04:00
/* Rev must be >= 0x10 */
pci_read_config_byte ( dev1 , 0x08 , & r ) ;
if ( r < 0x10 )
goto done_nomsg ;
/* Read the chipset system configuration to check our mode */
pci_read_config_byte ( dev1 , 0x5F , & r ) ;
ioport | = ( r < < 8 ) ;
outb ( 0x10 , ioport ) ;
/* Must be 66Mhz sync */
if ( ( inb ( ioport + 2 ) & 1 ) = = 0 )
goto done ;
/* Check the ATA arbitration/timing is suitable */
pci_read_config_byte ( pdev , 0x42 , & r ) ;
if ( ( r & 0x36 ) ! = 0x36 )
goto done ;
pci_read_config_byte ( dev1 , 0x52 , & r ) ;
if ( r & 0x80 ) /* IDEDIR disabled */
ret = 1 ;
2006-08-31 08:03:49 +04:00
done :
2006-08-30 02:12:40 +04:00
printk ( KERN_WARNING " UDMA not supported in this configuration. \n " ) ;
done_nomsg : /* Wrong chip revision */
pci_dev_put ( dev1 ) ;
return ret ;
}
static int optidma_init_one ( struct pci_dev * dev , const struct pci_device_id * id )
{
static struct ata_port_info info_82c700 = {
. sht = & optidma_sht ,
. flags = ATA_FLAG_SLAVE_POSS | ATA_FLAG_SRST ,
. pio_mask = 0x1f ,
. mwdma_mask = 0x07 ,
. port_ops = & optidma_port_ops
} ;
static struct ata_port_info info_82c700_udma = {
. sht = & optidma_sht ,
. flags = ATA_FLAG_SLAVE_POSS | ATA_FLAG_SRST ,
. pio_mask = 0x1f ,
. mwdma_mask = 0x07 ,
. udma_mask = 0x07 ,
. port_ops = & optiplus_port_ops
} ;
static struct ata_port_info * port_info [ 2 ] ;
struct ata_port_info * info = & info_82c700 ;
static int printed_version ;
if ( ! printed_version + + )
dev_printk ( KERN_DEBUG , & dev - > dev , " version " DRV_VERSION " \n " ) ;
/* Fixed location chipset magic */
inw ( 0x1F1 ) ;
inw ( 0x1F1 ) ;
pci_clock = inb ( 0x1F5 ) & 1 ; /* 0 = 33Mhz, 1 = 25Mhz */
2006-08-31 08:03:49 +04:00
2006-08-30 02:12:40 +04:00
if ( optiplus_with_udma ( dev ) )
info = & info_82c700_udma ;
port_info [ 0 ] = port_info [ 1 ] = info ;
return ata_pci_init_one ( dev , port_info , 2 ) ;
}
static const struct pci_device_id optidma [ ] = {
2006-09-29 04:21:59 +04:00
{ PCI_VDEVICE ( OPTI , 0xD568 ) , } , /* Opti 82C700 */
{ } ,
2006-08-30 02:12:40 +04:00
} ;
static struct pci_driver optidma_pci_driver = {
2006-09-29 04:21:59 +04:00
. name = DRV_NAME ,
2006-08-30 02:12:40 +04:00
. id_table = optidma ,
. probe = optidma_init_one ,
. remove = ata_pci_remove_one
} ;
static int __init optidma_init ( void )
{
return pci_register_driver ( & optidma_pci_driver ) ;
}
static void __exit optidma_exit ( void )
{
pci_unregister_driver ( & optidma_pci_driver ) ;
}
MODULE_AUTHOR ( " Alan Cox " ) ;
MODULE_DESCRIPTION ( " low-level driver for Opti Firestar/Firestar Plus " ) ;
MODULE_LICENSE ( " GPL " ) ;
MODULE_DEVICE_TABLE ( pci , optidma ) ;
MODULE_VERSION ( DRV_VERSION ) ;
module_init ( optidma_init ) ;
module_exit ( optidma_exit ) ;