1 | // SPDX-License-Identifier: GPL-2.0-or-later |
2 | /* |
3 | * GPIO Driver for Dialog DA9052 PMICs. |
4 | * |
5 | * Copyright(c) 2011 Dialog Semiconductor Ltd. |
6 | * |
7 | * Author: David Dajun Chen <dchen@diasemi.com> |
8 | */ |
9 | #include <linux/fs.h> |
10 | #include <linux/gpio/driver.h> |
11 | #include <linux/module.h> |
12 | #include <linux/platform_device.h> |
13 | #include <linux/syscalls.h> |
14 | #include <linux/uaccess.h> |
15 | |
16 | #include <linux/mfd/da9052/da9052.h> |
17 | #include <linux/mfd/da9052/pdata.h> |
18 | #include <linux/mfd/da9052/reg.h> |
19 | |
20 | #define DA9052_INPUT 1 |
21 | #define DA9052_OUTPUT_OPENDRAIN 2 |
22 | #define DA9052_OUTPUT_PUSHPULL 3 |
23 | |
24 | #define DA9052_SUPPLY_VDD_IO1 0 |
25 | |
26 | #define DA9052_DEBOUNCING_OFF 0 |
27 | #define DA9052_DEBOUNCING_ON 1 |
28 | |
29 | #define DA9052_OUTPUT_LOWLEVEL 0 |
30 | |
31 | #define DA9052_ACTIVE_LOW 0 |
32 | #define DA9052_ACTIVE_HIGH 1 |
33 | |
34 | #define DA9052_GPIO_MAX_PORTS_PER_REGISTER 8 |
35 | #define DA9052_GPIO_SHIFT_COUNT(no) (no%8) |
36 | #define DA9052_GPIO_MASK_UPPER_NIBBLE 0xF0 |
37 | #define DA9052_GPIO_MASK_LOWER_NIBBLE 0x0F |
38 | #define DA9052_GPIO_NIBBLE_SHIFT 4 |
39 | #define DA9052_IRQ_GPI0 16 |
40 | #define DA9052_GPIO_ODD_SHIFT 7 |
41 | #define DA9052_GPIO_EVEN_SHIFT 3 |
42 | |
43 | struct da9052_gpio { |
44 | struct da9052 *da9052; |
45 | struct gpio_chip gp; |
46 | }; |
47 | |
48 | static unsigned char da9052_gpio_port_odd(unsigned offset) |
49 | { |
50 | return offset % 2; |
51 | } |
52 | |
53 | static int da9052_gpio_get(struct gpio_chip *gc, unsigned offset) |
54 | { |
55 | struct da9052_gpio *gpio = gpiochip_get_data(gc); |
56 | int da9052_port_direction = 0; |
57 | int ret; |
58 | |
59 | ret = da9052_reg_read(da9052: gpio->da9052, |
60 | DA9052_GPIO_0_1_REG + (offset >> 1)); |
61 | if (ret < 0) |
62 | return ret; |
63 | |
64 | if (da9052_gpio_port_odd(offset)) { |
65 | da9052_port_direction = ret & DA9052_GPIO_ODD_PORT_PIN; |
66 | da9052_port_direction >>= 4; |
67 | } else { |
68 | da9052_port_direction = ret & DA9052_GPIO_EVEN_PORT_PIN; |
69 | } |
70 | |
71 | switch (da9052_port_direction) { |
72 | case DA9052_INPUT: |
73 | if (offset < DA9052_GPIO_MAX_PORTS_PER_REGISTER) |
74 | ret = da9052_reg_read(da9052: gpio->da9052, |
75 | DA9052_STATUS_C_REG); |
76 | else |
77 | ret = da9052_reg_read(da9052: gpio->da9052, |
78 | DA9052_STATUS_D_REG); |
79 | if (ret < 0) |
80 | return ret; |
81 | return !!(ret & (1 << DA9052_GPIO_SHIFT_COUNT(offset))); |
82 | case DA9052_OUTPUT_PUSHPULL: |
83 | if (da9052_gpio_port_odd(offset)) |
84 | return !!(ret & DA9052_GPIO_ODD_PORT_MODE); |
85 | else |
86 | return !!(ret & DA9052_GPIO_EVEN_PORT_MODE); |
87 | default: |
88 | return -EINVAL; |
89 | } |
90 | } |
91 | |
92 | static void da9052_gpio_set(struct gpio_chip *gc, unsigned offset, int value) |
93 | { |
94 | struct da9052_gpio *gpio = gpiochip_get_data(gc); |
95 | int ret; |
96 | |
97 | if (da9052_gpio_port_odd(offset)) { |
98 | ret = da9052_reg_update(da9052: gpio->da9052, reg: (offset >> 1) + |
99 | DA9052_GPIO_0_1_REG, |
100 | DA9052_GPIO_ODD_PORT_MODE, |
101 | reg_val: value << DA9052_GPIO_ODD_SHIFT); |
102 | if (ret != 0) |
103 | dev_err(gpio->da9052->dev, |
104 | "Failed to updated gpio odd reg,%d" , |
105 | ret); |
106 | } else { |
107 | ret = da9052_reg_update(da9052: gpio->da9052, reg: (offset >> 1) + |
108 | DA9052_GPIO_0_1_REG, |
109 | DA9052_GPIO_EVEN_PORT_MODE, |
110 | reg_val: value << DA9052_GPIO_EVEN_SHIFT); |
111 | if (ret != 0) |
112 | dev_err(gpio->da9052->dev, |
113 | "Failed to updated gpio even reg,%d" , |
114 | ret); |
115 | } |
116 | } |
117 | |
118 | static int da9052_gpio_direction_input(struct gpio_chip *gc, unsigned offset) |
119 | { |
120 | struct da9052_gpio *gpio = gpiochip_get_data(gc); |
121 | unsigned char register_value; |
122 | int ret; |
123 | |
124 | /* Format: function - 2 bits type - 1 bit mode - 1 bit */ |
125 | register_value = DA9052_INPUT | DA9052_ACTIVE_LOW << 2 | |
126 | DA9052_DEBOUNCING_ON << 3; |
127 | |
128 | if (da9052_gpio_port_odd(offset)) |
129 | ret = da9052_reg_update(da9052: gpio->da9052, reg: (offset >> 1) + |
130 | DA9052_GPIO_0_1_REG, |
131 | DA9052_GPIO_MASK_UPPER_NIBBLE, |
132 | reg_val: (register_value << |
133 | DA9052_GPIO_NIBBLE_SHIFT)); |
134 | else |
135 | ret = da9052_reg_update(da9052: gpio->da9052, reg: (offset >> 1) + |
136 | DA9052_GPIO_0_1_REG, |
137 | DA9052_GPIO_MASK_LOWER_NIBBLE, |
138 | reg_val: register_value); |
139 | |
140 | return ret; |
141 | } |
142 | |
143 | static int da9052_gpio_direction_output(struct gpio_chip *gc, |
144 | unsigned offset, int value) |
145 | { |
146 | struct da9052_gpio *gpio = gpiochip_get_data(gc); |
147 | unsigned char register_value; |
148 | int ret; |
149 | |
150 | /* Format: Function - 2 bits Type - 1 bit Mode - 1 bit */ |
151 | register_value = DA9052_OUTPUT_PUSHPULL | DA9052_SUPPLY_VDD_IO1 << 2 | |
152 | value << 3; |
153 | |
154 | if (da9052_gpio_port_odd(offset)) |
155 | ret = da9052_reg_update(da9052: gpio->da9052, reg: (offset >> 1) + |
156 | DA9052_GPIO_0_1_REG, |
157 | DA9052_GPIO_MASK_UPPER_NIBBLE, |
158 | reg_val: (register_value << |
159 | DA9052_GPIO_NIBBLE_SHIFT)); |
160 | else |
161 | ret = da9052_reg_update(da9052: gpio->da9052, reg: (offset >> 1) + |
162 | DA9052_GPIO_0_1_REG, |
163 | DA9052_GPIO_MASK_LOWER_NIBBLE, |
164 | reg_val: register_value); |
165 | |
166 | return ret; |
167 | } |
168 | |
169 | static int da9052_gpio_to_irq(struct gpio_chip *gc, u32 offset) |
170 | { |
171 | struct da9052_gpio *gpio = gpiochip_get_data(gc); |
172 | struct da9052 *da9052 = gpio->da9052; |
173 | |
174 | int irq; |
175 | |
176 | irq = regmap_irq_get_virq(data: da9052->irq_data, DA9052_IRQ_GPI0 + offset); |
177 | |
178 | return irq; |
179 | } |
180 | |
181 | static const struct gpio_chip reference_gp = { |
182 | .label = "da9052-gpio" , |
183 | .owner = THIS_MODULE, |
184 | .get = da9052_gpio_get, |
185 | .set = da9052_gpio_set, |
186 | .direction_input = da9052_gpio_direction_input, |
187 | .direction_output = da9052_gpio_direction_output, |
188 | .to_irq = da9052_gpio_to_irq, |
189 | .can_sleep = true, |
190 | .ngpio = 16, |
191 | .base = -1, |
192 | }; |
193 | |
194 | static int da9052_gpio_probe(struct platform_device *pdev) |
195 | { |
196 | struct da9052_gpio *gpio; |
197 | struct da9052_pdata *pdata; |
198 | |
199 | gpio = devm_kzalloc(dev: &pdev->dev, size: sizeof(*gpio), GFP_KERNEL); |
200 | if (!gpio) |
201 | return -ENOMEM; |
202 | |
203 | gpio->da9052 = dev_get_drvdata(dev: pdev->dev.parent); |
204 | pdata = dev_get_platdata(dev: gpio->da9052->dev); |
205 | |
206 | gpio->gp = reference_gp; |
207 | if (pdata && pdata->gpio_base) |
208 | gpio->gp.base = pdata->gpio_base; |
209 | |
210 | return devm_gpiochip_add_data(&pdev->dev, &gpio->gp, gpio); |
211 | } |
212 | |
213 | static struct platform_driver da9052_gpio_driver = { |
214 | .probe = da9052_gpio_probe, |
215 | .driver = { |
216 | .name = "da9052-gpio" , |
217 | }, |
218 | }; |
219 | |
220 | module_platform_driver(da9052_gpio_driver); |
221 | |
222 | MODULE_AUTHOR("David Dajun Chen <dchen@diasemi.com>" ); |
223 | MODULE_DESCRIPTION("DA9052 GPIO Device Driver" ); |
224 | MODULE_LICENSE("GPL" ); |
225 | MODULE_ALIAS("platform:da9052-gpio" ); |
226 | |