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