summaryrefslogtreecommitdiff
path: root/arch/arm/cpu/armv7/omap-common/vc.c
blob: e6e5f7893c2c0a1c2b5eb3f1f29a77a3bc8de7a9 (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
/*
 * Voltage Controller implementation for OMAP
 *
 * Copyright (C) 2012 Texas Instruments Incorporated - http://www.ti.com/
 *	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.
 *
 * This program is distributed "as is" WITHOUT ANY WARRANTY of any
 * kind, whether express or implied; without even the implied warranty
 * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 */

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

/*
 * Define Master code if there are multiple masters on the I2C_SR bus.
 * Normally not required
 */
#ifndef CONFIG_OMAP_VC_I2C_HS_MCODE
#define CONFIG_OMAP_VC_I2C_HS_MCODE 0x0
#endif

/* Register defines and masks for VC IP Block */
/* PRM_VC_CFG_I2C_MODE */
#define PRM_VC_CFG_I2C_MODE_DFILTEREN_BIT	(0x1 << 6)
#define PRM_VC_CFG_I2C_MODE_SRMODEEN_BIT	(0x1 << 4)
#define PRM_VC_CFG_I2C_MODE_HSMODEEN_BIT	(0x1 << 3)
#define PRM_VC_CFG_I2C_MODE_HSMCODE_SHIFT	0x0
#define PRM_VC_CFG_I2C_MODE_HSMCODE_MASK	0x3

/* PRM_VC_CFG_I2C_CLK */
#define PRM_VC_CFG_I2C_CLK_HSCLL_SHIFT		24
#define PRM_VC_CFG_I2C_CLK_HSCLL_MASK		0xFF
#define PRM_VC_CFG_I2C_CLK_HSCLH_SHIFT		16
#define PRM_VC_CFG_I2C_CLK_HSCLH_MASK		0xFF
#define PRM_VC_CFG_I2C_CLK_SCLH_SHIFT		0
#define PRM_VC_CFG_I2C_CLK_SCLH_MASK		0xFF
#define PRM_VC_CFG_I2C_CLK_SCLL_SHIFT		8
#define PRM_VC_CFG_I2C_CLK_SCLL_MASK		(0xFF << 8)

/* PRM_VC_VAL_BYPASS */
#define PRM_VC_VAL_BYPASS_VALID_BIT		(0x1 << 24)
#define PRM_VC_VAL_BYPASS_SLAVEADDR_SHIFT	0
#define PRM_VC_VAL_BYPASS_SLAVEADDR_MASK	0x7F
#define PRM_VC_VAL_BYPASS_REGADDR_SHIFT		8
#define PRM_VC_VAL_BYPASS_REGADDR_MASK		0xFF
#define PRM_VC_VAL_BYPASS_DATA_SHIFT		16
#define PRM_VC_VAL_BYPASS_DATA_MASK		0xFF

/**
 * omap_vc_init() - Initialization for Voltage controller
 * @speed_khz: I2C buspeed in KHz
 */
void omap_vc_init(u16 speed_khz)
{
	u32 val;
	u32 sys_clk_khz, cycles_hi, cycles_low;

	sys_clk_khz = get_sys_clk_freq() / 1000;

	if (speed_khz > 400) {
		puts("higher speed requested - throttle to 400Khz\n");
		speed_khz = 400;
	}

	/*
	 * Setup the dedicated I2C controller for Voltage Control
	 * I2C clk - high period 40% low period 60%
	 */
	speed_khz /= 10;
	cycles_hi = sys_clk_khz * 4 / speed_khz;
	cycles_low = sys_clk_khz * 6 / speed_khz;
	/* values to be set in register - less by 5 & 7 respectively */
	cycles_hi -= 5;
	cycles_low -= 7;
	val = (cycles_hi << PRM_VC_CFG_I2C_CLK_SCLH_SHIFT) |
	       (cycles_low << PRM_VC_CFG_I2C_CLK_SCLL_SHIFT);
	writel(val, (*prcm)->prm_vc_cfg_i2c_clk);

	val = CONFIG_OMAP_VC_I2C_HS_MCODE <<
		PRM_VC_CFG_I2C_MODE_HSMCODE_SHIFT;
	/* No HS mode for now */
	val &= ~PRM_VC_CFG_I2C_MODE_HSMODEEN_BIT;
	writel(val, (*prcm)->prm_vc_cfg_i2c_mode);
}

/**
 * omap_vc_bypass_send_value() - Send a data using VC Bypass command
 * @sa:		7 bit I2C slave address of the PMIC
 * @reg_addr:	I2C register address(8 bit) address in PMIC
 * @reg_data:	what 8 bit data to write
 */
int omap_vc_bypass_send_value(u8 sa, u8 reg_addr, u8 reg_data)
{
	/*
	 * Unfortunately we need to loop here instead of a defined time
	 * use arbitary large value
	 */
	u32 timeout = 0xFFFF;
	u32 reg_val;

	sa &= PRM_VC_VAL_BYPASS_SLAVEADDR_MASK;
	reg_addr &= PRM_VC_VAL_BYPASS_REGADDR_MASK;
	reg_data &= PRM_VC_VAL_BYPASS_DATA_MASK;

	/* program VC to send data */
	reg_val = sa << PRM_VC_VAL_BYPASS_SLAVEADDR_SHIFT |
	    reg_addr << PRM_VC_VAL_BYPASS_REGADDR_SHIFT |
	    reg_data << PRM_VC_VAL_BYPASS_DATA_SHIFT;
	writel(reg_val, (*prcm)->prm_vc_val_bypass);

	/* Signal VC to send data */
	writel(reg_val | PRM_VC_VAL_BYPASS_VALID_BIT,
				(*prcm)->prm_vc_val_bypass);

	/* Wait on VC to complete transmission */
	do {
		reg_val = readl((*prcm)->prm_vc_val_bypass) &
				PRM_VC_VAL_BYPASS_VALID_BIT;
		if (!reg_val)
			break;

		sdelay(100);
	} while (--timeout);

	/* Optional: cleanup PRM_IRQSTATUS_Ax */
	/* In case we can do something about it in future.. */
	if (!timeout)
		return -1;

	/* All good.. */
	return 0;
}