linux/drivers/net/ethernet/ti/cpsw-phy-sel.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0
   2/* Texas Instruments Ethernet Switch Driver
   3 *
   4 * Copyright (C) 2013 Texas Instruments
   5 *
   6 * Module Author: Mugunthan V N <mugunthanvnm@ti.com>
   7 *
   8 */
   9
  10#include <linux/platform_device.h>
  11#include <linux/init.h>
  12#include <linux/netdevice.h>
  13#include <linux/phy.h>
  14#include <linux/of.h>
  15#include <linux/of_device.h>
  16
  17#include "cpsw.h"
  18
  19/* AM33xx SoC specific definitions for the CONTROL port */
  20#define AM33XX_GMII_SEL_MODE_MII        0
  21#define AM33XX_GMII_SEL_MODE_RMII       1
  22#define AM33XX_GMII_SEL_MODE_RGMII      2
  23
  24#define AM33XX_GMII_SEL_RMII2_IO_CLK_EN BIT(7)
  25#define AM33XX_GMII_SEL_RMII1_IO_CLK_EN BIT(6)
  26#define AM33XX_GMII_SEL_RGMII2_IDMODE   BIT(5)
  27#define AM33XX_GMII_SEL_RGMII1_IDMODE   BIT(4)
  28
  29#define GMII_SEL_MODE_MASK              0x3
  30
  31struct cpsw_phy_sel_priv {
  32        struct device   *dev;
  33        u32 __iomem     *gmii_sel;
  34        bool            rmii_clock_external;
  35        void (*cpsw_phy_sel)(struct cpsw_phy_sel_priv *priv,
  36                             phy_interface_t phy_mode, int slave);
  37};
  38
  39
  40static void cpsw_gmii_sel_am3352(struct cpsw_phy_sel_priv *priv,
  41                                 phy_interface_t phy_mode, int slave)
  42{
  43        u32 reg;
  44        u32 mask;
  45        u32 mode = 0;
  46        bool rgmii_id = false;
  47
  48        reg = readl(priv->gmii_sel);
  49
  50        switch (phy_mode) {
  51        case PHY_INTERFACE_MODE_RMII:
  52                mode = AM33XX_GMII_SEL_MODE_RMII;
  53                break;
  54
  55        case PHY_INTERFACE_MODE_RGMII:
  56                mode = AM33XX_GMII_SEL_MODE_RGMII;
  57                break;
  58
  59        case PHY_INTERFACE_MODE_RGMII_ID:
  60        case PHY_INTERFACE_MODE_RGMII_RXID:
  61        case PHY_INTERFACE_MODE_RGMII_TXID:
  62                mode = AM33XX_GMII_SEL_MODE_RGMII;
  63                rgmii_id = true;
  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        mask |= BIT(slave + 4);
  78        mode <<= slave * 2;
  79
  80        if (priv->rmii_clock_external) {
  81                if (slave == 0)
  82                        mode |= AM33XX_GMII_SEL_RMII1_IO_CLK_EN;
  83                else
  84                        mode |= AM33XX_GMII_SEL_RMII2_IO_CLK_EN;
  85        }
  86
  87        if (rgmii_id) {
  88                if (slave == 0)
  89                        mode |= AM33XX_GMII_SEL_RGMII1_IDMODE;
  90                else
  91                        mode |= AM33XX_GMII_SEL_RGMII2_IDMODE;
  92        }
  93
  94        reg &= ~mask;
  95        reg |= mode;
  96
  97        writel(reg, priv->gmii_sel);
  98}
  99
 100static void cpsw_gmii_sel_dra7xx(struct cpsw_phy_sel_priv *priv,
 101                                 phy_interface_t phy_mode, int slave)
 102{
 103        u32 reg;
 104        u32 mask;
 105        u32 mode = 0;
 106
 107        reg = readl(priv->gmii_sel);
 108
 109        switch (phy_mode) {
 110        case PHY_INTERFACE_MODE_RMII:
 111                mode = AM33XX_GMII_SEL_MODE_RMII;
 112                break;
 113
 114        case PHY_INTERFACE_MODE_RGMII:
 115        case PHY_INTERFACE_MODE_RGMII_ID:
 116        case PHY_INTERFACE_MODE_RGMII_RXID:
 117        case PHY_INTERFACE_MODE_RGMII_TXID:
 118                mode = AM33XX_GMII_SEL_MODE_RGMII;
 119                break;
 120
 121        default:
 122                dev_warn(priv->dev,
 123                         "Unsupported PHY mode: \"%s\". Defaulting to MII.\n",
 124                        phy_modes(phy_mode));
 125                fallthrough;
 126        case PHY_INTERFACE_MODE_MII:
 127                mode = AM33XX_GMII_SEL_MODE_MII;
 128                break;
 129        }
 130
 131        switch (slave) {
 132        case 0:
 133                mask = GMII_SEL_MODE_MASK;
 134                break;
 135        case 1:
 136                mask = GMII_SEL_MODE_MASK << 4;
 137                mode <<= 4;
 138                break;
 139        default:
 140                dev_err(priv->dev, "invalid slave number...\n");
 141                return;
 142        }
 143
 144        if (priv->rmii_clock_external)
 145                dev_err(priv->dev, "RMII External clock is not supported\n");
 146
 147        reg &= ~mask;
 148        reg |= mode;
 149
 150        writel(reg, priv->gmii_sel);
 151}
 152
 153static struct platform_driver cpsw_phy_sel_driver;
 154static int match(struct device *dev, const void *data)
 155{
 156        const struct device_node *node = (const struct device_node *)data;
 157        return dev->of_node == node &&
 158                dev->driver == &cpsw_phy_sel_driver.driver;
 159}
 160
 161void cpsw_phy_sel(struct device *dev, phy_interface_t phy_mode, int slave)
 162{
 163        struct device_node *node;
 164        struct cpsw_phy_sel_priv *priv;
 165
 166        node = of_parse_phandle(dev->of_node, "cpsw-phy-sel", 0);
 167        if (!node) {
 168                node = of_get_child_by_name(dev->of_node, "cpsw-phy-sel");
 169                if (!node) {
 170                        dev_err(dev, "Phy mode driver DT not found\n");
 171                        return;
 172                }
 173        }
 174
 175        dev = bus_find_device(&platform_bus_type, NULL, node, match);
 176        if (!dev) {
 177                dev_err(dev, "unable to find platform device for %pOF\n", node);
 178                goto out;
 179        }
 180
 181        priv = dev_get_drvdata(dev);
 182
 183        priv->cpsw_phy_sel(priv, phy_mode, slave);
 184
 185        put_device(dev);
 186out:
 187        of_node_put(node);
 188}
 189EXPORT_SYMBOL_GPL(cpsw_phy_sel);
 190
 191static const struct of_device_id cpsw_phy_sel_id_table[] = {
 192        {
 193                .compatible     = "ti,am3352-cpsw-phy-sel",
 194                .data           = &cpsw_gmii_sel_am3352,
 195        },
 196        {
 197                .compatible     = "ti,dra7xx-cpsw-phy-sel",
 198                .data           = &cpsw_gmii_sel_dra7xx,
 199        },
 200        {
 201                .compatible     = "ti,am43xx-cpsw-phy-sel",
 202                .data           = &cpsw_gmii_sel_am3352,
 203        },
 204        {}
 205};
 206
 207static int cpsw_phy_sel_probe(struct platform_device *pdev)
 208{
 209        const struct of_device_id *of_id;
 210        struct cpsw_phy_sel_priv *priv;
 211
 212        of_id = of_match_node(cpsw_phy_sel_id_table, pdev->dev.of_node);
 213        if (!of_id)
 214                return -EINVAL;
 215
 216        priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
 217        if (!priv) {
 218                dev_err(&pdev->dev, "unable to alloc memory for cpsw phy sel\n");
 219                return -ENOMEM;
 220        }
 221
 222        priv->dev = &pdev->dev;
 223        priv->cpsw_phy_sel = of_id->data;
 224
 225        priv->gmii_sel = devm_platform_ioremap_resource_byname(pdev, "gmii-sel");
 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