summaryrefslogtreecommitdiff
path: root/arch/arm/mach-omap2/omap_tps6236x.c
blob: 904bb7f21bf64f2f07c42a25e33ec4eb3a8fa5bc (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
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
/*
 * OMAP and TPS6236x specific initialization
 *
 * Copyright (C) 2011 Texas Instruments Incorporated - http://www.ti.com/
 * Vishwanath BS
 * Nishanth Menon
 *
 * 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/kernel.h>
#include <linux/gpio.h>
#include <linux/i2c/twl.h>

#include "pm.h"
#include "vc.h"
#include "mux.h"

/* Voltage limits supported */
#define MIN_VOLTAGE_TPS62360_62_UV	770000
#define MAX_VOLTAGE_TPS62360_62_UV	1400000

#define MIN_VOLTAGE_TPS62361_UV		500000
#define MAX_VOLTAGE_TPS62361_UV		1770000

#define NOM_VOLTAGE_TPS62361_UV		1430000

#define MAX_VOLTAGE_RAMP_TPS6236X_UV	32000

/*
 * This is the voltage delta between 2 values in voltage register.
 * when switching voltage V1 to V2, TPS62361 can ramp up or down
 * initially with step sizes of 20mV with a last step of 10mV.
 * In the case of TPS6236[0|2], it is a constant 10mV steps
 * we choose the 10mV step for linearity when SR is configured.
 */
#define STEP_SIZE_TPS6236X		10000

/* I2C access parameters */
#define I2C_TPS6236X_SLAVE_ADDR		0x60

#define DEF_SET_REG(VSEL0, VSEL1)	(((VSEL1) << 1| (VSEL0) << 0) & 0x3)
#define REG_TPS6236X_SET_0		0x00
#define REG_TPS6236X_SET_1		0x01
#define REG_TPS6236X_SET_2		0x02
#define REG_TPS6236X_SET_3		0x03
#define REG_TPS6236X_CTRL		0x04
#define REG_TPS6236X_TEMP		0x05
#define REG_TPS6236X_RAMP_CTRL		0x06
#define REG_TPS6236X_CHIP_ID0		0x08
#define REG_TPS6236X_CHIP_ID1		0x09

#define MODE_TPS6236X_AUTO_PFM_PWM	0x00
#define MODE_TPS6236X_FORCE_PWM		BIT(7)

/* We use Auto PFM/PWM mode currently seems to have the best trade off */
#define VOLTAGE_PFM_MODE_VAL		MODE_TPS6236X_AUTO_PFM_PWM

#define REG_TPS6236X_RAMP_CTRL_RMP_MASK	(0x7 << 5)
#define REG_TPS6236X_RAMP_CTRL_EN_DISC	BIT(2)
#define REG_TPS6236X_RAMP_CTRL_RAMP_PFM	BIT(1)

#define REG_TPS6236X_CTRL_PD_EN		BIT(7)
#define REG_TPS6236X_CTRL_PD_VSEL0	BIT(6)
#define REG_TPS6236X_CTRL_PD_VSEL1	BIT(5)

/* TWL usage */
#define TWL6030_REG_SYSEN_CFG_GRP			0xB3
#define TWL6030_BIT_APE_GRP				BIT(0)

/* Voltage params of the attached device (all in uV) */
static unsigned long	voltage_min;
static unsigned long	voltage_max;

/* Which register do we use by default? */
static int __initdata default_reg = -1;

/* Do we need to setup internal pullups? */
static int __initdata pd_vsel0 = -1;
static int __initdata pd_vsel1 = -1;

static int __init _bd_setup(char *name, int gpio_vsel, int *pull, int *pd_vsel)
{
	int pull_dir;
	int r;

	if (gpio_vsel == -1) {
		if (*pull != -1) {
			*pd_vsel = (*pull == OMAP_PIN_OFF_OUTPUT_HIGH);
			*pull = *pd_vsel;
		} else {
			*pull = 0;
		}
		return 0;
	}

	/* if we have a pull gpio, with bad dir, pull low */
	if (*pull == -1 || (*pull != OMAP_PIN_OFF_OUTPUT_HIGH &&
				*pull != OMAP_PIN_OFF_OUTPUT_LOW))
		*pull = OMAP_PIN_OFF_OUTPUT_LOW;

	r = omap_mux_init_gpio(gpio_vsel, *pull);
	if (r) {
		pr_err("%s: unable to mux gpio%d=%d\n", __func__,
			gpio_vsel, r);
		goto out;
	}

	pull_dir = (*pull == OMAP_PIN_OFF_OUTPUT_HIGH);
	*pull = pull_dir;

	r = gpio_request(gpio_vsel, name);
	if (r) {
		pr_err("%s: unable to req gpio%d=%d\n", __func__,
			gpio_vsel, r);
		goto out;
	}
	r = gpio_direction_output(gpio_vsel, pull_dir);
	if (r) {
		pr_err("%s: unable to pull[%d] gpio%d=%d\n", __func__,
			gpio_vsel, pull_dir, r);
		gpio_free(gpio_vsel);
		goto out;
	}
out:
	return r;
}

/* Convert the ramp voltage to ramp value. */
static u8 __init tps6236x_ramp_value(unsigned long uv)
{
	if (!uv)
		return 0;

	if (uv > MAX_VOLTAGE_RAMP_TPS6236X_UV) {
		pr_err("%s: uv%ld greater than max %d\n", __func__,
			uv, MAX_VOLTAGE_RAMP_TPS6236X_UV);
		uv = MAX_VOLTAGE_RAMP_TPS6236X_UV;
	}
	return fls(MAX_VOLTAGE_RAMP_TPS6236X_UV / uv) - 1;
}

static unsigned long tps6236x_vsel_to_uv(const u8 vsel)
{
	return (voltage_min +
		(STEP_SIZE_TPS6236X * (vsel & ~VOLTAGE_PFM_MODE_VAL)));
}

static u8 tps6236x_uv_to_vsel(unsigned long uv)
{
	if (!uv)
		return 0;

	/* Round off requests to limits */
	if (uv > voltage_max) {
		pr_err("%s:Request for overvoltage[%ld] than supported[%ld]\n",
				__func__, uv, voltage_max);
		uv = voltage_max;
	}
	if (uv < voltage_min) {
		pr_err("%s:Request for undervoltage[%ld] than supported[%ld]\n",
				__func__, uv, voltage_min);
		uv = voltage_min;
	}
	return DIV_ROUND_UP(uv - voltage_min, STEP_SIZE_TPS6236X) |
			VOLTAGE_PFM_MODE_VAL;
}

static struct omap_voltdm_pmic omap4_mpu_pmic = {
	.slew_rate		= 8000,
	.step_size		= STEP_SIZE_TPS6236X,
	.volt_setup_time	= 0,
	.startup_time		= 1000,
	.shutdown_time		= 1,
	.switch_on_time		= 1000,
	.vp_erroroffset		= OMAP4_VP_CONFIG_ERROROFFSET,
	.vp_vstepmin		= OMAP4_VP_VSTEPMIN_VSTEPMIN,
	.vp_vstepmax		= OMAP4_VP_VSTEPMAX_VSTEPMAX,
	.vp_vddmin		= OMAP4460_VP_MPU_VLIMITTO_VDDMIN,
	.vp_vddmax		= OMAP4460_VP_MPU_VLIMITTO_VDDMAX,
	.vp_timeout_us		= OMAP4_VP_VLIMITTO_TIMEOUT_US,
	.i2c_slave_addr		= I2C_TPS6236X_SLAVE_ADDR,
	.volt_reg_addr		= REG_TPS6236X_SET_0,
	.cmd_reg_addr		= REG_TPS6236X_SET_0,
	.i2c_high_speed		= true,
	.i2c_scll_low		= 0x28,
	.i2c_scll_high		= 0x2C,
	.i2c_hscll_low		= 0x0B,
	.i2c_hscll_high		= 0x00,
	.vsel_to_uv		= tps6236x_vsel_to_uv,
	.uv_to_vsel		= tps6236x_uv_to_vsel,
};

/**
 * omap4_twl_tps62361_enable() - Enable tps chip
 *
 * This function enables TPS chip by associating SYSEN signal
 * to APE resource group of TWL6030.
 *
 * Returns 0 on sucess, error is returned if I2C read/write fails.
 */
static int __init omap4_twl_tps62361_enable(struct voltagedomain *voltdm)
{
	int ret = 0;
	u8 val;

	/* Dont trust the bootloader. start with nominal, pm will set later */
	val = voltdm->pmic->uv_to_vsel(NOM_VOLTAGE_TPS62361_UV);
	ret = omap_vc_bypass_send_i2c_msg(voltdm, voltdm->pmic->i2c_slave_addr,
			default_reg, val);

	/* Setup Ramp */
	val = tps6236x_ramp_value(voltdm->pmic->slew_rate) <<
		__ffs(REG_TPS6236X_RAMP_CTRL_RMP_MASK);
	val &= REG_TPS6236X_RAMP_CTRL_RMP_MASK;

	/* We would like to ramp the voltage asap */
	val |= REG_TPS6236X_RAMP_CTRL_RAMP_PFM;

	ret = omap_vc_bypass_send_i2c_msg(voltdm, voltdm->pmic->i2c_slave_addr,
			REG_TPS6236X_RAMP_CTRL, val);
	if (ret)
		goto out;

	/* Setup the internal pulls to select if needed */
	if (pd_vsel0 != -1 || pd_vsel1 != -1) {
		val = REG_TPS6236X_CTRL_PD_EN;
		val |= (pd_vsel0) ? 0 : REG_TPS6236X_CTRL_PD_VSEL0;
		val |= (pd_vsel1) ? 0 : REG_TPS6236X_CTRL_PD_VSEL1;
		ret = omap_vc_bypass_send_i2c_msg(voltdm,
					voltdm->pmic->i2c_slave_addr,
					REG_TPS6236X_CTRL, val);
		if (ret)
			goto out;
	}

	/* Enable thermal shutdown - 0 is enable :) */
	ret = omap_vc_bypass_send_i2c_msg(voltdm,
				voltdm->pmic->i2c_slave_addr,
				REG_TPS6236X_TEMP, 0x0);
	if (ret)
		goto out;

	/* if we have to work with TWL */
#ifdef CONFIG_TWL4030_CORE
	ret = twl_i2c_read_u8(TWL4030_MODULE_PM_RECEIVER, &val,
			TWL6030_REG_SYSEN_CFG_GRP);
	if (ret)
		goto out;

	val |= TWL6030_BIT_APE_GRP;
	ret = twl_i2c_write_u8(TWL4030_MODULE_PM_RECEIVER, val,
			TWL6030_REG_SYSEN_CFG_GRP);
#endif

out:
	if (ret)
		pr_err("%s: Error enabling TPS(%d)\n", __func__, ret);

	return ret;
}

static __initdata struct omap_pmic_map omap_tps_map[] = {
	{
		.name = "mpu",
		.pmic_data = &omap4_mpu_pmic,
		.special_action = omap4_twl_tps62361_enable,
	},
	/* Terminator */
	{ .name = NULL, .pmic_data = NULL},
};

int __init omap_tps6236x_init(void)
{
	struct omap_pmic_map *map;

	/* Without registers, I wont proceed */
	if (default_reg == -1)
		return -EINVAL;

	map = omap_tps_map;

	/* setup all the pmic's voltage addresses to the default one */
	while (map->name) {
		map->pmic_data->volt_reg_addr = default_reg;
		map->pmic_data->cmd_reg_addr = default_reg;
		map++;
	}

	return omap_pmic_register_data(omap_tps_map);
}

/**
 * omap_tps6236x_board_setup() - provide the board config for TPS connect
 * @use_62361:	Do we use TPS62361 variant?
 * @gpio_vsel0:	If using GPIO to control VSEL0, provide gpio number, else -1
 * @gpio_vsel1:	If using GPIO to control VSEL1, provide gpio number, else -1
 * @pull0:	If using GPIO, provide mux mode OMAP_PIN_OFF_OUTPUT_[HIGH|LOW]
 *		else provide any internal pull required, -1 if unused.
 * @pull1:	If using GPIO, provide mux mode OMAP_PIN_OFF_OUTPUT_[HIGH|LOW]
 *		else provide any internal pull required, -1 if unused.
 *
 * TPS6236x variants of PMIC can be hooked in numerous combinations on to the
 * board. Some platforms can choose to hardwire and save on a GPIO for other
 * uses, while others may hook a single line for GPIO control and may ground
 * the other line. support these configurations.
 *
 * WARNING: for platforms using GPIO, be careful to provide MUX setting
 * considering OFF mode configuration as well.
 */
int __init omap_tps6236x_board_setup(bool use_62361, int gpio_vsel0,
		int gpio_vsel1, int pull0, int pull1)
{
	int r;

	r = _bd_setup("tps6236x_vsel0", gpio_vsel0, &pull0, &pd_vsel0);
	if (r)
		goto out;
	r = _bd_setup("tps6236x_vsel1", gpio_vsel1, &pull1, &pd_vsel1);
	if (r) {
		if (gpio_vsel0 != -1)
			gpio_free(gpio_vsel0);
		goto out;
	}

	default_reg = ((pull1 & 0x1) << 1) | (pull0 & 0x1);

	if (use_62361) {
		voltage_min = MIN_VOLTAGE_TPS62361_UV;
		voltage_max = MAX_VOLTAGE_TPS62361_UV;
	} else {
		voltage_min = MIN_VOLTAGE_TPS62360_62_UV;
		voltage_max = MAX_VOLTAGE_TPS62360_62_UV;
	}
out:
	return r;
}