/* 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. */ /* FSP DPO (Delayed Power Off) event support */ #define pr_fmt(fmt) "FSP-DPO: " fmt #include #include #include #include #include #include #define DPO_CMD_SGN_BYTE0 0xf4 /* Byte[0] signature */ #define DPO_CMD_SGN_BYTE1 0x20 /* Byte[1] signature */ #define DPO_TIMEOUT 2700 /* 45 minutes in seconds */ bool fsp_dpo_pending; static unsigned long fsp_dpo_init_tb; /* * OPAL DPO interface * * Returns zero if DPO is not active, positive value indicating number * of seconds remaining for a forced system shutdown. This will enable * the host to schedule for shutdown voluntarily before timeout occurs. */ static int64_t fsp_opal_get_dpo_status(int64_t *dpo_timeout) { if (!fsp_dpo_pending) { *dpo_timeout = 0; return OPAL_WRONG_STATE; } *dpo_timeout = DPO_TIMEOUT - tb_to_secs(mftb() - fsp_dpo_init_tb); return OPAL_SUCCESS; } /* Process FSP DPO init message */ static void fsp_process_dpo(struct fsp_msg *msg) { struct fsp_msg *resp; u32 cmd = FSP_RSP_INIT_DPO; int rc; /* DPO message does not have the correct signatures */ if ((msg->data.bytes[0] != DPO_CMD_SGN_BYTE0) || (msg->data.bytes[1] != DPO_CMD_SGN_BYTE1)) { prerror("Message signatures did not match\n"); cmd |= FSP_STATUS_INVALID_CMD; resp = fsp_mkmsg(cmd, 0); if (resp == NULL) { prerror("%s : Message allocation failed\n", __func__); return; } if (fsp_queue_msg(resp, fsp_freemsg)) { fsp_freemsg(resp); prerror("%s : Failed to queue response " "message\n", __func__); } return; } /* OPAL is already in "DPO pending" state */ if (fsp_dpo_pending) { prlog(PR_INFO, "OPAL already in DPO pending state\n"); cmd |= FSP_STATUS_INVALID_DPOSTATE; resp = fsp_mkmsg(cmd, 0); if (resp == NULL) { prerror("%s : Message allocation failed\n", __func__); return; } if (fsp_queue_msg(resp, fsp_freemsg)) { fsp_freemsg(resp); prerror("%s : Failed to queue response " "message\n", __func__); } return; } /* Inform the host about DPO */ rc = opal_queue_msg(OPAL_MSG_DPO, NULL, NULL); if (rc) { prerror("OPAL message queuing failed\n"); cmd |= FSP_STATUS_GENERIC_ERROR; resp = fsp_mkmsg(cmd, 0); if (resp == NULL) { prerror("%s : Message allocation failed\n", __func__); return; } if (fsp_queue_msg(resp, fsp_freemsg)) { fsp_freemsg(resp); prerror("%s : Failed to queue response " "message\n", __func__); } return; } else prlog(PR_INFO, "Notified host about DPO event\n"); /* Acknowledge the FSP on DPO */ resp = fsp_mkmsg(cmd, 0); if (resp == NULL) { prerror("%s : Message allocation failed\n", __func__); return; } if (fsp_queue_msg(resp, fsp_freemsg)) { fsp_freemsg(resp); prerror("%s : Failed to queue response message\n", __func__); return; } /* Record DPO init time and set DPO pending flag */ fsp_dpo_init_tb = mftb(); fsp_dpo_pending = true; /* * OPAL is now in DPO pending state. After first detecting DPO * condition from OPAL, the host will have 45 minutes to prepare * the system for shutdown. The host must take all necessary actions * required in that regard and at the end shutdown itself. The host * shutdown sequence eventually will make the call OPAL_CEC_POWER_DOWN * which in turn ask the FSP to shutdown the CEC. If the FSP does not * receive the cec power down command from OPAL within 45 minutes, * it will assume that the host and the OPAL has processed the DPO * sequence successfully and hence force power off the system. */ } /* Handle DPO sub-command from FSP */ static bool fsp_dpo_message(u32 cmd_sub_mod, struct fsp_msg *msg) { if (cmd_sub_mod == FSP_CMD_INIT_DPO) { prlog(PR_INFO, "Delayed Power Off (DPO) notification received\n"); fsp_process_dpo(msg); return true; } return false; } static struct fsp_client fsp_dpo_client = { .message = fsp_dpo_message, }; void fsp_dpo_init(void) { fsp_register_client(&fsp_dpo_client, FSP_MCLASS_SERVICE); opal_register(OPAL_GET_DPO_STATUS, fsp_opal_get_dpo_status, 1); prlog(PR_INFO, "FSP DPO support initialized\n"); }