diff mbox series

[RFC,v2,13/96] cl8k: add chip.c

Message ID 20220524113502.1094459-14-viktor.barna@celeno.com
State New
Headers show
Series wireless: cl8k driver for Celeno IEEE 802.11ax devices | expand

Commit Message

Viktor Barna May 24, 2022, 11:33 a.m. UTC
From: Viktor Barna <viktor.barna@celeno.com>

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

Signed-off-by: Viktor Barna <viktor.barna@celeno.com>
---
 drivers/net/wireless/celeno/cl8k/chip.c | 580 ++++++++++++++++++++++++
 1 file changed, 580 insertions(+)
 create mode 100644 drivers/net/wireless/celeno/cl8k/chip.c
diff mbox series

Patch

diff --git a/drivers/net/wireless/celeno/cl8k/chip.c b/drivers/net/wireless/celeno/cl8k/chip.c
new file mode 100644
index 000000000000..67f65dffd804
--- /dev/null
+++ b/drivers/net/wireless/celeno/cl8k/chip.c
@@ -0,0 +1,580 @@ 
+// SPDX-License-Identifier: GPL-2.0 OR BSD-2-Clause
+/* Copyright(c) 2019-2022, Celeno Communications Ltd. */
+
+#include <linux/dmapool.h>
+
+#include "reg/reg_access.h"
+#include "reg/reg_defs.h"
+#include "temperature.h"
+#include "phy.h"
+#include "e2p.h"
+#include "main.h"
+#include "rates.h"
+#include "calib.h"
+#include "config.h"
+#include "utils.h"
+#include "chip.h"
+
+#define CL_ALIGN_BOUND_64KB BIT(16)
+
+static void cl_chip_set_max_antennas(struct cl_chip *chip)
+{
+	u16 device_id = cl_chip_get_device_id(chip);
+
+	switch (device_id) {
+	case 0x8040:
+	case 0x8046:
+		chip->max_antennas = MAX_ANTENNAS_CL804X;
+		break;
+	case 0x8060:
+	case 0x8066:
+		chip->max_antennas = MAX_ANTENNAS_CL806X;
+		break;
+	case 0x8080:
+	case 0x8086:
+	default:
+		chip->max_antennas = MAX_ANTENNAS_CL808X;
+		break;
+	}
+}
+
+/*
+ * This function is necessary since now we can't determine the max antenna number by the
+ * device_id alone, since there is at least one case of device_id 8046 and max antenna of 3.
+ */
+static void cl_chip_update_max_antennas(struct cl_chip *chip)
+{
+	u8 wiring_id = chip->fem.wiring_id;
+
+	if (wiring_id == FEM_WIRING_27_TCV0_2_TCV1_1 || wiring_id == FEM_WIRING_31_TCV0_2_TCV1_1)
+		chip->max_antennas = MAX_ANTENNAS_WIRING_ID_27_31;
+}
+
+static int cl_chip_print_serial_number(struct cl_chip *chip)
+{
+	u8 serial_number[SIZE_GEN_SERIAL_NUMBER + 1] = {0};
+
+	if (!IS_REAL_PHY(chip))
+		return 0;
+
+	if (cl_e2p_read(chip, (u8 *)&serial_number, SIZE_GEN_SERIAL_NUMBER, ADDR_GEN_SERIAL_NUMBER))
+		return -1;
+
+	if (strlen(serial_number) == 0)
+		cl_dbg_chip_verbose(chip, "Serial-number in not set in EEPROM\n");
+	else
+		cl_dbg_chip_verbose(chip, "Serial-number = %s\n", serial_number);
+
+	return 0;
+}
+
+static int cl_chip_ring_indices_init(struct cl_chip *chip)
+{
+	struct cl_ring_indices *ring_indices = &chip->ring_indices;
+
+	ring_indices->pool = dma_pool_create("cl_ring_indices_pool", chip->dev,
+					     (sizeof(struct cl_ipc_ring_indices) * TCV_MAX),
+					     CL_ALIGN_BOUND_64KB, CL_ALIGN_BOUND_64KB);
+
+	if (!ring_indices->pool) {
+		cl_dbg_chip_err(chip, "ring_indices pool create failed !!!\n");
+		return -ENOMEM;
+	}
+
+	ring_indices->params = dma_pool_alloc(ring_indices->pool, GFP_KERNEL,
+					      &ring_indices->dma_addr);
+	if (!ring_indices->params)
+		return -ENOMEM;
+
+	return 0;
+}
+
+static void cl_chip_ring_indices_deinit(struct cl_chip *chip)
+{
+	if (chip->ring_indices.params) {
+		dma_pool_free(chip->ring_indices.pool,
+			      chip->ring_indices.params,
+			      chip->ring_indices.dma_addr);
+		chip->ring_indices.params = NULL;
+	}
+
+	dma_pool_destroy(chip->ring_indices.pool);
+	chip->ring_indices.pool = NULL;
+}
+
+struct cl_chip *cl_chip_alloc(u8 idx)
+{
+	struct cl_chip *chip = vzalloc(sizeof(*chip));
+
+	if (!chip)
+		return NULL;
+
+	chip->idx = idx;
+	return chip;
+}
+
+void cl_chip_dealloc(struct cl_chip *chip)
+{
+	cl_chip_config_dealloc(chip);
+	vfree(chip);
+}
+
+static void cl_chip_workqueue_create(struct cl_chip *chip)
+{
+	if (!chip->chip_workqueue)
+		chip->chip_workqueue = create_singlethread_workqueue("chip_workqueue");
+}
+
+static void cl_chip_workqueue_destroy(struct cl_chip *chip)
+{
+	if (chip->chip_workqueue) {
+		destroy_workqueue(chip->chip_workqueue);
+		chip->chip_workqueue = NULL;
+	}
+}
+
+int cl_chip_init(struct cl_chip *chip)
+{
+	int ret = 0;
+
+	chip->ipc_host2xmac_trigger_set = ipc_host_2_umac_trigger_set;
+
+	chip->platform.app = NULL;
+	chip->platform.app_size = 0;
+	chip->platform.idx = U8_MAX;
+
+	spin_lock_init(&chip->isr_lock);
+	spin_lock_init(&chip->spi_lock);
+	mutex_init(&chip->start_msg_lock);
+	mutex_init(&chip->calib_runtime_mutex);
+
+	rwlock_init(&chip->cl_hw_lock);
+	mutex_init(&chip->recovery_mutex);
+	mutex_init(&chip->set_idle_mutex);
+
+	cl_chip_set_max_antennas(chip);
+
+	ret = cl_irq_request(chip);
+	if (ret)
+		return ret;
+
+	ipc_host_global_int_en_set(chip, 1);
+
+	cl_temperature_init(chip);
+
+	ret = cl_e2p_init(chip);
+	if (ret) {
+		cl_dbg_chip_err(chip, "cl_e2p_init failed %d\n", ret);
+		return ret;
+	}
+
+	ret = cl_fem_init(chip);
+	if (ret)
+		return ret;
+
+	/*
+	 * Update chip->max_antennas according to wiring_id if needed.
+	 * Should be called after cl_fem_init(), where wiring_id is read from eeprom.
+	 */
+	cl_chip_update_max_antennas(chip);
+
+	ret = cl_chip_ring_indices_init(chip);
+	if (ret)
+		return ret;
+
+	cl_chip_print_serial_number(chip);
+
+	cl_wrs_tables_global_build();
+	cl_data_rates_inverse_build();
+	cl_chip_workqueue_create(chip);
+
+	return ret;
+}
+
+void cl_chip_deinit(struct cl_chip *chip)
+{
+	cl_chip_workqueue_destroy(chip);
+
+	cl_chip_ring_indices_deinit(chip);
+
+	cl_temperature_close(chip);
+
+	cl_e2p_close(chip);
+
+	ipc_host_global_int_en_set(chip, 0);
+
+	cl_irq_free(chip);
+}
+
+u16 cl_chip_get_device_id(struct cl_chip *chip)
+{
+	u16 device_id = chip->pci_dev->device;
+
+	if (chip->conf->ci_sim_device_id)
+		return chip->conf->ci_sim_device_id;
+
+	/* If device-id is default, set it accoridng to phy-dev. */
+	if (device_id == 0x8000 || device_id == 0x8001)
+		device_id = (chip->conf->ci_phy_dev == PHY_DEV_ATHOS) ? 0x8086 : 0x8080;
+
+	return device_id;
+}
+
+bool cl_chip_is_enabled(struct cl_chip *chip)
+{
+	return cl_chip_is_tcv0_enabled(chip) || cl_chip_is_tcv1_enabled(chip);
+}
+
+bool cl_chip_is_both_enabled(struct cl_chip *chip)
+{
+	return cl_chip_is_tcv0_enabled(chip) && cl_chip_is_tcv1_enabled(chip);
+}
+
+bool cl_chip_is_tcv0_enabled(struct cl_chip *chip)
+{
+	return chip->conf->ce_tcv_enabled[TCV0];
+}
+
+bool cl_chip_is_tcv1_enabled(struct cl_chip *chip)
+{
+	return chip->conf->ce_tcv_enabled[TCV1];
+}
+
+bool cl_chip_is_only_tcv0_enabled(struct cl_chip *chip)
+{
+	return cl_chip_is_tcv0_enabled(chip) && !cl_chip_is_tcv1_enabled(chip);
+}
+
+bool cl_chip_is_only_tcv1_enabled(struct cl_chip *chip)
+{
+	return !cl_chip_is_tcv0_enabled(chip) && cl_chip_is_tcv1_enabled(chip);
+}
+
+void cl_chip_set_hw(struct cl_chip *chip, struct cl_hw *cl_hw)
+{
+	if (cl_hw_is_tcv0(cl_hw))
+		chip->cl_hw_tcv0 = cl_hw;
+	else
+		chip->cl_hw_tcv1 = cl_hw;
+}
+
+void cl_chip_unset_hw(struct cl_chip *chip, struct cl_hw *cl_hw)
+{
+	if (cl_hw_is_tcv0(cl_hw))
+		chip->cl_hw_tcv0 = NULL;
+	else
+		chip->cl_hw_tcv1 = NULL;
+}
+
+bool cl_chip_is_8ant(struct cl_chip *chip)
+{
+	return chip->max_antennas == MAX_ANTENNAS_CL808X;
+}
+
+bool cl_chip_is_6ant(struct cl_chip *chip)
+{
+	return chip->max_antennas == MAX_ANTENNAS_CL806X;
+}
+
+bool cl_chip_is_4ant(struct cl_chip *chip)
+{
+	return chip->max_antennas == MAX_ANTENNAS_CL804X;
+}
+
+bool cl_chip_is_3ant(struct cl_chip *chip)
+{
+	return chip->max_antennas == MAX_ANTENNAS_WIRING_ID_27_31;
+}
+
+bool cl_chip_is_6g(struct cl_chip *chip)
+{
+	u16 device_id = cl_chip_get_device_id(chip);
+	u8 band = device_id & 0xf;
+
+	return (band == 6);
+}
+
+#define MAX_FIRST_MASK_BIT ((ETH_ALEN * 8) - 1)
+
+static struct cl_chip_conf chip_conf = {
+	.ce_tcv_enabled = {
+		[TCV0] = false,
+		[TCV1] = false
+	},
+	.ce_lmac = "lmacfw.bin",
+	.ce_smac = "smacfw.bin",
+	.ce_irq_smp_affinity = -1,
+	.ce_eeprom_mode = E2P_MODE_BIN,
+	.ce_production_mode = false,
+	.ci_pci_msi_enable = true,
+	.ci_dma_lli_max_chan = {
+		[TCV0] = 6,
+		[TCV1] = 3
+	},
+	.ci_regdom_mode = "self",
+	.ci_country_code = "US",
+	.ce_ela_mode = "default",
+	.ci_phy_dev = PHY_DEV_OLYMPUS,
+	.ce_debug_level = DBG_LVL_ERROR,
+	.ce_host_pci_gen_ver = 3,
+	.ci_scale_down_fw = 1,
+	.ce_temp_comp_en = true,
+	.ce_temp_protect_en = TEMP_PROTECT_EXTERNAL,
+	.ce_temp_protect_delta = 0,
+	.ce_temp_protect_th_max = 105,
+	.ce_temp_protect_th_min = 95,
+	.ce_temp_protect_tx_period_ms = 50,
+	.ce_temp_protect_radio_off_th = 110,
+	.ci_phy_load_bootdrv = false,
+	.ce_phys_mac_addr = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
+	.ce_lam_enable = true,
+	.ce_first_mask_bit = 0,
+	.ci_no_capture_noise_sleep = true,
+	.ci_afe_config_en = true,
+	.ci_afe_vc_ref = 0x77777777,
+	.ci_afe_vc_avd = 0x55555555,
+	.ci_afe_eoc_ctrl = 0xaaaa,
+	.ci_afe_ch_cml_sel = 0xff,
+	.ci_afe_vc_cml = 0,
+	.ci_afe_cml_sel = 7,
+	.ci_afe_loopback = false,
+	.ci_afe_hw_mode = true,
+	.ci_dcoc_mv_thr = {
+		[CHNL_BW_20] = 150,
+		[CHNL_BW_40] = 100,
+		[CHNL_BW_80] = 100,
+		[CHNL_BW_160] = 100
+	},
+	.ci_calib_check_errors = false,
+	.ci_lolc_db_thr = -40,
+	.ci_iq_db_thr = -46,
+	.ci_rx_resched_tasklet = false,
+	.ci_rx_skb_max = 10000,
+	.ci_tcv1_chains_sx0 = false,
+	.ci_sim_device_id = 0,
+	.ce_calib_runtime_en = false,
+	.ci_calib_eeprom_en = 0,
+	.ci_calib_runtime_force = false,
+	.ci_la_mirror_en = true,
+};
+
+static int cl_chip_update_config(struct cl_chip *chip, char *name, char *value)
+{
+	struct cl_chip_conf *conf = chip->conf;
+	int ret = -ENOENT;
+
+	do {
+		READ_BOOL_ARR(ce_tcv_enabled, TCV_MAX);
+		READ_STR(ce_lmac);
+		READ_STR(ce_smac);
+		READ_S32(ce_irq_smp_affinity);
+		READ_U8(ce_eeprom_mode);
+		READ_BOOL(ce_production_mode);
+		READ_BOOL(ci_pci_msi_enable);
+		READ_U8_ARR(ci_dma_lli_max_chan, TCV_MAX, true);
+		READ_STR(ci_regdom_mode);
+		READ_STR(ci_country_code);
+		READ_STR(ce_ela_mode);
+		READ_U8(ci_phy_dev);
+		READ_S8(ce_debug_level);
+		READ_U8(ce_host_pci_gen_ver);
+		READ_S32(ci_scale_down_fw);
+		READ_BOOL(ce_temp_comp_en);
+		READ_U8(ce_temp_protect_en);
+		READ_S8(ce_temp_protect_delta);
+		READ_S16(ce_temp_protect_th_max);
+		READ_S16(ce_temp_protect_th_min);
+		READ_U16(ce_temp_protect_tx_period_ms);
+		READ_S16(ce_temp_protect_radio_off_th);
+		READ_BOOL(ci_phy_load_bootdrv);
+		READ_MAC(ce_phys_mac_addr);
+		READ_BOOL(ce_lam_enable);
+		READ_U8(ce_first_mask_bit);
+		READ_BOOL(ci_no_capture_noise_sleep);
+		READ_BOOL(ci_afe_config_en);
+		READ_U32(ci_afe_vc_ref);
+		READ_U8(ci_afe_cml_sel);
+		READ_BOOL(ci_afe_loopback);
+		READ_BOOL(ci_afe_hw_mode);
+		READ_U8_ARR(ci_dcoc_mv_thr, CHNL_BW_MAX, true);
+		READ_BOOL(ci_calib_check_errors);
+		READ_S8(ci_lolc_db_thr);
+		READ_S8(ci_iq_db_thr);
+		READ_BOOL(ci_rx_resched_tasklet);
+		READ_U32(ci_rx_skb_max);
+		READ_BOOL(ci_tcv1_chains_sx0);
+		READ_U16(ci_sim_device_id);
+		READ_BOOL(ce_calib_runtime_en);
+		READ_BOOL(ci_calib_eeprom_en);
+		READ_BOOL(ci_calib_runtime_force);
+		READ_BOOL(ci_la_mirror_en);
+	} while (0);
+
+	if (ret == -ENOENT) {
+		if (cl_config_is_non_driver_param(name))
+			ret = 0;
+		else
+			CL_DBG_ERROR_CHIP(chip, "No matching conf for nvram parameter %s\n", name);
+	}
+
+	return ret;
+}
+
+static bool cl_chip_is_valid_device_id(u16 device_id)
+{
+	switch (device_id) {
+	case 0x8000:
+	case 0x8001:
+	case 0x8080:
+	case 0x8086:
+	case 0x8060:
+	case 0x8040:
+	case 0x8066:
+	case 0x8046:
+		return true;
+	default:
+		pr_debug("Invalid device_id %u\n", device_id);
+		break;
+	}
+
+	return false;
+}
+
+static int cl_chip_post_configuration(struct cl_chip *chip)
+{
+	struct cl_chip_conf *conf = chip->conf;
+
+	if (conf->ce_eeprom_mode >= E2P_MODE_MAX) {
+		CL_DBG_ERROR_CHIP(chip,
+				  "Invalid ce_eeprom_mode [%u]. Must be 0 (file) or 1 (eeprom)\n",
+				  conf->ce_eeprom_mode);
+		return -EINVAL;
+	}
+
+	if (conf->ce_first_mask_bit > MAX_FIRST_MASK_BIT) {
+		CL_DBG_ERROR_CHIP(chip, "Invalid ce_first_mask_bit (%u). Must be <= %u\n",
+				  conf->ce_first_mask_bit, MAX_FIRST_MASK_BIT);
+		return -EINVAL;
+	}
+
+	if (conf->ci_tcv1_chains_sx0 && !conf->ce_production_mode) {
+		cl_dbg_chip_verbose(chip, "Force ci_tcv1_chains_sx0 to 0 for operational mode\n");
+		conf->ci_tcv1_chains_sx0 = false;
+	}
+
+	if (conf->ci_sim_device_id && !cl_chip_is_valid_device_id(conf->ci_sim_device_id)) {
+		CL_DBG_ERROR_CHIP(chip, "Invalid ci_sim_device_id (0x%x)\n",
+				  conf->ci_sim_device_id);
+		return -1;
+	}
+	/* IQ and DCOC calibration data will be saved only if runtime feature is enable */
+	if (chip->conf->ci_calib_eeprom_en && !chip->conf->ce_calib_runtime_en) {
+		chip->conf->ci_calib_eeprom_en = 0;
+		CL_DBG_ERROR_CHIP(chip, "Writing/reading calibration data from the EEPROM is "
+				  "disabled because ce_calib_runtime_en nvram isn't set\n");
+	}
+
+	return 0;
+}
+
+static int cl_chip_set_all_params_from_buf(struct cl_chip *chip, char *buf, loff_t size)
+{
+	char *line = buf;
+	char name[MAX_PARAM_NAME_LENGTH];
+	char value[STR_LEN_256B];
+	char *begin;
+	char *end;
+	int ret = 0;
+	int name_length = 0;
+	int value_length = 0;
+
+	while (line && strlen(line) && (line != (buf + size))) {
+		if ((*line == '#') || (*line == '\n')) {
+			/* Skip comment or blank line */
+			line = strstr(line, "\n") + 1;
+		} else if (*line) {
+			begin = line;
+			end = strstr(begin, "=");
+
+			if (!end) {
+				ret = -EBADMSG;
+				goto exit;
+			}
+
+			end++;
+			name_length = end - begin;
+			value_length = strstr(end, "\n") - end + 1;
+
+			if (name_length >= MAX_PARAM_NAME_LENGTH) {
+				cl_dbg_chip_err(chip, "Name too long (%u)\n", name_length);
+				ret = -EBADMSG;
+				goto exit;
+			}
+			if (value_length >= STR_LEN_256B) {
+				cl_dbg_chip_err(chip, "Value too long (%u)\n", value_length);
+				ret = -EBADMSG;
+				goto exit;
+			}
+
+			snprintf(name, name_length, "%s", begin);
+			snprintf(value, value_length, "%s", end);
+
+			ret = cl_chip_update_config(chip, name, value);
+			if (ret)
+				goto exit;
+
+			line = strstr(line, "\n") + 1;
+		}
+	}
+
+exit:
+
+	return ret;
+}
+
+int cl_chip_config_read(struct cl_chip *chip)
+{
+	char *buf = NULL;
+	loff_t size = 0;
+	int ret = 0;
+	char filename[CL_FILENAME_MAX] = {0};
+
+	/* Allocate cl_chip_conf */
+	chip->conf = kzalloc(sizeof(*chip->conf), GFP_KERNEL);
+	if (!chip->conf)
+		return -ENOMEM;
+
+	/* Copy default parameters */
+	memcpy(chip->conf, &chip_conf, sizeof(*chip->conf));
+
+	snprintf(filename, sizeof(filename), "cl_chip%u.dat", chip->idx);
+	pr_debug("%s: %s\n", __func__, filename);
+	size = cl_file_open_and_read(chip, filename, &buf);
+
+	if (!buf) {
+		pr_err("read %s failed !!!\n", filename);
+		return -ENODATA;
+	}
+
+	ret = cl_chip_set_all_params_from_buf(chip, buf, size);
+	if (ret) {
+		kfree(buf);
+		return ret;
+	}
+
+	kfree(buf);
+
+	if (!cl_chip_is_enabled(chip)) {
+		cl_dbg_chip_verbose(chip, "Disabled\n");
+		return -EOPNOTSUPP;
+	}
+
+	ret = cl_chip_post_configuration(chip);
+
+	return ret;
+}
+
+void cl_chip_config_dealloc(struct cl_chip *chip)
+{
+	kfree(chip->conf);
+}