mirror of
https://github.com/torvalds/linux.git
synced 2026-06-02 11:33:28 +02:00
Immutable branch for the pinctrl tree to pull from
Refactor the gpio-aggregator module as a prerequisite for merging the pin controller driver for AAEON UP boards. -----BEGIN PGP SIGNATURE----- iQIzBAABCgAdFiEEFp3rbAvDxGAT0sefEacuoBRx13IFAmiZ9HMACgkQEacuoBRx 13LFaA/+JqO0ZXgDfdMGHk28xSMv8aTQ/ZuGOZyPdqdurDGtH0eYL52xVQYMpQ9p qUNVgpNzs1QabvCdlZfmnPWwfmGdHdyjWfgJ7VLTu8QWMXw6XAnrUXnm5ubhFFdD IRTzx/E0PdtmU1O67YfuGgsODIxRCBx1iJb0fufYbavMj6gJQdDNdVVsOWox1Rdb TxRVzVenOwxzbvBGJNkA7pbopLNM3yOzILo0hs0qbd05m102pzdaUpH8dpQR06eD InfNnGGT5ED1gBGKk7+Jhpmnhg6ltMFnHphzsUbgaMkVY/qRoNAzIZjz+4MUbIk5 8k9dy8KMgvOLTz2FZRJp5ZAlcUPTfhAgJQYsnT+b2+RNUcDYFYeDNWCV5HvEbGke F3T11u93I2A4+SgssSUMbUTRcpZDFaS7JaTu2H652IRiquNeqDpzV0tPoAXHx/DP sCrA8YQAUmb9mQivFFgK6dlxcnugqXeM3LQHhgqoMNmItzmJjF/tKSEsApiDO/vC gdQ+MQqXLHXSKndRqOp8kzU205XauZnkdVWCkJKp0HiUSrB1sgSwgcig6DIyI3ET H7j/yrS+WVayGRIVFqt/J5g1+uBmSvnXbFLjeggoQB9gYcdZB266Qef+NxjbKJnT Fnp+/0VFuBW5AY4KWGpia6QxgUgYyPFmCllGeK5Ll6FD0TvIZnA= =lL6Q -----END PGP SIGNATURE----- Merge tag 'gpio-aggregator-refactoring-for-v6.18-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/brgl/linux into devel Immutable branch for the pinctrl tree to pull from Refactor the gpio-aggregator module as a prerequisite for merging the pin controller driver for AAEON UP boards. Signed-off-by: Linus Walleij <linus.walleij@linaro.org>
This commit is contained in:
commit
6b70896bbd
|
|
@ -12,6 +12,7 @@
|
|||
#include <linux/configfs.h>
|
||||
#include <linux/ctype.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/export.h>
|
||||
#include <linux/idr.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/list.h>
|
||||
|
|
@ -28,6 +29,7 @@
|
|||
|
||||
#include <linux/gpio/consumer.h>
|
||||
#include <linux/gpio/driver.h>
|
||||
#include <linux/gpio/forwarder.h>
|
||||
#include <linux/gpio/machine.h>
|
||||
|
||||
#include "dev-sync-probe.h"
|
||||
|
|
@ -244,6 +246,8 @@ struct gpiochip_fwd {
|
|||
spinlock_t slock; /* protects tmp[] if !can_sleep */
|
||||
};
|
||||
struct gpiochip_fwd_timing *delay_timings;
|
||||
void *data;
|
||||
unsigned long *valid_mask;
|
||||
unsigned long tmp[]; /* values and descs for multiple ops */
|
||||
};
|
||||
|
||||
|
|
@ -252,10 +256,24 @@ struct gpiochip_fwd {
|
|||
|
||||
#define fwd_tmp_size(ngpios) (BITS_TO_LONGS((ngpios)) + (ngpios))
|
||||
|
||||
static int gpio_fwd_request(struct gpio_chip *chip, unsigned int offset)
|
||||
{
|
||||
struct gpiochip_fwd *fwd = gpiochip_get_data(chip);
|
||||
|
||||
return test_bit(offset, fwd->valid_mask) ? 0 : -ENODEV;
|
||||
}
|
||||
|
||||
static int gpio_fwd_get_direction(struct gpio_chip *chip, unsigned int offset)
|
||||
{
|
||||
struct gpiochip_fwd *fwd = gpiochip_get_data(chip);
|
||||
|
||||
/*
|
||||
* get_direction() is called during gpiochip registration, return
|
||||
* -ENODEV if there is no GPIO desc for the line.
|
||||
*/
|
||||
if (!test_bit(offset, fwd->valid_mask))
|
||||
return -ENODEV;
|
||||
|
||||
return gpiod_get_direction(fwd->descs[offset]);
|
||||
}
|
||||
|
||||
|
|
@ -453,10 +471,11 @@ static int gpiochip_fwd_delay_of_xlate(struct gpio_chip *chip,
|
|||
return line;
|
||||
}
|
||||
|
||||
static int gpiochip_fwd_setup_delay_line(struct device *dev, struct gpio_chip *chip,
|
||||
struct gpiochip_fwd *fwd)
|
||||
static int gpiochip_fwd_setup_delay_line(struct gpiochip_fwd *fwd)
|
||||
{
|
||||
fwd->delay_timings = devm_kcalloc(dev, chip->ngpio,
|
||||
struct gpio_chip *chip = &fwd->chip;
|
||||
|
||||
fwd->delay_timings = devm_kcalloc(chip->parent, chip->ngpio,
|
||||
sizeof(*fwd->delay_timings),
|
||||
GFP_KERNEL);
|
||||
if (!fwd->delay_timings)
|
||||
|
|
@ -468,20 +487,333 @@ static int gpiochip_fwd_setup_delay_line(struct device *dev, struct gpio_chip *c
|
|||
return 0;
|
||||
}
|
||||
#else
|
||||
static int gpiochip_fwd_setup_delay_line(struct device *dev, struct gpio_chip *chip,
|
||||
struct gpiochip_fwd *fwd)
|
||||
static int gpiochip_fwd_setup_delay_line(struct gpiochip_fwd *fwd)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
#endif /* !CONFIG_OF_GPIO */
|
||||
|
||||
/**
|
||||
* gpiochip_fwd_get_gpiochip - Get the GPIO chip for the GPIO forwarder
|
||||
* @fwd: GPIO forwarder
|
||||
*
|
||||
* Returns: The GPIO chip for the GPIO forwarder
|
||||
*/
|
||||
struct gpio_chip *gpiochip_fwd_get_gpiochip(struct gpiochip_fwd *fwd)
|
||||
{
|
||||
return &fwd->chip;
|
||||
}
|
||||
EXPORT_SYMBOL_NS_GPL(gpiochip_fwd_get_gpiochip, "GPIO_FORWARDER");
|
||||
|
||||
/**
|
||||
* gpiochip_fwd_get_data - Get driver-private data for the GPIO forwarder
|
||||
* @fwd: GPIO forwarder
|
||||
*
|
||||
* Returns: The driver-private data for the GPIO forwarder
|
||||
*/
|
||||
void *gpiochip_fwd_get_data(struct gpiochip_fwd *fwd)
|
||||
{
|
||||
return fwd->data;
|
||||
}
|
||||
EXPORT_SYMBOL_NS_GPL(gpiochip_fwd_get_data, "GPIO_FORWARDER");
|
||||
|
||||
/**
|
||||
* gpiochip_fwd_gpio_request - Request a line of the GPIO forwarder
|
||||
* @fwd: GPIO forwarder
|
||||
* @offset: the offset of the line to request
|
||||
*
|
||||
* Returns: 0 on success, or negative errno on failure.
|
||||
*/
|
||||
int gpiochip_fwd_gpio_request(struct gpiochip_fwd *fwd, unsigned int offset)
|
||||
{
|
||||
struct gpio_chip *gc = gpiochip_fwd_get_gpiochip(fwd);
|
||||
|
||||
return gpio_fwd_request(gc, offset);
|
||||
}
|
||||
EXPORT_SYMBOL_NS_GPL(gpiochip_fwd_gpio_request, "GPIO_FORWARDER");
|
||||
|
||||
/**
|
||||
* gpiochip_fwd_gpio_get_direction - Return the current direction of a GPIO forwarder line
|
||||
* @fwd: GPIO forwarder
|
||||
* @offset: the offset of the line
|
||||
*
|
||||
* Returns: 0 for output, 1 for input, or an error code in case of error.
|
||||
*/
|
||||
int gpiochip_fwd_gpio_get_direction(struct gpiochip_fwd *fwd, unsigned int offset)
|
||||
{
|
||||
struct gpio_chip *gc = gpiochip_fwd_get_gpiochip(fwd);
|
||||
|
||||
return gpio_fwd_get_direction(gc, offset);
|
||||
}
|
||||
EXPORT_SYMBOL_NS_GPL(gpiochip_fwd_gpio_get_direction, "GPIO_FORWARDER");
|
||||
|
||||
/**
|
||||
* gpiochip_fwd_gpio_direction_output - Set a GPIO forwarder line direction to
|
||||
* output
|
||||
* @fwd: GPIO forwarder
|
||||
* @offset: the offset of the line
|
||||
* @value: value to set
|
||||
*
|
||||
* Returns: 0 on success, or negative errno on failure.
|
||||
*/
|
||||
int gpiochip_fwd_gpio_direction_output(struct gpiochip_fwd *fwd, unsigned int offset,
|
||||
int value)
|
||||
{
|
||||
struct gpio_chip *gc = gpiochip_fwd_get_gpiochip(fwd);
|
||||
|
||||
return gpio_fwd_direction_output(gc, offset, value);
|
||||
}
|
||||
EXPORT_SYMBOL_NS_GPL(gpiochip_fwd_gpio_direction_output, "GPIO_FORWARDER");
|
||||
|
||||
/**
|
||||
* gpiochip_fwd_gpio_direction_input - Set a GPIO forwarder line direction to input
|
||||
* @fwd: GPIO forwarder
|
||||
* @offset: the offset of the line
|
||||
*
|
||||
* Returns: 0 on success, or negative errno on failure.
|
||||
*/
|
||||
int gpiochip_fwd_gpio_direction_input(struct gpiochip_fwd *fwd, unsigned int offset)
|
||||
{
|
||||
struct gpio_chip *gc = gpiochip_fwd_get_gpiochip(fwd);
|
||||
|
||||
return gpio_fwd_direction_input(gc, offset);
|
||||
}
|
||||
EXPORT_SYMBOL_NS_GPL(gpiochip_fwd_gpio_direction_input, "GPIO_FORWARDER");
|
||||
|
||||
/**
|
||||
* gpiochip_fwd_gpio_get - Return a GPIO forwarder line's value
|
||||
* @fwd: GPIO forwarder
|
||||
* @offset: the offset of the line
|
||||
*
|
||||
* Returns: The GPIO's logical value, i.e. taking the ACTIVE_LOW status into
|
||||
* account, or negative errno on failure.
|
||||
*/
|
||||
int gpiochip_fwd_gpio_get(struct gpiochip_fwd *fwd, unsigned int offset)
|
||||
{
|
||||
struct gpio_chip *gc = gpiochip_fwd_get_gpiochip(fwd);
|
||||
|
||||
return gpio_fwd_get(gc, offset);
|
||||
}
|
||||
EXPORT_SYMBOL_NS_GPL(gpiochip_fwd_gpio_get, "GPIO_FORWARDER");
|
||||
|
||||
/**
|
||||
* gpiochip_fwd_gpio_get_multiple - Get values for multiple GPIO forwarder lines
|
||||
* @fwd: GPIO forwarder
|
||||
* @mask: bit mask array; one bit per line; BITS_PER_LONG bits per word defines
|
||||
* which lines are to be read
|
||||
* @bits: bit value array; one bit per line; BITS_PER_LONG bits per word will
|
||||
* contains the read values for the lines specified by mask
|
||||
*
|
||||
* Returns: 0 on success, or negative errno on failure.
|
||||
*/
|
||||
int gpiochip_fwd_gpio_get_multiple(struct gpiochip_fwd *fwd, unsigned long *mask,
|
||||
unsigned long *bits)
|
||||
{
|
||||
struct gpio_chip *gc = gpiochip_fwd_get_gpiochip(fwd);
|
||||
|
||||
return gpio_fwd_get_multiple_locked(gc, mask, bits);
|
||||
}
|
||||
EXPORT_SYMBOL_NS_GPL(gpiochip_fwd_gpio_get_multiple, "GPIO_FORWARDER");
|
||||
|
||||
/**
|
||||
* gpiochip_fwd_gpio_set - Assign value to a GPIO forwarder line.
|
||||
* @fwd: GPIO forwarder
|
||||
* @offset: the offset of the line
|
||||
* @value: value to set
|
||||
*
|
||||
* Returns: 0 on success, or negative errno on failure.
|
||||
*/
|
||||
int gpiochip_fwd_gpio_set(struct gpiochip_fwd *fwd, unsigned int offset, int value)
|
||||
{
|
||||
struct gpio_chip *gc = gpiochip_fwd_get_gpiochip(fwd);
|
||||
|
||||
return gpio_fwd_set(gc, offset, value);
|
||||
}
|
||||
EXPORT_SYMBOL_NS_GPL(gpiochip_fwd_gpio_set, "GPIO_FORWARDER");
|
||||
|
||||
/**
|
||||
* gpiochip_fwd_gpio_set_multiple - Assign values to multiple GPIO forwarder lines
|
||||
* @fwd: GPIO forwarder
|
||||
* @mask: bit mask array; one bit per output; BITS_PER_LONG bits per word
|
||||
* defines which outputs are to be changed
|
||||
* @bits: bit value array; one bit per output; BITS_PER_LONG bits per word
|
||||
* defines the values the outputs specified by mask are to be set to
|
||||
*
|
||||
* Returns: 0 on success, or negative errno on failure.
|
||||
*/
|
||||
int gpiochip_fwd_gpio_set_multiple(struct gpiochip_fwd *fwd, unsigned long *mask,
|
||||
unsigned long *bits)
|
||||
{
|
||||
struct gpio_chip *gc = gpiochip_fwd_get_gpiochip(fwd);
|
||||
|
||||
return gpio_fwd_set_multiple_locked(gc, mask, bits);
|
||||
}
|
||||
EXPORT_SYMBOL_NS_GPL(gpiochip_fwd_gpio_set_multiple, "GPIO_FORWARDER");
|
||||
|
||||
/**
|
||||
* gpiochip_fwd_gpio_set_config - Set @config for a GPIO forwarder line
|
||||
* @fwd: GPIO forwarder
|
||||
* @offset: the offset of the line
|
||||
* @config: Same packed config format as generic pinconf
|
||||
*
|
||||
* Returns: 0 on success, %-ENOTSUPP if the controller doesn't support setting
|
||||
* the configuration.
|
||||
*/
|
||||
int gpiochip_fwd_gpio_set_config(struct gpiochip_fwd *fwd, unsigned int offset,
|
||||
unsigned long config)
|
||||
{
|
||||
struct gpio_chip *gc = gpiochip_fwd_get_gpiochip(fwd);
|
||||
|
||||
return gpio_fwd_set_config(gc, offset, config);
|
||||
}
|
||||
EXPORT_SYMBOL_NS_GPL(gpiochip_fwd_gpio_set_config, "GPIO_FORWARDER");
|
||||
|
||||
/**
|
||||
* gpiochip_fwd_gpio_to_irq - Return the IRQ corresponding to a GPIO forwarder line
|
||||
* @fwd: GPIO forwarder
|
||||
* @offset: the offset of the line
|
||||
*
|
||||
* Returns: The Linux IRQ corresponding to the passed line, or an error code in
|
||||
* case of error.
|
||||
*/
|
||||
int gpiochip_fwd_gpio_to_irq(struct gpiochip_fwd *fwd, unsigned int offset)
|
||||
{
|
||||
struct gpio_chip *gc = gpiochip_fwd_get_gpiochip(fwd);
|
||||
|
||||
return gpio_fwd_to_irq(gc, offset);
|
||||
}
|
||||
EXPORT_SYMBOL_NS_GPL(gpiochip_fwd_gpio_to_irq, "GPIO_FORWARDER");
|
||||
|
||||
/**
|
||||
* devm_gpiochip_fwd_alloc - Allocate and initialize a new GPIO forwarder
|
||||
* @dev: Parent device pointer
|
||||
* @ngpios: Number of GPIOs in the forwarder
|
||||
*
|
||||
* Returns: An opaque object pointer, or an ERR_PTR()-encoded negative error
|
||||
* code on failure.
|
||||
*/
|
||||
struct gpiochip_fwd *devm_gpiochip_fwd_alloc(struct device *dev,
|
||||
unsigned int ngpios)
|
||||
{
|
||||
struct gpiochip_fwd *fwd;
|
||||
struct gpio_chip *chip;
|
||||
|
||||
fwd = devm_kzalloc(dev, struct_size(fwd, tmp, fwd_tmp_size(ngpios)), GFP_KERNEL);
|
||||
if (!fwd)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
fwd->descs = devm_kcalloc(dev, ngpios, sizeof(*fwd->descs), GFP_KERNEL);
|
||||
if (!fwd->descs)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
fwd->valid_mask = devm_bitmap_zalloc(dev, ngpios, GFP_KERNEL);
|
||||
if (!fwd->valid_mask)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
chip = &fwd->chip;
|
||||
|
||||
chip->label = dev_name(dev);
|
||||
chip->parent = dev;
|
||||
chip->owner = THIS_MODULE;
|
||||
chip->request = gpio_fwd_request;
|
||||
chip->get_direction = gpio_fwd_get_direction;
|
||||
chip->direction_input = gpio_fwd_direction_input;
|
||||
chip->direction_output = gpio_fwd_direction_output;
|
||||
chip->get = gpio_fwd_get;
|
||||
chip->get_multiple = gpio_fwd_get_multiple_locked;
|
||||
chip->set = gpio_fwd_set;
|
||||
chip->set_multiple = gpio_fwd_set_multiple_locked;
|
||||
chip->to_irq = gpio_fwd_to_irq;
|
||||
chip->base = -1;
|
||||
chip->ngpio = ngpios;
|
||||
|
||||
return fwd;
|
||||
}
|
||||
EXPORT_SYMBOL_NS_GPL(devm_gpiochip_fwd_alloc, "GPIO_FORWARDER");
|
||||
|
||||
/**
|
||||
* gpiochip_fwd_desc_add - Add a GPIO desc in the forwarder
|
||||
* @fwd: GPIO forwarder
|
||||
* @desc: GPIO descriptor to register
|
||||
* @offset: offset for the GPIO in the forwarder
|
||||
*
|
||||
* Returns: 0 on success, or negative errno on failure.
|
||||
*/
|
||||
int gpiochip_fwd_desc_add(struct gpiochip_fwd *fwd, struct gpio_desc *desc,
|
||||
unsigned int offset)
|
||||
{
|
||||
struct gpio_chip *chip = &fwd->chip;
|
||||
|
||||
if (offset > chip->ngpio)
|
||||
return -EINVAL;
|
||||
|
||||
if (test_and_set_bit(offset, fwd->valid_mask))
|
||||
return -EEXIST;
|
||||
|
||||
/*
|
||||
* If any of the GPIO lines are sleeping, then the entire forwarder
|
||||
* will be sleeping.
|
||||
*/
|
||||
if (gpiod_cansleep(desc))
|
||||
chip->can_sleep = true;
|
||||
|
||||
fwd->descs[offset] = desc;
|
||||
|
||||
dev_dbg(chip->parent, "%u => gpio %d irq %d\n", offset,
|
||||
desc_to_gpio(desc), gpiod_to_irq(desc));
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL_NS_GPL(gpiochip_fwd_desc_add, "GPIO_FORWARDER");
|
||||
|
||||
/**
|
||||
* gpiochip_fwd_desc_free - Remove a GPIO desc from the forwarder
|
||||
* @fwd: GPIO forwarder
|
||||
* @offset: offset of GPIO desc to remove
|
||||
*/
|
||||
void gpiochip_fwd_desc_free(struct gpiochip_fwd *fwd, unsigned int offset)
|
||||
{
|
||||
if (test_and_clear_bit(offset, fwd->valid_mask))
|
||||
gpiod_put(fwd->descs[offset]);
|
||||
}
|
||||
EXPORT_SYMBOL_NS_GPL(gpiochip_fwd_desc_free, "GPIO_FORWARDER");
|
||||
|
||||
/**
|
||||
* gpiochip_fwd_register - Register a GPIO forwarder
|
||||
* @fwd: GPIO forwarder
|
||||
* @data: driver-private data associated with this forwarder
|
||||
*
|
||||
* Returns: 0 on success, or negative errno on failure.
|
||||
*/
|
||||
int gpiochip_fwd_register(struct gpiochip_fwd *fwd, void *data)
|
||||
{
|
||||
struct gpio_chip *chip = &fwd->chip;
|
||||
|
||||
/*
|
||||
* Some gpio_desc were not registered. They will be registered at runtime
|
||||
* but we have to suppose they can sleep.
|
||||
*/
|
||||
if (!bitmap_full(fwd->valid_mask, chip->ngpio))
|
||||
chip->can_sleep = true;
|
||||
|
||||
if (chip->can_sleep)
|
||||
mutex_init(&fwd->mlock);
|
||||
else
|
||||
spin_lock_init(&fwd->slock);
|
||||
|
||||
fwd->data = data;
|
||||
|
||||
return devm_gpiochip_add_data(chip->parent, chip, fwd);
|
||||
}
|
||||
EXPORT_SYMBOL_NS_GPL(gpiochip_fwd_register, "GPIO_FORWARDER");
|
||||
|
||||
/**
|
||||
* gpiochip_fwd_create() - Create a new GPIO forwarder
|
||||
* @dev: Parent device pointer
|
||||
* @ngpios: Number of GPIOs in the forwarder.
|
||||
* @descs: Array containing the GPIO descriptors to forward to.
|
||||
* This array must contain @ngpios entries, and must not be deallocated
|
||||
* before the forwarder has been destroyed again.
|
||||
* This array must contain @ngpios entries, and can be deallocated
|
||||
* as the forwarder has its own array.
|
||||
* @features: Bitwise ORed features as defined with FWD_FEATURE_*.
|
||||
*
|
||||
* This function creates a new gpiochip, which forwards all GPIO operations to
|
||||
|
|
@ -495,64 +827,27 @@ static struct gpiochip_fwd *gpiochip_fwd_create(struct device *dev,
|
|||
struct gpio_desc *descs[],
|
||||
unsigned long features)
|
||||
{
|
||||
const char *label = dev_name(dev);
|
||||
struct gpiochip_fwd *fwd;
|
||||
struct gpio_chip *chip;
|
||||
unsigned int i;
|
||||
int error;
|
||||
|
||||
fwd = devm_kzalloc(dev, struct_size(fwd, tmp, fwd_tmp_size(ngpios)),
|
||||
GFP_KERNEL);
|
||||
if (!fwd)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
fwd = devm_gpiochip_fwd_alloc(dev, ngpios);
|
||||
if (IS_ERR(fwd))
|
||||
return fwd;
|
||||
|
||||
chip = &fwd->chip;
|
||||
|
||||
/*
|
||||
* If any of the GPIO lines are sleeping, then the entire forwarder
|
||||
* will be sleeping.
|
||||
* If any of the chips support .set_config(), then the forwarder will
|
||||
* support setting configs.
|
||||
*/
|
||||
for (i = 0; i < ngpios; i++) {
|
||||
struct gpio_chip *parent = gpiod_to_chip(descs[i]);
|
||||
|
||||
dev_dbg(dev, "%u => gpio %d irq %d\n", i,
|
||||
desc_to_gpio(descs[i]), gpiod_to_irq(descs[i]));
|
||||
|
||||
if (gpiod_cansleep(descs[i]))
|
||||
chip->can_sleep = true;
|
||||
if (parent && parent->set_config)
|
||||
chip->set_config = gpio_fwd_set_config;
|
||||
}
|
||||
|
||||
chip->label = label;
|
||||
chip->parent = dev;
|
||||
chip->owner = THIS_MODULE;
|
||||
chip->get_direction = gpio_fwd_get_direction;
|
||||
chip->direction_input = gpio_fwd_direction_input;
|
||||
chip->direction_output = gpio_fwd_direction_output;
|
||||
chip->get = gpio_fwd_get;
|
||||
chip->get_multiple = gpio_fwd_get_multiple_locked;
|
||||
chip->set = gpio_fwd_set;
|
||||
chip->set_multiple = gpio_fwd_set_multiple_locked;
|
||||
chip->to_irq = gpio_fwd_to_irq;
|
||||
chip->base = -1;
|
||||
chip->ngpio = ngpios;
|
||||
fwd->descs = descs;
|
||||
|
||||
if (chip->can_sleep)
|
||||
mutex_init(&fwd->mlock);
|
||||
else
|
||||
spin_lock_init(&fwd->slock);
|
||||
|
||||
if (features & FWD_FEATURE_DELAY) {
|
||||
error = gpiochip_fwd_setup_delay_line(dev, chip, fwd);
|
||||
error = gpiochip_fwd_desc_add(fwd, descs[i], i);
|
||||
if (error)
|
||||
return ERR_PTR(error);
|
||||
}
|
||||
|
||||
error = devm_gpiochip_add_data(dev, chip, fwd);
|
||||
if (features & FWD_FEATURE_DELAY) {
|
||||
error = gpiochip_fwd_setup_delay_line(fwd);
|
||||
if (error)
|
||||
return ERR_PTR(error);
|
||||
}
|
||||
|
||||
error = gpiochip_fwd_register(fwd, NULL);
|
||||
if (error)
|
||||
return ERR_PTR(error);
|
||||
|
||||
|
|
@ -1334,6 +1629,7 @@ static int gpio_aggregator_probe(struct platform_device *pdev)
|
|||
return PTR_ERR(fwd);
|
||||
|
||||
platform_set_drvdata(pdev, fwd);
|
||||
devm_kfree(dev, descs);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -2349,11 +2349,13 @@ int gpiochip_add_pingroup_range(struct gpio_chip *gc,
|
|||
EXPORT_SYMBOL_GPL(gpiochip_add_pingroup_range);
|
||||
|
||||
/**
|
||||
* gpiochip_add_pin_range() - add a range for GPIO <-> pin mapping
|
||||
* gpiochip_add_pin_range_with_pins() - add a range for GPIO <-> pin mapping
|
||||
* @gc: the gpiochip to add the range for
|
||||
* @pinctl_name: the dev_name() of the pin controller to map to
|
||||
* @gpio_offset: the start offset in the current gpio_chip number space
|
||||
* @pin_offset: the start offset in the pin controller number space
|
||||
* @pins: the list of non consecutive pins to accumulate in this range (if not
|
||||
* NULL, pin_offset is ignored by pinctrl core)
|
||||
* @npins: the number of pins from the offset of each pin space (GPIO and
|
||||
* pin controller) to accumulate in this range
|
||||
*
|
||||
|
|
@ -2365,9 +2367,12 @@ EXPORT_SYMBOL_GPL(gpiochip_add_pingroup_range);
|
|||
* Returns:
|
||||
* 0 on success, or a negative errno on failure.
|
||||
*/
|
||||
int gpiochip_add_pin_range(struct gpio_chip *gc, const char *pinctl_name,
|
||||
unsigned int gpio_offset, unsigned int pin_offset,
|
||||
unsigned int npins)
|
||||
int gpiochip_add_pin_range_with_pins(struct gpio_chip *gc,
|
||||
const char *pinctl_name,
|
||||
unsigned int gpio_offset,
|
||||
unsigned int pin_offset,
|
||||
unsigned int const *pins,
|
||||
unsigned int npins)
|
||||
{
|
||||
struct gpio_pin_range *pin_range;
|
||||
struct gpio_device *gdev = gc->gpiodev;
|
||||
|
|
@ -2385,6 +2390,7 @@ int gpiochip_add_pin_range(struct gpio_chip *gc, const char *pinctl_name,
|
|||
pin_range->range.name = gc->label;
|
||||
pin_range->range.base = gdev->base + gpio_offset;
|
||||
pin_range->range.pin_base = pin_offset;
|
||||
pin_range->range.pins = pins;
|
||||
pin_range->range.npins = npins;
|
||||
pin_range->pctldev = pinctrl_find_and_add_gpio_range(pinctl_name,
|
||||
&pin_range->range);
|
||||
|
|
@ -2394,16 +2400,21 @@ int gpiochip_add_pin_range(struct gpio_chip *gc, const char *pinctl_name,
|
|||
kfree(pin_range);
|
||||
return ret;
|
||||
}
|
||||
chip_dbg(gc, "created GPIO range %d->%d ==> %s PIN %d->%d\n",
|
||||
gpio_offset, gpio_offset + npins - 1,
|
||||
pinctl_name,
|
||||
pin_offset, pin_offset + npins - 1);
|
||||
if (pin_range->range.pins)
|
||||
chip_dbg(gc, "created GPIO range %d->%d ==> %s %d sparse PIN range { %d, ... }",
|
||||
gpio_offset, gpio_offset + npins - 1,
|
||||
pinctl_name, npins, pins[0]);
|
||||
else
|
||||
chip_dbg(gc, "created GPIO range %d->%d ==> %s PIN %d->%d\n",
|
||||
gpio_offset, gpio_offset + npins - 1,
|
||||
pinctl_name,
|
||||
pin_offset, pin_offset + npins - 1);
|
||||
|
||||
list_add_tail(&pin_range->node, &gdev->pin_ranges);
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(gpiochip_add_pin_range);
|
||||
EXPORT_SYMBOL_GPL(gpiochip_add_pin_range_with_pins);
|
||||
|
||||
/**
|
||||
* gpiochip_remove_pin_ranges() - remove all the GPIO <-> pin mappings
|
||||
|
|
|
|||
|
|
@ -772,16 +772,50 @@ struct gpio_pin_range {
|
|||
|
||||
#ifdef CONFIG_PINCTRL
|
||||
|
||||
int gpiochip_add_pin_range(struct gpio_chip *gc, const char *pinctl_name,
|
||||
unsigned int gpio_offset, unsigned int pin_offset,
|
||||
unsigned int npins);
|
||||
int gpiochip_add_pin_range_with_pins(struct gpio_chip *gc,
|
||||
const char *pinctl_name,
|
||||
unsigned int gpio_offset,
|
||||
unsigned int pin_offset,
|
||||
unsigned int const *pins,
|
||||
unsigned int npins);
|
||||
int gpiochip_add_pingroup_range(struct gpio_chip *gc,
|
||||
struct pinctrl_dev *pctldev,
|
||||
unsigned int gpio_offset, const char *pin_group);
|
||||
void gpiochip_remove_pin_ranges(struct gpio_chip *gc);
|
||||
|
||||
static inline int
|
||||
gpiochip_add_pin_range(struct gpio_chip *gc,
|
||||
const char *pinctl_name,
|
||||
unsigned int gpio_offset,
|
||||
unsigned int pin_offset,
|
||||
unsigned int npins)
|
||||
{
|
||||
return gpiochip_add_pin_range_with_pins(gc, pinctl_name, gpio_offset,
|
||||
pin_offset, NULL, npins);
|
||||
}
|
||||
|
||||
static inline int
|
||||
gpiochip_add_sparse_pin_range(struct gpio_chip *gc,
|
||||
const char *pinctl_name,
|
||||
unsigned int gpio_offset,
|
||||
unsigned int const *pins,
|
||||
unsigned int npins)
|
||||
{
|
||||
return gpiochip_add_pin_range_with_pins(gc, pinctl_name, gpio_offset, 0,
|
||||
pins, npins);
|
||||
}
|
||||
#else /* ! CONFIG_PINCTRL */
|
||||
|
||||
static inline int
|
||||
gpiochip_add_pin_range_with_pins(struct gpio_chip *gc,
|
||||
const char *pinctl_name,
|
||||
unsigned int gpio_offset,
|
||||
unsigned int pin_offset,
|
||||
unsigned int npins)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline int
|
||||
gpiochip_add_pin_range(struct gpio_chip *gc, const char *pinctl_name,
|
||||
unsigned int gpio_offset, unsigned int pin_offset,
|
||||
|
|
@ -789,6 +823,17 @@ gpiochip_add_pin_range(struct gpio_chip *gc, const char *pinctl_name,
|
|||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline int
|
||||
gpiochip_add_sparse_pin_range(struct gpio_chip *gc,
|
||||
const char *pinctl_name,
|
||||
unsigned int gpio_offset,
|
||||
unsigned int const *pins,
|
||||
unsigned int npins)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline int
|
||||
gpiochip_add_pingroup_range(struct gpio_chip *gc,
|
||||
struct pinctrl_dev *pctldev,
|
||||
|
|
|
|||
41
include/linux/gpio/forwarder.h
Normal file
41
include/linux/gpio/forwarder.h
Normal file
|
|
@ -0,0 +1,41 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
#ifndef __LINUX_GPIO_FORWARDER_H
|
||||
#define __LINUX_GPIO_FORWARDER_H
|
||||
|
||||
struct gpio_desc;
|
||||
struct gpio_chip;
|
||||
struct gpiochip_fwd;
|
||||
|
||||
struct gpiochip_fwd *devm_gpiochip_fwd_alloc(struct device *dev,
|
||||
unsigned int ngpios);
|
||||
int gpiochip_fwd_desc_add(struct gpiochip_fwd *fwd,
|
||||
struct gpio_desc *desc, unsigned int offset);
|
||||
void gpiochip_fwd_desc_free(struct gpiochip_fwd *fwd, unsigned int offset);
|
||||
int gpiochip_fwd_register(struct gpiochip_fwd *fwd, void *data);
|
||||
|
||||
struct gpio_chip *gpiochip_fwd_get_gpiochip(struct gpiochip_fwd *fwd);
|
||||
|
||||
void *gpiochip_fwd_get_data(struct gpiochip_fwd *fwd);
|
||||
|
||||
int gpiochip_fwd_gpio_request(struct gpiochip_fwd *fwd, unsigned int offset);
|
||||
int gpiochip_fwd_gpio_get_direction(struct gpiochip_fwd *fwd,
|
||||
unsigned int offset);
|
||||
int gpiochip_fwd_gpio_direction_input(struct gpiochip_fwd *fwd,
|
||||
unsigned int offset);
|
||||
int gpiochip_fwd_gpio_direction_output(struct gpiochip_fwd *fwd,
|
||||
unsigned int offset,
|
||||
int value);
|
||||
int gpiochip_fwd_gpio_get(struct gpiochip_fwd *fwd, unsigned int offset);
|
||||
int gpiochip_fwd_gpio_get_multiple(struct gpiochip_fwd *fwd,
|
||||
unsigned long *mask,
|
||||
unsigned long *bits);
|
||||
int gpiochip_fwd_gpio_set(struct gpiochip_fwd *fwd, unsigned int offset,
|
||||
int value);
|
||||
int gpiochip_fwd_gpio_set_multiple(struct gpiochip_fwd *fwd,
|
||||
unsigned long *mask,
|
||||
unsigned long *bits);
|
||||
int gpiochip_fwd_gpio_set_config(struct gpiochip_fwd *fwd, unsigned int offset,
|
||||
unsigned long config);
|
||||
int gpiochip_fwd_gpio_to_irq(struct gpiochip_fwd *fwd, unsigned int offset);
|
||||
|
||||
#endif
|
||||
|
|
@ -41,6 +41,12 @@ static inline const char *str_high_low(bool v)
|
|||
}
|
||||
#define str_low_high(v) str_high_low(!(v))
|
||||
|
||||
static inline const char *str_input_output(bool v)
|
||||
{
|
||||
return v ? "input" : "output";
|
||||
}
|
||||
#define str_output_input(v) str_input_output(!(v))
|
||||
|
||||
static inline const char *str_on_off(bool v)
|
||||
{
|
||||
return v ? "on" : "off";
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user