| 1 | |
| 2 | |
| 3 | * Copyright (C) 05/1998 Peter Maydell <[email protected]>
|
| 4 | * Copyright (C) 2004 Jochen Friedrich <[email protected]>
|
| 5 | *
|
| 6 | * This code has basically these routines at the moment:
|
| 7 | * int dio_find(u_int deviceid)
|
| 8 | * Search the list of DIO devices and return the select code
|
| 9 | * of the next unconfigured device found that matches the given device ID.
|
| 10 | * Note that the deviceid parameter should be the encoded ID.
|
| 11 | * This means that framebuffers should pass it as
|
| 12 | * DIO_ENCODE_ID(DIO_ID_FBUFFER,DIO_ID2_TOPCAT)
|
| 13 | * (or whatever); everybody else just uses DIO_ID_FOOBAR.
|
| 14 | * unsigned long dio_scodetophysaddr(int scode)
|
| 15 | * Return the physical address corresponding to the given select code.
|
| 16 | * int dio_scodetoipl(int scode)
|
| 17 | * Every DIO card has a fixed interrupt priority level. This function
|
| 18 | * returns it, whatever it is.
|
| 19 | * const char *dio_scodetoname(int scode)
|
| 20 | * Return a character string describing this board [might be "" if
|
| 21 | * not CONFIG_DIO_CONSTANTS]
|
| 22 | * void dio_config_board(int scode) mark board as configured in the list
|
| 23 | * void dio_unconfig_board(int scode) mark board as no longer configured
|
| 24 | *
|
| 25 | * This file is based on the way the Amiga port handles Zorro II cards,
|
| 26 | * although we aren't so complicated...
|
| 27 |
|
| 28 | #include <linux/module.h>
|
| 29 | #include <linux/string.h>
|
| 30 | #include <linux/types.h>
|
| 31 | #include <linux/kernel.h>
|
| 32 | #include <linux/init.h>
|
| 33 | #include <linux/dio.h>
|
| 34 | #include <linux/slab.h>
|
| 35 | #include <linux/uaccess.h>
|
| 36 | #include <linux/io.h>
|
| 37 |
|
| 38 | struct dio_bus dio_bus = {
|
| 39 | .resources = {
|
| 40 |
|
| 41 | { .name = "DIO mem", .start = 0x00600000, .end = 0x007fffff },
|
| 42 |
|
| 43 | { .name = "DIO-II mem", .start = 0x01000000, .end = 0x1fffffff }
|
| 44 | },
|
| 45 | .name = "DIO bus"
|
| 46 | };
|
| 47 |
|
| 48 |
|
| 49 | #define CONFIG_DIO_CONSTANTS
|
| 50 |
|
| 51 | #ifdef CONFIG_DIO_CONSTANTS
|
| 52 | |
| 53 | * using a constant array of these structs.
|
| 54 | * FIXME: we should be able to arrange to throw away most of the strings
|
| 55 | * using the initdata stuff. Then we wouldn't need to worry about
|
| 56 | * carrying them around...
|
| 57 | * I think we do this by copying them into newly kmalloc()ed memory and
|
| 58 | * marking the names[] array as .initdata ?
|
| 59 |
|
| 60 | struct dioname {
|
| 61 | int id;
|
| 62 | const char *name;
|
| 63 | };
|
| 64 |
|
| 65 |
|
| 66 | #define DIONAME(x) { DIO_ID_##x, DIO_DESC_##x }
|
| 67 | #define DIOFBNAME(x) { DIO_ENCODE_ID(DIO_ID_FBUFFER, DIO_ID2_##x), DIO_DESC2_##x }
|
| 68 |
|
| 69 | static struct dioname names[] = {
|
| 70 | DIONAME(DCA0), DIONAME(DCA0REM), DIONAME(DCA1), DIONAME(DCA1REM),
|
| 71 | DIONAME(DCM), DIONAME(DCMREM),
|
| 72 | DIONAME(LAN),
|
| 73 | DIONAME(FHPIB), DIONAME(NHPIB),
|
| 74 | DIONAME(SCSI0), DIONAME(SCSI1), DIONAME(SCSI2), DIONAME(SCSI3),
|
| 75 | DIONAME(FBUFFER),
|
| 76 | DIONAME(PARALLEL), DIONAME(VME), DIONAME(DCL), DIONAME(DCLREM),
|
| 77 | DIONAME(MISC0), DIONAME(MISC1), DIONAME(MISC2), DIONAME(MISC3),
|
| 78 | DIONAME(MISC4), DIONAME(MISC5), DIONAME(MISC6), DIONAME(MISC7),
|
| 79 | DIONAME(MISC8), DIONAME(MISC9), DIONAME(MISC10), DIONAME(MISC11),
|
| 80 | DIONAME(MISC12), DIONAME(MISC13),
|
| 81 | DIOFBNAME(GATORBOX), DIOFBNAME(TOPCAT), DIOFBNAME(RENAISSANCE),
|
| 82 | DIOFBNAME(LRCATSEYE), DIOFBNAME(HRCCATSEYE), DIOFBNAME(HRMCATSEYE),
|
| 83 | DIOFBNAME(DAVINCI), DIOFBNAME(XXXCATSEYE), DIOFBNAME(HYPERION),
|
| 84 | DIOFBNAME(XGENESIS), DIOFBNAME(TIGER), DIOFBNAME(YGENESIS)
|
| 85 | };
|
| 86 |
|
| 87 | #undef DIONAME
|
| 88 | #undef DIOFBNAME
|
| 89 |
|
| 90 | static const char unknowndioname[]
|
| 91 | = "unknown DIO board, please email [email protected]";
|
| 92 |
|
| 93 | static const char *dio_getname(int id)
|
| 94 | {
|
| 95 |
|
| 96 | unsigned int i;
|
| 97 |
|
| 98 | for (i = 0; i < ARRAY_SIZE(names); i++)
|
| 99 | if (names[i].id == id)
|
| 100 | return names[i].name;
|
| 101 |
|
| 102 | return unknowndioname;
|
| 103 | }
|
| 104 |
|
| 105 | #else
|
| 106 |
|
| 107 | static char dio_no_name[] = { 0 };
|
| 108 | #define dio_getname(_id) (dio_no_name)
|
| 109 |
|
| 110 | #endif
|
| 111 |
|
| 112 | static void dio_dev_release(struct device *dev)
|
| 113 | {
|
| 114 | struct dio_dev *ddev = container_of(dev, typeof(struct dio_dev), dev);
|
| 115 | kfree(ddev);
|
| 116 | }
|
| 117 |
|
| 118 | int __init dio_find(int deviceid)
|
| 119 | {
|
| 120 | |
| 121 | * Only used by the console driver.
|
| 122 |
|
| 123 | int scode, id;
|
| 124 | u_char prid, secid, i;
|
| 125 |
|
| 126 | for (scode = 0; scode < DIO_SCMAX; scode++) {
|
| 127 | void *va;
|
| 128 | unsigned long pa;
|
| 129 |
|
| 130 | if (DIO_SCINHOLE(scode))
|
| 131 | continue;
|
| 132 |
|
| 133 | pa = dio_scodetophysaddr(scode);
|
| 134 |
|
| 135 | if (!pa)
|
| 136 | continue;
|
| 137 |
|
| 138 | if (scode < DIOII_SCBASE)
|
| 139 | va = (void *)(pa + DIO_VIRADDRBASE);
|
| 140 | else
|
| 141 | va = ioremap(pa, PAGE_SIZE);
|
| 142 |
|
| 143 | if (copy_from_kernel_nofault(&i,
|
| 144 | (unsigned char *)va + DIO_IDOFF, 1)) {
|
| 145 | if (scode >= DIOII_SCBASE)
|
| 146 | iounmap(va);
|
| 147 | continue;
|
| 148 | }
|
| 149 |
|
| 150 | prid = DIO_ID(va);
|
| 151 |
|
| 152 | if (DIO_NEEDSSECID(prid)) {
|
| 153 | secid = DIO_SECID(va);
|
| 154 | id = DIO_ENCODE_ID(prid, secid);
|
| 155 | } else
|
| 156 | id = prid;
|
| 157 |
|
| 158 | if (id == deviceid) {
|
| 159 | if (scode >= DIOII_SCBASE)
|
| 160 | iounmap(va);
|
| 161 | return scode;
|
| 162 | }
|
| 163 | }
|
| 164 |
|
| 165 | return -1;
|
| 166 | }
|
| 167 |
|
| 168 | |
| 169 | * hardware is actually present.
|
| 170 |
|
| 171 | static int __init dio_init(void)
|
| 172 | {
|
| 173 | int scode;
|
| 174 | int i;
|
| 175 | struct dio_dev *dev;
|
| 176 | int error;
|
| 177 |
|
| 178 | if (!MACH_IS_HP300)
|
| 179 | return 0;
|
| 180 |
|
| 181 | pr_info("Scanning for DIO devices...\n");
|
| 182 |
|
| 183 |
|
| 184 | INIT_LIST_HEAD(&dio_bus.devices);
|
| 185 | dev_set_name(&dio_bus.dev, "dio");
|
| 186 | error = device_register(&dio_bus.dev);
|
| 187 | if (error) {
|
| 188 | pr_err("DIO: Error registering dio_bus\n");
|
| 189 | return error;
|
| 190 | }
|
| 191 |
|
| 192 |
|
| 193 | dio_bus.num_resources = (hp300_model == HP_320 ? 1 : 2);
|
| 194 | for (i = 0; i < dio_bus.num_resources; i++)
|
| 195 | request_resource(&iomem_resource, &dio_bus.resources[i]);
|
| 196 |
|
| 197 |
|
| 198 | for (scode = 0; scode < DIO_SCMAX; ++scode) {
|
| 199 | u_char prid, secid = 0;
|
| 200 | u_char *va;
|
| 201 | unsigned long pa;
|
| 202 |
|
| 203 | if (DIO_SCINHOLE(scode))
|
| 204 | continue;
|
| 205 |
|
| 206 | pa = dio_scodetophysaddr(scode);
|
| 207 |
|
| 208 | if (!pa)
|
| 209 | continue;
|
| 210 |
|
| 211 | if (scode < DIOII_SCBASE)
|
| 212 | va = (void *)(pa + DIO_VIRADDRBASE);
|
| 213 | else
|
| 214 | va = ioremap(pa, PAGE_SIZE);
|
| 215 |
|
| 216 | if (copy_from_kernel_nofault(&i,
|
| 217 | (unsigned char *)va + DIO_IDOFF, 1)) {
|
| 218 | if (scode >= DIOII_SCBASE)
|
| 219 | iounmap(va);
|
| 220 | continue;
|
| 221 | }
|
| 222 |
|
| 223 |
|
| 224 | dev = kzalloc_obj(struct dio_dev);
|
| 225 | if (!dev) {
|
| 226 | if (scode >= DIOII_SCBASE)
|
| 227 | iounmap(va);
|
| 228 | return -ENOMEM;
|
| 229 | }
|
| 230 |
|
| 231 | dev->bus = &dio_bus;
|
| 232 | dev->dev.parent = &dio_bus.dev;
|
| 233 | dev->dev.bus = &dio_bus_type;
|
| 234 | dev->dev.release = dio_dev_release;
|
| 235 | dev->scode = scode;
|
| 236 | dev->resource.start = pa;
|
| 237 | dev->resource.end = pa + DIO_SIZE(scode, va);
|
| 238 | dev_set_name(&dev->dev, "%02x", scode);
|
| 239 |
|
| 240 |
|
| 241 | prid = DIO_ID(va);
|
| 242 |
|
| 243 | if (DIO_NEEDSSECID(prid)) {
|
| 244 | secid = DIO_SECID(va);
|
| 245 | dev->id = DIO_ENCODE_ID(prid, secid);
|
| 246 | } else
|
| 247 | dev->id = prid;
|
| 248 |
|
| 249 | dev->ipl = DIO_IPL(va);
|
| 250 | strscpy(dev->name, dio_getname(dev->id));
|
| 251 | if (DIO_NEEDSSECID(prid))
|
| 252 | pr_info("select code %3d: ipl %u: ID %02X:%02X: %s\n",
|
| 253 | dev->scode, dev->ipl, prid, secid, dev->name);
|
| 254 | else
|
| 255 | pr_info("select code %3d: ipl %u: ID %02X: %s\n",
|
| 256 | dev->scode, dev->ipl, prid, dev->name);
|
| 257 |
|
| 258 | if (scode >= DIOII_SCBASE)
|
| 259 | iounmap(va);
|
| 260 | error = device_register(&dev->dev);
|
| 261 | if (error) {
|
| 262 | pr_err("DIO: Error registering device %s\n", dev->name);
|
| 263 | put_device(&dev->dev);
|
| 264 | continue;
|
| 265 | }
|
| 266 | error = dio_create_sysfs_dev_files(dev);
|
| 267 | if (error)
|
| 268 | dev_err(&dev->dev, "Error creating sysfs files\n");
|
| 269 | }
|
| 270 | return 0;
|
| 271 | }
|
| 272 |
|
| 273 | subsys_initcall(dio_init);
|
| 274 |
|
| 275 | |
| 276 | * in order to get the address of the serial port for the console...
|
| 277 |
|
| 278 | unsigned long dio_scodetophysaddr(int scode)
|
| 279 | {
|
| 280 | if (scode >= DIOII_SCBASE)
|
| 281 | return (DIOII_BASE + (scode - 132) * DIOII_DEVSIZE);
|
| 282 | else if (scode > DIO_SCMAX || scode < 0)
|
| 283 | return 0;
|
| 284 | else if (DIO_SCINHOLE(scode))
|
| 285 | return 0;
|
| 286 |
|
| 287 | return (DIO_BASE + scode * DIO_DEVSIZE);
|
| 288 | }
|