From: alexander.levin@verizon.com
To: "linux-kernel@vger.kernel.org" <linux-kernel@vger.kernel.org>,
"stable@vger.kernel.org" <stable@vger.kernel.org>
Cc: Andrew Jeffery <andrew@aj.id.au>,
Jacek Anaszewski <jacek.anaszewski@gmail.com>,
alexander.levin@verizon.com
Subject: [PATCH AUTOSEL for 4.14 29/60] leds: pca955x: Don't invert requested value in pca955x_gpio_set_value()
Date: Wed, 13 Dec 2017 01:55:18 +0000 [thread overview]
Message-ID: <20171213015455.6455-29-alexander.levin@verizon.com> (raw)
In-Reply-To: <20171213015455.6455-1-alexander.levin@verizon.com>
From: Andrew Jeffery <andrew@aj.id.au>
[ Upstream commit 52ca7d0f7bdad832b291ed979146443533ee79c0 ]
The PCA9552 lines can be used either for driving LEDs or as GPIOs. The
manual states that for LEDs, the operation is open-drain:
The LSn LED select registers determine the source of the LED data.
00 = output is set LOW (LED on)
01 = output is set high-impedance (LED off; default)
10 = output blinks at PWM0 rate
11 = output blinks at PWM1 rate
For GPIOs it suggests a pull-up so that the open-case drives the line
high:
For use as output, connect external pull-up resistor to the pin
and size it according to the DC recommended operating
characteristics. LED output pin is HIGH when the output is
programmed as high-impedance, and LOW when the output is
programmed LOW through the ‘LED selector’ register. The output
can be pulse-width controlled when PWM0 or PWM1 are used.
Now, I have a hardware design that uses the LED controller to control
LEDs. However, for $reasons, we're using the leds-gpio driver to drive
the them. The reasons are here are a tangent but lead to the discovery
of the inversion, which manifested as the LEDs being set to full
brightness at boot when we expected them to be off.
As we're driving the LEDs through leds-gpio, this means wending our way
through the gpiochip abstractions. So with that in mind we need to
describe an active-low GPIO configuration to drive the LEDs as though
they were GPIOs.
The set() gpiochip callback in leds-pca955x does the following:
...
if (val)
pca955x_led_set(&led->led_cdev, LED_FULL);
else
pca955x_led_set(&led->led_cdev, LED_OFF);
...
Where LED_FULL = 255. pca955x_led_set() in turn does:
...
switch (value) {
case LED_FULL:
ls = pca955x_ledsel(ls, ls_led, PCA955X_LS_LED_ON);
break;
...
Where PCA955X_LS_LED_ON is defined as:
#define PCA955X_LS_LED_ON 0x0 /* Output LOW */
So here we have some type confusion: We've crossed domains from GPIO
behaviour to LED behaviour without accounting for possible inversions
in the process.
Stepping back to leds-gpio for a moment, during probe() we call
create_gpio_led(), which eventually executes:
if (template->default_state == LEDS_GPIO_DEFSTATE_KEEP) {
state = gpiod_get_value_cansleep(led_dat->gpiod);
if (state < 0)
return state;
} else {
state = (template->default_state == LEDS_GPIO_DEFSTATE_ON);
}
...
ret = gpiod_direction_output(led_dat->gpiod, state);
In the devicetree the GPIO is annotated as active-low, and
gpiod_get_value_cansleep() handles this for us:
int gpiod_get_value_cansleep(const struct gpio_desc *desc)
{
int value;
might_sleep_if(extra_checks);
VALIDATE_DESC(desc);
value = _gpiod_get_raw_value(desc);
if (value < 0)
return value;
if (test_bit(FLAG_ACTIVE_LOW, &desc->flags))
value = !value;
return value;
}
_gpiod_get_raw_value() in turn calls through the get() callback for the
gpiochip implementation, so returning to our get() implementation in
leds-pca955x we find we extract the raw value from hardware:
static int pca955x_gpio_get_value(struct gpio_chip *gc, unsigned int offset)
{
struct pca955x *pca955x = gpiochip_get_data(gc);
struct pca955x_led *led = &pca955x->leds[offset];
u8 reg = pca955x_read_input(pca955x->client, led->led_num / 8);
return !!(reg & (1 << (led->led_num % 8)));
}
This behaviour is not symmetric with that of set(), where the val is
inverted by the driver.
Closing the loop on the GPIO_ACTIVE_LOW inversions,
gpiod_direction_output(), like gpiod_get_value_cansleep(), handles it
for us:
int gpiod_direction_output(struct gpio_desc *desc, int value)
{
VALIDATE_DESC(desc);
if (test_bit(FLAG_ACTIVE_LOW, &desc->flags))
value = !value;
else
value = !!value;
return _gpiod_direction_output_raw(desc, value);
}
All-in-all, with a value of 'keep' for default-state property in a
leds-gpio child node, the current state of the hardware will in-fact be
inverted; precisely the opposite of what was intended.
Rework leds-pca955x so that we avoid the incorrect inversion and clarify
the semantics with respect to GPIO.
Signed-off-by: Andrew Jeffery <andrew@aj.id.au>
Reviewed-by: Cédric Le Goater <clg@kaod.org>
Tested-by: Joel Stanley <joel@jms.id.au>
Tested-by: Matt Spinler <mspinler@linux.vnet.ibm.com>
Signed-off-by: Jacek Anaszewski <jacek.anaszewski@gmail.com>
Signed-off-by: Sasha Levin <alexander.levin@verizon.com>
---
drivers/leds/leds-pca955x.c | 17 ++++++++++++-----
1 file changed, 12 insertions(+), 5 deletions(-)
diff --git a/drivers/leds/leds-pca955x.c b/drivers/leds/leds-pca955x.c
index 905729191d3e..78183f90820e 100644
--- a/drivers/leds/leds-pca955x.c
+++ b/drivers/leds/leds-pca955x.c
@@ -61,6 +61,10 @@
#define PCA955X_LS_BLINK0 0x2 /* Blink at PWM0 rate */
#define PCA955X_LS_BLINK1 0x3 /* Blink at PWM1 rate */
+#define PCA955X_GPIO_INPUT LED_OFF
+#define PCA955X_GPIO_HIGH LED_OFF
+#define PCA955X_GPIO_LOW LED_FULL
+
enum pca955x_type {
pca9550,
pca9551,
@@ -329,9 +333,9 @@ static int pca955x_set_value(struct gpio_chip *gc, unsigned int offset,
struct pca955x_led *led = &pca955x->leds[offset];
if (val)
- return pca955x_led_set(&led->led_cdev, LED_FULL);
- else
- return pca955x_led_set(&led->led_cdev, LED_OFF);
+ return pca955x_led_set(&led->led_cdev, PCA955X_GPIO_HIGH);
+
+ return pca955x_led_set(&led->led_cdev, PCA955X_GPIO_LOW);
}
static void pca955x_gpio_set_value(struct gpio_chip *gc, unsigned int offset,
@@ -355,8 +359,11 @@ static int pca955x_gpio_get_value(struct gpio_chip *gc, unsigned int offset)
static int pca955x_gpio_direction_input(struct gpio_chip *gc,
unsigned int offset)
{
- /* To use as input ensure pin is not driven */
- return pca955x_set_value(gc, offset, 0);
+ struct pca955x *pca955x = gpiochip_get_data(gc);
+ struct pca955x_led *led = &pca955x->leds[offset];
+
+ /* To use as input ensure pin is not driven. */
+ return pca955x_led_set(&led->led_cdev, PCA955X_GPIO_INPUT);
}
static int pca955x_gpio_direction_output(struct gpio_chip *gc,
--
2.11.0
next prev parent reply other threads:[~2017-12-13 2:59 UTC|newest]
Thread overview: 65+ messages / expand[flat|nested] mbox.gz Atom feed top
2017-12-13 1:55 [PATCH AUTOSEL for 4.14 01/60] backlight: pwm_bl: Fix overflow condition alexander.levin
2017-12-13 1:55 ` [PATCH AUTOSEL for 4.14 02/60] drm: Add retries for lspcon mode detection alexander.levin
2017-12-13 1:55 ` [PATCH AUTOSEL for 4.14 03/60] clk: sunxi-ng: nm: Check if requested rate is supported by fractional clock alexander.levin
2017-12-13 1:55 ` [PATCH AUTOSEL for 4.14 06/60] crypto: talitos - fix AEAD test failures alexander.levin
2017-12-13 1:55 ` [PATCH AUTOSEL for 4.14 04/60] clk: sunxi-ng: sun5i: Fix bit offset of audio PLL post-divider alexander.levin
2017-12-13 1:55 ` [PATCH AUTOSEL for 4.14 05/60] crypto: talitos - fix ctr-aes-talitos alexander.levin
2017-12-13 1:55 ` [PATCH AUTOSEL for 4.14 07/60] crypto: talitos - fix memory corruption on SEC2 alexander.levin
2017-12-13 1:55 ` [PATCH AUTOSEL for 4.14 10/60] rtc: pl031: make interrupt optional alexander.levin
2017-12-13 1:55 ` [PATCH AUTOSEL for 4.14 11/60] kvm, mm: account kvm related kmem slabs to kmemcg alexander.levin
2017-12-13 1:55 ` [PATCH AUTOSEL for 4.14 09/60] crypto: lrw - Fix an error handling path in 'create()' alexander.levin
2017-12-13 1:55 ` [PATCH AUTOSEL for 4.14 08/60] crypto: crypto4xx - increase context and scatter ring buffer elements alexander.levin
2017-12-13 1:55 ` [PATCH AUTOSEL for 4.14 15/60] scsi: mpt3sas: Fix IO error occurs on pulling out a drive from RAID1 volume created on two SATA drive alexander.levin
2017-12-13 1:55 ` [PATCH AUTOSEL for 4.14 14/60] scsi: cxgb4i: fix Tx skb leak alexander.levin
2017-12-13 1:55 ` [PATCH AUTOSEL for 4.14 13/60] PCI: Avoid bus reset if bridge itself is broken alexander.levin
2017-12-13 1:55 ` [PATCH AUTOSEL for 4.14 12/60] net: phy: at803x: Change error to EINVAL for invalid MAC alexander.levin
2017-12-13 1:55 ` [PATCH AUTOSEL for 4.14 18/60] igb: check memory allocation failure alexander.levin
2017-12-13 1:55 ` [PATCH AUTOSEL for 4.14 19/60] Bluetooth: avoid silent hci_bcm ACPI PM regression alexander.levin
2017-12-13 8:14 ` Johan Hovold
2017-12-13 13:37 ` alexander.levin
2017-12-13 14:05 ` Johan Hovold
2017-12-13 14:18 ` alexander.levin
2017-12-13 1:55 ` [PATCH AUTOSEL for 4.14 16/60] PCI: Create SR-IOV virtfn/physfn links before attaching driver alexander.levin
2017-12-13 1:55 ` [PATCH AUTOSEL for 4.14 17/60] PM / OPP: Move error message to debug level alexander.levin
2017-12-13 1:55 ` [PATCH AUTOSEL for 4.14 22/60] iio: st_sensors: add register mask for status register alexander.levin
2017-12-13 1:55 ` [PATCH AUTOSEL for 4.14 21/60] i40e: use the safe hash table iterator when deleting mac filters alexander.levin
2017-12-13 1:55 ` [PATCH AUTOSEL for 4.14 20/60] kbuild: re-order the code to not parse unnecessary variables alexander.levin
2017-12-20 17:13 ` Greg KH
2017-12-13 1:55 ` [PATCH AUTOSEL for 4.14 23/60] ixgbe: fix use of uninitialized padding alexander.levin
2017-12-13 1:55 ` [PATCH AUTOSEL for 4.14 24/60] IB/rxe: check for allocation failure on elem alexander.levin
2017-12-13 1:55 ` [PATCH AUTOSEL for 4.14 26/60] md: always set THREAD_WAKEUP and wake up wqueue if thread existed alexander.levin
2017-12-13 1:55 ` [PATCH AUTOSEL for 4.14 25/60] block,bfq: Disable writeback throttling alexander.levin
2017-12-13 1:55 ` [PATCH AUTOSEL for 4.14 27/60] ip_gre: check packet length and mtu correctly in erspan tx alexander.levin
2017-12-13 1:55 ` [PATCH AUTOSEL for 4.14 30/60] Bluetooth: hci_uart_set_flow_control: Fix NULL deref when using serdev alexander.levin
2017-12-13 1:55 ` alexander.levin [this message]
2017-12-13 1:55 ` [PATCH AUTOSEL for 4.14 28/60] ipv6: grab rt->rt6i_ref before allocating pcpu rt alexander.levin
2017-12-13 1:55 ` [PATCH AUTOSEL for 4.14 31/60] Bluetooth: hci_bcm: Fix setting of irq trigger type alexander.levin
2017-12-13 1:55 ` [PATCH AUTOSEL for 4.14 32/60] i40e/i40evf: spread CPU affinity hints across online CPUs only alexander.levin
2017-12-13 1:55 ` [PATCH AUTOSEL for 4.14 34/60] tracing: Exclude 'generic fields' from histograms alexander.levin
2017-12-13 1:55 ` [PATCH AUTOSEL for 4.14 33/60] PCI/AER: Report non-fatal errors only to the affected endpoint alexander.levin
2017-12-13 1:55 ` [PATCH AUTOSEL for 4.14 37/60] ASoC: img-parallel-out: Add pm_runtime_get/put to set_fmt callback alexander.levin
2017-12-13 1:55 ` [PATCH AUTOSEL for 4.14 38/60] powerpc/xmon: Avoid tripping SMP hardlockup watchdog alexander.levin
2017-12-13 1:55 ` [PATCH AUTOSEL for 4.14 35/60] percpu: don't forget to free the temporary struct pcpu_alloc_info alexander.levin
2017-12-13 1:55 ` [PATCH AUTOSEL for 4.14 36/60] ASoC: codecs: msm8916-wcd-analog: fix micbias level alexander.levin
2017-12-13 1:55 ` [PATCH AUTOSEL for 4.14 40/60] sctp: silence warns on sctp_stream_init allocations alexander.levin
2017-12-13 1:55 ` [PATCH AUTOSEL for 4.14 39/60] powerpc/watchdog: Do not trigger SMP crash from touch_nmi_watchdog alexander.levin
2017-12-13 1:55 ` [PATCH AUTOSEL for 4.14 41/60] ASoC: codecs: msm8916-wcd-analog: fix module autoload alexander.levin
2017-12-13 1:55 ` [PATCH AUTOSEL for 4.14 42/60] fm10k: fix mis-ordered parameters in declaration for .ndo_set_vf_bw alexander.levin
2017-12-13 1:55 ` [PATCH AUTOSEL for 4.14 44/60] scsi: lpfc: PLOGI failures during NPIV testing alexander.levin
2017-12-13 1:55 ` [PATCH AUTOSEL for 4.14 45/60] scsi: lpfc: Fix warning messages when NVME_TARGET_FC not defined alexander.levin
2017-12-13 1:55 ` [PATCH AUTOSEL for 4.14 46/60] i40e: fix client notify of VF reset alexander.levin
2017-12-13 1:55 ` [PATCH AUTOSEL for 4.14 43/60] scsi: lpfc: Fix secure firmware updates alexander.levin
2017-12-13 1:55 ` [PATCH AUTOSEL for 4.14 49/60] fm10k: ensure we process SM mbx when processing VF mbx alexander.levin
2017-12-13 1:55 ` [PATCH AUTOSEL for 4.14 48/60] ARM: exynos_defconfig: Enable UAS support for Odroid HC1 board alexander.levin
2017-12-13 1:55 ` [PATCH AUTOSEL for 4.14 50/60] ibmvnic: Set state UP alexander.levin
2017-12-13 1:55 ` [PATCH AUTOSEL for 4.14 47/60] vfio/pci: Virtualize Maximum Payload Size alexander.levin
2017-12-13 1:55 ` [PATCH AUTOSEL for 4.14 53/60] staging: greybus: light: Release memory obtained by kasprintf alexander.levin
2017-12-13 1:55 ` [PATCH AUTOSEL for 4.14 54/60] clk: sunxi-ng: sun6i: Rename HDMI DDC clock to avoid name collision alexander.levin
2017-12-13 1:55 ` [PATCH AUTOSEL for 4.14 51/60] net: ipv6: send NS for DAD when link operationally up alexander.levin
2017-12-13 1:55 ` [PATCH AUTOSEL for 4.14 52/60] RDMA/hns: Avoid NULL pointer exception alexander.levin
2017-12-13 1:55 ` [PATCH AUTOSEL for 4.14 56/60] rtc: set the alarm to the next expiring timer alexander.levin
2017-12-13 1:55 ` [PATCH AUTOSEL for 4.14 58/60] drm/vc4: Avoid using vrefresh==0 mode in DSI htotal math alexander.levin
2017-12-13 1:55 ` [PATCH AUTOSEL for 4.14 55/60] tcp: fix under-evaluated ssthresh in TCP Vegas alexander.levin
2017-12-13 1:55 ` [PATCH AUTOSEL for 4.14 57/60] cpuidle: fix broadcast control when broadcast can not be entered alexander.levin
2017-12-13 1:55 ` [PATCH AUTOSEL for 4.14 60/60] IB/opa_vnic: Properly return the total MACs in UC MAC list alexander.levin
2017-12-13 1:55 ` [PATCH AUTOSEL for 4.14 59/60] IB/opa_vnic: Properly clear Mac Table Digest alexander.levin
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20171213015455.6455-29-alexander.levin@verizon.com \
--to=alexander.levin@verizon.com \
--cc=andrew@aj.id.au \
--cc=jacek.anaszewski@gmail.com \
--cc=linux-kernel@vger.kernel.org \
--cc=stable@vger.kernel.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox
all inboxes | Powered by JetHome®