前面已经介绍到要初始化南桥,现在就来看看南桥是怎么样初始化,并且更重要的工作,就是怎么样初始化串口输出东西。先来看
superio_init
实现代码:
#define PCICONF_WRITEB(dev,func,reg,data) /
li a0,CFGADDR(dev,func,reg); /
li a1,PHYS_TO_UNCACHED(PCI_CFG_SPACE); /
and a2,a0,0xffff; /
or a1,a2; /
srl a0,16; /
li a2,BONITO_BASE+BONITO_PCIMAP_CFG; /
sw a0,BONITO_PCIMAP_CFG(bonito); /
lw zero,BONITO_PCIMAP_CFG(bonito); /
or a0,zero,data; /
sb a0,(a1);
#define PCICONF_WRITEW(dev,func,reg,data) /
li a0,CFGADDR(dev,func,reg); /
li a1,PHYS_TO_UNCACHED(PCI_CFG_SPACE); /
and a2,a0,0xffff; /
or a1,a2; /
srl a0,16; /
li a2,BONITO_BASE+BONITO_PCIMAP_CFG; /
sw a0,BONITO_PCIMAP_CFG(bonito); /
lw zero,BONITO_PCIMAP_CFG(bonito); /
or a0,zero,data; /
sw a0,(a1);
#define PCICONF_ORB(dev,func,reg,data) /
li a0,CFGADDR(dev,func,reg); /
li a1,PHYS_TO_UNCACHED(PCI_CFG_SPACE); /
and a2,a0,0xffff; /
or a1,a2; /
srl a0,16; /
li a2,BONITO_BASE+BONITO_PCIMAP_CFG; /
sw a0,BONITO_PCIMAP_CFG(bonito); /
lw zero,BONITO_PCIMAP_CFG(bonito); /
lbu a2,(a1); /
ori a2,data; /
sw a0,BONITO_PCIMAP_CFG(bonito); /
lw zero,BONITO_PCIMAP_CFG(bonito); /
sb a2,(a1);
#define SUPERIO_WR(idx,data) /
li v0,BONITO_PCIIO_BASE_VA+0x3f0; /
or v1,zero,idx; /
sb v1,(v0); /
or v1,zero,data; /
sb v1,1(v0);
#define E2_EPP 2
#define E2_S1 (1<<2)
#define E2_S2 (1<<3)
#define E2_FLOPPY (1<<4)
LEAF(superio_init)
PCICONF_WRITEW(PCI_IDSEL_VIA686B,0,4,7);
/*positive decode*/
PCICONF_ORB(PCI_IDSEL_VIA686B,0,0x81,0x80);
PCICONF_WRITEB(PCI_IDSEL_VIA686B,0,0x83,0x80|0x1| 0x8);
PCICONF_WRITEB(PCI_IDSEL_VIA686B,0,0x85,3);
/* enable RTC/PS2/KBC */
PCICONF_WRITEB(PCI_IDSEL_VIA686B,0,0x5A,7);
SUPERIO_WR(0xe2,E2_S2|E2_S1|E2_EPP|E2_FLOPPY) /*enable serial and floppy */
SUPERIO_WR(0xe3,0x3f0>>2) /*floppy base address*/
SUPERIO_WR(0xe6,0x378>>2) /*parallel port*/
SUPERIO_WR(0xe7,0x3f8>>2) /*set serial port1 base addr 0x3f8*/
SUPERIO_WR(0xe8,0x2f8>>2) /*set serial port2 base addr 0x2f8*/
SUPERIO_WR(0xee,0xc0) /* both ports on high speed*/
PCICONF_WRITEB(PCI_IDSEL_VIA686B,0,0x85,1)
jr ra
nop
END(superio_init)
这段代码里,先用一个宏LEAF来简化子函数的定义,声明为“叶”函数,也就是说这个函数不再调用其它任可函数,如果又调用其它子函数就不叫做叶函数,这样做法,可以让汇编器尽可能地优化代码。接着下来,就看到这行代码:
PCICONF_WRITEW(PCI_IDSEL_VIA686B,0,4,7);
这里又调用一个宏实现往PCI总线上写一个字。现在把这个宏展开来看看,倒底是怎么样往PCI总线上写数据的,如下:
lui $a0,0x1
ori $a0,$a0,0x4
lui $a1,0xbfe8
andi $a2,$a0,0xffff
or $a1,$a1,$a2
在这里都是计算南桥的内存映射地址。
srl $a0,$a0,0x10
lui $a2,0x1fe0
ori $a2,$a2,0x118
sw $a0,280($s4)
lw $zero,280($s4)
到这里打开北桥的功能。(?)
li $a0,0x7
sw $a0,0($a1)
写数据到南桥的寄存器里。
PCICONF_ORB(PCI_IDSEL_VIA686B,0,0x81,0x80);
这句是选择正电压解码方式。
PCICONF_WRITEB(PCI_IDSEL_VIA686B,0,0x83,0x80|0x1| 0x8);
这句是启用两个串口的功能和地址。
PCICONF_WRITEB(PCI_IDSEL_VIA686B,0,0x85,3);
这句是启用SUPER IO的功能。
PCICONF_WRITEB(PCI_IDSEL_VIA686B,0,0x5A,7);
这句是启用RTC实时时钟,PS2,KBC键盘控制器。
后面那些都是设置SUPER IO端口,分配地址。
后面那一行:
PCICONF_WRITEB(PCI_IDSEL_VIA686B,0,0x85,1)
这里重新启用南桥。
最后调用JR RA,就是返回子函数调用。
现在接着看串口的初始化,其实串口的初始化,就是设置怎么样接收和发送数据,以及用什么样的波特率。代码如下:
LEAF(initserial)
la v0, COM1_BASE_ADDR
1:
li v1, FIFO_ENABLE|FIFO_RCV_RST|FIFO_XMT_RST|FIFO_TRIGGER_4
sb v1, NSREG(NS16550_FIFO)(v0)
li v1, CFCR_DLAB
sb v1, NSREG(NS16550_CFCR)(v0)
li v1, NS16550HZ/2/(16*CONS_BAUD)
sb v1, NSREG(NS16550_DATA)(v0)
srl v1, 8
sb v1, NSREG(NS16550_IER)(v0)
li v1, CFCR_8BITS
sb v1, NSREG(NS16550_CFCR)(v0)
li v1, MCR_DTR|MCR_RTS
sb v1, NSREG(NS16550_MCR)(v0)
li v1, 0x0
sb v1, NSREG(NS16550_IER)(v0)
nop
j ra
nop
END(initserial)
/*
蔡军生 2006-12-30 于深圳 */
这里先获取COM1的基地址,是通过内存映射地址实现的。接着把FIFO的设置,设置CFCR寄存器,设置波特率,而波特率设置跟系统运行的频率NS16550HZ有关。接着8位数据接收方式,设置中断允许寄存器。经过这样的初始化,就可以向串口写东西出来了,走出摸索前行的日子,进入调试的新天地。把串口接到另外一台电脑上,就可看到调试信息输出来,当然还可以实现源码级调试了。