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#define AM33XX_GMII_SEL_RGMII2_IDMODE   BIT(5)
  34#define AM33XX_GMII_SEL_RGMII1_IDMODE   BIT(4)
  35
  36#define GMII_SEL_MODE_MASK              0x3
  37
  38struct cpsw_phy_sel_priv {
  39        struct device   *dev;
  40        u32 __iomem     *gmii_sel;
  41        bool            rmii_clock_external;
  42        void (*cpsw_phy_sel)(struct cpsw_phy_sel_priv *priv,
  43                             phy_interface_t phy_mode, int slave);
  44};
  45
  46
  47static void cpsw_gmii_sel_am3352(struct cpsw_phy_sel_priv *priv,
  48                                 phy_interface_t phy_mode, int slave)
  49{
  50        u32 reg;
  51        u32 mask;
  52        u32 mode = 0;
  53        bool rgmii_id = false;
  54
  55        reg = readl(priv->gmii_sel);
  56
  57        switch (phy_mode) {
  58        case PHY_INTERFACE_MODE_RMII:
  59                mode = AM33XX_GMII_SEL_MODE_RMII;
  60                break;
  61
  62        case PHY_INTERFACE_MODE_RGMII:
  63                mode = AM33XX_GMII_SEL_MODE_RGMII;
  64                break;
  65
  66        case PHY_INTERFACE_MODE_RGMII_ID:
  67        case PHY_INTERFACE_MODE_RGMII_RXID:
  68        case PHY_INTERFACE_MODE_RGMII_TXID:
  69                mode = AM33XX_GMII_SEL_MODE_RGMII;
  70                rgmii_id = true;
  71                break;
  72
  73        default:
  74                dev_warn(priv->dev,
  75                         "Unsupported PHY mode: \"%s\". Defaulting to MII.\n",
  76                        phy_modes(phy_mode));
  77                /* fallthrough */
  78        case PHY_INTERFACE_MODE_MII:
  79                mode = AM33XX_GMII_SEL_MODE_MII;
  80                break;
  81        };
  82
  83        mask = GMII_SEL_MODE_MASK << (slave * 2) | BIT(slave + 6);
  84        mask |= BIT(slave + 4);
  85        mode <<= slave * 2;
  86
  87        if (priv->rmii_clock_external) {
  88                if (slave == 0)
  89                        mode |= AM33XX_GMII_SEL_RMII1_IO_CLK_EN;
  90                else
  91                        mode |= AM33XX_GMII_SEL_RMII2_IO_CLK_EN;
  92        }
  93
  94        if (rgmii_id) {
  95                if (slave == 0)
  96                        mode |= AM33XX_GMII_SEL_RGMII1_IDMODE;
  97                else
  98                        mode |= AM33XX_GMII_SEL_RGMII2_IDMODE;
  99        }
 100
 101        reg &= ~mask;
 102        reg |= mode;
 103
 104        writel(reg, priv->gmii_sel);
 105}
 106
 107static void cpsw_gmii_sel_dra7xx(struct cpsw_phy_sel_priv *priv,
 108                                 phy_interface_t phy_mode, int slave)
 109{
 110        u32 reg;
 111        u32 mask;
 112        u32 mode = 0;
 113
 114        reg = readl(priv->gmii_sel);
 115
 116        switch (phy_mode) {
 117        case PHY_INTERFACE_MODE_RMII:
 118                mode = AM33XX_GMII_SEL_MODE_RMII;
 119                break;
 120
 121        case PHY_INTERFACE_MODE_RGMII:
 122        case PHY_INTERFACE_MODE_RGMII_ID:
 123        case PHY_INTERFACE_MODE_RGMII_RXID:
 124        case PHY_INTERFACE_MODE_RGMII_TXID:
 125                mode = AM33XX_GMII_SEL_MODE_RGMII;
 126                break;
 127
 128        default:
 129                dev_warn(priv->dev,
 130                         "Unsupported PHY mode: \"%s\". Defaulting to MII.\n",
 131                        phy_modes(phy_mode));
 132                /* fallthrough */
 133        case PHY_INTERFACE_MODE_MII:
 134                mode = AM33XX_GMII_SEL_MODE_MII;
 135                break;
 136        };
 137
 138        switch (slave) {
 139        case 0:
 140                mask = GMII_SEL_MODE_MASK;
 141                break;
 142        case 1:
 143                mask = GMII_SEL_MODE_MASK << 4;
 144                mode <<= 4;
 145                break;
 146        default:
 147                dev_err(priv->dev, "invalid slave number...\n");
 148                return;
 149        }
 150
 151        if (priv->rmii_clock_external)
 152                dev_err(priv->dev, "RMII External clock is not supported\n");
 153
 154        reg &= ~mask;
 155        reg |= mode;
 156
 157        writel(reg, priv->gmii_sel);
 158}
 159
 160static struct platform_driver cpsw_phy_sel_driver;
 161static int match(struct device *dev, void *data)
 162{
 163        struct device_node *node = (struct device_node *)data;
 164        return dev->of_node == node &&
 165                dev->driver == &cpsw_phy_sel_driver.driver;
 166}
 167
 168void cpsw_phy_sel(struct device *dev, phy_interface_t phy_mode, int slave)
 169{
 170        struct device_node *node;
 171        struct cpsw_phy_sel_priv *priv;
 172
 173        node = of_get_child_by_name(dev->of_node, "cpsw-phy-sel");
 174        if (!node) {
 175                dev_err(dev, "Phy mode driver DT not found\n");
 176                return;
 177        }
 178
 179        dev = bus_find_device(&platform_bus_type, NULL, node, match);
 180        of_node_put(node);
 181        priv = dev_get_drvdata(dev);
 182
 183        priv->cpsw_phy_sel(priv, phy_mode, slave);
 184
 185        put_device(dev);
 186}
 187EXPORT_SYMBOL_GPL(cpsw_phy_sel);
 188
 189static const struct of_device_id cpsw_phy_sel_id_table[] = {
 190        {
 191                .compatible     = "ti,am3352-cpsw-phy-sel",
 192                .data           = &cpsw_gmii_sel_am3352,
 193        },
 194        {
 195                .compatible     = "ti,dra7xx-cpsw-phy-sel",
 196                .data           = &cpsw_gmii_sel_dra7xx,
 197        },
 198        {
 199                .compatible     = "ti,am43xx-cpsw-phy-sel",
 200                .data           = &cpsw_gmii_sel_am3352,
 201        },
 202        {}
 203};
 204
 205static int cpsw_phy_sel_probe(struct platform_device *pdev)
 206{
 207        struct resource *res;
 208        const struct of_device_id *of_id;
 209        struct cpsw_phy_sel_priv *priv;
 210
 211        of_id = of_match_node(cpsw_phy_sel_id_table, pdev->dev.of_node);
 212        if (!of_id)
 213                return -EINVAL;
 214
 215        priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
 216        if (!priv) {
 217                dev_err(&pdev->dev, "unable to alloc memory for cpsw phy sel\n");
 218                return -ENOMEM;
 219        }
 220
 221        priv->dev = &pdev->dev;
 222        priv->cpsw_phy_sel = of_id->data;
 223
 224        res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "gmii-sel");
 225        priv->gmii_sel = devm_ioremap_resource(&pdev->dev, res);
 226        if (IS_ERR(priv->gmii_sel))
 227                return PTR_ERR(priv->gmii_sel);
 228
 229        if (of_find_property(pdev->dev.of_node, "rmii-clock-ext", NULL))
 230                priv->rmii_clock_external = true;
 231
 232        dev_set_drvdata(&pdev->dev, priv);
 233
 234        return 0;
 235}
 236
 237static struct platform_driver cpsw_phy_sel_driver = {
 238        .probe          = cpsw_phy_sel_probe,
 239        .driver         = {
 240                .name   = "cpsw-phy-sel",
 241                .of_match_table = cpsw_phy_sel_id_table,
 242        },
 243};
 244builtin_platform_driver(cpsw_phy_sel_driver);
 245
lxr.linux.no kindly hosted by Redpill Linpro AS, provider of Linux consulting and operations services since 1995.