1/*
2 * Copyright 2010 Andreas Färber <andreas.faerber@web.de>
3 * Copyright 2006, Ingo Weinhold <bonefish@cs.tu-berlin.de>.
4 * All rights reserved. Distributed under the terms of the MIT License.
5 */
6/*-
7 * Copyright (c) 2000 Tsubai Masanari.  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 * 3. The name of the author may not be used to endorse or promote products
18 *    derived from this software without specific prior written permission.
19 *
20 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
21 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
22 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
23 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
24 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
25 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
26 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
27 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
28 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
29 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30 */
31
32
33#include <KernelExport.h>
34
35#include <platform/openfirmware/devices.h>
36#include <platform/openfirmware/openfirmware.h>
37#include <platform/openfirmware/pci.h>
38
39#include "pci_controller.h"
40#include "pci_io.h"
41#include "pci_openfirmware_priv.h"
42
43
44//#define TRACE_GRACKLE
45
46#ifdef TRACE_GRACKLE
47#define TRACE(fmt...) dprintf(fmt)
48#else
49#define TRACE(fmt...) ;
50#endif
51
52
53struct grackle_range {
54	uint32_t	pci_hi;
55	uint32_t	pci_mid;
56	uint32_t	pci_lo;
57	uint32_t	host;
58	uint32_t	size_hi;
59	uint32_t	size_lo;
60};
61
62
63struct grackle_host_bridge {
64	int						device_node;
65	addr_t					address_registers;
66	addr_t					data_registers;
67	uint32					bus;
68	struct grackle_range	ranges[6];
69	int						range_count;
70};
71
72
73#define out8rb(address, value)	ppc_out8((vuint8*)(address), value)
74#define out16rb(address, value)	ppc_out16_reverse((vuint16*)(address), value)
75#define out32rb(address, value)	ppc_out32_reverse((vuint32*)(address), value)
76#define in8rb(address)			ppc_in8((const vuint8*)(address))
77#define in16rb(address)			ppc_in16_reverse((const vuint16*)(address))
78#define in32rb(address)			ppc_in32_reverse((const vuint32*)(address))
79
80
81static status_t
82grackle_read_pci_config(void *cookie, uint8 bus, uint8 device, uint8 function,
83	uint8 offset, uint8 size, uint32 *value)
84{
85	grackle_host_bridge *bridge = (grackle_host_bridge*)cookie;
86	TRACE("grackle_read_pci_config(bus=%u, dev=%u, func=%u, offset=%u, "
87		"size=%u)\n", (int)bus, (int)device, (int)function, (int)offset,
88		(int)size);
89
90	out32rb(bridge->address_registers, (1 << 31)
91		| (bus << 16) | ((device & 0x1f) << 11) | ((function & 0x7) << 8)
92		| (offset & 0xfc));
93
94	addr_t dataAddress = bridge->data_registers + (offset & 0x3);
95	switch (size) {
96		case 1:
97			*value = in8rb(dataAddress);
98			break;
99		case 2:
100			*value = in16rb(dataAddress);
101			break;
102		case 4:
103			*value = in32rb(dataAddress);
104			break;
105		default:
106			*value = 0xffffffff;
107			break;
108	}
109
110	out32rb(bridge->address_registers, 0);
111
112	return B_OK;
113}
114
115
116static status_t
117grackle_write_pci_config(void *cookie, uint8 bus, uint8 device, uint8 function,
118	uint8 offset, uint8 size, uint32 value)
119{
120	grackle_host_bridge *bridge = (grackle_host_bridge*)cookie;
121	TRACE("grackle_write_pci_config(bus=%u, dev=%u, func=%u, offset=%u, "
122		"size=%u, value=%lu)\n", (int)bus, (int)device, (int)function,
123		(int)offset, (int)size, value);
124
125	out32rb(bridge->address_registers, (1 << 31)
126		| (bus << 16) | ((device & 0x1f) << 11) | ((function & 0x7) << 8)
127		| (offset & 0xfc));
128
129	addr_t dataAddress = bridge->data_registers + (offset & 0x3);
130	switch (size) {
131		case 1:
132			out8rb(dataAddress, (uint8)value);
133			break;
134		case 2:
135			out16rb(dataAddress, (uint16)value);
136			break;
137		case 4:
138			out32rb(dataAddress, value);
139			break;
140	}
141
142	out32rb(bridge->address_registers, 0);
143
144	return B_OK;
145}
146
147
148static status_t
149grackle_get_max_bus_devices(void *cookie, int32 *count)
150{
151	*count = 32;
152	return B_OK;
153}
154
155
156static status_t
157grackle_read_pci_irq(void *cookie, uint8 bus, uint8 device, uint8 function,
158	uint8 pin, uint8 *irq)
159{
160	return B_ERROR;
161}
162
163
164static status_t
165grackle_write_pci_irq(void *cookie, uint8 bus, uint8 device, uint8 function,
166	uint8 pin, uint8 irq)
167{
168	return B_ERROR;
169}
170
171
172static pci_controller sGracklePCIController = {
173	grackle_read_pci_config,
174	grackle_write_pci_config,
175	grackle_get_max_bus_devices,
176	grackle_read_pci_irq,
177	grackle_write_pci_irq,
178};
179
180
181status_t
182ppc_openfirmware_probe_grackle(int deviceNode,
183	const StringArrayPropertyValue &compatibleValue)
184{
185	if (!compatibleValue.ContainsElement("grackle"))
186		return B_ERROR;
187
188	uint32_t busrange[2];
189	if (of_getprop(deviceNode, "bus-range", busrange, sizeof(busrange)) != 8)
190		return B_ERROR;
191
192	grackle_host_bridge *bridge =
193		(grackle_host_bridge*)malloc(sizeof(grackle_host_bridge));
194	if (bridge == NULL)
195		return B_NO_MEMORY;
196
197	bridge->device_node = deviceNode;
198	bridge->address_registers = 0xfec00000;
199	bridge->data_registers = 0xfee00000;
200	bridge->bus = busrange[0];
201
202	memset(bridge->ranges, 0, sizeof(bridge->ranges));
203	int bytesRead = of_getprop(deviceNode, "ranges", bridge->ranges,
204	    sizeof(bridge->ranges));
205	if (bytesRead < 0) {
206		dprintf("ppc_openfirmware_probe_grackle: Could not get ranges.\n");
207		free(bridge);
208		return B_ERROR;
209	}
210	bridge->range_count = bytesRead / sizeof(grackle_range);
211
212	grackle_range *ioRange = NULL;
213	grackle_range *memoryRanges[2];
214	int memoryRangeCount = 0;
215
216	for (int i = 0; i < bridge->range_count; i++) {
217		grackle_range *range = bridge->ranges + i;
218		switch (range->pci_hi & OFW_PCI_PHYS_HI_SPACEMASK) {
219			case OFW_PCI_PHYS_HI_SPACE_CONFIG:
220				break;
221			case OFW_PCI_PHYS_HI_SPACE_IO:
222				ioRange = range;
223				break;
224			case OFW_PCI_PHYS_HI_SPACE_MEM32:
225				memoryRanges[memoryRangeCount++] = range;
226				break;
227			case OFW_PCI_PHYS_HI_SPACE_MEM64:
228				break;
229		}
230	}
231
232	if (ioRange == NULL) {
233		dprintf("ppc_openfirmware_probe_grackle: Can't find io range.\n");
234		free(bridge);
235		return B_ERROR;
236	}
237
238	if (memoryRangeCount == 0) {
239		dprintf("ppc_openfirmware_probe_grackle: Can't find mem ranges.\n");
240		free(bridge);
241		return B_ERROR;
242	}
243
244	status_t error = pci_controller_add(&sGracklePCIController, bridge);
245	if (error != B_OK)
246		free(bridge);
247	return error;
248}
249