[U-Boot] [PATCH 2/2] net: phy: Add ability to program the ksz9031 skew values from the uboot env

Vince Bridgers
2015-02-09T14:44:33Z

This patch adds the ability for the Micrel ksz9031 PHY driver to detect
skew settings from the UBoot environment and program those values at PHY
initialization time. This is a first step towards setting these values from
the UBoot environment or the Micrel PHY devicetree once supported. The skew
values may be negative or positive, therefore depends on UBoot environment
support to read positive or negative values in units of picoseconds.

Signed-off-by: Vince Bridgers <vbridger@opensource.altera.com>
---
drivers/net/phy/micrel.c | 299 ++++++++++++++++++++++++++++++++++++++++++++++-
1 file changed, 298 insertions(+), 1 deletion(-)

diff --git a/drivers/net/phy/micrel.c b/drivers/net/phy/micrel.c
index 1815b29..d8819a8 100644
--- a/drivers/net/phy/micrel.c
+++ b/drivers/net/phy/micrel.c
@@ -211,6 +211,13 @@ static struct phy_driver ksz9021_driver = {
/* PHY Registers */
#define MII_KSZ9031_MMD_ACCES_CTRL 0x0d
#define MII_KSZ9031_MMD_REG_DATA 0x0e
+#define KSZ9031_PS_TO_REG 60
+
+/* Extended registers */
+#define MII_KSZ9031RN_CONTROL_PAD_SKEW 4
+#define MII_KSZ9031RN_RX_DATA_PAD_SKEW 5
+#define MII_KSZ9031RN_TX_DATA_PAD_SKEW 6
+#define MII_KSZ9031RN_CLK_PAD_SKEW 8

/* Accessors to extended registers*/
int ksz9031_phy_extended_write(struct phy_device *phydev,
@@ -256,13 +263,303 @@ static int ksz9031_phy_extwrite(struct phy_device *phydev, int addr,
MII_KSZ9031_MOD_DATA_POST_INC_RW, val);
};

+u8
+ksz9031_get_rxc_skew(struct phy_device *phydev)
+{
+ u16 reg;
+
+ reg = ksz9031_phy_extended_read(phydev, 2, 8,
+ MII_KSZ9031_MOD_DATA_NO_POST_INC);
+ reg &= 0x1f;
+ return (u8)reg;
+}
+
+u8
+ksz9031_get_txc_skew(struct phy_device *phydev)
+{
+ u16 reg;
+
+ reg = ksz9031_phy_extended_read(phydev, 2, 8,
+ MII_KSZ9031_MOD_DATA_NO_POST_INC);
+ reg = reg >> 5;
+ reg &= 0x1f;
+ return (u8)reg;
+}
+
+u8
+ksz9031_get_txen_skew(struct phy_device *phydev)
+{
+ u16 reg;
+
+ reg = ksz9031_phy_extended_read(phydev, 2, 4,
+ MII_KSZ9031_MOD_DATA_NO_POST_INC);
+ reg &= 0xf;
+ return (u8)reg;
+}
+
+u8
+ksz9031_get_rxdv_skew(struct phy_device *phydev)
+{
+ u16 reg;
+
+ reg = ksz9031_phy_extended_read(phydev, 2, 4,
+ MII_KSZ9031_MOD_DATA_NO_POST_INC);
+ reg = reg >> 4;
+ reg &= 0xf;
+ return (u8)reg;
+}
+
+u8
+ksz9031_get_rxdx_skew(struct phy_device *phydev, int index)
+{
+ u16 reg;
+
+ reg = ksz9031_phy_extended_read(phydev, 2, 5,
+ MII_KSZ9031_MOD_DATA_NO_POST_INC);
+ reg = reg >> (index*4);
+ reg &= 0xf;
+ return (u8)reg;
+}
+
+u8
+ksz9031_get_txdx_skew(struct phy_device *phydev, int index)
+{
+ u16 reg;
+
+ reg = ksz9031_phy_extended_read(phydev, 2, 6,
+ MII_KSZ9031_MOD_DATA_NO_POST_INC);
+ reg = reg >> (index*4);
+ reg &= 0xf;
+ return (u8)reg;
+}
+
+void
+ksz9031_set_rxc_skew(struct phy_device *phydev, u8 val)
+{
+ u16 reg;
+
+ reg = ksz9031_phy_extended_read(phydev, 2, 8,
+ MII_KSZ9031_MOD_DATA_NO_POST_INC);
+
+ reg = reg & 0xffe0;
+ reg |= (u16)val;
+
+ ksz9031_phy_extended_write(phydev, 2, 8,
+ MII_KSZ9031_MOD_DATA_NO_POST_INC, reg);
+}
+
+void
+ksz9031_set_txc_skew(struct phy_device *phydev, u8 val)
+{
+ u16 reg;
+
+ reg = ksz9031_phy_extended_read(phydev, 2, 8,
+ MII_KSZ9031_MOD_DATA_NO_POST_INC);
+ reg = reg & 0xfc1f;
+ reg |= ((u16)val) << 5;
+
+ ksz9031_phy_extended_write(phydev, 2, 8,
+ MII_KSZ9031_MOD_DATA_NO_POST_INC, reg);
+}
+
+void
+ksz9031_set_txen_skew(struct phy_device *phydev, u8 val)
+{
+ u16 reg;
+
+ reg = ksz9031_phy_extended_read(phydev, 2, 4,
+ MII_KSZ9031_MOD_DATA_NO_POST_INC);
+
+ reg = reg & 0xfff0;
+ reg |= ((u16)val);
+ ksz9031_phy_extended_write(phydev, 2, 4,
+ MII_KSZ9031_MOD_DATA_NO_POST_INC, reg);
+}
+
+void
+ksz9031_set_rxdv_skew(struct phy_device *phydev, u8 val)
+{
+ u16 reg;
+
+ reg = ksz9031_phy_extended_read(phydev, 2, 4,
+ MII_KSZ9031_MOD_DATA_NO_POST_INC);
+
+ reg = reg & 0xff0f;
+ reg |= ((u16)val) << 4;
+ ksz9031_phy_extended_write(phydev, 2, 4,
+ MII_KSZ9031_MOD_DATA_NO_POST_INC, reg);
+}
+
+void
+ksz9031_set_rxdx_skew(struct phy_device *phydev, int index, u8 val)
+{
+ u16 reg;
+ u16 mask;
+
+ mask = ~(0xf << (index * 4));
+
+ reg = ksz9031_phy_extended_read(phydev, 2, 5,
+ MII_KSZ9031_MOD_DATA_NO_POST_INC);
+ reg = reg & mask;
+ reg |= ((u16)val) << (4 * index);
+
+ ksz9031_phy_extended_write(phydev, 2, 5,
+ MII_KSZ9031_MOD_DATA_NO_POST_INC, reg);
+}
+
+void
+ksz9031_set_txdx_skew(struct phy_device *phydev, int index, u8 val)
+{
+ u16 reg;
+ u16 mask;
+
+ mask = ~(0xf << (index * 4));
+ reg = ksz9031_phy_extended_read(phydev, 2, 6,
+ MII_KSZ9031_MOD_DATA_NO_POST_INC);
+
+ reg = reg & mask;
+ reg |= ((u16)val) << (4 * index);
+
+ ksz9031_phy_extended_write(phydev, 2, 6,
+ MII_KSZ9031_MOD_DATA_NO_POST_INC, reg);
+}
+
+long ksz9031_range_check(const char *name, long val, long lower, long upper)
+{
+ long returnval = val;
+
+ if (val < lower) {
+ printf("Warning: %s value %ld lt %ld, clipping to %ld\n",
+ name, val, lower, lower);
+ returnval = lower;
+ }
+ if (val > upper) {
+ printf("Warning: %s value %ld gt %ld, clipping to %ld\n",
+ name, val, upper, upper);
+ returnval = upper;
+ }
+ return returnval;
+}
+
+#define KSZ9031_DEFAULT_RXC_SKEW_PS 0
+#define KSZ9031_DEFAULT_TXC_SKEW_PS 0
+#define KSZ9031_DEFAULT_TXEN_SKEW_PS 0
+#define KSZ9031_DEFAULT_RXDV_SKEW_PS 0
+
+#define KSZ9031_DEFAULT_RXD_SKEW_PS 0
+#define KSZ9031_DEFAULT_TXD_SKEW_PS 0
+int ksz9031_config_init(struct phy_device *phydev)
+{
+ long rxc_skew_ps; /* -900ps through 960ps */
+ long txc_skew_ps; /* -900ps through 960ps */
+ long txen_skew_ps; /* -420ps through 480ps */
+ long rxdv_skew_ps; /* -420ps through 480ps */
+ long rxdx_skew_ps[4]; /* -420ps through 480ps */
+ long txdx_skew_ps[4]; /* -420ps through 480ps */
+
+ u8 rxc_skew_regval; /* 5 bit range */
+ u8 txc_skew_regval; /* 5 bit range */
+ u8 txen_skew_regval; /* 4 bit range */
+ u8 rxdv_skew_regval; /* 4 bit range */
+ u8 rxdx_skew_regval[4]; /* 4 bit range */
+ u8 txdx_skew_regval[4]; /* 4 bit range */
+
+ long upper, lower;
+
+ int i;
+
+ char *rx_data_skews[4] = {
+ "rxd0-skew-ps", "rxd1-skew-ps",
+ "rxd2-skew-ps", "rxd3-skew-ps"
+ };
+ char *tx_data_skews[4] = {
+ "txd0-skew-ps", "txd1-skew-ps",
+ "txd2-skew-ps", "txd3-skew-ps"
+ };
+
+ /* Get values from the environment */
+
+ rxc_skew_ps = getenv_long("rxc-skew-ps", 10,
+ KSZ9031_DEFAULT_RXC_SKEW_PS);
+ txc_skew_ps = getenv_long("txc-skew-ps", 10,
+ KSZ9031_DEFAULT_TXC_SKEW_PS);
+ txen_skew_ps = getenv_long("txen-skew-ps", 10,
+ KSZ9031_DEFAULT_TXEN_SKEW_PS);
+ rxdv_skew_ps = getenv_long("rxdv-skew-ps", 10,
+ KSZ9031_DEFAULT_RXDV_SKEW_PS);
+
+ for (i = 0; i < 4; i++) {
+ rxdx_skew_ps[i] = getenv_long(rx_data_skews[i], 10,
+ KSZ9031_DEFAULT_RXD_SKEW_PS);
+ txdx_skew_ps[i] = getenv_long(tx_data_skews[i], 10,
+ KSZ9031_DEFAULT_TXD_SKEW_PS);
+ }
+
+ /* Range check, normalize values */
+ lower = -900;
+ upper = 960;
+ rxc_skew_ps = ksz9031_range_check("rxc-skew-ps", rxc_skew_ps,
+ lower, upper);
+ txc_skew_ps = ksz9031_range_check("txc-skew-ps", txc_skew_ps,
+ lower, upper);
+
+ lower = -420;
+ upper = 480;
+ txen_skew_ps = ksz9031_range_check("txen-skew-ps", txen_skew_ps,
+ lower, upper);
+ rxdv_skew_ps = ksz9031_range_check("rxdv-skew-ps", rxdv_skew_ps,
+ lower, upper);
+
+ for (i = 0; i < 4; i++) {
+ rxdx_skew_ps[i] = ksz9031_range_check("rxd[x]-skew-ps",
+ rxdx_skew_ps[i],
+ lower, upper);
+ txdx_skew_ps[i] = ksz9031_range_check("txd[x]-skew-ps",
+ txdx_skew_ps[i],
+ lower, upper);
+ }
+
+ /* Assign values to register variables */
+
+ rxc_skew_regval = (rxc_skew_ps + 900)/60;
+ txc_skew_regval = (txc_skew_ps + 900)/60;
+
+ txen_skew_regval = (txen_skew_ps + 420)/60;
+ rxdv_skew_regval = (rxdv_skew_ps + 420)/60;
+
+ for (i = 0; i < 4; i++) {
+ rxdx_skew_regval[i] = (rxdx_skew_ps[i] + 420)/60;
+ txdx_skew_regval[i] = (txdx_skew_ps[i] + 420)/60;
+ }
+
+ /* Write the values to the registers */
+ ksz9031_set_rxc_skew(phydev, rxc_skew_regval);
+ ksz9031_set_txc_skew(phydev, txc_skew_regval);
+ ksz9031_set_txen_skew(phydev, txen_skew_regval);
+ ksz9031_set_rxdv_skew(phydev, rxdv_skew_regval);
+
+ for (i = 0; i < 4; i++) {
+ ksz9031_set_rxdx_skew(phydev, i, rxdx_skew_regval[i]);
+ ksz9031_set_txdx_skew(phydev, i, txdx_skew_regval[i]);
+ }
+
+ /* Be sure to disable asym pause since if enabled, the ksz9031 is not
+ * able to establish a link in some cases. There is an errata for
+ * asym pause on the ksz9031.
+ */
+
+ genphy_config_aneg(phydev);
+ genphy_restart_aneg(phydev);
+
+ return 0;
+}

static struct phy_driver ksz9031_driver = {
.name = "Micrel ksz9031",
.uid = 0x221620,
.mask = 0xfffff0,
.features = PHY_GBIT_FEATURES,
- .config = &genphy_config,
+ .config = &ksz9031_config_init,
.startup = &ksz90xx_startup,
.shutdown = &genphy_shutdown,
.writeext = &ksz9031_phy_extwrite,
--
1.9.1