u-boot加载dsp代码分析

加载dsp核心由下面的spl_load_cores函数作为入口进行执行。函数中主要执行spl_mmc_load_core函数将dsp核心执行文件从文件中加载到缓存。spl_boot_core函数对已经加载到缓存的执行文件进行处理然后加载到指定的内存还有对dsp设备进行配置然后启动核心。

void spl_load_cores(u32 boot_device, u32 *cores, u32 numcores)
{
	u32 i = 0;

	for (i = 0; i < numcores ; i++) {  		//circulation numcores times
		u32 core = cores[i];
		if (spl_mmc_load_core(core, boot_device) ||
		    spl_boot_core(core))
		    {
			cores[i] = (cores[i] | SPL_CORE_LOAD_ERR_ID);
			printf("Error loading remotecore %s!,"
				 "Continuing with boot ...\n",
				 rproc_cfg_arr[core]->core_name);
			} 
		else 
			{
			debug("loading remote core %s successful\n",
			      rproc_cfg_arr[core]->core_name);
			}
	}
	return;
}

spl_mmc_load_core函数中主要实现了对内存进行初始化,通过固件名字找到固件并将固件从文件加载到缓存buf中,然后将缓存buf的地址赋值给cfg->load_addr提供给接下来解析固件ELF文件使用。

u32 spl_mmc_load_core(u32 core_id, u32 boot_device)
{
	struct rproc *cfg = NULL;
	s32 err = 0;
	struct mmc *mmc;

	if ((core_id == 0) || (core_id >= RPROC_END_ENUMS)) {
		printf("Invalid core id speicified: %d\n", core_id);
		return 1;
	}

	cfg = rproc_cfg_arr[core_id];

	debug("spl: loading remote core image %s\n", cfg->firmware_name);

	spl_mmc_init(&mmc, boot_device);

	/* load the remote core image from partition */
	if (cfg->ptn) {
		disk_partition_t info;

		err = part_get_info_efi_by_name(&mmc->block_dev,
						     cfg->ptn, &info);
		if (err) {
#ifdef CONFIG_SPL_LIBCOMMON_SUPPORT
			printf("cannot find partition: '%s'\n",
			       cfg->ptn);
#endif
		} else if (!mmc->block_dev.block_read(&mmc->block_dev,
						      info.start,
						      info.size,
						      (void *)cfg->load_addr)) {
			printf("error reading from mmc\n");
			err = -1;
		} else {
			err = 1;
		}
	} else {
		/* load the remotecore image */
		err = spl_load_file_fat(&mmc->block_dev,
					CONFIG_SYS_MMCSD_FS_BOOT_PARTITION,
					cfg->firmware_name,
					(u8 *)cfg->load_addr);   //get image to cfg->load_addr
	}

	if (err <= 0) {
		printf("spl: error reading image %s, err - %d\n",
		       cfg->firmware_name, err);
		return 1;
	}

	return 0;
}

下面代码所示core_id参数对应的是那个核心,core_id的范围分别是 1~4

	if ((core_id == 0) || (core_id >= RPROC_END_ENUMS)) {
		printf("Invalid core id speicified: %d\n", core_id);
		return 1;
	}
enum rproc_core_id {
	IPU2 = 1,
	IPU1 = 2,
	DSP2 = 3,
	DSP1 = 4,
	RPROC_END_ENUMS = 5
};

下面代码中咱们以加载的是dsp1为例,先定义一个rproc结构体变量名为cfg的指针,然后对其进行赋值rproc_cfg_arr数组里下标为core_id的元素,rproc_cfg_arr数组中的元素对应的是一个结构体地址。所以当core_id为4时对应的是下面dsp1_config结构体的地址。dsp1_config 结构体放置了加载dsp1的配置数据,执行函数的地址这些。(dsp1_config结构体是全局变量随时可以进行调用)

struct rproc *cfg = NULL;
cfg = rproc_cfg_arr[core_id];
struct rproc *rproc_cfg_arr[RPROC_END_ENUMS] = {
	[IPU2] = &ipu2_config,
	[IPU1] = &ipu1_config,
	[DSP2] = &dsp2_config,
	[DSP1] = &dsp1_config
};
struct rproc dsp1_config = {
	.num_iommus = 2,
	.cma_base = DRA7_RPROC_CMA_BASE_DSP1,
	.cma_size = DRA7_RPROC_CMA_SIZE_DSP1,
	.page_table_addr = DRA7_PGTBL_BASE_DSP1,
	.mmu_base_addr = {0x40D01000, 0x40D02000},
	.load_addr = DSP1_LOAD_ADDR,
	.core_name = "DSP1",
	.firmware_name = "dra7-dsp1-fw.xe66",
#ifdef CONFIG_MMC_DSP1_PART_NAME
	.ptn = CONFIG_MMC_DSP1_PART_NAME,
#else
	.ptn = NULL,
#endif
	.start_clocks = dsp_start_clocks,
	.config_mmu = ipu_config_mmu,
	.config_peripherals = dsp1_config_peripherals,
	.start_core = dsp_start_core,
	.intmem_to_l3_mapping = &dsp1_intmem_to_l3_mapping
};

spl_mmc_init函数对flash进行初始化,这样才能对放在flash中的dsp固件进行操作

	spl_mmc_init(&mmc, boot_device);

上面通过spl_mmc_init函数对flash进行初始化后进行操作固件.firmware_name = “dra7-dsp1-fw.xe66”,。

struct mmc *mmc;
        disk_partition_t info;
		err = part_get_info_efi_by_name(&mmc->block_dev,
						     cfg->ptn, &info);

    .ptn = CONFIG_MMC_DSP1_PART_NAME,
    #define CONFIG_MMC_DSP1_PART_NAME "dsp1"

mmc->block_dev.block_read(&mmc->block_dev,
						      info.start,
						      info.size,
						      (void *)cfg->load_addr)

下面的spl_load_file_fat函数用于从flash中将dsp固件加载到缓存中并将缓存的地址赋值给cfg->load_addr

spl_load_file_fat(&mmc->block_dev,
					CONFIG_SYS_MMCSD_FS_BOOT_PARTITION,
					cfg->firmware_name,
					(u8 *)cfg->load_addr);   //get image to cfg->load_addr`

下面的spl_boot_core函数主要做了对dps固件的合法性进行检验,找到资源表,对dsp的外设比如电源,时钟,mmu等进行初始化。然后对dsp固件elf文件进行处理 将缓存的elf文件中的各个段加载到内存指定的位置,然后开始启动dsp。

u32 spl_boot_core(u32 core_id)
{
	struct rproc *cfg = NULL;
	unsigned long load_elf_status = 0;
	int tablesz;

	if ((core_id == 0) || (core_id >= RPROC_END_ENUMS)) {
		debug("Invalid core id speicified: %d\n", core_id);
		return 1;
	}

	cfg = rproc_cfg_arr[core_id];

	/* Check for valid elf image */ //检查dsp固件elf文件的合法性
	if (!valid_elf_image_local(cfg->load_addr))
		return 1;
	else
		debug("Core %d ELF Image is valid\n", core_id);

	if (find_resource_table(cfg->load_addr, &tablesz)) //查到资源表
		cfg->has_rsc_table = 1;
	else
		cfg->has_rsc_table = 0;

	/* Clock the remote core */   //开启dsp时钟
	if (cfg->start_clocks)
		cfg->start_clocks(core_id, cfg);
 
	debug("Configuring IOMMU\n");  //配置dsp的mmu

	/* Configure the MMU */
	if (cfg->config_mmu && cfg->has_rsc_table)
		cfg->config_mmu(core_id, cfg);

	debug("Configured the IOMMU\n");

	/* Load the remote core.
	 *
	 * Fill the page table of the first(possibly only) IOMMU during ELF
	 * loading.  Copy the page table to the second IOMMU before running the
	 * remote core.
	 */
    //各种elf文件各个段加载到指定的内存空间
    
	page_table_l1 = (unsigned int *)cfg->page_table_addr;
	page_table_l2 =
	    (unsigned int *)(cfg->page_table_addr + PAGE_TABLE_SIZE_L1);
	mem_base = cfg->cma_base;
	mem_size = cfg->cma_size;
	memset(mem_bitmap, 0x00, sizeof(mem_bitmap));
	mem_count = (cfg->cma_size >> PAGE_SHIFT);

	/* Clear variables used for level 2 page table allocation */
	memset(pgtable_l2_map, 0x00, sizeof(pgtable_l2_map));
	pgtable_l2_cnt = 0;

	debug("Loading ELF Image\n");
	load_elf_status = load_elf_image_phdr_rproc(cfg);
	if (load_elf_status == 0) {
		printf("load_elf_image_phdr returned error for core %s\n",
		       cfg->core_name);
		return 1;
	} else {
		debug("Core entry point is 0x%08x\n",
		      (unsigned int)cfg->entry_point);
	}
	flush_cache(cfg->page_table_addr, PAGE_TABLE_SIZE);


	if (cfg->config_peripherals)
		cfg->config_peripherals(core_id, cfg);

	/* Start running the remote core */
	debug("Starting the remote core\n");
	if (cfg->start_core)
		cfg->start_core(core_id, cfg);

	return 0;
}

下面代码主要通过函数传递参数core_id检验合法性和通过core_id获得远程加载核的配置数据

    struct rproc *cfg = NULL;
	if ((core_id == 0) || (core_id >= RPROC_END_ENUMS)) {
		debug("Invalid core id speicified: %d\n", core_id);
		return 1;
	}

	cfg = rproc_cfg_arr[core_id];

下面的valid_elf_image_local函数用于检查dsp固件elf文件的合法性

	/* Check for valid elf image */
	if (!valid_elf_image_local(cfg->load_addr))
		return 1;
	else
		debug("Core %d ELF Image is valid\n", core_id);

下面的valid_elf_image_local函数具体代码中对elf文件Elf32_Ehdr结构类型强制转换获得ehdr elf头结构体
,然后通过IS_ELF(*ehdr)和检验elf头结构体的e_type成员实现了对elf头文件的检验和elf文件类型进行检测。

static int valid_elf_image_local(unsigned long addr)
{
	Elf32_Ehdr *ehdr; /* Elf header structure pointer */

	ehdr = (Elf32_Ehdr *)addr;

	if (!IS_ELF(*ehdr)) {
		printf("## No elf image at address 0x%08lx\n", addr);
		return 0;
	}

	if (ehdr->e_type != ET_EXEC) {
		printf("## Not a 32-bit elf image at address 0x%08lx\n", addr);
		return 0;
	}

	return 1;
}

下面代码是IS_ELF*ehdr)的实现代码,检验这个elf文件是否为执行文件。

/* e_ident */
#define IS_ELF(ehdr) ((ehdr).e_ident[EI_MAG0] == ELFMAG0 && \
		      (ehdr).e_ident[EI_MAG1] == ELFMAG1 && \
		      (ehdr).e_ident[EI_MAG2] == ELFMAG2 && \
		      (ehdr).e_ident[EI_MAG3] == ELFMAG3)
#define	ELFMAG0		0x7f	/* EI_MAG */
#define	ELFMAG1		'E'
#define	ELFMAG2		'L'
#define	ELFMAG3		'F'

ehdr成员e_type用于标识文件类型,ET_EXEC表示为可执行文件

if (ehdr->e_type != ET_EXEC) {
		printf("## Not a 32-bit elf image at address 0x%08lx\n", addr);
		return 0;
	}

下面的find_resource_table函数用于找到资源表

	if (find_resource_table(cfg->load_addr, &tablesz)) //return tablesz 
		cfg->has_rsc_table = 1;
	else
		cfg->has_rsc_table = 0;

下面是find_resource_table函数的具体实现,Elf32_Shdr结构体是程序头部

struct resource_table *find_resource_table(unsigned int addr,
						  int *tablesz)
{
	Elf32_Shdr *shdr;
	Elf32_Shdr sectionheader;
	struct resource_table *ptable;  //resource table
	u8 *elf_data = (u8 *)addr;

	shdr = find_table(addr);  //find table 
	if (!shdr) {
		printf("find_resource_table: "
		       "failed to get resource section header\n");
		return NULL;
	}
	// copy resource stable to sectionheader
	memcpy(&sectionheader, shdr, sizeof(sectionheader)); 


	// go to 
	ptable = (struct resource_table *)(elf_data + sectionheader.sh_offset);
	if (tablesz)
		*tablesz = sectionheader.sh_size;

	return ptable;
}

下面的find_table函数实现了找到了resource_table节,判断是否这个节有错误,并返回描述resource_table节的节头结构体。

static Elf32_Shdr *find_table(unsigned int addr)
{
	Elf32_Ehdr *ehdr;	/* Elf header structure pointer     */
	Elf32_Shdr *shdr;	/* Section header structure pointer */
	Elf32_Shdr sectionheader;
	int i;
	u8 *elf_data;
	char *name_table;
	struct resource_table *ptable;

	ehdr = (Elf32_Ehdr *)addr;
	elf_data = (u8 *)ehdr;
	shdr = (Elf32_Shdr *)(elf_data + ehdr->e_shoff);
	memcpy(&sectionheader, &shdr[ehdr->e_shstrndx], sizeof(sectionheader));
	name_table = (char *)(elf_data + sectionheader.sh_offset);

	for (i = 0; i < ehdr->e_shnum; i++, shdr++) {
		memcpy(&sectionheader, shdr, sizeof(sectionheader));
		u32 size = sectionheader.sh_size;
		u32 offset = sectionheader.sh_offset;

		if (strcmp(name_table + sectionheader.sh_name,
			   ".resource_table"))
			continue;

		ptable = (struct resource_table *)(elf_data + offset);

		/* make sure table has at least the header */
		if (sizeof(struct resource_table) > size) {
			printf("header-less resource table\n");
			return NULL;
		}

		/* we don't support any version beyond the first */
		if (ptable->ver != 1) {
			printf("unsupported fw ver: %d\n", ptable->ver);
			return NULL;
		}

		/* make sure reserved bytes are zeroes */
		if (ptable->reserved[0] || ptable->reserved[1]) {
			printf("non zero reserved bytes\n");
			return NULL;
		}

		/* make sure the offsets array isn't truncated */
		if (ptable->num * sizeof(ptable->offset[0]) +
		    sizeof(struct resource_table) > size) {
			printf("resource table incomplete\n");
			return NULL;
		}

		return shdr;
	}

	return NULL;
}

下面这段函数是获取资源表section table的核心代码。

	Elf32_Ehdr *ehdr;	/* Elf header structure pointer     */
	Elf32_Shdr *shdr;	/* Section header structure pointer */
	Elf32_Shdr sectionheader;    // 一个实例的结构体
	int i;
	u8 *elf_data;
	char *name_table;
	struct resource_table *ptable;

	ehdr = (Elf32_Ehdr *)addr;
	elf_data = (u8 *)ehdr;
	shdr = (Elf32_Shdr *)(elf_data + ehdr->e_shoff);
	memcpy(&sectionheader, &shdr[ehdr->e_shstrndx], sizeof(sectionheader));
	name_table = (char *)(elf_data + sectionheader.sh_offset);

	for (i = 0; i < ehdr->e_shnum; i++, shdr++) {
		memcpy(&sectionheader, shdr, sizeof(sectionheader));
		u32 size = sectionheader.sh_size;
		u32 offset = sectionheader.sh_offset;

		if (strcmp(name_table + sectionheader.sh_name,
			   ".resource_table"))
			continue;

		ptable = (struct resource_table *)(elf_data + offset);

下面的代码分别是获取elf文件的结构体指针, 然后将这个指针赋值给elf_data。获取节头结构体。

	ehdr = (Elf32_Ehdr *)addr;
	elf_data = (u8 *)ehdr;
	shdr = (Elf32_Shdr *)(elf_data + ehdr->e_shoff);

e_shstrndx是文件头结构体中的成员用于描述表示字符串表在节头表中的索引,一般为节头表中的最后一个,每一个节对应一个节头结构体。shdr[ehdr->e_shstrndx]对应一个节头结构体数组的一个数值,取其地址为描述shstrndx节的节头结构体。然后将这个节头结构体的数据使用memcpy函数拷贝到sectionheader中。然后通过这个描述shstrndx节的节头结构体找到.shstrtab节的地址。

	memcpy(&sectionheader, &shdr[ehdr->e_shstrndx], sizeof(sectionheader));
	name_table = (char *)(elf_data + sectionheader.sh_offset);

通过上面找到.shstrtab节的地址,.shstrtab节里面装着各个节的节名。然后通过各个节头结构体的成员sh_name节名字索引找到资源表的节名。这样这个节头结构体描述的就是资源表节信息。通过获取这个资源表节头的sh_offset成员来得到资源表节的地址ptable 。

	for (i = 0; i < ehdr->e_shnum; i++, shdr++) {
		memcpy(&sectionheader, shdr, sizeof(sectionheader));
		u32 size = sectionheader.sh_size;
		u32 offset = sectionheader.sh_offset;

		if (strcmp(name_table + sectionheader.sh_name,
			   ".resource_table"))
			continue;

		ptable = (struct resource_table *)(elf_data + offset);

下面代码用于校验资源表节是否有问题,首先检验这个资源表节会不会小于resource_table的结构体大小,这样的话这个资源节的内存空间都放不下这个resource_table结构体。然后判断一些资源表的检验信息。接着返回者为描述这个资源表节的节头结构体。

		/* make sure table has at least the header */
		if (sizeof(struct resource_table) > size) {
			printf("header-less resource table\n");
			return NULL;
		}

		/* we don't support any version beyond the first */
		if (ptable->ver != 1) {
			printf("unsupported fw ver: %d\n", ptable->ver);
			return NULL;
		}

		/* make sure reserved bytes are zeroes */
		if (ptable->reserved[0] || ptable->reserved[1]) {
			printf("non zero reserved bytes\n");
			return NULL;
		}

		/* make sure the offsets array isn't truncated */
		if (ptable->num * sizeof(ptable->offset[0]) +
		    sizeof(struct resource_table) > size) {
			printf("resource table incomplete\n");
			return NULL;
		}

		return shdr;

回到find_resource_table函数,下面的代码实现了将返回资源表节头文件结构体拷贝到sectionheader中。然后通过这个节头文件结构体的成员sh_offset获取到资源节的偏移量sh_offset,通过文件头结构体的地址加上这个偏移量sh_offset就可以获得资源表的节地址。然后返回资源表的节地址

	// copy resource stable to sectionheader
	memcpy(&sectionheader, shdr, sizeof(sectionheader));   //copy data 

	// go to 
	ptable = (struct resource_table *)(elf_data + sectionheader.sh_offset);
	if (tablesz)
		*tablesz = sectionheader.sh_size;

	return ptable;

回到spl_boot_core函数,由于find_resource_table如果找到资源表结构体会返回一个地址所示这个判断将会是真,故
cfg->has_rsc_table = 1;

	if (find_resource_table(cfg->load_addr, &tablesz)) //return tablesz 
		cfg->has_rsc_table = 1;
	else
		cfg->has_rsc_table = 0;

下面的cfg->start_clocks(core_id, cfg)函数是用于配置dsp时钟。

	/* Clock the remote core */
	if (cfg->start_clocks)
		cfg->start_clocks(core_id, cfg);

dsp_start_clocks函数使用时钟配置需要配置几个时钟域:common时钟域、dpll、TIMER_SYS_CLK、watchdog、SW Wkup等等,以及需要启动dpll,设置reset信号

u32 dsp_start_clocks(u32 core_id, struct rproc *cfg)
{
	u32 reg = 0;
	u32 timer_reg = 0;
	u32 dsp_clkstctrl = 0;
	u32 prm_base = 0;
	u32 mmu_config = 0;
	u32 wdt_ctrl = 0;

	if ((core_id != DSP1) && (core_id != DSP2))
		return 1;

	debug("DSP initialization in progress\n");
	enable_common_clocks();

	/* Configure the DSP PLL */
	dsp_enable_dpll();

	if (core_id == DSP1) {
		/* Enable Timer 5 for DSP1 */
		timer_reg = CM_IPU_TIMER5_CLKCTRL;
		prm_base = DSP1_PRM_BASE;
		dsp_clkstctrl = CM_DSP1_CLKSTCTRL;
		mmu_config = DSP1_SYS_MMU_CONFIG;
		wdt_ctrl = CM_L4PER_TIMER10_CLKCTRL;

	} else {
		/* Enable Timer 6 for DSP2 */
		timer_reg = CM_IPU_TIMER6_CLKCTRL;
		prm_base = DSP2_PRM_BASE;
		dsp_clkstctrl = CM_DSP2_CLKSTCTRL;
		mmu_config = DSP2_SYS_MMU_CONFIG;
		wdt_ctrl = CM_L4PER3_TIMER13_CLKCTRL;
	}

	/* Using TIMER_SYS_CLK as the clock source */
	reg = __raw_readl(timer_reg);
	__raw_writel((reg & ~0x0F000003) | 0x00000002, timer_reg);
	debug("Enabled SysBIOS Tick Timer\n");

	/* Enable the watchdog timer */
	if (wdt_ctrl != 0) {
		/*
		 * Using SYS_CLK1_32K_CLK as the clock source for the
		 * watch dog timers. DSP will eventually configure
		 * these timers with the right clock source. If we use
		 * a higher frequency clock as the clock source, the
		 * timer will overflow and trigger a watchdog interrupt
		 * even before the kernel has a chance to connect to
		 * DSP.
		 */
		reg = __raw_readl(wdt_ctrl);
		__raw_writel((reg & ~0x0F000003) | 0x01000002,
			     wdt_ctrl);
	}

	/* Enable the DSP Clock domain in SW Wkup */
	__raw_writel(0x2, dsp_clkstctrl);

	/* Enable DSP and check that the clock is gated in the clock domain
	   register */
	__raw_writel(0x1, dsp_clkstctrl + 0x20);
	while ((__raw_readl(dsp_clkstctrl) & 0x100) != 0x100);
	debug("DSP Clock enabled and gated in domain controller\n");

	/*
	 * Clear the prm status bits before bringing the core out of reset.
	 * This will prevent the below status checks from passing prematurely
	 * if the DSP core was powered on and off earlier in U-Boot.
	 * The register is write '1' to clear a bit. */
	__raw_writel(0x3, prm_base + 0x14);

	/*
	 * Enable RESET for the DSP MMU, cache and slave interface and
	 * DSP local reset. This may not be necessary since the reset value
	 * is the same.*/
	__raw_writel(0x3, prm_base + 0x10);

	/* Bring the MMU, cache and reset interface out of reset */
	__raw_writel(0x1, prm_base + 0x10);

	/*
	 * Check that the reset state reflects correctly in the status
	 * register.
	 */
	while ((__raw_readl(prm_base + 0x14) & 0x2) != 0x2);
	debug("DSP MMU out of reset\n");

	/* Enable the DSP1 SDMA and MDMA accesses to pass through the MMU */
	if (cfg->has_rsc_table)
		__raw_writel(0x11, mmu_config);

	/* At this point, the DSP MMU can be configured. */

	debug("DSP ready for MMU configuration and code loading\n");

	return 0;
}
enable_common_clocks();
void enable_common_clocks(void)
{
	static u32 do_config = 1;

	if (do_config != 1)
		return;

	/* TODO: Review if all of these are actually
	 * necessary.
	 */

	/* enable CORE domain transitions */
	__raw_writel(0x2, CM_CAM_CLKSTCTRL);  // 0x4A009000 软件唤醒CAM clock时钟域
	__raw_writel(0x2, CM_L3INIT_CLKSTCTRL);  //0x4A009000 软件唤醒L3INIT clock时钟域
	__raw_writel(0x2, CM_GMAC_CLKSTCTRL);  //0x4A0090C0 软件唤醒GMAC clock时钟域
	__raw_writel(0x2, CM_EMIF_CLKSTCTRL); //0x4A008B00 软件唤醒EMIF clock时钟域
	__raw_writel(0x2, CM_L4CFG_CLKSTCTRL); //0x4A008D00 软件唤醒L4CFG clock时钟域
	__raw_writel(0x2, CM_DMA_CLKSTCTRL);    //0x4A008A00 软件唤醒DMA clock时钟域
	__raw_writel(0x2, CM_COREAON_CLKSTCTRL); //0x4A008600 软件唤醒COREAON
	__raw_writel(0x2, CM_L4PER_CLKSTCTRL);  //0x4A009700 软件唤醒L4PER1 clock时钟域
	__raw_writel(0x2, CM_L4PER3_CLKSTCTRL); //0x4A009910 软件唤醒L4PER2 clock时钟域

	/* Some of the timers are on the IPU clock domain */
	__raw_writel(0x2, CM_IPU_CLKSTCTRL);  //0x4A005540软件唤醒ABE(audio backend)时钟域
	do_config = 0;

	return;
}

配置dsp高频时钟

配置dpll获得高频时钟,首先先判断是否已经配置过,

u32 dsp_enable_dpll(void)
{
	static u32 dpll_do_init = 1;
	u32 dpll_m = 150;
	u32 dpll_n = 4;
	u32 divm2 = 1;
	u32 dpll_base_addr = CM_CLKMODE_DPLL_DSP;

	/* return if the DPLL is already configured */
	if (dpll_do_init == 0) {
		debug("DSP DPLL configuration already configured\n");
		return 0;
	}

	/* We are assuming that the DPLL is unlocked and
	   we do not need to unlock it.
	 */

	debug("DSP DPLL configuration in progress\n");

	if (__raw_readl(dpll_base_addr + 0x4) & 0x1) {
		debug("DSP DPLL already locked, now unlocking....\n");
		dpll_unlock_sequence(dpll_base_addr);
	}

	__raw_writel(((dpll_m << 8) | dpll_n), dpll_base_addr + 0x0C);
	__raw_writel(divm2, dpll_base_addr + 0x10);

	/* CM_DIV_M3_DPLL - not used in default configuration */
	/* Output of M3 divider can be routed to EVE if required */
	__raw_writel(0x3, dpll_base_addr + 0x14);

	dpll_lock_sequence(dpll_base_addr);

	debug("DSP DPLL configuration is DONE!\n");
	dpll_do_init = 0;

	return 0;
}
/******************************************************************************
 * dpll_unlock_sequence() : DPLL unlock sequence
 *****************************************************************************/
void dpll_unlock_sequence(u32 base_address)
{
	u32 reg = 0;

	reg = __raw_readl(base_address);

	/* Take DPLL out of lock mode */
	__raw_writel((reg & (~0x1)), base_address);
}

下面通过判断core_id知道加载的是dsp1还有dsp2,然后给其分配对应的配置数据

if (core_id == DSP1) {
		/* Enable Timer 5 for DSP1 */
		timer_reg = CM_IPU_TIMER5_CLKCTRL;
		prm_base = DSP1_PRM_BASE;
		dsp_clkstctrl = CM_DSP1_CLKSTCTRL;
		mmu_config = DSP1_SYS_MMU_CONFIG;
		wdt_ctrl = CM_L4PER_TIMER10_CLKCTRL;

	} else {
		/* Enable Timer 6 for DSP2 */
		timer_reg = CM_IPU_TIMER6_CLKCTRL;
		prm_base = DSP2_PRM_BASE;
		dsp_clkstctrl = CM_DSP2_CLKSTCTRL;
		mmu_config = DSP2_SYS_MMU_CONFIG;
		wdt_ctrl = CM_L4PER3_TIMER13_CLKCTRL;
	}
	/* Using TIMER_SYS_CLK as the clock source */
	reg = __raw_readl(timer_reg);
	__raw_writel((reg & ~0x0F000003) | 0x00000002, timer_reg);
	debug("Enabled SysBIOS Tick Timer\n");
timer_reg = CM_IPU_TIMER5_CLKCTRL;
#define CM_IPU_TIMER5_CLKCTRL        (IPU_CM_CORE_AON + 0x58)
#define IPU_CM_CORE_AON              (CM_CORE_AON + 0x500)
#define CM_CORE_AON                  (L4_CFG_TARG + 0x5000)
#define L4_CFG_TARG                  0x4A000000

在这里插入图片描述

配置dsp的mmu

	debug("Configuring IOMMU\n");

	/* Configure the MMU */
	if (cfg->config_mmu && cfg->has_rsc_table)
		cfg->config_mmu(core_id, cfg);

配置页表地址

	page_table_l1 = (unsigned int *)cfg->page_table_addr;
	page_table_l2 =
	    (unsigned int *)(cfg->page_table_addr + PAGE_TABLE_SIZE_L1);
	mem_base = cfg->cma_base;
	mem_size = cfg->cma_size;
.page_table_addr = DRA7_PGTBL_BASE_DSP1,
#define DRA7_PGTBL_BASE_DSP1                 (DRA7_PGTBL_BASE_IPU2 + \
					      PAGE_TABLE_SIZE)
#define DRA7_PGTBL_BASE_IPU2                 (DRA7_PGTBL_BASE_IPU1 + \
					      PAGE_TABLE_SIZE)
#define DRA7_PGTBL_BASE_IPU1                 (DRA7_PGTBL_BASE_ADDR)
#define DRA7_PGTBL_BASE_ADDR                 0xbfc00000
#define PAGE_TABLE_SIZE (PAGE_TABLE_SIZE_L1+(PAGE_TABLE_SIZE_L2_TOTAL))
#define PAGE_TABLE_SIZE_L1 (0x00004000)
#define PAGE_TABLE_SIZE_L2 (0x400)
#define MAX_NUM_L2_PAGE_TABLES (16)
#define PAGE_TABLE_SIZE_L2_TOTAL (MAX_NUM_L2_PAGE_TABLES*PAGE_TABLE_SIZE_L2)

下面将上面的宏编程一个C语言文件然后打印出他们的地址值

在这里插入图片描述

page_table_l2 = 
	    (unsigned int *)(cfg->page_table_addr + PAGE_TABLE_SIZE_L1);

一个页的大小4KB

#define PAGE_TABLE_SIZE_L1 (0x00004000)
#define DRA7_RPROC_CMA_BASE_DSP1             0x99000000
#define DRA7_RPROC_CMA_SIZE_DSP1             0x04000000

处理mem_bitmap

	memset(mem_bitmap, 0x00, sizeof(mem_bitmap));
	mem_count = (cfg->cma_size >> PAGE_SHIFT);
unsigned long mem_bitmap[BITS_TO_LONGS(DRA7_RPROC_MAX_CMA_SIZE >> PAGE_SHIFT)];
#define BITS_PER_BYTE		8
#define BITS_TO_LONGS(nr)	DIV_ROUND_UP(nr, BITS_PER_BYTE * sizeof(long))
#define DIV_ROUND_UP(n,d) (((n) + (d) - 1) / (d))
/* Set maximum carveout size to 96 MB */
#define DRA7_RPROC_MAX_CMA_SIZE (96*0x100000)
# define PAGE_SHIFT	12

mem_bitmap是一个数组 而下面打印出来的BITS_TO_LONGS(DRA7_RPROC_MAX_CMA_SIZE >> PAGE_SHIFT)就是数组的大小,memset函数是对这个数组的所有数据都写入0.

在这里插入图片描述

在原有的地址0x04000000上向左移动12位等于0x1000

mem_count = (cfg->cma_size >> PAGE_SHIFT)
	memset(pgtable_l2_map, 0x00, sizeof(pgtable_l2_map));
	pgtable_l2_cnt = 0;

pgtable_l2_map这个数组大小为16 与pgtable_l2_cnt都是全局变量

unsigned int pgtable_l2_map[MAX_NUM_L2_PAGE_TABLES];
unsigned int pgtable_l2_cnt = 0;
#define MAX_NUM_L2_PAGE_TABLES (16)

将ELF从缓存加载到内存

debug("Loading ELF Image\n");
	load_elf_status = load_elf_image_phdr_rproc(cfg);
	if (load_elf_status == 0) {
		printf("load_elf_image_phdr returned error for core %s\n",
		       cfg->core_name);
		return 1;
	} else {
		debug("Core entry point is 0x%08x\n",
		      (unsigned int)cfg->entry_point);
	}

load_elf_image_phdr_rproc函数是实现具体将ELF文件从缓存加载到内存

unsigned long load_elf_image_phdr_rproc(struct rproc *cfg)
{
	Elf32_Ehdr *ehdr;	/* Elf header structure pointer     */
	Elf32_Phdr *phdr;	/* Program header structure pointer */
	Elf32_Phdr proghdr;
	struct resource_table *ptable = NULL;
	int tablesz;
	int va;
	int pa;
	int ret;
	int i;
	unsigned long addr;

	if (cfg == 0)   // parameter check 
		return 0;

	addr = cfg->load_addr;

	ehdr = (Elf32_Ehdr *)addr;  //header structure
	phdr = (Elf32_Phdr *)(addr + ehdr->e_phoff);   //program header structure

	ptable = find_resource_table(addr, &tablesz);  //find table in elf file 
	if (!ptable) {
		printf("%s : failed to find resource table\n", __func__);
		return 0;
	} else {
		debug("%s : found resource table\n", __func__);

		rsc_table = kzalloc(tablesz, GFP_KERNEL);  //why?
		if (!rsc_table) {
			printf("resource table alloc failed!\n");
			return 0;
		}

		/* Copy the resource table into a local buffer
		 * before handling the resource table.
		 */
		memcpy(rsc_table, ptable, tablesz);  //rsc_table get resource date 

		INIT_LIST_HEAD(&mappings);   //list table 
		if (cfg->intmem_to_l3_mapping)
			handle_intmem_to_l3_mapping(cfg->intmem_to_l3_mapping);

		ret = handle_resources(tablesz, loading_handlers);
		if (ret) {
			printf("handle_resources failed: %d\n", ret);
			return 0;
		}

		/* Instead of trying to mimic the kernel flow of copying the
		 * processed resource table into its post ELF load location in
		 * DDR , I am copying it into its original location.
		 */
		memcpy(ptable, rsc_table, tablesz);

		free(rsc_table);
		rsc_table = NULL;
	}

	/* Load each program header */
	for (i = 0; i < ehdr->e_phnum; ++i) {
		memcpy(&proghdr, phdr, sizeof(Elf32_Phdr));

		if (proghdr.p_type != PT_LOAD) {
			++phdr;
			continue;
		}

		va = proghdr.p_paddr;
		pa = va_to_pa(va);
		if (pa)
			proghdr.p_paddr = pa;

		void *dst = (void *)(uintptr_t) proghdr.p_paddr;
		void *src = (void *)addr + proghdr.p_offset;

		debug("Loading phdr %i to 0x%p (%i bytes)\n",
		      i, dst, proghdr.p_filesz);
		if (proghdr.p_filesz)
			memcpy(dst, src, proghdr.p_filesz);

		
		/* TODO: This line needs to be removed after test */
		if (!ptable) {
			if ((proghdr.p_filesz != proghdr.p_memsz) &&
			    ((proghdr.p_paddr - 0x58820000)) > 0x4000 &&
			    proghdr.p_memsz > 9)
				memset(dst + proghdr.p_filesz, 0x00,
				       proghdr.p_memsz - proghdr.p_filesz);
		} else {
			if (proghdr.p_filesz != proghdr.p_memsz)
				memset(dst + proghdr.p_filesz, 0x00,
				       proghdr.p_memsz - proghdr.p_filesz);
		}

		flush_cache((unsigned long)dst, proghdr.p_memsz);

		++phdr;
	}

	cfg->entry_point = ehdr->e_entry;

	return 1;
}

下面的代码先对参数进行检查,然后再通过结构体将ELF文件强制转换成对应的结构体数据结构体,然后再通过这些数据结构对ELF文件进行解析找到资源表。

if (cfg == 0)   // parameter check 
		return 0;

	addr = cfg->load_addr;

	ehdr = (Elf32_Ehdr *)addr;  //header structure
	phdr = (Elf32_Phdr *)(addr + ehdr->e_phoff);   //program header structure

	ptable = find_resource_table(addr, &tablesz);  //find table in elf file 
	if (!ptable) {
		printf("%s : failed to find resource table\n", __func__);
		return 0;
	} else {
		debug("%s : found resource table\n", __func__);

		rsc_table = kzalloc(tablesz, GFP_KERNEL);  //why?
		if (!rsc_table) {
			printf("resource table alloc failed!\n");
			return 0;
		}

下面的handle_intmem_to_l3_mapping函数将对dsp1_intmem_to_l3_mapping 结构体的内容进行处理。将里面的结构体成员填充并进行注册。

static int handle_intmem_to_l3_mapping(struct rproc_intmem_to_l3_mapping *l3_mapping)
{
	u32 i = 0;

	for (i = 0; i < l3_mapping->num_entries; i++) {
		struct l3_map *curr_map = &l3_mapping->mappings[i];
		struct rproc_mem_entry *mapping;

		mapping = kzalloc(sizeof(*mapping), GFP_KERNEL);
		if (!mapping) {
			printf("kzalloc mapping failed\n");
			return -ENOMEM;
		}

		mapping->dma = curr_map->l3_addr;
		mapping->da = curr_map->priv_addr;
		mapping->len = curr_map->len;
		list_add_tail(&mapping->node, &mappings);
	}

	return 0;
}

参数传递

struct rproc_intmem_to_l3_mapping dsp1_intmem_to_l3_mapping = {
	.num_entries = 3,
	.mappings = {
		/* L2 SRAM */
		{
			.priv_addr = 0x00800000,
			.l3_addr = 0x40800000,
			.len = (288*1024)
		},
		/* L1P SRAM */
		{
			.priv_addr = 0x00E00000,
			.l3_addr = 0x40E00000,
			.len = (32*1024)
		},
		/* L1D SRAM */
		{
			.priv_addr = 0x00F00000,
			.l3_addr = 0x40F00000,
			.len = (32*1024)
		},
	}
};

对内存进行链表管理

static inline void list_add_tail(struct list_head *new, struct list_head *head)
{
	__list_add(new, head->prev, head);
}

将ELF文件中各个段加载到对应的内存中

/* Load each program header */
	for (i = 0; i < ehdr->e_phnum; ++i) {
		memcpy(&proghdr, phdr, sizeof(Elf32_Phdr));

		if (proghdr.p_type != PT_LOAD) {
			++phdr;
			continue;
		}

		va = proghdr.p_paddr;
		pa = va_to_pa(va);
		if (pa)
			proghdr.p_paddr = pa;

		void *dst = (void *)(uintptr_t) proghdr.p_paddr;
		void *src = (void *)addr + proghdr.p_offset;

		debug("Loading phdr %i to 0x%p (%i bytes)\n",
		      i, dst, proghdr.p_filesz);
		if (proghdr.p_filesz)
			memcpy(dst, src, proghdr.p_filesz);

		
		/* TODO: This line needs to be removed after test */
		if (!ptable) {
			if ((proghdr.p_filesz != proghdr.p_memsz) &&
			    ((proghdr.p_paddr - 0x58820000)) > 0x4000 &&
			    proghdr.p_memsz > 9)
				memset(dst + proghdr.p_filesz, 0x00,
				       proghdr.p_memsz - proghdr.p_filesz);
		} else {
			if (proghdr.p_filesz != proghdr.p_memsz)
				memset(dst + proghdr.p_filesz, 0x00,
				       proghdr.p_memsz - proghdr.p_filesz);
		}

		flush_cache((unsigned long)dst, proghdr.p_memsz);

		++phdr;
	}

配置dsp的外设,这里并无任何操作。

	if (cfg->config_peripherals)
		cfg->config_peripherals(core_id, cfg);
u32 dsp1_config_peripherals(u32 core_id, struct rproc *cfg)
{
	return 0;
}

一切准备好后开始加载远程核心

	/* Start running the remote core */
	debug("Starting the remote core\n");
	if (cfg->start_core)
		cfg->start_core(core_id, cfg);

将程序的入口放入CTRL_CORE_CONTROL_DSP1_RST_VECTOR寄存器中然后复位dsp这样就开始执行接着再判断是否功能正常

u32 dsp_start_core(u32 core_id, struct rproc *cfg)
{
	u32 prm_base = 0;
	u32 boot_addr = 0;
	u32 ret = 1;

	if (core_id == DSP1) {
		prm_base = DSP1_PRM_BASE;
		boot_addr = DSP1_BOOTADDR;
		ret = 0;
	} else if (core_id == DSP2) {
		prm_base = DSP2_PRM_BASE;
		boot_addr = DSP2_BOOTADDR;
		ret = 0;
	}

	if (ret == 0) {
		u32 boot_reg = 0;

		/* Configure the DSP entry point */
		/* DSP boots from CTRL_CORE_CONTROL_DSP1_RST_VECTOR */
		/* Boot address is shifted by 10 bits before begin written */

		boot_reg = __raw_readl(boot_addr);
		boot_reg = (boot_reg & (~DRA7XX_CTRL_CORE_DSP_RST_VECT_MASK));
		boot_reg =
		    (boot_reg |
		     ((cfg->
		       entry_point >> 10) &
		      DRA7XX_CTRL_CORE_DSP_RST_VECT_MASK));

		__raw_writel(boot_reg, boot_addr);

		/* bring the DSP out of reset */
		__raw_writel(0x0, prm_base + 0x10);

		/* check module is functional or not */
		while (((__raw_readl(prm_base + 0x14) & 0x3) != 0x3));

		ret = 0;
	}

	return ret;
}

将要执行的地址右移 10位后放入寄存器中
在这里插入图片描述
在这里插入图片描述

对dsp进行复位执行
在这里插入图片描述

通过读取下面这个寄存器来判断是否复位完成
在这里插入图片描述

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值