1 | // SPDX-License-Identifier: GPL-2.0-only |
2 | /* |
3 | * Support for viafb GPIO ports. |
4 | * |
5 | * Copyright 2009 Jonathan Corbet <corbet@lwn.net> |
6 | */ |
7 | |
8 | #include <linux/spinlock.h> |
9 | #include <linux/gpio/driver.h> |
10 | #include <linux/gpio/machine.h> |
11 | #include <linux/platform_device.h> |
12 | #include <linux/via-core.h> |
13 | #include <linux/export.h> |
14 | #include "via-gpio.h" |
15 | |
16 | /* |
17 | * The ports we know about. Note that the port-25 gpios are not |
18 | * mentioned in the datasheet. |
19 | */ |
20 | |
21 | struct viafb_gpio { |
22 | char *vg_name; /* Data sheet name */ |
23 | u16 vg_io_port; |
24 | u8 vg_port_index; |
25 | int vg_mask_shift; |
26 | }; |
27 | |
28 | static struct viafb_gpio viafb_all_gpios[] = { |
29 | { |
30 | .vg_name = "VGPIO0" , /* Guess - not in datasheet */ |
31 | .vg_io_port = VIASR, |
32 | .vg_port_index = 0x25, |
33 | .vg_mask_shift = 1 |
34 | }, |
35 | { |
36 | .vg_name = "VGPIO1" , |
37 | .vg_io_port = VIASR, |
38 | .vg_port_index = 0x25, |
39 | .vg_mask_shift = 0 |
40 | }, |
41 | { |
42 | .vg_name = "VGPIO2" , /* aka DISPCLKI0 */ |
43 | .vg_io_port = VIASR, |
44 | .vg_port_index = 0x2c, |
45 | .vg_mask_shift = 1 |
46 | }, |
47 | { |
48 | .vg_name = "VGPIO3" , /* aka DISPCLKO0 */ |
49 | .vg_io_port = VIASR, |
50 | .vg_port_index = 0x2c, |
51 | .vg_mask_shift = 0 |
52 | }, |
53 | { |
54 | .vg_name = "VGPIO4" , /* DISPCLKI1 */ |
55 | .vg_io_port = VIASR, |
56 | .vg_port_index = 0x3d, |
57 | .vg_mask_shift = 1 |
58 | }, |
59 | { |
60 | .vg_name = "VGPIO5" , /* DISPCLKO1 */ |
61 | .vg_io_port = VIASR, |
62 | .vg_port_index = 0x3d, |
63 | .vg_mask_shift = 0 |
64 | }, |
65 | }; |
66 | |
67 | #define VIAFB_NUM_GPIOS ARRAY_SIZE(viafb_all_gpios) |
68 | |
69 | /* |
70 | * This structure controls the active GPIOs, which may be a subset |
71 | * of those which are known. |
72 | */ |
73 | |
74 | struct viafb_gpio_cfg { |
75 | struct gpio_chip gpio_chip; |
76 | struct viafb_dev *vdev; |
77 | struct viafb_gpio *active_gpios[VIAFB_NUM_GPIOS]; |
78 | const char *gpio_names[VIAFB_NUM_GPIOS]; |
79 | }; |
80 | |
81 | /* |
82 | * GPIO access functions |
83 | */ |
84 | static void via_gpio_set(struct gpio_chip *chip, unsigned int nr, |
85 | int value) |
86 | { |
87 | struct viafb_gpio_cfg *cfg = gpiochip_get_data(gc: chip); |
88 | u8 reg; |
89 | struct viafb_gpio *gpio; |
90 | unsigned long flags; |
91 | |
92 | spin_lock_irqsave(&cfg->vdev->reg_lock, flags); |
93 | gpio = cfg->active_gpios[nr]; |
94 | reg = via_read_reg(VIASR, index: gpio->vg_port_index); |
95 | reg |= 0x40 << gpio->vg_mask_shift; /* output enable */ |
96 | if (value) |
97 | reg |= 0x10 << gpio->vg_mask_shift; |
98 | else |
99 | reg &= ~(0x10 << gpio->vg_mask_shift); |
100 | via_write_reg(VIASR, index: gpio->vg_port_index, data: reg); |
101 | spin_unlock_irqrestore(lock: &cfg->vdev->reg_lock, flags); |
102 | } |
103 | |
104 | static int via_gpio_dir_out(struct gpio_chip *chip, unsigned int nr, |
105 | int value) |
106 | { |
107 | via_gpio_set(chip, nr, value); |
108 | return 0; |
109 | } |
110 | |
111 | /* |
112 | * Set the input direction. I'm not sure this is right; we should |
113 | * be able to do input without disabling output. |
114 | */ |
115 | static int via_gpio_dir_input(struct gpio_chip *chip, unsigned int nr) |
116 | { |
117 | struct viafb_gpio_cfg *cfg = gpiochip_get_data(gc: chip); |
118 | struct viafb_gpio *gpio; |
119 | unsigned long flags; |
120 | |
121 | spin_lock_irqsave(&cfg->vdev->reg_lock, flags); |
122 | gpio = cfg->active_gpios[nr]; |
123 | via_write_reg_mask(VIASR, index: gpio->vg_port_index, data: 0, |
124 | mask: 0x40 << gpio->vg_mask_shift); |
125 | spin_unlock_irqrestore(lock: &cfg->vdev->reg_lock, flags); |
126 | return 0; |
127 | } |
128 | |
129 | static int via_gpio_get(struct gpio_chip *chip, unsigned int nr) |
130 | { |
131 | struct viafb_gpio_cfg *cfg = gpiochip_get_data(gc: chip); |
132 | u8 reg; |
133 | struct viafb_gpio *gpio; |
134 | unsigned long flags; |
135 | |
136 | spin_lock_irqsave(&cfg->vdev->reg_lock, flags); |
137 | gpio = cfg->active_gpios[nr]; |
138 | reg = via_read_reg(VIASR, index: gpio->vg_port_index); |
139 | spin_unlock_irqrestore(lock: &cfg->vdev->reg_lock, flags); |
140 | return !!(reg & (0x04 << gpio->vg_mask_shift)); |
141 | } |
142 | |
143 | |
144 | static struct viafb_gpio_cfg viafb_gpio_config = { |
145 | .gpio_chip = { |
146 | .label = "VIAFB onboard GPIO" , |
147 | .owner = THIS_MODULE, |
148 | .direction_output = via_gpio_dir_out, |
149 | .set = via_gpio_set, |
150 | .direction_input = via_gpio_dir_input, |
151 | .get = via_gpio_get, |
152 | .base = -1, |
153 | .ngpio = 0, |
154 | .can_sleep = 0 |
155 | } |
156 | }; |
157 | |
158 | /* |
159 | * Manage the software enable bit. |
160 | */ |
161 | static void viafb_gpio_enable(struct viafb_gpio *gpio) |
162 | { |
163 | via_write_reg_mask(VIASR, index: gpio->vg_port_index, data: 0x02, mask: 0x02); |
164 | } |
165 | |
166 | static void viafb_gpio_disable(struct viafb_gpio *gpio) |
167 | { |
168 | via_write_reg_mask(VIASR, index: gpio->vg_port_index, data: 0, mask: 0x02); |
169 | } |
170 | |
171 | #ifdef CONFIG_PM |
172 | |
173 | static int viafb_gpio_suspend(void *private) |
174 | { |
175 | return 0; |
176 | } |
177 | |
178 | static int viafb_gpio_resume(void *private) |
179 | { |
180 | int i; |
181 | |
182 | for (i = 0; i < viafb_gpio_config.gpio_chip.ngpio; i += 2) |
183 | viafb_gpio_enable(gpio: viafb_gpio_config.active_gpios[i]); |
184 | return 0; |
185 | } |
186 | |
187 | static struct viafb_pm_hooks viafb_gpio_pm_hooks = { |
188 | .suspend = viafb_gpio_suspend, |
189 | .resume = viafb_gpio_resume |
190 | }; |
191 | #endif /* CONFIG_PM */ |
192 | |
193 | static struct gpiod_lookup_table viafb_gpio_table = { |
194 | .dev_id = "viafb-camera" , |
195 | .table = { |
196 | GPIO_LOOKUP("via-gpio" , 2, "VGPIO2" , GPIO_ACTIVE_LOW), |
197 | GPIO_LOOKUP("via-gpio" , 3, "VGPIO3" , GPIO_ACTIVE_HIGH), |
198 | { } |
199 | }, |
200 | }; |
201 | |
202 | /* |
203 | * Platform device stuff. |
204 | */ |
205 | static int viafb_gpio_probe(struct platform_device *platdev) |
206 | { |
207 | struct viafb_dev *vdev = platdev->dev.platform_data; |
208 | struct via_port_cfg *port_cfg = vdev->port_cfg; |
209 | int i, ngpio = 0, ret; |
210 | struct viafb_gpio *gpio; |
211 | unsigned long flags; |
212 | |
213 | /* |
214 | * Set up entries for all GPIOs which have been configured to |
215 | * operate as such (as opposed to as i2c ports). |
216 | */ |
217 | for (i = 0; i < VIAFB_NUM_PORTS; i++) { |
218 | if (port_cfg[i].mode != VIA_MODE_GPIO) |
219 | continue; |
220 | for (gpio = viafb_all_gpios; |
221 | gpio < viafb_all_gpios + VIAFB_NUM_GPIOS; gpio++) |
222 | if (gpio->vg_port_index == port_cfg[i].ioport_index) { |
223 | viafb_gpio_config.active_gpios[ngpio] = gpio; |
224 | viafb_gpio_config.gpio_names[ngpio] = |
225 | gpio->vg_name; |
226 | ngpio++; |
227 | } |
228 | } |
229 | viafb_gpio_config.gpio_chip.ngpio = ngpio; |
230 | viafb_gpio_config.gpio_chip.names = viafb_gpio_config.gpio_names; |
231 | viafb_gpio_config.vdev = vdev; |
232 | if (ngpio == 0) { |
233 | printk(KERN_INFO "viafb: no GPIOs configured\n" ); |
234 | return 0; |
235 | } |
236 | /* |
237 | * Enable the ports. They come in pairs, with a single |
238 | * enable bit for both. |
239 | */ |
240 | spin_lock_irqsave(&viafb_gpio_config.vdev->reg_lock, flags); |
241 | for (i = 0; i < ngpio; i += 2) |
242 | viafb_gpio_enable(gpio: viafb_gpio_config.active_gpios[i]); |
243 | spin_unlock_irqrestore(lock: &viafb_gpio_config.vdev->reg_lock, flags); |
244 | /* |
245 | * Get registered. |
246 | */ |
247 | viafb_gpio_config.gpio_chip.base = -1; /* Dynamic */ |
248 | viafb_gpio_config.gpio_chip.label = "via-gpio" ; |
249 | ret = gpiochip_add_data(&viafb_gpio_config.gpio_chip, |
250 | &viafb_gpio_config); |
251 | if (ret) { |
252 | printk(KERN_ERR "viafb: failed to add gpios (%d)\n" , ret); |
253 | viafb_gpio_config.gpio_chip.ngpio = 0; |
254 | } |
255 | |
256 | gpiod_add_lookup_table(table: &viafb_gpio_table); |
257 | |
258 | #ifdef CONFIG_PM |
259 | viafb_pm_register(hooks: &viafb_gpio_pm_hooks); |
260 | #endif |
261 | return ret; |
262 | } |
263 | |
264 | |
265 | static void viafb_gpio_remove(struct platform_device *platdev) |
266 | { |
267 | unsigned long flags; |
268 | int i; |
269 | |
270 | #ifdef CONFIG_PM |
271 | viafb_pm_unregister(hooks: &viafb_gpio_pm_hooks); |
272 | #endif |
273 | |
274 | /* |
275 | * Get unregistered. |
276 | */ |
277 | if (viafb_gpio_config.gpio_chip.ngpio > 0) { |
278 | gpiochip_remove(gc: &viafb_gpio_config.gpio_chip); |
279 | } |
280 | /* |
281 | * Disable the ports. |
282 | */ |
283 | spin_lock_irqsave(&viafb_gpio_config.vdev->reg_lock, flags); |
284 | for (i = 0; i < viafb_gpio_config.gpio_chip.ngpio; i += 2) |
285 | viafb_gpio_disable(gpio: viafb_gpio_config.active_gpios[i]); |
286 | viafb_gpio_config.gpio_chip.ngpio = 0; |
287 | spin_unlock_irqrestore(lock: &viafb_gpio_config.vdev->reg_lock, flags); |
288 | } |
289 | |
290 | static struct platform_driver via_gpio_driver = { |
291 | .driver = { |
292 | .name = "viafb-gpio" , |
293 | }, |
294 | .probe = viafb_gpio_probe, |
295 | .remove_new = viafb_gpio_remove, |
296 | }; |
297 | |
298 | int viafb_gpio_init(void) |
299 | { |
300 | return platform_driver_register(&via_gpio_driver); |
301 | } |
302 | |
303 | void viafb_gpio_exit(void) |
304 | { |
305 | platform_driver_unregister(&via_gpio_driver); |
306 | } |
307 | |