@@ -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);
}
/*
@@ -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)
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(+)