mirror of
https://mirrors.bfsu.edu.cn/git/linux.git
synced 2024-11-17 17:24:17 +08:00
2ec98f5678
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 ...
317 lines
8.3 KiB
C
317 lines
8.3 KiB
C
// SPDX-License-Identifier: GPL-2.0-only
|
|
/*
|
|
* Atheros AR71XX/AR724X/AR913X GPIO API support
|
|
*
|
|
* Copyright (C) 2015 Alban Bedel <albeu@free.fr>
|
|
* Copyright (C) 2010-2011 Jaiganesh Narayanan <jnarayanan@atheros.com>
|
|
* Copyright (C) 2008-2011 Gabor Juhos <juhosg@openwrt.org>
|
|
* Copyright (C) 2008 Imre Kaloz <kaloz@openwrt.org>
|
|
*/
|
|
|
|
#include <linux/gpio/driver.h>
|
|
#include <linux/platform_data/gpio-ath79.h>
|
|
#include <linux/of_device.h>
|
|
#include <linux/interrupt.h>
|
|
#include <linux/module.h>
|
|
#include <linux/irq.h>
|
|
|
|
#define AR71XX_GPIO_REG_OE 0x00
|
|
#define AR71XX_GPIO_REG_IN 0x04
|
|
#define AR71XX_GPIO_REG_SET 0x0c
|
|
#define AR71XX_GPIO_REG_CLEAR 0x10
|
|
|
|
#define AR71XX_GPIO_REG_INT_ENABLE 0x14
|
|
#define AR71XX_GPIO_REG_INT_TYPE 0x18
|
|
#define AR71XX_GPIO_REG_INT_POLARITY 0x1c
|
|
#define AR71XX_GPIO_REG_INT_PENDING 0x20
|
|
#define AR71XX_GPIO_REG_INT_MASK 0x24
|
|
|
|
struct ath79_gpio_ctrl {
|
|
struct gpio_chip gc;
|
|
void __iomem *base;
|
|
raw_spinlock_t lock;
|
|
unsigned long both_edges;
|
|
};
|
|
|
|
static struct ath79_gpio_ctrl *irq_data_to_ath79_gpio(struct irq_data *data)
|
|
{
|
|
struct gpio_chip *gc = irq_data_get_irq_chip_data(data);
|
|
|
|
return container_of(gc, struct ath79_gpio_ctrl, gc);
|
|
}
|
|
|
|
static u32 ath79_gpio_read(struct ath79_gpio_ctrl *ctrl, unsigned reg)
|
|
{
|
|
return readl(ctrl->base + reg);
|
|
}
|
|
|
|
static void ath79_gpio_write(struct ath79_gpio_ctrl *ctrl,
|
|
unsigned reg, u32 val)
|
|
{
|
|
writel(val, ctrl->base + reg);
|
|
}
|
|
|
|
static bool ath79_gpio_update_bits(
|
|
struct ath79_gpio_ctrl *ctrl, unsigned reg, u32 mask, u32 bits)
|
|
{
|
|
u32 old_val, new_val;
|
|
|
|
old_val = ath79_gpio_read(ctrl, reg);
|
|
new_val = (old_val & ~mask) | (bits & mask);
|
|
|
|
if (new_val != old_val)
|
|
ath79_gpio_write(ctrl, reg, new_val);
|
|
|
|
return new_val != old_val;
|
|
}
|
|
|
|
static void ath79_gpio_irq_unmask(struct irq_data *data)
|
|
{
|
|
struct ath79_gpio_ctrl *ctrl = irq_data_to_ath79_gpio(data);
|
|
u32 mask = BIT(irqd_to_hwirq(data));
|
|
unsigned long flags;
|
|
|
|
raw_spin_lock_irqsave(&ctrl->lock, flags);
|
|
ath79_gpio_update_bits(ctrl, AR71XX_GPIO_REG_INT_MASK, mask, mask);
|
|
raw_spin_unlock_irqrestore(&ctrl->lock, flags);
|
|
}
|
|
|
|
static void ath79_gpio_irq_mask(struct irq_data *data)
|
|
{
|
|
struct ath79_gpio_ctrl *ctrl = irq_data_to_ath79_gpio(data);
|
|
u32 mask = BIT(irqd_to_hwirq(data));
|
|
unsigned long flags;
|
|
|
|
raw_spin_lock_irqsave(&ctrl->lock, flags);
|
|
ath79_gpio_update_bits(ctrl, AR71XX_GPIO_REG_INT_MASK, mask, 0);
|
|
raw_spin_unlock_irqrestore(&ctrl->lock, flags);
|
|
}
|
|
|
|
static void ath79_gpio_irq_enable(struct irq_data *data)
|
|
{
|
|
struct ath79_gpio_ctrl *ctrl = irq_data_to_ath79_gpio(data);
|
|
u32 mask = BIT(irqd_to_hwirq(data));
|
|
unsigned long flags;
|
|
|
|
raw_spin_lock_irqsave(&ctrl->lock, flags);
|
|
ath79_gpio_update_bits(ctrl, AR71XX_GPIO_REG_INT_ENABLE, mask, mask);
|
|
ath79_gpio_update_bits(ctrl, AR71XX_GPIO_REG_INT_MASK, mask, mask);
|
|
raw_spin_unlock_irqrestore(&ctrl->lock, flags);
|
|
}
|
|
|
|
static void ath79_gpio_irq_disable(struct irq_data *data)
|
|
{
|
|
struct ath79_gpio_ctrl *ctrl = irq_data_to_ath79_gpio(data);
|
|
u32 mask = BIT(irqd_to_hwirq(data));
|
|
unsigned long flags;
|
|
|
|
raw_spin_lock_irqsave(&ctrl->lock, flags);
|
|
ath79_gpio_update_bits(ctrl, AR71XX_GPIO_REG_INT_MASK, mask, 0);
|
|
ath79_gpio_update_bits(ctrl, AR71XX_GPIO_REG_INT_ENABLE, mask, 0);
|
|
raw_spin_unlock_irqrestore(&ctrl->lock, flags);
|
|
}
|
|
|
|
static int ath79_gpio_irq_set_type(struct irq_data *data,
|
|
unsigned int flow_type)
|
|
{
|
|
struct ath79_gpio_ctrl *ctrl = irq_data_to_ath79_gpio(data);
|
|
u32 mask = BIT(irqd_to_hwirq(data));
|
|
u32 type = 0, polarity = 0;
|
|
unsigned long flags;
|
|
bool disabled;
|
|
|
|
switch (flow_type) {
|
|
case IRQ_TYPE_EDGE_RISING:
|
|
polarity |= mask;
|
|
case IRQ_TYPE_EDGE_FALLING:
|
|
case IRQ_TYPE_EDGE_BOTH:
|
|
break;
|
|
|
|
case IRQ_TYPE_LEVEL_HIGH:
|
|
polarity |= mask;
|
|
/* fall through */
|
|
case IRQ_TYPE_LEVEL_LOW:
|
|
type |= mask;
|
|
break;
|
|
|
|
default:
|
|
return -EINVAL;
|
|
}
|
|
|
|
raw_spin_lock_irqsave(&ctrl->lock, flags);
|
|
|
|
if (flow_type == IRQ_TYPE_EDGE_BOTH) {
|
|
ctrl->both_edges |= mask;
|
|
polarity = ~ath79_gpio_read(ctrl, AR71XX_GPIO_REG_IN);
|
|
} else {
|
|
ctrl->both_edges &= ~mask;
|
|
}
|
|
|
|
/* As the IRQ configuration can't be loaded atomically we
|
|
* have to disable the interrupt while the configuration state
|
|
* is invalid.
|
|
*/
|
|
disabled = ath79_gpio_update_bits(
|
|
ctrl, AR71XX_GPIO_REG_INT_ENABLE, mask, 0);
|
|
|
|
ath79_gpio_update_bits(
|
|
ctrl, AR71XX_GPIO_REG_INT_TYPE, mask, type);
|
|
ath79_gpio_update_bits(
|
|
ctrl, AR71XX_GPIO_REG_INT_POLARITY, mask, polarity);
|
|
|
|
if (disabled)
|
|
ath79_gpio_update_bits(
|
|
ctrl, AR71XX_GPIO_REG_INT_ENABLE, mask, mask);
|
|
|
|
raw_spin_unlock_irqrestore(&ctrl->lock, flags);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static struct irq_chip ath79_gpio_irqchip = {
|
|
.name = "gpio-ath79",
|
|
.irq_enable = ath79_gpio_irq_enable,
|
|
.irq_disable = ath79_gpio_irq_disable,
|
|
.irq_mask = ath79_gpio_irq_mask,
|
|
.irq_unmask = ath79_gpio_irq_unmask,
|
|
.irq_set_type = ath79_gpio_irq_set_type,
|
|
};
|
|
|
|
static void ath79_gpio_irq_handler(struct irq_desc *desc)
|
|
{
|
|
struct gpio_chip *gc = irq_desc_get_handler_data(desc);
|
|
struct irq_chip *irqchip = irq_desc_get_chip(desc);
|
|
struct ath79_gpio_ctrl *ctrl =
|
|
container_of(gc, struct ath79_gpio_ctrl, gc);
|
|
unsigned long flags, pending;
|
|
u32 both_edges, state;
|
|
int irq;
|
|
|
|
chained_irq_enter(irqchip, desc);
|
|
|
|
raw_spin_lock_irqsave(&ctrl->lock, flags);
|
|
|
|
pending = ath79_gpio_read(ctrl, AR71XX_GPIO_REG_INT_PENDING);
|
|
|
|
/* Update the polarity of the both edges irqs */
|
|
both_edges = ctrl->both_edges & pending;
|
|
if (both_edges) {
|
|
state = ath79_gpio_read(ctrl, AR71XX_GPIO_REG_IN);
|
|
ath79_gpio_update_bits(ctrl, AR71XX_GPIO_REG_INT_POLARITY,
|
|
both_edges, ~state);
|
|
}
|
|
|
|
raw_spin_unlock_irqrestore(&ctrl->lock, flags);
|
|
|
|
if (pending) {
|
|
for_each_set_bit(irq, &pending, gc->ngpio)
|
|
generic_handle_irq(
|
|
irq_linear_revmap(gc->irq.domain, irq));
|
|
}
|
|
|
|
chained_irq_exit(irqchip, desc);
|
|
}
|
|
|
|
static const struct of_device_id ath79_gpio_of_match[] = {
|
|
{ .compatible = "qca,ar7100-gpio" },
|
|
{ .compatible = "qca,ar9340-gpio" },
|
|
{},
|
|
};
|
|
MODULE_DEVICE_TABLE(of, ath79_gpio_of_match);
|
|
|
|
static int ath79_gpio_probe(struct platform_device *pdev)
|
|
{
|
|
struct ath79_gpio_platform_data *pdata = dev_get_platdata(&pdev->dev);
|
|
struct device *dev = &pdev->dev;
|
|
struct device_node *np = dev->of_node;
|
|
struct ath79_gpio_ctrl *ctrl;
|
|
struct gpio_irq_chip *girq;
|
|
struct resource *res;
|
|
u32 ath79_gpio_count;
|
|
bool oe_inverted;
|
|
int err;
|
|
|
|
ctrl = devm_kzalloc(dev, sizeof(*ctrl), GFP_KERNEL);
|
|
if (!ctrl)
|
|
return -ENOMEM;
|
|
platform_set_drvdata(pdev, ctrl);
|
|
|
|
if (np) {
|
|
err = of_property_read_u32(np, "ngpios", &ath79_gpio_count);
|
|
if (err) {
|
|
dev_err(dev, "ngpios property is not valid\n");
|
|
return err;
|
|
}
|
|
oe_inverted = of_device_is_compatible(np, "qca,ar9340-gpio");
|
|
} else if (pdata) {
|
|
ath79_gpio_count = pdata->ngpios;
|
|
oe_inverted = pdata->oe_inverted;
|
|
} else {
|
|
dev_err(dev, "No DT node or platform data found\n");
|
|
return -EINVAL;
|
|
}
|
|
|
|
if (ath79_gpio_count >= 32) {
|
|
dev_err(dev, "ngpios must be less than 32\n");
|
|
return -EINVAL;
|
|
}
|
|
|
|
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
|
if (!res)
|
|
return -EINVAL;
|
|
ctrl->base = devm_ioremap_nocache(dev, res->start, resource_size(res));
|
|
if (!ctrl->base)
|
|
return -ENOMEM;
|
|
|
|
raw_spin_lock_init(&ctrl->lock);
|
|
err = bgpio_init(&ctrl->gc, dev, 4,
|
|
ctrl->base + AR71XX_GPIO_REG_IN,
|
|
ctrl->base + AR71XX_GPIO_REG_SET,
|
|
ctrl->base + AR71XX_GPIO_REG_CLEAR,
|
|
oe_inverted ? NULL : ctrl->base + AR71XX_GPIO_REG_OE,
|
|
oe_inverted ? ctrl->base + AR71XX_GPIO_REG_OE : NULL,
|
|
0);
|
|
if (err) {
|
|
dev_err(dev, "bgpio_init failed\n");
|
|
return err;
|
|
}
|
|
/* Use base 0 to stay compatible with legacy platforms */
|
|
ctrl->gc.base = 0;
|
|
|
|
/* Optional interrupt setup */
|
|
if (!np || of_property_read_bool(np, "interrupt-controller")) {
|
|
girq = &ctrl->gc.irq;
|
|
girq->chip = &ath79_gpio_irqchip;
|
|
girq->parent_handler = ath79_gpio_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] = platform_get_irq(pdev, 0);
|
|
girq->default_type = IRQ_TYPE_NONE;
|
|
girq->handler = handle_simple_irq;
|
|
}
|
|
|
|
err = devm_gpiochip_add_data(dev, &ctrl->gc, ctrl);
|
|
if (err) {
|
|
dev_err(dev,
|
|
"cannot add AR71xx GPIO chip, error=%d", err);
|
|
return err;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static struct platform_driver ath79_gpio_driver = {
|
|
.driver = {
|
|
.name = "ath79-gpio",
|
|
.of_match_table = ath79_gpio_of_match,
|
|
},
|
|
.probe = ath79_gpio_probe,
|
|
};
|
|
|
|
module_platform_driver(ath79_gpio_driver);
|
|
|
|
MODULE_DESCRIPTION("Atheros AR71XX/AR724X/AR913X GPIO API support");
|
|
MODULE_LICENSE("GPL v2");
|