aboutsummaryrefslogtreecommitdiffstats
path: root/target/linux/generic/backport-4.19/726-v5.5-net-sfp-avoid-power-switch-on-address-change-modules.patch
blob: 2ddd4c4d02699a1e6e94a32b349c92854d390838 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
From dca678b8838945572cf50584cb33a7199c1fd397 Mon Sep 17 00:00:00 2001
From: Russell King <rmk+kernel@armlinux.org.uk>
Date: Thu, 17 Oct 2019 00:24:18 +0100
Subject: [PATCH 624/660] net: sfp: avoid power switch on address-change
 modules

If the module indicates that it requires an address change sequence to
switch between address 0x50 and 0x51, which we don't support, we can't
write to the register that controls the power mode to switch to high
power mode.  Warn the user that the module may not be functional in
this case, and don't try to change the power mode.

Signed-off-by: Russell King <rmk+kernel@armlinux.org.uk>
---
 drivers/net/phy/sfp.c | 31 ++++++++++++++++++++-----------
 1 file changed, 20 insertions(+), 11 deletions(-)

--- a/drivers/net/phy/sfp.c
+++ b/drivers/net/phy/sfp.c
@@ -1320,25 +1320,34 @@ static int sfp_module_parse_power(struct
 	if (sfp->id.ext.options & cpu_to_be16(SFP_OPTIONS_HIGH_POWER_LEVEL))
 		power_mW = 2000;
 
-	if (sfp->id.ext.sff8472_compliance == SFP_SFF8472_COMPLIANCE_NONE &&
-	    (sfp->id.ext.diagmon & (SFP_DIAGMON_DDM | SFP_DIAGMON_ADDRMODE)) !=
-	    SFP_DIAGMON_DDM) {
-		/* The module appears not to implement bus address 0xa2,
-		 * or requires an address change sequence, so assume that
-		 * the module powers up in the indicated power mode.
-		 */
-		if (power_mW > sfp->max_power_mW) {
+	if (power_mW > sfp->max_power_mW) {
+		/* Module power specification exceeds the allowed maximum. */
+		if (sfp->id.ext.sff8472_compliance ==
+			SFP_SFF8472_COMPLIANCE_NONE &&
+		    !(sfp->id.ext.diagmon & SFP_DIAGMON_DDM)) {
+			/* The module appears not to implement bus address
+			 * 0xa2, so assume that the module powers up in the
+			 * indicated mode.
+			 */
 			dev_err(sfp->dev,
 				"Host does not support %u.%uW modules\n",
 				power_mW / 1000, (power_mW / 100) % 10);
 			return -EINVAL;
+		} else {
+			dev_warn(sfp->dev,
+				 "Host does not support %u.%uW modules, module left in power mode 1\n",
+				 power_mW / 1000, (power_mW / 100) % 10);
+			return 0;
 		}
-		return 0;
 	}
 
-	if (power_mW > sfp->max_power_mW) {
+	/* If the module requires a higher power mode, but also requires
+	 * an address change sequence, warn the user that the module may
+	 * not be functional.
+	 */
+	if (sfp->id.ext.diagmon & SFP_DIAGMON_ADDRMODE && power_mW > 1000) {
 		dev_warn(sfp->dev,
-			 "Host does not support %u.%uW modules, module left in power mode 1\n",
+			 "Address Change Sequence not supported but module requies %u.%uW, module may not be functional\n",
 			 power_mW / 1000, (power_mW / 100) % 10);
 		return 0;
 	}
.h" @@ -660,3 +662,68 @@ void of_gpiochip_remove(struct gpio_chip gpiochip_remove_pin_ranges(chip); of_node_put(chip->of_node); } + +static struct of_device_id gpio_export_ids[] = { + { .compatible = "gpio-export" }, + { /* sentinel */ } +}; + +static int of_gpio_export_probe(struct platform_device *pdev) +{ + struct device_node *np = pdev->dev.of_node; + struct device_node *cnp; + u32 val; + int nb = 0; + + for_each_child_of_node(np, cnp) { + const char *name = NULL; + int gpio; + bool dmc; + int max_gpio = 1; + int i; + + of_property_read_string(cnp, "gpio-export,name", &name); + + if (!name) + max_gpio = of_gpio_count(cnp); + + for (i = 0; i < max_gpio; i++) { + unsigned flags = 0; + enum of_gpio_flags of_flags; + + gpio = of_get_gpio_flags(cnp, i, &of_flags); + if (!gpio_is_valid(gpio)) + return gpio; + + if (of_flags == OF_GPIO_ACTIVE_LOW) + flags |= GPIOF_ACTIVE_LOW; + + if (!of_property_read_u32(cnp, "gpio-export,output", &val)) + flags |= val ? GPIOF_OUT_INIT_HIGH : GPIOF_OUT_INIT_LOW; + else + flags |= GPIOF_IN; + + if (devm_gpio_request_one(&pdev->dev, gpio, flags, name ? name : of_node_full_name(np))) + continue; + + dmc = of_property_read_bool(cnp, "gpio-export,direction_may_change"); + gpio_export_with_name(gpio, dmc, name); + nb++; + } + } + + dev_info(&pdev->dev, "%d gpio(s) exported\n", nb); + + return 0; +} + +static struct platform_driver gpio_export_driver = { + .driver = { + .name = "gpio-export", + .owner = THIS_MODULE, + .of_match_table = of_match_ptr(gpio_export_ids), + }, + .probe = of_gpio_export_probe, +}; + +module_platform_driver(gpio_export_driver); --- a/drivers/gpio/gpiolib-sysfs.c +++ b/drivers/gpio/gpiolib-sysfs.c @@ -568,7 +568,7 @@ static struct class gpio_class = { * * Returns zero on success, else an error. */ -int gpiod_export(struct gpio_desc *desc, bool direction_may_change) +int __gpiod_export(struct gpio_desc *desc, bool direction_may_change, const char *name) { struct gpio_chip *chip; struct gpio_device *gdev; @@ -630,6 +630,8 @@ int gpiod_export(struct gpio_desc *desc, offset = gpio_chip_hwgpio(desc); if (chip->names && chip->names[offset]) ioname = chip->names[offset]; + if (name) + ioname = name; dev = device_create_with_groups(&gpio_class, &gdev->dev, MKDEV(0, 0), data, gpio_groups, @@ -651,6 +653,12 @@ err_unlock: gpiod_dbg(desc, "%s: status %d\n", __func__, status); return status; } +EXPORT_SYMBOL_GPL(__gpiod_export); + +int gpiod_export(struct gpio_desc *desc, bool direction_may_change) +{ + return __gpiod_export(desc, direction_may_change, NULL); +} EXPORT_SYMBOL_GPL(gpiod_export); static int match_export(struct device *dev, const void *desc) --- a/include/asm-generic/gpio.h +++ b/include/asm-generic/gpio.h @@ -127,6 +127,12 @@ static inline int gpio_export(unsigned g return gpiod_export(gpio_to_desc(gpio), direction_may_change); } +int __gpiod_export(struct gpio_desc *desc, bool direction_may_change, const char *name); +static inline int gpio_export_with_name(unsigned gpio, bool direction_may_change, const char *name) +{ + return __gpiod_export(gpio_to_desc(gpio), direction_may_change, name); +} + static inline int gpio_export_link(struct device *dev, const char *name, unsigned gpio) { --- a/include/linux/gpio/consumer.h +++ b/include/linux/gpio/consumer.h @@ -533,6 +533,7 @@ struct gpio_desc *devm_fwnode_get_gpiod_ #if IS_ENABLED(CONFIG_GPIOLIB) && IS_ENABLED(CONFIG_GPIO_SYSFS) +int _gpiod_export(struct gpio_desc *desc, bool direction_may_change, const char *name); int gpiod_export(struct gpio_desc *desc, bool direction_may_change); int gpiod_export_link(struct device *dev, const char *name, struct gpio_desc *desc); @@ -540,6 +541,13 @@ void gpiod_unexport(struct gpio_desc *de #else /* CONFIG_GPIOLIB && CONFIG_GPIO_SYSFS */ +static inline int _gpiod_export(struct gpio_desc *desc, + bool direction_may_change, + const char *name) +{ + return -ENOSYS; +} + static inline int gpiod_export(struct gpio_desc *desc, bool direction_may_change) {