blob: e74f20b7bb60a481f0389f950236a30d1ed01d76 [file] [log] [blame]
developer5d148cb2023-06-02 13:08:11 +08001From 2dca4de7282d3003f3703f707d773f4dbbc0f28e Mon Sep 17 00:00:00 2001
2From: Sam Shih <sam.shih@mediatek.com>
3Date: Fri, 2 Jun 2023 13:06:27 +0800
4Subject: [PATCH]
5 [networking][999-2701-v5.8-net-phy-add-concept-of-shared-storage-for-PHYs.patch]
developer394d5eb2023-04-14 16:46:45 +08006
developer394d5eb2023-04-14 16:46:45 +08007---
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
developer5d148cb2023-06-02 13:08:11 +080013diff --git a/drivers/net/phy/mdio_bus.c b/drivers/net/phy/mdio_bus.c
14index fdf8221f4..d9f2cee33 100644
developer394d5eb2023-04-14 16:46:45 +080015--- a/drivers/net/phy/mdio_bus.c
16+++ b/drivers/net/phy/mdio_bus.c
developer5d148cb2023-06-02 13:08:11 +080017@@ -404,6 +404,7 @@ int __mdiobus_register(struct mii_bus *bus, struct module *owner)
developer394d5eb2023-04-14 16:46:45 +080018 }
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);
developer5d148cb2023-06-02 13:08:11 +080025diff --git a/drivers/net/phy/phy_device.c b/drivers/net/phy/phy_device.c
26index 0349801df..99f265a1c 100644
developer394d5eb2023-04-14 16:46:45 +080027--- a/drivers/net/phy/phy_device.c
28+++ b/drivers/net/phy/phy_device.c
developer5d148cb2023-06-02 13:08:11 +080029@@ -1447,6 +1447,144 @@ bool phy_driver_is_genphy_10g(struct phy_device *phydev)
30 }
developer394d5eb2023-04-14 16:46:45 +080031 EXPORT_SYMBOL_GPL(phy_driver_is_genphy_10g);
32
developer5d148cb2023-06-02 13:08:11 +080033+/**
developer394d5eb2023-04-14 16:46:45 +080034+ * 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+
developer5d148cb2023-06-02 13:08:11 +0800171 /**
developer394d5eb2023-04-14 16:46:45 +0800172 * phy_detach - detach a PHY device from its network device
173 * @phydev: target phy_device struct
developer5d148cb2023-06-02 13:08:11 +0800174diff --git a/include/linux/phy.h b/include/linux/phy.h
175index 107dcbea4..d26dba255 100644
developer394d5eb2023-04-14 16:46:45 +0800176--- 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
developer5d148cb2023-06-02 13:08:11 +0800186@@ -211,6 +212,28 @@ struct sfp_bus;
developer394d5eb2023-04-14 16:46:45 +0800187 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
developer5d148cb2023-06-02 13:08:11 +0800215@@ -258,6 +281,12 @@ struct mii_bus {
developer394d5eb2023-04-14 16:46:45 +0800216 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
developer5d148cb2023-06-02 13:08:11 +0800228@@ -437,6 +466,10 @@ struct phy_device {
developer394d5eb2023-04-14 16:46:45 +0800229 /* 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
developer5d148cb2023-06-02 13:08:11 +0800239@@ -1242,6 +1275,10 @@ int phy_ethtool_get_link_ksettings(struct net_device *ndev,
developer394d5eb2023-04-14 16:46:45 +0800240 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);
developer5d148cb2023-06-02 13:08:11 +0800250@@ -1294,6 +1331,58 @@ static inline int phy_ethtool_get_stats(struct phy_device *phydev,
developer394d5eb2023-04-14 16:46:45 +0800251 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 {
developer5d148cb2023-06-02 13:08:11 +0800309--
3102.34.1
311