developer | 5d148cb | 2023-06-02 13:08:11 +0800 | [diff] [blame] | 1 | From 2dca4de7282d3003f3703f707d773f4dbbc0f28e Mon Sep 17 00:00:00 2001 |
| 2 | From: Sam Shih <sam.shih@mediatek.com> |
| 3 | Date: Fri, 2 Jun 2023 13:06:27 +0800 |
| 4 | Subject: [PATCH] |
| 5 | [networking][999-2701-v5.8-net-phy-add-concept-of-shared-storage-for-PHYs.patch] |
developer | 394d5eb | 2023-04-14 16:46:45 +0800 | [diff] [blame] | 6 | |
developer | 394d5eb | 2023-04-14 16:46:45 +0800 | [diff] [blame] | 7 | --- |
| 8 | drivers/net/phy/mdio_bus.c | 1 + |
| 9 | drivers/net/phy/phy_device.c | 138 +++++++++++++++++++++++++++++++++++ |
| 10 | include/linux/phy.h | 89 ++++++++++++++++++++++ |
| 11 | 3 files changed, 228 insertions(+) |
| 12 | |
developer | 5d148cb | 2023-06-02 13:08:11 +0800 | [diff] [blame] | 13 | diff --git a/drivers/net/phy/mdio_bus.c b/drivers/net/phy/mdio_bus.c |
| 14 | index fdf8221f4..d9f2cee33 100644 |
developer | 394d5eb | 2023-04-14 16:46:45 +0800 | [diff] [blame] | 15 | --- a/drivers/net/phy/mdio_bus.c |
| 16 | +++ b/drivers/net/phy/mdio_bus.c |
developer | 5d148cb | 2023-06-02 13:08:11 +0800 | [diff] [blame] | 17 | @@ -404,6 +404,7 @@ int __mdiobus_register(struct mii_bus *bus, struct module *owner) |
developer | 394d5eb | 2023-04-14 16:46:45 +0800 | [diff] [blame] | 18 | } |
| 19 | |
| 20 | mutex_init(&bus->mdio_lock); |
| 21 | + mutex_init(&bus->shared_lock); |
| 22 | |
| 23 | /* de-assert bus level PHY GPIO reset */ |
| 24 | gpiod = devm_gpiod_get_optional(&bus->dev, "reset", GPIOD_OUT_LOW); |
developer | 5d148cb | 2023-06-02 13:08:11 +0800 | [diff] [blame] | 25 | diff --git a/drivers/net/phy/phy_device.c b/drivers/net/phy/phy_device.c |
| 26 | index 0349801df..99f265a1c 100644 |
developer | 394d5eb | 2023-04-14 16:46:45 +0800 | [diff] [blame] | 27 | --- a/drivers/net/phy/phy_device.c |
| 28 | +++ b/drivers/net/phy/phy_device.c |
developer | 5d148cb | 2023-06-02 13:08:11 +0800 | [diff] [blame] | 29 | @@ -1447,6 +1447,144 @@ bool phy_driver_is_genphy_10g(struct phy_device *phydev) |
| 30 | } |
developer | 394d5eb | 2023-04-14 16:46:45 +0800 | [diff] [blame] | 31 | EXPORT_SYMBOL_GPL(phy_driver_is_genphy_10g); |
| 32 | |
developer | 5d148cb | 2023-06-02 13:08:11 +0800 | [diff] [blame] | 33 | +/** |
developer | 394d5eb | 2023-04-14 16:46:45 +0800 | [diff] [blame] | 34 | + * phy_package_join - join a common PHY group |
| 35 | + * @phydev: target phy_device struct |
| 36 | + * @addr: cookie and PHY address for global register access |
| 37 | + * @priv_size: if non-zero allocate this amount of bytes for private data |
| 38 | + * |
| 39 | + * This joins a PHY group and provides a shared storage for all phydevs in |
| 40 | + * this group. This is intended to be used for packages which contain |
| 41 | + * more than one PHY, for example a quad PHY transceiver. |
| 42 | + * |
| 43 | + * The addr parameter serves as a cookie which has to have the same value |
| 44 | + * for all members of one group and as a PHY address to access generic |
| 45 | + * registers of a PHY package. Usually, one of the PHY addresses of the |
| 46 | + * different PHYs in the package provides access to these global registers. |
| 47 | + * The address which is given here, will be used in the phy_package_read() |
| 48 | + * and phy_package_write() convenience functions. If your PHY doesn't have |
| 49 | + * global registers you can just pick any of the PHY addresses. |
| 50 | + * |
| 51 | + * This will set the shared pointer of the phydev to the shared storage. |
| 52 | + * If this is the first call for a this cookie the shared storage will be |
| 53 | + * allocated. If priv_size is non-zero, the given amount of bytes are |
| 54 | + * allocated for the priv member. |
| 55 | + * |
| 56 | + * Returns < 1 on error, 0 on success. Esp. calling phy_package_join() |
| 57 | + * with the same cookie but a different priv_size is an error. |
| 58 | + */ |
| 59 | +int phy_package_join(struct phy_device *phydev, int addr, size_t priv_size) |
| 60 | +{ |
| 61 | + struct mii_bus *bus = phydev->mdio.bus; |
| 62 | + struct phy_package_shared *shared; |
| 63 | + int ret; |
| 64 | + |
| 65 | + if (addr < 0 || addr >= PHY_MAX_ADDR) |
| 66 | + return -EINVAL; |
| 67 | + |
| 68 | + mutex_lock(&bus->shared_lock); |
| 69 | + shared = bus->shared[addr]; |
| 70 | + if (!shared) { |
| 71 | + ret = -ENOMEM; |
| 72 | + shared = kzalloc(sizeof(*shared), GFP_KERNEL); |
| 73 | + if (!shared) |
| 74 | + goto err_unlock; |
| 75 | + if (priv_size) { |
| 76 | + shared->priv = kzalloc(priv_size, GFP_KERNEL); |
| 77 | + if (!shared->priv) |
| 78 | + goto err_free; |
| 79 | + shared->priv_size = priv_size; |
| 80 | + } |
| 81 | + shared->addr = addr; |
| 82 | + refcount_set(&shared->refcnt, 1); |
| 83 | + bus->shared[addr] = shared; |
| 84 | + } else { |
| 85 | + ret = -EINVAL; |
| 86 | + if (priv_size && priv_size != shared->priv_size) |
| 87 | + goto err_unlock; |
| 88 | + refcount_inc(&shared->refcnt); |
| 89 | + } |
| 90 | + mutex_unlock(&bus->shared_lock); |
| 91 | + |
| 92 | + phydev->shared = shared; |
| 93 | + |
| 94 | + return 0; |
| 95 | + |
| 96 | +err_free: |
| 97 | + kfree(shared); |
| 98 | +err_unlock: |
| 99 | + mutex_unlock(&bus->shared_lock); |
| 100 | + return ret; |
| 101 | +} |
| 102 | +EXPORT_SYMBOL_GPL(phy_package_join); |
| 103 | + |
| 104 | +/** |
| 105 | + * phy_package_leave - leave a common PHY group |
| 106 | + * @phydev: target phy_device struct |
| 107 | + * |
| 108 | + * This leaves a PHY group created by phy_package_join(). If this phydev |
| 109 | + * was the last user of the shared data between the group, this data is |
| 110 | + * freed. Resets the phydev->shared pointer to NULL. |
| 111 | + */ |
| 112 | +void phy_package_leave(struct phy_device *phydev) |
| 113 | +{ |
| 114 | + struct phy_package_shared *shared = phydev->shared; |
| 115 | + struct mii_bus *bus = phydev->mdio.bus; |
| 116 | + |
| 117 | + if (!shared) |
| 118 | + return; |
| 119 | + |
| 120 | + if (refcount_dec_and_mutex_lock(&shared->refcnt, &bus->shared_lock)) { |
| 121 | + bus->shared[shared->addr] = NULL; |
| 122 | + mutex_unlock(&bus->shared_lock); |
| 123 | + kfree(shared->priv); |
| 124 | + kfree(shared); |
| 125 | + } |
| 126 | + |
| 127 | + phydev->shared = NULL; |
| 128 | +} |
| 129 | +EXPORT_SYMBOL_GPL(phy_package_leave); |
| 130 | + |
| 131 | +static void devm_phy_package_leave(struct device *dev, void *res) |
| 132 | +{ |
| 133 | + phy_package_leave(*(struct phy_device **)res); |
| 134 | +} |
| 135 | + |
| 136 | +/** |
| 137 | + * devm_phy_package_join - resource managed phy_package_join() |
| 138 | + * @dev: device that is registering this PHY package |
| 139 | + * @phydev: target phy_device struct |
| 140 | + * @addr: cookie and PHY address for global register access |
| 141 | + * @priv_size: if non-zero allocate this amount of bytes for private data |
| 142 | + * |
| 143 | + * Managed phy_package_join(). Shared storage fetched by this function, |
| 144 | + * phy_package_leave() is automatically called on driver detach. See |
| 145 | + * phy_package_join() for more information. |
| 146 | + */ |
| 147 | +int devm_phy_package_join(struct device *dev, struct phy_device *phydev, |
| 148 | + int addr, size_t priv_size) |
| 149 | +{ |
| 150 | + struct phy_device **ptr; |
| 151 | + int ret; |
| 152 | + |
| 153 | + ptr = devres_alloc(devm_phy_package_leave, sizeof(*ptr), |
| 154 | + GFP_KERNEL); |
| 155 | + if (!ptr) |
| 156 | + return -ENOMEM; |
| 157 | + |
| 158 | + ret = phy_package_join(phydev, addr, priv_size); |
| 159 | + |
| 160 | + if (!ret) { |
| 161 | + *ptr = phydev; |
| 162 | + devres_add(dev, ptr); |
| 163 | + } else { |
| 164 | + devres_free(ptr); |
| 165 | + } |
| 166 | + |
| 167 | + return ret; |
| 168 | +} |
| 169 | +EXPORT_SYMBOL_GPL(devm_phy_package_join); |
| 170 | + |
developer | 5d148cb | 2023-06-02 13:08:11 +0800 | [diff] [blame] | 171 | /** |
developer | 394d5eb | 2023-04-14 16:46:45 +0800 | [diff] [blame] | 172 | * phy_detach - detach a PHY device from its network device |
| 173 | * @phydev: target phy_device struct |
developer | 5d148cb | 2023-06-02 13:08:11 +0800 | [diff] [blame] | 174 | diff --git a/include/linux/phy.h b/include/linux/phy.h |
| 175 | index 107dcbea4..d26dba255 100644 |
developer | 394d5eb | 2023-04-14 16:46:45 +0800 | [diff] [blame] | 176 | --- a/include/linux/phy.h |
| 177 | +++ b/include/linux/phy.h |
| 178 | @@ -22,6 +22,7 @@ |
| 179 | #include <linux/workqueue.h> |
| 180 | #include <linux/mod_devicetable.h> |
| 181 | #include <linux/iopoll.h> |
| 182 | +#include <linux/refcount.h> |
| 183 | |
| 184 | #include <linux/atomic.h> |
| 185 | |
developer | 5d148cb | 2023-06-02 13:08:11 +0800 | [diff] [blame] | 186 | @@ -211,6 +212,28 @@ struct sfp_bus; |
developer | 394d5eb | 2023-04-14 16:46:45 +0800 | [diff] [blame] | 187 | struct sfp_upstream_ops; |
| 188 | struct sk_buff; |
| 189 | |
| 190 | +/* Represents a shared structure between different phydev's in the same |
| 191 | + * package, for example a quad PHY. See phy_package_join() and |
| 192 | + * phy_package_leave(). |
| 193 | + */ |
| 194 | +struct phy_package_shared { |
| 195 | + int addr; |
| 196 | + refcount_t refcnt; |
| 197 | + unsigned long flags; |
| 198 | + size_t priv_size; |
| 199 | + |
| 200 | + /* private data pointer */ |
| 201 | + /* note that this pointer is shared between different phydevs and |
| 202 | + * the user has to take care of appropriate locking. It is allocated |
| 203 | + * and freed automatically by phy_package_join() and |
| 204 | + * phy_package_leave(). |
| 205 | + */ |
| 206 | + void *priv; |
| 207 | +}; |
| 208 | + |
| 209 | +/* used as bit number in atomic bitops */ |
| 210 | +#define PHY_SHARED_F_INIT_DONE 0 |
| 211 | + |
| 212 | /* |
| 213 | * The Bus class for PHYs. Devices which provide access to |
| 214 | * PHYs should register using this structure |
developer | 5d148cb | 2023-06-02 13:08:11 +0800 | [diff] [blame] | 215 | @@ -258,6 +281,12 @@ struct mii_bus { |
developer | 394d5eb | 2023-04-14 16:46:45 +0800 | [diff] [blame] | 216 | int reset_delay_us; |
| 217 | /* RESET GPIO descriptor pointer */ |
| 218 | struct gpio_desc *reset_gpiod; |
| 219 | + |
| 220 | + /* protect access to the shared element */ |
| 221 | + struct mutex shared_lock; |
| 222 | + |
| 223 | + /* shared state across different PHYs */ |
| 224 | + struct phy_package_shared *shared[PHY_MAX_ADDR]; |
| 225 | }; |
| 226 | #define to_mii_bus(d) container_of(d, struct mii_bus, dev) |
| 227 | |
developer | 5d148cb | 2023-06-02 13:08:11 +0800 | [diff] [blame] | 228 | @@ -437,6 +466,10 @@ struct phy_device { |
developer | 394d5eb | 2023-04-14 16:46:45 +0800 | [diff] [blame] | 229 | /* For use by PHYs to maintain extra state */ |
| 230 | void *priv; |
| 231 | |
| 232 | + /* shared data pointer */ |
| 233 | + /* For use by PHYs inside the same package that need a shared state. */ |
| 234 | + struct phy_package_shared *shared; |
| 235 | + |
| 236 | /* Interrupt and Polling infrastructure */ |
| 237 | struct delayed_work state_queue; |
| 238 | |
developer | 5d148cb | 2023-06-02 13:08:11 +0800 | [diff] [blame] | 239 | @@ -1242,6 +1275,10 @@ int phy_ethtool_get_link_ksettings(struct net_device *ndev, |
developer | 394d5eb | 2023-04-14 16:46:45 +0800 | [diff] [blame] | 240 | int phy_ethtool_set_link_ksettings(struct net_device *ndev, |
| 241 | const struct ethtool_link_ksettings *cmd); |
| 242 | int phy_ethtool_nway_reset(struct net_device *ndev); |
| 243 | +int phy_package_join(struct phy_device *phydev, int addr, size_t priv_size); |
| 244 | +void phy_package_leave(struct phy_device *phydev); |
| 245 | +int devm_phy_package_join(struct device *dev, struct phy_device *phydev, |
| 246 | + int addr, size_t priv_size); |
| 247 | |
| 248 | #if IS_ENABLED(CONFIG_PHYLIB) |
| 249 | int __init mdio_bus_init(void); |
developer | 5d148cb | 2023-06-02 13:08:11 +0800 | [diff] [blame] | 250 | @@ -1294,6 +1331,58 @@ static inline int phy_ethtool_get_stats(struct phy_device *phydev, |
developer | 394d5eb | 2023-04-14 16:46:45 +0800 | [diff] [blame] | 251 | return 0; |
| 252 | } |
| 253 | |
| 254 | +static inline int phy_package_read(struct phy_device *phydev, u32 regnum) |
| 255 | +{ |
| 256 | + struct phy_package_shared *shared = phydev->shared; |
| 257 | + |
| 258 | + if (!shared) |
| 259 | + return -EIO; |
| 260 | + |
| 261 | + return mdiobus_read(phydev->mdio.bus, shared->addr, regnum); |
| 262 | +} |
| 263 | + |
| 264 | +static inline int __phy_package_read(struct phy_device *phydev, u32 regnum) |
| 265 | +{ |
| 266 | + struct phy_package_shared *shared = phydev->shared; |
| 267 | + |
| 268 | + if (!shared) |
| 269 | + return -EIO; |
| 270 | + |
| 271 | + return __mdiobus_read(phydev->mdio.bus, shared->addr, regnum); |
| 272 | +} |
| 273 | + |
| 274 | +static inline int phy_package_write(struct phy_device *phydev, |
| 275 | + u32 regnum, u16 val) |
| 276 | +{ |
| 277 | + struct phy_package_shared *shared = phydev->shared; |
| 278 | + |
| 279 | + if (!shared) |
| 280 | + return -EIO; |
| 281 | + |
| 282 | + return mdiobus_write(phydev->mdio.bus, shared->addr, regnum, val); |
| 283 | +} |
| 284 | + |
| 285 | +static inline int __phy_package_write(struct phy_device *phydev, |
| 286 | + u32 regnum, u16 val) |
| 287 | +{ |
| 288 | + struct phy_package_shared *shared = phydev->shared; |
| 289 | + |
| 290 | + if (!shared) |
| 291 | + return -EIO; |
| 292 | + |
| 293 | + return __mdiobus_write(phydev->mdio.bus, shared->addr, regnum, val); |
| 294 | +} |
| 295 | + |
| 296 | +static inline bool phy_package_init_once(struct phy_device *phydev) |
| 297 | +{ |
| 298 | + struct phy_package_shared *shared = phydev->shared; |
| 299 | + |
| 300 | + if (!shared) |
| 301 | + return false; |
| 302 | + |
| 303 | + return !test_and_set_bit(PHY_SHARED_F_INIT_DONE, &shared->flags); |
| 304 | +} |
| 305 | + |
| 306 | extern struct bus_type mdio_bus_type; |
| 307 | |
| 308 | struct mdio_board_info { |
developer | 5d148cb | 2023-06-02 13:08:11 +0800 | [diff] [blame] | 309 | -- |
| 310 | 2.34.1 |
| 311 | |