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 sDeviceManager->uninit_driver(sDeviceManager->get_parent(node)); 84 } 85 86 openpic_supported_device *supported_device; 87 device_node_handle node; 88 pci_device_module_info *pci; 89 pci_device device; 90 91 addr_t physical_registers; // physical registers base 92 addr_t virtual_registers; // virtual (mapped) 93 // registers base 94 area_id register_area; // register area 95 size_t register_space_size; 96 }; 97 98 99 static openpic_supported_device * 100 openpic_check_supported_device(uint16 vendorID, uint16 deviceID) 101 { 102 for (openpic_supported_device *supportedDevice = sSupportedDevices; 103 supportedDevice->name; 104 supportedDevice++) { 105 if (supportedDevice->vendor_id == vendorID 106 && supportedDevice->device_id == deviceID) { 107 return supportedDevice; 108 } 109 } 110 111 return NULL; 112 } 113 114 115 static uint32 116 openpic_read(openpic_info *info, int reg) 117 { 118 return B_SWAP_INT32(info->pci->read_io_32(info->device, 119 info->virtual_registers + reg)); 120 } 121 122 123 static void 124 openpic_write(openpic_info *info, int reg, uint32 val) 125 { 126 info->pci->write_io_32(info->device, info->virtual_registers + reg, 127 B_SWAP_INT32(val)); 128 } 129 130 131 static int 132 openpic_read_irq(openpic_info *info, int cpu) 133 { 134 return openpic_read(info, OPENPIC_IACK(cpu)) & OPENPIC_VECTOR_MASK; 135 } 136 137 138 static void 139 openpic_eoi(openpic_info *info, int cpu) 140 { 141 openpic_write(info, OPENPIC_EOI(cpu), 0); 142 // the Linux driver does this: 143 //openpic_read(info, OPENPIC_EOI(cpu)); 144 } 145 146 147 static void 148 openpic_enable_irq(openpic_info *info, int irq, int type) 149 { 150 // TODO: Align this code with the sequence recommended in the Open PIC 151 // Specification (v 1.2 section 5.2.2). 152 uint32 x; 153 154 x = openpic_read(info, OPENPIC_SRC_VECTOR(irq)); 155 x &= ~(OPENPIC_IMASK | OPENPIC_SENSE_LEVEL | OPENPIC_SENSE_EDGE); 156 if (type == IRQ_TYPE_LEVEL) 157 x |= OPENPIC_SENSE_LEVEL; 158 else 159 x |= OPENPIC_SENSE_EDGE; 160 openpic_write(info, OPENPIC_SRC_VECTOR(irq), x); 161 } 162 163 164 static void 165 openpic_disable_irq(openpic_info *info, int irq) 166 { 167 uint32 x; 168 169 x = openpic_read(info, OPENPIC_SRC_VECTOR(irq)); 170 x |= OPENPIC_IMASK; 171 openpic_write(info, OPENPIC_SRC_VECTOR(irq), x); 172 } 173 174 175 static void 176 openpic_set_priority(openpic_info *info, int cpu, int pri) 177 { 178 uint32 x; 179 180 x = openpic_read(info, OPENPIC_CPU_PRIORITY(cpu)); 181 x &= ~OPENPIC_CPU_PRIORITY_MASK; 182 x |= pri; 183 openpic_write(info, OPENPIC_CPU_PRIORITY(cpu), x); 184 } 185 186 187 static status_t 188 openpic_init(openpic_info *info) 189 { 190 uint32 x = openpic_read(info, OPENPIC_FEATURE); 191 const char *featureVersion; 192 char versionBuffer[64]; 193 switch (x & OPENPIC_FEATURE_VERSION_MASK) { 194 case 1: 195 featureVersion = "1.0"; 196 break; 197 case 2: 198 featureVersion = "1.2"; 199 break; 200 case 3: 201 featureVersion = "1.3"; 202 break; 203 default: 204 snprintf(versionBuffer, sizeof(versionBuffer), 205 "unknown (feature reg: 0x%lx)", x); 206 featureVersion = versionBuffer; 207 break; 208 } 209 210 info->cpu_count = ((x & OPENPIC_FEATURE_LAST_CPU_MASK) >> 211 OPENPIC_FEATURE_LAST_CPU_SHIFT) + 1; 212 info->irq_count = ((x & OPENPIC_FEATURE_LAST_IRQ_MASK) >> 213 OPENPIC_FEATURE_LAST_IRQ_SHIFT) + 1; 214 215 /* 216 * PSIM seems to report 1 too many IRQs 217 */ 218 // if (sc->sc_psim) 219 // sc->sc_nirq--; 220 221 dprintf("openpic: Version %s, supports %d CPUs and %d irqs\n", 222 featureVersion, info->cpu_count, info->irq_count); 223 224 /* disable all interrupts */ 225 for (int irq = 0; irq < info->irq_count; irq++) 226 openpic_write(info, OPENPIC_SRC_VECTOR(irq), OPENPIC_IMASK); 227 228 openpic_set_priority(info, 0, 15); 229 230 /* we don't need 8259 passthrough mode */ 231 x = openpic_read(info, OPENPIC_CONFIG); 232 x |= OPENPIC_CONFIG_8259_PASSTHRU_DISABLE; 233 openpic_write(info, OPENPIC_CONFIG, x); 234 235 /* send all interrupts to cpu 0 */ 236 for (int irq = 0; irq < info->irq_count; irq++) 237 openpic_write(info, OPENPIC_IDEST(irq), 1 << 0); 238 239 for (int irq = 0; irq < info->irq_count; irq++) { 240 x = irq; 241 x |= OPENPIC_IMASK; 242 x |= OPENPIC_POLARITY_POSITIVE; 243 x |= OPENPIC_SENSE_LEVEL; 244 x |= 8 << OPENPIC_PRIORITY_SHIFT; 245 openpic_write(info, OPENPIC_SRC_VECTOR(irq), x); 246 } 247 248 /* XXX IPI */ 249 /* XXX set spurious intr vector */ 250 251 openpic_set_priority(info, 0, 0); 252 253 /* clear all pending interrupts */ 254 for (int irq = 0; irq < info->irq_count; irq++) { 255 openpic_read_irq(info, 0); 256 openpic_eoi(info, 0); 257 } 258 259 return B_OK; 260 } 261 262 263 // #pragma mark - driver interface 264 265 266 static status_t 267 openpic_std_ops(int32 op, ...) 268 { 269 switch (op) { 270 case B_MODULE_INIT: 271 case B_MODULE_UNINIT: 272 return B_OK; 273 274 default: 275 return B_ERROR; 276 } 277 } 278 279 280 static float 281 openpic_supports_device(device_node_handle parent, bool *_noConnection) 282 { 283 char *bus; 284 uint16 vendorID; 285 uint16 deviceID; 286 287 // get the bus (should be PCI) 288 if (sDeviceManager->get_attr_string(parent, B_DRIVER_BUS, &bus, false) 289 != B_OK) { 290 return B_ERROR; 291 } 292 MemoryDeleter _(bus); 293 294 // get vendor and device ID 295 if (sDeviceManager->get_attr_uint16(parent, PCI_DEVICE_VENDOR_ID_ITEM, 296 &vendorID, false) != B_OK 297 || sDeviceManager->get_attr_uint16(parent, PCI_DEVICE_DEVICE_ID_ITEM, 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_handle parent) 314 { 315 // get interface to PCI device 316 pci_device_module_info *pci; 317 pci_device device; 318 status_t error = sDeviceManager->init_driver(parent, NULL, 319 (driver_module_info**)&pci, (void**)&device); 320 if (error != B_OK) 321 return error; 322 323 sDeviceManager->uninit_driver(parent); 324 325 device_attr attrs[] = { 326 // info about ourself 327 { B_DRIVER_MODULE, B_STRING_TYPE, { string: OPENPIC_MODULE_NAME }}, 328 { B_DRIVER_DEVICE_TYPE, B_STRING_TYPE, 329 { string: B_INTERRUPT_CONTROLLER_DRIVER_TYPE }}, 330 331 {} 332 }; 333 334 return sDeviceManager->register_device(parent, attrs, NULL, NULL); 335 } 336 337 338 static status_t 339 openpic_init_driver(device_node_handle node, void *user_cookie, 340 void **cookie) 341 { 342 openpic_info *info = new(nothrow) openpic_info; 343 if (!info) 344 return B_NO_MEMORY; 345 ObjectDeleter<openpic_info> infoDeleter(info); 346 347 info->node = node; 348 349 // get interface to PCI device 350 status_t error = sDeviceManager->init_driver( 351 sDeviceManager->get_parent(node), NULL, 352 (driver_module_info**)&info->pci, (void**)&info->device); 353 if (error != B_OK) 354 return error; 355 356 // get the pci info for the device 357 pci_info pciInfo; 358 error = info->pci->get_pci_info(info->device, &pciInfo); 359 if (error != B_OK) 360 return error; 361 362 // find supported device info 363 info->supported_device = openpic_check_supported_device(pciInfo.vendor_id, 364 pciInfo.device_id); 365 if (!info->supported_device) { 366 dprintf("openpic: device (0x%04hx:0x%04hx) not supported\n", 367 pciInfo.vendor_id, pciInfo.device_id); 368 return B_ERROR; 369 } 370 dprintf("openpic: found supported device: %s (0x%04hx:0x%04hx)\n", 371 info->supported_device->name, pciInfo.vendor_id, pciInfo.device_id); 372 373 // get register space 374 addr_t physicalRegisterBase = pciInfo.u.h0.base_registers[0]; 375 uint32 registerSpaceSize = pciInfo.u.h0.base_register_sizes[0]; 376 if (registerSpaceSize < info->supported_device->register_offset 377 || registerSpaceSize - info->supported_device->register_offset 378 < OPENPIC_MIN_REGISTER_SPACE_SIZE) { 379 dprintf("openpic: register space too small\n"); 380 } 381 physicalRegisterBase += info->supported_device->register_offset; 382 registerSpaceSize -= info->supported_device->register_offset; 383 if (registerSpaceSize > OPENPIC_MAX_REGISTER_SPACE_SIZE) 384 registerSpaceSize = OPENPIC_MAX_REGISTER_SPACE_SIZE; 385 386 // map register space 387 void *virtualRegisterBase = NULL; 388 area_id registerArea = map_physical_memory("openpic registers", 389 (void*)physicalRegisterBase, registerSpaceSize, B_ANY_KERNEL_ADDRESS, 390 B_KERNEL_READ_AREA | B_KERNEL_WRITE_AREA, &virtualRegisterBase); 391 if (registerArea < 0) 392 return info->register_area; 393 394 info->physical_registers = physicalRegisterBase; 395 info->register_space_size = registerSpaceSize; 396 info->register_area = registerArea; 397 info->virtual_registers = (addr_t)virtualRegisterBase; 398 399 // init the controller 400 error = openpic_init(info); 401 if (error != B_OK) 402 return error; 403 404 // keep the info 405 infoDeleter.Detach(); 406 *cookie = info; 407 408 dprintf("openpic_init_driver(): Successfully initialized!\n"); 409 410 return B_OK; 411 } 412 413 414 static status_t 415 openpic_uninit_driver(void *cookie) 416 { 417 openpic_info *info = (openpic_info*)cookie; 418 419 delete info; 420 421 return B_OK; 422 } 423 424 425 static void 426 openpic_device_removed(device_node_handle node, void *cookie) 427 { 428 // TODO: ... 429 } 430 431 432 static void 433 openpic_get_paths(const char ***_bus, const char ***_device) 434 { 435 static const char *kBus[] = { "pci", NULL }; 436 // static const char *kDevice[] = { "drivers/dev/disk/ide", NULL }; 437 438 *_bus = kBus; 439 // *_device = kDevice; 440 *_device = NULL; 441 } 442 443 444 // #pragma mark - interrupt_controller interface 445 446 447 static status_t 448 openpic_get_controller_info(void *cookie, interrupt_controller_info *_info) 449 { 450 if (!_info) 451 return B_BAD_VALUE; 452 453 openpic_info *info = (openpic_info*)cookie; 454 455 *_info = *info; 456 457 return B_OK; 458 } 459 460 461 static status_t 462 openpic_enable_io_interrupt(void *cookie, int irq, int type) 463 { 464 openpic_info *info = (openpic_info*)cookie; 465 466 openpic_enable_irq(info, irq, type); 467 468 return B_OK; 469 } 470 471 472 static status_t 473 openpic_disable_io_interrupt(void *cookie, int irq) 474 { 475 openpic_info *info = (openpic_info*)cookie; 476 477 openpic_disable_irq(info, irq); 478 479 return B_OK; 480 } 481 482 483 static int 484 openpic_acknowledge_io_interrupt(void *cookie) 485 { 486 openpic_info *info = (openpic_info*)cookie; 487 488 int cpu = 0; 489 // Note: We direct all I/O interrupts to CPU 0. We could nevertheless 490 // check against the value of the "Who Am I Register". 491 492 int irq = openpic_read_irq(info, cpu); 493 if (irq == 255) 494 return -1; // spurious interrupt 495 496 // signal end of interrupt 497 openpic_eoi(info, cpu); 498 499 return irq; 500 } 501 502 503 static interrupt_controller_module_info sControllerModuleInfo = { 504 { 505 { 506 OPENPIC_MODULE_NAME, 507 0, 508 openpic_std_ops 509 }, 510 511 openpic_supports_device, 512 openpic_register_device, 513 openpic_init_driver, 514 openpic_uninit_driver, 515 openpic_device_removed, 516 NULL, // cleanup 517 openpic_get_paths, 518 }, 519 520 openpic_get_controller_info, 521 openpic_enable_io_interrupt, 522 openpic_disable_io_interrupt, 523 openpic_acknowledge_io_interrupt, 524 }; 525 526 module_dependency module_dependencies[] = { 527 { B_DEVICE_MANAGER_MODULE_NAME, (module_info **)&sDeviceManager }, 528 { B_PCI_MODULE_NAME, (module_info**)&sPCIBusManager}, 529 {} 530 }; 531 532 module_info *modules[] = { 533 (module_info *)&sControllerModuleInfo, 534 NULL 535 }; 536