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 
12 enum upwr_api_state api_state;
13 enum soc_domain pwr_domain;
14 void *sh_buffer[UPWR_SG_COUNT];
15 struct upwr_code_vers fw_rom_version;
16 struct upwr_code_vers fw_ram_version;
17 u32 fw_launch_option;
18 u32 sg_busy;
19 struct mu_type *mu;
20 upwr_up_max_msg sg_rsp_msg[UPWR_SG_COUNT];
21 upwr_callb user_callback[UPWR_SG_COUNT];
22 UPWR_RX_CALLB_FUNC_T  sgrp_callback[UPWR_SG_COUNT];
23 u32 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 
upwr_ptr2offset(u64 ptr,enum upwr_sg sg,size_t siz,size_t offset,const void * vptr)36 static 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 
upwr_req_status(enum upwr_sg sg,u32 * sgfptr,enum upwr_resp * errptr,int * retptr)49 enum 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 
upwr_copy2tr(struct mu_type * mu,const u32 * msg,u32 size)59 void 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 
upwr_tx(const u32 * msg,u32 size)67 int 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 
upwr_srv_req(enum upwr_sg sg,u32 * msg,u32 size)83 void 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 
upwr_pwm_power_on(const u32 swton[],const u32 memon[],upwr_callb callb)90 int 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 
upwr_poll_req_status(enum upwr_sg sg,u32 * sgfptr,enum upwr_resp * errptr,int * retptr,u32 attempts)122 enum 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 
upwr_xcp_i2c_access(u16 addr,int8_t data_size,uint8_t subaddr_size,u32 subaddr,u32 wdata,const upwr_callb callb)145 int 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 
upwr_xcp_set_ddr_retention(enum soc_domain domain,u32 enable,const upwr_callb callb)178 int 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 
upwr_rx(u32 * msg,u32 * size)198 int 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 
msg_copy(u32 * dest,u32 * src,u32 size)229 void msg_copy(u32 *dest, u32 *src, u32 size)
230 {
231 	*dest = *src;
232 	if (size > 1)
233 		*(dest + 1) = *(src + 1);
234 }
235 
upwr_mu_int_callback(void)236 void 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 */
272 done:
273 	if (rxmsg.hdr.errcode == UPWR_RESP_SHUTDOWN) /* shutdown error: */
274 		api_state = UPWR_API_INITLZED;
275 }
276 
upwr_txrx_isr(void)277 void upwr_txrx_isr(void)
278 {
279 	if (readl(&mu->rsr))
280 		upwr_mu_int_callback();
281 }
282 
upwr_start_callb(void)283 void 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 
upwr_init(enum soc_domain domain,struct mu_type * muptr)334 int 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 
upwr_start(u32 launchopt,const upwr_rdy_callb rdycallb)450 int 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 
upwr_rom_version(u32 * vmajor,u32 * vminor,u32 * vfixes)475 u32 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