blob: b25cf3d60fbb869231622e220044af061b58c99f [file] [log] [blame]
/*
* (Hisilicon's HiP04 SoC) flattened device tree enabled machine
*
* Copyright (c) 2013-2014 Hisilicon Ltd.
* Copyright (c) 2013-2014 Linaro Ltd.
*
* Author: Haojian Zhuang <haojian.zhuang@linaro.org>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#include <linux/ahci_platform.h>
#include <linux/delay.h>
#include <linux/io.h>
#include <linux/irqchip/arm-gic.h>
#include <linux/memblock.h>
#include <linux/of_address.h>
#include <linux/of_platform.h>
#include <linux/slab.h>
#include <asm/cp15.h>
#include <asm/cputype.h>
#include <asm/mcpm.h>
#include <asm/mach/arch.h>
#include <asm/mach/map.h>
#define BOOTWRAPPER_PHYS 0x10c00000
#define BOOTWRAPPER_MAGIC 0xa5a5a5a5
#define BOOTWRAPPER_SIZE 0x00010000
/* 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 = NULL, *sysctrl = NULL, *fabric = NULL;
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(BOOTWRAPPER_PHYS, relocation);
writel_relaxed(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;
}
#define HIP04_SATA_BASE (0xea000000)
static int sata_vsemiphy_init(struct device *dev, void __iomem *addr)
{
return 0;
}
static struct ahci_platform_data hip04_sata_pdata = {
.init = sata_vsemiphy_init,
};
static struct of_dev_auxdata hip04_auxdata_lookup[] __initdata = {
OF_DEV_AUXDATA("hisilicon,hisi-ahci", HIP04_SATA_BASE,
NULL, &hip04_sata_pdata),
{},
};
static void __init hip04_init_machine(void)
{
of_platform_populate(NULL, of_default_bus_match_table,
hip04_auxdata_lookup, NULL);
}
static const char *hip04_compat[] __initconst = {
"hisilicon,hip04-d01",
NULL,
};
static void __init hip04_reserve(void)
{
memblock_reserve(BOOTWRAPPER_PHYS, BOOTWRAPPER_SIZE);
}
DT_MACHINE_START(HIP01, "Hisilicon HiP04 (Flattened Device Tree)")
.dt_compat = hip04_compat,
.smp_init = smp_init_ops(hip04_smp_init_ops),
.init_machine = hip04_init_machine,
.reserve = hip04_reserve,
MACHINE_END