From patchwork Wed Sep 9 16:25:46 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: 1360769 X-Patchwork-Delegate: davem@davemloft.net Return-Path: X-Original-To: patchwork-incoming-netdev@ozlabs.org Delivered-To: patchwork-incoming-netdev@ozlabs.org Authentication-Results: ozlabs.org; spf=pass (sender SPF authorized) smtp.mailfrom=vger.kernel.org (client-ip=23.128.96.18; helo=vger.kernel.org; envelope-from=netdev-owner@vger.kernel.org; receiver=) Authentication-Results: ozlabs.org; dmarc=pass (p=none dis=none) header.from=nic.cz Authentication-Results: ozlabs.org; dkim=pass (1024-bit key; secure) header.d=nic.cz header.i=@nic.cz header.a=rsa-sha256 header.s=default header.b=f87YnBrk; dkim-atps=neutral Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by ozlabs.org (Postfix) with ESMTP id 4BmnYL1wrnz9sSP for ; Thu, 10 Sep 2020 02:29:58 +1000 (AEST) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1730969AbgIIQ1d (ORCPT ); Wed, 9 Sep 2020 12:27:33 -0400 Received: from mail.nic.cz ([217.31.204.67]:34622 "EHLO mail.nic.cz" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1731052AbgIIQ0D (ORCPT ); Wed, 9 Sep 2020 12:26:03 -0400 Received: from dellmb.labs.office.nic.cz (unknown [IPv6:2001:1488:fffe:6:cac7:3539:7f1f:463]) by mail.nic.cz (Postfix) with ESMTP id C2F08140A64; Wed, 9 Sep 2020 18:25:54 +0200 (CEST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=nic.cz; s=default; t=1599668754; bh=GiMyA6x4ElsGNt3VmolCaxWgc8p8+8SuvAMrXWbJfso=; h=From:To:Date; b=f87YnBrkbGhc0mIVSyThy0I8tEFadbY7bRevzKURZ9u8FsqdUpA2/lTT/7rnUADVz YpnEVtE4a7UcqDL5SUwko8Q7h8/8pJC0mgawuhLM6VqjkGqnUJPvleJRn4k2ROwRdS kCtCRgAacYXe2/ZszbGkdU37lhlonyDym53MpG/s= From: =?utf-8?q?Marek_Beh=C3=BAn?= To: netdev@vger.kernel.org Cc: linux-leds@vger.kernel.org, Pavel Machek , Dan Murphy , =?utf-8?q?Ond=C5=99ej_Jirman?= , Russell King , Andrew Lunn , linux-kernel@vger.kernel.org, Matthias Schiffer , "David S. Miller" , =?utf-8?q?Marek_Beh=C3=BAn?= , Rob Herring , devicetree@vger.kernel.org Subject: [PATCH net-next + leds v2 1/7] dt-bindings: leds: document binding for HW controlled LEDs Date: Wed, 9 Sep 2020 18:25:46 +0200 Message-Id: <20200909162552.11032-2-marek.behun@nic.cz> X-Mailer: git-send-email 2.26.2 In-Reply-To: <20200909162552.11032-1-marek.behun@nic.cz> References: <20200909162552.11032-1-marek.behun@nic.cz> MIME-Version: 1.0 X-Spam-Checker-Version: SpamAssassin 3.4.2 (2018-09-13) on mail.nic.cz X-Spam-Status: No, score=0.00 X-Spamd-Bar: / X-Virus-Scanned: clamav-milter 0.102.2 at mail X-Virus-Status: Clean Sender: netdev-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: netdev@vger.kernel.org Document binding for LEDs connected to and controlled by various chips (such as ethernet PHY chips). Signed-off-by: Marek Behún Cc: Rob Herring Cc: devicetree@vger.kernel.org --- .../leds/linux,hw-controlled-leds.yaml | 99 +++++++++++++++++++ 1 file changed, 99 insertions(+) create mode 100644 Documentation/devicetree/bindings/leds/linux,hw-controlled-leds.yaml diff --git a/Documentation/devicetree/bindings/leds/linux,hw-controlled-leds.yaml b/Documentation/devicetree/bindings/leds/linux,hw-controlled-leds.yaml new file mode 100644 index 0000000000000..eaf6e5d80c5f5 --- /dev/null +++ b/Documentation/devicetree/bindings/leds/linux,hw-controlled-leds.yaml @@ -0,0 +1,99 @@ +# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/leds/linux,hw-controlled-leds.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: LEDs that can be controlled by hardware (eg. by an ethernet PHY chip) + +maintainers: + - Marek Behún + +description: + Many an ethernet PHY (and other chips) supports various HW control modes + for LEDs connected directly to them. With this binding such LEDs can be + described. + +properties: + compatible: + const: linux,hw-controlled-leds + + "#address-cells": + const: 1 + + "#size-cells": + const: 0 + +patternProperties: + "^led@[0-9a-f]+$": + type: object + allOf: + - $ref: common.yaml# + description: + This node represents a LED device connected to a chip that can control + the LED in various HW controlled modes. + + properties: + reg: + maxItems: 1 + description: + This property identifies the LED to the chip the LED is connected to + (eg. an ethernet PHY chip can have multiple LEDs connected to it). + + enable-active-high: + description: + Polarity of LED is active high. If missing, assumed default is active + low. + type: boolean + + led-tristate: + description: + LED pin is tristate type. If missing, assumed false. + type: boolean + + linux,default-hw-mode: + description: + This parameter, if present, specifies the default HW triggering mode + of the LED when LED trigger is set to `dev-hw-mode`. + Available values are specific per device the LED is connected to and + per LED itself. + $ref: /schemas/types.yaml#definitions/string + + required: + - reg + +additionalProperties: false + +examples: + - | + + #include + + ethernet-phy@0 { + compatible = "ethernet-phy-ieee802.3-c45"; + reg = <0>; + + leds { + compatible = "linux,hw-controlled-leds"; + #address-cells = <1>; + #size-cells = <0>; + + led@0 { + reg = <0>; + color = ; + function = ; + linux,default-trigger = "dev-hw-mode"; + linux,default-hw-mode = "1Gbps"; + }; + + led@1 { + reg = <1>; + color = ; + function = ; + linux,default-trigger = "dev-hw-mode"; + linux,default-hw-mode = "activity"; + }; + }; + }; + +... From patchwork Wed Sep 9 16:25:47 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: 1360782 X-Patchwork-Delegate: davem@davemloft.net Return-Path: X-Original-To: patchwork-incoming-netdev@ozlabs.org Delivered-To: patchwork-incoming-netdev@ozlabs.org Authentication-Results: ozlabs.org; spf=pass (sender SPF authorized) smtp.mailfrom=vger.kernel.org (client-ip=23.128.96.18; helo=vger.kernel.org; envelope-from=netdev-owner@vger.kernel.org; receiver=) Authentication-Results: ozlabs.org; dmarc=pass (p=none dis=none) header.from=nic.cz Authentication-Results: ozlabs.org; dkim=pass (1024-bit key; secure) header.d=nic.cz header.i=@nic.cz header.a=rsa-sha256 header.s=default header.b=ML2TxvAb; dkim-atps=neutral Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by ozlabs.org (Postfix) with ESMTP id 4BmnjR1cscz9sT6 for ; Thu, 10 Sep 2020 02:36:59 +1000 (AEST) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1731225AbgIIQgu (ORCPT ); Wed, 9 Sep 2020 12:36:50 -0400 Received: from lists.nic.cz ([217.31.204.67]:34640 "EHLO mail.nic.cz" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1731056AbgIIQ0D (ORCPT ); Wed, 9 Sep 2020 12:26:03 -0400 Received: from dellmb.labs.office.nic.cz (unknown [IPv6:2001:1488:fffe:6:cac7:3539:7f1f:463]) by mail.nic.cz (Postfix) with ESMTP id EF442140A6D; Wed, 9 Sep 2020 18:25:54 +0200 (CEST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=nic.cz; s=default; t=1599668755; bh=u3IRK7xXgZ5VpwhMjFOls8Tmo4FXyAhmL45+VBDAzGQ=; h=From:To:Date; b=ML2TxvAbqcLwYDA0iPUeyrPLt7/bWP2m5GIAVAYoln6nWfuooRfcZy2RVrmzjS6cj qmzIEH+URpE8jFAlG9leOJRAMFPvg28Zqlud4BWYV1XBLgjEYyoONyIk3RU51Iefx6 Dn4QklVmNXP4LmWWd2ckC6Jx9pm5NoQENhkuFwoo= From: =?utf-8?q?Marek_Beh=C3=BAn?= To: netdev@vger.kernel.org Cc: linux-leds@vger.kernel.org, Pavel Machek , Dan Murphy , =?utf-8?q?Ond=C5=99ej_Jirman?= , Russell King , Andrew Lunn , linux-kernel@vger.kernel.org, Matthias Schiffer , "David S. Miller" , =?utf-8?q?Marek_Beh=C3=BAn?= Subject: [PATCH net-next + leds v2 2/7] leds: add generic API for LEDs that can be controlled by hardware Date: Wed, 9 Sep 2020 18:25:47 +0200 Message-Id: <20200909162552.11032-3-marek.behun@nic.cz> X-Mailer: git-send-email 2.26.2 In-Reply-To: <20200909162552.11032-1-marek.behun@nic.cz> References: <20200909162552.11032-1-marek.behun@nic.cz> MIME-Version: 1.0 X-Spam-Checker-Version: SpamAssassin 3.4.2 (2018-09-13) on mail.nic.cz X-Spam-Status: No, score=0.00 X-Spamd-Bar: / X-Virus-Scanned: clamav-milter 0.102.2 at mail X-Virus-Status: Clean Sender: netdev-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: netdev@vger.kernel.org Many an ethernet PHY (and other chips) supports various HW control modes for LEDs connected directly to them. This patch adds a generic API for registering such LEDs when described in device tree. This API also exposes generic way to select between these hardware control modes. This API registers a new private LED trigger called dev-hw-mode. When this trigger is enabled for a LED, the various HW control modes which are supported by the device for given LED can be get/set via hw_mode sysfs file. Signed-off-by: Marek Behún --- .../sysfs-class-led-trigger-dev-hw-mode | 8 + drivers/leds/Kconfig | 10 + drivers/leds/Makefile | 1 + drivers/leds/leds-hw-controlled.c | 227 ++++++++++++++++++ include/linux/leds-hw-controlled.h | 74 ++++++ 5 files changed, 320 insertions(+) create mode 100644 Documentation/ABI/testing/sysfs-class-led-trigger-dev-hw-mode create mode 100644 drivers/leds/leds-hw-controlled.c create mode 100644 include/linux/leds-hw-controlled.h diff --git a/Documentation/ABI/testing/sysfs-class-led-trigger-dev-hw-mode b/Documentation/ABI/testing/sysfs-class-led-trigger-dev-hw-mode new file mode 100644 index 0000000000000..7bca112e7ff93 --- /dev/null +++ b/Documentation/ABI/testing/sysfs-class-led-trigger-dev-hw-mode @@ -0,0 +1,8 @@ +What: /sys/class/leds//hw_mode +Date: September 2020 +KernelVersion: 5.10 +Contact: Marek Behún + linux-leds@vger.kernel.org +Description: (W) Set the HW control mode of this LED. The various available HW control modes + are specific per device to which the LED is connected to and per LED itself. + (R) Show the available HW control modes and the currently selected one. diff --git a/drivers/leds/Kconfig b/drivers/leds/Kconfig index 1c181df24eae4..5e47ab21aafb4 100644 --- a/drivers/leds/Kconfig +++ b/drivers/leds/Kconfig @@ -49,6 +49,16 @@ config LEDS_BRIGHTNESS_HW_CHANGED See Documentation/ABI/testing/sysfs-class-led for details. +config LEDS_HW_CONTROLLED + bool "API for LEDs that can be controlled by hardware" + depends on LEDS_CLASS + select LEDS_TRIGGERS + help + This option enables support for a generic API via which other drivers + can register LEDs that can be put into hardware controlled mode, eg. + a LED connected to an ethernet PHY can be configured to blink on + network activity. + comment "LED drivers" config LEDS_88PM860X diff --git a/drivers/leds/Makefile b/drivers/leds/Makefile index c2c7d7ade0d06..858e468e40df0 100644 --- a/drivers/leds/Makefile +++ b/drivers/leds/Makefile @@ -6,6 +6,7 @@ obj-$(CONFIG_LEDS_CLASS) += led-class.o obj-$(CONFIG_LEDS_CLASS_FLASH) += led-class-flash.o obj-$(CONFIG_LEDS_CLASS_MULTICOLOR) += led-class-multicolor.o obj-$(CONFIG_LEDS_TRIGGERS) += led-triggers.o +obj-$(CONFIG_LEDS_HW_CONTROLLED) += leds-hw-controlled.o # LED Platform Drivers (keep this sorted, M-| sort) obj-$(CONFIG_LEDS_88PM860X) += leds-88pm860x.o diff --git a/drivers/leds/leds-hw-controlled.c b/drivers/leds/leds-hw-controlled.c new file mode 100644 index 0000000000000..9ef58bf275efd --- /dev/null +++ b/drivers/leds/leds-hw-controlled.c @@ -0,0 +1,227 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Generic API for LEDs that can be controlled by hardware (eg. by an ethernet PHY chip) + * + * Copyright (C) 2020 Marek Behun + */ +#include +#include +#include + +int hw_controlled_led_brightness_set(struct led_classdev *cdev, enum led_brightness brightness) +{ + struct hw_controlled_led *led = led_cdev_to_hw_controlled_led(cdev); + int ret; + + mutex_lock(&led->lock); + ret = led->ops->led_brightness_set(cdev->dev->parent, led, brightness); + mutex_unlock(&led->lock); + + return ret; +} +EXPORT_SYMBOL_GPL(hw_controlled_led_brightness_set); + +static int of_register_hw_controlled_led(struct device *dev, struct device_node *np, + const char *devicename, + const struct hw_controlled_led_ops *ops) +{ + struct led_init_data init_data = {}; + struct hw_controlled_led *led; + u32 reg; + int ret; + + ret = of_property_read_u32(np, "reg", ®); + if (ret < 0) + return ret; + + led = devm_kzalloc(dev, sizeof(struct hw_controlled_led), GFP_KERNEL); + if (!led) + return -ENOMEM; + + led->ops = ops; + + led->cdev.max_brightness = 1; + led->cdev.brightness_set_blocking = hw_controlled_led_brightness_set; + led->cdev.trigger_type = &hw_control_led_trig_type; + led->addr = reg; + + of_property_read_string(np, "linux,default-trigger", &led->cdev.default_trigger); + of_property_read_string(np, "linux,default-hw-mode", &led->hw_mode); + + led->active_low = !of_property_read_bool(np, "enable-active-high"); + led->tristate = of_property_read_bool(np, "led-tristate"); + + init_data.fwnode = &np->fwnode; + init_data.devname_mandatory = true; + init_data.devicename = devicename; + + ret = led->ops->led_init(dev, led); + if (ret < 0) + goto err_free; + + ret = devm_led_classdev_register_ext(dev, &led->cdev, &init_data); + if (ret < 0) + goto err_free; + + return 0; +err_free: + devm_kfree(dev, led); + return ret; +} + +int of_register_hw_controlled_leds(struct device *dev, const char *devicename, + const struct hw_controlled_led_ops *ops) +{ + struct device_node *node = dev->of_node; + struct device_node *leds, *led; + int ret; + + if (!IS_ENABLED(CONFIG_OF_MDIO)) + return 0; + + if (!ops) + return -EINVAL; + + /* maybe we should have of_get_compatible_available_child as well */ + leds = of_get_compatible_child(node, "linux,hw-controlled-leds"); + if (!leds) + return 0; + + if (!devicename) + devicename = dev_name(dev); + + for_each_available_child_of_node(leds, led) { + ret = of_register_hw_controlled_led(dev, led, devicename, ops); + if (ret < 0) + dev_err(dev, "Nonfatal error: cannot register LED from node %pOFn: %i\n", + led, ret); + } + + return 0; +} +EXPORT_SYMBOL_GPL(of_register_hw_controlled_leds); + +static int hw_control_led_trig_activate(struct led_classdev *cdev) +{ + struct hw_controlled_led *led; + int ret; + + led = led_cdev_to_hw_controlled_led(cdev); + + if (!led->hw_mode) + return 0; + + mutex_lock(&led->lock); + ret = led->ops->led_set_hw_mode(cdev->dev->parent, led, led->hw_mode); + mutex_unlock(&led->lock); + + if (ret < 0) + dev_warn(cdev->dev->parent, "Could not set HW mode %s on LED %s: %i\n", + led->hw_mode, cdev->name, ret); + + /* don't fail to activate this trigger so that user can write hw_mode file */ + return 0; +} + +static void hw_control_led_trig_deactivate(struct led_classdev *cdev) +{ + struct hw_controlled_led *led; + int ret; + + led = led_cdev_to_hw_controlled_led(cdev); + + mutex_lock(&led->lock); + /* store HW mode before deactivation */ + led->hw_mode = led->ops->led_get_hw_mode(cdev->dev->parent, led); + ret = led->ops->led_set_hw_mode(cdev->dev->parent, led, NULL); + mutex_unlock(&led->lock); + + if (ret < 0) + dev_err(cdev->dev->parent, "Failed deactivating HW mode on LED %s\n", cdev->name); +} + +static ssize_t hw_mode_show(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct hw_controlled_led *led; + const char *mode, *cur_mode; + void *iter = NULL; + int len = 0; + + led = led_cdev_to_hw_controlled_led(led_trigger_get_led(dev)); + + mutex_lock(&led->lock); + + cur_mode = led->ops->led_get_hw_mode(dev->parent, led); + + for (mode = led->ops->led_iter_hw_mode(dev->parent, led, &iter); + mode; + mode = led->ops->led_iter_hw_mode(dev->parent, led, &iter)) { + bool sel; + + sel = cur_mode && !strcmp(mode, cur_mode); + + len += scnprintf(buf + len, PAGE_SIZE - len, "%s%s%s ", sel ? "[" : "", mode, + sel ? "]" : ""); + } + + if (buf[len - 1] == ' ') + buf[len - 1] = '\n'; + + mutex_unlock(&led->lock); + + return len; +} + +static ssize_t hw_mode_store(struct device *dev, struct device_attribute *attr, const char *buf, + size_t count) +{ + struct hw_controlled_led *led; + int ret; + + led = led_cdev_to_hw_controlled_led(led_trigger_get_led(dev)); + + mutex_lock(&led->lock); + ret = led->ops->led_set_hw_mode(dev->parent, led, buf); + if (ret < 0) + return ret; + mutex_unlock(&led->lock); + + return count; +} + +static DEVICE_ATTR_RW(hw_mode); + +static struct attribute *hw_control_led_trig_attrs[] = { + &dev_attr_hw_mode.attr, + NULL +}; +ATTRIBUTE_GROUPS(hw_control_led_trig); + +struct led_hw_trigger_type hw_control_led_trig_type; +EXPORT_SYMBOL_GPL(hw_control_led_trig_type); + +struct led_trigger hw_control_led_trig = { + .name = "dev-hw-mode", + .activate = hw_control_led_trig_activate, + .deactivate = hw_control_led_trig_deactivate, + .trigger_type = &hw_control_led_trig_type, + .groups = hw_control_led_trig_groups, +}; +EXPORT_SYMBOL_GPL(hw_control_led_trig); + +static int __init hw_controlled_leds_init(void) +{ + return led_trigger_register(&hw_control_led_trig); +} + +static void __exit hw_controlled_leds_exit(void) +{ + led_trigger_unregister(&hw_control_led_trig); +} + +subsys_initcall(hw_controlled_leds_init); +module_exit(hw_controlled_leds_exit); + +MODULE_AUTHOR("Marek Behun "); +MODULE_LICENSE("GPL v2"); +MODULE_DESCRIPTION("API for HW controlled LEDs"); diff --git a/include/linux/leds-hw-controlled.h b/include/linux/leds-hw-controlled.h new file mode 100644 index 0000000000000..2c9b8a06def18 --- /dev/null +++ b/include/linux/leds-hw-controlled.h @@ -0,0 +1,74 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * Generic API for LEDs that can be controlled by hardware (eg. by an ethernet PHY chip) + * + * Copyright (C) 2020 Marek Behun + */ +#ifndef _LINUX_LEDS_HW_CONTROLLED_H_ +#define _LINUX_LEDS_HW_CONTROLLED_H_ + +#include +#include + +struct hw_controlled_led { + struct led_classdev cdev; + const struct hw_controlled_led_ops *ops; + struct mutex lock; + + /* these members are filled in by OF if OF is enabled */ + int addr; + bool active_low; + bool tristate; + + /* also filled in by OF, but changed by led_set_hw_mode operation */ + const char *hw_mode; + + void *priv; +}; +#define led_cdev_to_hw_controlled_led(l) container_of(l, struct hw_controlled_led, cdev) + +/* struct hw_controlled_led_ops: Operations on LEDs that can be controlled by HW + * + * All the following operations must be implemented: + * @led_init: Should initialize the LED from OF data (and sanity check whether they are correct). + * This should also change led->cdev.max_brightness, if the value differs from default, + * which is 1. + * @led_brightness_set: Sets brightness. + * @led_iter_hw_mode: Iterates available HW control mode names for this LED. + * @led_set_hw_mode: Sets HW control mode to value specified by given name. + * @led_get_hw_mode: Returns current HW control mode name. + */ +struct hw_controlled_led_ops { + int (*led_init)(struct device *dev, struct hw_controlled_led *led); + int (*led_brightness_set)(struct device *dev, struct hw_controlled_led *led, + enum led_brightness brightness); + const char *(*led_iter_hw_mode)(struct device *dev, struct hw_controlled_led *led, + void **iter); + int (*led_set_hw_mode)(struct device *dev, struct hw_controlled_led *led, + const char *mode); + const char *(*led_get_hw_mode)(struct device *dev, struct hw_controlled_led *led); +}; + +#if IS_ENABLED(CONFIG_LEDS_HW_CONTROLLED) + +#define hw_controlled_led_ops_ptr(s) (s) + +int of_register_hw_controlled_leds(struct device *dev, const char *devicename, + const struct hw_controlled_led_ops *ops); +int hw_controlled_led_brightness_set(struct led_classdev *cdev, enum led_brightness brightness); + +extern struct led_hw_trigger_type hw_control_led_trig_type; +extern struct led_trigger hw_control_led_trig; + +#else /* !IS_ENABLED(CONFIG_LEDS_HW_CONTROLLED) */ + +#define hw_controlled_led_ops_ptr(s) NULL +static inline int of_register_hw_controlled_leds(struct device *dev, const char *devicename, + const struct hw_controlled_led_ops *ops) +{ + return 0; +} + +#endif /* !IS_ENABLED(CONFIG_LEDS_HW_CONTROLLED) */ + +#endif /* _LINUX_LEDS_HW_CONTROLLED_H_ */ From patchwork Wed Sep 9 16:25:48 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: 1360783 X-Patchwork-Delegate: davem@davemloft.net Return-Path: X-Original-To: patchwork-incoming-netdev@ozlabs.org Delivered-To: patchwork-incoming-netdev@ozlabs.org Authentication-Results: ozlabs.org; spf=pass (sender SPF authorized) smtp.mailfrom=vger.kernel.org (client-ip=23.128.96.18; helo=vger.kernel.org; envelope-from=netdev-owner@vger.kernel.org; receiver=) Authentication-Results: ozlabs.org; dmarc=pass (p=none dis=none) header.from=nic.cz Authentication-Results: ozlabs.org; dkim=pass (1024-bit key; secure) header.d=nic.cz header.i=@nic.cz header.a=rsa-sha256 header.s=default header.b=Y3DvO2vk; dkim-atps=neutral Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by ozlabs.org (Postfix) with ESMTP id 4BmnjW5hFnz9sT6 for ; Thu, 10 Sep 2020 02:37:03 +1000 (AEST) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1730964AbgIIQgp (ORCPT ); Wed, 9 Sep 2020 12:36:45 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:56128 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1730550AbgIIQ0D (ORCPT ); Wed, 9 Sep 2020 12:26:03 -0400 Received: from mail.nic.cz (mail.nic.cz [IPv6:2001:1488:800:400::400]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 0CAD3C061756; Wed, 9 Sep 2020 09:25:59 -0700 (PDT) Received: from dellmb.labs.office.nic.cz (unknown [IPv6:2001:1488:fffe:6:cac7:3539:7f1f:463]) by mail.nic.cz (Postfix) with ESMTP id 29126140A72; Wed, 9 Sep 2020 18:25:55 +0200 (CEST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=nic.cz; s=default; t=1599668755; bh=FR7+inwZvbFVAUyvtRKpQapSQN3oEXOfxjaahzSXaLs=; h=From:To:Date; b=Y3DvO2vkbr4CDz6hfyGSc2Tt7tvL65TSQAvTKBJaLK65CPVJLSceKrKy4iXRxjEYl 7vahHqtEdfyLlFVWHvqaRX68H2UiEmMDbbeIXV0pc6tyf2UR4LtZtA0Vrv2Bump1H4 GG4U/VQU1x1Khvj3+YShPfDfo772ZezJ9xqRxQO8= From: =?utf-8?q?Marek_Beh=C3=BAn?= To: netdev@vger.kernel.org Cc: linux-leds@vger.kernel.org, Pavel Machek , Dan Murphy , =?utf-8?q?Ond=C5=99ej_Jirman?= , Russell King , Andrew Lunn , linux-kernel@vger.kernel.org, Matthias Schiffer , "David S. Miller" , =?utf-8?q?Marek_Beh=C3=BAn?= Subject: [PATCH net-next + leds v2 3/7] net: phy: add simple incrementing phyindex member to phy_device struct Date: Wed, 9 Sep 2020 18:25:48 +0200 Message-Id: <20200909162552.11032-4-marek.behun@nic.cz> X-Mailer: git-send-email 2.26.2 In-Reply-To: <20200909162552.11032-1-marek.behun@nic.cz> References: <20200909162552.11032-1-marek.behun@nic.cz> MIME-Version: 1.0 X-Spam-Checker-Version: SpamAssassin 3.4.2 (2018-09-13) on mail.nic.cz X-Spam-Status: No, score=0.00 X-Spamd-Bar: / X-Virus-Scanned: clamav-milter 0.102.2 at mail X-Virus-Status: Clean Sender: netdev-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: netdev@vger.kernel.org Add a new integer member phyindex to struct phy_device. This member is unique for every phy_device. Atomic incrementation occurs in phy_device_register. This can be used for example in LED sysfs API. The LED subsystem names each LED in format `device:color:function`, but currently the PHY device names are not suited for this, since in some situations a PHY device name can look like this d0032004.mdio-mii:01 or even like this /soc/internal-regs@d0000000/mdio@32004/switch0@10/mdio:08 Clearly this cannot be used as the `device` part of a LED name. Signed-off-by: Marek Behún --- drivers/net/phy/phy_device.c | 3 +++ include/linux/phy.h | 3 +++ 2 files changed, 6 insertions(+) diff --git a/drivers/net/phy/phy_device.c b/drivers/net/phy/phy_device.c index 8adfbad0a1e8f..38f56d39f1229 100644 --- a/drivers/net/phy/phy_device.c +++ b/drivers/net/phy/phy_device.c @@ -9,6 +9,7 @@ #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt +#include #include #include #include @@ -892,6 +893,7 @@ EXPORT_SYMBOL(get_phy_device); */ int phy_device_register(struct phy_device *phydev) { + static atomic_t phyindex; int err; err = mdiobus_register_device(&phydev->mdio); @@ -908,6 +910,7 @@ int phy_device_register(struct phy_device *phydev) goto out; } + phydev->phyindex = atomic_inc_return(&phyindex) - 1; err = device_add(&phydev->mdio.dev); if (err) { phydev_err(phydev, "failed to add\n"); diff --git a/include/linux/phy.h b/include/linux/phy.h index 3a09d2bf69ea4..52881e21ad951 100644 --- a/include/linux/phy.h +++ b/include/linux/phy.h @@ -406,6 +406,7 @@ struct macsec_ops; /* phy_device: An instance of a PHY * * drv: Pointer to the driver for this PHY instance + * phyindex: a simple incrementing PHY index * phy_id: UID for this device found during discovery * c45_ids: 802.3-c45 Device Identifers if is_c45. * is_c45: Set to true if this phy uses clause 45 addressing. @@ -446,6 +447,8 @@ struct phy_device { /* And management functions */ struct phy_driver *drv; + int phyindex; + u32 phy_id; struct phy_c45_device_ids c45_ids; From patchwork Wed Sep 9 16:25:49 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: 1360787 X-Patchwork-Delegate: davem@davemloft.net Return-Path: X-Original-To: patchwork-incoming-netdev@ozlabs.org Delivered-To: patchwork-incoming-netdev@ozlabs.org Authentication-Results: ozlabs.org; spf=pass (sender SPF authorized) smtp.mailfrom=vger.kernel.org (client-ip=23.128.96.18; helo=vger.kernel.org; envelope-from=netdev-owner@vger.kernel.org; receiver=) Authentication-Results: ozlabs.org; dmarc=pass (p=none dis=none) header.from=nic.cz Authentication-Results: ozlabs.org; dkim=pass (1024-bit key; secure) header.d=nic.cz header.i=@nic.cz header.a=rsa-sha256 header.s=default header.b=esEDFi4f; dkim-atps=neutral Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by ozlabs.org (Postfix) with ESMTP id 4BmnkJ2Tbmz9sSP for ; Thu, 10 Sep 2020 02:37:44 +1000 (AEST) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1731175AbgIIQhe (ORCPT ); Wed, 9 Sep 2020 12:37:34 -0400 Received: from mail.nic.cz ([217.31.204.67]:34678 "EHLO mail.nic.cz" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1731057AbgIIQ0A (ORCPT ); Wed, 9 Sep 2020 12:26:00 -0400 Received: from dellmb.labs.office.nic.cz (unknown [IPv6:2001:1488:fffe:6:cac7:3539:7f1f:463]) by mail.nic.cz (Postfix) with ESMTP id 503A6140A73; Wed, 9 Sep 2020 18:25:55 +0200 (CEST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=nic.cz; s=default; t=1599668755; bh=MvXQD34WvMImgeFSgU4h5cxDM9wpALrPc1U2c97TNVY=; h=From:To:Date; b=esEDFi4f3TDJKUxF7mN6MzWKDJGWacG1D+Spd1xIHmg6lLdcjm+uuP3eEEmy+ElKF GjuPZAc1ZUJBazQibj2HS2xM7O0h2Hc2UKz3xfHunBg1krjXxGRpiNIx3Eww8D1xmI i/4QBWcehwJMgM4deFm8xFympqFdLIWqITJajpas= From: =?utf-8?q?Marek_Beh=C3=BAn?= To: netdev@vger.kernel.org Cc: linux-leds@vger.kernel.org, Pavel Machek , Dan Murphy , =?utf-8?q?Ond=C5=99ej_Jirman?= , Russell King , Andrew Lunn , linux-kernel@vger.kernel.org, Matthias Schiffer , "David S. Miller" , =?utf-8?q?Marek_Beh=C3=BAn?= , Rob Herring , devicetree@vger.kernel.org Subject: [PATCH net-next + leds v2 4/7] dt-bindings: net: ethernet-phy: add description for PHY LEDs Date: Wed, 9 Sep 2020 18:25:49 +0200 Message-Id: <20200909162552.11032-5-marek.behun@nic.cz> X-Mailer: git-send-email 2.26.2 In-Reply-To: <20200909162552.11032-1-marek.behun@nic.cz> References: <20200909162552.11032-1-marek.behun@nic.cz> MIME-Version: 1.0 X-Spam-Checker-Version: SpamAssassin 3.4.2 (2018-09-13) on mail.nic.cz X-Spam-Status: No, score=0.00 X-Spamd-Bar: / X-Virus-Scanned: clamav-milter 0.102.2 at mail X-Virus-Status: Clean Sender: netdev-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: netdev@vger.kernel.org Document binding for LEDs connected to an ethernet PHY chip. Signed-off-by: Marek Behún Cc: Rob Herring Cc: devicetree@vger.kernel.org --- Documentation/devicetree/bindings/net/ethernet-phy.yaml | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/Documentation/devicetree/bindings/net/ethernet-phy.yaml b/Documentation/devicetree/bindings/net/ethernet-phy.yaml index a9e547ac79051..f593e8709dd0d 100644 --- a/Documentation/devicetree/bindings/net/ethernet-phy.yaml +++ b/Documentation/devicetree/bindings/net/ethernet-phy.yaml @@ -174,6 +174,14 @@ properties: PHY's that have configurable TX internal delays. If this property is present then the PHY applies the TX delay. + leds: + type: object + description: | + This is used to described LEDs that are connected to the PHY chip and + their blinking can be controlled by the PHY. + allOf: + - $ref: /schemas/leds/linux,hw-controlled-leds.yaml# + required: - reg From patchwork Wed Sep 9 16:25:50 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: 1360756 X-Patchwork-Delegate: davem@davemloft.net Return-Path: X-Original-To: patchwork-incoming-netdev@ozlabs.org Delivered-To: patchwork-incoming-netdev@ozlabs.org Authentication-Results: ozlabs.org; spf=pass (sender SPF authorized) smtp.mailfrom=vger.kernel.org (client-ip=23.128.96.18; helo=vger.kernel.org; envelope-from=netdev-owner@vger.kernel.org; receiver=) Authentication-Results: ozlabs.org; dmarc=pass (p=none dis=none) header.from=nic.cz Authentication-Results: ozlabs.org; dkim=pass (1024-bit key; secure) header.d=nic.cz header.i=@nic.cz header.a=rsa-sha256 header.s=default header.b=FFePVVaj; dkim-atps=neutral Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by ozlabs.org (Postfix) with ESMTP id 4BmnW939rBz9sT6 for ; Thu, 10 Sep 2020 02:28:05 +1000 (AEST) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1730712AbgIIQ17 (ORCPT ); Wed, 9 Sep 2020 12:27:59 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:56140 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1731071AbgIIQ0E (ORCPT ); Wed, 9 Sep 2020 12:26:04 -0400 Received: from mail.nic.cz (lists.nic.cz [IPv6:2001:1488:800:400::400]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 3966DC0613ED; Wed, 9 Sep 2020 09:26:03 -0700 (PDT) Received: from dellmb.labs.office.nic.cz (unknown [IPv6:2001:1488:fffe:6:cac7:3539:7f1f:463]) by mail.nic.cz (Postfix) with ESMTP id 790C0140A76; Wed, 9 Sep 2020 18:25:55 +0200 (CEST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=nic.cz; s=default; t=1599668755; bh=ZrVlwYH21zVLENLWv1RHoCy4rTc2/MBngwHI5nVk+sk=; h=From:To:Date; b=FFePVVajsmNyWURu2rdDUSTaREBB6CsBiagszeJWJJvVROiUuJAsaAutbMfKIWmne miPgZK4F4TJZZ5btbfM5Yr7dznZvo9mCmBllbTd2fJXm1BsP4kW8cLJGIWILtokaIE T+bKddxLiBE5IfZIVViPov4YFIOz9cEKJWkZ1dpU= From: =?utf-8?q?Marek_Beh=C3=BAn?= To: netdev@vger.kernel.org Cc: linux-leds@vger.kernel.org, Pavel Machek , Dan Murphy , =?utf-8?q?Ond=C5=99ej_Jirman?= , Russell King , Andrew Lunn , linux-kernel@vger.kernel.org, Matthias Schiffer , "David S. Miller" , =?utf-8?q?Marek_Beh=C3=BAn?= Subject: [PATCH net-next + leds v2 5/7] net: phy: add support for LEDs controlled by ethernet PHY chips Date: Wed, 9 Sep 2020 18:25:50 +0200 Message-Id: <20200909162552.11032-6-marek.behun@nic.cz> X-Mailer: git-send-email 2.26.2 In-Reply-To: <20200909162552.11032-1-marek.behun@nic.cz> References: <20200909162552.11032-1-marek.behun@nic.cz> MIME-Version: 1.0 X-Spam-Checker-Version: SpamAssassin 3.4.2 (2018-09-13) on mail.nic.cz X-Spam-Status: No, score=0.00 X-Spamd-Bar: / X-Virus-Scanned: clamav-milter 0.102.2 at mail X-Virus-Status: Clean Sender: netdev-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: netdev@vger.kernel.org This patch uses the new API for HW controlled LEDs to add support for probing and control of LEDs connected to an ethernet PHY chip. A PHY driver wishing to utilize this API needs to implement the methods in struct hw_controlled_led_ops and set the member led_ops in struct phy_driver to point to that structure. Signed-off-by: Marek Behún Acked-by: Pavel Machek --- drivers/net/phy/phy_device.c | 103 +++++++++++++++++++++++++++++++++++ include/linux/phy.h | 4 ++ 2 files changed, 107 insertions(+) diff --git a/drivers/net/phy/phy_device.c b/drivers/net/phy/phy_device.c index 38f56d39f1229..54d5c88e4d4b2 100644 --- a/drivers/net/phy/phy_device.c +++ b/drivers/net/phy/phy_device.c @@ -2820,6 +2820,103 @@ static bool phy_drv_supports_irq(struct phy_driver *phydrv) return phydrv->config_intr && phydrv->ack_interrupt; } +#if IS_ENABLED(CONFIG_LEDS_HW_CONTROLLED) + +/* PHY mutex lock wrappers for operations on PHY HW controlled LEDs, so that PHY drivers + * implementing these operations don't have to lock phydev->lock themselves. + */ +static int phy_led_init(struct device *dev, struct hw_controlled_led *led) +{ + struct phy_device *phydev = to_phy_device(dev); + int ret; + + mutex_lock(&phydev->lock); + ret = phydev->drv->led_ops->led_init(dev, led); + mutex_unlock(&phydev->lock); + + return ret; +} + +static int phy_led_brightness_set(struct device *dev, struct hw_controlled_led *led, + enum led_brightness brightness) +{ + struct phy_device *phydev = to_phy_device(dev); + int ret; + + mutex_lock(&phydev->lock); + ret = phydev->drv->led_ops->led_brightness_set(dev, led, brightness); + mutex_unlock(&phydev->lock); + + return ret; +} + +static const char *phy_led_iter_hw_mode(struct device *dev, struct hw_controlled_led *led, + void **iter) +{ + struct phy_device *phydev = to_phy_device(dev); + const char *ret; + + mutex_lock(&phydev->lock); + ret = phydev->drv->led_ops->led_iter_hw_mode(dev, led, iter); + mutex_unlock(&phydev->lock); + + return ret; +} + +static int phy_led_set_hw_mode(struct device *dev, struct hw_controlled_led *led, const char *mode) +{ + struct phy_device *phydev = to_phy_device(dev); + int ret; + + mutex_lock(&phydev->lock); + ret = phydev->drv->led_ops->led_set_hw_mode(dev, led, mode); + mutex_unlock(&phydev->lock); + + return ret; +} + +static const char *phy_led_get_hw_mode(struct device *dev, struct hw_controlled_led *led) +{ + struct phy_device *phydev = to_phy_device(dev); + const char *ret; + + mutex_lock(&phydev->lock); + ret = phydev->drv->led_ops->led_get_hw_mode(dev, led); + mutex_unlock(&phydev->lock); + + return ret; +} + +static const struct hw_controlled_led_ops phy_hw_controlled_led_ops = { + .led_init = phy_led_init, + .led_brightness_set = phy_led_brightness_set, + .led_iter_hw_mode = phy_led_iter_hw_mode, + .led_set_hw_mode = phy_led_set_hw_mode, + .led_get_hw_mode = phy_led_get_hw_mode, +}; + +static int of_phy_probe_leds(struct phy_device *phydev) +{ + char devicename[32]; + + if (!phydev->drv->led_ops) + return 0; + + snprintf(devicename, sizeof(devicename), "ethernet-phy%i", phydev->phyindex); + + return of_register_hw_controlled_leds(&phydev->mdio.dev, devicename, + &phy_hw_controlled_led_ops); +} + +#else /* !IS_ENABLED(CONFIG_LEDS_HW_CONTROLLED) */ + +static inline int of_phy_probe_leds(struct phy_device *phydev) +{ + return 0; +} + +#endif /* !IS_ENABLED(CONFIG_LEDS_HW_CONTROLLED) */ + /** * phy_probe - probe and init a PHY device * @dev: device to probe and init @@ -2922,6 +3019,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 themselves + */ + if (!err) + of_phy_probe_leds(phydev); + return err; } diff --git a/include/linux/phy.h b/include/linux/phy.h index 52881e21ad951..8a4ab72c74dd4 100644 --- a/include/linux/phy.h +++ b/include/linux/phy.h @@ -14,6 +14,7 @@ #include #include #include +#include #include #include #include @@ -741,6 +742,9 @@ struct phy_driver { int (*set_loopback)(struct phy_device *dev, bool enable); int (*get_sqi)(struct phy_device *dev); int (*get_sqi_max)(struct phy_device *dev); + + /* PHY connected and controlled LEDs */ + const struct hw_controlled_led_ops *led_ops; }; #define to_phy_driver(d) container_of(to_mdio_common_driver(d), \ struct phy_driver, mdiodrv) From patchwork Wed Sep 9 16:25:51 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: 1360780 X-Patchwork-Delegate: davem@davemloft.net Return-Path: X-Original-To: patchwork-incoming-netdev@ozlabs.org Delivered-To: patchwork-incoming-netdev@ozlabs.org Authentication-Results: ozlabs.org; spf=pass (sender SPF authorized) smtp.mailfrom=vger.kernel.org (client-ip=23.128.96.18; helo=vger.kernel.org; envelope-from=netdev-owner@vger.kernel.org; receiver=) Authentication-Results: ozlabs.org; dmarc=pass (p=none dis=none) header.from=nic.cz Authentication-Results: ozlabs.org; dkim=pass (1024-bit key; secure) header.d=nic.cz header.i=@nic.cz header.a=rsa-sha256 header.s=default header.b=R6qBombe; dkim-atps=neutral Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by ozlabs.org (Postfix) with ESMTP id 4Bmnj65YYCz9sT6 for ; Thu, 10 Sep 2020 02:36:42 +1000 (AEST) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1730966AbgIIQgX (ORCPT ); Wed, 9 Sep 2020 12:36:23 -0400 Received: from mail.nic.cz ([217.31.204.67]:34820 "EHLO mail.nic.cz" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1731068AbgIIQ0G (ORCPT ); Wed, 9 Sep 2020 12:26:06 -0400 Received: from dellmb.labs.office.nic.cz (unknown [IPv6:2001:1488:fffe:6:cac7:3539:7f1f:463]) by mail.nic.cz (Postfix) with ESMTP id A4CB5140A77; Wed, 9 Sep 2020 18:25:55 +0200 (CEST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=nic.cz; s=default; t=1599668755; bh=CCID+6XO7E5eXop8eKP/NFb1pvL8WXiQXdWzIDrO1ko=; h=From:To:Date; b=R6qBombeGlUUJI8+cD9oVr800xcBkPHmLu9PoxsEh9GYi5WmDhU2uYJ5Q1Z1euztx aI+N6LjqzkoFeGn7xzuRS4eqSgaQ84EFPqgQw6Z+qWwIQI1MNL0FA0VADA88Ssb+xc xd2B9yPTQa/694GSdImzwU4rDjxPBKBhTUIh3ryE= From: =?utf-8?q?Marek_Beh=C3=BAn?= To: netdev@vger.kernel.org Cc: linux-leds@vger.kernel.org, Pavel Machek , Dan Murphy , =?utf-8?q?Ond=C5=99ej_Jirman?= , Russell King , Andrew Lunn , linux-kernel@vger.kernel.org, Matthias Schiffer , "David S. Miller" , =?utf-8?q?Marek_Beh=C3=BAn?= Subject: [PATCH net-next + leds v2 6/7] net: phy: marvell: add support for LEDs controlled by Marvell PHYs Date: Wed, 9 Sep 2020 18:25:51 +0200 Message-Id: <20200909162552.11032-7-marek.behun@nic.cz> X-Mailer: git-send-email 2.26.2 In-Reply-To: <20200909162552.11032-1-marek.behun@nic.cz> References: <20200909162552.11032-1-marek.behun@nic.cz> MIME-Version: 1.0 X-Spam-Checker-Version: SpamAssassin 3.4.2 (2018-09-13) on mail.nic.cz X-Spam-Status: No, score=0.00 X-Spamd-Bar: / X-Virus-Scanned: clamav-milter 0.102.2 at mail X-Virus-Status: Clean Sender: netdev-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: netdev@vger.kernel.org This patch adds support for controlling the LEDs connected to several families of Marvell PHYs via the PHY HW LED trigger API. These families are: 88E1112, 88E1121R, 88E1240, 88E1340S, 88E1510 and 88E1545. More can be added. This patch does not yet add support for compound LED modes. This could be achieved via the LED multicolor framework. Settings such as HW blink rate or pulse stretch duration are not yet supported. Signed-off-by: Marek Behún --- drivers/net/phy/marvell.c | 314 +++++++++++++++++++++++++++++++++++++- 1 file changed, 312 insertions(+), 2 deletions(-) diff --git a/drivers/net/phy/marvell.c b/drivers/net/phy/marvell.c index bb86ac0bd0920..7aedb529e1540 100644 --- a/drivers/net/phy/marvell.c +++ b/drivers/net/phy/marvell.c @@ -148,6 +148,13 @@ #define MII_88E1510_PHY_LED_DEF 0x1177 #define MII_88E1510_PHY_LED0_LINK_LED1_ACTIVE 0x1040 +#define MII_PHY_LED_POLARITY_CTRL 17 +#define MII_PHY_LED_TIMER_CTRL 18 +#define MII_PHY_LED45_CTRL 19 + +#define MII_PHY_LED_CTRL_FORCE_ON 0x9 +#define MII_PHY_LED_CTRL_FORCE_OFF 0x8 + #define MII_M1011_PHY_STATUS 0x11 #define MII_M1011_PHY_STATUS_1000 0x8000 #define MII_M1011_PHY_STATUS_100 0x4000 @@ -252,6 +259,8 @@ #define LPA_PAUSE_FIBER 0x180 #define LPA_PAUSE_ASYM_FIBER 0x100 +#define MARVELL_PHY_MAX_LEDS 6 + #define NB_FIBER_STATS 1 MODULE_DESCRIPTION("Marvell PHY driver"); @@ -280,6 +289,7 @@ struct marvell_priv { u32 last; u32 step; s8 pair; + u16 legacy_led_config_mask; }; static int marvell_read_page(struct phy_device *phydev) @@ -662,8 +672,300 @@ static int m88e1510_config_aneg(struct phy_device *phydev) return err; } +#if IS_ENABLED(CONFIG_LEDS_HW_CONTROLLED) + +enum { + COMMON = BIT(0), + L1V0_RECV = BIT(1), + L1V0_COPPER = BIT(2), + L1V5_100_FIBER = BIT(3), + L1V5_100_10 = BIT(4), + L2V2_INIT = BIT(5), + L2V2_PTP = BIT(6), + L2V2_DUPLEX = BIT(7), + L3V0_FIBER = BIT(8), + L3V0_LOS = BIT(9), + L3V5_TRANS = BIT(10), + L3V7_FIBER = BIT(11), + L3V7_DUPLEX = BIT(12), +}; + +struct marvell_led_mode_info { + const char *name; + s8 regval[MARVELL_PHY_MAX_LEDS]; + u32 flags; +}; + +static const struct marvell_led_mode_info marvell_led_mode_info[] = { + { "link", { 0x0, -1, 0x0, -1, -1, -1, }, COMMON }, + { "link/act", { 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, }, COMMON }, + { "1Gbps/100Mbps/10Mbps", { 0x2, -1, -1, -1, -1, -1, }, COMMON }, + { "act", { 0x3, 0x3, 0x3, 0x3, 0x3, 0x3, }, COMMON }, + { "blink-act", { 0x4, 0x4, 0x4, 0x4, 0x4, 0x4, }, COMMON }, + { "tx", { 0x5, -1, 0x5, -1, 0x5, 0x5, }, COMMON }, + { "tx", { -1, -1, -1, 0x5, -1, -1, }, L3V5_TRANS }, + { "rx", { -1, -1, -1, -1, 0x0, 0x0, }, COMMON }, + { "rx", { -1, 0x0, -1, -1, -1, -1, }, L1V0_RECV }, + { "copper", { 0x6, -1, -1, -1, -1, -1, }, COMMON }, + { "copper", { -1, 0x0, -1, -1, -1, -1, }, L1V0_COPPER }, + { "1Gbps", { 0x7, -1, -1, -1, -1, -1, }, COMMON }, + { "link/rx", { -1, 0x2, -1, 0x2, 0x2, 0x2, }, COMMON }, + { "100Mbps-fiber", { -1, 0x5, -1, -1, -1, -1, }, L1V5_100_FIBER }, + { "100Mbps-10Mbps", { -1, 0x5, -1, -1, -1, -1, }, L1V5_100_10 }, + { "1Gbps-100Mbps", { -1, 0x6, -1, -1, -1, -1, }, COMMON }, + { "1Gbps-10Mbps", { -1, -1, 0x6, 0x6, -1, -1, }, COMMON }, + { "100Mbps", { -1, 0x7, -1, -1, -1, -1, }, COMMON }, + { "10Mbps", { -1, -1, 0x7, -1, -1, -1, }, COMMON }, + { "fiber", { -1, -1, -1, 0x0, -1, -1, }, L3V0_FIBER }, + { "fiber", { -1, -1, -1, 0x7, -1, -1, }, L3V7_FIBER }, + { "FullDuplex", { -1, -1, -1, 0x7, -1, -1, }, L3V7_DUPLEX }, + { "FullDuplex", { -1, -1, -1, -1, 0x6, 0x6, }, COMMON }, + { "FullDuplex/collision", { -1, -1, -1, -1, 0x7, 0x7, }, COMMON }, + { "FullDuplex/collision", { -1, -1, 0x2, -1, -1, -1, }, L2V2_DUPLEX }, + { "ptp", { -1, -1, 0x2, -1, -1, -1, }, L2V2_PTP }, + { "init", { -1, -1, 0x2, -1, -1, -1, }, L2V2_INIT }, + { "los", { -1, -1, -1, 0x0, -1, -1, }, L3V0_LOS }, + { "blink", { 0xb, 0xb, 0xb, 0xb, 0xb, 0xb, }, COMMON }, +}; + +struct marvell_leds_info { + u32 family; + int nleds; + u32 flags; +}; + +#define LED(fam, n, flg) \ + { \ + .family = MARVELL_PHY_FAMILY_ID(MARVELL_PHY_ID_88E##fam), \ + .nleds = (n), \ + .flags = (flg), \ + } \ + +static const struct marvell_leds_info marvell_leds_info[] = { + LED(1112, 4, COMMON | L1V0_COPPER | L1V5_100_FIBER | L2V2_INIT | L3V0_LOS | L3V5_TRANS | + L3V7_FIBER), + LED(1121R, 3, COMMON | L1V5_100_10), + LED(1240, 6, COMMON | L3V5_TRANS), + LED(1340S, 6, COMMON | L1V0_COPPER | L1V5_100_FIBER | L2V2_PTP | L3V0_FIBER | L3V7_DUPLEX), + LED(1510, 3, COMMON | L1V0_RECV | L1V5_100_FIBER | L2V2_DUPLEX), + LED(1545, 6, COMMON | L1V0_COPPER | L1V5_100_FIBER | L3V0_FIBER | L3V7_DUPLEX), +}; + +static inline int marvell_led_reg(int led) +{ + switch (led) { + case 0 ... 3: + return MII_PHY_LED_CTRL; + case 4 ... 5: + return MII_PHY_LED45_CTRL; + default: + return -EINVAL; + } +} + +static int marvell_led_set_regval(struct phy_device *phydev, int led, u16 val) +{ + u16 mask; + int reg; + + reg = marvell_led_reg(led); + if (reg < 0) + return reg; + + val <<= (led % 4) * 4; + mask = 0xf << ((led % 4) * 4); + + return phy_modify_paged(phydev, MII_MARVELL_LED_PAGE, reg, mask, val); +} + +static int marvell_led_get_regval(struct phy_device *phydev, int led) +{ + int reg, val; + + reg = marvell_led_reg(led); + if (reg < 0) + return reg; + + val = phy_read_paged(phydev, MII_MARVELL_LED_PAGE, reg); + if (val < 0) + return val; + + val >>= (led % 4) * 4; + val &= 0xf; + + return val; +} + +static int marvell_led_set_polarity(struct phy_device *phydev, int led, bool active_low, + bool tristate) +{ + int reg, shift; + u16 mask, val; + + switch (led) { + case 0 ... 3: + reg = MII_PHY_LED_POLARITY_CTRL; + break; + case 4 ... 5: + reg = MII_PHY_LED45_CTRL; + break; + default: + return -EINVAL; + } + + val = 0; + if (!active_low) + val |= BIT(0); + if (tristate) + val |= BIT(1); + + shift = led * 2; + val <<= shift; + mask = 0x3 << shift; + + return phy_modify_paged(phydev, MII_MARVELL_LED_PAGE, reg, mask, val); +} + +static int marvell_led_brightness_set(struct device *dev, struct hw_controlled_led *led, + enum led_brightness brightness) +{ + struct phy_device *phydev = to_phy_device(dev); + u8 val; + + /* don't do anything if HW control is enabled */ + if (led->cdev.trigger == &hw_control_led_trig) + return 0; + + val = brightness ? MII_PHY_LED_CTRL_FORCE_ON : MII_PHY_LED_CTRL_FORCE_OFF; + + return marvell_led_set_regval(phydev, led->addr, val); +} + +static inline bool is_valid_led_mode(struct hw_controlled_led *led, + const struct marvell_led_mode_info *mode) +{ + const struct marvell_leds_info *info = led->priv; + + return mode->regval[led->addr] != -1 && (info->flags & mode->flags); +} + +static const char *marvell_led_iter_hw_mode(struct device *dev, struct hw_controlled_led *led, + void **iter) +{ + const struct marvell_led_mode_info *mode = *iter; + + if (!mode) + mode = marvell_led_mode_info; + + if (mode - marvell_led_mode_info == ARRAY_SIZE(marvell_led_mode_info)) + goto end; + + while (!is_valid_led_mode(led, mode)) { + ++mode; + if (mode - marvell_led_mode_info == ARRAY_SIZE(marvell_led_mode_info)) + goto end; + } + + *iter = (void *)(mode + 1); + return mode->name; +end: + *iter = NULL; + return NULL; +} + +static int marvell_led_set_hw_mode(struct device *dev, struct hw_controlled_led *led, + const char *name) +{ + struct phy_device *phydev = to_phy_device(dev); + const struct marvell_led_mode_info *mode; + int i; + + if (!name) + return 0; + + for (i = 0; i < ARRAY_SIZE(marvell_led_mode_info); ++i) { + mode = &marvell_led_mode_info[i]; + + if (!is_valid_led_mode(led, mode)) + continue; + + if (sysfs_streq(name, mode->name)) + return marvell_led_set_regval(phydev, led->addr, mode->regval[led->addr]); + } + + return -EINVAL; +} + +static const char *marvell_led_get_hw_mode(struct device *dev, struct hw_controlled_led *led) +{ + struct phy_device *phydev = to_phy_device(dev); + const struct marvell_led_mode_info *mode; + int i, regval; + + regval = marvell_led_get_regval(phydev, led->addr); + if (regval < 0) + return NULL; + + for (i = 0; i < ARRAY_SIZE(marvell_led_mode_info); ++i) { + mode = &marvell_led_mode_info[i]; + + if (!is_valid_led_mode(led, mode)) + continue; + + if (mode->regval[led->addr] == regval) + return mode->name; + } + + return NULL; +} + +static int marvell_led_init(struct device *dev, struct hw_controlled_led *led) +{ + struct phy_device *phydev = to_phy_device(dev); + const struct marvell_leds_info *info = NULL; + struct marvell_priv *priv = phydev->priv; + int ret, i; + + for (i = 0; i < ARRAY_SIZE(marvell_leds_info); ++i) { + if (MARVELL_PHY_FAMILY_ID(phydev->phy_id) == marvell_leds_info[i].family) { + info = &marvell_leds_info[i]; + break; + } + } + + if (!info) + return -EOPNOTSUPP; + + if (led->addr >= info->nleds) + return -EINVAL; + + led->priv = (void *)info; + led->cdev.max_brightness = 1; + + ret = marvell_led_set_polarity(phydev, led->addr, led->active_low, led->tristate); + if (ret < 0) + return ret; + + /* ensure marvell_config_led below does not change settings we have set for this LED */ + if (led->addr < 3) + priv->legacy_led_config_mask &= ~(0xf << (led->addr * 4)); + + return 0; +} + +static const struct hw_controlled_led_ops marvell_led_ops = { + .led_init = marvell_led_init, + .led_brightness_set = marvell_led_brightness_set, + .led_iter_hw_mode = marvell_led_iter_hw_mode, + .led_set_hw_mode = marvell_led_set_hw_mode, + .led_get_hw_mode = marvell_led_get_hw_mode, +}; + +#endif /* IS_ENABLED(CONFIG_LEDS_HW_CONTROLLED) */ + static void marvell_config_led(struct phy_device *phydev) { + struct marvell_priv *priv = phydev->priv; u16 def_config; int err; @@ -688,8 +990,9 @@ static void marvell_config_led(struct phy_device *phydev) return; } - err = phy_write_paged(phydev, MII_MARVELL_LED_PAGE, MII_PHY_LED_CTRL, - def_config); + def_config &= priv->legacy_led_config_mask; + err = phy_modify_paged(phydev, MII_MARVELL_LED_PAGE, MII_PHY_LED_CTRL, + priv->legacy_led_config_mask, def_config); if (err < 0) phydev_warn(phydev, "Fail to config marvell phy LED.\n"); } @@ -2580,6 +2883,7 @@ static int marvell_probe(struct phy_device *phydev) if (!priv) return -ENOMEM; + priv->legacy_led_config_mask = 0xffff; phydev->priv = priv; return 0; @@ -2656,6 +2960,7 @@ static struct phy_driver marvell_drivers[] = { .get_stats = marvell_get_stats, .get_tunable = m88e1011_get_tunable, .set_tunable = m88e1011_set_tunable, + .led_ops = hw_controlled_led_ops_ptr(&marvell_led_ops), }, { .phy_id = MARVELL_PHY_ID_88E1111, @@ -2717,6 +3022,7 @@ static struct phy_driver marvell_drivers[] = { .get_stats = marvell_get_stats, .get_tunable = m88e1011_get_tunable, .set_tunable = m88e1011_set_tunable, + .led_ops = hw_controlled_led_ops_ptr(&marvell_led_ops), }, { .phy_id = MARVELL_PHY_ID_88E1318S, @@ -2796,6 +3102,7 @@ static struct phy_driver marvell_drivers[] = { .get_sset_count = marvell_get_sset_count, .get_strings = marvell_get_strings, .get_stats = marvell_get_stats, + .led_ops = hw_controlled_led_ops_ptr(&marvell_led_ops), }, { .phy_id = MARVELL_PHY_ID_88E1116R, @@ -2844,6 +3151,7 @@ static struct phy_driver marvell_drivers[] = { .cable_test_start = marvell_vct7_cable_test_start, .cable_test_tdr_start = marvell_vct5_cable_test_tdr_start, .cable_test_get_status = marvell_vct7_cable_test_get_status, + .led_ops = hw_controlled_led_ops_ptr(&marvell_led_ops), }, { .phy_id = MARVELL_PHY_ID_88E1540, @@ -2896,6 +3204,7 @@ static struct phy_driver marvell_drivers[] = { .cable_test_start = marvell_vct7_cable_test_start, .cable_test_tdr_start = marvell_vct5_cable_test_tdr_start, .cable_test_get_status = marvell_vct7_cable_test_get_status, + .led_ops = hw_controlled_led_ops_ptr(&marvell_led_ops), }, { .phy_id = MARVELL_PHY_ID_88E3016, @@ -2964,6 +3273,7 @@ static struct phy_driver marvell_drivers[] = { .get_stats = marvell_get_stats, .get_tunable = m88e1540_get_tunable, .set_tunable = m88e1540_set_tunable, + .led_ops = hw_controlled_led_ops_ptr(&marvell_led_ops), }, { .phy_id = MARVELL_PHY_ID_88E1548P, From patchwork Wed Sep 9 16:25:52 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: 1360758 X-Patchwork-Delegate: davem@davemloft.net Return-Path: X-Original-To: patchwork-incoming-netdev@ozlabs.org Delivered-To: patchwork-incoming-netdev@ozlabs.org Authentication-Results: ozlabs.org; spf=pass (sender SPF authorized) smtp.mailfrom=vger.kernel.org (client-ip=23.128.96.18; helo=vger.kernel.org; envelope-from=netdev-owner@vger.kernel.org; receiver=) Authentication-Results: ozlabs.org; dmarc=pass (p=none dis=none) header.from=nic.cz Authentication-Results: ozlabs.org; dkim=pass (1024-bit key; secure) header.d=nic.cz header.i=@nic.cz header.a=rsa-sha256 header.s=default header.b=fCs8lADv; dkim-atps=neutral Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by ozlabs.org (Postfix) with ESMTP id 4BmnWV2dGBz9sSP for ; Thu, 10 Sep 2020 02:28:22 +1000 (AEST) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1731042AbgIIQ2O (ORCPT ); Wed, 9 Sep 2020 12:28:14 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:56148 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1731022AbgIIQ0G (ORCPT ); Wed, 9 Sep 2020 12:26:06 -0400 Received: from mail.nic.cz (lists.nic.cz [IPv6:2001:1488:800:400::400]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 8DA41C061795; Wed, 9 Sep 2020 09:26:05 -0700 (PDT) Received: from dellmb.labs.office.nic.cz (unknown [IPv6:2001:1488:fffe:6:cac7:3539:7f1f:463]) by mail.nic.cz (Postfix) with ESMTP id D5423140A7D; Wed, 9 Sep 2020 18:25:55 +0200 (CEST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=nic.cz; s=default; t=1599668755; bh=ETijTD2hHqyWxW2zwXhqdFU5Xk5g005codDN5mEO12w=; h=From:To:Date; b=fCs8lADvaSZx/tRW4It7vvjDQqLxXSf0ofh5ycAqs4qKwAnJ4M6nEOm7C/kWYjWzS ldSpXT1S247pBH6u+pIBV4mjNWMCYm3h+/2ed8WqrHa7K9IgMbq5PVpQOMQnqiu28j QeqYGFpVaS3hXsCIXMtHtGEsFgOywhS5gt+T5jio= From: =?utf-8?q?Marek_Beh=C3=BAn?= To: netdev@vger.kernel.org Cc: linux-leds@vger.kernel.org, Pavel Machek , Dan Murphy , =?utf-8?q?Ond=C5=99ej_Jirman?= , Russell King , Andrew Lunn , linux-kernel@vger.kernel.org, Matthias Schiffer , "David S. Miller" , =?utf-8?q?Marek_Beh=C3=BAn?= Subject: [PATCH net-next + mvebu v2 7/7] arm64: dts: armada-3720-turris-mox: add nodes for ethernet PHY LEDs Date: Wed, 9 Sep 2020 18:25:52 +0200 Message-Id: <20200909162552.11032-8-marek.behun@nic.cz> X-Mailer: git-send-email 2.26.2 In-Reply-To: <20200909162552.11032-1-marek.behun@nic.cz> References: <20200909162552.11032-1-marek.behun@nic.cz> MIME-Version: 1.0 X-Spam-Checker-Version: SpamAssassin 3.4.2 (2018-09-13) on mail.nic.cz X-Spam-Status: No, score=0.00 X-Spamd-Bar: / X-Virus-Scanned: clamav-milter 0.102.2 at mail X-Virus-Status: Clean Sender: netdev-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: netdev@vger.kernel.org Add nodes for the green and yellow LEDs that are connected to the ethernet PHY chip on Turris MOX A. Signed-off-by: Marek Behún --- .../dts/marvell/armada-3720-turris-mox.dts | 23 +++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/arch/arm64/boot/dts/marvell/armada-3720-turris-mox.dts b/arch/arm64/boot/dts/marvell/armada-3720-turris-mox.dts index f3a678e0fd99b..6da03b6c69c0a 100644 --- a/arch/arm64/boot/dts/marvell/armada-3720-turris-mox.dts +++ b/arch/arm64/boot/dts/marvell/armada-3720-turris-mox.dts @@ -9,6 +9,7 @@ #include #include #include +#include #include "armada-372x.dtsi" / { @@ -273,6 +274,28 @@ &mdio { phy1: ethernet-phy@1 { reg = <1>; + + leds { + compatible = "linux,hw-controlled-leds"; + #address-cells = <1>; + #size-cells = <0>; + + led@0 { + reg = <0>; + color = ; + function = LED_FUNCTION_STATUS; + linux,default-trigger = "dev-hw-mode"; + linux,default-hw-mode = "1Gbps/100Mbps/10Mbps"; + }; + + led@1 { + reg = <1>; + color = ; + function = LED_FUNCTION_ACTIVITY; + linux,default-trigger = "dev-hw-mode"; + linux,default-hw-mode = "blink-act"; + }; + }; }; /* switch nodes are enabled by U-Boot if modules are present */