This is the bulk of GPIO development for the v5.7 kernel cycle.
Core and userspace API: - The userspace API KFIFOs have been imoproved with locks that do not block interrupts. This makes us better at getting events to userspace without blocking or disturbing new events arriving in the same time. This was reviewed by the KFIFO maintainer Stefani. This is a generic improvement which paves the road for similar improvements in other subsystems. - We provide a new ioctl() for monitoring changes in the line information, such as when multiple clients are taking lines and giving them back, possibly reconfiguring them in the process: we can now monitor that and not get stuck with stale static information. - An example tool 'gpio-watch' is provided to showcase this functionality. - Timestamps for events are switched to ktime_get_ns() which is monotonic. We previously had a 'realtime' stamp which could move forward and *backward* in time, which probably would just cause silent bugs and weird behaviour. In the long run we see two relevant timestamps: ktime_get_ns() or the timestamp sometimes provided by the GPIO hardware itself, if that exists. - Device Tree overlay support for GPIO hogs. On systems that load overlays, these overlays can now contain hogs, and will then be respected. - Handle pin control interaction with nonexisting pin ranges in the GPIO library core instead of in the individual drivers. New drivers: - New driver for the Mellanox BlueField 2 GPIO controller. Driver improvements: - Introduce the BGPIOF_NO_SET_ON_INPUT flag to the generic MMIO GPIO library and use this flag in the MT7621 driver. - Texas Instruments OMAP CPU power management improvements, such as blocking of idle on pending GPIO interrupts. -----BEGIN PGP SIGNATURE----- iQIzBAABCAAdFiEElDRnuGcz/wPCXQWMQRCzN7AZXXMFAl6IWUAACgkQQRCzN7AZ XXPReQ//WUHALqbLOL/DZR8kVyygVZXV8JC4jcuMQgX+Mvm1DQuGp1T8VPlG1wC7 vy5eTz3oPc+jAaHGQgGiSGdPRQtDkAW5+Wo4hzcHJ5ATlzT7cgNywK3Jk6+YlPlF CuhHB6aPEymQ45/GseUwEb5AuWWKS03P3XdOBj2IdDhuRHRfyjHi+31s0lh6bfNs kC2eP+VtddVqLbOdPE1GKjZi+Iq9ffFRmiNTduHbl3o6ZoE6WnMvn2wsC6zPpJr4 AM8zpj9JFUq1DYaQf8bJJLTm8lItcyyUUhq8oKUxBpecjWfhUYwEYHGxqWIk9fbM oyJ3zBlU0G7bc4gu1VG3cTacUf//BTmQG3iZNSSw9w17I1hOMX9wNJan22VkEbJD 8X+6AcgkldNwnAVWcVgg1lkfgdxlWURMYXl1uQPQZSj2SQL6gFcdkAhKwlgl8jOi RctUfZ69tD+4ul2b+MhL6BtIk16o2RIBJF53ESxrR4qftRZ3ywW4JQ/AwNoQalxM Xg8U3axFm8kitOzemVoXGr/AIee9KpcA4NK8yQLEPmSFgBaVzaVlP9Q7Klr7QGXg RvK45uRULrWnvSyoVY7Ox+ie1NiG8JAbaxKBxkV8n+z+gVMh82xcyQX+5SgWxwW0 dzeEx1B0mCW4WDAK29CcdQVE6m4K2u/fabhZq5IhgS1mQoTC894= =EFm/ -----END PGP SIGNATURE----- Merge tag 'gpio-v5.7-1' of git://git.kernel.org/pub/scm/linux/kernel/git/linusw/linux-gpio Pull GPIO updates from Linus Walleij: "This is the bulk of GPIO development for the v5.7 kernel cycle. Core and userspace API: - The userspace API KFIFOs have been imoproved with locks that do not block interrupts. This makes us better at getting events to userspace without blocking or disturbing new events arriving in the same time. This was reviewed by the KFIFO maintainer Stefani. This is a generic improvement which paves the road for similar improvements in other subsystems. - We provide a new ioctl() for monitoring changes in the line information, such as when multiple clients are taking lines and giving them back, possibly reconfiguring them in the process: we can now monitor that and not get stuck with stale static information. - An example tool 'gpio-watch' is provided to showcase this functionality. - Timestamps for events are switched to ktime_get_ns() which is monotonic. We previously had a 'realtime' stamp which could move forward and *backward* in time, which probably would just cause silent bugs and weird behaviour. In the long run we see two relevant timestamps: ktime_get_ns() or the timestamp sometimes provided by the GPIO hardware itself, if that exists. - Device Tree overlay support for GPIO hogs. On systems that load overlays, these overlays can now contain hogs, and will then be respected. - Handle pin control interaction with nonexisting pin ranges in the GPIO library core instead of in the individual drivers. New drivers: - New driver for the Mellanox BlueField 2 GPIO controller. Driver improvements: - Introduce the BGPIOF_NO_SET_ON_INPUT flag to the generic MMIO GPIO library and use this flag in the MT7621 driver. - Texas Instruments OMAP CPU power management improvements, such as blocking of idle on pending GPIO interrupts" * tag 'gpio-v5.7-1' of git://git.kernel.org/pub/scm/linux/kernel/git/linusw/linux-gpio: (59 commits) Revert "gpio: eic-sprd: Use devm_platform_ioremap_resource()" pinctrl: Unconditionally assign .request()/.free() gpio: Unconditionally assign .request()/.free() gpio: export of_pinctrl_get to modules pinctrl: Define of_pinctrl_get() dummy for !PINCTRL gpio: Rename variable in core APIs gpio: Avoid using pin ranges with !PINCTRL gpiolib: Remove unused gpio_chip parameter from gpio_set_bias() gpiolib: Pass gpio_desc to gpio_set_config() gpiolib: Introduce gpiod_set_config() tools: gpio: Fix out-of-tree build regression gpio: gpiolib: fix a doc warning gpio: tegra186: Add Tegra194 pin ranges for GG.0 and GG.1 gpio: tegra186: Add support for pin ranges gpio: Support GPIO controllers without pin-ranges ARM: integrator: impd1: Use GPIO_LOOKUP() helper macro gpio: brcmstb: support gpio-line-names property tools: gpio: Fix typo in gpio-utils tools: gpio-hammer: Apply scripts/Lindent and retain good changes gpiolib: gpio_name_to_desc: factor out !name check ...
This commit is contained in:
commit
828907ef25
|
@ -416,7 +416,7 @@ 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.
|
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
|
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
|
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 cascaded interrupt handler using gpio_irq_chip:
|
||||||
|
|
||||||
.. code-block:: c
|
.. code-block:: c
|
||||||
|
|
||||||
|
@ -453,7 +453,7 @@ is a typical example of a cascaded interrupt handler using gpio_irq_chip::
|
||||||
return devm_gpiochip_add_data(dev, &g->gc, g);
|
return devm_gpiochip_add_data(dev, &g->gc, g);
|
||||||
|
|
||||||
The helper support using hierarchical interrupt controllers as well.
|
The helper support using hierarchical interrupt controllers as well.
|
||||||
In this case the typical set-up will look like this::
|
In this case the typical set-up will look like this:
|
||||||
|
|
||||||
.. code-block:: c
|
.. code-block:: c
|
||||||
|
|
||||||
|
|
|
@ -410,13 +410,10 @@ static int __ref impd1_probe(struct lm_device *dev)
|
||||||
* 5 = Key lower right
|
* 5 = Key lower right
|
||||||
*/
|
*/
|
||||||
/* We need the two MMCI GPIO entries */
|
/* We need the two MMCI GPIO entries */
|
||||||
lookup->table[0].chip_label = chipname;
|
lookup->table[0] = (struct gpiod_lookup)
|
||||||
lookup->table[0].chip_hwnum = 3;
|
GPIO_LOOKUP(chipname, 3, "wp", 0);
|
||||||
lookup->table[0].con_id = "wp";
|
lookup->table[1] = (struct gpiod_lookup)
|
||||||
lookup->table[1].chip_label = chipname;
|
GPIO_LOOKUP(chipname, 4, "cd", GPIO_ACTIVE_LOW);
|
||||||
lookup->table[1].chip_hwnum = 4;
|
|
||||||
lookup->table[1].con_id = "cd";
|
|
||||||
lookup->table[1].flags = GPIO_ACTIVE_LOW;
|
|
||||||
gpiod_add_lookup_table(lookup);
|
gpiod_add_lookup_table(lookup);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -109,6 +109,7 @@ static int omap3_enter_idle(struct cpuidle_device *dev,
|
||||||
int index)
|
int index)
|
||||||
{
|
{
|
||||||
struct omap3_idle_statedata *cx = &omap3_idle_data[index];
|
struct omap3_idle_statedata *cx = &omap3_idle_data[index];
|
||||||
|
int error;
|
||||||
|
|
||||||
if (omap_irq_pending() || need_resched())
|
if (omap_irq_pending() || need_resched())
|
||||||
goto return_sleep_time;
|
goto return_sleep_time;
|
||||||
|
@ -125,8 +126,11 @@ static int omap3_enter_idle(struct cpuidle_device *dev,
|
||||||
* Call idle CPU PM enter notifier chain so that
|
* Call idle CPU PM enter notifier chain so that
|
||||||
* VFP context is saved.
|
* VFP context is saved.
|
||||||
*/
|
*/
|
||||||
if (cx->mpu_state == PWRDM_POWER_OFF)
|
if (cx->mpu_state == PWRDM_POWER_OFF) {
|
||||||
cpu_pm_enter();
|
error = cpu_pm_enter();
|
||||||
|
if (error)
|
||||||
|
goto out_clkdm_set;
|
||||||
|
}
|
||||||
|
|
||||||
/* Execute ARM wfi */
|
/* Execute ARM wfi */
|
||||||
omap_sram_idle();
|
omap_sram_idle();
|
||||||
|
@ -139,6 +143,7 @@ static int omap3_enter_idle(struct cpuidle_device *dev,
|
||||||
pwrdm_read_prev_pwrst(mpu_pd) == PWRDM_POWER_OFF)
|
pwrdm_read_prev_pwrst(mpu_pd) == PWRDM_POWER_OFF)
|
||||||
cpu_pm_exit();
|
cpu_pm_exit();
|
||||||
|
|
||||||
|
out_clkdm_set:
|
||||||
/* Re-allow idle for C1 */
|
/* Re-allow idle for C1 */
|
||||||
if (cx->flags & OMAP_CPUIDLE_CX_NO_CLKDM_IDLE)
|
if (cx->flags & OMAP_CPUIDLE_CX_NO_CLKDM_IDLE)
|
||||||
clkdm_allow_idle(mpu_pd->pwrdm_clkdms[0]);
|
clkdm_allow_idle(mpu_pd->pwrdm_clkdms[0]);
|
||||||
|
|
|
@ -122,6 +122,7 @@ static int omap_enter_idle_coupled(struct cpuidle_device *dev,
|
||||||
{
|
{
|
||||||
struct idle_statedata *cx = state_ptr + index;
|
struct idle_statedata *cx = state_ptr + index;
|
||||||
u32 mpuss_can_lose_context = 0;
|
u32 mpuss_can_lose_context = 0;
|
||||||
|
int error;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* CPU0 has to wait and stay ON until CPU1 is OFF state.
|
* CPU0 has to wait and stay ON until CPU1 is OFF state.
|
||||||
|
@ -159,7 +160,9 @@ static int omap_enter_idle_coupled(struct cpuidle_device *dev,
|
||||||
* Call idle CPU PM enter notifier chain so that
|
* Call idle CPU PM enter notifier chain so that
|
||||||
* VFP and per CPU interrupt context is saved.
|
* VFP and per CPU interrupt context is saved.
|
||||||
*/
|
*/
|
||||||
cpu_pm_enter();
|
error = cpu_pm_enter();
|
||||||
|
if (error)
|
||||||
|
goto cpu_pm_out;
|
||||||
|
|
||||||
if (dev->cpu == 0) {
|
if (dev->cpu == 0) {
|
||||||
pwrdm_set_logic_retst(mpu_pd, cx->mpu_logic_state);
|
pwrdm_set_logic_retst(mpu_pd, cx->mpu_logic_state);
|
||||||
|
@ -169,13 +172,17 @@ static int omap_enter_idle_coupled(struct cpuidle_device *dev,
|
||||||
* Call idle CPU cluster PM enter notifier chain
|
* Call idle CPU cluster PM enter notifier chain
|
||||||
* to save GIC and wakeupgen context.
|
* to save GIC and wakeupgen context.
|
||||||
*/
|
*/
|
||||||
if (mpuss_can_lose_context)
|
if (mpuss_can_lose_context) {
|
||||||
cpu_cluster_pm_enter();
|
error = cpu_cluster_pm_enter();
|
||||||
|
if (error)
|
||||||
|
goto cpu_cluster_pm_out;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
omap4_enter_lowpower(dev->cpu, cx->cpu_state);
|
omap4_enter_lowpower(dev->cpu, cx->cpu_state);
|
||||||
cpu_done[dev->cpu] = true;
|
cpu_done[dev->cpu] = true;
|
||||||
|
|
||||||
|
cpu_cluster_pm_out:
|
||||||
/* Wakeup CPU1 only if it is not offlined */
|
/* Wakeup CPU1 only if it is not offlined */
|
||||||
if (dev->cpu == 0 && cpumask_test_cpu(1, cpu_online_mask)) {
|
if (dev->cpu == 0 && cpumask_test_cpu(1, cpu_online_mask)) {
|
||||||
|
|
||||||
|
@ -197,12 +204,6 @@ static int omap_enter_idle_coupled(struct cpuidle_device *dev,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
|
||||||
* Call idle CPU PM exit notifier chain to restore
|
|
||||||
* VFP and per CPU IRQ context.
|
|
||||||
*/
|
|
||||||
cpu_pm_exit();
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Call idle CPU cluster PM exit notifier chain
|
* Call idle CPU cluster PM exit notifier chain
|
||||||
* to restore GIC and wakeupgen context.
|
* to restore GIC and wakeupgen context.
|
||||||
|
@ -210,6 +211,13 @@ static int omap_enter_idle_coupled(struct cpuidle_device *dev,
|
||||||
if (dev->cpu == 0 && mpuss_can_lose_context)
|
if (dev->cpu == 0 && mpuss_can_lose_context)
|
||||||
cpu_cluster_pm_exit();
|
cpu_cluster_pm_exit();
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Call idle CPU PM exit notifier chain to restore
|
||||||
|
* VFP and per CPU IRQ context.
|
||||||
|
*/
|
||||||
|
cpu_pm_exit();
|
||||||
|
|
||||||
|
cpu_pm_out:
|
||||||
tick_broadcast_exit();
|
tick_broadcast_exit();
|
||||||
|
|
||||||
fail:
|
fail:
|
||||||
|
|
|
@ -191,6 +191,7 @@ void omap_sram_idle(void)
|
||||||
int per_next_state = PWRDM_POWER_ON;
|
int per_next_state = PWRDM_POWER_ON;
|
||||||
int core_next_state = PWRDM_POWER_ON;
|
int core_next_state = PWRDM_POWER_ON;
|
||||||
u32 sdrc_pwr = 0;
|
u32 sdrc_pwr = 0;
|
||||||
|
int error;
|
||||||
|
|
||||||
mpu_next_state = pwrdm_read_next_pwrst(mpu_pwrdm);
|
mpu_next_state = pwrdm_read_next_pwrst(mpu_pwrdm);
|
||||||
switch (mpu_next_state) {
|
switch (mpu_next_state) {
|
||||||
|
@ -219,8 +220,11 @@ void omap_sram_idle(void)
|
||||||
pwrdm_pre_transition(NULL);
|
pwrdm_pre_transition(NULL);
|
||||||
|
|
||||||
/* PER */
|
/* PER */
|
||||||
if (per_next_state == PWRDM_POWER_OFF)
|
if (per_next_state == PWRDM_POWER_OFF) {
|
||||||
cpu_cluster_pm_enter();
|
error = cpu_cluster_pm_enter();
|
||||||
|
if (error)
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
/* CORE */
|
/* CORE */
|
||||||
if (core_next_state < PWRDM_POWER_ON) {
|
if (core_next_state < PWRDM_POWER_ON) {
|
||||||
|
|
|
@ -394,13 +394,13 @@ config GPIO_MVEBU
|
||||||
|
|
||||||
config GPIO_MXC
|
config GPIO_MXC
|
||||||
def_bool y
|
def_bool y
|
||||||
depends on ARCH_MXC
|
depends on ARCH_MXC || COMPILE_TEST
|
||||||
select GPIO_GENERIC
|
select GPIO_GENERIC
|
||||||
select GENERIC_IRQ_CHIP
|
select GENERIC_IRQ_CHIP
|
||||||
|
|
||||||
config GPIO_MXS
|
config GPIO_MXS
|
||||||
def_bool y
|
def_bool y
|
||||||
depends on ARCH_MXS
|
depends on ARCH_MXS || COMPILE_TEST
|
||||||
select GPIO_GENERIC
|
select GPIO_GENERIC
|
||||||
select GENERIC_IRQ_CHIP
|
select GENERIC_IRQ_CHIP
|
||||||
|
|
||||||
|
@ -1399,6 +1399,13 @@ config GPIO_MLXBF
|
||||||
help
|
help
|
||||||
Say Y here if you want GPIO support on Mellanox BlueField SoC.
|
Say Y here if you want GPIO support on Mellanox BlueField SoC.
|
||||||
|
|
||||||
|
config GPIO_MLXBF2
|
||||||
|
tristate "Mellanox BlueField 2 SoC GPIO"
|
||||||
|
depends on (MELLANOX_PLATFORM && ARM64 && ACPI) || (64BIT && COMPILE_TEST)
|
||||||
|
select GPIO_GENERIC
|
||||||
|
help
|
||||||
|
Say Y here if you want GPIO support on Mellanox BlueField 2 SoC.
|
||||||
|
|
||||||
config GPIO_ML_IOH
|
config GPIO_ML_IOH
|
||||||
tristate "OKI SEMICONDUCTOR ML7213 IOH GPIO support"
|
tristate "OKI SEMICONDUCTOR ML7213 IOH GPIO support"
|
||||||
depends on X86 || COMPILE_TEST
|
depends on X86 || COMPILE_TEST
|
||||||
|
|
|
@ -93,6 +93,7 @@ obj-$(CONFIG_GPIO_MENZ127) += gpio-menz127.o
|
||||||
obj-$(CONFIG_GPIO_MERRIFIELD) += gpio-merrifield.o
|
obj-$(CONFIG_GPIO_MERRIFIELD) += gpio-merrifield.o
|
||||||
obj-$(CONFIG_GPIO_ML_IOH) += gpio-ml-ioh.o
|
obj-$(CONFIG_GPIO_ML_IOH) += gpio-ml-ioh.o
|
||||||
obj-$(CONFIG_GPIO_MLXBF) += gpio-mlxbf.o
|
obj-$(CONFIG_GPIO_MLXBF) += gpio-mlxbf.o
|
||||||
|
obj-$(CONFIG_GPIO_MLXBF2) += gpio-mlxbf2.o
|
||||||
obj-$(CONFIG_GPIO_MM_LANTIQ) += gpio-mm-lantiq.o
|
obj-$(CONFIG_GPIO_MM_LANTIQ) += gpio-mm-lantiq.o
|
||||||
obj-$(CONFIG_GPIO_MOCKUP) += gpio-mockup.o
|
obj-$(CONFIG_GPIO_MOCKUP) += gpio-mockup.o
|
||||||
obj-$(CONFIG_GPIO_MOXTET) += gpio-moxtet.o
|
obj-$(CONFIG_GPIO_MOXTET) += gpio-moxtet.o
|
||||||
|
|
|
@ -603,6 +603,49 @@ static const struct dev_pm_ops brcmstb_gpio_pm_ops = {
|
||||||
.resume_noirq = brcmstb_gpio_resume,
|
.resume_noirq = brcmstb_gpio_resume,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
static void brcmstb_gpio_set_names(struct device *dev,
|
||||||
|
struct brcmstb_gpio_bank *bank)
|
||||||
|
{
|
||||||
|
struct device_node *np = dev->of_node;
|
||||||
|
const char **names;
|
||||||
|
int nstrings, base;
|
||||||
|
unsigned int i;
|
||||||
|
|
||||||
|
base = bank->id * MAX_GPIO_PER_BANK;
|
||||||
|
|
||||||
|
nstrings = of_property_count_strings(np, "gpio-line-names");
|
||||||
|
if (nstrings <= base)
|
||||||
|
/* Line names not present */
|
||||||
|
return;
|
||||||
|
|
||||||
|
names = devm_kcalloc(dev, MAX_GPIO_PER_BANK, sizeof(*names),
|
||||||
|
GFP_KERNEL);
|
||||||
|
if (!names)
|
||||||
|
return;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Make sure to not index beyond the end of the number of descriptors
|
||||||
|
* of the GPIO device.
|
||||||
|
*/
|
||||||
|
for (i = 0; i < bank->width; i++) {
|
||||||
|
const char *name;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
ret = of_property_read_string_index(np, "gpio-line-names",
|
||||||
|
base + i, &name);
|
||||||
|
if (ret) {
|
||||||
|
if (ret != -ENODATA)
|
||||||
|
dev_err(dev, "unable to name line %d: %d\n",
|
||||||
|
base + i, ret);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (*name)
|
||||||
|
names[i] = name;
|
||||||
|
}
|
||||||
|
|
||||||
|
bank->gc.names = names;
|
||||||
|
}
|
||||||
|
|
||||||
static int brcmstb_gpio_probe(struct platform_device *pdev)
|
static int brcmstb_gpio_probe(struct platform_device *pdev)
|
||||||
{
|
{
|
||||||
struct device *dev = &pdev->dev;
|
struct device *dev = &pdev->dev;
|
||||||
|
@ -726,6 +769,7 @@ static int brcmstb_gpio_probe(struct platform_device *pdev)
|
||||||
need_wakeup_event |= !!__brcmstb_gpio_get_active_irqs(bank);
|
need_wakeup_event |= !!__brcmstb_gpio_get_active_irqs(bank);
|
||||||
gc->write_reg(reg_base + GIO_MASK(bank->id), 0);
|
gc->write_reg(reg_base + GIO_MASK(bank->id), 0);
|
||||||
|
|
||||||
|
brcmstb_gpio_set_names(dev, bank);
|
||||||
err = gpiochip_add_data(gc, bank);
|
err = gpiochip_add_data(gc, bank);
|
||||||
if (err) {
|
if (err) {
|
||||||
dev_err(dev, "Could not add gpiochip for bank %d\n",
|
dev_err(dev, "Could not add gpiochip for bank %d\n",
|
||||||
|
|
|
@ -259,11 +259,8 @@ static int davinci_gpio_probe(struct platform_device *pdev)
|
||||||
chips->chip.of_gpio_n_cells = 2;
|
chips->chip.of_gpio_n_cells = 2;
|
||||||
chips->chip.parent = dev;
|
chips->chip.parent = dev;
|
||||||
chips->chip.of_node = dev->of_node;
|
chips->chip.of_node = dev->of_node;
|
||||||
|
chips->chip.request = gpiochip_generic_request;
|
||||||
if (of_property_read_bool(dev->of_node, "gpio-ranges")) {
|
chips->chip.free = gpiochip_generic_free;
|
||||||
chips->chip.request = gpiochip_generic_request;
|
|
||||||
chips->chip.free = gpiochip_generic_free;
|
|
||||||
}
|
|
||||||
#endif
|
#endif
|
||||||
spin_lock_init(&chips->lock);
|
spin_lock_init(&chips->lock);
|
||||||
|
|
||||||
|
|
|
@ -569,6 +569,7 @@ static int sprd_eic_probe(struct platform_device *pdev)
|
||||||
const struct sprd_eic_variant_data *pdata;
|
const struct sprd_eic_variant_data *pdata;
|
||||||
struct gpio_irq_chip *irq;
|
struct gpio_irq_chip *irq;
|
||||||
struct sprd_eic *sprd_eic;
|
struct sprd_eic *sprd_eic;
|
||||||
|
struct resource *res;
|
||||||
int ret, i;
|
int ret, i;
|
||||||
|
|
||||||
pdata = of_device_get_match_data(&pdev->dev);
|
pdata = of_device_get_match_data(&pdev->dev);
|
||||||
|
@ -595,9 +596,13 @@ static int sprd_eic_probe(struct platform_device *pdev)
|
||||||
* have one bank EIC, thus base[1] and base[2] can be
|
* have one bank EIC, thus base[1] and base[2] can be
|
||||||
* optional.
|
* optional.
|
||||||
*/
|
*/
|
||||||
sprd_eic->base[i] = devm_platform_ioremap_resource(pdev, i);
|
res = platform_get_resource(pdev, IORESOURCE_MEM, i);
|
||||||
if (IS_ERR(sprd_eic->base[i]))
|
if (!res)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
|
sprd_eic->base[i] = devm_ioremap_resource(&pdev->dev, res);
|
||||||
|
if (IS_ERR(sprd_eic->base[i]))
|
||||||
|
return PTR_ERR(sprd_eic->base[i]);
|
||||||
}
|
}
|
||||||
|
|
||||||
sprd_eic->chip.label = sprd_eic_label_name[sprd_eic->type];
|
sprd_eic->chip.label = sprd_eic_label_name[sprd_eic->type];
|
||||||
|
|
|
@ -0,0 +1,335 @@
|
||||||
|
// SPDX-License-Identifier: GPL-2.0
|
||||||
|
|
||||||
|
#include <linux/acpi.h>
|
||||||
|
#include <linux/bitfield.h>
|
||||||
|
#include <linux/bitops.h>
|
||||||
|
#include <linux/device.h>
|
||||||
|
#include <linux/gpio/driver.h>
|
||||||
|
#include <linux/io.h>
|
||||||
|
#include <linux/ioport.h>
|
||||||
|
#include <linux/kernel.h>
|
||||||
|
#include <linux/module.h>
|
||||||
|
#include <linux/platform_device.h>
|
||||||
|
#include <linux/pm.h>
|
||||||
|
#include <linux/resource.h>
|
||||||
|
#include <linux/spinlock.h>
|
||||||
|
#include <linux/types.h>
|
||||||
|
#include <linux/version.h>
|
||||||
|
|
||||||
|
/*
|
||||||
|
* There are 3 YU GPIO blocks:
|
||||||
|
* gpio[0]: HOST_GPIO0->HOST_GPIO31
|
||||||
|
* gpio[1]: HOST_GPIO32->HOST_GPIO63
|
||||||
|
* gpio[2]: HOST_GPIO64->HOST_GPIO69
|
||||||
|
*/
|
||||||
|
#define MLXBF2_GPIO_MAX_PINS_PER_BLOCK 32
|
||||||
|
|
||||||
|
/*
|
||||||
|
* arm_gpio_lock register:
|
||||||
|
* bit[31] lock status: active if set
|
||||||
|
* bit[15:0] set lock
|
||||||
|
* The lock is enabled only if 0xd42f is written to this field
|
||||||
|
*/
|
||||||
|
#define YU_ARM_GPIO_LOCK_ADDR 0x2801088
|
||||||
|
#define YU_ARM_GPIO_LOCK_SIZE 0x8
|
||||||
|
#define YU_LOCK_ACTIVE_BIT(val) (val >> 31)
|
||||||
|
#define YU_ARM_GPIO_LOCK_ACQUIRE 0xd42f
|
||||||
|
#define YU_ARM_GPIO_LOCK_RELEASE 0x0
|
||||||
|
|
||||||
|
/*
|
||||||
|
* gpio[x] block registers and their offset
|
||||||
|
*/
|
||||||
|
#define YU_GPIO_DATAIN 0x04
|
||||||
|
#define YU_GPIO_MODE1 0x08
|
||||||
|
#define YU_GPIO_MODE0 0x0c
|
||||||
|
#define YU_GPIO_DATASET 0x14
|
||||||
|
#define YU_GPIO_DATACLEAR 0x18
|
||||||
|
#define YU_GPIO_MODE1_CLEAR 0x50
|
||||||
|
#define YU_GPIO_MODE0_SET 0x54
|
||||||
|
#define YU_GPIO_MODE0_CLEAR 0x58
|
||||||
|
|
||||||
|
#ifdef CONFIG_PM
|
||||||
|
struct mlxbf2_gpio_context_save_regs {
|
||||||
|
u32 gpio_mode0;
|
||||||
|
u32 gpio_mode1;
|
||||||
|
};
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* BlueField-2 gpio block context structure. */
|
||||||
|
struct mlxbf2_gpio_context {
|
||||||
|
struct gpio_chip gc;
|
||||||
|
|
||||||
|
/* YU GPIO blocks address */
|
||||||
|
void __iomem *gpio_io;
|
||||||
|
|
||||||
|
#ifdef CONFIG_PM
|
||||||
|
struct mlxbf2_gpio_context_save_regs *csave_regs;
|
||||||
|
#endif
|
||||||
|
};
|
||||||
|
|
||||||
|
/* BlueField-2 gpio shared structure. */
|
||||||
|
struct mlxbf2_gpio_param {
|
||||||
|
void __iomem *io;
|
||||||
|
struct resource *res;
|
||||||
|
struct mutex *lock;
|
||||||
|
};
|
||||||
|
|
||||||
|
static struct resource yu_arm_gpio_lock_res = {
|
||||||
|
.start = YU_ARM_GPIO_LOCK_ADDR,
|
||||||
|
.end = YU_ARM_GPIO_LOCK_ADDR + YU_ARM_GPIO_LOCK_SIZE - 1,
|
||||||
|
.name = "YU_ARM_GPIO_LOCK",
|
||||||
|
};
|
||||||
|
|
||||||
|
static DEFINE_MUTEX(yu_arm_gpio_lock_mutex);
|
||||||
|
|
||||||
|
static struct mlxbf2_gpio_param yu_arm_gpio_lock_param = {
|
||||||
|
.res = &yu_arm_gpio_lock_res,
|
||||||
|
.lock = &yu_arm_gpio_lock_mutex,
|
||||||
|
};
|
||||||
|
|
||||||
|
/* Request memory region and map yu_arm_gpio_lock resource */
|
||||||
|
static int mlxbf2_gpio_get_lock_res(struct platform_device *pdev)
|
||||||
|
{
|
||||||
|
struct device *dev = &pdev->dev;
|
||||||
|
struct resource *res;
|
||||||
|
resource_size_t size;
|
||||||
|
int ret = 0;
|
||||||
|
|
||||||
|
mutex_lock(yu_arm_gpio_lock_param.lock);
|
||||||
|
|
||||||
|
/* Check if the memory map already exists */
|
||||||
|
if (yu_arm_gpio_lock_param.io)
|
||||||
|
goto exit;
|
||||||
|
|
||||||
|
res = yu_arm_gpio_lock_param.res;
|
||||||
|
size = resource_size(res);
|
||||||
|
|
||||||
|
if (!devm_request_mem_region(dev, res->start, size, res->name)) {
|
||||||
|
ret = -EFAULT;
|
||||||
|
goto exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
yu_arm_gpio_lock_param.io = devm_ioremap(dev, res->start, size);
|
||||||
|
if (IS_ERR(yu_arm_gpio_lock_param.io))
|
||||||
|
ret = PTR_ERR(yu_arm_gpio_lock_param.io);
|
||||||
|
|
||||||
|
exit:
|
||||||
|
mutex_unlock(yu_arm_gpio_lock_param.lock);
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Acquire the YU arm_gpio_lock to be able to change the direction
|
||||||
|
* mode. If the lock_active bit is already set, return an error.
|
||||||
|
*/
|
||||||
|
static int mlxbf2_gpio_lock_acquire(struct mlxbf2_gpio_context *gs)
|
||||||
|
{
|
||||||
|
u32 arm_gpio_lock_val;
|
||||||
|
|
||||||
|
spin_lock(&gs->gc.bgpio_lock);
|
||||||
|
mutex_lock(yu_arm_gpio_lock_param.lock);
|
||||||
|
|
||||||
|
arm_gpio_lock_val = readl(yu_arm_gpio_lock_param.io);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* When lock active bit[31] is set, ModeX is write enabled
|
||||||
|
*/
|
||||||
|
if (YU_LOCK_ACTIVE_BIT(arm_gpio_lock_val)) {
|
||||||
|
mutex_unlock(yu_arm_gpio_lock_param.lock);
|
||||||
|
spin_unlock(&gs->gc.bgpio_lock);
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
writel(YU_ARM_GPIO_LOCK_ACQUIRE, yu_arm_gpio_lock_param.io);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Release the YU arm_gpio_lock after changing the direction mode.
|
||||||
|
*/
|
||||||
|
static void mlxbf2_gpio_lock_release(struct mlxbf2_gpio_context *gs)
|
||||||
|
{
|
||||||
|
writel(YU_ARM_GPIO_LOCK_RELEASE, yu_arm_gpio_lock_param.io);
|
||||||
|
mutex_unlock(yu_arm_gpio_lock_param.lock);
|
||||||
|
spin_unlock(&gs->gc.bgpio_lock);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* mode0 and mode1 are both locked by the gpio_lock field.
|
||||||
|
*
|
||||||
|
* Together, mode0 and mode1 define the gpio Mode dependeing also
|
||||||
|
* on Reg_DataOut.
|
||||||
|
*
|
||||||
|
* {mode1,mode0}:{Reg_DataOut=0,Reg_DataOut=1}->{DataOut=0,DataOut=1}
|
||||||
|
*
|
||||||
|
* {0,0}:Reg_DataOut{0,1}->{Z,Z} Input PAD
|
||||||
|
* {0,1}:Reg_DataOut{0,1}->{0,1} Full drive Output PAD
|
||||||
|
* {1,0}:Reg_DataOut{0,1}->{0,Z} 0-set PAD to low, 1-float
|
||||||
|
* {1,1}:Reg_DataOut{0,1}->{Z,1} 0-float, 1-set PAD to high
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Set input direction:
|
||||||
|
* {mode1,mode0} = {0,0}
|
||||||
|
*/
|
||||||
|
static int mlxbf2_gpio_direction_input(struct gpio_chip *chip,
|
||||||
|
unsigned int offset)
|
||||||
|
{
|
||||||
|
struct mlxbf2_gpio_context *gs = gpiochip_get_data(chip);
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Although the arm_gpio_lock was set in the probe function, check again
|
||||||
|
* if it is still enabled to be able to write to the ModeX registers.
|
||||||
|
*/
|
||||||
|
ret = mlxbf2_gpio_lock_acquire(gs);
|
||||||
|
if (ret < 0)
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
writel(BIT(offset), gs->gpio_io + YU_GPIO_MODE0_CLEAR);
|
||||||
|
writel(BIT(offset), gs->gpio_io + YU_GPIO_MODE1_CLEAR);
|
||||||
|
|
||||||
|
mlxbf2_gpio_lock_release(gs);
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Set output direction:
|
||||||
|
* {mode1,mode0} = {0,1}
|
||||||
|
*/
|
||||||
|
static int mlxbf2_gpio_direction_output(struct gpio_chip *chip,
|
||||||
|
unsigned int offset,
|
||||||
|
int value)
|
||||||
|
{
|
||||||
|
struct mlxbf2_gpio_context *gs = gpiochip_get_data(chip);
|
||||||
|
int ret = 0;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Although the arm_gpio_lock was set in the probe function,
|
||||||
|
* check again it is still enabled to be able to write to the
|
||||||
|
* ModeX registers.
|
||||||
|
*/
|
||||||
|
ret = mlxbf2_gpio_lock_acquire(gs);
|
||||||
|
if (ret < 0)
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
writel(BIT(offset), gs->gpio_io + YU_GPIO_MODE1_CLEAR);
|
||||||
|
writel(BIT(offset), gs->gpio_io + YU_GPIO_MODE0_SET);
|
||||||
|
|
||||||
|
mlxbf2_gpio_lock_release(gs);
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* BlueField-2 GPIO driver initialization routine. */
|
||||||
|
static int
|
||||||
|
mlxbf2_gpio_probe(struct platform_device *pdev)
|
||||||
|
{
|
||||||
|
struct mlxbf2_gpio_context *gs;
|
||||||
|
struct device *dev = &pdev->dev;
|
||||||
|
struct gpio_chip *gc;
|
||||||
|
struct resource *res;
|
||||||
|
unsigned int npins;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
gs = devm_kzalloc(dev, sizeof(*gs), GFP_KERNEL);
|
||||||
|
if (!gs)
|
||||||
|
return -ENOMEM;
|
||||||
|
|
||||||
|
/* YU GPIO block address */
|
||||||
|
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||||
|
if (!res)
|
||||||
|
return -ENODEV;
|
||||||
|
|
||||||
|
gs->gpio_io = devm_ioremap(dev, res->start, resource_size(res));
|
||||||
|
if (!gs->gpio_io)
|
||||||
|
return -ENOMEM;
|
||||||
|
|
||||||
|
ret = mlxbf2_gpio_get_lock_res(pdev);
|
||||||
|
if (ret) {
|
||||||
|
dev_err(dev, "Failed to get yu_arm_gpio_lock resource\n");
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (device_property_read_u32(dev, "npins", &npins))
|
||||||
|
npins = MLXBF2_GPIO_MAX_PINS_PER_BLOCK;
|
||||||
|
|
||||||
|
gc = &gs->gc;
|
||||||
|
|
||||||
|
ret = bgpio_init(gc, dev, 4,
|
||||||
|
gs->gpio_io + YU_GPIO_DATAIN,
|
||||||
|
gs->gpio_io + YU_GPIO_DATASET,
|
||||||
|
gs->gpio_io + YU_GPIO_DATACLEAR,
|
||||||
|
NULL,
|
||||||
|
NULL,
|
||||||
|
0);
|
||||||
|
|
||||||
|
gc->direction_input = mlxbf2_gpio_direction_input;
|
||||||
|
gc->direction_output = mlxbf2_gpio_direction_output;
|
||||||
|
gc->ngpio = npins;
|
||||||
|
gc->owner = THIS_MODULE;
|
||||||
|
|
||||||
|
platform_set_drvdata(pdev, gs);
|
||||||
|
|
||||||
|
ret = devm_gpiochip_add_data(dev, &gs->gc, gs);
|
||||||
|
if (ret) {
|
||||||
|
dev_err(dev, "Failed adding memory mapped gpiochip\n");
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef CONFIG_PM
|
||||||
|
static int mlxbf2_gpio_suspend(struct platform_device *pdev,
|
||||||
|
pm_message_t state)
|
||||||
|
{
|
||||||
|
struct mlxbf2_gpio_context *gs = platform_get_drvdata(pdev);
|
||||||
|
|
||||||
|
gs->csave_regs->gpio_mode0 = readl(gs->gpio_io +
|
||||||
|
YU_GPIO_MODE0);
|
||||||
|
gs->csave_regs->gpio_mode1 = readl(gs->gpio_io +
|
||||||
|
YU_GPIO_MODE1);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int mlxbf2_gpio_resume(struct platform_device *pdev)
|
||||||
|
{
|
||||||
|
struct mlxbf2_gpio_context *gs = platform_get_drvdata(pdev);
|
||||||
|
|
||||||
|
writel(gs->csave_regs->gpio_mode0, gs->gpio_io +
|
||||||
|
YU_GPIO_MODE0);
|
||||||
|
writel(gs->csave_regs->gpio_mode1, gs->gpio_io +
|
||||||
|
YU_GPIO_MODE1);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
static const struct acpi_device_id mlxbf2_gpio_acpi_match[] = {
|
||||||
|
{ "MLNXBF22", 0 },
|
||||||
|
{},
|
||||||
|
};
|
||||||
|
MODULE_DEVICE_TABLE(acpi, mlxbf2_gpio_acpi_match);
|
||||||
|
|
||||||
|
static struct platform_driver mlxbf2_gpio_driver = {
|
||||||
|
.driver = {
|
||||||
|
.name = "mlxbf2_gpio",
|
||||||
|
.acpi_match_table = ACPI_PTR(mlxbf2_gpio_acpi_match),
|
||||||
|
},
|
||||||
|
.probe = mlxbf2_gpio_probe,
|
||||||
|
#ifdef CONFIG_PM
|
||||||
|
.suspend = mlxbf2_gpio_suspend,
|
||||||
|
.resume = mlxbf2_gpio_resume,
|
||||||
|
#endif
|
||||||
|
};
|
||||||
|
|
||||||
|
module_platform_driver(mlxbf2_gpio_driver);
|
||||||
|
|
||||||
|
MODULE_DESCRIPTION("Mellanox BlueField-2 GPIO Driver");
|
||||||
|
MODULE_AUTHOR("Mellanox Technologies");
|
||||||
|
MODULE_LICENSE("GPL v2");
|
|
@ -389,12 +389,10 @@ static int bgpio_get_dir(struct gpio_chip *gc, unsigned int gpio)
|
||||||
return GPIO_LINE_DIRECTION_IN;
|
return GPIO_LINE_DIRECTION_IN;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int bgpio_dir_out(struct gpio_chip *gc, unsigned int gpio, int val)
|
static void bgpio_dir_out(struct gpio_chip *gc, unsigned int gpio, int val)
|
||||||
{
|
{
|
||||||
unsigned long flags;
|
unsigned long flags;
|
||||||
|
|
||||||
gc->set(gc, gpio, val);
|
|
||||||
|
|
||||||
spin_lock_irqsave(&gc->bgpio_lock, flags);
|
spin_lock_irqsave(&gc->bgpio_lock, flags);
|
||||||
|
|
||||||
gc->bgpio_dir |= bgpio_line2mask(gc, gpio);
|
gc->bgpio_dir |= bgpio_line2mask(gc, gpio);
|
||||||
|
@ -405,7 +403,21 @@ static int bgpio_dir_out(struct gpio_chip *gc, unsigned int gpio, int val)
|
||||||
gc->write_reg(gc->reg_dir_out, gc->bgpio_dir);
|
gc->write_reg(gc->reg_dir_out, gc->bgpio_dir);
|
||||||
|
|
||||||
spin_unlock_irqrestore(&gc->bgpio_lock, flags);
|
spin_unlock_irqrestore(&gc->bgpio_lock, flags);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int bgpio_dir_out_dir_first(struct gpio_chip *gc, unsigned int gpio,
|
||||||
|
int val)
|
||||||
|
{
|
||||||
|
bgpio_dir_out(gc, gpio, val);
|
||||||
|
gc->set(gc, gpio, val);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int bgpio_dir_out_val_first(struct gpio_chip *gc, unsigned int gpio,
|
||||||
|
int val)
|
||||||
|
{
|
||||||
|
gc->set(gc, gpio, val);
|
||||||
|
bgpio_dir_out(gc, gpio, val);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -538,7 +550,10 @@ static int bgpio_setup_direction(struct gpio_chip *gc,
|
||||||
if (dirout || dirin) {
|
if (dirout || dirin) {
|
||||||
gc->reg_dir_out = dirout;
|
gc->reg_dir_out = dirout;
|
||||||
gc->reg_dir_in = dirin;
|
gc->reg_dir_in = dirin;
|
||||||
gc->direction_output = bgpio_dir_out;
|
if (flags & BGPIOF_NO_SET_ON_INPUT)
|
||||||
|
gc->direction_output = bgpio_dir_out_dir_first;
|
||||||
|
else
|
||||||
|
gc->direction_output = bgpio_dir_out_val_first;
|
||||||
gc->direction_input = bgpio_dir_in;
|
gc->direction_input = bgpio_dir_in;
|
||||||
gc->get_direction = bgpio_get_dir;
|
gc->get_direction = bgpio_get_dir;
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -171,7 +171,7 @@ static int gpio_mockup_apply_pull(struct gpio_mockup_chip *chip,
|
||||||
|
|
||||||
/* Change the value unless we're actively driving the line. */
|
/* Change the value unless we're actively driving the line. */
|
||||||
if (!test_bit(FLAG_REQUESTED, &desc->flags) ||
|
if (!test_bit(FLAG_REQUESTED, &desc->flags) ||
|
||||||
!test_bit(FLAG_IS_OUT, &desc->flags))
|
!test_bit(FLAG_IS_OUT, &desc->flags))
|
||||||
__gpio_mockup_set(chip, offset, value);
|
__gpio_mockup_set(chip, offset, value);
|
||||||
|
|
||||||
out:
|
out:
|
||||||
|
|
|
@ -227,8 +227,8 @@ mediatek_gpio_bank_probe(struct device *dev,
|
||||||
ctrl = mtk->base + GPIO_REG_DCLR + (rg->bank * GPIO_BANK_STRIDE);
|
ctrl = mtk->base + GPIO_REG_DCLR + (rg->bank * GPIO_BANK_STRIDE);
|
||||||
diro = mtk->base + GPIO_REG_CTRL + (rg->bank * GPIO_BANK_STRIDE);
|
diro = mtk->base + GPIO_REG_CTRL + (rg->bank * GPIO_BANK_STRIDE);
|
||||||
|
|
||||||
ret = bgpio_init(&rg->chip, dev, 4,
|
ret = bgpio_init(&rg->chip, dev, 4, dat, set, ctrl, diro, NULL,
|
||||||
dat, set, ctrl, diro, NULL, 0);
|
BGPIOF_NO_SET_ON_INPUT);
|
||||||
if (ret) {
|
if (ret) {
|
||||||
dev_err(dev, "bgpio_init() failed\n");
|
dev_err(dev, "bgpio_init() failed\n");
|
||||||
return ret;
|
return ret;
|
||||||
|
|
|
@ -1247,7 +1247,7 @@ static int mvebu_gpio_probe(struct platform_device *pdev)
|
||||||
* pins.
|
* pins.
|
||||||
*/
|
*/
|
||||||
for (i = 0; i < 4; i++) {
|
for (i = 0; i < 4; i++) {
|
||||||
int irq = platform_get_irq(pdev, i);
|
int irq = platform_get_irq_optional(pdev, i);
|
||||||
|
|
||||||
if (irq < 0)
|
if (irq < 0)
|
||||||
continue;
|
continue;
|
||||||
|
|
|
@ -485,11 +485,8 @@ static int mxc_gpio_probe(struct platform_device *pdev)
|
||||||
if (err)
|
if (err)
|
||||||
goto out_bgio;
|
goto out_bgio;
|
||||||
|
|
||||||
if (of_property_read_bool(np, "gpio-ranges")) {
|
port->gc.request = gpiochip_generic_request;
|
||||||
port->gc.request = gpiochip_generic_request;
|
port->gc.free = gpiochip_generic_free;
|
||||||
port->gc.free = gpiochip_generic_free;
|
|
||||||
}
|
|
||||||
|
|
||||||
port->gc.to_irq = mxc_gpio_to_irq;
|
port->gc.to_irq = mxc_gpio_to_irq;
|
||||||
port->gc.base = (pdev->id < 0) ? of_alias_get_id(np, "gpio") * 32 :
|
port->gc.base = (pdev->id < 0) ? of_alias_get_id(np, "gpio") * 32 :
|
||||||
pdev->id * 32;
|
pdev->id * 32;
|
||||||
|
|
|
@ -1102,23 +1102,13 @@ static void omap_gpio_idle(struct gpio_bank *bank, bool may_lose_context)
|
||||||
{
|
{
|
||||||
struct device *dev = bank->chip.parent;
|
struct device *dev = bank->chip.parent;
|
||||||
void __iomem *base = bank->base;
|
void __iomem *base = bank->base;
|
||||||
u32 mask, nowake;
|
u32 nowake;
|
||||||
|
|
||||||
bank->saved_datain = readl_relaxed(base + bank->regs->datain);
|
bank->saved_datain = readl_relaxed(base + bank->regs->datain);
|
||||||
|
|
||||||
if (!bank->enabled_non_wakeup_gpios)
|
if (!bank->enabled_non_wakeup_gpios)
|
||||||
goto update_gpio_context_count;
|
goto update_gpio_context_count;
|
||||||
|
|
||||||
/* Check for pending EDGE_FALLING, ignore EDGE_BOTH */
|
|
||||||
mask = bank->enabled_non_wakeup_gpios & bank->context.fallingdetect;
|
|
||||||
mask &= ~bank->context.risingdetect;
|
|
||||||
bank->saved_datain |= mask;
|
|
||||||
|
|
||||||
/* Check for pending EDGE_RISING, ignore EDGE_BOTH */
|
|
||||||
mask = bank->enabled_non_wakeup_gpios & bank->context.risingdetect;
|
|
||||||
mask &= ~bank->context.fallingdetect;
|
|
||||||
bank->saved_datain &= ~mask;
|
|
||||||
|
|
||||||
if (!may_lose_context)
|
if (!may_lose_context)
|
||||||
goto update_gpio_context_count;
|
goto update_gpio_context_count;
|
||||||
|
|
||||||
|
@ -1237,26 +1227,35 @@ static int gpio_omap_cpu_notifier(struct notifier_block *nb,
|
||||||
{
|
{
|
||||||
struct gpio_bank *bank;
|
struct gpio_bank *bank;
|
||||||
unsigned long flags;
|
unsigned long flags;
|
||||||
|
int ret = NOTIFY_OK;
|
||||||
|
u32 isr, mask;
|
||||||
|
|
||||||
bank = container_of(nb, struct gpio_bank, nb);
|
bank = container_of(nb, struct gpio_bank, nb);
|
||||||
|
|
||||||
raw_spin_lock_irqsave(&bank->lock, flags);
|
raw_spin_lock_irqsave(&bank->lock, flags);
|
||||||
|
if (bank->is_suspended)
|
||||||
|
goto out_unlock;
|
||||||
|
|
||||||
switch (cmd) {
|
switch (cmd) {
|
||||||
case CPU_CLUSTER_PM_ENTER:
|
case CPU_CLUSTER_PM_ENTER:
|
||||||
if (bank->is_suspended)
|
mask = omap_get_gpio_irqbank_mask(bank);
|
||||||
|
isr = readl_relaxed(bank->base + bank->regs->irqstatus) & mask;
|
||||||
|
if (isr) {
|
||||||
|
ret = NOTIFY_BAD;
|
||||||
break;
|
break;
|
||||||
|
}
|
||||||
omap_gpio_idle(bank, true);
|
omap_gpio_idle(bank, true);
|
||||||
break;
|
break;
|
||||||
case CPU_CLUSTER_PM_ENTER_FAILED:
|
case CPU_CLUSTER_PM_ENTER_FAILED:
|
||||||
case CPU_CLUSTER_PM_EXIT:
|
case CPU_CLUSTER_PM_EXIT:
|
||||||
if (bank->is_suspended)
|
|
||||||
break;
|
|
||||||
omap_gpio_unidle(bank);
|
omap_gpio_unidle(bank);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
out_unlock:
|
||||||
raw_spin_unlock_irqrestore(&bank->lock, flags);
|
raw_spin_unlock_irqrestore(&bank->lock, flags);
|
||||||
|
|
||||||
return NOTIFY_OK;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
static const struct omap_gpio_reg_offs omap2_gpio_regs = {
|
static const struct omap_gpio_reg_offs omap2_gpio_regs = {
|
||||||
|
|
|
@ -298,11 +298,8 @@ static int pl061_probe(struct amba_device *adev, const struct amba_id *id)
|
||||||
return PTR_ERR(pl061->base);
|
return PTR_ERR(pl061->base);
|
||||||
|
|
||||||
raw_spin_lock_init(&pl061->lock);
|
raw_spin_lock_init(&pl061->lock);
|
||||||
if (of_property_read_bool(dev->of_node, "gpio-ranges")) {
|
pl061->gc.request = gpiochip_generic_request;
|
||||||
pl061->gc.request = gpiochip_generic_request;
|
pl061->gc.free = gpiochip_generic_free;
|
||||||
pl061->gc.free = gpiochip_generic_free;
|
|
||||||
}
|
|
||||||
|
|
||||||
pl061->gc.base = -1;
|
pl061->gc.base = -1;
|
||||||
pl061->gc.get_direction = pl061_get_direction;
|
pl061->gc.get_direction = pl061_get_direction;
|
||||||
pl061->gc.direction_input = pl061_direction_input;
|
pl061->gc.direction_input = pl061_direction_input;
|
||||||
|
@ -326,10 +323,8 @@ static int pl061_probe(struct amba_device *adev, const struct amba_id *id)
|
||||||
|
|
||||||
writeb(0, pl061->base + GPIOIE); /* disable irqs */
|
writeb(0, pl061->base + GPIOIE); /* disable irqs */
|
||||||
irq = adev->irq[0];
|
irq = adev->irq[0];
|
||||||
if (irq < 0) {
|
if (!irq)
|
||||||
dev_err(&adev->dev, "invalid IRQ\n");
|
dev_warn(&adev->dev, "IRQ support disabled\n");
|
||||||
return -ENODEV;
|
|
||||||
}
|
|
||||||
pl061->parent_irq = irq;
|
pl061->parent_irq = irq;
|
||||||
|
|
||||||
girq = &pl061->gc.irq;
|
girq = &pl061->gc.irq;
|
||||||
|
|
|
@ -361,11 +361,8 @@ static int pxa_init_gpio_chip(struct pxa_gpio_chip *pchip, int ngpio,
|
||||||
pchip->chip.set = pxa_gpio_set;
|
pchip->chip.set = pxa_gpio_set;
|
||||||
pchip->chip.to_irq = pxa_gpio_to_irq;
|
pchip->chip.to_irq = pxa_gpio_to_irq;
|
||||||
pchip->chip.ngpio = ngpio;
|
pchip->chip.ngpio = ngpio;
|
||||||
|
pchip->chip.request = gpiochip_generic_request;
|
||||||
if (pxa_gpio_has_pinctrl()) {
|
pchip->chip.free = gpiochip_generic_free;
|
||||||
pchip->chip.request = gpiochip_generic_request;
|
|
||||||
pchip->chip.free = gpiochip_generic_free;
|
|
||||||
}
|
|
||||||
|
|
||||||
#ifdef CONFIG_OF_GPIO
|
#ifdef CONFIG_OF_GPIO
|
||||||
pchip->chip.of_node = np;
|
pchip->chip.of_node = np;
|
||||||
|
@ -652,8 +649,8 @@ static int pxa_gpio_probe(struct platform_device *pdev)
|
||||||
if (!pchip->irqdomain)
|
if (!pchip->irqdomain)
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
|
|
||||||
irq0 = platform_get_irq_byname(pdev, "gpio0");
|
irq0 = platform_get_irq_byname_optional(pdev, "gpio0");
|
||||||
irq1 = platform_get_irq_byname(pdev, "gpio1");
|
irq1 = platform_get_irq_byname_optional(pdev, "gpio1");
|
||||||
irq_mux = platform_get_irq_byname(pdev, "gpio_mux");
|
irq_mux = platform_get_irq_byname(pdev, "gpio_mux");
|
||||||
if ((irq0 > 0 && irq1 <= 0) || (irq0 <= 0 && irq1 > 0)
|
if ((irq0 > 0 && irq1 <= 0) || (irq0 <= 0 && irq1 > 0)
|
||||||
|| (irq_mux <= 0))
|
|| (irq_mux <= 0))
|
||||||
|
|
|
@ -116,7 +116,7 @@ static void gpio_rcar_config_interrupt_input_mode(struct gpio_rcar_priv *p,
|
||||||
|
|
||||||
spin_lock_irqsave(&p->lock, flags);
|
spin_lock_irqsave(&p->lock, flags);
|
||||||
|
|
||||||
/* Configure postive or negative logic in POSNEG */
|
/* Configure positive or negative logic in POSNEG */
|
||||||
gpio_rcar_modify_bit(p, POSNEG, hwirq, !active_high_rising_edge);
|
gpio_rcar_modify_bit(p, POSNEG, hwirq, !active_high_rising_edge);
|
||||||
|
|
||||||
/* Configure edge or level trigger in EDGLEVEL */
|
/* Configure edge or level trigger in EDGLEVEL */
|
||||||
|
@ -228,7 +228,7 @@ static void gpio_rcar_config_general_input_output_mode(struct gpio_chip *chip,
|
||||||
|
|
||||||
spin_lock_irqsave(&p->lock, flags);
|
spin_lock_irqsave(&p->lock, flags);
|
||||||
|
|
||||||
/* Configure postive logic in POSNEG */
|
/* Configure positive logic in POSNEG */
|
||||||
gpio_rcar_modify_bit(p, POSNEG, gpio, false);
|
gpio_rcar_modify_bit(p, POSNEG, gpio, false);
|
||||||
|
|
||||||
/* Select "General Input/Output Mode" in IOINTSEL */
|
/* Select "General Input/Output Mode" in IOINTSEL */
|
||||||
|
|
|
@ -15,7 +15,7 @@ struct gpio_siox_ddata {
|
||||||
u8 setdata[1];
|
u8 setdata[1];
|
||||||
u8 getdata[3];
|
u8 getdata[3];
|
||||||
|
|
||||||
spinlock_t irqlock;
|
raw_spinlock_t irqlock;
|
||||||
u32 irq_enable;
|
u32 irq_enable;
|
||||||
u32 irq_status;
|
u32 irq_status;
|
||||||
u32 irq_type[20];
|
u32 irq_type[20];
|
||||||
|
@ -44,7 +44,7 @@ static int gpio_siox_get_data(struct siox_device *sdevice, const u8 buf[])
|
||||||
|
|
||||||
mutex_lock(&ddata->lock);
|
mutex_lock(&ddata->lock);
|
||||||
|
|
||||||
spin_lock_irq(&ddata->irqlock);
|
raw_spin_lock_irq(&ddata->irqlock);
|
||||||
|
|
||||||
for (offset = 0; offset < 12; ++offset) {
|
for (offset = 0; offset < 12; ++offset) {
|
||||||
unsigned int bitpos = 11 - offset;
|
unsigned int bitpos = 11 - offset;
|
||||||
|
@ -66,7 +66,7 @@ static int gpio_siox_get_data(struct siox_device *sdevice, const u8 buf[])
|
||||||
|
|
||||||
trigger = ddata->irq_status & ddata->irq_enable;
|
trigger = ddata->irq_status & ddata->irq_enable;
|
||||||
|
|
||||||
spin_unlock_irq(&ddata->irqlock);
|
raw_spin_unlock_irq(&ddata->irqlock);
|
||||||
|
|
||||||
ddata->getdata[0] = buf[0];
|
ddata->getdata[0] = buf[0];
|
||||||
ddata->getdata[1] = buf[1];
|
ddata->getdata[1] = buf[1];
|
||||||
|
@ -84,9 +84,9 @@ static int gpio_siox_get_data(struct siox_device *sdevice, const u8 buf[])
|
||||||
* handler of the irq chip. But it doesn't, so we have
|
* handler of the irq chip. But it doesn't, so we have
|
||||||
* to clean the irq_status here.
|
* to clean the irq_status here.
|
||||||
*/
|
*/
|
||||||
spin_lock_irq(&ddata->irqlock);
|
raw_spin_lock_irq(&ddata->irqlock);
|
||||||
ddata->irq_status &= ~(1 << offset);
|
ddata->irq_status &= ~(1 << offset);
|
||||||
spin_unlock_irq(&ddata->irqlock);
|
raw_spin_unlock_irq(&ddata->irqlock);
|
||||||
|
|
||||||
handle_nested_irq(irq);
|
handle_nested_irq(irq);
|
||||||
}
|
}
|
||||||
|
@ -101,9 +101,9 @@ static void gpio_siox_irq_ack(struct irq_data *d)
|
||||||
struct gpio_siox_ddata *ddata =
|
struct gpio_siox_ddata *ddata =
|
||||||
container_of(ic, struct gpio_siox_ddata, ichip);
|
container_of(ic, struct gpio_siox_ddata, ichip);
|
||||||
|
|
||||||
spin_lock_irq(&ddata->irqlock);
|
raw_spin_lock(&ddata->irqlock);
|
||||||
ddata->irq_status &= ~(1 << d->hwirq);
|
ddata->irq_status &= ~(1 << d->hwirq);
|
||||||
spin_unlock_irq(&ddata->irqlock);
|
raw_spin_unlock(&ddata->irqlock);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void gpio_siox_irq_mask(struct irq_data *d)
|
static void gpio_siox_irq_mask(struct irq_data *d)
|
||||||
|
@ -112,9 +112,9 @@ static void gpio_siox_irq_mask(struct irq_data *d)
|
||||||
struct gpio_siox_ddata *ddata =
|
struct gpio_siox_ddata *ddata =
|
||||||
container_of(ic, struct gpio_siox_ddata, ichip);
|
container_of(ic, struct gpio_siox_ddata, ichip);
|
||||||
|
|
||||||
spin_lock_irq(&ddata->irqlock);
|
raw_spin_lock(&ddata->irqlock);
|
||||||
ddata->irq_enable &= ~(1 << d->hwirq);
|
ddata->irq_enable &= ~(1 << d->hwirq);
|
||||||
spin_unlock_irq(&ddata->irqlock);
|
raw_spin_unlock(&ddata->irqlock);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void gpio_siox_irq_unmask(struct irq_data *d)
|
static void gpio_siox_irq_unmask(struct irq_data *d)
|
||||||
|
@ -123,9 +123,9 @@ static void gpio_siox_irq_unmask(struct irq_data *d)
|
||||||
struct gpio_siox_ddata *ddata =
|
struct gpio_siox_ddata *ddata =
|
||||||
container_of(ic, struct gpio_siox_ddata, ichip);
|
container_of(ic, struct gpio_siox_ddata, ichip);
|
||||||
|
|
||||||
spin_lock_irq(&ddata->irqlock);
|
raw_spin_lock(&ddata->irqlock);
|
||||||
ddata->irq_enable |= 1 << d->hwirq;
|
ddata->irq_enable |= 1 << d->hwirq;
|
||||||
spin_unlock_irq(&ddata->irqlock);
|
raw_spin_unlock(&ddata->irqlock);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int gpio_siox_irq_set_type(struct irq_data *d, u32 type)
|
static int gpio_siox_irq_set_type(struct irq_data *d, u32 type)
|
||||||
|
@ -134,9 +134,9 @@ static int gpio_siox_irq_set_type(struct irq_data *d, u32 type)
|
||||||
struct gpio_siox_ddata *ddata =
|
struct gpio_siox_ddata *ddata =
|
||||||
container_of(ic, struct gpio_siox_ddata, ichip);
|
container_of(ic, struct gpio_siox_ddata, ichip);
|
||||||
|
|
||||||
spin_lock_irq(&ddata->irqlock);
|
raw_spin_lock(&ddata->irqlock);
|
||||||
ddata->irq_type[d->hwirq] = type;
|
ddata->irq_type[d->hwirq] = type;
|
||||||
spin_unlock_irq(&ddata->irqlock);
|
raw_spin_unlock(&ddata->irqlock);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
@ -222,7 +222,7 @@ static int gpio_siox_probe(struct siox_device *sdevice)
|
||||||
dev_set_drvdata(dev, ddata);
|
dev_set_drvdata(dev, ddata);
|
||||||
|
|
||||||
mutex_init(&ddata->lock);
|
mutex_init(&ddata->lock);
|
||||||
spin_lock_init(&ddata->irqlock);
|
raw_spin_lock_init(&ddata->irqlock);
|
||||||
|
|
||||||
ddata->gchip.base = -1;
|
ddata->gchip.base = -1;
|
||||||
ddata->gchip.can_sleep = 1;
|
ddata->gchip.can_sleep = 1;
|
||||||
|
|
|
@ -58,11 +58,20 @@ struct tegra_gpio_port {
|
||||||
unsigned int pins;
|
unsigned int pins;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct tegra186_pin_range {
|
||||||
|
unsigned int offset;
|
||||||
|
const char *group;
|
||||||
|
};
|
||||||
|
|
||||||
struct tegra_gpio_soc {
|
struct tegra_gpio_soc {
|
||||||
const struct tegra_gpio_port *ports;
|
const struct tegra_gpio_port *ports;
|
||||||
unsigned int num_ports;
|
unsigned int num_ports;
|
||||||
const char *name;
|
const char *name;
|
||||||
unsigned int instance;
|
unsigned int instance;
|
||||||
|
|
||||||
|
const struct tegra186_pin_range *pin_ranges;
|
||||||
|
unsigned int num_pin_ranges;
|
||||||
|
const char *pinmux;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct tegra_gpio {
|
struct tegra_gpio {
|
||||||
|
@ -254,6 +263,50 @@ static int tegra186_gpio_set_config(struct gpio_chip *chip,
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int tegra186_gpio_add_pin_ranges(struct gpio_chip *chip)
|
||||||
|
{
|
||||||
|
struct tegra_gpio *gpio = gpiochip_get_data(chip);
|
||||||
|
struct pinctrl_dev *pctldev;
|
||||||
|
struct device_node *np;
|
||||||
|
unsigned int i, j;
|
||||||
|
int err;
|
||||||
|
|
||||||
|
if (!gpio->soc->pinmux || gpio->soc->num_pin_ranges == 0)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
np = of_find_compatible_node(NULL, NULL, gpio->soc->pinmux);
|
||||||
|
if (!np)
|
||||||
|
return -ENODEV;
|
||||||
|
|
||||||
|
pctldev = of_pinctrl_get(np);
|
||||||
|
of_node_put(np);
|
||||||
|
if (!pctldev)
|
||||||
|
return -EPROBE_DEFER;
|
||||||
|
|
||||||
|
for (i = 0; i < gpio->soc->num_pin_ranges; i++) {
|
||||||
|
unsigned int pin = gpio->soc->pin_ranges[i].offset, port;
|
||||||
|
const char *group = gpio->soc->pin_ranges[i].group;
|
||||||
|
|
||||||
|
port = pin / 8;
|
||||||
|
pin = pin % 8;
|
||||||
|
|
||||||
|
if (port >= gpio->soc->num_ports) {
|
||||||
|
dev_warn(chip->parent, "invalid port %u for %s\n",
|
||||||
|
port, group);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (j = 0; j < port; j++)
|
||||||
|
pin += gpio->soc->ports[j].pins;
|
||||||
|
|
||||||
|
err = gpiochip_add_pingroup_range(chip, pctldev, pin, group);
|
||||||
|
if (err < 0)
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
static int tegra186_gpio_of_xlate(struct gpio_chip *chip,
|
static int tegra186_gpio_of_xlate(struct gpio_chip *chip,
|
||||||
const struct of_phandle_args *spec,
|
const struct of_phandle_args *spec,
|
||||||
u32 *flags)
|
u32 *flags)
|
||||||
|
@ -578,12 +631,15 @@ static int tegra186_gpio_probe(struct platform_device *pdev)
|
||||||
gpio->gpio.label = gpio->soc->name;
|
gpio->gpio.label = gpio->soc->name;
|
||||||
gpio->gpio.parent = &pdev->dev;
|
gpio->gpio.parent = &pdev->dev;
|
||||||
|
|
||||||
|
gpio->gpio.request = gpiochip_generic_request;
|
||||||
|
gpio->gpio.free = gpiochip_generic_free;
|
||||||
gpio->gpio.get_direction = tegra186_gpio_get_direction;
|
gpio->gpio.get_direction = tegra186_gpio_get_direction;
|
||||||
gpio->gpio.direction_input = tegra186_gpio_direction_input;
|
gpio->gpio.direction_input = tegra186_gpio_direction_input;
|
||||||
gpio->gpio.direction_output = tegra186_gpio_direction_output;
|
gpio->gpio.direction_output = tegra186_gpio_direction_output;
|
||||||
gpio->gpio.get = tegra186_gpio_get,
|
gpio->gpio.get = tegra186_gpio_get,
|
||||||
gpio->gpio.set = tegra186_gpio_set;
|
gpio->gpio.set = tegra186_gpio_set;
|
||||||
gpio->gpio.set_config = tegra186_gpio_set_config;
|
gpio->gpio.set_config = tegra186_gpio_set_config;
|
||||||
|
gpio->gpio.add_pin_ranges = tegra186_gpio_add_pin_ranges;
|
||||||
|
|
||||||
gpio->gpio.base = -1;
|
gpio->gpio.base = -1;
|
||||||
|
|
||||||
|
@ -783,11 +839,19 @@ static const struct tegra_gpio_port tegra194_main_ports[] = {
|
||||||
TEGRA194_MAIN_GPIO_PORT(GG, 0, 0, 2)
|
TEGRA194_MAIN_GPIO_PORT(GG, 0, 0, 2)
|
||||||
};
|
};
|
||||||
|
|
||||||
|
static const struct tegra186_pin_range tegra194_main_pin_ranges[] = {
|
||||||
|
{ TEGRA194_MAIN_GPIO(GG, 0), "pex_l5_clkreq_n_pgg0" },
|
||||||
|
{ TEGRA194_MAIN_GPIO(GG, 1), "pex_l5_rst_n_pgg1" },
|
||||||
|
};
|
||||||
|
|
||||||
static const struct tegra_gpio_soc tegra194_main_soc = {
|
static const struct tegra_gpio_soc tegra194_main_soc = {
|
||||||
.num_ports = ARRAY_SIZE(tegra194_main_ports),
|
.num_ports = ARRAY_SIZE(tegra194_main_ports),
|
||||||
.ports = tegra194_main_ports,
|
.ports = tegra194_main_ports,
|
||||||
.name = "tegra194-gpio",
|
.name = "tegra194-gpio",
|
||||||
.instance = 0,
|
.instance = 0,
|
||||||
|
.num_pin_ranges = ARRAY_SIZE(tegra194_main_pin_ranges),
|
||||||
|
.pin_ranges = tegra194_main_pin_ranges,
|
||||||
|
.pinmux = "nvidia,tegra194-pinmux",
|
||||||
};
|
};
|
||||||
|
|
||||||
#define TEGRA194_AON_GPIO_PORT(_name, _bank, _port, _pins) \
|
#define TEGRA194_AON_GPIO_PORT(_name, _bank, _port, _pins) \
|
||||||
|
|
|
@ -30,7 +30,7 @@ struct uniphier_gpio_priv {
|
||||||
struct irq_domain *domain;
|
struct irq_domain *domain;
|
||||||
void __iomem *regs;
|
void __iomem *regs;
|
||||||
spinlock_t lock;
|
spinlock_t lock;
|
||||||
u32 saved_vals[0];
|
u32 saved_vals[];
|
||||||
};
|
};
|
||||||
|
|
||||||
static unsigned int uniphier_gpio_bank_to_reg(unsigned int bank)
|
static unsigned int uniphier_gpio_bank_to_reg(unsigned int bank)
|
||||||
|
|
|
@ -57,16 +57,19 @@ static int wcd_gpio_direction_output(struct gpio_chip *chip, unsigned int pin,
|
||||||
static int wcd_gpio_get(struct gpio_chip *chip, unsigned int pin)
|
static int wcd_gpio_get(struct gpio_chip *chip, unsigned int pin)
|
||||||
{
|
{
|
||||||
struct wcd_gpio_data *data = gpiochip_get_data(chip);
|
struct wcd_gpio_data *data = gpiochip_get_data(chip);
|
||||||
int value;
|
unsigned int value;
|
||||||
|
|
||||||
regmap_read(data->map, WCD_REG_VAL_CTL_OFFSET, &value);
|
regmap_read(data->map, WCD_REG_VAL_CTL_OFFSET, &value);
|
||||||
|
|
||||||
return !!(value && WCD_PIN_MASK(pin));
|
return !!(value & WCD_PIN_MASK(pin));
|
||||||
}
|
}
|
||||||
|
|
||||||
static void wcd_gpio_set(struct gpio_chip *chip, unsigned int pin, int val)
|
static void wcd_gpio_set(struct gpio_chip *chip, unsigned int pin, int val)
|
||||||
{
|
{
|
||||||
wcd_gpio_direction_output(chip, pin, val);
|
struct wcd_gpio_data *data = gpiochip_get_data(chip);
|
||||||
|
|
||||||
|
regmap_update_bits(data->map, WCD_REG_VAL_CTL_OFFSET,
|
||||||
|
WCD_PIN_MASK(pin), val ? WCD_PIN_MASK(pin) : 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int wcd_gpio_probe(struct platform_device *pdev)
|
static int wcd_gpio_probe(struct platform_device *pdev)
|
||||||
|
|
|
@ -226,13 +226,11 @@ static int zx_gpio_probe(struct platform_device *pdev)
|
||||||
if (IS_ERR(chip->base))
|
if (IS_ERR(chip->base))
|
||||||
return PTR_ERR(chip->base);
|
return PTR_ERR(chip->base);
|
||||||
|
|
||||||
raw_spin_lock_init(&chip->lock);
|
|
||||||
if (of_property_read_bool(dev->of_node, "gpio-ranges")) {
|
|
||||||
chip->gc.request = gpiochip_generic_request;
|
|
||||||
chip->gc.free = gpiochip_generic_free;
|
|
||||||
}
|
|
||||||
|
|
||||||
id = of_alias_get_id(dev->of_node, "gpio");
|
id = of_alias_get_id(dev->of_node, "gpio");
|
||||||
|
|
||||||
|
raw_spin_lock_init(&chip->lock);
|
||||||
|
chip->gc.request = gpiochip_generic_request;
|
||||||
|
chip->gc.free = gpiochip_generic_free;
|
||||||
chip->gc.direction_input = zx_direction_input;
|
chip->gc.direction_input = zx_direction_input;
|
||||||
chip->gc.direction_output = zx_direction_output;
|
chip->gc.direction_output = zx_direction_output;
|
||||||
chip->gc.get = zx_get_value;
|
chip->gc.get = zx_get_value;
|
||||||
|
|
|
@ -478,3 +478,49 @@ void devm_gpio_free(struct device *dev, unsigned int gpio)
|
||||||
&gpio));
|
&gpio));
|
||||||
}
|
}
|
||||||
EXPORT_SYMBOL_GPL(devm_gpio_free);
|
EXPORT_SYMBOL_GPL(devm_gpio_free);
|
||||||
|
|
||||||
|
static void devm_gpio_chip_release(struct device *dev, void *res)
|
||||||
|
{
|
||||||
|
struct gpio_chip *gc = *(struct gpio_chip **)res;
|
||||||
|
|
||||||
|
gpiochip_remove(gc);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* devm_gpiochip_add_data() - Resource managed gpiochip_add_data()
|
||||||
|
* @dev: pointer to the device that gpio_chip belongs to.
|
||||||
|
* @gc: the GPIO chip to register
|
||||||
|
* @data: driver-private data associated with this chip
|
||||||
|
*
|
||||||
|
* Context: potentially before irqs will work
|
||||||
|
*
|
||||||
|
* The gpio chip automatically be released when the device is unbound.
|
||||||
|
*
|
||||||
|
* Returns:
|
||||||
|
* A negative errno if the chip can't be registered, such as because the
|
||||||
|
* gc->base is invalid or already associated with a different chip.
|
||||||
|
* Otherwise it returns zero as a success code.
|
||||||
|
*/
|
||||||
|
int devm_gpiochip_add_data(struct device *dev, struct gpio_chip *gc,
|
||||||
|
void *data)
|
||||||
|
{
|
||||||
|
struct gpio_chip **ptr;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
ptr = devres_alloc(devm_gpio_chip_release, sizeof(*ptr),
|
||||||
|
GFP_KERNEL);
|
||||||
|
if (!ptr)
|
||||||
|
return -ENOMEM;
|
||||||
|
|
||||||
|
ret = gpiochip_add_data(gc, data);
|
||||||
|
if (ret < 0) {
|
||||||
|
devres_free(ptr);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
*ptr = gc;
|
||||||
|
devres_add(dev, ptr);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
EXPORT_SYMBOL_GPL(devm_gpiochip_add_data);
|
||||||
|
|
|
@ -604,6 +604,39 @@ static struct gpio_desc *of_parse_own_gpio(struct device_node *np,
|
||||||
return desc;
|
return desc;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* of_gpiochip_add_hog - Add all hogs in a hog device node
|
||||||
|
* @chip: gpio chip to act on
|
||||||
|
* @hog: device node describing the hogs
|
||||||
|
*
|
||||||
|
* Returns error if it fails otherwise 0 on success.
|
||||||
|
*/
|
||||||
|
static int of_gpiochip_add_hog(struct gpio_chip *chip, struct device_node *hog)
|
||||||
|
{
|
||||||
|
enum gpiod_flags dflags;
|
||||||
|
struct gpio_desc *desc;
|
||||||
|
unsigned long lflags;
|
||||||
|
const char *name;
|
||||||
|
unsigned int i;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
for (i = 0;; i++) {
|
||||||
|
desc = of_parse_own_gpio(hog, chip, i, &name, &lflags, &dflags);
|
||||||
|
if (IS_ERR(desc))
|
||||||
|
break;
|
||||||
|
|
||||||
|
ret = gpiod_hog(desc, name, lflags, dflags);
|
||||||
|
if (ret < 0)
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
#ifdef CONFIG_OF_DYNAMIC
|
||||||
|
desc->hog = hog;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* of_gpiochip_scan_gpios - Scan gpio-controller for gpio definitions
|
* of_gpiochip_scan_gpios - Scan gpio-controller for gpio definitions
|
||||||
* @chip: gpio chip to act on
|
* @chip: gpio chip to act on
|
||||||
|
@ -614,35 +647,109 @@ static struct gpio_desc *of_parse_own_gpio(struct device_node *np,
|
||||||
*/
|
*/
|
||||||
static int of_gpiochip_scan_gpios(struct gpio_chip *chip)
|
static int of_gpiochip_scan_gpios(struct gpio_chip *chip)
|
||||||
{
|
{
|
||||||
struct gpio_desc *desc = NULL;
|
|
||||||
struct device_node *np;
|
struct device_node *np;
|
||||||
const char *name;
|
|
||||||
unsigned long lflags;
|
|
||||||
enum gpiod_flags dflags;
|
|
||||||
unsigned int i;
|
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
for_each_available_child_of_node(chip->of_node, np) {
|
for_each_available_child_of_node(chip->of_node, np) {
|
||||||
if (!of_property_read_bool(np, "gpio-hog"))
|
if (!of_property_read_bool(np, "gpio-hog"))
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
for (i = 0;; i++) {
|
ret = of_gpiochip_add_hog(chip, np);
|
||||||
desc = of_parse_own_gpio(np, chip, i, &name, &lflags,
|
if (ret < 0) {
|
||||||
&dflags);
|
of_node_put(np);
|
||||||
if (IS_ERR(desc))
|
return ret;
|
||||||
break;
|
|
||||||
|
|
||||||
ret = gpiod_hog(desc, name, lflags, dflags);
|
|
||||||
if (ret < 0) {
|
|
||||||
of_node_put(np);
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
of_node_set_flag(np, OF_POPULATED);
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifdef CONFIG_OF_DYNAMIC
|
||||||
|
/**
|
||||||
|
* of_gpiochip_remove_hog - Remove all hogs in a hog device node
|
||||||
|
* @chip: gpio chip to act on
|
||||||
|
* @hog: device node describing the hogs
|
||||||
|
*/
|
||||||
|
static void of_gpiochip_remove_hog(struct gpio_chip *chip,
|
||||||
|
struct device_node *hog)
|
||||||
|
{
|
||||||
|
struct gpio_desc *descs = chip->gpiodev->descs;
|
||||||
|
unsigned int i;
|
||||||
|
|
||||||
|
for (i = 0; i < chip->ngpio; i++) {
|
||||||
|
if (test_bit(FLAG_IS_HOGGED, &descs[i].flags) &&
|
||||||
|
descs[i].hog == hog)
|
||||||
|
gpiochip_free_own_desc(&descs[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static int of_gpiochip_match_node(struct gpio_chip *chip, void *data)
|
||||||
|
{
|
||||||
|
return chip->gpiodev->dev.of_node == data;
|
||||||
|
}
|
||||||
|
|
||||||
|
static struct gpio_chip *of_find_gpiochip_by_node(struct device_node *np)
|
||||||
|
{
|
||||||
|
return gpiochip_find(np, of_gpiochip_match_node);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int of_gpio_notify(struct notifier_block *nb, unsigned long action,
|
||||||
|
void *arg)
|
||||||
|
{
|
||||||
|
struct of_reconfig_data *rd = arg;
|
||||||
|
struct gpio_chip *chip;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This only supports adding and removing complete gpio-hog nodes.
|
||||||
|
* Modifying an existing gpio-hog node is not supported (except for
|
||||||
|
* changing its "status" property, which is treated the same as
|
||||||
|
* addition/removal).
|
||||||
|
*/
|
||||||
|
switch (of_reconfig_get_state_change(action, arg)) {
|
||||||
|
case OF_RECONFIG_CHANGE_ADD:
|
||||||
|
if (!of_property_read_bool(rd->dn, "gpio-hog"))
|
||||||
|
return NOTIFY_OK; /* not for us */
|
||||||
|
|
||||||
|
if (of_node_test_and_set_flag(rd->dn, OF_POPULATED))
|
||||||
|
return NOTIFY_OK;
|
||||||
|
|
||||||
|
chip = of_find_gpiochip_by_node(rd->dn->parent);
|
||||||
|
if (chip == NULL)
|
||||||
|
return NOTIFY_OK; /* not for us */
|
||||||
|
|
||||||
|
ret = of_gpiochip_add_hog(chip, rd->dn);
|
||||||
|
if (ret < 0) {
|
||||||
|
pr_err("%s: failed to add hogs for %pOF\n", __func__,
|
||||||
|
rd->dn);
|
||||||
|
of_node_clear_flag(rd->dn, OF_POPULATED);
|
||||||
|
return notifier_from_errno(ret);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case OF_RECONFIG_CHANGE_REMOVE:
|
||||||
|
if (!of_node_check_flag(rd->dn, OF_POPULATED))
|
||||||
|
return NOTIFY_OK; /* already depopulated */
|
||||||
|
|
||||||
|
chip = of_find_gpiochip_by_node(rd->dn->parent);
|
||||||
|
if (chip == NULL)
|
||||||
|
return NOTIFY_OK; /* not for us */
|
||||||
|
|
||||||
|
of_gpiochip_remove_hog(chip, rd->dn);
|
||||||
|
of_node_clear_flag(rd->dn, OF_POPULATED);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return NOTIFY_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct notifier_block gpio_of_notifier = {
|
||||||
|
.notifier_call = of_gpio_notify,
|
||||||
|
};
|
||||||
|
#endif /* CONFIG_OF_DYNAMIC */
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* of_gpio_simple_xlate - translate gpiospec to the GPIO number and flags
|
* of_gpio_simple_xlate - translate gpiospec to the GPIO number and flags
|
||||||
* @gc: pointer to the gpio_chip structure
|
* @gc: pointer to the gpio_chip structure
|
||||||
|
|
|
@ -35,4 +35,6 @@ static inline bool of_gpio_need_valid_mask(const struct gpio_chip *gc)
|
||||||
}
|
}
|
||||||
#endif /* CONFIG_OF_GPIO */
|
#endif /* CONFIG_OF_GPIO */
|
||||||
|
|
||||||
|
extern struct notifier_block gpio_of_notifier;
|
||||||
|
|
||||||
#endif /* GPIOLIB_OF_H */
|
#endif /* GPIOLIB_OF_H */
|
||||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -56,6 +56,7 @@ struct gpio_device {
|
||||||
const char *label;
|
const char *label;
|
||||||
void *data;
|
void *data;
|
||||||
struct list_head list;
|
struct list_head list;
|
||||||
|
struct atomic_notifier_head notifier;
|
||||||
|
|
||||||
#ifdef CONFIG_PINCTRL
|
#ifdef CONFIG_PINCTRL
|
||||||
/*
|
/*
|
||||||
|
@ -119,6 +120,9 @@ struct gpio_desc {
|
||||||
const char *label;
|
const char *label;
|
||||||
/* Name of the GPIO */
|
/* Name of the GPIO */
|
||||||
const char *name;
|
const char *name;
|
||||||
|
#ifdef CONFIG_OF_DYNAMIC
|
||||||
|
struct device_node *hog;
|
||||||
|
#endif
|
||||||
};
|
};
|
||||||
|
|
||||||
int gpiod_request(struct gpio_desc *desc, const char *label);
|
int gpiod_request(struct gpio_desc *desc, const char *label);
|
||||||
|
|
|
@ -103,6 +103,7 @@ struct pinctrl_dev *of_pinctrl_get(struct device_node *np)
|
||||||
{
|
{
|
||||||
return get_pinctrl_dev_from_of_node(np);
|
return get_pinctrl_dev_from_of_node(np);
|
||||||
}
|
}
|
||||||
|
EXPORT_SYMBOL_GPL(of_pinctrl_get);
|
||||||
|
|
||||||
static int dt_to_map_one_config(struct pinctrl *p,
|
static int dt_to_map_one_config(struct pinctrl *p,
|
||||||
struct pinctrl_dev *hog_pctldev,
|
struct pinctrl_dev *hog_pctldev,
|
||||||
|
|
|
@ -2323,11 +2323,8 @@ static int __init ingenic_gpio_probe(struct ingenic_pinctrl *jzpc,
|
||||||
jzgc->gc.direction_input = ingenic_gpio_direction_input;
|
jzgc->gc.direction_input = ingenic_gpio_direction_input;
|
||||||
jzgc->gc.direction_output = ingenic_gpio_direction_output;
|
jzgc->gc.direction_output = ingenic_gpio_direction_output;
|
||||||
jzgc->gc.get_direction = ingenic_gpio_get_direction;
|
jzgc->gc.get_direction = ingenic_gpio_get_direction;
|
||||||
|
jzgc->gc.request = gpiochip_generic_request;
|
||||||
if (of_property_read_bool(node, "gpio-ranges")) {
|
jzgc->gc.free = gpiochip_generic_free;
|
||||||
jzgc->gc.request = gpiochip_generic_request;
|
|
||||||
jzgc->gc.free = gpiochip_generic_free;
|
|
||||||
}
|
|
||||||
|
|
||||||
jzgc->irq = irq_of_parse_and_map(node, 0);
|
jzgc->irq = irq_of_parse_and_map(node, 0);
|
||||||
if (!jzgc->irq)
|
if (!jzgc->irq)
|
||||||
|
|
|
@ -2,10 +2,8 @@
|
||||||
#ifndef _ASM_GENERIC_GPIO_H
|
#ifndef _ASM_GENERIC_GPIO_H
|
||||||
#define _ASM_GENERIC_GPIO_H
|
#define _ASM_GENERIC_GPIO_H
|
||||||
|
|
||||||
#include <linux/kernel.h>
|
|
||||||
#include <linux/types.h>
|
#include <linux/types.h>
|
||||||
#include <linux/errno.h>
|
#include <linux/errno.h>
|
||||||
#include <linux/of.h>
|
|
||||||
|
|
||||||
#ifdef CONFIG_GPIOLIB
|
#ifdef CONFIG_GPIOLIB
|
||||||
|
|
||||||
|
@ -140,6 +138,8 @@ static inline void gpio_unexport(unsigned gpio)
|
||||||
|
|
||||||
#else /* !CONFIG_GPIOLIB */
|
#else /* !CONFIG_GPIOLIB */
|
||||||
|
|
||||||
|
#include <linux/kernel.h>
|
||||||
|
|
||||||
static inline bool gpio_is_valid(int number)
|
static inline bool gpio_is_valid(int number)
|
||||||
{
|
{
|
||||||
/* only non-negative numbers are valid */
|
/* only non-negative numbers are valid */
|
||||||
|
|
|
@ -102,11 +102,9 @@ void devm_gpio_free(struct device *dev, unsigned int gpio);
|
||||||
#include <linux/kernel.h>
|
#include <linux/kernel.h>
|
||||||
#include <linux/types.h>
|
#include <linux/types.h>
|
||||||
#include <linux/bug.h>
|
#include <linux/bug.h>
|
||||||
#include <linux/pinctrl/pinctrl.h>
|
|
||||||
|
|
||||||
struct device;
|
struct device;
|
||||||
struct gpio_chip;
|
struct gpio_chip;
|
||||||
struct pinctrl_dev;
|
|
||||||
|
|
||||||
static inline bool gpio_is_valid(int number)
|
static inline bool gpio_is_valid(int number)
|
||||||
{
|
{
|
||||||
|
|
|
@ -2,9 +2,10 @@
|
||||||
#ifndef __LINUX_GPIO_CONSUMER_H
|
#ifndef __LINUX_GPIO_CONSUMER_H
|
||||||
#define __LINUX_GPIO_CONSUMER_H
|
#define __LINUX_GPIO_CONSUMER_H
|
||||||
|
|
||||||
|
#include <linux/bits.h>
|
||||||
#include <linux/bug.h>
|
#include <linux/bug.h>
|
||||||
|
#include <linux/compiler_types.h>
|
||||||
#include <linux/err.h>
|
#include <linux/err.h>
|
||||||
#include <linux/kernel.h>
|
|
||||||
|
|
||||||
struct device;
|
struct device;
|
||||||
|
|
||||||
|
@ -156,6 +157,7 @@ int gpiod_set_raw_array_value_cansleep(unsigned int array_size,
|
||||||
struct gpio_array *array_info,
|
struct gpio_array *array_info,
|
||||||
unsigned long *value_bitmap);
|
unsigned long *value_bitmap);
|
||||||
|
|
||||||
|
int gpiod_set_config(struct gpio_desc *desc, unsigned long config);
|
||||||
int gpiod_set_debounce(struct gpio_desc *desc, unsigned debounce);
|
int gpiod_set_debounce(struct gpio_desc *desc, unsigned debounce);
|
||||||
int gpiod_set_transitory(struct gpio_desc *desc, bool transitory);
|
int gpiod_set_transitory(struct gpio_desc *desc, bool transitory);
|
||||||
void gpiod_toggle_active_low(struct gpio_desc *desc);
|
void gpiod_toggle_active_low(struct gpio_desc *desc);
|
||||||
|
@ -189,6 +191,8 @@ struct gpio_desc *devm_fwnode_gpiod_get_index(struct device *dev,
|
||||||
|
|
||||||
#else /* CONFIG_GPIOLIB */
|
#else /* CONFIG_GPIOLIB */
|
||||||
|
|
||||||
|
#include <linux/kernel.h>
|
||||||
|
|
||||||
static inline int gpiod_count(struct device *dev, const char *con_id)
|
static inline int gpiod_count(struct device *dev, const char *con_id)
|
||||||
{
|
{
|
||||||
return 0;
|
return 0;
|
||||||
|
@ -470,6 +474,13 @@ static inline int gpiod_set_raw_array_value_cansleep(unsigned int array_size,
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static inline int gpiod_set_config(struct gpio_desc *desc, unsigned long config)
|
||||||
|
{
|
||||||
|
/* GPIO can never have been requested */
|
||||||
|
WARN_ON(desc);
|
||||||
|
return -ENOSYS;
|
||||||
|
}
|
||||||
|
|
||||||
static inline int gpiod_set_debounce(struct gpio_desc *desc, unsigned debounce)
|
static inline int gpiod_set_debounce(struct gpio_desc *desc, unsigned debounce)
|
||||||
{
|
{
|
||||||
/* GPIO can never have been requested */
|
/* GPIO can never have been requested */
|
||||||
|
|
|
@ -87,7 +87,7 @@ struct gpio_irq_chip {
|
||||||
* @need_valid_mask to make these GPIO lines unavailable for
|
* @need_valid_mask to make these GPIO lines unavailable for
|
||||||
* translation.
|
* translation.
|
||||||
*/
|
*/
|
||||||
int (*child_to_parent_hwirq)(struct gpio_chip *chip,
|
int (*child_to_parent_hwirq)(struct gpio_chip *gc,
|
||||||
unsigned int child_hwirq,
|
unsigned int child_hwirq,
|
||||||
unsigned int child_type,
|
unsigned int child_type,
|
||||||
unsigned int *parent_hwirq,
|
unsigned int *parent_hwirq,
|
||||||
|
@ -102,7 +102,7 @@ struct gpio_irq_chip {
|
||||||
* variant named &gpiochip_populate_parent_fwspec_fourcell is also
|
* variant named &gpiochip_populate_parent_fwspec_fourcell is also
|
||||||
* available.
|
* available.
|
||||||
*/
|
*/
|
||||||
void *(*populate_parent_alloc_arg)(struct gpio_chip *chip,
|
void *(*populate_parent_alloc_arg)(struct gpio_chip *gc,
|
||||||
unsigned int parent_hwirq,
|
unsigned int parent_hwirq,
|
||||||
unsigned int parent_type);
|
unsigned int parent_type);
|
||||||
|
|
||||||
|
@ -114,7 +114,7 @@ struct gpio_irq_chip {
|
||||||
* callback. If this is not specified, then a default callback will be
|
* callback. If this is not specified, then a default callback will be
|
||||||
* provided that returns the line offset.
|
* provided that returns the line offset.
|
||||||
*/
|
*/
|
||||||
unsigned int (*child_offset_to_irq)(struct gpio_chip *chip,
|
unsigned int (*child_offset_to_irq)(struct gpio_chip *gc,
|
||||||
unsigned int pin);
|
unsigned int pin);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -209,7 +209,7 @@ struct gpio_irq_chip {
|
||||||
* a particular driver wants to clear IRQ related registers
|
* a particular driver wants to clear IRQ related registers
|
||||||
* in order to avoid undesired events.
|
* in order to avoid undesired events.
|
||||||
*/
|
*/
|
||||||
int (*init_hw)(struct gpio_chip *chip);
|
int (*init_hw)(struct gpio_chip *gc);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @init_valid_mask: optional routine to initialize @valid_mask, to be
|
* @init_valid_mask: optional routine to initialize @valid_mask, to be
|
||||||
|
@ -220,7 +220,7 @@ struct gpio_irq_chip {
|
||||||
* then directly set some bits to "0" if they cannot be used for
|
* then directly set some bits to "0" if they cannot be used for
|
||||||
* interrupts.
|
* interrupts.
|
||||||
*/
|
*/
|
||||||
void (*init_valid_mask)(struct gpio_chip *chip,
|
void (*init_valid_mask)(struct gpio_chip *gc,
|
||||||
unsigned long *valid_mask,
|
unsigned long *valid_mask,
|
||||||
unsigned int ngpios);
|
unsigned int ngpios);
|
||||||
|
|
||||||
|
@ -348,40 +348,40 @@ struct gpio_chip {
|
||||||
struct device *parent;
|
struct device *parent;
|
||||||
struct module *owner;
|
struct module *owner;
|
||||||
|
|
||||||
int (*request)(struct gpio_chip *chip,
|
int (*request)(struct gpio_chip *gc,
|
||||||
unsigned offset);
|
unsigned offset);
|
||||||
void (*free)(struct gpio_chip *chip,
|
void (*free)(struct gpio_chip *gc,
|
||||||
unsigned offset);
|
unsigned offset);
|
||||||
int (*get_direction)(struct gpio_chip *chip,
|
int (*get_direction)(struct gpio_chip *gc,
|
||||||
unsigned offset);
|
unsigned offset);
|
||||||
int (*direction_input)(struct gpio_chip *chip,
|
int (*direction_input)(struct gpio_chip *gc,
|
||||||
unsigned offset);
|
unsigned offset);
|
||||||
int (*direction_output)(struct gpio_chip *chip,
|
int (*direction_output)(struct gpio_chip *gc,
|
||||||
unsigned offset, int value);
|
unsigned offset, int value);
|
||||||
int (*get)(struct gpio_chip *chip,
|
int (*get)(struct gpio_chip *gc,
|
||||||
unsigned offset);
|
unsigned offset);
|
||||||
int (*get_multiple)(struct gpio_chip *chip,
|
int (*get_multiple)(struct gpio_chip *gc,
|
||||||
unsigned long *mask,
|
unsigned long *mask,
|
||||||
unsigned long *bits);
|
unsigned long *bits);
|
||||||
void (*set)(struct gpio_chip *chip,
|
void (*set)(struct gpio_chip *gc,
|
||||||
unsigned offset, int value);
|
unsigned offset, int value);
|
||||||
void (*set_multiple)(struct gpio_chip *chip,
|
void (*set_multiple)(struct gpio_chip *gc,
|
||||||
unsigned long *mask,
|
unsigned long *mask,
|
||||||
unsigned long *bits);
|
unsigned long *bits);
|
||||||
int (*set_config)(struct gpio_chip *chip,
|
int (*set_config)(struct gpio_chip *gc,
|
||||||
unsigned offset,
|
unsigned offset,
|
||||||
unsigned long config);
|
unsigned long config);
|
||||||
int (*to_irq)(struct gpio_chip *chip,
|
int (*to_irq)(struct gpio_chip *gc,
|
||||||
unsigned offset);
|
unsigned offset);
|
||||||
|
|
||||||
void (*dbg_show)(struct seq_file *s,
|
void (*dbg_show)(struct seq_file *s,
|
||||||
struct gpio_chip *chip);
|
struct gpio_chip *gc);
|
||||||
|
|
||||||
int (*init_valid_mask)(struct gpio_chip *chip,
|
int (*init_valid_mask)(struct gpio_chip *gc,
|
||||||
unsigned long *valid_mask,
|
unsigned long *valid_mask,
|
||||||
unsigned int ngpios);
|
unsigned int ngpios);
|
||||||
|
|
||||||
int (*add_pin_ranges)(struct gpio_chip *chip);
|
int (*add_pin_ranges)(struct gpio_chip *gc);
|
||||||
|
|
||||||
int base;
|
int base;
|
||||||
u16 ngpio;
|
u16 ngpio;
|
||||||
|
@ -458,11 +458,11 @@ struct gpio_chip {
|
||||||
#endif /* CONFIG_OF_GPIO */
|
#endif /* CONFIG_OF_GPIO */
|
||||||
};
|
};
|
||||||
|
|
||||||
extern const char *gpiochip_is_requested(struct gpio_chip *chip,
|
extern const char *gpiochip_is_requested(struct gpio_chip *gc,
|
||||||
unsigned offset);
|
unsigned offset);
|
||||||
|
|
||||||
/* add/remove chips */
|
/* add/remove chips */
|
||||||
extern int gpiochip_add_data_with_key(struct gpio_chip *chip, void *data,
|
extern int gpiochip_add_data_with_key(struct gpio_chip *gc, void *data,
|
||||||
struct lock_class_key *lock_key,
|
struct lock_class_key *lock_key,
|
||||||
struct lock_class_key *request_key);
|
struct lock_class_key *request_key);
|
||||||
|
|
||||||
|
@ -490,43 +490,43 @@ extern int gpiochip_add_data_with_key(struct gpio_chip *chip, void *data,
|
||||||
* Otherwise it returns zero as a success code.
|
* Otherwise it returns zero as a success code.
|
||||||
*/
|
*/
|
||||||
#ifdef CONFIG_LOCKDEP
|
#ifdef CONFIG_LOCKDEP
|
||||||
#define gpiochip_add_data(chip, data) ({ \
|
#define gpiochip_add_data(gc, data) ({ \
|
||||||
static struct lock_class_key lock_key; \
|
static struct lock_class_key lock_key; \
|
||||||
static struct lock_class_key request_key; \
|
static struct lock_class_key request_key; \
|
||||||
gpiochip_add_data_with_key(chip, data, &lock_key, \
|
gpiochip_add_data_with_key(gc, data, &lock_key, \
|
||||||
&request_key); \
|
&request_key); \
|
||||||
})
|
})
|
||||||
#else
|
#else
|
||||||
#define gpiochip_add_data(chip, data) gpiochip_add_data_with_key(chip, data, NULL, NULL)
|
#define gpiochip_add_data(gc, data) gpiochip_add_data_with_key(gc, data, NULL, NULL)
|
||||||
#endif /* CONFIG_LOCKDEP */
|
#endif /* CONFIG_LOCKDEP */
|
||||||
|
|
||||||
static inline int gpiochip_add(struct gpio_chip *chip)
|
static inline int gpiochip_add(struct gpio_chip *gc)
|
||||||
{
|
{
|
||||||
return gpiochip_add_data(chip, NULL);
|
return gpiochip_add_data(gc, NULL);
|
||||||
}
|
}
|
||||||
extern void gpiochip_remove(struct gpio_chip *chip);
|
extern void gpiochip_remove(struct gpio_chip *gc);
|
||||||
extern int devm_gpiochip_add_data(struct device *dev, struct gpio_chip *chip,
|
extern int devm_gpiochip_add_data(struct device *dev, struct gpio_chip *gc,
|
||||||
void *data);
|
void *data);
|
||||||
|
|
||||||
extern struct gpio_chip *gpiochip_find(void *data,
|
extern struct gpio_chip *gpiochip_find(void *data,
|
||||||
int (*match)(struct gpio_chip *chip, void *data));
|
int (*match)(struct gpio_chip *gc, void *data));
|
||||||
|
|
||||||
bool gpiochip_line_is_irq(struct gpio_chip *chip, unsigned int offset);
|
bool gpiochip_line_is_irq(struct gpio_chip *gc, unsigned int offset);
|
||||||
int gpiochip_reqres_irq(struct gpio_chip *chip, unsigned int offset);
|
int gpiochip_reqres_irq(struct gpio_chip *gc, unsigned int offset);
|
||||||
void gpiochip_relres_irq(struct gpio_chip *chip, unsigned int offset);
|
void gpiochip_relres_irq(struct gpio_chip *gc, unsigned int offset);
|
||||||
void gpiochip_disable_irq(struct gpio_chip *chip, unsigned int offset);
|
void gpiochip_disable_irq(struct gpio_chip *gc, unsigned int offset);
|
||||||
void gpiochip_enable_irq(struct gpio_chip *chip, unsigned int offset);
|
void gpiochip_enable_irq(struct gpio_chip *gc, unsigned int offset);
|
||||||
|
|
||||||
/* Line status inquiry for drivers */
|
/* Line status inquiry for drivers */
|
||||||
bool gpiochip_line_is_open_drain(struct gpio_chip *chip, unsigned int offset);
|
bool gpiochip_line_is_open_drain(struct gpio_chip *gc, unsigned int offset);
|
||||||
bool gpiochip_line_is_open_source(struct gpio_chip *chip, unsigned int offset);
|
bool gpiochip_line_is_open_source(struct gpio_chip *gc, unsigned int offset);
|
||||||
|
|
||||||
/* Sleep persistence inquiry for drivers */
|
/* Sleep persistence inquiry for drivers */
|
||||||
bool gpiochip_line_is_persistent(struct gpio_chip *chip, unsigned int offset);
|
bool gpiochip_line_is_persistent(struct gpio_chip *gc, unsigned int offset);
|
||||||
bool gpiochip_line_is_valid(const struct gpio_chip *chip, unsigned int offset);
|
bool gpiochip_line_is_valid(const struct gpio_chip *gc, unsigned int offset);
|
||||||
|
|
||||||
/* get driver data */
|
/* get driver data */
|
||||||
void *gpiochip_get_data(struct gpio_chip *chip);
|
void *gpiochip_get_data(struct gpio_chip *gc);
|
||||||
|
|
||||||
struct bgpio_pdata {
|
struct bgpio_pdata {
|
||||||
const char *label;
|
const char *label;
|
||||||
|
@ -536,23 +536,23 @@ struct bgpio_pdata {
|
||||||
|
|
||||||
#ifdef CONFIG_IRQ_DOMAIN_HIERARCHY
|
#ifdef CONFIG_IRQ_DOMAIN_HIERARCHY
|
||||||
|
|
||||||
void *gpiochip_populate_parent_fwspec_twocell(struct gpio_chip *chip,
|
void *gpiochip_populate_parent_fwspec_twocell(struct gpio_chip *gc,
|
||||||
unsigned int parent_hwirq,
|
unsigned int parent_hwirq,
|
||||||
unsigned int parent_type);
|
unsigned int parent_type);
|
||||||
void *gpiochip_populate_parent_fwspec_fourcell(struct gpio_chip *chip,
|
void *gpiochip_populate_parent_fwspec_fourcell(struct gpio_chip *gc,
|
||||||
unsigned int parent_hwirq,
|
unsigned int parent_hwirq,
|
||||||
unsigned int parent_type);
|
unsigned int parent_type);
|
||||||
|
|
||||||
#else
|
#else
|
||||||
|
|
||||||
static inline void *gpiochip_populate_parent_fwspec_twocell(struct gpio_chip *chip,
|
static inline void *gpiochip_populate_parent_fwspec_twocell(struct gpio_chip *gc,
|
||||||
unsigned int parent_hwirq,
|
unsigned int parent_hwirq,
|
||||||
unsigned int parent_type)
|
unsigned int parent_type)
|
||||||
{
|
{
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline void *gpiochip_populate_parent_fwspec_fourcell(struct gpio_chip *chip,
|
static inline void *gpiochip_populate_parent_fwspec_fourcell(struct gpio_chip *gc,
|
||||||
unsigned int parent_hwirq,
|
unsigned int parent_hwirq,
|
||||||
unsigned int parent_type)
|
unsigned int parent_type)
|
||||||
{
|
{
|
||||||
|
@ -572,6 +572,7 @@ int bgpio_init(struct gpio_chip *gc, struct device *dev,
|
||||||
#define BGPIOF_BIG_ENDIAN_BYTE_ORDER BIT(3)
|
#define BGPIOF_BIG_ENDIAN_BYTE_ORDER BIT(3)
|
||||||
#define BGPIOF_READ_OUTPUT_REG_SET BIT(4) /* reg_set stores output value */
|
#define BGPIOF_READ_OUTPUT_REG_SET BIT(4) /* reg_set stores output value */
|
||||||
#define BGPIOF_NO_OUTPUT BIT(5) /* only input */
|
#define BGPIOF_NO_OUTPUT BIT(5) /* only input */
|
||||||
|
#define BGPIOF_NO_SET_ON_INPUT BIT(6)
|
||||||
|
|
||||||
int gpiochip_irq_map(struct irq_domain *d, unsigned int irq,
|
int gpiochip_irq_map(struct irq_domain *d, unsigned int irq,
|
||||||
irq_hw_number_t hwirq);
|
irq_hw_number_t hwirq);
|
||||||
|
@ -582,11 +583,11 @@ int gpiochip_irq_domain_activate(struct irq_domain *domain,
|
||||||
void gpiochip_irq_domain_deactivate(struct irq_domain *domain,
|
void gpiochip_irq_domain_deactivate(struct irq_domain *domain,
|
||||||
struct irq_data *data);
|
struct irq_data *data);
|
||||||
|
|
||||||
void gpiochip_set_nested_irqchip(struct gpio_chip *gpiochip,
|
void gpiochip_set_nested_irqchip(struct gpio_chip *gc,
|
||||||
struct irq_chip *irqchip,
|
struct irq_chip *irqchip,
|
||||||
unsigned int parent_irq);
|
unsigned int parent_irq);
|
||||||
|
|
||||||
int gpiochip_irqchip_add_key(struct gpio_chip *gpiochip,
|
int gpiochip_irqchip_add_key(struct gpio_chip *gc,
|
||||||
struct irq_chip *irqchip,
|
struct irq_chip *irqchip,
|
||||||
unsigned int first_irq,
|
unsigned int first_irq,
|
||||||
irq_flow_handler_t handler,
|
irq_flow_handler_t handler,
|
||||||
|
@ -595,7 +596,7 @@ int gpiochip_irqchip_add_key(struct gpio_chip *gpiochip,
|
||||||
struct lock_class_key *lock_key,
|
struct lock_class_key *lock_key,
|
||||||
struct lock_class_key *request_key);
|
struct lock_class_key *request_key);
|
||||||
|
|
||||||
bool gpiochip_irqchip_irq_valid(const struct gpio_chip *gpiochip,
|
bool gpiochip_irqchip_irq_valid(const struct gpio_chip *gc,
|
||||||
unsigned int offset);
|
unsigned int offset);
|
||||||
|
|
||||||
#ifdef CONFIG_LOCKDEP
|
#ifdef CONFIG_LOCKDEP
|
||||||
|
@ -606,7 +607,7 @@ bool gpiochip_irqchip_irq_valid(const struct gpio_chip *gpiochip,
|
||||||
* boilerplate static inlines provides such a key for each
|
* boilerplate static inlines provides such a key for each
|
||||||
* unique instance.
|
* unique instance.
|
||||||
*/
|
*/
|
||||||
static inline int gpiochip_irqchip_add(struct gpio_chip *gpiochip,
|
static inline int gpiochip_irqchip_add(struct gpio_chip *gc,
|
||||||
struct irq_chip *irqchip,
|
struct irq_chip *irqchip,
|
||||||
unsigned int first_irq,
|
unsigned int first_irq,
|
||||||
irq_flow_handler_t handler,
|
irq_flow_handler_t handler,
|
||||||
|
@ -615,12 +616,12 @@ static inline int gpiochip_irqchip_add(struct gpio_chip *gpiochip,
|
||||||
static struct lock_class_key lock_key;
|
static struct lock_class_key lock_key;
|
||||||
static struct lock_class_key request_key;
|
static struct lock_class_key request_key;
|
||||||
|
|
||||||
return gpiochip_irqchip_add_key(gpiochip, irqchip, first_irq,
|
return gpiochip_irqchip_add_key(gc, irqchip, first_irq,
|
||||||
handler, type, false,
|
handler, type, false,
|
||||||
&lock_key, &request_key);
|
&lock_key, &request_key);
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline int gpiochip_irqchip_add_nested(struct gpio_chip *gpiochip,
|
static inline int gpiochip_irqchip_add_nested(struct gpio_chip *gc,
|
||||||
struct irq_chip *irqchip,
|
struct irq_chip *irqchip,
|
||||||
unsigned int first_irq,
|
unsigned int first_irq,
|
||||||
irq_flow_handler_t handler,
|
irq_flow_handler_t handler,
|
||||||
|
@ -630,35 +631,35 @@ static inline int gpiochip_irqchip_add_nested(struct gpio_chip *gpiochip,
|
||||||
static struct lock_class_key lock_key;
|
static struct lock_class_key lock_key;
|
||||||
static struct lock_class_key request_key;
|
static struct lock_class_key request_key;
|
||||||
|
|
||||||
return gpiochip_irqchip_add_key(gpiochip, irqchip, first_irq,
|
return gpiochip_irqchip_add_key(gc, irqchip, first_irq,
|
||||||
handler, type, true,
|
handler, type, true,
|
||||||
&lock_key, &request_key);
|
&lock_key, &request_key);
|
||||||
}
|
}
|
||||||
#else /* ! CONFIG_LOCKDEP */
|
#else /* ! CONFIG_LOCKDEP */
|
||||||
static inline int gpiochip_irqchip_add(struct gpio_chip *gpiochip,
|
static inline int gpiochip_irqchip_add(struct gpio_chip *gc,
|
||||||
struct irq_chip *irqchip,
|
struct irq_chip *irqchip,
|
||||||
unsigned int first_irq,
|
unsigned int first_irq,
|
||||||
irq_flow_handler_t handler,
|
irq_flow_handler_t handler,
|
||||||
unsigned int type)
|
unsigned int type)
|
||||||
{
|
{
|
||||||
return gpiochip_irqchip_add_key(gpiochip, irqchip, first_irq,
|
return gpiochip_irqchip_add_key(gc, irqchip, first_irq,
|
||||||
handler, type, false, NULL, NULL);
|
handler, type, false, NULL, NULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline int gpiochip_irqchip_add_nested(struct gpio_chip *gpiochip,
|
static inline int gpiochip_irqchip_add_nested(struct gpio_chip *gc,
|
||||||
struct irq_chip *irqchip,
|
struct irq_chip *irqchip,
|
||||||
unsigned int first_irq,
|
unsigned int first_irq,
|
||||||
irq_flow_handler_t handler,
|
irq_flow_handler_t handler,
|
||||||
unsigned int type)
|
unsigned int type)
|
||||||
{
|
{
|
||||||
return gpiochip_irqchip_add_key(gpiochip, irqchip, first_irq,
|
return gpiochip_irqchip_add_key(gc, irqchip, first_irq,
|
||||||
handler, type, true, NULL, NULL);
|
handler, type, true, NULL, NULL);
|
||||||
}
|
}
|
||||||
#endif /* CONFIG_LOCKDEP */
|
#endif /* CONFIG_LOCKDEP */
|
||||||
|
|
||||||
int gpiochip_generic_request(struct gpio_chip *chip, unsigned offset);
|
int gpiochip_generic_request(struct gpio_chip *gc, unsigned offset);
|
||||||
void gpiochip_generic_free(struct gpio_chip *chip, unsigned offset);
|
void gpiochip_generic_free(struct gpio_chip *gc, unsigned offset);
|
||||||
int gpiochip_generic_config(struct gpio_chip *chip, unsigned offset,
|
int gpiochip_generic_config(struct gpio_chip *gc, unsigned offset,
|
||||||
unsigned long config);
|
unsigned long config);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -675,25 +676,25 @@ struct gpio_pin_range {
|
||||||
|
|
||||||
#ifdef CONFIG_PINCTRL
|
#ifdef CONFIG_PINCTRL
|
||||||
|
|
||||||
int gpiochip_add_pin_range(struct gpio_chip *chip, const char *pinctl_name,
|
int gpiochip_add_pin_range(struct gpio_chip *gc, const char *pinctl_name,
|
||||||
unsigned int gpio_offset, unsigned int pin_offset,
|
unsigned int gpio_offset, unsigned int pin_offset,
|
||||||
unsigned int npins);
|
unsigned int npins);
|
||||||
int gpiochip_add_pingroup_range(struct gpio_chip *chip,
|
int gpiochip_add_pingroup_range(struct gpio_chip *gc,
|
||||||
struct pinctrl_dev *pctldev,
|
struct pinctrl_dev *pctldev,
|
||||||
unsigned int gpio_offset, const char *pin_group);
|
unsigned int gpio_offset, const char *pin_group);
|
||||||
void gpiochip_remove_pin_ranges(struct gpio_chip *chip);
|
void gpiochip_remove_pin_ranges(struct gpio_chip *gc);
|
||||||
|
|
||||||
#else /* ! CONFIG_PINCTRL */
|
#else /* ! CONFIG_PINCTRL */
|
||||||
|
|
||||||
static inline int
|
static inline int
|
||||||
gpiochip_add_pin_range(struct gpio_chip *chip, const char *pinctl_name,
|
gpiochip_add_pin_range(struct gpio_chip *gc, const char *pinctl_name,
|
||||||
unsigned int gpio_offset, unsigned int pin_offset,
|
unsigned int gpio_offset, unsigned int pin_offset,
|
||||||
unsigned int npins)
|
unsigned int npins)
|
||||||
{
|
{
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
static inline int
|
static inline int
|
||||||
gpiochip_add_pingroup_range(struct gpio_chip *chip,
|
gpiochip_add_pingroup_range(struct gpio_chip *gc,
|
||||||
struct pinctrl_dev *pctldev,
|
struct pinctrl_dev *pctldev,
|
||||||
unsigned int gpio_offset, const char *pin_group)
|
unsigned int gpio_offset, const char *pin_group)
|
||||||
{
|
{
|
||||||
|
@ -701,27 +702,27 @@ gpiochip_add_pingroup_range(struct gpio_chip *chip,
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline void
|
static inline void
|
||||||
gpiochip_remove_pin_ranges(struct gpio_chip *chip)
|
gpiochip_remove_pin_ranges(struct gpio_chip *gc)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif /* CONFIG_PINCTRL */
|
#endif /* CONFIG_PINCTRL */
|
||||||
|
|
||||||
struct gpio_desc *gpiochip_request_own_desc(struct gpio_chip *chip,
|
struct gpio_desc *gpiochip_request_own_desc(struct gpio_chip *gc,
|
||||||
unsigned int hwnum,
|
unsigned int hwnum,
|
||||||
const char *label,
|
const char *label,
|
||||||
enum gpio_lookup_flags lflags,
|
enum gpio_lookup_flags lflags,
|
||||||
enum gpiod_flags dflags);
|
enum gpiod_flags dflags);
|
||||||
void gpiochip_free_own_desc(struct gpio_desc *desc);
|
void gpiochip_free_own_desc(struct gpio_desc *desc);
|
||||||
|
|
||||||
void devprop_gpiochip_set_names(struct gpio_chip *chip,
|
void devprop_gpiochip_set_names(struct gpio_chip *gc,
|
||||||
const struct fwnode_handle *fwnode);
|
const struct fwnode_handle *fwnode);
|
||||||
|
|
||||||
#ifdef CONFIG_GPIOLIB
|
#ifdef CONFIG_GPIOLIB
|
||||||
|
|
||||||
/* lock/unlock as IRQ */
|
/* lock/unlock as IRQ */
|
||||||
int gpiochip_lock_as_irq(struct gpio_chip *chip, unsigned int offset);
|
int gpiochip_lock_as_irq(struct gpio_chip *gc, unsigned int offset);
|
||||||
void gpiochip_unlock_as_irq(struct gpio_chip *chip, unsigned int offset);
|
void gpiochip_unlock_as_irq(struct gpio_chip *gc, unsigned int offset);
|
||||||
|
|
||||||
|
|
||||||
struct gpio_chip *gpiod_to_chip(const struct gpio_desc *desc);
|
struct gpio_chip *gpiod_to_chip(const struct gpio_desc *desc);
|
||||||
|
@ -735,14 +736,14 @@ static inline struct gpio_chip *gpiod_to_chip(const struct gpio_desc *desc)
|
||||||
return ERR_PTR(-ENODEV);
|
return ERR_PTR(-ENODEV);
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline int gpiochip_lock_as_irq(struct gpio_chip *chip,
|
static inline int gpiochip_lock_as_irq(struct gpio_chip *gc,
|
||||||
unsigned int offset)
|
unsigned int offset)
|
||||||
{
|
{
|
||||||
WARN_ON(1);
|
WARN_ON(1);
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline void gpiochip_unlock_as_irq(struct gpio_chip *chip,
|
static inline void gpiochip_unlock_as_irq(struct gpio_chip *gc,
|
||||||
unsigned int offset)
|
unsigned int offset)
|
||||||
{
|
{
|
||||||
WARN_ON(1);
|
WARN_ON(1);
|
||||||
|
|
|
@ -246,6 +246,37 @@ __kfifo_int_must_check_helper(int val)
|
||||||
__tmpq->kfifo.in == __tmpq->kfifo.out; \
|
__tmpq->kfifo.in == __tmpq->kfifo.out; \
|
||||||
})
|
})
|
||||||
|
|
||||||
|
/**
|
||||||
|
* kfifo_is_empty_spinlocked - returns true if the fifo is empty using
|
||||||
|
* a spinlock for locking
|
||||||
|
* @fifo: address of the fifo to be used
|
||||||
|
* @lock: spinlock to be used for locking
|
||||||
|
*/
|
||||||
|
#define kfifo_is_empty_spinlocked(fifo, lock) \
|
||||||
|
({ \
|
||||||
|
unsigned long __flags; \
|
||||||
|
bool __ret; \
|
||||||
|
spin_lock_irqsave(lock, __flags); \
|
||||||
|
__ret = kfifo_is_empty(fifo); \
|
||||||
|
spin_unlock_irqrestore(lock, __flags); \
|
||||||
|
__ret; \
|
||||||
|
})
|
||||||
|
|
||||||
|
/**
|
||||||
|
* kfifo_is_empty_spinlocked_noirqsave - returns true if the fifo is empty
|
||||||
|
* using a spinlock for locking, doesn't disable interrupts
|
||||||
|
* @fifo: address of the fifo to be used
|
||||||
|
* @lock: spinlock to be used for locking
|
||||||
|
*/
|
||||||
|
#define kfifo_is_empty_spinlocked_noirqsave(fifo, lock) \
|
||||||
|
({ \
|
||||||
|
bool __ret; \
|
||||||
|
spin_lock(lock); \
|
||||||
|
__ret = kfifo_is_empty(fifo); \
|
||||||
|
spin_unlock(lock); \
|
||||||
|
__ret; \
|
||||||
|
})
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* kfifo_is_full - returns true if the fifo is full
|
* kfifo_is_full - returns true if the fifo is full
|
||||||
* @fifo: address of the fifo to be used
|
* @fifo: address of the fifo to be used
|
||||||
|
@ -517,6 +548,26 @@ __kfifo_uint_must_check_helper( \
|
||||||
__ret; \
|
__ret; \
|
||||||
})
|
})
|
||||||
|
|
||||||
|
/**
|
||||||
|
* kfifo_in_spinlocked_noirqsave - put data into fifo using a spinlock for
|
||||||
|
* locking, don't disable interrupts
|
||||||
|
* @fifo: address of the fifo to be used
|
||||||
|
* @buf: the data to be added
|
||||||
|
* @n: number of elements to be added
|
||||||
|
* @lock: pointer to the spinlock to use for locking
|
||||||
|
*
|
||||||
|
* This is a variant of kfifo_in_spinlocked() but uses spin_lock/unlock()
|
||||||
|
* for locking and doesn't disable interrupts.
|
||||||
|
*/
|
||||||
|
#define kfifo_in_spinlocked_noirqsave(fifo, buf, n, lock) \
|
||||||
|
({ \
|
||||||
|
unsigned int __ret; \
|
||||||
|
spin_lock(lock); \
|
||||||
|
__ret = kfifo_in(fifo, buf, n); \
|
||||||
|
spin_unlock(lock); \
|
||||||
|
__ret; \
|
||||||
|
})
|
||||||
|
|
||||||
/* alias for kfifo_in_spinlocked, will be removed in a future release */
|
/* alias for kfifo_in_spinlocked, will be removed in a future release */
|
||||||
#define kfifo_in_locked(fifo, buf, n, lock) \
|
#define kfifo_in_locked(fifo, buf, n, lock) \
|
||||||
kfifo_in_spinlocked(fifo, buf, n, lock)
|
kfifo_in_spinlocked(fifo, buf, n, lock)
|
||||||
|
@ -569,6 +620,28 @@ __kfifo_uint_must_check_helper( \
|
||||||
}) \
|
}) \
|
||||||
)
|
)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* kfifo_out_spinlocked_noirqsave - get data from the fifo using a spinlock
|
||||||
|
* for locking, don't disable interrupts
|
||||||
|
* @fifo: address of the fifo to be used
|
||||||
|
* @buf: pointer to the storage buffer
|
||||||
|
* @n: max. number of elements to get
|
||||||
|
* @lock: pointer to the spinlock to use for locking
|
||||||
|
*
|
||||||
|
* This is a variant of kfifo_out_spinlocked() which uses spin_lock/unlock()
|
||||||
|
* for locking and doesn't disable interrupts.
|
||||||
|
*/
|
||||||
|
#define kfifo_out_spinlocked_noirqsave(fifo, buf, n, lock) \
|
||||||
|
__kfifo_uint_must_check_helper( \
|
||||||
|
({ \
|
||||||
|
unsigned int __ret; \
|
||||||
|
spin_lock(lock); \
|
||||||
|
__ret = kfifo_out(fifo, buf, n); \
|
||||||
|
spin_unlock(lock); \
|
||||||
|
__ret; \
|
||||||
|
}) \
|
||||||
|
)
|
||||||
|
|
||||||
/* alias for kfifo_out_spinlocked, will be removed in a future release */
|
/* alias for kfifo_out_spinlocked, will be removed in a future release */
|
||||||
#define kfifo_out_locked(fifo, buf, n, lock) \
|
#define kfifo_out_locked(fifo, buf, n, lock) \
|
||||||
kfifo_out_spinlocked(fifo, buf, n, lock)
|
kfifo_out_spinlocked(fifo, buf, n, lock)
|
||||||
|
|
|
@ -11,9 +11,8 @@
|
||||||
#define __LINUX_OF_GPIO_H
|
#define __LINUX_OF_GPIO_H
|
||||||
|
|
||||||
#include <linux/compiler.h>
|
#include <linux/compiler.h>
|
||||||
#include <linux/kernel.h>
|
#include <linux/gpio/driver.h>
|
||||||
#include <linux/errno.h>
|
#include <linux/gpio.h> /* FIXME: Shouldn't be here */
|
||||||
#include <linux/gpio.h>
|
|
||||||
#include <linux/of.h>
|
#include <linux/of.h>
|
||||||
|
|
||||||
struct device_node;
|
struct device_node;
|
||||||
|
@ -34,6 +33,8 @@ enum of_gpio_flags {
|
||||||
|
|
||||||
#ifdef CONFIG_OF_GPIO
|
#ifdef CONFIG_OF_GPIO
|
||||||
|
|
||||||
|
#include <linux/kernel.h>
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* OF GPIO chip for memory mapped banks
|
* OF GPIO chip for memory mapped banks
|
||||||
*/
|
*/
|
||||||
|
@ -63,6 +64,8 @@ extern void of_mm_gpiochip_remove(struct of_mm_gpio_chip *mm_gc);
|
||||||
|
|
||||||
#else /* CONFIG_OF_GPIO */
|
#else /* CONFIG_OF_GPIO */
|
||||||
|
|
||||||
|
#include <linux/errno.h>
|
||||||
|
|
||||||
/* Drivers may not strictly depend on the GPIO support, so let them link. */
|
/* Drivers may not strictly depend on the GPIO support, so let them link. */
|
||||||
static inline int of_get_named_gpio_flags(struct device_node *np,
|
static inline int of_get_named_gpio_flags(struct device_node *np,
|
||||||
const char *list_name, int index, enum of_gpio_flags *flags)
|
const char *list_name, int index, enum of_gpio_flags *flags)
|
||||||
|
|
|
@ -186,7 +186,7 @@ extern int pinctrl_get_group_pins(struct pinctrl_dev *pctldev,
|
||||||
const char *pin_group, const unsigned **pins,
|
const char *pin_group, const unsigned **pins,
|
||||||
unsigned *num_pins);
|
unsigned *num_pins);
|
||||||
|
|
||||||
#ifdef CONFIG_OF
|
#if IS_ENABLED(CONFIG_OF) && IS_ENABLED(CONFIG_PINCTRL)
|
||||||
extern struct pinctrl_dev *of_pinctrl_get(struct device_node *np);
|
extern struct pinctrl_dev *of_pinctrl_get(struct device_node *np);
|
||||||
#else
|
#else
|
||||||
static inline
|
static inline
|
||||||
|
|
|
@ -18,7 +18,7 @@
|
||||||
* struct gpiochip_info - Information about a certain GPIO chip
|
* struct gpiochip_info - Information about a certain GPIO chip
|
||||||
* @name: the Linux kernel name of this GPIO chip
|
* @name: the Linux kernel name of this GPIO chip
|
||||||
* @label: a functional name for this GPIO chip, such as a product
|
* @label: a functional name for this GPIO chip, such as a product
|
||||||
* number, may be NULL
|
* number, may be empty
|
||||||
* @lines: number of GPIO lines on this chip
|
* @lines: number of GPIO lines on this chip
|
||||||
*/
|
*/
|
||||||
struct gpiochip_info {
|
struct gpiochip_info {
|
||||||
|
@ -44,10 +44,10 @@ struct gpiochip_info {
|
||||||
* @flags: various flags for this line
|
* @flags: various flags for this line
|
||||||
* @name: the name of this GPIO line, such as the output pin of the line on the
|
* @name: the name of this GPIO line, such as the output pin of the line on the
|
||||||
* chip, a rail or a pin header name on a board, as specified by the gpio
|
* chip, a rail or a pin header name on a board, as specified by the gpio
|
||||||
* chip, may be NULL
|
* chip, may be empty
|
||||||
* @consumer: a functional name for the consumer of this GPIO line as set by
|
* @consumer: a functional name for the consumer of this GPIO line as set by
|
||||||
* whatever is using it, will be NULL if there is no current user but may
|
* whatever is using it, will be empty if there is no current user but may
|
||||||
* also be NULL if the consumer doesn't set this up
|
* also be empty if the consumer doesn't set this up
|
||||||
*/
|
*/
|
||||||
struct gpioline_info {
|
struct gpioline_info {
|
||||||
__u32 line_offset;
|
__u32 line_offset;
|
||||||
|
@ -59,6 +59,34 @@ struct gpioline_info {
|
||||||
/* Maximum number of requested handles */
|
/* Maximum number of requested handles */
|
||||||
#define GPIOHANDLES_MAX 64
|
#define GPIOHANDLES_MAX 64
|
||||||
|
|
||||||
|
/* Possible line status change events */
|
||||||
|
enum {
|
||||||
|
GPIOLINE_CHANGED_REQUESTED = 1,
|
||||||
|
GPIOLINE_CHANGED_RELEASED,
|
||||||
|
GPIOLINE_CHANGED_CONFIG,
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* struct gpioline_info_changed - Information about a change in status
|
||||||
|
* of a GPIO line
|
||||||
|
* @info: updated line information
|
||||||
|
* @timestamp: estimate of time of status change occurrence, in nanoseconds
|
||||||
|
* and GPIOLINE_CHANGED_CONFIG
|
||||||
|
* @event_type: one of GPIOLINE_CHANGED_REQUESTED, GPIOLINE_CHANGED_RELEASED
|
||||||
|
*
|
||||||
|
* Note: struct gpioline_info embedded here has 32-bit alignment on its own,
|
||||||
|
* but it works fine with 64-bit alignment too. With its 72 byte size, we can
|
||||||
|
* guarantee there are no implicit holes between it and subsequent members.
|
||||||
|
* The 20-byte padding at the end makes sure we don't add any implicit padding
|
||||||
|
* at the end of the structure on 64-bit architectures.
|
||||||
|
*/
|
||||||
|
struct gpioline_info_changed {
|
||||||
|
struct gpioline_info info;
|
||||||
|
__u64 timestamp;
|
||||||
|
__u32 event_type;
|
||||||
|
__u32 padding[5]; /* for future use */
|
||||||
|
};
|
||||||
|
|
||||||
/* Linerequest flags */
|
/* Linerequest flags */
|
||||||
#define GPIOHANDLE_REQUEST_INPUT (1UL << 0)
|
#define GPIOHANDLE_REQUEST_INPUT (1UL << 0)
|
||||||
#define GPIOHANDLE_REQUEST_OUTPUT (1UL << 1)
|
#define GPIOHANDLE_REQUEST_OUTPUT (1UL << 1)
|
||||||
|
@ -176,6 +204,8 @@ struct gpioevent_data {
|
||||||
|
|
||||||
#define GPIO_GET_CHIPINFO_IOCTL _IOR(0xB4, 0x01, struct gpiochip_info)
|
#define GPIO_GET_CHIPINFO_IOCTL _IOR(0xB4, 0x01, struct gpiochip_info)
|
||||||
#define GPIO_GET_LINEINFO_IOCTL _IOWR(0xB4, 0x02, struct gpioline_info)
|
#define GPIO_GET_LINEINFO_IOCTL _IOWR(0xB4, 0x02, struct gpioline_info)
|
||||||
|
#define GPIO_GET_LINEINFO_WATCH_IOCTL _IOWR(0xB4, 0x0b, struct gpioline_info)
|
||||||
|
#define GPIO_GET_LINEINFO_UNWATCH_IOCTL _IOWR(0xB4, 0x0c, __u32)
|
||||||
#define GPIO_GET_LINEHANDLE_IOCTL _IOWR(0xB4, 0x03, struct gpiohandle_request)
|
#define GPIO_GET_LINEHANDLE_IOCTL _IOWR(0xB4, 0x03, struct gpiohandle_request)
|
||||||
#define GPIO_GET_LINEEVENT_IOCTL _IOWR(0xB4, 0x04, struct gpioevent_request)
|
#define GPIO_GET_LINEEVENT_IOCTL _IOWR(0xB4, 0x04, struct gpioevent_request)
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
# SPDX-License-Identifier: GPL-2.0-only
|
# SPDX-License-Identifier: GPL-2.0-only
|
||||||
gpio-event-mon
|
gpio-event-mon
|
||||||
gpio-hammer
|
gpio-hammer
|
||||||
|
gpio-watch
|
||||||
lsgpio
|
lsgpio
|
||||||
include/linux/gpio.h
|
include/linux/gpio.h
|
||||||
|
|
|
@ -2,3 +2,4 @@ gpio-utils-y += gpio-utils.o
|
||||||
lsgpio-y += lsgpio.o gpio-utils.o
|
lsgpio-y += lsgpio.o gpio-utils.o
|
||||||
gpio-hammer-y += gpio-hammer.o gpio-utils.o
|
gpio-hammer-y += gpio-hammer.o gpio-utils.o
|
||||||
gpio-event-mon-y += gpio-event-mon.o gpio-utils.o
|
gpio-event-mon-y += gpio-event-mon.o gpio-utils.o
|
||||||
|
gpio-watch-y += gpio-watch.o
|
||||||
|
|
|
@ -18,7 +18,7 @@ MAKEFLAGS += -r
|
||||||
|
|
||||||
override CFLAGS += -O2 -Wall -g -D_GNU_SOURCE -I$(OUTPUT)include
|
override CFLAGS += -O2 -Wall -g -D_GNU_SOURCE -I$(OUTPUT)include
|
||||||
|
|
||||||
ALL_TARGETS := lsgpio gpio-hammer gpio-event-mon
|
ALL_TARGETS := lsgpio gpio-hammer gpio-event-mon gpio-watch
|
||||||
ALL_PROGRAMS := $(patsubst %,$(OUTPUT)%,$(ALL_TARGETS))
|
ALL_PROGRAMS := $(patsubst %,$(OUTPUT)%,$(ALL_TARGETS))
|
||||||
|
|
||||||
all: $(ALL_PROGRAMS)
|
all: $(ALL_PROGRAMS)
|
||||||
|
@ -35,7 +35,7 @@ $(OUTPUT)include/linux/gpio.h: ../../include/uapi/linux/gpio.h
|
||||||
|
|
||||||
prepare: $(OUTPUT)include/linux/gpio.h
|
prepare: $(OUTPUT)include/linux/gpio.h
|
||||||
|
|
||||||
GPIO_UTILS_IN := $(output)gpio-utils-in.o
|
GPIO_UTILS_IN := $(OUTPUT)gpio-utils-in.o
|
||||||
$(GPIO_UTILS_IN): prepare FORCE
|
$(GPIO_UTILS_IN): prepare FORCE
|
||||||
$(Q)$(MAKE) $(build)=gpio-utils
|
$(Q)$(MAKE) $(build)=gpio-utils
|
||||||
|
|
||||||
|
@ -66,6 +66,15 @@ $(GPIO_EVENT_MON_IN): prepare FORCE $(OUTPUT)gpio-utils-in.o
|
||||||
$(OUTPUT)gpio-event-mon: $(GPIO_EVENT_MON_IN)
|
$(OUTPUT)gpio-event-mon: $(GPIO_EVENT_MON_IN)
|
||||||
$(QUIET_LINK)$(CC) $(CFLAGS) $(LDFLAGS) $< -o $@
|
$(QUIET_LINK)$(CC) $(CFLAGS) $(LDFLAGS) $< -o $@
|
||||||
|
|
||||||
|
#
|
||||||
|
# gpio-watch
|
||||||
|
#
|
||||||
|
GPIO_WATCH_IN := $(OUTPUT)gpio-watch-in.o
|
||||||
|
$(GPIO_WATCH_IN): prepare FORCE
|
||||||
|
$(Q)$(MAKE) $(build)=gpio-watch
|
||||||
|
$(OUTPUT)gpio-watch: $(GPIO_WATCH_IN)
|
||||||
|
$(QUIET_LINK)$(CC) $(CFLAGS) $(LDFLAGS) $< -o $@
|
||||||
|
|
||||||
clean:
|
clean:
|
||||||
rm -f $(ALL_PROGRAMS)
|
rm -f $(ALL_PROGRAMS)
|
||||||
rm -f $(OUTPUT)include/linux/gpio.h
|
rm -f $(OUTPUT)include/linux/gpio.h
|
||||||
|
|
|
@ -77,7 +77,7 @@ int hammer_device(const char *device_name, unsigned int *lines, int nlines,
|
||||||
|
|
||||||
fprintf(stdout, "[%c] ", swirr[j]);
|
fprintf(stdout, "[%c] ", swirr[j]);
|
||||||
j++;
|
j++;
|
||||||
if (j == sizeof(swirr)-1)
|
if (j == sizeof(swirr) - 1)
|
||||||
j = 0;
|
j = 0;
|
||||||
|
|
||||||
fprintf(stdout, "[");
|
fprintf(stdout, "[");
|
||||||
|
@ -135,7 +135,14 @@ int main(int argc, char **argv)
|
||||||
device_name = optarg;
|
device_name = optarg;
|
||||||
break;
|
break;
|
||||||
case 'o':
|
case 'o':
|
||||||
lines[i] = strtoul(optarg, NULL, 10);
|
/*
|
||||||
|
* Avoid overflow. Do not immediately error, we want to
|
||||||
|
* be able to accurately report on the amount of times
|
||||||
|
* '-o' was given to give an accurate error message
|
||||||
|
*/
|
||||||
|
if (i < GPIOHANDLES_MAX)
|
||||||
|
lines[i] = strtoul(optarg, NULL, 10);
|
||||||
|
|
||||||
i++;
|
i++;
|
||||||
break;
|
break;
|
||||||
case '?':
|
case '?':
|
||||||
|
@ -143,6 +150,14 @@ int main(int argc, char **argv)
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (i >= GPIOHANDLES_MAX) {
|
||||||
|
fprintf(stderr,
|
||||||
|
"Only %d occurrences of '-o' are allowed, %d were found\n",
|
||||||
|
GPIOHANDLES_MAX, i + 1);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
nlines = i;
|
nlines = i;
|
||||||
|
|
||||||
if (!device_name || !nlines) {
|
if (!device_name || !nlines) {
|
||||||
|
|
|
@ -17,7 +17,7 @@
|
||||||
#include <linux/gpio.h>
|
#include <linux/gpio.h>
|
||||||
#include "gpio-utils.h"
|
#include "gpio-utils.h"
|
||||||
|
|
||||||
#define COMSUMER "gpio-utils"
|
#define CONSUMER "gpio-utils"
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* doc: Operation of gpio
|
* doc: Operation of gpio
|
||||||
|
@ -209,7 +209,7 @@ int gpiotools_gets(const char *device_name, unsigned int *lines,
|
||||||
|
|
||||||
ret = gpiotools_request_linehandle(device_name, lines, nlines,
|
ret = gpiotools_request_linehandle(device_name, lines, nlines,
|
||||||
GPIOHANDLE_REQUEST_INPUT, data,
|
GPIOHANDLE_REQUEST_INPUT, data,
|
||||||
COMSUMER);
|
CONSUMER);
|
||||||
if (ret < 0)
|
if (ret < 0)
|
||||||
return ret;
|
return ret;
|
||||||
|
|
||||||
|
@ -259,7 +259,7 @@ int gpiotools_sets(const char *device_name, unsigned int *lines,
|
||||||
|
|
||||||
ret = gpiotools_request_linehandle(device_name, lines, nlines,
|
ret = gpiotools_request_linehandle(device_name, lines, nlines,
|
||||||
GPIOHANDLE_REQUEST_OUTPUT, data,
|
GPIOHANDLE_REQUEST_OUTPUT, data,
|
||||||
COMSUMER);
|
CONSUMER);
|
||||||
if (ret < 0)
|
if (ret < 0)
|
||||||
return ret;
|
return ret;
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,99 @@
|
||||||
|
// SPDX-License-Identifier: GPL-2.0-only
|
||||||
|
/*
|
||||||
|
* gpio-watch - monitor unrequested lines for property changes using the
|
||||||
|
* character device
|
||||||
|
*
|
||||||
|
* Copyright (C) 2019 BayLibre SAS
|
||||||
|
* Author: Bartosz Golaszewski <bgolaszewski@baylibre.com>
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <ctype.h>
|
||||||
|
#include <errno.h>
|
||||||
|
#include <fcntl.h>
|
||||||
|
#include <linux/gpio.h>
|
||||||
|
#include <poll.h>
|
||||||
|
#include <stdbool.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <sys/ioctl.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
|
||||||
|
int main(int argc, char **argv)
|
||||||
|
{
|
||||||
|
struct gpioline_info_changed chg;
|
||||||
|
struct gpioline_info req;
|
||||||
|
struct pollfd pfd;
|
||||||
|
int fd, i, j, ret;
|
||||||
|
char *event, *end;
|
||||||
|
ssize_t rd;
|
||||||
|
|
||||||
|
if (argc < 3)
|
||||||
|
goto err_usage;
|
||||||
|
|
||||||
|
fd = open(argv[1], O_RDWR | O_CLOEXEC);
|
||||||
|
if (fd < 0) {
|
||||||
|
perror("unable to open gpiochip");
|
||||||
|
return EXIT_FAILURE;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (i = 0, j = 2; i < argc - 2; i++, j++) {
|
||||||
|
memset(&req, 0, sizeof(req));
|
||||||
|
|
||||||
|
req.line_offset = strtoul(argv[j], &end, 0);
|
||||||
|
if (*end != '\0')
|
||||||
|
goto err_usage;
|
||||||
|
|
||||||
|
ret = ioctl(fd, GPIO_GET_LINEINFO_WATCH_IOCTL, &req);
|
||||||
|
if (ret) {
|
||||||
|
perror("unable to set up line watch");
|
||||||
|
return EXIT_FAILURE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pfd.fd = fd;
|
||||||
|
pfd.events = POLLIN | POLLPRI;
|
||||||
|
|
||||||
|
for (;;) {
|
||||||
|
ret = poll(&pfd, 1, 5000);
|
||||||
|
if (ret < 0) {
|
||||||
|
perror("error polling the linechanged fd");
|
||||||
|
return EXIT_FAILURE;
|
||||||
|
} else if (ret > 0) {
|
||||||
|
memset(&chg, 0, sizeof(chg));
|
||||||
|
rd = read(pfd.fd, &chg, sizeof(chg));
|
||||||
|
if (rd < 0 || rd != sizeof(chg)) {
|
||||||
|
if (rd != sizeof(chg))
|
||||||
|
errno = EIO;
|
||||||
|
|
||||||
|
perror("error reading line change event");
|
||||||
|
return EXIT_FAILURE;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (chg.event_type) {
|
||||||
|
case GPIOLINE_CHANGED_REQUESTED:
|
||||||
|
event = "requested";
|
||||||
|
break;
|
||||||
|
case GPIOLINE_CHANGED_RELEASED:
|
||||||
|
event = "released";
|
||||||
|
break;
|
||||||
|
case GPIOLINE_CHANGED_CONFIG:
|
||||||
|
event = "config changed";
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
fprintf(stderr,
|
||||||
|
"invalid event type received from the kernel\n");
|
||||||
|
return EXIT_FAILURE;
|
||||||
|
}
|
||||||
|
|
||||||
|
printf("line %u: %s at %llu\n",
|
||||||
|
chg.info.line_offset, event, chg.timestamp);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
err_usage:
|
||||||
|
printf("%s: <gpiochip> <line0> <line1> ...\n", argv[0]);
|
||||||
|
return EXIT_FAILURE;
|
||||||
|
}
|
Loading…
Reference in New Issue