专栏首页linux驱动个人学习多功能设备mfd驱动

多功能设备mfd驱动

一、概述

mfd是Multifunction device的简称,即多功能设备,是许多有共性的设备的集合,mfd由核心层(core)以及其下的“子设备”组成。从下文将会看到,mfd只是将设备注册到platform总线——因此,其子设备属于platform设备。它并没有对涉及到的设备或驱动做实质性改变。但是,因为某些设备的共性,所以可以在mfd中提供共同的函数给其下子设备进行调用。

本文提到的hisi_fmc驱动就是如此:

下面就分析mfd设备注册过程,并结合1个实例讲解。

内核配置(make menuconfig)信息如下:

在里面可以选中自己需要的器件;

.config文件中配置CONFIG_MFD_CORE=y

二、mfd设备添加

mfd核心代码位于drivers/mfd/mfd-core.c文件中。对外提供添加设备和删除设备的接口:mfd_add_devices、mfd_remove_devices。设备添加函数原型如下:

int mfd_add_devices(struct device *parent, int id,
            const struct mfd_cell *cells, int n_devs,
            struct resource *mem_base,
            int irq_base, struct irq_domain *domain)
  • id:即设备ID号。它指示着设备的个数。一般可以设置为-1。即表示系统有且仅有一个这样的设备。如果有多个foo设备,则需要使用id来区别。

在/sys/bus/platform/devices目录下会产生foo.0,foo.1等设备。详情可以看platform设备添加函数过程。

  • cells:即mfd_cell结构体数组,n_devs为其数组大小,即设备数量。
  • mem_base:资源resource结构体。如果没有,可置为NULL。

描述mfd设备单元称为“cell”,mfd_cell定义如下:

/*
 * This struct describes the MFD part ("cell").
 * After registration the copy of this structure will become the platform data
 * of the resulting platform_device
 */
struct mfd_cell {
    const char      *name;
    int         id;
 
    /* refcounting for multiple drivers to use a single cell */
    atomic_t        *usage_count;
    int         (*enable)(struct platform_device *dev);
    int         (*disable)(struct platform_device *dev);
 
    int         (*suspend)(struct platform_device *dev);
    int         (*resume)(struct platform_device *dev);
 
    /* platform data passed to the sub devices drivers */
    void            *platform_data;
    size_t          pdata_size;
    /*
     * Device Tree compatible string
     * See: Documentation/devicetree/usage-model.txt Chapter 2.2 for details
     */
    const char      *of_compatible;
 
    /*
     * These resources can be specified relative to the parent device.
     * For accessing hardware you should use resources from the platform dev
     */
    int         num_resources;
    const struct resource   *resources;
 
    /* don't check for resource conflicts */
    bool            ignore_resource_conflicts;
 
    /*
     * Disable runtime PM callbacks for this subdevice - see
     * pm_runtime_no_callbacks().
     */
    bool            pm_runtime_no_callbacks;
 
    /* A list of regulator supplies that should be mapped to the MFD
     * device rather than the child device when requested
     */
    const char * const  *parent_supplies;
    int         num_parent_supplies;
};

部分常见的成员介绍如下:

  • name:设备平台。
  • platform_data:平台私有数据指针,数据大小使用pdata_size表示。
  • resources:资源结构体,资源数量使用num_resources表示。
  • ignore_resource_conflicts:为true表示不检查资源冲突。
  • of_compatible:设备树匹配compatible的字符串(具体参考Documentation/devicetree/usage-model.txt Chapter 2.2)这个根据我的理解,是用于platform device的,只是写在了mfd设备上;

至此,mfd设备的添加就完成了,最终调用驱动的probe函数。从这个过程中知道,mfd实质上就是封装一个接口,将一些可以归纳到一起的platform设备注册到platform总线上。它就是一个收纳盒子。里面的设备该是怎样处理就怎样处理。

三、mfd实例

下面介绍hisi_fmc驱动的实例:

static int hisi_fmc_probe(struct platform_device *pdev)
{
    struct hisi_fmc *fmc;
    struct resource *res;
    struct device *dev = &pdev->dev;
    int ret;

    pr_err("hisi_fmc_probe successfully!\n");
    fmc = devm_kzalloc(dev, sizeof(*fmc), GFP_KERNEL);
    if (!fmc)
        return -ENOMEM;

    res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "control");
    fmc->regbase = devm_ioremap_resource(dev, res);
    if (IS_ERR(fmc->regbase))
        return PTR_ERR(fmc->regbase);

    res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "memory");
    fmc->iobase = devm_ioremap_resource(dev, res);
    if (IS_ERR(fmc->iobase))
        return PTR_ERR(fmc->iobase);

    fmc->clk = devm_clk_get(dev, NULL);
    if (IS_ERR(fmc->clk))
        return PTR_ERR(fmc->clk);

    if (of_property_read_u32(dev->of_node, "max-dma-size", &fmc->dma_len)) {
        dev_err(dev, "Please set the suitable max-dma-size value !!!\n");
        return -ENOMEM;
    }

    ret = dma_set_mask_and_coherent(dev, DMA_BIT_MASK(32));
    if (ret) {
        dev_warn(dev, "Unable to set dma mask\n");
        return ret;
    }

    fmc->buffer = dmam_alloc_coherent(dev, fmc->dma_len,
            &fmc->dma_buffer, GFP_KERNEL);
    if (IS_ERR(fmc->buffer))
        return PTR_ERR(fmc->buffer);

    mutex_init(&fmc->lock);

    platform_set_drvdata(pdev, fmc);

    ret = mfd_add_devices(dev, 0, hisi_fmc_devs,
            ARRAY_SIZE(hisi_fmc_devs), NULL, 0, NULL);
    if (ret) {
        dev_err(dev, "add mfd devices failed: %d\n", ret);
        return ret;
    }

    return 0;
}
  1. 读取fmc的reg_base、io_base;
  2. 获取最大的max-dma-size
  3. 添加mfd设备
ret = mfd_add_devices(dev, 0, hisi_fmc_devs,
            ARRAY_SIZE(hisi_fmc_devs), NULL, 0, NULL);

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • Android分区

    实现手机必需的通信功能,大家通常所的刷RADIO就是刷写modem分区,在所有适配的ROM中这部分是不动,否则会造成通话不稳定

    233333
  • Memory barrier 简介

    程序在运行时内存实际的访问顺序和程序代码编写的访问顺序不一定一致,这就是内存乱序访问。内存乱序访问行为出现的理由是为了提升程序运行时的性能。内存乱序访问主要发生...

    233333
  • Linux进程上下文切换过程context_switch详解--Linux进程的管理与调度(二十一)

    因此当前linux的调度程序由两个调度器组成:主调度器,周期性调度器(两者又统称为通用调度器(generic scheduler)或核心调度器(core sch...

    233333
  • JavaScript 对象 — 重学 JavaScript

    这里我们继续学习两个比较重要的类型,就是 Object 和 Symbol。我们主要讲的是 Object,相对 Object 来说 Symbol 只是一个配角。

    三钻
  • Python之struct简介

           看到struct这么英文单词,大家应该并不陌生,因为c/c++中就有struct,在那里struct叫做结构体。在Python中也使用struc...

    py3study
  • 【技术综述】深度学习自动构图研究报告

    今天带来基于深度学习的图像构图的研究报告,主要涉及了基于CNN的图像剪裁方法的研究现状、数据集的发展、以及现有应用。

    用户1508658
  • 连续存储数组的算法(包含数组倒置、冒泡排序……)

    爱学习的孙小白
  • 贝叶斯统计:初学指南

    在statistical inference上,主要有两派:频率学派和贝叶斯学派。

    zhuanxu
  • centos 安装frp 实现内网穿透进行微信公众号开发

    最近在整理微信公众号开发教程,我们知道在公众号开发时候,需要备案域名。在本地联调的时候需要不方便。所以就直接搭建一个内网穿透服务器。之前使用ngrok搭建了,这...

    凯哥Java
  • 论 ArrayList如何实现线程安全

    一:使用synchronized关键字 二:使用Collections.synchronizedList();         假如你创建的代码如下:List<...

    房上的猫

扫码关注云+社区

领取腾讯云代金券