From 4ba9c3afda41213ec98c30053e32963892e6dc7c Mon Sep 17 00:00:00 2001 From: Sergio Paracuellos Date: Thu, 5 Jul 2018 15:43:10 +0200 Subject: gpio: mt7621: Add a driver for MT7621 Add driver support for gpio of MT7621 SoC. Signed-off-by: Sergio Paracuellos Reviewed-by: NeilBrown [Switched wording WIDTH to STRIDE] Signed-off-by: Linus Walleij --- drivers/gpio/gpio-mt7621.c | 335 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 335 insertions(+) create mode 100644 drivers/gpio/gpio-mt7621.c (limited to 'drivers/gpio/gpio-mt7621.c') diff --git a/drivers/gpio/gpio-mt7621.c b/drivers/gpio/gpio-mt7621.c new file mode 100644 index 000000000000..14660d38dd1a --- /dev/null +++ b/drivers/gpio/gpio-mt7621.c @@ -0,0 +1,335 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2009-2011 Gabor Juhos + * Copyright (C) 2013 John Crispin + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#define MTK_BANK_CNT 3 +#define MTK_BANK_WIDTH 32 + +#define GPIO_BANK_STRIDE 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_STRIDE) + 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_STRIDE) + 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 int +mediatek_gpio_xlate(struct gpio_chip *chip, + const struct of_phandle_args *spec, u32 *flags) +{ + int gpio = spec->args[0]; + struct mtk_gc *rg = to_mediatek_gpio(chip); + + if (rg->bank != gpio / MTK_BANK_WIDTH) + return -EINVAL; + + if (flags) + *flags = spec->args[1]; + + return gpio % MTK_BANK_WIDTH; +} + +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; + + dat = gpio->gpio_membase + GPIO_REG_DATA + + (rg->bank * GPIO_BANK_STRIDE); + set = gpio->gpio_membase + GPIO_REG_DSET + + (rg->bank * GPIO_BANK_STRIDE); + ctrl = gpio->gpio_membase + GPIO_REG_DCLR + + (rg->bank * GPIO_BANK_STRIDE); + diro = gpio->gpio_membase + GPIO_REG_CTRL + + (rg->bank * GPIO_BANK_STRIDE); + + 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; + } + + rg->chip.of_gpio_n_cells = 2; + rg->chip.of_xlate = mediatek_gpio_xlate; + rg->chip.label = devm_kasprintf(&pdev->dev, GFP_KERNEL, "%s-bank%d", + dev_name(&pdev->dev), bank); + + 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; + } + + 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); + mediatek_gpio_irq_chip.name = dev_name(&pdev->dev); + + 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); -- cgit v1.2.3 From 8512486ba2d441187d6d619006f034704a3e5a31 Mon Sep 17 00:00:00 2001 From: Linus Walleij Date: Mon, 9 Jul 2018 13:51:57 +0200 Subject: gpio: mt7621: Edit to preferred syntax This fixes some syntactic nits that makes the GPIO maintainer happier. It is way easier to show by example and do it myself than to try to explain it with comments. It's just my personal taste of minimalism. Cc: Sergio Paracuellos Cc: NeilBrown Signed-off-by: Linus Walleij --- drivers/gpio/gpio-mt7621.c | 85 ++++++++++++++++++++++------------------------ 1 file changed, 41 insertions(+), 44 deletions(-) (limited to 'drivers/gpio/gpio-mt7621.c') diff --git a/drivers/gpio/gpio-mt7621.c b/drivers/gpio/gpio-mt7621.c index 14660d38dd1a..d72af6f6cdbd 100644 --- a/drivers/gpio/gpio-mt7621.c +++ b/drivers/gpio/gpio-mt7621.c @@ -40,18 +40,18 @@ struct mtk_gc { }; /** - * struct mtk_data - state container for + * struct mtk - 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 + * @base: memory base address * @gpio_irq: irq number from the device tree * @gc_map: array of the gpio chips */ -struct mtk_data { +struct mtk { struct device *dev; - void __iomem *gpio_membase; + void __iomem *base; int gpio_irq; struct mtk_gc gc_map[MTK_BANK_CNT]; }; @@ -66,20 +66,20 @@ 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); + struct mtk *mtk = gpiochip_get_data(gc); offset = (rg->bank * GPIO_BANK_STRIDE) + offset; - gc->write_reg(gpio_data->gpio_membase + offset, val); + gc->write_reg(mtk->base + 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); + struct mtk *mtk = gpiochip_get_data(gc); offset = (rg->bank * GPIO_BANK_STRIDE) + offset; - return gc->read_reg(gpio_data->gpio_membase + offset); + return gc->read_reg(mtk->base + offset); } static irqreturn_t @@ -213,80 +213,76 @@ mediatek_gpio_xlate(struct gpio_chip *chip, } static int -mediatek_gpio_bank_probe(struct platform_device *pdev, +mediatek_gpio_bank_probe(struct device *dev, struct device_node *node, int bank) { - struct mtk_data *gpio = dev_get_drvdata(&pdev->dev); + struct mtk *mtk = dev_get_drvdata(dev); struct mtk_gc *rg; void __iomem *dat, *set, *ctrl, *diro; int ret; - rg = &gpio->gc_map[bank]; + rg = &mtk->gc_map[bank]; memset(rg, 0, sizeof(*rg)); spin_lock_init(&rg->lock); rg->chip.of_node = node; rg->bank = bank; - dat = gpio->gpio_membase + GPIO_REG_DATA + - (rg->bank * GPIO_BANK_STRIDE); - set = gpio->gpio_membase + GPIO_REG_DSET + - (rg->bank * GPIO_BANK_STRIDE); - ctrl = gpio->gpio_membase + GPIO_REG_DCLR + - (rg->bank * GPIO_BANK_STRIDE); - diro = gpio->gpio_membase + GPIO_REG_CTRL + - (rg->bank * GPIO_BANK_STRIDE); + dat = mtk->base + GPIO_REG_DATA + (rg->bank * GPIO_BANK_STRIDE); + set = mtk->base + GPIO_REG_DSET + (rg->bank * GPIO_BANK_STRIDE); + 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, &pdev->dev, 4, + ret = bgpio_init(&rg->chip, dev, 4, dat, set, ctrl, diro, NULL, 0); if (ret) { - dev_err(&pdev->dev, "bgpio_init() failed\n"); + dev_err(dev, "bgpio_init() failed\n"); return ret; } rg->chip.of_gpio_n_cells = 2; rg->chip.of_xlate = mediatek_gpio_xlate; - rg->chip.label = devm_kasprintf(&pdev->dev, GFP_KERNEL, "%s-bank%d", - dev_name(&pdev->dev), bank); + rg->chip.label = devm_kasprintf(dev, GFP_KERNEL, "%s-bank%d", + dev_name(dev), bank); - ret = devm_gpiochip_add_data(&pdev->dev, &rg->chip, gpio); + ret = devm_gpiochip_add_data(dev, &rg->chip, mtk); if (ret < 0) { - dev_err(&pdev->dev, "Could not register gpio %d, ret=%d\n", + dev_err(dev, "Could not register gpio %d, ret=%d\n", rg->chip.ngpio, ret); return ret; } - if (gpio->gpio_irq) { + if (mtk->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, + ret = devm_request_irq(dev, mtk->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); + dev_err(dev, "Error requesting IRQ %d: %d\n", + mtk->gpio_irq, ret); return ret; } 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"); + dev_err(dev, "failed to add gpiochip_irqchip\n"); return ret; } gpiochip_set_chained_irqchip(&rg->chip, &mediatek_gpio_irq_chip, - gpio->gpio_irq, NULL); + mtk->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); + dev_info(dev, "registering %d gpios\n", rg->chip.ngpio); return 0; } @@ -294,26 +290,27 @@ mediatek_gpio_bank_probe(struct platform_device *pdev, 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; + struct device *dev = &pdev->dev; + struct device_node *np = dev->of_node; + struct mtk *mtk; int i; - gpio_data = devm_kzalloc(&pdev->dev, sizeof(*gpio_data), GFP_KERNEL); - if (!gpio_data) + mtk = devm_kzalloc(dev, sizeof(*mtk), GFP_KERNEL); + if (!mtk) 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); + mtk->base = devm_ioremap_resource(dev, res); + if (IS_ERR(mtk->base)) + return PTR_ERR(mtk->base); - gpio_data->gpio_irq = irq_of_parse_and_map(np, 0); - gpio_data->dev = &pdev->dev; - platform_set_drvdata(pdev, gpio_data); - mediatek_gpio_irq_chip.name = dev_name(&pdev->dev); + mtk->gpio_irq = irq_of_parse_and_map(np, 0); + mtk->dev = dev; + platform_set_drvdata(pdev, mtk); + mediatek_gpio_irq_chip.name = dev_name(dev); for (i = 0; i < MTK_BANK_CNT; i++) - mediatek_gpio_bank_probe(pdev, np, i); + mediatek_gpio_bank_probe(dev, np, i); return 0; } -- cgit v1.2.3