| /* |
| <:copyright-BRCM:2016:DUAL/GPL:standard |
| |
| Broadcom Proprietary and Confidential.(c) 2016 Broadcom |
| All Rights Reserved |
| |
| Unless you and Broadcom execute a separate written software license |
| agreement governing use of this software, this software is licensed |
| to you under the terms of the GNU General Public License version 2 |
| (the "GPL"), available at http://www.broadcom.com/licenses/GPLv2.php, |
| with the following added to such license: |
| |
| As a special exception, the copyright holders of this software give |
| you permission to link this software with independent modules, and |
| to copy and distribute the resulting executable under terms of your |
| choice, provided that you also meet, for each linked independent |
| module, the terms and conditions of the license of that module. |
| An independent module is a module which is not derived from this |
| software. The special exception does not apply to any modifications |
| of the software. |
| |
| Not withstanding the above, under no circumstances may you combine |
| this software in any way with any other Broadcom software provided |
| under a license other than the GPL, without Broadcom's express prior |
| written consent. |
| |
| :> |
| */ |
| |
| #include <bcmos_system.h> |
| #include <bcm_config.h> |
| #include <bcmolt_msg.h> |
| #include <bcmolt_tr_mux.h> |
| #include <bcmolt_api.h> |
| #include <bcmolt_model_types.h> |
| #include <bcmolt_model_ids.h> |
| #include <bcmolt_host_sw_version.h> |
| #include <bcmolt_model_revision.h> |
| #include "bcmolt_dev_ctrl.h" |
| #include "bcmtr_pcie.h" |
| #include "bcmolt_llpcie.h" |
| #include "bcm_dev_log.h" |
| #include "bcmolt_utils.h" |
| #include "bcmolt_firmware_envelope.h" |
| #include "bcmolt_msg_pack.h" |
| #include "bcmolt_mh_utils.h" |
| #include "bcmolt_debug_ctrl.h" |
| |
| static bcmolt_dev_ctrl_params dev_ctrl_params; |
| static dev_ctrl_database dev_ctrl_db[BCMTR_MAX_OLTS]; |
| static uint8_t *image_buf[BCMTR_MAX_OLTS]; |
| static uint8_t *rd_image_buf[BCMTR_MAX_OLTS]; |
| |
| #define BCM_DEV_CTRL_RETURN_ON_ERROR(device, rc, fmt, args...)\ |
| do\ |
| {\ |
| if (BCM_ERR_OK != rc)\ |
| {\ |
| BCM_LOG(ERROR, dev_ctrl_db[device].log_id, "%s: "fmt, bcmos_strerror(rc), ## args);\ |
| return rc;\ |
| }\ |
| } while (0) |
| |
| #ifndef IN_BAND |
| static bcmos_timer_rc dev_ctrl_boot_seq_timer_handler(bcmos_timer *timer, long data); |
| static bcmos_timer_rc dev_ctrl_ddr_test_timer_handler(bcmos_timer *timer, long data); |
| #endif |
| |
| const char *bcm_str_device_state(bcmolt_device_state device_state) |
| { |
| static const char *strings[] = |
| { |
| [BCMOLT_DEVICE_STATE_DISCONNECTED] = "DISCONNECTED", |
| [BCMOLT_DEVICE_STATE_CONNECTING] = "CONNECTING", |
| [BCMOLT_DEVICE_STATE_READY] = "READY", |
| [BCMOLT_DEVICE_STATE_WAITING_FOR_DEVICE] = "WAITING_FOR_DEVICE", |
| [BCMOLT_DEVICE_STATE__NUM_OF] = "UNKNOWN" |
| }; |
| return strings[device_state > BCMOLT_DEVICE_STATE__NUM_OF ? BCMOLT_DEVICE_STATE__NUM_OF : device_state]; |
| } |
| |
| const char *bcm_str_device_event(dev_ctrl_event device_event) |
| { |
| static const char *strings[] = |
| { |
| [DEVICE_CONTROL_EVENT_DEVICE_CLEAR] = "DEVICE_CLEAR", |
| [DEVICE_CONTROL_EVENT_DEVICE_CONFIG_SET] = "DEVICE_CONFIG_SET", |
| [DEVICE_CONTROL_EVENT_DEVICE_CONFIG_GET] = "DEVICE_CONFIG_GET", |
| [DEVICE_CONTROL_EVENT_DEVICE_DISCONNECT] = "DEVICE_DISCONNECT", |
| [DEVICE_CONTROL_EVENT_DEVICE_TIMER_TIMEOUT] = "TIMER_TIMEOUT", |
| [DEVICE_CONTROL_EVENT_DEVICE_RECEIVED_ACK] = "RECEIVED_ACK", |
| [DEVICE_CONTROL_EVENT_DEVICE_CONNECT] = "DEVICE_CONNECT", |
| [DEVICE_CONTROL_EVENT_DEVICE_RESET] = "DEVICE_RESET", |
| [DEVICE_CONTROL_EVENT_CONNECTION_FAILURE] = "CONNECTION_FAILURE", |
| [DEVICE_CONTROL_EVENT_CONNECTION_ESTABLISHED] = "CONNECTION_ESTABLISHED", |
| [DEVICE_CONTROL_EVENT_DEVICE_READY] = "DEVICE_READY", |
| [DEVICE_CONTROL_EVENT_RUN_DDR_TEST] = "RUN_DDR_TEST", |
| [DEVICE_CONTROL_EVENT_DDR_TEST_COMPLETED] = "DDR_TEST_COMPLETED", |
| [DEVICE_CONTROL_EVENT_DDR_TEST_TIMEOUT] = "DDR_TEST_TIMEOUT", |
| [DEVICE_CONTROL_EVENT__NUM_OF] = "UNKNOWN" |
| }; |
| return strings[device_event > DEVICE_CONTROL_EVENT__NUM_OF ? DEVICE_CONTROL_EVENT__NUM_OF : device_event]; |
| } |
| |
| #ifdef ENABLE_LOG |
| static const char *bcm_str_auto_id(bcmolt_device_auto_id auto_id) |
| { |
| static const char *strings[] = |
| { |
| [BCMOLT_DEVICE_AUTO_ID_DEVICE_READY] = "DEVICE_READY", |
| [BCMOLT_DEVICE_AUTO_ID_CONNECTION_ESTABLISHED] = "CONNECTION_ESTABLISHED", |
| [BCMOLT_DEVICE_AUTO_ID_DEVICE_KEEP_ALIVE] = "DEVICE_KEEP_ALIVE", |
| [BCMOLT_DEVICE_AUTO_ID_CONNECTION_FAILURE] = "CONNECTION_FAILURE", |
| [BCMOLT_DEVICE_AUTO_ID_CONNECTION_COMPLETE] = "CONNECTION_COMPLETE", |
| [BCMOLT_DEVICE_AUTO_ID_DISCONNECTION_COMPLETE] = "DISCONNECTION_COMPLETE", |
| [BCMOLT_DEVICE_AUTO_ID_SW_ERROR] = "SW_ERROR", |
| [BCMOLT_DEVICE_AUTO_ID__NUM_OF] = "UNKNOWN" |
| }; |
| return strings[auto_id > BCMOLT_DEVICE_AUTO_ID__NUM_OF ? BCMOLT_DEVICE_AUTO_ID__NUM_OF : auto_id]; |
| } |
| #endif |
| |
| const char *bcm_str_host_connection_fail_reason(bcmolt_host_connection_fail_reason reason) |
| { |
| static const char *strings[] = |
| { |
| [BCMOLT_HOST_CONNECTION_FAIL_REASON_TIMEOUT] = "TIMEOUT", |
| [BCMOLT_HOST_CONNECTION_FAIL_REASON_KEEPALIVE] = "KEEPALIVE", |
| [BCMOLT_HOST_CONNECTION_FAIL_REASON_USER_CALLBACK_ERROR] = "USER_CALLBACK_ERROR", |
| [BCMOLT_HOST_CONNECTION_FAIL_REASON_SOFTWARE_VERSION_MISMATCH] = "SOFTWARE_VERSION_MISMATCH", |
| [BCMOLT_HOST_CONNECTION_FAIL_REASON_SYSTEM_MODE_MISMATCH] = "SYSTEM_MODE_MISMATCH", |
| [BCMOLT_HOST_CONNECTION_FAIL_REASON_NNI_SPEED_MISMATCH] = "NNI_SPEED_MISMATCH", |
| [BCMOLT_HOST_CONNECTION_FAIL_REASON_RECONNECT_TIMEOUT] = "RECONNECT_TIMEOUT", |
| [BCMOLT_HOST_CONNECTION_FAIL_REASON_INTERNAL_ERROR] = "INTERNAL_ERROR", |
| [BCMOLT_HOST_CONNECTION_FAIL_REASON_SYSTEM_MODE_NOT_SUPPORTED] = "SYSTEM_MODE_NOT_SUPPORTED", |
| [BCMOLT_HOST_CONNECTION_FAIL_REASON_PARAMETER] = "PARAMETER", |
| [BCMOLT_HOST_CONNECTION_FAIL_REASON__NUM_OF] = "UNKNOWN" |
| }; |
| return strings[ |
| reason > BCMOLT_HOST_CONNECTION_FAIL_REASON__NUM_OF ? BCMOLT_HOST_CONNECTION_FAIL_REASON__NUM_OF : reason]; |
| } |
| |
| const char *bcm_str_host_connecting_state(dev_ctrl_connecting_state state) |
| { |
| static const char *strings[] = |
| { |
| [DEV_CTRL_CONNECTING_STATE_ESTABLISHING] = "ESTABLISHING", |
| [DEV_CTRL_CONNECTING_STATE_CONFIGURING] = "CONFIGURING", |
| [DEV_CTRL_CONNECTING_STATE_STANDALONE] = "STANDALONE" |
| }; |
| return (state > DEV_CTRL_CONNECTING_STATE_STANDALONE) ? "UNKNOWN" : strings[state]; |
| } |
| |
| static inline bcmos_bool dev_ctrl_is_connected(bcmolt_device_state state) |
| { |
| return (state == BCMOLT_DEVICE_STATE_READY || state == BCMOLT_DEVICE_STATE_WAITING_FOR_DEVICE); |
| } |
| |
| void dev_ctrl_read_db(bcmolt_devid device, dev_ctrl_database *db) |
| { |
| *db = dev_ctrl_db[device]; |
| } |
| |
| #ifndef IN_BAND |
| #ifdef ENABLE_LOG |
| static bcmos_errno dev_ctrl_get_dev_log(bcmolt_devid device, bcmolt_logger_cfg *msg) |
| { |
| char *log_buf; |
| char *log_buf_copy; |
| int log_len; |
| int msg_len; |
| int i; |
| bcmos_errno rc; |
| bcm_dev_log_file log_file; |
| uint32_t msg_buf_offset; |
| |
| if (msg->key.file_id != BCMOLT_LOG_FILE_ID_SRAM) |
| { |
| return bcmolt_msg_err( |
| &msg->hdr.hdr, |
| DEV_LOG_INVALID_ID, |
| BCM_ERR_KEY_RANGE, |
| BCMOLT_LOGGER_KEY_ID_FILE_ID, |
| "While disconnected, only the SRAM logger may be read"); |
| } |
| |
| if (BCMOLT_CFG_PROP_IS_SET(msg, logger, wrap_around)) |
| { |
| return bcmolt_msg_err( |
| &msg->hdr.hdr, |
| DEV_LOG_INVALID_ID, |
| BCM_ERR_RANGE, |
| BCMOLT_LOGGER_CFG_ID_WRAP_AROUND, |
| "wrap_around cannot be retrieved while disconnected"); |
| } |
| |
| if (BCMOLT_CFG_PROP_IS_SET(msg, logger, enable_log)) |
| { |
| return bcmolt_msg_err( |
| &msg->hdr.hdr, |
| DEV_LOG_INVALID_ID, |
| BCM_ERR_RANGE, |
| BCMOLT_LOGGER_CFG_ID_ENABLE_LOG, |
| "enable_log cannot be retrieved while disconnected"); |
| } |
| |
| if (BCMOLT_CFG_PROP_IS_SET(msg, logger, log_names)) |
| { |
| return bcmolt_msg_err( |
| &msg->hdr.hdr, |
| DEV_LOG_INVALID_ID, |
| BCM_ERR_RANGE, |
| BCMOLT_LOGGER_CFG_ID_LOG_NAMES, |
| "log_names cannot be retrieved while disconnected"); |
| } |
| |
| if (!BCMOLT_CFG_PROP_IS_SET(msg, logger, buffer)) |
| { |
| return BCM_ERR_OK; /* nothing to do */ |
| } |
| |
| if (dev_ctrl_db[device].fld_info.soc_sram_base == 0) |
| { |
| return bcmolt_msg_err( |
| &msg->hdr.hdr, |
| DEV_LOG_INVALID_ID, |
| BCM_ERR_STATE, |
| BCMOLT_LOGGER_CFG_ID_BUFFER, |
| "log buffer can only be retreived after initial device connection"); |
| } |
| |
| rc = bcm_fld_get_logs(device, &log_buf, &log_len); |
| BCM_DEV_CTRL_RETURN_ON_ERROR(device, rc, "bcm_fld_get_logs\n"); |
| |
| if (!log_len) |
| return BCM_ERR_OK; |
| |
| /* Make a copy to avoid working over PCIe. Otherwise, the behavior can be unpredictable |
| * based on PCIe transaction size and controller configuration */ |
| log_buf_copy = bcmos_alloc(log_len); |
| if (!log_buf_copy) |
| return BCM_ERR_NOMEM; |
| |
| /* Copy byte by byte. Slow but works for any PCIe configuration */ |
| for (i = 0; i < log_len; i++) |
| log_buf_copy[i] = log_buf[i]; |
| |
| msg->hdr.hdr.presence_mask |= (1 << BCMOLT_LOGGER_CFG_ID_BUFFER); |
| |
| /* Attach log file to the buffer and read from the file */ |
| rc = bcm_dev_log_file_attach(log_buf_copy, log_len, &log_file); |
| |
| if (rc != BCM_ERR_OK || bcm_dev_log_get_num_of_messages(&log_file) == 0) |
| { |
| bcmos_free(log_buf_copy); |
| BCM_LOG(INFO, dev_ctrl_db[device].log_id, "SRAM log buffer is empty\n"); |
| return BCM_ERR_OK; |
| } |
| |
| /* copy all messages from SRAM to the API message buffer (stopping when buffer is full) */ |
| msg_buf_offset = 0; |
| do |
| { |
| msg_len = bcm_dev_log_file_read(&log_file, &dev_ctrl_db[device].sram_log_offset, |
| &msg->data.buffer.buff[msg_buf_offset], sizeof(msg->data.buffer.buff)- msg_buf_offset - 1); |
| if (msg_len <= 0) |
| break; |
| ++dev_ctrl_db[device].msgs_read; |
| /* The last character is 0-terminator. Take it out */ |
| msg_buf_offset += msg_len - 1; |
| } while (msg_buf_offset < sizeof(msg->data.buffer.buff) - 1); |
| |
| /* terminate the string with a null character */ |
| msg->data.buffer.buff[msg_buf_offset] = '\0'; |
| if (msg_len == BCM_ERR_OVERFLOW) |
| { |
| /* More records to read */ |
| msg->data.buffer.msg_to_read = bcm_dev_log_get_num_of_messages(&log_file) - dev_ctrl_db[device].msgs_read; |
| } |
| else |
| { |
| /* all done */ |
| msg->data.buffer.msg_to_read = 0; |
| dev_ctrl_db[device].sram_log_offset = 0; |
| dev_ctrl_db[device].msgs_read = 0; |
| } |
| |
| /* Detach file from buffer */ |
| bcm_dev_log_file_detach(&log_file); |
| |
| bcmos_free(log_buf_copy); |
| |
| return BCM_ERR_OK; |
| } |
| #endif |
| |
| static bcmos_errno dev_ctrl_get_sw_error_table(bcmolt_devid device) |
| { |
| bcmos_sw_error_table *sw_error_table; |
| uint8_t *sram; |
| uint8_t *local; |
| uint32_t size; |
| uint32_t i; |
| |
| sram = bcm_fld_get_sw_error_table(device, &size); |
| local = bcmos_alloc(size); |
| for (i = 0; i < size; i++) |
| { |
| local[i] = sram[i]; |
| } |
| |
| sw_error_table = (bcmos_sw_error_table*)local; |
| dev_ctrl_db[device].sw_error_count = BCMOS_ENDIAN_LITTLE_TO_CPU_U32(sw_error_table->count); |
| if (dev_ctrl_db[device].sw_error_count > NUM_ELEM(dev_ctrl_db[device].sw_errors)) |
| { |
| bcmos_free(local); |
| return BCM_ERR_OVERFLOW; |
| } |
| |
| for (i = 0; i < dev_ctrl_db[device].sw_error_count; i++) |
| { |
| dev_ctrl_db[device].sw_errors[i].first_error_time_us = |
| BCMOS_ENDIAN_LITTLE_TO_CPU_U64(sw_error_table->error[i].first_error_time); |
| dev_ctrl_db[device].sw_errors[i].last_error_time_us = |
| BCMOS_ENDIAN_LITTLE_TO_CPU_U64(sw_error_table->error[i].last_error_time); |
| dev_ctrl_db[device].sw_errors[i].line_number = |
| BCMOS_ENDIAN_LITTLE_TO_CPU_U32(sw_error_table->error[i].line_number); |
| dev_ctrl_db[device].sw_errors[i].error_counter = |
| BCMOS_ENDIAN_LITTLE_TO_CPU_U32(sw_error_table->error[i].error_counter); |
| dev_ctrl_db[device].sw_errors[i].instance = |
| BCMOS_ENDIAN_LITTLE_TO_CPU_U32(sw_error_table->error[i].instance); |
| strncpy( |
| dev_ctrl_db[device].sw_errors[i].filename, |
| sw_error_table->error[i].file_name, |
| BCMOLT_SW_ERROR_MAX_FILE_NAME_LEN); |
| dev_ctrl_db[device].sw_errors[i].filename[BCMOLT_SW_ERROR_MAX_FILE_NAME_LEN-1] = 0; |
| strncpy( |
| dev_ctrl_db[device].sw_errors[i].task_name, |
| sw_error_table->error[i].task_name, |
| BCMOLT_SW_ERROR_MAX_TASK_NAME_LEN); |
| dev_ctrl_db[device].sw_errors[i].task_name[BCMOLT_SW_ERROR_MAX_TASK_NAME_LEN-1] = 0; |
| } |
| |
| bcmos_free(local); |
| |
| return BCM_ERR_OK; |
| } |
| |
| static bcmos_bool sw_error_key_valid(const bcmolt_software_error_key *key) |
| { |
| bcmolt_devid device = (bcmolt_devid)bcmos_task_current()->parm.data; |
| return key->idx < dev_ctrl_db[device].sw_error_count; |
| } |
| |
| static bcmos_errno mh_software_error_key_validate(bcmolt_msg *msg, const bcmolt_software_error_key *key) |
| { |
| return sw_error_key_valid(key) ? BCM_ERR_OK : BCM_ERR_NOENT; |
| } |
| |
| static bcmos_errno mh_software_error_cfg_get( |
| bcmolt_msg *msg, |
| const bcmolt_software_error_key *key, |
| bcmolt_software_error_cfg_data *data) |
| { |
| bcmolt_devid device = (bcmolt_devid)bcmos_task_current()->parm.data; |
| bcmolt_presence_mask props = msg->presence_mask; |
| bcmos_errno rc = BCM_ERR_OK; |
| |
| if (props == BCMOLT_PRESENCE_MASK_ALL) |
| { |
| /* Convert from sentinel "all" presence bits to set of all valid bits*/ |
| props = (1U << BCMOLT_SOFTWARE_ERROR_CFG_ID__NUM_OF) - 1; |
| } |
| |
| if (BCMOLT_CFG_PROP_IS_SET((bcmolt_software_error_cfg*)msg, software_error, entry)) |
| { |
| if (dev_ctrl_db[device].fld_info.soc_sram_base == 0) |
| { |
| return bcmolt_msg_err( |
| msg, |
| DEV_LOG_INVALID_ID, |
| BCM_ERR_STATE, |
| BCMOLT_SOFTWARE_ERROR_CFG_ID_ENTRY, |
| "Software errors can only be retreived after initial device connection"); |
| } |
| |
| data->entry = dev_ctrl_db[device].sw_errors[key->idx]; |
| props &= ~BCMOLT_PROP_MASK_GET(software_error, _cfg, entry); |
| } |
| |
| if (props != 0) |
| { |
| rc = bcmolt_msg_err(msg, |
| DEV_LOG_INVALID_ID, |
| BCM_ERR_NOT_SUPPORTED, |
| BCMOLT_ERR_FIELD_NONE, |
| "Unsupported properties: 0x%llx", (unsigned long long)props); |
| } |
| |
| return rc; |
| } |
| |
| static bcmos_errno mh_software_error_key_iterate(bcmolt_software_error_key *key) |
| { |
| key->idx++; |
| return sw_error_key_valid(key) ? BCM_ERR_OK : BCM_ERR_NO_MORE; |
| } |
| |
| static bcmos_errno mh_software_error_key_resolve_wildcards(bcmolt_software_error_key *key) |
| { |
| return BCM_ERR_OK; |
| } |
| |
| MH_DECLARE_CFG_GET_MULTI(software_error) |
| |
| static bcmos_errno dev_ctrl_sw_error_get(bcmolt_devid device, bcmolt_msg *msg) |
| { |
| bcmolt_software_error_cfg *cfg = (bcmolt_software_error_cfg*)msg; |
| bcmos_errno err = dev_ctrl_get_sw_error_table(device); |
| if (err != BCM_ERR_OK) |
| { |
| return BCM_ERR_INTERNAL; |
| } |
| |
| if (msg->type == BCMOLT_MSG_TYPE_GET) |
| { |
| bcmolt_software_error_key_id key_prop_id; |
| if (!bcmolt_software_error_key_bounds_check(&cfg->key, BCMOLT_PRESENCE_MASK_ALL, &key_prop_id)) |
| { |
| msg->err_field_idx = (uint16_t) key_prop_id; |
| return BCM_ERR_KEY_RANGE; |
| } |
| |
| err = mh_software_error_key_validate(msg, &cfg->key); |
| if (err != BCM_ERR_OK) |
| { |
| return err; |
| } |
| |
| return mh_software_error_cfg_get(msg, &cfg->key, &cfg->data); |
| } |
| else if (msg->type == BCMOLT_MSG_TYPE_GET_MULTI) |
| { |
| return mh_software_error_cfg_get_multi(cfg, msg->msg_set->filter_flags, msg->msg_set); |
| } |
| else |
| { |
| BCM_LOG(ERROR, dev_ctrl_db[device].log_id, "Unexpected type 0x%x\n", msg->type); |
| } |
| return BCM_ERR_INTERNAL; |
| } |
| #endif |
| |
| static void dev_ctrl_send_indication(bcmolt_devid device, bcmolt_msg *msg) |
| { |
| msg->subch = BCMTRMUX_CHANNEL_AUTO_PROXY; |
| msg->type = BCMOLT_MSG_TYPE_SET; |
| bcmtrmux_control_to_host(device, msg); |
| } |
| |
| static void dev_ctrl_send_ind_connection_complete(bcmolt_devid device, bcmos_bool standalone) |
| { |
| bcmolt_device_connection_complete ind = {}; |
| BCM_LOG(INFO, dev_ctrl_db[device].log_id, "Connection complete: standalone=%s\n", standalone ? "yes" : "no"); |
| BCMOLT_AUTO_INIT(&ind, device, connection_complete); |
| ind.data.standalone = standalone; |
| dev_ctrl_send_indication(device, &ind.hdr.hdr); |
| } |
| |
| static void dev_ctrl_send_ind_disconnection_complete(bcmolt_devid device) |
| { |
| bcmolt_device_disconnection_complete ind = {}; |
| BCM_LOG(INFO, dev_ctrl_db[device].log_id, "Disconnection complete\n"); |
| BCMOLT_AUTO_INIT(&ind, device, disconnection_complete); |
| dev_ctrl_send_indication(device, &ind.hdr.hdr); |
| } |
| |
| static void dev_ctrl_send_ind_connection_failure(bcmolt_devid device, bcmolt_host_connection_fail_reason reason) |
| { |
| bcmolt_device_connection_failure ind = {}; |
| BCM_LOG(INFO, dev_ctrl_db[device].log_id, "Connection failure: %s\n", bcm_str_host_connection_fail_reason(reason)); |
| BCMOLT_AUTO_INIT(&ind, device, connection_failure); |
| ind.data.reason = reason; |
| dev_ctrl_send_indication(device, &ind.hdr.hdr); |
| dev_ctrl_db[device].conn_fail_reason = reason; /* log the failure reason to the db. */ |
| } |
| |
| static void dev_ctrl_send_ind_connection_failure_from_msg(bcmolt_devid device, const bcmolt_msg *msg) |
| { |
| BUG_ON(msg->subgroup != BCMOLT_DEVICE_AUTO_ID_CONNECTION_FAILURE); |
| dev_ctrl_send_ind_connection_failure(device, ((const bcmolt_device_connection_failure *)msg)->data.reason); |
| } |
| |
| #ifndef IN_BAND |
| static void dev_ctrl_send_ind_exception_log(bcmolt_devid device, uint32_t cpuid, const char *exception_buf) |
| { |
| /* Because indication size is limited (but IND_MSG_MAX_SIZE > sizeof(bcmolt_str_2000.str), we may need to split into multiple indications. |
| * We use a static variable because our stack size is limited. */ |
| static bcmolt_str_2000 api_exception_str; |
| const char *pos = exception_buf; |
| uint32_t remaining_len = strlen(pos); |
| |
| while (remaining_len) |
| { |
| bcmolt_device_sw_exception ind = {}; |
| uint32_t bytes_copied; |
| |
| strncpy(api_exception_str.str, pos, sizeof(api_exception_str.str) - 1); |
| api_exception_str.str[sizeof(api_exception_str.str) - 1] = '\0'; |
| |
| BCMOLT_AUTO_INIT(&ind, device, sw_exception); |
| ind.data.cpu_id = cpuid; |
| ind.data.text = api_exception_str; |
| dev_ctrl_send_indication(device, &ind.hdr.hdr); |
| |
| if (sizeof(api_exception_str.str) - 1 < remaining_len) |
| { |
| bytes_copied = sizeof(api_exception_str.str) - 1; |
| } |
| else |
| { |
| bytes_copied = remaining_len; |
| } |
| remaining_len -= bytes_copied; |
| pos += bytes_copied; |
| } |
| } |
| #endif |
| |
| static void dev_ctrl_send_config_set_msg( |
| bcmolt_devid device, |
| bcmolt_presence_mask mask, |
| uint16_t corr_tag, |
| const bcmolt_device_cfg_data *data) |
| { |
| bcmolt_device_key key = {}; |
| bcmolt_device_cfg cfg; |
| bcmos_errno rc; |
| |
| BCMOLT_CFG_INIT(&cfg, device, key); |
| cfg.data = *data; |
| cfg.hdr.hdr.presence_mask = mask; |
| |
| cfg.hdr.hdr.corr_tag = corr_tag; |
| cfg.hdr.hdr.type = BCMOLT_MSG_TYPE_SET; |
| |
| rc = bcmtrmux_control_to_line(device, &cfg.hdr.hdr); |
| if (rc != BCM_ERR_OK) |
| { |
| BCM_LOG(ERROR, dev_ctrl_db[device].log_id, |
| "bcmtrmux_control_to_line of device config msg returned error %s (%d)\n", |
| bcmos_strerror(rc), |
| rc); |
| } |
| } |
| |
| static void dev_ctrl_send_config_get_msg(bcmolt_devid device, bcmolt_presence_mask mask, uint16_t corr_tag) |
| { |
| bcmolt_device_key key = {}; |
| bcmolt_device_cfg cfg; |
| bcmos_errno rc; |
| |
| BCMOLT_CFG_INIT(&cfg, device, key); |
| cfg.hdr.hdr.presence_mask = mask; |
| |
| cfg.hdr.hdr.corr_tag = corr_tag; |
| cfg.hdr.hdr.type = BCMOLT_MSG_TYPE_GET; |
| |
| rc = bcmtrmux_control_to_line(device, &cfg.hdr.hdr); |
| if (rc != BCM_ERR_OK) |
| { |
| BCM_LOG(ERROR, dev_ctrl_db[device].log_id, |
| "bcmtrmux_control_to_line of device config msg returned error %s (%d)\n", |
| bcmos_strerror(rc), |
| rc); |
| } |
| } |
| |
| static void dev_ctrl_send_device_disconnect_msg(bcmolt_devid device, uint16_t corr_tag) |
| { |
| bcmolt_device_key key = {}; |
| bcmolt_device_disconnect oper; |
| bcmos_errno rc; |
| |
| BCMOLT_OPER_INIT(&oper, device, disconnect, key); |
| |
| oper.hdr.hdr.corr_tag = corr_tag; |
| oper.hdr.hdr.type = BCMOLT_MSG_TYPE_SET; |
| rc = bcmtrmux_control_to_line(device, &oper.hdr.hdr); |
| if (rc != BCM_ERR_OK) |
| { |
| BCM_LOG(ERROR, dev_ctrl_db[device].log_id, |
| "bcmtrmux_control_to_line of device disconnect operation returned error %s (%d)\n", |
| bcmos_strerror(rc), |
| rc); |
| } |
| } |
| |
| /* Set all device object properties to default values. */ |
| static void dev_ctrl_clear_device_cfg(bcmolt_devid device) |
| { |
| dev_ctrl_db[device].device_params_present = 0; |
| |
| /* Pay attention: We use the interval and tolerance from dev_ctrl_db[device].ka_info and not from |
| * dev_ctrl_db[device].device_params. These parameters exist in dev_ctrl_db[device].device_params only because it is |
| * the data model representation. */ |
| bcmolt_device_cfg_data_set_default(&dev_ctrl_db[device].device_params, BCMOLT_PRESENCE_MASK_ALL); |
| dev_ctrl_db[device].ka_info.ka_interval = |
| dev_ctrl_db[device].device_params.keepalive_interval * BCMOS_MICROSECONDS_IN_SECONDS; |
| dev_ctrl_db[device].ka_info.ka_tolerance = dev_ctrl_db[device].device_params.keepalive_tolerance; |
| } |
| |
| static void dev_ctrl_init_connection_state(bcmolt_devid device) |
| { |
| dev_ctrl_db[device].connection_info.config_send_counter = CONFIG_SEND_MAX_NUMBER; |
| dev_ctrl_db[device].connection_info.config_interval = DEVICE_CONTROL_CONFIG_TIME_US; |
| } |
| |
| static bcmos_errno dev_ctrl_validate_cfg_set(bcmolt_devid device, bcmolt_msg *msg) |
| { |
| #ifndef IN_BAND |
| bcmos_errno rc; |
| #endif |
| bcmolt_device_cfg *cfg = (bcmolt_device_cfg *)msg; |
| bcmolt_device_state state = dev_ctrl_db[device].device_params.state; |
| bcmolt_device_cfg_id failed_prop = BCMOLT_ERR_FIELD_NONE; |
| |
| if (!bcmolt_device_cfg_data_bounds_check(&cfg->data, msg->presence_mask, &failed_prop)) |
| { |
| msg->err_field_idx = (uint16_t)failed_prop; |
| return BCM_ERR_RANGE; |
| } |
| |
| #ifndef IN_BAND |
| if (BCMOLT_CFG_PROP_IS_SET(cfg, device, system_mode)) |
| { |
| if (state != BCMOLT_DEVICE_STATE_DISCONNECTED) |
| { |
| return bcmolt_msg_err( |
| msg, |
| DEV_LOG_INVALID_ID, |
| BCM_ERR_STATE, |
| BCMOLT_DEVICE_CFG_ID_SYSTEM_MODE, |
| "System mode can only be changed while disconnected"); |
| } |
| |
| rc = dev_ctrl_params.system_mode_validate_cb(device, cfg->data.system_mode); |
| if (rc != BCM_ERR_OK) |
| { |
| return bcmolt_msg_err( |
| msg, |
| DEV_LOG_INVALID_ID, |
| rc, |
| BCMOLT_DEVICE_CFG_ID_SYSTEM_MODE, |
| "Given system mode is not supported for this board"); |
| } |
| } |
| #endif |
| |
| if (BCMOLT_CFG_PROP_IS_SET(cfg, device, nni_speed) && state != BCMOLT_DEVICE_STATE_DISCONNECTED) |
| { |
| return bcmolt_msg_err( |
| msg, |
| DEV_LOG_INVALID_ID, |
| BCM_ERR_STATE, |
| BCMOLT_DEVICE_CFG_ID_NNI_SPEED, |
| "NNI speed can only be changed while disconnected"); |
| } |
| |
| if (BCMOLT_CFG_PROP_IS_SET(cfg, device, debug) && state != BCMOLT_DEVICE_STATE_DISCONNECTED) |
| { |
| return bcmolt_msg_err( |
| msg, |
| DEV_LOG_INVALID_ID, |
| BCM_ERR_STATE, |
| BCMOLT_DEVICE_CFG_ID_DEBUG, |
| "Debug parameters can only be changed while disconnected"); |
| } |
| |
| if (BCMOLT_CFG_PROP_IS_SET(cfg, device, firmware_sw_version) || |
| BCMOLT_CFG_PROP_IS_SET(cfg, device, host_sw_version) || |
| BCMOLT_CFG_PROP_IS_SET(cfg, device, chip_revision)|| |
| BCMOLT_CFG_PROP_IS_SET(cfg, device, state)|| |
| BCMOLT_CFG_PROP_IS_SET(cfg, device, chip_temperature)|| |
| BCMOLT_CFG_PROP_IS_SET(cfg, device, chip_voltage)) |
| { |
| return bcmolt_msg_err( |
| msg, |
| DEV_LOG_INVALID_ID, |
| BCM_ERR_STATE, |
| BCMOLT_ERR_FIELD_NONE, |
| "Cannot set read only fields"); |
| } |
| |
| return BCM_ERR_OK; |
| } |
| |
| static bcmos_errno dev_ctrl_validate_cfg_get(bcmolt_devid device, bcmolt_msg *msg) |
| { |
| bcmolt_device_state state = dev_ctrl_db[device].device_params.state; |
| bcmolt_device_cfg *cfg = (bcmolt_device_cfg *)msg; |
| |
| if (BCMOLT_CFG_PROP_IS_SET(cfg, device, firmware_sw_version) || |
| BCMOLT_CFG_PROP_IS_SET(cfg, device, chip_revision) || |
| BCMOLT_CFG_PROP_IS_SET(cfg, device, chip_temperature)|| |
| BCMOLT_CFG_PROP_IS_SET(cfg, device, chip_voltage) || |
| BCMOLT_CFG_PROP_IS_SET(cfg, device, epon_tod_string)) |
| { |
| if (!dev_ctrl_is_connected(state)) |
| { |
| return bcmolt_msg_err( |
| msg, |
| DEV_LOG_INVALID_ID, |
| BCM_ERR_STATE, |
| BCMOLT_ERR_FIELD_NONE, |
| "Cannot retrieve parameter while disconnected."); |
| } |
| } |
| if (BCMOLT_CFG_PROP_IS_SET(cfg, device, protection_switching_ext_irq)) |
| { |
| if (!dev_ctrl_is_connected(state) && |
| (dev_ctrl_db[device].device_params_present & |
| BCMOLT_PROP_MASK_GET(device, _cfg, protection_switching_ext_irq)) == 0) |
| { |
| return bcmolt_msg_err( |
| msg, |
| DEV_LOG_INVALID_ID, |
| BCM_ERR_STATE, |
| BCMOLT_DEVICE_CFG_ID_PROTECTION_SWITCHING_EXT_IRQ, |
| "Cannot retrieve protection_switching_ext_irq while disconnected and not having a cache."); |
| } |
| } |
| if (BCMOLT_CFG_PROP_IS_SET(cfg, device, epon_clock_transport_sample_delay)) |
| { |
| if (!dev_ctrl_is_connected(state) && |
| (dev_ctrl_db[device].device_params_present & |
| BCMOLT_PROP_MASK_GET(device, _cfg, epon_clock_transport_sample_delay)) == 0) |
| { |
| return bcmolt_msg_err( |
| msg, |
| DEV_LOG_INVALID_ID, |
| BCM_ERR_STATE, |
| BCMOLT_DEVICE_CFG_ID_EPON_CLOCK_TRANSPORT_SAMPLE_DELAY, |
| "Cannot retrieve epon_clock_transport_sample_delay while disconnected and not having a cache."); |
| } |
| } |
| if (BCMOLT_CFG_PROP_IS_SET(cfg, device, indication_shaping)) |
| { |
| if (!dev_ctrl_is_connected(state) && |
| (dev_ctrl_db[device].device_params_present & BCMOLT_PROP_MASK_GET(device, _cfg, indication_shaping)) == 0) |
| { |
| return bcmolt_msg_err( |
| msg, |
| DEV_LOG_INVALID_ID, |
| BCM_ERR_STATE, |
| BCMOLT_DEVICE_CFG_ID_INDICATION_SHAPING, |
| "Cannot retrieve indication_shaping while disconnected and not having a cache."); |
| } |
| } |
| if (BCMOLT_CFG_PROP_IS_SET(cfg, device, gpon_xgpon_tod_enable)) |
| { |
| if (!dev_ctrl_is_connected(state) && |
| (dev_ctrl_db[device].device_params_present & |
| BCMOLT_PROP_MASK_GET(device, _cfg, gpon_xgpon_tod_enable)) == 0) |
| { |
| return bcmolt_msg_err( |
| msg, |
| DEV_LOG_INVALID_ID, |
| BCM_ERR_STATE, |
| BCMOLT_DEVICE_CFG_ID_GPON_XGPON_TOD_ENABLE, |
| "Cannot retrieve gpon_xgpon_tod_enable while disconnected and not having a cache."); |
| } |
| } |
| if (BCMOLT_CFG_PROP_IS_SET(cfg, device, gpon_xgpon_tod_gpio_pin)) |
| { |
| if (!dev_ctrl_is_connected(state) && |
| (dev_ctrl_db[device].device_params_present & |
| BCMOLT_PROP_MASK_GET(device, _cfg, gpon_xgpon_tod_gpio_pin)) == 0) |
| { |
| return bcmolt_msg_err( |
| msg, |
| DEV_LOG_INVALID_ID, |
| BCM_ERR_STATE, |
| BCMOLT_DEVICE_CFG_ID_GPON_XGPON_TOD_GPIO_PIN, |
| "Cannot retrieve gpon_xgpon_tod_gpio_pin while disconnected and not having a cache."); |
| } |
| } |
| if (BCMOLT_CFG_PROP_IS_SET(cfg, device, gpon_xgpon_tod_connected_internally)) |
| { |
| if (!dev_ctrl_is_connected(state) && |
| (dev_ctrl_db[device].device_params_present & |
| BCMOLT_PROP_MASK_GET(device, _cfg, gpon_xgpon_tod_connected_internally)) == 0) |
| { |
| return bcmolt_msg_err( |
| msg, |
| DEV_LOG_INVALID_ID, |
| BCM_ERR_STATE, |
| BCMOLT_DEVICE_CFG_ID_GPON_XGPON_TOD_CONNECTED_INTERNALLY, |
| "Cannot retrieve gpon_xgpon_tod_connected_internally while disconnected and not having a cache."); |
| } |
| } |
| if (BCMOLT_CFG_PROP_IS_SET(cfg, device, epon_8021_as_tod_format)) |
| { |
| if (!dev_ctrl_is_connected(state) && |
| (dev_ctrl_db[device].device_params_present & |
| BCMOLT_PROP_MASK_GET(device, _cfg, epon_8021_as_tod_format)) == 0) |
| { |
| return bcmolt_msg_err( |
| msg, |
| DEV_LOG_INVALID_ID, |
| BCM_ERR_STATE, |
| BCMOLT_DEVICE_CFG_ID_EPON_8021_AS_TOD_FORMAT, |
| "Cannot retrieve epon_8021_as_tod_format while disconnected and not having a cache."); |
| } |
| } |
| if (BCMOLT_CFG_PROP_IS_SET(cfg, device, epon_shaper_mode)) |
| { |
| if (!dev_ctrl_is_connected(state) && |
| (dev_ctrl_db[device].device_params_present & |
| BCMOLT_PROP_MASK_GET(device, _cfg, epon_shaper_mode)) == 0) |
| { |
| return bcmolt_msg_err( |
| msg, |
| DEV_LOG_INVALID_ID, |
| BCM_ERR_STATE, |
| BCMOLT_DEVICE_CFG_ID_EPON_SHAPER_MODE, |
| "Cannot retrieve epon_shaper_mode while disconnected and not having a cache."); |
| } |
| } |
| if (BCMOLT_CFG_PROP_IS_SET(cfg, device, embedded_image_list)) |
| { |
| if (!dev_ctrl_is_connected(state) && |
| (dev_ctrl_db[device].device_params_present & |
| BCMOLT_PROP_MASK_GET(device, _cfg, embedded_image_list)) == 0) |
| { |
| return bcmolt_msg_err( |
| msg, |
| DEV_LOG_INVALID_ID, |
| BCM_ERR_STATE, |
| BCMOLT_DEVICE_CFG_ID_EMBEDDED_IMAGE_LIST, |
| "Cannot retrieve embedded_image_list while disconnected and not having a cache."); |
| } |
| } |
| if (BCMOLT_CFG_PROP_IS_SET(cfg, device, gpon_xgpon_tod_string_length)) |
| { |
| if (!dev_ctrl_is_connected(state) && |
| (dev_ctrl_db[device].device_params_present & |
| BCMOLT_PROP_MASK_GET(device, _cfg, gpon_xgpon_tod_string_length)) == 0) |
| { |
| return bcmolt_msg_err( |
| msg, |
| DEV_LOG_INVALID_ID, |
| BCM_ERR_STATE, |
| BCMOLT_DEVICE_CFG_ID_GPON_XGPON_TOD_STRING_LENGTH, |
| "Cannot retrieve gpon_xgpon_tod_string_length while disconnected and not having a cache."); |
| } |
| } |
| if (BCMOLT_CFG_PROP_IS_SET(cfg, device, tod_uart_baudrate)) |
| { |
| if (!dev_ctrl_is_connected(state) && |
| (dev_ctrl_db[device].device_params_present & |
| BCMOLT_PROP_MASK_GET(device, _cfg, tod_uart_baudrate)) == 0) |
| { |
| return bcmolt_msg_err( |
| msg, |
| DEV_LOG_INVALID_ID, |
| BCM_ERR_STATE, |
| BCMOLT_DEVICE_CFG_ID_TOD_UART_BAUDRATE, |
| "Cannot retrieve tod_uart_baudrate while disconnected and not having a cache."); |
| } |
| } |
| return BCM_ERR_OK; |
| } |
| |
| static bcmos_errno dev_ctrl_validate_cfg_clear(bcmolt_devid device, bcmolt_msg *msg) |
| { |
| if (dev_ctrl_db[device].device_params.state != BCMOLT_DEVICE_STATE_DISCONNECTED) |
| { |
| return bcmolt_msg_err( |
| msg, |
| DEV_LOG_INVALID_ID, |
| BCM_ERR_STATE, |
| BCMOLT_ERR_FIELD_NONE, |
| "Device cfg_clear is only allowed while disconnected"); |
| } |
| return BCM_ERR_OK; |
| } |
| |
| static bcmos_errno bcm_dev_ctrl_validate_oper_connect(bcmolt_devid device, bcmolt_msg *msg) |
| { |
| bcmolt_device_state state = dev_ctrl_db[device].device_params.state; |
| |
| if (dev_ctrl_is_connected(state)) |
| { |
| return bcmolt_msg_err(msg, DEV_LOG_INVALID_ID, BCM_ERR_ALREADY, BCMOLT_ERR_FIELD_NONE, "already connected"); |
| } |
| |
| /* If not all mandatory properties are valid return "mandatory parameter is missing" */ |
| if (dev_ctrl_db[device].device_params.system_mode >= BCMOLT_SYSTEM_MODE__NUM_OF) |
| { |
| BCM_LOG(INFO, dev_ctrl_db[device].log_id, "Cannot connect: system mode not included\n"); |
| return bcmolt_msg_err( |
| msg, |
| DEV_LOG_INVALID_ID, |
| BCM_ERR_MANDATORY_PARM_IS_MISSING, |
| BCMOLT_DEVICE_CFG_ID_SYSTEM_MODE, |
| "System mode must be set before connecting"); |
| } |
| #ifdef IN_BAND |
| /* For In Band Management IP Address and UDP port must be set prior to connecting*/ |
| /* If not all mandatory properties are valid return "mandatory parameter is missing" */ |
| if (dev_ctrl_db[device].device_params.device_ip_address.u32 == 0) |
| { |
| BCM_LOG(INFO, dev_ctrl_db[device].log_id, "Cannot connect: must set IP Address port\n"); |
| return bcmolt_msg_err( |
| msg, |
| DEV_LOG_INVALID_ID, |
| BCM_ERR_MANDATORY_PARM_IS_MISSING, |
| BCMOLT_DEVICE_CFG_ID_DEVICE_IP_ADDRESS, |
| "Device IP Address must be set before connecting"); |
| } |
| if (dev_ctrl_db[device].device_params.device_udp_port == 0) |
| { |
| BCM_LOG(INFO, dev_ctrl_db[device].log_id, "Cannot connect: must set IP Address UDP port\n"); |
| return bcmolt_msg_err( |
| msg, |
| DEV_LOG_INVALID_ID, |
| BCM_ERR_MANDATORY_PARM_IS_MISSING, |
| BCMOLT_DEVICE_CFG_ID_DEVICE_UDP_PORT, |
| "UDP port must be set before connecting"); |
| } |
| #endif |
| |
| return BCM_ERR_OK; |
| } |
| |
| static bcmos_errno bcm_dev_ctrl_validate_oper_disconnect(bcmolt_devid device, bcmolt_msg *msg) |
| { |
| bcmolt_device_state state = dev_ctrl_db[device].device_params.state; |
| if (state == BCMOLT_DEVICE_STATE_DISCONNECTED) |
| { |
| return bcmolt_msg_err(msg, DEV_LOG_INVALID_ID, BCM_ERR_ALREADY, BCMOLT_ERR_FIELD_NONE, "already disconnected"); |
| } |
| return BCM_ERR_OK; |
| } |
| |
| static bcmos_errno bcm_dev_ctrl_validate_oper_run_ddr_test(bcmolt_devid device, bcmolt_msg *msg) |
| { |
| bcmolt_device_run_ddr_test *ddr_test = (bcmolt_device_run_ddr_test*)msg; |
| bcmos_bool is_standalone; |
| bcmos_errno rc; |
| |
| if (BCMOLT_DEVICE_STATE_DISCONNECTED != dev_ctrl_db[device].device_params.state) |
| { |
| return bcmolt_msg_err( |
| msg, |
| DEV_LOG_INVALID_ID, |
| BCM_ERR_STATE, |
| BCMOLT_ERR_FIELD_NONE, |
| "DDR test can only be run when disconnected"); |
| } |
| |
| if (dev_ctrl_db[device].is_host_reset_pending) |
| { |
| return bcmolt_msg_err( |
| msg, |
| DEV_LOG_INVALID_ID, |
| BCM_ERR_STATE, |
| BCMOLT_ERR_FIELD_NONE, |
| "Cannot run DDR test while host reset is pending"); |
| } |
| |
| /* Check if the chip is running in standalone mode */ |
| rc = dev_ctrl_params.device_is_running_cb(device, &is_standalone); |
| if (rc != BCM_ERR_OK) |
| { |
| BCM_LOG(ERROR, dev_ctrl_db[device].log_id, "device_is_running_cb returned error: %s (%d)\n", bcmos_strerror(rc), rc); |
| return bcmolt_msg_err( |
| msg, |
| DEV_LOG_INVALID_ID, |
| rc, |
| BCMOLT_ERR_FIELD_NONE, |
| "Failed to retrieve device status"); |
| } |
| if (is_standalone) |
| { |
| return bcmolt_msg_err( |
| msg, |
| DEV_LOG_INVALID_ID, |
| BCM_ERR_STATE, |
| BCMOLT_ERR_FIELD_NONE, |
| "Cannot run DDR test while device is running"); |
| } |
| |
| if (!ddr_test->data.cpu && !ddr_test->data.ras_0 && !ddr_test->data.ras_1) |
| { |
| return bcmolt_msg_err( |
| msg, |
| DEV_LOG_INVALID_ID, |
| BCM_ERR_STATE, |
| BCMOLT_ERR_FIELD_NONE, |
| "Must request at least one DDR Test"); |
| } |
| |
| return BCM_ERR_OK; |
| } |
| |
| static bcmos_errno dev_ctrl_validate_operation(bcmolt_devid device, bcmolt_msg *msg) |
| { |
| bcmolt_device_state state = dev_ctrl_db[device].device_params.state; |
| if (state == BCMOLT_DEVICE_STATE_CONNECTING) |
| { |
| return bcmolt_msg_err( |
| msg, |
| DEV_LOG_INVALID_ID, |
| BCM_ERR_STATE, |
| BCMOLT_ERR_FIELD_NONE, |
| "Device operations are not allowed while connecting"); |
| } |
| if (state == BCMOLT_DEVICE_STATE_TESTING_DDR) |
| { |
| return bcmolt_msg_err( |
| msg, |
| DEV_LOG_INVALID_ID, |
| BCM_ERR_STATE, |
| BCMOLT_ERR_FIELD_NONE, |
| "Device operations are not allowed while DDR test is in progress"); |
| } |
| |
| switch (msg->subgroup) |
| { |
| case BCMOLT_DEVICE_OPER_ID_CONNECT: |
| return bcm_dev_ctrl_validate_oper_connect(device, msg); |
| case BCMOLT_DEVICE_OPER_ID_DISCONNECT: |
| return bcm_dev_ctrl_validate_oper_disconnect(device, msg); |
| case BCMOLT_DEVICE_OPER_ID_RUN_DDR_TEST: |
| return bcm_dev_ctrl_validate_oper_run_ddr_test(device, msg); |
| default: |
| return BCM_ERR_OK; |
| } |
| } |
| |
| static bcmos_errno dev_ctrl_validate_msg(bcmolt_devid device, bcmolt_msg *msg) |
| { |
| if (dev_ctrl_db[device].device_params.state == BCMOLT_DEVICE_STATE_WAITING_FOR_DEVICE) |
| { |
| return bcmolt_msg_err( |
| msg, |
| DEV_LOG_INVALID_ID, |
| BCM_ERR_STATE, |
| BCMOLT_ERR_FIELD_NONE, |
| "The device object is busy processing another API request"); |
| } |
| |
| if (msg->group == BCMOLT_MGT_GROUP_CFG) |
| { |
| switch (msg->type) |
| { |
| case BCMOLT_MSG_TYPE_CLEAR: |
| return dev_ctrl_validate_cfg_clear(device, msg); |
| case BCMOLT_MSG_TYPE_SET: |
| return dev_ctrl_validate_cfg_set(device, msg); |
| case BCMOLT_MSG_TYPE_GET: |
| return dev_ctrl_validate_cfg_get(device, msg); |
| default: |
| break; |
| } |
| } |
| |
| if (msg->group == BCMOLT_MGT_GROUP_OPER && msg->type == BCMOLT_MSG_TYPE_SET) |
| { |
| return dev_ctrl_validate_operation(device, msg); |
| } |
| |
| return bcmolt_msg_err( |
| msg, |
| DEV_LOG_INVALID_ID, |
| BCM_ERR_INTERNAL, |
| BCMOLT_ERR_FIELD_NONE, |
| "Invalid message for device control"); |
| } |
| |
| static bcmolt_presence_mask dev_ctrl_fill_from_local(bcmolt_devid device) |
| { |
| bcmolt_device_cfg *cfg = (bcmolt_device_cfg *)dev_ctrl_db[device].last_message; |
| bcmolt_presence_mask mask = dev_ctrl_db[device].last_message->presence_mask; |
| |
| if (BCMOLT_CFG_PROP_IS_SET(cfg, device, system_mode)) |
| { |
| cfg->data.system_mode = dev_ctrl_db[device].device_params.system_mode; |
| mask &= ~BCMOLT_PROP_MASK_GET(device, _cfg, system_mode); |
| } |
| |
| if (BCMOLT_CFG_PROP_IS_SET(cfg, device, keepalive_interval)) |
| { |
| cfg->data.keepalive_interval = dev_ctrl_db[device].ka_info.ka_interval / BCMOS_MICROSECONDS_IN_SECONDS; |
| mask &= ~BCMOLT_PROP_MASK_GET(device, _cfg, keepalive_interval); |
| } |
| |
| if (BCMOLT_CFG_PROP_IS_SET(cfg, device, keepalive_tolerance)) |
| { |
| cfg->data.keepalive_tolerance = dev_ctrl_db[device].ka_info.ka_tolerance; |
| mask &= ~BCMOLT_PROP_MASK_GET(device, _cfg, keepalive_tolerance); |
| } |
| |
| if (BCMOLT_CFG_PROP_IS_SET(cfg, device, host_sw_version)) |
| { |
| cfg->data.host_sw_version.major = BCMOLT_HOST_MAJOR_VER; |
| cfg->data.host_sw_version.minor = BCMOLT_HOST_MINOR_VER; |
| cfg->data.host_sw_version.revision = BCMOLT_HOST_REVISION_VER; |
| cfg->data.host_sw_version.model = BCMOLT_MODEL_REVISION; |
| snprintf( |
| cfg->data.host_sw_version.build_time, |
| sizeof(cfg->data.host_sw_version.build_time), |
| "%s %s", |
| __DATE__, |
| __TIME__); |
| mask &= ~BCMOLT_PROP_MASK_GET(device, _cfg, host_sw_version); |
| } |
| |
| if (BCMOLT_CFG_PROP_IS_SET(cfg, device, state)) |
| { |
| cfg->data.state = dev_ctrl_db[device].device_params.state; |
| mask &= ~BCMOLT_PROP_MASK_GET(device, _cfg, state); |
| } |
| |
| if (BCMOLT_CFG_PROP_IS_SET(cfg, device, debug)) |
| { |
| cfg->data.debug = dev_ctrl_db[device].device_params.debug; |
| mask &= ~BCMOLT_PROP_MASK_GET(device, _cfg, debug); |
| } |
| |
| if (BCMOLT_CFG_PROP_IS_SET(cfg, device, nni_speed)) |
| { |
| cfg->data.nni_speed = dev_ctrl_db[device].device_params.nni_speed; |
| mask &= ~BCMOLT_PROP_MASK_GET(device, _cfg, nni_speed); |
| } |
| |
| if (BCMOLT_CFG_PROP_IS_SET(cfg, device, xgpon_num_of_onus)) |
| { |
| cfg->data.xgpon_num_of_onus = dev_ctrl_db[device].device_params.xgpon_num_of_onus; |
| mask &= ~BCMOLT_PROP_MASK_GET(device, _cfg, xgpon_num_of_onus); |
| } |
| |
| return mask; |
| } |
| |
| static void dev_ctrl_update_local_configuration(bcmolt_devid device, const bcmolt_msg *msg) |
| { |
| const bcmolt_device_cfg *cfg = (const bcmolt_device_cfg *)msg; |
| |
| dev_ctrl_db[device].device_params_present |= msg->presence_mask; |
| |
| if (BCMOLT_CFG_PROP_IS_SET(cfg, device, system_mode)) |
| { |
| dev_ctrl_db[device].device_params.system_mode = cfg->data.system_mode; |
| } |
| if (BCMOLT_CFG_PROP_IS_SET(cfg, device, debug)) |
| { |
| dev_ctrl_db[device].device_params.debug = cfg->data.debug; |
| } |
| if (BCMOLT_CFG_PROP_IS_SET(cfg, device, nni_speed)) |
| { |
| dev_ctrl_db[device].device_params.nni_speed = cfg->data.nni_speed; |
| } |
| if (BCMOLT_CFG_PROP_IS_SET(cfg, device, keepalive_interval)) |
| { |
| dev_ctrl_db[device].ka_info.ka_interval = (cfg->data.keepalive_interval * BCMOS_MICROSECONDS_IN_SECONDS); |
| dev_ctrl_db[device].device_params.keepalive_interval = cfg->data.keepalive_interval; |
| } |
| if (BCMOLT_CFG_PROP_IS_SET(cfg, device, keepalive_tolerance)) |
| { |
| dev_ctrl_db[device].ka_info.ka_tolerance = cfg->data.keepalive_tolerance; |
| dev_ctrl_db[device].device_params.keepalive_tolerance = cfg->data.keepalive_tolerance; |
| } |
| if (BCMOLT_CFG_PROP_IS_SET(cfg, device, protection_switching_ext_irq)) |
| { |
| dev_ctrl_db[device].device_params.protection_switching_ext_irq = cfg->data.protection_switching_ext_irq; |
| } |
| if (BCMOLT_CFG_PROP_IS_SET(cfg, device, epon_clock_transport_sample_delay)) |
| { |
| dev_ctrl_db[device].device_params.epon_clock_transport_sample_delay = |
| cfg->data.epon_clock_transport_sample_delay; |
| } |
| if (BCMOLT_CFG_PROP_IS_SET(cfg, device, indication_shaping)) |
| { |
| dev_ctrl_db[device].device_params.indication_shaping = cfg->data.indication_shaping; |
| } |
| if (BCMOLT_CFG_PROP_IS_SET(cfg, device, gpon_xgpon_tod_enable)) |
| { |
| dev_ctrl_db[device].device_params.gpon_xgpon_tod_enable = cfg->data.gpon_xgpon_tod_enable; |
| } |
| if (BCMOLT_CFG_PROP_IS_SET(cfg, device, gpon_xgpon_tod_gpio_pin)) |
| { |
| dev_ctrl_db[device].device_params.gpon_xgpon_tod_gpio_pin = cfg->data.gpon_xgpon_tod_gpio_pin; |
| } |
| if (BCMOLT_CFG_PROP_IS_SET(cfg, device, gpon_xgpon_tod_connected_internally)) |
| { |
| dev_ctrl_db[device].device_params.gpon_xgpon_tod_connected_internally = cfg->data.gpon_xgpon_tod_connected_internally; |
| } |
| if (BCMOLT_CFG_PROP_IS_SET(cfg, device, epon_shaper_mode)) |
| { |
| dev_ctrl_db[device].device_params.epon_shaper_mode = cfg->data.epon_shaper_mode; |
| } |
| if (BCMOLT_CFG_PROP_IS_SET(cfg, device, embedded_image_list)) |
| { |
| dev_ctrl_db[device].device_params.embedded_image_list = cfg->data.embedded_image_list; |
| } |
| if (BCMOLT_CFG_PROP_IS_SET(cfg, device, xgpon_num_of_onus)) |
| { |
| dev_ctrl_db[device].device_params.xgpon_num_of_onus = cfg->data.xgpon_num_of_onus; |
| } |
| if (BCMOLT_CFG_PROP_IS_SET(cfg, device, device_ip_address)) |
| { |
| dev_ctrl_db[device].device_params.device_ip_address = cfg->data.device_ip_address; |
| } |
| if (BCMOLT_CFG_PROP_IS_SET(cfg, device, device_udp_port)) |
| { |
| dev_ctrl_db[device].device_params.device_udp_port = cfg->data.device_udp_port; |
| } |
| if (BCMOLT_CFG_PROP_IS_SET(cfg, device, gpon_xgpon_tod_string_length)) |
| { |
| dev_ctrl_db[device].device_params.gpon_xgpon_tod_string_length = cfg->data.gpon_xgpon_tod_string_length; |
| } |
| if (BCMOLT_CFG_PROP_IS_SET(cfg, device, tod_uart_baudrate)) |
| { |
| dev_ctrl_db[device].device_params.tod_uart_baudrate = cfg->data.tod_uart_baudrate; |
| } |
| } |
| |
| static bcmos_errno dev_ctrl_validate_fw_version(bcmolt_devid device, const bcmolt_firmware_sw_version *version) |
| { |
| BCM_LOG(INFO, dev_ctrl_db[device].log_id, |
| "Firmware SW version %u.%u.%u (Object model revision %u, Build time: %s)\n", |
| version->major, |
| version->minor, |
| version->revision, |
| version->model, |
| version->build_time); |
| |
| BCM_LOG(INFO, dev_ctrl_db[device].log_id, |
| "Host SW version %u.%u.%u (Object model revision %u, Build time: %s %s)\n", |
| BCMOLT_HOST_MAJOR_VER, |
| BCMOLT_HOST_MINOR_VER, |
| BCMOLT_HOST_REVISION_VER, |
| BCMOLT_MODEL_REVISION, |
| __DATE__, |
| __TIME__); |
| |
| /* Version mismatch - note that only the Host/Firmware revisions can be different between the host and the firmware */ |
| if (version->major != BCMOLT_HOST_MAJOR_VER || |
| version->minor != BCMOLT_HOST_MINOR_VER || |
| version->model != BCMOLT_MODEL_REVISION) |
| { |
| BCM_LOG(INFO, dev_ctrl_db[device].log_id, |
| "SW Versions Mismatch: Host SW version is %u.%u.%u with object model revision %d, while Firmware SW version is %u.%u.%u with object model revision %u\n", |
| BCMOLT_HOST_MAJOR_VER, |
| BCMOLT_HOST_MINOR_VER, |
| BCMOLT_HOST_REVISION_VER, |
| BCMOLT_MODEL_REVISION, |
| version->major, |
| version->minor, |
| version->revision, |
| version->model); |
| return BCM_ERR_STATE; |
| } |
| return BCM_ERR_OK; |
| } |
| |
| #ifndef IN_BAND |
| static bcmos_errno dev_ctrl_register_fld(bcmolt_devid device) |
| { |
| bcmos_errno rc; |
| bcm_ll_dev_info ll_info; |
| |
| if (dev_ctrl_db[device].fld_info.soc_sram_base > 0) |
| { |
| /* we have already queried the PCIe / registered with the FLD */ |
| return BCM_ERR_OK; |
| } |
| |
| rc = bcm_ll_pcie_query(device, &ll_info); |
| BCM_DEV_CTRL_RETURN_ON_ERROR(device, rc, "bcm_ll_pcie_query\n"); |
| dev_ctrl_db[device].fld_info.soc_ddr_length = ll_info.soc_ddr_length; |
| dev_ctrl_db[device].fld_info.soc_sram_base = ll_info.soc_sram_base; |
| dev_ctrl_db[device].fld_info.soc_ddr_base = ll_info.soc_ddr_base; |
| dev_ctrl_db[device].fld_info.soc_regs_base = ll_info.soc_regs_base; |
| |
| BCM_LOG(INFO, dev_ctrl_db[device].log_id, |
| "FLD_INFO: ddr_length=0x%x sram_base=%p soc_ddr_base=%p soc_regs_base=%p\n", |
| dev_ctrl_db[device].fld_info.soc_ddr_length, |
| (void *)dev_ctrl_db[device].fld_info.soc_sram_base, |
| (void *)dev_ctrl_db[device].fld_info.soc_ddr_base, |
| (void *)dev_ctrl_db[device].fld_info.soc_regs_base); |
| |
| rc = bcm_fld_register(device, &dev_ctrl_db[device].fld_info); |
| BCM_DEV_CTRL_RETURN_ON_ERROR(device, rc, "bcm_fld_register\n"); |
| |
| return BCM_ERR_OK; |
| } |
| |
| static bcmos_errno dev_ctrl_handle_file(bcmolt_devid device, bcmolt_device_image_type image_type) |
| { |
| int32_t read_len; |
| BCM_FLD_HOST_DEBUG_VALUES host_debug_value; |
| bcmos_errno rc; |
| uint32_t offset_r = 0, offset_w = 0; |
| volatile unsigned long write_complete[2]; |
| const char *image_name; |
| |
| BUG_ON((image_type != BCMOLT_DEVICE_IMAGE_TYPE_BOOTLOADER) && (image_type != BCMOLT_DEVICE_IMAGE_TYPE_APPLICATION)); |
| |
| image_name = image_type == BCMOLT_DEVICE_IMAGE_TYPE_APPLICATION ? "application" : "boot loader"; |
| |
| BCM_LOG(INFO, dev_ctrl_db[device].log_id, "Loading %s\n", image_name); |
| |
| host_debug_value = image_type == BCMOLT_DEVICE_IMAGE_TYPE_APPLICATION ? BCM_FLD_HOST_WRITE_DDR : BCM_FLD_HOST_WRITE_SRAM; |
| rc = bcm_fld_set_host_debug_status(device, host_debug_value); |
| BCM_DEV_CTRL_RETURN_ON_ERROR(device, rc, "bcm_fld_set_host_debug_status\n"); |
| |
| /* If this is the application image, skip the envelope (jump to the data itself). */ |
| if (image_type == BCMOLT_DEVICE_IMAGE_TYPE_APPLICATION) |
| { |
| bcmolt_firmware_envelope envelope; |
| |
| read_len = dev_ctrl_params.image_read_cb(device, image_type, offset_r, (uint8_t *)&envelope, sizeof(envelope)); |
| if (read_len < sizeof(envelope)) |
| { |
| BCM_LOG(ERROR, dev_ctrl_db[device].log_id, "image_read_cb returned error %s (%d)\n", bcmos_strerror(rc), rc); |
| return rc; |
| } |
| BCM_LOG(INFO, dev_ctrl_db[device].log_id, "Upload firmware version=%u.%u.%u.%u\n", |
| envelope.revision.release_major_id, envelope.revision.release_minor_id, envelope.revision.release_revision_id, envelope.revision.model_id); |
| offset_r += read_len; |
| } |
| |
| do |
| { |
| read_len = dev_ctrl_params.image_read_cb(device, image_type, offset_r, image_buf[device], IMAGE_BUF_SIZE); |
| if (read_len < 0) |
| { |
| BCM_LOG(ERROR, dev_ctrl_db[device].log_id, "image_read_cb returned error %s (%d)\n", bcmos_strerror(rc), rc); |
| return rc; |
| } |
| |
| if (!read_len) |
| break; |
| |
| BCM_LOG(INFO, dev_ctrl_db[device].log_id, "Writing %s (%d)\n", image_name, read_len); |
| rc = bcm_fld_write(device, (char *)image_buf[device], read_len, offset_w, image_type); |
| if (rc != BCM_ERR_OK) |
| { |
| BCM_LOG(ERROR, dev_ctrl_db[device].log_id, "bcm_fld_write returned error %s (%d)\n", bcmos_strerror(rc), rc); |
| return rc; |
| } |
| |
| BCM_LOG(INFO, dev_ctrl_db[device].log_id, "Reading %s for checking (%d)\n", image_name, read_len); |
| rc = bcm_fld_read(device, (char *)rd_image_buf[device], (uint32_t *)&read_len, offset_w, image_type); |
| if (rc != BCM_ERR_OK) |
| { |
| BCM_LOG(ERROR, dev_ctrl_db[device].log_id, "bcm_fld_read returned error %s (%d)\n", bcmos_strerror(rc), rc); |
| return rc; |
| } |
| |
| offset_r += read_len; |
| offset_w += read_len; |
| |
| BCM_LOG(INFO, dev_ctrl_db[device].log_id, "Comparing %s (%d)/(%u)\n", image_name, read_len, offset_w); |
| if (memcmp(rd_image_buf[device], image_buf[device], read_len)) |
| { |
| BCM_LOG(ERROR, dev_ctrl_db[device].log_id, "Error during image uploading %x\n", offset_w); |
| return BCM_ERR_INTERNAL; |
| } |
| } while (read_len); |
| |
| if (!offset_w) |
| { |
| BCM_LOG(ERROR, dev_ctrl_db[device].log_id, "%s image file empty or corrupted!!!\n", image_name); |
| return BCM_ERR_INTERNAL; |
| } |
| |
| if (image_type == BCMOLT_DEVICE_IMAGE_TYPE_APPLICATION) |
| { |
| read_len = sizeof(unsigned long); |
| /* first read word from star of the image */ |
| bcm_fld_read(device, (char *)(long)&write_complete[0], (uint32_t *)&read_len, 0, image_type); |
| /* sync before read tail of image */ |
| bcmos_barrier(); |
| /* read from tail of the image */ |
| bcm_fld_read( |
| device, |
| (char *)(long)&write_complete[1], |
| (uint32_t *)&read_len, |
| offset_w - sizeof(unsigned long), |
| image_type); |
| /* now we can be sure that full image in the embedded memory */ |
| } |
| |
| BCM_LOG(INFO, dev_ctrl_db[device].log_id, "Transferred %u bytes\n", offset_w); |
| |
| return BCM_ERR_OK; |
| } |
| |
| static bcmos_errno dev_ctrl_handle_application_image(bcmolt_devid device) |
| { |
| bcmolt_device_cfg_data *dev_params = &dev_ctrl_db[device].device_params; |
| bcmos_errno rc = BCM_ERR_OK; |
| |
| rc = dev_ctrl_handle_file(device, BCMOLT_DEVICE_IMAGE_TYPE_APPLICATION); |
| BCM_DEV_CTRL_RETURN_ON_ERROR(device, rc, "dev_ctrl_handle_file()\n"); |
| rc = bcm_fld_host_finish_write_ddr(device, 0); |
| BCM_DEV_CTRL_RETURN_ON_ERROR(device, rc, "bcm_fld_host_finish_write_ddr()\n"); |
| rc = bcmtrmux_connect(device, dev_params->debug.host_dma_tx_queue_size, dev_params->debug.host_dma_rx_queue_size); |
| BCM_DEV_CTRL_RETURN_ON_ERROR(device, rc, "bcmtrmux_connect()\n"); |
| /* We want that in case ONLY the host is reset, Maple enters standalone mode - this means we need to turn off "hot reset" on the PCIe channel. */ |
| rc = bcm_ll_pcie_host_reset_enable(device,BCMOS_FALSE); |
| BCM_DEV_CTRL_RETURN_ON_ERROR(device, rc, "bcm_ll_pcie_host_reset_enable()\n"); |
| |
| return rc; |
| } |
| |
| static bcmos_errno dev_ctrl_handle_bootloader_image(bcmolt_devid device, uint32_t test_ddr) |
| { |
| bcmos_errno rc = BCM_ERR_OK; |
| |
| rc = dev_ctrl_handle_file(device, BCMOLT_DEVICE_IMAGE_TYPE_BOOTLOADER); |
| BCM_DEV_CTRL_RETURN_ON_ERROR(device, rc, "dev_ctrl_handle_file()\n"); |
| rc = bcm_fld_start_bootloader(device, test_ddr); |
| BCM_DEV_CTRL_RETURN_ON_ERROR(device, rc, "bcm_fld_start_bootloader()\n"); |
| bcmos_timer_start( |
| &dev_ctrl_db[device].boot_seq_info.timer.timer, |
| dev_ctrl_db[device].boot_seq_info.polling_interval); |
| |
| return rc; |
| } |
| |
| static bcmos_errno dev_ctrl_start_fld(bcmolt_devid device, uint32_t test_ddr) |
| { |
| bcmos_errno rc = BCM_ERR_OK; |
| |
| bcm_fld_clear_comm_area(device); |
| |
| rc = dev_ctrl_handle_bootloader_image(device, test_ddr); |
| BCM_DEV_CTRL_RETURN_ON_ERROR(device, rc, "dev_ctrl_handle_bootloader_image\n"); |
| |
| return rc; |
| } |
| |
| static bcmos_errno dev_ctrl_get_exception_log_by_cpu(bcmolt_devid device, uint32_t cpuid) |
| { |
| char *exception_buf; |
| int exception_buf_len; |
| bcmos_errno rc; |
| |
| exception_buf = bcmos_alloc(BCM_FLD_CPU_POSTMORTEM_BUF_SIZE); |
| rc = bcm_fld_copy_exception_log(device, cpuid, exception_buf, &exception_buf_len); |
| if (rc) |
| { |
| BCM_LOG(ERROR, dev_ctrl_db[device].log_id, "bcm_fld_copy_exception_log failed, rc=%u (%s)\n", rc, bcmos_strerror(rc)); |
| goto exit; |
| } |
| bcm_fld_clear_exception_state(device, cpuid); |
| |
| dev_ctrl_send_ind_exception_log(device, cpuid, exception_buf); |
| rc = BCM_ERR_OK; |
| |
| exit: |
| bcmos_free(exception_buf); |
| return rc; |
| } |
| |
| /** Dump exception log if there is one, return BCM_ERR_NOENT if not. */ |
| static bcmos_errno dev_ctrl_get_exception_log_if_present(bcmolt_devid dev_id) |
| { |
| bcmos_errno rc; |
| uint32_t state0, state1; |
| |
| /* Dump exception log, if any. |
| * This should be done prior to writing bootloader, as it will overwrite the same place in SRAM. */ |
| rc = bcm_fld_get_exception_state(dev_id, &state0, &state1); |
| BCM_DEV_CTRL_RETURN_ON_ERROR(dev_id, rc, "bcm_fld_get_exception_state\n"); |
| if (state0) |
| dev_ctrl_get_exception_log_by_cpu(dev_id, 0); |
| if (state1) |
| dev_ctrl_get_exception_log_by_cpu(dev_id, 1); |
| if (!state0 && !state1) |
| return BCM_ERR_NOENT; |
| |
| return BCM_ERR_OK; |
| } |
| |
| /** Dump exception log if there is one, print a message and return BCM_ERR_OK if not. */ |
| static bcmos_errno dev_ctrl_get_exception_log(bcmolt_devid dev_id) |
| { |
| bcmos_errno rc = dev_ctrl_get_exception_log_if_present(dev_id); |
| if (rc == BCM_ERR_NOENT) |
| { |
| BCM_LOG(DEBUG, dev_ctrl_db[dev_id].log_id, "Exception log for device %d is empty\n", dev_id); |
| rc = BCM_ERR_OK; |
| } |
| return rc; |
| } |
| |
| static void dev_ctrl_sw_error_table_dump(bcmolt_devid dev_id) |
| { |
| bcmos_errno err = dev_ctrl_get_sw_error_table(dev_id); |
| uint8_t i; |
| |
| if (BCM_ERR_OK != err) |
| { |
| BCM_LOG(INFO, dev_ctrl_db[dev_id].log_id, "Failed to retrieve Embedded Software Error(s): %d\n", err); |
| } |
| else |
| { |
| BCM_LOG(INFO, dev_ctrl_db[dev_id].log_id, "Found %u Embedded Software Error(s):\n", dev_ctrl_db[dev_id].sw_error_count); |
| for (i = 0; i < dev_ctrl_db[dev_id].sw_error_count; i++) |
| { |
| BCM_LOG(INFO, dev_ctrl_db[dev_id].log_id, |
| "\t[%s] %s:%u inst:%u count:%u first:%llu last:%llu\n", |
| dev_ctrl_db[dev_id].sw_errors[i].task_name, |
| dev_ctrl_db[dev_id].sw_errors[i].filename, |
| dev_ctrl_db[dev_id].sw_errors[i].line_number, |
| dev_ctrl_db[dev_id].sw_errors[i].instance, |
| dev_ctrl_db[dev_id].sw_errors[i].error_counter, |
| (unsigned long long)dev_ctrl_db[dev_id].sw_errors[i].first_error_time_us, |
| (unsigned long long)dev_ctrl_db[dev_id].sw_errors[i].last_error_time_us); |
| } |
| } |
| } |
| #endif |
| |
| static bcmolt_host_connection_fail_reason dev_ctrl_connect_from_reset(bcmolt_devid device, uint32_t test_ddr) |
| { |
| #ifndef IN_BAND |
| bcmos_errno rc; |
| |
| rc = dev_ctrl_params.device_on_cb(device); |
| BCMOS_TRACE_CHECK_RETURN( |
| rc != BCM_ERR_OK, |
| BCMOLT_HOST_CONNECTION_FAIL_REASON_USER_CALLBACK_ERROR, |
| "device_on_cb\n"); |
| |
| rc = dev_ctrl_params.pcie_channel_prepare_cb(device); |
| BCMOS_TRACE_CHECK_RETURN( |
| rc != BCM_ERR_OK, |
| BCMOLT_HOST_CONNECTION_FAIL_REASON_USER_CALLBACK_ERROR, |
| "pcie_channel_prepare_cb\n"); |
| |
| |
| rc = dev_ctrl_register_fld(device); |
| BCMOS_TRACE_CHECK_RETURN( |
| rc != BCM_ERR_OK, |
| BCMOLT_HOST_CONNECTION_FAIL_REASON_INTERNAL_ERROR, |
| "dev_ctrl_register_fld\n"); |
| |
| dev_ctrl_get_exception_log(device); |
| dev_ctrl_sw_error_table_dump(device); |
| |
| #ifndef SIMULATION_BUILD |
| bcm_fld_set_host_event(device, dev_ctrl_db[device].trx_disable_mask); |
| #endif |
| rc = dev_ctrl_start_fld(device, test_ddr); |
| BCMOS_TRACE_CHECK_RETURN( |
| rc != BCM_ERR_OK, |
| BCMOLT_HOST_CONNECTION_FAIL_REASON_INTERNAL_ERROR, |
| "dev_ctrl_start_fld\n"); |
| #endif |
| return BCMOLT_HOST_CONNECTION_FAIL_REASON_NONE; |
| } |
| |
| static bcmolt_host_connection_fail_reason dev_ctrl_connect_to_standalone(bcmolt_devid device) |
| { |
| bcmos_errno rc; |
| |
| /* if Maple runs in standalone mode, and host did reset, we need to register maple to fld and and |
| reconnect the transport layer - no need to rescan, it was done automatically by host when |
| ll_pcie registered maple |
| re-scan without passing thru remove and maple off, lets maple out of reset, remapped, but stucked |
| not, really, working in standaloane mode |
| */ |
| |
| /* The chip is running and the PCIe channel hardware is initialized, we can now safely: |
| * - Register with the FLD driver. |
| * - Restart the PCIe connection process. As part of this process, the host will set the 'host queues valid' flag. |
| * If the device is running and sees this flag is set, it will start the reconnection process as well, in sync |
| * with the host. If the device fails to connect, it means that the device is unresponsive, so the best we can do |
| * is reset the device and reprogram it from scratch. */ |
| #ifndef IN_BAND |
| rc = dev_ctrl_register_fld(device); |
| BCMOS_TRACE_CHECK_RETURN( |
| rc != BCM_ERR_OK, |
| BCMOLT_HOST_CONNECTION_FAIL_REASON_INTERNAL_ERROR, |
| "dev_ctrl_register_fld\n"); |
| |
| dev_ctrl_get_exception_log(device); |
| |
| #ifndef SIMULATION_BUILD |
| bcm_fld_set_host_event(device, dev_ctrl_db[device].trx_disable_mask); |
| #endif |
| #endif |
| |
| BCM_LOG(INFO, dev_ctrl_db[device].log_id, "Requesting re-connect to running device...\n"); |
| #ifndef IN_BAND |
| rc = bcmtrmux_connect( |
| device, |
| dev_ctrl_db[device].device_params.debug.host_dma_tx_queue_size, |
| dev_ctrl_db[device].device_params.debug.host_dma_rx_queue_size); |
| #else |
| /*Need to pass in IP Address and UDP port when running in linux user space*/ |
| rc = bcmtrmux_connect(device, |
| dev_ctrl_db[device].device_params.device_ip_address, |
| dev_ctrl_db[device].device_params.device_udp_port); |
| |
| #endif |
| |
| if (rc == BCM_ERR_OK) |
| { |
| dev_ctrl_db[device].connection_info.state = DEV_CTRL_CONNECTING_STATE_STANDALONE; |
| return BCMOLT_HOST_CONNECTION_FAIL_REASON_NONE; |
| } |
| else |
| { |
| BCM_LOG(INFO, dev_ctrl_db[device].log_id, "Connection failed - running device didn't respond to connection request\n"); |
| return BCMOLT_HOST_CONNECTION_FAIL_REASON_RECONNECT_TIMEOUT; |
| } |
| } |
| |
| static void dev_ctrl_disconnect(bcmolt_devid device) |
| { |
| bcmos_errno rc; |
| |
| dev_ctrl_db[device].device_params.state = BCMOLT_DEVICE_STATE_DISCONNECTED; |
| #ifdef ENABLE_LOG |
| dev_ctrl_db[device].sram_log_offset = 0; |
| dev_ctrl_db[device].msgs_read = 0; |
| #endif |
| stop_keep_alive_process(&dev_ctrl_db[device].ka_info); |
| bcmos_timer_stop(&dev_ctrl_db[device].exception_monitor_timer.timer); |
| dev_ctrl_init_connection_state(device); |
| rc = bcmtrmux_disconnect(device); |
| if (rc != BCM_ERR_OK && rc != BCM_ERR_ALREADY) |
| { |
| BCM_LOG(ERROR, dev_ctrl_db[device].log_id, "bcmtrmux_disconnect returned error: %s (%d)\n", bcmos_strerror(rc), rc); |
| } |
| } |
| |
| static bcmos_errno dev_ctrl_perform_reset_device(bcmolt_devid device) |
| { |
| bcmos_errno rc = BCM_ERR_OK; |
| |
| dev_ctrl_disconnect(device); |
| |
| #ifndef IN_BAND |
| rc = dev_ctrl_params.pcie_channel_remove_cb(device); |
| if (rc != BCM_ERR_OK) |
| { |
| BCM_LOG(ERROR, dev_ctrl_db[device].log_id, "Device remove callback failed: %s (%d)\n", bcmos_strerror(rc), rc); |
| } |
| rc = dev_ctrl_params.device_off_cb(device); |
| if (rc != BCM_ERR_OK) |
| { |
| BCM_LOG(ERROR, dev_ctrl_db[device].log_id, "Device off callback failed: %s (%d)\n", bcmos_strerror(rc), rc); |
| } |
| |
| /* the device has been reset so our FLD info is now invalid */ |
| dev_ctrl_db[device].fld_info.soc_sram_base = 0; |
| bcm_fld_unregister(device); |
| #endif |
| return rc; |
| } |
| static bcmos_errno dev_ctrl_perform_reset(bcmolt_devid device, const bcmolt_msg *msg) |
| { |
| bcmos_errno rc = BCM_ERR_OK; |
| bcmolt_device_reset_mode mode = ((const bcmolt_device_reset *)msg)->data.mode; |
| bcmolt_devid i; |
| |
| switch (mode) |
| { |
| case BCMOLT_DEVICE_RESET_MODE_DEVICE: |
| return dev_ctrl_perform_reset_device(device); |
| |
| case BCMOLT_DEVICE_RESET_MODE_HOST: |
| dev_ctrl_db[device].device_params.state = BCMOLT_DEVICE_STATE_DISCONNECTED; |
| dev_ctrl_db[device].is_host_reset_pending = BCMOS_TRUE; |
| dev_ctrl_send_device_disconnect_msg(device, 0); |
| /* instead of resetting the host right away, wait a few ms so the host can receive the ack / indication */ |
| bcmos_timer_start(&dev_ctrl_db[device].reset_delay_timer.timer, HOST_RESET_DELAY_US); |
| break; |
| |
| case BCMOLT_DEVICE_RESET_MODE_ALL: |
| for (i = 0; i < BCMTR_MAX_OLTS; i++) |
| { |
| dev_ctrl_db[i].device_params.state = BCMOLT_DEVICE_STATE_DISCONNECTED; |
| dev_ctrl_db[i].is_host_reset_pending = BCMOS_TRUE; |
| rc = dev_ctrl_params.device_off_cb(i); |
| if (rc != BCM_ERR_OK) |
| { |
| BCM_LOG(ERROR, dev_ctrl_db[i].log_id, "Device off callback failed: %s (%d)\n", bcmos_strerror(rc), rc); |
| } |
| } |
| /* instead of resetting the host right away, wait a few ms so the host can receive the ack / indication */ |
| bcmos_timer_start(&dev_ctrl_db[device].reset_delay_timer.timer, HOST_RESET_DELAY_US); |
| break; |
| |
| default: |
| BCM_LOG(ERROR, dev_ctrl_db[device].log_id, "Unrecognized reset mode: %d\n", mode); |
| break; |
| } |
| |
| return rc; |
| } |
| |
| static void dev_ctrl_disconnected_state_device_clear_event(bcmolt_devid device, const bcmolt_msg *msg) |
| { |
| dev_ctrl_clear_device_cfg(device); |
| } |
| |
| static void dev_ctrl_disconnected_state_device_config_set_event(bcmolt_devid device, const bcmolt_msg *msg) |
| { |
| if (dev_ctrl_db[device].last_message == NULL) |
| { |
| BCM_LOG(ERROR, dev_ctrl_db[device].log_id, "Our saved message was invalid\n"); |
| return; |
| } |
| |
| dev_ctrl_update_local_configuration(device, msg); |
| dev_ctrl_db[device].last_message->dir = BCMOLT_MSG_DIR_RESPONSE; |
| bcmtrmux_control_to_host(device, dev_ctrl_db[device].last_message); |
| bcmolt_msg_free(dev_ctrl_db[device].last_message); |
| dev_ctrl_db[device].last_message = NULL; |
| } |
| |
| static void dev_ctrl_disconnected_state_device_config_get_event(bcmolt_devid device, const bcmolt_msg *msg) |
| { |
| bcmolt_presence_mask mask; |
| bcmolt_device_cfg *cfg; |
| |
| if (dev_ctrl_db[device].last_message == NULL) |
| { |
| BCM_LOG(ERROR, dev_ctrl_db[device].log_id, "Our saved message was invalid --- aborting\n"); |
| return; |
| } |
| |
| cfg = (bcmolt_device_cfg *)dev_ctrl_db[device].last_message; |
| cfg->data = dev_ctrl_db[device].device_params; |
| mask = dev_ctrl_fill_from_local(device); |
| |
| if ((mask & ~dev_ctrl_db[device].device_params_present) != 0) |
| { |
| bcmolt_msg_err( |
| dev_ctrl_db[device].last_message, |
| DEV_LOG_INVALID_ID, |
| BCM_ERR_INVALID_OP, |
| BCMOLT_ERR_FIELD_NONE, |
| "Unable to get parameters that have never been set"); |
| } |
| |
| dev_ctrl_db[device].last_message->dir = BCMOLT_MSG_DIR_RESPONSE; |
| bcmtrmux_control_to_host(device, dev_ctrl_db[device].last_message); |
| bcmolt_msg_free(dev_ctrl_db[device].last_message); |
| dev_ctrl_db[device].last_message = NULL; |
| } |
| |
| static void dev_ctrl_disconnected_state_device_connect_event(bcmolt_devid device, const bcmolt_msg *msg) |
| { |
| #ifdef IN_BAND |
| bcmolt_device_connect oper; |
| bcmolt_device_key key = {}; |
| #else |
| bcmos_errno rc; |
| #endif |
| bcmolt_host_connection_fail_reason fail_reason; |
| bcmos_bool is_standalone; |
| |
| if (dev_ctrl_db[device].is_host_reset_pending) |
| { |
| BCM_LOG(ERROR, dev_ctrl_db[device].log_id, "Cannot connect while host reset is pending\n"); |
| return; |
| } |
| |
| #ifndef IN_BAND |
| /* Check if the chip is running in standalone mode */ |
| |
| rc = dev_ctrl_params.device_is_running_cb(device, &is_standalone); |
| |
| if (rc != BCM_ERR_OK) |
| { |
| BCM_LOG(ERROR, dev_ctrl_db[device].log_id, "device_status_get_cb returned error: %s (%d)\n", bcmos_strerror(rc), rc); |
| return; |
| } |
| BCM_LOG(INFO, dev_ctrl_db[device].log_id, "device_status_get_cb returned success: is_standalone=%s\n", is_standalone ? "yes" : "no"); |
| |
| /* If the chip is running in standalone mode, connect to it: |
| * - Check the status of the PCIe channel and prepare it if necessary, using host callbacks |
| * - Register the device in the FLD driver |
| * - Use FLD to ask the device to re-initialize its PCIe transport layer |
| * - Initialize the host PCIe transport layer |
| * Otherwise, we must start the boot sequence: |
| * - Power on the device |
| * - Prepare the PCIe channel via host callback |
| * - Register the device in the FLD driver |
| * - Check if there is an exception log from the last time the application was run (and print it if so) |
| * - Start the FLD timer to load the bootcode |
| * In either case, the resulting state is 'connecting'. |
| */ |
| dev_ctrl_db[device].boot_seq_info.polling_counter = BOOT_SEQ_POLLING_MAX_NUMBER; |
| bcmos_timer_handler_set(&dev_ctrl_db[device].boot_seq_info.timer.timer, dev_ctrl_boot_seq_timer_handler, device); |
| #else |
| is_standalone= BCMOS_TRUE; |
| #endif |
| if (is_standalone) |
| { |
| fail_reason = dev_ctrl_connect_to_standalone(device); |
| } |
| else |
| { |
| fail_reason = dev_ctrl_connect_from_reset(device, 0); |
| dev_ctrl_db[device].connection_info.state = DEV_CTRL_CONNECTING_STATE_ESTABLISHING; |
| } |
| |
| if (fail_reason == BCMOLT_HOST_CONNECTION_FAIL_REASON_NONE) |
| { |
| bcmos_timer_start(&dev_ctrl_db[device].connection_info.timer.timer, DEVICE_CONTROL_CONNECT_TIME_US); |
| dev_ctrl_db[device].device_params.state = BCMOLT_DEVICE_STATE_CONNECTING; |
| BCM_LOG(DEBUG, dev_ctrl_db[device].log_id, "began connecting to %s\n", is_standalone ? "standby device" : "device from reset"); |
| |
| #ifndef IN_BAND |
| /*Do nothing*/ |
| #else |
| BCMOLT_OPER_INIT(&oper, device, connect, key); |
| oper.hdr.hdr.type = BCMOLT_MSG_TYPE_SET; |
| (void)bcmtrmux_control_to_line(device, &oper.hdr.hdr); |
| bcmos_printf("Sent connect to embedded...\n"); |
| #endif |
| |
| } |
| else |
| { |
| BCM_LOG(ERROR, dev_ctrl_db[device].log_id, |
| "failed to begin connection process: %s (%d)\n", |
| bcm_str_host_connection_fail_reason(fail_reason), |
| fail_reason); |
| dev_ctrl_send_ind_connection_failure(device, fail_reason); |
| } |
| } |
| |
| static void dev_ctrl_disconnected_state_device_reset_event(bcmolt_devid device, const bcmolt_msg *msg) |
| { |
| dev_ctrl_perform_reset(device, msg); |
| } |
| |
| #ifndef IN_BAND |
| static void dev_ctrl_disconnected_state_device_run_ddr_test_event(bcmolt_devid device, const bcmolt_msg *msg) |
| { |
| bcmolt_host_connection_fail_reason fail_reason; |
| uint32_t test_ddr = 0; |
| |
| const bcmolt_device_run_ddr_test_data *params = &((const bcmolt_device_run_ddr_test *)msg)->data; |
| if (params->cpu) |
| { |
| test_ddr |= BCM_FLD_HOST_RUN_CPU_DDR_TEST_MASK; |
| } |
| if (params->ras_0) |
| { |
| test_ddr |= BCM_FLD_HOST_RUN_RAS_0_TEST_MASK; |
| } |
| if (params->ras_1) |
| { |
| test_ddr |= BCM_FLD_HOST_RUN_RAS_1_TEST_MASK; |
| } |
| |
| dev_ctrl_db[device].boot_seq_info.polling_counter = DDR_TEST_POLLING_MAX_NUMBER; |
| bcmos_timer_handler_set(&dev_ctrl_db[device].boot_seq_info.timer.timer, dev_ctrl_ddr_test_timer_handler, device); |
| fail_reason = dev_ctrl_connect_from_reset(device, test_ddr); |
| if (fail_reason == BCMOLT_HOST_CONNECTION_FAIL_REASON_NONE) |
| { |
| dev_ctrl_db[device].device_params.state = BCMOLT_DEVICE_STATE_TESTING_DDR; |
| } |
| else |
| { |
| bcmolt_device_ddr_test_complete ind = {}; |
| BCMOLT_AUTO_INIT(&ind, device, ddr_test_complete); |
| ind.data.ddr_test.status = BCMOLT_DDR_TEST_STATUS_CONNECTION_FAILED; |
| ind.data.ddr_test.u.connection_failed.reason = fail_reason; |
| dev_ctrl_send_indication(device, &ind.hdr.hdr); |
| } |
| |
| } |
| |
| static void dev_ctrl_testing_ddr_state_ddr_test_completed_event(bcmolt_devid device, const bcmolt_msg *msg) |
| { |
| bcmolt_device_ddr_test_complete ind = {}; |
| |
| BCMOLT_AUTO_INIT(&ind, device, ddr_test_complete); |
| |
| ind.data.ddr_test.status = BCMOLT_DDR_TEST_STATUS_COMPLETED; |
| ind.data.ddr_test.u.completed.cpu_result = bcm_fld_ddr_test_result_get(device, 0); |
| ind.data.ddr_test.u.completed.ras_0_result = bcm_fld_ddr_test_result_get(device, 1); |
| ind.data.ddr_test.u.completed.ras_1_result = bcm_fld_ddr_test_result_get(device, 2); |
| |
| dev_ctrl_perform_reset_device(device); |
| dev_ctrl_db[device].device_params.state = BCMOLT_DEVICE_STATE_DISCONNECTED; |
| |
| dev_ctrl_send_indication(device, &ind.hdr.hdr); |
| } |
| |
| static void dev_ctrl_testing_ddr_state_ddr_test_timeout_event(bcmolt_devid device, const bcmolt_msg *msg) |
| { |
| bcmolt_device_ddr_test_complete ind = {}; |
| |
| dev_ctrl_perform_reset_device(device); |
| dev_ctrl_db[device].device_params.state = BCMOLT_DEVICE_STATE_DISCONNECTED; |
| |
| BCMOLT_AUTO_INIT(&ind, device, ddr_test_complete); |
| ind.data.ddr_test.status = BCMOLT_DDR_TEST_STATUS_TIMEOUT; |
| dev_ctrl_send_indication(device, &ind.hdr.hdr); |
| } |
| #endif |
| |
| static void dev_ctrl_connecting_state_connection_failure_event(bcmolt_devid device, const bcmolt_msg *msg) |
| { |
| dev_ctrl_disconnect(device); |
| dev_ctrl_send_ind_connection_failure_from_msg(device, msg); |
| } |
| |
| static void dev_ctrl_connecting_state_connection_established_event(bcmolt_devid device, const bcmolt_msg *msg) |
| { |
| if (dev_ctrl_db[device].connection_info.state == DEV_CTRL_CONNECTING_STATE_ESTABLISHING) |
| { |
| /* Initial connection is complete, now wait for the firmware to be initialized. */ |
| dev_ctrl_db[device].connection_info.state = DEV_CTRL_CONNECTING_STATE_CONFIGURING; |
| dev_ctrl_send_config_set_msg( |
| device, |
| dev_ctrl_db[device].device_params_present, |
| 0, |
| &dev_ctrl_db[device].device_params); |
| if (dev_ctrl_db[device].connection_info.config_send_counter > 0) |
| { |
| dev_ctrl_db[device].connection_info.config_send_counter--; |
| } |
| bcmos_timer_start( |
| &dev_ctrl_db[device].connection_info.timer.timer, |
| dev_ctrl_db[device].connection_info.config_interval); |
| } |
| else |
| { |
| /* Initial connection was already done - something must have gone wrong. Try again. */ |
| if (dev_ctrl_db[device].connection_info.config_send_counter > 0) |
| { |
| dev_ctrl_send_config_set_msg( |
| device, |
| dev_ctrl_db[device].device_params_present, |
| 0, |
| &dev_ctrl_db[device].device_params); |
| bcmos_timer_start( |
| &dev_ctrl_db[device].connection_info.timer.timer, |
| dev_ctrl_db[device].connection_info.config_interval); |
| dev_ctrl_db[device].connection_info.config_send_counter--; |
| } |
| else |
| { |
| dev_ctrl_disconnect(device); |
| dev_ctrl_send_ind_connection_failure(device, BCMOLT_HOST_CONNECTION_FAIL_REASON_TIMEOUT); |
| } |
| } |
| } |
| |
| static inline bcmos_bool dev_ctrl_nni_speed_equal(const bcmolt_device_nni_speed *a, const bcmolt_device_nni_speed *b) |
| { |
| return (a->first_half == b->first_half && a->second_half == b->second_half); |
| } |
| |
| static void dev_ctrl_connecting_state_device_ready_event(bcmolt_devid device, const bcmolt_msg *msg) |
| { |
| bcmos_errno rc; |
| const bcmolt_device_device_ready *ready_msg = (const bcmolt_device_device_ready *)msg; |
| bcmolt_host_connection_fail_reason fail_reason = BCMOLT_HOST_CONNECTION_FAIL_REASON__NUM_OF; |
| |
| if (dev_ctrl_db[device].connection_info.state == DEV_CTRL_CONNECTING_STATE_ESTABLISHING) |
| { |
| BCM_LOG(ERROR, dev_ctrl_db[device].log_id, "Unexpected 'device ready' indication before connection is established\n"); |
| return; |
| } |
| |
| if (ready_msg->data.system_mode != dev_ctrl_db[device].device_params.system_mode) |
| { |
| fail_reason = BCMOLT_HOST_CONNECTION_FAIL_REASON_SYSTEM_MODE_MISMATCH; |
| } |
| else if ((dev_ctrl_db[device].device_params_present & BCMOLT_PROP_MASK_GET(device, _cfg, nni_speed)) != 0 && |
| !dev_ctrl_nni_speed_equal(&ready_msg->data.nni_speed, &dev_ctrl_db[device].device_params.nni_speed)) |
| { |
| fail_reason = BCMOLT_HOST_CONNECTION_FAIL_REASON_NNI_SPEED_MISMATCH; |
| } |
| else |
| { |
| rc = dev_ctrl_validate_fw_version(device, &ready_msg->data.firmware_sw_version); |
| if (rc != BCM_ERR_OK) |
| { |
| fail_reason = BCMOLT_HOST_CONNECTION_FAIL_REASON_SOFTWARE_VERSION_MISMATCH; |
| } |
| } |
| |
| if (fail_reason == BCMOLT_HOST_CONNECTION_FAIL_REASON__NUM_OF) |
| { |
| dev_ctrl_db[device].device_params.firmware_sw_version = ready_msg->data.firmware_sw_version; |
| dev_ctrl_db[device].device_params.chip_revision = ready_msg->data.chip_revision; |
| if ((dev_ctrl_db[device].device_params_present & BCMOLT_PROP_MASK_GET(device, _cfg, nni_speed)) == 0) |
| { |
| dev_ctrl_db[device].device_params.nni_speed = ready_msg->data.nni_speed; |
| dev_ctrl_db[device].device_params_present |= BCMOLT_PROP_MASK_GET(device, _cfg, nni_speed); |
| } |
| |
| if (dev_ctrl_db[device].ka_info.ka_interval > 0) |
| { |
| start_keep_alive_process(&dev_ctrl_db[device].ka_info); |
| } |
| |
| #ifndef IN_BAND |
| bcmos_timer_start(&dev_ctrl_db[device].exception_monitor_timer.timer, EXCEPTION_LOG_MONITOR_INTERVAL); |
| #endif |
| dev_ctrl_db[device].device_params.state = BCMOLT_DEVICE_STATE_READY; |
| dev_ctrl_send_ind_connection_complete( |
| device, |
| dev_ctrl_db[device].connection_info.state == DEV_CTRL_CONNECTING_STATE_STANDALONE); |
| } |
| else |
| { |
| dev_ctrl_send_device_disconnect_msg(device, 0); |
| dev_ctrl_disconnect(device); |
| dev_ctrl_send_ind_connection_failure(device, fail_reason); |
| } |
| } |
| |
| static void dev_ctrl_connecting_state_received_ack(bcmolt_devid device, const bcmolt_msg *msg) |
| { |
| /* This is called when we receive the ACK response against the configuraiton set message which was sent while the |
| * state-machine as part of the initial connection process. If the result is OK, we should just move on and keep |
| * waiting for the "ready" indication. If the result is not OK, the device rejected the configuration so we |
| * should disconnect the device and send a failure indication. */ |
| if (msg->err != BCM_ERR_OK) |
| { |
| bcmolt_host_connection_fail_reason fail_reason; |
| |
| dev_ctrl_disconnect(device); |
| |
| /* Figure out which failure reason based on the message error code. */ |
| switch (msg->err) |
| { |
| case BCM_ERR_NOT_SUPPORTED: |
| fail_reason = BCMOLT_HOST_CONNECTION_FAIL_REASON_SYSTEM_MODE_NOT_SUPPORTED; |
| break; |
| case BCM_ERR_PARM: |
| fail_reason = BCMOLT_HOST_CONNECTION_FAIL_REASON_PARAMETER; |
| break; |
| default: |
| fail_reason = BCMOLT_HOST_CONNECTION_FAIL_REASON_INTERNAL_ERROR; |
| break; |
| } |
| |
| dev_ctrl_send_ind_connection_failure(device, fail_reason); |
| } |
| } |
| |
| static void dev_ctrl_ready_state_device_config_set_event(bcmolt_devid device, const bcmolt_msg *msg) |
| { |
| const bcmolt_device_cfg *cfg = (const bcmolt_device_cfg *)msg; |
| |
| dev_ctrl_send_config_set_msg(device, msg->presence_mask, dev_ctrl_db[device].last_message->corr_tag, &cfg->data); |
| |
| bcmos_timer_start(&dev_ctrl_db[device].device_response_timer.timer, DEVICE_RESPONSE_TIMEOUT_LENGTH); |
| dev_ctrl_db[device].device_params.state = BCMOLT_DEVICE_STATE_WAITING_FOR_DEVICE; |
| } |
| |
| static void dev_ctrl_ready_state_device_config_get_event(bcmolt_devid device, const bcmolt_msg *msg) |
| { |
| bcmolt_presence_mask mask; |
| |
| if (dev_ctrl_db[device].last_message == NULL) |
| { |
| BCM_LOG(ERROR, dev_ctrl_db[device].log_id, "Our saved message was invalid --- aborting\n"); |
| return; |
| } |
| |
| mask = dev_ctrl_fill_from_local(device); |
| if (mask == 0) |
| { |
| dev_ctrl_db[device].last_message->dir = BCMOLT_MSG_DIR_RESPONSE; |
| bcmtrmux_control_to_host(device, dev_ctrl_db[device].last_message); |
| bcmolt_msg_free(dev_ctrl_db[device].last_message); |
| dev_ctrl_db[device].last_message = NULL; |
| } |
| else |
| { |
| bcmos_timer_start(&dev_ctrl_db[device].device_response_timer.timer, DEVICE_RESPONSE_TIMEOUT_LENGTH); |
| dev_ctrl_db[device].device_params.state = BCMOLT_DEVICE_STATE_WAITING_FOR_DEVICE; |
| dev_ctrl_send_config_get_msg(device, mask, dev_ctrl_db[device].last_message->corr_tag); |
| } |
| } |
| |
| static void dev_ctrl_ready_state_device_disconnect_event(bcmolt_devid device, const bcmolt_msg *msg) |
| { |
| dev_ctrl_send_device_disconnect_msg(device, 0); |
| dev_ctrl_disconnect(device); |
| dev_ctrl_send_ind_disconnection_complete(device); |
| } |
| |
| static void dev_ctrl_ready_state_device_reset_event(bcmolt_devid device, const bcmolt_msg *msg) |
| { |
| dev_ctrl_perform_reset(device, msg); |
| dev_ctrl_send_ind_disconnection_complete(device); |
| } |
| |
| static void dev_ctrl_ready_state_connection_failure_event(bcmolt_devid device, const bcmolt_msg *msg) |
| { |
| dev_ctrl_disconnect(device); |
| dev_ctrl_send_ind_connection_failure_from_msg(device, msg); |
| } |
| |
| static void dev_ctrl_waiting_for_device_state_timer_timeout(bcmolt_devid device, const bcmolt_msg *msg) |
| { |
| if (dev_ctrl_db[device].last_message == NULL) |
| { |
| BCM_LOG(ERROR, dev_ctrl_db[device].log_id, "Our saved message was invalid --- aborting\n"); |
| return; |
| } |
| |
| bcmolt_msg_err( |
| dev_ctrl_db[device].last_message, |
| DEV_LOG_INVALID_ID, |
| BCM_ERR_INTERNAL, |
| BCMOLT_ERR_FIELD_NONE, |
| "No response to configuration message from embedded"); |
| dev_ctrl_db[device].last_message->dir = BCMOLT_MSG_DIR_RESPONSE; |
| bcmtrmux_control_to_host(device, dev_ctrl_db[device].last_message); |
| bcmolt_msg_free(dev_ctrl_db[device].last_message); |
| dev_ctrl_db[device].last_message = NULL; |
| dev_ctrl_db[device].device_params.state = BCMOLT_DEVICE_STATE_READY; |
| } |
| |
| static void dev_ctrl_waiting_for_device_state_received_ack(bcmolt_devid device, const bcmolt_msg *msg) |
| { |
| bcmos_timer_stop(&dev_ctrl_db[device].device_response_timer.timer); |
| |
| if (dev_ctrl_db[device].last_message == NULL) |
| { |
| BCM_LOG(ERROR, dev_ctrl_db[device].log_id, "Our saved message was invalid --- aborting\n"); |
| return; |
| } |
| |
| if (msg->corr_tag != dev_ctrl_db[device].last_message->corr_tag) |
| { |
| /* ignore this response as it was to something other than our request */ |
| return; |
| } |
| |
| if (msg->err != BCM_ERR_OK) |
| { |
| dev_ctrl_db[device].last_message->err = msg->err; |
| dev_ctrl_db[device].last_message->err_field_idx = msg->err_field_idx; |
| memcpy(dev_ctrl_db[device].last_message->err_text, msg->err_text, sizeof(msg->err_text)); |
| dev_ctrl_db[device].last_message->dir = BCMOLT_MSG_DIR_RESPONSE; |
| bcmtrmux_control_to_host(device, dev_ctrl_db[device].last_message); |
| bcmolt_msg_free(dev_ctrl_db[device].last_message); |
| dev_ctrl_db[device].last_message = NULL; |
| dev_ctrl_db[device].device_params.state = BCMOLT_DEVICE_STATE_READY; |
| return; |
| } |
| |
| if (msg->type == BCMOLT_MSG_TYPE_GET) |
| { |
| const bcmolt_device_cfg *source = (const bcmolt_device_cfg *)msg; |
| bcmolt_device_cfg *dest = (bcmolt_device_cfg *)dev_ctrl_db[device].last_message; |
| if (BCMOLT_CFG_PROP_IS_SET(source, device, protection_switching_ext_irq)) |
| { |
| dest->data.protection_switching_ext_irq = source->data.protection_switching_ext_irq; |
| } |
| if (BCMOLT_CFG_PROP_IS_SET(source, device, epon_clock_transport_sample_delay)) |
| { |
| dest->data.epon_clock_transport_sample_delay = source->data.epon_clock_transport_sample_delay; |
| } |
| if (BCMOLT_CFG_PROP_IS_SET(source, device, indication_shaping)) |
| { |
| dest->data.indication_shaping = source->data.indication_shaping; |
| } |
| if (BCMOLT_CFG_PROP_IS_SET(source, device, gpon_xgpon_tod_enable)) |
| { |
| dest->data.gpon_xgpon_tod_enable = source->data.gpon_xgpon_tod_enable; |
| } |
| if (BCMOLT_CFG_PROP_IS_SET(source, device, gpon_xgpon_tod_gpio_pin)) |
| { |
| dest->data.gpon_xgpon_tod_gpio_pin = source->data.gpon_xgpon_tod_gpio_pin; |
| } |
| if (BCMOLT_CFG_PROP_IS_SET(source, device, gpon_xgpon_tod_connected_internally)) |
| { |
| dest->data.gpon_xgpon_tod_connected_internally = source->data.gpon_xgpon_tod_connected_internally; |
| } |
| if (BCMOLT_CFG_PROP_IS_SET(source, device, epon_8021_as_tod_format)) |
| { |
| dest->data.epon_8021_as_tod_format = source->data.epon_8021_as_tod_format; |
| } |
| if (BCMOLT_CFG_PROP_IS_SET(source, device, firmware_sw_version)) |
| { |
| dest->data.firmware_sw_version = source->data.firmware_sw_version; |
| } |
| if (BCMOLT_CFG_PROP_IS_SET(source, device, chip_revision)) |
| { |
| dest->data.chip_revision = source->data.chip_revision; |
| } |
| if (BCMOLT_CFG_PROP_IS_SET(source, device, chip_temperature)) |
| { |
| dest->data.chip_temperature = source->data.chip_temperature; |
| } |
| if (BCMOLT_CFG_PROP_IS_SET(source, device, epon_shaper_mode)) |
| { |
| dest->data.epon_shaper_mode = source->data.epon_shaper_mode; |
| } |
| if (BCMOLT_CFG_PROP_IS_SET(source, device, embedded_image_list)) |
| { |
| dest->data.embedded_image_list = source->data.embedded_image_list; |
| } |
| if (BCMOLT_CFG_PROP_IS_SET(source, device, chip_voltage)) |
| { |
| dest->data.chip_voltage = source->data.chip_voltage; |
| } |
| if (BCMOLT_CFG_PROP_IS_SET(source, device, epon_tod_string)) |
| { |
| dest->data.epon_tod_string = source->data.epon_tod_string; |
| } |
| if (BCMOLT_CFG_PROP_IS_SET(source, device, xgpon_num_of_onus)) |
| { |
| dest->data.xgpon_num_of_onus = source->data.xgpon_num_of_onus; |
| } |
| if (BCMOLT_CFG_PROP_IS_SET(source, device, gpon_xgpon_tod_string_length)) |
| { |
| dest->data.gpon_xgpon_tod_string_length = source->data.gpon_xgpon_tod_string_length; |
| } |
| if (BCMOLT_CFG_PROP_IS_SET(source, device, tod_uart_baudrate)) |
| { |
| dest->data.tod_uart_baudrate = source->data.tod_uart_baudrate; |
| } |
| } |
| |
| if (msg->type == BCMOLT_MSG_TYPE_SET) |
| { |
| bcmolt_device_cfg *source = (bcmolt_device_cfg *)dev_ctrl_db[device].last_message; |
| bcmolt_device_cfg_data *dest = &dev_ctrl_db[device].device_params; |
| dev_ctrl_db[device].device_params_present |= source->hdr.hdr.presence_mask; |
| if (BCMOLT_CFG_PROP_IS_SET(source, device, protection_switching_ext_irq)) |
| { |
| dest->protection_switching_ext_irq = source->data.protection_switching_ext_irq; |
| } |
| if (BCMOLT_CFG_PROP_IS_SET(source, device, epon_clock_transport_sample_delay)) |
| { |
| dest->epon_clock_transport_sample_delay = source->data.epon_clock_transport_sample_delay; |
| } |
| if (BCMOLT_CFG_PROP_IS_SET(source, device, indication_shaping)) |
| { |
| dest->indication_shaping = source->data.indication_shaping; |
| } |
| if (BCMOLT_CFG_PROP_IS_SET(source, device, gpon_xgpon_tod_enable)) |
| { |
| dest->gpon_xgpon_tod_enable = source->data.gpon_xgpon_tod_enable; |
| } |
| if (BCMOLT_CFG_PROP_IS_SET(source, device, gpon_xgpon_tod_gpio_pin)) |
| { |
| dest->gpon_xgpon_tod_gpio_pin = source->data.gpon_xgpon_tod_gpio_pin; |
| } |
| if (BCMOLT_CFG_PROP_IS_SET(source, device, gpon_xgpon_tod_connected_internally)) |
| { |
| dest->gpon_xgpon_tod_connected_internally = source->data.gpon_xgpon_tod_connected_internally; |
| } |
| if (BCMOLT_CFG_PROP_IS_SET(source, device, epon_8021_as_tod_format)) |
| { |
| dest->epon_8021_as_tod_format = source->data.epon_8021_as_tod_format; |
| } |
| if (BCMOLT_CFG_PROP_IS_SET(source, device, epon_tod_string)) |
| { |
| dest->epon_tod_string = source->data.epon_tod_string; |
| } |
| if (BCMOLT_CFG_PROP_IS_SET(source, device, epon_shaper_mode)) |
| { |
| dest->epon_shaper_mode = source->data.epon_shaper_mode; |
| } |
| if (BCMOLT_CFG_PROP_IS_SET(source, device, keepalive_tolerance)) |
| { |
| dev_ctrl_db[device].ka_info.ka_tolerance = source->data.keepalive_tolerance; |
| } |
| if (BCMOLT_CFG_PROP_IS_SET(source, device, keepalive_interval)) |
| { |
| dev_ctrl_db[device].ka_info.ka_interval = source->data.keepalive_interval * BCMOS_MICROSECONDS_IN_SECONDS; |
| if (dev_ctrl_db[device].ka_info.ka_interval > 0) |
| { |
| start_keep_alive_process(&dev_ctrl_db[device].ka_info); |
| } |
| else |
| { |
| stop_keep_alive_process(&dev_ctrl_db[device].ka_info); |
| } |
| } |
| if (BCMOLT_CFG_PROP_IS_SET(source, device, gpon_xgpon_tod_string_length)) |
| { |
| dest->gpon_xgpon_tod_string_length = source->data.gpon_xgpon_tod_string_length; |
| } |
| if (BCMOLT_CFG_PROP_IS_SET(source, device, tod_uart_baudrate)) |
| { |
| dest->tod_uart_baudrate = source->data.tod_uart_baudrate; |
| } |
| } |
| |
| dev_ctrl_db[device].last_message->dir = BCMOLT_MSG_DIR_RESPONSE; |
| bcmtrmux_control_to_host(device, dev_ctrl_db[device].last_message); |
| dev_ctrl_db[device].device_params.state = BCMOLT_DEVICE_STATE_READY; |
| bcmolt_msg_free(dev_ctrl_db[device].last_message); |
| dev_ctrl_db[device].last_message = NULL; |
| } |
| |
| static dev_ctrl_sm_cb dev_ctrl_sm[BCMOLT_DEVICE_STATE__NUM_OF][DEVICE_CONTROL_EVENT__NUM_OF] = |
| { |
| [BCMOLT_DEVICE_STATE_DISCONNECTED] = |
| { |
| [DEVICE_CONTROL_EVENT_DEVICE_CLEAR] = dev_ctrl_disconnected_state_device_clear_event, |
| [DEVICE_CONTROL_EVENT_DEVICE_CONFIG_SET] = dev_ctrl_disconnected_state_device_config_set_event, |
| [DEVICE_CONTROL_EVENT_DEVICE_CONFIG_GET] = dev_ctrl_disconnected_state_device_config_get_event, |
| [DEVICE_CONTROL_EVENT_DEVICE_CONNECT] = dev_ctrl_disconnected_state_device_connect_event, |
| [DEVICE_CONTROL_EVENT_DEVICE_RESET] = dev_ctrl_disconnected_state_device_reset_event, |
| #ifndef IN_BAND |
| [DEVICE_CONTROL_EVENT_RUN_DDR_TEST] = dev_ctrl_disconnected_state_device_run_ddr_test_event, |
| #endif |
| }, |
| |
| [BCMOLT_DEVICE_STATE_CONNECTING] = |
| { |
| [DEVICE_CONTROL_EVENT_CONNECTION_FAILURE] = dev_ctrl_connecting_state_connection_failure_event, |
| [DEVICE_CONTROL_EVENT_CONNECTION_ESTABLISHED] = dev_ctrl_connecting_state_connection_established_event, |
| [DEVICE_CONTROL_EVENT_DEVICE_READY] = dev_ctrl_connecting_state_device_ready_event, |
| [DEVICE_CONTROL_EVENT_DEVICE_RECEIVED_ACK] = dev_ctrl_connecting_state_received_ack, |
| }, |
| |
| [BCMOLT_DEVICE_STATE_READY] = |
| { |
| [DEVICE_CONTROL_EVENT_DEVICE_CONFIG_SET] = dev_ctrl_ready_state_device_config_set_event, |
| [DEVICE_CONTROL_EVENT_DEVICE_CONFIG_GET] = dev_ctrl_ready_state_device_config_get_event, |
| [DEVICE_CONTROL_EVENT_DEVICE_DISCONNECT] = dev_ctrl_ready_state_device_disconnect_event, |
| [DEVICE_CONTROL_EVENT_DEVICE_RESET] = dev_ctrl_ready_state_device_reset_event, |
| [DEVICE_CONTROL_EVENT_CONNECTION_FAILURE] = dev_ctrl_ready_state_connection_failure_event, |
| }, |
| |
| [BCMOLT_DEVICE_STATE_WAITING_FOR_DEVICE] = |
| { |
| [DEVICE_CONTROL_EVENT_DEVICE_TIMER_TIMEOUT] = dev_ctrl_waiting_for_device_state_timer_timeout, |
| [DEVICE_CONTROL_EVENT_DEVICE_RECEIVED_ACK] = dev_ctrl_waiting_for_device_state_received_ack |
| }, |
| |
| #ifndef IN_BAND |
| [BCMOLT_DEVICE_STATE_TESTING_DDR] = |
| { |
| [DEVICE_CONTROL_EVENT_DDR_TEST_COMPLETED] = dev_ctrl_testing_ddr_state_ddr_test_completed_event, |
| [DEVICE_CONTROL_EVENT_DDR_TEST_TIMEOUT] = dev_ctrl_testing_ddr_state_ddr_test_timeout_event |
| } |
| #endif |
| }; |
| |
| static void dev_ctrl_sm_err_cb(bcmolt_devid device, dev_ctrl_event event) |
| { |
| BCM_LOG(ERROR, dev_ctrl_db[device].log_id, |
| "Unexpected event: device=%d, state=%s, event=%s\n", |
| device, |
| bcm_str_device_state(dev_ctrl_db[device].device_params.state), |
| bcm_str_device_event(event)); |
| } |
| |
| static void dev_ctrl_sm_call_state_cb(bcmolt_devid device, dev_ctrl_event event, const bcmolt_msg *msg) |
| { |
| dev_ctrl_sm_cb cb; |
| dev_ctrl_db[device].last_event = event; |
| cb = dev_ctrl_sm[dev_ctrl_db[device].device_params.state][event]; |
| if (cb == NULL) |
| { |
| dev_ctrl_sm_err_cb(device, event); |
| } |
| else |
| { |
| cb(device, msg); |
| } |
| } |
| |
| static void dev_ctrl_ka_rx_handler(bcmolt_devid device, bcmolt_device_device_keep_alive *msg) |
| { |
| keep_alive_rx_handler((const bcmos_keep_alive_data_msg *)&msg->data, &dev_ctrl_db[device].ka_info); |
| } |
| |
| static bcmos_errno dev_ctrl_ka_tx_handler(bcmolt_devid device, bcmolt_msg *msg) |
| { |
| bcmos_errno rc; |
| |
| if (!dev_ctrl_is_connected(dev_ctrl_db[device].device_params.state)) |
| { |
| return BCM_ERR_OK; /* in case this happens during a disconnect/reset operation */ |
| } |
| |
| msg->corr_tag = ++dev_ctrl_db[device].corr_tag; |
| rc = bcmtrmux_control_to_line(device, msg); |
| return rc; |
| } |
| |
| static bcmos_errno dev_ctrl_ka_disconnect_handler(bcmolt_devid device) |
| { |
| dev_ctrl_disconnect(device); |
| dev_ctrl_send_ind_connection_failure(device, BCMOLT_HOST_CONNECTION_FAIL_REASON_KEEPALIVE); |
| return BCM_ERR_OK; |
| } |
| |
| static void handle_oper_set_msg(bcmolt_devid device, bcmolt_msg *msg) |
| { |
| dev_ctrl_event event; |
| |
| switch (msg->subgroup) |
| { |
| case BCMOLT_DEVICE_OPER_ID_RESET: |
| event = DEVICE_CONTROL_EVENT_DEVICE_RESET; |
| break; |
| case BCMOLT_DEVICE_OPER_ID_CONNECT: |
| event = DEVICE_CONTROL_EVENT_DEVICE_CONNECT; |
| break; |
| case BCMOLT_DEVICE_OPER_ID_DISCONNECT: |
| event = DEVICE_CONTROL_EVENT_DEVICE_DISCONNECT; |
| break; |
| case BCMOLT_DEVICE_OPER_ID_RUN_DDR_TEST: |
| event = DEVICE_CONTROL_EVENT_RUN_DDR_TEST; |
| break; |
| default : |
| event = DEVICE_CONTROL_EVENT_NO_EVENT; |
| break; |
| } |
| |
| /* Send response to the waiting application */ |
| msg->dir = BCMOLT_MSG_DIR_RESPONSE; |
| bcmtrmux_control_to_host(device, msg); |
| |
| /* Handle event */ |
| if (event != DEVICE_CONTROL_EVENT_NO_EVENT) |
| { |
| dev_ctrl_sm_call_state_cb(device, event, msg); |
| } |
| } |
| |
| static void dev_ctrl_process_msg(bcmolt_devid device, bcmolt_msg *msg) |
| { |
| if (msg->dir == BCMOLT_MSG_DIR_RESPONSE) |
| { |
| dev_ctrl_sm_call_state_cb(device, DEVICE_CONTROL_EVENT_DEVICE_RECEIVED_ACK, msg); |
| } |
| |
| if (msg->group == BCMOLT_MGT_GROUP_AUTO) |
| { |
| if (msg->subgroup != BCMOLT_DEVICE_AUTO_ID_DEVICE_KEEP_ALIVE) |
| { |
| BCM_LOG(DEBUG, dev_ctrl_db[device].log_id, "Indication = %s (%d)\n", bcm_str_auto_id(msg->subgroup), msg->subgroup); |
| } |
| |
| switch (msg->subgroup) |
| { |
| case BCMOLT_DEVICE_AUTO_ID_DEVICE_KEEP_ALIVE: |
| dev_ctrl_ka_rx_handler(device, (bcmolt_device_device_keep_alive *)msg); |
| break; |
| case BCMOLT_DEVICE_AUTO_ID_CONNECTION_FAILURE: |
| /* note: this indication is not generated by the device - it's generated by device control itself */ |
| dev_ctrl_sm_call_state_cb(device, DEVICE_CONTROL_EVENT_CONNECTION_FAILURE, msg); |
| break; |
| case BCMOLT_DEVICE_AUTO_ID_CONNECTION_ESTABLISHED: |
| dev_ctrl_sm_call_state_cb(device, DEVICE_CONTROL_EVENT_CONNECTION_ESTABLISHED, msg); |
| break; |
| case BCMOLT_DEVICE_AUTO_ID_DEVICE_READY: |
| dev_ctrl_sm_call_state_cb(device, DEVICE_CONTROL_EVENT_DEVICE_READY, msg); |
| break; |
| default: |
| BCM_LOG(ERROR, dev_ctrl_db[device].log_id, "Unexpected indication\n"); |
| break; |
| } |
| } |
| |
| if (msg->group == BCMOLT_MGT_GROUP_OPER && msg->type == BCMOLT_MSG_TYPE_SET && msg->dir != BCMOLT_MSG_DIR_RESPONSE) |
| { |
| /* All operations happen entirely on the host side */ |
| handle_oper_set_msg(device, msg); |
| } |
| |
| if (msg->group == BCMOLT_MGT_GROUP_CFG && msg->dir != BCMOLT_MSG_DIR_RESPONSE) |
| { |
| dev_ctrl_db[device].last_message = msg; |
| switch (msg->type) |
| { |
| case BCMOLT_MSG_TYPE_GET: |
| dev_ctrl_sm_call_state_cb(device, DEVICE_CONTROL_EVENT_DEVICE_CONFIG_GET, msg); |
| break; |
| case BCMOLT_MSG_TYPE_CLEAR: |
| dev_ctrl_sm_call_state_cb(device, DEVICE_CONTROL_EVENT_DEVICE_CLEAR, msg); |
| break; |
| case BCMOLT_MSG_TYPE_SET: |
| dev_ctrl_sm_call_state_cb(device, DEVICE_CONTROL_EVENT_DEVICE_CONFIG_SET, msg); |
| break; |
| default: |
| break; |
| } |
| msg = NULL; |
| } |
| |
| if (msg != NULL) |
| { |
| bcmolt_msg_free(msg); |
| } |
| } |
| |
| static bcmos_timer_rc dev_ctrl_connection_timer_handler(bcmos_timer *timer, long data) |
| { |
| bcmolt_devid device = DEVICE_ID_FROM_MODULE_ID(timer->parm.owner); |
| |
| if (dev_ctrl_db[device].device_params.state != BCMOLT_DEVICE_STATE_CONNECTING) |
| { |
| /* The timer is irrelevant in this state - discard it and return. */ |
| } |
| else if (dev_ctrl_db[device].connection_info.state == DEV_CTRL_CONNECTING_STATE_CONFIGURING) |
| { |
| /* After we have sent the first "device configuration set" message to the device, the connection timer is used |
| * for retransmitting this message in case the device missed it. */ |
| bcmolt_device_connection_established ind = {}; |
| BCMOLT_AUTO_INIT(&ind, device, connection_established); |
| dev_ctrl_sm_call_state_cb(device, DEVICE_CONTROL_EVENT_CONNECTION_ESTABLISHED, &ind.hdr.hdr); |
| } |
| else |
| { |
| /* Connection failure due to timeout. */ |
| bcmolt_device_connection_failure ind = {}; |
| BCMOLT_AUTO_INIT(&ind, device, connection_failure); |
| ind.data.reason = BCMOLT_HOST_CONNECTION_FAIL_REASON_TIMEOUT; |
| dev_ctrl_sm_call_state_cb(device, DEVICE_CONTROL_EVENT_CONNECTION_FAILURE, &ind.hdr.hdr); |
| } |
| |
| return BCMOS_TIMER_STOP; |
| } |
| |
| #ifndef IN_BAND |
| static void dev_ctrl_get_ras_modes(bcmolt_system_mode system_mode, uint32_t *ras_0_mode, uint32_t *ras_1_mode) |
| { |
| switch (system_mode) |
| { |
| case BCMOLT_SYSTEM_MODE_GPON__4_X: |
| case BCMOLT_SYSTEM_MODE_GPON__8_X: |
| *ras_0_mode = BCM_FLD_RAS_MODE_GPON; |
| *ras_1_mode = BCM_FLD_RAS_MODE_NOT_CONFIGURED; |
| break; |
| case BCMOLT_SYSTEM_MODE_GPON__16_X: |
| *ras_0_mode = BCM_FLD_RAS_MODE_GPON; |
| *ras_1_mode = BCM_FLD_RAS_MODE_GPON; |
| break; |
| case BCMOLT_SYSTEM_MODE_XGPON_1__4_X: |
| case BCMOLT_SYSTEM_MODE_XGPON_1__8_X: |
| case BCMOLT_SYSTEM_MODE_NGPON2__8_X_2_P_5_G: |
| *ras_0_mode = BCM_FLD_RAS_MODE_XGPON; |
| *ras_1_mode = BCM_FLD_RAS_MODE_XGPON; |
| break; |
| case BCMOLT_SYSTEM_MODE_GPON_8_XGPON_4_X_COEXISTENCE: |
| *ras_0_mode = BCM_FLD_RAS_MODE_XGPON; |
| *ras_1_mode = BCM_FLD_RAS_MODE_GPON; |
| break; |
| case BCMOLT_SYSTEM_MODE_XGS__2_X_10_G: |
| case BCMOLT_SYSTEM_MODE_NGPON2__2_X_10_G: |
| *ras_0_mode = BCM_FLD_RAS_MODE_XGS_NGPON2; |
| *ras_1_mode = BCM_FLD_RAS_MODE_XGS_NGPON2; |
| break; |
| default: |
| *ras_0_mode = BCM_FLD_RAS_MODE_NOT_CONFIGURED; |
| *ras_1_mode = BCM_FLD_RAS_MODE_NOT_CONFIGURED; |
| break; |
| } |
| } |
| |
| static bcmos_timer_rc dev_ctrl_boot_seq_timer_handler(bcmos_timer *timer, long data) |
| { |
| bcmos_bool is_bootloader_done; |
| bcmolt_devid device; |
| uint32_t ras_0_mode; |
| uint32_t ras_1_mode; |
| bcmos_errno rc = BCM_ERR_OK; |
| bcmos_timer_rc timer_rc = BCMOS_TIMER_STOP; |
| |
| device = (bcmolt_devid)data; |
| |
| if (dev_ctrl_db[device].boot_seq_info.polling_counter > 0) |
| { |
| dev_ctrl_db[device].boot_seq_info.polling_counter--; |
| is_bootloader_done = bcm_fld_is_bootloader_done(device); |
| if (is_bootloader_done) |
| { |
| /* Stop the boot timer */ |
| timer_rc = BCMOS_TIMER_STOP; |
| BCM_LOG(INFO, dev_ctrl_db[device].log_id, "Boot loader phase done.\n"); |
| |
| if (dev_ctrl_db[device].device_params.debug.avs_control) |
| { |
| rc = bcm_fld_set_avs_cont(device, BCM_FLD_AVS_CONT); /* don't stop the cpu when avs fails. */ |
| } |
| else |
| { |
| rc = bcm_fld_set_avs_cont(device, BCM_FLD_AVS_STOP); /* stop the cpu when avs fails. */ |
| } |
| BCM_DEV_CTRL_RETURN_ON_ERROR(device, rc, "bcm_fld_set_avs_cont\n"); |
| |
| dev_ctrl_get_ras_modes(dev_ctrl_db[device].device_params.system_mode, &ras_0_mode, &ras_1_mode); |
| rc = bcm_fld_set_ras_mode_set(device, 0, ras_0_mode); |
| BCM_DEV_CTRL_RETURN_ON_ERROR(device, rc, "bcm_fld_set_ras_0_mode_set\n"); |
| rc = bcm_fld_set_ras_mode_set(device, 1, ras_1_mode); |
| BCM_DEV_CTRL_RETURN_ON_ERROR(device, rc, "bcm_fld_set_ras_1_mode_set\n"); |
| rc = dev_ctrl_handle_application_image(device); |
| BCM_DEV_CTRL_RETURN_ON_ERROR(device, rc, "dev_ctrl_handle_application_image()\n"); |
| } |
| else |
| { |
| /* Restart the boot timer */ |
| timer_rc = BCMOS_TIMER_OK; |
| } |
| } |
| else |
| { |
| timer_rc = BCMOS_TIMER_STOP; |
| BCM_LOG(ERROR, dev_ctrl_db[device].log_id, "dev_ctrl_boot_seq timer expired while trying to burn BOOTLOADER\n"); |
| } |
| |
| return timer_rc; |
| } |
| |
| static bcmos_timer_rc dev_ctrl_ddr_test_timer_handler(bcmos_timer *timer, long data) |
| { |
| bcmos_bool is_ddr_test_done; |
| bcmolt_devid device; |
| bcmos_timer_rc timer_rc = BCMOS_TIMER_STOP; |
| |
| device = (bcmolt_devid)data; |
| |
| if (dev_ctrl_db[device].boot_seq_info.polling_counter > 0) |
| { |
| dev_ctrl_db[device].boot_seq_info.polling_counter--; |
| is_ddr_test_done = bcm_fld_is_ddr_test_done(device); |
| if (is_ddr_test_done) |
| { |
| /* Stop the boot timer */ |
| timer_rc = BCMOS_TIMER_STOP; |
| dev_ctrl_sm_call_state_cb(device, DEVICE_CONTROL_EVENT_DDR_TEST_COMPLETED, NULL); |
| } |
| else |
| { |
| BCM_LOG(INFO, dev_ctrl_db[device].log_id, "Waiting for DDR Test %u\n", dev_ctrl_db[device].boot_seq_info.polling_counter); |
| /* Restart the boot timer */ |
| timer_rc = BCMOS_TIMER_OK; |
| } |
| } |
| else |
| { |
| timer_rc = BCMOS_TIMER_STOP; |
| dev_ctrl_sm_call_state_cb(device, DEVICE_CONTROL_EVENT_DDR_TEST_TIMEOUT, NULL); |
| } |
| |
| return timer_rc; |
| } |
| #endif |
| |
| static bcmos_errno dev_ctrl_init_modules(bcmolt_devid device) |
| { |
| bcmos_module_parm module_params = {}; |
| bcmos_errno rc = BCM_ERR_OK; |
| dev_ctrl_db[device].module_info.module_id = MODULE_ID_FROM_DEVICE_ID(device); |
| snprintf( |
| dev_ctrl_db[device].module_info.name, |
| sizeof(dev_ctrl_db[device].module_info.name), |
| "dev_ctrl%u_module", |
| device); |
| module_params.qparm.name = dev_ctrl_db[device].module_info.name; |
| module_params.qparm.size = DEVICE_CONTROL_MSG_QUEUE_SIZE; |
| module_params.init = NULL; |
| rc = bcmos_module_create( |
| dev_ctrl_db[device].module_info.module_id, |
| &dev_ctrl_db[device].task_info.task, |
| &module_params); |
| if (rc != BCM_ERR_OK) |
| { |
| BCM_LOG(ERROR, dev_ctrl_db[device].log_id, |
| "bcmos_module_create returned error %s (%d), module id %d\n", |
| bcmos_strerror(rc), |
| rc, |
| dev_ctrl_db[device].module_info.module_id); |
| } |
| return rc; |
| } |
| |
| static bcmos_errno dev_ctrl_init_tasks(bcmolt_devid device) |
| { |
| bcmos_task_parm task_params = {}; |
| bcmos_errno rc = BCM_ERR_OK; |
| snprintf(dev_ctrl_db[device].task_info.name, sizeof(dev_ctrl_db[device].task_info.name), "dev_ctrl%u", device); |
| task_params.name = dev_ctrl_db[device].task_info.name; |
| task_params.priority = TASK_PRIORITY_DEVICE_CONTROL; |
| task_params.core = BCMOS_CPU_CORE_ANY; /* No CPU affinity */ |
| task_params.init_handler = NULL; |
| task_params.data = (long)device; |
| rc = bcmos_task_create(&dev_ctrl_db[device].task_info.task, &task_params); |
| BCM_DEV_CTRL_RETURN_ON_ERROR(device, rc, "bcmos_task_create()\n"); |
| |
| return rc; |
| } |
| |
| static void dev_ctrl_mod_msg_handler(bcmos_module_id module_id, bcmos_msg *os_msg) |
| { |
| bcmolt_msg *msg; |
| bcmolt_devid device; |
| |
| device = DEVICE_ID_FROM_MODULE_ID(module_id); |
| msg = os_msg->data; |
| |
| #ifndef IN_BAND |
| #ifdef ENABLE_LOG |
| if (msg->obj_type == BCMOLT_OBJ_ID_LOGGER && |
| msg->group == BCMOLT_MGT_GROUP_CFG && |
| msg->type == BCMOLT_MSG_TYPE_GET && |
| !dev_ctrl_is_connected(dev_ctrl_db[device].device_params.state)) |
| { |
| /* The device is not ready, but still we can fetch the logger file from SRAM. */ |
| msg->err = dev_ctrl_get_dev_log(device, (bcmolt_logger_cfg *)msg); |
| msg->dir = BCMOLT_MSG_DIR_RESPONSE; |
| bcmtrmux_control_to_host(device, msg); |
| bcmolt_msg_free(msg); |
| return; |
| } |
| #endif |
| |
| if (msg->obj_type == BCMOLT_OBJ_ID_SOFTWARE_ERROR && |
| msg->group == BCMOLT_MGT_GROUP_CFG && |
| (msg->type == BCMOLT_MSG_TYPE_GET || msg->type == BCMOLT_MSG_TYPE_GET_MULTI) && |
| !dev_ctrl_is_connected(dev_ctrl_db[device].device_params.state)) |
| { |
| /* get sw error table from SRAM */ |
| msg->err = dev_ctrl_sw_error_get(device, msg); |
| msg->dir = BCMOLT_MSG_DIR_RESPONSE; |
| bcmtrmux_control_to_host(device, msg); |
| bcmolt_msg_free(msg); |
| return; |
| } |
| #endif |
| |
| if (msg->obj_type == BCMOLT_OBJ_ID_DEBUG) |
| { |
| bcmolt_debug_ctrl_process_msg(device, msg, dev_ctrl_is_connected(dev_ctrl_db[device].device_params.state)); |
| return; |
| } |
| |
| if (msg->obj_type != BCMOLT_OBJ_ID_DEVICE) |
| { |
| bcmolt_msg_err( |
| msg, |
| DEV_LOG_INVALID_ID, |
| BCM_ERR_INTERNAL, |
| BCMOLT_ERR_FIELD_NONE, |
| "Can't access target when disconnected"); |
| msg->dir = BCMOLT_MSG_DIR_RESPONSE; |
| bcmtrmux_control_to_host(device, msg); |
| bcmolt_msg_free(msg); |
| } |
| else if (msg->group == BCMOLT_MGT_GROUP_AUTO) |
| { |
| dev_ctrl_process_msg(device, msg); |
| } |
| else if (msg->dir == BCMOLT_MSG_DIR_RESPONSE) |
| { |
| if (msg->group == BCMOLT_MGT_GROUP_OPER && msg->subgroup == BCMOLT_DEVICE_OPER_ID_HOST_KEEP_ALIVE) |
| { |
| /* Just free it, otherwise ignore keepalive oper responses */ |
| bcmolt_msg_free(msg); |
| } |
| else if ((dev_ctrl_db[device].device_params.state == BCMOLT_DEVICE_STATE_WAITING_FOR_DEVICE) || (dev_ctrl_db[device].device_params.state == BCMOLT_DEVICE_STATE_CONNECTING)) |
| { |
| dev_ctrl_process_msg(device, msg); |
| } |
| else |
| { |
| BCM_LOG(INFO, dev_ctrl_db[device].log_id, "received unexpected response: group=%u subgroup=%u\n", msg->group, msg->subgroup); |
| bcmolt_msg_free(msg); |
| } |
| } |
| else if (dev_ctrl_db[device].device_params.state != BCMOLT_DEVICE_STATE_CONNECTING) |
| { |
| msg->err = dev_ctrl_validate_msg(device, msg); |
| if (msg->err == BCM_ERR_OK) |
| { |
| dev_ctrl_process_msg(device, msg); |
| } |
| else |
| { |
| msg->dir = BCMOLT_MSG_DIR_RESPONSE; |
| bcmtrmux_control_to_host(device, msg); |
| bcmolt_msg_free(msg); |
| } |
| } |
| else |
| { |
| bcmolt_msg_err(msg, DEV_LOG_INVALID_ID, BCM_ERR_STATE, BCMOLT_ERR_FIELD_NONE, "Device is busy"); |
| msg->dir = BCMOLT_MSG_DIR_RESPONSE; |
| bcmtrmux_control_to_host(device, msg); |
| bcmolt_msg_free(msg); |
| } |
| } |
| |
| static void dev_ctrl_mod_msg_release(bcmos_msg *os_msg) |
| { |
| bcmolt_msg_free(container_of(os_msg, bcmolt_msg, os_msg)); |
| } |
| |
| static bcmos_msg dev_ctrl_ipc_msg = { .handler = dev_ctrl_mod_msg_handler, .release = dev_ctrl_mod_msg_release }; |
| |
| static void bcmdev_rx_handler(bcmolt_devid device, bcmolt_msg *msg, void *data) |
| { |
| bcmos_errno rc; |
| |
| msg->os_msg = dev_ctrl_ipc_msg; |
| msg->os_msg.data = msg; |
| rc = bcmos_msg_send_to_module(dev_ctrl_db[device].module_info.module_id, &msg->os_msg, BCMOS_MSG_SEND_AUTO_FREE); |
| if (rc != BCM_ERR_OK) |
| { |
| BCM_LOG(ERROR, dev_ctrl_db[device].log_id, |
| "bcmos_msg_send_to_module returned error %s (%d), module id %d\n", |
| bcmos_strerror(rc), |
| rc, |
| dev_ctrl_db[device].module_info.module_id); |
| } |
| } |
| |
| #ifndef IN_BAND |
| static bcmos_timer_rc exception_monitor_timer_handler(bcmos_timer *timer, long data) |
| { |
| bcmolt_devid device = (bcmolt_devid)data; |
| bcmos_errno rc; |
| |
| if (!dev_ctrl_is_connected(dev_ctrl_db[device].device_params.state)) |
| { |
| return BCM_ERR_OK; /* in case this happens during a disconnect/reset operation */ |
| } |
| |
| rc = dev_ctrl_get_exception_log_if_present(device); |
| if (rc != BCM_ERR_OK && rc != BCM_ERR_NOENT) |
| { |
| BCM_LOG(ERROR, dev_ctrl_db[device].log_id, "cant read Exception log for device %d error %s (%d)\n", device, bcmos_strerror(rc), rc); |
| } |
| return BCMOS_TIMER_OK; |
| } |
| #endif |
| |
| static bcmos_timer_rc device_response_timer_handler(bcmos_timer *timer, long data) |
| { |
| bcmolt_devid device = (bcmolt_devid)data; |
| dev_ctrl_sm_call_state_cb(device, DEVICE_CONTROL_EVENT_DEVICE_TIMER_TIMEOUT, NULL); |
| return BCMOS_TIMER_OK; |
| } |
| |
| static bcmos_timer_rc reset_delay_timer_handler(bcmos_timer *timer, long data) |
| { |
| bcmos_errno rc = dev_ctrl_params.host_reset_cb(); |
| if (rc != BCM_ERR_OK) |
| { |
| BCM_LOG(ERROR, dev_ctrl_db[(bcmolt_devid)data].log_id, "Host reset callback failed: %s (%d)\n", bcmos_strerror(rc), rc); |
| } |
| return BCMOS_TIMER_OK; |
| } |
| |
| static bcmos_errno dev_ctrl_timer_create( |
| bcmolt_devid device, |
| bcmos_module_id module_id, |
| dev_ctrl_timer *timer, |
| const char *name, |
| bcmos_bool periodic, |
| F_bcmos_timer_handler handler) |
| { |
| bcmos_timer_parm timer_params = {}; |
| bcmos_errno rc = BCM_ERR_OK; |
| |
| snprintf(timer->name, sizeof(timer->name), "dev_ctrl_%s_timer%u", name, device); |
| timer_params.name = timer->name; |
| timer_params.owner = module_id; |
| timer_params.periodic = periodic; |
| timer_params.handler = handler; |
| timer_params.data = device; |
| rc = bcmos_timer_create(&timer->timer, &timer_params); |
| if (rc != BCM_ERR_OK) |
| { |
| BCM_LOG(ERROR, dev_ctrl_db[device].log_id, |
| "'%s' timer creation failed, bcmos_timer_create returned error %s (%d)\n", |
| timer->name, |
| bcmos_strerror(rc), |
| rc); |
| } |
| |
| return rc; |
| } |
| |
| #ifndef IN_BAND |
| static void dev_ctrl_link_up_down_cb(uint8_t dev_id, bcm_ll_pcie_link_status status) |
| { |
| BCM_LOG(INFO, dev_ctrl_db[dev_id].log_id, "Device %u: PCIe link %s\n", dev_id, status == BCM_LL_PCIE_LINK_DOWN ? "down" : "up"); |
| } |
| #endif |
| |
| static bcmtrmux_msg_dest dev_ctrl_msg_filter_cb( |
| bcmolt_devid device, |
| bcmolt_obj_id obj, |
| bcmolt_mgt_group group, |
| uint16_t subgroup) |
| { |
| /* Device control should intercept the message if one of the two happens: |
| * 1. The message object is "device". |
| * 2. The message object is not "device", but the device is not ready, so an appropriate error should be returned to |
| * the host application. */ |
| bcmtrmux_msg_dest dest = BCMTRMUX_DEST_REMOTE; |
| |
| if (!dev_ctrl_is_connected(dev_ctrl_db[device].device_params.state)) |
| { |
| dest = BCMTRMUX_DEST_LOCAL; |
| } |
| else if (obj == BCMOLT_OBJ_ID_DEBUG) |
| { |
| dest = BCMTRMUX_DEST_LOCAL; |
| } |
| else if (obj == BCMOLT_OBJ_ID_DEVICE) |
| { |
| switch (group) |
| { |
| case BCMOLT_MGT_GROUP_OPER: |
| switch (subgroup) |
| { |
| case BCMOLT_DEVICE_OPER_ID_IMAGE_TRANSFER_START: |
| case BCMOLT_DEVICE_OPER_ID_IMAGE_TRANSFER_DATA: |
| case BCMOLT_DEVICE_OPER_ID_SW_UPGRADE_ACTIVATE: |
| return BCMTRMUX_DEST_REMOTE; |
| default: |
| return BCMTRMUX_DEST_LOCAL; |
| } |
| break; |
| default: |
| return BCMTRMUX_DEST_LOCAL; |
| } |
| } |
| return dest; |
| } |
| |
| static void dev_ctrl_init_device_objects(bcmolt_devid device) |
| { |
| dev_ctrl_clear_device_cfg(device); |
| dev_ctrl_init_connection_state(device); |
| dev_ctrl_db[device].device_params.state = BCMOLT_DEVICE_STATE_DISCONNECTED; |
| dev_ctrl_db[device].device_params.system_mode = BCMOLT_SYSTEM_MODE__NUM_OF; |
| dev_ctrl_db[device].boot_seq_info.polling_counter = BOOT_SEQ_POLLING_MAX_NUMBER; |
| dev_ctrl_db[device].boot_seq_info.polling_interval = BOOT_SEQ_POLL_INTERVAL_SEC * BCMOS_MICROSECONDS_IN_SECONDS; |
| dev_ctrl_db[device].corr_tag = 0; |
| #ifdef ENABLE_LOG |
| dev_ctrl_db[device].sram_log_offset = 0; |
| dev_ctrl_db[device].msgs_read = 0; |
| #endif |
| |
| #ifndef SIMULATION_BUILD |
| dev_ctrl_db[device].trx_disable_mask = 0xFFFF; /* Mark all PONs with TRX disabled. */ |
| #endif |
| |
| dev_ctrl_db[device].conn_fail_reason = BCMOLT_HOST_CONNECTION_FAIL_REASON_NONE; |
| dev_ctrl_db[device].last_event = DEVICE_CONTROL_EVENT_NO_EVENT; |
| } |
| |
| bcmos_errno bcmolt_dev_ctrl_host_event_write(uint32_t device, uint32_t event) |
| { |
| #ifndef IN_BAND |
| bcmolt_device_state state = dev_ctrl_db[device].device_params.state; |
| #endif |
| #ifndef SIMULATION_BUILD |
| /* Update local database, so that after reset we will be able to sync this value with the device. */ |
| dev_ctrl_db[device].trx_disable_mask = event; |
| #endif |
| #ifndef IN_BAND |
| /* Notify device using SRAM, but only if we are already connected (otherwise FLD might be not ready). */ |
| if (dev_ctrl_is_connected(state)) |
| bcm_fld_set_host_event(device, event); |
| #endif |
| return BCM_ERR_OK; |
| } |
| /*Use different function signatures linux kernel device control and other*/ |
| #if !defined(LINUX_USER_SPACE) |
| bcmos_errno bcmolt_dev_ctrl_init(bcmolt_dev_ctrl_params *params) |
| #else |
| bcmos_errno bcmolt_dev_ctrl_init() |
| #endif |
| { |
| bcmolt_devid device; |
| bcmos_errno rc = BCM_ERR_OK; |
| |
| #if !defined(LINUX_USER_SPACE) |
| if (!params->system_mode_validate_cb || |
| !params->image_read_cb || |
| !params->device_off_cb || |
| !params->device_on_cb || |
| !params->device_is_running_cb || |
| !params->host_reset_cb || |
| !params->pcie_channel_prepare_cb || |
| !params->pcie_channel_remove_cb) |
| { |
| BCMOS_TRACE_ERR("Missing required callbacks\n"); |
| return BCM_ERR_PARM; |
| } |
| |
| dev_ctrl_params = *params; |
| #endif |
| rc = bcmtrmux_init(dev_ctrl_msg_filter_cb); |
| BCMOS_TRACE_CHECK_RETURN(rc, rc, "bcmtrmux_init()\n"); |
| |
| #ifndef IN_BAND |
| rc = bcm_fld_init(BCMTR_MAX_OLTS); |
| BCMOS_TRACE_CHECK_RETURN(rc, rc, "bcm_fld_init()\n"); |
| #endif |
| |
| for (device = 0; device < BCMTR_MAX_OLTS; device++) |
| { |
| #ifdef ENABLE_LOG |
| char log_name[MAX_DEV_LOG_ID_NAME]; |
| snprintf(log_name, sizeof(log_name) - 1, "dev_ctrl_%u", device); |
| dev_ctrl_db[device].log_id = bcm_dev_log_id_register(log_name, DEV_LOG_LEVEL_INFO, DEV_LOG_ID_TYPE_BOTH); |
| #endif |
| dev_ctrl_init_device_objects(device); |
| image_buf[device] = bcmos_alloc(IMAGE_BUF_SIZE); |
| if (!image_buf[device]) |
| { |
| BCM_LOG(ERROR, dev_ctrl_db[device].log_id, "Can't allocate packet buffer\n"); |
| return BCM_ERR_NOMEM; |
| } |
| rd_image_buf[device] = bcmos_alloc(IMAGE_BUF_SIZE); |
| if (!rd_image_buf[device]) |
| { |
| BCM_LOG(ERROR, dev_ctrl_db[device].log_id, "Can't allocate nh_packet buffer\n"); |
| return BCM_ERR_NOMEM; |
| } |
| rc = dev_ctrl_init_tasks(device); |
| BCM_DEV_CTRL_RETURN_ON_ERROR(device, rc, "dev_ctrl_init_tasks()\n"); |
| |
| rc = dev_ctrl_init_modules(device); |
| BCM_DEV_CTRL_RETURN_ON_ERROR(device, rc, "dev_ctrl_init_modules()\n"); |
| |
| dev_ctrl_db[device].ka_info.orig = BCM_KEEP_ALIVE_MSG_ORIG_HOST; |
| dev_ctrl_db[device].ka_info.send_handler = dev_ctrl_ka_tx_handler; |
| dev_ctrl_db[device].ka_info.disconnect_handler = dev_ctrl_ka_disconnect_handler; |
| dev_ctrl_db[device].ka_info.device = device; |
| rc = create_keep_alive_timer(&dev_ctrl_db[device].ka_info, dev_ctrl_db[device].module_info.module_id); |
| BCM_DEV_CTRL_RETURN_ON_ERROR(device, rc, "create_keep_alive_timer()\n"); |
| |
| rc = dev_ctrl_timer_create( |
| device, |
| dev_ctrl_db[device].module_info.module_id, |
| &dev_ctrl_db[device].connection_info.timer, |
| "connection", |
| BCMOS_FALSE, /* non-periodic */ |
| dev_ctrl_connection_timer_handler); |
| BCMOS_CHECK_RETURN_ERROR(rc, rc); |
| |
| #ifndef IN_BAND |
| rc = dev_ctrl_timer_create( |
| device, |
| dev_ctrl_db[device].module_info.module_id, |
| &dev_ctrl_db[device].boot_seq_info.timer, |
| "boot_seq", |
| BCMOS_TRUE, /* periodic */ |
| dev_ctrl_boot_seq_timer_handler); |
| BCMOS_CHECK_RETURN_ERROR(rc, rc); |
| |
| rc = dev_ctrl_timer_create( |
| device, |
| dev_ctrl_db[device].module_info.module_id, |
| &dev_ctrl_db[device].exception_monitor_timer, |
| "exception_monitor", |
| BCMOS_TRUE, /* periodic */ |
| exception_monitor_timer_handler); |
| BCMOS_CHECK_RETURN_ERROR(rc, rc); |
| #endif |
| rc = dev_ctrl_timer_create( |
| device, |
| dev_ctrl_db[device].module_info.module_id, |
| &dev_ctrl_db[device].device_response_timer, |
| "device_response", |
| BCMOS_FALSE, /* non-periodic */ |
| device_response_timer_handler); |
| BCMOS_CHECK_RETURN_ERROR(rc, rc); |
| |
| rc = dev_ctrl_timer_create( |
| device, |
| dev_ctrl_db[device].module_info.module_id, |
| &dev_ctrl_db[device].reset_delay_timer, |
| "reset_delay", |
| BCMOS_FALSE, /* non-periodic */ |
| reset_delay_timer_handler); |
| BCMOS_CHECK_RETURN_ERROR(rc, rc); |
| |
| /* Register callback to MUX */ |
| rc = bcmtrmux_local_handler_register(device, bcmdev_rx_handler, (void *)(long)device); |
| BCM_DEV_CTRL_RETURN_ON_ERROR(device, rc, "bcmtrmux_local_handler_register()\n"); |
| |
| /* Intercept CONNECTION_ESTABLISHED, DEVICE_READY, KEEP_ALIVE messages */ |
| rc = bcmtrmux_control_auto_intercept_filter( |
| device, |
| BCMOLT_OBJ_ID_DEVICE, |
| BCMOLT_DEVICE_AUTO_ID_CONNECTION_ESTABLISHED); |
| rc = rc ? rc : bcmtrmux_control_auto_intercept_filter( |
| device, |
| BCMOLT_OBJ_ID_DEVICE, |
| BCMOLT_DEVICE_AUTO_ID_DEVICE_READY); |
| rc = rc ? rc : bcmtrmux_control_auto_intercept_filter( |
| device, |
| BCMOLT_OBJ_ID_DEVICE, |
| BCMOLT_DEVICE_AUTO_ID_DEVICE_KEEP_ALIVE); |
| BCM_DEV_CTRL_RETURN_ON_ERROR(device, rc, "bcmtrmux_control_auto_intercept_filter()\n"); |
| } |
| #ifndef IN_BAND |
| /* Register for link_up / link_down interrupt */ |
| bcm_ll_pcie_status_change_register(dev_ctrl_link_up_down_cb); |
| #endif |
| |
| bcmolt_debug_ctrl_init(); |
| |
| return rc; |
| } |
| |
| void bcmolt_dev_ctrl_exit(void) |
| { |
| #ifndef IN_BAND |
| bcmolt_devid device; |
| |
| for (device = 0; device < BCMTR_MAX_OLTS; device++) |
| { |
| bcmtrmux_local_handler_unregister(device); |
| bcmos_free(image_buf[device]); |
| bcmos_free(rd_image_buf[device]); |
| bcmos_timer_destroy(&dev_ctrl_db[device].connection_info.timer.timer); |
| bcmos_timer_destroy(&dev_ctrl_db[device].ka_info.ka_timer.timer); |
| bcmos_timer_destroy(&dev_ctrl_db[device].boot_seq_info.timer.timer); |
| bcmos_timer_destroy(&dev_ctrl_db[device].exception_monitor_timer.timer); |
| bcmos_timer_destroy(&dev_ctrl_db[device].device_response_timer.timer); |
| bcmos_timer_destroy(&dev_ctrl_db[device].reset_delay_timer.timer); |
| bcmos_module_destroy(dev_ctrl_db[device].module_info.module_id); |
| bcmos_task_destroy(&dev_ctrl_db[device].task_info.task); |
| } |
| #endif |
| #ifndef IN_BAND |
| bcm_fld_exit(); |
| #endif |
| bcmtrmux_exit(); |
| #ifndef IN_BAND |
| bcm_ll_pcie_status_change_unregister(); |
| #endif |
| } |