Home | History | Annotate | Line # | Download | only in mpc5200
      1 /*	$NetBSD: mpc5200_pci.c,v 1.1 2026/06/27 13:28:35 rkujawa Exp $	*/
      2 
      3 /*-
      4  * Copyright (c) 2026 The NetBSD Foundation, Inc.
      5  * All rights reserved.
      6  *
      7  * This code is derived from software contributed to The NetBSD Foundation
      8  * by Radoslaw Kujawa.
      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 /*
     33  * MPC5200B PCI host bridge.
     34  */
     35 
     36 /* Expose the powerpc _bus_dma* back-ends for the coherent SoC DMA tag. */
     37 #define _POWERPC_BUS_DMA_PRIVATE
     38 
     39 #include "opt_pci.h"
     40 
     41 #include <sys/cdefs.h>
     42 __KERNEL_RCSID(0, "$NetBSD: mpc5200_pci.c,v 1.1 2026/06/27 13:28:35 rkujawa Exp $");
     43 
     44 #include <sys/param.h>
     45 #include <sys/device.h>
     46 #include <sys/kmem.h>
     47 #include <sys/systm.h>
     48 
     49 #include <dev/pci/pcivar.h>
     50 #include <dev/pci/pcireg.h>
     51 #include <dev/pci/pcidevs.h>
     52 #include <dev/pci/pciconf.h>
     53 #include <dev/ofw/openfirm.h>
     54 #include <dev/ofw/ofw_pci.h>
     55 
     56 #include <machine/autoconf.h>
     57 #include <machine/pio.h>
     58 #include <machine/cpu.h>		/* mapiodev() */
     59 
     60 #include <powerpc/mpc5200/mpc5200_pcivar.h>
     61 
     62 static int	mpcpci_match(device_t, cfdata_t, void *);
     63 static void	mpcpci_attach(device_t, device_t, void *);
     64 
     65 CFATTACH_DECL_NEW(mpcpci, sizeof(struct mpcpci_softc),
     66     mpcpci_match, mpcpci_attach, NULL, NULL);
     67 
     68 extern struct model_data modeldata;
     69 
     70 /* MPC5200 PCI Configuration Address Register: MBAR + PCI(0x0D00) + 0xF8. */
     71 #define	MPC5200_PCICAR_PADDR	0xf0000df8
     72 
     73 /*
     74  * DMA tag for bus-mastering PCI devices.  The MPC5200B XL bus does NOT snoop
     75  * bus-master traffic.
     76  */
     77 static struct powerpc_bus_dma_tag mpc5200_pci_bus_dma_tag = {
     78 	._dmamap_create = _bus_dmamap_create,
     79 	._dmamap_destroy = _bus_dmamap_destroy,
     80 	._dmamap_load = _bus_dmamap_load,
     81 	._dmamap_load_mbuf = _bus_dmamap_load_mbuf,
     82 	._dmamap_load_uio = _bus_dmamap_load_uio,
     83 	._dmamap_load_raw = _bus_dmamap_load_raw,
     84 	._dmamap_unload = _bus_dmamap_unload,
     85 	._dmamap_sync = _bus_dmamap_sync,
     86 	._dmamem_alloc = _bus_dmamem_alloc,
     87 	._dmamem_free = _bus_dmamem_free,
     88 	._dmamem_map = _bus_dmamem_map,
     89 	._dmamem_unmap = _bus_dmamem_unmap,
     90 	._dmamem_mmap = _bus_dmamem_mmap,
     91 };
     92 
     93 static pcireg_t
     94 mpcpci_conf_read(void *v, pcitag_t tag, int reg)
     95 {
     96 	pci_chipset_tag_t pc = v;
     97 	pcireg_t data;
     98 	int s;
     99 
    100 	if ((unsigned int)reg >= PCI_CONF_SIZE)
    101 		return (pcireg_t)-1;
    102 
    103 	s = splhigh();
    104 	out32(pc->pc_addr, tag | reg);		/* PCICAR: big-endian */
    105 	data = in32rb(pc->pc_data);		/* config data: little-endian */
    106 	out32(pc->pc_addr, 0);			/* clear E: restore I/O window */
    107 	splx(s);
    108 
    109 	return data;
    110 }
    111 
    112 static void
    113 mpcpci_conf_write(void *v, pcitag_t tag, int reg, pcireg_t data)
    114 {
    115 	pci_chipset_tag_t pc = v;
    116 	int s;
    117 
    118 	if ((unsigned int)reg >= PCI_CONF_SIZE)
    119 		return;
    120 
    121 	s = splhigh();
    122 	out32(pc->pc_addr, tag | reg);		/* PCICAR: big-endian */
    123 	out32rb(pc->pc_data, data);		/* config data: little-endian */
    124 	out32(pc->pc_addr, 0);			/* clear E: restore I/O window */
    125 	splx(s);
    126 }
    127 
    128 /*
    129  * The on-chip host bridge answers configuration cycles (as device 28 via its
    130  * IDSEL on AD28).
    131  */
    132 static int
    133 mpcpci_conf_hook(void *v, int bus, int dev, int func, pcireg_t id)
    134 {
    135 	pci_chipset_tag_t pc = v;
    136 	pcireg_t class;
    137 
    138 	class = pci_conf_read(pc, pci_make_tag(pc, bus, dev, func),
    139 	    PCI_CLASS_REG);
    140 
    141 	if (PCI_CLASS(class) == PCI_CLASS_BRIDGE &&
    142 	    (PCI_SUBCLASS(class) == PCI_SUBCLASS_BRIDGE_HOST ||
    143 	     PCI_SUBCLASS(class) == PCI_SUBCLASS_BRIDGE_MISC))
    144 		return 0;
    145 
    146 	return PCI_CONF_DEFAULT;
    147 }
    148 
    149 /*
    150  * Build the PCI interrupt-map property dictionary from the host bridge's
    151  * OpenFirmware "interrupt-map"...
    152  */
    153 static void
    154 mpcpci_setup_intrmap(pci_chipset_tag_t pc,
    155     struct genppc_pci_chipset_businfo *pbi, int pcinode)
    156 {
    157 	uint32_t map[160];
    158 	prop_dictionary_t dict, sub = NULL;
    159 	uint32_t acells, icells, pcells;
    160 	int parent, len, reclen, ndevs, curdev, i;
    161 
    162 	len = OF_getprop(pcinode, "interrupt-map", map, sizeof(map));
    163 	if (len <= 0) {
    164 		/* No interrupt-map; leave an empty dict (intr_map will fail). */
    165 		dict = prop_dictionary_create();
    166 		KASSERT(dict != NULL);
    167 		prop_dictionary_set(pbi->pbi_properties, "ofw-pci-intrmap", dict);
    168 		prop_object_release(dict);
    169 		return;
    170 	}
    171 
    172 	if (OF_getprop(pcinode, "#address-cells", &acells, sizeof(acells)) == -1)
    173 		acells = 1;
    174 	if (OF_getprop(pcinode, "#interrupt-cells", &icells, sizeof(icells))
    175 	    == -1)
    176 		icells = 1;
    177 	parent = map[acells + icells];
    178 	if (OF_getprop(parent, "#interrupt-cells", &pcells, sizeof(pcells)) == -1)
    179 		pcells = 1;
    180 
    181 	reclen = acells + pcells + icells + 1;
    182 	ndevs = len / (reclen * sizeof(uint32_t));
    183 
    184 	dict = prop_dictionary_create_with_capacity(ndevs * 2);
    185 	KASSERT(dict != NULL);
    186 	prop_dictionary_set(pbi->pbi_properties, "ofw-pci-intrmap", dict);
    187 
    188 	curdev = -1;
    189 	for (i = 0; i < ndevs; i++) {
    190 		prop_number_t intr_num;
    191 		uint32_t tier, source, line;
    192 		int dev, func, pin;
    193 		char key[20];
    194 
    195 		dev = (map[i * reclen] >> 8) / 0x8;
    196 		func = (map[i * reclen] >> 8) % 0x8;
    197 		pin = map[i * reclen + acells];
    198 		if (curdev != dev)
    199 			sub = prop_dictionary_create_with_capacity(4);
    200 
    201 		tier = map[i * reclen + acells + icells + 1];
    202 		source = (pcells >= 2) ? map[i * reclen + acells + icells + 2] : 0;
    203 		line = (tier << 5) + source;
    204 
    205 		intr_num = prop_number_create_integer(line);
    206 		snprintf(key, sizeof(key), "pin-%c", 'A' + (pin - 1));
    207 		prop_dictionary_set(sub, key, intr_num);
    208 		prop_object_release(intr_num);
    209 
    210 		snprintf(key, sizeof(key), "devfunc-%d", dev * 0x8 + func);
    211 		prop_dictionary_set(dict, key, sub);
    212 		if (curdev != dev) {
    213 			prop_object_release(sub);
    214 			curdev = dev;
    215 		}
    216 	}
    217 	prop_object_release(dict);
    218 	aprint_debug("%s\n", prop_dictionary_externalize(pbi->pbi_properties));
    219 }
    220 
    221 /*
    222  * Resolve a PCI interrupt to a flat IRQ via the interrupt-map dictionary built
    223  * above.
    224  */
    225 static int
    226 mpcpci_pci_intr_map(const struct pci_attach_args *pa, pci_intr_handle_t *ihp)
    227 {
    228 	struct genppc_pci_chipset_businfo *pbi;
    229 	prop_dictionary_t dict, devsub;
    230 	prop_object_t pinsub;
    231 	prop_number_t pbus;
    232 	int busno, pin, line, dev, origdev, func, i;
    233 	char key[20];
    234 
    235 	pin = pa->pa_intrpin;
    236 	line = pa->pa_intrline;
    237 	busno = pa->pa_bus;
    238 	origdev = dev = pa->pa_device;
    239 	func = pa->pa_function;
    240 	i = 0;
    241 
    242 	pbi = SIMPLEQ_FIRST(&pa->pa_pc->pc_pbi);
    243 	while (busno--)
    244 		pbi = SIMPLEQ_NEXT(pbi, next);
    245 	KASSERT(pbi != NULL);
    246 
    247 	dict = prop_dictionary_get(pbi->pbi_properties, "ofw-pci-intrmap");
    248 	if (dict != NULL)
    249 		i = prop_dictionary_count(dict);
    250 
    251 	if (dict == NULL || i == 0) {
    252 		/* Unmapped bus (behind a PCI-PCI bridge): walk to the parent. */
    253 		pbus = prop_dictionary_get(pbi->pbi_properties,
    254 		    "ofw-pcibus-parent");
    255 		if (pbus == NULL)
    256 			goto bad;
    257 		busno = prop_number_integer_value(pbus);
    258 		pbus = prop_dictionary_get(pbi->pbi_properties,
    259 		    "ofw-pcibus-rawdevnum");
    260 		dev = prop_number_integer_value(pbus);
    261 
    262 		pbi = SIMPLEQ_FIRST(&pa->pa_pc->pc_pbi);
    263 		while (busno--)
    264 			pbi = SIMPLEQ_NEXT(pbi, next);
    265 		KASSERT(pbi != NULL);
    266 
    267 		pin = ((pin + origdev - 1) & 3) + 1;	/* swizzle the pin */
    268 
    269 		dict = prop_dictionary_get(pbi->pbi_properties,
    270 		    "ofw-pci-intrmap");
    271 		if (dict == NULL)
    272 			goto bad;
    273 	}
    274 
    275 	if (pin == 0)			/* no interrupt used */
    276 		goto bad;
    277 	if (pin > 4) {
    278 		aprint_error("mpcpci_pci_intr_map: bad interrupt pin %d\n", pin);
    279 		goto bad;
    280 	}
    281 
    282 	snprintf(key, sizeof(key), "devfunc-%d", dev * 0x8 + func);
    283 	devsub = prop_dictionary_get(dict, key);
    284 	if (devsub == NULL)
    285 		goto bad;
    286 	snprintf(key, sizeof(key), "pin-%c", 'A' + (pin - 1));
    287 	pinsub = prop_dictionary_get(devsub, key);
    288 	if (pinsub == NULL)
    289 		goto bad;
    290 	line = prop_number_integer_value(pinsub);
    291 
    292 	if (line == 255) {		/* 0xff == PCI "unrouted" */
    293 		aprint_error("mpcpci_pci_intr_map: no mapping for pin %c\n",
    294 		    '@' + pin);
    295 		goto bad;
    296 	}
    297 
    298 	*ihp = line;			/* a real line, including flat IRQ 0 */
    299 	return 0;
    300 
    301 bad:
    302 	*ihp = -1;
    303 	return 1;
    304 }
    305 
    306 static void
    307 mpcpci_get_chipset_tag(pci_chipset_tag_t pc)
    308 {
    309 	pc->pc_conf_v = (void *)pc;
    310 
    311 	pc->pc_attach_hook = genppc_pci_indirect_attach_hook;
    312 	pc->pc_bus_maxdevs = genppc_pci_bus_maxdevs;
    313 	pc->pc_make_tag = genppc_pci_indirect_make_tag;
    314 	pc->pc_conf_read = mpcpci_conf_read;
    315 	pc->pc_conf_write = mpcpci_conf_write;
    316 
    317 	pc->pc_intr_v = (void *)pc;
    318 
    319 	pc->pc_intr_map = mpcpci_pci_intr_map;
    320 	pc->pc_intr_string = genppc_pci_intr_string;
    321 	pc->pc_intr_evcnt = genppc_pci_intr_evcnt;
    322 	pc->pc_intr_establish = genppc_pci_intr_establish;
    323 	pc->pc_intr_disestablish = genppc_pci_intr_disestablish;
    324 	pc->pc_intr_setattr = genppc_pci_intr_setattr;
    325 	pc->pc_intr_type = genppc_pci_intr_type;
    326 	pc->pc_intr_alloc = genppc_pci_intr_alloc;
    327 	pc->pc_intr_release = genppc_pci_intr_release;
    328 	pc->pc_intx_alloc = genppc_pci_intx_alloc;
    329 
    330 	pc->pc_msi_v = (void *)pc;
    331 	genppc_pci_chipset_msi_init(pc);
    332 
    333 	pc->pc_msix_v = (void *)pc;
    334 	genppc_pci_chipset_msix_init(pc);
    335 
    336 	pc->pc_conf_interrupt = genppc_pci_conf_interrupt;
    337 	pc->pc_decompose_tag = genppc_pci_indirect_decompose_tag;
    338 	pc->pc_conf_hook = mpcpci_conf_hook;
    339 
    340 	pc->pc_addr = 0;
    341 	pc->pc_data = 0;
    342 	pc->pc_bus = 0;
    343 	pc->pc_node = 0;
    344 	pc->pc_memt = 0;
    345 	pc->pc_iot = 0;
    346 	pc->pc_ihandle = 0;
    347 }
    348 
    349 static int
    350 mpcpci_match(device_t parent, cfdata_t cf, void *aux)
    351 {
    352 	struct confargs *ca = aux;
    353 	char name[32];
    354 
    355 	if (strcmp(ca->ca_name, "pci") != 0)
    356 		return 0;
    357 
    358 	memset(name, 0, sizeof(name));
    359 	OF_getprop(ca->ca_node, "device_type", name, sizeof(name));
    360 	if (strcmp(name, "pci") != 0)
    361 		return 0;
    362 
    363 	return 1;
    364 }
    365 
    366 static void
    367 mpcpci_attach(device_t parent, device_t self, void *aux)
    368 {
    369 	struct mpcpci_softc *sc = device_private(self);
    370 	pci_chipset_tag_t pc = &sc->sc_pc;
    371 	struct confargs *ca = aux;
    372 	struct pcibus_attach_args pba;
    373 	struct genppc_pci_chipset_businfo *pbi;
    374 	int node = ca->ca_node;
    375 	uint32_t busrange[2];
    376 
    377 	aprint_normal(": MPC5200B PCI host bridge\n");
    378 
    379 	sc->sc_dev = self;
    380 
    381 	if (OF_getprop(node, "bus-range", busrange, sizeof(busrange)) != 8) {
    382 		aprint_error_dev(self, "no bus-range property\n");
    383 		return;
    384 	}
    385 
    386 	/* PCI I/O and memory windows come from the firmware's /pci ranges. */
    387 	sc->sc_iot.pbs_flags = _BUS_SPACE_LITTLE_ENDIAN | _BUS_SPACE_IO_TYPE;
    388 	sc->sc_iot.pbs_base = 0x00000000;
    389 	if (ofwoea_map_space(RANGE_TYPE_PCI, RANGE_IO, node, &sc->sc_iot,
    390 	    "mpcpci io-space") != 0)
    391 		panic("mpcpci: can't init PCI io tag");
    392 
    393 	sc->sc_memt.pbs_flags = _BUS_SPACE_LITTLE_ENDIAN | _BUS_SPACE_MEM_TYPE;
    394 	sc->sc_memt.pbs_base = 0x00000000;
    395 	if (ofwoea_map_space(RANGE_TYPE_PCI, RANGE_MEM, node, &sc->sc_memt,
    396 	    "mpcpci mem-space") != 0)
    397 		panic("mpcpci: can't init PCI mem tag");
    398 
    399 	/*
    400 	 * Record the OFW interrupt-controller nodes (the SIU) so the PCI
    401 	 * interrupt-map routing below can resolve them. The SIU hardware was
    402 	 * already programmed by the mpc5200pic device, this is bookkeeping.
    403 	 */
    404 	init_ofppc_interrupt();
    405 
    406 	mpcpci_get_chipset_tag(pc);
    407 	pc->pc_node = node;
    408 	pc->pc_bus = busrange[0];
    409 	pc->pc_iot = &sc->sc_iot;
    410 	pc->pc_memt = &sc->sc_memt;
    411 
    412 	pc->pc_addr = mapiodev(MPC5200_PCICAR_PADDR, 4, false);
    413 	pc->pc_data = mapiodev(sc->sc_iot.pbs_offset, 4, false);
    414 	if (pc->pc_addr == NULL || pc->pc_data == NULL)
    415 		panic("mpcpci: can't map PCI config registers");
    416 
    417 	pbi = kmem_alloc(sizeof(struct genppc_pci_chipset_businfo), KM_SLEEP);
    418 	pbi->pbi_properties = prop_dictionary_create();
    419 	KASSERT(pbi->pbi_properties != NULL);
    420 	SIMPLEQ_INIT(&pc->pc_pbi);
    421 	SIMPLEQ_INSERT_TAIL(&pc->pc_pbi, pbi, next);
    422 
    423 	mpcpci_setup_intrmap(pc, pbi, pc->pc_node);
    424 
    425 #ifdef PCI_NETBSD_CONFIGURE
    426 	struct pciconf_resources *pcires = pciconf_resource_init();
    427 
    428 	pciconf_resource_add(pcires, PCICONF_RESOURCE_IO,
    429 	    modeldata.pciiodata[device_unit(self)].start,
    430 	    (modeldata.pciiodata[device_unit(self)].limit -
    431 	     modeldata.pciiodata[device_unit(self)].start) + 1);
    432 
    433 	pciconf_resource_add(pcires, PCICONF_RESOURCE_MEM,
    434 	    sc->sc_memt.pbs_base,
    435 	    (sc->sc_memt.pbs_limit - sc->sc_memt.pbs_base) + 1);
    436 
    437 	if (pci_configure_bus(pc, pcires, 0, CACHELINESIZE))
    438 		aprint_error_dev(self, "pci_configure_bus() failed\n");
    439 
    440 	pciconf_resource_fini(pcires);
    441 #endif /* PCI_NETBSD_CONFIGURE */
    442 
    443 	memset(&pba, 0, sizeof(pba));
    444 	pba.pba_memt = pc->pc_memt;
    445 	pba.pba_iot = pc->pc_iot;
    446 	pba.pba_dmat = &mpc5200_pci_bus_dma_tag;
    447 	pba.pba_dmat64 = NULL;
    448 	pba.pba_bus = pc->pc_bus;
    449 	pba.pba_bridgetag = NULL;
    450 	pba.pba_pc = pc;
    451 	pba.pba_flags = PCI_FLAGS_IO_OKAY | PCI_FLAGS_MEM_OKAY;
    452 	config_found(self, &pba, pcibusprint,
    453 	    CFARGS(.devhandle = device_handle(self)));
    454 }
    455