linux/drivers/net/ethernet/ti/cpsw-phy-sel.c
<<
>>
Prefs
   1/* Texas Instruments Ethernet Switch Driver
   2 *
   3 * Copyright (C) 2013 Texas Instruments
   4 *
   5 * Module Author: Mugunthan V N <mugunthanvnm@ti.com>
   6 *
   7 * This program is free software; you can redistribute it and/or
   8 * modify it under the terms of the GNU General Public License
   9 * version 2 as published by the Free Software Foundation.
  10 *
  11 * This program is distributed "as is" WITHOUT ANY WARRANTY of any
  12 * kind, whether express or implied; without even the implied warranty
  13 * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  14 * GNU General Public License for more details.
  15 */
  16
  17#include <linux/platform_device.h>
  18#include <linux/init.h>
  19#include <linux/netdevice.h>
  20#include <linux/phy.h>
  21#include <linux/of.h>
  22#include <linux/of_device.h>
  23
  24#include "cpsw.h"
  25
  26/* AM33xx SoC specific definitions for the CONTROL port */
  27#define AM33XX_GMII_SEL_MODE_MII        0
  28#define AM33XX_GMII_SEL_MODE_RMII       1
  29#define AM33XX_GMII_SEL_MODE_RGMII      2
  30
  31#define AM33XX_GMII_SEL_RMII2_IO_CLK_EN BIT(7)
  32#define AM33XX_GMII_SEL_RMII1_IO_CLK_EN BIT(6)
  33
  34#define GMII_SEL_MODE_MASK              0x3
  35
  36struct cpsw_phy_sel_priv {
  37        struct device   *dev;
  38        u32 __iomem     *gmii_sel;
  39        bool            rmii_clock_external;
  40        void (*cpsw_phy_sel)(struct cpsw_phy_sel_priv *priv,
  41                             phy_interface_t phy_mode, int slave);
  42};
  43
  44
  45static void cpsw_gmii_sel_am3352(struct cpsw_phy_sel_priv *priv,
  46                                 phy_interface_t phy_mode, int slave)
  47{
  48        u32 reg;
  49        u32 mask;
  50        u32 mode = 0;
  51
  52        reg = readl(priv->gmii_sel);
  53
  54        switch (phy_mode) {
  55        case PHY_INTERFACE_MODE_RMII:
  56                mode = AM33XX_GMII_SEL_MODE_RMII;
  57                break;
  58
  59        case PHY_INTERFACE_MODE_RGMII:
  60        case PHY_INTERFACE_MODE_RGMII_ID:
  61        case PHY_INTERFACE_MODE_RGMII_RXID:
  62        case PHY_INTERFACE_MODE_RGMII_TXID:
  63                mode = AM33XX_GMII_SEL_MODE_RGMII;
  64                break;
  65
  66        default:
  67                dev_warn(priv->dev,
  68                         "Unsupported PHY mode: \"%s\". Defaulting to MII.\n",
  69                        phy_modes(phy_mode));
  70                /* fallthrough */
  71        case PHY_INTERFACE_MODE_MII:
  72                mode = AM33XX_GMII_SEL_MODE_MII;
  73                break;
  74        };
  75
  76        mask = GMII_SEL_MODE_MASK << (slave * 2) | BIT(slave + 6);
  77        mode <<= slave * 2;
  78
  79        if (priv->rmii_clock_external) {
  80                if (slave == 0)
  81                        mode |= AM33XX_GMII_SEL_RMII1_IO_CLK_EN;
  82                else
  83                        mode |= AM33XX_GMII_SEL_RMII2_IO_CLK_EN;
  84        }
  85
  86        reg &= ~mask;
  87        reg |= mode;
  88
  89        writel(reg, priv->gmii_sel);
  90}
  91
  92static void cpsw_gmii_sel_dra7xx(struct cpsw_phy_sel_priv *priv,
  93                                 phy_interface_t phy_mode, int slave)
  94{
  95        u32 reg;
  96        u32 mask;
  97        u32 mode = 0;
  98
  99        reg = readl(priv->gmii_sel);
 100
 101        switch (phy_mode) {
 102        case PHY_INTERFACE_MODE_RMII:
 103                mode = AM33XX_GMII_SEL_MODE_RMII;
 104                break;
 105
 106        case PHY_INTERFACE_MODE_RGMII:
 107        case PHY_INTERFACE_MODE_RGMII_ID:
 108        case PHY_INTERFACE_MODE_RGMII_RXID:
 109        case PHY_INTERFACE_MODE_RGMII_TXID:
 110                mode = AM33XX_GMII_SEL_MODE_RGMII;
 111                break;
 112
 113        default:
 114                dev_warn(priv->dev,
 115                         "Unsupported PHY mode: \"%s\". Defaulting to MII.\n",
 116                        phy_modes(phy_mode));
 117                /* fallthrough */
 118        case PHY_INTERFACE_MODE_MII:
 119                mode = AM33XX_GMII_SEL_MODE_MII;
 120                break;
 121        };
 122
 123        switch (slave) {
 124        case 0:
 125                mask = GMII_SEL_MODE_MASK;
 126                break;
 127        case 1:
 128                mask = GMII_SEL_MODE_MASK << 4;
 129                mode <<= 4;
 130                break;
 131        default:
 132                dev_err(priv->dev, "invalid slave number...\n");
 133                return;
 134        }
 135
 136        if (priv->rmii_clock_external)
 137                dev_err(priv->dev, "RMII External clock is not supported\n");
 138
 139        reg &= ~mask;
 140        reg |= mode;
 141
 142        writel(reg, priv->gmii_sel);
 143}
 144
 145static struct platform_driver cpsw_phy_sel_driver;
 146static int match(struct device *dev, void *data)
 147{
 148        struct device_node *node = (struct device_node *)data;
 149        return dev->of_node == node &&
 150                dev->driver == &cpsw_phy_sel_driver.driver;
 151}
 152
 153void cpsw_phy_sel(struct device *dev, phy_interface_t phy_mode, int slave)
 154{
 155        struct device_node *node;
 156        struct cpsw_phy_sel_priv *priv;
 157
 158        node = of_get_child_by_name(dev->of_node, "cpsw-phy-sel");
 159        if (!node) {
 160                dev_err(dev, "Phy mode driver DT not found\n");
 161                return;
 162        }
 163
 164        dev = bus_find_device(&platform_bus_type, NULL, node, match);
 165        priv = dev_get_drvdata(dev);
 166
 167        priv->cpsw_phy_sel(priv, phy_mode, slave);
 168}
 169EXPORT_SYMBOL_GPL(cpsw_phy_sel);
 170
 171static const struct of_device_id cpsw_phy_sel_id_table[] = {
 172        {
 173                .compatible     = "ti,am3352-cpsw-phy-sel",
 174                .data           = &cpsw_gmii_sel_am3352,
 175        },
 176        {
 177                .compatible     = "ti,dra7xx-cpsw-phy-sel",
 178                .data           = &cpsw_gmii_sel_dra7xx,
 179        },
 180        {
 181                .compatible     = "ti,am43xx-cpsw-phy-sel",
 182                .data           = &cpsw_gmii_sel_am3352,
 183        },
 184        {}
 185};
 186
 187static int cpsw_phy_sel_probe(struct platform_device *pdev)
 188{
 189        struct resource *res;
 190        const struct of_device_id *of_id;
 191        struct cpsw_phy_sel_priv *priv;
 192
 193        of_id = of_match_node(cpsw_phy_sel_id_table, pdev->dev.of_node);
 194        if (!of_id)
 195                return -EINVAL;
 196
 197        priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
 198        if (!priv) {
 199                dev_err(&pdev->dev, "unable to alloc memory for cpsw phy sel\n");
 200                return -ENOMEM;
 201        }
 202
 203        priv->dev = &pdev->dev;
 204        priv->cpsw_phy_sel = of_id->data;
 205
 206        res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "gmii-sel");
 207        priv->gmii_sel = devm_ioremap_resource(&pdev->dev, res);
 208        if (IS_ERR(priv->gmii_sel))
 209                return PTR_ERR(priv->gmii_sel);
 210
 211        if (of_find_property(pdev->dev.of_node, "rmii-clock-ext", NULL))
 212                priv->rmii_clock_external = true;
 213
 214        dev_set_drvdata(&pdev->dev, priv);
 215
 216        return 0;
 217}
 218
 219static struct platform_driver cpsw_phy_sel_driver = {
 220        .probe          = cpsw_phy_sel_probe,
 221        .driver         = {
 222                .name   = "cpsw-phy-sel",
 223                .of_match_table = cpsw_phy_sel_id_table,
 224        },
 225};
 226builtin_platform_driver(cpsw_phy_sel_driver);
 227
lxr.linux.no kindly hosted by Redpill Linpro AS, provider of Linux consulting and operations services since 1995.