linux/drivers/net/ethernet/stmicro/stmmac/dwmac-sunxi.c
<<
>>
Prefs
   1/**
   2 * dwmac-sunxi.c - Allwinner sunxi DWMAC specific glue layer
   3 *
   4 * Copyright (C) 2013 Chen-Yu Tsai
   5 *
   6 * Chen-Yu Tsai  <wens@csie.org>
   7 *
   8 * This program is free software; you can redistribute it and/or modify
   9 * it under the terms of the GNU General Public License as published by
  10 * the Free Software Foundation; either version 2 of the License, or
  11 * (at your option) any later version.
  12 *
  13 * This program is distributed in the hope that it will be useful,
  14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
  15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  16 * GNU General Public License for more details.
  17 */
  18
  19#include <linux/stmmac.h>
  20#include <linux/clk.h>
  21#include <linux/phy.h>
  22#include <linux/of_net.h>
  23#include <linux/regulator/consumer.h>
  24
  25struct sunxi_priv_data {
  26        int interface;
  27        int clk_enabled;
  28        struct clk *tx_clk;
  29        struct regulator *regulator;
  30};
  31
  32static void *sun7i_gmac_setup(struct platform_device *pdev)
  33{
  34        struct sunxi_priv_data *gmac;
  35        struct device *dev = &pdev->dev;
  36
  37        gmac = devm_kzalloc(dev, sizeof(*gmac), GFP_KERNEL);
  38        if (!gmac)
  39                return ERR_PTR(-ENOMEM);
  40
  41        gmac->interface = of_get_phy_mode(dev->of_node);
  42
  43        gmac->tx_clk = devm_clk_get(dev, "allwinner_gmac_tx");
  44        if (IS_ERR(gmac->tx_clk)) {
  45                dev_err(dev, "could not get tx clock\n");
  46                return gmac->tx_clk;
  47        }
  48
  49        /* Optional regulator for PHY */
  50        gmac->regulator = devm_regulator_get_optional(dev, "phy");
  51        if (IS_ERR(gmac->regulator)) {
  52                if (PTR_ERR(gmac->regulator) == -EPROBE_DEFER)
  53                        return ERR_PTR(-EPROBE_DEFER);
  54                dev_info(dev, "no regulator found\n");
  55                gmac->regulator = NULL;
  56        }
  57
  58        return gmac;
  59}
  60
  61#define SUN7I_GMAC_GMII_RGMII_RATE      125000000
  62#define SUN7I_GMAC_MII_RATE             25000000
  63
  64static int sun7i_gmac_init(struct platform_device *pdev, void *priv)
  65{
  66        struct sunxi_priv_data *gmac = priv;
  67        int ret;
  68
  69        if (gmac->regulator) {
  70                ret = regulator_enable(gmac->regulator);
  71                if (ret)
  72                        return ret;
  73        }
  74
  75        /* Set GMAC interface port mode
  76         *
  77         * The GMAC TX clock lines are configured by setting the clock
  78         * rate, which then uses the auto-reparenting feature of the
  79         * clock driver, and enabling/disabling the clock.
  80         */
  81        if (gmac->interface == PHY_INTERFACE_MODE_RGMII) {
  82                clk_set_rate(gmac->tx_clk, SUN7I_GMAC_GMII_RGMII_RATE);
  83                clk_prepare_enable(gmac->tx_clk);
  84                gmac->clk_enabled = 1;
  85        } else {
  86                clk_set_rate(gmac->tx_clk, SUN7I_GMAC_MII_RATE);
  87                clk_prepare(gmac->tx_clk);
  88        }
  89
  90        return 0;
  91}
  92
  93static void sun7i_gmac_exit(struct platform_device *pdev, void *priv)
  94{
  95        struct sunxi_priv_data *gmac = priv;
  96
  97        if (gmac->clk_enabled) {
  98                clk_disable(gmac->tx_clk);
  99                gmac->clk_enabled = 0;
 100        }
 101        clk_unprepare(gmac->tx_clk);
 102
 103        if (gmac->regulator)
 104                regulator_disable(gmac->regulator);
 105}
 106
 107static void sun7i_fix_speed(void *priv, unsigned int speed)
 108{
 109        struct sunxi_priv_data *gmac = priv;
 110
 111        /* only GMII mode requires us to reconfigure the clock lines */
 112        if (gmac->interface != PHY_INTERFACE_MODE_GMII)
 113                return;
 114
 115        if (gmac->clk_enabled) {
 116                clk_disable(gmac->tx_clk);
 117                gmac->clk_enabled = 0;
 118        }
 119        clk_unprepare(gmac->tx_clk);
 120
 121        if (speed == 1000) {
 122                clk_set_rate(gmac->tx_clk, SUN7I_GMAC_GMII_RGMII_RATE);
 123                clk_prepare_enable(gmac->tx_clk);
 124                gmac->clk_enabled = 1;
 125        } else {
 126                clk_set_rate(gmac->tx_clk, SUN7I_GMAC_MII_RATE);
 127                clk_prepare(gmac->tx_clk);
 128        }
 129}
 130
 131/* of_data specifying hardware features and callbacks.
 132 * hardware features were copied from Allwinner drivers. */
 133const struct stmmac_of_data sun7i_gmac_data = {
 134        .has_gmac = 1,
 135        .tx_coe = 1,
 136        .fix_mac_speed = sun7i_fix_speed,
 137        .setup = sun7i_gmac_setup,
 138        .init = sun7i_gmac_init,
 139        .exit = sun7i_gmac_exit,
 140};
 141