linux/arch/arm/mach-actions/platsmp.c
Thomas Gleixner 2874c5fd28 treewide: Replace GPLv2 boilerplate/reference with SPDX - rule 152
Based on 1 normalized pattern(s):

  this program is free software you can redistribute it and or modify
  it under the terms of the gnu general public license as published by
  the free software foundation either version 2 of the license or at
  your option any later version

extracted by the scancode license scanner the SPDX license identifier

  GPL-2.0-or-later

has been chosen to replace the boilerplate/reference in 3029 file(s).

Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
Reviewed-by: Allison Randal <allison@lohutok.net>
Cc: linux-spdx@vger.kernel.org
Link: https://lkml.kernel.org/r/20190527070032.746973796@linutronix.de
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
2019-05-30 11:26:32 -07:00

153 lines
3.4 KiB
C

// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Actions Semi Leopard
*
* This file is based on arm realview smp platform.
*
* Copyright 2012 Actions Semi Inc.
* Author: Actions Semi, Inc.
*
* Copyright (c) 2017 Andreas Färber
*/
#include <linux/delay.h>
#include <linux/io.h>
#include <linux/of.h>
#include <linux/of_address.h>
#include <linux/smp.h>
#include <linux/soc/actions/owl-sps.h>
#include <asm/cacheflush.h>
#include <asm/smp_plat.h>
#include <asm/smp_scu.h>
#define OWL_CPU1_ADDR 0x50
#define OWL_CPU1_FLAG 0x5c
#define OWL_CPUx_FLAG_BOOT 0x55aa
#define OWL_SPS_PG_CTL_PWR_CPU2 BIT(5)
#define OWL_SPS_PG_CTL_PWR_CPU3 BIT(6)
#define OWL_SPS_PG_CTL_ACK_CPU2 BIT(21)
#define OWL_SPS_PG_CTL_ACK_CPU3 BIT(22)
static void __iomem *scu_base_addr;
static void __iomem *sps_base_addr;
static void __iomem *timer_base_addr;
static int ncores;
static int s500_wakeup_secondary(unsigned int cpu)
{
int ret;
if (cpu > 3)
return -EINVAL;
/* The generic PM domain driver is not available this early. */
switch (cpu) {
case 2:
ret = owl_sps_set_pg(sps_base_addr,
OWL_SPS_PG_CTL_PWR_CPU2,
OWL_SPS_PG_CTL_ACK_CPU2, true);
if (ret)
return ret;
break;
case 3:
ret = owl_sps_set_pg(sps_base_addr,
OWL_SPS_PG_CTL_PWR_CPU3,
OWL_SPS_PG_CTL_ACK_CPU3, true);
if (ret)
return ret;
break;
}
/* wait for CPUx to run to WFE instruction */
udelay(200);
writel(__pa_symbol(secondary_startup),
timer_base_addr + OWL_CPU1_ADDR + (cpu - 1) * 4);
writel(OWL_CPUx_FLAG_BOOT,
timer_base_addr + OWL_CPU1_FLAG + (cpu - 1) * 4);
dsb_sev();
mb();
return 0;
}
static int s500_smp_boot_secondary(unsigned int cpu, struct task_struct *idle)
{
int ret;
ret = s500_wakeup_secondary(cpu);
if (ret)
return ret;
udelay(10);
smp_send_reschedule(cpu);
writel(0, timer_base_addr + OWL_CPU1_ADDR + (cpu - 1) * 4);
writel(0, timer_base_addr + OWL_CPU1_FLAG + (cpu - 1) * 4);
return 0;
}
static void __init s500_smp_prepare_cpus(unsigned int max_cpus)
{
struct device_node *node;
node = of_find_compatible_node(NULL, NULL, "actions,s500-timer");
if (!node) {
pr_err("%s: missing timer\n", __func__);
return;
}
timer_base_addr = of_iomap(node, 0);
if (!timer_base_addr) {
pr_err("%s: could not map timer registers\n", __func__);
return;
}
node = of_find_compatible_node(NULL, NULL, "actions,s500-sps");
if (!node) {
pr_err("%s: missing sps\n", __func__);
return;
}
sps_base_addr = of_iomap(node, 0);
if (!sps_base_addr) {
pr_err("%s: could not map sps registers\n", __func__);
return;
}
if (read_cpuid_part() == ARM_CPU_PART_CORTEX_A9) {
node = of_find_compatible_node(NULL, NULL, "arm,cortex-a9-scu");
if (!node) {
pr_err("%s: missing scu\n", __func__);
return;
}
scu_base_addr = of_iomap(node, 0);
if (!scu_base_addr) {
pr_err("%s: could not map scu registers\n", __func__);
return;
}
/*
* While the number of cpus is gathered from dt, also get the
* number of cores from the scu to verify this value when
* booting the cores.
*/
ncores = scu_get_core_count(scu_base_addr);
pr_debug("%s: ncores %d\n", __func__, ncores);
scu_enable(scu_base_addr);
}
}
static const struct smp_operations s500_smp_ops __initconst = {
.smp_prepare_cpus = s500_smp_prepare_cpus,
.smp_boot_secondary = s500_smp_boot_secondary,
};
CPU_METHOD_OF_DECLARE(s500_smp, "actions,s500-smp", &s500_smp_ops);