net: phy: at803x: add device tree binding
authorMichael Walle <michael@walle.cc>
Wed, 6 Nov 2019 22:36:14 +0000 (23:36 +0100)
committerDavid S. Miller <davem@davemloft.net>
Thu, 7 Nov 2019 05:42:06 +0000 (21:42 -0800)
Add support for configuring the CLK_25M pin as well as the RGMII I/O
voltage by the device tree.

Signed-off-by: Michael Walle <michael@walle.cc>
Reviewed-by: Florian Fainelli <f.fainelli@gmail.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
drivers/net/phy/at803x.c

index 8e30db28fd7d28640efa42a65ab9f6cbfb31b366..aa782b28615baaedc3650c5ede73302fe77b6373 100644 (file)
 #include <linux/netdevice.h>
 #include <linux/etherdevice.h>
 #include <linux/of_gpio.h>
+#include <linux/bitfield.h>
 #include <linux/gpio/consumer.h>
+#include <linux/regulator/of_regulator.h>
+#include <linux/regulator/driver.h>
+#include <linux/regulator/consumer.h>
+#include <dt-bindings/net/qca-ar803x.h>
 
 #define AT803X_SPECIFIC_STATUS                 0x11
 #define AT803X_SS_SPEED_MASK                   (3 << 14)
 #define AT803X_DEBUG_REG_5                     0x05
 #define AT803X_DEBUG_TX_CLK_DLY_EN             BIT(8)
 
+#define AT803X_DEBUG_REG_1F                    0x1F
+#define AT803X_DEBUG_PLL_ON                    BIT(2)
+#define AT803X_DEBUG_RGMII_1V8                 BIT(3)
+
+/* AT803x supports either the XTAL input pad, an internal PLL or the
+ * DSP as clock reference for the clock output pad. The XTAL reference
+ * is only used for 25 MHz output, all other frequencies need the PLL.
+ * The DSP as a clock reference is used in synchronous ethernet
+ * applications.
+ *
+ * By default the PLL is only enabled if there is a link. Otherwise
+ * the PHY will go into low power state and disabled the PLL. You can
+ * set the PLL_ON bit (see debug register 0x1f) to keep the PLL always
+ * enabled.
+ */
+#define AT803X_MMD7_CLK25M                     0x8016
+#define AT803X_CLK_OUT_MASK                    GENMASK(4, 2)
+#define AT803X_CLK_OUT_25MHZ_XTAL              0
+#define AT803X_CLK_OUT_25MHZ_DSP               1
+#define AT803X_CLK_OUT_50MHZ_PLL               2
+#define AT803X_CLK_OUT_50MHZ_DSP               3
+#define AT803X_CLK_OUT_62_5MHZ_PLL             4
+#define AT803X_CLK_OUT_62_5MHZ_DSP             5
+#define AT803X_CLK_OUT_125MHZ_PLL              6
+#define AT803X_CLK_OUT_125MHZ_DSP              7
+
+/* The AR8035 has another mask which is compatible with the AR8031 mask but
+ * doesn't support choosing between XTAL/PLL and DSP.
+ */
+#define AT8035_CLK_OUT_MASK                    GENMASK(4, 3)
+
+#define AT803X_CLK_OUT_STRENGTH_MASK           GENMASK(8, 7)
+#define AT803X_CLK_OUT_STRENGTH_FULL           0
+#define AT803X_CLK_OUT_STRENGTH_HALF           1
+#define AT803X_CLK_OUT_STRENGTH_QUARTER                2
+
 #define ATH9331_PHY_ID 0x004dd041
 #define ATH8030_PHY_ID 0x004dd076
 #define ATH8031_PHY_ID 0x004dd074
@@ -72,6 +113,16 @@ MODULE_DESCRIPTION("Atheros 803x PHY driver");
 MODULE_AUTHOR("Matus Ujhelyi");
 MODULE_LICENSE("GPL");
 
+struct at803x_priv {
+       int flags;
+#define AT803X_KEEP_PLL_ENABLED        BIT(0)  /* don't turn off internal PLL */
+       u16 clk_25m_reg;
+       u16 clk_25m_mask;
+       struct regulator_dev *vddio_rdev;
+       struct regulator_dev *vddh_rdev;
+       struct regulator *vddio;
+};
+
 struct at803x_context {
        u16 bmcr;
        u16 advertise;
@@ -237,6 +288,238 @@ static int at803x_resume(struct phy_device *phydev)
        return phy_modify(phydev, MII_BMCR, BMCR_PDOWN | BMCR_ISOLATE, 0);
 }
 
+static int at803x_rgmii_reg_set_voltage_sel(struct regulator_dev *rdev,
+                                           unsigned int selector)
+{
+       struct phy_device *phydev = rdev_get_drvdata(rdev);
+
+       if (selector)
+               return at803x_debug_reg_mask(phydev, AT803X_DEBUG_REG_1F,
+                                            0, AT803X_DEBUG_RGMII_1V8);
+       else
+               return at803x_debug_reg_mask(phydev, AT803X_DEBUG_REG_1F,
+                                            AT803X_DEBUG_RGMII_1V8, 0);
+}
+
+static int at803x_rgmii_reg_get_voltage_sel(struct regulator_dev *rdev)
+{
+       struct phy_device *phydev = rdev_get_drvdata(rdev);
+       int val;
+
+       val = at803x_debug_reg_read(phydev, AT803X_DEBUG_REG_1F);
+       if (val < 0)
+               return val;
+
+       return (val & AT803X_DEBUG_RGMII_1V8) ? 1 : 0;
+}
+
+static struct regulator_ops vddio_regulator_ops = {
+       .list_voltage = regulator_list_voltage_table,
+       .set_voltage_sel = at803x_rgmii_reg_set_voltage_sel,
+       .get_voltage_sel = at803x_rgmii_reg_get_voltage_sel,
+};
+
+static const unsigned int vddio_voltage_table[] = {
+       1500000,
+       1800000,
+};
+
+static const struct regulator_desc vddio_desc = {
+       .name = "vddio",
+       .of_match = of_match_ptr("vddio-regulator"),
+       .n_voltages = ARRAY_SIZE(vddio_voltage_table),
+       .volt_table = vddio_voltage_table,
+       .ops = &vddio_regulator_ops,
+       .type = REGULATOR_VOLTAGE,
+       .owner = THIS_MODULE,
+};
+
+static struct regulator_ops vddh_regulator_ops = {
+};
+
+static const struct regulator_desc vddh_desc = {
+       .name = "vddh",
+       .of_match = of_match_ptr("vddh-regulator"),
+       .n_voltages = 1,
+       .fixed_uV = 2500000,
+       .ops = &vddh_regulator_ops,
+       .type = REGULATOR_VOLTAGE,
+       .owner = THIS_MODULE,
+};
+
+static int at8031_register_regulators(struct phy_device *phydev)
+{
+       struct at803x_priv *priv = phydev->priv;
+       struct device *dev = &phydev->mdio.dev;
+       struct regulator_config config = { };
+
+       config.dev = dev;
+       config.driver_data = phydev;
+
+       priv->vddio_rdev = devm_regulator_register(dev, &vddio_desc, &config);
+       if (IS_ERR(priv->vddio_rdev)) {
+               phydev_err(phydev, "failed to register VDDIO regulator\n");
+               return PTR_ERR(priv->vddio_rdev);
+       }
+
+       priv->vddh_rdev = devm_regulator_register(dev, &vddh_desc, &config);
+       if (IS_ERR(priv->vddh_rdev)) {
+               phydev_err(phydev, "failed to register VDDH regulator\n");
+               return PTR_ERR(priv->vddh_rdev);
+       }
+
+       return 0;
+}
+
+static bool at803x_match_phy_id(struct phy_device *phydev, u32 phy_id)
+{
+       return (phydev->phy_id & phydev->drv->phy_id_mask)
+               == (phy_id & phydev->drv->phy_id_mask);
+}
+
+static int at803x_parse_dt(struct phy_device *phydev)
+{
+       struct device_node *node = phydev->mdio.dev.of_node;
+       struct at803x_priv *priv = phydev->priv;
+       unsigned int sel, mask;
+       u32 freq, strength;
+       int ret;
+
+       if (!IS_ENABLED(CONFIG_OF_MDIO))
+               return 0;
+
+       ret = of_property_read_u32(node, "qca,clk-out-frequency", &freq);
+       if (!ret) {
+               mask = AT803X_CLK_OUT_MASK;
+               switch (freq) {
+               case 25000000:
+                       sel = AT803X_CLK_OUT_25MHZ_XTAL;
+                       break;
+               case 50000000:
+                       sel = AT803X_CLK_OUT_50MHZ_PLL;
+                       break;
+               case 62500000:
+                       sel = AT803X_CLK_OUT_62_5MHZ_PLL;
+                       break;
+               case 125000000:
+                       sel = AT803X_CLK_OUT_125MHZ_PLL;
+                       break;
+               default:
+                       phydev_err(phydev, "invalid qca,clk-out-frequency\n");
+                       return -EINVAL;
+               }
+
+               priv->clk_25m_reg |= FIELD_PREP(mask, sel);
+               priv->clk_25m_mask |= mask;
+
+               /* Fixup for the AR8030/AR8035. This chip has another mask and
+                * doesn't support the DSP reference. Eg. the lowest bit of the
+                * mask. The upper two bits select the same frequencies. Mask
+                * the lowest bit here.
+                *
+                * Warning:
+                *   There was no datasheet for the AR8030 available so this is
+                *   just a guess. But the AR8035 is listed as pin compatible
+                *   to the AR8030 so there might be a good chance it works on
+                *   the AR8030 too.
+                */
+               if (at803x_match_phy_id(phydev, ATH8030_PHY_ID) ||
+                   at803x_match_phy_id(phydev, ATH8035_PHY_ID)) {
+                       priv->clk_25m_reg &= ~AT8035_CLK_OUT_MASK;
+                       priv->clk_25m_mask &= ~AT8035_CLK_OUT_MASK;
+               }
+       }
+
+       ret = of_property_read_u32(node, "qca,clk-out-strength", &strength);
+       if (!ret) {
+               priv->clk_25m_mask |= AT803X_CLK_OUT_STRENGTH_MASK;
+               switch (strength) {
+               case AR803X_STRENGTH_FULL:
+                       priv->clk_25m_reg |= AT803X_CLK_OUT_STRENGTH_FULL;
+                       break;
+               case AR803X_STRENGTH_HALF:
+                       priv->clk_25m_reg |= AT803X_CLK_OUT_STRENGTH_HALF;
+                       break;
+               case AR803X_STRENGTH_QUARTER:
+                       priv->clk_25m_reg |= AT803X_CLK_OUT_STRENGTH_QUARTER;
+                       break;
+               default:
+                       phydev_err(phydev, "invalid qca,clk-out-strength\n");
+                       return -EINVAL;
+               }
+       }
+
+       /* Only supported on AR8031, the AR8030/AR8035 use strapping options */
+       if (at803x_match_phy_id(phydev, ATH8031_PHY_ID)) {
+               if (of_property_read_bool(node, "qca,keep-pll-enabled"))
+                       priv->flags |= AT803X_KEEP_PLL_ENABLED;
+
+               ret = at8031_register_regulators(phydev);
+               if (ret < 0)
+                       return ret;
+
+               priv->vddio = devm_regulator_get_optional(&phydev->mdio.dev,
+                                                         "vddio");
+               if (IS_ERR(priv->vddio)) {
+                       phydev_err(phydev, "failed to get VDDIO regulator\n");
+                       return PTR_ERR(priv->vddio);
+               }
+
+               ret = regulator_enable(priv->vddio);
+               if (ret < 0)
+                       return ret;
+       }
+
+       return 0;
+}
+
+static int at803x_probe(struct phy_device *phydev)
+{
+       struct device *dev = &phydev->mdio.dev;
+       struct at803x_priv *priv;
+
+       priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
+       if (!priv)
+               return -ENOMEM;
+
+       phydev->priv = priv;
+
+       return at803x_parse_dt(phydev);
+}
+
+static int at803x_clk_out_config(struct phy_device *phydev)
+{
+       struct at803x_priv *priv = phydev->priv;
+       int val;
+
+       if (!priv->clk_25m_mask)
+               return 0;
+
+       val = phy_read_mmd(phydev, MDIO_MMD_AN, AT803X_MMD7_CLK25M);
+       if (val < 0)
+               return val;
+
+       val &= ~priv->clk_25m_mask;
+       val |= priv->clk_25m_reg;
+
+       return phy_write_mmd(phydev, MDIO_MMD_AN, AT803X_MMD7_CLK25M, val);
+}
+
+static int at8031_pll_config(struct phy_device *phydev)
+{
+       struct at803x_priv *priv = phydev->priv;
+
+       /* The default after hardware reset is PLL OFF. After a soft reset, the
+        * values are retained.
+        */
+       if (priv->flags & AT803X_KEEP_PLL_ENABLED)
+               return at803x_debug_reg_mask(phydev, AT803X_DEBUG_REG_1F,
+                                            0, AT803X_DEBUG_PLL_ON);
+       else
+               return at803x_debug_reg_mask(phydev, AT803X_DEBUG_REG_1F,
+                                            AT803X_DEBUG_PLL_ON, 0);
+}
+
 static int at803x_config_init(struct phy_device *phydev)
 {
        int ret;
@@ -259,8 +542,20 @@ static int at803x_config_init(struct phy_device *phydev)
                ret = at803x_enable_tx_delay(phydev);
        else
                ret = at803x_disable_tx_delay(phydev);
+       if (ret < 0)
+               return ret;
 
-       return ret;
+       ret = at803x_clk_out_config(phydev);
+       if (ret < 0)
+               return ret;
+
+       if (at803x_match_phy_id(phydev, ATH8031_PHY_ID)) {
+               ret = at8031_pll_config(phydev);
+               if (ret < 0)
+                       return ret;
+       }
+
+       return 0;
 }
 
 static int at803x_ack_interrupt(struct phy_device *phydev)
@@ -413,6 +708,7 @@ static struct phy_driver at803x_driver[] = {
        .phy_id                 = ATH8035_PHY_ID,
        .name                   = "Atheros 8035 ethernet",
        .phy_id_mask            = AT803X_PHY_ID_MASK,
+       .probe                  = at803x_probe,
        .config_init            = at803x_config_init,
        .set_wol                = at803x_set_wol,
        .get_wol                = at803x_get_wol,
@@ -427,6 +723,7 @@ static struct phy_driver at803x_driver[] = {
        .phy_id                 = ATH8030_PHY_ID,
        .name                   = "Atheros 8030 ethernet",
        .phy_id_mask            = AT803X_PHY_ID_MASK,
+       .probe                  = at803x_probe,
        .config_init            = at803x_config_init,
        .link_change_notify     = at803x_link_change_notify,
        .set_wol                = at803x_set_wol,
@@ -441,6 +738,7 @@ static struct phy_driver at803x_driver[] = {
        .phy_id                 = ATH8031_PHY_ID,
        .name                   = "Atheros 8031 ethernet",
        .phy_id_mask            = AT803X_PHY_ID_MASK,
+       .probe                  = at803x_probe,
        .config_init            = at803x_config_init,
        .set_wol                = at803x_set_wol,
        .get_wol                = at803x_get_wol,
@@ -455,6 +753,7 @@ static struct phy_driver at803x_driver[] = {
        /* ATHEROS AR9331 */
        PHY_ID_MATCH_EXACT(ATH9331_PHY_ID),
        .name                   = "Atheros AR9331 built-in PHY",
+       .probe                  = at803x_probe,
        .config_init            = at803x_config_init,
        .suspend                = at803x_suspend,
        .resume                 = at803x_resume,