blob: 4b092a03a408f32f813850b88cef893e19870fc0 [file] [log] [blame]
/******************************************************************************
*
* <:copyright-BRCM:2016:DUAL/GPL:standard
*
* Copyright (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 <bcm_dev_log.h>
#include <bcmolt_math.h>
#include "bcm_topo.h"
#define BCM_TOPO_MAX_LINE_SIZE 256
#define BCM_TOPO_MAX_NNI_DEVICES 1
uint32_t g_max_nni_ports[BCM_TOPO_MAX_NNI_DEVICES] = {BCM_TOPO_MAX_NNI_PORTS};
typedef struct bcm_topo_dev_context_t bcm_topo_dev_context_t;
typedef struct
{
bcm_topo_dev_context_t *dev; /* Back pointer to the physical device this PON belongs to */
bcmos_bool is_initialized;
uint32_t pon_id;
uint32_t logical_pon;
bcm_topo_pon_mode pon_mode;
uint32_t max_num_of_onus;
void *contexts[BCM_TOPO_PON_CONTEXT_ID__NUM_OF]; /* User context - 1 entry per user */
} bcm_topo_pon_context_t;
struct bcm_topo_dev_context_t
{
bcm_topo_pon_mode pon_mode; /* Currently we do not allow more than one PON mode per device (e.g: GPONx8 + XGPONx4) */
bcmos_bool is_initialized;
uint32_t device_id;
uint32_t max_num_of_pons;
bcm_topo_pon_context_t pons[BCM_TOPO_MAX_NUM_OF_PONS_PER_DEV];
};
typedef struct
{
bcmos_bool is_initialized;
bcm_topo_dev_context_t devs[BCM_TOPO_MAX_NUM_OF_DEVS];
bcm_topo_pon_context_t *logical_pon2physical_pon[BCM_TOPO_MAX_NUM_OF_LOGICAL_PONS];
} bcm_topo_context_t;
static bcm_topo_context_t bcm_topo_context;
#ifdef ENABLE_LOG
static dev_log_id topo_log_id = DEV_LOG_INVALID_ID;
#endif
static int2str_t pon_mode2str[] =
{
{BCM_TOPO_PON_MODE_GPON, "gpon"},
{BCM_TOPO_PON_MODE_XGPON, "xgpon"},
{BCM_TOPO_PON_MODE_XGS, "xgs"},
{BCM_TOPO_PON_MODE_EPON_TDMA, "epon_tdma"},
{BCM_TOPO_PON_MODE_EPON_1G, "epon_1g"},
{BCM_TOPO_PON_MODE_EPON_10G, "epon_10g"},
{-1},
};
static int2int_t pon_mode2max_num_of_pons[] =
{
{BCM_TOPO_PON_MODE_GPON, 16},
{BCM_TOPO_PON_MODE_XGPON, 8},
{BCM_TOPO_PON_MODE_XGS, 2},
{BCM_TOPO_PON_MODE_EPON_TDMA, 8},
{BCM_TOPO_PON_MODE_EPON_1G, 16},
{BCM_TOPO_PON_MODE_EPON_10G, 8},
{-1},
};
const char *bcm_topo_dev_get_pon_mode_str(bcmolt_devid device_id)
{
return int2str(pon_mode2str, bcm_topo_context.devs[device_id].pon_mode);
}
int bcm_topo_dev_get_max_pon(bcmolt_devid device_id)
{
return int2int(pon_mode2max_num_of_pons, bcm_topo_context.devs[device_id].pon_mode);
}
bcm_topo_pon_mode bcm_topo_pon_get_pon_mode(uint32_t pon)
{
bcmolt_devid device_id;
uint32_t physical_pon;
bcmos_errno rc;
rc = bcm_topo_pon_get_logical2physical(pon, &device_id, &physical_pon);
if (rc != BCM_ERR_OK)
return BCM_TOPO_PON_MODE_INVALID;
return bcm_topo_context.devs[device_id].pons[physical_pon].pon_mode;
}
bcm_topo_pon_family bcm_topo_pon_get_pon_family(uint32_t pon)
{
switch (bcm_topo_pon_get_pon_mode(pon))
{
case BCM_TOPO_PON_MODE_GPON:
case BCM_TOPO_PON_MODE_XGPON:
case BCM_TOPO_PON_MODE_XGS:
return BCM_TOPO_PON_FAMILY_GPON;
case BCM_TOPO_PON_MODE_EPON_TDMA:
case BCM_TOPO_PON_MODE_EPON_1G:
case BCM_TOPO_PON_MODE_EPON_10G:
return BCM_TOPO_PON_FAMILY_EPON;
default:
return BCM_TOPO_PON_FAMILY_INVALID;
}
}
bcm_topo_pon_sub_family bcm_topo_pon_get_pon_sub_family(uint32_t pon)
{
switch (bcm_topo_pon_get_pon_mode(pon))
{
case BCM_TOPO_PON_MODE_GPON:
return BCM_TOPO_PON_SUB_FAMILY_GPON;
case BCM_TOPO_PON_MODE_XGPON:
case BCM_TOPO_PON_MODE_XGS:
return BCM_TOPO_PON_SUB_FAMILY_XGPON;
case BCM_TOPO_PON_MODE_EPON_TDMA:
case BCM_TOPO_PON_MODE_EPON_1G:
case BCM_TOPO_PON_MODE_EPON_10G:
return BCM_TOPO_PON_SUB_FAMILY_EPON;
default:
return BCM_TOPO_PON_SUB_FAMILY_INVALID;
}
}
uint32_t bcm_topo_pon_get_max_num_of_onus(uint32_t pon)
{
bcmolt_devid device_id;
uint32_t physical_pon;
bcmos_errno rc;
rc = bcm_topo_pon_get_logical2physical(pon, &device_id, &physical_pon);
if (rc != BCM_ERR_OK)
return BCM_TOPO_ERR_INVALID;
return bcm_topo_context.devs[device_id].pons[physical_pon].max_num_of_onus;
}
bcmos_bool bcm_topo_pon_is_valid(uint32_t pon)
{
bcmolt_devid device_id;
uint32_t physical_pon;
if (bcm_topo_pon_get_logical2physical(pon, &device_id, &physical_pon) != BCM_ERR_OK)
return BCMOS_FALSE;
return BCMOS_TRUE;
}
bcmolt_devid bcm_topo_dev_get_next(bcmolt_devid device_id)
{
if (device_id == BCM_TOPO_DEV_INVALID)
device_id = 0;
else
device_id++;
for (; device_id < BCM_TOPO_MAX_NUM_OF_DEVS && !bcm_topo_context.devs[device_id].is_initialized; device_id++);
return device_id == BCM_TOPO_MAX_NUM_OF_DEVS ? BCM_TOPO_DEV_INVALID : device_id;
}
uint32_t bcm_topo_pon_get_next(bcmolt_devid device_id, uint32_t pon)
{
uint32_t physical_pon;
bcmos_errno rc;
if (device_id >= BCM_TOPO_MAX_NUM_OF_DEVS)
{
BCM_LOG(ERROR, topo_log_id, "Device ID must be in the range 0 .. %u\n", BCM_TOPO_MAX_NUM_OF_DEVS - 1);
return BCM_TOPO_PON_INVALID;
}
if (pon == BCM_TOPO_PON_INVALID)
physical_pon = 0;
else
{
bcmolt_devid dummy;
rc = bcm_topo_pon_get_logical2physical(pon, &dummy, &physical_pon);
if (rc != BCM_ERR_OK)
{
return BCM_TOPO_PON_INVALID;
}
else
{
physical_pon++;
}
}
if (physical_pon < bcm_topo_context.devs[device_id].max_num_of_pons)
{
rc = bcm_topo_pon_get_physical2logical(device_id, physical_pon, &pon);
if (rc != BCM_ERR_OK)
{
return BCM_TOPO_PON_INVALID;
}
else
{
return pon;
}
}
return BCM_TOPO_PON_INVALID;
}
bcmos_errno bcm_topo_pon_get_logical2physical(uint32_t logical_pon, bcmolt_devid *device_id, uint32_t *physical_pon)
{
if (logical_pon >= BCM_TOPO_MAX_NUM_OF_LOGICAL_PONS)
{
BCM_LOG(ERROR, topo_log_id, "Logical PON ID must be in the range 0 .. %u\n", BCM_TOPO_MAX_NUM_OF_LOGICAL_PONS - 1);
return BCM_ERR_RANGE;
}
if (!bcm_topo_context.logical_pon2physical_pon[logical_pon])
{
BCM_LOG(ERROR, topo_log_id, "Logical PON=%u was not associated with a physical PON\n", logical_pon);
return BCM_ERR_RANGE;
}
*physical_pon = bcm_topo_context.logical_pon2physical_pon[logical_pon]->pon_id;
*device_id = bcm_topo_context.logical_pon2physical_pon[logical_pon]->dev->device_id;
return BCM_ERR_OK;
}
bcmos_errno bcm_topo_pon_get_physical2logical(bcmolt_devid device_id, uint32_t physical_pon, uint32_t *logical_pon)
{
if (device_id >= BCM_TOPO_MAX_NUM_OF_DEVS)
{
BCM_LOG(ERROR, topo_log_id, "Device ID must be in the range 0 .. %u\n", BCM_TOPO_MAX_NUM_OF_DEVS - 1);
return BCM_ERR_RANGE;
}
if (physical_pon >= bcm_topo_context.devs[device_id].max_num_of_pons)
{
BCM_LOG(ERROR, topo_log_id, "Physical PON ID must be in the range 0 .. %u\n", bcm_topo_context.devs[device_id].max_num_of_pons - 1);
return BCM_ERR_RANGE;
}
if (bcm_topo_context.devs[device_id].pons[physical_pon].logical_pon == BCM_TOPO_PON_INVALID)
{
BCM_LOG(ERROR, topo_log_id, "Physical PON=%u on device=%u was not associated with a logical PON\n", physical_pon, device_id);
return BCM_ERR_RANGE;
}
*logical_pon = bcm_topo_context.devs[device_id].pons[physical_pon].logical_pon;
return BCM_ERR_OK;
}
bcmos_errno bcm_topo_pon_set_context(uint32_t pon, bcm_topo_pon_context_id pon_context_id, void *context)
{
bcmolt_devid device_id;
uint32_t physical_pon;
bcmos_errno rc;
if (pon_context_id >= BCM_TOPO_PON_CONTEXT_ID__NUM_OF)
{
BCM_LOG(ERROR, topo_log_id, "Invalid PON context ID\n");
return BCM_ERR_RANGE;
}
rc = bcm_topo_pon_get_logical2physical(pon, &device_id, &physical_pon);
if (rc != BCM_ERR_OK)
return rc;
bcm_topo_context.devs[device_id].pons[physical_pon].contexts[pon_context_id] = context;
return BCM_ERR_OK;
}
void *bcm_topo_pon_get_context(uint32_t pon, bcm_topo_pon_context_id pon_context_id)
{
bcmolt_devid device_id;
uint32_t physical_pon;
bcmos_errno rc;
if (pon_context_id >= BCM_TOPO_PON_CONTEXT_ID__NUM_OF)
{
BCM_LOG(ERROR, topo_log_id, "Invalid PON context ID\n");
return NULL;
}
rc = bcm_topo_pon_get_logical2physical(pon, &device_id, &physical_pon);
if (rc != BCM_ERR_OK)
return NULL;
return bcm_topo_context.devs[device_id].pons[physical_pon].contexts[pon_context_id];
}
static void bcm_topo_init_context(void)
{
uint32_t device_id;
for (device_id = 0; device_id < BCM_TOPO_MAX_NUM_OF_DEVS; device_id++)
{
bcm_topo_dev_context_t *dev;
uint32_t pon_id;
dev = &bcm_topo_context.devs[device_id];
dev->device_id = device_id;
dev->pon_mode = BCM_TOPO_PON_MODE_INVALID;
for (pon_id = 0; pon_id < BCM_TOPO_MAX_NUM_OF_PONS_PER_DEV; pon_id++)
{
bcm_topo_pon_context_t *pon;
pon = &dev->pons[pon_id];
pon->dev = dev;
pon->pon_id = pon_id;
pon->pon_mode = BCM_TOPO_PON_MODE_INVALID;
pon->logical_pon = BCM_TOPO_PON_INVALID;
}
}
}
static bcmos_errno bcm_topo_init_line_parse(const char *line, const char *filename, uint32_t line_num, bcmos_bool *is_skipped, uint32_t *logical_pon, bcm_topo_pon_mode *pon_mode,
uint32_t *device_id, uint32_t *physical_pon)
{
int rc;
char logical_pon_str[BCM_TOPO_MAX_LINE_SIZE];
char pon_mode_str[BCM_TOPO_MAX_LINE_SIZE];
char device_id_str[BCM_TOPO_MAX_LINE_SIZE];
char physical_pon_str[BCM_TOPO_MAX_LINE_SIZE];
int2str_t *p;
/* Skip blank lines and comments. */
if (!*line || *line == '\n' || *line == '#')
{
*is_skipped = BCMOS_TRUE;
return BCM_ERR_OK;
}
*is_skipped = BCMOS_FALSE;
/* Read the tokens separated by commas. */
rc = sscanf(line, "%[^,],%*[ ]%[^,],%[^,],%[^\n]", logical_pon_str, pon_mode_str, device_id_str, physical_pon_str);
if (rc < 4)
{
BCM_LOG(ERROR, topo_log_id, "Error parsing line %s:%u (rc=%u)\n", filename, line_num, rc);
return BCM_ERR_PARSE;
}
if (sscanf(logical_pon_str, "%u", logical_pon) < 1)
{
BCM_LOG(ERROR, topo_log_id, "Error parsing line %s:%u (rc=%u)\n", filename, line_num, rc);
return BCM_ERR_PARSE;
}
if (sscanf(device_id_str, "%u", device_id) < 1)
{
BCM_LOG(ERROR, topo_log_id, "Error parsing line %s:%u (rc=%u)\n", filename, line_num, rc);
return BCM_ERR_PARSE;
}
if (sscanf(physical_pon_str, "%u", physical_pon) < 1)
{
BCM_LOG(ERROR, topo_log_id, "Error parsing line %s:%u (rc=%u)\n", filename, line_num, rc);
return BCM_ERR_PARSE;
}
BCM_LOG(INFO, topo_log_id, "Map Logical PON ID=%u -> (Physical Device ID=%u, Physical Pon ID=%u, PON mode='%s')\n", *logical_pon, *device_id, *physical_pon, pon_mode_str);
for (p = pon_mode2str; p->from != -1 && strcmp(pon_mode_str, p->to); p++);
if (p->from == -1)
{
BCM_LOG(ERROR, topo_log_id, "Error parsing PON mode at %s:%u\n", filename, line_num);
return BCM_ERR_PARSE;
}
*pon_mode = p->from;
return BCM_ERR_OK;
}
static void bcm_topo_init_dev(bcm_topo_dev_context_t *dev, bcm_topo_pon_mode pon_mode)
{
dev->pon_mode = pon_mode;
dev->max_num_of_pons = int2int(pon_mode2max_num_of_pons, pon_mode);
}
static void bcm_topo_init_pon(bcm_topo_pon_context_t *pon, bcm_topo_pon_mode pon_mode)
{
pon->pon_mode = pon_mode;
switch (pon_mode)
{
case BCM_TOPO_PON_MODE_GPON:
pon->max_num_of_onus = 128;
break;
case BCM_TOPO_PON_MODE_XGPON:
case BCM_TOPO_PON_MODE_XGS:
pon->max_num_of_onus = 256;
break;
case BCM_TOPO_PON_MODE_EPON_TDMA:
case BCM_TOPO_PON_MODE_EPON_1G:
case BCM_TOPO_PON_MODE_EPON_10G:
pon->max_num_of_onus = 0; /* There is no "ONU" in EPON, but LLIDs. */
break;
default:
break;
}
}
static bcmos_errno bcm_topo_init_by_file(FILE *fp, const char *filename)
{
char line[BCM_TOPO_MAX_LINE_SIZE];
uint32_t line_num;
/* Read next line. */
line_num = 1;
while (fgets(line, sizeof(line), fp))
{
bcmos_bool is_skipped;
uint32_t logical_pon, device_id, physical_pon;
bcm_topo_pon_mode pon_mode;
bcm_topo_dev_context_t *dev;
bcm_topo_pon_context_t *pon;
if (bcm_topo_init_line_parse(line, filename, line_num, &is_skipped, &logical_pon, &pon_mode, &device_id, &physical_pon) != BCM_ERR_OK)
return BCM_ERR_PARSE;
if (is_skipped)
{
line_num++;
continue;
}
if (logical_pon >= BCM_TOPO_MAX_NUM_OF_LOGICAL_PONS)
{
BCM_LOG(ERROR, topo_log_id, "Logical PON ID at %s:%u must be in the range 0 .. %u\n", filename, line_num, BCM_TOPO_MAX_NUM_OF_LOGICAL_PONS - 1);
return BCM_ERR_RANGE;
}
if (bcm_topo_context.logical_pon2physical_pon[logical_pon])
{
BCM_LOG(ERROR, topo_log_id, "Logical PON ID at %s:%u has already been set before\n", filename, line_num);
return BCM_ERR_ALREADY;
}
if (device_id >= BCM_TOPO_MAX_NUM_OF_DEVS)
{
BCM_LOG(ERROR, topo_log_id, "Physical device ID at %s:%u must be in the range 0 .. %u\n", filename, line_num, BCM_TOPO_MAX_NUM_OF_DEVS - 1);
return BCM_ERR_RANGE;
}
dev = &bcm_topo_context.devs[device_id];
if (!dev->is_initialized)
{
bcm_topo_init_dev(dev, pon_mode);
dev->is_initialized = BCMOS_TRUE;
}
else if (dev->pon_mode != pon_mode)
{
BCM_LOG(ERROR, topo_log_id, "PON mode at %s:%u conflicts with PON mode='%s' that has already been set for this device\n", filename, line_num,
int2str(pon_mode2str, dev->pon_mode));
return BCM_ERR_NOT_SUPPORTED;
}
if (physical_pon >= dev->max_num_of_pons)
{
BCM_LOG(ERROR, topo_log_id, "Physical PON ID at %s:%u must be in the range 0 .. %u\n", filename, line_num, dev->max_num_of_pons - 1);
return BCM_ERR_RANGE;
}
pon = &bcm_topo_context.devs[device_id].pons[physical_pon];
if (!pon->is_initialized)
{
bcm_topo_init_pon(pon, pon_mode);
pon->is_initialized = BCMOS_TRUE;
}
else
{
BCM_LOG(ERROR, topo_log_id, "Physical PON ID at %s:%u has already been set before\n", filename, line_num);
return BCM_ERR_ALREADY;
}
bcm_topo_context.logical_pon2physical_pon[logical_pon] = &bcm_topo_context.devs[device_id].pons[physical_pon];
bcm_topo_context.devs[device_id].pons[physical_pon].logical_pon = logical_pon;
line_num++;
}
return BCM_ERR_OK;
}
static bcmos_errno bcm_topo_init_by_args(bcm_topo_params *params)
{
uint32_t device_id;
uint32_t max_num_of_pons_per_dev;
if (params->num_of_devs > BCM_TOPO_MAX_NUM_OF_DEVS)
{
BCM_LOG(ERROR, topo_log_id, "Number of devices must be in the range 0 .. %u\n", BCM_TOPO_MAX_NUM_OF_DEVS);
return BCM_ERR_RANGE;
}
max_num_of_pons_per_dev = int2int(pon_mode2max_num_of_pons, params->pon_mode);
if (params->num_of_pons_per_dev > max_num_of_pons_per_dev)
{
BCM_LOG(ERROR, topo_log_id, "Number of PONs per device for PON mode '%s' must be in the range 0 .. %u\n", int2str(pon_mode2str, params->pon_mode), BCM_TOPO_MAX_NUM_OF_DEVS);
return BCM_ERR_RANGE;
}
for (device_id = 0; device_id < params->num_of_devs; device_id++)
{
uint32_t physical_pon;
bcm_topo_dev_context_t *dev;
bcm_topo_pon_context_t *pon;
dev = &bcm_topo_context.devs[device_id];
bcm_topo_init_dev(dev, params->pon_mode);
dev->is_initialized = BCMOS_TRUE;
for (physical_pon = 0; physical_pon < params->num_of_pons_per_dev; physical_pon++)
{
uint32_t logical_pon;
logical_pon = (device_id * params->num_of_pons_per_dev) + physical_pon;
BCM_LOG(INFO, topo_log_id, "Map Logical PON ID=%u -> (Physical Device ID=%u, Physical pon ID=%u, PON mode='%s')\n", logical_pon, device_id, physical_pon,
int2str(pon_mode2str, params->pon_mode));
pon = &bcm_topo_context.devs[device_id].pons[physical_pon];
bcm_topo_init_pon(pon, params->pon_mode);
pon->is_initialized = BCMOS_TRUE;
bcm_topo_context.logical_pon2physical_pon[logical_pon] = &bcm_topo_context.devs[device_id].pons[physical_pon];
bcm_topo_context.devs[device_id].pons[physical_pon].logical_pon = logical_pon;
}
}
return BCM_ERR_OK;
}
bcmos_errno bcm_topo_init(bcm_topo_params *params, const char *topo_filename)
{
bcmos_errno ret;
FILE *topo_fp;
#ifdef ENABLE_LOG
if (topo_log_id == DEV_LOG_INVALID_ID)
{
topo_log_id = bcm_dev_log_id_register("TOPOLOGY", DEV_LOG_LEVEL_INFO, DEV_LOG_ID_TYPE_BOTH);
BUG_ON(topo_log_id == DEV_LOG_INVALID_ID);
}
#endif
bcm_topo_init_context();
topo_fp = fopen(topo_filename, "r");
if (topo_fp)
{
BCM_LOG(INFO, topo_log_id, "Topology is taken from file\n");
ret = bcm_topo_init_by_file(topo_fp, topo_filename);
fclose(topo_fp);
}
else if (params)
{
BCM_LOG(INFO, topo_log_id, "Topology is taken from arguments\n");
ret = bcm_topo_init_by_args(params);
}
else
{
BCM_LOG(INFO, topo_log_id, "At least one of topo_fp and params must be specified and exist\n");
ret = BCM_ERR_PARM;
}
if (ret != BCM_ERR_OK)
goto exit;
bcm_topo_context.is_initialized = BCMOS_TRUE;
ret = BCM_ERR_OK;
exit:
return ret;
}
bcmos_bool bcm_topo_is_initialized(void)
{
return bcm_topo_context.is_initialized;
}
bcmos_bool bcm_topo_dev_set_max_nni(bcmolt_devid device_id, uint32_t num_nni_ports)
{
if(device_id >= BCM_TOPO_MAX_NNI_DEVICES)
{
return BCM_ERR_PARM;
}
/* make sure the max number does not exceed the allocated resrouce */
if( num_nni_ports > BCM_TOPO_MAX_NNI_PORTS)
{
return BCM_ERR_PARM;
}
g_max_nni_ports[device_id] = num_nni_ports;
return BCMOS_TRUE;
}
bcmos_bool bcm_topo_dev_get_max_nni(bcmolt_devid device_id, uint32_t *p_num_nni_ports)
{
if(device_id >= BCM_TOPO_MAX_NNI_DEVICES)
{
return BCM_ERR_PARM;
}
*p_num_nni_ports = g_max_nni_ports[device_id];
return BCMOS_TRUE;
}
bcmos_bool bcm_topo_nni_is_valid(uint32_t nni)
{
if (nni >= g_max_nni_ports[0])
return BCMOS_FALSE;
return BCMOS_TRUE;
}