diff options
author | Sascha Hauer <s.hauer@pengutronix.de> | 2013-05-06 09:30:53 +0200 |
---|---|---|
committer | Sascha Hauer <s.hauer@pengutronix.de> | 2013-05-06 09:30:53 +0200 |
commit | 740f01feb7a86161362d05dbc0f9b5cf8c4159b5 (patch) | |
tree | 165d73b6d9403f04b2ede1914b9f606d77845fc1 /drivers/gpio | |
parent | e9a39d79b9c57dfe0e5736de9ed192cc6273a56b (diff) | |
parent | fbcb481c60d80e76f3c5d537ae05d0adf01ac9e1 (diff) | |
download | barebox-740f01feb7a86161362d05dbc0f9b5cf8c4159b5.tar.gz barebox-740f01feb7a86161362d05dbc0f9b5cf8c4159b5.tar.xz |
Merge branch 'for-next/tegra'
Diffstat (limited to 'drivers/gpio')
-rw-r--r-- | drivers/gpio/Kconfig | 7 | ||||
-rw-r--r-- | drivers/gpio/Makefile | 1 | ||||
-rw-r--r-- | drivers/gpio/gpio-tegra.c | 214 |
3 files changed, 222 insertions, 0 deletions
diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig index 74a4baa788..e976db4831 100644 --- a/drivers/gpio/Kconfig +++ b/drivers/gpio/Kconfig @@ -39,6 +39,13 @@ config GPIO_PL061 config GPIO_STMPE depends on MFD_STMPE bool "STMPE GPIO Expander" + +config GPIO_TEGRA + bool "GPIO support for the Tegra SoCs" + depends on ARCH_TEGRA + help + Say yes here to include the driver for the GPIO controller found on the + Tegra line of SoCs. endmenu endif diff --git a/drivers/gpio/Makefile b/drivers/gpio/Makefile index 205d5531bb..c1ec5bf772 100644 --- a/drivers/gpio/Makefile +++ b/drivers/gpio/Makefile @@ -6,3 +6,4 @@ obj-$(CONFIG_GPIO_GENERIC) += gpio-generic.o obj-$(CONFIG_GPIO_IMX) += gpio-imx.o obj-$(CONFIG_GPIO_PL061) += gpio-pl061.o obj-$(CONFIG_GPIO_STMPE) += gpio-stmpe.o +obj-$(CONFIG_GPIO_TEGRA) += gpio-tegra.o diff --git a/drivers/gpio/gpio-tegra.c b/drivers/gpio/gpio-tegra.c new file mode 100644 index 0000000000..1e00f5ecc6 --- /dev/null +++ b/drivers/gpio/gpio-tegra.c @@ -0,0 +1,214 @@ +/* * + * Copyright (C) 2010 Erik Gilling <konkers@google.com>, Google, Inc + * Copyright (C) 2013 Lucas Stach <l.stach@pengutronix.de> + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#include <common.h> +#include <gpio.h> +#include <init.h> +#include <io.h> +#include <linux/err.h> + +#define GPIO_BANK(x) ((x) >> 5) +#define GPIO_PORT(x) (((x) >> 3) & 0x3) +#define GPIO_BIT(x) ((x) & 0x7) + +#define GPIO_REG(x) (GPIO_BANK(x) * config->bank_stride + \ + GPIO_PORT(x) * 4) + +#define GPIO_CNF(x) (GPIO_REG(x) + 0x00) +#define GPIO_OE(x) (GPIO_REG(x) + 0x10) +#define GPIO_OUT(x) (GPIO_REG(x) + 0x20) +#define GPIO_IN(x) (GPIO_REG(x) + 0x30) +#define GPIO_INT_ENB(x) (GPIO_REG(x) + 0x50) + +#define GPIO_MSK_CNF(x) (GPIO_REG(x) + config->upper_offset + 0x00) +#define GPIO_MSK_OE(x) (GPIO_REG(x) + config->upper_offset + 0x10) +#define GPIO_MSK_OUT(x) (GPIO_REG(x) + config->upper_offset + 0X20) + +struct tegra_gpio_bank { + int bank; + int irq; +}; + +struct tegra_gpio_soc_config { + u32 bank_stride; + u32 upper_offset; + u32 bank_count; +}; + +static void __iomem *gpio_base; +static struct tegra_gpio_soc_config *config; + +static inline void tegra_gpio_writel(u32 val, u32 reg) +{ + writel(val, gpio_base + reg); +} + +static inline u32 tegra_gpio_readl(u32 reg) +{ + return readl(gpio_base + reg); +} + +static int tegra_gpio_compose(int bank, int port, int bit) +{ + return (bank << 5) | ((port & 0x3) << 3) | (bit & 0x7); +} + +static void tegra_gpio_mask_write(u32 reg, int gpio, int value) +{ + u32 val; + + val = 0x100 << GPIO_BIT(gpio); + if (value) + val |= 1 << GPIO_BIT(gpio); + tegra_gpio_writel(val, reg); +} + +static void tegra_gpio_enable(int gpio) +{ + tegra_gpio_mask_write(GPIO_MSK_CNF(gpio), gpio, 1); +} + +static void tegra_gpio_disable(int gpio) +{ + tegra_gpio_mask_write(GPIO_MSK_CNF(gpio), gpio, 0); +} + +static int tegra_gpio_request(struct gpio_chip *chip, unsigned offset) +{ + return 0; +} + +static void tegra_gpio_free(struct gpio_chip *chip, unsigned offset) +{ + tegra_gpio_disable(offset); +} + +static void tegra_gpio_set(struct gpio_chip *chip, unsigned offset, int value) +{ + tegra_gpio_mask_write(GPIO_MSK_OUT(offset), offset, value); +} + +static int tegra_gpio_get(struct gpio_chip *chip, unsigned offset) +{ + /* If gpio is in output mode then read from the out value */ + if ((tegra_gpio_readl(GPIO_OE(offset)) >> GPIO_BIT(offset)) & 1) { + printf("GPIO output mode\n"); + return (tegra_gpio_readl(GPIO_OUT(offset)) >> + GPIO_BIT(offset)) & 0x1; + } + + printf("GPIO input mode\n"); + return (tegra_gpio_readl(GPIO_IN(offset)) >> GPIO_BIT(offset)) & 0x1; +} + +static int tegra_gpio_direction_input(struct gpio_chip *chip, unsigned offset) +{ + tegra_gpio_mask_write(GPIO_MSK_OE(offset), offset, 0); + tegra_gpio_enable(offset); + return 0; +} + +static int tegra_gpio_direction_output(struct gpio_chip *chip, unsigned offset, + int value) +{ + tegra_gpio_set(chip, offset, value); + tegra_gpio_mask_write(GPIO_MSK_OE(offset), offset, 1); + tegra_gpio_enable(offset); + return 0; +} + +static struct gpio_ops tegra_gpio_ops = { + .request = tegra_gpio_request, + .free = tegra_gpio_free, + .direction_input = tegra_gpio_direction_input, + .direction_output = tegra_gpio_direction_output, + .get = tegra_gpio_get, + .set = tegra_gpio_set, +}; + +static struct gpio_chip tegra_gpio_chip = { + .ops = &tegra_gpio_ops, + .base = 0, +}; + +static int tegra_gpio_probe(struct device_d *dev) +{ + int i, j, ret; + + ret = dev_get_drvdata(dev, (unsigned long *)&config); + if (ret) { + dev_err(dev, "dev_get_drvdata failed: %d\n", ret); + return ret; + } + + gpio_base = dev_request_mem_region(dev, 0); + if (!gpio_base) { + dev_err(dev, "could not get memory region\n"); + return -ENODEV; + } + + for (i = 0; i < config->bank_count; i++) { + for (j = 0; j < 4; j++) { + int gpio = tegra_gpio_compose(i, j, 0); + tegra_gpio_writel(0x00, GPIO_INT_ENB(gpio)); + } + } + + tegra_gpio_chip.ngpio = config->bank_count * 32; + tegra_gpio_chip.dev = dev; + + gpiochip_add(&tegra_gpio_chip); + + return 0; +} + +static struct tegra_gpio_soc_config tegra20_gpio_config = { + .bank_stride = 0x80, + .upper_offset = 0x800, + .bank_count = 7, +}; + +static struct platform_device_id tegra_gpio_ids[] = { + { + .name = "tegra20-gpio", + .driver_data = (unsigned long)&tegra20_gpio_config, + }, { + /* sentinel */ + }, +}; + +static __maybe_unused struct of_device_id tegra_gpio_dt_ids[] = { + { + .compatible = "nvidia,tegra20-gpio", + .data = (unsigned long)&tegra20_gpio_config + }, { + /* sentinel */ + }, +}; + +static struct driver_d tegra_gpio_driver = { + .name = "tegra-gpio", + .id_table = tegra_gpio_ids, + .of_compatible = DRV_OF_COMPAT(tegra_gpio_dt_ids), + .probe = tegra_gpio_probe, +}; + +static int __init tegra_gpio_init(void) +{ + return platform_driver_register(&tegra_gpio_driver); +} +coredevice_initcall(tegra_gpio_init); |