diff options
author | Lucile Quirion <lucile.quirion@savoirfairelinux.com> | 2016-08-12 11:16:49 -0400 |
---|---|---|
committer | Linus Walleij <linus.walleij@linaro.org> | 2016-08-19 05:04:53 -0400 |
commit | 9c6686322d749814e3e7af492954ca1777444023 (patch) | |
tree | b4c28621807f75838354d867cb2d220d6aa8b6d2 /drivers/gpio/gpio-ts4900.c | |
parent | 4cebaf17ecde6d3f78513932ab239af922750e09 (diff) |
gpio: add Technologic I2C-FPGA gpio support
This driver is generic and aims to support all Technologic Systems's
boards embedding FPGA GPIOs with an I2C interface.
This driver supports TS-4900, TS-7970, TS-7990 and TS-4100 series.
Signed-off-by: Lucile Quirion <lucile.quirion@savoirfairelinux.com>
Signed-off-by: Linus Walleij <linus.walleij@linaro.org>
Diffstat (limited to 'drivers/gpio/gpio-ts4900.c')
-rw-r--r-- | drivers/gpio/gpio-ts4900.c | 190 |
1 files changed, 190 insertions, 0 deletions
diff --git a/drivers/gpio/gpio-ts4900.c b/drivers/gpio/gpio-ts4900.c new file mode 100644 index 000000000000..9dd9aca820e7 --- /dev/null +++ b/drivers/gpio/gpio-ts4900.c | |||
@@ -0,0 +1,190 @@ | |||
1 | /* | ||
2 | * Digital I/O driver for Technologic Systems I2C FPGA Core | ||
3 | * | ||
4 | * Copyright (C) 2015 Technologic Systems | ||
5 | * Copyright (C) 2016 Savoir-Faire Linux | ||
6 | * | ||
7 | * This program is free software; you can redistribute it and/or | ||
8 | * modify it under the terms of the GNU General Public License version 2 as | ||
9 | * published by the Free Software Foundation. | ||
10 | * | ||
11 | * This program is distributed "as is" WITHOUT ANY WARRANTY of any | ||
12 | * kind, whether expressed or implied; without even the implied warranty | ||
13 | * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
14 | * GNU General Public License version 2 for more details. | ||
15 | */ | ||
16 | |||
17 | #include <linux/gpio/driver.h> | ||
18 | #include <linux/i2c.h> | ||
19 | #include <linux/of_device.h> | ||
20 | #include <linux/module.h> | ||
21 | #include <linux/regmap.h> | ||
22 | |||
23 | #define DEFAULT_PIN_NUMBER 32 | ||
24 | /* | ||
25 | * Register bits used by the GPIO device | ||
26 | * Some boards, such as TS-7970 do not have a separate input bit | ||
27 | */ | ||
28 | #define TS4900_GPIO_OE 0x01 | ||
29 | #define TS4900_GPIO_OUT 0x02 | ||
30 | #define TS4900_GPIO_IN 0x04 | ||
31 | #define TS7970_GPIO_IN 0x02 | ||
32 | |||
33 | struct ts4900_gpio_priv { | ||
34 | struct regmap *regmap; | ||
35 | struct gpio_chip gpio_chip; | ||
36 | unsigned int input_bit; | ||
37 | }; | ||
38 | |||
39 | static int ts4900_gpio_get_direction(struct gpio_chip *chip, | ||
40 | unsigned int offset) | ||
41 | { | ||
42 | struct ts4900_gpio_priv *priv = gpiochip_get_data(chip); | ||
43 | unsigned int reg; | ||
44 | |||
45 | regmap_read(priv->regmap, offset, ®); | ||
46 | |||
47 | return !(reg & TS4900_GPIO_OE); | ||
48 | } | ||
49 | |||
50 | static int ts4900_gpio_direction_input(struct gpio_chip *chip, | ||
51 | unsigned int offset) | ||
52 | { | ||
53 | struct ts4900_gpio_priv *priv = gpiochip_get_data(chip); | ||
54 | |||
55 | /* | ||
56 | * This will clear the output enable bit, the other bits are | ||
57 | * dontcare when this is cleared | ||
58 | */ | ||
59 | return regmap_write(priv->regmap, offset, 0); | ||
60 | } | ||
61 | |||
62 | static int ts4900_gpio_direction_output(struct gpio_chip *chip, | ||
63 | unsigned int offset, int value) | ||
64 | { | ||
65 | struct ts4900_gpio_priv *priv = gpiochip_get_data(chip); | ||
66 | int ret; | ||
67 | |||
68 | if (value) | ||
69 | ret = regmap_write(priv->regmap, offset, TS4900_GPIO_OE | | ||
70 | TS4900_GPIO_OUT); | ||
71 | else | ||
72 | ret = regmap_write(priv->regmap, offset, TS4900_GPIO_OE); | ||
73 | |||
74 | return ret; | ||
75 | } | ||
76 | |||
77 | static int ts4900_gpio_get(struct gpio_chip *chip, unsigned int offset) | ||
78 | { | ||
79 | struct ts4900_gpio_priv *priv = gpiochip_get_data(chip); | ||
80 | unsigned int reg; | ||
81 | |||
82 | regmap_read(priv->regmap, offset, ®); | ||
83 | |||
84 | return !!(reg & priv->input_bit); | ||
85 | } | ||
86 | |||
87 | static void ts4900_gpio_set(struct gpio_chip *chip, unsigned int offset, | ||
88 | int value) | ||
89 | { | ||
90 | struct ts4900_gpio_priv *priv = gpiochip_get_data(chip); | ||
91 | |||
92 | if (value) | ||
93 | regmap_update_bits(priv->regmap, offset, TS4900_GPIO_OUT, | ||
94 | TS4900_GPIO_OUT); | ||
95 | else | ||
96 | regmap_update_bits(priv->regmap, offset, TS4900_GPIO_OUT, 0); | ||
97 | } | ||
98 | |||
99 | static const struct regmap_config ts4900_regmap_config = { | ||
100 | .reg_bits = 16, | ||
101 | .val_bits = 8, | ||
102 | }; | ||
103 | |||
104 | static struct gpio_chip template_chip = { | ||
105 | .label = "ts4900-gpio", | ||
106 | .owner = THIS_MODULE, | ||
107 | .get_direction = ts4900_gpio_get_direction, | ||
108 | .direction_input = ts4900_gpio_direction_input, | ||
109 | .direction_output = ts4900_gpio_direction_output, | ||
110 | .get = ts4900_gpio_get, | ||
111 | .set = ts4900_gpio_set, | ||
112 | .base = -1, | ||
113 | .can_sleep = true, | ||
114 | }; | ||
115 | |||
116 | static const struct of_device_id ts4900_gpio_of_match_table[] = { | ||
117 | { | ||
118 | .compatible = "technologic,ts4900-gpio", | ||
119 | .data = (void *)TS4900_GPIO_IN, | ||
120 | }, { | ||
121 | .compatible = "technologic,ts7970-gpio", | ||
122 | .data = (void *)TS7970_GPIO_IN, | ||
123 | }, | ||
124 | { /* sentinel */ }, | ||
125 | }; | ||
126 | MODULE_DEVICE_TABLE(of, ts4900_gpio_of_match_table); | ||
127 | |||
128 | static int ts4900_gpio_probe(struct i2c_client *client, | ||
129 | const struct i2c_device_id *id) | ||
130 | { | ||
131 | const struct of_device_id *match; | ||
132 | struct ts4900_gpio_priv *priv; | ||
133 | u32 ngpio; | ||
134 | int ret; | ||
135 | |||
136 | match = of_match_device(ts4900_gpio_of_match_table, &client->dev); | ||
137 | if (!match) | ||
138 | return -EINVAL; | ||
139 | |||
140 | if (of_property_read_u32(client->dev.of_node, "ngpios", &ngpio)) | ||
141 | ngpio = DEFAULT_PIN_NUMBER; | ||
142 | |||
143 | priv = devm_kzalloc(&client->dev, sizeof(*priv), GFP_KERNEL); | ||
144 | if (!priv) | ||
145 | return -ENOMEM; | ||
146 | |||
147 | priv->gpio_chip = template_chip; | ||
148 | priv->gpio_chip.label = "ts4900-gpio"; | ||
149 | priv->gpio_chip.ngpio = ngpio; | ||
150 | priv->gpio_chip.parent = &client->dev; | ||
151 | priv->input_bit = (uintptr_t)match->data; | ||
152 | |||
153 | priv->regmap = devm_regmap_init_i2c(client, &ts4900_regmap_config); | ||
154 | if (IS_ERR(priv->regmap)) { | ||
155 | ret = PTR_ERR(priv->regmap); | ||
156 | dev_err(&client->dev, "Failed to allocate register map: %d\n", | ||
157 | ret); | ||
158 | return ret; | ||
159 | } | ||
160 | |||
161 | ret = devm_gpiochip_add_data(&client->dev, &priv->gpio_chip, priv); | ||
162 | if (ret < 0) { | ||
163 | dev_err(&client->dev, "Unable to register gpiochip\n"); | ||
164 | return ret; | ||
165 | } | ||
166 | |||
167 | i2c_set_clientdata(client, priv); | ||
168 | |||
169 | return 0; | ||
170 | } | ||
171 | |||
172 | static const struct i2c_device_id ts4900_gpio_id_table[] = { | ||
173 | { "ts4900-gpio", }, | ||
174 | { /* sentinel */ } | ||
175 | }; | ||
176 | MODULE_DEVICE_TABLE(i2c, ts4900_gpio_id_table); | ||
177 | |||
178 | static struct i2c_driver ts4900_gpio_driver = { | ||
179 | .driver = { | ||
180 | .name = "ts4900-gpio", | ||
181 | .of_match_table = ts4900_gpio_of_match_table, | ||
182 | }, | ||
183 | .probe = ts4900_gpio_probe, | ||
184 | .id_table = ts4900_gpio_id_table, | ||
185 | }; | ||
186 | module_i2c_driver(ts4900_gpio_driver); | ||
187 | |||
188 | MODULE_AUTHOR("Technologic Systems"); | ||
189 | MODULE_DESCRIPTION("GPIO interface for Technologic Systems I2C-FPGA core"); | ||
190 | MODULE_LICENSE("GPL"); | ||