1*1377ad26Sskrll /* $NetBSD: pcihost_fdt.c,v 1.33 2024/01/12 11:24:48 skrll Exp $ */
218d2f7fcSjmcneill
318d2f7fcSjmcneill /*-
418d2f7fcSjmcneill * Copyright (c) 2018 Jared D. McNeill <jmcneill@invisible.ca>
518d2f7fcSjmcneill * All rights reserved.
618d2f7fcSjmcneill *
718d2f7fcSjmcneill * Redistribution and use in source and binary forms, with or without
818d2f7fcSjmcneill * modification, are permitted provided that the following conditions
918d2f7fcSjmcneill * are met:
1018d2f7fcSjmcneill * 1. Redistributions of source code must retain the above copyright
1118d2f7fcSjmcneill * notice, this list of conditions and the following disclaimer.
1218d2f7fcSjmcneill * 2. Redistributions in binary form must reproduce the above copyright
1318d2f7fcSjmcneill * notice, this list of conditions and the following disclaimer in the
1418d2f7fcSjmcneill * documentation and/or other materials provided with the distribution.
1518d2f7fcSjmcneill *
1618d2f7fcSjmcneill * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
1718d2f7fcSjmcneill * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
1818d2f7fcSjmcneill * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
1918d2f7fcSjmcneill * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
2018d2f7fcSjmcneill * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
2118d2f7fcSjmcneill * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
2218d2f7fcSjmcneill * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
2318d2f7fcSjmcneill * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
2418d2f7fcSjmcneill * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
2518d2f7fcSjmcneill * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
2618d2f7fcSjmcneill * SUCH DAMAGE.
2718d2f7fcSjmcneill */
2818d2f7fcSjmcneill
2918d2f7fcSjmcneill #include <sys/cdefs.h>
30*1377ad26Sskrll __KERNEL_RCSID(0, "$NetBSD: pcihost_fdt.c,v 1.33 2024/01/12 11:24:48 skrll Exp $");
3118d2f7fcSjmcneill
3218d2f7fcSjmcneill #include <sys/param.h>
33f8a0ef89Sskrll
3418d2f7fcSjmcneill #include <sys/bus.h>
3518d2f7fcSjmcneill #include <sys/device.h>
3618d2f7fcSjmcneill #include <sys/intr.h>
3718d2f7fcSjmcneill #include <sys/kernel.h>
3818d2f7fcSjmcneill #include <sys/kmem.h>
3958bc9390Sskrll #include <sys/lwp.h>
40f8a0ef89Sskrll #include <sys/mutex.h>
41f8a0ef89Sskrll #include <sys/queue.h>
42f8a0ef89Sskrll #include <sys/systm.h>
4318d2f7fcSjmcneill
4418d2f7fcSjmcneill #include <machine/cpu.h>
4518d2f7fcSjmcneill
4618d2f7fcSjmcneill #include <arm/cpufunc.h>
4718d2f7fcSjmcneill
4818d2f7fcSjmcneill #include <dev/pci/pcireg.h>
4918d2f7fcSjmcneill #include <dev/pci/pcivar.h>
5018d2f7fcSjmcneill #include <dev/pci/pciconf.h>
5118d2f7fcSjmcneill
5218d2f7fcSjmcneill #include <dev/fdt/fdtvar.h>
5318d2f7fcSjmcneill
54dfdf9722Sjmcneill #include <arm/pci/pci_msi_machdep.h>
55c3e7c8a7Sjakllsch #include <arm/fdt/pcihost_fdtvar.h>
56dfdf9722Sjmcneill
5718d2f7fcSjmcneill #define PCIHOST_DEFAULT_BUS_MIN 0
5818d2f7fcSjmcneill #define PCIHOST_DEFAULT_BUS_MAX 255
5918d2f7fcSjmcneill
6018d2f7fcSjmcneill #define PCIHOST_CACHELINE_SIZE arm_dcache_align
6118d2f7fcSjmcneill
62c3e7c8a7Sjakllsch int pcihost_segment = 0;
6318d2f7fcSjmcneill
6418d2f7fcSjmcneill static int pcihost_match(device_t, cfdata_t, void *);
6518d2f7fcSjmcneill static void pcihost_attach(device_t, device_t, void *);
6618d2f7fcSjmcneill
6718d2f7fcSjmcneill static int pcihost_config(struct pcihost_softc *);
6818d2f7fcSjmcneill
6918d2f7fcSjmcneill static void pcihost_attach_hook(device_t, device_t,
7018d2f7fcSjmcneill struct pcibus_attach_args *);
7118d2f7fcSjmcneill static int pcihost_bus_maxdevs(void *, int);
7218d2f7fcSjmcneill static pcitag_t pcihost_make_tag(void *, int, int, int);
7318d2f7fcSjmcneill static void pcihost_decompose_tag(void *, pcitag_t, int *, int *, int *);
74dfdf9722Sjmcneill static u_int pcihost_get_segment(void *);
7518d2f7fcSjmcneill static pcireg_t pcihost_conf_read(void *, pcitag_t, int);
7618d2f7fcSjmcneill static void pcihost_conf_write(void *, pcitag_t, int, pcireg_t);
7718d2f7fcSjmcneill static int pcihost_conf_hook(void *, int, int, int, pcireg_t);
7818d2f7fcSjmcneill static void pcihost_conf_interrupt(void *, int, int, int, int, int *);
7918d2f7fcSjmcneill
8018d2f7fcSjmcneill static int pcihost_intr_map(const struct pci_attach_args *,
8118d2f7fcSjmcneill pci_intr_handle_t *);
8218d2f7fcSjmcneill static const char *pcihost_intr_string(void *, pci_intr_handle_t,
8318d2f7fcSjmcneill char *, size_t);
84201aaa4dSjakllsch static const struct evcnt *pcihost_intr_evcnt(void *, pci_intr_handle_t);
8518d2f7fcSjmcneill static int pcihost_intr_setattr(void *, pci_intr_handle_t *, int,
8618d2f7fcSjmcneill uint64_t);
8718d2f7fcSjmcneill static void * pcihost_intr_establish(void *, pci_intr_handle_t,
88cce19cc2Sjmcneill int, int (*)(void *), void *,
89cce19cc2Sjmcneill const char *);
9018d2f7fcSjmcneill static void pcihost_intr_disestablish(void *, void *);
9118d2f7fcSjmcneill
923689f53fSjakllsch static int pcihost_bus_space_map(void *, bus_addr_t, bus_size_t,
933689f53fSjakllsch int, bus_space_handle_t *);
943689f53fSjakllsch
9518d2f7fcSjmcneill CFATTACH_DECL_NEW(pcihost_fdt, sizeof(struct pcihost_softc),
9618d2f7fcSjmcneill pcihost_match, pcihost_attach, NULL, NULL);
9718d2f7fcSjmcneill
98646c0f59Sthorpej static const struct device_compatible_entry compat_data[] = {
99646c0f59Sthorpej { .compat = "pci-host-cam-generic", .value = PCIHOST_CAM },
100646c0f59Sthorpej { .compat = "pci-host-ecam-generic", .value = PCIHOST_ECAM },
1012dcdd1cdSthorpej DEVICE_COMPAT_EOL
10218d2f7fcSjmcneill };
10318d2f7fcSjmcneill
104ff4e9f70Sskrll struct pcihost_msi_handler {
105ff4e9f70Sskrll LIST_ENTRY(pcihost_msi_handler) pmh_next;
106ff4e9f70Sskrll void *pmh_ih;
107ff4e9f70Sskrll };
108ff4e9f70Sskrll
109ff4e9f70Sskrll
11018d2f7fcSjmcneill static int
pcihost_match(device_t parent,cfdata_t cf,void * aux)11118d2f7fcSjmcneill pcihost_match(device_t parent, cfdata_t cf, void *aux)
11218d2f7fcSjmcneill {
11318d2f7fcSjmcneill struct fdt_attach_args * const faa = aux;
11418d2f7fcSjmcneill
1156e54367aSthorpej return of_compatible_match(faa->faa_phandle, compat_data);
11618d2f7fcSjmcneill }
11718d2f7fcSjmcneill
11818d2f7fcSjmcneill static void
pcihost_attach(device_t parent,device_t self,void * aux)11918d2f7fcSjmcneill pcihost_attach(device_t parent, device_t self, void *aux)
12018d2f7fcSjmcneill {
12118d2f7fcSjmcneill struct pcihost_softc * const sc = device_private(self);
12218d2f7fcSjmcneill struct fdt_attach_args * const faa = aux;
12318d2f7fcSjmcneill bus_addr_t cs_addr;
12418d2f7fcSjmcneill bus_size_t cs_size;
125c3e7c8a7Sjakllsch int error;
12618d2f7fcSjmcneill
12718d2f7fcSjmcneill if (fdtbus_get_reg(faa->faa_phandle, 0, &cs_addr, &cs_size) != 0) {
12818d2f7fcSjmcneill aprint_error(": couldn't get registers\n");
12918d2f7fcSjmcneill return;
13018d2f7fcSjmcneill }
13118d2f7fcSjmcneill
13218d2f7fcSjmcneill sc->sc_dev = self;
13318d2f7fcSjmcneill sc->sc_dmat = faa->faa_dmat;
13418d2f7fcSjmcneill sc->sc_bst = faa->faa_bst;
13577a7b674Sjmcneill sc->sc_pci_bst = faa->faa_bst;
13618d2f7fcSjmcneill sc->sc_phandle = faa->faa_phandle;
137059f233aSjmcneill error = bus_space_map(sc->sc_bst, cs_addr, cs_size,
1385158b98cSjmcneill BUS_SPACE_MAP_NONPOSTED, &sc->sc_bsh);
13918d2f7fcSjmcneill if (error) {
14018d2f7fcSjmcneill aprint_error(": couldn't map registers: %d\n", error);
14118d2f7fcSjmcneill return;
14218d2f7fcSjmcneill }
1436e54367aSthorpej sc->sc_type = of_compatible_lookup(sc->sc_phandle, compat_data)->value;
14418d2f7fcSjmcneill
1452e99e80aSjmcneill #ifdef __HAVE_PCI_MSI_MSIX
1462e99e80aSjmcneill if (sc->sc_type == PCIHOST_ECAM) {
1472e99e80aSjmcneill sc->sc_pci_flags |= PCI_FLAGS_MSI_OKAY;
1482e99e80aSjmcneill sc->sc_pci_flags |= PCI_FLAGS_MSIX_OKAY;
1492e99e80aSjmcneill }
1502e99e80aSjmcneill #endif
1512e99e80aSjmcneill
15218d2f7fcSjmcneill aprint_naive("\n");
15318d2f7fcSjmcneill aprint_normal(": Generic PCI host controller\n");
15418d2f7fcSjmcneill
155c3e7c8a7Sjakllsch pcihost_init(&sc->sc_pc, sc);
156c3e7c8a7Sjakllsch pcihost_init2(sc);
157c3e7c8a7Sjakllsch }
158c3e7c8a7Sjakllsch
159c3e7c8a7Sjakllsch void
pcihost_init2(struct pcihost_softc * sc)160c3e7c8a7Sjakllsch pcihost_init2(struct pcihost_softc *sc)
161c3e7c8a7Sjakllsch {
162c3e7c8a7Sjakllsch struct pcibus_attach_args pba;
163c3e7c8a7Sjakllsch const u_int *data;
164c3e7c8a7Sjakllsch int len;
165c3e7c8a7Sjakllsch
16618d2f7fcSjmcneill if ((data = fdtbus_get_prop(sc->sc_phandle, "bus-range", &len)) != NULL) {
16718d2f7fcSjmcneill if (len != 8) {
168c3e7c8a7Sjakllsch aprint_error_dev(sc->sc_dev, "malformed 'bus-range' property\n");
16918d2f7fcSjmcneill return;
17018d2f7fcSjmcneill }
17118d2f7fcSjmcneill sc->sc_bus_min = be32toh(data[0]);
17218d2f7fcSjmcneill sc->sc_bus_max = be32toh(data[1]);
17318d2f7fcSjmcneill } else {
17418d2f7fcSjmcneill sc->sc_bus_min = PCIHOST_DEFAULT_BUS_MIN;
17518d2f7fcSjmcneill sc->sc_bus_max = PCIHOST_DEFAULT_BUS_MAX;
17618d2f7fcSjmcneill }
17718d2f7fcSjmcneill
178dfdf9722Sjmcneill /*
179dfdf9722Sjmcneill * Assign a fixed PCI segment ("domain") number. If the property is not
180dfdf9722Sjmcneill * present, assign one. The binding spec says if this property is used to
181dfdf9722Sjmcneill * assign static segment numbers, all host bridges should have segments
182dfdf9722Sjmcneill * astatic assigned to prevent overlaps.
183dfdf9722Sjmcneill */
184dfdf9722Sjmcneill if (of_getprop_uint32(sc->sc_phandle, "linux,pci-domain", &sc->sc_seg))
185dfdf9722Sjmcneill sc->sc_seg = pcihost_segment++;
186dfdf9722Sjmcneill
187ff4e9f70Sskrll mutex_init(&sc->sc_msi_handlers_mutex, MUTEX_DEFAULT, IPL_NONE);
188ff4e9f70Sskrll
18918d2f7fcSjmcneill if (pcihost_config(sc) != 0)
19018d2f7fcSjmcneill return;
19118d2f7fcSjmcneill
19218d2f7fcSjmcneill memset(&pba, 0, sizeof(pba));
19318d2f7fcSjmcneill pba.pba_flags = PCI_FLAGS_MRL_OKAY |
19418d2f7fcSjmcneill PCI_FLAGS_MRM_OKAY |
19518d2f7fcSjmcneill PCI_FLAGS_MWI_OKAY |
1962e99e80aSjmcneill sc->sc_pci_flags;
1973689f53fSjakllsch pba.pba_iot = &sc->sc_io.bst;
1983689f53fSjakllsch pba.pba_memt = &sc->sc_mem.bst;
19918d2f7fcSjmcneill pba.pba_dmat = sc->sc_dmat;
20018d2f7fcSjmcneill #ifdef _PCI_HAVE_DMA64
20118d2f7fcSjmcneill pba.pba_dmat64 = sc->sc_dmat;
20218d2f7fcSjmcneill #endif
20318d2f7fcSjmcneill pba.pba_pc = &sc->sc_pc;
204dfdf9722Sjmcneill pba.pba_bus = sc->sc_bus_min;
20518d2f7fcSjmcneill
206d1083ec1Sthorpej config_found(sc->sc_dev, &pba, pcibusprint,
207c7fb772bSthorpej CFARGS(.devhandle = device_handle(sc->sc_dev)));
20818d2f7fcSjmcneill }
20918d2f7fcSjmcneill
210c3e7c8a7Sjakllsch void
pcihost_init(pci_chipset_tag_t pc,void * priv)21118d2f7fcSjmcneill pcihost_init(pci_chipset_tag_t pc, void *priv)
21218d2f7fcSjmcneill {
21318d2f7fcSjmcneill pc->pc_conf_v = priv;
21418d2f7fcSjmcneill pc->pc_attach_hook = pcihost_attach_hook;
21518d2f7fcSjmcneill pc->pc_bus_maxdevs = pcihost_bus_maxdevs;
21618d2f7fcSjmcneill pc->pc_make_tag = pcihost_make_tag;
21718d2f7fcSjmcneill pc->pc_decompose_tag = pcihost_decompose_tag;
218dfdf9722Sjmcneill pc->pc_get_segment = pcihost_get_segment;
21918d2f7fcSjmcneill pc->pc_conf_read = pcihost_conf_read;
22018d2f7fcSjmcneill pc->pc_conf_write = pcihost_conf_write;
22118d2f7fcSjmcneill pc->pc_conf_hook = pcihost_conf_hook;
22218d2f7fcSjmcneill pc->pc_conf_interrupt = pcihost_conf_interrupt;
22318d2f7fcSjmcneill
22418d2f7fcSjmcneill pc->pc_intr_v = priv;
22518d2f7fcSjmcneill pc->pc_intr_map = pcihost_intr_map;
22618d2f7fcSjmcneill pc->pc_intr_string = pcihost_intr_string;
22718d2f7fcSjmcneill pc->pc_intr_evcnt = pcihost_intr_evcnt;
22818d2f7fcSjmcneill pc->pc_intr_setattr = pcihost_intr_setattr;
22918d2f7fcSjmcneill pc->pc_intr_establish = pcihost_intr_establish;
23018d2f7fcSjmcneill pc->pc_intr_disestablish = pcihost_intr_disestablish;
23118d2f7fcSjmcneill }
23218d2f7fcSjmcneill
23318d2f7fcSjmcneill static int
pcihost_config(struct pcihost_softc * sc)23418d2f7fcSjmcneill pcihost_config(struct pcihost_softc *sc)
23518d2f7fcSjmcneill {
23618d2f7fcSjmcneill const u_int *ranges;
237dfdf9722Sjmcneill u_int probe_only;
238ca8ce3aeSthorpej int error, len, type;
2392e99e80aSjmcneill bool swap;
24018d2f7fcSjmcneill
2413689f53fSjakllsch struct pcih_bus_space * const pibs = &sc->sc_io;
24277a7b674Sjmcneill pibs->bst = *sc->sc_pci_bst;
2433689f53fSjakllsch pibs->bst.bs_cookie = pibs;
2443689f53fSjakllsch pibs->map = pibs->bst.bs_map;
245059f233aSjmcneill pibs->flags = PCI_FLAGS_IO_OKAY;
2463689f53fSjakllsch pibs->bst.bs_map = pcihost_bus_space_map;
2473689f53fSjakllsch
2483689f53fSjakllsch struct pcih_bus_space * const pmbs = &sc->sc_mem;
24977a7b674Sjmcneill pmbs->bst = *sc->sc_pci_bst;
2503689f53fSjakllsch pmbs->bst.bs_cookie = pmbs;
2513689f53fSjakllsch pmbs->map = pmbs->bst.bs_map;
252059f233aSjmcneill pmbs->flags = PCI_FLAGS_MEM_OKAY;
2533689f53fSjakllsch pmbs->bst.bs_map = pcihost_bus_space_map;
2543689f53fSjakllsch
255dfdf9722Sjmcneill /*
256dfdf9722Sjmcneill * If this flag is set, skip configuration of the PCI bus and use existing config.
257dfdf9722Sjmcneill */
258ab8eeec9Sjmcneill const int chosen = OF_finddevice("/chosen");
259ab8eeec9Sjmcneill if (chosen <= 0 || of_getprop_uint32(chosen, "linux,pci-probe-only", &probe_only))
260dfdf9722Sjmcneill probe_only = 0;
261dfdf9722Sjmcneill
2622e99e80aSjmcneill if (sc->sc_pci_ranges != NULL) {
2632e99e80aSjmcneill ranges = sc->sc_pci_ranges;
2642e99e80aSjmcneill len = sc->sc_pci_ranges_cells * 4;
2652e99e80aSjmcneill swap = false;
2662e99e80aSjmcneill } else {
26718d2f7fcSjmcneill ranges = fdtbus_get_prop(sc->sc_phandle, "ranges", &len);
26818d2f7fcSjmcneill if (ranges == NULL) {
26918d2f7fcSjmcneill aprint_error_dev(sc->sc_dev, "missing 'ranges' property\n");
27018d2f7fcSjmcneill return EINVAL;
27118d2f7fcSjmcneill }
2722e99e80aSjmcneill swap = true;
2732e99e80aSjmcneill }
27418d2f7fcSjmcneill
275ca8ce3aeSthorpej struct pciconf_resources *pcires = pciconf_resource_init();
276ca8ce3aeSthorpej
27718d2f7fcSjmcneill /*
27818d2f7fcSjmcneill * Each entry in the ranges table contains:
27918d2f7fcSjmcneill * - bus address (3 cells)
28018d2f7fcSjmcneill * - cpu physical address (2 cells)
28118d2f7fcSjmcneill * - size (2 cells)
28218d2f7fcSjmcneill * Total size for each entry is 28 bytes (7 cells).
28318d2f7fcSjmcneill */
28418d2f7fcSjmcneill while (len >= 28) {
2852e99e80aSjmcneill #define DECODE32(x,o) (swap ? be32dec(&(x)[o]) : (x)[o])
2862e99e80aSjmcneill #define DECODE64(x,o) (swap ? be64dec(&(x)[o]) : (((uint64_t)((x)[(o)+0]) << 32) + (x)[(o)+1]))
2872e99e80aSjmcneill const uint32_t phys_hi = DECODE32(ranges, 0);
288ca8ce3aeSthorpej uint64_t bus_phys = DECODE64(ranges, 1);
2892e99e80aSjmcneill const uint64_t cpu_phys = DECODE64(ranges, 3);
290ca8ce3aeSthorpej uint64_t size = DECODE64(ranges, 5);
2912e99e80aSjmcneill #undef DECODE32
2922e99e80aSjmcneill #undef DECODE64
29318d2f7fcSjmcneill
2943689f53fSjakllsch len -= 28;
2953689f53fSjakllsch ranges += 7;
2963689f53fSjakllsch
2973689f53fSjakllsch const bool is64 = (__SHIFTOUT(phys_hi, PHYS_HI_SPACE) ==
2983689f53fSjakllsch PHYS_HI_SPACE_MEM64) ? true : false;
29918d2f7fcSjmcneill switch (__SHIFTOUT(phys_hi, PHYS_HI_SPACE)) {
30018d2f7fcSjmcneill case PHYS_HI_SPACE_IO:
3013689f53fSjakllsch if (pibs->nranges + 1 >= __arraycount(pibs->ranges)) {
3023689f53fSjakllsch aprint_error_dev(sc->sc_dev, "too many IO ranges\n");
3033689f53fSjakllsch continue;
3043689f53fSjakllsch }
3053689f53fSjakllsch pibs->ranges[pibs->nranges].bpci = bus_phys;
3063689f53fSjakllsch pibs->ranges[pibs->nranges].bbus = cpu_phys;
3073689f53fSjakllsch pibs->ranges[pibs->nranges].size = size;
3083689f53fSjakllsch ++pibs->nranges;
30918d2f7fcSjmcneill aprint_verbose_dev(sc->sc_dev,
3103689f53fSjakllsch "IO: 0x%" PRIx64 "+0x%" PRIx64 "@0x%" PRIx64 "\n",
3113689f53fSjakllsch bus_phys, size, cpu_phys);
312ca8ce3aeSthorpej /*
313ca8ce3aeSthorpej * Reserve a PC-like legacy IO ports range, perhaps
314ca8ce3aeSthorpej * for access to VGA registers.
315ca8ce3aeSthorpej */
316ca8ce3aeSthorpej if (bus_phys == 0 && size >= 0x10000) {
317ca8ce3aeSthorpej bus_phys += 0x1000;
318ca8ce3aeSthorpej size -= 0x1000;
319ca8ce3aeSthorpej }
320ca8ce3aeSthorpej error = pciconf_resource_add(pcires,
321ca8ce3aeSthorpej PCICONF_RESOURCE_IO, bus_phys, size);
322ca8ce3aeSthorpej if (error == 0)
3232e99e80aSjmcneill sc->sc_pci_flags |= PCI_FLAGS_IO_OKAY;
32418d2f7fcSjmcneill break;
3253689f53fSjakllsch case PHYS_HI_SPACE_MEM64:
3263689f53fSjakllsch /* FALLTHROUGH */
32718d2f7fcSjmcneill case PHYS_HI_SPACE_MEM32:
3283689f53fSjakllsch if (pmbs->nranges + 1 >= __arraycount(pmbs->ranges)) {
3293689f53fSjakllsch aprint_error_dev(sc->sc_dev, "too many mem ranges\n");
3303689f53fSjakllsch continue;
3313689f53fSjakllsch }
3323689f53fSjakllsch /* both pmem and mem spaces are in the same tag */
3333689f53fSjakllsch pmbs->ranges[pmbs->nranges].bpci = bus_phys;
3343689f53fSjakllsch pmbs->ranges[pmbs->nranges].bbus = cpu_phys;
3353689f53fSjakllsch pmbs->ranges[pmbs->nranges].size = size;
3363689f53fSjakllsch ++pmbs->nranges;
3373689f53fSjakllsch if ((phys_hi & PHYS_HI_PREFETCH) != 0 ||
3383689f53fSjakllsch __SHIFTOUT(phys_hi, PHYS_HI_SPACE) == PHYS_HI_SPACE_MEM64) {
339ca8ce3aeSthorpej type = PCICONF_RESOURCE_PREFETCHABLE_MEM;
34018d2f7fcSjmcneill aprint_verbose_dev(sc->sc_dev,
3413689f53fSjakllsch "MMIO (%d-bit prefetchable): 0x%" PRIx64 "+0x%" PRIx64 "@0x%" PRIx64 "\n",
3423689f53fSjakllsch is64 ? 64 : 32, bus_phys, size, cpu_phys);
34318d2f7fcSjmcneill } else {
344ca8ce3aeSthorpej type = PCICONF_RESOURCE_MEM;
34518d2f7fcSjmcneill aprint_verbose_dev(sc->sc_dev,
3463689f53fSjakllsch "MMIO (%d-bit non-prefetchable): 0x%" PRIx64 "+0x%" PRIx64 "@0x%" PRIx64 "\n",
3473689f53fSjakllsch is64 ? 64 : 32, bus_phys, size, cpu_phys);
34818d2f7fcSjmcneill }
349ca8ce3aeSthorpej error = pciconf_resource_add(pcires, type, bus_phys,
350ca8ce3aeSthorpej size);
351ca8ce3aeSthorpej if (error == 0)
3522e99e80aSjmcneill sc->sc_pci_flags |= PCI_FLAGS_MEM_OKAY;
35318d2f7fcSjmcneill break;
35418d2f7fcSjmcneill default:
35518d2f7fcSjmcneill break;
35618d2f7fcSjmcneill }
35718d2f7fcSjmcneill }
35818d2f7fcSjmcneill
3597063e10bSjmcneill if (probe_only) {
3607063e10bSjmcneill error = 0;
3617063e10bSjmcneill } else {
362ca8ce3aeSthorpej error = pci_configure_bus(&sc->sc_pc, pcires, sc->sc_bus_min,
363ca8ce3aeSthorpej PCIHOST_CACHELINE_SIZE);
3647063e10bSjmcneill }
365a896fd55Sjmcneill
366ca8ce3aeSthorpej pciconf_resource_fini(pcires);
36718d2f7fcSjmcneill
36818d2f7fcSjmcneill if (error) {
36918d2f7fcSjmcneill aprint_error_dev(sc->sc_dev, "configuration failed: %d\n", error);
37018d2f7fcSjmcneill return error;
37118d2f7fcSjmcneill }
37218d2f7fcSjmcneill
37318d2f7fcSjmcneill return 0;
37418d2f7fcSjmcneill }
37518d2f7fcSjmcneill
37618d2f7fcSjmcneill static void
pcihost_attach_hook(device_t parent,device_t self,struct pcibus_attach_args * pba)37718d2f7fcSjmcneill pcihost_attach_hook(device_t parent, device_t self,
37818d2f7fcSjmcneill struct pcibus_attach_args *pba)
37918d2f7fcSjmcneill {
38018d2f7fcSjmcneill }
38118d2f7fcSjmcneill
38218d2f7fcSjmcneill static int
pcihost_bus_maxdevs(void * v,int busno)38318d2f7fcSjmcneill pcihost_bus_maxdevs(void *v, int busno)
38418d2f7fcSjmcneill {
38518d2f7fcSjmcneill return 32;
38618d2f7fcSjmcneill }
38718d2f7fcSjmcneill
38818d2f7fcSjmcneill static pcitag_t
pcihost_make_tag(void * v,int b,int d,int f)38918d2f7fcSjmcneill pcihost_make_tag(void *v, int b, int d, int f)
39018d2f7fcSjmcneill {
39118d2f7fcSjmcneill return (b << 16) | (d << 11) | (f << 8);
39218d2f7fcSjmcneill }
39318d2f7fcSjmcneill
39418d2f7fcSjmcneill static void
pcihost_decompose_tag(void * v,pcitag_t tag,int * bp,int * dp,int * fp)39518d2f7fcSjmcneill pcihost_decompose_tag(void *v, pcitag_t tag, int *bp, int *dp, int *fp)
39618d2f7fcSjmcneill {
39718d2f7fcSjmcneill if (bp)
39818d2f7fcSjmcneill *bp = (tag >> 16) & 0xff;
39918d2f7fcSjmcneill if (dp)
40018d2f7fcSjmcneill *dp = (tag >> 11) & 0x1f;
40118d2f7fcSjmcneill if (fp)
40218d2f7fcSjmcneill *fp = (tag >> 8) & 0x7;
40318d2f7fcSjmcneill }
40418d2f7fcSjmcneill
405dfdf9722Sjmcneill static u_int
pcihost_get_segment(void * v)406dfdf9722Sjmcneill pcihost_get_segment(void *v)
407dfdf9722Sjmcneill {
408dfdf9722Sjmcneill struct pcihost_softc *sc = v;
409dfdf9722Sjmcneill
410dfdf9722Sjmcneill return sc->sc_seg;
411dfdf9722Sjmcneill }
412dfdf9722Sjmcneill
41318d2f7fcSjmcneill static pcireg_t
pcihost_conf_read(void * v,pcitag_t tag,int offset)41418d2f7fcSjmcneill pcihost_conf_read(void *v, pcitag_t tag, int offset)
41518d2f7fcSjmcneill {
41618d2f7fcSjmcneill struct pcihost_softc *sc = v;
41718d2f7fcSjmcneill int b, d, f;
41818d2f7fcSjmcneill u_int reg;
41918d2f7fcSjmcneill
42018d2f7fcSjmcneill pcihost_decompose_tag(v, tag, &b, &d, &f);
42118d2f7fcSjmcneill
42218d2f7fcSjmcneill if (b < sc->sc_bus_min || b > sc->sc_bus_max)
42318d2f7fcSjmcneill return (pcireg_t) -1;
42418d2f7fcSjmcneill
42518d2f7fcSjmcneill if (sc->sc_type == PCIHOST_CAM) {
42618d2f7fcSjmcneill if (offset & ~0xff)
42718d2f7fcSjmcneill return (pcireg_t) -1;
42818d2f7fcSjmcneill reg = (b << 16) | (d << 11) | (f << 8) | offset;
42918d2f7fcSjmcneill } else if (sc->sc_type == PCIHOST_ECAM) {
43018d2f7fcSjmcneill if (offset & ~0xfff)
43118d2f7fcSjmcneill return (pcireg_t) -1;
43218d2f7fcSjmcneill reg = (b << 20) | (d << 15) | (f << 12) | offset;
43318d2f7fcSjmcneill } else {
43418d2f7fcSjmcneill return (pcireg_t) -1;
43518d2f7fcSjmcneill }
43618d2f7fcSjmcneill
43718d2f7fcSjmcneill return bus_space_read_4(sc->sc_bst, sc->sc_bsh, reg);
43818d2f7fcSjmcneill }
43918d2f7fcSjmcneill
44018d2f7fcSjmcneill static void
pcihost_conf_write(void * v,pcitag_t tag,int offset,pcireg_t val)44118d2f7fcSjmcneill pcihost_conf_write(void *v, pcitag_t tag, int offset, pcireg_t val)
44218d2f7fcSjmcneill {
44318d2f7fcSjmcneill struct pcihost_softc *sc = v;
44418d2f7fcSjmcneill int b, d, f;
44518d2f7fcSjmcneill u_int reg;
44618d2f7fcSjmcneill
44718d2f7fcSjmcneill pcihost_decompose_tag(v, tag, &b, &d, &f);
44818d2f7fcSjmcneill
44918d2f7fcSjmcneill if (b < sc->sc_bus_min || b > sc->sc_bus_max)
45018d2f7fcSjmcneill return;
45118d2f7fcSjmcneill
45218d2f7fcSjmcneill if (sc->sc_type == PCIHOST_CAM) {
45318d2f7fcSjmcneill if (offset & ~0xff)
45418d2f7fcSjmcneill return;
45518d2f7fcSjmcneill reg = (b << 16) | (d << 11) | (f << 8) | offset;
45618d2f7fcSjmcneill } else if (sc->sc_type == PCIHOST_ECAM) {
45718d2f7fcSjmcneill if (offset & ~0xfff)
45818d2f7fcSjmcneill return;
45918d2f7fcSjmcneill reg = (b << 20) | (d << 15) | (f << 12) | offset;
46018d2f7fcSjmcneill } else {
46118d2f7fcSjmcneill return;
46218d2f7fcSjmcneill }
46318d2f7fcSjmcneill
46418d2f7fcSjmcneill bus_space_write_4(sc->sc_bst, sc->sc_bsh, reg, val);
46518d2f7fcSjmcneill }
46618d2f7fcSjmcneill
46718d2f7fcSjmcneill static int
pcihost_conf_hook(void * v,int b,int d,int f,pcireg_t id)46818d2f7fcSjmcneill pcihost_conf_hook(void *v, int b, int d, int f, pcireg_t id)
46918d2f7fcSjmcneill {
47018d2f7fcSjmcneill return PCI_CONF_DEFAULT;
47118d2f7fcSjmcneill }
47218d2f7fcSjmcneill
47318d2f7fcSjmcneill static void
pcihost_conf_interrupt(void * v,int bus,int dev,int ipin,int swiz,int * ilinep)47418d2f7fcSjmcneill pcihost_conf_interrupt(void *v, int bus, int dev, int ipin, int swiz, int *ilinep)
47518d2f7fcSjmcneill {
47618d2f7fcSjmcneill }
47718d2f7fcSjmcneill
47818d2f7fcSjmcneill static int
pcihost_intr_map(const struct pci_attach_args * pa,pci_intr_handle_t * ih)47918d2f7fcSjmcneill pcihost_intr_map(const struct pci_attach_args *pa, pci_intr_handle_t *ih)
48018d2f7fcSjmcneill {
481af559932Sjmcneill struct pcihost_softc *sc = pa->pa_pc->pc_intr_v;
482af559932Sjmcneill u_int addr_cells, interrupt_cells;
483af559932Sjmcneill const u_int *imap, *imask;
484af559932Sjmcneill int imaplen, imasklen;
485af559932Sjmcneill u_int match[4];
486af559932Sjmcneill int index;
487af559932Sjmcneill
48818d2f7fcSjmcneill if (pa->pa_intrpin == 0)
48918d2f7fcSjmcneill return EINVAL;
490af559932Sjmcneill
491af559932Sjmcneill imap = fdtbus_get_prop(sc->sc_phandle, "interrupt-map", &imaplen);
492af559932Sjmcneill imask = fdtbus_get_prop(sc->sc_phandle, "interrupt-map-mask", &imasklen);
493af559932Sjmcneill if (imap == NULL || imask == NULL || imasklen != 16)
494af559932Sjmcneill return EINVAL;
495af559932Sjmcneill
496af559932Sjmcneill /* Convert attach args to specifier */
497af559932Sjmcneill match[0] = htobe32(
498af559932Sjmcneill __SHIFTIN(pa->pa_bus, PHYS_HI_BUS) |
499af559932Sjmcneill __SHIFTIN(pa->pa_device, PHYS_HI_DEVICE) |
500af559932Sjmcneill __SHIFTIN(pa->pa_function, PHYS_HI_FUNCTION)
501af559932Sjmcneill ) & imask[0];
502af559932Sjmcneill match[1] = htobe32(0) & imask[1];
503af559932Sjmcneill match[2] = htobe32(0) & imask[2];
504af559932Sjmcneill match[3] = htobe32(pa->pa_intrpin) & imask[3];
505af559932Sjmcneill
506af559932Sjmcneill index = 0;
507af559932Sjmcneill while (imaplen >= 20) {
508af559932Sjmcneill const int map_ihandle = fdtbus_get_phandle_from_native(be32toh(imap[4]));
509af559932Sjmcneill if (of_getprop_uint32(map_ihandle, "#address-cells", &addr_cells))
510af559932Sjmcneill addr_cells = 2;
511af559932Sjmcneill if (of_getprop_uint32(map_ihandle, "#interrupt-cells", &interrupt_cells))
512af559932Sjmcneill interrupt_cells = 0;
513af559932Sjmcneill if (imaplen < (addr_cells + interrupt_cells) * 4)
514af559932Sjmcneill return ENXIO;
515af559932Sjmcneill
516af559932Sjmcneill if ((imap[0] & imask[0]) == match[0] &&
517af559932Sjmcneill (imap[1] & imask[1]) == match[1] &&
518af559932Sjmcneill (imap[2] & imask[2]) == match[2] &&
519af559932Sjmcneill (imap[3] & imask[3]) == match[3]) {
520af559932Sjmcneill *ih = index;
52118d2f7fcSjmcneill return 0;
52218d2f7fcSjmcneill }
52318d2f7fcSjmcneill
524af559932Sjmcneill imap += (5 + addr_cells + interrupt_cells);
525af559932Sjmcneill imaplen -= (5 + addr_cells + interrupt_cells) * 4;
526af559932Sjmcneill index++;
527af559932Sjmcneill }
528af559932Sjmcneill
529af559932Sjmcneill return EINVAL;
530af559932Sjmcneill }
531af559932Sjmcneill
53218d2f7fcSjmcneill static const u_int *
pcihost_find_intr(struct pcihost_softc * sc,pci_intr_handle_t ih,int * pihandle)533af559932Sjmcneill pcihost_find_intr(struct pcihost_softc *sc, pci_intr_handle_t ih, int *pihandle)
53418d2f7fcSjmcneill {
53518d2f7fcSjmcneill u_int addr_cells, interrupt_cells;
536af559932Sjmcneill int imaplen, index;
53718d2f7fcSjmcneill const u_int *imap;
53818d2f7fcSjmcneill
53918d2f7fcSjmcneill imap = fdtbus_get_prop(sc->sc_phandle, "interrupt-map", &imaplen);
540af559932Sjmcneill KASSERT(imap != NULL);
54118d2f7fcSjmcneill
542af559932Sjmcneill index = 0;
54318d2f7fcSjmcneill while (imaplen >= 20) {
54418d2f7fcSjmcneill const int map_ihandle = fdtbus_get_phandle_from_native(be32toh(imap[4]));
54518d2f7fcSjmcneill if (of_getprop_uint32(map_ihandle, "#address-cells", &addr_cells))
54618d2f7fcSjmcneill addr_cells = 2;
54718d2f7fcSjmcneill if (of_getprop_uint32(map_ihandle, "#interrupt-cells", &interrupt_cells))
54818d2f7fcSjmcneill interrupt_cells = 0;
54918d2f7fcSjmcneill if (imaplen < (addr_cells + interrupt_cells) * 4)
55018d2f7fcSjmcneill return NULL;
55118d2f7fcSjmcneill
552af559932Sjmcneill if (index == ih) {
55318d2f7fcSjmcneill *pihandle = map_ihandle;
55418d2f7fcSjmcneill return imap + 5 + addr_cells;
55518d2f7fcSjmcneill }
55618d2f7fcSjmcneill
557af559932Sjmcneill imap += (5 + addr_cells + interrupt_cells);
558af559932Sjmcneill imaplen -= (5 + addr_cells + interrupt_cells) * 4;
559af559932Sjmcneill index++;
56018d2f7fcSjmcneill }
56118d2f7fcSjmcneill
56218d2f7fcSjmcneill return NULL;
56318d2f7fcSjmcneill }
56418d2f7fcSjmcneill
56518d2f7fcSjmcneill static const char *
pcihost_intr_string(void * v,pci_intr_handle_t ih,char * buf,size_t len)56618d2f7fcSjmcneill pcihost_intr_string(void *v, pci_intr_handle_t ih, char *buf, size_t len)
56718d2f7fcSjmcneill {
568dfdf9722Sjmcneill const int irq = __SHIFTOUT(ih, ARM_PCI_INTR_IRQ);
569dfdf9722Sjmcneill const int vec = __SHIFTOUT(ih, ARM_PCI_INTR_MSI_VEC);
57018d2f7fcSjmcneill struct pcihost_softc *sc = v;
57118d2f7fcSjmcneill const u_int *specifier;
57218d2f7fcSjmcneill int ihandle;
57318d2f7fcSjmcneill
574dfdf9722Sjmcneill if (ih & ARM_PCI_INTR_MSIX) {
575dfdf9722Sjmcneill snprintf(buf, len, "irq %d (MSI-X vec %d)", irq, vec);
576dfdf9722Sjmcneill } else if (ih & ARM_PCI_INTR_MSI) {
577dfdf9722Sjmcneill snprintf(buf, len, "irq %d (MSI vec %d)", irq, vec);
578dfdf9722Sjmcneill } else {
5795966f2f4Sjmcneill specifier = pcihost_find_intr(sc, ih & ARM_PCI_INTR_IRQ, &ihandle);
58018d2f7fcSjmcneill if (specifier == NULL)
58118d2f7fcSjmcneill return NULL;
58218d2f7fcSjmcneill
58318d2f7fcSjmcneill if (!fdtbus_intr_str_raw(ihandle, specifier, buf, len))
58418d2f7fcSjmcneill return NULL;
585dfdf9722Sjmcneill }
58618d2f7fcSjmcneill
58718d2f7fcSjmcneill return buf;
58818d2f7fcSjmcneill }
58918d2f7fcSjmcneill
59018d2f7fcSjmcneill const struct evcnt *
pcihost_intr_evcnt(void * v,pci_intr_handle_t ih)59118d2f7fcSjmcneill pcihost_intr_evcnt(void *v, pci_intr_handle_t ih)
59218d2f7fcSjmcneill {
59318d2f7fcSjmcneill return NULL;
59418d2f7fcSjmcneill }
59518d2f7fcSjmcneill
59618d2f7fcSjmcneill static int
pcihost_intr_setattr(void * v,pci_intr_handle_t * ih,int attr,uint64_t data)59718d2f7fcSjmcneill pcihost_intr_setattr(void *v, pci_intr_handle_t *ih, int attr, uint64_t data)
59818d2f7fcSjmcneill {
59918d2f7fcSjmcneill switch (attr) {
60018d2f7fcSjmcneill case PCI_INTR_MPSAFE:
60118d2f7fcSjmcneill if (data)
6025966f2f4Sjmcneill *ih |= ARM_PCI_INTR_MPSAFE;
60318d2f7fcSjmcneill else
6045966f2f4Sjmcneill *ih &= ~ARM_PCI_INTR_MPSAFE;
60518d2f7fcSjmcneill return 0;
60618d2f7fcSjmcneill default:
60718d2f7fcSjmcneill return ENODEV;
60818d2f7fcSjmcneill }
60918d2f7fcSjmcneill }
61018d2f7fcSjmcneill
61118d2f7fcSjmcneill static void *
pcihost_intr_establish(void * v,pci_intr_handle_t pih,int ipl,int (* callback)(void *),void * arg,const char * xname)612ff4e9f70Sskrll pcihost_intr_establish(void *v, pci_intr_handle_t pih, int ipl,
613cce19cc2Sjmcneill int (*callback)(void *), void *arg, const char *xname)
61418d2f7fcSjmcneill {
61518d2f7fcSjmcneill struct pcihost_softc *sc = v;
616ff4e9f70Sskrll const int flags = (pih & ARM_PCI_INTR_MPSAFE) ? FDT_INTR_MPSAFE : 0;
61718d2f7fcSjmcneill const u_int *specifier;
61818d2f7fcSjmcneill int ihandle;
61918d2f7fcSjmcneill
620ff4e9f70Sskrll if ((pih & (ARM_PCI_INTR_MSI | ARM_PCI_INTR_MSIX)) != 0) {
621ff4e9f70Sskrll void *ih = arm_pci_msi_intr_establish(&sc->sc_pc, pih, ipl,
622ff4e9f70Sskrll callback, arg, xname);
623dfdf9722Sjmcneill
624ff4e9f70Sskrll if (ih) {
625ff4e9f70Sskrll struct pcihost_msi_handler * const pmh =
626ff4e9f70Sskrll kmem_alloc(sizeof(*pmh), KM_SLEEP);
627ff4e9f70Sskrll pmh->pmh_ih = ih;
628ff4e9f70Sskrll mutex_enter(&sc->sc_msi_handlers_mutex);
629ff4e9f70Sskrll LIST_INSERT_HEAD(&sc->sc_msi_handlers, pmh, pmh_next);
630ff4e9f70Sskrll mutex_exit(&sc->sc_msi_handlers_mutex);
631ff4e9f70Sskrll }
632ff4e9f70Sskrll return ih;
633ff4e9f70Sskrll }
634ff4e9f70Sskrll
635ff4e9f70Sskrll specifier = pcihost_find_intr(sc, pih & ARM_PCI_INTR_IRQ, &ihandle);
63618d2f7fcSjmcneill if (specifier == NULL)
63718d2f7fcSjmcneill return NULL;
63818d2f7fcSjmcneill
639b20f028bSskrll return fdtbus_intr_establish_raw(ihandle, specifier, ipl, flags,
640d7f617c3Sryo callback, arg, xname);
64118d2f7fcSjmcneill }
64218d2f7fcSjmcneill
64318d2f7fcSjmcneill static void
pcihost_intr_disestablish(void * v,void * vih)64418d2f7fcSjmcneill pcihost_intr_disestablish(void *v, void *vih)
64518d2f7fcSjmcneill {
64618d2f7fcSjmcneill struct pcihost_softc *sc = v;
64718d2f7fcSjmcneill
648ff4e9f70Sskrll mutex_enter(&sc->sc_msi_handlers_mutex);
649ff4e9f70Sskrll struct pcihost_msi_handler *pmh;
650ff4e9f70Sskrll LIST_FOREACH(pmh, &sc->sc_msi_handlers, pmh_next) {
651ff4e9f70Sskrll if (pmh->pmh_ih == vih) {
652ff4e9f70Sskrll LIST_REMOVE(pmh, pmh_next);
653ff4e9f70Sskrll mutex_exit(&sc->sc_msi_handlers_mutex);
654ff4e9f70Sskrll kmem_free(pmh, sizeof(*pmh));
655ff4e9f70Sskrll return;
656ff4e9f70Sskrll }
657ff4e9f70Sskrll }
658ff4e9f70Sskrll mutex_exit(&sc->sc_msi_handlers_mutex);
659ff4e9f70Sskrll
660b20f028bSskrll fdtbus_intr_disestablish(sc->sc_phandle, vih);
66118d2f7fcSjmcneill }
6623689f53fSjakllsch
6633689f53fSjakllsch static int
pcihost_bus_space_map(void * t,bus_addr_t bpa,bus_size_t size,int flag,bus_space_handle_t * bshp)6643689f53fSjakllsch pcihost_bus_space_map(void *t, bus_addr_t bpa, bus_size_t size, int flag,
6653689f53fSjakllsch bus_space_handle_t *bshp)
6663689f53fSjakllsch {
6673689f53fSjakllsch struct pcih_bus_space * const pbs = t;
6683689f53fSjakllsch
669059f233aSjmcneill if ((pbs->flags & PCI_FLAGS_IO_OKAY) != 0) {
670059f233aSjmcneill /* Force strongly ordered mapping for all I/O space */
6715158b98cSjmcneill flag = BUS_SPACE_MAP_NONPOSTED;
672059f233aSjmcneill }
673059f233aSjmcneill
6743689f53fSjakllsch for (size_t i = 0; i < pbs->nranges; i++) {
6753689f53fSjakllsch const bus_addr_t rmin = pbs->ranges[i].bpci;
6763689f53fSjakllsch const bus_addr_t rmax = pbs->ranges[i].bpci - 1 + pbs->ranges[i].size;
6773689f53fSjakllsch if ((bpa >= rmin) && ((bpa - 1 + size) <= rmax)) {
6783689f53fSjakllsch return pbs->map(t, bpa - pbs->ranges[i].bpci + pbs->ranges[i].bbus, size, flag, bshp);
6793689f53fSjakllsch }
6803689f53fSjakllsch }
6813689f53fSjakllsch
6823689f53fSjakllsch return ERANGE;
6833689f53fSjakllsch }
684