Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0-only
0002 /* linux/drivers/char/scx200_gpio.c
0003 
0004    National Semiconductor SCx200 GPIO driver.  Allows a user space
0005    process to play with the GPIO pins.
0006 
0007    Copyright (c) 2001,2002 Christer Weinigel <wingel@nano-system.com> */
0008 
0009 #include <linux/device.h>
0010 #include <linux/fs.h>
0011 #include <linux/module.h>
0012 #include <linux/errno.h>
0013 #include <linux/kernel.h>
0014 #include <linux/init.h>
0015 #include <linux/platform_device.h>
0016 #include <linux/uaccess.h>
0017 #include <asm/io.h>
0018 
0019 #include <linux/types.h>
0020 #include <linux/cdev.h>
0021 
0022 #include <linux/scx200_gpio.h>
0023 #include <linux/nsc_gpio.h>
0024 
0025 #define DRVNAME "scx200_gpio"
0026 
0027 static struct platform_device *pdev;
0028 
0029 MODULE_AUTHOR("Christer Weinigel <wingel@nano-system.com>");
0030 MODULE_DESCRIPTION("NatSemi/AMD SCx200 GPIO Pin Driver");
0031 MODULE_LICENSE("GPL");
0032 
0033 static int major = 0;       /* default to dynamic major */
0034 module_param(major, int, 0);
0035 MODULE_PARM_DESC(major, "Major device number");
0036 
0037 #define MAX_PINS 32     /* 64 later, when known ok */
0038 
0039 struct nsc_gpio_ops scx200_gpio_ops = {
0040     .owner      = THIS_MODULE,
0041     .gpio_config    = scx200_gpio_configure,
0042     .gpio_dump  = nsc_gpio_dump,
0043     .gpio_get   = scx200_gpio_get,
0044     .gpio_set   = scx200_gpio_set,
0045     .gpio_change    = scx200_gpio_change,
0046     .gpio_current   = scx200_gpio_current
0047 };
0048 EXPORT_SYMBOL_GPL(scx200_gpio_ops);
0049 
0050 static int scx200_gpio_open(struct inode *inode, struct file *file)
0051 {
0052     unsigned m = iminor(inode);
0053     file->private_data = &scx200_gpio_ops;
0054 
0055     if (m >= MAX_PINS)
0056         return -EINVAL;
0057     return nonseekable_open(inode, file);
0058 }
0059 
0060 static int scx200_gpio_release(struct inode *inode, struct file *file)
0061 {
0062     return 0;
0063 }
0064 
0065 static const struct file_operations scx200_gpio_fileops = {
0066     .owner   = THIS_MODULE,
0067     .write   = nsc_gpio_write,
0068     .read    = nsc_gpio_read,
0069     .open    = scx200_gpio_open,
0070     .release = scx200_gpio_release,
0071     .llseek  = no_llseek,
0072 };
0073 
0074 static struct cdev scx200_gpio_cdev;  /* use 1 cdev for all pins */
0075 
0076 static int __init scx200_gpio_init(void)
0077 {
0078     int rc;
0079     dev_t devid;
0080 
0081     if (!scx200_gpio_present()) {
0082         printk(KERN_ERR DRVNAME ": no SCx200 gpio present\n");
0083         return -ENODEV;
0084     }
0085 
0086     /* support dev_dbg() with pdev->dev */
0087     pdev = platform_device_alloc(DRVNAME, 0);
0088     if (!pdev)
0089         return -ENOMEM;
0090 
0091     rc = platform_device_add(pdev);
0092     if (rc)
0093         goto undo_malloc;
0094 
0095     /* nsc_gpio uses dev_dbg(), so needs this */
0096     scx200_gpio_ops.dev = &pdev->dev;
0097 
0098     if (major) {
0099         devid = MKDEV(major, 0);
0100         rc = register_chrdev_region(devid, MAX_PINS, "scx200_gpio");
0101     } else {
0102         rc = alloc_chrdev_region(&devid, 0, MAX_PINS, "scx200_gpio");
0103         major = MAJOR(devid);
0104     }
0105     if (rc < 0) {
0106         dev_err(&pdev->dev, "SCx200 chrdev_region err: %d\n", rc);
0107         goto undo_platform_device_add;
0108     }
0109 
0110     cdev_init(&scx200_gpio_cdev, &scx200_gpio_fileops);
0111     cdev_add(&scx200_gpio_cdev, devid, MAX_PINS);
0112 
0113     return 0; /* succeed */
0114 
0115 undo_platform_device_add:
0116     platform_device_del(pdev);
0117 undo_malloc:
0118     platform_device_put(pdev);
0119 
0120     return rc;
0121 }
0122 
0123 static void __exit scx200_gpio_cleanup(void)
0124 {
0125     cdev_del(&scx200_gpio_cdev);
0126     /* cdev_put(&scx200_gpio_cdev); */
0127 
0128     unregister_chrdev_region(MKDEV(major, 0), MAX_PINS);
0129     platform_device_unregister(pdev);
0130 }
0131 
0132 module_init(scx200_gpio_init);
0133 module_exit(scx200_gpio_cleanup);