1*09c434b8SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only
21da177e4SLinus Torvalds /* linux/drivers/char/scx200_gpio.c
31da177e4SLinus Torvalds
41da177e4SLinus Torvalds National Semiconductor SCx200 GPIO driver. Allows a user space
51da177e4SLinus Torvalds process to play with the GPIO pins.
61da177e4SLinus Torvalds
71da177e4SLinus Torvalds Copyright (c) 2001,2002 Christer Weinigel <wingel@nano-system.com> */
81da177e4SLinus Torvalds
9979b5ec3SJim Cromie #include <linux/device.h>
101da177e4SLinus Torvalds #include <linux/fs.h>
111da177e4SLinus Torvalds #include <linux/module.h>
121da177e4SLinus Torvalds #include <linux/errno.h>
131da177e4SLinus Torvalds #include <linux/kernel.h>
141da177e4SLinus Torvalds #include <linux/init.h>
15979b5ec3SJim Cromie #include <linux/platform_device.h>
167c0f6ba6SLinus Torvalds #include <linux/uaccess.h>
171da177e4SLinus Torvalds #include <asm/io.h>
181da177e4SLinus Torvalds
197d7f2126SJim Cromie #include <linux/types.h>
207d7f2126SJim Cromie #include <linux/cdev.h>
217d7f2126SJim Cromie
221da177e4SLinus Torvalds #include <linux/scx200_gpio.h>
23fe3a168aSJim Cromie #include <linux/nsc_gpio.h>
241da177e4SLinus Torvalds
25ae2d1f2fSJim Cromie #define DRVNAME "scx200_gpio"
26979b5ec3SJim Cromie
27979b5ec3SJim Cromie static struct platform_device *pdev;
281da177e4SLinus Torvalds
291da177e4SLinus Torvalds MODULE_AUTHOR("Christer Weinigel <wingel@nano-system.com>");
30ae2d1f2fSJim Cromie MODULE_DESCRIPTION("NatSemi/AMD SCx200 GPIO Pin Driver");
311da177e4SLinus Torvalds MODULE_LICENSE("GPL");
321da177e4SLinus Torvalds
331da177e4SLinus Torvalds static int major = 0; /* default to dynamic major */
341da177e4SLinus Torvalds module_param(major, int, 0);
351da177e4SLinus Torvalds MODULE_PARM_DESC(major, "Major device number");
361da177e4SLinus Torvalds
37ae2d1f2fSJim Cromie #define MAX_PINS 32 /* 64 later, when known ok */
38ae2d1f2fSJim Cromie
392e8f7a31SJim Cromie struct nsc_gpio_ops scx200_gpio_ops = {
40fe3a168aSJim Cromie .owner = THIS_MODULE,
41fe3a168aSJim Cromie .gpio_config = scx200_gpio_configure,
420e41ef3cSJim Cromie .gpio_dump = nsc_gpio_dump,
43fe3a168aSJim Cromie .gpio_get = scx200_gpio_get,
44fe3a168aSJim Cromie .gpio_set = scx200_gpio_set,
45fe3a168aSJim Cromie .gpio_change = scx200_gpio_change,
46fe3a168aSJim Cromie .gpio_current = scx200_gpio_current
47fe3a168aSJim Cromie };
4858012cd7SChris Boot EXPORT_SYMBOL_GPL(scx200_gpio_ops);
49fe3a168aSJim Cromie
scx200_gpio_open(struct inode * inode,struct file * file)501da177e4SLinus Torvalds static int scx200_gpio_open(struct inode *inode, struct file *file)
511da177e4SLinus Torvalds {
521da177e4SLinus Torvalds unsigned m = iminor(inode);
532e8f7a31SJim Cromie file->private_data = &scx200_gpio_ops;
54c3dc8071SJim Cromie
55ae2d1f2fSJim Cromie if (m >= MAX_PINS)
561da177e4SLinus Torvalds return -EINVAL;
571da177e4SLinus Torvalds return nonseekable_open(inode, file);
581da177e4SLinus Torvalds }
591da177e4SLinus Torvalds
scx200_gpio_release(struct inode * inode,struct file * file)601da177e4SLinus Torvalds static int scx200_gpio_release(struct inode *inode, struct file *file)
611da177e4SLinus Torvalds {
621da177e4SLinus Torvalds return 0;
631da177e4SLinus Torvalds }
641da177e4SLinus Torvalds
652e8f7a31SJim Cromie static const struct file_operations scx200_gpio_fileops = {
661da177e4SLinus Torvalds .owner = THIS_MODULE,
671a66fdf0SJim Cromie .write = nsc_gpio_write,
681a66fdf0SJim Cromie .read = nsc_gpio_read,
691da177e4SLinus Torvalds .open = scx200_gpio_open,
701da177e4SLinus Torvalds .release = scx200_gpio_release,
716038f373SArnd Bergmann .llseek = no_llseek,
721da177e4SLinus Torvalds };
731da177e4SLinus Torvalds
74c8ad9681SJim Cromie static struct cdev scx200_gpio_cdev; /* use 1 cdev for all pins */
757d7f2126SJim Cromie
scx200_gpio_init(void)761da177e4SLinus Torvalds static int __init scx200_gpio_init(void)
771da177e4SLinus Torvalds {
78635adb6cSJim Cromie int rc;
79ae2d1f2fSJim Cromie dev_t devid;
801da177e4SLinus Torvalds
811da177e4SLinus Torvalds if (!scx200_gpio_present()) {
82ae2d1f2fSJim Cromie printk(KERN_ERR DRVNAME ": no SCx200 gpio present\n");
831da177e4SLinus Torvalds return -ENODEV;
841da177e4SLinus Torvalds }
85979b5ec3SJim Cromie
86979b5ec3SJim Cromie /* support dev_dbg() with pdev->dev */
87ae2d1f2fSJim Cromie pdev = platform_device_alloc(DRVNAME, 0);
88979b5ec3SJim Cromie if (!pdev)
89979b5ec3SJim Cromie return -ENOMEM;
90979b5ec3SJim Cromie
91979b5ec3SJim Cromie rc = platform_device_add(pdev);
92979b5ec3SJim Cromie if (rc)
93979b5ec3SJim Cromie goto undo_malloc;
94979b5ec3SJim Cromie
95f31000e5SJim Cromie /* nsc_gpio uses dev_dbg(), so needs this */
962e8f7a31SJim Cromie scx200_gpio_ops.dev = &pdev->dev;
97f31000e5SJim Cromie
98ae2d1f2fSJim Cromie if (major) {
99ae2d1f2fSJim Cromie devid = MKDEV(major, 0);
100ae2d1f2fSJim Cromie rc = register_chrdev_region(devid, MAX_PINS, "scx200_gpio");
101ae2d1f2fSJim Cromie } else {
102ae2d1f2fSJim Cromie rc = alloc_chrdev_region(&devid, 0, MAX_PINS, "scx200_gpio");
103ae2d1f2fSJim Cromie major = MAJOR(devid);
1041da177e4SLinus Torvalds }
1057d7f2126SJim Cromie if (rc < 0) {
106979b5ec3SJim Cromie dev_err(&pdev->dev, "SCx200 chrdev_region err: %d\n", rc);
107979b5ec3SJim Cromie goto undo_platform_device_add;
1087d7f2126SJim Cromie }
109635adb6cSJim Cromie
1102e8f7a31SJim Cromie cdev_init(&scx200_gpio_cdev, &scx200_gpio_fileops);
111635adb6cSJim Cromie cdev_add(&scx200_gpio_cdev, devid, MAX_PINS);
1121da177e4SLinus Torvalds
1137d7f2126SJim Cromie return 0; /* succeed */
1147d7f2126SJim Cromie
115979b5ec3SJim Cromie undo_platform_device_add:
1161017f6afSIngo Molnar platform_device_del(pdev);
117979b5ec3SJim Cromie undo_malloc:
1181017f6afSIngo Molnar platform_device_put(pdev);
1191017f6afSIngo Molnar
1207d7f2126SJim Cromie return rc;
1211da177e4SLinus Torvalds }
1221da177e4SLinus Torvalds
scx200_gpio_cleanup(void)1231da177e4SLinus Torvalds static void __exit scx200_gpio_cleanup(void)
1241da177e4SLinus Torvalds {
125635adb6cSJim Cromie cdev_del(&scx200_gpio_cdev);
126635adb6cSJim Cromie /* cdev_put(&scx200_gpio_cdev); */
127635adb6cSJim Cromie
128ae2d1f2fSJim Cromie unregister_chrdev_region(MKDEV(major, 0), MAX_PINS);
129979b5ec3SJim Cromie platform_device_unregister(pdev);
1301da177e4SLinus Torvalds }
1311da177e4SLinus Torvalds
1321da177e4SLinus Torvalds module_init(scx200_gpio_init);
1331da177e4SLinus Torvalds module_exit(scx200_gpio_cleanup);
134