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/chip.c | 580 ++++++++++++++++++++++++ 1 file changed, 580 insertions(+) create mode 100644 drivers/net/wireless/celeno/cl8k/chip.c 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); +} -- 2.36.1