blob: 2ddfa8dd4b5c0e5497bd95af44dc51fe8409dfb3 [file] [log] [blame]
/*
<: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
}