07bfc915 |
1 | /* |
2 | * GPIO Driver for Dialog DA9052 PMICs. |
3 | * |
4 | * Copyright(c) 2011 Dialog Semiconductor Ltd. |
5 | * |
6 | * Author: David Dajun Chen <dchen@diasemi.com> |
7 | * |
8 | * This program is free software; you can redistribute it and/or modify it |
9 | * under the terms of the GNU General Public License as published by the |
10 | * Free Software Foundation; either version 2 of the License, or (at your |
11 | * option) any later version. |
12 | * |
13 | */ |
14 | #include <linux/module.h> |
15 | #include <linux/fs.h> |
16 | #include <linux/uaccess.h> |
17 | #include <linux/platform_device.h> |
18 | #include <linux/gpio.h> |
19 | #include <linux/syscalls.h> |
20 | #include <linux/seq_file.h> |
21 | |
22 | #include <linux/mfd/da9052/da9052.h> |
23 | #include <linux/mfd/da9052/reg.h> |
24 | #include <linux/mfd/da9052/pdata.h> |
25 | #include <linux/mfd/da9052/gpio.h> |
26 | |
27 | #define DA9052_INPUT 1 |
28 | #define DA9052_OUTPUT_OPENDRAIN 2 |
29 | #define DA9052_OUTPUT_PUSHPULL 3 |
30 | |
31 | #define DA9052_SUPPLY_VDD_IO1 0 |
32 | |
33 | #define DA9052_DEBOUNCING_OFF 0 |
34 | #define DA9052_DEBOUNCING_ON 1 |
35 | |
36 | #define DA9052_OUTPUT_LOWLEVEL 0 |
37 | |
38 | #define DA9052_ACTIVE_LOW 0 |
39 | #define DA9052_ACTIVE_HIGH 1 |
40 | |
41 | #define DA9052_GPIO_MAX_PORTS_PER_REGISTER 8 |
42 | #define DA9052_GPIO_SHIFT_COUNT(no) (no%8) |
43 | #define DA9052_GPIO_MASK_UPPER_NIBBLE 0xF0 |
44 | #define DA9052_GPIO_MASK_LOWER_NIBBLE 0x0F |
45 | #define DA9052_GPIO_NIBBLE_SHIFT 4 |
46 | |
47 | struct da9052_gpio { |
48 | struct da9052 *da9052; |
49 | struct gpio_chip gp; |
50 | }; |
51 | |
52 | static inline struct da9052_gpio *to_da9052_gpio(struct gpio_chip *chip) |
53 | { |
54 | return container_of(chip, struct da9052_gpio, gp); |
55 | } |
56 | |
57 | static unsigned char da9052_gpio_port_odd(unsigned offset) |
58 | { |
59 | return offset % 2; |
60 | } |
61 | |
62 | static int da9052_gpio_get(struct gpio_chip *gc, unsigned offset) |
63 | { |
64 | struct da9052_gpio *gpio = to_da9052_gpio(gc); |
65 | int da9052_port_direction = 0; |
66 | int ret; |
67 | |
68 | ret = da9052_reg_read(gpio->da9052, |
69 | DA9052_GPIO_0_1_REG + (offset >> 1)); |
70 | if (ret < 0) |
71 | return ret; |
72 | |
73 | if (da9052_gpio_port_odd(offset)) { |
74 | da9052_port_direction = ret & DA9052_GPIO_ODD_PORT_PIN; |
75 | da9052_port_direction >>= 4; |
76 | } else { |
77 | da9052_port_direction = ret & DA9052_GPIO_EVEN_PORT_PIN; |
78 | } |
79 | |
80 | switch (da9052_port_direction) { |
81 | case DA9052_INPUT: |
82 | if (offset < DA9052_GPIO_MAX_PORTS_PER_REGISTER) |
83 | ret = da9052_reg_read(gpio->da9052, |
84 | DA9052_STATUS_C_REG); |
85 | else |
86 | ret = da9052_reg_read(gpio->da9052, |
87 | DA9052_STATUS_D_REG); |
88 | if (ret < 0) |
89 | return ret; |
90 | if (ret & (1 << DA9052_GPIO_SHIFT_COUNT(offset))) |
91 | return 1; |
92 | else |
93 | return 0; |
94 | case DA9052_OUTPUT_PUSHPULL: |
95 | if (da9052_gpio_port_odd(offset)) |
96 | return ret & DA9052_GPIO_ODD_PORT_MODE; |
97 | else |
98 | return ret & DA9052_GPIO_EVEN_PORT_MODE; |
99 | default: |
100 | return -EINVAL; |
101 | } |
102 | } |
103 | |
104 | static void da9052_gpio_set(struct gpio_chip *gc, unsigned offset, int value) |
105 | { |
106 | struct da9052_gpio *gpio = to_da9052_gpio(gc); |
107 | unsigned char register_value = 0; |
108 | int ret; |
109 | |
110 | if (da9052_gpio_port_odd(offset)) { |
111 | if (value) { |
112 | register_value = DA9052_GPIO_ODD_PORT_MODE; |
113 | ret = da9052_reg_update(gpio->da9052, (offset >> 1) + |
114 | DA9052_GPIO_0_1_REG, |
115 | DA9052_GPIO_ODD_PORT_MODE, |
116 | register_value); |
117 | if (ret != 0) |
118 | dev_err(gpio->da9052->dev, |
119 | "Failed to updated gpio odd reg,%d", |
120 | ret); |
121 | } |
122 | } else { |
123 | if (value) { |
124 | register_value = DA9052_GPIO_EVEN_PORT_MODE; |
125 | ret = da9052_reg_update(gpio->da9052, (offset >> 1) + |
126 | DA9052_GPIO_0_1_REG, |
127 | DA9052_GPIO_EVEN_PORT_MODE, |
128 | register_value); |
129 | if (ret != 0) |
130 | dev_err(gpio->da9052->dev, |
131 | "Failed to updated gpio even reg,%d", |
132 | ret); |
133 | } |
134 | } |
135 | } |
136 | |
137 | static int da9052_gpio_direction_input(struct gpio_chip *gc, unsigned offset) |
138 | { |
139 | struct da9052_gpio *gpio = to_da9052_gpio(gc); |
140 | unsigned char register_value; |
141 | int ret; |
142 | |
143 | /* Format: function - 2 bits type - 1 bit mode - 1 bit */ |
144 | register_value = DA9052_INPUT | DA9052_ACTIVE_LOW << 2 | |
145 | DA9052_DEBOUNCING_ON << 3; |
146 | |
147 | if (da9052_gpio_port_odd(offset)) |
148 | ret = da9052_reg_update(gpio->da9052, (offset >> 1) + |
149 | DA9052_GPIO_0_1_REG, |
150 | DA9052_GPIO_MASK_UPPER_NIBBLE, |
151 | (register_value << |
152 | DA9052_GPIO_NIBBLE_SHIFT)); |
153 | else |
154 | ret = da9052_reg_update(gpio->da9052, (offset >> 1) + |
155 | DA9052_GPIO_0_1_REG, |
156 | DA9052_GPIO_MASK_LOWER_NIBBLE, |
157 | register_value); |
158 | |
159 | return ret; |
160 | } |
161 | |
162 | static int da9052_gpio_direction_output(struct gpio_chip *gc, |
163 | unsigned offset, int value) |
164 | { |
165 | struct da9052_gpio *gpio = to_da9052_gpio(gc); |
166 | unsigned char register_value; |
167 | int ret; |
168 | |
169 | /* Format: Function - 2 bits Type - 1 bit Mode - 1 bit */ |
170 | register_value = DA9052_OUTPUT_PUSHPULL | DA9052_SUPPLY_VDD_IO1 << 2 | |
171 | value << 3; |
172 | |
173 | if (da9052_gpio_port_odd(offset)) |
174 | ret = da9052_reg_update(gpio->da9052, (offset >> 1) + |
175 | DA9052_GPIO_0_1_REG, |
176 | DA9052_GPIO_MASK_UPPER_NIBBLE, |
177 | (register_value << |
178 | DA9052_GPIO_NIBBLE_SHIFT)); |
179 | else |
180 | ret = da9052_reg_update(gpio->da9052, (offset >> 1) + |
181 | DA9052_GPIO_0_1_REG, |
182 | DA9052_GPIO_MASK_LOWER_NIBBLE, |
183 | register_value); |
184 | |
185 | return ret; |
186 | } |
187 | |
188 | static int da9052_gpio_to_irq(struct gpio_chip *gc, u32 offset) |
189 | { |
190 | struct da9052_gpio *gpio = to_da9052_gpio(gc); |
191 | struct da9052 *da9052 = gpio->da9052; |
192 | |
193 | return da9052->irq_base + DA9052_IRQ_GPI0 + offset; |
194 | } |
195 | |
196 | static struct gpio_chip reference_gp __devinitdata = { |
197 | .label = "da9052-gpio", |
198 | .owner = THIS_MODULE, |
199 | .get = da9052_gpio_get, |
200 | .set = da9052_gpio_set, |
201 | .direction_input = da9052_gpio_direction_input, |
202 | .direction_output = da9052_gpio_direction_output, |
203 | .to_irq = da9052_gpio_to_irq, |
204 | .can_sleep = 1; |
205 | .ngpio = 16; |
206 | .base = -1; |
207 | }; |
208 | |
209 | static int __devinit da9052_gpio_probe(struct platform_device *pdev) |
210 | { |
211 | struct da9052_gpio *gpio; |
212 | struct da9052_pdata *pdata; |
213 | int ret; |
214 | |
215 | gpio = kzalloc(sizeof(*gpio), GFP_KERNEL); |
216 | if (gpio == NULL) |
217 | return -ENOMEM; |
218 | |
219 | gpio->da9052 = dev_get_drvdata(pdev->dev.parent); |
220 | pdata = gpio->da9052->dev->platform_data; |
221 | |
07bfc915 |
222 | gpio->gp = reference_gp; |
223 | if (pdata && pdata->gpio_base) |
224 | gpio->gp.base = pdata->gpio_base; |
225 | |
226 | ret = gpiochip_add(&gpio->gp); |
227 | if (ret < 0) { |
228 | dev_err(&pdev->dev, "Could not register gpiochip, %d\n", ret); |
229 | goto err_mem; |
230 | } |
231 | |
232 | platform_set_drvdata(pdev, gpio); |
233 | |
234 | return 0; |
235 | |
236 | err_mem: |
237 | kfree(gpio); |
238 | return ret; |
239 | } |
240 | |
241 | static int __devexit da9052_gpio_remove(struct platform_device *pdev) |
242 | { |
243 | struct da9052_gpio *gpio = platform_get_drvdata(pdev); |
244 | int ret; |
245 | |
246 | ret = gpiochip_remove(&gpio->gp); |
247 | if (ret == 0) |
248 | kfree(gpio); |
249 | |
250 | return ret; |
251 | } |
252 | |
253 | static struct platform_driver da9052_gpio_driver = { |
254 | .probe = da9052_gpio_probe, |
255 | .remove = __devexit_p(da9052_gpio_remove), |
256 | .driver = { |
257 | .name = "da9052-gpio", |
258 | .owner = THIS_MODULE, |
259 | }, |
260 | }; |
261 | |
262 | static int __init da9052_gpio_init(void) |
263 | { |
264 | return platform_driver_register(&da9052_gpio_driver); |
265 | } |
266 | module_init(da9052_gpio_init); |
267 | |
268 | static void __exit da9052_gpio_exit(void) |
269 | { |
270 | return platform_driver_unregister(&da9052_gpio_driver); |
271 | } |
272 | module_exit(da9052_gpio_exit); |
273 | |
274 | MODULE_AUTHOR("David Dajun Chen <dchen@diasemi.com>"); |
275 | MODULE_DESCRIPTION("DA9052 GPIO Device Driver"); |
276 | MODULE_LICENSE("GPL"); |
277 | MODULE_ALIAS("platform:da9052-gpio"); |