diff options
| author | Sang-Heon Jeon <ekffu200098@gmail.com> | 2026-08-24 01:18:14 +0900 |
|---|---|---|
| committer | Julia Lawall <Julia.Lawall@inria.fr> | 2026-08-23 22:12:11 +0200 |
| commit | 2bd30b8dc75b823dd7bbef9d7bc68e1fd7ecee5c (patch) | |
| tree | dcc4da1d99f17541c5590ac63c77da4bf372a4ab /drivers/gpio/gpio-timberdale.c | |
| download | linux-stable-2bd30b8dc75b823dd7bbef9d7bc68e1fd7ecee5c.tar.gz linux-stable-2bd30b8dc75b823dd7bbef9d7bc68e1fd7ecee5c.zip | |
coccinelle: alloc_cast: drop removed allocatorsgrafted
- pci_alloc_consistent() and pci_zalloc_consistent() were removed by
commit 7968778914e5
("PCI: Remove the deprecated "pci-dma-compat.h" API")
- kmem_alloc() was removed by commit f078d4ea8276
("xfs: convert kmem_alloc() to kmalloc()")
- kmem_zalloc() was removed by commit 10634530f7ba
("xfs: convert kmem_zalloc() to kzalloc()")
- kmem_zone_alloc() and kmem_zone_zalloc() were removed by commit
bae633a4a283 ("xfs: remove xfs_zone_{alloc,zalloc} helpers")
So drop them from the rules.
No functional change.
Signed-off-by: Sang-Heon Jeon <ekffu200098@gmail.com>
Signed-off-by: Julia Lawall <Julia.Lawall@inria.fr>
Diffstat (limited to 'drivers/gpio/gpio-timberdale.c')
| -rw-r--r-- | drivers/gpio/gpio-timberdale.c | 295 |
1 files changed, 295 insertions, 0 deletions
diff --git a/drivers/gpio/gpio-timberdale.c b/drivers/gpio/gpio-timberdale.c new file mode 100644 index 000000000..727fa1b5a --- /dev/null +++ b/drivers/gpio/gpio-timberdale.c @@ -0,0 +1,295 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Timberdale FPGA GPIO driver + * Author: Mocean Laboratories + * Copyright (c) 2009 Intel Corporation + */ + +/* Supports: + * Timberdale FPGA GPIO + */ + +#include <linux/init.h> +#include <linux/gpio/driver.h> +#include <linux/platform_device.h> +#include <linux/irq.h> +#include <linux/io.h> +#include <linux/interrupt.h> +#include <linux/slab.h> + +#define DRIVER_NAME "timb-gpio" + +#define TGPIOVAL 0x00 +#define TGPIODIR 0x04 +#define TGPIO_IER 0x08 +#define TGPIO_ISR 0x0c +#define TGPIO_IPR 0x10 +#define TGPIO_ICR 0x14 +#define TGPIO_FLR 0x18 +#define TGPIO_LVR 0x1c +#define TGPIO_VER 0x20 +#define TGPIO_BFLR 0x24 + +struct timbgpio { + void __iomem *membase; + spinlock_t lock; /* mutual exclusion */ + struct gpio_chip gpio; + int irq_base; + unsigned long last_ier; +}; + +static int timbgpio_update_bit(struct gpio_chip *gpio, unsigned index, + unsigned offset, bool enabled) +{ + struct timbgpio *tgpio = gpiochip_get_data(gpio); + unsigned long flags; + u32 reg; + + spin_lock_irqsave(&tgpio->lock, flags); + reg = ioread32(tgpio->membase + offset); + + if (enabled) + reg |= (1 << index); + else + reg &= ~(1 << index); + + iowrite32(reg, tgpio->membase + offset); + spin_unlock_irqrestore(&tgpio->lock, flags); + + return 0; +} + +static int timbgpio_gpio_direction_input(struct gpio_chip *gpio, unsigned nr) +{ + return timbgpio_update_bit(gpio, nr, TGPIODIR, true); +} + +static int timbgpio_gpio_get(struct gpio_chip *gpio, unsigned nr) +{ + struct timbgpio *tgpio = gpiochip_get_data(gpio); + u32 value; + + value = ioread32(tgpio->membase + TGPIOVAL); + return (value & (1 << nr)) ? 1 : 0; +} + +static int timbgpio_gpio_direction_output(struct gpio_chip *gpio, + unsigned nr, int val) +{ + return timbgpio_update_bit(gpio, nr, TGPIODIR, false); +} + +static int timbgpio_gpio_set(struct gpio_chip *gpio, unsigned int nr, int val) +{ + return timbgpio_update_bit(gpio, nr, TGPIOVAL, val != 0); +} + +static int timbgpio_to_irq(struct gpio_chip *gpio, unsigned offset) +{ + struct timbgpio *tgpio = gpiochip_get_data(gpio); + + if (tgpio->irq_base <= 0) + return -EINVAL; + + return tgpio->irq_base + offset; +} + +/* + * GPIO IRQ + */ +static void timbgpio_irq_disable(struct irq_data *d) +{ + struct timbgpio *tgpio = irq_data_get_irq_chip_data(d); + int offset = d->irq - tgpio->irq_base; + irq_hw_number_t hwirq = irqd_to_hwirq(d); + unsigned long flags; + + spin_lock_irqsave(&tgpio->lock, flags); + tgpio->last_ier &= ~(1UL << offset); + iowrite32(tgpio->last_ier, tgpio->membase + TGPIO_IER); + spin_unlock_irqrestore(&tgpio->lock, flags); + + gpiochip_disable_irq(&tgpio->gpio, hwirq); +} + +static void timbgpio_irq_enable(struct irq_data *d) +{ + struct timbgpio *tgpio = irq_data_get_irq_chip_data(d); + int offset = d->irq - tgpio->irq_base; + irq_hw_number_t hwirq = irqd_to_hwirq(d); + unsigned long flags; + + gpiochip_enable_irq(&tgpio->gpio, hwirq); + + spin_lock_irqsave(&tgpio->lock, flags); + tgpio->last_ier |= 1UL << offset; + iowrite32(tgpio->last_ier, tgpio->membase + TGPIO_IER); + spin_unlock_irqrestore(&tgpio->lock, flags); +} + +static int timbgpio_irq_type(struct irq_data *d, unsigned trigger) +{ + struct timbgpio *tgpio = irq_data_get_irq_chip_data(d); + int offset = d->irq - tgpio->irq_base; + unsigned long flags; + u32 lvr, flr, bflr = 0; + u32 ver; + int ret = 0; + + if (offset < 0 || offset >= tgpio->gpio.ngpio) + return -EINVAL; + + ver = ioread32(tgpio->membase + TGPIO_VER); + + spin_lock_irqsave(&tgpio->lock, flags); + + lvr = ioread32(tgpio->membase + TGPIO_LVR); + flr = ioread32(tgpio->membase + TGPIO_FLR); + if (ver > 2) + bflr = ioread32(tgpio->membase + TGPIO_BFLR); + + if (trigger & IRQ_TYPE_LEVEL_MASK) { + bflr &= ~(1 << offset); + flr &= ~(1 << offset); + if (trigger & IRQ_TYPE_LEVEL_HIGH) + lvr |= 1 << offset; + else + lvr &= ~(1 << offset); + } + + if ((trigger & IRQ_TYPE_EDGE_BOTH) == IRQ_TYPE_EDGE_BOTH) { + if (ver < 3) { + ret = -EINVAL; + goto out; + } else { + flr |= 1 << offset; + bflr |= 1 << offset; + } + } else { + bflr &= ~(1 << offset); + flr |= 1 << offset; + if (trigger & IRQ_TYPE_EDGE_FALLING) + lvr &= ~(1 << offset); + else + lvr |= 1 << offset; + } + + iowrite32(lvr, tgpio->membase + TGPIO_LVR); + iowrite32(flr, tgpio->membase + TGPIO_FLR); + if (ver > 2) + iowrite32(bflr, tgpio->membase + TGPIO_BFLR); + + iowrite32(1 << offset, tgpio->membase + TGPIO_ICR); + +out: + spin_unlock_irqrestore(&tgpio->lock, flags); + return ret; +} + +static void timbgpio_irq(struct irq_desc *desc) +{ + struct timbgpio *tgpio = irq_desc_get_handler_data(desc); + struct irq_data *data = irq_desc_get_irq_data(desc); + unsigned long ipr; + int offset; + + data->chip->irq_ack(data); + ipr = ioread32(tgpio->membase + TGPIO_IPR); + iowrite32(ipr, tgpio->membase + TGPIO_ICR); + + /* + * Some versions of the hardware trash the IER register if more than + * one interrupt is received simultaneously. + */ + iowrite32(0, tgpio->membase + TGPIO_IER); + + for_each_set_bit(offset, &ipr, tgpio->gpio.ngpio) + generic_handle_irq(timbgpio_to_irq(&tgpio->gpio, offset)); + + iowrite32(tgpio->last_ier, tgpio->membase + TGPIO_IER); +} + +static const struct irq_chip timbgpio_irqchip = { + .name = "GPIO", + .irq_enable = timbgpio_irq_enable, + .irq_disable = timbgpio_irq_disable, + .irq_set_type = timbgpio_irq_type, + .flags = IRQCHIP_IMMUTABLE, + GPIOCHIP_IRQ_RESOURCE_HELPERS, +}; + +static int timbgpio_probe(struct platform_device *pdev) +{ + int err, i; + struct device *dev = &pdev->dev; + struct gpio_chip *gc; + struct timbgpio *tgpio; + int irq = platform_get_irq(pdev, 0); + + tgpio = devm_kzalloc(dev, sizeof(*tgpio), GFP_KERNEL); + if (!tgpio) + return -ENOMEM; + + gc = &tgpio->gpio; + + err = device_property_read_u32(dev, "irq-base", &tgpio->irq_base); + if (err) + return err; + + err = device_property_read_u32(dev, "gpio-base", &gc->base); + if (err) + return err; + + spin_lock_init(&tgpio->lock); + + tgpio->membase = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(tgpio->membase)) + return PTR_ERR(tgpio->membase); + + gc->label = dev_name(&pdev->dev); + gc->owner = THIS_MODULE; + gc->parent = &pdev->dev; + gc->direction_input = timbgpio_gpio_direction_input; + gc->get = timbgpio_gpio_get; + gc->direction_output = timbgpio_gpio_direction_output; + gc->set = timbgpio_gpio_set; + gc->to_irq = (irq >= 0 && tgpio->irq_base > 0) ? timbgpio_to_irq : NULL; + gc->dbg_show = NULL; + gc->can_sleep = false; + + err = devm_gpiochip_add_data(&pdev->dev, gc, tgpio); + if (err) + return err; + + if (gc->ngpio > 32) + return dev_err_probe(dev, -EINVAL, "Invalid number of pins\n"); + + /* make sure to disable interrupts */ + iowrite32(0x0, tgpio->membase + TGPIO_IER); + + if (irq < 0 || tgpio->irq_base <= 0) + return 0; + + for (i = 0; i < gc->ngpio; i++) { + irq_set_chip_and_handler(tgpio->irq_base + i, + &timbgpio_irqchip, handle_simple_irq); + irq_set_chip_data(tgpio->irq_base + i, tgpio); + irq_clear_status_flags(tgpio->irq_base + i, IRQ_NOREQUEST | IRQ_NOPROBE); + } + + irq_set_chained_handler_and_data(irq, timbgpio_irq, tgpio); + + return 0; +} + +static struct platform_driver timbgpio_platform_driver = { + .driver = { + .name = DRIVER_NAME, + .suppress_bind_attrs = true, + }, + .probe = timbgpio_probe, +}; + +/*--------------------------------------------------------------------------*/ + +builtin_platform_driver(timbgpio_platform_driver); |
