1 | // SPDX-License-Identifier: GPL-2.0+ |
2 | /* |
3 | * Linux GPIOlib driver for the VIA VX855 integrated southbridge GPIO |
4 | * |
5 | * Copyright (C) 2009 VIA Technologies, Inc. |
6 | * Copyright (C) 2010 One Laptop per Child |
7 | * Author: Harald Welte <HaraldWelte@viatech.com> |
8 | * All rights reserved. |
9 | */ |
10 | #include <linux/kernel.h> |
11 | #include <linux/module.h> |
12 | #include <linux/gpio/driver.h> |
13 | #include <linux/slab.h> |
14 | #include <linux/device.h> |
15 | #include <linux/platform_device.h> |
16 | #include <linux/pci.h> |
17 | #include <linux/io.h> |
18 | |
19 | #define MODULE_NAME "vx855_gpio" |
20 | |
21 | /* The VX855 south bridge has the following GPIO pins: |
22 | * GPI 0...13 General Purpose Input |
23 | * GPO 0...12 General Purpose Output |
24 | * GPIO 0...14 General Purpose I/O (Open-Drain) |
25 | */ |
26 | |
27 | #define NR_VX855_GPI 14 |
28 | #define NR_VX855_GPO 13 |
29 | #define NR_VX855_GPIO 15 |
30 | |
31 | #define NR_VX855_GPInO (NR_VX855_GPI + NR_VX855_GPO) |
32 | #define NR_VX855_GP (NR_VX855_GPI + NR_VX855_GPO + NR_VX855_GPIO) |
33 | |
34 | struct vx855_gpio { |
35 | struct gpio_chip gpio; |
36 | spinlock_t lock; |
37 | u32 io_gpi; |
38 | u32 io_gpo; |
39 | }; |
40 | |
41 | /* resolve a GPIx into the corresponding bit position */ |
42 | static inline u_int32_t gpi_i_bit(int i) |
43 | { |
44 | if (i < 10) |
45 | return 1 << i; |
46 | else |
47 | return 1 << (i + 14); |
48 | } |
49 | |
50 | static inline u_int32_t gpo_o_bit(int i) |
51 | { |
52 | if (i < 11) |
53 | return 1 << i; |
54 | else |
55 | return 1 << (i + 14); |
56 | } |
57 | |
58 | static inline u_int32_t gpio_i_bit(int i) |
59 | { |
60 | if (i < 14) |
61 | return 1 << (i + 10); |
62 | else |
63 | return 1 << (i + 14); |
64 | } |
65 | |
66 | static inline u_int32_t gpio_o_bit(int i) |
67 | { |
68 | if (i < 14) |
69 | return 1 << (i + 11); |
70 | else |
71 | return 1 << (i + 13); |
72 | } |
73 | |
74 | /* Mapping between numeric GPIO ID and the actual GPIO hardware numbering: |
75 | * 0..13 GPI 0..13 |
76 | * 14..26 GPO 0..12 |
77 | * 27..41 GPIO 0..14 |
78 | */ |
79 | |
80 | static int vx855gpio_direction_input(struct gpio_chip *gpio, |
81 | unsigned int nr) |
82 | { |
83 | struct vx855_gpio *vg = gpiochip_get_data(gc: gpio); |
84 | unsigned long flags; |
85 | u_int32_t reg_out; |
86 | |
87 | /* Real GPI bits are always in input direction */ |
88 | if (nr < NR_VX855_GPI) |
89 | return 0; |
90 | |
91 | /* Real GPO bits cannot be put in output direction */ |
92 | if (nr < NR_VX855_GPInO) |
93 | return -EINVAL; |
94 | |
95 | /* Open Drain GPIO have to be set to one */ |
96 | spin_lock_irqsave(&vg->lock, flags); |
97 | reg_out = inl(port: vg->io_gpo); |
98 | reg_out |= gpio_o_bit(i: nr - NR_VX855_GPInO); |
99 | outl(value: reg_out, port: vg->io_gpo); |
100 | spin_unlock_irqrestore(lock: &vg->lock, flags); |
101 | |
102 | return 0; |
103 | } |
104 | |
105 | static int vx855gpio_get(struct gpio_chip *gpio, unsigned int nr) |
106 | { |
107 | struct vx855_gpio *vg = gpiochip_get_data(gc: gpio); |
108 | u_int32_t reg_in; |
109 | int ret = 0; |
110 | |
111 | if (nr < NR_VX855_GPI) { |
112 | reg_in = inl(port: vg->io_gpi); |
113 | if (reg_in & gpi_i_bit(i: nr)) |
114 | ret = 1; |
115 | } else if (nr < NR_VX855_GPInO) { |
116 | /* GPO don't have an input bit, we need to read it |
117 | * back from the output register */ |
118 | reg_in = inl(port: vg->io_gpo); |
119 | if (reg_in & gpo_o_bit(i: nr - NR_VX855_GPI)) |
120 | ret = 1; |
121 | } else { |
122 | reg_in = inl(port: vg->io_gpi); |
123 | if (reg_in & gpio_i_bit(i: nr - NR_VX855_GPInO)) |
124 | ret = 1; |
125 | } |
126 | |
127 | return ret; |
128 | } |
129 | |
130 | static void vx855gpio_set(struct gpio_chip *gpio, unsigned int nr, |
131 | int val) |
132 | { |
133 | struct vx855_gpio *vg = gpiochip_get_data(gc: gpio); |
134 | unsigned long flags; |
135 | u_int32_t reg_out; |
136 | |
137 | /* True GPI cannot be switched to output mode */ |
138 | if (nr < NR_VX855_GPI) |
139 | return; |
140 | |
141 | spin_lock_irqsave(&vg->lock, flags); |
142 | reg_out = inl(port: vg->io_gpo); |
143 | if (nr < NR_VX855_GPInO) { |
144 | if (val) |
145 | reg_out |= gpo_o_bit(i: nr - NR_VX855_GPI); |
146 | else |
147 | reg_out &= ~gpo_o_bit(i: nr - NR_VX855_GPI); |
148 | } else { |
149 | if (val) |
150 | reg_out |= gpio_o_bit(i: nr - NR_VX855_GPInO); |
151 | else |
152 | reg_out &= ~gpio_o_bit(i: nr - NR_VX855_GPInO); |
153 | } |
154 | outl(value: reg_out, port: vg->io_gpo); |
155 | spin_unlock_irqrestore(lock: &vg->lock, flags); |
156 | } |
157 | |
158 | static int vx855gpio_direction_output(struct gpio_chip *gpio, |
159 | unsigned int nr, int val) |
160 | { |
161 | /* True GPI cannot be switched to output mode */ |
162 | if (nr < NR_VX855_GPI) |
163 | return -EINVAL; |
164 | |
165 | /* True GPO don't need to be switched to output mode, |
166 | * and GPIO are open-drain, i.e. also need no switching, |
167 | * so all we do is set the level */ |
168 | vx855gpio_set(gpio, nr, val); |
169 | |
170 | return 0; |
171 | } |
172 | |
173 | static int vx855gpio_set_config(struct gpio_chip *gpio, unsigned int nr, |
174 | unsigned long config) |
175 | { |
176 | enum pin_config_param param = pinconf_to_config_param(config); |
177 | |
178 | /* The GPI cannot be single-ended */ |
179 | if (nr < NR_VX855_GPI) |
180 | return -EINVAL; |
181 | |
182 | /* The GPO's are push-pull */ |
183 | if (nr < NR_VX855_GPInO) { |
184 | if (param != PIN_CONFIG_DRIVE_PUSH_PULL) |
185 | return -ENOTSUPP; |
186 | return 0; |
187 | } |
188 | |
189 | /* The GPIO's are open drain */ |
190 | if (param != PIN_CONFIG_DRIVE_OPEN_DRAIN) |
191 | return -ENOTSUPP; |
192 | |
193 | return 0; |
194 | } |
195 | |
196 | static const char *vx855gpio_names[NR_VX855_GP] = { |
197 | "VX855_GPI0" , "VX855_GPI1" , "VX855_GPI2" , "VX855_GPI3" , "VX855_GPI4" , |
198 | "VX855_GPI5" , "VX855_GPI6" , "VX855_GPI7" , "VX855_GPI8" , "VX855_GPI9" , |
199 | "VX855_GPI10" , "VX855_GPI11" , "VX855_GPI12" , "VX855_GPI13" , |
200 | "VX855_GPO0" , "VX855_GPO1" , "VX855_GPO2" , "VX855_GPO3" , "VX855_GPO4" , |
201 | "VX855_GPO5" , "VX855_GPO6" , "VX855_GPO7" , "VX855_GPO8" , "VX855_GPO9" , |
202 | "VX855_GPO10" , "VX855_GPO11" , "VX855_GPO12" , |
203 | "VX855_GPIO0" , "VX855_GPIO1" , "VX855_GPIO2" , "VX855_GPIO3" , |
204 | "VX855_GPIO4" , "VX855_GPIO5" , "VX855_GPIO6" , "VX855_GPIO7" , |
205 | "VX855_GPIO8" , "VX855_GPIO9" , "VX855_GPIO10" , "VX855_GPIO11" , |
206 | "VX855_GPIO12" , "VX855_GPIO13" , "VX855_GPIO14" |
207 | }; |
208 | |
209 | static void vx855gpio_gpio_setup(struct vx855_gpio *vg) |
210 | { |
211 | struct gpio_chip *c = &vg->gpio; |
212 | |
213 | c->label = "VX855 South Bridge" ; |
214 | c->owner = THIS_MODULE; |
215 | c->direction_input = vx855gpio_direction_input; |
216 | c->direction_output = vx855gpio_direction_output; |
217 | c->get = vx855gpio_get; |
218 | c->set = vx855gpio_set; |
219 | c->set_config = vx855gpio_set_config; |
220 | c->dbg_show = NULL; |
221 | c->base = 0; |
222 | c->ngpio = NR_VX855_GP; |
223 | c->can_sleep = false; |
224 | c->names = vx855gpio_names; |
225 | } |
226 | |
227 | /* This platform device is ordinarily registered by the vx855 mfd driver */ |
228 | static int vx855gpio_probe(struct platform_device *pdev) |
229 | { |
230 | struct resource *res_gpi; |
231 | struct resource *res_gpo; |
232 | struct vx855_gpio *vg; |
233 | |
234 | res_gpi = platform_get_resource(pdev, IORESOURCE_IO, 0); |
235 | res_gpo = platform_get_resource(pdev, IORESOURCE_IO, 1); |
236 | if (!res_gpi || !res_gpo) |
237 | return -EBUSY; |
238 | |
239 | vg = devm_kzalloc(dev: &pdev->dev, size: sizeof(*vg), GFP_KERNEL); |
240 | if (!vg) |
241 | return -ENOMEM; |
242 | |
243 | dev_info(&pdev->dev, "found VX855 GPIO controller\n" ); |
244 | vg->io_gpi = res_gpi->start; |
245 | vg->io_gpo = res_gpo->start; |
246 | spin_lock_init(&vg->lock); |
247 | |
248 | /* |
249 | * A single byte is used to control various GPIO ports on the VX855, |
250 | * and in the case of the OLPC XO-1.5, some of those ports are used |
251 | * for switches that are interpreted and exposed through ACPI. ACPI |
252 | * will have reserved the region, so our own reservation will not |
253 | * succeed. Ignore and continue. |
254 | */ |
255 | |
256 | if (!devm_request_region(&pdev->dev, res_gpi->start, |
257 | resource_size(res_gpi), MODULE_NAME "_gpi" )) |
258 | dev_warn(&pdev->dev, |
259 | "GPI I/O resource busy, probably claimed by ACPI\n" ); |
260 | |
261 | if (!devm_request_region(&pdev->dev, res_gpo->start, |
262 | resource_size(res_gpo), MODULE_NAME "_gpo" )) |
263 | dev_warn(&pdev->dev, |
264 | "GPO I/O resource busy, probably claimed by ACPI\n" ); |
265 | |
266 | vx855gpio_gpio_setup(vg); |
267 | |
268 | return devm_gpiochip_add_data(&pdev->dev, &vg->gpio, vg); |
269 | } |
270 | |
271 | static struct platform_driver vx855gpio_driver = { |
272 | .driver = { |
273 | .name = MODULE_NAME, |
274 | }, |
275 | .probe = vx855gpio_probe, |
276 | }; |
277 | |
278 | module_platform_driver(vx855gpio_driver); |
279 | |
280 | MODULE_LICENSE("GPL" ); |
281 | MODULE_AUTHOR("Harald Welte <HaraldWelte@viatech.com>" ); |
282 | MODULE_DESCRIPTION("GPIO driver for the VIA VX855 chipset" ); |
283 | MODULE_ALIAS("platform:vx855_gpio" ); |
284 | |