diff options
Diffstat (limited to 'drivers/net/ethernet/stmicro/stmmac/dwmac-imx.c')
| -rw-r--r-- | drivers/net/ethernet/stmicro/stmmac/dwmac-imx.c | 279 |
1 files changed, 178 insertions, 101 deletions
diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac-imx.c b/drivers/net/ethernet/stmicro/stmmac/dwmac-imx.c index bd52fb7cf486..db288fbd5a4d 100644 --- a/drivers/net/ethernet/stmicro/stmmac/dwmac-imx.c +++ b/drivers/net/ethernet/stmicro/stmmac/dwmac-imx.c @@ -12,7 +12,6 @@ #include <linux/mfd/syscon.h> #include <linux/module.h> #include <linux/of.h> -#include <linux/of_device.h> #include <linux/of_net.h> #include <linux/phy.h> #include <linux/platform_device.h> @@ -24,18 +23,32 @@ #include "stmmac_platform.h" #define GPR_ENET_QOS_INTF_MODE_MASK GENMASK(21, 16) -#define GPR_ENET_QOS_INTF_SEL_MII (0x0 << 16) -#define GPR_ENET_QOS_INTF_SEL_RMII (0x4 << 16) -#define GPR_ENET_QOS_INTF_SEL_RGMII (0x1 << 16) +#define GPR_ENET_QOS_INTF_SEL_MASK GENMASK(20, 16) #define GPR_ENET_QOS_CLK_GEN_EN (0x1 << 19) #define GPR_ENET_QOS_CLK_TX_CLK_SEL (0x1 << 20) #define GPR_ENET_QOS_RGMII_EN (0x1 << 21) +#define MX93_GPR_ENET_QOS_INTF_MODE_MASK GENMASK(3, 0) +#define MX93_GPR_ENET_QOS_INTF_SEL_MASK GENMASK(3, 1) +#define MX93_GPR_ENET_QOS_CLK_GEN_EN (0x1 << 0) +#define MX93_GPR_ENET_QOS_CLK_SEL_MASK BIT_MASK(0) +#define MX93_GPR_CLK_SEL_OFFSET (4) + +#define DMA_BUS_MODE 0x00001000 +#define DMA_BUS_MODE_SFT_RESET (0x1 << 0) +#define RMII_RESET_SPEED (0x3 << 14) +#define CTRL_SPEED_MASK GENMASK(15, 14) + +struct imx_priv_data; + struct imx_dwmac_ops { u32 addr_width; + u32 flags; bool mac_rgmii_txclk_auto_adj; - int (*set_intf_mode)(struct plat_stmmacenet_data *plat_dat); + int (*fix_soc_reset)(struct stmmac_priv *priv, void __iomem *ioaddr); + int (*set_intf_mode)(struct imx_priv_data *dwmac, u8 phy_intf_sel); + void (*fix_mac_speed)(void *priv, int speed, unsigned int mode); }; struct imx_priv_data { @@ -45,51 +58,56 @@ struct imx_priv_data { struct regmap *intf_regmap; u32 intf_reg_off; bool rmii_refclk_ext; + void __iomem *base_addr; const struct imx_dwmac_ops *ops; struct plat_stmmacenet_data *plat_dat; }; -static int imx8mp_set_intf_mode(struct plat_stmmacenet_data *plat_dat) +static int imx8mp_set_intf_mode(struct imx_priv_data *dwmac, u8 phy_intf_sel) { - struct imx_priv_data *dwmac = plat_dat->bsp_priv; - int val; - - switch (plat_dat->interface) { - case PHY_INTERFACE_MODE_MII: - val = GPR_ENET_QOS_INTF_SEL_MII; - break; - case PHY_INTERFACE_MODE_RMII: - val = GPR_ENET_QOS_INTF_SEL_RMII; - val |= (dwmac->rmii_refclk_ext ? 0 : GPR_ENET_QOS_CLK_TX_CLK_SEL); - break; - case PHY_INTERFACE_MODE_RGMII: - case PHY_INTERFACE_MODE_RGMII_ID: - case PHY_INTERFACE_MODE_RGMII_RXID: - case PHY_INTERFACE_MODE_RGMII_TXID: - val = GPR_ENET_QOS_INTF_SEL_RGMII | - GPR_ENET_QOS_RGMII_EN; - break; - default: - pr_debug("imx dwmac doesn't support %d interface\n", - plat_dat->interface); - return -EINVAL; - } + unsigned int val; + + val = FIELD_PREP(GPR_ENET_QOS_INTF_SEL_MASK, phy_intf_sel) | + GPR_ENET_QOS_CLK_GEN_EN; + + if (phy_intf_sel == PHY_INTF_SEL_RMII && !dwmac->rmii_refclk_ext) + val |= GPR_ENET_QOS_CLK_TX_CLK_SEL; + else if (phy_intf_sel == PHY_INTF_SEL_RGMII) + val |= GPR_ENET_QOS_RGMII_EN; - val |= GPR_ENET_QOS_CLK_GEN_EN; return regmap_update_bits(dwmac->intf_regmap, dwmac->intf_reg_off, GPR_ENET_QOS_INTF_MODE_MASK, val); }; static int -imx8dxl_set_intf_mode(struct plat_stmmacenet_data *plat_dat) +imx8dxl_set_intf_mode(struct imx_priv_data *dwmac, u8 phy_intf_sel) { - int ret = 0; - /* TBD: depends on imx8dxl scu interfaces to be upstreamed */ - return ret; + return 0; } +static int imx93_set_intf_mode(struct imx_priv_data *dwmac, u8 phy_intf_sel) +{ + unsigned int val; + int ret; + + if (phy_intf_sel == PHY_INTF_SEL_RMII && dwmac->rmii_refclk_ext) { + ret = regmap_clear_bits(dwmac->intf_regmap, + dwmac->intf_reg_off + + MX93_GPR_CLK_SEL_OFFSET, + MX93_GPR_ENET_QOS_CLK_SEL_MASK); + if (ret) + return ret; + } + + val = FIELD_PREP(MX93_GPR_ENET_QOS_INTF_SEL_MASK, phy_intf_sel) | + MX93_GPR_ENET_QOS_CLK_GEN_EN; + + return regmap_update_bits(dwmac->intf_regmap, dwmac->intf_reg_off, + MX93_GPR_ENET_QOS_INTF_MODE_MASK, val); +}; + static int imx_dwmac_clks_config(void *priv, bool enabled) { struct imx_priv_data *dwmac = priv; @@ -116,54 +134,48 @@ static int imx_dwmac_clks_config(void *priv, bool enabled) return ret; } -static int imx_dwmac_init(struct platform_device *pdev, void *priv) +static int imx_set_phy_intf_sel(void *bsp_priv, u8 phy_intf_sel) { - struct plat_stmmacenet_data *plat_dat; - struct imx_priv_data *dwmac = priv; - int ret; + struct imx_priv_data *dwmac = bsp_priv; - plat_dat = dwmac->plat_dat; + if (!dwmac->ops->set_intf_mode) + return 0; - if (dwmac->ops->set_intf_mode) { - ret = dwmac->ops->set_intf_mode(plat_dat); - if (ret) - return ret; - } + if (phy_intf_sel != PHY_INTF_SEL_GMII_MII && + phy_intf_sel != PHY_INTF_SEL_RGMII && + phy_intf_sel != PHY_INTF_SEL_RMII) + return -EINVAL; - return 0; + return dwmac->ops->set_intf_mode(dwmac, phy_intf_sel); } -static void imx_dwmac_exit(struct platform_device *pdev, void *priv) +static int imx_dwmac_set_clk_tx_rate(void *bsp_priv, struct clk *clk_tx_i, + phy_interface_t interface, int speed) { - /* nothing to do now */ + if (interface == PHY_INTERFACE_MODE_RMII || + interface == PHY_INTERFACE_MODE_MII) + return 0; + + return stmmac_set_clk_tx_rate(bsp_priv, clk_tx_i, interface, speed); } -static void imx_dwmac_fix_speed(void *priv, unsigned int speed) +static void imx_dwmac_fix_speed(void *priv, int speed, unsigned int mode) { struct plat_stmmacenet_data *plat_dat; struct imx_priv_data *dwmac = priv; - unsigned long rate; + long rate; int err; plat_dat = dwmac->plat_dat; if (dwmac->ops->mac_rgmii_txclk_auto_adj || - (plat_dat->interface == PHY_INTERFACE_MODE_RMII) || - (plat_dat->interface == PHY_INTERFACE_MODE_MII)) + (plat_dat->phy_interface == PHY_INTERFACE_MODE_RMII) || + (plat_dat->phy_interface == PHY_INTERFACE_MODE_MII)) return; - switch (speed) { - case SPEED_1000: - rate = 125000000; - break; - case SPEED_100: - rate = 25000000; - break; - case SPEED_10: - rate = 2500000; - break; - default: - dev_err(dwmac->dev, "invalid speed %u\n", speed); + rate = rgmii_clock(speed); + if (rate < 0) { + dev_err(dwmac->dev, "invalid speed %d\n", speed); return; } @@ -172,14 +184,68 @@ static void imx_dwmac_fix_speed(void *priv, unsigned int speed) dev_err(dwmac->dev, "failed to set tx rate %lu\n", rate); } +static void imx93_dwmac_fix_speed(void *priv, int speed, unsigned int mode) +{ + struct imx_priv_data *dwmac = priv; + unsigned int iface; + int ctrl, old_ctrl; + + imx_dwmac_fix_speed(priv, speed, mode); + + if (!dwmac || mode != MLO_AN_FIXED) + return; + + if (regmap_read(dwmac->intf_regmap, dwmac->intf_reg_off, &iface)) + return; + + if (FIELD_GET(MX93_GPR_ENET_QOS_INTF_SEL_MASK, iface) != + PHY_INTF_SEL_RGMII) + return; + + old_ctrl = readl(dwmac->base_addr + MAC_CTRL_REG); + ctrl = old_ctrl & ~CTRL_SPEED_MASK; + regmap_update_bits(dwmac->intf_regmap, dwmac->intf_reg_off, + MX93_GPR_ENET_QOS_INTF_MODE_MASK, 0); + writel(ctrl, dwmac->base_addr + MAC_CTRL_REG); + + /* Ensure the settings for CTRL are applied. */ + readl(dwmac->base_addr + MAC_CTRL_REG); + + usleep_range(10, 20); + iface &= MX93_GPR_ENET_QOS_INTF_SEL_MASK; + iface |= MX93_GPR_ENET_QOS_CLK_GEN_EN; + regmap_update_bits(dwmac->intf_regmap, dwmac->intf_reg_off, + MX93_GPR_ENET_QOS_INTF_MODE_MASK, iface); + + writel(old_ctrl, dwmac->base_addr + MAC_CTRL_REG); +} + +static int imx_dwmac_mx93_reset(struct stmmac_priv *priv, void __iomem *ioaddr) +{ + struct plat_stmmacenet_data *plat_dat = priv->plat; + u32 value = readl(ioaddr + DMA_BUS_MODE); + + /* DMA SW reset */ + value |= DMA_BUS_MODE_SFT_RESET; + writel(value, ioaddr + DMA_BUS_MODE); + + if (plat_dat->phy_interface == PHY_INTERFACE_MODE_RMII) { + usleep_range(100, 200); + writel(RMII_RESET_SPEED, ioaddr + MAC_CTRL_REG); + } + + return readl_poll_timeout(ioaddr + DMA_BUS_MODE, value, + !(value & DMA_BUS_MODE_SFT_RESET), + 10000, 1000000); +} + static int imx_dwmac_parse_dt(struct imx_priv_data *dwmac, struct device *dev) { struct device_node *np = dev->of_node; int err = 0; - if (of_get_property(np, "snps,rmii_refclk_ext", NULL)) - dwmac->rmii_refclk_ext = true; + dwmac->rmii_refclk_ext = of_property_read_bool(np, "snps,rmii_refclk_ext"); dwmac->clk_tx = devm_clk_get(dev, "tx"); if (IS_ERR(dwmac->clk_tx)) { @@ -188,7 +254,10 @@ imx_dwmac_parse_dt(struct imx_priv_data *dwmac, struct device *dev) } dwmac->clk_mem = NULL; - if (of_machine_is_compatible("fsl,imx8dxl")) { + + if (of_machine_is_compatible("fsl,imx8dxl") || + of_machine_is_compatible("fsl,imx91") || + of_machine_is_compatible("fsl,imx93")) { dwmac->clk_mem = devm_clk_get(dev, "mem"); if (IS_ERR(dwmac->clk_mem)) { dev_err(dev, "failed to get mem clock\n"); @@ -196,20 +265,18 @@ imx_dwmac_parse_dt(struct imx_priv_data *dwmac, struct device *dev) } } - if (of_machine_is_compatible("fsl,imx8mp")) { - /* Binding doc describes the property: - is required by i.MX8MP. - is optional for i.MX8DXL. + if (of_machine_is_compatible("fsl,imx8mp") || + of_machine_is_compatible("fsl,imx91") || + of_machine_is_compatible("fsl,imx93")) { + /* Binding doc describes the propety: + * is required by i.MX8MP, i.MX91, i.MX93. + * is optinoal for i.MX8DXL. */ - dwmac->intf_regmap = syscon_regmap_lookup_by_phandle(np, "intf_mode"); + dwmac->intf_regmap = + syscon_regmap_lookup_by_phandle_args(np, "intf_mode", 1, + &dwmac->intf_reg_off); if (IS_ERR(dwmac->intf_regmap)) return PTR_ERR(dwmac->intf_regmap); - - err = of_property_read_u32_index(np, "intf_mode", 1, &dwmac->intf_reg_off); - if (err) { - dev_err(dev, "Can't get intf mode reg offset (%d)\n", err); - return err; - } } return err; @@ -231,15 +298,14 @@ static int imx_dwmac_probe(struct platform_device *pdev) if (!dwmac) return -ENOMEM; - plat_dat = stmmac_probe_config_dt(pdev, stmmac_res.mac); + plat_dat = devm_stmmac_probe_config_dt(pdev, stmmac_res.mac); if (IS_ERR(plat_dat)) return PTR_ERR(plat_dat); data = of_device_get_match_data(&pdev->dev); if (!data) { dev_err(&pdev->dev, "failed to get match data\n"); - ret = -EINVAL; - goto err_match_data; + return -EINVAL; } dwmac->ops = data; @@ -248,39 +314,40 @@ static int imx_dwmac_probe(struct platform_device *pdev) ret = imx_dwmac_parse_dt(dwmac, &pdev->dev); if (ret) { dev_err(&pdev->dev, "failed to parse OF data\n"); - goto err_parse_dt; + return ret; } - plat_dat->addr64 = dwmac->ops->addr_width; - plat_dat->init = imx_dwmac_init; - plat_dat->exit = imx_dwmac_exit; + if (data->flags & STMMAC_FLAG_HWTSTAMP_CORRECT_LATENCY) + plat_dat->flags |= STMMAC_FLAG_HWTSTAMP_CORRECT_LATENCY; + + /* Default TX Q0 to use TSO and rest TXQ for TBS */ + for (int i = 1; i < plat_dat->tx_queues_to_use; i++) + plat_dat->tx_queues_cfg[i].tbs_en = 1; + + plat_dat->host_dma_width = dwmac->ops->addr_width; + plat_dat->set_phy_intf_sel = imx_set_phy_intf_sel; plat_dat->clks_config = imx_dwmac_clks_config; - plat_dat->fix_mac_speed = imx_dwmac_fix_speed; plat_dat->bsp_priv = dwmac; dwmac->plat_dat = plat_dat; + dwmac->base_addr = stmmac_res.addr; ret = imx_dwmac_clks_config(dwmac, true); if (ret) - goto err_clks_config; + return ret; - ret = imx_dwmac_init(pdev, dwmac); - if (ret) - goto err_dwmac_init; + if (dwmac->ops->fix_mac_speed) { + plat_dat->fix_mac_speed = dwmac->ops->fix_mac_speed; + } else if (!dwmac->ops->mac_rgmii_txclk_auto_adj) { + plat_dat->clk_tx_i = dwmac->clk_tx; + plat_dat->set_clk_tx_rate = imx_dwmac_set_clk_tx_rate; + } - ret = stmmac_dvr_probe(&pdev->dev, plat_dat, &stmmac_res); - if (ret) - goto err_drv_probe; + dwmac->plat_dat->fix_soc_reset = dwmac->ops->fix_soc_reset; - return 0; + ret = stmmac_pltfr_probe(pdev, plat_dat, &stmmac_res); + if (ret) + imx_dwmac_clks_config(dwmac, false); -err_drv_probe: - imx_dwmac_exit(pdev, plat_dat->bsp_priv); -err_dwmac_init: - imx_dwmac_clks_config(dwmac, false); -err_clks_config: -err_parse_dt: -err_match_data: - stmmac_remove_config_dt(pdev, plat_dat); return ret; } @@ -288,6 +355,7 @@ static struct imx_dwmac_ops imx8mp_dwmac_data = { .addr_width = 34, .mac_rgmii_txclk_auto_adj = false, .set_intf_mode = imx8mp_set_intf_mode, + .flags = STMMAC_FLAG_HWTSTAMP_CORRECT_LATENCY, }; static struct imx_dwmac_ops imx8dxl_dwmac_data = { @@ -296,9 +364,18 @@ static struct imx_dwmac_ops imx8dxl_dwmac_data = { .set_intf_mode = imx8dxl_set_intf_mode, }; +static struct imx_dwmac_ops imx93_dwmac_data = { + .addr_width = 32, + .mac_rgmii_txclk_auto_adj = true, + .set_intf_mode = imx93_set_intf_mode, + .fix_soc_reset = imx_dwmac_mx93_reset, + .fix_mac_speed = imx93_dwmac_fix_speed, +}; + static const struct of_device_id imx_dwmac_match[] = { { .compatible = "nxp,imx8mp-dwmac-eqos", .data = &imx8mp_dwmac_data }, { .compatible = "nxp,imx8dxl-dwmac-eqos", .data = &imx8dxl_dwmac_data }, + { .compatible = "nxp,imx93-dwmac-eqos", .data = &imx93_dwmac_data }, { } }; MODULE_DEVICE_TABLE(of, imx_dwmac_match); |
