芯片手册:http://m4udit.dinauz.org/P2020RM_rev0.pdf
PowerPC体系结构Freescale架构系列芯片可以将l2_cache配置成l2_cache与sram结合的策略来使用,在内核中是以设备的方式来驱动l2cache_sram。驱动定义在arch/powerpc/platforms/85xx/Kconfig中,配置项FSL_85XX_CACHE_SRAM,核心代码为fsl_85xx_l2ctlr.c和fsl_85xx_cache_sram.c,均在arch/powerpc/sysdev路径下,主要结构定义在头文件arch/powerpc/sysdev/fsl_85xx_cache_ctlr.h中。
模块注册:
static __init int mpc85xx_l2ctlr_of_init(void)
{
return platform_driver_register(&mpc85xx_l2ctlr_of_platform_driver);
}
static void __exit mpc85xx_l2ctlr_of_exit(void)
{
platform_driver_unregister(&mpc85xx_l2ctlr_of_platform_driver);
}
/* 模块出口和入口 */
subsys_initcall(mpc85xx_l2ctlr_of_init);
module_exit(mpc85xx_l2ctlr_of_exit);
L2-Cache Sram驱动的注册过程如下:
mpc85xx_l2ctlr_of_probe
platform_drv_probe
driver_probe_device
__driver_attach
bus_for_each_dev
bus_add_driver
driver_register
do_one_initcall
kernel_init_freeable
kernel_init
ret_from_kernel_thread
实际上为Linux内核中驱动通用的注册逻辑,此处为platform设备驱动注册。
驱动定义:
static struct platform_driver mpc85xx_l2ctlr_of_platform_driver = {
.driver = {
.name = "fsl-l2ctlr",
.of_match_table = mpc85xx_l2ctlr_of_match,
},
.probe = mpc85xx_l2ctlr_of_probe,
.remove = mpc85xx_l2ctlr_of_remove,
};
其中有一项为兼容性列表,其定义如下:
static const struct of_device_id mpc85xx_l2ctlr_of_match[] = {
{
.compatible = "fsl,p2020-l2-cache-controller",
},
{
.compatible = "fsl,p2010-l2-cache-controller",
},
{
.compatible = "fsl,p1020-l2-cache-controller",
},
{
.compatible = "fsl,p1011-l2-cache-controller",
},
{
.compatible = "fsl,p1013-l2-cache-controller",
},
{
.compatible = "fsl,p1022-l2-cache-controller",
},
{
.compatible = "fsl,mpc8548-l2-cache-controller",
},
{ .compatible = "fsl,mpc8544-l2-cache-controller",},
{ .compatible = "fsl,mpc8572-l2-cache-controller",},
{ .compatible = "fsl,mpc8536-l2-cache-controller",},
{ .compatible = "fsl,p1021-l2-cache-controller",},
{ .compatible = "fsl,p1012-l2-cache-controller",},
{ .compatible = "fsl,p1025-l2-cache-controller",},
{ .compatible = "fsl,p1016-l2-cache-controller",},
{ .compatible = "fsl,p1024-l2-cache-controller",},
{ .compatible = "fsl,p1015-l2-cache-controller",},
{ .compatible = "fsl,p1010-l2-cache-controller",},
{ .compatible = "fsl,bsc9131-l2-cache-controller",},
{},
};
驱动模块仅仅实现了probe和remove接口。先看remove接口如下:
static int mpc85xx_l2ctlr_of_remove(struct platform_device *dev)
{
BUG_ON(!l2ctlr);
iounmap(l2ctlr);
remove_cache_sram(dev);
dev_info(&dev->dev, "MPC85xx L2 controller unloaded\n");
return 0;
}
在这里可以看到重要的全局参数l2ctlr,为l2-cache-sram控制寄存器集对应的结构定义。因为在RISC架构上采用统一编址,对于外设的访问和控制等全部通过寄存器读写等进行,此处对于l2-cache-sram寄存器也是类似的访问方式,硬件设计中指定寄存器对应的物理地址编址,其地址信息在dts文件中配置,初始化过程中通过解析dts节点获得,而实际的访问需要转化为虚拟地址进行。这里就进入了probe函数初始化的过程了。其中通过of_iomap接口直接将dts解析得到的信息映射为虚拟内存而返回虚拟地址得到,一遍后续配置l2-cache-sram。而我们看到在remove函数中首先是对获取到的虚拟地址空间进行释放。
另外一个重要的事情是对l2-cache-sram配置为sram的内存部分进行释放,包括其申请到的物理内存区域和实际使用中分配的虚拟地址空间信息,在remove_cache_sram函数中完成。
static int mpc85xx_l2ctlr_of_probe(struct platform_device *dev)
{
long rval;
unsigned int rem;
unsigned char ways;
const unsigned int *prop;
unsigned int l2cache_size;
struct sram_parameters sram_params;
if (!dev->dev.of_node) {
dev_err(&dev->dev, "Device's OF-node is NULL\n");
return -EINVAL;
}
prop = of_get_property(dev->dev.of_node, "cache-size", NULL);
if (!prop) {
dev_err(&dev->dev, "Missing L2 cache-size\n");
return -EINVAL;
}
l2cache_size = *prop;
if (get_cache_sram_params(&sram_params)) {
dev_err(&dev->dev,
"Entire L2 as cache, provide valid sram offset and size\n");
return -EINVAL;
}
rem = l2cache_size % sram_params.sram_size;
ways = LOCK_WAYS_FULL * sram_params.sram_size / l2cache_size;
if (rem || (ways & (ways - 1))) {
dev_err(&dev->dev, "Illegal cache-sram-size in command line\n");
return -EINVAL;
}
l2ctlr = of_iomap(dev->dev.of_node, 0);
if (!l2ctlr) {
dev_err(&dev->dev, "Can't map L2 controller\n");
return -EINVAL;
}
/*
* Write bits[0-17] to srbar0
*/
/* setup window for L2SRAM */
out_be32(&l2ctlr->srbar0,
lower_32_bits(sram_params.sram_offset) & L2SRAM_BAR_MSK_LO18);
/*
* Write bits[18-21] to srbare0
*/
#ifdef CONFIG_PHYS_64BIT
out_be32(&l2ctlr->srbarea0,
upper_32_bits(sram_params.sram_offset) & L2SRAM_BARE_MSK_HI4);
#endif
/* Disable L2SRAM to allow changing of block size */
clrsetbits_be32(&l2ctlr->ctl, L2CR_L2E, L2CR_L2FI);
switch (ways) {
case LOCK_WAYS_EIGHTH:
setbits32(&l2ctlr->ctl,
L2CR_L2E | L2CR_L2FI | L2CR_SRAM_EIGHTH);
break;
case LOCK_WAYS_TWO_EIGHTH:
setbits32(&l2ctlr->ctl,
L2CR_L2E | L2CR_L2FI | L2CR_SRAM_QUART);
break;
case LOCK_WAYS_HALF:
setbits32(&l2ctlr->ctl,
L2CR_L2E | L2CR_L2FI | L2CR_SRAM_HALF);
break;
case LOCK_WAYS_FULL:
default:
setbits32(&l2ctlr->ctl,
L2CR_L2E | L2CR_L2FI | L2CR_SRAM_FULL);
break;
}
eieio();
rval = instantiate_cache_sram(dev, sram_params);
if (rval < 0) {
dev_err(&dev->dev, "Can't instantiate Cache-SRAM\n");
iounmap(l2ctlr);
return -EINVAL;
}
return 0;
}
另外两个参数cache-sram-size和cache-sram-offset为启动参数信息,其初始化使用内核通用的初始化参数设置框架和接口__setup进行设置,实际上只是读取了字符串信息,实际的赋值判断过程在模块初始化时调用get_cache_sram_params函数进行,同样在probe接口中完成了调用过程。这两个参数的意义很明确,其中cache-sram-offset为给配置的sram指定的物理内存地址,其实就是内存区域的起始地址,而同样的cache-sram-size为内存区域的大小,这段内存区域配置不能与已经存在的内存节点区域信息重合,在32位系统4G的物理内存范围内可以较为自有地指定,在cmdline中以bootargs参数信息的形式配置。
要解读mpc85xx_l2ctlr_of_probe函数执行过程,需要了解芯片架构指定的寄存器的意义:
L2CTL—L2 control register控制寄存器的偏移为0x20000,在soc偏移地址的基础上加入该偏移进行映射就得到了寄存器集合的地址信息,从而给l2ctlr赋值。在初始化过程中:
- 首先设置了sram基址寄存器,指定了基地址信息srbar0成员寄存器(u32 srbar0; /* 0x100 - SRAM base address 0 */);
- 然后关闭L2SRAM以便设置sram以及l2-cache的大小,然后再开启L2SRAM;
- 紧接着调用instantiate_cache_sram对cache_sram进行初始化,该成员代表了sram对应的内存节点(struct mpc85xx_cache_sram *cache_sram;);对其初始化分为三点,请求内存节点区域、对物理内存节点区域进行ioremap_prot而分配虚拟内存空间以及为内存的分配做好准备工作(rh结构的attach工作)。
上面提到的主要是cache-sram的初始化工作,主要在文件fsl_85xx_l2ctlr.c中展开,而对外提供的接口则在fsl_85xx_cache_sram.c文件中实现,即sram内存的分配和释放。
struct mpc85xx_cache_sram *cache_sram;
void *mpc85xx_cache_sram_alloc(unsigned int size,
phys_addr_t *phys, unsigned int align)
{
unsigned long offset;
unsigned long flags;
if (unlikely(cache_sram == NULL))
return NULL;
if (!size || (size > cache_sram->size) || (align > cache_sram->size)) {
pr_err("%s(): size(=%x) or align(=%x) zero or too big\n",
__func__, size, align);
return NULL;
}
if ((align & (align - 1)) || align <= 1) {
pr_err("%s(): align(=%x) must be power of two and >1\n",
__func__, align);
return NULL;
}
spin_lock_irqsave(&cache_sram->lock, flags);
offset = rh_alloc_align(cache_sram->rh, size, align, NULL);
spin_unlock_irqrestore(&cache_sram->lock, flags);
if (IS_ERR_VALUE(offset))
return NULL;
*phys = cache_sram->base_phys + offset;
return (unsigned char *)cache_sram->base_virt + offset;
}
EXPORT_SYMBOL(mpc85xx_cache_sram_alloc);
void mpc85xx_cache_sram_free(void *ptr)
{
unsigned long flags;
BUG_ON(!ptr);
spin_lock_irqsave(&cache_sram->lock, flags);
rh_free(cache_sram->rh, ptr - cache_sram->base_virt);
spin_unlock_irqrestore(&cache_sram->lock, flags);
}
EXPORT_SYMBOL(mpc85xx_cache_sram_free);
接下来再看我们的dts配置信息:
/dts-v1/;
/ {
compatible = "fsl,P1021RDB-PC";
#address-cells = <0x2>;
#size-cells = <0x2>;
interrupt-parent = <0x1>;
model = "fsl,P2020DS";
soc@d0000000 {
ranges = <0x0 0x0 0xd0000000 0x100000>;
#address-cells = <0x1>;
#size-cells = <0x1>;
device_type = "soc";
compatible = "fsl,p2020-immr", "simple-bus";
bus-frequency = <400000000>;
l2-cache-controller@20000 {
compatible = "fsl,p2020-l2-cache-controller";
reg = <0x20000 0x1000>;
cache-line-size = <0x20>;
cache-size = <0x80000>;
interrupts = <0x10 0x2 0x0 0x0>;
linux,phandle = <0x2>;
phandle = <0x2>;
};
chosen {
name = "chosen";
bootargs = "console=ttyS0,9600 cache-sram-size=0x10000 cache-sram-offset=0x7ff00000";
};
}