diff options
author | Laxman Dewangan <ldewangan@nvidia.com> | 2016-05-13 01:19:14 -0400 |
---|---|---|
committer | Linus Walleij <linus.walleij@linaro.org> | 2016-06-07 03:35:15 -0400 |
commit | 02c5ba1ee99cd67b27f562c120ae659e8acadded (patch) | |
tree | a61de0a2e8d97f4cbf1ba8af9fc88f9f3ab24856 /drivers/gpio/gpio-max77620.c | |
parent | f72677939cf582118bd645c5015605577db74b25 (diff) |
gpio: max77620: add gpio driver for MAX77620/MAX20024
MAXIM Semiconductor's PMIC, MAX77620/MAX20024 has 8 GPIO
pins. It also supports interrupts from these pins.
Add GPIO driver for these pins to control via GPIO APIs.
Signed-off-by: Laxman Dewangan <ldewangan@nvidia.com>
Reviewed-by: Linus Walleij <linus.walleij@linaro.org>
Signed-off-by: Linus Walleij <linus.walleij@linaro.org>
Diffstat (limited to 'drivers/gpio/gpio-max77620.c')
-rw-r--r-- | drivers/gpio/gpio-max77620.c | 238 |
1 files changed, 238 insertions, 0 deletions
diff --git a/drivers/gpio/gpio-max77620.c b/drivers/gpio/gpio-max77620.c new file mode 100644 index 000000000000..d9275623577c --- /dev/null +++ b/drivers/gpio/gpio-max77620.c | |||
@@ -0,0 +1,238 @@ | |||
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[] = { | ||
30 | REGMAP_IRQ_REG(0, 0, MAX77620_IRQ_LVL2_GPIO_EDGE0), | ||
31 | REGMAP_IRQ_REG(1, 0, MAX77620_IRQ_LVL2_GPIO_EDGE1), | ||
32 | REGMAP_IRQ_REG(2, 0, MAX77620_IRQ_LVL2_GPIO_EDGE2), | ||
33 | REGMAP_IRQ_REG(3, 0, MAX77620_IRQ_LVL2_GPIO_EDGE3), | ||
34 | REGMAP_IRQ_REG(4, 0, MAX77620_IRQ_LVL2_GPIO_EDGE4), | ||
35 | REGMAP_IRQ_REG(5, 0, MAX77620_IRQ_LVL2_GPIO_EDGE5), | ||
36 | REGMAP_IRQ_REG(6, 0, MAX77620_IRQ_LVL2_GPIO_EDGE6), | ||
37 | REGMAP_IRQ_REG(7, 0, MAX77620_IRQ_LVL2_GPIO_EDGE7), | ||
38 | }; | ||
39 | |||
40 | static struct regmap_irq_chip max77620_gpio_irq_chip = { | ||
41 | .name = "max77620-gpio", | ||
42 | .irqs = max77620_gpio_irqs, | ||
43 | .num_irqs = ARRAY_SIZE(max77620_gpio_irqs), | ||
44 | .num_regs = 1, | ||
45 | .irq_reg_stride = 1, | ||
46 | .status_base = MAX77620_REG_IRQ_LVL2_GPIO, | ||
47 | }; | ||
48 | |||
49 | static int max77620_gpio_dir_input(struct gpio_chip *gc, unsigned int offset) | ||
50 | { | ||
51 | struct max77620_gpio *mgpio = gpiochip_get_data(gc); | ||
52 | int ret; | ||
53 | |||
54 | ret = regmap_update_bits(mgpio->rmap, GPIO_REG_ADDR(offset), | ||
55 | MAX77620_CNFG_GPIO_DIR_MASK, | ||
56 | MAX77620_CNFG_GPIO_DIR_INPUT); | ||
57 | if (ret < 0) | ||
58 | dev_err(mgpio->dev, "CNFG_GPIOx dir update failed: %d\n", ret); | ||
59 | |||
60 | return ret; | ||
61 | } | ||
62 | |||
63 | static int max77620_gpio_get(struct gpio_chip *gc, unsigned int offset) | ||
64 | { | ||
65 | struct max77620_gpio *mgpio = gpiochip_get_data(gc); | ||
66 | unsigned int val; | ||
67 | int ret; | ||
68 | |||
69 | ret = regmap_read(mgpio->rmap, GPIO_REG_ADDR(offset), &val); | ||
70 | if (ret < 0) { | ||
71 | dev_err(mgpio->dev, "CNFG_GPIOx read failed: %d\n", ret); | ||
72 | return ret; | ||
73 | } | ||
74 | |||
75 | return !!(val & MAX77620_CNFG_GPIO_INPUT_VAL_MASK); | ||
76 | } | ||
77 | |||
78 | static int max77620_gpio_dir_output(struct gpio_chip *gc, unsigned int offset, | ||
79 | int value) | ||
80 | { | ||
81 | struct max77620_gpio *mgpio = gpiochip_get_data(gc); | ||
82 | u8 val; | ||
83 | int ret; | ||
84 | |||
85 | val = (value) ? MAX77620_CNFG_GPIO_OUTPUT_VAL_HIGH : | ||
86 | MAX77620_CNFG_GPIO_OUTPUT_VAL_LOW; | ||
87 | |||
88 | ret = regmap_update_bits(mgpio->rmap, GPIO_REG_ADDR(offset), | ||
89 | MAX77620_CNFG_GPIO_OUTPUT_VAL_MASK, val); | ||
90 | if (ret < 0) { | ||
91 | dev_err(mgpio->dev, "CNFG_GPIOx val update failed: %d\n", ret); | ||
92 | return ret; | ||
93 | } | ||
94 | |||
95 | ret = regmap_update_bits(mgpio->rmap, GPIO_REG_ADDR(offset), | ||
96 | MAX77620_CNFG_GPIO_DIR_MASK, | ||
97 | MAX77620_CNFG_GPIO_DIR_OUTPUT); | ||
98 | if (ret < 0) | ||
99 | dev_err(mgpio->dev, "CNFG_GPIOx dir update failed: %d\n", ret); | ||
100 | |||
101 | return ret; | ||
102 | } | ||
103 | |||
104 | static int max77620_gpio_set_debounce(struct gpio_chip *gc, | ||
105 | unsigned int offset, | ||
106 | unsigned int debounce) | ||
107 | { | ||
108 | struct max77620_gpio *mgpio = gpiochip_get_data(gc); | ||
109 | u8 val; | ||
110 | int ret; | ||
111 | |||
112 | switch (debounce) { | ||
113 | case 0: | ||
114 | val = MAX77620_CNFG_GPIO_DBNC_None; | ||
115 | break; | ||
116 | case 1 ... 8: | ||
117 | val = MAX77620_CNFG_GPIO_DBNC_8ms; | ||
118 | break; | ||
119 | case 9 ... 16: | ||
120 | val = MAX77620_CNFG_GPIO_DBNC_16ms; | ||
121 | break; | ||
122 | case 17 ... 32: | ||
123 | val = MAX77620_CNFG_GPIO_DBNC_32ms; | ||
124 | break; | ||
125 | default: | ||
126 | dev_err(mgpio->dev, "Illegal value %u\n", debounce); | ||
127 | return -EINVAL; | ||
128 | } | ||
129 | |||
130 | ret = regmap_update_bits(mgpio->rmap, GPIO_REG_ADDR(offset), | ||
131 | MAX77620_CNFG_GPIO_DBNC_MASK, val); | ||
132 | if (ret < 0) | ||
133 | dev_err(mgpio->dev, "CNFG_GPIOx_DBNC update failed: %d\n", ret); | ||
134 | |||
135 | return ret; | ||
136 | } | ||
137 | |||
138 | static void max77620_gpio_set(struct gpio_chip *gc, unsigned int offset, | ||
139 | int value) | ||
140 | { | ||
141 | struct max77620_gpio *mgpio = gpiochip_get_data(gc); | ||
142 | u8 val; | ||
143 | int ret; | ||
144 | |||
145 | val = (value) ? MAX77620_CNFG_GPIO_OUTPUT_VAL_HIGH : | ||
146 | MAX77620_CNFG_GPIO_OUTPUT_VAL_LOW; | ||
147 | |||
148 | ret = regmap_update_bits(mgpio->rmap, GPIO_REG_ADDR(offset), | ||
149 | MAX77620_CNFG_GPIO_OUTPUT_VAL_MASK, val); | ||
150 | if (ret < 0) | ||
151 | dev_err(mgpio->dev, "CNFG_GPIO_OUT update failed: %d\n", ret); | ||
152 | } | ||
153 | |||
154 | static int max77620_gpio_to_irq(struct gpio_chip *gc, unsigned int offset) | ||
155 | { | ||
156 | struct max77620_gpio *mgpio = gpiochip_get_data(gc); | ||
157 | struct max77620_chip *chip = dev_get_drvdata(mgpio->dev->parent); | ||
158 | |||
159 | return regmap_irq_get_virq(chip->gpio_irq_data, offset); | ||
160 | } | ||
161 | |||
162 | static int max77620_gpio_probe(struct platform_device *pdev) | ||
163 | { | ||
164 | struct max77620_chip *chip = dev_get_drvdata(pdev->dev.parent); | ||
165 | struct max77620_gpio *mgpio; | ||
166 | int gpio_irq; | ||
167 | int ret; | ||
168 | |||
169 | gpio_irq = platform_get_irq(pdev, 0); | ||
170 | if (gpio_irq <= 0) { | ||
171 | dev_err(&pdev->dev, "GPIO irq not available %d\n", gpio_irq); | ||
172 | return -ENODEV; | ||
173 | } | ||
174 | |||
175 | mgpio = devm_kzalloc(&pdev->dev, sizeof(*mgpio), GFP_KERNEL); | ||
176 | if (!mgpio) | ||
177 | return -ENOMEM; | ||
178 | |||
179 | mgpio->rmap = chip->rmap; | ||
180 | mgpio->dev = &pdev->dev; | ||
181 | mgpio->gpio_irq = gpio_irq; | ||
182 | |||
183 | mgpio->gpio_chip.label = pdev->name; | ||
184 | mgpio->gpio_chip.parent = &pdev->dev; | ||
185 | mgpio->gpio_chip.direction_input = max77620_gpio_dir_input; | ||
186 | mgpio->gpio_chip.get = max77620_gpio_get; | ||
187 | mgpio->gpio_chip.direction_output = max77620_gpio_dir_output; | ||
188 | mgpio->gpio_chip.set_debounce = max77620_gpio_set_debounce; | ||
189 | mgpio->gpio_chip.set = max77620_gpio_set; | ||
190 | mgpio->gpio_chip.to_irq = max77620_gpio_to_irq; | ||
191 | mgpio->gpio_chip.ngpio = MAX77620_GPIO_NR; | ||
192 | mgpio->gpio_chip.can_sleep = 1; | ||
193 | mgpio->gpio_chip.base = -1; | ||
194 | mgpio->irq_base = -1; | ||
195 | #ifdef CONFIG_OF_GPIO | ||
196 | mgpio->gpio_chip.of_node = pdev->dev.parent->of_node; | ||
197 | #endif | ||
198 | |||
199 | platform_set_drvdata(pdev, mgpio); | ||
200 | |||
201 | ret = devm_gpiochip_add_data(&pdev->dev, &mgpio->gpio_chip, mgpio); | ||
202 | if (ret < 0) { | ||
203 | dev_err(&pdev->dev, "gpio_init: Failed to add max77620_gpio\n"); | ||
204 | return ret; | ||
205 | } | ||
206 | |||
207 | mgpio->gpio_base = mgpio->gpio_chip.base; | ||
208 | ret = devm_regmap_add_irq_chip(&pdev->dev, chip->rmap, mgpio->gpio_irq, | ||
209 | IRQF_ONESHOT, mgpio->irq_base, | ||
210 | &max77620_gpio_irq_chip, | ||
211 | &chip->gpio_irq_data); | ||
212 | if (ret < 0) { | ||
213 | dev_err(&pdev->dev, "Failed to add gpio irq_chip %d\n", ret); | ||
214 | return ret; | ||
215 | } | ||
216 | |||
217 | return 0; | ||
218 | } | ||
219 | |||
220 | static const struct platform_device_id max77620_gpio_devtype[] = { | ||
221 | { .name = "max77620-gpio", }, | ||
222 | {}, | ||
223 | }; | ||
224 | MODULE_DEVICE_TABLE(platform, max77620_gpio_devtype); | ||
225 | |||
226 | static struct platform_driver max77620_gpio_driver = { | ||
227 | .driver.name = "max77620-gpio", | ||
228 | .probe = max77620_gpio_probe, | ||
229 | .id_table = max77620_gpio_devtype, | ||
230 | }; | ||
231 | |||
232 | module_platform_driver(max77620_gpio_driver); | ||
233 | |||
234 | MODULE_DESCRIPTION("GPIO interface for MAX77620 and MAX20024 PMIC"); | ||
235 | MODULE_AUTHOR("Laxman Dewangan <ldewangan@nvidia.com>"); | ||
236 | MODULE_AUTHOR("Chaitanya Bandi <bandik@nvidia.com>"); | ||
237 | MODULE_ALIAS("platform:max77620-gpio"); | ||
238 | MODULE_LICENSE("GPL v2"); | ||