mirror of
https://github.com/openwrt/openwrt.git
synced 2024-12-25 16:31:13 +00:00
342 lines
12 KiB
Diff
342 lines
12 KiB
Diff
|
From 9eea577eb1155fe4a183bc5e7bf269b0b2e7a6ba Mon Sep 17 00:00:00 2001
|
||
|
From: Christian Marangi <ansuelsmth@gmail.com>
|
||
|
Date: Fri, 15 Dec 2023 14:15:32 +0100
|
||
|
Subject: [PATCH 2/4] net: phy: extend PHY package API to support multiple
|
||
|
global address
|
||
|
|
||
|
Current API for PHY package are limited to single address to configure
|
||
|
global settings for the PHY package.
|
||
|
|
||
|
It was found that some PHY package (for example the qca807x, a PHY
|
||
|
package that is shipped with a bundle of 5 PHY) requires multiple PHY
|
||
|
address to configure global settings. An example scenario is a PHY that
|
||
|
have a dedicated PHY for PSGMII/serdes calibrarion and have a specific
|
||
|
PHY in the package where the global PHY mode is set and affects every
|
||
|
other PHY in the package.
|
||
|
|
||
|
Change the API in the following way:
|
||
|
- Change phy_package_join() to take the base addr of the PHY package
|
||
|
instead of the global PHY addr.
|
||
|
- Make __/phy_package_write/read() require an additional arg that
|
||
|
select what global PHY address to use by passing the offset from the
|
||
|
base addr passed on phy_package_join().
|
||
|
|
||
|
Each user of this API is updated to follow this new implementation
|
||
|
following a pattern where an enum is defined to declare the offset of the
|
||
|
addr.
|
||
|
|
||
|
We also drop the check if shared is defined as any user of the
|
||
|
phy_package_read/write is expected to use phy_package_join first. Misuse
|
||
|
of this will correctly trigger a kernel panic for NULL pointer
|
||
|
exception.
|
||
|
|
||
|
Signed-off-by: Christian Marangi <ansuelsmth@gmail.com>
|
||
|
Signed-off-by: David S. Miller <davem@davemloft.net>
|
||
|
---
|
||
|
drivers/net/phy/bcm54140.c | 16 ++++++--
|
||
|
drivers/net/phy/mscc/mscc.h | 5 +++
|
||
|
drivers/net/phy/mscc/mscc_main.c | 4 +-
|
||
|
drivers/net/phy/phy_device.c | 35 +++++++++--------
|
||
|
include/linux/phy.h | 64 +++++++++++++++++++++-----------
|
||
|
5 files changed, 80 insertions(+), 44 deletions(-)
|
||
|
|
||
|
--- a/drivers/net/phy/bcm54140.c
|
||
|
+++ b/drivers/net/phy/bcm54140.c
|
||
|
@@ -128,6 +128,10 @@
|
||
|
#define BCM54140_DEFAULT_DOWNSHIFT 5
|
||
|
#define BCM54140_MAX_DOWNSHIFT 9
|
||
|
|
||
|
+enum bcm54140_global_phy {
|
||
|
+ BCM54140_BASE_ADDR = 0,
|
||
|
+};
|
||
|
+
|
||
|
struct bcm54140_priv {
|
||
|
int port;
|
||
|
int base_addr;
|
||
|
@@ -429,11 +433,13 @@ static int bcm54140_base_read_rdb(struct
|
||
|
int ret;
|
||
|
|
||
|
phy_lock_mdio_bus(phydev);
|
||
|
- ret = __phy_package_write(phydev, MII_BCM54XX_RDB_ADDR, rdb);
|
||
|
+ ret = __phy_package_write(phydev, BCM54140_BASE_ADDR,
|
||
|
+ MII_BCM54XX_RDB_ADDR, rdb);
|
||
|
if (ret < 0)
|
||
|
goto out;
|
||
|
|
||
|
- ret = __phy_package_read(phydev, MII_BCM54XX_RDB_DATA);
|
||
|
+ ret = __phy_package_read(phydev, BCM54140_BASE_ADDR,
|
||
|
+ MII_BCM54XX_RDB_DATA);
|
||
|
|
||
|
out:
|
||
|
phy_unlock_mdio_bus(phydev);
|
||
|
@@ -446,11 +452,13 @@ static int bcm54140_base_write_rdb(struc
|
||
|
int ret;
|
||
|
|
||
|
phy_lock_mdio_bus(phydev);
|
||
|
- ret = __phy_package_write(phydev, MII_BCM54XX_RDB_ADDR, rdb);
|
||
|
+ ret = __phy_package_write(phydev, BCM54140_BASE_ADDR,
|
||
|
+ MII_BCM54XX_RDB_ADDR, rdb);
|
||
|
if (ret < 0)
|
||
|
goto out;
|
||
|
|
||
|
- ret = __phy_package_write(phydev, MII_BCM54XX_RDB_DATA, val);
|
||
|
+ ret = __phy_package_write(phydev, BCM54140_BASE_ADDR,
|
||
|
+ MII_BCM54XX_RDB_DATA, val);
|
||
|
|
||
|
out:
|
||
|
phy_unlock_mdio_bus(phydev);
|
||
|
--- a/drivers/net/phy/mscc/mscc.h
|
||
|
+++ b/drivers/net/phy/mscc/mscc.h
|
||
|
@@ -414,6 +414,11 @@ struct vsc8531_private {
|
||
|
* gpio_lock: used for PHC operations. Common for all PHYs as the load/save GPIO
|
||
|
* is shared.
|
||
|
*/
|
||
|
+
|
||
|
+enum vsc85xx_global_phy {
|
||
|
+ VSC88XX_BASE_ADDR = 0,
|
||
|
+};
|
||
|
+
|
||
|
struct vsc85xx_shared_private {
|
||
|
struct mutex gpio_lock;
|
||
|
};
|
||
|
--- a/drivers/net/phy/mscc/mscc_main.c
|
||
|
+++ b/drivers/net/phy/mscc/mscc_main.c
|
||
|
@@ -700,7 +700,7 @@ int phy_base_write(struct phy_device *ph
|
||
|
dump_stack();
|
||
|
}
|
||
|
|
||
|
- return __phy_package_write(phydev, regnum, val);
|
||
|
+ return __phy_package_write(phydev, VSC88XX_BASE_ADDR, regnum, val);
|
||
|
}
|
||
|
|
||
|
/* phydev->bus->mdio_lock should be locked when using this function */
|
||
|
@@ -711,7 +711,7 @@ int phy_base_read(struct phy_device *phy
|
||
|
dump_stack();
|
||
|
}
|
||
|
|
||
|
- return __phy_package_read(phydev, regnum);
|
||
|
+ return __phy_package_read(phydev, VSC88XX_BASE_ADDR, regnum);
|
||
|
}
|
||
|
|
||
|
u32 vsc85xx_csr_read(struct phy_device *phydev,
|
||
|
--- a/drivers/net/phy/phy_device.c
|
||
|
+++ b/drivers/net/phy/phy_device.c
|
||
|
@@ -1602,20 +1602,22 @@ EXPORT_SYMBOL_GPL(phy_driver_is_genphy_1
|
||
|
/**
|
||
|
* phy_package_join - join a common PHY group
|
||
|
* @phydev: target phy_device struct
|
||
|
- * @addr: cookie and PHY address for global register access
|
||
|
+ * @base_addr: cookie and base PHY address of PHY package for offset
|
||
|
+ * calculation of global register access
|
||
|
* @priv_size: if non-zero allocate this amount of bytes for private data
|
||
|
*
|
||
|
* This joins a PHY group and provides a shared storage for all phydevs in
|
||
|
* this group. This is intended to be used for packages which contain
|
||
|
* more than one PHY, for example a quad PHY transceiver.
|
||
|
*
|
||
|
- * The addr parameter serves as a cookie which has to have the same value
|
||
|
- * for all members of one group and as a PHY address to access generic
|
||
|
- * registers of a PHY package. Usually, one of the PHY addresses of the
|
||
|
- * different PHYs in the package provides access to these global registers.
|
||
|
+ * The base_addr parameter serves as cookie which has to have the same values
|
||
|
+ * for all members of one group and as the base PHY address of the PHY package
|
||
|
+ * for offset calculation to access generic registers of a PHY package.
|
||
|
+ * Usually, one of the PHY addresses of the different PHYs in the package
|
||
|
+ * provides access to these global registers.
|
||
|
* The address which is given here, will be used in the phy_package_read()
|
||
|
- * and phy_package_write() convenience functions. If your PHY doesn't have
|
||
|
- * global registers you can just pick any of the PHY addresses.
|
||
|
+ * and phy_package_write() convenience functions as base and added to the
|
||
|
+ * passed offset in those functions.
|
||
|
*
|
||
|
* This will set the shared pointer of the phydev to the shared storage.
|
||
|
* If this is the first call for a this cookie the shared storage will be
|
||
|
@@ -1625,17 +1627,17 @@ EXPORT_SYMBOL_GPL(phy_driver_is_genphy_1
|
||
|
* Returns < 1 on error, 0 on success. Esp. calling phy_package_join()
|
||
|
* with the same cookie but a different priv_size is an error.
|
||
|
*/
|
||
|
-int phy_package_join(struct phy_device *phydev, int addr, size_t priv_size)
|
||
|
+int phy_package_join(struct phy_device *phydev, int base_addr, size_t priv_size)
|
||
|
{
|
||
|
struct mii_bus *bus = phydev->mdio.bus;
|
||
|
struct phy_package_shared *shared;
|
||
|
int ret;
|
||
|
|
||
|
- if (addr < 0 || addr >= PHY_MAX_ADDR)
|
||
|
+ if (base_addr < 0 || base_addr >= PHY_MAX_ADDR)
|
||
|
return -EINVAL;
|
||
|
|
||
|
mutex_lock(&bus->shared_lock);
|
||
|
- shared = bus->shared[addr];
|
||
|
+ shared = bus->shared[base_addr];
|
||
|
if (!shared) {
|
||
|
ret = -ENOMEM;
|
||
|
shared = kzalloc(sizeof(*shared), GFP_KERNEL);
|
||
|
@@ -1647,9 +1649,9 @@ int phy_package_join(struct phy_device *
|
||
|
goto err_free;
|
||
|
shared->priv_size = priv_size;
|
||
|
}
|
||
|
- shared->addr = addr;
|
||
|
+ shared->base_addr = base_addr;
|
||
|
refcount_set(&shared->refcnt, 1);
|
||
|
- bus->shared[addr] = shared;
|
||
|
+ bus->shared[base_addr] = shared;
|
||
|
} else {
|
||
|
ret = -EINVAL;
|
||
|
if (priv_size && priv_size != shared->priv_size)
|
||
|
@@ -1687,7 +1689,7 @@ void phy_package_leave(struct phy_device
|
||
|
return;
|
||
|
|
||
|
if (refcount_dec_and_mutex_lock(&shared->refcnt, &bus->shared_lock)) {
|
||
|
- bus->shared[shared->addr] = NULL;
|
||
|
+ bus->shared[shared->base_addr] = NULL;
|
||
|
mutex_unlock(&bus->shared_lock);
|
||
|
kfree(shared->priv);
|
||
|
kfree(shared);
|
||
|
@@ -1706,7 +1708,8 @@ static void devm_phy_package_leave(struc
|
||
|
* devm_phy_package_join - resource managed phy_package_join()
|
||
|
* @dev: device that is registering this PHY package
|
||
|
* @phydev: target phy_device struct
|
||
|
- * @addr: cookie and PHY address for global register access
|
||
|
+ * @base_addr: cookie and base PHY address of PHY package for offset
|
||
|
+ * calculation of global register access
|
||
|
* @priv_size: if non-zero allocate this amount of bytes for private data
|
||
|
*
|
||
|
* Managed phy_package_join(). Shared storage fetched by this function,
|
||
|
@@ -1714,7 +1717,7 @@ static void devm_phy_package_leave(struc
|
||
|
* phy_package_join() for more information.
|
||
|
*/
|
||
|
int devm_phy_package_join(struct device *dev, struct phy_device *phydev,
|
||
|
- int addr, size_t priv_size)
|
||
|
+ int base_addr, size_t priv_size)
|
||
|
{
|
||
|
struct phy_device **ptr;
|
||
|
int ret;
|
||
|
@@ -1724,7 +1727,7 @@ int devm_phy_package_join(struct device
|
||
|
if (!ptr)
|
||
|
return -ENOMEM;
|
||
|
|
||
|
- ret = phy_package_join(phydev, addr, priv_size);
|
||
|
+ ret = phy_package_join(phydev, base_addr, priv_size);
|
||
|
|
||
|
if (!ret) {
|
||
|
*ptr = phydev;
|
||
|
--- a/include/linux/phy.h
|
||
|
+++ b/include/linux/phy.h
|
||
|
@@ -319,7 +319,8 @@ struct mdio_bus_stats {
|
||
|
|
||
|
/**
|
||
|
* struct phy_package_shared - Shared information in PHY packages
|
||
|
- * @addr: Common PHY address used to combine PHYs in one package
|
||
|
+ * @base_addr: Base PHY address of PHY package used to combine PHYs
|
||
|
+ * in one package and for offset calculation of phy_package_read/write
|
||
|
* @refcnt: Number of PHYs connected to this shared data
|
||
|
* @flags: Initialization of PHY package
|
||
|
* @priv_size: Size of the shared private data @priv
|
||
|
@@ -330,7 +331,7 @@ struct mdio_bus_stats {
|
||
|
* phy_package_leave().
|
||
|
*/
|
||
|
struct phy_package_shared {
|
||
|
- u8 addr;
|
||
|
+ u8 base_addr;
|
||
|
refcount_t refcnt;
|
||
|
unsigned long flags;
|
||
|
size_t priv_size;
|
||
|
@@ -1763,10 +1764,10 @@ int phy_ethtool_get_link_ksettings(struc
|
||
|
int phy_ethtool_set_link_ksettings(struct net_device *ndev,
|
||
|
const struct ethtool_link_ksettings *cmd);
|
||
|
int phy_ethtool_nway_reset(struct net_device *ndev);
|
||
|
-int phy_package_join(struct phy_device *phydev, int addr, size_t priv_size);
|
||
|
+int phy_package_join(struct phy_device *phydev, int base_addr, 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 addr, size_t priv_size);
|
||
|
+ int base_addr, size_t priv_size);
|
||
|
|
||
|
#if IS_ENABLED(CONFIG_PHYLIB)
|
||
|
int __init mdio_bus_init(void);
|
||
|
@@ -1778,46 +1779,65 @@ int phy_ethtool_get_sset_count(struct ph
|
||
|
int phy_ethtool_get_stats(struct phy_device *phydev,
|
||
|
struct ethtool_stats *stats, u64 *data);
|
||
|
|
||
|
-static inline int phy_package_read(struct phy_device *phydev, u32 regnum)
|
||
|
+static inline int phy_package_address(struct phy_device *phydev,
|
||
|
+ unsigned int addr_offset)
|
||
|
{
|
||
|
struct phy_package_shared *shared = phydev->shared;
|
||
|
+ u8 base_addr = shared->base_addr;
|
||
|
|
||
|
- if (!shared)
|
||
|
+ if (addr_offset >= PHY_MAX_ADDR - base_addr)
|
||
|
return -EIO;
|
||
|
|
||
|
- return mdiobus_read(phydev->mdio.bus, shared->addr, regnum);
|
||
|
+ /* we know that addr will be in the range 0..31 and thus the
|
||
|
+ * implicit cast to a signed int is not a problem.
|
||
|
+ */
|
||
|
+ return base_addr + addr_offset;
|
||
|
}
|
||
|
|
||
|
-static inline int __phy_package_read(struct phy_device *phydev, u32 regnum)
|
||
|
+static inline int phy_package_read(struct phy_device *phydev,
|
||
|
+ unsigned int addr_offset, u32 regnum)
|
||
|
{
|
||
|
- struct phy_package_shared *shared = phydev->shared;
|
||
|
+ int addr = phy_package_address(phydev, addr_offset);
|
||
|
|
||
|
- if (!shared)
|
||
|
- return -EIO;
|
||
|
+ if (addr < 0)
|
||
|
+ return addr;
|
||
|
+
|
||
|
+ return mdiobus_read(phydev->mdio.bus, addr, regnum);
|
||
|
+}
|
||
|
+
|
||
|
+static inline int __phy_package_read(struct phy_device *phydev,
|
||
|
+ unsigned int addr_offset, u32 regnum)
|
||
|
+{
|
||
|
+ int addr = phy_package_address(phydev, addr_offset);
|
||
|
+
|
||
|
+ if (addr < 0)
|
||
|
+ return addr;
|
||
|
|
||
|
- return __mdiobus_read(phydev->mdio.bus, shared->addr, regnum);
|
||
|
+ return __mdiobus_read(phydev->mdio.bus, addr, regnum);
|
||
|
}
|
||
|
|
||
|
static inline int phy_package_write(struct phy_device *phydev,
|
||
|
- u32 regnum, u16 val)
|
||
|
+ unsigned int addr_offset, u32 regnum,
|
||
|
+ u16 val)
|
||
|
{
|
||
|
- struct phy_package_shared *shared = phydev->shared;
|
||
|
+ int addr = phy_package_address(phydev, addr_offset);
|
||
|
|
||
|
- if (!shared)
|
||
|
- return -EIO;
|
||
|
+ if (addr < 0)
|
||
|
+ return addr;
|
||
|
|
||
|
- return mdiobus_write(phydev->mdio.bus, shared->addr, regnum, val);
|
||
|
+ return mdiobus_write(phydev->mdio.bus, addr, regnum, val);
|
||
|
}
|
||
|
|
||
|
static inline int __phy_package_write(struct phy_device *phydev,
|
||
|
- u32 regnum, u16 val)
|
||
|
+ unsigned int addr_offset, u32 regnum,
|
||
|
+ u16 val)
|
||
|
{
|
||
|
- struct phy_package_shared *shared = phydev->shared;
|
||
|
+ int addr = phy_package_address(phydev, addr_offset);
|
||
|
|
||
|
- if (!shared)
|
||
|
- return -EIO;
|
||
|
+ if (addr < 0)
|
||
|
+ return addr;
|
||
|
|
||
|
- return __mdiobus_write(phydev->mdio.bus, shared->addr, regnum, val);
|
||
|
+ return __mdiobus_write(phydev->mdio.bus, addr, regnum, val);
|
||
|
}
|
||
|
|
||
|
static inline bool __phy_package_set_once(struct phy_device *phydev,
|