1c6fd2807SJeff Garzik /* 2c6fd2807SJeff Garzik * sata_qstor.c - Pacific Digital Corporation QStor SATA 3c6fd2807SJeff Garzik * 4c6fd2807SJeff Garzik * Maintained by: Mark Lord <mlord@pobox.com> 5c6fd2807SJeff Garzik * 6c6fd2807SJeff Garzik * Copyright 2005 Pacific Digital Corporation. 7c6fd2807SJeff Garzik * (OSL/GPL code release authorized by Jalil Fadavi). 8c6fd2807SJeff Garzik * 9c6fd2807SJeff Garzik * 10c6fd2807SJeff Garzik * This program is free software; you can redistribute it and/or modify 11c6fd2807SJeff Garzik * it under the terms of the GNU General Public License as published by 12c6fd2807SJeff Garzik * the Free Software Foundation; either version 2, or (at your option) 13c6fd2807SJeff Garzik * any later version. 14c6fd2807SJeff Garzik * 15c6fd2807SJeff Garzik * This program is distributed in the hope that it will be useful, 16c6fd2807SJeff Garzik * but WITHOUT ANY WARRANTY; without even the implied warranty of 17c6fd2807SJeff Garzik * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 18c6fd2807SJeff Garzik * GNU General Public License for more details. 19c6fd2807SJeff Garzik * 20c6fd2807SJeff Garzik * You should have received a copy of the GNU General Public License 21c6fd2807SJeff Garzik * along with this program; see the file COPYING. If not, write to 22c6fd2807SJeff Garzik * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. 23c6fd2807SJeff Garzik * 24c6fd2807SJeff Garzik * 25c6fd2807SJeff Garzik * libata documentation is available via 'make {ps|pdf}docs', 26c6fd2807SJeff Garzik * as Documentation/DocBook/libata.* 27c6fd2807SJeff Garzik * 28c6fd2807SJeff Garzik */ 29c6fd2807SJeff Garzik 30c6fd2807SJeff Garzik #include <linux/kernel.h> 31c6fd2807SJeff Garzik #include <linux/module.h> 32c6fd2807SJeff Garzik #include <linux/pci.h> 33c6fd2807SJeff Garzik #include <linux/init.h> 34c6fd2807SJeff Garzik #include <linux/blkdev.h> 35c6fd2807SJeff Garzik #include <linux/delay.h> 36c6fd2807SJeff Garzik #include <linux/interrupt.h> 37c6fd2807SJeff Garzik #include <linux/device.h> 38c6fd2807SJeff Garzik #include <scsi/scsi_host.h> 39c6fd2807SJeff Garzik #include <linux/libata.h> 40c6fd2807SJeff Garzik 41c6fd2807SJeff Garzik #define DRV_NAME "sata_qstor" 42c6fd2807SJeff Garzik #define DRV_VERSION "0.06" 43c6fd2807SJeff Garzik 44c6fd2807SJeff Garzik enum { 450d5ff566STejun Heo QS_MMIO_BAR = 4, 460d5ff566STejun Heo 47c6fd2807SJeff Garzik QS_PORTS = 4, 48c6fd2807SJeff Garzik QS_MAX_PRD = LIBATA_MAX_PRD, 49c6fd2807SJeff Garzik QS_CPB_ORDER = 6, 50c6fd2807SJeff Garzik QS_CPB_BYTES = (1 << QS_CPB_ORDER), 51c6fd2807SJeff Garzik QS_PRD_BYTES = QS_MAX_PRD * 16, 52c6fd2807SJeff Garzik QS_PKT_BYTES = QS_CPB_BYTES + QS_PRD_BYTES, 53c6fd2807SJeff Garzik 54c6fd2807SJeff Garzik /* global register offsets */ 55c6fd2807SJeff Garzik QS_HCF_CNFG3 = 0x0003, /* host configuration offset */ 56c6fd2807SJeff Garzik QS_HID_HPHY = 0x0004, /* host physical interface info */ 57c6fd2807SJeff Garzik QS_HCT_CTRL = 0x00e4, /* global interrupt mask offset */ 58c6fd2807SJeff Garzik QS_HST_SFF = 0x0100, /* host status fifo offset */ 59c6fd2807SJeff Garzik QS_HVS_SERD3 = 0x0393, /* PHY enable offset */ 60c6fd2807SJeff Garzik 61c6fd2807SJeff Garzik /* global control bits */ 62c6fd2807SJeff Garzik QS_HPHY_64BIT = (1 << 1), /* 64-bit bus detected */ 63c6fd2807SJeff Garzik QS_CNFG3_GSRST = 0x01, /* global chip reset */ 64c6fd2807SJeff Garzik QS_SERD3_PHY_ENA = 0xf0, /* PHY detection ENAble*/ 65c6fd2807SJeff Garzik 66c6fd2807SJeff Garzik /* per-channel register offsets */ 67c6fd2807SJeff Garzik QS_CCF_CPBA = 0x0710, /* chan CPB base address */ 68c6fd2807SJeff Garzik QS_CCF_CSEP = 0x0718, /* chan CPB separation factor */ 69c6fd2807SJeff Garzik QS_CFC_HUFT = 0x0800, /* host upstream fifo threshold */ 70c6fd2807SJeff Garzik QS_CFC_HDFT = 0x0804, /* host downstream fifo threshold */ 71c6fd2807SJeff Garzik QS_CFC_DUFT = 0x0808, /* dev upstream fifo threshold */ 72c6fd2807SJeff Garzik QS_CFC_DDFT = 0x080c, /* dev downstream fifo threshold */ 73c6fd2807SJeff Garzik QS_CCT_CTR0 = 0x0900, /* chan control-0 offset */ 74c6fd2807SJeff Garzik QS_CCT_CTR1 = 0x0901, /* chan control-1 offset */ 75c6fd2807SJeff Garzik QS_CCT_CFF = 0x0a00, /* chan command fifo offset */ 76c6fd2807SJeff Garzik 77c6fd2807SJeff Garzik /* channel control bits */ 78c6fd2807SJeff Garzik QS_CTR0_REG = (1 << 1), /* register mode (vs. pkt mode) */ 79c6fd2807SJeff Garzik QS_CTR0_CLER = (1 << 2), /* clear channel errors */ 80c6fd2807SJeff Garzik QS_CTR1_RDEV = (1 << 1), /* sata phy/comms reset */ 81c6fd2807SJeff Garzik QS_CTR1_RCHN = (1 << 4), /* reset channel logic */ 82c6fd2807SJeff Garzik QS_CCF_RUN_PKT = 0x107, /* RUN a new dma PKT */ 83c6fd2807SJeff Garzik 84c6fd2807SJeff Garzik /* pkt sub-field headers */ 85c6fd2807SJeff Garzik QS_HCB_HDR = 0x01, /* Host Control Block header */ 86c6fd2807SJeff Garzik QS_DCB_HDR = 0x02, /* Device Control Block header */ 87c6fd2807SJeff Garzik 88c6fd2807SJeff Garzik /* pkt HCB flag bits */ 89c6fd2807SJeff Garzik QS_HF_DIRO = (1 << 0), /* data DIRection Out */ 90c6fd2807SJeff Garzik QS_HF_DAT = (1 << 3), /* DATa pkt */ 91c6fd2807SJeff Garzik QS_HF_IEN = (1 << 4), /* Interrupt ENable */ 92c6fd2807SJeff Garzik QS_HF_VLD = (1 << 5), /* VaLiD pkt */ 93c6fd2807SJeff Garzik 94c6fd2807SJeff Garzik /* pkt DCB flag bits */ 95c6fd2807SJeff Garzik QS_DF_PORD = (1 << 2), /* Pio OR Dma */ 96c6fd2807SJeff Garzik QS_DF_ELBA = (1 << 3), /* Extended LBA (lba48) */ 97c6fd2807SJeff Garzik 98c6fd2807SJeff Garzik /* PCI device IDs */ 99c6fd2807SJeff Garzik board_2068_idx = 0, /* QStor 4-port SATA/RAID */ 100c6fd2807SJeff Garzik }; 101c6fd2807SJeff Garzik 102c6fd2807SJeff Garzik enum { 103c6fd2807SJeff Garzik QS_DMA_BOUNDARY = ~0UL 104c6fd2807SJeff Garzik }; 105c6fd2807SJeff Garzik 106c6fd2807SJeff Garzik typedef enum { qs_state_idle, qs_state_pkt, qs_state_mmio } qs_state_t; 107c6fd2807SJeff Garzik 108c6fd2807SJeff Garzik struct qs_port_priv { 109c6fd2807SJeff Garzik u8 *pkt; 110c6fd2807SJeff Garzik dma_addr_t pkt_dma; 111c6fd2807SJeff Garzik qs_state_t state; 112c6fd2807SJeff Garzik }; 113c6fd2807SJeff Garzik 114c6fd2807SJeff Garzik static u32 qs_scr_read (struct ata_port *ap, unsigned int sc_reg); 115c6fd2807SJeff Garzik static void qs_scr_write (struct ata_port *ap, unsigned int sc_reg, u32 val); 116c6fd2807SJeff Garzik static int qs_ata_init_one (struct pci_dev *pdev, const struct pci_device_id *ent); 1177d12e780SDavid Howells static irqreturn_t qs_intr (int irq, void *dev_instance); 118c6fd2807SJeff Garzik static int qs_port_start(struct ata_port *ap); 119cca3974eSJeff Garzik static void qs_host_stop(struct ata_host *host); 120c6fd2807SJeff Garzik static void qs_phy_reset(struct ata_port *ap); 121c6fd2807SJeff Garzik static void qs_qc_prep(struct ata_queued_cmd *qc); 122c6fd2807SJeff Garzik static unsigned int qs_qc_issue(struct ata_queued_cmd *qc); 123c6fd2807SJeff Garzik static int qs_check_atapi_dma(struct ata_queued_cmd *qc); 124c6fd2807SJeff Garzik static void qs_bmdma_stop(struct ata_queued_cmd *qc); 125c6fd2807SJeff Garzik static u8 qs_bmdma_status(struct ata_port *ap); 126c6fd2807SJeff Garzik static void qs_irq_clear(struct ata_port *ap); 127c6fd2807SJeff Garzik static void qs_eng_timeout(struct ata_port *ap); 128c6fd2807SJeff Garzik 129c6fd2807SJeff Garzik static struct scsi_host_template qs_ata_sht = { 130c6fd2807SJeff Garzik .module = THIS_MODULE, 131c6fd2807SJeff Garzik .name = DRV_NAME, 132c6fd2807SJeff Garzik .ioctl = ata_scsi_ioctl, 133c6fd2807SJeff Garzik .queuecommand = ata_scsi_queuecmd, 134c6fd2807SJeff Garzik .can_queue = ATA_DEF_QUEUE, 135c6fd2807SJeff Garzik .this_id = ATA_SHT_THIS_ID, 136c6fd2807SJeff Garzik .sg_tablesize = QS_MAX_PRD, 137c6fd2807SJeff Garzik .cmd_per_lun = ATA_SHT_CMD_PER_LUN, 138c6fd2807SJeff Garzik .emulated = ATA_SHT_EMULATED, 139c6fd2807SJeff Garzik //FIXME .use_clustering = ATA_SHT_USE_CLUSTERING, 140c6fd2807SJeff Garzik .use_clustering = ENABLE_CLUSTERING, 141c6fd2807SJeff Garzik .proc_name = DRV_NAME, 142c6fd2807SJeff Garzik .dma_boundary = QS_DMA_BOUNDARY, 143c6fd2807SJeff Garzik .slave_configure = ata_scsi_slave_config, 144c6fd2807SJeff Garzik .slave_destroy = ata_scsi_slave_destroy, 145c6fd2807SJeff Garzik .bios_param = ata_std_bios_param, 146c6fd2807SJeff Garzik }; 147c6fd2807SJeff Garzik 148c6fd2807SJeff Garzik static const struct ata_port_operations qs_ata_ops = { 149c6fd2807SJeff Garzik .port_disable = ata_port_disable, 150c6fd2807SJeff Garzik .tf_load = ata_tf_load, 151c6fd2807SJeff Garzik .tf_read = ata_tf_read, 152c6fd2807SJeff Garzik .check_status = ata_check_status, 153c6fd2807SJeff Garzik .check_atapi_dma = qs_check_atapi_dma, 154c6fd2807SJeff Garzik .exec_command = ata_exec_command, 155c6fd2807SJeff Garzik .dev_select = ata_std_dev_select, 156c6fd2807SJeff Garzik .phy_reset = qs_phy_reset, 157c6fd2807SJeff Garzik .qc_prep = qs_qc_prep, 158c6fd2807SJeff Garzik .qc_issue = qs_qc_issue, 1590d5ff566STejun Heo .data_xfer = ata_data_xfer, 160c6fd2807SJeff Garzik .eng_timeout = qs_eng_timeout, 161c6fd2807SJeff Garzik .irq_handler = qs_intr, 162c6fd2807SJeff Garzik .irq_clear = qs_irq_clear, 163246ce3b6SAkira Iguchi .irq_on = ata_irq_on, 164246ce3b6SAkira Iguchi .irq_ack = ata_irq_ack, 165c6fd2807SJeff Garzik .scr_read = qs_scr_read, 166c6fd2807SJeff Garzik .scr_write = qs_scr_write, 167c6fd2807SJeff Garzik .port_start = qs_port_start, 168c6fd2807SJeff Garzik .host_stop = qs_host_stop, 169c6fd2807SJeff Garzik .bmdma_stop = qs_bmdma_stop, 170c6fd2807SJeff Garzik .bmdma_status = qs_bmdma_status, 171c6fd2807SJeff Garzik }; 172c6fd2807SJeff Garzik 173c6fd2807SJeff Garzik static const struct ata_port_info qs_port_info[] = { 174c6fd2807SJeff Garzik /* board_2068_idx */ 175c6fd2807SJeff Garzik { 176c6fd2807SJeff Garzik .sht = &qs_ata_sht, 177cca3974eSJeff Garzik .flags = ATA_FLAG_SATA | ATA_FLAG_NO_LEGACY | 178c6fd2807SJeff Garzik ATA_FLAG_SATA_RESET | 179c6fd2807SJeff Garzik //FIXME ATA_FLAG_SRST | 180c6fd2807SJeff Garzik ATA_FLAG_MMIO | ATA_FLAG_PIO_POLLING, 181c6fd2807SJeff Garzik .pio_mask = 0x10, /* pio4 */ 182c6fd2807SJeff Garzik .udma_mask = 0x7f, /* udma0-6 */ 183c6fd2807SJeff Garzik .port_ops = &qs_ata_ops, 184c6fd2807SJeff Garzik }, 185c6fd2807SJeff Garzik }; 186c6fd2807SJeff Garzik 187c6fd2807SJeff Garzik static const struct pci_device_id qs_ata_pci_tbl[] = { 1882d2744fcSJeff Garzik { PCI_VDEVICE(PDC, 0x2068), board_2068_idx }, 189c6fd2807SJeff Garzik 190c6fd2807SJeff Garzik { } /* terminate list */ 191c6fd2807SJeff Garzik }; 192c6fd2807SJeff Garzik 193c6fd2807SJeff Garzik static struct pci_driver qs_ata_pci_driver = { 194c6fd2807SJeff Garzik .name = DRV_NAME, 195c6fd2807SJeff Garzik .id_table = qs_ata_pci_tbl, 196c6fd2807SJeff Garzik .probe = qs_ata_init_one, 197c6fd2807SJeff Garzik .remove = ata_pci_remove_one, 198c6fd2807SJeff Garzik }; 199c6fd2807SJeff Garzik 2000d5ff566STejun Heo static void __iomem *qs_mmio_base(struct ata_host *host) 2010d5ff566STejun Heo { 2020d5ff566STejun Heo return host->iomap[QS_MMIO_BAR]; 2030d5ff566STejun Heo } 2040d5ff566STejun Heo 205c6fd2807SJeff Garzik static int qs_check_atapi_dma(struct ata_queued_cmd *qc) 206c6fd2807SJeff Garzik { 207c6fd2807SJeff Garzik return 1; /* ATAPI DMA not supported */ 208c6fd2807SJeff Garzik } 209c6fd2807SJeff Garzik 210c6fd2807SJeff Garzik static void qs_bmdma_stop(struct ata_queued_cmd *qc) 211c6fd2807SJeff Garzik { 212c6fd2807SJeff Garzik /* nothing */ 213c6fd2807SJeff Garzik } 214c6fd2807SJeff Garzik 215c6fd2807SJeff Garzik static u8 qs_bmdma_status(struct ata_port *ap) 216c6fd2807SJeff Garzik { 217c6fd2807SJeff Garzik return 0; 218c6fd2807SJeff Garzik } 219c6fd2807SJeff Garzik 220c6fd2807SJeff Garzik static void qs_irq_clear(struct ata_port *ap) 221c6fd2807SJeff Garzik { 222c6fd2807SJeff Garzik /* nothing */ 223c6fd2807SJeff Garzik } 224c6fd2807SJeff Garzik 225c6fd2807SJeff Garzik static inline void qs_enter_reg_mode(struct ata_port *ap) 226c6fd2807SJeff Garzik { 2270d5ff566STejun Heo u8 __iomem *chan = qs_mmio_base(ap->host) + (ap->port_no * 0x4000); 228c6fd2807SJeff Garzik 229c6fd2807SJeff Garzik writeb(QS_CTR0_REG, chan + QS_CCT_CTR0); 230c6fd2807SJeff Garzik readb(chan + QS_CCT_CTR0); /* flush */ 231c6fd2807SJeff Garzik } 232c6fd2807SJeff Garzik 233c6fd2807SJeff Garzik static inline void qs_reset_channel_logic(struct ata_port *ap) 234c6fd2807SJeff Garzik { 2350d5ff566STejun Heo u8 __iomem *chan = qs_mmio_base(ap->host) + (ap->port_no * 0x4000); 236c6fd2807SJeff Garzik 237c6fd2807SJeff Garzik writeb(QS_CTR1_RCHN, chan + QS_CCT_CTR1); 238c6fd2807SJeff Garzik readb(chan + QS_CCT_CTR0); /* flush */ 239c6fd2807SJeff Garzik qs_enter_reg_mode(ap); 240c6fd2807SJeff Garzik } 241c6fd2807SJeff Garzik 242c6fd2807SJeff Garzik static void qs_phy_reset(struct ata_port *ap) 243c6fd2807SJeff Garzik { 244c6fd2807SJeff Garzik struct qs_port_priv *pp = ap->private_data; 245c6fd2807SJeff Garzik 246c6fd2807SJeff Garzik pp->state = qs_state_idle; 247c6fd2807SJeff Garzik qs_reset_channel_logic(ap); 248c6fd2807SJeff Garzik sata_phy_reset(ap); 249c6fd2807SJeff Garzik } 250c6fd2807SJeff Garzik 251c6fd2807SJeff Garzik static void qs_eng_timeout(struct ata_port *ap) 252c6fd2807SJeff Garzik { 253c6fd2807SJeff Garzik struct qs_port_priv *pp = ap->private_data; 254c6fd2807SJeff Garzik 255c6fd2807SJeff Garzik if (pp->state != qs_state_idle) /* healthy paranoia */ 256c6fd2807SJeff Garzik pp->state = qs_state_mmio; 257c6fd2807SJeff Garzik qs_reset_channel_logic(ap); 258c6fd2807SJeff Garzik ata_eng_timeout(ap); 259c6fd2807SJeff Garzik } 260c6fd2807SJeff Garzik 261c6fd2807SJeff Garzik static u32 qs_scr_read (struct ata_port *ap, unsigned int sc_reg) 262c6fd2807SJeff Garzik { 263c6fd2807SJeff Garzik if (sc_reg > SCR_CONTROL) 264c6fd2807SJeff Garzik return ~0U; 2650d5ff566STejun Heo return readl(ap->ioaddr.scr_addr + (sc_reg * 8)); 266c6fd2807SJeff Garzik } 267c6fd2807SJeff Garzik 268c6fd2807SJeff Garzik static void qs_scr_write (struct ata_port *ap, unsigned int sc_reg, u32 val) 269c6fd2807SJeff Garzik { 270c6fd2807SJeff Garzik if (sc_reg > SCR_CONTROL) 271c6fd2807SJeff Garzik return; 2720d5ff566STejun Heo writel(val, ap->ioaddr.scr_addr + (sc_reg * 8)); 273c6fd2807SJeff Garzik } 274c6fd2807SJeff Garzik 275c6fd2807SJeff Garzik static unsigned int qs_fill_sg(struct ata_queued_cmd *qc) 276c6fd2807SJeff Garzik { 277c6fd2807SJeff Garzik struct scatterlist *sg; 278c6fd2807SJeff Garzik struct ata_port *ap = qc->ap; 279c6fd2807SJeff Garzik struct qs_port_priv *pp = ap->private_data; 280c6fd2807SJeff Garzik unsigned int nelem; 281c6fd2807SJeff Garzik u8 *prd = pp->pkt + QS_CPB_BYTES; 282c6fd2807SJeff Garzik 283c6fd2807SJeff Garzik WARN_ON(qc->__sg == NULL); 284c6fd2807SJeff Garzik WARN_ON(qc->n_elem == 0 && qc->pad_len == 0); 285c6fd2807SJeff Garzik 286c6fd2807SJeff Garzik nelem = 0; 287c6fd2807SJeff Garzik ata_for_each_sg(sg, qc) { 288c6fd2807SJeff Garzik u64 addr; 289c6fd2807SJeff Garzik u32 len; 290c6fd2807SJeff Garzik 291c6fd2807SJeff Garzik addr = sg_dma_address(sg); 292c6fd2807SJeff Garzik *(__le64 *)prd = cpu_to_le64(addr); 293c6fd2807SJeff Garzik prd += sizeof(u64); 294c6fd2807SJeff Garzik 295c6fd2807SJeff Garzik len = sg_dma_len(sg); 296c6fd2807SJeff Garzik *(__le32 *)prd = cpu_to_le32(len); 297c6fd2807SJeff Garzik prd += sizeof(u64); 298c6fd2807SJeff Garzik 299c6fd2807SJeff Garzik VPRINTK("PRD[%u] = (0x%llX, 0x%X)\n", nelem, 300c6fd2807SJeff Garzik (unsigned long long)addr, len); 301c6fd2807SJeff Garzik nelem++; 302c6fd2807SJeff Garzik } 303c6fd2807SJeff Garzik 304c6fd2807SJeff Garzik return nelem; 305c6fd2807SJeff Garzik } 306c6fd2807SJeff Garzik 307c6fd2807SJeff Garzik static void qs_qc_prep(struct ata_queued_cmd *qc) 308c6fd2807SJeff Garzik { 309c6fd2807SJeff Garzik struct qs_port_priv *pp = qc->ap->private_data; 310c6fd2807SJeff Garzik u8 dflags = QS_DF_PORD, *buf = pp->pkt; 311c6fd2807SJeff Garzik u8 hflags = QS_HF_DAT | QS_HF_IEN | QS_HF_VLD; 312c6fd2807SJeff Garzik u64 addr; 313c6fd2807SJeff Garzik unsigned int nelem; 314c6fd2807SJeff Garzik 315c6fd2807SJeff Garzik VPRINTK("ENTER\n"); 316c6fd2807SJeff Garzik 317c6fd2807SJeff Garzik qs_enter_reg_mode(qc->ap); 318c6fd2807SJeff Garzik if (qc->tf.protocol != ATA_PROT_DMA) { 319c6fd2807SJeff Garzik ata_qc_prep(qc); 320c6fd2807SJeff Garzik return; 321c6fd2807SJeff Garzik } 322c6fd2807SJeff Garzik 323c6fd2807SJeff Garzik nelem = qs_fill_sg(qc); 324c6fd2807SJeff Garzik 325c6fd2807SJeff Garzik if ((qc->tf.flags & ATA_TFLAG_WRITE)) 326c6fd2807SJeff Garzik hflags |= QS_HF_DIRO; 327c6fd2807SJeff Garzik if ((qc->tf.flags & ATA_TFLAG_LBA48)) 328c6fd2807SJeff Garzik dflags |= QS_DF_ELBA; 329c6fd2807SJeff Garzik 330c6fd2807SJeff Garzik /* host control block (HCB) */ 331c6fd2807SJeff Garzik buf[ 0] = QS_HCB_HDR; 332c6fd2807SJeff Garzik buf[ 1] = hflags; 333726f0785STejun Heo *(__le32 *)(&buf[ 4]) = cpu_to_le32(qc->nbytes); 334c6fd2807SJeff Garzik *(__le32 *)(&buf[ 8]) = cpu_to_le32(nelem); 335c6fd2807SJeff Garzik addr = ((u64)pp->pkt_dma) + QS_CPB_BYTES; 336c6fd2807SJeff Garzik *(__le64 *)(&buf[16]) = cpu_to_le64(addr); 337c6fd2807SJeff Garzik 338c6fd2807SJeff Garzik /* device control block (DCB) */ 339c6fd2807SJeff Garzik buf[24] = QS_DCB_HDR; 340c6fd2807SJeff Garzik buf[28] = dflags; 341c6fd2807SJeff Garzik 342c6fd2807SJeff Garzik /* frame information structure (FIS) */ 343c6fd2807SJeff Garzik ata_tf_to_fis(&qc->tf, &buf[32], 0); 344c6fd2807SJeff Garzik } 345c6fd2807SJeff Garzik 346c6fd2807SJeff Garzik static inline void qs_packet_start(struct ata_queued_cmd *qc) 347c6fd2807SJeff Garzik { 348c6fd2807SJeff Garzik struct ata_port *ap = qc->ap; 3490d5ff566STejun Heo u8 __iomem *chan = qs_mmio_base(ap->host) + (ap->port_no * 0x4000); 350c6fd2807SJeff Garzik 351c6fd2807SJeff Garzik VPRINTK("ENTER, ap %p\n", ap); 352c6fd2807SJeff Garzik 353c6fd2807SJeff Garzik writeb(QS_CTR0_CLER, chan + QS_CCT_CTR0); 354c6fd2807SJeff Garzik wmb(); /* flush PRDs and pkt to memory */ 355c6fd2807SJeff Garzik writel(QS_CCF_RUN_PKT, chan + QS_CCT_CFF); 356c6fd2807SJeff Garzik readl(chan + QS_CCT_CFF); /* flush */ 357c6fd2807SJeff Garzik } 358c6fd2807SJeff Garzik 359c6fd2807SJeff Garzik static unsigned int qs_qc_issue(struct ata_queued_cmd *qc) 360c6fd2807SJeff Garzik { 361c6fd2807SJeff Garzik struct qs_port_priv *pp = qc->ap->private_data; 362c6fd2807SJeff Garzik 363c6fd2807SJeff Garzik switch (qc->tf.protocol) { 364c6fd2807SJeff Garzik case ATA_PROT_DMA: 365c6fd2807SJeff Garzik 366c6fd2807SJeff Garzik pp->state = qs_state_pkt; 367c6fd2807SJeff Garzik qs_packet_start(qc); 368c6fd2807SJeff Garzik return 0; 369c6fd2807SJeff Garzik 370c6fd2807SJeff Garzik case ATA_PROT_ATAPI_DMA: 371c6fd2807SJeff Garzik BUG(); 372c6fd2807SJeff Garzik break; 373c6fd2807SJeff Garzik 374c6fd2807SJeff Garzik default: 375c6fd2807SJeff Garzik break; 376c6fd2807SJeff Garzik } 377c6fd2807SJeff Garzik 378c6fd2807SJeff Garzik pp->state = qs_state_mmio; 379c6fd2807SJeff Garzik return ata_qc_issue_prot(qc); 380c6fd2807SJeff Garzik } 381c6fd2807SJeff Garzik 382cca3974eSJeff Garzik static inline unsigned int qs_intr_pkt(struct ata_host *host) 383c6fd2807SJeff Garzik { 384c6fd2807SJeff Garzik unsigned int handled = 0; 385c6fd2807SJeff Garzik u8 sFFE; 3860d5ff566STejun Heo u8 __iomem *mmio_base = qs_mmio_base(host); 387c6fd2807SJeff Garzik 388c6fd2807SJeff Garzik do { 389c6fd2807SJeff Garzik u32 sff0 = readl(mmio_base + QS_HST_SFF); 390c6fd2807SJeff Garzik u32 sff1 = readl(mmio_base + QS_HST_SFF + 4); 391c6fd2807SJeff Garzik u8 sEVLD = (sff1 >> 30) & 0x01; /* valid flag */ 392c6fd2807SJeff Garzik sFFE = sff1 >> 31; /* empty flag */ 393c6fd2807SJeff Garzik 394c6fd2807SJeff Garzik if (sEVLD) { 395c6fd2807SJeff Garzik u8 sDST = sff0 >> 16; /* dev status */ 396c6fd2807SJeff Garzik u8 sHST = sff1 & 0x3f; /* host status */ 397c6fd2807SJeff Garzik unsigned int port_no = (sff1 >> 8) & 0x03; 398cca3974eSJeff Garzik struct ata_port *ap = host->ports[port_no]; 399c6fd2807SJeff Garzik 400c6fd2807SJeff Garzik DPRINTK("SFF=%08x%08x: sCHAN=%u sHST=%d sDST=%02x\n", 401c6fd2807SJeff Garzik sff1, sff0, port_no, sHST, sDST); 402c6fd2807SJeff Garzik handled = 1; 403c6fd2807SJeff Garzik if (ap && !(ap->flags & ATA_FLAG_DISABLED)) { 404c6fd2807SJeff Garzik struct ata_queued_cmd *qc; 405c6fd2807SJeff Garzik struct qs_port_priv *pp = ap->private_data; 406c6fd2807SJeff Garzik if (!pp || pp->state != qs_state_pkt) 407c6fd2807SJeff Garzik continue; 408c6fd2807SJeff Garzik qc = ata_qc_from_tag(ap, ap->active_tag); 409c6fd2807SJeff Garzik if (qc && (!(qc->tf.flags & ATA_TFLAG_POLLING))) { 410c6fd2807SJeff Garzik switch (sHST) { 411c6fd2807SJeff Garzik case 0: /* successful CPB */ 412c6fd2807SJeff Garzik case 3: /* device error */ 413c6fd2807SJeff Garzik pp->state = qs_state_idle; 414c6fd2807SJeff Garzik qs_enter_reg_mode(qc->ap); 415c6fd2807SJeff Garzik qc->err_mask |= ac_err_mask(sDST); 416c6fd2807SJeff Garzik ata_qc_complete(qc); 417c6fd2807SJeff Garzik break; 418c6fd2807SJeff Garzik default: 419c6fd2807SJeff Garzik break; 420c6fd2807SJeff Garzik } 421c6fd2807SJeff Garzik } 422c6fd2807SJeff Garzik } 423c6fd2807SJeff Garzik } 424c6fd2807SJeff Garzik } while (!sFFE); 425c6fd2807SJeff Garzik return handled; 426c6fd2807SJeff Garzik } 427c6fd2807SJeff Garzik 428cca3974eSJeff Garzik static inline unsigned int qs_intr_mmio(struct ata_host *host) 429c6fd2807SJeff Garzik { 430c6fd2807SJeff Garzik unsigned int handled = 0, port_no; 431c6fd2807SJeff Garzik 432cca3974eSJeff Garzik for (port_no = 0; port_no < host->n_ports; ++port_no) { 433c6fd2807SJeff Garzik struct ata_port *ap; 434cca3974eSJeff Garzik ap = host->ports[port_no]; 435c6fd2807SJeff Garzik if (ap && 436c6fd2807SJeff Garzik !(ap->flags & ATA_FLAG_DISABLED)) { 437c6fd2807SJeff Garzik struct ata_queued_cmd *qc; 438c6fd2807SJeff Garzik struct qs_port_priv *pp = ap->private_data; 439c6fd2807SJeff Garzik if (!pp || pp->state != qs_state_mmio) 440c6fd2807SJeff Garzik continue; 441c6fd2807SJeff Garzik qc = ata_qc_from_tag(ap, ap->active_tag); 442c6fd2807SJeff Garzik if (qc && (!(qc->tf.flags & ATA_TFLAG_POLLING))) { 443c6fd2807SJeff Garzik 444c6fd2807SJeff Garzik /* check main status, clearing INTRQ */ 445c6fd2807SJeff Garzik u8 status = ata_check_status(ap); 446c6fd2807SJeff Garzik if ((status & ATA_BUSY)) 447c6fd2807SJeff Garzik continue; 448c6fd2807SJeff Garzik DPRINTK("ata%u: protocol %d (dev_stat 0x%X)\n", 44944877b4eSTejun Heo ap->print_id, qc->tf.protocol, status); 450c6fd2807SJeff Garzik 451c6fd2807SJeff Garzik /* complete taskfile transaction */ 452c6fd2807SJeff Garzik pp->state = qs_state_idle; 453c6fd2807SJeff Garzik qc->err_mask |= ac_err_mask(status); 454c6fd2807SJeff Garzik ata_qc_complete(qc); 455c6fd2807SJeff Garzik handled = 1; 456c6fd2807SJeff Garzik } 457c6fd2807SJeff Garzik } 458c6fd2807SJeff Garzik } 459c6fd2807SJeff Garzik return handled; 460c6fd2807SJeff Garzik } 461c6fd2807SJeff Garzik 4627d12e780SDavid Howells static irqreturn_t qs_intr(int irq, void *dev_instance) 463c6fd2807SJeff Garzik { 464cca3974eSJeff Garzik struct ata_host *host = dev_instance; 465c6fd2807SJeff Garzik unsigned int handled = 0; 466c6fd2807SJeff Garzik 467c6fd2807SJeff Garzik VPRINTK("ENTER\n"); 468c6fd2807SJeff Garzik 469cca3974eSJeff Garzik spin_lock(&host->lock); 470cca3974eSJeff Garzik handled = qs_intr_pkt(host) | qs_intr_mmio(host); 471cca3974eSJeff Garzik spin_unlock(&host->lock); 472c6fd2807SJeff Garzik 473c6fd2807SJeff Garzik VPRINTK("EXIT\n"); 474c6fd2807SJeff Garzik 475c6fd2807SJeff Garzik return IRQ_RETVAL(handled); 476c6fd2807SJeff Garzik } 477c6fd2807SJeff Garzik 4780d5ff566STejun Heo static void qs_ata_setup_port(struct ata_ioports *port, void __iomem *base) 479c6fd2807SJeff Garzik { 480c6fd2807SJeff Garzik port->cmd_addr = 481c6fd2807SJeff Garzik port->data_addr = base + 0x400; 482c6fd2807SJeff Garzik port->error_addr = 483c6fd2807SJeff Garzik port->feature_addr = base + 0x408; /* hob_feature = 0x409 */ 484c6fd2807SJeff Garzik port->nsect_addr = base + 0x410; /* hob_nsect = 0x411 */ 485c6fd2807SJeff Garzik port->lbal_addr = base + 0x418; /* hob_lbal = 0x419 */ 486c6fd2807SJeff Garzik port->lbam_addr = base + 0x420; /* hob_lbam = 0x421 */ 487c6fd2807SJeff Garzik port->lbah_addr = base + 0x428; /* hob_lbah = 0x429 */ 488c6fd2807SJeff Garzik port->device_addr = base + 0x430; 489c6fd2807SJeff Garzik port->status_addr = 490c6fd2807SJeff Garzik port->command_addr = base + 0x438; 491c6fd2807SJeff Garzik port->altstatus_addr = 492c6fd2807SJeff Garzik port->ctl_addr = base + 0x440; 493c6fd2807SJeff Garzik port->scr_addr = base + 0xc00; 494c6fd2807SJeff Garzik } 495c6fd2807SJeff Garzik 496c6fd2807SJeff Garzik static int qs_port_start(struct ata_port *ap) 497c6fd2807SJeff Garzik { 498cca3974eSJeff Garzik struct device *dev = ap->host->dev; 499c6fd2807SJeff Garzik struct qs_port_priv *pp; 5000d5ff566STejun Heo void __iomem *mmio_base = qs_mmio_base(ap->host); 501c6fd2807SJeff Garzik void __iomem *chan = mmio_base + (ap->port_no * 0x4000); 502c6fd2807SJeff Garzik u64 addr; 503c6fd2807SJeff Garzik int rc; 504c6fd2807SJeff Garzik 505c6fd2807SJeff Garzik rc = ata_port_start(ap); 506c6fd2807SJeff Garzik if (rc) 507c6fd2807SJeff Garzik return rc; 508c6fd2807SJeff Garzik qs_enter_reg_mode(ap); 50924dc5f33STejun Heo pp = devm_kzalloc(dev, sizeof(*pp), GFP_KERNEL); 51024dc5f33STejun Heo if (!pp) 51124dc5f33STejun Heo return -ENOMEM; 51224dc5f33STejun Heo pp->pkt = dmam_alloc_coherent(dev, QS_PKT_BYTES, &pp->pkt_dma, 513c6fd2807SJeff Garzik GFP_KERNEL); 51424dc5f33STejun Heo if (!pp->pkt) 51524dc5f33STejun Heo return -ENOMEM; 516c6fd2807SJeff Garzik memset(pp->pkt, 0, QS_PKT_BYTES); 517c6fd2807SJeff Garzik ap->private_data = pp; 518c6fd2807SJeff Garzik 519c6fd2807SJeff Garzik addr = (u64)pp->pkt_dma; 520c6fd2807SJeff Garzik writel((u32) addr, chan + QS_CCF_CPBA); 521c6fd2807SJeff Garzik writel((u32)(addr >> 32), chan + QS_CCF_CPBA + 4); 522c6fd2807SJeff Garzik return 0; 523c6fd2807SJeff Garzik } 524c6fd2807SJeff Garzik 525cca3974eSJeff Garzik static void qs_host_stop(struct ata_host *host) 526c6fd2807SJeff Garzik { 5270d5ff566STejun Heo void __iomem *mmio_base = qs_mmio_base(host); 528c6fd2807SJeff Garzik 529c6fd2807SJeff Garzik writeb(0, mmio_base + QS_HCT_CTRL); /* disable host interrupts */ 530c6fd2807SJeff Garzik writeb(QS_CNFG3_GSRST, mmio_base + QS_HCF_CNFG3); /* global reset */ 531c6fd2807SJeff Garzik } 532c6fd2807SJeff Garzik 533c6fd2807SJeff Garzik static void qs_host_init(unsigned int chip_id, struct ata_probe_ent *pe) 534c6fd2807SJeff Garzik { 5350d5ff566STejun Heo void __iomem *mmio_base = pe->iomap[QS_MMIO_BAR]; 536c6fd2807SJeff Garzik unsigned int port_no; 537c6fd2807SJeff Garzik 538c6fd2807SJeff Garzik writeb(0, mmio_base + QS_HCT_CTRL); /* disable host interrupts */ 539c6fd2807SJeff Garzik writeb(QS_CNFG3_GSRST, mmio_base + QS_HCF_CNFG3); /* global reset */ 540c6fd2807SJeff Garzik 541c6fd2807SJeff Garzik /* reset each channel in turn */ 542c6fd2807SJeff Garzik for (port_no = 0; port_no < pe->n_ports; ++port_no) { 543c6fd2807SJeff Garzik u8 __iomem *chan = mmio_base + (port_no * 0x4000); 544c6fd2807SJeff Garzik writeb(QS_CTR1_RDEV|QS_CTR1_RCHN, chan + QS_CCT_CTR1); 545c6fd2807SJeff Garzik writeb(QS_CTR0_REG, chan + QS_CCT_CTR0); 546c6fd2807SJeff Garzik readb(chan + QS_CCT_CTR0); /* flush */ 547c6fd2807SJeff Garzik } 548c6fd2807SJeff Garzik writeb(QS_SERD3_PHY_ENA, mmio_base + QS_HVS_SERD3); /* enable phy */ 549c6fd2807SJeff Garzik 550c6fd2807SJeff Garzik for (port_no = 0; port_no < pe->n_ports; ++port_no) { 551c6fd2807SJeff Garzik u8 __iomem *chan = mmio_base + (port_no * 0x4000); 552c6fd2807SJeff Garzik /* set FIFO depths to same settings as Windows driver */ 553c6fd2807SJeff Garzik writew(32, chan + QS_CFC_HUFT); 554c6fd2807SJeff Garzik writew(32, chan + QS_CFC_HDFT); 555c6fd2807SJeff Garzik writew(10, chan + QS_CFC_DUFT); 556c6fd2807SJeff Garzik writew( 8, chan + QS_CFC_DDFT); 557c6fd2807SJeff Garzik /* set CPB size in bytes, as a power of two */ 558c6fd2807SJeff Garzik writeb(QS_CPB_ORDER, chan + QS_CCF_CSEP); 559c6fd2807SJeff Garzik } 560c6fd2807SJeff Garzik writeb(1, mmio_base + QS_HCT_CTRL); /* enable host interrupts */ 561c6fd2807SJeff Garzik } 562c6fd2807SJeff Garzik 563c6fd2807SJeff Garzik /* 564c6fd2807SJeff Garzik * The QStor understands 64-bit buses, and uses 64-bit fields 565c6fd2807SJeff Garzik * for DMA pointers regardless of bus width. We just have to 566c6fd2807SJeff Garzik * make sure our DMA masks are set appropriately for whatever 567c6fd2807SJeff Garzik * bridge lies between us and the QStor, and then the DMA mapping 568c6fd2807SJeff Garzik * code will ensure we only ever "see" appropriate buffer addresses. 569c6fd2807SJeff Garzik * If we're 32-bit limited somewhere, then our 64-bit fields will 570c6fd2807SJeff Garzik * just end up with zeros in the upper 32-bits, without any special 571c6fd2807SJeff Garzik * logic required outside of this routine (below). 572c6fd2807SJeff Garzik */ 573c6fd2807SJeff Garzik static int qs_set_dma_masks(struct pci_dev *pdev, void __iomem *mmio_base) 574c6fd2807SJeff Garzik { 575c6fd2807SJeff Garzik u32 bus_info = readl(mmio_base + QS_HID_HPHY); 576c6fd2807SJeff Garzik int rc, have_64bit_bus = (bus_info & QS_HPHY_64BIT); 577c6fd2807SJeff Garzik 578c6fd2807SJeff Garzik if (have_64bit_bus && 579c6fd2807SJeff Garzik !pci_set_dma_mask(pdev, DMA_64BIT_MASK)) { 580c6fd2807SJeff Garzik rc = pci_set_consistent_dma_mask(pdev, DMA_64BIT_MASK); 581c6fd2807SJeff Garzik if (rc) { 582c6fd2807SJeff Garzik rc = pci_set_consistent_dma_mask(pdev, DMA_32BIT_MASK); 583c6fd2807SJeff Garzik if (rc) { 584c6fd2807SJeff Garzik dev_printk(KERN_ERR, &pdev->dev, 585c6fd2807SJeff Garzik "64-bit DMA enable failed\n"); 586c6fd2807SJeff Garzik return rc; 587c6fd2807SJeff Garzik } 588c6fd2807SJeff Garzik } 589c6fd2807SJeff Garzik } else { 590c6fd2807SJeff Garzik rc = pci_set_dma_mask(pdev, DMA_32BIT_MASK); 591c6fd2807SJeff Garzik if (rc) { 592c6fd2807SJeff Garzik dev_printk(KERN_ERR, &pdev->dev, 593c6fd2807SJeff Garzik "32-bit DMA enable failed\n"); 594c6fd2807SJeff Garzik return rc; 595c6fd2807SJeff Garzik } 596c6fd2807SJeff Garzik rc = pci_set_consistent_dma_mask(pdev, DMA_32BIT_MASK); 597c6fd2807SJeff Garzik if (rc) { 598c6fd2807SJeff Garzik dev_printk(KERN_ERR, &pdev->dev, 599c6fd2807SJeff Garzik "32-bit consistent DMA enable failed\n"); 600c6fd2807SJeff Garzik return rc; 601c6fd2807SJeff Garzik } 602c6fd2807SJeff Garzik } 603c6fd2807SJeff Garzik return 0; 604c6fd2807SJeff Garzik } 605c6fd2807SJeff Garzik 606c6fd2807SJeff Garzik static int qs_ata_init_one(struct pci_dev *pdev, 607c6fd2807SJeff Garzik const struct pci_device_id *ent) 608c6fd2807SJeff Garzik { 609c6fd2807SJeff Garzik static int printed_version; 6100d5ff566STejun Heo struct ata_probe_ent *probe_ent; 6110d5ff566STejun Heo void __iomem * const *iomap; 612c6fd2807SJeff Garzik unsigned int board_idx = (unsigned int) ent->driver_data; 613c6fd2807SJeff Garzik int rc, port_no; 614c6fd2807SJeff Garzik 615c6fd2807SJeff Garzik if (!printed_version++) 616c6fd2807SJeff Garzik dev_printk(KERN_DEBUG, &pdev->dev, "version " DRV_VERSION "\n"); 617c6fd2807SJeff Garzik 61824dc5f33STejun Heo rc = pcim_enable_device(pdev); 619c6fd2807SJeff Garzik if (rc) 620c6fd2807SJeff Garzik return rc; 621c6fd2807SJeff Garzik 6220d5ff566STejun Heo if ((pci_resource_flags(pdev, QS_MMIO_BAR) & IORESOURCE_MEM) == 0) 62324dc5f33STejun Heo return -ENODEV; 624c6fd2807SJeff Garzik 6250d5ff566STejun Heo rc = pcim_iomap_regions(pdev, 1 << QS_MMIO_BAR, DRV_NAME); 6260d5ff566STejun Heo if (rc) 6270d5ff566STejun Heo return rc; 6280d5ff566STejun Heo iomap = pcim_iomap_table(pdev); 629c6fd2807SJeff Garzik 6300d5ff566STejun Heo rc = qs_set_dma_masks(pdev, iomap[QS_MMIO_BAR]); 631c6fd2807SJeff Garzik if (rc) 63224dc5f33STejun Heo return rc; 633c6fd2807SJeff Garzik 63424dc5f33STejun Heo probe_ent = devm_kzalloc(&pdev->dev, sizeof(*probe_ent), GFP_KERNEL); 63524dc5f33STejun Heo if (probe_ent == NULL) 63624dc5f33STejun Heo return -ENOMEM; 637c6fd2807SJeff Garzik 638c6fd2807SJeff Garzik probe_ent->dev = pci_dev_to_dev(pdev); 639c6fd2807SJeff Garzik INIT_LIST_HEAD(&probe_ent->node); 640c6fd2807SJeff Garzik 641c6fd2807SJeff Garzik probe_ent->sht = qs_port_info[board_idx].sht; 642cca3974eSJeff Garzik probe_ent->port_flags = qs_port_info[board_idx].flags; 643c6fd2807SJeff Garzik probe_ent->pio_mask = qs_port_info[board_idx].pio_mask; 644c6fd2807SJeff Garzik probe_ent->mwdma_mask = qs_port_info[board_idx].mwdma_mask; 645c6fd2807SJeff Garzik probe_ent->udma_mask = qs_port_info[board_idx].udma_mask; 646c6fd2807SJeff Garzik probe_ent->port_ops = qs_port_info[board_idx].port_ops; 647c6fd2807SJeff Garzik 648c6fd2807SJeff Garzik probe_ent->irq = pdev->irq; 649c6fd2807SJeff Garzik probe_ent->irq_flags = IRQF_SHARED; 6500d5ff566STejun Heo probe_ent->iomap = iomap; 651c6fd2807SJeff Garzik probe_ent->n_ports = QS_PORTS; 652c6fd2807SJeff Garzik 653c6fd2807SJeff Garzik for (port_no = 0; port_no < probe_ent->n_ports; ++port_no) { 6540d5ff566STejun Heo void __iomem *chan = 6550d5ff566STejun Heo probe_ent->iomap[QS_MMIO_BAR] + (port_no * 0x4000); 656c6fd2807SJeff Garzik qs_ata_setup_port(&probe_ent->port[port_no], chan); 657c6fd2807SJeff Garzik } 658c6fd2807SJeff Garzik 659c6fd2807SJeff Garzik pci_set_master(pdev); 660c6fd2807SJeff Garzik 661c6fd2807SJeff Garzik /* initialize adapter */ 662c6fd2807SJeff Garzik qs_host_init(board_idx, probe_ent); 663c6fd2807SJeff Garzik 66424dc5f33STejun Heo if (ata_device_add(probe_ent) != QS_PORTS) 66524dc5f33STejun Heo return -EIO; 666c6fd2807SJeff Garzik 66724dc5f33STejun Heo devm_kfree(&pdev->dev, probe_ent); 66824dc5f33STejun Heo return 0; 669c6fd2807SJeff Garzik } 670c6fd2807SJeff Garzik 671c6fd2807SJeff Garzik static int __init qs_ata_init(void) 672c6fd2807SJeff Garzik { 673c6fd2807SJeff Garzik return pci_register_driver(&qs_ata_pci_driver); 674c6fd2807SJeff Garzik } 675c6fd2807SJeff Garzik 676c6fd2807SJeff Garzik static void __exit qs_ata_exit(void) 677c6fd2807SJeff Garzik { 678c6fd2807SJeff Garzik pci_unregister_driver(&qs_ata_pci_driver); 679c6fd2807SJeff Garzik } 680c6fd2807SJeff Garzik 681c6fd2807SJeff Garzik MODULE_AUTHOR("Mark Lord"); 682c6fd2807SJeff Garzik MODULE_DESCRIPTION("Pacific Digital Corporation QStor SATA low-level driver"); 683c6fd2807SJeff Garzik MODULE_LICENSE("GPL"); 684c6fd2807SJeff Garzik MODULE_DEVICE_TABLE(pci, qs_ata_pci_tbl); 685c6fd2807SJeff Garzik MODULE_VERSION(DRV_VERSION); 686c6fd2807SJeff Garzik 687c6fd2807SJeff Garzik module_init(qs_ata_init); 688c6fd2807SJeff Garzik module_exit(qs_ata_exit); 689