Blame view

drivers/scsi/a3000.c 7.21 KB
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1
2
  #include <linux/types.h>
  #include <linux/mm.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
3
4
  #include <linux/ioport.h>
  #include <linux/init.h>
c2a24a4ca   Geert Uytterhoeven   m68k: amiga - A30...
5
  #include <linux/slab.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
6
7
  #include <linux/spinlock.h>
  #include <linux/interrupt.h>
c2a24a4ca   Geert Uytterhoeven   m68k: amiga - A30...
8
  #include <linux/platform_device.h>
acf3368ff   Paul Gortmaker   scsi: Fix up file...
9
  #include <linux/module.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
10

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
11
12
13
14
  #include <asm/page.h>
  #include <asm/pgtable.h>
  #include <asm/amigaints.h>
  #include <asm/amigahw.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
15
16
  
  #include "scsi.h"
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
17
18
  #include "wd33c93.h"
  #include "a3000.h"
9387edbe6   Adrian Bunk   [SCSI] a3000: mak...
19

2b21d5e47   Geert Uytterhoeven   m68k/scsi: a3000 ...
20
21
22
23
  struct a3000_hostdata {
  	struct WD33C93_hostdata wh;
  	struct a3000_scsiregs *regs;
  };
a8169e605   Geert Uytterhoeven   m68k/scsi: a3000 ...
24
  static irqreturn_t a3000_intr(int irq, void *data)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
25
  {
a8169e605   Geert Uytterhoeven   m68k/scsi: a3000 ...
26
  	struct Scsi_Host *instance = data;
2b21d5e47   Geert Uytterhoeven   m68k/scsi: a3000 ...
27
28
  	struct a3000_hostdata *hdata = shost_priv(instance);
  	unsigned int status = hdata->regs->ISTR;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
29
  	unsigned long flags;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
30
31
32
  
  	if (!(status & ISTR_INT_P))
  		return IRQ_NONE;
213510134   Geert Uytterhoeven   [SCSI] a3000: Rei...
33
  	if (status & ISTR_INTS) {
a8169e605   Geert Uytterhoeven   m68k/scsi: a3000 ...
34
35
36
  		spin_lock_irqsave(instance->host_lock, flags);
  		wd33c93_intr(instance);
  		spin_unlock_irqrestore(instance->host_lock, flags);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
37
38
  		return IRQ_HANDLED;
  	}
c2a24a4ca   Geert Uytterhoeven   m68k: amiga - A30...
39
40
  	pr_warning("Non-serviced A3000 SCSI-interrupt? ISTR = %02x
  ", status);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
41
42
  	return IRQ_NONE;
  }
65396410a   Henrik Kretzschmar   [SCSI] wd33c93: S...
43
  static int dma_setup(struct scsi_cmnd *cmd, int dir_in)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
44
  {
a8169e605   Geert Uytterhoeven   m68k/scsi: a3000 ...
45
  	struct Scsi_Host *instance = cmd->device->host;
2b21d5e47   Geert Uytterhoeven   m68k/scsi: a3000 ...
46
47
48
  	struct a3000_hostdata *hdata = shost_priv(instance);
  	struct WD33C93_hostdata *wh = &hdata->wh;
  	struct a3000_scsiregs *regs = hdata->regs;
213510134   Geert Uytterhoeven   [SCSI] a3000: Rei...
49
50
51
52
53
54
55
56
57
58
  	unsigned short cntr = CNTR_PDMD | CNTR_INTEN;
  	unsigned long addr = virt_to_bus(cmd->SCp.ptr);
  
  	/*
  	 * if the physical address has the wrong alignment, or if
  	 * physical address is bad, or if it is a write and at the
  	 * end of a physical memory chunk, then allocate a bounce
  	 * buffer
  	 */
  	if (addr & A3000_XFER_MASK) {
2b21d5e47   Geert Uytterhoeven   m68k/scsi: a3000 ...
59
60
61
  		wh->dma_bounce_len = (cmd->SCp.this_residual + 511) & ~0x1ff;
  		wh->dma_bounce_buffer = kmalloc(wh->dma_bounce_len,
  						GFP_KERNEL);
213510134   Geert Uytterhoeven   [SCSI] a3000: Rei...
62
63
  
  		/* can't allocate memory; use PIO */
2b21d5e47   Geert Uytterhoeven   m68k/scsi: a3000 ...
64
65
  		if (!wh->dma_bounce_buffer) {
  			wh->dma_bounce_len = 0;
213510134   Geert Uytterhoeven   [SCSI] a3000: Rei...
66
67
68
69
70
  			return 1;
  		}
  
  		if (!dir_in) {
  			/* copy to bounce buffer for a write */
2b21d5e47   Geert Uytterhoeven   m68k/scsi: a3000 ...
71
  			memcpy(wh->dma_bounce_buffer, cmd->SCp.ptr,
afdbbc161   Geert Uytterhoeven   [SCSI] a3000: Use...
72
  			       cmd->SCp.this_residual);
213510134   Geert Uytterhoeven   [SCSI] a3000: Rei...
73
  		}
2b21d5e47   Geert Uytterhoeven   m68k/scsi: a3000 ...
74
  		addr = virt_to_bus(wh->dma_bounce_buffer);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
75
  	}
213510134   Geert Uytterhoeven   [SCSI] a3000: Rei...
76
77
78
  	/* setup dma direction */
  	if (!dir_in)
  		cntr |= CNTR_DDIR;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
79

213510134   Geert Uytterhoeven   [SCSI] a3000: Rei...
80
  	/* remember direction */
2b21d5e47   Geert Uytterhoeven   m68k/scsi: a3000 ...
81
  	wh->dma_dir = dir_in;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
82

d753722ee   Geert Uytterhoeven   m68k/scsi: a3000 ...
83
  	regs->CNTR = cntr;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
84

213510134   Geert Uytterhoeven   [SCSI] a3000: Rei...
85
  	/* setup DMA *physical* address */
d753722ee   Geert Uytterhoeven   m68k/scsi: a3000 ...
86
  	regs->ACR = addr;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
87

213510134   Geert Uytterhoeven   [SCSI] a3000: Rei...
88
89
90
91
92
93
94
  	if (dir_in) {
  		/* invalidate any cache */
  		cache_clear(addr, cmd->SCp.this_residual);
  	} else {
  		/* push any dirty cache */
  		cache_push(addr, cmd->SCp.this_residual);
  	}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
95

213510134   Geert Uytterhoeven   [SCSI] a3000: Rei...
96
97
  	/* start DMA */
  	mb();			/* make sure setup is completed */
d753722ee   Geert Uytterhoeven   m68k/scsi: a3000 ...
98
  	regs->ST_DMA = 1;
213510134   Geert Uytterhoeven   [SCSI] a3000: Rei...
99
  	mb();			/* make sure DMA has started before next IO */
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
100

213510134   Geert Uytterhoeven   [SCSI] a3000: Rei...
101
102
  	/* return success */
  	return 0;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
103
  }
65396410a   Henrik Kretzschmar   [SCSI] wd33c93: S...
104
105
  static void dma_stop(struct Scsi_Host *instance, struct scsi_cmnd *SCpnt,
  		     int status)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
106
  {
2b21d5e47   Geert Uytterhoeven   m68k/scsi: a3000 ...
107
108
109
  	struct a3000_hostdata *hdata = shost_priv(instance);
  	struct WD33C93_hostdata *wh = &hdata->wh;
  	struct a3000_scsiregs *regs = hdata->regs;
afdbbc161   Geert Uytterhoeven   [SCSI] a3000: Use...
110

213510134   Geert Uytterhoeven   [SCSI] a3000: Rei...
111
112
  	/* disable SCSI interrupts */
  	unsigned short cntr = CNTR_PDMD;
2b21d5e47   Geert Uytterhoeven   m68k/scsi: a3000 ...
113
  	if (!wh->dma_dir)
213510134   Geert Uytterhoeven   [SCSI] a3000: Rei...
114
  		cntr |= CNTR_DDIR;
d753722ee   Geert Uytterhoeven   m68k/scsi: a3000 ...
115
  	regs->CNTR = cntr;
213510134   Geert Uytterhoeven   [SCSI] a3000: Rei...
116
117
118
  	mb();			/* make sure CNTR is updated before next IO */
  
  	/* flush if we were reading */
2b21d5e47   Geert Uytterhoeven   m68k/scsi: a3000 ...
119
  	if (wh->dma_dir) {
d753722ee   Geert Uytterhoeven   m68k/scsi: a3000 ...
120
  		regs->FLUSH = 1;
213510134   Geert Uytterhoeven   [SCSI] a3000: Rei...
121
  		mb();		/* don't allow prefetch */
d753722ee   Geert Uytterhoeven   m68k/scsi: a3000 ...
122
  		while (!(regs->ISTR & ISTR_FE_FLG))
213510134   Geert Uytterhoeven   [SCSI] a3000: Rei...
123
124
125
126
127
128
129
130
  			barrier();
  		mb();		/* no IO until FLUSH is done */
  	}
  
  	/* clear a possible interrupt */
  	/* I think that this CINT is only necessary if you are
  	 * using the terminal count features.   HM 7 Mar 1994
  	 */
d753722ee   Geert Uytterhoeven   m68k/scsi: a3000 ...
131
  	regs->CINT = 1;
213510134   Geert Uytterhoeven   [SCSI] a3000: Rei...
132
133
  
  	/* stop DMA */
d753722ee   Geert Uytterhoeven   m68k/scsi: a3000 ...
134
  	regs->SP_DMA = 1;
213510134   Geert Uytterhoeven   [SCSI] a3000: Rei...
135
136
137
  	mb();			/* make sure DMA is stopped before next IO */
  
  	/* restore the CONTROL bits (minus the direction flag) */
d753722ee   Geert Uytterhoeven   m68k/scsi: a3000 ...
138
  	regs->CNTR = CNTR_PDMD | CNTR_INTEN;
213510134   Geert Uytterhoeven   [SCSI] a3000: Rei...
139
140
141
  	mb();			/* make sure CNTR is updated before next IO */
  
  	/* copy from a bounce buffer, if necessary */
2b21d5e47   Geert Uytterhoeven   m68k/scsi: a3000 ...
142
  	if (status && wh->dma_bounce_buffer) {
213510134   Geert Uytterhoeven   [SCSI] a3000: Rei...
143
  		if (SCpnt) {
2b21d5e47   Geert Uytterhoeven   m68k/scsi: a3000 ...
144
145
  			if (wh->dma_dir && SCpnt)
  				memcpy(SCpnt->SCp.ptr, wh->dma_bounce_buffer,
213510134   Geert Uytterhoeven   [SCSI] a3000: Rei...
146
  				       SCpnt->SCp.this_residual);
2b21d5e47   Geert Uytterhoeven   m68k/scsi: a3000 ...
147
148
149
  			kfree(wh->dma_bounce_buffer);
  			wh->dma_bounce_buffer = NULL;
  			wh->dma_bounce_len = 0;
213510134   Geert Uytterhoeven   [SCSI] a3000: Rei...
150
  		} else {
2b21d5e47   Geert Uytterhoeven   m68k/scsi: a3000 ...
151
152
153
  			kfree(wh->dma_bounce_buffer);
  			wh->dma_bounce_buffer = NULL;
  			wh->dma_bounce_len = 0;
213510134   Geert Uytterhoeven   [SCSI] a3000: Rei...
154
  		}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
155
  	}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
156
  }
c2a24a4ca   Geert Uytterhoeven   m68k: amiga - A30...
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
  static int a3000_bus_reset(struct scsi_cmnd *cmd)
  {
  	struct Scsi_Host *instance = cmd->device->host;
  
  	/* FIXME perform bus-specific reset */
  
  	/* FIXME 2: kill this entire function, which should
  	   cause mid-layer to call wd33c93_host_reset anyway? */
  
  	spin_lock_irq(instance->host_lock);
  	wd33c93_host_reset(cmd);
  	spin_unlock_irq(instance->host_lock);
  
  	return SUCCESS;
  }
  
  static struct scsi_host_template amiga_a3000_scsi_template = {
  	.module			= THIS_MODULE,
  	.name			= "Amiga 3000 built-in SCSI",
  	.proc_info		= wd33c93_proc_info,
  	.proc_name		= "A3000",
  	.queuecommand		= wd33c93_queuecommand,
  	.eh_abort_handler	= wd33c93_abort,
  	.eh_bus_reset_handler	= a3000_bus_reset,
  	.eh_host_reset_handler	= wd33c93_host_reset,
  	.can_queue		= CAN_QUEUE,
  	.this_id		= 7,
  	.sg_tablesize		= SG_ALL,
  	.cmd_per_lun		= CMD_PER_LUN,
  	.use_clustering		= ENABLE_CLUSTERING
  };
  
  static int __init amiga_a3000_scsi_probe(struct platform_device *pdev)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
190
  {
c2a24a4ca   Geert Uytterhoeven   m68k: amiga - A30...
191
  	struct resource *res;
a8169e605   Geert Uytterhoeven   m68k/scsi: a3000 ...
192
  	struct Scsi_Host *instance;
c2a24a4ca   Geert Uytterhoeven   m68k: amiga - A30...
193
  	int error;
c57c1cab7   Geert Uytterhoeven   m68k/scsi: a3000 ...
194
  	struct a3000_scsiregs *regs;
c2a24a4ca   Geert Uytterhoeven   m68k: amiga - A30...
195
  	wd33c93_regs wdregs;
2b21d5e47   Geert Uytterhoeven   m68k/scsi: a3000 ...
196
  	struct a3000_hostdata *hdata;
213510134   Geert Uytterhoeven   [SCSI] a3000: Rei...
197

c2a24a4ca   Geert Uytterhoeven   m68k: amiga - A30...
198
199
200
  	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
  	if (!res)
  		return -ENODEV;
213510134   Geert Uytterhoeven   [SCSI] a3000: Rei...
201

c2a24a4ca   Geert Uytterhoeven   m68k: amiga - A30...
202
203
  	if (!request_mem_region(res->start, resource_size(res), "wd33c93"))
  		return -EBUSY;
213510134   Geert Uytterhoeven   [SCSI] a3000: Rei...
204

c2a24a4ca   Geert Uytterhoeven   m68k: amiga - A30...
205
  	instance = scsi_host_alloc(&amiga_a3000_scsi_template,
2b21d5e47   Geert Uytterhoeven   m68k/scsi: a3000 ...
206
  				   sizeof(struct a3000_hostdata));
c2a24a4ca   Geert Uytterhoeven   m68k: amiga - A30...
207
208
209
210
  	if (!instance) {
  		error = -ENOMEM;
  		goto fail_alloc;
  	}
213510134   Geert Uytterhoeven   [SCSI] a3000: Rei...
211

a8169e605   Geert Uytterhoeven   m68k/scsi: a3000 ...
212
  	instance->irq = IRQ_AMIGA_PORTS;
c2a24a4ca   Geert Uytterhoeven   m68k: amiga - A30...
213

2b21d5e47   Geert Uytterhoeven   m68k/scsi: a3000 ...
214
  	regs = (struct a3000_scsiregs *)ZTWO_VADDR(res->start);
d753722ee   Geert Uytterhoeven   m68k/scsi: a3000 ...
215
  	regs->DAWR = DAWR_A3000;
c2a24a4ca   Geert Uytterhoeven   m68k: amiga - A30...
216

d753722ee   Geert Uytterhoeven   m68k/scsi: a3000 ...
217
218
  	wdregs.SASR = &regs->SASR;
  	wdregs.SCMD = &regs->SCMD;
c2a24a4ca   Geert Uytterhoeven   m68k: amiga - A30...
219

a8169e605   Geert Uytterhoeven   m68k/scsi: a3000 ...
220
  	hdata = shost_priv(instance);
2b21d5e47   Geert Uytterhoeven   m68k/scsi: a3000 ...
221
222
223
224
  	hdata->wh.no_sync = 0xff;
  	hdata->wh.fast = 0;
  	hdata->wh.dma_mode = CTRL_DMA;
  	hdata->regs = regs;
c2a24a4ca   Geert Uytterhoeven   m68k: amiga - A30...
225

a8169e605   Geert Uytterhoeven   m68k/scsi: a3000 ...
226
  	wd33c93_init(instance, wdregs, dma_setup, dma_stop, WD33C93_FS_12_15);
c2a24a4ca   Geert Uytterhoeven   m68k: amiga - A30...
227
228
229
  	error = request_irq(IRQ_AMIGA_PORTS, a3000_intr, IRQF_SHARED,
  			    "A3000 SCSI", instance);
  	if (error)
213510134   Geert Uytterhoeven   [SCSI] a3000: Rei...
230
  		goto fail_irq;
c2a24a4ca   Geert Uytterhoeven   m68k: amiga - A30...
231

d753722ee   Geert Uytterhoeven   m68k/scsi: a3000 ...
232
  	regs->CNTR = CNTR_PDMD | CNTR_INTEN;
213510134   Geert Uytterhoeven   [SCSI] a3000: Rei...
233

c2a24a4ca   Geert Uytterhoeven   m68k: amiga - A30...
234
235
236
  	error = scsi_add_host(instance, NULL);
  	if (error)
  		goto fail_host;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
237

c2a24a4ca   Geert Uytterhoeven   m68k: amiga - A30...
238
239
240
  	platform_set_drvdata(pdev, instance);
  
  	scsi_scan_host(instance);
213510134   Geert Uytterhoeven   [SCSI] a3000: Rei...
241
  	return 0;
c2a24a4ca   Geert Uytterhoeven   m68k: amiga - A30...
242
243
244
245
246
247
248
249
  
  fail_host:
  	free_irq(IRQ_AMIGA_PORTS, instance);
  fail_irq:
  	scsi_host_put(instance);
  fail_alloc:
  	release_mem_region(res->start, resource_size(res));
  	return error;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
250
  }
c2a24a4ca   Geert Uytterhoeven   m68k: amiga - A30...
251
  static int __exit amiga_a3000_scsi_remove(struct platform_device *pdev)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
252
  {
c2a24a4ca   Geert Uytterhoeven   m68k: amiga - A30...
253
  	struct Scsi_Host *instance = platform_get_drvdata(pdev);
2b21d5e47   Geert Uytterhoeven   m68k/scsi: a3000 ...
254
  	struct a3000_hostdata *hdata = shost_priv(instance);
c2a24a4ca   Geert Uytterhoeven   m68k: amiga - A30...
255
  	struct resource *res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
68b3aa7c9   Jeff Garzik   [SCSI] allow slee...
256

2b21d5e47   Geert Uytterhoeven   m68k/scsi: a3000 ...
257
  	hdata->regs->CNTR = 0;
c2a24a4ca   Geert Uytterhoeven   m68k: amiga - A30...
258
259
260
261
262
  	scsi_remove_host(instance);
  	free_irq(IRQ_AMIGA_PORTS, instance);
  	scsi_host_put(instance);
  	release_mem_region(res->start, resource_size(res));
  	return 0;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
263
  }
c2a24a4ca   Geert Uytterhoeven   m68k: amiga - A30...
264
265
266
267
268
269
  static struct platform_driver amiga_a3000_scsi_driver = {
  	.remove = __exit_p(amiga_a3000_scsi_remove),
  	.driver   = {
  		.name	= "amiga-a3000-scsi",
  		.owner	= THIS_MODULE,
  	},
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
270
  };
c2a24a4ca   Geert Uytterhoeven   m68k: amiga - A30...
271
  static int __init amiga_a3000_scsi_init(void)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
272
  {
c2a24a4ca   Geert Uytterhoeven   m68k: amiga - A30...
273
274
275
276
  	return platform_driver_probe(&amiga_a3000_scsi_driver,
  				     amiga_a3000_scsi_probe);
  }
  module_init(amiga_a3000_scsi_init);
d753722ee   Geert Uytterhoeven   m68k/scsi: a3000 ...
277

c2a24a4ca   Geert Uytterhoeven   m68k: amiga - A30...
278
279
280
  static void __exit amiga_a3000_scsi_exit(void)
  {
  	platform_driver_unregister(&amiga_a3000_scsi_driver);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
281
  }
c2a24a4ca   Geert Uytterhoeven   m68k: amiga - A30...
282
  module_exit(amiga_a3000_scsi_exit);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
283

c2a24a4ca   Geert Uytterhoeven   m68k: amiga - A30...
284
  MODULE_DESCRIPTION("Amiga 3000 built-in SCSI");
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
285
  MODULE_LICENSE("GPL");
c2a24a4ca   Geert Uytterhoeven   m68k: amiga - A30...
286
  MODULE_ALIAS("platform:amiga-a3000-scsi");