diff mbox series

[RFC,2/3] gpiolib: Add support for optional ramp-up delays

Message ID 20221212103525.231298-3-alexander.stein@ew.tq-group.com
State New
Headers show
Series gpiolib: ramp-up delay support | expand

Commit Message

Alexander Stein Dec. 12, 2022, 10:35 a.m. UTC
These delays are added when specified per GPIO line and GPIO is set
to high level, including any active low flag.

Signed-off-by: Alexander Stein <alexander.stein@ew.tq-group.com>
---
 drivers/gpio/gpiolib.c | 80 ++++++++++++++++++++++++++++++++++++++++++
 drivers/gpio/gpiolib.h |  3 ++
 2 files changed, 83 insertions(+)

Comments

Linus Walleij Dec. 13, 2022, 9:11 a.m. UTC | #1
On Mon, Dec 12, 2022 at 11:35 AM Alexander Stein
<alexander.stein@ew.tq-group.com> wrote:

> These delays are added when specified per GPIO line and GPIO is set
> to high level, including any active low flag.
>
> Signed-off-by: Alexander Stein <alexander.stein@ew.tq-group.com>

As stated in the binding patch this can be considered as a global ramp-up
for the entire chip, certainly there are not different driver stages on the
different outputs (in that case it would be a pin control driver BTW).

This is created to swipe the details of random board electronics under the
carpet and this doesn't work because we already have consumers
such as regulators specifying ramp-up on the consumer side.

Yours,
Linus Walleij
diff mbox series

Patch

diff --git a/drivers/gpio/gpiolib.c b/drivers/gpio/gpiolib.c
index 5a66d9616d7cc..5848caf0b1e12 100644
--- a/drivers/gpio/gpiolib.c
+++ b/drivers/gpio/gpiolib.c
@@ -20,6 +20,7 @@ 
 #include <linux/pinctrl/consumer.h>
 #include <linux/fs.h>
 #include <linux/compat.h>
+#include <linux/delay.h>
 #include <linux/file.h>
 #include <uapi/linux/gpio.h>
 
@@ -432,6 +433,73 @@  static int devprop_gpiochip_set_names(struct gpio_chip *chip)
 	return 0;
 }
 
+/*
+ * devprop_gpiochip_set_delays - Set GPIO line delays using device properties
+ * @chip: GPIO chip whose delays should be set, if possible
+ *
+ * Looks for device property "gpio-ramp-up-delays-us" and if it exists assigns
+ * GPIO delays for the chip.
+ */
+static int devprop_gpiochip_set_delays(struct gpio_chip *chip)
+{
+	struct gpio_device *gdev = chip->gpiodev;
+	struct device *dev = &gdev->dev;
+	u32 *delays;
+	int ret, i;
+	int count;
+
+	count = device_property_count_u32(dev, "gpio-ramp-up-delays-us");
+	if (count < 0)
+		return 0;
+
+	/*
+	 * When offset is set in the driver side we assume the driver internally
+	 * is using more than one gpiochip per the same device. We have to stop
+	 * setting delays if the specified ones with 'gpio-ramp-up-delays-us'
+	 * are less than the offset in the device itself. This means all the
+	 * lines are not present for every single pin within all the internal
+	 * gpiochips.
+	 */
+	if (count <= chip->offset) {
+		dev_warn(dev, "gpio-ramp-up-delays-us too short (length %d), cannot map delays for the gpiochip at offset %u\n",
+			 count, chip->offset);
+		return 0;
+	}
+
+	delays = kcalloc(count, sizeof(*delays), GFP_KERNEL);
+	if (!delays)
+		return -ENOMEM;
+
+	ret = device_property_read_u32_array(dev, "gpio-ramp-up-delays-us",
+					     delays, count);
+	if (ret < 0) {
+		dev_warn(dev, "failed to read GPIO rampup delays: %d\n", ret);
+		kfree(delays);
+		return ret;
+	}
+
+	/*
+	 * When more than one gpiochip per device is used, 'count' can
+	 * contain at most number gpiochips x chip->ngpio. We have to
+	 * correctly distribute all defined lines taking into account
+	 * chip->offset as starting point from where we will assign
+	 * the delays to pins from the 'delays' array. Since property
+	 * 'gpio-ramp-up-delays-us' cannot contains gaps, we have to be sure
+	 * we only assign those pins that really exists since chip->ngpio
+	 * can be different of the chip->offset.
+	 */
+	count = (count > chip->offset) ? count - chip->offset : count;
+	if (count > chip->ngpio)
+		count = chip->ngpio;
+
+	for (i = 0; i < count; i++)
+		gdev->descs[i].ramp_up_delay_us = delays[chip->offset + i];
+
+	kfree(delays);
+
+	return 0;
+}
+
 static unsigned long *gpiochip_allocate_mask(struct gpio_chip *gc)
 {
 	unsigned long *p;
@@ -806,6 +874,9 @@  int gpiochip_add_data_with_key(struct gpio_chip *gc, void *data,
 			goto err_remove_from_list;
 	}
 	ret = devprop_gpiochip_set_names(gc);
+	if (ret)
+		goto err_remove_from_list;
+	ret = devprop_gpiochip_set_delays(gc);
 	if (ret)
 		goto err_remove_from_list;
 
@@ -2962,6 +3033,9 @@  static void gpio_set_open_drain_value_commit(struct gpio_desc *desc, bool value)
 		gpiod_err(desc,
 			  "%s: Error in set_value for open drain err %d\n",
 			  __func__, ret);
+
+	if (desc->ramp_up_delay_us && value)
+		fsleep(desc->ramp_up_delay_us);
 }
 
 /*
@@ -2987,6 +3061,9 @@  static void gpio_set_open_source_value_commit(struct gpio_desc *desc, bool value
 		gpiod_err(desc,
 			  "%s: Error in set_value for open source err %d\n",
 			  __func__, ret);
+
+	if (desc->ramp_up_delay_us && value)
+		fsleep(desc->ramp_up_delay_us);
 }
 
 static void gpiod_set_raw_value_commit(struct gpio_desc *desc, bool value)
@@ -2996,6 +3073,9 @@  static void gpiod_set_raw_value_commit(struct gpio_desc *desc, bool value)
 	gc = desc->gdev->chip;
 	trace_gpio_value(desc_to_gpio(desc), 0, value);
 	gc->set(gc, gpio_chip_hwgpio(desc), value);
+
+	if (desc->ramp_up_delay_us && value)
+		fsleep(desc->ramp_up_delay_us);
 }
 
 /*
diff --git a/drivers/gpio/gpiolib.h b/drivers/gpio/gpiolib.h
index b3c2db6eba80c..3d7e5781e00d2 100644
--- a/drivers/gpio/gpiolib.h
+++ b/drivers/gpio/gpiolib.h
@@ -143,6 +143,7 @@  extern struct list_head gpio_devices;
  * @name:		Line name
  * @hog:		Pointer to the device node that hogs this line (if any)
  * @debounce_period_us:	Debounce period in microseconds
+ * @ramp_up_delay_us:	Enable propagation delay in microseconds
  *
  * These are obtained using gpiod_get() and are preferable to the old
  * integer-based handles.
@@ -184,6 +185,8 @@  struct gpio_desc {
 	/* debounce period in microseconds */
 	unsigned int		debounce_period_us;
 #endif
+	/* enable propagation delay in microseconds */
+	unsigned int		ramp_up_delay_us;
 };
 
 #define gpiod_not_found(desc)		(IS_ERR(desc) && PTR_ERR(desc) == -ENOENT)