1 // SPDX-License-Identifier: GPL-2.0 2 /* 3 * dwmac-imx.c - DWMAC Specific Glue layer for NXP imx8 4 * 5 * Copyright 2020 NXP 6 * 7 */ 8 9 #include <linux/clk.h> 10 #include <linux/gpio/consumer.h> 11 #include <linux/kernel.h> 12 #include <linux/mfd/syscon.h> 13 #include <linux/module.h> 14 #include <linux/of.h> 15 #include <linux/of_device.h> 16 #include <linux/of_net.h> 17 #include <linux/phy.h> 18 #include <linux/platform_device.h> 19 #include <linux/pm_wakeirq.h> 20 #include <linux/regmap.h> 21 #include <linux/slab.h> 22 #include <linux/stmmac.h> 23 24 #include "stmmac_platform.h" 25 26 #define GPR_ENET_QOS_INTF_MODE_MASK GENMASK(21, 16) 27 #define GPR_ENET_QOS_INTF_SEL_MII (0x0 << 16) 28 #define GPR_ENET_QOS_INTF_SEL_RMII (0x4 << 16) 29 #define GPR_ENET_QOS_INTF_SEL_RGMII (0x1 << 16) 30 #define GPR_ENET_QOS_CLK_GEN_EN (0x1 << 19) 31 #define GPR_ENET_QOS_CLK_TX_CLK_SEL (0x1 << 20) 32 #define GPR_ENET_QOS_RGMII_EN (0x1 << 21) 33 34 #define MX93_GPR_ENET_QOS_INTF_MODE_MASK GENMASK(3, 0) 35 #define MX93_GPR_ENET_QOS_INTF_SEL_MII (0x0 << 1) 36 #define MX93_GPR_ENET_QOS_INTF_SEL_RMII (0x4 << 1) 37 #define MX93_GPR_ENET_QOS_INTF_SEL_RGMII (0x1 << 1) 38 #define MX93_GPR_ENET_QOS_CLK_GEN_EN (0x1 << 0) 39 40 struct imx_dwmac_ops { 41 u32 addr_width; 42 bool mac_rgmii_txclk_auto_adj; 43 44 int (*set_intf_mode)(struct plat_stmmacenet_data *plat_dat); 45 }; 46 47 struct imx_priv_data { 48 struct device *dev; 49 struct clk *clk_tx; 50 struct clk *clk_mem; 51 struct regmap *intf_regmap; 52 u32 intf_reg_off; 53 bool rmii_refclk_ext; 54 55 const struct imx_dwmac_ops *ops; 56 struct plat_stmmacenet_data *plat_dat; 57 }; 58 59 static int imx8mp_set_intf_mode(struct plat_stmmacenet_data *plat_dat) 60 { 61 struct imx_priv_data *dwmac = plat_dat->bsp_priv; 62 int val; 63 64 switch (plat_dat->interface) { 65 case PHY_INTERFACE_MODE_MII: 66 val = GPR_ENET_QOS_INTF_SEL_MII; 67 break; 68 case PHY_INTERFACE_MODE_RMII: 69 val = GPR_ENET_QOS_INTF_SEL_RMII; 70 val |= (dwmac->rmii_refclk_ext ? 0 : GPR_ENET_QOS_CLK_TX_CLK_SEL); 71 break; 72 case PHY_INTERFACE_MODE_RGMII: 73 case PHY_INTERFACE_MODE_RGMII_ID: 74 case PHY_INTERFACE_MODE_RGMII_RXID: 75 case PHY_INTERFACE_MODE_RGMII_TXID: 76 val = GPR_ENET_QOS_INTF_SEL_RGMII | 77 GPR_ENET_QOS_RGMII_EN; 78 break; 79 default: 80 pr_debug("imx dwmac doesn't support %d interface\n", 81 plat_dat->interface); 82 return -EINVAL; 83 } 84 85 val |= GPR_ENET_QOS_CLK_GEN_EN; 86 return regmap_update_bits(dwmac->intf_regmap, dwmac->intf_reg_off, 87 GPR_ENET_QOS_INTF_MODE_MASK, val); 88 }; 89 90 static int 91 imx8dxl_set_intf_mode(struct plat_stmmacenet_data *plat_dat) 92 { 93 int ret = 0; 94 95 /* TBD: depends on imx8dxl scu interfaces to be upstreamed */ 96 return ret; 97 } 98 99 static int imx93_set_intf_mode(struct plat_stmmacenet_data *plat_dat) 100 { 101 struct imx_priv_data *dwmac = plat_dat->bsp_priv; 102 int val; 103 104 switch (plat_dat->interface) { 105 case PHY_INTERFACE_MODE_MII: 106 val = MX93_GPR_ENET_QOS_INTF_SEL_MII; 107 break; 108 case PHY_INTERFACE_MODE_RMII: 109 val = MX93_GPR_ENET_QOS_INTF_SEL_RMII; 110 break; 111 case PHY_INTERFACE_MODE_RGMII: 112 case PHY_INTERFACE_MODE_RGMII_ID: 113 case PHY_INTERFACE_MODE_RGMII_RXID: 114 case PHY_INTERFACE_MODE_RGMII_TXID: 115 val = MX93_GPR_ENET_QOS_INTF_SEL_RGMII; 116 break; 117 default: 118 dev_dbg(dwmac->dev, "imx dwmac doesn't support %d interface\n", 119 plat_dat->interface); 120 return -EINVAL; 121 } 122 123 val |= MX93_GPR_ENET_QOS_CLK_GEN_EN; 124 return regmap_update_bits(dwmac->intf_regmap, dwmac->intf_reg_off, 125 MX93_GPR_ENET_QOS_INTF_MODE_MASK, val); 126 }; 127 128 static int imx_dwmac_clks_config(void *priv, bool enabled) 129 { 130 struct imx_priv_data *dwmac = priv; 131 int ret = 0; 132 133 if (enabled) { 134 ret = clk_prepare_enable(dwmac->clk_mem); 135 if (ret) { 136 dev_err(dwmac->dev, "mem clock enable failed\n"); 137 return ret; 138 } 139 140 ret = clk_prepare_enable(dwmac->clk_tx); 141 if (ret) { 142 dev_err(dwmac->dev, "tx clock enable failed\n"); 143 clk_disable_unprepare(dwmac->clk_mem); 144 return ret; 145 } 146 } else { 147 clk_disable_unprepare(dwmac->clk_tx); 148 clk_disable_unprepare(dwmac->clk_mem); 149 } 150 151 return ret; 152 } 153 154 static int imx_dwmac_init(struct platform_device *pdev, void *priv) 155 { 156 struct plat_stmmacenet_data *plat_dat; 157 struct imx_priv_data *dwmac = priv; 158 int ret; 159 160 plat_dat = dwmac->plat_dat; 161 162 if (dwmac->ops->set_intf_mode) { 163 ret = dwmac->ops->set_intf_mode(plat_dat); 164 if (ret) 165 return ret; 166 } 167 168 return 0; 169 } 170 171 static void imx_dwmac_exit(struct platform_device *pdev, void *priv) 172 { 173 /* nothing to do now */ 174 } 175 176 static void imx_dwmac_fix_speed(void *priv, unsigned int speed) 177 { 178 struct plat_stmmacenet_data *plat_dat; 179 struct imx_priv_data *dwmac = priv; 180 unsigned long rate; 181 int err; 182 183 plat_dat = dwmac->plat_dat; 184 185 if (dwmac->ops->mac_rgmii_txclk_auto_adj || 186 (plat_dat->interface == PHY_INTERFACE_MODE_RMII) || 187 (plat_dat->interface == PHY_INTERFACE_MODE_MII)) 188 return; 189 190 switch (speed) { 191 case SPEED_1000: 192 rate = 125000000; 193 break; 194 case SPEED_100: 195 rate = 25000000; 196 break; 197 case SPEED_10: 198 rate = 2500000; 199 break; 200 default: 201 dev_err(dwmac->dev, "invalid speed %u\n", speed); 202 return; 203 } 204 205 err = clk_set_rate(dwmac->clk_tx, rate); 206 if (err < 0) 207 dev_err(dwmac->dev, "failed to set tx rate %lu\n", rate); 208 } 209 210 static int 211 imx_dwmac_parse_dt(struct imx_priv_data *dwmac, struct device *dev) 212 { 213 struct device_node *np = dev->of_node; 214 int err = 0; 215 216 if (of_get_property(np, "snps,rmii_refclk_ext", NULL)) 217 dwmac->rmii_refclk_ext = true; 218 219 dwmac->clk_tx = devm_clk_get(dev, "tx"); 220 if (IS_ERR(dwmac->clk_tx)) { 221 dev_err(dev, "failed to get tx clock\n"); 222 return PTR_ERR(dwmac->clk_tx); 223 } 224 225 dwmac->clk_mem = NULL; 226 227 if (of_machine_is_compatible("fsl,imx8dxl") || 228 of_machine_is_compatible("fsl,imx93")) { 229 dwmac->clk_mem = devm_clk_get(dev, "mem"); 230 if (IS_ERR(dwmac->clk_mem)) { 231 dev_err(dev, "failed to get mem clock\n"); 232 return PTR_ERR(dwmac->clk_mem); 233 } 234 } 235 236 if (of_machine_is_compatible("fsl,imx8mp") || 237 of_machine_is_compatible("fsl,imx93")) { 238 /* Binding doc describes the propety: 239 * is required by i.MX8MP, i.MX93. 240 * is optinoal for i.MX8DXL. 241 */ 242 dwmac->intf_regmap = syscon_regmap_lookup_by_phandle(np, "intf_mode"); 243 if (IS_ERR(dwmac->intf_regmap)) 244 return PTR_ERR(dwmac->intf_regmap); 245 246 err = of_property_read_u32_index(np, "intf_mode", 1, &dwmac->intf_reg_off); 247 if (err) { 248 dev_err(dev, "Can't get intf mode reg offset (%d)\n", err); 249 return err; 250 } 251 } 252 253 return err; 254 } 255 256 static int imx_dwmac_probe(struct platform_device *pdev) 257 { 258 struct plat_stmmacenet_data *plat_dat; 259 struct stmmac_resources stmmac_res; 260 struct imx_priv_data *dwmac; 261 const struct imx_dwmac_ops *data; 262 int ret; 263 264 ret = stmmac_get_platform_resources(pdev, &stmmac_res); 265 if (ret) 266 return ret; 267 268 dwmac = devm_kzalloc(&pdev->dev, sizeof(*dwmac), GFP_KERNEL); 269 if (!dwmac) 270 return -ENOMEM; 271 272 plat_dat = stmmac_probe_config_dt(pdev, stmmac_res.mac); 273 if (IS_ERR(plat_dat)) 274 return PTR_ERR(plat_dat); 275 276 data = of_device_get_match_data(&pdev->dev); 277 if (!data) { 278 dev_err(&pdev->dev, "failed to get match data\n"); 279 ret = -EINVAL; 280 goto err_match_data; 281 } 282 283 dwmac->ops = data; 284 dwmac->dev = &pdev->dev; 285 286 ret = imx_dwmac_parse_dt(dwmac, &pdev->dev); 287 if (ret) { 288 dev_err(&pdev->dev, "failed to parse OF data\n"); 289 goto err_parse_dt; 290 } 291 292 plat_dat->addr64 = dwmac->ops->addr_width; 293 plat_dat->init = imx_dwmac_init; 294 plat_dat->exit = imx_dwmac_exit; 295 plat_dat->clks_config = imx_dwmac_clks_config; 296 plat_dat->fix_mac_speed = imx_dwmac_fix_speed; 297 plat_dat->bsp_priv = dwmac; 298 dwmac->plat_dat = plat_dat; 299 300 ret = imx_dwmac_clks_config(dwmac, true); 301 if (ret) 302 goto err_clks_config; 303 304 ret = imx_dwmac_init(pdev, dwmac); 305 if (ret) 306 goto err_dwmac_init; 307 308 ret = stmmac_dvr_probe(&pdev->dev, plat_dat, &stmmac_res); 309 if (ret) 310 goto err_drv_probe; 311 312 return 0; 313 314 err_drv_probe: 315 imx_dwmac_exit(pdev, plat_dat->bsp_priv); 316 err_dwmac_init: 317 imx_dwmac_clks_config(dwmac, false); 318 err_clks_config: 319 err_parse_dt: 320 err_match_data: 321 stmmac_remove_config_dt(pdev, plat_dat); 322 return ret; 323 } 324 325 static struct imx_dwmac_ops imx8mp_dwmac_data = { 326 .addr_width = 34, 327 .mac_rgmii_txclk_auto_adj = false, 328 .set_intf_mode = imx8mp_set_intf_mode, 329 }; 330 331 static struct imx_dwmac_ops imx8dxl_dwmac_data = { 332 .addr_width = 32, 333 .mac_rgmii_txclk_auto_adj = true, 334 .set_intf_mode = imx8dxl_set_intf_mode, 335 }; 336 337 static struct imx_dwmac_ops imx93_dwmac_data = { 338 .addr_width = 32, 339 .mac_rgmii_txclk_auto_adj = true, 340 .set_intf_mode = imx93_set_intf_mode, 341 }; 342 343 static const struct of_device_id imx_dwmac_match[] = { 344 { .compatible = "nxp,imx8mp-dwmac-eqos", .data = &imx8mp_dwmac_data }, 345 { .compatible = "nxp,imx8dxl-dwmac-eqos", .data = &imx8dxl_dwmac_data }, 346 { .compatible = "nxp,imx93-dwmac-eqos", .data = &imx93_dwmac_data }, 347 { } 348 }; 349 MODULE_DEVICE_TABLE(of, imx_dwmac_match); 350 351 static struct platform_driver imx_dwmac_driver = { 352 .probe = imx_dwmac_probe, 353 .remove = stmmac_pltfr_remove, 354 .driver = { 355 .name = "imx-dwmac", 356 .pm = &stmmac_pltfr_pm_ops, 357 .of_match_table = imx_dwmac_match, 358 }, 359 }; 360 module_platform_driver(imx_dwmac_driver); 361 362 MODULE_AUTHOR("NXP"); 363 MODULE_DESCRIPTION("NXP imx8 DWMAC Specific Glue layer"); 364 MODULE_LICENSE("GPL v2"); 365