blob: 07a7801c92ba010e903f30c901f8794c22794eba [file] [log] [blame]
// SPDX-License-Identifier: GPL-2.0+
/*
* Copyright 2017-2019 NXP
*/
#include <common.h>
#include <clk.h>
#include <cpu.h>
#include <dm.h>
#include <dm/device-internal.h>
#include <dm/lists.h>
#include <dm/uclass.h>
#include <errno.h>
#include <asm/arch/sci/sci.h>
#include <power-domain.h>
#include <dm/device.h>
#include <dm/uclass-internal.h>
#include <thermal.h>
#include <elf.h>
#include <asm/arch/sid.h>
#include <asm/arch/sys_proto.h>
#include <asm/arch-imx/cpu.h>
#include <asm/armv8/cpu.h>
#include <asm/armv8/mmu.h>
#include <asm/setup.h>
#include <asm/mach-imx/boot_mode.h>
#include <asm/arch/video_common.h>
#include <linux/libfdt.h>
#include <fdt_support.h>
#include <fdtdec.h>
#include <generated/version_autogenerated.h>
#include <asm/mach-imx/imx_vservice.h>
#include <asm/arch/power-domain.h>
#include <spl.h>
DECLARE_GLOBAL_DATA_PTR;
struct edma_ch_map {
sc_rsrc_t ch_start_rsrc;
u32 ch_start_regs;
u32 ch_num;
const char* node_path;
};
#define BT_PASSOVER_TAG 0x504F
struct pass_over_info_t *get_pass_over_info(void)
{
struct pass_over_info_t *p =
(struct pass_over_info_t *)PASS_OVER_INFO_ADDR;
if (p->barker != BT_PASSOVER_TAG ||
p->len != sizeof(struct pass_over_info_t))
return NULL;
return p;
}
#if defined(CONFIG_SPL_BUILD) && defined(CONFIG_RECOVER_SPL_DATA_SECTION)
char __data_save_start[0] __attribute__((section(".__data_save_start")));
char __data_save_end[0] __attribute__((section(".__data_save_end")));
u32 cold_reboot_flag = 1;
static void save_restore_data(void)
{
u32 data_size = __data_save_end - __data_save_start;
if (cold_reboot_flag == 1) {
/* Save data section to data_save section */
memcpy(__data_save_start, __data_save_start - data_size, data_size);
} else {
/* Restore the data_save section to data section */
memcpy(__data_save_start - data_size, __data_save_start, data_size);
}
cold_reboot_flag++;
}
#endif
int arch_cpu_init(void)
{
#if defined(CONFIG_SPL_BUILD) && defined(CONFIG_RECOVER_SPL_DATA_SECTION)
save_restore_data();
#endif
return 0;
}
int arch_cpu_init_dm(void)
{
struct udevice *devp;
int node, ret;
node = fdt_node_offset_by_compatible(gd->fdt_blob, -1, "fsl,imx8-mu");
ret = uclass_get_device_by_of_offset(UCLASS_MISC, node, &devp);
if (ret) {
printf("could not get scu %d\n", ret);
return ret;
}
struct pass_over_info_t *pass_over;
if (is_soc_rev(CHIP_REV_A)) {
pass_over = get_pass_over_info();
if (pass_over && pass_over->g_ap_mu == 0) {
/*
* When ap_mu is 0, means the U-Boot booted
* from first container
*/
sc_misc_boot_status(-1, SC_MISC_BOOT_STATUS_SUCCESS);
}
}
#ifdef CONFIG_IMX_SMMU
int sciErr;
sciErr = sc_pm_set_resource_power_mode(-1, SC_R_SMMU,
SC_PM_PW_MODE_ON);
if (sciErr)
return 0;
#endif
return 0;
}
#ifdef CONFIG_IMX_BOOTAUX
#ifdef CONFIG_IMX8QM
int arch_auxiliary_core_up(u32 core_id, ulong boot_private_data)
{
sc_rsrc_t core_rsrc, mu_rsrc;
sc_faddr_t tcml_addr;
u32 tcm_size = SZ_256K; /* TCML + TCMU */
ulong addr;
switch (core_id) {
case 0:
core_rsrc = SC_R_M4_0_PID0;
tcml_addr = 0x34FE0000;
mu_rsrc = SC_R_M4_0_MU_1A;
break;
case 1:
core_rsrc = SC_R_M4_1_PID0;
tcml_addr = 0x38FE0000;
mu_rsrc = SC_R_M4_1_MU_1A;
break;
default:
printf("Not support this core boot up, ID:%u\n", core_id);
return -EINVAL;
}
addr = (sc_faddr_t)boot_private_data;
if (addr >= tcml_addr && addr <= tcml_addr + tcm_size) {
printf("Wrong image address 0x%lx, should not in TCML\n",
addr);
return -EINVAL;
}
printf("Power on M4 and MU\n");
if (sc_pm_set_resource_power_mode(-1, core_rsrc, SC_PM_PW_MODE_ON) != SC_ERR_NONE)
return -EIO;
if (sc_pm_set_resource_power_mode(-1, mu_rsrc, SC_PM_PW_MODE_ON) != SC_ERR_NONE)
return -EIO;
printf("Copy M4 image from 0x%lx to TCML 0x%lx\n", addr, (ulong)tcml_addr);
if (addr != tcml_addr)
memcpy((void *)tcml_addr, (void *)addr, tcm_size);
printf("Start M4 %u\n", core_id);
if (sc_pm_cpu_start(-1, core_rsrc, true, tcml_addr) != SC_ERR_NONE)
return -EIO;
printf("bootaux complete\n");
return 0;
}
#endif
#ifdef CONFIG_IMX8QXP
static unsigned long load_elf_image_shdr(unsigned long addr)
{
Elf32_Ehdr *ehdr; /* Elf header structure pointer */
Elf32_Shdr *shdr; /* Section header structure pointer */
unsigned char *strtab = 0; /* String table pointer */
unsigned char *image; /* Binary image pointer */
int i; /* Loop counter */
ehdr = (Elf32_Ehdr *)addr;
/* Find the section header string table for output info */
shdr = (Elf32_Shdr *)(addr + ehdr->e_shoff +
(ehdr->e_shstrndx * sizeof(Elf32_Shdr)));
if (shdr->sh_type == SHT_STRTAB)
strtab = (unsigned char *)(addr + shdr->sh_offset);
/* Load each appropriate section */
for (i = 0; i < ehdr->e_shnum; ++i) {
shdr = (Elf32_Shdr *)(addr + ehdr->e_shoff +
(i * sizeof(Elf32_Shdr)));
if (!(shdr->sh_flags & SHF_ALLOC) ||
shdr->sh_addr == 0 || shdr->sh_size == 0) {
continue;
}
if (strtab) {
debug("%sing %s @ 0x%08lx (%ld bytes)\n",
(shdr->sh_type == SHT_NOBITS) ? "Clear" : "Load",
&strtab[shdr->sh_name],
(unsigned long)shdr->sh_addr,
(long)shdr->sh_size);
}
if (shdr->sh_type == SHT_NOBITS) {
memset((void *)(uintptr_t)shdr->sh_addr, 0,
shdr->sh_size);
} else {
image = (unsigned char *)addr + shdr->sh_offset;
memcpy((void *)(uintptr_t)shdr->sh_addr,
(const void *)image, shdr->sh_size);
}
flush_cache(shdr->sh_addr, shdr->sh_size);
}
return ehdr->e_entry;
}
int arch_auxiliary_core_up(u32 core_id, ulong boot_private_data)
{
sc_rsrc_t core_rsrc, mu_rsrc = -1;
sc_faddr_t aux_core_ram;
u32 size;
ulong addr;
switch (core_id) {
case 0:
core_rsrc = SC_R_M4_0_PID0;
aux_core_ram = 0x34FE0000;
mu_rsrc = SC_R_M4_0_MU_1A;
size = SZ_256K;
break;
case 1:
core_rsrc = SC_R_DSP;
aux_core_ram = 0x596f8000;
size = SZ_2K;
break;
default:
printf("Not support this core boot up, ID:%u\n", core_id);
return -EINVAL;
}
addr = (sc_faddr_t)boot_private_data;
if (addr >= aux_core_ram && addr <= aux_core_ram + size) {
printf("Wrong image address 0x%lx, should not in aux core ram\n",
addr);
return -EINVAL;
}
printf("Power on aux core %d\n", core_id);
if (sc_pm_set_resource_power_mode(-1, core_rsrc, SC_PM_PW_MODE_ON) != SC_ERR_NONE)
return -EIO;
if (mu_rsrc != -1) {
if (sc_pm_set_resource_power_mode(-1, mu_rsrc, SC_PM_PW_MODE_ON) != SC_ERR_NONE)
return -EIO;
}
if (core_id == 1) {
struct power_domain pd;
if (sc_pm_clock_enable(-1, core_rsrc, SC_PM_CLK_PER, true, false) != SC_ERR_NONE) {
printf("Error enable clock\n");
return -EIO;
}
if (!power_domain_lookup_name("audio_sai0", &pd)) {
if (power_domain_on(&pd)) {
printf("Error power on SAI0\n");
return -EIO;
}
}
if (!power_domain_lookup_name("audio_ocram", &pd)) {
if (power_domain_on(&pd)) {
printf("Error power on HIFI RAM\n");
return -EIO;
}
}
}
printf("Copy image from 0x%lx to 0x%lx\n", addr, (ulong)aux_core_ram);
if (core_id == 0) {
/* M4 use bin file */
memcpy((void *)aux_core_ram, (void *)addr, size);
} else {
/* HIFI use elf file */
if (!valid_elf_image(addr))
return -1;
addr = load_elf_image_shdr(addr);
}
printf("Start %s\n", core_id == 0 ? "M4" : "HIFI");
if (sc_pm_cpu_start(-1, core_rsrc, true, aux_core_ram) != SC_ERR_NONE)
return -EIO;
printf("bootaux complete\n");
return 0;
}
#endif
int arch_auxiliary_core_check_up(u32 core_id)
{
sc_rsrc_t core_rsrc;
sc_pm_power_mode_t power_mode;
switch (core_id) {
case 0:
core_rsrc = SC_R_M4_0_PID0;
break;
#ifdef CONFIG_IMX8QM
case 1:
core_rsrc = SC_R_M4_1_PID0;
break;
#endif
default:
printf("Not support this core, ID:%u\n", core_id);
return 0;
}
if (sc_pm_get_resource_power_mode(-1, core_rsrc, &power_mode) != SC_ERR_NONE)
return 0;
if (power_mode != SC_PM_PW_MODE_OFF)
return 1;
return 0;
}
#endif
static bool check_owned_resource(sc_rsrc_t rsrc_id)
{
bool owned;
owned = sc_rm_is_resource_owned(-1, rsrc_id);
return owned;
}
#ifdef CONFIG_IMX_SMMU
struct smmu_sid dev_sids[] = {
};
int imx8_config_smmu_sid(struct smmu_sid *dev_sids, int size)
{
int i;
int sciErr = 0;
if ((dev_sids == NULL) || (size <= 0))
return 0;
for (i = 0; i < size; i++) {
if (!check_owned_resource(dev_sids[i].rsrc)) {
printf("%s rsrc[%d] not owned\n", __func__, dev_sids[i].rsrc);
continue;
}
sciErr = sc_rm_set_master_sid(-1,
dev_sids[i].rsrc,
dev_sids[i].sid);
if (sciErr) {
printf("set master sid error\n");
return sciErr;
}
}
return 0;
}
#endif
void arch_preboot_os(void)
{
#if defined(CONFIG_VIDEO_IMXDPUV1)
imxdpuv1_fb_disable();
#endif
#ifdef CONFIG_IMX_SMMU
imx8_config_smmu_sid(dev_sids, ARRAY_SIZE(dev_sids));
#endif
}
#if defined(CONFIG_ARCH_MISC_INIT)
#define FSL_SIP_BUILDINFO 0xC2000003
#define FSL_SIP_BUILDINFO_GET_COMMITHASH 0x00
extern uint32_t _end_ofs;
static void set_buildinfo_to_env(uint32_t scfw, uint32_t secofw, char *mkimage, char *atf)
{
if (!mkimage || !atf)
return;
env_set("commit_mkimage", mkimage);
env_set("commit_atf", atf);
env_set_hex("commit_scfw", (ulong)scfw);
env_set_hex("commit_secofw", (ulong)secofw);
}
static void acquire_buildinfo(void)
{
uint32_t sc_build = 0, sc_commit = 0;
uint32_t seco_build = 0, seco_commit = 0;
char *mkimage_commit, *temp;
uint64_t atf_commit = 0;
/* Get SCFW build and commit id */
sc_misc_build_info(-1, &sc_build, &sc_commit);
if (sc_build == 0) {
debug("SCFW does not support build info\n");
sc_commit = 0; /* Display 0 when the build info is not supported*/
}
/* Get SECO FW build and commit id */
sc_seco_build_info(-1, &seco_build, &seco_commit);
if (seco_build == 0) {
debug("SECO FW does not support build info\n");
seco_commit = 0; /* Display 0 when the build info is not supported*/
}
/* Get imx-mkimage commit id.
* The imx-mkimage puts the commit hash behind the end of u-boot.bin
*/
mkimage_commit = (char *)(ulong)(CONFIG_SYS_TEXT_BASE + _end_ofs + fdt_totalsize(gd->fdt_blob));
temp = mkimage_commit + 8;
*temp = '\0';
if (strlen(mkimage_commit) == 0) {
debug("IMX-MKIMAGE does not support build info\n");
mkimage_commit = "0"; /* Display 0 */
}
/* Get ARM Trusted Firmware commit id */
atf_commit = call_imx_sip(FSL_SIP_BUILDINFO, FSL_SIP_BUILDINFO_GET_COMMITHASH, 0, 0, 0);
if (atf_commit == 0xffffffff) {
debug("ATF does not support build info\n");
atf_commit = 0x30; /* Display 0, 0 ascii is 0x30 */
}
/* Set all to env */
set_buildinfo_to_env(sc_commit, seco_commit, mkimage_commit, (char *)&atf_commit);
printf("\n BuildInfo: \n - SCFW %08x, SECO-FW %08x, IMX-MKIMAGE %s, ATF %s\n - %s \n\n",
sc_commit, seco_commit, mkimage_commit, (char *)&atf_commit, U_BOOT_VERSION);
}
int arch_misc_init(void)
{
acquire_buildinfo();
return 0;
}
#endif
int print_bootinfo(void)
{
enum boot_device bt_dev = get_boot_device();
puts("Boot: ");
switch (bt_dev) {
case SD1_BOOT:
puts("SD0\n");
break;
case SD2_BOOT:
puts("SD1\n");
break;
case SD3_BOOT:
puts("SD2\n");
break;
case MMC1_BOOT:
puts("MMC0\n");
break;
case MMC2_BOOT:
puts("MMC1\n");
break;
case MMC3_BOOT:
puts("MMC2\n");
break;
case FLEXSPI_BOOT:
puts("FLEXSPI\n");
break;
case SATA_BOOT:
puts("SATA\n");
break;
case NAND_BOOT:
puts("NAND\n");
break;
case USB_BOOT:
puts("USB\n");
break;
default:
printf("Unknown device %u\n", bt_dev);
break;
}
return 0;
}
enum boot_device get_boot_device(void)
{
enum boot_device boot_dev = SD1_BOOT;
sc_rsrc_t dev_rsrc;
sc_misc_get_boot_dev(-1, &dev_rsrc);
switch (dev_rsrc) {
case SC_R_SDHC_0:
boot_dev = MMC1_BOOT;
break;
case SC_R_SDHC_1:
boot_dev = SD2_BOOT;
break;
case SC_R_SDHC_2:
boot_dev = SD3_BOOT;
break;
case SC_R_NAND:
boot_dev = NAND_BOOT;
break;
case SC_R_FSPI_0:
boot_dev = FLEXSPI_BOOT;
break;
case SC_R_SATA_0:
boot_dev = SATA_BOOT;
break;
case SC_R_USB_0:
case SC_R_USB_1:
case SC_R_USB_2:
boot_dev = USB_BOOT;
break;
default:
break;
}
return boot_dev;
}
bool is_usb_boot(void)
{
return get_boot_device() == USB_BOOT;
}
#ifdef CONFIG_SERIAL_TAG
#define FUSE_UNIQUE_ID_WORD0 16
#define FUSE_UNIQUE_ID_WORD1 17
void get_board_serial(struct tag_serialnr *serialnr)
{
sc_err_t err;
uint32_t val1 = 0, val2 = 0;
uint32_t word1, word2;
word1 = FUSE_UNIQUE_ID_WORD0;
word2 = FUSE_UNIQUE_ID_WORD1;
err = sc_misc_otp_fuse_read(-1, word1, &val1);
if (err != SC_ERR_NONE) {
printf("%s fuse %d read error: %d\n", __func__,word1, err);
return;
}
err = sc_misc_otp_fuse_read(-1, word2, &val2);
if (err != SC_ERR_NONE) {
printf("%s fuse %d read error: %d\n", __func__, word2, err);
return;
}
serialnr->low = val1;
serialnr->high = val2;
}
#endif /*CONFIG_SERIAL_TAG*/
__weak int board_mmc_get_env_dev(int devno)
{
return devno;
}
int mmc_get_env_dev(void)
{
sc_rsrc_t dev_rsrc;
int devno;
sc_misc_get_boot_dev(-1, &dev_rsrc);
switch (dev_rsrc) {
case SC_R_SDHC_0:
devno = 0;
break;
case SC_R_SDHC_1:
devno = 1;
break;
case SC_R_SDHC_2:
devno = 2;
break;
default:
/* If not boot from sd/mmc, use default value */
return env_get_ulong("mmcdev", 10, CONFIG_SYS_MMC_ENV_DEV);
}
return board_mmc_get_env_dev(devno);
}
static bool check_owned_resources_in_pd_tree(void *blob, int nodeoff,
unsigned int *unowned_rsrc)
{
unsigned int rsrc_id;
int phplen;
const fdt32_t *php;
/* Search the ancestors nodes in current SS power-domain tree,
* if all ancestors' resources are owned, we can enable the node,
* otherwise any ancestor is not owned, we should disable the node.
*/
do {
php = fdt_getprop(blob, nodeoff, "power-domains", &phplen);
if (!php) {
debug(" - ignoring no power-domains\n");
break;
}
if (phplen != 4) {
printf("ignoring %s power-domains of unexpected length %d\n",
fdt_get_name(blob, nodeoff, NULL), phplen);
break;
}
nodeoff = fdt_node_offset_by_phandle(blob, fdt32_to_cpu(*php));
rsrc_id = fdtdec_get_uint(blob, nodeoff, "reg", 0);
if (rsrc_id == SC_R_NONE) {
debug("%s's power domain use SC_R_NONE\n",
fdt_get_name(blob, nodeoff, NULL));
break;
}
debug("power-domains node 0x%x, resource id %u\n", nodeoff, rsrc_id);
if (!check_owned_resource(rsrc_id)) {
if (unowned_rsrc != NULL)
*unowned_rsrc = rsrc_id;
return false;
}
} while (fdt_node_check_compatible(blob, nodeoff, "nxp,imx8-pd"));
return true;
}
static int disable_fdt_node(void *blob, int nodeoffset)
{
int rc, ret;
const char *status = "disabled";
do {
rc = fdt_setprop(blob, nodeoffset, "status", status, strlen(status) + 1);
if (rc) {
if (rc == -FDT_ERR_NOSPACE) {
ret = fdt_increase_size(blob, 512);
if (ret)
return ret;
}
}
} while (rc == -FDT_ERR_NOSPACE);
return rc;
}
static void fdt_edma_debug_int_array(u32 *array, int count, u32 stride)
{
#ifdef DEBUG
int i;
for (i = 0; i < count; i++) {
printf("0x%x ", array[i]);
if (i % stride == stride - 1)
printf("\n");
}
printf("\n");
#endif
}
static void fdt_edma_debug_stringlist(const char *stringlist, int length)
{
#ifdef DEBUG
int i = 0, len;
while (i < length) {
printf("%s\n", stringlist);
len = strlen(stringlist) + 1;
i += len;
stringlist += len;
}
printf("\n");
#endif
}
static void fdt_edma_swap_int_array(u32 *array, int count)
{
int i;
for (i = 0; i < count; i++) {
array[i] = cpu_to_fdt32(array[i]);
}
}
static int fdt_edma_update_int_array(u32 *array, int count, u32 *new_array, u32 stride, int *remove_array, int remove_count)
{
int i = 0, j, curr = 0, new_cnt = 0;
do {
if (remove_count && curr == remove_array[i]) {
i++;
remove_count--;
array += stride;
} else {
for (j = 0; j< stride; j++) {
*new_array = *array;
new_array++;
array++;
}
new_cnt+= j;
}
curr++;
} while ((curr * stride) < count);
return new_cnt;
}
static int fdt_edma_update_stringlist(const char *stringlist, int stringlist_count, char *newlist, int *remove_array, int remove_count)
{
int i = 0, curr = 0, new_len = 0;
int length;
debug("fdt_edma_update_stringlist, remove_cnt %d\n", remove_count);
do {
if (remove_count && curr == remove_array[i]) {
debug("remove %s at %d\n", stringlist, remove_array[i]);
length = strlen(stringlist) + 1;
stringlist += length;
i++;
remove_count--;
} else {
length = strlen(stringlist) + 1;
strcpy(newlist, stringlist);
debug("copy %s, %s, curr %d, len %d\n", newlist, stringlist, curr, length);
stringlist += length;
newlist += length;
new_len += length;
}
curr++;
} while (curr < stringlist_count);
return new_len;
}
static int fdt_edma_get_channel_id(u32 *regs, int index, struct edma_ch_map *edma)
{
u32 ch_reg = regs[(index << 2) + 1];
u32 ch_reg_size = regs[(index << 2) + 3];
int ch_id = (ch_reg - edma->ch_start_regs) / ch_reg_size;
if (ch_id >= edma->ch_num)
return -1;
return ch_id;
}
static void update_fdt_edma_nodes(void *blob)
{
struct edma_ch_map edma_qm[] = {
{ SC_R_DMA_0_CH0, 0x5a200000, 32, "/dma-controller@5a1f0000"},
{ SC_R_DMA_1_CH0, 0x5aa00000, 32, "/dma-controller@5a9f0000"},
{ SC_R_DMA_2_CH0, 0x59200000, 5, "/dma-controller@591F0000"},
{ SC_R_DMA_2_CH5, 0x59250000, 27, "/dma-controller@591F0000"},
{ SC_R_DMA_3_CH0, 0x59a00000, 32, "/dma-controller@599F0000"},
};
struct edma_ch_map edma_qxp[] = {
{ SC_R_DMA_0_CH0, 0x59200000, 32, "/dma-controller@591F0000"},
{ SC_R_DMA_1_CH0, 0x59a00000, 32, "/dma-controller@599F0000"},
{ SC_R_DMA_2_CH0, 0x5a200000, 5, "/dma-controller@5a1f0000"},
{ SC_R_DMA_2_CH5, 0x5a250000, 27, "/dma-controller@5a1f0000"},
{ SC_R_DMA_3_CH0, 0x5aa00000, 32, "/dma-controller@5a9f0000"},
};
u32 i, j, edma_size;
int nodeoff, ret;
struct edma_ch_map *edma_array;
if (is_imx8qm()) {
edma_array = edma_qm;
edma_size = ARRAY_SIZE(edma_qm);
} else {
edma_array = edma_qxp;
edma_size = ARRAY_SIZE(edma_qxp);
}
for (i = 0; i < edma_size; i++, edma_array++) {
u32 regs[128];
u32 interrupts[96];
u32 dma_channels;
int regs_count, interrupts_count, int_names_count;
const char *list;
int list_len, newlist_len;
int remove[32];
int remove_cnt = 0;
char * newlist;
nodeoff = fdt_path_offset(blob, edma_array->node_path);
if (nodeoff < 0)
continue; /* Not found, skip it */
printf("%s, %d\n", edma_array->node_path, nodeoff);
regs_count = fdtdec_get_int_array_count(blob, nodeoff, "reg", regs, 128);
debug("regs_count %d\n", regs_count);
if (regs_count < 0)
continue;
interrupts_count = fdtdec_get_int_array_count(blob, nodeoff, "interrupts", interrupts, 96);
debug("interrupts_count %d\n", interrupts_count);
if (interrupts_count < 0)
continue;
dma_channels = fdtdec_get_uint(blob, nodeoff, "dma-channels", 0);
if (dma_channels == 0)
continue;
list = fdt_getprop(blob, nodeoff, "interrupt-names", &list_len);
if (!list)
continue;
int_names_count = fdt_stringlist_count(blob, nodeoff, "interrupt-names");
fdt_edma_debug_int_array(regs, regs_count, 4);
fdt_edma_debug_int_array(interrupts, interrupts_count, 3);
fdt_edma_debug_stringlist(list, list_len);
for (j = 0; j < (regs_count >> 2); j++) {
int ch_id = fdt_edma_get_channel_id(regs, j, edma_array);
if (ch_id < 0)
continue;
if (!check_owned_resource(edma_array->ch_start_rsrc + ch_id)) {
printf("remove edma items %d\n", j);
dma_channels--;
remove[remove_cnt] = j;
remove_cnt++;
}
}
if (remove_cnt > 0) {
u32 new_regs[128];
u32 new_interrupts[96];
regs_count = fdt_edma_update_int_array(regs, regs_count, new_regs, 4, remove, remove_cnt);
interrupts_count = fdt_edma_update_int_array(interrupts, interrupts_count, new_interrupts, 3, remove, remove_cnt);
fdt_edma_debug_int_array(new_regs, regs_count, 4);
fdt_edma_debug_int_array(new_interrupts, interrupts_count, 3);
fdt_edma_swap_int_array(new_regs, regs_count);
fdt_edma_swap_int_array(new_interrupts, interrupts_count);
/* malloc a new string list */
newlist = (char *)malloc(list_len);
if (!newlist) {
printf("malloc new string list failed, len=%d\n", list_len);
continue;
}
newlist_len = fdt_edma_update_stringlist(list, int_names_count, newlist, remove, remove_cnt);
fdt_edma_debug_stringlist(newlist, newlist_len);
ret = fdt_setprop(blob, nodeoff, "reg", new_regs, regs_count * sizeof(u32));
if (ret)
printf("fdt_setprop regs error %d\n", ret);
ret = fdt_setprop(blob, nodeoff, "interrupts", new_interrupts, interrupts_count * sizeof(u32));
if (ret)
printf("fdt_setprop interrupts error %d\n", ret);
ret = fdt_setprop_u32(blob, nodeoff, "dma-channels", dma_channels);
if (ret)
printf("fdt_setprop_u32 dma-channels error %d\n", ret);
ret = fdt_setprop(blob, nodeoff, "interrupt-names", newlist, newlist_len);
if (ret)
printf("fdt_setprop interrupt-names error %d\n", ret);
free(newlist);
}
}
}
static void update_fdt_with_owned_resources(void *blob)
{
/* Traverses the fdt nodes,
* check its power domain and use the resource id in the power domain
* for checking whether it is owned by current partition
*/
int offset = 0, next_off;
int depth = 0, next_depth;
unsigned int rsrc_id;
int rc;
for (offset = fdt_next_node(blob, offset, &depth); offset > 0;
offset = fdt_next_node(blob, offset, &depth)) {
debug("Node name: %s, depth %d\n", fdt_get_name(blob, offset, NULL), depth);
if (!fdtdec_get_is_enabled(blob, offset)) {
debug(" - ignoring disabled device\n");
continue;
}
if (!fdt_node_check_compatible(blob, offset, "nxp,imx8-pd")) {
/* Skip to next depth=1 node*/
next_off = offset;
next_depth = depth;
do {
offset = next_off;
depth = next_depth;
next_off = fdt_next_node(blob, offset, &next_depth);
if (next_off < 0 || next_depth < 1)
break;
debug("PD name: %s, offset %d, depth %d\n",
fdt_get_name(blob, next_off, NULL), next_off, next_depth);
} while (next_depth > 1);
continue;
}
if (!check_owned_resources_in_pd_tree(blob, offset, &rsrc_id)) {
/* If the resource is not owned, disable it in FDT */
rc = disable_fdt_node(blob, offset);
if (!rc)
printf("Disable %s, resource id %u not owned\n",
fdt_get_name(blob, offset, NULL), rsrc_id);
else
printf("Unable to disable %s, err=%s\n",
fdt_get_name(blob, offset, NULL), fdt_strerror(rc));
}
}
}
#ifdef CONFIG_IMX_SMMU
static int get_srsc_from_fdt_node_power_domain(void *blob, int device_offset)
{
const fdt32_t *prop;
int pdnode_offset;
prop = fdt_getprop(blob, device_offset, "power-domains", NULL);
if (!prop) {
debug("node %s has no power-domains\n",
fdt_get_name(blob, device_offset, NULL));
return -ENOENT;
}
pdnode_offset = fdt_node_offset_by_phandle(blob, fdt32_to_cpu(*prop));
if (pdnode_offset < 0) {
pr_err("failed to fetch node %s power-domain",
fdt_get_name(blob, device_offset, NULL));
return pdnode_offset;
}
return fdtdec_get_uint(blob, pdnode_offset, "reg", -ENOENT);
}
static int config_smmu_resource_sid(int rsrc, int sid)
{
int err;
if (!check_owned_resource(rsrc)) {
printf("%s rsrc[%d] not owned\n", __func__, rsrc);
return -1;
}
err = sc_rm_set_master_sid(-1, rsrc, sid);
debug("set_master_sid rsrc=%d sid=0x%x err=%d\n", rsrc, sid, err);
if (err != SC_ERR_NONE) {
pr_err("fail set_master_sid rsrc=%d sid=0x%x err=%d\n", rsrc, sid, err);
return -EINVAL;
}
return 0;
}
static int config_smmu_fdt_device_sid(void *blob, int device_offset, int sid)
{
int rsrc;
int proplen;
const fdt32_t *prop;
const char *name = fdt_get_name(blob, device_offset, NULL);
prop = fdt_getprop(blob, device_offset, "fsl,sc_rsrc_id", &proplen);
if (prop) {
int i;
debug("configure node %s sid 0x%x for %d resources\n",
name, sid, (int)(proplen / sizeof(fdt32_t)));
for (i = 0; i < proplen / sizeof(fdt32_t); ++i) {
config_smmu_resource_sid(fdt32_to_cpu(prop[i]), sid);
}
return 0;
}
rsrc = get_srsc_from_fdt_node_power_domain(blob, device_offset);
debug("configure node %s sid 0x%x rsrc=%d\n", name, sid, rsrc);
if (rsrc < 0) {
debug("failed to determine SC_R_* for node %s\n", name);
return rsrc;
}
return config_smmu_resource_sid(rsrc, sid);
}
/* assign master sid based on iommu properties in fdt */
static int config_smmu_fdt(void *blob)
{
int offset, proplen, i;
const fdt32_t *prop;
const char *name;
/* Legacy smmu bindings, still used by xen. */
offset = fdt_node_offset_by_compatible(blob, 0, "arm,mmu-500");
if (offset > 0 && (prop = fdt_getprop(blob, offset, "mmu-masters", &proplen)))
{
debug("found legacy mmu-masters property\n");
for (i = 0; i < proplen / 8; ++i) {
uint32_t phandle = fdt32_to_cpu(prop[2 * i]);
int sid = fdt32_to_cpu(prop[2 * i + 1]);
int device_offset;
device_offset = fdt_node_offset_by_phandle(blob, phandle);
if (device_offset < 0) {
pr_err("Failed to fetch device reference from mmu_masters: %d", device_offset);
continue;
}
config_smmu_fdt_device_sid(blob, device_offset, sid);
}
/* Ignore new bindings if old bindings found, just like linux. */
return 0;
}
/* Generic smmu bindings */
offset = 0;
while ((offset = fdt_next_node(blob, offset, NULL)) > 0)
{
name = fdt_get_name(blob, offset, NULL);
prop = fdt_getprop(blob, offset, "iommus", &proplen);
if (!prop)
continue;
debug("node %s iommus proplen %d\n", name, proplen);
if (proplen == 12) {
int sid = fdt32_to_cpu(prop[1]);
config_smmu_fdt_device_sid(blob, offset, sid);
} else if (proplen != 4) {
debug("node %s ignore unexpected iommus proplen=%d\n", name, proplen);
}
}
return 0;
}
#endif
#ifdef CONFIG_OF_SYSTEM_SETUP
static int ft_add_optee_node(void *fdt, bd_t *bd)
{
const char *path, *subpath;
int offs;
/*
* No TEE space allocated indicating no TEE running, so no
* need to add optee node in dts
*/
if (!rom_pointer[1])
return 0;
offs = fdt_increase_size(fdt, 512);
if (offs) {
printf("No Space for dtb\n");
return 1;
}
path = "/firmware";
offs = fdt_path_offset(fdt, path);
if (offs < 0) {
path = "/";
offs = fdt_path_offset(fdt, path);
if (offs < 0) {
printf("Could not find root node.\n");
return 1;
}
subpath = "firmware";
offs = fdt_add_subnode(fdt, offs, subpath);
if (offs < 0) {
printf("Could not create %s node.\n", subpath);
}
}
subpath = "optee";
offs = fdt_add_subnode(fdt, offs, subpath);
if (offs < 0) {
printf("Could not create %s node.\n", subpath);
}
fdt_setprop_string(fdt, offs, "compatible", "linaro,optee-tz");
fdt_setprop_string(fdt, offs, "method", "smc");
return 0;
}
int ft_system_setup(void *blob, bd_t *bd)
{
#if (CONFIG_BOOTAUX_RESERVED_MEM_SIZE != 0x00)
int off;
off = fdt_add_mem_rsv(blob, CONFIG_BOOTAUX_RESERVED_MEM_BASE,
CONFIG_BOOTAUX_RESERVED_MEM_SIZE);
if (off < 0)
printf("Failed to reserve memory for bootaux: %s\n",
fdt_strerror(off));
#endif
update_fdt_with_owned_resources(blob);
update_fdt_edma_nodes(blob);
#ifdef CONFIG_IMX_SMMU
config_smmu_fdt(blob);
#endif
ft_add_optee_node(blob, bd);
return 0;
}
#endif
#define MEMSTART_ALIGNMENT SZ_2M /* Align the memory start with 2MB */
static int get_owned_memreg(sc_rm_mr_t mr, sc_faddr_t *addr_start,
sc_faddr_t *addr_end)
{
sc_faddr_t start, end;
int ret;
bool owned;
owned = sc_rm_is_memreg_owned(-1, mr);
if (owned) {
ret = sc_rm_get_memreg_info(-1, mr, &start, &end);
if (ret) {
printf("Memreg get info failed, %d\n", ret);
return -EINVAL;
}
debug("0x%llx -- 0x%llx\n", start, end);
*addr_start = start;
*addr_end = end;
return 0;
}
return -EINVAL;
}
phys_size_t get_effective_memsize(void)
{
sc_rm_mr_t mr;
sc_faddr_t start, end, end1, start_aligned;
int err;
end1 = (sc_faddr_t)PHYS_SDRAM_1 + PHYS_SDRAM_1_SIZE;
for (mr = 0; mr < 64; mr++) {
err = get_owned_memreg(mr, &start, &end);
if (!err) {
start_aligned = roundup(start, MEMSTART_ALIGNMENT);
/* Too small memory region, not use it */
if (start_aligned > end)
continue;
/* Find the memory region runs the U-Boot */
if (start >= PHYS_SDRAM_1 && start <= end1 &&
(start <= CONFIG_SYS_TEXT_BASE &&
end >= CONFIG_SYS_TEXT_BASE)) {
if ((end + 1) <= ((sc_faddr_t)PHYS_SDRAM_1 +
PHYS_SDRAM_1_SIZE))
return (end - PHYS_SDRAM_1 + 1);
else
return PHYS_SDRAM_1_SIZE;
}
}
}
return PHYS_SDRAM_1_SIZE;
}
int dram_init(void)
{
sc_rm_mr_t mr;
sc_faddr_t start, end, end1, end2;
int err;
end1 = (sc_faddr_t)PHYS_SDRAM_1 + PHYS_SDRAM_1_SIZE;
end2 = (sc_faddr_t)PHYS_SDRAM_2 + PHYS_SDRAM_2_SIZE;
for (mr = 0; mr < 64; mr++) {
err = get_owned_memreg(mr, &start, &end);
if (!err) {
start = roundup(start, MEMSTART_ALIGNMENT);
/* Too small memory region, not use it */
if (start > end)
continue;
if (start >= PHYS_SDRAM_1 && start <= end1) {
if ((end + 1) <= end1)
gd->ram_size += end - start + 1;
else
gd->ram_size += end1 - start;
} else if (start >= PHYS_SDRAM_2 && start <= end2) {
if ((end + 1) <= end2)
gd->ram_size += end - start + 1;
else
gd->ram_size += end2 - start;
}
}
}
/* If error, set to the default value */
if (!gd->ram_size) {
gd->ram_size = PHYS_SDRAM_1_SIZE;
gd->ram_size += PHYS_SDRAM_2_SIZE;
}
return 0;
}
static void dram_bank_sort(int current_bank)
{
phys_addr_t start;
phys_size_t size;
while (current_bank > 0) {
if (gd->bd->bi_dram[current_bank - 1].start >
gd->bd->bi_dram[current_bank].start) {
start = gd->bd->bi_dram[current_bank - 1].start;
size = gd->bd->bi_dram[current_bank - 1].size;
gd->bd->bi_dram[current_bank - 1].start =
gd->bd->bi_dram[current_bank].start;
gd->bd->bi_dram[current_bank - 1].size =
gd->bd->bi_dram[current_bank].size;
gd->bd->bi_dram[current_bank].start = start;
gd->bd->bi_dram[current_bank].size = size;
}
current_bank--;
}
}
int dram_init_banksize(void)
{
sc_rm_mr_t mr;
sc_faddr_t start, end, end1, end2;
int i = 0;
int err;
end1 = (sc_faddr_t)PHYS_SDRAM_1 + PHYS_SDRAM_1_SIZE;
end2 = (sc_faddr_t)PHYS_SDRAM_2 + PHYS_SDRAM_2_SIZE;
for (mr = 0; mr < 64 && i < CONFIG_NR_DRAM_BANKS; mr++) {
err = get_owned_memreg(mr, &start, &end);
if (!err) {
start = roundup(start, MEMSTART_ALIGNMENT);
if (start > end) /* Small memory region, no use it */
continue;
if (start >= PHYS_SDRAM_1 && start <= end1) {
gd->bd->bi_dram[i].start = start;
if ((end + 1) <= end1)
gd->bd->bi_dram[i].size =
end - start + 1;
else
gd->bd->bi_dram[i].size = end1 - start;
dram_bank_sort(i);
i++;
} else if (start >= PHYS_SDRAM_2 && start <= end2) {
gd->bd->bi_dram[i].start = start;
if ((end + 1) <= end2)
gd->bd->bi_dram[i].size =
end - start + 1;
else
gd->bd->bi_dram[i].size = end2 - start;
dram_bank_sort(i);
i++;
}
}
}
/* If error, set to the default value */
if (!i) {
gd->bd->bi_dram[0].start = PHYS_SDRAM_1;
gd->bd->bi_dram[0].size = PHYS_SDRAM_1_SIZE;
gd->bd->bi_dram[1].start = PHYS_SDRAM_2;
gd->bd->bi_dram[1].size = PHYS_SDRAM_2_SIZE;
}
return 0;
}
static u64 get_block_attrs(sc_faddr_t addr_start)
{
u64 attr = PTE_BLOCK_MEMTYPE(MT_DEVICE_NGNRNE) | PTE_BLOCK_NON_SHARE |
PTE_BLOCK_PXN | PTE_BLOCK_UXN;
if ((addr_start >= PHYS_SDRAM_1 &&
addr_start <= ((sc_faddr_t)PHYS_SDRAM_1 + PHYS_SDRAM_1_SIZE)) ||
(addr_start >= PHYS_SDRAM_2 &&
addr_start <= ((sc_faddr_t)PHYS_SDRAM_2 + PHYS_SDRAM_2_SIZE)))
#ifdef CONFIG_IMX_TRUSTY_OS
return (PTE_BLOCK_MEMTYPE(MT_NORMAL) | PTE_BLOCK_INNER_SHARE);
#else
return (PTE_BLOCK_MEMTYPE(MT_NORMAL) | PTE_BLOCK_OUTER_SHARE);
#endif
return attr;
}
static u64 get_block_size(sc_faddr_t addr_start, sc_faddr_t addr_end)
{
sc_faddr_t end1, end2;
end1 = (sc_faddr_t)PHYS_SDRAM_1 + PHYS_SDRAM_1_SIZE;
end2 = (sc_faddr_t)PHYS_SDRAM_2 + PHYS_SDRAM_2_SIZE;
if (addr_start >= PHYS_SDRAM_1 && addr_start <= end1) {
if ((addr_end + 1) > end1)
return end1 - addr_start;
} else if (addr_start >= PHYS_SDRAM_2 && addr_start <= end2) {
if ((addr_end + 1) > end2)
return end2 - addr_start;
}
return (addr_end - addr_start + 1);
}
#define MAX_PTE_ENTRIES 512
#define MAX_MEM_MAP_REGIONS 16
static struct mm_region imx8_mem_map[MAX_MEM_MAP_REGIONS];
struct mm_region *mem_map = imx8_mem_map;
void enable_caches(void)
{
sc_rm_mr_t mr;
sc_faddr_t start, end;
int err, i;
/* Create map for registers access from 0x1c000000 to 0x80000000*/
imx8_mem_map[0].virt = 0x1c000000UL;
imx8_mem_map[0].phys = 0x1c000000UL;
imx8_mem_map[0].size = 0x64000000UL;
imx8_mem_map[0].attrs = PTE_BLOCK_MEMTYPE(MT_DEVICE_NGNRNE) |
PTE_BLOCK_NON_SHARE | PTE_BLOCK_PXN | PTE_BLOCK_UXN;
i = 1;
#ifdef CONFIG_IMX_VSERVICE_SHARED_BUFFER
imx8_mem_map[i].virt = CONFIG_IMX_VSERVICE_SHARED_BUFFER;
imx8_mem_map[i].phys = CONFIG_IMX_VSERVICE_SHARED_BUFFER;
imx8_mem_map[i].size = CONFIG_IMX_VSERVICE_SHARED_BUFFER_SIZE;
imx8_mem_map[i].attrs = PTE_BLOCK_MEMTYPE(MT_DEVICE_NGNRNE) |
PTE_BLOCK_NON_SHARE | PTE_BLOCK_PXN | PTE_BLOCK_UXN;
i++;
#endif
for (mr = 0; mr < 64 && i < MAX_MEM_MAP_REGIONS; mr++) {
err = get_owned_memreg(mr, &start, &end);
if (!err) {
imx8_mem_map[i].virt = start;
imx8_mem_map[i].phys = start;
imx8_mem_map[i].size = get_block_size(start, end);
imx8_mem_map[i].attrs = get_block_attrs(start);
i++;
}
}
if (i < MAX_MEM_MAP_REGIONS) {
imx8_mem_map[i].size = 0;
imx8_mem_map[i].attrs = 0;
} else {
puts("Error, need more MEM MAP REGIONS reserved\n");
icache_enable();
return;
}
for (i = 0; i < MAX_MEM_MAP_REGIONS; i++) {
debug("[%d] vir = 0x%llx phys = 0x%llx size = 0x%llx attrs = 0x%llx\n",
i, imx8_mem_map[i].virt, imx8_mem_map[i].phys,
imx8_mem_map[i].size, imx8_mem_map[i].attrs);
}
icache_enable();
dcache_enable();
}
#ifndef CONFIG_SYS_DCACHE_OFF
u64 get_page_table_size(void)
{
u64 one_pt = MAX_PTE_ENTRIES * sizeof(u64);
u64 size = 0;
/*
* For each memory region, the max table size:
* 2 level 3 tables + 2 level 2 tables + 1 level 1 table
*/
size = (2 + 2 + 1) * one_pt * MAX_MEM_MAP_REGIONS + one_pt;
/*
* We need to duplicate our page table once to have an emergency pt to
* resort to when splitting page tables later on
*/
size *= 2;
/*
* We may need to split page tables later on if dcache settings change,
* so reserve up to 4 (random pick) page tables for that.
*/
size += one_pt * 4;
return size;
}
#endif
#if defined(CONFIG_IMX8QM)
#define FUSE_MAC0_WORD0 452
#define FUSE_MAC0_WORD1 453
#define FUSE_MAC1_WORD0 454
#define FUSE_MAC1_WORD1 455
#elif defined(CONFIG_IMX8QXP)
#define FUSE_MAC0_WORD0 708
#define FUSE_MAC0_WORD1 709
#define FUSE_MAC1_WORD0 710
#define FUSE_MAC1_WORD1 711
#endif
void imx_get_mac_from_fuse(int dev_id, unsigned char *mac)
{
u32 word[2], val[2] = {};
int i, ret;
if (dev_id == 0) {
word[0] = FUSE_MAC0_WORD0;
word[1] = FUSE_MAC0_WORD1;
} else {
word[0] = FUSE_MAC1_WORD0;
word[1] = FUSE_MAC1_WORD1;
}
for (i = 0; i < 2; i++) {
ret = sc_misc_otp_fuse_read(-1, word[i], &val[i]);
if (ret < 0)
goto err;
}
mac[0] = val[0];
mac[1] = val[0] >> 8;
mac[2] = val[0] >> 16;
mac[3] = val[0] >> 24;
mac[4] = val[1];
mac[5] = val[1] >> 8;
debug("%s: MAC%d: %02x.%02x.%02x.%02x.%02x.%02x\n",
__func__, dev_id, mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]);
return;
err:
printf("%s: fuse %d, err: %d\n", __func__, word[i], ret);
}
u32 get_cpu_rev(void)
{
u32 id = 0, rev = 0;
int ret;
ret = sc_misc_get_control(-1, SC_R_SYSTEM, SC_C_ID, &id);
if (ret)
return 0;
rev = (id >> 5) & 0xf;
id = (id & 0x1f) + MXC_SOC_IMX8; /* Dummy ID for chip */
return (id << 12) | rev;
}
void disconnect_from_pc(void)
{
int ret;
struct power_domain pd;
if (!power_domain_lookup_name("conn_usb0", &pd)) {
ret = power_domain_on(&pd);
if (ret) {
printf("conn_usb0 Power up failed! (error = %d)\n", ret);
return;
}
writel(0x0, USB_BASE_ADDR + 0x140);
ret = power_domain_off(&pd);
if (ret) {
printf("conn_usb0 Power off failed! (error = %d)\n", ret);
return;
}
} else {
printf("conn_usb0 finding failed!\n");
return;
}
}
#if CONFIG_IS_ENABLED(CPU)
struct cpu_imx_platdata {
const char *name;
const char *rev;
const char *type;
u32 cpurev;
u32 freq_mhz;
u32 mpidr;
};
const char *get_imx8_type(u32 imxtype)
{
switch (imxtype) {
case MXC_CPU_IMX8QM:
return "QM"; /* i.MX8 Quad MAX */
case MXC_CPU_IMX8QXP:
case MXC_CPU_IMX8QXP_A0:
return "QXP";
default:
return "??";
}
}
const char *get_imx8_rev(u32 rev)
{
switch (rev) {
case CHIP_REV_A:
return "A";
case CHIP_REV_B:
return "B";
default:
return "?";
}
}
const char *get_core_name(struct udevice *dev)
{
const void *blob = gd->fdt_blob;
int node = dev_of_offset(dev);
if (!fdt_node_check_compatible(blob, node, "arm,cortex-a35"))
return "A35";
else if (!fdt_node_check_compatible(blob, node, "arm,cortex-a53"))
return "A53";
else if (!fdt_node_check_compatible(blob, node, "arm,cortex-a72"))
return "A72";
else
return "?";
}
int cpu_imx_get_desc(struct udevice *dev, char *buf, int size)
{
struct cpu_imx_platdata *plat = dev_get_platdata(dev);
int len;
if (size < 100)
return -ENOSPC;
len = snprintf(buf, size, "NXP i.MX8%s Rev%s %s at %u MHz",
plat->type, plat->rev, plat->name, plat->freq_mhz);
#if defined(CONFIG_IMX_SC_THERMAL)
struct udevice *thermal_dev;
int cpu_tmp, ret;
if (!strcmp(plat->name, "A72"))
ret = uclass_get_device_by_name(UCLASS_THERMAL, "cpu-thermal1", &thermal_dev);
else
ret = uclass_get_device_by_name(UCLASS_THERMAL, "cpu-thermal0", &thermal_dev);
if (!ret) {
ret = thermal_get_temp(thermal_dev, &cpu_tmp);
if (!ret)
len += snprintf(buf + len, size," at %dC", cpu_tmp);
else
len += snprintf(buf + len, size," - invalid sensor data");
} else {
len += snprintf(buf + len, size, " - invalid sensor device");
}
#endif
len += snprintf(buf + len, size, "\n");
return 0;
}
static int cpu_imx_get_info(struct udevice *dev, struct cpu_info *info)
{
struct cpu_imx_platdata *plat = dev_get_platdata(dev);
info->cpu_freq = plat->freq_mhz * 1000;
info->features = BIT(CPU_FEAT_L1_CACHE) | BIT(CPU_FEAT_MMU);
return 0;
}
static int cpu_imx_get_count(struct udevice *dev)
{
if (is_imx8qxp())
return 4;
else
return 6;
}
static int cpu_imx_get_vendor(struct udevice *dev, char *buf, int size)
{
snprintf(buf, size, "NXP");
return 0;
}
static bool cpu_imx_is_current(struct udevice *dev)
{
struct cpu_imx_platdata *plat = dev_get_platdata(dev);
if (plat->mpidr == (read_mpidr() & 0xffff))
return true;
return false;
}
static const struct cpu_ops cpu_imx8_ops = {
.get_desc = cpu_imx_get_desc,
.get_info = cpu_imx_get_info,
.get_count = cpu_imx_get_count,
.get_vendor = cpu_imx_get_vendor,
.is_current_cpu = cpu_imx_is_current,
};
static const struct udevice_id cpu_imx8_ids[] = {
{ .compatible = "arm,cortex-a35" },
{ .compatible = "arm,cortex-a53" },
{ .compatible = "arm,cortex-a72" },
{ }
};
static int imx8_cpu_probe(struct udevice *dev)
{
struct cpu_imx_platdata *plat = dev_get_platdata(dev);
struct clk cpu_clk;
u32 cpurev;
int ret;
cpurev = get_cpu_rev();
plat->cpurev = cpurev;
plat->name = get_core_name(dev);
plat->rev = get_imx8_rev(cpurev & 0xFFF);
plat->type = get_imx8_type((cpurev & 0xFF000) >> 12);
plat->mpidr = dev_read_addr(dev);
if (plat->mpidr == FDT_ADDR_T_NONE) {
printf("%s: Failed to get CPU reg property\n", __func__);
return -EINVAL;
}
ret = clk_get_by_index(dev, 0, &cpu_clk);
if (ret) {
debug("%s: Failed to get CPU clk: %d\n", __func__, ret);
return 0;
}
plat->freq_mhz = clk_get_rate(&cpu_clk) / 1000000;
return 0;
}
U_BOOT_DRIVER(cpu_imx8_drv) = {
.name = "imx8x_cpu",
.id = UCLASS_CPU,
.of_match = cpu_imx8_ids,
.ops = &cpu_imx8_ops,
.probe = imx8_cpu_probe,
.platdata_auto_alloc_size = sizeof(struct cpu_imx_platdata),
.flags = DM_FLAG_PRE_RELOC,
};
#endif
static bool check_device_power_off(struct udevice *dev,
const char* permanent_on_devices[], int size)
{
int i;
for (i = 0; i < size; i++) {
if (!strcmp(dev->name, permanent_on_devices[i]))
return false;
}
return true;
}
void power_off_pd_devices(const char* permanent_on_devices[], int size)
{
struct udevice *dev;
struct power_domain pd;
for (uclass_find_first_device(UCLASS_POWER_DOMAIN, &dev); dev;
uclass_find_next_device(&dev)) {
if (device_active(dev)) {
/* Power off active pd devices except the permanent power on devices */
if (check_device_power_off(dev, permanent_on_devices, size)) {
pd.dev = dev;
power_domain_off(&pd);
}
}
}
}
bool check_owned_udevice(struct udevice *dev)
{
int ret;
sc_rsrc_t resource_id;
struct ofnode_phandle_args args;
/* Get the resource id from its power-domain */
ret = dev_read_phandle_with_args(dev, "power-domains",
"#power-domain-cells", 0, 0, &args);
if (ret) {
printf("no power-domains found\n");
return false;
}
/* Get the owner partition for resource*/
resource_id = (sc_rsrc_t)ofnode_read_u32_default(args.node, "reg", SC_R_NONE);
if (resource_id == SC_R_NONE) {
printf("Can't find the resource id for udev %s\n", dev->name);
return false;
}
debug("udev %s, resource id %d\n", dev->name, resource_id);
return check_owned_resource(resource_id);
}
bool check_m4_parts_boot(void)
{
sc_rm_pt_t m4_parts[2];
int err;
err = sc_rm_get_resource_owner(-1, SC_R_M4_0_PID0, &m4_parts[0]);
if (err != SC_ERR_NONE) {
printf("%s get resource [%d] owner error: %d\n", __func__, SC_R_M4_0_PID0, err);
return false;
}
if (sc_pm_is_partition_started(-1, m4_parts[0]))
return true;
if (is_imx8qm()) {
err = sc_rm_get_resource_owner(-1, SC_R_M4_1_PID0, &m4_parts[1]);
if (err != SC_ERR_NONE) {
printf("%s get resource [%d] owner error: %d\n", __func__, SC_R_M4_1_PID0, err);
return false;
}
if (sc_pm_is_partition_started(-1, m4_parts[1]))
return true;
}
return false;
}
#ifdef CONFIG_IMX_VSERVICE
struct udevice * board_imx_vservice_find_mu(struct udevice *dev)
{
int ret;
const char *m4_mu_name[2] = {
"mu@5d230000",
"mu@5d240000"
};
struct udevice *m4_mu[2];
sc_rm_pt_t m4_parts[2];
int err;
struct ofnode_phandle_args args;
sc_rsrc_t resource_id;
sc_rm_pt_t resource_part;
/* Get the resource id from its power-domain */
ret = dev_read_phandle_with_args(dev, "power-domains",
"#power-domain-cells", 0, 0, &args);
if (ret) {
printf("Can't find the power-domains property for udev %s\n", dev->name);
return NULL;
}
/* Get the owner partition for resource*/
resource_id = (sc_rsrc_t)ofnode_read_u32_default(args.node, "reg", SC_R_NONE);
if (resource_id == SC_R_NONE) {
printf("Can't find the resource id for udev %s\n", dev->name);
return NULL;
}
err = sc_rm_get_resource_owner(-1, resource_id, &resource_part);
if (err != SC_ERR_NONE) {
printf("%s get resource [%d] owner error: %d\n", __func__, resource_id, err);
return NULL;
}
debug("udev %s, resource id %d, resource part %d\n", dev->name, resource_id, resource_part);
/* MU8 for communication between M4_0 and u-boot, MU9 for M4_1 and u-boot */
err = sc_rm_get_resource_owner(-1, SC_R_M4_0_PID0, &m4_parts[0]);
if (err != SC_ERR_NONE) {
printf("%s get resource [%d] owner error: %d\n", __func__, SC_R_M4_0_PID0, err);
return NULL;
}
ret = uclass_find_device_by_name(UCLASS_MISC, m4_mu_name[0], &m4_mu[0]);
if (!ret) {
/* If the i2c is in m4_0 partition, return the mu8 */
if (resource_part == m4_parts[0])
return m4_mu[0];
}
if (is_imx8qm()) {
err = sc_rm_get_resource_owner(-1, SC_R_M4_1_PID0, &m4_parts[1]);
if (err != SC_ERR_NONE) {
printf("%s get resource [%d] owner error: %d\n", __func__, SC_R_M4_1_PID0, err);
return NULL;
}
ret = uclass_find_device_by_name(UCLASS_MISC, m4_mu_name[1], &m4_mu[1]);
if (!ret) {
/* If the i2c is in m4_1 partition, return the mu9 */
if (resource_part == m4_parts[1])
return m4_mu[1];
}
}
return NULL;
}
void * board_imx_vservice_get_buffer(struct imx_vservice_channel *node, u32 size)
{
const char *m4_mu_name[2] = {
"mu@5d230000",
"mu@5d240000"
};
/* Each MU ownes 1M buffer */
if (size <= 0x100000) {
if (!strcmp(node->mu_dev->name, m4_mu_name[0]))
return (void * )CONFIG_IMX_VSERVICE_SHARED_BUFFER;
else if (!strcmp(node->mu_dev->name, m4_mu_name[1]))
return (void * )(CONFIG_IMX_VSERVICE_SHARED_BUFFER + 0x100000);
else
return NULL;
}
return NULL;
}
#endif
/* imx8qxp i2c1 has lots of devices may used by both M4 and A core
* If A core partition does not own the resource, we will start
* virtual i2c driver. Otherwise use local i2c driver.
*/
int board_imx_virt_i2c_bind(struct udevice *dev)
{
if (check_owned_udevice(dev))
return -ENODEV;
return 0;
}
int board_imx_lpi2c_bind(struct udevice *dev)
{
if (check_owned_udevice(dev))
return 0;
return -ENODEV;
}
void board_boot_order(u32 *spl_boot_list)
{
spl_boot_list[0] = spl_boot_device();
if (spl_boot_list[0] == BOOT_DEVICE_SPI) {
/* Check whether we own the flexspi0, if not, use NOR boot */
if (!check_owned_resource(SC_R_FSPI_0))
spl_boot_list[0] = BOOT_DEVICE_NOR;
}
}