aboutsummaryrefslogtreecommitdiffstats
path: root/target/linux/brcm2708/patches-3.18/0002-Add-bcm2708_gpio-driver.patch
diff options
context:
space:
mode:
authorJohn Crispin <john@openwrt.org>2015-02-11 10:17:55 +0000
committerJohn Crispin <john@openwrt.org>2015-02-11 10:17:55 +0000
commit408c9696264f8e25e4e4f6410e93d2edd06e59dd (patch)
tree1c9e8fbacb1508e8561c43f87389edf02cfb0955 /target/linux/brcm2708/patches-3.18/0002-Add-bcm2708_gpio-driver.patch
parentf90d9d486a8842077e68e90bba2b276ac1336bab (diff)
downloadupstream-408c9696264f8e25e4e4f6410e93d2edd06e59dd.tar.gz
upstream-408c9696264f8e25e4e4f6410e93d2edd06e59dd.tar.bz2
upstream-408c9696264f8e25e4e4f6410e93d2edd06e59dd.zip
brcm2708: update to v3.18
Signed-off-by: Álvaro Fernández Rojas <noltari@gmail.com> SVN-Revision: 44392
Diffstat (limited to 'target/linux/brcm2708/patches-3.18/0002-Add-bcm2708_gpio-driver.patch')
-rwxr-xr-xtarget/linux/brcm2708/patches-3.18/0002-Add-bcm2708_gpio-driver.patch609
1 files changed, 609 insertions, 0 deletions
diff --git a/target/linux/brcm2708/patches-3.18/0002-Add-bcm2708_gpio-driver.patch b/target/linux/brcm2708/patches-3.18/0002-Add-bcm2708_gpio-driver.patch
new file mode 100755
index 0000000000..b3f7b4af19
--- /dev/null
+++ b/target/linux/brcm2708/patches-3.18/0002-Add-bcm2708_gpio-driver.patch
@@ -0,0 +1,609 @@
+From 4f339b429583965a8eb7c23474414d0730db1215 Mon Sep 17 00:00:00 2001
+From: popcornmix <popcornmix@gmail.com>
+Date: Wed, 8 Oct 2014 18:50:05 +0100
+Subject: [PATCH 002/114] Add bcm2708_gpio driver
+
+Signed-off-by: popcornmix <popcornmix@gmail.com>
+
+bcm2708: Add extension to configure internal pulls
+
+The bcm2708 gpio controller supports internal pulls to be used as pull-up,
+pull-down or being entirely disabled. As it can be useful for a driver to
+change the pull configuration from it's default pull-down state, add an
+extension which allows configuring the pull per gpio.
+
+Signed-off-by: Julian Scheel <julian@jusst.de>
+
+bcm2708-gpio: Revert the use of pinctrl_request_gpio
+
+In non-DT systems, pinctrl_request_gpio always fails causing
+"requests probe deferral" messages. In DT systems, it isn't useful
+because the reference counting is independent of the normal pinctrl
+pin reservations.
+
+gpio: Only clear the currently occurring interrupt. Avoids losing interrupts
+
+See: linux #760
+
+bcm2708_gpio: Avoid calling irq_unmask for all interrupts
+
+When setting up the interrupts, specify that the handle_simple_irq
+handler should be used. This leaves interrupt acknowledgement to
+the caller, and prevents irq_unmask from being called for all
+interrupts.
+
+Issue: linux #760
+---
+ arch/arm/mach-bcm2708/Kconfig | 8 +
+ arch/arm/mach-bcm2708/Makefile | 1 +
+ arch/arm/mach-bcm2708/bcm2708.c | 28 ++
+ arch/arm/mach-bcm2708/bcm2708_gpio.c | 426 ++++++++++++++++++++++++++++++
+ arch/arm/mach-bcm2708/include/mach/gpio.h | 17 ++
+ include/linux/platform_data/bcm2708.h | 23 ++
+ 6 files changed, 503 insertions(+)
+ create mode 100644 arch/arm/mach-bcm2708/bcm2708_gpio.c
+ create mode 100644 arch/arm/mach-bcm2708/include/mach/gpio.h
+ create mode 100644 include/linux/platform_data/bcm2708.h
+
+diff --git a/arch/arm/mach-bcm2708/Kconfig b/arch/arm/mach-bcm2708/Kconfig
+index 1f11478..9355841 100644
+--- a/arch/arm/mach-bcm2708/Kconfig
++++ b/arch/arm/mach-bcm2708/Kconfig
+@@ -9,6 +9,14 @@ config MACH_BCM2708
+ help
+ Include support for the Broadcom(R) BCM2708 platform.
+
++config BCM2708_GPIO
++ bool "BCM2708 gpio support"
++ depends on MACH_BCM2708
++ select ARCH_REQUIRE_GPIOLIB
++ default y
++ help
++ Include support for the Broadcom(R) BCM2708 gpio.
++
+ config BCM2708_VCMEM
+ bool "Videocore Memory"
+ depends on MACH_BCM2708
+diff --git a/arch/arm/mach-bcm2708/Makefile b/arch/arm/mach-bcm2708/Makefile
+index c76f39bc..a722f3f 100644
+--- a/arch/arm/mach-bcm2708/Makefile
++++ b/arch/arm/mach-bcm2708/Makefile
+@@ -3,4 +3,5 @@
+ #
+
+ obj-$(CONFIG_MACH_BCM2708) += clock.o bcm2708.o armctrl.o vcio.o power.o dma.o
++obj-$(CONFIG_BCM2708_GPIO) += bcm2708_gpio.o
+ obj-$(CONFIG_BCM2708_VCMEM) += vc_mem.o
+diff --git a/arch/arm/mach-bcm2708/bcm2708.c b/arch/arm/mach-bcm2708/bcm2708.c
+index 9b4e709..7503649 100644
+--- a/arch/arm/mach-bcm2708/bcm2708.c
++++ b/arch/arm/mach-bcm2708/bcm2708.c
+@@ -331,6 +331,31 @@ static struct platform_device bcm2708_vcio_device = {
+ },
+ };
+
++#ifdef CONFIG_BCM2708_GPIO
++#define BCM_GPIO_DRIVER_NAME "bcm2708_gpio"
++
++static struct resource bcm2708_gpio_resources[] = {
++ [0] = { /* general purpose I/O */
++ .start = GPIO_BASE,
++ .end = GPIO_BASE + SZ_4K - 1,
++ .flags = IORESOURCE_MEM,
++ },
++};
++
++static u64 gpio_dmamask = DMA_BIT_MASK(DMA_MASK_BITS_COMMON);
++
++static struct platform_device bcm2708_gpio_device = {
++ .name = BCM_GPIO_DRIVER_NAME,
++ .id = -1, /* only one VideoCore I/O area */
++ .resource = bcm2708_gpio_resources,
++ .num_resources = ARRAY_SIZE(bcm2708_gpio_resources),
++ .dev = {
++ .dma_mask = &gpio_dmamask,
++ .coherent_dma_mask = DMA_BIT_MASK(DMA_MASK_BITS_COMMON),
++ },
++};
++#endif
++
+ static struct resource bcm2708_systemtimer_resources[] = {
+ [0] = { /* system timer access */
+ .start = ST_BASE,
+@@ -473,6 +498,9 @@ void __init bcm2708_init(void)
+
+ bcm_register_device(&bcm2708_dmaman_device);
+ bcm_register_device(&bcm2708_vcio_device);
++#ifdef CONFIG_BCM2708_GPIO
++ bcm_register_device(&bcm2708_gpio_device);
++#endif
+ bcm_register_device(&bcm2708_systemtimer_device);
+ bcm_register_device(&bcm2708_fb_device);
+ bcm_register_device(&bcm2708_usb_device);
+diff --git a/arch/arm/mach-bcm2708/bcm2708_gpio.c b/arch/arm/mach-bcm2708/bcm2708_gpio.c
+new file mode 100644
+index 0000000..c1e9254
+--- /dev/null
++++ b/arch/arm/mach-bcm2708/bcm2708_gpio.c
+@@ -0,0 +1,426 @@
++/*
++ * linux/arch/arm/mach-bcm2708/bcm2708_gpio.c
++ *
++ * Copyright (C) 2010 Broadcom
++ *
++ * 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 <linux/spinlock.h>
++#include <linux/module.h>
++#include <linux/delay.h>
++#include <linux/list.h>
++#include <linux/io.h>
++#include <linux/irq.h>
++#include <linux/interrupt.h>
++#include <linux/slab.h>
++#include <mach/gpio.h>
++#include <linux/gpio.h>
++#include <linux/platform_device.h>
++#include <mach/platform.h>
++#include <linux/pinctrl/consumer.h>
++
++#include <linux/platform_data/bcm2708.h>
++
++#define BCM_GPIO_DRIVER_NAME "bcm2708_gpio"
++#define DRIVER_NAME BCM_GPIO_DRIVER_NAME
++#define BCM_GPIO_USE_IRQ 1
++
++#define GPIOFSEL(x) (0x00+(x)*4)
++#define GPIOSET(x) (0x1c+(x)*4)
++#define GPIOCLR(x) (0x28+(x)*4)
++#define GPIOLEV(x) (0x34+(x)*4)
++#define GPIOEDS(x) (0x40+(x)*4)
++#define GPIOREN(x) (0x4c+(x)*4)
++#define GPIOFEN(x) (0x58+(x)*4)
++#define GPIOHEN(x) (0x64+(x)*4)
++#define GPIOLEN(x) (0x70+(x)*4)
++#define GPIOAREN(x) (0x7c+(x)*4)
++#define GPIOAFEN(x) (0x88+(x)*4)
++#define GPIOUD(x) (0x94+(x)*4)
++#define GPIOUDCLK(x) (0x98+(x)*4)
++
++#define GPIO_BANKS 2
++
++enum { GPIO_FSEL_INPUT, GPIO_FSEL_OUTPUT,
++ GPIO_FSEL_ALT5, GPIO_FSEL_ALT_4,
++ GPIO_FSEL_ALT0, GPIO_FSEL_ALT1,
++ GPIO_FSEL_ALT2, GPIO_FSEL_ALT3,
++};
++
++ /* Each of the two spinlocks protects a different set of hardware
++ * regiters and data structurs. This decouples the code of the IRQ from
++ * the GPIO code. This also makes the case of a GPIO routine call from
++ * the IRQ code simpler.
++ */
++static DEFINE_SPINLOCK(lock); /* GPIO registers */
++
++struct bcm2708_gpio {
++ struct list_head list;
++ void __iomem *base;
++ struct gpio_chip gc;
++ unsigned long rising[(BCM2708_NR_GPIOS + 31) / 32];
++ unsigned long falling[(BCM2708_NR_GPIOS + 31) / 32];
++ unsigned long high[(BCM2708_NR_GPIOS + 31) / 32];
++ unsigned long low[(BCM2708_NR_GPIOS + 31) / 32];
++};
++
++static int bcm2708_set_function(struct gpio_chip *gc, unsigned offset,
++ int function)
++{
++ struct bcm2708_gpio *gpio = container_of(gc, struct bcm2708_gpio, gc);
++ unsigned long flags;
++ unsigned gpiodir;
++ unsigned gpio_bank = offset / 10;
++ unsigned gpio_field_offset = (offset - 10 * gpio_bank) * 3;
++
++//printk(KERN_ERR DRIVER_NAME ": bcm2708_gpio_set_function %p (%d,%d)\n", gc, offset, function);
++ if (offset >= BCM2708_NR_GPIOS)
++ return -EINVAL;
++
++ spin_lock_irqsave(&lock, flags);
++
++ gpiodir = readl(gpio->base + GPIOFSEL(gpio_bank));
++ gpiodir &= ~(7 << gpio_field_offset);
++ gpiodir |= function << gpio_field_offset;
++ writel(gpiodir, gpio->base + GPIOFSEL(gpio_bank));
++ spin_unlock_irqrestore(&lock, flags);
++ gpiodir = readl(gpio->base + GPIOFSEL(gpio_bank));
++
++ return 0;
++}
++
++static int bcm2708_gpio_dir_in(struct gpio_chip *gc, unsigned offset)
++{
++ return bcm2708_set_function(gc, offset, GPIO_FSEL_INPUT);
++}
++
++static void bcm2708_gpio_set(struct gpio_chip *gc, unsigned offset, int value);
++static int bcm2708_gpio_dir_out(struct gpio_chip *gc, unsigned offset,
++ int value)
++{
++ int ret;
++ ret = bcm2708_set_function(gc, offset, GPIO_FSEL_OUTPUT);
++ if (ret >= 0)
++ bcm2708_gpio_set(gc, offset, value);
++ return ret;
++}
++
++static int bcm2708_gpio_get(struct gpio_chip *gc, unsigned offset)
++{
++ struct bcm2708_gpio *gpio = container_of(gc, struct bcm2708_gpio, gc);
++ unsigned gpio_bank = offset / 32;
++ unsigned gpio_field_offset = (offset - 32 * gpio_bank);
++ unsigned lev;
++
++ if (offset >= BCM2708_NR_GPIOS)
++ return 0;
++ lev = readl(gpio->base + GPIOLEV(gpio_bank));
++//printk(KERN_ERR DRIVER_NAME ": bcm2708_gpio_get %p (%d)=%d\n", gc, offset, 0x1 & (lev>>gpio_field_offset));
++ return 0x1 & (lev >> gpio_field_offset);
++}
++
++static void bcm2708_gpio_set(struct gpio_chip *gc, unsigned offset, int value)
++{
++ struct bcm2708_gpio *gpio = container_of(gc, struct bcm2708_gpio, gc);
++ unsigned gpio_bank = offset / 32;
++ unsigned gpio_field_offset = (offset - 32 * gpio_bank);
++//printk(KERN_ERR DRIVER_NAME ": bcm2708_gpio_set %p (%d=%d)\n", gc, offset, value);
++ if (offset >= BCM2708_NR_GPIOS)
++ return;
++ if (value)
++ writel(1 << gpio_field_offset, gpio->base + GPIOSET(gpio_bank));
++ else
++ writel(1 << gpio_field_offset, gpio->base + GPIOCLR(gpio_bank));
++}
++
++/**********************
++ * extension to configure pullups
++ */
++int bcm2708_gpio_setpull(struct gpio_chip *gc, unsigned offset,
++ bcm2708_gpio_pull_t value)
++{
++ struct bcm2708_gpio *gpio = container_of(gc, struct bcm2708_gpio, gc);
++ unsigned gpio_bank = offset / 32;
++ unsigned gpio_field_offset = (offset - 32 * gpio_bank);
++
++ if (offset >= BCM2708_NR_GPIOS)
++ return -EINVAL;
++
++ switch (value) {
++ case BCM2708_PULL_UP:
++ writel(2, gpio->base + GPIOUD(0));
++ break;
++ case BCM2708_PULL_DOWN:
++ writel(1, gpio->base + GPIOUD(0));
++ break;
++ case BCM2708_PULL_OFF:
++ writel(0, gpio->base + GPIOUD(0));
++ break;
++ }
++
++ udelay(5);
++ writel(1 << gpio_field_offset, gpio->base + GPIOUDCLK(gpio_bank));
++ udelay(5);
++ writel(0, gpio->base + GPIOUD(0));
++ writel(0 << gpio_field_offset, gpio->base + GPIOUDCLK(gpio_bank));
++
++ return 0;
++}
++EXPORT_SYMBOL(bcm2708_gpio_setpull);
++
++/*************************************************************************************************************************
++ * bcm2708 GPIO IRQ
++ */
++
++#if BCM_GPIO_USE_IRQ
++
++static int bcm2708_gpio_to_irq(struct gpio_chip *chip, unsigned gpio)
++{
++ return gpio_to_irq(gpio);
++}
++
++static int bcm2708_gpio_irq_set_type(struct irq_data *d, unsigned type)
++{
++ unsigned irq = d->irq;
++ struct bcm2708_gpio *gpio = irq_get_chip_data(irq);
++ unsigned gn = irq_to_gpio(irq);
++ unsigned gb = gn / 32;
++ unsigned go = gn % 32;
++
++ gpio->rising[gb] &= ~(1 << go);
++ gpio->falling[gb] &= ~(1 << go);
++ gpio->high[gb] &= ~(1 << go);
++ gpio->low[gb] &= ~(1 << go);
++
++ if (type & ~(IRQ_TYPE_EDGE_FALLING | IRQ_TYPE_EDGE_RISING | IRQ_TYPE_LEVEL_LOW | IRQ_TYPE_LEVEL_HIGH))
++ return -EINVAL;
++
++ if (type & IRQ_TYPE_EDGE_RISING)
++ gpio->rising[gb] |= (1 << go);
++ if (type & IRQ_TYPE_EDGE_FALLING)
++ gpio->falling[gb] |= (1 << go);
++ if (type & IRQ_TYPE_LEVEL_HIGH)
++ gpio->high[gb] |= (1 << go);
++ if (type & IRQ_TYPE_LEVEL_LOW)
++ gpio->low[gb] |= (1 << go);
++ return 0;
++}
++
++static void bcm2708_gpio_irq_mask(struct irq_data *d)
++{
++ unsigned irq = d->irq;
++ struct bcm2708_gpio *gpio = irq_get_chip_data(irq);
++ unsigned gn = irq_to_gpio(irq);
++ unsigned gb = gn / 32;
++ unsigned long rising = readl(gpio->base + GPIOREN(gb));
++ unsigned long falling = readl(gpio->base + GPIOFEN(gb));
++ unsigned long high = readl(gpio->base + GPIOHEN(gb));
++ unsigned long low = readl(gpio->base + GPIOLEN(gb));
++
++ gn = gn % 32;
++
++ writel(rising & ~(1 << gn), gpio->base + GPIOREN(gb));
++ writel(falling & ~(1 << gn), gpio->base + GPIOFEN(gb));
++ writel(high & ~(1 << gn), gpio->base + GPIOHEN(gb));
++ writel(low & ~(1 << gn), gpio->base + GPIOLEN(gb));
++}
++
++static void bcm2708_gpio_irq_unmask(struct irq_data *d)
++{
++ unsigned irq = d->irq;
++ struct bcm2708_gpio *gpio = irq_get_chip_data(irq);
++ unsigned gn = irq_to_gpio(irq);
++ unsigned gb = gn / 32;
++ unsigned go = gn % 32;
++ unsigned long rising = readl(gpio->base + GPIOREN(gb));
++ unsigned long falling = readl(gpio->base + GPIOFEN(gb));
++ unsigned long high = readl(gpio->base + GPIOHEN(gb));
++ unsigned long low = readl(gpio->base + GPIOLEN(gb));
++
++ if (gpio->rising[gb] & (1 << go)) {
++ writel(rising | (1 << go), gpio->base + GPIOREN(gb));
++ } else {
++ writel(rising & ~(1 << go), gpio->base + GPIOREN(gb));
++ }
++
++ if (gpio->falling[gb] & (1 << go)) {
++ writel(falling | (1 << go), gpio->base + GPIOFEN(gb));
++ } else {
++ writel(falling & ~(1 << go), gpio->base + GPIOFEN(gb));
++ }
++
++ if (gpio->high[gb] & (1 << go)) {
++ writel(high | (1 << go), gpio->base + GPIOHEN(gb));
++ } else {
++ writel(high & ~(1 << go), gpio->base + GPIOHEN(gb));
++ }
++
++ if (gpio->low[gb] & (1 << go)) {
++ writel(low | (1 << go), gpio->base + GPIOLEN(gb));
++ } else {
++ writel(low & ~(1 << go), gpio->base + GPIOLEN(gb));
++ }
++}
++
++static struct irq_chip bcm2708_irqchip = {
++ .name = "GPIO",
++ .irq_enable = bcm2708_gpio_irq_unmask,
++ .irq_disable = bcm2708_gpio_irq_mask,
++ .irq_unmask = bcm2708_gpio_irq_unmask,
++ .irq_mask = bcm2708_gpio_irq_mask,
++ .irq_set_type = bcm2708_gpio_irq_set_type,
++};
++
++static irqreturn_t bcm2708_gpio_interrupt(int irq, void *dev_id)
++{
++ unsigned long edsr;
++ unsigned bank;
++ int i;
++ unsigned gpio;
++ unsigned level_bits;
++ struct bcm2708_gpio *gpio_data = dev_id;
++
++ for (bank = 0; bank < GPIO_BANKS; bank++) {
++ edsr = readl(__io_address(GPIO_BASE) + GPIOEDS(bank));
++ level_bits = gpio_data->high[bank] | gpio_data->low[bank];
++
++ for_each_set_bit(i, &edsr, 32) {
++ gpio = i + bank * 32;
++ /* ack edge triggered IRQs immediately */
++ if (!(level_bits & (1<<i)))
++ writel(1<<i,
++ __io_address(GPIO_BASE) + GPIOEDS(bank));
++ generic_handle_irq(gpio_to_irq(gpio));
++ /* ack level triggered IRQ after handling them */
++ if (level_bits & (1<<i))
++ writel(1<<i,
++ __io_address(GPIO_BASE) + GPIOEDS(bank));
++ }
++ }
++ return IRQ_HANDLED;
++}
++
++static struct irqaction bcm2708_gpio_irq = {
++ .name = "BCM2708 GPIO catchall handler",
++ .flags = IRQF_DISABLED | IRQF_TIMER | IRQF_IRQPOLL,
++ .handler = bcm2708_gpio_interrupt,
++};
++
++static void bcm2708_gpio_irq_init(struct bcm2708_gpio *ucb)
++{
++ unsigned irq;
++
++ ucb->gc.to_irq = bcm2708_gpio_to_irq;
++
++ for (irq = GPIO_IRQ_START; irq < (GPIO_IRQ_START + GPIO_IRQS); irq++) {
++ irq_set_chip_data(irq, ucb);
++ irq_set_chip_and_handler(irq, &bcm2708_irqchip,
++ handle_simple_irq);
++ set_irq_flags(irq, IRQF_VALID);
++ }
++
++ bcm2708_gpio_irq.dev_id = ucb;
++ setup_irq(IRQ_GPIO3, &bcm2708_gpio_irq);
++}
++
++#else
++
++static void bcm2708_gpio_irq_init(struct bcm2708_gpio *ucb)
++{
++}
++
++#endif /* #if BCM_GPIO_USE_IRQ ***************************************************************************************************************** */
++
++static int bcm2708_gpio_probe(struct platform_device *dev)
++{
++ struct bcm2708_gpio *ucb;
++ struct resource *res;
++ int bank;
++ int err = 0;
++
++ printk(KERN_INFO DRIVER_NAME ": bcm2708_gpio_probe %p\n", dev);
++
++ ucb = kzalloc(sizeof(*ucb), GFP_KERNEL);
++ if (NULL == ucb) {
++ printk(KERN_ERR DRIVER_NAME ": failed to allocate "
++ "mailbox memory\n");
++ err = -ENOMEM;
++ goto err;
++ }
++
++ res = platform_get_resource(dev, IORESOURCE_MEM, 0);
++
++ platform_set_drvdata(dev, ucb);
++ ucb->base = __io_address(GPIO_BASE);
++
++ ucb->gc.label = "bcm2708_gpio";
++ ucb->gc.base = 0;
++ ucb->gc.ngpio = BCM2708_NR_GPIOS;
++ ucb->gc.owner = THIS_MODULE;
++
++ ucb->gc.direction_input = bcm2708_gpio_dir_in;
++ ucb->gc.direction_output = bcm2708_gpio_dir_out;
++ ucb->gc.get = bcm2708_gpio_get;
++ ucb->gc.set = bcm2708_gpio_set;
++ ucb->gc.can_sleep = 0;
++
++ for (bank = 0; bank < GPIO_BANKS; bank++) {
++ writel(0, ucb->base + GPIOREN(bank));
++ writel(0, ucb->base + GPIOFEN(bank));
++ writel(0, ucb->base + GPIOHEN(bank));
++ writel(0, ucb->base + GPIOLEN(bank));
++ writel(0, ucb->base + GPIOAREN(bank));
++ writel(0, ucb->base + GPIOAFEN(bank));
++ writel(~0, ucb->base + GPIOEDS(bank));
++ }
++
++ bcm2708_gpio_irq_init(ucb);
++
++ err = gpiochip_add(&ucb->gc);
++
++err:
++ return err;
++
++}
++
++static int bcm2708_gpio_remove(struct platform_device *dev)
++{
++ int err = 0;
++ struct bcm2708_gpio *ucb = platform_get_drvdata(dev);
++
++ printk(KERN_ERR DRIVER_NAME ": bcm2708_gpio_remove %p\n", dev);
++
++ gpiochip_remove(&ucb->gc);
++
++ platform_set_drvdata(dev, NULL);
++ kfree(ucb);
++
++ return err;
++}
++
++static struct platform_driver bcm2708_gpio_driver = {
++ .probe = bcm2708_gpio_probe,
++ .remove = bcm2708_gpio_remove,
++ .driver = {
++ .name = "bcm2708_gpio"},
++};
++
++static int __init bcm2708_gpio_init(void)
++{
++ return platform_driver_register(&bcm2708_gpio_driver);
++}
++
++static void __exit bcm2708_gpio_exit(void)
++{
++ platform_driver_unregister(&bcm2708_gpio_driver);
++}
++
++module_init(bcm2708_gpio_init);
++module_exit(bcm2708_gpio_exit);
++
++MODULE_DESCRIPTION("Broadcom BCM2708 GPIO driver");
++MODULE_LICENSE("GPL");
+diff --git a/arch/arm/mach-bcm2708/include/mach/gpio.h b/arch/arm/mach-bcm2708/include/mach/gpio.h
+new file mode 100644
+index 0000000..7965a97
+--- /dev/null
++++ b/arch/arm/mach-bcm2708/include/mach/gpio.h
+@@ -0,0 +1,17 @@
++/*
++ * arch/arm/mach-bcm2708/include/mach/gpio.h
++ *
++ * This file is licensed under the terms of the GNU General Public
++ * License version 2. This program is licensed "as is" without any
++ * warranty of any kind, whether express or implied.
++ */
++
++#ifndef __ASM_ARCH_GPIO_H
++#define __ASM_ARCH_GPIO_H
++
++#define BCM2708_NR_GPIOS 54 // number of gpio lines
++
++#define gpio_to_irq(x) ((x) + GPIO_IRQ_START)
++#define irq_to_gpio(x) ((x) - GPIO_IRQ_START)
++
++#endif
+diff --git a/include/linux/platform_data/bcm2708.h b/include/linux/platform_data/bcm2708.h
+new file mode 100644
+index 0000000..fb69624
+--- /dev/null
++++ b/include/linux/platform_data/bcm2708.h
+@@ -0,0 +1,23 @@
++/*
++ * include/linux/platform_data/bcm2708.h
++ *
++ * 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.
++ *
++ * (C) 2014 Julian Scheel <julian@jusst.de>
++ *
++ */
++#ifndef __BCM2708_H_
++#define __BCM2708_H_
++
++typedef enum {
++ BCM2708_PULL_OFF,
++ BCM2708_PULL_UP,
++ BCM2708_PULL_DOWN
++} bcm2708_gpio_pull_t;
++
++extern int bcm2708_gpio_setpull(struct gpio_chip *gc, unsigned offset,
++ bcm2708_gpio_pull_t value);
++
++#endif
+--
+1.8.3.2
+