summaryrefslogtreecommitdiffstats
path: root/plat/brcm/board/stingray/driver/swreg.c
blob: 2b7c53b53a606276bb84076d0dfa6e0e86546efc (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
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
/*
 * Copyright (c) 2017 - 2020, Broadcom
 *
 * SPDX-License-Identifier: BSD-3-Clause
 */

#include <assert.h>
#include <errno.h>
#include <stdint.h>

#include <common/debug.h>
#include <drivers/delay_timer.h>
#include <lib/mmio.h>

#include <sr_utils.h>
#include <swreg.h>

#define MIN_VOLT                760000
#define MAX_VOLT                1060000

#define BSTI_WRITE              0x1
#define BSTI_READ               0x2
#define BSTI_COMMAND_TA         0x2
#define BSTI_COMMAND_DATA       0xFF
#define BSTI_CONTROL_VAL        0x81
#define BSTI_CONTROL_BUSY       0x100
#define BSTI_TOGGLE_BIT         0x2
#define BSTI_CONFI_DONE_MASK    0xFFFFFFFD
#define BSTI_REG_DATA_MASK      0xFFFF
#define BSTI_CMD(sb, op, pa, ra, ta, data) \
	((((sb) & 0x3) << 30) | (((op) & 0x3) << 28) | \
	(((pa) & 0x1F) << 23) | (((ra) & 0x1F) << 18) | \
	(((ta) & 0x3) << 16) | (data))

#define PHY_REG0        0x0
#define PHY_REG1        0x1
#define PHY_REG4        0x4
#define PHY_REG5        0x5
#define PHY_REG6        0x6
#define PHY_REG7        0x7
#define PHY_REGC        0xc

#define IHOST_VDDC_DATA 0x560
#define DDR_CORE_DATA   0x2560
#define UPDATE_POS_EDGE(data, set)    ((data) | ((set) << 1))

/*
 * Formula for SR A2 reworked board:
 * step = ((vol/(1.4117 * 0.98)) - 500000)/3125
 * where,
 *      vol    - input voltage
 *      500000 - Reference voltage
 *      3125   - one step value
 */
#define A2_VOL_REF         500000
#define ONE_STEP_VALUE  3125
#define VOL_DIV(vol)    (((vol*10000ull)/(14117*98ull)) * 100ull)
#define STEP_VALUE(vol) \
	((((((VOL_DIV(vol)) - A2_VOL_REF) / ONE_STEP_VALUE) & 0xFF) << 8) | 4)

#define B0_VOL_REF         ((500000/100)*98)
#define B0_ONE_STEP_VALUE  3125
/*
 * Formula for SR B0 chip for IHOST12/03 and VDDC_CORE
 * step = ((vol/1.56) - (500000 * 0.98))/3125
 * where,
 *      vol    - input voltage
 *      500000 - Reference voltage
 *      3125   - one step value
 */
#define B0_VOL_DIV(vol)    (((vol)*100ull)/156)
#define B0_STEP_VALUE(vol) \
	((((((B0_VOL_DIV(vol)) - B0_VOL_REF) / B0_ONE_STEP_VALUE) \
		& 0xFF) << 8) | 4)

/*
 * Formula for SR B0 chip for DDR-CORE
 * step = ((vol/1) - (500000 * 0.98))/3125
 * where,
 *      vol    - input voltage
 *      500000 - Reference voltage
 *      3125   - one step value
 */
#define B0_DDR_VDDC_VOL_DIV(vol)    ((vol)/1)
#define B0_DDR_VDDC_STEP_VALUE(vol) \
	((((((B0_DDR_VDDC_VOL_DIV(vol)) - B0_VOL_REF) / B0_ONE_STEP_VALUE) \
		& 0xFF) << 8) | 4)

#define MAX_SWREG_CNT       8
#define MAX_ADDR_PER_SWREG  16
#define MAX_REG_ADDR        0xF
#define MIN_REG_ADDR        0x0

static const char *sw_reg_name[MAX_SWREG_CNT] = {
	"DDR_VDDC",
	"IHOST03",
	"IHOST12",
	"IHOST_ARRAY",
	"DDRIO_SLAVE",
	"VDDC_CORE",
	"VDDC1",
	"DDRIO_MASTER"
};

/* firmware values for all SWREG for 3.3V input operation */
static const uint16_t swreg_fm_data_bx[MAX_SWREG_CNT][MAX_ADDR_PER_SWREG] = {
	/* DDR logic: Power Domains independent of 12v or 3p3v */
	{0x25E0, 0x2D54, 0x0EC6, 0x01EC, 0x28BB, 0x1144, 0x0200, 0x69C0,
	 0x0010, 0x0EDF, 0x90D7, 0x8000, 0x820C, 0x0003, 0x0001, 0x0000},

	/* ihost03, 3p3V */
	{0x05E0, 0x39E5, 0x03C1, 0x007C, 0x8BA9, 0x4444, 0x3300, 0x6B80,
	 0x003F, 0x0FFF, 0x90D7, 0x8000, 0x240C, 0x0003, 0x0001, 0x0000},

	/* ihost12 3p3v */
	{0x05E0, 0x39E5, 0x03C1, 0x007C, 0x8BA9, 0x4444, 0x3300, 0x6B80,
	 0x003F, 0x0FFF, 0x90D7, 0x8000, 0x240C, 0x0003, 0x0001, 0x0000},

	/* ihost array */
	{0x25E0, 0x2D94, 0x0EC6, 0x01EC, 0x2ABB, 0x1144, 0x0340, 0x69C0,
	 0x0010, 0x0EDF, 0x90D7, 0x8000, 0x860C, 0x0003, 0x0001, 0x0000},

	/* ddr io slave : 3p3v */
	{0x0560, 0x4438, 0x0000, 0x001F, 0x8028, 0x4444, 0x0300, 0x4380,
	 0x003F, 0x0FFF, 0x10D7, 0x8000, 0xA70C, 0x0003, 0x0001, 0x0000},

	/* core master 3p3v */
	{0x05E0, 0x39E5, 0x03C1, 0x007C, 0x8BA9, 0x4444, 0x3300, 0x6B80,
	 0x003F, 0x0FFF, 0x90D7, 0x8000, 0x240C, 0x0003, 0x0001, 0x0000},

	/* core slave 3p3v */
	{0x0560, 0x4438, 0x0000, 0x001F, 0x8028, 0x4444, 0x0300, 0x4380,
	 0x003F, 0x0FFF, 0x10D7, 0x8000, 0x240C, 0x0003, 0x0001, 0x0000},

	/* ddr io master : 3p3v */
	{0x05E0, 0x39E5, 0x03C1, 0x007C, 0x8BA9, 0x4444, 0x3300, 0x6B80,
	 0x003F, 0x0FFF, 0x90D7, 0x8000, 0xA70C, 0x0003, 0x0001, 0x0000},
};

#define FM_DATA swreg_fm_data_bx

static int swreg_poll(void)
{
	uint32_t data;
	int retry = 100;

	do {
		data = mmio_read_32(BSTI_CONTROL_OFFSET);
		if ((data & BSTI_CONTROL_BUSY) != BSTI_CONTROL_BUSY)
			return 0;
		retry--;
		udelay(1);
	} while (retry > 0);

	return -ETIMEDOUT;
}

static int write_swreg_config(enum sw_reg reg_id, uint32_t addr, uint32_t data)
{
	uint32_t cmd;
	int ret;

	cmd = BSTI_CMD(0x1, BSTI_WRITE, reg_id, addr, BSTI_COMMAND_TA, data);
	mmio_write_32(BSTI_CONTROL_OFFSET, BSTI_CONTROL_VAL);
	mmio_write_32(BSTI_COMMAND_OFFSET, cmd);
	ret = swreg_poll();
	if (ret) {
		ERROR("Failed to write swreg %s addr 0x%x\n",
			sw_reg_name[reg_id-1], addr);
		return ret;
	}
	return ret;
}

static int read_swreg_config(enum sw_reg reg_id, uint32_t addr, uint32_t *data)
{
	uint32_t cmd;
	int ret;

	cmd = BSTI_CMD(0x1, BSTI_READ, reg_id, addr, BSTI_COMMAND_TA, PHY_REG0);
	mmio_write_32(BSTI_CONTROL_OFFSET, BSTI_CONTROL_VAL);
	mmio_write_32(BSTI_COMMAND_OFFSET, cmd);
	ret = swreg_poll();
	if (ret) {
		ERROR("Failed to read swreg %s addr 0x%x\n",
			sw_reg_name[reg_id-1], addr);
		return ret;
	}

	*data = mmio_read_32(BSTI_COMMAND_OFFSET);
	*data &= BSTI_REG_DATA_MASK;
	return ret;
}

static int swreg_config_done(enum sw_reg reg_id)
{
	uint32_t read_data;
	int ret;

	ret = read_swreg_config(reg_id, PHY_REG0, &read_data);
	if (ret)
		return ret;

	read_data &= BSTI_CONFI_DONE_MASK;
	read_data |= BSTI_TOGGLE_BIT;
	ret = write_swreg_config(reg_id, PHY_REG0, read_data);
	if (ret)
		return ret;

	ret = read_swreg_config(reg_id, PHY_REG0, &read_data);
	if (ret)
		return ret;

	read_data &= BSTI_CONFI_DONE_MASK;
	ret = write_swreg_config(reg_id, PHY_REG0, read_data);
	if (ret)
		return ret;

	return ret;
}

#ifdef DUMP_SWREG
static void dump_swreg_firmware(void)
{
	enum sw_reg reg_id;
	uint32_t data;
	int addr;
	int ret;

	for (reg_id = DDR_VDDC; reg_id <= DDRIO_MASTER; reg_id++) {
		INFO("SWREG: %s\n", sw_reg_name[reg_id - 1]);
		for (addr = MIN_REG_ADDR; addr <= MAX_REG_ADDR; addr++) {
			ret = read_swreg_config(reg_id, addr, &data);
			if (ret)
				ERROR("Failed to read offset %d\n", addr);
			INFO("\t0x%x: 0x%04x\n", addr, data);
		}
	}
}
#endif

int set_swreg(enum sw_reg reg_id, uint32_t micro_volts)
{
	uint32_t step, programmed_step;
	uint32_t data = IHOST_VDDC_DATA;
	int ret;

	if ((micro_volts > MAX_VOLT) || (micro_volts < MIN_VOLT)) {
		ERROR("input voltage out-of-range\n");
		ret = -EINVAL;
		goto failed;
	}

	ret = read_swreg_config(reg_id, PHY_REGC, &programmed_step);
	if (ret)
		goto failed;

	if (reg_id == DDR_VDDC)
		step = B0_DDR_VDDC_STEP_VALUE(micro_volts);
	else
		step = B0_STEP_VALUE(micro_volts);

	if ((step >> 8) != (programmed_step >> 8)) {
		ret = write_swreg_config(reg_id, PHY_REGC, step);
		if (ret)
			goto failed;

		if (reg_id == DDR_VDDC)
			data = DDR_CORE_DATA;

		ret = write_swreg_config(reg_id, PHY_REG0,
					UPDATE_POS_EDGE(data, 1));
		if (ret)
			goto failed;

		ret = write_swreg_config(reg_id, PHY_REG0,
					UPDATE_POS_EDGE(data, 0));
		if (ret)
			goto failed;
	}

	INFO("%s voltage updated to %duV\n", sw_reg_name[reg_id-1],
		micro_volts);
	return ret;

failed:
	/*
	 * Stop booting if voltages are not set
	 * correctly. Booting will fail at random point
	 * if we continue with wrong voltage settings.
	 */
	ERROR("Failed to set %s voltage to %duV\n", sw_reg_name[reg_id-1],
		micro_volts);
	assert(0);

	return ret;
}

/* Update SWREG firmware for all power doman for A2 chip */
int swreg_firmware_update(void)
{
	enum sw_reg reg_id;
	uint32_t data;
	int addr;
	int ret;

	/* write firmware values */
	for (reg_id = DDR_VDDC; reg_id <= DDRIO_MASTER; reg_id++) {
		/* write higher location first */
		for (addr = MAX_REG_ADDR; addr >= MIN_REG_ADDR; addr--) {
			ret = write_swreg_config(reg_id, addr,
						 FM_DATA[reg_id - 1][addr]);
			if (ret)
				goto exit;
		}
	}

	/* trigger SWREG firmware update */
	for (reg_id = DDR_VDDC; reg_id <= DDRIO_MASTER; reg_id++) {
		/*
		 * Slave regulator doesn't have to be updated,
		 * Updating Master is enough
		 */
		if ((reg_id == DDRIO_SLAVE) || (reg_id == VDDC1))
			continue;

		ret = swreg_config_done(reg_id);
		if (ret) {
			ERROR("Failed to trigger SWREG firmware update for %s\n"
				, sw_reg_name[reg_id-1]);
			return ret;
		}
	}

	for (reg_id = DDR_VDDC; reg_id <= DDRIO_MASTER; reg_id++) {
		/*
		 * IHOST_ARRAY will be used on some boards like STRATUS and
		 * there will not be any issue even if it is updated on other
		 * boards where it is not used.
		 */
		if (reg_id == IHOST_ARRAY)
			continue;

		for (addr = MIN_REG_ADDR; addr <= MAX_REG_ADDR; addr++) {
			ret = read_swreg_config(reg_id, addr, &data);
			if (ret || (!ret &&
				(data != FM_DATA[reg_id - 1][addr]))) {
				ERROR("swreg fm update failed: %s at off %d\n",
					sw_reg_name[reg_id - 1], addr);
				ERROR("Read val: 0x%x, expected val: 0x%x\n",
					data, FM_DATA[reg_id - 1][addr]);
				return -1;
			}
		}
	}

	INFO("Updated SWREG firmware\n");

#ifdef DUMP_SWREG
	dump_swreg_firmware();
#endif
	return ret;

exit:
	/*
	 * Stop booting if swreg firmware update fails.
	 * Booting will fail at random point if we
	 * continue with wrong voltage settings.
	 */
	ERROR("Failed to update firmware for %s SWREG\n",
		sw_reg_name[reg_id-1]);
	assert(0);

	return ret;
}