diff options
Diffstat (limited to 'drivers/video/via/via-gpio.c')
-rw-r--r-- | drivers/video/via/via-gpio.c | 268 |
1 files changed, 268 insertions, 0 deletions
diff --git a/drivers/video/via/via-gpio.c b/drivers/video/via/via-gpio.c new file mode 100644 index 000000000000..e119d2103730 --- /dev/null +++ b/drivers/video/via/via-gpio.c | |||
@@ -0,0 +1,268 @@ | |||
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 "via-core.h" | ||
11 | #include "via-gpio.h" | ||
12 | #include "global.h" | ||
13 | |||
14 | /* | ||
15 | * The ports we know about. Note that the port-25 gpios are not | ||
16 | * mentioned in the datasheet. | ||
17 | */ | ||
18 | |||
19 | struct viafb_gpio { | ||
20 | char *vg_name; /* Data sheet name */ | ||
21 | u16 vg_io_port; | ||
22 | u8 vg_port_index; | ||
23 | int vg_mask_shift; | ||
24 | }; | ||
25 | |||
26 | static struct viafb_gpio viafb_all_gpios[] = { | ||
27 | { | ||
28 | .vg_name = "VGPIO0", /* Guess - not in datasheet */ | ||
29 | .vg_io_port = VIASR, | ||
30 | .vg_port_index = 0x25, | ||
31 | .vg_mask_shift = 1 | ||
32 | }, | ||
33 | { | ||
34 | .vg_name = "VGPIO1", | ||
35 | .vg_io_port = VIASR, | ||
36 | .vg_port_index = 0x25, | ||
37 | .vg_mask_shift = 0 | ||
38 | }, | ||
39 | { | ||
40 | .vg_name = "VGPIO2", /* aka DISPCLKI0 */ | ||
41 | .vg_io_port = VIASR, | ||
42 | .vg_port_index = 0x2c, | ||
43 | .vg_mask_shift = 1 | ||
44 | }, | ||
45 | { | ||
46 | .vg_name = "VGPIO3", /* aka DISPCLKO0 */ | ||
47 | .vg_io_port = VIASR, | ||
48 | .vg_port_index = 0x2c, | ||
49 | .vg_mask_shift = 0 | ||
50 | }, | ||
51 | { | ||
52 | .vg_name = "VGPIO4", /* DISPCLKI1 */ | ||
53 | .vg_io_port = VIASR, | ||
54 | .vg_port_index = 0x3d, | ||
55 | .vg_mask_shift = 1 | ||
56 | }, | ||
57 | { | ||
58 | .vg_name = "VGPIO5", /* DISPCLKO1 */ | ||
59 | .vg_io_port = VIASR, | ||
60 | .vg_port_index = 0x3d, | ||
61 | .vg_mask_shift = 0 | ||
62 | }, | ||
63 | }; | ||
64 | |||
65 | #define VIAFB_NUM_GPIOS ARRAY_SIZE(viafb_all_gpios) | ||
66 | |||
67 | /* | ||
68 | * This structure controls the active GPIOs, which may be a subset | ||
69 | * of those which are known. | ||
70 | */ | ||
71 | |||
72 | struct viafb_gpio_cfg { | ||
73 | struct gpio_chip gpio_chip; | ||
74 | struct viafb_dev *vdev; | ||
75 | struct viafb_gpio *active_gpios[VIAFB_NUM_GPIOS]; | ||
76 | char *gpio_names[VIAFB_NUM_GPIOS]; | ||
77 | }; | ||
78 | |||
79 | /* | ||
80 | * GPIO access functions | ||
81 | */ | ||
82 | static void via_gpio_set(struct gpio_chip *chip, unsigned int nr, | ||
83 | int value) | ||
84 | { | ||
85 | struct viafb_gpio_cfg *cfg = container_of(chip, | ||
86 | struct viafb_gpio_cfg, | ||
87 | gpio_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 = viafb_read_reg(VIASR, 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 | viafb_write_reg(gpio->vg_port_index, VIASR, reg); | ||
101 | spin_unlock_irqrestore(&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 = container_of(chip, | ||
118 | struct viafb_gpio_cfg, | ||
119 | gpio_chip); | ||
120 | struct viafb_gpio *gpio; | ||
121 | unsigned long flags; | ||
122 | |||
123 | spin_lock_irqsave(&cfg->vdev->reg_lock, flags); | ||
124 | gpio = cfg->active_gpios[nr]; | ||
125 | viafb_write_reg_mask(gpio->vg_port_index, VIASR, 0, | ||
126 | 0x40 << gpio->vg_mask_shift); | ||
127 | spin_unlock_irqrestore(&cfg->vdev->reg_lock, flags); | ||
128 | return 0; | ||
129 | } | ||
130 | |||
131 | static int via_gpio_get(struct gpio_chip *chip, unsigned int nr) | ||
132 | { | ||
133 | struct viafb_gpio_cfg *cfg = container_of(chip, | ||
134 | struct viafb_gpio_cfg, | ||
135 | gpio_chip); | ||
136 | u8 reg; | ||
137 | struct viafb_gpio *gpio; | ||
138 | unsigned long flags; | ||
139 | |||
140 | spin_lock_irqsave(&cfg->vdev->reg_lock, flags); | ||
141 | gpio = cfg->active_gpios[nr]; | ||
142 | reg = viafb_read_reg(VIASR, gpio->vg_port_index); | ||
143 | spin_unlock_irqrestore(&cfg->vdev->reg_lock, flags); | ||
144 | return reg & (0x04 << gpio->vg_mask_shift); | ||
145 | } | ||
146 | |||
147 | |||
148 | static struct viafb_gpio_cfg gpio_config = { | ||
149 | .gpio_chip = { | ||
150 | .label = "VIAFB onboard GPIO", | ||
151 | .owner = THIS_MODULE, | ||
152 | .direction_output = via_gpio_dir_out, | ||
153 | .set = via_gpio_set, | ||
154 | .direction_input = via_gpio_dir_input, | ||
155 | .get = via_gpio_get, | ||
156 | .base = -1, | ||
157 | .ngpio = 0, | ||
158 | .can_sleep = 0 | ||
159 | } | ||
160 | }; | ||
161 | |||
162 | /* | ||
163 | * Manage the software enable bit. | ||
164 | */ | ||
165 | static void viafb_gpio_enable(struct viafb_gpio *gpio) | ||
166 | { | ||
167 | viafb_write_reg_mask(gpio->vg_port_index, VIASR, 0x02, 0x02); | ||
168 | } | ||
169 | |||
170 | static void viafb_gpio_disable(struct viafb_gpio *gpio) | ||
171 | { | ||
172 | viafb_write_reg_mask(gpio->vg_port_index, VIASR, 0, 0x02); | ||
173 | } | ||
174 | |||
175 | |||
176 | |||
177 | |||
178 | int viafb_create_gpios(struct viafb_dev *vdev, | ||
179 | const struct via_port_cfg *port_cfg) | ||
180 | { | ||
181 | int i, ngpio = 0, ret; | ||
182 | struct viafb_gpio *gpio; | ||
183 | unsigned long flags; | ||
184 | |||
185 | /* | ||
186 | * Set up entries for all GPIOs which have been configured to | ||
187 | * operate as such (as opposed to as i2c ports). | ||
188 | */ | ||
189 | for (i = 0; i < VIAFB_NUM_PORTS; i++) { | ||
190 | if (port_cfg[i].mode != VIA_MODE_GPIO) | ||
191 | continue; | ||
192 | for (gpio = viafb_all_gpios; | ||
193 | gpio < viafb_all_gpios + VIAFB_NUM_GPIOS; gpio++) | ||
194 | if (gpio->vg_port_index == port_cfg[i].ioport_index) { | ||
195 | gpio_config.active_gpios[ngpio] = gpio; | ||
196 | gpio_config.gpio_names[ngpio] = gpio->vg_name; | ||
197 | ngpio++; | ||
198 | } | ||
199 | } | ||
200 | gpio_config.gpio_chip.ngpio = ngpio; | ||
201 | gpio_config.gpio_chip.names = gpio_config.gpio_names; | ||
202 | gpio_config.vdev = vdev; | ||
203 | if (ngpio == 0) { | ||
204 | printk(KERN_INFO "viafb: no GPIOs configured\n"); | ||
205 | return 0; | ||
206 | } | ||
207 | /* | ||
208 | * Enable the ports. They come in pairs, with a single | ||
209 | * enable bit for both. | ||
210 | */ | ||
211 | spin_lock_irqsave(&gpio_config.vdev->reg_lock, flags); | ||
212 | for (i = 0; i < ngpio; i += 2) | ||
213 | viafb_gpio_enable(gpio_config.active_gpios[i]); | ||
214 | spin_unlock_irqrestore(&gpio_config.vdev->reg_lock, flags); | ||
215 | /* | ||
216 | * Get registered. | ||
217 | */ | ||
218 | gpio_config.gpio_chip.base = -1; /* Dynamic */ | ||
219 | ret = gpiochip_add(&gpio_config.gpio_chip); | ||
220 | if (ret) { | ||
221 | printk(KERN_ERR "viafb: failed to add gpios (%d)\n", ret); | ||
222 | gpio_config.gpio_chip.ngpio = 0; | ||
223 | } | ||
224 | return ret; | ||
225 | /* Port enable ? */ | ||
226 | } | ||
227 | |||
228 | |||
229 | int viafb_destroy_gpios(void) | ||
230 | { | ||
231 | unsigned long flags; | ||
232 | int ret = 0, i; | ||
233 | |||
234 | spin_lock_irqsave(&gpio_config.vdev->reg_lock, flags); | ||
235 | /* | ||
236 | * Get unregistered. | ||
237 | */ | ||
238 | if (gpio_config.gpio_chip.ngpio > 0) { | ||
239 | ret = gpiochip_remove(&gpio_config.gpio_chip); | ||
240 | if (ret) { /* Somebody still using it? */ | ||
241 | printk(KERN_ERR "Viafb: GPIO remove failed\n"); | ||
242 | goto out; | ||
243 | } | ||
244 | } | ||
245 | /* | ||
246 | * Disable the ports. | ||
247 | */ | ||
248 | for (i = 0; i < gpio_config.gpio_chip.ngpio; i += 2) | ||
249 | viafb_gpio_disable(gpio_config.active_gpios[i]); | ||
250 | gpio_config.gpio_chip.ngpio = 0; | ||
251 | out: | ||
252 | spin_unlock_irqrestore(&gpio_config.vdev->reg_lock, flags); | ||
253 | return ret; | ||
254 | } | ||
255 | |||
256 | /* | ||
257 | * Look up a specific gpio and return the number it was assigned. | ||
258 | */ | ||
259 | int viafb_gpio_lookup(const char *name) | ||
260 | { | ||
261 | int i; | ||
262 | |||
263 | for (i = 0; i < gpio_config.gpio_chip.ngpio; i++) | ||
264 | if (!strcmp(name, gpio_config.active_gpios[i]->vg_name)) | ||
265 | return gpio_config.gpio_chip.base + i; | ||
266 | return -1; | ||
267 | } | ||
268 | EXPORT_SYMBOL_GPL(viafb_gpio_lookup); | ||