/* * LEDs driver for GPIOs * * Copyright (C) 2007 8D Technologies inc. * Raphael Assenat <[email protected]> * Copyright (C) 2008 Freescale Semiconductor, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. * */ #include <linux/kernel.h> #include <linux/init.h> #include <linux/platform_device.h> #include <linux/of_platform.h> #include <linux/cdev.h> #include <asm/uaccess.h> #include <asm/io.h> #include <asm/fsl_lbc.h> #define DEV_NAME "di8" struct lbc_di_of_platform_data { struct device *pdev; void __iomem *vbase; /* Chip select base virtual address */ dev_t dev_no; struct cdev di_cdev; }; static struct lbc_di_of_platform_data *pPlatform_data; static int didev_open(struct inode *inode, struct file *file) { file->private_data = pPlatform_data; return 0; } static int didev_release(struct inode *inode, struct file *file) { return 0; } static ssize_t didev_read(struct file * file, char __user * buf, size_t size, loff_t *ppos) { struct lbc_di_of_platform_data *pdata = (struct lbc_di_of_platform_data *)(file->private_data); char result = in_8(pdata->vbase); result = ~result; if(size == 0) return 0; if(copy_to_user(buf,&result,1)) //copy_to_user: if success return 0;else return the number not copied return 0; else return 1; } static ssize_t didev_write(struct file *file, const char __user *buf, size_t size, loff_t *ppos) { return 0; } static struct file_operations didev_fops = { .owner = THIS_MODULE, .open = didev_open, .read = didev_read, .write = didev_write, .release = didev_release, }; static int lbc_di_add_cdev(struct lbc_di_of_platform_data *pdata) { int ret,add_ret; struct class *dev_class = NULL; struct device *dev_device = NULL; pdata->dev_no = 0; ret = alloc_chrdev_region(&pdata->dev_no, 0, 1, DEV_NAME); if (ret) { dev_err(pdata->pdev, "alloc_chrdev_region failed!\n"); return ret; } cdev_init(&pdata->di_cdev, &didev_fops); pdata->di_cdev.owner = THIS_MODULE; // add a character device add_ret = cdev_add(&pdata->di_cdev, pdata->dev_no, 1); if (add_ret) { dev_err(pdata->pdev, " cdev_add failed!\n"); goto PROBE_ERR; } // create the device class dev_class = class_create(THIS_MODULE, DEV_NAME); if (IS_ERR(dev_class)) { dev_err(pdata->pdev, " class_create failed!\n"); goto PROBE_ERR; } // create the device node in /dev dev_device = device_create(dev_class, NULL, pdata->dev_no,"%s", DEV_NAME); if (NULL == dev_device) { dev_err(pdata->pdev, " device_create failed!\n"); goto PROBE_ERR; } dev_info(pdata->pdev, " cdev %s add ok , MAJOR:%d MINOR:%d !\n", DEV_NAME,MAJOR(pdata->dev_no),MINOR(pdata->dev_no)); return 0; PROBE_ERR: if (dev_device) device_destroy(dev_class, pdata->dev_no); if (!IS_ERR(dev_class)) class_destroy(dev_class); if(!add_ret) cdev_del(&pdata->di_cdev); if(pdata->dev_no) unregister_chrdev_region(pdata->dev_no,1); return -1; } static int __devinit of_lbc_di_probe(struct of_device *ofdev, const struct of_device_id *match) { struct device_node *np = ofdev->node; struct lbc_di_of_platform_data *pdata; struct resource res; int ret; /* get, allocate and map the memory resource */ ret = of_address_to_resource(np , 0, &res); if (ret) { dev_err(&ofdev->dev, "failed to get resource\n"); return ret; } dev_info(&ofdev->dev, "LBC resource:0x%X -- 0x%X\n",res.start,res.end); pdata= kzalloc(sizeof(*pdata), GFP_KERNEL); if (!pdata) return -ENOMEM; pdata->pdev = &ofdev->dev; pdata->vbase = ioremap(res.start, res.end - res.start +1); if (!pdata->vbase) { dev_err(pdata->pdev, "failed to map chip region\n"); ret = -ENOMEM; goto err; } ret = lbc_di_add_cdev(pdata); if(ret) { dev_err(pdata->pdev, "failed to add_cdev.\n"); goto err; } dev_set_drvdata(&ofdev->dev, pdata); pPlatform_data = pdata; return 0; err: if(pdata->vbase) iounmap(pdata->vbase); kfree(pdata); return ret; } static int __devexit of_lbc_di_remove(struct of_device *ofdev) { return 0; } static const struct of_device_id of_lbc_di_match[] = { { .compatible = "kt,di8", }, {}, }; static struct of_platform_driver of_lbc_di_driver = { .driver = { .name = "lbc_di", .owner = THIS_MODULE, }, .match_table = of_lbc_di_match, .probe = of_lbc_di_probe, .remove = __devexit_p(of_lbc_di_remove), }; static int __init lbc_di_init(void) { return of_register_platform_driver(&of_lbc_di_driver); } static void __exit lbc_di_exit(void) { of_unregister_platform_driver(&of_lbc_di_driver); } module_init(lbc_di_init); module_exit(lbc_di_exit); MODULE_AUTHOR("Raphael Assenat <[email protected]>, Trent Piepho <[email protected]>"); MODULE_DESCRIPTION("GPIO LED driver"); MODULE_LICENSE("GPL");