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