自制AMD CS5536关机代码和嵌入式

    技术2022-05-19  23

    AMD CS5536是一款很流行的嵌入式处理器,在基于它的架构上,可以做成各种小器具,然后如果操作系统是linux,且内核低于2.6.18的话,据我所知,它不能实现软关机,也就是说调用shutdown之后机器无法关闭,怎么办呢?最近我搞到一块AMD CS5536的板子,想自己做点东西,可无奈发现它在linux下无法关机,按电源也必须4秒,索性只好将开关做成纯电气的而不是电子的,也就是说按下开关直接切断电源,然而这决不是长久之计,最重要的是要实现软关机,即使实现不了也要实现按下电源立马关闭,也就是说不用再等4秒。      谁让咱是搞IT的呢?自己动手的乐趣不亚于厨师为自己做一桌子菜的乐趣,马上下载了CS5536的手册《AMDG_CS5536.pdf》(网上一搜便是,第一步如此顺利),接下来就是漫无边际的“浏览”了,浏览过后终于找到了PM河ACPI的章节,唉只怪英文太差,足足浏览了我一天。接下来就是写写看了,我觉得只要手册看懂了,搞硬件这种东西无非就是写写端口,比软件容易多了,然则最难的就是看手册啊,照这么说,软件只要设计好了流程和数据结构,写代码也很简单;买了房子之后,搬进去也是很简单的…,不是吗      不管怎么说,直觉上觉得按下power键立马关机要简单一些,毕竟“按下键”这个动作产生的signal已经被你的手一个动作完成了,所剩下的只是设法设置一下按下的delay时间了,而实现软关机,想产生power键被按下的信号,不知要写多少寄存器或者端口呢,想想都恐怖,还要注意时序…还是先搞瞬时关机吧,首先看到的是以下一段

    看起来也没有那么复杂,只要拉低两个引脚信号就可以了,但是真要做起来还是要写寄存器的,接下来找要搞到PMC寄存器的内容,于是找到下面这段:

    首先注意7到15位,32位,44到47位,比较重要的是7到15位,通过它我们可以得到pm的base地址,然后就可以在这个base的基础上根据其它的功能的offset来寻找其它的功能IO端口了: addr = 0x5140000f;   //pmc这个msr的地址 msr = rdmsr(addr);   //读取msr,rdmsr可从linux代码中找到 unsigned long base = msr.lo&0xff80;   //0xff80为7到15位的mask 耗了将近一天所得到的成果就是找到了几个有用的信息,比如下面的这个:

    那么下面的代码就找到了这个io口: addr = base + 0x40; 于是往里面写些什么呢?下面的信息绝对有用

    于是下面的代码加上以后一切就完成了,按下电源,立马关机: outl( 0x40001750, addr ); 总的代码就是: #include <asm/io.h> #include <unistd.h> #include <stdio.h> #include <stdlib.h> typedef struct msr_struct {     unsigned long lo;     unsigned long hi; } msr_t; static inline msr_t rdmsr(unsigned index) {     msr_t result;     __asm__ __volatile__ (" /             movw    $0xac1c, %%dx ; /             movw    $0xfc53, %%ax ; /             outw    %%ax, %%dx; /             movw    $0x0007, %%ax ; /             outw    %%ax, %%dx; /             movw    $0xac1e, %%dx ; /             inw     %%dx, %%ax; "             : "=a" (result.lo), "=d" (result.hi)             : "c" (index)             );     return result; } int main (int argc, char *argv[]) {     msr_t   msr;     unsigned long addr = 0;     msr.lo = 0;     msr.hi = 0;     addr = 0x5140000f;     iopl(3);     msr = rdmsr(addr);     unsigned long base = msr.lo&0xff80; addr = base + 0x40;   outl( 0x40001750, addr );     return 0; } 这个程序运行之后,按下电源键,直接就关机了,很是快乐,接下来开始搞软关机了,至于软关机,最重要的就是能使硬件产生几个序列,这些序列最终拉低WORK和WORK-AUX引脚从而实现关机,现在的关键是如何找到这个序列,这就要看手册了,实现快速关机时仅仅使用了PMC这个msr,这是因为你的手已经产生了一个序列,但是如果要靠写端口产生类似的序列就不得不使用别的msr或者端口,通过看手册知道其中比较重要的是GPIO,ACPI以及PMC,搞了一天之后,终于成功了,代码如下: void power_off(void) {         unsigned long acpi_low = 0,acpi_high = 0,                       pmc_low = 0, pmc_high = 0,                       gpio_low = 0,gpio_high = 0;        int acpi_addr = 0,pmc_addr = 0,gpio_addr = 0;        msr_t acpi = rdmsr( 0x5140000e);        acpi_low = acpi.lo;        acpi_high = acpi.hi;        msr_t pmc = rdmsr( 0x5140000f);        pmc_low = pmc.lo;        pmc_high = pmc.hi;        msr_t gpio = rdmsr( 0x5140000c);        gpio_low = gpio.lo;        gpio_high = gpio.hi;        acpi_addr = acpi_low&0xffe0;        pmc_addr = pmc_low&0xff80;        gpio_addr = gpio_low&0xff00;        outl(0x08000000,gpio_addr+0x04);         outl(0x08000000,gpio_addr+0x10);          outl(0x40000008,pmc_addr+0x10);         outl(0x40000002,pmc_addr+0x30);         outl(0x40000005,pmc_addr+0x34);        outl(0x2ffff,pmc_addr+0x54);        int p = acpi_addr+0x02;        outw(inw(p)|0x0100,p);        inw(p);        p = acpi_addr+0x1C;        outl(inl(p)&(0x40000000|0x80000000),p);        inl(p);        p = acpi_addr+0x18;        outl(0xffffffff,p);        inl(p);        p = acpi_addr+0x00;        outw(0xffff,p);        inw(p);        p = acpi_addr+0x08;        int iTyp = 5<<10;    //5就是关机的type值,将之移位到它该到的位置        iTyp = iTyp|0x2000; //使能位置位        outw(iTyp,p); //触发序列 } 前面的设置都是设置使能位的,只有最后的那个outw才触发了序列,所有的规范都在那个手册里面,这里就不贴图了。      总结起来就是,搞硬件虽然写了一个寄存器,但是带来的快乐却很多,因为你真的能实际控制硬件了,就好像心里想了一件事,吹了一口气,顿时风雨大作一样。然而现如今嵌入式开发的定义是不是太滥了啊,就我上面做的这件事就是“嵌入式开发”了,实际上并不是的,嵌入式开发是很复杂的,要考虑很多,甚至更多,比如可扩展性,比如资源的使用情况,等等等等…只要你到了一个搞linux内核的公司,那十有八九是照着硬件手册写寄存器端口的,linux内核的内容那么多,当你兴致勃勃去公司报到后,最终却落实到了in/out两条指令上,事实上,要真的写端口,大可不必搞什么linux内核,只要在用户态就可以,另外找一块板子,什么操作系统也没有也可以照着手册读写端口和寄存器…

     


    最新回复(0)