《Linux设备驱动开发详解》读书笔记(7)

第十九章,电源管理的系统架构与驱动

Linux的电源管理主要包括cpufreq,cpuidle, SMP hotplug, PM QoS, Suspend, Runtime PM等。

CPUfreq程序在drivers/cpufreq目录下,通过cpufreq_register_driver()注册驱动,关键函数是setpolicy()和target()。前者设置频率一个范围,后者直接设置目标频率。用户空间有一个governor,监控当前的负载,调节CPU频率。具体的policy包括ondemand, performance, conservative,powersave, userspace等。设置/sys/devices/system/cpu/cpux/cpufreq下面的governor和setspeed,即可设置策略和频率。CPUFREQ运行时会发出通知,比如CPUFREQ_NOTIFY,CPUFREQ_PRECHANGE,CPUFREQ_POSTCHANGE等。如果某设备需要关注cpufreq的变化,可以调用cpufreq_register_notifier()来接收注册。

除CPUfreq外,Linux也支持Devfreq,见drivers/devfreq目录。

CPUidle的核心是cpu_do_idle() ,一般用汇编实现,ARM中就是WFI。注册驱动使用cpuidle_regsiter_driver,注册CPU使用cpuidle_register_device。结构struct cpuidle_driver的核心是exit_latency(退出延迟)和enter()(进入的方法)。进入IDLE的governor有LADDER和MENU两种,也可以通过sysfs查看和设置。

PM QoS提供了一套接口以设定性能期望,比如开启摄像头时,可以通过pm_qos_add_request设置CPU_DMA_LATENCY,这样cpuidle的governor就会比较目前power state对应的exit latency与QoS的要求,阻止系统进入太深的状态(如C3)。

CPU的热插拔可以通过设置sysfs中的cpux/online状态来实现。在big.LITTLE架构下,Cortex-A15+Cortex-A7, 64位的Cortex-A57+Cortex-A53。两者配合以实现最优功耗。

Linux的挂机可以支持Suspend toRAM, Suspend to Disk等,一般嵌入式系统只实现STR。内核中有一个INPUT_APMPOWER驱动,代码在drivers/input/apm-power.c中,它监听EV_PWR事件,并通过apm_queue_event(APM_USER_SUSPEND)进入STR模式。在各设备驱动的structdevice_driver中,有一个dev_pm_ops的成员,定义了系统Suspend时(进入和退出)的一些回调函数,进入时有prepare, suspend, suspend_late, suspend_noirq,退出时,有resume_noirq, resume_early, resume,complete等。一般来讲,在设备驱动的挂起入口函数中,会关闭设备,关闭该设备的时钟输入,甚至关闭设备的电源,而在恢复时做相反的操作。

将Linux移植到一个新的ARM SoC时,芯片供应商需要实现platform_suspend_ops的成员函数,并用suspend_set_ops()注册。

在系统运行时,某些设备因为没有使用,也可以进入休眠模式,这通过runtime PM实现,在dev_pm_ops中有3个函数runtime_suspend(),runtime_resume(), runtime_idle()。实现的关键是对设备的使用进行计数,函数pm_runtime_get_xxx()增加计数,而pm_runtime_put_xxx()减少计数。增加计数时,设备可能唤醒,调用runtime_resume()。减少计数时,设备可能休眠,调用runtime_idle()。空闲一段时间后,就可能自动调用runtime_suspend()了。,

在消费电子中,可能有超过半数的bug都属于电源管理,很多工作就是在搞定鲁棒性和健壮性。

第二十章,芯片级移植及底层驱动

移植到一个新的SoC上,主要的工作包括定时器驱动、中断控制器、SMP启动、CPU热插拔以及底层的GPIO、时钟、pinctrl和DMA硬件封装等。

当前Linux多支持tickless或者NO_HZ,没有固定的tick。其定时器驱动为clock_event_device和clocksource。前者实现set_mode()和set_next_event()。后者主要是read()函数。在定时器中断中,调用clock_event_device的event_handler()成员函数。在NO_HZ时,使用ONESHOT模式。对于多核处理器,一般是每个核分配一个独立的定时器,各自处理自己的时钟中断。当然也可以只由CPU0处理定时器,然后通过IPI广播到其他核。ARM中1号IPI_TIMER就是负责这个广播的。

中断控制可以在ARM核上,local_irq_enable()/disable()就是直接调用CPSID/CPSIE指令执行。而enable_irq()/disable_irq()针对的是中断控制器。内核中,通过struct irq_chip描述中断控制器。中断号更多的是一个逻辑概念,具体数值不重要。目前多数ARM芯片内部的一级中断控制器都使用了ARM公司的GIC,代码不需要改动,只需要在设备树中添加相关的节点。

在多核系统中,CPU0唤醒其他CPU的动作定义在structsmp_operations中,smp_init_cpus(),然后是smp_boot_secondary()。支持热插拔,实现3个函数cpu_kill(), cpu_die(), cpu_disable()。

为了调试启动过程,需要使能DEBUG_LL和EARLY_PRINTK,实现简单的uart驱动。

GPIO驱动一般在drivers/pinctrl目录下连同pinmux一起实现。

时钟驱动定义于struct clk,其中有clk_ops,关键函数包括set_rate(), set_parent(), get_parent()等。目前一般在DTS中定义时钟树,然后通过clk API来操作所有的时钟。

Dmaengine提供了一套通用的DMA API。首先通过dmaengine_prep_dma_xxx()初始化一个具体的DMA传输描述符。然后调用dmaengine_submit()将该描述符插入到驱动的传输队列。需要传输时,使用dma_async_issue_pending()启动对应DMA通道上的传输。DMA传输完成时,回调函数会被调用。

第二十一章,设备驱动的调试

用户空间的程序可以使用GDB来调试。GDB可以配合图形界面调试工具DDD使用。内核调试可以使能KGDB,然后主机上的GDB可与目标价的KGDB通过串口或网口通信。当然也可以使用JTAG/BDM等硬件调试器。或者使用printk(), oops, strace等。要看到早期的log,需要使能DEBUG_LL和EARLY_PRINTK。

/proc可以用来与内核中实体进行通信。/sys也可以。

系统crash时,会通过Oops给出core dump,这样可以查看事发现场。

调试代码时,可以添加BUG_ON(),WARN_ON()来主动产生core dump,查看函数的call stack。

Strace绑定到一个用户空间的程序(pid)上,可以用来监视系统调用。                

如果是调试目标板上的用户程序,可以用GDB server。在目标板上运行GDB server,主机上运行GDB,两者通过串口或者网口通信。在Android上,可以使用adb forward。

在性能优化时,可以使用top, vmstat, iostat, netstat, sar等    ,也可以用sysctl查看所有内核参数或者修改参数。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值