blob: 5e19b9861f026bca71c54772f17720e227ba10ad [file] [log] [blame]
Peng Fanac2cb4d2021-08-07 16:01:10 +08001// SPDX-License-Identifier: BSD-3-Clause
2/*
3 * Copyright 2021 NXP
4 */
5
6#include <linux/types.h>
7#include <string.h>
8#include <asm/arch/imx-regs.h>
9#include <asm/io.h>
10#include "upower_api.h"
11
12enum upwr_api_state api_state;
13enum soc_domain pwr_domain;
14void *sh_buffer[UPWR_SG_COUNT];
15struct upwr_code_vers fw_rom_version;
16struct upwr_code_vers fw_ram_version;
17u32 fw_launch_option;
18u32 sg_busy;
19struct mu_type *mu;
20upwr_up_max_msg sg_rsp_msg[UPWR_SG_COUNT];
21upwr_callb user_callback[UPWR_SG_COUNT];
22UPWR_RX_CALLB_FUNC_T sgrp_callback[UPWR_SG_COUNT];
23u32 sg_rsp_siz[UPWR_SG_COUNT];
24
25#define UPWR_MU_MSG_SIZE (2)
26#define UPWR_SG_BUSY(sg) (sg_busy & (1 << (sg)))
27#define UPWR_USR_CALLB(sg, cb) \
28 do { \
29 user_callback[sg] = cb; \
30 } while (0)
31#define UPWR_MSG_HDR(hdr, sg, fn) \
32 (hdr).domain = (u32)pwr_domain; \
33 (hdr).srvgrp = sg; \
34 (hdr).function = fn
35
36static u32 upwr_ptr2offset(u64 ptr, enum upwr_sg sg, size_t siz, size_t offset, const void *vptr)
37{
38 if (ptr >= UPWR_DRAM_SHARED_BASE_ADDR &&
39 ((ptr - UPWR_DRAM_SHARED_BASE_ADDR) < UPWR_DRAM_SHARED_SIZE)) {
40 return (u32)(ptr - UPWR_DRAM_SHARED_BASE_ADDR);
41 }
42
43 /* pointer is outside the shared memory, copy the struct to buffer */
44 memcpy(offset + (char *)sh_buffer[sg], (void *)vptr, siz);
45
46 return (u32)((u64)sh_buffer[sg] + offset - UPWR_DRAM_SHARED_BASE_ADDR);
47}
48
49enum upwr_req_status upwr_req_status(enum upwr_sg sg, u32 *sgfptr, enum upwr_resp *errptr,
50 int *retptr)
51{
52 enum upwr_req_status status;
53
54 status = (sg_rsp_msg[sg].hdr.errcode == UPWR_RESP_OK) ? UPWR_REQ_OK : UPWR_REQ_ERR;
55
56 return status;
57}
58
59void upwr_copy2tr(struct mu_type *mu, const u32 *msg, u32 size)
60{
61 int i;
62
63 for (i = size - 1; i > -1; i--)
64 writel(msg[i], &mu->tr[i]);
65}
66
67int upwr_tx(const u32 *msg, u32 size)
68{
69 if (size > UPWR_MU_MSG_SIZE)
70 return -2;
71 if (!size)
72 return -2;
73
74 if (readl(&mu->tsr) != UPWR_MU_TSR_EMPTY)
75 return -1; /* not all TE bits in 1: some data to send still */
76
77 upwr_copy2tr(mu, msg, size);
78 writel(1 << (size - 1), &mu->tcr);
79
80 return 0;
81}
82
83void upwr_srv_req(enum upwr_sg sg, u32 *msg, u32 size)
84{
85 sg_busy |= 1 << sg;
86
87 upwr_tx(msg, size);
88}
89
90int upwr_pwm_power_on(const u32 swton[], const u32 memon[], upwr_callb callb)
91{
92 upwr_pwm_pwron_msg txmsg;
93 u64 ptrval; /* needed for X86, ARM64 */
94 size_t stsize = 0;
95
96 if (api_state != UPWR_API_READY)
97 return -3;
98 if (UPWR_SG_BUSY(UPWR_SG_PWRMGMT))
99 return -1;
100
101 UPWR_USR_CALLB(UPWR_SG_PWRMGMT, callb);
102
103 UPWR_MSG_HDR(txmsg.hdr, UPWR_SG_PWRMGMT, UPWR_PWM_PWR_ON);
104
105 if (!swton)
106 txmsg.ptrs.ptr0 = 0; /* NULL pointer -> 0 offset */
107 else
108 txmsg.ptrs.ptr0 = upwr_ptr2offset(ptrval, UPWR_SG_PWRMGMT,
109 (stsize = UPWR_PMC_SWT_WORDS * 4), 0, swton);
110
111 if (!memon)
112 txmsg.ptrs.ptr1 = 0; /* NULL pointer -> 0 offset */
113 else
114 txmsg.ptrs.ptr1 = upwr_ptr2offset(ptrval, UPWR_SG_PWRMGMT, UPWR_PMC_MEM_WORDS * 4,
115 stsize, memon);
116
117 upwr_srv_req(UPWR_SG_PWRMGMT, (u32 *)&txmsg, sizeof(txmsg) / 4);
118
119 return 0;
120}
121
122enum upwr_req_status upwr_poll_req_status(enum upwr_sg sg, u32 *sgfptr,
123 enum upwr_resp *errptr, int *retptr,
124 u32 attempts)
125{
126 u32 i;
127 enum upwr_req_status ret;
128
129 if (!attempts) {
130 ret = UPWR_REQ_BUSY;
131 while (ret == UPWR_REQ_BUSY)
132 ret = upwr_req_status(sg, sgfptr, errptr, retptr);
133 return ret;
134 }
135
136 for (i = 0; i < attempts; i++) {
137 ret = upwr_req_status(sg, sgfptr, errptr, retptr);
138 if (ret != UPWR_REQ_BUSY)
139 break;
140 }
141
142 return ret;
143}
144
145int upwr_xcp_i2c_access(u16 addr, int8_t data_size, uint8_t subaddr_size, u32 subaddr,
146 u32 wdata, const upwr_callb callb)
147{
148 u64 ptrval = (u64)sh_buffer[UPWR_SG_EXCEPT];
149 struct upwr_i2c_access *i2c_acc_ptr = (struct upwr_i2c_access *)ptrval;
150 struct upwr_pointer_msg txmsg;
151
152 if (api_state != UPWR_API_READY)
153 return -3;
154 if (UPWR_SG_BUSY(UPWR_SG_EXCEPT))
155 return -1;
156
157 UPWR_USR_CALLB(UPWR_SG_EXCEPT, callb);
158
159 UPWR_MSG_HDR(txmsg.hdr, UPWR_SG_EXCEPT, UPWR_XCP_I2C);
160
161 i2c_acc_ptr->addr = addr;
162 i2c_acc_ptr->subaddr = subaddr;
163 i2c_acc_ptr->subaddr_size = subaddr_size;
164 i2c_acc_ptr->data = wdata;
165 i2c_acc_ptr->data_size = data_size;
166
167 txmsg.ptr = upwr_ptr2offset(ptrval,
168 UPWR_SG_EXCEPT,
169 (size_t)sizeof(struct upwr_i2c_access),
170 0,
171 i2c_acc_ptr);
172
173 upwr_srv_req(UPWR_SG_EXCEPT, (u32 *)&txmsg, sizeof(txmsg) / 4);
174
175 return 0;
176}
177
178int upwr_xcp_set_ddr_retention(enum soc_domain domain, u32 enable, const upwr_callb callb)
179{
180 union upwr_down_1w_msg txmsg;
181
182 if (api_state != UPWR_API_READY)
183 return -3;
184 if (UPWR_SG_BUSY(UPWR_SG_EXCEPT))
185 return -1;
186
187 UPWR_USR_CALLB(UPWR_SG_EXCEPT, callb);
188
189 UPWR_MSG_HDR(txmsg.hdr, UPWR_SG_EXCEPT, UPWR_XCP_SET_DDR_RETN);
190 txmsg.hdr.domain = (u32)domain;
191 txmsg.hdr.arg = (u32)enable;
192
193 upwr_srv_req(UPWR_SG_EXCEPT, (u32 *)&txmsg, sizeof(txmsg) / 4);
194
195 return 0;
196}
197
198int upwr_rx(u32 *msg, u32 *size)
199{
200 u32 len = readl(&mu->rsr);
201
202 len = (len == 0x0) ? 0 :
203 (len == 0x1) ? 1 :
204 #if UPWR_MU_MSG_SIZE > 1
205 (len == 0x3) ? 2 :
206 #if UPWR_MU_MSG_SIZE > 2
207 (len == 0x7) ? 3 :
208 #if UPWR_MU_MSG_SIZE > 3
209 (len == 0xF) ? 4 :
210 #endif
211 #endif
212 #endif
213 0xFFFFFFFF; /* something wrong */
214
215 if (len == 0xFFFFFFFF)
216 return -3;
217
218 *size = len;
219 if (!len)
220 return -1;
221
222 /* copy the received message to the rx queue, so the interrupts are cleared; */
223 for (u32 i = 0; i < len; i++)
224 msg[i] = readl(&mu->rr[i]);
225
226 return 0;
227}
228
229void msg_copy(u32 *dest, u32 *src, u32 size)
230{
231 *dest = *src;
232 if (size > 1)
233 *(dest + 1) = *(src + 1);
234}
235
236void upwr_mu_int_callback(void)
237{
238 enum upwr_sg sg; /* service group number */
239 UPWR_RX_CALLB_FUNC_T sg_callb; /* service group callback */
240 struct upwr_up_2w_msg rxmsg;
241 u32 size; /* in words */
242
243 if (upwr_rx((u32 *)&rxmsg, &size) < 0) {
244 UPWR_API_ASSERT(0);
245 return;
246 }
247
248 sg = (enum upwr_sg)rxmsg.hdr.srvgrp;
249
250 /* copy msg to the service group buffer */
251 msg_copy((u32 *)&sg_rsp_msg[sg], (u32 *)&rxmsg, size);
252 sg_rsp_siz[sg] = size;
253 sg_busy &= ~(1 << sg);
254
255 sg_callb = sgrp_callback[sg];
256 if (!sg_callb) {
257 upwr_callb user_callb = user_callback[sg];
258
259 /* no service group callback; call the user callback if any */
260 if (!user_callb)
261 goto done; /* no user callback */
262
263 /* make the user callback */
264 user_callb(sg, rxmsg.hdr.function, (enum upwr_resp)rxmsg.hdr.errcode,
265 (int)(size == 2) ? rxmsg.word2 : rxmsg.hdr.ret);
266 goto done;
267 }
268
269 /* finally make the group callback */
270 sg_callb();
271 /* don't uninstall the group callback, it's permanent */
272done:
273 if (rxmsg.hdr.errcode == UPWR_RESP_SHUTDOWN) /* shutdown error: */
274 api_state = UPWR_API_INITLZED;
275}
276
277void upwr_txrx_isr(void)
278{
279 if (readl(&mu->rsr))
280 upwr_mu_int_callback();
281}
282
283void upwr_start_callb(void)
284{
285 switch (api_state) {
286 case UPWR_API_START_WAIT:
287 {
288 upwr_rdy_callb start_callb = (upwr_rdy_callb)user_callback[UPWR_SG_EXCEPT];
289
290 union upwr_ready_msg *msg = (union upwr_ready_msg *)&sg_rsp_msg[UPWR_SG_EXCEPT];
291
292 /* message sanity check */
293 UPWR_API_ASSERT(msg->hdr.srvgrp == UPWR_SG_EXCEPT);
294 UPWR_API_ASSERT(msg->hdr.function == UPWR_XCP_START);
295 UPWR_API_ASSERT(msg->hdr.errcode == UPWR_RESP_OK);
296
297 fw_ram_version.soc_id = fw_rom_version.soc_id;
298 fw_ram_version.vmajor = msg->args.vmajor;
299 fw_ram_version.vminor = msg->args.vminor;
300 fw_ram_version.vfixes = msg->args.vfixes;
301
302 /*
303 * vmajor == vminor == vfixes == 0 indicates start error
304 * in this case, go back to the INITLZED state
305 */
306
307 if (fw_ram_version.vmajor || fw_ram_version.vminor || fw_ram_version.vfixes) {
308 api_state = UPWR_API_READY;
309
310 /* initialization is over: uninstall the callbacks just in case */
311 UPWR_USR_CALLB(UPWR_SG_EXCEPT, NULL);
312 sgrp_callback[UPWR_SG_EXCEPT] = NULL;
313
314 if (!fw_launch_option) {
315 /* launched ROM firmware: RAM fw versions must be all 0s */
316 fw_ram_version.vmajor =
317 fw_ram_version.vminor =
318 fw_ram_version.vfixes = 0;
319 }
320 } else {
321 api_state = UPWR_API_INITLZED;
322 }
323
324 start_callb(msg->args.vmajor, msg->args.vminor, msg->args.vfixes);
325 }
326 break;
327
328 default:
329 UPWR_API_ASSERT(0);
330 break;
331 }
332}
333
334int upwr_init(enum soc_domain domain, struct mu_type *muptr)
335{
336 u32 dom_buffer_base = ((UPWR_API_BUFFER_ENDPLUS + UPWR_API_BUFFER_BASE) / 2);
337 union upwr_init_msg *msg = (union upwr_init_msg *)&sg_rsp_msg[UPWR_SG_EXCEPT];
338 enum upwr_sg sg; /* service group number */
339 u32 size; /* in words */
340 int j;
341
342 mu = muptr;
343 writel(0, &mu->tcr);
344 writel(0, &mu->rcr);
345
346 api_state = UPWR_API_INIT_WAIT;
347 pwr_domain = domain;
348 sg_busy = 0;
349
350 /* initialize the versions, in case they are polled */
351 fw_rom_version.soc_id =
352 fw_rom_version.vmajor =
353 fw_rom_version.vminor =
354 fw_rom_version.vfixes = 0;
355
356 fw_ram_version.soc_id =
357 fw_ram_version.vmajor =
358 fw_ram_version.vminor =
359 fw_ram_version.vfixes = 0;
360
361 sh_buffer[UPWR_SG_EXCEPT] = (void *)(ulong)dom_buffer_base;
362 sh_buffer[UPWR_SG_PWRMGMT] = (void *)(ulong)(dom_buffer_base +
363 sizeof(union upwr_xcp_union));
364 sh_buffer[UPWR_SG_DELAYM] = NULL;
365 sh_buffer[UPWR_SG_VOLTM] = NULL;
366 sh_buffer[UPWR_SG_CURRM] = NULL;
367 sh_buffer[UPWR_SG_TEMPM] = NULL;
368 sh_buffer[UPWR_SG_DIAG] = NULL;
369 /* (no buffers service groups other than xcp and pwm for now) */
370
371 for (j = 0; j < UPWR_SG_COUNT; j++) {
372 user_callback[j] = NULL;
373 /* service group Exception gets the initialization callbacks */
374 sgrp_callback[j] = (j == UPWR_SG_EXCEPT) ? upwr_start_callb : NULL;
375
376 /* response messages with an initial consistent content */
377 sg_rsp_msg[j].hdr.errcode = UPWR_RESP_SHUTDOWN;
378 }
379
380 if (readl(&mu->fsr) & BIT(0)) {
381 /* send a ping message down to get the ROM version back */
382 upwr_xcp_ping_msg ping_msg;
383
384 ping_msg.hdr.domain = pwr_domain;
385 ping_msg.hdr.srvgrp = UPWR_SG_EXCEPT;
386 ping_msg.hdr.function = UPWR_XCP_PING;
387
388 if (readl(&mu->rsr) & BIT(0)) /* first clean any Rx message left over */
389 upwr_rx((u32 *)msg, &size);
390
391 while (readl(&mu->tsr) != UPWR_MU_TSR_EMPTY)
392 ;
393
394 /*
395 * now send the ping message;
396 * do not use upwr_tx, which needs API initilized;
397 * just write to the MU TR register(s)
398 */
399 setbits_le32(&mu->fcr, BIT(0)); /* flag urgency status */
400 upwr_copy2tr(mu, (u32 *)&ping_msg, sizeof(ping_msg) / 4);
401 }
402
403 do {
404 /* poll for the MU Rx status: wait for an init message, either
405 * 1st sent from uPower after reset or as a response to a ping
406 */
407 while (!readl(&mu->rsr) & BIT(0))
408 ;
409
410 clrbits_le32(&mu->fcr, BIT(0));
411
412 if (upwr_rx((u32 *)msg, &size) < 0)
413 return -4;
414
415 if (size != (sizeof(union upwr_init_msg) / 4)) {
416 if (readl(&mu->fsr) & BIT(0))
417 continue; /* discard left over msg */
418 else
419 return -4;
420 }
421
422 sg = (enum upwr_sg)msg->hdr.srvgrp;
423 if (sg != UPWR_SG_EXCEPT) {
424 if (readl(&mu->fsr) & BIT(0))
425 continue;
426 else
427 return -4;
428 }
429
430 if ((enum upwr_xcp_f)msg->hdr.function != UPWR_XCP_INIT) {
431 if (readl(&mu->fsr) & BIT(0))
432 continue;
433 else
434 return -4;
435 }
436
437 break;
438 } while (true);
439
440 fw_rom_version.soc_id = msg->args.soc;
441 fw_rom_version.vmajor = msg->args.vmajor;
442 fw_rom_version.vminor = msg->args.vminor;
443 fw_rom_version.vfixes = msg->args.vfixes;
444
445 api_state = UPWR_API_INITLZED;
446
447 return 0;
448} /* upwr_init */
449
450int upwr_start(u32 launchopt, const upwr_rdy_callb rdycallb)
451{
452 upwr_start_msg txmsg;
453
454 if (api_state != UPWR_API_INITLZED)
455 return -3;
456
457 UPWR_USR_CALLB(UPWR_SG_EXCEPT, (upwr_callb)rdycallb);
458
459 UPWR_MSG_HDR(txmsg.hdr, UPWR_SG_EXCEPT, UPWR_XCP_START);
460
461 txmsg.hdr.arg = launchopt;
462 fw_launch_option = launchopt;
463
464 if (upwr_tx((u32 *)&txmsg, sizeof(txmsg) / 4) < 0) {
465 /* catastrophic error, but is it possible to happen? */
466 UPWR_API_ASSERT(0);
467 return -1;
468 }
469
470 api_state = UPWR_API_START_WAIT;
471
472 return 0;
473}
474
475u32 upwr_rom_version(u32 *vmajor, u32 *vminor, u32 *vfixes)
476{
477 u32 soc;
478
479 soc = fw_rom_version.soc_id;
480 *vmajor = fw_rom_version.vmajor;
481 *vminor = fw_rom_version.vminor;
482 *vfixes = fw_rom_version.vfixes;
483
484 return soc;
485}