Bios工程师手边事—battery

来源:互联网 发布:java 写html文件 编辑:程序博客网 时间:2024/05/17 23:35

公司的电池又出问题了。对于电池,只是解决问题的话,不会困难。但将整个电池的知识点串连起来,却不是件轻松的事。

电池部分可以分成二块来看待:

1,ECBIOS读取电池,并处理

2,SYSTEMBIOS写ASL代码,提供给驱动

附:驱动和EC沟通,报给内核

 

嵌入式BIOS部分

嵌入式BIOS工程师做电池,手边的事情一般有:

1,侦测电池插拔动作

一般有两种方式来作这个功能:

(1)通过一根GPIO PIN的电平高低来检测,需要提前去抖;

(2)通过ADC控制器来侦测电池温度,其实在软件的眼中,不管温度还是阻值或者其它什么东西,都是电压。我们可以通过电压是否在一定的范围内来确定电池是否存在。

 

2,获取电池信息

EC获取电池信息,要通过读SMART BATTERY IC的寄存器来获取。比较重要的寄存器有:

BatteryCurrent

电池电流

BatteryVoltage

电池电压

DesignCapacity

电池设计容量

RemainingCapacity

电池现有容量

FullChargeCapacity

电池满充容量

RSOC

电池现有容量百分比

CycleCount

电池跑的CYCLE数

BatteryStatus

电池状态

 

CycleCount的值代表了电池使用的程度;RemainingCapacity和FullChargeCapacity的比值就是WINDOWS显示的百分比,由于算法的原因,可能和RSOC不一致;而电池剩余时间可以通过BatteryCurrent和RemainingCapacity的比值得到。

读取这些寄存器需要遵循SMBUS协议。

 

3,电池充放电

(1)当AC和电池均存在,而batteryStatus的FULLY_CHARGED位没置1时,要充电。充电IC一般接受DAC或SMBUS的通信方式。

(2)其余情况下,不应充电。

(3)电池充放电过程中,要点亮一些指示灯给终端用户使用。

 

4,通知ACPI驱动更新电池信息

电池信息的传递需要通过62/66端口来实现。EC代码的一般做法是用256 BYTE的内存来作为ECRAM或ECSpace,HOST端驱动或应用会通过约定的时序来读取。

当电池拔出时,我们应该将ECRAM有关电池部分清0,然后通过SCI中断发送Q事件号来通知电池驱动来读取电池信息。

当电池插入时,我们不应立即通知电池驱动来获取电池信息,因为此时还没有读电池信息,我们最好要第一次读完电池信息后,再通知驱动来更新电池信息。如果不这样做,电池图标就会指示错误。SYSTEM BIOS为了避免这种情况出现,也可以利用线程SLEEP的方式来绕过这段时间。

在电池充放电过程中,我们可以实时读取RSOC的值,一旦RSOC值有变化,我们就可以通知电池驱动更新电池信息。

 

5,电池Learning功能的辅助性设计

一般来讲,主板都会设计留出电源开关来给EC使用,EC利用这些开关来决定当前系统使用交流电或是电池,EC不会主动调用这些开关,而是把它们封装做成接口提供给HOST端使用。

这种灵活的设计主要为电池Learning功能使用。我们可以通过操作系统层面上应用调用EC的接口,或是直接在BIOS层面调用EC的接口来做电池 Learning功能。

 

6,电池保护功能

电池在充放电过程中,有时会产生过压过流或过温现象,这种现象会对电池造成危害。EC工程师可以在电源工程师的设定规则下,来做一些保护功能,当达到临界点时,要停些充电或关机。

 

 

 

 

 

SYSTEM BIOS部分

SystemBIOS工程师做电池,手边的事情一般有:

1, 在DeviceEC ASL中加入电池相产信息的字段。然后在QEvent加三个事件来通知电池驱动更新电池信息。这三个事件通知的时机分别是:

(1)   电池插入

(2)   电池拔出

(3)   电池百分比改变

(4)   AC插入和拔出

电池插入和拔出的事件:

Method(_QXX)

{

     Sleep(5)        //此处防止EC BIOS代码考虑不全面

     Notify(BATT,0x80)

     Sleep(5)        //此处防止读取数据时,时序错乱

Notify(BATT,0x81)

}

电池百分比改变事件:

Method(_QYY)

{

     Notify(BATT,0x80)

}

AC插拔事件:

Method(_QZZ)

{

     Notify(ACAD,0x80)   //通知AC状态改变

     Sleep(5)       

     Notify(BATT,0x80)

     Sleep(5)       

Notify(BATT,0x81)

     Notify(EveryCPU)    //_CST,让操作系统重新决定CPU省电策略

}

2, 添加DeviceBattery ASL代码。

里面主要注意两个Method:_BIF和_BST

我们可以看到,_BST的package里有Battery Remaining Capacity字段,根据_QYY事件中的代码,我们可以推断出电池的Notify Value 0x80就是更新_BST的package。

这个需要细心和耐心,对照ACPI SPEC和EC SPACE空间,一点点地给电池驱动报数据。

值得一提的是,和SLEEP一样,为了防止数据读取错误,我们也可以在此添加Mutex。Sleep是通过线程休眠来达到延时的目的,而Mutex则是通过互斥量来阻止两段代码争资源。我也分不清谁优谁劣,不过为了防止错误发生,做项目中尽量两者兼用。

 

 

附:驱动部分

    为了搞明白我们的接口怎么被操作系统获取的,我特地读了下Linux3.18.2驱动。由于不专业,所以还请各位同仁斧正。

    有关battery部分的驱动,有两个比较重要的文件:

    Drivers\acpi\sbs.c

    Drivers\acpi\battery.c

 

    首先在sbs.c中:

我们可以看到驱动入品函数为:acpi_sbs_init(void)调用acpi_bus_register_driver(&acpi_sbs_driver),Acpi_sbs_driver.ops.add = acpi_sbs_add,Acpi_sbs_add会调用acpi_battery_add(sbs, id),acpi_battery_add调用power_supply_register(&sbs->device->dev,&battery->bat),

battery->bat.get_property= acpi_sbs_battery_get_property;

    我们知道,Linux系统中,power_supply_regiter会注册Power设备,而根文件系统显示电池相关信息是由acpi_sbs_battery_get_property()提供的,所以我们就需要在硬件相关部分,将我们读取到的信息提供给acpi_sbs_battery_get_property()所需的变量。而读取硬件信息相关代码就在battery.c中。

 

    在battery.c文件中:

    入口函数为acpi_battery_init(),调用async_schedule(acpi_battery_init_async, NULL),acpi_battery_init_async()调用acpi_bus_register_driver(&acpi_battery_driver),acpi_battery.driver.ops.add=acpi_battery_add,acpi_battery_add()中赋值battery->pm_nb.notifier_call =battery_notify,battery_notify()调用acpi_battery_get_info()和

acpi_battery_get_state(),而这两个函数会通过acpi_evaluate_object(battery->device->handle,"_BST",NULL, &buffer)

acpi_evaluate_object(battery->device->handle,name,NULL, &buffer)将读到的电池信息放到acpi_battery结构体变量中,而该结构体变量会被sbs.c的acpi_sbs_battery_get_property()使用。至于怎么读到电池信息,这部分内容会涉及到ec.c中的内容,就不再展开了。

    对于Linux驱动实在太不专业,去描述这些的时候让我想到《圣经》的马太福音第一章前几节,我就用这几节内容来结束battery内容吧。

1:1 亚伯拉罕的后裔,大卫的子孙,耶稣基督的家谱,

1:2 亚伯拉罕生以撒,以撒生雅各,雅各生犹大和他的弟兄,
1:3 犹大从她玛氏生法勒斯和谢拉,法勒斯生希斯仑,希斯仑生亚兰,
1:4 亚兰生亚米拿达,亚米拿达生拿顺,拿顺生撒门,
1:5 撒门从喇合氏生波阿斯,波阿斯从路得氏生俄备得,俄备得生耶西,
1:6 耶西生大卫王。大卫从乌利亚的妻子生所罗门,
1:7 所罗门生罗波安,罗波安生亚比雅,亚比雅生亚撒,
1:8 亚撒生约沙法,约沙法生约兰,约兰生乌西亚,
1:9 乌西亚生约坦,约坦生亚哈斯,亚哈斯生希西家,
1:10 希西家生玛拿西,玛拿西生亚们,亚们生约西亚。
1:11 百姓被迁到巴比伦的时候,约西亚生耶哥尼雅和他的弟兄。
1:12 迁到巴比伦之后,耶哥尼雅生撒拉铁,撒拉铁生所罗巴伯,
1:13 所罗巴伯生亚比玉,亚比玉生以利亚敬,以利亚敬生亚所,
1:14 亚所生撒督,撒督生亚金,亚金生以律,
1:15 以律生以利亚撒,以利亚撒生马但,马但生雅各,
1:16 雅各生约瑟,就是马利亚的丈夫。那称为基督的耶稣,是从马利亚生的。
0 0