2022-05-24 13:03:32

by Viktor Barna

[permalink] [raw]
Subject: [RFC v2 52/96] cl8k: add platform.c

From: Viktor Barna <[email protected]>

(Part of the split. Please, take a look at the cover letter for more
details).

Signed-off-by: Viktor Barna <[email protected]>
---
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