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