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