xref: /openbmc/linux/drivers/ata/sata_vsc.c (revision 1ac731c529cd4d6adbce134754b51ff7d822b145)
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