aboutsummaryrefslogtreecommitdiffstats
path: root/arch/arm/mach-tegra/pmc.c
diff options
context:
space:
mode:
Diffstat (limited to 'arch/arm/mach-tegra/pmc.c')
-rw-r--r--arch/arm/mach-tegra/pmc.c152
1 files changed, 122 insertions, 30 deletions
diff --git a/arch/arm/mach-tegra/pmc.c b/arch/arm/mach-tegra/pmc.c
index d4fdb5fcec20..b30e921cc3a9 100644
--- a/arch/arm/mach-tegra/pmc.c
+++ b/arch/arm/mach-tegra/pmc.c
@@ -1,5 +1,5 @@
1/* 1/*
2 * Copyright (C) 2012 NVIDIA CORPORATION. All rights reserved. 2 * Copyright (C) 2012,2013 NVIDIA CORPORATION. All rights reserved.
3 * 3 *
4 * This program is free software; you can redistribute it and/or modify it 4 * This program is free software; you can redistribute it and/or modify it
5 * under the terms and conditions of the GNU General Public License, 5 * under the terms and conditions of the GNU General Public License,
@@ -18,57 +18,149 @@
18#include <linux/kernel.h> 18#include <linux/kernel.h>
19#include <linux/io.h> 19#include <linux/io.h>
20#include <linux/of.h> 20#include <linux/of.h>
21#include <linux/of_address.h>
21 22
22#include "iomap.h" 23#define PMC_CTRL 0x0
24#define PMC_CTRL_INTR_LOW (1 << 17)
25#define PMC_PWRGATE_TOGGLE 0x30
26#define PMC_PWRGATE_TOGGLE_START (1 << 8)
27#define PMC_REMOVE_CLAMPING 0x34
28#define PMC_PWRGATE_STATUS 0x38
23 29
24#define PMC_CTRL 0x0 30#define TEGRA_POWERGATE_PCIE 3
25#define PMC_CTRL_INTR_LOW (1 << 17) 31#define TEGRA_POWERGATE_VDEC 4
32#define TEGRA_POWERGATE_CPU1 9
33#define TEGRA_POWERGATE_CPU2 10
34#define TEGRA_POWERGATE_CPU3 11
35
36static u8 tegra_cpu_domains[] = {
37 0xFF, /* not available for CPU0 */
38 TEGRA_POWERGATE_CPU1,
39 TEGRA_POWERGATE_CPU2,
40 TEGRA_POWERGATE_CPU3,
41};
42static DEFINE_SPINLOCK(tegra_powergate_lock);
43
44static void __iomem *tegra_pmc_base;
45static bool tegra_pmc_invert_interrupt;
26 46
27static inline u32 tegra_pmc_readl(u32 reg) 47static inline u32 tegra_pmc_readl(u32 reg)
28{ 48{
29 return readl(IO_ADDRESS(TEGRA_PMC_BASE + reg)); 49 return readl(tegra_pmc_base + reg);
30} 50}
31 51
32static inline void tegra_pmc_writel(u32 val, u32 reg) 52static inline void tegra_pmc_writel(u32 val, u32 reg)
33{ 53{
34 writel(val, IO_ADDRESS(TEGRA_PMC_BASE + reg)); 54 writel(val, tegra_pmc_base + reg);
55}
56
57static int tegra_pmc_get_cpu_powerdomain_id(int cpuid)
58{
59 if (cpuid <= 0 || cpuid >= num_possible_cpus())
60 return -EINVAL;
61 return tegra_cpu_domains[cpuid];
62}
63
64static bool tegra_pmc_powergate_is_powered(int id)
65{
66 return (tegra_pmc_readl(PMC_PWRGATE_STATUS) >> id) & 1;
67}
68
69static int tegra_pmc_powergate_set(int id, bool new_state)
70{
71 bool old_state;
72 unsigned long flags;
73
74 spin_lock_irqsave(&tegra_powergate_lock, flags);
75
76 old_state = tegra_pmc_powergate_is_powered(id);
77 WARN_ON(old_state == new_state);
78
79 tegra_pmc_writel(PMC_PWRGATE_TOGGLE_START | id, PMC_PWRGATE_TOGGLE);
80
81 spin_unlock_irqrestore(&tegra_powergate_lock, flags);
82
83 return 0;
84}
85
86static int tegra_pmc_powergate_remove_clamping(int id)
87{
88 u32 mask;
89
90 /*
91 * Tegra has a bug where PCIE and VDE clamping masks are
92 * swapped relatively to the partition ids.
93 */
94 if (id == TEGRA_POWERGATE_VDEC)
95 mask = (1 << TEGRA_POWERGATE_PCIE);
96 else if (id == TEGRA_POWERGATE_PCIE)
97 mask = (1 << TEGRA_POWERGATE_VDEC);
98 else
99 mask = (1 << id);
100
101 tegra_pmc_writel(mask, PMC_REMOVE_CLAMPING);
102
103 return 0;
104}
105
106bool tegra_pmc_cpu_is_powered(int cpuid)
107{
108 int id;
109
110 id = tegra_pmc_get_cpu_powerdomain_id(cpuid);
111 if (id < 0)
112 return false;
113 return tegra_pmc_powergate_is_powered(id);
114}
115
116int tegra_pmc_cpu_power_on(int cpuid)
117{
118 int id;
119
120 id = tegra_pmc_get_cpu_powerdomain_id(cpuid);
121 if (id < 0)
122 return id;
123 return tegra_pmc_powergate_set(id, true);
124}
125
126int tegra_pmc_cpu_remove_clamping(int cpuid)
127{
128 int id;
129
130 id = tegra_pmc_get_cpu_powerdomain_id(cpuid);
131 if (id < 0)
132 return id;
133 return tegra_pmc_powergate_remove_clamping(id);
35} 134}
36 135
37#ifdef CONFIG_OF
38static const struct of_device_id matches[] __initconst = { 136static const struct of_device_id matches[] __initconst = {
137 { .compatible = "nvidia,tegra114-pmc" },
138 { .compatible = "nvidia,tegra30-pmc" },
39 { .compatible = "nvidia,tegra20-pmc" }, 139 { .compatible = "nvidia,tegra20-pmc" },
40 { } 140 { }
41}; 141};
42#endif
43 142
44void __init tegra_pmc_init(void) 143static void tegra_pmc_parse_dt(void)
45{ 144{
46 /* 145 struct device_node *np;
47 * For now, Harmony is the only board that uses the PMC, and it wants
48 * the signal inverted. Seaboard would too if it used the PMC.
49 * Hopefully by the time other boards want to use the PMC, everything
50 * will be device-tree, or they also want it inverted.
51 */
52 bool invert_interrupt = true;
53 u32 val;
54 146
55#ifdef CONFIG_OF 147 np = of_find_matching_node(NULL, matches);
56 if (of_have_populated_dt()) { 148 BUG_ON(!np);
57 struct device_node *np;
58 149
59 invert_interrupt = false; 150 tegra_pmc_base = of_iomap(np, 0);
151
152 tegra_pmc_invert_interrupt = of_property_read_bool(np,
153 "nvidia,invert-interrupt");
154}
155
156void __init tegra_pmc_init(void)
157{
158 u32 val;
60 159
61 np = of_find_matching_node(NULL, matches); 160 tegra_pmc_parse_dt();
62 if (np) {
63 if (of_find_property(np, "nvidia,invert-interrupt",
64 NULL))
65 invert_interrupt = true;
66 }
67 }
68#endif
69 161
70 val = tegra_pmc_readl(PMC_CTRL); 162 val = tegra_pmc_readl(PMC_CTRL);
71 if (invert_interrupt) 163 if (tegra_pmc_invert_interrupt)
72 val |= PMC_CTRL_INTR_LOW; 164 val |= PMC_CTRL_INTR_LOW;
73 else 165 else
74 val &= ~PMC_CTRL_INTR_LOW; 166 val &= ~PMC_CTRL_INTR_LOW;