关键词不能为空

当前您在: 主页 > 英语 >

我所知道的EC

作者:高考题库网
来源:https://www.bjmy2z.cn/gaokao
2021-02-13 12:05
tags:

-

2021年2月13日发(作者:jersey)


我所知道的


EC====>


ower Sequence



1. What’s Power Sequence ?


+ w8 f* m, 4 K9 Y0 {4 H3 x









Power


Sequence


是指


HW


Device


上电的顺序


,


它的大致顺序如下:

< br>/


v+


s#


G& {: Z' J4 ~, S3 o



1)ALWAYS


! }



P6 m* [2 h! U, v% u


2)SUS_ON3)DIMM_ON


6 h


4)RUN_ON


- R# g9 U: p!


t( k


5)VR_ON


) Z3 W( k2 S' v8 k/ I, {' C




这基本上是


NB


工作需要的所有

< p>
POWER



插入


AC< /p>


或者


DC


后,


机 器内部的


开启的电为


ALWAYS


电, 主要用以保证


EC


的正常运行;系统正常工作进入


os


以后,


所有的


Powe r


都开启。


完整的过程其实是这样的:


AC


或者


DC


插入以后,


EC


Reset


开始跑


c ode


,用户按下


Power


Swi tch


时,


EC


SUS_ON


送给一颗


POWER


IC,+3VSUS,+5VSUS


起来后,


POWER


IC



EC


SUS_OK


。接下来


EC


发给南桥一 个称为


?RSMRST#?


的信号。这时候南桥的部分功能开始 初始化并等


待开机信号。这时候的南桥并没有打开全部电源,只有很少一部分的功能可用 ,


比如供检测开机信号的


PWRBTN#


信号。


,


在用户按下


Power


键的时候,


EC


检测


到一个中断,然后


duplicate


一个开机信号(


PWRBTN#


)给南桥,南桥收到


PWRB TN#


信号后依次拉高


SLP_S5#,SLP_S4#,SL P_S3#


信号,开启了所有的


POWER

,在


V_CORE


电起来后,


IC


会发送


ALLSYSPWOK


信号给< /p>


EC


这信号


表明外围电源正常开启。南桥 会发出


PCI


RST#


信号到


PCI


总线,于是总线上的


设备都被初始化( 包括北桥)


,然后


CPU


RST#< /p>


信号送出,


HOST


正式接手并开


始工作。


/


至此,上电时序完成。

< p>
4 t- O& f% Q



C7 J



: l/ f, I9 r



@



2. Why Power Sequence




need s equence



(


个人猜测,欢迎指 教


?


)



为什么需要


Power


Sequence


呢?没有


Power


Sequence


直接把所有的电压都供


上可以开机吗?答案是


YES


。直接上电开机是没有问题的, 可是没有办法达成


省电的功能;另外


S3/S4,Long R un


也会有问题。系统進入待机的时候


(S3)


,机器


内部的电只有


SUS&DIMM


电,主要是提供


DDR


的电压,以保证


RAM


内部的


资料不丢失(


S 3


是将数据


Store


to


Ram


,在


Resume

< br>之后


OS



RAM


中获得数


据,所以


DDR


的 电一定要留着)


,而


S4


和关机


(S5)


的电是一样的,都是


Always< /p>



(



AC


在的时候其实


SUS


电要留着,

< p>
目的是加速


Resume


或者

Power On


的速



)



0 _- e) Q6 7 O



J9 m



II.S4



S5


共用一个信号?


# M3 H3 M6 v, F( A3 _# S




做过


EC


的同志可能会发现,


线路图上


S4,S5


的信号只会有一个。

< p>
根据我们的


使用经验


S4



S5


有很大的差异。原因其实是因为


S4,S5


都只有


ALWAYS


电,从


EC


的角度,


根本没有必要区分


S4,S5


所以


SB


只有一个信号拉到


EC



. m; W0 B: q+ {)


我所知道的


EC====>System Architecture



1



EC


功能概述



E C



NB


独有的组成部分。它将


MB


上面的


keyboard



mouse


集中于一体在,


EC


内部提供了


KBC


控制器统一控制 它们,这样大大节省了空间实现了便携。


NB


的另一个特点是它 需要电池供电,这样省电是一个重要的问题。


EC


配合


chipset



S3



S4



S5


分别供不同的电源策略以达到节省的目的,另外


EC


也会


控制电池的冲放电的过程,检测电池电量用于决定是否待机


or


紧急关机



or Wakeup


等。另外由于


NB


集成度高、空间受限


,


散热非常重要但同时还要兼顾


噪音的问题,


EC


会通过


thermal sensor< /p>


获得当前的


CPU


VGA


温度,


根据不同


的温度自动 调节风扇的转速。这些都是


EC


提供的功能。

< br>7 T0 F- D: M1 Y



2

< br>.常见


HW


架构



b6 l9 `5 l3 D% Y; o



现在


NB


HW


线路系统中


EC


的常见架构有两种,比较旧一点的案子会将< /p>


BIOS



EC



code


放在一起挂在


EC



X-BUS


下面。而新一些的做法会将


BIOS



EC


分开 ;


BIOS


挂在


SB


LPC


或者


SPI


下面而


EC


挂在


EC


的< /p>


SPI


下面。如下图


所示:


0 A5 R% c/ g6 c0 l% R2 ^






























那么为 什么会有两种架构呢?他们有什么区别呢?听我慢慢道来


?


zz z


。这其实


是个多方原因导致的结果。


1.


出于成本的考虑。大家都知道现在


NB

的价格战是


愈演愈烈,大家都在拼成本。所以低端机种都在拼命的

< br>cost down.


很明显第一种


架构也就是图


1


左边的架构会省掉一颗


IC


的成本。


那么大家可能又会问既然这么


economi c


为什么还要导入第二种架构呢?


2.


出于性能的考虑



有所得就有所失


”< /p>


没有完美的方案,


第一种架构存在一个致命得缺陷,


就是


BIOS



EC



code



会抢总 线,也就是说有可能某一段时间


BIOS


狂飙得时候,


EC


就卡住了,反之


亦然。于是就发现


POST


时间过长,


S3



S4 Resume


时间过长的问题了。于是乎

< p>
第二种架构应运而生;所以在新机种以及那些高性能的


game

< p>
系列中就会采用这


种架构。


PS


:现在应该以第二种架构为主了)


# Y+ {( }1 q: r



3



EC


如何与


HOST


通信


# d- [) s5 y* C



由图< /p>


1


可以看出


EC



SB


通过


LPC

相连,所以


EC



HOST


端的通信主要是通



LPC BUS


进行的,除此之外


EC


还会有

< br>SMI



SCI



pin


拉到


SB


上也就是说


EC


也可以通过发


SCI



SMI


的中断通知


Chi pset, Chipset


再从


LPC BUS


获得相关



EVENT


(< /p>


PS


:后续的会详细讨论这部分)



LPC BUS


通信的部分如下图所示:




1 U& h



B1 d9 W9 T( t& ~0 i! y




< /p>


总之


EC



NB


系统中最为底层的部件,只要有电


EC


就会工作,检测各种输入


信息,同时它还负责提供


power sequence



host


通信等重 要的工作。


(



我所知道的


EC====>LID


< /p>


1.


What



s lid?



Lid


是盖子的意思, 在


NB


上其实就是指


Panel


打开和关闭的过程。不知道有没


有人试过用磁石去碰

< br>NB


的底边缘,试过就会发现一碰


NB

< br>就会进


S3



S4


当然


也可能什么动作都没有(取决于


OS

< p>
的配置)


。其实这部分是因为大多


NB

< p>
都是


通过一颗


magnet switch IC


控制


lid



Lid


有两种状态


Open



Close HW


的线路


通常还会将它们用作背 光的始能信号


8 q8 {: `; |( B8 M



2.


Lid Open



Lid Open


就是通常上讲的开盖,


HW


会将这支信号拉到


EC


一根


GPIO pin


上,


也就是说一旦


Lid


状态有变化


EC


这端就会收到中断


, EC


收到中断后会发

< br>SCI


通知


host



host


再去做其他的动作。可是通常第一次开机的时候


Panel


是打开


的,


NB< /p>


并没有供电所以


EC


没法更新

< p>
lid


的状态,这时


EC


就会在进入


S0


的时


候根据

< p>
lid


这根


GPIO


的状 态(


H&L


)去更新


lid

< p>
的状态。在


OS


装载的过程中会

< br>主动去


call BIOS


中的


asl code


去更新


Lid


的状态。




3.


Lid Close


4 z2 z9 {



Lid Close


指的是合盖,


EC


处理的过程和


Lid Open


基本一 致,


区别之处在于


EC


记录

< p>


Lid


的状态有变化。另外在

< br>OS


下有一个选项可以设置


Lid Close



OS



以进行的动作 。如下图所示





如上图


1


所示


close l id


可以选择的


action


有三种< /p>




1. Do nothing



) n3 V$$ S& W' c



A; f1 {. t' ^



2. Stand by


]0 d3 P) I9 m# P# ?* w( A+ I



3. Hibernat


X; i) l! i! s! K2 y



这些功能是怎么实现的呢?完整的流程如下图所示:












* d4 }% T8 {+ {(





2


就是完整的工作过程,步骤


5


是猜的。要知 道真正的做法恐怕只有问了微软


才知道


?




我所知道的


EC====>Ke yboard


- B- u( W$$ E0 ]% B# f8 j



rd Introduction



Keyboard



PC


架构中的一个重要组成部分。


在常见的


PC


系统中主板上都有一颗专用的


8042


接口芯片 去处理


(


现在被集成进了


SB



)



8042


控制


keyboard


的整个工作过程,包括 加电自


检键盘扫描码的缓冲以及与


chipset


沟通。在


NB


上这部分工作都有

EC


负责,它有一个


keyboard controll er


,它扮演


8042


相似的角色。< /p>


NB


都有一个内置


Keyboard


,这个


keyboard


是由


EC


控制的。


Keyboard


touchpad


都是


EC


内置的一个部分,它们按照


ps2


协议工作 ,


最终的数据通过


EC


送给

< p>
host


。常见的


102 key


的键盘如下图


1


所示:




2



3 0 Y8 A4 f. c6 p1 N


Scan code and Make & Break


1 [) H. {: `* S( D0 R& v



- m1 h- }5 f8 r# e




当键盘上有键被按下,

< p>
键盘将产生扫描码



scan code




scan code


有两种


Make code



Break


code



也就是通常所说的通码和断码。


每一个按 键都有一个唯一的


Make code



Break code


。< /p>


当一个键被按下就会产生


Make code


,松开时就会产生一个


Break code



Scan code


一共有


三套称之为


set1



set2



set3



PS2


接口键盘默认使用


set2

< p>


EC


收到


set2 scan code


以后会


将它转化为


set1


送给


host



Set1



scan code


中标准按键的


Scan code Make code



Break


code


都只有一个字节,


Make code



BreakCode


的差别 就在最高位。


Make code


最高位为

0



Break code


最高位 为


1




A



scan code


如下图


1


所示


:


A key



Set1



Set2



Set3






$$ g6 D; U5 K, T6 ]9 R



2


Make code



1Eh



1Ch



1Ch



Break code



$$ F



x6 }



9Eh



F0h,1Ch



F0h,1Ch



Does Keyboard Work?



Key board


功能虽然比较简单,


只是让用户可以输入一些字符而已,


可是它的工作原理却不简


单。


从一个键被按下到操作系 统识别它并送给其他的


driver


或者


AP



中间经历了很多道工序。


键盘 是一种矩阵结构,


每一个键都有一个行地址和列地址,


用户按下 键以后


,EC


获得该按键的


matri x address



EC


将该


address


转化为


matrix val ue


然后判断该键的类型是特殊功能键还


是标准按键,


然后采用不同的方法将


matrix value


转成


Set2



最后在转成


Set1 value


送给


host

< p>


host


收到就可以送给其它需要的程序了。其 完整的工作流程如下图


3


所示:



























4



Customized


- Q2 |- ?6 N. h! x, s) S



Hot Key




NB


上有一些被称为


Hot Key< /p>


(热键)的东东,比如用户可以按


Fn+F4/F5


调整亮度等。这些


是如何实现的呢?既然


key board


部分是由


EC FW


处理, 那定制这些特殊功能键就不是什么


难事了。


Fn


没有


scan code


但是它有


matrix address


所以


EC


收到该键按下后置一个

flag



后续检测到


F1-F1 2


被按下后,


EC


发一个


Q_EVENT


(什么是


Q_EVENT?


后续会详细描述


?




Host,Host


就可以和


EC


通信了。如此便可以定制出各种各样的功能了。


+ k- e& z& N9 G8 }+ n



% u9 c/ H) I5 Y



5



6 N, t* B



K. S! j4 ^


IO Port Command



Host


通过


60h



64h


这两个


ports


和< /p>


Keyboard


进行通信,其中


60h


被称为数据端口,


64h


< p>
命令端口。


Host



E C


发命令是通过


64h port


实现的命令分别为:



Command



EDh



Description



设置


LED



Keyboard

< br>收到此命令后,一个


LED


设置会话开始。


Keyboard


首先回复一个


ACK



FAh



,然后等待从< /p>


60h


端口写入的


LED


设置字节,


如果等到一个,


则再次回复一个

< p>
ACK



然后根据此


字节 设置


LED



然后接着等待。




直到等到一个非


L ED


设置字节


(


位被设置


)


,此时


LED


设置会话结束。



EEh



诊断


Echo


。此命令纯粹为了检测


Keyboard


是否正常,如果正常,



Keyboard


收到此命令后,将会回复一个


EEh


字节。



选择


Scan


code


set


< br>Keyboard


系统共可能有


3



Scan


code


set




Keyboard

收到此命令后,将回复一个


ACK


,然后等待一个来自



60h


端口的


S can code set


代码。系统必须在此命令之后发送



Keyboard


一个


Scan code set


代码。当


Keyboard

< br>收到此代码


后,将再次回复一个


ACK

< br>,然后将


Scan code set


设置为收到的


Scan code set


代码所要求的。



读取


Keyboard


ID


。由于


EC


芯片后不仅仅能够接


Keyboard


。此命


令是为了读取后所接的设备< /p>


ID



设备


ID



2


个字节,


Keyboard


ID



83ABh


。当键盘收到此命令后,会首先回复一个


ACK


,然后,将


2


字节的


Keyb oard ID


一个一个回复回去。



设置


Typematic Rate/Delay


。当


Keyboard


收到此命令后,将回


复一个


ACK



然后等 待来自于


60h


的设置字节。


一旦收到 ,


将回复


一个


ACK

< br>,然后将


Keyboard Rate/Delay


设置为相应的值。



F0h



F2h



F3h



/ V% h7 p3 R' O! C




F4h



清理键盘的


Output


Buffe r


。一旦


Keyboard


收到此命令 ,将会将


Output


buffer


清空,


然后回复一个


ACK



然后继续接受


Keyboard


的击键。



设置默认状态


(w/Disable)


。一旦


Keyboard


收到此命令,将会 将


Keyboard


完全初始化成默认状态。

< br>之前所有对它的设置都将失效


——


Output buffer


被清空,


Typematic Rate/Del ay


被设置成默


认值。然后回复一个


A CK


,接着等待下一个命令。需要注意的是,


这个命令被执行后 ,键盘的击键接受是禁止的。如果想让键盘接


受击键输入,必须


Enable Keyboard




设置默认状态。和


F5


命令唯一不同的是,当此命令被执行之后 ,


键盘的击键接收是允许的。



Res end



如果


Keyboard


收到此命令,


则必须将刚才发送到


Outpu t


Register


F5h



F6h



FEh



中的数据重新发送一遍。当系 统检测到一个来自于


Keyboard


的错误之后,

< p>
可以使用自命令让


Keyboard


重新发送刚才 发送的字节。



Reset


Keyb oard


。如果


Keyboard


收到 此命令,则首先回复一个


ACK


,然后


FFh



启动自身的


Reset


程序,


并进行自身基本正确性检测



BAT-Basic


Assurance


T est



。等这一切结束之后,将返回给系统一个单字节的结束 码



AAh=Success, FCh=Failed



,并将键盘的


Scan code set


设置为


2




20h



准备读取芯片的


Command Byte


;其行为是将当前


Command Byte


的内容放置于


Output Register


中,下一个从


60H


端口的读操作

< p>
将会将其读取出来。



准备写入

< br>EC


芯片的


Command


B yte



下一个通过


60h

< p>
写入的字节将


会被放入


Command Byte




测试一下键盘密码是否被设置;测试结果放置在


Output


Register


,然后可以通过


60 h


读取出来。测试结果可以有两种值:


FAh=


密码被设置;


F1h=


没有密码。


设置键盘密码。其结果被按照顺序通过


60h

< p>
端口一个一个被放置



Input Regist er


中。密码的最后是一个空字节(内容为


0

< br>)




让密码生效。在发布这个 命令之前,必须首先使用


A5h


命令设置


密码。



自检。诊断结果放置在


Output


Register


中,可以通过


60h


读取。


55h=OK




禁止键盘接口。


Command Byte


bit-4


被设置。当此命令被发布

后,


Keyboard


将被禁止发送数据到


Output Register




打开键盘接口。


Command Byte


bit-4


被清除。当此命令被发布

后,


Keyboard


将被允许发送数据到


Output Register




准备读取


Input Port



Input Port


的内容被放置于


Output


Register


中,随后可以通过


60h

< br>端口读取。



准备读取


Outp ort


端口。结果被放在


Output Register


中,随后


通过


60h


端口读取出来。



准备写


Outpu t


端口。随后通过


60h


端口写入的字 节,会被放置在


Output Port


中。



准备写数据到


Output Register


中。随后通过


60h


写入到


I nput


Register


的字节会被放入到


Output


Register


中,此功能被用来< /p>


模拟来自于


Keyboard


发送的数据 。


如果中断被允许,


则会触发一


个中断 。



60h



A4h



A5h



A6h



AAh



ADh



AEh



C0h



D0h



D1h



D2h



% Y8 f1 Q0 X' O3 c



.



上面的表格就是


EC


支持的全部的


c ommand



那么如何向


EC


发一个命令呢?在向端口


60h


< p>
64h


写任何信息之前,


EC

输入缓冲区必须为空。读取


64h


获得状态,然后检查


bit1


,如果是


0


表示


buffer


为空可写,否则为满不能写入。

< p>


Example




+ H# P( U* Q- ^9 q/ h2 Y


in


2 8 y5 J5 Q- P



al,64h




test


al,2


* N$$ s5 c5 M' _/ U



R



jz


( n1 W2 X1 ?) T



H$$ i9 G


. m; N



send_cmd


1 X, L, {# p3 h5 Q2 U( f' W/ e



ret


send_cmd:



mov


bl,adh


3 a$$ v3 b7 ^/ H



a0 w0 d8 Q



out 64h,bl



如何从


EC


端读取数据呢?读取任何信息之前 ,


必须检查控制器输出缓冲区状态,


以确定可以


读取一个字节。读取


64h bit0


如果是


1


表示


buffer


为 满可读,否则为空不能读取。



Example




1 k; ]6 h+ s



in


/ U& o0 Q. t



al



64h


4 m/ p1 O; j( O



test




al



1




s6 ], 5 M



jnz


read_key_ready


9 r4 h# N5 ]7 [$$ [; M) d! P



ret


4 e



, t



read_key_ready:


! C8 F9 ]! : a- L' ~



in


% P* Z$$ b9 X1 g# `+ J



al,60h


( i7 4 X& h: v5 _




Status Register


( 状态寄存器)


的状态位如下所述:



Bit7: PARITY- EVEN(P_E):


从键盘获得的数据奇偶校验错误



Bit6: RCV-TMOUT(R_T):


接收超时,置


1


) M: c i0 d* p- z& O' i* R2 v6 v



Bit5: TRANS_TMOUT(T_T):


发送超时,置


1


4 Y' s! ]& O. L6 Q K9 P



Bit4: KYBD_INH(K_I):



1


键盘没有被禁止。为


0


键盘被禁止。


! C5 `1 f( M+ @% s1 ~2 O) U5 v



Bit3: CMD_DATA(C_D):



1


输入缓冲器中的内容为命令,


0

< br>输入缓冲器中的内容为数据。



Bit2: SYS_FLAG(S_F):


系统标志,加电启动置


0


,自检通过后置


1


Bit1: INPUT_BUF_FULL(I_B_F):


输入缓冲器满置

1



i8042


取走后置


0


' q' g/ X4 ~ G' F



BitO: OUT_BUF_FULL(O_B_F):


输出缓冲器满置


1



CPU


读取后置

< br>0





6



Co-Work With USB Keyboard



$$ I, E& 1 D& j# a$$ f



大家可能会觉得


Usb k eyboard


好像和


EC


没什么关系 ,其实不然。



Usb keyboard



Legacy mode

< br>需要将数据送给


EC


,由


EC< /p>


在送给


Host



(借腹生子哈哈)



完整的流程如图


4


所示:




usb keyboard


有数据输 入,


BIOS


将数据转化并通过


D2 command


将数据送给


EC




EC


通过


IRQ 1


通知


Host


Host


再下来读取





我所知道的


EC====>Batt ery




1.


Battery Information


w# c# g) [8 X






探测< /p>


Battery


的信息是


EC

< p>
一个重要的工作,


OS


也要通过读取


ECRAM



的内容获取电池电量、温度、电压、充 、放电电流、


Battery


是否存在的信息。


那么


EC


如何获得这些信息呢?


Battery


通常是一个


smbus device


所以它会接



EC


的 一组


smbus


上,而


Battery



spec


上会给出

< br>Battery



smbus



地址以及读取


Battery


信息相关 的命令。


EC


通过向


smbus


上送


Battery



address



cmd


然后就会取 得相应的信息了。



2.


Battery Charge&Discharge Algorithm



1)Charge






充电的过程是这样的,


AC in


的状 态下


Battery


插入,这时


EC< /p>


会进入


precharge mode


进 行小电流充电。


如果


precharge


时间过长


(通常是指超过


一个小时)


EC


就会停止充电并且认为


Battery dead


,送给


host


一个


B attery


Fail



Event


。在


precharge mode


如 果充电电流增大到特定的数值后


EC



会进入


fastcharge mode


快速充电。

< p>
Battery


充满后停止充电。在这个过程中


如 果电池温度异常


EC


也会进行一些处理。这就充电的简单过程。



2)Discharge



AC out & Battery in


这时


Battery


开始放电,


在放电过程中如果电量小< /p>


Critical


Low


,系统处于< /p>


S0




EC


会发


Critical Even t



host


,然后

< br>OS


紧急关机。系


统处


S3,E C


将会唤醒


OS


如果电量小于


BattLowLowled


将会被点亮



Battery


Low Policy


会详述)


。另外放电温度也会被检测,如果温度过高将会做降频 动作


等。



, ~



H. Y6 f


, O





3.


LowLed Policy


< /p>


经常使用


NB


的话,大家可能会发现有一 个


led


很有趣。插入


AC

< p>
充电时会


看到一个黄色的


led

< br>被点亮,


电池没电了会看到一个红色的


led

< p>
亮,


有时还会一


闪一闪的。其实这就是

< p>
EC


导入的一个


Function


。黄色的


led


其实跟这个


F unction


无关,它是充电指示灯,不过它和


LowLed


摆在一个位置。那么这个


红色的


Low Led


什么时候会恒亮,什么时候会闪烁呢?当


Battery


的电量很低


的时候也就是小于


Batt Low


时,


LowLed


就会恒亮。当


Battery


Dead



LowLed


就会闪烁,导致


Battery


Dead


的原因有


Prechage< /p>


时间太长温度过


高或过低。



4.


Battery Even


Ba ttery


在充放电的过程中,因为电量、电流、温度等原因


E C


需要向


host


端发送


SCI Event


,用于


host


在特定情况下采取相应的补救措施。



1)BattChgEvent






Battery

< br>插入或者拔出时


EC


会发送


Ba ttChgEvent


通知


Host



这时


Host


会读取

< br>ECRAM


获得


Battery


是否存在的信息并更新系统端的显示。



2)BattWarnEvent




Battery


的剩余电量到达


Bat tery Warning Level


时,


EC


会发送


BattWarnEvent


通知

< p>
Host



Host


端收 到后会给出提示信息。



3)BattLowEvent



5







Battery

< br>的剩余电量到达


Battery Low Level


时 ,


EC


会发送


BattLowEven t


通知


Host


,而且这时

< p>
LowLed


会恒亮。



4)BattCrtEvent



( d





Ba ttery


的剩余电量接近


0%


时,< /p>


EC


会发送


BattCrtEvent< /p>


通知


Host



Host


收到后做


shutdown


动 作。如果在很长的一段时间里(几十秒)


,Host




没有


shutdown


,那么


EC


将会


Emergency Shutdown










! V3 ^$$ H, ^: `! m5 @3 I











5.


How to Debug Battery?








EC< /p>


通过


smbus


不断的获取电池信息


,


所以电池信息在不断的变化之中。


有些时 候为了澄清问题,


或者为了增加新的


Function


该如何


Debu


呢?我的做

法是使用


debug tool



smbus


停掉,


然后将一些符合条件的值通

< br>debug tool


写入


ECRAM

< br>中,


然后验证相关的


Function

< br>是否生效。


另外如果是需要给


BIOS

< br>发


SCI Event


,我会直接将

Event


填入


SCI queue


。然后驱动


SCI,



SCI


Event


被发送出去。



Peter







(31.66 KB)



2009-4-18 10:00


我所知道的


EC====>IDLE & RESET MODE





1. What



s this?


0 C* Z



m9 Y0 l0 o




IDLE


&


RESET


EC


的两种工作模式,从字面上看


IDLE


是空闲的意思而


RESET


则是复位的意思。


他们的使用场景各有不同,


IDLE


主要是用在


BIOS




NVRAM


区域,


RESET


则是用于


flash bios


的时候。


6 v, N$$ ^0 K5 Y9 j, W! E



2. Why & How




?


IDLE Mode


3 r



k+ - q


BIOS



post


过程中会多次 进入


IDLE Mode


,用于保存一些系统



设置, 以及保存硬件资源供


OS


获取。那么这些操作跟


EC


有什么关系呢?原因



N VRAM


包含多个区域,有些部分保存在


CMOS


之中,可是还有很多信息保


存在


ROM


之中,很多时候


BIOS



EC


code


放在一颗


ROM


之中,那么这时


就非常有必要让


EC


进入


IDLE mode


。另外


BIOS


在更新


NVRAM


的时候,


EC


端有可能通过


KBSMI /KBSCI


以及其他一些接在


SB


上 的


pin


影响到


BIOS


(这


部分纯属猜测


?




IDLE Mode


的实现过程是这样的 ,



BIOS


需要跟新


NVRAM


时,它发命令告诉


EC


EC


收到命令后关掉


LPC


(假设


EC


通过


L PC


连到


SB




write protection,


然后


EC


保存寄存器的内容,


关掉中断只保留一个唤醒源< /p>


(如


EC host command wake up



,然后回给


BIOS



FA



表示进入


IDLE


,最后一步


停掉

< br>8051 clock


真正进入


IDLE


。那么为什么要先回



FA



然后再进


IDLE


< br>,


因为



IDLE


以后


8051 clock


停掉了,


EC


无法跑


code


了,这样 也造成了一个隐患,


有时


BIOS


收到



FA



就开 始更新


NVRAM


了,可是这时


EC< /p>


还没有进入


IDLE


mode



EC


的速度和


hos t


没法比)那就惨了,系统可能会


hang

住,我就遇到


过这样的


bug


?< /p>


,所以


BIOS


收到


FA



最好

delay


一段时间。


BIOS


更 新完成后



66port


随便送个


command



EC

就会从


IDLE


恢复了。



?


RESET MODE


A6 z% |& g# B2 y




flash


bios


的时候


,BIOS

< p>
会发命令让


EC


进入


RE SET


mode


,只所以称之为


RE SET


,是由于


ROM


更新以后


EC


要重新开始跑


code

< br>,所以有一个


RESET


的动作。


RESET mode


的做法是


EC


停掉


sustain fetch



也 就是说


EC


不去预取


指令了,需要跑一 条指令才会去抓一条。然后


EC


关掉所有中断并关掉

< p>
Write


Protection


,让风扇全速 转(因为刷


BIOS


时能耗比较大温度会很高)


,回给


BIOS



FA



,


接下来


EC< /p>


就会进入死循环。


BIOS


通过


backdoor


修改


EC register



8051


pc< /p>


停在


0


地址处。然后

BIOS


开始


flash



flash


完成后


BIOS

< br>将会通过


back door


修改


EC register



8051 reset pc


复位, 这时


EC


就会重新加载新的


code< /p>


开始


run



9 K6 K! P4 N* C! z



% H0 m5 a0 e: H# O, `5 k



以上就是


IDLE &RESET Mode


的完整过程。



我所知道的< /p>


EC====>KBSMI&KBSCI



# y1 r- _; m* i4 c4 u* Q! `6 c



6 t9 V0 ?9 Q/ b3 K+ r1 _



}$$ D



1. Introduction




q6 V% _1 f* b0 _



3 l0 ]$$ ?0 X; }6 {$$ I



SCI


是指系统控制中断


,


为支持


ACPI


的操作系统提供系统管理,客制 化功能。


SMI


是指系统管理中断,由设备或者软件需要呼叫< /p>


SMM


功能产生,使


CPU




SMM


mode


。基本上进入


ACPI


mode


以后


SMI


就很少用到了,对于


EC




SCI



SMI


则是 互斥的,一旦进入


ACPI mode EC


就只会发


SCI


。通常


EC


会 有两根


pin KBSMI&KBSCI


连接到


SB



EC


可以配置这两根< /p>


pin


的属性,


决定

使用何种方式产生中断,比如


level trig



edge trig



pulse trig


,我 做的案子常常


配置成


64us


低电平的


pulse trig



; z- i& x) J+ V. q3 S0 ~7 H1 _. d7 h



2. KBSMI#


7 D& l7 X0 k+ Q: v



其实


EC


很少用到


SMI


,除了少数测试项比如


DOS




Fn+F2


下切屏等。不过


KBSMI#


的实现原理还是很有趣的 。它的原理是这样的:


EC


将一根


pi n


接在


SB


上,而

SB



GPIO


有些具有


multi function


,可以配置成具有


SMI/SCI


的功能。


BIOS code


在初始化时将这个信息宣告给


SMI Table



一旦


EC


发了一 个


SMI,EC


接在


SB


上面这根


pin



# Z' `2 P) Z' O



status


就会被置位,


SB


检测到以后通过拉接在


cpu



SMI pin


产生一个< /p>


SMI



cpu


切换到


SMM mode


然后就会通过之前宣告的那些


pin



status identify smi


Owner


,这时就可以认出是


EC



SMI,


随后通过下


command



EC


读取


SMI


event id


,并通 过该


id


去调用相关的


method< /p>



8 F+ U' D* V' _# v



3. KBSCI#


8 k; s. y* v7 b3 j



?


Q_EVENT


; ~( y. {' t2 Z3 {$$ n$$ V



w4 B



所谓


Q_EVENT


指的是


OS


收到


EC



SCI


后,


OS< /p>


通过发


84hcommand



EC


读取


EC Ram


中的值,这个值被称为


Q_EVENT id

< p>
。这也是


Q_EVENT


得名的

< br>原因



Q


Query


的缩写,



84h


就是


Query Embedded Controller




然后


OS


中的


asl code


会根据该


id


去调用


_QXX()


如 下面的


code


所示


,


这里的


XX


指的就



EVENT


id


。那么


EC


什么时候会发


Q_EVENT


呢 ?当


AC



Battery


in/out



LID open/close ...



// AC Status Changed


% k. F( t/ X5 H; l1



Method(_Q83


8 k) N



k& T4 i& W4 A



Store(0x83, DBG8)


! v& u) L' ]: E! P9 [1 |5 O



J



Store(0x00, Local0)


$$ D4 I- E3 b6 k) R: ' V



# p/ Y: M% k



H/ i3 `& b& {% |



Store(POWS, Local0)


5 P$$ S3 p9 z* X9 d8 F, Q! U* }# e




If(LEqual(Local0,1))


, r5 r6 |$$ G6 _# O1 t; j1 d-



{



9 ]: a# k: f! ?) K% L+ {4 R4 f' T



Store(1,_


2 / f' T: z6 q. K8 P4 z



else{



1 u/ _% F



Store(0,_)


' m9 e5 d2 T



I# ]1 ]1 `



& d7 V! x



O: m



}


& q! }: k* v; B3 f# n. K




Notify(_1,0x80)



- a0 s& m) y+ N+ t



Notify(_PR.P001,0x80)


2 ~5 I; S8 O: l7 c; A, ?




Notify(_PR.P002,0x80)


: H



- L- b% t1 ?



K% K( I- U. A# Q+ ~



}


+ D' 7 |6 A



那么又有一个问题,那就是


OS


怎么知道这个< /p>


SCI



EC


的 呢?请看下面的


asl


code


你就 会明白了


?


, k$$ q0 Z( o. o* F2 l/ O; k



Device(EC)



{


6 M0 E4 m+ f2 L(



, l8 f: Z* w% Q4 R% Y



Name(_HID,EISAID(



4 1 P7 {; M: ' I



Name(_GPE,0x06)


// KB_SCI



, {- `$$ @7 t, I& T2 q+ X! s1 v7 f! X0 S



...



}




Device EC


里面宣告了


EC



KB_SCI


接在了


SB


的哪个


pin

< p>
上,


这样


OS




EC


SCI


的过程 就和前面的


SMI


异曲同工了。最后一个问题

< br>OS


如何检测到


SCI


产生了呢 ?前面


SMI


已经说过


SB

< p>
有一根


SMI pin


接到


CPU


,可以让


CPU


产生


SMI


,可是


SCI


并 没有这样的


pin


,那么


CPU


怎么知道


SCI


产生的呢?我


想到了两种可能:



polling GPE register status bit



4 a' N* F- Z. b1 G0 u1 |) k% ^



< p>
通过


8259


或者


API C


,产生


IRQ


( b9 q5 {6 u4 R% r$$ C0



Chipset spec


印证了我的想法

,SCI


可以配置成通过


8259/APIC


的方式产生中断,


而且中断向量也是可配置的如下图


1


所示:

















b4 U+ n5 _8 l% p



2 k/ X. @' C& ?4 D8 N2 V





8 f! j9 c! h! Y1 J+ A



- P5 S; _7 d- ?& v2 L9 ?/ e



) V- J( X% T( r+ x



8 f8 J3 M4 @3 Z





T( W: Q6 J4 V: k6 j# q





1




BIOS


在设置好该寄存器后会将


SCI


INT


信息存入


ACPI

< p>
FADT


中,这样


OS



可以通过


FADT


获得


SCI


使用的中断号码,


从而能够在

< br>SCI


产生时处理该中断,


这部分请参考


ACPI SPEC FADT


部分。下图


2


是使用


ACPIVIEW


看到我的工作


机的


FADT dump,


如图


2


所示我的机器


ACPI


使用


APIC INT 9




7 u: d% f7 U3 u: K5 [% Z




0 x- ?; T/ H5 r1 X0 G/ V3 d2 f



& P


, E. h; |4 m( U- v, w5 Y




% Q# i7 N& _



Q





2




3 j1 [$$ j( i6 t



{



?


?


GPE


9 Q' {% U1 e. K# L) n



GPE

< p>
其实是属于


BIOS


的范畴了(当然


Q_EVENT


也是


GPE


的一种)


,其他部


分跟


EC

< p>
并没有多少关系,不过既然讲了


SCI


,就顺便提 提


GPE


。所谓


GPE



的是


ACPI


定义的一个< /p>


general-purpose


event


namespace



SB


中的


GP


registers


相对应。


GPE


register


包括


GPE_ST S



GPE_EN


两个部分对应该


GPIO



status

< br>和


enableEvent


的触发分

Level



edge


两种,分别 对应


ACPI


Method _LXX(),_EXX(),XX


分别代表具体的


GPIO pin



所以当系统在


S0

< p>
一旦相


关的


GPIO


status


有变化并且


SCI


enable


了,那么就触发


SCI,


于是形如


_LXX(),_EXX(),


就 会被调用了。




That


?


s all!



我所知道的


EC====>Auto Detect




1. What



s it?



+ o1 h7 J: `4 P


. P


. l( C& ?



: `/ b8 l2 W7 Q8 E9 H9 [



r





G! P$$ f



Auto Detect Function


指的是


BIOS

< p>


EC


配合探测


NB


上存在哪些


device


。常


见的


device



wla n



bluetooth


< p>
camera



3G


等 都是


USBDevice


,它们都接



usb port


上面,而始能信号则是由

EC


控制。探测结束以后,当用户按下


hot

< p>
key



EC


可以根据探 测的结果给出相应


Function


。比如当

< br>wlan


不存在时,该


hotkey

的功能可能就是


search key


功能。




2. Why need it?





! m( Y4 X* K



[& }- t



1 p, P) |( k



Q$$ v3 r



这个功能的存在主要是因为同一个


model


在出货的时候因为客户要求,或者市


场地位的不同可能配置会不一样,比如搭配不同的< /p>


cpu,


上不同的外围


Device



等。这样


EC


就 需要知道哪些设备存在,否则就会出现漏电以及配合


NB


上层软


体出错的状况。另外


Auto


Det ect


还有一个功能就是它能够保持


device


的前一


次的开关状态,


这样只要没有断电就算是下了


S5


再回到


S0



这些设备还会保持



S5


之前的状态,这样可能会比较人性化一些。



$$ A! O: @2 f1 ~2 U) ^( Q# Z



3. How to implement




9 x$$ {9 D



F; c% 7 d1 P1 n/ J




) Q! u! U. s& u' K* W) h* }



Auto


Detect


听上去挺神秘 ,其实实现起来也还是挺简单的


?


。因为这些

< br>device


都是接在


usb port


上,


如果它们


power on


那么


usb port


上的


Connect status



会显示


device is present on port



如此


BIOS


就可以检测出设备存在与否。


具体

< br>来讲是这样首先


BIOS


要从


E C


读回所有


device


的上次


power status



& u! 6 u! g) D%


l1 l



然后< /p>


BIOS


发命令让


EC power on device



BIOS


读取相 关


usb port


上的状态确



device


是否存在;接下来


BIOS< /p>


回填探测到的设备存在状况到


EC


相应位 置;


最后


BIOS


将读回的

< p>
power status


送给


EC



EC


根据这些状态给


de vice


送始能


信号,这样


devic e


就保持了上次的状态,这就是


Auto detect


的完整过程,完整


流程如下图


1


所示:


# s: `










我所知道的


EC====>Uart




# ) u, X/ t& F



m5 x, M



1. Introduction


- ^& u* k) Y! h



k



0 I4 ?




“没有输出之前调试是一 门艺术,


有了输出以后调试就是一门技术!


”这句话准


确的道出了调试程序时能够查看运行状况和环境信息的重要性;


尤其在< /p>


FW


环境



de bug


手段非常有限,


uart


是几乎 所有


FW


都会采用的


debug


方式。


Uart



称 是



Universal Asynchronous Rec eiver/Transmitter


即通用异步收发器


(异步 串


行通信口)它包括了


RS232


、< /p>


RS485


等接口和总线标准规范


,


它作为一种低速通


信协议广泛应用于通信领域。



# F: k8 S0 o6 ]; y) r



2. Hardware Interface


7 _* Z3 ^! h



! C. v. f0 {



n5 S- f



Uart spec


定义了 非常多的信号,但


debug


过程中通常只需要接三根信号


RX



TX



GND


就可以了,故常见串口


debug< /p>


线路如下图


1


所示:

& ~2 i1 D* {+ L6 P






串口读写数据时数据送往内部的< /p>


fifo


,如果


fifo


满了数据就会按照顺讯逐个


bit


送往总线。数据读写 的时序和格式如下图


2


所示:





; [- Q1 }2 - S; e+ 0 c. P



串口通信常见的波特率有


19200



115200



9600



4800

< br>,


2400



1200 Bps


,波


特率可以通过寄存器配置。



( o9 u+ }2 R) v0 Y# o




3. How to Implement


) @. G



3 a1 u# U, L. A: d




前一段时间有同事问我一个问题


:



为什么我们


EC


之中使用


printf



puts



putchar


这些函数输出字符,字符会送给串口呢


?


以前写的


DOS


下的程序是输出到终端


的呀。



,他的困惑不无道理,为什么呢?不 要着急,让我来告诉你其中的奥秘。


若干年以前,我曾经在


ar m9


上面


portting



bootloader



u-boot



,u-boot


也有


一个串口


debug


的部分,所以我就碰巧大致翻了翻这部分的 代码,关注过这个


问题


?


.

< p>
先来看看


printf


的实现吧

< br>,


下述代码是


u-boot


中的


printf code



$$ [; p0 ?: a3 a.


e9 v/ o) ?8 D









voidprintf (


const



char


*fmt, ...)




{


6 ]# S+ m7 L, ~+ M! c: x/ M3 `



@



4 Z' T' d4 w6 D- G9 V) Q6 L



va_list args;


- i& N! Q0 {& j4 O( N! y% L




uint i;















char


printbuffer[CFG_PBSIZE];



) _. N& M% X6 & P* T- o. _




va_start (args, fmt);



9 p/ Y


. M+ o* F! r! q1 @















/* For this to work, printbuffer must be larger than





* anything we ever want to print.



2 g6 k& U0 Z1 c$$ Z




*/















i = vsprintf (printbuffer, fmt, args);




va_end (args);
















/* Print the string */















puts (printbuffer);


5 ]% B



|( q7 Y



}


4 t! P2 H% E4 d2 b




注意看红色部分的代码,


printf


先使用


vsprintf


处理输入的参数,并将处理后的


结果存入


pr intbuffer


,随后调用


puts


输出。那么我们看一下


vsprintf


的实现吧,


下面的代码是


vsprintf



code




/ W6 g! n& ~5 Y7 g



7 U# u1 p2 {: P& u






intvsprintf(


char


*buf,


const



char


*fmt, va_list args)


. q) I$$ {* m) F' n/ l



+ `4 U: K( t



{















int


len;


. {' p1 t& W' C; I9 T















unsigned



long


num;















int


i, base;


+ r6 o4 X1 ~; B- z* @/ s4 o/ g















char


* str;


$$ q0 q' ' w



[















const



char


*s;



- e: ]1 D6 J( S/ ~















int


flags;


/* flags to number() */



: [9 u- t9 s8 u1 Q/ l2 |















int


field_width;


/* width of output field */















int


precision;


* b$$ H3 {# U) E% R5 O



/* min. # of digits for integers; max


' E5 F- }9 K/ G2 d





number of chars for from string */


4 Q! E6 c1 M1 a( p( E: k















int


qualifier;


/* 'h', 'l', or 'q' for integer fields */



4 ~) Q5 }. v$$ `7 1 n) H: `















for


(str=buf *fmt ++fmt) {


0 i( G& & X; 9 y6 e4 P



























if


(*fmt !=


'%'


) {


1 j4 M: G7 a5 V2 f




*str++ = *fmt;


- U0 o4 E2 Y) a* 2 [: h







































continue


;




}


/ `( U2 {, B# f



3 ?- {) c, u4 G/ c



























/* process flags */





flags = 0;




repeat:


- o% Q4 S$$ A$$ ^( N) O( f




++fmt;


2 K$$ P4 |; S6 m1 M



/* this also skips first '%' */


6 D4 C+ d6 ]0 a; ]/ P% G. q







































switch


(*fmt) {



















































case



'-'


: flags |= LEFT;


goto


repeat;


# # M: _0 j' Z6 _



V9 X



















































case



'+'


: flags |= PLUS;


goto


repeat;


1 x, j3 `



Z' S


l



}$$ F' M



















































case



' '


: flags |= SPACE;


goto


repeat;


) P) s5 ]- g0 e&


`. _2 A! ?



















































case



'#'


: flags |= SPECIAL;


goto


repeat;


0 R# P& I2 j:


Q8 b, y



















































case



'0'


: flags |= ZEROPAD;


goto


repeat;



2 }+ H7 T. K0 o- s



}




























/* get field width */




field_width = -1;


! d% A( b* x2 s- v7 `



























if


(is_digit(*fmt))



) E9 u6 J: g8 A



field_width = skip_atoi(&fmt);


+ c& M) V0 f- u0 H2 b$$ C: F



























else



if


(*fmt ==


'*'


) {


3 R9 j! r3 }* F( n& l4 v! y



( ?3 O4 E$$ x) n/ j3 B9 @



++fmt;


2 m2 X4 J1 g4 N+ b. e







































/* it's the next argument */


/ D; K6 z& _/ }



( i( |& D0 A



R



field_width = v a_arg(args,


int


);


' c( o



r1 _; Y







































if


(field_width < 0) {



# T. @% H2 M4 J2 q7 W9 - z* N- o



field_width = -field_width;


1 z& r/ c2 U. d, {6 ], J



5 y. M( U+ d- S& Z0 q( x2 X+ w* b/ P



flags |= LEFT;


; h+ @/ ?7 C) p- y' x+ h. j



) e6 L1 U7 C. S5 r



}


, W/ X, E6 {+ V: S5 P



s. }




}


% x$$ b3 z! Q' S1 A



P: C* I# y! h8 E



+ N7 O4 [( h+ N4 U



























/* get the precision */




precision = -1;



























if


(*fmt ==


'.'


) {


* P# ^+ N+ b) u) w7 c




++fmt;


/ O7 |2 ]+ `0 J



' g& O: Z1 B7 O& [% h



8 H+ ]+ H0 h8 e- m& [



if


(is_digit(*fmt))



- v' H5 s2 _0 i5 S3 H' w



precision = skip_atoi(&fmt);







































else



if


(*fmt ==


'*'


) {


% [2 z* q# W7 t9 B




++fmt;



















































/* it's the next argument */




; c) j. A* k- ~0 p



precision = va_arg(args,


int< /p>


);


( t



?) k/ k2 r5 ]




}







































if


(precision < 0)





precision = 0;




}


% C$$ N+ w( J% Y% I: _* Z$$ d



- y6 U* c& L, M0 q- j+ r. l. d# J



























/* get the conversion qualifier */





qualifier = -1;


! m: `: Z+ Q; B9 p0 C1 I



























if


(*fmt ==


'h'


|| *fmt ==


'l'


|| *fmt ==


'L'


||


7 c- Y5 G$$ F; U



* D



`: l! {



j




*fmt ==


'Z'


|| *fmt ==


'z'


|| *fmt ==


't'


||




4 u% C# F, E' v6 n8 M% J



*fmt ==


'q'


) {



+ u: c' k( Q$$ w, [9 S9 @



qualifier = *fmt;


! H0 g) Y# 7 M







































if


(qualifier ==


'l'


&& *(fmt+1) ==


'l'


) {





qualifier =


'q'


;




++fmt;




}


8 E3 x; c0 @3 P




++fmt;



/ f( e



p( l* u' F2 g: z/ L# c



}


1 L- Y) z' }



s0 1 I1 w




























/* default base */



! M! d) ?



base = 10;



5 Y8 H2 @7 D2 / U+ u0 p



























switch


(*fmt) {


# {7 I6 C& n0 ~: e3 G; ~7 [8 c



























case



'c'


:







































if


(!(flags & LEFT))



















































while


(--field_width > 0)




*str++ =


' '


;


' {. }4 0 ~( l3 t




*str++ = (


unsigned



char


) va_arg(args,


int


);


/ C2 n- l% z3 U* ^







































while


(--field_width > 0)



! U) Z# M- v/ n, D



*str++ =


' '


;







































continue


;




























case



's'


:




7 w6 z% ^9 O' K; |$$ u



s = va_arg(args,


char


*);


. J: R& P( ~; |. T% G6 o







































if


(!s)




s =



;





len = strnlen(s, precision);








































if


(!(flags & LEFT))



















































while


(len < field_width--)



8 f2 G+ P7 Q, c# U% U' w



*str++ =


' '


;


- ~# V+ U+ o6 y' ]7 `







































for


(i = 0; i < len; ++i)




*str++ = *s++;







































while


(len < field_width--)




X5 M5 C8 u: J$$ b1 R/ x. _4 A




*str++ =


' '


;


8 Z0 [& F1 b: G: 2 G


. W







































continue


;



! A# t



























case



'p'


:







































if


(field_width == -1) {


8 d& m, ?. O



A, P



c




field_width = 2*


sizeof


(


void


*);




flags |= ZEROPAD;


7 _9 D( E# B, }6 N6 {



' ~# G( J0 D+ B* M



}


6 d5 L9 k: j' H3 }



: d- Q



str = number(str,



+ b; k& _$$ B! Z1 L



(


unsigned



long


) va_arg(args,


void


*), 16,


$$ t4 x& t+ O( I+ D+ ]5 |5 Y



0 w7 |) k7 B4 ?



field_width, precision, flags);


8 O3 h; s, h8 y$$ Y5 ?4 g* D5 ]# ]







































continue


;


% f) M, `% [



|



5 T: ~( @5 b$$ g2 y, @




























case



'n'


:


! W; w8 R& L) Z



' h1 Q0 ! G



[1 V



5 A4 k' q# l) {) G4 |2 }



if


(qualifier ==


'l'


) {



















































long


* ip = va_arg(args,


long


*);



; |3 s7 [



p( o



*ip = (str - buf);




}


else


{


9 {3 r0 ~' j% ~: S9 G0 Z! m



















































int


* ip = va_arg(args,


int


*);



% t; e: F( k6 {5 h' l) j



*ip = (str - buf);


, f. z+ ~* l



+ N7 f/ n* y$$ k! d* D



}







































continue


;



# Z8 B: e8 l5 M8 `



























case



'%'


:


) b* @3 L- y) M# x% c



& P: o1 K0 F& B) g



*str++ =


'%'


;







































continue


;


/ i$$ u) P3 ]- y+ O



% s$$ |1 c( I



h# P& b- }; h



























/* integer number formats - set up the flags and



























case



'o'


:



+ n5 d) j# j: n6 b7 }



base = 8;


7 T) s0 S: L2 z1 m( _







































break


;


0 }* I( J) J3 t, y; O/ w: _




























case



'X'


:


( m3 v* U



w4 @; c8 I




flags |= LARGE;




w. B0 j- c0 p: x4 O! W- a



























case



'x'


:



' T+ i6 w( z+ r& n' }



base = 16;







































break


;



1 h8 R



j- |



u% B/ u' Z* p



























case



'd'


:



























case



'i'


:



4 p: j2 o% b/ J



flags |= SIGN;


- b' |. }- m9 x



























case



'u'


:


, [$$ X. K! L0 1 ~5 |& V







































break


;


4 g& D5 V: H+ R* C6 W



- `( e1 s. h$$ g$$ Y



























default


:



% a+ I- Q# h6 u% q9 C3 h( Y



*str++ =


'%'


;







































if


(*fmt)



. ?& A; t& h( `. @( v$$ r$$ }



*str++ = *fmt;



6 D, x& Y% {9 ~9 t



]0 N



3 n



else


0 w, [/ ^8 O: e' X6 @( a



3 W( O) x, D& ^! d' |: Z# w1 s



--fmt;







































continue


;




}



























if


(qualifier ==


'l'


) {


9 [5 Q6 a



J, L# Y+ u! q+



+ c9 y7 p) p' l3 d: N0 b



|5 D



num = va_arg(args,


unsigned



long


);



$$ {1 h3 x0 L4 r+ s+ R9 R



}


else



if


(qualifier ==


'Z'


|| qualifier ==


'z'


) {



( l5 {: t1 l8 i- D* `( j



num = va_arg(args, size_t);


1 q# E: a& r- ^* s





5 Y4 B0 n3 D& {& M6 U2 j



}


else



if


(qualifier ==


't'


) {


* v, f/ c* {



b4 R: X) K



: x; g, ]: Y



num = va_arg(args, ptrdiff_t);


! P




}


else



if


(qualifier ==


'h'


) {





}4 Z5 Q7 B( U5 E



num = (


unsigned



short


) va_arg(args,

int


);


% F6 d: Q/ m$$ E+ W$$ Y1 V9 A







































if


(flags & SIGN)



# H; V) E, v- T' W3 [$$ P# Q



num = (


short


) num;




}


else



if


(flags & SIGN)


; x



o9 k0 q5 b3 F9 E




num = va_arg(args,


int


);


. n1 H- l0 n



X



a; ^4 ^



























else


7 n3 J7 c



C3 A



) v1 p1 ]) D3 F. n0 l( q8 a



num = va_arg(args,


unsigned



int


);



' F$$ P9 v



p8 B$$ u% N



str = number(str, num, base, field_width, precision, flags);



8 K5 s& [; F



?8 c4 O& W



}


1 k5 ?: D; R, A



0 Q. W4 ~$$ O- y# K% J/ V# u8 ]



*str =


'0'


;















return


str-buf;


$$ M6 s4 L4 _) a/ R



}




上述代 码大致的意思就是解析


fmt


字符串,并据此分析计算


args,


最后将结果写


buf


之中,有兴趣可以自己单步


debug


玩一玩。下面我们来看一看


puts


的实

< p>
现代码:


9 y9 a1 X- u# k' i




8 j7 B$$ L3 r- T0 ^& f+ Y+ U









voidputs (


const



char


*s)




{


+ f/ Y: @1 v( @





[3 o



#ifdefCONFIG_SILENT_CONSOLE


9 g#



H. m- & X7 D) c















if


(gd->flags & GD_FLG_SILENT)



























return


;




Y9 X- }% V5 L



#endif















if


(gd->flags & GD_FLG_DEVINIT)




{


1 A- ?# b$$ U* S- U; h+ r5 m



























/* Send to the standard output */


- R% x/ H# a3 k0 Z# O% m



( ^: a6 r6 X/ n8 R; }



fputs (stdout, s);



* j5 L% q5 H6 t0 l



}















else



% w& ~+ L- V, K( t/ v




{



























/* Send directly to the handler */



























serial_puts (s);


: T! h' ~8 A) D( o, o




}


& c& ~/ i



}


8 w) c7 v8 l# F8 m8 ~6 U+ D6 K





这段


code


一出问题就明了了


?


,puts


函数会先检查


GD_FLG_S ILENT flag


如置


起就什么都不干直接返回,


silent


就是安静的意思嘛


?


,


而后会看


GD_FLG_DEVINIT fla g


如置起,那么输出到


stdout(


也就是通常所说的终端


)



否则就是红 色部分的代码了,这里就是问题的关键,它会调用


serial_puts


输出


到串口了,到


serial_puts


这里就已经很明了了,反正一不做二不休,我们一追


到底看个明白


(


我参考的是


s3c24x0



code):




voidserial_puts (


const



char


*s)


5 X% |



@: E



`



{


0 h: G



q



4 f



_serial_puts(s, UART_NR);



}




voi d_serial_puts(


const



char


*s,


const



int


dev_index)


7 * L% f5 ?% m0 h( X/ |! B: {



{















while


(*s)




{


; u( W4 E/ L+ l





4 n! ?/ ^0 X3 f



_serial_putc (*s++, dev_index);


2 z4 r5 w% F% m1 B




}



}



7 w8 n8 ) n$$ k



/*




* Output a single byte to the serial port.



1 }3 r9 |, X& A



*/



void_serial_putc (


const



char


c,


const



int


dev_index)



{



- l7 T2 A8 e) {! {. S



S3C24X0_UART *


const


uart = S3C24X0_GetBase_UART(dev_index);



#ifdefCONFIG_MODEM_SUPPORT


; s; T; P6 I) L: p, m; ?















if


(be_quiet)



























return


;




E- _# I



#endif


$$ k6 J( B- 7 R4 H



$$ }- V$$ ]# S: @( K9 L















/* wait for room in the tx FIFO */


. ]7 E& t3 j8 w+ E# T




while (!(uart->UTRSTAT & 0x2));




s( A$$ ?3 X+ y% t& l- r& _+ I



9 X6 K1 E2 c. _$$ [3 p$$ b



#ifdefCONFIG_HWFLOW















/* Wait for CTS up */


3 t; g& Y& n4 }1 }! L8 d4 Y















while


(hwflow && !(uart->UMSTAT & 0x1))




s



s* S, U# n




;


& y: 1 z



G! I



#endif




H5 U# P; _7 }
















uart->UTXH = c;


4 N( @. H, p: m! u; @) ~: Q- m; w% C



& [9 T: |' @0 q- ^* ?















/* If n, also do r */















if


(c ==


'n'


)



$$ q. V$$ I7 C6 o



serial_putc (


'r'


);



}


' `8 q8 o( D. A# o9 i




图穷匕见,秘密终于揭开,红色 部分的


code


已经说明一切了


,最终 是通过操纵


s3c24x0 uart register


将数 据抛出去的。经这一追


printf



puts



putchar


都解决



看完上面的分析之后,大家应该不会再困惑了吧!



: Z% T



N/ c6 N/ t



That


?


s all!



+


我所知道的


EC====>SPI



2 N0 x( _4 Q$$ u, [




1. Introduction



/ C* u& j$$ ' _+ Y: |: @5 u



V( m5 m



SPI


全称为


Serial Peripheral Interface Bus



串行外围总线。它是由


Motorola


制定的四线式全双工的同步串行数据通信标准。


spi


允许


mcu


和 各种外围设备进


行全双工的串行通信。常见的


spi device



flash rom


, 触摸屏,


LCD


等。它有


比较高的传输 速率,传输速度通常可以达到几


Mbps


spi


采用主从模式。通常


master

< br>只有一个,但是可以有多个


slave


,多个

< p>
slave


通过片选定址。




2. Hardware Interface


Spi


接口如下图



1


所示,通常就四根


pin



EC Chip


有按



Motorola


的经典命名方式将这四根

< br>pin


分别称为


MISO



MOSI



SPICLK

< br>、


SPICS#


。不同的


IC< /p>


厂商


pin


命名的方式可能会有不同,比 如有些也会命名为


SCK



SDO



SDI



CS#


等。


$$ Y7 [- n# U& C- P) t



y



) Z! n



d4 Z7 {6 ]



m4 Y







其中


SP ICLK


提供通信所需要的


clock



clock


太重要了,没有了它,那就全乱

< br>套了,什么时候开始、结束,何时是有效数据,何时为跳变都无法分辨,由此可



SPICLK


是非常重要的同步时钟信号。

SPICS#


是片选信号,如果要使用某一



slave device


首要做的就是先片选该设备,也就是将该


SPICS# pin pull low



MISO master input slave output


反过来读就是


output slave input master


,正反


读都讲的通,


MOSI


也同解


Motorola

真是太牛了!名字取得这么好。当


master



slave


发送信息时,


mater

< p>
将数据送到


MOSI


上,


slave



MOSI


上读走该信


息,如果


slave


要回信息给

< p>
master



slave


就会将数据送到


MISO



mast er



MISO


上获得信息。


spi


支持单


master


slave




mater



slave


模式 ,



NB


上我们常用的就是将


BIOS


rom


通过


spi


接在南桥,或者接在


EC



spi


接口,


都是单


master



slave


的模式。 因此我们只讨论这种模式。下图


2


是我手上的

< br>NB


专案的线路,


EC spi


接口上接了一颗


w25x80 flash rom


,我们后续的讨论将


基于这颗


IC



& u' L0 U



d. ^1 Q



) L$$ @) d. B/ k3 @& f



+ E



k+ N0 |* i' B




1 r, p



3. SPI Instructions




为了方便的操纵


slave


device



slave


device


定义一些


instruction s


。这些


instructions


包 括了操作


device


的基本操作如:


读数据,


写数据,


擦除数据等等。


W2 5x80


这颗


IC


< br>spec


中定义了



enable



disable



status


register status register data read read dual


program



erase


10.


block


erase



erase



down


e power down manu/devid jedec id


等具体可以参考


w25x80 spec


。对一颗


flash rom


我们常用的操作就是通过写里面的内容完成刷


bios


的功能, 而刷


bios


之前


spec


规定还需要做一个


erase


的动作,将


rom



所有的内容清为“

< br>1


”。另外有时无法开机时,我们还需要读取


flash


rom


的内


容判断出错的原因。针对上 述讨论我们只需要如下几个


instructions


即可完成 任


务。



& }; @3 T



Z# ?* X' a+ }



?


Read data



Read data


指令可以从


flash rom


中一次读取多个字节,执行该指令需要先片选


SPICS#(


将该


pin pull low)


,然后送


read data


指令给


spi flash rom

< br>,随后将


24


位的地址按照


MS B


格式分成三个字节


A2,A1,A0


,然后分别送给


spi flash rom



然后就可以从


SPIDAT


中读取其中该地址上的数据 了,


地址会自动累加,


该指令


会一直读 下去直到将


SPICS# pull high


。时序如下图< /p>


3


所示:




0 W1 c& C1 _! n2 B4 q! V# C. f




; X- B3 h& b: M3 ?8 j



?


Sector erase


; m8 Z: G7 A) h# k- _9 g



Sector


erase


指令将


flash


rom

< p>
的指定的


4kBytes


内容全部清为‘


1


’。执行该


指令之前要先发

< br>write enable


指令而且


status register



block protect bits< /p>


必须


要清‘


0


’ ,否则


sector erase


指令将不会执行,做完上述准 备工作以后,仍然


要做的是片选


SPICS#

< br>,然后送


sector


erase


指令给


spi


flash


rom


,随后按照


MSB


格式送


24


位 地址


A2,A1,A0



要注意的是判 断


sector erase


指令是否完成要

< br>使用


read


status


register


指令检查


BUSY


位 ,一次指令完成要将


SPICS#


pull


high


。时序如下图


4


所示 :


, V( b: S& o% @1 A( Q2 ^



d# s





2 K- w8 U9 {5 W) `2 H



?


Page program




n7 T0 X/ U



Page


program


指令允许一次写特定地址开始的


256


个字节 ,前提是该区域必须


被清为‘


1


’,所 以要先发


sector erase


指令将


flash rom


清为‘


1


’然后才能执



page program



执行


page program


之前要先送


write enable


指令而且


status


register



block protect bits


必须要清‘


0


’,


接下来送


page program

指令并给



MSB


格式的


24


位地址


A2,A1,A0

< br>,


后续将数据送到


spi bus


上,


如果数据小于


256bytes


,则只修改特定的字节数,如果大于


256bytes


,写的内 容就会回绕到


开始地址的头部。可以通过


read


status


register


指令 检查


BUSY


位确定指令是


否执行完毕 ,一次指令完成要将


SPICS# pull high


。时序 如下图


5


所示:









-


-


-


-


-


-


-


-



本文更新与2021-02-13 12:05,由作者提供,不代表本网站立场,转载请注明出处:https://www.bjmy2z.cn/gaokao/650064.html

我所知道的EC的相关文章