text: /* Copyright 2013-2014 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.
   */
  
  #include <skiboot.h>
  #include <fsp.h>
  #include <lock.h>
  #include <timebase.h>
  #include <time.h>
  #include <time-utils.h>
  #include <opal-api.h>
  #include <opal-msg.h>
  #include <errorlog.h>
  #include <device.h>
  
  /*
   * Note on how those operate:
   *
   * Because the RTC calls can be pretty slow, these functions will shoot
   * an asynchronous request to the FSP (if none is already pending)
   *
   * The requests will return OPAL_BUSY_EVENT as long as the event has
   * not been completed.
   *
   * WARNING: An attempt at doing an RTC write while one is already pending
   * will simply ignore the new arguments and continue returning
   * OPAL_BUSY_EVENT. This is to be compatible with existing Linux code.
   *
   * Completion of the request will result in an event OPAL_EVENT_RTC
   * being signaled, which will remain raised until a corresponding call
   * to opal_rtc_read() or opal_rtc_write() finally returns OPAL_SUCCESS,
   * at which point the operation is complete and the event cleared.
   *
   * If we end up taking longer than rtc_read_timeout_ms millieconds waiting
   * for the response from a read request, we simply return a cached value (plus
   * an offset calculated from the timebase. When the read request finally
   * returns, we update our cache value accordingly.
   *
   * There is two separate set of state for reads and writes. If both are
   * attempted at the same time, the event bit will remain set as long as either
   * of the two has a pending event to signal.
   */
  
  #include <rtc.h>
  
  /* All of the below state is protected by rtc_lock.
   * It should be held for the shortest amount of time possible.
   * Certainly not across calls to FSP.
   */
  static struct lock rtc_lock;
  
  static enum {
  	RTC_TOD_VALID,
  	RTC_TOD_INVALID,
  	RTC_TOD_PERMANENT_ERROR,
  } rtc_tod_state = RTC_TOD_INVALID;
  
  /* State machine for getting an RTC request.
   * RTC_{READ/WRITE}_NO_REQUEST -> RTC_{READ/WRITE}_PENDING_REQUEST (one in flight)
   * RTC_{READ/WRITE}_PENDING_REQUEST -> RTC_{READ/WRITE}_REQUEST_AVAILABLE,
   * when FSP responds
   * RTC_{READ/WRITE}_REQUEST_AVAILABLE -> RTC_{READ/WRITE}_NO_REQUEST,
   * when OS retrieves it
   */
  static enum {
  	RTC_READ_NO_REQUEST,
  	RTC_READ_PENDING_REQUEST,
  	RTC_READ_REQUEST_AVAILABLE,
  } rtc_read_request_state = RTC_READ_NO_REQUEST;
  
  static enum {
  	RTC_WRITE_NO_REQUEST,
  	RTC_WRITE_PENDING_REQUEST,
  	RTC_WRITE_REQUEST_AVAILABLE,
  } rtc_write_request_state = RTC_WRITE_NO_REQUEST;
  
  static bool rtc_tod_cache_dirty = false;
  
  /* TODO We'd probably want to export and use this variable declared in fsp.c,
   * instead of each component individually maintaining the state.. may be for
   * later optimization
   */
  static bool fsp_in_reset = false;
  
  struct opal_tpo_data {
  	uint64_t tpo_async_token;
  	uint32_t *year_month_day;
  	uint32_t *hour_min;
  };
  
  /* Timebase value when we last initiated a RTC read request */
  static unsigned long read_req_tb;
  
  /* If a RTC read takes longer than this, we return a value generated
   * from the cache + timebase */
  static const int rtc_read_timeout_ms = 1500;
  
  DEFINE_LOG_ENTRY(OPAL_RC_RTC_TOD, OPAL_PLATFORM_ERR_EVT, OPAL_RTC,
  			OPAL_PLATFORM_FIRMWARE, OPAL_INFO,
  			OPAL_NA, NULL);
  
  DEFINE_LOG_ENTRY(OPAL_RC_RTC_READ, OPAL_PLATFORM_ERR_EVT, OPAL_RTC,
  			OPAL_PLATFORM_FIRMWARE, OPAL_INFO,
  			OPAL_NA, NULL);
  
  static void fsp_tpo_req_complete(struct fsp_msg *read_resp)
  {
  	struct opal_tpo_data *attr = read_resp->user_data;
  	int val;
  	int rc;
  
  	val = (read_resp->resp->word1 >> 8) & 0xff;
  	switch (val) {
  	case FSP_STATUS_TOD_RESET:
  		log_simple_error(&e_info(OPAL_RC_RTC_TOD),
  			"RTC TPO in invalid state\n");
  		rc = OPAL_INTERNAL_ERROR;
  		break;
  
  	case FSP_STATUS_TOD_PERMANENT_ERROR:
  		log_simple_error(&e_info(OPAL_RC_RTC_TOD),
  			"RTC TPO in permanent error state\n");
  		rc = OPAL_INTERNAL_ERROR;
  		break;
  	case FSP_STATUS_INVALID_DATA:
  		log_simple_error(&e_info(OPAL_RC_RTC_TOD),
  			"RTC TPO in permanent error state\n");
  		rc = OPAL_PARAMETER;
  		break;
  	case FSP_STATUS_SUCCESS:
  		/* Save the read TPO value in our cache */
  		if (attr->year_month_day)
  			*(attr->year_month_day) =
  				read_resp->resp->data.words[0];
  		if (attr->hour_min)
  			*(attr->hour_min) = read_resp->resp->data.words[1];
  		rc = OPAL_SUCCESS;
  		break;
  
  	default:
  		log_simple_error(&e_info(OPAL_RC_RTC_TOD),
  			"TPO read failed: %d\n", val);
  		rc = OPAL_INTERNAL_ERROR;
  		break;
  	}
  	opal_queue_msg(OPAL_MSG_ASYNC_COMP, NULL, NULL,
  		       attr->tpo_async_token, rc);
  	free(attr);
  	fsp_freemsg(read_resp);
  }
  
  static void fsp_rtc_process_read(struct fsp_msg *read_resp)
  {
  	int val = (read_resp->word1 >> 8) & 0xff;
  	struct tm tm;
  
  	assert(lock_held_by_me(&rtc_lock));
  
  	assert(rtc_read_request_state == RTC_READ_PENDING_REQUEST);
  
  	switch (val) {
  	case FSP_STATUS_TOD_RESET:
  		log_simple_error(&e_info(OPAL_RC_RTC_TOD),
  				"RTC TOD in invalid state\n");
  		rtc_tod_state = RTC_TOD_INVALID;
  		rtc_read_request_state = RTC_READ_NO_REQUEST;
  		break;
  
  	case FSP_STATUS_TOD_PERMANENT_ERROR:
  		log_simple_error(&e_info(OPAL_RC_RTC_TOD),
  			"RTC TOD in permanent error state\n");
  		rtc_tod_state = RTC_TOD_PERMANENT_ERROR;
  		rtc_read_request_state = RTC_READ_NO_REQUEST;
  		break;
  
  	case FSP_STATUS_SUCCESS:
  		/* Save the read RTC value in our cache */
  		rtc_read_request_state = RTC_READ_REQUEST_AVAILABLE;
  		rtc_tod_state = RTC_TOD_VALID;
  		datetime_to_tm(read_resp->data.words[0],
  			       (u64) read_resp->data.words[1] << 32, &tm);
  		rtc_cache_update(&tm);
  		prlog(PR_TRACE, "FSP-RTC Got time: %d-%d-%d %d:%d:%d\n",
  		      tm.tm_year, tm.tm_mon, tm.tm_mday,
  		      tm.tm_hour, tm.tm_min, tm.tm_sec);
  		break;
  
  	default:
  		log_simple_error(&e_info(OPAL_RC_RTC_TOD),
  				"RTC TOD read failed: %d\n", val);
  		rtc_tod_state = RTC_TOD_INVALID;
  		rtc_read_request_state = RTC_READ_NO_REQUEST;
  	}
  }
  
  static void opal_rtc_eval_events(bool read_write)
  {
  	bool request_available;
  
  	if (read_write)
  		request_available = (rtc_read_request_state ==
  				     RTC_READ_REQUEST_AVAILABLE);
  	else
  		request_available = (rtc_write_request_state ==
  				    RTC_WRITE_REQUEST_AVAILABLE);
  
  	assert(lock_held_by_me(&rtc_lock));
  	opal_update_pending_evt(OPAL_EVENT_RTC,
  				request_available ? OPAL_EVENT_RTC : 0);
  }
  
  static void fsp_rtc_req_complete(struct fsp_msg *msg)
  {
  	lock(&rtc_lock);
  	prlog(PR_TRACE, "RTC completion %p\n", msg);
  
  	if (fsp_msg_cmd(msg) == (FSP_CMD_READ_TOD & 0xffffff)) {
  		fsp_rtc_process_read(msg->resp);
  		opal_rtc_eval_events(true);
  	} else {
  		assert(rtc_write_request_state == RTC_WRITE_PENDING_REQUEST);
  		rtc_write_request_state = RTC_WRITE_REQUEST_AVAILABLE;
  		opal_rtc_eval_events(false);
  	}
  
  	unlock(&rtc_lock);
  	fsp_freemsg(msg);
  }
  
  static int64_t fsp_rtc_send_read_request(void)
  {
  	struct fsp_msg *msg;
  	int rc;
  
  	assert(lock_held_by_me(&rtc_lock));
  	assert(rtc_read_request_state == RTC_READ_NO_REQUEST);
  
  	msg = fsp_mkmsg(FSP_CMD_READ_TOD, 0);
  	if (!msg) {
  		log_simple_error(&e_info(OPAL_RC_RTC_READ),
  			"RTC: failed to allocate read message\n");
  		return OPAL_INTERNAL_ERROR;
  	}
  
  	rc = fsp_queue_msg(msg, fsp_rtc_req_complete);
  	if (rc) {
  		fsp_freemsg(msg);
  		log_simple_error(&e_info(OPAL_RC_RTC_READ),
  			"RTC: failed to queue read message: %d\n", rc);
  		return OPAL_INTERNAL_ERROR;
  	}
  
  	rtc_read_request_state = RTC_READ_PENDING_REQUEST;
  
  	read_req_tb = mftb();
  
  	return OPAL_BUSY_EVENT;
  }
  
  static int64_t fsp_opal_rtc_read(uint32_t *year_month_day,
  				 uint64_t *hour_minute_second_millisecond)
  {
  	int64_t rc;
  
  	if (!year_month_day || !hour_minute_second_millisecond)
  		return OPAL_PARAMETER;
  
  	lock(&rtc_lock);
  
  	if (rtc_tod_state == RTC_TOD_PERMANENT_ERROR) {
  		rc = OPAL_HARDWARE;
  		goto out;
  	}
  
  	/* During R/R of FSP, read cached TOD */
  	if (fsp_in_reset) {
  		if (rtc_tod_state == RTC_TOD_VALID) {
  			rtc_cache_get_datetime(year_month_day,
  					       hour_minute_second_millisecond);
  			rc = OPAL_SUCCESS;
  		} else {
  			rc = OPAL_INTERNAL_ERROR;
  		}
  		goto out;
  	}
  
  	/* If we don't have a read pending already, fire off a request and
  	 * return */
  	if (rtc_read_request_state == RTC_READ_NO_REQUEST) {
  		prlog(PR_TRACE, "Sending new RTC read request\n");
  		rc = fsp_rtc_send_read_request();
  	/* If our pending read is done, clear events and return the time
  	 * from the cache */
  	} else if (rtc_read_request_state == RTC_READ_REQUEST_AVAILABLE) {
                  prlog(PR_TRACE, "RTC read complete, state %d\n", rtc_tod_state);
  		rtc_read_request_state = RTC_READ_NO_REQUEST;
  
                  opal_rtc_eval_events(true);
  
                  if (rtc_tod_state == RTC_TOD_VALID) {
                          rtc_cache_get_datetime(year_month_day,
  					       hour_minute_second_millisecond);
                          prlog(PR_TRACE,"FSP-RTC Cached datetime: %x %llx\n",
                                *year_month_day,
                                *hour_minute_second_millisecond);
                          rc = OPAL_SUCCESS;
                  } else {
                          rc = OPAL_INTERNAL_ERROR;
  		}
  
  	/* Timeout: return our cached value (updated from tb), but leave the
  	 * read request pending so it will update the cache later */
  	} else if (mftb() > read_req_tb + msecs_to_tb(rtc_read_timeout_ms)) {
  		prlog(PR_TRACE, "RTC read timed out\n");
  
  		if (rtc_tod_state == RTC_TOD_VALID) {
  			rtc_cache_get_datetime(year_month_day,
  					       hour_minute_second_millisecond);
  			rc = OPAL_SUCCESS;
  		} else {
                          rc = OPAL_INTERNAL_ERROR;
  		}
  	/* Otherwise, we're still waiting on the read to complete */
  	} else {
  		assert(rtc_read_request_state == RTC_READ_PENDING_REQUEST);
  		rc = OPAL_BUSY_EVENT;
  	}
  out:
  	unlock(&rtc_lock);
  	return rc;
  }
  
  static int64_t fsp_rtc_send_write_request(uint32_t year_month_day,
  					  uint64_t hour_minute_second_millisecond)
  {
  	struct fsp_msg *msg;
  	uint32_t w0, w1, w2;
  	struct tm tm;
  
  	assert(lock_held_by_me(&rtc_lock));
  	assert(rtc_write_request_state == RTC_WRITE_NO_REQUEST);
  
  	/* Create a request and send it. Just like for read, we ignore
  	 * the "millisecond" field which is probably supposed to be
  	 * microseconds and which Linux ignores as well anyway
  	 */
  	w0 = year_month_day;
  	w1 = (hour_minute_second_millisecond >> 32) & 0xffffff00;
  	w2 = 0;
  
  	msg = fsp_mkmsg(FSP_CMD_WRITE_TOD, 3, w0, w1, w2);
  	if (!msg) {
  		prlog(PR_TRACE, " -> allocation failed !\n");
  		return OPAL_INTERNAL_ERROR;
  	}
  	prlog(PR_TRACE, " -> req at %p\n", msg);
  
  	if (fsp_in_reset) {
  		datetime_to_tm(msg->data.words[0],
  			       (u64) msg->data.words[1] << 32,  &tm);
  		rtc_cache_update(&tm);
  		rtc_tod_cache_dirty = true;
  		fsp_freemsg(msg);
  		return OPAL_SUCCESS;
  	} else if (fsp_queue_msg(msg, fsp_rtc_req_complete)) {
  		prlog(PR_TRACE, " -> queueing failed !\n");
  		fsp_freemsg(msg);
  		return OPAL_INTERNAL_ERROR;
  	}
  
  	rtc_write_request_state = RTC_WRITE_PENDING_REQUEST;
  
  	return OPAL_BUSY_EVENT;
  }
  
  static int64_t fsp_opal_rtc_write(uint32_t year_month_day,
  				  uint64_t hour_minute_second_millisecond)
  {
  	int rc;
  
  	lock(&rtc_lock);
  	if (rtc_tod_state == RTC_TOD_PERMANENT_ERROR) {
  		rc = OPAL_HARDWARE;
  		goto out;
  	}
  
  	if (rtc_write_request_state == RTC_WRITE_NO_REQUEST) {
  		prlog(PR_TRACE, "Sending new RTC write request\n");
  		rc = fsp_rtc_send_write_request(year_month_day,
  						hour_minute_second_millisecond);
  	} else if (rtc_write_request_state == RTC_WRITE_PENDING_REQUEST) {
  		rc = OPAL_BUSY_EVENT;
  	} else {
  		assert(rtc_write_request_state == RTC_WRITE_REQUEST_AVAILABLE);
  		rtc_write_request_state = RTC_WRITE_NO_REQUEST;
  
  		opal_rtc_eval_events(false);
  		rc = OPAL_SUCCESS;
  	}
  
  out:
  	unlock(&rtc_lock);
  	return rc;
  }
  
  /* Set timed power on values to fsp */
  static int64_t fsp_opal_tpo_write(uint64_t async_token, uint32_t y_m_d,
  			uint32_t hr_min)
  {
  	static struct opal_tpo_data *attr;
  	struct fsp_msg *msg;
  
  	if (!fsp_present())
  		return OPAL_HARDWARE;
  
  	attr = zalloc(sizeof(struct opal_tpo_data));
  	if (!attr)
  		return OPAL_NO_MEM;
  
  	/* Create a request and send it.*/
  	attr->tpo_async_token = async_token;
  
  	prlog(PR_TRACE, "Sending TPO write request...\n");
  
  	msg = fsp_mkmsg(FSP_CMD_TPO_WRITE, 2, y_m_d, hr_min);
  	if (!msg) {
  		prerror("TPO: Failed to create message for WRITE to FSP\n");
  		free(attr);
  		return OPAL_INTERNAL_ERROR;
  	}
  	msg->user_data = attr;
  	if (fsp_queue_msg(msg, fsp_tpo_req_complete)) {
  		free(attr);
  		fsp_freemsg(msg);
  		return OPAL_INTERNAL_ERROR;
  	}
  	return OPAL_ASYNC_COMPLETION;
  }
  
  /* Read Timed power on (TPO) from FSP */
  static int64_t fsp_opal_tpo_read(uint64_t async_token, uint32_t *y_m_d,
  			uint32_t *hr_min)
  {
  	static struct opal_tpo_data *attr;
  	struct fsp_msg *msg;
  	int64_t rc;
  
  	if (!fsp_present())
  		return OPAL_HARDWARE;
  
  	if (!y_m_d || !hr_min)
  		return OPAL_PARAMETER;
  
  	attr = zalloc(sizeof(*attr));
  	if (!attr)
  		return OPAL_NO_MEM;
  
  	/* Send read requet to FSP */
  	attr->tpo_async_token = async_token;
  	attr->year_month_day = y_m_d;
  	attr->hour_min = hr_min;
  
  	prlog(PR_TRACE, "Sending new TPO read request\n");
  	msg = fsp_mkmsg(FSP_CMD_TPO_READ, 0);
  	if (!msg) {
  		log_simple_error(&e_info(OPAL_RC_RTC_READ),
  			"TPO: failed to allocate read message\n");
  		free(attr);
  		return OPAL_INTERNAL_ERROR;
  	}
  	msg->user_data = attr;
  	rc = fsp_queue_msg(msg, fsp_tpo_req_complete);
  	if (rc) {
  		free(attr);
  		fsp_freemsg(msg);
  		log_simple_error(&e_info(OPAL_RC_RTC_READ),
  			"TPO: failed to queue read message: %lld\n", rc);
  		return OPAL_INTERNAL_ERROR;
  	}
  	return OPAL_ASYNC_COMPLETION;
  }
  
  static void rtc_flush_cached_tod(void)
  {
  	struct fsp_msg *msg;
  	uint64_t h_m_s_m;
  	uint32_t y_m_d;
  
  	if (rtc_cache_get_datetime(&y_m_d, &h_m_s_m))
  		return;
  	msg = fsp_mkmsg(FSP_CMD_WRITE_TOD, 3, y_m_d,
  			(h_m_s_m >> 32) & 0xffffff00, 0);
  	if (!msg) {
  		prerror("TPO: %s : Failed to allocate write TOD message\n",
  			__func__);
  		return;
  	}
  	if (fsp_queue_msg(msg, fsp_freemsg)) {
  		fsp_freemsg(msg);
  		prerror("TPO: %s : Failed to queue WRITE_TOD command\n",
  			__func__);
  		return;
  	}
  }
  
  static bool fsp_rtc_msg_rr(u32 cmd_sub_mod, struct fsp_msg *msg)
  {
  
  	int rc = false;
  	assert(msg == NULL);
  
  	switch (cmd_sub_mod) {
  	case FSP_RESET_START:
  		lock(&rtc_lock);
  		fsp_in_reset = true;
  		unlock(&rtc_lock);
  		rc = true;
  		break;
  	case FSP_RELOAD_COMPLETE:
  		lock(&rtc_lock);
  		fsp_in_reset = false;
  		if (rtc_tod_cache_dirty) {
  			rtc_flush_cached_tod();
  			rtc_tod_cache_dirty = false;
  		}
  		unlock(&rtc_lock);
  		rc = true;
  		break;
  	}
  
  	return rc;
  }
  
  static struct fsp_client fsp_rtc_client_rr = {
  	.message = fsp_rtc_msg_rr,
  };
  
  void fsp_rtc_init(void)
  {
  	struct dt_node *np;
  
  	if (!fsp_present()) {
  		rtc_tod_state = RTC_TOD_PERMANENT_ERROR;
  		return;
  	}
  
  	opal_register(OPAL_RTC_READ, fsp_opal_rtc_read, 2);
  	opal_register(OPAL_RTC_WRITE, fsp_opal_rtc_write, 2);
  	opal_register(OPAL_WRITE_TPO, fsp_opal_tpo_write, 3);
  	opal_register(OPAL_READ_TPO,  fsp_opal_tpo_read, 3);
  
  	np = dt_new(opal_node, "rtc");
  	dt_add_property_strings(np, "compatible", "ibm,opal-rtc");
  	dt_add_property(np, "has-tpo", NULL, 0);
  
  	/* Register for the reset/reload event */
  	fsp_register_client(&fsp_rtc_client_rr, FSP_MCLASS_RR_EVENT);
  
  	prlog(PR_TRACE, "Getting initial RTC TOD\n");
  
  	/* We don't wait for RTC response and this is actually okay as
  	 * any OPAL callers will wait correctly and if we ever have
  	 * internal users then they should check the state properly
  	 */
  	lock(&rtc_lock);
  	fsp_rtc_send_read_request();
  	unlock(&rtc_lock);
  }
  