diff mbox

[v2,07/12] ARM: hisi: add hip04 SoC support

Message ID 1396944052-9887-8-git-send-email-haojian.zhuang@linaro.org
State Changes Requested
Headers show

Commit Message

Haojian Zhuang April 8, 2014, 8 a.m. UTC
Hisilicon Hi3xxx is based on Cortex A9 Core. Now HiP04 SoC is based on
Cortex A15 Core. Since multiple clusters is used in HiP04 SoC, it could
be based on MCPM.

And HiP04 supports LPAE to support large memory.

Signed-off-by: Haojian Zhuang <haojian.zhuang@linaro.org>
---
 arch/arm/Kconfig               |   2 +-
 arch/arm/mach-hisi/Kconfig     |   7 ++
 arch/arm/mach-hisi/core.h      |   6 +
 arch/arm/mach-hisi/hisilicon.c |  19 +++
 arch/arm/mach-hisi/platsmp.c   | 259 +++++++++++++++++++++++++++++++++++++++++
 5 files changed, 292 insertions(+), 1 deletion(-)

Comments

Arnd Bergmann April 8, 2014, 11:10 a.m. UTC | #1
On Tuesday 08 April 2014 16:00:47 Haojian Zhuang wrote:
> diff --git a/arch/arm/mach-hisi/hisilicon.c b/arch/arm/mach-hisi/hisilicon.c
> index 741faf3..10a605f 100644
> --- a/arch/arm/mach-hisi/hisilicon.c
> +++ b/arch/arm/mach-hisi/hisilicon.c
> @@ -14,6 +14,7 @@
>  #include <linux/clk-provider.h>
>  #include <linux/clocksource.h>
>  #include <linux/irqchip.h>
> +#include <linux/memblock.h>
>  #include <linux/of_address.h>
>  #include <linux/of_platform.h>
>  
> @@ -88,3 +89,21 @@ DT_MACHINE_START(HI3620, "Hisilicon Hi3620 (Flattened Device Tree)")
>  	.smp		= smp_ops(hi3xxx_smp_ops),
>  	.restart	= hi3xxx_restart,
>  MACHINE_END
> +
> +static const char *hip04_compat[] __initconst = {
> +	"hisilicon,hip04-d01",
> +	NULL,
> +};
> +
> +static void __init hip04_reserve(void)
> +{
> +	memblock_reserve(HIP04_BOOTWRAPPER_PHYS, HIP04_BOOTWRAPPER_SIZE);
> +}

Can you explain why you do this? Shouldn't that memory just be listed
in the DT?

> +DT_MACHINE_START(HIP04, "Hisilicon HiP04 (Flattened Device Tree)")
> +	.dt_compat	= hip04_compat,
> +#ifdef CONFIG_MCPM
> +	.smp_init	= smp_init_ops(hip04_smp_init_ops),
> +#endif
> +	.reserve	= hip04_reserve,
> +MACHINE_END

I think the #ifdef is not needed here, you already hide hip04_smp_init_ops
if CONFIG_SMP is disabled, and SMP implies MCPM based on your Kconfig.

> diff --git a/arch/arm/mach-hisi/platsmp.c b/arch/arm/mach-hisi/platsmp.c
> index 471f1ee..3a5833f 100644
> --- a/arch/arm/mach-hisi/platsmp.c
> +++ b/arch/arm/mach-hisi/platsmp.c


>  };
> +
> +#ifdef CONFIG_MCPM
> +/* bits definition in SC_CPU_RESET_REQ[x]/SC_CPU_RESET_DREQ[x]
> + * 1 -- unreset; 0 -- reset
> + */
> ...

It would seem appropriate to put all of this into a separate
source file -- there is no shared code between the two SMP
implementations.

> +static int __init hip04_mcpm_init(void)
> +{
> +	struct device_node *np;
> +	int ret = -ENODEV;
> +
> +	np = of_find_compatible_node(NULL, NULL, "hisilicon,hip04-mcpm");
> +	if (!np) {
> +		pr_err("failed to find hisilicon,hip04-mcpm node\n");
> +		goto err;
> +	}

This shouldn't be a fatal error: if you run the kernel on a platform
other than hip04, the code will still be executed here but it won't
find the node and should just ignore the device silently.

> +	relocation = of_iomap(np, 0);
> +	if (!relocation) {
> +		pr_err("failed to get relocation space\n");
> +		ret = -ENOMEM;
> +		goto err;
> +	}
> +	sysctrl = of_iomap(np, 1);
> +	if (!sysctrl) {
> +		pr_err("failed to get sysctrl base\n");
> +		ret = -ENOMEM;
> +		goto err_sysctrl;
> +	}

sysctrl sounds like a shared device that you probably don't want
to map here, but rather use a "syscon" node.

> +}
> +early_initcall(hip04_mcpm_init);

Actually, I guess it would be better to do this from one of the
various calls you already have for SMP initialization than
doing an initcall.

	Arnd
Mark Rutland April 10, 2014, 8:50 a.m. UTC | #2
On Tue, Apr 08, 2014 at 09:00:47AM +0100, Haojian Zhuang wrote:
> Hisilicon Hi3xxx is based on Cortex A9 Core. Now HiP04 SoC is based on
> Cortex A15 Core. Since multiple clusters is used in HiP04 SoC, it could
> be based on MCPM.
> 
> And HiP04 supports LPAE to support large memory.
> 
> Signed-off-by: Haojian Zhuang <haojian.zhuang@linaro.org>
> ---
>  arch/arm/Kconfig               |   2 +-
>  arch/arm/mach-hisi/Kconfig     |   7 ++
>  arch/arm/mach-hisi/core.h      |   6 +
>  arch/arm/mach-hisi/hisilicon.c |  19 +++
>  arch/arm/mach-hisi/platsmp.c   | 259 +++++++++++++++++++++++++++++++++++++++++
>  5 files changed, 292 insertions(+), 1 deletion(-)
> 
> diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig
> index a8b2b45..6af6609 100644
> --- a/arch/arm/Kconfig
> +++ b/arch/arm/Kconfig
> @@ -1113,7 +1113,7 @@ source arch/arm/mm/Kconfig
> 
>  config ARM_NR_BANKS
>         int
> -       default 16 if ARCH_EP93XX
> +       default 16 if ARCH_EP93XX || ARCH_HIP04
>         default 8
> 
>  config IWMMXT
> diff --git a/arch/arm/mach-hisi/Kconfig b/arch/arm/mach-hisi/Kconfig
> index da16efd..4dd966a 100644
> --- a/arch/arm/mach-hisi/Kconfig
> +++ b/arch/arm/mach-hisi/Kconfig
> @@ -19,6 +19,13 @@ config ARCH_HI3xxx
>         help
>           Support for Hisilicon Hi36xx/Hi37xx processor family
> 
> +config ARCH_HIP04
> +       bool "Hisilicon HiP04 Cortex A15 family" if ARCH_MULTI_V7_LPAE
> +       select HAVE_ARM_ARCH_TIMER
> +       select MCPM if SMP
> +       help
> +         Support for Hisilicon HiP04 processor family

Nit: Surely this is an SoC family? The processor is Cortex-A15, as you
state above.

> +
>  endmenu
> 
>  endif
> diff --git a/arch/arm/mach-hisi/core.h b/arch/arm/mach-hisi/core.h
> index af23ec2..e008c7a 100644
> --- a/arch/arm/mach-hisi/core.h
> +++ b/arch/arm/mach-hisi/core.h
> @@ -12,4 +12,10 @@ extern void hi3xxx_cpu_die(unsigned int cpu);
>  extern int hi3xxx_cpu_kill(unsigned int cpu);
>  extern void hi3xxx_set_cpu(int cpu, bool enable);
> 
> +#define HIP04_BOOTWRAPPER_PHYS         0x10c00000
> +#define HIP04_BOOTWRAPPER_MAGIC                0xa5a5a5a5
> +#define HIP04_BOOTWRAPPER_SIZE         0x00010000

The address and size should come from DT.

What is the magic, exactly? How is it used by the kernel and
bootwrapper?

[...]

> +static void __init hip04_reserve(void)
> +{
> +       memblock_reserve(HIP04_BOOTWRAPPER_PHYS, HIP04_BOOTWRAPPER_SIZE);
> +}

As Arnd said, do this in your DT. This is not a hardware property the
kernel needs to statically know, but rather a system-specific property
that needs to be in the DT such that it can vary.

[...]

> +static int hip04_mcpm_power_up(unsigned int cpu, unsigned int cluster)
> +{
> +       unsigned long data, mask;
> +
> +       if (!relocation || !sysctrl)
> +               return -ENODEV;
> +       if (cluster >= HIP04_MAX_CLUSTERS || cpu >= HIP04_MAX_CPUS_PER_CLUSTER)
> +               return -EINVAL;
> +
> +       spin_lock(&boot_lock);
> +       writel_relaxed(HIP04_BOOTWRAPPER_PHYS, relocation);
> +       writel_relaxed(HIP04_BOOTWRAPPER_MAGIC, relocation + 4);
> +       writel_relaxed(virt_to_phys(mcpm_entry_point), relocation + 8);

So the kernel is poking the hardware directly to throw CPUs into a
bootwrapper that it pokes to throw the CPUs into the kernel? That does
not strike me as fantastic.

It's a shame we have to manage coherency here at all and don't have a
PSCI implementation to abstract this. That would mean you wouldn't need
a machine descriptor at all, could reuse existing infrastructure, and
you'd be able to use Hyp.

[...]

> +static void hip04_mcpm_power_down(void)
> +{
> +       unsigned int mpidr, cpu, cluster;
> +       unsigned int v;
> +
> +       spin_lock(&boot_lock);
> +       spin_unlock(&boot_lock);

Huh? What's this for?

> +       asm volatile(
> +       "       mrc     p15, 0, %0, c1, c0, 0\n"
> +       "       bic     %0, %0, %1\n"
> +       "       mcr     p15, 0, %0, c1, c0, 0\n"
> +         : "=&r" (v)
> +         : "Ir" (CR_C)
> +         : "cc");

I don't think that cc clobber is necessary, none of these instructions
set the flags.

> +       asm volatile(
> +       /*
> +       * Turn off coherency
> +       */
> +       "       mrc     p15, 0, %0, c1, c0, 1\n"
> +       "       bic     %0, %0, %1\n"
> +       "       mcr     p15, 0, %0, c1, c0, 1\n"
> +       : "=&r" (v)
> +       : "Ir" (0x40)

0x40?

> +       : "cc");

Likewise I don't think this clobber is necessary.

[...]

> +static int __init hip04_mcpm_init(void)
> +{
> +       struct device_node *np;
> +       int ret = -ENODEV;
> +
> +       np = of_find_compatible_node(NULL, NULL, "hisilicon,hip04-mcpm");
> +       if (!np) {
> +               pr_err("failed to find hisilicon,hip04-mcpm node\n");
> +               goto err;
> +       }

MCPM is a Linux-specific software construct. It is not a piece of
hardware, and not a generic interface name. Please come up with a better
name for this that makes it clear exactly what you are describing.

> +       relocation = of_iomap(np, 0);
> +       if (!relocation) {
> +               pr_err("failed to get relocation space\n");
> +               ret = -ENOMEM;
> +               goto err;
> +       }
> +       sysctrl = of_iomap(np, 1);
> +       if (!sysctrl) {
> +               pr_err("failed to get sysctrl base\n");
> +               ret = -ENOMEM;
> +               goto err_sysctrl;
> +       }
> +       fabric = of_iomap(np, 2);
> +       if (!fabric) {
> +               pr_err("failed to get fabric base\n");
> +               ret = -ENOMEM;
> +               goto err_fabric;
> +       }

These sounds like they would be better described by separate nodes.

Thanks,
Mark.
Dave Martin April 10, 2014, 11:21 a.m. UTC | #3
On Tue, Apr 08, 2014 at 04:00:47PM +0800, Haojian Zhuang wrote:
> Hisilicon Hi3xxx is based on Cortex A9 Core. Now HiP04 SoC is based on
> Cortex A15 Core. Since multiple clusters is used in HiP04 SoC, it could
> be based on MCPM.

The mcpm backend implementation here looks strange.

Before commenting on implementation details, can you explain the
hardware config, and what this code is trying to achieve?

At the moment, I'm confused:

  * There is no obvious code here to actually power CPUs or clusters up
    and down.  There is no wfi(), so I can't see why a CPU will even
    halt when mcpm_power_down() is called.

  * _power_down_finish() looks odd.  It is intended for serialisation
    only, i.e., to confirm that a CPU is physically powered down or
    parked somehow in hardware.  There is no guarantee that the
    _power_down_finish() is called at all.

  * __mcpu_cpu_going_down() and __mcpm_cpu_down() are called in the
    wrong order (if __mcpu_cpu_going_down() is called at all).

  * ... etc.


Cheers
---Dave

> 
> And HiP04 supports LPAE to support large memory.
> 
> Signed-off-by: Haojian Zhuang <haojian.zhuang@linaro.org>
> ---
>  arch/arm/Kconfig               |   2 +-
>  arch/arm/mach-hisi/Kconfig     |   7 ++
>  arch/arm/mach-hisi/core.h      |   6 +
>  arch/arm/mach-hisi/hisilicon.c |  19 +++
>  arch/arm/mach-hisi/platsmp.c   | 259 +++++++++++++++++++++++++++++++++++++++++
>  5 files changed, 292 insertions(+), 1 deletion(-)
> 
> diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig
> index a8b2b45..6af6609 100644
> --- a/arch/arm/Kconfig
> +++ b/arch/arm/Kconfig
> @@ -1113,7 +1113,7 @@ source arch/arm/mm/Kconfig
>  
>  config ARM_NR_BANKS
>  	int
> -	default 16 if ARCH_EP93XX
> +	default 16 if ARCH_EP93XX || ARCH_HIP04
>  	default 8
>  
>  config IWMMXT
> diff --git a/arch/arm/mach-hisi/Kconfig b/arch/arm/mach-hisi/Kconfig
> index da16efd..4dd966a 100644
> --- a/arch/arm/mach-hisi/Kconfig
> +++ b/arch/arm/mach-hisi/Kconfig
> @@ -19,6 +19,13 @@ config ARCH_HI3xxx
>  	help
>  	  Support for Hisilicon Hi36xx/Hi37xx processor family
>  
> +config ARCH_HIP04
> +	bool "Hisilicon HiP04 Cortex A15 family" if ARCH_MULTI_V7_LPAE
> +	select HAVE_ARM_ARCH_TIMER
> +	select MCPM if SMP
> +	help
> +	  Support for Hisilicon HiP04 processor family
> +
>  endmenu
>  
>  endif
> diff --git a/arch/arm/mach-hisi/core.h b/arch/arm/mach-hisi/core.h
> index af23ec2..e008c7a 100644
> --- a/arch/arm/mach-hisi/core.h
> +++ b/arch/arm/mach-hisi/core.h
> @@ -12,4 +12,10 @@ extern void hi3xxx_cpu_die(unsigned int cpu);
>  extern int hi3xxx_cpu_kill(unsigned int cpu);
>  extern void hi3xxx_set_cpu(int cpu, bool enable);
>  
> +#define HIP04_BOOTWRAPPER_PHYS		0x10c00000
> +#define HIP04_BOOTWRAPPER_MAGIC		0xa5a5a5a5
> +#define HIP04_BOOTWRAPPER_SIZE		0x00010000
> +
> +extern bool __init hip04_smp_init_ops(void);
> +
>  #endif
> diff --git a/arch/arm/mach-hisi/hisilicon.c b/arch/arm/mach-hisi/hisilicon.c
> index 741faf3..10a605f 100644
> --- a/arch/arm/mach-hisi/hisilicon.c
> +++ b/arch/arm/mach-hisi/hisilicon.c
> @@ -14,6 +14,7 @@
>  #include <linux/clk-provider.h>
>  #include <linux/clocksource.h>
>  #include <linux/irqchip.h>
> +#include <linux/memblock.h>
>  #include <linux/of_address.h>
>  #include <linux/of_platform.h>
>  
> @@ -88,3 +89,21 @@ DT_MACHINE_START(HI3620, "Hisilicon Hi3620 (Flattened Device Tree)")
>  	.smp		= smp_ops(hi3xxx_smp_ops),
>  	.restart	= hi3xxx_restart,
>  MACHINE_END
> +
> +static const char *hip04_compat[] __initconst = {
> +	"hisilicon,hip04-d01",
> +	NULL,
> +};
> +
> +static void __init hip04_reserve(void)
> +{
> +	memblock_reserve(HIP04_BOOTWRAPPER_PHYS, HIP04_BOOTWRAPPER_SIZE);
> +}
> +
> +DT_MACHINE_START(HIP04, "Hisilicon HiP04 (Flattened Device Tree)")
> +	.dt_compat	= hip04_compat,
> +#ifdef CONFIG_MCPM
> +	.smp_init	= smp_init_ops(hip04_smp_init_ops),
> +#endif
> +	.reserve	= hip04_reserve,
> +MACHINE_END
> diff --git a/arch/arm/mach-hisi/platsmp.c b/arch/arm/mach-hisi/platsmp.c
> index 471f1ee..3a5833f 100644
> --- a/arch/arm/mach-hisi/platsmp.c
> +++ b/arch/arm/mach-hisi/platsmp.c
> @@ -9,9 +9,13 @@
>   */
>  #include <linux/smp.h>
>  #include <linux/io.h>
> +#include <linux/irqchip/arm-gic.h>
>  #include <linux/of_address.h>
>  
>  #include <asm/cacheflush.h>
> +#include <asm/cp15.h>
> +#include <asm/cputype.h>
> +#include <asm/mcpm.h>
>  #include <asm/smp_plat.h>
>  #include <asm/smp_scu.h>
>  
> @@ -87,3 +91,258 @@ struct smp_operations hi3xxx_smp_ops __initdata = {
>  	.cpu_kill		= hi3xxx_cpu_kill,
>  #endif
>  };
> +
> +#ifdef CONFIG_MCPM

It would be cleaner to split the mcpm code out into a separate file
and lose the #ifdefs.  There doesn't seem to be any interdependency
between the mcpm code and platsmp code here.

> +/* bits definition in SC_CPU_RESET_REQ[x]/SC_CPU_RESET_DREQ[x]
> + * 1 -- unreset; 0 -- reset
> + */
> +#define CORE_RESET_BIT(x)		(1 << x)
> +#define NEON_RESET_BIT(x)		(1 << (x + 4))
> +#define CORE_DEBUG_RESET_BIT(x)		(1 << (x + 9))
> +#define CLUSTER_L2_RESET_BIT		(1 << 8)
> +#define CLUSTER_DEBUG_RESET_BIT		(1 << 13)
> +
> +/*
> + * bits definition in SC_CPU_RESET_STATUS[x]
> + * 1 -- reset status; 0 -- unreset status
> + */
> +#define CORE_RESET_STATUS(x)		(1 << x)
> +#define NEON_RESET_STATUS(x)		(1 << (x + 4))
> +#define CORE_DEBUG_RESET_STATUS(x)	(1 << (x + 9))
> +#define CLUSTER_L2_RESET_STATUS		(1 << 8)
> +#define CLUSTER_DEBUG_RESET_STATUS	(1 << 13)
> +#define CORE_WFI_STATUS(x)		(1 << (x + 16))
> +#define CORE_WFE_STATUS(x)		(1 << (x + 20))
> +#define CORE_DEBUG_ACK(x)		(1 << (x + 24))
> +
> +#define SC_CPU_RESET_REQ(x)		(0x520 + (x << 3))	/* reset */
> +#define SC_CPU_RESET_DREQ(x)		(0x524 + (x << 3))	/* unreset */
> +#define SC_CPU_RESET_STATUS(x)		(0x1520 + (x << 3))
> +
> +#define FAB_SF_MODE			0x0c
> +#define FAB_SF_INVLD			0x10
> +
> +/* bits definition in FB_SF_INVLD */
> +#define FB_SF_INVLD_START		(1 << 8)
> +
> +#define HIP04_MAX_CLUSTERS		4
> +#define HIP04_MAX_CPUS_PER_CLUSTER	4
> +
> +static void __iomem *relocation, *sysctrl, *fabric;
> +static int hip04_cpu_table[HIP04_MAX_CLUSTERS][HIP04_MAX_CPUS_PER_CLUSTER];
> +static DEFINE_SPINLOCK(boot_lock);
> +
> +static bool hip04_cluster_down(unsigned int cluster)
> +{
> +	int i;
> +
> +	for (i = 0; i < HIP04_MAX_CPUS_PER_CLUSTER; i++)
> +		if (hip04_cpu_table[cluster][i])
> +			return false;
> +	return true;
> +}
> +
> +static void hip04_set_snoop_filter(unsigned int cluster, unsigned int on)
> +{
> +	unsigned long data;
> +
> +	if (!fabric)
> +		return;
> +	data = readl_relaxed(fabric + FAB_SF_MODE);
> +	if (on)
> +		data |= 1 << cluster;
> +	else
> +		data &= ~(1 << cluster);
> +	writel_relaxed(data, fabric + FAB_SF_MODE);
> +	while (1) {
> +		if (data == readl_relaxed(fabric + FAB_SF_MODE))
> +			break;
> +	}
> +}
> +
> +static int hip04_mcpm_power_up(unsigned int cpu, unsigned int cluster)
> +{
> +	unsigned long data, mask;
> +
> +	if (!relocation || !sysctrl)
> +		return -ENODEV;
> +	if (cluster >= HIP04_MAX_CLUSTERS || cpu >= HIP04_MAX_CPUS_PER_CLUSTER)
> +		return -EINVAL;
> +
> +	spin_lock(&boot_lock);
> +	writel_relaxed(HIP04_BOOTWRAPPER_PHYS, relocation);
> +	writel_relaxed(HIP04_BOOTWRAPPER_MAGIC, relocation + 4);
> +	writel_relaxed(virt_to_phys(mcpm_entry_point), relocation + 8);
> +	writel_relaxed(0, relocation + 12);
> +
> +	if (hip04_cluster_down(cluster)) {
> +		data = CLUSTER_L2_RESET_BIT | CLUSTER_DEBUG_RESET_BIT;
> +		writel_relaxed(data, sysctrl + SC_CPU_RESET_DREQ(cluster));
> +		do {
> +			mask = CLUSTER_L2_RESET_STATUS | \
> +			       CLUSTER_DEBUG_RESET_STATUS;
> +			data = readl_relaxed(sysctrl + \
> +					     SC_CPU_RESET_STATUS(cluster));
> +		} while (data & mask);
> +		hip04_set_snoop_filter(cluster, 1);
> +	}
> +
> +	hip04_cpu_table[cluster][cpu]++;
> +
> +	data = CORE_RESET_BIT(cpu) | NEON_RESET_BIT(cpu) | \
> +	       CORE_DEBUG_RESET_BIT(cpu);
> +	writel_relaxed(data, sysctrl + SC_CPU_RESET_DREQ(cluster));
> +	spin_unlock(&boot_lock);
> +
> +	return 0;
> +}
> +
> +static void hip04_mcpm_power_down(void)
> +{
> +	unsigned int mpidr, cpu, cluster;
> +	unsigned int v;
> +
> +	spin_lock(&boot_lock);
> +	spin_unlock(&boot_lock);
> +
> +	mpidr = read_cpuid_mpidr();
> +	cpu = MPIDR_AFFINITY_LEVEL(mpidr, 0);
> +	cluster = MPIDR_AFFINITY_LEVEL(mpidr, 1);
> +
> +	local_irq_disable();
> +	gic_cpu_if_down();
> +
> +	__mcpm_cpu_down(cpu, cluster);
> +
> +	asm volatile(
> +	"	mrc	p15, 0, %0, c1, c0, 0\n"
> +	"	bic	%0, %0, %1\n"
> +	"	mcr	p15, 0, %0, c1, c0, 0\n"
> +	  : "=&r" (v)
> +	  : "Ir" (CR_C)
> +	  : "cc");
> +
> +	flush_cache_louis();
> +
> +	asm volatile(
> +	/*
> +	* Turn off coherency
> +	*/
> +	"	mrc	p15, 0, %0, c1, c0, 1\n"
> +	"	bic	%0, %0, %1\n"
> +	"	mcr	p15, 0, %0, c1, c0, 1\n"
> +	: "=&r" (v)
> +	: "Ir" (0x40)
> +	: "cc");
> +
> +	isb();
> +	dsb();
> +}
> +
> +static int hip04_mcpm_power_down_finish(unsigned int cpu, unsigned int cluster)
> +{
> +	int ret = -EBUSY;
> +
> +	spin_lock(&boot_lock);
> +	BUG_ON(__mcpm_cluster_state(cluster) != CLUSTER_UP);
> +	__mcpm_cpu_going_down(cpu, cluster);
> +
> +	hip04_cpu_table[cluster][cpu]--;
> +	if (hip04_cpu_table[cluster][cpu]) {
> +		pr_err("Cluster %d CPU%d is still running\n", cluster, cpu);
> +		goto out;
> +	}
> +	ret = 0;
> +out:
> +	spin_unlock(&boot_lock);
> +	return ret;
> +}
> +
> +static void hip04_mcpm_powered_up(void)
> +{
> +	if (!relocation)
> +		return;
> +	spin_lock(&boot_lock);
> +	writel_relaxed(0, relocation);
> +	writel_relaxed(0, relocation + 4);
> +	writel_relaxed(0, relocation + 8);
> +	writel_relaxed(0, relocation + 12);
> +	spin_unlock(&boot_lock);
> +}
> +
> +static const struct mcpm_platform_ops hip04_mcpm_ops = {
> +	.power_up		= hip04_mcpm_power_up,
> +	.power_down		= hip04_mcpm_power_down,
> +	.power_down_finish	= hip04_mcpm_power_down_finish,
> +	.powered_up		= hip04_mcpm_powered_up,
> +};
> +
> +static bool __init hip04_cpu_table_init(void)
> +{
> +	unsigned int mpidr, cpu, cluster;
> +
> +	mpidr = read_cpuid_mpidr();
> +	cpu = MPIDR_AFFINITY_LEVEL(mpidr, 0);
> +	cluster = MPIDR_AFFINITY_LEVEL(mpidr, 1);
> +
> +	if (cluster >= HIP04_MAX_CLUSTERS ||
> +	    cpu >= HIP04_MAX_CPUS_PER_CLUSTER) {
> +		pr_err("%s: boot CPU is out of bound!\n", __func__);
> +		return false;
> +	}
> +	hip04_set_snoop_filter(cluster, 1);
> +	hip04_cpu_table[cluster][cpu] = 1;
> +	return true;
> +}
> +
> +static int __init hip04_mcpm_init(void)
> +{
> +	struct device_node *np;
> +	int ret = -ENODEV;
> +
> +	np = of_find_compatible_node(NULL, NULL, "hisilicon,hip04-mcpm");
> +	if (!np) {
> +		pr_err("failed to find hisilicon,hip04-mcpm node\n");
> +		goto err;
> +	}
> +	relocation = of_iomap(np, 0);
> +	if (!relocation) {
> +		pr_err("failed to get relocation space\n");
> +		ret = -ENOMEM;
> +		goto err;
> +	}
> +	sysctrl = of_iomap(np, 1);
> +	if (!sysctrl) {
> +		pr_err("failed to get sysctrl base\n");
> +		ret = -ENOMEM;
> +		goto err_sysctrl;
> +	}
> +	fabric = of_iomap(np, 2);
> +	if (!fabric) {
> +		pr_err("failed to get fabric base\n");
> +		ret = -ENOMEM;
> +		goto err_fabric;
> +	}
> +	if (!hip04_cpu_table_init())
> +		return -EINVAL;
> +	ret = mcpm_platform_register(&hip04_mcpm_ops);
> +	if (!ret) {
> +		mcpm_sync_init(NULL);
> +		pr_info("HiP04 MCPM initialized\n");
> +	}
> +	return ret;
> +err_fabric:
> +	iounmap(sysctrl);
> +err_sysctrl:
> +	iounmap(relocation);
> +err:
> +	return ret;
> +}
> +early_initcall(hip04_mcpm_init);
> +
> +bool __init hip04_smp_init_ops(void)
> +{
> +	mcpm_smp_set_ops();
> +	return true;
> +}
> +#endif	/* CONFIG_MCPM */
> -- 
> 1.8.3.2
> 
> 
> _______________________________________________
> linux-arm-kernel mailing list
> linux-arm-kernel@lists.infradead.org
> http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
Haojian Zhuang April 15, 2014, 7:02 a.m. UTC | #4
On 8 April 2014 19:10, Arnd Bergmann <arnd@arndb.de> wrote:
> On Tuesday 08 April 2014 16:00:47 Haojian Zhuang wrote:
>> diff --git a/arch/arm/mach-hisi/hisilicon.c b/arch/arm/mach-hisi/hisilicon.c
>> index 741faf3..10a605f 100644
>> --- a/arch/arm/mach-hisi/hisilicon.c
>> +++ b/arch/arm/mach-hisi/hisilicon.c
>> @@ -14,6 +14,7 @@
>>  #include <linux/clk-provider.h>
>>  #include <linux/clocksource.h>
>>  #include <linux/irqchip.h>
>> +#include <linux/memblock.h>
>>  #include <linux/of_address.h>
>>  #include <linux/of_platform.h>
>>
>> @@ -88,3 +89,21 @@ DT_MACHINE_START(HI3620, "Hisilicon Hi3620 (Flattened Device Tree)")
>>       .smp            = smp_ops(hi3xxx_smp_ops),
>>       .restart        = hi3xxx_restart,
>>  MACHINE_END
>> +
>> +static const char *hip04_compat[] __initconst = {
>> +     "hisilicon,hip04-d01",
>> +     NULL,
>> +};
>> +
>> +static void __init hip04_reserve(void)
>> +{
>> +     memblock_reserve(HIP04_BOOTWRAPPER_PHYS, HIP04_BOOTWRAPPER_SIZE);
>> +}
>
> Can you explain why you do this? Shouldn't that memory just be listed
> in the DT?

In current implementation, boot code of secondary CPUs are stored in the memory
of HIP04_BOOTWRAPPER_PHYS.

If the bootwrapper could be removed, we need to refresh the code on booting
up secondary CPU.
>
>> +DT_MACHINE_START(HIP04, "Hisilicon HiP04 (Flattened Device Tree)")
>> +     .dt_compat      = hip04_compat,
>> +#ifdef CONFIG_MCPM
>> +     .smp_init       = smp_init_ops(hip04_smp_init_ops),
>> +#endif
>> +     .reserve        = hip04_reserve,
>> +MACHINE_END
>
> I think the #ifdef is not needed here, you already hide hip04_smp_init_ops
> if CONFIG_SMP is disabled, and SMP implies MCPM based on your Kconfig.
>

If I build only ARCH_HI3xxx without ARCH_HIP04, CONFIG_MCPM won't be
selected. Then I'll meet the build error.

>> diff --git a/arch/arm/mach-hisi/platsmp.c b/arch/arm/mach-hisi/platsmp.c
>> index 471f1ee..3a5833f 100644
>> --- a/arch/arm/mach-hisi/platsmp.c
>> +++ b/arch/arm/mach-hisi/platsmp.c
>
>
>>  };
>> +
>> +#ifdef CONFIG_MCPM
>> +/* bits definition in SC_CPU_RESET_REQ[x]/SC_CPU_RESET_DREQ[x]
>> + * 1 -- unreset; 0 -- reset
>> + */
>> ...
>
> It would seem appropriate to put all of this into a separate
> source file -- there is no shared code between the two SMP
> implementations.
>

Sure. I'll move the contents into platmcpm.c in mach-hisi directory.

>> +static int __init hip04_mcpm_init(void)
>> +{
>> +     struct device_node *np;
>> +     int ret = -ENODEV;
>> +
>> +     np = of_find_compatible_node(NULL, NULL, "hisilicon,hip04-mcpm");
>> +     if (!np) {
>> +             pr_err("failed to find hisilicon,hip04-mcpm node\n");
>> +             goto err;
>> +     }
>
> This shouldn't be a fatal error: if you run the kernel on a platform
> other than hip04, the code will still be executed here but it won't
> find the node and should just ignore the device silently.
>

OK. I'll fix it.

>> +     relocation = of_iomap(np, 0);
>> +     if (!relocation) {
>> +             pr_err("failed to get relocation space\n");
>> +             ret = -ENOMEM;
>> +             goto err;
>> +     }
>> +     sysctrl = of_iomap(np, 1);
>> +     if (!sysctrl) {
>> +             pr_err("failed to get sysctrl base\n");
>> +             ret = -ENOMEM;
>> +             goto err_sysctrl;
>> +     }
>
> sysctrl sounds like a shared device that you probably don't want
> to map here, but rather use a "syscon" node.
>
Up to now, only this driver is using it.

>> +}
>> +early_initcall(hip04_mcpm_init);
>
> Actually, I guess it would be better to do this from one of the
> various calls you already have for SMP initialization than
> doing an initcall.
>
>         Arnd
Haojian Zhuang April 15, 2014, 7:35 a.m. UTC | #5
On 10 April 2014 16:50, Mark Rutland <mark.rutland@arm.com> wrote:
> On Tue, Apr 08, 2014 at 09:00:47AM +0100, Haojian Zhuang wrote:
>> Hisilicon Hi3xxx is based on Cortex A9 Core. Now HiP04 SoC is based on
>> Cortex A15 Core. Since multiple clusters is used in HiP04 SoC, it could
>> be based on MCPM.
>>
>>
>> +config ARCH_HIP04
>> +       bool "Hisilicon HiP04 Cortex A15 family" if ARCH_MULTI_V7_LPAE
>> +       select HAVE_ARM_ARCH_TIMER
>> +       select MCPM if SMP
>> +       help
>> +         Support for Hisilicon HiP04 processor family
>
> Nit: Surely this is an SoC family? The processor is Cortex-A15, as you
> state above.
>
Yes, it's SoC, not processor. I'll fix it.

>> +
>>  endmenu
>>
>>  endif
>> diff --git a/arch/arm/mach-hisi/core.h b/arch/arm/mach-hisi/core.h
>> index af23ec2..e008c7a 100644
>> --- a/arch/arm/mach-hisi/core.h
>> +++ b/arch/arm/mach-hisi/core.h
>> @@ -12,4 +12,10 @@ extern void hi3xxx_cpu_die(unsigned int cpu);
>>  extern int hi3xxx_cpu_kill(unsigned int cpu);
>>  extern void hi3xxx_set_cpu(int cpu, bool enable);
>>
>> +#define HIP04_BOOTWRAPPER_PHYS         0x10c00000
>> +#define HIP04_BOOTWRAPPER_MAGIC                0xa5a5a5a5
>> +#define HIP04_BOOTWRAPPER_SIZE         0x00010000
>
> The address and size should come from DT.
>
> What is the magic, exactly? How is it used by the kernel and
> bootwrapper?
>
Of course, I can move it into DT.

>
>> +static void __init hip04_reserve(void)
>> +{
>> +       memblock_reserve(HIP04_BOOTWRAPPER_PHYS, HIP04_BOOTWRAPPER_SIZE);
>> +}
>
> As Arnd said, do this in your DT. This is not a hardware property the
> kernel needs to statically know, but rather a system-specific property
> that needs to be in the DT such that it can vary.
>
Sure. I'll update it.

>
>> +static int hip04_mcpm_power_up(unsigned int cpu, unsigned int cluster)
>> +{
>> +       unsigned long data, mask;
>> +
>> +       if (!relocation || !sysctrl)
>> +               return -ENODEV;
>> +       if (cluster >= HIP04_MAX_CLUSTERS || cpu >= HIP04_MAX_CPUS_PER_CLUSTER)
>> +               return -EINVAL;
>> +
>> +       spin_lock(&boot_lock);
>> +       writel_relaxed(HIP04_BOOTWRAPPER_PHYS, relocation);
>> +       writel_relaxed(HIP04_BOOTWRAPPER_MAGIC, relocation + 4);
>> +       writel_relaxed(virt_to_phys(mcpm_entry_point), relocation + 8);
>
> So the kernel is poking the hardware directly to throw CPUs into a
> bootwrapper that it pokes to throw the CPUs into the kernel? That does
> not strike me as fantastic.
>
> It's a shame we have to manage coherency here at all and don't have a
> PSCI implementation to abstract this. That would mean you wouldn't need
> a machine descriptor at all, could reuse existing infrastructure, and
> you'd be able to use Hyp.
>

Do we need a firmware to support PSCI first? Up to now, I don't have
this kind of firmware.

>
>> +static void hip04_mcpm_power_down(void)
>> +{
>> +       unsigned int mpidr, cpu, cluster;
>> +       unsigned int v;
>> +
>> +       spin_lock(&boot_lock);
>> +       spin_unlock(&boot_lock);
>
> Huh? What's this for?

Actually it could be removed. Since secondary CPU is always booted up
one by one.
>
>> +       asm volatile(
>> +       "       mrc     p15, 0, %0, c1, c0, 0\n"
>> +       "       bic     %0, %0, %1\n"
>> +       "       mcr     p15, 0, %0, c1, c0, 0\n"
>> +         : "=&r" (v)
>> +         : "Ir" (CR_C)
>> +         : "cc");
>
> I don't think that cc clobber is necessary, none of these instructions
> set the flags.

Yes, I'll remove it.
>
>> +       asm volatile(
>> +       /*
>> +       * Turn off coherency
>> +       */
>> +       "       mrc     p15, 0, %0, c1, c0, 1\n"
>> +       "       bic     %0, %0, %1\n"
>> +       "       mcr     p15, 0, %0, c1, c0, 1\n"
>> +       : "=&r" (v)
>> +       : "Ir" (0x40)
>
> 0x40?

Disable SMP bit in ACTLR register.

>
>> +       : "cc");
>
> Likewise I don't think this clobber is necessary.

Yes, I'll remove it.
>
>> +static int __init hip04_mcpm_init(void)
>> +{
>> +       struct device_node *np;
>> +       int ret = -ENODEV;
>> +
>> +       np = of_find_compatible_node(NULL, NULL, "hisilicon,hip04-mcpm");
>> +       if (!np) {
>> +               pr_err("failed to find hisilicon,hip04-mcpm node\n");
>> +               goto err;
>> +       }
>
> MCPM is a Linux-specific software construct. It is not a piece of
> hardware, and not a generic interface name. Please come up with a better
> name for this that makes it clear exactly what you are describing.
>

OK. I'll use "hisilicon,core-power-control" instead.

>> +       relocation = of_iomap(np, 0);
>> +       if (!relocation) {
>> +               pr_err("failed to get relocation space\n");
>> +               ret = -ENOMEM;
>> +               goto err;
>> +       }
>> +       sysctrl = of_iomap(np, 1);
>> +       if (!sysctrl) {
>> +               pr_err("failed to get sysctrl base\n");
>> +               ret = -ENOMEM;
>> +               goto err_sysctrl;
>> +       }
>> +       fabric = of_iomap(np, 2);
>> +       if (!fabric) {
>> +               pr_err("failed to get fabric base\n");
>> +               ret = -ENOMEM;
>> +               goto err_fabric;
>> +       }
>
> These sounds like they would be better described by separate nodes.
>
OK
Arnd Bergmann April 15, 2014, 7:50 a.m. UTC | #6
On Tuesday 15 April 2014 15:02:10 Haojian Zhuang wrote:
> On 8 April 2014 19:10, Arnd Bergmann <arnd@arndb.de> wrote:
> > On Tuesday 08 April 2014 16:00:47 Haojian Zhuang wrote:
> >> diff --git a/arch/arm/mach-hisi/hisilicon.c b/arch/arm/mach-hisi/hisilicon.c
> >> index 741faf3..10a605f 100644
> >> --- a/arch/arm/mach-hisi/hisilicon.c
> >> +++ b/arch/arm/mach-hisi/hisilicon.c
> >> @@ -14,6 +14,7 @@
> >>  #include <linux/clk-provider.h>
> >>  #include <linux/clocksource.h>
> >>  #include <linux/irqchip.h>
> >> +#include <linux/memblock.h>
> >>  #include <linux/of_address.h>
> >>  #include <linux/of_platform.h>
> >>
> >> @@ -88,3 +89,21 @@ DT_MACHINE_START(HI3620, "Hisilicon Hi3620 (Flattened Device Tree)")
> >>       .smp            = smp_ops(hi3xxx_smp_ops),
> >>       .restart        = hi3xxx_restart,
> >>  MACHINE_END
> >> +
> >> +static const char *hip04_compat[] __initconst = {
> >> +     "hisilicon,hip04-d01",
> >> +     NULL,
> >> +};
> >> +
> >> +static void __init hip04_reserve(void)
> >> +{
> >> +     memblock_reserve(HIP04_BOOTWRAPPER_PHYS, HIP04_BOOTWRAPPER_SIZE);
> >> +}
> >
> > Can you explain why you do this? Shouldn't that memory just be listed
> > in the DT?
> 
> In current implementation, boot code of secondary CPUs are stored in the memory
> of HIP04_BOOTWRAPPER_PHYS.
> 
> If the bootwrapper could be removed, we need to refresh the code on booting
> up secondary CPU.

What I meant was that you should not call memblock_reserve() from an early
function here, but instead pass correct data through the DT so you don't
have to. You can reserve the memory in DT.

If changing the boot code is an option, you might consider just using the
standard PSCI interface. That is definitely the recommended approach, although
I don't know how that interacts with MCPM

> >> +DT_MACHINE_START(HIP04, "Hisilicon HiP04 (Flattened Device Tree)")
> >> +     .dt_compat      = hip04_compat,
> >> +#ifdef CONFIG_MCPM
> >> +     .smp_init       = smp_init_ops(hip04_smp_init_ops),
> >> +#endif
> >> +     .reserve        = hip04_reserve,
> >> +MACHINE_END
> >
> > I think the #ifdef is not needed here, you already hide hip04_smp_init_ops
> > if CONFIG_SMP is disabled, and SMP implies MCPM based on your Kconfig.
> >
> 
> If I build only ARCH_HI3xxx without ARCH_HIP04, CONFIG_MCPM won't be
> selected. Then I'll meet the build error.

I would rather see an #ifdef ARCH_HIP04 around the HIP04 code then.
You could also just use separate files for HI3xxx and HIP04, since
there is nothing shared between the two anyway.

	Arnd
diff mbox

Patch

diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig
index a8b2b45..6af6609 100644
--- a/arch/arm/Kconfig
+++ b/arch/arm/Kconfig
@@ -1113,7 +1113,7 @@  source arch/arm/mm/Kconfig
 
 config ARM_NR_BANKS
 	int
-	default 16 if ARCH_EP93XX
+	default 16 if ARCH_EP93XX || ARCH_HIP04
 	default 8
 
 config IWMMXT
diff --git a/arch/arm/mach-hisi/Kconfig b/arch/arm/mach-hisi/Kconfig
index da16efd..4dd966a 100644
--- a/arch/arm/mach-hisi/Kconfig
+++ b/arch/arm/mach-hisi/Kconfig
@@ -19,6 +19,13 @@  config ARCH_HI3xxx
 	help
 	  Support for Hisilicon Hi36xx/Hi37xx processor family
 
+config ARCH_HIP04
+	bool "Hisilicon HiP04 Cortex A15 family" if ARCH_MULTI_V7_LPAE
+	select HAVE_ARM_ARCH_TIMER
+	select MCPM if SMP
+	help
+	  Support for Hisilicon HiP04 processor family
+
 endmenu
 
 endif
diff --git a/arch/arm/mach-hisi/core.h b/arch/arm/mach-hisi/core.h
index af23ec2..e008c7a 100644
--- a/arch/arm/mach-hisi/core.h
+++ b/arch/arm/mach-hisi/core.h
@@ -12,4 +12,10 @@  extern void hi3xxx_cpu_die(unsigned int cpu);
 extern int hi3xxx_cpu_kill(unsigned int cpu);
 extern void hi3xxx_set_cpu(int cpu, bool enable);
 
+#define HIP04_BOOTWRAPPER_PHYS		0x10c00000
+#define HIP04_BOOTWRAPPER_MAGIC		0xa5a5a5a5
+#define HIP04_BOOTWRAPPER_SIZE		0x00010000
+
+extern bool __init hip04_smp_init_ops(void);
+
 #endif
diff --git a/arch/arm/mach-hisi/hisilicon.c b/arch/arm/mach-hisi/hisilicon.c
index 741faf3..10a605f 100644
--- a/arch/arm/mach-hisi/hisilicon.c
+++ b/arch/arm/mach-hisi/hisilicon.c
@@ -14,6 +14,7 @@ 
 #include <linux/clk-provider.h>
 #include <linux/clocksource.h>
 #include <linux/irqchip.h>
+#include <linux/memblock.h>
 #include <linux/of_address.h>
 #include <linux/of_platform.h>
 
@@ -88,3 +89,21 @@  DT_MACHINE_START(HI3620, "Hisilicon Hi3620 (Flattened Device Tree)")
 	.smp		= smp_ops(hi3xxx_smp_ops),
 	.restart	= hi3xxx_restart,
 MACHINE_END
+
+static const char *hip04_compat[] __initconst = {
+	"hisilicon,hip04-d01",
+	NULL,
+};
+
+static void __init hip04_reserve(void)
+{
+	memblock_reserve(HIP04_BOOTWRAPPER_PHYS, HIP04_BOOTWRAPPER_SIZE);
+}
+
+DT_MACHINE_START(HIP04, "Hisilicon HiP04 (Flattened Device Tree)")
+	.dt_compat	= hip04_compat,
+#ifdef CONFIG_MCPM
+	.smp_init	= smp_init_ops(hip04_smp_init_ops),
+#endif
+	.reserve	= hip04_reserve,
+MACHINE_END
diff --git a/arch/arm/mach-hisi/platsmp.c b/arch/arm/mach-hisi/platsmp.c
index 471f1ee..3a5833f 100644
--- a/arch/arm/mach-hisi/platsmp.c
+++ b/arch/arm/mach-hisi/platsmp.c
@@ -9,9 +9,13 @@ 
  */
 #include <linux/smp.h>
 #include <linux/io.h>
+#include <linux/irqchip/arm-gic.h>
 #include <linux/of_address.h>
 
 #include <asm/cacheflush.h>
+#include <asm/cp15.h>
+#include <asm/cputype.h>
+#include <asm/mcpm.h>
 #include <asm/smp_plat.h>
 #include <asm/smp_scu.h>
 
@@ -87,3 +91,258 @@  struct smp_operations hi3xxx_smp_ops __initdata = {
 	.cpu_kill		= hi3xxx_cpu_kill,
 #endif
 };
+
+#ifdef CONFIG_MCPM
+/* bits definition in SC_CPU_RESET_REQ[x]/SC_CPU_RESET_DREQ[x]
+ * 1 -- unreset; 0 -- reset
+ */
+#define CORE_RESET_BIT(x)		(1 << x)
+#define NEON_RESET_BIT(x)		(1 << (x + 4))
+#define CORE_DEBUG_RESET_BIT(x)		(1 << (x + 9))
+#define CLUSTER_L2_RESET_BIT		(1 << 8)
+#define CLUSTER_DEBUG_RESET_BIT		(1 << 13)
+
+/*
+ * bits definition in SC_CPU_RESET_STATUS[x]
+ * 1 -- reset status; 0 -- unreset status
+ */
+#define CORE_RESET_STATUS(x)		(1 << x)
+#define NEON_RESET_STATUS(x)		(1 << (x + 4))
+#define CORE_DEBUG_RESET_STATUS(x)	(1 << (x + 9))
+#define CLUSTER_L2_RESET_STATUS		(1 << 8)
+#define CLUSTER_DEBUG_RESET_STATUS	(1 << 13)
+#define CORE_WFI_STATUS(x)		(1 << (x + 16))
+#define CORE_WFE_STATUS(x)		(1 << (x + 20))
+#define CORE_DEBUG_ACK(x)		(1 << (x + 24))
+
+#define SC_CPU_RESET_REQ(x)		(0x520 + (x << 3))	/* reset */
+#define SC_CPU_RESET_DREQ(x)		(0x524 + (x << 3))	/* unreset */
+#define SC_CPU_RESET_STATUS(x)		(0x1520 + (x << 3))
+
+#define FAB_SF_MODE			0x0c
+#define FAB_SF_INVLD			0x10
+
+/* bits definition in FB_SF_INVLD */
+#define FB_SF_INVLD_START		(1 << 8)
+
+#define HIP04_MAX_CLUSTERS		4
+#define HIP04_MAX_CPUS_PER_CLUSTER	4
+
+static void __iomem *relocation, *sysctrl, *fabric;
+static int hip04_cpu_table[HIP04_MAX_CLUSTERS][HIP04_MAX_CPUS_PER_CLUSTER];
+static DEFINE_SPINLOCK(boot_lock);
+
+static bool hip04_cluster_down(unsigned int cluster)
+{
+	int i;
+
+	for (i = 0; i < HIP04_MAX_CPUS_PER_CLUSTER; i++)
+		if (hip04_cpu_table[cluster][i])
+			return false;
+	return true;
+}
+
+static void hip04_set_snoop_filter(unsigned int cluster, unsigned int on)
+{
+	unsigned long data;
+
+	if (!fabric)
+		return;
+	data = readl_relaxed(fabric + FAB_SF_MODE);
+	if (on)
+		data |= 1 << cluster;
+	else
+		data &= ~(1 << cluster);
+	writel_relaxed(data, fabric + FAB_SF_MODE);
+	while (1) {
+		if (data == readl_relaxed(fabric + FAB_SF_MODE))
+			break;
+	}
+}
+
+static int hip04_mcpm_power_up(unsigned int cpu, unsigned int cluster)
+{
+	unsigned long data, mask;
+
+	if (!relocation || !sysctrl)
+		return -ENODEV;
+	if (cluster >= HIP04_MAX_CLUSTERS || cpu >= HIP04_MAX_CPUS_PER_CLUSTER)
+		return -EINVAL;
+
+	spin_lock(&boot_lock);
+	writel_relaxed(HIP04_BOOTWRAPPER_PHYS, relocation);
+	writel_relaxed(HIP04_BOOTWRAPPER_MAGIC, relocation + 4);
+	writel_relaxed(virt_to_phys(mcpm_entry_point), relocation + 8);
+	writel_relaxed(0, relocation + 12);
+
+	if (hip04_cluster_down(cluster)) {
+		data = CLUSTER_L2_RESET_BIT | CLUSTER_DEBUG_RESET_BIT;
+		writel_relaxed(data, sysctrl + SC_CPU_RESET_DREQ(cluster));
+		do {
+			mask = CLUSTER_L2_RESET_STATUS | \
+			       CLUSTER_DEBUG_RESET_STATUS;
+			data = readl_relaxed(sysctrl + \
+					     SC_CPU_RESET_STATUS(cluster));
+		} while (data & mask);
+		hip04_set_snoop_filter(cluster, 1);
+	}
+
+	hip04_cpu_table[cluster][cpu]++;
+
+	data = CORE_RESET_BIT(cpu) | NEON_RESET_BIT(cpu) | \
+	       CORE_DEBUG_RESET_BIT(cpu);
+	writel_relaxed(data, sysctrl + SC_CPU_RESET_DREQ(cluster));
+	spin_unlock(&boot_lock);
+
+	return 0;
+}
+
+static void hip04_mcpm_power_down(void)
+{
+	unsigned int mpidr, cpu, cluster;
+	unsigned int v;
+
+	spin_lock(&boot_lock);
+	spin_unlock(&boot_lock);
+
+	mpidr = read_cpuid_mpidr();
+	cpu = MPIDR_AFFINITY_LEVEL(mpidr, 0);
+	cluster = MPIDR_AFFINITY_LEVEL(mpidr, 1);
+
+	local_irq_disable();
+	gic_cpu_if_down();
+
+	__mcpm_cpu_down(cpu, cluster);
+
+	asm volatile(
+	"	mrc	p15, 0, %0, c1, c0, 0\n"
+	"	bic	%0, %0, %1\n"
+	"	mcr	p15, 0, %0, c1, c0, 0\n"
+	  : "=&r" (v)
+	  : "Ir" (CR_C)
+	  : "cc");
+
+	flush_cache_louis();
+
+	asm volatile(
+	/*
+	* Turn off coherency
+	*/
+	"	mrc	p15, 0, %0, c1, c0, 1\n"
+	"	bic	%0, %0, %1\n"
+	"	mcr	p15, 0, %0, c1, c0, 1\n"
+	: "=&r" (v)
+	: "Ir" (0x40)
+	: "cc");
+
+	isb();
+	dsb();
+}
+
+static int hip04_mcpm_power_down_finish(unsigned int cpu, unsigned int cluster)
+{
+	int ret = -EBUSY;
+
+	spin_lock(&boot_lock);
+	BUG_ON(__mcpm_cluster_state(cluster) != CLUSTER_UP);
+	__mcpm_cpu_going_down(cpu, cluster);
+
+	hip04_cpu_table[cluster][cpu]--;
+	if (hip04_cpu_table[cluster][cpu]) {
+		pr_err("Cluster %d CPU%d is still running\n", cluster, cpu);
+		goto out;
+	}
+	ret = 0;
+out:
+	spin_unlock(&boot_lock);
+	return ret;
+}
+
+static void hip04_mcpm_powered_up(void)
+{
+	if (!relocation)
+		return;
+	spin_lock(&boot_lock);
+	writel_relaxed(0, relocation);
+	writel_relaxed(0, relocation + 4);
+	writel_relaxed(0, relocation + 8);
+	writel_relaxed(0, relocation + 12);
+	spin_unlock(&boot_lock);
+}
+
+static const struct mcpm_platform_ops hip04_mcpm_ops = {
+	.power_up		= hip04_mcpm_power_up,
+	.power_down		= hip04_mcpm_power_down,
+	.power_down_finish	= hip04_mcpm_power_down_finish,
+	.powered_up		= hip04_mcpm_powered_up,
+};
+
+static bool __init hip04_cpu_table_init(void)
+{
+	unsigned int mpidr, cpu, cluster;
+
+	mpidr = read_cpuid_mpidr();
+	cpu = MPIDR_AFFINITY_LEVEL(mpidr, 0);
+	cluster = MPIDR_AFFINITY_LEVEL(mpidr, 1);
+
+	if (cluster >= HIP04_MAX_CLUSTERS ||
+	    cpu >= HIP04_MAX_CPUS_PER_CLUSTER) {
+		pr_err("%s: boot CPU is out of bound!\n", __func__);
+		return false;
+	}
+	hip04_set_snoop_filter(cluster, 1);
+	hip04_cpu_table[cluster][cpu] = 1;
+	return true;
+}
+
+static int __init hip04_mcpm_init(void)
+{
+	struct device_node *np;
+	int ret = -ENODEV;
+
+	np = of_find_compatible_node(NULL, NULL, "hisilicon,hip04-mcpm");
+	if (!np) {
+		pr_err("failed to find hisilicon,hip04-mcpm node\n");
+		goto err;
+	}
+	relocation = of_iomap(np, 0);
+	if (!relocation) {
+		pr_err("failed to get relocation space\n");
+		ret = -ENOMEM;
+		goto err;
+	}
+	sysctrl = of_iomap(np, 1);
+	if (!sysctrl) {
+		pr_err("failed to get sysctrl base\n");
+		ret = -ENOMEM;
+		goto err_sysctrl;
+	}
+	fabric = of_iomap(np, 2);
+	if (!fabric) {
+		pr_err("failed to get fabric base\n");
+		ret = -ENOMEM;
+		goto err_fabric;
+	}
+	if (!hip04_cpu_table_init())
+		return -EINVAL;
+	ret = mcpm_platform_register(&hip04_mcpm_ops);
+	if (!ret) {
+		mcpm_sync_init(NULL);
+		pr_info("HiP04 MCPM initialized\n");
+	}
+	return ret;
+err_fabric:
+	iounmap(sysctrl);
+err_sysctrl:
+	iounmap(relocation);
+err:
+	return ret;
+}
+early_initcall(hip04_mcpm_init);
+
+bool __init hip04_smp_init_ops(void)
+{
+	mcpm_smp_set_ops();
+	return true;
+}
+#endif	/* CONFIG_MCPM */