developer | 394d5eb | 2023-04-14 16:46:45 +0800 | [diff] [blame] | 1 | From patchwork Wed May 6 14:53:13 2020 |
| 2 | Content-Type: text/plain; charset="utf-8" |
| 3 | MIME-Version: 1.0 |
| 4 | Content-Transfer-Encoding: 7bit |
| 5 | X-Patchwork-Submitter: Michael Walle <michael@walle.cc> |
| 6 | X-Patchwork-Id: 1284481 |
| 7 | X-Patchwork-Delegate: davem@davemloft.net |
| 8 | Return-Path: <netdev-owner@vger.kernel.org> |
| 9 | X-Original-To: patchwork-incoming-netdev@ozlabs.org |
| 10 | Delivered-To: patchwork-incoming-netdev@ozlabs.org |
| 11 | Authentication-Results: ozlabs.org; |
| 12 | spf=pass (sender SPF authorized) smtp.mailfrom=vger.kernel.org |
| 13 | (client-ip=23.128.96.18; helo=vger.kernel.org; |
| 14 | envelope-from=netdev-owner@vger.kernel.org; receiver=<UNKNOWN>) |
| 15 | Authentication-Results: ozlabs.org; |
| 16 | dmarc=none (p=none dis=none) header.from=walle.cc |
| 17 | Authentication-Results: ozlabs.org; |
| 18 | dkim=pass (1024-bit key; |
| 19 | secure) header.d=walle.cc header.i=@walle.cc header.a=rsa-sha256 |
| 20 | header.s=mail2016061301 header.b=m9HhLh3d; |
| 21 | dkim-atps=neutral |
| 22 | Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) |
| 23 | by ozlabs.org (Postfix) with ESMTP id 49HKQ62Q28z9sSG |
| 24 | for <patchwork-incoming-netdev@ozlabs.org>; |
| 25 | Thu, 7 May 2020 00:55:10 +1000 (AEST) |
| 26 | Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand |
| 27 | id S1729301AbgEFOzD (ORCPT |
| 28 | <rfc822;patchwork-incoming-netdev@ozlabs.org>); |
| 29 | Wed, 6 May 2020 10:55:03 -0400 |
| 30 | Received: from ssl.serverraum.org ([176.9.125.105]:43029 "EHLO |
| 31 | ssl.serverraum.org" rhost-flags-OK-OK-OK-OK) by vger.kernel.org |
| 32 | with ESMTP id S1729078AbgEFOzC (ORCPT |
| 33 | <rfc822;netdev@vger.kernel.org>); Wed, 6 May 2020 10:55:02 -0400 |
| 34 | Received: from apollo.fritz.box (unknown |
| 35 | [IPv6:2a02:810c:c200:2e91:6257:18ff:fec4:ca34]) |
| 36 | (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) |
| 37 | key-exchange ECDHE (P-384) server-signature RSA-PSS (2048 bits) |
| 38 | server-digest SHA256) |
| 39 | (No client certificate requested) |
| 40 | by ssl.serverraum.org (Postfix) with ESMTPSA id 2354022EEB; |
| 41 | Wed, 6 May 2020 16:54:57 +0200 (CEST) |
| 42 | DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=walle.cc; |
| 43 | s=mail2016061301; |
| 44 | t=1588776897; |
| 45 | h=from:from:reply-to:subject:subject:date:date:message-id:message-id: |
| 46 | to:to:cc:cc:mime-version:mime-version: |
| 47 | content-transfer-encoding:content-transfer-encoding: |
| 48 | in-reply-to:in-reply-to:references:references; |
| 49 | bh=Y1HXOD90+xthCbcF5aODRvO5s4y3GjqVZeWMcm2C9hg=; |
| 50 | b=m9HhLh3dnD9BTg85PIRYHxEzW+9tKI8srVGI3MjgXJkJaWDcnUKGyPN86orzkyHrB0ai5O |
| 51 | VyiY7R2tdN04JifV18FNmxuUW/9Pc3kWUfo+q974YzVhTm0Tkrc3osn/smhyhl7PxpHZMl |
| 52 | VHiTEHII3umwamTkGQq8kpYUr38joLY= |
| 53 | From: Michael Walle <michael@walle.cc> |
| 54 | To: linux-kernel@vger.kernel.org, netdev@vger.kernel.org |
| 55 | Cc: Andrew Lunn <andrew@lunn.ch>, |
| 56 | Florian Fainelli <f.fainelli@gmail.com>, |
| 57 | Heiner Kallweit <hkallweit1@gmail.com>, |
| 58 | Russell King <linux@armlinux.org.uk>, |
| 59 | "David S . Miller" <davem@davemloft.net>, |
| 60 | Vladimir Oltean <vladimir.oltean@nxp.com>, |
| 61 | Antoine Tenart <antoine.tenart@bootlin.com>, |
| 62 | Michael Walle <michael@walle.cc> |
| 63 | Subject: [PATCH net-next v3 1/3] net: phy: add concept of shared storage for |
| 64 | PHYs |
| 65 | Date: Wed, 6 May 2020 16:53:13 +0200 |
| 66 | Message-Id: <20200506145315.13967-2-michael@walle.cc> |
| 67 | X-Mailer: git-send-email 2.20.1 |
| 68 | In-Reply-To: <20200506145315.13967-1-michael@walle.cc> |
| 69 | References: <20200506145315.13967-1-michael@walle.cc> |
| 70 | MIME-Version: 1.0 |
| 71 | X-Spam: Yes |
| 72 | Sender: netdev-owner@vger.kernel.org |
| 73 | Precedence: bulk |
| 74 | List-ID: <netdev.vger.kernel.org> |
| 75 | X-Mailing-List: netdev@vger.kernel.org |
| 76 | |
| 77 | There are packages which contain multiple PHY devices, eg. a quad PHY |
| 78 | transceiver. Provide functions to allocate and free shared storage. |
| 79 | |
| 80 | Usually, a quad PHY contains global registers, which don't belong to any |
| 81 | PHY. Provide convenience functions to access these registers. |
| 82 | |
| 83 | Signed-off-by: Michael Walle <michael@walle.cc> |
| 84 | Reviewed-by: Andrew Lunn <andrew@lunn.ch> |
| 85 | Reviewed-by: Florian Fainelli <f.fainelli@gmail.com> |
| 86 | --- |
| 87 | drivers/net/phy/mdio_bus.c | 1 + |
| 88 | drivers/net/phy/phy_device.c | 138 +++++++++++++++++++++++++++++++++++ |
| 89 | include/linux/phy.h | 89 ++++++++++++++++++++++ |
| 90 | 3 files changed, 228 insertions(+) |
| 91 | |
| 92 | --- a/drivers/net/phy/mdio_bus.c |
| 93 | +++ b/drivers/net/phy/mdio_bus.c |
| 94 | @@ -404,6 +404,7 @@ int __mdiobus_register(struct mii_bus *b |
| 95 | } |
| 96 | |
| 97 | mutex_init(&bus->mdio_lock); |
| 98 | + mutex_init(&bus->shared_lock); |
| 99 | |
| 100 | /* de-assert bus level PHY GPIO reset */ |
| 101 | gpiod = devm_gpiod_get_optional(&bus->dev, "reset", GPIOD_OUT_LOW); |
| 102 | --- a/drivers/net/phy/phy_device.c |
| 103 | +++ b/drivers/net/phy/phy_device.c |
| 104 | @@ -1448,6 +1448,144 @@ bool phy_driver_is_genphy_10g(struct phy |
| 105 | EXPORT_SYMBOL_GPL(phy_driver_is_genphy_10g); |
| 106 | |
| 107 | /** |
| 108 | + * phy_package_join - join a common PHY group |
| 109 | + * @phydev: target phy_device struct |
| 110 | + * @addr: cookie and PHY address for global register access |
| 111 | + * @priv_size: if non-zero allocate this amount of bytes for private data |
| 112 | + * |
| 113 | + * This joins a PHY group and provides a shared storage for all phydevs in |
| 114 | + * this group. This is intended to be used for packages which contain |
| 115 | + * more than one PHY, for example a quad PHY transceiver. |
| 116 | + * |
| 117 | + * The addr parameter serves as a cookie which has to have the same value |
| 118 | + * for all members of one group and as a PHY address to access generic |
| 119 | + * registers of a PHY package. Usually, one of the PHY addresses of the |
| 120 | + * different PHYs in the package provides access to these global registers. |
| 121 | + * The address which is given here, will be used in the phy_package_read() |
| 122 | + * and phy_package_write() convenience functions. If your PHY doesn't have |
| 123 | + * global registers you can just pick any of the PHY addresses. |
| 124 | + * |
| 125 | + * This will set the shared pointer of the phydev to the shared storage. |
| 126 | + * If this is the first call for a this cookie the shared storage will be |
| 127 | + * allocated. If priv_size is non-zero, the given amount of bytes are |
| 128 | + * allocated for the priv member. |
| 129 | + * |
| 130 | + * Returns < 1 on error, 0 on success. Esp. calling phy_package_join() |
| 131 | + * with the same cookie but a different priv_size is an error. |
| 132 | + */ |
| 133 | +int phy_package_join(struct phy_device *phydev, int addr, size_t priv_size) |
| 134 | +{ |
| 135 | + struct mii_bus *bus = phydev->mdio.bus; |
| 136 | + struct phy_package_shared *shared; |
| 137 | + int ret; |
| 138 | + |
| 139 | + if (addr < 0 || addr >= PHY_MAX_ADDR) |
| 140 | + return -EINVAL; |
| 141 | + |
| 142 | + mutex_lock(&bus->shared_lock); |
| 143 | + shared = bus->shared[addr]; |
| 144 | + if (!shared) { |
| 145 | + ret = -ENOMEM; |
| 146 | + shared = kzalloc(sizeof(*shared), GFP_KERNEL); |
| 147 | + if (!shared) |
| 148 | + goto err_unlock; |
| 149 | + if (priv_size) { |
| 150 | + shared->priv = kzalloc(priv_size, GFP_KERNEL); |
| 151 | + if (!shared->priv) |
| 152 | + goto err_free; |
| 153 | + shared->priv_size = priv_size; |
| 154 | + } |
| 155 | + shared->addr = addr; |
| 156 | + refcount_set(&shared->refcnt, 1); |
| 157 | + bus->shared[addr] = shared; |
| 158 | + } else { |
| 159 | + ret = -EINVAL; |
| 160 | + if (priv_size && priv_size != shared->priv_size) |
| 161 | + goto err_unlock; |
| 162 | + refcount_inc(&shared->refcnt); |
| 163 | + } |
| 164 | + mutex_unlock(&bus->shared_lock); |
| 165 | + |
| 166 | + phydev->shared = shared; |
| 167 | + |
| 168 | + return 0; |
| 169 | + |
| 170 | +err_free: |
| 171 | + kfree(shared); |
| 172 | +err_unlock: |
| 173 | + mutex_unlock(&bus->shared_lock); |
| 174 | + return ret; |
| 175 | +} |
| 176 | +EXPORT_SYMBOL_GPL(phy_package_join); |
| 177 | + |
| 178 | +/** |
| 179 | + * phy_package_leave - leave a common PHY group |
| 180 | + * @phydev: target phy_device struct |
| 181 | + * |
| 182 | + * This leaves a PHY group created by phy_package_join(). If this phydev |
| 183 | + * was the last user of the shared data between the group, this data is |
| 184 | + * freed. Resets the phydev->shared pointer to NULL. |
| 185 | + */ |
| 186 | +void phy_package_leave(struct phy_device *phydev) |
| 187 | +{ |
| 188 | + struct phy_package_shared *shared = phydev->shared; |
| 189 | + struct mii_bus *bus = phydev->mdio.bus; |
| 190 | + |
| 191 | + if (!shared) |
| 192 | + return; |
| 193 | + |
| 194 | + if (refcount_dec_and_mutex_lock(&shared->refcnt, &bus->shared_lock)) { |
| 195 | + bus->shared[shared->addr] = NULL; |
| 196 | + mutex_unlock(&bus->shared_lock); |
| 197 | + kfree(shared->priv); |
| 198 | + kfree(shared); |
| 199 | + } |
| 200 | + |
| 201 | + phydev->shared = NULL; |
| 202 | +} |
| 203 | +EXPORT_SYMBOL_GPL(phy_package_leave); |
| 204 | + |
| 205 | +static void devm_phy_package_leave(struct device *dev, void *res) |
| 206 | +{ |
| 207 | + phy_package_leave(*(struct phy_device **)res); |
| 208 | +} |
| 209 | + |
| 210 | +/** |
| 211 | + * devm_phy_package_join - resource managed phy_package_join() |
| 212 | + * @dev: device that is registering this PHY package |
| 213 | + * @phydev: target phy_device struct |
| 214 | + * @addr: cookie and PHY address for global register access |
| 215 | + * @priv_size: if non-zero allocate this amount of bytes for private data |
| 216 | + * |
| 217 | + * Managed phy_package_join(). Shared storage fetched by this function, |
| 218 | + * phy_package_leave() is automatically called on driver detach. See |
| 219 | + * phy_package_join() for more information. |
| 220 | + */ |
| 221 | +int devm_phy_package_join(struct device *dev, struct phy_device *phydev, |
| 222 | + int addr, size_t priv_size) |
| 223 | +{ |
| 224 | + struct phy_device **ptr; |
| 225 | + int ret; |
| 226 | + |
| 227 | + ptr = devres_alloc(devm_phy_package_leave, sizeof(*ptr), |
| 228 | + GFP_KERNEL); |
| 229 | + if (!ptr) |
| 230 | + return -ENOMEM; |
| 231 | + |
| 232 | + ret = phy_package_join(phydev, addr, priv_size); |
| 233 | + |
| 234 | + if (!ret) { |
| 235 | + *ptr = phydev; |
| 236 | + devres_add(dev, ptr); |
| 237 | + } else { |
| 238 | + devres_free(ptr); |
| 239 | + } |
| 240 | + |
| 241 | + return ret; |
| 242 | +} |
| 243 | +EXPORT_SYMBOL_GPL(devm_phy_package_join); |
| 244 | + |
| 245 | +/** |
| 246 | * phy_detach - detach a PHY device from its network device |
| 247 | * @phydev: target phy_device struct |
| 248 | * |
| 249 | --- a/include/linux/phy.h |
| 250 | +++ b/include/linux/phy.h |
| 251 | @@ -22,6 +22,7 @@ |
| 252 | #include <linux/workqueue.h> |
| 253 | #include <linux/mod_devicetable.h> |
| 254 | #include <linux/iopoll.h> |
| 255 | +#include <linux/refcount.h> |
| 256 | |
| 257 | #include <linux/atomic.h> |
| 258 | |
| 259 | @@ -208,6 +209,28 @@ struct sfp_bus; |
| 260 | struct sfp_upstream_ops; |
| 261 | struct sk_buff; |
| 262 | |
| 263 | +/* Represents a shared structure between different phydev's in the same |
| 264 | + * package, for example a quad PHY. See phy_package_join() and |
| 265 | + * phy_package_leave(). |
| 266 | + */ |
| 267 | +struct phy_package_shared { |
| 268 | + int addr; |
| 269 | + refcount_t refcnt; |
| 270 | + unsigned long flags; |
| 271 | + size_t priv_size; |
| 272 | + |
| 273 | + /* private data pointer */ |
| 274 | + /* note that this pointer is shared between different phydevs and |
| 275 | + * the user has to take care of appropriate locking. It is allocated |
| 276 | + * and freed automatically by phy_package_join() and |
| 277 | + * phy_package_leave(). |
| 278 | + */ |
| 279 | + void *priv; |
| 280 | +}; |
| 281 | + |
| 282 | +/* used as bit number in atomic bitops */ |
| 283 | +#define PHY_SHARED_F_INIT_DONE 0 |
| 284 | + |
| 285 | /* |
| 286 | * The Bus class for PHYs. Devices which provide access to |
| 287 | * PHYs should register using this structure |
| 288 | @@ -255,6 +278,12 @@ struct mii_bus { |
| 289 | int reset_delay_us; |
| 290 | /* RESET GPIO descriptor pointer */ |
| 291 | struct gpio_desc *reset_gpiod; |
| 292 | + |
| 293 | + /* protect access to the shared element */ |
| 294 | + struct mutex shared_lock; |
| 295 | + |
| 296 | + /* shared state across different PHYs */ |
| 297 | + struct phy_package_shared *shared[PHY_MAX_ADDR]; |
| 298 | }; |
| 299 | #define to_mii_bus(d) container_of(d, struct mii_bus, dev) |
| 300 | |
| 301 | @@ -434,6 +463,10 @@ struct phy_device { |
| 302 | /* For use by PHYs to maintain extra state */ |
| 303 | void *priv; |
| 304 | |
| 305 | + /* shared data pointer */ |
| 306 | + /* For use by PHYs inside the same package that need a shared state. */ |
| 307 | + struct phy_package_shared *shared; |
| 308 | + |
| 309 | /* Interrupt and Polling infrastructure */ |
| 310 | struct delayed_work state_queue; |
| 311 | |
| 312 | @@ -1232,6 +1265,10 @@ int phy_ethtool_get_link_ksettings(struc |
| 313 | int phy_ethtool_set_link_ksettings(struct net_device *ndev, |
| 314 | const struct ethtool_link_ksettings *cmd); |
| 315 | int phy_ethtool_nway_reset(struct net_device *ndev); |
| 316 | +int phy_package_join(struct phy_device *phydev, int addr, size_t priv_size); |
| 317 | +void phy_package_leave(struct phy_device *phydev); |
| 318 | +int devm_phy_package_join(struct device *dev, struct phy_device *phydev, |
| 319 | + int addr, size_t priv_size); |
| 320 | |
| 321 | #if IS_ENABLED(CONFIG_PHYLIB) |
| 322 | int __init mdio_bus_init(void); |
| 323 | @@ -1284,6 +1321,58 @@ static inline int phy_ethtool_get_stats( |
| 324 | return 0; |
| 325 | } |
| 326 | |
| 327 | +static inline int phy_package_read(struct phy_device *phydev, u32 regnum) |
| 328 | +{ |
| 329 | + struct phy_package_shared *shared = phydev->shared; |
| 330 | + |
| 331 | + if (!shared) |
| 332 | + return -EIO; |
| 333 | + |
| 334 | + return mdiobus_read(phydev->mdio.bus, shared->addr, regnum); |
| 335 | +} |
| 336 | + |
| 337 | +static inline int __phy_package_read(struct phy_device *phydev, u32 regnum) |
| 338 | +{ |
| 339 | + struct phy_package_shared *shared = phydev->shared; |
| 340 | + |
| 341 | + if (!shared) |
| 342 | + return -EIO; |
| 343 | + |
| 344 | + return __mdiobus_read(phydev->mdio.bus, shared->addr, regnum); |
| 345 | +} |
| 346 | + |
| 347 | +static inline int phy_package_write(struct phy_device *phydev, |
| 348 | + u32 regnum, u16 val) |
| 349 | +{ |
| 350 | + struct phy_package_shared *shared = phydev->shared; |
| 351 | + |
| 352 | + if (!shared) |
| 353 | + return -EIO; |
| 354 | + |
| 355 | + return mdiobus_write(phydev->mdio.bus, shared->addr, regnum, val); |
| 356 | +} |
| 357 | + |
| 358 | +static inline int __phy_package_write(struct phy_device *phydev, |
| 359 | + u32 regnum, u16 val) |
| 360 | +{ |
| 361 | + struct phy_package_shared *shared = phydev->shared; |
| 362 | + |
| 363 | + if (!shared) |
| 364 | + return -EIO; |
| 365 | + |
| 366 | + return __mdiobus_write(phydev->mdio.bus, shared->addr, regnum, val); |
| 367 | +} |
| 368 | + |
| 369 | +static inline bool phy_package_init_once(struct phy_device *phydev) |
| 370 | +{ |
| 371 | + struct phy_package_shared *shared = phydev->shared; |
| 372 | + |
| 373 | + if (!shared) |
| 374 | + return false; |
| 375 | + |
| 376 | + return !test_and_set_bit(PHY_SHARED_F_INIT_DONE, &shared->flags); |
| 377 | +} |
| 378 | + |
| 379 | extern struct bus_type mdio_bus_type; |
| 380 | |
| 381 | struct mdio_board_info { |