aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/gpio
diff options
context:
space:
mode:
authorAlexander Shiyan <shc_work@mail.ru>2014-03-11 13:55:14 -0400
committerLinus Walleij <linus.walleij@linaro.org>2014-03-18 04:28:31 -0400
commit6a8a0c1d87377c6ce97de2fedc0762c2fa8233ac (patch)
treef587b88cc527e7902dbf07bbc5b21310cce1ef32 /drivers/gpio
parent193385305b71b329b8fbec2c0465d4afd3f946b8 (diff)
gpio: Driver for SYSCON-based GPIOs
SYSCON driver was designed for using memory areas (registers) that are used in several subsystems. There are systems (CPUs) which use bits in one register for various purposes and thus should be handled by various kernel subsystems. This driver allows you to use the individual SYSCON bits as GPIOs. ARM CLPS711X SYSFLG1 input lines has been added as first user of this driver. Signed-off-by: Alexander Shiyan <shc_work@mail.ru> Signed-off-by: Linus Walleij <linus.walleij@linaro.org>
Diffstat (limited to 'drivers/gpio')
-rw-r--r--drivers/gpio/Kconfig6
-rw-r--r--drivers/gpio/Makefile1
-rw-r--r--drivers/gpio/gpio-syscon.c191
3 files changed, 198 insertions, 0 deletions
diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig
index 8ca94e10aed5..2e461e459d88 100644
--- a/drivers/gpio/Kconfig
+++ b/drivers/gpio/Kconfig
@@ -290,6 +290,12 @@ config GPIO_STA2X11
290 Say yes here to support the STA2x11/ConneXt GPIO device. 290 Say yes here to support the STA2x11/ConneXt GPIO device.
291 The GPIO module has 128 GPIO pins with alternate functions. 291 The GPIO module has 128 GPIO pins with alternate functions.
292 292
293config GPIO_SYSCON
294 tristate "GPIO based on SYSCON"
295 depends on MFD_SYSCON && OF
296 help
297 Say yes here to support GPIO functionality though SYSCON driver.
298
293config GPIO_TS5500 299config GPIO_TS5500
294 tristate "TS-5500 DIO blocks and compatibles" 300 tristate "TS-5500 DIO blocks and compatibles"
295 depends on TS5500 || COMPILE_TEST 301 depends on TS5500 || COMPILE_TEST
diff --git a/drivers/gpio/Makefile b/drivers/gpio/Makefile
index aaff7ff9a8f7..6309aff1d806 100644
--- a/drivers/gpio/Makefile
+++ b/drivers/gpio/Makefile
@@ -77,6 +77,7 @@ obj-$(CONFIG_GPIO_STA2X11) += gpio-sta2x11.o
77obj-$(CONFIG_GPIO_STMPE) += gpio-stmpe.o 77obj-$(CONFIG_GPIO_STMPE) += gpio-stmpe.o
78obj-$(CONFIG_GPIO_STP_XWAY) += gpio-stp-xway.o 78obj-$(CONFIG_GPIO_STP_XWAY) += gpio-stp-xway.o
79obj-$(CONFIG_GPIO_SX150X) += gpio-sx150x.o 79obj-$(CONFIG_GPIO_SX150X) += gpio-sx150x.o
80obj-$(CONFIG_GPIO_SYSCON) += gpio-syscon.o
80obj-$(CONFIG_GPIO_TB10X) += gpio-tb10x.o 81obj-$(CONFIG_GPIO_TB10X) += gpio-tb10x.o
81obj-$(CONFIG_GPIO_TC3589X) += gpio-tc3589x.o 82obj-$(CONFIG_GPIO_TC3589X) += gpio-tc3589x.o
82obj-$(CONFIG_ARCH_TEGRA) += gpio-tegra.o 83obj-$(CONFIG_ARCH_TEGRA) += gpio-tegra.o
diff --git a/drivers/gpio/gpio-syscon.c b/drivers/gpio/gpio-syscon.c
new file mode 100644
index 000000000000..b50fe1297748
--- /dev/null
+++ b/drivers/gpio/gpio-syscon.c
@@ -0,0 +1,191 @@
1/*
2 * SYSCON GPIO driver
3 *
4 * Copyright (C) 2014 Alexander Shiyan <shc_work@mail.ru>
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
10 */
11
12#include <linux/err.h>
13#include <linux/gpio.h>
14#include <linux/module.h>
15#include <linux/of.h>
16#include <linux/of_device.h>
17#include <linux/platform_device.h>
18#include <linux/regmap.h>
19#include <linux/mfd/syscon.h>
20
21#define GPIO_SYSCON_FEAT_IN BIT(0)
22#define GPIO_SYSCON_FEAT_OUT BIT(1)
23#define GPIO_SYSCON_FEAT_DIR BIT(2)
24
25/* SYSCON driver is designed to use 32-bit wide registers */
26#define SYSCON_REG_SIZE (4)
27#define SYSCON_REG_BITS (SYSCON_REG_SIZE * 8)
28
29/**
30 * struct syscon_gpio_data - Configuration for the device.
31 * compatible: SYSCON driver compatible string.
32 * flags: Set of GPIO_SYSCON_FEAT_ flags:
33 * GPIO_SYSCON_FEAT_IN: GPIOs supports input,
34 * GPIO_SYSCON_FEAT_OUT: GPIOs supports output,
35 * GPIO_SYSCON_FEAT_DIR: GPIOs supports switch direction.
36 * bit_count: Number of bits used as GPIOs.
37 * dat_bit_offset: Offset (in bits) to the first GPIO bit.
38 * dir_bit_offset: Optional offset (in bits) to the first bit to switch
39 * GPIO direction (Used with GPIO_SYSCON_FEAT_DIR flag).
40 */
41
42struct syscon_gpio_data {
43 const char *compatible;
44 unsigned int flags;
45 unsigned int bit_count;
46 unsigned int dat_bit_offset;
47 unsigned int dir_bit_offset;
48};
49
50struct syscon_gpio_priv {
51 struct gpio_chip chip;
52 struct regmap *syscon;
53 const struct syscon_gpio_data *data;
54};
55
56static inline struct syscon_gpio_priv *to_syscon_gpio(struct gpio_chip *chip)
57{
58 return container_of(chip, struct syscon_gpio_priv, chip);
59}
60
61static int syscon_gpio_get(struct gpio_chip *chip, unsigned offset)
62{
63 struct syscon_gpio_priv *priv = to_syscon_gpio(chip);
64 unsigned int val, offs = priv->data->dat_bit_offset + offset;
65 int ret;
66
67 ret = regmap_read(priv->syscon,
68 (offs / SYSCON_REG_BITS) * SYSCON_REG_SIZE, &val);
69 if (ret)
70 return ret;
71
72 return !!(val & BIT(offs % SYSCON_REG_BITS));
73}
74
75static void syscon_gpio_set(struct gpio_chip *chip, unsigned offset, int val)
76{
77 struct syscon_gpio_priv *priv = to_syscon_gpio(chip);
78 unsigned int offs = priv->data->dat_bit_offset + offset;
79
80 regmap_update_bits(priv->syscon,
81 (offs / SYSCON_REG_BITS) * SYSCON_REG_SIZE,
82 BIT(offs % SYSCON_REG_BITS),
83 val ? BIT(offs % SYSCON_REG_BITS) : 0);
84}
85
86static int syscon_gpio_dir_in(struct gpio_chip *chip, unsigned offset)
87{
88 struct syscon_gpio_priv *priv = to_syscon_gpio(chip);
89
90 if (priv->data->flags & GPIO_SYSCON_FEAT_DIR) {
91 unsigned int offs = priv->data->dir_bit_offset + offset;
92
93 regmap_update_bits(priv->syscon,
94 (offs / SYSCON_REG_BITS) * SYSCON_REG_SIZE,
95 BIT(offs % SYSCON_REG_BITS), 0);
96 }
97
98 return 0;
99}
100
101static int syscon_gpio_dir_out(struct gpio_chip *chip, unsigned offset, int val)
102{
103 struct syscon_gpio_priv *priv = to_syscon_gpio(chip);
104
105 if (priv->data->flags & GPIO_SYSCON_FEAT_DIR) {
106 unsigned int offs = priv->data->dir_bit_offset + offset;
107
108 regmap_update_bits(priv->syscon,
109 (offs / SYSCON_REG_BITS) * SYSCON_REG_SIZE,
110 BIT(offs % SYSCON_REG_BITS),
111 BIT(offs % SYSCON_REG_BITS));
112 }
113
114 syscon_gpio_set(chip, offset, val);
115
116 return 0;
117}
118
119static const struct syscon_gpio_data clps711x_mctrl_gpio = {
120 /* ARM CLPS711X SYSFLG1 Bits 8-10 */
121 .compatible = "cirrus,clps711x-syscon1",
122 .flags = GPIO_SYSCON_FEAT_IN,
123 .bit_count = 3,
124 .dat_bit_offset = 0x40 * 8 + 8,
125};
126
127static const struct of_device_id syscon_gpio_ids[] = {
128 {
129 .compatible = "cirrus,clps711x-mctrl-gpio",
130 .data = &clps711x_mctrl_gpio,
131 },
132 { }
133};
134MODULE_DEVICE_TABLE(of, syscon_gpio_ids);
135
136static int syscon_gpio_probe(struct platform_device *pdev)
137{
138 struct device *dev = &pdev->dev;
139 const struct of_device_id *of_id = of_match_device(syscon_gpio_ids, dev);
140 struct syscon_gpio_priv *priv;
141
142 priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
143 if (!priv)
144 return -ENOMEM;
145
146 priv->data = of_id->data;
147
148 priv->syscon =
149 syscon_regmap_lookup_by_compatible(priv->data->compatible);
150 if (IS_ERR(priv->syscon))
151 return PTR_ERR(priv->syscon);
152
153 priv->chip.dev = dev;
154 priv->chip.owner = THIS_MODULE;
155 priv->chip.label = dev_name(dev);
156 priv->chip.base = -1;
157 priv->chip.ngpio = priv->data->bit_count;
158 priv->chip.get = syscon_gpio_get;
159 if (priv->data->flags & GPIO_SYSCON_FEAT_IN)
160 priv->chip.direction_input = syscon_gpio_dir_in;
161 if (priv->data->flags & GPIO_SYSCON_FEAT_OUT) {
162 priv->chip.set = syscon_gpio_set;
163 priv->chip.direction_output = syscon_gpio_dir_out;
164 }
165
166 platform_set_drvdata(pdev, priv);
167
168 return gpiochip_add(&priv->chip);
169}
170
171static int syscon_gpio_remove(struct platform_device *pdev)
172{
173 struct syscon_gpio_priv *priv = platform_get_drvdata(pdev);
174
175 return gpiochip_remove(&priv->chip);
176}
177
178static struct platform_driver syscon_gpio_driver = {
179 .driver = {
180 .name = "gpio-syscon",
181 .owner = THIS_MODULE,
182 .of_match_table = syscon_gpio_ids,
183 },
184 .probe = syscon_gpio_probe,
185 .remove = syscon_gpio_remove,
186};
187module_platform_driver(syscon_gpio_driver);
188
189MODULE_AUTHOR("Alexander Shiyan <shc_work@mail.ru>");
190MODULE_DESCRIPTION("SYSCON GPIO driver");
191MODULE_LICENSE("GPL");