From patchwork Tue Jul 26 08:13:13 2011 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Barry Song X-Patchwork-Id: 1007472 Received: from merlin.infradead.org (merlin.infradead.org [205.233.59.134]) by demeter1.kernel.org (8.14.4/8.14.4) with ESMTP id p6Q8F7mX007087 (version=TLSv1/SSLv3 cipher=DHE-RSA-AES256-SHA bits=256 verify=NO) for ; Tue, 26 Jul 2011 08:15:28 GMT Received: from canuck.infradead.org ([2001:4978:20e::1]) by merlin.infradead.org with esmtps (Exim 4.76 #1 (Red Hat Linux)) id 1Qlcmv-0007Kn-E7; Tue, 26 Jul 2011 08:14:49 +0000 Received: from localhost ([127.0.0.1] helo=canuck.infradead.org) by canuck.infradead.org with esmtp (Exim 4.76 #1 (Red Hat Linux)) id 1Qlcmt-0002jH-Gv; Tue, 26 Jul 2011 08:14:47 +0000 Received: from cluster-g.mailcontrol.com ([208.87.233.190]) by canuck.infradead.org with esmtps (Exim 4.76 #1 (Red Hat Linux)) id 1QlcmE-0002cs-5O for linux-arm-kernel@lists.infradead.org; Tue, 26 Jul 2011 08:14:08 +0000 Received: from rly18g.srv.mailcontrol.com (localhost.localdomain [127.0.0.1]) by rly18g.srv.mailcontrol.com (MailControl) with ESMTP id p6Q8Duva029658 for ; Tue, 26 Jul 2011 09:14:00 +0100 Received: from localhost.localdomain (localhost.localdomain [127.0.0.1]) by rly18g.srv.mailcontrol.com (MailControl) id p6Q8Dl8J028763 for ; Tue, 26 Jul 2011 09:13:47 +0100 Received: from banasiexc01.ASIA.ROOT.PRI ([202.80.51.114]) by rly18g-eth0.srv.mailcontrol.com (envelope-sender ) (MIMEDefang) with ESMTP id p6Q8Dbb7027757 (TLS bits=128 verify=FAIL); Tue, 26 Jul 2011 09:13:47 +0100 (BST) Received: from SHAASIEXC02.ASIA.ROOT.PRI (10.125.12.85) by banasiexc01.ASIA.ROOT.PRI (10.190.12.21) with Microsoft SMTP Server (TLS) id 14.1.270.1; Tue, 26 Jul 2011 13:43:44 +0530 Received: from localhost.localdomain (10.125.4.148) by asimail.csr.com (10.125.12.88) with Microsoft SMTP Server (TLS) id 14.1.270.1; Tue, 26 Jul 2011 16:13:42 +0800 From: Barry Song To: , , Subject: [RFC PATCH 2/2] GPIO: add gpiolib and irqchip for CSR SiRFprimaII GPIO controller Date: Tue, 26 Jul 2011 01:13:13 -0700 Message-ID: <1311667993-27189-3-git-send-email-bs14@csr.com> X-Mailer: git-send-email 1.7.1 In-Reply-To: <1311667993-27189-1-git-send-email-bs14@csr.com> References: <1311667993-27189-1-git-send-email-bs14@csr.com> MIME-Version: 1.0 X-Originating-IP: [10.125.4.148] X-Scanned-By: MailControl A-12-00-01 (www.mailcontrol.com) on 10.71.1.128 X-CRM114-Version: 20090807-BlameThorstenAndJenny ( TRE 0.7.6 (BSD) ) MR-646709E3 X-CRM114-CacheID: sfid-20110726_041406_664807_B8923D9F X-CRM114-Status: GOOD ( 22.57 ) X-Spam-Score: -0.7 (/) X-Spam-Report: SpamAssassin version 3.3.1 on canuck.infradead.org summary: Content analysis details: (-0.7 points) pts rule name description ---- ---------------------- -------------------------------------------------- -0.7 RCVD_IN_DNSWL_LOW RBL: Sender listed at http://www.dnswl.org/, low trust [208.87.233.190 listed in list.dnswl.org] Cc: Barry Song , workgroup.linux@csr.com, linux-arm-kernel@lists.infradead.org, Yuping Luo X-BeenThere: linux-arm-kernel@lists.infradead.org X-Mailman-Version: 2.1.12 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Sender: linux-arm-kernel-bounces@lists.infradead.org Errors-To: linux-arm-kernel-bounces+patchwork-linux-arm=patchwork.kernel.org@lists.infradead.org X-Greylist: IP, sender and recipient auto-whitelisted, not delayed by milter-greylist-4.2.6 (demeter1.kernel.org [140.211.167.41]); Tue, 26 Jul 2011 08:15:28 +0000 (UTC) From: Yuping Luo Signed-off-by: Yuping Luo Signed-off-by: Barry Song --- arch/arm/mach-prima2/include/mach/gpio.h | 34 ++ drivers/gpio/Kconfig | 6 + drivers/gpio/Makefile | 1 + drivers/gpio/gpio-sirf.c | 529 ++++++++++++++++++++++++++++++ 4 files changed, 570 insertions(+), 0 deletions(-) create mode 100644 arch/arm/mach-prima2/include/mach/gpio.h create mode 100644 drivers/gpio/gpio-sirf.c diff --git a/arch/arm/mach-prima2/include/mach/gpio.h b/arch/arm/mach-prima2/include/mach/gpio.h new file mode 100644 index 0000000..25673b1 --- /dev/null +++ b/arch/arm/mach-prima2/include/mach/gpio.h @@ -0,0 +1,34 @@ +/* + * arch/arm/mach-prima2/include/mach/gpio.h + * + * Copyright (c) 2011 Cambridge Silicon Radio Limited, a CSR plc group company. + * + * Licensed under GPLv2 or later. + */ + +#ifndef __MACH_GPIO_H +#define __MACH_GPIO_H + +#include + +#ifndef CONFIG_GPIO_SIRFCPLD +#define ARCH_NR_GPIOS (SIRFSOC_GPIO_BANK_SIZE * SIRFSOC_GPIO_NO_OF_BANKS) +#else +#define ARCH_NR_GPIOS (SIRFSOC_GPIO_BANK_SIZE * SIRFSOC_GPIO_NO_OF_BANKS + \ + SIRFSOC_GPIO_CPLD_SIZE + SIRFSOC_GPIO_IO_CPLD_SIZE + \ + SIRFSOC_GPIO_HS_CPLD_SIZE) +#endif + +#include +#include + +#define gpio_get_value __gpio_get_value +#define gpio_set_value __gpio_set_value +#define gpio_cansleep __gpio_cansleep +#define gpio_to_irq __gpio_to_irq + +void gpio_set_pull(unsigned gpio, int enable); +void gpio_pull_down(unsigned gpio); +void gpio_pull_up(unsigned gpio); + +#endif diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig index 3634986..c5da284 100644 --- a/drivers/gpio/Kconfig +++ b/drivers/gpio/Kconfig @@ -162,6 +162,12 @@ config GPIO_SCH The Intel Tunnel Creek processor has 5 GPIOs powered by the core power rail and 9 from suspend power supply. +config GPIO_SIRF + bool "CSR SiRFprimaII GPIO" + default y + help + Say yes here to support GPIO interface on CSR SiRFprimaII. + config GPIO_VX855 tristate "VIA VX855/VX875 GPIO" depends on MFD_SUPPORT && PCI diff --git a/drivers/gpio/Makefile b/drivers/gpio/Makefile index 7207112..f4ffd0e 100644 --- a/drivers/gpio/Makefile +++ b/drivers/gpio/Makefile @@ -42,6 +42,7 @@ obj-$(CONFIG_GPIO_S5PC100) += gpio-s5pc100.o obj-$(CONFIG_GPIO_S5PV210) += gpio-s5pv210.o obj-$(CONFIG_GPIO_SCH) += gpio-sch.o +obj-$(CONFIG_GPIO_SIRF) += gpio-sirf.o obj-$(CONFIG_GPIO_STMPE) += gpio-stmpe.o obj-$(CONFIG_GPIO_SX150X) += gpio-sx150x.o obj-$(CONFIG_GPIO_TC3589X) += gpio-tc3589x.o diff --git a/drivers/gpio/gpio-sirf.c b/drivers/gpio/gpio-sirf.c new file mode 100644 index 0000000..34b5820 --- /dev/null +++ b/drivers/gpio/gpio-sirf.c @@ -0,0 +1,529 @@ +/* + * GPIO controller driver for CSR SiRFprimaII + * + * Copyright (c) 2011 Cambridge Silicon Radio Limited, a CSR plc group company. + * + * Licensed under GPLv2 or later. + */ + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#define SIRFSOC_IRQ_SIRFSOC_GPIO_GROUP0 43 +#define SIRFSOC_IRQ_SIRFSOC_GPIO_GROUP1 44 +#define SIRFSOC_IRQ_SIRFSOC_GPIO_GROUP2 45 +#define SIRFSOC_IRQ_SIRFSOC_GPIO_GROUP3 46 +#define SIRFSOC_IRQ_SIRFSOC_GPIO_GROUP4 47 + +struct gpio_bank { + u8 group; + u16 irq; + u32 paden_bk_map; + u8 wake_mask; + + spinlock_t lock; + struct gpio_chip chip; +}; + +static struct gpio_bank sirfsoc_gpio_bank[SIRFSOC_GPIO_NO_OF_BANKS] = { + {.group = 0, .irq = SIRFSOC_IRQ_SIRFSOC_GPIO_GROUP0,}, + {.group = 1, .irq = SIRFSOC_IRQ_SIRFSOC_GPIO_GROUP1,}, + {.group = 2, .irq = SIRFSOC_IRQ_SIRFSOC_GPIO_GROUP2,}, + {.group = 3, .irq = SIRFSOC_IRQ_SIRFSOC_GPIO_GROUP3,}, + {.group = 4, .irq = SIRFSOC_IRQ_SIRFSOC_GPIO_GROUP4,} +}; + +static DEFINE_SPINLOCK(gpio_lock); + +static struct gpio_bank *sirfsoc_irq_to_bank(unsigned int irq) +{ + int bk; + + if ((irq < SIRFSOC_GPIO_IRQ_START) || (irq >= SIRFSOC_GPIO_IRQ_END)) { + printk(KERN_ALERT " Invalid GPIO IRQ/Bank: %d\n", irq); + return NULL; + } + + bk = (irq - SIRFSOC_GPIO_IRQ_START) / SIRFSOC_GPIO_BANK_SIZE; + return &sirfsoc_gpio_bank[bk]; +} + +static int sirfsoc_gpio_to_irq(struct gpio_chip *chip, unsigned offset) +{ + return SIRFSOC_GPIO_IRQ_START + (chip->base + offset); +} + +static int sirfsoc_irq_to_indx(unsigned int irq) +{ + return (irq - SIRFSOC_GPIO_IRQ_START) % SIRFSOC_GPIO_BANK_SIZE; +} + +static struct gpio_bank *sirfsoc_gpio_to_bank(unsigned int gpio) +{ + unsigned int bk = gpio / SIRFSOC_GPIO_BANK_SIZE; + + if (bk >= SIRFSOC_GPIO_NO_OF_BANKS) { + printk(KERN_ALERT "Invalid GPIO Number: %d\n", gpio); + return NULL; + } else { + return &sirfsoc_gpio_bank[bk]; + } +} + +static int sirfsoc_gpio_to_indx(unsigned int gpio) +{ + return gpio % SIRFSOC_GPIO_BANK_SIZE; +} + +static void sirfsoc_gpio_irq_ack(struct irq_data *d) +{ + struct gpio_bank *bank = sirfsoc_irq_to_bank(d->irq); + int index = sirfsoc_irq_to_indx(d->irq); + u32 status, offset; + unsigned long flags; + + if (bank != NULL) { + offset = SIRFSOC_GPIO_CTRL(bank->group, index); + spin_lock_irqsave(&gpio_lock, flags); + + status = readl(sirfsoc_gpio_pinmux_base + offset); + + writel(status, sirfsoc_gpio_pinmux_base + offset); + pr_debug("%s: ack gpio group %d index %d, status %#x\n", + __func__, bank->group, index, + readl(sirfsoc_gpio_pinmux_base + offset)); + spin_unlock_irqrestore(&gpio_lock, flags); + } + +} + +static void _sirfsoc_gpio_irq_mask(unsigned int irq) +{ + struct gpio_bank *bank = sirfsoc_irq_to_bank(irq); + int index = sirfsoc_irq_to_indx(irq); + u32 status, offset; + unsigned long flags; + + if (bank != NULL) { + pr_debug("%s: unmask gpio group %d index %d\n", __func__, + bank->group, index); + offset = SIRFSOC_GPIO_CTRL(bank->group, index); + spin_lock_irqsave(&gpio_lock, flags); + status = readl(sirfsoc_gpio_pinmux_base + offset); + + status &= ~SIRFSOC_GPIO_CTL_INTR_EN_MASK; + status &= ~SIRFSOC_GPIO_CTL_INTR_STS_MASK; + + writel(status, sirfsoc_gpio_pinmux_base + offset); + + spin_unlock_irqrestore(&gpio_lock, flags); + } +} + + +static void sirfsoc_gpio_irq_mask(struct irq_data *d) +{ + _sirfsoc_gpio_irq_mask(d->irq); +} + +static void sirfsoc_gpio_irq_unmask(struct irq_data *d) +{ + struct gpio_bank *bank = sirfsoc_irq_to_bank(d->irq); + int index = sirfsoc_irq_to_indx(d->irq); + u32 status, offset; + unsigned long flags; + + if (bank != NULL) { + pr_debug("%s: unmask gpio group %d index %d\n", __func__, + bank->group, index); + offset = SIRFSOC_GPIO_CTRL(bank->group, index); + + spin_lock_irqsave(&gpio_lock, flags); + status = readl(sirfsoc_gpio_pinmux_base + offset); + + status &= ~SIRFSOC_GPIO_CTL_INTR_STS_MASK; + status |= SIRFSOC_GPIO_CTL_INTR_EN_MASK; + + writel(status, sirfsoc_gpio_pinmux_base + offset); + spin_unlock_irqrestore(&gpio_lock, flags); + } +} + +static int sirfsoc_gpio_irq_type(struct irq_data *d, unsigned type) +{ + struct gpio_bank *bank = sirfsoc_irq_to_bank(d->irq); + int index = sirfsoc_irq_to_indx(d->irq); + u32 status, offset; + unsigned long flags; + + if (bank == NULL) + return -EINVAL; + + offset = SIRFSOC_GPIO_CTRL(bank->group, index); + spin_lock_irqsave(&gpio_lock, flags); + status = readl(sirfsoc_gpio_pinmux_base + offset); + status &= ~SIRFSOC_GPIO_CTL_INTR_STS_MASK; + + switch (type) { + case IRQ_TYPE_NONE: + break; + case IRQ_TYPE_EDGE_RISING: + status |= (SIRFSOC_GPIO_CTL_INTR_HIGH_MASK | SIRFSOC_GPIO_CTL_INTR_TYPE_MASK); + status &= ~SIRFSOC_GPIO_CTL_INTR_LOW_MASK; + break; + case IRQ_TYPE_EDGE_FALLING: + status &= ~SIRFSOC_GPIO_CTL_INTR_HIGH_MASK; + status |= (SIRFSOC_GPIO_CTL_INTR_LOW_MASK | SIRFSOC_GPIO_CTL_INTR_TYPE_MASK); + break; + case IRQ_TYPE_EDGE_BOTH: + status |= + (SIRFSOC_GPIO_CTL_INTR_HIGH_MASK | SIRFSOC_GPIO_CTL_INTR_LOW_MASK | + SIRFSOC_GPIO_CTL_INTR_TYPE_MASK); + break; + case IRQ_TYPE_LEVEL_LOW: + status &= ~(SIRFSOC_GPIO_CTL_INTR_HIGH_MASK | SIRFSOC_GPIO_CTL_INTR_TYPE_MASK); + status |= SIRFSOC_GPIO_CTL_INTR_LOW_MASK; + break; + case IRQ_TYPE_LEVEL_HIGH: + status |= SIRFSOC_GPIO_CTL_INTR_HIGH_MASK; + status &= ~(SIRFSOC_GPIO_CTL_INTR_LOW_MASK | SIRFSOC_GPIO_CTL_INTR_TYPE_MASK); + break; + } + + writel(status, sirfsoc_gpio_pinmux_base + offset); + + spin_unlock_irqrestore(&gpio_lock, flags); + + return 0; +} + +static struct irq_chip sirfsoc_irq_chip = { + .name = "SiRF SoC GPIO IRQ", + .irq_ack = sirfsoc_gpio_irq_ack, + .irq_mask = sirfsoc_gpio_irq_mask, + .irq_unmask = sirfsoc_gpio_irq_unmask, + .irq_set_type = sirfsoc_gpio_irq_type, +}; + +static void sirfsoc_gpio_handle_irq(unsigned int irq, struct irq_desc *desc) +{ + struct gpio_bank *bank = NULL; + u32 status, ctrl; + int i, index = 0; + + pr_debug("%s: irq %d\n", __func__, irq); + + for (i = 0; i < SIRFSOC_GPIO_NO_OF_BANKS; i++) { + if (sirfsoc_gpio_bank[i].irq == irq) { + bank = &sirfsoc_gpio_bank[i]; + break; + } + } + + if (bank == NULL) { + printk(KERN_ALERT " Invalid GPIO IRQ/Bank: %d\n", irq); + handle_bad_irq(irq, desc); + return; + } + + status = readl(sirfsoc_gpio_pinmux_base + SIRFSOC_GPIO_INT_STATUS(bank->group)); + if (!status) { + printk(KERN_WARNING + "%s: gpio group %d status %#x no interrupt is flaged\n", + __func__, bank->group, status); + handle_bad_irq(irq, desc); + return; + } + + while (status) { + ctrl = readl(sirfsoc_gpio_pinmux_base + SIRFSOC_GPIO_CTRL(bank->group, index)); + + /* + * Here we must check whether the corresponding GPIO's interrupt + * has been enabled, otherwise just skip it + */ + if ((status & 0x1) && (ctrl & SIRFSOC_GPIO_CTL_INTR_EN_MASK)) { + pr_debug("%s: gpio group %d index %d happens\n", + __func__, bank->group, index); + irq = + (SIRFSOC_GPIO_IRQ_START + + (bank->group * SIRFSOC_GPIO_BANK_SIZE)) + index; + generic_handle_irq(irq); + } + + index++; + status = status >> 1; + } + + return; +} + +static inline void sirfsoc_gpio_set_input(struct gpio_bank *bank, unsigned ctrl_offset) +{ + u32 status; + status = readl(sirfsoc_gpio_pinmux_base + ctrl_offset); + status &= ~SIRFSOC_GPIO_CTL_OUT_EN_MASK; + writel(status, sirfsoc_gpio_pinmux_base + ctrl_offset); +} + +static int sirfsoc_gpio_request(struct gpio_chip *chip, unsigned offset) +{ + struct gpio_bank *bank = container_of(chip, struct gpio_bank, chip); + unsigned long flags; + + spin_lock_irqsave(&bank->lock, flags); + + /*set direction as input and disable/mask irq */ + sirfsoc_gpio_set_input(bank, SIRFSOC_GPIO_CTRL(bank->group, offset)); + _sirfsoc_gpio_irq_mask(sirfsoc_gpio_to_irq(chip, offset)); + sirfsoc_get_gpio(bank->group, offset); + + spin_unlock_irqrestore(&bank->lock, flags); + return 0; +} + +static void sirfsoc_gpio_free(struct gpio_chip *chip, unsigned offset) +{ + struct gpio_bank *bank = container_of(chip, struct gpio_bank, chip); + unsigned long flags; + + spin_lock_irqsave(&bank->lock, flags); + + /*disable irq */ + _sirfsoc_gpio_irq_mask(sirfsoc_gpio_to_irq(chip, offset)); + + /*set gpio to input */ + sirfsoc_gpio_set_input(bank, SIRFSOC_GPIO_CTRL(bank->group, offset)); + + if (bank->paden_bk_map & (1 << offset)) + sirfsoc_get_gpio(bank->group, offset); + else + sirfsoc_put_gpio(bank->group, offset); +} + +static int sirfsoc_gpio_direction_input(struct gpio_chip *chip, unsigned gpio) +{ + struct gpio_bank *bank = container_of(chip, struct gpio_bank, chip); + int index = sirfsoc_gpio_to_indx(gpio); + unsigned long flags; + unsigned offset; + + offset = SIRFSOC_GPIO_CTRL(bank->group, index); + spin_lock_irqsave(&bank->lock, flags); + sirfsoc_gpio_set_input(bank, offset); + spin_unlock_irqrestore(&bank->lock, flags); + return 0; +} + +static inline void sirfsoc_gpio_set_output(struct gpio_bank *bank, unsigned offset, + int value) +{ + u32 status; + + status = readl(sirfsoc_gpio_pinmux_base + offset); + if (value) + status |= SIRFSOC_GPIO_CTL_DATAOUT_MASK; + else + status &= ~SIRFSOC_GPIO_CTL_DATAOUT_MASK; + + status &= ~SIRFSOC_GPIO_CTL_INTR_EN_MASK; + status |= SIRFSOC_GPIO_CTL_OUT_EN_MASK; + + writel(status, sirfsoc_gpio_pinmux_base + offset); +} + +static int sirfsoc_gpio_direction_output(struct gpio_chip *chip, unsigned gpio, int value) +{ + struct gpio_bank *bank = container_of(chip, struct gpio_bank, chip); + int index = sirfsoc_gpio_to_indx(gpio); + u32 offset; + unsigned long flags; + + offset = SIRFSOC_GPIO_CTRL(bank->group, index); + spin_lock_irqsave(&gpio_lock, flags); + sirfsoc_gpio_set_output(bank, offset, value); + spin_unlock_irqrestore(&gpio_lock, flags); + + return 0; +} + +void gpio_set_pull(unsigned gpio, int enable) +{ + struct gpio_bank *bank = sirfsoc_gpio_to_bank(gpio); + int index = sirfsoc_gpio_to_indx(gpio); + u32 status, offset; + unsigned long flags; + + BUG_ON(!bank); + + offset = SIRFSOC_GPIO_CTRL(bank->group, index); + + spin_lock_irqsave(&gpio_lock, flags); + + status = readl(sirfsoc_gpio_pinmux_base + offset); + + if (enable) + status |= SIRFSOC_GPIO_CTL_PULL_MASK; + else + status &= ~SIRFSOC_GPIO_CTL_PULL_MASK; + + writel(status, sirfsoc_gpio_pinmux_base + offset); + + spin_unlock_irqrestore(&gpio_lock, flags); +} +EXPORT_SYMBOL(gpio_set_pull); + +void gpio_pull_up(unsigned int gpio) +{ + struct gpio_bank *bank = sirfsoc_gpio_to_bank(gpio); + int index = sirfsoc_gpio_to_indx(gpio); + u32 status, offset; + unsigned long flags; + + BUG_ON(!bank); + + gpio_set_pull(gpio, 1); + + offset = SIRFSOC_GPIO_CTRL(bank->group, index); + + spin_lock_irqsave(&gpio_lock, flags); + + status = readl(sirfsoc_gpio_pinmux_base + offset); + status |= SIRFSOC_GPIO_CTL_PULL_HIGH; + writel(status, sirfsoc_gpio_pinmux_base + offset); + + spin_unlock_irqrestore(&gpio_lock, flags); +} +EXPORT_SYMBOL(gpio_pull_up); + +void gpio_pull_down(unsigned int gpio) +{ + struct gpio_bank *bank = sirfsoc_gpio_to_bank(gpio); + int index = sirfsoc_gpio_to_indx(gpio); + u32 status, offset; + unsigned long flags; + + BUG_ON(!bank); + + gpio_set_pull(gpio, 1); + + offset = SIRFSOC_GPIO_CTRL(bank->group, index); + + spin_lock_irqsave(&gpio_lock, flags); + + status = readl(sirfsoc_gpio_pinmux_base + offset); + status &= ~SIRFSOC_GPIO_CTL_PULL_HIGH; + writel(status, sirfsoc_gpio_pinmux_base + offset); + + spin_unlock_irqrestore(&gpio_lock, flags); +} +EXPORT_SYMBOL(gpio_pull_down); + +static int sirfsoc_gpio_get_value(struct gpio_chip *chip, unsigned offset) +{ + struct gpio_bank *bank = container_of(chip, struct gpio_bank, chip); + u32 status; + + status = readl(sirfsoc_gpio_pinmux_base + SIRFSOC_GPIO_CTRL(bank->group, offset)); + + return !!(status & SIRFSOC_GPIO_CTL_DATAIN_MASK); +} + +static void sirfsoc_gpio_set_value(struct gpio_chip *chip, unsigned offset, + int value) +{ + struct gpio_bank *bank = container_of(chip, struct gpio_bank, chip); + u32 status; + + status = readl(sirfsoc_gpio_pinmux_base + SIRFSOC_GPIO_CTRL(bank->group, offset)); + if (value) + status |= SIRFSOC_GPIO_CTL_DATAOUT_MASK; + else + status &= ~SIRFSOC_GPIO_CTL_DATAOUT_MASK; + writel(status, sirfsoc_gpio_pinmux_base + SIRFSOC_GPIO_CTRL(bank->group, offset)); +} + +static int sirfsoc_gpio_probe(struct platform_device *pdev) +{ + int i; + + struct gpio_bank *bank; + int gpio = 0; + + for (i = 0; i < SIRFSOC_GPIO_NO_OF_BANKS; i++) { + bank = &sirfsoc_gpio_bank[i]; + spin_lock_init(&bank->lock); + bank->paden_bk_map = readl(sirfsoc_gpio_pinmux_base + SIRFSOC_GPIO_PAD_EN(i)); + irq_set_chained_handler(bank->irq, sirfsoc_gpio_handle_irq); + + bank->chip.request = sirfsoc_gpio_request; + bank->chip.free = sirfsoc_gpio_free; + bank->chip.direction_input = sirfsoc_gpio_direction_input; + bank->chip.get = sirfsoc_gpio_get_value; + bank->chip.direction_output = sirfsoc_gpio_direction_output; + bank->chip.set = sirfsoc_gpio_set_value; + bank->chip.to_irq = sirfsoc_gpio_to_irq; + + bank->chip.label = "gpio"; + bank->chip.base = gpio; + + gpio += SIRFSOC_GPIO_BANK_SIZE; + bank->chip.ngpio = SIRFSOC_GPIO_BANK_SIZE; + gpiochip_add(&bank->chip); + } + + for (i = SIRFSOC_GPIO_IRQ_START; i < SIRFSOC_GPIO_IRQ_END; i++) { + irq_set_chip(i, &sirfsoc_irq_chip); + irq_set_handler(i, handle_level_irq); + set_irq_flags(i, IRQF_VALID | IRQF_PROBE); + } + + return 0; +} + +static int sirfsoc_gpio_remove(struct platform_device *dev) +{ + return 0; +} + +static const struct of_device_id sirf_gpio_of_match[] = { + {.compatible = "sirf,prima2-gpio", }, + {}, +}; +MODULE_DEVICE_TABLE(of, sirf_gpio_of_match); + +static struct platform_driver sirfsoc_gpio_driver = { + .driver = { + .name = "sirfsoc_gpio", + .of_match_table = sirf_gpio_of_match, + }, + .probe = sirfsoc_gpio_probe, + .remove = sirfsoc_gpio_remove, +}; + +static int __init sirfsoc_gpio_init(void) +{ + return platform_driver_register(&sirfsoc_gpio_driver); +} +core_initcall(sirfsoc_gpio_init); + +static void __exit sirfsoc_gpio_exit(void) +{ + platform_driver_unregister(&sirfsoc_gpio_driver); +} +module_exit(sirfsoc_gpio_exit); + +MODULE_DESCRIPTION("SiRFSoC gpio driver"); +MODULE_AUTHOR("Yuping Luo , Barry Song "); +MODULE_LICENSE("GPL");