1 // SPDX-License-Identifier: GPL-2.0 2 /* 3 * Eswin DWC Ethernet linux driver 4 * 5 * Copyright 2025, Beijing ESWIN Computing Technology Co., Ltd. 6 * 7 * Authors: 8 * Zhi Li <lizhi2@eswincomputing.com> 9 * Shuang Liang <liangshuang@eswincomputing.com> 10 * Shangjuan Wei <weishangjuan@eswincomputing.com> 11 */ 12 13 #include <linux/platform_device.h> 14 #include <linux/mfd/syscon.h> 15 #include <linux/pm_runtime.h> 16 #include <linux/stmmac.h> 17 #include <linux/regmap.h> 18 #include <linux/of.h> 19 20 #include "stmmac_platform.h" 21 22 /* eth_phy_ctrl_offset eth0:0x100 */ 23 #define EIC7700_ETH_TX_CLK_SEL BIT(16) 24 #define EIC7700_ETH_PHY_INTF_SELI BIT(0) 25 26 /* eth_axi_lp_ctrl_offset eth0:0x108 */ 27 #define EIC7700_ETH_CSYSREQ_VAL BIT(0) 28 29 /* 30 * TX/RX Clock Delay Bit Masks: 31 * - TX Delay: bits [14:8] — TX_CLK delay (unit: 0.1ns per bit) 32 * - RX Delay: bits [30:24] — RX_CLK delay (unit: 0.1ns per bit) 33 */ 34 #define EIC7700_ETH_TX_ADJ_DELAY GENMASK(14, 8) 35 #define EIC7700_ETH_RX_ADJ_DELAY GENMASK(30, 24) 36 37 #define EIC7700_MAX_DELAY_UNIT 0x7F 38 39 static const char * const eic7700_clk_names[] = { 40 "tx", "axi", "cfg", 41 }; 42 43 struct eic7700_qos_priv { 44 struct plat_stmmacenet_data *plat_dat; 45 }; 46 47 static int eic7700_clks_config(void *priv, bool enabled) 48 { 49 struct eic7700_qos_priv *dwc = (struct eic7700_qos_priv *)priv; 50 struct plat_stmmacenet_data *plat = dwc->plat_dat; 51 int ret = 0; 52 53 if (enabled) 54 ret = clk_bulk_prepare_enable(plat->num_clks, plat->clks); 55 else 56 clk_bulk_disable_unprepare(plat->num_clks, plat->clks); 57 58 return ret; 59 } 60 61 static int eic7700_dwmac_init(struct device *dev, void *priv) 62 { 63 struct eic7700_qos_priv *dwc = priv; 64 65 return eic7700_clks_config(dwc, true); 66 } 67 68 static void eic7700_dwmac_exit(struct device *dev, void *priv) 69 { 70 struct eic7700_qos_priv *dwc = priv; 71 72 eic7700_clks_config(dwc, false); 73 } 74 75 static int eic7700_dwmac_suspend(struct device *dev, void *priv) 76 { 77 return pm_runtime_force_suspend(dev); 78 } 79 80 static int eic7700_dwmac_resume(struct device *dev, void *priv) 81 { 82 int ret; 83 84 ret = pm_runtime_force_resume(dev); 85 if (ret) 86 dev_err(dev, "%s failed: %d\n", __func__, ret); 87 88 return ret; 89 } 90 91 static int eic7700_dwmac_probe(struct platform_device *pdev) 92 { 93 struct plat_stmmacenet_data *plat_dat; 94 struct stmmac_resources stmmac_res; 95 struct eic7700_qos_priv *dwc_priv; 96 struct regmap *eic7700_hsp_regmap; 97 u32 eth_axi_lp_ctrl_offset; 98 u32 eth_phy_ctrl_offset; 99 u32 eth_phy_ctrl_regset; 100 u32 eth_rxd_dly_offset; 101 u32 eth_dly_param = 0; 102 u32 delay_ps; 103 int i, ret; 104 105 ret = stmmac_get_platform_resources(pdev, &stmmac_res); 106 if (ret) 107 return dev_err_probe(&pdev->dev, ret, 108 "failed to get resources\n"); 109 110 plat_dat = devm_stmmac_probe_config_dt(pdev, stmmac_res.mac); 111 if (IS_ERR(plat_dat)) 112 return dev_err_probe(&pdev->dev, PTR_ERR(plat_dat), 113 "dt configuration failed\n"); 114 115 dwc_priv = devm_kzalloc(&pdev->dev, sizeof(*dwc_priv), GFP_KERNEL); 116 if (!dwc_priv) 117 return -ENOMEM; 118 119 /* Read rx-internal-delay-ps and update rx_clk delay */ 120 if (!of_property_read_u32(pdev->dev.of_node, 121 "rx-internal-delay-ps", &delay_ps)) { 122 u32 val = min(delay_ps / 100, EIC7700_MAX_DELAY_UNIT); 123 124 eth_dly_param &= ~EIC7700_ETH_RX_ADJ_DELAY; 125 eth_dly_param |= FIELD_PREP(EIC7700_ETH_RX_ADJ_DELAY, val); 126 } else { 127 return dev_err_probe(&pdev->dev, -EINVAL, 128 "missing required property rx-internal-delay-ps\n"); 129 } 130 131 /* Read tx-internal-delay-ps and update tx_clk delay */ 132 if (!of_property_read_u32(pdev->dev.of_node, 133 "tx-internal-delay-ps", &delay_ps)) { 134 u32 val = min(delay_ps / 100, EIC7700_MAX_DELAY_UNIT); 135 136 eth_dly_param &= ~EIC7700_ETH_TX_ADJ_DELAY; 137 eth_dly_param |= FIELD_PREP(EIC7700_ETH_TX_ADJ_DELAY, val); 138 } else { 139 return dev_err_probe(&pdev->dev, -EINVAL, 140 "missing required property tx-internal-delay-ps\n"); 141 } 142 143 eic7700_hsp_regmap = syscon_regmap_lookup_by_phandle(pdev->dev.of_node, 144 "eswin,hsp-sp-csr"); 145 if (IS_ERR(eic7700_hsp_regmap)) 146 return dev_err_probe(&pdev->dev, 147 PTR_ERR(eic7700_hsp_regmap), 148 "Failed to get hsp-sp-csr regmap\n"); 149 150 ret = of_property_read_u32_index(pdev->dev.of_node, 151 "eswin,hsp-sp-csr", 152 1, ð_phy_ctrl_offset); 153 if (ret) 154 return dev_err_probe(&pdev->dev, ret, 155 "can't get eth_phy_ctrl_offset\n"); 156 157 regmap_read(eic7700_hsp_regmap, eth_phy_ctrl_offset, 158 ð_phy_ctrl_regset); 159 eth_phy_ctrl_regset |= 160 (EIC7700_ETH_TX_CLK_SEL | EIC7700_ETH_PHY_INTF_SELI); 161 regmap_write(eic7700_hsp_regmap, eth_phy_ctrl_offset, 162 eth_phy_ctrl_regset); 163 164 ret = of_property_read_u32_index(pdev->dev.of_node, 165 "eswin,hsp-sp-csr", 166 2, ð_axi_lp_ctrl_offset); 167 if (ret) 168 return dev_err_probe(&pdev->dev, ret, 169 "can't get eth_axi_lp_ctrl_offset\n"); 170 171 regmap_write(eic7700_hsp_regmap, eth_axi_lp_ctrl_offset, 172 EIC7700_ETH_CSYSREQ_VAL); 173 174 ret = of_property_read_u32_index(pdev->dev.of_node, 175 "eswin,hsp-sp-csr", 176 3, ð_rxd_dly_offset); 177 if (ret) 178 return dev_err_probe(&pdev->dev, ret, 179 "can't get eth_rxd_dly_offset\n"); 180 181 regmap_write(eic7700_hsp_regmap, eth_rxd_dly_offset, 182 eth_dly_param); 183 184 plat_dat->num_clks = ARRAY_SIZE(eic7700_clk_names); 185 plat_dat->clks = devm_kcalloc(&pdev->dev, 186 plat_dat->num_clks, 187 sizeof(*plat_dat->clks), 188 GFP_KERNEL); 189 if (!plat_dat->clks) 190 return -ENOMEM; 191 192 for (i = 0; i < ARRAY_SIZE(eic7700_clk_names); i++) 193 plat_dat->clks[i].id = eic7700_clk_names[i]; 194 195 ret = devm_clk_bulk_get_optional(&pdev->dev, 196 plat_dat->num_clks, 197 plat_dat->clks); 198 if (ret) 199 return dev_err_probe(&pdev->dev, ret, 200 "Failed to get clocks\n"); 201 202 plat_dat->clk_tx_i = stmmac_pltfr_find_clk(plat_dat, "tx"); 203 plat_dat->set_clk_tx_rate = stmmac_set_clk_tx_rate; 204 plat_dat->clks_config = eic7700_clks_config; 205 plat_dat->bsp_priv = dwc_priv; 206 dwc_priv->plat_dat = plat_dat; 207 plat_dat->init = eic7700_dwmac_init; 208 plat_dat->exit = eic7700_dwmac_exit; 209 plat_dat->suspend = eic7700_dwmac_suspend; 210 plat_dat->resume = eic7700_dwmac_resume; 211 212 return devm_stmmac_pltfr_probe(pdev, plat_dat, &stmmac_res); 213 } 214 215 static const struct of_device_id eic7700_dwmac_match[] = { 216 { .compatible = "eswin,eic7700-qos-eth" }, 217 { } 218 }; 219 MODULE_DEVICE_TABLE(of, eic7700_dwmac_match); 220 221 static struct platform_driver eic7700_dwmac_driver = { 222 .probe = eic7700_dwmac_probe, 223 .driver = { 224 .name = "eic7700-eth-dwmac", 225 .pm = &stmmac_pltfr_pm_ops, 226 .of_match_table = eic7700_dwmac_match, 227 }, 228 }; 229 module_platform_driver(eic7700_dwmac_driver); 230 231 MODULE_AUTHOR("Zhi Li <lizhi2@eswincomputing.com>"); 232 MODULE_AUTHOR("Shuang Liang <liangshuang@eswincomputing.com>"); 233 MODULE_AUTHOR("Shangjuan Wei <weishangjuan@eswincomputing.com>"); 234 MODULE_DESCRIPTION("Eswin eic7700 qos ethernet driver"); 235 MODULE_LICENSE("GPL"); 236