aboutsummaryrefslogtreecommitdiffstats
path: root/target/linux/ramips/patches-4.4/generated by cgit v1.2.3 (git 2.25.1) at 2025-02-15 10:03:57 +0000
ref='#n320'>320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405
From 61ac7d9b4228de8c332900902c2b93189b042eab Mon Sep 17 00:00:00 2001
From: John Crispin <blogic@openwrt.org>
Date: Sun, 27 Jul 2014 11:00:32 +0100
Subject: [PATCH 28/53] GPIO: ralink: add mt7621 gpio controller

Signed-off-by: John Crispin <blogic@openwrt.org>
---
 arch/mips/Kconfig          |    3 +
 drivers/gpio/Kconfig       |    6 +
 drivers/gpio/Makefile      |    1 +
 drivers/gpio/gpio-mt7621.c |  354 ++++++++++++++++++++++++++++++++++++++++++++
 4 files changed, 364 insertions(+)
 create mode 100644 drivers/gpio/gpio-mt7621.c

--- a/arch/mips/Kconfig
+++ b/arch/mips/Kconfig
@@ -588,6 +588,9 @@ config RALINK
 	select RESET_CONTROLLER
 	select PINCTRL
 	select PINCTRL_RT2880
+	select ARCH_HAS_RESET_CONTROLLER
+	select RESET_CONTROLLER
+	select ARCH_REQUIRE_GPIOLIB
 
 config SGI_IP22
 	bool "SGI IP22 (Indy/Indigo2)"
--- a/drivers/gpio/Kconfig
+++ b/drivers/gpio/Kconfig
@@ -261,6 +261,12 @@ config GPIO_MB86S7X
 	help
 	  Say yes here to support the GPIO controller in Fujitsu MB86S70 SoCs.
 
+config GPIO_MT7621
+	bool "Mediatek GPIO Support"
+	depends on SOC_MT7620 || SOC_MT7621
+	help
+	  Say yes here to support the Mediatek SoC GPIO device
+
 config GPIO_MM_LANTIQ
 	bool "Lantiq Memory mapped GPIOs"
 	depends on LANTIQ && SOC_XWAY
--- a/drivers/gpio/Makefile
+++ b/drivers/gpio/Makefile
@@ -120,3 +120,4 @@ obj-$(CONFIG_GPIO_XTENSA)	+= gpio-xtensa
 obj-$(CONFIG_GPIO_ZEVIO)	+= gpio-zevio.o
 obj-$(CONFIG_GPIO_ZYNQ)		+= gpio-zynq.o
 obj-$(CONFIG_GPIO_ZX)		+= gpio-zx.o
+obj-$(CONFIG_GPIO_MT7621)	+= gpio-mt7621.o
--- /dev/null
+++ b/drivers/gpio/gpio-mt7621.c
@@ -0,0 +1,354 @@
+/*
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published
+ * by the Free Software Foundation.
+ *
+ * Copyright (C) 2009-2011 Gabor Juhos <juhosg@openwrt.org>
+ * Copyright (C) 2013 John Crispin <blogic@openwrt.org>
+ */
+
+#include <linux/io.h>
+#include <linux/err.h>
+#include <linux/gpio.h>
+#include <linux/module.h>
+#include <linux/of_irq.h>
+#include <linux/spinlock.h>
+#include <linux/irqdomain.h>
+#include <linux/interrupt.h>
+#include <linux/platform_device.h>
+
+#define MTK_MAX_BANK		3
+#define MTK_BANK_WIDTH		32
+
+enum mediatek_gpio_reg {
+	GPIO_REG_CTRL = 0,
+	GPIO_REG_POL,
+	GPIO_REG_DATA,
+	GPIO_REG_DSET,
+	GPIO_REG_DCLR,
+	GPIO_REG_REDGE,
+	GPIO_REG_FEDGE,
+	GPIO_REG_HLVL,
+	GPIO_REG_LLVL,
+	GPIO_REG_STAT,
+	GPIO_REG_EDGE,
+};
+
+static void __iomem *mediatek_gpio_membase;
+static int mediatek_gpio_irq;
+static struct irq_domain *mediatek_gpio_irq_domain;
+static atomic_t irq_refcount = ATOMIC_INIT(0);
+
+struct mtk_gc {
+	struct gpio_chip chip;
+	spinlock_t lock;
+	int bank;
+	u32 rising;
+	u32 falling;
+} *gc_map[MTK_MAX_BANK];
+
+static inline struct mtk_gc
+*to_mediatek_gpio(struct gpio_chip *chip)
+{
+	struct mtk_gc *mgc;
+
+	mgc = container_of(chip, struct mtk_gc, chip);
+
+	return mgc;
+}
+
+static inline void
+mtk_gpio_w32(struct mtk_gc *rg, u8 reg, u32 val)
+{
+	iowrite32(val, mediatek_gpio_membase + (reg * 0x10) + (rg->bank * 0x4));
+}
+
+static inline u32
+mtk_gpio_r32(struct mtk_gc *rg, u8 reg)
+{
+	return ioread32(mediatek_gpio_membase + (reg * 0x10) + (rg->bank * 0x4));
+}
+
+static void
+mediatek_gpio_set(struct gpio_chip *chip, unsigned offset, int value)
+{
+	struct mtk_gc *rg = to_mediatek_gpio(chip);
+
+	mtk_gpio_w32(rg, (value) ? GPIO_REG_DSET : GPIO_REG_DCLR, BIT(offset));
+}
+
+static int
+mediatek_gpio_get(struct gpio_chip *chip, unsigned offset)
+{
+	struct mtk_gc *rg = to_mediatek_gpio(chip);
+
+	return !!(mtk_gpio_r32(rg, GPIO_REG_DATA) & BIT(offset));
+}
+
+static int
+mediatek_gpio_direction_input(struct gpio_chip *chip, unsigned offset)
+{
+	struct mtk_gc *rg = to_mediatek_gpio(chip);
+	unsigned long flags;
+	u32 t;
+
+	spin_lock_irqsave(&rg->lock, flags);
+	t = mtk_gpio_r32(rg, GPIO_REG_CTRL);
+	t &= ~BIT(offset);
+	mtk_gpio_w32(rg, GPIO_REG_CTRL, t);
+	spin_unlock_irqrestore(&rg->lock, flags);
+
+	return 0;
+}
+
+static int
+mediatek_gpio_direction_output(struct gpio_chip *chip,
+					unsigned offset, int value)
+{
+	struct mtk_gc *rg = to_mediatek_gpio(chip);
+	unsigned long flags;
+	u32 t;
+
+	spin_lock_irqsave(&rg->lock, flags);
+	t = mtk_gpio_r32(rg, GPIO_REG_CTRL);
+	t |= BIT(offset);
+	mtk_gpio_w32(rg, GPIO_REG_CTRL, t);
+	mediatek_gpio_set(chip, offset, value);
+	spin_unlock_irqrestore(&rg->lock, flags);
+
+	return 0;
+}
+
+static int
+mediatek_gpio_get_direction(struct gpio_chip *chip, unsigned offset)
+{
+	struct mtk_gc *rg = to_mediatek_gpio(chip);
+	unsigned long flags;
+	u32 t;
+
+	spin_lock_irqsave(&rg->lock, flags);
+	t = mtk_gpio_r32(rg, GPIO_REG_CTRL);
+	spin_unlock_irqrestore(&rg->lock, flags);
+
+	if (t & BIT(offset))
+		return 0;
+
+	return 1;
+}
+
+static int
+mediatek_gpio_to_irq(struct gpio_chip *chip, unsigned pin)
+{
+	struct mtk_gc *rg = to_mediatek_gpio(chip);
+
+	return irq_create_mapping(mediatek_gpio_irq_domain, pin + (rg->bank * MTK_BANK_WIDTH));
+}
+
+static int
+mediatek_gpio_bank_probe(struct platform_device *pdev, struct device_node *bank)
+{
+	const __be32 *id = of_get_property(bank, "reg", NULL);
+	struct mtk_gc *rg = devm_kzalloc(&pdev->dev,
+				sizeof(struct mtk_gc), GFP_KERNEL);
+
+	if (!rg || !id || be32_to_cpu(*id) > MTK_MAX_BANK)
+		return -ENOMEM;
+
+	gc_map[be32_to_cpu(*id)] = rg;
+
+	memset(rg, 0, sizeof(struct mtk_gc));
+
+	spin_lock_init(&rg->lock);
+
+	rg->chip.dev = &pdev->dev;
+	rg->chip.label = dev_name(&pdev->dev);
+	rg->chip.of_node = bank;
+	rg->chip.base = MTK_BANK_WIDTH * be32_to_cpu(*id);
+	rg->chip.ngpio = MTK_BANK_WIDTH;
+	rg->chip.direction_input = mediatek_gpio_direction_input;
+	rg->chip.direction_output = mediatek_gpio_direction_output;
+	rg->chip.get_direction = mediatek_gpio_get_direction;
+	rg->chip.get = mediatek_gpio_get;
+	rg->chip.set = mediatek_gpio_set;
+	if (mediatek_gpio_irq_domain)
+		rg->chip.to_irq = mediatek_gpio_to_irq;
+	rg->bank = be32_to_cpu(*id);
+
+	/* 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 gpiochip_add(&rg->chip);
+}
+
+static void
+mediatek_gpio_irq_handler(struct irq_desc *desc)
+{
+	int i;
+
+	for (i = 0; i < MTK_MAX_BANK; i++) {
+		struct mtk_gc *rg = gc_map[i];
+		unsigned long pending;
+		int bit;
+
+		if (!rg)
+			continue;
+
+		pending = mtk_gpio_r32(rg, GPIO_REG_STAT);
+
+		for_each_set_bit(bit, &pending, MTK_BANK_WIDTH) {
+			u32 map = irq_find_mapping(mediatek_gpio_irq_domain, (MTK_BANK_WIDTH * i) + bit);
+
+			generic_handle_irq(map);
+			mtk_gpio_w32(rg, GPIO_REG_STAT, BIT(bit));
+		}
+	}
+}
+
+static void
+mediatek_gpio_irq_unmask(struct irq_data *d)
+{
+	int pin = d->hwirq;
+	int bank = pin / 32;
+	struct mtk_gc *rg = gc_map[bank];
+	unsigned long flags;
+	u32 rise, fall;
+
+	if (!rg)
+		return;
+
+	rise = mtk_gpio_r32(rg, GPIO_REG_REDGE);
+	fall = mtk_gpio_r32(rg, GPIO_REG_FEDGE);
+
+	spin_lock_irqsave(&rg->lock, flags);
+	mtk_gpio_w32(rg, GPIO_REG_REDGE, rise | (BIT(d->hwirq) & rg->rising));
+	mtk_gpio_w32(rg, GPIO_REG_FEDGE, fall | (BIT(d->hwirq) & rg->falling));
+	spin_unlock_irqrestore(&rg->lock, flags);
+}
+
+static void
+mediatek_gpio_irq_mask(struct irq_data *d)
+{
+	int pin = d->hwirq;
+	int bank = pin / 32;
+	struct mtk_gc *rg = gc_map[bank];
+	unsigned long flags;
+	u32 rise, fall;
+
+	if (!rg)
+		return;
+
+	rise = mtk_gpio_r32(rg, GPIO_REG_REDGE);
+	fall = mtk_gpio_r32(rg, GPIO_REG_FEDGE);
+
+	spin_lock_irqsave(&rg->lock, flags);
+	mtk_gpio_w32(rg, GPIO_REG_FEDGE, fall & ~BIT(d->hwirq));
+	mtk_gpio_w32(rg, GPIO_REG_REDGE, rise & ~BIT(d->hwirq));
+	spin_unlock_irqrestore(&rg->lock, flags);
+}
+
+static int
+mediatek_gpio_irq_type(struct irq_data *d, unsigned int type)
+{
+	int pin = d->hwirq;
+	int bank = pin / 32;
+	struct mtk_gc *rg = gc_map[bank];
+	u32 mask = BIT(d->hwirq);
+
+	if (!rg)
+		return -1;
+
+	if (type == IRQ_TYPE_PROBE) {
+		if ((rg->rising | rg->falling) & mask)
+			return 0;
+
+		type = IRQ_TYPE_EDGE_RISING | IRQ_TYPE_EDGE_FALLING;
+	}
+
+	if (type & IRQ_TYPE_EDGE_RISING)
+		rg->rising |= mask;
+	else
+		rg->rising &= ~mask;
+
+	if (type & IRQ_TYPE_EDGE_FALLING)
+		rg->falling |= mask;
+	else
+		rg->falling &= ~mask;
+
+	return 0;
+}
+
+static struct irq_chip mediatek_gpio_irq_chip = {
+	.name		= "GPIO",
+	.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_gpio_map(struct irq_domain *d, unsigned int irq, irq_hw_number_t hw)
+{
+	irq_set_chip_and_handler(irq, &mediatek_gpio_irq_chip, handle_level_irq);
+	irq_set_handler_data(irq, d);
+
+	return 0;
+}
+
+static const struct irq_domain_ops irq_domain_ops = {
+	.xlate = irq_domain_xlate_onecell,
+	.map = mediatek_gpio_gpio_map,
+};
+
+static int
+mediatek_gpio_probe(struct platform_device *pdev)
+{
+	struct device_node *bank, *np = pdev->dev.of_node;
+	struct resource *res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+
+	mediatek_gpio_membase = devm_ioremap_resource(&pdev->dev, res);
+	if (IS_ERR(mediatek_gpio_membase))
+		return PTR_ERR(mediatek_gpio_membase);
+
+	mediatek_gpio_irq = irq_of_parse_and_map(np, 0);
+	if (mediatek_gpio_irq) {
+		mediatek_gpio_irq_domain = irq_domain_add_linear(np,
+			MTK_MAX_BANK * MTK_BANK_WIDTH,
+			&irq_domain_ops, NULL);
+		if (!mediatek_gpio_irq_domain)
+			dev_err(&pdev->dev, "irq_domain_add_linear failed\n");
+	}
+
+	for_each_child_of_node(np, bank)
+		if (of_device_is_compatible(bank, "mtk,mt7621-gpio-bank"))
+			mediatek_gpio_bank_probe(pdev, bank);
+
+	if (mediatek_gpio_irq_domain)
+		irq_set_chained_handler(mediatek_gpio_irq, mediatek_gpio_irq_handler);
+
+	return 0;
+}
+
+static const struct of_device_id mediatek_gpio_match[] = {
+	{ .compatible = "mtk,mt7621-gpio" },
+	{},
+};
+MODULE_DEVICE_TABLE(of, mediatek_gpio_match);
+
+static struct platform_driver mediatek_gpio_driver = {
+	.probe = mediatek_gpio_probe,
+	.driver = {
+		.name = "mt7621_gpio",
+		.owner = THIS_MODULE,
+		.of_match_table = mediatek_gpio_match,
+	},
+};
+
+static int __init
+mediatek_gpio_init(void)
+{
+	return platform_driver_register(&mediatek_gpio_driver);
+}
+
+subsys_initcall(mediatek_gpio_init);