芯片手冊: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";
};
}