diff options
author | Linus Torvalds <torvalds@ppc970.osdl.org> | 2005-04-16 18:20:36 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@ppc970.osdl.org> | 2005-04-16 18:20:36 -0400 |
commit | 1da177e4c3f41524e886b7f1b8a0c1fc7321cac2 (patch) | |
tree | 0bba044c4ce775e45a88a51686b5d9f90697ea9d /drivers/scsi/sun3x_esp.c |
Linux-2.6.12-rc2v2.6.12-rc2
Initial git repository build. I'm not bothering with the full history,
even though we have it. We can create a separate "historical" git
archive of that later if we want to, and in the meantime it's about
3.2GB when imported into git - space that would just make the early
git days unnecessarily complicated, when we don't have a lot of good
infrastructure for it.
Let it rip!
Diffstat (limited to 'drivers/scsi/sun3x_esp.c')
-rw-r--r-- | drivers/scsi/sun3x_esp.c | 394 |
1 files changed, 394 insertions, 0 deletions
diff --git a/drivers/scsi/sun3x_esp.c b/drivers/scsi/sun3x_esp.c new file mode 100644 index 000000000000..5d1dc0e8ba21 --- /dev/null +++ b/drivers/scsi/sun3x_esp.c | |||
@@ -0,0 +1,394 @@ | |||
1 | /* sun3x_esp.c: EnhancedScsiProcessor Sun3x SCSI driver code. | ||
2 | * | ||
3 | * (C) 1999 Thomas Bogendoerfer (tsbogend@alpha.franken.de) | ||
4 | * | ||
5 | * Based on David S. Miller's esp driver | ||
6 | */ | ||
7 | |||
8 | #include <linux/kernel.h> | ||
9 | #include <linux/types.h> | ||
10 | #include <linux/string.h> | ||
11 | #include <linux/slab.h> | ||
12 | #include <linux/blkdev.h> | ||
13 | #include <linux/proc_fs.h> | ||
14 | #include <linux/stat.h> | ||
15 | #include <linux/delay.h> | ||
16 | #include <linux/interrupt.h> | ||
17 | |||
18 | #include "scsi.h" | ||
19 | #include <scsi/scsi_host.h> | ||
20 | #include "NCR53C9x.h" | ||
21 | |||
22 | #include <asm/sun3x.h> | ||
23 | #include <asm/dvma.h> | ||
24 | #include <asm/irq.h> | ||
25 | |||
26 | extern struct NCR_ESP *espchain; | ||
27 | |||
28 | static void dma_barrier(struct NCR_ESP *esp); | ||
29 | static int dma_bytes_sent(struct NCR_ESP *esp, int fifo_count); | ||
30 | static int dma_can_transfer(struct NCR_ESP *esp, Scsi_Cmnd *sp); | ||
31 | static void dma_drain(struct NCR_ESP *esp); | ||
32 | static void dma_invalidate(struct NCR_ESP *esp); | ||
33 | static void dma_dump_state(struct NCR_ESP *esp); | ||
34 | static void dma_init_read(struct NCR_ESP *esp, __u32 vaddress, int length); | ||
35 | static void dma_init_write(struct NCR_ESP *esp, __u32 vaddress, int length); | ||
36 | static void dma_ints_off(struct NCR_ESP *esp); | ||
37 | static void dma_ints_on(struct NCR_ESP *esp); | ||
38 | static int dma_irq_p(struct NCR_ESP *esp); | ||
39 | static void dma_poll(struct NCR_ESP *esp, unsigned char *vaddr); | ||
40 | static int dma_ports_p(struct NCR_ESP *esp); | ||
41 | static void dma_reset(struct NCR_ESP *esp); | ||
42 | static void dma_setup(struct NCR_ESP *esp, __u32 addr, int count, int write); | ||
43 | static void dma_mmu_get_scsi_one (struct NCR_ESP *esp, Scsi_Cmnd *sp); | ||
44 | static void dma_mmu_get_scsi_sgl (struct NCR_ESP *esp, Scsi_Cmnd *sp); | ||
45 | static void dma_mmu_release_scsi_one (struct NCR_ESP *esp, Scsi_Cmnd *sp); | ||
46 | static void dma_mmu_release_scsi_sgl (struct NCR_ESP *esp, Scsi_Cmnd *sp); | ||
47 | static void dma_advance_sg (Scsi_Cmnd *sp); | ||
48 | |||
49 | /* Detecting ESP chips on the machine. This is the simple and easy | ||
50 | * version. | ||
51 | */ | ||
52 | int sun3x_esp_detect(Scsi_Host_Template *tpnt) | ||
53 | { | ||
54 | struct NCR_ESP *esp; | ||
55 | struct ConfigDev *esp_dev; | ||
56 | |||
57 | esp_dev = 0; | ||
58 | esp = esp_allocate(tpnt, (void *) esp_dev); | ||
59 | |||
60 | /* Do command transfer with DMA */ | ||
61 | esp->do_pio_cmds = 0; | ||
62 | |||
63 | /* Required functions */ | ||
64 | esp->dma_bytes_sent = &dma_bytes_sent; | ||
65 | esp->dma_can_transfer = &dma_can_transfer; | ||
66 | esp->dma_dump_state = &dma_dump_state; | ||
67 | esp->dma_init_read = &dma_init_read; | ||
68 | esp->dma_init_write = &dma_init_write; | ||
69 | esp->dma_ints_off = &dma_ints_off; | ||
70 | esp->dma_ints_on = &dma_ints_on; | ||
71 | esp->dma_irq_p = &dma_irq_p; | ||
72 | esp->dma_ports_p = &dma_ports_p; | ||
73 | esp->dma_setup = &dma_setup; | ||
74 | |||
75 | /* Optional functions */ | ||
76 | esp->dma_barrier = &dma_barrier; | ||
77 | esp->dma_invalidate = &dma_invalidate; | ||
78 | esp->dma_drain = &dma_drain; | ||
79 | esp->dma_irq_entry = 0; | ||
80 | esp->dma_irq_exit = 0; | ||
81 | esp->dma_led_on = 0; | ||
82 | esp->dma_led_off = 0; | ||
83 | esp->dma_poll = &dma_poll; | ||
84 | esp->dma_reset = &dma_reset; | ||
85 | |||
86 | /* virtual DMA functions */ | ||
87 | esp->dma_mmu_get_scsi_one = &dma_mmu_get_scsi_one; | ||
88 | esp->dma_mmu_get_scsi_sgl = &dma_mmu_get_scsi_sgl; | ||
89 | esp->dma_mmu_release_scsi_one = &dma_mmu_release_scsi_one; | ||
90 | esp->dma_mmu_release_scsi_sgl = &dma_mmu_release_scsi_sgl; | ||
91 | esp->dma_advance_sg = &dma_advance_sg; | ||
92 | |||
93 | /* SCSI chip speed */ | ||
94 | esp->cfreq = 20000000; | ||
95 | esp->eregs = (struct ESP_regs *)(SUN3X_ESP_BASE); | ||
96 | esp->dregs = (void *)SUN3X_ESP_DMA; | ||
97 | |||
98 | esp->esp_command = (volatile unsigned char *)dvma_malloc(DVMA_PAGE_SIZE); | ||
99 | esp->esp_command_dvma = dvma_vtob((unsigned long)esp->esp_command); | ||
100 | |||
101 | esp->irq = 2; | ||
102 | if (request_irq(esp->irq, esp_intr, SA_INTERRUPT, | ||
103 | "SUN3X SCSI", esp->ehost)) { | ||
104 | esp_deallocate(esp); | ||
105 | return 0; | ||
106 | } | ||
107 | |||
108 | esp->scsi_id = 7; | ||
109 | esp->diff = 0; | ||
110 | |||
111 | esp_initialize(esp); | ||
112 | |||
113 | /* for reasons beyond my knowledge (and which should likely be fixed) | ||
114 | sync mode doesn't work on a 3/80 at 5mhz. but it does at 4. */ | ||
115 | esp->sync_defp = 0x3f; | ||
116 | |||
117 | printk("ESP: Total of %d ESP hosts found, %d actually in use.\n", nesps, | ||
118 | esps_in_use); | ||
119 | esps_running = esps_in_use; | ||
120 | return esps_in_use; | ||
121 | } | ||
122 | |||
123 | static void dma_do_drain(struct NCR_ESP *esp) | ||
124 | { | ||
125 | struct sparc_dma_registers *dregs = | ||
126 | (struct sparc_dma_registers *) esp->dregs; | ||
127 | |||
128 | int count = 500000; | ||
129 | |||
130 | while((dregs->cond_reg & DMA_PEND_READ) && (--count > 0)) | ||
131 | udelay(1); | ||
132 | |||
133 | if(!count) { | ||
134 | printk("%s:%d timeout CSR %08lx\n", __FILE__, __LINE__, dregs->cond_reg); | ||
135 | } | ||
136 | |||
137 | dregs->cond_reg |= DMA_FIFO_STDRAIN; | ||
138 | |||
139 | count = 500000; | ||
140 | |||
141 | while((dregs->cond_reg & DMA_FIFO_ISDRAIN) && (--count > 0)) | ||
142 | udelay(1); | ||
143 | |||
144 | if(!count) { | ||
145 | printk("%s:%d timeout CSR %08lx\n", __FILE__, __LINE__, dregs->cond_reg); | ||
146 | } | ||
147 | |||
148 | } | ||
149 | |||
150 | static void dma_barrier(struct NCR_ESP *esp) | ||
151 | { | ||
152 | struct sparc_dma_registers *dregs = | ||
153 | (struct sparc_dma_registers *) esp->dregs; | ||
154 | int count = 500000; | ||
155 | |||
156 | while((dregs->cond_reg & DMA_PEND_READ) && (--count > 0)) | ||
157 | udelay(1); | ||
158 | |||
159 | if(!count) { | ||
160 | printk("%s:%d timeout CSR %08lx\n", __FILE__, __LINE__, dregs->cond_reg); | ||
161 | } | ||
162 | |||
163 | dregs->cond_reg &= ~(DMA_ENABLE); | ||
164 | } | ||
165 | |||
166 | /* This uses various DMA csr fields and the fifo flags count value to | ||
167 | * determine how many bytes were successfully sent/received by the ESP. | ||
168 | */ | ||
169 | static int dma_bytes_sent(struct NCR_ESP *esp, int fifo_count) | ||
170 | { | ||
171 | struct sparc_dma_registers *dregs = | ||
172 | (struct sparc_dma_registers *) esp->dregs; | ||
173 | |||
174 | int rval = dregs->st_addr - esp->esp_command_dvma; | ||
175 | |||
176 | return rval - fifo_count; | ||
177 | } | ||
178 | |||
179 | static int dma_can_transfer(struct NCR_ESP *esp, Scsi_Cmnd *sp) | ||
180 | { | ||
181 | return sp->SCp.this_residual; | ||
182 | } | ||
183 | |||
184 | static void dma_drain(struct NCR_ESP *esp) | ||
185 | { | ||
186 | struct sparc_dma_registers *dregs = | ||
187 | (struct sparc_dma_registers *) esp->dregs; | ||
188 | int count = 500000; | ||
189 | |||
190 | if(dregs->cond_reg & DMA_FIFO_ISDRAIN) { | ||
191 | dregs->cond_reg |= DMA_FIFO_STDRAIN; | ||
192 | while((dregs->cond_reg & DMA_FIFO_ISDRAIN) && (--count > 0)) | ||
193 | udelay(1); | ||
194 | if(!count) { | ||
195 | printk("%s:%d timeout CSR %08lx\n", __FILE__, __LINE__, dregs->cond_reg); | ||
196 | } | ||
197 | |||
198 | } | ||
199 | } | ||
200 | |||
201 | static void dma_invalidate(struct NCR_ESP *esp) | ||
202 | { | ||
203 | struct sparc_dma_registers *dregs = | ||
204 | (struct sparc_dma_registers *) esp->dregs; | ||
205 | |||
206 | __u32 tmp; | ||
207 | int count = 500000; | ||
208 | |||
209 | while(((tmp = dregs->cond_reg) & DMA_PEND_READ) && (--count > 0)) | ||
210 | udelay(1); | ||
211 | |||
212 | if(!count) { | ||
213 | printk("%s:%d timeout CSR %08lx\n", __FILE__, __LINE__, dregs->cond_reg); | ||
214 | } | ||
215 | |||
216 | dregs->cond_reg = tmp | DMA_FIFO_INV; | ||
217 | dregs->cond_reg &= ~DMA_FIFO_INV; | ||
218 | |||
219 | } | ||
220 | |||
221 | static void dma_dump_state(struct NCR_ESP *esp) | ||
222 | { | ||
223 | struct sparc_dma_registers *dregs = | ||
224 | (struct sparc_dma_registers *) esp->dregs; | ||
225 | |||
226 | ESPLOG(("esp%d: dma -- cond_reg<%08lx> addr<%08lx>\n", | ||
227 | esp->esp_id, dregs->cond_reg, dregs->st_addr)); | ||
228 | } | ||
229 | |||
230 | static void dma_init_read(struct NCR_ESP *esp, __u32 vaddress, int length) | ||
231 | { | ||
232 | struct sparc_dma_registers *dregs = | ||
233 | (struct sparc_dma_registers *) esp->dregs; | ||
234 | |||
235 | dregs->st_addr = vaddress; | ||
236 | dregs->cond_reg |= (DMA_ST_WRITE | DMA_ENABLE); | ||
237 | } | ||
238 | |||
239 | static void dma_init_write(struct NCR_ESP *esp, __u32 vaddress, int length) | ||
240 | { | ||
241 | struct sparc_dma_registers *dregs = | ||
242 | (struct sparc_dma_registers *) esp->dregs; | ||
243 | |||
244 | /* Set up the DMA counters */ | ||
245 | |||
246 | dregs->st_addr = vaddress; | ||
247 | dregs->cond_reg = ((dregs->cond_reg & ~(DMA_ST_WRITE)) | DMA_ENABLE); | ||
248 | } | ||
249 | |||
250 | static void dma_ints_off(struct NCR_ESP *esp) | ||
251 | { | ||
252 | DMA_INTSOFF((struct sparc_dma_registers *) esp->dregs); | ||
253 | } | ||
254 | |||
255 | static void dma_ints_on(struct NCR_ESP *esp) | ||
256 | { | ||
257 | DMA_INTSON((struct sparc_dma_registers *) esp->dregs); | ||
258 | } | ||
259 | |||
260 | static int dma_irq_p(struct NCR_ESP *esp) | ||
261 | { | ||
262 | return DMA_IRQ_P((struct sparc_dma_registers *) esp->dregs); | ||
263 | } | ||
264 | |||
265 | static void dma_poll(struct NCR_ESP *esp, unsigned char *vaddr) | ||
266 | { | ||
267 | int count = 50; | ||
268 | dma_do_drain(esp); | ||
269 | |||
270 | /* Wait till the first bits settle. */ | ||
271 | while((*(volatile unsigned char *)vaddr == 0xff) && (--count > 0)) | ||
272 | udelay(1); | ||
273 | |||
274 | if(!count) { | ||
275 | // printk("%s:%d timeout expire (data %02x)\n", __FILE__, __LINE__, | ||
276 | // esp_read(esp->eregs->esp_fdata)); | ||
277 | //mach_halt(); | ||
278 | vaddr[0] = esp_read(esp->eregs->esp_fdata); | ||
279 | vaddr[1] = esp_read(esp->eregs->esp_fdata); | ||
280 | } | ||
281 | |||
282 | } | ||
283 | |||
284 | static int dma_ports_p(struct NCR_ESP *esp) | ||
285 | { | ||
286 | return (((struct sparc_dma_registers *) esp->dregs)->cond_reg | ||
287 | & DMA_INT_ENAB); | ||
288 | } | ||
289 | |||
290 | /* Resetting various pieces of the ESP scsi driver chipset/buses. */ | ||
291 | static void dma_reset(struct NCR_ESP *esp) | ||
292 | { | ||
293 | struct sparc_dma_registers *dregs = | ||
294 | (struct sparc_dma_registers *)esp->dregs; | ||
295 | |||
296 | /* Punt the DVMA into a known state. */ | ||
297 | dregs->cond_reg |= DMA_RST_SCSI; | ||
298 | dregs->cond_reg &= ~(DMA_RST_SCSI); | ||
299 | DMA_INTSON(dregs); | ||
300 | } | ||
301 | |||
302 | static void dma_setup(struct NCR_ESP *esp, __u32 addr, int count, int write) | ||
303 | { | ||
304 | struct sparc_dma_registers *dregs = | ||
305 | (struct sparc_dma_registers *) esp->dregs; | ||
306 | unsigned long nreg = dregs->cond_reg; | ||
307 | |||
308 | // printk("dma_setup %c addr %08x cnt %08x\n", | ||
309 | // write ? 'W' : 'R', addr, count); | ||
310 | |||
311 | dma_do_drain(esp); | ||
312 | |||
313 | if(write) | ||
314 | nreg |= DMA_ST_WRITE; | ||
315 | else { | ||
316 | nreg &= ~(DMA_ST_WRITE); | ||
317 | } | ||
318 | |||
319 | nreg |= DMA_ENABLE; | ||
320 | dregs->cond_reg = nreg; | ||
321 | dregs->st_addr = addr; | ||
322 | } | ||
323 | |||
324 | static void dma_mmu_get_scsi_one (struct NCR_ESP *esp, Scsi_Cmnd *sp) | ||
325 | { | ||
326 | sp->SCp.have_data_in = dvma_map((unsigned long)sp->SCp.buffer, | ||
327 | sp->SCp.this_residual); | ||
328 | sp->SCp.ptr = (char *)((unsigned long)sp->SCp.have_data_in); | ||
329 | } | ||
330 | |||
331 | static void dma_mmu_get_scsi_sgl (struct NCR_ESP *esp, Scsi_Cmnd *sp) | ||
332 | { | ||
333 | int sz = sp->SCp.buffers_residual; | ||
334 | struct scatterlist *sg = sp->SCp.buffer; | ||
335 | |||
336 | while (sz >= 0) { | ||
337 | sg[sz].dvma_address = dvma_map((unsigned long)page_address(sg[sz].page) + | ||
338 | sg[sz].offset, sg[sz].length); | ||
339 | sz--; | ||
340 | } | ||
341 | sp->SCp.ptr=(char *)((unsigned long)sp->SCp.buffer->dvma_address); | ||
342 | } | ||
343 | |||
344 | static void dma_mmu_release_scsi_one (struct NCR_ESP *esp, Scsi_Cmnd *sp) | ||
345 | { | ||
346 | dvma_unmap((char *)sp->SCp.have_data_in); | ||
347 | } | ||
348 | |||
349 | static void dma_mmu_release_scsi_sgl (struct NCR_ESP *esp, Scsi_Cmnd *sp) | ||
350 | { | ||
351 | int sz = sp->use_sg - 1; | ||
352 | struct scatterlist *sg = (struct scatterlist *)sp->buffer; | ||
353 | |||
354 | while(sz >= 0) { | ||
355 | dvma_unmap((char *)sg[sz].dvma_address); | ||
356 | sz--; | ||
357 | } | ||
358 | } | ||
359 | |||
360 | static void dma_advance_sg (Scsi_Cmnd *sp) | ||
361 | { | ||
362 | sp->SCp.ptr = (char *)((unsigned long)sp->SCp.buffer->dvma_address); | ||
363 | } | ||
364 | |||
365 | static int sun3x_esp_release(struct Scsi_Host *instance) | ||
366 | { | ||
367 | /* this code does not support being compiled as a module */ | ||
368 | return 1; | ||
369 | |||
370 | } | ||
371 | |||
372 | static Scsi_Host_Template driver_template = { | ||
373 | .proc_name = "sun3x_esp", | ||
374 | .proc_info = &esp_proc_info, | ||
375 | .name = "Sun ESP 100/100a/200", | ||
376 | .detect = sun3x_esp_detect, | ||
377 | .release = sun3x_esp_release, | ||
378 | .slave_alloc = esp_slave_alloc, | ||
379 | .slave_destroy = esp_slave_destroy, | ||
380 | .info = esp_info, | ||
381 | .queuecommand = esp_queue, | ||
382 | .eh_abort_handler = esp_abort, | ||
383 | .eh_bus_reset_handler = esp_reset, | ||
384 | .can_queue = 7, | ||
385 | .this_id = 7, | ||
386 | .sg_tablesize = SG_ALL, | ||
387 | .cmd_per_lun = 1, | ||
388 | .use_clustering = DISABLE_CLUSTERING, | ||
389 | }; | ||
390 | |||
391 | |||
392 | #include "scsi_module.c" | ||
393 | |||
394 | MODULE_LICENSE("GPL"); | ||