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 {
openpic_infoopenpic_info69 openpic_info()
70 {
71 memset(this, 0, sizeof(openpic_info));
72 register_area = -1;
73 }
74
~openpic_infoopenpic_info75 ~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 *
openpic_check_supported_device(uint16 vendorID,uint16 deviceID)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
openpic_read(openpic_info * info,int reg)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
openpic_write(openpic_info * info,int reg,uint32 val)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
openpic_read_irq(openpic_info * info,int cpu)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
openpic_eoi(openpic_info * info,int cpu)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
openpic_enable_irq(openpic_info * info,int irq,int type)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
openpic_disable_irq(openpic_info * info,int irq)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
openpic_set_priority(openpic_info * info,int cpu,int pri)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
openpic_init(openpic_info * info)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
openpic_std_ops(int32 op,...)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
openpic_supports_device(device_node * parent)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
openpic_register_device(device_node * parent)313 openpic_register_device(device_node *parent)
314 {
315 device_node *newNode;
316 device_attr attrs[] = {
317 // info about ourself
318 { B_DEVICE_TYPE, B_UINT16_TYPE, { .ui16 = PCI_base_peripheral }},
319 { B_DEVICE_SUB_TYPE, B_UINT16_TYPE, { .ui16 = PCI_pic }},
320 // TODO How do we identify ourselves as OpenPIC?
321 { B_DEVICE_INTERFACE, B_UINT16_TYPE, { .ui16 = PCI_pic_8259 }},
322 {}
323 };
324 io_resource resources[] = {
325 // TODO Fill in whatever necessary
326 {}
327 };
328
329 return sDeviceManager->register_node(parent, OPENPIC_MODULE_NAME,
330 attrs, resources, &newNode);
331 }
332
333
334 static status_t
openpic_init_driver(device_node * node,void ** cookie)335 openpic_init_driver(device_node *node, void **cookie)
336 {
337 openpic_info *info = new(nothrow) openpic_info;
338 if (!info)
339 return B_NO_MEMORY;
340 ObjectDeleter<openpic_info> infoDeleter(info);
341
342 info->node = node;
343
344 // get interface to PCI device
345 void *aCookie;
346 status_t status = sDeviceManager->get_driver(sDeviceManager->get_parent_node(node),
347 (driver_module_info**)&info->pci, &aCookie);
348 if (status != B_OK)
349 return status;
350
351 info->pci->info.init_driver(node, (void**)&info->device);
352
353 // get the pci info for the device
354 pci_info pciInfo;
355 info->pci->get_pci_info(info->device, &pciInfo);
356
357 // find supported device info
358 info->supported_device = openpic_check_supported_device(pciInfo.vendor_id,
359 pciInfo.device_id);
360 if (!info->supported_device) {
361 dprintf("openpic: device (0x%04hx:0x%04hx) not supported\n",
362 pciInfo.vendor_id, pciInfo.device_id);
363 return B_ERROR;
364 }
365 dprintf("openpic: found supported device: %s (0x%04hx:0x%04hx)\n",
366 info->supported_device->name, pciInfo.vendor_id, pciInfo.device_id);
367
368 // get register space
369 addr_t physicalRegisterBase = pciInfo.u.h0.base_registers[0];
370 uint32 registerSpaceSize = pciInfo.u.h0.base_register_sizes[0];
371 if (registerSpaceSize < info->supported_device->register_offset
372 || registerSpaceSize - info->supported_device->register_offset
373 < OPENPIC_MIN_REGISTER_SPACE_SIZE) {
374 dprintf("openpic: register space too small\n");
375 }
376 physicalRegisterBase += info->supported_device->register_offset;
377 registerSpaceSize -= info->supported_device->register_offset;
378 if (registerSpaceSize > OPENPIC_MAX_REGISTER_SPACE_SIZE)
379 registerSpaceSize = OPENPIC_MAX_REGISTER_SPACE_SIZE;
380
381 // map register space
382 void *virtualRegisterBase = NULL;
383 area_id registerArea = map_physical_memory("openpic registers",
384 physicalRegisterBase, registerSpaceSize, B_ANY_KERNEL_ADDRESS,
385 B_KERNEL_READ_AREA | B_KERNEL_WRITE_AREA, &virtualRegisterBase);
386 if (registerArea < 0)
387 return info->register_area;
388
389 info->physical_registers = physicalRegisterBase;
390 info->register_space_size = registerSpaceSize;
391 info->register_area = registerArea;
392 info->virtual_registers = (addr_t)virtualRegisterBase;
393
394 // init the controller
395 status = openpic_init(info);
396 if (status != B_OK)
397 return status;
398
399 // keep the info
400 infoDeleter.Detach();
401 *cookie = info;
402
403 dprintf("openpic_init_driver(): Successfully initialized!\n");
404
405 return B_OK;
406 }
407
408
409 static void
openpic_uninit_driver(void * cookie)410 openpic_uninit_driver(void *cookie)
411 {
412 openpic_info *info = (openpic_info*)cookie;
413
414 delete info;
415 }
416
417
418 static status_t
openpic_register_child_devices(void * cookie)419 openpic_register_child_devices(void *cookie)
420 {
421 return B_OK;
422 }
423
424
425 static status_t
openpic_rescan_child_devices(void * cookie)426 openpic_rescan_child_devices(void *cookie)
427 {
428 return B_OK;
429 }
430
431
432 static void
openpic_device_removed(void * driverCookie)433 openpic_device_removed(void *driverCookie)
434 {
435 // TODO: ...
436 }
437
438
439 // #pragma mark - interrupt_controller interface
440
441
442 static status_t
openpic_get_controller_info(void * cookie,interrupt_controller_info * _info)443 openpic_get_controller_info(void *cookie, interrupt_controller_info *_info)
444 {
445 if (!_info)
446 return B_BAD_VALUE;
447
448 openpic_info *info = (openpic_info*)cookie;
449
450 *_info = *info;
451
452 return B_OK;
453 }
454
455
456 static status_t
openpic_enable_io_interrupt(void * cookie,int irq,int type)457 openpic_enable_io_interrupt(void *cookie, int irq, int type)
458 {
459 openpic_info *info = (openpic_info*)cookie;
460
461 openpic_enable_irq(info, irq, type);
462
463 return B_OK;
464 }
465
466
467 static status_t
openpic_disable_io_interrupt(void * cookie,int irq)468 openpic_disable_io_interrupt(void *cookie, int irq)
469 {
470 openpic_info *info = (openpic_info*)cookie;
471
472 openpic_disable_irq(info, irq);
473
474 return B_OK;
475 }
476
477
478 static int
openpic_acknowledge_io_interrupt(void * cookie)479 openpic_acknowledge_io_interrupt(void *cookie)
480 {
481 openpic_info *info = (openpic_info*)cookie;
482
483 int cpu = 0;
484 // Note: We direct all I/O interrupts to CPU 0. We could nevertheless
485 // check against the value of the "Who Am I Register".
486
487 int irq = openpic_read_irq(info, cpu);
488 if (irq == 255)
489 return -1; // spurious interrupt
490
491 // signal end of interrupt
492 openpic_eoi(info, cpu);
493
494 return irq;
495 }
496
497
498 static interrupt_controller_module_info sControllerModuleInfo = {
499 {
500 {
501 OPENPIC_MODULE_NAME,
502 0,
503 openpic_std_ops
504 },
505
506 openpic_supports_device,
507 openpic_register_device,
508 openpic_init_driver,
509 openpic_uninit_driver,
510 openpic_register_child_devices,
511 openpic_rescan_child_devices,
512 openpic_device_removed,
513 NULL, // suspend
514 NULL // resume
515 },
516
517 openpic_get_controller_info,
518 openpic_enable_io_interrupt,
519 openpic_disable_io_interrupt,
520 openpic_acknowledge_io_interrupt,
521 };
522
523 module_dependency module_dependencies[] = {
524 { B_DEVICE_MANAGER_MODULE_NAME, (module_info **)&sDeviceManager },
525 { B_PCI_MODULE_NAME, (module_info**)&sPCIBusManager},
526 {}
527 };
528
529 module_info *modules[] = {
530 (module_info *)&sControllerModuleInfo,
531 NULL
532 };
533