2005-04-16 15:20:36 -07:00
/*
* sata_sis . c - Silicon Integrated Systems SATA
*
* Maintained by : Uwe Koziolek
* Please ALWAYS copy linux - ide @ vger . kernel . org
* on emails .
*
* Copyright 2004 Uwe Koziolek
*
2005-08-28 20:18:39 -04:00
*
* This program is free software ; you can redistribute it and / or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation ; either version 2 , or ( at your option )
* any later version .
*
* This program is distributed in the hope that it will be useful ,
* but WITHOUT ANY WARRANTY ; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the
* GNU General Public License for more details .
*
* You should have received a copy of the GNU General Public License
* along with this program ; see the file COPYING . If not , write to
* the Free Software Foundation , 675 Mass Ave , Cambridge , MA 0213 9 , USA .
*
*
* libata documentation is available via ' make { ps | pdf } docs ' ,
* as Documentation / DocBook / libata . *
*
* Hardware documentation available under NDA .
2005-04-16 15:20:36 -07:00
*
*/
# 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 <linux/interrupt.h>
2005-10-30 14:39:11 -05:00
# include <linux/device.h>
2005-04-16 15:20:36 -07:00
# include <scsi/scsi_host.h>
# include <linux/libata.h>
2007-02-16 01:40:04 -08:00
# include "sis.h"
2005-04-16 15:20:36 -07:00
# define DRV_NAME "sata_sis"
2007-08-31 04:54:06 -04:00
# define DRV_VERSION "1.0"
2005-04-16 15:20:36 -07:00
enum {
sis_180 = 0 ,
SIS_SCR_PCI_BAR = 5 ,
/* PCI configuration registers */
SIS_GENCTL = 0x54 , /* IDE General Control register */
SIS_SCR_BASE = 0xc0 , /* sata0 phy SCR registers */
2005-09-07 22:44:48 +02:00
SIS180_SATA1_OFS = 0x10 , /* offset from sata0->sata1 phy regs */
SIS182_SATA1_OFS = 0x20 , /* offset from sata0->sata1 phy regs */
SIS_PMR = 0x90 , /* port mapping register */
2005-09-08 23:07:29 -04:00
SIS_PMR_COMBINED = 0x30 ,
2005-04-16 15:20:36 -07:00
/* random bits */
SIS_FLAG_CFGSCR = ( 1 < < 30 ) , /* host flag: SCRs via PCI cfg */
GENCTL_IOMAPPED_SCR = ( 1 < < 26 ) , /* if set, SCRs are in IO space */
} ;
2007-10-26 00:03:37 -04:00
static int sis_init_one ( struct pci_dev * pdev , const struct pci_device_id * ent ) ;
static int sis_scr_read ( struct ata_port * ap , unsigned int sc_reg , u32 * val ) ;
static int sis_scr_write ( struct ata_port * ap , unsigned int sc_reg , u32 val ) ;
2005-04-16 15:20:36 -07:00
2005-11-10 11:04:11 -05:00
static const struct pci_device_id sis_pci_tbl [ ] = {
2007-10-26 00:03:37 -04:00
{ PCI_VDEVICE ( SI , 0x0180 ) , sis_180 } , /* SiS 964/180 */
{ PCI_VDEVICE ( SI , 0x0181 ) , sis_180 } , /* SiS 964/180 */
{ PCI_VDEVICE ( SI , 0x0182 ) , sis_180 } , /* SiS 965/965L */
{ PCI_VDEVICE ( SI , 0x0183 ) , sis_180 } , /* SiS 965/965L */
{ PCI_VDEVICE ( SI , 0x1182 ) , sis_180 } , /* SiS 966/680 */
{ PCI_VDEVICE ( SI , 0x1183 ) , sis_180 } , /* SiS 966/966L/968/680 */
2006-09-28 20:21:59 -04:00
2005-04-16 15:20:36 -07:00
{ } /* terminate list */
} ;
static struct pci_driver sis_pci_driver = {
. name = DRV_NAME ,
. id_table = sis_pci_tbl ,
. probe = sis_init_one ,
. remove = ata_pci_remove_one ,
} ;
2005-11-07 00:59:37 -05:00
static struct scsi_host_template sis_sht = {
2005-04-16 15:20:36 -07:00
. 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 ,
2007-10-19 22:56:44 -04:00
. sg_tablesize = LIBATA_MAX_PRD ,
2005-04-16 15:20:36 -07:00
. 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-05-31 18:28:09 +09:00
. slave_destroy = ata_scsi_slave_destroy ,
2005-04-16 15:20:36 -07:00
. bios_param = ata_std_bios_param ,
} ;
2005-10-22 14:27:05 -04:00
static const struct ata_port_operations sis_ops = {
2005-04-16 15:20:36 -07:00
. 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 ,
. 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 ,
2007-02-01 15:06:36 +09:00
. data_xfer = ata_data_xfer ,
2006-06-16 15:00:18 +09:00
. freeze = ata_bmdma_freeze ,
. thaw = ata_bmdma_thaw ,
. error_handler = ata_bmdma_error_handler ,
. post_internal_cmd = ata_bmdma_post_internal_cmd ,
2005-04-16 15:20:36 -07:00
. irq_clear = ata_bmdma_irq_clear ,
2007-01-26 16:27:58 +09:00
. irq_on = ata_irq_on ,
2005-04-16 15:20:36 -07:00
. scr_read = sis_scr_read ,
. scr_write = sis_scr_write ,
. port_start = ata_port_start ,
} ;
2007-05-04 12:43:58 +02:00
static const struct ata_port_info sis_port_info = {
2006-08-24 03:19:22 -04:00
. flags = ATA_FLAG_SATA | ATA_FLAG_NO_LEGACY ,
2005-04-16 15:20:36 -07:00
. pio_mask = 0x1f ,
. mwdma_mask = 0x7 ,
2007-07-09 12:16:50 -04:00
. udma_mask = ATA_UDMA6 ,
2005-04-16 15:20:36 -07:00
. port_ops = & sis_ops ,
} ;
MODULE_AUTHOR ( " Uwe Koziolek " ) ;
MODULE_DESCRIPTION ( " low-level driver for Silicon Integratad Systems SATA controller " ) ;
MODULE_LICENSE ( " GPL " ) ;
MODULE_DEVICE_TABLE ( pci , sis_pci_tbl ) ;
MODULE_VERSION ( DRV_VERSION ) ;
2007-01-08 16:11:07 +00:00
static unsigned int get_scr_cfg_addr ( struct ata_port * ap , unsigned int sc_reg )
2005-04-16 15:20:36 -07:00
{
2007-01-08 16:11:07 +00:00
struct pci_dev * pdev = to_pci_dev ( ap - > host - > dev ) ;
2005-04-16 15:20:36 -07:00
unsigned int addr = SIS_SCR_BASE + ( 4 * sc_reg ) ;
2007-01-08 16:11:07 +00:00
u8 pmr ;
2005-04-16 15:20:36 -07:00
2007-01-08 16:11:07 +00:00
if ( ap - > port_no ) {
2006-12-04 01:34:42 +01:00
switch ( pdev - > device ) {
2007-10-26 00:03:37 -04:00
case 0x0180 :
case 0x0181 :
pci_read_config_byte ( pdev , SIS_PMR , & pmr ) ;
if ( ( pmr & SIS_PMR_COMBINED ) = = 0 )
addr + = SIS180_SATA1_OFS ;
break ;
case 0x0182 :
case 0x0183 :
case 0x1182 :
addr + = SIS182_SATA1_OFS ;
break ;
2006-12-04 01:34:42 +01:00
}
2005-09-08 23:07:29 -04:00
}
2005-04-16 15:20:36 -07:00
return addr ;
}
2007-10-26 00:03:37 -04:00
static u32 sis_scr_cfg_read ( struct ata_port * ap , unsigned int sc_reg , u32 * val )
2005-04-16 15:20:36 -07:00
{
2006-08-24 03:19:22 -04:00
struct pci_dev * pdev = to_pci_dev ( ap - > host - > dev ) ;
2007-01-08 16:11:07 +00:00
unsigned int cfg_addr = get_scr_cfg_addr ( ap , sc_reg ) ;
2007-10-18 11:53:39 +09:00
u32 val2 = 0 ;
2005-09-07 22:44:48 +02:00
u8 pmr ;
2005-04-16 15:20:36 -07:00
if ( sc_reg = = SCR_ERROR ) /* doesn't exist in PCI cfg space */
return 0xffffffff ;
2005-09-07 22:44:48 +02:00
pci_read_config_byte ( pdev , SIS_PMR , & pmr ) ;
2005-09-08 23:07:29 -04:00
2007-10-18 11:53:39 +09:00
pci_read_config_dword ( pdev , cfg_addr , val ) ;
2005-09-07 22:44:48 +02:00
2007-06-14 23:40:43 +02:00
if ( ( pdev - > device = = 0x0182 ) | | ( pdev - > device = = 0x0183 ) | |
( pdev - > device = = 0x1182 ) | | ( pmr & SIS_PMR_COMBINED ) )
2005-09-07 22:44:48 +02:00
pci_read_config_dword ( pdev , cfg_addr + 0x10 , & val2 ) ;
2007-10-18 11:53:39 +09:00
* val | = val2 ;
* val & = 0xfffffffb ; /* avoid problems with powerdowned ports */
return 0 ;
2005-04-16 15:20:36 -07:00
}
2007-10-26 00:03:37 -04:00
static void sis_scr_cfg_write ( struct ata_port * ap , unsigned int sc_reg , u32 val )
2005-04-16 15:20:36 -07:00
{
2006-08-24 03:19:22 -04:00
struct pci_dev * pdev = to_pci_dev ( ap - > host - > dev ) ;
2007-01-08 16:11:07 +00:00
unsigned int cfg_addr = get_scr_cfg_addr ( ap , sc_reg ) ;
2005-09-07 22:44:48 +02:00
u8 pmr ;
2005-04-16 15:20:36 -07:00
2007-01-08 16:11:07 +00:00
if ( sc_reg = = SCR_ERROR ) /* doesn't exist in PCI cfg space */
2005-04-16 15:20:36 -07:00
return ;
2005-09-07 22:44:48 +02:00
pci_read_config_byte ( pdev , SIS_PMR , & pmr ) ;
2005-09-08 23:07:29 -04:00
2005-04-16 15:20:36 -07:00
pci_write_config_dword ( pdev , cfg_addr , val ) ;
2005-09-07 22:44:48 +02:00
2007-06-14 23:40:43 +02:00
if ( ( pdev - > device = = 0x0182 ) | | ( pdev - > device = = 0x0183 ) | |
( pdev - > device = = 0x1182 ) | | ( pmr & SIS_PMR_COMBINED ) )
2005-09-07 22:44:48 +02:00
pci_write_config_dword ( pdev , cfg_addr + 0x10 , val ) ;
2005-04-16 15:20:36 -07:00
}
2007-07-16 14:29:40 +09:00
static int sis_scr_read ( struct ata_port * ap , unsigned int sc_reg , u32 * val )
2005-04-16 15:20:36 -07:00
{
2006-08-24 03:19:22 -04:00
struct pci_dev * pdev = to_pci_dev ( ap - > host - > dev ) ;
2005-09-07 22:44:48 +02:00
u8 pmr ;
2005-04-16 15:20:36 -07:00
if ( sc_reg > SCR_CONTROL )
2007-07-16 14:29:40 +09:00
return - EINVAL ;
2005-04-16 15:20:36 -07:00
if ( ap - > flags & SIS_FLAG_CFGSCR )
2007-10-18 11:53:39 +09:00
return sis_scr_cfg_read ( ap , sc_reg , val ) ;
2005-09-07 22:44:48 +02:00
pci_read_config_byte ( pdev , SIS_PMR , & pmr ) ;
2007-07-16 14:29:40 +09:00
* val = ioread32 ( ap - > ioaddr . scr_addr + ( sc_reg * 4 ) ) ;
2005-09-07 22:44:48 +02:00
2007-06-14 23:40:43 +02:00
if ( ( pdev - > device = = 0x0182 ) | | ( pdev - > device = = 0x0183 ) | |
( pdev - > device = = 0x1182 ) | | ( pmr & SIS_PMR_COMBINED ) )
2007-07-16 14:29:40 +09:00
* val | = ioread32 ( ap - > ioaddr . scr_addr + ( sc_reg * 4 ) + 0x10 ) ;
* val & = 0xfffffffb ;
2005-09-07 22:44:48 +02:00
2007-07-16 14:29:40 +09:00
return 0 ;
2005-04-16 15:20:36 -07:00
}
2007-07-16 14:29:40 +09:00
static int sis_scr_write ( struct ata_port * ap , unsigned int sc_reg , u32 val )
2005-04-16 15:20:36 -07:00
{
2006-08-24 03:19:22 -04:00
struct pci_dev * pdev = to_pci_dev ( ap - > host - > dev ) ;
2005-09-07 22:44:48 +02:00
u8 pmr ;
2005-04-16 15:20:36 -07:00
if ( sc_reg > SCR_CONTROL )
2007-07-16 14:29:40 +09:00
return - EINVAL ;
2005-04-16 15:20:36 -07:00
2005-09-07 22:44:48 +02:00
pci_read_config_byte ( pdev , SIS_PMR , & pmr ) ;
2005-09-08 23:07:29 -04:00
2005-04-16 15:20:36 -07:00
if ( ap - > flags & SIS_FLAG_CFGSCR )
sis_scr_cfg_write ( ap , sc_reg , val ) ;
2005-09-07 22:44:48 +02:00
else {
2007-02-01 15:06:36 +09:00
iowrite32 ( val , ap - > ioaddr . scr_addr + ( sc_reg * 4 ) ) ;
2007-06-14 23:40:43 +02:00
if ( ( pdev - > device = = 0x0182 ) | | ( pdev - > device = = 0x0183 ) | |
( pdev - > device = = 0x1182 ) | | ( pmr & SIS_PMR_COMBINED ) )
2007-02-01 15:06:36 +09:00
iowrite32 ( val , ap - > ioaddr . scr_addr + ( sc_reg * 4 ) + 0x10 ) ;
2005-09-07 22:44:48 +02:00
}
2007-07-16 14:29:40 +09:00
return 0 ;
2005-04-16 15:20:36 -07:00
}
2007-10-26 00:03:37 -04:00
static int sis_init_one ( struct pci_dev * pdev , const struct pci_device_id * ent )
2005-04-16 15:20:36 -07:00
{
2005-10-30 14:39:11 -05:00
static int printed_version ;
2007-04-17 23:44:08 +09:00
struct ata_port_info pi = sis_port_info ;
2007-05-25 09:48:52 +02:00
const struct ata_port_info * ppi [ ] = { & pi , & pi } ;
2007-04-17 23:44:08 +09:00
struct ata_host * host ;
2006-11-08 09:57:00 +01:00
u32 genctl , val ;
2005-09-07 22:44:48 +02:00
u8 pmr ;
2006-12-04 01:34:42 +01:00
u8 port2_start = 0x20 ;
2007-04-17 23:44:08 +09:00
int rc ;
2005-04-16 15:20:36 -07:00
2005-10-30 14:39:11 -05:00
if ( ! printed_version + + )
dev_printk ( KERN_INFO , & pdev - > dev , " version " DRV_VERSION " \n " ) ;
2007-01-20 16:00:28 +09:00
rc = pcim_enable_device ( pdev ) ;
2005-04-16 15:20:36 -07:00
if ( rc )
return rc ;
/* check and see if the SCRs are in IO space or PCI cfg space */
pci_read_config_dword ( pdev , SIS_GENCTL , & genctl ) ;
if ( ( genctl & GENCTL_IOMAPPED_SCR ) = = 0 )
2006-10-27 19:08:47 -07:00
pi . flags | = SIS_FLAG_CFGSCR ;
2005-07-31 13:13:24 -04:00
2005-04-16 15:20:36 -07:00
/* if hardware thinks SCRs are in IO space, but there are
* no IO resources assigned , change to PCI cfg space .
*/
2006-10-27 19:08:47 -07:00
if ( ( ! ( pi . flags & SIS_FLAG_CFGSCR ) ) & &
2005-04-16 15:20:36 -07:00
( ( pci_resource_start ( pdev , SIS_SCR_PCI_BAR ) = = 0 ) | |
( pci_resource_len ( pdev , SIS_SCR_PCI_BAR ) < 128 ) ) ) {
genctl & = ~ GENCTL_IOMAPPED_SCR ;
pci_write_config_dword ( pdev , SIS_GENCTL , genctl ) ;
2006-10-27 19:08:47 -07:00
pi . flags | = SIS_FLAG_CFGSCR ;
2005-04-16 15:20:36 -07:00
}
2005-09-07 22:44:48 +02:00
pci_read_config_byte ( pdev , SIS_PMR , & pmr ) ;
2006-12-04 01:34:42 +01:00
switch ( ent - > device ) {
case 0x0180 :
case 0x0181 :
2007-01-08 16:11:07 +00:00
/* The PATA-handling is provided by pata_sis */
switch ( pmr & 0x30 ) {
case 0x10 :
2007-06-14 23:40:43 +02:00
ppi [ 1 ] = & sis_info133_for_sata ;
2007-01-08 16:11:07 +00:00
break ;
2007-02-26 05:51:33 -05:00
2007-01-08 16:11:07 +00:00
case 0x30 :
2007-06-14 23:40:43 +02:00
ppi [ 0 ] = & sis_info133_for_sata ;
2007-01-08 16:11:07 +00:00
break ;
}
2005-09-07 22:44:48 +02:00
if ( ( pmr & SIS_PMR_COMBINED ) = = 0 ) {
2005-10-30 14:39:11 -05:00
dev_printk ( KERN_INFO , & pdev - > dev ,
2006-11-08 09:57:00 +01:00
" Detected SiS 180/181/964 chipset in SATA mode \n " ) ;
2005-09-13 00:36:45 +02:00
port2_start = 64 ;
2006-12-04 01:34:42 +01:00
} else {
2005-10-30 14:39:11 -05:00
dev_printk ( KERN_INFO , & pdev - > dev ,
" Detected SiS 180/181 chipset in combined mode \n " ) ;
2007-10-26 00:03:37 -04:00
port2_start = 0 ;
2006-11-08 09:57:00 +01:00
pi . flags | = ATA_FLAG_SLAVE_POSS ;
2005-09-07 22:44:48 +02:00
}
2006-12-04 01:34:42 +01:00
break ;
2006-12-11 11:14:06 -05:00
2006-12-04 01:34:42 +01:00
case 0x0182 :
case 0x0183 :
2007-10-26 00:03:37 -04:00
pci_read_config_dword ( pdev , 0x6C , & val ) ;
2006-11-08 09:57:00 +01:00
if ( val & ( 1L < < 31 ) ) {
2007-10-26 00:03:37 -04:00
dev_printk ( KERN_INFO , & pdev - > dev ,
" Detected SiS 182/965 chipset \n " ) ;
2006-11-08 09:57:00 +01:00
pi . flags | = ATA_FLAG_SLAVE_POSS ;
2006-12-04 01:34:42 +01:00
} else {
2007-10-26 00:03:37 -04:00
dev_printk ( KERN_INFO , & pdev - > dev ,
" Detected SiS 182/965L chipset \n " ) ;
2006-12-04 01:34:42 +01:00
}
break ;
case 0x1182 :
2007-10-26 00:03:37 -04:00
dev_printk ( KERN_INFO , & pdev - > dev ,
" Detected SiS 1182/966/680 SATA controller \n " ) ;
2007-06-14 23:40:43 +02:00
pi . flags | = ATA_FLAG_SLAVE_POSS ;
break ;
2006-12-04 01:34:42 +01:00
case 0x1183 :
2007-10-26 00:03:37 -04:00
dev_printk ( KERN_INFO , & pdev - > dev ,
" Detected SiS 1183/966/966L/968/680 controller in PATA mode \n " ) ;
2007-06-14 23:40:43 +02:00
ppi [ 0 ] = & sis_info133_for_sata ;
ppi [ 1 ] = & sis_info133_for_sata ;
2006-12-04 01:34:42 +01:00
break ;
2005-09-07 22:44:48 +02:00
}
2007-07-04 18:02:07 +09:00
rc = ata_pci_prepare_sff_host ( pdev , ppi , & host ) ;
2007-04-17 23:44:08 +09:00
if ( rc )
return rc ;
2006-10-27 19:08:47 -07:00
2007-04-17 23:44:08 +09:00
if ( ! ( pi . flags & SIS_FLAG_CFGSCR ) ) {
2007-03-14 09:19:00 +00:00
void __iomem * mmio ;
2007-02-01 15:06:36 +09:00
2007-04-17 23:44:08 +09:00
rc = pcim_iomap_regions ( pdev , 1 < < SIS_SCR_PCI_BAR , DRV_NAME ) ;
if ( rc )
return rc ;
mmio = host - > iomap [ SIS_SCR_PCI_BAR ] ;
2007-02-01 15:06:36 +09:00
2007-04-17 23:44:08 +09:00
host - > ports [ 0 ] - > ioaddr . scr_addr = mmio ;
host - > ports [ 1 ] - > ioaddr . scr_addr = mmio + port2_start ;
2005-04-16 15:20:36 -07:00
}
pci_set_master ( pdev ) ;
2005-08-15 15:23:41 -04:00
pci_intx ( pdev , 1 ) ;
2007-04-17 23:44:08 +09:00
return ata_host_activate ( host , pdev - > irq , ata_interrupt , IRQF_SHARED ,
& sis_sht ) ;
2005-04-16 15:20:36 -07:00
}
static int __init sis_init ( void )
{
2006-08-10 18:13:18 +09:00
return pci_register_driver ( & sis_pci_driver ) ;
2005-04-16 15:20:36 -07:00
}
static void __exit sis_exit ( void )
{
pci_unregister_driver ( & sis_pci_driver ) ;
}
module_init ( sis_init ) ;
module_exit ( sis_exit ) ;