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