18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * pdc_adma.c - Pacific Digital Corporation ADMA 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Maintained by: Tejun Heo <tj@kernel.org> 68c2ecf20Sopenharmony_ci * 78c2ecf20Sopenharmony_ci * Copyright 2005 Mark Lord 88c2ecf20Sopenharmony_ci * 98c2ecf20Sopenharmony_ci * libata documentation is available via 'make {ps|pdf}docs', 108c2ecf20Sopenharmony_ci * as Documentation/driver-api/libata.rst 118c2ecf20Sopenharmony_ci * 128c2ecf20Sopenharmony_ci * Supports ATA disks in single-packet ADMA mode. 138c2ecf20Sopenharmony_ci * Uses PIO for everything else. 148c2ecf20Sopenharmony_ci * 158c2ecf20Sopenharmony_ci * TODO: Use ADMA transfers for ATAPI devices, when possible. 168c2ecf20Sopenharmony_ci * This requires careful attention to a number of quirks of the chip. 178c2ecf20Sopenharmony_ci */ 188c2ecf20Sopenharmony_ci 198c2ecf20Sopenharmony_ci#include <linux/kernel.h> 208c2ecf20Sopenharmony_ci#include <linux/module.h> 218c2ecf20Sopenharmony_ci#include <linux/gfp.h> 228c2ecf20Sopenharmony_ci#include <linux/pci.h> 238c2ecf20Sopenharmony_ci#include <linux/blkdev.h> 248c2ecf20Sopenharmony_ci#include <linux/delay.h> 258c2ecf20Sopenharmony_ci#include <linux/interrupt.h> 268c2ecf20Sopenharmony_ci#include <linux/device.h> 278c2ecf20Sopenharmony_ci#include <scsi/scsi_host.h> 288c2ecf20Sopenharmony_ci#include <linux/libata.h> 298c2ecf20Sopenharmony_ci 308c2ecf20Sopenharmony_ci#define DRV_NAME "pdc_adma" 318c2ecf20Sopenharmony_ci#define DRV_VERSION "1.0" 328c2ecf20Sopenharmony_ci 338c2ecf20Sopenharmony_ci/* macro to calculate base address for ATA regs */ 348c2ecf20Sopenharmony_ci#define ADMA_ATA_REGS(base, port_no) ((base) + ((port_no) * 0x40)) 358c2ecf20Sopenharmony_ci 368c2ecf20Sopenharmony_ci/* macro to calculate base address for ADMA regs */ 378c2ecf20Sopenharmony_ci#define ADMA_REGS(base, port_no) ((base) + 0x80 + ((port_no) * 0x20)) 388c2ecf20Sopenharmony_ci 398c2ecf20Sopenharmony_ci/* macro to obtain addresses from ata_port */ 408c2ecf20Sopenharmony_ci#define ADMA_PORT_REGS(ap) \ 418c2ecf20Sopenharmony_ci ADMA_REGS((ap)->host->iomap[ADMA_MMIO_BAR], ap->port_no) 428c2ecf20Sopenharmony_ci 438c2ecf20Sopenharmony_cienum { 448c2ecf20Sopenharmony_ci ADMA_MMIO_BAR = 4, 458c2ecf20Sopenharmony_ci 468c2ecf20Sopenharmony_ci ADMA_PORTS = 2, 478c2ecf20Sopenharmony_ci ADMA_CPB_BYTES = 40, 488c2ecf20Sopenharmony_ci ADMA_PRD_BYTES = LIBATA_MAX_PRD * 16, 498c2ecf20Sopenharmony_ci ADMA_PKT_BYTES = ADMA_CPB_BYTES + ADMA_PRD_BYTES, 508c2ecf20Sopenharmony_ci 518c2ecf20Sopenharmony_ci ADMA_DMA_BOUNDARY = 0xffffffff, 528c2ecf20Sopenharmony_ci 538c2ecf20Sopenharmony_ci /* global register offsets */ 548c2ecf20Sopenharmony_ci ADMA_MODE_LOCK = 0x00c7, 558c2ecf20Sopenharmony_ci 568c2ecf20Sopenharmony_ci /* per-channel register offsets */ 578c2ecf20Sopenharmony_ci ADMA_CONTROL = 0x0000, /* ADMA control */ 588c2ecf20Sopenharmony_ci ADMA_STATUS = 0x0002, /* ADMA status */ 598c2ecf20Sopenharmony_ci ADMA_CPB_COUNT = 0x0004, /* CPB count */ 608c2ecf20Sopenharmony_ci ADMA_CPB_CURRENT = 0x000c, /* current CPB address */ 618c2ecf20Sopenharmony_ci ADMA_CPB_NEXT = 0x000c, /* next CPB address */ 628c2ecf20Sopenharmony_ci ADMA_CPB_LOOKUP = 0x0010, /* CPB lookup table */ 638c2ecf20Sopenharmony_ci ADMA_FIFO_IN = 0x0014, /* input FIFO threshold */ 648c2ecf20Sopenharmony_ci ADMA_FIFO_OUT = 0x0016, /* output FIFO threshold */ 658c2ecf20Sopenharmony_ci 668c2ecf20Sopenharmony_ci /* ADMA_CONTROL register bits */ 678c2ecf20Sopenharmony_ci aNIEN = (1 << 8), /* irq mask: 1==masked */ 688c2ecf20Sopenharmony_ci aGO = (1 << 7), /* packet trigger ("Go!") */ 698c2ecf20Sopenharmony_ci aRSTADM = (1 << 5), /* ADMA logic reset */ 708c2ecf20Sopenharmony_ci aPIOMD4 = 0x0003, /* PIO mode 4 */ 718c2ecf20Sopenharmony_ci 728c2ecf20Sopenharmony_ci /* ADMA_STATUS register bits */ 738c2ecf20Sopenharmony_ci aPSD = (1 << 6), 748c2ecf20Sopenharmony_ci aUIRQ = (1 << 4), 758c2ecf20Sopenharmony_ci aPERR = (1 << 0), 768c2ecf20Sopenharmony_ci 778c2ecf20Sopenharmony_ci /* CPB bits */ 788c2ecf20Sopenharmony_ci cDONE = (1 << 0), 798c2ecf20Sopenharmony_ci cATERR = (1 << 3), 808c2ecf20Sopenharmony_ci 818c2ecf20Sopenharmony_ci cVLD = (1 << 0), 828c2ecf20Sopenharmony_ci cDAT = (1 << 2), 838c2ecf20Sopenharmony_ci cIEN = (1 << 3), 848c2ecf20Sopenharmony_ci 858c2ecf20Sopenharmony_ci /* PRD bits */ 868c2ecf20Sopenharmony_ci pORD = (1 << 4), 878c2ecf20Sopenharmony_ci pDIRO = (1 << 5), 888c2ecf20Sopenharmony_ci pEND = (1 << 7), 898c2ecf20Sopenharmony_ci 908c2ecf20Sopenharmony_ci /* ATA register flags */ 918c2ecf20Sopenharmony_ci rIGN = (1 << 5), 928c2ecf20Sopenharmony_ci rEND = (1 << 7), 938c2ecf20Sopenharmony_ci 948c2ecf20Sopenharmony_ci /* ATA register addresses */ 958c2ecf20Sopenharmony_ci ADMA_REGS_CONTROL = 0x0e, 968c2ecf20Sopenharmony_ci ADMA_REGS_SECTOR_COUNT = 0x12, 978c2ecf20Sopenharmony_ci ADMA_REGS_LBA_LOW = 0x13, 988c2ecf20Sopenharmony_ci ADMA_REGS_LBA_MID = 0x14, 998c2ecf20Sopenharmony_ci ADMA_REGS_LBA_HIGH = 0x15, 1008c2ecf20Sopenharmony_ci ADMA_REGS_DEVICE = 0x16, 1018c2ecf20Sopenharmony_ci ADMA_REGS_COMMAND = 0x17, 1028c2ecf20Sopenharmony_ci 1038c2ecf20Sopenharmony_ci /* PCI device IDs */ 1048c2ecf20Sopenharmony_ci board_1841_idx = 0, /* ADMA 2-port controller */ 1058c2ecf20Sopenharmony_ci}; 1068c2ecf20Sopenharmony_ci 1078c2ecf20Sopenharmony_citypedef enum { adma_state_idle, adma_state_pkt, adma_state_mmio } adma_state_t; 1088c2ecf20Sopenharmony_ci 1098c2ecf20Sopenharmony_cistruct adma_port_priv { 1108c2ecf20Sopenharmony_ci u8 *pkt; 1118c2ecf20Sopenharmony_ci dma_addr_t pkt_dma; 1128c2ecf20Sopenharmony_ci adma_state_t state; 1138c2ecf20Sopenharmony_ci}; 1148c2ecf20Sopenharmony_ci 1158c2ecf20Sopenharmony_cistatic int adma_ata_init_one(struct pci_dev *pdev, 1168c2ecf20Sopenharmony_ci const struct pci_device_id *ent); 1178c2ecf20Sopenharmony_cistatic int adma_port_start(struct ata_port *ap); 1188c2ecf20Sopenharmony_cistatic void adma_port_stop(struct ata_port *ap); 1198c2ecf20Sopenharmony_cistatic enum ata_completion_errors adma_qc_prep(struct ata_queued_cmd *qc); 1208c2ecf20Sopenharmony_cistatic unsigned int adma_qc_issue(struct ata_queued_cmd *qc); 1218c2ecf20Sopenharmony_cistatic int adma_check_atapi_dma(struct ata_queued_cmd *qc); 1228c2ecf20Sopenharmony_cistatic void adma_freeze(struct ata_port *ap); 1238c2ecf20Sopenharmony_cistatic void adma_thaw(struct ata_port *ap); 1248c2ecf20Sopenharmony_cistatic int adma_prereset(struct ata_link *link, unsigned long deadline); 1258c2ecf20Sopenharmony_ci 1268c2ecf20Sopenharmony_cistatic struct scsi_host_template adma_ata_sht = { 1278c2ecf20Sopenharmony_ci ATA_BASE_SHT(DRV_NAME), 1288c2ecf20Sopenharmony_ci .sg_tablesize = LIBATA_MAX_PRD, 1298c2ecf20Sopenharmony_ci .dma_boundary = ADMA_DMA_BOUNDARY, 1308c2ecf20Sopenharmony_ci}; 1318c2ecf20Sopenharmony_ci 1328c2ecf20Sopenharmony_cistatic struct ata_port_operations adma_ata_ops = { 1338c2ecf20Sopenharmony_ci .inherits = &ata_sff_port_ops, 1348c2ecf20Sopenharmony_ci 1358c2ecf20Sopenharmony_ci .lost_interrupt = ATA_OP_NULL, 1368c2ecf20Sopenharmony_ci 1378c2ecf20Sopenharmony_ci .check_atapi_dma = adma_check_atapi_dma, 1388c2ecf20Sopenharmony_ci .qc_prep = adma_qc_prep, 1398c2ecf20Sopenharmony_ci .qc_issue = adma_qc_issue, 1408c2ecf20Sopenharmony_ci 1418c2ecf20Sopenharmony_ci .freeze = adma_freeze, 1428c2ecf20Sopenharmony_ci .thaw = adma_thaw, 1438c2ecf20Sopenharmony_ci .prereset = adma_prereset, 1448c2ecf20Sopenharmony_ci 1458c2ecf20Sopenharmony_ci .port_start = adma_port_start, 1468c2ecf20Sopenharmony_ci .port_stop = adma_port_stop, 1478c2ecf20Sopenharmony_ci}; 1488c2ecf20Sopenharmony_ci 1498c2ecf20Sopenharmony_cistatic struct ata_port_info adma_port_info[] = { 1508c2ecf20Sopenharmony_ci /* board_1841_idx */ 1518c2ecf20Sopenharmony_ci { 1528c2ecf20Sopenharmony_ci .flags = ATA_FLAG_SLAVE_POSS | ATA_FLAG_PIO_POLLING, 1538c2ecf20Sopenharmony_ci .pio_mask = ATA_PIO4_ONLY, 1548c2ecf20Sopenharmony_ci .udma_mask = ATA_UDMA4, 1558c2ecf20Sopenharmony_ci .port_ops = &adma_ata_ops, 1568c2ecf20Sopenharmony_ci }, 1578c2ecf20Sopenharmony_ci}; 1588c2ecf20Sopenharmony_ci 1598c2ecf20Sopenharmony_cistatic const struct pci_device_id adma_ata_pci_tbl[] = { 1608c2ecf20Sopenharmony_ci { PCI_VDEVICE(PDC, 0x1841), board_1841_idx }, 1618c2ecf20Sopenharmony_ci 1628c2ecf20Sopenharmony_ci { } /* terminate list */ 1638c2ecf20Sopenharmony_ci}; 1648c2ecf20Sopenharmony_ci 1658c2ecf20Sopenharmony_cistatic struct pci_driver adma_ata_pci_driver = { 1668c2ecf20Sopenharmony_ci .name = DRV_NAME, 1678c2ecf20Sopenharmony_ci .id_table = adma_ata_pci_tbl, 1688c2ecf20Sopenharmony_ci .probe = adma_ata_init_one, 1698c2ecf20Sopenharmony_ci .remove = ata_pci_remove_one, 1708c2ecf20Sopenharmony_ci}; 1718c2ecf20Sopenharmony_ci 1728c2ecf20Sopenharmony_cistatic int adma_check_atapi_dma(struct ata_queued_cmd *qc) 1738c2ecf20Sopenharmony_ci{ 1748c2ecf20Sopenharmony_ci return 1; /* ATAPI DMA not yet supported */ 1758c2ecf20Sopenharmony_ci} 1768c2ecf20Sopenharmony_ci 1778c2ecf20Sopenharmony_cistatic void adma_reset_engine(struct ata_port *ap) 1788c2ecf20Sopenharmony_ci{ 1798c2ecf20Sopenharmony_ci void __iomem *chan = ADMA_PORT_REGS(ap); 1808c2ecf20Sopenharmony_ci 1818c2ecf20Sopenharmony_ci /* reset ADMA to idle state */ 1828c2ecf20Sopenharmony_ci writew(aPIOMD4 | aNIEN | aRSTADM, chan + ADMA_CONTROL); 1838c2ecf20Sopenharmony_ci udelay(2); 1848c2ecf20Sopenharmony_ci writew(aPIOMD4, chan + ADMA_CONTROL); 1858c2ecf20Sopenharmony_ci udelay(2); 1868c2ecf20Sopenharmony_ci} 1878c2ecf20Sopenharmony_ci 1888c2ecf20Sopenharmony_cistatic void adma_reinit_engine(struct ata_port *ap) 1898c2ecf20Sopenharmony_ci{ 1908c2ecf20Sopenharmony_ci struct adma_port_priv *pp = ap->private_data; 1918c2ecf20Sopenharmony_ci void __iomem *chan = ADMA_PORT_REGS(ap); 1928c2ecf20Sopenharmony_ci 1938c2ecf20Sopenharmony_ci /* mask/clear ATA interrupts */ 1948c2ecf20Sopenharmony_ci writeb(ATA_NIEN, ap->ioaddr.ctl_addr); 1958c2ecf20Sopenharmony_ci ata_sff_check_status(ap); 1968c2ecf20Sopenharmony_ci 1978c2ecf20Sopenharmony_ci /* reset the ADMA engine */ 1988c2ecf20Sopenharmony_ci adma_reset_engine(ap); 1998c2ecf20Sopenharmony_ci 2008c2ecf20Sopenharmony_ci /* set in-FIFO threshold to 0x100 */ 2018c2ecf20Sopenharmony_ci writew(0x100, chan + ADMA_FIFO_IN); 2028c2ecf20Sopenharmony_ci 2038c2ecf20Sopenharmony_ci /* set CPB pointer */ 2048c2ecf20Sopenharmony_ci writel((u32)pp->pkt_dma, chan + ADMA_CPB_NEXT); 2058c2ecf20Sopenharmony_ci 2068c2ecf20Sopenharmony_ci /* set out-FIFO threshold to 0x100 */ 2078c2ecf20Sopenharmony_ci writew(0x100, chan + ADMA_FIFO_OUT); 2088c2ecf20Sopenharmony_ci 2098c2ecf20Sopenharmony_ci /* set CPB count */ 2108c2ecf20Sopenharmony_ci writew(1, chan + ADMA_CPB_COUNT); 2118c2ecf20Sopenharmony_ci 2128c2ecf20Sopenharmony_ci /* read/discard ADMA status */ 2138c2ecf20Sopenharmony_ci readb(chan + ADMA_STATUS); 2148c2ecf20Sopenharmony_ci} 2158c2ecf20Sopenharmony_ci 2168c2ecf20Sopenharmony_cistatic inline void adma_enter_reg_mode(struct ata_port *ap) 2178c2ecf20Sopenharmony_ci{ 2188c2ecf20Sopenharmony_ci void __iomem *chan = ADMA_PORT_REGS(ap); 2198c2ecf20Sopenharmony_ci 2208c2ecf20Sopenharmony_ci writew(aPIOMD4, chan + ADMA_CONTROL); 2218c2ecf20Sopenharmony_ci readb(chan + ADMA_STATUS); /* flush */ 2228c2ecf20Sopenharmony_ci} 2238c2ecf20Sopenharmony_ci 2248c2ecf20Sopenharmony_cistatic void adma_freeze(struct ata_port *ap) 2258c2ecf20Sopenharmony_ci{ 2268c2ecf20Sopenharmony_ci void __iomem *chan = ADMA_PORT_REGS(ap); 2278c2ecf20Sopenharmony_ci 2288c2ecf20Sopenharmony_ci /* mask/clear ATA interrupts */ 2298c2ecf20Sopenharmony_ci writeb(ATA_NIEN, ap->ioaddr.ctl_addr); 2308c2ecf20Sopenharmony_ci ata_sff_check_status(ap); 2318c2ecf20Sopenharmony_ci 2328c2ecf20Sopenharmony_ci /* reset ADMA to idle state */ 2338c2ecf20Sopenharmony_ci writew(aPIOMD4 | aNIEN | aRSTADM, chan + ADMA_CONTROL); 2348c2ecf20Sopenharmony_ci udelay(2); 2358c2ecf20Sopenharmony_ci writew(aPIOMD4 | aNIEN, chan + ADMA_CONTROL); 2368c2ecf20Sopenharmony_ci udelay(2); 2378c2ecf20Sopenharmony_ci} 2388c2ecf20Sopenharmony_ci 2398c2ecf20Sopenharmony_cistatic void adma_thaw(struct ata_port *ap) 2408c2ecf20Sopenharmony_ci{ 2418c2ecf20Sopenharmony_ci adma_reinit_engine(ap); 2428c2ecf20Sopenharmony_ci} 2438c2ecf20Sopenharmony_ci 2448c2ecf20Sopenharmony_cistatic int adma_prereset(struct ata_link *link, unsigned long deadline) 2458c2ecf20Sopenharmony_ci{ 2468c2ecf20Sopenharmony_ci struct ata_port *ap = link->ap; 2478c2ecf20Sopenharmony_ci struct adma_port_priv *pp = ap->private_data; 2488c2ecf20Sopenharmony_ci 2498c2ecf20Sopenharmony_ci if (pp->state != adma_state_idle) /* healthy paranoia */ 2508c2ecf20Sopenharmony_ci pp->state = adma_state_mmio; 2518c2ecf20Sopenharmony_ci adma_reinit_engine(ap); 2528c2ecf20Sopenharmony_ci 2538c2ecf20Sopenharmony_ci return ata_sff_prereset(link, deadline); 2548c2ecf20Sopenharmony_ci} 2558c2ecf20Sopenharmony_ci 2568c2ecf20Sopenharmony_cistatic int adma_fill_sg(struct ata_queued_cmd *qc) 2578c2ecf20Sopenharmony_ci{ 2588c2ecf20Sopenharmony_ci struct scatterlist *sg; 2598c2ecf20Sopenharmony_ci struct ata_port *ap = qc->ap; 2608c2ecf20Sopenharmony_ci struct adma_port_priv *pp = ap->private_data; 2618c2ecf20Sopenharmony_ci u8 *buf = pp->pkt, *last_buf = NULL; 2628c2ecf20Sopenharmony_ci int i = (2 + buf[3]) * 8; 2638c2ecf20Sopenharmony_ci u8 pFLAGS = pORD | ((qc->tf.flags & ATA_TFLAG_WRITE) ? pDIRO : 0); 2648c2ecf20Sopenharmony_ci unsigned int si; 2658c2ecf20Sopenharmony_ci 2668c2ecf20Sopenharmony_ci for_each_sg(qc->sg, sg, qc->n_elem, si) { 2678c2ecf20Sopenharmony_ci u32 addr; 2688c2ecf20Sopenharmony_ci u32 len; 2698c2ecf20Sopenharmony_ci 2708c2ecf20Sopenharmony_ci addr = (u32)sg_dma_address(sg); 2718c2ecf20Sopenharmony_ci *(__le32 *)(buf + i) = cpu_to_le32(addr); 2728c2ecf20Sopenharmony_ci i += 4; 2738c2ecf20Sopenharmony_ci 2748c2ecf20Sopenharmony_ci len = sg_dma_len(sg) >> 3; 2758c2ecf20Sopenharmony_ci *(__le32 *)(buf + i) = cpu_to_le32(len); 2768c2ecf20Sopenharmony_ci i += 4; 2778c2ecf20Sopenharmony_ci 2788c2ecf20Sopenharmony_ci last_buf = &buf[i]; 2798c2ecf20Sopenharmony_ci buf[i++] = pFLAGS; 2808c2ecf20Sopenharmony_ci buf[i++] = qc->dev->dma_mode & 0xf; 2818c2ecf20Sopenharmony_ci buf[i++] = 0; /* pPKLW */ 2828c2ecf20Sopenharmony_ci buf[i++] = 0; /* reserved */ 2838c2ecf20Sopenharmony_ci 2848c2ecf20Sopenharmony_ci *(__le32 *)(buf + i) = 2858c2ecf20Sopenharmony_ci (pFLAGS & pEND) ? 0 : cpu_to_le32(pp->pkt_dma + i + 4); 2868c2ecf20Sopenharmony_ci i += 4; 2878c2ecf20Sopenharmony_ci 2888c2ecf20Sopenharmony_ci VPRINTK("PRD[%u] = (0x%lX, 0x%X)\n", i/4, 2898c2ecf20Sopenharmony_ci (unsigned long)addr, len); 2908c2ecf20Sopenharmony_ci } 2918c2ecf20Sopenharmony_ci 2928c2ecf20Sopenharmony_ci if (likely(last_buf)) 2938c2ecf20Sopenharmony_ci *last_buf |= pEND; 2948c2ecf20Sopenharmony_ci 2958c2ecf20Sopenharmony_ci return i; 2968c2ecf20Sopenharmony_ci} 2978c2ecf20Sopenharmony_ci 2988c2ecf20Sopenharmony_cistatic enum ata_completion_errors adma_qc_prep(struct ata_queued_cmd *qc) 2998c2ecf20Sopenharmony_ci{ 3008c2ecf20Sopenharmony_ci struct adma_port_priv *pp = qc->ap->private_data; 3018c2ecf20Sopenharmony_ci u8 *buf = pp->pkt; 3028c2ecf20Sopenharmony_ci u32 pkt_dma = (u32)pp->pkt_dma; 3038c2ecf20Sopenharmony_ci int i = 0; 3048c2ecf20Sopenharmony_ci 3058c2ecf20Sopenharmony_ci VPRINTK("ENTER\n"); 3068c2ecf20Sopenharmony_ci 3078c2ecf20Sopenharmony_ci adma_enter_reg_mode(qc->ap); 3088c2ecf20Sopenharmony_ci if (qc->tf.protocol != ATA_PROT_DMA) 3098c2ecf20Sopenharmony_ci return AC_ERR_OK; 3108c2ecf20Sopenharmony_ci 3118c2ecf20Sopenharmony_ci buf[i++] = 0; /* Response flags */ 3128c2ecf20Sopenharmony_ci buf[i++] = 0; /* reserved */ 3138c2ecf20Sopenharmony_ci buf[i++] = cVLD | cDAT | cIEN; 3148c2ecf20Sopenharmony_ci i++; /* cLEN, gets filled in below */ 3158c2ecf20Sopenharmony_ci 3168c2ecf20Sopenharmony_ci *(__le32 *)(buf+i) = cpu_to_le32(pkt_dma); /* cNCPB */ 3178c2ecf20Sopenharmony_ci i += 4; /* cNCPB */ 3188c2ecf20Sopenharmony_ci i += 4; /* cPRD, gets filled in below */ 3198c2ecf20Sopenharmony_ci 3208c2ecf20Sopenharmony_ci buf[i++] = 0; /* reserved */ 3218c2ecf20Sopenharmony_ci buf[i++] = 0; /* reserved */ 3228c2ecf20Sopenharmony_ci buf[i++] = 0; /* reserved */ 3238c2ecf20Sopenharmony_ci buf[i++] = 0; /* reserved */ 3248c2ecf20Sopenharmony_ci 3258c2ecf20Sopenharmony_ci /* ATA registers; must be a multiple of 4 */ 3268c2ecf20Sopenharmony_ci buf[i++] = qc->tf.device; 3278c2ecf20Sopenharmony_ci buf[i++] = ADMA_REGS_DEVICE; 3288c2ecf20Sopenharmony_ci if ((qc->tf.flags & ATA_TFLAG_LBA48)) { 3298c2ecf20Sopenharmony_ci buf[i++] = qc->tf.hob_nsect; 3308c2ecf20Sopenharmony_ci buf[i++] = ADMA_REGS_SECTOR_COUNT; 3318c2ecf20Sopenharmony_ci buf[i++] = qc->tf.hob_lbal; 3328c2ecf20Sopenharmony_ci buf[i++] = ADMA_REGS_LBA_LOW; 3338c2ecf20Sopenharmony_ci buf[i++] = qc->tf.hob_lbam; 3348c2ecf20Sopenharmony_ci buf[i++] = ADMA_REGS_LBA_MID; 3358c2ecf20Sopenharmony_ci buf[i++] = qc->tf.hob_lbah; 3368c2ecf20Sopenharmony_ci buf[i++] = ADMA_REGS_LBA_HIGH; 3378c2ecf20Sopenharmony_ci } 3388c2ecf20Sopenharmony_ci buf[i++] = qc->tf.nsect; 3398c2ecf20Sopenharmony_ci buf[i++] = ADMA_REGS_SECTOR_COUNT; 3408c2ecf20Sopenharmony_ci buf[i++] = qc->tf.lbal; 3418c2ecf20Sopenharmony_ci buf[i++] = ADMA_REGS_LBA_LOW; 3428c2ecf20Sopenharmony_ci buf[i++] = qc->tf.lbam; 3438c2ecf20Sopenharmony_ci buf[i++] = ADMA_REGS_LBA_MID; 3448c2ecf20Sopenharmony_ci buf[i++] = qc->tf.lbah; 3458c2ecf20Sopenharmony_ci buf[i++] = ADMA_REGS_LBA_HIGH; 3468c2ecf20Sopenharmony_ci buf[i++] = 0; 3478c2ecf20Sopenharmony_ci buf[i++] = ADMA_REGS_CONTROL; 3488c2ecf20Sopenharmony_ci buf[i++] = rIGN; 3498c2ecf20Sopenharmony_ci buf[i++] = 0; 3508c2ecf20Sopenharmony_ci buf[i++] = qc->tf.command; 3518c2ecf20Sopenharmony_ci buf[i++] = ADMA_REGS_COMMAND | rEND; 3528c2ecf20Sopenharmony_ci 3538c2ecf20Sopenharmony_ci buf[3] = (i >> 3) - 2; /* cLEN */ 3548c2ecf20Sopenharmony_ci *(__le32 *)(buf+8) = cpu_to_le32(pkt_dma + i); /* cPRD */ 3558c2ecf20Sopenharmony_ci 3568c2ecf20Sopenharmony_ci i = adma_fill_sg(qc); 3578c2ecf20Sopenharmony_ci wmb(); /* flush PRDs and pkt to memory */ 3588c2ecf20Sopenharmony_ci#if 0 3598c2ecf20Sopenharmony_ci /* dump out CPB + PRDs for debug */ 3608c2ecf20Sopenharmony_ci { 3618c2ecf20Sopenharmony_ci int j, len = 0; 3628c2ecf20Sopenharmony_ci static char obuf[2048]; 3638c2ecf20Sopenharmony_ci for (j = 0; j < i; ++j) { 3648c2ecf20Sopenharmony_ci len += sprintf(obuf+len, "%02x ", buf[j]); 3658c2ecf20Sopenharmony_ci if ((j & 7) == 7) { 3668c2ecf20Sopenharmony_ci printk("%s\n", obuf); 3678c2ecf20Sopenharmony_ci len = 0; 3688c2ecf20Sopenharmony_ci } 3698c2ecf20Sopenharmony_ci } 3708c2ecf20Sopenharmony_ci if (len) 3718c2ecf20Sopenharmony_ci printk("%s\n", obuf); 3728c2ecf20Sopenharmony_ci } 3738c2ecf20Sopenharmony_ci#endif 3748c2ecf20Sopenharmony_ci return AC_ERR_OK; 3758c2ecf20Sopenharmony_ci} 3768c2ecf20Sopenharmony_ci 3778c2ecf20Sopenharmony_cistatic inline void adma_packet_start(struct ata_queued_cmd *qc) 3788c2ecf20Sopenharmony_ci{ 3798c2ecf20Sopenharmony_ci struct ata_port *ap = qc->ap; 3808c2ecf20Sopenharmony_ci void __iomem *chan = ADMA_PORT_REGS(ap); 3818c2ecf20Sopenharmony_ci 3828c2ecf20Sopenharmony_ci VPRINTK("ENTER, ap %p\n", ap); 3838c2ecf20Sopenharmony_ci 3848c2ecf20Sopenharmony_ci /* fire up the ADMA engine */ 3858c2ecf20Sopenharmony_ci writew(aPIOMD4 | aGO, chan + ADMA_CONTROL); 3868c2ecf20Sopenharmony_ci} 3878c2ecf20Sopenharmony_ci 3888c2ecf20Sopenharmony_cistatic unsigned int adma_qc_issue(struct ata_queued_cmd *qc) 3898c2ecf20Sopenharmony_ci{ 3908c2ecf20Sopenharmony_ci struct adma_port_priv *pp = qc->ap->private_data; 3918c2ecf20Sopenharmony_ci 3928c2ecf20Sopenharmony_ci switch (qc->tf.protocol) { 3938c2ecf20Sopenharmony_ci case ATA_PROT_DMA: 3948c2ecf20Sopenharmony_ci pp->state = adma_state_pkt; 3958c2ecf20Sopenharmony_ci adma_packet_start(qc); 3968c2ecf20Sopenharmony_ci return 0; 3978c2ecf20Sopenharmony_ci 3988c2ecf20Sopenharmony_ci case ATAPI_PROT_DMA: 3998c2ecf20Sopenharmony_ci BUG(); 4008c2ecf20Sopenharmony_ci break; 4018c2ecf20Sopenharmony_ci 4028c2ecf20Sopenharmony_ci default: 4038c2ecf20Sopenharmony_ci break; 4048c2ecf20Sopenharmony_ci } 4058c2ecf20Sopenharmony_ci 4068c2ecf20Sopenharmony_ci pp->state = adma_state_mmio; 4078c2ecf20Sopenharmony_ci return ata_sff_qc_issue(qc); 4088c2ecf20Sopenharmony_ci} 4098c2ecf20Sopenharmony_ci 4108c2ecf20Sopenharmony_cistatic inline unsigned int adma_intr_pkt(struct ata_host *host) 4118c2ecf20Sopenharmony_ci{ 4128c2ecf20Sopenharmony_ci unsigned int handled = 0, port_no; 4138c2ecf20Sopenharmony_ci 4148c2ecf20Sopenharmony_ci for (port_no = 0; port_no < host->n_ports; ++port_no) { 4158c2ecf20Sopenharmony_ci struct ata_port *ap = host->ports[port_no]; 4168c2ecf20Sopenharmony_ci struct adma_port_priv *pp; 4178c2ecf20Sopenharmony_ci struct ata_queued_cmd *qc; 4188c2ecf20Sopenharmony_ci void __iomem *chan = ADMA_PORT_REGS(ap); 4198c2ecf20Sopenharmony_ci u8 status = readb(chan + ADMA_STATUS); 4208c2ecf20Sopenharmony_ci 4218c2ecf20Sopenharmony_ci if (status == 0) 4228c2ecf20Sopenharmony_ci continue; 4238c2ecf20Sopenharmony_ci handled = 1; 4248c2ecf20Sopenharmony_ci adma_enter_reg_mode(ap); 4258c2ecf20Sopenharmony_ci pp = ap->private_data; 4268c2ecf20Sopenharmony_ci if (!pp || pp->state != adma_state_pkt) 4278c2ecf20Sopenharmony_ci continue; 4288c2ecf20Sopenharmony_ci qc = ata_qc_from_tag(ap, ap->link.active_tag); 4298c2ecf20Sopenharmony_ci if (qc && (!(qc->tf.flags & ATA_TFLAG_POLLING))) { 4308c2ecf20Sopenharmony_ci if (status & aPERR) 4318c2ecf20Sopenharmony_ci qc->err_mask |= AC_ERR_HOST_BUS; 4328c2ecf20Sopenharmony_ci else if ((status & (aPSD | aUIRQ))) 4338c2ecf20Sopenharmony_ci qc->err_mask |= AC_ERR_OTHER; 4348c2ecf20Sopenharmony_ci 4358c2ecf20Sopenharmony_ci if (pp->pkt[0] & cATERR) 4368c2ecf20Sopenharmony_ci qc->err_mask |= AC_ERR_DEV; 4378c2ecf20Sopenharmony_ci else if (pp->pkt[0] != cDONE) 4388c2ecf20Sopenharmony_ci qc->err_mask |= AC_ERR_OTHER; 4398c2ecf20Sopenharmony_ci 4408c2ecf20Sopenharmony_ci if (!qc->err_mask) 4418c2ecf20Sopenharmony_ci ata_qc_complete(qc); 4428c2ecf20Sopenharmony_ci else { 4438c2ecf20Sopenharmony_ci struct ata_eh_info *ehi = &ap->link.eh_info; 4448c2ecf20Sopenharmony_ci ata_ehi_clear_desc(ehi); 4458c2ecf20Sopenharmony_ci ata_ehi_push_desc(ehi, 4468c2ecf20Sopenharmony_ci "ADMA-status 0x%02X", status); 4478c2ecf20Sopenharmony_ci ata_ehi_push_desc(ehi, 4488c2ecf20Sopenharmony_ci "pkt[0] 0x%02X", pp->pkt[0]); 4498c2ecf20Sopenharmony_ci 4508c2ecf20Sopenharmony_ci if (qc->err_mask == AC_ERR_DEV) 4518c2ecf20Sopenharmony_ci ata_port_abort(ap); 4528c2ecf20Sopenharmony_ci else 4538c2ecf20Sopenharmony_ci ata_port_freeze(ap); 4548c2ecf20Sopenharmony_ci } 4558c2ecf20Sopenharmony_ci } 4568c2ecf20Sopenharmony_ci } 4578c2ecf20Sopenharmony_ci return handled; 4588c2ecf20Sopenharmony_ci} 4598c2ecf20Sopenharmony_ci 4608c2ecf20Sopenharmony_cistatic inline unsigned int adma_intr_mmio(struct ata_host *host) 4618c2ecf20Sopenharmony_ci{ 4628c2ecf20Sopenharmony_ci unsigned int handled = 0, port_no; 4638c2ecf20Sopenharmony_ci 4648c2ecf20Sopenharmony_ci for (port_no = 0; port_no < host->n_ports; ++port_no) { 4658c2ecf20Sopenharmony_ci struct ata_port *ap = host->ports[port_no]; 4668c2ecf20Sopenharmony_ci struct adma_port_priv *pp = ap->private_data; 4678c2ecf20Sopenharmony_ci struct ata_queued_cmd *qc; 4688c2ecf20Sopenharmony_ci 4698c2ecf20Sopenharmony_ci if (!pp || pp->state != adma_state_mmio) 4708c2ecf20Sopenharmony_ci continue; 4718c2ecf20Sopenharmony_ci qc = ata_qc_from_tag(ap, ap->link.active_tag); 4728c2ecf20Sopenharmony_ci if (qc && (!(qc->tf.flags & ATA_TFLAG_POLLING))) { 4738c2ecf20Sopenharmony_ci 4748c2ecf20Sopenharmony_ci /* check main status, clearing INTRQ */ 4758c2ecf20Sopenharmony_ci u8 status = ata_sff_check_status(ap); 4768c2ecf20Sopenharmony_ci if ((status & ATA_BUSY)) 4778c2ecf20Sopenharmony_ci continue; 4788c2ecf20Sopenharmony_ci DPRINTK("ata%u: protocol %d (dev_stat 0x%X)\n", 4798c2ecf20Sopenharmony_ci ap->print_id, qc->tf.protocol, status); 4808c2ecf20Sopenharmony_ci 4818c2ecf20Sopenharmony_ci /* complete taskfile transaction */ 4828c2ecf20Sopenharmony_ci pp->state = adma_state_idle; 4838c2ecf20Sopenharmony_ci qc->err_mask |= ac_err_mask(status); 4848c2ecf20Sopenharmony_ci if (!qc->err_mask) 4858c2ecf20Sopenharmony_ci ata_qc_complete(qc); 4868c2ecf20Sopenharmony_ci else { 4878c2ecf20Sopenharmony_ci struct ata_eh_info *ehi = &ap->link.eh_info; 4888c2ecf20Sopenharmony_ci ata_ehi_clear_desc(ehi); 4898c2ecf20Sopenharmony_ci ata_ehi_push_desc(ehi, "status 0x%02X", status); 4908c2ecf20Sopenharmony_ci 4918c2ecf20Sopenharmony_ci if (qc->err_mask == AC_ERR_DEV) 4928c2ecf20Sopenharmony_ci ata_port_abort(ap); 4938c2ecf20Sopenharmony_ci else 4948c2ecf20Sopenharmony_ci ata_port_freeze(ap); 4958c2ecf20Sopenharmony_ci } 4968c2ecf20Sopenharmony_ci handled = 1; 4978c2ecf20Sopenharmony_ci } 4988c2ecf20Sopenharmony_ci } 4998c2ecf20Sopenharmony_ci return handled; 5008c2ecf20Sopenharmony_ci} 5018c2ecf20Sopenharmony_ci 5028c2ecf20Sopenharmony_cistatic irqreturn_t adma_intr(int irq, void *dev_instance) 5038c2ecf20Sopenharmony_ci{ 5048c2ecf20Sopenharmony_ci struct ata_host *host = dev_instance; 5058c2ecf20Sopenharmony_ci unsigned int handled = 0; 5068c2ecf20Sopenharmony_ci 5078c2ecf20Sopenharmony_ci VPRINTK("ENTER\n"); 5088c2ecf20Sopenharmony_ci 5098c2ecf20Sopenharmony_ci spin_lock(&host->lock); 5108c2ecf20Sopenharmony_ci handled = adma_intr_pkt(host) | adma_intr_mmio(host); 5118c2ecf20Sopenharmony_ci spin_unlock(&host->lock); 5128c2ecf20Sopenharmony_ci 5138c2ecf20Sopenharmony_ci VPRINTK("EXIT\n"); 5148c2ecf20Sopenharmony_ci 5158c2ecf20Sopenharmony_ci return IRQ_RETVAL(handled); 5168c2ecf20Sopenharmony_ci} 5178c2ecf20Sopenharmony_ci 5188c2ecf20Sopenharmony_cistatic void adma_ata_setup_port(struct ata_ioports *port, void __iomem *base) 5198c2ecf20Sopenharmony_ci{ 5208c2ecf20Sopenharmony_ci port->cmd_addr = 5218c2ecf20Sopenharmony_ci port->data_addr = base + 0x000; 5228c2ecf20Sopenharmony_ci port->error_addr = 5238c2ecf20Sopenharmony_ci port->feature_addr = base + 0x004; 5248c2ecf20Sopenharmony_ci port->nsect_addr = base + 0x008; 5258c2ecf20Sopenharmony_ci port->lbal_addr = base + 0x00c; 5268c2ecf20Sopenharmony_ci port->lbam_addr = base + 0x010; 5278c2ecf20Sopenharmony_ci port->lbah_addr = base + 0x014; 5288c2ecf20Sopenharmony_ci port->device_addr = base + 0x018; 5298c2ecf20Sopenharmony_ci port->status_addr = 5308c2ecf20Sopenharmony_ci port->command_addr = base + 0x01c; 5318c2ecf20Sopenharmony_ci port->altstatus_addr = 5328c2ecf20Sopenharmony_ci port->ctl_addr = base + 0x038; 5338c2ecf20Sopenharmony_ci} 5348c2ecf20Sopenharmony_ci 5358c2ecf20Sopenharmony_cistatic int adma_port_start(struct ata_port *ap) 5368c2ecf20Sopenharmony_ci{ 5378c2ecf20Sopenharmony_ci struct device *dev = ap->host->dev; 5388c2ecf20Sopenharmony_ci struct adma_port_priv *pp; 5398c2ecf20Sopenharmony_ci 5408c2ecf20Sopenharmony_ci adma_enter_reg_mode(ap); 5418c2ecf20Sopenharmony_ci pp = devm_kzalloc(dev, sizeof(*pp), GFP_KERNEL); 5428c2ecf20Sopenharmony_ci if (!pp) 5438c2ecf20Sopenharmony_ci return -ENOMEM; 5448c2ecf20Sopenharmony_ci pp->pkt = dmam_alloc_coherent(dev, ADMA_PKT_BYTES, &pp->pkt_dma, 5458c2ecf20Sopenharmony_ci GFP_KERNEL); 5468c2ecf20Sopenharmony_ci if (!pp->pkt) 5478c2ecf20Sopenharmony_ci return -ENOMEM; 5488c2ecf20Sopenharmony_ci /* paranoia? */ 5498c2ecf20Sopenharmony_ci if ((pp->pkt_dma & 7) != 0) { 5508c2ecf20Sopenharmony_ci printk(KERN_ERR "bad alignment for pp->pkt_dma: %08x\n", 5518c2ecf20Sopenharmony_ci (u32)pp->pkt_dma); 5528c2ecf20Sopenharmony_ci return -ENOMEM; 5538c2ecf20Sopenharmony_ci } 5548c2ecf20Sopenharmony_ci ap->private_data = pp; 5558c2ecf20Sopenharmony_ci adma_reinit_engine(ap); 5568c2ecf20Sopenharmony_ci return 0; 5578c2ecf20Sopenharmony_ci} 5588c2ecf20Sopenharmony_ci 5598c2ecf20Sopenharmony_cistatic void adma_port_stop(struct ata_port *ap) 5608c2ecf20Sopenharmony_ci{ 5618c2ecf20Sopenharmony_ci adma_reset_engine(ap); 5628c2ecf20Sopenharmony_ci} 5638c2ecf20Sopenharmony_ci 5648c2ecf20Sopenharmony_cistatic void adma_host_init(struct ata_host *host, unsigned int chip_id) 5658c2ecf20Sopenharmony_ci{ 5668c2ecf20Sopenharmony_ci unsigned int port_no; 5678c2ecf20Sopenharmony_ci 5688c2ecf20Sopenharmony_ci /* enable/lock aGO operation */ 5698c2ecf20Sopenharmony_ci writeb(7, host->iomap[ADMA_MMIO_BAR] + ADMA_MODE_LOCK); 5708c2ecf20Sopenharmony_ci 5718c2ecf20Sopenharmony_ci /* reset the ADMA logic */ 5728c2ecf20Sopenharmony_ci for (port_no = 0; port_no < ADMA_PORTS; ++port_no) 5738c2ecf20Sopenharmony_ci adma_reset_engine(host->ports[port_no]); 5748c2ecf20Sopenharmony_ci} 5758c2ecf20Sopenharmony_ci 5768c2ecf20Sopenharmony_cistatic int adma_ata_init_one(struct pci_dev *pdev, 5778c2ecf20Sopenharmony_ci const struct pci_device_id *ent) 5788c2ecf20Sopenharmony_ci{ 5798c2ecf20Sopenharmony_ci unsigned int board_idx = (unsigned int) ent->driver_data; 5808c2ecf20Sopenharmony_ci const struct ata_port_info *ppi[] = { &adma_port_info[board_idx], NULL }; 5818c2ecf20Sopenharmony_ci struct ata_host *host; 5828c2ecf20Sopenharmony_ci void __iomem *mmio_base; 5838c2ecf20Sopenharmony_ci int rc, port_no; 5848c2ecf20Sopenharmony_ci 5858c2ecf20Sopenharmony_ci ata_print_version_once(&pdev->dev, DRV_VERSION); 5868c2ecf20Sopenharmony_ci 5878c2ecf20Sopenharmony_ci /* alloc host */ 5888c2ecf20Sopenharmony_ci host = ata_host_alloc_pinfo(&pdev->dev, ppi, ADMA_PORTS); 5898c2ecf20Sopenharmony_ci if (!host) 5908c2ecf20Sopenharmony_ci return -ENOMEM; 5918c2ecf20Sopenharmony_ci 5928c2ecf20Sopenharmony_ci /* acquire resources and fill host */ 5938c2ecf20Sopenharmony_ci rc = pcim_enable_device(pdev); 5948c2ecf20Sopenharmony_ci if (rc) 5958c2ecf20Sopenharmony_ci return rc; 5968c2ecf20Sopenharmony_ci 5978c2ecf20Sopenharmony_ci if ((pci_resource_flags(pdev, 4) & IORESOURCE_MEM) == 0) 5988c2ecf20Sopenharmony_ci return -ENODEV; 5998c2ecf20Sopenharmony_ci 6008c2ecf20Sopenharmony_ci rc = pcim_iomap_regions(pdev, 1 << ADMA_MMIO_BAR, DRV_NAME); 6018c2ecf20Sopenharmony_ci if (rc) 6028c2ecf20Sopenharmony_ci return rc; 6038c2ecf20Sopenharmony_ci host->iomap = pcim_iomap_table(pdev); 6048c2ecf20Sopenharmony_ci mmio_base = host->iomap[ADMA_MMIO_BAR]; 6058c2ecf20Sopenharmony_ci 6068c2ecf20Sopenharmony_ci rc = dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(32)); 6078c2ecf20Sopenharmony_ci if (rc) { 6088c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "32-bit DMA enable failed\n"); 6098c2ecf20Sopenharmony_ci return rc; 6108c2ecf20Sopenharmony_ci } 6118c2ecf20Sopenharmony_ci 6128c2ecf20Sopenharmony_ci for (port_no = 0; port_no < ADMA_PORTS; ++port_no) { 6138c2ecf20Sopenharmony_ci struct ata_port *ap = host->ports[port_no]; 6148c2ecf20Sopenharmony_ci void __iomem *port_base = ADMA_ATA_REGS(mmio_base, port_no); 6158c2ecf20Sopenharmony_ci unsigned int offset = port_base - mmio_base; 6168c2ecf20Sopenharmony_ci 6178c2ecf20Sopenharmony_ci adma_ata_setup_port(&ap->ioaddr, port_base); 6188c2ecf20Sopenharmony_ci 6198c2ecf20Sopenharmony_ci ata_port_pbar_desc(ap, ADMA_MMIO_BAR, -1, "mmio"); 6208c2ecf20Sopenharmony_ci ata_port_pbar_desc(ap, ADMA_MMIO_BAR, offset, "port"); 6218c2ecf20Sopenharmony_ci } 6228c2ecf20Sopenharmony_ci 6238c2ecf20Sopenharmony_ci /* initialize adapter */ 6248c2ecf20Sopenharmony_ci adma_host_init(host, board_idx); 6258c2ecf20Sopenharmony_ci 6268c2ecf20Sopenharmony_ci pci_set_master(pdev); 6278c2ecf20Sopenharmony_ci return ata_host_activate(host, pdev->irq, adma_intr, IRQF_SHARED, 6288c2ecf20Sopenharmony_ci &adma_ata_sht); 6298c2ecf20Sopenharmony_ci} 6308c2ecf20Sopenharmony_ci 6318c2ecf20Sopenharmony_cimodule_pci_driver(adma_ata_pci_driver); 6328c2ecf20Sopenharmony_ci 6338c2ecf20Sopenharmony_ciMODULE_AUTHOR("Mark Lord"); 6348c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Pacific Digital Corporation ADMA low-level driver"); 6358c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 6368c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(pci, adma_ata_pci_tbl); 6378c2ecf20Sopenharmony_ciMODULE_VERSION(DRV_VERSION); 638