2006-08-30 02:12:40 +04:00
/*
* pata_sil680 . c - SIL680 PATA for new ATA layer
* ( C ) 2005 Red Hat Inc
*
* based upon
*
* linux / drivers / ide / pci / siimage . c Version 1.07 Nov 30 , 2003
*
* Copyright ( C ) 2001 - 2002 Andre Hedrick < andre @ linux - ide . org >
* Copyright ( C ) 2003 Red Hat < alan @ redhat . com >
*
* May be copied or modified under the terms of the GNU General Public License
*
* Documentation publically available .
*
* If you have strange problems with nVidia chipset systems please
* see the SI support documentation and update your system BIOS
2007-10-20 01:10:43 +04:00
* if necessary
2006-08-30 02:12:40 +04:00
*
* TODO
* If we know all our devices are LBA28 ( or LBA28 sized ) we could use
* the command fifo mode .
*/
# 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_sil680"
2009-01-05 17:16:39 +03:00
# define DRV_VERSION "0.4.9"
2006-08-30 02:12:40 +04:00
2007-05-28 15:22:30 +04:00
# define SIL680_MMIO_BAR 5
2006-08-30 02:12:40 +04:00
/**
* sil680_selreg - return register base
* @ hwif : interface
* @ r : config offset
*
* Turn a config register offset into the right address in either
* PCI space or MMIO space to access the control register in question
* Thankfully this is a configuration operation so isnt performance
* criticial .
*/
static unsigned long sil680_selreg ( struct ata_port * ap , int r )
{
unsigned long base = 0xA0 + r ;
base + = ( ap - > port_no < < 4 ) ;
return base ;
}
/**
* sil680_seldev - return register base
* @ hwif : interface
* @ r : config offset
*
* Turn a config register offset into the right address in either
* PCI space or MMIO space to access the control register in question
* including accounting for the unit shift .
*/
static unsigned long sil680_seldev ( struct ata_port * ap , struct ata_device * adev , int r )
{
unsigned long base = 0xA0 + r ;
base + = ( ap - > port_no < < 4 ) ;
base | = adev - > devno ? 2 : 0 ;
return base ;
}
/**
* sil680_cable_detect - cable detection
* @ ap : ATA port
*
* Perform cable detection . The SIL680 stores this in PCI config
* space for us .
*/
static int sil680_cable_detect ( struct ata_port * ap ) {
struct pci_dev * pdev = to_pci_dev ( ap - > host - > dev ) ;
unsigned long addr = sil680_selreg ( ap , 0 ) ;
u8 ata66 ;
pci_read_config_byte ( pdev , addr , & ata66 ) ;
if ( ata66 & 1 )
return ATA_CBL_PATA80 ;
else
return ATA_CBL_PATA40 ;
}
/**
* sil680_set_piomode - set initial PIO mode data
* @ ap : ATA interface
* @ adev : ATA device
*
* Program the SIL680 registers for PIO mode . Note that the task speed
* registers are shared between the devices so we must pick the lowest
* mode for command work .
*/
static void sil680_set_piomode ( struct ata_port * ap , struct ata_device * adev )
{
static u16 speed_p [ 5 ] = { 0x328A , 0x2283 , 0x1104 , 0x10C3 , 0x10C1 } ;
2007-01-28 21:33:44 +03:00
static u16 speed_t [ 5 ] = { 0x328A , 0x2283 , 0x1281 , 0x10C3 , 0x10C1 } ;
2006-08-30 02:12:40 +04:00
unsigned long tfaddr = sil680_selreg ( ap , 0x02 ) ;
unsigned long addr = sil680_seldev ( ap , adev , 0x04 ) ;
2007-02-20 20:49:25 +03:00
unsigned long addr_mask = 0x80 + 4 * ap - > port_no ;
2006-08-30 02:12:40 +04:00
struct pci_dev * pdev = to_pci_dev ( ap - > host - > dev ) ;
int pio = adev - > pio_mode - XFER_PIO_0 ;
int lowest_pio = pio ;
2007-02-20 20:49:25 +03:00
int port_shift = 4 * adev - > devno ;
2006-08-30 02:12:40 +04:00
u16 reg ;
2007-02-20 20:49:25 +03:00
u8 mode ;
2006-08-30 02:12:40 +04:00
struct ata_device * pair = ata_dev_pair ( adev ) ;
if ( pair ! = NULL & & adev - > pio_mode > pair - > pio_mode )
lowest_pio = pair - > pio_mode - XFER_PIO_0 ;
pci_write_config_word ( pdev , addr , speed_p [ pio ] ) ;
pci_write_config_word ( pdev , tfaddr , speed_t [ lowest_pio ] ) ;
pci_read_config_word ( pdev , tfaddr - 2 , & reg ) ;
2007-02-20 20:49:25 +03:00
pci_read_config_byte ( pdev , addr_mask , & mode ) ;
2007-02-26 13:51:33 +03:00
2006-08-30 02:12:40 +04:00
reg & = ~ 0x0200 ; /* Clear IORDY */
2007-02-20 20:49:25 +03:00
mode & = ~ ( 3 < < port_shift ) ; /* Clear IORDY and DMA bits */
2007-02-26 13:51:33 +03:00
2007-02-20 20:49:25 +03:00
if ( ata_pio_need_iordy ( adev ) ) {
2006-08-30 02:12:40 +04:00
reg | = 0x0200 ; /* Enable IORDY */
2007-02-20 20:49:25 +03:00
mode | = 1 < < port_shift ;
}
2006-08-30 02:12:40 +04:00
pci_write_config_word ( pdev , tfaddr - 2 , reg ) ;
2007-02-20 20:49:25 +03:00
pci_write_config_byte ( pdev , addr_mask , mode ) ;
2006-08-30 02:12:40 +04:00
}
/**
* sil680_set_dmamode - set initial DMA mode data
* @ ap : ATA interface
* @ adev : ATA device
*
* Program the MWDMA / UDMA modes for the sil680 k
* chipset . The MWDMA mode values are pulled from a lookup table
* while the chipset uses mode number for UDMA .
*/
static void sil680_set_dmamode ( struct ata_port * ap , struct ata_device * adev )
{
static u8 ultra_table [ 2 ] [ 7 ] = {
{ 0x0C , 0x07 , 0x05 , 0x04 , 0x02 , 0x01 , 0xFF } , /* 100MHz */
{ 0x0F , 0x0B , 0x07 , 0x05 , 0x03 , 0x02 , 0x01 } , /* 133Mhz */
} ;
static u16 dma_table [ 3 ] = { 0x2208 , 0x10C2 , 0x10C1 } ;
struct pci_dev * pdev = to_pci_dev ( ap - > host - > dev ) ;
unsigned long ma = sil680_seldev ( ap , adev , 0x08 ) ;
unsigned long ua = sil680_seldev ( ap , adev , 0x0C ) ;
unsigned long addr_mask = 0x80 + 4 * ap - > port_no ;
int port_shift = adev - > devno * 4 ;
u8 scsc , mode ;
u16 multi , ultra ;
pci_read_config_byte ( pdev , 0x8A , & scsc ) ;
pci_read_config_byte ( pdev , addr_mask , & mode ) ;
pci_read_config_word ( pdev , ma , & multi ) ;
pci_read_config_word ( pdev , ua , & ultra ) ;
/* Mask timing bits */
ultra & = ~ 0x3F ;
mode & = ~ ( 0x03 < < port_shift ) ;
/* Extract scsc */
scsc = ( scsc & 0x30 ) ? 1 : 0 ;
if ( adev - > dma_mode > = XFER_UDMA_0 ) {
multi = 0x10C1 ;
ultra | = ultra_table [ scsc ] [ adev - > dma_mode - XFER_UDMA_0 ] ;
mode | = ( 0x03 < < port_shift ) ;
} else {
multi = dma_table [ adev - > dma_mode - XFER_MW_DMA_0 ] ;
mode | = ( 0x02 < < port_shift ) ;
}
pci_write_config_byte ( pdev , addr_mask , mode ) ;
pci_write_config_word ( pdev , ma , multi ) ;
pci_write_config_word ( pdev , ua , ultra ) ;
}
2010-05-05 13:25:58 +04:00
/**
* sil680_sff_exec_command - issue ATA command to host controller
* @ ap : port to which command is being issued
* @ tf : ATA taskfile register set
*
* Issues ATA command , with proper synchronization with interrupt
* handler / other threads . Use our MMIO space for PCI posting to avoid
* a hideously slow cycle all the way to the device .
*
* LOCKING :
* spin_lock_irqsave ( host lock )
*/
void sil680_sff_exec_command ( struct ata_port * ap ,
const struct ata_taskfile * tf )
{
DPRINTK ( " ata%u: cmd 0x%X \n " , ap - > print_id , tf - > command ) ;
iowrite8 ( tf - > command , ap - > ioaddr . command_addr ) ;
ioread8 ( ap - > ioaddr . bmdma_addr + ATA_DMA_CMD ) ;
}
2006-08-30 02:12:40 +04:00
static struct scsi_host_template sil680_sht = {
2008-03-25 06:22:49 +03:00
ATA_BMDMA_SHT ( DRV_NAME ) ,
2006-08-30 02:12:40 +04:00
} ;
2010-05-05 13:25:58 +04:00
2006-08-30 02:12:40 +04:00
static struct ata_port_operations sil680_port_ops = {
2010-05-05 13:25:58 +04:00
. inherits = & ata_bmdma32_port_ops ,
. sff_exec_command = sil680_sff_exec_command ,
. cable_detect = sil680_cable_detect ,
. set_piomode = sil680_set_piomode ,
. set_dmamode = sil680_set_dmamode ,
2006-08-30 02:12:40 +04:00
} ;
2006-11-22 20:28:41 +03:00
/**
* sil680_init_chip - chip setup
* @ pdev : PCI device
*
* Perform all the chip setup which must be done both when the device
* is powered up on boot and when we resume in case we resumed from RAM .
* Returns the final clock settings .
*/
2006-12-11 19:14:06 +03:00
2007-07-07 03:21:22 +04:00
static u8 sil680_init_chip ( struct pci_dev * pdev , int * try_mmio )
2006-08-30 02:12:40 +04:00
{
u8 tmpbyte = 0 ;
/* FIXME: double check */
2009-11-24 21:54:49 +03:00
pci_write_config_byte ( pdev , PCI_CACHE_LINE_SIZE ,
pdev - > revision ? 1 : 255 ) ;
2006-08-30 02:12:40 +04:00
pci_write_config_byte ( pdev , 0x80 , 0x00 ) ;
pci_write_config_byte ( pdev , 0x84 , 0x00 ) ;
pci_read_config_byte ( pdev , 0x8A , & tmpbyte ) ;
2007-05-28 15:22:30 +04:00
dev_dbg ( & pdev - > dev , " sil680: BA5_EN = %d clock = %02X \n " ,
tmpbyte & 1 , tmpbyte & 0x30 ) ;
2006-08-30 02:12:40 +04:00
2008-03-29 00:52:29 +03:00
* try_mmio = 0 ;
2008-09-23 01:47:33 +04:00
# ifdef CONFIG_PPC
2008-03-29 00:52:29 +03:00
if ( machine_is ( cell ) )
* try_mmio = ( tmpbyte & 1 ) | | pci_resource_start ( pdev , 5 ) ;
# endif
2007-07-07 03:21:22 +04:00
2006-08-30 02:12:40 +04:00
switch ( tmpbyte & 0x30 ) {
case 0x00 :
/* 133 clock attempt to force it on */
pci_write_config_byte ( pdev , 0x8A , tmpbyte | 0x10 ) ;
break ;
case 0x30 :
/* if clocking is disabled */
/* 133 clock attempt to force it on */
pci_write_config_byte ( pdev , 0x8A , tmpbyte & ~ 0x20 ) ;
break ;
case 0x10 :
/* 133 already */
break ;
case 0x20 :
/* BIOS set PCI x2 clocking */
break ;
}
pci_read_config_byte ( pdev , 0x8A , & tmpbyte ) ;
2007-05-28 15:22:30 +04:00
dev_dbg ( & pdev - > dev , " sil680: BA5_EN = %d clock = %02X \n " ,
tmpbyte & 1 , tmpbyte & 0x30 ) ;
2006-08-30 02:12:40 +04:00
pci_write_config_byte ( pdev , 0xA1 , 0x72 ) ;
pci_write_config_word ( pdev , 0xA2 , 0x328A ) ;
pci_write_config_dword ( pdev , 0xA4 , 0x62DD62DD ) ;
pci_write_config_dword ( pdev , 0xA8 , 0x43924392 ) ;
pci_write_config_dword ( pdev , 0xAC , 0x40094009 ) ;
pci_write_config_byte ( pdev , 0xB1 , 0x72 ) ;
pci_write_config_word ( pdev , 0xB2 , 0x328A ) ;
pci_write_config_dword ( pdev , 0xB4 , 0x62DD62DD ) ;
pci_write_config_dword ( pdev , 0xB8 , 0x43924392 ) ;
pci_write_config_dword ( pdev , 0xBC , 0x40094009 ) ;
switch ( tmpbyte & 0x30 ) {
case 0x00 : printk ( KERN_INFO " sil680: 100MHz clock. \n " ) ; break ;
case 0x10 : printk ( KERN_INFO " sil680: 133MHz clock. \n " ) ; break ;
case 0x20 : printk ( KERN_INFO " sil680: Using PCI clock. \n " ) ; break ;
/* This last case is _NOT_ ok */
case 0x30 : printk ( KERN_ERR " sil680: Clock disabled ? \n " ) ;
2006-11-22 20:28:41 +03:00
}
return tmpbyte & 0x30 ;
}
2007-05-28 15:22:30 +04:00
static int __devinit sil680_init_one ( struct pci_dev * pdev ,
const struct pci_device_id * id )
2006-11-22 20:28:41 +03:00
{
2007-05-04 14:43:58 +04:00
static const struct ata_port_info info = {
2007-05-28 14:59:48 +04:00
. flags = ATA_FLAG_SLAVE_POSS ,
2009-03-14 23:38:24 +03:00
. pio_mask = ATA_PIO4 ,
. mwdma_mask = ATA_MWDMA2 ,
2007-07-09 20:16:50 +04:00
. udma_mask = ATA_UDMA6 ,
2006-11-22 20:28:41 +03:00
. port_ops = & sil680_port_ops
} ;
2007-05-04 14:43:58 +04:00
static const struct ata_port_info info_slow = {
2007-05-28 14:59:48 +04:00
. flags = ATA_FLAG_SLAVE_POSS ,
2009-03-14 23:38:24 +03:00
. pio_mask = ATA_PIO4 ,
. mwdma_mask = ATA_MWDMA2 ,
2007-07-09 20:16:50 +04:00
. udma_mask = ATA_UDMA5 ,
2006-11-22 20:28:41 +03:00
. port_ops = & sil680_port_ops
} ;
2007-05-04 14:43:58 +04:00
const struct ata_port_info * ppi [ ] = { & info , NULL } ;
2006-11-22 20:28:41 +03:00
static int printed_version ;
2007-07-07 03:21:22 +04:00
struct ata_host * host ;
void __iomem * mmio_base ;
int rc , try_mmio ;
2006-11-22 20:28:41 +03:00
if ( ! printed_version + + )
dev_printk ( KERN_DEBUG , & pdev - > dev , " version " DRV_VERSION " \n " ) ;
2008-03-25 06:22:47 +03:00
rc = pcim_enable_device ( pdev ) ;
if ( rc )
return rc ;
2007-07-07 03:21:22 +04:00
switch ( sil680_init_chip ( pdev , & try_mmio ) ) {
2006-11-22 20:28:41 +03:00
case 0 :
2007-05-04 14:43:58 +04:00
ppi [ 0 ] = & info_slow ;
2006-11-22 20:28:41 +03:00
break ;
case 0x30 :
return - ENODEV ;
2006-08-30 02:12:40 +04:00
}
2007-07-07 03:21:22 +04:00
if ( ! try_mmio )
goto use_ioports ;
/* Try to acquire MMIO resources and fallback to PIO if
* that fails
*/
rc = pcim_iomap_regions ( pdev , 1 < < SIL680_MMIO_BAR , DRV_NAME ) ;
if ( rc )
goto use_ioports ;
/* Allocate host and set it up */
host = ata_host_alloc_pinfo ( & pdev - > dev , ppi , 2 ) ;
if ( ! host )
return - ENOMEM ;
host - > iomap = pcim_iomap_table ( pdev ) ;
/* Setup DMA masks */
rc = pci_set_dma_mask ( pdev , ATA_DMA_MASK ) ;
if ( rc )
return rc ;
rc = pci_set_consistent_dma_mask ( pdev , ATA_DMA_MASK ) ;
if ( rc )
return rc ;
pci_set_master ( pdev ) ;
/* Get MMIO base and initialize port addresses */
mmio_base = host - > iomap [ SIL680_MMIO_BAR ] ;
host - > ports [ 0 ] - > ioaddr . bmdma_addr = mmio_base + 0x00 ;
host - > ports [ 0 ] - > ioaddr . cmd_addr = mmio_base + 0x80 ;
host - > ports [ 0 ] - > ioaddr . ctl_addr = mmio_base + 0x8a ;
host - > ports [ 0 ] - > ioaddr . altstatus_addr = mmio_base + 0x8a ;
2008-04-07 17:47:16 +04:00
ata_sff_std_ports ( & host - > ports [ 0 ] - > ioaddr ) ;
2007-07-07 03:21:22 +04:00
host - > ports [ 1 ] - > ioaddr . bmdma_addr = mmio_base + 0x08 ;
host - > ports [ 1 ] - > ioaddr . cmd_addr = mmio_base + 0xc0 ;
host - > ports [ 1 ] - > ioaddr . ctl_addr = mmio_base + 0xca ;
host - > ports [ 1 ] - > ioaddr . altstatus_addr = mmio_base + 0xca ;
2008-04-07 17:47:16 +04:00
ata_sff_std_ports ( & host - > ports [ 1 ] - > ioaddr ) ;
2007-07-07 03:21:22 +04:00
/* Register & activate */
2008-04-07 17:47:16 +04:00
return ata_host_activate ( host , pdev - > irq , ata_sff_interrupt ,
IRQF_SHARED , & sil680_sht ) ;
2007-07-07 03:21:22 +04:00
use_ioports :
2010-02-23 10:26:06 +03:00
return ata_pci_sff_init_one ( pdev , ppi , & sil680_sht , NULL , 0 ) ;
2006-08-30 02:12:40 +04:00
}
2007-03-02 11:31:26 +03:00
# ifdef CONFIG_PM
2006-11-22 20:28:41 +03:00
static int sil680_reinit_one ( struct pci_dev * pdev )
{
2008-03-25 06:22:47 +03:00
struct ata_host * host = dev_get_drvdata ( & pdev - > dev ) ;
int try_mmio , rc ;
2007-07-07 03:21:22 +04:00
2008-03-25 06:22:47 +03:00
rc = ata_pci_device_do_resume ( pdev ) ;
if ( rc )
return rc ;
2007-07-07 03:21:22 +04:00
sil680_init_chip ( pdev , & try_mmio ) ;
2008-03-25 06:22:47 +03:00
ata_host_resume ( host ) ;
return 0 ;
2006-11-22 20:28:41 +03:00
}
2007-03-02 11:31:26 +03:00
# endif
2006-11-22 20:28:41 +03:00
2006-08-30 02:12:40 +04:00
static const struct pci_device_id sil680 [ ] = {
2006-09-29 04:21:59 +04:00
{ PCI_VDEVICE ( CMD , PCI_DEVICE_ID_SII_680 ) , } ,
{ } ,
2006-08-30 02:12:40 +04:00
} ;
static struct pci_driver sil680_pci_driver = {
2006-09-29 04:21:59 +04:00
. name = DRV_NAME ,
2006-08-30 02:12:40 +04:00
. id_table = sil680 ,
. probe = sil680_init_one ,
2006-11-22 20:28:41 +03:00
. remove = ata_pci_remove_one ,
2007-03-02 11:31:26 +03:00
# ifdef CONFIG_PM
2006-11-22 20:28:41 +03:00
. suspend = ata_pci_device_suspend ,
. resume = sil680_reinit_one ,
2007-03-02 11:31:26 +03:00
# endif
2006-08-30 02:12:40 +04:00
} ;
static int __init sil680_init ( void )
{
return pci_register_driver ( & sil680_pci_driver ) ;
}
static void __exit sil680_exit ( void )
{
pci_unregister_driver ( & sil680_pci_driver ) ;
}
MODULE_AUTHOR ( " Alan Cox " ) ;
MODULE_DESCRIPTION ( " low-level driver for SI680 PATA " ) ;
MODULE_LICENSE ( " GPL " ) ;
MODULE_DEVICE_TABLE ( pci , sil680 ) ;
MODULE_VERSION ( DRV_VERSION ) ;
module_init ( sil680_init ) ;
module_exit ( sil680_exit ) ;