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