Skip to content

Commit 3114b07

Browse files
oleremkuba-moo
authored andcommitted
net: add framework to support Ethernet PSE and PDs devices
This framework was create with intention to provide support for Ethernet PSE (Power Sourcing Equipment) and PDs (Powered Device). At current step this patch implements generic PSE support for PoDL (Power over Data Lines 802.3bu) specification with reserving name space for PD devices as well. This framework can be extended to support 802.3af and 802.3at "Power via the Media Dependent Interface" (or PoE/Power over Ethernet) Signed-off-by: Oleksij Rempel <[email protected]> Reviewed-by: Andrew Lunn <[email protected]> Signed-off-by: Jakub Kicinski <[email protected]>
1 parent e9554b3 commit 3114b07

File tree

6 files changed

+341
-0
lines changed

6 files changed

+341
-0
lines changed

drivers/net/Kconfig

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -500,6 +500,8 @@ config NET_SB1000
500500

501501
source "drivers/net/phy/Kconfig"
502502

503+
source "drivers/net/pse-pd/Kconfig"
504+
503505
source "drivers/net/can/Kconfig"
504506

505507
source "drivers/net/mctp/Kconfig"

drivers/net/Makefile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ obj-$(CONFIG_NET) += loopback.o
2323
obj-$(CONFIG_NETDEV_LEGACY_INIT) += Space.o
2424
obj-$(CONFIG_NETCONSOLE) += netconsole.o
2525
obj-y += phy/
26+
obj-y += pse-pd/
2627
obj-y += mdio/
2728
obj-y += pcs/
2829
obj-$(CONFIG_RIONET) += rionet.o

drivers/net/pse-pd/Kconfig

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
# SPDX-License-Identifier: GPL-2.0-only
2+
#
3+
# Ethernet Power Sourcing Equipment drivers
4+
#
5+
6+
menuconfig PSE_CONTROLLER
7+
bool "Ethernet Power Sourcing Equipment Support"
8+
help
9+
Generic Power Sourcing Equipment Controller support.
10+
11+
If unsure, say no.

drivers/net/pse-pd/Makefile

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
# SPDX-License-Identifier: GPL-2.0-only
2+
# Makefile for Linux PSE drivers
3+
4+
obj-$(CONFIG_PSE_CONTROLLER) += pse_core.o

drivers/net/pse-pd/pse_core.c

Lines changed: 256 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,256 @@
1+
// SPDX-License-Identifier: GPL-2.0-only
2+
//
3+
// Framework for Ethernet Power Sourcing Equipment
4+
//
5+
// Copyright (c) 2022 Pengutronix, Oleksij Rempel <[email protected]>
6+
//
7+
8+
#include <linux/device.h>
9+
#include <linux/of.h>
10+
#include <linux/pse-pd/pse.h>
11+
12+
static DEFINE_MUTEX(pse_list_mutex);
13+
static LIST_HEAD(pse_controller_list);
14+
15+
/**
16+
* struct pse_control - a PSE control
17+
* @pcdev: a pointer to the PSE controller device
18+
* this PSE control belongs to
19+
* @list: list entry for the pcdev's PSE controller list
20+
* @id: ID of the PSE line in the PSE controller device
21+
* @refcnt: Number of gets of this pse_control
22+
*/
23+
struct pse_control {
24+
struct pse_controller_dev *pcdev;
25+
struct list_head list;
26+
unsigned int id;
27+
struct kref refcnt;
28+
};
29+
30+
/**
31+
* of_pse_zero_xlate - dummy function for controllers with one only control
32+
* @pcdev: a pointer to the PSE controller device
33+
* @pse_spec: PSE line specifier as found in the device tree
34+
*
35+
* This static translation function is used by default if of_xlate in
36+
* :c:type:`pse_controller_dev` is not set. It is useful for all PSE
37+
* controllers with #pse-cells = <0>.
38+
*/
39+
static int of_pse_zero_xlate(struct pse_controller_dev *pcdev,
40+
const struct of_phandle_args *pse_spec)
41+
{
42+
return 0;
43+
}
44+
45+
/**
46+
* of_pse_simple_xlate - translate pse_spec to the PSE line number
47+
* @pcdev: a pointer to the PSE controller device
48+
* @pse_spec: PSE line specifier as found in the device tree
49+
*
50+
* This static translation function is used by default if of_xlate in
51+
* :c:type:`pse_controller_dev` is not set. It is useful for all PSE
52+
* controllers with 1:1 mapping, where PSE lines can be indexed by number
53+
* without gaps.
54+
*/
55+
static int of_pse_simple_xlate(struct pse_controller_dev *pcdev,
56+
const struct of_phandle_args *pse_spec)
57+
{
58+
if (pse_spec->args[0] >= pcdev->nr_lines)
59+
return -EINVAL;
60+
61+
return pse_spec->args[0];
62+
}
63+
64+
/**
65+
* pse_controller_register - register a PSE controller device
66+
* @pcdev: a pointer to the initialized PSE controller device
67+
*/
68+
int pse_controller_register(struct pse_controller_dev *pcdev)
69+
{
70+
if (!pcdev->of_xlate) {
71+
if (pcdev->of_pse_n_cells == 0)
72+
pcdev->of_xlate = of_pse_zero_xlate;
73+
else if (pcdev->of_pse_n_cells == 1)
74+
pcdev->of_xlate = of_pse_simple_xlate;
75+
}
76+
77+
mutex_init(&pcdev->lock);
78+
INIT_LIST_HEAD(&pcdev->pse_control_head);
79+
80+
mutex_lock(&pse_list_mutex);
81+
list_add(&pcdev->list, &pse_controller_list);
82+
mutex_unlock(&pse_list_mutex);
83+
84+
return 0;
85+
}
86+
EXPORT_SYMBOL_GPL(pse_controller_register);
87+
88+
/**
89+
* pse_controller_unregister - unregister a PSE controller device
90+
* @pcdev: a pointer to the PSE controller device
91+
*/
92+
void pse_controller_unregister(struct pse_controller_dev *pcdev)
93+
{
94+
mutex_lock(&pse_list_mutex);
95+
list_del(&pcdev->list);
96+
mutex_unlock(&pse_list_mutex);
97+
}
98+
EXPORT_SYMBOL_GPL(pse_controller_unregister);
99+
100+
static void devm_pse_controller_release(struct device *dev, void *res)
101+
{
102+
pse_controller_unregister(*(struct pse_controller_dev **)res);
103+
}
104+
105+
/**
106+
* devm_pse_controller_register - resource managed pse_controller_register()
107+
* @dev: device that is registering this PSE controller
108+
* @pcdev: a pointer to the initialized PSE controller device
109+
*
110+
* Managed pse_controller_register(). For PSE controllers registered by
111+
* this function, pse_controller_unregister() is automatically called on
112+
* driver detach. See pse_controller_register() for more information.
113+
*/
114+
int devm_pse_controller_register(struct device *dev,
115+
struct pse_controller_dev *pcdev)
116+
{
117+
struct pse_controller_dev **pcdevp;
118+
int ret;
119+
120+
pcdevp = devres_alloc(devm_pse_controller_release, sizeof(*pcdevp),
121+
GFP_KERNEL);
122+
if (!pcdevp)
123+
return -ENOMEM;
124+
125+
ret = pse_controller_register(pcdev);
126+
if (ret) {
127+
devres_free(pcdevp);
128+
return ret;
129+
}
130+
131+
*pcdevp = pcdev;
132+
devres_add(dev, pcdevp);
133+
134+
return 0;
135+
}
136+
EXPORT_SYMBOL_GPL(devm_pse_controller_register);
137+
138+
/* PSE control section */
139+
140+
static void __pse_control_release(struct kref *kref)
141+
{
142+
struct pse_control *psec = container_of(kref, struct pse_control,
143+
refcnt);
144+
145+
lockdep_assert_held(&pse_list_mutex);
146+
147+
module_put(psec->pcdev->owner);
148+
149+
list_del(&psec->list);
150+
kfree(psec);
151+
}
152+
153+
static void __pse_control_put_internal(struct pse_control *psec)
154+
{
155+
lockdep_assert_held(&pse_list_mutex);
156+
157+
kref_put(&psec->refcnt, __pse_control_release);
158+
}
159+
160+
/**
161+
* pse_control_put - free the PSE control
162+
* @psec: PSE control pointer
163+
*/
164+
void pse_control_put(struct pse_control *psec)
165+
{
166+
if (IS_ERR_OR_NULL(psec))
167+
return;
168+
169+
mutex_lock(&pse_list_mutex);
170+
__pse_control_put_internal(psec);
171+
mutex_unlock(&pse_list_mutex);
172+
}
173+
EXPORT_SYMBOL_GPL(pse_control_put);
174+
175+
static struct pse_control *
176+
pse_control_get_internal(struct pse_controller_dev *pcdev, unsigned int index)
177+
{
178+
struct pse_control *psec;
179+
180+
lockdep_assert_held(&pse_list_mutex);
181+
182+
list_for_each_entry(psec, &pcdev->pse_control_head, list) {
183+
if (psec->id == index) {
184+
kref_get(&psec->refcnt);
185+
return psec;
186+
}
187+
}
188+
189+
psec = kzalloc(sizeof(*psec), GFP_KERNEL);
190+
if (!psec)
191+
return ERR_PTR(-ENOMEM);
192+
193+
if (!try_module_get(pcdev->owner)) {
194+
kfree(psec);
195+
return ERR_PTR(-ENODEV);
196+
}
197+
198+
psec->pcdev = pcdev;
199+
list_add(&psec->list, &pcdev->pse_control_head);
200+
psec->id = index;
201+
kref_init(&psec->refcnt);
202+
203+
return psec;
204+
}
205+
206+
struct pse_control *
207+
of_pse_control_get(struct device_node *node)
208+
{
209+
struct pse_controller_dev *r, *pcdev;
210+
struct of_phandle_args args;
211+
struct pse_control *psec;
212+
int psec_id;
213+
int ret;
214+
215+
if (!node)
216+
return ERR_PTR(-EINVAL);
217+
218+
ret = of_parse_phandle_with_args(node, "pses", "#pse-cells", 0, &args);
219+
if (ret)
220+
return ERR_PTR(ret);
221+
222+
mutex_lock(&pse_list_mutex);
223+
pcdev = NULL;
224+
list_for_each_entry(r, &pse_controller_list, list) {
225+
if (args.np == r->dev->of_node) {
226+
pcdev = r;
227+
break;
228+
}
229+
}
230+
231+
if (!pcdev) {
232+
psec = ERR_PTR(-EPROBE_DEFER);
233+
goto out;
234+
}
235+
236+
if (WARN_ON(args.args_count != pcdev->of_pse_n_cells)) {
237+
psec = ERR_PTR(-EINVAL);
238+
goto out;
239+
}
240+
241+
psec_id = pcdev->of_xlate(pcdev, &args);
242+
if (psec_id < 0) {
243+
psec = ERR_PTR(psec_id);
244+
goto out;
245+
}
246+
247+
/* pse_list_mutex also protects the pcdev's pse_control list */
248+
psec = pse_control_get_internal(pcdev, psec_id);
249+
250+
out:
251+
mutex_unlock(&pse_list_mutex);
252+
of_node_put(args.np);
253+
254+
return psec;
255+
}
256+
EXPORT_SYMBOL_GPL(of_pse_control_get);

include/linux/pse-pd/pse.h

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
// SPDX-License-Identifier: GPL-2.0-only
2+
/*
3+
// Copyright (c) 2022 Pengutronix, Oleksij Rempel <[email protected]>
4+
*/
5+
#ifndef _LINUX_PSE_CONTROLLER_H
6+
#define _LINUX_PSE_CONTROLLER_H
7+
8+
#include <linux/ethtool.h>
9+
#include <linux/list.h>
10+
#include <uapi/linux/ethtool.h>
11+
12+
struct module;
13+
struct device_node;
14+
struct of_phandle_args;
15+
struct pse_control;
16+
17+
/**
18+
* struct pse_controller_dev - PSE controller entity that might
19+
* provide multiple PSE controls
20+
* @ops: a pointer to device specific struct pse_controller_ops
21+
* @owner: kernel module of the PSE controller driver
22+
* @list: internal list of PSE controller devices
23+
* @pse_control_head: head of internal list of requested PSE controls
24+
* @dev: corresponding driver model device struct
25+
* @of_pse_n_cells: number of cells in PSE line specifiers
26+
* @of_xlate: translation function to translate from specifier as found in the
27+
* device tree to id as given to the PSE control ops
28+
* @nr_lines: number of PSE controls in this controller device
29+
* @lock: Mutex for serialization access to the PSE controller
30+
*/
31+
struct pse_controller_dev {
32+
const struct pse_controller_ops *ops;
33+
struct module *owner;
34+
struct list_head list;
35+
struct list_head pse_control_head;
36+
struct device *dev;
37+
int of_pse_n_cells;
38+
int (*of_xlate)(struct pse_controller_dev *pcdev,
39+
const struct of_phandle_args *pse_spec);
40+
unsigned int nr_lines;
41+
struct mutex lock;
42+
};
43+
44+
#if IS_ENABLED(CONFIG_PSE_CONTROLLER)
45+
int pse_controller_register(struct pse_controller_dev *pcdev);
46+
void pse_controller_unregister(struct pse_controller_dev *pcdev);
47+
struct device;
48+
int devm_pse_controller_register(struct device *dev,
49+
struct pse_controller_dev *pcdev);
50+
51+
struct pse_control *of_pse_control_get(struct device_node *node);
52+
void pse_control_put(struct pse_control *psec);
53+
54+
#else
55+
56+
static inline struct pse_control *of_pse_control_get(struct device_node *node)
57+
{
58+
return ERR_PTR(-ENOENT);
59+
}
60+
61+
static inline void pse_control_put(struct pse_control *psec)
62+
{
63+
}
64+
65+
#endif
66+
67+
#endif

0 commit comments

Comments
 (0)