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