blob: 6ceec59cb2913c2228e2e629ab6435f647fe7e35 [file] [log] [blame]
Thomas Gleixnerc82ee6d2019-05-19 15:51:48 +02001// SPDX-License-Identifier: GPL-2.0-or-later
Linus Torvalds1da177e2005-04-16 15:20:36 -07002/*
3 * sata_sx4.c - Promise SATA
4 *
Tejun Heo8c3d3d42013-05-14 11:09:50 -07005 * Maintained by: Tejun Heo <tj@kernel.org>
Linus Torvalds1da177e2005-04-16 15:20:36 -07006 * Please ALWAYS copy linux-ide@vger.kernel.org
7 * on emails.
8 *
9 * Copyright 2003-2004 Red Hat, Inc.
10 *
Jeff Garzikaf36d7f2005-08-28 20:18:39 -040011 * libata documentation is available via 'make {ps|pdf}docs',
Mauro Carvalho Chehab19285f32017-05-14 11:52:56 -030012 * as Documentation/driver-api/libata.rst
Jeff Garzikaf36d7f2005-08-28 20:18:39 -040013 *
14 * Hardware documentation available under NDA.
Linus Torvalds1da177e2005-04-16 15:20:36 -070015 */
16
Jeff Garzika09060f2007-05-28 08:17:06 -040017/*
18 Theory of operation
19 -------------------
20
21 The SX4 (PDC20621) chip features a single Host DMA (HDMA) copy
22 engine, DIMM memory, and four ATA engines (one per SATA port).
23 Data is copied to/from DIMM memory by the HDMA engine, before
24 handing off to one (or more) of the ATA engines. The ATA
25 engines operate solely on DIMM memory.
26
27 The SX4 behaves like a PATA chip, with no SATA controls or
28 knowledge whatsoever, leading to the presumption that
29 PATA<->SATA bridges exist on SX4 boards, external to the
30 PDC20621 chip itself.
31
32 The chip is quite capable, supporting an XOR engine and linked
33 hardware commands (permits a string to transactions to be
34 submitted and waited-on as a single unit), and an optional
35 microprocessor.
36
37 The limiting factor is largely software. This Linux driver was
38 written to multiplex the single HDMA engine to copy disk
39 transactions into a fixed DIMM memory space, from where an ATA
40 engine takes over. As a result, each WRITE looks like this:
41
42 submit HDMA packet to hardware
43 hardware copies data from system memory to DIMM
44 hardware raises interrupt
45
46 submit ATA packet to hardware
47 hardware executes ATA WRITE command, w/ data in DIMM
48 hardware raises interrupt
Jeff Garzik2dcb4072007-10-19 06:42:56 -040049
Jeff Garzika09060f2007-05-28 08:17:06 -040050 and each READ looks like this:
51
52 submit ATA packet to hardware
53 hardware executes ATA READ command, w/ data in DIMM
54 hardware raises interrupt
Jeff Garzik2dcb4072007-10-19 06:42:56 -040055
Jeff Garzika09060f2007-05-28 08:17:06 -040056 submit HDMA packet to hardware
57 hardware copies data from DIMM to system memory
58 hardware raises interrupt
59
60 This is a very slow, lock-step way of doing things that can
61 certainly be improved by motivated kernel hackers.
62
63 */
64
Linus Torvalds1da177e2005-04-16 15:20:36 -070065#include <linux/kernel.h>
66#include <linux/module.h>
67#include <linux/pci.h>
Tejun Heo5a0e3ad2010-03-24 17:04:11 +090068#include <linux/slab.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070069#include <linux/blkdev.h>
70#include <linux/delay.h>
71#include <linux/interrupt.h>
Jeff Garzika9524a72005-10-30 14:39:11 -050072#include <linux/device.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070073#include <scsi/scsi_host.h>
Jeff Garzik193515d2005-11-07 00:59:37 -050074#include <scsi/scsi_cmnd.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070075#include <linux/libata.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070076#include "sata_promise.h"
77
78#define DRV_NAME "sata_sx4"
Jeff Garzik2a3103c2007-08-31 04:54:06 -040079#define DRV_VERSION "0.12"
Linus Torvalds1da177e2005-04-16 15:20:36 -070080
Hannes Reineckef11c5402021-12-21 08:21:00 +010081static int dimm_test;
82module_param(dimm_test, int, 0644);
83MODULE_PARM_DESC(dimm_test, "Enable DIMM test during startup (1 = enabled)");
Linus Torvalds1da177e2005-04-16 15:20:36 -070084
85enum {
Tejun Heo0d5ff562007-02-01 15:06:36 +090086 PDC_MMIO_BAR = 3,
87 PDC_DIMM_BAR = 4,
88
Linus Torvalds1da177e2005-04-16 15:20:36 -070089 PDC_PRD_TBL = 0x44, /* Direct command DMA table addr */
90
91 PDC_PKT_SUBMIT = 0x40, /* Command packet pointer addr */
92 PDC_HDMA_PKT_SUBMIT = 0x100, /* Host DMA packet pointer addr */
93 PDC_INT_SEQMASK = 0x40, /* Mask of asserted SEQ INTs */
94 PDC_HDMA_CTLSTAT = 0x12C, /* Host DMA control / status */
95
Jeff Garzika09060f2007-05-28 08:17:06 -040096 PDC_CTLSTAT = 0x60, /* IDEn control / status */
97
Linus Torvalds1da177e2005-04-16 15:20:36 -070098 PDC_20621_SEQCTL = 0x400,
99 PDC_20621_SEQMASK = 0x480,
100 PDC_20621_GENERAL_CTL = 0x484,
101 PDC_20621_PAGE_SIZE = (32 * 1024),
102
103 /* chosen, not constant, values; we design our own DIMM mem map */
104 PDC_20621_DIMM_WINDOW = 0x0C, /* page# for 32K DIMM window */
105 PDC_20621_DIMM_BASE = 0x00200000,
106 PDC_20621_DIMM_DATA = (64 * 1024),
107 PDC_DIMM_DATA_STEP = (256 * 1024),
108 PDC_DIMM_WINDOW_STEP = (8 * 1024),
109 PDC_DIMM_HOST_PRD = (6 * 1024),
110 PDC_DIMM_HOST_PKT = (128 * 0),
111 PDC_DIMM_HPKT_PRD = (128 * 1),
112 PDC_DIMM_ATA_PKT = (128 * 2),
113 PDC_DIMM_APKT_PRD = (128 * 3),
114 PDC_DIMM_HEADER_SZ = PDC_DIMM_APKT_PRD + 128,
115 PDC_PAGE_WINDOW = 0x40,
116 PDC_PAGE_DATA = PDC_PAGE_WINDOW +
117 (PDC_20621_DIMM_DATA / PDC_20621_PAGE_SIZE),
118 PDC_PAGE_SET = PDC_DIMM_DATA_STEP / PDC_20621_PAGE_SIZE,
119
120 PDC_CHIP0_OFS = 0xC0000, /* offset of chip #0 */
121
122 PDC_20621_ERR_MASK = (1<<19) | (1<<20) | (1<<21) | (1<<22) |
123 (1<<23),
124
125 board_20621 = 0, /* FastTrak S150 SX4 */
126
Jeff Garzikb2d46b62007-05-27 22:58:54 -0400127 PDC_MASK_INT = (1 << 10), /* HDMA/ATA mask int */
128 PDC_RESET = (1 << 11), /* HDMA/ATA reset */
Jeff Garzika09060f2007-05-28 08:17:06 -0400129 PDC_DMA_ENABLE = (1 << 7), /* DMA start/stop */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700130
131 PDC_MAX_HDMA = 32,
132 PDC_HDMA_Q_MASK = (PDC_MAX_HDMA - 1),
133
Jeff Garzikb2d46b62007-05-27 22:58:54 -0400134 PDC_DIMM0_SPD_DEV_ADDRESS = 0x50,
135 PDC_DIMM1_SPD_DEV_ADDRESS = 0x51,
136 PDC_I2C_CONTROL = 0x48,
137 PDC_I2C_ADDR_DATA = 0x4C,
138 PDC_DIMM0_CONTROL = 0x80,
139 PDC_DIMM1_CONTROL = 0x84,
140 PDC_SDRAM_CONTROL = 0x88,
141 PDC_I2C_WRITE = 0, /* master -> slave */
142 PDC_I2C_READ = (1 << 6), /* master <- slave */
143 PDC_I2C_START = (1 << 7), /* start I2C proto */
144 PDC_I2C_MASK_INT = (1 << 5), /* mask I2C interrupt */
145 PDC_I2C_COMPLETE = (1 << 16), /* I2C normal compl. */
146 PDC_I2C_NO_ACK = (1 << 20), /* slave no-ack addr */
147 PDC_DIMM_SPD_SUBADDRESS_START = 0x00,
148 PDC_DIMM_SPD_SUBADDRESS_END = 0x7F,
149 PDC_DIMM_SPD_ROW_NUM = 3,
150 PDC_DIMM_SPD_COLUMN_NUM = 4,
151 PDC_DIMM_SPD_MODULE_ROW = 5,
152 PDC_DIMM_SPD_TYPE = 11,
153 PDC_DIMM_SPD_FRESH_RATE = 12,
154 PDC_DIMM_SPD_BANK_NUM = 17,
155 PDC_DIMM_SPD_CAS_LATENCY = 18,
156 PDC_DIMM_SPD_ATTRIBUTE = 21,
157 PDC_DIMM_SPD_ROW_PRE_CHARGE = 27,
158 PDC_DIMM_SPD_ROW_ACTIVE_DELAY = 28,
159 PDC_DIMM_SPD_RAS_CAS_DELAY = 29,
160 PDC_DIMM_SPD_ACTIVE_PRECHARGE = 30,
161 PDC_DIMM_SPD_SYSTEM_FREQ = 126,
162 PDC_CTL_STATUS = 0x08,
163 PDC_DIMM_WINDOW_CTLR = 0x0C,
164 PDC_TIME_CONTROL = 0x3C,
165 PDC_TIME_PERIOD = 0x40,
166 PDC_TIME_COUNTER = 0x44,
167 PDC_GENERAL_CTLR = 0x484,
168 PCI_PLL_INIT = 0x8A531824,
169 PCI_X_TCOUNT = 0xEE1E5CFF,
170
171 /* PDC_TIME_CONTROL bits */
172 PDC_TIMER_BUZZER = (1 << 10),
173 PDC_TIMER_MODE_PERIODIC = 0, /* bits 9:8 == 00 */
174 PDC_TIMER_MODE_ONCE = (1 << 8), /* bits 9:8 == 01 */
175 PDC_TIMER_ENABLE = (1 << 7),
176 PDC_TIMER_MASK_INT = (1 << 5),
177 PDC_TIMER_SEQ_MASK = 0x1f, /* SEQ ID for timer */
178 PDC_TIMER_DEFAULT = PDC_TIMER_MODE_ONCE |
179 PDC_TIMER_ENABLE |
180 PDC_TIMER_MASK_INT,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700181};
182
Alexander Beregalovf35b5e72009-04-15 00:00:54 +0400183#define ECC_ERASE_BUF_SZ (128 * 1024)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700184
185struct pdc_port_priv {
186 u8 dimm_buf[(ATA_PRD_SZ * ATA_MAX_PRD) + 512];
187 u8 *pkt;
188 dma_addr_t pkt_dma;
189};
190
191struct pdc_host_priv {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700192 unsigned int doing_hdma;
193 unsigned int hdma_prod;
194 unsigned int hdma_cons;
195 struct {
196 struct ata_queued_cmd *qc;
197 unsigned int seq;
198 unsigned long pkt_ofs;
199 } hdma[32];
200};
201
202
Jeff Garzik5796d1c2007-10-26 00:03:37 -0400203static int pdc_sata_init_one(struct pci_dev *pdev, const struct pci_device_id *ent);
Jeff Garzik67651ee2009-04-08 16:02:18 -0400204static void pdc_error_handler(struct ata_port *ap);
205static void pdc_freeze(struct ata_port *ap);
206static void pdc_thaw(struct ata_port *ap);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700207static int pdc_port_start(struct ata_port *ap);
Jiri Slaby95364f32019-10-31 10:59:45 +0100208static enum ata_completion_errors pdc20621_qc_prep(struct ata_queued_cmd *qc);
Jeff Garzik057ace52005-10-22 14:27:05 -0400209static void pdc_tf_load_mmio(struct ata_port *ap, const struct ata_taskfile *tf);
210static void pdc_exec_command_mmio(struct ata_port *ap, const struct ata_taskfile *tf);
Tejun Heo4447d352007-04-17 23:44:08 +0900211static unsigned int pdc20621_dimm_init(struct ata_host *host);
212static int pdc20621_detect_dimm(struct ata_host *host);
213static unsigned int pdc20621_i2c_read(struct ata_host *host,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700214 u32 device, u32 subaddr, u32 *pdata);
Tejun Heo4447d352007-04-17 23:44:08 +0900215static int pdc20621_prog_dimm0(struct ata_host *host);
216static unsigned int pdc20621_prog_dimm_global(struct ata_host *host);
Tejun Heo4447d352007-04-17 23:44:08 +0900217static void pdc20621_get_from_dimm(struct ata_host *host,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700218 void *psource, u32 offset, u32 size);
Tejun Heo4447d352007-04-17 23:44:08 +0900219static void pdc20621_put_to_dimm(struct ata_host *host,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700220 void *psource, u32 offset, u32 size);
221static void pdc20621_irq_clear(struct ata_port *ap);
Tejun Heo9363c382008-04-07 22:47:16 +0900222static unsigned int pdc20621_qc_issue(struct ata_queued_cmd *qc);
Jeff Garzik67651ee2009-04-08 16:02:18 -0400223static int pdc_softreset(struct ata_link *link, unsigned int *class,
224 unsigned long deadline);
225static void pdc_post_internal_cmd(struct ata_queued_cmd *qc);
226static int pdc_check_atapi_dma(struct ata_queued_cmd *qc);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700227
228
Jeff Garzik193515d2005-11-07 00:59:37 -0500229static struct scsi_host_template pdc_sata_sht = {
Tejun Heo68d1d072008-03-25 12:22:49 +0900230 ATA_BASE_SHT(DRV_NAME),
Linus Torvalds1da177e2005-04-16 15:20:36 -0700231 .sg_tablesize = LIBATA_MAX_PRD,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700232 .dma_boundary = ATA_DMA_BOUNDARY,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700233};
234
Tejun Heo029cfd62008-03-25 12:22:49 +0900235/* TODO: inherit from base port_ops after converting to new EH */
236static struct ata_port_operations pdc_20621_ops = {
Jeff Garzik67651ee2009-04-08 16:02:18 -0400237 .inherits = &ata_sff_port_ops,
238
239 .check_atapi_dma = pdc_check_atapi_dma,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700240 .qc_prep = pdc20621_qc_prep,
Tejun Heo9363c382008-04-07 22:47:16 +0900241 .qc_issue = pdc20621_qc_issue,
Jeff Garzik67651ee2009-04-08 16:02:18 -0400242
243 .freeze = pdc_freeze,
244 .thaw = pdc_thaw,
245 .softreset = pdc_softreset,
246 .error_handler = pdc_error_handler,
247 .lost_interrupt = ATA_OP_NULL,
248 .post_internal_cmd = pdc_post_internal_cmd,
249
Linus Torvalds1da177e2005-04-16 15:20:36 -0700250 .port_start = pdc_port_start,
Jeff Garzik67651ee2009-04-08 16:02:18 -0400251
252 .sff_tf_load = pdc_tf_load_mmio,
253 .sff_exec_command = pdc_exec_command_mmio,
254 .sff_irq_clear = pdc20621_irq_clear,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700255};
256
Arjan van de Ven98ac62d2005-11-28 10:06:23 +0100257static const struct ata_port_info pdc_port_info[] = {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700258 /* board_20621 */
259 {
Sergei Shtylyov9cbe0562011-02-04 22:05:48 +0300260 .flags = ATA_FLAG_SATA | ATA_FLAG_NO_ATAPI |
261 ATA_FLAG_PIO_POLLING,
Erik Inge Bolsø14bdef92009-03-14 21:38:24 +0100262 .pio_mask = ATA_PIO4,
263 .mwdma_mask = ATA_MWDMA2,
Jeff Garzik469248a2007-07-08 01:13:16 -0400264 .udma_mask = ATA_UDMA6,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700265 .port_ops = &pdc_20621_ops,
266 },
267
268};
269
Jeff Garzik3b7d6972005-11-10 11:04:11 -0500270static const struct pci_device_id pdc_sata_pci_tbl[] = {
Jeff Garzik54bb3a942006-09-27 22:20:11 -0400271 { PCI_VDEVICE(PROMISE, 0x6622), board_20621 },
272
Linus Torvalds1da177e2005-04-16 15:20:36 -0700273 { } /* terminate list */
274};
275
Linus Torvalds1da177e2005-04-16 15:20:36 -0700276static struct pci_driver pdc_sata_pci_driver = {
277 .name = DRV_NAME,
278 .id_table = pdc_sata_pci_tbl,
279 .probe = pdc_sata_init_one,
280 .remove = ata_pci_remove_one,
281};
282
283
Linus Torvalds1da177e2005-04-16 15:20:36 -0700284static int pdc_port_start(struct ata_port *ap)
285{
Jeff Garzikcca39742006-08-24 03:19:22 -0400286 struct device *dev = ap->host->dev;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700287 struct pdc_port_priv *pp;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700288
Tejun Heo24dc5f32007-01-20 16:00:28 +0900289 pp = devm_kzalloc(dev, sizeof(*pp), GFP_KERNEL);
290 if (!pp)
291 return -ENOMEM;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700292
Tejun Heo24dc5f32007-01-20 16:00:28 +0900293 pp->pkt = dmam_alloc_coherent(dev, 128, &pp->pkt_dma, GFP_KERNEL);
294 if (!pp->pkt)
295 return -ENOMEM;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700296
297 ap->private_data = pp;
298
299 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700300}
301
Sergei Shtylyov7c26dea2012-10-25 21:19:12 +0400302static inline void pdc20621_ata_sg(u8 *buf, unsigned int portno,
303 unsigned int total_len)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700304{
305 u32 addr;
306 unsigned int dw = PDC_DIMM_APKT_PRD >> 2;
Al Viro4ca4e432007-12-30 09:32:22 +0000307 __le32 *buf32 = (__le32 *) buf;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700308
309 /* output ATA packet S/G table */
310 addr = PDC_20621_DIMM_BASE + PDC_20621_DIMM_DATA +
311 (PDC_DIMM_DATA_STEP * portno);
Hannes Reineckebc21c102021-12-21 08:20:59 +0100312
Linus Torvalds1da177e2005-04-16 15:20:36 -0700313 buf32[dw] = cpu_to_le32(addr);
314 buf32[dw + 1] = cpu_to_le32(total_len | ATA_PRD_EOT);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700315}
316
Sergei Shtylyov7c26dea2012-10-25 21:19:12 +0400317static inline void pdc20621_host_sg(u8 *buf, unsigned int portno,
318 unsigned int total_len)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700319{
320 u32 addr;
321 unsigned int dw = PDC_DIMM_HPKT_PRD >> 2;
Al Viro4ca4e432007-12-30 09:32:22 +0000322 __le32 *buf32 = (__le32 *) buf;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700323
324 /* output Host DMA packet S/G table */
325 addr = PDC_20621_DIMM_BASE + PDC_20621_DIMM_DATA +
326 (PDC_DIMM_DATA_STEP * portno);
327
328 buf32[dw] = cpu_to_le32(addr);
329 buf32[dw + 1] = cpu_to_le32(total_len | ATA_PRD_EOT);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700330}
331
332static inline unsigned int pdc20621_ata_pkt(struct ata_taskfile *tf,
333 unsigned int devno, u8 *buf,
334 unsigned int portno)
335{
336 unsigned int i, dw;
Al Viro4ca4e432007-12-30 09:32:22 +0000337 __le32 *buf32 = (__le32 *) buf;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700338 u8 dev_reg;
339
340 unsigned int dimm_sg = PDC_20621_DIMM_BASE +
341 (PDC_DIMM_WINDOW_STEP * portno) +
342 PDC_DIMM_APKT_PRD;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700343
344 i = PDC_DIMM_ATA_PKT;
345
346 /*
347 * Set up ATA packet
348 */
349 if ((tf->protocol == ATA_PROT_DMA) && (!(tf->flags & ATA_TFLAG_WRITE)))
350 buf[i++] = PDC_PKT_READ;
351 else if (tf->protocol == ATA_PROT_NODATA)
352 buf[i++] = PDC_PKT_NODATA;
353 else
354 buf[i++] = 0;
355 buf[i++] = 0; /* reserved */
356 buf[i++] = portno + 1; /* seq. id */
357 buf[i++] = 0xff; /* delay seq. id */
358
359 /* dimm dma S/G, and next-pkt */
360 dw = i >> 2;
361 if (tf->protocol == ATA_PROT_NODATA)
362 buf32[dw] = 0;
363 else
364 buf32[dw] = cpu_to_le32(dimm_sg);
365 buf32[dw + 1] = 0;
366 i += 8;
367
368 if (devno == 0)
369 dev_reg = ATA_DEVICE_OBS;
370 else
371 dev_reg = ATA_DEVICE_OBS | ATA_DEV1;
372
373 /* select device */
374 buf[i++] = (1 << 5) | PDC_PKT_CLEAR_BSY | ATA_REG_DEVICE;
375 buf[i++] = dev_reg;
376
377 /* device control register */
378 buf[i++] = (1 << 5) | PDC_REG_DEVCTL;
379 buf[i++] = tf->ctl;
380
381 return i;
382}
383
384static inline void pdc20621_host_pkt(struct ata_taskfile *tf, u8 *buf,
385 unsigned int portno)
386{
387 unsigned int dw;
Al Viro4ca4e432007-12-30 09:32:22 +0000388 u32 tmp;
389 __le32 *buf32 = (__le32 *) buf;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700390
391 unsigned int host_sg = PDC_20621_DIMM_BASE +
392 (PDC_DIMM_WINDOW_STEP * portno) +
393 PDC_DIMM_HOST_PRD;
394 unsigned int dimm_sg = PDC_20621_DIMM_BASE +
395 (PDC_DIMM_WINDOW_STEP * portno) +
396 PDC_DIMM_HPKT_PRD;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700397
398 dw = PDC_DIMM_HOST_PKT >> 2;
399
400 /*
401 * Set up Host DMA packet
402 */
403 if ((tf->protocol == ATA_PROT_DMA) && (!(tf->flags & ATA_TFLAG_WRITE)))
404 tmp = PDC_PKT_READ;
405 else
406 tmp = 0;
407 tmp |= ((portno + 1 + 4) << 16); /* seq. id */
408 tmp |= (0xff << 24); /* delay seq. id */
409 buf32[dw + 0] = cpu_to_le32(tmp);
410 buf32[dw + 1] = cpu_to_le32(host_sg);
411 buf32[dw + 2] = cpu_to_le32(dimm_sg);
412 buf32[dw + 3] = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700413}
414
415static void pdc20621_dma_prep(struct ata_queued_cmd *qc)
416{
Jeff Garzikcedc9a42005-10-05 07:13:30 -0400417 struct scatterlist *sg;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700418 struct ata_port *ap = qc->ap;
419 struct pdc_port_priv *pp = ap->private_data;
Tejun Heo0d5ff562007-02-01 15:06:36 +0900420 void __iomem *mmio = ap->host->iomap[PDC_MMIO_BAR];
421 void __iomem *dimm_mmio = ap->host->iomap[PDC_DIMM_BAR];
Linus Torvalds1da177e2005-04-16 15:20:36 -0700422 unsigned int portno = ap->port_no;
Tejun Heoff2aeb12007-12-05 16:43:11 +0900423 unsigned int i, si, idx, total_len = 0, sgt_len;
Al Viro826cd152008-03-25 05:18:11 +0000424 __le32 *buf = (__le32 *) &pp->dimm_buf[PDC_DIMM_HEADER_SZ];
Linus Torvalds1da177e2005-04-16 15:20:36 -0700425
Tejun Heobeec7db2006-02-11 19:11:13 +0900426 WARN_ON(!(qc->flags & ATA_QCFLAG_DMAMAP));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700427
Linus Torvalds1da177e2005-04-16 15:20:36 -0700428 /* hard-code chip #0 */
429 mmio += PDC_CHIP0_OFS;
430
431 /*
432 * Build S/G table
433 */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700434 idx = 0;
Tejun Heoff2aeb12007-12-05 16:43:11 +0900435 for_each_sg(qc->sg, sg, qc->n_elem, si) {
Jeff Garzikcedc9a42005-10-05 07:13:30 -0400436 buf[idx++] = cpu_to_le32(sg_dma_address(sg));
437 buf[idx++] = cpu_to_le32(sg_dma_len(sg));
438 total_len += sg_dma_len(sg);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700439 }
440 buf[idx - 1] |= cpu_to_le32(ATA_PRD_EOT);
441 sgt_len = idx * 4;
442
443 /*
444 * Build ATA, host DMA packets
445 */
Sergei Shtylyov7c26dea2012-10-25 21:19:12 +0400446 pdc20621_host_sg(&pp->dimm_buf[0], portno, total_len);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700447 pdc20621_host_pkt(&qc->tf, &pp->dimm_buf[0], portno);
448
Sergei Shtylyov7c26dea2012-10-25 21:19:12 +0400449 pdc20621_ata_sg(&pp->dimm_buf[0], portno, total_len);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700450 i = pdc20621_ata_pkt(&qc->tf, qc->dev->devno, &pp->dimm_buf[0], portno);
451
452 if (qc->tf.flags & ATA_TFLAG_LBA48)
453 i = pdc_prep_lba48(&qc->tf, &pp->dimm_buf[0], i);
454 else
455 i = pdc_prep_lba28(&qc->tf, &pp->dimm_buf[0], i);
456
457 pdc_pkt_footer(&qc->tf, &pp->dimm_buf[0], i);
458
459 /* copy three S/G tables and two packets to DIMM MMIO window */
460 memcpy_toio(dimm_mmio + (portno * PDC_DIMM_WINDOW_STEP),
461 &pp->dimm_buf, PDC_DIMM_HEADER_SZ);
462 memcpy_toio(dimm_mmio + (portno * PDC_DIMM_WINDOW_STEP) +
463 PDC_DIMM_HOST_PRD,
464 &pp->dimm_buf[PDC_DIMM_HEADER_SZ], sgt_len);
465
466 /* force host FIFO dump */
467 writel(0x00000001, mmio + PDC_20621_GENERAL_CTL);
468
469 readl(dimm_mmio); /* MMIO PCI posting flush */
470
Hannes Reineckebc21c102021-12-21 08:20:59 +0100471 ata_port_dbg(ap, "ata pkt buf ofs %u, prd size %u, mmio copied\n",
472 i, sgt_len);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700473}
474
475static void pdc20621_nodata_prep(struct ata_queued_cmd *qc)
476{
477 struct ata_port *ap = qc->ap;
478 struct pdc_port_priv *pp = ap->private_data;
Tejun Heo0d5ff562007-02-01 15:06:36 +0900479 void __iomem *mmio = ap->host->iomap[PDC_MMIO_BAR];
480 void __iomem *dimm_mmio = ap->host->iomap[PDC_DIMM_BAR];
Linus Torvalds1da177e2005-04-16 15:20:36 -0700481 unsigned int portno = ap->port_no;
482 unsigned int i;
483
Linus Torvalds1da177e2005-04-16 15:20:36 -0700484 /* hard-code chip #0 */
485 mmio += PDC_CHIP0_OFS;
486
487 i = pdc20621_ata_pkt(&qc->tf, qc->dev->devno, &pp->dimm_buf[0], portno);
488
489 if (qc->tf.flags & ATA_TFLAG_LBA48)
490 i = pdc_prep_lba48(&qc->tf, &pp->dimm_buf[0], i);
491 else
492 i = pdc_prep_lba28(&qc->tf, &pp->dimm_buf[0], i);
493
494 pdc_pkt_footer(&qc->tf, &pp->dimm_buf[0], i);
495
496 /* copy three S/G tables and two packets to DIMM MMIO window */
497 memcpy_toio(dimm_mmio + (portno * PDC_DIMM_WINDOW_STEP),
498 &pp->dimm_buf, PDC_DIMM_HEADER_SZ);
499
500 /* force host FIFO dump */
501 writel(0x00000001, mmio + PDC_20621_GENERAL_CTL);
502
503 readl(dimm_mmio); /* MMIO PCI posting flush */
504
Hannes Reineckebc21c102021-12-21 08:20:59 +0100505 ata_port_dbg(ap, "ata pkt buf ofs %u, mmio copied\n", i);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700506}
507
Jiri Slaby95364f32019-10-31 10:59:45 +0100508static enum ata_completion_errors pdc20621_qc_prep(struct ata_queued_cmd *qc)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700509{
510 switch (qc->tf.protocol) {
511 case ATA_PROT_DMA:
512 pdc20621_dma_prep(qc);
513 break;
514 case ATA_PROT_NODATA:
515 pdc20621_nodata_prep(qc);
516 break;
517 default:
518 break;
519 }
Jiri Slaby95364f32019-10-31 10:59:45 +0100520
521 return AC_ERR_OK;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700522}
523
524static void __pdc20621_push_hdma(struct ata_queued_cmd *qc,
525 unsigned int seq,
526 u32 pkt_ofs)
527{
528 struct ata_port *ap = qc->ap;
Jeff Garzikcca39742006-08-24 03:19:22 -0400529 struct ata_host *host = ap->host;
Tejun Heo0d5ff562007-02-01 15:06:36 +0900530 void __iomem *mmio = host->iomap[PDC_MMIO_BAR];
Linus Torvalds1da177e2005-04-16 15:20:36 -0700531
532 /* hard-code chip #0 */
533 mmio += PDC_CHIP0_OFS;
534
535 writel(0x00000001, mmio + PDC_20621_SEQCTL + (seq * 4));
536 readl(mmio + PDC_20621_SEQCTL + (seq * 4)); /* flush */
537
538 writel(pkt_ofs, mmio + PDC_HDMA_PKT_SUBMIT);
539 readl(mmio + PDC_HDMA_PKT_SUBMIT); /* flush */
540}
541
542static void pdc20621_push_hdma(struct ata_queued_cmd *qc,
543 unsigned int seq,
544 u32 pkt_ofs)
545{
546 struct ata_port *ap = qc->ap;
Jeff Garzikcca39742006-08-24 03:19:22 -0400547 struct pdc_host_priv *pp = ap->host->private_data;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700548 unsigned int idx = pp->hdma_prod & PDC_HDMA_Q_MASK;
549
550 if (!pp->doing_hdma) {
551 __pdc20621_push_hdma(qc, seq, pkt_ofs);
552 pp->doing_hdma = 1;
553 return;
554 }
555
556 pp->hdma[idx].qc = qc;
557 pp->hdma[idx].seq = seq;
558 pp->hdma[idx].pkt_ofs = pkt_ofs;
559 pp->hdma_prod++;
560}
561
562static void pdc20621_pop_hdma(struct ata_queued_cmd *qc)
563{
564 struct ata_port *ap = qc->ap;
Jeff Garzikcca39742006-08-24 03:19:22 -0400565 struct pdc_host_priv *pp = ap->host->private_data;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700566 unsigned int idx = pp->hdma_cons & PDC_HDMA_Q_MASK;
567
568 /* if nothing on queue, we're done */
569 if (pp->hdma_prod == pp->hdma_cons) {
570 pp->doing_hdma = 0;
571 return;
572 }
573
574 __pdc20621_push_hdma(pp->hdma[idx].qc, pp->hdma[idx].seq,
575 pp->hdma[idx].pkt_ofs);
576 pp->hdma_cons++;
577}
578
Linus Torvalds1da177e2005-04-16 15:20:36 -0700579static void pdc20621_dump_hdma(struct ata_queued_cmd *qc)
580{
581 struct ata_port *ap = qc->ap;
582 unsigned int port_no = ap->port_no;
Tejun Heo0d5ff562007-02-01 15:06:36 +0900583 void __iomem *dimm_mmio = ap->host->iomap[PDC_DIMM_BAR];
Linus Torvalds1da177e2005-04-16 15:20:36 -0700584
585 dimm_mmio += (port_no * PDC_DIMM_WINDOW_STEP);
586 dimm_mmio += PDC_DIMM_HOST_PKT;
587
Hannes Reineckef11c5402021-12-21 08:21:00 +0100588 ata_port_dbg(ap, "HDMA 0x%08X 0x%08X 0x%08X 0x%08X\n",
589 readl(dimm_mmio), readl(dimm_mmio + 4),
590 readl(dimm_mmio + 8), readl(dimm_mmio + 12));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700591}
Linus Torvalds1da177e2005-04-16 15:20:36 -0700592
593static void pdc20621_packet_start(struct ata_queued_cmd *qc)
594{
595 struct ata_port *ap = qc->ap;
Jeff Garzikcca39742006-08-24 03:19:22 -0400596 struct ata_host *host = ap->host;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700597 unsigned int port_no = ap->port_no;
Tejun Heo0d5ff562007-02-01 15:06:36 +0900598 void __iomem *mmio = host->iomap[PDC_MMIO_BAR];
Linus Torvalds1da177e2005-04-16 15:20:36 -0700599 unsigned int rw = (qc->tf.flags & ATA_TFLAG_WRITE);
600 u8 seq = (u8) (port_no + 1);
601 unsigned int port_ofs;
602
603 /* hard-code chip #0 */
604 mmio += PDC_CHIP0_OFS;
605
Linus Torvalds1da177e2005-04-16 15:20:36 -0700606 wmb(); /* flush PRD, pkt writes */
607
608 port_ofs = PDC_20621_DIMM_BASE + (PDC_DIMM_WINDOW_STEP * port_no);
609
610 /* if writing, we (1) DMA to DIMM, then (2) do ATA command */
611 if (rw && qc->tf.protocol == ATA_PROT_DMA) {
612 seq += 4;
613
614 pdc20621_dump_hdma(qc);
615 pdc20621_push_hdma(qc, seq, port_ofs + PDC_DIMM_HOST_PKT);
Hannes Reineckebc21c102021-12-21 08:20:59 +0100616 ata_port_dbg(ap, "queued ofs 0x%x (%u), seq %u\n",
Linus Torvalds1da177e2005-04-16 15:20:36 -0700617 port_ofs + PDC_DIMM_HOST_PKT,
618 port_ofs + PDC_DIMM_HOST_PKT,
619 seq);
620 } else {
621 writel(0x00000001, mmio + PDC_20621_SEQCTL + (seq * 4));
622 readl(mmio + PDC_20621_SEQCTL + (seq * 4)); /* flush */
623
624 writel(port_ofs + PDC_DIMM_ATA_PKT,
Tejun Heo0d5ff562007-02-01 15:06:36 +0900625 ap->ioaddr.cmd_addr + PDC_PKT_SUBMIT);
626 readl(ap->ioaddr.cmd_addr + PDC_PKT_SUBMIT);
Hannes Reineckebc21c102021-12-21 08:20:59 +0100627 ata_port_dbg(ap, "submitted ofs 0x%x (%u), seq %u\n",
Linus Torvalds1da177e2005-04-16 15:20:36 -0700628 port_ofs + PDC_DIMM_ATA_PKT,
629 port_ofs + PDC_DIMM_ATA_PKT,
630 seq);
631 }
632}
633
Tejun Heo9363c382008-04-07 22:47:16 +0900634static unsigned int pdc20621_qc_issue(struct ata_queued_cmd *qc)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700635{
636 switch (qc->tf.protocol) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700637 case ATA_PROT_NODATA:
David Milburn19799bf2009-05-13 18:02:21 -0500638 if (qc->tf.flags & ATA_TFLAG_POLLING)
639 break;
Gustavo A. R. Silvadf561f662020-08-23 17:36:59 -0500640 fallthrough;
David Milburn19799bf2009-05-13 18:02:21 -0500641 case ATA_PROT_DMA:
Linus Torvalds1da177e2005-04-16 15:20:36 -0700642 pdc20621_packet_start(qc);
643 return 0;
644
Tejun Heo0dc36882007-12-18 16:34:43 -0500645 case ATAPI_PROT_DMA:
Linus Torvalds1da177e2005-04-16 15:20:36 -0700646 BUG();
647 break;
648
649 default:
650 break;
651 }
652
Tejun Heo9363c382008-04-07 22:47:16 +0900653 return ata_sff_qc_issue(qc);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700654}
655
Jeff Garzik5796d1c2007-10-26 00:03:37 -0400656static inline unsigned int pdc20621_host_intr(struct ata_port *ap,
657 struct ata_queued_cmd *qc,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700658 unsigned int doing_hdma,
Jeff Garzikea6ba102005-08-30 05:18:18 -0400659 void __iomem *mmio)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700660{
661 unsigned int port_no = ap->port_no;
662 unsigned int port_ofs =
663 PDC_20621_DIMM_BASE + (PDC_DIMM_WINDOW_STEP * port_no);
664 u8 status;
665 unsigned int handled = 0;
666
Linus Torvalds1da177e2005-04-16 15:20:36 -0700667 if ((qc->tf.protocol == ATA_PROT_DMA) && /* read */
668 (!(qc->tf.flags & ATA_TFLAG_WRITE))) {
669
670 /* step two - DMA from DIMM to host */
671 if (doing_hdma) {
Hannes Reineckebc21c102021-12-21 08:20:59 +0100672 ata_port_dbg(ap, "read hdma, 0x%x 0x%x\n",
Linus Torvalds1da177e2005-04-16 15:20:36 -0700673 readl(mmio + 0x104), readl(mmio + PDC_HDMA_CTLSTAT));
674 /* get drive status; clear intr; complete txn */
Albert Leea22e2eb2005-12-05 15:38:02 +0800675 qc->err_mask |= ac_err_mask(ata_wait_idle(ap));
676 ata_qc_complete(qc);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700677 pdc20621_pop_hdma(qc);
678 }
679
680 /* step one - exec ATA command */
681 else {
682 u8 seq = (u8) (port_no + 1 + 4);
Hannes Reineckebc21c102021-12-21 08:20:59 +0100683 ata_port_dbg(ap, "read ata, 0x%x 0x%x\n",
Linus Torvalds1da177e2005-04-16 15:20:36 -0700684 readl(mmio + 0x104), readl(mmio + PDC_HDMA_CTLSTAT));
685
686 /* submit hdma pkt */
687 pdc20621_dump_hdma(qc);
688 pdc20621_push_hdma(qc, seq,
689 port_ofs + PDC_DIMM_HOST_PKT);
690 }
691 handled = 1;
692
693 } else if (qc->tf.protocol == ATA_PROT_DMA) { /* write */
694
695 /* step one - DMA from host to DIMM */
696 if (doing_hdma) {
697 u8 seq = (u8) (port_no + 1);
Hannes Reineckebc21c102021-12-21 08:20:59 +0100698 ata_port_dbg(ap, "write hdma, 0x%x 0x%x\n",
Linus Torvalds1da177e2005-04-16 15:20:36 -0700699 readl(mmio + 0x104), readl(mmio + PDC_HDMA_CTLSTAT));
700
701 /* submit ata pkt */
702 writel(0x00000001, mmio + PDC_20621_SEQCTL + (seq * 4));
703 readl(mmio + PDC_20621_SEQCTL + (seq * 4));
704 writel(port_ofs + PDC_DIMM_ATA_PKT,
Tejun Heo0d5ff562007-02-01 15:06:36 +0900705 ap->ioaddr.cmd_addr + PDC_PKT_SUBMIT);
706 readl(ap->ioaddr.cmd_addr + PDC_PKT_SUBMIT);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700707 }
708
709 /* step two - execute ATA command */
710 else {
Hannes Reineckebc21c102021-12-21 08:20:59 +0100711 ata_port_dbg(ap, "write ata, 0x%x 0x%x\n",
Linus Torvalds1da177e2005-04-16 15:20:36 -0700712 readl(mmio + 0x104), readl(mmio + PDC_HDMA_CTLSTAT));
713 /* get drive status; clear intr; complete txn */
Albert Leea22e2eb2005-12-05 15:38:02 +0800714 qc->err_mask |= ac_err_mask(ata_wait_idle(ap));
715 ata_qc_complete(qc);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700716 pdc20621_pop_hdma(qc);
717 }
718 handled = 1;
719
720 /* command completion, but no data xfer */
721 } else if (qc->tf.protocol == ATA_PROT_NODATA) {
722
Tejun Heo9363c382008-04-07 22:47:16 +0900723 status = ata_sff_busy_wait(ap, ATA_BUSY | ATA_DRQ, 1000);
Hannes Reineckebc21c102021-12-21 08:20:59 +0100724 ata_port_dbg(ap, "BUS_NODATA (drv_stat 0x%X)\n", status);
Albert Leea22e2eb2005-12-05 15:38:02 +0800725 qc->err_mask |= ac_err_mask(status);
726 ata_qc_complete(qc);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700727 handled = 1;
728
729 } else {
730 ap->stats.idle_irq++;
731 }
732
733 return handled;
734}
735
736static void pdc20621_irq_clear(struct ata_port *ap)
737{
David Milburn19799bf2009-05-13 18:02:21 -0500738 ioread8(ap->ioaddr.status_addr);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700739}
740
Jeff Garzik5796d1c2007-10-26 00:03:37 -0400741static irqreturn_t pdc20621_interrupt(int irq, void *dev_instance)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700742{
Jeff Garzikcca39742006-08-24 03:19:22 -0400743 struct ata_host *host = dev_instance;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700744 struct ata_port *ap;
745 u32 mask = 0;
746 unsigned int i, tmp, port_no;
747 unsigned int handled = 0;
Jeff Garzikea6ba102005-08-30 05:18:18 -0400748 void __iomem *mmio_base;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700749
Hannes Reineckebc21c102021-12-21 08:20:59 +0100750 if (!host || !host->iomap[PDC_MMIO_BAR])
Linus Torvalds1da177e2005-04-16 15:20:36 -0700751 return IRQ_NONE;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700752
Tejun Heo0d5ff562007-02-01 15:06:36 +0900753 mmio_base = host->iomap[PDC_MMIO_BAR];
Linus Torvalds1da177e2005-04-16 15:20:36 -0700754
755 /* reading should also clear interrupts */
756 mmio_base += PDC_CHIP0_OFS;
757 mask = readl(mmio_base + PDC_20621_SEQMASK);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700758
Hannes Reineckebc21c102021-12-21 08:20:59 +0100759 if (mask == 0xffffffff)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700760 return IRQ_NONE;
Hannes Reineckebc21c102021-12-21 08:20:59 +0100761
Linus Torvalds1da177e2005-04-16 15:20:36 -0700762 mask &= 0xffff; /* only 16 tags possible */
Hannes Reineckebc21c102021-12-21 08:20:59 +0100763 if (!mask)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700764 return IRQ_NONE;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700765
Jeff Garzik5796d1c2007-10-26 00:03:37 -0400766 spin_lock(&host->lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700767
Jeff Garzik5796d1c2007-10-26 00:03:37 -0400768 for (i = 1; i < 9; i++) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700769 port_no = i - 1;
770 if (port_no > 3)
771 port_no -= 4;
Jeff Garzikcca39742006-08-24 03:19:22 -0400772 if (port_no >= host->n_ports)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700773 ap = NULL;
774 else
Jeff Garzikcca39742006-08-24 03:19:22 -0400775 ap = host->ports[port_no];
Linus Torvalds1da177e2005-04-16 15:20:36 -0700776 tmp = mask & (1 << i);
Hannes Reineckebc21c102021-12-21 08:20:59 +0100777 if (ap)
778 ata_port_dbg(ap, "seq %u, tmp %x\n", i, tmp);
Tejun Heo3e4ec342010-05-10 21:41:30 +0200779 if (tmp && ap) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700780 struct ata_queued_cmd *qc;
781
Tejun Heo9af5c9c2007-08-06 18:36:22 +0900782 qc = ata_qc_from_tag(ap, ap->link.active_tag);
Albert Leee50362e2005-09-27 17:39:50 +0800783 if (qc && (!(qc->tf.flags & ATA_TFLAG_POLLING)))
Linus Torvalds1da177e2005-04-16 15:20:36 -0700784 handled += pdc20621_host_intr(ap, qc, (i > 4),
785 mmio_base);
786 }
787 }
788
Jeff Garzik5796d1c2007-10-26 00:03:37 -0400789 spin_unlock(&host->lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700790
Linus Torvalds1da177e2005-04-16 15:20:36 -0700791 return IRQ_RETVAL(handled);
792}
793
Jeff Garzik67651ee2009-04-08 16:02:18 -0400794static void pdc_freeze(struct ata_port *ap)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700795{
Jeff Garzik67651ee2009-04-08 16:02:18 -0400796 void __iomem *mmio = ap->ioaddr.cmd_addr;
797 u32 tmp;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700798
Jeff Garzik67651ee2009-04-08 16:02:18 -0400799 /* FIXME: if all 4 ATA engines are stopped, also stop HDMA engine */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700800
Jeff Garzik67651ee2009-04-08 16:02:18 -0400801 tmp = readl(mmio + PDC_CTLSTAT);
802 tmp |= PDC_MASK_INT;
803 tmp &= ~PDC_DMA_ENABLE;
804 writel(tmp, mmio + PDC_CTLSTAT);
805 readl(mmio + PDC_CTLSTAT); /* flush */
806}
Jeff Garzikb8f61532005-08-25 22:01:20 -0400807
Jeff Garzik67651ee2009-04-08 16:02:18 -0400808static void pdc_thaw(struct ata_port *ap)
809{
810 void __iomem *mmio = ap->ioaddr.cmd_addr;
Jeff Garzik67651ee2009-04-08 16:02:18 -0400811 u32 tmp;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700812
Jeff Garzik67651ee2009-04-08 16:02:18 -0400813 /* FIXME: start HDMA engine, if zero ATA engines running */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700814
David Milburn19799bf2009-05-13 18:02:21 -0500815 /* clear IRQ */
816 ioread8(ap->ioaddr.status_addr);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700817
Jeff Garzik67651ee2009-04-08 16:02:18 -0400818 /* turn IRQ back on */
819 tmp = readl(mmio + PDC_CTLSTAT);
820 tmp &= ~PDC_MASK_INT;
821 writel(tmp, mmio + PDC_CTLSTAT);
822 readl(mmio + PDC_CTLSTAT); /* flush */
823}
Linus Torvalds1da177e2005-04-16 15:20:36 -0700824
Jeff Garzik67651ee2009-04-08 16:02:18 -0400825static void pdc_reset_port(struct ata_port *ap)
826{
827 void __iomem *mmio = ap->ioaddr.cmd_addr + PDC_CTLSTAT;
828 unsigned int i;
829 u32 tmp;
830
831 /* FIXME: handle HDMA copy engine */
832
833 for (i = 11; i > 0; i--) {
834 tmp = readl(mmio);
835 if (tmp & PDC_RESET)
836 break;
837
838 udelay(100);
839
840 tmp |= PDC_RESET;
841 writel(tmp, mmio);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700842 }
843
Jeff Garzik67651ee2009-04-08 16:02:18 -0400844 tmp &= ~PDC_RESET;
845 writel(tmp, mmio);
846 readl(mmio); /* flush */
847}
848
849static int pdc_softreset(struct ata_link *link, unsigned int *class,
850 unsigned long deadline)
851{
852 pdc_reset_port(link->ap);
853 return ata_sff_softreset(link, class, deadline);
854}
855
856static void pdc_error_handler(struct ata_port *ap)
857{
858 if (!(ap->pflags & ATA_PFLAG_FROZEN))
859 pdc_reset_port(ap);
860
Tejun Heofe06e5f2010-05-10 21:41:39 +0200861 ata_sff_error_handler(ap);
Jeff Garzik67651ee2009-04-08 16:02:18 -0400862}
863
864static void pdc_post_internal_cmd(struct ata_queued_cmd *qc)
865{
866 struct ata_port *ap = qc->ap;
867
868 /* make DMA engine forget about the failed command */
869 if (qc->flags & ATA_QCFLAG_FAILED)
870 pdc_reset_port(ap);
871}
872
873static int pdc_check_atapi_dma(struct ata_queued_cmd *qc)
874{
875 u8 *scsicmd = qc->scsicmd->cmnd;
876 int pio = 1; /* atapi dma off by default */
877
878 /* Whitelist commands that may use DMA. */
879 switch (scsicmd[0]) {
880 case WRITE_12:
881 case WRITE_10:
882 case WRITE_6:
883 case READ_12:
884 case READ_10:
885 case READ_6:
886 case 0xad: /* READ_DVD_STRUCTURE */
887 case 0xbe: /* READ_CD */
888 pio = 0;
889 }
890 /* -45150 (FFFF4FA2) to -1 (FFFFFFFF) shall use PIO mode */
891 if (scsicmd[0] == WRITE_10) {
892 unsigned int lba =
893 (scsicmd[2] << 24) |
894 (scsicmd[3] << 16) |
895 (scsicmd[4] << 8) |
896 scsicmd[5];
897 if (lba >= 0xFFFF4FA2)
898 pio = 1;
899 }
900 return pio;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700901}
902
Jeff Garzik057ace52005-10-22 14:27:05 -0400903static void pdc_tf_load_mmio(struct ata_port *ap, const struct ata_taskfile *tf)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700904{
Jeff Garzik5796d1c2007-10-26 00:03:37 -0400905 WARN_ON(tf->protocol == ATA_PROT_DMA ||
David Milburn19799bf2009-05-13 18:02:21 -0500906 tf->protocol == ATAPI_PROT_DMA);
Tejun Heo9363c382008-04-07 22:47:16 +0900907 ata_sff_tf_load(ap, tf);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700908}
909
910
Jeff Garzik057ace52005-10-22 14:27:05 -0400911static void pdc_exec_command_mmio(struct ata_port *ap, const struct ata_taskfile *tf)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700912{
Jeff Garzik5796d1c2007-10-26 00:03:37 -0400913 WARN_ON(tf->protocol == ATA_PROT_DMA ||
David Milburn19799bf2009-05-13 18:02:21 -0500914 tf->protocol == ATAPI_PROT_DMA);
Tejun Heo9363c382008-04-07 22:47:16 +0900915 ata_sff_exec_command(ap, tf);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700916}
917
918
Tejun Heo0d5ff562007-02-01 15:06:36 +0900919static void pdc_sata_setup_port(struct ata_ioports *port, void __iomem *base)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700920{
921 port->cmd_addr = base;
922 port->data_addr = base;
923 port->feature_addr =
924 port->error_addr = base + 0x4;
925 port->nsect_addr = base + 0x8;
926 port->lbal_addr = base + 0xc;
927 port->lbam_addr = base + 0x10;
928 port->lbah_addr = base + 0x14;
929 port->device_addr = base + 0x18;
930 port->command_addr =
931 port->status_addr = base + 0x1c;
932 port->altstatus_addr =
933 port->ctl_addr = base + 0x38;
934}
935
936
Tejun Heo4447d352007-04-17 23:44:08 +0900937static void pdc20621_get_from_dimm(struct ata_host *host, void *psource,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700938 u32 offset, u32 size)
939{
940 u32 window_size;
941 u16 idx;
942 u8 page_mask;
943 long dist;
Tejun Heo4447d352007-04-17 23:44:08 +0900944 void __iomem *mmio = host->iomap[PDC_MMIO_BAR];
945 void __iomem *dimm_mmio = host->iomap[PDC_DIMM_BAR];
Linus Torvalds1da177e2005-04-16 15:20:36 -0700946
947 /* hard-code chip #0 */
948 mmio += PDC_CHIP0_OFS;
949
Jeff Garzik8a60a072005-07-31 13:13:24 -0400950 page_mask = 0x00;
Jeff Garzik5796d1c2007-10-26 00:03:37 -0400951 window_size = 0x2000 * 4; /* 32K byte uchar size */
Jeff Garzik8a60a072005-07-31 13:13:24 -0400952 idx = (u16) (offset / window_size);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700953
954 writel(0x01, mmio + PDC_GENERAL_CTLR);
955 readl(mmio + PDC_GENERAL_CTLR);
956 writel(((idx) << page_mask), mmio + PDC_DIMM_WINDOW_CTLR);
957 readl(mmio + PDC_DIMM_WINDOW_CTLR);
958
959 offset -= (idx * window_size);
960 idx++;
Jeff Garzik8a60a072005-07-31 13:13:24 -0400961 dist = ((long) (window_size - (offset + size))) >= 0 ? size :
Linus Torvalds1da177e2005-04-16 15:20:36 -0700962 (long) (window_size - offset);
Joe Perchesd5185d62014-03-26 09:34:49 -0700963 memcpy_fromio(psource, dimm_mmio + offset / 4, dist);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700964
Jeff Garzik8a60a072005-07-31 13:13:24 -0400965 psource += dist;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700966 size -= dist;
967 for (; (long) size >= (long) window_size ;) {
968 writel(0x01, mmio + PDC_GENERAL_CTLR);
969 readl(mmio + PDC_GENERAL_CTLR);
970 writel(((idx) << page_mask), mmio + PDC_DIMM_WINDOW_CTLR);
971 readl(mmio + PDC_DIMM_WINDOW_CTLR);
Joe Perchesd5185d62014-03-26 09:34:49 -0700972 memcpy_fromio(psource, dimm_mmio, window_size / 4);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700973 psource += window_size;
974 size -= window_size;
Jeff Garzik5796d1c2007-10-26 00:03:37 -0400975 idx++;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700976 }
977
978 if (size) {
979 writel(0x01, mmio + PDC_GENERAL_CTLR);
980 readl(mmio + PDC_GENERAL_CTLR);
981 writel(((idx) << page_mask), mmio + PDC_DIMM_WINDOW_CTLR);
982 readl(mmio + PDC_DIMM_WINDOW_CTLR);
Joe Perchesd5185d62014-03-26 09:34:49 -0700983 memcpy_fromio(psource, dimm_mmio, size / 4);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700984 }
985}
Linus Torvalds1da177e2005-04-16 15:20:36 -0700986
987
Tejun Heo4447d352007-04-17 23:44:08 +0900988static void pdc20621_put_to_dimm(struct ata_host *host, void *psource,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700989 u32 offset, u32 size)
990{
991 u32 window_size;
992 u16 idx;
993 u8 page_mask;
994 long dist;
Tejun Heo4447d352007-04-17 23:44:08 +0900995 void __iomem *mmio = host->iomap[PDC_MMIO_BAR];
996 void __iomem *dimm_mmio = host->iomap[PDC_DIMM_BAR];
Linus Torvalds1da177e2005-04-16 15:20:36 -0700997
Jeff Garzik8a60a072005-07-31 13:13:24 -0400998 /* hard-code chip #0 */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700999 mmio += PDC_CHIP0_OFS;
1000
Jeff Garzik8a60a072005-07-31 13:13:24 -04001001 page_mask = 0x00;
Jeff Garzik5796d1c2007-10-26 00:03:37 -04001002 window_size = 0x2000 * 4; /* 32K byte uchar size */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001003 idx = (u16) (offset / window_size);
1004
1005 writel(((idx) << page_mask), mmio + PDC_DIMM_WINDOW_CTLR);
1006 readl(mmio + PDC_DIMM_WINDOW_CTLR);
Jeff Garzik8a60a072005-07-31 13:13:24 -04001007 offset -= (idx * window_size);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001008 idx++;
1009 dist = ((long)(s32)(window_size - (offset + size))) >= 0 ? size :
1010 (long) (window_size - offset);
Al Viroa9afd7c2005-10-21 06:46:02 +01001011 memcpy_toio(dimm_mmio + offset / 4, psource, dist);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001012 writel(0x01, mmio + PDC_GENERAL_CTLR);
1013 readl(mmio + PDC_GENERAL_CTLR);
1014
Jeff Garzik8a60a072005-07-31 13:13:24 -04001015 psource += dist;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001016 size -= dist;
1017 for (; (long) size >= (long) window_size ;) {
1018 writel(((idx) << page_mask), mmio + PDC_DIMM_WINDOW_CTLR);
1019 readl(mmio + PDC_DIMM_WINDOW_CTLR);
Al Viroa9afd7c2005-10-21 06:46:02 +01001020 memcpy_toio(dimm_mmio, psource, window_size / 4);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001021 writel(0x01, mmio + PDC_GENERAL_CTLR);
1022 readl(mmio + PDC_GENERAL_CTLR);
1023 psource += window_size;
1024 size -= window_size;
Jeff Garzik5796d1c2007-10-26 00:03:37 -04001025 idx++;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001026 }
Jeff Garzik8a60a072005-07-31 13:13:24 -04001027
Linus Torvalds1da177e2005-04-16 15:20:36 -07001028 if (size) {
1029 writel(((idx) << page_mask), mmio + PDC_DIMM_WINDOW_CTLR);
1030 readl(mmio + PDC_DIMM_WINDOW_CTLR);
Al Viroa9afd7c2005-10-21 06:46:02 +01001031 memcpy_toio(dimm_mmio, psource, size / 4);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001032 writel(0x01, mmio + PDC_GENERAL_CTLR);
1033 readl(mmio + PDC_GENERAL_CTLR);
1034 }
1035}
1036
1037
Tejun Heo4447d352007-04-17 23:44:08 +09001038static unsigned int pdc20621_i2c_read(struct ata_host *host, u32 device,
Linus Torvalds1da177e2005-04-16 15:20:36 -07001039 u32 subaddr, u32 *pdata)
1040{
Tejun Heo4447d352007-04-17 23:44:08 +09001041 void __iomem *mmio = host->iomap[PDC_MMIO_BAR];
Linus Torvalds1da177e2005-04-16 15:20:36 -07001042 u32 i2creg = 0;
Jeff Garzik8a60a072005-07-31 13:13:24 -04001043 u32 status;
Jeff Garzik5796d1c2007-10-26 00:03:37 -04001044 u32 count = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001045
1046 /* hard-code chip #0 */
1047 mmio += PDC_CHIP0_OFS;
1048
1049 i2creg |= device << 24;
1050 i2creg |= subaddr << 16;
1051
1052 /* Set the device and subaddress */
Jeff Garzikb2d46b62007-05-27 22:58:54 -04001053 writel(i2creg, mmio + PDC_I2C_ADDR_DATA);
1054 readl(mmio + PDC_I2C_ADDR_DATA);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001055
1056 /* Write Control to perform read operation, mask int */
Jeff Garzik8a60a072005-07-31 13:13:24 -04001057 writel(PDC_I2C_READ | PDC_I2C_START | PDC_I2C_MASK_INT,
Jeff Garzikb2d46b62007-05-27 22:58:54 -04001058 mmio + PDC_I2C_CONTROL);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001059
1060 for (count = 0; count <= 1000; count ++) {
Jeff Garzikb2d46b62007-05-27 22:58:54 -04001061 status = readl(mmio + PDC_I2C_CONTROL);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001062 if (status & PDC_I2C_COMPLETE) {
Jeff Garzikb2d46b62007-05-27 22:58:54 -04001063 status = readl(mmio + PDC_I2C_ADDR_DATA);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001064 break;
1065 } else if (count == 1000)
1066 return 0;
1067 }
1068
1069 *pdata = (status >> 8) & 0x000000ff;
Jeff Garzik8a60a072005-07-31 13:13:24 -04001070 return 1;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001071}
1072
1073
Tejun Heo4447d352007-04-17 23:44:08 +09001074static int pdc20621_detect_dimm(struct ata_host *host)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001075{
Jeff Garzik5796d1c2007-10-26 00:03:37 -04001076 u32 data = 0;
Tejun Heo4447d352007-04-17 23:44:08 +09001077 if (pdc20621_i2c_read(host, PDC_DIMM0_SPD_DEV_ADDRESS,
Linus Torvalds1da177e2005-04-16 15:20:36 -07001078 PDC_DIMM_SPD_SYSTEM_FREQ, &data)) {
Jeff Garzik5796d1c2007-10-26 00:03:37 -04001079 if (data == 100)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001080 return 100;
Jeff Garzik5796d1c2007-10-26 00:03:37 -04001081 } else
Linus Torvalds1da177e2005-04-16 15:20:36 -07001082 return 0;
Jeff Garzik8a60a072005-07-31 13:13:24 -04001083
Tejun Heo4447d352007-04-17 23:44:08 +09001084 if (pdc20621_i2c_read(host, PDC_DIMM0_SPD_DEV_ADDRESS, 9, &data)) {
Jeff Garzikb4479162007-10-25 20:47:30 -04001085 if (data <= 0x75)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001086 return 133;
Jeff Garzik5796d1c2007-10-26 00:03:37 -04001087 } else
Linus Torvalds1da177e2005-04-16 15:20:36 -07001088 return 0;
Jeff Garzik8a60a072005-07-31 13:13:24 -04001089
Jeff Garzik5796d1c2007-10-26 00:03:37 -04001090 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001091}
1092
1093
Tejun Heo4447d352007-04-17 23:44:08 +09001094static int pdc20621_prog_dimm0(struct ata_host *host)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001095{
1096 u32 spd0[50];
1097 u32 data = 0;
Jeff Garzik5796d1c2007-10-26 00:03:37 -04001098 int size, i;
1099 u8 bdimmsize;
Tejun Heo4447d352007-04-17 23:44:08 +09001100 void __iomem *mmio = host->iomap[PDC_MMIO_BAR];
Linus Torvalds1da177e2005-04-16 15:20:36 -07001101 static const struct {
1102 unsigned int reg;
1103 unsigned int ofs;
1104 } pdc_i2c_read_data [] = {
Jeff Garzik8a60a072005-07-31 13:13:24 -04001105 { PDC_DIMM_SPD_TYPE, 11 },
Linus Torvalds1da177e2005-04-16 15:20:36 -07001106 { PDC_DIMM_SPD_FRESH_RATE, 12 },
Jeff Garzik8a60a072005-07-31 13:13:24 -04001107 { PDC_DIMM_SPD_COLUMN_NUM, 4 },
Linus Torvalds1da177e2005-04-16 15:20:36 -07001108 { PDC_DIMM_SPD_ATTRIBUTE, 21 },
1109 { PDC_DIMM_SPD_ROW_NUM, 3 },
1110 { PDC_DIMM_SPD_BANK_NUM, 17 },
1111 { PDC_DIMM_SPD_MODULE_ROW, 5 },
1112 { PDC_DIMM_SPD_ROW_PRE_CHARGE, 27 },
1113 { PDC_DIMM_SPD_ROW_ACTIVE_DELAY, 28 },
1114 { PDC_DIMM_SPD_RAS_CAS_DELAY, 29 },
1115 { PDC_DIMM_SPD_ACTIVE_PRECHARGE, 30 },
Jeff Garzik8a60a072005-07-31 13:13:24 -04001116 { PDC_DIMM_SPD_CAS_LATENCY, 18 },
Linus Torvalds1da177e2005-04-16 15:20:36 -07001117 };
1118
1119 /* hard-code chip #0 */
1120 mmio += PDC_CHIP0_OFS;
1121
Jeff Garzik5796d1c2007-10-26 00:03:37 -04001122 for (i = 0; i < ARRAY_SIZE(pdc_i2c_read_data); i++)
Tejun Heo4447d352007-04-17 23:44:08 +09001123 pdc20621_i2c_read(host, PDC_DIMM0_SPD_DEV_ADDRESS,
Jeff Garzik8a60a072005-07-31 13:13:24 -04001124 pdc_i2c_read_data[i].reg,
Linus Torvalds1da177e2005-04-16 15:20:36 -07001125 &spd0[pdc_i2c_read_data[i].ofs]);
Jeff Garzik8a60a072005-07-31 13:13:24 -04001126
Jeff Garzik5796d1c2007-10-26 00:03:37 -04001127 data |= (spd0[4] - 8) | ((spd0[21] != 0) << 3) | ((spd0[3]-11) << 4);
1128 data |= ((spd0[17] / 4) << 6) | ((spd0[5] / 2) << 7) |
Linus Torvalds1da177e2005-04-16 15:20:36 -07001129 ((((spd0[27] + 9) / 10) - 1) << 8) ;
Jeff Garzik5796d1c2007-10-26 00:03:37 -04001130 data |= (((((spd0[29] > spd0[28])
Jeff Garzik8a60a072005-07-31 13:13:24 -04001131 ? spd0[29] : spd0[28]) + 9) / 10) - 1) << 10;
Jeff Garzik5796d1c2007-10-26 00:03:37 -04001132 data |= ((spd0[30] - spd0[29] + 9) / 10 - 2) << 12;
Jeff Garzik8a60a072005-07-31 13:13:24 -04001133
Jeff Garzik5796d1c2007-10-26 00:03:37 -04001134 if (spd0[18] & 0x08)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001135 data |= ((0x03) << 14);
Jeff Garzik5796d1c2007-10-26 00:03:37 -04001136 else if (spd0[18] & 0x04)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001137 data |= ((0x02) << 14);
Jeff Garzik5796d1c2007-10-26 00:03:37 -04001138 else if (spd0[18] & 0x01)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001139 data |= ((0x01) << 14);
Jeff Garzik5796d1c2007-10-26 00:03:37 -04001140 else
Linus Torvalds1da177e2005-04-16 15:20:36 -07001141 data |= (0 << 14);
1142
Jeff Garzik5796d1c2007-10-26 00:03:37 -04001143 /*
Linus Torvalds1da177e2005-04-16 15:20:36 -07001144 Calculate the size of bDIMMSize (power of 2) and
1145 merge the DIMM size by program start/end address.
1146 */
1147
Jeff Garzik5796d1c2007-10-26 00:03:37 -04001148 bdimmsize = spd0[4] + (spd0[5] / 2) + spd0[3] + (spd0[17] / 2) + 3;
1149 size = (1 << bdimmsize) >> 20; /* size = xxx(MB) */
1150 data |= (((size / 16) - 1) << 16);
1151 data |= (0 << 23);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001152 data |= 8;
Jeff Garzik5796d1c2007-10-26 00:03:37 -04001153 writel(data, mmio + PDC_DIMM0_CONTROL);
Jeff Garzikb2d46b62007-05-27 22:58:54 -04001154 readl(mmio + PDC_DIMM0_CONTROL);
Jeff Garzik5796d1c2007-10-26 00:03:37 -04001155 return size;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001156}
1157
1158
Tejun Heo4447d352007-04-17 23:44:08 +09001159static unsigned int pdc20621_prog_dimm_global(struct ata_host *host)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001160{
1161 u32 data, spd0;
Tejun Heo0d5ff562007-02-01 15:06:36 +09001162 int error, i;
Tejun Heo4447d352007-04-17 23:44:08 +09001163 void __iomem *mmio = host->iomap[PDC_MMIO_BAR];
Linus Torvalds1da177e2005-04-16 15:20:36 -07001164
1165 /* hard-code chip #0 */
Jeff Garzik5796d1c2007-10-26 00:03:37 -04001166 mmio += PDC_CHIP0_OFS;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001167
Jeff Garzik5796d1c2007-10-26 00:03:37 -04001168 /*
Linus Torvalds1da177e2005-04-16 15:20:36 -07001169 Set To Default : DIMM Module Global Control Register (0x022259F1)
1170 DIMM Arbitration Disable (bit 20)
1171 DIMM Data/Control Output Driving Selection (bit12 - bit15)
1172 Refresh Enable (bit 17)
1173 */
1174
Jeff Garzik8a60a072005-07-31 13:13:24 -04001175 data = 0x022259F1;
Jeff Garzikb2d46b62007-05-27 22:58:54 -04001176 writel(data, mmio + PDC_SDRAM_CONTROL);
1177 readl(mmio + PDC_SDRAM_CONTROL);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001178
1179 /* Turn on for ECC */
Tomer Barletzbb44e152015-08-03 12:18:13 -07001180 if (!pdc20621_i2c_read(host, PDC_DIMM0_SPD_DEV_ADDRESS,
1181 PDC_DIMM_SPD_TYPE, &spd0)) {
Hannes Reinecke16d66232021-12-21 08:21:21 +01001182 dev_err(host->dev,
1183 "Failed in i2c read: device=%#x, subaddr=%#x\n",
1184 PDC_DIMM0_SPD_DEV_ADDRESS, PDC_DIMM_SPD_TYPE);
Tomer Barletzbb44e152015-08-03 12:18:13 -07001185 return 1;
1186 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001187 if (spd0 == 0x02) {
1188 data |= (0x01 << 16);
Jeff Garzikb2d46b62007-05-27 22:58:54 -04001189 writel(data, mmio + PDC_SDRAM_CONTROL);
1190 readl(mmio + PDC_SDRAM_CONTROL);
Hannes Reinecke16d66232021-12-21 08:21:21 +01001191 dev_err(host->dev, "Local DIMM ECC Enabled\n");
Jeff Garzik5796d1c2007-10-26 00:03:37 -04001192 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001193
Jeff Garzik5796d1c2007-10-26 00:03:37 -04001194 /* DIMM Initialization Select/Enable (bit 18/19) */
1195 data &= (~(1<<18));
1196 data |= (1<<19);
1197 writel(data, mmio + PDC_SDRAM_CONTROL);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001198
Jeff Garzik5796d1c2007-10-26 00:03:37 -04001199 error = 1;
1200 for (i = 1; i <= 10; i++) { /* polling ~5 secs */
Jeff Garzikb2d46b62007-05-27 22:58:54 -04001201 data = readl(mmio + PDC_SDRAM_CONTROL);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001202 if (!(data & (1<<19))) {
Jeff Garzik5796d1c2007-10-26 00:03:37 -04001203 error = 0;
1204 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001205 }
1206 msleep(i*100);
Jeff Garzik5796d1c2007-10-26 00:03:37 -04001207 }
1208 return error;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001209}
Jeff Garzik8a60a072005-07-31 13:13:24 -04001210
Linus Torvalds1da177e2005-04-16 15:20:36 -07001211
Tejun Heo4447d352007-04-17 23:44:08 +09001212static unsigned int pdc20621_dimm_init(struct ata_host *host)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001213{
Jeff Garzik8a60a072005-07-31 13:13:24 -04001214 int speed, size, length;
Jeff Garzik5796d1c2007-10-26 00:03:37 -04001215 u32 addr, spd0, pci_status;
Jeff Garzik5796d1c2007-10-26 00:03:37 -04001216 u32 time_period = 0;
1217 u32 tcount = 0;
1218 u32 ticks = 0;
1219 u32 clock = 0;
1220 u32 fparam = 0;
Tejun Heo4447d352007-04-17 23:44:08 +09001221 void __iomem *mmio = host->iomap[PDC_MMIO_BAR];
Linus Torvalds1da177e2005-04-16 15:20:36 -07001222
1223 /* hard-code chip #0 */
Jeff Garzik5796d1c2007-10-26 00:03:37 -04001224 mmio += PDC_CHIP0_OFS;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001225
1226 /* Initialize PLL based upon PCI Bus Frequency */
1227
1228 /* Initialize Time Period Register */
1229 writel(0xffffffff, mmio + PDC_TIME_PERIOD);
1230 time_period = readl(mmio + PDC_TIME_PERIOD);
Hannes Reineckebc21c102021-12-21 08:20:59 +01001231 dev_dbg(host->dev, "Time Period Register (0x40): 0x%x\n", time_period);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001232
1233 /* Enable timer */
Jeff Garzikb2d46b62007-05-27 22:58:54 -04001234 writel(PDC_TIMER_DEFAULT, mmio + PDC_TIME_CONTROL);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001235 readl(mmio + PDC_TIME_CONTROL);
1236
1237 /* Wait 3 seconds */
1238 msleep(3000);
1239
Jeff Garzik8a60a072005-07-31 13:13:24 -04001240 /*
Linus Torvalds1da177e2005-04-16 15:20:36 -07001241 When timer is enabled, counter is decreased every internal
1242 clock cycle.
1243 */
1244
1245 tcount = readl(mmio + PDC_TIME_COUNTER);
Hannes Reineckebc21c102021-12-21 08:20:59 +01001246 dev_dbg(host->dev, "Time Counter Register (0x44): 0x%x\n", tcount);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001247
Jeff Garzik8a60a072005-07-31 13:13:24 -04001248 /*
Linus Torvalds1da177e2005-04-16 15:20:36 -07001249 If SX4 is on PCI-X bus, after 3 seconds, the timer counter
1250 register should be >= (0xffffffff - 3x10^8).
1251 */
Jeff Garzikb4479162007-10-25 20:47:30 -04001252 if (tcount >= PCI_X_TCOUNT) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001253 ticks = (time_period - tcount);
Hannes Reineckebc21c102021-12-21 08:20:59 +01001254 dev_dbg(host->dev, "Num counters 0x%x (%d)\n", ticks, ticks);
Jeff Garzik8a60a072005-07-31 13:13:24 -04001255
Linus Torvalds1da177e2005-04-16 15:20:36 -07001256 clock = (ticks / 300000);
Hannes Reineckebc21c102021-12-21 08:20:59 +01001257 dev_dbg(host->dev, "10 * Internal clk = 0x%x (%d)\n",
1258 clock, clock);
Jeff Garzik8a60a072005-07-31 13:13:24 -04001259
Linus Torvalds1da177e2005-04-16 15:20:36 -07001260 clock = (clock * 33);
Hannes Reineckebc21c102021-12-21 08:20:59 +01001261 dev_dbg(host->dev, "10 * Internal clk * 33 = 0x%x (%d)\n",
1262 clock, clock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001263
1264 /* PLL F Param (bit 22:16) */
1265 fparam = (1400000 / clock) - 2;
Hannes Reineckebc21c102021-12-21 08:20:59 +01001266 dev_dbg(host->dev, "PLL F Param: 0x%x (%d)\n", fparam, fparam);
Jeff Garzik8a60a072005-07-31 13:13:24 -04001267
Linus Torvalds1da177e2005-04-16 15:20:36 -07001268 /* OD param = 0x2 (bit 31:30), R param = 0x5 (bit 29:25) */
1269 pci_status = (0x8a001824 | (fparam << 16));
1270 } else
1271 pci_status = PCI_PLL_INIT;
1272
1273 /* Initialize PLL. */
Hannes Reineckebc21c102021-12-21 08:20:59 +01001274 dev_dbg(host->dev, "pci_status: 0x%x\n", pci_status);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001275 writel(pci_status, mmio + PDC_CTL_STATUS);
1276 readl(mmio + PDC_CTL_STATUS);
1277
Jeff Garzik8a60a072005-07-31 13:13:24 -04001278 /*
Linus Torvalds1da177e2005-04-16 15:20:36 -07001279 Read SPD of DIMM by I2C interface,
1280 and program the DIMM Module Controller.
1281 */
Tejun Heo4447d352007-04-17 23:44:08 +09001282 if (!(speed = pdc20621_detect_dimm(host))) {
Hannes Reinecke16d66232021-12-21 08:21:21 +01001283 dev_err(host->dev, "Detect Local DIMM Fail\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001284 return 1; /* DIMM error */
Jeff Garzik5796d1c2007-10-26 00:03:37 -04001285 }
Hannes Reineckebc21c102021-12-21 08:20:59 +01001286 dev_dbg(host->dev, "Local DIMM Speed = %d\n", speed);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001287
Jeff Garzik5796d1c2007-10-26 00:03:37 -04001288 /* Programming DIMM0 Module Control Register (index_CID0:80h) */
Tejun Heo4447d352007-04-17 23:44:08 +09001289 size = pdc20621_prog_dimm0(host);
Hannes Reineckebc21c102021-12-21 08:20:59 +01001290 dev_dbg(host->dev, "Local DIMM Size = %dMB\n", size);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001291
Jeff Garzik5796d1c2007-10-26 00:03:37 -04001292 /* Programming DIMM Module Global Control Register (index_CID0:88h) */
Tejun Heo4447d352007-04-17 23:44:08 +09001293 if (pdc20621_prog_dimm_global(host)) {
Hannes Reineckebc21c102021-12-21 08:20:59 +01001294 dev_err(host->dev,
1295 "Programming DIMM Module Global Control Register Fail\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001296 return 1;
Jeff Garzik5796d1c2007-10-26 00:03:37 -04001297 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001298
Hannes Reineckef11c5402021-12-21 08:21:00 +01001299 if (dimm_test) {
Jeff Garzik5796d1c2007-10-26 00:03:37 -04001300 u8 test_parttern1[40] =
1301 {0x55,0xAA,'P','r','o','m','i','s','e',' ',
1302 'N','o','t',' ','Y','e','t',' ',
1303 'D','e','f','i','n','e','d',' ',
1304 '1','.','1','0',
1305 '9','8','0','3','1','6','1','2',0,0};
Linus Torvalds1da177e2005-04-16 15:20:36 -07001306 u8 test_parttern2[40] = {0};
1307
Jeff Garzik5796d1c2007-10-26 00:03:37 -04001308 pdc20621_put_to_dimm(host, test_parttern2, 0x10040, 40);
1309 pdc20621_put_to_dimm(host, test_parttern2, 0x40, 40);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001310
Jeff Garzik5796d1c2007-10-26 00:03:37 -04001311 pdc20621_put_to_dimm(host, test_parttern1, 0x10040, 40);
1312 pdc20621_get_from_dimm(host, test_parttern2, 0x40, 40);
Hannes Reineckef11c5402021-12-21 08:21:00 +01001313 dev_info(host->dev, "DIMM test pattern 1: %x, %x, %s\n", test_parttern2[0],
Linus Torvalds1da177e2005-04-16 15:20:36 -07001314 test_parttern2[1], &(test_parttern2[2]));
Jeff Garzik5796d1c2007-10-26 00:03:37 -04001315 pdc20621_get_from_dimm(host, test_parttern2, 0x10040,
Linus Torvalds1da177e2005-04-16 15:20:36 -07001316 40);
Hannes Reineckef11c5402021-12-21 08:21:00 +01001317 dev_info(host->dev, "DIMM test pattern 2: %x, %x, %s\n",
1318 test_parttern2[0],
1319 test_parttern2[1], &(test_parttern2[2]));
Linus Torvalds1da177e2005-04-16 15:20:36 -07001320
Jeff Garzik5796d1c2007-10-26 00:03:37 -04001321 pdc20621_put_to_dimm(host, test_parttern1, 0x40, 40);
1322 pdc20621_get_from_dimm(host, test_parttern2, 0x40, 40);
Hannes Reineckef11c5402021-12-21 08:21:00 +01001323 dev_info(host->dev, "DIMM test pattern 3: %x, %x, %s\n",
1324 test_parttern2[0],
1325 test_parttern2[1], &(test_parttern2[2]));
Linus Torvalds1da177e2005-04-16 15:20:36 -07001326 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001327
1328 /* ECC initiliazation. */
1329
Tomer Barletzbb44e152015-08-03 12:18:13 -07001330 if (!pdc20621_i2c_read(host, PDC_DIMM0_SPD_DEV_ADDRESS,
1331 PDC_DIMM_SPD_TYPE, &spd0)) {
Hannes Reineckebc21c102021-12-21 08:20:59 +01001332 dev_err(host->dev,
1333 "Failed in i2c read: device=%#x, subaddr=%#x\n",
Tomer Barletzbb44e152015-08-03 12:18:13 -07001334 PDC_DIMM0_SPD_DEV_ADDRESS, PDC_DIMM_SPD_TYPE);
1335 return 1;
1336 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001337 if (spd0 == 0x02) {
Alexander Beregalovf35b5e72009-04-15 00:00:54 +04001338 void *buf;
Hannes Reineckebc21c102021-12-21 08:20:59 +01001339 dev_dbg(host->dev, "Start ECC initialization\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001340 addr = 0;
1341 length = size * 1024 * 1024;
Alexander Beregalovf35b5e72009-04-15 00:00:54 +04001342 buf = kzalloc(ECC_ERASE_BUF_SZ, GFP_KERNEL);
Insu Yun427cc612015-12-29 16:21:15 -05001343 if (!buf)
1344 return 1;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001345 while (addr < length) {
Alexander Beregalovf35b5e72009-04-15 00:00:54 +04001346 pdc20621_put_to_dimm(host, buf, addr,
1347 ECC_ERASE_BUF_SZ);
1348 addr += ECC_ERASE_BUF_SZ;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001349 }
Alexander Beregalovf35b5e72009-04-15 00:00:54 +04001350 kfree(buf);
Hannes Reineckebc21c102021-12-21 08:20:59 +01001351 dev_dbg(host->dev, "Finish ECC initialization\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001352 }
1353 return 0;
1354}
1355
1356
Tejun Heo4447d352007-04-17 23:44:08 +09001357static void pdc_20621_init(struct ata_host *host)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001358{
1359 u32 tmp;
Tejun Heo4447d352007-04-17 23:44:08 +09001360 void __iomem *mmio = host->iomap[PDC_MMIO_BAR];
Linus Torvalds1da177e2005-04-16 15:20:36 -07001361
1362 /* hard-code chip #0 */
1363 mmio += PDC_CHIP0_OFS;
1364
1365 /*
1366 * Select page 0x40 for our 32k DIMM window
1367 */
1368 tmp = readl(mmio + PDC_20621_DIMM_WINDOW) & 0xffff0000;
1369 tmp |= PDC_PAGE_WINDOW; /* page 40h; arbitrarily selected */
1370 writel(tmp, mmio + PDC_20621_DIMM_WINDOW);
1371
1372 /*
1373 * Reset Host DMA
1374 */
1375 tmp = readl(mmio + PDC_HDMA_CTLSTAT);
1376 tmp |= PDC_RESET;
1377 writel(tmp, mmio + PDC_HDMA_CTLSTAT);
1378 readl(mmio + PDC_HDMA_CTLSTAT); /* flush */
1379
1380 udelay(10);
1381
1382 tmp = readl(mmio + PDC_HDMA_CTLSTAT);
1383 tmp &= ~PDC_RESET;
1384 writel(tmp, mmio + PDC_HDMA_CTLSTAT);
1385 readl(mmio + PDC_HDMA_CTLSTAT); /* flush */
1386}
1387
Jeff Garzik5796d1c2007-10-26 00:03:37 -04001388static int pdc_sata_init_one(struct pci_dev *pdev,
1389 const struct pci_device_id *ent)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001390{
Tejun Heo4447d352007-04-17 23:44:08 +09001391 const struct ata_port_info *ppi[] =
1392 { &pdc_port_info[ent->driver_data], NULL };
1393 struct ata_host *host;
Tejun Heo24dc5f32007-01-20 16:00:28 +09001394 struct pdc_host_priv *hpriv;
Tejun Heocbcdd872007-08-18 13:14:55 +09001395 int i, rc;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001396
Joe Perches06296a12011-04-15 15:52:00 -07001397 ata_print_version_once(&pdev->dev, DRV_VERSION);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001398
Tejun Heo4447d352007-04-17 23:44:08 +09001399 /* allocate host */
1400 host = ata_host_alloc_pinfo(&pdev->dev, ppi, 4);
1401 hpriv = devm_kzalloc(&pdev->dev, sizeof(*hpriv), GFP_KERNEL);
1402 if (!host || !hpriv)
1403 return -ENOMEM;
1404
1405 host->private_data = hpriv;
1406
1407 /* acquire resources and fill host */
Tejun Heo24dc5f32007-01-20 16:00:28 +09001408 rc = pcim_enable_device(pdev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001409 if (rc)
1410 return rc;
1411
Tejun Heo0d5ff562007-02-01 15:06:36 +09001412 rc = pcim_iomap_regions(pdev, (1 << PDC_MMIO_BAR) | (1 << PDC_DIMM_BAR),
1413 DRV_NAME);
1414 if (rc == -EBUSY)
Tejun Heo24dc5f32007-01-20 16:00:28 +09001415 pcim_pin_device(pdev);
Tejun Heo0d5ff562007-02-01 15:06:36 +09001416 if (rc)
Tejun Heo24dc5f32007-01-20 16:00:28 +09001417 return rc;
Tejun Heo4447d352007-04-17 23:44:08 +09001418 host->iomap = pcim_iomap_table(pdev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001419
Tejun Heocbcdd872007-08-18 13:14:55 +09001420 for (i = 0; i < 4; i++) {
1421 struct ata_port *ap = host->ports[i];
1422 void __iomem *base = host->iomap[PDC_MMIO_BAR] + PDC_CHIP0_OFS;
1423 unsigned int offset = 0x200 + i * 0x80;
1424
1425 pdc_sata_setup_port(&ap->ioaddr, base + offset);
1426
1427 ata_port_pbar_desc(ap, PDC_MMIO_BAR, -1, "mmio");
1428 ata_port_pbar_desc(ap, PDC_DIMM_BAR, -1, "dimm");
1429 ata_port_pbar_desc(ap, PDC_MMIO_BAR, offset, "port");
1430 }
Tejun Heo4447d352007-04-17 23:44:08 +09001431
1432 /* configure and activate */
Christoph Hellwigb5e55552019-08-26 12:57:25 +02001433 rc = dma_set_mask_and_coherent(&pdev->dev, ATA_DMA_MASK);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001434 if (rc)
Tejun Heo24dc5f32007-01-20 16:00:28 +09001435 return rc;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001436
Tejun Heo4447d352007-04-17 23:44:08 +09001437 if (pdc20621_dimm_init(host))
Tejun Heo24dc5f32007-01-20 16:00:28 +09001438 return -ENOMEM;
Tejun Heo4447d352007-04-17 23:44:08 +09001439 pdc_20621_init(host);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001440
1441 pci_set_master(pdev);
Tejun Heo4447d352007-04-17 23:44:08 +09001442 return ata_host_activate(host, pdev->irq, pdc20621_interrupt,
1443 IRQF_SHARED, &pdc_sata_sht);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001444}
1445
Axel Lin2fc75da2012-04-19 13:43:05 +08001446module_pci_driver(pdc_sata_pci_driver);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001447
1448MODULE_AUTHOR("Jeff Garzik");
1449MODULE_DESCRIPTION("Promise SATA low-level driver");
1450MODULE_LICENSE("GPL");
1451MODULE_DEVICE_TABLE(pci, pdc_sata_pci_tbl);
1452MODULE_VERSION(DRV_VERSION);