1 | // SPDX-License-Identifier: GPL-2.0-only |
2 | /* |
3 | * Driver for Western Digital WD7193, WD7197 and WD7296 SCSI cards |
4 | * Copyright 2013 Ondrej Zary |
5 | * |
6 | * Original driver by |
7 | * Aaron Dewell <dewell@woods.net> |
8 | * Gaerti <Juergen.Gaertner@mbox.si.uni-hannover.de> |
9 | * |
10 | * HW documentation available in book: |
11 | * |
12 | * SPIDER Command Protocol |
13 | * by Chandru M. Sippy |
14 | * SCSI Storage Products (MCP) |
15 | * Western Digital Corporation |
16 | * 09-15-95 |
17 | * |
18 | * http://web.archive.org/web/20070717175254/http://sun1.rrzn.uni-hannover.de/gaertner.juergen/wd719x/Linux/Docu/Spider/ |
19 | */ |
20 | |
21 | /* |
22 | * Driver workflow: |
23 | * 1. SCSI command is transformed to SCB (Spider Control Block) by the |
24 | * queuecommand function. |
25 | * 2. The address of the SCB is stored in a list to be able to access it, if |
26 | * something goes wrong. |
27 | * 3. The address of the SCB is written to the Controller, which loads the SCB |
28 | * via BM-DMA and processes it. |
29 | * 4. After it has finished, it generates an interrupt, and sets registers. |
30 | * |
31 | * flaws: |
32 | * - abort/reset functions |
33 | * |
34 | * ToDo: |
35 | * - tagged queueing |
36 | */ |
37 | |
38 | #include <linux/interrupt.h> |
39 | #include <linux/module.h> |
40 | #include <linux/delay.h> |
41 | #include <linux/pci.h> |
42 | #include <linux/firmware.h> |
43 | #include <linux/eeprom_93cx6.h> |
44 | #include <scsi/scsi_cmnd.h> |
45 | #include <scsi/scsi_device.h> |
46 | #include <scsi/scsi_host.h> |
47 | #include "wd719x.h" |
48 | |
49 | /* low-level register access */ |
50 | static inline u8 wd719x_readb(struct wd719x *wd, u8 reg) |
51 | { |
52 | return ioread8(wd->base + reg); |
53 | } |
54 | |
55 | static inline u32 wd719x_readl(struct wd719x *wd, u8 reg) |
56 | { |
57 | return ioread32(wd->base + reg); |
58 | } |
59 | |
60 | static inline void wd719x_writeb(struct wd719x *wd, u8 reg, u8 val) |
61 | { |
62 | iowrite8(val, wd->base + reg); |
63 | } |
64 | |
65 | static inline void wd719x_writew(struct wd719x *wd, u8 reg, u16 val) |
66 | { |
67 | iowrite16(val, wd->base + reg); |
68 | } |
69 | |
70 | static inline void wd719x_writel(struct wd719x *wd, u8 reg, u32 val) |
71 | { |
72 | iowrite32(val, wd->base + reg); |
73 | } |
74 | |
75 | /* wait until the command register is ready */ |
76 | static inline int wd719x_wait_ready(struct wd719x *wd) |
77 | { |
78 | int i = 0; |
79 | |
80 | do { |
81 | if (wd719x_readb(wd, WD719X_AMR_COMMAND) == WD719X_CMD_READY) |
82 | return 0; |
83 | udelay(1); |
84 | } while (i++ < WD719X_WAIT_FOR_CMD_READY); |
85 | |
86 | dev_err(&wd->pdev->dev, "command register is not ready: 0x%02x\n" , |
87 | wd719x_readb(wd, WD719X_AMR_COMMAND)); |
88 | |
89 | return -ETIMEDOUT; |
90 | } |
91 | |
92 | /* poll interrupt status register until command finishes */ |
93 | static inline int wd719x_wait_done(struct wd719x *wd, int timeout) |
94 | { |
95 | u8 status; |
96 | |
97 | while (timeout > 0) { |
98 | status = wd719x_readb(wd, WD719X_AMR_INT_STATUS); |
99 | if (status) |
100 | break; |
101 | timeout--; |
102 | udelay(1); |
103 | } |
104 | |
105 | if (timeout <= 0) { |
106 | dev_err(&wd->pdev->dev, "direct command timed out\n" ); |
107 | return -ETIMEDOUT; |
108 | } |
109 | |
110 | if (status != WD719X_INT_NOERRORS) { |
111 | u8 sue = wd719x_readb(wd, WD719X_AMR_SCB_ERROR); |
112 | /* we get this after wd719x_dev_reset, it's not an error */ |
113 | if (sue == WD719X_SUE_TERM) |
114 | return 0; |
115 | /* we get this after wd719x_bus_reset, it's not an error */ |
116 | if (sue == WD719X_SUE_RESET) |
117 | return 0; |
118 | dev_err(&wd->pdev->dev, "direct command failed, status 0x%02x, SUE 0x%02x\n" , |
119 | status, sue); |
120 | return -EIO; |
121 | } |
122 | |
123 | return 0; |
124 | } |
125 | |
126 | static int wd719x_direct_cmd(struct wd719x *wd, u8 opcode, u8 dev, u8 lun, |
127 | u8 tag, dma_addr_t data, int timeout) |
128 | { |
129 | int ret = 0; |
130 | |
131 | /* clear interrupt status register (allow command register to clear) */ |
132 | wd719x_writeb(wd, WD719X_AMR_INT_STATUS, WD719X_INT_NONE); |
133 | |
134 | /* Wait for the Command register to become free */ |
135 | if (wd719x_wait_ready(wd)) |
136 | return -ETIMEDOUT; |
137 | |
138 | /* disable interrupts except for RESET/ABORT (it breaks them) */ |
139 | if (opcode != WD719X_CMD_BUSRESET && opcode != WD719X_CMD_ABORT && |
140 | opcode != WD719X_CMD_ABORT_TAG && opcode != WD719X_CMD_RESET) |
141 | dev |= WD719X_DISABLE_INT; |
142 | wd719x_writeb(wd, WD719X_AMR_CMD_PARAM, val: dev); |
143 | wd719x_writeb(wd, WD719X_AMR_CMD_PARAM_2, val: lun); |
144 | wd719x_writeb(wd, WD719X_AMR_CMD_PARAM_3, val: tag); |
145 | if (data) |
146 | wd719x_writel(wd, WD719X_AMR_SCB_IN, val: data); |
147 | |
148 | /* clear interrupt status register again */ |
149 | wd719x_writeb(wd, WD719X_AMR_INT_STATUS, WD719X_INT_NONE); |
150 | |
151 | /* Now, write the command */ |
152 | wd719x_writeb(wd, WD719X_AMR_COMMAND, val: opcode); |
153 | |
154 | if (timeout) /* wait for the command to complete */ |
155 | ret = wd719x_wait_done(wd, timeout); |
156 | |
157 | /* clear interrupt status register (clean up) */ |
158 | if (opcode != WD719X_CMD_READ_FIRMVER) |
159 | wd719x_writeb(wd, WD719X_AMR_INT_STATUS, WD719X_INT_NONE); |
160 | |
161 | return ret; |
162 | } |
163 | |
164 | static void wd719x_destroy(struct wd719x *wd) |
165 | { |
166 | /* stop the RISC */ |
167 | if (wd719x_direct_cmd(wd, WD719X_CMD_SLEEP, dev: 0, lun: 0, tag: 0, data: 0, |
168 | WD719X_WAIT_FOR_RISC)) |
169 | dev_warn(&wd->pdev->dev, "RISC sleep command failed\n" ); |
170 | /* disable RISC */ |
171 | wd719x_writeb(wd, WD719X_PCI_MODE_SELECT, val: 0); |
172 | |
173 | WARN_ON_ONCE(!list_empty(&wd->active_scbs)); |
174 | |
175 | /* free internal buffers */ |
176 | dma_free_coherent(dev: &wd->pdev->dev, size: wd->fw_size, cpu_addr: wd->fw_virt, |
177 | dma_handle: wd->fw_phys); |
178 | wd->fw_virt = NULL; |
179 | dma_free_coherent(dev: &wd->pdev->dev, WD719X_HASH_TABLE_SIZE, cpu_addr: wd->hash_virt, |
180 | dma_handle: wd->hash_phys); |
181 | wd->hash_virt = NULL; |
182 | dma_free_coherent(dev: &wd->pdev->dev, size: sizeof(struct wd719x_host_param), |
183 | cpu_addr: wd->params, dma_handle: wd->params_phys); |
184 | wd->params = NULL; |
185 | free_irq(wd->pdev->irq, wd); |
186 | } |
187 | |
188 | /* finish a SCSI command, unmap buffers */ |
189 | static void wd719x_finish_cmd(struct wd719x_scb *scb, int result) |
190 | { |
191 | struct scsi_cmnd *cmd = scb->cmd; |
192 | struct wd719x *wd = shost_priv(shost: cmd->device->host); |
193 | |
194 | list_del(entry: &scb->list); |
195 | |
196 | dma_unmap_single(&wd->pdev->dev, scb->phys, |
197 | sizeof(struct wd719x_scb), DMA_BIDIRECTIONAL); |
198 | scsi_dma_unmap(cmd); |
199 | dma_unmap_single(&wd->pdev->dev, scb->dma_handle, |
200 | SCSI_SENSE_BUFFERSIZE, DMA_FROM_DEVICE); |
201 | |
202 | cmd->result = result << 16; |
203 | scsi_done(cmd); |
204 | } |
205 | |
206 | /* Build a SCB and send it to the card */ |
207 | static int wd719x_queuecommand(struct Scsi_Host *sh, struct scsi_cmnd *cmd) |
208 | { |
209 | int i, count_sg; |
210 | unsigned long flags; |
211 | struct wd719x_scb *scb = scsi_cmd_priv(cmd); |
212 | struct wd719x *wd = shost_priv(shost: sh); |
213 | |
214 | scb->cmd = cmd; |
215 | |
216 | scb->CDB_tag = 0; /* Tagged queueing not supported yet */ |
217 | scb->devid = cmd->device->id; |
218 | scb->lun = cmd->device->lun; |
219 | |
220 | /* copy the command */ |
221 | memcpy(scb->CDB, cmd->cmnd, cmd->cmd_len); |
222 | |
223 | /* map SCB */ |
224 | scb->phys = dma_map_single(&wd->pdev->dev, scb, sizeof(*scb), |
225 | DMA_BIDIRECTIONAL); |
226 | |
227 | if (dma_mapping_error(dev: &wd->pdev->dev, dma_addr: scb->phys)) |
228 | goto out_error; |
229 | |
230 | /* map sense buffer */ |
231 | scb->sense_buf_length = SCSI_SENSE_BUFFERSIZE; |
232 | scb->dma_handle = dma_map_single(&wd->pdev->dev, cmd->sense_buffer, |
233 | SCSI_SENSE_BUFFERSIZE, DMA_FROM_DEVICE); |
234 | if (dma_mapping_error(dev: &wd->pdev->dev, dma_addr: scb->dma_handle)) |
235 | goto out_unmap_scb; |
236 | scb->sense_buf = cpu_to_le32(scb->dma_handle); |
237 | |
238 | /* request autosense */ |
239 | scb->SCB_options |= WD719X_SCB_FLAGS_AUTO_REQUEST_SENSE; |
240 | |
241 | /* check direction */ |
242 | if (cmd->sc_data_direction == DMA_TO_DEVICE) |
243 | scb->SCB_options |= WD719X_SCB_FLAGS_CHECK_DIRECTION |
244 | | WD719X_SCB_FLAGS_PCI_TO_SCSI; |
245 | else if (cmd->sc_data_direction == DMA_FROM_DEVICE) |
246 | scb->SCB_options |= WD719X_SCB_FLAGS_CHECK_DIRECTION; |
247 | |
248 | /* Scather/gather */ |
249 | count_sg = scsi_dma_map(cmd); |
250 | if (count_sg < 0) |
251 | goto out_unmap_sense; |
252 | BUG_ON(count_sg > WD719X_SG); |
253 | |
254 | if (count_sg) { |
255 | struct scatterlist *sg; |
256 | |
257 | scb->data_length = cpu_to_le32(count_sg * |
258 | sizeof(struct wd719x_sglist)); |
259 | scb->data_p = cpu_to_le32(scb->phys + |
260 | offsetof(struct wd719x_scb, sg_list)); |
261 | |
262 | scsi_for_each_sg(cmd, sg, count_sg, i) { |
263 | scb->sg_list[i].ptr = cpu_to_le32(sg_dma_address(sg)); |
264 | scb->sg_list[i].length = cpu_to_le32(sg_dma_len(sg)); |
265 | } |
266 | scb->SCB_options |= WD719X_SCB_FLAGS_DO_SCATTER_GATHER; |
267 | } else { /* zero length */ |
268 | scb->data_length = 0; |
269 | scb->data_p = 0; |
270 | } |
271 | |
272 | spin_lock_irqsave(wd->sh->host_lock, flags); |
273 | |
274 | /* check if the Command register is free */ |
275 | if (wd719x_readb(wd, WD719X_AMR_COMMAND) != WD719X_CMD_READY) { |
276 | spin_unlock_irqrestore(lock: wd->sh->host_lock, flags); |
277 | return SCSI_MLQUEUE_HOST_BUSY; |
278 | } |
279 | |
280 | list_add(new: &scb->list, head: &wd->active_scbs); |
281 | |
282 | /* write pointer to the AMR */ |
283 | wd719x_writel(wd, WD719X_AMR_SCB_IN, val: scb->phys); |
284 | /* send SCB opcode */ |
285 | wd719x_writeb(wd, WD719X_AMR_COMMAND, WD719X_CMD_PROCESS_SCB); |
286 | |
287 | spin_unlock_irqrestore(lock: wd->sh->host_lock, flags); |
288 | return 0; |
289 | |
290 | out_unmap_sense: |
291 | dma_unmap_single(&wd->pdev->dev, scb->dma_handle, |
292 | SCSI_SENSE_BUFFERSIZE, DMA_FROM_DEVICE); |
293 | out_unmap_scb: |
294 | dma_unmap_single(&wd->pdev->dev, scb->phys, sizeof(*scb), |
295 | DMA_BIDIRECTIONAL); |
296 | out_error: |
297 | cmd->result = DID_ERROR << 16; |
298 | scsi_done(cmd); |
299 | return 0; |
300 | } |
301 | |
302 | static int wd719x_chip_init(struct wd719x *wd) |
303 | { |
304 | int i, ret; |
305 | u32 risc_init[3]; |
306 | const struct firmware *fw_wcs, *fw_risc; |
307 | const char fwname_wcs[] = "wd719x-wcs.bin" ; |
308 | const char fwname_risc[] = "wd719x-risc.bin" ; |
309 | |
310 | memset(wd->hash_virt, 0, WD719X_HASH_TABLE_SIZE); |
311 | |
312 | /* WCS (sequencer) firmware */ |
313 | ret = request_firmware(fw: &fw_wcs, name: fwname_wcs, device: &wd->pdev->dev); |
314 | if (ret) { |
315 | dev_err(&wd->pdev->dev, "Unable to load firmware %s: %d\n" , |
316 | fwname_wcs, ret); |
317 | return ret; |
318 | } |
319 | /* RISC firmware */ |
320 | ret = request_firmware(fw: &fw_risc, name: fwname_risc, device: &wd->pdev->dev); |
321 | if (ret) { |
322 | dev_err(&wd->pdev->dev, "Unable to load firmware %s: %d\n" , |
323 | fwname_risc, ret); |
324 | release_firmware(fw: fw_wcs); |
325 | return ret; |
326 | } |
327 | wd->fw_size = ALIGN(fw_wcs->size, 4) + fw_risc->size; |
328 | |
329 | if (!wd->fw_virt) |
330 | wd->fw_virt = dma_alloc_coherent(dev: &wd->pdev->dev, size: wd->fw_size, |
331 | dma_handle: &wd->fw_phys, GFP_KERNEL); |
332 | if (!wd->fw_virt) { |
333 | ret = -ENOMEM; |
334 | goto wd719x_init_end; |
335 | } |
336 | |
337 | /* make a fresh copy of WCS and RISC code */ |
338 | memcpy(wd->fw_virt, fw_wcs->data, fw_wcs->size); |
339 | memcpy(wd->fw_virt + ALIGN(fw_wcs->size, 4), fw_risc->data, |
340 | fw_risc->size); |
341 | |
342 | /* Reset the Spider Chip and adapter itself */ |
343 | wd719x_writeb(wd, WD719X_PCI_PORT_RESET, WD719X_PCI_RESET); |
344 | udelay(WD719X_WAIT_FOR_RISC); |
345 | /* Clear PIO mode bits set by BIOS */ |
346 | wd719x_writeb(wd, WD719X_AMR_CMD_PARAM, val: 0); |
347 | /* ensure RISC is not running */ |
348 | wd719x_writeb(wd, WD719X_PCI_MODE_SELECT, val: 0); |
349 | /* ensure command port is ready */ |
350 | wd719x_writeb(wd, WD719X_AMR_COMMAND, val: 0); |
351 | if (wd719x_wait_ready(wd)) { |
352 | ret = -ETIMEDOUT; |
353 | goto wd719x_init_end; |
354 | } |
355 | |
356 | /* Transfer the first 2K words of RISC code to kick start the uP */ |
357 | risc_init[0] = wd->fw_phys; /* WCS FW */ |
358 | risc_init[1] = wd->fw_phys + ALIGN(fw_wcs->size, 4); /* RISC FW */ |
359 | risc_init[2] = wd->hash_phys; /* hash table */ |
360 | |
361 | /* clear DMA status */ |
362 | wd719x_writeb(wd, WD719X_PCI_CHANNEL2_3STATUS, val: 0); |
363 | |
364 | /* address to read firmware from */ |
365 | wd719x_writel(wd, WD719X_PCI_EXTERNAL_ADDR, val: risc_init[1]); |
366 | /* base address to write firmware to (on card) */ |
367 | wd719x_writew(wd, WD719X_PCI_INTERNAL_ADDR, WD719X_PRAM_BASE_ADDR); |
368 | /* size: first 2K words */ |
369 | wd719x_writew(wd, WD719X_PCI_DMA_TRANSFER_SIZE, val: 2048 * 2); |
370 | /* start DMA */ |
371 | wd719x_writeb(wd, WD719X_PCI_CHANNEL2_3CMD, WD719X_START_CHANNEL2_3DMA); |
372 | |
373 | /* wait for DMA to complete */ |
374 | i = WD719X_WAIT_FOR_RISC; |
375 | while (i-- > 0) { |
376 | u8 status = wd719x_readb(wd, WD719X_PCI_CHANNEL2_3STATUS); |
377 | if (status == WD719X_START_CHANNEL2_3DONE) |
378 | break; |
379 | if (status == WD719X_START_CHANNEL2_3ABORT) { |
380 | dev_warn(&wd->pdev->dev, "RISC bootstrap failed: DMA aborted\n" ); |
381 | ret = -EIO; |
382 | goto wd719x_init_end; |
383 | } |
384 | udelay(1); |
385 | } |
386 | if (i < 1) { |
387 | dev_warn(&wd->pdev->dev, "RISC bootstrap failed: DMA timeout\n" ); |
388 | ret = -ETIMEDOUT; |
389 | goto wd719x_init_end; |
390 | } |
391 | |
392 | /* firmware is loaded, now initialize and wake up the RISC */ |
393 | /* write RISC initialization long words to Spider */ |
394 | wd719x_writel(wd, WD719X_AMR_SCB_IN, val: risc_init[0]); |
395 | wd719x_writel(wd, WD719X_AMR_SCB_IN + 4, val: risc_init[1]); |
396 | wd719x_writel(wd, WD719X_AMR_SCB_IN + 8, val: risc_init[2]); |
397 | |
398 | /* disable interrupts during initialization of RISC */ |
399 | wd719x_writeb(wd, WD719X_AMR_CMD_PARAM, WD719X_DISABLE_INT); |
400 | |
401 | /* issue INITIALIZE RISC comand */ |
402 | wd719x_writeb(wd, WD719X_AMR_COMMAND, WD719X_CMD_INIT_RISC); |
403 | /* enable advanced mode (wake up RISC) */ |
404 | wd719x_writeb(wd, WD719X_PCI_MODE_SELECT, WD719X_ENABLE_ADVANCE_MODE); |
405 | udelay(WD719X_WAIT_FOR_RISC); |
406 | |
407 | ret = wd719x_wait_done(wd, WD719X_WAIT_FOR_RISC); |
408 | /* clear interrupt status register */ |
409 | wd719x_writeb(wd, WD719X_AMR_INT_STATUS, WD719X_INT_NONE); |
410 | if (ret) { |
411 | dev_warn(&wd->pdev->dev, "Unable to initialize RISC\n" ); |
412 | goto wd719x_init_end; |
413 | } |
414 | /* RISC is up and running */ |
415 | |
416 | /* Read FW version from RISC */ |
417 | ret = wd719x_direct_cmd(wd, WD719X_CMD_READ_FIRMVER, dev: 0, lun: 0, tag: 0, data: 0, |
418 | WD719X_WAIT_FOR_RISC); |
419 | if (ret) { |
420 | dev_warn(&wd->pdev->dev, "Unable to read firmware version\n" ); |
421 | goto wd719x_init_end; |
422 | } |
423 | dev_info(&wd->pdev->dev, "RISC initialized with firmware version %.2x.%.2x\n" , |
424 | wd719x_readb(wd, WD719X_AMR_SCB_OUT + 1), |
425 | wd719x_readb(wd, WD719X_AMR_SCB_OUT)); |
426 | |
427 | /* RESET SCSI bus */ |
428 | ret = wd719x_direct_cmd(wd, WD719X_CMD_BUSRESET, dev: 0, lun: 0, tag: 0, data: 0, |
429 | WD719X_WAIT_FOR_SCSI_RESET); |
430 | if (ret) { |
431 | dev_warn(&wd->pdev->dev, "SCSI bus reset failed\n" ); |
432 | goto wd719x_init_end; |
433 | } |
434 | |
435 | /* use HostParameter structure to set Spider's Host Parameter Block */ |
436 | ret = wd719x_direct_cmd(wd, WD719X_CMD_SET_PARAM, dev: 0, |
437 | lun: sizeof(struct wd719x_host_param), tag: 0, |
438 | data: wd->params_phys, WD719X_WAIT_FOR_RISC); |
439 | if (ret) { |
440 | dev_warn(&wd->pdev->dev, "Failed to set HOST PARAMETERS\n" ); |
441 | goto wd719x_init_end; |
442 | } |
443 | |
444 | /* initiate SCAM (does nothing if disabled in BIOS) */ |
445 | /* bug?: we should pass a mask of static IDs which we don't have */ |
446 | ret = wd719x_direct_cmd(wd, WD719X_CMD_INIT_SCAM, dev: 0, lun: 0, tag: 0, data: 0, |
447 | WD719X_WAIT_FOR_SCSI_RESET); |
448 | if (ret) { |
449 | dev_warn(&wd->pdev->dev, "SCAM initialization failed\n" ); |
450 | goto wd719x_init_end; |
451 | } |
452 | |
453 | /* clear AMR_BIOS_SHARE_INT register */ |
454 | wd719x_writeb(wd, WD719X_AMR_BIOS_SHARE_INT, val: 0); |
455 | |
456 | wd719x_init_end: |
457 | release_firmware(fw: fw_wcs); |
458 | release_firmware(fw: fw_risc); |
459 | |
460 | return ret; |
461 | } |
462 | |
463 | static int wd719x_abort(struct scsi_cmnd *cmd) |
464 | { |
465 | int action, result; |
466 | unsigned long flags; |
467 | struct wd719x_scb *scb = scsi_cmd_priv(cmd); |
468 | struct wd719x *wd = shost_priv(shost: cmd->device->host); |
469 | struct device *dev = &wd->pdev->dev; |
470 | |
471 | dev_info(dev, "abort command, tag: %x\n" , scsi_cmd_to_rq(cmd)->tag); |
472 | |
473 | action = WD719X_CMD_ABORT; |
474 | |
475 | spin_lock_irqsave(wd->sh->host_lock, flags); |
476 | result = wd719x_direct_cmd(wd, opcode: action, dev: cmd->device->id, |
477 | lun: cmd->device->lun, tag: scsi_cmd_to_rq(scmd: cmd)->tag, |
478 | data: scb->phys, timeout: 0); |
479 | wd719x_finish_cmd(scb, result: DID_ABORT); |
480 | spin_unlock_irqrestore(lock: wd->sh->host_lock, flags); |
481 | if (result) |
482 | return FAILED; |
483 | |
484 | return SUCCESS; |
485 | } |
486 | |
487 | static int wd719x_reset(struct scsi_cmnd *cmd, u8 opcode, u8 device) |
488 | { |
489 | int result; |
490 | unsigned long flags; |
491 | struct wd719x *wd = shost_priv(shost: cmd->device->host); |
492 | struct wd719x_scb *scb, *tmp; |
493 | |
494 | dev_info(&wd->pdev->dev, "%s reset requested\n" , |
495 | (opcode == WD719X_CMD_BUSRESET) ? "bus" : "device" ); |
496 | |
497 | spin_lock_irqsave(wd->sh->host_lock, flags); |
498 | result = wd719x_direct_cmd(wd, opcode, dev: device, lun: 0, tag: 0, data: 0, |
499 | WD719X_WAIT_FOR_SCSI_RESET); |
500 | /* flush all SCBs (or all for a device if dev_reset) */ |
501 | list_for_each_entry_safe(scb, tmp, &wd->active_scbs, list) { |
502 | if (opcode == WD719X_CMD_BUSRESET || |
503 | scb->cmd->device->id == device) |
504 | wd719x_finish_cmd(scb, result: DID_RESET); |
505 | } |
506 | spin_unlock_irqrestore(lock: wd->sh->host_lock, flags); |
507 | if (result) |
508 | return FAILED; |
509 | |
510 | return SUCCESS; |
511 | } |
512 | |
513 | static int wd719x_dev_reset(struct scsi_cmnd *cmd) |
514 | { |
515 | return wd719x_reset(cmd, WD719X_CMD_RESET, device: cmd->device->id); |
516 | } |
517 | |
518 | static int wd719x_bus_reset(struct scsi_cmnd *cmd) |
519 | { |
520 | return wd719x_reset(cmd, WD719X_CMD_BUSRESET, device: 0); |
521 | } |
522 | |
523 | static int wd719x_host_reset(struct scsi_cmnd *cmd) |
524 | { |
525 | struct wd719x *wd = shost_priv(shost: cmd->device->host); |
526 | struct wd719x_scb *scb, *tmp; |
527 | unsigned long flags; |
528 | |
529 | dev_info(&wd->pdev->dev, "host reset requested\n" ); |
530 | spin_lock_irqsave(wd->sh->host_lock, flags); |
531 | /* stop the RISC */ |
532 | if (wd719x_direct_cmd(wd, WD719X_CMD_SLEEP, dev: 0, lun: 0, tag: 0, data: 0, |
533 | WD719X_WAIT_FOR_RISC)) |
534 | dev_warn(&wd->pdev->dev, "RISC sleep command failed\n" ); |
535 | /* disable RISC */ |
536 | wd719x_writeb(wd, WD719X_PCI_MODE_SELECT, val: 0); |
537 | |
538 | /* flush all SCBs */ |
539 | list_for_each_entry_safe(scb, tmp, &wd->active_scbs, list) |
540 | wd719x_finish_cmd(scb, result: DID_RESET); |
541 | spin_unlock_irqrestore(lock: wd->sh->host_lock, flags); |
542 | |
543 | /* Try to reinit the RISC */ |
544 | return wd719x_chip_init(wd) == 0 ? SUCCESS : FAILED; |
545 | } |
546 | |
547 | static int wd719x_biosparam(struct scsi_device *sdev, struct block_device *bdev, |
548 | sector_t capacity, int geom[]) |
549 | { |
550 | if (capacity >= 0x200000) { |
551 | geom[0] = 255; /* heads */ |
552 | geom[1] = 63; /* sectors */ |
553 | } else { |
554 | geom[0] = 64; /* heads */ |
555 | geom[1] = 32; /* sectors */ |
556 | } |
557 | geom[2] = sector_div(capacity, geom[0] * geom[1]); /* cylinders */ |
558 | |
559 | return 0; |
560 | } |
561 | |
562 | /* process a SCB-completion interrupt */ |
563 | static inline void wd719x_interrupt_SCB(struct wd719x *wd, |
564 | union wd719x_regs regs, |
565 | struct wd719x_scb *scb) |
566 | { |
567 | int result; |
568 | |
569 | /* now have to find result from card */ |
570 | switch (regs.bytes.SUE) { |
571 | case WD719X_SUE_NOERRORS: |
572 | result = DID_OK; |
573 | break; |
574 | case WD719X_SUE_REJECTED: |
575 | dev_err(&wd->pdev->dev, "command rejected\n" ); |
576 | result = DID_ERROR; |
577 | break; |
578 | case WD719X_SUE_SCBQFULL: |
579 | dev_err(&wd->pdev->dev, "SCB queue is full\n" ); |
580 | result = DID_ERROR; |
581 | break; |
582 | case WD719X_SUE_TERM: |
583 | dev_dbg(&wd->pdev->dev, "SCB terminated by direct command\n" ); |
584 | result = DID_ABORT; /* or DID_RESET? */ |
585 | break; |
586 | case WD719X_SUE_CHAN1ABORT: |
587 | case WD719X_SUE_CHAN23ABORT: |
588 | result = DID_ABORT; |
589 | dev_err(&wd->pdev->dev, "DMA abort\n" ); |
590 | break; |
591 | case WD719X_SUE_CHAN1PAR: |
592 | case WD719X_SUE_CHAN23PAR: |
593 | result = DID_PARITY; |
594 | dev_err(&wd->pdev->dev, "DMA parity error\n" ); |
595 | break; |
596 | case WD719X_SUE_TIMEOUT: |
597 | result = DID_TIME_OUT; |
598 | dev_dbg(&wd->pdev->dev, "selection timeout\n" ); |
599 | break; |
600 | case WD719X_SUE_RESET: |
601 | dev_dbg(&wd->pdev->dev, "bus reset occurred\n" ); |
602 | result = DID_RESET; |
603 | break; |
604 | case WD719X_SUE_BUSERROR: |
605 | dev_dbg(&wd->pdev->dev, "SCSI bus error\n" ); |
606 | result = DID_ERROR; |
607 | break; |
608 | case WD719X_SUE_WRONGWAY: |
609 | dev_err(&wd->pdev->dev, "wrong data transfer direction\n" ); |
610 | result = DID_ERROR; |
611 | break; |
612 | case WD719X_SUE_BADPHASE: |
613 | dev_err(&wd->pdev->dev, "invalid SCSI phase\n" ); |
614 | result = DID_ERROR; |
615 | break; |
616 | case WD719X_SUE_TOOLONG: |
617 | dev_err(&wd->pdev->dev, "record too long\n" ); |
618 | result = DID_ERROR; |
619 | break; |
620 | case WD719X_SUE_BUSFREE: |
621 | dev_err(&wd->pdev->dev, "unexpected bus free\n" ); |
622 | result = DID_NO_CONNECT; /* or DID_ERROR ???*/ |
623 | break; |
624 | case WD719X_SUE_ARSDONE: |
625 | dev_dbg(&wd->pdev->dev, "auto request sense\n" ); |
626 | if (regs.bytes.SCSI == 0) |
627 | result = DID_OK; |
628 | else |
629 | result = DID_PARITY; |
630 | break; |
631 | case WD719X_SUE_IGNORED: |
632 | dev_err(&wd->pdev->dev, "target id %d ignored command\n" , |
633 | scb->cmd->device->id); |
634 | result = DID_NO_CONNECT; |
635 | break; |
636 | case WD719X_SUE_WRONGTAGS: |
637 | dev_err(&wd->pdev->dev, "reversed tags\n" ); |
638 | result = DID_ERROR; |
639 | break; |
640 | case WD719X_SUE_BADTAGS: |
641 | dev_err(&wd->pdev->dev, "tag type not supported by target\n" ); |
642 | result = DID_ERROR; |
643 | break; |
644 | case WD719X_SUE_NOSCAMID: |
645 | dev_err(&wd->pdev->dev, "no SCAM soft ID available\n" ); |
646 | result = DID_ERROR; |
647 | break; |
648 | default: |
649 | dev_warn(&wd->pdev->dev, "unknown SUE error code: 0x%x\n" , |
650 | regs.bytes.SUE); |
651 | result = DID_ERROR; |
652 | break; |
653 | } |
654 | |
655 | wd719x_finish_cmd(scb, result); |
656 | } |
657 | |
658 | static irqreturn_t wd719x_interrupt(int irq, void *dev_id) |
659 | { |
660 | struct wd719x *wd = dev_id; |
661 | union wd719x_regs regs; |
662 | unsigned long flags; |
663 | u32 SCB_out; |
664 | |
665 | spin_lock_irqsave(wd->sh->host_lock, flags); |
666 | /* read SCB pointer back from card */ |
667 | SCB_out = wd719x_readl(wd, WD719X_AMR_SCB_OUT); |
668 | /* read all status info at once */ |
669 | regs.all = cpu_to_le32(wd719x_readl(wd, WD719X_AMR_OP_CODE)); |
670 | |
671 | switch (regs.bytes.INT) { |
672 | case WD719X_INT_NONE: |
673 | spin_unlock_irqrestore(lock: wd->sh->host_lock, flags); |
674 | return IRQ_NONE; |
675 | case WD719X_INT_LINKNOSTATUS: |
676 | dev_err(&wd->pdev->dev, "linked command completed with no status\n" ); |
677 | break; |
678 | case WD719X_INT_BADINT: |
679 | dev_err(&wd->pdev->dev, "unsolicited interrupt\n" ); |
680 | break; |
681 | case WD719X_INT_NOERRORS: |
682 | case WD719X_INT_LINKNOERRORS: |
683 | case WD719X_INT_ERRORSLOGGED: |
684 | case WD719X_INT_SPIDERFAILED: |
685 | /* was the cmd completed a direct or SCB command? */ |
686 | if (regs.bytes.OPC == WD719X_CMD_PROCESS_SCB) { |
687 | struct wd719x_scb *scb; |
688 | list_for_each_entry(scb, &wd->active_scbs, list) |
689 | if (SCB_out == scb->phys) |
690 | break; |
691 | if (SCB_out == scb->phys) |
692 | wd719x_interrupt_SCB(wd, regs, scb); |
693 | else |
694 | dev_err(&wd->pdev->dev, "card returned invalid SCB pointer\n" ); |
695 | } else |
696 | dev_dbg(&wd->pdev->dev, "direct command 0x%x completed\n" , |
697 | regs.bytes.OPC); |
698 | break; |
699 | case WD719X_INT_PIOREADY: |
700 | dev_err(&wd->pdev->dev, "card indicates PIO data ready but we never use PIO\n" ); |
701 | /* interrupt will not be cleared until all data is read */ |
702 | break; |
703 | default: |
704 | dev_err(&wd->pdev->dev, "unknown interrupt reason: %d\n" , |
705 | regs.bytes.INT); |
706 | |
707 | } |
708 | /* clear interrupt so another can happen */ |
709 | wd719x_writeb(wd, WD719X_AMR_INT_STATUS, WD719X_INT_NONE); |
710 | spin_unlock_irqrestore(lock: wd->sh->host_lock, flags); |
711 | |
712 | return IRQ_HANDLED; |
713 | } |
714 | |
715 | static void wd719x_eeprom_reg_read(struct eeprom_93cx6 *eeprom) |
716 | { |
717 | struct wd719x *wd = eeprom->data; |
718 | u8 reg = wd719x_readb(wd, WD719X_PCI_GPIO_DATA); |
719 | |
720 | eeprom->reg_data_out = reg & WD719X_EE_DO; |
721 | } |
722 | |
723 | static void wd719x_eeprom_reg_write(struct eeprom_93cx6 *eeprom) |
724 | { |
725 | struct wd719x *wd = eeprom->data; |
726 | u8 reg = 0; |
727 | |
728 | if (eeprom->reg_data_in) |
729 | reg |= WD719X_EE_DI; |
730 | if (eeprom->reg_data_clock) |
731 | reg |= WD719X_EE_CLK; |
732 | if (eeprom->reg_chip_select) |
733 | reg |= WD719X_EE_CS; |
734 | |
735 | wd719x_writeb(wd, WD719X_PCI_GPIO_DATA, val: reg); |
736 | } |
737 | |
738 | /* read config from EEPROM so it can be downloaded by the RISC on (re-)init */ |
739 | static void wd719x_read_eeprom(struct wd719x *wd) |
740 | { |
741 | struct eeprom_93cx6 eeprom; |
742 | u8 gpio; |
743 | struct wd719x_eeprom_header ; |
744 | |
745 | eeprom.data = wd; |
746 | eeprom.register_read = wd719x_eeprom_reg_read; |
747 | eeprom.register_write = wd719x_eeprom_reg_write; |
748 | eeprom.width = PCI_EEPROM_WIDTH_93C46; |
749 | |
750 | /* set all outputs to low */ |
751 | wd719x_writeb(wd, WD719X_PCI_GPIO_DATA, val: 0); |
752 | /* configure GPIO pins */ |
753 | gpio = wd719x_readb(wd, WD719X_PCI_GPIO_CONTROL); |
754 | /* GPIO outputs */ |
755 | gpio &= (~(WD719X_EE_CLK | WD719X_EE_DI | WD719X_EE_CS)); |
756 | /* GPIO input */ |
757 | gpio |= WD719X_EE_DO; |
758 | wd719x_writeb(wd, WD719X_PCI_GPIO_CONTROL, val: gpio); |
759 | |
760 | /* read EEPROM header */ |
761 | eeprom_93cx6_multireadb(eeprom: &eeprom, byte: 0, data: (u8 *)&header, bytes: sizeof(header)); |
762 | |
763 | if (header.sig1 == 'W' && header.sig2 == 'D') |
764 | eeprom_93cx6_multireadb(eeprom: &eeprom, byte: header.cfg_offset, |
765 | data: (u8 *)wd->params, |
766 | bytes: sizeof(struct wd719x_host_param)); |
767 | else { /* default EEPROM values */ |
768 | dev_warn(&wd->pdev->dev, "EEPROM signature is invalid (0x%02x 0x%02x), using default values\n" , |
769 | header.sig1, header.sig2); |
770 | wd->params->ch_1_th = 0x10; /* 16 DWs = 64 B */ |
771 | wd->params->scsi_conf = 0x4c; /* 48ma, spue, parity check */ |
772 | wd->params->own_scsi_id = 0x07; /* ID 7, SCAM disabled */ |
773 | wd->params->sel_timeout = 0x4d; /* 250 ms */ |
774 | wd->params->sleep_timer = 0x01; |
775 | wd->params->cdb_size = cpu_to_le16(0x5555); /* all 6 B */ |
776 | wd->params->scsi_pad = 0x1b; |
777 | if (wd->type == WD719X_TYPE_7193) /* narrow card - disable */ |
778 | wd->params->wide = cpu_to_le32(0x00000000); |
779 | else /* initiate & respond to WIDE messages */ |
780 | wd->params->wide = cpu_to_le32(0xffffffff); |
781 | wd->params->sync = cpu_to_le32(0xffffffff); |
782 | wd->params->soft_mask = 0x00; /* all disabled */ |
783 | wd->params->unsol_mask = 0x00; /* all disabled */ |
784 | } |
785 | /* disable TAGGED messages */ |
786 | wd->params->tag_en = cpu_to_le16(0x0000); |
787 | } |
788 | |
789 | /* Read card type from GPIO bits 1 and 3 */ |
790 | static enum wd719x_card_type wd719x_detect_type(struct wd719x *wd) |
791 | { |
792 | u8 card = wd719x_readb(wd, WD719X_PCI_GPIO_CONTROL); |
793 | |
794 | card |= WD719X_GPIO_ID_BITS; |
795 | wd719x_writeb(wd, WD719X_PCI_GPIO_CONTROL, val: card); |
796 | card = wd719x_readb(wd, WD719X_PCI_GPIO_DATA) & WD719X_GPIO_ID_BITS; |
797 | switch (card) { |
798 | case 0x08: |
799 | return WD719X_TYPE_7193; |
800 | case 0x02: |
801 | return WD719X_TYPE_7197; |
802 | case 0x00: |
803 | return WD719X_TYPE_7296; |
804 | default: |
805 | dev_warn(&wd->pdev->dev, "unknown card type 0x%x\n" , card); |
806 | return WD719X_TYPE_UNKNOWN; |
807 | } |
808 | } |
809 | |
810 | static int wd719x_board_found(struct Scsi_Host *sh) |
811 | { |
812 | struct wd719x *wd = shost_priv(shost: sh); |
813 | static const char * const card_types[] = { |
814 | "Unknown card" , "WD7193" , "WD7197" , "WD7296" |
815 | }; |
816 | int ret; |
817 | |
818 | INIT_LIST_HEAD(list: &wd->active_scbs); |
819 | |
820 | sh->base = pci_resource_start(wd->pdev, 0); |
821 | |
822 | wd->type = wd719x_detect_type(wd); |
823 | |
824 | wd->sh = sh; |
825 | sh->irq = wd->pdev->irq; |
826 | wd->fw_virt = NULL; |
827 | |
828 | /* memory area for host (EEPROM) parameters */ |
829 | wd->params = dma_alloc_coherent(dev: &wd->pdev->dev, |
830 | size: sizeof(struct wd719x_host_param), |
831 | dma_handle: &wd->params_phys, GFP_KERNEL); |
832 | if (!wd->params) { |
833 | dev_warn(&wd->pdev->dev, "unable to allocate parameter buffer\n" ); |
834 | return -ENOMEM; |
835 | } |
836 | |
837 | /* memory area for the RISC for hash table of outstanding requests */ |
838 | wd->hash_virt = dma_alloc_coherent(dev: &wd->pdev->dev, |
839 | WD719X_HASH_TABLE_SIZE, |
840 | dma_handle: &wd->hash_phys, GFP_KERNEL); |
841 | if (!wd->hash_virt) { |
842 | dev_warn(&wd->pdev->dev, "unable to allocate hash buffer\n" ); |
843 | ret = -ENOMEM; |
844 | goto fail_free_params; |
845 | } |
846 | |
847 | ret = request_irq(irq: wd->pdev->irq, handler: wd719x_interrupt, IRQF_SHARED, |
848 | name: "wd719x" , dev: wd); |
849 | if (ret) { |
850 | dev_warn(&wd->pdev->dev, "unable to assign IRQ %d\n" , |
851 | wd->pdev->irq); |
852 | goto fail_free_hash; |
853 | } |
854 | |
855 | /* read parameters from EEPROM */ |
856 | wd719x_read_eeprom(wd); |
857 | |
858 | ret = wd719x_chip_init(wd); |
859 | if (ret) |
860 | goto fail_free_irq; |
861 | |
862 | sh->this_id = wd->params->own_scsi_id & WD719X_EE_SCSI_ID_MASK; |
863 | |
864 | dev_info(&wd->pdev->dev, "%s at I/O 0x%lx, IRQ %u, SCSI ID %d\n" , |
865 | card_types[wd->type], sh->base, sh->irq, sh->this_id); |
866 | |
867 | return 0; |
868 | |
869 | fail_free_irq: |
870 | free_irq(wd->pdev->irq, wd); |
871 | fail_free_hash: |
872 | dma_free_coherent(dev: &wd->pdev->dev, WD719X_HASH_TABLE_SIZE, cpu_addr: wd->hash_virt, |
873 | dma_handle: wd->hash_phys); |
874 | fail_free_params: |
875 | dma_free_coherent(dev: &wd->pdev->dev, size: sizeof(struct wd719x_host_param), |
876 | cpu_addr: wd->params, dma_handle: wd->params_phys); |
877 | |
878 | return ret; |
879 | } |
880 | |
881 | static const struct scsi_host_template wd719x_template = { |
882 | .module = THIS_MODULE, |
883 | .name = "Western Digital 719x" , |
884 | .cmd_size = sizeof(struct wd719x_scb), |
885 | .queuecommand = wd719x_queuecommand, |
886 | .eh_abort_handler = wd719x_abort, |
887 | .eh_device_reset_handler = wd719x_dev_reset, |
888 | .eh_bus_reset_handler = wd719x_bus_reset, |
889 | .eh_host_reset_handler = wd719x_host_reset, |
890 | .bios_param = wd719x_biosparam, |
891 | .proc_name = "wd719x" , |
892 | .can_queue = 255, |
893 | .this_id = 7, |
894 | .sg_tablesize = WD719X_SG, |
895 | }; |
896 | |
897 | static int wd719x_pci_probe(struct pci_dev *pdev, const struct pci_device_id *d) |
898 | { |
899 | int err; |
900 | struct Scsi_Host *sh; |
901 | struct wd719x *wd; |
902 | |
903 | err = pci_enable_device(dev: pdev); |
904 | if (err) |
905 | goto fail; |
906 | |
907 | err = dma_set_mask(dev: &pdev->dev, DMA_BIT_MASK(32)); |
908 | if (err) { |
909 | dev_warn(&pdev->dev, "Unable to set 32-bit DMA mask\n" ); |
910 | goto disable_device; |
911 | } |
912 | |
913 | err = pci_request_regions(pdev, "wd719x" ); |
914 | if (err) |
915 | goto disable_device; |
916 | pci_set_master(dev: pdev); |
917 | |
918 | err = -ENODEV; |
919 | if (pci_resource_len(pdev, 0) == 0) |
920 | goto release_region; |
921 | |
922 | err = -ENOMEM; |
923 | sh = scsi_host_alloc(&wd719x_template, sizeof(struct wd719x)); |
924 | if (!sh) |
925 | goto release_region; |
926 | |
927 | wd = shost_priv(shost: sh); |
928 | wd->base = pci_iomap(dev: pdev, bar: 0, max: 0); |
929 | if (!wd->base) |
930 | goto free_host; |
931 | wd->pdev = pdev; |
932 | |
933 | err = wd719x_board_found(sh); |
934 | if (err) |
935 | goto unmap; |
936 | |
937 | err = scsi_add_host(host: sh, dev: &wd->pdev->dev); |
938 | if (err) |
939 | goto destroy; |
940 | |
941 | scsi_scan_host(sh); |
942 | |
943 | pci_set_drvdata(pdev, data: sh); |
944 | return 0; |
945 | |
946 | destroy: |
947 | wd719x_destroy(wd); |
948 | unmap: |
949 | pci_iounmap(dev: pdev, wd->base); |
950 | free_host: |
951 | scsi_host_put(t: sh); |
952 | release_region: |
953 | pci_release_regions(pdev); |
954 | disable_device: |
955 | pci_disable_device(dev: pdev); |
956 | fail: |
957 | return err; |
958 | } |
959 | |
960 | |
961 | static void wd719x_pci_remove(struct pci_dev *pdev) |
962 | { |
963 | struct Scsi_Host *sh = pci_get_drvdata(pdev); |
964 | struct wd719x *wd = shost_priv(shost: sh); |
965 | |
966 | scsi_remove_host(sh); |
967 | wd719x_destroy(wd); |
968 | pci_iounmap(dev: pdev, wd->base); |
969 | pci_release_regions(pdev); |
970 | pci_disable_device(dev: pdev); |
971 | |
972 | scsi_host_put(t: sh); |
973 | } |
974 | |
975 | static const struct pci_device_id wd719x_pci_table[] = { |
976 | { PCI_DEVICE(PCI_VENDOR_ID_WD, 0x3296) }, |
977 | {} |
978 | }; |
979 | |
980 | MODULE_DEVICE_TABLE(pci, wd719x_pci_table); |
981 | |
982 | static struct pci_driver wd719x_pci_driver = { |
983 | .name = "wd719x" , |
984 | .id_table = wd719x_pci_table, |
985 | .probe = wd719x_pci_probe, |
986 | .remove = wd719x_pci_remove, |
987 | }; |
988 | |
989 | module_pci_driver(wd719x_pci_driver); |
990 | |
991 | MODULE_DESCRIPTION("Western Digital WD7193/7197/7296 SCSI driver" ); |
992 | MODULE_AUTHOR("Ondrej Zary, Aaron Dewell, Juergen Gaertner" ); |
993 | MODULE_LICENSE("GPL" ); |
994 | MODULE_FIRMWARE("wd719x-wcs.bin" ); |
995 | MODULE_FIRMWARE("wd719x-risc.bin" ); |
996 | |