Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

net: ethernet: dwmac: add ethernet glue logic for NXP imx8 chip

NXP imx8 family like imx8mp/imx8dxl chips support Synopsys MAC 5.10a IP.
This patch adds settings for NXP imx8 glue layer:
- clocks
- dwmac address width
- phy interface mode selection
- adjust rgmii txclk rate

v2:
- adjust code sequences in order to have reverse christmas
tree local variable ordering.

Signed-off-by: Fugang Duan <fugang.duan@nxp.com>
Signed-off-by: David S. Miller <davem@davemloft.net>

authored by

Fugang Duan and committed by
David S. Miller
94abdad6 139df98b

+329
+13
drivers/net/ethernet/stmicro/stmmac/Kconfig
··· 196 196 This selects Allwinner SoC glue layer support for the 197 197 stmmac device driver. This driver is used for H3/A83T/A64 198 198 EMAC ethernet controller. 199 + 200 + config DWMAC_IMX8 201 + tristate "NXP IMX8 DWMAC support" 202 + default ARCH_MXC 203 + depends on OF && (ARCH_MXC || COMPILE_TEST) 204 + select MFD_SYSCON 205 + ---help--- 206 + Support for ethernet controller on NXP i.MX8 SOCs. 207 + 208 + This selects NXP SoC glue layer support for the stmmac 209 + device driver. This driver is used for i.MX8 series like 210 + iMX8MP/iMX8DXL GMAC ethernet controller. 211 + 199 212 endif 200 213 201 214 config DWMAC_INTEL
+1
drivers/net/ethernet/stmicro/stmmac/Makefile
··· 27 27 obj-$(CONFIG_DWMAC_SUN8I) += dwmac-sun8i.o 28 28 obj-$(CONFIG_DWMAC_DWC_QOS_ETH) += dwmac-dwc-qos-eth.o 29 29 obj-$(CONFIG_DWMAC_GENERIC) += dwmac-generic.o 30 + obj-$(CONFIG_DWMAC_IMX8) += dwmac-imx.o 30 31 stmmac-platform-objs:= stmmac_platform.o 31 32 dwmac-altr-socfpga-objs := altr_tse_pcs.o dwmac-socfpga.o 32 33
+315
drivers/net/ethernet/stmicro/stmmac/dwmac-imx.c
··· 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_init(struct platform_device *pdev, void *priv) 94 + { 95 + struct plat_stmmacenet_data *plat_dat; 96 + struct imx_priv_data *dwmac = priv; 97 + int ret; 98 + 99 + plat_dat = dwmac->plat_dat; 100 + 101 + ret = clk_prepare_enable(dwmac->clk_mem); 102 + if (ret) { 103 + dev_err(&pdev->dev, "mem clock enable failed\n"); 104 + return ret; 105 + } 106 + 107 + ret = clk_prepare_enable(dwmac->clk_tx); 108 + if (ret) { 109 + dev_err(&pdev->dev, "tx clock enable failed\n"); 110 + goto clk_tx_en_failed; 111 + } 112 + 113 + if (dwmac->ops->set_intf_mode) { 114 + ret = dwmac->ops->set_intf_mode(plat_dat); 115 + if (ret) 116 + goto intf_mode_failed; 117 + } 118 + 119 + return 0; 120 + 121 + intf_mode_failed: 122 + clk_disable_unprepare(dwmac->clk_tx); 123 + clk_tx_en_failed: 124 + clk_disable_unprepare(dwmac->clk_mem); 125 + return ret; 126 + } 127 + 128 + static void imx_dwmac_exit(struct platform_device *pdev, void *priv) 129 + { 130 + struct imx_priv_data *dwmac = priv; 131 + 132 + if (dwmac->clk_tx) 133 + clk_disable_unprepare(dwmac->clk_tx); 134 + clk_disable_unprepare(dwmac->clk_mem); 135 + } 136 + 137 + static void imx_dwmac_fix_speed(void *priv, unsigned int speed) 138 + { 139 + struct plat_stmmacenet_data *plat_dat; 140 + struct imx_priv_data *dwmac = priv; 141 + unsigned long rate; 142 + int err; 143 + 144 + plat_dat = dwmac->plat_dat; 145 + 146 + if (dwmac->ops->mac_rgmii_txclk_auto_adj || 147 + (plat_dat->interface == PHY_INTERFACE_MODE_RMII) || 148 + (plat_dat->interface == PHY_INTERFACE_MODE_MII)) 149 + return; 150 + 151 + switch (speed) { 152 + case SPEED_1000: 153 + rate = 125000000; 154 + break; 155 + case SPEED_100: 156 + rate = 25000000; 157 + break; 158 + case SPEED_10: 159 + rate = 2500000; 160 + break; 161 + default: 162 + dev_err(dwmac->dev, "invalid speed %u\n", speed); 163 + return; 164 + } 165 + 166 + err = clk_set_rate(dwmac->clk_tx, rate); 167 + if (err < 0) 168 + dev_err(dwmac->dev, "failed to set tx rate %lu\n", rate); 169 + } 170 + 171 + static int 172 + imx_dwmac_parse_dt(struct imx_priv_data *dwmac, struct device *dev) 173 + { 174 + struct device_node *np = dev->of_node; 175 + int err = 0; 176 + 177 + if (of_get_property(np, "snps,rmii_refclk_ext", NULL)) 178 + dwmac->rmii_refclk_ext = true; 179 + 180 + dwmac->clk_tx = devm_clk_get(dev, "tx"); 181 + if (IS_ERR(dwmac->clk_tx)) { 182 + dev_err(dev, "failed to get tx clock\n"); 183 + return PTR_ERR(dwmac->clk_tx); 184 + } 185 + 186 + dwmac->clk_mem = NULL; 187 + if (of_machine_is_compatible("fsl,imx8dxl")) { 188 + dwmac->clk_mem = devm_clk_get(dev, "mem"); 189 + if (IS_ERR(dwmac->clk_mem)) { 190 + dev_err(dev, "failed to get mem clock\n"); 191 + return PTR_ERR(dwmac->clk_mem); 192 + } 193 + } 194 + 195 + if (of_machine_is_compatible("fsl,imx8mp")) { 196 + /* Binding doc describes the propety: 197 + is required by i.MX8MP. 198 + is optinoal for i.MX8DXL. 199 + */ 200 + dwmac->intf_regmap = syscon_regmap_lookup_by_phandle(np, "intf_mode"); 201 + if (IS_ERR(dwmac->intf_regmap)) 202 + return PTR_ERR(dwmac->intf_regmap); 203 + 204 + err = of_property_read_u32_index(np, "intf_mode", 1, &dwmac->intf_reg_off); 205 + if (err) { 206 + dev_err(dev, "Can't get intf mode reg offset (%d)\n", err); 207 + return err; 208 + } 209 + } 210 + 211 + return err; 212 + } 213 + 214 + static int imx_dwmac_probe(struct platform_device *pdev) 215 + { 216 + struct plat_stmmacenet_data *plat_dat; 217 + struct stmmac_resources stmmac_res; 218 + struct imx_priv_data *dwmac; 219 + const struct imx_dwmac_ops *data; 220 + int ret; 221 + 222 + ret = stmmac_get_platform_resources(pdev, &stmmac_res); 223 + if (ret) 224 + return ret; 225 + 226 + dwmac = devm_kzalloc(&pdev->dev, sizeof(*dwmac), GFP_KERNEL); 227 + if (!dwmac) 228 + return PTR_ERR(dwmac); 229 + 230 + plat_dat = stmmac_probe_config_dt(pdev, &stmmac_res.mac); 231 + if (IS_ERR(plat_dat)) 232 + return PTR_ERR(plat_dat); 233 + 234 + data = of_device_get_match_data(&pdev->dev); 235 + if (!data) { 236 + dev_err(&pdev->dev, "failed to get match data\n"); 237 + ret = -EINVAL; 238 + goto err_match_data; 239 + } 240 + 241 + dwmac->ops = data; 242 + dwmac->dev = &pdev->dev; 243 + 244 + ret = imx_dwmac_parse_dt(dwmac, &pdev->dev); 245 + if (ret) { 246 + dev_err(&pdev->dev, "failed to parse OF data\n"); 247 + goto err_parse_dt; 248 + } 249 + 250 + ret = dma_set_mask_and_coherent(&pdev->dev, 251 + DMA_BIT_MASK(dwmac->ops->addr_width)); 252 + if (ret) { 253 + dev_err(&pdev->dev, "DMA mask set failed\n"); 254 + goto err_dma_mask; 255 + } 256 + 257 + plat_dat->init = imx_dwmac_init; 258 + plat_dat->exit = imx_dwmac_exit; 259 + plat_dat->fix_mac_speed = imx_dwmac_fix_speed; 260 + plat_dat->bsp_priv = dwmac; 261 + dwmac->plat_dat = plat_dat; 262 + 263 + ret = imx_dwmac_init(pdev, dwmac); 264 + if (ret) 265 + goto err_dwmac_init; 266 + 267 + ret = stmmac_dvr_probe(&pdev->dev, plat_dat, &stmmac_res); 268 + if (ret) 269 + goto err_drv_probe; 270 + 271 + return 0; 272 + 273 + err_dwmac_init: 274 + err_drv_probe: 275 + imx_dwmac_exit(pdev, plat_dat->bsp_priv); 276 + err_dma_mask: 277 + err_parse_dt: 278 + err_match_data: 279 + stmmac_remove_config_dt(pdev, plat_dat); 280 + return ret; 281 + } 282 + 283 + static struct imx_dwmac_ops imx8mp_dwmac_data = { 284 + .addr_width = 34, 285 + .mac_rgmii_txclk_auto_adj = false, 286 + .set_intf_mode = imx8mp_set_intf_mode, 287 + }; 288 + 289 + static struct imx_dwmac_ops imx8dxl_dwmac_data = { 290 + .addr_width = 32, 291 + .mac_rgmii_txclk_auto_adj = true, 292 + .set_intf_mode = imx8dxl_set_intf_mode, 293 + }; 294 + 295 + static const struct of_device_id imx_dwmac_match[] = { 296 + { .compatible = "nxp,imx8mp-dwmac-eqos", .data = &imx8mp_dwmac_data }, 297 + { .compatible = "nxp,imx8dxl-dwmac-eqos", .data = &imx8dxl_dwmac_data }, 298 + { } 299 + }; 300 + MODULE_DEVICE_TABLE(of, imx_dwmac_match); 301 + 302 + static struct platform_driver imx_dwmac_driver = { 303 + .probe = imx_dwmac_probe, 304 + .remove = stmmac_pltfr_remove, 305 + .driver = { 306 + .name = "imx-dwmac", 307 + .pm = &stmmac_pltfr_pm_ops, 308 + .of_match_table = imx_dwmac_match, 309 + }, 310 + }; 311 + module_platform_driver(imx_dwmac_driver); 312 + 313 + MODULE_AUTHOR("NXP"); 314 + MODULE_DESCRIPTION("NXP imx8 DWMAC Specific Glue layer"); 315 + MODULE_LICENSE("GPL v2");