summaryrefslogtreecommitdiff
path: root/src/soc/rockchip/common/rk808.c
blob: 9b4708cfab019dfb1c09c91fa675820d9e5ce23c (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
/*
 * This file is part of the coreboot project.
 *
 * Copyright 2014 Rockchip Inc.
 *
 * 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; version 2 of the License.
 *
 * 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.
 */

#include <assert.h>
#include <bcd.h>
#include <console/console.h>
#include <delay.h>
#include <device/i2c_simple.h>
#include <rtc.h>
#include <soc/rk808.h>
#include <stdint.h>
#include <stdlib.h>

#if CONFIG_PMIC_BUS < 0
#error "PMIC_BUS must be set in mainboard's Kconfig."
#endif

#define RK808_ADDR		0x1b

#define DCDC_EN			0x23
#define LDO_EN			0x24
#define BUCK1SEL		0x2f
#define BUCK4SEL		0x38
#define LDO_ONSEL(i)		(0x39 + 2 * i)
#define LDO_SLPSEL(i)		(0x3a + 2 * i)

#define RTC_SECOND		0x00
#define RTC_MINUTE		0x01
#define RTC_HOUR		0x02
#define RTC_DAY			0x03
#define RTC_MONTH		0x04
#define RTC_YEAR		0x05
#define RTC_WEEKS		0x06
#define RTC_CTRL		0x10
#define RTC_STATUS		0x11

#define RTC_CTRL_STOP_RTC	(1 << 0)
#define RTC_CTRL_GET_TIME	(1 << 6)
#define RTC_CTRL_RTC_READSEL	(1 << 7)

#define DCDC_UV_ACT		0x28
#define DCDC_ILMAX		0x90

static int rk808_read(uint8_t reg, uint8_t *value)
{
	return i2c_readb(CONFIG_PMIC_BUS, RK808_ADDR, reg, value);
}

static int rk808_write(uint8_t reg, uint8_t value)
{
	return i2c_writeb(CONFIG_PMIC_BUS, RK808_ADDR, reg, value);
}

static void rk808_clrsetbits(uint8_t reg, uint8_t clr, uint8_t set)
{
	uint8_t value;

	if (rk808_read(reg, &value) || rk808_write(reg, (value & ~clr) | set))
		printk(BIOS_ERR, "ERROR: Cannot set Rk808[%#x]!\n", reg);
}

void rk808_configure_switch(int sw, int enabled)
{
	assert(sw == 1 || sw == 2);
	rk808_clrsetbits(DCDC_EN, 1 << (sw + 4), !!enabled << (sw + 4));
}

void rk808_configure_ldo(int ldo, int millivolts)
{
	uint8_t vsel;

	if (!millivolts) {
		rk808_clrsetbits(LDO_EN, 1 << (ldo - 1), 0);
		return;
	}

	switch (ldo) {
	case 1:
	case 2:
	case 4:
	case 5:
	case 8:
		vsel = div_round_up(millivolts, 100) - 18;
		assert(vsel <= 0x10);
		break;
	case 3:
	case 6:
	case 7:
		vsel = div_round_up(millivolts, 100) - 8;
		assert(vsel <= 0x11);
		break;
	default:
		die("Unknown LDO index!");
	}

	rk808_clrsetbits(LDO_ONSEL(ldo), 0x1f, vsel);
	rk808_clrsetbits(LDO_EN, 0, 1 << (ldo - 1));
}

void rk808_configure_buck(int buck, int millivolts)
{
	uint8_t vsel;
	uint8_t buck_reg;

	switch (buck) {
	case 1:
	case 2:
		/* 25mV steps. base = 29 * 25mV = 725 */
		vsel = (div_round_up(millivolts, 25) - 29) * 2 + 1;
		assert(vsel <= 0x3f);
		buck_reg = BUCK1SEL + 4 * (buck - 1);
		break;
	case 4:
		vsel = div_round_up(millivolts, 100) - 18;
		assert(vsel <= 0xf);
		buck_reg = BUCK4SEL;
		break;
	default:
		die("Unknown buck index!");
	}
	rk808_clrsetbits(DCDC_ILMAX, 0, 3 << ((buck - 1) * 2));

	/* undervoltage detection may be wrong, disable it */
	rk808_clrsetbits(DCDC_UV_ACT, 1 << (buck - 1), 0);

	rk808_clrsetbits(buck_reg, 0x3f, vsel);
	rk808_clrsetbits(DCDC_EN, 0, 1 << (buck - 1));
}

static void rk808rtc_stop(void)
{
	rk808_clrsetbits(RTC_CTRL, RTC_CTRL_STOP_RTC, 0);
}

static void rk808rtc_start(void)
{
	rk808_clrsetbits(RTC_CTRL, 0, RTC_CTRL_STOP_RTC);
}

int rtc_set(const struct rtc_time *time)
{
	int ret = 0;

	/* RTC time can only be set when RTC is frozen */
	rk808rtc_stop();

	ret |= rk808_write(RTC_SECOND, bin2bcd(time->sec));
	ret |= rk808_write(RTC_MINUTE, bin2bcd(time->min));
	ret |= rk808_write(RTC_HOUR, bin2bcd(time->hour));
	ret |= rk808_write(RTC_DAY, bin2bcd(time->mday));
	ret |= rk808_write(RTC_MONTH, bin2bcd(time->mon));
	ret |= rk808_write(RTC_YEAR, bin2bcd(time->year));

	rk808rtc_start();
	return ret;
}

int rtc_get(struct rtc_time *time)
{
	uint8_t value;
	int ret = 0;

	/*
	 * Set RTC_READSEL to cause reads to access shadow registers and
	 * transition GET_TIME from 0 to 1 to cause dynamic register content
	 * to be copied into shadow registers. This ensures a coherent reading
	 * of time values as we access each register using slow I2C transfers.
	 */
	rk808_clrsetbits(RTC_CTRL, RTC_CTRL_GET_TIME, 0);
	rk808_clrsetbits(RTC_CTRL, 0, RTC_CTRL_GET_TIME | RTC_CTRL_RTC_READSEL);

	/*
	 * After we set the GET_TIME bit, the rtc time can't be read
	 * immediately. So we should wait up to 31.25 us.
	 */
	udelay(32);

	ret |= rk808_read(RTC_SECOND, &value);
	time->sec = bcd2bin(value & 0x7f);

	ret |= rk808_read(RTC_MINUTE, &value);
	time->min = bcd2bin(value & 0x7f);

	ret |= rk808_read(RTC_HOUR, &value);
	time->hour = bcd2bin(value & 0x3f);

	ret |= rk808_read(RTC_DAY, &value);
	time->mday = bcd2bin(value & 0x3f);

	ret |= rk808_read(RTC_MONTH, &value);
	time->mon = bcd2bin(value & 0x1f);

	ret |= rk808_read(RTC_YEAR, &value);
	time->year = bcd2bin(value);

	time->wday = -1; /* unknown */

	return ret;
}