[openharmony]L2系统SPI的实现原理

摘要

本文基于Hi3516DV300开发板进行分析,此开发板使用的是海思的一款基于双核Cortex-A7的soc,提供了3路SPI总线(但是开发板上没有对外提供SPI接口,需要使用的话就需要飞线)。由于工作需要使用SPI连接了一个外设芯片进行调试,在调试过程中学习了Openharmony在L2上SPI实现的原理

DT和HDF

在分析SPI总线实现原理之前,先了解一下linux系统以及openharmony下对硬件描述最为关键的几个概念

linux系统

  • DT(Device Tree),设备树是描述硬件的一种数据结构,来源于OF(OpenFirmware)。当前广泛应用于linux系统中。
  • DTS(Device Tree Source),DT的源文件,也可以称为易于人理解的硬件配置文件
  • DTB(Device Tree Blob),DTS被编译成二进制形式,方便linux内核中读取
  • DTC(Device Tree Compiler),将DTS编译成DTB的工具,类似gcc,也提供反编译功能

openharmony

  • HDF(Hardware Driver Foundation)是一种驱动框架,为驱动开发者提供驱动框架能力,包括驱动加载、驱动服务管理和驱动消息机制。旨在构建统一的驱动架构平台,为驱动开发者提供更精准、更高效的开发环境,力求做到一次开发,多系统部署。(来自openharmony官网文档
  • HCS(HDF Configuration Source)是HDF驱动框架的配置描述源码,内容以Key-Value为主要形式。它实现了配置代码与驱动代码解耦,便于开发者进行配置管理。(来自openharmony官网文档
  • HCB(HDF Configuration Binary),HCS被编译成二进制形式,方便Openharmony驱动框架读取
  • HC-GEN(HDF Configuration Generator),将HCS编译成HCB的编译工具

对比说明

从前面两节的几个概念描述看,Openharmony中实现了一套与linux系统下类似的硬件描述结构

linux openharmony 说明
DT HDF 硬件描述的结构
DTS HCS 硬件结构描述的源码
DTB HCB 硬件结构描述的源码编译后的二进制文件
DTC HC-GEN 将硬件结构描述源码转换为二进制文件的编译工具

对比来看两者的功能基本上类似,Openharmony重新实现一套机制的好处是将Openharmony下的L0/L1/L2系统(分别对应的是liteos-m/liteos-a/linux)统一起来。但是个人理解其实还有一种实现方式,就是将linux下的dt移植到openharmony的L0/L1系统,而L2完全使用linux默认的DT,这样可能对于开发人员的学习成本会低很多(只是个人理解)。

然而还有一个问题,在分析SPI实现原理的过程中发现,openharmony的L2系统中其实DT和HDF是共存的,并不是替换的关系(只是在L0/L1上是独有的)。并且两边都有对SPI的配置及解析,两者是怎么配合的呢,下面一步步来分析一下。

linux下SPI总线的配置及实现

从前面描述看SPI总线的配置也是在DTS中,对于Hi3568DV300开发板来说对应的dts文件路径为:

linux-5.10/arch/arm/boot/dts/hi3516dv300.dtsi
linux-5.10/arch/arm/boot/dts/hi3516dv300-demb.dts

但是在源码中发现并没有这两个文件,而是在编译好版本之后在输出目录out/KERNEL_OBJ/kernel/src_tmp/可以找到,原因就是这两个文件不是linux自带的,而是在编译的时候给内核打补丁出现的,参见patch文件kernel/linux/patches/linux-5.10/hi3516dv300_patch/hi3516dv300.patch

dtsi中SPI配置如下(三路SPI配置类似):

        spi_bus1: spi@120c1000 {
            compatible = "arm,pl022", "arm,primecell";
            arm,primecell-periphid = <0x00800022>;
            reg = <0x120c1000 0x1000>, <0x12030000 0x4>;
            interrupts = <0 69 4>;
            clocks = <&clock HI3516DV300_SPI1_CLK>;
            clock-names = "apb_pclk";
            #address-cells = <1>;
            #size-cells = <0>;
            num-cs = <2>;
            hisi,spi_cs_sb = <2>;
            hisi,spi_cs_mask_bit = <0x4>;//0100
#ifdef CONFIG_HIEDMACV310
            dmas = <&hiedmacv310_0 29 29>, <&hiedmacv310_0 28 28>;
            dma-names = "tx","rx";
#endif
            status = "disabled";
        };

而在dts文件中将status改为OK,如下

&spi_bus1{
    status = "okay";
    num-cs = <2>;

    spidev@0 {
        compatible = "rohm,dh2228fv";
        reg = <0>;
        pl022,interface = <0>;
        pl022,com-mode = <1>;
        spi-max-frequency = <25000000>;
    };
    spidev@1 {
        compatible = "rohm,dh2228fv";
        reg = <1>;
        pl022,interface = <0>;
        pl022,com-mode = <0>;
        spi-max-frequency = <25000000>;
    };
};

在启动时就会将spi_dev挂到设备树中。

从配置的compatible字段看,SPI总线驱动使用的是arm内核一种公共的SPI驱动pl022,驱动文件路径为kernel/linux/linux-5.10/drivers/spi/spi-pl022.c ,主要接口配置如下:

  static int pl022_probe(struct amba_device *adev, const struct amba_id *id)
  {
      ......
      master->cleanup = pl022_cleanup;
      master->setup = pl022_setup;
      master->auto_runtime_pm = true;
      master->transfer_one_message = pl022_transfer_one_message;
      master->unprepare_transfer_hardware = pl022_unprepare_transfer_hardware;
      ......
  }

而linux系统对SPI总线提供了一个统一的接口,放在kernel/linux/linux-5.10/drivers/spi/spi.c中。

Openharmony下SPI总线的配置及实现

配置

HDF驱动开发方法可以先看下官网文档

HCS配置文件路径:

vendor/<company>/<product>/hdf_config/khdf/device_info/device_info.hcs
vendor/<company>/<product>/hdf_config/khdf/platform/hi35xx_spi_config.hcs

在HDF下SPI在platform-host下,如下

        platform :: host {
            hostName = "platform_host";
            priority = 50;
            ......
            device_spi :: device {
                ......
                device1 :: deviceNode {
                    policy = 1;
                    priority = 60;
                    permission = 0644;
                    moduleName = "HDF_PLATFORM_SPI";
                    serviceName = "HDF_PLATFORM_SPI_1";
                    deviceMatchAttr = "hisilicon_hi35xx_spi_1";
                }
                ......
            }
        ......
        }

SPI私有配置

root {
    platform {
        spi_config {
            template spi_controller {
                serviceName = "";
                match_attr = "";
                busNum = 0;
                numCs = 0;
            }

            controller_0x120c0000 :: spi_controller {
                busNum = 0;
                numCs = 1;
                match_attr = "hisilicon_hi35xx_spi_0";
            }

            controller_0x120c1000 :: spi_controller {
                match_attr = "hisilicon_hi35xx_spi_1";
                busNum = 1;
                numCs = 2;
            }

            controller_0x120c2000 :: spi_controller {
                match_attr = "hisilicon_hi35xx_spi_2";
                busNum = 2;
                numCs = 1;
            }
        }
    }
}

HDF下SPI总线驱动代码

代码路径为drivers/adapter/khdf/linux/platform/spi/hi35xx_spi_adapter.c
提供了下面几个接口,并将spi_driver加入到HDF框架下面

struct SpiCntlrMethod g_method = {
    .Transfer = SpiAdatperTransfer,
    .SetCfg = SpiAdatperSetCfg,
    .GetCfg = SpiAdatperGetCfg,
    .Open = SpiAdatperOpen,
    .Close = SpiAdatperClose,
};
  • open接口,通过linux的接口bus_for_each_dev获取挂在设备树中的SPI结构体地址并赋给struct SpiCntlr的指针变量

    static int32_t SpiAdatperOpen(struct SpiCntlr *cntlr)
    {
        ......
        ret = bus_for_each_dev(&spi_bus_type, NULL, (void *)cntlr, SpiFindDeviceFromBus);
        ......
        return HDF_SUCCESS;
    }
    

    当找到对应设备之后,会调用回调接口SpiFindDeviceFromBus,如果是第一次调用需要创建一个HDF的spi_dev挂在HDF下,并初始化配置信息。

    static int32_t SpiFindDeviceFromBus(struct device *dev, void *para)
    {
        ......
        if (spidev->master->bus_num == cntlr->busNum && spidev->chip_select == cntlr->curCs) {
            spi = SpiFindDeviceByCsNum(cntlr, cntlr->curCs);
            if (spi == NULL) {
                spi = SpiDevCreat(cntlr);
            }
            ......
            SpiDevInit(spi, spidev);
            return SPI_DEV_FIND_SUCCESS;
        } else {
            put_device(&spidev->dev);
            return SPI_DEV_NEED_FIND_NEXT;
        }
    }
    
  • setcfg接口,先将用户的配置信息转换成linux下spi配置格式,然后调用linux下spi统一接口spi_setup

    static int32_t SpiAdatperSetCfg(struct SpiCntlr *cntlr, struct SpiCfg *cfg)
    {
        ......
        spidev->mode = HdfSpiModeToLinuxMode(cfg->mode);
        ret = spi_setup(spidev);
        ......
    }
    

    文件kernel/linux/linux-5.10/drivers/spi/spi.cspi_setup接口调用具体的spi总线驱动的setup接口

    int spi_setup(struct spi_device *spi)
    {
        ......
        if (spi->controller->setup)
            status = spi->controller->setup(spi);
        ......
    
        return status;
    }
    

    前面信息可知hi3516dv300使用的是spi-pl022.c,具体的功能实现接口为pl022_setup接口,就是对spi总线寄存器的操作。

  • transfer接口,先将用户的msg转换成transfer,再调用linux提供的统一接口spi_sync

    注意:Openharmony的HDF中stransfer/msg的概念与linux下正好相反。LInux下一个spi_message包含一组transfer,一个tansfer表示一次传输;HDF用一个SpiMsg表示一次传输,多次传输组合用SpiMsg数组。

    static int32_t SpiAdatperTransfer(struct SpiCntlr *cntlr, struct SpiMsg *msg, uint32_t count)
    {
        ......
        spi_message_init(&xfer);
        for (i = 0; i < count; i++) {
            // 将用户的message转换成transfer
            spi_message_add_tail(&transfer[i], &xfer);
        }
    
        ret = spi_sync(dev->priv, &xfer);
        kfree(transfer);
        return ret;
    }
    

    文件kernel/linux/linux-5.10/drivers/spi/spi.cspi_sync接口最终调用具体总线驱动的接口``

    int spi_sync(struct spi_device *spi, struct spi_message *message)
    {
        int ret;
    
        mutex_lock(&spi->controller->bus_lock_mutex);
        ret = __spi_sync(spi, message);
        mutex_unlock(&spi->controller->bus_lock_mutex);
    
        return ret;
    }
    static int __spi_sync(struct spi_device *spi, struct spi_message *message)
    {
        ......
                __spi_pump_messages(ctlr, false);
        ......
    }
    
    static void __spi_pump_messages(struct spi_controller *ctlr, bool in_kthread)
    {
        ......
        ret = ctlr->transfer_one_message(ctlr, msg);
    }
    

    前面信息可知hi3516dv300使用的是spi-pl022.c,具体的功能实现接口为pl022_transfer_one_message接口,就是对spi总线寄存器的操作。

Openharmony下SPI总线的使用

从前面分析来看,Openharmony下SPI总线驱动最终调用的还是linux下对应的具体的驱动实现。这样HDF与DT之间就建立起了关联。而在Openharmony下SPI总线又给其他驱动提供了统一的驱动接口,路径为:

drivers/framework/support/platform/src/spi/spi_if.c
drivers/framework/support/platform/src/spi/spi_core.c

从这两个源文件分析,实际就是从HDF下查找HDF_PLATFORM_SPI,从上一节分析看,这个就是drivers/adapter/khdf/linux/platform/spi/hi35xx_spi_adapter.c的实现。
这样调用顺序就是

图片1.png

总结

在Openharmony的L2下使用SPI总线,其实最底层还是调用的linux下具体的SPI总线驱动代码。而对于SPI-slave设备使用SPI总线通信时:

  • 需要确认linux下对应的DTS的配置
  • 需要确认HDF下对应的HCS的配置
  • 调用Openharmony提供的统一接口(文件spi-if.c

这样后续移植代码到Openharmony的L1/L0时就比较简单了。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 217,406评论 6 503
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,732评论 3 393
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 163,711评论 0 353
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,380评论 1 293
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,432评论 6 392
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,301评论 1 301
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,145评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,008评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,443评论 1 314
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,649评论 3 334
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,795评论 1 347
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,501评论 5 345
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,119评论 3 328
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,731评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,865评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,899评论 2 370
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,724评论 2 354

推荐阅读更多精彩内容