From patchwork Fri Jun 12 08:35:15 2015 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Jun Nie X-Patchwork-Id: 49818 Return-Path: X-Original-To: linaro@patches.linaro.org Delivered-To: linaro@patches.linaro.org Received: from mail-la0-f70.google.com (mail-la0-f70.google.com [209.85.215.70]) by ip-10-151-82-157.ec2.internal (Postfix) with ESMTPS id C17FA20C81 for ; Fri, 12 Jun 2015 08:35:54 +0000 (UTC) Received: by labsp1 with SMTP id sp1sf8727335lab.3 for ; Fri, 12 Jun 2015 01:35:53 -0700 (PDT) X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20130820; h=x-gm-message-state:mime-version:delivered-to:from:to:cc:subject :date:message-id:in-reply-to:references:sender:precedence:list-id :x-original-sender:x-original-authentication-results:mailing-list :list-post:list-help:list-archive:list-unsubscribe; bh=dGiwOlnazBdr/OA9Z4Fx2xvtrlFf7mYoLY7X6MbDYYg=; b=NGIJ+3060yEf/bYGK0kaK2OM6yXJmuchF49yQGjI91S0wrqXcuwJ4JaslY4kRv/yAs VroQc6WPdVfRY+tLGyZT1Be9iSV/8ljn38Mp6ahLKL+I/VubwYRy+sg8Z6VorBNqP3Se m9qipMhcgoMyDU5sS5lv3uivJ7eqCVkWqHREc1ISBBq+apYBFx9DAJ1zxhBNQNN0Kz0W QAsqwVGh9bLst0ykJ8j6HlJW5e9bTVJk33vd/eKrmL/ITHYsLlYYeUYyl5dewoXTZsIs hZ7m5QhcMlG3GDJoTlfHnV2WqXONCOJjzfso29mmQ12oESQFHq5g1oKhOssxHHma4Fqn 3WOw== X-Gm-Message-State: ALoCoQmTv/xlyW9cUUqR69cRBLdpLHOmglEhfsvQvIvDQLjehq3qNAHlaeBPYM1DdMCC+VGb3Lvr X-Received: by 10.112.142.170 with SMTP id rx10mr13166264lbb.12.1434098153765; Fri, 12 Jun 2015 01:35:53 -0700 (PDT) MIME-Version: 1.0 X-BeenThere: patchwork-forward@linaro.org Received: by 10.152.5.34 with SMTP id p2ls613059lap.32.gmail; Fri, 12 Jun 2015 01:35:53 -0700 (PDT) X-Received: by 10.152.5.164 with SMTP id t4mr13874720lat.16.1434098153492; Fri, 12 Jun 2015 01:35:53 -0700 (PDT) Received: from mail-lb0-f180.google.com (mail-lb0-f180.google.com. [209.85.217.180]) by mx.google.com with ESMTPS id bc7si2801235lbc.144.2015.06.12.01.35.53 for (version=TLSv1.2 cipher=ECDHE-RSA-AES128-GCM-SHA256 bits=128/128); Fri, 12 Jun 2015 01:35:53 -0700 (PDT) Received-SPF: pass (google.com: domain of patch+caf_=patchwork-forward=linaro.org@linaro.org designates 209.85.217.180 as permitted sender) client-ip=209.85.217.180; Received: by lbcue7 with SMTP id ue7so15958442lbc.0 for ; Fri, 12 Jun 2015 01:35:53 -0700 (PDT) X-Received: by 10.152.206.75 with SMTP id lm11mr13715525lac.41.1434098153362; Fri, 12 Jun 2015 01:35:53 -0700 (PDT) X-Forwarded-To: patchwork-forward@linaro.org X-Forwarded-For: patch@linaro.org patchwork-forward@linaro.org Delivered-To: patch@linaro.org Received: by 10.112.108.230 with SMTP id hn6csp381128lbb; Fri, 12 Jun 2015 01:35:52 -0700 (PDT) X-Received: by 10.70.138.38 with SMTP id qn6mr21100903pdb.119.1434098151300; Fri, 12 Jun 2015 01:35:51 -0700 (PDT) Received: from vger.kernel.org (vger.kernel.org. [209.132.180.67]) by mx.google.com with ESMTP id ai4si4383657pbc.176.2015.06.12.01.35.50; Fri, 12 Jun 2015 01:35:51 -0700 (PDT) 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; Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1751067AbbFLIfo (ORCPT + 2 others); Fri, 12 Jun 2015 04:35:44 -0400 Received: from mail-pa0-f46.google.com ([209.85.220.46]:34633 "EHLO mail-pa0-f46.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1752062AbbFLIfj (ORCPT ); Fri, 12 Jun 2015 04:35:39 -0400 Received: by payr10 with SMTP id r10so18980857pay.1 for ; Fri, 12 Jun 2015 01:35:39 -0700 (PDT) X-Received: by 10.66.144.40 with SMTP id sj8mr21122640pab.55.1434098139051; Fri, 12 Jun 2015 01:35:39 -0700 (PDT) Received: from localhost.localdomain ([107.6.117.179]) by mx.google.com with ESMTPSA id je4sm2885662pbb.17.2015.06.12.01.35.33 (version=TLSv1.2 cipher=ECDHE-RSA-AES128-SHA bits=128/128); Fri, 12 Jun 2015 01:35:38 -0700 (PDT) From: Jun Nie To: linus.walleij@linaro.org, linux-gpio@vger.kernel.org Cc: shawn.guo@linaro.org, wan.zhijun@zte.com.cn, jason.liu@linaro.org, Jun Nie Subject: [PATCH 2/2] gpio: zx: Add ZTE zx296702 GPIO support Date: Fri, 12 Jun 2015 16:35:15 +0800 Message-Id: <1434098115-17292-2-git-send-email-jun.nie@linaro.org> X-Mailer: git-send-email 1.9.1 In-Reply-To: <1434098115-17292-1-git-send-email-jun.nie@linaro.org> References: <1434098115-17292-1-git-send-email-jun.nie@linaro.org> Sender: linux-gpio-owner@vger.kernel.org Precedence: list List-ID: X-Mailing-List: linux-gpio@vger.kernel.org X-Removed-Original-Auth: Dkim didn't pass. X-Original-Sender: jun.nie@linaro.org X-Original-Authentication-Results: mx.google.com; spf=pass (google.com: domain of patch+caf_=patchwork-forward=linaro.org@linaro.org designates 209.85.217.180 as permitted sender) smtp.mail=patch+caf_=patchwork-forward=linaro.org@linaro.org Mailing-list: list patchwork-forward@linaro.org; contact patchwork-forward+owners@linaro.org X-Google-Group-Id: 836684582541 List-Post: , List-Help: , List-Archive: List-Unsubscribe: , Add ZTE zx296702 GPIO controller support Signed-off-by: Jun Nie --- drivers/gpio/Kconfig | 7 + drivers/gpio/Makefile | 1 + drivers/gpio/gpio-zx296702.c | 322 +++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 330 insertions(+) create mode 100644 drivers/gpio/gpio-zx296702.c diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig index caefe80..d513668 100644 --- a/drivers/gpio/Kconfig +++ b/drivers/gpio/Kconfig @@ -970,6 +970,13 @@ config GPIO_MC33880 SPI driver for Freescale MC33880 high-side/low-side switch. This provides GPIO interface supporting inputs and outputs. +config GPIO_ZX296702 + bool "ZTE ZX296702 GPIO support" + select IRQ_DOMAIN + select GPIOLIB_IRQCHIP + help + Say yes here to support the ZTE ZX296702 GPIO device + endmenu menu "USB GPIO expanders" diff --git a/drivers/gpio/Makefile b/drivers/gpio/Makefile index f71bb97..c26d81e 100644 --- a/drivers/gpio/Makefile +++ b/drivers/gpio/Makefile @@ -112,3 +112,4 @@ obj-$(CONFIG_GPIO_XILINX) += gpio-xilinx.o obj-$(CONFIG_GPIO_XTENSA) += gpio-xtensa.o obj-$(CONFIG_GPIO_ZEVIO) += gpio-zevio.o obj-$(CONFIG_GPIO_ZYNQ) += gpio-zynq.o +obj-$(CONFIG_GPIO_ZX296702) += gpio-zx296702.o diff --git a/drivers/gpio/gpio-zx296702.c b/drivers/gpio/gpio-zx296702.c new file mode 100644 index 0000000..2368c6b --- /dev/null +++ b/drivers/gpio/gpio-zx296702.c @@ -0,0 +1,322 @@ +/* + * Copyright (C) 2015 Linaro Ltd. + * + * 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. + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define ZX_GPIO_DIR 0x00 +#define ZX_GPIO_IVE 0x04 +#define ZX_GPIO_IV 0x08 +#define ZX_GPIO_IEP 0x0C +#define ZX_GPIO_IEN 0x10 +#define ZX_GPIO_DI 0x14 +#define ZX_GPIO_DO1 0x18 +#define ZX_GPIO_DO0 0x1C +#define ZX_GPIO_DO 0x20 + +#define ZX_GPIO_IM 0x28 +#define ZX_GPIO_IE 0x2C + +#define ZX_GPIO_MIS 0x30 +#define ZX_GPIO_IC 0x34 + +#define ZX_GPIO_NR 16 + +struct zx_gpio { + spinlock_t lock; + + void __iomem *base; + struct gpio_chip gc; + bool uses_pinctrl; + int irq; +}; + +static int zx_gpio_request(struct gpio_chip *gc, unsigned offset) +{ + struct zx_gpio *chip = container_of(gc, struct zx_gpio, gc); + int gpio = gc->base + offset; + + if (chip->uses_pinctrl) + return pinctrl_request_gpio(gpio); + return 0; +} + +static void zx_gpio_free(struct gpio_chip *gc, unsigned offset) +{ + struct zx_gpio *chip = container_of(gc, struct zx_gpio, gc); + int gpio = gc->base + offset; + + if (chip->uses_pinctrl) + pinctrl_free_gpio(gpio); +} + +static int zx_direction_input(struct gpio_chip *gc, unsigned offset) +{ + struct zx_gpio *chip = container_of(gc, struct zx_gpio, gc); + unsigned long flags; + unsigned char gpiodir; + + if (offset >= gc->ngpio) + return -EINVAL; + + spin_lock_irqsave(&chip->lock, flags); + gpiodir = readw_relaxed(chip->base + ZX_GPIO_DIR); + gpiodir &= ~(BIT(offset)); + writew_relaxed(gpiodir, chip->base + ZX_GPIO_DIR); + spin_unlock_irqrestore(&chip->lock, flags); + + return 0; +} + +static int zx_direction_output(struct gpio_chip *gc, unsigned offset, + int value) +{ + struct zx_gpio *chip = container_of(gc, struct zx_gpio, gc); + unsigned long flags; + unsigned char gpiodir; + + if (offset >= gc->ngpio) + return -EINVAL; + + spin_lock_irqsave(&chip->lock, flags); + gpiodir = readw_relaxed(chip->base + ZX_GPIO_DIR); + gpiodir |= BIT(offset); + writew_relaxed(gpiodir, chip->base + ZX_GPIO_DIR); + + if (value) + writew_relaxed(BIT(offset), chip->base + ZX_GPIO_DO1); + else + writew_relaxed(BIT(offset), chip->base + ZX_GPIO_DO0); + spin_unlock_irqrestore(&chip->lock, flags); + + return 0; +} + +static int zx_get_value(struct gpio_chip *gc, unsigned offset) +{ + struct zx_gpio *chip = container_of(gc, struct zx_gpio, gc); + + return !!(BIT(offset) & readw_relaxed(chip->base + ZX_GPIO_DI)); +} + +static void zx_set_value(struct gpio_chip *gc, unsigned offset, int value) +{ + struct zx_gpio *chip = container_of(gc, struct zx_gpio, gc); + + if (value) + writew_relaxed(BIT(offset), chip->base + ZX_GPIO_DO1); + else + writew_relaxed(BIT(offset), chip->base + ZX_GPIO_DO0); +} + +static int zx_irq_type(struct irq_data *d, unsigned trigger) +{ + struct gpio_chip *gc = irq_data_get_irq_chip_data(d); + struct zx_gpio *chip = container_of(gc, struct zx_gpio, gc); + int offset = irqd_to_hwirq(d); + unsigned long flags; + u8 gpiois, gpioi_epos, gpioi_eneg, gpioiev; + u8 bit = BIT(offset); + + if (offset < 0 || offset >= ZX_GPIO_NR) + return -EINVAL; + + spin_lock_irqsave(&chip->lock, flags); + + gpioiev = readw_relaxed(chip->base + ZX_GPIO_IV); + gpiois = readw_relaxed(chip->base + ZX_GPIO_IVE); + gpioi_epos = readw_relaxed(chip->base + ZX_GPIO_IEP); + gpioi_eneg = readw_relaxed(chip->base + ZX_GPIO_IEN); + + if (trigger & (IRQ_TYPE_LEVEL_HIGH | IRQ_TYPE_LEVEL_LOW)) { + gpiois |= bit; + if (trigger & IRQ_TYPE_LEVEL_HIGH) + gpioiev |= bit; + else + gpioiev &= ~bit; + } else + gpiois &= ~bit; + + if ((trigger & IRQ_TYPE_EDGE_BOTH) == IRQ_TYPE_EDGE_BOTH) { + gpioi_epos |= bit; + gpioi_eneg |= bit; + } else { + if (trigger & IRQ_TYPE_EDGE_RISING) { + gpioi_epos |= bit; + gpioi_eneg &= ~bit; + } else if (trigger & IRQ_TYPE_EDGE_FALLING) { + gpioi_eneg |= bit; + gpioi_epos &= ~bit; + } + } + + writew_relaxed(gpiois, chip->base + ZX_GPIO_IVE); + writew_relaxed(gpioi_epos, chip->base + ZX_GPIO_IEP); + writew_relaxed(gpioi_eneg, chip->base + ZX_GPIO_IEN); + writew_relaxed(gpioiev, chip->base + ZX_GPIO_IV); + spin_unlock_irqrestore(&chip->lock, flags); + + return 0; +} + +static void zx_irq_handler(unsigned irq, struct irq_desc *desc) +{ + unsigned long pending; + int offset; + struct gpio_chip *gc = irq_desc_get_handler_data(desc); + struct zx_gpio *chip = container_of(gc, struct zx_gpio, gc); + struct irq_chip *irqchip = irq_desc_get_chip(desc); + + chained_irq_enter(irqchip, desc); + + pending = readw_relaxed(chip->base + ZX_GPIO_MIS); + writew_relaxed(pending, chip->base + ZX_GPIO_IC); + if (pending) { + for_each_set_bit(offset, &pending, ZX_GPIO_NR) + generic_handle_irq(irq_find_mapping(gc->irqdomain, + offset)); + } + + chained_irq_exit(irqchip, desc); +} + +static void zx_irq_mask(struct irq_data *d) +{ + struct gpio_chip *gc = irq_data_get_irq_chip_data(d); + struct zx_gpio *chip = container_of(gc, struct zx_gpio, gc); + u8 mask = BIT(irqd_to_hwirq(d) % ZX_GPIO_NR); + u8 gpioie; + + spin_lock(&chip->lock); + gpioie = readw_relaxed(chip->base + ZX_GPIO_IM) | mask; + writew_relaxed(gpioie, chip->base + ZX_GPIO_IM); + gpioie = readw_relaxed(chip->base + ZX_GPIO_IE) & ~mask; + writew_relaxed(gpioie, chip->base + ZX_GPIO_IE); + spin_unlock(&chip->lock); +} + +static void zx_irq_unmask(struct irq_data *d) +{ + struct gpio_chip *gc = irq_data_get_irq_chip_data(d); + struct zx_gpio *chip = container_of(gc, struct zx_gpio, gc); + u8 mask = BIT(irqd_to_hwirq(d) % ZX_GPIO_NR); + u8 gpioie; + + spin_lock(&chip->lock); + gpioie = readw_relaxed(chip->base + ZX_GPIO_IM) & ~mask; + writew_relaxed(gpioie, chip->base + ZX_GPIO_IM); + gpioie = readw_relaxed(chip->base + ZX_GPIO_IE) | mask; + writew_relaxed(gpioie, chip->base + ZX_GPIO_IE); + spin_unlock(&chip->lock); +} + +static struct irq_chip zx_irqchip = { + .name = "zx", + .irq_mask = zx_irq_mask, + .irq_unmask = zx_irq_unmask, + .irq_set_type = zx_irq_type, +}; + +static int zx_gpio_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct zx_gpio *chip; + struct resource *res; + int id, ret, irq_base = 0; + + chip = devm_kzalloc(dev, sizeof(*chip), GFP_KERNEL); + if (chip == NULL) + return -ENOMEM; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + chip->base = devm_ioremap_resource(dev, res); + if (IS_ERR(chip->base)) + return PTR_ERR(chip->base); + + spin_lock_init(&chip->lock); + if (of_property_read_bool(dev->of_node, "gpio-ranges")) + chip->uses_pinctrl = true; + + id = of_alias_get_id(dev->of_node, "gpio"); + chip->gc.request = zx_gpio_request; + chip->gc.free = zx_gpio_free; + chip->gc.direction_input = zx_direction_input; + chip->gc.direction_output = zx_direction_output; + chip->gc.get = zx_get_value; + chip->gc.set = zx_set_value; + chip->gc.base = ZX_GPIO_NR * id; + chip->gc.ngpio = ZX_GPIO_NR; + chip->gc.label = dev_name(dev); + chip->gc.dev = dev; + chip->gc.owner = THIS_MODULE; + + ret = gpiochip_add(&chip->gc); + if (ret) + return ret; + + /* + * irq_chip support + */ + writew_relaxed(0xffff, chip->base + ZX_GPIO_IM); + writew_relaxed(0, chip->base + ZX_GPIO_IE); + chip->irq = platform_get_irq(pdev, 0); + if (chip->irq < 0) { + dev_err(dev, "invalid IRQ\n"); + return -ENODEV; + } + + ret = gpiochip_irqchip_add(&chip->gc, &zx_irqchip, + irq_base, handle_simple_irq, + IRQ_TYPE_NONE); + if (ret) { + dev_info(dev, "could not add irqchip\n"); + return ret; + } + gpiochip_set_chained_irqchip(&chip->gc, &zx_irqchip, + chip->irq, zx_irq_handler); + + platform_set_drvdata(pdev, chip); + dev_info(dev, "ZX GPIO chip registered\n"); + + return 0; +} + +#if defined(CONFIG_OF) +static const struct of_device_id zx_gpio_match[] = { + { + .compatible = "zte,zx296702-gpio", + }, + { }, +}; +MODULE_DEVICE_TABLE(of, zx_gpio_match); +#endif + +static struct platform_driver zx_gpio_driver = { + .probe = zx_gpio_probe, + .driver = { + .name = "zx_gpio", + .of_match_table = of_match_ptr(zx_gpio_match), + }, +}; + +module_platform_driver(zx_gpio_driver) + +MODULE_AUTHOR("Jun Nie "); +MODULE_DESCRIPTION("ZTE ZX296702 GPIO driver"); +MODULE_LICENSE("GPL");