diff options
Diffstat (limited to 'drivers/scsi')
-rw-r--r-- | drivers/scsi/sgiwd93.c | 264 |
1 files changed, 120 insertions, 144 deletions
diff --git a/drivers/scsi/sgiwd93.c b/drivers/scsi/sgiwd93.c index a15752b37990..eef82758d047 100644 --- a/drivers/scsi/sgiwd93.c +++ b/drivers/scsi/sgiwd93.c | |||
@@ -6,87 +6,49 @@ | |||
6 | * Copyright (C) 1996 David S. Miller (dm@engr.sgi.com) | 6 | * Copyright (C) 1996 David S. Miller (dm@engr.sgi.com) |
7 | * Copyright (C) 1999 Andrew R. Baker (andrewb@uab.edu) | 7 | * Copyright (C) 1999 Andrew R. Baker (andrewb@uab.edu) |
8 | * Copyright (C) 2001 Florian Lohoff (flo@rfc822.org) | 8 | * Copyright (C) 2001 Florian Lohoff (flo@rfc822.org) |
9 | * Copyright (C) 2003 Ralf Baechle (ralf@linux-mips.org) | 9 | * Copyright (C) 2003, 07 Ralf Baechle (ralf@linux-mips.org) |
10 | * | 10 | * |
11 | * (In all truth, Jed Schimmel wrote all this code.) | 11 | * (In all truth, Jed Schimmel wrote all this code.) |
12 | */ | 12 | */ |
13 | #include <linux/init.h> | 13 | |
14 | #include <linux/interrupt.h> | 14 | #undef DEBUG |
15 | #include <linux/types.h> | 15 | |
16 | #include <linux/mm.h> | ||
17 | #include <linux/blkdev.h> | ||
18 | #include <linux/delay.h> | 16 | #include <linux/delay.h> |
19 | #include <linux/dma-mapping.h> | 17 | #include <linux/dma-mapping.h> |
18 | #include <linux/gfp.h> | ||
19 | #include <linux/interrupt.h> | ||
20 | #include <linux/init.h> | ||
21 | #include <linux/kernel.h> | ||
22 | #include <linux/types.h> | ||
23 | #include <linux/module.h> | ||
24 | #include <linux/platform_device.h> | ||
20 | #include <linux/spinlock.h> | 25 | #include <linux/spinlock.h> |
21 | 26 | ||
22 | #include <asm/page.h> | ||
23 | #include <asm/pgtable.h> | ||
24 | #include <asm/sgialib.h> | ||
25 | #include <asm/sgi/sgi.h> | ||
26 | #include <asm/sgi/mc.h> | ||
27 | #include <asm/sgi/hpc3.h> | 27 | #include <asm/sgi/hpc3.h> |
28 | #include <asm/sgi/ip22.h> | 28 | #include <asm/sgi/ip22.h> |
29 | #include <asm/irq.h> | 29 | #include <asm/sgi/wd.h> |
30 | #include <asm/io.h> | ||
31 | 30 | ||
32 | #include "scsi.h" | 31 | #include "scsi.h" |
33 | #include <scsi/scsi_host.h> | ||
34 | #include "wd33c93.h" | 32 | #include "wd33c93.h" |
35 | 33 | ||
36 | #include <linux/stat.h> | ||
37 | |||
38 | #if 0 | ||
39 | #define DPRINTK(args...) printk(args) | ||
40 | #else | ||
41 | #define DPRINTK(args...) | ||
42 | #endif | ||
43 | |||
44 | #define HDATA(ptr) ((struct ip22_hostdata *)((ptr)->hostdata)) | ||
45 | |||
46 | struct ip22_hostdata { | 34 | struct ip22_hostdata { |
47 | struct WD33C93_hostdata wh; | 35 | struct WD33C93_hostdata wh; |
48 | struct hpc_data { | 36 | struct hpc_data { |
49 | dma_addr_t dma; | 37 | dma_addr_t dma; |
50 | void * cpu; | 38 | void *cpu; |
51 | } hd; | 39 | } hd; |
52 | }; | 40 | }; |
53 | 41 | ||
42 | #define host_to_hostdata(host) ((struct ip22_hostdata *)((host)->hostdata)) | ||
43 | |||
54 | struct hpc_chunk { | 44 | struct hpc_chunk { |
55 | struct hpc_dma_desc desc; | 45 | struct hpc_dma_desc desc; |
56 | u32 _padding; /* align to quadword boundary */ | 46 | u32 _padding; /* align to quadword boundary */ |
57 | }; | 47 | }; |
58 | 48 | ||
59 | struct Scsi_Host *sgiwd93_host; | ||
60 | struct Scsi_Host *sgiwd93_host1; | ||
61 | |||
62 | /* Wuff wuff, wuff, wd33c93.c, wuff wuff, object oriented, bow wow. */ | ||
63 | static inline void write_wd33c93_count(const wd33c93_regs regs, | ||
64 | unsigned long value) | ||
65 | { | ||
66 | *regs.SASR = WD_TRANSFER_COUNT_MSB; | ||
67 | mb(); | ||
68 | *regs.SCMD = ((value >> 16) & 0xff); | ||
69 | *regs.SCMD = ((value >> 8) & 0xff); | ||
70 | *regs.SCMD = ((value >> 0) & 0xff); | ||
71 | mb(); | ||
72 | } | ||
73 | |||
74 | static inline unsigned long read_wd33c93_count(const wd33c93_regs regs) | ||
75 | { | ||
76 | unsigned long value; | ||
77 | |||
78 | *regs.SASR = WD_TRANSFER_COUNT_MSB; | ||
79 | mb(); | ||
80 | value = ((*regs.SCMD & 0xff) << 16); | ||
81 | value |= ((*regs.SCMD & 0xff) << 8); | ||
82 | value |= ((*regs.SCMD & 0xff) << 0); | ||
83 | mb(); | ||
84 | return value; | ||
85 | } | ||
86 | |||
87 | static irqreturn_t sgiwd93_intr(int irq, void *dev_id) | 49 | static irqreturn_t sgiwd93_intr(int irq, void *dev_id) |
88 | { | 50 | { |
89 | struct Scsi_Host * host = (struct Scsi_Host *) dev_id; | 51 | struct Scsi_Host * host = dev_id; |
90 | unsigned long flags; | 52 | unsigned long flags; |
91 | 53 | ||
92 | spin_lock_irqsave(host->host_lock, flags); | 54 | spin_lock_irqsave(host->host_lock, flags); |
@@ -131,12 +93,12 @@ void fill_hpc_entries(struct hpc_chunk *hcp, struct scsi_cmnd *cmd, int datainp) | |||
131 | 93 | ||
132 | static int dma_setup(struct scsi_cmnd *cmd, int datainp) | 94 | static int dma_setup(struct scsi_cmnd *cmd, int datainp) |
133 | { | 95 | { |
134 | struct ip22_hostdata *hdata = HDATA(cmd->device->host); | 96 | struct ip22_hostdata *hdata = host_to_hostdata(cmd->device->host); |
135 | struct hpc3_scsiregs *hregs = | 97 | struct hpc3_scsiregs *hregs = |
136 | (struct hpc3_scsiregs *) cmd->device->host->base; | 98 | (struct hpc3_scsiregs *) cmd->device->host->base; |
137 | struct hpc_chunk *hcp = (struct hpc_chunk *) hdata->hd.cpu; | 99 | struct hpc_chunk *hcp = (struct hpc_chunk *) hdata->hd.cpu; |
138 | 100 | ||
139 | DPRINTK("dma_setup: datainp<%d> hcp<%p> ", datainp, hcp); | 101 | pr_debug("dma_setup: datainp<%d> hcp<%p> ", datainp, hcp); |
140 | 102 | ||
141 | hdata->wh.dma_dir = datainp; | 103 | hdata->wh.dma_dir = datainp; |
142 | 104 | ||
@@ -151,7 +113,7 @@ static int dma_setup(struct scsi_cmnd *cmd, int datainp) | |||
151 | 113 | ||
152 | fill_hpc_entries(hcp, cmd, datainp); | 114 | fill_hpc_entries(hcp, cmd, datainp); |
153 | 115 | ||
154 | DPRINTK(" HPCGO\n"); | 116 | pr_debug(" HPCGO\n"); |
155 | 117 | ||
156 | /* Start up the HPC. */ | 118 | /* Start up the HPC. */ |
157 | hregs->ndptr = hdata->hd.dma; | 119 | hregs->ndptr = hdata->hd.dma; |
@@ -166,7 +128,7 @@ static int dma_setup(struct scsi_cmnd *cmd, int datainp) | |||
166 | static void dma_stop(struct Scsi_Host *instance, struct scsi_cmnd *SCpnt, | 128 | static void dma_stop(struct Scsi_Host *instance, struct scsi_cmnd *SCpnt, |
167 | int status) | 129 | int status) |
168 | { | 130 | { |
169 | struct ip22_hostdata *hdata = HDATA(instance); | 131 | struct ip22_hostdata *hdata = host_to_hostdata(instance); |
170 | struct hpc3_scsiregs *hregs; | 132 | struct hpc3_scsiregs *hregs; |
171 | 133 | ||
172 | if (!SCpnt) | 134 | if (!SCpnt) |
@@ -174,7 +136,7 @@ static void dma_stop(struct Scsi_Host *instance, struct scsi_cmnd *SCpnt, | |||
174 | 136 | ||
175 | hregs = (struct hpc3_scsiregs *) SCpnt->device->host->base; | 137 | hregs = (struct hpc3_scsiregs *) SCpnt->device->host->base; |
176 | 138 | ||
177 | DPRINTK("dma_stop: status<%d> ", status); | 139 | pr_debug("dma_stop: status<%d> ", status); |
178 | 140 | ||
179 | /* First stop the HPC and flush it's FIFO. */ | 141 | /* First stop the HPC and flush it's FIFO. */ |
180 | if (hdata->wh.dma_dir) { | 142 | if (hdata->wh.dma_dir) { |
@@ -186,7 +148,7 @@ static void dma_stop(struct Scsi_Host *instance, struct scsi_cmnd *SCpnt, | |||
186 | dma_unmap_single(NULL, SCpnt->SCp.dma_handle, SCpnt->SCp.this_residual, | 148 | dma_unmap_single(NULL, SCpnt->SCp.dma_handle, SCpnt->SCp.this_residual, |
187 | SCpnt->sc_data_direction); | 149 | SCpnt->sc_data_direction); |
188 | 150 | ||
189 | DPRINTK("\n"); | 151 | pr_debug("\n"); |
190 | } | 152 | } |
191 | 153 | ||
192 | void sgiwd93_reset(unsigned long base) | 154 | void sgiwd93_reset(unsigned long base) |
@@ -216,29 +178,71 @@ static inline void init_hpc_chain(struct hpc_data *hd) | |||
216 | hcp->desc.pnext = hd->dma; | 178 | hcp->desc.pnext = hd->dma; |
217 | } | 179 | } |
218 | 180 | ||
219 | static struct Scsi_Host * __init sgiwd93_setup_scsi( | 181 | static int sgiwd93_bus_reset(struct scsi_cmnd *cmd) |
220 | struct scsi_host_template *SGIblows, int unit, int irq, | 182 | { |
221 | struct hpc3_scsiregs *hregs, unsigned char *wdregs) | 183 | /* FIXME perform bus-specific reset */ |
184 | |||
185 | /* FIXME 2: kill this function, and let midlayer fallback | ||
186 | to the same result, calling wd33c93_host_reset() */ | ||
187 | |||
188 | spin_lock_irq(cmd->device->host->host_lock); | ||
189 | wd33c93_host_reset(cmd); | ||
190 | spin_unlock_irq(cmd->device->host->host_lock); | ||
191 | |||
192 | return SUCCESS; | ||
193 | } | ||
194 | |||
195 | /* | ||
196 | * Kludge alert - the SCSI code calls the abort and reset method with int | ||
197 | * arguments not with pointers. So this is going to blow up beautyfully | ||
198 | * on 64-bit systems with memory outside the compat address spaces. | ||
199 | */ | ||
200 | static struct scsi_host_template sgiwd93_template = { | ||
201 | .module = THIS_MODULE, | ||
202 | .proc_name = "SGIWD93", | ||
203 | .name = "SGI WD93", | ||
204 | .queuecommand = wd33c93_queuecommand, | ||
205 | .eh_abort_handler = wd33c93_abort, | ||
206 | .eh_bus_reset_handler = sgiwd93_bus_reset, | ||
207 | .eh_host_reset_handler = wd33c93_host_reset, | ||
208 | .can_queue = 16, | ||
209 | .this_id = 7, | ||
210 | .sg_tablesize = SG_ALL, | ||
211 | .cmd_per_lun = 8, | ||
212 | .use_clustering = DISABLE_CLUSTERING, | ||
213 | }; | ||
214 | |||
215 | static int __init sgiwd93_probe(struct platform_device *pdev) | ||
222 | { | 216 | { |
217 | struct sgiwd93_platform_data *pd = pdev->dev.platform_data; | ||
218 | unsigned char *wdregs = pd->wdregs; | ||
219 | struct hpc3_scsiregs *hregs = pd->hregs; | ||
223 | struct ip22_hostdata *hdata; | 220 | struct ip22_hostdata *hdata; |
224 | struct Scsi_Host *host; | 221 | struct Scsi_Host *host; |
225 | wd33c93_regs regs; | 222 | wd33c93_regs regs; |
226 | 223 | unsigned int unit = pd->unit; | |
227 | host = scsi_register(SGIblows, sizeof(struct ip22_hostdata)); | 224 | unsigned int irq = pd->irq; |
228 | if (!host) | 225 | int err; |
229 | return NULL; | 226 | |
227 | host = scsi_host_alloc(&sgiwd93_template, sizeof(struct ip22_hostdata)); | ||
228 | if (!host) { | ||
229 | err = -ENOMEM; | ||
230 | goto out; | ||
231 | } | ||
230 | 232 | ||
231 | host->base = (unsigned long) hregs; | 233 | host->base = (unsigned long) hregs; |
232 | host->irq = irq; | 234 | host->irq = irq; |
233 | 235 | ||
234 | hdata = HDATA(host); | 236 | hdata = host_to_hostdata(host); |
235 | hdata->hd.cpu = dma_alloc_coherent(NULL, PAGE_SIZE, &hdata->hd.dma, | 237 | hdata->hd.cpu = dma_alloc_coherent(&pdev->dev, PAGE_SIZE, |
236 | GFP_KERNEL); | 238 | &hdata->hd.dma, GFP_KERNEL); |
237 | if (!hdata->hd.cpu) { | 239 | if (!hdata->hd.cpu) { |
238 | printk(KERN_WARNING "sgiwd93: Could not allocate memory for " | 240 | printk(KERN_WARNING "sgiwd93: Could not allocate memory for " |
239 | "host %d buffer.\n", unit); | 241 | "host %d buffer.\n", unit); |
240 | goto out_unregister; | 242 | err = -ENOMEM; |
243 | goto out_put; | ||
241 | } | 244 | } |
245 | |||
242 | init_hpc_chain(&hdata->hd); | 246 | init_hpc_chain(&hdata->hd); |
243 | 247 | ||
244 | regs.SASR = wdregs + 3; | 248 | regs.SASR = wdregs + 3; |
@@ -249,95 +253,67 @@ static struct Scsi_Host * __init sgiwd93_setup_scsi( | |||
249 | if (hdata->wh.no_sync == 0xff) | 253 | if (hdata->wh.no_sync == 0xff) |
250 | hdata->wh.no_sync = 0; | 254 | hdata->wh.no_sync = 0; |
251 | 255 | ||
252 | if (request_irq(irq, sgiwd93_intr, 0, "SGI WD93", (void *) host)) { | 256 | err = request_irq(irq, sgiwd93_intr, 0, "SGI WD93", host); |
257 | if (err) { | ||
253 | printk(KERN_WARNING "sgiwd93: Could not register irq %d " | 258 | printk(KERN_WARNING "sgiwd93: Could not register irq %d " |
254 | "for host %d.\n", irq, unit); | 259 | "for host %d.\n", irq, unit); |
255 | goto out_free; | 260 | goto out_free; |
256 | } | 261 | } |
257 | return host; | ||
258 | 262 | ||
259 | out_free: | 263 | platform_set_drvdata(pdev, host); |
260 | dma_free_coherent(NULL, PAGE_SIZE, hdata->hd.cpu, hdata->hd.dma); | ||
261 | wd33c93_release(); | ||
262 | 264 | ||
263 | out_unregister: | 265 | err = scsi_add_host(host, NULL); |
264 | scsi_unregister(host); | 266 | if (err) |
267 | goto out_irq; | ||
265 | 268 | ||
266 | return NULL; | 269 | scsi_scan_host(host); |
267 | } | ||
268 | |||
269 | static int __init sgiwd93_detect(struct scsi_host_template *SGIblows) | ||
270 | { | ||
271 | int found = 0; | ||
272 | |||
273 | SGIblows->proc_name = "SGIWD93"; | ||
274 | sgiwd93_host = sgiwd93_setup_scsi(SGIblows, 0, SGI_WD93_0_IRQ, | ||
275 | &hpc3c0->scsi_chan0, | ||
276 | (unsigned char *)hpc3c0->scsi0_ext); | ||
277 | if (sgiwd93_host) | ||
278 | found++; | ||
279 | |||
280 | /* Set up second controller on the Indigo2 */ | ||
281 | if (ip22_is_fullhouse()) { | ||
282 | sgiwd93_host1 = sgiwd93_setup_scsi(SGIblows, 1, SGI_WD93_1_IRQ, | ||
283 | &hpc3c0->scsi_chan1, | ||
284 | (unsigned char *)hpc3c0->scsi1_ext); | ||
285 | if (sgiwd93_host1) | ||
286 | found++; | ||
287 | } | ||
288 | |||
289 | return found; | ||
290 | } | ||
291 | 270 | ||
292 | static int sgiwd93_release(struct Scsi_Host *instance) | 271 | return 0; |
293 | { | ||
294 | struct ip22_hostdata *hdata = HDATA(instance); | ||
295 | int irq = 0; | ||
296 | |||
297 | if (sgiwd93_host && sgiwd93_host == instance) | ||
298 | irq = SGI_WD93_0_IRQ; | ||
299 | else if (sgiwd93_host1 && sgiwd93_host1 == instance) | ||
300 | irq = SGI_WD93_1_IRQ; | ||
301 | 272 | ||
302 | free_irq(irq, sgiwd93_intr); | 273 | out_irq: |
274 | free_irq(irq, host); | ||
275 | out_free: | ||
303 | dma_free_coherent(NULL, PAGE_SIZE, hdata->hd.cpu, hdata->hd.dma); | 276 | dma_free_coherent(NULL, PAGE_SIZE, hdata->hd.cpu, hdata->hd.dma); |
304 | wd33c93_release(); | 277 | out_put: |
278 | scsi_host_put(host); | ||
279 | out: | ||
305 | 280 | ||
306 | return 1; | 281 | return err; |
307 | } | 282 | } |
308 | 283 | ||
309 | static int sgiwd93_bus_reset(struct scsi_cmnd *cmd) | 284 | static void __exit sgiwd93_remove(struct platform_device *pdev) |
310 | { | 285 | { |
311 | /* FIXME perform bus-specific reset */ | 286 | struct Scsi_Host *host = platform_get_drvdata(pdev); |
287 | struct ip22_hostdata *hdata = (struct ip22_hostdata *) host->hostdata; | ||
288 | struct sgiwd93_platform_data *pd = pdev->dev.platform_data; | ||
289 | |||
290 | scsi_remove_host(host); | ||
291 | free_irq(pd->irq, host); | ||
292 | dma_free_coherent(&pdev->dev, PAGE_SIZE, hdata->hd.cpu, hdata->hd.dma); | ||
293 | scsi_host_put(host); | ||
294 | } | ||
312 | 295 | ||
313 | /* FIXME 2: kill this function, and let midlayer fallback | 296 | static struct platform_driver sgiwd93_driver = { |
314 | to the same result, calling wd33c93_host_reset() */ | 297 | .probe = sgiwd93_probe, |
298 | .remove = __devexit_p(sgiwd93_remove), | ||
299 | .driver = { | ||
300 | .name = "sgiwd93" | ||
301 | } | ||
302 | }; | ||
315 | 303 | ||
316 | spin_lock_irq(cmd->device->host->host_lock); | 304 | static int __init sgiwd93_module_init(void) |
317 | wd33c93_host_reset(cmd); | 305 | { |
318 | spin_unlock_irq(cmd->device->host->host_lock); | 306 | return platform_driver_register(&sgiwd93_driver); |
307 | } | ||
319 | 308 | ||
320 | return SUCCESS; | 309 | static void __exit sgiwd93_module_exit(void) |
310 | { | ||
311 | return platform_driver_unregister(&sgiwd93_driver); | ||
321 | } | 312 | } |
322 | 313 | ||
323 | /* | 314 | module_init(sgiwd93_module_init); |
324 | * Kludge alert - the SCSI code calls the abort and reset method with int | 315 | module_exit(sgiwd93_module_exit); |
325 | * arguments not with pointers. So this is going to blow up beautyfully | 316 | |
326 | * on 64-bit systems with memory outside the compat address spaces. | 317 | MODULE_DESCRIPTION("SGI WD33C93 driver"); |
327 | */ | 318 | MODULE_AUTHOR("Ralf Baechle <ralf@linux-mips.org>"); |
328 | static struct scsi_host_template driver_template = { | 319 | MODULE_LICENSE("GPL"); |
329 | .proc_name = "SGIWD93", | ||
330 | .name = "SGI WD93", | ||
331 | .detect = sgiwd93_detect, | ||
332 | .release = sgiwd93_release, | ||
333 | .queuecommand = wd33c93_queuecommand, | ||
334 | .eh_abort_handler = wd33c93_abort, | ||
335 | .eh_bus_reset_handler = sgiwd93_bus_reset, | ||
336 | .eh_host_reset_handler = wd33c93_host_reset, | ||
337 | .can_queue = 16, | ||
338 | .this_id = 7, | ||
339 | .sg_tablesize = SG_ALL, | ||
340 | .cmd_per_lun = 8, | ||
341 | .use_clustering = DISABLE_CLUSTERING, | ||
342 | }; | ||
343 | #include "scsi_module.c" | ||