forked from Minki/linux
47f92418c7
-----BEGIN PGP SIGNATURE-----
Version: GnuPG v1
iQIcBAABAgAGBQJVkO0aAAoJEOvOhAQsB9HWoRUQAII3ueftazj4ZYVEF5QBIdNL
MrEyUbPMyXHYBKmcAVkig5N4JLkITvPnuT/LRQ3ba7gMyeYKUFCHpLniRHwGkHIJ
UAHgDYzLXO7bv2ZhHiSk6ZL6XtEq2vY+dcedS26CAoR53erjmRiniakYhT/NbfSf
rFfPKaVOH7E7yGn7OrFAASGSPlKbzjICqfnUQQi5Z9XxAfdXcHLiuX98KufSaXbM
a27KDJudH11/guwCkACvD0C0NnUoRxe6X7/JyTmt/ldqH3f9JR2jEnDgrh/jakJE
LgsguiHGcDuZjC1n4jb5n72Iua0zOtyrKhvDAZ77bG7NEcK6OlvhY0No3rEvXA9X
XhScF3BUd1/pBKRwVALWTprHJ0q8HjQAfci1ONX9qMWNhjpY1jU6J8cUpAEKf8hk
nHSsSesvsXKLogTEY05vF4sOUupILi1zlP/1dGDLXr/FVWX52U4BFlur67Vay7pN
PtjvKl30Tw0yLOLkZwr53jKpqFZOgcTy+mm/DF3j2v77da1JXS94pCV3PcV8lNxT
xlQPB2O6LvYn5LPnyJ8SwuLm466+OD3jfLNBPgXVLgZb+lY/GpL/mHgxoK+/FweH
quHj3e7q3OWADxPEdnJswuCKpeG/w+jcpK7fWWo+4haC4bMcSDM42Dc78o9ve4rU
E9Ex9ymil7W4DmAWsd2g
=qNLm
-----END PGP SIGNATURE-----
Merge tag 'cpuinit-v4.1-rc8' of git://git.kernel.org/pub/scm/linux/kernel/git/paulg/linux
Pull __cpuinit removal from Paul Gortmaker:
"Remove __cpuinit macros and users.
We removed the __cpuinit stuff in 3.11-rc1 with commit 22f0a27367
("init.h: remove __cpuinit sections from the kernel") but we left some
no-op stubs as a courtesy to unmerged code.
Here we get rid of the stubs as well, since (as can be seen in these
changes) they are enabling use cases to sneak back in, primarily from
older BSP code that has been living out of tree for some time prior to
getting mainlined. So we get rid of these "new" users 1st and then
get rid of the stubs.
Obviously, getting rid of the stubs can't happen until all the users
are gone, so I had to keep this together as a series, even though some
of these commits since got picked up into maintainers trees as well.
The nature of this change is such that it should have zero impact on
the generated runtime.
This is one of several independent cleanup branches aimed at enabling
better organization in the init.h and module.h code. They have been
getting coverage in the linux-next tree for the last month, in
addition to my local testing, which also covers approximately a half
dozen or more architectures"
* tag 'cpuinit-v4.1-rc8' of git://git.kernel.org/pub/scm/linux/kernel/git/paulg/linux:
init: delete the __cpuinit related stubs
kernel/cpu.c: remove new instance of __cpuinit that crept back in
sched/core: remove __cpuinit section tag that crept back in.
mips/mm/tlbex: remove new instance of __cpuinit that crept back in
mips/c-r4k: remove legacy __cpuinit section that crept in
mips/bcm77xx: remove legacy __cpuinit sections that crept in
mips/ath25: remove legacy __cpuinit section that crept in
arm/mach-hisi: remove legacy __CPUINIT section that crept in
arm/mach-rockchip: remove legacy __cpuinit section that crept in
arm/mach-mvebu: remove legacy __cpuinit sections that crept in
arm/mach-keystone: remove legacy __cpuinit sections that crept in
341 lines
8.1 KiB
C
341 lines
8.1 KiB
C
/*
|
|
* Copyright (c) 2013 MundoReader S.L.
|
|
* Author: Heiko Stuebner <heiko@sntech.de>
|
|
*
|
|
* 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.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*/
|
|
|
|
#include <linux/delay.h>
|
|
#include <linux/init.h>
|
|
#include <linux/smp.h>
|
|
#include <linux/io.h>
|
|
#include <linux/of.h>
|
|
#include <linux/of_address.h>
|
|
#include <linux/regmap.h>
|
|
#include <linux/mfd/syscon.h>
|
|
|
|
#include <linux/reset.h>
|
|
#include <linux/cpu.h>
|
|
#include <asm/cacheflush.h>
|
|
#include <asm/cp15.h>
|
|
#include <asm/smp_scu.h>
|
|
#include <asm/smp_plat.h>
|
|
#include <asm/mach/map.h>
|
|
|
|
#include "core.h"
|
|
|
|
static void __iomem *scu_base_addr;
|
|
static void __iomem *sram_base_addr;
|
|
static int ncores;
|
|
|
|
#define PMU_PWRDN_CON 0x08
|
|
#define PMU_PWRDN_ST 0x0c
|
|
|
|
#define PMU_PWRDN_SCU 4
|
|
|
|
static struct regmap *pmu;
|
|
|
|
static int pmu_power_domain_is_on(int pd)
|
|
{
|
|
u32 val;
|
|
int ret;
|
|
|
|
ret = regmap_read(pmu, PMU_PWRDN_ST, &val);
|
|
if (ret < 0)
|
|
return ret;
|
|
|
|
return !(val & BIT(pd));
|
|
}
|
|
|
|
static struct reset_control *rockchip_get_core_reset(int cpu)
|
|
{
|
|
struct device *dev = get_cpu_device(cpu);
|
|
struct device_node *np;
|
|
|
|
/* The cpu device is only available after the initial core bringup */
|
|
if (dev)
|
|
np = dev->of_node;
|
|
else
|
|
np = of_get_cpu_node(cpu, 0);
|
|
|
|
return of_reset_control_get(np, NULL);
|
|
}
|
|
|
|
static int pmu_set_power_domain(int pd, bool on)
|
|
{
|
|
u32 val = (on) ? 0 : BIT(pd);
|
|
int ret;
|
|
|
|
/*
|
|
* We need to soft reset the cpu when we turn off the cpu power domain,
|
|
* or else the active processors might be stalled when the individual
|
|
* processor is powered down.
|
|
*/
|
|
if (read_cpuid_part() != ARM_CPU_PART_CORTEX_A9) {
|
|
struct reset_control *rstc = rockchip_get_core_reset(pd);
|
|
|
|
if (IS_ERR(rstc)) {
|
|
pr_err("%s: could not get reset control for core %d\n",
|
|
__func__, pd);
|
|
return PTR_ERR(rstc);
|
|
}
|
|
|
|
if (on)
|
|
reset_control_deassert(rstc);
|
|
else
|
|
reset_control_assert(rstc);
|
|
|
|
reset_control_put(rstc);
|
|
}
|
|
|
|
ret = regmap_update_bits(pmu, PMU_PWRDN_CON, BIT(pd), val);
|
|
if (ret < 0) {
|
|
pr_err("%s: could not update power domain\n", __func__);
|
|
return ret;
|
|
}
|
|
|
|
ret = -1;
|
|
while (ret != on) {
|
|
ret = pmu_power_domain_is_on(pd);
|
|
if (ret < 0) {
|
|
pr_err("%s: could not read power domain state\n",
|
|
__func__);
|
|
return ret;
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* Handling of CPU cores
|
|
*/
|
|
|
|
static int rockchip_boot_secondary(unsigned int cpu, struct task_struct *idle)
|
|
{
|
|
int ret;
|
|
|
|
if (!sram_base_addr || !pmu) {
|
|
pr_err("%s: sram or pmu missing for cpu boot\n", __func__);
|
|
return -ENXIO;
|
|
}
|
|
|
|
if (cpu >= ncores) {
|
|
pr_err("%s: cpu %d outside maximum number of cpus %d\n",
|
|
__func__, cpu, ncores);
|
|
return -ENXIO;
|
|
}
|
|
|
|
/* start the core */
|
|
ret = pmu_set_power_domain(0 + cpu, true);
|
|
if (ret < 0)
|
|
return ret;
|
|
|
|
if (read_cpuid_part() != ARM_CPU_PART_CORTEX_A9) {
|
|
/* We communicate with the bootrom to active the cpus other
|
|
* than cpu0, after a blob of initialize code, they will
|
|
* stay at wfe state, once they are actived, they will check
|
|
* the mailbox:
|
|
* sram_base_addr + 4: 0xdeadbeaf
|
|
* sram_base_addr + 8: start address for pc
|
|
* */
|
|
udelay(10);
|
|
writel(virt_to_phys(secondary_startup), sram_base_addr + 8);
|
|
writel(0xDEADBEAF, sram_base_addr + 4);
|
|
dsb_sev();
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* rockchip_smp_prepare_sram - populate necessary sram block
|
|
* Starting cores execute the code residing at the start of the on-chip sram
|
|
* after power-on. Therefore make sure, this sram region is reserved and
|
|
* big enough. After this check, copy the trampoline code that directs the
|
|
* core to the real startup code in ram into the sram-region.
|
|
* @node: mmio-sram device node
|
|
*/
|
|
static int __init rockchip_smp_prepare_sram(struct device_node *node)
|
|
{
|
|
unsigned int trampoline_sz = &rockchip_secondary_trampoline_end -
|
|
&rockchip_secondary_trampoline;
|
|
struct resource res;
|
|
unsigned int rsize;
|
|
int ret;
|
|
|
|
ret = of_address_to_resource(node, 0, &res);
|
|
if (ret < 0) {
|
|
pr_err("%s: could not get address for node %s\n",
|
|
__func__, node->full_name);
|
|
return ret;
|
|
}
|
|
|
|
rsize = resource_size(&res);
|
|
if (rsize < trampoline_sz) {
|
|
pr_err("%s: reserved block with size 0x%x is to small for trampoline size 0x%x\n",
|
|
__func__, rsize, trampoline_sz);
|
|
return -EINVAL;
|
|
}
|
|
|
|
/* set the boot function for the sram code */
|
|
rockchip_boot_fn = virt_to_phys(secondary_startup);
|
|
|
|
/* copy the trampoline to sram, that runs during startup of the core */
|
|
memcpy(sram_base_addr, &rockchip_secondary_trampoline, trampoline_sz);
|
|
flush_cache_all();
|
|
outer_clean_range(0, trampoline_sz);
|
|
|
|
dsb_sev();
|
|
|
|
return 0;
|
|
}
|
|
|
|
static const struct regmap_config rockchip_pmu_regmap_config = {
|
|
.reg_bits = 32,
|
|
.val_bits = 32,
|
|
.reg_stride = 4,
|
|
};
|
|
|
|
static int __init rockchip_smp_prepare_pmu(void)
|
|
{
|
|
struct device_node *node;
|
|
void __iomem *pmu_base;
|
|
|
|
/*
|
|
* This function is only called via smp_ops->smp_prepare_cpu().
|
|
* That only happens if a "/cpus" device tree node exists
|
|
* and has an "enable-method" property that selects the SMP
|
|
* operations defined herein.
|
|
*/
|
|
node = of_find_node_by_path("/cpus");
|
|
|
|
pmu = syscon_regmap_lookup_by_phandle(node, "rockchip,pmu");
|
|
of_node_put(node);
|
|
if (!IS_ERR(pmu))
|
|
return 0;
|
|
|
|
pmu = syscon_regmap_lookup_by_compatible("rockchip,rk3066-pmu");
|
|
if (!IS_ERR(pmu))
|
|
return 0;
|
|
|
|
/* fallback, create our own regmap for the pmu area */
|
|
pmu = NULL;
|
|
node = of_find_compatible_node(NULL, NULL, "rockchip,rk3066-pmu");
|
|
if (!node) {
|
|
pr_err("%s: could not find pmu dt node\n", __func__);
|
|
return -ENODEV;
|
|
}
|
|
|
|
pmu_base = of_iomap(node, 0);
|
|
if (!pmu_base) {
|
|
pr_err("%s: could not map pmu registers\n", __func__);
|
|
return -ENOMEM;
|
|
}
|
|
|
|
pmu = regmap_init_mmio(NULL, pmu_base, &rockchip_pmu_regmap_config);
|
|
if (IS_ERR(pmu)) {
|
|
int ret = PTR_ERR(pmu);
|
|
|
|
iounmap(pmu_base);
|
|
pmu = NULL;
|
|
pr_err("%s: regmap init failed\n", __func__);
|
|
return ret;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static void __init rockchip_smp_prepare_cpus(unsigned int max_cpus)
|
|
{
|
|
struct device_node *node;
|
|
unsigned int i;
|
|
|
|
node = of_find_compatible_node(NULL, NULL, "rockchip,rk3066-smp-sram");
|
|
if (!node) {
|
|
pr_err("%s: could not find sram dt node\n", __func__);
|
|
return;
|
|
}
|
|
|
|
sram_base_addr = of_iomap(node, 0);
|
|
if (!sram_base_addr) {
|
|
pr_err("%s: could not map sram registers\n", __func__);
|
|
return;
|
|
}
|
|
|
|
if (rockchip_smp_prepare_pmu())
|
|
return;
|
|
|
|
if (read_cpuid_part() == ARM_CPU_PART_CORTEX_A9) {
|
|
if (rockchip_smp_prepare_sram(node))
|
|
return;
|
|
|
|
/* enable the SCU power domain */
|
|
pmu_set_power_domain(PMU_PWRDN_SCU, true);
|
|
|
|
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_err("%s: ncores %d\n", __func__, ncores);
|
|
|
|
scu_enable(scu_base_addr);
|
|
} else {
|
|
unsigned int l2ctlr;
|
|
|
|
asm ("mrc p15, 1, %0, c9, c0, 2\n" : "=r" (l2ctlr));
|
|
ncores = ((l2ctlr >> 24) & 0x3) + 1;
|
|
}
|
|
|
|
/* Make sure that all cores except the first are really off */
|
|
for (i = 1; i < ncores; i++)
|
|
pmu_set_power_domain(0 + i, false);
|
|
}
|
|
|
|
#ifdef CONFIG_HOTPLUG_CPU
|
|
static int rockchip_cpu_kill(unsigned int cpu)
|
|
{
|
|
pmu_set_power_domain(0 + cpu, false);
|
|
return 1;
|
|
}
|
|
|
|
static void rockchip_cpu_die(unsigned int cpu)
|
|
{
|
|
v7_exit_coherency_flush(louis);
|
|
while(1)
|
|
cpu_do_idle();
|
|
}
|
|
#endif
|
|
|
|
static struct smp_operations rockchip_smp_ops __initdata = {
|
|
.smp_prepare_cpus = rockchip_smp_prepare_cpus,
|
|
.smp_boot_secondary = rockchip_boot_secondary,
|
|
#ifdef CONFIG_HOTPLUG_CPU
|
|
.cpu_kill = rockchip_cpu_kill,
|
|
.cpu_die = rockchip_cpu_die,
|
|
#endif
|
|
};
|
|
CPU_METHOD_OF_DECLARE(rk3066_smp, "rockchip,rk3066-smp", &rockchip_smp_ops);
|