xref: /netbsd-src/sys/arch/arm/nvidia/tegra_pcie.c (revision 5158b98c3ad5629f6fdc5db5d57b396373690489)
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