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 <stdlib.h>
33#include <string.h>
34
35#include <KernelExport.h>
36
37#include <platform/openfirmware/devices.h>
38#include <platform/openfirmware/openfirmware.h>
39#include <platform/openfirmware/pci.h>
40
41#include "pci_controller.h"
42#include "pci_io.h"
43#include "pci_openfirmware_priv.h"
44
45
46struct uninorth_range {
47	uint32	pci_hi;
48	uint32	pci_mid;
49	uint32	pci_lo;
50	uint32	host;
51	uint32	size_hi;
52	uint32	size_lo;
53};
54
55struct uninorth_host_bridge {
56	int						device_node;
57	addr_t					address_registers;
58	addr_t					data_registers;
59	uint32					bus;
60	struct uninorth_range	ranges[6];
61	int						range_count;
62};
63
64
65#define out8rb(address, value)	ppc_out8((vuint8*)(address), value)
66#define out16rb(address, value)	ppc_out16_reverse((vuint16*)(address), value)
67#define out32rb(address, value)	ppc_out32_reverse((vuint32*)(address), value)
68#define in8rb(address)			ppc_in8((const vuint8*)(address))
69#define in16rb(address)			ppc_in16_reverse((const vuint16*)(address))
70#define in32rb(address)			ppc_in32_reverse((const vuint32*)(address))
71
72
73static int		uninorth_enable_config(struct uninorth_host_bridge *bridge,
74					uint8 bus, uint8 slot, uint8 function, uint8 offset);
75
76static status_t	uninorth_read_pci_config(void *cookie, uint8 bus, uint8 device,
77					uint8 function, uint8 offset, uint8 size, uint32 *value);
78static status_t	uninorth_write_pci_config(void *cookie, uint8 bus,
79					uint8 device, uint8 function, uint8 offset, uint8 size,
80					uint32 value);
81static status_t	uninorth_get_max_bus_devices(void *cookie, int32 *count);
82static status_t	uninorth_read_pci_irq(void *cookie, uint8 bus, uint8 device,
83					uint8 function, uint8 pin, uint8 *irq);
84static status_t	uninorth_write_pci_irq(void *cookie, uint8 bus, uint8 device,
85					uint8 function, uint8 pin, uint8 irq);
86
87static pci_controller sUniNorthPCIController = {
88	uninorth_read_pci_config,
89	uninorth_write_pci_config,
90	uninorth_get_max_bus_devices,
91	uninorth_read_pci_irq,
92	uninorth_write_pci_irq,
93};
94
95
96static status_t
97uninorth_read_pci_config(void *cookie, uint8 bus, uint8 device, uint8 function,
98	uint8 offset, uint8 size, uint32 *value)
99{
100	uninorth_host_bridge *bridge = (uninorth_host_bridge*)cookie;
101
102	addr_t caoff = bridge->data_registers + (offset & 0x07);
103
104	if (uninorth_enable_config(bridge, bus, device, function, offset) != 0) {
105		switch (size) {
106			case 1:
107				*value = in8rb(caoff);
108				break;
109			case 2:
110				*value = in16rb(caoff);
111				break;
112			case 4:
113				*value = in32rb(caoff);
114				break;
115			default:
116				*value = 0xffffffff;
117				break;
118		}
119	} else
120		*value = 0xffffffff;
121
122	return B_OK;
123}
124
125
126static status_t uninorth_write_pci_config(void *cookie, uint8 bus, uint8 device,
127	uint8 function, uint8 offset, uint8 size, uint32 value)
128{
129	uninorth_host_bridge *bridge = (uninorth_host_bridge*)cookie;
130
131	addr_t caoff = bridge->data_registers + (offset & 0x07);
132
133	if (uninorth_enable_config(bridge, bus, device, function, offset)) {
134		switch (size) {
135			case 1:
136				out8rb(caoff, (uint8)value);
137				(void)in8rb(caoff);
138				break;
139			case 2:
140				out16rb(caoff, (uint16)value);
141				(void)in16rb(caoff);
142				break;
143			case 4:
144				out32rb(caoff, value);
145				(void)in32rb(caoff);
146				break;
147		}
148	}
149
150	return B_OK;
151}
152
153
154static status_t uninorth_get_max_bus_devices(void *cookie, int32 *count)
155{
156	*count = 32;
157	return B_OK;
158}
159
160
161static status_t uninorth_read_pci_irq(void *cookie, uint8 bus, uint8 device,
162	uint8 function, uint8 pin, uint8 *irq)
163{
164	return B_ERROR;
165}
166
167
168static status_t uninorth_write_pci_irq(void *cookie, uint8 bus, uint8 device,
169	uint8 function, uint8 pin, uint8 irq)
170{
171	return B_ERROR;
172}
173
174
175// #pragma mark -
176
177
178static int
179uninorth_enable_config(struct uninorth_host_bridge *bridge, uint8 bus,
180	uint8 slot, uint8 function, uint8 offset)
181{
182//	uint32 pass;
183// 	if (resource_int_value(device_get_name(sc->sc_dev),
184// 	        device_get_unit(sc->sc_dev), "skipslot", &pass) == 0) {
185// 		if (pass == slot)
186// 			return (0);
187// 	}
188
189	uint32 cfgval;
190	if (bridge->bus == bus) {
191		/*
192		 * No slots less than 11 on the primary bus
193		 */
194		if (slot < 11)
195			return (0);
196
197		cfgval = (1 << slot) | (function << 8) | (offset & 0xfc);
198	} else {
199		cfgval = (bus << 16) | (slot << 11) | (function << 8) |
200		    (offset & 0xfc) | 1;
201	}
202
203	do {
204		out32rb(bridge->address_registers, cfgval);
205	} while (in32rb(bridge->address_registers) != cfgval);
206
207	return (1);
208}
209
210
211// #pragma mark -
212
213status_t
214ppc_openfirmware_probe_uninorth(int deviceNode,
215	const StringArrayPropertyValue &compatibleValue)
216{
217	if (!compatibleValue.ContainsElement("uni-north"))
218		return B_ERROR;
219
220	uint32 reg[2];
221	if (of_getprop(deviceNode, "reg", reg, sizeof(reg)) < 8)
222		return B_ERROR;
223
224	uint32 busrange[2];
225	if (of_getprop(deviceNode, "bus-range", busrange, sizeof(busrange)) != 8)
226		return B_ERROR;
227
228	uninorth_host_bridge *bridge
229		= (uninorth_host_bridge*)malloc(sizeof(uninorth_host_bridge));
230	if (!bridge)
231		return B_NO_MEMORY;
232
233	bridge->device_node = deviceNode;
234	bridge->address_registers = reg[0] + 0x800000;
235	bridge->data_registers = reg[0] + 0xc00000;
236	bridge->bus = busrange[0];
237// TODO: Check whether address and data registers have already been mapped by
238// the Open Firmware. If not, map them ourselves.
239
240	memset(bridge->ranges, 0, sizeof(bridge->ranges));
241	int bytesRead = of_getprop(deviceNode, "ranges", bridge->ranges,
242	    sizeof(bridge->ranges));
243
244	if (bytesRead < 0) {
245		dprintf("ppc_openfirmware_probe_uninorth: Could not get ranges.\n");
246		free(bridge);
247		return B_ERROR;
248	}
249	bridge->range_count = bytesRead / sizeof(uninorth_range);
250
251	uninorth_range *ioRange = NULL;
252	uninorth_range *memoryRanges[2];
253	int memoryRangeCount = 0;
254
255	for (int i = 0; i < bridge->range_count; i++) {
256		uninorth_range *range = bridge->ranges + i;
257		switch (range->pci_hi & OFW_PCI_PHYS_HI_SPACEMASK) {
258			case OFW_PCI_PHYS_HI_SPACE_CONFIG:
259				break;
260			case OFW_PCI_PHYS_HI_SPACE_IO:
261				ioRange = range;
262				break;
263			case OFW_PCI_PHYS_HI_SPACE_MEM32:
264				memoryRanges[memoryRangeCount++] = range;
265				break;
266			case OFW_PCI_PHYS_HI_SPACE_MEM64:
267				break;
268		}
269	}
270
271	if (ioRange == NULL) {
272		dprintf("ppc_openfirmware_probe_uninorth: Can't find io range.\n");
273		free(bridge);
274		return B_ERROR;
275	}
276
277	if (memoryRangeCount == 0) {
278		dprintf("ppc_openfirmware_probe_uninorth: Can't find mem ranges.\n");
279		free(bridge);
280		return B_ERROR;
281	}
282
283	status_t error = pci_controller_add(&sUniNorthPCIController, bridge);
284	if (error != B_OK)
285		free(bridge);
286	return error;
287}
288