linux/drivers/net/ethernet/stmicro/stmmac/dwmac-sunxi.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0-or-later
   2/*
   3 * dwmac-sunxi.c - Allwinner sunxi DWMAC specific glue layer
   4 *
   5 * Copyright (C) 2013 Chen-Yu Tsai
   6 *
   7 * Chen-Yu Tsai  <wens@csie.org>
   8 */
   9
  10#include <linux/stmmac.h>
  11#include <linux/clk.h>
  12#include <linux/module.h>
  13#include <linux/phy.h>
  14#include <linux/platform_device.h>
  15#include <linux/of_net.h>
  16#include <linux/regulator/consumer.h>
  17
  18#include "stmmac_platform.h"
  19
  20struct sunxi_priv_data {
  21        phy_interface_t interface;
  22        int clk_enabled;
  23        struct clk *tx_clk;
  24        struct regulator *regulator;
  25};
  26
  27#define SUN7I_GMAC_GMII_RGMII_RATE      125000000
  28#define SUN7I_GMAC_MII_RATE             25000000
  29
  30static int sun7i_gmac_init(struct platform_device *pdev, void *priv)
  31{
  32        struct sunxi_priv_data *gmac = priv;
  33        int ret;
  34
  35        if (gmac->regulator) {
  36                ret = regulator_enable(gmac->regulator);
  37                if (ret)
  38                        return ret;
  39        }
  40
  41        /* Set GMAC interface port mode
  42         *
  43         * The GMAC TX clock lines are configured by setting the clock
  44         * rate, which then uses the auto-reparenting feature of the
  45         * clock driver, and enabling/disabling the clock.
  46         */
  47        if (phy_interface_mode_is_rgmii(gmac->interface)) {
  48                clk_set_rate(gmac->tx_clk, SUN7I_GMAC_GMII_RGMII_RATE);
  49                clk_prepare_enable(gmac->tx_clk);
  50                gmac->clk_enabled = 1;
  51        } else {
  52                clk_set_rate(gmac->tx_clk, SUN7I_GMAC_MII_RATE);
  53                ret = clk_prepare(gmac->tx_clk);
  54                if (ret)
  55                        return ret;
  56        }
  57
  58        return 0;
  59}
  60
  61static void sun7i_gmac_exit(struct platform_device *pdev, void *priv)
  62{
  63        struct sunxi_priv_data *gmac = priv;
  64
  65        if (gmac->clk_enabled) {
  66                clk_disable(gmac->tx_clk);
  67                gmac->clk_enabled = 0;
  68        }
  69        clk_unprepare(gmac->tx_clk);
  70
  71        if (gmac->regulator)
  72                regulator_disable(gmac->regulator);
  73}
  74
  75static void sun7i_fix_speed(void *priv, unsigned int speed)
  76{
  77        struct sunxi_priv_data *gmac = priv;
  78
  79        /* only GMII mode requires us to reconfigure the clock lines */
  80        if (gmac->interface != PHY_INTERFACE_MODE_GMII)
  81                return;
  82
  83        if (gmac->clk_enabled) {
  84                clk_disable(gmac->tx_clk);
  85                gmac->clk_enabled = 0;
  86        }
  87        clk_unprepare(gmac->tx_clk);
  88
  89        if (speed == 1000) {
  90                clk_set_rate(gmac->tx_clk, SUN7I_GMAC_GMII_RGMII_RATE);
  91                clk_prepare_enable(gmac->tx_clk);
  92                gmac->clk_enabled = 1;
  93        } else {
  94                clk_set_rate(gmac->tx_clk, SUN7I_GMAC_MII_RATE);
  95                clk_prepare(gmac->tx_clk);
  96        }
  97}
  98
  99static int sun7i_gmac_probe(struct platform_device *pdev)
 100{
 101        struct plat_stmmacenet_data *plat_dat;
 102        struct stmmac_resources stmmac_res;
 103        struct sunxi_priv_data *gmac;
 104        struct device *dev = &pdev->dev;
 105        int ret;
 106
 107        ret = stmmac_get_platform_resources(pdev, &stmmac_res);
 108        if (ret)
 109                return ret;
 110
 111        plat_dat = stmmac_probe_config_dt(pdev, &stmmac_res.mac);
 112        if (IS_ERR(plat_dat))
 113                return PTR_ERR(plat_dat);
 114
 115        gmac = devm_kzalloc(dev, sizeof(*gmac), GFP_KERNEL);
 116        if (!gmac) {
 117                ret = -ENOMEM;
 118                goto err_remove_config_dt;
 119        }
 120
 121        ret = of_get_phy_mode(dev->of_node, &gmac->interface);
 122        if (ret && ret != -ENODEV) {
 123                dev_err(dev, "Can't get phy-mode\n");
 124                goto err_remove_config_dt;
 125        }
 126
 127        gmac->tx_clk = devm_clk_get(dev, "allwinner_gmac_tx");
 128        if (IS_ERR(gmac->tx_clk)) {
 129                dev_err(dev, "could not get tx clock\n");
 130                ret = PTR_ERR(gmac->tx_clk);
 131                goto err_remove_config_dt;
 132        }
 133
 134        /* Optional regulator for PHY */
 135        gmac->regulator = devm_regulator_get_optional(dev, "phy");
 136        if (IS_ERR(gmac->regulator)) {
 137                if (PTR_ERR(gmac->regulator) == -EPROBE_DEFER) {
 138                        ret = -EPROBE_DEFER;
 139                        goto err_remove_config_dt;
 140                }
 141                dev_info(dev, "no regulator found\n");
 142                gmac->regulator = NULL;
 143        }
 144
 145        /* platform data specifying hardware features and callbacks.
 146         * hardware features were copied from Allwinner drivers. */
 147        plat_dat->tx_coe = 1;
 148        plat_dat->has_gmac = true;
 149        plat_dat->bsp_priv = gmac;
 150        plat_dat->init = sun7i_gmac_init;
 151        plat_dat->exit = sun7i_gmac_exit;
 152        plat_dat->fix_mac_speed = sun7i_fix_speed;
 153        plat_dat->tx_fifo_size = 4096;
 154        plat_dat->rx_fifo_size = 16384;
 155
 156        ret = sun7i_gmac_init(pdev, plat_dat->bsp_priv);
 157        if (ret)
 158                goto err_remove_config_dt;
 159
 160        ret = stmmac_dvr_probe(&pdev->dev, plat_dat, &stmmac_res);
 161        if (ret)
 162                goto err_gmac_exit;
 163
 164        return 0;
 165
 166err_gmac_exit:
 167        sun7i_gmac_exit(pdev, plat_dat->bsp_priv);
 168err_remove_config_dt:
 169        stmmac_remove_config_dt(pdev, plat_dat);
 170
 171        return ret;
 172}
 173
 174static const struct of_device_id sun7i_dwmac_match[] = {
 175        { .compatible = "allwinner,sun7i-a20-gmac" },
 176        { }
 177};
 178MODULE_DEVICE_TABLE(of, sun7i_dwmac_match);
 179
 180static struct platform_driver sun7i_dwmac_driver = {
 181        .probe  = sun7i_gmac_probe,
 182        .remove = stmmac_pltfr_remove,
 183        .driver = {
 184                .name           = "sun7i-dwmac",
 185                .pm             = &stmmac_pltfr_pm_ops,
 186                .of_match_table = sun7i_dwmac_match,
 187        },
 188};
 189module_platform_driver(sun7i_dwmac_driver);
 190
 191MODULE_AUTHOR("Chen-Yu Tsai <wens@csie.org>");
 192MODULE_DESCRIPTION("Allwinner sunxi DWMAC specific glue layer");
 193MODULE_LICENSE("GPL");
 194