1*5158b98cSjmcneill /* $NetBSD: tegra_pcie.c,v 1.40 2022/10/15 11:07:39 jmcneill Exp $ */
2e1b20f28Sjmcneill
3e1b20f28Sjmcneill /*-
4e1b20f28Sjmcneill * Copyright (c) 2015 Jared D. McNeill <jmcneill@invisible.ca>
5e1b20f28Sjmcneill * All rights reserved.
6e1b20f28Sjmcneill *
7e1b20f28Sjmcneill * Redistribution and use in source and binary forms, with or without
8e1b20f28Sjmcneill * modification, are permitted provided that the following conditions
9e1b20f28Sjmcneill * are met:
10e1b20f28Sjmcneill * 1. Redistributions of source code must retain the above copyright
11e1b20f28Sjmcneill * notice, this list of conditions and the following disclaimer.
12e1b20f28Sjmcneill * 2. Redistributions in binary form must reproduce the above copyright
13e1b20f28Sjmcneill * notice, this list of conditions and the following disclaimer in the
14e1b20f28Sjmcneill * documentation and/or other materials provided with the distribution.
15e1b20f28Sjmcneill *
16e1b20f28Sjmcneill * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
17e1b20f28Sjmcneill * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
18e1b20f28Sjmcneill * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
19e1b20f28Sjmcneill * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
20e1b20f28Sjmcneill * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
21e1b20f28Sjmcneill * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
22e1b20f28Sjmcneill * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
23e1b20f28Sjmcneill * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
24e1b20f28Sjmcneill * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25e1b20f28Sjmcneill * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26e1b20f28Sjmcneill * SUCH DAMAGE.
27e1b20f28Sjmcneill */
28e1b20f28Sjmcneill
29e1b20f28Sjmcneill #include <sys/cdefs.h>
30*5158b98cSjmcneill __KERNEL_RCSID(0, "$NetBSD: tegra_pcie.c,v 1.40 2022/10/15 11:07:39 jmcneill Exp $");
31e1b20f28Sjmcneill
32e1b20f28Sjmcneill #include <sys/param.h>
33f8a0ef89Sskrll
34e1b20f28Sjmcneill #include <sys/bus.h>
35e1b20f28Sjmcneill #include <sys/device.h>
36f8a0ef89Sskrll #include <sys/intr.h>
37e1b20f28Sjmcneill #include <sys/kmem.h>
38f8a0ef89Sskrll #include <sys/kernel.h>
3958bc9390Sskrll #include <sys/lwp.h>
40f8a0ef89Sskrll #include <sys/mutex.h>
41f8a0ef89Sskrll #include <sys/queue.h>
42f8a0ef89Sskrll #include <sys/systm.h>
43e1b20f28Sjmcneill
44fe33aa27Sryo #include <machine/cpu.h>
45fe33aa27Sryo
46e1b20f28Sjmcneill #include <arm/cpufunc.h>
47e1b20f28Sjmcneill
48e1b20f28Sjmcneill #include <dev/pci/pcireg.h>
49e1b20f28Sjmcneill #include <dev/pci/pcivar.h>
50e1b20f28Sjmcneill #include <dev/pci/pciconf.h>
51e1b20f28Sjmcneill
52e1b20f28Sjmcneill #include <arm/nvidia/tegra_reg.h>
53e1b20f28Sjmcneill #include <arm/nvidia/tegra_pciereg.h>
54479a90aaSjmcneill #include <arm/nvidia/tegra_pmcreg.h>
55e1b20f28Sjmcneill #include <arm/nvidia/tegra_var.h>
56e1b20f28Sjmcneill
57d59db8d0Sjmcneill #include <dev/fdt/fdtvar.h>
58d59db8d0Sjmcneill
59c983b58bSjmcneill /* Interrupt handle flags */
60c983b58bSjmcneill #define IH_MPSAFE 0x80000000
61c983b58bSjmcneill
62e1b20f28Sjmcneill static int tegra_pcie_match(device_t, cfdata_t, void *);
63e1b20f28Sjmcneill static void tegra_pcie_attach(device_t, device_t, void *);
64e1b20f28Sjmcneill
651f4f07e0Sjakllsch #define TEGRA_PCIE_NBUS 256
661f4f07e0Sjakllsch #define TEGRA_PCIE_ECFB (1<<(12 - 8)) /* extended conf frags per bus */
671f4f07e0Sjakllsch
683f7c862bSthorpej enum tegra_pcie_type {
693f7c862bSthorpej TEGRA_PCIE_124 = 0,
703f7c862bSthorpej TEGRA_PCIE_210 = 1,
713f7c862bSthorpej };
723f7c862bSthorpej
73e1b20f28Sjmcneill struct tegra_pcie_ih {
74e1b20f28Sjmcneill int (*ih_callback)(void *);
75e1b20f28Sjmcneill void *ih_arg;
76e1b20f28Sjmcneill int ih_ipl;
77fa7f7f0eSjmcneill int ih_mpsafe;
78e1b20f28Sjmcneill TAILQ_ENTRY(tegra_pcie_ih) ih_entry;
79e1b20f28Sjmcneill };
80e1b20f28Sjmcneill
81e1b20f28Sjmcneill struct tegra_pcie_softc {
82e1b20f28Sjmcneill device_t sc_dev;
83e1b20f28Sjmcneill bus_dma_tag_t sc_dmat;
84e1b20f28Sjmcneill bus_space_tag_t sc_bst;
85e1b20f28Sjmcneill bus_space_handle_t sc_bsh_afi;
86479a90aaSjmcneill bus_space_handle_t sc_bsh_pads;
87abb2550cSjakllsch bus_space_handle_t sc_bsh_rpconf;
88d59db8d0Sjmcneill int sc_phandle;
893f7c862bSthorpej enum tegra_pcie_type sc_type;
90e1b20f28Sjmcneill
91e1b20f28Sjmcneill struct arm32_pci_chipset sc_pc;
92e1b20f28Sjmcneill
93e1b20f28Sjmcneill void *sc_ih;
94e1b20f28Sjmcneill
95e1b20f28Sjmcneill kmutex_t sc_lock;
96e1b20f28Sjmcneill
97e1b20f28Sjmcneill TAILQ_HEAD(, tegra_pcie_ih) sc_intrs;
98e1b20f28Sjmcneill u_int sc_intrgen;
991f4f07e0Sjakllsch
1001f4f07e0Sjakllsch bus_space_handle_t sc_bsh_extc[TEGRA_PCIE_NBUS-1][TEGRA_PCIE_ECFB];
101e1b20f28Sjmcneill };
102e1b20f28Sjmcneill
103e1b20f28Sjmcneill static int tegra_pcie_intr(void *);
104e1b20f28Sjmcneill static void tegra_pcie_init(pci_chipset_tag_t, void *);
105e1b20f28Sjmcneill static void tegra_pcie_enable(struct tegra_pcie_softc *);
106479a90aaSjmcneill static void tegra_pcie_enable_ports(struct tegra_pcie_softc *);
10742f8fa7cSjmcneill static void tegra_pcie_enable_clocks(struct tegra_pcie_softc *);
108d7a3612eSjakllsch static void tegra_pcie_setup(struct tegra_pcie_softc * const);
1091f4f07e0Sjakllsch static void tegra_pcie_conf_frag_map(struct tegra_pcie_softc * const,
1101f4f07e0Sjakllsch uint, uint);
1111f4f07e0Sjakllsch static void tegra_pcie_conf_map_bus(struct tegra_pcie_softc * const, uint);
1121f4f07e0Sjakllsch static void tegra_pcie_conf_map_buses(struct tegra_pcie_softc * const);
113e1b20f28Sjmcneill
114e1b20f28Sjmcneill static void tegra_pcie_attach_hook(device_t, device_t,
115e1b20f28Sjmcneill struct pcibus_attach_args *);
116e1b20f28Sjmcneill static int tegra_pcie_bus_maxdevs(void *, int);
117e1b20f28Sjmcneill static pcitag_t tegra_pcie_make_tag(void *, int, int, int);
118e1b20f28Sjmcneill static void tegra_pcie_decompose_tag(void *, pcitag_t, int *, int *, int *);
119e1b20f28Sjmcneill static pcireg_t tegra_pcie_conf_read(void *, pcitag_t, int);
120e1b20f28Sjmcneill static void tegra_pcie_conf_write(void *, pcitag_t, int, pcireg_t);
121e1b20f28Sjmcneill static int tegra_pcie_conf_hook(void *, int, int, int, pcireg_t);
122e1b20f28Sjmcneill static void tegra_pcie_conf_interrupt(void *, int, int, int, int, int *);
123e1b20f28Sjmcneill
124e1b20f28Sjmcneill static int tegra_pcie_intr_map(const struct pci_attach_args *,
125e1b20f28Sjmcneill pci_intr_handle_t *);
126e1b20f28Sjmcneill static const char *tegra_pcie_intr_string(void *, pci_intr_handle_t,
127e1b20f28Sjmcneill char *, size_t);
128e1b20f28Sjmcneill const struct evcnt *tegra_pcie_intr_evcnt(void *, pci_intr_handle_t);
129fa7f7f0eSjmcneill static int tegra_pcie_intr_setattr(void *, pci_intr_handle_t *, int,
130fa7f7f0eSjmcneill uint64_t);
131e1b20f28Sjmcneill static void * tegra_pcie_intr_establish(void *, pci_intr_handle_t,
132cce19cc2Sjmcneill int, int (*)(void *), void *,
133cce19cc2Sjmcneill const char *);
134e1b20f28Sjmcneill static void tegra_pcie_intr_disestablish(void *, void *);
135e1b20f28Sjmcneill
136e1b20f28Sjmcneill CFATTACH_DECL_NEW(tegra_pcie, sizeof(struct tegra_pcie_softc),
137e1b20f28Sjmcneill tegra_pcie_match, tegra_pcie_attach, NULL, NULL);
138e1b20f28Sjmcneill
1393f7c862bSthorpej static const struct device_compatible_entry compat_data[] = {
1403f7c862bSthorpej { .compat = "nvidia,tegra210-pcie", .value = TEGRA_PCIE_210 },
1413f7c862bSthorpej { .compat = "nvidia,tegra124-pcie", .value = TEGRA_PCIE_124 },
142ec189949Sthorpej DEVICE_COMPAT_EOL
1433f7c862bSthorpej };
1443f7c862bSthorpej
145e1b20f28Sjmcneill static int
tegra_pcie_match(device_t parent,cfdata_t cf,void * aux)146e1b20f28Sjmcneill tegra_pcie_match(device_t parent, cfdata_t cf, void *aux)
147e1b20f28Sjmcneill {
148d59db8d0Sjmcneill struct fdt_attach_args * const faa = aux;
149d59db8d0Sjmcneill
1506e54367aSthorpej return of_compatible_match(faa->faa_phandle, compat_data);
151e1b20f28Sjmcneill }
152e1b20f28Sjmcneill
153e1b20f28Sjmcneill static void
tegra_pcie_attach(device_t parent,device_t self,void * aux)154e1b20f28Sjmcneill tegra_pcie_attach(device_t parent, device_t self, void *aux)
155e1b20f28Sjmcneill {
156e1b20f28Sjmcneill struct tegra_pcie_softc * const sc = device_private(self);
157d59db8d0Sjmcneill struct fdt_attach_args * const faa = aux;
1583f7c862bSthorpej const struct device_compatible_entry *dce;
159ca8ce3aeSthorpej struct pciconf_resources *pcires;
160e1b20f28Sjmcneill struct pcibus_attach_args pba;
161479a90aaSjmcneill bus_addr_t afi_addr, cs_addr, pads_addr;
162479a90aaSjmcneill bus_size_t afi_size, cs_size, pads_size;
163d59db8d0Sjmcneill char intrstr[128];
164e1b20f28Sjmcneill int error;
165e1b20f28Sjmcneill
166479a90aaSjmcneill if (fdtbus_get_reg_byname(faa->faa_phandle, "afi", &afi_addr, &afi_size) != 0) {
167d59db8d0Sjmcneill aprint_error(": couldn't get afi registers\n");
168d59db8d0Sjmcneill return;
169d59db8d0Sjmcneill }
170479a90aaSjmcneill if (fdtbus_get_reg_byname(faa->faa_phandle, "pads", &pads_addr, &pads_size) != 0) {
171479a90aaSjmcneill aprint_error(": couldn't get pads registers\n");
172479a90aaSjmcneill return;
173479a90aaSjmcneill }
174d59db8d0Sjmcneill #if notyet
175d59db8d0Sjmcneill if (fdtbus_get_reg(faa->faa_phandle, 2, &cs_addr, &cs_size) != 0) {
176d59db8d0Sjmcneill aprint_error(": couldn't get cs registers\n");
177d59db8d0Sjmcneill return;
178d59db8d0Sjmcneill }
179d59db8d0Sjmcneill #else
180d59db8d0Sjmcneill cs_addr = TEGRA_PCIE_RPCONF_BASE;
181d59db8d0Sjmcneill cs_size = TEGRA_PCIE_RPCONF_SIZE;
182d59db8d0Sjmcneill #endif
183d59db8d0Sjmcneill
184e1b20f28Sjmcneill sc->sc_dev = self;
185d59db8d0Sjmcneill sc->sc_dmat = faa->faa_dmat;
186d59db8d0Sjmcneill sc->sc_bst = faa->faa_bst;
187d59db8d0Sjmcneill sc->sc_phandle = faa->faa_phandle;
188d59db8d0Sjmcneill error = bus_space_map(sc->sc_bst, afi_addr, afi_size, 0,
189d59db8d0Sjmcneill &sc->sc_bsh_afi);
190d59db8d0Sjmcneill if (error) {
191d59db8d0Sjmcneill aprint_error(": couldn't map afi registers: %d\n", error);
192d59db8d0Sjmcneill return;
193d59db8d0Sjmcneill }
194479a90aaSjmcneill error = bus_space_map(sc->sc_bst, pads_addr, pads_size, 0,
195479a90aaSjmcneill &sc->sc_bsh_pads);
196479a90aaSjmcneill if (error) {
1973b9efca7Sjakllsch aprint_error(": couldn't map pads registers: %d\n", error);
198479a90aaSjmcneill return;
199479a90aaSjmcneill }
200059f233aSjmcneill error = bus_space_map(sc->sc_bst, cs_addr, cs_size,
201*5158b98cSjmcneill BUS_SPACE_MAP_NONPOSTED, &sc->sc_bsh_rpconf);
202d59db8d0Sjmcneill if (error) {
203d59db8d0Sjmcneill aprint_error(": couldn't map cs registers: %d\n", error);
204d59db8d0Sjmcneill return;
205d59db8d0Sjmcneill }
206d59db8d0Sjmcneill
2076e54367aSthorpej dce = of_compatible_lookup(faa->faa_phandle, compat_data);
2083f7c862bSthorpej KASSERT(dce != NULL);
2093f7c862bSthorpej sc->sc_type = dce->value;
2103f7c862bSthorpej
2111f4f07e0Sjakllsch tegra_pcie_conf_map_buses(sc);
212e1b20f28Sjmcneill
213e1b20f28Sjmcneill TAILQ_INIT(&sc->sc_intrs);
214e1b20f28Sjmcneill mutex_init(&sc->sc_lock, MUTEX_DEFAULT, IPL_VM);
215e1b20f28Sjmcneill
216e1b20f28Sjmcneill aprint_naive("\n");
217e1b20f28Sjmcneill aprint_normal(": PCIE\n");
218e1b20f28Sjmcneill
219479a90aaSjmcneill tegra_pmc_power(PMC_PARTID_PCX, true);
220479a90aaSjmcneill tegra_pmc_remove_clamping(PMC_PARTID_PCX);
221479a90aaSjmcneill
22242f8fa7cSjmcneill tegra_pcie_enable_clocks(sc);
22342f8fa7cSjmcneill
224d59db8d0Sjmcneill if (!fdtbus_intr_str(faa->faa_phandle, 0, intrstr, sizeof(intrstr))) {
225d59db8d0Sjmcneill aprint_error_dev(self, "failed to decode interrupt\n");
226e1b20f28Sjmcneill return;
227e1b20f28Sjmcneill }
228d59db8d0Sjmcneill
2293f513eddSjmcneill sc->sc_ih = fdtbus_intr_establish_xname(faa->faa_phandle, 0, IPL_VM,
2303f513eddSjmcneill FDT_INTR_MPSAFE, tegra_pcie_intr, sc, device_xname(self));
231d59db8d0Sjmcneill if (sc->sc_ih == NULL) {
232d59db8d0Sjmcneill aprint_error_dev(self, "failed to establish interrupt on %s\n",
233d59db8d0Sjmcneill intrstr);
234d59db8d0Sjmcneill return;
235d59db8d0Sjmcneill }
236d59db8d0Sjmcneill aprint_normal_dev(self, "interrupting on %s\n", intrstr);
237e1b20f28Sjmcneill
238d7a3612eSjakllsch tegra_pcie_setup(sc);
239d7a3612eSjakllsch
240e1b20f28Sjmcneill tegra_pcie_init(&sc->sc_pc, sc);
241e1b20f28Sjmcneill
242ca8ce3aeSthorpej pcires = pciconf_resource_init();
243e1b20f28Sjmcneill
244ca8ce3aeSthorpej pciconf_resource_add(pcires, PCICONF_RESOURCE_IO,
245ca8ce3aeSthorpej TEGRA_PCIE_IO_BASE, TEGRA_PCIE_IO_SIZE);
246ca8ce3aeSthorpej pciconf_resource_add(pcires, PCICONF_RESOURCE_MEM,
247ca8ce3aeSthorpej TEGRA_PCIE_MEM_BASE, TEGRA_PCIE_MEM_SIZE);
248ca8ce3aeSthorpej pciconf_resource_add(pcires, PCICONF_RESOURCE_PREFETCHABLE_MEM,
249ca8ce3aeSthorpej TEGRA_PCIE_PMEM_BASE, TEGRA_PCIE_PMEM_SIZE);
250ca8ce3aeSthorpej
251ca8ce3aeSthorpej error = pci_configure_bus(&sc->sc_pc, pcires, 0,
252e1b20f28Sjmcneill arm_dcache_align);
253e1b20f28Sjmcneill
254ca8ce3aeSthorpej pciconf_resource_fini(pcires);
255e1b20f28Sjmcneill
256e1b20f28Sjmcneill if (error) {
257e1b20f28Sjmcneill aprint_error_dev(self, "configuration failed (%d)\n",
258e1b20f28Sjmcneill error);
259e1b20f28Sjmcneill return;
260e1b20f28Sjmcneill }
261e1b20f28Sjmcneill
262e1b20f28Sjmcneill tegra_pcie_enable(sc);
263e1b20f28Sjmcneill
264479a90aaSjmcneill tegra_pcie_enable_ports(sc);
265479a90aaSjmcneill
266e1b20f28Sjmcneill memset(&pba, 0, sizeof(pba));
267e1b20f28Sjmcneill pba.pba_flags = PCI_FLAGS_MRL_OKAY |
268e1b20f28Sjmcneill PCI_FLAGS_MRM_OKAY |
269e1b20f28Sjmcneill PCI_FLAGS_MWI_OKAY |
270d7a3612eSjakllsch PCI_FLAGS_MEM_OKAY |
271d7a3612eSjakllsch PCI_FLAGS_IO_OKAY;
272d7a3612eSjakllsch pba.pba_iot = sc->sc_bst;
273e1b20f28Sjmcneill pba.pba_memt = sc->sc_bst;
274e1b20f28Sjmcneill pba.pba_dmat = sc->sc_dmat;
275e1b20f28Sjmcneill pba.pba_pc = &sc->sc_pc;
276e1b20f28Sjmcneill pba.pba_bus = 0;
277e1b20f28Sjmcneill
278d1083ec1Sthorpej config_found(self, &pba, pcibusprint,
279c7fb772bSthorpej CFARGS(.devhandle = device_handle(self)));
280e1b20f28Sjmcneill }
281e1b20f28Sjmcneill
282e1b20f28Sjmcneill static int
tegra_pcie_legacy_intr(struct tegra_pcie_softc * sc)283fe03655cSjmcneill tegra_pcie_legacy_intr(struct tegra_pcie_softc *sc)
284fe03655cSjmcneill {
285fe03655cSjmcneill const uint32_t msg = bus_space_read_4(sc->sc_bst, sc->sc_bsh_afi,
286fe03655cSjmcneill AFI_MSG_REG);
287fe03655cSjmcneill struct tegra_pcie_ih *pcie_ih;
288fe03655cSjmcneill int rv = 0;
289fe03655cSjmcneill
290fe03655cSjmcneill if (msg & (AFI_MSG_INT0|AFI_MSG_INT1)) {
291fe03655cSjmcneill mutex_enter(&sc->sc_lock);
292fe03655cSjmcneill const u_int lastgen = sc->sc_intrgen;
293fe03655cSjmcneill TAILQ_FOREACH(pcie_ih, &sc->sc_intrs, ih_entry) {
294fe03655cSjmcneill int (*callback)(void *) = pcie_ih->ih_callback;
295fe03655cSjmcneill void *arg = pcie_ih->ih_arg;
296fa7f7f0eSjmcneill const int mpsafe = pcie_ih->ih_mpsafe;
297fe03655cSjmcneill mutex_exit(&sc->sc_lock);
298fa7f7f0eSjmcneill
299fa7f7f0eSjmcneill if (!mpsafe)
300fa7f7f0eSjmcneill KERNEL_LOCK(1, curlwp);
301fe03655cSjmcneill rv += callback(arg);
302fa7f7f0eSjmcneill if (!mpsafe)
303fa7f7f0eSjmcneill KERNEL_UNLOCK_ONE(curlwp);
304fa7f7f0eSjmcneill
305fe03655cSjmcneill mutex_enter(&sc->sc_lock);
306fe03655cSjmcneill if (lastgen != sc->sc_intrgen)
307fe03655cSjmcneill break;
308fe03655cSjmcneill }
309fe03655cSjmcneill mutex_exit(&sc->sc_lock);
310fe03655cSjmcneill } else if (msg & (AFI_MSG_PM_PME0|AFI_MSG_PM_PME1)) {
311fe03655cSjmcneill device_printf(sc->sc_dev, "PM PME message; AFI_MSG=%08x\n",
312fe03655cSjmcneill msg);
313fe03655cSjmcneill } else {
314fe03655cSjmcneill bus_space_write_4(sc->sc_bst, sc->sc_bsh_afi, AFI_MSG_REG, msg);
315fe03655cSjmcneill rv = 1;
316fe03655cSjmcneill }
317fe03655cSjmcneill
318fe03655cSjmcneill return rv;
319fe03655cSjmcneill }
320fe03655cSjmcneill
321fe03655cSjmcneill static int
tegra_pcie_intr(void * priv)322e1b20f28Sjmcneill tegra_pcie_intr(void *priv)
323e1b20f28Sjmcneill {
324e1b20f28Sjmcneill struct tegra_pcie_softc *sc = priv;
325bd3ae4e6Sjakllsch int rv;
326e1b20f28Sjmcneill
327e1b20f28Sjmcneill const uint32_t code = bus_space_read_4(sc->sc_bst, sc->sc_bsh_afi,
328e1b20f28Sjmcneill AFI_INTR_CODE_REG);
329e1b20f28Sjmcneill const uint32_t sig = bus_space_read_4(sc->sc_bst, sc->sc_bsh_afi,
330e1b20f28Sjmcneill AFI_INTR_SIGNATURE_REG);
331e1b20f28Sjmcneill
332e1b20f28Sjmcneill switch (__SHIFTOUT(code, AFI_INTR_CODE_INT_CODE)) {
333e1b20f28Sjmcneill case AFI_INTR_CODE_SM_MSG:
334bd3ae4e6Sjakllsch rv = tegra_pcie_legacy_intr(sc);
335bd3ae4e6Sjakllsch break;
336e1b20f28Sjmcneill default:
337e1b20f28Sjmcneill device_printf(sc->sc_dev, "intr: code %#x sig %#x\n",
338e1b20f28Sjmcneill code, sig);
339bd3ae4e6Sjakllsch rv = 1;
340bd3ae4e6Sjakllsch break;
341e1b20f28Sjmcneill }
342bd3ae4e6Sjakllsch
343bd3ae4e6Sjakllsch bus_space_write_4(sc->sc_bst, sc->sc_bsh_afi, AFI_INTR_CODE_REG, 0);
344bd3ae4e6Sjakllsch
345bd3ae4e6Sjakllsch return rv;
346e1b20f28Sjmcneill }
347e1b20f28Sjmcneill
348e1b20f28Sjmcneill static void
tegra_pcie_enable_clocks(struct tegra_pcie_softc * const sc)34942f8fa7cSjmcneill tegra_pcie_enable_clocks(struct tegra_pcie_softc * const sc)
35042f8fa7cSjmcneill {
35142f8fa7cSjmcneill const char *clock_names[] = { "pex", "afi", "pll_e", "cml" };
35242f8fa7cSjmcneill const char *reset_names[] = { "pex", "afi", "pcie_x" };
35342f8fa7cSjmcneill struct fdtbus_reset *rst;
35442f8fa7cSjmcneill struct clk *clk;
35542f8fa7cSjmcneill int n;
35642f8fa7cSjmcneill
35742f8fa7cSjmcneill for (n = 0; n < __arraycount(clock_names); n++) {
35842f8fa7cSjmcneill clk = fdtbus_clock_get(sc->sc_phandle, clock_names[n]);
35942f8fa7cSjmcneill if (clk == NULL || clk_enable(clk) != 0)
36042f8fa7cSjmcneill aprint_error_dev(sc->sc_dev, "couldn't enable clock %s\n",
36142f8fa7cSjmcneill clock_names[n]);
36242f8fa7cSjmcneill }
36342f8fa7cSjmcneill
36442f8fa7cSjmcneill for (n = 0; n < __arraycount(reset_names); n++) {
36542f8fa7cSjmcneill rst = fdtbus_reset_get(sc->sc_phandle, reset_names[n]);
36642f8fa7cSjmcneill if (rst == NULL || fdtbus_reset_deassert(rst) != 0)
36742f8fa7cSjmcneill aprint_error_dev(sc->sc_dev, "couldn't de-assert reset %s\n",
36842f8fa7cSjmcneill reset_names[n]);
36942f8fa7cSjmcneill }
37042f8fa7cSjmcneill }
37142f8fa7cSjmcneill
3729288b0c3Sskrll #if 0
37342f8fa7cSjmcneill static void
374479a90aaSjmcneill tegra_pcie_reset_port(struct tegra_pcie_softc * const sc, int index)
375479a90aaSjmcneill {
376479a90aaSjmcneill uint32_t val;
377479a90aaSjmcneill
378479a90aaSjmcneill val = bus_space_read_4(sc->sc_bst, sc->sc_bsh_afi, AFI_PEXn_CTRL_REG(index));
379479a90aaSjmcneill val &= ~AFI_PEXn_CTRL_RST_L;
380479a90aaSjmcneill bus_space_write_4(sc->sc_bst, sc->sc_bsh_afi, AFI_PEXn_CTRL_REG(index), val);
381479a90aaSjmcneill
382479a90aaSjmcneill delay(2000);
383479a90aaSjmcneill
384479a90aaSjmcneill val = bus_space_read_4(sc->sc_bst, sc->sc_bsh_afi, AFI_PEXn_CTRL_REG(index));
385479a90aaSjmcneill val |= AFI_PEXn_CTRL_RST_L;
386479a90aaSjmcneill bus_space_write_4(sc->sc_bst, sc->sc_bsh_afi, AFI_PEXn_CTRL_REG(index), val);
387479a90aaSjmcneill }
3889288b0c3Sskrll #endif
389479a90aaSjmcneill
390479a90aaSjmcneill static void
tegra_pcie_enable_ports(struct tegra_pcie_softc * const sc)391479a90aaSjmcneill tegra_pcie_enable_ports(struct tegra_pcie_softc * const sc)
392479a90aaSjmcneill {
393a9f139f8Sjmcneill struct fdtbus_phy *phy;
394479a90aaSjmcneill const u_int *data;
395a9f139f8Sjmcneill int child, len, n;
396479a90aaSjmcneill uint32_t val;
397479a90aaSjmcneill
398479a90aaSjmcneill for (child = OF_child(sc->sc_phandle); child; child = OF_peer(child)) {
399479a90aaSjmcneill if (!fdtbus_status_okay(child))
400479a90aaSjmcneill continue;
401a9f139f8Sjmcneill
402a9f139f8Sjmcneill /* Enable PHYs */
403a9f139f8Sjmcneill for (n = 0; (phy = fdtbus_phy_get_index(child, n)) != NULL; n++)
404a9f139f8Sjmcneill if (fdtbus_phy_enable(phy, true) != 0)
405a9f139f8Sjmcneill aprint_error_dev(sc->sc_dev, "couldn't enable %s phy #%d\n",
406a9f139f8Sjmcneill fdtbus_get_string(child, "name"), n);
407a9f139f8Sjmcneill
408479a90aaSjmcneill data = fdtbus_get_prop(child, "reg", &len);
409479a90aaSjmcneill if (data == NULL || len < 4)
410479a90aaSjmcneill continue;
411479a90aaSjmcneill const u_int index = ((be32toh(data[0]) >> 11) & 0x1f) - 1;
412479a90aaSjmcneill
413479a90aaSjmcneill val = bus_space_read_4(sc->sc_bst, sc->sc_bsh_afi, AFI_PEXn_CTRL_REG(index));
414479a90aaSjmcneill val |= AFI_PEXn_CTRL_CLKREQ_EN;
415479a90aaSjmcneill val |= AFI_PEXn_CTRL_REFCLK_EN;
416479a90aaSjmcneill val |= AFI_PEXn_CTRL_REFCLK_OVERRIDE_EN;
417479a90aaSjmcneill bus_space_write_4(sc->sc_bst, sc->sc_bsh_afi, AFI_PEXn_CTRL_REG(index), val);
418479a90aaSjmcneill
4199288b0c3Sskrll #if 0
420479a90aaSjmcneill tegra_pcie_reset_port(sc, index);
4219288b0c3Sskrll #endif
422a9f139f8Sjmcneill
423479a90aaSjmcneill }
424479a90aaSjmcneill }
425479a90aaSjmcneill
426479a90aaSjmcneill static void
tegra_pcie_setup(struct tegra_pcie_softc * const sc)427d7a3612eSjakllsch tegra_pcie_setup(struct tegra_pcie_softc * const sc)
428d7a3612eSjakllsch {
429479a90aaSjmcneill uint32_t val, cfg, lanes;
430479a90aaSjmcneill int child, len;
431479a90aaSjmcneill const u_int *data;
432d7a3612eSjakllsch size_t i;
433d7a3612eSjakllsch
434479a90aaSjmcneill /* Enable PLLE control */
435479a90aaSjmcneill val = bus_space_read_4(sc->sc_bst, sc->sc_bsh_afi, AFI_PLLE_CONTROL_REG);
436479a90aaSjmcneill val &= ~AFI_PLLE_CONTROL_BYPASS_PADS2PLLE_CONTROL;
437479a90aaSjmcneill val |= AFI_PLLE_CONTROL_PADS2PLLE_CONTROL_EN;
438479a90aaSjmcneill bus_space_write_4(sc->sc_bst, sc->sc_bsh_afi, AFI_PLLE_CONTROL_REG, val);
439479a90aaSjmcneill
440479a90aaSjmcneill /* Disable PEX clock bias pad power down */
441479a90aaSjmcneill bus_space_write_4(sc->sc_bst, sc->sc_bsh_afi, AFI_PEXBIAS_CTRL_REG, 0);
442479a90aaSjmcneill
443479a90aaSjmcneill /* Configure PCIE mode and enable ports */
444479a90aaSjmcneill cfg = bus_space_read_4(sc->sc_bst, sc->sc_bsh_afi, AFI_PCIE_CONFIG_REG);
445479a90aaSjmcneill cfg |= AFI_PCIE_CONFIG_PCIECn_DISABLE_DEVICE(0);
446479a90aaSjmcneill cfg |= AFI_PCIE_CONFIG_PCIECn_DISABLE_DEVICE(1);
447479a90aaSjmcneill cfg &= ~AFI_PCIE_CONFIG_SM2TMS0_XBAR_CONFIG;
448479a90aaSjmcneill
449479a90aaSjmcneill lanes = 0;
450479a90aaSjmcneill for (child = OF_child(sc->sc_phandle); child; child = OF_peer(child)) {
451479a90aaSjmcneill if (!fdtbus_status_okay(child))
452479a90aaSjmcneill continue;
453479a90aaSjmcneill data = fdtbus_get_prop(child, "reg", &len);
454479a90aaSjmcneill if (data == NULL || len < 4)
455479a90aaSjmcneill continue;
456479a90aaSjmcneill const u_int index = ((be32toh(data[0]) >> 11) & 0x1f) - 1;
457479a90aaSjmcneill if (of_getprop_uint32(child, "nvidia,num-lanes", &val) != 0)
458479a90aaSjmcneill continue;
459479a90aaSjmcneill lanes |= (val << (index << 3));
460479a90aaSjmcneill cfg &= ~AFI_PCIE_CONFIG_PCIECn_DISABLE_DEVICE(index);
461479a90aaSjmcneill }
462479a90aaSjmcneill
463479a90aaSjmcneill switch (lanes) {
464479a90aaSjmcneill case 0x0104:
465479a90aaSjmcneill aprint_normal_dev(sc->sc_dev, "lane config: x4 x1\n");
466479a90aaSjmcneill cfg |= __SHIFTIN(AFI_PCIE_CONFIG_SM2TMS0_XBAR_CONFIG_4_1,
467479a90aaSjmcneill AFI_PCIE_CONFIG_SM2TMS0_XBAR_CONFIG);
468479a90aaSjmcneill break;
469479a90aaSjmcneill case 0x0102:
470479a90aaSjmcneill aprint_normal_dev(sc->sc_dev, "lane config: x2 x1\n");
471479a90aaSjmcneill cfg |= __SHIFTIN(AFI_PCIE_CONFIG_SM2TMS0_XBAR_CONFIG_2_1,
472479a90aaSjmcneill AFI_PCIE_CONFIG_SM2TMS0_XBAR_CONFIG);
473479a90aaSjmcneill break;
474479a90aaSjmcneill }
475479a90aaSjmcneill
476479a90aaSjmcneill bus_space_write_4(sc->sc_bst, sc->sc_bsh_afi, AFI_PCIE_CONFIG_REG, cfg);
477479a90aaSjmcneill
478479a90aaSjmcneill /* Configure refclk pad */
4793f7c862bSthorpej if (sc->sc_type == TEGRA_PCIE_124) {
4803f7c862bSthorpej bus_space_write_4(sc->sc_bst, sc->sc_bsh_pads,
4813f7c862bSthorpej PADS_REFCLK_CFG0_REG, 0x44ac44ac);
4823f7c862bSthorpej }
4833f7c862bSthorpej if (sc->sc_type == TEGRA_PCIE_210) {
4843f7c862bSthorpej bus_space_write_4(sc->sc_bst, sc->sc_bsh_pads,
4853f7c862bSthorpej PADS_REFCLK_CFG0_REG, 0x90b890b8);
4863f7c862bSthorpej }
487479a90aaSjmcneill
488d7a3612eSjakllsch /*
489d7a3612eSjakllsch * Map PCI address spaces into ARM address space via
490d7a3612eSjakllsch * HyperTransport-like "FPCI".
491d7a3612eSjakllsch */
492d7a3612eSjakllsch static const struct { uint32_t size, base, fpci; } pcie_init_table[] = {
493d7a3612eSjakllsch /*
494d7a3612eSjakllsch * === BEWARE ===
495d7a3612eSjakllsch *
496d7a3612eSjakllsch * We depend on our TEGRA_PCIE_IO window overlaping the
497d7a3612eSjakllsch * TEGRA_PCIE_A1 window to allow us to use the same
498d7a3612eSjakllsch * bus_space_tag for both PCI IO and Memory spaces.
499d7a3612eSjakllsch *
500d7a3612eSjakllsch * 0xfdfc000000-0xfdfdffffff is the FPCI/HyperTransport
501d7a3612eSjakllsch * mapping for 0x0000000-0x1ffffff of PCI IO space.
502d7a3612eSjakllsch */
503d7a3612eSjakllsch { TEGRA_PCIE_IO_SIZE >> 12, TEGRA_PCIE_IO_BASE,
504d7a3612eSjakllsch (0xfdfc000000 + TEGRA_PCIE_IO_BASE) >> 8 | 0, },
505d7a3612eSjakllsch
506d7a3612eSjakllsch /* HyperTransport Technology Type 1 Address Format */
507d7a3612eSjakllsch { TEGRA_PCIE_CONF_SIZE >> 12, TEGRA_PCIE_CONF_BASE,
508d7a3612eSjakllsch 0xfdff000000 >> 8 | 0, },
509d7a3612eSjakllsch
510d7a3612eSjakllsch /* 1:1 MMIO mapping */
511d7a3612eSjakllsch { TEGRA_PCIE_MEM_SIZE >> 12, TEGRA_PCIE_MEM_BASE,
512d7a3612eSjakllsch TEGRA_PCIE_MEM_BASE >> 8 | 1, },
513d7a3612eSjakllsch
514d7a3612eSjakllsch /* Extended HyperTransport Technology Type 1 Address Format */
515d7a3612eSjakllsch { TEGRA_PCIE_EXTC_SIZE >> 12, TEGRA_PCIE_EXTC_BASE,
516d7a3612eSjakllsch 0xfe10000000 >> 8 | 0, },
517d7a3612eSjakllsch
518d7a3612eSjakllsch /* 1:1 prefetchable MMIO mapping */
519d7a3612eSjakllsch { TEGRA_PCIE_PMEM_SIZE >> 12, TEGRA_PCIE_PMEM_BASE,
520d7a3612eSjakllsch TEGRA_PCIE_PMEM_BASE >> 8 | 1, },
521d7a3612eSjakllsch };
522d7a3612eSjakllsch
523d7a3612eSjakllsch for (i = 0; i < AFI_AXI_NBAR; i++) {
524d7a3612eSjakllsch bus_space_write_4(sc->sc_bst, sc->sc_bsh_afi,
525d7a3612eSjakllsch AFI_AXI_BARi_SZ(i), 0);
526d7a3612eSjakllsch bus_space_write_4(sc->sc_bst, sc->sc_bsh_afi,
527d7a3612eSjakllsch AFI_AXI_BARi_START(i), 0);
528d7a3612eSjakllsch bus_space_write_4(sc->sc_bst, sc->sc_bsh_afi,
529d7a3612eSjakllsch AFI_FPCI_BARi(i), 0);
530d7a3612eSjakllsch }
531d7a3612eSjakllsch
532d7a3612eSjakllsch for (i = 0; i < __arraycount(pcie_init_table); i++) {
533d7a3612eSjakllsch bus_space_write_4(sc->sc_bst, sc->sc_bsh_afi,
534d7a3612eSjakllsch AFI_AXI_BARi_START(i), pcie_init_table[i].base);
535d7a3612eSjakllsch bus_space_write_4(sc->sc_bst, sc->sc_bsh_afi,
536d7a3612eSjakllsch AFI_FPCI_BARi(i), pcie_init_table[i].fpci);
537d7a3612eSjakllsch bus_space_write_4(sc->sc_bst, sc->sc_bsh_afi,
538d7a3612eSjakllsch AFI_AXI_BARi_SZ(i), pcie_init_table[i].size);
539d7a3612eSjakllsch }
540d7a3612eSjakllsch }
541d7a3612eSjakllsch
542d7a3612eSjakllsch static void
tegra_pcie_enable(struct tegra_pcie_softc * sc)543e1b20f28Sjmcneill tegra_pcie_enable(struct tegra_pcie_softc *sc)
544e1b20f28Sjmcneill {
545fe03655cSjmcneill /* disable MSI */
546fe03655cSjmcneill bus_space_write_4(sc->sc_bst, sc->sc_bsh_afi,
547fe03655cSjmcneill AFI_MSI_BAR_SZ_REG, 0);
548fe03655cSjmcneill bus_space_write_4(sc->sc_bst, sc->sc_bsh_afi,
549fe03655cSjmcneill AFI_MSI_FPCI_BAR_ST_REG, 0);
550fe03655cSjmcneill bus_space_write_4(sc->sc_bst, sc->sc_bsh_afi,
551fe03655cSjmcneill AFI_MSI_AXI_BAR_ST_REG, 0);
552fe03655cSjmcneill
553e1b20f28Sjmcneill bus_space_write_4(sc->sc_bst, sc->sc_bsh_afi,
554e1b20f28Sjmcneill AFI_SM_INTR_ENABLE_REG, 0xffffffff);
555e1b20f28Sjmcneill bus_space_write_4(sc->sc_bst, sc->sc_bsh_afi,
556e1b20f28Sjmcneill AFI_AFI_INTR_ENABLE_REG, 0);
557e1b20f28Sjmcneill bus_space_write_4(sc->sc_bst, sc->sc_bsh_afi, AFI_INTR_CODE_REG, 0);
558e1b20f28Sjmcneill bus_space_write_4(sc->sc_bst, sc->sc_bsh_afi,
559e1b20f28Sjmcneill AFI_INTR_MASK_REG, AFI_INTR_MASK_INT);
560e1b20f28Sjmcneill }
561e1b20f28Sjmcneill
5621f4f07e0Sjakllsch static void
tegra_pcie_conf_frag_map(struct tegra_pcie_softc * const sc,uint bus,uint frg)5631f4f07e0Sjakllsch tegra_pcie_conf_frag_map(struct tegra_pcie_softc * const sc, uint bus,
5641f4f07e0Sjakllsch uint frg)
5651f4f07e0Sjakllsch {
5661f4f07e0Sjakllsch bus_addr_t a;
5671f4f07e0Sjakllsch
5681f4f07e0Sjakllsch KASSERT(bus >= 1);
5691f4f07e0Sjakllsch KASSERT(bus < TEGRA_PCIE_NBUS);
5701f4f07e0Sjakllsch KASSERT(frg < TEGRA_PCIE_ECFB);
5711f4f07e0Sjakllsch
5721f4f07e0Sjakllsch if (sc->sc_bsh_extc[bus-1][frg] != 0) {
5731f4f07e0Sjakllsch device_printf(sc->sc_dev, "bus %u fragment %#x already "
5741f4f07e0Sjakllsch "mapped\n", bus, frg);
5751f4f07e0Sjakllsch return;
5761f4f07e0Sjakllsch }
5771f4f07e0Sjakllsch
5781f4f07e0Sjakllsch a = TEGRA_PCIE_EXTC_BASE + (bus << 16) + (frg << 24);
579059f233aSjmcneill if (bus_space_map(sc->sc_bst, a, 1 << 16,
580*5158b98cSjmcneill BUS_SPACE_MAP_NONPOSTED,
5811f4f07e0Sjakllsch &sc->sc_bsh_extc[bus-1][frg]) != 0)
5821f4f07e0Sjakllsch device_printf(sc->sc_dev, "couldn't map PCIE "
5831f4f07e0Sjakllsch "configuration for bus %u fragment %#x", bus, frg);
5841f4f07e0Sjakllsch }
5851f4f07e0Sjakllsch
5861f4f07e0Sjakllsch /* map non-non-extended configuration space for full bus range */
5871f4f07e0Sjakllsch static void
tegra_pcie_conf_map_bus(struct tegra_pcie_softc * const sc,uint bus)5881f4f07e0Sjakllsch tegra_pcie_conf_map_bus(struct tegra_pcie_softc * const sc, uint bus)
5891f4f07e0Sjakllsch {
5901f4f07e0Sjakllsch uint i;
5911f4f07e0Sjakllsch
5921f4f07e0Sjakllsch for (i = 1; i < TEGRA_PCIE_ECFB; i++) {
5931f4f07e0Sjakllsch tegra_pcie_conf_frag_map(sc, bus, i);
5941f4f07e0Sjakllsch }
5951f4f07e0Sjakllsch }
5961f4f07e0Sjakllsch
5971f4f07e0Sjakllsch /* map non-extended configuration space for full bus range */
5981f4f07e0Sjakllsch static void
tegra_pcie_conf_map_buses(struct tegra_pcie_softc * const sc)5991f4f07e0Sjakllsch tegra_pcie_conf_map_buses(struct tegra_pcie_softc * const sc)
6001f4f07e0Sjakllsch {
6011f4f07e0Sjakllsch uint b;
6021f4f07e0Sjakllsch
6031f4f07e0Sjakllsch for (b = 1; b < TEGRA_PCIE_NBUS; b++) {
6041f4f07e0Sjakllsch tegra_pcie_conf_frag_map(sc, b, 0);
6051f4f07e0Sjakllsch }
6061f4f07e0Sjakllsch }
6071f4f07e0Sjakllsch
608e1b20f28Sjmcneill void
tegra_pcie_init(pci_chipset_tag_t pc,void * priv)609e1b20f28Sjmcneill tegra_pcie_init(pci_chipset_tag_t pc, void *priv)
610e1b20f28Sjmcneill {
611e1b20f28Sjmcneill pc->pc_conf_v = priv;
612e1b20f28Sjmcneill pc->pc_attach_hook = tegra_pcie_attach_hook;
613e1b20f28Sjmcneill pc->pc_bus_maxdevs = tegra_pcie_bus_maxdevs;
614e1b20f28Sjmcneill pc->pc_make_tag = tegra_pcie_make_tag;
615e1b20f28Sjmcneill pc->pc_decompose_tag = tegra_pcie_decompose_tag;
616e1b20f28Sjmcneill pc->pc_conf_read = tegra_pcie_conf_read;
617e1b20f28Sjmcneill pc->pc_conf_write = tegra_pcie_conf_write;
618e1b20f28Sjmcneill pc->pc_conf_hook = tegra_pcie_conf_hook;
619e1b20f28Sjmcneill pc->pc_conf_interrupt = tegra_pcie_conf_interrupt;
620e1b20f28Sjmcneill
621e1b20f28Sjmcneill pc->pc_intr_v = priv;
622e1b20f28Sjmcneill pc->pc_intr_map = tegra_pcie_intr_map;
623e1b20f28Sjmcneill pc->pc_intr_string = tegra_pcie_intr_string;
624e1b20f28Sjmcneill pc->pc_intr_evcnt = tegra_pcie_intr_evcnt;
625fa7f7f0eSjmcneill pc->pc_intr_setattr = tegra_pcie_intr_setattr;
626e1b20f28Sjmcneill pc->pc_intr_establish = tegra_pcie_intr_establish;
627e1b20f28Sjmcneill pc->pc_intr_disestablish = tegra_pcie_intr_disestablish;
628e1b20f28Sjmcneill }
629e1b20f28Sjmcneill
630e1b20f28Sjmcneill static void
tegra_pcie_attach_hook(device_t parent,device_t self,struct pcibus_attach_args * pba)631e1b20f28Sjmcneill tegra_pcie_attach_hook(device_t parent, device_t self,
632e1b20f28Sjmcneill struct pcibus_attach_args *pba)
633e1b20f28Sjmcneill {
6341f4f07e0Sjakllsch const pci_chipset_tag_t pc = pba->pba_pc;
6351f4f07e0Sjakllsch struct tegra_pcie_softc * const sc = pc->pc_conf_v;
6361f4f07e0Sjakllsch
6371f4f07e0Sjakllsch if (pba->pba_bus >= 1) {
6381f4f07e0Sjakllsch tegra_pcie_conf_map_bus(sc, pba->pba_bus);
6391f4f07e0Sjakllsch }
640e1b20f28Sjmcneill }
641e1b20f28Sjmcneill
642e1b20f28Sjmcneill static int
tegra_pcie_bus_maxdevs(void * v,int busno)643e1b20f28Sjmcneill tegra_pcie_bus_maxdevs(void *v, int busno)
644e1b20f28Sjmcneill {
645e1b20f28Sjmcneill return busno == 0 ? 2 : 32;
646e1b20f28Sjmcneill }
647e1b20f28Sjmcneill
648e1b20f28Sjmcneill static pcitag_t
tegra_pcie_make_tag(void * v,int b,int d,int f)649e1b20f28Sjmcneill tegra_pcie_make_tag(void *v, int b, int d, int f)
650e1b20f28Sjmcneill {
651e1b20f28Sjmcneill return (b << 16) | (d << 11) | (f << 8);
652e1b20f28Sjmcneill }
653e1b20f28Sjmcneill
654e1b20f28Sjmcneill static void
tegra_pcie_decompose_tag(void * v,pcitag_t tag,int * bp,int * dp,int * fp)655e1b20f28Sjmcneill tegra_pcie_decompose_tag(void *v, pcitag_t tag, int *bp, int *dp, int *fp)
656e1b20f28Sjmcneill {
657e1b20f28Sjmcneill if (bp)
658e1b20f28Sjmcneill *bp = (tag >> 16) & 0xff;
659e1b20f28Sjmcneill if (dp)
660e1b20f28Sjmcneill *dp = (tag >> 11) & 0x1f;
661e1b20f28Sjmcneill if (fp)
662e1b20f28Sjmcneill *fp = (tag >> 8) & 0x7;
663e1b20f28Sjmcneill }
664e1b20f28Sjmcneill
665e1b20f28Sjmcneill static pcireg_t
tegra_pcie_conf_read(void * v,pcitag_t tag,int offset)666e1b20f28Sjmcneill tegra_pcie_conf_read(void *v, pcitag_t tag, int offset)
667e1b20f28Sjmcneill {
668e1b20f28Sjmcneill struct tegra_pcie_softc *sc = v;
669e1b20f28Sjmcneill bus_space_handle_t bsh;
670e1b20f28Sjmcneill int b, d, f;
671e1b20f28Sjmcneill u_int reg;
672e1b20f28Sjmcneill
673605f564fSmsaitoh if ((unsigned int)offset >= PCI_EXTCONF_SIZE)
674605f564fSmsaitoh return (pcireg_t) -1;
675605f564fSmsaitoh
676e1b20f28Sjmcneill tegra_pcie_decompose_tag(v, tag, &b, &d, &f);
677e1b20f28Sjmcneill
6781f4f07e0Sjakllsch if (b >= TEGRA_PCIE_NBUS)
6791f4f07e0Sjakllsch return (pcireg_t) -1;
6801f4f07e0Sjakllsch
681e1b20f28Sjmcneill if (b == 0) {
6826ec5e368Sjakllsch if (d >= 2 || f != 0)
6836ec5e368Sjakllsch return (pcireg_t) -1;
684e1b20f28Sjmcneill reg = d * 0x1000 + offset;
685abb2550cSjakllsch bsh = sc->sc_bsh_rpconf;
686e1b20f28Sjmcneill } else {
6871f4f07e0Sjakllsch reg = (d << 11) | (f << 8) | (offset & 0xff);
6881f4f07e0Sjakllsch bsh = sc->sc_bsh_extc[b-1][(offset >> 8) & 0xf];
6891f4f07e0Sjakllsch if (bsh == 0)
690d87d62cfSjakllsch return (pcireg_t) -1;
691e1b20f28Sjmcneill }
692e1b20f28Sjmcneill
693e1b20f28Sjmcneill return bus_space_read_4(sc->sc_bst, bsh, reg);
694e1b20f28Sjmcneill }
695e1b20f28Sjmcneill
696e1b20f28Sjmcneill static void
tegra_pcie_conf_write(void * v,pcitag_t tag,int offset,pcireg_t val)697e1b20f28Sjmcneill tegra_pcie_conf_write(void *v, pcitag_t tag, int offset, pcireg_t val)
698e1b20f28Sjmcneill {
699e1b20f28Sjmcneill struct tegra_pcie_softc *sc = v;
700e1b20f28Sjmcneill bus_space_handle_t bsh;
701e1b20f28Sjmcneill int b, d, f;
702e1b20f28Sjmcneill u_int reg;
703e1b20f28Sjmcneill
704605f564fSmsaitoh if ((unsigned int)offset >= PCI_EXTCONF_SIZE)
705605f564fSmsaitoh return;
706605f564fSmsaitoh
707e1b20f28Sjmcneill tegra_pcie_decompose_tag(v, tag, &b, &d, &f);
708e1b20f28Sjmcneill
7091f4f07e0Sjakllsch if (b >= TEGRA_PCIE_NBUS)
7101f4f07e0Sjakllsch return;
7111f4f07e0Sjakllsch
712e1b20f28Sjmcneill if (b == 0) {
7136ec5e368Sjakllsch if (d >= 2 || f != 0)
7146ec5e368Sjakllsch return;
715e1b20f28Sjmcneill reg = d * 0x1000 + offset;
716abb2550cSjakllsch bsh = sc->sc_bsh_rpconf;
717e1b20f28Sjmcneill } else {
7181f4f07e0Sjakllsch reg = (d << 11) | (f << 8) | (offset & 0xff);
7191f4f07e0Sjakllsch bsh = sc->sc_bsh_extc[b-1][(offset >> 8) & 0xf];
7201f4f07e0Sjakllsch if (bsh == 0)
721d87d62cfSjakllsch return;
722e1b20f28Sjmcneill }
723e1b20f28Sjmcneill
724e1b20f28Sjmcneill bus_space_write_4(sc->sc_bst, bsh, reg, val);
725e1b20f28Sjmcneill }
726e1b20f28Sjmcneill
727e1b20f28Sjmcneill static int
tegra_pcie_conf_hook(void * v,int b,int d,int f,pcireg_t id)728e1b20f28Sjmcneill tegra_pcie_conf_hook(void *v, int b, int d, int f, pcireg_t id)
729e1b20f28Sjmcneill {
73033332c25Sjakllsch return PCI_CONF_DEFAULT & ~PCI_CONF_ENABLE_BM;
731e1b20f28Sjmcneill }
732e1b20f28Sjmcneill
733e1b20f28Sjmcneill static void
tegra_pcie_conf_interrupt(void * v,int bus,int dev,int ipin,int swiz,int * ilinep)734e1b20f28Sjmcneill tegra_pcie_conf_interrupt(void *v, int bus, int dev, int ipin, int swiz,
735e1b20f28Sjmcneill int *ilinep)
736e1b20f28Sjmcneill {
737d59db8d0Sjmcneill *ilinep = 5;
738e1b20f28Sjmcneill }
739e1b20f28Sjmcneill
740e1b20f28Sjmcneill static int
tegra_pcie_intr_map(const struct pci_attach_args * pa,pci_intr_handle_t * ih)741e1b20f28Sjmcneill tegra_pcie_intr_map(const struct pci_attach_args *pa, pci_intr_handle_t *ih)
742e1b20f28Sjmcneill {
743e1b20f28Sjmcneill if (pa->pa_intrpin == 0)
744e1b20f28Sjmcneill return EINVAL;
745e1b20f28Sjmcneill *ih = pa->pa_intrpin;
746e1b20f28Sjmcneill return 0;
747e1b20f28Sjmcneill }
748e1b20f28Sjmcneill
749e1b20f28Sjmcneill static const char *
tegra_pcie_intr_string(void * v,pci_intr_handle_t ih,char * buf,size_t len)750e1b20f28Sjmcneill tegra_pcie_intr_string(void *v, pci_intr_handle_t ih, char *buf, size_t len)
751e1b20f28Sjmcneill {
752e1b20f28Sjmcneill struct tegra_pcie_softc *sc = v;
753e1b20f28Sjmcneill
754e1b20f28Sjmcneill if (ih == PCI_INTERRUPT_PIN_NONE)
755e1b20f28Sjmcneill return NULL;
756e1b20f28Sjmcneill
757d59db8d0Sjmcneill if (!fdtbus_intr_str(sc->sc_phandle, 0, buf, len))
758d59db8d0Sjmcneill return NULL;
759d59db8d0Sjmcneill
760e1b20f28Sjmcneill return buf;
761e1b20f28Sjmcneill }
762e1b20f28Sjmcneill
763e1b20f28Sjmcneill const struct evcnt *
tegra_pcie_intr_evcnt(void * v,pci_intr_handle_t ih)764e1b20f28Sjmcneill tegra_pcie_intr_evcnt(void *v, pci_intr_handle_t ih)
765e1b20f28Sjmcneill {
766e1b20f28Sjmcneill return NULL;
767e1b20f28Sjmcneill }
768e1b20f28Sjmcneill
769fa7f7f0eSjmcneill static int
tegra_pcie_intr_setattr(void * v,pci_intr_handle_t * ih,int attr,uint64_t data)770fa7f7f0eSjmcneill tegra_pcie_intr_setattr(void *v, pci_intr_handle_t *ih, int attr, uint64_t data)
771fa7f7f0eSjmcneill {
772fa7f7f0eSjmcneill switch (attr) {
773fa7f7f0eSjmcneill case PCI_INTR_MPSAFE:
774c983b58bSjmcneill if (data)
775c983b58bSjmcneill *ih |= IH_MPSAFE;
776c983b58bSjmcneill else
777c983b58bSjmcneill *ih &= ~IH_MPSAFE;
778fa7f7f0eSjmcneill return 0;
779fa7f7f0eSjmcneill default:
780fa7f7f0eSjmcneill return ENODEV;
781fa7f7f0eSjmcneill }
782fa7f7f0eSjmcneill }
783fa7f7f0eSjmcneill
784e1b20f28Sjmcneill static void *
tegra_pcie_intr_establish(void * v,pci_intr_handle_t ih,int ipl,int (* callback)(void *),void * arg,const char * xname)785e1b20f28Sjmcneill tegra_pcie_intr_establish(void *v, pci_intr_handle_t ih, int ipl,
786cce19cc2Sjmcneill int (*callback)(void *), void *arg, const char *xname)
787e1b20f28Sjmcneill {
788e1b20f28Sjmcneill struct tegra_pcie_softc *sc = v;
789e1b20f28Sjmcneill struct tegra_pcie_ih *pcie_ih;
790e1b20f28Sjmcneill
791e1b20f28Sjmcneill if (ih == 0)
792e1b20f28Sjmcneill return NULL;
793e1b20f28Sjmcneill
794e1b20f28Sjmcneill pcie_ih = kmem_alloc(sizeof(*pcie_ih), KM_SLEEP);
795e1b20f28Sjmcneill pcie_ih->ih_callback = callback;
796e1b20f28Sjmcneill pcie_ih->ih_arg = arg;
797e1b20f28Sjmcneill pcie_ih->ih_ipl = ipl;
798c983b58bSjmcneill pcie_ih->ih_mpsafe = (ih & IH_MPSAFE) != 0;
799e1b20f28Sjmcneill
800e1b20f28Sjmcneill mutex_enter(&sc->sc_lock);
801e1b20f28Sjmcneill TAILQ_INSERT_TAIL(&sc->sc_intrs, pcie_ih, ih_entry);
802e1b20f28Sjmcneill sc->sc_intrgen++;
803e1b20f28Sjmcneill mutex_exit(&sc->sc_lock);
804e1b20f28Sjmcneill
805e1b20f28Sjmcneill return pcie_ih;
806e1b20f28Sjmcneill }
807e1b20f28Sjmcneill
808e1b20f28Sjmcneill static void
tegra_pcie_intr_disestablish(void * v,void * vih)809e1b20f28Sjmcneill tegra_pcie_intr_disestablish(void *v, void *vih)
810e1b20f28Sjmcneill {
811e1b20f28Sjmcneill struct tegra_pcie_softc *sc = v;
812e1b20f28Sjmcneill struct tegra_pcie_ih *pcie_ih = vih;
813e1b20f28Sjmcneill
814e1b20f28Sjmcneill mutex_enter(&sc->sc_lock);
815e1b20f28Sjmcneill TAILQ_REMOVE(&sc->sc_intrs, pcie_ih, ih_entry);
816e1b20f28Sjmcneill mutex_exit(&sc->sc_lock);
817e1b20f28Sjmcneill
818e1b20f28Sjmcneill kmem_free(pcie_ih, sizeof(*pcie_ih));
819e1b20f28Sjmcneill }
820