linux_module_learn/platform_device/plat_drv.c

239 lines
5.8 KiB
C

#include <linux/module.h>
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/sched.h>
#include <linux/cdev.h>
#include <linux/fs.h>
#include <linux/wait.h>
#include <linux/errno.h>
#include <linux/slab.h>
#include <linux/uaccess.h>
#include <linux/device.h>
#include <linux/io.h>
#include <linux/platform_device.h>
struct led_chrdev {
unsigned int __iomem *va_dr;
unsigned int __iomem *va_gdir;
unsigned int __iomem *va_iomuxc_mux;
unsigned int __iomem *va_ccm_ccgrx;
unsigned int __iomem *va_iomux_pad;
unsigned int led_pin;
unsigned int clock_offset;
struct cdev dev;
dev_t my_devno;
};
static struct class *led_class = NULL;
static int led_open(struct inode *inode, struct file *filp)
{
uint32_t val = 0;
struct led_chrdev *my_led_dev = NULL;
printk("led open %d:%d...\n",MAJOR(inode->i_rdev),MINOR(inode->i_rdev));
my_led_dev = container_of(inode->i_cdev, struct led_chrdev, dev);
filp->private_data = my_led_dev;
val = ioread32(my_led_dev->va_ccm_ccgrx);
val &= ~(3 << my_led_dev->clock_offset);
val |= 3 << my_led_dev->clock_offset;
iowrite32(val, my_led_dev->va_ccm_ccgrx);
iowrite32(5, my_led_dev->va_iomuxc_mux);
iowrite32(0x1F838, my_led_dev->va_iomux_pad);
val = ioread32(my_led_dev->va_gdir);
val &= ~(1 << my_led_dev->led_pin);
val |= (1 << my_led_dev->led_pin);
iowrite32(val, my_led_dev->va_gdir);
val = ioread32(my_led_dev->va_dr);
val |= (0x01 << my_led_dev->led_pin);
iowrite32(val, my_led_dev->va_dr);
return 0;
}
static ssize_t led_read(struct file *filp, char __user *buf, size_t cnt, loff_t *offt)
{
return cnt;
}
static ssize_t led_write(struct file *filp,const char __user *buf,size_t cnt, loff_t *offt)
{
uint32_t val = 0;
uint32_t ret = 0;
struct led_chrdev *my_led_dev = NULL;
printk("led write...\n");
my_led_dev = filp->private_data;
kstrtoul_from_user(buf,cnt,10,&ret);
//printk("ret = %u\n",ret);
val = ioread32(my_led_dev->va_dr);
if (ret)
val &= ~(0x01 << my_led_dev->led_pin);
else
val |= (0x01 << my_led_dev->led_pin);
iowrite32(val, my_led_dev->va_dr);
*offt += cnt;
return cnt;
}
static int led_release(struct inode *inode, struct file *filp)
{
return 0;
}
static struct file_operations led_fops = {
.owner = THIS_MODULE,
.open = led_open,
.read = led_read,
.write = led_write,
.release = led_release,
};
static struct platform_device_id led_pdev_ids[] = {
{.name = "led_pdev"},
{}
};
MODULE_DEVICE_TABLE(platform, led_pdev_ids);
int led_plat_probe(struct platform_device *pdev)
{
uint32_t val = 0;
int ret = 0;
dev_t devno = 0;
struct led_chrdev *my_led_dev = NULL;
unsigned int *led_hwinfo;
struct resource *mem_dr;
struct resource *mem_gdir;
struct resource *mem_iomuxc_mux;
struct resource *mem_ccm_ccgrx;
struct resource *mem_iomux_pad;
led_hwinfo = dev_get_platdata(&(pdev->dev));
mem_dr = platform_get_resource(pdev,IORESOURCE_MEM,0);
mem_gdir = platform_get_resource(pdev,IORESOURCE_MEM,1);
mem_iomuxc_mux = platform_get_resource(pdev,IORESOURCE_MEM,2);
mem_ccm_ccgrx = platform_get_resource(pdev,IORESOURCE_MEM,3);
mem_iomux_pad = platform_get_resource(pdev,IORESOURCE_MEM,4);
my_led_dev = devm_kzalloc(&(pdev->dev),sizeof(struct led_chrdev), GFP_KERNEL);
if(!my_led_dev)
return -ENOMEM;
my_led_dev->led_pin = led_hwinfo[0];
my_led_dev->clock_offset = led_hwinfo[1];
my_led_dev->va_ccm_ccgrx = ioremap(mem_ccm_ccgrx->start,4);
my_led_dev->va_dr = ioremap(mem_dr->start,4);
my_led_dev->va_gdir = ioremap(mem_gdir->start,4);
my_led_dev->va_iomuxc_mux = ioremap(mem_iomuxc_mux->start,4);
my_led_dev->va_iomux_pad = ioremap(mem_iomux_pad->start,4);
ret = alloc_chrdev_region(&devno, pdev->id, 1, "led");
if(ret < 0){
printk(KERN_ERR "chrdev alloc region failed\n");
return ret;
}
printk(KERN_INFO "devno is %u(major:%u,minor:%u)\n",devno,MAJOR(devno), MINOR(devno));
my_led_dev->my_devno = devno;
cdev_init(&my_led_dev->dev, &led_fops);
my_led_dev->dev.owner = THIS_MODULE;
ret = cdev_add(&my_led_dev->dev, devno, 1);
if(ret < 0){
printk(KERN_ERR "chrdev add failed\n");
goto add_err;
}
device_create(led_class,NULL,devno,NULL,"led_%s",(pdev->id == 0)?"red":((pdev->id == 1)? "green":"blue"));
platform_set_drvdata(pdev, my_led_dev);
return 0;
add_err:
unregister_chrdev_region(my_led_dev, 1);
return ret;
}
int led_plat_remove(struct platform_device *pdev)
{
dev_t cur_dev;
struct led_chrdev *my_led_dev = platform_get_drvdata(pdev);
printk("led platform driver remove\n");
iounmap(my_led_dev->va_ccm_ccgrx);
iounmap(my_led_dev->va_dr);
iounmap(my_led_dev->va_gdir);
iounmap(my_led_dev->va_iomuxc_mux);
iounmap(my_led_dev->va_iomux_pad);
cur_dev = my_led_dev->my_devno;
cdev_del(&my_led_dev->dev);
device_destroy(led_class, cur_dev);
unregister_chrdev_region(cur_dev, 1);
return 0;
}
static struct platform_driver led_pdrv = {
.probe = led_plat_probe,
.remove = led_plat_remove,
.driver.name = "led_pdev",
.id_table = led_pdev_ids,
};
static int __init led_plat_drv_init(void)
{
printk("led platform driver init\n");
led_class = class_create(THIS_MODULE, "led_class");
if(IS_ERR(led_class))
{
printk(KERN_ERR" create class faild!/n");
return -EBUSY;
}
platform_driver_register(&led_pdrv);
return 0;
}
static void __exit led_plat_drv_exit(void)
{
printk("led platform driver exit\n");
platform_driver_unregister(&led_pdrv);
class_destroy(led_class);
}
module_init(led_plat_drv_init);
module_exit(led_plat_drv_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("liuchao");