linux/arch/arm/mach-actions/platsmp.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0-or-later
   2/*
   3 * Actions Semi Leopard
   4 *
   5 * This file is based on arm realview smp platform.
   6 *
   7 * Copyright 2012 Actions Semi Inc.
   8 * Author: Actions Semi, Inc.
   9 *
  10 * Copyright (c) 2017 Andreas F\xC3\xA4rber
  11 */
  12
  13#include <linux/delay.h>
  14#include <linux/io.h>
  15#include <linux/of.h>
  16#include <linux/of_address.h>
  17#include <linux/smp.h>
  18#include <linux/soc/actions/owl-sps.h>
  19#include <asm/cacheflush.h>
  20#include <asm/smp_plat.h>
  21#include <asm/smp_scu.h>
  22
  23#define OWL_CPU1_ADDR   0x50
  24#define OWL_CPU1_FLAG   0x5c
  25
  26#define OWL_CPUx_FLAG_BOOT      0x55aa
  27
  28#define OWL_SPS_PG_CTL_PWR_CPU2 BIT(5)
  29#define OWL_SPS_PG_CTL_PWR_CPU3 BIT(6)
  30#define OWL_SPS_PG_CTL_ACK_CPU2 BIT(21)
  31#define OWL_SPS_PG_CTL_ACK_CPU3 BIT(22)
  32
  33static void __iomem *scu_base_addr;
  34static void __iomem *sps_base_addr;
  35static void __iomem *timer_base_addr;
  36static int ncores;
  37
  38static int s500_wakeup_secondary(unsigned int cpu)
  39{
  40        int ret;
  41
  42        if (cpu > 3)
  43                return -EINVAL;
  44
  45        /* The generic PM domain driver is not available this early. */
  46        switch (cpu) {
  47        case 2:
  48                ret = owl_sps_set_pg(sps_base_addr,
  49                                     OWL_SPS_PG_CTL_PWR_CPU2,
  50                                     OWL_SPS_PG_CTL_ACK_CPU2, true);
  51                if (ret)
  52                        return ret;
  53                break;
  54        case 3:
  55                ret = owl_sps_set_pg(sps_base_addr,
  56                                     OWL_SPS_PG_CTL_PWR_CPU3,
  57                                     OWL_SPS_PG_CTL_ACK_CPU3, true);
  58                if (ret)
  59                        return ret;
  60                break;
  61        }
  62
  63        /* wait for CPUx to run to WFE instruction */
  64        udelay(200);
  65
  66        writel(__pa_symbol(secondary_startup),
  67               timer_base_addr + OWL_CPU1_ADDR + (cpu - 1) * 4);
  68        writel(OWL_CPUx_FLAG_BOOT,
  69               timer_base_addr + OWL_CPU1_FLAG + (cpu - 1) * 4);
  70
  71        dsb_sev();
  72        mb();
  73
  74        return 0;
  75}
  76
  77static int s500_smp_boot_secondary(unsigned int cpu, struct task_struct *idle)
  78{
  79        int ret;
  80
  81        ret = s500_wakeup_secondary(cpu);
  82        if (ret)
  83                return ret;
  84
  85        udelay(10);
  86
  87        smp_send_reschedule(cpu);
  88
  89        writel(0, timer_base_addr + OWL_CPU1_ADDR + (cpu - 1) * 4);
  90        writel(0, timer_base_addr + OWL_CPU1_FLAG + (cpu - 1) * 4);
  91
  92        return 0;
  93}
  94
  95static void __init s500_smp_prepare_cpus(unsigned int max_cpus)
  96{
  97        struct device_node *node;
  98
  99        node = of_find_compatible_node(NULL, NULL, "actions,s500-timer");
 100        if (!node) {
 101                pr_err("%s: missing timer\n", __func__);
 102                return;
 103        }
 104
 105        timer_base_addr = of_iomap(node, 0);
 106        if (!timer_base_addr) {
 107                pr_err("%s: could not map timer registers\n", __func__);
 108                return;
 109        }
 110
 111        node = of_find_compatible_node(NULL, NULL, "actions,s500-sps");
 112        if (!node) {
 113                pr_err("%s: missing sps\n", __func__);
 114                return;
 115        }
 116
 117        sps_base_addr = of_iomap(node, 0);
 118        if (!sps_base_addr) {
 119                pr_err("%s: could not map sps registers\n", __func__);
 120                return;
 121        }
 122
 123        if (read_cpuid_part() == ARM_CPU_PART_CORTEX_A9) {
 124                node = of_find_compatible_node(NULL, NULL, "arm,cortex-a9-scu");
 125                if (!node) {
 126                        pr_err("%s: missing scu\n", __func__);
 127                        return;
 128                }
 129
 130                scu_base_addr = of_iomap(node, 0);
 131                if (!scu_base_addr) {
 132                        pr_err("%s: could not map scu registers\n", __func__);
 133                        return;
 134                }
 135
 136                /*
 137                 * While the number of cpus is gathered from dt, also get the
 138                 * number of cores from the scu to verify this value when
 139                 * booting the cores.
 140                 */
 141                ncores = scu_get_core_count(scu_base_addr);
 142                pr_debug("%s: ncores %d\n", __func__, ncores);
 143
 144                scu_enable(scu_base_addr);
 145        }
 146}
 147
 148static const struct smp_operations s500_smp_ops __initconst = {
 149        .smp_prepare_cpus = s500_smp_prepare_cpus,
 150        .smp_boot_secondary = s500_smp_boot_secondary,
 151};
 152CPU_METHOD_OF_DECLARE(s500_smp, "actions,s500-smp", &s500_smp_ops);
 153