Commit | Line | Data |
---|---|---|
e84de0c6 TB |
1 | #include <linux/export.h> |
2 | #include <linux/kernel.h> | |
3 | #include <linux/init.h> | |
4 | #include <linux/slab.h> | |
5 | ||
6 | #include <asm/addrspace.h> | |
7 | #include <asm/paccess.h> | |
8 | #include <asm/gio_device.h> | |
9 | #include <asm/sgi/gio.h> | |
10 | #include <asm/sgi/hpc3.h> | |
11 | #include <asm/sgi/mc.h> | |
12 | #include <asm/sgi/ip22.h> | |
13 | ||
14 | static struct bus_type gio_bus_type; | |
15 | ||
16 | static struct { | |
17 | const char *name; | |
70342287 | 18 | __u8 id; |
e84de0c6 TB |
19 | } gio_name_table[] = { |
20 | { .name = "SGI Impact", .id = 0x10 }, | |
21 | { .name = "Phobos G160", .id = 0x35 }, | |
1d421ca9 TB |
22 | { .name = "Phobos G130", .id = 0x36 }, |
23 | { .name = "Phobos G100", .id = 0x37 }, | |
24 | { .name = "Set Engineering GFE", .id = 0x38 }, | |
e84de0c6 TB |
25 | /* fake IDs */ |
26 | { .name = "SGI Newport", .id = 0x7e }, | |
27 | { .name = "SGI GR2/GR3", .id = 0x7f }, | |
28 | }; | |
29 | ||
82242d28 LK |
30 | static void gio_bus_release(struct device *dev) |
31 | { | |
32 | kfree(dev); | |
33 | } | |
34 | ||
e84de0c6 TB |
35 | static struct device gio_bus = { |
36 | .init_name = "gio", | |
82242d28 | 37 | .release = &gio_bus_release, |
e84de0c6 TB |
38 | }; |
39 | ||
40 | /** | |
41 | * gio_match_device - Tell if an of_device structure has a matching | |
42 | * gio_match structure | |
43 | * @ids: array of of device match structures to search in | |
44 | * @dev: the of device structure to match against | |
45 | * | |
46 | * Used by a driver to check whether an of_device present in the | |
47 | * system is in its list of supported devices. | |
48 | */ | |
49 | const struct gio_device_id *gio_match_device(const struct gio_device_id *match, | |
50 | const struct gio_device *dev) | |
51 | { | |
52 | const struct gio_device_id *ids; | |
53 | ||
54 | for (ids = match; ids->id != 0xff; ids++) | |
55 | if (ids->id == dev->id.id) | |
56 | return ids; | |
57 | ||
58 | return NULL; | |
59 | } | |
60 | EXPORT_SYMBOL_GPL(gio_match_device); | |
61 | ||
62 | struct gio_device *gio_dev_get(struct gio_device *dev) | |
63 | { | |
64 | struct device *tmp; | |
65 | ||
66 | if (!dev) | |
67 | return NULL; | |
68 | tmp = get_device(&dev->dev); | |
69 | if (tmp) | |
70 | return to_gio_device(tmp); | |
71 | else | |
72 | return NULL; | |
73 | } | |
74 | EXPORT_SYMBOL_GPL(gio_dev_get); | |
75 | ||
76 | void gio_dev_put(struct gio_device *dev) | |
77 | { | |
78 | if (dev) | |
79 | put_device(&dev->dev); | |
80 | } | |
81 | EXPORT_SYMBOL_GPL(gio_dev_put); | |
82 | ||
83 | /** | |
84 | * gio_release_dev - free an gio device structure when all users of it are finished. | |
85 | * @dev: device that's been disconnected | |
86 | * | |
87 | * Will be called only by the device core when all users of this gio device are | |
88 | * done. | |
89 | */ | |
90 | void gio_release_dev(struct device *dev) | |
91 | { | |
92 | struct gio_device *giodev; | |
93 | ||
94 | giodev = to_gio_device(dev); | |
95 | kfree(giodev); | |
96 | } | |
97 | EXPORT_SYMBOL_GPL(gio_release_dev); | |
98 | ||
99 | int gio_device_register(struct gio_device *giodev) | |
100 | { | |
101 | giodev->dev.bus = &gio_bus_type; | |
102 | giodev->dev.parent = &gio_bus; | |
103 | return device_register(&giodev->dev); | |
104 | } | |
105 | EXPORT_SYMBOL_GPL(gio_device_register); | |
106 | ||
107 | void gio_device_unregister(struct gio_device *giodev) | |
108 | { | |
109 | device_unregister(&giodev->dev); | |
110 | } | |
111 | EXPORT_SYMBOL_GPL(gio_device_unregister); | |
112 | ||
113 | static int gio_bus_match(struct device *dev, struct device_driver *drv) | |
114 | { | |
115 | struct gio_device *gio_dev = to_gio_device(dev); | |
116 | struct gio_driver *gio_drv = to_gio_driver(drv); | |
117 | ||
118 | return gio_match_device(gio_drv->id_table, gio_dev) != NULL; | |
119 | } | |
120 | ||
121 | static int gio_device_probe(struct device *dev) | |
122 | { | |
123 | int error = -ENODEV; | |
124 | struct gio_driver *drv; | |
125 | struct gio_device *gio_dev; | |
126 | const struct gio_device_id *match; | |
127 | ||
128 | drv = to_gio_driver(dev->driver); | |
129 | gio_dev = to_gio_device(dev); | |
130 | ||
131 | if (!drv->probe) | |
132 | return error; | |
133 | ||
134 | gio_dev_get(gio_dev); | |
135 | ||
136 | match = gio_match_device(drv->id_table, gio_dev); | |
137 | if (match) | |
138 | error = drv->probe(gio_dev, match); | |
139 | if (error) | |
140 | gio_dev_put(gio_dev); | |
141 | ||
142 | return error; | |
143 | } | |
144 | ||
145 | static int gio_device_remove(struct device *dev) | |
146 | { | |
147 | struct gio_device *gio_dev = to_gio_device(dev); | |
148 | struct gio_driver *drv = to_gio_driver(dev->driver); | |
149 | ||
150 | if (dev->driver && drv->remove) | |
151 | drv->remove(gio_dev); | |
152 | return 0; | |
153 | } | |
154 | ||
e84de0c6 TB |
155 | static void gio_device_shutdown(struct device *dev) |
156 | { | |
157 | struct gio_device *gio_dev = to_gio_device(dev); | |
158 | struct gio_driver *drv = to_gio_driver(dev->driver); | |
159 | ||
160 | if (dev->driver && drv->shutdown) | |
161 | drv->shutdown(gio_dev); | |
162 | } | |
163 | ||
164 | static ssize_t modalias_show(struct device *dev, struct device_attribute *a, | |
165 | char *buf) | |
166 | { | |
167 | struct gio_device *gio_dev = to_gio_device(dev); | |
168 | int len = snprintf(buf, PAGE_SIZE, "gio:%x\n", gio_dev->id.id); | |
169 | ||
170 | return (len >= PAGE_SIZE) ? (PAGE_SIZE - 1) : len; | |
171 | } | |
172 | ||
173 | static ssize_t name_show(struct device *dev, | |
174 | struct device_attribute *attr, char *buf) | |
175 | { | |
176 | struct gio_device *giodev; | |
177 | ||
178 | giodev = to_gio_device(dev); | |
179 | return sprintf(buf, "%s", giodev->name); | |
180 | } | |
181 | ||
182 | static ssize_t id_show(struct device *dev, | |
183 | struct device_attribute *attr, char *buf) | |
184 | { | |
185 | struct gio_device *giodev; | |
186 | ||
187 | giodev = to_gio_device(dev); | |
188 | return sprintf(buf, "%x", giodev->id.id); | |
189 | } | |
190 | ||
191 | static struct device_attribute gio_dev_attrs[] = { | |
192 | __ATTR_RO(modalias), | |
193 | __ATTR_RO(name), | |
194 | __ATTR_RO(id), | |
195 | __ATTR_NULL, | |
196 | }; | |
197 | ||
198 | static int gio_device_uevent(struct device *dev, struct kobj_uevent_env *env) | |
199 | { | |
200 | struct gio_device *gio_dev = to_gio_device(dev); | |
201 | ||
202 | add_uevent_var(env, "MODALIAS=gio:%x", gio_dev->id.id); | |
203 | return 0; | |
204 | } | |
205 | ||
206 | int gio_register_driver(struct gio_driver *drv) | |
207 | { | |
208 | /* initialize common driver fields */ | |
209 | if (!drv->driver.name) | |
210 | drv->driver.name = drv->name; | |
211 | if (!drv->driver.owner) | |
212 | drv->driver.owner = drv->owner; | |
213 | drv->driver.bus = &gio_bus_type; | |
214 | ||
215 | /* register with core */ | |
216 | return driver_register(&drv->driver); | |
217 | } | |
218 | EXPORT_SYMBOL_GPL(gio_register_driver); | |
219 | ||
220 | void gio_unregister_driver(struct gio_driver *drv) | |
221 | { | |
222 | driver_unregister(&drv->driver); | |
223 | } | |
224 | EXPORT_SYMBOL_GPL(gio_unregister_driver); | |
225 | ||
226 | void gio_set_master(struct gio_device *dev) | |
227 | { | |
228 | u32 tmp = sgimc->giopar; | |
229 | ||
230 | switch (dev->slotno) { | |
231 | case 0: | |
232 | tmp |= SGIMC_GIOPAR_MASTERGFX; | |
233 | break; | |
234 | case 1: | |
235 | tmp |= SGIMC_GIOPAR_MASTEREXP0; | |
236 | break; | |
237 | case 2: | |
238 | tmp |= SGIMC_GIOPAR_MASTEREXP1; | |
239 | break; | |
240 | } | |
241 | sgimc->giopar = tmp; | |
242 | } | |
243 | EXPORT_SYMBOL_GPL(gio_set_master); | |
244 | ||
245 | void ip22_gio_set_64bit(int slotno) | |
246 | { | |
247 | u32 tmp = sgimc->giopar; | |
248 | ||
249 | switch (slotno) { | |
250 | case 0: | |
251 | tmp |= SGIMC_GIOPAR_GFX64; | |
252 | break; | |
253 | case 1: | |
254 | tmp |= SGIMC_GIOPAR_EXP064; | |
255 | break; | |
256 | case 2: | |
257 | tmp |= SGIMC_GIOPAR_EXP164; | |
258 | break; | |
259 | } | |
260 | sgimc->giopar = tmp; | |
261 | } | |
262 | ||
263 | static int ip22_gio_id(unsigned long addr, u32 *res) | |
264 | { | |
265 | u8 tmp8; | |
266 | u8 tmp16; | |
267 | u32 tmp32; | |
268 | u8 *ptr8; | |
269 | u16 *ptr16; | |
270 | u32 *ptr32; | |
271 | ||
272 | ptr32 = (void *)CKSEG1ADDR(addr); | |
273 | if (!get_dbe(tmp32, ptr32)) { | |
274 | /* | |
275 | * We got no DBE, but this doesn't mean anything. | |
276 | * If GIO is pipelined (which can't be disabled | |
277 | * for GFX slot) we don't get a DBE, but we see | |
278 | * the transfer size as data. So we do an 8bit | |
279 | * and a 16bit access and check whether the common | |
280 | * data matches | |
281 | */ | |
282 | ptr8 = (void *)CKSEG1ADDR(addr + 3); | |
1d421ca9 TB |
283 | if (get_dbe(tmp8, ptr8)) { |
284 | /* | |
285 | * 32bit access worked, but 8bit doesn't | |
286 | * so we don't see phantom reads on | |
287 | * a pipelined bus, but a real card which | |
288 | * doesn't support 8 bit reads | |
289 | */ | |
290 | *res = tmp32; | |
291 | return 1; | |
292 | } | |
e84de0c6 TB |
293 | ptr16 = (void *)CKSEG1ADDR(addr + 2); |
294 | get_dbe(tmp16, ptr16); | |
295 | if (tmp8 == (tmp16 & 0xff) && | |
296 | tmp8 == (tmp32 & 0xff) && | |
297 | tmp16 == (tmp32 & 0xffff)) { | |
298 | *res = tmp32; | |
299 | return 1; | |
300 | } | |
301 | } | |
302 | return 0; /* nothing here */ | |
303 | } | |
304 | ||
305 | #define HQ2_MYSTERY_OFFS 0x6A07C | |
306 | #define NEWPORT_USTATUS_OFFS 0xF133C | |
307 | ||
308 | static int ip22_is_gr2(unsigned long addr) | |
309 | { | |
310 | u32 tmp; | |
311 | u32 *ptr; | |
312 | ||
313 | /* HQ2 only allows 32bit accesses */ | |
314 | ptr = (void *)CKSEG1ADDR(addr + HQ2_MYSTERY_OFFS); | |
315 | if (!get_dbe(tmp, ptr)) { | |
316 | if (tmp == 0xdeadbeef) | |
317 | return 1; | |
318 | } | |
319 | return 0; | |
320 | } | |
321 | ||
322 | ||
1d421ca9 | 323 | static void ip22_check_gio(int slotno, unsigned long addr, int irq) |
e84de0c6 TB |
324 | { |
325 | const char *name = "Unknown"; | |
326 | struct gio_device *gio_dev; | |
327 | u32 tmp; | |
328 | __u8 id; | |
329 | int i; | |
330 | ||
331 | /* first look for GR2/GR3 by checking mystery register */ | |
332 | if (ip22_is_gr2(addr)) | |
333 | tmp = 0x7f; | |
334 | else { | |
335 | if (!ip22_gio_id(addr, &tmp)) { | |
336 | /* | |
1d421ca9 TB |
337 | * no GIO signature at start address of slot |
338 | * since Newport doesn't have one, we check if | |
339 | * user status register is readable | |
e84de0c6 TB |
340 | */ |
341 | if (ip22_gio_id(addr + NEWPORT_USTATUS_OFFS, &tmp)) | |
342 | tmp = 0x7e; | |
343 | else | |
344 | tmp = 0; | |
345 | } | |
346 | } | |
347 | if (tmp) { | |
348 | id = GIO_ID(tmp); | |
349 | if (tmp & GIO_32BIT_ID) { | |
350 | if (tmp & GIO_64BIT_IFACE) | |
351 | ip22_gio_set_64bit(slotno); | |
352 | } | |
353 | for (i = 0; i < ARRAY_SIZE(gio_name_table); i++) { | |
354 | if (id == gio_name_table[i].id) { | |
355 | name = gio_name_table[i].name; | |
356 | break; | |
357 | } | |
358 | } | |
359 | printk(KERN_INFO "GIO: slot %d : %s (id %x)\n", | |
360 | slotno, name, id); | |
361 | gio_dev = kzalloc(sizeof *gio_dev, GFP_KERNEL); | |
362 | gio_dev->name = name; | |
363 | gio_dev->slotno = slotno; | |
364 | gio_dev->id.id = id; | |
365 | gio_dev->resource.start = addr; | |
366 | gio_dev->resource.end = addr + 0x3fffff; | |
367 | gio_dev->resource.flags = IORESOURCE_MEM; | |
1d421ca9 | 368 | gio_dev->irq = irq; |
e84de0c6 TB |
369 | dev_set_name(&gio_dev->dev, "%d", slotno); |
370 | gio_device_register(gio_dev); | |
371 | } else | |
372 | printk(KERN_INFO "GIO: slot %d : Empty\n", slotno); | |
373 | } | |
374 | ||
375 | static struct bus_type gio_bus_type = { | |
70342287 | 376 | .name = "gio", |
e84de0c6 | 377 | .dev_attrs = gio_dev_attrs, |
70342287 RB |
378 | .match = gio_bus_match, |
379 | .probe = gio_device_probe, | |
380 | .remove = gio_device_remove, | |
e84de0c6 | 381 | .shutdown = gio_device_shutdown, |
70342287 | 382 | .uevent = gio_device_uevent, |
e84de0c6 TB |
383 | }; |
384 | ||
385 | static struct resource gio_bus_resource = { | |
386 | .start = GIO_SLOT_GFX_BASE, | |
387 | .end = GIO_SLOT_GFX_BASE + 0x9fffff, | |
388 | .name = "GIO Bus", | |
389 | .flags = IORESOURCE_MEM, | |
390 | }; | |
391 | ||
392 | int __init ip22_gio_init(void) | |
393 | { | |
394 | unsigned int pbdma __maybe_unused; | |
395 | int ret; | |
396 | ||
397 | ret = device_register(&gio_bus); | |
82242d28 LK |
398 | if (ret) { |
399 | put_device(&gio_bus); | |
e84de0c6 | 400 | return ret; |
82242d28 | 401 | } |
e84de0c6 TB |
402 | |
403 | ret = bus_register(&gio_bus_type); | |
404 | if (!ret) { | |
405 | request_resource(&iomem_resource, &gio_bus_resource); | |
406 | printk(KERN_INFO "GIO: Probing bus...\n"); | |
407 | ||
1d421ca9 TB |
408 | if (ip22_is_fullhouse()) { |
409 | /* Indigo2 */ | |
410 | ip22_check_gio(0, GIO_SLOT_GFX_BASE, SGI_GIO_1_IRQ); | |
411 | ip22_check_gio(1, GIO_SLOT_EXP0_BASE, SGI_GIO_1_IRQ); | |
e84de0c6 | 412 | } else { |
1d421ca9 TB |
413 | /* Indy/Challenge S */ |
414 | if (get_dbe(pbdma, (unsigned int *)&hpc3c1->pbdma[1])) | |
415 | ip22_check_gio(0, GIO_SLOT_GFX_BASE, | |
416 | SGI_GIO_0_IRQ); | |
417 | ip22_check_gio(1, GIO_SLOT_EXP0_BASE, SGI_GIOEXP0_IRQ); | |
418 | ip22_check_gio(2, GIO_SLOT_EXP1_BASE, SGI_GIOEXP1_IRQ); | |
e84de0c6 TB |
419 | } |
420 | } else | |
421 | device_unregister(&gio_bus); | |
422 | ||
423 | return ret; | |
424 | } | |
425 | ||
426 | subsys_initcall(ip22_gio_init); |