Merge branch 'devel' into for-next
This commit is contained in:
commit
27b5ea2fcb
@ -416,7 +416,8 @@ The preferred way to set up the helpers is to fill in the
|
||||
struct gpio_irq_chip inside struct gpio_chip before adding the gpio_chip.
|
||||
If you do this, the additional irq_chip will be set up by gpiolib at the
|
||||
same time as setting up the rest of the GPIO functionality. The following
|
||||
is a typical example of a cascaded interrupt handler using gpio_irq_chip:
|
||||
is a typical example of a chained cascaded interrupt handler using
|
||||
the gpio_irq_chip:
|
||||
|
||||
.. code-block:: c
|
||||
|
||||
@ -452,7 +453,46 @@ is a typical example of a cascaded interrupt handler using gpio_irq_chip:
|
||||
|
||||
return devm_gpiochip_add_data(dev, &g->gc, g);
|
||||
|
||||
The helper support using hierarchical interrupt controllers as well.
|
||||
The helper supports using threaded interrupts as well. Then you just request
|
||||
the interrupt separately and go with it:
|
||||
|
||||
.. code-block:: c
|
||||
|
||||
/* Typical state container with dynamic irqchip */
|
||||
struct my_gpio {
|
||||
struct gpio_chip gc;
|
||||
struct irq_chip irq;
|
||||
};
|
||||
|
||||
int irq; /* from platform etc */
|
||||
struct my_gpio *g;
|
||||
struct gpio_irq_chip *girq;
|
||||
|
||||
/* Set up the irqchip dynamically */
|
||||
g->irq.name = "my_gpio_irq";
|
||||
g->irq.irq_ack = my_gpio_ack_irq;
|
||||
g->irq.irq_mask = my_gpio_mask_irq;
|
||||
g->irq.irq_unmask = my_gpio_unmask_irq;
|
||||
g->irq.irq_set_type = my_gpio_set_irq_type;
|
||||
|
||||
ret = devm_request_threaded_irq(dev, irq, NULL,
|
||||
irq_thread_fn, IRQF_ONESHOT, "my-chip", g);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
/* Get a pointer to the gpio_irq_chip */
|
||||
girq = &g->gc.irq;
|
||||
girq->chip = &g->irq;
|
||||
/* This will let us handle the parent IRQ in the driver */
|
||||
girq->parent_handler = NULL;
|
||||
girq->num_parents = 0;
|
||||
girq->parents = NULL;
|
||||
girq->default_type = IRQ_TYPE_NONE;
|
||||
girq->handler = handle_bad_irq;
|
||||
|
||||
return devm_gpiochip_add_data(dev, &g->gc, g);
|
||||
|
||||
The helper supports using hierarchical interrupt controllers as well.
|
||||
In this case the typical set-up will look like this:
|
||||
|
||||
.. code-block:: c
|
||||
@ -493,32 +533,13 @@ the parent hardware irq from a child (i.e. this gpio chip) hardware irq.
|
||||
As always it is good to look at examples in the kernel tree for advice
|
||||
on how to find the required pieces.
|
||||
|
||||
The old way of adding irqchips to gpiochips after registration is also still
|
||||
available but we try to move away from this:
|
||||
|
||||
- DEPRECATED: gpiochip_irqchip_add(): adds a chained cascaded irqchip to a
|
||||
gpiochip. It will pass the struct gpio_chip* for the chip to all IRQ
|
||||
callbacks, so the callbacks need to embed the gpio_chip in its state
|
||||
container and obtain a pointer to the container using container_of().
|
||||
(See Documentation/driver-api/driver-model/design-patterns.rst)
|
||||
|
||||
- gpiochip_irqchip_add_nested(): adds a nested cascaded irqchip to a gpiochip,
|
||||
as discussed above regarding different types of cascaded irqchips. The
|
||||
cascaded irq has to be handled by a threaded interrupt handler.
|
||||
Apart from that it works exactly like the chained irqchip.
|
||||
|
||||
- gpiochip_set_nested_irqchip(): sets up a nested cascaded irq handler for a
|
||||
gpio_chip from a parent IRQ. As the parent IRQ has usually been
|
||||
explicitly requested by the driver, this does very little more than
|
||||
mark all the child IRQs as having the other IRQ as parent.
|
||||
|
||||
If there is a need to exclude certain GPIO lines from the IRQ domain handled by
|
||||
these helpers, we can set .irq.need_valid_mask of the gpiochip before
|
||||
devm_gpiochip_add_data() or gpiochip_add_data() is called. This allocates an
|
||||
.irq.valid_mask with as many bits set as there are GPIO lines in the chip, each
|
||||
bit representing line 0..n-1. Drivers can exclude GPIO lines by clearing bits
|
||||
from this mask. The mask must be filled in before gpiochip_irqchip_add() or
|
||||
gpiochip_irqchip_add_nested() is called.
|
||||
from this mask. The mask can be filled in the init_valid_mask() callback
|
||||
that is part of the struct gpio_irq_chip.
|
||||
|
||||
To use the helpers please keep the following in mind:
|
||||
|
||||
|
@ -129,58 +129,9 @@ GPIOLIB irqchip
|
||||
The GPIOLIB irqchip is a helper irqchip for "simple cases" that should
|
||||
try to cover any generic kind of irqchip cascaded from a GPIO.
|
||||
|
||||
- Convert all the GPIOLIB_IRQCHIP users to pass an irqchip template,
|
||||
parent and flags before calling [devm_]gpiochip_add[_data]().
|
||||
Currently we set up the irqchip after setting up the gpiochip
|
||||
using gpiochip_irqchip_add() and gpiochip_set_[chained|nested]_irqchip().
|
||||
This is too complex, so convert all users over to just set up
|
||||
the irqchip before registering the gpio_chip, typical example:
|
||||
|
||||
/* Typical state container with dynamic irqchip */
|
||||
struct my_gpio {
|
||||
struct gpio_chip gc;
|
||||
struct irq_chip irq;
|
||||
};
|
||||
|
||||
int irq; /* from platform etc */
|
||||
struct my_gpio *g;
|
||||
struct gpio_irq_chip *girq;
|
||||
|
||||
/* Set up the irqchip dynamically */
|
||||
g->irq.name = "my_gpio_irq";
|
||||
g->irq.irq_ack = my_gpio_ack_irq;
|
||||
g->irq.irq_mask = my_gpio_mask_irq;
|
||||
g->irq.irq_unmask = my_gpio_unmask_irq;
|
||||
g->irq.irq_set_type = my_gpio_set_irq_type;
|
||||
|
||||
/* Get a pointer to the gpio_irq_chip */
|
||||
girq = &g->gc.irq;
|
||||
girq->chip = &g->irq;
|
||||
girq->parent_handler = ftgpio_gpio_irq_handler;
|
||||
girq->num_parents = 1;
|
||||
girq->parents = devm_kcalloc(dev, 1, sizeof(*girq->parents),
|
||||
GFP_KERNEL);
|
||||
if (!girq->parents)
|
||||
return -ENOMEM;
|
||||
girq->default_type = IRQ_TYPE_NONE;
|
||||
girq->handler = handle_bad_irq;
|
||||
girq->parents[0] = irq;
|
||||
|
||||
When this is done, we will delete the old APIs for instatiating
|
||||
GPIOLIB_IRQCHIP and simplify the code.
|
||||
|
||||
- Look over and identify any remaining easily converted drivers and
|
||||
dry-code conversions to gpiolib irqchip for maintainers to test
|
||||
|
||||
- Drop gpiochip_set_chained_irqchip() when all the chained irqchips
|
||||
have been converted to the above infrastructure.
|
||||
|
||||
- Add more infrastructure to make it possible to also pass a threaded
|
||||
irqchip in struct gpio_irq_chip.
|
||||
|
||||
- Drop gpiochip_irqchip_add_nested() when all the chained irqchips
|
||||
have been converted to the above infrastructure.
|
||||
|
||||
|
||||
Increase integration with pin control
|
||||
|
||||
|
@ -132,8 +132,7 @@ static void idi_48_irq_mask(struct irq_data *data)
|
||||
|
||||
outb(idi48gpio->cos_enb, idi48gpio->base + 7);
|
||||
|
||||
raw_spin_unlock_irqrestore(&idi48gpio->lock,
|
||||
flags);
|
||||
raw_spin_unlock_irqrestore(&idi48gpio->lock, flags);
|
||||
}
|
||||
|
||||
return;
|
||||
@ -166,8 +165,7 @@ static void idi_48_irq_unmask(struct irq_data *data)
|
||||
|
||||
outb(idi48gpio->cos_enb, idi48gpio->base + 7);
|
||||
|
||||
raw_spin_unlock_irqrestore(&idi48gpio->lock,
|
||||
flags);
|
||||
raw_spin_unlock_irqrestore(&idi48gpio->lock, flags);
|
||||
}
|
||||
|
||||
return;
|
||||
|
@ -474,15 +474,6 @@ static int stmpe_gpio_probe(struct platform_device *pdev)
|
||||
stmpe_gpio->chip.parent = &pdev->dev;
|
||||
stmpe_gpio->chip.of_node = np;
|
||||
stmpe_gpio->chip.base = -1;
|
||||
/*
|
||||
* REVISIT: this makes sure the valid mask gets allocated and
|
||||
* filled in when adding the gpio_chip, but the rest of the
|
||||
* gpio_irqchip is still filled in using the old method
|
||||
* in gpiochip_irqchip_add_nested() so clean this up once we
|
||||
* get the gpio_irqchip to initialize while adding the
|
||||
* gpio_chip also for threaded irqchips.
|
||||
*/
|
||||
stmpe_gpio->chip.irq.init_valid_mask = stmpe_init_irq_valid_mask;
|
||||
|
||||
if (IS_ENABLED(CONFIG_DEBUG_FS))
|
||||
stmpe_gpio->chip.dbg_show = stmpe_dbg_show;
|
||||
@ -520,6 +511,7 @@ static int stmpe_gpio_probe(struct platform_device *pdev)
|
||||
girq->default_type = IRQ_TYPE_NONE;
|
||||
girq->handler = handle_simple_irq;
|
||||
girq->threaded = true;
|
||||
girq->init_valid_mask = stmpe_init_irq_valid_mask;
|
||||
}
|
||||
|
||||
ret = gpiochip_add_data(&stmpe_gpio->chip, stmpe_gpio);
|
||||
|
@ -1479,21 +1479,10 @@ static __poll_t lineevent_poll(struct file *file,
|
||||
return events;
|
||||
}
|
||||
|
||||
static ssize_t lineevent_get_size(void)
|
||||
{
|
||||
#if defined(CONFIG_X86_64) && !defined(CONFIG_UML)
|
||||
/* i386 has no padding after 'id' */
|
||||
if (in_ia32_syscall()) {
|
||||
struct compat_gpioeevent_data {
|
||||
compat_u64 timestamp;
|
||||
u32 id;
|
||||
};
|
||||
|
||||
return sizeof(struct compat_gpioeevent_data);
|
||||
}
|
||||
#endif
|
||||
return sizeof(struct gpioevent_data);
|
||||
}
|
||||
struct compat_gpioeevent_data {
|
||||
compat_u64 timestamp;
|
||||
u32 id;
|
||||
};
|
||||
|
||||
static ssize_t lineevent_read(struct file *file,
|
||||
char __user *buf,
|
||||
@ -1515,7 +1504,10 @@ static ssize_t lineevent_read(struct file *file,
|
||||
* actual sizeof() and pass this as an argument to copy_to_user() to
|
||||
* drop unneeded bytes from the output.
|
||||
*/
|
||||
ge_size = lineevent_get_size();
|
||||
if (compat_need_64bit_alignment_fixup())
|
||||
ge_size = sizeof(struct compat_gpioeevent_data);
|
||||
else
|
||||
ge_size = sizeof(struct gpioevent_data);
|
||||
if (count < ge_size)
|
||||
return -EINVAL;
|
||||
|
||||
|
@ -936,67 +936,6 @@ bool gpiochip_irqchip_irq_valid(const struct gpio_chip *gc,
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(gpiochip_irqchip_irq_valid);
|
||||
|
||||
/**
|
||||
* gpiochip_set_cascaded_irqchip() - connects a cascaded irqchip to a gpiochip
|
||||
* @gc: the gpiochip to set the irqchip chain to
|
||||
* @parent_irq: the irq number corresponding to the parent IRQ for this
|
||||
* cascaded irqchip
|
||||
* @parent_handler: the parent interrupt handler for the accumulated IRQ
|
||||
* coming out of the gpiochip. If the interrupt is nested rather than
|
||||
* cascaded, pass NULL in this handler argument
|
||||
*/
|
||||
static void gpiochip_set_cascaded_irqchip(struct gpio_chip *gc,
|
||||
unsigned int parent_irq,
|
||||
irq_flow_handler_t parent_handler)
|
||||
{
|
||||
struct gpio_irq_chip *girq = &gc->irq;
|
||||
struct device *dev = &gc->gpiodev->dev;
|
||||
|
||||
if (!girq->domain) {
|
||||
chip_err(gc, "called %s before setting up irqchip\n",
|
||||
__func__);
|
||||
return;
|
||||
}
|
||||
|
||||
if (parent_handler) {
|
||||
if (gc->can_sleep) {
|
||||
chip_err(gc,
|
||||
"you cannot have chained interrupts on a chip that may sleep\n");
|
||||
return;
|
||||
}
|
||||
girq->parents = devm_kcalloc(dev, 1,
|
||||
sizeof(*girq->parents),
|
||||
GFP_KERNEL);
|
||||
if (!girq->parents) {
|
||||
chip_err(gc, "out of memory allocating parent IRQ\n");
|
||||
return;
|
||||
}
|
||||
girq->parents[0] = parent_irq;
|
||||
girq->num_parents = 1;
|
||||
/*
|
||||
* The parent irqchip is already using the chip_data for this
|
||||
* irqchip, so our callbacks simply use the handler_data.
|
||||
*/
|
||||
irq_set_chained_handler_and_data(parent_irq, parent_handler,
|
||||
gc);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* gpiochip_set_nested_irqchip() - connects a nested irqchip to a gpiochip
|
||||
* @gc: the gpiochip to set the irqchip nested handler to
|
||||
* @irqchip: the irqchip to nest to the gpiochip
|
||||
* @parent_irq: the irq number corresponding to the parent IRQ for this
|
||||
* nested irqchip
|
||||
*/
|
||||
void gpiochip_set_nested_irqchip(struct gpio_chip *gc,
|
||||
struct irq_chip *irqchip,
|
||||
unsigned int parent_irq)
|
||||
{
|
||||
gpiochip_set_cascaded_irqchip(gc, parent_irq, NULL);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(gpiochip_set_nested_irqchip);
|
||||
|
||||
#ifdef CONFIG_IRQ_DOMAIN_HIERARCHY
|
||||
|
||||
/**
|
||||
@ -1647,98 +1586,6 @@ static void gpiochip_irqchip_remove(struct gpio_chip *gc)
|
||||
gpiochip_irqchip_free_valid_mask(gc);
|
||||
}
|
||||
|
||||
/**
|
||||
* gpiochip_irqchip_add_key() - adds an irqchip to a gpiochip
|
||||
* @gc: the gpiochip to add the irqchip to
|
||||
* @irqchip: the irqchip to add to the gpiochip
|
||||
* @first_irq: if not dynamically assigned, the base (first) IRQ to
|
||||
* allocate gpiochip irqs from
|
||||
* @handler: the irq handler to use (often a predefined irq core function)
|
||||
* @type: the default type for IRQs on this irqchip, pass IRQ_TYPE_NONE
|
||||
* to have the core avoid setting up any default type in the hardware.
|
||||
* @threaded: whether this irqchip uses a nested thread handler
|
||||
* @lock_key: lockdep class for IRQ lock
|
||||
* @request_key: lockdep class for IRQ request
|
||||
*
|
||||
* This function closely associates a certain irqchip with a certain
|
||||
* gpiochip, providing an irq domain to translate the local IRQs to
|
||||
* global irqs in the gpiolib core, and making sure that the gpiochip
|
||||
* is passed as chip data to all related functions. Driver callbacks
|
||||
* need to use gpiochip_get_data() to get their local state containers back
|
||||
* from the gpiochip passed as chip data. An irqdomain will be stored
|
||||
* in the gpiochip that shall be used by the driver to handle IRQ number
|
||||
* translation. The gpiochip will need to be initialized and registered
|
||||
* before calling this function.
|
||||
*
|
||||
* This function will handle two cell:ed simple IRQs and assumes all
|
||||
* the pins on the gpiochip can generate a unique IRQ. Everything else
|
||||
* need to be open coded.
|
||||
*/
|
||||
int gpiochip_irqchip_add_key(struct gpio_chip *gc,
|
||||
struct irq_chip *irqchip,
|
||||
unsigned int first_irq,
|
||||
irq_flow_handler_t handler,
|
||||
unsigned int type,
|
||||
bool threaded,
|
||||
struct lock_class_key *lock_key,
|
||||
struct lock_class_key *request_key)
|
||||
{
|
||||
struct device_node *of_node;
|
||||
|
||||
if (!gc || !irqchip)
|
||||
return -EINVAL;
|
||||
|
||||
if (!gc->parent) {
|
||||
chip_err(gc, "missing gpiochip .dev parent pointer\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
gc->irq.threaded = threaded;
|
||||
of_node = gc->parent->of_node;
|
||||
#ifdef CONFIG_OF_GPIO
|
||||
/*
|
||||
* If the gpiochip has an assigned OF node this takes precedence
|
||||
* FIXME: get rid of this and use gc->parent->of_node
|
||||
* everywhere
|
||||
*/
|
||||
if (gc->of_node)
|
||||
of_node = gc->of_node;
|
||||
#endif
|
||||
/*
|
||||
* Specifying a default trigger is a terrible idea if DT or ACPI is
|
||||
* used to configure the interrupts, as you may end-up with
|
||||
* conflicting triggers. Tell the user, and reset to NONE.
|
||||
*/
|
||||
if (WARN(of_node && type != IRQ_TYPE_NONE,
|
||||
"%pOF: Ignoring %d default trigger\n", of_node, type))
|
||||
type = IRQ_TYPE_NONE;
|
||||
if (has_acpi_companion(gc->parent) && type != IRQ_TYPE_NONE) {
|
||||
acpi_handle_warn(ACPI_HANDLE(gc->parent),
|
||||
"Ignoring %d default trigger\n", type);
|
||||
type = IRQ_TYPE_NONE;
|
||||
}
|
||||
|
||||
gc->irq.chip = irqchip;
|
||||
gc->irq.handler = handler;
|
||||
gc->irq.default_type = type;
|
||||
gc->to_irq = gpiochip_to_irq;
|
||||
gc->irq.lock_key = lock_key;
|
||||
gc->irq.request_key = request_key;
|
||||
gc->irq.domain = irq_domain_add_simple(of_node,
|
||||
gc->ngpio, first_irq,
|
||||
&gpiochip_domain_ops, gc);
|
||||
if (!gc->irq.domain) {
|
||||
gc->irq.chip = NULL;
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
gpiochip_set_irq_hooks(gc);
|
||||
|
||||
acpi_gpiochip_request_interrupts(gc);
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(gpiochip_irqchip_add_key);
|
||||
|
||||
/**
|
||||
* gpiochip_irqchip_add_domain() - adds an irqdomain to a gpiochip
|
||||
* @gc: the gpiochip to add the irqchip to
|
||||
@ -2268,8 +2115,8 @@ static int gpio_set_config(struct gpio_desc *desc, enum pin_config_param mode)
|
||||
|
||||
static int gpio_set_bias(struct gpio_desc *desc)
|
||||
{
|
||||
int bias = 0;
|
||||
int ret = 0;
|
||||
enum pin_config_param bias;
|
||||
int ret;
|
||||
|
||||
if (test_bit(FLAG_BIAS_DISABLE, &desc->flags))
|
||||
bias = PIN_CONFIG_BIAS_DISABLE;
|
||||
@ -2277,12 +2124,13 @@ static int gpio_set_bias(struct gpio_desc *desc)
|
||||
bias = PIN_CONFIG_BIAS_PULL_UP;
|
||||
else if (test_bit(FLAG_PULL_DOWN, &desc->flags))
|
||||
bias = PIN_CONFIG_BIAS_PULL_DOWN;
|
||||
else
|
||||
return 0;
|
||||
|
||||
ret = gpio_set_config(desc, bias);
|
||||
if (ret != -ENOTSUPP)
|
||||
return ret;
|
||||
|
||||
if (bias) {
|
||||
ret = gpio_set_config(desc, bias);
|
||||
if (ret != -ENOTSUPP)
|
||||
return ret;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -621,83 +621,12 @@ int gpiochip_irq_domain_activate(struct irq_domain *domain,
|
||||
void gpiochip_irq_domain_deactivate(struct irq_domain *domain,
|
||||
struct irq_data *data);
|
||||
|
||||
void gpiochip_set_nested_irqchip(struct gpio_chip *gc,
|
||||
struct irq_chip *irqchip,
|
||||
unsigned int parent_irq);
|
||||
|
||||
int gpiochip_irqchip_add_key(struct gpio_chip *gc,
|
||||
struct irq_chip *irqchip,
|
||||
unsigned int first_irq,
|
||||
irq_flow_handler_t handler,
|
||||
unsigned int type,
|
||||
bool threaded,
|
||||
struct lock_class_key *lock_key,
|
||||
struct lock_class_key *request_key);
|
||||
|
||||
bool gpiochip_irqchip_irq_valid(const struct gpio_chip *gc,
|
||||
unsigned int offset);
|
||||
|
||||
int gpiochip_irqchip_add_domain(struct gpio_chip *gc,
|
||||
struct irq_domain *domain);
|
||||
|
||||
#ifdef CONFIG_LOCKDEP
|
||||
|
||||
/*
|
||||
* Lockdep requires that each irqchip instance be created with a
|
||||
* unique key so as to avoid unnecessary warnings. This upfront
|
||||
* boilerplate static inlines provides such a key for each
|
||||
* unique instance.
|
||||
*/
|
||||
static inline int gpiochip_irqchip_add(struct gpio_chip *gc,
|
||||
struct irq_chip *irqchip,
|
||||
unsigned int first_irq,
|
||||
irq_flow_handler_t handler,
|
||||
unsigned int type)
|
||||
{
|
||||
static struct lock_class_key lock_key;
|
||||
static struct lock_class_key request_key;
|
||||
|
||||
return gpiochip_irqchip_add_key(gc, irqchip, first_irq,
|
||||
handler, type, false,
|
||||
&lock_key, &request_key);
|
||||
}
|
||||
|
||||
static inline int gpiochip_irqchip_add_nested(struct gpio_chip *gc,
|
||||
struct irq_chip *irqchip,
|
||||
unsigned int first_irq,
|
||||
irq_flow_handler_t handler,
|
||||
unsigned int type)
|
||||
{
|
||||
|
||||
static struct lock_class_key lock_key;
|
||||
static struct lock_class_key request_key;
|
||||
|
||||
return gpiochip_irqchip_add_key(gc, irqchip, first_irq,
|
||||
handler, type, true,
|
||||
&lock_key, &request_key);
|
||||
}
|
||||
#else /* ! CONFIG_LOCKDEP */
|
||||
static inline int gpiochip_irqchip_add(struct gpio_chip *gc,
|
||||
struct irq_chip *irqchip,
|
||||
unsigned int first_irq,
|
||||
irq_flow_handler_t handler,
|
||||
unsigned int type)
|
||||
{
|
||||
return gpiochip_irqchip_add_key(gc, irqchip, first_irq,
|
||||
handler, type, false, NULL, NULL);
|
||||
}
|
||||
|
||||
static inline int gpiochip_irqchip_add_nested(struct gpio_chip *gc,
|
||||
struct irq_chip *irqchip,
|
||||
unsigned int first_irq,
|
||||
irq_flow_handler_t handler,
|
||||
unsigned int type)
|
||||
{
|
||||
return gpiochip_irqchip_add_key(gc, irqchip, first_irq,
|
||||
handler, type, true, NULL, NULL);
|
||||
}
|
||||
#endif /* CONFIG_LOCKDEP */
|
||||
|
||||
int gpiochip_generic_request(struct gpio_chip *gc, unsigned int offset);
|
||||
void gpiochip_generic_free(struct gpio_chip *gc, unsigned int offset);
|
||||
int gpiochip_generic_config(struct gpio_chip *gc, unsigned int offset,
|
||||
|
Loading…
Reference in New Issue
Block a user