xref: /haiku/src/add-ons/kernel/interrupt_controllers/openpic/openpic.cpp (revision b55a57da7173b9af0432bd3e148d03f06161d036)
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