]>
Commit | Line | Data |
---|---|---|
1da177e4 LT |
1 | /* -*- mode: c; c-basic-offset: 8 -*- */ |
2 | ||
3 | /* NCR Quad 720 MCA SCSI Driver | |
4 | * | |
5 | * Copyright (C) 2003 by [email protected] | |
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> | |
5a0e3ad6 | 13 | #include <linux/slab.h> |
1da177e4 LT |
14 | #include <linux/types.h> |
15 | #include <linux/init.h> | |
16 | #include <linux/delay.h> | |
17 | #include <asm/io.h> | |
18 | ||
19 | #include "scsi.h" | |
20 | #include <scsi/scsi_host.h> | |
21 | ||
22 | #include "ncr53c8xx.h" | |
23 | ||
24 | #include "NCR_Q720.h" | |
25 | ||
26 | static struct ncr_chip q720_chip __initdata = { | |
27 | .revision_id = 0x0f, | |
28 | .burst_max = 3, | |
29 | .offset_max = 8, | |
30 | .nr_divisor = 4, | |
31 | .features = FE_WIDE | FE_DIFF | FE_VARCLK, | |
32 | }; | |
33 | ||
34 | MODULE_AUTHOR("James Bottomley"); | |
35 | MODULE_DESCRIPTION("NCR Quad 720 SCSI Driver"); | |
36 | MODULE_LICENSE("GPL"); | |
37 | ||
38 | #define NCR_Q720_VERSION "0.9" | |
39 | ||
40 | /* We needs this helper because we have up to four hosts per struct device */ | |
41 | struct NCR_Q720_private { | |
42 | struct device *dev; | |
43 | void __iomem * mem_base; | |
44 | __u32 phys_mem_base; | |
45 | __u32 mem_size; | |
46 | __u8 irq; | |
47 | __u8 siops; | |
48 | __u8 irq_enable; | |
49 | struct Scsi_Host *hosts[4]; | |
50 | }; | |
51 | ||
52 | static struct scsi_host_template NCR_Q720_tpnt = { | |
53 | .module = THIS_MODULE, | |
54 | .proc_name = "NCR_Q720", | |
55 | }; | |
56 | ||
57 | static irqreturn_t | |
7d12e780 | 58 | NCR_Q720_intr(int irq, void *data) |
1da177e4 LT |
59 | { |
60 | struct NCR_Q720_private *p = (struct NCR_Q720_private *)data; | |
61 | __u8 sir = (readb(p->mem_base + 0x0d) & 0xf0) >> 4; | |
62 | __u8 siop; | |
63 | ||
64 | sir |= ~p->irq_enable; | |
65 | ||
66 | if(sir == 0xff) | |
67 | return IRQ_NONE; | |
68 | ||
69 | ||
70 | while((siop = ffz(sir)) < p->siops) { | |
71 | sir |= 1<<siop; | |
7d12e780 | 72 | ncr53c8xx_intr(irq, p->hosts[siop]); |
1da177e4 LT |
73 | } |
74 | return IRQ_HANDLED; | |
75 | } | |
76 | ||
77 | static int __init | |
78 | NCR_Q720_probe_one(struct NCR_Q720_private *p, int siop, | |
79 | int irq, int slot, __u32 paddr, void __iomem *vaddr) | |
80 | { | |
81 | struct ncr_device device; | |
82 | __u8 scsi_id; | |
83 | static int unit = 0; | |
84 | __u8 scsr1 = readb(vaddr + NCR_Q720_SCSR_OFFSET + 1); | |
85 | __u8 differential = readb(vaddr + NCR_Q720_SCSR_OFFSET) & 0x20; | |
86 | __u8 version; | |
87 | int error; | |
88 | ||
89 | scsi_id = scsr1 >> 4; | |
90 | /* enable burst length 16 (FIXME: should allow this) */ | |
91 | scsr1 |= 0x02; | |
92 | /* force a siop reset */ | |
93 | scsr1 |= 0x04; | |
94 | writeb(scsr1, vaddr + NCR_Q720_SCSR_OFFSET + 1); | |
95 | udelay(10); | |
96 | version = readb(vaddr + 0x18) >> 4; | |
97 | ||
98 | memset(&device, 0, sizeof(struct ncr_device)); | |
99 | /* Initialise ncr_device structure with items required by ncr_attach. */ | |
100 | device.chip = q720_chip; | |
101 | device.chip.revision_id = version; | |
102 | device.host_id = scsi_id; | |
103 | device.dev = p->dev; | |
104 | device.slot.base = paddr; | |
105 | device.slot.base_c = paddr; | |
106 | device.slot.base_v = vaddr; | |
107 | device.slot.irq = irq; | |
108 | device.differential = differential ? 2 : 0; | |
109 | printk("Q720 probe unit %d (siop%d) at 0x%lx, diff = %d, vers = %d\n", unit, siop, | |
110 | (unsigned long)paddr, differential, version); | |
111 | ||
112 | p->hosts[siop] = ncr_attach(&NCR_Q720_tpnt, unit++, &device); | |
113 | ||
114 | if (!p->hosts[siop]) | |
115 | goto fail; | |
116 | ||
117 | p->irq_enable |= (1<<siop); | |
118 | scsr1 = readb(vaddr + NCR_Q720_SCSR_OFFSET + 1); | |
119 | /* clear the disable interrupt bit */ | |
120 | scsr1 &= ~0x01; | |
121 | writeb(scsr1, vaddr + NCR_Q720_SCSR_OFFSET + 1); | |
122 | ||
123 | error = scsi_add_host(p->hosts[siop], p->dev); | |
124 | if (error) | |
125 | ncr53c8xx_release(p->hosts[siop]); | |
126 | else | |
127 | scsi_scan_host(p->hosts[siop]); | |
128 | return error; | |
129 | ||
130 | fail: | |
131 | return -ENODEV; | |
132 | } | |
133 | ||
134 | /* Detect a Q720 card. Note, because of the setup --- the chips are | |
135 | * essentially connectecd to the MCA bus independently, it is easier | |
136 | * to set them up as two separate host adapters, rather than one | |
137 | * adapter with two channels */ | |
138 | static int __init | |
139 | NCR_Q720_probe(struct device *dev) | |
140 | { | |
141 | struct NCR_Q720_private *p; | |
142 | static int banner = 1; | |
143 | struct mca_device *mca_dev = to_mca_device(dev); | |
144 | int slot = mca_dev->slot; | |
145 | int found = 0; | |
146 | int irq, i, siops; | |
147 | __u8 pos2, pos4, asr2, asr9, asr10; | |
148 | __u16 io_base; | |
149 | __u32 base_addr, mem_size; | |
150 | void __iomem *mem_base; | |
151 | ||
dd00cc48 | 152 | p = kzalloc(sizeof(*p), GFP_KERNEL); |
1da177e4 LT |
153 | if (!p) |
154 | return -ENOMEM; | |
155 | ||
1da177e4 LT |
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 [email protected]\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 | ||
1d6f359a | 268 | if (request_irq(irq, NCR_Q720_intr, IRQF_SHARED, "NCR_Q720", p)) { |
1da177e4 LT |
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, | |
6f039790 | 354 | .remove = NCR_Q720_remove, |
1da177e4 LT |
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); |