2 * GPIO driver for NCT5104D
4 * Author: Tasanakorn Phaipool <tasanakorn@gmail.com>
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
12 #include <linux/module.h>
13 #include <linux/init.h>
14 #include <linux/platform_device.h>
16 #include <linux/gpio.h>
17 #include <linux/version.h>
18 #include <linux/dmi.h>
19 #include <linux/string.h>
21 #define DRVNAME "gpio-nct5104d"
26 #define SIO_LDSEL 0x07 /* Logical device select */
27 #define SIO_CHIPID 0x20 /* Chaip ID (2 bytes) */
28 #define SIO_GPIO_ENABLE 0x30 /* GPIO enable */
29 #define SIO_GPIO1_MODE 0xE0 /* GPIO1 Mode OpenDrain/Push-Pull */
30 #define SIO_GPIO2_MODE 0xE1 /* GPIO2 Mode OpenDrain/Push-Pull */
32 #define SIO_LD_GPIO 0x07 /* GPIO logical device */
33 #define SIO_LD_GPIO_MODE 0x0F /* GPIO mode control device */
34 #define SIO_UNLOCK_KEY 0x87 /* Key to enable Super-I/O */
35 #define SIO_LOCK_KEY 0xAA /* Key to disable Super-I/O */
37 #define SIO_NCT5104D_ID 0x1061 /* Chip ID */
38 #define SIO_PCENGINES_APU_NCT5104D_ID1 0xc452 /* Chip ID */
39 #define SIO_PCENGINES_APU_NCT5104D_ID2 0xc453 /* Chip ID */
41 enum chips
{ nct5104d
};
43 static const char * const nct5104d_names
[] = {
52 struct nct5104d_gpio_bank
{
53 struct gpio_chip chip
;
55 struct nct5104d_gpio_data
*data
;
58 struct nct5104d_gpio_data
{
59 struct nct5104d_sio
*sio
;
61 struct nct5104d_gpio_bank
*bank
;
65 * Super-I/O functions.
68 static inline int superio_inb(int base
, int reg
)
74 static int superio_inw(int base
, int reg
)
79 val
= inb(base
+ 1) << 8;
86 static inline void superio_outb(int base
, int reg
, int val
)
92 static inline int superio_enter(int base
)
94 /* Don't step on other drivers' I/O space by accident. */
95 if (!request_muxed_region(base
, 2, DRVNAME
)) {
96 pr_err(DRVNAME
"I/O address 0x%04x already in use\n", base
);
100 /* According to the datasheet the key must be send twice. */
101 outb(SIO_UNLOCK_KEY
, base
);
102 outb(SIO_UNLOCK_KEY
, base
);
107 static inline void superio_select(int base
, int ld
)
109 outb(SIO_LDSEL
, base
);
113 static inline void superio_exit(int base
)
115 outb(SIO_LOCK_KEY
, base
);
116 release_region(base
, 2);
123 static int nct5104d_gpio_direction_in(struct gpio_chip
*chip
, unsigned offset
);
124 static int nct5104d_gpio_get(struct gpio_chip
*chip
, unsigned offset
);
125 static int nct5104d_gpio_direction_out(struct gpio_chip
*chip
,
126 unsigned offset
, int value
);
127 static void nct5104d_gpio_set(struct gpio_chip
*chip
, unsigned offset
, int value
);
129 #define NCT5104D_GPIO_BANK(_base, _ngpio, _regbase) \
133 .owner = THIS_MODULE, \
134 .direction_input = nct5104d_gpio_direction_in, \
135 .get = nct5104d_gpio_get, \
136 .direction_output = nct5104d_gpio_direction_out, \
137 .set = nct5104d_gpio_set, \
142 .regbase = _regbase, \
145 #define gpio_dir(base) (base + 0)
146 #define gpio_data(base) (base + 1)
148 static struct nct5104d_gpio_bank nct5104d_gpio_bank
[] = {
149 NCT5104D_GPIO_BANK(0 , 8, 0xE0),
150 NCT5104D_GPIO_BANK(10, 8, 0xE4)
153 static int nct5104d_gpio_direction_in(struct gpio_chip
*chip
, unsigned offset
)
156 struct nct5104d_gpio_bank
*bank
=
157 container_of(chip
, struct nct5104d_gpio_bank
, chip
);
158 struct nct5104d_sio
*sio
= bank
->data
->sio
;
161 err
= superio_enter(sio
->addr
);
164 superio_select(sio
->addr
, SIO_LD_GPIO
);
166 dir
= superio_inb(sio
->addr
, gpio_dir(bank
->regbase
));
167 dir
|= (1 << offset
);
168 superio_outb(sio
->addr
, gpio_dir(bank
->regbase
), dir
);
170 superio_exit(sio
->addr
);
175 static int nct5104d_gpio_get(struct gpio_chip
*chip
, unsigned offset
)
178 struct nct5104d_gpio_bank
*bank
=
179 container_of(chip
, struct nct5104d_gpio_bank
, chip
);
180 struct nct5104d_sio
*sio
= bank
->data
->sio
;
183 err
= superio_enter(sio
->addr
);
186 superio_select(sio
->addr
, SIO_LD_GPIO
);
188 data
= superio_inb(sio
->addr
, gpio_data(bank
->regbase
));
190 superio_exit(sio
->addr
);
192 return !!(data
& 1 << offset
);
195 static int nct5104d_gpio_direction_out(struct gpio_chip
*chip
,
196 unsigned offset
, int value
)
199 struct nct5104d_gpio_bank
*bank
=
200 container_of(chip
, struct nct5104d_gpio_bank
, chip
);
201 struct nct5104d_sio
*sio
= bank
->data
->sio
;
204 err
= superio_enter(sio
->addr
);
207 superio_select(sio
->addr
, SIO_LD_GPIO
);
209 data_out
= superio_inb(sio
->addr
, gpio_data(bank
->regbase
));
211 data_out
|= (1 << offset
);
213 data_out
&= ~(1 << offset
);
214 superio_outb(sio
->addr
, gpio_data(bank
->regbase
), data_out
);
216 dir
= superio_inb(sio
->addr
, gpio_dir(bank
->regbase
));
217 dir
&= ~(1 << offset
);
218 superio_outb(sio
->addr
, gpio_dir(bank
->regbase
), dir
);
220 superio_exit(sio
->addr
);
225 static void nct5104d_gpio_set(struct gpio_chip
*chip
, unsigned offset
, int value
)
228 struct nct5104d_gpio_bank
*bank
=
229 container_of(chip
, struct nct5104d_gpio_bank
, chip
);
230 struct nct5104d_sio
*sio
= bank
->data
->sio
;
233 err
= superio_enter(sio
->addr
);
236 superio_select(sio
->addr
, SIO_LD_GPIO
);
238 data_out
= superio_inb(sio
->addr
, gpio_data(bank
->regbase
));
240 data_out
|= (1 << offset
);
242 data_out
&= ~(1 << offset
);
243 superio_outb(sio
->addr
, gpio_data(bank
->regbase
), data_out
);
245 superio_exit(sio
->addr
);
249 * Platform device and driver.
252 static int nct5104d_gpio_probe(struct platform_device
*pdev
)
256 struct nct5104d_sio
*sio
= pdev
->dev
.platform_data
;
257 struct nct5104d_gpio_data
*data
;
259 data
= devm_kzalloc(&pdev
->dev
, sizeof(*data
), GFP_KERNEL
);
265 data
->nr_bank
= ARRAY_SIZE(nct5104d_gpio_bank
);
266 data
->bank
= nct5104d_gpio_bank
;
273 platform_set_drvdata(pdev
, data
);
275 /* For each GPIO bank, register a GPIO chip. */
276 for (i
= 0; i
< data
->nr_bank
; i
++) {
277 struct nct5104d_gpio_bank
*bank
= &data
->bank
[i
];
279 bank
->chip
.parent
= &pdev
->dev
;
282 err
= gpiochip_add(&bank
->chip
);
285 "Failed to register gpiochip %d: %d\n",
294 for (i
= i
- 1; i
>= 0; i
--) {
295 struct nct5104d_gpio_bank
*bank
= &data
->bank
[i
];
297 gpiochip_remove (&bank
->chip
);
303 static int nct5104d_gpio_remove(struct platform_device
*pdev
)
306 struct nct5104d_gpio_data
*data
= platform_get_drvdata(pdev
);
308 for (i
= 0; i
< data
->nr_bank
; i
++) {
309 struct nct5104d_gpio_bank
*bank
= &data
->bank
[i
];
311 gpiochip_remove (&bank
->chip
);
317 static int __init
nct5104d_find(int addr
, struct nct5104d_sio
*sio
)
323 err
= superio_enter(addr
);
329 devid
= superio_inw(addr
, SIO_CHIPID
);
331 case SIO_NCT5104D_ID
:
332 case SIO_PCENGINES_APU_NCT5104D_ID1
:
333 case SIO_PCENGINES_APU_NCT5104D_ID2
:
334 sio
->type
= nct5104d
;
335 /* enable GPIO0 and GPIO1 */
336 superio_select(addr
, SIO_LD_GPIO
);
337 gpio_cfg
= superio_inb(addr
, SIO_GPIO_ENABLE
);
339 superio_outb(addr
, SIO_GPIO_ENABLE
, gpio_cfg
);
342 pr_info(DRVNAME
": Unsupported device 0x%04x\n", devid
);
348 pr_info(DRVNAME
": Found %s at %#x chip id 0x%04x\n",
349 nct5104d_names
[sio
->type
],
351 (int) superio_inw(addr
, SIO_CHIPID
));
353 superio_select(sio
->addr
, SIO_LD_GPIO_MODE
);
354 superio_outb(sio
->addr
, SIO_GPIO1_MODE
, 0x0);
355 superio_outb(sio
->addr
, SIO_GPIO2_MODE
, 0x0);
362 static struct platform_device
*nct5104d_gpio_pdev
;
365 nct5104d_gpio_device_add(const struct nct5104d_sio
*sio
)
369 nct5104d_gpio_pdev
= platform_device_alloc(DRVNAME
, -1);
370 if (!nct5104d_gpio_pdev
)
371 pr_err(DRVNAME
": Error platform_device_alloc\n");
372 if (!nct5104d_gpio_pdev
)
375 err
= platform_device_add_data(nct5104d_gpio_pdev
,
378 pr_err(DRVNAME
"Platform data allocation failed\n");
382 err
= platform_device_add(nct5104d_gpio_pdev
);
384 pr_err(DRVNAME
"Device addition failed\n");
387 pr_info(DRVNAME
": Device added\n");
391 platform_device_put(nct5104d_gpio_pdev
);
399 static struct platform_driver nct5104d_gpio_driver
= {
401 .owner
= THIS_MODULE
,
404 .probe
= nct5104d_gpio_probe
,
405 .remove
= nct5104d_gpio_remove
,
408 static int __init
nct5104d_gpio_init(void)
411 struct nct5104d_sio sio
;
412 const char *board_vendor
= dmi_get_system_info(DMI_BOARD_VENDOR
);
413 const char *board_name
= dmi_get_system_info(DMI_BOARD_NAME
);
415 if (nct5104d_find(0x2e, &sio
) &&
416 nct5104d_find(0x4e, &sio
))
419 err
= platform_driver_register(&nct5104d_gpio_driver
);
421 pr_info(DRVNAME
": platform_driver_register\n");
422 err
= nct5104d_gpio_device_add(&sio
);
424 platform_driver_unregister(&nct5104d_gpio_driver
);
429 subsys_initcall(nct5104d_gpio_init
);
431 static void __exit
nct5104d_gpio_exit(void)
433 platform_device_unregister(nct5104d_gpio_pdev
);
434 platform_driver_unregister(&nct5104d_gpio_driver
);
436 module_exit(nct5104d_gpio_exit
);
438 MODULE_DESCRIPTION("GPIO driver for Super-I/O chips NCT5104D");
439 MODULE_AUTHOR("Tasanakorn Phaipool <tasanakorn@gmail.com>");
440 MODULE_LICENSE("GPL");