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/NCR_Q720.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/NCR_Q720.c')
-rw-r--r-- | drivers/scsi/NCR_Q720.c | 377 |
1 files changed, 377 insertions, 0 deletions
diff --git a/drivers/scsi/NCR_Q720.c b/drivers/scsi/NCR_Q720.c new file mode 100644 index 000000000000..9d18ec90510f --- /dev/null +++ b/drivers/scsi/NCR_Q720.c | |||
@@ -0,0 +1,377 @@ | |||
1 | /* -*- mode: c; c-basic-offset: 8 -*- */ | ||
2 | |||
3 | /* NCR Quad 720 MCA SCSI Driver | ||
4 | * | ||
5 | * Copyright (C) 2003 by James.Bottomley@HansenPartnership.com | ||
6 | */ | ||
7 | |||
8 | #include <linux/blkdev.h> | ||
9 | #include <linux/interrupt.h> | ||
10 | #include <linux/kernel.h> | ||
11 | #include <linux/module.h> | ||
12 | #include <linux/mca.h> | ||
13 | #include <linux/types.h> | ||
14 | #include <linux/init.h> | ||
15 | #include <linux/delay.h> | ||
16 | #include <asm/io.h> | ||
17 | |||
18 | #include "scsi.h" | ||
19 | #include <scsi/scsi_host.h> | ||
20 | |||
21 | #include "ncr53c8xx.h" | ||
22 | |||
23 | #include "NCR_Q720.h" | ||
24 | |||
25 | static struct ncr_chip q720_chip __initdata = { | ||
26 | .revision_id = 0x0f, | ||
27 | .burst_max = 3, | ||
28 | .offset_max = 8, | ||
29 | .nr_divisor = 4, | ||
30 | .features = FE_WIDE | FE_DIFF | FE_VARCLK, | ||
31 | }; | ||
32 | |||
33 | MODULE_AUTHOR("James Bottomley"); | ||
34 | MODULE_DESCRIPTION("NCR Quad 720 SCSI Driver"); | ||
35 | MODULE_LICENSE("GPL"); | ||
36 | |||
37 | #define NCR_Q720_VERSION "0.9" | ||
38 | |||
39 | /* We needs this helper because we have up to four hosts per struct device */ | ||
40 | struct NCR_Q720_private { | ||
41 | struct device *dev; | ||
42 | void __iomem * mem_base; | ||
43 | __u32 phys_mem_base; | ||
44 | __u32 mem_size; | ||
45 | __u8 irq; | ||
46 | __u8 siops; | ||
47 | __u8 irq_enable; | ||
48 | struct Scsi_Host *hosts[4]; | ||
49 | }; | ||
50 | |||
51 | static struct scsi_host_template NCR_Q720_tpnt = { | ||
52 | .module = THIS_MODULE, | ||
53 | .proc_name = "NCR_Q720", | ||
54 | }; | ||
55 | |||
56 | static irqreturn_t | ||
57 | NCR_Q720_intr(int irq, void *data, struct pt_regs * regs) | ||
58 | { | ||
59 | struct NCR_Q720_private *p = (struct NCR_Q720_private *)data; | ||
60 | __u8 sir = (readb(p->mem_base + 0x0d) & 0xf0) >> 4; | ||
61 | __u8 siop; | ||
62 | |||
63 | sir |= ~p->irq_enable; | ||
64 | |||
65 | if(sir == 0xff) | ||
66 | return IRQ_NONE; | ||
67 | |||
68 | |||
69 | while((siop = ffz(sir)) < p->siops) { | ||
70 | sir |= 1<<siop; | ||
71 | ncr53c8xx_intr(irq, p->hosts[siop], regs); | ||
72 | } | ||
73 | return IRQ_HANDLED; | ||
74 | } | ||
75 | |||
76 | static int __init | ||
77 | NCR_Q720_probe_one(struct NCR_Q720_private *p, int siop, | ||
78 | int irq, int slot, __u32 paddr, void __iomem *vaddr) | ||
79 | { | ||
80 | struct ncr_device device; | ||
81 | __u8 scsi_id; | ||
82 | static int unit = 0; | ||
83 | __u8 scsr1 = readb(vaddr + NCR_Q720_SCSR_OFFSET + 1); | ||
84 | __u8 differential = readb(vaddr + NCR_Q720_SCSR_OFFSET) & 0x20; | ||
85 | __u8 version; | ||
86 | int error; | ||
87 | |||
88 | scsi_id = scsr1 >> 4; | ||
89 | /* enable burst length 16 (FIXME: should allow this) */ | ||
90 | scsr1 |= 0x02; | ||
91 | /* force a siop reset */ | ||
92 | scsr1 |= 0x04; | ||
93 | writeb(scsr1, vaddr + NCR_Q720_SCSR_OFFSET + 1); | ||
94 | udelay(10); | ||
95 | version = readb(vaddr + 0x18) >> 4; | ||
96 | |||
97 | memset(&device, 0, sizeof(struct ncr_device)); | ||
98 | /* Initialise ncr_device structure with items required by ncr_attach. */ | ||
99 | device.chip = q720_chip; | ||
100 | device.chip.revision_id = version; | ||
101 | device.host_id = scsi_id; | ||
102 | device.dev = p->dev; | ||
103 | device.slot.base = paddr; | ||
104 | device.slot.base_c = paddr; | ||
105 | device.slot.base_v = vaddr; | ||
106 | device.slot.irq = irq; | ||
107 | device.differential = differential ? 2 : 0; | ||
108 | printk("Q720 probe unit %d (siop%d) at 0x%lx, diff = %d, vers = %d\n", unit, siop, | ||
109 | (unsigned long)paddr, differential, version); | ||
110 | |||
111 | p->hosts[siop] = ncr_attach(&NCR_Q720_tpnt, unit++, &device); | ||
112 | |||
113 | if (!p->hosts[siop]) | ||
114 | goto fail; | ||
115 | |||
116 | p->irq_enable |= (1<<siop); | ||
117 | scsr1 = readb(vaddr + NCR_Q720_SCSR_OFFSET + 1); | ||
118 | /* clear the disable interrupt bit */ | ||
119 | scsr1 &= ~0x01; | ||
120 | writeb(scsr1, vaddr + NCR_Q720_SCSR_OFFSET + 1); | ||
121 | |||
122 | error = scsi_add_host(p->hosts[siop], p->dev); | ||
123 | if (error) | ||
124 | ncr53c8xx_release(p->hosts[siop]); | ||
125 | else | ||
126 | scsi_scan_host(p->hosts[siop]); | ||
127 | return error; | ||
128 | |||
129 | fail: | ||
130 | return -ENODEV; | ||
131 | } | ||
132 | |||
133 | /* Detect a Q720 card. Note, because of the setup --- the chips are | ||
134 | * essentially connectecd to the MCA bus independently, it is easier | ||
135 | * to set them up as two separate host adapters, rather than one | ||
136 | * adapter with two channels */ | ||
137 | static int __init | ||
138 | NCR_Q720_probe(struct device *dev) | ||
139 | { | ||
140 | struct NCR_Q720_private *p; | ||
141 | static int banner = 1; | ||
142 | struct mca_device *mca_dev = to_mca_device(dev); | ||
143 | int slot = mca_dev->slot; | ||
144 | int found = 0; | ||
145 | int irq, i, siops; | ||
146 | __u8 pos2, pos4, asr2, asr9, asr10; | ||
147 | __u16 io_base; | ||
148 | __u32 base_addr, mem_size; | ||
149 | void __iomem *mem_base; | ||
150 | |||
151 | p = kmalloc(sizeof(*p), GFP_KERNEL); | ||
152 | if (!p) | ||
153 | return -ENOMEM; | ||
154 | |||
155 | memset(p, 0, sizeof(*p)); | ||
156 | pos2 = mca_device_read_pos(mca_dev, 2); | ||
157 | /* enable device */ | ||
158 | pos2 |= NCR_Q720_POS2_BOARD_ENABLE | NCR_Q720_POS2_INTERRUPT_ENABLE; | ||
159 | mca_device_write_pos(mca_dev, 2, pos2); | ||
160 | |||
161 | io_base = (pos2 & NCR_Q720_POS2_IO_MASK) << NCR_Q720_POS2_IO_SHIFT; | ||
162 | |||
163 | |||
164 | if(banner) { | ||
165 | printk(KERN_NOTICE "NCR Q720: Driver Version " NCR_Q720_VERSION "\n" | ||
166 | "NCR Q720: Copyright (c) 2003 by James.Bottomley@HansenPartnership.com\n" | ||
167 | "NCR Q720:\n"); | ||
168 | banner = 0; | ||
169 | } | ||
170 | io_base = mca_device_transform_ioport(mca_dev, io_base); | ||
171 | |||
172 | /* OK, this is phase one of the bootstrap, we now know the | ||
173 | * I/O space base address. All the configuration registers | ||
174 | * are mapped here (including pos) */ | ||
175 | |||
176 | /* sanity check I/O mapping */ | ||
177 | i = inb(io_base) | (inb(io_base+1)<<8); | ||
178 | if(i != NCR_Q720_MCA_ID) { | ||
179 | printk(KERN_ERR "NCR_Q720, adapter failed to I/O map registers correctly at 0x%x(0x%x)\n", io_base, i); | ||
180 | kfree(p); | ||
181 | return -ENODEV; | ||
182 | } | ||
183 | |||
184 | /* Phase II, find the ram base and memory map the board register */ | ||
185 | pos4 = inb(io_base + 4); | ||
186 | /* enable streaming data */ | ||
187 | pos4 |= 0x01; | ||
188 | outb(pos4, io_base + 4); | ||
189 | base_addr = (pos4 & 0x7e) << 20; | ||
190 | base_addr += (pos4 & 0x80) << 23; | ||
191 | asr10 = inb(io_base + 0x12); | ||
192 | base_addr += (asr10 & 0x80) << 24; | ||
193 | base_addr += (asr10 & 0x70) << 23; | ||
194 | |||
195 | /* OK, got the base addr, now we need to find the ram size, | ||
196 | * enable and map it */ | ||
197 | asr9 = inb(io_base + 0x11); | ||
198 | i = (asr9 & 0xc0) >> 6; | ||
199 | if(i == 0) | ||
200 | mem_size = 1024; | ||
201 | else | ||
202 | mem_size = 1 << (19 + i); | ||
203 | |||
204 | /* enable the sram mapping */ | ||
205 | asr9 |= 0x20; | ||
206 | |||
207 | /* disable the rom mapping */ | ||
208 | asr9 &= ~0x10; | ||
209 | |||
210 | outb(asr9, io_base + 0x11); | ||
211 | |||
212 | if(!request_mem_region(base_addr, mem_size, "NCR_Q720")) { | ||
213 | printk(KERN_ERR "NCR_Q720: Failed to claim memory region 0x%lx\n-0x%lx", | ||
214 | (unsigned long)base_addr, | ||
215 | (unsigned long)(base_addr + mem_size)); | ||
216 | goto out_free; | ||
217 | } | ||
218 | |||
219 | if (dma_declare_coherent_memory(dev, base_addr, base_addr, | ||
220 | mem_size, DMA_MEMORY_MAP) | ||
221 | != DMA_MEMORY_MAP) { | ||
222 | printk(KERN_ERR "NCR_Q720: DMA declare memory failed\n"); | ||
223 | goto out_release_region; | ||
224 | } | ||
225 | |||
226 | /* The first 1k of the memory buffer is a memory map of the registers | ||
227 | */ | ||
228 | mem_base = dma_mark_declared_memory_occupied(dev, base_addr, | ||
229 | 1024); | ||
230 | if (IS_ERR(mem_base)) { | ||
231 | printk("NCR_Q720 failed to reserve memory mapped region\n"); | ||
232 | goto out_release; | ||
233 | } | ||
234 | |||
235 | /* now also enable accesses in asr 2 */ | ||
236 | asr2 = inb(io_base + 0x0a); | ||
237 | |||
238 | asr2 |= 0x01; | ||
239 | |||
240 | outb(asr2, io_base + 0x0a); | ||
241 | |||
242 | /* get the number of SIOPs (this should be 2 or 4) */ | ||
243 | siops = ((asr2 & 0xe0) >> 5) + 1; | ||
244 | |||
245 | /* sanity check mapping (again) */ | ||
246 | i = readw(mem_base); | ||
247 | if(i != NCR_Q720_MCA_ID) { | ||
248 | printk(KERN_ERR "NCR_Q720, adapter failed to memory map registers correctly at 0x%lx(0x%x)\n", (unsigned long)base_addr, i); | ||
249 | goto out_release; | ||
250 | } | ||
251 | |||
252 | irq = readb(mem_base + 5) & 0x0f; | ||
253 | |||
254 | |||
255 | /* now do the bus related transforms */ | ||
256 | irq = mca_device_transform_irq(mca_dev, irq); | ||
257 | |||
258 | printk(KERN_NOTICE "NCR Q720: found in slot %d irq = %d mem base = 0x%lx siops = %d\n", slot, irq, (unsigned long)base_addr, siops); | ||
259 | printk(KERN_NOTICE "NCR Q720: On board ram %dk\n", mem_size/1024); | ||
260 | |||
261 | p->dev = dev; | ||
262 | p->mem_base = mem_base; | ||
263 | p->phys_mem_base = base_addr; | ||
264 | p->mem_size = mem_size; | ||
265 | p->irq = irq; | ||
266 | p->siops = siops; | ||
267 | |||
268 | if (request_irq(irq, NCR_Q720_intr, SA_SHIRQ, "NCR_Q720", p)) { | ||
269 | printk(KERN_ERR "NCR_Q720: request irq %d failed\n", irq); | ||
270 | goto out_release; | ||
271 | } | ||
272 | /* disable all the siop interrupts */ | ||
273 | for(i = 0; i < siops; i++) { | ||
274 | void __iomem *reg_scsr1 = mem_base + NCR_Q720_CHIP_REGISTER_OFFSET | ||
275 | + i*NCR_Q720_SIOP_SHIFT + NCR_Q720_SCSR_OFFSET + 1; | ||
276 | __u8 scsr1 = readb(reg_scsr1); | ||
277 | scsr1 |= 0x01; | ||
278 | writeb(scsr1, reg_scsr1); | ||
279 | } | ||
280 | |||
281 | /* plumb in all 720 chips */ | ||
282 | for (i = 0; i < siops; i++) { | ||
283 | void __iomem *siop_v_base = mem_base + NCR_Q720_CHIP_REGISTER_OFFSET | ||
284 | + i*NCR_Q720_SIOP_SHIFT; | ||
285 | __u32 siop_p_base = base_addr + NCR_Q720_CHIP_REGISTER_OFFSET | ||
286 | + i*NCR_Q720_SIOP_SHIFT; | ||
287 | __u16 port = io_base + NCR_Q720_CHIP_REGISTER_OFFSET | ||
288 | + i*NCR_Q720_SIOP_SHIFT; | ||
289 | int err; | ||
290 | |||
291 | outb(0xff, port + 0x40); | ||
292 | outb(0x07, port + 0x41); | ||
293 | if ((err = NCR_Q720_probe_one(p, i, irq, slot, | ||
294 | siop_p_base, siop_v_base)) != 0) | ||
295 | printk("Q720: SIOP%d: probe failed, error = %d\n", | ||
296 | i, err); | ||
297 | else | ||
298 | found++; | ||
299 | } | ||
300 | |||
301 | if (!found) { | ||
302 | kfree(p); | ||
303 | return -ENODEV; | ||
304 | } | ||
305 | |||
306 | mca_device_set_claim(mca_dev, 1); | ||
307 | mca_device_set_name(mca_dev, "NCR_Q720"); | ||
308 | dev_set_drvdata(dev, p); | ||
309 | |||
310 | return 0; | ||
311 | |||
312 | out_release: | ||
313 | dma_release_declared_memory(dev); | ||
314 | out_release_region: | ||
315 | release_mem_region(base_addr, mem_size); | ||
316 | out_free: | ||
317 | kfree(p); | ||
318 | |||
319 | return -ENODEV; | ||
320 | } | ||
321 | |||
322 | static void __exit | ||
323 | NCR_Q720_remove_one(struct Scsi_Host *host) | ||
324 | { | ||
325 | scsi_remove_host(host); | ||
326 | ncr53c8xx_release(host); | ||
327 | } | ||
328 | |||
329 | static int __exit | ||
330 | NCR_Q720_remove(struct device *dev) | ||
331 | { | ||
332 | struct NCR_Q720_private *p = dev_get_drvdata(dev); | ||
333 | int i; | ||
334 | |||
335 | for (i = 0; i < p->siops; i++) | ||
336 | if(p->hosts[i]) | ||
337 | NCR_Q720_remove_one(p->hosts[i]); | ||
338 | |||
339 | dma_release_declared_memory(dev); | ||
340 | release_mem_region(p->phys_mem_base, p->mem_size); | ||
341 | free_irq(p->irq, p); | ||
342 | kfree(p); | ||
343 | return 0; | ||
344 | } | ||
345 | |||
346 | static short NCR_Q720_id_table[] = { NCR_Q720_MCA_ID, 0 }; | ||
347 | |||
348 | static struct mca_driver NCR_Q720_driver = { | ||
349 | .id_table = NCR_Q720_id_table, | ||
350 | .driver = { | ||
351 | .name = "NCR_Q720", | ||
352 | .bus = &mca_bus_type, | ||
353 | .probe = NCR_Q720_probe, | ||
354 | .remove = __devexit_p(NCR_Q720_remove), | ||
355 | }, | ||
356 | }; | ||
357 | |||
358 | static int __init | ||
359 | NCR_Q720_init(void) | ||
360 | { | ||
361 | int ret = ncr53c8xx_init(); | ||
362 | if (!ret) | ||
363 | ret = mca_register_driver(&NCR_Q720_driver); | ||
364 | if (ret) | ||
365 | ncr53c8xx_exit(); | ||
366 | return ret; | ||
367 | } | ||
368 | |||
369 | static void __exit | ||
370 | NCR_Q720_exit(void) | ||
371 | { | ||
372 | mca_unregister_driver(&NCR_Q720_driver); | ||
373 | ncr53c8xx_exit(); | ||
374 | } | ||
375 | |||
376 | module_init(NCR_Q720_init); | ||
377 | module_exit(NCR_Q720_exit); | ||