LPC IO地址映射配置及运用
LPC概念
LPC全称为Low Pin Count,是Intel用来替换ISA(Industry Standard Architecture)总线的,用来减少总线引脚数的。在x86芯片上,LPC是以PCI桥模式存在的,其设备功能号一般为PCI D31:F0。同时该LPC桥下还挂有其它的子设备,以Xeon D-1500 CPU为例,该PCI桥下还挂有DMA、Timer、RTC、GPIO、电源管理、中断控制器等子设备。
标准的LPC接口设备连接图:
假如我们这里的LPC Device为一个普通的CPLD设备,我们想在Linux内核通过LPC控制器来读写CPLD设备的地址空间,比如该CPLD地址空间为256字节。那对应的驱动代码该如何编写呢?
首先,我们先了解下x86上的IO地址空间的知识。比如上面的CPLD地址空间就是属于IO地址空间。x86的IO地址空间为16位,大小为64KB,地址范围为0x0:0xFFFF。一般划分为固定IO地址空间和可配IO地址空间。固定IO地址空间是属于CPU固定使用或保留的空间,可配IO地址空间一般不要和固定IO地址空间冲突,否则不利于产品兼容和出现未知异常问题。固定IO地址空间如下图所示,在CPU芯片手册上会有描述:
其次,LPC桥配置空间寄存器有提供4个LPC接口通用IO地址空间映射寄存器(LPC I/F Generic Decode Range 1 Register),如下图所示,我们可以选择任意的一个寄存器用来映射CPLD设备。
注:这4个寄存器需要在BIOS下开启使能才可以,这样在内核下该解码功能才可以正常工作。
该寄存器有3个域值,比特0为使能位,比特15:2为映射的IO基址,比特23:18为映射大小的掩码位,基址和掩码位均为忽略最低2比特位,比如该值配置为0x00fc0901,则表示,该窗口使能,基址为0x900,大小为256字节。
内核shell下可以通过lspci -xxx -s 00:1f.0命令来查看LPC桥配置空间寄存器值,如下图:
驱动示例
我们找到0x0900-0x09FF这段无人使用的IO地址空间给CPLD设备使用,大小为256字节。使用LPC I/F Generic Decode Range 4 Register来进行映射。驱动初始化配置及使用代码如下:
static u16 lpc_cpld_base1 = 0x900;
u8 lpc_cpld_read_reg(int index, u16 address)
{
u8 reg_val;
u16 cpld_base;
cpld_base = lpc_cpld_base1;
reg_val = inb(cpld_base + (address & 0xff));
LPC_LOG_DBG(LOG_DEBUG, "cpld_base:0x%x, address:0x%x, value:0x%02x\n",
cpld_base, address, reg_val);
return reg_val;
}
void lpc_cpld_write_reg(int index, u16 address, u8 reg_val)
{
u16 cpld_base;
cpld_base = lpc_cpld_base1;
outb((reg_val & 0xff), cpld_base + (address & 0xff));
LPC_LOG_DBG(LOG_DEBUG, "cpld_base:0x%x, address:0x%x, value:0x%02x\n",
cpld_base, address, reg_val);
return;
}
static int __init lpc_dbg_init(void)
{
struct pci_dev *pdev = NULL;
printk("lpc_dbg_init\n");
pdev = pci_get_device(0x8086, 0x8c54, pdev);
if (!pdev) {
printk("pci_get_device(0x8086, 0x8c54) failed!\n");
}
lpc_pdev = pdev;
/*
* LPC I/F Generic Decode Range 4 Register for cpld1 0x0900-0x09FF
*/
pci_write_config_dword(pdev, 0x90, 0xfc0901);
if (!request_region(lpc_cpld_base1, 0x100, "lpc_cpld1")) {
printk("request_region 0x%x failed!\n", lpc_cpld_base1);
return -EBUSY;
}
return 0;
}
运行结果
下图即为上述代码运行后的LPC桥配置空间寄存器值及dump出来的CPLD IO地址空间内容。