Commit 8fe72b4c authored by Ye Li's avatar Ye Li Committed by Jason Liu
Browse files

MLK-14938-5 imx8: Add SoC level support



Add cpu, power, and clocks functions for support i.MX8QM and i.MX8QXP SoCs.
Signed-off-by: default avatarRanjani Vaidyanathan <Ranjani.Vaidyanathan@nxp.com>
Signed-off-by: default avatarNitin Garg <nitin.garg@nxp.com>
Signed-off-by: default avatarAnson Huang <Anson.Huang@nxp.com>
Signed-off-by: default avatarAdrian Alonso <adrian.alonso@nxp.com>
Signed-off-by: default avatarPeng Fan <peng.fan@nxp.com>
Signed-off-by: default avatarFugang Duan <fugang.duan@nxp.com>
Signed-off-by: default avatarYe Li <ye.li@nxp.com>
parent 093aa0ed
......@@ -4,4 +4,6 @@
# SPDX-License-Identifier: GPL-2.0+
#
obj-y += cpu.o
obj-y += clock.o
obj-y += fsl_mu_hal.o
/*
* Copyright 2017 NXP
*
* SPDX-License-Identifier: GPL-2.0+
*/
#include <common.h>
#include <linux/errno.h>
#include <asm/arch/clock.h>
#include <asm/imx-common/sci/sci.h>
#include <asm/arch/imx8-pins.h>
#include <asm/arch/i2c.h>
DECLARE_GLOBAL_DATA_PTR;
u32 get_lpuart_clk(void)
{
return mxc_get_clock(MXC_UART_CLK);
}
static u32 get_arm_main_clk(void)
{
sc_err_t err;
sc_pm_clock_rate_t clkrate;
err = sc_pm_get_clock_rate((sc_ipc_t)gd->arch.ipc_channel_handle,
SC_R_A53, SC_PM_CLK_CPU, &clkrate);
if (err != SC_ERR_NONE) {
printf("sc get ARM A53 clk failed! err=%d\n", err);
return 0;
}
return clkrate;
}
unsigned int mxc_get_clock(enum mxc_clock clk)
{
sc_err_t err;
sc_pm_clock_rate_t clkrate;
switch (clk) {
case MXC_UART_CLK:
err = sc_pm_get_clock_rate((sc_ipc_t)gd->arch.ipc_channel_handle,
SC_R_UART_0, 2, &clkrate);
if (err != SC_ERR_NONE) {
printf("sc get UART clk failed! err=%d\n", err);
return 0;
}
return clkrate;
case MXC_ESDHC_CLK:
err = sc_pm_get_clock_rate((sc_ipc_t)gd->arch.ipc_channel_handle,
SC_R_SDHC_0, 2, &clkrate);
if (err != SC_ERR_NONE) {
printf("sc get uSDHC1 clk failed! err=%d\n", err);
return 0;
}
return clkrate;
case MXC_ESDHC2_CLK:
err = sc_pm_get_clock_rate((sc_ipc_t)gd->arch.ipc_channel_handle,
SC_R_SDHC_1, 2, &clkrate);
if (err != SC_ERR_NONE) {
printf("sc get uSDHC2 clk failed! err=%d\n", err);
return 0;
}
return clkrate;
case MXC_ESDHC3_CLK:
err = sc_pm_get_clock_rate((sc_ipc_t)gd->arch.ipc_channel_handle,
SC_R_SDHC_2, 2, &clkrate);
if (err != SC_ERR_NONE) {
printf("sc get uSDHC3 clk failed! err=%d\n", err);
return 0;
}
return clkrate;
case MXC_FEC_CLK:
err = sc_pm_get_clock_rate((sc_ipc_t)gd->arch.ipc_channel_handle,
SC_R_ENET_0, 2, &clkrate);
if (err != SC_ERR_NONE) {
printf("sc get ENET clk failed! err=%d\n", err);
return 0;
}
return clkrate;
case MXC_ARM_CLK:
return get_arm_main_clk();
default:
printf("Unsupported mxc_clock %d\n", clk);
break;
}
return 0;
}
u32 imx_get_fecclk(void)
{
return mxc_get_clock(MXC_FEC_CLK);
}
int enable_i2c_clk(unsigned char enable, unsigned i2c_num)
{
sc_ipc_t ipc;
sc_err_t err;
if (i2c_num >= ARRAY_SIZE(imx_i2c_desc))
return -EINVAL;
ipc = gd->arch.ipc_channel_handle;
if (enable)
err = sc_pm_clock_enable(ipc,
imx_i2c_desc[i2c_num].rsrc, 2, true, false);
else
err = sc_pm_clock_enable(ipc,
imx_i2c_desc[i2c_num].rsrc, 2, false, false);
if (err != SC_ERR_NONE) {
printf("i2c clock error %d\n", err);
return -EPERM;
}
return 0;
}
u32 imx_get_i2cclk(unsigned i2c_num)
{
sc_err_t err;
sc_ipc_t ipc;
u32 clock_rate;
if (i2c_num >= ARRAY_SIZE(imx_i2c_desc))
return 0;
ipc = gd->arch.ipc_channel_handle;
err = sc_pm_get_clock_rate(ipc, imx_i2c_desc[i2c_num].rsrc, 2,
&clock_rate);
if (err != SC_ERR_NONE)
return 0;
return clock_rate;
}
int set_clk_qspi(void)
{
u32 err;
u32 rate = 40000000;
sc_ipc_t ipc;
ipc = gd->arch.ipc_channel_handle;
err = sc_pm_set_clock_rate(ipc, SC_R_QSPI_0, SC_PM_CLK_PER, &rate);
if (err != SC_ERR_NONE) {
printf("\nqspi set clock rate (error = %d)\n", err);
return -EPERM;
}
err = sc_pm_clock_enable(ipc, SC_R_QSPI_0 , SC_PM_CLK_PER, true, false);
if (err != SC_ERR_NONE) {
printf("\nqspi enable clock enable (error = %d)\n", err);
return -EPERM;
}
return 0;
}
void enable_usboh3_clk(unsigned char enable)
{
sc_err_t err;
sc_ipc_t ipc;
sc_rsrc_t usbs[] = {SC_R_USB_0, SC_R_USB_1, SC_R_USB_2};
int i;
ipc = gd->arch.ipc_channel_handle;
if (enable) {
for (i = 0; i < 2; i++) {
/* The 24Mhz OTG PHY clock is pd linked, so it has been power up when pd is on */
err = sc_pm_clock_enable(ipc, usbs[i], SC_PM_CLK_PHY, true, false);
if (err != SC_ERR_NONE)
printf("\nSC_R_USB_%d enable clock enable failed! (error = %d)\n", i, err);
}
} else {
for (i = 0; i < 2; i++) {
err = sc_pm_clock_enable(ipc, usbs[i], SC_PM_CLK_PHY, false, false);
if (err != SC_ERR_NONE)
printf("\nSC_R_USB_%d enable clock disable failed! (error = %d)\n", i, err);
}
}
return;
}
void init_clk_usdhc(u32 index)
{
#ifdef CONFIG_IMX8QM
sc_rsrc_t usdhcs[] = {SC_R_SDHC_0, SC_R_SDHC_1, SC_R_SDHC_2};
u32 instances = 3;
#else
sc_rsrc_t usdhcs[] = {SC_R_SDHC_0, SC_R_SDHC_1};
u32 instances = 2;
#endif
sc_err_t err;
sc_ipc_t ipc;
sc_pm_clock_rate_t actual = 200000000;
ipc = gd->arch.ipc_channel_handle;
if (index >= instances)
return;
/* Power on the usdhc */
err = sc_pm_set_resource_power_mode(ipc, usdhcs[index],
SC_PM_PW_MODE_ON);
if (err != SC_ERR_NONE) {
printf("SDHC_%d Power on failed! (error = %d)\n", index, err);
return;
}
err = sc_pm_set_clock_rate(ipc, usdhcs[index], 2, &actual);
if (err != SC_ERR_NONE) {
printf("SDHC_%d set clock failed! (error = %d)\n", index, err);
return;
}
if (actual != 200000000)
debug("Actual rate for SDHC_%d is %d\n", index, actual);
err = sc_pm_clock_enable(ipc, usdhcs[index], SC_PM_CLK_PER, true, false);
if (err != SC_ERR_NONE) {
printf("SDHC_%d per clk enable failed!\n", index);
return;
}
}
/*
* Copyright 2017 NXP
*
* SPDX-License-Identifier: GPL-2.0+
*
*/
#include <common.h>
#include <errno.h>
#include <asm/io.h>
#include <asm/imx-common/sci/sci.h>
#include <asm/arch/i2c.h>
#include <asm/arch/clock.h>
#include <asm/armv8/mmu.h>
#include <elf.h>
#include <asm/arch-imx/cpu.h>
DECLARE_GLOBAL_DATA_PTR;
static struct mm_region imx8_mem_map[] = {
{
.virt = 0x0UL,
.phys = 0x0UL,
.size = 0x2000000UL,
.attrs = PTE_BLOCK_MEMTYPE(MT_NORMAL) |
PTE_BLOCK_OUTER_SHARE
}, {
.virt = 0x2000000UL,
.phys = 0x2000000UL,
.size = 0x7E000000UL,
.attrs = PTE_BLOCK_MEMTYPE(MT_DEVICE_NGNRNE) |
PTE_BLOCK_NON_SHARE |
PTE_BLOCK_PXN | PTE_BLOCK_UXN
}, {
.virt = 0x80000000UL,
.phys = 0x80000000UL,
.size = 0x80000000UL,
.attrs = PTE_BLOCK_MEMTYPE(MT_NORMAL) |
PTE_BLOCK_OUTER_SHARE
}, {
.virt = 0x100000000UL,
.phys = 0x100000000UL,
.size = 0x700000000UL,
.attrs = PTE_BLOCK_MEMTYPE(MT_DEVICE_NGNRNE) |
PTE_BLOCK_NON_SHARE |
PTE_BLOCK_PXN | PTE_BLOCK_UXN
}, {
.virt = 0x880000000UL,
.phys = 0x880000000UL,
.size = 0x780000000UL,
.attrs = PTE_BLOCK_MEMTYPE(MT_NORMAL) |
PTE_BLOCK_OUTER_SHARE
}, {
/* List terminator */
0,
}
};
struct mm_region *mem_map = imx8_mem_map;
u32 get_cpu_rev(void)
{
sc_ipc_t ipcHndl;
uint32_t id = 0, rev = 0;
sc_err_t err;
ipcHndl = gd->arch.ipc_channel_handle;
err = sc_misc_get_control(ipcHndl, SC_R_SC_PID0, SC_C_ID, &id);
if (err != SC_ERR_NONE)
return 0;
rev = (id >> 5) & 0xf;
id = (id & 0x1f) + MXC_SOC_IMX8; /* Dummy ID for chip */
return (id << 12) | rev;
}
#ifdef CONFIG_DISPLAY_CPUINFO
const char *get_imx8_type(u32 imxtype)
{
switch (imxtype) {
case MXC_CPU_IMX8QM:
return "8QM"; /* i.MX8 Quad MAX */
case MXC_CPU_IMX8QXP:
return "8QXP"; /* i.MX8 Quad XP */
case MXC_CPU_IMX8DX:
return "8DX"; /* i.MX8 Dual X */
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 "?";
}
}
int print_cpuinfo(void)
{
u32 cpurev;
cpurev = get_cpu_rev();
printf("CPU: Freescale i.MX%s rev%s at %d MHz\n",
get_imx8_type((cpurev & 0xFF000) >> 12),
get_imx8_rev((cpurev & 0xFFF)),
mxc_get_clock(MXC_ARM_CLK) / 1000000);
return 0;
}
#endif
int arch_cpu_init(void)
{
sc_ipc_t ipcHndl = 0;
sc_err_t sciErr = 0;
gd->arch.ipc_channel_handle = 0;
/* Open IPC channel */
sciErr = sc_ipc_open(&ipcHndl, SC_IPC_CH);
if (sciErr != SC_ERR_NONE)
return -EPERM;
gd->arch.ipc_channel_handle = ipcHndl;
return 0;
}
u32 cpu_mask(void)
{
#ifdef CONFIG_IMX8QM
return 0x3f;
#else
return 0xf; /*For IMX8QXP*/
#endif
}
#define CCI400_DVM_MESSAGE_REQ_EN 0x00000002
#define CCI400_SNOOP_REQ_EN 0x00000001
#define CHANGE_PENDING_BIT (1 << 0)
int imx8qm_wake_seconday_cores(void)
{
#ifdef CONFIG_ARMV8_MULTIENTRY
sc_ipc_t ipcHndl;
u64 *table = get_spin_tbl_addr();
/* Clear spin table so that secondary processors
* observe the correct value after waking up from wfe.
*/
memset(table, 0, CONFIG_MAX_CPUS*SPIN_TABLE_ELEM_SIZE);
flush_dcache_range((unsigned long)table,
(unsigned long)table +
(CONFIG_MAX_CPUS*SPIN_TABLE_ELEM_SIZE));
/* Open IPC channel */
if (sc_ipc_open(&ipcHndl, SC_IPC_CH) != SC_ERR_NONE)
return -EIO;
__raw_writel(0xc, 0x52090000);
__raw_writel(1, 0x52090008);
/* IPC to pwr up and boot other cores */
if (sc_pm_set_resource_power_mode(ipcHndl, SC_R_A53_1, SC_PM_PW_MODE_ON) != SC_ERR_NONE)
return -EIO;
if (sc_pm_cpu_start(ipcHndl, SC_R_A53_1, true, 0x80000000) != SC_ERR_NONE)
return -EIO;
if (sc_pm_set_resource_power_mode(ipcHndl, SC_R_A53_2, SC_PM_PW_MODE_ON) != SC_ERR_NONE)
return -EIO;
if (sc_pm_cpu_start(ipcHndl, SC_R_A53_2, true, 0x80000000) != SC_ERR_NONE)
return -EIO;
if (sc_pm_set_resource_power_mode(ipcHndl, SC_R_A53_3, SC_PM_PW_MODE_ON) != SC_ERR_NONE)
return -EIO;
if (sc_pm_cpu_start(ipcHndl, SC_R_A53_3, true, 0x80000000) != SC_ERR_NONE)
return -EIO;
/* Enable snoop and dvm msg requests for a53 port on CCI slave interface 3 */
__raw_writel(CCI400_DVM_MESSAGE_REQ_EN | CCI400_SNOOP_REQ_EN, 0x52094000);
while (__raw_readl(0x5209000c) & CHANGE_PENDING_BIT)
;
/* Pwr up cluster 1 and boot core 0*/
if (sc_pm_set_resource_power_mode(ipcHndl, SC_R_A72, SC_PM_PW_MODE_ON) != SC_ERR_NONE)
return -EIO;
if (sc_pm_set_resource_power_mode(ipcHndl, SC_R_A72_0, SC_PM_PW_MODE_ON) != SC_ERR_NONE)
return -EIO;
if (sc_pm_cpu_start(ipcHndl, SC_R_A72_0, true, 0x80000000) != SC_ERR_NONE)
return -EIO;
/* IPC to pwr up and boot core 1 */
if (sc_pm_set_resource_power_mode(ipcHndl, SC_R_A72_1, SC_PM_PW_MODE_ON) != SC_ERR_NONE)
return -EIO;
if (sc_pm_cpu_start(ipcHndl, SC_R_A72_1, true, 0x80000000) != SC_ERR_NONE)
return -EIO;
/* Enable snoop and dvm msg requests for a72 port on CCI slave interface 4 */
__raw_writel(CCI400_DVM_MESSAGE_REQ_EN | CCI400_SNOOP_REQ_EN, 0x52095000);
while (__raw_readl(0x5209000c) & CHANGE_PENDING_BIT)
;
#endif
return 0;
}
int imx8qxp_wake_secondary_cores(void)
{
#ifdef CONFIG_ARMV8_MULTIENTRY
sc_ipc_t ipcHndl;
u64 *table = get_spin_tbl_addr();
/* Clear spin table so that secondary processors
* observe the correct value after waking up from wfe.
*/
memset(table, 0, CONFIG_MAX_CPUS*SPIN_TABLE_ELEM_SIZE);
flush_dcache_range((unsigned long)table,
(unsigned long)table +
(CONFIG_MAX_CPUS*SPIN_TABLE_ELEM_SIZE));
/* Open IPC channel */
if (sc_ipc_open(&ipcHndl, SC_IPC_CH) != SC_ERR_NONE)
return -EIO;
/* IPC to pwr up and boot other cores */
if (sc_pm_set_resource_power_mode(ipcHndl, SC_R_A35_1, SC_PM_PW_MODE_ON) != SC_ERR_NONE)
return -EIO;
if (sc_pm_cpu_start(ipcHndl, SC_R_A35_1, true, 0x80000000) != SC_ERR_NONE)
return -EIO;
if (sc_pm_set_resource_power_mode(ipcHndl, SC_R_A35_2, SC_PM_PW_MODE_ON) != SC_ERR_NONE)
return -EIO;
if (sc_pm_cpu_start(ipcHndl, SC_R_A35_2, true, 0x80000000) != SC_ERR_NONE)
return -EIO;
if (sc_pm_set_resource_power_mode(ipcHndl, SC_R_A35_3, SC_PM_PW_MODE_ON) != SC_ERR_NONE)
return -EIO;
if (sc_pm_cpu_start(ipcHndl, SC_R_A35_3, true, 0x80000000) != SC_ERR_NONE)
return -EIO;
#endif
return 0;
}
int init_i2c_power(unsigned i2c_num)
{
sc_ipc_t ipc;
sc_err_t err;
u32 i;
if (i2c_num >= ARRAY_SIZE(imx_i2c_desc))
return -EINVAL;
ipc = gd->arch.ipc_channel_handle;
for (i = 0; i < ARRAY_SIZE(i2c_parent_power_desc); i++) {
if (i2c_parent_power_desc[i].index == i2c_num) {
err = sc_pm_set_resource_power_mode(ipc,
i2c_parent_power_desc[i].rsrc, SC_PM_PW_MODE_ON);
if (err != SC_ERR_NONE)
return -EPERM;
}
}
/* power up i2c resource */
err = sc_pm_set_resource_power_mode(ipc,
imx_i2c_desc[i2c_num].rsrc, SC_PM_PW_MODE_ON);
if (err != SC_ERR_NONE)
return -EPERM;
return 0;
}
#define FUSE_MAC0_WORD0 452
#define FUSE_MAC0_WORD1 453
#define FUSE_MAC1_WORD0 454
#define FUSE_MAC1_WORD1 455
void imx_get_mac_from_fuse(int dev_id, unsigned char *mac)
{
sc_err_t err;
sc_ipc_t ipc;
uint32_t val1 = 0, val2 = 0;
uint32_t word1, word2;
ipc = gd->arch.ipc_channel_handle;
if (dev_id == 0) {
word1 = FUSE_MAC0_WORD0;
word2 = FUSE_MAC0_WORD1;
} else {
word1 = FUSE_MAC1_WORD0;
word2 = FUSE_MAC1_WORD1;
}
err = sc_misc_otp_fuse_read(ipc, 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(ipc, word2, &val2);
if (err != SC_ERR_NONE) {
printf("%s fuse %d read error: %d\n", __func__, word2, err);
return;
}
mac[0] = val1;
mac[1] = val1 >> 8;
mac[2] = val1 >> 16;
mac[3] = val1 >> 24;
mac[4] = val2;
mac[5] = val2 >> 8;
}
......@@ -26,11 +26,15 @@
#define MXC_CPU_MX7S 0x71 /* dummy ID */
#define MXC_CPU_MX7D 0x72
#define MXC_CPU_MX7ULP 0x81 /* Temporally hard code */
#define MXC_CPU_IMX8QM 0x91 /* dummy ID */
#define MXC_CPU_IMX8QXP 0x92 /* dummy ID */
#define MXC_CPU_IMX8DX 0x93 /* dummy ID */
#define MXC_CPU_VF610 0xF6 /* dummy ID */
#define MXC_SOC_MX6 0x60
#define MXC_SOC_MX7 0x70
#define MXC_SOC_MX7ULP 0x80 /* dummy */
#define MXC_SOC_IMX8 0x90 /* dummy */
#define CHIP_REV_1_0 0x10
#define CHIP_REV_1_1 0x11
......@@ -40,6 +44,9 @@
#define CHIP_REV_2_5 0x25
#define CHIP_REV_3_0 0x30
#define CHIP_REV_A 0x0
#define CHIP_REV_B 0x1
#define BOARD_REV_1_0 0x0
#define BOARD_REV_2_0 0x1
#define BOARD_VER_OFFSET 0x8
......
/*
* Copyright 2017 NXP
*
* SPDX-License-Identifier: GPL-2.0+
*
*/
#ifndef __ASM_ARCH_CLOCK_H__
#define __ASM_ARCH_CLOCK_H__
/* Mainly for compatible to imx common code. */
enum mxc_clock {
MXC_ARM_CLK = 0,
MXC_AHB_CLK,
MXC_IPG_CLK,
MXC_UART_CLK,
MXC_CSPI_CLK,
MXC_AXI_CLK,
MXC_DDR_CLK,
MXC_ESDHC_CLK,
MXC_ESDHC2_CLK,
MXC_ESDHC3_CLK,
MXC_I2C_CLK,
MXC_FEC_CLK,
};
u32 mxc_get_clock(enum mxc_clock clk);
u32 get_lpuart_clk(void);
int enable_i2c_clk(unsigned char enable, unsigned i2c_num);