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