From patchwork Sun Jan 22 12:18:44 2017 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Linus Walleij X-Patchwork-Id: 92149 Delivered-To: patch@linaro.org Received: by 10.140.20.99 with SMTP id 90csp803238qgi; Sun, 22 Jan 2017 04:19:43 -0800 (PST) X-Received: by 10.84.247.2 with SMTP id n2mr35504522pll.39.1485087583874; Sun, 22 Jan 2017 04:19:43 -0800 (PST) Return-Path: Received: from vger.kernel.org (vger.kernel.org. [209.132.180.67]) by mx.google.com with ESMTP id f17si12487849pgh.74.2017.01.22.04.19.43; Sun, 22 Jan 2017 04:19:43 -0800 (PST) Received-SPF: pass (google.com: best guess record for domain of linux-gpio-owner@vger.kernel.org designates 209.132.180.67 as permitted sender) client-ip=209.132.180.67; Authentication-Results: mx.google.com; dkim=neutral (body hash did not verify) header.i=@linaro.org; spf=pass (google.com: best guess record for domain of linux-gpio-owner@vger.kernel.org designates 209.132.180.67 as permitted sender) smtp.mailfrom=linux-gpio-owner@vger.kernel.org; dmarc=fail (p=NONE sp=NONE dis=NONE) header.from=linaro.org Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1751390AbdAVMTW (ORCPT + 4 others); Sun, 22 Jan 2017 07:19:22 -0500 Received: from mail-lf0-f45.google.com ([209.85.215.45]:34339 "EHLO mail-lf0-f45.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1751104AbdAVMTT (ORCPT ); Sun, 22 Jan 2017 07:19:19 -0500 Received: by mail-lf0-f45.google.com with SMTP id v186so80683659lfa.1 for ; Sun, 22 Jan 2017 04:18:48 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=linaro.org; s=google; h=from:to:cc:subject:date:message-id; bh=IZljskRvBeKALVj+xmi0l0xzutPeFlxks1QlKg0h434=; b=Dw0R7QKn02FpPUHRHfl7JEkL+jyvQRcphVTBtEG53TOAVVHzdjeorhENooIlflZ2dp CFJP4duFkWsngInEY/L/msUMhjztm0ZoEBgnUFh3H7rOcGOKmrNtAiCXTseqll7iH0fn J+mBogHUbruegF4SM1/vi/y2gd2su7odOBFRw= X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:from:to:cc:subject:date:message-id; bh=IZljskRvBeKALVj+xmi0l0xzutPeFlxks1QlKg0h434=; b=Tq4DcNjraEA4q+tyrZjVkA4Wkkps5Yd0KdzrGgwJvz+/NpQi52W3j84OOOflio2s+D TrKpomN5nhERYMpWGP+OJ3ah2v7Zcqg3Qx+Nnlzs3TXuobLl1RMyumFo00p1dQbQBKQV zqAIg34tX03rhfVay0NJvXQvoz81Wn89k5vNcjw//q4yathP5GRV3lS2i7srI+msM+69 Wo9vEIF69ULEDa8StL2Qv3iqKV/mKdJFMD4UUpXjsc4WaYwjnUYW5zLvYUZyCq16fWp9 shtuo+lvzfH2WbGaaZxAqsLKMQX2UMA2Hy1D4j5QeWAnKXg0tl8Yjio5idc1fTKOsbT4 wlCw== X-Gm-Message-State: AIkVDXKWUhrU3xeeNEdVgLll4Gd15jF14GRA11fbM3iMwgSPuPmxz9gOTq8MnYlzFF58vmAa X-Received: by 10.25.242.79 with SMTP id d15mr1857392lfk.22.1485087527582; Sun, 22 Jan 2017 04:18:47 -0800 (PST) Received: from localhost.localdomain (c-357171d5.014-348-6c756e10.cust.bredbandsbolaget.se. [213.113.113.53]) by smtp.gmail.com with ESMTPSA id x3sm5307384lfa.7.2017.01.22.04.18.46 (version=TLS1_2 cipher=ECDHE-RSA-AES128-GCM-SHA256 bits=128/128); Sun, 22 Jan 2017 04:18:46 -0800 (PST) From: Linus Walleij To: linux-arm-kernel@lists.infradead.org, Hans Ulli Kroll , Florian Fainelli , Linus Walleij , Alexandre Courbot Cc: Janos Laube , Paulius Zaleckas , openwrt-devel@openwrt.org, Arnd Bergmann , linux-kernel@vger.kernel.org, linux-gpio@vger.kernel.org Subject: [PATCH 07/22] gpio: Add a driver for Cortina Systems Gemini GPIO Date: Sun, 22 Jan 2017 13:18:44 +0100 Message-Id: <20170122121844.10386-1-linus.walleij@linaro.org> X-Mailer: git-send-email 2.9.3 Sender: linux-gpio-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-gpio@vger.kernel.org This is a heavy edit/rewrite of the GPIO driver for the Gemini SoC from arch/arm/mach-gemini/gpio.c. This rewrite uses all the best-in-class helper like generic GPIO and GPIOLIB_IRQCHIP and has been tested on ITian Square One Gemini-based NAS/router. Cc: Janos Laube Cc: Paulius Zaleckas Cc: Hans Ulli Kroll Cc: Florian Fainelli Signed-off-by: Linus Walleij --- Note to self: just apply this to the GPIO tree when discussion is over. --- drivers/gpio/Kconfig | 9 ++ drivers/gpio/Makefile | 1 + drivers/gpio/gpio-gemini.c | 236 +++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 246 insertions(+) create mode 100644 drivers/gpio/gpio-gemini.c -- 2.9.3 -- To unsubscribe from this list: send the line "unsubscribe linux-gpio" in the body of a message to majordomo@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig index d5d36549ecc1..701809b1a105 100644 --- a/drivers/gpio/Kconfig +++ b/drivers/gpio/Kconfig @@ -197,6 +197,15 @@ config GPIO_GE_FPGA and write pin state) for GPIO implemented in a number of GE single board computers. +config GPIO_GEMINI + bool "Gemini GPIO" + depends on ARCH_GEMINI + depends on OF_GPIO + select GPIO_GENERIC + select GPIOLIB_IRQCHIP + help + Support for common GPIOs found in Cortina systems Gemini platforms. + config GPIO_GENERIC_PLATFORM tristate "Generic memory-mapped GPIO controller support (MMIO platform device)" select GPIO_GENERIC diff --git a/drivers/gpio/Makefile b/drivers/gpio/Makefile index a7676b82de6f..de45957b9b62 100644 --- a/drivers/gpio/Makefile +++ b/drivers/gpio/Makefile @@ -48,6 +48,7 @@ obj-$(CONFIG_GPIO_EP93XX) += gpio-ep93xx.o obj-$(CONFIG_GPIO_ETRAXFS) += gpio-etraxfs.o obj-$(CONFIG_GPIO_F7188X) += gpio-f7188x.o obj-$(CONFIG_GPIO_GE_FPGA) += gpio-ge.o +obj-$(CONFIG_GPIO_GEMINI) += gpio-gemini.o obj-$(CONFIG_GPIO_GPIO_MM) += gpio-gpio-mm.o obj-$(CONFIG_GPIO_GRGPIO) += gpio-grgpio.o obj-$(CONFIG_HTC_EGPIO) += gpio-htc-egpio.o diff --git a/drivers/gpio/gpio-gemini.c b/drivers/gpio/gpio-gemini.c new file mode 100644 index 000000000000..962485163b7f --- /dev/null +++ b/drivers/gpio/gpio-gemini.c @@ -0,0 +1,236 @@ +/* + * Gemini gpiochip and interrupt routines + * Copyright (C) 2017 Linus Walleij + * + * Based on arch/arm/mach-gemini/gpio.c: + * Copyright (C) 2008-2009 Paulius Zaleckas + * + * Based on plat-mxc/gpio.c: + * MXC GPIO support. (c) 2008 Daniel Mack + * Copyright 2008 Juergen Beisert, kernel@pengutronix.de + */ +#include +#include +#include +#include +#include +#include + +/* GPIO registers definition */ +#define GPIO_DATA_OUT 0x00 +#define GPIO_DATA_IN 0x04 +#define GPIO_DIR 0x08 +#define GPIO_DATA_SET 0x10 +#define GPIO_DATA_CLR 0x14 +#define GPIO_PULL_EN 0x18 +#define GPIO_PULL_TYPE 0x1C +#define GPIO_INT_EN 0x20 +#define GPIO_INT_STAT 0x24 +#define GPIO_INT_MASK 0x2C +#define GPIO_INT_CLR 0x30 +#define GPIO_INT_TYPE 0x34 +#define GPIO_INT_BOTH_EDGE 0x38 +#define GPIO_INT_LEVEL 0x3C +#define GPIO_DEBOUNCE_EN 0x40 +#define GPIO_DEBOUNCE_PRESCALE 0x44 + +/** + * struct gemini_gpio - Gemini GPIO state container + * @dev: containing device for this instance + * @gc: gpiochip for this instance + */ +struct gemini_gpio { + struct device *dev; + struct gpio_chip gc; + void __iomem *base; +}; + +static void gemini_gpio_ack_irq(struct irq_data *d) +{ + struct gpio_chip *gc = irq_data_get_irq_chip_data(d); + struct gemini_gpio *g = gpiochip_get_data(gc); + + writel(BIT(irqd_to_hwirq(d)), g->base + GPIO_INT_CLR); +} + +static void gemini_gpio_mask_irq(struct irq_data *d) +{ + struct gpio_chip *gc = irq_data_get_irq_chip_data(d); + struct gemini_gpio *g = gpiochip_get_data(gc); + u32 val; + + val = readl(g->base + GPIO_INT_EN); + val &= ~BIT(irqd_to_hwirq(d)); + writel(val, g->base + GPIO_INT_EN); +} + +static void gemini_gpio_unmask_irq(struct irq_data *d) +{ + struct gpio_chip *gc = irq_data_get_irq_chip_data(d); + struct gemini_gpio *g = gpiochip_get_data(gc); + u32 val; + + val = readl(g->base + GPIO_INT_EN); + val |= BIT(irqd_to_hwirq(d)); + writel(val, g->base + GPIO_INT_EN); +} + +static int gemini_gpio_set_irq_type(struct irq_data *d, unsigned int type) +{ + struct gpio_chip *gc = irq_data_get_irq_chip_data(d); + struct gemini_gpio *g = gpiochip_get_data(gc); + u32 mask = BIT(irqd_to_hwirq(d)); + u32 reg_both, reg_level, reg_type; + + reg_type = readl(g->base + GPIO_INT_TYPE); + reg_level = readl(g->base + GPIO_INT_LEVEL); + reg_both = readl(g->base + GPIO_INT_BOTH_EDGE); + + switch (type) { + case IRQ_TYPE_EDGE_BOTH: + irq_set_handler_locked(d, handle_edge_irq); + reg_type &= ~mask; + reg_both |= mask; + break; + case IRQ_TYPE_EDGE_RISING: + irq_set_handler_locked(d, handle_edge_irq); + reg_type &= ~mask; + reg_both &= ~mask; + reg_level &= ~mask; + break; + case IRQ_TYPE_EDGE_FALLING: + irq_set_handler_locked(d, handle_edge_irq); + reg_type &= ~mask; + reg_both &= ~mask; + reg_level |= mask; + break; + case IRQ_TYPE_LEVEL_HIGH: + irq_set_handler_locked(d, handle_level_irq); + reg_type |= mask; + reg_level &= ~mask; + break; + case IRQ_TYPE_LEVEL_LOW: + irq_set_handler_locked(d, handle_level_irq); + reg_type |= mask; + reg_level |= mask; + break; + default: + irq_set_handler_locked(d, handle_bad_irq); + return -EINVAL; + } + + writel(reg_type, g->base + GPIO_INT_TYPE); + writel(reg_level, g->base + GPIO_INT_LEVEL); + writel(reg_both, g->base + GPIO_INT_BOTH_EDGE); + + gemini_gpio_ack_irq(d); + + return 0; +} + +static struct irq_chip gemini_gpio_irqchip = { + .name = "GPIO", + .irq_ack = gemini_gpio_ack_irq, + .irq_mask = gemini_gpio_mask_irq, + .irq_unmask = gemini_gpio_unmask_irq, + .irq_set_type = gemini_gpio_set_irq_type, +}; + +static void gemini_gpio_irq_handler(struct irq_desc *desc) +{ + struct gpio_chip *gc = irq_desc_get_handler_data(desc); + struct gemini_gpio *g = gpiochip_get_data(gc); + struct irq_chip *irqchip = irq_desc_get_chip(desc); + int offset; + unsigned long stat; + + chained_irq_enter(irqchip, desc); + + stat = readl(g->base + GPIO_INT_STAT); + if (stat) + for_each_set_bit(offset, &stat, gc->ngpio) + generic_handle_irq(irq_find_mapping(gc->irqdomain, + offset)); + + chained_irq_exit(irqchip, desc); +} + +static int gemini_gpio_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct resource *res; + struct gemini_gpio *g; + int irq; + int ret; + + g = devm_kzalloc(dev, sizeof(*g), GFP_KERNEL); + if (!g) + return -ENOMEM; + + g->dev = dev; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + g->base = devm_ioremap_resource(dev, res); + if (IS_ERR(g->base)) + return PTR_ERR(g->base); + + irq = platform_get_irq(pdev, 0); + if (!irq) + return -EINVAL; + + ret = bgpio_init(&g->gc, dev, 4, + g->base + GPIO_DATA_IN, + g->base + GPIO_DATA_SET, + g->base + GPIO_DATA_CLR, + g->base + GPIO_DIR, + NULL, + 0); + if (ret) { + dev_err(dev, "unable to init generic GPIO\n"); + return ret; + } + g->gc.label = "Gemini"; + g->gc.base = -1; + g->gc.parent = dev; + g->gc.owner = THIS_MODULE; + /* ngpio is set by bgpio_init() */ + + ret = devm_gpiochip_add_data(dev, &g->gc, g); + if (ret) + return ret; + + /* Disable, unmask and clear all interrupts */ + writel(0x0, g->base + GPIO_INT_EN); + writel(0x0, g->base + GPIO_INT_MASK); + writel(~0x0, g->base + GPIO_INT_CLR); + + ret = gpiochip_irqchip_add(&g->gc, &gemini_gpio_irqchip, + 0, handle_bad_irq, + IRQ_TYPE_NONE); + if (ret) { + dev_info(dev, "could not add irqchip\n"); + return ret; + } + gpiochip_set_chained_irqchip(&g->gc, &gemini_gpio_irqchip, + irq, gemini_gpio_irq_handler); + + dev_info(dev, "Gemini GPIO @%p registered\n", g->base); + + return 0; +} + +static const struct of_device_id gemini_gpio_of_match[] = { + { + .compatible = "cortina,gemini-gpio", + }, + {}, +}; + +static struct platform_driver gemini_gpio_driver = { + .driver = { + .name = "gemini-gpio", + .of_match_table = of_match_ptr(gemini_gpio_of_match), + }, + .probe = gemini_gpio_probe, +}; +builtin_platform_driver(gemini_gpio_driver);