2005-04-17 02:20:36 +04:00
/*
* sata_uli . c - ULi Electronics SATA
*
2005-08-29 04: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-17 02:20:36 +04: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 22:39:11 +03:00
# include <linux/device.h>
2005-04-17 02:20:36 +04:00
# include <scsi/scsi_host.h>
# include <linux/libata.h>
# define DRV_NAME "sata_uli"
2007-05-22 04:26:38 +04:00
# define DRV_VERSION "1.2"
2005-04-17 02:20:36 +04:00
enum {
uli_5289 = 0 ,
uli_5287 = 1 ,
uli_5281 = 2 ,
2006-03-23 08:14:36 +03:00
uli_max_ports = 4 ,
2005-04-17 02:20:36 +04:00
/* PCI configuration registers */
ULI5287_BASE = 0x90 , /* sata0 phy SCR registers */
ULI5287_OFFS = 0x10 , /* offset from sata0->sata1 phy regs */
ULI5281_BASE = 0x60 , /* sata0 phy SCR registers */
ULI5281_OFFS = 0x60 , /* offset from sata0->sata1 phy regs */
} ;
2006-03-23 08:14:36 +03:00
struct uli_priv {
unsigned int scr_cfg_addr [ uli_max_ports ] ;
} ;
2005-04-17 02:20:36 +04:00
static int uli_init_one ( struct pci_dev * pdev , const struct pci_device_id * ent ) ;
2007-07-16 09:29:40 +04:00
static int uli_scr_read ( struct ata_port * ap , unsigned int sc_reg , u32 * val ) ;
static int uli_scr_write ( struct ata_port * ap , unsigned int sc_reg , u32 val ) ;
2005-04-17 02:20:36 +04:00
2005-11-10 19:04:11 +03:00
static const struct pci_device_id uli_pci_tbl [ ] = {
2006-09-28 06:20:11 +04:00
{ PCI_VDEVICE ( AL , 0x5289 ) , uli_5289 } ,
{ PCI_VDEVICE ( AL , 0x5287 ) , uli_5287 } ,
{ PCI_VDEVICE ( AL , 0x5281 ) , uli_5281 } ,
2005-04-17 02:20:36 +04:00
{ } /* terminate list */
} ;
static struct pci_driver uli_pci_driver = {
. name = DRV_NAME ,
. id_table = uli_pci_tbl ,
. probe = uli_init_one ,
. remove = ata_pci_remove_one ,
} ;
2005-11-07 08:59:37 +03:00
static struct scsi_host_template uli_sht = {
2005-04-17 02:20:36 +04: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 ,
. sg_tablesize = LIBATA_MAX_PRD ,
. 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 13:28:09 +04:00
. slave_destroy = ata_scsi_slave_destroy ,
2005-04-17 02:20:36 +04:00
. bios_param = ata_std_bios_param ,
} ;
2005-10-22 22:27:05 +04:00
static const struct ata_port_operations uli_ops = {
2005-04-17 02:20:36 +04:00
. port_disable = ata_port_disable ,
. 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 09:06:36 +03:00
. data_xfer = ata_data_xfer ,
2005-04-17 02:20:36 +04:00
2006-06-16 10:00:18 +04: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-17 02:20:36 +04:00
. irq_clear = ata_bmdma_irq_clear ,
2007-01-26 10:27:58 +03:00
. irq_on = ata_irq_on ,
. irq_ack = ata_irq_ack ,
2005-04-17 02:20:36 +04:00
. scr_read = uli_scr_read ,
. scr_write = uli_scr_write ,
. port_start = ata_port_start ,
} ;
2007-05-04 14:43:58 +04:00
static const struct ata_port_info uli_port_info = {
2007-01-25 13:40:05 +03:00
. flags = ATA_FLAG_SATA | ATA_FLAG_NO_LEGACY |
ATA_FLAG_IGN_SIMPLEX ,
2005-09-02 05:53:34 +04:00
. pio_mask = 0x1f , /* pio0-4 */
2007-07-09 20:16:50 +04:00
. udma_mask = ATA_UDMA6 ,
2005-04-17 02:20:36 +04:00
. port_ops = & uli_ops ,
} ;
MODULE_AUTHOR ( " Peer Chen " ) ;
MODULE_DESCRIPTION ( " low-level driver for ULi Electronics SATA controller " ) ;
MODULE_LICENSE ( " GPL " ) ;
MODULE_DEVICE_TABLE ( pci , uli_pci_tbl ) ;
MODULE_VERSION ( DRV_VERSION ) ;
static unsigned int get_scr_cfg_addr ( struct ata_port * ap , unsigned int sc_reg )
{
2006-08-24 11:19:22 +04:00
struct uli_priv * hpriv = ap - > host - > private_data ;
2006-03-23 08:14:36 +03:00
return hpriv - > scr_cfg_addr [ ap - > port_no ] + ( 4 * sc_reg ) ;
2005-04-17 02:20:36 +04:00
}
static u32 uli_scr_cfg_read ( struct ata_port * ap , unsigned int sc_reg )
{
2006-08-24 11:19:22 +04:00
struct pci_dev * pdev = to_pci_dev ( ap - > host - > dev ) ;
2005-04-17 02:20:36 +04:00
unsigned int cfg_addr = get_scr_cfg_addr ( ap , sc_reg ) ;
u32 val ;
pci_read_config_dword ( pdev , cfg_addr , & val ) ;
return val ;
}
static void uli_scr_cfg_write ( struct ata_port * ap , unsigned int scr , u32 val )
{
2006-08-24 11:19:22 +04:00
struct pci_dev * pdev = to_pci_dev ( ap - > host - > dev ) ;
2005-04-17 02:20:36 +04:00
unsigned int cfg_addr = get_scr_cfg_addr ( ap , scr ) ;
pci_write_config_dword ( pdev , cfg_addr , val ) ;
}
2007-07-16 09:29:40 +04:00
static int uli_scr_read ( struct ata_port * ap , unsigned int sc_reg , u32 * val )
2005-04-17 02:20:36 +04:00
{
if ( sc_reg > SCR_CONTROL )
2007-07-16 09:29:40 +04:00
return - EINVAL ;
2005-04-17 02:20:36 +04:00
2007-07-16 09:29:40 +04:00
* val = uli_scr_cfg_read ( ap , sc_reg ) ;
return 0 ;
2005-04-17 02:20:36 +04:00
}
2007-07-16 09:29:40 +04:00
static int uli_scr_write ( struct ata_port * ap , unsigned int sc_reg , u32 val )
2005-04-17 02:20:36 +04:00
{
if ( sc_reg > SCR_CONTROL ) //SCR_CONTROL=2, SCR_ERROR=1, SCR_STATUS=0
2007-07-16 09:29:40 +04:00
return - EINVAL ;
2005-04-17 02:20:36 +04:00
uli_scr_cfg_write ( ap , sc_reg , val ) ;
2007-07-16 09:29:40 +04:00
return 0 ;
2005-04-17 02:20:36 +04:00
}
static int uli_init_one ( struct pci_dev * pdev , const struct pci_device_id * ent )
{
2005-10-30 22:39:11 +03:00
static int printed_version ;
2007-04-17 18:44:08 +04:00
const struct ata_port_info * ppi [ ] = { & uli_port_info , NULL } ;
2005-04-17 02:20:36 +04:00
unsigned int board_idx = ( unsigned int ) ent - > driver_data ;
2007-04-17 18:44:08 +04:00
struct ata_host * host ;
2006-03-23 08:14:36 +03:00
struct uli_priv * hpriv ;
2007-02-01 09:06:36 +03:00
void __iomem * const * iomap ;
2007-04-17 18:44:08 +04:00
struct ata_ioports * ioaddr ;
int n_ports , rc ;
2005-04-17 02:20:36 +04:00
2005-10-30 22:39:11 +03:00
if ( ! printed_version + + )
dev_printk ( KERN_INFO , & pdev - > dev , " version " DRV_VERSION " \n " ) ;
2007-01-20 10:00:28 +03:00
rc = pcim_enable_device ( pdev ) ;
2005-04-17 02:20:36 +04:00
if ( rc )
return rc ;
2007-04-17 18:44:08 +04:00
n_ports = 2 ;
if ( board_idx = = uli_5287 )
n_ports = 4 ;
2007-05-04 14:43:58 +04:00
/* allocate the host */
host = ata_host_alloc_pinfo ( & pdev - > dev , ppi , n_ports ) ;
if ( ! host )
return - ENOMEM ;
2005-04-17 02:20:36 +04:00
2007-01-20 10:00:28 +03:00
hpriv = devm_kzalloc ( & pdev - > dev , sizeof ( * hpriv ) , GFP_KERNEL ) ;
if ( ! hpriv )
return - ENOMEM ;
2007-04-17 18:44:08 +04:00
host - > private_data = hpriv ;
2006-03-23 08:14:36 +03:00
2007-05-04 14:43:58 +04:00
/* the first two ports are standard SFF */
2007-07-04 13:02:07 +04:00
rc = ata_pci_init_sff_host ( host ) ;
2007-05-04 14:43:58 +04:00
if ( rc )
return rc ;
rc = ata_pci_init_bmdma ( host ) ;
if ( rc )
return rc ;
2007-04-17 18:44:08 +04:00
iomap = host - > iomap ;
2007-02-01 09:06:36 +03:00
2005-04-17 02:20:36 +04:00
switch ( board_idx ) {
case uli_5287 :
2007-05-04 14:43:58 +04:00
/* If there are four, the last two live right after
* the standard SFF ports .
*/
2006-03-23 08:14:36 +03:00
hpriv - > scr_cfg_addr [ 0 ] = ULI5287_BASE ;
hpriv - > scr_cfg_addr [ 1 ] = ULI5287_BASE + ULI5287_OFFS ;
2005-04-17 02:20:36 +04:00
2007-04-17 18:44:08 +04:00
ioaddr = & host - > ports [ 2 ] - > ioaddr ;
ioaddr - > cmd_addr = iomap [ 0 ] + 8 ;
ioaddr - > altstatus_addr =
ioaddr - > ctl_addr = ( void __iomem * )
2007-02-01 09:06:36 +03:00
( ( unsigned long ) iomap [ 1 ] | ATA_PCI_CTL_OFS ) + 4 ;
2007-04-17 18:44:08 +04:00
ioaddr - > bmdma_addr = iomap [ 4 ] + 16 ;
2006-03-23 08:14:36 +03:00
hpriv - > scr_cfg_addr [ 2 ] = ULI5287_BASE + ULI5287_OFFS * 4 ;
2007-04-17 18:44:08 +04:00
ata_std_ports ( ioaddr ) ;
2005-04-17 02:20:36 +04:00
2007-04-17 18:44:08 +04:00
ioaddr = & host - > ports [ 3 ] - > ioaddr ;
ioaddr - > cmd_addr = iomap [ 2 ] + 8 ;
ioaddr - > altstatus_addr =
ioaddr - > ctl_addr = ( void __iomem * )
2007-02-01 09:06:36 +03:00
( ( unsigned long ) iomap [ 3 ] | ATA_PCI_CTL_OFS ) + 4 ;
2007-04-17 18:44:08 +04:00
ioaddr - > bmdma_addr = iomap [ 4 ] + 24 ;
2006-03-23 08:14:36 +03:00
hpriv - > scr_cfg_addr [ 3 ] = ULI5287_BASE + ULI5287_OFFS * 5 ;
2007-04-17 18:44:08 +04:00
ata_std_ports ( ioaddr ) ;
2005-04-17 02:20:36 +04:00
break ;
case uli_5289 :
2006-03-23 08:14:36 +03:00
hpriv - > scr_cfg_addr [ 0 ] = ULI5287_BASE ;
hpriv - > scr_cfg_addr [ 1 ] = ULI5287_BASE + ULI5287_OFFS ;
2005-04-17 02:20:36 +04:00
break ;
case uli_5281 :
2006-03-23 08:14:36 +03:00
hpriv - > scr_cfg_addr [ 0 ] = ULI5281_BASE ;
hpriv - > scr_cfg_addr [ 1 ] = ULI5281_BASE + ULI5281_OFFS ;
2005-04-17 02:20:36 +04:00
break ;
default :
BUG ( ) ;
break ;
}
pci_set_master ( pdev ) ;
2005-08-15 23:23:41 +04:00
pci_intx ( pdev , 1 ) ;
2007-04-17 18:44:08 +04:00
return ata_host_activate ( host , pdev - > irq , ata_interrupt , IRQF_SHARED ,
& uli_sht ) ;
2005-04-17 02:20:36 +04:00
}
static int __init uli_init ( void )
{
2006-08-10 13:13:18 +04:00
return pci_register_driver ( & uli_pci_driver ) ;
2005-04-17 02:20:36 +04:00
}
static void __exit uli_exit ( void )
{
pci_unregister_driver ( & uli_pci_driver ) ;
}
module_init ( uli_init ) ;
module_exit ( uli_exit ) ;