Commit | Line | Data |
---|---|---|
02c5ba1e LD |
1 | /* |
2 | * MAXIM MAX77620 GPIO driver | |
3 | * | |
4 | * Copyright (c) 2016, NVIDIA CORPORATION. All rights reserved. | |
5 | * | |
6 | * This program is free software; you can redistribute it and/or modify it | |
7 | * under the terms and conditions of the GNU General Public License, | |
8 | * version 2, as published by the Free Software Foundation. | |
9 | */ | |
10 | ||
11 | #include <linux/gpio/driver.h> | |
12 | #include <linux/interrupt.h> | |
13 | #include <linux/mfd/max77620.h> | |
14 | #include <linux/module.h> | |
15 | #include <linux/platform_device.h> | |
16 | #include <linux/regmap.h> | |
17 | ||
18 | #define GPIO_REG_ADDR(offset) (MAX77620_REG_GPIO0 + offset) | |
19 | ||
20 | struct max77620_gpio { | |
21 | struct gpio_chip gpio_chip; | |
22 | struct regmap *rmap; | |
23 | struct device *dev; | |
02c5ba1e LD |
24 | }; |
25 | ||
26 | static const struct regmap_irq max77620_gpio_irqs[] = { | |
ff93ec74 | 27 | [0] = { |
ff93ec74 | 28 | .reg_offset = 0, |
1c2928e3 MV |
29 | .mask = MAX77620_IRQ_LVL2_GPIO_EDGE0, |
30 | .type = { | |
31 | .type_rising_val = MAX77620_CNFG_GPIO_INT_RISING, | |
32 | .type_falling_val = MAX77620_CNFG_GPIO_INT_FALLING, | |
33 | .type_reg_mask = MAX77620_CNFG_GPIO_INT_MASK, | |
34 | .type_reg_offset = 0, | |
35 | .types_supported = IRQ_TYPE_EDGE_BOTH, | |
36 | }, | |
ff93ec74 LD |
37 | }, |
38 | [1] = { | |
ff93ec74 | 39 | .reg_offset = 0, |
1c2928e3 MV |
40 | .mask = MAX77620_IRQ_LVL2_GPIO_EDGE1, |
41 | .type = { | |
42 | .type_rising_val = MAX77620_CNFG_GPIO_INT_RISING, | |
43 | .type_falling_val = MAX77620_CNFG_GPIO_INT_FALLING, | |
44 | .type_reg_mask = MAX77620_CNFG_GPIO_INT_MASK, | |
45 | .type_reg_offset = 1, | |
46 | .types_supported = IRQ_TYPE_EDGE_BOTH, | |
47 | }, | |
ff93ec74 LD |
48 | }, |
49 | [2] = { | |
ff93ec74 | 50 | .reg_offset = 0, |
1c2928e3 MV |
51 | .mask = MAX77620_IRQ_LVL2_GPIO_EDGE2, |
52 | .type = { | |
53 | .type_rising_val = MAX77620_CNFG_GPIO_INT_RISING, | |
54 | .type_falling_val = MAX77620_CNFG_GPIO_INT_FALLING, | |
55 | .type_reg_mask = MAX77620_CNFG_GPIO_INT_MASK, | |
56 | .type_reg_offset = 2, | |
57 | .types_supported = IRQ_TYPE_EDGE_BOTH, | |
58 | }, | |
ff93ec74 LD |
59 | }, |
60 | [3] = { | |
ff93ec74 | 61 | .reg_offset = 0, |
1c2928e3 MV |
62 | .mask = MAX77620_IRQ_LVL2_GPIO_EDGE3, |
63 | .type = { | |
64 | .type_rising_val = MAX77620_CNFG_GPIO_INT_RISING, | |
65 | .type_falling_val = MAX77620_CNFG_GPIO_INT_FALLING, | |
66 | .type_reg_mask = MAX77620_CNFG_GPIO_INT_MASK, | |
67 | .type_reg_offset = 3, | |
68 | .types_supported = IRQ_TYPE_EDGE_BOTH, | |
69 | }, | |
ff93ec74 LD |
70 | }, |
71 | [4] = { | |
ff93ec74 | 72 | .reg_offset = 0, |
1c2928e3 MV |
73 | .mask = MAX77620_IRQ_LVL2_GPIO_EDGE4, |
74 | .type = { | |
75 | .type_rising_val = MAX77620_CNFG_GPIO_INT_RISING, | |
76 | .type_falling_val = MAX77620_CNFG_GPIO_INT_FALLING, | |
77 | .type_reg_mask = MAX77620_CNFG_GPIO_INT_MASK, | |
78 | .type_reg_offset = 4, | |
79 | .types_supported = IRQ_TYPE_EDGE_BOTH, | |
80 | }, | |
ff93ec74 LD |
81 | }, |
82 | [5] = { | |
ff93ec74 | 83 | .reg_offset = 0, |
1c2928e3 MV |
84 | .mask = MAX77620_IRQ_LVL2_GPIO_EDGE5, |
85 | .type = { | |
86 | .type_rising_val = MAX77620_CNFG_GPIO_INT_RISING, | |
87 | .type_falling_val = MAX77620_CNFG_GPIO_INT_FALLING, | |
88 | .type_reg_mask = MAX77620_CNFG_GPIO_INT_MASK, | |
89 | .type_reg_offset = 5, | |
90 | .types_supported = IRQ_TYPE_EDGE_BOTH, | |
91 | }, | |
ff93ec74 LD |
92 | }, |
93 | [6] = { | |
ff93ec74 | 94 | .reg_offset = 0, |
1c2928e3 MV |
95 | .mask = MAX77620_IRQ_LVL2_GPIO_EDGE6, |
96 | .type = { | |
97 | .type_rising_val = MAX77620_CNFG_GPIO_INT_RISING, | |
98 | .type_falling_val = MAX77620_CNFG_GPIO_INT_FALLING, | |
99 | .type_reg_mask = MAX77620_CNFG_GPIO_INT_MASK, | |
100 | .type_reg_offset = 6, | |
101 | .types_supported = IRQ_TYPE_EDGE_BOTH, | |
102 | }, | |
ff93ec74 LD |
103 | }, |
104 | [7] = { | |
ff93ec74 | 105 | .reg_offset = 0, |
1c2928e3 MV |
106 | .mask = MAX77620_IRQ_LVL2_GPIO_EDGE7, |
107 | .type = { | |
108 | .type_rising_val = MAX77620_CNFG_GPIO_INT_RISING, | |
109 | .type_falling_val = MAX77620_CNFG_GPIO_INT_FALLING, | |
110 | .type_reg_mask = MAX77620_CNFG_GPIO_INT_MASK, | |
111 | .type_reg_offset = 7, | |
112 | .types_supported = IRQ_TYPE_EDGE_BOTH, | |
113 | }, | |
ff93ec74 | 114 | }, |
02c5ba1e LD |
115 | }; |
116 | ||
ee949b51 | 117 | static const struct regmap_irq_chip max77620_gpio_irq_chip = { |
02c5ba1e LD |
118 | .name = "max77620-gpio", |
119 | .irqs = max77620_gpio_irqs, | |
120 | .num_irqs = ARRAY_SIZE(max77620_gpio_irqs), | |
121 | .num_regs = 1, | |
ff93ec74 | 122 | .num_type_reg = 8, |
02c5ba1e | 123 | .irq_reg_stride = 1, |
ff93ec74 | 124 | .type_reg_stride = 1, |
02c5ba1e | 125 | .status_base = MAX77620_REG_IRQ_LVL2_GPIO, |
ff93ec74 | 126 | .type_base = MAX77620_REG_GPIO0, |
02c5ba1e LD |
127 | }; |
128 | ||
129 | static int max77620_gpio_dir_input(struct gpio_chip *gc, unsigned int offset) | |
130 | { | |
131 | struct max77620_gpio *mgpio = gpiochip_get_data(gc); | |
132 | int ret; | |
133 | ||
134 | ret = regmap_update_bits(mgpio->rmap, GPIO_REG_ADDR(offset), | |
135 | MAX77620_CNFG_GPIO_DIR_MASK, | |
136 | MAX77620_CNFG_GPIO_DIR_INPUT); | |
137 | if (ret < 0) | |
138 | dev_err(mgpio->dev, "CNFG_GPIOx dir update failed: %d\n", ret); | |
139 | ||
140 | return ret; | |
141 | } | |
142 | ||
143 | static int max77620_gpio_get(struct gpio_chip *gc, unsigned int offset) | |
144 | { | |
145 | struct max77620_gpio *mgpio = gpiochip_get_data(gc); | |
146 | unsigned int val; | |
147 | int ret; | |
148 | ||
149 | ret = regmap_read(mgpio->rmap, GPIO_REG_ADDR(offset), &val); | |
150 | if (ret < 0) { | |
151 | dev_err(mgpio->dev, "CNFG_GPIOx read failed: %d\n", ret); | |
152 | return ret; | |
153 | } | |
154 | ||
1941b441 VRT |
155 | if (val & MAX77620_CNFG_GPIO_DIR_MASK) |
156 | return !!(val & MAX77620_CNFG_GPIO_INPUT_VAL_MASK); | |
157 | else | |
158 | return !!(val & MAX77620_CNFG_GPIO_OUTPUT_VAL_MASK); | |
02c5ba1e LD |
159 | } |
160 | ||
161 | static int max77620_gpio_dir_output(struct gpio_chip *gc, unsigned int offset, | |
162 | int value) | |
163 | { | |
164 | struct max77620_gpio *mgpio = gpiochip_get_data(gc); | |
165 | u8 val; | |
166 | int ret; | |
167 | ||
168 | val = (value) ? MAX77620_CNFG_GPIO_OUTPUT_VAL_HIGH : | |
169 | MAX77620_CNFG_GPIO_OUTPUT_VAL_LOW; | |
170 | ||
171 | ret = regmap_update_bits(mgpio->rmap, GPIO_REG_ADDR(offset), | |
172 | MAX77620_CNFG_GPIO_OUTPUT_VAL_MASK, val); | |
173 | if (ret < 0) { | |
174 | dev_err(mgpio->dev, "CNFG_GPIOx val update failed: %d\n", ret); | |
175 | return ret; | |
176 | } | |
177 | ||
178 | ret = regmap_update_bits(mgpio->rmap, GPIO_REG_ADDR(offset), | |
179 | MAX77620_CNFG_GPIO_DIR_MASK, | |
180 | MAX77620_CNFG_GPIO_DIR_OUTPUT); | |
181 | if (ret < 0) | |
182 | dev_err(mgpio->dev, "CNFG_GPIOx dir update failed: %d\n", ret); | |
183 | ||
184 | return ret; | |
185 | } | |
186 | ||
2956b5d9 | 187 | static int max77620_gpio_set_debounce(struct max77620_gpio *mgpio, |
02c5ba1e LD |
188 | unsigned int offset, |
189 | unsigned int debounce) | |
190 | { | |
02c5ba1e LD |
191 | u8 val; |
192 | int ret; | |
193 | ||
194 | switch (debounce) { | |
195 | case 0: | |
196 | val = MAX77620_CNFG_GPIO_DBNC_None; | |
197 | break; | |
198 | case 1 ... 8: | |
199 | val = MAX77620_CNFG_GPIO_DBNC_8ms; | |
200 | break; | |
201 | case 9 ... 16: | |
202 | val = MAX77620_CNFG_GPIO_DBNC_16ms; | |
203 | break; | |
204 | case 17 ... 32: | |
205 | val = MAX77620_CNFG_GPIO_DBNC_32ms; | |
206 | break; | |
207 | default: | |
208 | dev_err(mgpio->dev, "Illegal value %u\n", debounce); | |
209 | return -EINVAL; | |
210 | } | |
211 | ||
212 | ret = regmap_update_bits(mgpio->rmap, GPIO_REG_ADDR(offset), | |
213 | MAX77620_CNFG_GPIO_DBNC_MASK, val); | |
214 | if (ret < 0) | |
215 | dev_err(mgpio->dev, "CNFG_GPIOx_DBNC update failed: %d\n", ret); | |
216 | ||
217 | return ret; | |
218 | } | |
219 | ||
220 | static void max77620_gpio_set(struct gpio_chip *gc, unsigned int offset, | |
221 | int value) | |
222 | { | |
223 | struct max77620_gpio *mgpio = gpiochip_get_data(gc); | |
224 | u8 val; | |
225 | int ret; | |
226 | ||
227 | val = (value) ? MAX77620_CNFG_GPIO_OUTPUT_VAL_HIGH : | |
228 | MAX77620_CNFG_GPIO_OUTPUT_VAL_LOW; | |
229 | ||
230 | ret = regmap_update_bits(mgpio->rmap, GPIO_REG_ADDR(offset), | |
231 | MAX77620_CNFG_GPIO_OUTPUT_VAL_MASK, val); | |
232 | if (ret < 0) | |
233 | dev_err(mgpio->dev, "CNFG_GPIO_OUT update failed: %d\n", ret); | |
234 | } | |
235 | ||
2956b5d9 MW |
236 | static int max77620_gpio_set_config(struct gpio_chip *gc, unsigned int offset, |
237 | unsigned long config) | |
23087a05 LD |
238 | { |
239 | struct max77620_gpio *mgpio = gpiochip_get_data(gc); | |
240 | ||
2956b5d9 MW |
241 | switch (pinconf_to_config_param(config)) { |
242 | case PIN_CONFIG_DRIVE_OPEN_DRAIN: | |
23087a05 LD |
243 | return regmap_update_bits(mgpio->rmap, GPIO_REG_ADDR(offset), |
244 | MAX77620_CNFG_GPIO_DRV_MASK, | |
245 | MAX77620_CNFG_GPIO_DRV_OPENDRAIN); | |
2956b5d9 | 246 | case PIN_CONFIG_DRIVE_PUSH_PULL: |
23087a05 LD |
247 | return regmap_update_bits(mgpio->rmap, GPIO_REG_ADDR(offset), |
248 | MAX77620_CNFG_GPIO_DRV_MASK, | |
249 | MAX77620_CNFG_GPIO_DRV_PUSHPULL); | |
2956b5d9 MW |
250 | case PIN_CONFIG_INPUT_DEBOUNCE: |
251 | return max77620_gpio_set_debounce(mgpio, offset, | |
252 | pinconf_to_config_argument(config)); | |
23087a05 LD |
253 | default: |
254 | break; | |
255 | } | |
256 | ||
257 | return -ENOTSUPP; | |
258 | } | |
259 | ||
02c5ba1e LD |
260 | static int max77620_gpio_to_irq(struct gpio_chip *gc, unsigned int offset) |
261 | { | |
262 | struct max77620_gpio *mgpio = gpiochip_get_data(gc); | |
263 | struct max77620_chip *chip = dev_get_drvdata(mgpio->dev->parent); | |
264 | ||
265 | return regmap_irq_get_virq(chip->gpio_irq_data, offset); | |
266 | } | |
267 | ||
268 | static int max77620_gpio_probe(struct platform_device *pdev) | |
269 | { | |
270 | struct max77620_chip *chip = dev_get_drvdata(pdev->dev.parent); | |
271 | struct max77620_gpio *mgpio; | |
272 | int gpio_irq; | |
273 | int ret; | |
274 | ||
275 | gpio_irq = platform_get_irq(pdev, 0); | |
276 | if (gpio_irq <= 0) { | |
277 | dev_err(&pdev->dev, "GPIO irq not available %d\n", gpio_irq); | |
278 | return -ENODEV; | |
279 | } | |
280 | ||
281 | mgpio = devm_kzalloc(&pdev->dev, sizeof(*mgpio), GFP_KERNEL); | |
282 | if (!mgpio) | |
283 | return -ENOMEM; | |
284 | ||
285 | mgpio->rmap = chip->rmap; | |
286 | mgpio->dev = &pdev->dev; | |
02c5ba1e LD |
287 | |
288 | mgpio->gpio_chip.label = pdev->name; | |
289 | mgpio->gpio_chip.parent = &pdev->dev; | |
290 | mgpio->gpio_chip.direction_input = max77620_gpio_dir_input; | |
291 | mgpio->gpio_chip.get = max77620_gpio_get; | |
292 | mgpio->gpio_chip.direction_output = max77620_gpio_dir_output; | |
02c5ba1e | 293 | mgpio->gpio_chip.set = max77620_gpio_set; |
2956b5d9 | 294 | mgpio->gpio_chip.set_config = max77620_gpio_set_config; |
02c5ba1e LD |
295 | mgpio->gpio_chip.to_irq = max77620_gpio_to_irq; |
296 | mgpio->gpio_chip.ngpio = MAX77620_GPIO_NR; | |
297 | mgpio->gpio_chip.can_sleep = 1; | |
298 | mgpio->gpio_chip.base = -1; | |
02c5ba1e LD |
299 | #ifdef CONFIG_OF_GPIO |
300 | mgpio->gpio_chip.of_node = pdev->dev.parent->of_node; | |
301 | #endif | |
302 | ||
303 | platform_set_drvdata(pdev, mgpio); | |
304 | ||
305 | ret = devm_gpiochip_add_data(&pdev->dev, &mgpio->gpio_chip, mgpio); | |
306 | if (ret < 0) { | |
307 | dev_err(&pdev->dev, "gpio_init: Failed to add max77620_gpio\n"); | |
308 | return ret; | |
309 | } | |
310 | ||
fdf4332f AL |
311 | ret = devm_regmap_add_irq_chip(&pdev->dev, chip->rmap, gpio_irq, |
312 | IRQF_ONESHOT, -1, | |
02c5ba1e LD |
313 | &max77620_gpio_irq_chip, |
314 | &chip->gpio_irq_data); | |
315 | if (ret < 0) { | |
316 | dev_err(&pdev->dev, "Failed to add gpio irq_chip %d\n", ret); | |
317 | return ret; | |
318 | } | |
319 | ||
320 | return 0; | |
321 | } | |
322 | ||
323 | static const struct platform_device_id max77620_gpio_devtype[] = { | |
324 | { .name = "max77620-gpio", }, | |
3107d572 | 325 | { .name = "max20024-gpio", }, |
02c5ba1e LD |
326 | {}, |
327 | }; | |
328 | MODULE_DEVICE_TABLE(platform, max77620_gpio_devtype); | |
329 | ||
330 | static struct platform_driver max77620_gpio_driver = { | |
331 | .driver.name = "max77620-gpio", | |
332 | .probe = max77620_gpio_probe, | |
333 | .id_table = max77620_gpio_devtype, | |
334 | }; | |
335 | ||
336 | module_platform_driver(max77620_gpio_driver); | |
337 | ||
338 | MODULE_DESCRIPTION("GPIO interface for MAX77620 and MAX20024 PMIC"); | |
339 | MODULE_AUTHOR("Laxman Dewangan <ldewangan@nvidia.com>"); | |
340 | MODULE_AUTHOR("Chaitanya Bandi <bandik@nvidia.com>"); | |
341 | MODULE_ALIAS("platform:max77620-gpio"); | |
342 | MODULE_LICENSE("GPL v2"); |