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