From patchwork Fri Oct 30 11:44:30 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: =?utf-8?q?Marek_Beh=C3=BAn?= X-Patchwork-Id: 315021 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-13.1 required=3.0 tests=BAYES_00,DKIMWL_WL_HIGH, DKIM_SIGNED, DKIM_VALID, DKIM_VALID_AU, INCLUDES_PATCH, MAILING_LIST_MULTI, SIGNED_OFF_BY,SPF_HELO_NONE,SPF_PASS,USER_AGENT_GIT autolearn=ham autolearn_force=no version=3.4.0 Received: from mail.kernel.org (mail.kernel.org [198.145.29.99]) by smtp.lore.kernel.org (Postfix) with ESMTP id 7BFBCC4363A for ; Fri, 30 Oct 2020 11:44:48 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id 284E3207DE for ; Fri, 30 Oct 2020 11:44:48 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=default; t=1604058288; bh=cryOkmKl8L3TgEJqLbJ8D9YdHNR4WBDJ0yPKZfD9574=; h=From:To:Cc:Subject:Date:In-Reply-To:References:List-ID:From; b=fOjJrA+Tq90mBROQ0eAX897dMORmULEsKHg7runZK7cVVkQETrefIkl+CQglUBuDa pMBHRVmV7CVrSKA4YsHWAJZx0NZsId/I+ht32/O96ACuDwZDyX3NUK9JznfG6tXSjY nPmKPpHC54/Tfj5ZeK8R22VKivPFmb0I7SSoQ7sM= Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1725993AbgJ3Lor (ORCPT ); Fri, 30 Oct 2020 07:44:47 -0400 Received: from mail.kernel.org ([198.145.29.99]:37324 "EHLO mail.kernel.org" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1725808AbgJ3Lor (ORCPT ); Fri, 30 Oct 2020 07:44:47 -0400 Received: from dellmb.labs.office.nic.cz (nat-1.nic.cz [217.31.205.1]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by mail.kernel.org (Postfix) with ESMTPSA id 9D6B622210; Fri, 30 Oct 2020 11:44:43 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=default; t=1604058285; bh=cryOkmKl8L3TgEJqLbJ8D9YdHNR4WBDJ0yPKZfD9574=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=OPokpCHbiUOTXeZcaNpfYxKyv6II4WoNwxDO/Hz5P7ZryapStNfWJi5k61eHhVVbA FZsGQDtDcQF5tK+xPzrVdH1b18v7vO5Ihd3JKY3s/tRodovPj/OhDhbPP9j+p2Wnwe 5S9X/fHhJjwLlex3nVKgekMNUluFM/SCCLBfWP3E= From: =?utf-8?q?Marek_Beh=C3=BAn?= To: netdev@vger.kernel.org Cc: linux-leds@vger.kernel.org, Pavel Machek , Dan Murphy , Russell King , Andrew Lunn , Matthias Schiffer , "David S. Miller" , Jacek Anaszewski , Ben Whitten , =?utf-8?q?Marek_Beh=C3=BAn?= Subject: [PATCH RFC leds + net-next 2/7] leds: trigger: netdev: simplify the driver by using bit field members Date: Fri, 30 Oct 2020 12:44:30 +0100 Message-Id: <20201030114435.20169-3-kabel@kernel.org> X-Mailer: git-send-email 2.26.2 In-Reply-To: <20201030114435.20169-1-kabel@kernel.org> References: <20201030114435.20169-1-kabel@kernel.org> MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: linux-leds@vger.kernel.org Use bit fields members in struct led_netdev_data instead of one mode member and set_bit/clear_bit/test_bit functions. These functions are suitable for longer or variable length bit arrays. Signed-off-by: Marek Behún --- drivers/leds/trigger/ledtrig-netdev.c | 69 ++++++++++++--------------- 1 file changed, 30 insertions(+), 39 deletions(-) diff --git a/drivers/leds/trigger/ledtrig-netdev.c b/drivers/leds/trigger/ledtrig-netdev.c index 4f6b73e3b491..8f013b6df4fa 100644 --- a/drivers/leds/trigger/ledtrig-netdev.c +++ b/drivers/leds/trigger/ledtrig-netdev.c @@ -49,11 +49,11 @@ struct led_netdev_data { atomic_t interval; unsigned int last_activity; - unsigned long mode; -#define NETDEV_LED_LINK 0 -#define NETDEV_LED_TX 1 -#define NETDEV_LED_RX 2 -#define NETDEV_LED_MODE_LINKUP 3 + unsigned link:1; + unsigned tx:1; + unsigned rx:1; + + unsigned linkup:1; }; enum netdev_led_attr { @@ -73,10 +73,10 @@ static void set_baseline_state(struct led_netdev_data *trigger_data) if (!led_cdev->blink_brightness) led_cdev->blink_brightness = led_cdev->max_brightness; - if (!test_bit(NETDEV_LED_MODE_LINKUP, &trigger_data->mode)) + if (!trigger_data->linkup) led_set_brightness(led_cdev, LED_OFF); else { - if (test_bit(NETDEV_LED_LINK, &trigger_data->mode)) + if (trigger_data->link) led_set_brightness(led_cdev, led_cdev->blink_brightness); else @@ -85,8 +85,7 @@ static void set_baseline_state(struct led_netdev_data *trigger_data) /* If we are looking for RX/TX start periodically * checking stats */ - if (test_bit(NETDEV_LED_TX, &trigger_data->mode) || - test_bit(NETDEV_LED_RX, &trigger_data->mode)) + if (trigger_data->tx || trigger_data->rx) schedule_delayed_work(&trigger_data->work, 0); } } @@ -131,10 +130,10 @@ static ssize_t device_name_store(struct device *dev, trigger_data->net_dev = dev_get_by_name(&init_net, trigger_data->device_name); - clear_bit(NETDEV_LED_MODE_LINKUP, &trigger_data->mode); + trigger_data->linkup = 0; if (trigger_data->net_dev != NULL) if (netif_carrier_ok(trigger_data->net_dev)) - set_bit(NETDEV_LED_MODE_LINKUP, &trigger_data->mode); + trigger_data->linkup = 1; trigger_data->last_activity = 0; @@ -150,23 +149,24 @@ static ssize_t netdev_led_attr_show(struct device *dev, char *buf, enum netdev_led_attr attr) { struct led_netdev_data *trigger_data = led_trigger_get_drvdata(dev); - int bit; + int val; switch (attr) { case NETDEV_ATTR_LINK: - bit = NETDEV_LED_LINK; + val = trigger_data->link; break; case NETDEV_ATTR_TX: - bit = NETDEV_LED_TX; + val = trigger_data->tx; break; case NETDEV_ATTR_RX: - bit = NETDEV_LED_RX; + val = trigger_data->rx; break; default: - return -EINVAL; + /* unreachable */ + break; } - return sprintf(buf, "%u\n", test_bit(bit, &trigger_data->mode)); + return sprintf(buf, "%u\n", val); } static ssize_t netdev_led_attr_store(struct device *dev, const char *buf, @@ -175,33 +175,28 @@ static ssize_t netdev_led_attr_store(struct device *dev, const char *buf, struct led_netdev_data *trigger_data = led_trigger_get_drvdata(dev); unsigned long state; int ret; - int bit; ret = kstrtoul(buf, 0, &state); if (ret) return ret; + cancel_delayed_work_sync(&trigger_data->work); + switch (attr) { case NETDEV_ATTR_LINK: - bit = NETDEV_LED_LINK; + trigger_data->link = state; break; case NETDEV_ATTR_TX: - bit = NETDEV_LED_TX; + trigger_data->tx = state; break; case NETDEV_ATTR_RX: - bit = NETDEV_LED_RX; + trigger_data->rx = state; break; default: - return -EINVAL; + /* unreachable */ + break; } - cancel_delayed_work_sync(&trigger_data->work); - - if (state) - set_bit(bit, &trigger_data->mode); - else - clear_bit(bit, &trigger_data->mode); - set_baseline_state(trigger_data); return size; @@ -315,7 +310,7 @@ static int netdev_trig_notify(struct notifier_block *nb, spin_lock_bh(&trigger_data->lock); - clear_bit(NETDEV_LED_MODE_LINKUP, &trigger_data->mode); + trigger_data->linkup = 0; switch (evt) { case NETDEV_CHANGENAME: case NETDEV_REGISTER: @@ -331,7 +326,7 @@ static int netdev_trig_notify(struct notifier_block *nb, case NETDEV_UP: case NETDEV_CHANGE: if (netif_carrier_ok(dev)) - set_bit(NETDEV_LED_MODE_LINKUP, &trigger_data->mode); + trigger_data->linkup = 1; break; } @@ -360,21 +355,17 @@ static void netdev_trig_work(struct work_struct *work) } /* If we are not looking for RX/TX then return */ - if (!test_bit(NETDEV_LED_TX, &trigger_data->mode) && - !test_bit(NETDEV_LED_RX, &trigger_data->mode)) + if (!trigger_data->tx && !trigger_data->rx) return; dev_stats = dev_get_stats(trigger_data->net_dev, &temp); - new_activity = - (test_bit(NETDEV_LED_TX, &trigger_data->mode) ? - dev_stats->tx_packets : 0) + - (test_bit(NETDEV_LED_RX, &trigger_data->mode) ? - dev_stats->rx_packets : 0); + new_activity = (trigger_data->tx ? dev_stats->tx_packets : 0) + + (trigger_data->rx ? dev_stats->rx_packets : 0); if (trigger_data->last_activity != new_activity) { led_stop_software_blink(trigger_data->led_cdev); - invert = test_bit(NETDEV_LED_LINK, &trigger_data->mode); + invert = trigger_data->link; interval = jiffies_to_msecs( atomic_read(&trigger_data->interval)); /* base state is ON (link present) */ From patchwork Fri Oct 30 11:44:32 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: =?utf-8?q?Marek_Beh=C3=BAn?= X-Patchwork-Id: 315020 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-13.1 required=3.0 tests=BAYES_00,DKIMWL_WL_HIGH, DKIM_SIGNED, DKIM_VALID, DKIM_VALID_AU, INCLUDES_PATCH, MAILING_LIST_MULTI, SIGNED_OFF_BY,SPF_HELO_NONE,SPF_PASS,URIBL_BLOCKED,USER_AGENT_GIT autolearn=ham autolearn_force=no version=3.4.0 Received: from mail.kernel.org (mail.kernel.org [198.145.29.99]) by smtp.lore.kernel.org (Postfix) with ESMTP id 567E1C4363A for ; Fri, 30 Oct 2020 11:44:53 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id 0169B20825 for ; Fri, 30 Oct 2020 11:44:52 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=default; t=1604058293; bh=mYokQTUy6lsno3U1tjXMofY2tBJRANrDVXM+A08CQko=; h=From:To:Cc:Subject:Date:In-Reply-To:References:List-ID:From; b=x1zz5qOSdV3z6fycJjWCbR5+5G8JyU1cpJDzhj95OTIcgvJM94OTPMxlnwkKluMst Vnqku8Sa4FTmaZGqJeUgpPUvySl7aghJW1HX500FczcCQYASXzMQj0XN25yAMdwJl5 DiAdVnkrEt7VUOYw+0pQb/v+mZ/m0tZVKx+7XOuA= Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1726560AbgJ3Low (ORCPT ); Fri, 30 Oct 2020 07:44:52 -0400 Received: from mail.kernel.org ([198.145.29.99]:37388 "EHLO mail.kernel.org" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1726530AbgJ3Low (ORCPT ); Fri, 30 Oct 2020 07:44:52 -0400 Received: from dellmb.labs.office.nic.cz (nat-1.nic.cz [217.31.205.1]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by mail.kernel.org (Postfix) with ESMTPSA id B2447207DE; Fri, 30 Oct 2020 11:44:48 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=default; t=1604058290; bh=mYokQTUy6lsno3U1tjXMofY2tBJRANrDVXM+A08CQko=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=LuoRdXsyT3cnIewel50zEWxyxvXkdAYGgFdQPsDUVFOuouvEVsgvoNysEVWOIGo/k LdF1HWCFTcRgxSEFeV3Bz8DJnU0Ze2ZMpLcKcjzUi6EkngX4Pff9Uv3x5u8AAK93c2 /nt+twLbaZozDQXyB8yjD601juTLcSOKRkuEp0KY= From: =?utf-8?q?Marek_Beh=C3=BAn?= To: netdev@vger.kernel.org Cc: linux-leds@vger.kernel.org, Pavel Machek , Dan Murphy , Russell King , Andrew Lunn , Matthias Schiffer , "David S. Miller" , Jacek Anaszewski , Ben Whitten , =?utf-8?q?Marek_Beh=C3=BAn?= Subject: [PATCH RFC leds + net-next 4/7] leds: trigger: netdev: support HW offloading Date: Fri, 30 Oct 2020 12:44:32 +0100 Message-Id: <20201030114435.20169-5-kabel@kernel.org> X-Mailer: git-send-email 2.26.2 In-Reply-To: <20201030114435.20169-1-kabel@kernel.org> References: <20201030114435.20169-1-kabel@kernel.org> MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: linux-leds@vger.kernel.org Add support for HW offloading of the netdev trigger. We need to change spinlock to mutex, because if spinlock is used, the trigger_offload() method cannot sleep, which can happen for ethernet PHYs. We can change the spinlock to mutex because, according to Jacek: register_netdevice_notifier() registers raw notifier chain, whose callbacks are not called from atomic context and there are no restrictions on callbacks. See include/linux/notifier.h. Move struct led_trigger_data into global include directory, into file linux/ledtrig.h, so that drivers wanting to offload the trigger can access its settings. Also export the netdev_led_trigger variable and declare it in ledtrih.h so that drivers may check whether the LED is set to this trigger. Signed-off-by: Marek Behún --- drivers/leds/trigger/ledtrig-netdev.c | 48 ++++++++++----------------- include/linux/ledtrig.h | 40 ++++++++++++++++++++++ 2 files changed, 57 insertions(+), 31 deletions(-) create mode 100644 include/linux/ledtrig.h diff --git a/drivers/leds/trigger/ledtrig-netdev.c b/drivers/leds/trigger/ledtrig-netdev.c index 8f013b6df4fa..d4fa031d3a9e 100644 --- a/drivers/leds/trigger/ledtrig-netdev.c +++ b/drivers/leds/trigger/ledtrig-netdev.c @@ -10,17 +10,16 @@ // Copyright 2005-2006 Openedhand Ltd. // Author: Richard Purdie -#include #include #include #include #include #include #include +#include #include #include #include -#include #include #include "../leds.h" @@ -36,26 +35,6 @@ * */ -struct led_netdev_data { - spinlock_t lock; - - struct delayed_work work; - struct notifier_block notifier; - - struct led_classdev *led_cdev; - struct net_device *net_dev; - - char device_name[IFNAMSIZ]; - atomic_t interval; - unsigned int last_activity; - - unsigned link:1; - unsigned tx:1; - unsigned rx:1; - - unsigned linkup:1; -}; - enum netdev_led_attr { NETDEV_ATTR_LINK, NETDEV_ATTR_TX, @@ -73,6 +52,9 @@ static void set_baseline_state(struct led_netdev_data *trigger_data) if (!led_cdev->blink_brightness) led_cdev->blink_brightness = led_cdev->max_brightness; + if (!led_trigger_offload(led_cdev)) + return; + if (!trigger_data->linkup) led_set_brightness(led_cdev, LED_OFF); else { @@ -96,9 +78,9 @@ static ssize_t device_name_show(struct device *dev, struct led_netdev_data *trigger_data = led_trigger_get_drvdata(dev); ssize_t len; - spin_lock_bh(&trigger_data->lock); + mutex_lock(&trigger_data->lock); len = sprintf(buf, "%s\n", trigger_data->device_name); - spin_unlock_bh(&trigger_data->lock); + mutex_unlock(&trigger_data->lock); return len; } @@ -114,7 +96,7 @@ static ssize_t device_name_store(struct device *dev, cancel_delayed_work_sync(&trigger_data->work); - spin_lock_bh(&trigger_data->lock); + mutex_lock(&trigger_data->lock); if (trigger_data->net_dev) { dev_put(trigger_data->net_dev); @@ -138,7 +120,7 @@ static ssize_t device_name_store(struct device *dev, trigger_data->last_activity = 0; set_baseline_state(trigger_data); - spin_unlock_bh(&trigger_data->lock); + mutex_unlock(&trigger_data->lock); return size; } @@ -295,6 +277,7 @@ static int netdev_trig_notify(struct notifier_block *nb, netdev_notifier_info_to_dev((struct netdev_notifier_info *)dv); struct led_netdev_data *trigger_data = container_of(nb, struct led_netdev_data, notifier); + bool reset = true; if (evt != NETDEV_UP && evt != NETDEV_DOWN && evt != NETDEV_CHANGE && evt != NETDEV_REGISTER && evt != NETDEV_UNREGISTER @@ -308,7 +291,7 @@ static int netdev_trig_notify(struct notifier_block *nb, cancel_delayed_work_sync(&trigger_data->work); - spin_lock_bh(&trigger_data->lock); + mutex_lock(&trigger_data->lock); trigger_data->linkup = 0; switch (evt) { @@ -327,12 +310,14 @@ static int netdev_trig_notify(struct notifier_block *nb, case NETDEV_CHANGE: if (netif_carrier_ok(dev)) trigger_data->linkup = 1; + reset = !trigger_data->led_cdev->offloaded; break; } - set_baseline_state(trigger_data); + if (reset) + set_baseline_state(trigger_data); - spin_unlock_bh(&trigger_data->lock); + mutex_unlock(&trigger_data->lock); return NOTIFY_DONE; } @@ -389,7 +374,7 @@ static int netdev_trig_activate(struct led_classdev *led_cdev) if (!trigger_data) return -ENOMEM; - spin_lock_init(&trigger_data->lock); + mutex_init(&trigger_data->lock); trigger_data->notifier.notifier_call = netdev_trig_notify; trigger_data->notifier.priority = 10; @@ -423,12 +408,13 @@ static void netdev_trig_deactivate(struct led_classdev *led_cdev) kfree(trigger_data); } -static struct led_trigger netdev_led_trigger = { +struct led_trigger netdev_led_trigger = { .name = "netdev", .activate = netdev_trig_activate, .deactivate = netdev_trig_deactivate, .groups = netdev_trig_groups, }; +EXPORT_SYMBOL_GPL(netdev_led_trigger); static int __init netdev_trig_init(void) { diff --git a/include/linux/ledtrig.h b/include/linux/ledtrig.h new file mode 100644 index 000000000000..593b2bee6ca0 --- /dev/null +++ b/include/linux/ledtrig.h @@ -0,0 +1,40 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * LED trigger shared structures + */ + +#ifndef __LINUX_LEDTRIG_H__ +#define __LINUX_LEDTRIG_H__ + +#include +#include +#include +#include + +#if IS_ENABLED(CONFIG_LEDS_TRIGGER_NETDEV) + +struct led_netdev_data { + struct mutex lock; + + struct delayed_work work; + struct notifier_block notifier; + + struct led_classdev *led_cdev; + struct net_device *net_dev; + + char device_name[IFNAMSIZ]; + atomic_t interval; + unsigned int last_activity; + + unsigned link:1; + unsigned tx:1; + unsigned rx:1; + + unsigned linkup:1; +}; + +extern struct led_trigger netdev_led_trigger; + +#endif /* IS_ENABLED(CONFIG_LEDS_TRIGGER_NETDEV) */ + +#endif /* __LINUX_LEDTRIG_H__ */ From patchwork Fri Oct 30 11:44:34 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: =?utf-8?q?Marek_Beh=C3=BAn?= X-Patchwork-Id: 315019 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-13.1 required=3.0 tests=BAYES_00,DKIMWL_WL_HIGH, DKIM_SIGNED, DKIM_VALID, DKIM_VALID_AU, INCLUDES_PATCH, MAILING_LIST_MULTI, SIGNED_OFF_BY,SPF_HELO_NONE,SPF_PASS,USER_AGENT_GIT autolearn=ham autolearn_force=no version=3.4.0 Received: from mail.kernel.org (mail.kernel.org [198.145.29.99]) by smtp.lore.kernel.org (Postfix) with ESMTP id 7BA09C388F9 for ; Fri, 30 Oct 2020 11:44:58 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id 28343207DE for ; Fri, 30 Oct 2020 11:44:58 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=default; t=1604058298; bh=plgDzjSsOk9XLxXzM1odiJm4W1YvjhI2cPZI5xc8Rys=; h=From:To:Cc:Subject:Date:In-Reply-To:References:List-ID:From; b=a6b4LwsfK+o1cdQ+eBv03Yd6eknaOzm7mWR4LwhkiZRAojIuIkg4Mi8xXUxNxVYw8 VNsDhWC894JO3l7k/KyvoFu+q7K386OvLb2Dre1mhv7+6v/fDD6m3rdR5CXvamChTf fqwgXOCTPkzbTL+0U+Mq+Sm32adPfTO41zh49GLk= Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1726072AbgJ3Lo5 (ORCPT ); Fri, 30 Oct 2020 07:44:57 -0400 Received: from mail.kernel.org ([198.145.29.99]:37450 "EHLO mail.kernel.org" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1726592AbgJ3Lo5 (ORCPT ); Fri, 30 Oct 2020 07:44:57 -0400 Received: from dellmb.labs.office.nic.cz (nat-1.nic.cz [217.31.205.1]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by mail.kernel.org (Postfix) with ESMTPSA id C96C72083B; Fri, 30 Oct 2020 11:44:53 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=default; t=1604058295; bh=plgDzjSsOk9XLxXzM1odiJm4W1YvjhI2cPZI5xc8Rys=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=lWyNBDzmXRDqgP1t1FXnbqiG5ox05SpyEYGBYksHhsa19e9ZZU57ghwGxsrnvo8YJ 4zf1JWeTBF8pfZ+LYw41mogNSM1nnvACuL/FlJQgO7sky3TtqjGAN6yWrl2FG/ZCNH CInLWwuOBQkkWREpq/5ef8GwPTuJ1sisA7Gy6hek= From: =?utf-8?q?Marek_Beh=C3=BAn?= To: netdev@vger.kernel.org Cc: linux-leds@vger.kernel.org, Pavel Machek , Dan Murphy , Russell King , Andrew Lunn , Matthias Schiffer , "David S. Miller" , Jacek Anaszewski , Ben Whitten , =?utf-8?q?Marek_Beh=C3=BAn?= Subject: [PATCH RFC leds + net-next 6/7] net: phy: add support for LEDs connected to ethernet PHYs Date: Fri, 30 Oct 2020 12:44:34 +0100 Message-Id: <20201030114435.20169-7-kabel@kernel.org> X-Mailer: git-send-email 2.26.2 In-Reply-To: <20201030114435.20169-1-kabel@kernel.org> References: <20201030114435.20169-1-kabel@kernel.org> MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: linux-leds@vger.kernel.org Many an ethernet PHY chip has pins dedicated for LEDs. On some PHYs it can be configured via registers whether the LED should be ON, OFF, or whether its state should depend on events within the chip (link, rx/tx activity and so on). Add support for probing such LEDs. A PHY driver wishing to utilize this API must implement methods led_init() and led_brightness_set(). Methods led_blink_set() and led_trigger_offload() are optional. Signed-off-by: Marek Behún --- drivers/net/phy/phy_device.c | 140 +++++++++++++++++++++++++++++++++++ include/linux/phy.h | 50 +++++++++++++ 2 files changed, 190 insertions(+) diff --git a/drivers/net/phy/phy_device.c b/drivers/net/phy/phy_device.c index 38f581cc9713..6bea8635cac8 100644 --- a/drivers/net/phy/phy_device.c +++ b/drivers/net/phy/phy_device.c @@ -2816,6 +2816,140 @@ s32 phy_get_internal_delay(struct phy_device *phydev, struct device *dev, } EXPORT_SYMBOL(phy_get_internal_delay); +#if IS_ENABLED(CONFIG_LEDS_CLASS) + +static int phy_led_brightness_set(struct led_classdev *cdev, + enum led_brightness brightness) +{ + struct phy_device *phydev = to_phy_device(cdev->dev->parent); + struct phy_led *led = to_phy_led(cdev); + int ret; + + mutex_lock(&phydev->lock); + ret = phydev->drv->led_brightness_set(phydev, led, brightness); + mutex_unlock(&phydev->lock); + + return ret; +} + +static int phy_led_blink_set(struct led_classdev *cdev, unsigned long *delay_on, + unsigned long *delay_off) +{ + struct phy_device *phydev = to_phy_device(cdev->dev->parent); + struct phy_led *led = to_phy_led(cdev); + int ret; + + mutex_lock(&phydev->lock); + ret = phydev->drv->led_blink_set(phydev, led, delay_on, delay_off); + mutex_unlock(&phydev->lock); + + return ret; +} + +#if IS_ENABLED(CONFIG_LEDS_TRIGGERS) +static int phy_led_trigger_offload(struct led_classdev *cdev, bool enable) +{ + struct phy_device *phydev = to_phy_device(cdev->dev->parent); + struct phy_led *led = to_phy_led(cdev); + int ret; + + mutex_lock(&phydev->lock); + ret = phydev->drv->led_trigger_offload(phydev, led, enable); + mutex_unlock(&phydev->lock); + + return ret; +} +#endif /* IS_ENABLED(CONFIG_LEDS_TRIGGERS) */ + +static int phy_probe_led(struct phy_device *phydev, + struct fwnode_handle *fwnode, char *devicename) +{ + struct device *dev = &phydev->mdio.dev; + struct led_init_data init_data = {}; + struct phy_led *led; + u32 reg; + int ret; + + led = devm_kzalloc(dev, sizeof(*led), GFP_KERNEL); + if (!led) + return -ENOMEM; + + led->addr = -1; + if (!fwnode_property_read_u32(fwnode, "reg", ®)) + led->addr = reg; + + led->active_low = !fwnode_property_read_bool(fwnode, + "enable-active-high"); + led->tristate = fwnode_property_read_bool(fwnode, "tristate"); + + led->cdev.max_brightness = 1; + led->cdev.brightness_set_blocking = phy_led_brightness_set; + if (phydev->drv->led_blink_set) + led->cdev.blink_set = phy_led_blink_set; +#if IS_ENABLED(CONFIG_LEDS_TRIGGERS) + if (phydev->drv->led_trigger_offload) + led->cdev.trigger_offload = phy_led_trigger_offload; +#endif /* IS_ENABLED(CONFIG_LEDS_TRIGGERS) */ + + ret = phydev->drv->led_init(phydev, led, fwnode); + if (ret) + goto err; + + init_data.fwnode = fwnode; + init_data.devname_mandatory = true; + init_data.devicename = devicename; + + ret = devm_led_classdev_register_ext(dev, &led->cdev, &init_data); + if (ret) + goto err; + + return 0; +err: + devm_kfree(dev, led); + return ret; +} + +static int phy_probe_leds(struct phy_device *phydev) +{ + struct fwnode_handle *leds, *led; + char devicename[32]; + int ret; + + if (!phydev->drv->led_init) + return 0; + + if (WARN_ON(!phydev->drv->led_brightness_set)) + return 0; + + leds = device_get_named_child_node(&phydev->mdio.dev, "leds"); + if (!leds) + return 0; + + snprintf(devicename, sizeof(devicename), "ethernet-phy%i", + phydev->phyindex); + + fwnode_for_each_available_child_node(leds, led) { + ret = phy_probe_led(phydev, led, devicename); + if (ret) { + phydev_err(phydev, "Error probing PHY LED %pfw: %d\n", + led, ret); + fwnode_handle_put(led); + return ret; + } + } + + return 0; +} + +#else /* !IS_ENABLED(CONFIG_LEDS_CLASS) */ + +static inline int phy_probe_leds(struct phy_device *phydev) +{ + return 0; +} + +#endif /* IS_ENABLED(CONFIG_LEDS_CLASS) */ + static bool phy_drv_supports_irq(struct phy_driver *phydrv) { return phydrv->config_intr && phydrv->ack_interrupt; @@ -2923,6 +3057,12 @@ static int phy_probe(struct device *dev) mutex_unlock(&phydev->lock); + /* LEDs have to be registered with phydev mutex unlocked, because some + * operations can be called during registration that lock the mutex. + */ + if (!err) + err = phy_probe_leds(phydev); + return err; } diff --git a/include/linux/phy.h b/include/linux/phy.h index 6dd4a28135c3..7df3f4632bdc 100644 --- a/include/linux/phy.h +++ b/include/linux/phy.h @@ -14,6 +14,7 @@ #include #include #include +#include #include #include #include @@ -679,6 +680,28 @@ struct phy_tdr_config { }; #define PHY_PAIR_ALL -1 +/** + * struct phy_led - a PHY connected LED instance + * + * @cdev: underlying LED classdev + * @addr: identifier of the LED on the PHY, -1 if not present + * @active_low: whether this LED is connected as active low + * @tristate: whether this LED should be put into tristate mode when off + * @priv: private data for the underlying driver + */ +struct phy_led { + struct led_classdev cdev; + + /* These come from firmware/OF */ + int addr; + unsigned active_low:1; + unsigned tristate:1; + + /* driver private data */ + void *priv; +}; +#define to_phy_led(l) container_of(l, struct phy_led, cdev) + /** * struct phy_driver - Driver structure for a particular PHY type * @@ -884,6 +907,33 @@ struct phy_driver { int (*get_sqi)(struct phy_device *dev); /** @get_sqi_max: Get the maximum signal quality indication */ int (*get_sqi_max)(struct phy_device *dev); + +#if IS_ENABLED(CONFIG_LEDS_CLASS) + /* PHY connected LEDs operations */ + /** @led_init: Check for valid configuration and initialize the LED */ + int (*led_init)(struct phy_device *dev, struct phy_led *led, + struct fwnode_handle *fwnode); + /** + * @led_brightness_set: Set the brightness of the LED. Mandatory if + * led_init is present. Refer to method brightness_set_blocking() from + * struct led_classdev in linux/leds.h + */ + int (*led_brightness_set)(struct phy_device *dev, struct phy_led *led, + enum led_brightness brightness); + /** + * @led_blink:set: Set HW blinking. Refer to method blink_set() from + * struct led_classdev in linux/leds.h + */ + int (*led_blink_set)(struct phy_device *dev, struct phy_led *led, + unsigned long *delay_on, unsigned long *delay_off); + /** + * @led_trigger_offload: If possible, offload LED trigger to HW. + * Refer to method trigger_offload() from struct led_classdev in + * linux/leds.h + */ + int (*led_trigger_offload)(struct phy_device *dev, struct phy_led *led, + bool enable); +#endif /* IS_ENABLED(CONFIG_LEDS_CLASS) */ }; #define to_phy_driver(d) container_of(to_mdio_common_driver(d), \ struct phy_driver, mdiodrv)