xref: /openbmc/linux/drivers/ata/pdc_adma.c (revision 25df73d9)
1c82ee6d3SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-or-later
2c6fd2807SJeff Garzik /*
3c6fd2807SJeff Garzik  *  pdc_adma.c - Pacific Digital Corporation ADMA
4c6fd2807SJeff Garzik  *
58c3d3d4bSTejun Heo  *  Maintained by:  Tejun Heo <tj@kernel.org>
6c6fd2807SJeff Garzik  *
7c6fd2807SJeff Garzik  *  Copyright 2005 Mark Lord
8c6fd2807SJeff Garzik  *
9c6fd2807SJeff Garzik  *  libata documentation is available via 'make {ps|pdf}docs',
1019285f3cSMauro Carvalho Chehab  *  as Documentation/driver-api/libata.rst
11c6fd2807SJeff Garzik  *
12c6fd2807SJeff Garzik  *  Supports ATA disks in single-packet ADMA mode.
13c6fd2807SJeff Garzik  *  Uses PIO for everything else.
14c6fd2807SJeff Garzik  *
15c6fd2807SJeff Garzik  *  TODO:  Use ADMA transfers for ATAPI devices, when possible.
16c6fd2807SJeff Garzik  *  This requires careful attention to a number of quirks of the chip.
17c6fd2807SJeff Garzik  */
18c6fd2807SJeff Garzik 
19c6fd2807SJeff Garzik #include <linux/kernel.h>
20c6fd2807SJeff Garzik #include <linux/module.h>
215a0e3ad6STejun Heo #include <linux/gfp.h>
22c6fd2807SJeff Garzik #include <linux/pci.h>
23c6fd2807SJeff Garzik #include <linux/blkdev.h>
24c6fd2807SJeff Garzik #include <linux/delay.h>
25c6fd2807SJeff Garzik #include <linux/interrupt.h>
26c6fd2807SJeff Garzik #include <linux/device.h>
27c6fd2807SJeff Garzik #include <scsi/scsi_host.h>
28c6fd2807SJeff Garzik #include <linux/libata.h>
29c6fd2807SJeff Garzik 
30c6fd2807SJeff Garzik #define DRV_NAME	"pdc_adma"
312a3103ceSJeff Garzik #define DRV_VERSION	"1.0"
32c6fd2807SJeff Garzik 
33c6fd2807SJeff Garzik /* macro to calculate base address for ATA regs */
34c6fd2807SJeff Garzik #define ADMA_ATA_REGS(base, port_no)	((base) + ((port_no) * 0x40))
35c6fd2807SJeff Garzik 
36c6fd2807SJeff Garzik /* macro to calculate base address for ADMA regs */
37c6fd2807SJeff Garzik #define ADMA_REGS(base, port_no)	((base) + 0x80 + ((port_no) * 0x20))
38c6fd2807SJeff Garzik 
395d728824STejun Heo /* macro to obtain addresses from ata_port */
405d728824STejun Heo #define ADMA_PORT_REGS(ap) \
415d728824STejun Heo 	ADMA_REGS((ap)->host->iomap[ADMA_MMIO_BAR], ap->port_no)
420d5ff566STejun Heo 
43c6fd2807SJeff Garzik enum {
440d5ff566STejun Heo 	ADMA_MMIO_BAR		= 4,
450d5ff566STejun Heo 
46c6fd2807SJeff Garzik 	ADMA_PORTS		= 2,
47c6fd2807SJeff Garzik 	ADMA_CPB_BYTES		= 40,
48c6fd2807SJeff Garzik 	ADMA_PRD_BYTES		= LIBATA_MAX_PRD * 16,
49c6fd2807SJeff Garzik 	ADMA_PKT_BYTES		= ADMA_CPB_BYTES + ADMA_PRD_BYTES,
50c6fd2807SJeff Garzik 
51c6fd2807SJeff Garzik 	ADMA_DMA_BOUNDARY	= 0xffffffff,
52c6fd2807SJeff Garzik 
53c6fd2807SJeff Garzik 	/* global register offsets */
54c6fd2807SJeff Garzik 	ADMA_MODE_LOCK		= 0x00c7,
55c6fd2807SJeff Garzik 
56c6fd2807SJeff Garzik 	/* per-channel register offsets */
57c6fd2807SJeff Garzik 	ADMA_CONTROL		= 0x0000, /* ADMA control */
58c6fd2807SJeff Garzik 	ADMA_STATUS		= 0x0002, /* ADMA status */
59c6fd2807SJeff Garzik 	ADMA_CPB_COUNT		= 0x0004, /* CPB count */
60c6fd2807SJeff Garzik 	ADMA_CPB_CURRENT	= 0x000c, /* current CPB address */
61c6fd2807SJeff Garzik 	ADMA_CPB_NEXT		= 0x000c, /* next CPB address */
62c6fd2807SJeff Garzik 	ADMA_CPB_LOOKUP		= 0x0010, /* CPB lookup table */
63c6fd2807SJeff Garzik 	ADMA_FIFO_IN		= 0x0014, /* input FIFO threshold */
64c6fd2807SJeff Garzik 	ADMA_FIFO_OUT		= 0x0016, /* output FIFO threshold */
65c6fd2807SJeff Garzik 
66c6fd2807SJeff Garzik 	/* ADMA_CONTROL register bits */
67c6fd2807SJeff Garzik 	aNIEN			= (1 << 8), /* irq mask: 1==masked */
68c6fd2807SJeff Garzik 	aGO			= (1 << 7), /* packet trigger ("Go!") */
69c6fd2807SJeff Garzik 	aRSTADM			= (1 << 5), /* ADMA logic reset */
70c6fd2807SJeff Garzik 	aPIOMD4			= 0x0003,   /* PIO mode 4 */
71c6fd2807SJeff Garzik 
72c6fd2807SJeff Garzik 	/* ADMA_STATUS register bits */
73c6fd2807SJeff Garzik 	aPSD			= (1 << 6),
74c6fd2807SJeff Garzik 	aUIRQ			= (1 << 4),
75c6fd2807SJeff Garzik 	aPERR			= (1 << 0),
76c6fd2807SJeff Garzik 
77c6fd2807SJeff Garzik 	/* CPB bits */
78c6fd2807SJeff Garzik 	cDONE			= (1 << 0),
79640fdb50SJeff Garzik 	cATERR			= (1 << 3),
80640fdb50SJeff Garzik 
81c6fd2807SJeff Garzik 	cVLD			= (1 << 0),
82c6fd2807SJeff Garzik 	cDAT			= (1 << 2),
83c6fd2807SJeff Garzik 	cIEN			= (1 << 3),
84c6fd2807SJeff Garzik 
85c6fd2807SJeff Garzik 	/* PRD bits */
86c6fd2807SJeff Garzik 	pORD			= (1 << 4),
87c6fd2807SJeff Garzik 	pDIRO			= (1 << 5),
88c6fd2807SJeff Garzik 	pEND			= (1 << 7),
89c6fd2807SJeff Garzik 
90c6fd2807SJeff Garzik 	/* ATA register flags */
91c6fd2807SJeff Garzik 	rIGN			= (1 << 5),
92c6fd2807SJeff Garzik 	rEND			= (1 << 7),
93c6fd2807SJeff Garzik 
94c6fd2807SJeff Garzik 	/* ATA register addresses */
95c6fd2807SJeff Garzik 	ADMA_REGS_CONTROL	= 0x0e,
96c6fd2807SJeff Garzik 	ADMA_REGS_SECTOR_COUNT	= 0x12,
97c6fd2807SJeff Garzik 	ADMA_REGS_LBA_LOW	= 0x13,
98c6fd2807SJeff Garzik 	ADMA_REGS_LBA_MID	= 0x14,
99c6fd2807SJeff Garzik 	ADMA_REGS_LBA_HIGH	= 0x15,
100c6fd2807SJeff Garzik 	ADMA_REGS_DEVICE	= 0x16,
101c6fd2807SJeff Garzik 	ADMA_REGS_COMMAND	= 0x17,
102c6fd2807SJeff Garzik 
103c6fd2807SJeff Garzik 	/* PCI device IDs */
104c6fd2807SJeff Garzik 	board_1841_idx		= 0,	/* ADMA 2-port controller */
105c6fd2807SJeff Garzik };
106c6fd2807SJeff Garzik 
107c6fd2807SJeff Garzik typedef enum { adma_state_idle, adma_state_pkt, adma_state_mmio } adma_state_t;
108c6fd2807SJeff Garzik 
109c6fd2807SJeff Garzik struct adma_port_priv {
110c6fd2807SJeff Garzik 	u8			*pkt;
111c6fd2807SJeff Garzik 	dma_addr_t		pkt_dma;
112c6fd2807SJeff Garzik 	adma_state_t		state;
113c6fd2807SJeff Garzik };
114c6fd2807SJeff Garzik 
115c6fd2807SJeff Garzik static int adma_ata_init_one(struct pci_dev *pdev,
116c6fd2807SJeff Garzik 				const struct pci_device_id *ent);
117c6fd2807SJeff Garzik static int adma_port_start(struct ata_port *ap);
118c6fd2807SJeff Garzik static void adma_port_stop(struct ata_port *ap);
11995364f36SJiri Slaby static enum ata_completion_errors adma_qc_prep(struct ata_queued_cmd *qc);
120c6fd2807SJeff Garzik static unsigned int adma_qc_issue(struct ata_queued_cmd *qc);
121c6fd2807SJeff Garzik static int adma_check_atapi_dma(struct ata_queued_cmd *qc);
122640fdb50SJeff Garzik static void adma_freeze(struct ata_port *ap);
123640fdb50SJeff Garzik static void adma_thaw(struct ata_port *ap);
124a1efdabaSTejun Heo static int adma_prereset(struct ata_link *link, unsigned long deadline);
125c6fd2807SJeff Garzik 
126*25df73d9SBart Van Assche static const struct scsi_host_template adma_ata_sht = {
12768d1d07bSTejun Heo 	ATA_BASE_SHT(DRV_NAME),
12849de0ac8SJeff Garzik 	.sg_tablesize		= LIBATA_MAX_PRD,
12949de0ac8SJeff Garzik 	.dma_boundary		= ADMA_DMA_BOUNDARY,
130c6fd2807SJeff Garzik };
131c6fd2807SJeff Garzik 
132029cfd6bSTejun Heo static struct ata_port_operations adma_ata_ops = {
133b0316b15STejun Heo 	.inherits		= &ata_sff_port_ops,
134029cfd6bSTejun Heo 
135c96f1732SAlan Cox 	.lost_interrupt		= ATA_OP_NULL,
136c96f1732SAlan Cox 
137029cfd6bSTejun Heo 	.check_atapi_dma	= adma_check_atapi_dma,
138c6fd2807SJeff Garzik 	.qc_prep		= adma_qc_prep,
139c6fd2807SJeff Garzik 	.qc_issue		= adma_qc_issue,
140029cfd6bSTejun Heo 
141640fdb50SJeff Garzik 	.freeze			= adma_freeze,
142640fdb50SJeff Garzik 	.thaw			= adma_thaw,
143a1efdabaSTejun Heo 	.prereset		= adma_prereset,
144029cfd6bSTejun Heo 
145c6fd2807SJeff Garzik 	.port_start		= adma_port_start,
146c6fd2807SJeff Garzik 	.port_stop		= adma_port_stop,
147c6fd2807SJeff Garzik };
148c6fd2807SJeff Garzik 
149c6fd2807SJeff Garzik static struct ata_port_info adma_port_info[] = {
150c6fd2807SJeff Garzik 	/* board_1841_idx */
151c6fd2807SJeff Garzik 	{
1529cbe056fSSergei Shtylyov 		.flags		= ATA_FLAG_SLAVE_POSS | ATA_FLAG_PIO_POLLING,
15314bdef98SErik Inge Bolsø 		.pio_mask	= ATA_PIO4_ONLY,
154bf6263a8SJeff Garzik 		.udma_mask	= ATA_UDMA4,
155c6fd2807SJeff Garzik 		.port_ops	= &adma_ata_ops,
156c6fd2807SJeff Garzik 	},
157c6fd2807SJeff Garzik };
158c6fd2807SJeff Garzik 
159c6fd2807SJeff Garzik static const struct pci_device_id adma_ata_pci_tbl[] = {
16054bb3a94SJeff Garzik 	{ PCI_VDEVICE(PDC, 0x1841), board_1841_idx },
161c6fd2807SJeff Garzik 
162c6fd2807SJeff Garzik 	{ }	/* terminate list */
163c6fd2807SJeff Garzik };
164c6fd2807SJeff Garzik 
165c6fd2807SJeff Garzik static struct pci_driver adma_ata_pci_driver = {
166c6fd2807SJeff Garzik 	.name			= DRV_NAME,
167c6fd2807SJeff Garzik 	.id_table		= adma_ata_pci_tbl,
168c6fd2807SJeff Garzik 	.probe			= adma_ata_init_one,
169c6fd2807SJeff Garzik 	.remove			= ata_pci_remove_one,
170c6fd2807SJeff Garzik };
171c6fd2807SJeff Garzik 
adma_check_atapi_dma(struct ata_queued_cmd * qc)172c6fd2807SJeff Garzik static int adma_check_atapi_dma(struct ata_queued_cmd *qc)
173c6fd2807SJeff Garzik {
174c6fd2807SJeff Garzik 	return 1;	/* ATAPI DMA not yet supported */
175c6fd2807SJeff Garzik }
176c6fd2807SJeff Garzik 
adma_reset_engine(struct ata_port * ap)1775d728824STejun Heo static void adma_reset_engine(struct ata_port *ap)
178c6fd2807SJeff Garzik {
1795d728824STejun Heo 	void __iomem *chan = ADMA_PORT_REGS(ap);
1805d728824STejun Heo 
181c6fd2807SJeff Garzik 	/* reset ADMA to idle state */
182c6fd2807SJeff Garzik 	writew(aPIOMD4 | aNIEN | aRSTADM, chan + ADMA_CONTROL);
183c6fd2807SJeff Garzik 	udelay(2);
184c6fd2807SJeff Garzik 	writew(aPIOMD4, chan + ADMA_CONTROL);
185c6fd2807SJeff Garzik 	udelay(2);
186c6fd2807SJeff Garzik }
187c6fd2807SJeff Garzik 
adma_reinit_engine(struct ata_port * ap)188c6fd2807SJeff Garzik static void adma_reinit_engine(struct ata_port *ap)
189c6fd2807SJeff Garzik {
190c6fd2807SJeff Garzik 	struct adma_port_priv *pp = ap->private_data;
1915d728824STejun Heo 	void __iomem *chan = ADMA_PORT_REGS(ap);
192c6fd2807SJeff Garzik 
193c6fd2807SJeff Garzik 	/* mask/clear ATA interrupts */
1940d5ff566STejun Heo 	writeb(ATA_NIEN, ap->ioaddr.ctl_addr);
1959363c382STejun Heo 	ata_sff_check_status(ap);
196c6fd2807SJeff Garzik 
197c6fd2807SJeff Garzik 	/* reset the ADMA engine */
1985d728824STejun Heo 	adma_reset_engine(ap);
199c6fd2807SJeff Garzik 
200c6fd2807SJeff Garzik 	/* set in-FIFO threshold to 0x100 */
201c6fd2807SJeff Garzik 	writew(0x100, chan + ADMA_FIFO_IN);
202c6fd2807SJeff Garzik 
203c6fd2807SJeff Garzik 	/* set CPB pointer */
204c6fd2807SJeff Garzik 	writel((u32)pp->pkt_dma, chan + ADMA_CPB_NEXT);
205c6fd2807SJeff Garzik 
206c6fd2807SJeff Garzik 	/* set out-FIFO threshold to 0x100 */
207c6fd2807SJeff Garzik 	writew(0x100, chan + ADMA_FIFO_OUT);
208c6fd2807SJeff Garzik 
209c6fd2807SJeff Garzik 	/* set CPB count */
210c6fd2807SJeff Garzik 	writew(1, chan + ADMA_CPB_COUNT);
211c6fd2807SJeff Garzik 
212c6fd2807SJeff Garzik 	/* read/discard ADMA status */
213c6fd2807SJeff Garzik 	readb(chan + ADMA_STATUS);
214c6fd2807SJeff Garzik }
215c6fd2807SJeff Garzik 
adma_enter_reg_mode(struct ata_port * ap)216c6fd2807SJeff Garzik static inline void adma_enter_reg_mode(struct ata_port *ap)
217c6fd2807SJeff Garzik {
2185d728824STejun Heo 	void __iomem *chan = ADMA_PORT_REGS(ap);
219c6fd2807SJeff Garzik 
220c6fd2807SJeff Garzik 	writew(aPIOMD4, chan + ADMA_CONTROL);
221c6fd2807SJeff Garzik 	readb(chan + ADMA_STATUS);	/* flush */
222c6fd2807SJeff Garzik }
223c6fd2807SJeff Garzik 
adma_freeze(struct ata_port * ap)224640fdb50SJeff Garzik static void adma_freeze(struct ata_port *ap)
225c6fd2807SJeff Garzik {
226640fdb50SJeff Garzik 	void __iomem *chan = ADMA_PORT_REGS(ap);
227c6fd2807SJeff Garzik 
228640fdb50SJeff Garzik 	/* mask/clear ATA interrupts */
229640fdb50SJeff Garzik 	writeb(ATA_NIEN, ap->ioaddr.ctl_addr);
2309363c382STejun Heo 	ata_sff_check_status(ap);
231640fdb50SJeff Garzik 
232640fdb50SJeff Garzik 	/* reset ADMA to idle state */
233640fdb50SJeff Garzik 	writew(aPIOMD4 | aNIEN | aRSTADM, chan + ADMA_CONTROL);
234640fdb50SJeff Garzik 	udelay(2);
235640fdb50SJeff Garzik 	writew(aPIOMD4 | aNIEN, chan + ADMA_CONTROL);
236640fdb50SJeff Garzik 	udelay(2);
237c6fd2807SJeff Garzik }
238c6fd2807SJeff Garzik 
adma_thaw(struct ata_port * ap)239640fdb50SJeff Garzik static void adma_thaw(struct ata_port *ap)
240640fdb50SJeff Garzik {
241640fdb50SJeff Garzik 	adma_reinit_engine(ap);
242640fdb50SJeff Garzik }
243640fdb50SJeff Garzik 
adma_prereset(struct ata_link * link,unsigned long deadline)2440260731fSTejun Heo static int adma_prereset(struct ata_link *link, unsigned long deadline)
245c6fd2807SJeff Garzik {
2460260731fSTejun Heo 	struct ata_port *ap = link->ap;
247c6fd2807SJeff Garzik 	struct adma_port_priv *pp = ap->private_data;
248c6fd2807SJeff Garzik 
249c6fd2807SJeff Garzik 	if (pp->state != adma_state_idle) /* healthy paranoia */
250c6fd2807SJeff Garzik 		pp->state = adma_state_mmio;
251c6fd2807SJeff Garzik 	adma_reinit_engine(ap);
252640fdb50SJeff Garzik 
2539363c382STejun Heo 	return ata_sff_prereset(link, deadline);
254640fdb50SJeff Garzik }
255640fdb50SJeff Garzik 
adma_fill_sg(struct ata_queued_cmd * qc)256c6fd2807SJeff Garzik static int adma_fill_sg(struct ata_queued_cmd *qc)
257c6fd2807SJeff Garzik {
258c6fd2807SJeff Garzik 	struct scatterlist *sg;
259c6fd2807SJeff Garzik 	struct ata_port *ap = qc->ap;
260c6fd2807SJeff Garzik 	struct adma_port_priv *pp = ap->private_data;
2613be6cbd7SJeff Garzik 	u8  *buf = pp->pkt, *last_buf = NULL;
262c6fd2807SJeff Garzik 	int i = (2 + buf[3]) * 8;
263c6fd2807SJeff Garzik 	u8 pFLAGS = pORD | ((qc->tf.flags & ATA_TFLAG_WRITE) ? pDIRO : 0);
264ff2aeb1eSTejun Heo 	unsigned int si;
265c6fd2807SJeff Garzik 
266ff2aeb1eSTejun Heo 	for_each_sg(qc->sg, sg, qc->n_elem, si) {
267c6fd2807SJeff Garzik 		u32 addr;
268c6fd2807SJeff Garzik 		u32 len;
269c6fd2807SJeff Garzik 
270c6fd2807SJeff Garzik 		addr = (u32)sg_dma_address(sg);
271c6fd2807SJeff Garzik 		*(__le32 *)(buf + i) = cpu_to_le32(addr);
272c6fd2807SJeff Garzik 		i += 4;
273c6fd2807SJeff Garzik 
274c6fd2807SJeff Garzik 		len = sg_dma_len(sg) >> 3;
275c6fd2807SJeff Garzik 		*(__le32 *)(buf + i) = cpu_to_le32(len);
276c6fd2807SJeff Garzik 		i += 4;
277c6fd2807SJeff Garzik 
2783be6cbd7SJeff Garzik 		last_buf = &buf[i];
279c6fd2807SJeff Garzik 		buf[i++] = pFLAGS;
280c6fd2807SJeff Garzik 		buf[i++] = qc->dev->dma_mode & 0xf;
281c6fd2807SJeff Garzik 		buf[i++] = 0;	/* pPKLW */
282c6fd2807SJeff Garzik 		buf[i++] = 0;	/* reserved */
283c6fd2807SJeff Garzik 
2845796d1c4SJeff Garzik 		*(__le32 *)(buf + i) =
2855796d1c4SJeff Garzik 			(pFLAGS & pEND) ? 0 : cpu_to_le32(pp->pkt_dma + i + 4);
286c6fd2807SJeff Garzik 		i += 4;
287c6fd2807SJeff Garzik 	}
2883be6cbd7SJeff Garzik 
2893be6cbd7SJeff Garzik 	if (likely(last_buf))
2903be6cbd7SJeff Garzik 		*last_buf |= pEND;
2913be6cbd7SJeff Garzik 
292c6fd2807SJeff Garzik 	return i;
293c6fd2807SJeff Garzik }
294c6fd2807SJeff Garzik 
adma_qc_prep(struct ata_queued_cmd * qc)29595364f36SJiri Slaby static enum ata_completion_errors adma_qc_prep(struct ata_queued_cmd *qc)
296c6fd2807SJeff Garzik {
297c6fd2807SJeff Garzik 	struct adma_port_priv *pp = qc->ap->private_data;
298c6fd2807SJeff Garzik 	u8  *buf = pp->pkt;
299c6fd2807SJeff Garzik 	u32 pkt_dma = (u32)pp->pkt_dma;
300c6fd2807SJeff Garzik 	int i = 0;
301c6fd2807SJeff Garzik 
302c6fd2807SJeff Garzik 	adma_enter_reg_mode(qc->ap);
303f47451c4STejun Heo 	if (qc->tf.protocol != ATA_PROT_DMA)
30495364f36SJiri Slaby 		return AC_ERR_OK;
305c6fd2807SJeff Garzik 
306c6fd2807SJeff Garzik 	buf[i++] = 0;	/* Response flags */
307c6fd2807SJeff Garzik 	buf[i++] = 0;	/* reserved */
308c6fd2807SJeff Garzik 	buf[i++] = cVLD | cDAT | cIEN;
309c6fd2807SJeff Garzik 	i++;		/* cLEN, gets filled in below */
310c6fd2807SJeff Garzik 
311c6fd2807SJeff Garzik 	*(__le32 *)(buf+i) = cpu_to_le32(pkt_dma);	/* cNCPB */
312c6fd2807SJeff Garzik 	i += 4;		/* cNCPB */
313c6fd2807SJeff Garzik 	i += 4;		/* cPRD, gets filled in below */
314c6fd2807SJeff Garzik 
315c6fd2807SJeff Garzik 	buf[i++] = 0;	/* reserved */
316c6fd2807SJeff Garzik 	buf[i++] = 0;	/* reserved */
317c6fd2807SJeff Garzik 	buf[i++] = 0;	/* reserved */
318c6fd2807SJeff Garzik 	buf[i++] = 0;	/* reserved */
319c6fd2807SJeff Garzik 
320c6fd2807SJeff Garzik 	/* ATA registers; must be a multiple of 4 */
321c6fd2807SJeff Garzik 	buf[i++] = qc->tf.device;
322c6fd2807SJeff Garzik 	buf[i++] = ADMA_REGS_DEVICE;
323c6fd2807SJeff Garzik 	if ((qc->tf.flags & ATA_TFLAG_LBA48)) {
324c6fd2807SJeff Garzik 		buf[i++] = qc->tf.hob_nsect;
325c6fd2807SJeff Garzik 		buf[i++] = ADMA_REGS_SECTOR_COUNT;
326c6fd2807SJeff Garzik 		buf[i++] = qc->tf.hob_lbal;
327c6fd2807SJeff Garzik 		buf[i++] = ADMA_REGS_LBA_LOW;
328c6fd2807SJeff Garzik 		buf[i++] = qc->tf.hob_lbam;
329c6fd2807SJeff Garzik 		buf[i++] = ADMA_REGS_LBA_MID;
330c6fd2807SJeff Garzik 		buf[i++] = qc->tf.hob_lbah;
331c6fd2807SJeff Garzik 		buf[i++] = ADMA_REGS_LBA_HIGH;
332c6fd2807SJeff Garzik 	}
333c6fd2807SJeff Garzik 	buf[i++] = qc->tf.nsect;
334c6fd2807SJeff Garzik 	buf[i++] = ADMA_REGS_SECTOR_COUNT;
335c6fd2807SJeff Garzik 	buf[i++] = qc->tf.lbal;
336c6fd2807SJeff Garzik 	buf[i++] = ADMA_REGS_LBA_LOW;
337c6fd2807SJeff Garzik 	buf[i++] = qc->tf.lbam;
338c6fd2807SJeff Garzik 	buf[i++] = ADMA_REGS_LBA_MID;
339c6fd2807SJeff Garzik 	buf[i++] = qc->tf.lbah;
340c6fd2807SJeff Garzik 	buf[i++] = ADMA_REGS_LBA_HIGH;
341c6fd2807SJeff Garzik 	buf[i++] = 0;
342c6fd2807SJeff Garzik 	buf[i++] = ADMA_REGS_CONTROL;
343c6fd2807SJeff Garzik 	buf[i++] = rIGN;
344c6fd2807SJeff Garzik 	buf[i++] = 0;
345c6fd2807SJeff Garzik 	buf[i++] = qc->tf.command;
346c6fd2807SJeff Garzik 	buf[i++] = ADMA_REGS_COMMAND | rEND;
347c6fd2807SJeff Garzik 
348c6fd2807SJeff Garzik 	buf[3] = (i >> 3) - 2;				/* cLEN */
349c6fd2807SJeff Garzik 	*(__le32 *)(buf+8) = cpu_to_le32(pkt_dma + i);	/* cPRD */
350c6fd2807SJeff Garzik 
351c6fd2807SJeff Garzik 	i = adma_fill_sg(qc);
352c6fd2807SJeff Garzik 	wmb();	/* flush PRDs and pkt to memory */
35395364f36SJiri Slaby 	return AC_ERR_OK;
354c6fd2807SJeff Garzik }
355c6fd2807SJeff Garzik 
adma_packet_start(struct ata_queued_cmd * qc)356c6fd2807SJeff Garzik static inline void adma_packet_start(struct ata_queued_cmd *qc)
357c6fd2807SJeff Garzik {
358c6fd2807SJeff Garzik 	struct ata_port *ap = qc->ap;
3595d728824STejun Heo 	void __iomem *chan = ADMA_PORT_REGS(ap);
360c6fd2807SJeff Garzik 
361c6fd2807SJeff Garzik 	/* fire up the ADMA engine */
362c6fd2807SJeff Garzik 	writew(aPIOMD4 | aGO, chan + ADMA_CONTROL);
363c6fd2807SJeff Garzik }
364c6fd2807SJeff Garzik 
adma_qc_issue(struct ata_queued_cmd * qc)365c6fd2807SJeff Garzik static unsigned int adma_qc_issue(struct ata_queued_cmd *qc)
366c6fd2807SJeff Garzik {
367c6fd2807SJeff Garzik 	struct adma_port_priv *pp = qc->ap->private_data;
368c6fd2807SJeff Garzik 
369c6fd2807SJeff Garzik 	switch (qc->tf.protocol) {
370c6fd2807SJeff Garzik 	case ATA_PROT_DMA:
371c6fd2807SJeff Garzik 		pp->state = adma_state_pkt;
372c6fd2807SJeff Garzik 		adma_packet_start(qc);
373c6fd2807SJeff Garzik 		return 0;
374c6fd2807SJeff Garzik 
3750dc36888STejun Heo 	case ATAPI_PROT_DMA:
376c6fd2807SJeff Garzik 		BUG();
377c6fd2807SJeff Garzik 		break;
378c6fd2807SJeff Garzik 
379c6fd2807SJeff Garzik 	default:
380c6fd2807SJeff Garzik 		break;
381c6fd2807SJeff Garzik 	}
382c6fd2807SJeff Garzik 
383c6fd2807SJeff Garzik 	pp->state = adma_state_mmio;
3849363c382STejun Heo 	return ata_sff_qc_issue(qc);
385c6fd2807SJeff Garzik }
386c6fd2807SJeff Garzik 
adma_intr_pkt(struct ata_host * host)387cca3974eSJeff Garzik static inline unsigned int adma_intr_pkt(struct ata_host *host)
388c6fd2807SJeff Garzik {
389c6fd2807SJeff Garzik 	unsigned int handled = 0, port_no;
390c6fd2807SJeff Garzik 
391cca3974eSJeff Garzik 	for (port_no = 0; port_no < host->n_ports; ++port_no) {
392cca3974eSJeff Garzik 		struct ata_port *ap = host->ports[port_no];
393c6fd2807SJeff Garzik 		struct adma_port_priv *pp;
394c6fd2807SJeff Garzik 		struct ata_queued_cmd *qc;
3955d728824STejun Heo 		void __iomem *chan = ADMA_PORT_REGS(ap);
396c6fd2807SJeff Garzik 		u8 status = readb(chan + ADMA_STATUS);
397c6fd2807SJeff Garzik 
398c6fd2807SJeff Garzik 		if (status == 0)
399c6fd2807SJeff Garzik 			continue;
400c6fd2807SJeff Garzik 		handled = 1;
401c6fd2807SJeff Garzik 		adma_enter_reg_mode(ap);
402c6fd2807SJeff Garzik 		pp = ap->private_data;
403c6fd2807SJeff Garzik 		if (!pp || pp->state != adma_state_pkt)
404c6fd2807SJeff Garzik 			continue;
4059af5c9c9STejun Heo 		qc = ata_qc_from_tag(ap, ap->link.active_tag);
406c6fd2807SJeff Garzik 		if (qc && (!(qc->tf.flags & ATA_TFLAG_POLLING))) {
407640fdb50SJeff Garzik 			if (status & aPERR)
408640fdb50SJeff Garzik 				qc->err_mask |= AC_ERR_HOST_BUS;
409640fdb50SJeff Garzik 			else if ((status & (aPSD | aUIRQ)))
410c6fd2807SJeff Garzik 				qc->err_mask |= AC_ERR_OTHER;
411640fdb50SJeff Garzik 
412640fdb50SJeff Garzik 			if (pp->pkt[0] & cATERR)
413640fdb50SJeff Garzik 				qc->err_mask |= AC_ERR_DEV;
414c6fd2807SJeff Garzik 			else if (pp->pkt[0] != cDONE)
415c6fd2807SJeff Garzik 				qc->err_mask |= AC_ERR_OTHER;
416c6fd2807SJeff Garzik 
417640fdb50SJeff Garzik 			if (!qc->err_mask)
418c6fd2807SJeff Garzik 				ata_qc_complete(qc);
419640fdb50SJeff Garzik 			else {
4209af5c9c9STejun Heo 				struct ata_eh_info *ehi = &ap->link.eh_info;
421640fdb50SJeff Garzik 				ata_ehi_clear_desc(ehi);
422640fdb50SJeff Garzik 				ata_ehi_push_desc(ehi,
423640fdb50SJeff Garzik 					"ADMA-status 0x%02X", status);
424640fdb50SJeff Garzik 				ata_ehi_push_desc(ehi,
425640fdb50SJeff Garzik 					"pkt[0] 0x%02X", pp->pkt[0]);
426640fdb50SJeff Garzik 
427640fdb50SJeff Garzik 				if (qc->err_mask == AC_ERR_DEV)
428640fdb50SJeff Garzik 					ata_port_abort(ap);
429640fdb50SJeff Garzik 				else
430640fdb50SJeff Garzik 					ata_port_freeze(ap);
431640fdb50SJeff Garzik 			}
432c6fd2807SJeff Garzik 		}
433c6fd2807SJeff Garzik 	}
434c6fd2807SJeff Garzik 	return handled;
435c6fd2807SJeff Garzik }
436c6fd2807SJeff Garzik 
adma_intr_mmio(struct ata_host * host)437cca3974eSJeff Garzik static inline unsigned int adma_intr_mmio(struct ata_host *host)
438c6fd2807SJeff Garzik {
439c6fd2807SJeff Garzik 	unsigned int handled = 0, port_no;
440c6fd2807SJeff Garzik 
441cca3974eSJeff Garzik 	for (port_no = 0; port_no < host->n_ports; ++port_no) {
4423e4ec344STejun Heo 		struct ata_port *ap = host->ports[port_no];
443c6fd2807SJeff Garzik 		struct adma_port_priv *pp = ap->private_data;
4443e4ec344STejun Heo 		struct ata_queued_cmd *qc;
4453e4ec344STejun Heo 
446c6fd2807SJeff Garzik 		if (!pp || pp->state != adma_state_mmio)
447c6fd2807SJeff Garzik 			continue;
4489af5c9c9STejun Heo 		qc = ata_qc_from_tag(ap, ap->link.active_tag);
449c6fd2807SJeff Garzik 		if (qc && (!(qc->tf.flags & ATA_TFLAG_POLLING))) {
450c6fd2807SJeff Garzik 
451c6fd2807SJeff Garzik 			/* check main status, clearing INTRQ */
4529363c382STejun Heo 			u8 status = ata_sff_check_status(ap);
453c6fd2807SJeff Garzik 			if ((status & ATA_BUSY))
454c6fd2807SJeff Garzik 				continue;
455c6fd2807SJeff Garzik 
456c6fd2807SJeff Garzik 			/* complete taskfile transaction */
457c6fd2807SJeff Garzik 			pp->state = adma_state_idle;
458c6fd2807SJeff Garzik 			qc->err_mask |= ac_err_mask(status);
459640fdb50SJeff Garzik 			if (!qc->err_mask)
460c6fd2807SJeff Garzik 				ata_qc_complete(qc);
461640fdb50SJeff Garzik 			else {
4623e4ec344STejun Heo 				struct ata_eh_info *ehi = &ap->link.eh_info;
463640fdb50SJeff Garzik 				ata_ehi_clear_desc(ehi);
4643e4ec344STejun Heo 				ata_ehi_push_desc(ehi, "status 0x%02X", status);
465640fdb50SJeff Garzik 
466640fdb50SJeff Garzik 				if (qc->err_mask == AC_ERR_DEV)
467640fdb50SJeff Garzik 					ata_port_abort(ap);
468640fdb50SJeff Garzik 				else
469640fdb50SJeff Garzik 					ata_port_freeze(ap);
470640fdb50SJeff Garzik 			}
471c6fd2807SJeff Garzik 			handled = 1;
472c6fd2807SJeff Garzik 		}
473c6fd2807SJeff Garzik 	}
474c6fd2807SJeff Garzik 	return handled;
475c6fd2807SJeff Garzik }
476c6fd2807SJeff Garzik 
adma_intr(int irq,void * dev_instance)4777d12e780SDavid Howells static irqreturn_t adma_intr(int irq, void *dev_instance)
478c6fd2807SJeff Garzik {
479cca3974eSJeff Garzik 	struct ata_host *host = dev_instance;
480c6fd2807SJeff Garzik 	unsigned int handled = 0;
481c6fd2807SJeff Garzik 
482cca3974eSJeff Garzik 	spin_lock(&host->lock);
483cca3974eSJeff Garzik 	handled  = adma_intr_pkt(host) | adma_intr_mmio(host);
484cca3974eSJeff Garzik 	spin_unlock(&host->lock);
485c6fd2807SJeff Garzik 
486c6fd2807SJeff Garzik 	return IRQ_RETVAL(handled);
487c6fd2807SJeff Garzik }
488c6fd2807SJeff Garzik 
adma_ata_setup_port(struct ata_ioports * port,void __iomem * base)4890d5ff566STejun Heo static void adma_ata_setup_port(struct ata_ioports *port, void __iomem *base)
490c6fd2807SJeff Garzik {
491c6fd2807SJeff Garzik 	port->cmd_addr		=
492c6fd2807SJeff Garzik 	port->data_addr		= base + 0x000;
493c6fd2807SJeff Garzik 	port->error_addr	=
494c6fd2807SJeff Garzik 	port->feature_addr	= base + 0x004;
495c6fd2807SJeff Garzik 	port->nsect_addr	= base + 0x008;
496c6fd2807SJeff Garzik 	port->lbal_addr		= base + 0x00c;
497c6fd2807SJeff Garzik 	port->lbam_addr		= base + 0x010;
498c6fd2807SJeff Garzik 	port->lbah_addr		= base + 0x014;
499c6fd2807SJeff Garzik 	port->device_addr	= base + 0x018;
500c6fd2807SJeff Garzik 	port->status_addr	=
501c6fd2807SJeff Garzik 	port->command_addr	= base + 0x01c;
502c6fd2807SJeff Garzik 	port->altstatus_addr	=
503c6fd2807SJeff Garzik 	port->ctl_addr		= base + 0x038;
504c6fd2807SJeff Garzik }
505c6fd2807SJeff Garzik 
adma_port_start(struct ata_port * ap)506c6fd2807SJeff Garzik static int adma_port_start(struct ata_port *ap)
507c6fd2807SJeff Garzik {
508cca3974eSJeff Garzik 	struct device *dev = ap->host->dev;
509c6fd2807SJeff Garzik 	struct adma_port_priv *pp;
510c6fd2807SJeff Garzik 
511c6fd2807SJeff Garzik 	adma_enter_reg_mode(ap);
51224dc5f33STejun Heo 	pp = devm_kzalloc(dev, sizeof(*pp), GFP_KERNEL);
513c6fd2807SJeff Garzik 	if (!pp)
51424dc5f33STejun Heo 		return -ENOMEM;
51524dc5f33STejun Heo 	pp->pkt = dmam_alloc_coherent(dev, ADMA_PKT_BYTES, &pp->pkt_dma,
516c6fd2807SJeff Garzik 				      GFP_KERNEL);
517c6fd2807SJeff Garzik 	if (!pp->pkt)
51824dc5f33STejun Heo 		return -ENOMEM;
519c6fd2807SJeff Garzik 	/* paranoia? */
520c6fd2807SJeff Garzik 	if ((pp->pkt_dma & 7) != 0) {
52151d628f1SHannes Reinecke 		ata_port_err(ap, "bad alignment for pp->pkt_dma: %08x\n",
522c6fd2807SJeff Garzik 			     (u32)pp->pkt_dma);
52324dc5f33STejun Heo 		return -ENOMEM;
524c6fd2807SJeff Garzik 	}
525c6fd2807SJeff Garzik 	ap->private_data = pp;
526c6fd2807SJeff Garzik 	adma_reinit_engine(ap);
527c6fd2807SJeff Garzik 	return 0;
528c6fd2807SJeff Garzik }
529c6fd2807SJeff Garzik 
adma_port_stop(struct ata_port * ap)530c6fd2807SJeff Garzik static void adma_port_stop(struct ata_port *ap)
531c6fd2807SJeff Garzik {
5325d728824STejun Heo 	adma_reset_engine(ap);
533c6fd2807SJeff Garzik }
534c6fd2807SJeff Garzik 
adma_host_init(struct ata_host * host,unsigned int chip_id)5355d728824STejun Heo static void adma_host_init(struct ata_host *host, unsigned int chip_id)
536c6fd2807SJeff Garzik {
537c6fd2807SJeff Garzik 	unsigned int port_no;
538c6fd2807SJeff Garzik 
539c6fd2807SJeff Garzik 	/* enable/lock aGO operation */
5405d728824STejun Heo 	writeb(7, host->iomap[ADMA_MMIO_BAR] + ADMA_MODE_LOCK);
541c6fd2807SJeff Garzik 
542c6fd2807SJeff Garzik 	/* reset the ADMA logic */
543c6fd2807SJeff Garzik 	for (port_no = 0; port_no < ADMA_PORTS; ++port_no)
5445d728824STejun Heo 		adma_reset_engine(host->ports[port_no]);
545c6fd2807SJeff Garzik }
546c6fd2807SJeff Garzik 
adma_ata_init_one(struct pci_dev * pdev,const struct pci_device_id * ent)547c6fd2807SJeff Garzik static int adma_ata_init_one(struct pci_dev *pdev,
548c6fd2807SJeff Garzik 			     const struct pci_device_id *ent)
549c6fd2807SJeff Garzik {
550c6fd2807SJeff Garzik 	unsigned int board_idx = (unsigned int) ent->driver_data;
5515d728824STejun Heo 	const struct ata_port_info *ppi[] = { &adma_port_info[board_idx], NULL };
5525d728824STejun Heo 	struct ata_host *host;
5535d728824STejun Heo 	void __iomem *mmio_base;
554c6fd2807SJeff Garzik 	int rc, port_no;
555c6fd2807SJeff Garzik 
55606296a1eSJoe Perches 	ata_print_version_once(&pdev->dev, DRV_VERSION);
557c6fd2807SJeff Garzik 
5585d728824STejun Heo 	/* alloc host */
5595d728824STejun Heo 	host = ata_host_alloc_pinfo(&pdev->dev, ppi, ADMA_PORTS);
5605d728824STejun Heo 	if (!host)
5615d728824STejun Heo 		return -ENOMEM;
5625d728824STejun Heo 
5635d728824STejun Heo 	/* acquire resources and fill host */
56424dc5f33STejun Heo 	rc = pcim_enable_device(pdev);
565c6fd2807SJeff Garzik 	if (rc)
566c6fd2807SJeff Garzik 		return rc;
567c6fd2807SJeff Garzik 
56824dc5f33STejun Heo 	if ((pci_resource_flags(pdev, 4) & IORESOURCE_MEM) == 0)
56924dc5f33STejun Heo 		return -ENODEV;
570c6fd2807SJeff Garzik 
5710d5ff566STejun Heo 	rc = pcim_iomap_regions(pdev, 1 << ADMA_MMIO_BAR, DRV_NAME);
5720d5ff566STejun Heo 	if (rc)
5730d5ff566STejun Heo 		return rc;
5745d728824STejun Heo 	host->iomap = pcim_iomap_table(pdev);
5755d728824STejun Heo 	mmio_base = host->iomap[ADMA_MMIO_BAR];
576c6fd2807SJeff Garzik 
57794c58148SChristoph Hellwig 	rc = dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(32));
57894c58148SChristoph Hellwig 	if (rc) {
57994c58148SChristoph Hellwig 		dev_err(&pdev->dev, "32-bit DMA enable failed\n");
58024dc5f33STejun Heo 		return rc;
58194c58148SChristoph Hellwig 	}
582c6fd2807SJeff Garzik 
583cbcdd875STejun Heo 	for (port_no = 0; port_no < ADMA_PORTS; ++port_no) {
584cbcdd875STejun Heo 		struct ata_port *ap = host->ports[port_no];
585cbcdd875STejun Heo 		void __iomem *port_base = ADMA_ATA_REGS(mmio_base, port_no);
586cbcdd875STejun Heo 		unsigned int offset = port_base - mmio_base;
587cbcdd875STejun Heo 
588cbcdd875STejun Heo 		adma_ata_setup_port(&ap->ioaddr, port_base);
589cbcdd875STejun Heo 
590cbcdd875STejun Heo 		ata_port_pbar_desc(ap, ADMA_MMIO_BAR, -1, "mmio");
591cbcdd875STejun Heo 		ata_port_pbar_desc(ap, ADMA_MMIO_BAR, offset, "port");
592cbcdd875STejun Heo 	}
593c6fd2807SJeff Garzik 
594c6fd2807SJeff Garzik 	/* initialize adapter */
5955d728824STejun Heo 	adma_host_init(host, board_idx);
596c6fd2807SJeff Garzik 
5975d728824STejun Heo 	pci_set_master(pdev);
5985d728824STejun Heo 	return ata_host_activate(host, pdev->irq, adma_intr, IRQF_SHARED,
5995d728824STejun Heo 				 &adma_ata_sht);
600c6fd2807SJeff Garzik }
601c6fd2807SJeff Garzik 
6022fc75da0SAxel Lin module_pci_driver(adma_ata_pci_driver);
603c6fd2807SJeff Garzik 
604c6fd2807SJeff Garzik MODULE_AUTHOR("Mark Lord");
605c6fd2807SJeff Garzik MODULE_DESCRIPTION("Pacific Digital Corporation ADMA low-level driver");
606c6fd2807SJeff Garzik MODULE_LICENSE("GPL");
607c6fd2807SJeff Garzik MODULE_DEVICE_TABLE(pci, adma_ata_pci_tbl);
608c6fd2807SJeff Garzik MODULE_VERSION(DRV_VERSION);
609