From: Viktor Barna <viktor.barna@xxxxxxxxxx> (Part of the split. Please, take a look at the cover letter for more details). Signed-off-by: Viktor Barna <viktor.barna@xxxxxxxxxx> --- drivers/net/wireless/celeno/cl8k/platform.c | 392 ++++++++++++++++++++ 1 file changed, 392 insertions(+) create mode 100644 drivers/net/wireless/celeno/cl8k/platform.c diff --git a/drivers/net/wireless/celeno/cl8k/platform.c b/drivers/net/wireless/celeno/cl8k/platform.c new file mode 100644 index 000000000000..140505523fb2 --- /dev/null +++ b/drivers/net/wireless/celeno/cl8k/platform.c @@ -0,0 +1,392 @@ +// SPDX-License-Identifier: GPL-2.0 OR BSD-2-Clause +/* Copyright(c) 2019-2022, Celeno Communications Ltd. */ + +#include <linux/types.h> + +#include "e2p.h" +#include "debug.h" +#include "utils.h" +#include "hw.h" +#include "platform.h" + +static int cl_platform_update_idx_v1(struct cl_chip *chip) +{ + u8 i = 0; + u32 platform_id = 0; + struct cl_agc_platform_pack *app = chip->platform.app; + ssize_t buf_len = chip->platform.app_size; + ssize_t data_len = buf_len - sizeof(*app); + ssize_t offset = 0; + struct cl_platform_binding *platform = NULL; + + if (cl_e2p_read(chip, (u8 *)&platform_id, SIZE_FEM_PLATFORM_ID, ADDR_FEM_PLATFORM_ID)) + return -1; + + cl_dbg_chip_verbose(chip, + "platform_id: 0x%08x, " + "customer_id: 0x%04x, board_id: 0x%02x, chip_id: 0x%x\n", + platform_id, + PLATFORM_CUSTOMER(platform_id), + PLATFORM_BOARD(platform_id), + PLATFORM_CHIP(platform_id)); + + while (offset <= data_len) { + struct cl_agc_tilv *tilv = (struct cl_agc_tilv *)((u8 *)app->data + offset); + + if (tilv->t == CL_AGC_PACK_EOF || tilv->t == CL_AGC_PACK_UNDEFINED) + break; + + if (tilv->t != CL_AGC_PACK_PLATFORM_BINDING) + goto next_tilv; + + platform = (void *)tilv->v; + if (platform_id != platform->platform_id) + goto next_tilv; + + cl_dbg_chip_verbose(chip, "%s\n", platform->platform_description); + chip->platform.idx = i; + return 0; +next_tilv: + i += 1; + offset += sizeof(*tilv) + tilv->l; + } + + if (IS_REAL_PHY(chip)) { + CL_DBG_ERROR_CHIP(chip, "Invalid platform_id 0x%08x\n", platform_id); + + if (!chip->conf->ce_production_mode) + return -1; + } + + return 0; +} + +struct cl_platform_table *cl_platform_get_active_table(struct cl_chip *chip, u8 idx) +{ + bool is_active = (idx == chip->platform.idx); + + if (idx >= cl_platform_get_tables_cnt(chip)) + return NULL; + + if (is_active && chip->platform.app) + return &chip->platform.table; + + return NULL; +} + +static int cl_calc_platforms_number(struct cl_agc_platform_pack *app, ssize_t buf_len) +{ + ssize_t data_len = buf_len - sizeof(*app); + ssize_t offset = 0; + int ret = 0; + + while (offset <= data_len) { + struct cl_agc_tilv *tilv = (struct cl_agc_tilv *)((u8 *)app->data + offset); + + if (tilv->t == CL_AGC_PACK_EOF || tilv->t == CL_AGC_PACK_UNDEFINED) + break; + + if (tilv->t != CL_AGC_PACK_PLATFORM_BINDING) + goto next_tilv; + + ret += 1; +next_tilv: + offset += sizeof(*tilv) + tilv->l; + } + + return ret; +} + +static void *cl_find_tilv_data(struct cl_agc_platform_pack *app, + ssize_t buf_len, u8 seeking_id, u8 seeking_type) +{ + ssize_t data_len = buf_len - sizeof(*app); + ssize_t offset = 0; + void *ret = NULL; + + if (seeking_type >= CL_AGC_PACK_MAX) + return NULL; + + while (offset <= data_len) { + struct cl_agc_tilv *tilv = (struct cl_agc_tilv *)((u8 *)app->data + offset); + + if (tilv->t == CL_AGC_PACK_EOF || tilv->t == CL_AGC_PACK_UNDEFINED) + break; + + if (tilv->t != seeking_type) + goto next_tilv; + + if (tilv->i == seeking_id) { + ret = (void *)&tilv->v; + break; + } + +next_tilv: + offset += sizeof(*tilv) + tilv->l; + } + + return ret; +} + +static struct cl_platform_binding *cl_find_binding(struct cl_agc_platform_pack *app, + ssize_t buf_len, u8 seeking_id) +{ + return cl_find_tilv_data(app, buf_len, seeking_id, CL_AGC_PACK_PLATFORM_BINDING); +} + +static struct cl_agc_profile_per_bw *cl_find_agc_profile(struct cl_agc_platform_pack *app, + ssize_t buf_len, u8 seeking_id) +{ + return cl_find_tilv_data(app, buf_len, seeking_id, CL_AGC_PACK_PROFILE); +} + +static const u8 *cl_find_power_table(struct cl_agc_platform_pack *app, + ssize_t buf_len, u8 seeking_id) +{ + return cl_find_tilv_data(app, buf_len, seeking_id, CL_AGC_PACK_POWER_TABLE); +} + +static bool cl_is_valid_agc_profile(u8 profile_id) +{ + return (profile_id >= CL_AGC_PROFILE_FIRST) && + (profile_id < CL_AGC_PROFILE_MAX); +} + +static bool cl_is_valid_power_table(u8 table_id) +{ + return (table_id >= CL_POWER_TO_POWERWORD_CONV_TABLE_FIRST) && + (table_id < CL_POWER_TO_POWERWORD_CONV_TABLE_MAX); +} + +int cl_platform_get_tables_cnt(struct cl_chip *chip) +{ + return cl_calc_platforms_number(chip->platform.app, chip->platform.app_size); +} + +int cl_platform_unpack_v1(struct cl_platform_table *table, + struct cl_agc_platform_pack *app, + ssize_t buf_len, u8 platform_idx) +{ + struct cl_platform_binding *binding = NULL; + u8 profile_id = CL_AGC_PROFILE_UNDEFINED; + + binding = cl_find_binding(app, buf_len, platform_idx); + if (!binding) + return -ENODATA; + + /* Find corresponding binding */ + table->platform_id = binding->platform_id; + memcpy(table->platform_description, binding->platform_description, + sizeof(table->platform_description)); + + /* Bind AGC profiles */ + profile_id = binding->agc_profile[TCV0]; + if (cl_is_valid_agc_profile(profile_id)) + table->agc_profile[TCV0] = cl_find_agc_profile(app, buf_len, profile_id); + + profile_id = binding->agc_profile[TCV1]; + if (cl_is_valid_agc_profile(profile_id)) + table->agc_profile[TCV1] = cl_find_agc_profile(app, buf_len, profile_id); + + profile_id = binding->agc_profile_elastic[TCV0]; + if (cl_is_valid_agc_profile(profile_id)) + table->agc_profile_elastic[TCV0] = cl_find_agc_profile(app, buf_len, profile_id); + + profile_id = binding->agc_profile_elastic[TCV1]; + if (cl_is_valid_agc_profile(profile_id)) + table->agc_profile_elastic[TCV1] = cl_find_agc_profile(app, buf_len, profile_id); + + profile_id = binding->agc_profile_sensing; + if (cl_is_valid_agc_profile(profile_id)) + table->agc_profile_sensing = cl_find_agc_profile(app, buf_len, profile_id); + + /* Bind power tables */ + if (cl_is_valid_power_table(binding->power_conv_table_2)) + table->power_conv_table_2 = cl_find_power_table(app, buf_len, + binding->power_conv_table_2); + + if (cl_is_valid_power_table(binding->power_conv_table_5)) + table->power_conv_table_5 = cl_find_power_table(app, buf_len, + binding->power_conv_table_5); + + if (cl_is_valid_power_table(binding->power_conv_table_6)) + table->power_conv_table_6 = cl_find_power_table(app, buf_len, + binding->power_conv_table_6); + + return 0; +} + +static int cl_platform_unpack(struct cl_chip *chip) +{ + int ret = 0; + struct cl_agc_platform_pack *app = chip->platform.app; + ssize_t buf_len = chip->platform.app_size; + + if (strncmp(app->magic, PLATFORM_PACK_MAGIC_V1, sizeof(app->magic)) != 0) { + cl_dbg_chip_err(chip, + "Magic differs, got %s, expected %s\n", + app->magic, PLATFORM_PACK_MAGIC_V1); + return -EINVAL; + } + if (le32_to_cpu(app->version) != PLATFORM_PACK_VERSION_V1) { + cl_dbg_chip_err(chip, + "Invalid pack version: %u\n", + le32_to_cpu(app->version)); + return -EINVAL; + } + + ret = cl_platform_update_idx_v1(chip); + if (ret) + return ret; + + ret = cl_platform_unpack_v1(&chip->platform.table, app, buf_len, + chip->platform.idx); + return ret; +} + +int cl_platform_alloc(struct cl_chip *chip) +{ + char filename[CL_FILENAME_MAX]; + size_t size = 0; + char *buf = NULL; + int ret = 0; + + snprintf(filename, sizeof(filename), PLATFORM_PACK_FILENAME_V1); + size = cl_file_open_and_read(chip, filename, (char **)&buf); + if (!buf) { + cl_dbg_chip_err(chip, "AGC platform pack data buffer is empty\n"); + + ret = -ENODATA; + goto err; + } + + chip->platform.app_size = size; + chip->platform.app = (struct cl_agc_platform_pack *)buf; + + ret = cl_platform_unpack(chip); +err: + return ret; +} + +void cl_platform_dealloc(struct cl_chip *chip) +{ + if (chip->platform.app) { + kvfree(chip->platform.app); + chip->platform.app = NULL; + chip->platform.app_size = 0; + } +} + +/* AGC PROFILE */ +#define AGC_PROFILE_BAND_MASK 0xff000000 +#define AGC_PROFILE_BRANCH_MASK 0x00ff0000 +#define AGC_PROFILE_VERSION_MASK 0x0000ff00 + +#define AGC_PROFILE_BAND(profile) \ + u32_get_bits(profile, AGC_PROFILE_BAND_MASK) +#define AGC_PROFILE_BRANCH(profile) \ + u32_get_bits(profile, AGC_PROFILE_BRANCH_MASK) +#define AGC_PROFILE_VERSION(profile) \ + u32_get_bits(profile, AGC_PROFILE_VERSION_MASK) + +#ifdef __BIG_ENDIAN_BITFIELD +static void cl_agc_profile_to_le32(struct cl_agc_profile *profile) +{ + u32 i; + u32 size = sizeof(struct cl_agc_profile); + u32 *ptr = (u32 *)profile; + + /* Make sure that size divides by 4 */ + WARN_ON((size & 0x3) != 0); + + for (i = 0; i < size / 4; i++) + ptr[i] = cpu_to_le32(ptr[i]); +} +#endif + +int cl_agc_params_fill(struct cl_hw *cl_hw, struct cl_agc_params *agc_params) +{ + u8 tcv_idx = cl_hw->tcv_idx; + u8 platform_idx = cl_hw->chip->platform.idx; + u8 bw = cl_hw->bw; + struct cl_platform_table *table = NULL; + + memset(agc_params, 0, sizeof(struct cl_agc_params)); + + if (platform_idx == U8_MAX) + return 0; + + table = cl_platform_get_active_table(cl_hw->chip, platform_idx); + if (!table) + return -EINVAL; + + if (!table->agc_profile_elastic[tcv_idx] || cl_hw->num_antennas <= 2) { + u8 ant_shift = cl_hw_ant_shift(cl_hw); + + if (table->agc_profile[tcv_idx]) { + agc_params->profile1.id = table->agc_profile[tcv_idx]->id; + agc_params->profile1.regs = table->agc_profile[tcv_idx]->regs[bw]; + } else if (tcv_idx == TCV1 && table->agc_profile_sensing) { + agc_params->profile1.id = table->agc_profile_sensing->id; + agc_params->profile1.regs = table->agc_profile_sensing->regs[bw]; + } else { + CL_DBG_ERROR(cl_hw, "Invalid tcv/sensing profile"); + return -1; + } + + agc_params->num_profiles = 1; + agc_params->ant_mask1 = ANT_MASK(cl_hw->num_antennas) << ant_shift; + agc_params->ant_mask2 = 0; + +#ifdef __BIG_ENDIAN_BITFIELD + cl_agc_profile_to_le32(&agc_params->profile1); +#endif + } else { + if (table->agc_profile[tcv_idx]) { + memcpy(&agc_params->profile1, + table->agc_profile[tcv_idx], + sizeof(struct cl_agc_profile)); + } else { + CL_DBG_ERROR(cl_hw, "Invalid tcv profile"); + return -1; + } + + if (table->agc_profile_elastic[tcv_idx]) { + memcpy(&agc_params->profile2, + table->agc_profile_elastic[tcv_idx], + sizeof(struct cl_agc_profile)); + } else { + CL_DBG_ERROR(cl_hw, "Invalid elastic profile"); + return -1; + } + + agc_params->num_profiles = 2; + agc_params->ant_mask1 = ANT_MASK(2); + agc_params->ant_mask2 = ANT_MASK(cl_hw->num_antennas - 2) << 2; + +#ifdef __BIG_ENDIAN_BITFIELD + cl_agc_profile_to_le32(&agc_params->profile1); + cl_agc_profile_to_le32(&agc_params->profile2); +#endif + } + + return 0; +} + +void cl_agc_params_dump_profile_id(char *buf, ssize_t buf_size, ssize_t *len, + u32 id, const char *str) +{ + u8 band, branch, version; + + band = AGC_PROFILE_BAND(id); + branch = AGC_PROFILE_BRANCH(id); + version = AGC_PROFILE_VERSION(id); + + if (*buf) { + *len += scnprintf(buf + *len, buf_size - *len, + "%s %u.%u.%u\n", str, band, branch, version); + } else { + pr_debug("%s %u.%u.%u\n", str, band, branch, version); + } +} + -- 2.36.1