linux/drivers/char/scx200_gpio.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0-only
   2/* linux/drivers/char/scx200_gpio.c
   3
   4   National Semiconductor SCx200 GPIO driver.  Allows a user space
   5   process to play with the GPIO pins.
   6
   7   Copyright (c) 2001,2002 Christer Weinigel <wingel@nano-system.com> */
   8
   9#include <linux/device.h>
  10#include <linux/fs.h>
  11#include <linux/module.h>
  12#include <linux/errno.h>
  13#include <linux/kernel.h>
  14#include <linux/init.h>
  15#include <linux/platform_device.h>
  16#include <linux/uaccess.h>
  17#include <asm/io.h>
  18
  19#include <linux/types.h>
  20#include <linux/cdev.h>
  21
  22#include <linux/scx200_gpio.h>
  23#include <linux/nsc_gpio.h>
  24
  25#define DRVNAME "scx200_gpio"
  26
  27static struct platform_device *pdev;
  28
  29MODULE_AUTHOR("Christer Weinigel <wingel@nano-system.com>");
  30MODULE_DESCRIPTION("NatSemi/AMD SCx200 GPIO Pin Driver");
  31MODULE_LICENSE("GPL");
  32
  33static int major = 0;           /* default to dynamic major */
  34module_param(major, int, 0);
  35MODULE_PARM_DESC(major, "Major device number");
  36
  37#define MAX_PINS 32             /* 64 later, when known ok */
  38
  39struct nsc_gpio_ops scx200_gpio_ops = {
  40        .owner          = THIS_MODULE,
  41        .gpio_config    = scx200_gpio_configure,
  42        .gpio_dump      = nsc_gpio_dump,
  43        .gpio_get       = scx200_gpio_get,
  44        .gpio_set       = scx200_gpio_set,
  45        .gpio_change    = scx200_gpio_change,
  46        .gpio_current   = scx200_gpio_current
  47};
  48EXPORT_SYMBOL_GPL(scx200_gpio_ops);
  49
  50static int scx200_gpio_open(struct inode *inode, struct file *file)
  51{
  52        unsigned m = iminor(inode);
  53        file->private_data = &scx200_gpio_ops;
  54
  55        if (m >= MAX_PINS)
  56                return -EINVAL;
  57        return nonseekable_open(inode, file);
  58}
  59
  60static int scx200_gpio_release(struct inode *inode, struct file *file)
  61{
  62        return 0;
  63}
  64
  65static const struct file_operations scx200_gpio_fileops = {
  66        .owner   = THIS_MODULE,
  67        .write   = nsc_gpio_write,
  68        .read    = nsc_gpio_read,
  69        .open    = scx200_gpio_open,
  70        .release = scx200_gpio_release,
  71        .llseek  = no_llseek,
  72};
  73
  74static struct cdev scx200_gpio_cdev;  /* use 1 cdev for all pins */
  75
  76static int __init scx200_gpio_init(void)
  77{
  78        int rc;
  79        dev_t devid;
  80
  81        if (!scx200_gpio_present()) {
  82                printk(KERN_ERR DRVNAME ": no SCx200 gpio present\n");
  83                return -ENODEV;
  84        }
  85
  86        /* support dev_dbg() with pdev->dev */
  87        pdev = platform_device_alloc(DRVNAME, 0);
  88        if (!pdev)
  89                return -ENOMEM;
  90
  91        rc = platform_device_add(pdev);
  92        if (rc)
  93                goto undo_malloc;
  94
  95        /* nsc_gpio uses dev_dbg(), so needs this */
  96        scx200_gpio_ops.dev = &pdev->dev;
  97
  98        if (major) {
  99                devid = MKDEV(major, 0);
 100                rc = register_chrdev_region(devid, MAX_PINS, "scx200_gpio");
 101        } else {
 102                rc = alloc_chrdev_region(&devid, 0, MAX_PINS, "scx200_gpio");
 103                major = MAJOR(devid);
 104        }
 105        if (rc < 0) {
 106                dev_err(&pdev->dev, "SCx200 chrdev_region err: %d\n", rc);
 107                goto undo_platform_device_add;
 108        }
 109
 110        cdev_init(&scx200_gpio_cdev, &scx200_gpio_fileops);
 111        cdev_add(&scx200_gpio_cdev, devid, MAX_PINS);
 112
 113        return 0; /* succeed */
 114
 115undo_platform_device_add:
 116        platform_device_del(pdev);
 117undo_malloc:
 118        platform_device_put(pdev);
 119
 120        return rc;
 121}
 122
 123static void __exit scx200_gpio_cleanup(void)
 124{
 125        cdev_del(&scx200_gpio_cdev);
 126        /* cdev_put(&scx200_gpio_cdev); */
 127
 128        unregister_chrdev_region(MKDEV(major, 0), MAX_PINS);
 129        platform_device_unregister(pdev);
 130}
 131
 132module_init(scx200_gpio_init);
 133module_exit(scx200_gpio_cleanup);
 134