linux/drivers/net/ethernet/stmicro/stmmac/dwmac-meson8b.c
<<
>>
Prefs
   1/*
   2 * Amlogic Meson8b and GXBB DWMAC glue layer
   3 *
   4 * Copyright (C) 2016 Martin Blumenstingl <martin.blumenstingl@googlemail.com>
   5 *
   6 * This program is free software; you can redistribute it and/or modify
   7 * it under the terms of the GNU General Public License version 2 as
   8 * published by the Free Software Foundation.
   9 *
  10 * You should have received a copy of the GNU General Public License
  11 * along with this program. If not, see <http://www.gnu.org/licenses/>.
  12 */
  13
  14#include <linux/clk.h>
  15#include <linux/clk-provider.h>
  16#include <linux/device.h>
  17#include <linux/ethtool.h>
  18#include <linux/io.h>
  19#include <linux/ioport.h>
  20#include <linux/module.h>
  21#include <linux/of_net.h>
  22#include <linux/mfd/syscon.h>
  23#include <linux/platform_device.h>
  24#include <linux/stmmac.h>
  25
  26#include "stmmac_platform.h"
  27
  28#define PRG_ETH0                        0x0
  29
  30#define PRG_ETH0_RGMII_MODE             BIT(0)
  31
  32/* mux to choose between fclk_div2 (bit unset) and mpll2 (bit set) */
  33#define PRG_ETH0_CLK_M250_SEL_SHIFT     4
  34#define PRG_ETH0_CLK_M250_SEL_MASK      GENMASK(4, 4)
  35
  36#define PRG_ETH0_TXDLY_SHIFT            5
  37#define PRG_ETH0_TXDLY_MASK             GENMASK(6, 5)
  38
  39/* divider for the result of m250_sel */
  40#define PRG_ETH0_CLK_M250_DIV_SHIFT     7
  41#define PRG_ETH0_CLK_M250_DIV_WIDTH     3
  42
  43#define PRG_ETH0_RGMII_TX_CLK_EN        10
  44
  45#define PRG_ETH0_INVERTED_RMII_CLK      BIT(11)
  46#define PRG_ETH0_TX_AND_PHY_REF_CLK     BIT(12)
  47
  48#define MUX_CLK_NUM_PARENTS             2
  49
  50struct meson8b_dwmac {
  51        struct platform_device  *pdev;
  52
  53        void __iomem            *regs;
  54
  55        phy_interface_t         phy_mode;
  56
  57        struct clk_mux          m250_mux;
  58        struct clk              *m250_mux_clk;
  59        struct clk              *m250_mux_parent[MUX_CLK_NUM_PARENTS];
  60
  61        struct clk_divider      m250_div;
  62        struct clk              *m250_div_clk;
  63
  64        struct clk_fixed_factor fixed_div2;
  65        struct clk              *fixed_div2_clk;
  66
  67        struct clk_gate         rgmii_tx_en;
  68        struct clk              *rgmii_tx_en_clk;
  69
  70        u32                     tx_delay_ns;
  71};
  72
  73static void meson8b_dwmac_mask_bits(struct meson8b_dwmac *dwmac, u32 reg,
  74                                    u32 mask, u32 value)
  75{
  76        u32 data;
  77
  78        data = readl(dwmac->regs + reg);
  79        data &= ~mask;
  80        data |= (value & mask);
  81
  82        writel(data, dwmac->regs + reg);
  83}
  84
  85static int meson8b_init_rgmii_tx_clk(struct meson8b_dwmac *dwmac)
  86{
  87        struct clk_init_data init;
  88        int i, ret;
  89        struct device *dev = &dwmac->pdev->dev;
  90        char clk_name[32];
  91        const char *clk_div_parents[1];
  92        const char *mux_parent_names[MUX_CLK_NUM_PARENTS];
  93
  94        /* get the mux parents from DT */
  95        for (i = 0; i < MUX_CLK_NUM_PARENTS; i++) {
  96                char name[16];
  97
  98                snprintf(name, sizeof(name), "clkin%d", i);
  99                dwmac->m250_mux_parent[i] = devm_clk_get(dev, name);
 100                if (IS_ERR(dwmac->m250_mux_parent[i])) {
 101                        ret = PTR_ERR(dwmac->m250_mux_parent[i]);
 102                        if (ret != -EPROBE_DEFER)
 103                                dev_err(dev, "Missing clock %s\n", name);
 104                        return ret;
 105                }
 106
 107                mux_parent_names[i] =
 108                        __clk_get_name(dwmac->m250_mux_parent[i]);
 109        }
 110
 111        /* create the m250_mux */
 112        snprintf(clk_name, sizeof(clk_name), "%s#m250_sel", dev_name(dev));
 113        init.name = clk_name;
 114        init.ops = &clk_mux_ops;
 115        init.flags = CLK_SET_RATE_PARENT;
 116        init.parent_names = mux_parent_names;
 117        init.num_parents = MUX_CLK_NUM_PARENTS;
 118
 119        dwmac->m250_mux.reg = dwmac->regs + PRG_ETH0;
 120        dwmac->m250_mux.shift = PRG_ETH0_CLK_M250_SEL_SHIFT;
 121        dwmac->m250_mux.mask = PRG_ETH0_CLK_M250_SEL_MASK;
 122        dwmac->m250_mux.flags = 0;
 123        dwmac->m250_mux.table = NULL;
 124        dwmac->m250_mux.hw.init = &init;
 125
 126        dwmac->m250_mux_clk = devm_clk_register(dev, &dwmac->m250_mux.hw);
 127        if (WARN_ON(IS_ERR(dwmac->m250_mux_clk)))
 128                return PTR_ERR(dwmac->m250_mux_clk);
 129
 130        /* create the m250_div */
 131        snprintf(clk_name, sizeof(clk_name), "%s#m250_div", dev_name(dev));
 132        init.name = devm_kstrdup(dev, clk_name, GFP_KERNEL);
 133        init.ops = &clk_divider_ops;
 134        init.flags = CLK_SET_RATE_PARENT;
 135        clk_div_parents[0] = __clk_get_name(dwmac->m250_mux_clk);
 136        init.parent_names = clk_div_parents;
 137        init.num_parents = ARRAY_SIZE(clk_div_parents);
 138
 139        dwmac->m250_div.reg = dwmac->regs + PRG_ETH0;
 140        dwmac->m250_div.shift = PRG_ETH0_CLK_M250_DIV_SHIFT;
 141        dwmac->m250_div.width = PRG_ETH0_CLK_M250_DIV_WIDTH;
 142        dwmac->m250_div.hw.init = &init;
 143        dwmac->m250_div.flags = CLK_DIVIDER_ONE_BASED |
 144                                CLK_DIVIDER_ALLOW_ZERO |
 145                                CLK_DIVIDER_ROUND_CLOSEST;
 146
 147        dwmac->m250_div_clk = devm_clk_register(dev, &dwmac->m250_div.hw);
 148        if (WARN_ON(IS_ERR(dwmac->m250_div_clk)))
 149                return PTR_ERR(dwmac->m250_div_clk);
 150
 151        /* create the fixed_div2 */
 152        snprintf(clk_name, sizeof(clk_name), "%s#fixed_div2", dev_name(dev));
 153        init.name = devm_kstrdup(dev, clk_name, GFP_KERNEL);
 154        init.ops = &clk_fixed_factor_ops;
 155        init.flags = CLK_SET_RATE_PARENT;
 156        clk_div_parents[0] = __clk_get_name(dwmac->m250_div_clk);
 157        init.parent_names = clk_div_parents;
 158        init.num_parents = ARRAY_SIZE(clk_div_parents);
 159
 160        dwmac->fixed_div2.mult = 1;
 161        dwmac->fixed_div2.div = 2;
 162        dwmac->fixed_div2.hw.init = &init;
 163
 164        dwmac->fixed_div2_clk = devm_clk_register(dev, &dwmac->fixed_div2.hw);
 165        if (WARN_ON(IS_ERR(dwmac->fixed_div2_clk)))
 166                return PTR_ERR(dwmac->fixed_div2_clk);
 167
 168        /* create the rgmii_tx_en */
 169        init.name = devm_kasprintf(dev, GFP_KERNEL, "%s#rgmii_tx_en",
 170                                   dev_name(dev));
 171        init.ops = &clk_gate_ops;
 172        init.flags = CLK_SET_RATE_PARENT;
 173        clk_div_parents[0] = __clk_get_name(dwmac->fixed_div2_clk);
 174        init.parent_names = clk_div_parents;
 175        init.num_parents = ARRAY_SIZE(clk_div_parents);
 176
 177        dwmac->rgmii_tx_en.reg = dwmac->regs + PRG_ETH0;
 178        dwmac->rgmii_tx_en.bit_idx = PRG_ETH0_RGMII_TX_CLK_EN;
 179        dwmac->rgmii_tx_en.hw.init = &init;
 180
 181        dwmac->rgmii_tx_en_clk = devm_clk_register(dev,
 182                                                   &dwmac->rgmii_tx_en.hw);
 183        if (WARN_ON(IS_ERR(dwmac->rgmii_tx_en_clk)))
 184                return PTR_ERR(dwmac->rgmii_tx_en_clk);
 185
 186        return 0;
 187}
 188
 189static int meson8b_init_prg_eth(struct meson8b_dwmac *dwmac)
 190{
 191        int ret;
 192        u8 tx_dly_val = 0;
 193
 194        switch (dwmac->phy_mode) {
 195        case PHY_INTERFACE_MODE_RGMII:
 196        case PHY_INTERFACE_MODE_RGMII_RXID:
 197                /* TX clock delay in ns = "8ns / 4 * tx_dly_val" (where
 198                 * 8ns are exactly one cycle of the 125MHz RGMII TX clock):
 199                 * 0ns = 0x0, 2ns = 0x1, 4ns = 0x2, 6ns = 0x3
 200                 */
 201                tx_dly_val = dwmac->tx_delay_ns >> 1;
 202                /* fall through */
 203
 204        case PHY_INTERFACE_MODE_RGMII_ID:
 205        case PHY_INTERFACE_MODE_RGMII_TXID:
 206                /* enable RGMII mode */
 207                meson8b_dwmac_mask_bits(dwmac, PRG_ETH0, PRG_ETH0_RGMII_MODE,
 208                                        PRG_ETH0_RGMII_MODE);
 209
 210                /* only relevant for RMII mode -> disable in RGMII mode */
 211                meson8b_dwmac_mask_bits(dwmac, PRG_ETH0,
 212                                        PRG_ETH0_INVERTED_RMII_CLK, 0);
 213
 214                meson8b_dwmac_mask_bits(dwmac, PRG_ETH0, PRG_ETH0_TXDLY_MASK,
 215                                        tx_dly_val << PRG_ETH0_TXDLY_SHIFT);
 216
 217                /* Configure the 125MHz RGMII TX clock, the IP block changes
 218                 * the output automatically (= without us having to configure
 219                 * a register) based on the line-speed (125MHz for Gbit speeds,
 220                 * 25MHz for 100Mbit/s and 2.5MHz for 10Mbit/s).
 221                 */
 222                ret = clk_set_rate(dwmac->rgmii_tx_en_clk, 125 * 1000 * 1000);
 223                if (ret) {
 224                        dev_err(&dwmac->pdev->dev,
 225                                "failed to set RGMII TX clock\n");
 226                        return ret;
 227                }
 228
 229                ret = clk_prepare_enable(dwmac->rgmii_tx_en_clk);
 230                if (ret) {
 231                        dev_err(&dwmac->pdev->dev,
 232                                "failed to enable the RGMII TX clock\n");
 233                        return ret;
 234                }
 235                break;
 236
 237        case PHY_INTERFACE_MODE_RMII:
 238                /* disable RGMII mode -> enables RMII mode */
 239                meson8b_dwmac_mask_bits(dwmac, PRG_ETH0, PRG_ETH0_RGMII_MODE,
 240                                        0);
 241
 242                /* invert internal clk_rmii_i to generate 25/2.5 tx_rx_clk */
 243                meson8b_dwmac_mask_bits(dwmac, PRG_ETH0,
 244                                        PRG_ETH0_INVERTED_RMII_CLK,
 245                                        PRG_ETH0_INVERTED_RMII_CLK);
 246
 247                /* TX clock delay cannot be configured in RMII mode */
 248                meson8b_dwmac_mask_bits(dwmac, PRG_ETH0, PRG_ETH0_TXDLY_MASK,
 249                                        0);
 250
 251                break;
 252
 253        default:
 254                dev_err(&dwmac->pdev->dev, "unsupported phy-mode %s\n",
 255                        phy_modes(dwmac->phy_mode));
 256                return -EINVAL;
 257        }
 258
 259        /* enable TX_CLK and PHY_REF_CLK generator */
 260        meson8b_dwmac_mask_bits(dwmac, PRG_ETH0, PRG_ETH0_TX_AND_PHY_REF_CLK,
 261                                PRG_ETH0_TX_AND_PHY_REF_CLK);
 262
 263        return 0;
 264}
 265
 266static int meson8b_dwmac_probe(struct platform_device *pdev)
 267{
 268        struct plat_stmmacenet_data *plat_dat;
 269        struct stmmac_resources stmmac_res;
 270        struct resource *res;
 271        struct meson8b_dwmac *dwmac;
 272        int ret;
 273
 274        ret = stmmac_get_platform_resources(pdev, &stmmac_res);
 275        if (ret)
 276                return ret;
 277
 278        plat_dat = stmmac_probe_config_dt(pdev, &stmmac_res.mac);
 279        if (IS_ERR(plat_dat))
 280                return PTR_ERR(plat_dat);
 281
 282        dwmac = devm_kzalloc(&pdev->dev, sizeof(*dwmac), GFP_KERNEL);
 283        if (!dwmac) {
 284                ret = -ENOMEM;
 285                goto err_remove_config_dt;
 286        }
 287
 288        res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
 289        dwmac->regs = devm_ioremap_resource(&pdev->dev, res);
 290        if (IS_ERR(dwmac->regs)) {
 291                ret = PTR_ERR(dwmac->regs);
 292                goto err_remove_config_dt;
 293        }
 294
 295        dwmac->pdev = pdev;
 296        dwmac->phy_mode = of_get_phy_mode(pdev->dev.of_node);
 297        if (dwmac->phy_mode < 0) {
 298                dev_err(&pdev->dev, "missing phy-mode property\n");
 299                ret = -EINVAL;
 300                goto err_remove_config_dt;
 301        }
 302
 303        /* use 2ns as fallback since this value was previously hardcoded */
 304        if (of_property_read_u32(pdev->dev.of_node, "amlogic,tx-delay-ns",
 305                                 &dwmac->tx_delay_ns))
 306                dwmac->tx_delay_ns = 2;
 307
 308        ret = meson8b_init_rgmii_tx_clk(dwmac);
 309        if (ret)
 310                goto err_remove_config_dt;
 311
 312        ret = meson8b_init_prg_eth(dwmac);
 313        if (ret)
 314                goto err_remove_config_dt;
 315
 316        plat_dat->bsp_priv = dwmac;
 317
 318        ret = stmmac_dvr_probe(&pdev->dev, plat_dat, &stmmac_res);
 319        if (ret)
 320                goto err_clk_disable;
 321
 322        return 0;
 323
 324err_clk_disable:
 325        if (phy_interface_mode_is_rgmii(dwmac->phy_mode))
 326                clk_disable_unprepare(dwmac->rgmii_tx_en_clk);
 327err_remove_config_dt:
 328        stmmac_remove_config_dt(pdev, plat_dat);
 329
 330        return ret;
 331}
 332
 333static int meson8b_dwmac_remove(struct platform_device *pdev)
 334{
 335        struct meson8b_dwmac *dwmac = get_stmmac_bsp_priv(&pdev->dev);
 336
 337        if (phy_interface_mode_is_rgmii(dwmac->phy_mode))
 338                clk_disable_unprepare(dwmac->rgmii_tx_en_clk);
 339
 340        return stmmac_pltfr_remove(pdev);
 341}
 342
 343static const struct of_device_id meson8b_dwmac_match[] = {
 344        { .compatible = "amlogic,meson8b-dwmac" },
 345        { .compatible = "amlogic,meson-gxbb-dwmac" },
 346        { }
 347};
 348MODULE_DEVICE_TABLE(of, meson8b_dwmac_match);
 349
 350static struct platform_driver meson8b_dwmac_driver = {
 351        .probe  = meson8b_dwmac_probe,
 352        .remove = meson8b_dwmac_remove,
 353        .driver = {
 354                .name           = "meson8b-dwmac",
 355                .pm             = &stmmac_pltfr_pm_ops,
 356                .of_match_table = meson8b_dwmac_match,
 357        },
 358};
 359module_platform_driver(meson8b_dwmac_driver);
 360
 361MODULE_AUTHOR("Martin Blumenstingl <martin.blumenstingl@googlemail.com>");
 362MODULE_DESCRIPTION("Amlogic Meson8b and GXBB DWMAC glue layer");
 363MODULE_LICENSE("GPL v2");
 364