X-Git-Url: http://pilppa.org/gitweb/gitweb.cgi?a=blobdiff_plain;f=drivers%2Fchar%2Fscx200_gpio.c;h=99e5272e3c53e1ef1aa32f0733b1bbf0e9f2487e;hb=ab521dc0f8e117fd808d3e425216864d60390500;hp=507a5bf567cbf61d7dce71e5b3bd1946df8e3ddc;hpb=7d7f212661d68d231fdfaead03dda0dd44d5bbbb;p=linux-2.6-omap-h63xx.git diff --git a/drivers/char/scx200_gpio.c b/drivers/char/scx200_gpio.c index 507a5bf567c..99e5272e3c5 100644 --- a/drivers/char/scx200_gpio.c +++ b/drivers/char/scx200_gpio.c @@ -5,12 +5,13 @@ Copyright (c) 2001,2002 Christer Weinigel */ -#include +#include #include #include #include #include #include +#include #include #include @@ -18,83 +19,39 @@ #include #include +#include -#define NAME "scx200_gpio" +#define DRVNAME "scx200_gpio" + +static struct platform_device *pdev; MODULE_AUTHOR("Christer Weinigel "); -MODULE_DESCRIPTION("NatSemi SCx200 GPIO Pin Driver"); +MODULE_DESCRIPTION("NatSemi/AMD SCx200 GPIO Pin Driver"); MODULE_LICENSE("GPL"); static int major = 0; /* default to dynamic major */ module_param(major, int, 0); MODULE_PARM_DESC(major, "Major device number"); -extern void scx200_gpio_dump(unsigned index); - -static ssize_t scx200_gpio_write(struct file *file, const char __user *data, - size_t len, loff_t *ppos) -{ - unsigned m = iminor(file->f_dentry->d_inode); - size_t i; - - for (i = 0; i < len; ++i) { - char c; - if (get_user(c, data + i)) - return -EFAULT; - switch (c) { - case '0': - scx200_gpio_set(m, 0); - break; - case '1': - scx200_gpio_set(m, 1); - break; - case 'O': - printk(KERN_INFO NAME ": GPIO%d output enabled\n", m); - scx200_gpio_configure(m, ~1, 1); - break; - case 'o': - printk(KERN_INFO NAME ": GPIO%d output disabled\n", m); - scx200_gpio_configure(m, ~1, 0); - break; - case 'T': - printk(KERN_INFO NAME ": GPIO%d output is push pull\n", m); - scx200_gpio_configure(m, ~2, 2); - break; - case 't': - printk(KERN_INFO NAME ": GPIO%d output is open drain\n", m); - scx200_gpio_configure(m, ~2, 0); - break; - case 'P': - printk(KERN_INFO NAME ": GPIO%d pull up enabled\n", m); - scx200_gpio_configure(m, ~4, 4); - break; - case 'p': - printk(KERN_INFO NAME ": GPIO%d pull up disabled\n", m); - scx200_gpio_configure(m, ~4, 0); - break; - } - } - - return len; -} - -static ssize_t scx200_gpio_read(struct file *file, char __user *buf, - size_t len, loff_t *ppos) -{ - unsigned m = iminor(file->f_dentry->d_inode); - int value; - - value = scx200_gpio_get(m); - if (put_user(value ? '1' : '0', buf)) - return -EFAULT; +#define MAX_PINS 32 /* 64 later, when known ok */ - return 1; -} +struct nsc_gpio_ops scx200_gpio_ops = { + .owner = THIS_MODULE, + .gpio_config = scx200_gpio_configure, + .gpio_dump = nsc_gpio_dump, + .gpio_get = scx200_gpio_get, + .gpio_set = scx200_gpio_set, + .gpio_change = scx200_gpio_change, + .gpio_current = scx200_gpio_current +}; +EXPORT_SYMBOL_GPL(scx200_gpio_ops); static int scx200_gpio_open(struct inode *inode, struct file *file) { unsigned m = iminor(inode); - if (m > 63) + file->private_data = &scx200_gpio_ops; + + if (m >= MAX_PINS) return -EINVAL; return nonseekable_open(inode, file); } @@ -104,66 +61,70 @@ static int scx200_gpio_release(struct inode *inode, struct file *file) return 0; } - -static struct file_operations scx200_gpio_fops = { +static const struct file_operations scx200_gpio_fileops = { .owner = THIS_MODULE, - .write = scx200_gpio_write, - .read = scx200_gpio_read, + .write = nsc_gpio_write, + .read = nsc_gpio_read, .open = scx200_gpio_open, .release = scx200_gpio_release, }; -struct cdev *scx200_devices; -int num_devs = 32; +static struct cdev scx200_gpio_cdev; /* use 1 cdev for all pins */ static int __init scx200_gpio_init(void) { - int rc, i; - dev_t dev = MKDEV(major, 0); - - printk(KERN_DEBUG NAME ": NatSemi SCx200 GPIO Driver\n"); + int rc; + dev_t devid; if (!scx200_gpio_present()) { - printk(KERN_ERR NAME ": no SCx200 gpio present\n"); + printk(KERN_ERR DRVNAME ": no SCx200 gpio present\n"); return -ENODEV; } - if (major) - rc = register_chrdev_region(dev, num_devs, "scx200_gpio"); - else { - rc = alloc_chrdev_region(&dev, 0, num_devs, "scx200_gpio"); - major = MAJOR(dev); + + /* support dev_dbg() with pdev->dev */ + pdev = platform_device_alloc(DRVNAME, 0); + if (!pdev) + return -ENOMEM; + + rc = platform_device_add(pdev); + if (rc) + goto undo_malloc; + + /* nsc_gpio uses dev_dbg(), so needs this */ + scx200_gpio_ops.dev = &pdev->dev; + + if (major) { + devid = MKDEV(major, 0); + rc = register_chrdev_region(devid, MAX_PINS, "scx200_gpio"); + } else { + rc = alloc_chrdev_region(&devid, 0, MAX_PINS, "scx200_gpio"); + major = MAJOR(devid); } if (rc < 0) { - printk(KERN_ERR NAME ": SCx200 chrdev_region: %d\n", rc); - return rc; - } - scx200_devices = kzalloc(num_devs * sizeof(struct cdev), GFP_KERNEL); - if (!scx200_devices) { - rc = -ENOMEM; - goto fail_malloc; - } - for (i = 0; i < num_devs; i++) { - struct cdev *cdev = &scx200_devices[i]; - cdev_init(cdev, &scx200_gpio_fops); - cdev->owner = THIS_MODULE; - cdev->ops = &scx200_gpio_fops; - rc = cdev_add(cdev, MKDEV(major, i), 1); - /* Fail gracefully if need be */ - if (rc) - printk(KERN_ERR NAME "Error %d on minor %d", rc, i); + dev_err(&pdev->dev, "SCx200 chrdev_region err: %d\n", rc); + goto undo_platform_device_add; } - return 0; /* succeed */ + cdev_init(&scx200_gpio_cdev, &scx200_gpio_fileops); + cdev_add(&scx200_gpio_cdev, devid, MAX_PINS); + + return 0; /* succeed */ + +undo_platform_device_add: + platform_device_del(pdev); +undo_malloc: + platform_device_put(pdev); -fail_malloc: - unregister_chrdev_region(dev, num_devs); return rc; } static void __exit scx200_gpio_cleanup(void) { - kfree(scx200_devices); - unregister_chrdev_region(MKDEV(major, 0), num_devs); + cdev_del(&scx200_gpio_cdev); + /* cdev_put(&scx200_gpio_cdev); */ + + unregister_chrdev_region(MKDEV(major, 0), MAX_PINS); + platform_device_unregister(pdev); } module_init(scx200_gpio_init);