1*ca5860d9Sskrll /* $NetBSD: rk3399_pcie_phy.c,v 1.5 2024/11/19 08:24:47 skrll Exp $ */ 2e8b82306Sjakllsch /* $OpenBSD: rkpcie.c,v 1.6 2018/08/28 09:33:18 jsg Exp $ */ 3e8b82306Sjakllsch /* 4e8b82306Sjakllsch * Copyright (c) 2018 Mark Kettenis <kettenis@openbsd.org> 5e8b82306Sjakllsch * 6e8b82306Sjakllsch * Permission to use, copy, modify, and distribute this software for any 7e8b82306Sjakllsch * purpose with or without fee is hereby granted, provided that the above 8e8b82306Sjakllsch * copyright notice and this permission notice appear in all copies. 9e8b82306Sjakllsch * 10e8b82306Sjakllsch * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 11e8b82306Sjakllsch * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 12e8b82306Sjakllsch * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 13e8b82306Sjakllsch * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 14e8b82306Sjakllsch * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 15e8b82306Sjakllsch * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 16e8b82306Sjakllsch * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 17e8b82306Sjakllsch */ 18e8b82306Sjakllsch 19e8b82306Sjakllsch #include <sys/cdefs.h> 20e8b82306Sjakllsch 21*ca5860d9Sskrll __KERNEL_RCSID(1, "$NetBSD: rk3399_pcie_phy.c,v 1.5 2024/11/19 08:24:47 skrll Exp $"); 22e8b82306Sjakllsch 23e8b82306Sjakllsch #include <sys/param.h> 24e8b82306Sjakllsch #include <sys/systm.h> 25e8b82306Sjakllsch #include <sys/device.h> 26e8b82306Sjakllsch #include <sys/kmem.h> 27e8b82306Sjakllsch 28e8b82306Sjakllsch #include <machine/intr.h> 29e8b82306Sjakllsch #include <sys/bus.h> 30e8b82306Sjakllsch #include <dev/fdt/fdtvar.h> 31e8b82306Sjakllsch #include <dev/fdt/syscon.h> 32e8b82306Sjakllsch 33e8b82306Sjakllsch #include <sys/gpio.h> 34e8b82306Sjakllsch 35e8b82306Sjakllsch #define RKPCIEPHY_MAXPHY 4 36e8b82306Sjakllsch 37e8b82306Sjakllsch struct rkpciephy_softc { 38e8b82306Sjakllsch device_t sc_dev; 39e8b82306Sjakllsch int sc_phy_node; 40e8b82306Sjakllsch uint8_t sc_phys[RKPCIEPHY_MAXPHY]; 41e8b82306Sjakllsch u_int sc_phys_on; 42e8b82306Sjakllsch }; 43e8b82306Sjakllsch 44e8b82306Sjakllsch static int rkpciephy_match(device_t, cfdata_t, void *); 45e8b82306Sjakllsch static void rkpciephy_attach(device_t, device_t, void *); 46e8b82306Sjakllsch 47e8b82306Sjakllsch CFATTACH_DECL_NEW(rkpciephy, sizeof(struct rkpciephy_softc), 48e8b82306Sjakllsch rkpciephy_match, rkpciephy_attach, NULL, NULL); 49e8b82306Sjakllsch 506e54367aSthorpej static const struct device_compatible_entry compat_data[] = { 516e54367aSthorpej { .compat = "rockchip,rk3399-pcie-phy" }, 526e54367aSthorpej DEVICE_COMPAT_EOL 53e8b82306Sjakllsch }; 54e8b82306Sjakllsch 55e8b82306Sjakllsch static int 56e8b82306Sjakllsch rkpciephy_match(device_t parent, cfdata_t cf, void *aux) 57e8b82306Sjakllsch { 58e8b82306Sjakllsch struct fdt_attach_args *faa = aux; 59e8b82306Sjakllsch 606e54367aSthorpej return of_compatible_match(faa->faa_phandle, compat_data); 61e8b82306Sjakllsch } 62e8b82306Sjakllsch 63e8b82306Sjakllsch static void rkpcie_phy_poweron(struct rkpciephy_softc *, u_int); 64e8b82306Sjakllsch 65e8b82306Sjakllsch static inline void 66e8b82306Sjakllsch clock_enable(int phandle, const char *name) 67e8b82306Sjakllsch { 68e8b82306Sjakllsch struct clk * clk = fdtbus_clock_get(phandle, name); 69e8b82306Sjakllsch if (clk == NULL) 70e8b82306Sjakllsch return; 71e8b82306Sjakllsch if (clk_enable(clk) != 0) 72e8b82306Sjakllsch return; 73e8b82306Sjakllsch } 74e8b82306Sjakllsch 75e8b82306Sjakllsch static void 76e8b82306Sjakllsch reset_assert(int phandle, const char *name) 77e8b82306Sjakllsch { 78e8b82306Sjakllsch struct fdtbus_reset *rst; 79e8b82306Sjakllsch 80e8b82306Sjakllsch rst = fdtbus_reset_get(phandle, name); 81e8b82306Sjakllsch fdtbus_reset_assert(rst); 82e8b82306Sjakllsch fdtbus_reset_put(rst); 83e8b82306Sjakllsch } 84e8b82306Sjakllsch 85e8b82306Sjakllsch static void 86e8b82306Sjakllsch reset_deassert(int phandle, const char *name) 87e8b82306Sjakllsch { 88e8b82306Sjakllsch struct fdtbus_reset *rst; 89e8b82306Sjakllsch 90e8b82306Sjakllsch rst = fdtbus_reset_get(phandle, name); 91e8b82306Sjakllsch fdtbus_reset_deassert(rst); 92e8b82306Sjakllsch fdtbus_reset_put(rst); 93e8b82306Sjakllsch } 94e8b82306Sjakllsch 95e8b82306Sjakllsch static void * 96e8b82306Sjakllsch rkpciephy_phy_acquire(device_t dev, const void *data, size_t len) 97e8b82306Sjakllsch { 98e8b82306Sjakllsch struct rkpciephy_softc * const sc = device_private(dev); 99e8b82306Sjakllsch 100e8b82306Sjakllsch if (len != 4) 101e8b82306Sjakllsch return NULL; 102e8b82306Sjakllsch 103e8b82306Sjakllsch const int phy_id = be32dec(data); 104e8b82306Sjakllsch if (phy_id >= RKPCIEPHY_MAXPHY) 105e8b82306Sjakllsch return NULL; 106e8b82306Sjakllsch // device_printf(dev, "%s phy_id %d %d\n", __func__, phy_id, sc->sc_phys[phy_id]); 107e8b82306Sjakllsch 108e8b82306Sjakllsch if (true /*XXX*/ || sc->sc_phys_on == 0) { 109e8b82306Sjakllsch clock_enable(sc->sc_phy_node, "refclk"); 110e8b82306Sjakllsch reset_assert(sc->sc_phy_node, "phy"); 111e8b82306Sjakllsch } 112e8b82306Sjakllsch 113e8b82306Sjakllsch return &sc->sc_phys[phy_id]; 114e8b82306Sjakllsch } 115e8b82306Sjakllsch 116e8b82306Sjakllsch static int 117e8b82306Sjakllsch rkpciephy_phy_enable(device_t dev, void *priv, bool enable) 118e8b82306Sjakllsch { 119e8b82306Sjakllsch struct rkpciephy_softc * const sc = device_private(dev); 120e8b82306Sjakllsch uint8_t * const lane = priv; 121e8b82306Sjakllsch 122e8b82306Sjakllsch // device_printf(dev, "%s %u %u\n", __func__, *lane, enable); 123e8b82306Sjakllsch 124e8b82306Sjakllsch if (enable) { 125e8b82306Sjakllsch rkpcie_phy_poweron(sc, *lane); 126e8b82306Sjakllsch sc->sc_phys_on |= 1U << *lane; 127e8b82306Sjakllsch } else { 128e8b82306Sjakllsch #if notyet 129e8b82306Sjakllsch sc->sc_phys_on &= ~(1U << *lane); 130e8b82306Sjakllsch #endif 131e8b82306Sjakllsch } 132e8b82306Sjakllsch 133e8b82306Sjakllsch return 0; 134e8b82306Sjakllsch } 135e8b82306Sjakllsch 136e8b82306Sjakllsch const struct fdtbus_phy_controller_func rkpciephy_phy_funcs = { 137e8b82306Sjakllsch .acquire = rkpciephy_phy_acquire, 138e8b82306Sjakllsch .release = (void *)voidop, 139e8b82306Sjakllsch .enable = rkpciephy_phy_enable, 140e8b82306Sjakllsch }; 141e8b82306Sjakllsch 142e8b82306Sjakllsch static void 143e8b82306Sjakllsch rkpciephy_attach(device_t parent, device_t self, void *aux) 144e8b82306Sjakllsch { 145e8b82306Sjakllsch struct rkpciephy_softc *sc = device_private(self); 146e8b82306Sjakllsch struct fdt_attach_args *faa = aux; 147e8b82306Sjakllsch 148e8b82306Sjakllsch sc->sc_dev = self; 149e8b82306Sjakllsch sc->sc_phy_node = faa->faa_phandle; 150e8b82306Sjakllsch 151e8b82306Sjakllsch aprint_naive("\n"); 152e8b82306Sjakllsch aprint_normal(": RK3399 PCIe PHY\n"); 153e8b82306Sjakllsch 154e8b82306Sjakllsch for (size_t i = 0; i < RKPCIEPHY_MAXPHY; i++) 155e8b82306Sjakllsch sc->sc_phys[i] = i; 156e8b82306Sjakllsch 157e8b82306Sjakllsch fdtbus_register_phy_controller(self, faa->faa_phandle, &rkpciephy_phy_funcs); 158e8b82306Sjakllsch } 159e8b82306Sjakllsch 160e8b82306Sjakllsch /* 161e8b82306Sjakllsch * PHY Support. 162e8b82306Sjakllsch */ 163e8b82306Sjakllsch 164e8b82306Sjakllsch #define RK3399_GRF_SOC_CON5_PCIE 0xe214 165e8b82306Sjakllsch #define RK3399_TX_ELEC_IDLE_OFF_MASK ((1 << 3) << 16) 166e8b82306Sjakllsch #define RK3399_TX_ELEC_IDLE_OFF (1 << 3) 167e8b82306Sjakllsch #define RK3399_GRF_SOC_CON8 0xe220 168e8b82306Sjakllsch #define RK3399_PCIE_TEST_DATA_MASK ((0xf << 7) << 16) 169e8b82306Sjakllsch #define RK3399_PCIE_TEST_DATA_SHIFT 7 170e8b82306Sjakllsch #define RK3399_PCIE_TEST_ADDR_MASK ((0x3f << 1) << 16) 171e8b82306Sjakllsch #define RK3399_PCIE_TEST_ADDR_SHIFT 1 172e8b82306Sjakllsch #define RK3399_PCIE_TEST_WRITE_ENABLE (((1 << 0) << 16) | (1 << 0)) 173e8b82306Sjakllsch #define RK3399_PCIE_TEST_WRITE_DISABLE (((1 << 0) << 16) | (0 << 0)) 174e8b82306Sjakllsch #define RK3399_GRF_SOC_STATUS1 0xe2a4 175e8b82306Sjakllsch #define RK3399_PCIE_PHY_PLL_LOCKED (1 << 9) 176e8b82306Sjakllsch #define RK3399_PCIE_PHY_PLL_OUTPUT (1 << 10) 177e8b82306Sjakllsch 178e8b82306Sjakllsch #define RK3399_PCIE_PHY_CFG_PLL_LOCK 0x10 179e8b82306Sjakllsch #define RK3399_PCIE_PHY_CFG_CLK_TEST 0x10 180e8b82306Sjakllsch #define RK3399_PCIE_PHY_CFG_SEPE_RATE (1 << 3) 181e8b82306Sjakllsch #define RK3399_PCIE_PHY_CFG_CLK_SCC 0x12 182e8b82306Sjakllsch #define RK3399_PCIE_PHY_CFG_PLL_100M (1 << 3) 183e8b82306Sjakllsch 184e8b82306Sjakllsch static void 185e8b82306Sjakllsch rkpcie_phy_write_conf(struct syscon *rm, uint8_t addr, uint8_t data) 186e8b82306Sjakllsch { 187e8b82306Sjakllsch syscon_write_4(rm, RK3399_GRF_SOC_CON8, 188e8b82306Sjakllsch RK3399_PCIE_TEST_ADDR_MASK | 189e8b82306Sjakllsch (addr << RK3399_PCIE_TEST_ADDR_SHIFT) | 190e8b82306Sjakllsch RK3399_PCIE_TEST_DATA_MASK | 191e8b82306Sjakllsch (data << RK3399_PCIE_TEST_DATA_SHIFT) | 192e8b82306Sjakllsch RK3399_PCIE_TEST_WRITE_DISABLE); 193e8b82306Sjakllsch delay(1); 194e8b82306Sjakllsch syscon_write_4(rm, RK3399_GRF_SOC_CON8, 195e8b82306Sjakllsch RK3399_PCIE_TEST_WRITE_ENABLE); 196e8b82306Sjakllsch delay(1); 197e8b82306Sjakllsch syscon_write_4(rm, RK3399_GRF_SOC_CON8, 198e8b82306Sjakllsch RK3399_PCIE_TEST_WRITE_DISABLE); 199e8b82306Sjakllsch } 200e8b82306Sjakllsch 201e8b82306Sjakllsch static void 202e8b82306Sjakllsch rkpcie_phy_poweron(struct rkpciephy_softc *sc, u_int lane) 203e8b82306Sjakllsch { 204e8b82306Sjakllsch struct syscon *rm; 205e8b82306Sjakllsch uint32_t status; 206e8b82306Sjakllsch int timo; 207e8b82306Sjakllsch 208e8b82306Sjakllsch reset_deassert(sc->sc_phy_node, "phy"); 209e8b82306Sjakllsch 210e8b82306Sjakllsch rm = fdtbus_syscon_lookup(OF_parent(sc->sc_phy_node)); 211e8b82306Sjakllsch if (rm == NULL) 212e8b82306Sjakllsch return; 213e8b82306Sjakllsch 214e8b82306Sjakllsch syscon_lock(rm); 215e8b82306Sjakllsch syscon_write_4(rm, RK3399_GRF_SOC_CON8, 216e8b82306Sjakllsch RK3399_PCIE_TEST_ADDR_MASK | 217e8b82306Sjakllsch RK3399_PCIE_PHY_CFG_PLL_LOCK << RK3399_PCIE_TEST_ADDR_SHIFT); 218e8b82306Sjakllsch syscon_write_4(rm, RK3399_GRF_SOC_CON5_PCIE, 219e8b82306Sjakllsch RK3399_TX_ELEC_IDLE_OFF_MASK << lane | 0); 220e8b82306Sjakllsch //printf("%s %x\n", __func__, syscon_read_4(rm, RK3399_GRF_SOC_CON5_PCIE)); 221e8b82306Sjakllsch 222e8b82306Sjakllsch for (timo = 50; timo > 0; timo--) { 223e8b82306Sjakllsch status = syscon_read_4(rm, RK3399_GRF_SOC_STATUS1); 224e8b82306Sjakllsch if (status & RK3399_PCIE_PHY_PLL_LOCKED) 225e8b82306Sjakllsch break; 226e8b82306Sjakllsch delay(20000); 227e8b82306Sjakllsch } 228e8b82306Sjakllsch if (timo == 0) { 229e8b82306Sjakllsch device_printf(sc->sc_dev, "PHY PLL lock timeout\n"); 230e8b82306Sjakllsch syscon_unlock(rm); 231e8b82306Sjakllsch return; 232e8b82306Sjakllsch } 233e8b82306Sjakllsch 234e8b82306Sjakllsch rkpcie_phy_write_conf(rm, RK3399_PCIE_PHY_CFG_CLK_TEST, 235e8b82306Sjakllsch RK3399_PCIE_PHY_CFG_SEPE_RATE); 236e8b82306Sjakllsch rkpcie_phy_write_conf(rm, RK3399_PCIE_PHY_CFG_CLK_SCC, 237e8b82306Sjakllsch RK3399_PCIE_PHY_CFG_PLL_100M); 238e8b82306Sjakllsch 239e8b82306Sjakllsch for (timo = 50; timo > 0; timo--) { 240e8b82306Sjakllsch status = syscon_read_4(rm, RK3399_GRF_SOC_STATUS1); 241e8b82306Sjakllsch if ((status & RK3399_PCIE_PHY_PLL_OUTPUT) == 0) 242e8b82306Sjakllsch break; 243e8b82306Sjakllsch delay(20000); 244e8b82306Sjakllsch } 245e8b82306Sjakllsch if (timo == 0) { 246e8b82306Sjakllsch device_printf(sc->sc_dev, "PHY PLL output enable timeout\n"); 247e8b82306Sjakllsch syscon_unlock(rm); 248e8b82306Sjakllsch return; 249e8b82306Sjakllsch } 250e8b82306Sjakllsch 251e8b82306Sjakllsch syscon_write_4(rm, RK3399_GRF_SOC_CON8, 252e8b82306Sjakllsch RK3399_PCIE_TEST_ADDR_MASK | 253e8b82306Sjakllsch RK3399_PCIE_PHY_CFG_PLL_LOCK << RK3399_PCIE_TEST_ADDR_SHIFT); 254e8b82306Sjakllsch 255e8b82306Sjakllsch for (timo = 50; timo > 0; timo--) { 256e8b82306Sjakllsch status = syscon_read_4(rm, RK3399_GRF_SOC_STATUS1); 257e8b82306Sjakllsch if (status & RK3399_PCIE_PHY_PLL_LOCKED) 258e8b82306Sjakllsch break; 259e8b82306Sjakllsch delay(20000); 260e8b82306Sjakllsch } 261e8b82306Sjakllsch if (timo == 0) { 262e8b82306Sjakllsch device_printf(sc->sc_dev, "PHY PLL relock timeout\n"); 263e8b82306Sjakllsch syscon_unlock(rm); 264e8b82306Sjakllsch return; 265e8b82306Sjakllsch } 266e8b82306Sjakllsch syscon_unlock(rm); 267e8b82306Sjakllsch } 268