1c82ee6d3SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-or-later
2c6fd2807SJeff Garzik /*
3c6fd2807SJeff Garzik * sata_vsc.c - Vitesse VSC7174 4 port DPA SATA
4c6fd2807SJeff Garzik *
5c6fd2807SJeff Garzik * Maintained by: Jeremy Higdon @ SGI
6c6fd2807SJeff Garzik * Please ALWAYS copy linux-ide@vger.kernel.org
7c6fd2807SJeff Garzik * on emails.
8c6fd2807SJeff Garzik *
9c6fd2807SJeff Garzik * Copyright 2004 SGI
10c6fd2807SJeff Garzik *
11c6fd2807SJeff Garzik * Bits from Jeff Garzik, Copyright RedHat, Inc.
12c6fd2807SJeff Garzik *
13c6fd2807SJeff Garzik * libata documentation is available via 'make {ps|pdf}docs',
1419285f3cSMauro Carvalho Chehab * as Documentation/driver-api/libata.rst
15c6fd2807SJeff Garzik *
16c6fd2807SJeff Garzik * Vitesse hardware documentation presumably available under NDA.
17c6fd2807SJeff Garzik * Intel 31244 (same hardware interface) documentation presumably
18c6fd2807SJeff Garzik * available from http://developer.intel.com/
19c6fd2807SJeff Garzik */
20c6fd2807SJeff Garzik
21c6fd2807SJeff Garzik #include <linux/kernel.h>
22c6fd2807SJeff Garzik #include <linux/module.h>
23c6fd2807SJeff Garzik #include <linux/pci.h>
24c6fd2807SJeff Garzik #include <linux/blkdev.h>
25c6fd2807SJeff Garzik #include <linux/delay.h>
26c6fd2807SJeff Garzik #include <linux/interrupt.h>
27c6fd2807SJeff Garzik #include <linux/dma-mapping.h>
28c6fd2807SJeff Garzik #include <linux/device.h>
29c6fd2807SJeff Garzik #include <scsi/scsi_host.h>
30c6fd2807SJeff Garzik #include <linux/libata.h>
31c6fd2807SJeff Garzik
32c6fd2807SJeff Garzik #define DRV_NAME "sata_vsc"
332a3103ceSJeff Garzik #define DRV_VERSION "2.3"
34c6fd2807SJeff Garzik
35c6fd2807SJeff Garzik enum {
360d5ff566STejun Heo VSC_MMIO_BAR = 0,
370d5ff566STejun Heo
38c6fd2807SJeff Garzik /* Interrupt register offsets (from chip base address) */
39c6fd2807SJeff Garzik VSC_SATA_INT_STAT_OFFSET = 0x00,
40c6fd2807SJeff Garzik VSC_SATA_INT_MASK_OFFSET = 0x04,
41c6fd2807SJeff Garzik
42c6fd2807SJeff Garzik /* Taskfile registers offsets */
43c6fd2807SJeff Garzik VSC_SATA_TF_CMD_OFFSET = 0x00,
44c6fd2807SJeff Garzik VSC_SATA_TF_DATA_OFFSET = 0x00,
45c6fd2807SJeff Garzik VSC_SATA_TF_ERROR_OFFSET = 0x04,
46c6fd2807SJeff Garzik VSC_SATA_TF_FEATURE_OFFSET = 0x06,
47c6fd2807SJeff Garzik VSC_SATA_TF_NSECT_OFFSET = 0x08,
48c6fd2807SJeff Garzik VSC_SATA_TF_LBAL_OFFSET = 0x0c,
49c6fd2807SJeff Garzik VSC_SATA_TF_LBAM_OFFSET = 0x10,
50c6fd2807SJeff Garzik VSC_SATA_TF_LBAH_OFFSET = 0x14,
51c6fd2807SJeff Garzik VSC_SATA_TF_DEVICE_OFFSET = 0x18,
52c6fd2807SJeff Garzik VSC_SATA_TF_STATUS_OFFSET = 0x1c,
53c6fd2807SJeff Garzik VSC_SATA_TF_COMMAND_OFFSET = 0x1d,
54c6fd2807SJeff Garzik VSC_SATA_TF_ALTSTATUS_OFFSET = 0x28,
55c6fd2807SJeff Garzik VSC_SATA_TF_CTL_OFFSET = 0x29,
56c6fd2807SJeff Garzik
57c6fd2807SJeff Garzik /* DMA base */
58c6fd2807SJeff Garzik VSC_SATA_UP_DESCRIPTOR_OFFSET = 0x64,
59c6fd2807SJeff Garzik VSC_SATA_UP_DATA_BUFFER_OFFSET = 0x6C,
60c6fd2807SJeff Garzik VSC_SATA_DMA_CMD_OFFSET = 0x70,
61c6fd2807SJeff Garzik
62c6fd2807SJeff Garzik /* SCRs base */
63c6fd2807SJeff Garzik VSC_SATA_SCR_STATUS_OFFSET = 0x100,
64c6fd2807SJeff Garzik VSC_SATA_SCR_ERROR_OFFSET = 0x104,
65c6fd2807SJeff Garzik VSC_SATA_SCR_CONTROL_OFFSET = 0x108,
66c6fd2807SJeff Garzik
67c6fd2807SJeff Garzik /* Port stride */
68c6fd2807SJeff Garzik VSC_SATA_PORT_OFFSET = 0x200,
69c6fd2807SJeff Garzik
70c6fd2807SJeff Garzik /* Error interrupt status bit offsets */
71c6fd2807SJeff Garzik VSC_SATA_INT_ERROR_CRC = 0x40,
72c6fd2807SJeff Garzik VSC_SATA_INT_ERROR_T = 0x20,
73c6fd2807SJeff Garzik VSC_SATA_INT_ERROR_P = 0x10,
74c6fd2807SJeff Garzik VSC_SATA_INT_ERROR_R = 0x8,
75c6fd2807SJeff Garzik VSC_SATA_INT_ERROR_E = 0x4,
76c6fd2807SJeff Garzik VSC_SATA_INT_ERROR_M = 0x2,
77c6fd2807SJeff Garzik VSC_SATA_INT_PHY_CHANGE = 0x1,
78c6fd2807SJeff Garzik VSC_SATA_INT_ERROR = (VSC_SATA_INT_ERROR_CRC | VSC_SATA_INT_ERROR_T | \
79c6fd2807SJeff Garzik VSC_SATA_INT_ERROR_P | VSC_SATA_INT_ERROR_R | \
80c6fd2807SJeff Garzik VSC_SATA_INT_ERROR_E | VSC_SATA_INT_ERROR_M | \
81c6fd2807SJeff Garzik VSC_SATA_INT_PHY_CHANGE),
827cbaa86bSDan Wolstenholme };
83c6fd2807SJeff Garzik
vsc_sata_scr_read(struct ata_link * link,unsigned int sc_reg,u32 * val)8482ef04fbSTejun Heo static int vsc_sata_scr_read(struct ata_link *link,
8582ef04fbSTejun Heo unsigned int sc_reg, u32 *val)
86c6fd2807SJeff Garzik {
87c6fd2807SJeff Garzik if (sc_reg > SCR_CONTROL)
88da3dbb17STejun Heo return -EINVAL;
8982ef04fbSTejun Heo *val = readl(link->ap->ioaddr.scr_addr + (sc_reg * 4));
90da3dbb17STejun Heo return 0;
91c6fd2807SJeff Garzik }
92c6fd2807SJeff Garzik
93c6fd2807SJeff Garzik
vsc_sata_scr_write(struct ata_link * link,unsigned int sc_reg,u32 val)9482ef04fbSTejun Heo static int vsc_sata_scr_write(struct ata_link *link,
9582ef04fbSTejun Heo unsigned int sc_reg, u32 val)
96c6fd2807SJeff Garzik {
97c6fd2807SJeff Garzik if (sc_reg > SCR_CONTROL)
98da3dbb17STejun Heo return -EINVAL;
9982ef04fbSTejun Heo writel(val, link->ap->ioaddr.scr_addr + (sc_reg * 4));
100da3dbb17STejun Heo return 0;
101c6fd2807SJeff Garzik }
102c6fd2807SJeff Garzik
103c6fd2807SJeff Garzik
vsc_freeze(struct ata_port * ap)104ea34e45aSDan Williams static void vsc_freeze(struct ata_port *ap)
105ea34e45aSDan Williams {
106ea34e45aSDan Williams void __iomem *mask_addr;
107ea34e45aSDan Williams
108ea34e45aSDan Williams mask_addr = ap->host->iomap[VSC_MMIO_BAR] +
109ea34e45aSDan Williams VSC_SATA_INT_MASK_OFFSET + ap->port_no;
110ea34e45aSDan Williams
111ea34e45aSDan Williams writeb(0, mask_addr);
112ea34e45aSDan Williams }
113ea34e45aSDan Williams
114ea34e45aSDan Williams
vsc_thaw(struct ata_port * ap)115ea34e45aSDan Williams static void vsc_thaw(struct ata_port *ap)
116ea34e45aSDan Williams {
117ea34e45aSDan Williams void __iomem *mask_addr;
118ea34e45aSDan Williams
119ea34e45aSDan Williams mask_addr = ap->host->iomap[VSC_MMIO_BAR] +
120ea34e45aSDan Williams VSC_SATA_INT_MASK_OFFSET + ap->port_no;
121ea34e45aSDan Williams
122ea34e45aSDan Williams writeb(0xff, mask_addr);
123ea34e45aSDan Williams }
124ea34e45aSDan Williams
125ea34e45aSDan Williams
vsc_intr_mask_update(struct ata_port * ap,u8 ctl)126c6fd2807SJeff Garzik static void vsc_intr_mask_update(struct ata_port *ap, u8 ctl)
127c6fd2807SJeff Garzik {
128c6fd2807SJeff Garzik void __iomem *mask_addr;
129c6fd2807SJeff Garzik u8 mask;
130c6fd2807SJeff Garzik
1310d5ff566STejun Heo mask_addr = ap->host->iomap[VSC_MMIO_BAR] +
132c6fd2807SJeff Garzik VSC_SATA_INT_MASK_OFFSET + ap->port_no;
133c6fd2807SJeff Garzik mask = readb(mask_addr);
134c6fd2807SJeff Garzik if (ctl & ATA_NIEN)
135c6fd2807SJeff Garzik mask |= 0x80;
136c6fd2807SJeff Garzik else
137c6fd2807SJeff Garzik mask &= 0x7F;
138c6fd2807SJeff Garzik writeb(mask, mask_addr);
139c6fd2807SJeff Garzik }
140c6fd2807SJeff Garzik
141c6fd2807SJeff Garzik
vsc_sata_tf_load(struct ata_port * ap,const struct ata_taskfile * tf)142c6fd2807SJeff Garzik static void vsc_sata_tf_load(struct ata_port *ap, const struct ata_taskfile *tf)
143c6fd2807SJeff Garzik {
144c6fd2807SJeff Garzik struct ata_ioports *ioaddr = &ap->ioaddr;
145c6fd2807SJeff Garzik unsigned int is_addr = tf->flags & ATA_TFLAG_ISADDR;
146c6fd2807SJeff Garzik
147c6fd2807SJeff Garzik /*
148c6fd2807SJeff Garzik * The only thing the ctl register is used for is SRST.
149c6fd2807SJeff Garzik * That is not enabled or disabled via tf_load.
1505796d1c4SJeff Garzik * However, if ATA_NIEN is changed, then we need to change
1515796d1c4SJeff Garzik * the interrupt register.
152c6fd2807SJeff Garzik */
153c6fd2807SJeff Garzik if ((tf->ctl & ATA_NIEN) != (ap->last_ctl & ATA_NIEN)) {
154c6fd2807SJeff Garzik ap->last_ctl = tf->ctl;
155c6fd2807SJeff Garzik vsc_intr_mask_update(ap, tf->ctl & ATA_NIEN);
156c6fd2807SJeff Garzik }
157c6fd2807SJeff Garzik if (is_addr && (tf->flags & ATA_TFLAG_LBA48)) {
158850a9d8aSJeff Garzik writew(tf->feature | (((u16)tf->hob_feature) << 8),
1590d5ff566STejun Heo ioaddr->feature_addr);
160850a9d8aSJeff Garzik writew(tf->nsect | (((u16)tf->hob_nsect) << 8),
1610d5ff566STejun Heo ioaddr->nsect_addr);
162850a9d8aSJeff Garzik writew(tf->lbal | (((u16)tf->hob_lbal) << 8),
1630d5ff566STejun Heo ioaddr->lbal_addr);
164850a9d8aSJeff Garzik writew(tf->lbam | (((u16)tf->hob_lbam) << 8),
1650d5ff566STejun Heo ioaddr->lbam_addr);
166850a9d8aSJeff Garzik writew(tf->lbah | (((u16)tf->hob_lbah) << 8),
1670d5ff566STejun Heo ioaddr->lbah_addr);
168c6fd2807SJeff Garzik } else if (is_addr) {
1690d5ff566STejun Heo writew(tf->feature, ioaddr->feature_addr);
1700d5ff566STejun Heo writew(tf->nsect, ioaddr->nsect_addr);
1710d5ff566STejun Heo writew(tf->lbal, ioaddr->lbal_addr);
1720d5ff566STejun Heo writew(tf->lbam, ioaddr->lbam_addr);
1730d5ff566STejun Heo writew(tf->lbah, ioaddr->lbah_addr);
174c6fd2807SJeff Garzik }
175c6fd2807SJeff Garzik
176c6fd2807SJeff Garzik if (tf->flags & ATA_TFLAG_DEVICE)
1770d5ff566STejun Heo writeb(tf->device, ioaddr->device_addr);
178c6fd2807SJeff Garzik
179c6fd2807SJeff Garzik ata_wait_idle(ap);
180c6fd2807SJeff Garzik }
181c6fd2807SJeff Garzik
182c6fd2807SJeff Garzik
vsc_sata_tf_read(struct ata_port * ap,struct ata_taskfile * tf)183c6fd2807SJeff Garzik static void vsc_sata_tf_read(struct ata_port *ap, struct ata_taskfile *tf)
184c6fd2807SJeff Garzik {
185c6fd2807SJeff Garzik struct ata_ioports *ioaddr = &ap->ioaddr;
186efcef265SSergey Shtylyov u16 nsect, lbal, lbam, lbah, error;
187c6fd2807SJeff Garzik
188efcef265SSergey Shtylyov tf->status = ata_sff_check_status(ap);
1890d5ff566STejun Heo tf->device = readw(ioaddr->device_addr);
190efcef265SSergey Shtylyov error = readw(ioaddr->error_addr);
1910d5ff566STejun Heo nsect = readw(ioaddr->nsect_addr);
1920d5ff566STejun Heo lbal = readw(ioaddr->lbal_addr);
1930d5ff566STejun Heo lbam = readw(ioaddr->lbam_addr);
1940d5ff566STejun Heo lbah = readw(ioaddr->lbah_addr);
195c6fd2807SJeff Garzik
196efcef265SSergey Shtylyov tf->error = error;
197c6fd2807SJeff Garzik tf->nsect = nsect;
198c6fd2807SJeff Garzik tf->lbal = lbal;
199c6fd2807SJeff Garzik tf->lbam = lbam;
200c6fd2807SJeff Garzik tf->lbah = lbah;
201c6fd2807SJeff Garzik
202c6fd2807SJeff Garzik if (tf->flags & ATA_TFLAG_LBA48) {
203efcef265SSergey Shtylyov tf->hob_feature = error >> 8;
204c6fd2807SJeff Garzik tf->hob_nsect = nsect >> 8;
205c6fd2807SJeff Garzik tf->hob_lbal = lbal >> 8;
206c6fd2807SJeff Garzik tf->hob_lbam = lbam >> 8;
207c6fd2807SJeff Garzik tf->hob_lbah = lbah >> 8;
208c6fd2807SJeff Garzik }
209c6fd2807SJeff Garzik }
210c6fd2807SJeff Garzik
vsc_error_intr(u8 port_status,struct ata_port * ap)211ea34e45aSDan Williams static inline void vsc_error_intr(u8 port_status, struct ata_port *ap)
212ea34e45aSDan Williams {
213ea34e45aSDan Williams if (port_status & (VSC_SATA_INT_PHY_CHANGE | VSC_SATA_INT_ERROR_M))
214ea34e45aSDan Williams ata_port_freeze(ap);
215ea34e45aSDan Williams else
216ea34e45aSDan Williams ata_port_abort(ap);
217ea34e45aSDan Williams }
218ea34e45aSDan Williams
vsc_port_intr(u8 port_status,struct ata_port * ap)219ea34e45aSDan Williams static void vsc_port_intr(u8 port_status, struct ata_port *ap)
220ea34e45aSDan Williams {
221ea34e45aSDan Williams struct ata_queued_cmd *qc;
222ea34e45aSDan Williams int handled = 0;
223ea34e45aSDan Williams
224ea34e45aSDan Williams if (unlikely(port_status & VSC_SATA_INT_ERROR)) {
225ea34e45aSDan Williams vsc_error_intr(port_status, ap);
226ea34e45aSDan Williams return;
227ea34e45aSDan Williams }
228ea34e45aSDan Williams
2299af5c9c9STejun Heo qc = ata_qc_from_tag(ap, ap->link.active_tag);
230ea34e45aSDan Williams if (qc && likely(!(qc->tf.flags & ATA_TFLAG_POLLING)))
231c3b28894STejun Heo handled = ata_bmdma_port_intr(ap, qc);
232ea34e45aSDan Williams
233ea34e45aSDan Williams /* We received an interrupt during a polled command,
234ea34e45aSDan Williams * or some other spurious condition. Interrupt reporting
235ea34e45aSDan Williams * with this hardware is fairly reliable so it is safe to
236ea34e45aSDan Williams * simply clear the interrupt
237ea34e45aSDan Williams */
238ea34e45aSDan Williams if (unlikely(!handled))
2395682ed33STejun Heo ap->ops->sff_check_status(ap);
240ea34e45aSDan Williams }
241c6fd2807SJeff Garzik
242c6fd2807SJeff Garzik /*
243c6fd2807SJeff Garzik * vsc_sata_interrupt
244c6fd2807SJeff Garzik *
2455796d1c4SJeff Garzik * Read the interrupt register and process for the devices that have
2465796d1c4SJeff Garzik * them pending.
247c6fd2807SJeff Garzik */
vsc_sata_interrupt(int irq,void * dev_instance)2487d12e780SDavid Howells static irqreturn_t vsc_sata_interrupt(int irq, void *dev_instance)
249c6fd2807SJeff Garzik {
250cca3974eSJeff Garzik struct ata_host *host = dev_instance;
251c6fd2807SJeff Garzik unsigned int i;
252c6fd2807SJeff Garzik unsigned int handled = 0;
253ea34e45aSDan Williams u32 status;
254ea34e45aSDan Williams
255ea34e45aSDan Williams status = readl(host->iomap[VSC_MMIO_BAR] + VSC_SATA_INT_STAT_OFFSET);
256ea34e45aSDan Williams
257ea34e45aSDan Williams if (unlikely(status == 0xffffffff || status == 0)) {
258ea34e45aSDan Williams if (status)
259a44fec1fSJoe Perches dev_err(host->dev,
260a44fec1fSJoe Perches ": IRQ status == 0xffffffff, PCI fault or device removal?\n");
261ea34e45aSDan Williams goto out;
262ea34e45aSDan Williams }
263c6fd2807SJeff Garzik
264cca3974eSJeff Garzik spin_lock(&host->lock);
265c6fd2807SJeff Garzik
266cca3974eSJeff Garzik for (i = 0; i < host->n_ports; i++) {
267ea34e45aSDan Williams u8 port_status = (status >> (8 * i)) & 0xff;
268ea34e45aSDan Williams if (port_status) {
2693e4ec344STejun Heo vsc_port_intr(port_status, host->ports[i]);
270c6fd2807SJeff Garzik handled++;
271c6fd2807SJeff Garzik }
272c6fd2807SJeff Garzik }
273c6fd2807SJeff Garzik
274cca3974eSJeff Garzik spin_unlock(&host->lock);
275ea34e45aSDan Williams out:
276c6fd2807SJeff Garzik return IRQ_RETVAL(handled);
277c6fd2807SJeff Garzik }
278c6fd2807SJeff Garzik
279c6fd2807SJeff Garzik
280*25df73d9SBart Van Assche static const struct scsi_host_template vsc_sata_sht = {
28168d1d07bSTejun Heo ATA_BMDMA_SHT(DRV_NAME),
282c6fd2807SJeff Garzik };
283c6fd2807SJeff Garzik
284c6fd2807SJeff Garzik
285029cfd6bSTejun Heo static struct ata_port_operations vsc_sata_ops = {
286029cfd6bSTejun Heo .inherits = &ata_bmdma_port_ops,
287c96f1732SAlan Cox /* The IRQ handling is not quite standard SFF behaviour so we
288c96f1732SAlan Cox cannot use the default lost interrupt handler */
289c96f1732SAlan Cox .lost_interrupt = ATA_OP_NULL,
2905682ed33STejun Heo .sff_tf_load = vsc_sata_tf_load,
2915682ed33STejun Heo .sff_tf_read = vsc_sata_tf_read,
292ea34e45aSDan Williams .freeze = vsc_freeze,
293ea34e45aSDan Williams .thaw = vsc_thaw,
294c6fd2807SJeff Garzik .scr_read = vsc_sata_scr_read,
295c6fd2807SJeff Garzik .scr_write = vsc_sata_scr_write,
296c6fd2807SJeff Garzik };
297c6fd2807SJeff Garzik
vsc_sata_setup_port(struct ata_ioports * port,void __iomem * base)2980ec24914SGreg Kroah-Hartman static void vsc_sata_setup_port(struct ata_ioports *port, void __iomem *base)
299c6fd2807SJeff Garzik {
300c6fd2807SJeff Garzik port->cmd_addr = base + VSC_SATA_TF_CMD_OFFSET;
301c6fd2807SJeff Garzik port->data_addr = base + VSC_SATA_TF_DATA_OFFSET;
302c6fd2807SJeff Garzik port->error_addr = base + VSC_SATA_TF_ERROR_OFFSET;
303c6fd2807SJeff Garzik port->feature_addr = base + VSC_SATA_TF_FEATURE_OFFSET;
304c6fd2807SJeff Garzik port->nsect_addr = base + VSC_SATA_TF_NSECT_OFFSET;
305c6fd2807SJeff Garzik port->lbal_addr = base + VSC_SATA_TF_LBAL_OFFSET;
306c6fd2807SJeff Garzik port->lbam_addr = base + VSC_SATA_TF_LBAM_OFFSET;
307c6fd2807SJeff Garzik port->lbah_addr = base + VSC_SATA_TF_LBAH_OFFSET;
308c6fd2807SJeff Garzik port->device_addr = base + VSC_SATA_TF_DEVICE_OFFSET;
309c6fd2807SJeff Garzik port->status_addr = base + VSC_SATA_TF_STATUS_OFFSET;
310c6fd2807SJeff Garzik port->command_addr = base + VSC_SATA_TF_COMMAND_OFFSET;
311c6fd2807SJeff Garzik port->altstatus_addr = base + VSC_SATA_TF_ALTSTATUS_OFFSET;
312c6fd2807SJeff Garzik port->ctl_addr = base + VSC_SATA_TF_CTL_OFFSET;
313c6fd2807SJeff Garzik port->bmdma_addr = base + VSC_SATA_DMA_CMD_OFFSET;
314c6fd2807SJeff Garzik port->scr_addr = base + VSC_SATA_SCR_STATUS_OFFSET;
3150d5ff566STejun Heo writel(0, base + VSC_SATA_UP_DESCRIPTOR_OFFSET);
3160d5ff566STejun Heo writel(0, base + VSC_SATA_UP_DATA_BUFFER_OFFSET);
317c6fd2807SJeff Garzik }
318c6fd2807SJeff Garzik
319c6fd2807SJeff Garzik
vsc_sata_init_one(struct pci_dev * pdev,const struct pci_device_id * ent)3200ec24914SGreg Kroah-Hartman static int vsc_sata_init_one(struct pci_dev *pdev,
3215796d1c4SJeff Garzik const struct pci_device_id *ent)
322c6fd2807SJeff Garzik {
3234447d351STejun Heo static const struct ata_port_info pi = {
3249cbe056fSSergei Shtylyov .flags = ATA_FLAG_SATA,
32514bdef98SErik Inge Bolsø .pio_mask = ATA_PIO4,
32614bdef98SErik Inge Bolsø .mwdma_mask = ATA_MWDMA2,
327bf6263a8SJeff Garzik .udma_mask = ATA_UDMA6,
3284447d351STejun Heo .port_ops = &vsc_sata_ops,
3294447d351STejun Heo };
3304447d351STejun Heo const struct ata_port_info *ppi[] = { &pi, NULL };
3314447d351STejun Heo struct ata_host *host;
332c6fd2807SJeff Garzik void __iomem *mmio_base;
3334447d351STejun Heo int i, rc;
3347de970e1SNate Dailey u8 cls;
335c6fd2807SJeff Garzik
33606296a1eSJoe Perches ata_print_version_once(&pdev->dev, DRV_VERSION);
337c6fd2807SJeff Garzik
3384447d351STejun Heo /* allocate host */
3394447d351STejun Heo host = ata_host_alloc_pinfo(&pdev->dev, ppi, 4);
3404447d351STejun Heo if (!host)
3414447d351STejun Heo return -ENOMEM;
3424447d351STejun Heo
34324dc5f33STejun Heo rc = pcim_enable_device(pdev);
344c6fd2807SJeff Garzik if (rc)
345c6fd2807SJeff Garzik return rc;
346c6fd2807SJeff Garzik
3474447d351STejun Heo /* check if we have needed resource mapped */
34824dc5f33STejun Heo if (pci_resource_len(pdev, 0) == 0)
34924dc5f33STejun Heo return -ENODEV;
350c6fd2807SJeff Garzik
351b595076aSUwe Kleine-König /* map IO regions and initialize host accordingly */
3520d5ff566STejun Heo rc = pcim_iomap_regions(pdev, 1 << VSC_MMIO_BAR, DRV_NAME);
3530d5ff566STejun Heo if (rc == -EBUSY)
35424dc5f33STejun Heo pcim_pin_device(pdev);
3550d5ff566STejun Heo if (rc)
35624dc5f33STejun Heo return rc;
3574447d351STejun Heo host->iomap = pcim_iomap_table(pdev);
3584447d351STejun Heo
3594447d351STejun Heo mmio_base = host->iomap[VSC_MMIO_BAR];
3604447d351STejun Heo
361cbcdd875STejun Heo for (i = 0; i < host->n_ports; i++) {
362cbcdd875STejun Heo struct ata_port *ap = host->ports[i];
363cbcdd875STejun Heo unsigned int offset = (i + 1) * VSC_SATA_PORT_OFFSET;
364cbcdd875STejun Heo
365cbcdd875STejun Heo vsc_sata_setup_port(&ap->ioaddr, mmio_base + offset);
366cbcdd875STejun Heo
367cbcdd875STejun Heo ata_port_pbar_desc(ap, VSC_MMIO_BAR, -1, "mmio");
368cbcdd875STejun Heo ata_port_pbar_desc(ap, VSC_MMIO_BAR, offset, "port");
369cbcdd875STejun Heo }
370c6fd2807SJeff Garzik
371c6fd2807SJeff Garzik /*
372c6fd2807SJeff Garzik * Use 32 bit DMA mask, because 64 bit address support is poor.
373c6fd2807SJeff Garzik */
374b5e55556SChristoph Hellwig rc = dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(32));
375c6fd2807SJeff Garzik if (rc)
37624dc5f33STejun Heo return rc;
377c6fd2807SJeff Garzik
378c6fd2807SJeff Garzik /*
3797de970e1SNate Dailey * Due to a bug in the chip, the default cache line size can't be
3807de970e1SNate Dailey * used (unless the default is non-zero).
381c6fd2807SJeff Garzik */
3827de970e1SNate Dailey pci_read_config_byte(pdev, PCI_CACHE_LINE_SIZE, &cls);
3837de970e1SNate Dailey if (cls == 0x00)
384c6fd2807SJeff Garzik pci_write_config_byte(pdev, PCI_CACHE_LINE_SIZE, 0x80);
385c6fd2807SJeff Garzik
38624dc5f33STejun Heo if (pci_enable_msi(pdev) == 0)
3877cbaa86bSDan Wolstenholme pci_intx(pdev, 0);
388c6fd2807SJeff Garzik
389c6fd2807SJeff Garzik /*
390c6fd2807SJeff Garzik * Config offset 0x98 is "Extended Control and Status Register 0"
391c6fd2807SJeff Garzik * Default value is (1 << 28). All bits except bit 28 are reserved in
392c6fd2807SJeff Garzik * DPA mode. If bit 28 is set, LED 0 reflects all ports' activity.
393c6fd2807SJeff Garzik * If bit 28 is clear, each port has its own LED.
394c6fd2807SJeff Garzik */
395c6fd2807SJeff Garzik pci_write_config_dword(pdev, 0x98, 0);
396c6fd2807SJeff Garzik
3974447d351STejun Heo pci_set_master(pdev);
3984447d351STejun Heo return ata_host_activate(host, pdev->irq, vsc_sata_interrupt,
3994447d351STejun Heo IRQF_SHARED, &vsc_sata_sht);
400c6fd2807SJeff Garzik }
401c6fd2807SJeff Garzik
402c6fd2807SJeff Garzik static const struct pci_device_id vsc_sata_pci_tbl[] = {
403c6fd2807SJeff Garzik { PCI_VENDOR_ID_VITESSE, 0x7174,
404c6fd2807SJeff Garzik PCI_ANY_ID, PCI_ANY_ID, 0x10600, 0xFFFFFF, 0 },
405c6fd2807SJeff Garzik { PCI_VENDOR_ID_INTEL, 0x3200,
406c6fd2807SJeff Garzik PCI_ANY_ID, PCI_ANY_ID, 0x10600, 0xFFFFFF, 0 },
4072d2744fcSJeff Garzik
408c6fd2807SJeff Garzik { } /* terminate list */
409c6fd2807SJeff Garzik };
410c6fd2807SJeff Garzik
411c6fd2807SJeff Garzik static struct pci_driver vsc_sata_pci_driver = {
412c6fd2807SJeff Garzik .name = DRV_NAME,
413c6fd2807SJeff Garzik .id_table = vsc_sata_pci_tbl,
414c6fd2807SJeff Garzik .probe = vsc_sata_init_one,
415c6fd2807SJeff Garzik .remove = ata_pci_remove_one,
416c6fd2807SJeff Garzik };
417c6fd2807SJeff Garzik
4182fc75da0SAxel Lin module_pci_driver(vsc_sata_pci_driver);
419c6fd2807SJeff Garzik
420c6fd2807SJeff Garzik MODULE_AUTHOR("Jeremy Higdon");
421c6fd2807SJeff Garzik MODULE_DESCRIPTION("low-level driver for Vitesse VSC7174 SATA controller");
422c6fd2807SJeff Garzik MODULE_LICENSE("GPL");
423c6fd2807SJeff Garzik MODULE_DEVICE_TABLE(pci, vsc_sata_pci_tbl);
424c6fd2807SJeff Garzik MODULE_VERSION(DRV_VERSION);
425