mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/chenhuacai/linux-loongson
synced 2025-08-28 00:19:36 +00:00
net: pse-pd: Add support for reporting events
Add support for devm_pse_irq_helper() to register PSE interrupts and report events such as over-current or over-temperature conditions. This follows a similar approach to the regulator API but also sends notifications using a dedicated PSE ethtool netlink socket. Signed-off-by: Kory Maincent (Dent Project) <kory.maincent@bootlin.com> Link: https://patch.msgid.link/20250617-feature_poe_port_prio-v14-2-78a1a645e2ee@bootlin.com Signed-off-by: Jakub Kicinski <kuba@kernel.org>
This commit is contained in:
parent
fa2f045417
commit
fc0e6db309
@ -118,6 +118,17 @@ definitions:
|
||||
doc: |
|
||||
Hardware timestamp comes from one PHY device
|
||||
of the network topology
|
||||
-
|
||||
name: pse-event
|
||||
doc: PSE event list for the PSE controller
|
||||
type: flags
|
||||
entries:
|
||||
-
|
||||
name: over-current
|
||||
doc: PSE output current is too high
|
||||
-
|
||||
name: over-temp
|
||||
doc: PSE in over temperature state
|
||||
|
||||
attribute-sets:
|
||||
-
|
||||
@ -1555,6 +1566,19 @@ attribute-sets:
|
||||
name: hwtstamp-flags
|
||||
type: nest
|
||||
nested-attributes: bitset
|
||||
-
|
||||
name: pse-ntf
|
||||
attr-cnt-name: --ethtool-a-pse-ntf-cnt
|
||||
attributes:
|
||||
-
|
||||
name: header
|
||||
type: nest
|
||||
nested-attributes: header
|
||||
-
|
||||
name: events
|
||||
type: uint
|
||||
enum: pse-event
|
||||
doc: List of events reported by the PSE controller
|
||||
|
||||
operations:
|
||||
enum-model: directional
|
||||
@ -2413,3 +2437,13 @@ operations:
|
||||
attributes: *tsconfig
|
||||
reply:
|
||||
attributes: *tsconfig
|
||||
-
|
||||
name: pse-ntf
|
||||
doc: Notification for PSE events.
|
||||
|
||||
attribute-set: pse-ntf
|
||||
|
||||
event:
|
||||
attributes:
|
||||
- header
|
||||
- events
|
||||
|
@ -290,6 +290,7 @@ Kernel to userspace:
|
||||
``ETHTOOL_MSG_PHY_NTF`` Ethernet PHY information change
|
||||
``ETHTOOL_MSG_TSCONFIG_GET_REPLY`` hw timestamping configuration
|
||||
``ETHTOOL_MSG_TSCONFIG_SET_REPLY`` new hw timestamping configuration
|
||||
``ETHTOOL_MSG_PSE_NTF`` PSE events notification
|
||||
======================================== =================================
|
||||
|
||||
``GET`` requests are sent by userspace applications to retrieve device
|
||||
@ -1896,6 +1897,24 @@ various existing products that document power consumption in watts rather than
|
||||
classes. If power limit configuration based on classes is needed, the
|
||||
conversion can be done in user space, for example by ethtool.
|
||||
|
||||
PSE_NTF
|
||||
=======
|
||||
|
||||
Notify PSE events.
|
||||
|
||||
Notification contents:
|
||||
|
||||
=============================== ====== ========================
|
||||
``ETHTOOL_A_PSE_HEADER`` nested request header
|
||||
``ETHTOOL_A_PSE_EVENTS`` bitset PSE events
|
||||
=============================== ====== ========================
|
||||
|
||||
When set, the optional ``ETHTOOL_A_PSE_EVENTS`` attribute identifies the
|
||||
PSE events.
|
||||
|
||||
.. kernel-doc:: include/uapi/linux/ethtool_netlink_generated.h
|
||||
:identifiers: ethtool_pse_event
|
||||
|
||||
RSS_GET
|
||||
=======
|
||||
|
||||
|
@ -7,10 +7,14 @@
|
||||
|
||||
#include <linux/device.h>
|
||||
#include <linux/ethtool.h>
|
||||
#include <linux/ethtool_netlink.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/phy.h>
|
||||
#include <linux/pse-pd/pse.h>
|
||||
#include <linux/regulator/driver.h>
|
||||
#include <linux/regulator/machine.h>
|
||||
#include <linux/rtnetlink.h>
|
||||
#include <net/net_trackers.h>
|
||||
|
||||
static DEFINE_MUTEX(pse_list_mutex);
|
||||
static LIST_HEAD(pse_controller_list);
|
||||
@ -210,6 +214,48 @@ static int of_load_pse_pis(struct pse_controller_dev *pcdev)
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* pse_control_find_net_by_id - Find net attached to the pse control id
|
||||
* @pcdev: a pointer to the PSE
|
||||
* @id: index of the PSE control
|
||||
*
|
||||
* Return: pse_control pointer or NULL. The device returned has had a
|
||||
* reference added and the pointer is safe until the user calls
|
||||
* pse_control_put() to indicate they have finished with it.
|
||||
*/
|
||||
static struct pse_control *
|
||||
pse_control_find_by_id(struct pse_controller_dev *pcdev, int id)
|
||||
{
|
||||
struct pse_control *psec;
|
||||
|
||||
mutex_lock(&pse_list_mutex);
|
||||
list_for_each_entry(psec, &pcdev->pse_control_head, list) {
|
||||
if (psec->id == id) {
|
||||
kref_get(&psec->refcnt);
|
||||
mutex_unlock(&pse_list_mutex);
|
||||
return psec;
|
||||
}
|
||||
}
|
||||
mutex_unlock(&pse_list_mutex);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* pse_control_get_netdev - Return netdev associated to a PSE control
|
||||
* @psec: PSE control pointer
|
||||
*
|
||||
* Return: netdev pointer or NULL
|
||||
*/
|
||||
static struct net_device *pse_control_get_netdev(struct pse_control *psec)
|
||||
{
|
||||
ASSERT_RTNL();
|
||||
|
||||
if (!psec || !psec->attached_phydev)
|
||||
return NULL;
|
||||
|
||||
return psec->attached_phydev->attached_dev;
|
||||
}
|
||||
|
||||
static int pse_pi_is_enabled(struct regulator_dev *rdev)
|
||||
{
|
||||
struct pse_controller_dev *pcdev = rdev_get_drvdata(rdev);
|
||||
@ -559,6 +605,139 @@ int devm_pse_controller_register(struct device *dev,
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(devm_pse_controller_register);
|
||||
|
||||
struct pse_irq {
|
||||
struct pse_controller_dev *pcdev;
|
||||
struct pse_irq_desc desc;
|
||||
unsigned long *notifs;
|
||||
};
|
||||
|
||||
/**
|
||||
* pse_to_regulator_notifs - Convert PSE notifications to Regulator
|
||||
* notifications
|
||||
* @notifs: PSE notifications
|
||||
*
|
||||
* Return: Regulator notifications
|
||||
*/
|
||||
static unsigned long pse_to_regulator_notifs(unsigned long notifs)
|
||||
{
|
||||
unsigned long rnotifs = 0;
|
||||
|
||||
if (notifs & ETHTOOL_PSE_EVENT_OVER_CURRENT)
|
||||
rnotifs |= REGULATOR_EVENT_OVER_CURRENT;
|
||||
if (notifs & ETHTOOL_PSE_EVENT_OVER_TEMP)
|
||||
rnotifs |= REGULATOR_EVENT_OVER_TEMP;
|
||||
|
||||
return rnotifs;
|
||||
}
|
||||
|
||||
/**
|
||||
* pse_isr - IRQ handler for PSE
|
||||
* @irq: irq number
|
||||
* @data: pointer to user interrupt structure
|
||||
*
|
||||
* Return: irqreturn_t - status of IRQ
|
||||
*/
|
||||
static irqreturn_t pse_isr(int irq, void *data)
|
||||
{
|
||||
struct pse_controller_dev *pcdev;
|
||||
unsigned long notifs_mask = 0;
|
||||
struct pse_irq_desc *desc;
|
||||
struct pse_irq *h = data;
|
||||
int ret, i;
|
||||
|
||||
desc = &h->desc;
|
||||
pcdev = h->pcdev;
|
||||
|
||||
/* Clear notifs mask */
|
||||
memset(h->notifs, 0, pcdev->nr_lines * sizeof(*h->notifs));
|
||||
mutex_lock(&pcdev->lock);
|
||||
ret = desc->map_event(irq, pcdev, h->notifs, ¬ifs_mask);
|
||||
mutex_unlock(&pcdev->lock);
|
||||
if (ret || !notifs_mask)
|
||||
return IRQ_NONE;
|
||||
|
||||
for_each_set_bit(i, ¬ifs_mask, pcdev->nr_lines) {
|
||||
unsigned long notifs, rnotifs;
|
||||
struct net_device *netdev;
|
||||
struct pse_control *psec;
|
||||
|
||||
/* Do nothing PI not described */
|
||||
if (!pcdev->pi[i].rdev)
|
||||
continue;
|
||||
|
||||
notifs = h->notifs[i];
|
||||
dev_dbg(h->pcdev->dev,
|
||||
"Sending PSE notification EVT 0x%lx\n", notifs);
|
||||
|
||||
psec = pse_control_find_by_id(pcdev, i);
|
||||
rtnl_lock();
|
||||
netdev = pse_control_get_netdev(psec);
|
||||
if (netdev)
|
||||
ethnl_pse_send_ntf(netdev, notifs);
|
||||
rtnl_unlock();
|
||||
pse_control_put(psec);
|
||||
|
||||
rnotifs = pse_to_regulator_notifs(notifs);
|
||||
regulator_notifier_call_chain(pcdev->pi[i].rdev, rnotifs,
|
||||
NULL);
|
||||
}
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
/**
|
||||
* devm_pse_irq_helper - Register IRQ based PSE event notifier
|
||||
* @pcdev: a pointer to the PSE
|
||||
* @irq: the irq value to be passed to request_irq
|
||||
* @irq_flags: the flags to be passed to request_irq
|
||||
* @d: PSE interrupt description
|
||||
*
|
||||
* Return: 0 on success and errno on failure
|
||||
*/
|
||||
int devm_pse_irq_helper(struct pse_controller_dev *pcdev, int irq,
|
||||
int irq_flags, const struct pse_irq_desc *d)
|
||||
{
|
||||
struct device *dev = pcdev->dev;
|
||||
size_t irq_name_len;
|
||||
struct pse_irq *h;
|
||||
char *irq_name;
|
||||
int ret;
|
||||
|
||||
if (!d || !d->map_event || !d->name)
|
||||
return -EINVAL;
|
||||
|
||||
h = devm_kzalloc(dev, sizeof(*h), GFP_KERNEL);
|
||||
if (!h)
|
||||
return -ENOMEM;
|
||||
|
||||
h->pcdev = pcdev;
|
||||
h->desc = *d;
|
||||
|
||||
/* IRQ name len is pcdev dev name + 5 char + irq desc name + 1 */
|
||||
irq_name_len = strlen(dev_name(pcdev->dev)) + 5 + strlen(d->name) + 1;
|
||||
irq_name = devm_kzalloc(dev, irq_name_len, GFP_KERNEL);
|
||||
if (!irq_name)
|
||||
return -ENOMEM;
|
||||
|
||||
snprintf(irq_name, irq_name_len, "pse-%s:%s", dev_name(pcdev->dev),
|
||||
d->name);
|
||||
|
||||
h->notifs = devm_kcalloc(dev, pcdev->nr_lines,
|
||||
sizeof(*h->notifs), GFP_KERNEL);
|
||||
if (!h->notifs)
|
||||
return -ENOMEM;
|
||||
|
||||
ret = devm_request_threaded_irq(dev, irq, NULL, pse_isr,
|
||||
IRQF_ONESHOT | irq_flags,
|
||||
irq_name, h);
|
||||
if (ret)
|
||||
dev_err(pcdev->dev, "Failed to request IRQ %d\n", irq);
|
||||
|
||||
pcdev->irq = irq;
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(devm_pse_irq_helper);
|
||||
|
||||
/* PSE control section */
|
||||
|
||||
static void __pse_control_release(struct kref *kref)
|
||||
|
@ -43,6 +43,8 @@ void ethtool_aggregate_rmon_stats(struct net_device *dev,
|
||||
struct ethtool_rmon_stats *rmon_stats);
|
||||
bool ethtool_dev_mm_supported(struct net_device *dev);
|
||||
|
||||
void ethnl_pse_send_ntf(struct net_device *netdev, unsigned long notif);
|
||||
|
||||
#else
|
||||
static inline int ethnl_cable_test_alloc(struct phy_device *phydev, u8 cmd)
|
||||
{
|
||||
@ -120,6 +122,11 @@ static inline bool ethtool_dev_mm_supported(struct net_device *dev)
|
||||
return false;
|
||||
}
|
||||
|
||||
static inline void ethnl_pse_send_ntf(struct phy_device *phydev,
|
||||
unsigned long notif)
|
||||
{
|
||||
}
|
||||
|
||||
#endif /* IS_ENABLED(CONFIG_ETHTOOL_NETLINK) */
|
||||
|
||||
static inline int ethnl_cable_test_result(struct phy_device *phydev, u8 pair,
|
||||
|
@ -7,12 +7,15 @@
|
||||
|
||||
#include <linux/list.h>
|
||||
#include <uapi/linux/ethtool.h>
|
||||
#include <uapi/linux/ethtool_netlink_generated.h>
|
||||
#include <linux/regulator/driver.h>
|
||||
|
||||
/* Maximum current in uA according to IEEE 802.3-2022 Table 145-1 */
|
||||
#define MAX_PI_CURRENT 1920000
|
||||
/* Maximum power in mW according to IEEE 802.3-2022 Table 145-16 */
|
||||
#define MAX_PI_PW 99900
|
||||
|
||||
struct net_device;
|
||||
struct phy_device;
|
||||
struct pse_controller_dev;
|
||||
struct netlink_ext_ack;
|
||||
@ -37,6 +40,19 @@ struct ethtool_c33_pse_pw_limit_range {
|
||||
u32 max;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct pse_irq_desc - notification sender description for IRQ based events.
|
||||
*
|
||||
* @name: the visible name for the IRQ
|
||||
* @map_event: driver callback to map IRQ status into PSE devices with events.
|
||||
*/
|
||||
struct pse_irq_desc {
|
||||
const char *name;
|
||||
int (*map_event)(int irq, struct pse_controller_dev *pcdev,
|
||||
unsigned long *notifs,
|
||||
unsigned long *notifs_mask);
|
||||
};
|
||||
|
||||
/**
|
||||
* struct pse_control_config - PSE control/channel configuration.
|
||||
*
|
||||
@ -228,6 +244,7 @@ struct pse_pi {
|
||||
* @types: types of the PSE controller
|
||||
* @pi: table of PSE PIs described in this controller device
|
||||
* @no_of_pse_pi: flag set if the pse_pis devicetree node is not used
|
||||
* @irq: PSE interrupt
|
||||
*/
|
||||
struct pse_controller_dev {
|
||||
const struct pse_controller_ops *ops;
|
||||
@ -241,6 +258,7 @@ struct pse_controller_dev {
|
||||
enum ethtool_pse_types types;
|
||||
struct pse_pi *pi;
|
||||
bool no_of_pse_pi;
|
||||
int irq;
|
||||
};
|
||||
|
||||
#if IS_ENABLED(CONFIG_PSE_CONTROLLER)
|
||||
@ -249,6 +267,8 @@ void pse_controller_unregister(struct pse_controller_dev *pcdev);
|
||||
struct device;
|
||||
int devm_pse_controller_register(struct device *dev,
|
||||
struct pse_controller_dev *pcdev);
|
||||
int devm_pse_irq_helper(struct pse_controller_dev *pcdev, int irq,
|
||||
int irq_flags, const struct pse_irq_desc *d);
|
||||
|
||||
struct pse_control *of_pse_control_get(struct device_node *node,
|
||||
struct phy_device *phydev);
|
||||
|
@ -49,6 +49,16 @@ enum hwtstamp_source {
|
||||
HWTSTAMP_SOURCE_PHYLIB,
|
||||
};
|
||||
|
||||
/**
|
||||
* enum ethtool_pse_event - PSE event list for the PSE controller
|
||||
* @ETHTOOL_PSE_EVENT_OVER_CURRENT: PSE output current is too high
|
||||
* @ETHTOOL_PSE_EVENT_OVER_TEMP: PSE in over temperature state
|
||||
*/
|
||||
enum ethtool_pse_event {
|
||||
ETHTOOL_PSE_EVENT_OVER_CURRENT = 1,
|
||||
ETHTOOL_PSE_EVENT_OVER_TEMP = 2,
|
||||
};
|
||||
|
||||
enum {
|
||||
ETHTOOL_A_HEADER_UNSPEC,
|
||||
ETHTOOL_A_HEADER_DEV_INDEX,
|
||||
@ -718,6 +728,14 @@ enum {
|
||||
ETHTOOL_A_TSCONFIG_MAX = (__ETHTOOL_A_TSCONFIG_CNT - 1)
|
||||
};
|
||||
|
||||
enum {
|
||||
ETHTOOL_A_PSE_NTF_HEADER = 1,
|
||||
ETHTOOL_A_PSE_NTF_EVENTS,
|
||||
|
||||
__ETHTOOL_A_PSE_NTF_CNT,
|
||||
ETHTOOL_A_PSE_NTF_MAX = (__ETHTOOL_A_PSE_NTF_CNT - 1)
|
||||
};
|
||||
|
||||
enum {
|
||||
ETHTOOL_MSG_USER_NONE = 0,
|
||||
ETHTOOL_MSG_STRSET_GET = 1,
|
||||
@ -822,6 +840,7 @@ enum {
|
||||
ETHTOOL_MSG_PHY_NTF,
|
||||
ETHTOOL_MSG_TSCONFIG_GET_REPLY,
|
||||
ETHTOOL_MSG_TSCONFIG_SET_REPLY,
|
||||
ETHTOOL_MSG_PSE_NTF,
|
||||
|
||||
__ETHTOOL_MSG_KERNEL_CNT,
|
||||
ETHTOOL_MSG_KERNEL_MAX = (__ETHTOOL_MSG_KERNEL_CNT - 1)
|
||||
|
@ -315,3 +315,42 @@ const struct ethnl_request_ops ethnl_pse_request_ops = {
|
||||
.set = ethnl_set_pse,
|
||||
/* PSE has no notification */
|
||||
};
|
||||
|
||||
void ethnl_pse_send_ntf(struct net_device *netdev, unsigned long notifs)
|
||||
{
|
||||
void *reply_payload;
|
||||
struct sk_buff *skb;
|
||||
int reply_len;
|
||||
int ret;
|
||||
|
||||
ASSERT_RTNL();
|
||||
|
||||
if (!netdev || !notifs)
|
||||
return;
|
||||
|
||||
reply_len = ethnl_reply_header_size() +
|
||||
nla_total_size(sizeof(u32)); /* _PSE_NTF_EVENTS */
|
||||
|
||||
skb = genlmsg_new(reply_len, GFP_KERNEL);
|
||||
if (!skb)
|
||||
return;
|
||||
|
||||
reply_payload = ethnl_bcastmsg_put(skb, ETHTOOL_MSG_PSE_NTF);
|
||||
if (!reply_payload)
|
||||
goto err_skb;
|
||||
|
||||
ret = ethnl_fill_reply_header(skb, netdev, ETHTOOL_A_PSE_NTF_HEADER);
|
||||
if (ret < 0)
|
||||
goto err_skb;
|
||||
|
||||
if (nla_put_uint(skb, ETHTOOL_A_PSE_NTF_EVENTS, notifs))
|
||||
goto err_skb;
|
||||
|
||||
genlmsg_end(skb, reply_payload);
|
||||
ethnl_multicast(skb, netdev);
|
||||
return;
|
||||
|
||||
err_skb:
|
||||
nlmsg_free(skb);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(ethnl_pse_send_ntf);
|
||||
|
Loading…
Reference in New Issue
Block a user