xref: /netbsd-src/sys/arch/prep/prep/platform.c (revision e5fbc36ada28f9b9a5836ecffaf4a06aa1ebb687)
1 /*	$NetBSD: platform.c,v 1.29 2023/12/20 15:29:06 thorpej Exp $	*/
2 
3 /*-
4  * Copyright (c) 2001 The NetBSD Foundation, Inc.
5  * All rights reserved.
6  *
7  * This code is derived from software contributed to The NetBSD Foundation
8  * by NONAKA Kimihiro.
9  *
10  * Redistribution and use in source and binary forms, with or without
11  * modification, are permitted provided that the following conditions
12  * are met:
13  * 1. Redistributions of source code must retain the above copyright
14  *    notice, this list of conditions and the following disclaimer.
15  * 2. Redistributions in binary form must reproduce the above copyright
16  *    notice, this list of conditions and the following disclaimer in the
17  *    documentation and/or other materials provided with the distribution.
18  *
19  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
20  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
21  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
22  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
23  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
24  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
25  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
27  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29  * POSSIBILITY OF SUCH DAMAGE.
30  */
31 
32 #include <sys/cdefs.h>
33 __KERNEL_RCSID(0, "$NetBSD: platform.c,v 1.29 2023/12/20 15:29:06 thorpej Exp $");
34 
35 #include <sys/param.h>
36 #include <sys/systm.h>
37 #include <sys/intr.h>
38 #include <sys/inttypes.h>
39 
40 #include <powerpc/pio.h>
41 #include <powerpc/psl.h>
42 
43 #include <dev/pci/pcivar.h>
44 
45 #include <machine/platform.h>
46 #include <machine/pcipnp.h>
47 #include <machine/residual.h>
48 
49 u_int32_t prep_pci_baseaddr = 0x80000cf8;
50 u_int32_t prep_pci_basedata = 0x80000cfc;
51 
52 struct pciroutinginfo *pciroutinginfo;
53 extern struct prep_pci_chipset *genppc_pct;
54 
55 extern void pci_intr_fixup_ibm_6015(void);
56 #if NMCCLOCK > 0
57 /* from mcclock_pnpbus.c */
58 extern void ds1585_reboot(void);
59 #endif
60 
61 struct platform_quirkdata platform_quirks[] = {
62 	{ "IBM PPS Model 6015", PLAT_QUIRK_INTRFIXUP,
63 	   pci_intr_fixup_ibm_6015, NULL, 0 },
64 	{ "(e1)", PLAT_QUIRK_ISA_HANDLER, NULL, NULL, EXT_INTR_I8259 },
65 	{ "000000000000000000000000000(e2)", PLAT_QUIRK_ISA_HANDLER, NULL,
66 	   NULL, EXT_INTR_I8259 },
67 	{ NULL, 0, NULL, NULL, 0 }
68 };
69 
70 /* find the platform quirk entry for this model, -1 if none */
71 
72 int
find_platform_quirk(const char * model)73 find_platform_quirk(const char *model)
74 {
75 	int i;
76 
77 	for (i = 0; platform_quirks[i].model != NULL; i++)
78 		if (strcmp(model, platform_quirks[i].model) == 0)
79 			return i;
80 	return -1;
81 }
82 
83 /* XXX This should be conditional on finding L2 in residual */
84 void
cpu_setup_prep_generic(device_t dev)85 cpu_setup_prep_generic(device_t dev)
86 {
87 	u_int8_t l2ctrl;
88 
89 	/* system control register */
90 	l2ctrl = inb(PREP_BUS_SPACE_IO + 0x81c);
91 	/* device status register */
92 	(void)inb(PREP_BUS_SPACE_IO + 0x80c);
93 
94 	/* Enable L2 cache */
95 	outb(PREP_BUS_SPACE_IO + 0x81c, l2ctrl | 0xc0);
96 }
97 
98 /* We don't bus_space_map this because it can happen early in boot */
99 static void
reset_prep_generic(void)100 reset_prep_generic(void)
101 {
102 	u_int8_t reg;
103 
104 	mtmsr(mfmsr() | PSL_IP);
105 
106 #if NMCCLOCK > 0
107 	/* XXX This is a special hack for 7024 and 7025 models, which have
108 	 * no obvious method of rebooting. We call this, because it will
109 	 * return if we do not have a 1585.
110 	 */
111 	ds1585_reboot();
112 #endif
113 
114 	reg = inb(PREP_BUS_SPACE_IO + 0x92);
115 	reg &= ~1UL;
116 	outb(PREP_BUS_SPACE_IO + 0x92, reg);
117 	reg = inb(PREP_BUS_SPACE_IO + 0x92);
118 	reg |= 1;
119 	outb(PREP_BUS_SPACE_IO + 0x92, reg);
120 }
121 
122 void
reset_prep(void)123 reset_prep(void)
124 {
125 	int i;
126 
127 	i = find_platform_quirk(res->VitalProductData.PrintableModel);
128 	if (i != -1) {
129 		if (platform_quirks[i].quirk & PLAT_QUIRK_RESET &&
130 		    platform_quirks[i].reset != NULL)
131 			(*platform_quirks[i].reset)();
132 	}
133 	reset_prep_generic();
134 }
135 
136 /*
137  * Gather the data needed to route interrupts on this machine from
138  * the residual data.
139  */
140 
141 
142 /* Count the number of PCI devices on a given pci bus */
143 
144 static int
count_pnp_pci_devices(void * v,int * device)145 count_pnp_pci_devices(void *v, int *device)
146 {
147 
148 	int item, size, i;
149 	int tag = *(unsigned char *)v;
150 	unsigned char *q = v;
151 	struct _L4_Pack *pack = v;
152 	struct _L4_PPCPack *p =  &pack->L4_Data.L4_PPCPack;
153 
154 	item = tag_large_item_name(tag);
155 	size = (q[1] | (q[2] << 8)) + 3 /* tag + length */;
156 
157 	if (item != LargeVendorItem)
158 		return size;
159 	if (p->Type != LV_PCIBridge)
160 		return size;
161 
162 	/* offset 20 begins irqmap, of 12 bytes each */
163 	for (i = 20; i < size - 4; i += 12)
164 		(*device)++;
165 
166 	return size;
167 }
168 
169 /* Nop for small pnp packets */
170 
171 static int
pnp_small_pkt(void * v)172 pnp_small_pkt(void *v)
173 {
174 	int tag = *(unsigned char *)v;
175 
176 	return tag_small_count(tag) + 1 /* tag */;
177 }
178 
179 /*
180  * We look to see what kind of bridge this is, and return it.  If we have
181  * 1.1 residual, we also look up the bridge data, and get the config base
182  * address from it.  We set a default sane value for the config base addr
183  * at initialization, so it shouldn't matter if we can't find one here.
184  */
185 
186 int
pci_chipset_tag_type(void)187 pci_chipset_tag_type(void)
188 {
189 	PPC_DEVICE *dev;
190 	uint32_t addr, data, l;
191 	unsigned char *p;
192 	int size;
193 
194 	dev = find_nth_pnp_device("PNP0A03", 0, 0);
195 	if (dev == NULL)
196 		return PCIBridgeIndirect;
197 
198 	l = be32toh(dev->AllocatedOffset);
199 	p = res->DevicePnPHeap + l;
200 	if (p == NULL)
201 		return PCIBridgeIndirect;
202 
203 	/* gather the pci base address from PNP */
204 	for (; p[0] != END_TAG; p += size) {
205 		if (tag_type(p[0]) == PNP_SMALL)
206 			size = pnp_small_pkt(p);
207 		else {
208 			size = pnp_pci_configbase(p, &addr, &data);
209 			if (addr != 0 && data != 0) {
210 				prep_pci_baseaddr = addr;
211 				prep_pci_basedata = data;
212 				break;
213 			}
214 		}
215 	}
216 
217 	return dev->DeviceId.Interface;
218 }
219 
220 static int
create_intr_map(void * v,prop_dictionary_t dict)221 create_intr_map(void *v, prop_dictionary_t dict)
222 {
223 	prop_dictionary_t sub;
224 	int item, size, i, j, numslots;
225 	int tag = *(unsigned char *)v;
226 	unsigned char *q = v;
227 	PCIInfoPack *pi = v;
228 	struct _L4_Pack *pack = v;
229 	struct _L4_PPCPack *p = &pack->L4_Data.L4_PPCPack;
230 
231 	item = tag_large_item_name(tag);
232 	size = (q[1] | (q[2] << 8)) + 3 /* tag + length */;
233 
234 	if (item != LargeVendorItem)
235 		return size;
236 	if (p->Type != LV_PCIBridge) /* PCI Bridge type */
237 		return size;
238 
239 	numslots = (le16dec(&pi->count0)-21)/sizeof(IntrMap);
240 
241 	for (i = 0; i < numslots; i++) {
242 		int lines[MAX_PCI_INTRS] = { 0, 0, 0, 0 };
243 		int offset = 0;
244 		int dev;
245 		char key[20];
246 
247 		sub = prop_dictionary_create_with_capacity(MAX_PCI_INTRS);
248 		dev = pi->map[i].devfunc / 0x8;
249 
250 		for (j = 0; j < MAX_PCI_INTRS; j++) {
251 			int line = bswap16(pi->map[i].intr[j]);
252 
253 			if (line != 0xffff) /*unusable*/
254 				lines[j] = 1;
255 		}
256 		if (pi->map[i].intrctrltype == 2) /* MPIC */
257 			offset += I8259_INTR_NUM;
258 		for (j = 0; j < MAX_PCI_INTRS; j++) {
259 			int line = bswap16(pi->map[i].intr[j]);
260 			prop_number_t intr_num;
261 
262 			if (line == 0xffff || lines[j] == 0)
263 				intr_num = prop_number_create_integer(0);
264 			else
265 				intr_num = prop_number_create_integer(
266 				    (line & 0x7fff) + offset);
267 			snprintf(key, sizeof(key), "pin-%c", 'A' + j);
268 			prop_dictionary_set(sub, key, intr_num);
269 			prop_object_release(intr_num);
270 		}
271 		snprintf(key, sizeof(key), "devfunc-%d", dev);
272 		prop_dictionary_set(dict, key, sub);
273 		prop_object_release(sub);
274 	}
275 	return size;
276 }
277 
278 /*
279  * Decode the interrupt mappings from PnP, and write them into a device
280  * property attached to the PCI bus.
281  * The bus, device and func arguments are the PCI locators where the bridge
282  * device was FOUND.
283  */
284 void
setup_pciintr_map(struct genppc_pci_chipset_businfo * pbi,int bus,int device,int func)285 setup_pciintr_map(struct genppc_pci_chipset_businfo *pbi, int bus, int device,
286 	int func)
287 {
288 	int devfunc, nbus, size, i, found = 0, nrofpcidevs = 0;
289 	uint32_t l;
290 	PPC_DEVICE *dev;
291 	BUS_ACCESS *busacc;
292 	unsigned char *p;
293 	prop_dictionary_t dict;
294 
295 	/* revision 0 residual does not have valid pci bridge data */
296 	if (res->Revision == 0)
297 		return;
298 
299 	devfunc = device * 8 + func;
300 
301 	nbus = count_pnp_devices("PNP0A03");
302 	for (i = 0; i < nbus; i++) {
303 		dev = find_nth_pnp_device("PNP0A03", 0, i);
304 		busacc = &dev->BusAccess;
305 		l = be32toh(dev->AllocatedOffset);
306 		p = res->DevicePnPHeap + l;
307 		if (p == NULL)
308 			return;
309 		if (busacc->PCIAccess.BusNumber == bus &&
310 		    busacc->PCIAccess.DevFuncNumber == devfunc) {
311 			found++;
312 			break;
313 		}
314 	}
315 	if (!found) {
316 		aprint_error("Couldn't find PNP data for bus %d devfunc 0x%x\n",
317 		    bus, devfunc);
318 		return;
319 	}
320 	/* p, l and dev should be valid now */
321 
322 	/* count the number of PCI device slots on the bus */
323 	for (; p[0] != END_TAG; p += size) {
324 		if (tag_type(p[0]) == PNP_SMALL)
325 			size = pnp_small_pkt(p);
326 		else
327 			size = count_pnp_pci_devices(p, &nrofpcidevs);
328 	}
329 	dict = prop_dictionary_create_with_capacity(nrofpcidevs*2);
330 	KASSERT(dict != NULL);
331 
332 	prop_dictionary_set(pbi->pbi_properties, "prep-pci-intrmap", dict);
333 
334 	/* reset p */
335 	p = res->DevicePnPHeap + l;
336 	/* now we've created the dictionary, loop again and add the sub-dicts */
337 	for (; p[0] != END_TAG; p += size) {
338 		if (tag_type(p[0]) == PNP_SMALL)
339 			size = pnp_small_pkt(p);
340 		else
341 			size = create_intr_map(p, dict);
342 	}
343 	prop_object_release(dict);
344 }
345 
346 
347 /*
348  * Some platforms have invalid or insufficient PCI routing information
349  * in the residual.  Check the quirk table and if we find one, call it.
350  */
351 
352 void
setup_pciroutinginfo(void)353 setup_pciroutinginfo(void)
354 {
355 	int i;
356 
357 	i = find_platform_quirk(res->VitalProductData.PrintableModel);
358 	if (i == -1)
359 		return;
360 	if (platform_quirks[i].quirk & PLAT_QUIRK_INTRFIXUP &&
361 	    platform_quirks[i].pci_intr_fixup != NULL)
362 		(*platform_quirks[i].pci_intr_fixup)();
363 }
364