bda6f8e6cd
Tegra already supports the common clock framework, but had issues: 1) The clock driver was located in arch/arm/mach-tegra/ rather than drivers/clk/. 2) A single "Tegra clock" type was implemented, rather than separate clock types for PLL, mux, divider, ... type in HW. 3) Clock lookups by device drivers were still driven by device name and connection ID, rather than through device tree. This pull request solves all three issues. This required some DT changes to add clocks properties, and driver changes to request clocks more "correctly". Finally, this rework allows all AUXDATA to be removed from Tegra board files, and various duplicate clock lookup entries to be removed from the driver. This pull request is based on the previous pull request, with tag tegra-for-3.9-cleanup. -----BEGIN PGP SIGNATURE----- Version: GnuPG v1.4.11 (GNU/Linux) iQIcBAABAgAGBQJRCYtBAAoJEMzrak5tbycxKb8P/0cXt2X7mPfoApWV96bI2c9h VE1wZYREcq0Au3hiNuMmPp1Nwous2zvrXRKXMvLoQi42KwpvZlFjlyn8+xACKmxO okSJ+aXETzlGh85l5RlnFJMgq181Kn0nDhN5Iwy0FUEJ8/oqdS8fEz5mwQlHflX1 CLaquDVr/edr8LffvsFlxtSmeYNvZ2jYkSgroWeDhVR5Np1/LUCyh5y3edjVl/es B0/keuZ2fnYZnEfqLTpBEARYDBimymuu8gIoHK5nvtz3d/GGu92sVeda4LuHt8eH 1N+f41ceDR2JG/MIJbLr6PGYmCkAGSM/5Vcfa33G+A7GQT0EVb8jLozGCdrCjaEG OM33pN5wtv1M9gTLR9swITBWhbTpRWaHnXeZQF7ttaV8dvr/fuOzWBw47k8Jw0FJ zjGta66kwW7WkT3HDNoM2RRzm9dlJr1xdHOzAaVJnX3VHtHcIvYzDi90Xv9Nn46D E/qIpExmL4rMrb2+4MxT9CdbfzdBSmsnlRFoWZTIM1NPxA/97i7oAyYVAJ34LCNx xWqwimhXK14LzGffpSHm9CSz8DHNbehDZRMQD0jGYMn61PFtDB+E/oEq5AEqneuC KDht3Qdx/mPzJQPE8WV3d5FxeXfXDjj203x/i6x8TOdH8Bt4aoK9ajvPYBpA+2aE 4fPJIobLHGYN/F+GF1VJ =s9hz -----END PGP SIGNATURE----- Merge tag 'tegra-for-3.9-soc-ccf' of git://git.kernel.org/pub/scm/linux/kernel/git/swarren/linux-tegra into next/soc From Stephen Warren: ARM: tegra: Common Clock Framework rework Tegra already supports the common clock framework, but had issues: 1) The clock driver was located in arch/arm/mach-tegra/ rather than drivers/clk/. 2) A single "Tegra clock" type was implemented, rather than separate clock types for PLL, mux, divider, ... type in HW. 3) Clock lookups by device drivers were still driven by device name and connection ID, rather than through device tree. This pull request solves all three issues. This required some DT changes to add clocks properties, and driver changes to request clocks more "correctly". Finally, this rework allows all AUXDATA to be removed from Tegra board files, and various duplicate clock lookup entries to be removed from the driver. This pull request is based on the previous pull request, with tag tegra-for-3.9-cleanup. * tag 'tegra-for-3.9-soc-ccf' of git://git.kernel.org/pub/scm/linux/kernel/git/swarren/linux-tegra: (31 commits) clk: tegra30: remove unused TEGRA_CLK_DUPLICATE()s clk: tegra20: remove unused TEGRA_CLK_DUPLICATE()s ARM: tegra30: remove auxdata ARM: tegra20: remove auxdata ASoC: tegra: remove auxdata staging: nvec: remove use of clk_get_sys ARM: tegra: paz00: add clock information to DT ARM: tegra: add clock properties to Tegra30 DT ARM: tegra: add clock properties to Tegra20 DT spi: tegra: do not use clock name to get clock ARM: tegra: remove legacy clock code ARM: tegra: migrate to new clock code clk: tegra: add clock support for Tegra30 clk: tegra: add clock support for Tegra20 clk: tegra: add Tegra specific clocks ARM: tegra: define Tegra30 CAR binding ARM: tegra: define Tegra20 CAR binding ARM: tegra: move tegra_cpu_car.h to linux/clk/tegra.h ARM: tegra: add function to read chipid ARM: tegra: fix compile error when disable CPU_IDLE ... Signed-off-by: Olof Johansson <olof@lixom.net> Conflicts: arch/arm/mach-tegra/board-dt-tegra20.c arch/arm/mach-tegra/board-dt-tegra30.c arch/arm/mach-tegra/common.c arch/arm/mach-tegra/platsmp.c drivers/clocksource/Makefile
217 lines
5.3 KiB
C
217 lines
5.3 KiB
C
/*
|
|
* 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 <linux/irqchip/arm-gic.h>
|
|
#include <linux/clk/tegra.h>
|
|
|
|
#include <asm/cacheflush.h>
|
|
#include <asm/mach-types.h>
|
|
#include <asm/smp_scu.h>
|
|
#include <asm/smp_plat.h>
|
|
|
|
#include <mach/powergate.h>
|
|
|
|
#include "fuse.h"
|
|
#include "flowctrl.h"
|
|
#include "reset.h"
|
|
|
|
#include "common.h"
|
|
#include "iomap.h"
|
|
|
|
extern void tegra_secondary_startup(void);
|
|
|
|
static cpumask_t tegra_cpu_init_mask;
|
|
static void __iomem *scu_base = IO_ADDRESS(TEGRA_ARM_PERIF_BASE);
|
|
|
|
#define EVP_CPU_RESET_VECTOR \
|
|
(IO_ADDRESS(TEGRA_EXCEPTION_VECTORS_BASE) + 0x100)
|
|
|
|
static void __cpuinit tegra_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
|
|
*/
|
|
gic_secondary_init(0);
|
|
|
|
cpumask_set_cpu(cpu, &tegra_cpu_init_mask);
|
|
}
|
|
|
|
static int tegra20_power_up_cpu(unsigned int cpu)
|
|
{
|
|
/* Enable the CPU clock. */
|
|
tegra_enable_cpu_clock(cpu);
|
|
|
|
/* Clear flow controller CSR. */
|
|
flowctrl_write_cpu_csr(cpu, 0);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int tegra30_power_up_cpu(unsigned int cpu)
|
|
{
|
|
int ret, pwrgateid;
|
|
unsigned long timeout;
|
|
|
|
pwrgateid = tegra_cpu_powergate_id(cpu);
|
|
if (pwrgateid < 0)
|
|
return pwrgateid;
|
|
|
|
/*
|
|
* The power up sequence of cold boot CPU and warm boot CPU
|
|
* was different.
|
|
*
|
|
* For warm boot CPU that was resumed from CPU hotplug, the
|
|
* power will be resumed automatically after un-halting the
|
|
* flow controller of the warm boot CPU. We need to wait for
|
|
* the confirmaiton that the CPU is powered then removing
|
|
* the IO clamps.
|
|
* For cold boot CPU, do not wait. After the cold boot CPU be
|
|
* booted, it will run to tegra_secondary_init() and set
|
|
* tegra_cpu_init_mask which influences what tegra30_power_up_cpu()
|
|
* next time around.
|
|
*/
|
|
if (cpumask_test_cpu(cpu, &tegra_cpu_init_mask)) {
|
|
timeout = jiffies + msecs_to_jiffies(50);
|
|
do {
|
|
if (!tegra_powergate_is_powered(pwrgateid))
|
|
goto remove_clamps;
|
|
udelay(10);
|
|
} while (time_before(jiffies, timeout));
|
|
}
|
|
|
|
/*
|
|
* The power status of the cold boot CPU is power gated as
|
|
* default. To power up the cold boot CPU, the power should
|
|
* be un-gated by un-toggling the power gate register
|
|
* manually.
|
|
*/
|
|
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 + msecs_to_jiffies(100);
|
|
while (tegra_powergate_is_powered(pwrgateid)) {
|
|
if (time_after(jiffies, timeout))
|
|
return -ETIMEDOUT;
|
|
udelay(10);
|
|
}
|
|
}
|
|
|
|
remove_clamps:
|
|
/* CPU partition is powered. Enable the CPU clock. */
|
|
tegra_enable_cpu_clock(cpu);
|
|
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;
|
|
}
|
|
|
|
static int __cpuinit tegra_boot_secondary(unsigned int cpu, struct task_struct *idle)
|
|
{
|
|
int status;
|
|
|
|
cpu = cpu_logical_map(cpu);
|
|
|
|
/*
|
|
* Force the CPU into reset. The CPU must remain in reset when the
|
|
* 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.
|
|
*/
|
|
tegra_put_cpu_in_reset(cpu);
|
|
|
|
/*
|
|
* 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.
|
|
*/
|
|
flowctrl_write_cpu_halt(cpu, 0);
|
|
|
|
switch (tegra_chip_id) {
|
|
case TEGRA20:
|
|
status = tegra20_power_up_cpu(cpu);
|
|
break;
|
|
case TEGRA30:
|
|
status = tegra30_power_up_cpu(cpu);
|
|
break;
|
|
default:
|
|
status = -EINVAL;
|
|
break;
|
|
}
|
|
|
|
if (status)
|
|
goto done;
|
|
|
|
/* Take the CPU out of reset. */
|
|
tegra_cpu_out_of_reset(cpu);
|
|
done:
|
|
return status;
|
|
}
|
|
|
|
/*
|
|
* Initialise the CPU possible map early - this describes the CPUs
|
|
* which may be present or become present in the system.
|
|
*/
|
|
static void __init tegra_smp_init_cpus(void)
|
|
{
|
|
unsigned int i, ncores = scu_get_core_count(scu_base);
|
|
|
|
if (ncores > nr_cpu_ids) {
|
|
pr_warn("SMP: %u cores greater than maximum (%u), clipping\n",
|
|
ncores, nr_cpu_ids);
|
|
ncores = nr_cpu_ids;
|
|
}
|
|
|
|
for (i = 0; i < ncores; i++)
|
|
set_cpu_possible(i, true);
|
|
}
|
|
|
|
static void __init tegra_smp_prepare_cpus(unsigned int max_cpus)
|
|
{
|
|
/* Always mark the boot CPU (CPU0) as initialized. */
|
|
cpumask_set_cpu(0, &tegra_cpu_init_mask);
|
|
|
|
scu_enable(scu_base);
|
|
}
|
|
|
|
struct smp_operations tegra_smp_ops __initdata = {
|
|
.smp_init_cpus = tegra_smp_init_cpus,
|
|
.smp_prepare_cpus = tegra_smp_prepare_cpus,
|
|
.smp_secondary_init = tegra_secondary_init,
|
|
.smp_boot_secondary = tegra_boot_secondary,
|
|
#ifdef CONFIG_HOTPLUG_CPU
|
|
.cpu_kill = tegra_cpu_kill,
|
|
.cpu_die = tegra_cpu_die,
|
|
.cpu_disable = tegra_cpu_disable,
|
|
#endif
|
|
};
|