2 * Copyright 2006, Ingo Weinhold <bonefish@cs.tu-berlin.de>.
3 * All rights reserved. Distributed under the terms of the MIT License.
6 * Copyright (C) 2002 Benno Rice.
9 * Redistribution and use in source and binary forms, with or without
10 * modification, are permitted provided that the following conditions
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.
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.
35 #include <ByteOrder.h>
36 #include <KernelExport.h>
38 #include <AutoDeleter.h>
40 #include <interrupt_controller.h>
41 #include <util/kernel_cpp.h>
46 #define OPENPIC_MODULE_NAME "interrupt_controllers/openpic/device_v1"
49 OPENPIC_MIN_REGISTER_SPACE_SIZE
= 0x21000,
50 OPENPIC_MAX_REGISTER_SPACE_SIZE
= 0x40000,
53 struct openpic_supported_device
{
57 uint32 register_offset
;
60 static openpic_supported_device sSupportedDevices
[] = {
61 { "Intrepid I/O Controller", 0x106b, 0x003e, 0x40000 },
65 static device_manager_info
*sDeviceManager
;
66 static pci_module_info
*sPCIBusManager
;
68 struct openpic_info
: interrupt_controller_info
{
71 memset(this, 0, sizeof(openpic_info
));
78 if (register_area
>= 0)
79 delete_area(register_area
);
81 // uninit parent node driver
84 sDeviceManager
->put_node(sDeviceManager
->get_parent_node(node
));
87 openpic_supported_device
*supported_device
;
89 pci_device_module_info
*pci
;
92 addr_t physical_registers
; // physical registers base
93 addr_t virtual_registers
; // virtual (mapped)
95 area_id register_area
; // register area
96 size_t register_space_size
;
100 static openpic_supported_device
*
101 openpic_check_supported_device(uint16 vendorID
, uint16 deviceID
)
103 for (openpic_supported_device
*supportedDevice
= sSupportedDevices
;
104 supportedDevice
->name
;
106 if (supportedDevice
->vendor_id
== vendorID
107 && supportedDevice
->device_id
== deviceID
) {
108 return supportedDevice
;
117 openpic_read(openpic_info
*info
, int reg
)
119 return B_SWAP_INT32(info
->pci
->read_io_32(info
->device
,
120 info
->virtual_registers
+ reg
));
125 openpic_write(openpic_info
*info
, int reg
, uint32 val
)
127 info
->pci
->write_io_32(info
->device
, info
->virtual_registers
+ reg
,
133 openpic_read_irq(openpic_info
*info
, int cpu
)
135 return openpic_read(info
, OPENPIC_IACK(cpu
)) & OPENPIC_VECTOR_MASK
;
140 openpic_eoi(openpic_info
*info
, int cpu
)
142 openpic_write(info
, OPENPIC_EOI(cpu
), 0);
143 // the Linux driver does this:
144 //openpic_read(info, OPENPIC_EOI(cpu));
149 openpic_enable_irq(openpic_info
*info
, int irq
, int type
)
151 // TODO: Align this code with the sequence recommended in the Open PIC
152 // Specification (v 1.2 section 5.2.2).
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
;
160 x
|= OPENPIC_SENSE_EDGE
;
161 openpic_write(info
, OPENPIC_SRC_VECTOR(irq
), x
);
166 openpic_disable_irq(openpic_info
*info
, int irq
)
170 x
= openpic_read(info
, OPENPIC_SRC_VECTOR(irq
));
172 openpic_write(info
, OPENPIC_SRC_VECTOR(irq
), x
);
177 openpic_set_priority(openpic_info
*info
, int cpu
, int pri
)
181 x
= openpic_read(info
, OPENPIC_CPU_PRIORITY(cpu
));
182 x
&= ~OPENPIC_CPU_PRIORITY_MASK
;
184 openpic_write(info
, OPENPIC_CPU_PRIORITY(cpu
), x
);
189 openpic_init(openpic_info
*info
)
191 uint32 x
= openpic_read(info
, OPENPIC_FEATURE
);
192 const char *featureVersion
;
193 char versionBuffer
[64];
194 switch (x
& OPENPIC_FEATURE_VERSION_MASK
) {
196 featureVersion
= "1.0";
199 featureVersion
= "1.2";
202 featureVersion
= "1.3";
205 snprintf(versionBuffer
, sizeof(versionBuffer
),
206 "unknown (feature reg: 0x%lx)", x
);
207 featureVersion
= versionBuffer
;
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;
217 * PSIM seems to report 1 too many IRQs
222 dprintf("openpic: Version %s, supports %d CPUs and %d irqs\n",
223 featureVersion
, info
->cpu_count
, info
->irq_count
);
225 /* disable all interrupts */
226 for (int irq
= 0; irq
< info
->irq_count
; irq
++)
227 openpic_write(info
, OPENPIC_SRC_VECTOR(irq
), OPENPIC_IMASK
);
229 openpic_set_priority(info
, 0, 15);
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
);
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);
240 for (int irq
= 0; irq
< info
->irq_count
; irq
++) {
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
);
250 /* XXX set spurious intr vector */
252 openpic_set_priority(info
, 0, 0);
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);
264 // #pragma mark - driver interface
268 openpic_std_ops(int32 op
, ...)
272 case B_MODULE_UNINIT
:
282 openpic_supports_device(device_node
*parent
)
288 // get the bus (should be PCI)
289 if (sDeviceManager
->get_attr_string(parent
, B_DEVICE_BUS
, &bus
, false)
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
) {
302 // check, whether bus, vendor and device ID match
303 if (strcmp(bus
, "pci") != 0
304 || !openpic_check_supported_device(vendorID
, deviceID
)) {
313 openpic_register_device(device_node
*parent
)
315 #if 0 //XXX: what do I do ?
316 // get interface to PCI device
317 pci_device_module_info
*pci
;
319 driver_module_info
*driver
;
322 error
= sDeviceManager
->get_driver(parent
, &driver
, &cookie
);
325 error
= driver
->init_driver(parent
, cookie
);
326 // (driver_module_info**)&pci, (void**)&device); // wtf?
330 sDeviceManager
->uninit_driver(parent
);
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 }},
343 // HACK: to get it compiled, I will break anything.
344 return sDeviceManager
->register_node(parent
, NULL
, attrs
, NULL
, &newNode
);
349 openpic_init_driver(device_node
*node
, void **cookie
)
351 // OK, this module is broken for now. But it compiles.
353 openpic_info
*info
= new(nothrow
) openpic_info
;
356 ObjectDeleter
<openpic_info
> infoDeleter(info
);
360 // get interface to PCI device
362 void *anotherCookie
; // possibly the same cookie.
363 driver_module_info
*driver
;
364 status_t status
= sDeviceManager
->get_driver(sDeviceManager
->get_parent_node(node
),
369 driver
->init_driver(node
, &anotherCookie
);
371 /* status = sDeviceManager->init_driver(
372 sDeviceManager->get_parent(node), NULL,
373 (driver_module_info**)&info->pci, (void**)&info->device);
377 // get the pci info for the device
379 info
->pci
->get_pci_info(info
->device
, &pciInfo
);
381 // find supported device info
382 info
->supported_device
= openpic_check_supported_device(pciInfo
.vendor_id
,
384 if (!info
->supported_device
) {
385 dprintf("openpic: device (0x%04hx:0x%04hx) not supported\n",
386 pciInfo
.vendor_id
, pciInfo
.device_id
);
389 dprintf("openpic: found supported device: %s (0x%04hx:0x%04hx)\n",
390 info
->supported_device
->name
, pciInfo
.vendor_id
, pciInfo
.device_id
);
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");
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
;
405 // map register space
406 void *virtualRegisterBase
= NULL
;
407 area_id registerArea
= map_physical_memory("openpic registers",
408 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
;
413 info
->physical_registers
= physicalRegisterBase
;
414 info
->register_space_size
= registerSpaceSize
;
415 info
->register_area
= registerArea
;
416 info
->virtual_registers
= (addr_t
)virtualRegisterBase
;
418 // init the controller
419 status
= openpic_init(info
);
424 infoDeleter
.Detach();
427 dprintf("openpic_init_driver(): Successfully initialized!\n");
434 openpic_uninit_driver(void *cookie
)
436 openpic_info
*info
= (openpic_info
*)cookie
;
443 openpic_device_removed(void *driverCookie
)
449 // FIXME: I don't think this is needed...
451 openpic_get_paths(const char **_bus, const char **_device)
453 static const char *kBus[] = { "pci", NULL };
454 // static const char *kDevice[] = { "drivers/dev/disk/ide", NULL };
457 // *_device = kDevice;
462 // #pragma mark - interrupt_controller interface
466 openpic_get_controller_info(void *cookie
, interrupt_controller_info
*_info
)
471 openpic_info
*info
= (openpic_info
*)cookie
;
480 openpic_enable_io_interrupt(void *cookie
, int irq
, int type
)
482 openpic_info
*info
= (openpic_info
*)cookie
;
484 openpic_enable_irq(info
, irq
, type
);
491 openpic_disable_io_interrupt(void *cookie
, int irq
)
493 openpic_info
*info
= (openpic_info
*)cookie
;
495 openpic_disable_irq(info
, irq
);
502 openpic_acknowledge_io_interrupt(void *cookie
)
504 openpic_info
*info
= (openpic_info
*)cookie
;
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".
510 int irq
= openpic_read_irq(info
, cpu
);
512 return -1; // spurious interrupt
514 // signal end of interrupt
515 openpic_eoi(info
, cpu
);
521 static interrupt_controller_module_info sControllerModuleInfo
= {
529 openpic_supports_device
,
530 openpic_register_device
,
532 openpic_uninit_driver
,
533 NULL
, // HACK: register_child_devices
534 NULL
, // HACK: rescan_child_devices
535 openpic_device_removed
,
540 openpic_get_controller_info
,
541 openpic_enable_io_interrupt
,
542 openpic_disable_io_interrupt
,
543 openpic_acknowledge_io_interrupt
,
546 module_dependency module_dependencies
[] = {
547 { B_DEVICE_MANAGER_MODULE_NAME
, (module_info
**)&sDeviceManager
},
548 { B_PCI_MODULE_NAME
, (module_info
**)&sPCIBusManager
},
552 module_info
*modules
[] = {
553 (module_info
*)&sControllerModuleInfo
,