/* Copyright 2017-2019 IBM Corp.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *	http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
 * implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

/*
 * P9 OPAL - SBE communication driver
 *
 * P9 chip has Self Boot Engine (SBE). OPAL uses SBE for various purpose like
 * timer, scom, MPIPL, etc,. Every chip has SBE. OPAL can communicate to SBE
 * on all chips. Based on message type it selects appropriate SBE (ex: schedule
 * timer on any chip).
 *
 * OPAL communicates to SBE via a set of data and control registers provided by
 * the PSU block in P9 chip.
 *  - Four 8 byte registers for Host to send command packets to SBE.
 *  - Four 8 byte registers for SBE to send response packets to Host.
 *  - Two doorbell registers (1 on each side) to alert either party
 *    when data is placed in above mentioned data registers. Once Host/SBE reads
 *    incoming data, it should clear doorbell register. Interrupt is disabled
 *    as soon as doorbell register is cleared.
 *
 * OPAL - SBE message format:
 *  - OPAL communicates to SBE via set of well defined commands.
 *  - Reg0 contains message header (command class, subclass, flags etc).
 *  - Reg1-3 contains actual data. If data is big then it uses indirect method
 *    (data is passed via memory and memory address/size is passed in Reg1-3).
 *  - Every message has defined timeout. SBE must respond within specified
 *    time. Otherwise OPAL discards message and sends error message to caller.
 *
 * Constraints:
 *  - Only one command is accepted in the command buffer until the response for
 *    the command is enqueued in the response buffer by SBE.
 */

#define pr_fmt(fmt) "SBE: " fmt

#include <chip.h>
#include <errorlog.h>
#include <lock.h>
#include <opal.h>
#include <sbe-p9.h>
#include <skiboot.h>
#include <timebase.h>
#include <timer.h>
#include <trace.h>
#include <xscom.h>

enum p9_sbe_mbox_state {
	sbe_mbox_idle = 0,	/* Ready to send message */
	sbe_mbox_send,		/* Message sent, waiting for ack/response */
	sbe_mbox_rr,		/* SBE in R/R */
};

struct p9_sbe {
	/* Chip ID to send message */
	u32			chip_id;

	/* List to hold SBE queue messages */
	struct list_head	msg_list;

	struct lock		lock;

	enum p9_sbe_mbox_state	state;

	/* SBE MBOX message sequence number */
	u16			cur_seq;
};

/* Default SBE chip ID */
static int sbe_default_chip_id = -1;

/* Is SBE timer running? */
static bool sbe_has_timer = false;
static bool sbe_timer_in_progress = false;
static bool has_new_target = false;

/* Inflight and next timer in TB */
static uint64_t sbe_last_gen_stamp;
static uint64_t sbe_timer_target;

/* Timer lock */
static struct lock sbe_timer_lock;

/*
 * Minimum timeout value for P9 is 500 microseconds. After that
 * SBE timer can handle granularity of 1 microsecond.
 */
#define SBE_TIMER_DEFAULT_US	500
static uint64_t sbe_timer_def_tb;

/* Timer control message */
static struct p9_sbe_msg *timer_ctrl_msg;

#define SBE_STATUS_PRI_SHIFT	0x30
#define SBE_STATUS_SEC_SHIFT	0x20

/* Forward declaration */
static void p9_sbe_timeout_poll_one(struct p9_sbe *sbe);
static void p9_sbe_timer_schedule(void);

/* bit 0-15 : Primary status code */
static inline u16 p9_sbe_get_primary_rc(struct p9_sbe_msg *resp)
{
	return (resp->reg[0] >> SBE_STATUS_PRI_SHIFT);
}

static inline void p9_sbe_set_primary_rc(struct p9_sbe_msg *resp, u64 rc)
{
	resp->reg[0] |= (rc << SBE_STATUS_PRI_SHIFT);
}

static u64 p9_sbe_rreg(u32 chip_id, u64 reg)
{
	u64 data = 0;
	int rc;

	rc = xscom_read(chip_id, reg, &data);
	if (rc != OPAL_SUCCESS) {
		prlog(PR_DEBUG, "XSCOM error %d reading reg 0x%llx\n", rc, reg);
		return 0xffffffff;
	}

	return be64_to_cpu(data);
}

static void p9_sbe_reg_dump(u32 chip_id)
{
#define SBE_DUMP_REG_ONE(chip_id, x) \
	prlog(PR_DEBUG, "  %20s: %016llx\n", #x, p9_sbe_rreg(chip_id, x))

	prlog(PR_DEBUG, "MBOX register dump for chip : %x\n", chip_id);
	SBE_DUMP_REG_ONE(chip_id, PSU_SBE_DOORBELL_REG_RW);
	SBE_DUMP_REG_ONE(chip_id, PSU_HOST_SBE_MBOX_REG0);
	SBE_DUMP_REG_ONE(chip_id, PSU_HOST_SBE_MBOX_REG1);
	SBE_DUMP_REG_ONE(chip_id, PSU_HOST_SBE_MBOX_REG2);
	SBE_DUMP_REG_ONE(chip_id, PSU_HOST_SBE_MBOX_REG3);
	SBE_DUMP_REG_ONE(chip_id, PSU_HOST_DOORBELL_REG_RW);
	SBE_DUMP_REG_ONE(chip_id, PSU_HOST_SBE_MBOX_REG4);
	SBE_DUMP_REG_ONE(chip_id, PSU_HOST_SBE_MBOX_REG5);
	SBE_DUMP_REG_ONE(chip_id, PSU_HOST_SBE_MBOX_REG6);
	SBE_DUMP_REG_ONE(chip_id, PSU_HOST_SBE_MBOX_REG7);
}

void p9_sbe_freemsg(struct p9_sbe_msg *msg)
{
	if (msg && msg->resp)
		free(msg->resp);
	free(msg);
}

static void p9_sbe_fillmsg(struct p9_sbe_msg *msg, u16 cmd,
			   u16 ctrl_flag, u64 reg1, u64 reg2, u64 reg3)
{
	bool response = !!(ctrl_flag & SBE_CMD_CTRL_RESP_REQ);
	u16 flag;

	/*
	 * Always set ack required flag. SBE will interrupt OPAL once it read
	 * message from mailbox register. If OPAL is expecting response, then
	 * it will update message timeout, otherwise it will send next message.
	 */
	flag = ctrl_flag | SBE_CMD_CTRL_ACK_REQ;

	/* Seqence ID is filled by p9_sbe_queue_msg() */
	msg->reg[0] = ((u64)flag << 32) | cmd;
	msg->reg[1] = reg1;
	msg->reg[2] = reg2;
	msg->reg[3] = reg3;
	msg->state = sbe_msg_unused;
	msg->response = response;
}

static struct p9_sbe_msg *p9_sbe_allocmsg(bool alloc_resp)
{
	struct p9_sbe_msg *msg;

	msg = zalloc(sizeof(struct p9_sbe_msg));
	if (!msg) {
		prlog(PR_ERR, "Failed to allocate SBE message\n");
		return NULL;
	}
	if (alloc_resp) {
		msg->resp = zalloc(sizeof(struct p9_sbe_msg));
		if (!msg->resp) {
			prlog(PR_ERR, "Failed to allocate SBE resp message\n");
			free(msg);
			return NULL;
		}
	}

	return msg;
}

/*
 * Handles "command with direct data" format only.
 *
 * Note: All mbox messages of our interest uses direct data format. If we need
 *       indirect data format then we may have to enhance this function.
 */
struct p9_sbe_msg *p9_sbe_mkmsg(u16 cmd, u16 ctrl_flag,
				u64 reg1, u64 reg2, u64 reg3)
{
	struct p9_sbe_msg *msg;

	msg = p9_sbe_allocmsg(!!(ctrl_flag & SBE_CMD_CTRL_RESP_REQ));
	if (!msg)
		return NULL;

	p9_sbe_fillmsg(msg, cmd, ctrl_flag, reg1, reg2, reg3);
	return msg;
}

static inline bool p9_sbe_mbox_busy(struct p9_sbe *sbe)
{
	return (sbe->state != sbe_mbox_idle);
}

static inline bool p9_sbe_msg_busy(struct p9_sbe_msg *msg)
{
	switch (msg->state) {
	case sbe_msg_queued:
	/* fall through */
	case sbe_msg_sent:
	case sbe_msg_wresp:
		return true;
	default:	/* + sbe_msg_unused, sbe_msg_done,
			     sbe_msg_timeout, sbe_msg_error */
		break;
	}
	return false;
}

static inline struct p9_sbe *p9_sbe_get_sbe(u32 chip_id)
{
	struct proc_chip *chip;

	/* Default to SBE on master chip */
	if (chip_id == -1) {
		if (sbe_default_chip_id == -1)
			return NULL;

		chip = get_chip(sbe_default_chip_id);
	} else {
		chip = get_chip(chip_id);
	}
	if (chip == NULL || chip->sbe == NULL)
		return NULL;

	return chip->sbe;
}

static int p9_sbe_msg_send(struct p9_sbe *sbe, struct p9_sbe_msg *msg)
{
	int rc, i;
	u64 addr, *data;

	addr = PSU_HOST_SBE_MBOX_REG0;
	data = &msg->reg[0];

	for (i = 0; i < NR_HOST_SBE_MBOX_REG; i++) {
		rc = xscom_write(sbe->chip_id, addr, *data);
		if (rc)
			return rc;

		addr++;
		data++;
	}

	rc = xscom_write(sbe->chip_id, PSU_SBE_DOORBELL_REG_OR,
			 HOST_SBE_MSG_WAITING);
	if (rc != OPAL_SUCCESS)
		return rc;

	prlog(PR_TRACE, "Message queued [chip id = 0x%x]:\n\t Reg0 : %016llx"
	      "\n\t Reg1 : %016llx\n\t Reg2 : %016llx\n\t Reg3 : %016llx\n",
	      sbe->chip_id, msg->reg[0], msg->reg[1], msg->reg[2], msg->reg[3]);

	msg->timeout = mftb() + msecs_to_tb(SBE_CMD_TIMEOUT_MAX);
	sbe->state = sbe_mbox_send;
	msg->state = sbe_msg_sent;
	return rc;
}

static int p9_sbe_msg_receive(u32 chip_id, struct p9_sbe_msg *resp)
{
	int i;
	int rc = OPAL_SUCCESS;
	u64 addr, *data;

	addr = PSU_HOST_SBE_MBOX_REG4;
	data = &resp->reg[0];

	for (i = 0; i < NR_HOST_SBE_MBOX_REG; i++) {
		rc = xscom_read(chip_id, addr, data);
		if (rc)
			return rc;

		addr++;
		data++;
	}
	return rc;
}

/* WARNING: This will drop sbe->lock */
static void p9_sbe_msg_complete(struct p9_sbe *sbe, struct p9_sbe_msg *msg,
				enum p9_sbe_msg_state msg_state)
{
	void (*comp)(struct p9_sbe_msg *msg);

	prlog(PR_TRACE, "Completing msg [chip id = %x], reg0 : 0x%llx\n",
	      sbe->chip_id, msg->reg[0]);

	comp = msg->complete;
	list_del(&msg->link);
	sync();
	msg->state = msg_state;

	if (comp) {
		unlock(&sbe->lock);
		comp(msg);
		lock(&sbe->lock);
	}
}

/* WARNING: This will drop sbe->lock */
static void p9_sbe_send_complete(struct p9_sbe *sbe)
{
	struct p9_sbe_msg *msg;

	if (list_empty(&sbe->msg_list))
		return;

	msg = list_top(&sbe->msg_list, struct p9_sbe_msg, link);
	/* Need response */
	if (msg->response) {
		msg->state = sbe_msg_wresp;
	} else {
		sbe->state = sbe_mbox_idle;
		p9_sbe_msg_complete(sbe, msg, sbe_msg_done);
	}
}

/* WARNING: This will drop sbe->lock */
static void p9_sbe_process_queue(struct p9_sbe *sbe)
{
	int rc, retry_cnt = 0;
	struct p9_sbe_msg *msg = NULL;

	if (p9_sbe_mbox_busy(sbe))
		return;

	while (!list_empty(&sbe->msg_list)) {
		msg = list_top(&sbe->msg_list, struct p9_sbe_msg, link);
		/* Send message */
		rc = p9_sbe_msg_send(sbe, msg);
		if (rc == OPAL_SUCCESS)
			return;

		prlog(PR_ERR, "Failed to send message to SBE [chip id = %x]\n",
		      sbe->chip_id);
		if (msg->resp) {
			p9_sbe_set_primary_rc(msg->resp,
					      SBE_STATUS_PRI_GENERIC_ERR);
		}
		p9_sbe_msg_complete(sbe, msg, sbe_msg_error);

		/*
		 * Repeatedly failed to send message to SBE. Lets stop
		 * sending message.
		 */
		if (retry_cnt++ >= 3) {
			prlog(PR_ERR, "Temporarily stopped sending "
			      "message to SBE\n");
			return;
		}
	}
}

/*
 * WARNING:
 *         Only one command is accepted in the command buffer until response
 *         to the command is enqueued in the response buffer by SBE.
 *
 *         Head of msg_list contains in-flight message. Hence we should always
 *         add new message to tail of the list.
 */
int p9_sbe_queue_msg(u32 chip_id, struct p9_sbe_msg *msg,
		     void (*comp)(struct p9_sbe_msg *msg))
{
	struct p9_sbe *sbe;

	if (!msg)
		return OPAL_PARAMETER;

	sbe = p9_sbe_get_sbe(chip_id);
	if (!sbe)
		return OPAL_HARDWARE;

	lock(&sbe->lock);
	/* Set completion and update sequence number */
	msg->complete = comp;
	msg->state = sbe_msg_queued;
	msg->reg[0] = msg->reg[0] | ((u64)sbe->cur_seq << 16);
	sbe->cur_seq++;

	/* Reset sequence number */
	if (sbe->cur_seq == 0xffff)
		sbe->cur_seq = 1;

	/* Add message to queue */
	list_add_tail(&sbe->msg_list, &msg->link);
	p9_sbe_process_queue(sbe);
	unlock(&sbe->lock);

	return OPAL_SUCCESS;
}

int p9_sbe_sync_msg(u32 chip_id, struct p9_sbe_msg *msg, bool autofree)
{
	int rc;
	struct p9_sbe *sbe;

	rc = p9_sbe_queue_msg(chip_id, msg, NULL);
	if (rc)
		goto free_msg;

	sbe = p9_sbe_get_sbe(chip_id);
	if (!sbe) {
		rc = OPAL_HARDWARE;
		goto free_msg;
	}

	while (p9_sbe_msg_busy(msg)) {
		cpu_relax();
		p9_sbe_timeout_poll_one(sbe);
	}

	if (msg->state == sbe_msg_done)
		rc = SBE_STATUS_PRI_SUCCESS;
	else
		rc = SBE_STATUS_PRI_GENERIC_ERR;

	if (msg->response && msg->resp)
		rc = p9_sbe_get_primary_rc(msg->resp);

free_msg:
	if (autofree)
		p9_sbe_freemsg(msg);

	return rc;
}

/* Remove SBE message from queue. It will not remove inflight message */
int p9_sbe_cancelmsg(u32 chip_id, struct p9_sbe_msg *msg)
{
	struct p9_sbe *sbe;

	sbe = p9_sbe_get_sbe(chip_id);
	if (!sbe)
		return OPAL_PARAMETER;

	lock(&sbe->lock);
	if (msg->state != sbe_msg_queued) {
		unlock(&sbe->lock);
		return OPAL_BUSY;
	}

	list_del(&msg->link);
	msg->state = sbe_msg_done;
	unlock(&sbe->lock);
	return OPAL_SUCCESS;
}

static void p9_sbe_handle_response(u32 chip_id, struct p9_sbe_msg *msg)
{
	u16 send_seq, resp_seq;
	int rc;

	if (msg == NULL || msg->resp == NULL)
		return;

	memset(msg->resp, 0, sizeof(struct p9_sbe_msg));

	rc = p9_sbe_msg_receive(chip_id, msg->resp);
	if (rc != OPAL_SUCCESS) {
		prlog(PR_ERR, "Failed to read response message "
		      "[chip id = %x]\n", chip_id);
		p9_sbe_set_primary_rc(msg->resp, SBE_STATUS_PRI_GENERIC_ERR);
		return;
	}

	/* Validate sequence number */
	send_seq = (msg->reg[0] >> 16) & 0xffff;
	resp_seq = (msg->resp->reg[0] >> 16) & 0xffff;
	if (send_seq != resp_seq) {
		/*
		 * XXX Handle SBE R/R.
		 *     Lets send sequence error to caller until SBE reset works.
		 */
		prlog(PR_ERR, "Invalid sequence id [chip id = %x]\n", chip_id);
		p9_sbe_set_primary_rc(msg->resp, SBE_STATUS_PRI_SEQ_ERR);
		return;
	}
}

static int p9_sbe_clear_interrupt(struct p9_sbe *sbe, u64 bits)
{
	int rc;
	u64 val;

	/* Clear doorbell register */
	val = SBE_HOST_RESPONSE_MASK & ~bits;
	rc = xscom_write(sbe->chip_id, PSU_HOST_DOORBELL_REG_AND, val);
	if (rc) {
		prlog(PR_ERR, "Failed to clear SBE to Host doorbell "
		      "interrupt [chip id = %x]\n", sbe->chip_id);
	}
	return rc;
}

/* WARNING: This will drop sbe->lock */
static void p9_sbe_timer_response(struct p9_sbe *sbe)
{
	if (sbe->chip_id != sbe_default_chip_id)
		return;

	sbe_timer_in_progress = false;
	/* Drop lock and call timers */
	unlock(&sbe->lock);
	check_timers(true);
	lock(&sbe->lock);
}

/* WARNING: This will drop sbe->lock */
static void __p9_sbe_interrupt(struct p9_sbe *sbe)
{
	bool has_response;
	int rc;
	u64 data = 0, val;
	struct p9_sbe_msg *msg = NULL;

again:
	/* Read doorbell register */
	rc = xscom_read(sbe->chip_id, PSU_HOST_DOORBELL_REG_RW, &data);
	if (rc) {
		prlog(PR_ERR, "Failed to read SBE to Host doorbell register "
		      "[chip id = %x]\n", sbe->chip_id);
		p9_sbe_reg_dump(sbe->chip_id);
		return;
	}

	/* Completed processing all the bits */
	if (!data)
		return;

	/* SBE came back from reset */
	if (data & SBE_HOST_RESET) {
		/* Clear all bits and restart sending message */
		rc = p9_sbe_clear_interrupt(sbe, data);
		if (rc)
			return;

		prlog(PR_NOTICE,
		      "Back from reset [chip id = %x]\n", sbe->chip_id);
		/* Reset SBE MBOX state */
		sbe->state = sbe_mbox_idle;

		/* Reset message state */
		if (!list_empty(&sbe->msg_list)) {
			msg = list_top(&sbe->msg_list, struct p9_sbe_msg, link);
			msg->state = sbe_msg_queued;
		}
		return;
	}

	/* Process ACK message before response */
	if (data & SBE_HOST_MSG_READ) {
		rc = p9_sbe_clear_interrupt(sbe, SBE_HOST_MSG_READ);
		if (rc)
			return;
		p9_sbe_send_complete(sbe);
		goto again;
	}

	/* Read SBE response before clearing doorbell register */
	if (data & SBE_HOST_RESPONSE_WAITING) {
		if (!list_empty(&sbe->msg_list)) {
			msg = list_top(&sbe->msg_list, struct p9_sbe_msg, link);
			p9_sbe_handle_response(sbe->chip_id, msg);
			has_response = true;
		} else {
			has_response = false;
			prlog(PR_DEBUG,
			      "Got response with no pending message\n");
		}

		rc = p9_sbe_clear_interrupt(sbe, SBE_HOST_RESPONSE_WAITING);
		if (rc)
			return;

		/* Reset SBE MBOX state */
		sbe->state = sbe_mbox_idle;
		if (has_response)
			p9_sbe_msg_complete(sbe, msg, sbe_msg_done);

		goto again;
	}

	/* SBE passthrough command, call prd handler */
	if (data & SBE_HOST_PASSTHROUGH) {
		rc = p9_sbe_clear_interrupt(sbe, SBE_HOST_PASSTHROUGH);
		if (rc)
			return;
		prd_sbe_passthrough(sbe->chip_id);
		goto again;
	}

	/* Timer expired */
	if (data & SBE_HOST_TIMER_EXPIRY) {
		rc = p9_sbe_clear_interrupt(sbe, SBE_HOST_TIMER_EXPIRY);
		if (rc)
			return;
		p9_sbe_timer_response(sbe);
		goto again;
	}

	/* Unhandled bits */
	val = data & ~(SBE_HOST_RESPONSE_MASK);
	if (val) {
		prlog(PR_ERR, "Unhandled interrupt bit [chip id = %x] : "
		      " %016llx\n", sbe->chip_id, val);
		rc = p9_sbe_clear_interrupt(sbe, data);
		if (rc)
			return;
		goto again;
	}
}

void p9_sbe_interrupt(uint32_t chip_id)
{
	struct proc_chip *chip;
	struct p9_sbe *sbe;

	chip = get_chip(chip_id);
	if (chip == NULL || chip->sbe == NULL)
		return;

	sbe = chip->sbe;
	lock(&sbe->lock);
	__p9_sbe_interrupt(sbe);
	p9_sbe_process_queue(sbe);
	unlock(&sbe->lock);
}

/*
 * Check if the timer is working. If at least 10ms elapsed since
 * last scheduled timer expiry.
 */
static void p9_sbe_timer_poll(struct p9_sbe *sbe)
{
	if (sbe->chip_id != sbe_default_chip_id)
		return;

	if (!sbe_has_timer || !sbe_timer_in_progress)
		return;

	if (tb_compare(mftb(), sbe_last_gen_stamp + msecs_to_tb(10))
	    != TB_AAFTERB)
		return;

	prlog(PR_ERR, "Timer stuck, falling back to OPAL pollers.\n");
	prlog(PR_ERR, "You will likely have slower I2C and may have "
	      "experienced increased jitter.\n");
	p9_sbe_reg_dump(sbe->chip_id);
	sbe_has_timer = false;
	sbe_timer_in_progress = false;
}

static void p9_sbe_timeout_poll_one(struct p9_sbe *sbe)
{
	struct p9_sbe_msg *msg;

	if (sbe->chip_id == sbe_default_chip_id) {
		if (list_empty_nocheck(&sbe->msg_list) &&
		    !sbe_timer_in_progress)
			return;
	} else {
		if (list_empty_nocheck(&sbe->msg_list))
			return;
	}

	lock(&sbe->lock);

	/*
	 * In some cases there will be a delay in calling OPAL interrupt
	 * handler routine (opal_handle_interrupt). In such cases its
	 * possible that SBE has responded, but OPAL didn't act on that.
	 * Hence check for SBE response.
	 */
	__p9_sbe_interrupt(sbe);
	p9_sbe_timer_poll(sbe);

	if (list_empty(&sbe->msg_list))
		goto out;

	/*
	 * For some reason OPAL didn't sent message to SBE.
	 * Lets try to send message again.
	 */
	if (!p9_sbe_mbox_busy(sbe)) {
		p9_sbe_process_queue(sbe);
		goto out;
	}

	msg = list_top(&sbe->msg_list, struct p9_sbe_msg, link);
	if (tb_compare(mftb(), msg->timeout) != TB_AAFTERB)
		goto out;

	/* Message timeout */
	prlog(PR_ERR, "Message timeout [chip id = %x], cmd = %llx, "
	      "subcmd = %llx\n", sbe->chip_id,
	      (msg->reg[0] >> 8) & 0xff, msg->reg[0] & 0xff);
	p9_sbe_reg_dump(sbe->chip_id);
	if (msg->resp) {
		p9_sbe_set_primary_rc(msg->resp,
				      SBE_STATUS_PRI_GENERIC_ERR);
	}

	/* XXX Handle SBE R/R. Reset SBE state until SBE R/R works. */
	sbe->state = sbe_mbox_idle;
	p9_sbe_msg_complete(sbe, msg, sbe_msg_timeout);
	p9_sbe_process_queue(sbe);

out:
	unlock(&sbe->lock);
}

static void p9_sbe_timeout_poll(void *user_data __unused)
{
	struct p9_sbe *sbe;
	struct proc_chip *chip;

	for_each_chip(chip) {
		if (chip->sbe == NULL)
			continue;
		sbe = chip->sbe;
		p9_sbe_timeout_poll_one(sbe);
	}
}

static void p9_sbe_timer_resp(struct p9_sbe_msg *msg)
{
	if (msg->state != sbe_msg_done) {
		prlog(PR_DEBUG, "Failed to schedule timer [chip id %x]\n",
		      sbe_default_chip_id);
	} else {
		/* Update last scheduled timer value */
		sbe_last_gen_stamp = mftb() +
			usecs_to_tb(timer_ctrl_msg->reg[1]);
		sbe_timer_in_progress = true;
	}

	if (!has_new_target)
		return;

	lock(&sbe_timer_lock);
	if (has_new_target) {
		has_new_target = false;
		p9_sbe_timer_schedule();
	}
	unlock(&sbe_timer_lock);
}

static void p9_sbe_timer_schedule(void)
{
	int rc;
	u32 tick_us = SBE_TIMER_DEFAULT_US;
	u64 tb_cnt, now = mftb();

	if (sbe_timer_in_progress) {
		if (now >= sbe_last_gen_stamp)
			return;

		/* Remaining time of inflight timer <= sbe_timer_def_tb */
		if ((sbe_last_gen_stamp - now) <= sbe_timer_def_tb)
			return;
	}

	if (now < sbe_timer_target) {
		/* Calculate how many microseconds from now, rounded up */
		if ((sbe_timer_target - now) > sbe_timer_def_tb) {
			tb_cnt = sbe_timer_target - now + usecs_to_tb(1) - 1;
			tick_us = tb_to_usecs(tb_cnt);
		}
	}

	/* Clear sequence number. p9_sbe_queue_msg will add new sequene ID */
	timer_ctrl_msg->reg[0] &= ~(PPC_BITMASK(32, 47));
	/* Update timeout value */
	timer_ctrl_msg->reg[1] = tick_us;
	rc = p9_sbe_queue_msg(sbe_default_chip_id, timer_ctrl_msg,
			      p9_sbe_timer_resp);
	if (rc != OPAL_SUCCESS) {
		prlog(PR_ERR, "Failed to start timer [chip id = %x]\n",
		      sbe_default_chip_id);
		return;
	}
}

/*
 * This is called with the timer lock held, so there is no
 * issue with re-entrancy or concurrence
 */
void p9_sbe_update_timer_expiry(uint64_t new_target)
{
	if (!sbe_has_timer || new_target == sbe_timer_target)
		return;

	lock(&sbe_timer_lock);
	/* Timer message is in flight. Record new timer and schedule later */
	if (p9_sbe_msg_busy(timer_ctrl_msg) || has_new_target) {
		if (new_target < sbe_timer_target) {
			sbe_timer_target = new_target;
			has_new_target = true;
		}
	} else {
		sbe_timer_target = new_target;
		p9_sbe_timer_schedule();
	}
	unlock(&sbe_timer_lock);
}

/* Initialize SBE timer */
static void p9_sbe_timer_init(void)
{
	timer_ctrl_msg = p9_sbe_mkmsg(SBE_CMD_CONTROL_TIMER,
				      CONTROL_TIMER_START, 0, 0, 0);
	assert(timer_ctrl_msg);
	init_lock(&sbe_timer_lock);
	sbe_has_timer = true;
	sbe_timer_target = mftb();
	sbe_last_gen_stamp = ~0ull;
	sbe_timer_def_tb = usecs_to_tb(SBE_TIMER_DEFAULT_US);
	prlog(PR_INFO, "Timer facility on chip %x\n", sbe_default_chip_id);
}

bool p9_sbe_timer_ok(void)
{
	return sbe_has_timer;
}

void p9_sbe_init(void)
{
	struct dt_node *xn;
	struct proc_chip *chip;
	struct p9_sbe *sbe;

	if (proc_gen < proc_gen_p9)
		return;

	dt_for_each_compatible(dt_root, xn, "ibm,xscom") {
		sbe = zalloc(sizeof(struct p9_sbe));
		assert(sbe);
		sbe->chip_id = dt_get_chip_id(xn);
		sbe->cur_seq = 1;
		sbe->state = sbe_mbox_idle;
		list_head_init(&sbe->msg_list);
		init_lock(&sbe->lock);

		chip = get_chip(sbe->chip_id);
		assert(chip);
		chip->sbe = sbe;

		if (dt_has_node_property(xn, "primary", NULL)) {
			sbe_default_chip_id = sbe->chip_id;
			prlog(PR_DEBUG, "Master chip id : %x\n", sbe->chip_id);
		}
	}

	if (sbe_default_chip_id == -1) {
		prlog(PR_ERR, "Master chip ID not found.\n");
		return;
	}

	/* Initiate SBE timer */
	p9_sbe_timer_init();

	/* Initiate SBE timeout poller */
	opal_add_poller(p9_sbe_timeout_poll, NULL);
}
