s3c6410 GPIO的Platform驱动

项目场景:

记录linux platform驱动学习
1.选用mini6410核心板
2.使用LED作为驱动测试,LED0-3分别为GPK4-7
3.本记录使用insmod方式加载驱动,并没有加入内核中,直接在内核中加入驱动更为简单
(记录分为3步:device(平台设备),driver(设备驱动),app(应用测试))
驱动注册流程:平台设备---->设备驱动—>APP测试

step1.平台设备-device:

首先进行平台设备注册

#include <linux/interrupt.h>
#include <linux/platform_device.h>
#include <linux/miscdevice.h>
#include <linux/delay.h>
#include <asm/irq.h>
//#include <mach/regs-gpio.h>
#include <mach/hardware.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/mm.h>
#include <linux/fs.h>
#include <linux/types.h>
#include <linux/delay.h>
#include <linux/moduleparam.h>
#include <linux/slab.h>
#include <linux/errno.h>
#include <linux/ioctl.h>
#include <linux/cdev.h>
#include <linux/string.h>
#include <linux/list.h>
#include <linux/pci.h>
#include <asm/uaccess.h>
#include <asm/atomic.h>
#include <asm/unistd.h>
 
#include <mach/map.h>
#include <mach/regs-clock.h>
#include <mach/regs-gpio.h>
 
#include <plat/gpio-cfg.h>
#include <mach/gpio-bank-e.h>
#include <mach/gpio-bank-k.h>
#include <plat/devs.h>
//MAP.H内定义所有GPIO地址0x7F008000  这里因为LED是GPK  所以自己定义地址  
#define S3C64XX_PA_GPK		(0x7F008800)
#define S3C64XX_SZ_GPK		0xC
static void my_plat_gpio_release(struct device * dev);
static int my_plat_gpio_init(void);
static void my_plat_gpio_exit(void);
/* 平台资源的定义 */
static struct resource my_plat_gpio_resource[] = {
        [0] = {
                .start = S3C64XX_PA_GPK,
                .end   = S3C64XX_PA_GPK+S3C64XX_SZ_GPK-1,
                .flags = IORESOURCE_MEM,
        }
};
//资源整合
struct platform_device my_plat_gpio_device =    //此处不要定义为static的,否则会有错误
{
 .name = "my_plat_gpio",
 .id = -1,
 .num_resources   = ARRAY_SIZE(my_plat_gpio_resource),
 .resource   = my_plat_gpio_resource,
 .dev = {
        .release = my_plat_gpio_release,  },//也许可以不要   没验证
};
//释放  感觉没有实质作用
static void my_plat_gpio_release(struct device * dev)
{
    printk("remove device\n");
}
//初始化
static int my_plat_gpio_init(void)
{
    platform_device_register(&my_plat_gpio_device);
    return 0;
}
//注销
static void my_plat_gpio_exit(void)
{
    platform_device_unregister(&my_plat_gpio_device);
}


module_init(my_plat_gpio_init);
module_exit(my_plat_gpio_exit);
MODULE_LICENSE("GPL");

makefile 编译:

obj-m :=led_device.o   
KERNELDIR :=/opt/FriendlyARM/mini6410/linux/linux-2.6.38
all:  
	make -C $(KERNELDIR) M=$(PWD) modules ARCH=arm CROSS_COMPILE=arm-linux-  
clean:  
	rm -f *.o *.ko *.mod.o *.mod.c *.symvers modul*  

step2.设备驱动-driver:

设备驱动代码

#include<linux/kernel.h>
#include<linux/module.h>
#include<linux/init.h>
#include<linux/ioctl.h>
#include<linux/types.h>
#include<linux/cdev.h>
#include<linux/device.h>
#include<linux/fs.h>
#include <mach/map.h>
#include <asm/io.h>  //包含了IO内存的相关操作
#include <linux/platform_device.h>

#define DEVICE_NAME "my_plat_gpio"
#define LED_ON 1
#define LED_OFF 0
dev_t devid;
//led GPK 4 5 6 7 
volatile unsigned long *gpkcon;
volatile unsigned long *gpkdat;
volatile unsigned long *gpkup;
//open
static int leds_open(struct inode *inode , struct file *file)
{
  unsigned tmp;

  tmp=ioread32(gpkcon);
  mb();
tmp = (tmp & ~(0xffffU<<16))|(0x1111U<<16);
  iowrite32(tmp,gpkcon);

  mb();   //设置读写操作屏障,保证读写按照当前所写的顺序进行
  tmp=ioread32(gpkup);
  mb();  //设置读写操作屏障,保证读写按照当前所写的顺序进行

tmp=(tmp & ~(0xffU<<4))|(0xaaU<<4);
  iowrite32(tmp,gpkup);
  return 0;
}
//ioctl
static long leds_ioctl( struct file *filp,unsigned int cmd,
                        unsigned long arg)
                     
{
  unsigned tmp;

  if(arg>4) return -EINVAL;
  tmp = ioread32(gpkdat);
  mb();
printk("my platform led:%d,on:%d\n",arg,cmd);
printk("tmp:0x%x,gpkdat:0x%x\n",tmp,gpkdat);
  switch(cmd)
  {
    case LED_ON:  tmp &= ~(1<<(arg+4)); iowrite32(tmp,gpkdat);
                  return 0;
    case LED_OFF: tmp |= 1<<(arg+4); iowrite32(tmp,gpkdat);
                  return 0;
    default: return -EINVAL;
  }
}
//接口定义
static struct file_operations leds_ops = {
    .owner = THIS_MODULE,
    .open = leds_open,
    .unlocked_ioctl = leds_ioctl,
};
static struct cdev *cdev_led;
static struct class *led_class;
static struct resource *ret;
//探测
static int led_probe(struct platform_device *pdev)
{   
    int msize,val;
    ret = platform_get_resource(pdev,IORESOURCE_MEM, 0);
    msize = ret->end - ret->start + 1;
   printk("ret->start:0x%x,msize:0x%x\n",ret->start,msize);
    if(!request_mem_region(ret->start,msize,DEVICE_NAME)) 
	printk("request con error\n");
    else 
    {
	gpkcon=ioremap_nocache(ret->start,msize);
	printk("request con success \ngpkcon:0x%x\n",gpkcon);
    }

    gpkdat = gpkcon + 2;
    gpkup = gpkcon + 3;
printk("gpkdat:0x%x,gpkup:0x%x\n",gpkdat,gpkup);
    val = alloc_chrdev_region(&devid,0,1,DEVICE_NAME);
      if(val) return -1;

    cdev_led = cdev_alloc();
      cdev_init(cdev_led,&leds_ops);
      val = cdev_add(cdev_led,devid,1);
      if(val)
      {
            printk(KERN_INFO "Add device led error!\n");
            return -1;
      }
 
      led_class = class_create(THIS_MODULE,DEVICE_NAME);
      device_create(led_class,NULL,devid,NULL,"%s",DEVICE_NAME);

    printk(KERN_INFO "LED Initilized I'm in! ^_^ \n");
      return 0;
}
//平台设备驱动移除
static int led_remove(struct platform_device *pdev)
{
    iounmap(gpkcon);
      release_mem_region(ret->start,(ret->end - ret->start + 1));
      cdev_del(cdev_led);
      device_destroy(led_class,devid);
      class_destroy(led_class); 
      unregister_chrdev_region(devid, 1);
    return 0;
}
//平台设备资源接口
struct platform_driver led_driver = {
    .probe = led_probe,
    .remove = led_remove,
    .driver = {
        .owner    = THIS_MODULE,
        .name = DEVICE_NAME,
    },
};
//平台设备驱动注册
static int __init leddrv_init(void)
{
    platform_driver_register(&led_driver);
    return 0;
}
//平台设备驱动注销
static void __exit leddrv_exit(void)
{
    platform_driver_unregister(&led_driver);
}

module_init(leddrv_init);
module_exit(leddrv_exit);

MODULE_LICENSE("GPL");

makefile 编译:

obj-m :=led_driver.o   
KERNELDIR :=/opt/FriendlyARM/mini6410/linux/linux-2.6.38
all:  
	make -C $(KERNELDIR) M=$(PWD) modules ARCH=arm CROSS_COMPILE=arm-linux-  
clean:  
	rm -f *.o *.ko *.mod.o *.mod.c *.symvers modul*  

step3.应用测试:

APP代码:

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/ioctl.h>
//arm-linux-gcc led.c -o led
int main(int argc, char **argv)
{
int on;
int led_no;
int fd;
/* 检查led控制的两个参数,如果没有参数输入则退出。*/
if (argc != 3 || sscanf(argv[1], "%d", &led_no) != 1 || sscanf(argv[2],"%d", &on) != 1 ||
on < 0 || on > 1 || led_no < 0 || led_no > 3) {
fprintf(stderr, "Usage: leds led_no 0|1\n");
exit(1);
}
/*打开/dev/leds设备文件*/
fd = open("/dev/my_plat_gpio", 0);
if (fd < 0) {
perror("my gpio");
exit(1);
}
printf("open dev success\n");
/*通过系统调用ioctl和输入的参数控制led*/
ioctl(fd, led_no, on);
/*关闭设备句柄*/
close(fd);
return 0;
}

arm-linux-gcc led.c -o led 编译生成led应用程序
点亮led0 ./led 1 0
因为是普通IO驱动修改而来, cmd \arg顺序相反,arg不能写2问题有需要自行将2个参数互换,测试通过就懒得改了.

软、硬件解释:

1、
leds_ioctl、leds_open:
因为LED使用GPK4\5\6\7
根据手册所示,寄存器设置为代码所示
在这里插入图片描述
2、
设备驱动:led_probe内:

volatile unsigned long *gpkcon;
volatile unsigned long *gpkdat;
volatile unsigned long *gpkup;
    gpkdat = gpkcon + 2;
    gpkup = gpkcon + 3;

根据寄存器所示,指针+2+3时地址分别为对应寄存器

gpkcon=ioremap_nocache(ret->start,msize);

将0x7F008800寄存器地址映射到ram中的一块线性地址访问.和io_remap有些类似

3、
首先insmod led_device.ko 进行平台设备注册
然后 insmod led_driver.ko 安装设备驱动
最后 ./led 1 1 点亮led1 测试完成

通过终端查看 ls /sys/bus/platform/device

在这里插入图片描述
通过终端查看 ls /sys/bus/platform/driver
在这里插入图片描述
这里都具有my_plat_gpio则说明已经将驱动与平台设备关联成功

通过终端查看 ls /dev发现有my_plat_gpio节点
因为动态分配设备设备号,所以此处不需要mknod再生成节点
应用程序直接调用此节点即可
在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值