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
48enum {
49	OPENPIC_MIN_REGISTER_SPACE_SIZE	= 0x21000,
50	OPENPIC_MAX_REGISTER_SPACE_SIZE	= 0x40000,
51};
52
53struct openpic_supported_device {
54	const char	*name;
55	uint16		vendor_id;
56	uint16		device_id;
57	uint32		register_offset;
58};
59
60static openpic_supported_device sSupportedDevices[] = {
61	{ "Intrepid I/O Controller",	0x106b,	0x003e,	0x40000 },
62	{}
63};
64
65static device_manager_info *sDeviceManager;
66static pci_module_info *sPCIBusManager;
67
68struct 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
100static openpic_supported_device *
101openpic_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
116static uint32
117openpic_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
124static void
125openpic_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
132static int
133openpic_read_irq(openpic_info *info, int cpu)
134{
135	return openpic_read(info, OPENPIC_IACK(cpu)) & OPENPIC_VECTOR_MASK;
136}
137
138
139static void
140openpic_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
148static void
149openpic_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
165static void
166openpic_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
176static void
177openpic_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
188static status_t
189openpic_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
267static status_t
268openpic_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
281static float
282openpic_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
312static status_t
313openpic_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
334static status_t
335openpic_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
409static void
410openpic_uninit_driver(void *cookie)
411{
412	openpic_info *info = (openpic_info*)cookie;
413
414	delete info;
415}
416
417
418static status_t
419openpic_register_child_devices(void *cookie)
420{
421	return B_OK;
422}
423
424
425static status_t
426openpic_rescan_child_devices(void *cookie)
427{
428	return B_OK;
429}
430
431
432static void
433openpic_device_removed(void *driverCookie)
434{
435	// TODO: ...
436}
437
438
439// #pragma mark - interrupt_controller interface
440
441
442static status_t
443openpic_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
456static status_t
457openpic_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
467static status_t
468openpic_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
478static int
479openpic_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
498static 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
523module_dependency module_dependencies[] = {
524	{ B_DEVICE_MANAGER_MODULE_NAME, (module_info **)&sDeviceManager },
525	{ B_PCI_MODULE_NAME, (module_info**)&sPCIBusManager},
526	{}
527};
528
529module_info *modules[] = {
530	(module_info *)&sControllerModuleInfo,
531	NULL
532};
533