Commit | Line | Data |
---|---|---|
62c83cde | 1 | /* linux/drivers/char/scx200_gpio.c |
1da177e4 LT |
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 | ||
8 | #include <linux/config.h> | |
979b5ec3 | 9 | #include <linux/device.h> |
1da177e4 LT |
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> | |
979b5ec3 | 15 | #include <linux/platform_device.h> |
1da177e4 LT |
16 | #include <asm/uaccess.h> |
17 | #include <asm/io.h> | |
18 | ||
7d7f2126 JC |
19 | #include <linux/types.h> |
20 | #include <linux/cdev.h> | |
21 | ||
1da177e4 | 22 | #include <linux/scx200_gpio.h> |
fe3a168a | 23 | #include <linux/nsc_gpio.h> |
1da177e4 LT |
24 | |
25 | #define NAME "scx200_gpio" | |
979b5ec3 JC |
26 | #define DEVNAME NAME |
27 | ||
28 | static struct platform_device *pdev; | |
1da177e4 LT |
29 | |
30 | MODULE_AUTHOR("Christer Weinigel <wingel@nano-system.com>"); | |
31 | MODULE_DESCRIPTION("NatSemi SCx200 GPIO Pin Driver"); | |
32 | MODULE_LICENSE("GPL"); | |
33 | ||
34 | static int major = 0; /* default to dynamic major */ | |
35 | module_param(major, int, 0); | |
36 | MODULE_PARM_DESC(major, "Major device number"); | |
37 | ||
fe3a168a JC |
38 | struct nsc_gpio_ops scx200_access = { |
39 | .owner = THIS_MODULE, | |
40 | .gpio_config = scx200_gpio_configure, | |
0e41ef3c | 41 | .gpio_dump = nsc_gpio_dump, |
fe3a168a JC |
42 | .gpio_get = scx200_gpio_get, |
43 | .gpio_set = scx200_gpio_set, | |
44 | .gpio_set_high = scx200_gpio_set_high, | |
45 | .gpio_set_low = scx200_gpio_set_low, | |
46 | .gpio_change = scx200_gpio_change, | |
47 | .gpio_current = scx200_gpio_current | |
48 | }; | |
49 | ||
1da177e4 LT |
50 | static int scx200_gpio_open(struct inode *inode, struct file *file) |
51 | { | |
52 | unsigned m = iminor(inode); | |
c3dc8071 JC |
53 | file->private_data = &scx200_access; |
54 | ||
1da177e4 LT |
55 | if (m > 63) |
56 | return -EINVAL; | |
57 | return nonseekable_open(inode, file); | |
58 | } | |
59 | ||
60 | static int scx200_gpio_release(struct inode *inode, struct file *file) | |
61 | { | |
62 | return 0; | |
63 | } | |
64 | ||
65 | ||
62322d25 | 66 | static const struct file_operations scx200_gpio_fops = { |
1da177e4 | 67 | .owner = THIS_MODULE, |
1a66fdf0 JC |
68 | .write = nsc_gpio_write, |
69 | .read = nsc_gpio_read, | |
1da177e4 LT |
70 | .open = scx200_gpio_open, |
71 | .release = scx200_gpio_release, | |
72 | }; | |
73 | ||
7d7f2126 | 74 | struct cdev *scx200_devices; |
979b5ec3 | 75 | static int num_pins = 32; |
7d7f2126 | 76 | |
1da177e4 LT |
77 | static int __init scx200_gpio_init(void) |
78 | { | |
7d7f2126 JC |
79 | int rc, i; |
80 | dev_t dev = MKDEV(major, 0); | |
1da177e4 | 81 | |
1da177e4 | 82 | if (!scx200_gpio_present()) { |
7d7f2126 | 83 | printk(KERN_ERR NAME ": no SCx200 gpio present\n"); |
1da177e4 LT |
84 | return -ENODEV; |
85 | } | |
979b5ec3 JC |
86 | |
87 | /* support dev_dbg() with pdev->dev */ | |
88 | pdev = platform_device_alloc(DEVNAME, 0); | |
89 | if (!pdev) | |
90 | return -ENOMEM; | |
91 | ||
92 | rc = platform_device_add(pdev); | |
93 | if (rc) | |
94 | goto undo_malloc; | |
95 | ||
f31000e5 JC |
96 | /* nsc_gpio uses dev_dbg(), so needs this */ |
97 | scx200_access.dev = &pdev->dev; | |
98 | ||
7d7f2126 | 99 | if (major) |
979b5ec3 | 100 | rc = register_chrdev_region(dev, num_pins, "scx200_gpio"); |
7d7f2126 | 101 | else { |
979b5ec3 | 102 | rc = alloc_chrdev_region(&dev, 0, num_pins, "scx200_gpio"); |
7d7f2126 | 103 | major = MAJOR(dev); |
1da177e4 | 104 | } |
7d7f2126 | 105 | if (rc < 0) { |
979b5ec3 JC |
106 | dev_err(&pdev->dev, "SCx200 chrdev_region err: %d\n", rc); |
107 | goto undo_platform_device_add; | |
7d7f2126 | 108 | } |
979b5ec3 | 109 | scx200_devices = kzalloc(num_pins * sizeof(struct cdev), GFP_KERNEL); |
7d7f2126 JC |
110 | if (!scx200_devices) { |
111 | rc = -ENOMEM; | |
979b5ec3 | 112 | goto undo_chrdev_region; |
7d7f2126 | 113 | } |
979b5ec3 | 114 | for (i = 0; i < num_pins; i++) { |
7d7f2126 JC |
115 | struct cdev *cdev = &scx200_devices[i]; |
116 | cdev_init(cdev, &scx200_gpio_fops); | |
117 | cdev->owner = THIS_MODULE; | |
7d7f2126 | 118 | rc = cdev_add(cdev, MKDEV(major, i), 1); |
979b5ec3 | 119 | /* tolerate 'minor' errors */ |
7d7f2126 | 120 | if (rc) |
979b5ec3 | 121 | dev_err(&pdev->dev, "Error %d on minor %d", rc, i); |
1da177e4 LT |
122 | } |
123 | ||
979b5ec3 | 124 | return 0; /* succeed */ |
7d7f2126 | 125 | |
979b5ec3 JC |
126 | undo_chrdev_region: |
127 | unregister_chrdev_region(dev, num_pins); | |
128 | undo_platform_device_add: | |
1017f6af | 129 | platform_device_del(pdev); |
979b5ec3 | 130 | undo_malloc: |
1017f6af IM |
131 | platform_device_put(pdev); |
132 | ||
7d7f2126 | 133 | return rc; |
1da177e4 LT |
134 | } |
135 | ||
136 | static void __exit scx200_gpio_cleanup(void) | |
137 | { | |
7d7f2126 | 138 | kfree(scx200_devices); |
979b5ec3 | 139 | unregister_chrdev_region(MKDEV(major, 0), num_pins); |
979b5ec3 JC |
140 | platform_device_unregister(pdev); |
141 | /* kfree(pdev); */ | |
1da177e4 LT |
142 | } |
143 | ||
144 | module_init(scx200_gpio_init); | |
145 | module_exit(scx200_gpio_cleanup); |