2005-04-17 02:20:36 +04:00
/*
* sata_qstor . c - Pacific Digital Corporation QStor SATA
*
* Maintained by : Mark Lord < mlord @ pobox . com >
*
* Copyright 2005 Pacific Digital Corporation .
* ( OSL / GPL code release authorized by Jalil Fadavi ) .
*
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 . *
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>
# include <linux/sched.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 <asm/io.h>
# include <linux/libata.h>
# define DRV_NAME "sata_qstor"
2005-11-16 19:06:59 +03:00
# define DRV_VERSION "0.05"
2005-04-17 02:20:36 +04:00
enum {
QS_PORTS = 4 ,
QS_MAX_PRD = LIBATA_MAX_PRD ,
QS_CPB_ORDER = 6 ,
QS_CPB_BYTES = ( 1 < < QS_CPB_ORDER ) ,
QS_PRD_BYTES = QS_MAX_PRD * 16 ,
QS_PKT_BYTES = QS_CPB_BYTES + QS_PRD_BYTES ,
/* global register offsets */
QS_HCF_CNFG3 = 0x0003 , /* host configuration offset */
QS_HID_HPHY = 0x0004 , /* host physical interface info */
QS_HCT_CTRL = 0x00e4 , /* global interrupt mask offset */
QS_HST_SFF = 0x0100 , /* host status fifo offset */
QS_HVS_SERD3 = 0x0393 , /* PHY enable offset */
/* global control bits */
QS_HPHY_64BIT = ( 1 < < 1 ) , /* 64-bit bus detected */
QS_CNFG3_GSRST = 0x01 , /* global chip reset */
QS_SERD3_PHY_ENA = 0xf0 , /* PHY detection ENAble*/
/* per-channel register offsets */
QS_CCF_CPBA = 0x0710 , /* chan CPB base address */
QS_CCF_CSEP = 0x0718 , /* chan CPB separation factor */
QS_CFC_HUFT = 0x0800 , /* host upstream fifo threshold */
QS_CFC_HDFT = 0x0804 , /* host downstream fifo threshold */
QS_CFC_DUFT = 0x0808 , /* dev upstream fifo threshold */
QS_CFC_DDFT = 0x080c , /* dev downstream fifo threshold */
QS_CCT_CTR0 = 0x0900 , /* chan control-0 offset */
QS_CCT_CTR1 = 0x0901 , /* chan control-1 offset */
QS_CCT_CFF = 0x0a00 , /* chan command fifo offset */
/* channel control bits */
QS_CTR0_REG = ( 1 < < 1 ) , /* register mode (vs. pkt mode) */
QS_CTR0_CLER = ( 1 < < 2 ) , /* clear channel errors */
QS_CTR1_RDEV = ( 1 < < 1 ) , /* sata phy/comms reset */
QS_CTR1_RCHN = ( 1 < < 4 ) , /* reset channel logic */
QS_CCF_RUN_PKT = 0x107 , /* RUN a new dma PKT */
/* pkt sub-field headers */
QS_HCB_HDR = 0x01 , /* Host Control Block header */
QS_DCB_HDR = 0x02 , /* Device Control Block header */
/* pkt HCB flag bits */
QS_HF_DIRO = ( 1 < < 0 ) , /* data DIRection Out */
QS_HF_DAT = ( 1 < < 3 ) , /* DATa pkt */
QS_HF_IEN = ( 1 < < 4 ) , /* Interrupt ENable */
QS_HF_VLD = ( 1 < < 5 ) , /* VaLiD pkt */
/* pkt DCB flag bits */
QS_DF_PORD = ( 1 < < 2 ) , /* Pio OR Dma */
QS_DF_ELBA = ( 1 < < 3 ) , /* Extended LBA (lba48) */
/* PCI device IDs */
board_2068_idx = 0 , /* QStor 4-port SATA/RAID */
} ;
2005-10-21 09:46:02 +04:00
enum {
QS_DMA_BOUNDARY = ~ 0UL
} ;
2005-04-17 02:20:36 +04:00
typedef enum { qs_state_idle , qs_state_pkt , qs_state_mmio } qs_state_t ;
struct qs_port_priv {
u8 * pkt ;
dma_addr_t pkt_dma ;
qs_state_t state ;
} ;
static u32 qs_scr_read ( struct ata_port * ap , unsigned int sc_reg ) ;
static void qs_scr_write ( struct ata_port * ap , unsigned int sc_reg , u32 val ) ;
static int qs_ata_init_one ( struct pci_dev * pdev , const struct pci_device_id * ent ) ;
static irqreturn_t qs_intr ( int irq , void * dev_instance , struct pt_regs * regs ) ;
static int qs_port_start ( struct ata_port * ap ) ;
static void qs_host_stop ( struct ata_host_set * host_set ) ;
static void qs_port_stop ( struct ata_port * ap ) ;
static void qs_phy_reset ( struct ata_port * ap ) ;
static void qs_qc_prep ( struct ata_queued_cmd * qc ) ;
2006-01-23 07:09:36 +03:00
static unsigned int qs_qc_issue ( struct ata_queued_cmd * qc ) ;
2005-04-17 02:20:36 +04:00
static int qs_check_atapi_dma ( struct ata_queued_cmd * qc ) ;
2005-08-26 19:03:19 +04:00
static void qs_bmdma_stop ( struct ata_queued_cmd * qc ) ;
2005-04-17 02:20:36 +04:00
static u8 qs_bmdma_status ( struct ata_port * ap ) ;
static void qs_irq_clear ( struct ata_port * ap ) ;
static void qs_eng_timeout ( struct ata_port * ap ) ;
2005-11-07 08:59:37 +03:00
static struct scsi_host_template qs_ata_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 = QS_MAX_PRD ,
. cmd_per_lun = ATA_SHT_CMD_PER_LUN ,
. emulated = ATA_SHT_EMULATED ,
//FIXME .use_clustering = ATA_SHT_USE_CLUSTERING,
. use_clustering = ENABLE_CLUSTERING ,
. proc_name = DRV_NAME ,
. dma_boundary = QS_DMA_BOUNDARY ,
. slave_configure = ata_scsi_slave_config ,
. bios_param = ata_std_bios_param ,
} ;
2005-10-22 22:27:05 +04:00
static const struct ata_port_operations qs_ata_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 ,
. check_atapi_dma = qs_check_atapi_dma ,
. exec_command = ata_exec_command ,
. dev_select = ata_std_dev_select ,
. phy_reset = qs_phy_reset ,
. qc_prep = qs_qc_prep ,
. qc_issue = qs_qc_issue ,
. eng_timeout = qs_eng_timeout ,
. irq_handler = qs_intr ,
. irq_clear = qs_irq_clear ,
. scr_read = qs_scr_read ,
. scr_write = qs_scr_write ,
. port_start = qs_port_start ,
. port_stop = qs_port_stop ,
. host_stop = qs_host_stop ,
. bmdma_stop = qs_bmdma_stop ,
. bmdma_status = qs_bmdma_status ,
} ;
2005-11-28 12:06:23 +03:00
static const struct ata_port_info qs_port_info [ ] = {
2005-04-17 02:20:36 +04:00
/* board_2068_idx */
{
. sht = & qs_ata_sht ,
. host_flags = ATA_FLAG_SATA | ATA_FLAG_NO_LEGACY |
ATA_FLAG_SATA_RESET |
//FIXME ATA_FLAG_SRST |
ATA_FLAG_MMIO ,
. pio_mask = 0x10 , /* pio4 */
. udma_mask = 0x7f , /* udma0-6 */
. port_ops = & qs_ata_ops ,
} ,
} ;
2005-11-10 19:04:11 +03:00
static const struct pci_device_id qs_ata_pci_tbl [ ] = {
2005-04-17 02:20:36 +04:00
{ PCI_VENDOR_ID_PDC , 0x2068 , PCI_ANY_ID , PCI_ANY_ID , 0 , 0 ,
board_2068_idx } ,
{ } /* terminate list */
} ;
static struct pci_driver qs_ata_pci_driver = {
. name = DRV_NAME ,
. id_table = qs_ata_pci_tbl ,
. probe = qs_ata_init_one ,
. remove = ata_pci_remove_one ,
} ;
static int qs_check_atapi_dma ( struct ata_queued_cmd * qc )
{
return 1 ; /* ATAPI DMA not supported */
}
2005-08-27 12:13:52 +04:00
static void qs_bmdma_stop ( struct ata_queued_cmd * qc )
2005-04-17 02:20:36 +04:00
{
/* nothing */
}
static u8 qs_bmdma_status ( struct ata_port * ap )
{
return 0 ;
}
static void qs_irq_clear ( struct ata_port * ap )
{
/* nothing */
}
static inline void qs_enter_reg_mode ( struct ata_port * ap )
{
u8 __iomem * chan = ap - > host_set - > mmio_base + ( ap - > port_no * 0x4000 ) ;
writeb ( QS_CTR0_REG , chan + QS_CCT_CTR0 ) ;
readb ( chan + QS_CCT_CTR0 ) ; /* flush */
}
static inline void qs_reset_channel_logic ( struct ata_port * ap )
{
u8 __iomem * chan = ap - > host_set - > mmio_base + ( ap - > port_no * 0x4000 ) ;
writeb ( QS_CTR1_RCHN , chan + QS_CCT_CTR1 ) ;
readb ( chan + QS_CCT_CTR0 ) ; /* flush */
qs_enter_reg_mode ( ap ) ;
}
static void qs_phy_reset ( struct ata_port * ap )
{
struct qs_port_priv * pp = ap - > private_data ;
pp - > state = qs_state_idle ;
qs_reset_channel_logic ( ap ) ;
sata_phy_reset ( ap ) ;
}
static void qs_eng_timeout ( struct ata_port * ap )
{
struct qs_port_priv * pp = ap - > private_data ;
if ( pp - > state ! = qs_state_idle ) /* healthy paranoia */
pp - > state = qs_state_mmio ;
qs_reset_channel_logic ( ap ) ;
ata_eng_timeout ( ap ) ;
}
static u32 qs_scr_read ( struct ata_port * ap , unsigned int sc_reg )
{
if ( sc_reg > SCR_CONTROL )
return ~ 0U ;
return readl ( ( void __iomem * ) ( ap - > ioaddr . scr_addr + ( sc_reg * 8 ) ) ) ;
}
static void qs_scr_write ( struct ata_port * ap , unsigned int sc_reg , u32 val )
{
if ( sc_reg > SCR_CONTROL )
return ;
writel ( val , ( void __iomem * ) ( ap - > ioaddr . scr_addr + ( sc_reg * 8 ) ) ) ;
}
2005-11-12 09:27:07 +03:00
static unsigned int qs_fill_sg ( struct ata_queued_cmd * qc )
2005-04-17 02:20:36 +04:00
{
2005-10-05 15:13:30 +04:00
struct scatterlist * sg ;
2005-04-17 02:20:36 +04:00
struct ata_port * ap = qc - > ap ;
struct qs_port_priv * pp = ap - > private_data ;
unsigned int nelem ;
u8 * prd = pp - > pkt + QS_CPB_BYTES ;
2006-02-11 13:11:13 +03:00
WARN_ON ( qc - > __sg = = NULL ) ;
2006-02-21 00:55:56 +03:00
WARN_ON ( qc - > n_elem = = 0 & & qc - > pad_len = = 0 ) ;
2005-04-17 02:20:36 +04:00
2005-10-05 15:13:30 +04:00
nelem = 0 ;
ata_for_each_sg ( sg , qc ) {
2005-04-17 02:20:36 +04:00
u64 addr ;
u32 len ;
addr = sg_dma_address ( sg ) ;
* ( __le64 * ) prd = cpu_to_le64 ( addr ) ;
prd + = sizeof ( u64 ) ;
len = sg_dma_len ( sg ) ;
* ( __le32 * ) prd = cpu_to_le32 ( len ) ;
prd + = sizeof ( u64 ) ;
VPRINTK ( " PRD[%u] = (0x%llX, 0x%X) \n " , nelem ,
( unsigned long long ) addr , len ) ;
2005-10-05 15:13:30 +04:00
nelem + + ;
2005-04-17 02:20:36 +04:00
}
2005-11-12 09:27:07 +03:00
return nelem ;
2005-04-17 02:20:36 +04:00
}
static void qs_qc_prep ( struct ata_queued_cmd * qc )
{
struct qs_port_priv * pp = qc - > ap - > private_data ;
u8 dflags = QS_DF_PORD , * buf = pp - > pkt ;
u8 hflags = QS_HF_DAT | QS_HF_IEN | QS_HF_VLD ;
u64 addr ;
2005-11-12 09:27:07 +03:00
unsigned int nelem ;
2005-04-17 02:20:36 +04:00
VPRINTK ( " ENTER \n " ) ;
qs_enter_reg_mode ( qc - > ap ) ;
if ( qc - > tf . protocol ! = ATA_PROT_DMA ) {
ata_qc_prep ( qc ) ;
return ;
}
2005-11-12 09:27:07 +03:00
nelem = qs_fill_sg ( qc ) ;
2005-04-17 02:20:36 +04:00
if ( ( qc - > tf . flags & ATA_TFLAG_WRITE ) )
hflags | = QS_HF_DIRO ;
if ( ( qc - > tf . flags & ATA_TFLAG_LBA48 ) )
dflags | = QS_DF_ELBA ;
/* host control block (HCB) */
buf [ 0 ] = QS_HCB_HDR ;
buf [ 1 ] = hflags ;
* ( __le32 * ) ( & buf [ 4 ] ) = cpu_to_le32 ( qc - > nsect * ATA_SECT_SIZE ) ;
2005-11-12 09:27:07 +03:00
* ( __le32 * ) ( & buf [ 8 ] ) = cpu_to_le32 ( nelem ) ;
2005-04-17 02:20:36 +04:00
addr = ( ( u64 ) pp - > pkt_dma ) + QS_CPB_BYTES ;
* ( __le64 * ) ( & buf [ 16 ] ) = cpu_to_le64 ( addr ) ;
/* device control block (DCB) */
buf [ 24 ] = QS_DCB_HDR ;
buf [ 28 ] = dflags ;
/* frame information structure (FIS) */
ata_tf_to_fis ( & qc - > tf , & buf [ 32 ] , 0 ) ;
}
static inline void qs_packet_start ( struct ata_queued_cmd * qc )
{
struct ata_port * ap = qc - > ap ;
u8 __iomem * chan = ap - > host_set - > mmio_base + ( ap - > port_no * 0x4000 ) ;
VPRINTK ( " ENTER, ap %p \n " , ap ) ;
writeb ( QS_CTR0_CLER , chan + QS_CCT_CTR0 ) ;
wmb ( ) ; /* flush PRDs and pkt to memory */
writel ( QS_CCF_RUN_PKT , chan + QS_CCT_CFF ) ;
readl ( chan + QS_CCT_CFF ) ; /* flush */
}
2006-01-23 07:09:36 +03:00
static unsigned int qs_qc_issue ( struct ata_queued_cmd * qc )
2005-04-17 02:20:36 +04:00
{
struct qs_port_priv * pp = qc - > ap - > private_data ;
switch ( qc - > tf . protocol ) {
case ATA_PROT_DMA :
pp - > state = qs_state_pkt ;
qs_packet_start ( qc ) ;
return 0 ;
case ATA_PROT_ATAPI_DMA :
BUG ( ) ;
break ;
default :
break ;
}
pp - > state = qs_state_mmio ;
return ata_qc_issue_prot ( qc ) ;
}
static inline unsigned int qs_intr_pkt ( struct ata_host_set * host_set )
{
unsigned int handled = 0 ;
u8 sFFE ;
u8 __iomem * mmio_base = host_set - > mmio_base ;
do {
u32 sff0 = readl ( mmio_base + QS_HST_SFF ) ;
u32 sff1 = readl ( mmio_base + QS_HST_SFF + 4 ) ;
u8 sEVLD = ( sff1 > > 30 ) & 0x01 ; /* valid flag */
sFFE = sff1 > > 31 ; /* empty flag */
if ( sEVLD ) {
u8 sDST = sff0 > > 16 ; /* dev status */
u8 sHST = sff1 & 0x3f ; /* host status */
unsigned int port_no = ( sff1 > > 8 ) & 0x03 ;
struct ata_port * ap = host_set - > ports [ port_no ] ;
DPRINTK ( " SFF=%08x%08x: sCHAN=%u sHST=%d sDST=%02x \n " ,
sff1 , sff0 , port_no , sHST , sDST ) ;
handled = 1 ;
2005-08-22 09:59:24 +04:00
if ( ap & & ! ( ap - > flags &
( ATA_FLAG_PORT_DISABLED | ATA_FLAG_NOINTR ) ) ) {
2005-04-17 02:20:36 +04:00
struct ata_queued_cmd * qc ;
struct qs_port_priv * pp = ap - > private_data ;
if ( ! pp | | pp - > state ! = qs_state_pkt )
continue ;
qc = ata_qc_from_tag ( ap , ap - > active_tag ) ;
if ( qc & & ( ! ( qc - > tf . ctl & ATA_NIEN ) ) ) {
switch ( sHST ) {
2005-10-30 12:44:42 +03:00
case 0 : /* successful CPB */
2005-04-17 02:20:36 +04:00
case 3 : /* device error */
pp - > state = qs_state_idle ;
qs_enter_reg_mode ( qc - > ap ) ;
2005-12-05 10:38:02 +03:00
qc - > err_mask | = ac_err_mask ( sDST ) ;
ata_qc_complete ( qc ) ;
2005-04-17 02:20:36 +04:00
break ;
default :
break ;
}
}
}
}
} while ( ! sFFE ) ;
return handled ;
}
static inline unsigned int qs_intr_mmio ( struct ata_host_set * host_set )
{
unsigned int handled = 0 , port_no ;
for ( port_no = 0 ; port_no < host_set - > n_ports ; + + port_no ) {
struct ata_port * ap ;
ap = host_set - > ports [ port_no ] ;
2005-08-22 09:59:24 +04:00
if ( ap & &
! ( ap - > flags & ( ATA_FLAG_PORT_DISABLED | ATA_FLAG_NOINTR ) ) ) {
2005-04-17 02:20:36 +04:00
struct ata_queued_cmd * qc ;
struct qs_port_priv * pp = ap - > private_data ;
if ( ! pp | | pp - > state ! = qs_state_mmio )
continue ;
qc = ata_qc_from_tag ( ap , ap - > active_tag ) ;
if ( qc & & ( ! ( qc - > tf . ctl & ATA_NIEN ) ) ) {
/* check main status, clearing INTRQ */
2005-10-29 21:58:21 +04:00
u8 status = ata_check_status ( ap ) ;
2005-04-17 02:20:36 +04:00
if ( ( status & ATA_BUSY ) )
continue ;
DPRINTK ( " ata%u: protocol %d (dev_stat 0x%X) \n " ,
ap - > id , qc - > tf . protocol , status ) ;
2005-07-31 21:13:24 +04:00
2005-04-17 02:20:36 +04:00
/* complete taskfile transaction */
pp - > state = qs_state_idle ;
2005-12-05 10:38:02 +03:00
qc - > err_mask | = ac_err_mask ( status ) ;
ata_qc_complete ( qc ) ;
2005-04-17 02:20:36 +04:00
handled = 1 ;
}
}
}
return handled ;
}
static irqreturn_t qs_intr ( int irq , void * dev_instance , struct pt_regs * regs )
{
struct ata_host_set * host_set = dev_instance ;
unsigned int handled = 0 ;
VPRINTK ( " ENTER \n " ) ;
spin_lock ( & host_set - > lock ) ;
handled = qs_intr_pkt ( host_set ) | qs_intr_mmio ( host_set ) ;
spin_unlock ( & host_set - > lock ) ;
VPRINTK ( " EXIT \n " ) ;
return IRQ_RETVAL ( handled ) ;
}
static void qs_ata_setup_port ( struct ata_ioports * port , unsigned long base )
{
port - > cmd_addr =
port - > data_addr = base + 0x400 ;
port - > error_addr =
port - > feature_addr = base + 0x408 ; /* hob_feature = 0x409 */
port - > nsect_addr = base + 0x410 ; /* hob_nsect = 0x411 */
port - > lbal_addr = base + 0x418 ; /* hob_lbal = 0x419 */
port - > lbam_addr = base + 0x420 ; /* hob_lbam = 0x421 */
port - > lbah_addr = base + 0x428 ; /* hob_lbah = 0x429 */
port - > device_addr = base + 0x430 ;
port - > status_addr =
port - > command_addr = base + 0x438 ;
port - > altstatus_addr =
port - > ctl_addr = base + 0x440 ;
port - > scr_addr = base + 0xc00 ;
}
static int qs_port_start ( struct ata_port * ap )
{
struct device * dev = ap - > host_set - > dev ;
struct qs_port_priv * pp ;
void __iomem * mmio_base = ap - > host_set - > mmio_base ;
void __iomem * chan = mmio_base + ( ap - > port_no * 0x4000 ) ;
u64 addr ;
int rc ;
rc = ata_port_start ( ap ) ;
if ( rc )
return rc ;
qs_enter_reg_mode ( ap ) ;
2005-09-07 02:18:35 +04:00
pp = kzalloc ( sizeof ( * pp ) , GFP_KERNEL ) ;
2005-04-17 02:20:36 +04:00
if ( ! pp ) {
rc = - ENOMEM ;
goto err_out ;
}
pp - > pkt = dma_alloc_coherent ( dev , QS_PKT_BYTES , & pp - > pkt_dma ,
GFP_KERNEL ) ;
if ( ! pp - > pkt ) {
rc = - ENOMEM ;
goto err_out_kfree ;
}
memset ( pp - > pkt , 0 , QS_PKT_BYTES ) ;
ap - > private_data = pp ;
addr = ( u64 ) pp - > pkt_dma ;
writel ( ( u32 ) addr , chan + QS_CCF_CPBA ) ;
writel ( ( u32 ) ( addr > > 32 ) , chan + QS_CCF_CPBA + 4 ) ;
return 0 ;
err_out_kfree :
kfree ( pp ) ;
err_out :
ata_port_stop ( ap ) ;
return rc ;
}
static void qs_port_stop ( struct ata_port * ap )
{
struct device * dev = ap - > host_set - > dev ;
struct qs_port_priv * pp = ap - > private_data ;
if ( pp ! = NULL ) {
ap - > private_data = NULL ;
if ( pp - > pkt ! = NULL )
dma_free_coherent ( dev , QS_PKT_BYTES , pp - > pkt ,
pp - > pkt_dma ) ;
kfree ( pp ) ;
}
ata_port_stop ( ap ) ;
}
static void qs_host_stop ( struct ata_host_set * host_set )
{
void __iomem * mmio_base = host_set - > mmio_base ;
2005-08-30 13:42:52 +04:00
struct pci_dev * pdev = to_pci_dev ( host_set - > dev ) ;
2005-04-17 02:20:36 +04:00
writeb ( 0 , mmio_base + QS_HCT_CTRL ) ; /* disable host interrupts */
writeb ( QS_CNFG3_GSRST , mmio_base + QS_HCF_CNFG3 ) ; /* global reset */
2005-05-27 05:54:27 +04:00
2005-08-30 13:42:52 +04:00
pci_iounmap ( pdev , mmio_base ) ;
2005-04-17 02:20:36 +04:00
}
static void qs_host_init ( unsigned int chip_id , struct ata_probe_ent * pe )
{
void __iomem * mmio_base = pe - > mmio_base ;
unsigned int port_no ;
writeb ( 0 , mmio_base + QS_HCT_CTRL ) ; /* disable host interrupts */
writeb ( QS_CNFG3_GSRST , mmio_base + QS_HCF_CNFG3 ) ; /* global reset */
/* reset each channel in turn */
for ( port_no = 0 ; port_no < pe - > n_ports ; + + port_no ) {
u8 __iomem * chan = mmio_base + ( port_no * 0x4000 ) ;
writeb ( QS_CTR1_RDEV | QS_CTR1_RCHN , chan + QS_CCT_CTR1 ) ;
writeb ( QS_CTR0_REG , chan + QS_CCT_CTR0 ) ;
readb ( chan + QS_CCT_CTR0 ) ; /* flush */
}
writeb ( QS_SERD3_PHY_ENA , mmio_base + QS_HVS_SERD3 ) ; /* enable phy */
for ( port_no = 0 ; port_no < pe - > n_ports ; + + port_no ) {
u8 __iomem * chan = mmio_base + ( port_no * 0x4000 ) ;
/* set FIFO depths to same settings as Windows driver */
writew ( 32 , chan + QS_CFC_HUFT ) ;
writew ( 32 , chan + QS_CFC_HDFT ) ;
writew ( 10 , chan + QS_CFC_DUFT ) ;
writew ( 8 , chan + QS_CFC_DDFT ) ;
/* set CPB size in bytes, as a power of two */
writeb ( QS_CPB_ORDER , chan + QS_CCF_CSEP ) ;
}
writeb ( 1 , mmio_base + QS_HCT_CTRL ) ; /* enable host interrupts */
}
/*
* The QStor understands 64 - bit buses , and uses 64 - bit fields
* for DMA pointers regardless of bus width . We just have to
* make sure our DMA masks are set appropriately for whatever
* bridge lies between us and the QStor , and then the DMA mapping
* code will ensure we only ever " see " appropriate buffer addresses .
* If we ' re 32 - bit limited somewhere , then our 64 - bit fields will
* just end up with zeros in the upper 32 - bits , without any special
* logic required outside of this routine ( below ) .
*/
static int qs_set_dma_masks ( struct pci_dev * pdev , void __iomem * mmio_base )
{
u32 bus_info = readl ( mmio_base + QS_HID_HPHY ) ;
int rc , have_64bit_bus = ( bus_info & QS_HPHY_64BIT ) ;
if ( have_64bit_bus & &
! pci_set_dma_mask ( pdev , DMA_64BIT_MASK ) ) {
rc = pci_set_consistent_dma_mask ( pdev , DMA_64BIT_MASK ) ;
if ( rc ) {
rc = pci_set_consistent_dma_mask ( pdev , DMA_32BIT_MASK ) ;
if ( rc ) {
2005-10-30 22:39:11 +03:00
dev_printk ( KERN_ERR , & pdev - > dev ,
" 64-bit DMA enable failed \n " ) ;
2005-04-17 02:20:36 +04:00
return rc ;
}
}
} else {
rc = pci_set_dma_mask ( pdev , DMA_32BIT_MASK ) ;
if ( rc ) {
2005-10-30 22:39:11 +03:00
dev_printk ( KERN_ERR , & pdev - > dev ,
" 32-bit DMA enable failed \n " ) ;
2005-04-17 02:20:36 +04:00
return rc ;
}
rc = pci_set_consistent_dma_mask ( pdev , DMA_32BIT_MASK ) ;
if ( rc ) {
2005-10-30 22:39:11 +03:00
dev_printk ( KERN_ERR , & pdev - > dev ,
" 32-bit consistent DMA enable failed \n " ) ;
2005-04-17 02:20:36 +04:00
return rc ;
}
}
return 0 ;
}
static int qs_ata_init_one ( struct pci_dev * pdev ,
const struct pci_device_id * ent )
{
static int printed_version ;
struct ata_probe_ent * probe_ent = NULL ;
void __iomem * mmio_base ;
unsigned int board_idx = ( unsigned int ) ent - > driver_data ;
int rc , port_no ;
if ( ! printed_version + + )
2005-10-30 22:39:11 +03:00
dev_printk ( KERN_DEBUG , & pdev - > dev , " version " DRV_VERSION " \n " ) ;
2005-04-17 02:20:36 +04:00
rc = pci_enable_device ( pdev ) ;
if ( rc )
return rc ;
rc = pci_request_regions ( pdev , DRV_NAME ) ;
if ( rc )
goto err_out ;
if ( ( pci_resource_flags ( pdev , 4 ) & IORESOURCE_MEM ) = = 0 ) {
rc = - ENODEV ;
goto err_out_regions ;
}
2005-08-30 13:42:52 +04:00
mmio_base = pci_iomap ( pdev , 4 , 0 ) ;
2005-04-17 02:20:36 +04:00
if ( mmio_base = = NULL ) {
rc = - ENOMEM ;
goto err_out_regions ;
}
rc = qs_set_dma_masks ( pdev , mmio_base ) ;
if ( rc )
goto err_out_iounmap ;
probe_ent = kmalloc ( sizeof ( * probe_ent ) , GFP_KERNEL ) ;
if ( probe_ent = = NULL ) {
rc = - ENOMEM ;
goto err_out_iounmap ;
}
memset ( probe_ent , 0 , sizeof ( * probe_ent ) ) ;
probe_ent - > dev = pci_dev_to_dev ( pdev ) ;
INIT_LIST_HEAD ( & probe_ent - > node ) ;
probe_ent - > sht = qs_port_info [ board_idx ] . sht ;
probe_ent - > host_flags = qs_port_info [ board_idx ] . host_flags ;
probe_ent - > pio_mask = qs_port_info [ board_idx ] . pio_mask ;
probe_ent - > mwdma_mask = qs_port_info [ board_idx ] . mwdma_mask ;
probe_ent - > udma_mask = qs_port_info [ board_idx ] . udma_mask ;
probe_ent - > port_ops = qs_port_info [ board_idx ] . port_ops ;
probe_ent - > irq = pdev - > irq ;
probe_ent - > irq_flags = SA_SHIRQ ;
probe_ent - > mmio_base = mmio_base ;
probe_ent - > n_ports = QS_PORTS ;
for ( port_no = 0 ; port_no < probe_ent - > n_ports ; + + port_no ) {
unsigned long chan = ( unsigned long ) mmio_base +
( port_no * 0x4000 ) ;
qs_ata_setup_port ( & probe_ent - > port [ port_no ] , chan ) ;
}
pci_set_master ( pdev ) ;
/* initialize adapter */
qs_host_init ( board_idx , probe_ent ) ;
rc = ata_device_add ( probe_ent ) ;
kfree ( probe_ent ) ;
if ( rc ! = QS_PORTS )
goto err_out_iounmap ;
return 0 ;
err_out_iounmap :
2005-08-30 13:42:52 +04:00
pci_iounmap ( pdev , mmio_base ) ;
2005-04-17 02:20:36 +04:00
err_out_regions :
pci_release_regions ( pdev ) ;
err_out :
pci_disable_device ( pdev ) ;
return rc ;
}
static int __init qs_ata_init ( void )
{
return pci_module_init ( & qs_ata_pci_driver ) ;
}
static void __exit qs_ata_exit ( void )
{
pci_unregister_driver ( & qs_ata_pci_driver ) ;
}
MODULE_AUTHOR ( " Mark Lord " ) ;
MODULE_DESCRIPTION ( " Pacific Digital Corporation QStor SATA low-level driver " ) ;
MODULE_LICENSE ( " GPL " ) ;
MODULE_DEVICE_TABLE ( pci , qs_ata_pci_tbl ) ;
MODULE_VERSION ( DRV_VERSION ) ;
module_init ( qs_ata_init ) ;
module_exit ( qs_ata_exit ) ;