On Fri, Jun 29, 2018 at 2:43 PM, Sergio Paracuellos <sergio.paracuellos@xxxxxxxxx> wrote: > Add driver support for gpio of MT7621 SoC. > > Signed-off-by: Sergio Paracuellos <sergio.paracuellos@xxxxxxxxx> > --- > drivers/gpio/Kconfig | 8 ++ > drivers/gpio/Makefile | 1 + > drivers/gpio/gpio-mt7621.c | 320 +++++++++++++++++++++++++++++++++++++++++++++ > 3 files changed, 329 insertions(+) > create mode 100644 drivers/gpio/gpio-mt7621.c Hi all, Sorry for the noise. Ignore this and v3 for now. It seems the "only one node" approach for the gpio in the device tree is not working properly. I thought it was but Neil points out here it was not: http://driverdev.linuxdriverproject.org/pipermail/driverdev-devel/2018-June/122621.html We'll send v4 with all properly working. Thanks in advance and sorry again. Best regards, Sergio Paracuellos > > diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig > index 71c0ab4..836aa21 100644 > --- a/drivers/gpio/Kconfig > +++ b/drivers/gpio/Kconfig > @@ -359,6 +359,14 @@ config GPIO_MPC8XXX > Say Y here if you're going to use hardware that connects to the > MPC512x/831x/834x/837x/8572/8610/QorIQ GPIOs. > > +config GPIO_MT7621 > + bool "Mediatek MT7621 GPIO Support" > + depends on SOC_MT7620 || SOC_MT7621 || COMPILE_TEST > + select GPIO_GENERIC > + select GPIOLIB_IRQCHIP > + help > + Say yes here to support the Mediatek MT7621 SoC GPIO device > + > config GPIO_MVEBU > def_bool y > depends on PLAT_ORION || ARCH_MVEBU > diff --git a/drivers/gpio/Makefile b/drivers/gpio/Makefile > index 1324c8f..b264426 100644 > --- a/drivers/gpio/Makefile > +++ b/drivers/gpio/Makefile > @@ -88,6 +88,7 @@ obj-$(CONFIG_GPIO_MOCKUP) += gpio-mockup.o > obj-$(CONFIG_GPIO_MPC5200) += gpio-mpc5200.o > obj-$(CONFIG_GPIO_MPC8XXX) += gpio-mpc8xxx.o > obj-$(CONFIG_GPIO_MSIC) += gpio-msic.o > +obj-$(CONFIG_GPIO_MSIC) += gpio-mt7621.o > obj-$(CONFIG_GPIO_MVEBU) += gpio-mvebu.o > obj-$(CONFIG_GPIO_MXC) += gpio-mxc.o > obj-$(CONFIG_GPIO_MXS) += gpio-mxs.o > diff --git a/drivers/gpio/gpio-mt7621.c b/drivers/gpio/gpio-mt7621.c > new file mode 100644 > index 0000000..281e621 > --- /dev/null > +++ b/drivers/gpio/gpio-mt7621.c > @@ -0,0 +1,320 @@ > +// SPDX-License-Identifier: GPL-2.0 > +/* > + * Copyright (C) 2009-2011 Gabor Juhos <juhosg@xxxxxxxxxxx> > + * Copyright (C) 2013 John Crispin <blogic@xxxxxxxxxxx> > + */ > + > +#include <linux/err.h> > +#include <linux/gpio/driver.h> > +#include <linux/interrupt.h> > +#include <linux/io.h> > +#include <linux/module.h> > +#include <linux/of_irq.h> > +#include <linux/platform_device.h> > +#include <linux/spinlock.h> > + > +#define MTK_BANK_CNT 3 > +#define MTK_BANK_WIDTH 32 > + > +#define GPIO_BANK_WIDE 0x04 > +#define GPIO_REG_CTRL 0x00 > +#define GPIO_REG_POL 0x10 > +#define GPIO_REG_DATA 0x20 > +#define GPIO_REG_DSET 0x30 > +#define GPIO_REG_DCLR 0x40 > +#define GPIO_REG_REDGE 0x50 > +#define GPIO_REG_FEDGE 0x60 > +#define GPIO_REG_HLVL 0x70 > +#define GPIO_REG_LLVL 0x80 > +#define GPIO_REG_STAT 0x90 > +#define GPIO_REG_EDGE 0xA0 > + > +struct mtk_gc { > + struct gpio_chip chip; > + spinlock_t lock; > + int bank; > + u32 rising; > + u32 falling; > + u32 hlevel; > + u32 llevel; > +}; > + > +/** > + * struct mtk_data - state container for > + * data of the platform driver. It is 3 > + * separate gpio-chip each one with its > + * own irq_chip. > + * @dev: device instance > + * @gpio_membase: memory base address > + * @gpio_irq: irq number from the device tree > + * @gc_map: array of the gpio chips > + */ > +struct mtk_data { > + struct device *dev; > + void __iomem *gpio_membase; > + int gpio_irq; > + struct mtk_gc gc_map[MTK_BANK_CNT]; > +}; > + > +static inline struct mtk_gc * > +to_mediatek_gpio(struct gpio_chip *chip) > +{ > + return container_of(chip, struct mtk_gc, chip); > +} > + > +static inline void > +mtk_gpio_w32(struct mtk_gc *rg, u32 offset, u32 val) > +{ > + struct gpio_chip *gc = &rg->chip; > + struct mtk_data *gpio_data = gpiochip_get_data(gc); > + > + offset = (rg->bank * GPIO_BANK_WIDE) + offset; > + gc->write_reg(gpio_data->gpio_membase + offset, val); > +} > + > +static inline u32 > +mtk_gpio_r32(struct mtk_gc *rg, u32 offset) > +{ > + struct gpio_chip *gc = &rg->chip; > + struct mtk_data *gpio_data = gpiochip_get_data(gc); > + > + offset = (rg->bank * GPIO_BANK_WIDE) + offset; > + return gc->read_reg(gpio_data->gpio_membase + offset); > +} > + > +static irqreturn_t > +mediatek_gpio_irq_handler(int irq, void *data) > +{ > + struct gpio_chip *gc = data; > + struct mtk_gc *rg = to_mediatek_gpio(gc); > + irqreturn_t ret = IRQ_NONE; > + unsigned long pending; > + int bit; > + > + pending = mtk_gpio_r32(rg, GPIO_REG_STAT); > + > + for_each_set_bit(bit, &pending, MTK_BANK_WIDTH) { > + u32 map = irq_find_mapping(gc->irq.domain, bit); > + > + generic_handle_irq(map); > + mtk_gpio_w32(rg, GPIO_REG_STAT, BIT(bit)); > + ret |= IRQ_HANDLED; > + } > + > + return ret; > +} > + > +static void > +mediatek_gpio_irq_unmask(struct irq_data *d) > +{ > + struct gpio_chip *gc = irq_data_get_irq_chip_data(d); > + struct mtk_gc *rg = to_mediatek_gpio(gc); > + int pin = d->hwirq; > + unsigned long flags; > + u32 rise, fall, high, low; > + > + spin_lock_irqsave(&rg->lock, flags); > + rise = mtk_gpio_r32(rg, GPIO_REG_REDGE); > + fall = mtk_gpio_r32(rg, GPIO_REG_FEDGE); > + high = mtk_gpio_r32(rg, GPIO_REG_HLVL); > + low = mtk_gpio_r32(rg, GPIO_REG_LLVL); > + mtk_gpio_w32(rg, GPIO_REG_REDGE, rise | (BIT(pin) & rg->rising)); > + mtk_gpio_w32(rg, GPIO_REG_FEDGE, fall | (BIT(pin) & rg->falling)); > + mtk_gpio_w32(rg, GPIO_REG_HLVL, high | (BIT(pin) & rg->hlevel)); > + mtk_gpio_w32(rg, GPIO_REG_LLVL, low | (BIT(pin) & rg->llevel)); > + spin_unlock_irqrestore(&rg->lock, flags); > +} > + > +static void > +mediatek_gpio_irq_mask(struct irq_data *d) > +{ > + struct gpio_chip *gc = irq_data_get_irq_chip_data(d); > + struct mtk_gc *rg = to_mediatek_gpio(gc); > + int pin = d->hwirq; > + unsigned long flags; > + u32 rise, fall, high, low; > + > + spin_lock_irqsave(&rg->lock, flags); > + rise = mtk_gpio_r32(rg, GPIO_REG_REDGE); > + fall = mtk_gpio_r32(rg, GPIO_REG_FEDGE); > + high = mtk_gpio_r32(rg, GPIO_REG_HLVL); > + low = mtk_gpio_r32(rg, GPIO_REG_LLVL); > + mtk_gpio_w32(rg, GPIO_REG_FEDGE, fall & ~BIT(pin)); > + mtk_gpio_w32(rg, GPIO_REG_REDGE, rise & ~BIT(pin)); > + mtk_gpio_w32(rg, GPIO_REG_HLVL, high & ~BIT(pin)); > + mtk_gpio_w32(rg, GPIO_REG_LLVL, low & ~BIT(pin)); > + spin_unlock_irqrestore(&rg->lock, flags); > +} > + > +static int > +mediatek_gpio_irq_type(struct irq_data *d, unsigned int type) > +{ > + struct gpio_chip *gc = irq_data_get_irq_chip_data(d); > + struct mtk_gc *rg = to_mediatek_gpio(gc); > + int pin = d->hwirq; > + u32 mask = BIT(pin); > + > + if (type == IRQ_TYPE_PROBE) { > + if ((rg->rising | rg->falling | > + rg->hlevel | rg->llevel) & mask) > + return 0; > + > + type = IRQ_TYPE_EDGE_RISING | IRQ_TYPE_EDGE_FALLING; > + } > + > + rg->rising &= ~mask; > + rg->falling &= ~mask; > + rg->hlevel &= ~mask; > + rg->llevel &= ~mask; > + > + switch (type & IRQ_TYPE_SENSE_MASK) { > + case IRQ_TYPE_EDGE_BOTH: > + rg->rising |= mask; > + rg->falling |= mask; > + break; > + case IRQ_TYPE_EDGE_RISING: > + rg->rising |= mask; > + break; > + case IRQ_TYPE_EDGE_FALLING: > + rg->falling |= mask; > + break; > + case IRQ_TYPE_LEVEL_HIGH: > + rg->hlevel |= mask; > + break; > + case IRQ_TYPE_LEVEL_LOW: > + rg->llevel |= mask; > + break; > + } > + > + return 0; > +} > + > +static struct irq_chip mediatek_gpio_irq_chip = { > + .irq_unmask = mediatek_gpio_irq_unmask, > + .irq_mask = mediatek_gpio_irq_mask, > + .irq_mask_ack = mediatek_gpio_irq_mask, > + .irq_set_type = mediatek_gpio_irq_type, > +}; > + > +static inline const char * const mediatek_gpio_bank_name(int bank) > +{ > + static const char * const bank_names[] = { > + "mt7621-bank0", "mt7621-bank1", "mt7621-bank2", > + }; > + > + return bank_names[bank]; > +} > + > +static int > +mediatek_gpio_bank_probe(struct platform_device *pdev, > + struct device_node *node, int bank) > +{ > + struct mtk_data *gpio = dev_get_drvdata(&pdev->dev); > + struct mtk_gc *rg; > + void __iomem *dat, *set, *ctrl, *diro; > + int ret; > + > + rg = &gpio->gc_map[bank]; > + memset(rg, 0, sizeof(*rg)); > + > + spin_lock_init(&rg->lock); > + rg->chip.of_node = node; > + rg->bank = bank; > + rg->chip.label = mediatek_gpio_bank_name(rg->bank); > + > + dat = gpio->gpio_membase + GPIO_REG_DATA + (rg->bank * GPIO_BANK_WIDE); > + set = gpio->gpio_membase + GPIO_REG_DSET + (rg->bank * GPIO_BANK_WIDE); > + ctrl = gpio->gpio_membase + GPIO_REG_DCLR + (rg->bank * GPIO_BANK_WIDE); > + diro = gpio->gpio_membase + GPIO_REG_CTRL + (rg->bank * GPIO_BANK_WIDE); > + > + ret = bgpio_init(&rg->chip, &pdev->dev, 4, > + dat, set, ctrl, diro, NULL, 0); > + if (ret) { > + dev_err(&pdev->dev, "bgpio_init() failed\n"); > + return ret; > + } > + > + ret = devm_gpiochip_add_data(&pdev->dev, &rg->chip, gpio); > + if (ret < 0) { > + dev_err(&pdev->dev, "Could not register gpio %d, ret=%d\n", > + rg->chip.ngpio, ret); > + return ret; > + } > + > + if (gpio->gpio_irq) { > + /* > + * Manually request the irq here instead of passing > + * a flow-handler to gpiochip_set_chained_irqchip, > + * because the irq is shared. > + */ > + ret = devm_request_irq(&pdev->dev, gpio->gpio_irq, > + mediatek_gpio_irq_handler, IRQF_SHARED, > + rg->chip.label, &rg->chip); > + > + if (ret) { > + dev_err(&pdev->dev, "Error requesting IRQ %d: %d\n", > + gpio->gpio_irq, ret); > + return ret; > + } > + > + mediatek_gpio_irq_chip.name = rg->chip.label; > + ret = gpiochip_irqchip_add(&rg->chip, &mediatek_gpio_irq_chip, > + 0, handle_simple_irq, IRQ_TYPE_NONE); > + if (ret) { > + dev_err(&pdev->dev, "failed to add gpiochip_irqchip\n"); > + return ret; > + } > + > + gpiochip_set_chained_irqchip(&rg->chip, &mediatek_gpio_irq_chip, > + gpio->gpio_irq, NULL); > + } > + > + /* set polarity to low for all gpios */ > + mtk_gpio_w32(rg, GPIO_REG_POL, 0); > + > + dev_info(&pdev->dev, "registering %d gpios\n", rg->chip.ngpio); > + > + return 0; > +} > + > +static int > +mediatek_gpio_probe(struct platform_device *pdev) > +{ > + struct device_node *np = pdev->dev.of_node; > + struct resource *res = platform_get_resource(pdev, IORESOURCE_MEM, 0); > + struct mtk_data *gpio_data; > + int i; > + > + gpio_data = devm_kzalloc(&pdev->dev, sizeof(*gpio_data), GFP_KERNEL); > + if (!gpio_data) > + return -ENOMEM; > + > + gpio_data->gpio_membase = devm_ioremap_resource(&pdev->dev, res); > + if (IS_ERR(gpio_data->gpio_membase)) > + return PTR_ERR(gpio_data->gpio_membase); > + > + gpio_data->gpio_irq = irq_of_parse_and_map(np, 0); > + gpio_data->dev = &pdev->dev; > + platform_set_drvdata(pdev, gpio_data); > + > + for (i = 0; i < MTK_BANK_CNT; i++) > + mediatek_gpio_bank_probe(pdev, np, i); > + > + return 0; > +} > + > +static const struct of_device_id mediatek_gpio_match[] = { > + { .compatible = "mediatek,mt7621-gpio" }, > + {}, > +}; > +MODULE_DEVICE_TABLE(of, mediatek_gpio_match); > + > +static struct platform_driver mediatek_gpio_driver = { > + .probe = mediatek_gpio_probe, > + .driver = { > + .name = "mt7621_gpio", > + .of_match_table = mediatek_gpio_match, > + }, > +}; > + > +builtin_platform_driver(mediatek_gpio_driver); > -- > 2.7.4 > -- To unsubscribe from this list: send the line "unsubscribe linux-gpio" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html