驱动_Platform平台总线

platform

作用:实现硬件的操作方法和设备信息的分离,便于SOC控制器驱动的升级

<结构体>

platform_driver {

  struct platform_driver {

  int (*probe)(struct platform_device *); //实现初级驱动中加载函数中的代码

  (1.实例化全局对象-----kzalloc)

  (2.申请设备号 --- register_chrdev)

  (3.自动创建设备节点 ---- class_create,device_create)

  (4.拿到pdev中的资源)

  (5.初始化硬件------ ioremap)

  (6.实现file_opretions)

  int (*remove)(struct platform_device *); //实现初级驱动中卸载函数中的代码

  void (*shutdown)(struct platform_device *);   // 电源管理

  int (*suspend)(struct platform_device *, pm_message_t state);    // 电源管理

  int (*resume)(struct platform_device *);      // 电源管理

  struct device_driver driver;

  const struct platform_device_id *id_table; //与pdev中的名称匹配

}

platform_bus_type {

  const char *name;

  struct bus_attribute *bus_attrs;

  struct device_attribute *dev_attrs;

  struct driver_attribute *drv_attrs;

  int (*uevent)(struct device *dev, struct kobj_uevent_env *env);

  int (*probe)(struct device *dev);

  int (*remove)(struct device *dev);

  void (*shutdown)(struct device *dev);

  int (*suspend)(struct device *dev, pm_message_t state);

  int (*resume)(struct device *dev);

  const struct dev_pm_ops *pm;

  struct subsys_private *p;

  int (*match)(struct device *dev, struct device_driver *drv); // 匹配方法-------------↓

}                                                              ↓            

static int platform_match (struct device *dev, struct device_driver *drv){
    struct platform_device *pdev = to_platform_device(dev);
    struct platform_driver *pdrv = to_platform_driver(drv);

    /* Attempt an OF style match first */
    if (of_driver_match_device(dev, drv))
    return 1;

    /* Then try to match against the id table */
    if (pdrv->id_table)
    return platform_match_id(pdrv->id_table, pdev) != NULL;

    /* fall-back to driver name match */
    return (strcmp(pdev->name, drv->name) == 0);
  }

platform_device {

  const char * name; //名字,用于与pdrv匹配

  int id; //表示不同寄存器组的编号,一般可以填:-1

  struct device dev; //父类    ——————>  void *platform_data;    /指向自定义数据的指针

  const struct platform_device_id *id_entry;/* MFD cell pointer */

  struct mfd_cell *mfd_cell;/* arch specific additions */

  struct pdev_archdata archdata;

  u32 num_resources; //资源的个数

  struct resource * resource; //资源的详细信息----描述中断或者内存地址————↓

}                                      ↓

struct  resource {
  resource_size_t start; //如果资源为地址,则为起始地址,如果是中断,则表示中断号
  resource_size_t end; //如果资源为地址,则为最后一个字节的地址,如果是中断,则表示中断号
  const char *name;
  unsigned long flags;
  struct resource *parent, *sibling, *child;

};

<函数>

  注册:
   int platform_driver_register(struct platform_driver *);
   void platform_driver_unregister(struct platform_driver *);

   代码示例:https://www.cnblogs.com/panda-w/p/10991957.html

<笔记>

1. 总线匹配成功自动调用pdrv中的probe方法;

2. pdrv可以一对多,只要名字相同均可匹配

3. 什么时候用平台总线:------将操作和数据(资源,自定义数据)分离

  1,只要有设备地址和中断都可以用
  2,如果驱动需要咋多个平台总升级使用  

4. 查看平台总线:ls /sys/bus/platform/devices/  (内核启动自动调用,开机批量注册pdev)

5. 因为probe 和 remove都在pdrv实现,卸载驱动时,要先卸载pdev,然后再卸载pdrv

6. gpio口的寄存器初始化操作方式:   

    看原理图 ----> 看芯片手册 ------> 寄存器地址 --------> ioremap(地址映射)
    *gpc0_conf &= ~(0xff<<12);
    *gpco_conf |= 0x11<<12;

    

  1, 直接地址操作
    volatile unsigned long *gpc0conf;
    volatile unsigned long *gpc0data;

    gpc0conf = ioremap(地址, 长度);
    gpc0conf = gpc0data + 1; 

  2, 内核提供的库函数操作
    __raw_readl(地址)       __raw_writel(value, 地址);

    unsigned long value = __raw_readl(led_reg_base);
    value &= ~(0xff<<12);
    value |= (0x11<<12); //配置成输出功能
    __raw_writel(value, led_reg_base);

7. 内核提供的接口: readl/writel ------ __raw_readl       __raw_writel   

    static unsigned int __raw_readl(unsigned int ptr) //参数: ptr ----- 地址:address
    {
      return *((volatile unsigned int *)ptr);
    }

    

    static void __raw_writel(unsigned int value, unsigned int ptr) //参数: value--写入的数据,ptr---地址:address
    {
      *((volatile unsigned int *)ptr) = value;
    }

8. 编程思想:      

第一步:设计自定义数据类型:                        
  struct led_platdata{      //设计一个平台总线的自定义数据类型
   char *name;
   int shift; //移位数
   int conf_reg_data; //配置寄存器的值
   int conf_reg_clear; //配置寄存器的值清空
   int data_reg; //数据寄存器的值
  };
第二步:在pdev中初始化自定义数据
  struct led_platdata led_pdata = {
   .name = "gpc0_3/4",
   .shift = 3,
   .conf_reg_data = 0x11,
   .conf_reg_clear = 0xff,
   .data_reg = 0x3,

  };
第三步:
  struct platform_device led_pdev = {
   .name = "s5pv210_led",
   .id = -1,
   .num_resources = ARRAY_SIZE(led_resource),
   .resource = led_resource,
   .dev = {
   .platform_data = &led_pdata, //将自定义数据赋给pdev的父类中的:platform_data
   .release = led_dev_release,
  },

9. 平台总线默认内核就自动帮我们创建,使用平台总线编写驱动,将一个驱动分成两个模块:pdev,pdrv

10.  杂项设备的注册: 默认的主设备号是10  

  struct miscdevice {
   int minor; //次设备--自由定义--0-255之间
   const char *name; //设备节点的名字
   const struct file_operations *fops; //文件操作接口
   struct list_head list; //链表
  };

Stay hungry, stay foolish 待续。。。
原文地址:https://www.cnblogs.com/panda-w/p/10922753.html