mirror of
https://github.com/openwrt/openwrt.git
synced 2024-12-23 15:32:33 +00:00
generic: 6.1: backport QCA807x PHY patches
Backport QCA807x PHY patches merged upstream that introduce the new concept of PHY package. Also add in generic config the new Kconfig CONFIG_QCA807X_PHY. All affected patch automatically refreshed with make target/linux/refresh. Signed-off-by: Christian Marangi <ansuelsmth@gmail.com>
This commit is contained in:
parent
2253645411
commit
16364e4100
@ -26,7 +26,7 @@ Signed-off-by: Jonathan Bell <jonathan@raspberrypi.org>
|
||||
return;
|
||||
|
||||
val = bcm_phy_read_shadow(phydev, BCM54XX_SHD_SCR3);
|
||||
@@ -905,7 +906,7 @@ static struct phy_driver broadcom_driver
|
||||
@@ -906,7 +907,7 @@ static struct phy_driver broadcom_driver
|
||||
.link_change_notify = bcm54xx_link_change_notify,
|
||||
}, {
|
||||
.phy_id = PHY_ID_BCM54210E,
|
||||
@ -35,7 +35,7 @@ Signed-off-by: Jonathan Bell <jonathan@raspberrypi.org>
|
||||
.name = "Broadcom BCM54210E",
|
||||
/* PHY_GBIT_FEATURES */
|
||||
.get_sset_count = bcm_phy_get_sset_count,
|
||||
@@ -919,6 +920,13 @@ static struct phy_driver broadcom_driver
|
||||
@@ -920,6 +921,13 @@ static struct phy_driver broadcom_driver
|
||||
.suspend = bcm54xx_suspend,
|
||||
.resume = bcm54xx_resume,
|
||||
}, {
|
||||
@ -49,7 +49,7 @@ Signed-off-by: Jonathan Bell <jonathan@raspberrypi.org>
|
||||
.phy_id = PHY_ID_BCM5461,
|
||||
.phy_id_mask = 0xfffffff0,
|
||||
.name = "Broadcom BCM5461",
|
||||
@@ -1155,7 +1163,8 @@ module_phy_driver(broadcom_drivers);
|
||||
@@ -1156,7 +1164,8 @@ module_phy_driver(broadcom_drivers);
|
||||
static struct mdio_device_id __maybe_unused broadcom_tbl[] = {
|
||||
{ PHY_ID_BCM5411, 0xfffffff0 },
|
||||
{ PHY_ID_BCM5421, 0xfffffff0 },
|
||||
|
@ -16,7 +16,7 @@ Signed-off-by: Jonathan Lemon <jonathan.lemon@gmail.com>
|
||||
|
||||
--- a/drivers/net/phy/broadcom.c
|
||||
+++ b/drivers/net/phy/broadcom.c
|
||||
@@ -932,8 +932,14 @@ static struct phy_driver broadcom_driver
|
||||
@@ -933,8 +933,14 @@ static struct phy_driver broadcom_driver
|
||||
.phy_id_mask = 0xffffffff,
|
||||
.name = "Broadcom BCM54213PE",
|
||||
/* PHY_GBIT_FEATURES */
|
||||
|
@ -0,0 +1,211 @@
|
||||
From 385ef48f468696d6d172eb367656a3466fa0408d Mon Sep 17 00:00:00 2001
|
||||
From: Christian Marangi <ansuelsmth@gmail.com>
|
||||
Date: Tue, 6 Feb 2024 18:31:05 +0100
|
||||
Subject: [PATCH 02/10] net: phy: add support for scanning PHY in PHY packages
|
||||
nodes
|
||||
|
||||
Add support for scanning PHY in PHY package nodes. PHY packages nodes
|
||||
are just container for actual PHY on the MDIO bus.
|
||||
|
||||
Their PHY address defined in the PHY package node are absolute and
|
||||
reflect the address on the MDIO bus.
|
||||
|
||||
mdio_bus.c and of_mdio.c is updated to now support and parse also
|
||||
PHY package subnode by checking if the node name match
|
||||
"ethernet-phy-package".
|
||||
|
||||
As PHY package reg is mandatory and each PHY in the PHY package must
|
||||
have a reg, every invalid PHY Package node is ignored and will be
|
||||
skipped by the autoscan fallback.
|
||||
|
||||
Signed-off-by: Christian Marangi <ansuelsmth@gmail.com>
|
||||
Reviewed-by: Andrew Lunn <andrew@lunn.ch>
|
||||
Signed-off-by: David S. Miller <davem@davemloft.net>
|
||||
---
|
||||
drivers/net/mdio/of_mdio.c | 79 +++++++++++++++++++++++++++-----------
|
||||
drivers/net/phy/mdio_bus.c | 44 +++++++++++++++++----
|
||||
2 files changed, 92 insertions(+), 31 deletions(-)
|
||||
|
||||
--- a/drivers/net/mdio/of_mdio.c
|
||||
+++ b/drivers/net/mdio/of_mdio.c
|
||||
@@ -138,6 +138,53 @@ bool of_mdiobus_child_is_phy(struct devi
|
||||
}
|
||||
EXPORT_SYMBOL(of_mdiobus_child_is_phy);
|
||||
|
||||
+static int __of_mdiobus_parse_phys(struct mii_bus *mdio, struct device_node *np,
|
||||
+ bool *scanphys)
|
||||
+{
|
||||
+ struct device_node *child;
|
||||
+ int addr, rc = 0;
|
||||
+
|
||||
+ /* Loop over the child nodes and register a phy_device for each phy */
|
||||
+ for_each_available_child_of_node(np, child) {
|
||||
+ if (of_node_name_eq(child, "ethernet-phy-package")) {
|
||||
+ /* Ignore invalid ethernet-phy-package node */
|
||||
+ if (!of_find_property(child, "reg", NULL))
|
||||
+ continue;
|
||||
+
|
||||
+ rc = __of_mdiobus_parse_phys(mdio, child, NULL);
|
||||
+ if (rc && rc != -ENODEV)
|
||||
+ goto exit;
|
||||
+
|
||||
+ continue;
|
||||
+ }
|
||||
+
|
||||
+ addr = of_mdio_parse_addr(&mdio->dev, child);
|
||||
+ if (addr < 0) {
|
||||
+ /* Skip scanning for invalid ethernet-phy-package node */
|
||||
+ if (scanphys)
|
||||
+ *scanphys = true;
|
||||
+ continue;
|
||||
+ }
|
||||
+
|
||||
+ if (of_mdiobus_child_is_phy(child))
|
||||
+ rc = of_mdiobus_register_phy(mdio, child, addr);
|
||||
+ else
|
||||
+ rc = of_mdiobus_register_device(mdio, child, addr);
|
||||
+
|
||||
+ if (rc == -ENODEV)
|
||||
+ dev_err(&mdio->dev,
|
||||
+ "MDIO device at address %d is missing.\n",
|
||||
+ addr);
|
||||
+ else if (rc)
|
||||
+ goto exit;
|
||||
+ }
|
||||
+
|
||||
+ return 0;
|
||||
+exit:
|
||||
+ of_node_put(child);
|
||||
+ return rc;
|
||||
+}
|
||||
+
|
||||
/**
|
||||
* __of_mdiobus_register - Register mii_bus and create PHYs from the device tree
|
||||
* @mdio: pointer to mii_bus structure
|
||||
@@ -179,33 +226,18 @@ int __of_mdiobus_register(struct mii_bus
|
||||
return rc;
|
||||
|
||||
/* Loop over the child nodes and register a phy_device for each phy */
|
||||
- for_each_available_child_of_node(np, child) {
|
||||
- addr = of_mdio_parse_addr(&mdio->dev, child);
|
||||
- if (addr < 0) {
|
||||
- scanphys = true;
|
||||
- continue;
|
||||
- }
|
||||
-
|
||||
- if (of_mdiobus_child_is_phy(child))
|
||||
- rc = of_mdiobus_register_phy(mdio, child, addr);
|
||||
- else
|
||||
- rc = of_mdiobus_register_device(mdio, child, addr);
|
||||
-
|
||||
- if (rc == -ENODEV)
|
||||
- dev_err(&mdio->dev,
|
||||
- "MDIO device at address %d is missing.\n",
|
||||
- addr);
|
||||
- else if (rc)
|
||||
- goto unregister;
|
||||
- }
|
||||
+ rc = __of_mdiobus_parse_phys(mdio, np, &scanphys);
|
||||
+ if (rc)
|
||||
+ goto unregister;
|
||||
|
||||
if (!scanphys)
|
||||
return 0;
|
||||
|
||||
/* auto scan for PHYs with empty reg property */
|
||||
for_each_available_child_of_node(np, child) {
|
||||
- /* Skip PHYs with reg property set */
|
||||
- if (of_find_property(child, "reg", NULL))
|
||||
+ /* Skip PHYs with reg property set or ethernet-phy-package node */
|
||||
+ if (of_find_property(child, "reg", NULL) ||
|
||||
+ of_node_name_eq(child, "ethernet-phy-package"))
|
||||
continue;
|
||||
|
||||
for (addr = 0; addr < PHY_MAX_ADDR; addr++) {
|
||||
@@ -226,15 +258,16 @@ int __of_mdiobus_register(struct mii_bus
|
||||
if (!rc)
|
||||
break;
|
||||
if (rc != -ENODEV)
|
||||
- goto unregister;
|
||||
+ goto put_unregister;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
-unregister:
|
||||
+put_unregister:
|
||||
of_node_put(child);
|
||||
+unregister:
|
||||
mdiobus_unregister(mdio);
|
||||
return rc;
|
||||
}
|
||||
--- a/drivers/net/phy/mdio_bus.c
|
||||
+++ b/drivers/net/phy/mdio_bus.c
|
||||
@@ -448,19 +448,34 @@ EXPORT_SYMBOL(of_mdio_find_bus);
|
||||
* found, set the of_node pointer for the mdio device. This allows
|
||||
* auto-probed phy devices to be supplied with information passed in
|
||||
* via DT.
|
||||
+ * If a PHY package is found, PHY is searched also there.
|
||||
*/
|
||||
-static void of_mdiobus_link_mdiodev(struct mii_bus *bus,
|
||||
- struct mdio_device *mdiodev)
|
||||
+static int of_mdiobus_find_phy(struct device *dev, struct mdio_device *mdiodev,
|
||||
+ struct device_node *np)
|
||||
{
|
||||
- struct device *dev = &mdiodev->dev;
|
||||
struct device_node *child;
|
||||
|
||||
- if (dev->of_node || !bus->dev.of_node)
|
||||
- return;
|
||||
-
|
||||
- for_each_available_child_of_node(bus->dev.of_node, child) {
|
||||
+ for_each_available_child_of_node(np, child) {
|
||||
int addr;
|
||||
|
||||
+ if (of_node_name_eq(child, "ethernet-phy-package")) {
|
||||
+ /* Validate PHY package reg presence */
|
||||
+ if (!of_find_property(child, "reg", NULL)) {
|
||||
+ of_node_put(child);
|
||||
+ return -EINVAL;
|
||||
+ }
|
||||
+
|
||||
+ if (!of_mdiobus_find_phy(dev, mdiodev, child)) {
|
||||
+ /* The refcount for the PHY package will be
|
||||
+ * incremented later when PHY join the Package.
|
||||
+ */
|
||||
+ of_node_put(child);
|
||||
+ return 0;
|
||||
+ }
|
||||
+
|
||||
+ continue;
|
||||
+ }
|
||||
+
|
||||
addr = of_mdio_parse_addr(dev, child);
|
||||
if (addr < 0)
|
||||
continue;
|
||||
@@ -470,9 +485,22 @@ static void of_mdiobus_link_mdiodev(stru
|
||||
/* The refcount on "child" is passed to the mdio
|
||||
* device. Do _not_ use of_node_put(child) here.
|
||||
*/
|
||||
- return;
|
||||
+ return 0;
|
||||
}
|
||||
}
|
||||
+
|
||||
+ return -ENODEV;
|
||||
+}
|
||||
+
|
||||
+static void of_mdiobus_link_mdiodev(struct mii_bus *bus,
|
||||
+ struct mdio_device *mdiodev)
|
||||
+{
|
||||
+ struct device *dev = &mdiodev->dev;
|
||||
+
|
||||
+ if (dev->of_node || !bus->dev.of_node)
|
||||
+ return;
|
||||
+
|
||||
+ of_mdiobus_find_phy(dev, mdiodev, bus->dev.of_node);
|
||||
}
|
||||
#else /* !IS_ENABLED(CONFIG_OF_MDIO) */
|
||||
static inline void of_mdiobus_link_mdiodev(struct mii_bus *mdio,
|
@ -0,0 +1,185 @@
|
||||
From 471e8fd3afcef5a9f9089f0bd21965ad9ba35c91 Mon Sep 17 00:00:00 2001
|
||||
From: Christian Marangi <ansuelsmth@gmail.com>
|
||||
Date: Tue, 6 Feb 2024 18:31:06 +0100
|
||||
Subject: [PATCH 03/10] net: phy: add devm/of_phy_package_join helper
|
||||
|
||||
Add devm/of_phy_package_join helper to join PHYs in a PHY package. These
|
||||
are variant of the manual phy_package_join with the difference that
|
||||
these will use DT nodes to derive the base_addr instead of manually
|
||||
passing an hardcoded value.
|
||||
|
||||
An additional value is added in phy_package_shared, "np" to reference
|
||||
the PHY package node pointer in specific PHY driver probe_once and
|
||||
config_init_once functions to make use of additional specific properties
|
||||
defined in the PHY package node in DT.
|
||||
|
||||
The np value is filled only with of_phy_package_join if a valid PHY
|
||||
package node is found. A valid PHY package node must have the node name
|
||||
set to "ethernet-phy-package".
|
||||
|
||||
Signed-off-by: Christian Marangi <ansuelsmth@gmail.com>
|
||||
Reviewed-by: Andrew Lunn <andrew@lunn.ch>
|
||||
Signed-off-by: David S. Miller <davem@davemloft.net>
|
||||
---
|
||||
drivers/net/phy/phy_device.c | 96 ++++++++++++++++++++++++++++++++++++
|
||||
include/linux/phy.h | 6 +++
|
||||
2 files changed, 102 insertions(+)
|
||||
|
||||
--- a/drivers/net/phy/phy_device.c
|
||||
+++ b/drivers/net/phy/phy_device.c
|
||||
@@ -1650,6 +1650,7 @@ int phy_package_join(struct phy_device *
|
||||
shared->priv_size = priv_size;
|
||||
}
|
||||
shared->base_addr = base_addr;
|
||||
+ shared->np = NULL;
|
||||
refcount_set(&shared->refcnt, 1);
|
||||
bus->shared[base_addr] = shared;
|
||||
} else {
|
||||
@@ -1673,6 +1674,63 @@ err_unlock:
|
||||
EXPORT_SYMBOL_GPL(phy_package_join);
|
||||
|
||||
/**
|
||||
+ * of_phy_package_join - join a common PHY group in PHY package
|
||||
+ * @phydev: target phy_device struct
|
||||
+ * @priv_size: if non-zero allocate this amount of bytes for private data
|
||||
+ *
|
||||
+ * This is a variant of phy_package_join for PHY package defined in DT.
|
||||
+ *
|
||||
+ * The parent node of the @phydev is checked as a valid PHY package node
|
||||
+ * structure (by matching the node name "ethernet-phy-package") and the
|
||||
+ * base_addr for the PHY package is passed to phy_package_join.
|
||||
+ *
|
||||
+ * With this configuration the shared struct will also have the np value
|
||||
+ * filled to use additional DT defined properties in PHY specific
|
||||
+ * probe_once and config_init_once PHY package OPs.
|
||||
+ *
|
||||
+ * Returns < 0 on error, 0 on success. Esp. calling phy_package_join()
|
||||
+ * with the same cookie but a different priv_size is an error. Or a parent
|
||||
+ * node is not detected or is not valid or doesn't match the expected node
|
||||
+ * name for PHY package.
|
||||
+ */
|
||||
+int of_phy_package_join(struct phy_device *phydev, size_t priv_size)
|
||||
+{
|
||||
+ struct device_node *node = phydev->mdio.dev.of_node;
|
||||
+ struct device_node *package_node;
|
||||
+ u32 base_addr;
|
||||
+ int ret;
|
||||
+
|
||||
+ if (!node)
|
||||
+ return -EINVAL;
|
||||
+
|
||||
+ package_node = of_get_parent(node);
|
||||
+ if (!package_node)
|
||||
+ return -EINVAL;
|
||||
+
|
||||
+ if (!of_node_name_eq(package_node, "ethernet-phy-package")) {
|
||||
+ ret = -EINVAL;
|
||||
+ goto exit;
|
||||
+ }
|
||||
+
|
||||
+ if (of_property_read_u32(package_node, "reg", &base_addr)) {
|
||||
+ ret = -EINVAL;
|
||||
+ goto exit;
|
||||
+ }
|
||||
+
|
||||
+ ret = phy_package_join(phydev, base_addr, priv_size);
|
||||
+ if (ret)
|
||||
+ goto exit;
|
||||
+
|
||||
+ phydev->shared->np = package_node;
|
||||
+
|
||||
+ return 0;
|
||||
+exit:
|
||||
+ of_node_put(package_node);
|
||||
+ return ret;
|
||||
+}
|
||||
+EXPORT_SYMBOL_GPL(of_phy_package_join);
|
||||
+
|
||||
+/**
|
||||
* phy_package_leave - leave a common PHY group
|
||||
* @phydev: target phy_device struct
|
||||
*
|
||||
@@ -1688,6 +1746,10 @@ void phy_package_leave(struct phy_device
|
||||
if (!shared)
|
||||
return;
|
||||
|
||||
+ /* Decrease the node refcount on leave if present */
|
||||
+ if (shared->np)
|
||||
+ of_node_put(shared->np);
|
||||
+
|
||||
if (refcount_dec_and_mutex_lock(&shared->refcnt, &bus->shared_lock)) {
|
||||
bus->shared[shared->base_addr] = NULL;
|
||||
mutex_unlock(&bus->shared_lock);
|
||||
@@ -1741,6 +1803,40 @@ int devm_phy_package_join(struct device
|
||||
EXPORT_SYMBOL_GPL(devm_phy_package_join);
|
||||
|
||||
/**
|
||||
+ * devm_of_phy_package_join - resource managed of_phy_package_join()
|
||||
+ * @dev: device that is registering this PHY package
|
||||
+ * @phydev: target phy_device struct
|
||||
+ * @priv_size: if non-zero allocate this amount of bytes for private data
|
||||
+ *
|
||||
+ * Managed of_phy_package_join(). Shared storage fetched by this function,
|
||||
+ * phy_package_leave() is automatically called on driver detach. See
|
||||
+ * of_phy_package_join() for more information.
|
||||
+ */
|
||||
+int devm_of_phy_package_join(struct device *dev, struct phy_device *phydev,
|
||||
+ size_t priv_size)
|
||||
+{
|
||||
+ struct phy_device **ptr;
|
||||
+ int ret;
|
||||
+
|
||||
+ ptr = devres_alloc(devm_phy_package_leave, sizeof(*ptr),
|
||||
+ GFP_KERNEL);
|
||||
+ if (!ptr)
|
||||
+ return -ENOMEM;
|
||||
+
|
||||
+ ret = of_phy_package_join(phydev, priv_size);
|
||||
+
|
||||
+ if (!ret) {
|
||||
+ *ptr = phydev;
|
||||
+ devres_add(dev, ptr);
|
||||
+ } else {
|
||||
+ devres_free(ptr);
|
||||
+ }
|
||||
+
|
||||
+ return ret;
|
||||
+}
|
||||
+EXPORT_SYMBOL_GPL(devm_of_phy_package_join);
|
||||
+
|
||||
+/**
|
||||
* phy_detach - detach a PHY device from its network device
|
||||
* @phydev: target phy_device struct
|
||||
*
|
||||
--- a/include/linux/phy.h
|
||||
+++ b/include/linux/phy.h
|
||||
@@ -321,6 +321,7 @@ struct mdio_bus_stats {
|
||||
* struct phy_package_shared - Shared information in PHY packages
|
||||
* @base_addr: Base PHY address of PHY package used to combine PHYs
|
||||
* in one package and for offset calculation of phy_package_read/write
|
||||
+ * @np: Pointer to the Device Node if PHY package defined in DT
|
||||
* @refcnt: Number of PHYs connected to this shared data
|
||||
* @flags: Initialization of PHY package
|
||||
* @priv_size: Size of the shared private data @priv
|
||||
@@ -332,6 +333,8 @@ struct mdio_bus_stats {
|
||||
*/
|
||||
struct phy_package_shared {
|
||||
u8 base_addr;
|
||||
+ /* With PHY package defined in DT this points to the PHY package node */
|
||||
+ struct device_node *np;
|
||||
refcount_t refcnt;
|
||||
unsigned long flags;
|
||||
size_t priv_size;
|
||||
@@ -1765,9 +1768,12 @@ int phy_ethtool_set_link_ksettings(struc
|
||||
const struct ethtool_link_ksettings *cmd);
|
||||
int phy_ethtool_nway_reset(struct net_device *ndev);
|
||||
int phy_package_join(struct phy_device *phydev, int base_addr, size_t priv_size);
|
||||
+int of_phy_package_join(struct phy_device *phydev, size_t priv_size);
|
||||
void phy_package_leave(struct phy_device *phydev);
|
||||
int devm_phy_package_join(struct device *dev, struct phy_device *phydev,
|
||||
int base_addr, size_t priv_size);
|
||||
+int devm_of_phy_package_join(struct device *dev, struct phy_device *phydev,
|
||||
+ size_t priv_size);
|
||||
|
||||
#if IS_ENABLED(CONFIG_PHYLIB)
|
||||
int __init mdio_bus_init(void);
|
@ -0,0 +1,583 @@
|
||||
From 737eb75a815f9c08dcbb6631db57f4f4b0540a5b Mon Sep 17 00:00:00 2001
|
||||
From: Christian Marangi <ansuelsmth@gmail.com>
|
||||
Date: Tue, 6 Feb 2024 18:31:07 +0100
|
||||
Subject: [PATCH 04/10] net: phy: qcom: move more function to shared library
|
||||
|
||||
Move more function to shared library in preparation for introduction of
|
||||
new PHY Family qca807x that will make use of both functions from at803x
|
||||
and qca808x as it's a transition PHY with some implementation of at803x
|
||||
and some from the new qca808x.
|
||||
|
||||
Signed-off-by: Christian Marangi <ansuelsmth@gmail.com>
|
||||
Reviewed-by: Andrew Lunn <andrew@lunn.ch>
|
||||
Signed-off-by: David S. Miller <davem@davemloft.net>
|
||||
---
|
||||
drivers/net/phy/qcom/at803x.c | 35 -----
|
||||
drivers/net/phy/qcom/qca808x.c | 205 ----------------------------
|
||||
drivers/net/phy/qcom/qcom-phy-lib.c | 193 ++++++++++++++++++++++++++
|
||||
drivers/net/phy/qcom/qcom.h | 51 +++++++
|
||||
4 files changed, 244 insertions(+), 240 deletions(-)
|
||||
|
||||
--- a/drivers/net/phy/qcom/at803x.c
|
||||
+++ b/drivers/net/phy/qcom/at803x.c
|
||||
@@ -504,41 +504,6 @@ static void at803x_link_change_notify(st
|
||||
}
|
||||
}
|
||||
|
||||
-static int at803x_read_status(struct phy_device *phydev)
|
||||
-{
|
||||
- struct at803x_ss_mask ss_mask = { 0 };
|
||||
- int err, old_link = phydev->link;
|
||||
-
|
||||
- /* Update the link, but return if there was an error */
|
||||
- err = genphy_update_link(phydev);
|
||||
- if (err)
|
||||
- return err;
|
||||
-
|
||||
- /* why bother the PHY if nothing can have changed */
|
||||
- if (phydev->autoneg == AUTONEG_ENABLE && old_link && phydev->link)
|
||||
- return 0;
|
||||
-
|
||||
- phydev->speed = SPEED_UNKNOWN;
|
||||
- phydev->duplex = DUPLEX_UNKNOWN;
|
||||
- phydev->pause = 0;
|
||||
- phydev->asym_pause = 0;
|
||||
-
|
||||
- err = genphy_read_lpa(phydev);
|
||||
- if (err < 0)
|
||||
- return err;
|
||||
-
|
||||
- ss_mask.speed_mask = AT803X_SS_SPEED_MASK;
|
||||
- ss_mask.speed_shift = __bf_shf(AT803X_SS_SPEED_MASK);
|
||||
- err = at803x_read_specific_status(phydev, ss_mask);
|
||||
- if (err < 0)
|
||||
- return err;
|
||||
-
|
||||
- if (phydev->autoneg == AUTONEG_ENABLE && phydev->autoneg_complete)
|
||||
- phy_resolve_aneg_pause(phydev);
|
||||
-
|
||||
- return 0;
|
||||
-}
|
||||
-
|
||||
static int at803x_config_aneg(struct phy_device *phydev)
|
||||
{
|
||||
struct at803x_priv *priv = phydev->priv;
|
||||
--- a/drivers/net/phy/qcom/qca808x.c
|
||||
+++ b/drivers/net/phy/qcom/qca808x.c
|
||||
@@ -2,7 +2,6 @@
|
||||
|
||||
#include <linux/phy.h>
|
||||
#include <linux/module.h>
|
||||
-#include <linux/ethtool_netlink.h>
|
||||
|
||||
#include "qcom.h"
|
||||
|
||||
@@ -63,55 +62,6 @@
|
||||
#define QCA808X_DBG_AN_TEST 0xb
|
||||
#define QCA808X_HIBERNATION_EN BIT(15)
|
||||
|
||||
-#define QCA808X_CDT_ENABLE_TEST BIT(15)
|
||||
-#define QCA808X_CDT_INTER_CHECK_DIS BIT(13)
|
||||
-#define QCA808X_CDT_STATUS BIT(11)
|
||||
-#define QCA808X_CDT_LENGTH_UNIT BIT(10)
|
||||
-
|
||||
-#define QCA808X_MMD3_CDT_STATUS 0x8064
|
||||
-#define QCA808X_MMD3_CDT_DIAG_PAIR_A 0x8065
|
||||
-#define QCA808X_MMD3_CDT_DIAG_PAIR_B 0x8066
|
||||
-#define QCA808X_MMD3_CDT_DIAG_PAIR_C 0x8067
|
||||
-#define QCA808X_MMD3_CDT_DIAG_PAIR_D 0x8068
|
||||
-#define QCA808X_CDT_DIAG_LENGTH_SAME_SHORT GENMASK(15, 8)
|
||||
-#define QCA808X_CDT_DIAG_LENGTH_CROSS_SHORT GENMASK(7, 0)
|
||||
-
|
||||
-#define QCA808X_CDT_CODE_PAIR_A GENMASK(15, 12)
|
||||
-#define QCA808X_CDT_CODE_PAIR_B GENMASK(11, 8)
|
||||
-#define QCA808X_CDT_CODE_PAIR_C GENMASK(7, 4)
|
||||
-#define QCA808X_CDT_CODE_PAIR_D GENMASK(3, 0)
|
||||
-
|
||||
-#define QCA808X_CDT_STATUS_STAT_TYPE GENMASK(1, 0)
|
||||
-#define QCA808X_CDT_STATUS_STAT_FAIL FIELD_PREP_CONST(QCA808X_CDT_STATUS_STAT_TYPE, 0)
|
||||
-#define QCA808X_CDT_STATUS_STAT_NORMAL FIELD_PREP_CONST(QCA808X_CDT_STATUS_STAT_TYPE, 1)
|
||||
-#define QCA808X_CDT_STATUS_STAT_SAME_OPEN FIELD_PREP_CONST(QCA808X_CDT_STATUS_STAT_TYPE, 2)
|
||||
-#define QCA808X_CDT_STATUS_STAT_SAME_SHORT FIELD_PREP_CONST(QCA808X_CDT_STATUS_STAT_TYPE, 3)
|
||||
-
|
||||
-#define QCA808X_CDT_STATUS_STAT_MDI GENMASK(3, 2)
|
||||
-#define QCA808X_CDT_STATUS_STAT_MDI1 FIELD_PREP_CONST(QCA808X_CDT_STATUS_STAT_MDI, 1)
|
||||
-#define QCA808X_CDT_STATUS_STAT_MDI2 FIELD_PREP_CONST(QCA808X_CDT_STATUS_STAT_MDI, 2)
|
||||
-#define QCA808X_CDT_STATUS_STAT_MDI3 FIELD_PREP_CONST(QCA808X_CDT_STATUS_STAT_MDI, 3)
|
||||
-
|
||||
-/* NORMAL are MDI with type set to 0 */
|
||||
-#define QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI1_SAME_NORMAL QCA808X_CDT_STATUS_STAT_MDI1
|
||||
-#define QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI1_SAME_OPEN (QCA808X_CDT_STATUS_STAT_SAME_OPEN |\
|
||||
- QCA808X_CDT_STATUS_STAT_MDI1)
|
||||
-#define QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI1_SAME_SHORT (QCA808X_CDT_STATUS_STAT_SAME_SHORT |\
|
||||
- QCA808X_CDT_STATUS_STAT_MDI1)
|
||||
-#define QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI2_SAME_NORMAL QCA808X_CDT_STATUS_STAT_MDI2
|
||||
-#define QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI2_SAME_OPEN (QCA808X_CDT_STATUS_STAT_SAME_OPEN |\
|
||||
- QCA808X_CDT_STATUS_STAT_MDI2)
|
||||
-#define QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI2_SAME_SHORT (QCA808X_CDT_STATUS_STAT_SAME_SHORT |\
|
||||
- QCA808X_CDT_STATUS_STAT_MDI2)
|
||||
-#define QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI3_SAME_NORMAL QCA808X_CDT_STATUS_STAT_MDI3
|
||||
-#define QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI3_SAME_OPEN (QCA808X_CDT_STATUS_STAT_SAME_OPEN |\
|
||||
- QCA808X_CDT_STATUS_STAT_MDI3)
|
||||
-#define QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI3_SAME_SHORT (QCA808X_CDT_STATUS_STAT_SAME_SHORT |\
|
||||
- QCA808X_CDT_STATUS_STAT_MDI3)
|
||||
-
|
||||
-/* Added for reference of existence but should be handled by wait_for_completion already */
|
||||
-#define QCA808X_CDT_STATUS_STAT_BUSY (BIT(1) | BIT(3))
|
||||
-
|
||||
#define QCA808X_MMD7_LED_GLOBAL 0x8073
|
||||
#define QCA808X_LED_BLINK_1 GENMASK(11, 6)
|
||||
#define QCA808X_LED_BLINK_2 GENMASK(5, 0)
|
||||
@@ -406,86 +356,6 @@ static int qca808x_soft_reset(struct phy
|
||||
return ret;
|
||||
}
|
||||
|
||||
-static bool qca808x_cdt_fault_length_valid(int cdt_code)
|
||||
-{
|
||||
- switch (cdt_code) {
|
||||
- case QCA808X_CDT_STATUS_STAT_SAME_SHORT:
|
||||
- case QCA808X_CDT_STATUS_STAT_SAME_OPEN:
|
||||
- case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI1_SAME_NORMAL:
|
||||
- case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI1_SAME_OPEN:
|
||||
- case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI1_SAME_SHORT:
|
||||
- case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI2_SAME_NORMAL:
|
||||
- case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI2_SAME_OPEN:
|
||||
- case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI2_SAME_SHORT:
|
||||
- case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI3_SAME_NORMAL:
|
||||
- case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI3_SAME_OPEN:
|
||||
- case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI3_SAME_SHORT:
|
||||
- return true;
|
||||
- default:
|
||||
- return false;
|
||||
- }
|
||||
-}
|
||||
-
|
||||
-static int qca808x_cable_test_result_trans(int cdt_code)
|
||||
-{
|
||||
- switch (cdt_code) {
|
||||
- case QCA808X_CDT_STATUS_STAT_NORMAL:
|
||||
- return ETHTOOL_A_CABLE_RESULT_CODE_OK;
|
||||
- case QCA808X_CDT_STATUS_STAT_SAME_SHORT:
|
||||
- return ETHTOOL_A_CABLE_RESULT_CODE_SAME_SHORT;
|
||||
- case QCA808X_CDT_STATUS_STAT_SAME_OPEN:
|
||||
- return ETHTOOL_A_CABLE_RESULT_CODE_OPEN;
|
||||
- case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI1_SAME_NORMAL:
|
||||
- case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI1_SAME_OPEN:
|
||||
- case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI1_SAME_SHORT:
|
||||
- case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI2_SAME_NORMAL:
|
||||
- case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI2_SAME_OPEN:
|
||||
- case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI2_SAME_SHORT:
|
||||
- case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI3_SAME_NORMAL:
|
||||
- case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI3_SAME_OPEN:
|
||||
- case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI3_SAME_SHORT:
|
||||
- return ETHTOOL_A_CABLE_RESULT_CODE_CROSS_SHORT;
|
||||
- case QCA808X_CDT_STATUS_STAT_FAIL:
|
||||
- default:
|
||||
- return ETHTOOL_A_CABLE_RESULT_CODE_UNSPEC;
|
||||
- }
|
||||
-}
|
||||
-
|
||||
-static int qca808x_cdt_fault_length(struct phy_device *phydev, int pair,
|
||||
- int result)
|
||||
-{
|
||||
- int val;
|
||||
- u32 cdt_length_reg = 0;
|
||||
-
|
||||
- switch (pair) {
|
||||
- case ETHTOOL_A_CABLE_PAIR_A:
|
||||
- cdt_length_reg = QCA808X_MMD3_CDT_DIAG_PAIR_A;
|
||||
- break;
|
||||
- case ETHTOOL_A_CABLE_PAIR_B:
|
||||
- cdt_length_reg = QCA808X_MMD3_CDT_DIAG_PAIR_B;
|
||||
- break;
|
||||
- case ETHTOOL_A_CABLE_PAIR_C:
|
||||
- cdt_length_reg = QCA808X_MMD3_CDT_DIAG_PAIR_C;
|
||||
- break;
|
||||
- case ETHTOOL_A_CABLE_PAIR_D:
|
||||
- cdt_length_reg = QCA808X_MMD3_CDT_DIAG_PAIR_D;
|
||||
- break;
|
||||
- default:
|
||||
- return -EINVAL;
|
||||
- }
|
||||
-
|
||||
- val = phy_read_mmd(phydev, MDIO_MMD_PCS, cdt_length_reg);
|
||||
- if (val < 0)
|
||||
- return val;
|
||||
-
|
||||
- if (result == ETHTOOL_A_CABLE_RESULT_CODE_SAME_SHORT)
|
||||
- val = FIELD_GET(QCA808X_CDT_DIAG_LENGTH_SAME_SHORT, val);
|
||||
- else
|
||||
- val = FIELD_GET(QCA808X_CDT_DIAG_LENGTH_CROSS_SHORT, val);
|
||||
-
|
||||
- return at803x_cdt_fault_length(val);
|
||||
-}
|
||||
-
|
||||
static int qca808x_cable_test_start(struct phy_device *phydev)
|
||||
{
|
||||
int ret;
|
||||
@@ -526,81 +396,6 @@ static int qca808x_cable_test_start(stru
|
||||
|
||||
return 0;
|
||||
}
|
||||
-
|
||||
-static int qca808x_cable_test_get_pair_status(struct phy_device *phydev, u8 pair,
|
||||
- u16 status)
|
||||
-{
|
||||
- int length, result;
|
||||
- u16 pair_code;
|
||||
-
|
||||
- switch (pair) {
|
||||
- case ETHTOOL_A_CABLE_PAIR_A:
|
||||
- pair_code = FIELD_GET(QCA808X_CDT_CODE_PAIR_A, status);
|
||||
- break;
|
||||
- case ETHTOOL_A_CABLE_PAIR_B:
|
||||
- pair_code = FIELD_GET(QCA808X_CDT_CODE_PAIR_B, status);
|
||||
- break;
|
||||
- case ETHTOOL_A_CABLE_PAIR_C:
|
||||
- pair_code = FIELD_GET(QCA808X_CDT_CODE_PAIR_C, status);
|
||||
- break;
|
||||
- case ETHTOOL_A_CABLE_PAIR_D:
|
||||
- pair_code = FIELD_GET(QCA808X_CDT_CODE_PAIR_D, status);
|
||||
- break;
|
||||
- default:
|
||||
- return -EINVAL;
|
||||
- }
|
||||
-
|
||||
- result = qca808x_cable_test_result_trans(pair_code);
|
||||
- ethnl_cable_test_result(phydev, pair, result);
|
||||
-
|
||||
- if (qca808x_cdt_fault_length_valid(pair_code)) {
|
||||
- length = qca808x_cdt_fault_length(phydev, pair, result);
|
||||
- ethnl_cable_test_fault_length(phydev, pair, length);
|
||||
- }
|
||||
-
|
||||
- return 0;
|
||||
-}
|
||||
-
|
||||
-static int qca808x_cable_test_get_status(struct phy_device *phydev, bool *finished)
|
||||
-{
|
||||
- int ret, val;
|
||||
-
|
||||
- *finished = false;
|
||||
-
|
||||
- val = QCA808X_CDT_ENABLE_TEST |
|
||||
- QCA808X_CDT_LENGTH_UNIT;
|
||||
- ret = at803x_cdt_start(phydev, val);
|
||||
- if (ret)
|
||||
- return ret;
|
||||
-
|
||||
- ret = at803x_cdt_wait_for_completion(phydev, QCA808X_CDT_ENABLE_TEST);
|
||||
- if (ret)
|
||||
- return ret;
|
||||
-
|
||||
- val = phy_read_mmd(phydev, MDIO_MMD_PCS, QCA808X_MMD3_CDT_STATUS);
|
||||
- if (val < 0)
|
||||
- return val;
|
||||
-
|
||||
- ret = qca808x_cable_test_get_pair_status(phydev, ETHTOOL_A_CABLE_PAIR_A, val);
|
||||
- if (ret)
|
||||
- return ret;
|
||||
-
|
||||
- ret = qca808x_cable_test_get_pair_status(phydev, ETHTOOL_A_CABLE_PAIR_B, val);
|
||||
- if (ret)
|
||||
- return ret;
|
||||
-
|
||||
- ret = qca808x_cable_test_get_pair_status(phydev, ETHTOOL_A_CABLE_PAIR_C, val);
|
||||
- if (ret)
|
||||
- return ret;
|
||||
-
|
||||
- ret = qca808x_cable_test_get_pair_status(phydev, ETHTOOL_A_CABLE_PAIR_D, val);
|
||||
- if (ret)
|
||||
- return ret;
|
||||
-
|
||||
- *finished = true;
|
||||
-
|
||||
- return 0;
|
||||
-}
|
||||
|
||||
static int qca808x_get_features(struct phy_device *phydev)
|
||||
{
|
||||
--- a/drivers/net/phy/qcom/qcom-phy-lib.c
|
||||
+++ b/drivers/net/phy/qcom/qcom-phy-lib.c
|
||||
@@ -5,6 +5,7 @@
|
||||
|
||||
#include <linux/netdevice.h>
|
||||
#include <linux/etherdevice.h>
|
||||
+#include <linux/ethtool_netlink.h>
|
||||
|
||||
#include "qcom.h"
|
||||
|
||||
@@ -311,6 +312,42 @@ int at803x_prepare_config_aneg(struct ph
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(at803x_prepare_config_aneg);
|
||||
|
||||
+int at803x_read_status(struct phy_device *phydev)
|
||||
+{
|
||||
+ struct at803x_ss_mask ss_mask = { 0 };
|
||||
+ int err, old_link = phydev->link;
|
||||
+
|
||||
+ /* Update the link, but return if there was an error */
|
||||
+ err = genphy_update_link(phydev);
|
||||
+ if (err)
|
||||
+ return err;
|
||||
+
|
||||
+ /* why bother the PHY if nothing can have changed */
|
||||
+ if (phydev->autoneg == AUTONEG_ENABLE && old_link && phydev->link)
|
||||
+ return 0;
|
||||
+
|
||||
+ phydev->speed = SPEED_UNKNOWN;
|
||||
+ phydev->duplex = DUPLEX_UNKNOWN;
|
||||
+ phydev->pause = 0;
|
||||
+ phydev->asym_pause = 0;
|
||||
+
|
||||
+ err = genphy_read_lpa(phydev);
|
||||
+ if (err < 0)
|
||||
+ return err;
|
||||
+
|
||||
+ ss_mask.speed_mask = AT803X_SS_SPEED_MASK;
|
||||
+ ss_mask.speed_shift = __bf_shf(AT803X_SS_SPEED_MASK);
|
||||
+ err = at803x_read_specific_status(phydev, ss_mask);
|
||||
+ if (err < 0)
|
||||
+ return err;
|
||||
+
|
||||
+ if (phydev->autoneg == AUTONEG_ENABLE && phydev->autoneg_complete)
|
||||
+ phy_resolve_aneg_pause(phydev);
|
||||
+
|
||||
+ return 0;
|
||||
+}
|
||||
+EXPORT_SYMBOL_GPL(at803x_read_status);
|
||||
+
|
||||
static int at803x_get_downshift(struct phy_device *phydev, u8 *d)
|
||||
{
|
||||
int val;
|
||||
@@ -427,3 +464,159 @@ int at803x_cdt_wait_for_completion(struc
|
||||
return ret < 0 ? ret : 0;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(at803x_cdt_wait_for_completion);
|
||||
+
|
||||
+static bool qca808x_cdt_fault_length_valid(int cdt_code)
|
||||
+{
|
||||
+ switch (cdt_code) {
|
||||
+ case QCA808X_CDT_STATUS_STAT_SAME_SHORT:
|
||||
+ case QCA808X_CDT_STATUS_STAT_SAME_OPEN:
|
||||
+ case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI1_SAME_NORMAL:
|
||||
+ case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI1_SAME_OPEN:
|
||||
+ case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI1_SAME_SHORT:
|
||||
+ case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI2_SAME_NORMAL:
|
||||
+ case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI2_SAME_OPEN:
|
||||
+ case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI2_SAME_SHORT:
|
||||
+ case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI3_SAME_NORMAL:
|
||||
+ case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI3_SAME_OPEN:
|
||||
+ case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI3_SAME_SHORT:
|
||||
+ return true;
|
||||
+ default:
|
||||
+ return false;
|
||||
+ }
|
||||
+}
|
||||
+
|
||||
+static int qca808x_cable_test_result_trans(int cdt_code)
|
||||
+{
|
||||
+ switch (cdt_code) {
|
||||
+ case QCA808X_CDT_STATUS_STAT_NORMAL:
|
||||
+ return ETHTOOL_A_CABLE_RESULT_CODE_OK;
|
||||
+ case QCA808X_CDT_STATUS_STAT_SAME_SHORT:
|
||||
+ return ETHTOOL_A_CABLE_RESULT_CODE_SAME_SHORT;
|
||||
+ case QCA808X_CDT_STATUS_STAT_SAME_OPEN:
|
||||
+ return ETHTOOL_A_CABLE_RESULT_CODE_OPEN;
|
||||
+ case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI1_SAME_NORMAL:
|
||||
+ case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI1_SAME_OPEN:
|
||||
+ case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI1_SAME_SHORT:
|
||||
+ case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI2_SAME_NORMAL:
|
||||
+ case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI2_SAME_OPEN:
|
||||
+ case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI2_SAME_SHORT:
|
||||
+ case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI3_SAME_NORMAL:
|
||||
+ case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI3_SAME_OPEN:
|
||||
+ case QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI3_SAME_SHORT:
|
||||
+ return ETHTOOL_A_CABLE_RESULT_CODE_CROSS_SHORT;
|
||||
+ case QCA808X_CDT_STATUS_STAT_FAIL:
|
||||
+ default:
|
||||
+ return ETHTOOL_A_CABLE_RESULT_CODE_UNSPEC;
|
||||
+ }
|
||||
+}
|
||||
+
|
||||
+static int qca808x_cdt_fault_length(struct phy_device *phydev, int pair,
|
||||
+ int result)
|
||||
+{
|
||||
+ int val;
|
||||
+ u32 cdt_length_reg = 0;
|
||||
+
|
||||
+ switch (pair) {
|
||||
+ case ETHTOOL_A_CABLE_PAIR_A:
|
||||
+ cdt_length_reg = QCA808X_MMD3_CDT_DIAG_PAIR_A;
|
||||
+ break;
|
||||
+ case ETHTOOL_A_CABLE_PAIR_B:
|
||||
+ cdt_length_reg = QCA808X_MMD3_CDT_DIAG_PAIR_B;
|
||||
+ break;
|
||||
+ case ETHTOOL_A_CABLE_PAIR_C:
|
||||
+ cdt_length_reg = QCA808X_MMD3_CDT_DIAG_PAIR_C;
|
||||
+ break;
|
||||
+ case ETHTOOL_A_CABLE_PAIR_D:
|
||||
+ cdt_length_reg = QCA808X_MMD3_CDT_DIAG_PAIR_D;
|
||||
+ break;
|
||||
+ default:
|
||||
+ return -EINVAL;
|
||||
+ }
|
||||
+
|
||||
+ val = phy_read_mmd(phydev, MDIO_MMD_PCS, cdt_length_reg);
|
||||
+ if (val < 0)
|
||||
+ return val;
|
||||
+
|
||||
+ if (result == ETHTOOL_A_CABLE_RESULT_CODE_SAME_SHORT)
|
||||
+ val = FIELD_GET(QCA808X_CDT_DIAG_LENGTH_SAME_SHORT, val);
|
||||
+ else
|
||||
+ val = FIELD_GET(QCA808X_CDT_DIAG_LENGTH_CROSS_SHORT, val);
|
||||
+
|
||||
+ return at803x_cdt_fault_length(val);
|
||||
+}
|
||||
+
|
||||
+static int qca808x_cable_test_get_pair_status(struct phy_device *phydev, u8 pair,
|
||||
+ u16 status)
|
||||
+{
|
||||
+ int length, result;
|
||||
+ u16 pair_code;
|
||||
+
|
||||
+ switch (pair) {
|
||||
+ case ETHTOOL_A_CABLE_PAIR_A:
|
||||
+ pair_code = FIELD_GET(QCA808X_CDT_CODE_PAIR_A, status);
|
||||
+ break;
|
||||
+ case ETHTOOL_A_CABLE_PAIR_B:
|
||||
+ pair_code = FIELD_GET(QCA808X_CDT_CODE_PAIR_B, status);
|
||||
+ break;
|
||||
+ case ETHTOOL_A_CABLE_PAIR_C:
|
||||
+ pair_code = FIELD_GET(QCA808X_CDT_CODE_PAIR_C, status);
|
||||
+ break;
|
||||
+ case ETHTOOL_A_CABLE_PAIR_D:
|
||||
+ pair_code = FIELD_GET(QCA808X_CDT_CODE_PAIR_D, status);
|
||||
+ break;
|
||||
+ default:
|
||||
+ return -EINVAL;
|
||||
+ }
|
||||
+
|
||||
+ result = qca808x_cable_test_result_trans(pair_code);
|
||||
+ ethnl_cable_test_result(phydev, pair, result);
|
||||
+
|
||||
+ if (qca808x_cdt_fault_length_valid(pair_code)) {
|
||||
+ length = qca808x_cdt_fault_length(phydev, pair, result);
|
||||
+ ethnl_cable_test_fault_length(phydev, pair, length);
|
||||
+ }
|
||||
+
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
+int qca808x_cable_test_get_status(struct phy_device *phydev, bool *finished)
|
||||
+{
|
||||
+ int ret, val;
|
||||
+
|
||||
+ *finished = false;
|
||||
+
|
||||
+ val = QCA808X_CDT_ENABLE_TEST |
|
||||
+ QCA808X_CDT_LENGTH_UNIT;
|
||||
+ ret = at803x_cdt_start(phydev, val);
|
||||
+ if (ret)
|
||||
+ return ret;
|
||||
+
|
||||
+ ret = at803x_cdt_wait_for_completion(phydev, QCA808X_CDT_ENABLE_TEST);
|
||||
+ if (ret)
|
||||
+ return ret;
|
||||
+
|
||||
+ val = phy_read_mmd(phydev, MDIO_MMD_PCS, QCA808X_MMD3_CDT_STATUS);
|
||||
+ if (val < 0)
|
||||
+ return val;
|
||||
+
|
||||
+ ret = qca808x_cable_test_get_pair_status(phydev, ETHTOOL_A_CABLE_PAIR_A, val);
|
||||
+ if (ret)
|
||||
+ return ret;
|
||||
+
|
||||
+ ret = qca808x_cable_test_get_pair_status(phydev, ETHTOOL_A_CABLE_PAIR_B, val);
|
||||
+ if (ret)
|
||||
+ return ret;
|
||||
+
|
||||
+ ret = qca808x_cable_test_get_pair_status(phydev, ETHTOOL_A_CABLE_PAIR_C, val);
|
||||
+ if (ret)
|
||||
+ return ret;
|
||||
+
|
||||
+ ret = qca808x_cable_test_get_pair_status(phydev, ETHTOOL_A_CABLE_PAIR_D, val);
|
||||
+ if (ret)
|
||||
+ return ret;
|
||||
+
|
||||
+ *finished = true;
|
||||
+
|
||||
+ return 0;
|
||||
+}
|
||||
+EXPORT_SYMBOL_GPL(qca808x_cable_test_get_status);
|
||||
--- a/drivers/net/phy/qcom/qcom.h
|
||||
+++ b/drivers/net/phy/qcom/qcom.h
|
||||
@@ -54,6 +54,55 @@
|
||||
#define AT803X_CDT_STATUS_STAT_MASK GENMASK(9, 8)
|
||||
#define AT803X_CDT_STATUS_DELTA_TIME_MASK GENMASK(7, 0)
|
||||
|
||||
+#define QCA808X_CDT_ENABLE_TEST BIT(15)
|
||||
+#define QCA808X_CDT_INTER_CHECK_DIS BIT(13)
|
||||
+#define QCA808X_CDT_STATUS BIT(11)
|
||||
+#define QCA808X_CDT_LENGTH_UNIT BIT(10)
|
||||
+
|
||||
+#define QCA808X_MMD3_CDT_STATUS 0x8064
|
||||
+#define QCA808X_MMD3_CDT_DIAG_PAIR_A 0x8065
|
||||
+#define QCA808X_MMD3_CDT_DIAG_PAIR_B 0x8066
|
||||
+#define QCA808X_MMD3_CDT_DIAG_PAIR_C 0x8067
|
||||
+#define QCA808X_MMD3_CDT_DIAG_PAIR_D 0x8068
|
||||
+#define QCA808X_CDT_DIAG_LENGTH_SAME_SHORT GENMASK(15, 8)
|
||||
+#define QCA808X_CDT_DIAG_LENGTH_CROSS_SHORT GENMASK(7, 0)
|
||||
+
|
||||
+#define QCA808X_CDT_CODE_PAIR_A GENMASK(15, 12)
|
||||
+#define QCA808X_CDT_CODE_PAIR_B GENMASK(11, 8)
|
||||
+#define QCA808X_CDT_CODE_PAIR_C GENMASK(7, 4)
|
||||
+#define QCA808X_CDT_CODE_PAIR_D GENMASK(3, 0)
|
||||
+
|
||||
+#define QCA808X_CDT_STATUS_STAT_TYPE GENMASK(1, 0)
|
||||
+#define QCA808X_CDT_STATUS_STAT_FAIL FIELD_PREP_CONST(QCA808X_CDT_STATUS_STAT_TYPE, 0)
|
||||
+#define QCA808X_CDT_STATUS_STAT_NORMAL FIELD_PREP_CONST(QCA808X_CDT_STATUS_STAT_TYPE, 1)
|
||||
+#define QCA808X_CDT_STATUS_STAT_SAME_OPEN FIELD_PREP_CONST(QCA808X_CDT_STATUS_STAT_TYPE, 2)
|
||||
+#define QCA808X_CDT_STATUS_STAT_SAME_SHORT FIELD_PREP_CONST(QCA808X_CDT_STATUS_STAT_TYPE, 3)
|
||||
+
|
||||
+#define QCA808X_CDT_STATUS_STAT_MDI GENMASK(3, 2)
|
||||
+#define QCA808X_CDT_STATUS_STAT_MDI1 FIELD_PREP_CONST(QCA808X_CDT_STATUS_STAT_MDI, 1)
|
||||
+#define QCA808X_CDT_STATUS_STAT_MDI2 FIELD_PREP_CONST(QCA808X_CDT_STATUS_STAT_MDI, 2)
|
||||
+#define QCA808X_CDT_STATUS_STAT_MDI3 FIELD_PREP_CONST(QCA808X_CDT_STATUS_STAT_MDI, 3)
|
||||
+
|
||||
+/* NORMAL are MDI with type set to 0 */
|
||||
+#define QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI1_SAME_NORMAL QCA808X_CDT_STATUS_STAT_MDI1
|
||||
+#define QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI1_SAME_OPEN (QCA808X_CDT_STATUS_STAT_SAME_OPEN |\
|
||||
+ QCA808X_CDT_STATUS_STAT_MDI1)
|
||||
+#define QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI1_SAME_SHORT (QCA808X_CDT_STATUS_STAT_SAME_SHORT |\
|
||||
+ QCA808X_CDT_STATUS_STAT_MDI1)
|
||||
+#define QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI2_SAME_NORMAL QCA808X_CDT_STATUS_STAT_MDI2
|
||||
+#define QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI2_SAME_OPEN (QCA808X_CDT_STATUS_STAT_SAME_OPEN |\
|
||||
+ QCA808X_CDT_STATUS_STAT_MDI2)
|
||||
+#define QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI2_SAME_SHORT (QCA808X_CDT_STATUS_STAT_SAME_SHORT |\
|
||||
+ QCA808X_CDT_STATUS_STAT_MDI2)
|
||||
+#define QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI3_SAME_NORMAL QCA808X_CDT_STATUS_STAT_MDI3
|
||||
+#define QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI3_SAME_OPEN (QCA808X_CDT_STATUS_STAT_SAME_OPEN |\
|
||||
+ QCA808X_CDT_STATUS_STAT_MDI3)
|
||||
+#define QCA808X_CDT_STATUS_STAT_CROSS_SHORT_WITH_MDI3_SAME_SHORT (QCA808X_CDT_STATUS_STAT_SAME_SHORT |\
|
||||
+ QCA808X_CDT_STATUS_STAT_MDI3)
|
||||
+
|
||||
+/* Added for reference of existence but should be handled by wait_for_completion already */
|
||||
+#define QCA808X_CDT_STATUS_STAT_BUSY (BIT(1) | BIT(3))
|
||||
+
|
||||
#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
|
||||
@@ -110,6 +159,7 @@ int at803x_read_specific_status(struct p
|
||||
struct at803x_ss_mask ss_mask);
|
||||
int at803x_config_mdix(struct phy_device *phydev, u8 ctrl);
|
||||
int at803x_prepare_config_aneg(struct phy_device *phydev);
|
||||
+int at803x_read_status(struct phy_device *phydev);
|
||||
int at803x_get_tunable(struct phy_device *phydev,
|
||||
struct ethtool_tunable *tuna, void *data);
|
||||
int at803x_set_tunable(struct phy_device *phydev,
|
||||
@@ -118,3 +168,4 @@ int at803x_cdt_fault_length(int dt);
|
||||
int at803x_cdt_start(struct phy_device *phydev, u32 cdt_start);
|
||||
int at803x_cdt_wait_for_completion(struct phy_device *phydev,
|
||||
u32 cdt_en);
|
||||
+int qca808x_cable_test_get_status(struct phy_device *phydev, bool *finished);
|
@ -0,0 +1,100 @@
|
||||
From 9b1d5e055508393561e26bd1720f4c2639b03b1a Mon Sep 17 00:00:00 2001
|
||||
From: Christian Marangi <ansuelsmth@gmail.com>
|
||||
Date: Tue, 6 Feb 2024 18:31:09 +0100
|
||||
Subject: [PATCH 06/10] net: phy: provide whether link has changed in
|
||||
c37_read_status
|
||||
|
||||
Some PHY driver might require additional regs call after
|
||||
genphy_c37_read_status() is called.
|
||||
|
||||
Expand genphy_c37_read_status to provide a bool wheather the link has
|
||||
changed or not to permit PHY driver to skip additional regs call if
|
||||
nothing has changed.
|
||||
|
||||
Every user of genphy_c37_read_status() is updated with the new
|
||||
additional bool.
|
||||
|
||||
Signed-off-by: Christian Marangi <ansuelsmth@gmail.com>
|
||||
Reviewed-by: Andrew Lunn <andrew@lunn.ch>
|
||||
Signed-off-by: David S. Miller <davem@davemloft.net>
|
||||
---
|
||||
drivers/net/phy/broadcom.c | 3 ++-
|
||||
drivers/net/phy/phy_device.c | 11 +++++++++--
|
||||
drivers/net/phy/qcom/at803x.c | 3 ++-
|
||||
include/linux/phy.h | 2 +-
|
||||
4 files changed, 14 insertions(+), 5 deletions(-)
|
||||
|
||||
--- a/drivers/net/phy/broadcom.c
|
||||
+++ b/drivers/net/phy/broadcom.c
|
||||
@@ -609,10 +609,11 @@ static int bcm54616s_config_aneg(struct
|
||||
static int bcm54616s_read_status(struct phy_device *phydev)
|
||||
{
|
||||
struct bcm54616s_phy_priv *priv = phydev->priv;
|
||||
+ bool changed;
|
||||
int err;
|
||||
|
||||
if (priv->mode_1000bx_en)
|
||||
- err = genphy_c37_read_status(phydev);
|
||||
+ err = genphy_c37_read_status(phydev, &changed);
|
||||
else
|
||||
err = genphy_read_status(phydev);
|
||||
|
||||
--- a/drivers/net/phy/phy_device.c
|
||||
+++ b/drivers/net/phy/phy_device.c
|
||||
@@ -2551,12 +2551,15 @@ EXPORT_SYMBOL(genphy_read_status);
|
||||
/**
|
||||
* genphy_c37_read_status - check the link status and update current link state
|
||||
* @phydev: target phy_device struct
|
||||
+ * @changed: pointer where to store if link changed
|
||||
*
|
||||
* Description: Check the link, then figure out the current state
|
||||
* by comparing what we advertise with what the link partner
|
||||
* advertises. This function is for Clause 37 1000Base-X mode.
|
||||
+ *
|
||||
+ * If link has changed, @changed is set to true, false otherwise.
|
||||
*/
|
||||
-int genphy_c37_read_status(struct phy_device *phydev)
|
||||
+int genphy_c37_read_status(struct phy_device *phydev, bool *changed)
|
||||
{
|
||||
int lpa, err, old_link = phydev->link;
|
||||
|
||||
@@ -2566,9 +2569,13 @@ int genphy_c37_read_status(struct phy_de
|
||||
return err;
|
||||
|
||||
/* why bother the PHY if nothing can have changed */
|
||||
- if (phydev->autoneg == AUTONEG_ENABLE && old_link && phydev->link)
|
||||
+ if (phydev->autoneg == AUTONEG_ENABLE && old_link && phydev->link) {
|
||||
+ *changed = false;
|
||||
return 0;
|
||||
+ }
|
||||
|
||||
+ /* Signal link has changed */
|
||||
+ *changed = true;
|
||||
phydev->duplex = DUPLEX_UNKNOWN;
|
||||
phydev->pause = 0;
|
||||
phydev->asym_pause = 0;
|
||||
--- a/drivers/net/phy/qcom/at803x.c
|
||||
+++ b/drivers/net/phy/qcom/at803x.c
|
||||
@@ -912,9 +912,10 @@ static int at8031_config_intr(struct phy
|
||||
static int at8031_read_status(struct phy_device *phydev)
|
||||
{
|
||||
struct at803x_priv *priv = phydev->priv;
|
||||
+ bool changed;
|
||||
|
||||
if (priv->is_1000basex)
|
||||
- return genphy_c37_read_status(phydev);
|
||||
+ return genphy_c37_read_status(phydev, &changed);
|
||||
|
||||
return at803x_read_status(phydev);
|
||||
}
|
||||
--- a/include/linux/phy.h
|
||||
+++ b/include/linux/phy.h
|
||||
@@ -1660,7 +1660,7 @@ int genphy_write_mmd_unsupported(struct
|
||||
|
||||
/* Clause 37 */
|
||||
int genphy_c37_config_aneg(struct phy_device *phydev);
|
||||
-int genphy_c37_read_status(struct phy_device *phydev);
|
||||
+int genphy_c37_read_status(struct phy_device *phydev, bool *changed);
|
||||
|
||||
/* Clause 45 PHY */
|
||||
int genphy_c45_restart_aneg(struct phy_device *phydev);
|
@ -0,0 +1,668 @@
|
||||
From d1cb613efbd3cd7d0c000167816beb3f248f5eb8 Mon Sep 17 00:00:00 2001
|
||||
From: Robert Marko <robert.marko@sartura.hr>
|
||||
Date: Tue, 6 Feb 2024 18:31:10 +0100
|
||||
Subject: [PATCH 07/10] net: phy: qcom: add support for QCA807x PHY Family
|
||||
|
||||
This adds driver for the Qualcomm QCA8072 and QCA8075 PHY-s.
|
||||
|
||||
They are 2 or 5 port IEEE 802.3 clause 22 compliant 10BASE-Te,
|
||||
100BASE-TX and 1000BASE-T PHY-s.
|
||||
|
||||
They feature 2 SerDes, one for PSGMII or QSGMII connection with
|
||||
MAC, while second one is SGMII for connection to MAC or fiber.
|
||||
|
||||
Both models have a combo port that supports 1000BASE-X and
|
||||
100BASE-FX fiber.
|
||||
|
||||
PHY package can be configured in 3 mode following this table:
|
||||
|
||||
First Serdes mode Second Serdes mode
|
||||
Option 1 PSGMII for copper Disabled
|
||||
ports 0-4
|
||||
Option 2 PSGMII for copper 1000BASE-X / 100BASE-FX
|
||||
ports 0-4
|
||||
Option 3 QSGMII for copper SGMII for
|
||||
ports 0-3 copper port 4
|
||||
|
||||
Each PHY inside of QCA807x series has 4 digitally controlled
|
||||
output only pins that natively drive LED-s.
|
||||
But some vendors used these to driver generic LED-s controlled
|
||||
by userspace, so lets enable registering each PHY as GPIO
|
||||
controller and add driver for it.
|
||||
|
||||
These are commonly used in Qualcomm IPQ40xx, IPQ60xx and IPQ807x
|
||||
boards.
|
||||
|
||||
Co-developed-by: Christian Marangi <ansuelsmth@gmail.com>
|
||||
Signed-off-by: Robert Marko <robert.marko@sartura.hr>
|
||||
Signed-off-by: Christian Marangi <ansuelsmth@gmail.com>
|
||||
Reviewed-by: Andrew Lunn <andrew@lunn.ch>
|
||||
Signed-off-by: David S. Miller <davem@davemloft.net>
|
||||
---
|
||||
drivers/net/phy/qcom/Kconfig | 8 +
|
||||
drivers/net/phy/qcom/Makefile | 1 +
|
||||
drivers/net/phy/qcom/qca807x.c | 597 +++++++++++++++++++++++++++++++++
|
||||
3 files changed, 606 insertions(+)
|
||||
create mode 100644 drivers/net/phy/qcom/qca807x.c
|
||||
|
||||
--- a/drivers/net/phy/qcom/Kconfig
|
||||
+++ b/drivers/net/phy/qcom/Kconfig
|
||||
@@ -20,3 +20,11 @@ config QCA808X_PHY
|
||||
select QCOM_NET_PHYLIB
|
||||
help
|
||||
Currently supports the QCA8081 model
|
||||
+
|
||||
+config QCA807X_PHY
|
||||
+ tristate "Qualcomm QCA807x PHYs"
|
||||
+ select QCOM_NET_PHYLIB
|
||||
+ depends on OF_MDIO
|
||||
+ help
|
||||
+ Currently supports the Qualcomm QCA8072, QCA8075 and the PSGMII
|
||||
+ control PHY.
|
||||
--- a/drivers/net/phy/qcom/Makefile
|
||||
+++ b/drivers/net/phy/qcom/Makefile
|
||||
@@ -3,3 +3,4 @@ obj-$(CONFIG_QCOM_NET_PHYLIB) += qcom-ph
|
||||
obj-$(CONFIG_AT803X_PHY) += at803x.o
|
||||
obj-$(CONFIG_QCA83XX_PHY) += qca83xx.o
|
||||
obj-$(CONFIG_QCA808X_PHY) += qca808x.o
|
||||
+obj-$(CONFIG_QCA807X_PHY) += qca807x.o
|
||||
--- /dev/null
|
||||
+++ b/drivers/net/phy/qcom/qca807x.c
|
||||
@@ -0,0 +1,597 @@
|
||||
+// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
+/*
|
||||
+ * Copyright (c) 2023 Sartura Ltd.
|
||||
+ *
|
||||
+ * Author: Robert Marko <robert.marko@sartura.hr>
|
||||
+ * Christian Marangi <ansuelsmth@gmail.com>
|
||||
+ *
|
||||
+ * Qualcomm QCA8072 and QCA8075 PHY driver
|
||||
+ */
|
||||
+
|
||||
+#include <linux/module.h>
|
||||
+#include <linux/of.h>
|
||||
+#include <linux/phy.h>
|
||||
+#include <linux/bitfield.h>
|
||||
+#include <linux/gpio/driver.h>
|
||||
+#include <linux/sfp.h>
|
||||
+
|
||||
+#include "qcom.h"
|
||||
+
|
||||
+#define QCA807X_CHIP_CONFIGURATION 0x1f
|
||||
+#define QCA807X_BT_BX_REG_SEL BIT(15)
|
||||
+#define QCA807X_BT_BX_REG_SEL_FIBER 0
|
||||
+#define QCA807X_BT_BX_REG_SEL_COPPER 1
|
||||
+#define QCA807X_CHIP_CONFIGURATION_MODE_CFG_MASK GENMASK(3, 0)
|
||||
+#define QCA807X_CHIP_CONFIGURATION_MODE_QSGMII_SGMII 4
|
||||
+#define QCA807X_CHIP_CONFIGURATION_MODE_PSGMII_FIBER 3
|
||||
+#define QCA807X_CHIP_CONFIGURATION_MODE_PSGMII_ALL_COPPER 0
|
||||
+
|
||||
+#define QCA807X_MEDIA_SELECT_STATUS 0x1a
|
||||
+#define QCA807X_MEDIA_DETECTED_COPPER BIT(5)
|
||||
+#define QCA807X_MEDIA_DETECTED_1000_BASE_X BIT(4)
|
||||
+#define QCA807X_MEDIA_DETECTED_100_BASE_FX BIT(3)
|
||||
+
|
||||
+#define QCA807X_MMD7_FIBER_MODE_AUTO_DETECTION 0x807e
|
||||
+#define QCA807X_MMD7_FIBER_MODE_AUTO_DETECTION_EN BIT(0)
|
||||
+
|
||||
+#define QCA807X_MMD7_1000BASE_T_POWER_SAVE_PER_CABLE_LENGTH 0x801a
|
||||
+#define QCA807X_CONTROL_DAC_MASK GENMASK(2, 0)
|
||||
+/* List of tweaks enabled by this bit:
|
||||
+ * - With both FULL amplitude and FULL bias current: bias current
|
||||
+ * is set to half.
|
||||
+ * - With only DSP amplitude: bias current is set to half and
|
||||
+ * is set to 1/4 with cable < 10m.
|
||||
+ * - With DSP bias current (included both DSP amplitude and
|
||||
+ * DSP bias current): bias current is half the detected current
|
||||
+ * with cable < 10m.
|
||||
+ */
|
||||
+#define QCA807X_CONTROL_DAC_BIAS_CURRENT_TWEAK BIT(2)
|
||||
+#define QCA807X_CONTROL_DAC_DSP_BIAS_CURRENT BIT(1)
|
||||
+#define QCA807X_CONTROL_DAC_DSP_AMPLITUDE BIT(0)
|
||||
+
|
||||
+#define QCA807X_MMD7_LED_100N_1 0x8074
|
||||
+#define QCA807X_MMD7_LED_100N_2 0x8075
|
||||
+#define QCA807X_MMD7_LED_1000N_1 0x8076
|
||||
+#define QCA807X_MMD7_LED_1000N_2 0x8077
|
||||
+
|
||||
+#define QCA807X_MMD7_LED_CTRL(x) (0x8074 + ((x) * 2))
|
||||
+#define QCA807X_MMD7_LED_FORCE_CTRL(x) (0x8075 + ((x) * 2))
|
||||
+
|
||||
+#define QCA807X_GPIO_FORCE_EN BIT(15)
|
||||
+#define QCA807X_GPIO_FORCE_MODE_MASK GENMASK(14, 13)
|
||||
+
|
||||
+#define QCA807X_FUNCTION_CONTROL 0x10
|
||||
+#define QCA807X_FC_MDI_CROSSOVER_MODE_MASK GENMASK(6, 5)
|
||||
+#define QCA807X_FC_MDI_CROSSOVER_AUTO 3
|
||||
+#define QCA807X_FC_MDI_CROSSOVER_MANUAL_MDIX 1
|
||||
+#define QCA807X_FC_MDI_CROSSOVER_MANUAL_MDI 0
|
||||
+
|
||||
+/* PQSGMII Analog PHY specific */
|
||||
+#define PQSGMII_CTRL_REG 0x0
|
||||
+#define PQSGMII_ANALOG_SW_RESET BIT(6)
|
||||
+#define PQSGMII_DRIVE_CONTROL_1 0xb
|
||||
+#define PQSGMII_TX_DRIVER_MASK GENMASK(7, 4)
|
||||
+#define PQSGMII_TX_DRIVER_140MV 0x0
|
||||
+#define PQSGMII_TX_DRIVER_160MV 0x1
|
||||
+#define PQSGMII_TX_DRIVER_180MV 0x2
|
||||
+#define PQSGMII_TX_DRIVER_200MV 0x3
|
||||
+#define PQSGMII_TX_DRIVER_220MV 0x4
|
||||
+#define PQSGMII_TX_DRIVER_240MV 0x5
|
||||
+#define PQSGMII_TX_DRIVER_260MV 0x6
|
||||
+#define PQSGMII_TX_DRIVER_280MV 0x7
|
||||
+#define PQSGMII_TX_DRIVER_300MV 0x8
|
||||
+#define PQSGMII_TX_DRIVER_320MV 0x9
|
||||
+#define PQSGMII_TX_DRIVER_400MV 0xa
|
||||
+#define PQSGMII_TX_DRIVER_500MV 0xb
|
||||
+#define PQSGMII_TX_DRIVER_600MV 0xc
|
||||
+#define PQSGMII_MODE_CTRL 0x6d
|
||||
+#define PQSGMII_MODE_CTRL_AZ_WORKAROUND_MASK BIT(0)
|
||||
+#define PQSGMII_MMD3_SERDES_CONTROL 0x805a
|
||||
+
|
||||
+#define PHY_ID_QCA8072 0x004dd0b2
|
||||
+#define PHY_ID_QCA8075 0x004dd0b1
|
||||
+
|
||||
+#define QCA807X_COMBO_ADDR_OFFSET 4
|
||||
+#define QCA807X_PQSGMII_ADDR_OFFSET 5
|
||||
+#define SERDES_RESET_SLEEP 100
|
||||
+
|
||||
+enum qca807x_global_phy {
|
||||
+ QCA807X_COMBO_ADDR = 4,
|
||||
+ QCA807X_PQSGMII_ADDR = 5,
|
||||
+};
|
||||
+
|
||||
+struct qca807x_shared_priv {
|
||||
+ unsigned int package_mode;
|
||||
+ u32 tx_drive_strength;
|
||||
+};
|
||||
+
|
||||
+struct qca807x_gpio_priv {
|
||||
+ struct phy_device *phy;
|
||||
+};
|
||||
+
|
||||
+struct qca807x_priv {
|
||||
+ bool dac_full_amplitude;
|
||||
+ bool dac_full_bias_current;
|
||||
+ bool dac_disable_bias_current_tweak;
|
||||
+};
|
||||
+
|
||||
+static int qca807x_cable_test_start(struct phy_device *phydev)
|
||||
+{
|
||||
+ /* we do all the (time consuming) work later */
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
+#ifdef CONFIG_GPIOLIB
|
||||
+static int qca807x_gpio_get_direction(struct gpio_chip *gc, unsigned int offset)
|
||||
+{
|
||||
+ return GPIO_LINE_DIRECTION_OUT;
|
||||
+}
|
||||
+
|
||||
+static int qca807x_gpio_get(struct gpio_chip *gc, unsigned int offset)
|
||||
+{
|
||||
+ struct qca807x_gpio_priv *priv = gpiochip_get_data(gc);
|
||||
+ u16 reg;
|
||||
+ int val;
|
||||
+
|
||||
+ reg = QCA807X_MMD7_LED_FORCE_CTRL(offset);
|
||||
+ val = phy_read_mmd(priv->phy, MDIO_MMD_AN, reg);
|
||||
+
|
||||
+ return FIELD_GET(QCA807X_GPIO_FORCE_MODE_MASK, val);
|
||||
+}
|
||||
+
|
||||
+static void qca807x_gpio_set(struct gpio_chip *gc, unsigned int offset, int value)
|
||||
+{
|
||||
+ struct qca807x_gpio_priv *priv = gpiochip_get_data(gc);
|
||||
+ u16 reg;
|
||||
+ int val;
|
||||
+
|
||||
+ reg = QCA807X_MMD7_LED_FORCE_CTRL(offset);
|
||||
+
|
||||
+ val = phy_read_mmd(priv->phy, MDIO_MMD_AN, reg);
|
||||
+ val &= ~QCA807X_GPIO_FORCE_MODE_MASK;
|
||||
+ val |= QCA807X_GPIO_FORCE_EN;
|
||||
+ val |= FIELD_PREP(QCA807X_GPIO_FORCE_MODE_MASK, value);
|
||||
+
|
||||
+ phy_write_mmd(priv->phy, MDIO_MMD_AN, reg, val);
|
||||
+}
|
||||
+
|
||||
+static int qca807x_gpio_dir_out(struct gpio_chip *gc, unsigned int offset, int value)
|
||||
+{
|
||||
+ qca807x_gpio_set(gc, offset, value);
|
||||
+
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
+static int qca807x_gpio(struct phy_device *phydev)
|
||||
+{
|
||||
+ struct device *dev = &phydev->mdio.dev;
|
||||
+ struct qca807x_gpio_priv *priv;
|
||||
+ struct gpio_chip *gc;
|
||||
+
|
||||
+ priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
|
||||
+ if (!priv)
|
||||
+ return -ENOMEM;
|
||||
+
|
||||
+ priv->phy = phydev;
|
||||
+
|
||||
+ gc = devm_kzalloc(dev, sizeof(*gc), GFP_KERNEL);
|
||||
+ if (!gc)
|
||||
+ return -ENOMEM;
|
||||
+
|
||||
+ gc->label = dev_name(dev);
|
||||
+ gc->base = -1;
|
||||
+ gc->ngpio = 2;
|
||||
+ gc->parent = dev;
|
||||
+ gc->owner = THIS_MODULE;
|
||||
+ gc->can_sleep = true;
|
||||
+ gc->get_direction = qca807x_gpio_get_direction;
|
||||
+ gc->direction_output = qca807x_gpio_dir_out;
|
||||
+ gc->get = qca807x_gpio_get;
|
||||
+ gc->set = qca807x_gpio_set;
|
||||
+
|
||||
+ return devm_gpiochip_add_data(dev, gc, priv);
|
||||
+}
|
||||
+#endif
|
||||
+
|
||||
+static int qca807x_read_fiber_status(struct phy_device *phydev)
|
||||
+{
|
||||
+ bool changed;
|
||||
+ int ss, err;
|
||||
+
|
||||
+ err = genphy_c37_read_status(phydev, &changed);
|
||||
+ if (err || !changed)
|
||||
+ return err;
|
||||
+
|
||||
+ /* Read the QCA807x PHY-Specific Status register fiber page,
|
||||
+ * which indicates the speed and duplex that the PHY is actually
|
||||
+ * using, irrespective of whether we are in autoneg mode or not.
|
||||
+ */
|
||||
+ ss = phy_read(phydev, AT803X_SPECIFIC_STATUS);
|
||||
+ if (ss < 0)
|
||||
+ return ss;
|
||||
+
|
||||
+ phydev->speed = SPEED_UNKNOWN;
|
||||
+ phydev->duplex = DUPLEX_UNKNOWN;
|
||||
+ if (ss & AT803X_SS_SPEED_DUPLEX_RESOLVED) {
|
||||
+ switch (FIELD_GET(AT803X_SS_SPEED_MASK, ss)) {
|
||||
+ case AT803X_SS_SPEED_100:
|
||||
+ phydev->speed = SPEED_100;
|
||||
+ break;
|
||||
+ case AT803X_SS_SPEED_1000:
|
||||
+ phydev->speed = SPEED_1000;
|
||||
+ break;
|
||||
+ }
|
||||
+
|
||||
+ if (ss & AT803X_SS_DUPLEX)
|
||||
+ phydev->duplex = DUPLEX_FULL;
|
||||
+ else
|
||||
+ phydev->duplex = DUPLEX_HALF;
|
||||
+ }
|
||||
+
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
+static int qca807x_read_status(struct phy_device *phydev)
|
||||
+{
|
||||
+ if (linkmode_test_bit(ETHTOOL_LINK_MODE_FIBRE_BIT, phydev->supported)) {
|
||||
+ switch (phydev->port) {
|
||||
+ case PORT_FIBRE:
|
||||
+ return qca807x_read_fiber_status(phydev);
|
||||
+ case PORT_TP:
|
||||
+ return at803x_read_status(phydev);
|
||||
+ default:
|
||||
+ return -EINVAL;
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ return at803x_read_status(phydev);
|
||||
+}
|
||||
+
|
||||
+static int qca807x_phy_package_probe_once(struct phy_device *phydev)
|
||||
+{
|
||||
+ struct phy_package_shared *shared = phydev->shared;
|
||||
+ struct qca807x_shared_priv *priv = shared->priv;
|
||||
+ unsigned int tx_drive_strength;
|
||||
+ const char *package_mode_name;
|
||||
+
|
||||
+ /* Default to 600mw if not defined */
|
||||
+ if (of_property_read_u32(shared->np, "qcom,tx-drive-strength-milliwatt",
|
||||
+ &tx_drive_strength))
|
||||
+ tx_drive_strength = 600;
|
||||
+
|
||||
+ switch (tx_drive_strength) {
|
||||
+ case 140:
|
||||
+ priv->tx_drive_strength = PQSGMII_TX_DRIVER_140MV;
|
||||
+ break;
|
||||
+ case 160:
|
||||
+ priv->tx_drive_strength = PQSGMII_TX_DRIVER_160MV;
|
||||
+ break;
|
||||
+ case 180:
|
||||
+ priv->tx_drive_strength = PQSGMII_TX_DRIVER_180MV;
|
||||
+ break;
|
||||
+ case 200:
|
||||
+ priv->tx_drive_strength = PQSGMII_TX_DRIVER_200MV;
|
||||
+ break;
|
||||
+ case 220:
|
||||
+ priv->tx_drive_strength = PQSGMII_TX_DRIVER_220MV;
|
||||
+ break;
|
||||
+ case 240:
|
||||
+ priv->tx_drive_strength = PQSGMII_TX_DRIVER_240MV;
|
||||
+ break;
|
||||
+ case 260:
|
||||
+ priv->tx_drive_strength = PQSGMII_TX_DRIVER_260MV;
|
||||
+ break;
|
||||
+ case 280:
|
||||
+ priv->tx_drive_strength = PQSGMII_TX_DRIVER_280MV;
|
||||
+ break;
|
||||
+ case 300:
|
||||
+ priv->tx_drive_strength = PQSGMII_TX_DRIVER_300MV;
|
||||
+ break;
|
||||
+ case 320:
|
||||
+ priv->tx_drive_strength = PQSGMII_TX_DRIVER_320MV;
|
||||
+ break;
|
||||
+ case 400:
|
||||
+ priv->tx_drive_strength = PQSGMII_TX_DRIVER_400MV;
|
||||
+ break;
|
||||
+ case 500:
|
||||
+ priv->tx_drive_strength = PQSGMII_TX_DRIVER_500MV;
|
||||
+ break;
|
||||
+ case 600:
|
||||
+ priv->tx_drive_strength = PQSGMII_TX_DRIVER_600MV;
|
||||
+ break;
|
||||
+ default:
|
||||
+ return -EINVAL;
|
||||
+ }
|
||||
+
|
||||
+ priv->package_mode = PHY_INTERFACE_MODE_NA;
|
||||
+ if (!of_property_read_string(shared->np, "qcom,package-mode",
|
||||
+ &package_mode_name)) {
|
||||
+ if (!strcasecmp(package_mode_name,
|
||||
+ phy_modes(PHY_INTERFACE_MODE_PSGMII)))
|
||||
+ priv->package_mode = PHY_INTERFACE_MODE_PSGMII;
|
||||
+ else if (!strcasecmp(package_mode_name,
|
||||
+ phy_modes(PHY_INTERFACE_MODE_QSGMII)))
|
||||
+ priv->package_mode = PHY_INTERFACE_MODE_QSGMII;
|
||||
+ else
|
||||
+ return -EINVAL;
|
||||
+ }
|
||||
+
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
+static int qca807x_phy_package_config_init_once(struct phy_device *phydev)
|
||||
+{
|
||||
+ struct phy_package_shared *shared = phydev->shared;
|
||||
+ struct qca807x_shared_priv *priv = shared->priv;
|
||||
+ int val, ret;
|
||||
+
|
||||
+ phy_lock_mdio_bus(phydev);
|
||||
+
|
||||
+ /* Set correct PHY package mode */
|
||||
+ val = __phy_package_read(phydev, QCA807X_COMBO_ADDR,
|
||||
+ QCA807X_CHIP_CONFIGURATION);
|
||||
+ val &= ~QCA807X_CHIP_CONFIGURATION_MODE_CFG_MASK;
|
||||
+ /* package_mode can be QSGMII or PSGMII and we validate
|
||||
+ * this in probe_once.
|
||||
+ * With package_mode to NA, we default to PSGMII.
|
||||
+ */
|
||||
+ switch (priv->package_mode) {
|
||||
+ case PHY_INTERFACE_MODE_QSGMII:
|
||||
+ val |= QCA807X_CHIP_CONFIGURATION_MODE_QSGMII_SGMII;
|
||||
+ break;
|
||||
+ case PHY_INTERFACE_MODE_PSGMII:
|
||||
+ default:
|
||||
+ val |= QCA807X_CHIP_CONFIGURATION_MODE_PSGMII_ALL_COPPER;
|
||||
+ }
|
||||
+ ret = __phy_package_write(phydev, QCA807X_COMBO_ADDR,
|
||||
+ QCA807X_CHIP_CONFIGURATION, val);
|
||||
+ if (ret)
|
||||
+ goto exit;
|
||||
+
|
||||
+ /* After mode change Serdes reset is required */
|
||||
+ val = __phy_package_read(phydev, QCA807X_PQSGMII_ADDR,
|
||||
+ PQSGMII_CTRL_REG);
|
||||
+ val &= ~PQSGMII_ANALOG_SW_RESET;
|
||||
+ ret = __phy_package_write(phydev, QCA807X_PQSGMII_ADDR,
|
||||
+ PQSGMII_CTRL_REG, val);
|
||||
+ if (ret)
|
||||
+ goto exit;
|
||||
+
|
||||
+ msleep(SERDES_RESET_SLEEP);
|
||||
+
|
||||
+ val = __phy_package_read(phydev, QCA807X_PQSGMII_ADDR,
|
||||
+ PQSGMII_CTRL_REG);
|
||||
+ val |= PQSGMII_ANALOG_SW_RESET;
|
||||
+ ret = __phy_package_write(phydev, QCA807X_PQSGMII_ADDR,
|
||||
+ PQSGMII_CTRL_REG, val);
|
||||
+ if (ret)
|
||||
+ goto exit;
|
||||
+
|
||||
+ /* Workaround to enable AZ transmitting ability */
|
||||
+ val = __phy_package_read_mmd(phydev, QCA807X_PQSGMII_ADDR,
|
||||
+ MDIO_MMD_PMAPMD, PQSGMII_MODE_CTRL);
|
||||
+ val &= ~PQSGMII_MODE_CTRL_AZ_WORKAROUND_MASK;
|
||||
+ ret = __phy_package_write_mmd(phydev, QCA807X_PQSGMII_ADDR,
|
||||
+ MDIO_MMD_PMAPMD, PQSGMII_MODE_CTRL, val);
|
||||
+ if (ret)
|
||||
+ goto exit;
|
||||
+
|
||||
+ /* Set PQSGMII TX AMP strength */
|
||||
+ val = __phy_package_read(phydev, QCA807X_PQSGMII_ADDR,
|
||||
+ PQSGMII_DRIVE_CONTROL_1);
|
||||
+ val &= ~PQSGMII_TX_DRIVER_MASK;
|
||||
+ val |= FIELD_PREP(PQSGMII_TX_DRIVER_MASK, priv->tx_drive_strength);
|
||||
+ ret = __phy_package_write(phydev, QCA807X_PQSGMII_ADDR,
|
||||
+ PQSGMII_DRIVE_CONTROL_1, val);
|
||||
+ if (ret)
|
||||
+ goto exit;
|
||||
+
|
||||
+ /* Prevent PSGMII going into hibernation via PSGMII self test */
|
||||
+ val = __phy_package_read_mmd(phydev, QCA807X_COMBO_ADDR,
|
||||
+ MDIO_MMD_PCS, PQSGMII_MMD3_SERDES_CONTROL);
|
||||
+ val &= ~BIT(1);
|
||||
+ ret = __phy_package_write_mmd(phydev, QCA807X_COMBO_ADDR,
|
||||
+ MDIO_MMD_PCS, PQSGMII_MMD3_SERDES_CONTROL, val);
|
||||
+
|
||||
+exit:
|
||||
+ phy_unlock_mdio_bus(phydev);
|
||||
+
|
||||
+ return ret;
|
||||
+}
|
||||
+
|
||||
+static int qca807x_sfp_insert(void *upstream, const struct sfp_eeprom_id *id)
|
||||
+{
|
||||
+ struct phy_device *phydev = upstream;
|
||||
+ __ETHTOOL_DECLARE_LINK_MODE_MASK(support) = { 0, };
|
||||
+ phy_interface_t iface;
|
||||
+ int ret;
|
||||
+ DECLARE_PHY_INTERFACE_MASK(interfaces);
|
||||
+
|
||||
+ sfp_parse_support(phydev->sfp_bus, id, support, interfaces);
|
||||
+ iface = sfp_select_interface(phydev->sfp_bus, support);
|
||||
+
|
||||
+ dev_info(&phydev->mdio.dev, "%s SFP module inserted\n", phy_modes(iface));
|
||||
+
|
||||
+ switch (iface) {
|
||||
+ case PHY_INTERFACE_MODE_1000BASEX:
|
||||
+ case PHY_INTERFACE_MODE_100BASEX:
|
||||
+ /* Set PHY mode to PSGMII combo (1/4 copper + combo ports) mode */
|
||||
+ ret = phy_modify(phydev,
|
||||
+ QCA807X_CHIP_CONFIGURATION,
|
||||
+ QCA807X_CHIP_CONFIGURATION_MODE_CFG_MASK,
|
||||
+ QCA807X_CHIP_CONFIGURATION_MODE_PSGMII_FIBER);
|
||||
+ /* Enable fiber mode autodection (1000Base-X or 100Base-FX) */
|
||||
+ ret = phy_set_bits_mmd(phydev,
|
||||
+ MDIO_MMD_AN,
|
||||
+ QCA807X_MMD7_FIBER_MODE_AUTO_DETECTION,
|
||||
+ QCA807X_MMD7_FIBER_MODE_AUTO_DETECTION_EN);
|
||||
+ /* Select fiber page */
|
||||
+ ret = phy_clear_bits(phydev,
|
||||
+ QCA807X_CHIP_CONFIGURATION,
|
||||
+ QCA807X_BT_BX_REG_SEL);
|
||||
+
|
||||
+ phydev->port = PORT_FIBRE;
|
||||
+ break;
|
||||
+ default:
|
||||
+ dev_err(&phydev->mdio.dev, "Incompatible SFP module inserted\n");
|
||||
+ return -EINVAL;
|
||||
+ }
|
||||
+
|
||||
+ return ret;
|
||||
+}
|
||||
+
|
||||
+static void qca807x_sfp_remove(void *upstream)
|
||||
+{
|
||||
+ struct phy_device *phydev = upstream;
|
||||
+
|
||||
+ /* Select copper page */
|
||||
+ phy_set_bits(phydev,
|
||||
+ QCA807X_CHIP_CONFIGURATION,
|
||||
+ QCA807X_BT_BX_REG_SEL);
|
||||
+
|
||||
+ phydev->port = PORT_TP;
|
||||
+}
|
||||
+
|
||||
+static const struct sfp_upstream_ops qca807x_sfp_ops = {
|
||||
+ .attach = phy_sfp_attach,
|
||||
+ .detach = phy_sfp_detach,
|
||||
+ .module_insert = qca807x_sfp_insert,
|
||||
+ .module_remove = qca807x_sfp_remove,
|
||||
+};
|
||||
+
|
||||
+static int qca807x_probe(struct phy_device *phydev)
|
||||
+{
|
||||
+ struct device_node *node = phydev->mdio.dev.of_node;
|
||||
+ struct qca807x_shared_priv *shared_priv;
|
||||
+ struct device *dev = &phydev->mdio.dev;
|
||||
+ struct phy_package_shared *shared;
|
||||
+ struct qca807x_priv *priv;
|
||||
+ int ret;
|
||||
+
|
||||
+ ret = devm_of_phy_package_join(dev, phydev, sizeof(*shared_priv));
|
||||
+ if (ret)
|
||||
+ return ret;
|
||||
+
|
||||
+ if (phy_package_probe_once(phydev)) {
|
||||
+ ret = qca807x_phy_package_probe_once(phydev);
|
||||
+ if (ret)
|
||||
+ return ret;
|
||||
+ }
|
||||
+
|
||||
+ shared = phydev->shared;
|
||||
+ shared_priv = shared->priv;
|
||||
+
|
||||
+ /* Make sure PHY follow PHY package mode if enforced */
|
||||
+ if (shared_priv->package_mode != PHY_INTERFACE_MODE_NA &&
|
||||
+ phydev->interface != shared_priv->package_mode)
|
||||
+ return -EINVAL;
|
||||
+
|
||||
+ priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
|
||||
+ if (!priv)
|
||||
+ return -ENOMEM;
|
||||
+
|
||||
+ priv->dac_full_amplitude = of_property_read_bool(node, "qcom,dac-full-amplitude");
|
||||
+ priv->dac_full_bias_current = of_property_read_bool(node, "qcom,dac-full-bias-current");
|
||||
+ priv->dac_disable_bias_current_tweak = of_property_read_bool(node,
|
||||
+ "qcom,dac-disable-bias-current-tweak");
|
||||
+
|
||||
+ if (IS_ENABLED(CONFIG_GPIOLIB)) {
|
||||
+ /* Do not register a GPIO controller unless flagged for it */
|
||||
+ if (of_property_read_bool(node, "gpio-controller")) {
|
||||
+ ret = qca807x_gpio(phydev);
|
||||
+ if (ret)
|
||||
+ return ret;
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ /* Attach SFP bus on combo port*/
|
||||
+ if (phy_read(phydev, QCA807X_CHIP_CONFIGURATION)) {
|
||||
+ ret = phy_sfp_probe(phydev, &qca807x_sfp_ops);
|
||||
+ if (ret)
|
||||
+ return ret;
|
||||
+ linkmode_set_bit(ETHTOOL_LINK_MODE_FIBRE_BIT, phydev->supported);
|
||||
+ linkmode_set_bit(ETHTOOL_LINK_MODE_FIBRE_BIT, phydev->advertising);
|
||||
+ }
|
||||
+
|
||||
+ phydev->priv = priv;
|
||||
+
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
+static int qca807x_config_init(struct phy_device *phydev)
|
||||
+{
|
||||
+ struct qca807x_priv *priv = phydev->priv;
|
||||
+ u16 control_dac;
|
||||
+ int ret;
|
||||
+
|
||||
+ if (phy_package_init_once(phydev)) {
|
||||
+ ret = qca807x_phy_package_config_init_once(phydev);
|
||||
+ if (ret)
|
||||
+ return ret;
|
||||
+ }
|
||||
+
|
||||
+ control_dac = phy_read_mmd(phydev, MDIO_MMD_AN,
|
||||
+ QCA807X_MMD7_1000BASE_T_POWER_SAVE_PER_CABLE_LENGTH);
|
||||
+ control_dac &= ~QCA807X_CONTROL_DAC_MASK;
|
||||
+ if (!priv->dac_full_amplitude)
|
||||
+ control_dac |= QCA807X_CONTROL_DAC_DSP_AMPLITUDE;
|
||||
+ if (!priv->dac_full_amplitude)
|
||||
+ control_dac |= QCA807X_CONTROL_DAC_DSP_BIAS_CURRENT;
|
||||
+ if (!priv->dac_disable_bias_current_tweak)
|
||||
+ control_dac |= QCA807X_CONTROL_DAC_BIAS_CURRENT_TWEAK;
|
||||
+ return phy_write_mmd(phydev, MDIO_MMD_AN,
|
||||
+ QCA807X_MMD7_1000BASE_T_POWER_SAVE_PER_CABLE_LENGTH,
|
||||
+ control_dac);
|
||||
+}
|
||||
+
|
||||
+static struct phy_driver qca807x_drivers[] = {
|
||||
+ {
|
||||
+ PHY_ID_MATCH_EXACT(PHY_ID_QCA8072),
|
||||
+ .name = "Qualcomm QCA8072",
|
||||
+ .flags = PHY_POLL_CABLE_TEST,
|
||||
+ /* PHY_GBIT_FEATURES */
|
||||
+ .probe = qca807x_probe,
|
||||
+ .config_init = qca807x_config_init,
|
||||
+ .read_status = qca807x_read_status,
|
||||
+ .config_intr = at803x_config_intr,
|
||||
+ .handle_interrupt = at803x_handle_interrupt,
|
||||
+ .soft_reset = genphy_soft_reset,
|
||||
+ .get_tunable = at803x_get_tunable,
|
||||
+ .set_tunable = at803x_set_tunable,
|
||||
+ .resume = genphy_resume,
|
||||
+ .suspend = genphy_suspend,
|
||||
+ .cable_test_start = qca807x_cable_test_start,
|
||||
+ .cable_test_get_status = qca808x_cable_test_get_status,
|
||||
+ },
|
||||
+ {
|
||||
+ PHY_ID_MATCH_EXACT(PHY_ID_QCA8075),
|
||||
+ .name = "Qualcomm QCA8075",
|
||||
+ .flags = PHY_POLL_CABLE_TEST,
|
||||
+ /* PHY_GBIT_FEATURES */
|
||||
+ .probe = qca807x_probe,
|
||||
+ .config_init = qca807x_config_init,
|
||||
+ .read_status = qca807x_read_status,
|
||||
+ .config_intr = at803x_config_intr,
|
||||
+ .handle_interrupt = at803x_handle_interrupt,
|
||||
+ .soft_reset = genphy_soft_reset,
|
||||
+ .get_tunable = at803x_get_tunable,
|
||||
+ .set_tunable = at803x_set_tunable,
|
||||
+ .resume = genphy_resume,
|
||||
+ .suspend = genphy_suspend,
|
||||
+ .cable_test_start = qca807x_cable_test_start,
|
||||
+ .cable_test_get_status = qca808x_cable_test_get_status,
|
||||
+ },
|
||||
+};
|
||||
+module_phy_driver(qca807x_drivers);
|
||||
+
|
||||
+static struct mdio_device_id __maybe_unused qca807x_tbl[] = {
|
||||
+ { PHY_ID_MATCH_EXACT(PHY_ID_QCA8072) },
|
||||
+ { PHY_ID_MATCH_EXACT(PHY_ID_QCA8075) },
|
||||
+ { }
|
||||
+};
|
||||
+
|
||||
+MODULE_AUTHOR("Robert Marko <robert.marko@sartura.hr>");
|
||||
+MODULE_AUTHOR("Christian Marangi <ansuelsmth@gmail.com>");
|
||||
+MODULE_DESCRIPTION("Qualcomm QCA807x PHY driver");
|
||||
+MODULE_DEVICE_TABLE(mdio, qca807x_tbl);
|
||||
+MODULE_LICENSE("GPL");
|
@ -0,0 +1,179 @@
|
||||
From ee9d9807bee0e6af8ca2a4db6f0d1dc0e5b41f44 Mon Sep 17 00:00:00 2001
|
||||
From: Christian Marangi <ansuelsmth@gmail.com>
|
||||
Date: Tue, 6 Feb 2024 18:31:11 +0100
|
||||
Subject: [PATCH 08/10] net: phy: qcom: move common qca808x LED define to
|
||||
shared header
|
||||
|
||||
The LED implementation of qca808x and qca807x is the same but qca807x
|
||||
supports also Fiber port and have different hw control bits for Fiber
|
||||
port.
|
||||
|
||||
In preparation for qca807x introduction, move all the common define to
|
||||
shared header.
|
||||
|
||||
Signed-off-by: Christian Marangi <ansuelsmth@gmail.com>
|
||||
Reviewed-by: Andrew Lunn <andrew@lunn.ch>
|
||||
Signed-off-by: David S. Miller <davem@davemloft.net>
|
||||
---
|
||||
drivers/net/phy/qcom/qca808x.c | 65 ----------------------------------
|
||||
drivers/net/phy/qcom/qcom.h | 65 ++++++++++++++++++++++++++++++++++
|
||||
2 files changed, 65 insertions(+), 65 deletions(-)
|
||||
|
||||
--- a/drivers/net/phy/qcom/qca808x.c
|
||||
+++ b/drivers/net/phy/qcom/qca808x.c
|
||||
@@ -62,29 +62,6 @@
|
||||
#define QCA808X_DBG_AN_TEST 0xb
|
||||
#define QCA808X_HIBERNATION_EN BIT(15)
|
||||
|
||||
-#define QCA808X_MMD7_LED_GLOBAL 0x8073
|
||||
-#define QCA808X_LED_BLINK_1 GENMASK(11, 6)
|
||||
-#define QCA808X_LED_BLINK_2 GENMASK(5, 0)
|
||||
-/* Values are the same for both BLINK_1 and BLINK_2 */
|
||||
-#define QCA808X_LED_BLINK_FREQ_MASK GENMASK(5, 3)
|
||||
-#define QCA808X_LED_BLINK_FREQ_2HZ FIELD_PREP(QCA808X_LED_BLINK_FREQ_MASK, 0x0)
|
||||
-#define QCA808X_LED_BLINK_FREQ_4HZ FIELD_PREP(QCA808X_LED_BLINK_FREQ_MASK, 0x1)
|
||||
-#define QCA808X_LED_BLINK_FREQ_8HZ FIELD_PREP(QCA808X_LED_BLINK_FREQ_MASK, 0x2)
|
||||
-#define QCA808X_LED_BLINK_FREQ_16HZ FIELD_PREP(QCA808X_LED_BLINK_FREQ_MASK, 0x3)
|
||||
-#define QCA808X_LED_BLINK_FREQ_32HZ FIELD_PREP(QCA808X_LED_BLINK_FREQ_MASK, 0x4)
|
||||
-#define QCA808X_LED_BLINK_FREQ_64HZ FIELD_PREP(QCA808X_LED_BLINK_FREQ_MASK, 0x5)
|
||||
-#define QCA808X_LED_BLINK_FREQ_128HZ FIELD_PREP(QCA808X_LED_BLINK_FREQ_MASK, 0x6)
|
||||
-#define QCA808X_LED_BLINK_FREQ_256HZ FIELD_PREP(QCA808X_LED_BLINK_FREQ_MASK, 0x7)
|
||||
-#define QCA808X_LED_BLINK_DUTY_MASK GENMASK(2, 0)
|
||||
-#define QCA808X_LED_BLINK_DUTY_50_50 FIELD_PREP(QCA808X_LED_BLINK_DUTY_MASK, 0x0)
|
||||
-#define QCA808X_LED_BLINK_DUTY_75_25 FIELD_PREP(QCA808X_LED_BLINK_DUTY_MASK, 0x1)
|
||||
-#define QCA808X_LED_BLINK_DUTY_25_75 FIELD_PREP(QCA808X_LED_BLINK_DUTY_MASK, 0x2)
|
||||
-#define QCA808X_LED_BLINK_DUTY_33_67 FIELD_PREP(QCA808X_LED_BLINK_DUTY_MASK, 0x3)
|
||||
-#define QCA808X_LED_BLINK_DUTY_67_33 FIELD_PREP(QCA808X_LED_BLINK_DUTY_MASK, 0x4)
|
||||
-#define QCA808X_LED_BLINK_DUTY_17_83 FIELD_PREP(QCA808X_LED_BLINK_DUTY_MASK, 0x5)
|
||||
-#define QCA808X_LED_BLINK_DUTY_83_17 FIELD_PREP(QCA808X_LED_BLINK_DUTY_MASK, 0x6)
|
||||
-#define QCA808X_LED_BLINK_DUTY_8_92 FIELD_PREP(QCA808X_LED_BLINK_DUTY_MASK, 0x7)
|
||||
-
|
||||
#define QCA808X_MMD7_LED2_CTRL 0x8074
|
||||
#define QCA808X_MMD7_LED2_FORCE_CTRL 0x8075
|
||||
#define QCA808X_MMD7_LED1_CTRL 0x8076
|
||||
@@ -92,51 +69,9 @@
|
||||
#define QCA808X_MMD7_LED0_CTRL 0x8078
|
||||
#define QCA808X_MMD7_LED_CTRL(x) (0x8078 - ((x) * 2))
|
||||
|
||||
-/* LED hw control pattern is the same for every LED */
|
||||
-#define QCA808X_LED_PATTERN_MASK GENMASK(15, 0)
|
||||
-#define QCA808X_LED_SPEED2500_ON BIT(15)
|
||||
-#define QCA808X_LED_SPEED2500_BLINK BIT(14)
|
||||
-/* Follow blink trigger even if duplex or speed condition doesn't match */
|
||||
-#define QCA808X_LED_BLINK_CHECK_BYPASS BIT(13)
|
||||
-#define QCA808X_LED_FULL_DUPLEX_ON BIT(12)
|
||||
-#define QCA808X_LED_HALF_DUPLEX_ON BIT(11)
|
||||
-#define QCA808X_LED_TX_BLINK BIT(10)
|
||||
-#define QCA808X_LED_RX_BLINK BIT(9)
|
||||
-#define QCA808X_LED_TX_ON_10MS BIT(8)
|
||||
-#define QCA808X_LED_RX_ON_10MS BIT(7)
|
||||
-#define QCA808X_LED_SPEED1000_ON BIT(6)
|
||||
-#define QCA808X_LED_SPEED100_ON BIT(5)
|
||||
-#define QCA808X_LED_SPEED10_ON BIT(4)
|
||||
-#define QCA808X_LED_COLLISION_BLINK BIT(3)
|
||||
-#define QCA808X_LED_SPEED1000_BLINK BIT(2)
|
||||
-#define QCA808X_LED_SPEED100_BLINK BIT(1)
|
||||
-#define QCA808X_LED_SPEED10_BLINK BIT(0)
|
||||
-
|
||||
#define QCA808X_MMD7_LED0_FORCE_CTRL 0x8079
|
||||
#define QCA808X_MMD7_LED_FORCE_CTRL(x) (0x8079 - ((x) * 2))
|
||||
|
||||
-/* LED force ctrl is the same for every LED
|
||||
- * No documentation exist for this, not even internal one
|
||||
- * with NDA as QCOM gives only info about configuring
|
||||
- * hw control pattern rules and doesn't indicate any way
|
||||
- * to force the LED to specific mode.
|
||||
- * These define comes from reverse and testing and maybe
|
||||
- * lack of some info or some info are not entirely correct.
|
||||
- * For the basic LED control and hw control these finding
|
||||
- * are enough to support LED control in all the required APIs.
|
||||
- *
|
||||
- * On doing some comparison with implementation with qca807x,
|
||||
- * it was found that it's 1:1 equal to it and confirms all the
|
||||
- * reverse done. It was also found further specification with the
|
||||
- * force mode and the blink modes.
|
||||
- */
|
||||
-#define QCA808X_LED_FORCE_EN BIT(15)
|
||||
-#define QCA808X_LED_FORCE_MODE_MASK GENMASK(14, 13)
|
||||
-#define QCA808X_LED_FORCE_BLINK_1 FIELD_PREP(QCA808X_LED_FORCE_MODE_MASK, 0x3)
|
||||
-#define QCA808X_LED_FORCE_BLINK_2 FIELD_PREP(QCA808X_LED_FORCE_MODE_MASK, 0x2)
|
||||
-#define QCA808X_LED_FORCE_ON FIELD_PREP(QCA808X_LED_FORCE_MODE_MASK, 0x1)
|
||||
-#define QCA808X_LED_FORCE_OFF FIELD_PREP(QCA808X_LED_FORCE_MODE_MASK, 0x0)
|
||||
-
|
||||
#define QCA808X_MMD7_LED_POLARITY_CTRL 0x901a
|
||||
/* QSDK sets by default 0x46 to this reg that sets BIT 6 for
|
||||
* LED to active high. It's not clear what BIT 3 and BIT 4 does.
|
||||
--- a/drivers/net/phy/qcom/qcom.h
|
||||
+++ b/drivers/net/phy/qcom/qcom.h
|
||||
@@ -103,6 +103,71 @@
|
||||
/* Added for reference of existence but should be handled by wait_for_completion already */
|
||||
#define QCA808X_CDT_STATUS_STAT_BUSY (BIT(1) | BIT(3))
|
||||
|
||||
+#define QCA808X_MMD7_LED_GLOBAL 0x8073
|
||||
+#define QCA808X_LED_BLINK_1 GENMASK(11, 6)
|
||||
+#define QCA808X_LED_BLINK_2 GENMASK(5, 0)
|
||||
+/* Values are the same for both BLINK_1 and BLINK_2 */
|
||||
+#define QCA808X_LED_BLINK_FREQ_MASK GENMASK(5, 3)
|
||||
+#define QCA808X_LED_BLINK_FREQ_2HZ FIELD_PREP(QCA808X_LED_BLINK_FREQ_MASK, 0x0)
|
||||
+#define QCA808X_LED_BLINK_FREQ_4HZ FIELD_PREP(QCA808X_LED_BLINK_FREQ_MASK, 0x1)
|
||||
+#define QCA808X_LED_BLINK_FREQ_8HZ FIELD_PREP(QCA808X_LED_BLINK_FREQ_MASK, 0x2)
|
||||
+#define QCA808X_LED_BLINK_FREQ_16HZ FIELD_PREP(QCA808X_LED_BLINK_FREQ_MASK, 0x3)
|
||||
+#define QCA808X_LED_BLINK_FREQ_32HZ FIELD_PREP(QCA808X_LED_BLINK_FREQ_MASK, 0x4)
|
||||
+#define QCA808X_LED_BLINK_FREQ_64HZ FIELD_PREP(QCA808X_LED_BLINK_FREQ_MASK, 0x5)
|
||||
+#define QCA808X_LED_BLINK_FREQ_128HZ FIELD_PREP(QCA808X_LED_BLINK_FREQ_MASK, 0x6)
|
||||
+#define QCA808X_LED_BLINK_FREQ_256HZ FIELD_PREP(QCA808X_LED_BLINK_FREQ_MASK, 0x7)
|
||||
+#define QCA808X_LED_BLINK_DUTY_MASK GENMASK(2, 0)
|
||||
+#define QCA808X_LED_BLINK_DUTY_50_50 FIELD_PREP(QCA808X_LED_BLINK_DUTY_MASK, 0x0)
|
||||
+#define QCA808X_LED_BLINK_DUTY_75_25 FIELD_PREP(QCA808X_LED_BLINK_DUTY_MASK, 0x1)
|
||||
+#define QCA808X_LED_BLINK_DUTY_25_75 FIELD_PREP(QCA808X_LED_BLINK_DUTY_MASK, 0x2)
|
||||
+#define QCA808X_LED_BLINK_DUTY_33_67 FIELD_PREP(QCA808X_LED_BLINK_DUTY_MASK, 0x3)
|
||||
+#define QCA808X_LED_BLINK_DUTY_67_33 FIELD_PREP(QCA808X_LED_BLINK_DUTY_MASK, 0x4)
|
||||
+#define QCA808X_LED_BLINK_DUTY_17_83 FIELD_PREP(QCA808X_LED_BLINK_DUTY_MASK, 0x5)
|
||||
+#define QCA808X_LED_BLINK_DUTY_83_17 FIELD_PREP(QCA808X_LED_BLINK_DUTY_MASK, 0x6)
|
||||
+#define QCA808X_LED_BLINK_DUTY_8_92 FIELD_PREP(QCA808X_LED_BLINK_DUTY_MASK, 0x7)
|
||||
+
|
||||
+/* LED hw control pattern is the same for every LED */
|
||||
+#define QCA808X_LED_PATTERN_MASK GENMASK(15, 0)
|
||||
+#define QCA808X_LED_SPEED2500_ON BIT(15)
|
||||
+#define QCA808X_LED_SPEED2500_BLINK BIT(14)
|
||||
+/* Follow blink trigger even if duplex or speed condition doesn't match */
|
||||
+#define QCA808X_LED_BLINK_CHECK_BYPASS BIT(13)
|
||||
+#define QCA808X_LED_FULL_DUPLEX_ON BIT(12)
|
||||
+#define QCA808X_LED_HALF_DUPLEX_ON BIT(11)
|
||||
+#define QCA808X_LED_TX_BLINK BIT(10)
|
||||
+#define QCA808X_LED_RX_BLINK BIT(9)
|
||||
+#define QCA808X_LED_TX_ON_10MS BIT(8)
|
||||
+#define QCA808X_LED_RX_ON_10MS BIT(7)
|
||||
+#define QCA808X_LED_SPEED1000_ON BIT(6)
|
||||
+#define QCA808X_LED_SPEED100_ON BIT(5)
|
||||
+#define QCA808X_LED_SPEED10_ON BIT(4)
|
||||
+#define QCA808X_LED_COLLISION_BLINK BIT(3)
|
||||
+#define QCA808X_LED_SPEED1000_BLINK BIT(2)
|
||||
+#define QCA808X_LED_SPEED100_BLINK BIT(1)
|
||||
+#define QCA808X_LED_SPEED10_BLINK BIT(0)
|
||||
+
|
||||
+/* LED force ctrl is the same for every LED
|
||||
+ * No documentation exist for this, not even internal one
|
||||
+ * with NDA as QCOM gives only info about configuring
|
||||
+ * hw control pattern rules and doesn't indicate any way
|
||||
+ * to force the LED to specific mode.
|
||||
+ * These define comes from reverse and testing and maybe
|
||||
+ * lack of some info or some info are not entirely correct.
|
||||
+ * For the basic LED control and hw control these finding
|
||||
+ * are enough to support LED control in all the required APIs.
|
||||
+ *
|
||||
+ * On doing some comparison with implementation with qca807x,
|
||||
+ * it was found that it's 1:1 equal to it and confirms all the
|
||||
+ * reverse done. It was also found further specification with the
|
||||
+ * force mode and the blink modes.
|
||||
+ */
|
||||
+#define QCA808X_LED_FORCE_EN BIT(15)
|
||||
+#define QCA808X_LED_FORCE_MODE_MASK GENMASK(14, 13)
|
||||
+#define QCA808X_LED_FORCE_BLINK_1 FIELD_PREP(QCA808X_LED_FORCE_MODE_MASK, 0x3)
|
||||
+#define QCA808X_LED_FORCE_BLINK_2 FIELD_PREP(QCA808X_LED_FORCE_MODE_MASK, 0x2)
|
||||
+#define QCA808X_LED_FORCE_ON FIELD_PREP(QCA808X_LED_FORCE_MODE_MASK, 0x1)
|
||||
+#define QCA808X_LED_FORCE_OFF FIELD_PREP(QCA808X_LED_FORCE_MODE_MASK, 0x0)
|
||||
+
|
||||
#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
|
@ -0,0 +1,172 @@
|
||||
From 47b930d0dd437af927145dba50a2e2ea1ba97c67 Mon Sep 17 00:00:00 2001
|
||||
From: Christian Marangi <ansuelsmth@gmail.com>
|
||||
Date: Tue, 6 Feb 2024 18:31:12 +0100
|
||||
Subject: [PATCH 09/10] net: phy: qcom: generalize some qca808x LED functions
|
||||
|
||||
Generalize some qca808x LED functions in preparation for qca807x LED
|
||||
support.
|
||||
|
||||
The LED implementation of qca808x and qca807x is the same but qca807x
|
||||
supports also Fiber port and have different hw control bits for Fiber
|
||||
port. To limit code duplication introduce micro functions that takes reg
|
||||
instead of LED index to tweak all the supported LED modes.
|
||||
|
||||
Signed-off-by: Christian Marangi <ansuelsmth@gmail.com>
|
||||
Signed-off-by: David S. Miller <davem@davemloft.net>
|
||||
---
|
||||
drivers/net/phy/qcom/qca808x.c | 38 +++-----------------
|
||||
drivers/net/phy/qcom/qcom-phy-lib.c | 54 +++++++++++++++++++++++++++++
|
||||
drivers/net/phy/qcom/qcom.h | 7 ++++
|
||||
3 files changed, 65 insertions(+), 34 deletions(-)
|
||||
|
||||
--- a/drivers/net/phy/qcom/qca808x.c
|
||||
+++ b/drivers/net/phy/qcom/qca808x.c
|
||||
@@ -437,9 +437,7 @@ static int qca808x_led_hw_control_enable
|
||||
return -EINVAL;
|
||||
|
||||
reg = QCA808X_MMD7_LED_FORCE_CTRL(index);
|
||||
-
|
||||
- return phy_clear_bits_mmd(phydev, MDIO_MMD_AN, reg,
|
||||
- QCA808X_LED_FORCE_EN);
|
||||
+ return qca808x_led_reg_hw_control_enable(phydev, reg);
|
||||
}
|
||||
|
||||
static int qca808x_led_hw_is_supported(struct phy_device *phydev, u8 index,
|
||||
@@ -480,16 +478,12 @@ static int qca808x_led_hw_control_set(st
|
||||
static bool qca808x_led_hw_control_status(struct phy_device *phydev, u8 index)
|
||||
{
|
||||
u16 reg;
|
||||
- int val;
|
||||
|
||||
if (index > 2)
|
||||
return false;
|
||||
|
||||
reg = QCA808X_MMD7_LED_FORCE_CTRL(index);
|
||||
-
|
||||
- val = phy_read_mmd(phydev, MDIO_MMD_AN, reg);
|
||||
-
|
||||
- return !(val & QCA808X_LED_FORCE_EN);
|
||||
+ return qca808x_led_reg_hw_control_status(phydev, reg);
|
||||
}
|
||||
|
||||
static int qca808x_led_hw_control_get(struct phy_device *phydev, u8 index,
|
||||
@@ -557,44 +551,20 @@ static int qca808x_led_brightness_set(st
|
||||
}
|
||||
|
||||
reg = QCA808X_MMD7_LED_FORCE_CTRL(index);
|
||||
-
|
||||
- return phy_modify_mmd(phydev, MDIO_MMD_AN, reg,
|
||||
- QCA808X_LED_FORCE_EN | QCA808X_LED_FORCE_MODE_MASK,
|
||||
- QCA808X_LED_FORCE_EN | (value ? QCA808X_LED_FORCE_ON :
|
||||
- QCA808X_LED_FORCE_OFF));
|
||||
+ return qca808x_led_reg_brightness_set(phydev, reg, value);
|
||||
}
|
||||
|
||||
static int qca808x_led_blink_set(struct phy_device *phydev, u8 index,
|
||||
unsigned long *delay_on,
|
||||
unsigned long *delay_off)
|
||||
{
|
||||
- int ret;
|
||||
u16 reg;
|
||||
|
||||
if (index > 2)
|
||||
return -EINVAL;
|
||||
|
||||
reg = QCA808X_MMD7_LED_FORCE_CTRL(index);
|
||||
-
|
||||
- /* Set blink to 50% off, 50% on at 4Hz by default */
|
||||
- ret = phy_modify_mmd(phydev, MDIO_MMD_AN, QCA808X_MMD7_LED_GLOBAL,
|
||||
- QCA808X_LED_BLINK_FREQ_MASK | QCA808X_LED_BLINK_DUTY_MASK,
|
||||
- QCA808X_LED_BLINK_FREQ_4HZ | QCA808X_LED_BLINK_DUTY_50_50);
|
||||
- if (ret)
|
||||
- return ret;
|
||||
-
|
||||
- /* We use BLINK_1 for normal blinking */
|
||||
- ret = phy_modify_mmd(phydev, MDIO_MMD_AN, reg,
|
||||
- QCA808X_LED_FORCE_EN | QCA808X_LED_FORCE_MODE_MASK,
|
||||
- QCA808X_LED_FORCE_EN | QCA808X_LED_FORCE_BLINK_1);
|
||||
- if (ret)
|
||||
- return ret;
|
||||
-
|
||||
- /* We set blink to 4Hz, aka 250ms */
|
||||
- *delay_on = 250 / 2;
|
||||
- *delay_off = 250 / 2;
|
||||
-
|
||||
- return 0;
|
||||
+ return qca808x_led_reg_blink_set(phydev, reg, delay_on, delay_off);
|
||||
}
|
||||
|
||||
static int qca808x_led_polarity_set(struct phy_device *phydev, int index,
|
||||
--- a/drivers/net/phy/qcom/qcom-phy-lib.c
|
||||
+++ b/drivers/net/phy/qcom/qcom-phy-lib.c
|
||||
@@ -620,3 +620,57 @@ int qca808x_cable_test_get_status(struct
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(qca808x_cable_test_get_status);
|
||||
+
|
||||
+int qca808x_led_reg_hw_control_enable(struct phy_device *phydev, u16 reg)
|
||||
+{
|
||||
+ return phy_clear_bits_mmd(phydev, MDIO_MMD_AN, reg,
|
||||
+ QCA808X_LED_FORCE_EN);
|
||||
+}
|
||||
+EXPORT_SYMBOL_GPL(qca808x_led_reg_hw_control_enable);
|
||||
+
|
||||
+bool qca808x_led_reg_hw_control_status(struct phy_device *phydev, u16 reg)
|
||||
+{
|
||||
+ int val;
|
||||
+
|
||||
+ val = phy_read_mmd(phydev, MDIO_MMD_AN, reg);
|
||||
+ return !(val & QCA808X_LED_FORCE_EN);
|
||||
+}
|
||||
+EXPORT_SYMBOL_GPL(qca808x_led_reg_hw_control_status);
|
||||
+
|
||||
+int qca808x_led_reg_brightness_set(struct phy_device *phydev,
|
||||
+ u16 reg, enum led_brightness value)
|
||||
+{
|
||||
+ return phy_modify_mmd(phydev, MDIO_MMD_AN, reg,
|
||||
+ QCA808X_LED_FORCE_EN | QCA808X_LED_FORCE_MODE_MASK,
|
||||
+ QCA808X_LED_FORCE_EN | (value ? QCA808X_LED_FORCE_ON :
|
||||
+ QCA808X_LED_FORCE_OFF));
|
||||
+}
|
||||
+EXPORT_SYMBOL_GPL(qca808x_led_reg_brightness_set);
|
||||
+
|
||||
+int qca808x_led_reg_blink_set(struct phy_device *phydev, u16 reg,
|
||||
+ unsigned long *delay_on,
|
||||
+ unsigned long *delay_off)
|
||||
+{
|
||||
+ int ret;
|
||||
+
|
||||
+ /* Set blink to 50% off, 50% on at 4Hz by default */
|
||||
+ ret = phy_modify_mmd(phydev, MDIO_MMD_AN, QCA808X_MMD7_LED_GLOBAL,
|
||||
+ QCA808X_LED_BLINK_FREQ_MASK | QCA808X_LED_BLINK_DUTY_MASK,
|
||||
+ QCA808X_LED_BLINK_FREQ_4HZ | QCA808X_LED_BLINK_DUTY_50_50);
|
||||
+ if (ret)
|
||||
+ return ret;
|
||||
+
|
||||
+ /* We use BLINK_1 for normal blinking */
|
||||
+ ret = phy_modify_mmd(phydev, MDIO_MMD_AN, reg,
|
||||
+ QCA808X_LED_FORCE_EN | QCA808X_LED_FORCE_MODE_MASK,
|
||||
+ QCA808X_LED_FORCE_EN | QCA808X_LED_FORCE_BLINK_1);
|
||||
+ if (ret)
|
||||
+ return ret;
|
||||
+
|
||||
+ /* We set blink to 4Hz, aka 250ms */
|
||||
+ *delay_on = 250 / 2;
|
||||
+ *delay_off = 250 / 2;
|
||||
+
|
||||
+ return 0;
|
||||
+}
|
||||
+EXPORT_SYMBOL_GPL(qca808x_led_reg_blink_set);
|
||||
--- a/drivers/net/phy/qcom/qcom.h
|
||||
+++ b/drivers/net/phy/qcom/qcom.h
|
||||
@@ -234,3 +234,10 @@ int at803x_cdt_start(struct phy_device *
|
||||
int at803x_cdt_wait_for_completion(struct phy_device *phydev,
|
||||
u32 cdt_en);
|
||||
int qca808x_cable_test_get_status(struct phy_device *phydev, bool *finished);
|
||||
+int qca808x_led_reg_hw_control_enable(struct phy_device *phydev, u16 reg);
|
||||
+bool qca808x_led_reg_hw_control_status(struct phy_device *phydev, u16 reg);
|
||||
+int qca808x_led_reg_brightness_set(struct phy_device *phydev,
|
||||
+ u16 reg, enum led_brightness value);
|
||||
+int qca808x_led_reg_blink_set(struct phy_device *phydev, u16 reg,
|
||||
+ unsigned long *delay_on,
|
||||
+ unsigned long *delay_off);
|
@ -0,0 +1,326 @@
|
||||
From f508a226b517a6a8afd78a317de46bc83e3e3d51 Mon Sep 17 00:00:00 2001
|
||||
From: Christian Marangi <ansuelsmth@gmail.com>
|
||||
Date: Tue, 6 Feb 2024 18:31:13 +0100
|
||||
Subject: [PATCH 10/10] net: phy: qca807x: add support for configurable LED
|
||||
|
||||
QCA8072/5 have up to 2 LEDs attached for PHY.
|
||||
|
||||
LEDs can be configured to be ON/hw blink or be set to HW control.
|
||||
|
||||
Hw blink mode is set to blink at 4Hz or 250ms.
|
||||
|
||||
PHY can support both copper (TP) or fiber (FIBRE) kind and supports
|
||||
different HW control modes based on the port type.
|
||||
|
||||
HW control modes supported for netdev trigger for copper ports are:
|
||||
- LINK_10
|
||||
- LINK_100
|
||||
- LINK_1000
|
||||
- TX
|
||||
- RX
|
||||
- FULL_DUPLEX
|
||||
- HALF_DUPLEX
|
||||
|
||||
HW control modes supported for netdev trigger for fiber ports are:
|
||||
- LINK_100
|
||||
- LINK_1000
|
||||
- TX
|
||||
- RX
|
||||
- FULL_DUPLEX
|
||||
- HALF_DUPLEX
|
||||
|
||||
LED support conflicts with GPIO controller feature and must be disabled
|
||||
if gpio-controller is used for the PHY.
|
||||
|
||||
Signed-off-by: Christian Marangi <ansuelsmth@gmail.com>
|
||||
Signed-off-by: David S. Miller <davem@davemloft.net>
|
||||
---
|
||||
drivers/net/phy/qcom/qca807x.c | 256 ++++++++++++++++++++++++++++++++-
|
||||
1 file changed, 254 insertions(+), 2 deletions(-)
|
||||
|
||||
--- a/drivers/net/phy/qcom/qca807x.c
|
||||
+++ b/drivers/net/phy/qcom/qca807x.c
|
||||
@@ -57,8 +57,18 @@
|
||||
#define QCA807X_MMD7_LED_CTRL(x) (0x8074 + ((x) * 2))
|
||||
#define QCA807X_MMD7_LED_FORCE_CTRL(x) (0x8075 + ((x) * 2))
|
||||
|
||||
-#define QCA807X_GPIO_FORCE_EN BIT(15)
|
||||
-#define QCA807X_GPIO_FORCE_MODE_MASK GENMASK(14, 13)
|
||||
+/* LED hw control pattern for fiber port */
|
||||
+#define QCA807X_LED_FIBER_PATTERN_MASK GENMASK(11, 1)
|
||||
+#define QCA807X_LED_FIBER_TXACT_BLK_EN BIT(10)
|
||||
+#define QCA807X_LED_FIBER_RXACT_BLK_EN BIT(9)
|
||||
+#define QCA807X_LED_FIBER_FDX_ON_EN BIT(6)
|
||||
+#define QCA807X_LED_FIBER_HDX_ON_EN BIT(5)
|
||||
+#define QCA807X_LED_FIBER_1000BX_ON_EN BIT(2)
|
||||
+#define QCA807X_LED_FIBER_100FX_ON_EN BIT(1)
|
||||
+
|
||||
+/* Some device repurpose the LED as GPIO out */
|
||||
+#define QCA807X_GPIO_FORCE_EN QCA808X_LED_FORCE_EN
|
||||
+#define QCA807X_GPIO_FORCE_MODE_MASK QCA808X_LED_FORCE_MODE_MASK
|
||||
|
||||
#define QCA807X_FUNCTION_CONTROL 0x10
|
||||
#define QCA807X_FC_MDI_CROSSOVER_MODE_MASK GENMASK(6, 5)
|
||||
@@ -121,6 +131,233 @@ static int qca807x_cable_test_start(stru
|
||||
return 0;
|
||||
}
|
||||
|
||||
+static int qca807x_led_parse_netdev(struct phy_device *phydev, unsigned long rules,
|
||||
+ u16 *offload_trigger)
|
||||
+{
|
||||
+ /* Parsing specific to netdev trigger */
|
||||
+ switch (phydev->port) {
|
||||
+ case PORT_TP:
|
||||
+ if (test_bit(TRIGGER_NETDEV_TX, &rules))
|
||||
+ *offload_trigger |= QCA808X_LED_TX_BLINK;
|
||||
+ if (test_bit(TRIGGER_NETDEV_RX, &rules))
|
||||
+ *offload_trigger |= QCA808X_LED_RX_BLINK;
|
||||
+ if (test_bit(TRIGGER_NETDEV_LINK_10, &rules))
|
||||
+ *offload_trigger |= QCA808X_LED_SPEED10_ON;
|
||||
+ if (test_bit(TRIGGER_NETDEV_LINK_100, &rules))
|
||||
+ *offload_trigger |= QCA808X_LED_SPEED100_ON;
|
||||
+ if (test_bit(TRIGGER_NETDEV_LINK_1000, &rules))
|
||||
+ *offload_trigger |= QCA808X_LED_SPEED1000_ON;
|
||||
+ if (test_bit(TRIGGER_NETDEV_HALF_DUPLEX, &rules))
|
||||
+ *offload_trigger |= QCA808X_LED_HALF_DUPLEX_ON;
|
||||
+ if (test_bit(TRIGGER_NETDEV_FULL_DUPLEX, &rules))
|
||||
+ *offload_trigger |= QCA808X_LED_FULL_DUPLEX_ON;
|
||||
+ break;
|
||||
+ case PORT_FIBRE:
|
||||
+ if (test_bit(TRIGGER_NETDEV_TX, &rules))
|
||||
+ *offload_trigger |= QCA807X_LED_FIBER_TXACT_BLK_EN;
|
||||
+ if (test_bit(TRIGGER_NETDEV_RX, &rules))
|
||||
+ *offload_trigger |= QCA807X_LED_FIBER_RXACT_BLK_EN;
|
||||
+ if (test_bit(TRIGGER_NETDEV_LINK_100, &rules))
|
||||
+ *offload_trigger |= QCA807X_LED_FIBER_100FX_ON_EN;
|
||||
+ if (test_bit(TRIGGER_NETDEV_LINK_1000, &rules))
|
||||
+ *offload_trigger |= QCA807X_LED_FIBER_1000BX_ON_EN;
|
||||
+ if (test_bit(TRIGGER_NETDEV_HALF_DUPLEX, &rules))
|
||||
+ *offload_trigger |= QCA807X_LED_FIBER_HDX_ON_EN;
|
||||
+ if (test_bit(TRIGGER_NETDEV_FULL_DUPLEX, &rules))
|
||||
+ *offload_trigger |= QCA807X_LED_FIBER_FDX_ON_EN;
|
||||
+ break;
|
||||
+ default:
|
||||
+ return -EOPNOTSUPP;
|
||||
+ }
|
||||
+
|
||||
+ if (rules && !*offload_trigger)
|
||||
+ return -EOPNOTSUPP;
|
||||
+
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
+static int qca807x_led_hw_control_enable(struct phy_device *phydev, u8 index)
|
||||
+{
|
||||
+ u16 reg;
|
||||
+
|
||||
+ if (index > 1)
|
||||
+ return -EINVAL;
|
||||
+
|
||||
+ reg = QCA807X_MMD7_LED_FORCE_CTRL(index);
|
||||
+ return qca808x_led_reg_hw_control_enable(phydev, reg);
|
||||
+}
|
||||
+
|
||||
+static int qca807x_led_hw_is_supported(struct phy_device *phydev, u8 index,
|
||||
+ unsigned long rules)
|
||||
+{
|
||||
+ u16 offload_trigger = 0;
|
||||
+
|
||||
+ if (index > 1)
|
||||
+ return -EINVAL;
|
||||
+
|
||||
+ return qca807x_led_parse_netdev(phydev, rules, &offload_trigger);
|
||||
+}
|
||||
+
|
||||
+static int qca807x_led_hw_control_set(struct phy_device *phydev, u8 index,
|
||||
+ unsigned long rules)
|
||||
+{
|
||||
+ u16 reg, mask, offload_trigger = 0;
|
||||
+ int ret;
|
||||
+
|
||||
+ if (index > 1)
|
||||
+ return -EINVAL;
|
||||
+
|
||||
+ ret = qca807x_led_parse_netdev(phydev, rules, &offload_trigger);
|
||||
+ if (ret)
|
||||
+ return ret;
|
||||
+
|
||||
+ ret = qca807x_led_hw_control_enable(phydev, index);
|
||||
+ if (ret)
|
||||
+ return ret;
|
||||
+
|
||||
+ switch (phydev->port) {
|
||||
+ case PORT_TP:
|
||||
+ reg = QCA807X_MMD7_LED_CTRL(index);
|
||||
+ mask = QCA808X_LED_PATTERN_MASK;
|
||||
+ break;
|
||||
+ case PORT_FIBRE:
|
||||
+ /* HW control pattern bits are in LED FORCE reg */
|
||||
+ reg = QCA807X_MMD7_LED_FORCE_CTRL(index);
|
||||
+ mask = QCA807X_LED_FIBER_PATTERN_MASK;
|
||||
+ break;
|
||||
+ default:
|
||||
+ return -EINVAL;
|
||||
+ }
|
||||
+
|
||||
+ return phy_modify_mmd(phydev, MDIO_MMD_AN, reg, mask,
|
||||
+ offload_trigger);
|
||||
+}
|
||||
+
|
||||
+static bool qca807x_led_hw_control_status(struct phy_device *phydev, u8 index)
|
||||
+{
|
||||
+ u16 reg;
|
||||
+
|
||||
+ if (index > 1)
|
||||
+ return false;
|
||||
+
|
||||
+ reg = QCA807X_MMD7_LED_FORCE_CTRL(index);
|
||||
+ return qca808x_led_reg_hw_control_status(phydev, reg);
|
||||
+}
|
||||
+
|
||||
+static int qca807x_led_hw_control_get(struct phy_device *phydev, u8 index,
|
||||
+ unsigned long *rules)
|
||||
+{
|
||||
+ u16 reg;
|
||||
+ int val;
|
||||
+
|
||||
+ if (index > 1)
|
||||
+ return -EINVAL;
|
||||
+
|
||||
+ /* Check if we have hw control enabled */
|
||||
+ if (qca807x_led_hw_control_status(phydev, index))
|
||||
+ return -EINVAL;
|
||||
+
|
||||
+ /* Parsing specific to netdev trigger */
|
||||
+ switch (phydev->port) {
|
||||
+ case PORT_TP:
|
||||
+ reg = QCA807X_MMD7_LED_CTRL(index);
|
||||
+ val = phy_read_mmd(phydev, MDIO_MMD_AN, reg);
|
||||
+ if (val & QCA808X_LED_TX_BLINK)
|
||||
+ set_bit(TRIGGER_NETDEV_TX, rules);
|
||||
+ if (val & QCA808X_LED_RX_BLINK)
|
||||
+ set_bit(TRIGGER_NETDEV_RX, rules);
|
||||
+ if (val & QCA808X_LED_SPEED10_ON)
|
||||
+ set_bit(TRIGGER_NETDEV_LINK_10, rules);
|
||||
+ if (val & QCA808X_LED_SPEED100_ON)
|
||||
+ set_bit(TRIGGER_NETDEV_LINK_100, rules);
|
||||
+ if (val & QCA808X_LED_SPEED1000_ON)
|
||||
+ set_bit(TRIGGER_NETDEV_LINK_1000, rules);
|
||||
+ if (val & QCA808X_LED_HALF_DUPLEX_ON)
|
||||
+ set_bit(TRIGGER_NETDEV_HALF_DUPLEX, rules);
|
||||
+ if (val & QCA808X_LED_FULL_DUPLEX_ON)
|
||||
+ set_bit(TRIGGER_NETDEV_FULL_DUPLEX, rules);
|
||||
+ break;
|
||||
+ case PORT_FIBRE:
|
||||
+ /* HW control pattern bits are in LED FORCE reg */
|
||||
+ reg = QCA807X_MMD7_LED_FORCE_CTRL(index);
|
||||
+ val = phy_read_mmd(phydev, MDIO_MMD_AN, reg);
|
||||
+ if (val & QCA807X_LED_FIBER_TXACT_BLK_EN)
|
||||
+ set_bit(TRIGGER_NETDEV_TX, rules);
|
||||
+ if (val & QCA807X_LED_FIBER_RXACT_BLK_EN)
|
||||
+ set_bit(TRIGGER_NETDEV_RX, rules);
|
||||
+ if (val & QCA807X_LED_FIBER_100FX_ON_EN)
|
||||
+ set_bit(TRIGGER_NETDEV_LINK_100, rules);
|
||||
+ if (val & QCA807X_LED_FIBER_1000BX_ON_EN)
|
||||
+ set_bit(TRIGGER_NETDEV_LINK_1000, rules);
|
||||
+ if (val & QCA807X_LED_FIBER_HDX_ON_EN)
|
||||
+ set_bit(TRIGGER_NETDEV_HALF_DUPLEX, rules);
|
||||
+ if (val & QCA807X_LED_FIBER_FDX_ON_EN)
|
||||
+ set_bit(TRIGGER_NETDEV_FULL_DUPLEX, rules);
|
||||
+ break;
|
||||
+ default:
|
||||
+ return -EINVAL;
|
||||
+ }
|
||||
+
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
+static int qca807x_led_hw_control_reset(struct phy_device *phydev, u8 index)
|
||||
+{
|
||||
+ u16 reg, mask;
|
||||
+
|
||||
+ if (index > 1)
|
||||
+ return -EINVAL;
|
||||
+
|
||||
+ switch (phydev->port) {
|
||||
+ case PORT_TP:
|
||||
+ reg = QCA807X_MMD7_LED_CTRL(index);
|
||||
+ mask = QCA808X_LED_PATTERN_MASK;
|
||||
+ break;
|
||||
+ case PORT_FIBRE:
|
||||
+ /* HW control pattern bits are in LED FORCE reg */
|
||||
+ reg = QCA807X_MMD7_LED_FORCE_CTRL(index);
|
||||
+ mask = QCA807X_LED_FIBER_PATTERN_MASK;
|
||||
+ break;
|
||||
+ default:
|
||||
+ return -EINVAL;
|
||||
+ }
|
||||
+
|
||||
+ return phy_clear_bits_mmd(phydev, MDIO_MMD_AN, reg, mask);
|
||||
+}
|
||||
+
|
||||
+static int qca807x_led_brightness_set(struct phy_device *phydev,
|
||||
+ u8 index, enum led_brightness value)
|
||||
+{
|
||||
+ u16 reg;
|
||||
+ int ret;
|
||||
+
|
||||
+ if (index > 1)
|
||||
+ return -EINVAL;
|
||||
+
|
||||
+ /* If we are setting off the LED reset any hw control rule */
|
||||
+ if (!value) {
|
||||
+ ret = qca807x_led_hw_control_reset(phydev, index);
|
||||
+ if (ret)
|
||||
+ return ret;
|
||||
+ }
|
||||
+
|
||||
+ reg = QCA807X_MMD7_LED_FORCE_CTRL(index);
|
||||
+ return qca808x_led_reg_brightness_set(phydev, reg, value);
|
||||
+}
|
||||
+
|
||||
+static int qca807x_led_blink_set(struct phy_device *phydev, u8 index,
|
||||
+ unsigned long *delay_on,
|
||||
+ unsigned long *delay_off)
|
||||
+{
|
||||
+ u16 reg;
|
||||
+
|
||||
+ if (index > 1)
|
||||
+ return -EINVAL;
|
||||
+
|
||||
+ reg = QCA807X_MMD7_LED_FORCE_CTRL(index);
|
||||
+ return qca808x_led_reg_blink_set(phydev, reg, delay_on, delay_off);
|
||||
+}
|
||||
+
|
||||
#ifdef CONFIG_GPIOLIB
|
||||
static int qca807x_gpio_get_direction(struct gpio_chip *gc, unsigned int offset)
|
||||
{
|
||||
@@ -496,6 +733,16 @@ static int qca807x_probe(struct phy_devi
|
||||
"qcom,dac-disable-bias-current-tweak");
|
||||
|
||||
if (IS_ENABLED(CONFIG_GPIOLIB)) {
|
||||
+ /* Make sure we don't have mixed leds node and gpio-controller
|
||||
+ * to prevent registering leds and having gpio-controller usage
|
||||
+ * conflicting with them.
|
||||
+ */
|
||||
+ if (of_find_property(node, "leds", NULL) &&
|
||||
+ of_find_property(node, "gpio-controller", NULL)) {
|
||||
+ phydev_err(phydev, "Invalid property detected. LEDs and gpio-controller are mutually exclusive.");
|
||||
+ return -EINVAL;
|
||||
+ }
|
||||
+
|
||||
/* Do not register a GPIO controller unless flagged for it */
|
||||
if (of_property_read_bool(node, "gpio-controller")) {
|
||||
ret = qca807x_gpio(phydev);
|
||||
@@ -580,6 +827,11 @@ static struct phy_driver qca807x_drivers
|
||||
.suspend = genphy_suspend,
|
||||
.cable_test_start = qca807x_cable_test_start,
|
||||
.cable_test_get_status = qca808x_cable_test_get_status,
|
||||
+ .led_brightness_set = qca807x_led_brightness_set,
|
||||
+ .led_blink_set = qca807x_led_blink_set,
|
||||
+ .led_hw_is_supported = qca807x_led_hw_is_supported,
|
||||
+ .led_hw_control_set = qca807x_led_hw_control_set,
|
||||
+ .led_hw_control_get = qca807x_led_hw_control_get,
|
||||
},
|
||||
};
|
||||
module_phy_driver(qca807x_drivers);
|
@ -56,7 +56,7 @@ Signed-off-by: David S. Miller <davem@davemloft.net>
|
||||
|
||||
mutex_init(&dev->lock);
|
||||
INIT_DELAYED_WORK(&dev->state_queue, phy_state_machine);
|
||||
@@ -2934,6 +2937,74 @@ static bool phy_drv_supports_irq(struct
|
||||
@@ -3037,6 +3040,74 @@ static bool phy_drv_supports_irq(struct
|
||||
return phydrv->config_intr && phydrv->handle_interrupt;
|
||||
}
|
||||
|
||||
@ -131,7 +131,7 @@ Signed-off-by: David S. Miller <davem@davemloft.net>
|
||||
/**
|
||||
* fwnode_mdio_find_device - Given a fwnode, find the mdio_device
|
||||
* @fwnode: pointer to the mdio_device's fwnode
|
||||
@@ -3112,6 +3183,11 @@ static int phy_probe(struct device *dev)
|
||||
@@ -3215,6 +3286,11 @@ static int phy_probe(struct device *dev)
|
||||
/* Set the state to READY by default */
|
||||
phydev->state = PHY_READY;
|
||||
|
||||
@ -153,7 +153,7 @@ Signed-off-by: David S. Miller <davem@davemloft.net>
|
||||
#include <linux/linkmode.h>
|
||||
#include <linux/netlink.h>
|
||||
#include <linux/mdio.h>
|
||||
@@ -603,6 +604,7 @@ struct macsec_ops;
|
||||
@@ -606,6 +607,7 @@ struct macsec_ops;
|
||||
* @phy_num_led_triggers: Number of triggers in @phy_led_triggers
|
||||
* @led_link_trigger: LED trigger for link up/down
|
||||
* @last_triggered: last LED trigger for link speed
|
||||
@ -161,7 +161,7 @@ Signed-off-by: David S. Miller <davem@davemloft.net>
|
||||
* @master_slave_set: User requested master/slave configuration
|
||||
* @master_slave_get: Current master/slave advertisement
|
||||
* @master_slave_state: Current master/slave configuration
|
||||
@@ -695,6 +697,7 @@ struct phy_device {
|
||||
@@ -698,6 +700,7 @@ struct phy_device {
|
||||
|
||||
struct phy_led_trigger *led_link_trigger;
|
||||
#endif
|
||||
@ -169,7 +169,7 @@ Signed-off-by: David S. Miller <davem@davemloft.net>
|
||||
|
||||
/*
|
||||
* Interrupt number for this PHY
|
||||
@@ -769,6 +772,19 @@ struct phy_tdr_config {
|
||||
@@ -772,6 +775,19 @@ struct phy_tdr_config {
|
||||
#define PHY_PAIR_ALL -1
|
||||
|
||||
/**
|
||||
|
@ -20,7 +20,7 @@ Signed-off-by: David S. Miller <davem@davemloft.net>
|
||||
|
||||
--- a/drivers/net/phy/phy_device.c
|
||||
+++ b/drivers/net/phy/phy_device.c
|
||||
@@ -2937,11 +2937,18 @@ static bool phy_drv_supports_irq(struct
|
||||
@@ -3040,11 +3040,18 @@ static bool phy_drv_supports_irq(struct
|
||||
return phydrv->config_intr && phydrv->handle_interrupt;
|
||||
}
|
||||
|
||||
@ -41,7 +41,7 @@ Signed-off-by: David S. Miller <davem@davemloft.net>
|
||||
}
|
||||
|
||||
static int of_phy_led(struct phy_device *phydev,
|
||||
@@ -2958,12 +2965,14 @@ static int of_phy_led(struct phy_device
|
||||
@@ -3061,12 +3068,14 @@ static int of_phy_led(struct phy_device
|
||||
return -ENOMEM;
|
||||
|
||||
cdev = &phyled->led_cdev;
|
||||
@ -59,7 +59,7 @@ Signed-off-by: David S. Miller <davem@davemloft.net>
|
||||
init_data.fwnode = of_fwnode_handle(led);
|
||||
--- a/include/linux/phy.h
|
||||
+++ b/include/linux/phy.h
|
||||
@@ -775,15 +775,19 @@ struct phy_tdr_config {
|
||||
@@ -778,15 +778,19 @@ struct phy_tdr_config {
|
||||
* struct phy_led: An LED driven by the PHY
|
||||
*
|
||||
* @list: List of LEDs
|
||||
@ -79,7 +79,7 @@ Signed-off-by: David S. Miller <davem@davemloft.net>
|
||||
/**
|
||||
* struct phy_driver - Driver structure for a particular PHY type
|
||||
*
|
||||
@@ -998,6 +1002,15 @@ struct phy_driver {
|
||||
@@ -1001,6 +1005,15 @@ struct phy_driver {
|
||||
int (*get_sqi)(struct phy_device *dev);
|
||||
/** @get_sqi_max: Get the maximum signal quality indication */
|
||||
int (*get_sqi_max)(struct phy_device *dev);
|
||||
|
@ -18,7 +18,7 @@ Signed-off-by: David S. Miller <davem@davemloft.net>
|
||||
|
||||
--- a/drivers/net/phy/phy_device.c
|
||||
+++ b/drivers/net/phy/phy_device.c
|
||||
@@ -2951,6 +2951,22 @@ static int phy_led_set_brightness(struct
|
||||
@@ -3054,6 +3054,22 @@ static int phy_led_set_brightness(struct
|
||||
return err;
|
||||
}
|
||||
|
||||
@ -41,7 +41,7 @@ Signed-off-by: David S. Miller <davem@davemloft.net>
|
||||
static int of_phy_led(struct phy_device *phydev,
|
||||
struct device_node *led)
|
||||
{
|
||||
@@ -2973,6 +2989,8 @@ static int of_phy_led(struct phy_device
|
||||
@@ -3076,6 +3092,8 @@ static int of_phy_led(struct phy_device
|
||||
|
||||
if (phydev->drv->led_brightness_set)
|
||||
cdev->brightness_set_blocking = phy_led_set_brightness;
|
||||
@ -52,7 +52,7 @@ Signed-off-by: David S. Miller <davem@davemloft.net>
|
||||
init_data.fwnode = of_fwnode_handle(led);
|
||||
--- a/include/linux/phy.h
|
||||
+++ b/include/linux/phy.h
|
||||
@@ -1011,6 +1011,18 @@ struct phy_driver {
|
||||
@@ -1014,6 +1014,18 @@ struct phy_driver {
|
||||
*/
|
||||
int (*led_brightness_set)(struct phy_device *dev,
|
||||
u8 index, enum led_brightness value);
|
||||
|
@ -53,7 +53,7 @@ Signed-off-by: Jakub Kicinski <kuba@kernel.org>
|
||||
tristate "MDIO Bus/PHY emulation with fixed speed/link PHYs"
|
||||
--- a/drivers/net/phy/phy_device.c
|
||||
+++ b/drivers/net/phy/phy_device.c
|
||||
@@ -3213,7 +3213,8 @@ static int phy_probe(struct device *dev)
|
||||
@@ -3316,7 +3316,8 @@ static int phy_probe(struct device *dev)
|
||||
/* Get the LEDs from the device tree, and instantiate standard
|
||||
* LEDs for them.
|
||||
*/
|
||||
|
@ -18,7 +18,7 @@ Signed-off-by: Jakub Kicinski <kuba@kernel.org>
|
||||
|
||||
--- a/drivers/net/phy/phy_device.c
|
||||
+++ b/drivers/net/phy/phy_device.c
|
||||
@@ -2974,6 +2974,7 @@ static int of_phy_led(struct phy_device
|
||||
@@ -3077,6 +3077,7 @@ static int of_phy_led(struct phy_device
|
||||
struct led_init_data init_data = {};
|
||||
struct led_classdev *cdev;
|
||||
struct phy_led *phyled;
|
||||
@ -26,7 +26,7 @@ Signed-off-by: Jakub Kicinski <kuba@kernel.org>
|
||||
int err;
|
||||
|
||||
phyled = devm_kzalloc(dev, sizeof(*phyled), GFP_KERNEL);
|
||||
@@ -2983,10 +2984,13 @@ static int of_phy_led(struct phy_device
|
||||
@@ -3086,10 +3087,13 @@ static int of_phy_led(struct phy_device
|
||||
cdev = &phyled->led_cdev;
|
||||
phyled->phydev = phydev;
|
||||
|
||||
|
@ -22,7 +22,7 @@ Signed-off-by: David S. Miller <davem@davemloft.net>
|
||||
|
||||
--- a/drivers/net/phy/phy_device.c
|
||||
+++ b/drivers/net/phy/phy_device.c
|
||||
@@ -2967,6 +2967,15 @@ static int phy_led_blink_set(struct led_
|
||||
@@ -3070,6 +3070,15 @@ static int phy_led_blink_set(struct led_
|
||||
return err;
|
||||
}
|
||||
|
||||
@ -38,7 +38,7 @@ Signed-off-by: David S. Miller <davem@davemloft.net>
|
||||
static int of_phy_led(struct phy_device *phydev,
|
||||
struct device_node *led)
|
||||
{
|
||||
@@ -3000,7 +3009,7 @@ static int of_phy_led(struct phy_device
|
||||
@@ -3103,7 +3112,7 @@ static int of_phy_led(struct phy_device
|
||||
init_data.fwnode = of_fwnode_handle(led);
|
||||
init_data.devname_mandatory = true;
|
||||
|
||||
@ -47,7 +47,7 @@ Signed-off-by: David S. Miller <davem@davemloft.net>
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
@@ -3029,6 +3038,7 @@ static int of_phy_leds(struct phy_device
|
||||
@@ -3132,6 +3141,7 @@ static int of_phy_leds(struct phy_device
|
||||
err = of_phy_led(phydev, led);
|
||||
if (err) {
|
||||
of_node_put(led);
|
||||
@ -55,7 +55,7 @@ Signed-off-by: David S. Miller <davem@davemloft.net>
|
||||
return err;
|
||||
}
|
||||
}
|
||||
@@ -3234,6 +3244,9 @@ static int phy_remove(struct device *dev
|
||||
@@ -3337,6 +3347,9 @@ static int phy_remove(struct device *dev
|
||||
|
||||
cancel_delayed_work_sync(&phydev->state_queue);
|
||||
|
||||
|
@ -23,7 +23,7 @@ Signed-off-by: Jakub Kicinski <kuba@kernel.org>
|
||||
|
||||
--- a/drivers/net/phy/phy_device.c
|
||||
+++ b/drivers/net/phy/phy_device.c
|
||||
@@ -2967,6 +2967,61 @@ static int phy_led_blink_set(struct led_
|
||||
@@ -3070,6 +3070,61 @@ static int phy_led_blink_set(struct led_
|
||||
return err;
|
||||
}
|
||||
|
||||
@ -85,7 +85,7 @@ Signed-off-by: Jakub Kicinski <kuba@kernel.org>
|
||||
static void phy_leds_unregister(struct phy_device *phydev)
|
||||
{
|
||||
struct phy_led *phyled;
|
||||
@@ -3004,6 +3059,19 @@ static int of_phy_led(struct phy_device
|
||||
@@ -3107,6 +3162,19 @@ static int of_phy_led(struct phy_device
|
||||
cdev->brightness_set_blocking = phy_led_set_brightness;
|
||||
if (phydev->drv->led_blink_set)
|
||||
cdev->blink_set = phy_led_blink_set;
|
||||
@ -107,7 +107,7 @@ Signed-off-by: Jakub Kicinski <kuba@kernel.org>
|
||||
init_data.fwnode = of_fwnode_handle(led);
|
||||
--- a/include/linux/phy.h
|
||||
+++ b/include/linux/phy.h
|
||||
@@ -1023,6 +1023,39 @@ struct phy_driver {
|
||||
@@ -1026,6 +1026,39 @@ struct phy_driver {
|
||||
int (*led_blink_set)(struct phy_device *dev, u8 index,
|
||||
unsigned long *delay_on,
|
||||
unsigned long *delay_off);
|
||||
|
@ -28,7 +28,7 @@ Signed-off-by: Jakub Kicinski <kuba@kernel.org>
|
||||
|
||||
--- a/drivers/net/phy/phy_device.c
|
||||
+++ b/drivers/net/phy/phy_device.c
|
||||
@@ -3037,6 +3037,7 @@ static int of_phy_led(struct phy_device
|
||||
@@ -3140,6 +3140,7 @@ static int of_phy_led(struct phy_device
|
||||
struct device *dev = &phydev->mdio.dev;
|
||||
struct led_init_data init_data = {};
|
||||
struct led_classdev *cdev;
|
||||
@ -36,7 +36,7 @@ Signed-off-by: Jakub Kicinski <kuba@kernel.org>
|
||||
struct phy_led *phyled;
|
||||
u32 index;
|
||||
int err;
|
||||
@@ -3054,6 +3055,21 @@ static int of_phy_led(struct phy_device
|
||||
@@ -3157,6 +3158,21 @@ static int of_phy_led(struct phy_device
|
||||
if (index > U8_MAX)
|
||||
return -EINVAL;
|
||||
|
||||
@ -60,7 +60,7 @@ Signed-off-by: Jakub Kicinski <kuba@kernel.org>
|
||||
cdev->brightness_set_blocking = phy_led_set_brightness;
|
||||
--- a/include/linux/phy.h
|
||||
+++ b/include/linux/phy.h
|
||||
@@ -788,6 +788,15 @@ struct phy_led {
|
||||
@@ -791,6 +791,15 @@ struct phy_led {
|
||||
|
||||
#define to_phy_led(d) container_of(d, struct phy_led, led_cdev)
|
||||
|
||||
@ -76,7 +76,7 @@ Signed-off-by: Jakub Kicinski <kuba@kernel.org>
|
||||
/**
|
||||
* struct phy_driver - Driver structure for a particular PHY type
|
||||
*
|
||||
@@ -1056,6 +1065,19 @@ struct phy_driver {
|
||||
@@ -1059,6 +1068,19 @@ struct phy_driver {
|
||||
int (*led_hw_control_get)(struct phy_device *dev, u8 index,
|
||||
unsigned long *rules);
|
||||
|
||||
|
@ -5078,6 +5078,7 @@ CONFIG_PWRSEQ_SIMPLE=y
|
||||
# CONFIG_QCA7000_SPI is not set
|
||||
# CONFIG_QCA7000_UART is not set
|
||||
# CONFIG_QCA83XX_PHY is not set
|
||||
# CONFIG_QCA807X_PHY is not set
|
||||
# CONFIG_QCA808X_PHY is not set
|
||||
# CONFIG_QCOM_A7PLL is not set
|
||||
# CONFIG_QCOM_BAM_DMUX is not set
|
||||
|
@ -11,7 +11,7 @@ Signed-off-by: Gabor Juhos <juhosg@openwrt.org>
|
||||
|
||||
--- a/drivers/net/phy/phy_device.c
|
||||
+++ b/drivers/net/phy/phy_device.c
|
||||
@@ -1756,6 +1756,9 @@ void phy_detach(struct phy_device *phyde
|
||||
@@ -1852,6 +1852,9 @@ void phy_detach(struct phy_device *phyde
|
||||
struct module *ndev_owner = NULL;
|
||||
struct mii_bus *bus;
|
||||
|
||||
@ -23,7 +23,7 @@ Signed-off-by: Gabor Juhos <juhosg@openwrt.org>
|
||||
sysfs_remove_link(&dev->dev.kobj, "phydev");
|
||||
--- a/include/linux/phy.h
|
||||
+++ b/include/linux/phy.h
|
||||
@@ -897,6 +897,12 @@ struct phy_driver {
|
||||
@@ -900,6 +900,12 @@ struct phy_driver {
|
||||
/** @handle_interrupt: Override default interrupt handling */
|
||||
irqreturn_t (*handle_interrupt)(struct phy_device *phydev);
|
||||
|
||||
|
@ -107,7 +107,7 @@ still required by target/linux/ramips/files/drivers/net/ethernet/ralink/mdio.c
|
||||
bool tx_pause, rx_pause;
|
||||
--- a/include/linux/phy.h
|
||||
+++ b/include/linux/phy.h
|
||||
@@ -736,7 +736,7 @@ struct phy_device {
|
||||
@@ -739,7 +739,7 @@ struct phy_device {
|
||||
|
||||
int pma_extable;
|
||||
|
||||
|
@ -37,7 +37,7 @@ Signed-off-by: John Crispin <blogic@openwrt.org>
|
||||
break;
|
||||
--- a/include/linux/phy.h
|
||||
+++ b/include/linux/phy.h
|
||||
@@ -644,6 +644,7 @@ struct phy_device {
|
||||
@@ -647,6 +647,7 @@ struct phy_device {
|
||||
unsigned downshifted_rate:1;
|
||||
unsigned is_on_sfp_module:1;
|
||||
unsigned mac_managed_pm:1;
|
||||
|
Loading…
Reference in New Issue
Block a user