blob: b4767511f46d6d17bb2b23fd4f975a6c97bec8c5 [file] [log] [blame]
b.liue9582032025-04-17 19:18:16 +08001From 136d46d2fa27815cc4cc3a57d5e3d54523028768 Mon Sep 17 00:00:00 2001
2From: Sachin Saxena <sachin.saxena@nxp.com>
3Date: Thu, 19 Dec 2019 12:57:35 +0530
4Subject: [PATCH] fsl_qbman: Framework for enabling Link status notification
5
6 - This will enable link update event notification for
7 user space USDPAA application.
8
9Signed-off-by: Sachin Saxena <sachin.saxena@nxp.com>
10DPDK-2128
11(cherry picked from commit fb53c813a779cc3fc28c8ed3fc8bc0dde24db0ea)
12---
13 drivers/staging/fsl_qbman/Makefile | 4 +
14 drivers/staging/fsl_qbman/fsl_usdpaa.c | 278 ++++++++++++++++++++++++++++++++-
15 include/linux/fsl_usdpaa.h | 32 ++++
16 3 files changed, 313 insertions(+), 1 deletion(-)
17
18--- a/drivers/staging/fsl_qbman/Makefile
19+++ b/drivers/staging/fsl_qbman/Makefile
20@@ -1,5 +1,9 @@
21 subdir-ccflags-y := -Werror
22
23+# Include netcomm SW specific definitions
24+include $(srctree)/drivers/net/ethernet/freescale/sdk_fman/ncsw_config.mk
25+ccflags-y += -I$(NET_DPA)
26+
27 # Common
28 obj-$(CONFIG_FSL_SDK_DPA) += dpa_alloc.o
29 obj-$(CONFIG_FSL_SDK_DPA) += qbman_driver.o
30--- a/drivers/staging/fsl_qbman/fsl_usdpaa.c
31+++ b/drivers/staging/fsl_qbman/fsl_usdpaa.c
32@@ -18,6 +18,8 @@
33 #include <linux/slab.h>
34 #include <linux/mman.h>
35 #include <linux/of_reserved_mem.h>
36+#include <linux/eventfd.h>
37+#include <linux/fdtable.h>
38
39 #if !(defined(CONFIG_ARM) || defined(CONFIG_ARM64))
40 #include <mm/mmu_decl.h>
41@@ -27,6 +29,26 @@
42 #include <linux/fsl_usdpaa.h>
43 #include "bman_low.h"
44 #include "qman_low.h"
45+/* Headers requires for
46+ * Link status support
47+ */
48+#include <linux/device.h>
49+#include <linux/of_mdio.h>
50+#include "mac.h"
51+#include "dpaa_eth_common.h"
52+
53+/* Private data for Proxy Interface */
54+struct dpa_proxy_priv_s {
55+ struct mac_device *mac_dev;
56+ struct eventfd_ctx *efd_ctx;
57+};
58+/* Interface Helpers */
59+static inline struct device *get_dev_ptr(char *if_name);
60+static void phy_link_updates(struct net_device *net_dev);
61+/* IOCTL handlers */
62+static inline int ioctl_usdpaa_get_link_status(char *if_name);
63+static int ioctl_en_if_link_status(struct usdpaa_ioctl_link_status *args);
64+static int ioctl_disable_if_link_status(char *if_name);
65
66 /* Physical address range of the memory reservation, exported for mm/mem.c */
67 static u64 phys_start;
68@@ -556,7 +578,6 @@ static bool check_portal_channel(void *c
69
70
71
72-
73 static int usdpaa_release(struct inode *inode, struct file *filp)
74 {
75 int err = 0;
76@@ -1656,6 +1677,220 @@ found:
77 return 0;
78 }
79
80+
81+static inline struct device *get_dev_ptr(char *if_name)
82+{
83+ struct device *dev;
84+ char node[NODE_NAME_LEN];
85+
86+ sprintf(node, "soc:fsl,dpaa:%s",if_name);
87+ dev = bus_find_device_by_name(&platform_bus_type, NULL, node);
88+ if (dev == NULL) {
89+ pr_err(KBUILD_MODNAME "IF %s not found\n", if_name);
90+ return NULL;
91+ }
92+ pr_debug("%s: found dev 0x%lX for If %s ,dev->platform_data %p\n",
93+ __func__, (unsigned long)dev,
94+ if_name, dev->platform_data);
95+
96+ return dev;
97+}
98+
99+/* This function will return Current link status of the device
100+ * '1' if Link is UP, '0' otherwise.
101+ *
102+ * Input parameter:
103+ * if_name: Interface node name
104+ *
105+ */
106+static inline int ioctl_usdpaa_get_link_status(char *if_name)
107+{
108+ struct net_device *net_dev = NULL;
109+ struct device *dev;
110+
111+ dev = get_dev_ptr(if_name);
112+ if (dev == NULL)
113+ return -ENODEV;
114+ net_dev = dev->platform_data;
115+ if (net_dev == NULL)
116+ return -ENODEV;
117+
118+ if (test_bit(__LINK_STATE_NOCARRIER, &net_dev->state))
119+ return 0; /* Link is DOWN */
120+ else
121+ return 1; /* Link is UP */
122+}
123+
124+
125+/* Link Status Callback Function
126+ * This function will be resgitered to PHY framework to get
127+ * Link update notifications and should be responsible for waking up
128+ * user space task when there is a link update notification.
129+ */
130+static void phy_link_updates(struct net_device *net_dev)
131+{
132+ struct dpa_proxy_priv_s *priv = NULL;
133+
134+ pr_debug("%s: Link '%s': Speed '%d-Mbps': Autoneg '%d': Duplex '%d'\n",
135+ net_dev->name,
136+ ioctl_usdpaa_get_link_status(net_dev->name)?"UP":"DOWN",
137+ net_dev->phydev->speed,
138+ net_dev->phydev->autoneg,
139+ net_dev->phydev->duplex);
140+
141+ /* Wake up the user space context to notify PHY update */
142+ priv = netdev_priv(net_dev);
143+ eventfd_signal(priv->efd_ctx, 1);
144+}
145+
146+
147+/* IOCTL handler for enabling Link status request for a given interface
148+ * Input parameters:
149+ * args->if_name: This the network interface node name as defind in
150+ * device tree file. Currently, it has format of
151+ * "ethernet@x" type for each interface.
152+ * args->efd: The eventfd value which should be waked up when
153+ * there is any link update received.
154+ */
155+static int ioctl_en_if_link_status(struct usdpaa_ioctl_link_status *args)
156+{
157+ struct net_device *net_dev = NULL;
158+ struct dpa_proxy_priv_s *priv = NULL;
159+ struct device *dev;
160+ struct mac_device *mac_dev;
161+ struct proxy_device *proxy_dev;
162+ struct task_struct *userspace_task = NULL;
163+ struct file *efd_file = NULL;
164+
165+ dev = get_dev_ptr(args->if_name);
166+ if (dev == NULL)
167+ return -ENODEV;
168+ /* Utilize dev->platform_data to save netdevice
169+ pointer as it will not be registered */
170+ if (dev->platform_data) {
171+ pr_debug("%s: IF %s already initialized\n",
172+ __func__, args->if_name);
173+ /* This will happen when application is not able to initiate
174+ * cleanup in last run. We still need to save the new
175+ * eventfd context.
176+ */
177+ net_dev = dev->platform_data;
178+ priv = netdev_priv(net_dev);
179+
180+ /* Get current task context from which IOCTL was called */
181+ userspace_task = current;
182+
183+ rcu_read_lock();
184+ efd_file = fcheck_files(userspace_task->files, args->efd);
185+ rcu_read_unlock();
186+
187+ priv->efd_ctx = eventfd_ctx_fileget(efd_file);
188+ if (!priv->efd_ctx) {
189+ pr_err(KBUILD_MODNAME "get eventfd context failed\n");
190+ /* Free the allocated memory for net device */
191+ dev->platform_data = NULL;
192+ free_netdev(net_dev);
193+ return -EINVAL;
194+ }
195+ /* Since there will be NO PHY update as link is already setup,
196+ * wake user context once so that current PHY status can
197+ * be fetched.
198+ */
199+ phy_link_updates(net_dev);
200+ return 0;
201+ }
202+
203+ proxy_dev = dev_get_drvdata(dev);
204+ mac_dev = proxy_dev->mac_dev;
205+ /* Allocate an dummy net device for proxy interface */
206+ net_dev = alloc_etherdev(sizeof(*priv));
207+ if (!net_dev) {
208+ pr_err(KBUILD_MODNAME "alloc_etherdev failed\n");
209+ return -ENOMEM;
210+ } else {
211+ SET_NETDEV_DEV(net_dev, dev);
212+ priv = netdev_priv(net_dev);
213+ priv->mac_dev = mac_dev;
214+ /* Get current task context from which IOCTL was called */
215+ userspace_task = current;
216+
217+ rcu_read_lock();
218+ efd_file = fcheck_files(userspace_task->files, args->efd);
219+ rcu_read_unlock();
220+
221+ priv->efd_ctx = eventfd_ctx_fileget(efd_file);
222+
223+ if (!priv->efd_ctx) {
224+ pr_err(KBUILD_MODNAME "get eventfd context failed\n");
225+ /* Free the allocated memory for net device */
226+ free_netdev(net_dev);
227+ return -EINVAL;
228+ }
229+ strncpy(net_dev->name, args->if_name, IF_NAME_MAX_LEN);
230+ dev->platform_data = net_dev;
231+ }
232+
233+ pr_debug("%s: mac_dev %p cell_index %d\n",
234+ __func__, mac_dev, mac_dev->cell_index);
235+ mac_dev->phy_dev = of_phy_connect(net_dev, mac_dev->phy_node,
236+ phy_link_updates, 0, mac_dev->phy_if);
237+ if (unlikely(mac_dev->phy_dev == NULL) || IS_ERR(mac_dev->phy_dev)) {
238+ pr_err("%s: --------Error in PHY Connect\n", __func__);
239+ /* Free the allocated memory for net device */
240+ free_netdev(net_dev);
241+ return -ENODEV;
242+ }
243+ net_dev->phydev = mac_dev->phy_dev;
244+ mac_dev->start(mac_dev);
245+ pr_debug("%s: --- PHY connected for %s\n", __func__, args->if_name);
246+
247+ return 0;
248+}
249+
250+/* IOCTL handler for disabling Link status for a given interface
251+ * Input parameters:
252+ * if_name: This the network interface node name as defind in
253+ * device tree file. Currently, it has format of
254+ * "ethernet@x" type for each interface.
255+ */
256+static int ioctl_disable_if_link_status(char *if_name)
257+{
258+ struct net_device *net_dev = NULL;
259+ struct device *dev;
260+ struct mac_device *mac_dev;
261+ struct proxy_device *proxy_dev;
262+ struct dpa_proxy_priv_s *priv = NULL;
263+
264+ dev = get_dev_ptr(if_name);
265+ if (dev == NULL)
266+ return -ENODEV;
267+ /* Utilize dev->platform_data to save netdevice
268+ pointer as it will not be registered */
269+ if (!dev->platform_data) {
270+ pr_debug("%s: IF %s already Disabled for Link status\n",
271+ __func__, if_name);
272+ return 0;
273+ }
274+
275+ net_dev = dev->platform_data;
276+ proxy_dev = dev_get_drvdata(dev);
277+ mac_dev = proxy_dev->mac_dev;
278+ mac_dev->stop(mac_dev);
279+
280+ priv = netdev_priv(net_dev);
281+ eventfd_ctx_put(priv->efd_ctx);
282+
283+ /* This will also deregister the call back */
284+ phy_disconnect(mac_dev->phy_dev);
285+ phy_resume(mac_dev->phy_dev);
286+
287+ free_netdev(net_dev);
288+ dev->platform_data = NULL;
289+
290+ pr_debug("%s: Link status Disabled for %s\n", __func__, if_name);
291+ return 0;
292+}
293+
294 static long usdpaa_ioctl(struct file *fp, unsigned int cmd, unsigned long arg)
295 {
296 struct ctx *ctx = fp->private_data;
297@@ -1722,6 +1957,47 @@ static long usdpaa_ioctl(struct file *fp
298 return -EFAULT;
299 return ioctl_free_raw_portal(fp, ctx, &input);
300 }
301+ case USDPAA_IOCTL_ENABLE_LINK_STATUS_INTERRUPT:
302+ {
303+ struct usdpaa_ioctl_link_status input;
304+ int ret;
305+
306+ if (copy_from_user(&input, a, sizeof(input)))
307+ return -EFAULT;
308+ ret = ioctl_en_if_link_status(&input);
309+ if (ret)
310+ pr_err("Error(%d) enable link interrupt:IF: %s\n",
311+ ret, input.if_name);
312+ return ret;
313+ }
314+ case USDPAA_IOCTL_DISABLE_LINK_STATUS_INTERRUPT:
315+ {
316+ char *input;
317+ int ret;
318+
319+ if (copy_from_user(&input, a, sizeof(input)))
320+ return -EFAULT;
321+ ret = ioctl_disable_if_link_status(input);
322+ if (ret)
323+ pr_err("Error(%d) Disabling link interrupt:IF: %s\n",
324+ ret, input);
325+ return ret;
326+ }
327+ case USDPAA_IOCTL_GET_LINK_STATUS:
328+ {
329+ struct usdpaa_ioctl_link_status_args input;
330+
331+ if (copy_from_user(&input, a, sizeof(input)))
332+ return -EFAULT;
333+
334+ input.link_status = ioctl_usdpaa_get_link_status(input.if_name);
335+ if (input.link_status < 0)
336+ return input.link_status;
337+ if (copy_to_user(a, &input, sizeof(input)))
338+ return -EFAULT;
339+
340+ return 0;
341+ }
342 }
343 return -EINVAL;
344 }
345--- a/include/linux/fsl_usdpaa.h
346+++ b/include/linux/fsl_usdpaa.h
347@@ -365,6 +365,38 @@ int dpa_alloc_pop(struct dpa_alloc *allo
348 int dpa_alloc_check(struct dpa_alloc *list, u32 id);
349 #endif /* __KERNEL__ */
350
351+
352+/************************************
353+ * Link Status support for user space
354+ * interface
355+ ************************************/
356+#define IF_NAME_MAX_LEN 16
357+#define NODE_NAME_LEN 32
358+
359+struct usdpaa_ioctl_link_status {
360+ /* network device node name */
361+ char if_name[IF_NAME_MAX_LEN];
362+ /* Eventfd value */
363+ uint32_t efd;
364+};
365+
366+#define USDPAA_IOCTL_ENABLE_LINK_STATUS_INTERRUPT \
367+ _IOW(USDPAA_IOCTL_MAGIC, 0x0E, struct usdpaa_ioctl_link_status)
368+
369+#define USDPAA_IOCTL_DISABLE_LINK_STATUS_INTERRUPT \
370+ _IOW(USDPAA_IOCTL_MAGIC, 0x0F, char *)
371+
372+struct usdpaa_ioctl_link_status_args {
373+ /* network device node name */
374+ char if_name[IF_NAME_MAX_LEN];
375+ /* link status(UP/DOWN) */
376+ int link_status;
377+};
378+
379+#define USDPAA_IOCTL_GET_LINK_STATUS \
380+ _IOWR(USDPAA_IOCTL_MAGIC, 0x10, struct usdpaa_ioctl_link_status_args)
381+
382+
383 #ifdef __cplusplus
384 }
385 #endif