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