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:
Linus Torvalds 2020-04-04 10:27:00 -07:00
commit 828907ef25
46 changed files with 1817 additions and 777 deletions

View File

@ -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.
If you do this, the additional irq_chip will be set up by gpiolib at the
same time as setting up the rest of the GPIO functionality. The following
is a typical example of a cascaded interrupt handler using gpio_irq_chip::
is a typical example of a cascaded interrupt handler using gpio_irq_chip:
.. 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);
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

View File

@ -410,13 +410,10 @@ static int __ref impd1_probe(struct lm_device *dev)
* 5 = Key lower right
*/
/* We need the two MMCI GPIO entries */
lookup->table[0].chip_label = chipname;
lookup->table[0].chip_hwnum = 3;
lookup->table[0].con_id = "wp";
lookup->table[1].chip_label = chipname;
lookup->table[1].chip_hwnum = 4;
lookup->table[1].con_id = "cd";
lookup->table[1].flags = GPIO_ACTIVE_LOW;
lookup->table[0] = (struct gpiod_lookup)
GPIO_LOOKUP(chipname, 3, "wp", 0);
lookup->table[1] = (struct gpiod_lookup)
GPIO_LOOKUP(chipname, 4, "cd", GPIO_ACTIVE_LOW);
gpiod_add_lookup_table(lookup);
}

View File

@ -109,6 +109,7 @@ static int omap3_enter_idle(struct cpuidle_device *dev,
int index)
{
struct omap3_idle_statedata *cx = &omap3_idle_data[index];
int error;
if (omap_irq_pending() || need_resched())
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
* VFP context is saved.
*/
if (cx->mpu_state == PWRDM_POWER_OFF)
cpu_pm_enter();
if (cx->mpu_state == PWRDM_POWER_OFF) {
error = cpu_pm_enter();
if (error)
goto out_clkdm_set;
}
/* Execute ARM wfi */
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)
cpu_pm_exit();
out_clkdm_set:
/* Re-allow idle for C1 */
if (cx->flags & OMAP_CPUIDLE_CX_NO_CLKDM_IDLE)
clkdm_allow_idle(mpu_pd->pwrdm_clkdms[0]);

View File

@ -122,6 +122,7 @@ static int omap_enter_idle_coupled(struct cpuidle_device *dev,
{
struct idle_statedata *cx = state_ptr + index;
u32 mpuss_can_lose_context = 0;
int error;
/*
* 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
* 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) {
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
* to save GIC and wakeupgen context.
*/
if (mpuss_can_lose_context)
cpu_cluster_pm_enter();
if (mpuss_can_lose_context) {
error = cpu_cluster_pm_enter();
if (error)
goto cpu_cluster_pm_out;
}
}
omap4_enter_lowpower(dev->cpu, cx->cpu_state);
cpu_done[dev->cpu] = true;
cpu_cluster_pm_out:
/* Wakeup CPU1 only if it is not offlined */
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
* 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)
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();
fail:

View File

@ -191,6 +191,7 @@ void omap_sram_idle(void)
int per_next_state = PWRDM_POWER_ON;
int core_next_state = PWRDM_POWER_ON;
u32 sdrc_pwr = 0;
int error;
mpu_next_state = pwrdm_read_next_pwrst(mpu_pwrdm);
switch (mpu_next_state) {
@ -219,8 +220,11 @@ void omap_sram_idle(void)
pwrdm_pre_transition(NULL);
/* PER */
if (per_next_state == PWRDM_POWER_OFF)
cpu_cluster_pm_enter();
if (per_next_state == PWRDM_POWER_OFF) {
error = cpu_cluster_pm_enter();
if (error)
return;
}
/* CORE */
if (core_next_state < PWRDM_POWER_ON) {

View File

@ -394,13 +394,13 @@ config GPIO_MVEBU
config GPIO_MXC
def_bool y
depends on ARCH_MXC
depends on ARCH_MXC || COMPILE_TEST
select GPIO_GENERIC
select GENERIC_IRQ_CHIP
config GPIO_MXS
def_bool y
depends on ARCH_MXS
depends on ARCH_MXS || COMPILE_TEST
select GPIO_GENERIC
select GENERIC_IRQ_CHIP
@ -1399,6 +1399,13 @@ config GPIO_MLXBF
help
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
tristate "OKI SEMICONDUCTOR ML7213 IOH GPIO support"
depends on X86 || COMPILE_TEST

View File

@ -93,6 +93,7 @@ obj-$(CONFIG_GPIO_MENZ127) += gpio-menz127.o
obj-$(CONFIG_GPIO_MERRIFIELD) += gpio-merrifield.o
obj-$(CONFIG_GPIO_ML_IOH) += gpio-ml-ioh.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_MOCKUP) += gpio-mockup.o
obj-$(CONFIG_GPIO_MOXTET) += gpio-moxtet.o

View File

@ -603,6 +603,49 @@ static const struct dev_pm_ops brcmstb_gpio_pm_ops = {
.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)
{
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);
gc->write_reg(reg_base + GIO_MASK(bank->id), 0);
brcmstb_gpio_set_names(dev, bank);
err = gpiochip_add_data(gc, bank);
if (err) {
dev_err(dev, "Could not add gpiochip for bank %d\n",

View File

@ -259,11 +259,8 @@ static int davinci_gpio_probe(struct platform_device *pdev)
chips->chip.of_gpio_n_cells = 2;
chips->chip.parent = dev;
chips->chip.of_node = dev->of_node;
if (of_property_read_bool(dev->of_node, "gpio-ranges")) {
chips->chip.request = gpiochip_generic_request;
chips->chip.free = gpiochip_generic_free;
}
chips->chip.request = gpiochip_generic_request;
chips->chip.free = gpiochip_generic_free;
#endif
spin_lock_init(&chips->lock);

View File

@ -569,6 +569,7 @@ static int sprd_eic_probe(struct platform_device *pdev)
const struct sprd_eic_variant_data *pdata;
struct gpio_irq_chip *irq;
struct sprd_eic *sprd_eic;
struct resource *res;
int ret, i;
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
* optional.
*/
sprd_eic->base[i] = devm_platform_ioremap_resource(pdev, i);
if (IS_ERR(sprd_eic->base[i]))
res = platform_get_resource(pdev, IORESOURCE_MEM, i);
if (!res)
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];

335
drivers/gpio/gpio-mlxbf2.c Normal file
View File

@ -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");

View File

@ -389,12 +389,10 @@ static int bgpio_get_dir(struct gpio_chip *gc, unsigned int gpio)
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;
gc->set(gc, gpio, val);
spin_lock_irqsave(&gc->bgpio_lock, flags);
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);
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;
}
@ -538,7 +550,10 @@ static int bgpio_setup_direction(struct gpio_chip *gc,
if (dirout || dirin) {
gc->reg_dir_out = dirout;
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->get_direction = bgpio_get_dir;
} else {

View File

@ -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. */
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);
out:

View File

@ -227,8 +227,8 @@ mediatek_gpio_bank_probe(struct device *dev,
ctrl = mtk->base + GPIO_REG_DCLR + (rg->bank * GPIO_BANK_STRIDE);
diro = mtk->base + GPIO_REG_CTRL + (rg->bank * GPIO_BANK_STRIDE);
ret = bgpio_init(&rg->chip, dev, 4,
dat, set, ctrl, diro, NULL, 0);
ret = bgpio_init(&rg->chip, dev, 4, dat, set, ctrl, diro, NULL,
BGPIOF_NO_SET_ON_INPUT);
if (ret) {
dev_err(dev, "bgpio_init() failed\n");
return ret;

View File

@ -1247,7 +1247,7 @@ static int mvebu_gpio_probe(struct platform_device *pdev)
* pins.
*/
for (i = 0; i < 4; i++) {
int irq = platform_get_irq(pdev, i);
int irq = platform_get_irq_optional(pdev, i);
if (irq < 0)
continue;

View File

@ -485,11 +485,8 @@ static int mxc_gpio_probe(struct platform_device *pdev)
if (err)
goto out_bgio;
if (of_property_read_bool(np, "gpio-ranges")) {
port->gc.request = gpiochip_generic_request;
port->gc.free = gpiochip_generic_free;
}
port->gc.request = gpiochip_generic_request;
port->gc.free = gpiochip_generic_free;
port->gc.to_irq = mxc_gpio_to_irq;
port->gc.base = (pdev->id < 0) ? of_alias_get_id(np, "gpio") * 32 :
pdev->id * 32;

View File

@ -1102,23 +1102,13 @@ static void omap_gpio_idle(struct gpio_bank *bank, bool may_lose_context)
{
struct device *dev = bank->chip.parent;
void __iomem *base = bank->base;
u32 mask, nowake;
u32 nowake;
bank->saved_datain = readl_relaxed(base + bank->regs->datain);
if (!bank->enabled_non_wakeup_gpios)
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)
goto update_gpio_context_count;
@ -1237,26 +1227,35 @@ static int gpio_omap_cpu_notifier(struct notifier_block *nb,
{
struct gpio_bank *bank;
unsigned long flags;
int ret = NOTIFY_OK;
u32 isr, mask;
bank = container_of(nb, struct gpio_bank, nb);
raw_spin_lock_irqsave(&bank->lock, flags);
if (bank->is_suspended)
goto out_unlock;
switch (cmd) {
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;
}
omap_gpio_idle(bank, true);
break;
case CPU_CLUSTER_PM_ENTER_FAILED:
case CPU_CLUSTER_PM_EXIT:
if (bank->is_suspended)
break;
omap_gpio_unidle(bank);
break;
}
out_unlock:
raw_spin_unlock_irqrestore(&bank->lock, flags);
return NOTIFY_OK;
return ret;
}
static const struct omap_gpio_reg_offs omap2_gpio_regs = {

View File

@ -298,11 +298,8 @@ static int pl061_probe(struct amba_device *adev, const struct amba_id *id)
return PTR_ERR(pl061->base);
raw_spin_lock_init(&pl061->lock);
if (of_property_read_bool(dev->of_node, "gpio-ranges")) {
pl061->gc.request = gpiochip_generic_request;
pl061->gc.free = gpiochip_generic_free;
}
pl061->gc.request = gpiochip_generic_request;
pl061->gc.free = gpiochip_generic_free;
pl061->gc.base = -1;
pl061->gc.get_direction = pl061_get_direction;
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 */
irq = adev->irq[0];
if (irq < 0) {
dev_err(&adev->dev, "invalid IRQ\n");
return -ENODEV;
}
if (!irq)
dev_warn(&adev->dev, "IRQ support disabled\n");
pl061->parent_irq = irq;
girq = &pl061->gc.irq;

View File

@ -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.to_irq = pxa_gpio_to_irq;
pchip->chip.ngpio = ngpio;
if (pxa_gpio_has_pinctrl()) {
pchip->chip.request = gpiochip_generic_request;
pchip->chip.free = gpiochip_generic_free;
}
pchip->chip.request = gpiochip_generic_request;
pchip->chip.free = gpiochip_generic_free;
#ifdef CONFIG_OF_GPIO
pchip->chip.of_node = np;
@ -652,8 +649,8 @@ static int pxa_gpio_probe(struct platform_device *pdev)
if (!pchip->irqdomain)
return -ENOMEM;
irq0 = platform_get_irq_byname(pdev, "gpio0");
irq1 = platform_get_irq_byname(pdev, "gpio1");
irq0 = platform_get_irq_byname_optional(pdev, "gpio0");
irq1 = platform_get_irq_byname_optional(pdev, "gpio1");
irq_mux = platform_get_irq_byname(pdev, "gpio_mux");
if ((irq0 > 0 && irq1 <= 0) || (irq0 <= 0 && irq1 > 0)
|| (irq_mux <= 0))

View File

@ -116,7 +116,7 @@ static void gpio_rcar_config_interrupt_input_mode(struct gpio_rcar_priv *p,
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);
/* 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);
/* Configure postive logic in POSNEG */
/* Configure positive logic in POSNEG */
gpio_rcar_modify_bit(p, POSNEG, gpio, false);
/* Select "General Input/Output Mode" in IOINTSEL */

View File

@ -15,7 +15,7 @@ struct gpio_siox_ddata {
u8 setdata[1];
u8 getdata[3];
spinlock_t irqlock;
raw_spinlock_t irqlock;
u32 irq_enable;
u32 irq_status;
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);
spin_lock_irq(&ddata->irqlock);
raw_spin_lock_irq(&ddata->irqlock);
for (offset = 0; offset < 12; ++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;
spin_unlock_irq(&ddata->irqlock);
raw_spin_unlock_irq(&ddata->irqlock);
ddata->getdata[0] = buf[0];
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
* to clean the irq_status here.
*/
spin_lock_irq(&ddata->irqlock);
raw_spin_lock_irq(&ddata->irqlock);
ddata->irq_status &= ~(1 << offset);
spin_unlock_irq(&ddata->irqlock);
raw_spin_unlock_irq(&ddata->irqlock);
handle_nested_irq(irq);
}
@ -101,9 +101,9 @@ static void gpio_siox_irq_ack(struct irq_data *d)
struct gpio_siox_ddata *ddata =
container_of(ic, struct gpio_siox_ddata, ichip);
spin_lock_irq(&ddata->irqlock);
raw_spin_lock(&ddata->irqlock);
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)
@ -112,9 +112,9 @@ static void gpio_siox_irq_mask(struct irq_data *d)
struct gpio_siox_ddata *ddata =
container_of(ic, struct gpio_siox_ddata, ichip);
spin_lock_irq(&ddata->irqlock);
raw_spin_lock(&ddata->irqlock);
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)
@ -123,9 +123,9 @@ static void gpio_siox_irq_unmask(struct irq_data *d)
struct gpio_siox_ddata *ddata =
container_of(ic, struct gpio_siox_ddata, ichip);
spin_lock_irq(&ddata->irqlock);
raw_spin_lock(&ddata->irqlock);
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)
@ -134,9 +134,9 @@ static int gpio_siox_irq_set_type(struct irq_data *d, u32 type)
struct gpio_siox_ddata *ddata =
container_of(ic, struct gpio_siox_ddata, ichip);
spin_lock_irq(&ddata->irqlock);
raw_spin_lock(&ddata->irqlock);
ddata->irq_type[d->hwirq] = type;
spin_unlock_irq(&ddata->irqlock);
raw_spin_unlock(&ddata->irqlock);
return 0;
}
@ -222,7 +222,7 @@ static int gpio_siox_probe(struct siox_device *sdevice)
dev_set_drvdata(dev, ddata);
mutex_init(&ddata->lock);
spin_lock_init(&ddata->irqlock);
raw_spin_lock_init(&ddata->irqlock);
ddata->gchip.base = -1;
ddata->gchip.can_sleep = 1;

View File

@ -58,11 +58,20 @@ struct tegra_gpio_port {
unsigned int pins;
};
struct tegra186_pin_range {
unsigned int offset;
const char *group;
};
struct tegra_gpio_soc {
const struct tegra_gpio_port *ports;
unsigned int num_ports;
const char *name;
unsigned int instance;
const struct tegra186_pin_range *pin_ranges;
unsigned int num_pin_ranges;
const char *pinmux;
};
struct tegra_gpio {
@ -254,6 +263,50 @@ static int tegra186_gpio_set_config(struct gpio_chip *chip,
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,
const struct of_phandle_args *spec,
u32 *flags)
@ -578,12 +631,15 @@ static int tegra186_gpio_probe(struct platform_device *pdev)
gpio->gpio.label = gpio->soc->name;
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.direction_input = tegra186_gpio_direction_input;
gpio->gpio.direction_output = tegra186_gpio_direction_output;
gpio->gpio.get = tegra186_gpio_get,
gpio->gpio.set = tegra186_gpio_set;
gpio->gpio.set_config = tegra186_gpio_set_config;
gpio->gpio.add_pin_ranges = tegra186_gpio_add_pin_ranges;
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)
};
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 = {
.num_ports = ARRAY_SIZE(tegra194_main_ports),
.ports = tegra194_main_ports,
.name = "tegra194-gpio",
.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) \

View File

@ -30,7 +30,7 @@ struct uniphier_gpio_priv {
struct irq_domain *domain;
void __iomem *regs;
spinlock_t lock;
u32 saved_vals[0];
u32 saved_vals[];
};
static unsigned int uniphier_gpio_bank_to_reg(unsigned int bank)

View File

@ -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)
{
struct wcd_gpio_data *data = gpiochip_get_data(chip);
int value;
unsigned int 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)
{
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)

View File

@ -226,13 +226,11 @@ static int zx_gpio_probe(struct platform_device *pdev)
if (IS_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");
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_output = zx_direction_output;
chip->gc.get = zx_get_value;

View File

@ -478,3 +478,49 @@ void devm_gpio_free(struct device *dev, unsigned int gpio)
&gpio));
}
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);

View File

@ -604,6 +604,39 @@ static struct gpio_desc *of_parse_own_gpio(struct device_node *np,
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
* @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)
{
struct gpio_desc *desc = NULL;
struct device_node *np;
const char *name;
unsigned long lflags;
enum gpiod_flags dflags;
unsigned int i;
int ret;
for_each_available_child_of_node(chip->of_node, np) {
if (!of_property_read_bool(np, "gpio-hog"))
continue;
for (i = 0;; i++) {
desc = of_parse_own_gpio(np, chip, i, &name, &lflags,
&dflags);
if (IS_ERR(desc))
break;
ret = gpiod_hog(desc, name, lflags, dflags);
if (ret < 0) {
of_node_put(np);
return ret;
}
ret = of_gpiochip_add_hog(chip, np);
if (ret < 0) {
of_node_put(np);
return ret;
}
of_node_set_flag(np, OF_POPULATED);
}
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
* @gc: pointer to the gpio_chip structure

View File

@ -35,4 +35,6 @@ static inline bool of_gpio_need_valid_mask(const struct gpio_chip *gc)
}
#endif /* CONFIG_OF_GPIO */
extern struct notifier_block gpio_of_notifier;
#endif /* GPIOLIB_OF_H */

File diff suppressed because it is too large Load Diff

View File

@ -56,6 +56,7 @@ struct gpio_device {
const char *label;
void *data;
struct list_head list;
struct atomic_notifier_head notifier;
#ifdef CONFIG_PINCTRL
/*
@ -119,6 +120,9 @@ struct gpio_desc {
const char *label;
/* Name of the GPIO */
const char *name;
#ifdef CONFIG_OF_DYNAMIC
struct device_node *hog;
#endif
};
int gpiod_request(struct gpio_desc *desc, const char *label);

View File

@ -103,6 +103,7 @@ struct pinctrl_dev *of_pinctrl_get(struct device_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,
struct pinctrl_dev *hog_pctldev,

View File

@ -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_output = ingenic_gpio_direction_output;
jzgc->gc.get_direction = ingenic_gpio_get_direction;
if (of_property_read_bool(node, "gpio-ranges")) {
jzgc->gc.request = gpiochip_generic_request;
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);
if (!jzgc->irq)

View File

@ -2,10 +2,8 @@
#ifndef _ASM_GENERIC_GPIO_H
#define _ASM_GENERIC_GPIO_H
#include <linux/kernel.h>
#include <linux/types.h>
#include <linux/errno.h>
#include <linux/of.h>
#ifdef CONFIG_GPIOLIB
@ -140,6 +138,8 @@ static inline void gpio_unexport(unsigned gpio)
#else /* !CONFIG_GPIOLIB */
#include <linux/kernel.h>
static inline bool gpio_is_valid(int number)
{
/* only non-negative numbers are valid */

View File

@ -102,11 +102,9 @@ void devm_gpio_free(struct device *dev, unsigned int gpio);
#include <linux/kernel.h>
#include <linux/types.h>
#include <linux/bug.h>
#include <linux/pinctrl/pinctrl.h>
struct device;
struct gpio_chip;
struct pinctrl_dev;
static inline bool gpio_is_valid(int number)
{

View File

@ -2,9 +2,10 @@
#ifndef __LINUX_GPIO_CONSUMER_H
#define __LINUX_GPIO_CONSUMER_H
#include <linux/bits.h>
#include <linux/bug.h>
#include <linux/compiler_types.h>
#include <linux/err.h>
#include <linux/kernel.h>
struct device;
@ -156,6 +157,7 @@ int gpiod_set_raw_array_value_cansleep(unsigned int array_size,
struct gpio_array *array_info,
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_transitory(struct gpio_desc *desc, bool transitory);
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 */
#include <linux/kernel.h>
static inline int gpiod_count(struct device *dev, const char *con_id)
{
return 0;
@ -470,6 +474,13 @@ static inline int gpiod_set_raw_array_value_cansleep(unsigned int array_size,
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)
{
/* GPIO can never have been requested */

View File

@ -87,7 +87,7 @@ struct gpio_irq_chip {
* @need_valid_mask to make these GPIO lines unavailable for
* 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_type,
unsigned int *parent_hwirq,
@ -102,7 +102,7 @@ struct gpio_irq_chip {
* variant named &gpiochip_populate_parent_fwspec_fourcell is also
* 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_type);
@ -114,7 +114,7 @@ struct gpio_irq_chip {
* callback. If this is not specified, then a default callback will be
* 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);
/**
@ -209,7 +209,7 @@ struct gpio_irq_chip {
* a particular driver wants to clear IRQ related registers
* 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
@ -220,7 +220,7 @@ struct gpio_irq_chip {
* then directly set some bits to "0" if they cannot be used for
* interrupts.
*/
void (*init_valid_mask)(struct gpio_chip *chip,
void (*init_valid_mask)(struct gpio_chip *gc,
unsigned long *valid_mask,
unsigned int ngpios);
@ -348,40 +348,40 @@ struct gpio_chip {
struct device *parent;
struct module *owner;
int (*request)(struct gpio_chip *chip,
int (*request)(struct gpio_chip *gc,
unsigned offset);
void (*free)(struct gpio_chip *chip,
void (*free)(struct gpio_chip *gc,
unsigned offset);
int (*get_direction)(struct gpio_chip *chip,
int (*get_direction)(struct gpio_chip *gc,
unsigned offset);
int (*direction_input)(struct gpio_chip *chip,
int (*direction_input)(struct gpio_chip *gc,
unsigned offset);
int (*direction_output)(struct gpio_chip *chip,
int (*direction_output)(struct gpio_chip *gc,
unsigned offset, int value);
int (*get)(struct gpio_chip *chip,
int (*get)(struct gpio_chip *gc,
unsigned offset);
int (*get_multiple)(struct gpio_chip *chip,
int (*get_multiple)(struct gpio_chip *gc,
unsigned long *mask,
unsigned long *bits);
void (*set)(struct gpio_chip *chip,
void (*set)(struct gpio_chip *gc,
unsigned offset, int value);
void (*set_multiple)(struct gpio_chip *chip,
void (*set_multiple)(struct gpio_chip *gc,
unsigned long *mask,
unsigned long *bits);
int (*set_config)(struct gpio_chip *chip,
int (*set_config)(struct gpio_chip *gc,
unsigned offset,
unsigned long config);
int (*to_irq)(struct gpio_chip *chip,
int (*to_irq)(struct gpio_chip *gc,
unsigned offset);
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 int ngpios);
int (*add_pin_ranges)(struct gpio_chip *chip);
int (*add_pin_ranges)(struct gpio_chip *gc);
int base;
u16 ngpio;
@ -458,11 +458,11 @@ struct gpio_chip {
#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);
/* 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 *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.
*/
#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 request_key; \
gpiochip_add_data_with_key(chip, data, &lock_key, \
gpiochip_add_data_with_key(gc, data, &lock_key, \
&request_key); \
})
#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 */
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 int devm_gpiochip_add_data(struct device *dev, struct gpio_chip *chip,
extern void gpiochip_remove(struct gpio_chip *gc);
extern int devm_gpiochip_add_data(struct device *dev, struct gpio_chip *gc,
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);
int gpiochip_reqres_irq(struct gpio_chip *chip, unsigned int offset);
void gpiochip_relres_irq(struct gpio_chip *chip, unsigned int offset);
void gpiochip_disable_irq(struct gpio_chip *chip, unsigned int offset);
void gpiochip_enable_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 *gc, unsigned int offset);
void gpiochip_relres_irq(struct gpio_chip *gc, unsigned int offset);
void gpiochip_disable_irq(struct gpio_chip *gc, unsigned int offset);
void gpiochip_enable_irq(struct gpio_chip *gc, unsigned int offset);
/* Line status inquiry for drivers */
bool gpiochip_line_is_open_drain(struct gpio_chip *chip, unsigned int offset);
bool gpiochip_line_is_open_source(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 *gc, unsigned int offset);
/* Sleep persistence inquiry for drivers */
bool gpiochip_line_is_persistent(struct gpio_chip *chip, unsigned int offset);
bool gpiochip_line_is_valid(const 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 *gc, unsigned int offset);
/* get driver data */
void *gpiochip_get_data(struct gpio_chip *chip);
void *gpiochip_get_data(struct gpio_chip *gc);
struct bgpio_pdata {
const char *label;
@ -536,23 +536,23 @@ struct bgpio_pdata {
#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_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_type);
#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_type)
{
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_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_READ_OUTPUT_REG_SET BIT(4) /* reg_set stores output value */
#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,
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,
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,
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,
unsigned int first_irq,
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 *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);
#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
* 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,
unsigned int first_irq,
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 request_key;
return gpiochip_irqchip_add_key(gpiochip, irqchip, first_irq,
return gpiochip_irqchip_add_key(gc, irqchip, first_irq,
handler, type, false,
&lock_key, &request_key);
}
static inline int gpiochip_irqchip_add_nested(struct gpio_chip *gpiochip,
static inline int gpiochip_irqchip_add_nested(struct gpio_chip *gc,
struct irq_chip *irqchip,
unsigned int first_irq,
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 request_key;
return gpiochip_irqchip_add_key(gpiochip, irqchip, first_irq,
return gpiochip_irqchip_add_key(gc, irqchip, first_irq,
handler, type, true,
&lock_key, &request_key);
}
#else /* ! CONFIG_LOCKDEP */
static inline int gpiochip_irqchip_add(struct gpio_chip *gpiochip,
static inline int gpiochip_irqchip_add(struct gpio_chip *gc,
struct irq_chip *irqchip,
unsigned int first_irq,
irq_flow_handler_t handler,
unsigned int type)
{
return gpiochip_irqchip_add_key(gpiochip, irqchip, first_irq,
return gpiochip_irqchip_add_key(gc, irqchip, first_irq,
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,
unsigned int first_irq,
irq_flow_handler_t handler,
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);
}
#endif /* CONFIG_LOCKDEP */
int gpiochip_generic_request(struct gpio_chip *chip, unsigned offset);
void gpiochip_generic_free(struct gpio_chip *chip, unsigned offset);
int gpiochip_generic_config(struct gpio_chip *chip, unsigned offset,
int gpiochip_generic_request(struct gpio_chip *gc, unsigned offset);
void gpiochip_generic_free(struct gpio_chip *gc, unsigned offset);
int gpiochip_generic_config(struct gpio_chip *gc, unsigned offset,
unsigned long config);
/**
@ -675,25 +676,25 @@ struct gpio_pin_range {
#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 npins);
int gpiochip_add_pingroup_range(struct gpio_chip *chip,
int gpiochip_add_pingroup_range(struct gpio_chip *gc,
struct pinctrl_dev *pctldev,
unsigned int gpio_offset, const char *pin_group);
void gpiochip_remove_pin_ranges(struct gpio_chip *chip);
void gpiochip_remove_pin_ranges(struct gpio_chip *gc);
#else /* ! CONFIG_PINCTRL */
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 npins)
{
return 0;
}
static inline int
gpiochip_add_pingroup_range(struct gpio_chip *chip,
gpiochip_add_pingroup_range(struct gpio_chip *gc,
struct pinctrl_dev *pctldev,
unsigned int gpio_offset, const char *pin_group)
{
@ -701,27 +702,27 @@ gpiochip_add_pingroup_range(struct gpio_chip *chip,
}
static inline void
gpiochip_remove_pin_ranges(struct gpio_chip *chip)
gpiochip_remove_pin_ranges(struct gpio_chip *gc)
{
}
#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,
const char *label,
enum gpio_lookup_flags lflags,
enum gpiod_flags dflags);
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);
#ifdef CONFIG_GPIOLIB
/* lock/unlock as IRQ */
int gpiochip_lock_as_irq(struct gpio_chip *chip, unsigned int offset);
void gpiochip_unlock_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 *gc, unsigned int offset);
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);
}
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)
{
WARN_ON(1);
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)
{
WARN_ON(1);

View File

@ -246,6 +246,37 @@ __kfifo_int_must_check_helper(int val)
__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
* @fifo: address of the fifo to be used
@ -517,6 +548,26 @@ __kfifo_uint_must_check_helper( \
__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 */
#define kfifo_in_locked(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 */
#define kfifo_out_locked(fifo, buf, n, lock) \
kfifo_out_spinlocked(fifo, buf, n, lock)

View File

@ -11,9 +11,8 @@
#define __LINUX_OF_GPIO_H
#include <linux/compiler.h>
#include <linux/kernel.h>
#include <linux/errno.h>
#include <linux/gpio.h>
#include <linux/gpio/driver.h>
#include <linux/gpio.h> /* FIXME: Shouldn't be here */
#include <linux/of.h>
struct device_node;
@ -34,6 +33,8 @@ enum of_gpio_flags {
#ifdef CONFIG_OF_GPIO
#include <linux/kernel.h>
/*
* 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 */
#include <linux/errno.h>
/* 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,
const char *list_name, int index, enum of_gpio_flags *flags)

View File

@ -186,7 +186,7 @@ extern int pinctrl_get_group_pins(struct pinctrl_dev *pctldev,
const char *pin_group, const unsigned **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);
#else
static inline

View File

@ -18,7 +18,7 @@
* struct gpiochip_info - Information about a certain GPIO chip
* @name: the Linux kernel name of this GPIO chip
* @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
*/
struct gpiochip_info {
@ -44,10 +44,10 @@ struct gpiochip_info {
* @flags: various flags for this line
* @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, may be NULL
* chip, may be empty
* @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
* also be NULL if the consumer doesn't set this up
* whatever is using it, will be empty if there is no current user but may
* also be empty if the consumer doesn't set this up
*/
struct gpioline_info {
__u32 line_offset;
@ -59,6 +59,34 @@ struct gpioline_info {
/* Maximum number of requested handles */
#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 */
#define GPIOHANDLE_REQUEST_INPUT (1UL << 0)
#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_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_LINEEVENT_IOCTL _IOWR(0xB4, 0x04, struct gpioevent_request)

View File

@ -1,5 +1,6 @@
# SPDX-License-Identifier: GPL-2.0-only
gpio-event-mon
gpio-hammer
gpio-watch
lsgpio
include/linux/gpio.h

View File

@ -2,3 +2,4 @@ gpio-utils-y += gpio-utils.o
lsgpio-y += lsgpio.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-watch-y += gpio-watch.o

View File

@ -18,7 +18,7 @@ MAKEFLAGS += -r
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: $(ALL_PROGRAMS)
@ -35,7 +35,7 @@ $(OUTPUT)include/linux/gpio.h: ../../include/uapi/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
$(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)
$(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:
rm -f $(ALL_PROGRAMS)
rm -f $(OUTPUT)include/linux/gpio.h

View File

@ -77,7 +77,7 @@ int hammer_device(const char *device_name, unsigned int *lines, int nlines,
fprintf(stdout, "[%c] ", swirr[j]);
j++;
if (j == sizeof(swirr)-1)
if (j == sizeof(swirr) - 1)
j = 0;
fprintf(stdout, "[");
@ -135,7 +135,14 @@ int main(int argc, char **argv)
device_name = optarg;
break;
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++;
break;
case '?':
@ -143,6 +150,14 @@ int main(int argc, char **argv)
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;
if (!device_name || !nlines) {

View File

@ -17,7 +17,7 @@
#include <linux/gpio.h>
#include "gpio-utils.h"
#define COMSUMER "gpio-utils"
#define CONSUMER "gpio-utils"
/**
* 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,
GPIOHANDLE_REQUEST_INPUT, data,
COMSUMER);
CONSUMER);
if (ret < 0)
return ret;
@ -259,7 +259,7 @@ int gpiotools_sets(const char *device_name, unsigned int *lines,
ret = gpiotools_request_linehandle(device_name, lines, nlines,
GPIOHANDLE_REQUEST_OUTPUT, data,
COMSUMER);
CONSUMER);
if (ret < 0)
return ret;

99
tools/gpio/gpio-watch.c Normal file
View File

@ -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;
}