/*
 * LEDs driver for GPIOs
 *
 * Copyright (C) 2007 8D Technologies inc.
 * Raphael Assenat <raph@8d.com>
 * 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 <raph@8d.com>, Trent Piepho <tpiepho@freescale.com>");
MODULE_DESCRIPTION("GPIO LED driver");
MODULE_LICENSE("GPL");



Logo

开放原子开发者工作坊旨在鼓励更多人参与开源活动,与志同道合的开发者们相互交流开发经验、分享开发心得、获取前沿技术趋势。工作坊有多种形式的开发者活动,如meetup、训练营等,主打技术交流,干货满满,真诚地邀请各位开发者共同参与!

更多推荐