~funderscore blog cgit wiki get in touch
aboutsummaryrefslogtreecommitdiff
blob: 9294d36ba358afa67e41213dd606d8912a93b558 (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
// SPDX-License-Identifier: GPL-2.0+
/*
 * Copyright (C) 2015 Samsung Electronics
 * Przemyslaw Marczak <p.marczak@samsung.com>
 */

#include <common.h>
#include <adc.h>
#include <dm.h>
#include <errno.h>
#include <fdtdec.h>
#include <asm/global_data.h>
#include <linux/delay.h>
#include <power/pmic.h>
#include <power/regulator.h>
#include <power/s2mps11.h>
#include <samsung/exynos5-dt-types.h>
#include <samsung/misc.h>

DECLARE_GLOBAL_DATA_PTR;

static const struct udevice_id board_ids[] = {
	{ .compatible = "samsung,odroidxu3", .data = EXYNOS5_BOARD_ODROID_XU3 },
	{ .compatible = "samsung,exynos5", .data = EXYNOS5_BOARD_GENERIC },
	{ },
};

/**
 * Odroix XU3/XU4/HC1/HC2 board revisions (from HC1+_HC2_MAIN_REV0.1_20171017.pdf):
 * Rev   ADCmax  Board
 * 0.1     0     XU3 0.1
 * 0.2   372     XU3 0.2 | XU3L - no DISPLAYPORT (probe I2C0:0x40 / INA231)
 * 0.3  1280     XU4 0.1
 * 0.4   739     XU4 0.2
 * 0.5  1016     XU4+Air0.1 (Passive cooling)
 * 0.6  1309     XU4-HC1 0.1
 * 0.7  1470     XU4-HC1+ 0.1 (HC2)
 * Use +1% for ADC value tolerance in the array below, the code loops until
 * the measured ADC value is lower than then ADCmax from the array.
 */
struct odroid_rev_info odroid_info[] = {
	{ EXYNOS5_BOARD_ODROID_XU3_REV01, 1, 10, "xu3" },
	{ EXYNOS5_BOARD_ODROID_XU3_REV02, 2, 375, "xu3" },
	{ EXYNOS5_BOARD_ODROID_XU4_REV01, 1, 1293, "xu4" },
	{ EXYNOS5_BOARD_ODROID_HC1_REV01, 1, 1322, "hc1" },
	{ EXYNOS5_BOARD_ODROID_HC2_REV01, 1, 1484, "hc1" },
	{ EXYNOS5_BOARD_ODROID_UNKNOWN, 0, 4095, "unknown" },
};

/*
 * Read ADC at least twice and check the resuls.  If regulator providing voltage
 * on to measured point was just turned on, first reads might require time
 * to stabilize.
 */
static int odroid_get_adc_val(unsigned int *adcval)
{
	unsigned int adcval_prev = 0;
	int ret, retries = 20;

	ret = adc_channel_single_shot("adc@12D10000", CFG_ODROID_REV_AIN,
				      &adcval_prev);
	if (ret)
		return ret;

	while (retries--) {
		mdelay(5);

		ret = adc_channel_single_shot("adc@12D10000",
					      CFG_ODROID_REV_AIN, adcval);
		if (ret)
			return ret;

		/*
		 * If difference between ADC reads is less than 3%,
		 * accept the result
		 */
		if ((100 * abs(*adcval - adcval_prev) / adcval_prev) < 3)
			return ret;

		adcval_prev = *adcval;
	}

	return ret;
}

static int odroid_get_board_type(void)
{
	unsigned int adcval;
	int ret, i;

	ret = odroid_get_adc_val(&adcval);
	if (ret)
		goto rev_default;

	for (i = 0; i < ARRAY_SIZE(odroid_info); i++) {
		/* ADC tolerance: +1% */
		if (adcval < odroid_info[i].adc_val)
			return odroid_info[i].board_type;
	}

rev_default:
	return EXYNOS5_BOARD_ODROID_XU3;
}

/**
 * odroid_get_type_str - returns pointer to one of the board type string.
 * Board types: "xu3", "xu3-lite", "xu4". However the "xu3lite" can be
 * detected only when the i2c controller is ready to use. Fortunately,
 * XU3 and XU3L are compatible, and the information about board lite
 * revision is needed before booting the linux, to set proper environment
 * variable: $fdtfile.
 */
static const char *odroid_get_type_str(void)
{
	const char *type_xu3l = "xu3-lite";
	struct udevice *dev, *chip;
	int i, ret;

	if (gd->board_type != EXYNOS5_BOARD_ODROID_XU3_REV02)
		goto exit;

	ret = pmic_get("s2mps11_pmic@66", &dev);
	if (ret)
		goto exit;

	/* Enable LDO26: 3.0V */
	ret = pmic_reg_write(dev, S2MPS11_REG_L26CTRL,
			     S2MPS11_LDO26_ENABLE);
	if (ret)
		goto exit;

	/* Check XU3Lite by probe INA231 I2C0:0x40 */
	ret = uclass_get_device(UCLASS_I2C, 0, &dev);
	if (ret)
		goto exit;

	ret = dm_i2c_probe(dev, 0x40, 0x0, &chip);
	if (ret)
		return type_xu3l;

exit:
	for (i = 0; i < ARRAY_SIZE(odroid_info); i++) {
		if (odroid_info[i].board_type == gd->board_type)
			return odroid_info[i].name;
	}

	return NULL;
}

bool board_is_odroidxu3(void)
{
	if (gd->board_type >= EXYNOS5_BOARD_ODROID_XU3 &&
	    gd->board_type <= EXYNOS5_BOARD_ODROID_XU3_REV02)
		return true;

	return false;
}

bool board_is_odroidxu4(void)
{
	if (gd->board_type == EXYNOS5_BOARD_ODROID_XU4_REV01)
		return true;

	return false;
}

bool board_is_odroidhc1(void)
{
	if (gd->board_type == EXYNOS5_BOARD_ODROID_HC1_REV01)
		return true;

	return false;
}

bool board_is_odroidhc2(void)
{
	if (gd->board_type == EXYNOS5_BOARD_ODROID_HC2_REV01)
		return true;

	return false;
}

bool board_is_generic(void)
{
	if (gd->board_type == EXYNOS5_BOARD_GENERIC)
		return true;

	return false;
}

#ifdef CONFIG_REVISION_TAG
static unsigned int odroid_get_rev(void)
{
	int i;

	for (i = 0; i < ARRAY_SIZE(odroid_info); i++) {
		if (odroid_info[i].board_type == gd->board_type)
			return odroid_info[i].board_rev;
	}

	return 0;
}

/**
 * get_board_rev() - return detected board revision.
 *
 * @return:  return board revision number for XU3 or 0 for generic
 */
u32 get_board_rev(void)
{
	if (board_is_generic())
		return 0;

	return odroid_get_rev();
}
#endif

/**
 * get_board_type() - returns board type string.
 *
 * @return:  return board type string for XU3 or empty string for generic
 */
const char *get_board_type(void)
{
	const char *generic = "";

	if (board_is_generic())
		return generic;

	return odroid_get_type_str();
}

/**
 * set_board_type() - set board type in gd->board_type.
 * As default type set EXYNOS5_BOARD_GENERIC. If Odroid is detected,
 * set its proper type based on device tree.
 *
 * This might be called early when some more specific ways to detect revision
 * are not yet available.
 */
void set_board_type(void)
{
	const struct udevice_id *of_match = board_ids;
	int ret;

	gd->board_type = EXYNOS5_BOARD_GENERIC;

	while (of_match->compatible) {
		ret = fdt_node_check_compatible(gd->fdt_blob, 0,
						of_match->compatible);
		if (ret)
			of_match++;

		gd->board_type = of_match->data;
		break;
	}
}

/**
 * set_board_revision() - set detailed board type in gd->board_type.
 * Should be called when resources (e.g. regulators) are available
 * so ADC can be used to detect the specific revision of a board.
 */
void set_board_revision(void)
{
	if (board_is_odroidxu3())
		gd->board_type = odroid_get_board_type();
}