xref: /netbsd-src/sys/arch/arm/samsung/exynos_usbdrdphy.c (revision 6e54367a22fbc89a1139d033e95bec0c0cf0975b)
1*6e54367aSthorpej /* $NetBSD: exynos_usbdrdphy.c,v 1.6 2021/01/27 03:10:19 thorpej Exp $ */
21095f2f4Sjmcneill 
31095f2f4Sjmcneill /*-
41095f2f4Sjmcneill  * Copyright (c) 2018 Jared McNeill <jmcneill@invisible.ca>
51095f2f4Sjmcneill  * All rights reserved.
61095f2f4Sjmcneill  *
71095f2f4Sjmcneill  * Redistribution and use in source and binary forms, with or without
81095f2f4Sjmcneill  * modification, are permitted provided that the following conditions
91095f2f4Sjmcneill  * are met:
101095f2f4Sjmcneill  * 1. Redistributions of source code must retain the above copyright
111095f2f4Sjmcneill  *    notice, this list of conditions and the following disclaimer.
121095f2f4Sjmcneill  * 2. Redistributions in binary form must reproduce the above copyright
131095f2f4Sjmcneill  *    notice, this list of conditions and the following disclaimer in the
141095f2f4Sjmcneill  *    documentation and/or other materials provided with the distribution.
151095f2f4Sjmcneill  *
161095f2f4Sjmcneill  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
171095f2f4Sjmcneill  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
181095f2f4Sjmcneill  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
191095f2f4Sjmcneill  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
201095f2f4Sjmcneill  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
211095f2f4Sjmcneill  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
221095f2f4Sjmcneill  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
231095f2f4Sjmcneill  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
241095f2f4Sjmcneill  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
251095f2f4Sjmcneill  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
261095f2f4Sjmcneill  * POSSIBILITY OF SUCH DAMAGE.
271095f2f4Sjmcneill  */
281095f2f4Sjmcneill 
291095f2f4Sjmcneill #include <sys/cdefs.h>
301095f2f4Sjmcneill 
31*6e54367aSthorpej __KERNEL_RCSID(0, "$NetBSD: exynos_usbdrdphy.c,v 1.6 2021/01/27 03:10:19 thorpej Exp $");
321095f2f4Sjmcneill 
331095f2f4Sjmcneill #include <sys/param.h>
341095f2f4Sjmcneill #include <sys/bus.h>
351095f2f4Sjmcneill #include <sys/device.h>
361095f2f4Sjmcneill #include <sys/intr.h>
371095f2f4Sjmcneill #include <sys/systm.h>
381095f2f4Sjmcneill #include <sys/kmem.h>
391095f2f4Sjmcneill 
401095f2f4Sjmcneill #include <dev/fdt/fdtvar.h>
411095f2f4Sjmcneill #include <dev/fdt/syscon.h>
421095f2f4Sjmcneill 
431095f2f4Sjmcneill /*
441095f2f4Sjmcneill  * PHY Registers
451095f2f4Sjmcneill  */
461095f2f4Sjmcneill #define	PHY_LINK_SYSTEM			0x04
471095f2f4Sjmcneill #define	 PHY_LINK_SYSTEM_XHCI_VERCTL	__BIT(27)
481095f2f4Sjmcneill #define	 PHY_LINK_SYSTEM_FLADJ		__BITS(6,1)
491095f2f4Sjmcneill #define	PHY_UTMI			0x08
501095f2f4Sjmcneill #define	 PHY_UTMI_OTGDISABLE		__BIT(6)
511095f2f4Sjmcneill #define	PHY_CLK_RST			0x10
521095f2f4Sjmcneill #define	 PHY_CLK_RST_SSC_REFCLKSEL	__BITS(30,23)
531095f2f4Sjmcneill #define	 PHY_CLK_RST_SSC_EN		__BIT(20)
541095f2f4Sjmcneill #define	 PHY_CLK_RST_REF_SSP_EN		__BIT(19)
551095f2f4Sjmcneill #define	 PHY_CLK_RST_MPLL_MULT		__BITS(17,11)
561095f2f4Sjmcneill #define	  PHY_CLK_RST_MPLL_MULT_24M	0x68
571095f2f4Sjmcneill #define	 PHY_CLK_RST_FSEL		__BITS(10,5)
585d85bfa1Sjmcneill #define	  PHY_CLK_RST_FSEL_24M		0x5
591095f2f4Sjmcneill #define	 PHY_CLK_RST_RETENABLEN		__BIT(4)
601095f2f4Sjmcneill #define	 PHY_CLK_RST_REFCLKSEL		__BITS(3,2)
611095f2f4Sjmcneill #define	  PHY_CLK_RST_REFCLKSEL_EXT	3
621095f2f4Sjmcneill #define	 PHY_CLK_RST_PORTRESET		__BIT(1)
631095f2f4Sjmcneill #define	 PHY_CLK_RST_COMMONONN		__BIT(0)
641095f2f4Sjmcneill #define	PHY_REG0			0x14
651095f2f4Sjmcneill #define	PHY_PARAM0			0x1c
661095f2f4Sjmcneill #define	 PHY_PARAM0_REF_USE_PAD		__BIT(31)
671095f2f4Sjmcneill #define	 PHY_PARAM0_REF_LOSLEVEL	__BITS(30,26)
681095f2f4Sjmcneill #define	PHY_PARAM1			0x20
691095f2f4Sjmcneill #define	 PHY_PARAM1_TXDEEMPH		__BITS(4,0)
701095f2f4Sjmcneill #define	PHY_TEST			0x28
711095f2f4Sjmcneill #define	 PHY_TEST_POWERDOWN_SSP		__BIT(3)
721095f2f4Sjmcneill #define	 PHY_TEST_POWERDOWN_HSP		__BIT(2)
731095f2f4Sjmcneill #define	PHY_BATCHG			0x30
741095f2f4Sjmcneill #define	 PHY_BATCHG_UTMI_CLKSEL		__BIT(2)
751095f2f4Sjmcneill #define	PHY_RESUME			0x34
761095f2f4Sjmcneill 
771095f2f4Sjmcneill /*
781095f2f4Sjmcneill  * PMU Registers
791095f2f4Sjmcneill  */
801095f2f4Sjmcneill #define	USBDRD_PHY_CTRL(n)		(0x704 + (n) * 4)
811095f2f4Sjmcneill #define	 USBDRD_PHY_CTRL_EN		__BIT(0)
821095f2f4Sjmcneill 
831095f2f4Sjmcneill static int exynos_usbdrdphy_match(device_t, cfdata_t, void *);
841095f2f4Sjmcneill static void exynos_usbdrdphy_attach(device_t, device_t, void *);
851095f2f4Sjmcneill 
861095f2f4Sjmcneill enum {
871095f2f4Sjmcneill 	PHY_ID_UTMI_PLUS = 0,
881095f2f4Sjmcneill 	PHY_ID_PIPE3,
891095f2f4Sjmcneill 	NPHY_ID
901095f2f4Sjmcneill };
911095f2f4Sjmcneill 
92646c0f59Sthorpej static const struct device_compatible_entry compat_data[] = {
93646c0f59Sthorpej 	{ .compat = "samsung,exynos5420-usbdrd-phy" },
94ec189949Sthorpej 	DEVICE_COMPAT_EOL
951095f2f4Sjmcneill };
961095f2f4Sjmcneill 
971095f2f4Sjmcneill struct exynos_usbdrdphy_softc;
981095f2f4Sjmcneill 
991095f2f4Sjmcneill struct exynos_usbdrdphy {
1001095f2f4Sjmcneill 	struct exynos_usbdrdphy_softc *phy_sc;
1011095f2f4Sjmcneill 	u_int			phy_index;
1021095f2f4Sjmcneill };
1031095f2f4Sjmcneill 
1041095f2f4Sjmcneill struct exynos_usbdrdphy_softc {
1051095f2f4Sjmcneill 	device_t		sc_dev;
1061095f2f4Sjmcneill 	bus_space_tag_t		sc_bst;
1071095f2f4Sjmcneill 	bus_space_handle_t	sc_bsh;
1081095f2f4Sjmcneill 	int			sc_phandle;
1091095f2f4Sjmcneill 	struct syscon		*sc_pmureg;
1101095f2f4Sjmcneill 
1111095f2f4Sjmcneill 	struct exynos_usbdrdphy	*sc_phy;
1121095f2f4Sjmcneill 	u_int			sc_nphy;
1131095f2f4Sjmcneill 
1141095f2f4Sjmcneill 	struct fdtbus_gpio_pin	*sc_gpio_id_det;
1151095f2f4Sjmcneill 	struct fdtbus_gpio_pin	*sc_gpio_vbus_det;
1161095f2f4Sjmcneill };
1171095f2f4Sjmcneill 
1181095f2f4Sjmcneill #define	PHY_READ(sc, reg)				\
1191095f2f4Sjmcneill 	bus_space_read_4((sc)->sc_bst, (sc)->sc_bsh, (reg))
1201095f2f4Sjmcneill #define	PHY_WRITE(sc, reg, val)				\
1211095f2f4Sjmcneill 	bus_space_write_4((sc)->sc_bst, (sc)->sc_bsh, (reg), (val))
1221095f2f4Sjmcneill 
1231095f2f4Sjmcneill CFATTACH_DECL_NEW(exynos_usbdrdphy, sizeof(struct exynos_usbdrdphy_softc),
1241095f2f4Sjmcneill 	exynos_usbdrdphy_match, exynos_usbdrdphy_attach, NULL, NULL);
1251095f2f4Sjmcneill 
1261095f2f4Sjmcneill static void *
exynos_usbdrdphy_acquire(device_t dev,const void * data,size_t len)1271095f2f4Sjmcneill exynos_usbdrdphy_acquire(device_t dev, const void *data, size_t len)
1281095f2f4Sjmcneill {
1291095f2f4Sjmcneill 	struct exynos_usbdrdphy_softc * const sc = device_private(dev);
1301095f2f4Sjmcneill 
1311095f2f4Sjmcneill 	if (len != 4)
1321095f2f4Sjmcneill 		return NULL;
1331095f2f4Sjmcneill 
1341095f2f4Sjmcneill 	const u_int index = be32dec(data);
1351095f2f4Sjmcneill 	if (index >= sc->sc_nphy)
1361095f2f4Sjmcneill 		return NULL;
1371095f2f4Sjmcneill 
1381095f2f4Sjmcneill 	return &sc->sc_phy[index];
1391095f2f4Sjmcneill }
1401095f2f4Sjmcneill 
1411095f2f4Sjmcneill static void
exynos_usbdrdphy_release(device_t dev,void * priv)1421095f2f4Sjmcneill exynos_usbdrdphy_release(device_t dev, void *priv)
1431095f2f4Sjmcneill {
1441095f2f4Sjmcneill }
1451095f2f4Sjmcneill 
1461095f2f4Sjmcneill static int
exynos_usbdrdphy_enable(device_t dev,void * priv,bool enable)1471095f2f4Sjmcneill exynos_usbdrdphy_enable(device_t dev, void *priv, bool enable)
1481095f2f4Sjmcneill {
1491095f2f4Sjmcneill 	struct exynos_usbdrdphy * const phy = priv;
1501095f2f4Sjmcneill 	struct exynos_usbdrdphy_softc * const sc = phy->phy_sc;
1511095f2f4Sjmcneill 	uint32_t val;
1521095f2f4Sjmcneill 
1531095f2f4Sjmcneill 	syscon_lock(sc->sc_pmureg);
1541095f2f4Sjmcneill 	val = syscon_read_4(sc->sc_pmureg, USBDRD_PHY_CTRL(phy->phy_index));
1551095f2f4Sjmcneill 	if (enable)
1561095f2f4Sjmcneill 		val |= USBDRD_PHY_CTRL_EN;
1571095f2f4Sjmcneill 	else
1581095f2f4Sjmcneill 		val &= ~USBDRD_PHY_CTRL_EN;
1591095f2f4Sjmcneill 	syscon_write_4(sc->sc_pmureg, USBDRD_PHY_CTRL(phy->phy_index), val);
1601095f2f4Sjmcneill 	syscon_unlock(sc->sc_pmureg);
1611095f2f4Sjmcneill 
1621095f2f4Sjmcneill 	return 0;
1631095f2f4Sjmcneill }
1641095f2f4Sjmcneill 
1651095f2f4Sjmcneill const struct fdtbus_phy_controller_func exynos_usbdrdphy_funcs = {
1661095f2f4Sjmcneill 	.acquire = exynos_usbdrdphy_acquire,
1671095f2f4Sjmcneill 	.release = exynos_usbdrdphy_release,
1681095f2f4Sjmcneill 	.enable = exynos_usbdrdphy_enable,
1691095f2f4Sjmcneill };
1701095f2f4Sjmcneill 
1711095f2f4Sjmcneill static void
exynos_usbdrdphy_init(struct exynos_usbdrdphy_softc * sc)1721095f2f4Sjmcneill exynos_usbdrdphy_init(struct exynos_usbdrdphy_softc *sc)
1731095f2f4Sjmcneill {
1741095f2f4Sjmcneill 	uint32_t val;
1751095f2f4Sjmcneill 
1761095f2f4Sjmcneill 	PHY_WRITE(sc, PHY_REG0, 0);
1771095f2f4Sjmcneill 
1781095f2f4Sjmcneill 	val = PHY_READ(sc, PHY_PARAM0);
1791095f2f4Sjmcneill 	val &= ~PHY_PARAM0_REF_USE_PAD;
1801095f2f4Sjmcneill 	val &= ~PHY_PARAM0_REF_LOSLEVEL;
1811095f2f4Sjmcneill 	val |= __SHIFTIN(9, PHY_PARAM0_REF_LOSLEVEL);
1821095f2f4Sjmcneill 	PHY_WRITE(sc, PHY_PARAM0, val);
1831095f2f4Sjmcneill 
1841095f2f4Sjmcneill 	PHY_WRITE(sc, PHY_RESUME, 0);
1851095f2f4Sjmcneill 
1861095f2f4Sjmcneill 	val = PHY_READ(sc, PHY_LINK_SYSTEM);
1871095f2f4Sjmcneill 	val |= PHY_LINK_SYSTEM_XHCI_VERCTL;
1881095f2f4Sjmcneill 	val &= ~PHY_LINK_SYSTEM_FLADJ;
1891095f2f4Sjmcneill 	val |= __SHIFTIN(0x20, PHY_LINK_SYSTEM_FLADJ);
1901095f2f4Sjmcneill 	PHY_WRITE(sc, PHY_LINK_SYSTEM, val);
1911095f2f4Sjmcneill 
1921095f2f4Sjmcneill 	val = PHY_READ(sc, PHY_PARAM1);
1931095f2f4Sjmcneill 	val &= ~PHY_PARAM1_TXDEEMPH;
1941095f2f4Sjmcneill 	val |= __SHIFTIN(0x1c, PHY_PARAM1_TXDEEMPH);
1951095f2f4Sjmcneill 	PHY_WRITE(sc, PHY_PARAM1, val);
1961095f2f4Sjmcneill 
1971095f2f4Sjmcneill 	val = PHY_READ(sc, PHY_BATCHG);
1981095f2f4Sjmcneill 	val |= PHY_BATCHG_UTMI_CLKSEL;
1991095f2f4Sjmcneill 	PHY_WRITE(sc, PHY_BATCHG, val);
2001095f2f4Sjmcneill 
2011095f2f4Sjmcneill 	val = PHY_READ(sc, PHY_TEST);
2021095f2f4Sjmcneill 	val &= ~PHY_TEST_POWERDOWN_SSP;
2031095f2f4Sjmcneill 	val &= ~PHY_TEST_POWERDOWN_HSP;
2041095f2f4Sjmcneill 	PHY_WRITE(sc, PHY_TEST, val);
2051095f2f4Sjmcneill 
2061095f2f4Sjmcneill 	PHY_WRITE(sc, PHY_UTMI, PHY_UTMI_OTGDISABLE);
2071095f2f4Sjmcneill 
2081095f2f4Sjmcneill 	val = __SHIFTIN(PHY_CLK_RST_REFCLKSEL_EXT, PHY_CLK_RST_REFCLKSEL);
2091095f2f4Sjmcneill 	val |= __SHIFTIN(PHY_CLK_RST_FSEL_24M, PHY_CLK_RST_FSEL);
2101095f2f4Sjmcneill 	val |= __SHIFTIN(PHY_CLK_RST_MPLL_MULT_24M, PHY_CLK_RST_MPLL_MULT);
2111095f2f4Sjmcneill 	val |= __SHIFTIN(0x88, PHY_CLK_RST_SSC_REFCLKSEL);
2121095f2f4Sjmcneill 	val |= PHY_CLK_RST_PORTRESET;
2131095f2f4Sjmcneill 	val |= PHY_CLK_RST_RETENABLEN;
2141095f2f4Sjmcneill 	val |= PHY_CLK_RST_REF_SSP_EN;
2151095f2f4Sjmcneill 	val |= PHY_CLK_RST_SSC_EN;
2161095f2f4Sjmcneill 	val |= PHY_CLK_RST_COMMONONN;
2171095f2f4Sjmcneill 	PHY_WRITE(sc, PHY_CLK_RST, val);
2181095f2f4Sjmcneill 
2191095f2f4Sjmcneill 	delay(50000);
2201095f2f4Sjmcneill 
2211095f2f4Sjmcneill 	val &= ~PHY_CLK_RST_PORTRESET;
2221095f2f4Sjmcneill 	PHY_WRITE(sc, PHY_CLK_RST, val);
2231095f2f4Sjmcneill }
2241095f2f4Sjmcneill 
2251095f2f4Sjmcneill static int
exynos_usbdrdphy_match(device_t parent,cfdata_t cf,void * aux)2261095f2f4Sjmcneill exynos_usbdrdphy_match(device_t parent, cfdata_t cf, void *aux)
2271095f2f4Sjmcneill {
2281095f2f4Sjmcneill 	struct fdt_attach_args * const faa = aux;
2291095f2f4Sjmcneill 
230*6e54367aSthorpej 	return of_compatible_match(faa->faa_phandle, compat_data);
2311095f2f4Sjmcneill }
2321095f2f4Sjmcneill 
2331095f2f4Sjmcneill static void
exynos_usbdrdphy_attach(device_t parent,device_t self,void * aux)2341095f2f4Sjmcneill exynos_usbdrdphy_attach(device_t parent, device_t self, void *aux)
2351095f2f4Sjmcneill {
2361095f2f4Sjmcneill 	struct exynos_usbdrdphy_softc * const sc = device_private(self);
2371095f2f4Sjmcneill 	struct fdt_attach_args * const faa = aux;
2381095f2f4Sjmcneill 	const int phandle = faa->faa_phandle;
2391095f2f4Sjmcneill 	struct clk *clk;
2401095f2f4Sjmcneill 	bus_addr_t addr;
2411095f2f4Sjmcneill 	bus_size_t size;
2421095f2f4Sjmcneill 	u_int n;
2431095f2f4Sjmcneill 
2441095f2f4Sjmcneill 	if (fdtbus_get_reg(phandle, 0, &addr, &size) != 0) {
2451095f2f4Sjmcneill 		aprint_error(": couldn't get phy registers\n");
2461095f2f4Sjmcneill 		return;
2471095f2f4Sjmcneill 	}
2481095f2f4Sjmcneill 
2491095f2f4Sjmcneill 	sc->sc_dev = self;
2501095f2f4Sjmcneill 	sc->sc_phandle = phandle;
2511095f2f4Sjmcneill 	sc->sc_bst = faa->faa_bst;
2521095f2f4Sjmcneill 	if (bus_space_map(sc->sc_bst, addr, size, 0, &sc->sc_bsh) != 0) {
2531095f2f4Sjmcneill 		aprint_error(": couldn't map phy registers\n");
2541095f2f4Sjmcneill 		return;
2551095f2f4Sjmcneill 	}
2561095f2f4Sjmcneill 
2571095f2f4Sjmcneill 	sc->sc_nphy = NPHY_ID;
2581095f2f4Sjmcneill 	sc->sc_phy = kmem_alloc(sizeof(*sc->sc_phy) * sc->sc_nphy, KM_SLEEP);
2591095f2f4Sjmcneill 	for (n = 0; n < sc->sc_nphy; n++) {
2601095f2f4Sjmcneill 		sc->sc_phy[n].phy_sc = sc;
2611095f2f4Sjmcneill 		sc->sc_phy[n].phy_index = n;
2621095f2f4Sjmcneill 	}
2631095f2f4Sjmcneill 
2641095f2f4Sjmcneill 	sc->sc_pmureg = fdtbus_syscon_acquire(phandle, "samsung,pmu-syscon");
2651095f2f4Sjmcneill 	if (sc->sc_pmureg == NULL) {
2661095f2f4Sjmcneill 		aprint_error(": couldn't acquire pmureg syscon\n");
2671095f2f4Sjmcneill 		return;
2681095f2f4Sjmcneill 	}
2691095f2f4Sjmcneill 
2701095f2f4Sjmcneill 	/* Enable clocks */
2711095f2f4Sjmcneill 	clk = fdtbus_clock_get(phandle, "phy");
2721095f2f4Sjmcneill 	if (clk == NULL || clk_enable(clk) != 0) {
2731095f2f4Sjmcneill 		aprint_error(": couldn't enable phy clock\n");
2741095f2f4Sjmcneill 		return;
2751095f2f4Sjmcneill 	}
2761095f2f4Sjmcneill 	clk = fdtbus_clock_get(phandle, "ref");
2771095f2f4Sjmcneill 	if (clk == NULL || clk_enable(clk) != 0) {
2781095f2f4Sjmcneill 		aprint_error(": couldn't enable ref clock\n");
2791095f2f4Sjmcneill 		return;
2801095f2f4Sjmcneill 	}
2811095f2f4Sjmcneill 
2821095f2f4Sjmcneill 	aprint_naive("\n");
2831095f2f4Sjmcneill 	aprint_normal(": USB DRD PHY\n");
2841095f2f4Sjmcneill 
2851095f2f4Sjmcneill 	exynos_usbdrdphy_init(sc);
2861095f2f4Sjmcneill 
2871095f2f4Sjmcneill 	fdtbus_register_phy_controller(self, phandle, &exynos_usbdrdphy_funcs);
2881095f2f4Sjmcneill }
289