1b9e7cb89SCoia Prant // SPDX-License-Identifier: GPL-2.0 2b9e7cb89SCoia Prant /* 3b9e7cb89SCoia Prant * Rockchip XPCS platform device driver 4b9e7cb89SCoia Prant * 5b9e7cb89SCoia Prant * Based on the Synopsys DesignWare XPCS platform driver. 6b9e7cb89SCoia Prant * Copyright (C) 2024 Serge Semin 7b9e7cb89SCoia Prant * 8b9e7cb89SCoia Prant * Adapted for Rockchip SoCs, with reference to the Rockchip OEM driver. 9b9e7cb89SCoia Prant * Copyright (C) 2026 Coia Prant 10b9e7cb89SCoia Prant */ 11b9e7cb89SCoia Prant 12b9e7cb89SCoia Prant #include <linux/atomic.h> 13b9e7cb89SCoia Prant #include <linux/bitfield.h> 14b9e7cb89SCoia Prant #include <linux/clk.h> 15b9e7cb89SCoia Prant #include <linux/device.h> 16b9e7cb89SCoia Prant #include <linux/io.h> 17b9e7cb89SCoia Prant #include <linux/iopoll.h> 18b9e7cb89SCoia Prant #include <linux/math.h> 19b9e7cb89SCoia Prant #include <linux/mdio.h> 20b9e7cb89SCoia Prant #include <linux/module.h> 21b9e7cb89SCoia Prant #include <linux/of.h> 22b9e7cb89SCoia Prant #include <linux/of_platform.h> 23b9e7cb89SCoia Prant #include <linux/pcs/pcs-xpcs-rk.h> 24b9e7cb89SCoia Prant #include <linux/phy.h> 25b9e7cb89SCoia Prant #include <linux/phy/phy.h> 26b9e7cb89SCoia Prant #include <linux/platform_device.h> 27b9e7cb89SCoia Prant #include <linux/pm_domain.h> 28b9e7cb89SCoia Prant #include <linux/pm_runtime.h> 29b9e7cb89SCoia Prant #include <linux/property.h> 30b9e7cb89SCoia Prant #include <linux/sizes.h> 31b9e7cb89SCoia Prant #include <linux/time.h> 32b9e7cb89SCoia Prant 33b9e7cb89SCoia Prant #include "pcs-xpcs.h" 34b9e7cb89SCoia Prant 35b9e7cb89SCoia Prant struct dw_xpcs_rk { 36b9e7cb89SCoia Prant struct platform_device *pdev; 37b9e7cb89SCoia Prant struct mii_bus *bus; 38b9e7cb89SCoia Prant void __iomem *reg_base; 39b9e7cb89SCoia Prant struct phy *serdes_phy; 40b9e7cb89SCoia Prant struct clk *csr_clk; 41b9e7cb89SCoia Prant struct clk *eee_clk; 42b9e7cb89SCoia Prant u8 eee_mult_fact; 43b9e7cb89SCoia Prant }; 44b9e7cb89SCoia Prant 45b9e7cb89SCoia Prant static ptrdiff_t xpcs_rk_addr_format(int dev, int reg) 46b9e7cb89SCoia Prant { 47b9e7cb89SCoia Prant return FIELD_PREP(0x70000, dev) | FIELD_PREP(0xffff, reg); 48b9e7cb89SCoia Prant } 49b9e7cb89SCoia Prant 50b9e7cb89SCoia Prant static int xpcs_rk_read_reg(struct dw_xpcs_rk *pxpcs, int dev, int reg) 51b9e7cb89SCoia Prant { 52b9e7cb89SCoia Prant ptrdiff_t csr; 53b9e7cb89SCoia Prant int ret; 54b9e7cb89SCoia Prant 55b9e7cb89SCoia Prant csr = xpcs_rk_addr_format(dev, reg); 56b9e7cb89SCoia Prant 57b9e7cb89SCoia Prant ret = pm_runtime_resume_and_get(&pxpcs->pdev->dev); 58b9e7cb89SCoia Prant if (ret) 59b9e7cb89SCoia Prant return ret; 60b9e7cb89SCoia Prant 61b9e7cb89SCoia Prant ret = readl(pxpcs->reg_base + (csr << 2)) & 0xffff; 62b9e7cb89SCoia Prant 63b9e7cb89SCoia Prant pm_runtime_put(&pxpcs->pdev->dev); 64b9e7cb89SCoia Prant return ret; 65b9e7cb89SCoia Prant } 66b9e7cb89SCoia Prant 67b9e7cb89SCoia Prant static int xpcs_rk_write_reg(struct dw_xpcs_rk *pxpcs, int dev, int reg, u16 val) 68b9e7cb89SCoia Prant { 69b9e7cb89SCoia Prant ptrdiff_t csr; 70b9e7cb89SCoia Prant int ret; 71b9e7cb89SCoia Prant 72b9e7cb89SCoia Prant csr = xpcs_rk_addr_format(dev, reg); 73b9e7cb89SCoia Prant 74b9e7cb89SCoia Prant ret = pm_runtime_resume_and_get(&pxpcs->pdev->dev); 75b9e7cb89SCoia Prant if (ret) 76b9e7cb89SCoia Prant return ret; 77b9e7cb89SCoia Prant 78b9e7cb89SCoia Prant writel(val, pxpcs->reg_base + (csr << 2)); 79b9e7cb89SCoia Prant 80b9e7cb89SCoia Prant pm_runtime_put(&pxpcs->pdev->dev); 81b9e7cb89SCoia Prant return 0; 82b9e7cb89SCoia Prant } 83b9e7cb89SCoia Prant 84b9e7cb89SCoia Prant #define ROCKCHIP_MMD_MII1 2 85b9e7cb89SCoia Prant #define ROCKCHIP_MMD_MII2 3 86b9e7cb89SCoia Prant #define ROCKCHIP_MMD_MII3 4 87b9e7cb89SCoia Prant #define ROCKCHIP_MMD_PMAPMD 6 88b9e7cb89SCoia Prant #define ROCKCHIP_MMD_MII 7 89b9e7cb89SCoia Prant 90b9e7cb89SCoia Prant static bool xpcs_rk_mdio_addr_validate(int addr) 91b9e7cb89SCoia Prant { 92b9e7cb89SCoia Prant return !(addr < 0 || addr > 3); 93b9e7cb89SCoia Prant } 94b9e7cb89SCoia Prant 95b9e7cb89SCoia Prant static int xpcs_rk_mdio_read_remapping(int addr, int dev) 96b9e7cb89SCoia Prant { 97b9e7cb89SCoia Prant switch (dev) { 98b9e7cb89SCoia Prant case MDIO_MMD_PMAPMD: 99b9e7cb89SCoia Prant return ROCKCHIP_MMD_PMAPMD; 100b9e7cb89SCoia Prant case MDIO_MMD_VEND2: 101b9e7cb89SCoia Prant break; 102b9e7cb89SCoia Prant default: 103b9e7cb89SCoia Prant return -ENXIO; 104b9e7cb89SCoia Prant } 105b9e7cb89SCoia Prant 106b9e7cb89SCoia Prant /* 107b9e7cb89SCoia Prant * Reads are redirected by hardware to the port's read-only mirror; 108b9e7cb89SCoia Prant * only writes have to be targeted at MII (see the write path). 109b9e7cb89SCoia Prant */ 110b9e7cb89SCoia Prant switch (addr) { 111b9e7cb89SCoia Prant case 0: 112b9e7cb89SCoia Prant return ROCKCHIP_MMD_MII; 113b9e7cb89SCoia Prant case 1: 114b9e7cb89SCoia Prant return ROCKCHIP_MMD_MII1; 115b9e7cb89SCoia Prant case 2: 116b9e7cb89SCoia Prant return ROCKCHIP_MMD_MII2; 117b9e7cb89SCoia Prant case 3: 118b9e7cb89SCoia Prant return ROCKCHIP_MMD_MII3; 119b9e7cb89SCoia Prant default: 120b9e7cb89SCoia Prant return -ENODEV; 121b9e7cb89SCoia Prant } 122b9e7cb89SCoia Prant } 123b9e7cb89SCoia Prant 124b9e7cb89SCoia Prant static int xpcs_rk_mdio_write_remapping(int addr, int dev, int reg) 125b9e7cb89SCoia Prant { 126b9e7cb89SCoia Prant switch (dev) { 127b9e7cb89SCoia Prant case MDIO_MMD_PMAPMD: 128b9e7cb89SCoia Prant return ROCKCHIP_MMD_PMAPMD; 129b9e7cb89SCoia Prant case MDIO_MMD_VEND2: 130b9e7cb89SCoia Prant break; 131b9e7cb89SCoia Prant default: 132b9e7cb89SCoia Prant return -ENXIO; 133b9e7cb89SCoia Prant } 134b9e7cb89SCoia Prant 135b9e7cb89SCoia Prant /* 136b9e7cb89SCoia Prant * These registers physically live only in MII (the management port). 137b9e7cb89SCoia Prant * Ports 1-3 expose read-only mirrors of these bits, so writes must 138b9e7cb89SCoia Prant * always target MII; the read path remaps per address and the 139b9e7cb89SCoia Prant * hardware redirects to the port's mirror. 140b9e7cb89SCoia Prant */ 141b9e7cb89SCoia Prant switch (reg) { 142b9e7cb89SCoia Prant case DW_VR_MII_AN_CTRL: 143b9e7cb89SCoia Prant case DW_VR_MII_AN_INTR_STS: 144b9e7cb89SCoia Prant case DW_VR_MII_EEE_MCTRL0: 145b9e7cb89SCoia Prant case DW_VR_MII_EEE_MCTRL1: 146b9e7cb89SCoia Prant case DW_VR_MII_DIG_CTRL2: 147b9e7cb89SCoia Prant return ROCKCHIP_MMD_MII; 148b9e7cb89SCoia Prant default: 149b9e7cb89SCoia Prant break; 150b9e7cb89SCoia Prant } 151b9e7cb89SCoia Prant 152b9e7cb89SCoia Prant switch (addr) { 153b9e7cb89SCoia Prant case 0: 154b9e7cb89SCoia Prant return ROCKCHIP_MMD_MII; 155b9e7cb89SCoia Prant case 1: 156b9e7cb89SCoia Prant return ROCKCHIP_MMD_MII1; 157b9e7cb89SCoia Prant case 2: 158b9e7cb89SCoia Prant return ROCKCHIP_MMD_MII2; 159b9e7cb89SCoia Prant case 3: 160b9e7cb89SCoia Prant return ROCKCHIP_MMD_MII3; 161b9e7cb89SCoia Prant default: 162b9e7cb89SCoia Prant return -ENODEV; 163b9e7cb89SCoia Prant } 164b9e7cb89SCoia Prant } 165b9e7cb89SCoia Prant 166b9e7cb89SCoia Prant static int xpcs_rk_read_c22(struct mii_bus *bus, int addr, int reg) 167b9e7cb89SCoia Prant { 168b9e7cb89SCoia Prant struct dw_xpcs_rk *pxpcs = bus->priv; 169b9e7cb89SCoia Prant int dev; 170b9e7cb89SCoia Prant 171b9e7cb89SCoia Prant if (!xpcs_rk_mdio_addr_validate(addr)) 172b9e7cb89SCoia Prant return -ENODEV; 173b9e7cb89SCoia Prant 174b9e7cb89SCoia Prant dev = xpcs_rk_mdio_read_remapping(addr, MDIO_MMD_VEND2); 175b9e7cb89SCoia Prant if (dev < 0) 176b9e7cb89SCoia Prant return 0xffff; 177b9e7cb89SCoia Prant 178b9e7cb89SCoia Prant return xpcs_rk_read_reg(pxpcs, dev, reg); 179b9e7cb89SCoia Prant } 180b9e7cb89SCoia Prant 181b9e7cb89SCoia Prant static int xpcs_rk_write_c22(struct mii_bus *bus, int addr, int reg, u16 val) 182b9e7cb89SCoia Prant { 183b9e7cb89SCoia Prant struct dw_xpcs_rk *pxpcs = bus->priv; 184b9e7cb89SCoia Prant int dev; 185b9e7cb89SCoia Prant 186b9e7cb89SCoia Prant if (!xpcs_rk_mdio_addr_validate(addr)) 187b9e7cb89SCoia Prant return -ENODEV; 188b9e7cb89SCoia Prant 189b9e7cb89SCoia Prant dev = xpcs_rk_mdio_write_remapping(addr, MDIO_MMD_VEND2, reg); 190b9e7cb89SCoia Prant if (dev < 0) 191b9e7cb89SCoia Prant return 0; 192b9e7cb89SCoia Prant 193b9e7cb89SCoia Prant return xpcs_rk_write_reg(pxpcs, dev, reg, val); 194b9e7cb89SCoia Prant } 195b9e7cb89SCoia Prant 196b9e7cb89SCoia Prant static int xpcs_rk_read_c45(struct mii_bus *bus, int addr, int dev, int reg) 197b9e7cb89SCoia Prant { 198b9e7cb89SCoia Prant struct dw_xpcs_rk *pxpcs = bus->priv; 199b9e7cb89SCoia Prant 200b9e7cb89SCoia Prant if (!xpcs_rk_mdio_addr_validate(addr)) 201b9e7cb89SCoia Prant return -ENODEV; 202b9e7cb89SCoia Prant 203b9e7cb89SCoia Prant dev = xpcs_rk_mdio_read_remapping(addr, dev); 204b9e7cb89SCoia Prant if (dev < 0) 205b9e7cb89SCoia Prant return 0xffff; 206b9e7cb89SCoia Prant 207b9e7cb89SCoia Prant return xpcs_rk_read_reg(pxpcs, dev, reg); 208b9e7cb89SCoia Prant } 209b9e7cb89SCoia Prant 210b9e7cb89SCoia Prant static int xpcs_rk_write_c45(struct mii_bus *bus, int addr, int dev, int reg, u16 val) 211b9e7cb89SCoia Prant { 212b9e7cb89SCoia Prant struct dw_xpcs_rk *pxpcs = bus->priv; 213b9e7cb89SCoia Prant 214b9e7cb89SCoia Prant if (!xpcs_rk_mdio_addr_validate(addr)) 215b9e7cb89SCoia Prant return -ENODEV; 216b9e7cb89SCoia Prant 217b9e7cb89SCoia Prant dev = xpcs_rk_mdio_write_remapping(addr, dev, reg); 218b9e7cb89SCoia Prant if (dev < 0) 219b9e7cb89SCoia Prant return 0; 220b9e7cb89SCoia Prant 221b9e7cb89SCoia Prant return xpcs_rk_write_reg(pxpcs, dev, reg, val); 222b9e7cb89SCoia Prant } 223b9e7cb89SCoia Prant 224b9e7cb89SCoia Prant static struct dw_xpcs_rk *xpcs_rk_create_data(struct platform_device *pdev) 225b9e7cb89SCoia Prant { 226b9e7cb89SCoia Prant struct dw_xpcs_rk *pxpcs; 227b9e7cb89SCoia Prant 228b9e7cb89SCoia Prant pxpcs = devm_kzalloc(&pdev->dev, sizeof(*pxpcs), GFP_KERNEL); 229b9e7cb89SCoia Prant if (!pxpcs) 230b9e7cb89SCoia Prant return ERR_PTR(-ENOMEM); 231b9e7cb89SCoia Prant 232b9e7cb89SCoia Prant pxpcs->pdev = pdev; 233b9e7cb89SCoia Prant 234b9e7cb89SCoia Prant dev_set_drvdata(&pdev->dev, pxpcs); 235b9e7cb89SCoia Prant 236b9e7cb89SCoia Prant return pxpcs; 237b9e7cb89SCoia Prant } 238b9e7cb89SCoia Prant 239b9e7cb89SCoia Prant static int xpcs_rk_serdes_phy_init(struct dw_xpcs_rk *pxpcs) 240b9e7cb89SCoia Prant { 241b9e7cb89SCoia Prant struct device *dev = &pxpcs->pdev->dev; 242b9e7cb89SCoia Prant 243b9e7cb89SCoia Prant pxpcs->serdes_phy = devm_phy_get(dev, NULL); 244b9e7cb89SCoia Prant if (IS_ERR(pxpcs->serdes_phy)) 245b9e7cb89SCoia Prant return dev_err_probe(dev, PTR_ERR(pxpcs->serdes_phy), 246b9e7cb89SCoia Prant "Failed to get SerDes PHY\n"); 247b9e7cb89SCoia Prant 248b9e7cb89SCoia Prant return 0; 249b9e7cb89SCoia Prant } 250b9e7cb89SCoia Prant 251b9e7cb89SCoia Prant static void xpcs_rk_serdes_phy_poweroff(void *data) 252b9e7cb89SCoia Prant { 253b9e7cb89SCoia Prant struct dw_xpcs_rk *pxpcs = data; 254b9e7cb89SCoia Prant struct device *dev = &pxpcs->pdev->dev; 255b9e7cb89SCoia Prant 256b9e7cb89SCoia Prant phy_power_off(pxpcs->serdes_phy); 257b9e7cb89SCoia Prant phy_exit(pxpcs->serdes_phy); 258b9e7cb89SCoia Prant 259b9e7cb89SCoia Prant dev_pm_genpd_rpm_always_on(dev, false); 260b9e7cb89SCoia Prant } 261b9e7cb89SCoia Prant 262b9e7cb89SCoia Prant static int xpcs_rk_serdes_phy_poweron(struct dw_xpcs_rk *pxpcs) 263b9e7cb89SCoia Prant { 264b9e7cb89SCoia Prant struct device *dev = &pxpcs->pdev->dev; 265b9e7cb89SCoia Prant int ret; 266b9e7cb89SCoia Prant 267b9e7cb89SCoia Prant /* 268b9e7cb89SCoia Prant * The power domain is required and must be enabled, which allows us to 269b9e7cb89SCoia Prant * dynamically turn the CSR clock on/off using PM while keeping the PCS 270b9e7cb89SCoia Prant * powered on. 271b9e7cb89SCoia Prant */ 272b9e7cb89SCoia Prant ret = dev_pm_genpd_rpm_always_on(dev, true); 273b9e7cb89SCoia Prant if (ret) { 274b9e7cb89SCoia Prant dev_err(dev, "Failed to power on power-domains\n"); 275b9e7cb89SCoia Prant return ret; 276b9e7cb89SCoia Prant } 277b9e7cb89SCoia Prant 278b9e7cb89SCoia Prant ret = phy_init(pxpcs->serdes_phy); 279b9e7cb89SCoia Prant if (ret) { 280b9e7cb89SCoia Prant dev_err(dev, "Failed to init SerDes PHY\n"); 281b9e7cb89SCoia Prant goto pm_domain; 282b9e7cb89SCoia Prant } 283b9e7cb89SCoia Prant 284b9e7cb89SCoia Prant ret = phy_power_on(pxpcs->serdes_phy); 285b9e7cb89SCoia Prant if (ret) { 286b9e7cb89SCoia Prant dev_err(dev, "Failed to power on SerDes PHY\n"); 287b9e7cb89SCoia Prant goto serdes_phy; 288b9e7cb89SCoia Prant } 289b9e7cb89SCoia Prant 290b9e7cb89SCoia Prant ret = devm_add_action_or_reset(dev, xpcs_rk_serdes_phy_poweroff, pxpcs); 291b9e7cb89SCoia Prant if (ret) { 292b9e7cb89SCoia Prant dev_err(dev, "Failed to register devm for SerDes PHY: %d\n", ret); 293b9e7cb89SCoia Prant return ret; 294b9e7cb89SCoia Prant } 295b9e7cb89SCoia Prant 296b9e7cb89SCoia Prant return 0; 297b9e7cb89SCoia Prant 298b9e7cb89SCoia Prant serdes_phy: 299b9e7cb89SCoia Prant phy_exit(pxpcs->serdes_phy); 300b9e7cb89SCoia Prant pm_domain: 301b9e7cb89SCoia Prant dev_pm_genpd_rpm_always_on(dev, false); 302b9e7cb89SCoia Prant return ret; 303b9e7cb89SCoia Prant } 304b9e7cb89SCoia Prant 305b9e7cb89SCoia Prant static int xpcs_rk_init_res(struct dw_xpcs_rk *pxpcs) 306b9e7cb89SCoia Prant { 307b9e7cb89SCoia Prant struct platform_device *pdev = pxpcs->pdev; 308b9e7cb89SCoia Prant struct device *dev = &pdev->dev; 309b9e7cb89SCoia Prant struct resource *res; 310b9e7cb89SCoia Prant 311b9e7cb89SCoia Prant res = platform_get_resource(pdev, IORESOURCE_MEM, 0); 312b9e7cb89SCoia Prant if (!res) { 313b9e7cb89SCoia Prant dev_err(dev, "No reg-space found\n"); 314b9e7cb89SCoia Prant return -EINVAL; 315b9e7cb89SCoia Prant } 316b9e7cb89SCoia Prant 317b9e7cb89SCoia Prant if (resource_size(res) < SZ_2M) { 318b9e7cb89SCoia Prant dev_err(dev, "Invalid reg-space size\n"); 319b9e7cb89SCoia Prant return -EINVAL; 320b9e7cb89SCoia Prant } 321b9e7cb89SCoia Prant 322b9e7cb89SCoia Prant pxpcs->reg_base = devm_ioremap_resource(dev, res); 323b9e7cb89SCoia Prant if (IS_ERR(pxpcs->reg_base)) { 324b9e7cb89SCoia Prant dev_err(dev, "Failed to map reg-space\n"); 325b9e7cb89SCoia Prant return PTR_ERR(pxpcs->reg_base); 326b9e7cb89SCoia Prant } 327b9e7cb89SCoia Prant 328b9e7cb89SCoia Prant return 0; 329b9e7cb89SCoia Prant } 330b9e7cb89SCoia Prant 331b9e7cb89SCoia Prant static void xpcs_rk_exit_clk(void *data) 332b9e7cb89SCoia Prant { 333b9e7cb89SCoia Prant struct dw_xpcs_rk *pxpcs = data; 334b9e7cb89SCoia Prant struct device *dev = &pxpcs->pdev->dev; 335b9e7cb89SCoia Prant 336b9e7cb89SCoia Prant pm_runtime_force_suspend(dev); 337b9e7cb89SCoia Prant clk_disable_unprepare(pxpcs->eee_clk); 338b9e7cb89SCoia Prant } 339b9e7cb89SCoia Prant 340b9e7cb89SCoia Prant static int xpcs_rk_init_clk(struct dw_xpcs_rk *pxpcs) 341b9e7cb89SCoia Prant { 342b9e7cb89SCoia Prant struct device *dev = &pxpcs->pdev->dev; 343b9e7cb89SCoia Prant unsigned long rate; 344b9e7cb89SCoia Prant u64 mult; 345b9e7cb89SCoia Prant int ret; 346b9e7cb89SCoia Prant 347b9e7cb89SCoia Prant pxpcs->csr_clk = devm_clk_get(dev, "csr"); 348b9e7cb89SCoia Prant if (IS_ERR(pxpcs->csr_clk)) 349b9e7cb89SCoia Prant return dev_err_probe(dev, PTR_ERR(pxpcs->csr_clk), 350b9e7cb89SCoia Prant "Failed to get CSR clock\n"); 351b9e7cb89SCoia Prant 352b9e7cb89SCoia Prant pxpcs->eee_clk = devm_clk_get(dev, "eee"); 353b9e7cb89SCoia Prant if (IS_ERR(pxpcs->eee_clk)) 354b9e7cb89SCoia Prant return dev_err_probe(dev, PTR_ERR(pxpcs->eee_clk), 355b9e7cb89SCoia Prant "Failed to get EEE clock\n"); 356b9e7cb89SCoia Prant 357b9e7cb89SCoia Prant ret = clk_prepare_enable(pxpcs->eee_clk); 358b9e7cb89SCoia Prant if (ret) { 359b9e7cb89SCoia Prant dev_err(dev, "Failed to enable EEE clock\n"); 360b9e7cb89SCoia Prant return ret; 361b9e7cb89SCoia Prant } 362b9e7cb89SCoia Prant 363b9e7cb89SCoia Prant pm_runtime_set_suspended(dev); 364b9e7cb89SCoia Prant pm_runtime_enable(dev); 365b9e7cb89SCoia Prant 366b9e7cb89SCoia Prant ret = devm_add_action_or_reset(dev, xpcs_rk_exit_clk, pxpcs); 367b9e7cb89SCoia Prant if (ret) { 368*e7cccfdeSCoia Prant dev_err(dev, "Failed to register devm for xpcs_rk_exit_clk: %d\n", ret); 369b9e7cb89SCoia Prant return ret; 370b9e7cb89SCoia Prant } 371b9e7cb89SCoia Prant 372b9e7cb89SCoia Prant /* 373b9e7cb89SCoia Prant * Compute the multiplier for the EEE clock so that 374b9e7cb89SCoia Prant * clk_eee_period * (mult_fact + 1) falls within 80..120 ns. 375b9e7cb89SCoia Prant * 376b9e7cb89SCoia Prant * On RK3568, clk_xpcs_eee is muxed between gpll200 (200 MHz, 5 ns) 377b9e7cb89SCoia Prant * and cpll125 (125 MHz, 8 ns), selected by CRU_CLKSEL_CON29 bit 13. 378b9e7cb89SCoia Prant * The reset value is 0 (200 MHz), but derive the value at runtime to 379b9e7cb89SCoia Prant * stay correct if the mux is changed by a board. 380b9e7cb89SCoia Prant * 381b9e7cb89SCoia Prant * Use a 64-bit intermediate: on 32-bit builds, 100 * 200000000 382b9e7cb89SCoia Prant * does not fit in unsigned long. Clamp to the 4-bit 383b9e7cb89SCoia Prant * DW_VR_MII_EEE_MULT_FACT_100NS field. The mux only provides 384b9e7cb89SCoia Prant * 125 MHz or 200 MHz, so the rate cannot drop below the 5 MHz 385b9e7cb89SCoia Prant * threshold where DIV_ROUND_CLOSEST_ULL() would return 0 and the 386b9e7cb89SCoia Prant * subtraction below would underflow. 387b9e7cb89SCoia Prant */ 388b9e7cb89SCoia Prant rate = clk_get_rate(pxpcs->eee_clk); 389b9e7cb89SCoia Prant if (!rate) 390b9e7cb89SCoia Prant return dev_err_probe(dev, -EINVAL, "Invalid EEE clock rate\n"); 391b9e7cb89SCoia Prant 392b9e7cb89SCoia Prant mult = DIV_ROUND_CLOSEST_ULL(100ULL * rate, NSEC_PER_SEC) - 1; 393b9e7cb89SCoia Prant pxpcs->eee_mult_fact = min_t(u64, mult, 15); 394b9e7cb89SCoia Prant return 0; 395b9e7cb89SCoia Prant } 396b9e7cb89SCoia Prant 397b9e7cb89SCoia Prant static int xpcs_rk_init_bus(struct dw_xpcs_rk *pxpcs) 398b9e7cb89SCoia Prant { 399b9e7cb89SCoia Prant struct device *dev = &pxpcs->pdev->dev; 400b9e7cb89SCoia Prant static atomic_t id = ATOMIC_INIT(-1); 401b9e7cb89SCoia Prant struct mii_bus *bus; 402b9e7cb89SCoia Prant int ret; 403b9e7cb89SCoia Prant 404b9e7cb89SCoia Prant bus = devm_mdiobus_alloc_size(dev, 0); 405b9e7cb89SCoia Prant if (!bus) 406b9e7cb89SCoia Prant return -ENOMEM; 407b9e7cb89SCoia Prant 408b9e7cb89SCoia Prant bus->name = "Rockchip DW XPCS MCI/APB3"; 409b9e7cb89SCoia Prant bus->read = xpcs_rk_read_c22; 410b9e7cb89SCoia Prant bus->write = xpcs_rk_write_c22; 411b9e7cb89SCoia Prant bus->read_c45 = xpcs_rk_read_c45; 412b9e7cb89SCoia Prant bus->write_c45 = xpcs_rk_write_c45; 413b9e7cb89SCoia Prant bus->phy_mask = ~0; 414b9e7cb89SCoia Prant bus->parent = dev; 415b9e7cb89SCoia Prant bus->priv = pxpcs; 416b9e7cb89SCoia Prant 417b9e7cb89SCoia Prant snprintf(bus->id, MII_BUS_ID_SIZE, 418b9e7cb89SCoia Prant "rockchip_dwxpcs-%x", atomic_inc_return(&id)); 419b9e7cb89SCoia Prant 420b9e7cb89SCoia Prant /* 421b9e7cb89SCoia Prant * MDIO-bus here serves as just a back-end engine abstracting out 422b9e7cb89SCoia Prant * the MDIO and MCI/APB3 IO interfaces utilized for the Rockchip DWXPCS CSRs 423b9e7cb89SCoia Prant * access. 424b9e7cb89SCoia Prant */ 425b9e7cb89SCoia Prant ret = devm_mdiobus_register(dev, bus); 426b9e7cb89SCoia Prant if (ret) { 427b9e7cb89SCoia Prant dev_err(dev, "Failed to create MDIO bus\n"); 428b9e7cb89SCoia Prant return ret; 429b9e7cb89SCoia Prant } 430b9e7cb89SCoia Prant 431b9e7cb89SCoia Prant pxpcs->bus = bus; 432b9e7cb89SCoia Prant return 0; 433b9e7cb89SCoia Prant } 434b9e7cb89SCoia Prant 435b9e7cb89SCoia Prant static int xpcs_rk_probe(struct platform_device *pdev) 436b9e7cb89SCoia Prant { 437b9e7cb89SCoia Prant struct dw_xpcs_rk *pxpcs; 438b9e7cb89SCoia Prant int ret; 439b9e7cb89SCoia Prant 440b9e7cb89SCoia Prant pxpcs = xpcs_rk_create_data(pdev); 441b9e7cb89SCoia Prant if (IS_ERR(pxpcs)) 442b9e7cb89SCoia Prant return PTR_ERR(pxpcs); 443b9e7cb89SCoia Prant 444b9e7cb89SCoia Prant /* 445b9e7cb89SCoia Prant * The XPCS lives in the PD_PIPE power domain. The domain must be 446b9e7cb89SCoia Prant * powered on before any register access, otherwise the SoC will 447b9e7cb89SCoia Prant * trigger a synchronous external abort (SError). 448b9e7cb89SCoia Prant * 449b9e7cb89SCoia Prant * Accessing the XPCS registers also requires a TX clock from the 450b9e7cb89SCoia Prant * SerDes, which is needed for the soft reset. 451b9e7cb89SCoia Prant */ 452b9e7cb89SCoia Prant ret = xpcs_rk_serdes_phy_init(pxpcs); 453b9e7cb89SCoia Prant if (ret) 454b9e7cb89SCoia Prant return ret; 455b9e7cb89SCoia Prant 456b9e7cb89SCoia Prant ret = xpcs_rk_serdes_phy_poweron(pxpcs); 457b9e7cb89SCoia Prant if (ret) 458b9e7cb89SCoia Prant return ret; 459b9e7cb89SCoia Prant 460b9e7cb89SCoia Prant ret = xpcs_rk_init_res(pxpcs); 461b9e7cb89SCoia Prant if (ret) 462b9e7cb89SCoia Prant return ret; 463b9e7cb89SCoia Prant 464b9e7cb89SCoia Prant ret = xpcs_rk_init_clk(pxpcs); 465b9e7cb89SCoia Prant if (ret) 466b9e7cb89SCoia Prant return ret; 467b9e7cb89SCoia Prant 468b9e7cb89SCoia Prant ret = xpcs_rk_init_bus(pxpcs); 469b9e7cb89SCoia Prant if (ret) 470b9e7cb89SCoia Prant return ret; 471b9e7cb89SCoia Prant 472b9e7cb89SCoia Prant return 0; 473b9e7cb89SCoia Prant } 474b9e7cb89SCoia Prant 475b9e7cb89SCoia Prant static const struct of_device_id xpcs_rk_of_ids[] = { 476b9e7cb89SCoia Prant { .compatible = "rockchip,rk3568-xpcs" }, 477b9e7cb89SCoia Prant { /* sentinel */ }, 478b9e7cb89SCoia Prant }; 479b9e7cb89SCoia Prant MODULE_DEVICE_TABLE(of, xpcs_rk_of_ids); 480b9e7cb89SCoia Prant 481b9e7cb89SCoia Prant struct dw_xpcs *xpcs_rk_create(struct device *dev, struct device_node *pcs, int port) 482b9e7cb89SCoia Prant { 483b9e7cb89SCoia Prant struct platform_device *pdev; 484b9e7cb89SCoia Prant struct dw_xpcs_rk *pxpcs; 485b9e7cb89SCoia Prant struct dw_xpcs *xpcs; 486b9e7cb89SCoia Prant 487b9e7cb89SCoia Prant if (!of_device_is_available(pcs)) 488b9e7cb89SCoia Prant return ERR_PTR(-ENODEV); 489b9e7cb89SCoia Prant 490b9e7cb89SCoia Prant if (!of_match_node(xpcs_rk_of_ids, pcs)) 491b9e7cb89SCoia Prant return ERR_PTR(-EINVAL); 492b9e7cb89SCoia Prant 493b9e7cb89SCoia Prant if (!xpcs_rk_mdio_addr_validate(port)) 494b9e7cb89SCoia Prant return ERR_PTR(-EINVAL); 495b9e7cb89SCoia Prant 496b9e7cb89SCoia Prant pdev = of_find_device_by_node(pcs); 497b9e7cb89SCoia Prant if (!pdev) 498b9e7cb89SCoia Prant return ERR_PTR(-EPROBE_DEFER); 499b9e7cb89SCoia Prant 500b9e7cb89SCoia Prant /* 501b9e7cb89SCoia Prant * Establish the device link before reading the supplier's drvdata. 502b9e7cb89SCoia Prant * device_link_add() does not fail on a supplier that is unbinding; 503b9e7cb89SCoia Prant * it creates the link in DL_STATE_SUPPLIER_UNBIND. 504b9e7cb89SCoia Prant * 505b9e7cb89SCoia Prant * The check below only confirms that the supplier is bound at this 506b9e7cb89SCoia Prant * point. It does not fully protect against a concurrent unbind 507b9e7cb89SCoia Prant * between here and xpcs_create_mdiodev(); closing that window would 508b9e7cb89SCoia Prant * require reading a managed link's status or device_lock(), neither 509b9e7cb89SCoia Prant * of which is acceptable on this path. The window is small and 510b9e7cb89SCoia Prant * requires an explicit unbind from userspace. Discussed on 511b9e7cb89SCoia Prant * driver-core. 512b9e7cb89SCoia Prant * 513b9e7cb89SCoia Prant * The link is released automatically when the consumer device is 514b9e7cb89SCoia Prant * destroyed (DL_FLAG_AUTOREMOVE_CONSUMER), so no explicit 515b9e7cb89SCoia Prant * device_link_remove() is needed on the failure paths. 516b9e7cb89SCoia Prant */ 517b9e7cb89SCoia Prant if (!device_link_add(dev, &pdev->dev, DL_FLAG_AUTOREMOVE_CONSUMER)) { 518b9e7cb89SCoia Prant put_device(&pdev->dev); 519b9e7cb89SCoia Prant return ERR_PTR(-EPROBE_DEFER); 520b9e7cb89SCoia Prant } 521b9e7cb89SCoia Prant 522b9e7cb89SCoia Prant if (READ_ONCE(pdev->dev.links.status) != DL_DEV_DRIVER_BOUND) { 523b9e7cb89SCoia Prant put_device(&pdev->dev); 524b9e7cb89SCoia Prant return ERR_PTR(-EPROBE_DEFER); 525b9e7cb89SCoia Prant } 526b9e7cb89SCoia Prant 527b9e7cb89SCoia Prant pxpcs = platform_get_drvdata(pdev); 528b9e7cb89SCoia Prant if (!pxpcs || !pxpcs->bus) { 529b9e7cb89SCoia Prant put_device(&pdev->dev); 530b9e7cb89SCoia Prant return ERR_PTR(-EPROBE_DEFER); 531b9e7cb89SCoia Prant } 532b9e7cb89SCoia Prant 533b9e7cb89SCoia Prant xpcs = xpcs_create_mdiodev(pxpcs->bus, port); 534b9e7cb89SCoia Prant if (IS_ERR(xpcs)) { 535b9e7cb89SCoia Prant put_device(&pdev->dev); 536b9e7cb89SCoia Prant return xpcs; 537b9e7cb89SCoia Prant } 538b9e7cb89SCoia Prant 539b9e7cb89SCoia Prant xpcs_config_eee_mult_fact(xpcs, pxpcs->eee_mult_fact); 540b9e7cb89SCoia Prant put_device(&pdev->dev); 541b9e7cb89SCoia Prant return xpcs; 542b9e7cb89SCoia Prant } 543b9e7cb89SCoia Prant EXPORT_SYMBOL_GPL(xpcs_rk_create); 544b9e7cb89SCoia Prant 545b9e7cb89SCoia Prant static int xpcs_rk_pm_runtime_suspend(struct device *dev) 546b9e7cb89SCoia Prant { 547b9e7cb89SCoia Prant struct dw_xpcs_rk *pxpcs = dev_get_drvdata(dev); 548b9e7cb89SCoia Prant 549b9e7cb89SCoia Prant clk_disable_unprepare(pxpcs->csr_clk); 550b9e7cb89SCoia Prant 551b9e7cb89SCoia Prant return 0; 552b9e7cb89SCoia Prant } 553b9e7cb89SCoia Prant 554b9e7cb89SCoia Prant static int xpcs_rk_pm_runtime_resume(struct device *dev) 555b9e7cb89SCoia Prant { 556b9e7cb89SCoia Prant struct dw_xpcs_rk *pxpcs = dev_get_drvdata(dev); 557b9e7cb89SCoia Prant 558b9e7cb89SCoia Prant return clk_prepare_enable(pxpcs->csr_clk); 559b9e7cb89SCoia Prant } 560b9e7cb89SCoia Prant 561b9e7cb89SCoia Prant static int xpcs_rk_system_suspend(struct device *dev) 562b9e7cb89SCoia Prant { 563b9e7cb89SCoia Prant /* 564b9e7cb89SCoia Prant * Keep the PD_PIPE power domain on during system suspend. 565b9e7cb89SCoia Prant * 566b9e7cb89SCoia Prant * PD_PIPE is shared with SATA/PCIe and would be powered down by 567b9e7cb89SCoia Prant * genpd once all its consumers are suspended, killing the SerDes 568b9e7cb89SCoia Prant * and breaking MAC WoL. Mark the XPCS as part of the wakeup path 569b9e7cb89SCoia Prant * so genpd keeps the domain on. Unconditional because the XPCS 570b9e7cb89SCoia Prant * core has no callback to convey the MAC WoL state. 571b9e7cb89SCoia Prant */ 572b9e7cb89SCoia Prant device_set_wakeup_path(dev); 573b9e7cb89SCoia Prant return 0; 574b9e7cb89SCoia Prant } 575b9e7cb89SCoia Prant 576b9e7cb89SCoia Prant static int xpcs_rk_system_resume(struct device *dev) 577b9e7cb89SCoia Prant { 578b9e7cb89SCoia Prant return 0; 579b9e7cb89SCoia Prant } 580b9e7cb89SCoia Prant 581b9e7cb89SCoia Prant static _DEFINE_DEV_PM_OPS(xpcs_rk_pm_ops, 582b9e7cb89SCoia Prant xpcs_rk_system_suspend, xpcs_rk_system_resume, 583b9e7cb89SCoia Prant xpcs_rk_pm_runtime_suspend, xpcs_rk_pm_runtime_resume, 584b9e7cb89SCoia Prant NULL); 585b9e7cb89SCoia Prant 586b9e7cb89SCoia Prant static struct platform_driver xpcs_rk_driver = { 587b9e7cb89SCoia Prant .probe = xpcs_rk_probe, 588b9e7cb89SCoia Prant .driver = { 589b9e7cb89SCoia Prant .name = "rk_xpcs-dwxpcs", 590b9e7cb89SCoia Prant .pm = pm_ptr(&xpcs_rk_pm_ops), 591b9e7cb89SCoia Prant .of_match_table = xpcs_rk_of_ids, 592b9e7cb89SCoia Prant }, 593b9e7cb89SCoia Prant }; 594b9e7cb89SCoia Prant module_platform_driver(xpcs_rk_driver); 595b9e7cb89SCoia Prant 596b9e7cb89SCoia Prant MODULE_DESCRIPTION("Rockchip XPCS platform device driver"); 597b9e7cb89SCoia Prant MODULE_AUTHOR("Coia Prant <coiaprant@gmail.com>"); 598b9e7cb89SCoia Prant MODULE_LICENSE("GPL"); 599