/* * Arm SCP/MCP Software * Copyright (c) 2020-2022, Arm Limited and Contributors. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ #include #include #include #include #include #include #include #include #include #include #include static struct debug_dev_ctx *ctx_table; static inline void mark_user(fwk_id_t id, bool enable, enum scp_debug_user user_id) { uint32_t user_mask = (1 << user_id); struct debug_dev_ctx *ctx = &ctx_table[fwk_id_get_element_idx(id)]; if (enable) { /* Add user to the list of users that is enabling this element */ ctx->debug_users_mask |= user_mask; } else { /* Remove current user */ ctx->debug_users_mask &= ~user_mask; } } /* * APIs */ /* * This function can be called by the USER_AP as part of processing a request * coming (usually) from scmi_power_domain. * It can also be called from this module when processing an event that was * generated by the driver (USER_DAP). In this case, there is no need to submit * an event as per 'deferred_response_architecture', because the * driver does not require any response. */ static int set_enabled(fwk_id_t id, bool enable, enum scp_debug_user user_id) { uint32_t user_mask; struct debug_dev_ctx *ctx; struct fwk_event req; int status; struct mod_debug_request_params *req_params; if (!fwk_expect(user_id < SCP_DEBUG_USER_COUNT)) { return FWK_E_PARAM; } ctx = &ctx_table[fwk_id_get_element_idx(id)]; if (ctx->state != DEBUG_IDLE) { return FWK_E_BUSY; } user_mask = (1 << user_id); /* * Ensure that the user attempting to enable the element had not already * enabled it. */ if ((enable && (ctx->debug_users_mask & user_mask)) || (!enable && (!(ctx->debug_users_mask & user_mask)))) { return FWK_E_ACCESS; } status = ctx->driver_api->set_enabled(ctx->config->driver_id, enable, user_id); if (status == FWK_PENDING) { if (user_id == SCP_DEBUG_USER_DAP) { /* * In case the request comes from the driver, we won't respond so * we don't even need to put an event */ ctx->requester = user_id; ctx->state = DEBUG_SET; return FWK_PENDING; } req = (struct fwk_event) { .target_id = id, .id = mod_debug_event_id_req_enable_set, .response_requested = true, }; req_params = (struct mod_debug_request_params *)&req.params; req_params->user_id = user_id; req_params->enable = enable; status = fwk_put_event(&req); if (status == FWK_SUCCESS) { ctx->state = DEBUG_SET; return FWK_PENDING; } return status; } else if (status != FWK_SUCCESS) { return status; } mark_user(id, enable, user_id); return FWK_SUCCESS; } static int get_enabled(fwk_id_t id, bool *enable, enum scp_debug_user user_id) { struct debug_dev_ctx *ctx; struct fwk_event req; int status; struct mod_debug_request_params *req_params; if ((enable == NULL) || !fwk_expect(user_id < SCP_DEBUG_USER_COUNT)) { return FWK_E_PARAM; } ctx = &ctx_table[fwk_id_get_element_idx(id)]; if (ctx->state != DEBUG_IDLE) { return FWK_E_BUSY; } status = ctx->driver_api->get_enabled(ctx->config->driver_id, enable, user_id); if (status == FWK_PENDING) { req = (struct fwk_event) { .target_id = id, .id = mod_debug_event_id_req_enable_get, .response_requested = true, }; req_params = (struct mod_debug_request_params *)&req.params; req_params->user_id = user_id; status = fwk_put_event(&req); if (status == FWK_SUCCESS) { ctx->state = DEBUG_GET; return FWK_PENDING; } return status; } return status; } static int debug_reset(fwk_id_t id) { return FWK_E_SUPPORT; } static int debug_end(fwk_id_t id) { return FWK_E_SUPPORT; } static const struct mod_debug_api debug_api = { .set_enabled = set_enabled, .get_enabled = get_enabled, .reset = debug_reset, .end = debug_end, }; /* * Driver Input API */ static int enable_request(fwk_id_t id, bool enable, enum scp_debug_user user_id) { struct fwk_event req; struct mod_debug_request_params *req_params; struct debug_dev_ctx *ctx; ctx = &ctx_table[fwk_id_get_element_idx(id)]; fwk_assert(user_id == SCP_DEBUG_USER_DAP); req = (struct fwk_event) { .target_id = id, .source_id = ctx->config->driver_id, .id = mod_debug_event_id_req_drv, }; req_params = (struct mod_debug_request_params *)&req.params; req_params->user_id = user_id; req_params->enable = enable; return fwk_put_event(&req); } /* * Driver Response API */ static void request_complete(fwk_id_t id, struct mod_debug_response_params *response) { int status; struct fwk_event event; struct debug_dev_ctx *ctx; struct mod_debug_response_params *resp_params = (struct mod_debug_response_params *)event.params; fwk_assert(response != NULL); ctx = &ctx_table[fwk_id_get_element_idx(id)]; if (ctx->requester == SCP_DEBUG_USER_DAP) { mark_user(id, response->enabled, ctx->requester); ctx->state = DEBUG_IDLE; return; } event = (struct fwk_event) { .source_id = ctx->config->driver_id, .target_id = id, }; if (ctx->state == DEBUG_GET) { event.id = mod_debug_event_id_get_complete; } else { event.id = mod_debug_event_id_set_complete; } resp_params->status = response->status; resp_params->enabled = response->enabled; status = fwk_put_event(&event); fwk_assert(status == FWK_SUCCESS); } static const struct mod_debug_driver_input_api driver_input_api = { .enable = enable_request, .request_complete = request_complete, }; /* * Framework handlers */ static int mod_debug_init( fwk_id_t module_id, unsigned int element_count, const void *unused) { fwk_assert(element_count > 0); ctx_table = fwk_mm_calloc(element_count, sizeof(struct debug_dev_ctx)); if (ctx_table == NULL) { return FWK_E_NOMEM; } return FWK_SUCCESS; } static int mod_debug_dev_init( fwk_id_t element_id, unsigned int sub_element_count, const void *data) { struct debug_dev_ctx *ctx; struct mod_debug_dev_config *config; ctx = &ctx_table[fwk_id_get_element_idx(element_id)]; config = (struct mod_debug_dev_config*)data; fwk_assert(config != NULL); ctx->config = config; return FWK_SUCCESS; } static int mod_debug_bind(fwk_id_t id, unsigned int round) { int status; struct mod_debug_driver_api *driver_api = NULL; struct debug_dev_ctx *ctx; struct mod_debug_dev_config *dev_cfg; if ((round > 0) || fwk_id_is_type(id, FWK_ID_TYPE_MODULE)) { return FWK_SUCCESS; } ctx = &ctx_table[fwk_id_get_element_idx(id)]; dev_cfg = ctx->config; /* Bind to debug driver */ status = fwk_module_bind( dev_cfg->driver_id, dev_cfg->driver_api_id, &driver_api); if (status != FWK_SUCCESS) { return status; } /* Validate driver API */ if ((driver_api->set_enabled == NULL) || (driver_api->get_enabled == NULL)) { return FWK_E_DATA; } ctx->driver_api = driver_api; return FWK_SUCCESS; } static int mod_debug_process_bind_request( fwk_id_t source_id, fwk_id_t target_id, fwk_id_t api_id, const void **api) { struct debug_dev_ctx *ctx; unsigned int api_idx; api_idx = fwk_id_get_api_idx(api_id); /* Only binding to elements is allowed */ if (!fwk_id_is_type(target_id, FWK_ID_TYPE_ELEMENT)) { return FWK_E_ACCESS; } ctx = &ctx_table[fwk_id_get_element_idx(target_id)]; switch (api_idx) { case MOD_DEBUG_API_IDX_HAL: *api = &debug_api; return FWK_SUCCESS; case MOD_DEBUG_API_IDX_DRIVER_INPUT: if (fwk_id_is_equal(source_id, ctx->config->driver_id)) { *api = &driver_input_api; } return FWK_SUCCESS; default: return FWK_E_PARAM; } } static int respond(const struct fwk_event *event) { int status; struct fwk_event response; struct mod_debug_response_params *resp_params = (struct mod_debug_response_params *)response.params; struct mod_debug_response_params *req_result = (struct mod_debug_response_params *)event->params; struct debug_dev_ctx *ctx = &ctx_table[fwk_id_get_element_idx(event->target_id)]; ctx->state = DEBUG_IDLE; status = fwk_get_delayed_response(event->target_id, ctx->cookie, &response); if (status != FWK_SUCCESS) { return status; } resp_params->status = req_result->status; resp_params->enabled = req_result->enabled; return fwk_put_event(&response); } static int mod_debug_process_event(const struct fwk_event *event, struct fwk_event *resp_event) { int status; struct debug_dev_ctx *ctx; struct mod_debug_request_params *req_params; struct mod_debug_response_params *req_result; ctx = &ctx_table[fwk_id_get_element_idx(event->target_id)]; switch (fwk_id_get_event_idx(event->id)) { case MOD_DEBUG_PUBLIC_EVENT_IDX_REQ_ENABLE_GET: case MOD_DEBUG_PUBLIC_EVENT_IDX_REQ_ENABLE_SET: case MOD_DEBUG_EVENT_IDX_REQ_DRV: req_params = (struct mod_debug_request_params *)event->params; ctx->requester = req_params->user_id; /* Request from USER_AP we will need to respond */ if (resp_event->response_requested) { ctx->cookie = event->cookie; resp_event->is_delayed_response = true; return FWK_SUCCESS; } /* * When there is no response requested, we are processing an event * generated by the driver (MOD_DEBUG_EVENT_IDX_REQ_DRV) */ status = set_enabled(ctx->config->driver_id, req_params->enable, req_params->user_id); if (status == FWK_PENDING) { break; } if (status != FWK_SUCCESS) { return FWK_E_DEVICE; } break; case MOD_DEBUG_EVENT_IDX_SET_COMPLETE: req_result = (struct mod_debug_response_params *)event->params; if (req_result->status == FWK_SUCCESS) { mark_user(event->target_id, req_result->enabled, ctx->requester); } return respond(event); case MOD_DEBUG_EVENT_IDX_GET_COMPLETE: return respond(event); default: return FWK_E_PARAM; } return FWK_SUCCESS; } const struct fwk_module module_debug = { .api_count = MOD_DEBUG_API_IDX_COUNT, .event_count = MOD_DEBUG_EVENT_COUNT, .type = FWK_MODULE_TYPE_HAL, .init = mod_debug_init, .element_init = mod_debug_dev_init, .bind = mod_debug_bind, .process_bind_request = mod_debug_process_bind_request, .process_event = mod_debug_process_event, };