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