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