diff options
Diffstat (limited to 'drivers/video/fbdev/via/via-gpio.c')
-rw-r--r-- | drivers/video/fbdev/via/via-gpio.c | 316 |
1 files changed, 316 insertions, 0 deletions
diff --git a/drivers/video/fbdev/via/via-gpio.c b/drivers/video/fbdev/via/via-gpio.c new file mode 100644 index 000000000000..e408679081ab --- /dev/null +++ b/drivers/video/fbdev/via/via-gpio.c | |||
@@ -0,0 +1,316 @@ | |||
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> | ||
10 | #include <linux/platform_device.h> | ||
11 | #include <linux/via-core.h> | ||
12 | #include <linux/via-gpio.h> | ||
13 | #include <linux/export.h> | ||
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]; | ||
77 | const char *gpio_names[VIAFB_NUM_GPIOS]; | ||
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]; | ||
95 | reg = via_read_reg(VIASR, gpio->vg_port_index); | ||
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); | ||
101 | via_write_reg(VIASR, gpio->vg_port_index, reg); | ||
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]; | ||
126 | via_write_reg_mask(VIASR, gpio->vg_port_index, 0, | ||
127 | 0x40 << gpio->vg_mask_shift); | ||
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]; | ||
143 | reg = via_read_reg(VIASR, gpio->vg_port_index); | ||
144 | spin_unlock_irqrestore(&cfg->vdev->reg_lock, flags); | ||
145 | return reg & (0x04 << gpio->vg_mask_shift); | ||
146 | } | ||
147 | |||
148 | |||
149 | static struct viafb_gpio_cfg viafb_gpio_config = { | ||
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 | { | ||
168 | via_write_reg_mask(VIASR, gpio->vg_port_index, 0x02, 0x02); | ||
169 | } | ||
170 | |||
171 | static void viafb_gpio_disable(struct viafb_gpio *gpio) | ||
172 | { | ||
173 | via_write_reg_mask(VIASR, gpio->vg_port_index, 0, 0x02); | ||
174 | } | ||
175 | |||
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 | |||
187 | for (i = 0; i < viafb_gpio_config.gpio_chip.ngpio; i += 2) | ||
188 | viafb_gpio_enable(viafb_gpio_config.active_gpios[i]); | ||
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 | |||
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; | ||
204 | |||
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; | ||
208 | return -1; | ||
209 | } | ||
210 | EXPORT_SYMBOL_GPL(viafb_gpio_lookup); | ||
211 | |||
212 | /* | ||
213 | * Platform device stuff. | ||
214 | */ | ||
215 | static int viafb_gpio_probe(struct platform_device *platdev) | ||
216 | { | ||
217 | struct viafb_dev *vdev = platdev->dev.platform_data; | ||
218 | struct via_port_cfg *port_cfg = vdev->port_cfg; | ||
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) { | ||
233 | viafb_gpio_config.active_gpios[ngpio] = gpio; | ||
234 | viafb_gpio_config.gpio_names[ngpio] = | ||
235 | gpio->vg_name; | ||
236 | ngpio++; | ||
237 | } | ||
238 | } | ||
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; | ||
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 | */ | ||
250 | spin_lock_irqsave(&viafb_gpio_config.vdev->reg_lock, flags); | ||
251 | for (i = 0; i < ngpio; i += 2) | ||
252 | viafb_gpio_enable(viafb_gpio_config.active_gpios[i]); | ||
253 | spin_unlock_irqrestore(&viafb_gpio_config.vdev->reg_lock, flags); | ||
254 | /* | ||
255 | * Get registered. | ||
256 | */ | ||
257 | viafb_gpio_config.gpio_chip.base = -1; /* Dynamic */ | ||
258 | ret = gpiochip_add(&viafb_gpio_config.gpio_chip); | ||
259 | if (ret) { | ||
260 | printk(KERN_ERR "viafb: failed to add gpios (%d)\n", ret); | ||
261 | viafb_gpio_config.gpio_chip.ngpio = 0; | ||
262 | } | ||
263 | #ifdef CONFIG_PM | ||
264 | viafb_pm_register(&viafb_gpio_pm_hooks); | ||
265 | #endif | ||
266 | return ret; | ||
267 | } | ||
268 | |||
269 | |||
270 | static int viafb_gpio_remove(struct platform_device *platdev) | ||
271 | { | ||
272 | unsigned long flags; | ||
273 | int ret = 0, i; | ||
274 | |||
275 | #ifdef CONFIG_PM | ||
276 | viafb_pm_unregister(&viafb_gpio_pm_hooks); | ||
277 | #endif | ||
278 | |||
279 | /* | ||
280 | * Get unregistered. | ||
281 | */ | ||
282 | if (viafb_gpio_config.gpio_chip.ngpio > 0) { | ||
283 | ret = gpiochip_remove(&viafb_gpio_config.gpio_chip); | ||
284 | if (ret) { /* Somebody still using it? */ | ||
285 | printk(KERN_ERR "Viafb: GPIO remove failed\n"); | ||
286 | return ret; | ||
287 | } | ||
288 | } | ||
289 | /* | ||
290 | * Disable the ports. | ||
291 | */ | ||
292 | spin_lock_irqsave(&viafb_gpio_config.vdev->reg_lock, flags); | ||
293 | for (i = 0; i < viafb_gpio_config.gpio_chip.ngpio; i += 2) | ||
294 | viafb_gpio_disable(viafb_gpio_config.active_gpios[i]); | ||
295 | viafb_gpio_config.gpio_chip.ngpio = 0; | ||
296 | spin_unlock_irqrestore(&viafb_gpio_config.vdev->reg_lock, flags); | ||
297 | return ret; | ||
298 | } | ||
299 | |||
300 | static struct platform_driver via_gpio_driver = { | ||
301 | .driver = { | ||
302 | .name = "viafb-gpio", | ||
303 | }, | ||
304 | .probe = viafb_gpio_probe, | ||
305 | .remove = viafb_gpio_remove, | ||
306 | }; | ||
307 | |||
308 | int viafb_gpio_init(void) | ||
309 | { | ||
310 | return platform_driver_register(&via_gpio_driver); | ||
311 | } | ||
312 | |||
313 | void viafb_gpio_exit(void) | ||
314 | { | ||
315 | platform_driver_unregister(&via_gpio_driver); | ||
316 | } | ||