/* 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 #include #include #include #include #include #include #include #include #include /* * 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 /* 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); DEFINE_LOG_ENTRY(OPAL_RC_RTC_READ, OPAL_PLATFORM_ERR_EVT, OPAL_RTC, OPAL_PLATFORM_FIRMWARE, OPAL_INFO, OPAL_NA); 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); }