summaryrefslogtreecommitdiff
path: root/drivers/net/phy
diff options
context:
space:
mode:
authorFugang Duan <fugang.duan@nxp.com>2017-06-28 19:58:27 +0800
committerLeonard Crestez <leonard.crestez@nxp.com>2018-08-24 12:41:33 +0300
commit3746c2c7eae4e6c8f745ab9776ad6bf164aff60e (patch)
tree5d502e1431430517a114da8ff4f3a0c7b3cc7c23 /drivers/net/phy
parent25cdf463160ff36e450c1d0897aefdb007f6f345 (diff)
MLK-15309-01 net: phy: at803x: add EEE mode, 1.8V IO, led_act blinding workaround support
SmartEEE feature is enabled in default, add interface for user to disable the feature for IEEE1588 high accurate convergence. The phy support 1.8v RGMII VDDIO voltage, add interface for user to enable VDDIO 1.8v support. When phy/RJ45 power supply is not stable, LED_ACT may be busy on blinding, add sw workaround to fix the issue. Signed-off-by: Fugang Duan <fugang.duan@nxp.com> Reviewed-by: Pandy Gao <pandy.gao@nxp.com>
Diffstat (limited to 'drivers/net/phy')
-rw-r--r--drivers/net/phy/at803x.c103
-rw-r--r--drivers/net/phy/phy_device.c31
2 files changed, 122 insertions, 12 deletions
diff --git a/drivers/net/phy/at803x.c b/drivers/net/phy/at803x.c
index 3603eec7217f..3133a05c56a8 100644
--- a/drivers/net/phy/at803x.c
+++ b/drivers/net/phy/at803x.c
@@ -18,6 +18,7 @@
#include <linux/etherdevice.h>
#include <linux/of_gpio.h>
#include <linux/gpio/consumer.h>
+#include <linux/phy.h>
#define AT803X_INTR_ENABLE 0x12
#define AT803X_INTR_ENABLE_AUTONEG_ERR BIT(15)
@@ -39,11 +40,13 @@
#define AT803X_LOC_MAC_ADDR_0_15_OFFSET 0x804C
#define AT803X_LOC_MAC_ADDR_16_31_OFFSET 0x804B
#define AT803X_LOC_MAC_ADDR_32_47_OFFSET 0x804A
+#define AT803X_SMARTEEE_CTL3_OFFSET 0x805D
#define AT803X_MMD_ACCESS_CONTROL 0x0D
#define AT803X_MMD_ACCESS_CONTROL_DATA 0x0E
#define AT803X_FUNC_DATA 0x4003
#define AT803X_REG_CHIP_CONFIG 0x1f
#define AT803X_BT_BX_REG_SEL 0x8000
+#define AT803X_SMARTEEE_DISABLED_VAL 0x1000
#define AT803X_DEBUG_ADDR 0x1D
#define AT803X_DEBUG_DATA 0x1E
@@ -60,10 +63,21 @@
#define AT803X_DEBUG_REG_5 0x05
#define AT803X_DEBUG_TX_CLK_DLY_EN BIT(8)
+#define AT803X_DEBUG_REG_31 0x1f
+#define AT803X_VDDIO_1P8V_EN 0x8
+
#define ATH8030_PHY_ID 0x004dd076
#define ATH8031_PHY_ID 0x004dd074
#define ATH8035_PHY_ID 0x004dd072
+/* LED_ACT is busy on blinding even if no any frame transferring,
+ * it may cause by PHY/RJ45 power supply issue, the fixup flag just
+ * to do sw workaround for the issue.
+ */
+#define AT803X_LED_ACT_BLINDING_WORKAROUND (1 << 0)
+#define AT803X_EEE_FEATURE_DISABLE (1 << 1)
+#define AT803X_VDDIO_1P8V (1 << 2)
+
MODULE_DESCRIPTION("Atheros 803x PHY driver");
MODULE_AUTHOR("Matus Ujhelyi");
MODULE_LICENSE("GPL");
@@ -71,6 +85,7 @@ MODULE_LICENSE("GPL");
struct at803x_priv {
bool phy_reset:1;
struct gpio_desc *gpiod_reset;
+ u32 quirks;
};
struct at803x_context {
@@ -122,6 +137,39 @@ static inline int at803x_enable_tx_delay(struct phy_device *phydev)
AT803X_DEBUG_TX_CLK_DLY_EN);
}
+static inline int at803x_set_vddio_1p8v(struct phy_device *phydev)
+{
+ return at803x_debug_reg_mask(phydev, AT803X_DEBUG_REG_31, 0,
+ AT803X_VDDIO_1P8V_EN);
+}
+
+static int at803x_disable_eee(struct phy_device *phydev)
+{
+ int ret;
+
+ ret = phy_write(phydev, AT803X_MMD_ACCESS_CONTROL,
+ AT803X_DEVICE_ADDR);
+ if (ret < 0)
+ return ret;
+
+ ret = phy_write(phydev, AT803X_MMD_ACCESS_CONTROL_DATA,
+ AT803X_SMARTEEE_CTL3_OFFSET);
+ if (ret < 0)
+ return ret;
+
+ ret = phy_write(phydev, AT803X_MMD_ACCESS_CONTROL,
+ AT803X_FUNC_DATA);
+ if (ret < 0)
+ return ret;
+
+ ret = phy_write(phydev, AT803X_MMD_ACCESS_CONTROL_DATA,
+ AT803X_SMARTEEE_DISABLED_VAL);
+ if (ret < 0)
+ return ret;
+
+ return 0;
+}
+
/* save relevant PHY registers to private copy */
static void at803x_context_save(struct phy_device *phydev,
struct at803x_context *context)
@@ -259,6 +307,15 @@ static int at803x_probe(struct phy_device *phydev)
if (!priv)
return -ENOMEM;
+ if (of_property_read_bool(dev->of_node, "at803x,led-act-blind-workaround"))
+ priv->quirks |= AT803X_LED_ACT_BLINDING_WORKAROUND;
+
+ if (of_property_read_bool(dev->of_node, "at803x,eee-disabled"))
+ priv->quirks |= AT803X_EEE_FEATURE_DISABLE;
+
+ if (of_property_read_bool(dev->of_node, "at803x,vddio-1p8v"))
+ priv->quirks |= AT803X_VDDIO_1P8V;
+
if (phydev->drv->phy_id != ATH8030_PHY_ID)
goto does_not_require_reset_workaround;
@@ -277,6 +334,7 @@ does_not_require_reset_workaround:
static int at803x_config_init(struct phy_device *phydev)
{
int ret;
+ struct at803x_priv *priv = phydev->priv;
ret = genphy_config_init(phydev);
if (ret < 0)
@@ -296,6 +354,18 @@ static int at803x_config_init(struct phy_device *phydev)
return ret;
}
+ if (priv->quirks & AT803X_VDDIO_1P8V) {
+ ret = at803x_set_vddio_1p8v(phydev);
+ if (ret < 0)
+ return ret;
+ }
+
+ if (priv->quirks & AT803X_EEE_FEATURE_DISABLE) {
+ ret = at803x_disable_eee(phydev);
+ if (ret < 0)
+ return ret;
+ }
+
return 0;
}
@@ -363,6 +433,37 @@ static void at803x_link_change_notify(struct phy_device *phydev)
}
}
+int at803x_config_aneg(struct phy_device *phydev)
+{
+ struct at803x_priv *priv = phydev->priv;
+ int result;
+
+ /* Only restart aneg if we are advertising something different
+ * than we were before.
+ */
+ result = genphy_config_aneg_check(phydev);
+ if (result > 0) {
+ /* do autonegotiation here */
+ result = phy_read(phydev, MII_BMCR);
+ if (result < 0)
+ return result;
+
+ /* firstly power down here */
+ if (priv->quirks & AT803X_LED_ACT_BLINDING_WORKAROUND) {
+ phy_write(phydev, MII_BMCR, BMCR_PDOWN);
+ msleep(1);
+ }
+
+ result |= BMCR_ANENABLE | BMCR_ANRESTART;
+ /* Don't isolate the PHY if we're negotiating */
+ result &= ~BMCR_ISOLATE;
+
+ result = phy_write(phydev, MII_BMCR, result);
+ }
+
+ return result;
+}
+
static int at803x_aneg_done(struct phy_device *phydev)
{
int ccr;
@@ -442,7 +543,7 @@ static struct phy_driver at803x_driver[] = {
.resume = at803x_resume,
.features = PHY_GBIT_FEATURES,
.flags = PHY_HAS_INTERRUPT,
- .config_aneg = genphy_config_aneg,
+ .config_aneg = at803x_config_aneg,
.read_status = genphy_read_status,
.aneg_done = at803x_aneg_done,
.ack_interrupt = &at803x_ack_interrupt,
diff --git a/drivers/net/phy/phy_device.c b/drivers/net/phy/phy_device.c
index df8e5e5ac1d2..ec656d91f14b 100644
--- a/drivers/net/phy/phy_device.c
+++ b/drivers/net/phy/phy_device.c
@@ -1241,15 +1241,7 @@ int genphy_restart_aneg(struct phy_device *phydev)
}
EXPORT_SYMBOL(genphy_restart_aneg);
-/**
- * genphy_config_aneg - restart auto-negotiation or write BMCR
- * @phydev: target phy_device struct
- *
- * Description: If auto-negotiation is enabled, we configure the
- * advertising, and then restart auto-negotiation. If it is not
- * enabled, then we write the BMCR.
- */
-int genphy_config_aneg(struct phy_device *phydev)
+int genphy_config_aneg_check(struct phy_device *phydev)
{
int err, changed;
@@ -1277,11 +1269,28 @@ int genphy_config_aneg(struct phy_device *phydev)
changed = 1; /* do restart aneg */
}
+ return changed;
+}
+EXPORT_SYMBOL(genphy_config_aneg_check);
+
+/**
+ * genphy_config_aneg - restart auto-negotiation or write BMCR
+ * @phydev: target phy_device struct
+ *
+ * Description: If auto-negotiation is enabled, we configure the
+ * advertising, and then restart auto-negotiation. If it is not
+ * enabled, then we write the BMCR.
+ */
+int genphy_config_aneg(struct phy_device *phydev)
+{
+ int result;
+
/* Only restart aneg if we are advertising something different
* than we were before.
*/
- if (changed > 0)
- return genphy_restart_aneg(phydev);
+ result = genphy_config_aneg_check(phydev);
+ if (result > 0)
+ result = genphy_restart_aneg(phydev);
return 0;
}