Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0
0002 /* Texas Instruments Ethernet Switch Driver
0003  *
0004  * Copyright (C) 2013 Texas Instruments
0005  *
0006  * Module Author: Mugunthan V N <mugunthanvnm@ti.com>
0007  *
0008  */
0009 
0010 #include <linux/platform_device.h>
0011 #include <linux/init.h>
0012 #include <linux/netdevice.h>
0013 #include <linux/phy.h>
0014 #include <linux/of.h>
0015 #include <linux/of_device.h>
0016 
0017 #include "cpsw.h"
0018 
0019 /* AM33xx SoC specific definitions for the CONTROL port */
0020 #define AM33XX_GMII_SEL_MODE_MII    0
0021 #define AM33XX_GMII_SEL_MODE_RMII   1
0022 #define AM33XX_GMII_SEL_MODE_RGMII  2
0023 
0024 #define AM33XX_GMII_SEL_RMII2_IO_CLK_EN BIT(7)
0025 #define AM33XX_GMII_SEL_RMII1_IO_CLK_EN BIT(6)
0026 #define AM33XX_GMII_SEL_RGMII2_IDMODE   BIT(5)
0027 #define AM33XX_GMII_SEL_RGMII1_IDMODE   BIT(4)
0028 
0029 #define GMII_SEL_MODE_MASK      0x3
0030 
0031 struct cpsw_phy_sel_priv {
0032     struct device   *dev;
0033     u32 __iomem *gmii_sel;
0034     bool        rmii_clock_external;
0035     void (*cpsw_phy_sel)(struct cpsw_phy_sel_priv *priv,
0036                  phy_interface_t phy_mode, int slave);
0037 };
0038 
0039 
0040 static void cpsw_gmii_sel_am3352(struct cpsw_phy_sel_priv *priv,
0041                  phy_interface_t phy_mode, int slave)
0042 {
0043     u32 reg;
0044     u32 mask;
0045     u32 mode = 0;
0046     bool rgmii_id = false;
0047 
0048     reg = readl(priv->gmii_sel);
0049 
0050     switch (phy_mode) {
0051     case PHY_INTERFACE_MODE_RMII:
0052         mode = AM33XX_GMII_SEL_MODE_RMII;
0053         break;
0054 
0055     case PHY_INTERFACE_MODE_RGMII:
0056         mode = AM33XX_GMII_SEL_MODE_RGMII;
0057         break;
0058 
0059     case PHY_INTERFACE_MODE_RGMII_ID:
0060     case PHY_INTERFACE_MODE_RGMII_RXID:
0061     case PHY_INTERFACE_MODE_RGMII_TXID:
0062         mode = AM33XX_GMII_SEL_MODE_RGMII;
0063         rgmii_id = true;
0064         break;
0065 
0066     default:
0067         dev_warn(priv->dev,
0068              "Unsupported PHY mode: \"%s\". Defaulting to MII.\n",
0069             phy_modes(phy_mode));
0070         fallthrough;
0071     case PHY_INTERFACE_MODE_MII:
0072         mode = AM33XX_GMII_SEL_MODE_MII;
0073         break;
0074     }
0075 
0076     mask = GMII_SEL_MODE_MASK << (slave * 2) | BIT(slave + 6);
0077     mask |= BIT(slave + 4);
0078     mode <<= slave * 2;
0079 
0080     if (priv->rmii_clock_external) {
0081         if (slave == 0)
0082             mode |= AM33XX_GMII_SEL_RMII1_IO_CLK_EN;
0083         else
0084             mode |= AM33XX_GMII_SEL_RMII2_IO_CLK_EN;
0085     }
0086 
0087     if (rgmii_id) {
0088         if (slave == 0)
0089             mode |= AM33XX_GMII_SEL_RGMII1_IDMODE;
0090         else
0091             mode |= AM33XX_GMII_SEL_RGMII2_IDMODE;
0092     }
0093 
0094     reg &= ~mask;
0095     reg |= mode;
0096 
0097     writel(reg, priv->gmii_sel);
0098 }
0099 
0100 static void cpsw_gmii_sel_dra7xx(struct cpsw_phy_sel_priv *priv,
0101                  phy_interface_t phy_mode, int slave)
0102 {
0103     u32 reg;
0104     u32 mask;
0105     u32 mode = 0;
0106 
0107     reg = readl(priv->gmii_sel);
0108 
0109     switch (phy_mode) {
0110     case PHY_INTERFACE_MODE_RMII:
0111         mode = AM33XX_GMII_SEL_MODE_RMII;
0112         break;
0113 
0114     case PHY_INTERFACE_MODE_RGMII:
0115     case PHY_INTERFACE_MODE_RGMII_ID:
0116     case PHY_INTERFACE_MODE_RGMII_RXID:
0117     case PHY_INTERFACE_MODE_RGMII_TXID:
0118         mode = AM33XX_GMII_SEL_MODE_RGMII;
0119         break;
0120 
0121     default:
0122         dev_warn(priv->dev,
0123              "Unsupported PHY mode: \"%s\". Defaulting to MII.\n",
0124             phy_modes(phy_mode));
0125         fallthrough;
0126     case PHY_INTERFACE_MODE_MII:
0127         mode = AM33XX_GMII_SEL_MODE_MII;
0128         break;
0129     }
0130 
0131     switch (slave) {
0132     case 0:
0133         mask = GMII_SEL_MODE_MASK;
0134         break;
0135     case 1:
0136         mask = GMII_SEL_MODE_MASK << 4;
0137         mode <<= 4;
0138         break;
0139     default:
0140         dev_err(priv->dev, "invalid slave number...\n");
0141         return;
0142     }
0143 
0144     if (priv->rmii_clock_external)
0145         dev_err(priv->dev, "RMII External clock is not supported\n");
0146 
0147     reg &= ~mask;
0148     reg |= mode;
0149 
0150     writel(reg, priv->gmii_sel);
0151 }
0152 
0153 static struct platform_driver cpsw_phy_sel_driver;
0154 static int match(struct device *dev, const void *data)
0155 {
0156     const struct device_node *node = (const struct device_node *)data;
0157     return dev->of_node == node &&
0158         dev->driver == &cpsw_phy_sel_driver.driver;
0159 }
0160 
0161 void cpsw_phy_sel(struct device *dev, phy_interface_t phy_mode, int slave)
0162 {
0163     struct device_node *node;
0164     struct cpsw_phy_sel_priv *priv;
0165 
0166     node = of_parse_phandle(dev->of_node, "cpsw-phy-sel", 0);
0167     if (!node) {
0168         node = of_get_child_by_name(dev->of_node, "cpsw-phy-sel");
0169         if (!node) {
0170             dev_err(dev, "Phy mode driver DT not found\n");
0171             return;
0172         }
0173     }
0174 
0175     dev = bus_find_device(&platform_bus_type, NULL, node, match);
0176     if (!dev) {
0177         dev_err(dev, "unable to find platform device for %pOF\n", node);
0178         goto out;
0179     }
0180 
0181     priv = dev_get_drvdata(dev);
0182 
0183     priv->cpsw_phy_sel(priv, phy_mode, slave);
0184 
0185     put_device(dev);
0186 out:
0187     of_node_put(node);
0188 }
0189 EXPORT_SYMBOL_GPL(cpsw_phy_sel);
0190 
0191 static const struct of_device_id cpsw_phy_sel_id_table[] = {
0192     {
0193         .compatible = "ti,am3352-cpsw-phy-sel",
0194         .data       = &cpsw_gmii_sel_am3352,
0195     },
0196     {
0197         .compatible = "ti,dra7xx-cpsw-phy-sel",
0198         .data       = &cpsw_gmii_sel_dra7xx,
0199     },
0200     {
0201         .compatible = "ti,am43xx-cpsw-phy-sel",
0202         .data       = &cpsw_gmii_sel_am3352,
0203     },
0204     {}
0205 };
0206 
0207 static int cpsw_phy_sel_probe(struct platform_device *pdev)
0208 {
0209     const struct of_device_id *of_id;
0210     struct cpsw_phy_sel_priv *priv;
0211 
0212     of_id = of_match_node(cpsw_phy_sel_id_table, pdev->dev.of_node);
0213     if (!of_id)
0214         return -EINVAL;
0215 
0216     priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
0217     if (!priv) {
0218         dev_err(&pdev->dev, "unable to alloc memory for cpsw phy sel\n");
0219         return -ENOMEM;
0220     }
0221 
0222     priv->dev = &pdev->dev;
0223     priv->cpsw_phy_sel = of_id->data;
0224 
0225     priv->gmii_sel = devm_platform_ioremap_resource_byname(pdev, "gmii-sel");
0226     if (IS_ERR(priv->gmii_sel))
0227         return PTR_ERR(priv->gmii_sel);
0228 
0229     if (of_find_property(pdev->dev.of_node, "rmii-clock-ext", NULL))
0230         priv->rmii_clock_external = true;
0231 
0232     dev_set_drvdata(&pdev->dev, priv);
0233 
0234     return 0;
0235 }
0236 
0237 static struct platform_driver cpsw_phy_sel_driver = {
0238     .probe      = cpsw_phy_sel_probe,
0239     .driver     = {
0240         .name   = "cpsw-phy-sel",
0241         .of_match_table = cpsw_phy_sel_id_table,
0242     },
0243 };
0244 builtin_platform_driver(cpsw_phy_sel_driver);