kernel_optimize_test/drivers/gpio/gpio-pl061.c
Linus Torvalds 2ec98f5678 Bulk GPIO changes for the v5.3 kernel cycle:
Core:
 
 - When a gpio_chip request GPIOs from itself, it can now fully
   control the line characteristics, both machine and consumer
   flags. This makes a lot of sense, but took some time before I
   figured out that this is how it has to work.
 
 - Several smallish documentation fixes.
 
 New drivers:
 
 - The PCA953x driver now supports the TI TCA9539.
 
 - The DaVinci driver now supports the K3 AM654 SoCs.
 
 Driver improvements:
 
 - Major overhaul and hardening of the OMAP driver by Russell
   King.
 
 - Starting to move some drivers to the new API passing irq_chip
   along with the gpio_chip when adding the gpio_chip instead
   of adding it separately.
 
 Unrelated:
 
 - Delete the FMC subsystem.
 -----BEGIN PGP SIGNATURE-----
 
 iQIzBAABCAAdFiEElDRnuGcz/wPCXQWMQRCzN7AZXXMFAl0i7gEACgkQQRCzN7AZ
 XXOeUA/+JKyI2zebTWBcgtxhn6VQCufMCtFmQl2JkEcy4pT7aBJcGWqFQCBW2Szf
 VTtqc8nNa90SZoOzsNbkeQgRjNKGZruMbh0ARUPcW4v3ZJHtUNUEDLTo8c3iyTgS
 9k/FTeaTLt4WSZujeAO0O7G4KNnOOlTKLh58dr0PmXUR+0v+fbMhcJqJ9ABueV+V
 qENdpkTuG1ZcvzgLhBBEXdt3Plw9ICLWmPXtwY+784ewucVPbyQX7jV4+bBZ25fL
 DerCuMIgL5vRWWdiFO6/Jp603rHzZpTnjLJJocXUFiD6zA5rvU2jTWxsnUttjisg
 8cTLMyQspsDvBxhEhCJVTuIKotbKH900TSaz+vx20W72/A1euy4y6uVi8FGZo4Ww
 KDkzB7anwHyEFKGnlYgHzDrfctgZrhQoyFz808DQRYg1JseZB5oGVDvScrPBD43j
 nbNDd8gwG4yp3tFnDx9xjIwQy3Ax4d510rAZyUN2801IlbA1bueq4t6Z2cCucWzX
 XA1gCKlXe4BUeitRAoZtqZNZG1ymEysW4jXy1V8xrwtAf8+QSN+xO98akz3VpnQL
 ae9q+HtF76fDBY1xFSXT37Ma3+4OR2vMF9QWuo4TCb9j1cL7llf8ZxtUq9LEHbDu
 erKLSSnwSFmqJNGSEA5SulGOCR/tRPkClngE9x0XEM6gOD+bs6E=
 =8zSV
 -----END PGP SIGNATURE-----

Merge tag 'gpio-v5.3-1' of git://git.kernel.org/pub/scm/linux/kernel/git/linusw/linux-gpio

Pull GPIO updates from Linus Walleij:
 "This is the big slew of GPIO changes for the v5.3 kernel cycle. This
  is mostly incremental work this time.

  Three important things:

   - The FMC subsystem is deleted through my tree. This happens through
     GPIO as its demise was discussed in relation to a patch decoupling
     its GPIO implementation from the standard way of handling GPIO. As
     it turns out, that is not the only subsystem it reimplements and
     the authors think it is better do scratch it and start over using
     the proper kernel subsystems than try to polish the rust shiny. See
     the commit (ACKed by the maintainers) for details.

   - Arnd made a small devres patch that was ACKed by Greg and goes into
     the device core.

   - SPDX header change colissions may happen, because at times I've
     seen that quite a lot changed during the -rc:s in regards to SPDX.
     (It is good stuff, tglx has me convinced, and it is worth the
     occasional pain.)

  Apart from this is is nothing controversial or problematic.

  Summary:

  Core:

   - When a gpio_chip request GPIOs from itself, it can now fully
     control the line characteristics, both machine and consumer flags.
     This makes a lot of sense, but took some time before I figured out
     that this is how it has to work.

   - Several smallish documentation fixes.

  New drivers:

   - The PCA953x driver now supports the TI TCA9539.

   - The DaVinci driver now supports the K3 AM654 SoCs.

  Driver improvements:

   - Major overhaul and hardening of the OMAP driver by Russell King.

   - Starting to move some drivers to the new API passing irq_chip along
     with the gpio_chip when adding the gpio_chip instead of adding it
     separately.

  Unrelated:

   - Delete the FMC subsystem"

* tag 'gpio-v5.3-1' of git://git.kernel.org/pub/scm/linux/kernel/git/linusw/linux-gpio: (87 commits)
  Revert "gpio: tegra: Clean-up debugfs initialisation"
  gpiolib: Use spinlock_t instead of struct spinlock
  gpio: stp-xway: allow compile-testing
  gpio: stp-xway: get rid of the #include <lantiq_soc.h> dependency
  gpio: stp-xway: improve module clock error handling
  gpio: stp-xway: simplify error handling in xway_stp_probe()
  gpiolib: Clarify use of non-sleeping functions
  gpiolib: Fix references to gpiod_[gs]et_*value_cansleep() variants
  gpiolib: Document new gpio_chip.init_valid_mask field
  Documentation: gpio: Fix reference to gpiod_get_array()
  gpio: pl061: drop duplicate printing of device name
  gpio: altera: Pass irqchip when adding gpiochip
  gpio: siox: Use devm_ managed gpiochip
  gpio: siox: Add struct device *dev helper variable
  gpio: siox: Pass irqchip when adding gpiochip
  drivers: gpio: amd-fch: make resource struct const
  devres: allow const resource arguments
  gpio: ath79: Pass irqchip when adding gpiochip
  gpio: tegra: Clean-up debugfs initialisation
  gpio: siox: Switch to IRQ_TYPE_NONE
  ...
2019-07-09 09:07:00 -07:00

430 lines
11 KiB
C

// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (C) 2008, 2009 Provigent Ltd.
*
* Author: Baruch Siach <baruch@tkos.co.il>
*
* Driver for the ARM PrimeCell(tm) General Purpose Input/Output (PL061)
*
* Data sheet: ARM DDI 0190B, September 2000
*/
#include <linux/spinlock.h>
#include <linux/errno.h>
#include <linux/init.h>
#include <linux/io.h>
#include <linux/ioport.h>
#include <linux/interrupt.h>
#include <linux/irq.h>
#include <linux/irqchip/chained_irq.h>
#include <linux/bitops.h>
#include <linux/gpio/driver.h>
#include <linux/device.h>
#include <linux/amba/bus.h>
#include <linux/slab.h>
#include <linux/pinctrl/consumer.h>
#include <linux/pm.h>
#define GPIODIR 0x400
#define GPIOIS 0x404
#define GPIOIBE 0x408
#define GPIOIEV 0x40C
#define GPIOIE 0x410
#define GPIORIS 0x414
#define GPIOMIS 0x418
#define GPIOIC 0x41C
#define PL061_GPIO_NR 8
#ifdef CONFIG_PM
struct pl061_context_save_regs {
u8 gpio_data;
u8 gpio_dir;
u8 gpio_is;
u8 gpio_ibe;
u8 gpio_iev;
u8 gpio_ie;
};
#endif
struct pl061 {
raw_spinlock_t lock;
void __iomem *base;
struct gpio_chip gc;
struct irq_chip irq_chip;
int parent_irq;
#ifdef CONFIG_PM
struct pl061_context_save_regs csave_regs;
#endif
};
static int pl061_get_direction(struct gpio_chip *gc, unsigned offset)
{
struct pl061 *pl061 = gpiochip_get_data(gc);
return !(readb(pl061->base + GPIODIR) & BIT(offset));
}
static int pl061_direction_input(struct gpio_chip *gc, unsigned offset)
{
struct pl061 *pl061 = gpiochip_get_data(gc);
unsigned long flags;
unsigned char gpiodir;
raw_spin_lock_irqsave(&pl061->lock, flags);
gpiodir = readb(pl061->base + GPIODIR);
gpiodir &= ~(BIT(offset));
writeb(gpiodir, pl061->base + GPIODIR);
raw_spin_unlock_irqrestore(&pl061->lock, flags);
return 0;
}
static int pl061_direction_output(struct gpio_chip *gc, unsigned offset,
int value)
{
struct pl061 *pl061 = gpiochip_get_data(gc);
unsigned long flags;
unsigned char gpiodir;
raw_spin_lock_irqsave(&pl061->lock, flags);
writeb(!!value << offset, pl061->base + (BIT(offset + 2)));
gpiodir = readb(pl061->base + GPIODIR);
gpiodir |= BIT(offset);
writeb(gpiodir, pl061->base + GPIODIR);
/*
* gpio value is set again, because pl061 doesn't allow to set value of
* a gpio pin before configuring it in OUT mode.
*/
writeb(!!value << offset, pl061->base + (BIT(offset + 2)));
raw_spin_unlock_irqrestore(&pl061->lock, flags);
return 0;
}
static int pl061_get_value(struct gpio_chip *gc, unsigned offset)
{
struct pl061 *pl061 = gpiochip_get_data(gc);
return !!readb(pl061->base + (BIT(offset + 2)));
}
static void pl061_set_value(struct gpio_chip *gc, unsigned offset, int value)
{
struct pl061 *pl061 = gpiochip_get_data(gc);
writeb(!!value << offset, pl061->base + (BIT(offset + 2)));
}
static int pl061_irq_type(struct irq_data *d, unsigned trigger)
{
struct gpio_chip *gc = irq_data_get_irq_chip_data(d);
struct pl061 *pl061 = gpiochip_get_data(gc);
int offset = irqd_to_hwirq(d);
unsigned long flags;
u8 gpiois, gpioibe, gpioiev;
u8 bit = BIT(offset);
if (offset < 0 || offset >= PL061_GPIO_NR)
return -EINVAL;
if ((trigger & (IRQ_TYPE_LEVEL_HIGH | IRQ_TYPE_LEVEL_LOW)) &&
(trigger & (IRQ_TYPE_EDGE_RISING | IRQ_TYPE_EDGE_FALLING)))
{
dev_err(gc->parent,
"trying to configure line %d for both level and edge "
"detection, choose one!\n",
offset);
return -EINVAL;
}
raw_spin_lock_irqsave(&pl061->lock, flags);
gpioiev = readb(pl061->base + GPIOIEV);
gpiois = readb(pl061->base + GPIOIS);
gpioibe = readb(pl061->base + GPIOIBE);
if (trigger & (IRQ_TYPE_LEVEL_HIGH | IRQ_TYPE_LEVEL_LOW)) {
bool polarity = trigger & IRQ_TYPE_LEVEL_HIGH;
/* Disable edge detection */
gpioibe &= ~bit;
/* Enable level detection */
gpiois |= bit;
/* Select polarity */
if (polarity)
gpioiev |= bit;
else
gpioiev &= ~bit;
irq_set_handler_locked(d, handle_level_irq);
dev_dbg(gc->parent, "line %d: IRQ on %s level\n",
offset,
polarity ? "HIGH" : "LOW");
} else if ((trigger & IRQ_TYPE_EDGE_BOTH) == IRQ_TYPE_EDGE_BOTH) {
/* Disable level detection */
gpiois &= ~bit;
/* Select both edges, setting this makes GPIOEV be ignored */
gpioibe |= bit;
irq_set_handler_locked(d, handle_edge_irq);
dev_dbg(gc->parent, "line %d: IRQ on both edges\n", offset);
} else if ((trigger & IRQ_TYPE_EDGE_RISING) ||
(trigger & IRQ_TYPE_EDGE_FALLING)) {
bool rising = trigger & IRQ_TYPE_EDGE_RISING;
/* Disable level detection */
gpiois &= ~bit;
/* Clear detection on both edges */
gpioibe &= ~bit;
/* Select edge */
if (rising)
gpioiev |= bit;
else
gpioiev &= ~bit;
irq_set_handler_locked(d, handle_edge_irq);
dev_dbg(gc->parent, "line %d: IRQ on %s edge\n",
offset,
rising ? "RISING" : "FALLING");
} else {
/* No trigger: disable everything */
gpiois &= ~bit;
gpioibe &= ~bit;
gpioiev &= ~bit;
irq_set_handler_locked(d, handle_bad_irq);
dev_warn(gc->parent, "no trigger selected for line %d\n",
offset);
}
writeb(gpiois, pl061->base + GPIOIS);
writeb(gpioibe, pl061->base + GPIOIBE);
writeb(gpioiev, pl061->base + GPIOIEV);
raw_spin_unlock_irqrestore(&pl061->lock, flags);
return 0;
}
static void pl061_irq_handler(struct irq_desc *desc)
{
unsigned long pending;
int offset;
struct gpio_chip *gc = irq_desc_get_handler_data(desc);
struct pl061 *pl061 = gpiochip_get_data(gc);
struct irq_chip *irqchip = irq_desc_get_chip(desc);
chained_irq_enter(irqchip, desc);
pending = readb(pl061->base + GPIOMIS);
if (pending) {
for_each_set_bit(offset, &pending, PL061_GPIO_NR)
generic_handle_irq(irq_find_mapping(gc->irq.domain,
offset));
}
chained_irq_exit(irqchip, desc);
}
static void pl061_irq_mask(struct irq_data *d)
{
struct gpio_chip *gc = irq_data_get_irq_chip_data(d);
struct pl061 *pl061 = gpiochip_get_data(gc);
u8 mask = BIT(irqd_to_hwirq(d) % PL061_GPIO_NR);
u8 gpioie;
raw_spin_lock(&pl061->lock);
gpioie = readb(pl061->base + GPIOIE) & ~mask;
writeb(gpioie, pl061->base + GPIOIE);
raw_spin_unlock(&pl061->lock);
}
static void pl061_irq_unmask(struct irq_data *d)
{
struct gpio_chip *gc = irq_data_get_irq_chip_data(d);
struct pl061 *pl061 = gpiochip_get_data(gc);
u8 mask = BIT(irqd_to_hwirq(d) % PL061_GPIO_NR);
u8 gpioie;
raw_spin_lock(&pl061->lock);
gpioie = readb(pl061->base + GPIOIE) | mask;
writeb(gpioie, pl061->base + GPIOIE);
raw_spin_unlock(&pl061->lock);
}
/**
* pl061_irq_ack() - ACK an edge IRQ
* @d: IRQ data for this IRQ
*
* This gets called from the edge IRQ handler to ACK the edge IRQ
* in the GPIOIC (interrupt-clear) register. For level IRQs this is
* not needed: these go away when the level signal goes away.
*/
static void pl061_irq_ack(struct irq_data *d)
{
struct gpio_chip *gc = irq_data_get_irq_chip_data(d);
struct pl061 *pl061 = gpiochip_get_data(gc);
u8 mask = BIT(irqd_to_hwirq(d) % PL061_GPIO_NR);
raw_spin_lock(&pl061->lock);
writeb(mask, pl061->base + GPIOIC);
raw_spin_unlock(&pl061->lock);
}
static int pl061_irq_set_wake(struct irq_data *d, unsigned int state)
{
struct gpio_chip *gc = irq_data_get_irq_chip_data(d);
struct pl061 *pl061 = gpiochip_get_data(gc);
return irq_set_irq_wake(pl061->parent_irq, state);
}
static int pl061_probe(struct amba_device *adev, const struct amba_id *id)
{
struct device *dev = &adev->dev;
struct pl061 *pl061;
struct gpio_irq_chip *girq;
int ret, irq;
pl061 = devm_kzalloc(dev, sizeof(*pl061), GFP_KERNEL);
if (pl061 == NULL)
return -ENOMEM;
pl061->base = devm_ioremap_resource(dev, &adev->res);
if (IS_ERR(pl061->base))
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.base = -1;
pl061->gc.get_direction = pl061_get_direction;
pl061->gc.direction_input = pl061_direction_input;
pl061->gc.direction_output = pl061_direction_output;
pl061->gc.get = pl061_get_value;
pl061->gc.set = pl061_set_value;
pl061->gc.ngpio = PL061_GPIO_NR;
pl061->gc.label = dev_name(dev);
pl061->gc.parent = dev;
pl061->gc.owner = THIS_MODULE;
/*
* irq_chip support
*/
pl061->irq_chip.name = dev_name(dev);
pl061->irq_chip.irq_ack = pl061_irq_ack;
pl061->irq_chip.irq_mask = pl061_irq_mask;
pl061->irq_chip.irq_unmask = pl061_irq_unmask;
pl061->irq_chip.irq_set_type = pl061_irq_type;
pl061->irq_chip.irq_set_wake = pl061_irq_set_wake;
writeb(0, pl061->base + GPIOIE); /* disable irqs */
irq = adev->irq[0];
if (irq < 0) {
dev_err(&adev->dev, "invalid IRQ\n");
return -ENODEV;
}
pl061->parent_irq = irq;
girq = &pl061->gc.irq;
girq->chip = &pl061->irq_chip;
girq->parent_handler = pl061_irq_handler;
girq->num_parents = 1;
girq->parents = devm_kcalloc(dev, 1, sizeof(*girq->parents),
GFP_KERNEL);
if (!girq->parents)
return -ENOMEM;
girq->parents[0] = irq;
girq->default_type = IRQ_TYPE_NONE;
girq->handler = handle_bad_irq;
ret = devm_gpiochip_add_data(dev, &pl061->gc, pl061);
if (ret)
return ret;
amba_set_drvdata(adev, pl061);
dev_info(dev, "PL061 GPIO chip registered\n");
return 0;
}
#ifdef CONFIG_PM
static int pl061_suspend(struct device *dev)
{
struct pl061 *pl061 = dev_get_drvdata(dev);
int offset;
pl061->csave_regs.gpio_data = 0;
pl061->csave_regs.gpio_dir = readb(pl061->base + GPIODIR);
pl061->csave_regs.gpio_is = readb(pl061->base + GPIOIS);
pl061->csave_regs.gpio_ibe = readb(pl061->base + GPIOIBE);
pl061->csave_regs.gpio_iev = readb(pl061->base + GPIOIEV);
pl061->csave_regs.gpio_ie = readb(pl061->base + GPIOIE);
for (offset = 0; offset < PL061_GPIO_NR; offset++) {
if (pl061->csave_regs.gpio_dir & (BIT(offset)))
pl061->csave_regs.gpio_data |=
pl061_get_value(&pl061->gc, offset) << offset;
}
return 0;
}
static int pl061_resume(struct device *dev)
{
struct pl061 *pl061 = dev_get_drvdata(dev);
int offset;
for (offset = 0; offset < PL061_GPIO_NR; offset++) {
if (pl061->csave_regs.gpio_dir & (BIT(offset)))
pl061_direction_output(&pl061->gc, offset,
pl061->csave_regs.gpio_data &
(BIT(offset)));
else
pl061_direction_input(&pl061->gc, offset);
}
writeb(pl061->csave_regs.gpio_is, pl061->base + GPIOIS);
writeb(pl061->csave_regs.gpio_ibe, pl061->base + GPIOIBE);
writeb(pl061->csave_regs.gpio_iev, pl061->base + GPIOIEV);
writeb(pl061->csave_regs.gpio_ie, pl061->base + GPIOIE);
return 0;
}
static const struct dev_pm_ops pl061_dev_pm_ops = {
.suspend = pl061_suspend,
.resume = pl061_resume,
.freeze = pl061_suspend,
.restore = pl061_resume,
};
#endif
static const struct amba_id pl061_ids[] = {
{
.id = 0x00041061,
.mask = 0x000fffff,
},
{ 0, 0 },
};
static struct amba_driver pl061_gpio_driver = {
.drv = {
.name = "pl061_gpio",
#ifdef CONFIG_PM
.pm = &pl061_dev_pm_ops,
#endif
},
.id_table = pl061_ids,
.probe = pl061_probe,
};
static int __init pl061_gpio_init(void)
{
return amba_driver_register(&pl061_gpio_driver);
}
device_initcall(pl061_gpio_init);