summaryrefslogtreecommitdiff
path: root/arch/arm/mach-mx6/cpu_regulator-mx6.c
blob: d905132e98b108629826c83c4b23f085576e9b8b (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
/*
 * Copyright (C) 2012 Freescale Semiconductor, Inc. All Rights Reserved.
 */

/*
 * The code contained herein is licensed under the GNU General Public
 * License. You may obtain a copy of the GNU General Public License
 * Version 2 or later at the following locations:
 *
 * http://www.opensource.org/licenses/gpl-license.html
 * http://www.gnu.org/copyleft/gpl.html
 */

#include <linux/types.h>
#include <linux/kernel.h>
#include <linux/err.h>
#include <linux/jiffies.h>
#include <linux/regulator/consumer.h>
#include <linux/clk.h>
#if defined(CONFIG_CPU_FREQ)
#include <linux/cpufreq.h>
#endif
#include <linux/io.h>
#include <asm/cpu.h>

#include <mach/clock.h>
#include <mach/hardware.h>
#include <mach/system.h>
#include "regs-anadig.h"
#include "crm_regs.h"
struct regulator *cpu_regulator;
struct regulator *soc_regulator;
struct regulator *pu_regulator;
char *gp_reg_id;
char *soc_reg_id;
char *pu_reg_id;
static struct clk *cpu_clk;
static int cpu_op_nr;
static struct cpu_op *cpu_op_tbl;
extern struct cpu_op *(*get_cpu_op)(int *op);

extern unsigned long loops_per_jiffy;
extern u32 enable_ldo_mode;
int external_pureg;

static inline unsigned long mx6_cpu_jiffies(unsigned long old, u_int div,
					      u_int mult)
{
#if BITS_PER_LONG == 32

	u64 result = ((u64) old) * ((u64) mult);
	do_div(result, div);
	return (unsigned long) result;

#elif BITS_PER_LONG == 64

	unsigned long result = old * ((u64) mult);
	result /= div;
	return result;

#endif
}

void mx6_cpu_regulator_init(void)
{
	int cpu;
	u32 curr_cpu = 0;
	unsigned int reg;
#ifndef CONFIG_SMP
	unsigned long old_loops_per_jiffy;
#endif
	void __iomem *gpc_base = IO_ADDRESS(GPC_BASE_ADDR);
	external_pureg = 0;
	/*If internal ldo actived, use internal cpu_* regulator to replace the
	*regulator ids from board file. If internal ldo bypassed, use the
	*regulator ids which defined in board file and source from extern pmic
	*power rails.
	*If you want to use ldo bypass,you should do:
	*1.set enable_ldo_mode=LDO_MODE_BYPASSED in your board file by default
	*   or set in commandline from u-boot
	*2.set your extern pmic regulator name in your board file.
	*/
	if (enable_ldo_mode != LDO_MODE_BYPASSED) {
		gp_reg_id = "cpu_vddgp";
		soc_reg_id = "cpu_vddsoc";
		pu_reg_id = "cpu_vddgpu";
	}
	printk(KERN_INFO "cpu regulator init ldo=%x\n", enable_ldo_mode);
	cpu_regulator = regulator_get(NULL, gp_reg_id);
	if (IS_ERR(cpu_regulator))
		printk(KERN_ERR "%s: failed to get cpu regulator\n", __func__);
	else {
		cpu_clk = clk_get(NULL, "cpu_clk");
		if (IS_ERR(cpu_clk)) {
			printk(KERN_ERR "%s: failed to get cpu clock\n",
			       __func__);
		} else {
			curr_cpu = clk_get_rate(cpu_clk);
			cpu_op_tbl = get_cpu_op(&cpu_op_nr);
			/* Set the core to max frequency requested. */
			regulator_set_voltage(cpu_regulator,
					      cpu_op_tbl[0].cpu_voltage,
					      cpu_op_tbl[0].cpu_voltage);
			if (enable_ldo_mode == LDO_MODE_BYPASSED) {
				/*digital bypass VDDPU/VDDSOC/VDDARM*/
				reg = __raw_readl(ANADIG_REG_CORE);
				reg &= ~BM_ANADIG_REG_CORE_REG0_TRG;
				reg |= BF_ANADIG_REG_CORE_REG0_TRG(0x1f);
				reg &= ~BM_ANADIG_REG_CORE_REG1_TRG;
				reg |= BF_ANADIG_REG_CORE_REG1_TRG(0x1f);
				reg &= ~BM_ANADIG_REG_CORE_REG2_TRG;
				reg |= BF_ANADIG_REG_CORE_REG2_TRG(0x1f);
				__raw_writel(reg, ANADIG_REG_CORE);
				/* Mask the ANATOP brown out interrupt in the GPC. */
				reg = __raw_readl(gpc_base + 0x14);
				reg |= 0x80000000;
				__raw_writel(reg, gpc_base + 0x14);
			}
			clk_set_rate(cpu_clk, cpu_op_tbl[0].cpu_rate);

			/*Fix loops-per-jiffy */
#ifdef CONFIG_SMP
			for_each_online_cpu(cpu)
				per_cpu(cpu_data, cpu).loops_per_jiffy =
				mx6_cpu_jiffies(
					per_cpu(cpu_data, cpu).loops_per_jiffy,
					curr_cpu / 1000,
					clk_get_rate(cpu_clk) / 1000);
#else
			old_loops_per_jiffy = loops_per_jiffy;

			loops_per_jiffy =
				mx6_cpu_jiffies(old_loops_per_jiffy,
						curr_cpu/1000,
						clk_get_rate(cpu_clk) / 1000);
#endif
#if defined(CONFIG_CPU_FREQ)
			/* Fix CPU frequency for CPUFREQ. */
			for (cpu = 0; cpu < num_online_cpus(); cpu++)
				cpufreq_get(cpu);
#endif
		}
	}
	soc_regulator = regulator_get(NULL, soc_reg_id);
	if (IS_ERR(soc_regulator))
		printk(KERN_ERR "%s: failed to get soc regulator\n", __func__);
	pu_regulator = regulator_get(NULL, pu_reg_id);
	if (IS_ERR(pu_regulator))
		printk(KERN_ERR "%s: failed to get pu regulator\n", __func__);
	/*If use ldo bypass and VDDPU_IN is single supplied
	*by external pmic, it means VDDPU_IN can be turned off if GPU/VPU driver
	*not running.In this case we should set external_pureg which can be used
	*in pu_enable/pu_disable of arch/arm/mach-mx6/mx6_anatop_regulator.c to
	*enable or disable external VDDPU regulator from pmic. But for FSL
	*reference boards, VDDSOC_IN connect with VDDPU_IN, so we didn't set
	*pu_reg_id to the external pmic regulator supply name in the board file.
	*In this case external_pureg should be 0 and can't turn off extern pmic
	*regulator, but can turn off VDDPU by internal anatop power gate.
	*
	*If enable internal ldo , external_pureg will be 0, and
	*VDDPU can be turned off by internal anatop anatop power gate.
	*
	*/
	else if (!IS_ERR(pu_regulator) && strcmp(pu_reg_id, "cpu_vddgpu"))
		external_pureg = 1;
}