2010-02-22 09:46:23 +08:00
|
|
|
/*
|
|
|
|
* linux/arch/arm/mach-tegra/platsmp.c
|
|
|
|
*
|
|
|
|
* Copyright (C) 2002 ARM Ltd.
|
|
|
|
* All Rights Reserved
|
|
|
|
*
|
|
|
|
* Copyright (C) 2009 Palm
|
|
|
|
* All Rights Reserved
|
|
|
|
*
|
|
|
|
* 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/init.h>
|
|
|
|
#include <linux/errno.h>
|
|
|
|
#include <linux/delay.h>
|
|
|
|
#include <linux/device.h>
|
|
|
|
#include <linux/jiffies.h>
|
|
|
|
#include <linux/smp.h>
|
|
|
|
#include <linux/io.h>
|
|
|
|
|
|
|
|
#include <asm/cacheflush.h>
|
2011-04-03 20:01:30 +08:00
|
|
|
#include <asm/hardware/gic.h>
|
2010-02-22 09:46:23 +08:00
|
|
|
#include <asm/mach-types.h>
|
|
|
|
#include <asm/smp_scu.h>
|
|
|
|
|
2012-02-10 07:47:50 +08:00
|
|
|
#include <mach/clk.h>
|
2010-02-22 09:46:23 +08:00
|
|
|
#include <mach/iomap.h>
|
2012-02-10 07:47:50 +08:00
|
|
|
#include <mach/powergate.h>
|
2010-02-22 09:46:23 +08:00
|
|
|
|
2012-02-10 07:47:45 +08:00
|
|
|
#include "fuse.h"
|
|
|
|
#include "flowctrl.h"
|
|
|
|
#include "reset.h"
|
|
|
|
|
2010-02-22 09:46:23 +08:00
|
|
|
extern void tegra_secondary_startup(void);
|
|
|
|
|
|
|
|
static void __iomem *scu_base = IO_ADDRESS(TEGRA_ARM_PERIF_BASE);
|
|
|
|
|
|
|
|
#define EVP_CPU_RESET_VECTOR \
|
|
|
|
(IO_ADDRESS(TEGRA_EXCEPTION_VECTORS_BASE) + 0x100)
|
|
|
|
#define CLK_RST_CONTROLLER_CLK_CPU_CMPLX \
|
|
|
|
(IO_ADDRESS(TEGRA_CLK_RESET_BASE) + 0x4c)
|
2012-02-10 07:47:45 +08:00
|
|
|
#define CLK_RST_CONTROLLER_RST_CPU_CMPLX_SET \
|
|
|
|
(IO_ADDRESS(TEGRA_CLK_RESET_BASE) + 0x340)
|
2010-02-22 09:46:23 +08:00
|
|
|
#define CLK_RST_CONTROLLER_RST_CPU_CMPLX_CLR \
|
|
|
|
(IO_ADDRESS(TEGRA_CLK_RESET_BASE) + 0x344)
|
2012-02-10 07:47:50 +08:00
|
|
|
#define CLK_RST_CONTROLLER_CLK_CPU_CMPLX_CLR \
|
|
|
|
(IO_ADDRESS(TEGRA_CLK_RESET_BASE) + 0x34c)
|
2010-02-22 09:46:23 +08:00
|
|
|
|
2012-02-10 07:47:45 +08:00
|
|
|
#define CPU_CLOCK(cpu) (0x1<<(8+cpu))
|
|
|
|
#define CPU_RESET(cpu) (0x1111ul<<(cpu))
|
|
|
|
|
2010-02-22 09:46:23 +08:00
|
|
|
void __cpuinit platform_secondary_init(unsigned int cpu)
|
|
|
|
{
|
|
|
|
/*
|
|
|
|
* if any interrupts are already enabled for the primary
|
|
|
|
* core (e.g. timer irq), then they will not have been enabled
|
|
|
|
* for us: do so
|
|
|
|
*/
|
2010-12-05 00:01:03 +08:00
|
|
|
gic_secondary_init(0);
|
2010-02-22 09:46:23 +08:00
|
|
|
|
|
|
|
}
|
|
|
|
|
2012-02-10 07:47:45 +08:00
|
|
|
static int tegra20_power_up_cpu(unsigned int cpu)
|
2010-02-22 09:46:23 +08:00
|
|
|
{
|
|
|
|
u32 reg;
|
|
|
|
|
2012-02-10 07:47:45 +08:00
|
|
|
/* Enable the CPU clock. */
|
|
|
|
reg = readl(CLK_RST_CONTROLLER_CLK_CPU_CMPLX);
|
|
|
|
writel(reg & ~CPU_CLOCK(cpu), CLK_RST_CONTROLLER_CLK_CPU_CMPLX);
|
|
|
|
barrier();
|
2010-02-22 09:46:23 +08:00
|
|
|
reg = readl(CLK_RST_CONTROLLER_CLK_CPU_CMPLX);
|
|
|
|
|
2012-02-10 07:47:45 +08:00
|
|
|
/* Clear flow controller CSR. */
|
|
|
|
flowctrl_write_cpu_csr(cpu, 0);
|
2010-02-22 09:46:23 +08:00
|
|
|
|
2012-02-10 07:47:45 +08:00
|
|
|
return 0;
|
|
|
|
}
|
2010-02-22 09:46:23 +08:00
|
|
|
|
2012-02-10 07:47:50 +08:00
|
|
|
static int tegra30_power_up_cpu(unsigned int cpu)
|
|
|
|
{
|
|
|
|
u32 reg;
|
|
|
|
int ret, pwrgateid;
|
|
|
|
unsigned long timeout;
|
|
|
|
|
|
|
|
pwrgateid = tegra_cpu_powergate_id(cpu);
|
|
|
|
if (pwrgateid < 0)
|
|
|
|
return pwrgateid;
|
|
|
|
|
|
|
|
/* If this is the first boot, toggle powergates directly. */
|
|
|
|
if (!tegra_powergate_is_powered(pwrgateid)) {
|
|
|
|
ret = tegra_powergate_power_on(pwrgateid);
|
|
|
|
if (ret)
|
|
|
|
return ret;
|
|
|
|
|
|
|
|
/* Wait for the power to come up. */
|
|
|
|
timeout = jiffies + 10*HZ;
|
|
|
|
while (tegra_powergate_is_powered(pwrgateid)) {
|
|
|
|
if (time_after(jiffies, timeout))
|
|
|
|
return -ETIMEDOUT;
|
|
|
|
udelay(10);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* CPU partition is powered. Enable the CPU clock. */
|
|
|
|
writel(CPU_CLOCK(cpu), CLK_RST_CONTROLLER_CLK_CPU_CMPLX_CLR);
|
|
|
|
reg = readl(CLK_RST_CONTROLLER_CLK_CPU_CMPLX_CLR);
|
|
|
|
udelay(10);
|
|
|
|
|
|
|
|
/* Remove I/O clamps. */
|
|
|
|
ret = tegra_powergate_remove_clamping(pwrgateid);
|
|
|
|
udelay(10);
|
|
|
|
|
|
|
|
/* Clear flow controller CSR. */
|
|
|
|
flowctrl_write_cpu_csr(cpu, 0);
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2012-02-10 07:47:45 +08:00
|
|
|
int __cpuinit boot_secondary(unsigned int cpu, struct task_struct *idle)
|
|
|
|
{
|
|
|
|
int status;
|
2010-02-22 09:46:23 +08:00
|
|
|
|
2012-02-10 07:47:50 +08:00
|
|
|
/*
|
|
|
|
* Force the CPU into reset. The CPU must remain in reset when the
|
2012-02-10 07:47:45 +08:00
|
|
|
* flow controller state is cleared (which will cause the flow
|
|
|
|
* controller to stop driving reset if the CPU has been power-gated
|
|
|
|
* via the flow controller). This will have no effect on first boot
|
|
|
|
* of the CPU since it should already be in reset.
|
|
|
|
*/
|
|
|
|
writel(CPU_RESET(cpu), CLK_RST_CONTROLLER_RST_CPU_CMPLX_SET);
|
|
|
|
dmb();
|
2010-02-22 09:46:23 +08:00
|
|
|
|
|
|
|
/*
|
2012-02-10 07:47:45 +08:00
|
|
|
* Unhalt the CPU. If the flow controller was used to power-gate the
|
|
|
|
* CPU this will cause the flow controller to stop driving reset.
|
|
|
|
* The CPU will remain in reset because the clock and reset block
|
|
|
|
* is now driving reset.
|
2010-02-22 09:46:23 +08:00
|
|
|
*/
|
2012-02-10 07:47:45 +08:00
|
|
|
flowctrl_write_cpu_halt(cpu, 0);
|
|
|
|
|
|
|
|
switch (tegra_chip_id) {
|
|
|
|
case TEGRA20:
|
|
|
|
status = tegra20_power_up_cpu(cpu);
|
|
|
|
break;
|
2012-02-10 07:47:50 +08:00
|
|
|
case TEGRA30:
|
|
|
|
status = tegra30_power_up_cpu(cpu);
|
|
|
|
break;
|
2012-02-10 07:47:45 +08:00
|
|
|
default:
|
|
|
|
status = -EINVAL;
|
|
|
|
break;
|
|
|
|
}
|
2010-02-22 09:46:23 +08:00
|
|
|
|
2012-02-10 07:47:45 +08:00
|
|
|
if (status)
|
|
|
|
goto done;
|
|
|
|
|
|
|
|
/* Take the CPU out of reset. */
|
|
|
|
writel(CPU_RESET(cpu), CLK_RST_CONTROLLER_RST_CPU_CMPLX_CLR);
|
|
|
|
wmb();
|
|
|
|
done:
|
|
|
|
return status;
|
2010-02-22 09:46:23 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Initialise the CPU possible map early - this describes the CPUs
|
|
|
|
* which may be present or become present in the system.
|
|
|
|
*/
|
|
|
|
void __init smp_init_cpus(void)
|
|
|
|
{
|
|
|
|
unsigned int i, ncores = scu_get_core_count(scu_base);
|
|
|
|
|
2011-10-21 05:04:18 +08:00
|
|
|
if (ncores > nr_cpu_ids) {
|
|
|
|
pr_warn("SMP: %u cores greater than maximum (%u), clipping\n",
|
|
|
|
ncores, nr_cpu_ids);
|
|
|
|
ncores = nr_cpu_ids;
|
2010-12-04 03:29:53 +08:00
|
|
|
}
|
|
|
|
|
2010-02-22 09:46:23 +08:00
|
|
|
for (i = 0; i < ncores; i++)
|
2011-06-23 16:28:28 +08:00
|
|
|
set_cpu_possible(i, true);
|
2011-04-03 20:01:30 +08:00
|
|
|
|
|
|
|
set_smp_cross_call(gic_raise_softirq);
|
2010-02-22 09:46:23 +08:00
|
|
|
}
|
|
|
|
|
2010-12-03 19:09:48 +08:00
|
|
|
void __init platform_smp_prepare_cpus(unsigned int max_cpus)
|
2010-02-22 09:46:23 +08:00
|
|
|
{
|
2012-02-10 07:47:45 +08:00
|
|
|
tegra_cpu_reset_handler_init();
|
2010-12-03 19:09:48 +08:00
|
|
|
scu_enable(scu_base);
|
2010-02-22 09:46:23 +08:00
|
|
|
}
|