From: kettenis Date: Sun, 12 Mar 2023 14:29:50 +0000 (+0000) Subject: Add rkcomphy(3), a driver for the "naneng" combo PHY found on the RK356x X-Git-Url: http://artulab.com/gitweb/?a=commitdiff_plain;h=e1414c5087eac83c0386bb4ea9a6f00d9399c291;p=openbsd Add rkcomphy(3), a driver for the "naneng" combo PHY found on the RK356x (and RK3588). This is a PIPE PHY with support for PCIe, SATA, USB3, SGMII and QSGMII. For now only PCIe, SATA and USB3 support are implemented. SATA support has not been tested. Also add the refernce clocks needed by the PHYs to rkclock(4). ok mlarkin@ --- diff --git a/sys/arch/arm64/conf/GENERIC b/sys/arch/arm64/conf/GENERIC index 4063fcce22e..d810a158300 100644 --- a/sys/arch/arm64/conf/GENERIC +++ b/sys/arch/arm64/conf/GENERIC @@ -1,4 +1,4 @@ -# $OpenBSD: GENERIC,v 1.260 2023/03/04 22:36:15 kettenis Exp $ +# $OpenBSD: GENERIC,v 1.261 2023/03/12 14:29:50 kettenis Exp $ # # GENERIC machine description file # @@ -290,6 +290,7 @@ sfp* at fdt? rkclock* at fdt? early 1 rkgrf* at fdt? early 1 rkpinctrl* at fdt? early 1 +rkcomphy* at fdt? early 1 rktcphy* at fdt? early 1 rkanxdp* at fdt? rkdrm* at fdt? diff --git a/sys/arch/arm64/conf/RAMDISK b/sys/arch/arm64/conf/RAMDISK index f0cd7946a61..214391d84cc 100644 --- a/sys/arch/arm64/conf/RAMDISK +++ b/sys/arch/arm64/conf/RAMDISK @@ -1,4 +1,4 @@ -# $OpenBSD: RAMDISK,v 1.191 2023/03/04 22:48:00 kettenis Exp $ +# $OpenBSD: RAMDISK,v 1.192 2023/03/12 14:29:50 kettenis Exp $ machine arm64 maxusers 4 @@ -221,6 +221,7 @@ sfp* at fdt? rkclock* at fdt? early 1 rkgrf* at fdt? early 1 rkpinctrl* at fdt? early 1 +rkcomphy* at fdt? early 1 rktcphy* at fdt? early 1 rkemmcphy* at fdt? rkgpio* at fdt? diff --git a/sys/dev/fdt/files.fdt b/sys/dev/fdt/files.fdt index f1dfa0eda7a..26663e9ab40 100644 --- a/sys/dev/fdt/files.fdt +++ b/sys/dev/fdt/files.fdt @@ -1,4 +1,4 @@ -# $OpenBSD: files.fdt,v 1.179 2023/02/14 08:26:59 kettenis Exp $ +# $OpenBSD: files.fdt,v 1.180 2023/03/12 14:29:50 kettenis Exp $ # # Config file and device description for machine-independent FDT code. # Included by ports that need it. @@ -341,6 +341,10 @@ device rkclock attach rkclock at fdt file dev/fdt/rkclock.c rkclock +device rkcomphy +attach rkcomphy at fdt +file dev/fdt/rkcomphy.c + device rkdrm: drmbase, wsemuldisplaydev, rasops15, rasops16, rasops24, rasops32 attach rkdrm at fdt file dev/fdt/rkdrm.c rkdrm diff --git a/sys/dev/fdt/rkclock.c b/sys/dev/fdt/rkclock.c index 28cbddf7a3a..223b2a478cc 100644 --- a/sys/dev/fdt/rkclock.c +++ b/sys/dev/fdt/rkclock.c @@ -1,4 +1,4 @@ -/* $OpenBSD: rkclock.c,v 1.67 2023/03/10 10:54:29 kettenis Exp $ */ +/* $OpenBSD: rkclock.c,v 1.68 2023/03/12 14:29:50 kettenis Exp $ */ /* * Copyright (c) 2017, 2018 Mark Kettenis * @@ -186,6 +186,7 @@ #define RK3568_PMUCRU_HPLL_CON(i) (0x0040 + (i) * 4) #define RK3568_PMUCRU_MODE_CON 0x0080 #define RK3568_PMUCRU_CLKSEL_CON(i) (0x0100 + (i) * 4) +#define RK3568_PMUCRU_GATE_CON(i) (0x0180 + (i) * 4) /* RK3588 registers */ #define RK3588_CRU_AUPLL_CON(i) (0x00180 + (i) * 4) @@ -3563,6 +3564,51 @@ const struct rkclock rk3568_pmu_clocks[] = { SEL(11, 10), 0, { 0, 0, RK3568_XIN24M } }, + { + RK3568_CLK_PCIEPHY0_OSC0, 0, 0, 0, + { RK3568_XIN24M } + }, + { + RK3568_CLK_PCIEPHY0_DIV, RK3568_PMUCRU_CLKSEL_CON(9), + 0, DIV(2, 0), + { RK3568_PPLL_PH0 } + }, + { + RK3568_CLK_PCIEPHY0_REF, RK3568_PMUCRU_CLKSEL_CON(9), + SEL(3, 3), 0, + { RK3568_CLK_PCIEPHY0_OSC0, RK3568_CLK_PCIEPHY0_DIV }, + SET_PARENT + }, + { + RK3568_CLK_PCIEPHY1_OSC0, 0, 0, 0, + { RK3568_XIN24M } + }, + { + RK3568_CLK_PCIEPHY1_DIV, RK3568_PMUCRU_CLKSEL_CON(9), + 0, DIV(6, 4), + { RK3568_PPLL_PH0 } + }, + { + RK3568_CLK_PCIEPHY1_REF, RK3568_PMUCRU_CLKSEL_CON(9), + SEL(7, 7), 0, + { RK3568_CLK_PCIEPHY1_OSC0, RK3568_CLK_PCIEPHY1_DIV }, + SET_PARENT + }, + { + RK3568_CLK_PCIEPHY2_OSC0, 0, 0, 0, + { RK3568_XIN24M } + }, + { + RK3568_CLK_PCIEPHY2_DIV, RK3568_PMUCRU_CLKSEL_CON(9), + 0, DIV(10, 8), + { RK3568_PPLL_PH0 } + }, + { + RK3568_CLK_PCIEPHY2_REF, RK3568_PMUCRU_CLKSEL_CON(9), + SEL(11, 11), 0, + { RK3568_CLK_PCIEPHY2_OSC0, RK3568_CLK_PCIEPHY2_DIV }, + SET_PARENT + }, { RK3568_CLK_PDPMU, RK3568_PMUCRU_CLKSEL_CON(2), SEL(15, 15), 0, @@ -3576,6 +3622,16 @@ const struct rkclock rk3568_pmu_clocks[] = { void rk3568_pmu_init(struct rkclock_softc *sc) { + int i; + + /* The code below assumes all clocks are enabled. Check this!. */ + for (i = 0; i <= 2; i++) { + if (HREAD4(sc, RK3568_PMUCRU_GATE_CON(i)) != 0x00000000) { + printf("CRU_GATE_CON%d: 0x%08x\n", i, + HREAD4(sc, RK3568_CRU_GATE_CON(i))); + } + } + sc->sc_clocks = rk3568_pmu_clocks; } @@ -3656,6 +3712,9 @@ rk3568_pmu_get_frequency(void *cookie, uint32_t *cells) return rk3328_get_pll(sc, RK3568_PMUCRU_PPLL_CON(0)); case RK3568_PLL_HPLL: return rk3328_get_pll(sc, RK3568_PMUCRU_HPLL_CON(0)); + case RK3568_PPLL_PH0: + idx = RK3568_PLL_PPLL; + return rk3568_get_frequency(sc, &idx) / 2; case RK3568_XIN24M: return 24000000; default: @@ -3689,6 +3748,9 @@ rk3568_pmu_enable(void *cookie, uint32_t *cells, int on) uint32_t idx = cells[0]; switch (idx) { + case RK3568_CLK_PCIEPHY0_REF: + case RK3568_CLK_PCIEPHY1_REF: + case RK3568_CLK_PCIEPHY2_REF: case RK3568_CLK_I2C0: case RK3568_SCLK_UART0: case RK3568_PCLK_I2C0: diff --git a/sys/dev/fdt/rkclock_clocks.h b/sys/dev/fdt/rkclock_clocks.h index ae012153442..7233a1beaee 100644 --- a/sys/dev/fdt/rkclock_clocks.h +++ b/sys/dev/fdt/rkclock_clocks.h @@ -355,9 +355,20 @@ #define RK3568_CLK_I2C0 7 #define RK3568_SCLK_UART0 11 -#define RK3568_PCLK_I2C0 45 +#define RK3568_CLK_PCIEPHY0_DIV 29 +#define RK3568_CLK_PCIEPHY0_OSC0 30 +#define RK3568_CLK_PCIEPHY0_REF 31 +#define RK3568_CLK_PCIEPHY1_DIV 32 +#define RK3568_CLK_PCIEPHY1_OSC0 33 +#define RK3568_CLK_PCIEPHY1_REF 34 +#define RK3568_CLK_PCIEPHY2_DIV 35 +#define RK3568_CLK_PCIEPHY2_OSC0 36 +#define RK3568_CLK_PCIEPHY2_REF 37 +#define RK3568_PCLK_I2C0 45 #define RK3568_CLK_PDPMU 49 +#define RK3568_PPLL_PH0 1022 + /* * RK3588 clocks. */ diff --git a/sys/dev/fdt/rkcomphy.c b/sys/dev/fdt/rkcomphy.c new file mode 100644 index 00000000000..16920e94e5d --- /dev/null +++ b/sys/dev/fdt/rkcomphy.c @@ -0,0 +1,310 @@ +/* $OpenBSD: rkcomphy.c,v 1.1 2023/03/12 14:29:50 kettenis Exp $ */ +/* + * Copyright (c) 2023 Mark Kettenis + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include +#include +#include + +#include +#include +#include + +#include +#include +#include +#include + +/* Combo PHY registers */ +#define COMBO_PIPE_PHY_REG(idx) ((idx) * 4) +/* REG_005 */ +#define COMBO_PIPE_PHY_PLL_DIV_MASK (0x3 << 6) +#define COMBO_PIPE_PHY_PLL_DIV_2 (0x1 << 6) +/* REG_006 */ +#define COMBO_PIPE_PHY_TX_RTERM_50OHM (0x8 << 4) +#define COMBO_PIPE_PHY_RX_RTERM_44OHM (0xf << 4) +/* REG_010 */ +#define COMBO_PIPE_PHY_SU_TRIM_0_7 0xf0 +/* REG_011 */ +#define COMBO_PIPE_PHY_PLL_LPF_ADJ_VALUE 4 +/* REG_014 */ +#define COMBO_PIPE_PHY_SSC_CNT_LO_MASK (0x3 << 6) +#define COMBO_PIPE_PHY_SSC_CNT_LO_VALUE (0x1 << 6) +#define COMBO_PIPE_PHY_CTLE_EN (1 << 0) +/* REG_015 */ +#define COMBO_PIPE_PHY_SSC_CNT_HI_MASK (0xff << 0) +#define COMBO_PIPE_PHY_SSC_CNT_HI_VALUE (0x5f << 0) +/* REG_017 */ +#define COMBO_PIPE_PHY_PLL_LOOP 0x32 +/* REG_031 */ +#define COMBO_PIPE_PHY_SSC_DIR_MASK (0x3 << 4) +#define COMBO_PIPE_PHY_SSC_DIR_DOWN (0x1 << 4) +#define COMBO_PIPE_PHY_SSC_OFFSET_MASK (0x3 << 6) +#define COMBO_PIPE_PHY_SSC_OFFSET_500PPM (0x1 << 6) +/* REG_032 */ +#define COMBO_PIPE_PHY_PLL_KVCO_MASK (0x7 << 2) +#define COMBO_PIPE_PHY_PLL_KVCO_VALUE (0x2 << 2) + +/* GRF registers */ +#define PIPE_GRF_PIPE_CON0 0x0000 + +/* PHY GRF registers */ +#define PIPE_PHY_GRF_PIPE_CON(idx) ((idx) * 4) +/* CON0 */ +#define PIPE_PHY_GRF_PIPE_MODE_PCIE 0x003f0000 +#define PIPE_PHY_GRF_PIPE_MODE_USB 0x003f0004 +#define PIPE_PHY_GRF_PIPE_MODE_SATA 0x003f0019 +/* CON1 */ +#define PIPE_PHY_GRF_PIPE_CLK_24M 0x60000000 +#define PIPE_PHY_GRF_PIPE_CLK_25M 0x60002000 +#define PIPE_PHY_GRF_PIPE_CLK_100M 0x60004000 +/* CON2 */ +#define PIPE_PHY_GRF_PIPE_TXCOMP_SEL_CTRL 0x80000000 +#define PIPE_PHY_GRF_PIPE_TXCOMP_SEL_GRF 0x80008000 +#define PIPE_PHY_GRF_PIPE_TXELEC_SEL_CTRL 0x10000000 +#define PIPE_PHY_GRF_PIPE_TXELEC_SEL_GRF 0x10001000 +/* CON3 */ +#define PIPE_PHY_GRF_PIPE_SEL_PCIE 0x60000000 +#define PIPE_PHY_GRF_PIPE_SEL_USB 0x60002000 +#define PIPE_PHY_GRF_PIPE_SEL_SATA 0x60004000 +/* STATUS1 */ +#define PIPE_PHY_GRF_PIPE_STATUS1 0x34 +#define PIPE_PHY_GRF_PIPE_PHYSTATUS (1 << 6) + +#define HREAD4(sc, reg) \ + (bus_space_read_4((sc)->sc_iot, (sc)->sc_ioh, (reg))) +#define HWRITE4(sc, reg, val) \ + bus_space_write_4((sc)->sc_iot, (sc)->sc_ioh, (reg), (val)) +#define HSET4(sc, reg, bits) \ + HWRITE4((sc), (reg), HREAD4((sc), (reg)) | (bits)) +#define HCLR4(sc, reg, bits) \ + HWRITE4((sc), (reg), HREAD4((sc), (reg)) & ~(bits)) + +struct rkcomphy_softc { + struct device sc_dev; + bus_space_tag_t sc_iot; + bus_space_handle_t sc_ioh; + + struct phy_device sc_pd; +}; + +int rkcomphy_match(struct device *, void *, void *); +void rkcomphy_attach(struct device *, struct device *, void *); + +const struct cfattach rkcomphy_ca = { + sizeof (struct rkcomphy_softc), rkcomphy_match, rkcomphy_attach +}; + +struct cfdriver rkcomphy_cd = { + NULL, "rkcomphy", DV_DULL +}; + +int rkcomphy_enable(void *, uint32_t *); + +int +rkcomphy_match(struct device *parent, void *match, void *aux) +{ + struct fdt_attach_args *faa = aux; + + return OF_is_compatible(faa->fa_node, + "rockchip,rk3568-naneng-combphy"); +} + +void +rkcomphy_attach(struct device *parent, struct device *self, void *aux) +{ + struct rkcomphy_softc *sc = (struct rkcomphy_softc *)self; + struct fdt_attach_args *faa = aux; + + if (faa->fa_nreg < 1) { + printf(": no registers\n"); + return; + } + + sc->sc_iot = faa->fa_iot; + if (bus_space_map(sc->sc_iot, faa->fa_reg[0].addr, + faa->fa_reg[0].size, 0, &sc->sc_ioh)) { + printf(": can't map registers\n"); + return; + } + + reset_assert_all(faa->fa_node); + + printf("\n"); + + sc->sc_pd.pd_node = faa->fa_node; + sc->sc_pd.pd_cookie = sc; + sc->sc_pd.pd_enable = rkcomphy_enable; + phy_register(&sc->sc_pd); +} + +void +rkcomphy_pll_tune(struct rkcomphy_softc *sc) +{ + uint32_t reg; + + reg = HREAD4(sc, COMBO_PIPE_PHY_REG(32)); + reg &= ~COMBO_PIPE_PHY_PLL_KVCO_MASK; + reg |= COMBO_PIPE_PHY_PLL_KVCO_VALUE; + HWRITE4(sc, COMBO_PIPE_PHY_REG(32), reg); + + HWRITE4(sc, COMBO_PIPE_PHY_REG(11), COMBO_PIPE_PHY_PLL_LPF_ADJ_VALUE); + + reg = HREAD4(sc, COMBO_PIPE_PHY_REG(5)); + reg &= ~COMBO_PIPE_PHY_PLL_DIV_MASK; + reg |= COMBO_PIPE_PHY_PLL_DIV_2; + HWRITE4(sc, COMBO_PIPE_PHY_REG(5), reg); + + HWRITE4(sc, COMBO_PIPE_PHY_REG(17), COMBO_PIPE_PHY_PLL_LOOP); + HWRITE4(sc, COMBO_PIPE_PHY_REG(10), COMBO_PIPE_PHY_SU_TRIM_0_7); +} + +int +rkcomphy_enable(void *cookie, uint32_t *cells) +{ + struct rkcomphy_softc *sc = cookie; + struct regmap *rm, *phy_rm; + int node = sc->sc_pd.pd_node; + uint32_t type = cells[0]; + uint32_t freq, grf, phy_grf, reg; + int stat, timo; + + /* We only support PCIe, SATA and USB 3 for now. */ + switch (type) { + case PHY_TYPE_PCIE: + case PHY_TYPE_SATA: + case PHY_TYPE_USB3: + break; + default: + return EINVAL; + } + + grf = OF_getpropint(node, "rockchip,pipe-grf", 0); + rm = regmap_byphandle(grf); + if (rm == NULL) + return ENXIO; + + phy_grf = OF_getpropint(node, "rockchip,pipe-phy-grf", 0); + phy_rm = regmap_byphandle(phy_grf); + if (phy_rm == NULL) + return ENXIO; + + clock_set_assigned(node); + clock_enable_all(node); + + if (type == PHY_TYPE_PCIE || type == PHY_TYPE_USB3) { + reg = HREAD4(sc, COMBO_PIPE_PHY_REG(31)); + reg &= ~COMBO_PIPE_PHY_SSC_OFFSET_MASK; + reg &= ~COMBO_PIPE_PHY_SSC_DIR_MASK; + reg |= COMBO_PIPE_PHY_SSC_DIR_DOWN; + HWRITE4(sc, COMBO_PIPE_PHY_REG(31), reg); + } + + if (type == PHY_TYPE_SATA || type == PHY_TYPE_USB3) { + reg = HREAD4(sc, COMBO_PIPE_PHY_REG(14)); + reg |= COMBO_PIPE_PHY_CTLE_EN; + HWRITE4(sc, COMBO_PIPE_PHY_REG(14), reg); + } + + switch (type) { + case PHY_TYPE_PCIE: + regmap_write_4(phy_rm, PIPE_PHY_GRF_PIPE_CON(0), 0xffff1000); + regmap_write_4(phy_rm, PIPE_PHY_GRF_PIPE_CON(1), 0xffff0000); + regmap_write_4(phy_rm, PIPE_PHY_GRF_PIPE_CON(2), 0xffff0101); + regmap_write_4(phy_rm, PIPE_PHY_GRF_PIPE_CON(3), 0xffff0200); + break; + case PHY_TYPE_SATA: + HWRITE4(sc, COMBO_PIPE_PHY_REG(6), + COMBO_PIPE_PHY_TX_RTERM_50OHM | + COMBO_PIPE_PHY_RX_RTERM_44OHM); + + regmap_write_4(phy_rm, PIPE_PHY_GRF_PIPE_CON(0), 0xffff0119); + regmap_write_4(phy_rm, PIPE_PHY_GRF_PIPE_CON(1), 0xffff0040); + regmap_write_4(phy_rm, PIPE_PHY_GRF_PIPE_CON(2), 0xffff80c3); + regmap_write_4(phy_rm, PIPE_PHY_GRF_PIPE_CON(3), 0xffff4407); + regmap_write_4(rm, PIPE_GRF_PIPE_CON0, 0xffff2220); + break; + case PHY_TYPE_USB3: + rkcomphy_pll_tune(sc); + + regmap_write_4(phy_rm, PIPE_PHY_GRF_PIPE_CON(0), + PIPE_PHY_GRF_PIPE_MODE_USB); + regmap_write_4(phy_rm, PIPE_PHY_GRF_PIPE_CON(2), + PIPE_PHY_GRF_PIPE_TXCOMP_SEL_CTRL); + regmap_write_4(phy_rm, PIPE_PHY_GRF_PIPE_CON(2), + PIPE_PHY_GRF_PIPE_TXELEC_SEL_CTRL); + regmap_write_4(phy_rm, PIPE_PHY_GRF_PIPE_CON(3), + PIPE_PHY_GRF_PIPE_SEL_USB); + break; + } + + freq = clock_get_frequency(node, "ref"); + switch (freq) { + case 24000000: + regmap_write_4(phy_rm, PIPE_PHY_GRF_PIPE_CON(1), + PIPE_PHY_GRF_PIPE_CLK_24M); + if (type == PHY_TYPE_SATA || type == PHY_TYPE_USB3) { + reg = HREAD4(sc, COMBO_PIPE_PHY_REG(14)); + reg &= ~COMBO_PIPE_PHY_SSC_CNT_LO_MASK; + reg |= COMBO_PIPE_PHY_SSC_CNT_LO_VALUE; + HWRITE4(sc, COMBO_PIPE_PHY_REG(14), reg); + reg = HREAD4(sc, COMBO_PIPE_PHY_REG(15)); + reg &= ~COMBO_PIPE_PHY_SSC_CNT_HI_MASK; + reg |= COMBO_PIPE_PHY_SSC_CNT_HI_VALUE; + HWRITE4(sc, COMBO_PIPE_PHY_REG(15), reg); + } + break; + case 25000000: + regmap_write_4(phy_rm, PIPE_PHY_GRF_PIPE_CON(1), + PIPE_PHY_GRF_PIPE_CLK_25M); + break; + case 100000000: + regmap_write_4(phy_rm, PIPE_PHY_GRF_PIPE_CON(1), + PIPE_PHY_GRF_PIPE_CLK_100M); + switch (type) { + case PHY_TYPE_PCIE: + rkcomphy_pll_tune(sc); + break; + case PHY_TYPE_SATA: + reg = HREAD4(sc, COMBO_PIPE_PHY_REG(31)); + reg &= ~COMBO_PIPE_PHY_SSC_OFFSET_MASK; + reg |= COMBO_PIPE_PHY_SSC_OFFSET_500PPM; + reg &= ~COMBO_PIPE_PHY_SSC_DIR_MASK; + reg |= COMBO_PIPE_PHY_SSC_DIR_DOWN; + HWRITE4(sc, COMBO_PIPE_PHY_REG(31), reg); + break; + } + break; + } + + reset_deassert_all(node); + + if (type == PHY_TYPE_USB3) { + for (timo = 100; timo > 0; timo--) { + stat = regmap_read_4(phy_rm, + PIPE_PHY_GRF_PIPE_STATUS1); + if ((stat & PIPE_PHY_GRF_PIPE_PHYSTATUS) == 0) + break; + delay(10); + } + if (timo == 0) { + printf("%s: timeout\n", sc->sc_dev.dv_xname); + return ETIMEDOUT; + } + } + + return 0; +}