blob: a5b5b9f3ab352daa0fc8f185bb37b834d3fe2a24 [file] [log] [blame]
Sheetal Tigadoliad0943e2019-12-18 19:44:43 +05301/*
2 * Copyright (c) 2017 - 2020, Broadcom
3 *
4 * SPDX-License-Identifier: BSD-3-Clause
5 */
6
7#include <assert.h>
8#include <errno.h>
9#include <stdint.h>
10
11#include <common/debug.h>
12#include <drivers/delay_timer.h>
13#include <lib/mmio.h>
14
15#include <sr_utils.h>
16#include <swreg.h>
17
18#define MIN_VOLT 760000
19#define MAX_VOLT 1060000
20
21#define BSTI_WRITE 0x1
22#define BSTI_READ 0x2
23#define BSTI_COMMAND_TA 0x2
24#define BSTI_COMMAND_DATA 0xFF
25#define BSTI_CONTROL_VAL 0x81
26#define BSTI_CONTROL_BUSY 0x100
27#define BSTI_TOGGLE_BIT 0x2
28#define BSTI_CONFI_DONE_MASK 0xFFFFFFFD
29#define BSTI_REG_DATA_MASK 0xFFFF
30#define BSTI_CMD(sb, op, pa, ra, ta, data) \
31 ((((sb) & 0x3) << 30) | (((op) & 0x3) << 28) | \
32 (((pa) & 0x1F) << 23) | (((ra) & 0x1F) << 18) | \
33 (((ta) & 0x3) << 16) | (data))
34
35#define PHY_REG0 0x0
36#define PHY_REG1 0x1
37#define PHY_REG4 0x4
38#define PHY_REG5 0x5
39#define PHY_REG6 0x6
40#define PHY_REG7 0x7
41#define PHY_REGC 0xc
42
43#define IHOST_VDDC_DATA 0x560
44#define DDR_CORE_DATA 0x2560
45#define UPDATE_POS_EDGE(data, set) ((data) | ((set) << 1))
46
47/*
48 * Formula for SR A2 reworked board:
49 * step = ((vol/(1.4117 * 0.98)) - 500000)/3125
50 * where,
51 * vol - input voltage
52 * 500000 - Reference voltage
53 * 3125 - one step value
54 */
55#define A2_VOL_REF 500000
56#define ONE_STEP_VALUE 3125
57#define VOL_DIV(vol) (((vol*10000ull)/(14117*98ull)) * 100ull)
58#define STEP_VALUE(vol) \
59 ((((((VOL_DIV(vol)) - A2_VOL_REF) / ONE_STEP_VALUE) & 0xFF) << 8) | 4)
60
61#define B0_VOL_REF ((500000/100)*98)
62#define B0_ONE_STEP_VALUE 3125
63/*
64 * Formula for SR B0 chip for IHOST12/03 and VDDC_CORE
65 * step = ((vol/1.56) - (500000 * 0.98))/3125
66 * where,
67 * vol - input voltage
68 * 500000 - Reference voltage
69 * 3125 - one step value
70 */
71#define B0_VOL_DIV(vol) (((vol)*100ull)/156)
72#define B0_STEP_VALUE(vol) \
73 ((((((B0_VOL_DIV(vol)) - B0_VOL_REF) / B0_ONE_STEP_VALUE) \
74 & 0xFF) << 8) | 4)
75
76/*
77 * Formula for SR B0 chip for DDR-CORE
78 * step = ((vol/1) - (500000 * 0.98))/3125
79 * where,
80 * vol - input voltage
81 * 500000 - Reference voltage
82 * 3125 - one step value
83 */
84#define B0_DDR_VDDC_VOL_DIV(vol) ((vol)/1)
85#define B0_DDR_VDDC_STEP_VALUE(vol) \
86 ((((((B0_DDR_VDDC_VOL_DIV(vol)) - B0_VOL_REF) / B0_ONE_STEP_VALUE) \
87 & 0xFF) << 8) | 4)
88
89#define MAX_SWREG_CNT 8
90#define MAX_ADDR_PER_SWREG 16
91#define MAX_REG_ADDR 0xF
92#define MIN_REG_ADDR 0x0
93
94static const char *sw_reg_name[MAX_SWREG_CNT] = {
95 "DDR_VDDC",
96 "IHOST03",
97 "IHOST12",
98 "IHOST_ARRAY",
99 "DDRIO_SLAVE",
100 "VDDC_CORE",
101 "VDDC1",
102 "DDRIO_MASTER"
103};
104
105/* firmware values for all SWREG for 3.3V input operation */
106static const uint16_t swreg_fm_data_bx[MAX_SWREG_CNT][MAX_ADDR_PER_SWREG] = {
107 /* DDR logic: Power Domains independent of 12v or 3p3v */
108 {0x25E0, 0x2D54, 0x0EC6, 0x01EC, 0x28BB, 0x1144, 0x0200, 0x69C0,
109 0x0010, 0x0EDF, 0x90D7, 0x8000, 0x820C, 0x0003, 0x0001, 0x0000},
110
111 /* ihost03, 3p3V */
112 {0x05E0, 0x39E5, 0x03C1, 0x007C, 0x8BA9, 0x4444, 0x3300, 0x6B80,
113 0x003F, 0x0FFF, 0x90D7, 0x8000, 0x240C, 0x0003, 0x0001, 0x0000},
114
115 /* ihost12 3p3v */
116 {0x05E0, 0x39E5, 0x03C1, 0x007C, 0x8BA9, 0x4444, 0x3300, 0x6B80,
117 0x003F, 0x0FFF, 0x90D7, 0x8000, 0x240C, 0x0003, 0x0001, 0x0000},
118
119 /* ihost array */
120 {0x25E0, 0x2D94, 0x0EC6, 0x01EC, 0x2ABB, 0x1144, 0x0340, 0x69C0,
121 0x0010, 0x0EDF, 0x90D7, 0x8000, 0x860C, 0x0003, 0x0001, 0x0000},
122
123 /* ddr io slave : 3p3v */
124 {0x0560, 0x4438, 0x0000, 0x001F, 0x8028, 0x4444, 0x0300, 0x4380,
125 0x003F, 0x0FFF, 0x10D7, 0x8000, 0xA70C, 0x0003, 0x0001, 0x0000},
126
127 /* core master 3p3v */
128 {0x05E0, 0x39E5, 0x03C1, 0x007C, 0x8BA9, 0x4444, 0x3300, 0x6B80,
129 0x003F, 0x0FFF, 0x90D7, 0x8000, 0x240C, 0x0003, 0x0001, 0x0000},
130
131 /* core slave 3p3v */
132 {0x0560, 0x4438, 0x0000, 0x001F, 0x8028, 0x4444, 0x0300, 0x4380,
133 0x003F, 0x0FFF, 0x10D7, 0x8000, 0x240C, 0x0003, 0x0001, 0x0000},
134
135 /* ddr io master : 3p3v */
136 {0x05E0, 0x39E5, 0x03C1, 0x007C, 0x8BA9, 0x4444, 0x3300, 0x6B80,
137 0x003F, 0x0FFF, 0x90D7, 0x8000, 0xA70C, 0x0003, 0x0001, 0x0000},
138};
139
140#define FM_DATA swreg_fm_data_bx
141
142static int swreg_poll(void)
143{
144 uint32_t data;
145 int retry = 100;
146
147 do {
148 data = mmio_read_32(BSTI_CONTROL_OFFSET);
149 if ((data & BSTI_CONTROL_BUSY) != BSTI_CONTROL_BUSY)
150 return 0;
151 retry--;
152 udelay(1);
153 } while (retry > 0);
154
155 return -ETIMEDOUT;
156}
157
158static int write_swreg_config(enum sw_reg reg_id, uint32_t addr, uint32_t data)
159{
160 uint32_t cmd;
161 int ret;
162
163 cmd = BSTI_CMD(0x1, BSTI_WRITE, reg_id, addr, BSTI_COMMAND_TA, data);
164 mmio_write_32(BSTI_CONTROL_OFFSET, BSTI_CONTROL_VAL);
165 mmio_write_32(BSTI_COMMAND_OFFSET, cmd);
166 ret = swreg_poll();
167 if (ret) {
168 ERROR("Failed to write swreg %s addr 0x%x\n",
169 sw_reg_name[reg_id-1], addr);
170 return ret;
171 }
172 return ret;
173}
174
175static int read_swreg_config(enum sw_reg reg_id, uint32_t addr, uint32_t *data)
176{
177 uint32_t cmd;
178 int ret;
179
180 cmd = BSTI_CMD(0x1, BSTI_READ, reg_id, addr, BSTI_COMMAND_TA, PHY_REG0);
181 mmio_write_32(BSTI_CONTROL_OFFSET, BSTI_CONTROL_VAL);
182 mmio_write_32(BSTI_COMMAND_OFFSET, cmd);
183 ret = swreg_poll();
184 if (ret) {
185 ERROR("Failed to read swreg %s addr 0x%x\n",
186 sw_reg_name[reg_id-1], addr);
187 return ret;
188 }
189
190 *data = mmio_read_32(BSTI_COMMAND_OFFSET);
191 *data &= BSTI_REG_DATA_MASK;
192 return ret;
193}
194
195static int swreg_config_done(enum sw_reg reg_id)
196{
197 uint32_t read_data;
198 int ret;
199
200 ret = read_swreg_config(reg_id, PHY_REG0, &read_data);
201 if (ret)
202 return ret;
203
204 read_data &= BSTI_CONFI_DONE_MASK;
205 read_data |= BSTI_TOGGLE_BIT;
206 ret = write_swreg_config(reg_id, PHY_REG0, read_data);
207 if (ret)
208 return ret;
209
210 ret = read_swreg_config(reg_id, PHY_REG0, &read_data);
211 if (ret)
212 return ret;
213
214 read_data &= BSTI_CONFI_DONE_MASK;
215 ret = write_swreg_config(reg_id, PHY_REG0, read_data);
216 if (ret)
217 return ret;
218
219 return ret;
220}
221
222#ifdef DUMP_SWREG
223static void dump_swreg_firmware(void)
224{
225 enum sw_reg reg_id;
226 uint32_t data;
227 int addr;
228 int ret;
229
230 for (reg_id = DDR_VDDC; reg_id <= DDRIO_MASTER; reg_id++) {
231 INFO("SWREG: %s\n", sw_reg_name[reg_id - 1]);
232 for (addr = MIN_REG_ADDR; addr <= MAX_REG_ADDR; addr++) {
233 ret = read_swreg_config(reg_id, addr, &data);
234 if (ret)
235 ERROR("Failed to read offset %d\n", addr);
236 INFO("\t0x%x: 0x%04x\n", addr, data);
237 }
238 }
239}
240#endif
241
242int set_swreg(enum sw_reg reg_id, uint32_t micro_volts)
243{
244 uint32_t step, programmed_step;
245 uint32_t data = IHOST_VDDC_DATA;
246 int ret;
247
248 if ((micro_volts > MAX_VOLT) || (micro_volts < MIN_VOLT)) {
249 ERROR("input voltage out-of-range\n");
250 ret = -EINVAL;
251 goto failed;
252 }
253
254 ret = read_swreg_config(reg_id, PHY_REGC, &programmed_step);
255 if (ret)
256 goto failed;
257
258 if (reg_id == DDR_VDDC)
259 step = B0_DDR_VDDC_STEP_VALUE(micro_volts);
260 else
261 step = B0_STEP_VALUE(micro_volts);
262
263 if ((step >> 8) != (programmed_step >> 8)) {
264 ret = write_swreg_config(reg_id, PHY_REGC, step);
265 if (ret)
266 goto failed;
267
268 if (reg_id == DDR_VDDC)
269 data = DDR_CORE_DATA;
270
271 ret = write_swreg_config(reg_id, PHY_REG0,
272 UPDATE_POS_EDGE(data, 1));
273 if (ret)
274 goto failed;
275
276 ret = write_swreg_config(reg_id, PHY_REG0,
277 UPDATE_POS_EDGE(data, 0));
278 if (ret)
279 goto failed;
280 }
281
282 INFO("%s voltage updated to %duV\n", sw_reg_name[reg_id-1],
283 micro_volts);
284 return ret;
285
286failed:
287 /*
288 * Stop booting if voltages are not set
289 * correctly. Booting will fail at random point
290 * if we continue with wrong voltage settings.
291 */
292 ERROR("Failed to set %s voltage to %duV\n", sw_reg_name[reg_id-1],
293 micro_volts);
294 assert(0);
295
296 return ret;
297}
298
Elyes Haouas2be03c02023-02-13 09:14:48 +0100299/* Update SWREG firmware for all power domain for A2 chip */
Sheetal Tigadoliad0943e2019-12-18 19:44:43 +0530300int swreg_firmware_update(void)
301{
302 enum sw_reg reg_id;
303 uint32_t data;
304 int addr;
305 int ret;
306
307 /* write firmware values */
308 for (reg_id = DDR_VDDC; reg_id <= DDRIO_MASTER; reg_id++) {
309 /* write higher location first */
310 for (addr = MAX_REG_ADDR; addr >= MIN_REG_ADDR; addr--) {
311 ret = write_swreg_config(reg_id, addr,
312 FM_DATA[reg_id - 1][addr]);
313 if (ret)
314 goto exit;
315 }
316 }
317
318 /* trigger SWREG firmware update */
319 for (reg_id = DDR_VDDC; reg_id <= DDRIO_MASTER; reg_id++) {
320 /*
321 * Slave regulator doesn't have to be updated,
322 * Updating Master is enough
323 */
324 if ((reg_id == DDRIO_SLAVE) || (reg_id == VDDC1))
325 continue;
326
327 ret = swreg_config_done(reg_id);
328 if (ret) {
329 ERROR("Failed to trigger SWREG firmware update for %s\n"
330 , sw_reg_name[reg_id-1]);
331 return ret;
332 }
333 }
334
335 for (reg_id = DDR_VDDC; reg_id <= DDRIO_MASTER; reg_id++) {
336 /*
337 * IHOST_ARRAY will be used on some boards like STRATUS and
338 * there will not be any issue even if it is updated on other
339 * boards where it is not used.
340 */
341 if (reg_id == IHOST_ARRAY)
342 continue;
343
344 for (addr = MIN_REG_ADDR; addr <= MAX_REG_ADDR; addr++) {
345 ret = read_swreg_config(reg_id, addr, &data);
346 if (ret || (!ret &&
347 (data != FM_DATA[reg_id - 1][addr]))) {
348 ERROR("swreg fm update failed: %s at off %d\n",
349 sw_reg_name[reg_id - 1], addr);
350 ERROR("Read val: 0x%x, expected val: 0x%x\n",
351 data, FM_DATA[reg_id - 1][addr]);
352 return -1;
353 }
354 }
355 }
356
357 INFO("Updated SWREG firmware\n");
358
359#ifdef DUMP_SWREG
360 dump_swreg_firmware();
361#endif
362 return ret;
363
364exit:
365 /*
366 * Stop booting if swreg firmware update fails.
367 * Booting will fail at random point if we
368 * continue with wrong voltage settings.
369 */
370 ERROR("Failed to update firmware for %s SWREG\n",
371 sw_reg_name[reg_id-1]);
372 assert(0);
373
374 return ret;
375}