1// 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} 486