RK30SDK开发板驱动分析(一):platform device 的概念与注册

摘要:
好的。有了以上的理解,让我们看看LINUX中的平台设备和平台驱动程序。平台设备实际上相当于“片上和片外设备”,例如UART。当内核启动时,它不知道运行的CPU上有哪些片内和片外设备。平台设备的作用是全面定义和描述CPU的所有内部和外部设备,并将其注册到系统中。每个平台设备都有一个唯一的名称。RK30SDK使用ROCKCHIP CPU。型号为RK3066。到目前为止,我们已经看到了RK上led的平台设备的定义。总之,它描述了RK开发板上led的所有控制IO端口引脚、led名称和其他信息。那么,平台设备如何告诉内核它存在?

做过51单片机或者ARM开发的人都知道,单片机内部都有自己的“片内外设”,比如UART,比如I2C,比如SPI等等。。。

写单片机程序的时候,比如对于UART的驱动,我们都是在程序中直接写一套函数,来操作相关的UART寄存器,在程序中的其它地方调用这些函数,完成串口的收发。 在小规模的单片机程序中,这样做是再正常不过的。

但是,在规模庞大的LINUX内核中,要处理各种各样的CPU,各种各样的UART收发器,上述办法就有心无力了:没有灵活性,无法移植,接口不一致,很难想象有一个UART驱动程序能够完成这个工作。

OK,有了上面的理解,我们再来看LINUX中的platform device和platform driver。 platform device 其实就是相当于“片内外设”,比如UART,内核刚开始启动时,并不知道它所运行的CPU上面有哪些片内外设。有没有UART? 有没有I2C?等等这些问题,内核都是一无所知。而platform device的作用就是全面定义描述该CPU的所有片内外设,并注册到系统中,每一个platform device都有一个唯一的名字name。

更进一步,将“片内外设”扩展到“板载外设”,一块电路板上不止有CPU,还有很多其它设备通过GPIO,I2C/SPI等总线来连接,比如led灯,就需要一个GPIO来控制它的亮灭;比如继电器,也需要一个GPIO来控制它的开合。 GPIO控制的开关设备是最简单的,对应的驱动也是非常简单的。

RK30SDK使用了ROCKCHIP的CPU,型号是RK3066。在RK30SDK中,我们以led驱动为例,首先查找它的platform device定义,在arch/arm/mach-rk30/board-rk30-box.c文件中,有如下的定义:

static struct platform_device rk29_device_gpio_leds = {
    .name    = "leds-gpio",
    .id    = -1,
    .dev    = {
        .platform_data  = &rk29_leds_pdata,
    },
};

这个设备的名称是"leds-gpio",这个platform_data里面包含了各个不同led的名称/GPIO资源占用情况,它的定义如下:

static struct gpio_led_platform_data rk29_leds_pdata = {
    .leds = rk29_leds,
    .num_leds = ARRAY_SIZE(rk29_leds),
};

似乎有很多led,定义成了一个数组?带着疑问,我们找到rk29_leds的定义:

#ifdef CONFIG_LEDS_GPIO_PLATFORM
static struct gpio_led rk29_leds[] = {
#ifdef CONFIG_DISPLAY_KEY_LED_CONTROL
    #ifdef CONFIG_HDMI_RK30
    {
        .name = "hdmi-soc",
        .gpio = RK30_PIN4_PD7,
//        .default_trigger = "timer",
        .active_low = 0,
        .retain_state_suspended = 0,
        .default_state = LEDS_GPIO_DEFSTATE_OFF,
    },
    #endif
    #ifdef CONFIG_HDMI_ITV
    {
        .name = "hdmi-transmitter",
        .gpio = RK30_PIN4_PD2,
        .active_low = 0,
        .retain_state_suspended = 0,
        .default_state = LEDS_GPIO_DEFSTATE_OFF,
    },
    #endif

没错,这就是一个数组,只是有很多ifdef。不用管它,他们的意思是这里的源码支持在make menuconfig里面进行配置,如果将来你设计的电路板上有hdmi发送的指示灯,就可以把CONFIG_HDMI_ITV打开,这样led驱动里面就包含HDMI的发送指示灯了。gpio = RK30_PIN4_PD2这句话表示这个led灯是通过RK30_PIN4_PD2这个管脚进行控制的,如果你去看电路原理图,这个管脚一定是连着一个led灯,如果在你设计的电路板上是另外的管脚,那么这里改成你自己的管脚名称就行了。

至此,我们看完了RK上led的platform device定义了,一句话,它描述了RK开发板上led的所有控制IO口管脚,led名称等信息。实际上,这些配置代码是通过电路原理图写出来的,如果你能看懂原理图,那你也能写出这样的代码。

RK30SDK开发板驱动分析(一):platform device 的概念与注册第1张

那么,platform device是如何告知内核自己的存在呢?我们继续在board-rk30-box.c中寻找rk29_device_gpio_leds出现的地方:

static struct platform_device *devices[] __initdata = {
#ifdef CONFIG_BACKLIGHT_RK29_BL
    &rk29_device_backlight,
#endif
#ifdef CONFIG_FB_ROCKCHIP
    &device_fb,
#endif
#ifdef CONFIG_ION
    &device_ion,
#endif
#ifdef CONFIG_ANDROID_TIMED_GPIO
    &rk29_device_vibrator,
#endif
#ifdef CONFIG_LEDS_GPIO_PLATFORM
    &rk29_device_gpio_leds,
#endif

OK,看来我们的rk29_device_gpio_leds被包含在了devices数组中,而且是支持make menuconfig配置的,这是多么的合情合理啊!我们可以在menuconfig中打开或者关闭led,非常好。其它地方没有再用到rk29_device_gpio_leds这个device了,我们需要继续寻找devices数组是怎么被使用的:

static void __init machine_rk30_board_init(void)
{
    avs_init();
    gpio_request(POWER_ON_PIN, "poweronpin");
    gpio_direction_output(POWER_ON_PIN, GPIO_HIGH);
    
    pm_power_off = rk30_pm_power_off;
    
    rk30_i2c_register_board_info();
    spi_register_board_info(board_spi_devices, ARRAY_SIZE(board_spi_devices));
    platform_add_devices(devices, ARRAY_SIZE(devices));
    board_usb_detect_init(RK30_PIN6_PA3);

#ifdef CONFIG_WIFI_CONTROL_FUNC
    rk29sdk_wifi_bt_gpio_control_init();
#endif
}

哈哈,终于找到它了,看名字也知道这里是向内核注册platform device了,关于platform_add_devices的工作原理这里就不说了,网上一大把,无非就是调用另外一个函数将devices数组中的每一个device分别注册到系统中。这里上面的代码,这里先打个埋伏:platform_add_devices是注册所有的"platform"总线上的设备,"platform"是一个虚拟的总线,因为实际上这些外设并没有挂在总线上,只是通过GPIO简单的连接起来。而上面的rk30_i2c_register_board_info和spi_register_board_info则是分别注册I2C总线和SPI总线上的设备了,逻辑上来说,他们和platform总线的概念是对等的。I2C是广泛使用的,用来连接CPU和其它外部IC的总线,你所看到的各种sensor,EEPROM等都是通过I2C和CPU进行通信的。

在文件的末尾,有这样的代码:

MACHINE_START(RK30, "RK30board")
    .boot_params    = PLAT_PHYS_OFFSET + 0x800,
    .fixup        = rk30_fixup,
    .reserve    = &rk30_reserve,
    .map_io        = rk30_map_io,
    .init_irq    = rk30_init_irq,
    .timer        = &rk30_timer,
    .init_machine    = machine_rk30_board_init,
MACHINE_END

看样子,一起都是从machine_rk30_board_init函数开始的,如果我们倒回来看,就知道这个led device是如何被注册到内核里面的。

至于led device对应的platform driver,我们在下一篇分析。

免责声明:文章转载自《RK30SDK开发板驱动分析(一):platform device 的概念与注册》仅用于学习参考。如对内容有疑问,请及时联系本站处理。

上篇HandlerMapping 详解博科SAN交换机的状态查询,故障处理等方法(华为SNS系列交换机为例OEM博科)下篇

宿迁高防,2C2G15M,22元/月;香港BGP,2C5G5M,25元/月 雨云优惠码:MjYwNzM=

相关文章

MM32初识(兼容STM32)

MM32初识(兼容STM32) 资源与开发环境 keil 5.0 MM32 miniboard 提要 stm32入门(MM32兼容) 点亮LED思路简介 GPIO配置 stm32寄存器理解与操作步骤 分层抽象思想 对于数字逻辑硬件,能识别的只有二进制(0/1),我们的操作最终都会变成0/1的操作,只是我们聪明的计算机工程师在中间加入许多抽象层...

STM32自己的封装库

以前一直使用STM32的标准库,需要一步步地将代码加进去,将编译选项设置好,然后再编译整个工程。 这个编译过程是一个相当慢的过程!完全编译大约需要一支烟的时间。每次建立工程都这么编译,是一个相当浪费时间和香烟的过程。 于是,我有了将库编译成lib文件的想法。本博文就是我将STM32F4的标准库编译成lib文件并在工程中使用的过程。 适用对象: 1、熟悉库...

esp-12e折腾

寒假前就从x宝买了esp-12e以及esp32s,当时似乎是想给自己的蓝牙开门升级换代?esp32s拿来过度linux? 寒假放在书包拿回去以为有时间会玩玩,没想到一直耽搁到现在。前两天才拿出来,网上搜了搜完全不是那么会事,反映了自己购物的时候多有不理智 不知道为什么,官方最新版本里面直接把esp12e的文档给删除了,估计是设计有缺陷?只留下了esp12f...

STM32 0.96OLED I2C 显示(转载)

转载于:https://www.cnblogs.com/hjf-log/p/stm32-oled.html 使用stm32工程模板,移植其他人使用0.96寸的Oled代码,在OLED上显示字符,使用的引脚是PB3和PB4,是用了重映射的引脚 这里使用的IC是stm32f103vet6,软件是keil5 百度网盘链接:https://pan.baidu.c...

【STM32】MDK中寄存器地址名称映射分析

对于MCU,一切底层配置,最终都是在配置寄存器 51单片机访问地址 51单片机经常会引用一个reg51.h的头文件。下面看看它是怎么把名字和寄存器联系在一起的: 1 sfr p0=0x80; 2 p0=0x00; sfr是一种扩充数据类型,使用一个内存单位,值域为0-255.利用它可以访问51单片机内部所有的特殊功能寄存器。前一句“sfr p0=0x80”...

android 功耗(1)---android 功耗分析方法和优化

1、底电流调试(Rock Bottom Current Optimization) 底电流在手机飞行模式下调试。每个平台的底电流数据可能不一样,具体可以参考release出来的Current Consumption Data文档或者release note。一般情况下的底电流参考数据上限是: 底电流在手机飞行模式下调试。每个平台的底电流数据可能不一样,具体...