xref: /openbsd-src/sys/dev/fdt/dwpcie.c (revision 54fbbda3b5f8c42357b8601b12a514e2d25a2771)
1*54fbbda3Sjsg /*	$OpenBSD: dwpcie.c,v 1.57 2024/09/01 03:08:56 jsg Exp $	*/
27659d2d7Skettenis /*
37659d2d7Skettenis  * Copyright (c) 2018 Mark Kettenis <kettenis@openbsd.org>
47659d2d7Skettenis  *
57659d2d7Skettenis  * Permission to use, copy, modify, and distribute this software for any
67659d2d7Skettenis  * purpose with or without fee is hereby granted, provided that the above
77659d2d7Skettenis  * copyright notice and this permission notice appear in all copies.
87659d2d7Skettenis  *
97659d2d7Skettenis  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
107659d2d7Skettenis  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
117659d2d7Skettenis  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
127659d2d7Skettenis  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
137659d2d7Skettenis  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
147659d2d7Skettenis  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
157659d2d7Skettenis  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
167659d2d7Skettenis  */
177659d2d7Skettenis 
187659d2d7Skettenis #include <sys/param.h>
197659d2d7Skettenis #include <sys/systm.h>
207659d2d7Skettenis #include <sys/device.h>
21d5dfbc11Skettenis #include <sys/evcount.h>
227659d2d7Skettenis #include <sys/extent.h>
237659d2d7Skettenis #include <sys/malloc.h>
247659d2d7Skettenis 
257659d2d7Skettenis #include <machine/intr.h>
267659d2d7Skettenis #include <machine/bus.h>
277659d2d7Skettenis #include <machine/fdt.h>
287659d2d7Skettenis 
297659d2d7Skettenis #include <dev/pci/pcidevs.h>
307659d2d7Skettenis #include <dev/pci/pcireg.h>
317659d2d7Skettenis #include <dev/pci/pcivar.h>
32d43be508Skettenis #include <dev/pci/ppbreg.h>
337659d2d7Skettenis 
347659d2d7Skettenis #include <dev/ofw/openfirm.h>
357659d2d7Skettenis #include <dev/ofw/ofw_clock.h>
366a2b914cSpatrick #include <dev/ofw/ofw_gpio.h>
376a2b914cSpatrick #include <dev/ofw/ofw_misc.h>
384605d28eSkettenis #include <dev/ofw/ofw_pinctrl.h>
396a2b914cSpatrick #include <dev/ofw/ofw_power.h>
401140f82aSkettenis #include <dev/ofw/ofw_regulator.h>
417659d2d7Skettenis #include <dev/ofw/fdt.h>
427659d2d7Skettenis 
434605d28eSkettenis /* Registers */
446a2b914cSpatrick #define PCIE_PORT_LINK_CTRL		0x710
456a2b914cSpatrick #define  PCIE_PORT_LINK_CTRL_LANES_MASK			(0x3f << 16)
466a2b914cSpatrick #define  PCIE_PORT_LINK_CTRL_LANES_1			(0x1 << 16)
476a2b914cSpatrick #define  PCIE_PORT_LINK_CTRL_LANES_2			(0x3 << 16)
486a2b914cSpatrick #define  PCIE_PORT_LINK_CTRL_LANES_4			(0x7 << 16)
496a2b914cSpatrick #define  PCIE_PORT_LINK_CTRL_LANES_8			(0xf << 16)
506a2b914cSpatrick #define PCIE_PHY_DEBUG_R1		0x72c
516a2b914cSpatrick #define  PCIE_PHY_DEBUG_R1_XMLH_LINK_IN_TRAINING	(1 << 29)
526a2b914cSpatrick #define  PCIE_PHY_DEBUG_R1_XMLH_LINK_UP			(1 << 4)
536a2b914cSpatrick #define PCIE_LINK_WIDTH_SPEED_CTRL	0x80c
546a2b914cSpatrick #define  PCIE_LINK_WIDTH_SPEED_CTRL_LANES_MASK		(0x1f << 8)
556a2b914cSpatrick #define  PCIE_LINK_WIDTH_SPEED_CTRL_LANES_1		(0x1 << 8)
566a2b914cSpatrick #define  PCIE_LINK_WIDTH_SPEED_CTRL_LANES_2		(0x2 << 8)
576a2b914cSpatrick #define  PCIE_LINK_WIDTH_SPEED_CTRL_LANES_4		(0x4 << 8)
586a2b914cSpatrick #define  PCIE_LINK_WIDTH_SPEED_CTRL_LANES_8		(0x8 << 8)
596a2b914cSpatrick #define  PCIE_LINK_WIDTH_SPEED_CTRL_CHANGE		(1 << 17)
606a2b914cSpatrick 
61d5dfbc11Skettenis #define PCIE_MSI_ADDR_LO	0x820
62d5dfbc11Skettenis #define PCIE_MSI_ADDR_HI	0x824
639a76443bSkettenis #define PCIE_MSI_INTR_ENABLE(x)	(0x828 + (x) * 12)
649a76443bSkettenis #define PCIE_MSI_INTR_MASK(x)	(0x82c + (x) * 12)
659a76443bSkettenis #define PCIE_MSI_INTR_STATUS(x)	(0x830 + (x) * 12)
66d5dfbc11Skettenis 
674605d28eSkettenis #define MISC_CONTROL_1		0x8bc
684605d28eSkettenis #define  MISC_CONTROL_1_DBI_RO_WR_EN	(1 << 0)
694605d28eSkettenis #define IATU_VIEWPORT		0x900
706a2b914cSpatrick #define  IATU_VIEWPORT_INDEX0		0
716a2b914cSpatrick #define  IATU_VIEWPORT_INDEX1		1
726a2b914cSpatrick #define  IATU_VIEWPORT_INDEX2		2
73d2397242Skettenis #define  IATU_VIEWPORT_INDEX3		3
746a2b914cSpatrick #define IATU_OFFSET_VIEWPORT	0x904
756a2b914cSpatrick #define IATU_OFFSET_UNROLL(x)	(0x200 * (x))
766a2b914cSpatrick #define IATU_REGION_CTRL_1	0x000
776a2b914cSpatrick #define  IATU_REGION_CTRL_1_TYPE_MEM	0
7871fe361bSkettenis #define  IATU_REGION_CTRL_1_TYPE_IO	2
794605d28eSkettenis #define  IATU_REGION_CTRL_1_TYPE_CFG0	4
804605d28eSkettenis #define  IATU_REGION_CTRL_1_TYPE_CFG1	5
816a2b914cSpatrick #define IATU_REGION_CTRL_2	0x004
824605d28eSkettenis #define  IATU_REGION_CTRL_2_REGION_EN	(1U << 31)
836a2b914cSpatrick #define IATU_LWR_BASE_ADDR	0x08
846a2b914cSpatrick #define IATU_UPPER_BASE_ADDR	0x0c
856a2b914cSpatrick #define IATU_LIMIT_ADDR		0x10
866a2b914cSpatrick #define IATU_LWR_TARGET_ADDR	0x14
876a2b914cSpatrick #define IATU_UPPER_TARGET_ADDR	0x18
884605d28eSkettenis 
895f800b9bSkettenis /* Marvell ARMADA 8k registers */
904605d28eSkettenis #define PCIE_GLOBAL_CTRL	0x8000
914605d28eSkettenis #define  PCIE_GLOBAL_CTRL_APP_LTSSM_EN		(1 << 2)
924605d28eSkettenis #define  PCIE_GLOBAL_CTRL_DEVICE_TYPE_MASK	(0xf << 4)
934605d28eSkettenis #define  PCIE_GLOBAL_CTRL_DEVICE_TYPE_RC	(0x4 << 4)
944605d28eSkettenis #define PCIE_GLOBAL_STATUS	0x8008
954605d28eSkettenis #define  PCIE_GLOBAL_STATUS_RDLH_LINK_UP	(1 << 1)
964605d28eSkettenis #define  PCIE_GLOBAL_STATUS_PHY_LINK_UP		(1 << 9)
974605d28eSkettenis #define PCIE_PM_STATUS		0x8014
98f2c2f061Skettenis #define PCIE_GLOBAL_INT_CAUSE	0x801c
99f2c2f061Skettenis #define PCIE_GLOBAL_INT_MASK	0x8020
100f2c2f061Skettenis #define  PCIE_GLOBAL_INT_MASK_INT_A		(1 << 9)
101f2c2f061Skettenis #define  PCIE_GLOBAL_INT_MASK_INT_B		(1 << 10)
102f2c2f061Skettenis #define  PCIE_GLOBAL_INT_MASK_INT_C		(1 << 11)
103f2c2f061Skettenis #define  PCIE_GLOBAL_INT_MASK_INT_D		(1 << 12)
1044605d28eSkettenis #define PCIE_ARCACHE_TRC	0x8050
1054605d28eSkettenis #define  PCIE_ARCACHE_TRC_DEFAULT		0x3511
1064605d28eSkettenis #define PCIE_AWCACHE_TRC	0x8054
1074605d28eSkettenis #define  PCIE_AWCACHE_TRC_DEFAULT		0x5311
1084605d28eSkettenis #define PCIE_ARUSER		0x805c
1094605d28eSkettenis #define PCIE_AWUSER		0x8060
1104605d28eSkettenis #define  PCIE_AXUSER_DOMAIN_MASK		(0x3 << 4)
1114605d28eSkettenis #define  PCIE_AXUSER_DOMAIN_INNER_SHARABLE	(0x1 << 4)
1124605d28eSkettenis #define  PCIE_AXUSER_DOMAIN_OUTER_SHARABLE	(0x2 << 4)
113eae9bc5eSpatrick #define PCIE_STREAMID		0x8064
114eae9bc5eSpatrick #define  PCIE_STREAMID_FUNC_BITS(x)		((x) << 0)
115eae9bc5eSpatrick #define  PCIE_STREAMID_DEV_BITS(x)		((x) << 4)
116eae9bc5eSpatrick #define  PCIE_STREAMID_BUS_BITS(x)		((x) << 8)
117eae9bc5eSpatrick #define  PCIE_STREAMID_ROOTPORT(x)		((x) << 12)
118eae9bc5eSpatrick #define  PCIE_STREAMID_8040			\
119eae9bc5eSpatrick     (PCIE_STREAMID_ROOTPORT(0x80) | PCIE_STREAMID_BUS_BITS(2) | \
120eae9bc5eSpatrick      PCIE_STREAMID_DEV_BITS(2) | PCIE_STREAMID_FUNC_BITS(3))
1217659d2d7Skettenis 
122c244e331Skettenis /* Amlogic G12A registers */
123c244e331Skettenis #define PCIE_CFG0		0x0000
124c244e331Skettenis #define  PCIE_CFG0_APP_LTSSM_EN			(1 << 7)
125c244e331Skettenis #define PCIE_STATUS12		0x0030
126c244e331Skettenis #define  PCIE_STATUS12_RDLH_LINK_UP		(1 << 16)
127c244e331Skettenis #define  PCIE_STATUS12_LTSSM_MASK		(0x1f << 10)
128c244e331Skettenis #define  PCIE_STATUS12_LTSSM_UP			(0x11 << 10)
129c244e331Skettenis #define  PCIE_STATUS12_SMLH_LINK_UP		(1 << 6)
130c244e331Skettenis 
131c244e331Skettenis /* NXP i.MX8MQ registers */
1326a2b914cSpatrick #define PCIE_RC_LCR				0x7c
1336a2b914cSpatrick #define  PCIE_RC_LCR_MAX_LINK_SPEEDS_GEN1		0x1
1346a2b914cSpatrick #define  PCIE_RC_LCR_MAX_LINK_SPEEDS_GEN2		0x2
1356a2b914cSpatrick #define  PCIE_RC_LCR_MAX_LINK_SPEEDS_MASK		0xf
1366a2b914cSpatrick #define  PCIE_RC_LCR_L1EL_MASK				(0x7 << 15)
1376a2b914cSpatrick #define  PCIE_RC_LCR_L1EL_64US				(0x6 << 15)
1386a2b914cSpatrick 
1396a2b914cSpatrick #define IOMUXC_GPR12				0x30
1406a2b914cSpatrick #define  IMX8MQ_GPR_PCIE2_DEVICE_TYPE_MASK		(0xf << 8)
1416a2b914cSpatrick #define  IMX8MQ_GPR_PCIE2_DEVICE_TYPE_RC		(0x4 << 8)
1426a2b914cSpatrick #define  IMX8MQ_GPR_PCIE1_DEVICE_TYPE_MASK		(0xf << 12)
1436a2b914cSpatrick #define  IMX8MQ_GPR_PCIE1_DEVICE_TYPE_RC		(0x4 << 12)
1446a2b914cSpatrick #define IOMUXC_GPR14				0x38
1456a2b914cSpatrick #define IOMUXC_GPR16				0x40
1466a2b914cSpatrick #define  IMX8MQ_GPR_PCIE_REF_USE_PAD			(1 << 9)
1476a2b914cSpatrick #define  IMX8MQ_GPR_PCIE_CLK_REQ_OVERRIDE_EN		(1 << 10)
1486a2b914cSpatrick #define  IMX8MQ_GPR_PCIE_CLK_REQ_OVERRIDE		(1 << 11)
1491ad6abbaSpatrick #define  IMX8MM_GPR_PCIE_SSC_EN				(1 << 16)
1501ad6abbaSpatrick #define  IMX8MM_GPR_PCIE_POWER_OFF			(1 << 17)
1511ad6abbaSpatrick #define  IMX8MM_GPR_PCIE_CMN_RST			(1 << 18)
1521ad6abbaSpatrick #define  IMX8MM_GPR_PCIE_AUX_EN				(1 << 19)
1531ad6abbaSpatrick #define  IMX8MM_GPR_PCIE_REF_CLK_MASK			(0x3 << 24)
1541ad6abbaSpatrick #define  IMX8MM_GPR_PCIE_REF_CLK_PLL			(0x3 << 24)
1551ad6abbaSpatrick #define  IMX8MM_GPR_PCIE_REF_CLK_EXT			(0x2 << 24)
1561ad6abbaSpatrick 
1571ad6abbaSpatrick #define IMX8MM_PCIE_PHY_CMN_REG62			0x188
1581ad6abbaSpatrick #define  IMX8MM_PCIE_PHY_CMN_REG62_PLL_CLK_OUT			0x08
1591ad6abbaSpatrick #define IMX8MM_PCIE_PHY_CMN_REG64			0x190
1601ad6abbaSpatrick #define  IMX8MM_PCIE_PHY_CMN_REG64_AUX_RX_TX_TERM		0x8c
1611ad6abbaSpatrick #define IMX8MM_PCIE_PHY_CMN_REG75			0x1d4
1621ad6abbaSpatrick #define  IMX8MM_PCIE_PHY_CMN_REG75_PLL_DONE			0x3
1631ad6abbaSpatrick #define IMX8MM_PCIE_PHY_TRSV_REG5			0x414
1641ad6abbaSpatrick #define  IMX8MM_PCIE_PHY_TRSV_REG5_GEN1_DEEMP			0x2d
1651ad6abbaSpatrick #define IMX8MM_PCIE_PHY_TRSV_REG6			0x418
1661ad6abbaSpatrick #define  IMX8MM_PCIE_PHY_TRSV_REG6_GEN2_DEEMP			0xf
1676a2b914cSpatrick 
1686a2b914cSpatrick #define ANATOP_PLLOUT_CTL			0x74
1696a2b914cSpatrick #define  ANATOP_PLLOUT_CTL_CKE				(1 << 4)
1706a2b914cSpatrick #define  ANATOP_PLLOUT_CTL_SEL_SYSPLL1			0xb
1716a2b914cSpatrick #define  ANATOP_PLLOUT_CTL_SEL_MASK			0xf
1726a2b914cSpatrick #define ANATOP_PLLOUT_DIV			0x7c
1736a2b914cSpatrick #define  ANATOP_PLLOUT_DIV_SYSPLL1			0x7
1746a2b914cSpatrick 
1755f800b9bSkettenis /* Rockchip RK3568/RK3588 registers */
176f98fd054Skettenis #define PCIE_CLIENT_GENERAL_CON			0x0000
177f98fd054Skettenis #define  PCIE_CLIENT_DEV_TYPE_RC		((0xf << 4) << 16 | (0x4 << 4))
178f98fd054Skettenis #define  PCIE_CLIENT_LINK_REQ_RST_GRT		((1 << 3) << 16 | (1 << 3))
179f98fd054Skettenis #define  PCIE_CLIENT_APP_LTSSM_ENABLE		((1 << 2) << 16 | (1 << 2))
1805f800b9bSkettenis #define PCIE_CLIENT_INTR_STATUS_LEGACY		0x0008
1815f800b9bSkettenis #define PCIE_CLIENT_INTR_MASK_LEGACY		0x001c
182f98fd054Skettenis #define PCIE_CLIENT_HOT_RESET_CTRL		0x0180
183f98fd054Skettenis #define  PCIE_CLIENT_APP_LTSSM_ENABLE_ENHANCE	((1 << 4) << 16 | (1 << 4))
184f98fd054Skettenis #define PCIE_CLIENT_LTSSM_STATUS		0x0300
185f98fd054Skettenis #define  PCIE_CLIENT_RDLH_LINK_UP		(1 << 17)
186f98fd054Skettenis #define  PCIE_CLIENT_SMLH_LINK_UP		(1 << 16)
187f98fd054Skettenis #define  PCIE_CLIENT_LTSSM_MASK			(0x1f << 0)
188f98fd054Skettenis #define  PCIE_CLIENT_LTSSM_UP			(0x11 << 0)
189f98fd054Skettenis 
1907659d2d7Skettenis #define HREAD4(sc, reg)							\
1917659d2d7Skettenis 	(bus_space_read_4((sc)->sc_iot, (sc)->sc_ioh, (reg)))
1927659d2d7Skettenis #define HWRITE4(sc, reg, val)						\
1937659d2d7Skettenis 	bus_space_write_4((sc)->sc_iot, (sc)->sc_ioh, (reg), (val))
1947659d2d7Skettenis #define HSET4(sc, reg, bits)						\
1957659d2d7Skettenis 	HWRITE4((sc), (reg), HREAD4((sc), (reg)) | (bits))
1967659d2d7Skettenis #define HCLR4(sc, reg, bits)						\
1977659d2d7Skettenis 	HWRITE4((sc), (reg), HREAD4((sc), (reg)) & ~(bits))
1987659d2d7Skettenis 
1994605d28eSkettenis struct dwpcie_range {
2004605d28eSkettenis 	uint32_t		flags;
2014605d28eSkettenis 	uint64_t		pci_base;
2024605d28eSkettenis 	uint64_t		phys_base;
2034605d28eSkettenis 	uint64_t		size;
2044605d28eSkettenis };
2054605d28eSkettenis 
2065f800b9bSkettenis struct dwpcie_intx {
2075f800b9bSkettenis 	int			(*di_func)(void *);
2085f800b9bSkettenis 	void			*di_arg;
2095f800b9bSkettenis 	int			di_ipl;
2105f800b9bSkettenis 	int			di_flags;
2115f800b9bSkettenis 	int			di_pin;
2125f800b9bSkettenis 	struct evcount		di_count;
2135f800b9bSkettenis 	char			*di_name;
2145f800b9bSkettenis 	struct dwpcie_softc	*di_sc;
2155f800b9bSkettenis 	TAILQ_ENTRY(dwpcie_intx) di_next;
2165f800b9bSkettenis };
2175f800b9bSkettenis 
2189a76443bSkettenis #define DWPCIE_MAX_MSI		64
219d5dfbc11Skettenis 
220d5dfbc11Skettenis struct dwpcie_msi {
221d5dfbc11Skettenis 	int			(*dm_func)(void *);
222d5dfbc11Skettenis 	void			*dm_arg;
223d5dfbc11Skettenis 	int			dm_ipl;
224d5dfbc11Skettenis 	int			dm_flags;
225d5dfbc11Skettenis 	int			dm_vec;
2269a76443bSkettenis 	int			dm_nvec;
227d5dfbc11Skettenis 	struct evcount		dm_count;
228d5dfbc11Skettenis 	char			*dm_name;
229d5dfbc11Skettenis };
230d5dfbc11Skettenis 
2317659d2d7Skettenis struct dwpcie_softc {
2327659d2d7Skettenis 	struct device		sc_dev;
2337659d2d7Skettenis 	bus_space_tag_t		sc_iot;
2347659d2d7Skettenis 	bus_space_handle_t	sc_ioh;
23531876e8bSkettenis 	bus_dma_tag_t		sc_dmat;
2366a2b914cSpatrick 
23731876e8bSkettenis 	bus_addr_t		sc_ctrl_base;
23831876e8bSkettenis 	bus_size_t		sc_ctrl_size;
23901dd2b7cSkettenis 
24001dd2b7cSkettenis 	bus_addr_t		sc_conf_base;
24101dd2b7cSkettenis 	bus_size_t		sc_conf_size;
24201dd2b7cSkettenis 	bus_space_handle_t	sc_conf_ioh;
243c244e331Skettenis 
244c244e331Skettenis 	bus_addr_t		sc_glue_base;
245c244e331Skettenis 	bus_size_t		sc_glue_size;
246c244e331Skettenis 	bus_space_handle_t	sc_glue_ioh;
247c244e331Skettenis 
248a848a3cbSpatrick 	bus_addr_t		sc_atu_base;
249a848a3cbSpatrick 	bus_size_t		sc_atu_size;
250a848a3cbSpatrick 	bus_space_handle_t	sc_atu_ioh;
251a848a3cbSpatrick 
2526a2b914cSpatrick 	bus_addr_t		sc_io_base;
2536a2b914cSpatrick 	bus_addr_t		sc_io_bus_addr;
2546a2b914cSpatrick 	bus_size_t		sc_io_size;
2556a2b914cSpatrick 	bus_addr_t		sc_mem_base;
2566a2b914cSpatrick 	bus_addr_t		sc_mem_bus_addr;
2576a2b914cSpatrick 	bus_size_t		sc_mem_size;
258d2397242Skettenis 	bus_addr_t		sc_pmem_base;
259d2397242Skettenis 	bus_addr_t		sc_pmem_bus_addr;
260d2397242Skettenis 	bus_size_t		sc_pmem_size;
2617659d2d7Skettenis 
2624605d28eSkettenis 	int			sc_node;
2637659d2d7Skettenis 	int			sc_acells;
2647659d2d7Skettenis 	int			sc_scells;
2657659d2d7Skettenis 	int			sc_pacells;
2667659d2d7Skettenis 	int			sc_pscells;
2674605d28eSkettenis 	struct dwpcie_range	*sc_ranges;
268d43be508Skettenis 	int			sc_nranges;
2697659d2d7Skettenis 
27071fe361bSkettenis 	struct bus_space	sc_bus_iot;
27171fe361bSkettenis 	struct bus_space	sc_bus_memt;
27271fe361bSkettenis 
2732b0be198Skettenis 	struct machine_pci_chipset sc_pc;
2747659d2d7Skettenis 	int			sc_bus;
275f2c2f061Skettenis 
2766a2b914cSpatrick 	int			sc_num_viewport;
2776a2b914cSpatrick 	int			sc_atu_unroll;
278a848a3cbSpatrick 	int			sc_atu_viewport;
2796a2b914cSpatrick 
280f2c2f061Skettenis 	void			*sc_ih;
2815f800b9bSkettenis 	struct interrupt_controller sc_ic;
2825f800b9bSkettenis 	TAILQ_HEAD(,dwpcie_intx) sc_intx[4];
283d5dfbc11Skettenis 
2849a76443bSkettenis 	void			*sc_msi_ih[2];
285d5dfbc11Skettenis 	uint64_t		sc_msi_addr;
2869a76443bSkettenis 	uint64_t		sc_msi_mask;
2879a76443bSkettenis 	struct dwpcie_msi	sc_msi[DWPCIE_MAX_MSI];
2889a76443bSkettenis 	int			sc_num_msi;
2897659d2d7Skettenis };
2907659d2d7Skettenis 
291cc0ede06Spatrick struct dwpcie_intr_handle {
2922b0be198Skettenis 	struct machine_intr_handle pih_ih;
293d5dfbc11Skettenis 	struct dwpcie_softc	*pih_sc;
294d5dfbc11Skettenis 	struct dwpcie_msi	*pih_dm;
295cc0ede06Spatrick 	bus_dma_tag_t		pih_dmat;
296cc0ede06Spatrick 	bus_dmamap_t		pih_map;
297cc0ede06Spatrick };
298cc0ede06Spatrick 
2997659d2d7Skettenis int dwpcie_match(struct device *, void *, void *);
3007659d2d7Skettenis void dwpcie_attach(struct device *, struct device *, void *);
3017659d2d7Skettenis 
3029fdf0c62Smpi const struct cfattach	dwpcie_ca = {
3037659d2d7Skettenis 	sizeof (struct dwpcie_softc), dwpcie_match, dwpcie_attach
3047659d2d7Skettenis };
3057659d2d7Skettenis 
3067659d2d7Skettenis struct cfdriver dwpcie_cd = {
3077659d2d7Skettenis 	NULL, "dwpcie", DV_DULL
3087659d2d7Skettenis };
3097659d2d7Skettenis 
3107659d2d7Skettenis int
3117659d2d7Skettenis dwpcie_match(struct device *parent, void *match, void *aux)
3127659d2d7Skettenis {
3137659d2d7Skettenis 	struct fdt_attach_args *faa = aux;
3147659d2d7Skettenis 
31508f55413Skn 	return (OF_is_compatible(faa->fa_node, "amlogic,g12a-pcie") ||
31608f55413Skn 	    OF_is_compatible(faa->fa_node, "baikal,bm1000-pcie") ||
3171ad6abbaSpatrick 	    OF_is_compatible(faa->fa_node, "fsl,imx8mm-pcie") ||
318919f025aSkettenis 	    OF_is_compatible(faa->fa_node, "fsl,imx8mq-pcie") ||
3195f3e3118Skettenis 	    OF_is_compatible(faa->fa_node, "marvell,armada8k-pcie") ||
320a848a3cbSpatrick 	    OF_is_compatible(faa->fa_node, "qcom,pcie-sc8280xp") ||
321c53ff863Spatrick 	    OF_is_compatible(faa->fa_node, "qcom,pcie-x1e80100") ||
3225f3e3118Skettenis 	    OF_is_compatible(faa->fa_node, "rockchip,rk3568-pcie") ||
3235f800b9bSkettenis 	    OF_is_compatible(faa->fa_node, "rockchip,rk3588-pcie") ||
324919f025aSkettenis 	    OF_is_compatible(faa->fa_node, "sifive,fu740-pcie"));
3257659d2d7Skettenis }
3267659d2d7Skettenis 
32731876e8bSkettenis void	dwpcie_attach_deferred(struct device *);
32831876e8bSkettenis 
3295f3e3118Skettenis void	dwpcie_atu_disable(struct dwpcie_softc *, int);
3306a2b914cSpatrick void	dwpcie_atu_config(struct dwpcie_softc *, int, int,
3316a2b914cSpatrick 	    uint64_t, uint64_t, uint64_t);
3326a2b914cSpatrick void	dwpcie_link_config(struct dwpcie_softc *);
3336a2b914cSpatrick int	dwpcie_link_up(struct dwpcie_softc *);
3346a2b914cSpatrick 
335c3f9cac9Spatrick int	dwpcie_armada8k_init(struct dwpcie_softc *);
3364605d28eSkettenis int	dwpcie_armada8k_link_up(struct dwpcie_softc *);
337f2c2f061Skettenis int	dwpcie_armada8k_intr(void *);
3387659d2d7Skettenis 
339c244e331Skettenis int	dwpcie_g12a_init(struct dwpcie_softc *);
340c244e331Skettenis int	dwpcie_g12a_link_up(struct dwpcie_softc *);
341c244e331Skettenis 
342c3f9cac9Spatrick int	dwpcie_imx8mq_init(struct dwpcie_softc *);
3436a2b914cSpatrick int	dwpcie_imx8mq_intr(void *);
3446a2b914cSpatrick 
345d2397242Skettenis int	dwpcie_fu740_init(struct dwpcie_softc *);
3465f800b9bSkettenis 
3475f3e3118Skettenis int	dwpcie_rk3568_init(struct dwpcie_softc *);
3485f800b9bSkettenis int	dwpcie_rk3568_intr(void *);
3495f800b9bSkettenis void	*dwpcie_rk3568_intr_establish(void *, int *, int,
3505f800b9bSkettenis  	    struct cpu_info *, int (*)(void *), void *, char *);
3515f800b9bSkettenis void	dwpcie_rk3568_intr_disestablish(void *);
3525f800b9bSkettenis void	dwpcie_rk3568_intr_barrier(void *);
3535f800b9bSkettenis 
354a848a3cbSpatrick int	dwpcie_sc8280xp_init(struct dwpcie_softc *);
355d2397242Skettenis 
3567659d2d7Skettenis void	dwpcie_attach_hook(struct device *, struct device *,
3577659d2d7Skettenis 	    struct pcibus_attach_args *);
3587659d2d7Skettenis int	dwpcie_bus_maxdevs(void *, int);
3597659d2d7Skettenis pcitag_t dwpcie_make_tag(void *, int, int, int);
3607659d2d7Skettenis void	dwpcie_decompose_tag(void *, pcitag_t, int *, int *, int *);
3617659d2d7Skettenis int	dwpcie_conf_size(void *, pcitag_t);
3627659d2d7Skettenis pcireg_t dwpcie_conf_read(void *, pcitag_t, int);
3637659d2d7Skettenis void	dwpcie_conf_write(void *, pcitag_t, int, pcireg_t);
364619b146dSpatrick int	dwpcie_probe_device_hook(void *, struct pci_attach_args *);
3657659d2d7Skettenis 
3667659d2d7Skettenis int	dwpcie_intr_map(struct pci_attach_args *, pci_intr_handle_t *);
3677659d2d7Skettenis const char *dwpcie_intr_string(void *, pci_intr_handle_t);
3687659d2d7Skettenis void	*dwpcie_intr_establish(void *, pci_intr_handle_t, int,
369d67371fdSpatrick 	    struct cpu_info *, int (*)(void *), void *, char *);
3707659d2d7Skettenis void	dwpcie_intr_disestablish(void *, void *);
3717659d2d7Skettenis 
37271fe361bSkettenis int	dwpcie_bs_iomap(bus_space_tag_t, bus_addr_t, bus_size_t, int,
37371fe361bSkettenis 	    bus_space_handle_t *);
37471fe361bSkettenis int	dwpcie_bs_memmap(bus_space_tag_t, bus_addr_t, bus_size_t, int,
37571fe361bSkettenis 	    bus_space_handle_t *);
37671fe361bSkettenis 
377cc0ede06Spatrick struct interrupt_controller dwpcie_ic = {
378cc0ede06Spatrick 	.ic_barrier = intr_barrier
379cc0ede06Spatrick };
380cc0ede06Spatrick 
3817659d2d7Skettenis void
3827659d2d7Skettenis dwpcie_attach(struct device *parent, struct device *self, void *aux)
3837659d2d7Skettenis {
3847659d2d7Skettenis 	struct dwpcie_softc *sc = (struct dwpcie_softc *)self;
3857659d2d7Skettenis 	struct fdt_attach_args *faa = aux;
3864605d28eSkettenis 	uint32_t *ranges;
3874605d28eSkettenis 	int i, j, nranges, rangeslen;
388a848a3cbSpatrick 	int atu, config, ctrl, glue;
3897659d2d7Skettenis 
3907659d2d7Skettenis 	if (faa->fa_nreg < 2) {
3917659d2d7Skettenis 		printf(": no registers\n");
3927659d2d7Skettenis 		return;
3937659d2d7Skettenis 	}
3947659d2d7Skettenis 
395c244e331Skettenis 	sc->sc_ctrl_base = faa->fa_reg[0].addr;
396c244e331Skettenis 	sc->sc_ctrl_size = faa->fa_reg[0].size;
397c244e331Skettenis 
398a848a3cbSpatrick 	ctrl = OF_getindex(faa->fa_node, "dbi", "reg-names");
399a848a3cbSpatrick 	if (ctrl >= 0 && ctrl < faa->fa_nreg) {
400a848a3cbSpatrick 		sc->sc_ctrl_base = faa->fa_reg[ctrl].addr;
401a848a3cbSpatrick 		sc->sc_ctrl_size = faa->fa_reg[ctrl].size;
402a848a3cbSpatrick 	}
403a848a3cbSpatrick 
404c244e331Skettenis 	config = OF_getindex(faa->fa_node, "config", "reg-names");
405c244e331Skettenis 	if (config < 0 || config >= faa->fa_nreg) {
406c244e331Skettenis 		printf(": no config registers\n");
407c244e331Skettenis 		return;
408c244e331Skettenis 	}
409c244e331Skettenis 
41001dd2b7cSkettenis 	sc->sc_conf_base = faa->fa_reg[config].addr;
41101dd2b7cSkettenis 	sc->sc_conf_size = faa->fa_reg[config].size;
412c244e331Skettenis 
413a848a3cbSpatrick 	sc->sc_atu_base = sc->sc_ctrl_base + 0x300000;
414a848a3cbSpatrick 	sc->sc_atu_size = sc->sc_ctrl_size - 0x300000;
415a848a3cbSpatrick 
416a848a3cbSpatrick 	atu = OF_getindex(faa->fa_node, "atu", "reg-names");
417a848a3cbSpatrick 	if (atu >= 0 && atu < faa->fa_nreg) {
418a848a3cbSpatrick 		sc->sc_atu_base = faa->fa_reg[atu].addr;
419a848a3cbSpatrick 		sc->sc_atu_size = faa->fa_reg[atu].size;
420a848a3cbSpatrick 	}
421a848a3cbSpatrick 
422c244e331Skettenis 	if (OF_is_compatible(faa->fa_node, "amlogic,g12a-pcie")) {
423c244e331Skettenis 		glue = OF_getindex(faa->fa_node, "cfg", "reg-names");
424c244e331Skettenis 		if (glue < 0 || glue >= faa->fa_nreg) {
425c244e331Skettenis 			printf(": no glue registers\n");
426c244e331Skettenis 			return;
427c244e331Skettenis 		}
428c244e331Skettenis 
429c244e331Skettenis 		sc->sc_glue_base = faa->fa_reg[glue].addr;
430c244e331Skettenis 		sc->sc_glue_size = faa->fa_reg[glue].size;
431c244e331Skettenis 	}
432c244e331Skettenis 
4335f800b9bSkettenis 	if (OF_is_compatible(faa->fa_node, "rockchip,rk3568-pcie") ||
4345f800b9bSkettenis 	    OF_is_compatible(faa->fa_node, "rockchip,rk3588-pcie")) {
435f98fd054Skettenis 		glue = OF_getindex(faa->fa_node, "apb", "reg-names");
436f98fd054Skettenis 		if (glue < 0 || glue >= faa->fa_nreg) {
437f98fd054Skettenis 			printf(": no glue registers\n");
438f98fd054Skettenis 			return;
439f98fd054Skettenis 		}
440f98fd054Skettenis 
441f98fd054Skettenis 		sc->sc_glue_base = faa->fa_reg[glue].addr;
442f98fd054Skettenis 		sc->sc_glue_size = faa->fa_reg[glue].size;
443f98fd054Skettenis 	}
444f98fd054Skettenis 
4457659d2d7Skettenis 	sc->sc_iot = faa->fa_iot;
44631876e8bSkettenis 	sc->sc_dmat = faa->fa_dmat;
4474605d28eSkettenis 	sc->sc_node = faa->fa_node;
4487659d2d7Skettenis 
4497659d2d7Skettenis 	sc->sc_acells = OF_getpropint(sc->sc_node, "#address-cells",
4507659d2d7Skettenis 	    faa->fa_acells);
4517659d2d7Skettenis 	sc->sc_scells = OF_getpropint(sc->sc_node, "#size-cells",
4527659d2d7Skettenis 	    faa->fa_scells);
4537659d2d7Skettenis 	sc->sc_pacells = faa->fa_acells;
4547659d2d7Skettenis 	sc->sc_pscells = faa->fa_scells;
4557659d2d7Skettenis 
4564605d28eSkettenis 	rangeslen = OF_getproplen(sc->sc_node, "ranges");
4574605d28eSkettenis 	if (rangeslen <= 0 || (rangeslen % sizeof(uint32_t)) ||
4584605d28eSkettenis 	     (rangeslen / sizeof(uint32_t)) % (sc->sc_acells +
4594605d28eSkettenis 	     sc->sc_pacells + sc->sc_scells)) {
4604605d28eSkettenis 		printf(": invalid ranges property\n");
4614605d28eSkettenis 		return;
4624605d28eSkettenis 	}
4634605d28eSkettenis 
4644605d28eSkettenis 	ranges = malloc(rangeslen, M_TEMP, M_WAITOK);
4654605d28eSkettenis 	OF_getpropintarray(sc->sc_node, "ranges", ranges,
4664605d28eSkettenis 	    rangeslen);
4674605d28eSkettenis 
4684605d28eSkettenis 	nranges = (rangeslen / sizeof(uint32_t)) /
4694605d28eSkettenis 	    (sc->sc_acells + sc->sc_pacells + sc->sc_scells);
4704605d28eSkettenis 	sc->sc_ranges = mallocarray(nranges,
4714605d28eSkettenis 	    sizeof(struct dwpcie_range), M_TEMP, M_WAITOK);
472d43be508Skettenis 	sc->sc_nranges = nranges;
4734605d28eSkettenis 
474d43be508Skettenis 	for (i = 0, j = 0; i < sc->sc_nranges; i++) {
4754605d28eSkettenis 		sc->sc_ranges[i].flags = ranges[j++];
4764605d28eSkettenis 		sc->sc_ranges[i].pci_base = ranges[j++];
4774605d28eSkettenis 		if (sc->sc_acells - 1 == 2) {
4784605d28eSkettenis 			sc->sc_ranges[i].pci_base <<= 32;
4794605d28eSkettenis 			sc->sc_ranges[i].pci_base |= ranges[j++];
4804605d28eSkettenis 		}
4814605d28eSkettenis 		sc->sc_ranges[i].phys_base = ranges[j++];
4824605d28eSkettenis 		if (sc->sc_pacells == 2) {
4834605d28eSkettenis 			sc->sc_ranges[i].phys_base <<= 32;
4844605d28eSkettenis 			sc->sc_ranges[i].phys_base |= ranges[j++];
4854605d28eSkettenis 		}
4864605d28eSkettenis 		sc->sc_ranges[i].size = ranges[j++];
4874605d28eSkettenis 		if (sc->sc_scells == 2) {
4884605d28eSkettenis 			sc->sc_ranges[i].size <<= 32;
4894605d28eSkettenis 			sc->sc_ranges[i].size |= ranges[j++];
4904605d28eSkettenis 		}
4914605d28eSkettenis 	}
4924605d28eSkettenis 
4934605d28eSkettenis 	free(ranges, M_TEMP, rangeslen);
4944605d28eSkettenis 
49531876e8bSkettenis 	if (bus_space_map(sc->sc_iot, sc->sc_ctrl_base,
49631876e8bSkettenis 	    sc->sc_ctrl_size, 0, &sc->sc_ioh)) {
497f9087cb3Spatrick 		free(sc->sc_ranges, M_TEMP, sc->sc_nranges *
498f9087cb3Spatrick 		    sizeof(struct dwpcie_range));
4997659d2d7Skettenis 		printf(": can't map ctrl registers\n");
5007659d2d7Skettenis 		return;
5017659d2d7Skettenis 	}
5027659d2d7Skettenis 
50301dd2b7cSkettenis 	if (bus_space_map(sc->sc_iot, sc->sc_conf_base,
50401dd2b7cSkettenis 	    sc->sc_conf_size, 0, &sc->sc_conf_ioh)) {
50531876e8bSkettenis 		bus_space_unmap(sc->sc_iot, sc->sc_ioh, sc->sc_ctrl_size);
506f9087cb3Spatrick 		free(sc->sc_ranges, M_TEMP, sc->sc_nranges *
507f9087cb3Spatrick 		    sizeof(struct dwpcie_range));
5086a2b914cSpatrick 		printf(": can't map config registers\n");
5096a2b914cSpatrick 		return;
5106a2b914cSpatrick 	}
5116a2b914cSpatrick 
5126a2b914cSpatrick 	sc->sc_num_viewport = OF_getpropint(sc->sc_node, "num-viewport", 2);
5137659d2d7Skettenis 
5147659d2d7Skettenis 	printf("\n");
5157659d2d7Skettenis 
5164605d28eSkettenis 	pinctrl_byname(sc->sc_node, "default");
5176a2b914cSpatrick 	clock_set_assigned(sc->sc_node);
5187659d2d7Skettenis 
51931876e8bSkettenis 	config_defer(self, dwpcie_attach_deferred);
52031876e8bSkettenis }
52131876e8bSkettenis 
52231876e8bSkettenis void
52331876e8bSkettenis dwpcie_attach_deferred(struct device *self)
52431876e8bSkettenis {
52531876e8bSkettenis 	struct dwpcie_softc *sc = (struct dwpcie_softc *)self;
52631876e8bSkettenis 	struct pcibus_attach_args pba;
52731876e8bSkettenis 	bus_addr_t iobase, iolimit;
52831876e8bSkettenis 	bus_addr_t membase, memlimit;
529d2397242Skettenis 	bus_addr_t pmembase, pmemlimit;
53031876e8bSkettenis 	uint32_t bus_range[2];
53131876e8bSkettenis 	pcireg_t bir, blr, csr;
53231876e8bSkettenis 	int i, error = 0;
53331876e8bSkettenis 
5344605d28eSkettenis 	if (OF_is_compatible(sc->sc_node, "marvell,armada8k-pcie"))
535c3f9cac9Spatrick 		error = dwpcie_armada8k_init(sc);
536c244e331Skettenis 	if (OF_is_compatible(sc->sc_node, "amlogic,g12a-pcie"))
537c244e331Skettenis 		error = dwpcie_g12a_init(sc);
5381ad6abbaSpatrick 	if (OF_is_compatible(sc->sc_node, "fsl,imx8mm-pcie") ||
5391ad6abbaSpatrick 	    OF_is_compatible(sc->sc_node, "fsl,imx8mq-pcie"))
540c3f9cac9Spatrick 		error = dwpcie_imx8mq_init(sc);
541c53ff863Spatrick 	if (OF_is_compatible(sc->sc_node, "qcom,pcie-sc8280xp") ||
542c53ff863Spatrick 	    OF_is_compatible(sc->sc_node, "qcom,pcie-x1e80100"))
543a848a3cbSpatrick 		error = dwpcie_sc8280xp_init(sc);
5445f800b9bSkettenis 	if (OF_is_compatible(sc->sc_node, "rockchip,rk3568-pcie") ||
5455f800b9bSkettenis 	    OF_is_compatible(sc->sc_node, "rockchip,rk3588-pcie"))
5465f3e3118Skettenis 		error = dwpcie_rk3568_init(sc);
547d2397242Skettenis 	if (OF_is_compatible(sc->sc_node, "sifive,fu740-pcie"))
548d2397242Skettenis 		error = dwpcie_fu740_init(sc);
549c3f9cac9Spatrick 	if (error != 0) {
55001dd2b7cSkettenis 		bus_space_unmap(sc->sc_iot, sc->sc_conf_ioh, sc->sc_conf_size);
55131876e8bSkettenis 		bus_space_unmap(sc->sc_iot, sc->sc_ioh, sc->sc_ctrl_size);
552c3f9cac9Spatrick 		free(sc->sc_ranges, M_TEMP, sc->sc_nranges *
553c3f9cac9Spatrick 		    sizeof(struct dwpcie_range));
554c3f9cac9Spatrick 		printf("%s: can't initialize hardware\n",
555c3f9cac9Spatrick 		    sc->sc_dev.dv_xname);
556c3f9cac9Spatrick 		return;
557c3f9cac9Spatrick 	}
5586a2b914cSpatrick 
559a848a3cbSpatrick 	sc->sc_atu_viewport = -1;
5606a2b914cSpatrick 	if (HREAD4(sc, IATU_VIEWPORT) == 0xffffffff) {
5616a2b914cSpatrick 		sc->sc_atu_unroll = 1;
562a848a3cbSpatrick 		if (bus_space_map(sc->sc_iot, sc->sc_atu_base,
563a848a3cbSpatrick 		    sc->sc_atu_size, 0, &sc->sc_atu_ioh)) {
564a848a3cbSpatrick 			bus_space_unmap(sc->sc_iot, sc->sc_conf_ioh,
565a848a3cbSpatrick 			    sc->sc_conf_size);
566a848a3cbSpatrick 			bus_space_unmap(sc->sc_iot, sc->sc_ioh,
567a848a3cbSpatrick 			    sc->sc_ctrl_size);
568a848a3cbSpatrick 			free(sc->sc_ranges, M_TEMP, sc->sc_nranges *
569a848a3cbSpatrick 			    sizeof(struct dwpcie_range));
570a848a3cbSpatrick 			printf("%s: can't map atu registers\n",
571a848a3cbSpatrick 			    sc->sc_dev.dv_xname);
572a848a3cbSpatrick 			return;
573a848a3cbSpatrick 		}
5746a2b914cSpatrick 	}
5756a2b914cSpatrick 
5766a2b914cSpatrick 	/* Set up address translation for I/O space. */
5776a2b914cSpatrick 	for (i = 0; i < sc->sc_nranges; i++) {
5786a2b914cSpatrick 		if ((sc->sc_ranges[i].flags & 0x03000000) == 0x01000000 &&
5796a2b914cSpatrick 		    sc->sc_ranges[i].size > 0) {
5806a2b914cSpatrick 			sc->sc_io_base = sc->sc_ranges[i].phys_base;
5816a2b914cSpatrick 			sc->sc_io_bus_addr = sc->sc_ranges[i].pci_base;
5826a2b914cSpatrick 			sc->sc_io_size = sc->sc_ranges[i].size;
5836a2b914cSpatrick 		}
5846a2b914cSpatrick 		if ((sc->sc_ranges[i].flags & 0x03000000) == 0x02000000 &&
5856a2b914cSpatrick 		    sc->sc_ranges[i].size > 0) {
5866a2b914cSpatrick 			sc->sc_mem_base = sc->sc_ranges[i].phys_base;
5876a2b914cSpatrick 			sc->sc_mem_bus_addr = sc->sc_ranges[i].pci_base;
5886a2b914cSpatrick 			sc->sc_mem_size = sc->sc_ranges[i].size;
5896a2b914cSpatrick 		}
590d2397242Skettenis 		if ((sc->sc_ranges[i].flags & 0x03000000) == 0x03000000 &&
591d2397242Skettenis 		    sc->sc_ranges[i].size > 0) {
592d2397242Skettenis 			sc->sc_pmem_base = sc->sc_ranges[i].phys_base;
593d2397242Skettenis 			sc->sc_pmem_bus_addr = sc->sc_ranges[i].pci_base;
594d2397242Skettenis 			sc->sc_pmem_size = sc->sc_ranges[i].size;
5956a2b914cSpatrick 		}
596d2397242Skettenis 	}
597d2397242Skettenis 	if (sc->sc_mem_size == 0) {
598d2397242Skettenis 		printf("%s: no memory mapped I/O window\n",
599d2397242Skettenis 		    sc->sc_dev.dv_xname);
600d2397242Skettenis 		return;
601d2397242Skettenis 	}
602d2397242Skettenis 
603d2397242Skettenis 	/*
604d2397242Skettenis 	 * Disable prefetchable memory mapped I/O window if we don't
605d2397242Skettenis 	 * have enough viewports to enable it.
606d2397242Skettenis 	 */
607d2397242Skettenis 	if (sc->sc_num_viewport < 4)
608d2397242Skettenis 		sc->sc_pmem_size = 0;
6096a2b914cSpatrick 
6105f3e3118Skettenis 	for (i = 0; i < sc->sc_num_viewport; i++)
6115f3e3118Skettenis 		dwpcie_atu_disable(sc, i);
6125f3e3118Skettenis 
6136a2b914cSpatrick 	dwpcie_atu_config(sc, IATU_VIEWPORT_INDEX0,
6146a2b914cSpatrick 	    IATU_REGION_CTRL_1_TYPE_MEM, sc->sc_mem_base,
6156a2b914cSpatrick 	    sc->sc_mem_bus_addr, sc->sc_mem_size);
616d2397242Skettenis 	if (sc->sc_num_viewport > 2 && sc->sc_io_size > 0)
6176a2b914cSpatrick 		dwpcie_atu_config(sc, IATU_VIEWPORT_INDEX2,
6186a2b914cSpatrick 		    IATU_REGION_CTRL_1_TYPE_IO, sc->sc_io_base,
6196a2b914cSpatrick 		    sc->sc_io_bus_addr, sc->sc_io_size);
620d2397242Skettenis 	if (sc->sc_num_viewport > 3 && sc->sc_pmem_size > 0)
621d2397242Skettenis 		dwpcie_atu_config(sc, IATU_VIEWPORT_INDEX3,
622d2397242Skettenis 		    IATU_REGION_CTRL_1_TYPE_MEM, sc->sc_pmem_base,
623d2397242Skettenis 		    sc->sc_pmem_bus_addr, sc->sc_pmem_size);
6244605d28eSkettenis 
6254605d28eSkettenis 	/* Enable modification of read-only bits. */
6264605d28eSkettenis 	HSET4(sc, MISC_CONTROL_1, MISC_CONTROL_1_DBI_RO_WR_EN);
6274605d28eSkettenis 
6284605d28eSkettenis 	/* A Root Port is a PCI-PCI Bridge. */
6294605d28eSkettenis 	HWRITE4(sc, PCI_CLASS_REG,
6304605d28eSkettenis 	    PCI_CLASS_BRIDGE << PCI_CLASS_SHIFT |
6314605d28eSkettenis 	    PCI_SUBCLASS_BRIDGE_PCI << PCI_SUBCLASS_SHIFT);
6324605d28eSkettenis 
6334605d28eSkettenis 	/* Clear BAR as U-Boot seems to leave garbage in it. */
6347659d2d7Skettenis 	HWRITE4(sc, PCI_MAPREG_START, PCI_MAPREG_MEM_TYPE_64BIT);
6357659d2d7Skettenis 	HWRITE4(sc, PCI_MAPREG_START + 4, 0);
6364605d28eSkettenis 
6377385e486Skettenis 	/* Enable 32-bit I/O addressing. */
6387385e486Skettenis 	HSET4(sc, PPB_REG_IOSTATUS,
6397385e486Skettenis 	    PPB_IO_32BIT | (PPB_IO_32BIT << PPB_IOLIMIT_SHIFT));
6407385e486Skettenis 
6414605d28eSkettenis 	/* Make sure read-only bits are write-protected. */
6424605d28eSkettenis 	HCLR4(sc, MISC_CONTROL_1, MISC_CONTROL_1_DBI_RO_WR_EN);
6437659d2d7Skettenis 
6447659d2d7Skettenis 	/* Set up bus range. */
6457659d2d7Skettenis 	if (OF_getpropintarray(sc->sc_node, "bus-range", bus_range,
6465f800b9bSkettenis 	    sizeof(bus_range)) != sizeof(bus_range)) {
6477659d2d7Skettenis 		bus_range[0] = 0;
6487659d2d7Skettenis 		bus_range[1] = 31;
6497659d2d7Skettenis 	}
6507659d2d7Skettenis 	sc->sc_bus = bus_range[0];
651d43be508Skettenis 
652d43be508Skettenis 	/* Initialize bus range. */
653d43be508Skettenis 	bir = bus_range[0];
654d43be508Skettenis 	bir |= ((bus_range[0] + 1) << 8);
655d43be508Skettenis 	bir |= (bus_range[1] << 16);
656d43be508Skettenis 	HWRITE4(sc, PPB_REG_BUSINFO, bir);
6577659d2d7Skettenis 
658d2397242Skettenis 	/* Initialize memory mapped I/O window. */
659d2397242Skettenis 	membase = sc->sc_mem_bus_addr;
660d2397242Skettenis 	memlimit = membase + sc->sc_mem_size - 1;
661d2397242Skettenis 	blr = memlimit & PPB_MEM_MASK;
662d2397242Skettenis 	blr |= (membase >> PPB_MEM_SHIFT);
663d2397242Skettenis 	HWRITE4(sc, PPB_REG_MEM, blr);
664d2397242Skettenis 
665d43be508Skettenis 	/* Initialize I/O window. */
666d2397242Skettenis 	if (sc->sc_io_size > 0) {
6676a2b914cSpatrick 		iobase = sc->sc_io_bus_addr;
6686a2b914cSpatrick 		iolimit = iobase + sc->sc_io_size - 1;
669d43be508Skettenis 		blr = iolimit & PPB_IO_MASK;
670d43be508Skettenis 		blr |= (iobase >> PPB_IO_SHIFT);
671d43be508Skettenis 		HWRITE4(sc, PPB_REG_IOSTATUS, blr);
672d43be508Skettenis 		blr = (iobase & 0xffff0000) >> 16;
673d43be508Skettenis 		blr |= iolimit & 0xffff0000;
674d43be508Skettenis 		HWRITE4(sc, PPB_REG_IO_HI, blr);
675d2397242Skettenis 	} else {
676d2397242Skettenis 		HWRITE4(sc, PPB_REG_IOSTATUS, 0x000000ff);
677d2397242Skettenis 		HWRITE4(sc, PPB_REG_IO_HI, 0x0000ffff);
678d2397242Skettenis 	}
679d43be508Skettenis 
680d2397242Skettenis 	/* Initialize prefetchable memory mapped I/O window. */
681d2397242Skettenis 	if (sc->sc_pmem_size > 0) {
682d2397242Skettenis 		pmembase = sc->sc_pmem_bus_addr;
683d2397242Skettenis 		pmemlimit = pmembase + sc->sc_pmem_size - 1;
684d2397242Skettenis 		blr = pmemlimit & PPB_MEM_MASK;
6854bc9c22fSkettenis 		blr |= ((pmembase & PPB_MEM_MASK) >> PPB_MEM_SHIFT);
686d2397242Skettenis 		HWRITE4(sc, PPB_REG_PREFMEM, blr);
687d2397242Skettenis 		HWRITE4(sc, PPB_REG_PREFBASE_HI32, pmembase >> 32);
688d2397242Skettenis 		HWRITE4(sc, PPB_REG_PREFLIM_HI32, pmemlimit >> 32);
689d2397242Skettenis 	} else {
690d43be508Skettenis 		HWRITE4(sc, PPB_REG_PREFMEM, 0x0000ffff);
691d43be508Skettenis 		HWRITE4(sc, PPB_REG_PREFBASE_HI32, 0);
692d43be508Skettenis 		HWRITE4(sc, PPB_REG_PREFLIM_HI32, 0);
693d2397242Skettenis 	}
694d43be508Skettenis 
695d2397242Skettenis 	csr = PCI_COMMAND_MASTER_ENABLE | PCI_COMMAND_MEM_ENABLE;
696d2397242Skettenis 	if (sc->sc_io_size > 0)
697d43be508Skettenis 		csr |= PCI_COMMAND_IO_ENABLE;
698d43be508Skettenis 	HWRITE4(sc, PCI_COMMAND_STATUS_REG, csr);
6994605d28eSkettenis 
70071fe361bSkettenis 	memcpy(&sc->sc_bus_iot, sc->sc_iot, sizeof(sc->sc_bus_iot));
70171fe361bSkettenis 	sc->sc_bus_iot.bus_private = sc;
70271fe361bSkettenis 	sc->sc_bus_iot._space_map = dwpcie_bs_iomap;
70371fe361bSkettenis 	memcpy(&sc->sc_bus_memt, sc->sc_iot, sizeof(sc->sc_bus_memt));
70471fe361bSkettenis 	sc->sc_bus_memt.bus_private = sc;
70571fe361bSkettenis 	sc->sc_bus_memt._space_map = dwpcie_bs_memmap;
70671fe361bSkettenis 
7077659d2d7Skettenis 	sc->sc_pc.pc_conf_v = sc;
7087659d2d7Skettenis 	sc->sc_pc.pc_attach_hook = dwpcie_attach_hook;
7097659d2d7Skettenis 	sc->sc_pc.pc_bus_maxdevs = dwpcie_bus_maxdevs;
7107659d2d7Skettenis 	sc->sc_pc.pc_make_tag = dwpcie_make_tag;
7117659d2d7Skettenis 	sc->sc_pc.pc_decompose_tag = dwpcie_decompose_tag;
7127659d2d7Skettenis 	sc->sc_pc.pc_conf_size = dwpcie_conf_size;
7137659d2d7Skettenis 	sc->sc_pc.pc_conf_read = dwpcie_conf_read;
7147659d2d7Skettenis 	sc->sc_pc.pc_conf_write = dwpcie_conf_write;
715619b146dSpatrick 	sc->sc_pc.pc_probe_device_hook = dwpcie_probe_device_hook;
7167659d2d7Skettenis 
7177659d2d7Skettenis 	sc->sc_pc.pc_intr_v = sc;
7187659d2d7Skettenis 	sc->sc_pc.pc_intr_map = dwpcie_intr_map;
719e410b95bSkettenis 	sc->sc_pc.pc_intr_map_msi = _pci_intr_map_msi;
72056d02c00Skettenis 	sc->sc_pc.pc_intr_map_msivec = _pci_intr_map_msivec;
721e410b95bSkettenis 	sc->sc_pc.pc_intr_map_msix = _pci_intr_map_msix;
7227659d2d7Skettenis 	sc->sc_pc.pc_intr_string = dwpcie_intr_string;
7237659d2d7Skettenis 	sc->sc_pc.pc_intr_establish = dwpcie_intr_establish;
7247659d2d7Skettenis 	sc->sc_pc.pc_intr_disestablish = dwpcie_intr_disestablish;
7257659d2d7Skettenis 
7267659d2d7Skettenis 	memset(&pba, 0, sizeof(pba));
7277659d2d7Skettenis 	pba.pba_busname = "pci";
72871fe361bSkettenis 	pba.pba_iot = &sc->sc_bus_iot;
72971fe361bSkettenis 	pba.pba_memt = &sc->sc_bus_memt;
73031876e8bSkettenis 	pba.pba_dmat = sc->sc_dmat;
7317659d2d7Skettenis 	pba.pba_pc = &sc->sc_pc;
7327659d2d7Skettenis 	pba.pba_domain = pci_ndomains++;
7337659d2d7Skettenis 	pba.pba_bus = sc->sc_bus;
7349a76443bSkettenis 
735094225feSkn 	if (OF_is_compatible(sc->sc_node, "baikal,bm1000-pcie") ||
736094225feSkn 	    OF_is_compatible(sc->sc_node, "marvell,armada8k-pcie") ||
737d0adc912Spatrick 	    OF_getproplen(sc->sc_node, "msi-map") > 0 ||
738d5dfbc11Skettenis 	    sc->sc_msi_addr)
7390045467fSkettenis 		pba.pba_flags |= PCI_FLAGS_MSI_ENABLED;
7409a76443bSkettenis 
7419a76443bSkettenis 	/*
742*54fbbda3Sjsg 	 * Only support multiple MSI vectors if we have enough MSI
7439a76443bSkettenis 	 * interrupts (or are using an external interrupt controller
744*54fbbda3Sjsg 	 * that hopefully supports plenty of MSI interrupts).
7459a76443bSkettenis 	 */
7469a76443bSkettenis 	if (OF_getproplen(sc->sc_node, "msi-map") > 0 ||
7479a76443bSkettenis 	    sc->sc_num_msi > 32)
74856d02c00Skettenis 		pba.pba_flags |= PCI_FLAGS_MSIVEC_ENABLED;
7497659d2d7Skettenis 
750f02e807fSpatrick 	pci_dopm = 1;
751f02e807fSpatrick 
7527659d2d7Skettenis 	config_found(self, &pba, NULL);
7537659d2d7Skettenis }
7547659d2d7Skettenis 
7557659d2d7Skettenis void
7566a2b914cSpatrick dwpcie_link_config(struct dwpcie_softc *sc)
7576a2b914cSpatrick {
7586a2b914cSpatrick 	uint32_t mode, width, reg;
7596a2b914cSpatrick 	int lanes;
7606a2b914cSpatrick 
7616a2b914cSpatrick 	lanes = OF_getpropint(sc->sc_node, "num-lanes", 0);
7626a2b914cSpatrick 
7636a2b914cSpatrick 	switch (lanes) {
7646a2b914cSpatrick 	case 1:
7656a2b914cSpatrick 		mode = PCIE_PORT_LINK_CTRL_LANES_1;
7666a2b914cSpatrick 		width = PCIE_LINK_WIDTH_SPEED_CTRL_LANES_1;
7676a2b914cSpatrick 		break;
7686a2b914cSpatrick 	case 2:
7696a2b914cSpatrick 		mode = PCIE_PORT_LINK_CTRL_LANES_2;
7706a2b914cSpatrick 		width = PCIE_LINK_WIDTH_SPEED_CTRL_LANES_2;
7716a2b914cSpatrick 		break;
7726a2b914cSpatrick 	case 4:
7736a2b914cSpatrick 		mode = PCIE_PORT_LINK_CTRL_LANES_4;
7746a2b914cSpatrick 		width = PCIE_LINK_WIDTH_SPEED_CTRL_LANES_4;
7756a2b914cSpatrick 		break;
7766a2b914cSpatrick 	case 8:
7776a2b914cSpatrick 		mode = PCIE_PORT_LINK_CTRL_LANES_8;
7786a2b914cSpatrick 		width = PCIE_LINK_WIDTH_SPEED_CTRL_LANES_8;
7796a2b914cSpatrick 		break;
7806a2b914cSpatrick 	default:
7816a2b914cSpatrick 		printf("%s: %d lanes not supported\n", __func__, lanes);
7826a2b914cSpatrick 		return;
7836a2b914cSpatrick 	}
7846a2b914cSpatrick 
7856a2b914cSpatrick 	reg = HREAD4(sc, PCIE_PORT_LINK_CTRL);
7866a2b914cSpatrick 	reg &= ~PCIE_PORT_LINK_CTRL_LANES_MASK;
7876a2b914cSpatrick 	reg |= mode;
7886a2b914cSpatrick 	HWRITE4(sc, PCIE_PORT_LINK_CTRL, reg);
7896a2b914cSpatrick 
7906a2b914cSpatrick 	reg = HREAD4(sc, PCIE_LINK_WIDTH_SPEED_CTRL);
7916a2b914cSpatrick 	reg &= ~PCIE_LINK_WIDTH_SPEED_CTRL_LANES_MASK;
7926a2b914cSpatrick 	reg |= width;
7936a2b914cSpatrick 	HWRITE4(sc, PCIE_LINK_WIDTH_SPEED_CTRL, reg);
7946a2b914cSpatrick 
7956a2b914cSpatrick 	reg = HREAD4(sc, PCIE_LINK_WIDTH_SPEED_CTRL);
7966a2b914cSpatrick 	reg |= PCIE_LINK_WIDTH_SPEED_CTRL_CHANGE;
7976a2b914cSpatrick 	HWRITE4(sc, PCIE_LINK_WIDTH_SPEED_CTRL, reg);
7986a2b914cSpatrick }
7996a2b914cSpatrick 
800c3f9cac9Spatrick int
8019a76443bSkettenis dwpcie_msi_intr(struct dwpcie_softc *sc, int idx)
802d5dfbc11Skettenis {
803d5dfbc11Skettenis 	struct dwpcie_msi *dm;
804d5dfbc11Skettenis 	uint32_t status;
805d5dfbc11Skettenis 	int vec, s;
806d5dfbc11Skettenis 
8079a76443bSkettenis 	status = HREAD4(sc, PCIE_MSI_INTR_STATUS(idx));
808d5dfbc11Skettenis 	if (status == 0)
809d5dfbc11Skettenis 		return 0;
810d5dfbc11Skettenis 
8119a76443bSkettenis 	HWRITE4(sc, PCIE_MSI_INTR_STATUS(idx), status);
812d5dfbc11Skettenis 	while (status) {
813d5dfbc11Skettenis 		vec = ffs(status) - 1;
814d5dfbc11Skettenis 		status &= ~(1U << vec);
815d5dfbc11Skettenis 
8169a76443bSkettenis 		dm = &sc->sc_msi[idx * 32 + vec];
817d5dfbc11Skettenis 		if (dm->dm_func == NULL)
818d5dfbc11Skettenis 			continue;
819d5dfbc11Skettenis 
820d5dfbc11Skettenis 		if ((dm->dm_flags & IPL_MPSAFE) == 0)
821d5dfbc11Skettenis 			KERNEL_LOCK();
822d5dfbc11Skettenis 		s = splraise(dm->dm_ipl);
823d5dfbc11Skettenis 		if (dm->dm_func(dm->dm_arg))
824d5dfbc11Skettenis 			dm->dm_count.ec_count++;
825d5dfbc11Skettenis 		splx(s);
826d5dfbc11Skettenis 		if ((dm->dm_flags & IPL_MPSAFE) == 0)
827d5dfbc11Skettenis 			KERNEL_UNLOCK();
828d5dfbc11Skettenis 	}
829d5dfbc11Skettenis 
830d5dfbc11Skettenis 	return 1;
831d5dfbc11Skettenis }
832d5dfbc11Skettenis 
833d5dfbc11Skettenis int
8349a76443bSkettenis dwpcie_msi0_intr(void *arg)
8359a76443bSkettenis {
8369a76443bSkettenis 	return dwpcie_msi_intr(arg, 0);
8379a76443bSkettenis }
8389a76443bSkettenis 
8399a76443bSkettenis int
8409a76443bSkettenis dwpcie_msi1_intr(void *arg)
8419a76443bSkettenis {
8429a76443bSkettenis 	return dwpcie_msi_intr(arg, 1);
8439a76443bSkettenis }
8449a76443bSkettenis 
8459a76443bSkettenis int
846d5dfbc11Skettenis dwpcie_msi_init(struct dwpcie_softc *sc)
847d5dfbc11Skettenis {
848d5dfbc11Skettenis 	bus_dma_segment_t seg;
849d5dfbc11Skettenis 	bus_dmamap_t map;
850d5dfbc11Skettenis 	uint64_t addr;
851d5dfbc11Skettenis 	int error, rseg;
8529a76443bSkettenis 	int idx;
853d5dfbc11Skettenis 
854d5dfbc11Skettenis 	/*
855d5dfbc11Skettenis 	 * Allocate some DMA memory such that we have a "safe" target
856d5dfbc11Skettenis 	 * address for MSIs.
857d5dfbc11Skettenis 	 */
858d5dfbc11Skettenis 	error = bus_dmamem_alloc(sc->sc_dmat, sizeof(uint32_t),
859d5dfbc11Skettenis 	    sizeof(uint32_t), 0, &seg, 1, &rseg, BUS_DMA_WAITOK);
860d5dfbc11Skettenis 	if (error)
861d5dfbc11Skettenis 		return error;
862d5dfbc11Skettenis 
863d5dfbc11Skettenis 	/*
864d5dfbc11Skettenis 	 * Translate the CPU address into a bus address that we can
865d5dfbc11Skettenis 	 * program into the hardware.
866d5dfbc11Skettenis 	 */
867d5dfbc11Skettenis 	error = bus_dmamap_create(sc->sc_dmat, sizeof(uint32_t), 1,
868d5dfbc11Skettenis 	    sizeof(uint32_t), 0, BUS_DMA_WAITOK, &map);
869d5dfbc11Skettenis 	if (error) {
870d5dfbc11Skettenis 		bus_dmamem_free(sc->sc_dmat, &seg, 1);
871d5dfbc11Skettenis 		return error;
872d5dfbc11Skettenis 	}
873d5dfbc11Skettenis 	error = bus_dmamap_load_raw(sc->sc_dmat, map, &seg, 1,
874d5dfbc11Skettenis 	    sizeof(uint32_t), BUS_DMA_WAITOK);
875d5dfbc11Skettenis 	if (error) {
876d5dfbc11Skettenis 		bus_dmamap_destroy(sc->sc_dmat, map);
877d5dfbc11Skettenis 		bus_dmamem_free(sc->sc_dmat, &seg, 1);
878d5dfbc11Skettenis 		return error;
879d5dfbc11Skettenis 	}
880d5dfbc11Skettenis 
881d5dfbc11Skettenis 	addr = map->dm_segs[0].ds_addr;
882d5dfbc11Skettenis 	HWRITE4(sc, PCIE_MSI_ADDR_LO, addr);
883d5dfbc11Skettenis 	HWRITE4(sc, PCIE_MSI_ADDR_HI, addr >> 32);
884d5dfbc11Skettenis 
885d5dfbc11Skettenis 	bus_dmamap_unload(sc->sc_dmat, map);
886d5dfbc11Skettenis 	bus_dmamap_destroy(sc->sc_dmat, map);
887d5dfbc11Skettenis 
8889a76443bSkettenis 	/*
8899a76443bSkettenis 	 * See if the device tree indicates that the hardware supports
8909a76443bSkettenis 	 * more than 32 vectors.  Some hardware supports more than 64,
8919a76443bSkettenis 	 * but 64 is good enough for now.
8929a76443bSkettenis 	 */
8939a76443bSkettenis 	idx = OF_getindex(sc->sc_node, "msi1", "interrupt-names");
8949a76443bSkettenis 	if (idx == -1)
8959a76443bSkettenis 		sc->sc_num_msi = 32;
8969a76443bSkettenis 	else
8979a76443bSkettenis 		sc->sc_num_msi = 64;
8989a76443bSkettenis 	KASSERT(sc->sc_num_msi <= DWPCIE_MAX_MSI);
899d5dfbc11Skettenis 
9009a76443bSkettenis 	/* Enable, mask and clear all MSIs. */
9019a76443bSkettenis 	for (idx = 0; idx < sc->sc_num_msi / 32; idx++) {
9029a76443bSkettenis 		HWRITE4(sc, PCIE_MSI_INTR_ENABLE(idx), 0xffffffff);
9039a76443bSkettenis 		HWRITE4(sc, PCIE_MSI_INTR_MASK(idx), 0xffffffff);
9049a76443bSkettenis 		HWRITE4(sc, PCIE_MSI_INTR_STATUS(idx), 0xffffffff);
9059a76443bSkettenis 	}
9069a76443bSkettenis 
9079a76443bSkettenis 	idx = OF_getindex(sc->sc_node, "msi0", "interrupt-names");
9089a76443bSkettenis 	if (idx == -1)
9099a76443bSkettenis 		idx = 0;
9109a76443bSkettenis 
9119a76443bSkettenis 	sc->sc_msi_ih[0] = fdt_intr_establish_idx(sc->sc_node, idx,
9129a76443bSkettenis 	    IPL_BIO | IPL_MPSAFE, dwpcie_msi0_intr, sc, sc->sc_dev.dv_xname);
9139a76443bSkettenis 	if (sc->sc_msi_ih[0] == NULL) {
914d5dfbc11Skettenis 		bus_dmamem_free(sc->sc_dmat, &seg, 1);
915d5dfbc11Skettenis 		return EINVAL;
916d5dfbc11Skettenis 	}
917d5dfbc11Skettenis 
9189a76443bSkettenis 	idx = OF_getindex(sc->sc_node, "msi1", "interrupt-names");
9199a76443bSkettenis 	if (idx == -1)
9209a76443bSkettenis 		goto finish;
9219a76443bSkettenis 
9229a76443bSkettenis 	sc->sc_msi_ih[1] = fdt_intr_establish_idx(sc->sc_node, idx,
9239a76443bSkettenis 	    IPL_BIO | IPL_MPSAFE, dwpcie_msi1_intr, sc, sc->sc_dev.dv_xname);
9249a76443bSkettenis 	if (sc->sc_msi_ih[1] == NULL)
9259a76443bSkettenis 		sc->sc_num_msi = 32;
9269a76443bSkettenis 
9279a76443bSkettenis finish:
928d5dfbc11Skettenis 	/*
929d5dfbc11Skettenis 	 * Hold on to the DMA memory such that nobody can use it to
930d5dfbc11Skettenis 	 * actually do DMA transfers.
931d5dfbc11Skettenis 	 */
932d5dfbc11Skettenis 
933d5dfbc11Skettenis 	sc->sc_msi_addr = addr;
934d5dfbc11Skettenis 	return 0;
935d5dfbc11Skettenis }
936d5dfbc11Skettenis 
937d5dfbc11Skettenis int
9384605d28eSkettenis dwpcie_armada8k_init(struct dwpcie_softc *sc)
9394605d28eSkettenis {
9404605d28eSkettenis 	uint32_t reg;
9416a2b914cSpatrick 	int timo;
9426a2b914cSpatrick 
9436a2b914cSpatrick 	clock_enable_all(sc->sc_node);
9446a2b914cSpatrick 
9456a2b914cSpatrick 	dwpcie_link_config(sc);
9464605d28eSkettenis 
9474605d28eSkettenis 	if (!dwpcie_armada8k_link_up(sc)) {
9484605d28eSkettenis 		reg = HREAD4(sc, PCIE_GLOBAL_CTRL);
9494605d28eSkettenis 		reg &= ~PCIE_GLOBAL_CTRL_APP_LTSSM_EN;
9504605d28eSkettenis 		HWRITE4(sc, PCIE_GLOBAL_CTRL, reg);
9514605d28eSkettenis 	}
9524605d28eSkettenis 
953eae9bc5eSpatrick 	/*
954eae9bc5eSpatrick 	 * Setup Requester-ID to Stream-ID mapping
955eae9bc5eSpatrick 	 * XXX: TF-A is supposed to set this up, but doesn't!
956eae9bc5eSpatrick 	 */
957eae9bc5eSpatrick 	HWRITE4(sc, PCIE_STREAMID, PCIE_STREAMID_8040);
958eae9bc5eSpatrick 
9594605d28eSkettenis 	/* Enable Root Complex mode. */
9604605d28eSkettenis 	reg = HREAD4(sc, PCIE_GLOBAL_CTRL);
9614605d28eSkettenis 	reg &= ~PCIE_GLOBAL_CTRL_DEVICE_TYPE_MASK;
9624605d28eSkettenis 	reg |= PCIE_GLOBAL_CTRL_DEVICE_TYPE_RC;
9634605d28eSkettenis 	HWRITE4(sc, PCIE_GLOBAL_CTRL, reg);
9644605d28eSkettenis 
9654605d28eSkettenis 	HWRITE4(sc, PCIE_ARCACHE_TRC, PCIE_ARCACHE_TRC_DEFAULT);
9664605d28eSkettenis 	HWRITE4(sc, PCIE_AWCACHE_TRC, PCIE_AWCACHE_TRC_DEFAULT);
9674605d28eSkettenis 	reg = HREAD4(sc, PCIE_ARUSER);
9684605d28eSkettenis 	reg &= ~PCIE_AXUSER_DOMAIN_MASK;
9694605d28eSkettenis 	reg |= PCIE_AXUSER_DOMAIN_OUTER_SHARABLE;
9704605d28eSkettenis 	HWRITE4(sc, PCIE_ARUSER, reg);
9714605d28eSkettenis 	reg = HREAD4(sc, PCIE_AWUSER);
9724605d28eSkettenis 	reg &= ~PCIE_AXUSER_DOMAIN_MASK;
9734605d28eSkettenis 	reg |= PCIE_AXUSER_DOMAIN_OUTER_SHARABLE;
9744605d28eSkettenis 	HWRITE4(sc, PCIE_AWUSER, reg);
9754605d28eSkettenis 
9764605d28eSkettenis 	if (!dwpcie_armada8k_link_up(sc)) {
9774605d28eSkettenis 		reg = HREAD4(sc, PCIE_GLOBAL_CTRL);
9784605d28eSkettenis 		reg |= PCIE_GLOBAL_CTRL_APP_LTSSM_EN;
9794605d28eSkettenis 		HWRITE4(sc, PCIE_GLOBAL_CTRL, reg);
9804605d28eSkettenis 	}
9814605d28eSkettenis 
9824605d28eSkettenis 	for (timo = 40; timo > 0; timo--) {
9834605d28eSkettenis 		if (dwpcie_armada8k_link_up(sc))
9844605d28eSkettenis 			break;
9854605d28eSkettenis 		delay(1000);
9864605d28eSkettenis 	}
987c3f9cac9Spatrick 	if (timo == 0)
988c3f9cac9Spatrick 		return ETIMEDOUT;
989f2c2f061Skettenis 
99070e69ae2Spatrick 	sc->sc_ih = fdt_intr_establish(sc->sc_node, IPL_AUDIO | IPL_MPSAFE,
991f2c2f061Skettenis 	    dwpcie_armada8k_intr, sc, sc->sc_dev.dv_xname);
992f2c2f061Skettenis 
993f2c2f061Skettenis 	/* Unmask INTx interrupts. */
994f2c2f061Skettenis 	HWRITE4(sc, PCIE_GLOBAL_INT_MASK,
995f2c2f061Skettenis 	    PCIE_GLOBAL_INT_MASK_INT_A | PCIE_GLOBAL_INT_MASK_INT_B |
996f2c2f061Skettenis 	    PCIE_GLOBAL_INT_MASK_INT_C | PCIE_GLOBAL_INT_MASK_INT_D);
997c3f9cac9Spatrick 
998c3f9cac9Spatrick 	return 0;
9994605d28eSkettenis }
10004605d28eSkettenis 
10014605d28eSkettenis int
10024605d28eSkettenis dwpcie_armada8k_link_up(struct dwpcie_softc *sc)
10034605d28eSkettenis {
10044605d28eSkettenis 	uint32_t reg, mask;
10054605d28eSkettenis 
10064605d28eSkettenis 	mask = PCIE_GLOBAL_STATUS_RDLH_LINK_UP;
10074605d28eSkettenis 	mask |= PCIE_GLOBAL_STATUS_PHY_LINK_UP;
10084605d28eSkettenis 	reg = HREAD4(sc, PCIE_GLOBAL_STATUS);
10094605d28eSkettenis 	return ((reg & mask) == mask);
10104605d28eSkettenis }
10114605d28eSkettenis 
1012f2c2f061Skettenis int
1013f2c2f061Skettenis dwpcie_armada8k_intr(void *arg)
1014f2c2f061Skettenis {
1015f2c2f061Skettenis 	struct dwpcie_softc *sc = arg;
1016f2c2f061Skettenis 	uint32_t cause;
1017f2c2f061Skettenis 
1018f2c2f061Skettenis 	/* Acknowledge interrupts. */
1019f2c2f061Skettenis 	cause = HREAD4(sc, PCIE_GLOBAL_INT_CAUSE);
1020f2c2f061Skettenis 	HWRITE4(sc, PCIE_GLOBAL_INT_CAUSE, cause);
1021f2c2f061Skettenis 
1022f2c2f061Skettenis 	/* INTx interrupt, so not really ours. */
1023f2c2f061Skettenis 	return 0;
1024f2c2f061Skettenis }
1025f2c2f061Skettenis 
1026c3f9cac9Spatrick int
1027c244e331Skettenis dwpcie_g12a_init(struct dwpcie_softc *sc)
1028c244e331Skettenis {
1029c244e331Skettenis 	uint32_t *reset_gpio;
1030c244e331Skettenis 	ssize_t reset_gpiolen;
1031c244e331Skettenis 	uint32_t reg;
1032d5dfbc11Skettenis 	int error, timo;
1033c244e331Skettenis 
1034c244e331Skettenis 	reset_gpiolen = OF_getproplen(sc->sc_node, "reset-gpios");
1035c244e331Skettenis 	if (reset_gpiolen <= 0)
1036c244e331Skettenis 		return ENXIO;
1037c244e331Skettenis 
1038c244e331Skettenis 	if (bus_space_map(sc->sc_iot, sc->sc_glue_base,
1039c244e331Skettenis 	    sc->sc_glue_size, 0, &sc->sc_glue_ioh))
1040c244e331Skettenis 		return ENOMEM;
1041c244e331Skettenis 
1042c244e331Skettenis 	power_domain_enable(sc->sc_node);
1043c244e331Skettenis 
1044c244e331Skettenis 	phy_enable(sc->sc_node, "pcie");
1045c244e331Skettenis 
1046c244e331Skettenis 	reset_assert_all(sc->sc_node);
1047c244e331Skettenis 	delay(500);
1048c244e331Skettenis 	reset_deassert_all(sc->sc_node);
1049c244e331Skettenis 	delay(500);
1050c244e331Skettenis 
1051c244e331Skettenis 	clock_set_frequency(sc->sc_node, "port", 100000000UL);
1052c244e331Skettenis 	clock_enable_all(sc->sc_node);
1053c244e331Skettenis 
1054c244e331Skettenis 	reset_gpio = malloc(reset_gpiolen, M_TEMP, M_WAITOK);
1055c244e331Skettenis 	OF_getpropintarray(sc->sc_node, "reset-gpios", reset_gpio,
1056c244e331Skettenis 	    reset_gpiolen);
1057c244e331Skettenis 	gpio_controller_config_pin(reset_gpio, GPIO_CONFIG_OUTPUT);
1058c244e331Skettenis 	gpio_controller_set_pin(reset_gpio, 1);
1059c244e331Skettenis 
1060c244e331Skettenis 	dwpcie_link_config(sc);
1061c244e331Skettenis 
1062c244e331Skettenis 	reg = bus_space_read_4(sc->sc_iot, sc->sc_glue_ioh, PCIE_CFG0);
1063c244e331Skettenis 	reg |= PCIE_CFG0_APP_LTSSM_EN;
1064c244e331Skettenis 	bus_space_write_4(sc->sc_iot, sc->sc_glue_ioh, PCIE_CFG0, reg);
1065c244e331Skettenis 
1066c244e331Skettenis 	gpio_controller_set_pin(reset_gpio, 1);
1067c244e331Skettenis 	delay(500);
1068c244e331Skettenis 	gpio_controller_set_pin(reset_gpio, 0);
1069c244e331Skettenis 
1070c244e331Skettenis 	free(reset_gpio, M_TEMP, reset_gpiolen);
1071c244e331Skettenis 
1072c244e331Skettenis 	for (timo = 40; timo > 0; timo--) {
1073c244e331Skettenis 		if (dwpcie_g12a_link_up(sc))
1074c244e331Skettenis 			break;
1075c244e331Skettenis 		delay(1000);
1076c244e331Skettenis 	}
1077c244e331Skettenis 	if (timo == 0)
1078c244e331Skettenis 		return ETIMEDOUT;
1079c244e331Skettenis 
1080d5dfbc11Skettenis 	error = dwpcie_msi_init(sc);
1081d5dfbc11Skettenis 	if (error)
1082d5dfbc11Skettenis 		return error;
1083d5dfbc11Skettenis 
1084c244e331Skettenis 	return 0;
1085c244e331Skettenis }
1086c244e331Skettenis 
1087c244e331Skettenis int
1088c244e331Skettenis dwpcie_g12a_link_up(struct dwpcie_softc *sc)
1089c244e331Skettenis {
1090c244e331Skettenis 	uint32_t reg;
1091c244e331Skettenis 
1092c244e331Skettenis 	reg = bus_space_read_4(sc->sc_iot, sc->sc_glue_ioh, PCIE_STATUS12);
1093c244e331Skettenis 	if ((reg & PCIE_STATUS12_SMLH_LINK_UP) &&
1094c244e331Skettenis 	    (reg & PCIE_STATUS12_RDLH_LINK_UP) &&
1095c244e331Skettenis 	    (reg & PCIE_STATUS12_LTSSM_MASK) == PCIE_STATUS12_LTSSM_UP)
1096c244e331Skettenis 		return 1;
1097c244e331Skettenis 	return 0;
1098c244e331Skettenis }
1099c244e331Skettenis 
1100c244e331Skettenis int
11016a2b914cSpatrick dwpcie_imx8mq_init(struct dwpcie_softc *sc)
11027659d2d7Skettenis {
11036a2b914cSpatrick 	uint32_t *clkreq_gpio, *disable_gpio, *reset_gpio;
11046a2b914cSpatrick 	ssize_t clkreq_gpiolen, disable_gpiolen, reset_gpiolen;
11051ad6abbaSpatrick 	struct regmap *anatop, *gpr, *phy;
11066a2b914cSpatrick 	uint32_t off, reg;
1107c3f9cac9Spatrick 	int error, timo;
11086a2b914cSpatrick 
11091ad6abbaSpatrick 	if (OF_is_compatible(sc->sc_node, "fsl,imx8mm-pcie")) {
11101ad6abbaSpatrick 		anatop = regmap_bycompatible("fsl,imx8mm-anatop");
11111ad6abbaSpatrick 		gpr = regmap_bycompatible("fsl,imx8mm-iomuxc-gpr");
11121ad6abbaSpatrick 		phy = regmap_bycompatible("fsl,imx7d-pcie-phy");
11131ad6abbaSpatrick 		KASSERT(phy != NULL);
11141ad6abbaSpatrick 	} else {
11156a2b914cSpatrick 		anatop = regmap_bycompatible("fsl,imx8mq-anatop");
11166a2b914cSpatrick 		gpr = regmap_bycompatible("fsl,imx8mq-iomuxc-gpr");
11171ad6abbaSpatrick 	}
11186a2b914cSpatrick 	KASSERT(anatop != NULL);
11196a2b914cSpatrick 	KASSERT(gpr != NULL);
11206a2b914cSpatrick 
11216a2b914cSpatrick 	clkreq_gpiolen = OF_getproplen(sc->sc_node, "clkreq-gpio");
11226a2b914cSpatrick 	disable_gpiolen = OF_getproplen(sc->sc_node, "disable-gpio");
11236a2b914cSpatrick 	reset_gpiolen = OF_getproplen(sc->sc_node, "reset-gpio");
11246a2b914cSpatrick 
11256a2b914cSpatrick 	if (clkreq_gpiolen > 0) {
11266a2b914cSpatrick 		clkreq_gpio = malloc(clkreq_gpiolen, M_TEMP, M_WAITOK);
11276a2b914cSpatrick 		OF_getpropintarray(sc->sc_node, "clkreq-gpio", clkreq_gpio,
11286a2b914cSpatrick 		    clkreq_gpiolen);
11296a2b914cSpatrick 		gpio_controller_config_pin(clkreq_gpio, GPIO_CONFIG_OUTPUT);
11306a2b914cSpatrick 		gpio_controller_set_pin(clkreq_gpio, 1);
11316a2b914cSpatrick 	}
11326a2b914cSpatrick 
11336a2b914cSpatrick 	if (disable_gpiolen > 0) {
11346a2b914cSpatrick 		disable_gpio = malloc(disable_gpiolen, M_TEMP, M_WAITOK);
11356a2b914cSpatrick 		OF_getpropintarray(sc->sc_node, "disable-gpio", disable_gpio,
11366a2b914cSpatrick 		    disable_gpiolen);
11376a2b914cSpatrick 		gpio_controller_config_pin(disable_gpio, GPIO_CONFIG_OUTPUT);
11386a2b914cSpatrick 		gpio_controller_set_pin(disable_gpio, 0);
11396a2b914cSpatrick 	}
11406a2b914cSpatrick 
11416a2b914cSpatrick 	if (reset_gpiolen > 0) {
11426a2b914cSpatrick 		reset_gpio = malloc(reset_gpiolen, M_TEMP, M_WAITOK);
11436a2b914cSpatrick 		OF_getpropintarray(sc->sc_node, "reset-gpio", reset_gpio,
11446a2b914cSpatrick 		    reset_gpiolen);
11456a2b914cSpatrick 		gpio_controller_config_pin(reset_gpio, GPIO_CONFIG_OUTPUT);
11466a2b914cSpatrick 		gpio_controller_set_pin(reset_gpio, 1);
11476a2b914cSpatrick 	}
11486a2b914cSpatrick 
11496a2b914cSpatrick 	power_domain_enable(sc->sc_node);
11506a2b914cSpatrick 	reset_assert(sc->sc_node, "pciephy");
11516a2b914cSpatrick 	reset_assert(sc->sc_node, "apps");
11526a2b914cSpatrick 
11536a2b914cSpatrick 	reg = regmap_read_4(gpr, IOMUXC_GPR12);
11546a2b914cSpatrick 	if (OF_getpropint(sc->sc_node, "ctrl-id", 0) == 0) {
11556a2b914cSpatrick 		off = IOMUXC_GPR14;
11566a2b914cSpatrick 		reg &= ~IMX8MQ_GPR_PCIE1_DEVICE_TYPE_MASK;
11576a2b914cSpatrick 		reg |= IMX8MQ_GPR_PCIE1_DEVICE_TYPE_RC;
11586a2b914cSpatrick 	} else {
11596a2b914cSpatrick 		off = IOMUXC_GPR16;
11606a2b914cSpatrick 		reg &= ~IMX8MQ_GPR_PCIE2_DEVICE_TYPE_MASK;
11616a2b914cSpatrick 		reg |= IMX8MQ_GPR_PCIE2_DEVICE_TYPE_RC;
11626a2b914cSpatrick 	}
11636a2b914cSpatrick 	regmap_write_4(gpr, IOMUXC_GPR12, reg);
11646a2b914cSpatrick 
11651ad6abbaSpatrick 	if (OF_is_compatible(sc->sc_node, "fsl,imx8mm-pcie")) {
11661ad6abbaSpatrick 		if (OF_getproplen(sc->sc_node, "ext_osc") == 0 ||
11671ad6abbaSpatrick 		    OF_getpropint(sc->sc_node, "ext_osc", 0)) {
11681ad6abbaSpatrick 			reg = regmap_read_4(gpr, off);
11691ad6abbaSpatrick 			reg &= ~(IMX8MQ_GPR_PCIE_REF_USE_PAD |
11701ad6abbaSpatrick 			    IMX8MM_GPR_PCIE_SSC_EN |
11711ad6abbaSpatrick 			    IMX8MM_GPR_PCIE_POWER_OFF |
11721ad6abbaSpatrick 			    IMX8MM_GPR_PCIE_REF_CLK_MASK);
11731ad6abbaSpatrick 			reg |= (IMX8MM_GPR_PCIE_AUX_EN |
11741ad6abbaSpatrick 			    IMX8MM_GPR_PCIE_REF_CLK_EXT);
11751ad6abbaSpatrick 			regmap_write_4(gpr, off, reg);
11761ad6abbaSpatrick 			delay(100);
11771ad6abbaSpatrick 			reg = regmap_read_4(gpr, off);
11781ad6abbaSpatrick 			reg |= IMX8MM_GPR_PCIE_CMN_RST;
11791ad6abbaSpatrick 			regmap_write_4(gpr, off, reg);
11801ad6abbaSpatrick 			delay(200);
11811ad6abbaSpatrick 		} else {
11821ad6abbaSpatrick 			reg = regmap_read_4(gpr, off);
11831ad6abbaSpatrick 			reg &= ~(IMX8MQ_GPR_PCIE_REF_USE_PAD |
11841ad6abbaSpatrick 			    IMX8MM_GPR_PCIE_SSC_EN |
11851ad6abbaSpatrick 			    IMX8MM_GPR_PCIE_POWER_OFF |
11861ad6abbaSpatrick 			    IMX8MM_GPR_PCIE_REF_CLK_MASK);
11871ad6abbaSpatrick 			reg |= (IMX8MM_GPR_PCIE_AUX_EN |
11881ad6abbaSpatrick 			    IMX8MM_GPR_PCIE_REF_CLK_PLL);
11891ad6abbaSpatrick 			regmap_write_4(gpr, off, reg);
11901ad6abbaSpatrick 			delay(100);
11911ad6abbaSpatrick 			regmap_write_4(phy, IMX8MM_PCIE_PHY_CMN_REG62,
11921ad6abbaSpatrick 			    IMX8MM_PCIE_PHY_CMN_REG62_PLL_CLK_OUT);
11931ad6abbaSpatrick 			regmap_write_4(phy, IMX8MM_PCIE_PHY_CMN_REG64,
11941ad6abbaSpatrick 			    IMX8MM_PCIE_PHY_CMN_REG64_AUX_RX_TX_TERM);
11951ad6abbaSpatrick 			reg = regmap_read_4(gpr, off);
11961ad6abbaSpatrick 			reg |= IMX8MM_GPR_PCIE_CMN_RST;
11971ad6abbaSpatrick 			regmap_write_4(gpr, off, reg);
11981ad6abbaSpatrick 			delay(200);
11991ad6abbaSpatrick 			regmap_write_4(phy, IMX8MM_PCIE_PHY_TRSV_REG5,
12001ad6abbaSpatrick 			    IMX8MM_PCIE_PHY_TRSV_REG5_GEN1_DEEMP);
12011ad6abbaSpatrick 			regmap_write_4(phy, IMX8MM_PCIE_PHY_TRSV_REG6,
12021ad6abbaSpatrick 			    IMX8MM_PCIE_PHY_TRSV_REG6_GEN2_DEEMP);
12031ad6abbaSpatrick 		}
12041ad6abbaSpatrick 	} else {
1205ddff7592Spatrick 		if (OF_getproplen(sc->sc_node, "ext_osc") == 0 ||
1206ddff7592Spatrick 		    OF_getpropint(sc->sc_node, "ext_osc", 0)) {
12076a2b914cSpatrick 			reg = regmap_read_4(gpr, off);
12086a2b914cSpatrick 			reg |= IMX8MQ_GPR_PCIE_REF_USE_PAD;
12096a2b914cSpatrick 			regmap_write_4(gpr, off, reg);
12106a2b914cSpatrick 		} else {
12116a2b914cSpatrick 			reg = regmap_read_4(gpr, off);
12126a2b914cSpatrick 			reg &= ~IMX8MQ_GPR_PCIE_REF_USE_PAD;
12136a2b914cSpatrick 			regmap_write_4(gpr, off, reg);
12146a2b914cSpatrick 
12156a2b914cSpatrick 			regmap_write_4(anatop, ANATOP_PLLOUT_CTL,
12161ad6abbaSpatrick 			    ANATOP_PLLOUT_CTL_CKE |
12171ad6abbaSpatrick 			    ANATOP_PLLOUT_CTL_SEL_SYSPLL1);
12186a2b914cSpatrick 			regmap_write_4(anatop, ANATOP_PLLOUT_DIV,
12196a2b914cSpatrick 			    ANATOP_PLLOUT_DIV_SYSPLL1);
12206a2b914cSpatrick 		}
12211ad6abbaSpatrick 	}
12226a2b914cSpatrick 
12236a2b914cSpatrick 	clock_enable(sc->sc_node, "pcie_phy");
12246a2b914cSpatrick 	clock_enable(sc->sc_node, "pcie_bus");
12256a2b914cSpatrick 	clock_enable(sc->sc_node, "pcie");
1226e9c4ad80Spatrick 	clock_enable(sc->sc_node, "pcie_aux");
12276a2b914cSpatrick 
12286a2b914cSpatrick 	/* Allow clocks to stabilize. */
12296a2b914cSpatrick 	delay(200);
12306a2b914cSpatrick 
12316a2b914cSpatrick 	if (reset_gpiolen > 0) {
12326a2b914cSpatrick 		gpio_controller_set_pin(reset_gpio, 1);
1233915a5ad8Spatrick 		delay(100000);
12346a2b914cSpatrick 		gpio_controller_set_pin(reset_gpio, 0);
12356a2b914cSpatrick 	}
12366a2b914cSpatrick 
12376a2b914cSpatrick 	reset_deassert(sc->sc_node, "pciephy");
12386a2b914cSpatrick 
12391ad6abbaSpatrick 	if (OF_is_compatible(sc->sc_node, "fsl,imx8mm-pcie")) {
12401ad6abbaSpatrick 		for (timo = 2000; timo > 0; timo--) {
12411ad6abbaSpatrick 			if (regmap_read_4(phy, IMX8MM_PCIE_PHY_CMN_REG75) ==
12421ad6abbaSpatrick 			    IMX8MM_PCIE_PHY_CMN_REG75_PLL_DONE)
12431ad6abbaSpatrick 				break;
12441ad6abbaSpatrick 			delay(10);
12451ad6abbaSpatrick 		}
1246c3f9cac9Spatrick 		if (timo == 0) {
1247c3f9cac9Spatrick 			error = ETIMEDOUT;
1248c3f9cac9Spatrick 			goto err;
1249c3f9cac9Spatrick 		}
12501ad6abbaSpatrick 	}
12511ad6abbaSpatrick 
12526a2b914cSpatrick 	reg = HREAD4(sc, 0x100000 + PCIE_RC_LCR);
12536a2b914cSpatrick 	reg &= ~PCIE_RC_LCR_L1EL_MASK;
12546a2b914cSpatrick 	reg |= PCIE_RC_LCR_L1EL_64US;
12556a2b914cSpatrick 	HWRITE4(sc, 0x100000 + PCIE_RC_LCR, reg);
12566a2b914cSpatrick 
12576a2b914cSpatrick 	dwpcie_link_config(sc);
12586a2b914cSpatrick 
12596a2b914cSpatrick 	reg = HREAD4(sc, PCIE_RC_LCR);
12606a2b914cSpatrick 	reg &= ~PCIE_RC_LCR_MAX_LINK_SPEEDS_MASK;
12616a2b914cSpatrick 	reg |= PCIE_RC_LCR_MAX_LINK_SPEEDS_GEN1;
12626a2b914cSpatrick 	HWRITE4(sc, PCIE_RC_LCR, reg);
12636a2b914cSpatrick 
12646a2b914cSpatrick 	reset_deassert(sc->sc_node, "apps");
12656a2b914cSpatrick 
12666a2b914cSpatrick 	for (timo = 20000; timo > 0; timo--) {
12676a2b914cSpatrick 		if (dwpcie_link_up(sc))
12686a2b914cSpatrick 			break;
12696a2b914cSpatrick 		delay(10);
12706a2b914cSpatrick 	}
1271c3f9cac9Spatrick 	if (timo == 0) {
1272c3f9cac9Spatrick 		error = ETIMEDOUT;
1273c3f9cac9Spatrick 		goto err;
1274c3f9cac9Spatrick 	}
12756a2b914cSpatrick 
12766a2b914cSpatrick 	if (OF_getpropint(sc->sc_node, "fsl,max-link-speed", 1) >= 2) {
12776a2b914cSpatrick 		reg = HREAD4(sc, PCIE_RC_LCR);
12786a2b914cSpatrick 		reg &= ~PCIE_RC_LCR_MAX_LINK_SPEEDS_MASK;
12796a2b914cSpatrick 		reg |= PCIE_RC_LCR_MAX_LINK_SPEEDS_GEN2;
12806a2b914cSpatrick 		HWRITE4(sc, PCIE_RC_LCR, reg);
12816a2b914cSpatrick 
12826a2b914cSpatrick 		reg = HREAD4(sc, PCIE_LINK_WIDTH_SPEED_CTRL);
12836a2b914cSpatrick 		reg |= PCIE_LINK_WIDTH_SPEED_CTRL_CHANGE;
12846a2b914cSpatrick 		HWRITE4(sc, PCIE_LINK_WIDTH_SPEED_CTRL, reg);
12856a2b914cSpatrick 
12866a2b914cSpatrick 		for (timo = 20000; timo > 0; timo--) {
12876a2b914cSpatrick 			if (dwpcie_link_up(sc))
12886a2b914cSpatrick 				break;
12896a2b914cSpatrick 			delay(10);
12906a2b914cSpatrick 		}
1291c3f9cac9Spatrick 		if (timo == 0) {
1292c3f9cac9Spatrick 			error = ETIMEDOUT;
1293c3f9cac9Spatrick 			goto err;
1294c3f9cac9Spatrick 		}
12956a2b914cSpatrick 	}
12966a2b914cSpatrick 
12976a2b914cSpatrick 	sc->sc_ih = fdt_intr_establish(sc->sc_node, IPL_AUDIO | IPL_MPSAFE,
12986a2b914cSpatrick 	    dwpcie_imx8mq_intr, sc, sc->sc_dev.dv_xname);
12996a2b914cSpatrick 
13006a2b914cSpatrick 	/* Unmask INTx interrupts. */
13016a2b914cSpatrick 	HWRITE4(sc, PCIE_GLOBAL_INT_MASK,
13026a2b914cSpatrick 	    PCIE_GLOBAL_INT_MASK_INT_A | PCIE_GLOBAL_INT_MASK_INT_B |
13036a2b914cSpatrick 	    PCIE_GLOBAL_INT_MASK_INT_C | PCIE_GLOBAL_INT_MASK_INT_D);
13046a2b914cSpatrick 
1305c3f9cac9Spatrick 	error = 0;
1306c3f9cac9Spatrick err:
13076a2b914cSpatrick 	if (clkreq_gpiolen > 0)
13086a2b914cSpatrick 		free(clkreq_gpio, M_TEMP, clkreq_gpiolen);
13096a2b914cSpatrick 	if (disable_gpiolen > 0)
13106a2b914cSpatrick 		free(disable_gpio, M_TEMP, disable_gpiolen);
13116a2b914cSpatrick 	if (reset_gpiolen > 0)
13126a2b914cSpatrick 		free(reset_gpio, M_TEMP, reset_gpiolen);
1313c3f9cac9Spatrick 	return error;
13146a2b914cSpatrick }
13156a2b914cSpatrick 
13166a2b914cSpatrick int
13176a2b914cSpatrick dwpcie_imx8mq_intr(void *arg)
13186a2b914cSpatrick {
13196a2b914cSpatrick 	struct dwpcie_softc *sc = arg;
13206a2b914cSpatrick 	uint32_t cause;
13216a2b914cSpatrick 
13226a2b914cSpatrick 	/* Acknowledge interrupts. */
13236a2b914cSpatrick 	cause = HREAD4(sc, PCIE_GLOBAL_INT_CAUSE);
13246a2b914cSpatrick 	HWRITE4(sc, PCIE_GLOBAL_INT_CAUSE, cause);
13256a2b914cSpatrick 
13266a2b914cSpatrick 	/* INTx interrupt, so not really ours. */
13276a2b914cSpatrick 	return 0;
13286a2b914cSpatrick }
13296a2b914cSpatrick 
1330d2397242Skettenis int
1331d2397242Skettenis dwpcie_fu740_init(struct dwpcie_softc *sc)
1332d2397242Skettenis {
1333d2397242Skettenis 	sc->sc_num_viewport = 8;
1334d2397242Skettenis 
1335d2397242Skettenis 	return 0;
1336d2397242Skettenis }
1337d2397242Skettenis 
13385f3e3118Skettenis int
1339f98fd054Skettenis dwpcie_rk3568_link_up(struct dwpcie_softc *sc)
1340f98fd054Skettenis {
1341f98fd054Skettenis 	uint32_t reg;
1342f98fd054Skettenis 
1343f98fd054Skettenis 	reg = bus_space_read_4(sc->sc_iot, sc->sc_glue_ioh,
1344f98fd054Skettenis 	    PCIE_CLIENT_LTSSM_STATUS);
1345f98fd054Skettenis 	if ((reg & PCIE_CLIENT_SMLH_LINK_UP) &&
1346f98fd054Skettenis 	    (reg & PCIE_CLIENT_RDLH_LINK_UP) &&
1347f98fd054Skettenis 	    (reg & PCIE_CLIENT_LTSSM_MASK) == PCIE_CLIENT_LTSSM_UP)
1348f98fd054Skettenis 		return 1;
1349f98fd054Skettenis 	return 0;
1350f98fd054Skettenis }
1351f98fd054Skettenis 
1352f98fd054Skettenis int
13535f3e3118Skettenis dwpcie_rk3568_init(struct dwpcie_softc *sc)
13545f3e3118Skettenis {
13551140f82aSkettenis 	uint32_t *reset_gpio;
13561140f82aSkettenis 	ssize_t reset_gpiolen;
13575f800b9bSkettenis 	int error, idx, node;
13585f800b9bSkettenis 	int pin, timo;
1359f98fd054Skettenis 
1360f98fd054Skettenis 	sc->sc_num_viewport = 8;
1361f98fd054Skettenis 
1362f98fd054Skettenis 	if (bus_space_map(sc->sc_iot, sc->sc_glue_base,
1363f98fd054Skettenis 	    sc->sc_glue_size, 0, &sc->sc_glue_ioh))
1364f98fd054Skettenis 		return ENOMEM;
13651140f82aSkettenis 
13661140f82aSkettenis 	reset_assert_all(sc->sc_node);
1367f98fd054Skettenis 	/* Power must be enabled before initializing the PHY. */
13681140f82aSkettenis 	regulator_enable(OF_getpropint(sc->sc_node, "vpcie3v3-supply", 0));
13691140f82aSkettenis 	phy_enable(sc->sc_node, "pcie-phy");
13701140f82aSkettenis 	reset_deassert_all(sc->sc_node);
13711140f82aSkettenis 
13721140f82aSkettenis 	clock_enable_all(sc->sc_node);
13731140f82aSkettenis 
1374f98fd054Skettenis 	if (dwpcie_rk3568_link_up(sc))
1375f98fd054Skettenis 		return 0;
1376f98fd054Skettenis 
13771140f82aSkettenis 	reset_gpiolen = OF_getproplen(sc->sc_node, "reset-gpios");
13781140f82aSkettenis 	if (reset_gpiolen > 0) {
13791140f82aSkettenis 		reset_gpio = malloc(reset_gpiolen, M_TEMP, M_WAITOK);
13801140f82aSkettenis 		OF_getpropintarray(sc->sc_node, "reset-gpios", reset_gpio,
13811140f82aSkettenis 		    reset_gpiolen);
13821140f82aSkettenis 		gpio_controller_config_pin(reset_gpio, GPIO_CONFIG_OUTPUT);
13831140f82aSkettenis 		gpio_controller_set_pin(reset_gpio, 1);
13841140f82aSkettenis 	}
13851140f82aSkettenis 
1386f98fd054Skettenis 	bus_space_write_4(sc->sc_iot, sc->sc_glue_ioh,
1387f98fd054Skettenis 	    PCIE_CLIENT_HOT_RESET_CTRL, PCIE_CLIENT_APP_LTSSM_ENABLE_ENHANCE);
1388f98fd054Skettenis 	bus_space_write_4(sc->sc_iot, sc->sc_glue_ioh,
1389f98fd054Skettenis 	    PCIE_CLIENT_GENERAL_CON, PCIE_CLIENT_DEV_TYPE_RC);
13905f3e3118Skettenis 
1391f98fd054Skettenis 	/* Assert PERST#. */
1392f98fd054Skettenis 	if (reset_gpiolen > 0)
1393f98fd054Skettenis 		gpio_controller_set_pin(reset_gpio, 0);
1394f98fd054Skettenis 
1395a14a93a7Skettenis 	dwpcie_link_config(sc);
1396a14a93a7Skettenis 
1397f98fd054Skettenis 	/* Enable LTSSM. */
1398f98fd054Skettenis 	bus_space_write_4(sc->sc_iot, sc->sc_glue_ioh, PCIE_CLIENT_GENERAL_CON,
1399f98fd054Skettenis 	    PCIE_CLIENT_LINK_REQ_RST_GRT | PCIE_CLIENT_APP_LTSSM_ENABLE);
1400f98fd054Skettenis 
1401f98fd054Skettenis 	/*
1402f98fd054Skettenis 	 * PERST# must remain asserted for at least 100us after the
1403f98fd054Skettenis 	 * reference clock becomes stable.  But also has to remain
1404f98fd054Skettenis 	 * active at least 100ms after power up.  Since we may have
1405f98fd054Skettenis 	 * just powered on the device, play it safe and use 100ms.
1406f98fd054Skettenis 	 */
1407f98fd054Skettenis 	delay(100000);
1408f98fd054Skettenis 
1409f98fd054Skettenis 	/* Deassert PERST#. */
1410f98fd054Skettenis 	if (reset_gpiolen > 0)
1411f98fd054Skettenis 		gpio_controller_set_pin(reset_gpio, 1);
1412f98fd054Skettenis 
1413f98fd054Skettenis 	/* Wait for the link to come up. */
1414f98fd054Skettenis 	for (timo = 100; timo > 0; timo--) {
1415f98fd054Skettenis 		if (dwpcie_rk3568_link_up(sc))
1416f98fd054Skettenis 			break;
1417f98fd054Skettenis 		delay(10000);
1418f98fd054Skettenis 	}
1419f98fd054Skettenis 	if (timo == 0) {
1420f98fd054Skettenis 		error = ETIMEDOUT;
1421f98fd054Skettenis 		goto err;
1422f98fd054Skettenis 	}
1423f98fd054Skettenis 
14245f800b9bSkettenis 	node = OF_getnodebyname(sc->sc_node, "legacy-interrupt-controller");
14255f800b9bSkettenis 	idx = OF_getindex(sc->sc_node, "legacy", "interrupt-names");
14265f800b9bSkettenis 	if (node && idx != -1) {
14275f800b9bSkettenis 		sc->sc_ih = fdt_intr_establish_idx(sc->sc_node, idx,
14285f800b9bSkettenis 		    IPL_BIO | IPL_MPSAFE, dwpcie_rk3568_intr, sc,
14295f800b9bSkettenis 		    sc->sc_dev.dv_xname);
14305f800b9bSkettenis 	}
14315f800b9bSkettenis 
14325f800b9bSkettenis 	if (sc->sc_ih) {
14335f800b9bSkettenis 		for (pin = 0; pin < nitems(sc->sc_intx); pin++)
14345f800b9bSkettenis 			TAILQ_INIT(&sc->sc_intx[pin]);
14355f800b9bSkettenis 		sc->sc_ic.ic_node = node;
14365f800b9bSkettenis 		sc->sc_ic.ic_cookie = sc;
14375f800b9bSkettenis 		sc->sc_ic.ic_establish = dwpcie_rk3568_intr_establish;
14385f800b9bSkettenis 		sc->sc_ic.ic_disestablish = dwpcie_rk3568_intr_disestablish;
14395f800b9bSkettenis 		sc->sc_ic.ic_barrier = dwpcie_rk3568_intr_barrier;
14405f800b9bSkettenis 		fdt_intr_register(&sc->sc_ic);
14415f800b9bSkettenis 	}
14425f800b9bSkettenis 
1443f98fd054Skettenis 	error = 0;
1444f98fd054Skettenis err:
1445f98fd054Skettenis 	if (reset_gpiolen > 0)
1446f98fd054Skettenis 		free(reset_gpio, M_TEMP, reset_gpiolen);
1447f98fd054Skettenis 
1448f98fd054Skettenis 	return error;
14495f3e3118Skettenis }
14505f3e3118Skettenis 
1451a848a3cbSpatrick int
14525f800b9bSkettenis dwpcie_rk3568_intr(void *arg)
14535f800b9bSkettenis {
14545f800b9bSkettenis 	struct dwpcie_softc *sc = arg;
14555f800b9bSkettenis 	struct dwpcie_intx *di;
14565f800b9bSkettenis 	uint32_t status;
14575f800b9bSkettenis 	int pin, s;
14585f800b9bSkettenis 
14595f800b9bSkettenis 	status = bus_space_read_4(sc->sc_iot, sc->sc_glue_ioh,
14605f800b9bSkettenis 	    PCIE_CLIENT_INTR_STATUS_LEGACY);
14615f800b9bSkettenis 	for (pin = 0; pin < nitems(sc->sc_intx); pin++) {
14625f800b9bSkettenis 		if ((status & (1 << pin)) == 0)
14635f800b9bSkettenis 			continue;
14645f800b9bSkettenis 
14655f800b9bSkettenis 		TAILQ_FOREACH(di, &sc->sc_intx[pin], di_next) {
14665f800b9bSkettenis 			if ((di->di_flags & IPL_MPSAFE) == 0)
14675f800b9bSkettenis 				KERNEL_LOCK();
14685f800b9bSkettenis 			s = splraise(di->di_ipl);
14695f800b9bSkettenis 			if (di->di_func(di->di_arg))
14705f800b9bSkettenis 				di->di_count.ec_count++;
14715f800b9bSkettenis 			splx(s);
14725f800b9bSkettenis 			if ((di->di_flags & IPL_MPSAFE) == 0)
14735f800b9bSkettenis 				KERNEL_UNLOCK();
14745f800b9bSkettenis 		}
14755f800b9bSkettenis 	}
14765f800b9bSkettenis 
14775f800b9bSkettenis 	return 1;
14785f800b9bSkettenis }
14795f800b9bSkettenis 
14805f800b9bSkettenis void *
14815f800b9bSkettenis dwpcie_rk3568_intr_establish(void *cookie, int *cell, int level,
14825f800b9bSkettenis     struct cpu_info *ci, int (*func)(void *), void *arg, char *name)
14835f800b9bSkettenis {
14845f800b9bSkettenis 	struct dwpcie_softc *sc = (struct dwpcie_softc *)cookie;
14855f800b9bSkettenis 	struct dwpcie_intx *di;
14865f800b9bSkettenis 	int pin = cell[0];
14875f800b9bSkettenis 	uint32_t mask = (1U << pin);
14885f800b9bSkettenis 
14895f800b9bSkettenis 	if (ci != NULL && !CPU_IS_PRIMARY(ci))
14905f800b9bSkettenis 		return NULL;
14915f800b9bSkettenis 
14925f800b9bSkettenis 	if (pin < 0 || pin >= nitems(sc->sc_intx))
14935f800b9bSkettenis 		return NULL;
14945f800b9bSkettenis 
14955f800b9bSkettenis 	/* Mask the interrupt. */
14965f800b9bSkettenis 	bus_space_write_4(sc->sc_iot, sc->sc_glue_ioh,
14975f800b9bSkettenis 	    PCIE_CLIENT_INTR_MASK_LEGACY, (mask << 16) | mask);
14985f800b9bSkettenis 	intr_barrier(sc->sc_ih);
14995f800b9bSkettenis 
15005f800b9bSkettenis 	di = malloc(sizeof(*di), M_DEVBUF, M_WAITOK | M_ZERO);
15015f800b9bSkettenis 	di->di_func = func;
15025f800b9bSkettenis 	di->di_arg = arg;
15035f800b9bSkettenis 	di->di_ipl = level & IPL_IRQMASK;
15045f800b9bSkettenis 	di->di_flags = level & IPL_FLAGMASK;
15055f800b9bSkettenis 	di->di_pin = pin;
15065f800b9bSkettenis 	di->di_name = name;
15075f800b9bSkettenis 	if (name != NULL)
15085f800b9bSkettenis 		evcount_attach(&di->di_count, name, &di->di_pin);
15095f800b9bSkettenis 	di->di_sc = sc;
15105f800b9bSkettenis 	TAILQ_INSERT_TAIL(&sc->sc_intx[pin], di, di_next);
15115f800b9bSkettenis 
15125f800b9bSkettenis 	/* Unmask the interrupt. */
15135f800b9bSkettenis 	bus_space_write_4(sc->sc_iot, sc->sc_glue_ioh,
15145f800b9bSkettenis 	    PCIE_CLIENT_INTR_MASK_LEGACY, mask << 16);
15155f800b9bSkettenis 
15165f800b9bSkettenis 	return di;
15175f800b9bSkettenis }
15185f800b9bSkettenis 
15195f800b9bSkettenis void
15205f800b9bSkettenis dwpcie_rk3568_intr_disestablish(void *cookie)
15215f800b9bSkettenis {
15225f800b9bSkettenis 	struct dwpcie_intx *di = cookie;
15235f800b9bSkettenis 	struct dwpcie_softc *sc = di->di_sc;
15245f800b9bSkettenis 	uint32_t mask = (1U << di->di_pin);
15255f800b9bSkettenis 
15265f800b9bSkettenis 	/* Mask the interrupt. */
15275f800b9bSkettenis 	bus_space_write_4(sc->sc_iot, sc->sc_glue_ioh,
15285f800b9bSkettenis 	    PCIE_CLIENT_INTR_MASK_LEGACY, (mask << 16) | mask);
15295f800b9bSkettenis 	intr_barrier(sc->sc_ih);
15305f800b9bSkettenis 
15315f800b9bSkettenis 	if (di->di_name)
15325f800b9bSkettenis 		evcount_detach(&di->di_count);
15335f800b9bSkettenis 
15345f800b9bSkettenis 	TAILQ_REMOVE(&sc->sc_intx[di->di_pin], di, di_next);
15355f800b9bSkettenis 
15365f800b9bSkettenis 	if (!TAILQ_EMPTY(&sc->sc_intx[di->di_pin])) {
15375f800b9bSkettenis 		/* Unmask the interrupt. */
15385f800b9bSkettenis 		bus_space_write_4(sc->sc_iot, sc->sc_glue_ioh,
15395f800b9bSkettenis 		    PCIE_CLIENT_INTR_MASK_LEGACY, mask << 16);
15405f800b9bSkettenis 	}
15415045af7fSjsg 
15425045af7fSjsg 	free(di, M_DEVBUF, sizeof(*di));
15435f800b9bSkettenis }
15445f800b9bSkettenis 
15455f800b9bSkettenis void
15465f800b9bSkettenis dwpcie_rk3568_intr_barrier(void *cookie)
15475f800b9bSkettenis {
15485f800b9bSkettenis 	struct dwpcie_intx *di = cookie;
15495f800b9bSkettenis 	struct dwpcie_softc *sc = di->di_sc;
15505f800b9bSkettenis 
15515f800b9bSkettenis 	intr_barrier(sc->sc_ih);
15525f800b9bSkettenis }
15535f800b9bSkettenis 
15545f800b9bSkettenis int
1555a848a3cbSpatrick dwpcie_sc8280xp_init(struct dwpcie_softc *sc)
1556a848a3cbSpatrick {
1557a848a3cbSpatrick 	sc->sc_num_viewport = 8;
1558a848a3cbSpatrick 
1559351aecf3Spatrick 	if (OF_getproplen(sc->sc_node, "msi-map") <= 0)
1560351aecf3Spatrick 		return dwpcie_msi_init(sc);
1561351aecf3Spatrick 
1562a848a3cbSpatrick 	return 0;
1563a848a3cbSpatrick }
1564a848a3cbSpatrick 
1565a848a3cbSpatrick void
1566a848a3cbSpatrick dwpcie_atu_write(struct dwpcie_softc *sc, int index, off_t reg,
1567a848a3cbSpatrick     uint32_t val)
1568a848a3cbSpatrick {
1569a848a3cbSpatrick 	if (sc->sc_atu_unroll) {
1570a848a3cbSpatrick 		bus_space_write_4(sc->sc_iot, sc->sc_atu_ioh,
1571a848a3cbSpatrick 		    IATU_OFFSET_UNROLL(index) + reg, val);
1572a848a3cbSpatrick 		return;
1573a848a3cbSpatrick 	}
1574a848a3cbSpatrick 
1575a848a3cbSpatrick 	if (sc->sc_atu_viewport != index) {
1576a848a3cbSpatrick 		HWRITE4(sc, IATU_VIEWPORT, index);
1577a848a3cbSpatrick 		sc->sc_atu_viewport = index;
1578a848a3cbSpatrick 	}
1579a848a3cbSpatrick 
1580a848a3cbSpatrick 	HWRITE4(sc, IATU_OFFSET_VIEWPORT + reg, val);
1581a848a3cbSpatrick }
1582a848a3cbSpatrick 
1583a848a3cbSpatrick uint32_t
1584a848a3cbSpatrick dwpcie_atu_read(struct dwpcie_softc *sc, int index, off_t reg)
1585a848a3cbSpatrick {
1586a848a3cbSpatrick 	if (sc->sc_atu_unroll) {
1587a848a3cbSpatrick 		return bus_space_read_4(sc->sc_iot, sc->sc_atu_ioh,
1588a848a3cbSpatrick 		    IATU_OFFSET_UNROLL(index) + reg);
1589a848a3cbSpatrick 	}
1590a848a3cbSpatrick 
1591a848a3cbSpatrick 	if (sc->sc_atu_viewport != index) {
1592a848a3cbSpatrick 		HWRITE4(sc, IATU_VIEWPORT, index);
1593a848a3cbSpatrick 		sc->sc_atu_viewport = index;
1594a848a3cbSpatrick 	}
1595a848a3cbSpatrick 
1596a848a3cbSpatrick 	return HREAD4(sc, IATU_OFFSET_VIEWPORT + reg);
1597a848a3cbSpatrick }
1598a848a3cbSpatrick 
15995f3e3118Skettenis void
16005f3e3118Skettenis dwpcie_atu_disable(struct dwpcie_softc *sc, int index)
16015f3e3118Skettenis {
1602a848a3cbSpatrick 	dwpcie_atu_write(sc, index, IATU_REGION_CTRL_2, 0);
16035f3e3118Skettenis }
16045f3e3118Skettenis 
16056a2b914cSpatrick void
16066a2b914cSpatrick dwpcie_atu_config(struct dwpcie_softc *sc, int index, int type,
16076a2b914cSpatrick     uint64_t cpu_addr, uint64_t pci_addr, uint64_t size)
16086a2b914cSpatrick {
1609a848a3cbSpatrick 	uint32_t reg;
16106a2b914cSpatrick 	int timo;
16116a2b914cSpatrick 
1612a848a3cbSpatrick 	dwpcie_atu_write(sc, index, IATU_LWR_BASE_ADDR, cpu_addr);
1613a848a3cbSpatrick 	dwpcie_atu_write(sc, index, IATU_UPPER_BASE_ADDR, cpu_addr >> 32);
1614a848a3cbSpatrick 	dwpcie_atu_write(sc, index, IATU_LIMIT_ADDR, cpu_addr + size - 1);
1615a848a3cbSpatrick 	dwpcie_atu_write(sc, index, IATU_LWR_TARGET_ADDR, pci_addr);
1616a848a3cbSpatrick 	dwpcie_atu_write(sc, index, IATU_UPPER_TARGET_ADDR, pci_addr >> 32);
1617a848a3cbSpatrick 	dwpcie_atu_write(sc, index, IATU_REGION_CTRL_1, type);
1618a848a3cbSpatrick 	dwpcie_atu_write(sc, index, IATU_REGION_CTRL_2,
1619a848a3cbSpatrick 	    IATU_REGION_CTRL_2_REGION_EN);
16206a2b914cSpatrick 
16216a2b914cSpatrick 	for (timo = 5; timo > 0; timo--) {
1622a848a3cbSpatrick 		reg = dwpcie_atu_read(sc, index, IATU_REGION_CTRL_2);
16236a2b914cSpatrick 		if (reg & IATU_REGION_CTRL_2_REGION_EN)
16246a2b914cSpatrick 			break;
16256a2b914cSpatrick 		delay(9000);
16266a2b914cSpatrick 	}
16276a2b914cSpatrick 	if (timo == 0)
16286a2b914cSpatrick 		printf("%s:%d: timeout\n", __func__, __LINE__);
16296a2b914cSpatrick }
16306a2b914cSpatrick 
16316a2b914cSpatrick int
16326a2b914cSpatrick dwpcie_link_up(struct dwpcie_softc *sc)
16336a2b914cSpatrick {
16346a2b914cSpatrick 	uint32_t reg;
16356a2b914cSpatrick 
16366a2b914cSpatrick 	reg = HREAD4(sc, PCIE_PHY_DEBUG_R1);
16376a2b914cSpatrick 	if ((reg & PCIE_PHY_DEBUG_R1_XMLH_LINK_UP) != 0 &&
16386a2b914cSpatrick 	    (reg & PCIE_PHY_DEBUG_R1_XMLH_LINK_IN_TRAINING) == 0)
16396a2b914cSpatrick 		return 1;
16406a2b914cSpatrick 	return 0;
16417659d2d7Skettenis }
16427659d2d7Skettenis 
16437659d2d7Skettenis void
16447659d2d7Skettenis dwpcie_attach_hook(struct device *parent, struct device *self,
16457659d2d7Skettenis     struct pcibus_attach_args *pba)
16467659d2d7Skettenis {
16477659d2d7Skettenis }
16487659d2d7Skettenis 
16497659d2d7Skettenis int
16507659d2d7Skettenis dwpcie_bus_maxdevs(void *v, int bus)
16517659d2d7Skettenis {
16527659d2d7Skettenis 	struct dwpcie_softc *sc = v;
16537659d2d7Skettenis 
16547659d2d7Skettenis 	if (bus == sc->sc_bus || bus == sc->sc_bus + 1)
16557659d2d7Skettenis 		return 1;
16567659d2d7Skettenis 	return 32;
16577659d2d7Skettenis }
16587659d2d7Skettenis 
1659e1e95ce2Spatrick int
1660e1e95ce2Spatrick dwpcie_find_node(int node, int bus, int device, int function)
1661e1e95ce2Spatrick {
1662e1e95ce2Spatrick 	uint32_t reg[5];
1663e1e95ce2Spatrick 	uint32_t phys_hi;
1664e1e95ce2Spatrick 	int child;
1665e1e95ce2Spatrick 
1666e1e95ce2Spatrick 	phys_hi = ((bus << 16) | (device << 11) | (function << 8));
1667e1e95ce2Spatrick 
1668e1e95ce2Spatrick 	for (child = OF_child(node); child; child = OF_peer(child)) {
1669e1e95ce2Spatrick 		if (OF_getpropintarray(child, "reg",
1670e1e95ce2Spatrick 		    reg, sizeof(reg)) != sizeof(reg))
1671e1e95ce2Spatrick 			continue;
1672e1e95ce2Spatrick 
1673e1e95ce2Spatrick 		if (reg[0] == phys_hi)
1674e1e95ce2Spatrick 			return child;
1675e1e95ce2Spatrick 
1676e1e95ce2Spatrick 		node = dwpcie_find_node(child, bus, device, function);
1677e1e95ce2Spatrick 		if (node)
1678e1e95ce2Spatrick 			return node;
1679e1e95ce2Spatrick 	}
1680e1e95ce2Spatrick 
1681e1e95ce2Spatrick 	return 0;
1682e1e95ce2Spatrick }
1683e1e95ce2Spatrick 
16847659d2d7Skettenis pcitag_t
16857659d2d7Skettenis dwpcie_make_tag(void *v, int bus, int device, int function)
16867659d2d7Skettenis {
1687e1e95ce2Spatrick 	struct dwpcie_softc *sc = v;
1688e1e95ce2Spatrick 	int node;
1689e1e95ce2Spatrick 
1690e1e95ce2Spatrick 	node = dwpcie_find_node(sc->sc_node, bus, device, function);
1691e1e95ce2Spatrick 	return (((pcitag_t)node << 32) |
1692e1e95ce2Spatrick 	    (bus << 24) | (device << 19) | (function << 16));
16937659d2d7Skettenis }
16947659d2d7Skettenis 
16957659d2d7Skettenis void
16967659d2d7Skettenis dwpcie_decompose_tag(void *v, pcitag_t tag, int *bp, int *dp, int *fp)
16977659d2d7Skettenis {
16987659d2d7Skettenis 	if (bp != NULL)
16997659d2d7Skettenis 		*bp = (tag >> 24) & 0xff;
17007659d2d7Skettenis 	if (dp != NULL)
17017659d2d7Skettenis 		*dp = (tag >> 19) & 0x1f;
17027659d2d7Skettenis 	if (fp != NULL)
17037659d2d7Skettenis 		*fp = (tag >> 16) & 0x7;
17047659d2d7Skettenis }
17057659d2d7Skettenis 
17067659d2d7Skettenis int
17077659d2d7Skettenis dwpcie_conf_size(void *v, pcitag_t tag)
17087659d2d7Skettenis {
17097659d2d7Skettenis 	return PCIE_CONFIG_SPACE_SIZE;
17107659d2d7Skettenis }
17117659d2d7Skettenis 
17127659d2d7Skettenis pcireg_t
17137659d2d7Skettenis dwpcie_conf_read(void *v, pcitag_t tag, int reg)
17147659d2d7Skettenis {
17157659d2d7Skettenis 	struct dwpcie_softc *sc = v;
17167659d2d7Skettenis 	int bus, dev, fn;
17176a2b914cSpatrick 	uint32_t ret;
17187659d2d7Skettenis 
17197659d2d7Skettenis 	dwpcie_decompose_tag(sc, tag, &bus, &dev, &fn);
17207659d2d7Skettenis 	if (bus == sc->sc_bus) {
17217659d2d7Skettenis 		KASSERT(dev == 0);
1722f6d21d67Skettenis 		tag = dwpcie_make_tag(sc, 0, dev, fn);
1723e1e95ce2Spatrick 		return HREAD4(sc, PCITAG_OFFSET(tag) | reg);
17247659d2d7Skettenis 	}
17257659d2d7Skettenis 
17266a2b914cSpatrick 	if (bus == sc->sc_bus + 1) {
17276a2b914cSpatrick 		dwpcie_atu_config(sc, IATU_VIEWPORT_INDEX1,
17286a2b914cSpatrick 		    IATU_REGION_CTRL_1_TYPE_CFG0,
1729e1e95ce2Spatrick 		    sc->sc_conf_base, PCITAG_OFFSET(tag),
1730e1e95ce2Spatrick 		    sc->sc_conf_size);
17316a2b914cSpatrick 	} else {
17326a2b914cSpatrick 		dwpcie_atu_config(sc, IATU_VIEWPORT_INDEX1,
17336a2b914cSpatrick 		    IATU_REGION_CTRL_1_TYPE_CFG1,
1734e1e95ce2Spatrick 		    sc->sc_conf_base, PCITAG_OFFSET(tag),
1735e1e95ce2Spatrick 		    sc->sc_conf_size);
17366a2b914cSpatrick 	}
173701dd2b7cSkettenis 
173801dd2b7cSkettenis 	ret = bus_space_read_4(sc->sc_iot, sc->sc_conf_ioh, reg);
173901dd2b7cSkettenis 
1740d2397242Skettenis 	if (sc->sc_num_viewport <= 2 && sc->sc_io_size > 0) {
17416a2b914cSpatrick 		dwpcie_atu_config(sc, IATU_VIEWPORT_INDEX1,
17426a2b914cSpatrick 		    IATU_REGION_CTRL_1_TYPE_IO, sc->sc_io_base,
17436a2b914cSpatrick 		    sc->sc_io_bus_addr, sc->sc_io_size);
174401dd2b7cSkettenis 	}
17456a2b914cSpatrick 
17466a2b914cSpatrick 	return ret;
17477659d2d7Skettenis }
17487659d2d7Skettenis 
17497659d2d7Skettenis void
17507659d2d7Skettenis dwpcie_conf_write(void *v, pcitag_t tag, int reg, pcireg_t data)
17517659d2d7Skettenis {
17527659d2d7Skettenis 	struct dwpcie_softc *sc = v;
17537659d2d7Skettenis 	int bus, dev, fn;
17547659d2d7Skettenis 
17557659d2d7Skettenis 	dwpcie_decompose_tag(sc, tag, &bus, &dev, &fn);
17567659d2d7Skettenis 	if (bus == sc->sc_bus) {
17577659d2d7Skettenis 		KASSERT(dev == 0);
1758f6d21d67Skettenis 		tag = dwpcie_make_tag(sc, 0, dev, fn);
1759e1e95ce2Spatrick 		HWRITE4(sc, PCITAG_OFFSET(tag) | reg, data);
17607659d2d7Skettenis 		return;
17617659d2d7Skettenis 	}
17627659d2d7Skettenis 
17636a2b914cSpatrick 	if (bus == sc->sc_bus + 1) {
17646a2b914cSpatrick 		dwpcie_atu_config(sc, IATU_VIEWPORT_INDEX1,
17656a2b914cSpatrick 		    IATU_REGION_CTRL_1_TYPE_CFG0,
1766e1e95ce2Spatrick 		    sc->sc_conf_base, PCITAG_OFFSET(tag),
1767e1e95ce2Spatrick 		    sc->sc_conf_size);
17686a2b914cSpatrick 	} else {
17696a2b914cSpatrick 		dwpcie_atu_config(sc, IATU_VIEWPORT_INDEX1,
17706a2b914cSpatrick 		    IATU_REGION_CTRL_1_TYPE_CFG1,
1771e1e95ce2Spatrick 		    sc->sc_conf_base, PCITAG_OFFSET(tag),
1772e1e95ce2Spatrick 		    sc->sc_conf_size);
17736a2b914cSpatrick 	}
177401dd2b7cSkettenis 
177501dd2b7cSkettenis 	bus_space_write_4(sc->sc_iot, sc->sc_conf_ioh, reg, data);
177601dd2b7cSkettenis 
1777d2397242Skettenis 	if (sc->sc_num_viewport <= 2 && sc->sc_io_size > 0) {
17786a2b914cSpatrick 		dwpcie_atu_config(sc, IATU_VIEWPORT_INDEX1,
17796a2b914cSpatrick 		    IATU_REGION_CTRL_1_TYPE_IO, sc->sc_io_base,
17806a2b914cSpatrick 		    sc->sc_io_bus_addr, sc->sc_io_size);
17817659d2d7Skettenis 	}
178201dd2b7cSkettenis }
17837659d2d7Skettenis 
17847659d2d7Skettenis int
1785619b146dSpatrick dwpcie_probe_device_hook(void *v, struct pci_attach_args *pa)
1786619b146dSpatrick {
178719800b49Spatrick 	struct dwpcie_softc *sc = v;
178819800b49Spatrick 	uint16_t rid;
1789415019ceSpatrick 	int i;
179019800b49Spatrick 
179119800b49Spatrick 	rid = pci_requester_id(pa->pa_pc, pa->pa_tag);
179219800b49Spatrick 	pa->pa_dmat = iommu_device_map_pci(sc->sc_node, rid, pa->pa_dmat);
179319800b49Spatrick 
1794415019ceSpatrick 	for (i = 0; i < sc->sc_nranges; i++) {
1795415019ceSpatrick 		iommu_reserve_region_pci(sc->sc_node, rid,
1796415019ceSpatrick 		    sc->sc_ranges[i].pci_base, sc->sc_ranges[i].size);
1797415019ceSpatrick 	}
1798415019ceSpatrick 
1799619b146dSpatrick 	return 0;
1800619b146dSpatrick }
1801619b146dSpatrick 
1802619b146dSpatrick int
18037659d2d7Skettenis dwpcie_intr_map(struct pci_attach_args *pa, pci_intr_handle_t *ihp)
18047659d2d7Skettenis {
18057659d2d7Skettenis 	int pin = pa->pa_rawintrpin;
18067659d2d7Skettenis 
18077659d2d7Skettenis 	if (pin == 0 || pin > PCI_INTERRUPT_PIN_MAX)
18087659d2d7Skettenis 		return -1;
18097659d2d7Skettenis 
18107659d2d7Skettenis 	if (pa->pa_tag == 0)
18117659d2d7Skettenis 		return -1;
18127659d2d7Skettenis 
1813e410b95bSkettenis 	ihp->ih_pc = pa->pa_pc;
1814e410b95bSkettenis 	ihp->ih_tag = pa->pa_intrtag;
1815e410b95bSkettenis 	ihp->ih_intrpin = pa->pa_intrpin;
1816e410b95bSkettenis 	ihp->ih_type = PCI_INTX;
1817e33d8a25Skettenis 
1818e33d8a25Skettenis 	return 0;
18197659d2d7Skettenis }
18207659d2d7Skettenis 
18217659d2d7Skettenis const char *
1822e410b95bSkettenis dwpcie_intr_string(void *v, pci_intr_handle_t ih)
18237659d2d7Skettenis {
1824e410b95bSkettenis 	switch (ih.ih_type) {
1825e33d8a25Skettenis 	case PCI_MSI:
18267659d2d7Skettenis 		return "msi";
1827e33d8a25Skettenis 	case PCI_MSIX:
1828e33d8a25Skettenis 		return "msix";
1829e33d8a25Skettenis 	}
18307659d2d7Skettenis 
18317659d2d7Skettenis 	return "intx";
18327659d2d7Skettenis }
18337659d2d7Skettenis 
1834d5dfbc11Skettenis struct dwpcie_msi *
18359a76443bSkettenis dwpcie_msi_establish(struct dwpcie_softc *sc, pci_intr_handle_t *ihp,
18369a76443bSkettenis     int level, int (*func)(void *), void *arg, char *name)
1837d5dfbc11Skettenis {
18389a76443bSkettenis 	pci_chipset_tag_t pc = ihp->ih_pc;
18399a76443bSkettenis 	pcitag_t tag = ihp->ih_tag;
1840d5dfbc11Skettenis 	struct dwpcie_msi *dm;
18419a76443bSkettenis 	uint64_t msi_mask;
18429a76443bSkettenis 	int vec = ihp->ih_intrpin;
18439a76443bSkettenis 	int base, mme, nvec, off;
18449a76443bSkettenis 	pcireg_t reg;
1845d5dfbc11Skettenis 
18469a76443bSkettenis 	if (ihp->ih_type == PCI_MSI) {
18479a76443bSkettenis 		if (pci_get_capability(pc, tag, PCI_CAP_MSI, &off, &reg) == 0)
18489a76443bSkettenis 			panic("%s: no msi capability", __func__);
18499a76443bSkettenis 
18509a76443bSkettenis 		reg = pci_conf_read(ihp->ih_pc, ihp->ih_tag, off);
18519a76443bSkettenis 		mme = ((reg & PCI_MSI_MC_MME_MASK) >> PCI_MSI_MC_MME_SHIFT);
18529a76443bSkettenis 		if (vec >= (1 << mme))
18539a76443bSkettenis 			return NULL;
18549a76443bSkettenis 		if (reg & PCI_MSI_MC_C64)
18559a76443bSkettenis 			base = pci_conf_read(pc, tag, off + PCI_MSI_MD64);
18569a76443bSkettenis 		else
18579a76443bSkettenis 			base = pci_conf_read(pc, tag, off + PCI_MSI_MD32);
18589a76443bSkettenis 	} else {
18599a76443bSkettenis 		mme = 0;
18609a76443bSkettenis 		base = 0;
18619a76443bSkettenis 	}
18629a76443bSkettenis 
18639a76443bSkettenis 	if (vec == 0) {
18649a76443bSkettenis 		/*
18659a76443bSkettenis 		 * Pre-allocate all the requested vectors.  Remember
18669a76443bSkettenis 		 * the number of requested vectors such that we can
18679a76443bSkettenis 		 * deallocate them in one go.
18689a76443bSkettenis 		 */
18699a76443bSkettenis 		msi_mask = (1ULL << (1 << mme)) - 1;
18709a76443bSkettenis 		while (vec <= sc->sc_num_msi - (1 << mme)) {
18719a76443bSkettenis 			if ((sc->sc_msi_mask & (msi_mask << vec)) == 0) {
18729a76443bSkettenis 				sc->sc_msi_mask |= (msi_mask << vec);
1873d5dfbc11Skettenis 				break;
1874d5dfbc11Skettenis 			}
18759a76443bSkettenis 			vec += (1 << mme);
18769a76443bSkettenis 		}
18779a76443bSkettenis 		base = vec;
18789a76443bSkettenis 		nvec = (1 << mme);
18799a76443bSkettenis 	} else {
18809a76443bSkettenis 		KASSERT(ihp->ih_type == PCI_MSI);
18819a76443bSkettenis 		vec += base;
18829a76443bSkettenis 		nvec = 0;
18839a76443bSkettenis 	}
18849a76443bSkettenis 
18859a76443bSkettenis 	if (vec >= sc->sc_num_msi)
1886d5dfbc11Skettenis 		return NULL;
1887d5dfbc11Skettenis 
18889a76443bSkettenis 	if (ihp->ih_type == PCI_MSI) {
18899a76443bSkettenis 		if (reg & PCI_MSI_MC_C64)
18909a76443bSkettenis 			pci_conf_write(pc, tag, off + PCI_MSI_MD64, base);
18919a76443bSkettenis 		else
18929a76443bSkettenis 			pci_conf_write(pc, tag, off + PCI_MSI_MD32, base);
18939a76443bSkettenis 	}
18949a76443bSkettenis 
18959a76443bSkettenis 	dm = &sc->sc_msi[vec];
18969a76443bSkettenis 	KASSERT(dm->dm_func == NULL);
18979a76443bSkettenis 
1898d5dfbc11Skettenis 	dm->dm_func = func;
1899d5dfbc11Skettenis 	dm->dm_arg = arg;
1900d5dfbc11Skettenis 	dm->dm_ipl = level & IPL_IRQMASK;
1901d5dfbc11Skettenis 	dm->dm_flags = level & IPL_FLAGMASK;
1902d5dfbc11Skettenis 	dm->dm_vec = vec;
19039a76443bSkettenis 	dm->dm_nvec = nvec;
1904d5dfbc11Skettenis 	dm->dm_name = name;
1905d5dfbc11Skettenis 	if (name != NULL)
1906d5dfbc11Skettenis 		evcount_attach(&dm->dm_count, name, &dm->dm_vec);
1907d5dfbc11Skettenis 
1908d5dfbc11Skettenis 	/* Unmask the MSI. */
19099a76443bSkettenis 	HCLR4(sc, PCIE_MSI_INTR_MASK(vec / 32), (1U << (vec % 32)));
1910d5dfbc11Skettenis 
1911d5dfbc11Skettenis 	return dm;
1912d5dfbc11Skettenis }
1913d5dfbc11Skettenis 
1914d5dfbc11Skettenis void
1915d5dfbc11Skettenis dwpcie_msi_disestablish(struct dwpcie_softc *sc, struct dwpcie_msi *dm)
1916d5dfbc11Skettenis {
19179a76443bSkettenis 	uint64_t msi_mask = (1ULL << dm->dm_nvec) - 1;
19189a76443bSkettenis 
1919d5dfbc11Skettenis 	/* Mask the MSI. */
19209a76443bSkettenis 	HSET4(sc, PCIE_MSI_INTR_MASK(dm->dm_vec / 32),
19219a76443bSkettenis 	    (1U << (dm->dm_vec % 32)));
1922d5dfbc11Skettenis 
1923d5dfbc11Skettenis 	if (dm->dm_name)
1924d5dfbc11Skettenis 		evcount_detach(&dm->dm_count);
1925d5dfbc11Skettenis 	dm->dm_func = NULL;
19269a76443bSkettenis 
19279a76443bSkettenis 	/*
19289a76443bSkettenis 	 * Unallocate all allocated vetcors if this is the first
19299a76443bSkettenis 	 * vector for the device.
19309a76443bSkettenis 	 */
19319a76443bSkettenis 	sc->sc_msi_mask &= ~(msi_mask << dm->dm_vec);
1932d5dfbc11Skettenis }
1933d5dfbc11Skettenis 
19347659d2d7Skettenis void *
1935e410b95bSkettenis dwpcie_intr_establish(void *v, pci_intr_handle_t ih, int level,
1936d67371fdSpatrick     struct cpu_info *ci, int (*func)(void *), void *arg, char *name)
19377659d2d7Skettenis {
19387659d2d7Skettenis 	struct dwpcie_softc *sc = v;
1939cc0ede06Spatrick 	struct dwpcie_intr_handle *pih;
1940d5dfbc11Skettenis 	void *cookie = NULL;
19417659d2d7Skettenis 
1942e410b95bSkettenis 	KASSERT(ih.ih_type != PCI_NONE);
1943e410b95bSkettenis 
1944e410b95bSkettenis 	if (ih.ih_type != PCI_INTX) {
1945d5dfbc11Skettenis 		struct dwpcie_msi *dm = NULL;
1946d5dfbc11Skettenis 		bus_dma_tag_t dmat = ih.ih_dmat;
1947d5dfbc11Skettenis 		bus_dma_segment_t seg;
1948d5dfbc11Skettenis 		bus_dmamap_t map;
19497659d2d7Skettenis 		uint64_t addr, data;
19507659d2d7Skettenis 
1951d5dfbc11Skettenis 		if (sc->sc_msi_addr) {
19529a76443bSkettenis 			dm = dwpcie_msi_establish(sc, &ih, level, func, arg, name);
1953d5dfbc11Skettenis 			if (dm == NULL)
1954d5dfbc11Skettenis 				return NULL;
1955d5dfbc11Skettenis 			addr = sc->sc_msi_addr;
1956d5dfbc11Skettenis 			data = dm->dm_vec;
1957d5dfbc11Skettenis 		} else {
1958d5dfbc11Skettenis 			/*
1959d5dfbc11Skettenis 			 * Assume hardware passes Requester ID as
1960d5dfbc11Skettenis 			 * sideband data.
1961d5dfbc11Skettenis 			 */
196256d02c00Skettenis 			addr = ih.ih_intrpin;
1963e410b95bSkettenis 			data = pci_requester_id(ih.ih_pc, ih.ih_tag);
1964d67371fdSpatrick 			cookie = fdt_intr_establish_msi_cpu(sc->sc_node, &addr,
1965d67371fdSpatrick 			    &data, level, ci, func, arg, (void *)name);
19667659d2d7Skettenis 			if (cookie == NULL)
19677659d2d7Skettenis 				return NULL;
1968d5dfbc11Skettenis 		}
19697659d2d7Skettenis 
1970d5dfbc11Skettenis 		pih = malloc(sizeof(*pih), M_DEVBUF, M_WAITOK | M_ZERO);
1971cc0ede06Spatrick 		pih->pih_ih.ih_ic = &dwpcie_ic;
1972cc0ede06Spatrick 		pih->pih_ih.ih_ih = cookie;
1973d5dfbc11Skettenis 		pih->pih_sc = sc;
1974d5dfbc11Skettenis 		pih->pih_dm = dm;
19757659d2d7Skettenis 
1976d5dfbc11Skettenis 		if (sc->sc_msi_addr == 0) {
1977d5dfbc11Skettenis 			if (bus_dmamap_create(dmat, sizeof(uint32_t), 1,
1978d5dfbc11Skettenis 			    sizeof(uint32_t), 0, BUS_DMA_WAITOK, &map)) {
1979cc0ede06Spatrick 				free(pih, M_DEVBUF, sizeof(*pih));
1980cc0ede06Spatrick 				fdt_intr_disestablish(cookie);
1981cc0ede06Spatrick 				return NULL;
1982cc0ede06Spatrick 			}
1983cc0ede06Spatrick 
1984cc0ede06Spatrick 			memset(&seg, 0, sizeof(seg));
1985cc0ede06Spatrick 			seg.ds_addr = addr;
1986cc0ede06Spatrick 			seg.ds_len = sizeof(uint32_t);
1987cc0ede06Spatrick 
1988d5dfbc11Skettenis 			if (bus_dmamap_load_raw(dmat, map, &seg, 1,
1989d5dfbc11Skettenis 			    sizeof(uint32_t), BUS_DMA_WAITOK)) {
1990d5dfbc11Skettenis 				bus_dmamap_destroy(dmat, map);
1991cc0ede06Spatrick 				free(pih, M_DEVBUF, sizeof(*pih));
1992cc0ede06Spatrick 				fdt_intr_disestablish(cookie);
1993cc0ede06Spatrick 				return NULL;
1994cc0ede06Spatrick 			}
1995cc0ede06Spatrick 
1996d5dfbc11Skettenis 			addr = map->dm_segs[0].ds_addr;
1997d5dfbc11Skettenis 			pih->pih_dmat = dmat;
1998d5dfbc11Skettenis 			pih->pih_map = map;
1999d5dfbc11Skettenis 		}
2000d5dfbc11Skettenis 
2001e410b95bSkettenis 		if (ih.ih_type == PCI_MSIX) {
2002e410b95bSkettenis 			pci_msix_enable(ih.ih_pc, ih.ih_tag,
2003e410b95bSkettenis 			    &sc->sc_bus_memt, ih.ih_intrpin, addr, data);
2004e33d8a25Skettenis 		} else
2005e410b95bSkettenis 			pci_msi_enable(ih.ih_pc, ih.ih_tag, addr, data);
20067659d2d7Skettenis 	} else {
20077659d2d7Skettenis 		int bus, dev, fn;
20087659d2d7Skettenis 		uint32_t reg[4];
20097659d2d7Skettenis 
2010e410b95bSkettenis 		dwpcie_decompose_tag(sc, ih.ih_tag, &bus, &dev, &fn);
20117659d2d7Skettenis 
20127659d2d7Skettenis 		reg[0] = bus << 16 | dev << 11 | fn << 8;
20137659d2d7Skettenis 		reg[1] = reg[2] = 0;
2014e410b95bSkettenis 		reg[3] = ih.ih_intrpin;
20157659d2d7Skettenis 
2016d67371fdSpatrick 		cookie = fdt_intr_establish_imap_cpu(sc->sc_node, reg,
2017d67371fdSpatrick 		    sizeof(reg), level, ci, func, arg, name);
2018cc0ede06Spatrick 		if (cookie == NULL)
2019cc0ede06Spatrick 			return NULL;
2020cc0ede06Spatrick 
2021d5dfbc11Skettenis 		pih = malloc(sizeof(*pih), M_DEVBUF, M_WAITOK | M_ZERO);
2022cc0ede06Spatrick 		pih->pih_ih.ih_ic = &dwpcie_ic;
2023cc0ede06Spatrick 		pih->pih_ih.ih_ih = cookie;
20247659d2d7Skettenis 	}
20257659d2d7Skettenis 
2026cc0ede06Spatrick 	return pih;
20277659d2d7Skettenis }
20287659d2d7Skettenis 
20297659d2d7Skettenis void
20307659d2d7Skettenis dwpcie_intr_disestablish(void *v, void *cookie)
20317659d2d7Skettenis {
2032cc0ede06Spatrick 	struct dwpcie_intr_handle *pih = cookie;
2033cc0ede06Spatrick 
2034d5dfbc11Skettenis 	if (pih->pih_dm)
2035d5dfbc11Skettenis 		dwpcie_msi_disestablish(pih->pih_sc, pih->pih_dm);
2036d5dfbc11Skettenis 	else
2037cc0ede06Spatrick 		fdt_intr_disestablish(pih->pih_ih.ih_ih);
2038d5dfbc11Skettenis 
2039cc0ede06Spatrick 	if (pih->pih_dmat) {
2040cc0ede06Spatrick 		bus_dmamap_unload(pih->pih_dmat, pih->pih_map);
2041cc0ede06Spatrick 		bus_dmamap_destroy(pih->pih_dmat, pih->pih_map);
2042cc0ede06Spatrick 	}
2043d5dfbc11Skettenis 
2044cc0ede06Spatrick 	free(pih, M_DEVBUF, sizeof(*pih));
20457659d2d7Skettenis }
204671fe361bSkettenis 
204771fe361bSkettenis int
204871fe361bSkettenis dwpcie_bs_iomap(bus_space_tag_t t, bus_addr_t addr, bus_size_t size,
204971fe361bSkettenis     int flags, bus_space_handle_t *bshp)
205071fe361bSkettenis {
205171fe361bSkettenis 	struct dwpcie_softc *sc = t->bus_private;
205271fe361bSkettenis 	int i;
205371fe361bSkettenis 
2054d43be508Skettenis 	for (i = 0; i < sc->sc_nranges; i++) {
205571fe361bSkettenis 		uint64_t pci_start = sc->sc_ranges[i].pci_base;
205671fe361bSkettenis 		uint64_t pci_end = pci_start + sc->sc_ranges[i].size;
205771fe361bSkettenis 		uint64_t phys_start = sc->sc_ranges[i].phys_base;
205871fe361bSkettenis 
205971fe361bSkettenis 		if ((sc->sc_ranges[i].flags & 0x03000000) == 0x01000000 &&
206071fe361bSkettenis 		    addr >= pci_start && addr + size <= pci_end) {
206171fe361bSkettenis 			return bus_space_map(sc->sc_iot,
206271fe361bSkettenis 			    addr - pci_start + phys_start, size, flags, bshp);
206371fe361bSkettenis 		}
206471fe361bSkettenis 	}
206571fe361bSkettenis 
206671fe361bSkettenis 	return ENXIO;
206771fe361bSkettenis }
206871fe361bSkettenis 
206971fe361bSkettenis int
207071fe361bSkettenis dwpcie_bs_memmap(bus_space_tag_t t, bus_addr_t addr, bus_size_t size,
207171fe361bSkettenis     int flags, bus_space_handle_t *bshp)
207271fe361bSkettenis {
207371fe361bSkettenis 	struct dwpcie_softc *sc = t->bus_private;
207471fe361bSkettenis 	int i;
207571fe361bSkettenis 
2076d43be508Skettenis 	for (i = 0; i < sc->sc_nranges; i++) {
207771fe361bSkettenis 		uint64_t pci_start = sc->sc_ranges[i].pci_base;
207871fe361bSkettenis 		uint64_t pci_end = pci_start + sc->sc_ranges[i].size;
207971fe361bSkettenis 		uint64_t phys_start = sc->sc_ranges[i].phys_base;
208071fe361bSkettenis 
2081254dd109Skettenis 		if ((sc->sc_ranges[i].flags & 0x02000000) == 0x02000000 &&
208271fe361bSkettenis 		    addr >= pci_start && addr + size <= pci_end) {
208371fe361bSkettenis 			return bus_space_map(sc->sc_iot,
208471fe361bSkettenis 			    addr - pci_start + phys_start, size, flags, bshp);
208571fe361bSkettenis 		}
208671fe361bSkettenis 	}
208771fe361bSkettenis 
208871fe361bSkettenis 	return ENXIO;
208971fe361bSkettenis }
2090