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@xxxxxxxxxxxxxxx> --- drivers/gpio/gpiolib.c | 80 ++++++++++++++++++++++++++++++++++++++++++ drivers/gpio/gpiolib.h | 3 ++ 2 files changed, 83 insertions(+) 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) -- 2.34.1