1 /* 2 * Copyright 2006, Ingo Weinhold <bonefish@cs.tu-berlin.de>. 3 * All rights reserved. Distributed under the terms of the MIT License. 4 */ 5 /*- 6 * Copyright (C) 2002 Benno Rice. 7 * All rights reserved. 8 * 9 * Redistribution and use in source and binary forms, with or without 10 * modification, are permitted provided that the following conditions 11 * are met: 12 * 1. Redistributions of source code must retain the above copyright 13 * notice, this list of conditions and the following disclaimer. 14 * 2. Redistributions in binary form must reproduce the above copyright 15 * notice, this list of conditions and the following disclaimer in the 16 * documentation and/or other materials provided with the distribution. 17 * 18 * THIS SOFTWARE IS PROVIDED BY Benno Rice ``AS IS'' AND ANY EXPRESS OR 19 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 20 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 21 * IN NO EVENT SHALL TOOLS GMBH BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 22 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 23 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; 24 * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 25 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR 26 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF 27 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28 * 29 * $FreeBSD$ 30 */ 31 32 #include <stdio.h> 33 #include <string.h> 34 35 #include <ByteOrder.h> 36 #include <KernelExport.h> 37 38 #include <AutoDeleter.h> 39 #include <bus/PCI.h> 40 #include <interrupt_controller.h> 41 #include <util/kernel_cpp.h> 42 43 #include "openpic.h" 44 45 46 #define OPENPIC_MODULE_NAME "interrupt_controllers/openpic/device_v1" 47 48 enum { 49 OPENPIC_MIN_REGISTER_SPACE_SIZE = 0x21000, 50 OPENPIC_MAX_REGISTER_SPACE_SIZE = 0x40000, 51 }; 52 53 struct openpic_supported_device { 54 const char *name; 55 uint16 vendor_id; 56 uint16 device_id; 57 uint32 register_offset; 58 }; 59 60 static openpic_supported_device sSupportedDevices[] = { 61 { "Intrepid I/O Controller", 0x106b, 0x003e, 0x40000 }, 62 {} 63 }; 64 65 static device_manager_info *sDeviceManager; 66 static pci_module_info *sPCIBusManager; 67 68 struct openpic_info : interrupt_controller_info { 69 openpic_info() 70 { 71 memset(this, 0, sizeof(openpic_info)); 72 register_area = -1; 73 } 74 75 ~openpic_info() 76 { 77 // unmap registers) 78 if (register_area >= 0) 79 delete_area(register_area); 80 81 // uninit parent node driver 82 if (pci) 83 //XXX do I mean it ? 84 sDeviceManager->put_node(sDeviceManager->get_parent_node(node)); 85 } 86 87 openpic_supported_device *supported_device; 88 device_node *node; 89 pci_device_module_info *pci; 90 pci_device *device; 91 92 addr_t physical_registers; // physical registers base 93 addr_t virtual_registers; // virtual (mapped) 94 // registers base 95 area_id register_area; // register area 96 size_t register_space_size; 97 }; 98 99 100 static openpic_supported_device * 101 openpic_check_supported_device(uint16 vendorID, uint16 deviceID) 102 { 103 for (openpic_supported_device *supportedDevice = sSupportedDevices; 104 supportedDevice->name; 105 supportedDevice++) { 106 if (supportedDevice->vendor_id == vendorID 107 && supportedDevice->device_id == deviceID) { 108 return supportedDevice; 109 } 110 } 111 112 return NULL; 113 } 114 115 116 static uint32 117 openpic_read(openpic_info *info, int reg) 118 { 119 return B_SWAP_INT32(info->pci->read_io_32(info->device, 120 info->virtual_registers + reg)); 121 } 122 123 124 static void 125 openpic_write(openpic_info *info, int reg, uint32 val) 126 { 127 info->pci->write_io_32(info->device, info->virtual_registers + reg, 128 B_SWAP_INT32(val)); 129 } 130 131 132 static int 133 openpic_read_irq(openpic_info *info, int cpu) 134 { 135 return openpic_read(info, OPENPIC_IACK(cpu)) & OPENPIC_VECTOR_MASK; 136 } 137 138 139 static void 140 openpic_eoi(openpic_info *info, int cpu) 141 { 142 openpic_write(info, OPENPIC_EOI(cpu), 0); 143 // the Linux driver does this: 144 //openpic_read(info, OPENPIC_EOI(cpu)); 145 } 146 147 148 static void 149 openpic_enable_irq(openpic_info *info, int irq, int type) 150 { 151 // TODO: Align this code with the sequence recommended in the Open PIC 152 // Specification (v 1.2 section 5.2.2). 153 uint32 x; 154 155 x = openpic_read(info, OPENPIC_SRC_VECTOR(irq)); 156 x &= ~(OPENPIC_IMASK | OPENPIC_SENSE_LEVEL | OPENPIC_SENSE_EDGE); 157 if (type == IRQ_TYPE_LEVEL) 158 x |= OPENPIC_SENSE_LEVEL; 159 else 160 x |= OPENPIC_SENSE_EDGE; 161 openpic_write(info, OPENPIC_SRC_VECTOR(irq), x); 162 } 163 164 165 static void 166 openpic_disable_irq(openpic_info *info, int irq) 167 { 168 uint32 x; 169 170 x = openpic_read(info, OPENPIC_SRC_VECTOR(irq)); 171 x |= OPENPIC_IMASK; 172 openpic_write(info, OPENPIC_SRC_VECTOR(irq), x); 173 } 174 175 176 static void 177 openpic_set_priority(openpic_info *info, int cpu, int pri) 178 { 179 uint32 x; 180 181 x = openpic_read(info, OPENPIC_CPU_PRIORITY(cpu)); 182 x &= ~OPENPIC_CPU_PRIORITY_MASK; 183 x |= pri; 184 openpic_write(info, OPENPIC_CPU_PRIORITY(cpu), x); 185 } 186 187 188 static status_t 189 openpic_init(openpic_info *info) 190 { 191 uint32 x = openpic_read(info, OPENPIC_FEATURE); 192 const char *featureVersion; 193 char versionBuffer[64]; 194 switch (x & OPENPIC_FEATURE_VERSION_MASK) { 195 case 1: 196 featureVersion = "1.0"; 197 break; 198 case 2: 199 featureVersion = "1.2"; 200 break; 201 case 3: 202 featureVersion = "1.3"; 203 break; 204 default: 205 snprintf(versionBuffer, sizeof(versionBuffer), 206 "unknown (feature reg: 0x%lx)", x); 207 featureVersion = versionBuffer; 208 break; 209 } 210 211 info->cpu_count = ((x & OPENPIC_FEATURE_LAST_CPU_MASK) >> 212 OPENPIC_FEATURE_LAST_CPU_SHIFT) + 1; 213 info->irq_count = ((x & OPENPIC_FEATURE_LAST_IRQ_MASK) >> 214 OPENPIC_FEATURE_LAST_IRQ_SHIFT) + 1; 215 216 /* 217 * PSIM seems to report 1 too many IRQs 218 */ 219 // if (sc->sc_psim) 220 // sc->sc_nirq--; 221 222 dprintf("openpic: Version %s, supports %d CPUs and %d irqs\n", 223 featureVersion, info->cpu_count, info->irq_count); 224 225 /* disable all interrupts */ 226 for (int irq = 0; irq < info->irq_count; irq++) 227 openpic_write(info, OPENPIC_SRC_VECTOR(irq), OPENPIC_IMASK); 228 229 openpic_set_priority(info, 0, 15); 230 231 /* we don't need 8259 passthrough mode */ 232 x = openpic_read(info, OPENPIC_CONFIG); 233 x |= OPENPIC_CONFIG_8259_PASSTHRU_DISABLE; 234 openpic_write(info, OPENPIC_CONFIG, x); 235 236 /* send all interrupts to cpu 0 */ 237 for (int irq = 0; irq < info->irq_count; irq++) 238 openpic_write(info, OPENPIC_IDEST(irq), 1 << 0); 239 240 for (int irq = 0; irq < info->irq_count; irq++) { 241 x = irq; 242 x |= OPENPIC_IMASK; 243 x |= OPENPIC_POLARITY_POSITIVE; 244 x |= OPENPIC_SENSE_LEVEL; 245 x |= 8 << OPENPIC_PRIORITY_SHIFT; 246 openpic_write(info, OPENPIC_SRC_VECTOR(irq), x); 247 } 248 249 /* XXX IPI */ 250 /* XXX set spurious intr vector */ 251 252 openpic_set_priority(info, 0, 0); 253 254 /* clear all pending interrupts */ 255 for (int irq = 0; irq < info->irq_count; irq++) { 256 openpic_read_irq(info, 0); 257 openpic_eoi(info, 0); 258 } 259 260 return B_OK; 261 } 262 263 264 // #pragma mark - driver interface 265 266 267 static status_t 268 openpic_std_ops(int32 op, ...) 269 { 270 switch (op) { 271 case B_MODULE_INIT: 272 case B_MODULE_UNINIT: 273 return B_OK; 274 275 default: 276 return B_ERROR; 277 } 278 } 279 280 281 static float 282 openpic_supports_device(device_node *parent) 283 { 284 const char *bus; 285 uint16 vendorID; 286 uint16 deviceID; 287 288 // get the bus (should be PCI) 289 if (sDeviceManager->get_attr_string(parent, B_DEVICE_BUS, &bus, false) 290 != B_OK) { 291 return B_ERROR; 292 } 293 294 // get vendor and device ID 295 if (sDeviceManager->get_attr_uint16(parent, B_DEVICE_VENDOR_ID, 296 &vendorID, false) != B_OK 297 || sDeviceManager->get_attr_uint16(parent, B_DEVICE_ID, 298 &deviceID, false) != B_OK) { 299 return B_ERROR; 300 } 301 302 // check, whether bus, vendor and device ID match 303 if (strcmp(bus, "pci") != 0 304 || !openpic_check_supported_device(vendorID, deviceID)) { 305 return 0.0; 306 } 307 308 return 0.6; 309 } 310 311 312 static status_t 313 openpic_register_device(device_node *parent) 314 { 315 #if 0 //XXX: what do I do ? 316 // get interface to PCI device 317 pci_device_module_info *pci; 318 pci_device *device; 319 driver_module_info *driver; 320 void *cookie; 321 status_t error; 322 error = sDeviceManager->get_driver(parent, &driver, &cookie); 323 if (error < B_OK) 324 return error; 325 error = driver->init_driver(parent, cookie); 326 // (driver_module_info**)&pci, (void**)&device); // wtf? 327 if (error != B_OK) 328 return error; 329 330 sDeviceManager->uninit_driver(parent); 331 #endif 332 device_node *newNode; 333 device_attr attrs[] = { 334 // info about ourself 335 //{ B_DRIVER_MODULE, B_STRING_TYPE, { string: OPENPIC_MODULE_NAME }}, 336 //XXX: that's inconsistent with the header! 337 //{ B_DEVICE_TYPE, B_STRING_TYPE, 338 // { string: B_INTERRUPT_CONTROLLER_DRIVER_TYPE }}, 339 340 {} 341 }; 342 343 // HACK: to get it compiled, I will break anything. 344 return sDeviceManager->register_node(parent, NULL, attrs, NULL, &newNode); 345 } 346 347 348 static status_t 349 openpic_init_driver(device_node *node, void **cookie) 350 { 351 // OK, this module is broken for now. But it compiles. 352 return B_ERROR; 353 openpic_info *info = new(nothrow) openpic_info; 354 if (!info) 355 return B_NO_MEMORY; 356 ObjectDeleter<openpic_info> infoDeleter(info); 357 358 info->node = node; 359 360 // get interface to PCI device 361 void *aCookie; 362 void *anotherCookie; // possibly the same cookie. 363 driver_module_info *driver; 364 status_t status = sDeviceManager->get_driver(sDeviceManager->get_parent_node(node), 365 &driver, &aCookie); 366 if (status != B_OK) 367 return status; 368 369 driver->init_driver(node, &anotherCookie); 370 371 /* status = sDeviceManager->init_driver( 372 sDeviceManager->get_parent(node), NULL, 373 (driver_module_info**)&info->pci, (void**)&info->device); 374 if (status != B_OK) 375 return status; */ 376 377 // get the pci info for the device 378 pci_info pciInfo; 379 info->pci->get_pci_info(info->device, &pciInfo); 380 381 // find supported device info 382 info->supported_device = openpic_check_supported_device(pciInfo.vendor_id, 383 pciInfo.device_id); 384 if (!info->supported_device) { 385 dprintf("openpic: device (0x%04hx:0x%04hx) not supported\n", 386 pciInfo.vendor_id, pciInfo.device_id); 387 return B_ERROR; 388 } 389 dprintf("openpic: found supported device: %s (0x%04hx:0x%04hx)\n", 390 info->supported_device->name, pciInfo.vendor_id, pciInfo.device_id); 391 392 // get register space 393 addr_t physicalRegisterBase = pciInfo.u.h0.base_registers[0]; 394 uint32 registerSpaceSize = pciInfo.u.h0.base_register_sizes[0]; 395 if (registerSpaceSize < info->supported_device->register_offset 396 || registerSpaceSize - info->supported_device->register_offset 397 < OPENPIC_MIN_REGISTER_SPACE_SIZE) { 398 dprintf("openpic: register space too small\n"); 399 } 400 physicalRegisterBase += info->supported_device->register_offset; 401 registerSpaceSize -= info->supported_device->register_offset; 402 if (registerSpaceSize > OPENPIC_MAX_REGISTER_SPACE_SIZE) 403 registerSpaceSize = OPENPIC_MAX_REGISTER_SPACE_SIZE; 404 405 // map register space 406 void *virtualRegisterBase = NULL; 407 area_id registerArea = map_physical_memory("openpic registers", 408 (void*)physicalRegisterBase, registerSpaceSize, B_ANY_KERNEL_ADDRESS, 409 B_KERNEL_READ_AREA | B_KERNEL_WRITE_AREA, &virtualRegisterBase); 410 if (registerArea < 0) 411 return info->register_area; 412 413 info->physical_registers = physicalRegisterBase; 414 info->register_space_size = registerSpaceSize; 415 info->register_area = registerArea; 416 info->virtual_registers = (addr_t)virtualRegisterBase; 417 418 // init the controller 419 status = openpic_init(info); 420 if (status != B_OK) 421 return status; 422 423 // keep the info 424 infoDeleter.Detach(); 425 *cookie = info; 426 427 dprintf("openpic_init_driver(): Successfully initialized!\n"); 428 429 return B_OK; 430 } 431 432 433 static void 434 openpic_uninit_driver(void *cookie) 435 { 436 openpic_info *info = (openpic_info*)cookie; 437 438 delete info; 439 } 440 441 442 static void 443 openpic_device_removed(void *driverCookie) 444 { 445 // TODO: ... 446 } 447 448 449 // FIXME: I don't think this is needed... 450 /*static void 451 openpic_get_paths(const char **_bus, const char **_device) 452 { 453 static const char *kBus[] = { "pci", NULL }; 454 // static const char *kDevice[] = { "drivers/dev/disk/ide", NULL }; 455 456 *_bus = kBus; 457 // *_device = kDevice; 458 *_device = NULL; 459 }*/ 460 461 462 // #pragma mark - interrupt_controller interface 463 464 465 static status_t 466 openpic_get_controller_info(void *cookie, interrupt_controller_info *_info) 467 { 468 if (!_info) 469 return B_BAD_VALUE; 470 471 openpic_info *info = (openpic_info*)cookie; 472 473 *_info = *info; 474 475 return B_OK; 476 } 477 478 479 static status_t 480 openpic_enable_io_interrupt(void *cookie, int irq, int type) 481 { 482 openpic_info *info = (openpic_info*)cookie; 483 484 openpic_enable_irq(info, irq, type); 485 486 return B_OK; 487 } 488 489 490 static status_t 491 openpic_disable_io_interrupt(void *cookie, int irq) 492 { 493 openpic_info *info = (openpic_info*)cookie; 494 495 openpic_disable_irq(info, irq); 496 497 return B_OK; 498 } 499 500 501 static int 502 openpic_acknowledge_io_interrupt(void *cookie) 503 { 504 openpic_info *info = (openpic_info*)cookie; 505 506 int cpu = 0; 507 // Note: We direct all I/O interrupts to CPU 0. We could nevertheless 508 // check against the value of the "Who Am I Register". 509 510 int irq = openpic_read_irq(info, cpu); 511 if (irq == 255) 512 return -1; // spurious interrupt 513 514 // signal end of interrupt 515 openpic_eoi(info, cpu); 516 517 return irq; 518 } 519 520 521 static interrupt_controller_module_info sControllerModuleInfo = { 522 { 523 { 524 OPENPIC_MODULE_NAME, 525 0, 526 openpic_std_ops 527 }, 528 529 openpic_supports_device, 530 openpic_register_device, 531 openpic_init_driver, 532 openpic_uninit_driver, 533 NULL, // HACK: register_child_devices 534 NULL, // HACK: rescan_child_devices 535 openpic_device_removed, 536 NULL, // suspend 537 NULL // resume 538 }, 539 540 openpic_get_controller_info, 541 openpic_enable_io_interrupt, 542 openpic_disable_io_interrupt, 543 openpic_acknowledge_io_interrupt, 544 }; 545 546 module_dependency module_dependencies[] = { 547 { B_DEVICE_MANAGER_MODULE_NAME, (module_info **)&sDeviceManager }, 548 { B_PCI_MODULE_NAME, (module_info**)&sPCIBusManager}, 549 {} 550 }; 551 552 module_info *modules[] = { 553 (module_info *)&sControllerModuleInfo, 554 NULL 555 }; 556