mirror of
https://github.com/hardkernel/linux.git
synced 2026-06-07 19:30:30 +09:00
pinctrl&gpio: add new interface to set pull-down/up
PD#142470: pinctrl&gpio: add new interface to set pull-down/up 1. add property file 'pull' in directory /sys/class/gpio/gpioX/ pull-up: echo up > pull pull-down: echo down > pull pull-disable: echo disable > pull 2. add new functions, as follows: + gpiod_set_pull(struct gpio_desc *desc, int value) + gpiod_set_pull_cansleep(struct gpio_desc *desc, int value) 'value' can be set: GPIOD_PULL_DIS GPIOD_PULL_DOWN GPIOD_PULL_UP Change-Id: Iba750729288651a897ebac827093ebc6c143f16b Signed-off-by: xingyu.chen <xingyu.chen@amlogic.com>
This commit is contained in:
@@ -658,6 +658,41 @@ static void meson_gpio_set(struct gpio_chip *chip, unsigned int gpio,
|
||||
value ? BIT(bit) : 0);
|
||||
}
|
||||
|
||||
static int meson_gpio_pull_set(struct gpio_chip *chip, unsigned int gpio,
|
||||
int value)
|
||||
{
|
||||
struct meson_domain *domain = to_meson_domain(chip);
|
||||
unsigned int reg, bit, pin;
|
||||
struct meson_bank *bank;
|
||||
int ret;
|
||||
|
||||
if ((value != GPIOD_PULL_DIS) && (value != GPIOD_PULL_DOWN)
|
||||
&& (value != GPIOD_PULL_UP))
|
||||
return -EINVAL;
|
||||
|
||||
pin = domain->data->pin_base + gpio;
|
||||
ret = meson_get_bank(domain, pin, &bank);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
meson_calc_reg_and_bit(bank, pin, REG_PULLEN,
|
||||
®, &bit);
|
||||
ret = regmap_update_bits(domain->reg_pullen, reg,
|
||||
BIT(bit),
|
||||
(value == GPIOD_PULL_DIS) ? 0 : BIT(bit));
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
meson_calc_reg_and_bit(bank, pin, REG_PULL, ®, &bit);
|
||||
ret = regmap_update_bits(domain->reg_pull, reg,
|
||||
BIT(bit),
|
||||
(value == GPIOD_PULL_DOWN) ? 0 : BIT(bit));
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int meson_gpio_get(struct gpio_chip *chip, unsigned int gpio)
|
||||
{
|
||||
struct meson_domain *domain = to_meson_domain(chip);
|
||||
@@ -1088,6 +1123,7 @@ static int meson_gpiolib_register(struct meson_pinctrl *pc)
|
||||
domain->chip.direction_output = meson_gpio_direction_output;
|
||||
domain->chip.get = meson_gpio_get;
|
||||
domain->chip.set = meson_gpio_set;
|
||||
domain->chip.set_pull = meson_gpio_pull_set;
|
||||
domain->chip.base = domain->data->pin_base;
|
||||
domain->chip.ngpio = domain->data->num_pins;
|
||||
domain->chip.can_sleep = false;
|
||||
|
||||
@@ -350,6 +350,30 @@ static ssize_t active_low_store(struct device *dev,
|
||||
}
|
||||
static DEVICE_ATTR_RW(active_low);
|
||||
|
||||
#ifdef CONFIG_AMLOGIC_PINCTRL
|
||||
static ssize_t pull_store(struct device *dev,
|
||||
struct device_attribute *attr, const char *buf, size_t size)
|
||||
{
|
||||
struct gpiod_data *data = dev_get_drvdata(dev);
|
||||
struct gpio_desc *desc = data->desc;
|
||||
ssize_t status;
|
||||
|
||||
mutex_lock(&data->mutex);
|
||||
if (sysfs_streq(buf, "disable"))
|
||||
status = gpiod_set_pull(desc, GPIOD_PULL_DIS);
|
||||
else if (sysfs_streq(buf, "down"))
|
||||
status = gpiod_set_pull(desc, GPIOD_PULL_DOWN);
|
||||
else if (sysfs_streq(buf, "up"))
|
||||
status = gpiod_set_pull(desc, GPIOD_PULL_UP);
|
||||
else
|
||||
status = -EINVAL;
|
||||
mutex_unlock(&data->mutex);
|
||||
|
||||
return status ? : size;
|
||||
}
|
||||
static DEVICE_ATTR_WO(pull);
|
||||
#endif
|
||||
|
||||
static umode_t gpio_is_visible(struct kobject *kobj, struct attribute *attr,
|
||||
int n)
|
||||
{
|
||||
@@ -377,6 +401,9 @@ static struct attribute *gpio_attrs[] = {
|
||||
&dev_attr_edge.attr,
|
||||
&dev_attr_value.attr,
|
||||
&dev_attr_active_low.attr,
|
||||
#ifdef CONFIG_AMLOGIC_PINCTRL
|
||||
&dev_attr_pull.attr,
|
||||
#endif
|
||||
NULL,
|
||||
};
|
||||
|
||||
|
||||
@@ -2509,6 +2509,26 @@ static void _gpiod_set_raw_value(struct gpio_desc *desc, bool value)
|
||||
chip->set(chip, gpio_chip_hwgpio(desc), value);
|
||||
}
|
||||
|
||||
#ifdef CONFIG_AMLOGIC_PINCTRL
|
||||
static int _gpiod_set_pull(struct gpio_desc *desc, int value)
|
||||
{
|
||||
struct gpio_chip *chip;
|
||||
int status = -EINVAL;
|
||||
|
||||
chip = desc->gdev->chip;
|
||||
if (!chip || !chip->set_pull) {
|
||||
gpiod_warn(desc,
|
||||
"%s: missing set_pull() operations\n",
|
||||
__func__);
|
||||
return -EIO;
|
||||
}
|
||||
if (test_bit(FLAG_REQUESTED, &desc->flags))
|
||||
status = chip->set_pull(chip, gpio_chip_hwgpio(desc), value);
|
||||
|
||||
return status;
|
||||
}
|
||||
#endif
|
||||
|
||||
/*
|
||||
* set multiple outputs on the same chip;
|
||||
* use the chip's set_multiple function if available;
|
||||
@@ -2631,6 +2651,25 @@ void gpiod_set_value(struct gpio_desc *desc, int value)
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(gpiod_set_value);
|
||||
|
||||
/**
|
||||
* gpiod_set_pull() - enable pull-down/up for the gpio, or disable.
|
||||
* @desc: gpio whose value will be set
|
||||
* @value: value to set
|
||||
*
|
||||
* This function should be called from contexts where we cannot sleep, and will
|
||||
* complain if the GPIO chip functions potentially sleep.
|
||||
*/
|
||||
#ifdef CONFIG_AMLOGIC_PINCTRL
|
||||
int gpiod_set_pull(struct gpio_desc *desc, int value)
|
||||
{
|
||||
VALIDATE_DESC(desc);
|
||||
/* Should be using gpiod_set_pull_cansleep() */
|
||||
WARN_ON(desc->gdev->chip->can_sleep);
|
||||
return _gpiod_set_pull(desc, value);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(gpiod_set_pull);
|
||||
#endif
|
||||
|
||||
/**
|
||||
* gpiod_set_raw_array_value() - assign values to an array of GPIOs
|
||||
* @array_size: number of elements in the descriptor / value arrays
|
||||
@@ -2888,6 +2927,23 @@ void gpiod_set_value_cansleep(struct gpio_desc *desc, int value)
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(gpiod_set_value_cansleep);
|
||||
|
||||
/**
|
||||
* gpiod_set_pull_cansleep() - enable pull-down/up for the gpio, or disable.
|
||||
* @desc: gpio whose value will be set
|
||||
* @value: value to set
|
||||
*
|
||||
* This function is to be called from contexts that can sleep.
|
||||
*/
|
||||
#ifdef CONFIG_AMLOGIC_PINCTRL
|
||||
int gpiod_set_pull_cansleep(struct gpio_desc *desc, int value)
|
||||
{
|
||||
might_sleep_if(extra_checks);
|
||||
VALIDATE_DESC(desc);
|
||||
return _gpiod_set_pull(desc, value);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(gpiod_set_pull_cansleep);
|
||||
#endif
|
||||
|
||||
/**
|
||||
* gpiod_set_raw_array_value_cansleep() - assign values to an array of GPIOs
|
||||
* @array_size: number of elements in the descriptor / value arrays
|
||||
|
||||
@@ -41,6 +41,14 @@ enum gpiod_flags {
|
||||
GPIOD_FLAGS_BIT_DIR_VAL,
|
||||
};
|
||||
|
||||
#ifdef CONFIG_AMLOGIC_PINCTRL
|
||||
enum gpiod_pull_type {
|
||||
GPIOD_PULL_DIS = 0,
|
||||
GPIOD_PULL_DOWN = 1,
|
||||
GPIOD_PULL_UP = 2,
|
||||
};
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_GPIOLIB
|
||||
|
||||
/* Return the number of GPIOs associated with a device / function */
|
||||
@@ -100,6 +108,9 @@ int gpiod_direction_output_raw(struct gpio_desc *desc, int value);
|
||||
/* Value get/set from non-sleeping context */
|
||||
int gpiod_get_value(const struct gpio_desc *desc);
|
||||
void gpiod_set_value(struct gpio_desc *desc, int value);
|
||||
#ifdef CONFIG_AMLOGIC_PINCTRL
|
||||
int gpiod_set_pull(struct gpio_desc *desc, int value);
|
||||
#endif
|
||||
void gpiod_set_array_value(unsigned int array_size,
|
||||
struct gpio_desc **desc_array, int *value_array);
|
||||
int gpiod_get_raw_value(const struct gpio_desc *desc);
|
||||
@@ -111,6 +122,9 @@ void gpiod_set_raw_array_value(unsigned int array_size,
|
||||
/* Value get/set from sleeping context */
|
||||
int gpiod_get_value_cansleep(const struct gpio_desc *desc);
|
||||
void gpiod_set_value_cansleep(struct gpio_desc *desc, int value);
|
||||
#ifdef CONFIG_AMLOGIC_PINCTRL
|
||||
int gpiod_set_pull_scansleep(struct gpio_desc *desc, int value);
|
||||
#endif
|
||||
void gpiod_set_array_value_cansleep(unsigned int array_size,
|
||||
struct gpio_desc **desc_array,
|
||||
int *value_array);
|
||||
@@ -323,6 +337,15 @@ static inline void gpiod_set_raw_value(struct gpio_desc *desc, int value)
|
||||
/* GPIO can never have been requested */
|
||||
WARN_ON(1);
|
||||
}
|
||||
|
||||
#ifdef CONFIG_AMLOGIC_PINCTRL
|
||||
static inline int gpiod_set_pull(struct gpio_desc *desc, int value)
|
||||
{
|
||||
WARN_ON(1);
|
||||
return -EINVAL;
|
||||
}
|
||||
#endif
|
||||
|
||||
static inline void gpiod_set_raw_array_value(unsigned int array_size,
|
||||
struct gpio_desc **desc_array,
|
||||
int *value_array)
|
||||
@@ -342,6 +365,15 @@ static inline void gpiod_set_value_cansleep(struct gpio_desc *desc, int value)
|
||||
/* GPIO can never have been requested */
|
||||
WARN_ON(1);
|
||||
}
|
||||
|
||||
#ifdef CONFIG_AMLOGIC_PINCTRL
|
||||
static inline int gpiod_set_pull_cansleep(struct gpio_desc *desc, int value)
|
||||
{
|
||||
WARN_ON(1);
|
||||
return -EINVAL;
|
||||
}
|
||||
#endif
|
||||
|
||||
static inline void gpiod_set_array_value_cansleep(unsigned int array_size,
|
||||
struct gpio_desc **desc_array,
|
||||
int *value_array)
|
||||
|
||||
@@ -58,6 +58,7 @@ enum single_ended_mode {
|
||||
* if a consumer request this. The driver may return -ENOTSUPP if e.g.
|
||||
* it supports just open drain but not open source and is called
|
||||
* with LINE_MODE_OPEN_SOURCE as mode argument.
|
||||
* @set_pull: set the current pull configuration for the GPIO.
|
||||
* @to_irq: optional hook supporting non-static gpio_to_irq() mappings;
|
||||
* implementation may not sleep
|
||||
* @dbg_show: optional routine to show contents in debugfs; default code
|
||||
@@ -156,7 +157,10 @@ struct gpio_chip {
|
||||
int (*set_single_ended)(struct gpio_chip *chip,
|
||||
unsigned offset,
|
||||
enum single_ended_mode mode);
|
||||
|
||||
#ifdef CONFIG_AMLOGIC_PINCTRL
|
||||
int (*set_pull)(struct gpio_chip *chip,
|
||||
unsigned int offset, int value);
|
||||
#endif
|
||||
int (*to_irq)(struct gpio_chip *chip,
|
||||
unsigned offset);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user