summaryrefslogtreecommitdiffstats
path: root/arch/arm/cpu/armv7/omap-common/abb.c
blob: 87d1fb82eba64689c57e11150479df08b7319d38 (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
/*
 *
 * Adaptive Body Bias programming sequence for OMAP family
 *
 * (C) Copyright 2013
 * Texas Instruments, <www.ti.com>
 *
 * Andrii Tseglytskyi <andrii.tseglytskyi@ti.com>
 *
 * See file CREDITS for list of people who contributed to this
 * project.
 *
 * 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.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston,
 * MA 02111-1307 USA
 */

#include <common.h>
#include <asm/omap_common.h>
#include <asm/io.h>
#include <asm/arch/sys_proto.h>

__weak s8 abb_setup_ldovbb(u32 fuse, u32 ldovbb)
{
	return -1;
}

static void abb_setup_timings(u32 setup)
{
	u32 sys_rate, sr2_cnt, clk_cycles;

	/*
	 * SR2_WTCNT_VALUE is the settling time for the ABB ldo after a
	 * transition and must be programmed with the correct time at boot.
	 * The value programmed into the register is the number of SYS_CLK
	 * clock cycles that match a given wall time profiled for the ldo.
	 * This value depends on:
	 * settling time of ldo in micro-seconds (varies per OMAP family),
	 * of clock cycles per SYS_CLK period (varies per OMAP family),
	 * the SYS_CLK frequency in MHz (varies per board)
	 * The formula is:
	 *
	 *		       ldo settling time (in micro-seconds)
	 * SR2_WTCNT_VALUE = ------------------------------------------
	 *		    (# system clock cycles) * (sys_clk period)
	 *
	 * Put another way:
	 *
	 * SR2_WTCNT_VALUE = settling time / (# SYS_CLK cycles / SYS_CLK rate))
	 *
	 * To avoid dividing by zero multiply both "# clock cycles" and
	 * "settling time" by 10 such that the final result is the one we want.
	 */

	/* calculate SR2_WTCNT_VALUE */
	sys_rate = DIV_ROUND(V_OSCK, 1000000);
	clk_cycles = DIV_ROUND(OMAP_ABB_CLOCK_CYCLES * 10, sys_rate);
	sr2_cnt = DIV_ROUND(OMAP_ABB_SETTLING_TIME * 10, clk_cycles);

	setbits_le32(setup,
		     sr2_cnt << (ffs(OMAP_ABB_SETUP_SR2_WTCNT_VALUE_MASK) - 1));
}

void abb_setup(u32 fuse, u32 ldovbb, u32 setup, u32 control,
	       u32 txdone, u32 txdone_mask, u32 opp)
{
	u32 abb_type_mask, opp_sel_mask;

	/* sanity check */
	if (!setup || !control || !txdone)
		return;

	/* setup ABB only in case of Fast or Slow OPP */
	switch (opp) {
	case OMAP_ABB_FAST_OPP:
		abb_type_mask = OMAP_ABB_SETUP_ACTIVE_FBB_SEL_MASK;
		opp_sel_mask = OMAP_ABB_CONTROL_FAST_OPP_SEL_MASK;
		break;
	case OMAP_ABB_SLOW_OPP:
		abb_type_mask = OMAP_ABB_SETUP_ACTIVE_RBB_SEL_MASK;
		opp_sel_mask = OMAP_ABB_CONTROL_SLOW_OPP_SEL_MASK;
		break;
	default:
	       return;
	}

	/*
	 * For some OMAP silicons additional setup for LDOVBB register is
	 * required. This is determined by data retrieved from corresponding
	 * OPP EFUSE register. Data, which is retrieved from EFUSE - is
	 * ABB enable/disable flag and VSET value, which must be copied
	 * to LDOVBB register. If function call fails - return quietly,
	 * it means no ABB is required for such silicon.
	 *
	 * For silicons, which don't require LDOVBB setup "fuse" and
	 * "ldovbb" offsets are not defined. ABB will be initialized in
	 * the common way for them.
	 */
	if (fuse && ldovbb) {
		if (abb_setup_ldovbb(fuse, ldovbb))
			return;
	}

	/* clear ABB registers */
	writel(0, setup);
	writel(0, control);

	/* configure timings, based on oscillator value */
	abb_setup_timings(setup);

	/* clear pending interrupts before setup */
	setbits_le32(txdone, txdone_mask);

	/* select ABB type */
	setbits_le32(setup, abb_type_mask | OMAP_ABB_SETUP_SR2EN_MASK);

	/* initiate ABB ldo change */
	setbits_le32(control, opp_sel_mask | OMAP_ABB_CONTROL_OPP_CHANGE_MASK);

	/* wait until transition complete */
	if (!wait_on_value(txdone_mask, txdone_mask, (void *)txdone, LDELAY))
		puts("Error: ABB txdone is not set\n");

	/* clear ABB tranxdone */
	setbits_le32(txdone, txdone_mask);
}
OpenPOWER on IntegriCloud