xref: /openbmc/linux/drivers/char/scx200_gpio.c (revision 58e16d792a6a8c6b750f637a4649967fcac853dc)
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