1 | // SPDX-License-Identifier: GPL-2.0-or-later |
2 | /* |
3 | * GPIO driver for the SMSC SCH311x Super-I/O chips |
4 | * |
5 | * Copyright (C) 2013 Bruno Randolf <br1@einfach.org> |
6 | * |
7 | * SuperIO functions and chip detection: |
8 | * (c) Copyright 2008 Wim Van Sebroeck <wim@iguana.be>. |
9 | */ |
10 | |
11 | #include <linux/ioport.h> |
12 | #include <linux/module.h> |
13 | #include <linux/kernel.h> |
14 | #include <linux/init.h> |
15 | #include <linux/platform_device.h> |
16 | #include <linux/gpio/driver.h> |
17 | #include <linux/bitops.h> |
18 | #include <linux/io.h> |
19 | |
20 | #define DRV_NAME "gpio-sch311x" |
21 | |
22 | #define SCH311X_GPIO_CONF_DIR BIT(0) |
23 | #define SCH311X_GPIO_CONF_INVERT BIT(1) |
24 | #define SCH311X_GPIO_CONF_OPEN_DRAIN BIT(7) |
25 | |
26 | #define SIO_CONFIG_KEY_ENTER 0x55 |
27 | #define SIO_CONFIG_KEY_EXIT 0xaa |
28 | |
29 | #define GP1 0x4b |
30 | |
31 | static int sch311x_ioports[] = { 0x2e, 0x4e, 0x162e, 0x164e }; |
32 | |
33 | static struct platform_device *sch311x_gpio_pdev; |
34 | |
35 | struct sch311x_pdev_data { /* platform device data */ |
36 | unsigned short runtime_reg; /* runtime register base address */ |
37 | }; |
38 | |
39 | struct sch311x_gpio_block { /* one GPIO block runtime data */ |
40 | struct gpio_chip chip; |
41 | unsigned short data_reg; /* from definition below */ |
42 | unsigned short *config_regs; /* pointer to definition below */ |
43 | unsigned short runtime_reg; /* runtime register */ |
44 | spinlock_t lock; /* lock for this GPIO block */ |
45 | }; |
46 | |
47 | struct sch311x_gpio_priv { /* driver private data */ |
48 | struct sch311x_gpio_block blocks[6]; |
49 | }; |
50 | |
51 | struct sch311x_gpio_block_def { /* register address definitions */ |
52 | unsigned short data_reg; |
53 | unsigned short config_regs[8]; |
54 | unsigned short base; |
55 | }; |
56 | |
57 | /* Note: some GPIOs are not available, these are marked with 0x00 */ |
58 | |
59 | static struct sch311x_gpio_block_def sch311x_gpio_blocks[] = { |
60 | { |
61 | .data_reg = 0x4b, /* GP1 */ |
62 | .config_regs = {0x23, 0x24, 0x25, 0x26, 0x27, 0x29, 0x2a, 0x2b}, |
63 | .base = 10, |
64 | }, |
65 | { |
66 | .data_reg = 0x4c, /* GP2 */ |
67 | .config_regs = {0x00, 0x2c, 0x2d, 0x00, 0x00, 0x00, 0x00, 0x32}, |
68 | .base = 20, |
69 | }, |
70 | { |
71 | .data_reg = 0x4d, /* GP3 */ |
72 | .config_regs = {0x33, 0x34, 0x35, 0x36, 0x37, 0x00, 0x39, 0x3a}, |
73 | .base = 30, |
74 | }, |
75 | { |
76 | .data_reg = 0x4e, /* GP4 */ |
77 | .config_regs = {0x3b, 0x00, 0x3d, 0x00, 0x6e, 0x6f, 0x72, 0x73}, |
78 | .base = 40, |
79 | }, |
80 | { |
81 | .data_reg = 0x4f, /* GP5 */ |
82 | .config_regs = {0x3f, 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46}, |
83 | .base = 50, |
84 | }, |
85 | { |
86 | .data_reg = 0x50, /* GP6 */ |
87 | .config_regs = {0x47, 0x48, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59}, |
88 | .base = 60, |
89 | }, |
90 | }; |
91 | |
92 | /* |
93 | * Super-IO functions |
94 | */ |
95 | |
96 | static inline int sch311x_sio_enter(int sio_config_port) |
97 | { |
98 | /* Don't step on other drivers' I/O space by accident. */ |
99 | if (!request_muxed_region(sio_config_port, 2, DRV_NAME)) { |
100 | pr_err(DRV_NAME "I/O address 0x%04x already in use\n" , |
101 | sio_config_port); |
102 | return -EBUSY; |
103 | } |
104 | |
105 | outb(SIO_CONFIG_KEY_ENTER, port: sio_config_port); |
106 | return 0; |
107 | } |
108 | |
109 | static inline void sch311x_sio_exit(int sio_config_port) |
110 | { |
111 | outb(SIO_CONFIG_KEY_EXIT, port: sio_config_port); |
112 | release_region(sio_config_port, 2); |
113 | } |
114 | |
115 | static inline int sch311x_sio_inb(int sio_config_port, int reg) |
116 | { |
117 | outb(value: reg, port: sio_config_port); |
118 | return inb(port: sio_config_port + 1); |
119 | } |
120 | |
121 | static inline void sch311x_sio_outb(int sio_config_port, int reg, int val) |
122 | { |
123 | outb(value: reg, port: sio_config_port); |
124 | outb(value: val, port: sio_config_port + 1); |
125 | } |
126 | |
127 | |
128 | /* |
129 | * GPIO functions |
130 | */ |
131 | |
132 | static int sch311x_gpio_request(struct gpio_chip *chip, unsigned offset) |
133 | { |
134 | struct sch311x_gpio_block *block = gpiochip_get_data(gc: chip); |
135 | |
136 | if (block->config_regs[offset] == 0) /* GPIO is not available */ |
137 | return -ENODEV; |
138 | |
139 | if (!request_region(block->runtime_reg + block->config_regs[offset], |
140 | 1, DRV_NAME)) { |
141 | dev_err(chip->parent, "Failed to request region 0x%04x.\n" , |
142 | block->runtime_reg + block->config_regs[offset]); |
143 | return -EBUSY; |
144 | } |
145 | return 0; |
146 | } |
147 | |
148 | static void sch311x_gpio_free(struct gpio_chip *chip, unsigned offset) |
149 | { |
150 | struct sch311x_gpio_block *block = gpiochip_get_data(gc: chip); |
151 | |
152 | if (block->config_regs[offset] == 0) /* GPIO is not available */ |
153 | return; |
154 | |
155 | release_region(block->runtime_reg + block->config_regs[offset], 1); |
156 | } |
157 | |
158 | static int sch311x_gpio_get(struct gpio_chip *chip, unsigned offset) |
159 | { |
160 | struct sch311x_gpio_block *block = gpiochip_get_data(gc: chip); |
161 | u8 data; |
162 | |
163 | spin_lock(lock: &block->lock); |
164 | data = inb(port: block->runtime_reg + block->data_reg); |
165 | spin_unlock(lock: &block->lock); |
166 | |
167 | return !!(data & BIT(offset)); |
168 | } |
169 | |
170 | static void __sch311x_gpio_set(struct sch311x_gpio_block *block, |
171 | unsigned offset, int value) |
172 | { |
173 | u8 data = inb(port: block->runtime_reg + block->data_reg); |
174 | if (value) |
175 | data |= BIT(offset); |
176 | else |
177 | data &= ~BIT(offset); |
178 | outb(value: data, port: block->runtime_reg + block->data_reg); |
179 | } |
180 | |
181 | static void sch311x_gpio_set(struct gpio_chip *chip, unsigned offset, |
182 | int value) |
183 | { |
184 | struct sch311x_gpio_block *block = gpiochip_get_data(gc: chip); |
185 | |
186 | spin_lock(lock: &block->lock); |
187 | __sch311x_gpio_set(block, offset, value); |
188 | spin_unlock(lock: &block->lock); |
189 | } |
190 | |
191 | static int sch311x_gpio_direction_in(struct gpio_chip *chip, unsigned offset) |
192 | { |
193 | struct sch311x_gpio_block *block = gpiochip_get_data(gc: chip); |
194 | u8 data; |
195 | |
196 | spin_lock(lock: &block->lock); |
197 | data = inb(port: block->runtime_reg + block->config_regs[offset]); |
198 | data |= SCH311X_GPIO_CONF_DIR; |
199 | outb(value: data, port: block->runtime_reg + block->config_regs[offset]); |
200 | spin_unlock(lock: &block->lock); |
201 | |
202 | return 0; |
203 | } |
204 | |
205 | static int sch311x_gpio_direction_out(struct gpio_chip *chip, unsigned offset, |
206 | int value) |
207 | { |
208 | struct sch311x_gpio_block *block = gpiochip_get_data(gc: chip); |
209 | u8 data; |
210 | |
211 | spin_lock(lock: &block->lock); |
212 | |
213 | data = inb(port: block->runtime_reg + block->config_regs[offset]); |
214 | data &= ~SCH311X_GPIO_CONF_DIR; |
215 | outb(value: data, port: block->runtime_reg + block->config_regs[offset]); |
216 | __sch311x_gpio_set(block, offset, value); |
217 | |
218 | spin_unlock(lock: &block->lock); |
219 | return 0; |
220 | } |
221 | |
222 | static int sch311x_gpio_get_direction(struct gpio_chip *chip, unsigned offset) |
223 | { |
224 | struct sch311x_gpio_block *block = gpiochip_get_data(gc: chip); |
225 | u8 data; |
226 | |
227 | spin_lock(lock: &block->lock); |
228 | data = inb(port: block->runtime_reg + block->config_regs[offset]); |
229 | spin_unlock(lock: &block->lock); |
230 | |
231 | if (data & SCH311X_GPIO_CONF_DIR) |
232 | return GPIO_LINE_DIRECTION_IN; |
233 | |
234 | return GPIO_LINE_DIRECTION_OUT; |
235 | } |
236 | |
237 | static int sch311x_gpio_set_config(struct gpio_chip *chip, unsigned offset, |
238 | unsigned long config) |
239 | { |
240 | struct sch311x_gpio_block *block = gpiochip_get_data(gc: chip); |
241 | enum pin_config_param param = pinconf_to_config_param(config); |
242 | u8 data; |
243 | |
244 | switch (param) { |
245 | case PIN_CONFIG_DRIVE_OPEN_DRAIN: |
246 | spin_lock(lock: &block->lock); |
247 | data = inb(port: block->runtime_reg + block->config_regs[offset]); |
248 | data |= SCH311X_GPIO_CONF_OPEN_DRAIN; |
249 | outb(value: data, port: block->runtime_reg + block->config_regs[offset]); |
250 | spin_unlock(lock: &block->lock); |
251 | return 0; |
252 | case PIN_CONFIG_DRIVE_PUSH_PULL: |
253 | spin_lock(lock: &block->lock); |
254 | data = inb(port: block->runtime_reg + block->config_regs[offset]); |
255 | data &= ~SCH311X_GPIO_CONF_OPEN_DRAIN; |
256 | outb(value: data, port: block->runtime_reg + block->config_regs[offset]); |
257 | spin_unlock(lock: &block->lock); |
258 | return 0; |
259 | default: |
260 | break; |
261 | } |
262 | return -ENOTSUPP; |
263 | } |
264 | |
265 | static int sch311x_gpio_probe(struct platform_device *pdev) |
266 | { |
267 | struct sch311x_pdev_data *pdata = dev_get_platdata(dev: &pdev->dev); |
268 | struct sch311x_gpio_priv *priv; |
269 | struct sch311x_gpio_block *block; |
270 | int err, i; |
271 | |
272 | /* we can register all GPIO data registers at once */ |
273 | if (!devm_request_region(&pdev->dev, pdata->runtime_reg + GP1, 6, |
274 | DRV_NAME)) { |
275 | dev_err(&pdev->dev, "Failed to request region 0x%04x-0x%04x.\n" , |
276 | pdata->runtime_reg + GP1, pdata->runtime_reg + GP1 + 5); |
277 | return -EBUSY; |
278 | } |
279 | |
280 | priv = devm_kzalloc(dev: &pdev->dev, size: sizeof(*priv), GFP_KERNEL); |
281 | if (!priv) |
282 | return -ENOMEM; |
283 | |
284 | for (i = 0; i < ARRAY_SIZE(priv->blocks); i++) { |
285 | block = &priv->blocks[i]; |
286 | |
287 | spin_lock_init(&block->lock); |
288 | |
289 | block->chip.label = DRV_NAME; |
290 | block->chip.owner = THIS_MODULE; |
291 | block->chip.request = sch311x_gpio_request; |
292 | block->chip.free = sch311x_gpio_free; |
293 | block->chip.direction_input = sch311x_gpio_direction_in; |
294 | block->chip.direction_output = sch311x_gpio_direction_out; |
295 | block->chip.get_direction = sch311x_gpio_get_direction; |
296 | block->chip.set_config = sch311x_gpio_set_config; |
297 | block->chip.get = sch311x_gpio_get; |
298 | block->chip.set = sch311x_gpio_set; |
299 | block->chip.ngpio = 8; |
300 | block->chip.parent = &pdev->dev; |
301 | block->chip.base = sch311x_gpio_blocks[i].base; |
302 | block->config_regs = sch311x_gpio_blocks[i].config_regs; |
303 | block->data_reg = sch311x_gpio_blocks[i].data_reg; |
304 | block->runtime_reg = pdata->runtime_reg; |
305 | |
306 | err = devm_gpiochip_add_data(&pdev->dev, &block->chip, block); |
307 | if (err < 0) { |
308 | dev_err(&pdev->dev, |
309 | "Could not register gpiochip, %d\n" , err); |
310 | return err; |
311 | } |
312 | dev_info(&pdev->dev, |
313 | "SMSC SCH311x GPIO block %d registered.\n" , i); |
314 | } |
315 | |
316 | return 0; |
317 | } |
318 | |
319 | static struct platform_driver sch311x_gpio_driver = { |
320 | .driver.name = DRV_NAME, |
321 | .probe = sch311x_gpio_probe, |
322 | }; |
323 | |
324 | |
325 | /* |
326 | * Init & exit routines |
327 | */ |
328 | |
329 | static int __init sch311x_detect(int sio_config_port, unsigned short *addr) |
330 | { |
331 | int err = 0, reg; |
332 | unsigned short base_addr; |
333 | u8 dev_id; |
334 | |
335 | err = sch311x_sio_enter(sio_config_port); |
336 | if (err) |
337 | return err; |
338 | |
339 | /* Check device ID. */ |
340 | reg = sch311x_sio_inb(sio_config_port, reg: 0x20); |
341 | switch (reg) { |
342 | case 0x7c: /* SCH3112 */ |
343 | dev_id = 2; |
344 | break; |
345 | case 0x7d: /* SCH3114 */ |
346 | dev_id = 4; |
347 | break; |
348 | case 0x7f: /* SCH3116 */ |
349 | dev_id = 6; |
350 | break; |
351 | default: |
352 | err = -ENODEV; |
353 | goto exit; |
354 | } |
355 | |
356 | /* Select logical device A (runtime registers) */ |
357 | sch311x_sio_outb(sio_config_port, reg: 0x07, val: 0x0a); |
358 | |
359 | /* Check if Logical Device Register is currently active */ |
360 | if ((sch311x_sio_inb(sio_config_port, reg: 0x30) & 0x01) == 0) |
361 | pr_info("Seems that LDN 0x0a is not active...\n" ); |
362 | |
363 | /* Get the base address of the runtime registers */ |
364 | base_addr = (sch311x_sio_inb(sio_config_port, reg: 0x60) << 8) | |
365 | sch311x_sio_inb(sio_config_port, reg: 0x61); |
366 | if (!base_addr) { |
367 | pr_err("Base address not set\n" ); |
368 | err = -ENODEV; |
369 | goto exit; |
370 | } |
371 | *addr = base_addr; |
372 | |
373 | pr_info("Found an SMSC SCH311%d chip at 0x%04x\n" , dev_id, base_addr); |
374 | |
375 | exit: |
376 | sch311x_sio_exit(sio_config_port); |
377 | return err; |
378 | } |
379 | |
380 | static int __init sch311x_gpio_pdev_add(const unsigned short addr) |
381 | { |
382 | struct sch311x_pdev_data pdata; |
383 | int err; |
384 | |
385 | pdata.runtime_reg = addr; |
386 | |
387 | sch311x_gpio_pdev = platform_device_alloc(DRV_NAME, id: -1); |
388 | if (!sch311x_gpio_pdev) |
389 | return -ENOMEM; |
390 | |
391 | err = platform_device_add_data(pdev: sch311x_gpio_pdev, |
392 | data: &pdata, size: sizeof(pdata)); |
393 | if (err) { |
394 | pr_err(DRV_NAME "Platform data allocation failed\n" ); |
395 | goto err; |
396 | } |
397 | |
398 | err = platform_device_add(pdev: sch311x_gpio_pdev); |
399 | if (err) { |
400 | pr_err(DRV_NAME "Device addition failed\n" ); |
401 | goto err; |
402 | } |
403 | return 0; |
404 | |
405 | err: |
406 | platform_device_put(pdev: sch311x_gpio_pdev); |
407 | return err; |
408 | } |
409 | |
410 | static int __init sch311x_gpio_init(void) |
411 | { |
412 | int err, i; |
413 | unsigned short addr = 0; |
414 | |
415 | for (i = 0; i < ARRAY_SIZE(sch311x_ioports); i++) |
416 | if (sch311x_detect(sio_config_port: sch311x_ioports[i], addr: &addr) == 0) |
417 | break; |
418 | |
419 | if (!addr) |
420 | return -ENODEV; |
421 | |
422 | err = platform_driver_register(&sch311x_gpio_driver); |
423 | if (err) |
424 | return err; |
425 | |
426 | err = sch311x_gpio_pdev_add(addr); |
427 | if (err) |
428 | goto unreg_platform_driver; |
429 | |
430 | return 0; |
431 | |
432 | unreg_platform_driver: |
433 | platform_driver_unregister(&sch311x_gpio_driver); |
434 | return err; |
435 | } |
436 | |
437 | static void __exit sch311x_gpio_exit(void) |
438 | { |
439 | platform_device_unregister(sch311x_gpio_pdev); |
440 | platform_driver_unregister(&sch311x_gpio_driver); |
441 | } |
442 | |
443 | module_init(sch311x_gpio_init); |
444 | module_exit(sch311x_gpio_exit); |
445 | |
446 | MODULE_AUTHOR("Bruno Randolf <br1@einfach.org>" ); |
447 | MODULE_DESCRIPTION("SMSC SCH311x GPIO Driver" ); |
448 | MODULE_LICENSE("GPL" ); |
449 | MODULE_ALIAS("platform:gpio-sch311x" ); |
450 | |