123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341 |
- From 9eea577eb1155fe4a183bc5e7bf269b0b2e7a6ba Mon Sep 17 00:00:00 2001
- From: Christian Marangi <[email protected]>
- 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 <[email protected]>
- Signed-off-by: David S. Miller <[email protected]>
- ---
- 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
- @@ -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;
- };
- --- a/drivers/net/phy/mscc/mscc_main.c
- +++ b/drivers/net/phy/mscc/mscc_main.c
- @@ -711,7 +711,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 */
- @@ -722,7 +722,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
- @@ -1650,20 +1650,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
- @@ -1673,17 +1675,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);
- @@ -1695,9 +1697,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)
- @@ -1735,7 +1737,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);
- @@ -1754,7 +1756,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,
- @@ -1762,7 +1765,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;
- @@ -1772,7 +1775,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
- @@ -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;
- @@ -1969,10 +1970,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);
-
- int __init mdio_bus_init(void);
- void mdio_bus_exit(void);
- @@ -1995,46 +1996,65 @@ int __phy_hwtstamp_set(struct phy_device
- 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,
|