net: phy: extend PHY package API to support multiple global address
authorChristian Marangi <ansuelsmth@gmail.com>
Fri, 15 Dec 2023 13:15:32 +0000 (14:15 +0100)
committerDavid S. Miller <davem@davemloft.net>
Sun, 17 Dec 2023 20:10:07 +0000 (20:10 +0000)
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
drivers/net/phy/mscc/mscc.h
drivers/net/phy/mscc/mscc_main.c
drivers/net/phy/phy_device.c
include/linux/phy.h

index d43076592f81d44581856ccd816c6510935bf1aa..2eea3d09b1e6f8f1a74caf0f69ab2fecd0d19e7a 100644 (file)
 #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 phy_device *phydev, u16 rdb)
        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(struct phy_device *phydev,
        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);
index 7a962050a4d45e77707519826b7f135057034129..6a3d8a754eb8de6dfc1740208744f625fe328384 100644 (file)
@@ -416,6 +416,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;
 };
index 4171f01d34e5792e91dff50837a9869802c8c36c..6f74ce0ab1aad8a4a2d74b098a1625658d997824 100644 (file)
@@ -711,7 +711,7 @@ int phy_base_write(struct phy_device *phydev, u32 regnum, u16 val)
                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 */
@@ -722,7 +722,7 @@ int phy_base_read(struct phy_device *phydev, u32 regnum)
                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,
index d8e9335d415ca5a28ee0098d006164e7b8b015ad..0c52a9eff188dab05eaba0fa9eb0b3157a7aadda 100644 (file)
@@ -1651,20 +1651,22 @@ EXPORT_SYMBOL_GPL(phy_driver_is_genphy_10g);
 /**
  * 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
@@ -1674,17 +1676,17 @@ EXPORT_SYMBOL_GPL(phy_driver_is_genphy_10g);
  * 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);
@@ -1696,9 +1698,9 @@ int phy_package_join(struct phy_device *phydev, int addr, size_t priv_size)
                                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)
@@ -1736,7 +1738,7 @@ void phy_package_leave(struct phy_device *phydev)
                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);
@@ -1755,7 +1757,8 @@ static void devm_phy_package_leave(struct device *dev, void *res)
  * 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,
@@ -1763,7 +1766,7 @@ static void devm_phy_package_leave(struct device *dev, void *res)
  * 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;
@@ -1773,7 +1776,7 @@ int devm_phy_package_join(struct device *dev, struct phy_device *phydev,
        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;
index 4b13cc85c4f5b0b2ee9972109cb8da436fa27921..d653f660c39d70043dfb58989509f7721f2fa772 100644 (file)
@@ -327,7 +327,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
@@ -338,7 +339,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;
@@ -1976,10 +1977,10 @@ int phy_ethtool_get_link_ksettings(struct net_device *ndev,
 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);
 
 int __init mdio_bus_init(void);
 void mdio_bus_exit(void);
@@ -2002,46 +2003,65 @@ int __phy_hwtstamp_set(struct phy_device *phydev,
                       struct kernel_hwtstamp_config *config,
                       struct netlink_ext_ack *extack);
 
-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,