blob: ec04b8d3c791ecc3e0c544aa2d4b05f34b9923a7 [file] [log] [blame]
Linus Torvalds1da177e2005-04-16 15:20:36 -07001/*
2 * sata_sx4.c - Promise SATA
3 *
4 * Maintained by: Jeff Garzik <jgarzik@pobox.com>
5 * Please ALWAYS copy linux-ide@vger.kernel.org
6 * on emails.
7 *
8 * Copyright 2003-2004 Red Hat, Inc.
9 *
Linus Torvalds1da177e2005-04-16 15:20:36 -070010 *
Jeff Garzikaf36d7f2005-08-28 20:18:39 -040011 * This program is free software; you can redistribute it and/or modify
12 * it under the terms of the GNU General Public License as published by
13 * the Free Software Foundation; either version 2, or (at your option)
14 * any later version.
15 *
16 * This program is distributed in the hope that it will be useful,
17 * but WITHOUT ANY WARRANTY; without even the implied warranty of
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 * GNU General Public License for more details.
20 *
21 * You should have received a copy of the GNU General Public License
22 * along with this program; see the file COPYING. If not, write to
23 * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
24 *
25 *
26 * libata documentation is available via 'make {ps|pdf}docs',
27 * as Documentation/DocBook/libata.*
28 *
29 * Hardware documentation available under NDA.
Linus Torvalds1da177e2005-04-16 15:20:36 -070030 *
31 */
32
Jeff Garzika09060f2007-05-28 08:17:06 -040033/*
34 Theory of operation
35 -------------------
36
37 The SX4 (PDC20621) chip features a single Host DMA (HDMA) copy
38 engine, DIMM memory, and four ATA engines (one per SATA port).
39 Data is copied to/from DIMM memory by the HDMA engine, before
40 handing off to one (or more) of the ATA engines. The ATA
41 engines operate solely on DIMM memory.
42
43 The SX4 behaves like a PATA chip, with no SATA controls or
44 knowledge whatsoever, leading to the presumption that
45 PATA<->SATA bridges exist on SX4 boards, external to the
46 PDC20621 chip itself.
47
48 The chip is quite capable, supporting an XOR engine and linked
49 hardware commands (permits a string to transactions to be
50 submitted and waited-on as a single unit), and an optional
51 microprocessor.
52
53 The limiting factor is largely software. This Linux driver was
54 written to multiplex the single HDMA engine to copy disk
55 transactions into a fixed DIMM memory space, from where an ATA
56 engine takes over. As a result, each WRITE looks like this:
57
58 submit HDMA packet to hardware
59 hardware copies data from system memory to DIMM
60 hardware raises interrupt
61
62 submit ATA packet to hardware
63 hardware executes ATA WRITE command, w/ data in DIMM
64 hardware raises interrupt
Jeff Garzik2dcb4072007-10-19 06:42:56 -040065
Jeff Garzika09060f2007-05-28 08:17:06 -040066 and each READ looks like this:
67
68 submit ATA packet to hardware
69 hardware executes ATA READ command, w/ data in DIMM
70 hardware raises interrupt
Jeff Garzik2dcb4072007-10-19 06:42:56 -040071
Jeff Garzika09060f2007-05-28 08:17:06 -040072 submit HDMA packet to hardware
73 hardware copies data from DIMM to system memory
74 hardware raises interrupt
75
76 This is a very slow, lock-step way of doing things that can
77 certainly be improved by motivated kernel hackers.
78
79 */
80
Linus Torvalds1da177e2005-04-16 15:20:36 -070081#include <linux/kernel.h>
82#include <linux/module.h>
83#include <linux/pci.h>
84#include <linux/init.h>
85#include <linux/blkdev.h>
86#include <linux/delay.h>
87#include <linux/interrupt.h>
Jeff Garzika9524a72005-10-30 14:39:11 -050088#include <linux/device.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070089#include <scsi/scsi_host.h>
Jeff Garzik193515d2005-11-07 00:59:37 -050090#include <scsi/scsi_cmnd.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070091#include <linux/libata.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070092#include "sata_promise.h"
93
94#define DRV_NAME "sata_sx4"
Jeff Garzik2a3103c2007-08-31 04:54:06 -040095#define DRV_VERSION "0.12"
Linus Torvalds1da177e2005-04-16 15:20:36 -070096
97
98enum {
Tejun Heo0d5ff562007-02-01 15:06:36 +090099 PDC_MMIO_BAR = 3,
100 PDC_DIMM_BAR = 4,
101
Linus Torvalds1da177e2005-04-16 15:20:36 -0700102 PDC_PRD_TBL = 0x44, /* Direct command DMA table addr */
103
104 PDC_PKT_SUBMIT = 0x40, /* Command packet pointer addr */
105 PDC_HDMA_PKT_SUBMIT = 0x100, /* Host DMA packet pointer addr */
106 PDC_INT_SEQMASK = 0x40, /* Mask of asserted SEQ INTs */
107 PDC_HDMA_CTLSTAT = 0x12C, /* Host DMA control / status */
108
Jeff Garzika09060f2007-05-28 08:17:06 -0400109 PDC_CTLSTAT = 0x60, /* IDEn control / status */
110
Linus Torvalds1da177e2005-04-16 15:20:36 -0700111 PDC_20621_SEQCTL = 0x400,
112 PDC_20621_SEQMASK = 0x480,
113 PDC_20621_GENERAL_CTL = 0x484,
114 PDC_20621_PAGE_SIZE = (32 * 1024),
115
116 /* chosen, not constant, values; we design our own DIMM mem map */
117 PDC_20621_DIMM_WINDOW = 0x0C, /* page# for 32K DIMM window */
118 PDC_20621_DIMM_BASE = 0x00200000,
119 PDC_20621_DIMM_DATA = (64 * 1024),
120 PDC_DIMM_DATA_STEP = (256 * 1024),
121 PDC_DIMM_WINDOW_STEP = (8 * 1024),
122 PDC_DIMM_HOST_PRD = (6 * 1024),
123 PDC_DIMM_HOST_PKT = (128 * 0),
124 PDC_DIMM_HPKT_PRD = (128 * 1),
125 PDC_DIMM_ATA_PKT = (128 * 2),
126 PDC_DIMM_APKT_PRD = (128 * 3),
127 PDC_DIMM_HEADER_SZ = PDC_DIMM_APKT_PRD + 128,
128 PDC_PAGE_WINDOW = 0x40,
129 PDC_PAGE_DATA = PDC_PAGE_WINDOW +
130 (PDC_20621_DIMM_DATA / PDC_20621_PAGE_SIZE),
131 PDC_PAGE_SET = PDC_DIMM_DATA_STEP / PDC_20621_PAGE_SIZE,
132
133 PDC_CHIP0_OFS = 0xC0000, /* offset of chip #0 */
134
135 PDC_20621_ERR_MASK = (1<<19) | (1<<20) | (1<<21) | (1<<22) |
136 (1<<23),
137
138 board_20621 = 0, /* FastTrak S150 SX4 */
139
Jeff Garzikb2d46b62007-05-27 22:58:54 -0400140 PDC_MASK_INT = (1 << 10), /* HDMA/ATA mask int */
141 PDC_RESET = (1 << 11), /* HDMA/ATA reset */
Jeff Garzika09060f2007-05-28 08:17:06 -0400142 PDC_DMA_ENABLE = (1 << 7), /* DMA start/stop */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700143
144 PDC_MAX_HDMA = 32,
145 PDC_HDMA_Q_MASK = (PDC_MAX_HDMA - 1),
146
Jeff Garzikb2d46b62007-05-27 22:58:54 -0400147 PDC_DIMM0_SPD_DEV_ADDRESS = 0x50,
148 PDC_DIMM1_SPD_DEV_ADDRESS = 0x51,
149 PDC_I2C_CONTROL = 0x48,
150 PDC_I2C_ADDR_DATA = 0x4C,
151 PDC_DIMM0_CONTROL = 0x80,
152 PDC_DIMM1_CONTROL = 0x84,
153 PDC_SDRAM_CONTROL = 0x88,
154 PDC_I2C_WRITE = 0, /* master -> slave */
155 PDC_I2C_READ = (1 << 6), /* master <- slave */
156 PDC_I2C_START = (1 << 7), /* start I2C proto */
157 PDC_I2C_MASK_INT = (1 << 5), /* mask I2C interrupt */
158 PDC_I2C_COMPLETE = (1 << 16), /* I2C normal compl. */
159 PDC_I2C_NO_ACK = (1 << 20), /* slave no-ack addr */
160 PDC_DIMM_SPD_SUBADDRESS_START = 0x00,
161 PDC_DIMM_SPD_SUBADDRESS_END = 0x7F,
162 PDC_DIMM_SPD_ROW_NUM = 3,
163 PDC_DIMM_SPD_COLUMN_NUM = 4,
164 PDC_DIMM_SPD_MODULE_ROW = 5,
165 PDC_DIMM_SPD_TYPE = 11,
166 PDC_DIMM_SPD_FRESH_RATE = 12,
167 PDC_DIMM_SPD_BANK_NUM = 17,
168 PDC_DIMM_SPD_CAS_LATENCY = 18,
169 PDC_DIMM_SPD_ATTRIBUTE = 21,
170 PDC_DIMM_SPD_ROW_PRE_CHARGE = 27,
171 PDC_DIMM_SPD_ROW_ACTIVE_DELAY = 28,
172 PDC_DIMM_SPD_RAS_CAS_DELAY = 29,
173 PDC_DIMM_SPD_ACTIVE_PRECHARGE = 30,
174 PDC_DIMM_SPD_SYSTEM_FREQ = 126,
175 PDC_CTL_STATUS = 0x08,
176 PDC_DIMM_WINDOW_CTLR = 0x0C,
177 PDC_TIME_CONTROL = 0x3C,
178 PDC_TIME_PERIOD = 0x40,
179 PDC_TIME_COUNTER = 0x44,
180 PDC_GENERAL_CTLR = 0x484,
181 PCI_PLL_INIT = 0x8A531824,
182 PCI_X_TCOUNT = 0xEE1E5CFF,
183
184 /* PDC_TIME_CONTROL bits */
185 PDC_TIMER_BUZZER = (1 << 10),
186 PDC_TIMER_MODE_PERIODIC = 0, /* bits 9:8 == 00 */
187 PDC_TIMER_MODE_ONCE = (1 << 8), /* bits 9:8 == 01 */
188 PDC_TIMER_ENABLE = (1 << 7),
189 PDC_TIMER_MASK_INT = (1 << 5),
190 PDC_TIMER_SEQ_MASK = 0x1f, /* SEQ ID for timer */
191 PDC_TIMER_DEFAULT = PDC_TIMER_MODE_ONCE |
192 PDC_TIMER_ENABLE |
193 PDC_TIMER_MASK_INT,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700194};
195
196
197struct pdc_port_priv {
198 u8 dimm_buf[(ATA_PRD_SZ * ATA_MAX_PRD) + 512];
199 u8 *pkt;
200 dma_addr_t pkt_dma;
201};
202
203struct pdc_host_priv {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700204 unsigned int doing_hdma;
205 unsigned int hdma_prod;
206 unsigned int hdma_cons;
207 struct {
208 struct ata_queued_cmd *qc;
209 unsigned int seq;
210 unsigned long pkt_ofs;
211 } hdma[32];
212};
213
214
Jeff Garzik5796d1c2007-10-26 00:03:37 -0400215static int pdc_sata_init_one(struct pci_dev *pdev, const struct pci_device_id *ent);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700216static void pdc_eng_timeout(struct ata_port *ap);
Jeff Garzik5796d1c2007-10-26 00:03:37 -0400217static void pdc_20621_phy_reset(struct ata_port *ap);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700218static int pdc_port_start(struct ata_port *ap);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700219static void pdc20621_qc_prep(struct ata_queued_cmd *qc);
Jeff Garzik057ace52005-10-22 14:27:05 -0400220static void pdc_tf_load_mmio(struct ata_port *ap, const struct ata_taskfile *tf);
221static void pdc_exec_command_mmio(struct ata_port *ap, const struct ata_taskfile *tf);
Tejun Heo4447d352007-04-17 23:44:08 +0900222static unsigned int pdc20621_dimm_init(struct ata_host *host);
223static int pdc20621_detect_dimm(struct ata_host *host);
224static unsigned int pdc20621_i2c_read(struct ata_host *host,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700225 u32 device, u32 subaddr, u32 *pdata);
Tejun Heo4447d352007-04-17 23:44:08 +0900226static int pdc20621_prog_dimm0(struct ata_host *host);
227static unsigned int pdc20621_prog_dimm_global(struct ata_host *host);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700228#ifdef ATA_VERBOSE_DEBUG
Tejun Heo4447d352007-04-17 23:44:08 +0900229static void pdc20621_get_from_dimm(struct ata_host *host,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700230 void *psource, u32 offset, u32 size);
231#endif
Tejun Heo4447d352007-04-17 23:44:08 +0900232static void pdc20621_put_to_dimm(struct ata_host *host,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700233 void *psource, u32 offset, u32 size);
234static void pdc20621_irq_clear(struct ata_port *ap);
Tejun Heo9363c382008-04-07 22:47:16 +0900235static unsigned int pdc20621_qc_issue(struct ata_queued_cmd *qc);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700236
237
Jeff Garzik193515d2005-11-07 00:59:37 -0500238static struct scsi_host_template pdc_sata_sht = {
Tejun Heo68d1d072008-03-25 12:22:49 +0900239 ATA_BASE_SHT(DRV_NAME),
Linus Torvalds1da177e2005-04-16 15:20:36 -0700240 .sg_tablesize = LIBATA_MAX_PRD,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700241 .dma_boundary = ATA_DMA_BOUNDARY,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700242};
243
Tejun Heo029cfd62008-03-25 12:22:49 +0900244/* TODO: inherit from base port_ops after converting to new EH */
245static struct ata_port_operations pdc_20621_ops = {
Tejun Heo5682ed32008-04-07 22:47:16 +0900246 .sff_tf_load = pdc_tf_load_mmio,
247 .sff_tf_read = ata_sff_tf_read,
248 .sff_check_status = ata_sff_check_status,
249 .sff_exec_command = pdc_exec_command_mmio,
250 .sff_dev_select = ata_sff_dev_select,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700251 .phy_reset = pdc_20621_phy_reset,
252 .qc_prep = pdc20621_qc_prep,
Tejun Heo9363c382008-04-07 22:47:16 +0900253 .qc_issue = pdc20621_qc_issue,
Tejun Heo22183bf2008-04-07 22:47:20 +0900254 .qc_fill_rtf = ata_sff_qc_fill_rtf,
Tejun Heo5682ed32008-04-07 22:47:16 +0900255 .sff_data_xfer = ata_sff_data_xfer,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700256 .eng_timeout = pdc_eng_timeout,
Tejun Heo5682ed32008-04-07 22:47:16 +0900257 .sff_irq_clear = pdc20621_irq_clear,
258 .sff_irq_on = ata_sff_irq_on,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700259 .port_start = pdc_port_start,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700260};
261
Arjan van de Ven98ac62d2005-11-28 10:06:23 +0100262static const struct ata_port_info pdc_port_info[] = {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700263 /* board_20621 */
264 {
Jeff Garzikcca39742006-08-24 03:19:22 -0400265 .flags = ATA_FLAG_SATA | ATA_FLAG_NO_LEGACY |
Jeff Garzik50630192005-12-13 02:29:45 -0500266 ATA_FLAG_SRST | ATA_FLAG_MMIO |
Albert Lee1f3461a2006-05-23 18:12:30 +0800267 ATA_FLAG_NO_ATAPI | ATA_FLAG_PIO_POLLING,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700268 .pio_mask = 0x1f, /* pio0-4 */
269 .mwdma_mask = 0x07, /* mwdma0-2 */
Jeff Garzik469248a2007-07-08 01:13:16 -0400270 .udma_mask = ATA_UDMA6,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700271 .port_ops = &pdc_20621_ops,
272 },
273
274};
275
Jeff Garzik3b7d6972005-11-10 11:04:11 -0500276static const struct pci_device_id pdc_sata_pci_tbl[] = {
Jeff Garzik54bb3a92006-09-27 22:20:11 -0400277 { PCI_VDEVICE(PROMISE, 0x6622), board_20621 },
278
Linus Torvalds1da177e2005-04-16 15:20:36 -0700279 { } /* terminate list */
280};
281
Linus Torvalds1da177e2005-04-16 15:20:36 -0700282static struct pci_driver pdc_sata_pci_driver = {
283 .name = DRV_NAME,
284 .id_table = pdc_sata_pci_tbl,
285 .probe = pdc_sata_init_one,
286 .remove = ata_pci_remove_one,
287};
288
289
Linus Torvalds1da177e2005-04-16 15:20:36 -0700290static int pdc_port_start(struct ata_port *ap)
291{
Jeff Garzikcca39742006-08-24 03:19:22 -0400292 struct device *dev = ap->host->dev;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700293 struct pdc_port_priv *pp;
294 int rc;
295
296 rc = ata_port_start(ap);
297 if (rc)
298 return rc;
299
Tejun Heo24dc5f32007-01-20 16:00:28 +0900300 pp = devm_kzalloc(dev, sizeof(*pp), GFP_KERNEL);
301 if (!pp)
302 return -ENOMEM;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700303
Tejun Heo24dc5f32007-01-20 16:00:28 +0900304 pp->pkt = dmam_alloc_coherent(dev, 128, &pp->pkt_dma, GFP_KERNEL);
305 if (!pp->pkt)
306 return -ENOMEM;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700307
308 ap->private_data = pp;
309
310 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700311}
312
Jeff Garzik5796d1c2007-10-26 00:03:37 -0400313static void pdc_20621_phy_reset(struct ata_port *ap)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700314{
315 VPRINTK("ENTER\n");
Jeff Garzik5796d1c2007-10-26 00:03:37 -0400316 ap->cbl = ATA_CBL_SATA;
317 ata_port_probe(ap);
318 ata_bus_reset(ap);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700319}
320
321static inline void pdc20621_ata_sg(struct ata_taskfile *tf, u8 *buf,
Jeff Garzik5796d1c2007-10-26 00:03:37 -0400322 unsigned int portno,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700323 unsigned int total_len)
324{
325 u32 addr;
326 unsigned int dw = PDC_DIMM_APKT_PRD >> 2;
Al Viro4ca4e432007-12-30 09:32:22 +0000327 __le32 *buf32 = (__le32 *) buf;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700328
329 /* output ATA packet S/G table */
330 addr = PDC_20621_DIMM_BASE + PDC_20621_DIMM_DATA +
331 (PDC_DIMM_DATA_STEP * portno);
332 VPRINTK("ATA sg addr 0x%x, %d\n", addr, addr);
333 buf32[dw] = cpu_to_le32(addr);
334 buf32[dw + 1] = cpu_to_le32(total_len | ATA_PRD_EOT);
335
336 VPRINTK("ATA PSG @ %x == (0x%x, 0x%x)\n",
337 PDC_20621_DIMM_BASE +
338 (PDC_DIMM_WINDOW_STEP * portno) +
339 PDC_DIMM_APKT_PRD,
340 buf32[dw], buf32[dw + 1]);
341}
342
343static inline void pdc20621_host_sg(struct ata_taskfile *tf, u8 *buf,
Jeff Garzik5796d1c2007-10-26 00:03:37 -0400344 unsigned int portno,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700345 unsigned int total_len)
346{
347 u32 addr;
348 unsigned int dw = PDC_DIMM_HPKT_PRD >> 2;
Al Viro4ca4e432007-12-30 09:32:22 +0000349 __le32 *buf32 = (__le32 *) buf;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700350
351 /* output Host DMA packet S/G table */
352 addr = PDC_20621_DIMM_BASE + PDC_20621_DIMM_DATA +
353 (PDC_DIMM_DATA_STEP * portno);
354
355 buf32[dw] = cpu_to_le32(addr);
356 buf32[dw + 1] = cpu_to_le32(total_len | ATA_PRD_EOT);
357
358 VPRINTK("HOST PSG @ %x == (0x%x, 0x%x)\n",
359 PDC_20621_DIMM_BASE +
360 (PDC_DIMM_WINDOW_STEP * portno) +
361 PDC_DIMM_HPKT_PRD,
362 buf32[dw], buf32[dw + 1]);
363}
364
365static inline unsigned int pdc20621_ata_pkt(struct ata_taskfile *tf,
366 unsigned int devno, u8 *buf,
367 unsigned int portno)
368{
369 unsigned int i, dw;
Al Viro4ca4e432007-12-30 09:32:22 +0000370 __le32 *buf32 = (__le32 *) buf;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700371 u8 dev_reg;
372
373 unsigned int dimm_sg = PDC_20621_DIMM_BASE +
374 (PDC_DIMM_WINDOW_STEP * portno) +
375 PDC_DIMM_APKT_PRD;
376 VPRINTK("ENTER, dimm_sg == 0x%x, %d\n", dimm_sg, dimm_sg);
377
378 i = PDC_DIMM_ATA_PKT;
379
380 /*
381 * Set up ATA packet
382 */
383 if ((tf->protocol == ATA_PROT_DMA) && (!(tf->flags & ATA_TFLAG_WRITE)))
384 buf[i++] = PDC_PKT_READ;
385 else if (tf->protocol == ATA_PROT_NODATA)
386 buf[i++] = PDC_PKT_NODATA;
387 else
388 buf[i++] = 0;
389 buf[i++] = 0; /* reserved */
390 buf[i++] = portno + 1; /* seq. id */
391 buf[i++] = 0xff; /* delay seq. id */
392
393 /* dimm dma S/G, and next-pkt */
394 dw = i >> 2;
395 if (tf->protocol == ATA_PROT_NODATA)
396 buf32[dw] = 0;
397 else
398 buf32[dw] = cpu_to_le32(dimm_sg);
399 buf32[dw + 1] = 0;
400 i += 8;
401
402 if (devno == 0)
403 dev_reg = ATA_DEVICE_OBS;
404 else
405 dev_reg = ATA_DEVICE_OBS | ATA_DEV1;
406
407 /* select device */
408 buf[i++] = (1 << 5) | PDC_PKT_CLEAR_BSY | ATA_REG_DEVICE;
409 buf[i++] = dev_reg;
410
411 /* device control register */
412 buf[i++] = (1 << 5) | PDC_REG_DEVCTL;
413 buf[i++] = tf->ctl;
414
415 return i;
416}
417
418static inline void pdc20621_host_pkt(struct ata_taskfile *tf, u8 *buf,
419 unsigned int portno)
420{
421 unsigned int dw;
Al Viro4ca4e432007-12-30 09:32:22 +0000422 u32 tmp;
423 __le32 *buf32 = (__le32 *) buf;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700424
425 unsigned int host_sg = PDC_20621_DIMM_BASE +
426 (PDC_DIMM_WINDOW_STEP * portno) +
427 PDC_DIMM_HOST_PRD;
428 unsigned int dimm_sg = PDC_20621_DIMM_BASE +
429 (PDC_DIMM_WINDOW_STEP * portno) +
430 PDC_DIMM_HPKT_PRD;
431 VPRINTK("ENTER, dimm_sg == 0x%x, %d\n", dimm_sg, dimm_sg);
432 VPRINTK("host_sg == 0x%x, %d\n", host_sg, host_sg);
433
434 dw = PDC_DIMM_HOST_PKT >> 2;
435
436 /*
437 * Set up Host DMA packet
438 */
439 if ((tf->protocol == ATA_PROT_DMA) && (!(tf->flags & ATA_TFLAG_WRITE)))
440 tmp = PDC_PKT_READ;
441 else
442 tmp = 0;
443 tmp |= ((portno + 1 + 4) << 16); /* seq. id */
444 tmp |= (0xff << 24); /* delay seq. id */
445 buf32[dw + 0] = cpu_to_le32(tmp);
446 buf32[dw + 1] = cpu_to_le32(host_sg);
447 buf32[dw + 2] = cpu_to_le32(dimm_sg);
448 buf32[dw + 3] = 0;
449
450 VPRINTK("HOST PKT @ %x == (0x%x 0x%x 0x%x 0x%x)\n",
451 PDC_20621_DIMM_BASE + (PDC_DIMM_WINDOW_STEP * portno) +
452 PDC_DIMM_HOST_PKT,
453 buf32[dw + 0],
454 buf32[dw + 1],
455 buf32[dw + 2],
456 buf32[dw + 3]);
457}
458
459static void pdc20621_dma_prep(struct ata_queued_cmd *qc)
460{
Jeff Garzikcedc9a42005-10-05 07:13:30 -0400461 struct scatterlist *sg;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700462 struct ata_port *ap = qc->ap;
463 struct pdc_port_priv *pp = ap->private_data;
Tejun Heo0d5ff562007-02-01 15:06:36 +0900464 void __iomem *mmio = ap->host->iomap[PDC_MMIO_BAR];
465 void __iomem *dimm_mmio = ap->host->iomap[PDC_DIMM_BAR];
Linus Torvalds1da177e2005-04-16 15:20:36 -0700466 unsigned int portno = ap->port_no;
Tejun Heoff2aeb12007-12-05 16:43:11 +0900467 unsigned int i, si, idx, total_len = 0, sgt_len;
Al Viro826cd152008-03-25 05:18:11 +0000468 __le32 *buf = (__le32 *) &pp->dimm_buf[PDC_DIMM_HEADER_SZ];
Linus Torvalds1da177e2005-04-16 15:20:36 -0700469
Tejun Heobeec7db2006-02-11 19:11:13 +0900470 WARN_ON(!(qc->flags & ATA_QCFLAG_DMAMAP));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700471
Tejun Heo44877b42007-02-21 01:06:51 +0900472 VPRINTK("ata%u: ENTER\n", ap->print_id);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700473
474 /* hard-code chip #0 */
475 mmio += PDC_CHIP0_OFS;
476
477 /*
478 * Build S/G table
479 */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700480 idx = 0;
Tejun Heoff2aeb12007-12-05 16:43:11 +0900481 for_each_sg(qc->sg, sg, qc->n_elem, si) {
Jeff Garzikcedc9a42005-10-05 07:13:30 -0400482 buf[idx++] = cpu_to_le32(sg_dma_address(sg));
483 buf[idx++] = cpu_to_le32(sg_dma_len(sg));
484 total_len += sg_dma_len(sg);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700485 }
486 buf[idx - 1] |= cpu_to_le32(ATA_PRD_EOT);
487 sgt_len = idx * 4;
488
489 /*
490 * Build ATA, host DMA packets
491 */
492 pdc20621_host_sg(&qc->tf, &pp->dimm_buf[0], portno, total_len);
493 pdc20621_host_pkt(&qc->tf, &pp->dimm_buf[0], portno);
494
495 pdc20621_ata_sg(&qc->tf, &pp->dimm_buf[0], portno, total_len);
496 i = pdc20621_ata_pkt(&qc->tf, qc->dev->devno, &pp->dimm_buf[0], portno);
497
498 if (qc->tf.flags & ATA_TFLAG_LBA48)
499 i = pdc_prep_lba48(&qc->tf, &pp->dimm_buf[0], i);
500 else
501 i = pdc_prep_lba28(&qc->tf, &pp->dimm_buf[0], i);
502
503 pdc_pkt_footer(&qc->tf, &pp->dimm_buf[0], i);
504
505 /* copy three S/G tables and two packets to DIMM MMIO window */
506 memcpy_toio(dimm_mmio + (portno * PDC_DIMM_WINDOW_STEP),
507 &pp->dimm_buf, PDC_DIMM_HEADER_SZ);
508 memcpy_toio(dimm_mmio + (portno * PDC_DIMM_WINDOW_STEP) +
509 PDC_DIMM_HOST_PRD,
510 &pp->dimm_buf[PDC_DIMM_HEADER_SZ], sgt_len);
511
512 /* force host FIFO dump */
513 writel(0x00000001, mmio + PDC_20621_GENERAL_CTL);
514
515 readl(dimm_mmio); /* MMIO PCI posting flush */
516
517 VPRINTK("ata pkt buf ofs %u, prd size %u, mmio copied\n", i, sgt_len);
518}
519
520static void pdc20621_nodata_prep(struct ata_queued_cmd *qc)
521{
522 struct ata_port *ap = qc->ap;
523 struct pdc_port_priv *pp = ap->private_data;
Tejun Heo0d5ff562007-02-01 15:06:36 +0900524 void __iomem *mmio = ap->host->iomap[PDC_MMIO_BAR];
525 void __iomem *dimm_mmio = ap->host->iomap[PDC_DIMM_BAR];
Linus Torvalds1da177e2005-04-16 15:20:36 -0700526 unsigned int portno = ap->port_no;
527 unsigned int i;
528
Tejun Heo44877b42007-02-21 01:06:51 +0900529 VPRINTK("ata%u: ENTER\n", ap->print_id);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700530
531 /* hard-code chip #0 */
532 mmio += PDC_CHIP0_OFS;
533
534 i = pdc20621_ata_pkt(&qc->tf, qc->dev->devno, &pp->dimm_buf[0], portno);
535
536 if (qc->tf.flags & ATA_TFLAG_LBA48)
537 i = pdc_prep_lba48(&qc->tf, &pp->dimm_buf[0], i);
538 else
539 i = pdc_prep_lba28(&qc->tf, &pp->dimm_buf[0], i);
540
541 pdc_pkt_footer(&qc->tf, &pp->dimm_buf[0], i);
542
543 /* copy three S/G tables and two packets to DIMM MMIO window */
544 memcpy_toio(dimm_mmio + (portno * PDC_DIMM_WINDOW_STEP),
545 &pp->dimm_buf, PDC_DIMM_HEADER_SZ);
546
547 /* force host FIFO dump */
548 writel(0x00000001, mmio + PDC_20621_GENERAL_CTL);
549
550 readl(dimm_mmio); /* MMIO PCI posting flush */
551
552 VPRINTK("ata pkt buf ofs %u, mmio copied\n", i);
553}
554
555static void pdc20621_qc_prep(struct ata_queued_cmd *qc)
556{
557 switch (qc->tf.protocol) {
558 case ATA_PROT_DMA:
559 pdc20621_dma_prep(qc);
560 break;
561 case ATA_PROT_NODATA:
562 pdc20621_nodata_prep(qc);
563 break;
564 default:
565 break;
566 }
567}
568
569static void __pdc20621_push_hdma(struct ata_queued_cmd *qc,
570 unsigned int seq,
571 u32 pkt_ofs)
572{
573 struct ata_port *ap = qc->ap;
Jeff Garzikcca39742006-08-24 03:19:22 -0400574 struct ata_host *host = ap->host;
Tejun Heo0d5ff562007-02-01 15:06:36 +0900575 void __iomem *mmio = host->iomap[PDC_MMIO_BAR];
Linus Torvalds1da177e2005-04-16 15:20:36 -0700576
577 /* hard-code chip #0 */
578 mmio += PDC_CHIP0_OFS;
579
580 writel(0x00000001, mmio + PDC_20621_SEQCTL + (seq * 4));
581 readl(mmio + PDC_20621_SEQCTL + (seq * 4)); /* flush */
582
583 writel(pkt_ofs, mmio + PDC_HDMA_PKT_SUBMIT);
584 readl(mmio + PDC_HDMA_PKT_SUBMIT); /* flush */
585}
586
587static void pdc20621_push_hdma(struct ata_queued_cmd *qc,
588 unsigned int seq,
589 u32 pkt_ofs)
590{
591 struct ata_port *ap = qc->ap;
Jeff Garzikcca39742006-08-24 03:19:22 -0400592 struct pdc_host_priv *pp = ap->host->private_data;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700593 unsigned int idx = pp->hdma_prod & PDC_HDMA_Q_MASK;
594
595 if (!pp->doing_hdma) {
596 __pdc20621_push_hdma(qc, seq, pkt_ofs);
597 pp->doing_hdma = 1;
598 return;
599 }
600
601 pp->hdma[idx].qc = qc;
602 pp->hdma[idx].seq = seq;
603 pp->hdma[idx].pkt_ofs = pkt_ofs;
604 pp->hdma_prod++;
605}
606
607static void pdc20621_pop_hdma(struct ata_queued_cmd *qc)
608{
609 struct ata_port *ap = qc->ap;
Jeff Garzikcca39742006-08-24 03:19:22 -0400610 struct pdc_host_priv *pp = ap->host->private_data;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700611 unsigned int idx = pp->hdma_cons & PDC_HDMA_Q_MASK;
612
613 /* if nothing on queue, we're done */
614 if (pp->hdma_prod == pp->hdma_cons) {
615 pp->doing_hdma = 0;
616 return;
617 }
618
619 __pdc20621_push_hdma(pp->hdma[idx].qc, pp->hdma[idx].seq,
620 pp->hdma[idx].pkt_ofs);
621 pp->hdma_cons++;
622}
623
624#ifdef ATA_VERBOSE_DEBUG
625static void pdc20621_dump_hdma(struct ata_queued_cmd *qc)
626{
627 struct ata_port *ap = qc->ap;
628 unsigned int port_no = ap->port_no;
Tejun Heo0d5ff562007-02-01 15:06:36 +0900629 void __iomem *dimm_mmio = ap->host->iomap[PDC_DIMM_BAR];
Linus Torvalds1da177e2005-04-16 15:20:36 -0700630
631 dimm_mmio += (port_no * PDC_DIMM_WINDOW_STEP);
632 dimm_mmio += PDC_DIMM_HOST_PKT;
633
634 printk(KERN_ERR "HDMA[0] == 0x%08X\n", readl(dimm_mmio));
635 printk(KERN_ERR "HDMA[1] == 0x%08X\n", readl(dimm_mmio + 4));
636 printk(KERN_ERR "HDMA[2] == 0x%08X\n", readl(dimm_mmio + 8));
637 printk(KERN_ERR "HDMA[3] == 0x%08X\n", readl(dimm_mmio + 12));
638}
639#else
640static inline void pdc20621_dump_hdma(struct ata_queued_cmd *qc) { }
641#endif /* ATA_VERBOSE_DEBUG */
642
643static void pdc20621_packet_start(struct ata_queued_cmd *qc)
644{
645 struct ata_port *ap = qc->ap;
Jeff Garzikcca39742006-08-24 03:19:22 -0400646 struct ata_host *host = ap->host;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700647 unsigned int port_no = ap->port_no;
Tejun Heo0d5ff562007-02-01 15:06:36 +0900648 void __iomem *mmio = host->iomap[PDC_MMIO_BAR];
Linus Torvalds1da177e2005-04-16 15:20:36 -0700649 unsigned int rw = (qc->tf.flags & ATA_TFLAG_WRITE);
650 u8 seq = (u8) (port_no + 1);
651 unsigned int port_ofs;
652
653 /* hard-code chip #0 */
654 mmio += PDC_CHIP0_OFS;
655
Tejun Heo44877b42007-02-21 01:06:51 +0900656 VPRINTK("ata%u: ENTER\n", ap->print_id);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700657
658 wmb(); /* flush PRD, pkt writes */
659
660 port_ofs = PDC_20621_DIMM_BASE + (PDC_DIMM_WINDOW_STEP * port_no);
661
662 /* if writing, we (1) DMA to DIMM, then (2) do ATA command */
663 if (rw && qc->tf.protocol == ATA_PROT_DMA) {
664 seq += 4;
665
666 pdc20621_dump_hdma(qc);
667 pdc20621_push_hdma(qc, seq, port_ofs + PDC_DIMM_HOST_PKT);
668 VPRINTK("queued ofs 0x%x (%u), seq %u\n",
669 port_ofs + PDC_DIMM_HOST_PKT,
670 port_ofs + PDC_DIMM_HOST_PKT,
671 seq);
672 } else {
673 writel(0x00000001, mmio + PDC_20621_SEQCTL + (seq * 4));
674 readl(mmio + PDC_20621_SEQCTL + (seq * 4)); /* flush */
675
676 writel(port_ofs + PDC_DIMM_ATA_PKT,
Tejun Heo0d5ff562007-02-01 15:06:36 +0900677 ap->ioaddr.cmd_addr + PDC_PKT_SUBMIT);
678 readl(ap->ioaddr.cmd_addr + PDC_PKT_SUBMIT);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700679 VPRINTK("submitted ofs 0x%x (%u), seq %u\n",
680 port_ofs + PDC_DIMM_ATA_PKT,
681 port_ofs + PDC_DIMM_ATA_PKT,
682 seq);
683 }
684}
685
Tejun Heo9363c382008-04-07 22:47:16 +0900686static unsigned int pdc20621_qc_issue(struct ata_queued_cmd *qc)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700687{
688 switch (qc->tf.protocol) {
689 case ATA_PROT_DMA:
690 case ATA_PROT_NODATA:
691 pdc20621_packet_start(qc);
692 return 0;
693
Tejun Heo0dc36882007-12-18 16:34:43 -0500694 case ATAPI_PROT_DMA:
Linus Torvalds1da177e2005-04-16 15:20:36 -0700695 BUG();
696 break;
697
698 default:
699 break;
700 }
701
Tejun Heo9363c382008-04-07 22:47:16 +0900702 return ata_sff_qc_issue(qc);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700703}
704
Jeff Garzik5796d1c2007-10-26 00:03:37 -0400705static inline unsigned int pdc20621_host_intr(struct ata_port *ap,
706 struct ata_queued_cmd *qc,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700707 unsigned int doing_hdma,
Jeff Garzikea6ba102005-08-30 05:18:18 -0400708 void __iomem *mmio)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700709{
710 unsigned int port_no = ap->port_no;
711 unsigned int port_ofs =
712 PDC_20621_DIMM_BASE + (PDC_DIMM_WINDOW_STEP * port_no);
713 u8 status;
714 unsigned int handled = 0;
715
716 VPRINTK("ENTER\n");
717
718 if ((qc->tf.protocol == ATA_PROT_DMA) && /* read */
719 (!(qc->tf.flags & ATA_TFLAG_WRITE))) {
720
721 /* step two - DMA from DIMM to host */
722 if (doing_hdma) {
Tejun Heo44877b42007-02-21 01:06:51 +0900723 VPRINTK("ata%u: read hdma, 0x%x 0x%x\n", ap->print_id,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700724 readl(mmio + 0x104), readl(mmio + PDC_HDMA_CTLSTAT));
725 /* get drive status; clear intr; complete txn */
Albert Leea22e2eb2005-12-05 15:38:02 +0800726 qc->err_mask |= ac_err_mask(ata_wait_idle(ap));
727 ata_qc_complete(qc);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700728 pdc20621_pop_hdma(qc);
729 }
730
731 /* step one - exec ATA command */
732 else {
733 u8 seq = (u8) (port_no + 1 + 4);
Tejun Heo44877b42007-02-21 01:06:51 +0900734 VPRINTK("ata%u: read ata, 0x%x 0x%x\n", ap->print_id,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700735 readl(mmio + 0x104), readl(mmio + PDC_HDMA_CTLSTAT));
736
737 /* submit hdma pkt */
738 pdc20621_dump_hdma(qc);
739 pdc20621_push_hdma(qc, seq,
740 port_ofs + PDC_DIMM_HOST_PKT);
741 }
742 handled = 1;
743
744 } else if (qc->tf.protocol == ATA_PROT_DMA) { /* write */
745
746 /* step one - DMA from host to DIMM */
747 if (doing_hdma) {
748 u8 seq = (u8) (port_no + 1);
Tejun Heo44877b42007-02-21 01:06:51 +0900749 VPRINTK("ata%u: write hdma, 0x%x 0x%x\n", ap->print_id,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700750 readl(mmio + 0x104), readl(mmio + PDC_HDMA_CTLSTAT));
751
752 /* submit ata pkt */
753 writel(0x00000001, mmio + PDC_20621_SEQCTL + (seq * 4));
754 readl(mmio + PDC_20621_SEQCTL + (seq * 4));
755 writel(port_ofs + PDC_DIMM_ATA_PKT,
Tejun Heo0d5ff562007-02-01 15:06:36 +0900756 ap->ioaddr.cmd_addr + PDC_PKT_SUBMIT);
757 readl(ap->ioaddr.cmd_addr + PDC_PKT_SUBMIT);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700758 }
759
760 /* step two - execute ATA command */
761 else {
Tejun Heo44877b42007-02-21 01:06:51 +0900762 VPRINTK("ata%u: write ata, 0x%x 0x%x\n", ap->print_id,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700763 readl(mmio + 0x104), readl(mmio + PDC_HDMA_CTLSTAT));
764 /* get drive status; clear intr; complete txn */
Albert Leea22e2eb2005-12-05 15:38:02 +0800765 qc->err_mask |= ac_err_mask(ata_wait_idle(ap));
766 ata_qc_complete(qc);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700767 pdc20621_pop_hdma(qc);
768 }
769 handled = 1;
770
771 /* command completion, but no data xfer */
772 } else if (qc->tf.protocol == ATA_PROT_NODATA) {
773
Tejun Heo9363c382008-04-07 22:47:16 +0900774 status = ata_sff_busy_wait(ap, ATA_BUSY | ATA_DRQ, 1000);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700775 DPRINTK("BUS_NODATA (drv_stat 0x%X)\n", status);
Albert Leea22e2eb2005-12-05 15:38:02 +0800776 qc->err_mask |= ac_err_mask(status);
777 ata_qc_complete(qc);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700778 handled = 1;
779
780 } else {
781 ap->stats.idle_irq++;
782 }
783
784 return handled;
785}
786
787static void pdc20621_irq_clear(struct ata_port *ap)
788{
Jeff Garzikcca39742006-08-24 03:19:22 -0400789 struct ata_host *host = ap->host;
Tejun Heo0d5ff562007-02-01 15:06:36 +0900790 void __iomem *mmio = host->iomap[PDC_MMIO_BAR];
Linus Torvalds1da177e2005-04-16 15:20:36 -0700791
792 mmio += PDC_CHIP0_OFS;
793
794 readl(mmio + PDC_20621_SEQMASK);
795}
796
Jeff Garzik5796d1c2007-10-26 00:03:37 -0400797static irqreturn_t pdc20621_interrupt(int irq, void *dev_instance)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700798{
Jeff Garzikcca39742006-08-24 03:19:22 -0400799 struct ata_host *host = dev_instance;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700800 struct ata_port *ap;
801 u32 mask = 0;
802 unsigned int i, tmp, port_no;
803 unsigned int handled = 0;
Jeff Garzikea6ba102005-08-30 05:18:18 -0400804 void __iomem *mmio_base;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700805
806 VPRINTK("ENTER\n");
807
Tejun Heo0d5ff562007-02-01 15:06:36 +0900808 if (!host || !host->iomap[PDC_MMIO_BAR]) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700809 VPRINTK("QUICK EXIT\n");
810 return IRQ_NONE;
811 }
812
Tejun Heo0d5ff562007-02-01 15:06:36 +0900813 mmio_base = host->iomap[PDC_MMIO_BAR];
Linus Torvalds1da177e2005-04-16 15:20:36 -0700814
815 /* reading should also clear interrupts */
816 mmio_base += PDC_CHIP0_OFS;
817 mask = readl(mmio_base + PDC_20621_SEQMASK);
818 VPRINTK("mask == 0x%x\n", mask);
819
820 if (mask == 0xffffffff) {
821 VPRINTK("QUICK EXIT 2\n");
822 return IRQ_NONE;
823 }
824 mask &= 0xffff; /* only 16 tags possible */
825 if (!mask) {
826 VPRINTK("QUICK EXIT 3\n");
827 return IRQ_NONE;
828 }
829
Jeff Garzik5796d1c2007-10-26 00:03:37 -0400830 spin_lock(&host->lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700831
Jeff Garzik5796d1c2007-10-26 00:03:37 -0400832 for (i = 1; i < 9; i++) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700833 port_no = i - 1;
834 if (port_no > 3)
835 port_no -= 4;
Jeff Garzikcca39742006-08-24 03:19:22 -0400836 if (port_no >= host->n_ports)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700837 ap = NULL;
838 else
Jeff Garzikcca39742006-08-24 03:19:22 -0400839 ap = host->ports[port_no];
Linus Torvalds1da177e2005-04-16 15:20:36 -0700840 tmp = mask & (1 << i);
841 VPRINTK("seq %u, port_no %u, ap %p, tmp %x\n", i, port_no, ap, tmp);
Tejun Heoc1389502005-08-22 14:59:24 +0900842 if (tmp && ap &&
Jeff Garzik029f5462006-04-02 10:30:40 -0400843 !(ap->flags & ATA_FLAG_DISABLED)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700844 struct ata_queued_cmd *qc;
845
Tejun Heo9af5c9c2007-08-06 18:36:22 +0900846 qc = ata_qc_from_tag(ap, ap->link.active_tag);
Albert Leee50362e2005-09-27 17:39:50 +0800847 if (qc && (!(qc->tf.flags & ATA_TFLAG_POLLING)))
Linus Torvalds1da177e2005-04-16 15:20:36 -0700848 handled += pdc20621_host_intr(ap, qc, (i > 4),
849 mmio_base);
850 }
851 }
852
Jeff Garzik5796d1c2007-10-26 00:03:37 -0400853 spin_unlock(&host->lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700854
855 VPRINTK("mask == 0x%x\n", mask);
856
857 VPRINTK("EXIT\n");
858
859 return IRQ_RETVAL(handled);
860}
861
862static void pdc_eng_timeout(struct ata_port *ap)
863{
864 u8 drv_stat;
Jeff Garzikcca39742006-08-24 03:19:22 -0400865 struct ata_host *host = ap->host;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700866 struct ata_queued_cmd *qc;
Jeff Garzikb8f61532005-08-25 22:01:20 -0400867 unsigned long flags;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700868
869 DPRINTK("ENTER\n");
870
Jeff Garzikcca39742006-08-24 03:19:22 -0400871 spin_lock_irqsave(&host->lock, flags);
Jeff Garzikb8f61532005-08-25 22:01:20 -0400872
Tejun Heo9af5c9c2007-08-06 18:36:22 +0900873 qc = ata_qc_from_tag(ap, ap->link.active_tag);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700874
Linus Torvalds1da177e2005-04-16 15:20:36 -0700875 switch (qc->tf.protocol) {
876 case ATA_PROT_DMA:
877 case ATA_PROT_NODATA:
Tejun Heof15a1da2006-05-15 20:57:56 +0900878 ata_port_printk(ap, KERN_ERR, "command timeout\n");
Albert Leea22e2eb2005-12-05 15:38:02 +0800879 qc->err_mask |= __ac_err_mask(ata_wait_idle(ap));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700880 break;
881
882 default:
Tejun Heo9363c382008-04-07 22:47:16 +0900883 drv_stat = ata_sff_busy_wait(ap, ATA_BUSY | ATA_DRQ, 1000);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700884
Tejun Heof15a1da2006-05-15 20:57:56 +0900885 ata_port_printk(ap, KERN_ERR,
886 "unknown timeout, cmd 0x%x stat 0x%x\n",
887 qc->tf.command, drv_stat);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700888
Albert Leea22e2eb2005-12-05 15:38:02 +0800889 qc->err_mask |= ac_err_mask(drv_stat);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700890 break;
891 }
892
Jeff Garzikcca39742006-08-24 03:19:22 -0400893 spin_unlock_irqrestore(&host->lock, flags);
Tejun Heof6379022006-02-10 15:10:48 +0900894 ata_eh_qc_complete(qc);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700895 DPRINTK("EXIT\n");
896}
897
Jeff Garzik057ace52005-10-22 14:27:05 -0400898static void pdc_tf_load_mmio(struct ata_port *ap, const struct ata_taskfile *tf)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700899{
Jeff Garzik5796d1c2007-10-26 00:03:37 -0400900 WARN_ON(tf->protocol == ATA_PROT_DMA ||
901 tf->protocol == ATA_PROT_NODATA);
Tejun Heo9363c382008-04-07 22:47:16 +0900902 ata_sff_tf_load(ap, tf);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700903}
904
905
Jeff Garzik057ace52005-10-22 14:27:05 -0400906static void pdc_exec_command_mmio(struct ata_port *ap, const struct ata_taskfile *tf)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700907{
Jeff Garzik5796d1c2007-10-26 00:03:37 -0400908 WARN_ON(tf->protocol == ATA_PROT_DMA ||
909 tf->protocol == ATA_PROT_NODATA);
Tejun Heo9363c382008-04-07 22:47:16 +0900910 ata_sff_exec_command(ap, tf);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700911}
912
913
Tejun Heo0d5ff562007-02-01 15:06:36 +0900914static void pdc_sata_setup_port(struct ata_ioports *port, void __iomem *base)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700915{
916 port->cmd_addr = base;
917 port->data_addr = base;
918 port->feature_addr =
919 port->error_addr = base + 0x4;
920 port->nsect_addr = base + 0x8;
921 port->lbal_addr = base + 0xc;
922 port->lbam_addr = base + 0x10;
923 port->lbah_addr = base + 0x14;
924 port->device_addr = base + 0x18;
925 port->command_addr =
926 port->status_addr = base + 0x1c;
927 port->altstatus_addr =
928 port->ctl_addr = base + 0x38;
929}
930
931
932#ifdef ATA_VERBOSE_DEBUG
Tejun Heo4447d352007-04-17 23:44:08 +0900933static void pdc20621_get_from_dimm(struct ata_host *host, void *psource,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700934 u32 offset, u32 size)
935{
936 u32 window_size;
937 u16 idx;
938 u8 page_mask;
939 long dist;
Tejun Heo4447d352007-04-17 23:44:08 +0900940 void __iomem *mmio = host->iomap[PDC_MMIO_BAR];
941 void __iomem *dimm_mmio = host->iomap[PDC_DIMM_BAR];
Linus Torvalds1da177e2005-04-16 15:20:36 -0700942
943 /* hard-code chip #0 */
944 mmio += PDC_CHIP0_OFS;
945
Jeff Garzik8a60a072005-07-31 13:13:24 -0400946 page_mask = 0x00;
Jeff Garzik5796d1c2007-10-26 00:03:37 -0400947 window_size = 0x2000 * 4; /* 32K byte uchar size */
Jeff Garzik8a60a072005-07-31 13:13:24 -0400948 idx = (u16) (offset / window_size);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700949
950 writel(0x01, mmio + PDC_GENERAL_CTLR);
951 readl(mmio + PDC_GENERAL_CTLR);
952 writel(((idx) << page_mask), mmio + PDC_DIMM_WINDOW_CTLR);
953 readl(mmio + PDC_DIMM_WINDOW_CTLR);
954
955 offset -= (idx * window_size);
956 idx++;
Jeff Garzik8a60a072005-07-31 13:13:24 -0400957 dist = ((long) (window_size - (offset + size))) >= 0 ? size :
Linus Torvalds1da177e2005-04-16 15:20:36 -0700958 (long) (window_size - offset);
Jeff Garzik8a60a072005-07-31 13:13:24 -0400959 memcpy_fromio((char *) psource, (char *) (dimm_mmio + offset / 4),
Linus Torvalds1da177e2005-04-16 15:20:36 -0700960 dist);
961
Jeff Garzik8a60a072005-07-31 13:13:24 -0400962 psource += dist;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700963 size -= dist;
964 for (; (long) size >= (long) window_size ;) {
965 writel(0x01, mmio + PDC_GENERAL_CTLR);
966 readl(mmio + PDC_GENERAL_CTLR);
967 writel(((idx) << page_mask), mmio + PDC_DIMM_WINDOW_CTLR);
968 readl(mmio + PDC_DIMM_WINDOW_CTLR);
Jeff Garzik8a60a072005-07-31 13:13:24 -0400969 memcpy_fromio((char *) psource, (char *) (dimm_mmio),
Linus Torvalds1da177e2005-04-16 15:20:36 -0700970 window_size / 4);
971 psource += window_size;
972 size -= window_size;
Jeff Garzik5796d1c2007-10-26 00:03:37 -0400973 idx++;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700974 }
975
976 if (size) {
977 writel(0x01, mmio + PDC_GENERAL_CTLR);
978 readl(mmio + PDC_GENERAL_CTLR);
979 writel(((idx) << page_mask), mmio + PDC_DIMM_WINDOW_CTLR);
980 readl(mmio + PDC_DIMM_WINDOW_CTLR);
Jeff Garzik8a60a072005-07-31 13:13:24 -0400981 memcpy_fromio((char *) psource, (char *) (dimm_mmio),
Linus Torvalds1da177e2005-04-16 15:20:36 -0700982 size / 4);
983 }
984}
985#endif
986
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 */
Tejun Heo4447d352007-04-17 23:44:08 +09001180 pdc20621_i2c_read(host, PDC_DIMM0_SPD_DEV_ADDRESS,
Linus Torvalds1da177e2005-04-16 15:20:36 -07001181 PDC_DIMM_SPD_TYPE, &spd0);
1182 if (spd0 == 0x02) {
1183 data |= (0x01 << 16);
Jeff Garzikb2d46b62007-05-27 22:58:54 -04001184 writel(data, mmio + PDC_SDRAM_CONTROL);
1185 readl(mmio + PDC_SDRAM_CONTROL);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001186 printk(KERN_ERR "Local DIMM ECC Enabled\n");
Jeff Garzik5796d1c2007-10-26 00:03:37 -04001187 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001188
Jeff Garzik5796d1c2007-10-26 00:03:37 -04001189 /* DIMM Initialization Select/Enable (bit 18/19) */
1190 data &= (~(1<<18));
1191 data |= (1<<19);
1192 writel(data, mmio + PDC_SDRAM_CONTROL);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001193
Jeff Garzik5796d1c2007-10-26 00:03:37 -04001194 error = 1;
1195 for (i = 1; i <= 10; i++) { /* polling ~5 secs */
Jeff Garzikb2d46b62007-05-27 22:58:54 -04001196 data = readl(mmio + PDC_SDRAM_CONTROL);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001197 if (!(data & (1<<19))) {
Jeff Garzik5796d1c2007-10-26 00:03:37 -04001198 error = 0;
1199 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001200 }
1201 msleep(i*100);
Jeff Garzik5796d1c2007-10-26 00:03:37 -04001202 }
1203 return error;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001204}
Jeff Garzik8a60a072005-07-31 13:13:24 -04001205
Linus Torvalds1da177e2005-04-16 15:20:36 -07001206
Tejun Heo4447d352007-04-17 23:44:08 +09001207static unsigned int pdc20621_dimm_init(struct ata_host *host)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001208{
Jeff Garzik8a60a072005-07-31 13:13:24 -04001209 int speed, size, length;
Jeff Garzik5796d1c2007-10-26 00:03:37 -04001210 u32 addr, spd0, pci_status;
1211 u32 tmp = 0;
1212 u32 time_period = 0;
1213 u32 tcount = 0;
1214 u32 ticks = 0;
1215 u32 clock = 0;
1216 u32 fparam = 0;
Tejun Heo4447d352007-04-17 23:44:08 +09001217 void __iomem *mmio = host->iomap[PDC_MMIO_BAR];
Linus Torvalds1da177e2005-04-16 15:20:36 -07001218
1219 /* hard-code chip #0 */
Jeff Garzik5796d1c2007-10-26 00:03:37 -04001220 mmio += PDC_CHIP0_OFS;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001221
1222 /* Initialize PLL based upon PCI Bus Frequency */
1223
1224 /* Initialize Time Period Register */
1225 writel(0xffffffff, mmio + PDC_TIME_PERIOD);
1226 time_period = readl(mmio + PDC_TIME_PERIOD);
1227 VPRINTK("Time Period Register (0x40): 0x%x\n", time_period);
1228
1229 /* Enable timer */
Jeff Garzikb2d46b62007-05-27 22:58:54 -04001230 writel(PDC_TIMER_DEFAULT, mmio + PDC_TIME_CONTROL);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001231 readl(mmio + PDC_TIME_CONTROL);
1232
1233 /* Wait 3 seconds */
1234 msleep(3000);
1235
Jeff Garzik8a60a072005-07-31 13:13:24 -04001236 /*
Linus Torvalds1da177e2005-04-16 15:20:36 -07001237 When timer is enabled, counter is decreased every internal
1238 clock cycle.
1239 */
1240
1241 tcount = readl(mmio + PDC_TIME_COUNTER);
1242 VPRINTK("Time Counter Register (0x44): 0x%x\n", tcount);
1243
Jeff Garzik8a60a072005-07-31 13:13:24 -04001244 /*
Linus Torvalds1da177e2005-04-16 15:20:36 -07001245 If SX4 is on PCI-X bus, after 3 seconds, the timer counter
1246 register should be >= (0xffffffff - 3x10^8).
1247 */
Jeff Garzikb4479162007-10-25 20:47:30 -04001248 if (tcount >= PCI_X_TCOUNT) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001249 ticks = (time_period - tcount);
1250 VPRINTK("Num counters 0x%x (%d)\n", ticks, ticks);
Jeff Garzik8a60a072005-07-31 13:13:24 -04001251
Linus Torvalds1da177e2005-04-16 15:20:36 -07001252 clock = (ticks / 300000);
1253 VPRINTK("10 * Internal clk = 0x%x (%d)\n", clock, clock);
Jeff Garzik8a60a072005-07-31 13:13:24 -04001254
Linus Torvalds1da177e2005-04-16 15:20:36 -07001255 clock = (clock * 33);
1256 VPRINTK("10 * Internal clk * 33 = 0x%x (%d)\n", clock, clock);
1257
1258 /* PLL F Param (bit 22:16) */
1259 fparam = (1400000 / clock) - 2;
1260 VPRINTK("PLL F Param: 0x%x (%d)\n", fparam, fparam);
Jeff Garzik8a60a072005-07-31 13:13:24 -04001261
Linus Torvalds1da177e2005-04-16 15:20:36 -07001262 /* OD param = 0x2 (bit 31:30), R param = 0x5 (bit 29:25) */
1263 pci_status = (0x8a001824 | (fparam << 16));
1264 } else
1265 pci_status = PCI_PLL_INIT;
1266
1267 /* Initialize PLL. */
1268 VPRINTK("pci_status: 0x%x\n", pci_status);
1269 writel(pci_status, mmio + PDC_CTL_STATUS);
1270 readl(mmio + PDC_CTL_STATUS);
1271
Jeff Garzik8a60a072005-07-31 13:13:24 -04001272 /*
Linus Torvalds1da177e2005-04-16 15:20:36 -07001273 Read SPD of DIMM by I2C interface,
1274 and program the DIMM Module Controller.
1275 */
Tejun Heo4447d352007-04-17 23:44:08 +09001276 if (!(speed = pdc20621_detect_dimm(host))) {
Jeff Garzik8a60a072005-07-31 13:13:24 -04001277 printk(KERN_ERR "Detect Local DIMM Fail\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001278 return 1; /* DIMM error */
Jeff Garzik5796d1c2007-10-26 00:03:37 -04001279 }
1280 VPRINTK("Local DIMM Speed = %d\n", speed);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001281
Jeff Garzik5796d1c2007-10-26 00:03:37 -04001282 /* Programming DIMM0 Module Control Register (index_CID0:80h) */
Tejun Heo4447d352007-04-17 23:44:08 +09001283 size = pdc20621_prog_dimm0(host);
Jeff Garzik5796d1c2007-10-26 00:03:37 -04001284 VPRINTK("Local DIMM Size = %dMB\n", size);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001285
Jeff Garzik5796d1c2007-10-26 00:03:37 -04001286 /* Programming DIMM Module Global Control Register (index_CID0:88h) */
Tejun Heo4447d352007-04-17 23:44:08 +09001287 if (pdc20621_prog_dimm_global(host)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001288 printk(KERN_ERR "Programming DIMM Module Global Control Register Fail\n");
1289 return 1;
Jeff Garzik5796d1c2007-10-26 00:03:37 -04001290 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001291
1292#ifdef ATA_VERBOSE_DEBUG
1293 {
Jeff Garzik5796d1c2007-10-26 00:03:37 -04001294 u8 test_parttern1[40] =
1295 {0x55,0xAA,'P','r','o','m','i','s','e',' ',
1296 'N','o','t',' ','Y','e','t',' ',
1297 'D','e','f','i','n','e','d',' ',
1298 '1','.','1','0',
1299 '9','8','0','3','1','6','1','2',0,0};
Linus Torvalds1da177e2005-04-16 15:20:36 -07001300 u8 test_parttern2[40] = {0};
1301
Jeff Garzik5796d1c2007-10-26 00:03:37 -04001302 pdc20621_put_to_dimm(host, test_parttern2, 0x10040, 40);
1303 pdc20621_put_to_dimm(host, test_parttern2, 0x40, 40);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001304
Jeff Garzik5796d1c2007-10-26 00:03:37 -04001305 pdc20621_put_to_dimm(host, test_parttern1, 0x10040, 40);
1306 pdc20621_get_from_dimm(host, test_parttern2, 0x40, 40);
Jeff Garzik8a60a072005-07-31 13:13:24 -04001307 printk(KERN_ERR "%x, %x, %s\n", test_parttern2[0],
Linus Torvalds1da177e2005-04-16 15:20:36 -07001308 test_parttern2[1], &(test_parttern2[2]));
Jeff Garzik5796d1c2007-10-26 00:03:37 -04001309 pdc20621_get_from_dimm(host, test_parttern2, 0x10040,
Linus Torvalds1da177e2005-04-16 15:20:36 -07001310 40);
Jeff Garzik8a60a072005-07-31 13:13:24 -04001311 printk(KERN_ERR "%x, %x, %s\n", test_parttern2[0],
Linus Torvalds1da177e2005-04-16 15:20:36 -07001312 test_parttern2[1], &(test_parttern2[2]));
1313
Jeff Garzik5796d1c2007-10-26 00:03:37 -04001314 pdc20621_put_to_dimm(host, test_parttern1, 0x40, 40);
1315 pdc20621_get_from_dimm(host, test_parttern2, 0x40, 40);
Jeff Garzik8a60a072005-07-31 13:13:24 -04001316 printk(KERN_ERR "%x, %x, %s\n", test_parttern2[0],
Linus Torvalds1da177e2005-04-16 15:20:36 -07001317 test_parttern2[1], &(test_parttern2[2]));
1318 }
1319#endif
1320
1321 /* ECC initiliazation. */
1322
Tejun Heo4447d352007-04-17 23:44:08 +09001323 pdc20621_i2c_read(host, PDC_DIMM0_SPD_DEV_ADDRESS,
Linus Torvalds1da177e2005-04-16 15:20:36 -07001324 PDC_DIMM_SPD_TYPE, &spd0);
1325 if (spd0 == 0x02) {
1326 VPRINTK("Start ECC initialization\n");
1327 addr = 0;
1328 length = size * 1024 * 1024;
1329 while (addr < length) {
Tejun Heo4447d352007-04-17 23:44:08 +09001330 pdc20621_put_to_dimm(host, (void *) &tmp, addr,
Linus Torvalds1da177e2005-04-16 15:20:36 -07001331 sizeof(u32));
1332 addr += sizeof(u32);
1333 }
1334 VPRINTK("Finish ECC initialization\n");
1335 }
1336 return 0;
1337}
1338
1339
Tejun Heo4447d352007-04-17 23:44:08 +09001340static void pdc_20621_init(struct ata_host *host)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001341{
1342 u32 tmp;
Tejun Heo4447d352007-04-17 23:44:08 +09001343 void __iomem *mmio = host->iomap[PDC_MMIO_BAR];
Linus Torvalds1da177e2005-04-16 15:20:36 -07001344
1345 /* hard-code chip #0 */
1346 mmio += PDC_CHIP0_OFS;
1347
1348 /*
1349 * Select page 0x40 for our 32k DIMM window
1350 */
1351 tmp = readl(mmio + PDC_20621_DIMM_WINDOW) & 0xffff0000;
1352 tmp |= PDC_PAGE_WINDOW; /* page 40h; arbitrarily selected */
1353 writel(tmp, mmio + PDC_20621_DIMM_WINDOW);
1354
1355 /*
1356 * Reset Host DMA
1357 */
1358 tmp = readl(mmio + PDC_HDMA_CTLSTAT);
1359 tmp |= PDC_RESET;
1360 writel(tmp, mmio + PDC_HDMA_CTLSTAT);
1361 readl(mmio + PDC_HDMA_CTLSTAT); /* flush */
1362
1363 udelay(10);
1364
1365 tmp = readl(mmio + PDC_HDMA_CTLSTAT);
1366 tmp &= ~PDC_RESET;
1367 writel(tmp, mmio + PDC_HDMA_CTLSTAT);
1368 readl(mmio + PDC_HDMA_CTLSTAT); /* flush */
1369}
1370
Jeff Garzik5796d1c2007-10-26 00:03:37 -04001371static int pdc_sata_init_one(struct pci_dev *pdev,
1372 const struct pci_device_id *ent)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001373{
1374 static int printed_version;
Tejun Heo4447d352007-04-17 23:44:08 +09001375 const struct ata_port_info *ppi[] =
1376 { &pdc_port_info[ent->driver_data], NULL };
1377 struct ata_host *host;
Tejun Heo24dc5f32007-01-20 16:00:28 +09001378 struct pdc_host_priv *hpriv;
Tejun Heocbcdd872007-08-18 13:14:55 +09001379 int i, rc;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001380
1381 if (!printed_version++)
Jeff Garzika9524a72005-10-30 14:39:11 -05001382 dev_printk(KERN_DEBUG, &pdev->dev, "version " DRV_VERSION "\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001383
Tejun Heo4447d352007-04-17 23:44:08 +09001384 /* allocate host */
1385 host = ata_host_alloc_pinfo(&pdev->dev, ppi, 4);
1386 hpriv = devm_kzalloc(&pdev->dev, sizeof(*hpriv), GFP_KERNEL);
1387 if (!host || !hpriv)
1388 return -ENOMEM;
1389
1390 host->private_data = hpriv;
1391
1392 /* acquire resources and fill host */
Tejun Heo24dc5f32007-01-20 16:00:28 +09001393 rc = pcim_enable_device(pdev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001394 if (rc)
1395 return rc;
1396
Tejun Heo0d5ff562007-02-01 15:06:36 +09001397 rc = pcim_iomap_regions(pdev, (1 << PDC_MMIO_BAR) | (1 << PDC_DIMM_BAR),
1398 DRV_NAME);
1399 if (rc == -EBUSY)
Tejun Heo24dc5f32007-01-20 16:00:28 +09001400 pcim_pin_device(pdev);
Tejun Heo0d5ff562007-02-01 15:06:36 +09001401 if (rc)
Tejun Heo24dc5f32007-01-20 16:00:28 +09001402 return rc;
Tejun Heo4447d352007-04-17 23:44:08 +09001403 host->iomap = pcim_iomap_table(pdev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001404
Tejun Heocbcdd872007-08-18 13:14:55 +09001405 for (i = 0; i < 4; i++) {
1406 struct ata_port *ap = host->ports[i];
1407 void __iomem *base = host->iomap[PDC_MMIO_BAR] + PDC_CHIP0_OFS;
1408 unsigned int offset = 0x200 + i * 0x80;
1409
1410 pdc_sata_setup_port(&ap->ioaddr, base + offset);
1411
1412 ata_port_pbar_desc(ap, PDC_MMIO_BAR, -1, "mmio");
1413 ata_port_pbar_desc(ap, PDC_DIMM_BAR, -1, "dimm");
1414 ata_port_pbar_desc(ap, PDC_MMIO_BAR, offset, "port");
1415 }
Tejun Heo4447d352007-04-17 23:44:08 +09001416
1417 /* configure and activate */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001418 rc = pci_set_dma_mask(pdev, ATA_DMA_MASK);
1419 if (rc)
Tejun Heo24dc5f32007-01-20 16:00:28 +09001420 return rc;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001421 rc = pci_set_consistent_dma_mask(pdev, ATA_DMA_MASK);
1422 if (rc)
Tejun Heo24dc5f32007-01-20 16:00:28 +09001423 return rc;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001424
Tejun Heo4447d352007-04-17 23:44:08 +09001425 if (pdc20621_dimm_init(host))
Tejun Heo24dc5f32007-01-20 16:00:28 +09001426 return -ENOMEM;
Tejun Heo4447d352007-04-17 23:44:08 +09001427 pdc_20621_init(host);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001428
1429 pci_set_master(pdev);
Tejun Heo4447d352007-04-17 23:44:08 +09001430 return ata_host_activate(host, pdev->irq, pdc20621_interrupt,
1431 IRQF_SHARED, &pdc_sata_sht);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001432}
1433
1434
1435static int __init pdc_sata_init(void)
1436{
Pavel Roskinb7887192006-08-10 18:13:18 +09001437 return pci_register_driver(&pdc_sata_pci_driver);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001438}
1439
1440
1441static void __exit pdc_sata_exit(void)
1442{
1443 pci_unregister_driver(&pdc_sata_pci_driver);
1444}
1445
1446
1447MODULE_AUTHOR("Jeff Garzik");
1448MODULE_DESCRIPTION("Promise SATA low-level driver");
1449MODULE_LICENSE("GPL");
1450MODULE_DEVICE_TABLE(pci, pdc_sata_pci_tbl);
1451MODULE_VERSION(DRV_VERSION);
1452
1453module_init(pdc_sata_init);
1454module_exit(pdc_sata_exit);