linux/drivers/net/ethernet/stmicro/stmmac/dwmac-imx.c
<<
>>
Prefs
   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
  34struct 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
  41struct 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
  53static 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
  84static int
  85imx8dxl_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
  93static 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
 119static 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
 136static void imx_dwmac_exit(struct platform_device *pdev, void *priv)
 137{
 138        /* nothing to do now */
 139}
 140
 141static 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
 175static int
 176imx_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
 218static 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
 276err_drv_probe:
 277        imx_dwmac_exit(pdev, plat_dat->bsp_priv);
 278err_dwmac_init:
 279        imx_dwmac_clks_config(dwmac, false);
 280err_clks_config:
 281err_parse_dt:
 282err_match_data:
 283        stmmac_remove_config_dt(pdev, plat_dat);
 284        return ret;
 285}
 286
 287static 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
 293static 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
 299static 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};
 304MODULE_DEVICE_TABLE(of, imx_dwmac_match);
 305
 306static 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};
 315module_platform_driver(imx_dwmac_driver);
 316
 317MODULE_AUTHOR("NXP");
 318MODULE_DESCRIPTION("NXP imx8 DWMAC Specific Glue layer");
 319MODULE_LICENSE("GPL v2");
 320