mirror of
				https://github.com/smaeul/u-boot.git
				synced 2025-11-04 14:00:19 +00:00 
			
		
		
		
	Add upower api support, this is modified from upower firmware exported package. Signed-off-by: Peng Fan <peng.fan@nxp.com>
		
			
				
	
	
		
			486 lines
		
	
	
		
			12 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			486 lines
		
	
	
		
			12 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
// SPDX-License-Identifier: BSD-3-Clause
 | 
						|
/*
 | 
						|
 * Copyright 2021 NXP
 | 
						|
 */
 | 
						|
 | 
						|
#include <linux/types.h>
 | 
						|
#include <string.h>
 | 
						|
#include <asm/arch/imx-regs.h>
 | 
						|
#include <asm/io.h>
 | 
						|
#include "upower_api.h"
 | 
						|
 | 
						|
enum upwr_api_state api_state;
 | 
						|
enum soc_domain pwr_domain;
 | 
						|
void *sh_buffer[UPWR_SG_COUNT];
 | 
						|
struct upwr_code_vers fw_rom_version;
 | 
						|
struct upwr_code_vers fw_ram_version;
 | 
						|
u32 fw_launch_option;
 | 
						|
u32 sg_busy;
 | 
						|
struct mu_type *mu;
 | 
						|
upwr_up_max_msg sg_rsp_msg[UPWR_SG_COUNT];
 | 
						|
upwr_callb user_callback[UPWR_SG_COUNT];
 | 
						|
UPWR_RX_CALLB_FUNC_T  sgrp_callback[UPWR_SG_COUNT];
 | 
						|
u32 sg_rsp_siz[UPWR_SG_COUNT];
 | 
						|
 | 
						|
#define UPWR_MU_MSG_SIZE            (2)
 | 
						|
#define UPWR_SG_BUSY(sg) (sg_busy & (1 << (sg)))
 | 
						|
#define UPWR_USR_CALLB(sg, cb)		\
 | 
						|
	do {				\
 | 
						|
		user_callback[sg] = cb;	\
 | 
						|
	} while (0)
 | 
						|
#define UPWR_MSG_HDR(hdr, sg, fn)		\
 | 
						|
	(hdr).domain   = (u32)pwr_domain;	\
 | 
						|
	(hdr).srvgrp   = sg;			\
 | 
						|
	(hdr).function = fn
 | 
						|
 | 
						|
static u32 upwr_ptr2offset(u64 ptr, enum upwr_sg sg, size_t siz, size_t offset, const void *vptr)
 | 
						|
{
 | 
						|
	if (ptr >= UPWR_DRAM_SHARED_BASE_ADDR &&
 | 
						|
	    ((ptr - UPWR_DRAM_SHARED_BASE_ADDR) < UPWR_DRAM_SHARED_SIZE)) {
 | 
						|
		return (u32)(ptr - UPWR_DRAM_SHARED_BASE_ADDR);
 | 
						|
	}
 | 
						|
 | 
						|
	/* pointer is outside the shared memory, copy the struct to buffer */
 | 
						|
	memcpy(offset + (char *)sh_buffer[sg], (void *)vptr, siz);
 | 
						|
 | 
						|
	return (u32)((u64)sh_buffer[sg] + offset - UPWR_DRAM_SHARED_BASE_ADDR);
 | 
						|
}
 | 
						|
 | 
						|
enum upwr_req_status upwr_req_status(enum upwr_sg sg, u32 *sgfptr, enum upwr_resp *errptr,
 | 
						|
				     int *retptr)
 | 
						|
{
 | 
						|
	enum upwr_req_status status;
 | 
						|
 | 
						|
	status = (sg_rsp_msg[sg].hdr.errcode == UPWR_RESP_OK) ? UPWR_REQ_OK : UPWR_REQ_ERR;
 | 
						|
 | 
						|
	return status;
 | 
						|
}
 | 
						|
 | 
						|
void upwr_copy2tr(struct mu_type *mu, const u32 *msg, u32 size)
 | 
						|
{
 | 
						|
	int i;
 | 
						|
 | 
						|
	for (i = size - 1; i > -1; i--)
 | 
						|
		writel(msg[i], &mu->tr[i]);
 | 
						|
}
 | 
						|
 | 
						|
int upwr_tx(const u32 *msg, u32 size)
 | 
						|
{
 | 
						|
	if (size > UPWR_MU_MSG_SIZE)
 | 
						|
		return -2;
 | 
						|
	if (!size)
 | 
						|
		return -2;
 | 
						|
 | 
						|
	if (readl(&mu->tsr) != UPWR_MU_TSR_EMPTY)
 | 
						|
		return -1;  /* not all TE bits in 1: some data to send still */
 | 
						|
 | 
						|
	upwr_copy2tr(mu, msg, size);
 | 
						|
	writel(1 << (size - 1), &mu->tcr);
 | 
						|
 | 
						|
	return 0;
 | 
						|
}
 | 
						|
 | 
						|
void upwr_srv_req(enum upwr_sg sg, u32 *msg, u32 size)
 | 
						|
{
 | 
						|
	sg_busy |= 1 << sg;
 | 
						|
 | 
						|
	upwr_tx(msg, size);
 | 
						|
}
 | 
						|
 | 
						|
int upwr_pwm_power_on(const u32 swton[], const u32 memon[], upwr_callb callb)
 | 
						|
{
 | 
						|
	upwr_pwm_pwron_msg txmsg;
 | 
						|
	u64 ptrval; /* needed for X86, ARM64 */
 | 
						|
	size_t stsize = 0;
 | 
						|
 | 
						|
	if (api_state != UPWR_API_READY)
 | 
						|
		return -3;
 | 
						|
	if (UPWR_SG_BUSY(UPWR_SG_PWRMGMT))
 | 
						|
		return -1;
 | 
						|
 | 
						|
	UPWR_USR_CALLB(UPWR_SG_PWRMGMT, callb);
 | 
						|
 | 
						|
	UPWR_MSG_HDR(txmsg.hdr, UPWR_SG_PWRMGMT, UPWR_PWM_PWR_ON);
 | 
						|
 | 
						|
	if (!swton)
 | 
						|
		txmsg.ptrs.ptr0 = 0; /* NULL pointer -> 0 offset */
 | 
						|
	else
 | 
						|
		txmsg.ptrs.ptr0 = upwr_ptr2offset(ptrval, UPWR_SG_PWRMGMT,
 | 
						|
						  (stsize = UPWR_PMC_SWT_WORDS * 4), 0, swton);
 | 
						|
 | 
						|
	if (!memon)
 | 
						|
		txmsg.ptrs.ptr1 = 0; /* NULL pointer -> 0 offset */
 | 
						|
	else
 | 
						|
		txmsg.ptrs.ptr1 = upwr_ptr2offset(ptrval, UPWR_SG_PWRMGMT, UPWR_PMC_MEM_WORDS * 4,
 | 
						|
						  stsize, memon);
 | 
						|
 | 
						|
	upwr_srv_req(UPWR_SG_PWRMGMT, (u32 *)&txmsg, sizeof(txmsg) / 4);
 | 
						|
 | 
						|
	return 0;
 | 
						|
}
 | 
						|
 | 
						|
enum upwr_req_status upwr_poll_req_status(enum upwr_sg sg, u32 *sgfptr,
 | 
						|
					  enum upwr_resp *errptr, int *retptr,
 | 
						|
					  u32 attempts)
 | 
						|
{
 | 
						|
	u32 i;
 | 
						|
	enum upwr_req_status ret;
 | 
						|
 | 
						|
	if (!attempts) {
 | 
						|
		ret = UPWR_REQ_BUSY;
 | 
						|
		while (ret == UPWR_REQ_BUSY)
 | 
						|
			ret = upwr_req_status(sg, sgfptr, errptr, retptr);
 | 
						|
		return ret;
 | 
						|
	}
 | 
						|
 | 
						|
	for (i = 0; i < attempts; i++) {
 | 
						|
		ret = upwr_req_status(sg, sgfptr, errptr, retptr);
 | 
						|
		if (ret != UPWR_REQ_BUSY)
 | 
						|
			break;
 | 
						|
	}
 | 
						|
 | 
						|
	return ret;
 | 
						|
}
 | 
						|
 | 
						|
int upwr_xcp_i2c_access(u16 addr, int8_t data_size, uint8_t subaddr_size, u32 subaddr,
 | 
						|
			u32 wdata, const upwr_callb callb)
 | 
						|
{
 | 
						|
	u64 ptrval = (u64)sh_buffer[UPWR_SG_EXCEPT];
 | 
						|
	struct upwr_i2c_access *i2c_acc_ptr = (struct upwr_i2c_access *)ptrval;
 | 
						|
	struct upwr_pointer_msg txmsg;
 | 
						|
 | 
						|
	if (api_state != UPWR_API_READY)
 | 
						|
		return -3;
 | 
						|
	if (UPWR_SG_BUSY(UPWR_SG_EXCEPT))
 | 
						|
		return -1;
 | 
						|
 | 
						|
	UPWR_USR_CALLB(UPWR_SG_EXCEPT, callb);
 | 
						|
 | 
						|
	UPWR_MSG_HDR(txmsg.hdr, UPWR_SG_EXCEPT, UPWR_XCP_I2C);
 | 
						|
 | 
						|
	i2c_acc_ptr->addr = addr;
 | 
						|
	i2c_acc_ptr->subaddr = subaddr;
 | 
						|
	i2c_acc_ptr->subaddr_size = subaddr_size;
 | 
						|
	i2c_acc_ptr->data = wdata;
 | 
						|
	i2c_acc_ptr->data_size = data_size;
 | 
						|
 | 
						|
	txmsg.ptr = upwr_ptr2offset(ptrval,
 | 
						|
				    UPWR_SG_EXCEPT,
 | 
						|
				    (size_t)sizeof(struct upwr_i2c_access),
 | 
						|
				    0,
 | 
						|
				    i2c_acc_ptr);
 | 
						|
 | 
						|
	upwr_srv_req(UPWR_SG_EXCEPT, (u32 *)&txmsg, sizeof(txmsg) / 4);
 | 
						|
 | 
						|
	return 0;
 | 
						|
}
 | 
						|
 | 
						|
int upwr_xcp_set_ddr_retention(enum soc_domain domain, u32 enable, const upwr_callb callb)
 | 
						|
{
 | 
						|
	union upwr_down_1w_msg txmsg;
 | 
						|
 | 
						|
	if (api_state != UPWR_API_READY)
 | 
						|
		return -3;
 | 
						|
	if (UPWR_SG_BUSY(UPWR_SG_EXCEPT))
 | 
						|
		return -1;
 | 
						|
 | 
						|
	UPWR_USR_CALLB(UPWR_SG_EXCEPT, callb);
 | 
						|
 | 
						|
	UPWR_MSG_HDR(txmsg.hdr, UPWR_SG_EXCEPT, UPWR_XCP_SET_DDR_RETN);
 | 
						|
	txmsg.hdr.domain = (u32)domain;
 | 
						|
	txmsg.hdr.arg    = (u32)enable;
 | 
						|
 | 
						|
	upwr_srv_req(UPWR_SG_EXCEPT, (u32 *)&txmsg, sizeof(txmsg) / 4);
 | 
						|
 | 
						|
	return 0;
 | 
						|
}
 | 
						|
 | 
						|
int upwr_rx(u32 *msg, u32 *size)
 | 
						|
{
 | 
						|
	u32 len = readl(&mu->rsr);
 | 
						|
 | 
						|
	len = (len == 0x0) ? 0 :
 | 
						|
	      (len == 0x1) ? 1 :
 | 
						|
	      #if UPWR_MU_MSG_SIZE > 1
 | 
						|
	      (len == 0x3) ? 2 :
 | 
						|
	      #if UPWR_MU_MSG_SIZE > 2
 | 
						|
	      (len == 0x7) ? 3 :
 | 
						|
	      #if UPWR_MU_MSG_SIZE > 3
 | 
						|
	      (len == 0xF) ? 4 :
 | 
						|
	      #endif
 | 
						|
	      #endif
 | 
						|
	      #endif
 | 
						|
	      0xFFFFFFFF; /* something wrong */
 | 
						|
 | 
						|
	if (len == 0xFFFFFFFF)
 | 
						|
		return -3;
 | 
						|
 | 
						|
	*size = len;
 | 
						|
	if (!len)
 | 
						|
		return -1;
 | 
						|
 | 
						|
	/* copy the received message to the rx queue, so the interrupts are cleared; */
 | 
						|
	for (u32 i = 0; i < len; i++)
 | 
						|
		msg[i] = readl(&mu->rr[i]);
 | 
						|
 | 
						|
	return 0;
 | 
						|
}
 | 
						|
 | 
						|
void msg_copy(u32 *dest, u32 *src, u32 size)
 | 
						|
{
 | 
						|
	*dest = *src;
 | 
						|
	if (size > 1)
 | 
						|
		*(dest + 1) = *(src + 1);
 | 
						|
}
 | 
						|
 | 
						|
void upwr_mu_int_callback(void)
 | 
						|
{
 | 
						|
	enum upwr_sg sg;	/* service group number */
 | 
						|
	UPWR_RX_CALLB_FUNC_T sg_callb; /* service group callback */
 | 
						|
	struct upwr_up_2w_msg rxmsg;
 | 
						|
	u32 size;	/* in words */
 | 
						|
 | 
						|
	if (upwr_rx((u32 *)&rxmsg, &size) < 0) {
 | 
						|
		UPWR_API_ASSERT(0);
 | 
						|
		return;
 | 
						|
	}
 | 
						|
 | 
						|
	sg = (enum upwr_sg)rxmsg.hdr.srvgrp;
 | 
						|
 | 
						|
	/* copy msg to the service group buffer */
 | 
						|
	msg_copy((u32 *)&sg_rsp_msg[sg], (u32 *)&rxmsg, size);
 | 
						|
	sg_rsp_siz[sg] = size;
 | 
						|
	sg_busy &= ~(1 << sg);
 | 
						|
 | 
						|
	sg_callb = sgrp_callback[sg];
 | 
						|
	if (!sg_callb) {
 | 
						|
		upwr_callb user_callb = user_callback[sg];
 | 
						|
 | 
						|
		/* no service group callback; call the user callback if any */
 | 
						|
		if (!user_callb)
 | 
						|
			goto done; /* no user callback */
 | 
						|
 | 
						|
		/* make the user callback */
 | 
						|
		user_callb(sg, rxmsg.hdr.function, (enum upwr_resp)rxmsg.hdr.errcode,
 | 
						|
			   (int)(size == 2) ? rxmsg.word2 : rxmsg.hdr.ret);
 | 
						|
		goto done;
 | 
						|
	}
 | 
						|
 | 
						|
	/* finally make the group callback */
 | 
						|
	sg_callb();
 | 
						|
	/* don't uninstall the group callback, it's permanent */
 | 
						|
done:
 | 
						|
	if (rxmsg.hdr.errcode == UPWR_RESP_SHUTDOWN) /* shutdown error: */
 | 
						|
		api_state = UPWR_API_INITLZED;
 | 
						|
}
 | 
						|
 | 
						|
void upwr_txrx_isr(void)
 | 
						|
{
 | 
						|
	if (readl(&mu->rsr))
 | 
						|
		upwr_mu_int_callback();
 | 
						|
}
 | 
						|
 | 
						|
void upwr_start_callb(void)
 | 
						|
{
 | 
						|
	switch (api_state) {
 | 
						|
	case UPWR_API_START_WAIT:
 | 
						|
	{
 | 
						|
		upwr_rdy_callb start_callb = (upwr_rdy_callb)user_callback[UPWR_SG_EXCEPT];
 | 
						|
 | 
						|
		union upwr_ready_msg *msg = (union upwr_ready_msg *)&sg_rsp_msg[UPWR_SG_EXCEPT];
 | 
						|
 | 
						|
		/* message sanity check */
 | 
						|
		UPWR_API_ASSERT(msg->hdr.srvgrp   == UPWR_SG_EXCEPT);
 | 
						|
		UPWR_API_ASSERT(msg->hdr.function == UPWR_XCP_START);
 | 
						|
		UPWR_API_ASSERT(msg->hdr.errcode  == UPWR_RESP_OK);
 | 
						|
 | 
						|
		fw_ram_version.soc_id = fw_rom_version.soc_id;
 | 
						|
		fw_ram_version.vmajor = msg->args.vmajor;
 | 
						|
		fw_ram_version.vminor = msg->args.vminor;
 | 
						|
		fw_ram_version.vfixes = msg->args.vfixes;
 | 
						|
 | 
						|
		/*
 | 
						|
		 * vmajor == vminor == vfixes == 0 indicates start error
 | 
						|
		 * in this case, go back to the INITLZED state
 | 
						|
		 */
 | 
						|
 | 
						|
		if (fw_ram_version.vmajor || fw_ram_version.vminor || fw_ram_version.vfixes) {
 | 
						|
			api_state = UPWR_API_READY;
 | 
						|
 | 
						|
			/* initialization is over: uninstall the callbacks just in case */
 | 
						|
			UPWR_USR_CALLB(UPWR_SG_EXCEPT, NULL);
 | 
						|
			sgrp_callback[UPWR_SG_EXCEPT] = NULL;
 | 
						|
 | 
						|
			if (!fw_launch_option) {
 | 
						|
				/* launched ROM firmware: RAM fw versions must be all 0s */
 | 
						|
				fw_ram_version.vmajor =
 | 
						|
				fw_ram_version.vminor =
 | 
						|
				fw_ram_version.vfixes = 0;
 | 
						|
			}
 | 
						|
		} else {
 | 
						|
			api_state = UPWR_API_INITLZED;
 | 
						|
		}
 | 
						|
 | 
						|
		start_callb(msg->args.vmajor, msg->args.vminor, msg->args.vfixes);
 | 
						|
	}
 | 
						|
	break;
 | 
						|
 | 
						|
	default:
 | 
						|
		UPWR_API_ASSERT(0);
 | 
						|
		break;
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
int upwr_init(enum soc_domain domain, struct mu_type *muptr)
 | 
						|
{
 | 
						|
	u32 dom_buffer_base = ((UPWR_API_BUFFER_ENDPLUS + UPWR_API_BUFFER_BASE) / 2);
 | 
						|
	union upwr_init_msg *msg = (union upwr_init_msg *)&sg_rsp_msg[UPWR_SG_EXCEPT];
 | 
						|
	enum upwr_sg sg; /* service group number */
 | 
						|
	u32 size; /* in words */
 | 
						|
	int j;
 | 
						|
 | 
						|
	mu = muptr;
 | 
						|
	writel(0, &mu->tcr);
 | 
						|
	writel(0, &mu->rcr);
 | 
						|
 | 
						|
	api_state = UPWR_API_INIT_WAIT;
 | 
						|
	pwr_domain = domain;
 | 
						|
	sg_busy = 0;
 | 
						|
 | 
						|
	/* initialize the versions, in case they are polled */
 | 
						|
	fw_rom_version.soc_id =
 | 
						|
	fw_rom_version.vmajor =
 | 
						|
	fw_rom_version.vminor =
 | 
						|
	fw_rom_version.vfixes = 0;
 | 
						|
 | 
						|
	fw_ram_version.soc_id =
 | 
						|
	fw_ram_version.vmajor =
 | 
						|
	fw_ram_version.vminor =
 | 
						|
	fw_ram_version.vfixes = 0;
 | 
						|
 | 
						|
	sh_buffer[UPWR_SG_EXCEPT] = (void *)(ulong)dom_buffer_base;
 | 
						|
	sh_buffer[UPWR_SG_PWRMGMT] = (void *)(ulong)(dom_buffer_base +
 | 
						|
						     sizeof(union upwr_xcp_union));
 | 
						|
	sh_buffer[UPWR_SG_DELAYM] = NULL;
 | 
						|
	sh_buffer[UPWR_SG_VOLTM] = NULL;
 | 
						|
	sh_buffer[UPWR_SG_CURRM] = NULL;
 | 
						|
	sh_buffer[UPWR_SG_TEMPM] = NULL;
 | 
						|
	sh_buffer[UPWR_SG_DIAG] = NULL;
 | 
						|
	/* (no buffers service groups other than xcp and pwm for now) */
 | 
						|
 | 
						|
	for (j = 0; j < UPWR_SG_COUNT; j++) {
 | 
						|
		user_callback[j] = NULL;
 | 
						|
		/* service group Exception gets the initialization callbacks */
 | 
						|
		sgrp_callback[j] = (j == UPWR_SG_EXCEPT) ? upwr_start_callb : NULL;
 | 
						|
 | 
						|
		/* response messages with an initial consistent content */
 | 
						|
		sg_rsp_msg[j].hdr.errcode = UPWR_RESP_SHUTDOWN;
 | 
						|
	}
 | 
						|
 | 
						|
	if (readl(&mu->fsr) & BIT(0)) {
 | 
						|
		/* send a ping message down to get the ROM version back */
 | 
						|
		upwr_xcp_ping_msg ping_msg;
 | 
						|
 | 
						|
		ping_msg.hdr.domain = pwr_domain;
 | 
						|
		ping_msg.hdr.srvgrp = UPWR_SG_EXCEPT;
 | 
						|
		ping_msg.hdr.function = UPWR_XCP_PING;
 | 
						|
 | 
						|
		if (readl(&mu->rsr) & BIT(0)) /* first clean any Rx message left over */
 | 
						|
			upwr_rx((u32 *)msg, &size);
 | 
						|
 | 
						|
		while (readl(&mu->tsr) != UPWR_MU_TSR_EMPTY)
 | 
						|
			;
 | 
						|
 | 
						|
		/*
 | 
						|
		 * now send the ping message;
 | 
						|
		 * do not use upwr_tx, which needs API initilized;
 | 
						|
		 * just write to the MU TR register(s)
 | 
						|
		 */
 | 
						|
		setbits_le32(&mu->fcr, BIT(0)); /* flag urgency status */
 | 
						|
		upwr_copy2tr(mu, (u32 *)&ping_msg, sizeof(ping_msg) / 4);
 | 
						|
	}
 | 
						|
 | 
						|
	do {
 | 
						|
		/* poll for the MU Rx status: wait for an init message, either
 | 
						|
		 * 1st sent from uPower after reset or as a response to a ping
 | 
						|
		 */
 | 
						|
		while (!readl(&mu->rsr) & BIT(0))
 | 
						|
			;
 | 
						|
 | 
						|
		clrbits_le32(&mu->fcr, BIT(0));
 | 
						|
 | 
						|
		if (upwr_rx((u32 *)msg, &size) < 0)
 | 
						|
			return -4;
 | 
						|
 | 
						|
		if (size != (sizeof(union upwr_init_msg) / 4)) {
 | 
						|
			if (readl(&mu->fsr) & BIT(0))
 | 
						|
				continue; /* discard left over msg */
 | 
						|
			else
 | 
						|
				return -4;
 | 
						|
		}
 | 
						|
 | 
						|
		sg = (enum upwr_sg)msg->hdr.srvgrp;
 | 
						|
		if (sg != UPWR_SG_EXCEPT) {
 | 
						|
			if (readl(&mu->fsr) & BIT(0))
 | 
						|
				continue;
 | 
						|
			else
 | 
						|
				return -4;
 | 
						|
		}
 | 
						|
 | 
						|
		if ((enum upwr_xcp_f)msg->hdr.function   != UPWR_XCP_INIT) {
 | 
						|
			if (readl(&mu->fsr) & BIT(0))
 | 
						|
				continue;
 | 
						|
			else
 | 
						|
				return -4;
 | 
						|
		}
 | 
						|
 | 
						|
		break;
 | 
						|
	} while (true);
 | 
						|
 | 
						|
	fw_rom_version.soc_id = msg->args.soc;
 | 
						|
	fw_rom_version.vmajor = msg->args.vmajor;
 | 
						|
	fw_rom_version.vminor = msg->args.vminor;
 | 
						|
	fw_rom_version.vfixes = msg->args.vfixes;
 | 
						|
 | 
						|
	api_state = UPWR_API_INITLZED;
 | 
						|
 | 
						|
	return 0;
 | 
						|
} /* upwr_init */
 | 
						|
 | 
						|
int upwr_start(u32 launchopt, const upwr_rdy_callb rdycallb)
 | 
						|
{
 | 
						|
	upwr_start_msg txmsg;
 | 
						|
 | 
						|
	if (api_state != UPWR_API_INITLZED)
 | 
						|
		return -3;
 | 
						|
 | 
						|
	UPWR_USR_CALLB(UPWR_SG_EXCEPT, (upwr_callb)rdycallb);
 | 
						|
 | 
						|
	UPWR_MSG_HDR(txmsg.hdr, UPWR_SG_EXCEPT, UPWR_XCP_START);
 | 
						|
 | 
						|
	txmsg.hdr.arg = launchopt;
 | 
						|
	fw_launch_option = launchopt;
 | 
						|
 | 
						|
	if (upwr_tx((u32 *)&txmsg, sizeof(txmsg) / 4) < 0) {
 | 
						|
		/* catastrophic error, but is it possible to happen? */
 | 
						|
		UPWR_API_ASSERT(0);
 | 
						|
		return -1;
 | 
						|
	}
 | 
						|
 | 
						|
	api_state = UPWR_API_START_WAIT;
 | 
						|
 | 
						|
	return 0;
 | 
						|
}
 | 
						|
 | 
						|
u32 upwr_rom_version(u32 *vmajor, u32 *vminor, u32 *vfixes)
 | 
						|
{
 | 
						|
	u32 soc;
 | 
						|
 | 
						|
	soc = fw_rom_version.soc_id;
 | 
						|
	*vmajor = fw_rom_version.vmajor;
 | 
						|
	*vminor = fw_rom_version.vminor;
 | 
						|
	*vfixes = fw_rom_version.vfixes;
 | 
						|
 | 
						|
	return soc;
 | 
						|
}
 |