diff options
Diffstat (limited to 'drivers')
65 files changed, 5829 insertions, 513 deletions
diff --git a/drivers/bluetooth/btqca.c b/drivers/bluetooth/btqca.c index c9064d34d830..9dcad5af2491 100644 --- a/drivers/bluetooth/btqca.c +++ b/drivers/bluetooth/btqca.c @@ -205,6 +205,48 @@ static int qca_send_reset(struct hci_dev *hdev) return 0; } +static int qca_read_fw_board_id(struct hci_dev *hdev, u16 *bid) +{ + u8 cmd; + struct sk_buff *skb; + struct edl_event_hdr *edl; + int err = 0; + int bid_len; + + bt_dev_dbg(hdev, "QCA read board ID"); + + cmd = EDL_GET_BID_REQ_CMD; + skb = __hci_cmd_sync_ev(hdev, EDL_PATCH_CMD_OPCODE, EDL_PATCH_CMD_LEN, + &cmd, 0, HCI_INIT_TIMEOUT); + if (IS_ERR(skb)) { + err = PTR_ERR(skb); + bt_dev_err(hdev, "Reading QCA board ID failed (%d)", err); + return err; + } + + edl = skb_pull_data(skb, sizeof(*edl)); + if (!edl) { + bt_dev_err(hdev, "QCA read board ID with no header"); + err = -EILSEQ; + goto out; + } + + if (edl->cresp != EDL_CMD_REQ_RES_EVT || + edl->rtype != EDL_GET_BID_REQ_CMD) { + bt_dev_err(hdev, "QCA Wrong packet: %d %d", edl->cresp, edl->rtype); + err = -EIO; + goto out; + } + + bid_len = edl->data[0]; + *bid = (edl->data[1] << 8) + edl->data[2]; + bt_dev_info(hdev, "%s: bid len = %x, bid = %x", __func__, bid_len, *bid); + +out: + kfree_skb(skb); + return err; +} + int qca_send_pre_shutdown_cmd(struct hci_dev *hdev) { struct sk_buff *skb; @@ -574,6 +616,30 @@ int qca_set_bdaddr_rome(struct hci_dev *hdev, const bdaddr_t *bdaddr) } EXPORT_SYMBOL_GPL(qca_set_bdaddr_rome); +static void qca_generate_nvm_name(struct hci_dev *hdev, char *fwname, + size_t max_size, struct qca_btsoc_version ver, u16 bid) +{ + u8 rom_ver = 0; + u32 soc_ver; + const char *variant; + + soc_ver = get_soc_ver(ver.soc_id, ver.rom_ver); + rom_ver = ((soc_ver & 0x00000f00) >> 0x04) | (soc_ver & 0x0000000f); + + if ((ver.soc_id & 0x0000ff00) == QCA_HSP_GF_SOC_ID) /* hsp gf chip */ + variant = "g"; + else + variant = ""; + + if (bid == 0x0) + snprintf(fwname, max_size, "qca/hpnv%02x%s.bin", rom_ver, variant); + else + snprintf(fwname, max_size, "qca/hpnv%02x%s.%x", + rom_ver, variant, bid); + + bt_dev_info(hdev, "%s: nvm name is %s", __func__, fwname); +} + int qca_uart_setup(struct hci_dev *hdev, uint8_t baudrate, enum qca_btsoc_type soc_type, struct qca_btsoc_version ver, const char *firmware_name) @@ -582,6 +648,7 @@ int qca_uart_setup(struct hci_dev *hdev, uint8_t baudrate, int err; u8 rom_ver = 0; u32 soc_ver; + u16 boardid = 0; bt_dev_dbg(hdev, "QCA setup on UART"); @@ -604,6 +671,9 @@ int qca_uart_setup(struct hci_dev *hdev, uint8_t baudrate, if (qca_is_wcn399x(soc_type)) { snprintf(config.fwname, sizeof(config.fwname), "qca/crbtfw%02x.tlv", rom_ver); + } else if (soc_type == QCA_QCA2066) { + snprintf(config.fwname, sizeof(config.fwname), + "qca/hpbtfw%02x.tlv", rom_ver); } else if (soc_type == QCA_QCA6390) { snprintf(config.fwname, sizeof(config.fwname), "qca/htbtfw%02x.tlv", rom_ver); @@ -628,6 +698,9 @@ int qca_uart_setup(struct hci_dev *hdev, uint8_t baudrate, /* Give the controller some time to get ready to receive the NVM */ msleep(10); + if (soc_type == QCA_QCA2066) + qca_read_fw_board_id(hdev, &boardid); + /* Download NVM configuration */ config.type = TLV_TYPE_NVM; if (firmware_name) @@ -641,7 +714,9 @@ int qca_uart_setup(struct hci_dev *hdev, uint8_t baudrate, snprintf(config.fwname, sizeof(config.fwname), "qca/crnv%02x.bin", rom_ver); } - } + } else if (soc_type == QCA_QCA2066) + qca_generate_nvm_name(hdev, config.fwname, sizeof(config.fwname), + ver, boardid); else if (soc_type == QCA_QCA6390) snprintf(config.fwname, sizeof(config.fwname), "qca/htnv%02x.bin", rom_ver); diff --git a/drivers/bluetooth/btqca.h b/drivers/bluetooth/btqca.h index 61e9a50e66ae..cb9a58b30b5e 100644 --- a/drivers/bluetooth/btqca.h +++ b/drivers/bluetooth/btqca.h @@ -13,6 +13,7 @@ #define EDL_PATCH_TLV_REQ_CMD (0x1E) #define EDL_GET_BUILD_INFO_CMD (0x20) #define EDL_NVM_ACCESS_SET_REQ_CMD (0x01) +#define EDL_GET_BID_REQ_CMD (0x23) #define EDL_PATCH_CONFIG_CMD (0x28) #define MAX_SIZE_PER_TLV_SEGMENT (243) #define QCA_PRE_SHUTDOWN_CMD (0xFC08) @@ -48,6 +49,7 @@ #define QCA_FW_BUILD_VER_LEN 255 +#define QCA_HSP_GF_SOC_ID 0x1200 enum qca_baudrate { QCA_BAUDRATE_115200 = 0, @@ -145,6 +147,7 @@ enum qca_btsoc_type { QCA_WCN3990, QCA_WCN3998, QCA_WCN3991, + QCA_QCA2066, QCA_QCA6390, QCA_WCN6750, }; diff --git a/drivers/bluetooth/hci_qca.c b/drivers/bluetooth/hci_qca.c index 45dffd2cbc71..e7361dfe858a 100644 --- a/drivers/bluetooth/hci_qca.c +++ b/drivers/bluetooth/hci_qca.c @@ -1721,7 +1721,7 @@ static int qca_setup(struct hci_uart *hu) bt_dev_info(hdev, "setting up %s", qca_is_wcn399x(soc_type) ? "wcn399x" : - (soc_type == QCA_WCN6750) ? "wcn6750" : "ROME/QCA6390"); + (soc_type == QCA_WCN6750) ? "wcn6750" : "ROME/QCA6390/QCA2066"); qca->memdump_state = QCA_MEMDUMP_IDLE; @@ -1770,7 +1770,8 @@ retry: qca_debugfs_init(hdev); hu->hdev->hw_error = qca_hw_error; hu->hdev->cmd_timeout = qca_cmd_timeout; - hu->hdev->wakeup = qca_wakeup; + if (device_can_wakeup(hu->serdev->ctrl->dev.parent)) + hu->hdev->wakeup = qca_wakeup; } else if (ret == -ENOENT) { /* No patch/nvm-config found, run with original fw/config */ set_bit(QCA_ROM_FW, &qca->flags); @@ -1863,6 +1864,11 @@ static const struct qca_device_data qca_soc_data_qca6390 = { .num_vregs = 0, }; +static const struct qca_device_data qca_soc_data_qca2066 = { + .soc_type = QCA_QCA2066, + .num_vregs = 0, +}; + static const struct qca_device_data qca_soc_data_wcn6750 = { .soc_type = QCA_WCN6750, .vregs = (struct qca_vreg []) { @@ -2332,6 +2338,7 @@ static const struct of_device_id qca_bluetooth_of_match[] = { { .compatible = "qcom,wcn3991-bt", .data = &qca_soc_data_wcn3991}, { .compatible = "qcom,wcn3998-bt", .data = &qca_soc_data_wcn3998}, { .compatible = "qcom,wcn6750-bt", .data = &qca_soc_data_wcn6750}, + { .compatible = "qcom,qca2066-bt", .data = &qca_soc_data_qca2066}, { /* sentinel */ } }; MODULE_DEVICE_TABLE(of, qca_bluetooth_of_match); @@ -2343,6 +2350,7 @@ static const struct acpi_device_id qca_bluetooth_acpi_match[] = { { "DLA16390", (kernel_ulong_t)&qca_soc_data_qca6390 }, { "DLB16390", (kernel_ulong_t)&qca_soc_data_qca6390 }, { "DLB26390", (kernel_ulong_t)&qca_soc_data_qca6390 }, + { "QCOM2066", (kernel_ulong_t)&qca_soc_data_qca2066 }, { }, }; MODULE_DEVICE_TABLE(acpi, qca_bluetooth_acpi_match); diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu.h b/drivers/gpu/drm/amd/amdgpu/amdgpu.h index afe39421d10c..df59a6919d87 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu.h +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu.h @@ -1104,6 +1104,8 @@ struct amdgpu_device { bool dc_enabled; /* Mask of active clusters */ uint32_t aid_mask; + + bool csib_initialized; }; static inline struct amdgpu_device *drm_to_adev(struct drm_device *ddev) diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_device.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_device.c index 1ecc6b095a87..b2def958aad2 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_device.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_device.c @@ -4410,6 +4410,10 @@ int amdgpu_device_suspend(struct drm_device *dev, bool fbcon) if (amdgpu_sriov_vf(adev)) amdgpu_virt_release_full_gpu(adev, false); + r = amdgpu_dpm_set_rlc_state(adev, false); + if (r) + dev_err(adev->dev, "Failed to notify RLC to be OFF.\n"); + return 0; } diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_ring.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_ring.c index 7429b20257a6..72085a3ef53c 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_ring.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_ring.c @@ -147,7 +147,7 @@ void amdgpu_ring_commit(struct amdgpu_ring *ring) count %= ring->funcs->align_mask + 1; ring->funcs->insert_nop(ring, count); - mb(); + smp_wmb(); amdgpu_ring_set_wptr(ring); if (ring->funcs->end_use) diff --git a/drivers/gpu/drm/amd/amdgpu/gfx_v10_0.c b/drivers/gpu/drm/amd/amdgpu/gfx_v10_0.c index f7ad883a70fa..8256f80d468d 100644 --- a/drivers/gpu/drm/amd/amdgpu/gfx_v10_0.c +++ b/drivers/gpu/drm/amd/amdgpu/gfx_v10_0.c @@ -3492,6 +3492,7 @@ static uint64_t gfx_v10_0_get_gpu_clock_counter(struct amdgpu_device *adev); static void gfx_v10_0_select_se_sh(struct amdgpu_device *adev, u32 se_num, u32 sh_num, u32 instance, int xcc_id); static u32 gfx_v10_0_get_wgp_active_bitmap_per_sh(struct amdgpu_device *adev); +static int gfx_v10_0_wait_for_idle(void *handle); static int gfx_v10_0_rlc_backdoor_autoload_buffer_init(struct amdgpu_device *adev); static void gfx_v10_0_rlc_backdoor_autoload_buffer_fini(struct amdgpu_device *adev); @@ -5931,7 +5932,7 @@ static int gfx_v10_0_cp_gfx_load_microcode(struct amdgpu_device *adev) return 0; } -static int gfx_v10_0_cp_gfx_start(struct amdgpu_device *adev) +static int gfx_v10_csib_submit(struct amdgpu_device *adev) { struct amdgpu_ring *ring; const struct cs_section_def *sect = NULL; @@ -5939,13 +5940,6 @@ static int gfx_v10_0_cp_gfx_start(struct amdgpu_device *adev) int r, i; int ctx_reg_offset; - /* init the CP */ - WREG32_SOC15(GC, 0, mmCP_MAX_CONTEXT, - adev->gfx.config.max_hw_contexts - 1); - WREG32_SOC15(GC, 0, mmCP_DEVICE_ID, 1); - - gfx_v10_0_cp_gfx_enable(adev, true); - ring = &adev->gfx.gfx_ring[0]; r = amdgpu_ring_alloc(ring, gfx_v10_0_get_csb_size(adev) + 4); if (r) { @@ -6008,6 +6002,25 @@ static int gfx_v10_0_cp_gfx_start(struct amdgpu_device *adev) amdgpu_ring_commit(ring); } + + gfx_v10_0_wait_for_idle(adev); + adev->csib_initialized = true; + + return 0; +}; + +static int gfx_v10_0_cp_gfx_start(struct amdgpu_device *adev) +{ + /* init the CP */ + WREG32_SOC15(GC, 0, mmCP_MAX_CONTEXT, + adev->gfx.config.max_hw_contexts - 1); + WREG32_SOC15(GC, 0, mmCP_DEVICE_ID, 1); + + gfx_v10_0_cp_gfx_enable(adev, true); + + if (!adev->csib_initialized) + gfx_v10_csib_submit(adev); + return 0; } @@ -7167,8 +7180,11 @@ static int gfx_v10_0_hw_fini(void *handle) return 0; } +static int gfx_v10_0_set_powergating_state(void *handle, + enum amd_powergating_state state); static int gfx_v10_0_suspend(void *handle) { + gfx_v10_0_set_powergating_state(handle, AMD_CG_STATE_UNGATE); return gfx_v10_0_hw_fini(handle); } diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c index 28e6fa8d7860..c1aeeb927a0b 100644 --- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c +++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c @@ -147,7 +147,7 @@ MODULE_FIRMWARE(FIRMWARE_NAVI12_DMCU); #define PSP_FOOTER_BYTES 0x100 /* Maximum backlight level. */ -#define AMDGPU_MAX_BL_LEVEL 0xFFFF +#define AMDGPU_MAX_BL_LEVEL 0xFFF /** * DOC: overview @@ -7349,7 +7349,8 @@ static int amdgpu_dm_connector_get_modes(struct drm_connector *connector) drm_add_modes_noedid(connector, 1920, 1080); } else { amdgpu_dm_connector_ddc_get_modes(connector, edid); - amdgpu_dm_connector_add_common_modes(encoder, connector); + if (connector->connector_type != DRM_MODE_CONNECTOR_eDP) + amdgpu_dm_connector_add_common_modes(encoder, connector); amdgpu_dm_connector_add_freesync_modes(connector, edid); } amdgpu_dm_fbc_init(connector); diff --git a/drivers/gpu/drm/amd/display/dc/dce110/dce110_hw_sequencer.c b/drivers/gpu/drm/amd/display/dc/dce110/dce110_hw_sequencer.c index e5f4587aa40c..55bb9fa9eb62 100644 --- a/drivers/gpu/drm/amd/display/dc/dce110/dce110_hw_sequencer.c +++ b/drivers/gpu/drm/amd/display/dc/dce110/dce110_hw_sequencer.c @@ -963,7 +963,9 @@ void dce110_edp_backlight_control( return; } - if (link->panel_cntl) { + if (link->panel_cntl && !(link->dpcd_sink_ext_caps.bits.oled || + link->dpcd_sink_ext_caps.bits.hdr_aux_backlight_control == 1 || + link->dpcd_sink_ext_caps.bits.sdr_aux_backlight_control == 1)) { bool is_backlight_on = link->panel_cntl->funcs->is_panel_backlight_on(link->panel_cntl); if ((enable && is_backlight_on) || (!enable && !is_backlight_on)) { diff --git a/drivers/gpu/drm/amd/display/dc/dcn301/dcn301_init.c b/drivers/gpu/drm/amd/display/dc/dcn301/dcn301_init.c index 81fd50ee97c3..fdbe3d42cd7b 100644 --- a/drivers/gpu/drm/amd/display/dc/dcn301/dcn301_init.c +++ b/drivers/gpu/drm/amd/display/dc/dcn301/dcn301_init.c @@ -75,6 +75,7 @@ static const struct hw_sequencer_funcs dcn301_funcs = { .get_hw_state = dcn10_get_hw_state, .clear_status_bits = dcn10_clear_status_bits, .wait_for_mpcc_disconnect = dcn10_wait_for_mpcc_disconnect, + .edp_backlight_control = dce110_edp_backlight_control, .edp_power_control = dce110_edp_power_control, .edp_wait_for_hpd_ready = dce110_edp_wait_for_hpd_ready, .set_cursor_position = dcn10_set_cursor_position, diff --git a/drivers/gpu/drm/amd/display/dc/link/protocols/link_edp_panel_control.c b/drivers/gpu/drm/amd/display/dc/link/protocols/link_edp_panel_control.c index 2039a345f23a..e4626f2072f0 100644 --- a/drivers/gpu/drm/amd/display/dc/link/protocols/link_edp_panel_control.c +++ b/drivers/gpu/drm/amd/display/dc/link/protocols/link_edp_panel_control.c @@ -266,8 +266,8 @@ bool set_default_brightness_aux(struct dc_link *link) if (link && link->dpcd_sink_ext_caps.bits.oled == 1) { if (!read_default_bl_aux(link, &default_backlight)) default_backlight = 150000; - // if < 5 nits or > 5000, it might be wrong readback - if (default_backlight < 5000 || default_backlight > 5000000) + // if < 1 nits or > 5000, it might be wrong readback + if (default_backlight < 1000 || default_backlight > 5000000) default_backlight = 150000; // return edp_set_backlight_level_nits(link, true, diff --git a/drivers/gpu/drm/amd/include/kgd_pp_interface.h b/drivers/gpu/drm/amd/include/kgd_pp_interface.h index 9f542f6e19ed..ec9e740aa913 100644 --- a/drivers/gpu/drm/amd/include/kgd_pp_interface.h +++ b/drivers/gpu/drm/amd/include/kgd_pp_interface.h @@ -420,6 +420,10 @@ struct amd_pm_funcs { struct dpm_clocks *clock_table); int (*get_smu_prv_buf_details)(void *handle, void **addr, size_t *size); void (*pm_compute_clocks)(void *handle); + /** + * @system_features_control: Enable/disable all SMU features. + */ + int (*system_features_control)(void *handle, bool en); }; struct metrics_table_header { diff --git a/drivers/gpu/drm/amd/pm/amdgpu_dpm.c b/drivers/gpu/drm/amd/pm/amdgpu_dpm.c index 078aaaa53162..5c36fc114e91 100644 --- a/drivers/gpu/drm/amd/pm/amdgpu_dpm.c +++ b/drivers/gpu/drm/amd/pm/amdgpu_dpm.c @@ -180,6 +180,24 @@ int amdgpu_dpm_set_mp1_state(struct amdgpu_device *adev, return ret; } +int amdgpu_dpm_set_rlc_state(struct amdgpu_device *adev, bool en) +{ + int ret = 0; + const struct amd_pm_funcs *pp_funcs = adev->powerplay.pp_funcs; + + if (pp_funcs && pp_funcs->system_features_control) { + mutex_lock(&adev->pm.mutex); + + ret = pp_funcs->system_features_control( + adev->powerplay.pp_handle, + en); + + mutex_unlock(&adev->pm.mutex); + } + + return ret; +} + bool amdgpu_dpm_is_baco_supported(struct amdgpu_device *adev) { const struct amd_pm_funcs *pp_funcs = adev->powerplay.pp_funcs; diff --git a/drivers/gpu/drm/amd/pm/inc/amdgpu_dpm.h b/drivers/gpu/drm/amd/pm/inc/amdgpu_dpm.h index 42172b00be66..a96406c4109f 100644 --- a/drivers/gpu/drm/amd/pm/inc/amdgpu_dpm.h +++ b/drivers/gpu/drm/amd/pm/inc/amdgpu_dpm.h @@ -401,6 +401,8 @@ int amdgpu_dpm_mode1_reset(struct amdgpu_device *adev); int amdgpu_dpm_set_mp1_state(struct amdgpu_device *adev, enum pp_mp1_state mp1_state); +int amdgpu_dpm_set_rlc_state(struct amdgpu_device *adev, bool en); + int amdgpu_dpm_set_gfx_power_up_by_imu(struct amdgpu_device *adev); int amdgpu_dpm_baco_exit(struct amdgpu_device *adev); diff --git a/drivers/gpu/drm/bridge/analogix/Kconfig b/drivers/gpu/drm/bridge/analogix/Kconfig index 173dada218ec..2ad49e625c77 100644 --- a/drivers/gpu/drm/bridge/analogix/Kconfig +++ b/drivers/gpu/drm/bridge/analogix/Kconfig @@ -13,6 +13,12 @@ config DRM_ANALOGIX_ANX6345 ANX6345 transforms the LVTTL RGB output of an application processor to eDP or DisplayPort. +config DRM_ANALOGIX_ANX7580 + tristate "Analogix ANX7580 Decoder" + depends on I2C + help + Support for the Analogix ANX7580 to MIPI DSI bridge. + config DRM_ANALOGIX_ANX78XX tristate "Analogix ANX78XX bridge" select DRM_ANALOGIX_DP diff --git a/drivers/gpu/drm/bridge/analogix/Makefile b/drivers/gpu/drm/bridge/analogix/Makefile index 44da392bb9f9..f7f39671845a 100644 --- a/drivers/gpu/drm/bridge/analogix/Makefile +++ b/drivers/gpu/drm/bridge/analogix/Makefile @@ -3,4 +3,5 @@ analogix_dp-objs := analogix_dp_core.o analogix_dp_reg.o analogix-i2c-dptx.o obj-$(CONFIG_DRM_ANALOGIX_ANX6345) += analogix-anx6345.o obj-$(CONFIG_DRM_ANALOGIX_ANX7625) += anx7625.o obj-$(CONFIG_DRM_ANALOGIX_ANX78XX) += analogix-anx78xx.o +obj-$(CONFIG_DRM_ANALOGIX_ANX7580) += anx7580.o obj-$(CONFIG_DRM_ANALOGIX_DP) += analogix_dp.o diff --git a/drivers/gpu/drm/bridge/analogix/anx7580.c b/drivers/gpu/drm/bridge/analogix/anx7580.c new file mode 100644 index 000000000000..2852f8671f59 --- /dev/null +++ b/drivers/gpu/drm/bridge/analogix/anx7580.c @@ -0,0 +1,623 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright(c) 2023, Valve Software. All rights reserved. + * + */ +#include <linux/gpio/consumer.h> +#include <linux/i2c.h> +#include <linux/interrupt.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/pm.h> +#include <linux/pm_runtime.h> +#include <linux/workqueue.h> +#include <linux/delay.h> +#include <linux/interrupt.h> + +#include "anx7580_regs.h" + +#define LOG_TAG "ANX" + +typedef struct { + struct i2c_client *client; + u8 panel_read_data[512]; + int panel_read_reg_addr, panel_read_num_bytes; + unsigned int read_sid, read_offset, read_len; + uint16_t dp_vtotal; + struct delayed_work work; +} anx7580_t; + +/* i2c functions ported from analogix chicago android driver */ + +static int anx7580_i2c_write_byte( anx7580_t * anx, u8 slave_id, u16 offset_addr, u8 data ) +{ + int ret = 0; + + if ((((slave_id & 0x0F)!=0)&&((offset_addr&0xFF00)!=0))||((offset_addr&0xF000)!=0)) { + pr_err( "%s %s: I2C slave_id or offset_addr ERROR!! %02x %04x\n",LOG_TAG,__func__,slave_id,offset_addr ); + return -EINVAL; + } + + anx->client->addr = ( ANX7580_SLAVE_ID_ADDR >> 1 ); + ret = i2c_smbus_write_byte_data( anx->client, 0x00, (slave_id | (u8)((offset_addr&0x0F00)>>8)) ); + if ( ret < 0 ) { + pr_err("%s %s: failed to write i2c addr=%x\n", LOG_TAG,__func__, anx->client->addr); + }else{ + anx->client->addr = ( ANX7580_OFFSET_ADDR >> 1 ); + ret = i2c_smbus_write_byte_data( anx->client, (u8)(offset_addr&0x00FF), data ); + if ( ret < 0 ) { + pr_err( "%s %s: failed to write i2c addr=%x\n", LOG_TAG,__func__, anx->client->addr ); + } + } + return ret; +} + +static int anx7580_i2c_write_block( anx7580_t * anx, u8 slave_id, u16 offset_addr, u8 length, u8 *p_data ) +{ + int ret = 0; + + if ((((slave_id & 0x0F)!=0)&&((offset_addr&0xFF00)!=0))||((offset_addr&0xF000)!=0)) { + pr_info( "%s %s: I2C slave_id or offset_addr ERROR!! %02x %04x\n",LOG_TAG,__func__,slave_id,offset_addr ); + return -EINVAL; + } + + anx->client->addr = ( ANX7580_SLAVE_ID_ADDR >> 1 ); + ret = i2c_smbus_write_byte_data( anx->client, 0x00, (slave_id | (u8)((offset_addr&0x0F00)>>8)) ); + if (ret < 0) { + pr_err( "%s %s: failed to write i2c addr=%x\n", LOG_TAG,__func__, anx->client->addr ); + }else{ + anx->client->addr = ( ANX7580_OFFSET_ADDR >> 1 ); + ret = i2c_smbus_write_i2c_block_data( anx->client, (u8)(offset_addr&0x00FF), length, p_data ); + if (ret < 0) { + pr_err( "%s %s: failed to write i2c addr=%x\n", LOG_TAG,__func__, anx->client->addr ); + } + } + return ret; +} + +static int anx7580_i2c_write_byte4( anx7580_t * anx, u8 slave_id, u16 offset_addr, u32 data ) +{ + int ret = 0; + u8 buf[4],i; + for ( i=0; i<4; i++ ) { + buf[i] = (u8)(data&0x000000FF); + data = data >> 8; + } + + ret = anx7580_i2c_write_block( anx, slave_id, offset_addr, 4, &buf[0] ); + + return ret; +} + +static int anx7580_i2c_read_byte( anx7580_t * anx, u8 slave_id, u16 offset_addr, u8 *p_data ) +{ + int ret = 0; + + if ((((slave_id & 0x0F)!=0)&&((offset_addr&0xFF00)!=0))||((offset_addr&0xF000)!=0)) { + pr_info( "%s %s: I2C slave_id or offset_addr ERROR!! %02x %04x\n",LOG_TAG,__func__,slave_id,offset_addr ); + return -EINVAL; + } + + anx->client->addr = ( ANX7580_SLAVE_ID_ADDR >> 1 ); + ret = i2c_smbus_write_byte_data( anx->client, 0x00, (slave_id | (u8)((offset_addr&0x0F00)>>8)) ); + if (ret < 0) { + pr_err("%s %s: failed to write i2c addr=%x\n", LOG_TAG,__func__, anx->client->addr); + }else{ + anx->client->addr = ( ANX7580_OFFSET_ADDR >> 1 ); + ret = i2c_smbus_read_byte_data( anx->client, (u8)(offset_addr&0x00FF) ); + if (ret < 0) { + pr_err(" %s %s: failed to write i2c addr=%x\n", LOG_TAG,__func__, anx->client->addr ); + return ret; + } + *p_data = (u8)ret; + } + return ret; +} + +static int anx7580_i2c_read_block( anx7580_t * anx, u8 slave_id, u16 offset_addr, u8 length, u8 *p_data ) +{ + int ret = 0; + + if ((((slave_id & 0x0F)!=0)&&((offset_addr&0xFF00)!=0))||((offset_addr&0xF000)!=0)) { + pr_err( "%s %s: I2C slave_id or offset_addr ERROR!! %02x %04x\n",LOG_TAG,__func__,slave_id,offset_addr ); + return -EINVAL; + } + + anx->client->addr = (ANX7580_SLAVE_ID_ADDR >> 1); + ret = i2c_smbus_write_byte_data( anx->client, 0x00, (slave_id | (u8)((offset_addr&0x0F00)>>8)) ); + if (ret < 0) { + pr_err( "%s %s: failed to write i2c addr=%x\n", LOG_TAG,__func__, anx->client->addr ); + } else { + anx->client->addr = ( ANX7580_OFFSET_ADDR >> 1 ); + ret = i2c_smbus_read_i2c_block_data( anx->client, (u8)(offset_addr&0x00FF), length, p_data ); + if (ret < 0) { + pr_err("%s %s: failed to write i2c addr=%x\n", LOG_TAG,__func__, anx->client->addr); + } + } + return ret; +} + +static int anx7580_i2c_read_byte4( anx7580_t * anx, u8 slave_id, u16 offset_addr, u32 *p_data ) +{ + int ret = 0; + u8 buf[4],i; + + ret = anx7580_i2c_read_block( anx, slave_id, offset_addr, 4, &buf[0] ); + + if ( ret>=0 ) { + *p_data = 0; + for ( i=0; i<3; i++ ){ + *p_data += (u32)(buf[3-i]); + *p_data = *p_data<<8; + } + *p_data += (u32)(buf[3-i]); + } + + return ret; +} + +static void anx7580_panel_dcs_cmd( anx7580_t * anx, uint8_t cmd ) +{ + uint32_t reg_val = DCS_WRITE_NO_PARAM | cmd << 8; + anx7580_i2c_write_byte4( anx, SLAVEID_MIPI_PORT0, GEN_HDR, reg_val ); +} + +static void anx7580_panel_write_immediate( anx7580_t * anx, uint8_t reg, uint8_t val ) +{ + uint32_t reg_val = DCS_WRITE_ONE_PARAM | reg << 8 | val << 16; + anx7580_i2c_write_byte4( anx, SLAVEID_MIPI_PORT0, GEN_HDR, reg_val ); +} + +static void anx7580_panel_write_long( anx7580_t * anx, void * _buf, uint8_t len ) +{ + const uint8_t *buf = _buf; + for ( uint32_t v = 0, l = len; l; ) { + int i; + + // swap byte order in string to meet anx mipi port interface requirements + for ( i = 0; i < 4 && l; i++, l-- ) { + v = ( v >> 8 ) | ( *( buf++ ) << 24 ); + } + anx7580_i2c_write_byte4( anx, SLAVEID_MIPI_PORT0, GEN_PLD_DATA, v >> ( 8 * ( 4 - i ) ) ); + } + anx7580_i2c_write_byte4( anx, SLAVEID_MIPI_PORT0, GEN_HDR, ( ( len ) << 8 ) | DCS_WRITE_LONG ); +} + +//----------------------------------------------------------------------------- +// Reset mipi output block +// Must do this after panel read to clear buffer offset +static int anx7580_mipi_reset( anx7580_t * anx ) +{ + anx7580_i2c_write_byte4( anx, SLAVEID_MIPI_PORT0, PWR_UP, 0 ); + return anx7580_i2c_write_byte4( anx, SLAVEID_MIPI_PORT0, PWR_UP, 1 ); +}; + +//Minimum time between DCS transfers. +//Right now, we are going to indiscriminately wait, since the methodology for +//timing out and verifiying transaction seems to be fraught with peril. +// +//This seems to be pretty reliable in my tests. +#define DCS_INTERWRITE_DELAY_MS 34 + +static uint32_t anx7580_dcs_read( anx7580_t * anx, uint8_t reg, uint8_t len, uint8_t *pay ) +{ + uint32_t data, bytes_copied = 0, returned_bytes = 0; + uint16_t dcs_timeout_msec = 0; + uint32_t fifo_status; + + // request len number of bytes from reg + anx7580_i2c_write_byte4( anx, SLAVEID_MIPI_PORT0, GEN_HDR, ( len << 8 ) | DCS_SET_RETURN_SIZE ); + anx7580_i2c_write_byte4( anx, SLAVEID_MIPI_PORT0, GEN_HDR, ( uint32_t )DCS_READ | ( reg << 8 ) ); + + do + { + anx7580_i2c_read_byte4( anx, SLAVEID_MIPI_PORT0, CMD_PKT_STATUS, &fifo_status ); + msleep(1); + dcs_timeout_msec++; + } + while (( fifo_status & 0x10 ) && ( dcs_timeout_msec < DCS_INTERWRITE_DELAY_MS ) ); + + if ( dcs_timeout_msec >= DCS_INTERWRITE_DELAY_MS ) { + pr_err( "anx7580: MIPI Read timeout\n" ); + return 0; + } + + // get data from anx7580 fifo + do + { + anx7580_i2c_read_byte4(anx, SLAVEID_MIPI_PORT0, GEN_PLD_DATA, &data ); + for ( uint8_t i = 0; i < 4 && ( i + returned_bytes ) < len; i++ ) + { + pay[bytes_copied++] = ( data >> ( i * 8 ) ) & 0xFF; + } + returned_bytes += 4; + anx7580_i2c_read_byte4( anx, SLAVEID_MIPI_PORT0, CMD_PKT_STATUS, &fifo_status ); + } while ( !( fifo_status & 0x10 ) ); + + // must reset mipi buffers after read to correct offset + anx7580_mipi_reset(anx); + + return bytes_copied; +} + +static void anx7580_read_chip_id(anx7580_t * anx) +{ + u8 reg_temp; + u16 reg_int; + + anx7580_i2c_read_byte( anx,SLAVEID_SPI, CHIP_ID_HIGH, ®_temp ); + reg_int = ((((u16)reg_temp)<<8)&0xFF00); + anx7580_i2c_read_byte( anx,SLAVEID_SPI, CHIP_ID_LOW, ®_temp ); + reg_int |= (((u16)reg_temp)&0x00FF); + pr_info( "anx7580: Chip ID = %04X\n", reg_int ); +} + +static u8 anx7580_check_ocm_status(anx7580_t * anx) +{ + u8 reg_temp; + // Check OCM status + anx7580_i2c_read_byte( anx, SLAVEID_SERDES,SERDES_POWER_CONTROL, ®_temp ); + if ( OCM_LOAD_DONE == ( OCM_LOAD_DONE & reg_temp ) ) { + return 1; + } + return 0; +} + +static void anx7580_get_dp_vtotal(anx7580_t * anx, uint16_t * dp_vtotal) +{ + uint8_t high, low; + anx7580_i2c_read_byte( anx, SLAVEID_MAIN_LINK, ADDR_VTOTAL_DBG, &high ); + anx7580_i2c_read_byte( anx, SLAVEID_MAIN_LINK, ADDR_VTOTAL_DBG+4, &low ); + *dp_vtotal = high << 8 | low; +} + +// sysfs interface +static ssize_t bmode_store(struct device *device, + struct device_attribute *attr, + const char *buf, size_t count) +{ + int result; + unsigned int mode; + anx7580_t *anx = dev_get_drvdata(device); + if ( !anx ) { + return 0; + } + + result = sscanf( buf, "%d", &mode ); + if( result == 1 ) { + if ( mode == 1 ){ + anx7580_panel_write_immediate( anx, 0x53, 0xE0 ); // high brightness mode + } else if (mode == 2){ + anx7580_panel_write_immediate( anx, 0x53, 0x28 ); // normal mode, update brightness within 32 frame + } else if (mode == 3){ + anx7580_panel_write_immediate( anx, 0x53, 0x20 ); // normal mode, update brightness within 32 frame + } + } + return count; +} + +static ssize_t bmode_show(struct device *device, + struct device_attribute *attr, char *buf) +{ + uint8_t brightness_mode_reg, count, bytes_copied = 0; + anx7580_t *anx = dev_get_drvdata( device ); + // TODO return current brightness mode + bytes_copied = anx7580_dcs_read( anx, 0x53, 1, &brightness_mode_reg ); + if ( brightness_mode_reg == 0xE0 ) { + count = sprintf( buf, "High Brightness\n"); + } else if ( brightness_mode_reg == 0x28 ) { + count = sprintf( buf, "Normal Brightness 32 frames\n" ); + } else if ( brightness_mode_reg == 0x20 ) { + count = sprintf( buf, "Normal Brightness 1 frames\n" ); + } else { + count = sprintf( buf, "Unknown Brightness mode %x\n", brightness_mode_reg ); + } + return count; +} + +static ssize_t brightness_store(struct device *device, + struct device_attribute *attr, + const char *buf, size_t count) +{ + unsigned int result; + unsigned int brightness; + uint8_t cmd[3]; + anx7580_t *anx = dev_get_drvdata( device ); + + result = sscanf( buf, "%d", &brightness ); + + if ( result == 1 ) { + // limit brightness to 16 bits + brightness &= 0xFFFF; + cmd[0] = 0x51; + cmd[1] = ( brightness & 0xFF00 ) >> 8; + cmd[2] = brightness & 0xFF; + + anx7580_panel_write_long( anx, cmd, 3 ); + } + return count; +} + +static ssize_t brightness_show( struct device *device, struct device_attribute *attr, char *buf ) +{ + uint8_t brightness_reg[2]; + uint8_t count, bytes_copied = 0; + anx7580_t *anx = dev_get_drvdata( device ); + // TODO return current brightness mode + bytes_copied = anx7580_dcs_read( anx, 0x51, 2, brightness_reg ); + if (bytes_copied) { + count = sprintf( buf, "Brightness %d\n", (int)(brightness_reg[0] << 8 | brightness_reg[1]) ); + } else { + count = sprintf( buf, "Brightness Unknwn\n" ); + } + return count; +} + +static ssize_t panel_read_show( struct device *device, struct device_attribute *attr, char *buf ) +{ + int ret; + ssize_t len = 0; + unsigned int i; + anx7580_t *anx = dev_get_drvdata( device ); + ret = anx7580_dcs_read( anx, anx->panel_read_reg_addr, anx->panel_read_num_bytes, anx->panel_read_data ); + if ( ret < 0 ) { + pr_err( "anx7580: failed to read panel reg %u\n", anx->panel_read_reg_addr ); + return ret; + } else { + for (i = 0; i < anx->panel_read_num_bytes; i++) { + ret = scnprintf( buf + len, PAGE_SIZE - len, "%x ", + anx->panel_read_data[i] ); + if ( ret < 0 ) + return ret; + len += ret; + } + buf[len - 1] = '\n'; + return len; + } +} + +static ssize_t panel_read_store( struct device *device, struct device_attribute *attr,const char *buf, size_t count ) +{ + int ret; + anx7580_t *anx = dev_get_drvdata( device ); + ret = sscanf( buf, "%2x %d", &anx->panel_read_reg_addr, &anx->panel_read_num_bytes ); + if(ret != 2) { + pr_err( "anx7580: panel read got wrong number of input parameters from sscanf" ); + return -EINVAL; + } + return count; +} + +static ssize_t panel_write_store( struct device *device, struct device_attribute *attr,const char *buf, size_t count ) +{ + int ret; + int addr, len, i, v; + char char_data[256]; + u8 payload[256]; + anx7580_t *anx = dev_get_drvdata(device); + // get the base parameters + ret = sscanf( buf, "%2x %d %s", &addr, &len, char_data ); + if(ret != 3) { + pr_err( "anx7580: panel write got wrong number of input parameters (%d) from sscanf", ret ); + return -EINVAL; + } + // TODO: for now we limit writes to a total of 256 bytes + if(len >256) { + pr_err( "anx7580: panel write exceed max length %d\n", len ); + return -EINVAL; + } + payload[0] = addr; + // get the data payload + for ( i=0; i<len; i++ ) { + if ( sscanf( char_data + i * 2, "%2x", &v ) != 1 ) break; + payload[i+1] = (unsigned char)v; + } + + if ( len == 0 ) + anx7580_panel_dcs_cmd( anx, payload[0] ); + if ( len == 1 ) + anx7580_panel_write_immediate( anx, payload[0], payload[1] ); + else + anx7580_panel_write_long( anx, payload, len ); + + pr_info( "anx7580: wrote %s to reg %x\n", char_data, addr ); + + return count; +} + +static ssize_t panel_write_show( struct device *device, struct device_attribute *attr, char *buf ) +{ + return 0; +} + +static ssize_t anx7580_read_reg_store( struct device *device, struct device_attribute *attr,const char *buf, size_t count ) +{ + int ret; + anx7580_t *anx = dev_get_drvdata(device); + ret = sscanf( buf, "%x %x %u", &anx->read_sid, &anx->read_offset, &anx->read_len); + if ( ret != 3 ) { + pr_err( "anx7580: invalid reg read params\n" ); + return -EINVAL; + } + return count; +} + +static ssize_t anx7580_read_reg_show( struct device *device, struct device_attribute *attr, char *buf ) +{ + ssize_t len = 0; + unsigned int i, ret; + uint8_t data[256]; + anx7580_t *anx = dev_get_drvdata(device); + anx7580_i2c_read_block(anx, anx->read_sid, anx->read_offset, anx->read_len, data); + for (i = 0; i < anx->read_len; i++) { + ret = scnprintf( buf + len, PAGE_SIZE - len, "%x ", data[i] ); + if ( ret < 0 ) + return ret; + len += ret; + } + buf[len - 1] = '\n'; + return len; +} + +static ssize_t anx7580_write_reg_store( struct device *device, struct device_attribute *attr,const char *buf, size_t count ) +{ + int ret; + unsigned int write_byte_sid, write_byte_offset, write_byte_data; + anx7580_t *anx = dev_get_drvdata(device); + ret = sscanf( buf, "%x %x %x", &write_byte_sid, &write_byte_offset, &write_byte_data); + if ( ret != 3 ) { + pr_err( "anx7580: invalid write byte params\n" ); + return -EINVAL; + } + anx7580_i2c_write_byte(anx, write_byte_sid, write_byte_offset, write_byte_data); + return count; +} + +static ssize_t anx7580_write_reg_show( struct device *device, struct device_attribute *attr, char *buf ) +{ + return 0; +} + +static ssize_t anx7580_write_reg4_store( struct device *device, struct device_attribute *attr,const char *buf, size_t count ) +{ + int ret, i, v; + unsigned int sid, offset; + char char_data[4*2]; + unsigned int reg_value = 0; + anx7580_t *anx = dev_get_drvdata(device); + + // get the base parameters + ret = sscanf( buf, "%x %x %s", &sid, &offset, char_data ); + if(ret != 3) { + pr_err( "anx7580: panel write got wrong number of input parameters (%d) from sscanf", ret ); + return -EINVAL; + } + + // get the data payload + for ( i=0; i<4; i++ ) { + if ( sscanf( char_data + i * 2, "%2x", &v ) != 1 ) break; + reg_value = ( reg_value << (8) ) | (unsigned char)v; + } + // pr_info("anx7580: write4 %x %x = %x\n", sid, offset, reg_value); + anx7580_i2c_write_byte4(anx, sid, offset, reg_value); + return count; +} + +static ssize_t anx7580_write_reg4_show( struct device *device, struct device_attribute *attr, char *buf ) +{ + return 0; +} + +static struct device_attribute anx7580_attrs[] = { + __ATTR(bmode, 0664, bmode_show, bmode_store), + __ATTR(brightness, 0664, brightness_show, brightness_store), + __ATTR(panel_read, 0664, panel_read_show, panel_read_store), + __ATTR(panel_write, 0664, panel_write_show, panel_write_store), + __ATTR(anx7580_read_reg, 0664, anx7580_read_reg_show, anx7580_read_reg_store), + __ATTR(anx7580_write_reg, 0664, anx7580_write_reg_show, anx7580_write_reg_store), + __ATTR(anx7580_write_reg4, 0664, anx7580_write_reg4_show, anx7580_write_reg4_store), +}; + +static int anx7580_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + anx7580_t *anx; + unsigned int i; + int ret; + + if ( !i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_I2C_BLOCK) ) { + pr_err("%s: i2c bus does not support the anx7580\n", __func__); + ret = -ENODEV; + goto exit; + } + + anx = kzalloc(sizeof(anx7580_t), GFP_KERNEL); + if ( !anx ) { + pr_err("%s: failed to allocate driver data\n", __func__); + ret = -ENOMEM; + goto exit; + } + + dev_set_drvdata( &client->dev, anx ); + anx->client = client; + + // anx7580 is powered on by bios, so we assume ocm wait period is complete + if ( anx7580_check_ocm_status(anx) ) + { + // OCM load done + // Read Chip ID + anx7580_read_chip_id(anx); + // store current dp vtotal for future mode changes + anx7580_get_dp_vtotal(anx, &anx->dp_vtotal); + pr_info("anx7580: initial dp vtotal %d\n", anx->dp_vtotal); + } + + for ( i=0; i < ARRAY_SIZE(anx7580_attrs); i++ ) + { + if ( device_create_file(&client->dev, &anx7580_attrs[i]) ) + pr_err( "anx7580: could not create sysfs index %d\n", i ); + } + +exit: + return 0; +} + +static void anx7580_remove(struct i2c_client *client) +{ + anx7580_t *data; + unsigned int i; + data = i2c_get_clientdata(client); + for (i=0; i < ARRAY_SIZE(anx7580_attrs); i++) + { + device_remove_file(&client->dev, &anx7580_attrs[i]); + } + kfree(data); +} + +static int anx7580_runtime_suspend(struct device *dev) +{ + return 0; +} + +static int anx7580_runtime_resume(struct device *dev) +{ + return 0; +} + +static DEFINE_RUNTIME_DEV_PM_OPS(anx7580_pm_ops, anx7580_runtime_suspend, + anx7580_runtime_resume, NULL); + +static const struct acpi_device_id anx7580_acpi_match[] = { + { "ANX7580A", 0 }, + { } +}; +MODULE_DEVICE_TABLE(acpi, anx7580_acpi_match); + +static const struct i2c_device_id anx7580_i2c_ids[] = { + { "anx7580", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, anx7580_i2c_ids); + +static const struct of_device_id anx7580_of_ids[] = { + { .compatible = "analogix,anx7580", }, + { } +}; +MODULE_DEVICE_TABLE(of, anx7580_of_ids); + +static struct i2c_driver anx7580_i2c_driver = { + .driver = { + .name = "anx7580", + .pm = pm_ptr(&anx7580_pm_ops), + .of_match_table = anx7580_of_ids, + .acpi_match_table = anx7580_acpi_match, + }, + .probe = anx7580_probe, + .remove = anx7580_remove, + .id_table = anx7580_i2c_ids, +}; +module_i2c_driver(anx7580_i2c_driver); + +MODULE_AUTHOR("Keith Mikoleit <keithm@valvesoftware.com>"); +MODULE_DESCRIPTION("Analogix ANX7580 DP to MIPI Bridge Interface"); +MODULE_LICENSE("GPL"); \ No newline at end of file diff --git a/drivers/gpu/drm/bridge/analogix/anx7580_regs.h b/drivers/gpu/drm/bridge/analogix/anx7580_regs.h new file mode 100644 index 000000000000..9b51239f90fe --- /dev/null +++ b/drivers/gpu/drm/bridge/analogix/anx7580_regs.h @@ -0,0 +1,1033 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright(c) 2023, Valve Software. All rights reserved. + * + */ + +#ifndef __ANX7580_H__ +#define __ANX7580_H__ + +/* +// From ACPI Tables +l ANX1 (_HID:ANX7580A) for “Select Slave ID”, 7-bit slave address is 0x47 +l ANX2 (_HID:ANX7580D) for “Select Offset”, 7-bit slave address is 0x43 +l GPIO AGPIO90 for interrupt pin ANX_APU_INT# +*/ + +// combine the two endpoints into a single driver by manually modifying the i2c address +#define ANX7580_SLAVE_ID_ADDR 0x8E +#define ANX7580_OFFSET_ADDR 0x86 + +/********* ANX7580 Register **********/ + +#define _BIT0 0x01 +#define _BIT1 0x02 +#define _BIT2 0x04 +#define _BIT3 0x08 +#define _BIT4 0x10 +#define _BIT5 0x20 +#define _BIT6 0x40 +#define _BIT7 0x80 + +// Register Slave ID definition +#define SLAVEID_SPI 0x01 +#define SLAVEID_PPS 0x02 +#define SLAVEID_EDID 0x03 +#define SLAVEID_MIPI_CTRL 0x08 +#define SLAVEID_SERDES 0x0A +#define SLAVEID_DP_TOP 0xA0 +#define SLAVEID_DPCD 0x10 +#define SLAVEID_MAIN_LINK 0x20 +#define SLAVEID_DP_IP 0x30 +#define SLAVEID_AUDIO 0x50 +#define SLAVEID_VIDEO 0x60 +#define SLAVEID_PLL 0x70 +#define SLAVEID_MIPI_PORT0 0xC0 +#define SLAVEID_MIPI_PORT1 0xD0 +#define SLAVEID_MIPI_PORT2 0xE0 +#define SLAVEID_MIPI_PORT3 0xF0 + +//************************************************* +//SLAVEID_SPI 0x0100 register offset_addr definition +//************************************************* +#define R_RAM_CS 0x0000 +#define R_RAM_ADDR_H 0x0001 +#define R_RAM_ADDR_L 0x0002 +#define R_RAM_LEN_H 0x0003 +#define R_RAM_LEN_L 0x0004 +#define R_RAM_CTRL 0x0005 +// bit definition +#define FLASH_DONE _BIT7 +#define BOOT_LOAD_DONE _BIT6 +#define CRC_OK _BIT5 +#define LOAD_DONE _BIT4 +#define DECRYPT_EN _BIT1 +#define LOAD_START _BIT0 + +#define R_FLASH_ADDR_H 0x000F +#define R_FLASH_ADDR_L 0x0010 +#define R_FLASH_ADDR_0 0x0011 +#define R_FLASH_LEN_H 0x0031 +#define R_FLASH_LEN_L 0x0032 +#define R_FLASH_RW_CTRL 0x0033 +// bit definition +#define READ_DELAY_SELECT _BIT7 +#define GENERAL_INSTRUCTION_EN _BIT6 +#define FLASH_ERASE_EN _BIT5 +#define RDID_READ_EN _BIT4 +#define REMS_READ_EN _BIT3 +#define WRITE_STATUS_EN _BIT2 +#define FLASH_READ _BIT1 +#define FLASH_WRITE _BIT0 + +#define R_FLASH_STATUS_0 0x0034 +#define R_FLASH_STATUS_2 0x0036 +// data definition +// Flash operation commands - refer to Table 2 in GD25D10B/05B datasheet +#define WRITE_ENABLE 0x06 +#define WRITE_DISABLE 0x04 +#define DEEP_POWER_DOWN 0xB9 +#define DEEP_PD_REL 0xAB /* Release from Deep Power-Down */ +#define CHIP_ERASE_A 0xC7 +#define CHIP_ERASE_B 0x60 + +#define R_FLASH_STATUS_3 0x0037 +// data definition +#define SECTOR_ERASE 0x20 +#define BLOCK_ERASE_32K 0x52 +#define BLOCK_ERASE_64K 0xD8 + +#define R_FLASH_STATUS_4 0x0038 +// bit definition +/* Status Register Protect bit, operates in conjunction with the Write Protect (WP#) signal */ +/* The SRP bit and WP signal set the device to the Hardware Protected mode. */ +/* When the SRP = 1, and WP# signal is Low, the non-volatile bits of the Status Register (SRP, BP2, BP1, BP0) + become read-only and the Write Status Register (WRSR) instruction is not executed. */ +/* The default value of SRP bit is 0. */ +#define SRP0 _BIT7 + +/* Block Protect bits */ +/* These bits are non-volatile. They define the size of the area to be software protected against Program and Erase commands. */ +/* These bits are written with the Write Status Register (WRSR) command. */ +/* When the (BP4, BP3, BP2, BP1, BP0) bits are set to 1, the relevant memory area becomes protected against Page Program (PP), + Sector Erase (SE), and Block Erase (BE) commands. Refer to Table 1.0 in GD25D10B/05B datasheet for details. */ +/* The (BP4, BP3, BP2, BP1, BP0) bits can be written provided that the Hardware Protected mode has not been set. */ +#define BP4 _BIT6 +#define BP3 _BIT5 +#define BP2 _BIT4 +#define BP1 _BIT3 +#define BP0 _BIT2 + +/* Write Enable Latch bit, indicates the status of the internal Write Enable Latch. */ +/* When WEL bit is 1, the internal Write Enable Latch is set. */ +/* When WEL bit is 0, the internal Write Enable Latch is reset, and Write Status Register, Program or + Erase commands are NOT accepted. */ +/* The default value of WEL bit is 0. */ +#define WEL _BIT1 + +/* Write In Progress bit, indicates whether the memory is busy in program/erase/write status register progress. */ +/* When WIP bit is 1, it means the device is busy in program/erase/write status register progress. */ +/* When WIP bit is 0, it means the device is not in program/erase/write status register progress. */ +/* The default value of WIP bit is 0. */ +#define WIP _BIT0 + +#define R_FLASH_CTRL_0 0x003F +// bit definition +#define AUX_DET _BIT5 + +#define R_DSC_CTRL_0 0x0040 +// bit definition +#define READ_STATUS_EN _BIT7 +#define DSC_EN _BIT0 + + +#define GPIO_CTRL_1 0x0048 +#define GPIO_CTRL_2 0x0049 +// bit definition +#define HDCP14_ST _BIT6 +#define R_AUX_DET_MASK _BIT5 +#define TRAN_ORDER _BIT4 +// data definition +#define PANEL_INFO_MASK 0x1F + +#define GPIO_STATUS_1 0x004B +// bit definition +#define FLASH_WP _BIT0 + +#define FLASH_READ_D0 0x0060 +// data definition +#define FLASH_READ_MAX_LENGTH 0x20 +#define FLASH_WRITE_MAX_LENGTH 0x20 + +#define R_SYS_CTRL_1 0x0081 +#define REG_BAUD_DIV_RATIO 0x0087 +#define REG_OCM_INTR_END_COUNTER 0x0088 +#define OCM_DEBUG_CTRL 0x0089 +// bit definition +#define OCM_INT_GATE _BIT7 +#define OCM_RESET _BIT6 + +#define INT_NOTIFY_MCU0 0x0090 +// bit definition +#define AUX_CABLE_OUT _BIT0 +#define VIDEO_STABLE _BIT1 +#define VIDEO_RE_CALCULATE _BIT2 +#define AUX_CABLE_IN _BIT3 +#define VIDEO_INPUT_EMPTY _BIT4 +#define AUDIO_MN_RST _BIT5 +#define AUDIO_PLL_RST _BIT7 + +#define INT_NOTIFY_MCU1 0x0091 +// bit definition +#define CHIP_STANDBY_MODE _BIT0 +#define CHIP_NORMAL_MODE _BIT1 +#define DP_PHY_CTS_START _BIT2 +#define DP_PHY_CTS_STOP _BIT3 +#define DP_LINK_TRAINING_FAIL _BIT4 + +#define MISC_NOTIFY_OCM1 0x0092 +// bit definition +#define TURN_ON_OCM_MAILBOX _BIT1 + +#define VIDEO_STABLE_DELAY_L 0x0093 +#define VIDEO_STABLE_DELAY_H 0x0094 +#define VIDEO_EMPTY_DELAY 0x0095 + +#define CHIP_ID_HIGH 0x0096 +#define CHIP_ID_LOW 0x0097 +#define OCM_VERSION_MAJOR 0x0098 +#define OCM_BUILD_NUM 0x0099 +#define SECURE_OCM_VERSION 0x009B +#define HDCP_LOAD_STATUS 0x009C +// bit definition +#define OCM_FW_CRC32 _BIT7 +#define HDCP_22_FW_LOAD_DONE _BIT5 +#define HDCP_22_KEY_LOAD_DONE _BIT4 +#define HDCP_14_KEY_LOAD_DONE _BIT3 +#define HDCP_22_FW_CRC32 _BIT2 +#define HDCP_22_KEY_CRC32 _BIT1 +#define HDCP_14_KEY_CRC32 _BIT0 + +#define SW_PANEL_FRAME_RATE 0x009D + +#define MISC_NOTIFY_OCM0 0x009E +// bit definition +#define RESET_DP_PHY_WHEN_VIDEO_MUTE _BIT1 +#define ENABLE_EMPTY_RESET_DP_PHY _BIT2 +#define ENABLE_DP_LS_CHECK _BIT3 +#define ENABLE_STANDBY_MODE _BIT4 +#define AUD_MCLK_ALWAYS_ON _BIT5 +#define PANEL_INFO_SET_DONE _BIT6 +#define MCU_LOAD_DONE _BIT7 + +#define M_VALUE_MULTIPLY 0x009F +#define SW_H_ACTIVE_L 0x00A0 +#define SW_H_ACTIVE_H 0x00A1 +// data definition +#define SW_H_ACTIVE_H_BITS 0x3F + +#define SW_HFP_L 0x00A2 +#define SW_HFP_H 0x00A3 +// data definition +#define SW_HFP_H_BITS 0x0F + +#define SW_HSYNC_L 0x00A4 +#define SW_HSYNC_H 0x00A5 +// data definition +#define SW_HSYNC_H_BITS 0x0F + +#define SW_HBP_L 0x00A6 +#define SW_HBP_H 0x00A7 +// data definition +#define SW_HBP_H_BITS 0x0F + +#define SW_V_ACTIVE_L 0x00A8 +#define SW_V_ACTIVE_H 0x00A9 +// data definition +#define SW_V_ACTIVE_H_BITS 0x3F + +#define SW_VFP_L 0x00AA +#define SW_VFP_H 0x00AB +// data definition +#define SW_VFP_H_BITS 0x0F + +#define SW_VSYNC_L 0x00AC +#define SW_VSYNC_H 0x00AD +// data definition +#define SW_VSYNC_H_BITS 0x0F + +#define SW_VBP_L 0x00AE +#define SW_VBP_H 0x00AF +// data definition +#define SW_VBP_H_BITS 0x0F + +#define SW_PANEL_INFO_0 0x00B0 +// data definition +#define REG_PANEL_COUNT_SHIFT 6 +#define REG_MIPI_TOTAL_PORT_SHIFT 4 +#define REG_MIPI_LANE_COUNT_SHIFT 2 +#define REG_PANEL_VIDEO_MODE_SHIFT 0 +// data definition +#define REG_PANEL_COUNT 0xC0 +#define REG_MIPI_TOTAL_PORT 0x30 +#define REG_MIPI_LANE_COUNT 0x0C +#define REG_PANEL_VIDEO_MODE 0x03 + +#define SW_PANEL_INFO_1 0x00B1 +// data definition +#define REG_PANEL_TRANS_MODE_SHIFT 2 +// data definition +#define REG_PANEL_TRANS_MODE 0x0C +// bit definition +#define REG_PANEL_ORDER _BIT0 +#define REG_PANEL_DSC_MODE _BIT1 +#define VIDEO_BIST_MODE _BIT5 +#define SET_DPHY_TIMING _BIT6 + +#define SW_AUD_MAUD_SVAL_7_0 0xB2 +#define SW_AUD_MAUD_SVAL_15_8 0xB3 +#define SW_AUD_MAUD_SVAL_23_16 0xB4 +#define SW_AUD_NAUD_SVAL_7_0 0xB5 +#define SW_AUD_NAUD_SVAL_15_8 0xB6 +#define SW_AUD_NAUD_SVAL_23_16 0xB7 + +#define TEST_PATTERN_CTRL 0x00ED +// data definition + #define TEST_PATTERN_EN 0x80 + #define DISPLAY_BIST_EN 0x08 + #define TEST_PATTERN_MODE1 0x10 + #define TEST_PATTERN_MODE2 0x20 + #define TEST_PATTERN_MODE3 0x30 + +#define H_BLANK_L 0x00EF +#define H_BLANK_H 0x00F0 + +#define REG_BYTE_CLK_EN 0x00F1 +#define MIPI_CLK_EN 0x00F2 +#define CONFIG_X_ACCESS_FIFO 0x00F6 // config_x(i2c_master) read data out +#define CONFIG_X_CTRL_0 0x00F7 // config_x(i2c_master) device address +#define CONFIG_X_CTRL_1 0x00F8 // config_x(i2c_master) offset +#define CONFIG_X_CTRL_2 0x00F9 +// bit definition +#define CONFIG_X_GLH_SEL _BIT7 +#define CONFIG_X_CMD _BIT6|_BIT5|_BIT4 +#define I2C_BYTE_READ 0x00 +#define I2C_BYTE_WRITE 0x10 +#define I2C_RESET 0x40 +#define CONFIG_X_SPEED _BIT3|_BIT2 +#define CONFIG_X_NO_STOP _BIT1 +#define CONFIG_X_NO_ACK _BIT0 + +#define CONFIG_X_CTRL_3 0x00FA // config_x(i2c_master) access number low byte +#define CONFIG_X_CTRL_4 0x00FB +// bit definition +#define CONFIG_X_DDC_STATE _BIT7|_BIT6|_BIT5|_BIT4|_BIT3 +#define CONFIG_X_MODE _BIT2 +#define CONFIG_X_ACCESS_NUM_HIGH _BIT1|_BIT0 + +#define CONFIG_X_CTRL_5 0x00FC +#define CONFIG_X_CTRL_6 0x00FD +#define OCM_DEBUG 0x00FE +#define R_VERSION 0x00FF + +//************************************************* +//SLAVEID_PPS 0x0200 register offset_addr definition +//************************************************* +#define REG_ADDR_ACTIVE_LINE_CFG_L 0x0014 +#define REG_ADDR_ACTIVE_LINE_CFG_H 0x0015 +// data definition +#define REG_V_ACTIVE_H_BITS 0x3F + +#define REG_ADDR_V_SYNC_CFG 0x0017 +#define REG_ADDR_TOTAL_PIXEL_CFG_L 0x0019 +#define REG_ADDR_TOTAL_PIXEL_CFG_H 0x001A +// data definition +#define REG_H_TOTAL_H_BITS 0x3F + +#define REG_ADDR_ACTIVE_PIXEL_CFG_L 0x001B +#define REG_ADDR_ACTIVE_PIXEL_CFG_H 0x001C +// data definition +#define REG_H_ACTIVE_H_BITS 0x3F + +#define REG_ADDR_H_F_PORCH_CFG_L 0x001D +#define REG_ADDR_H_F_PORCH_CFG_H 0x001E +// data definition +#define REG_HFP_H_BITS 0x0F + +#define REG_ADDR_H_SYNC_CFG_L 0x001F +#define REG_ADDR_H_SYNC_CFG_H 0x0020 +// data definition +#define REG_HSYNC_H_BITS 0x0F + +#define REG_ADDR_H_B_PORCH_CFG_L 0x0021 +#define REG_ADDR_H_B_PORCH_CFG_H 0x0022 +// data definition +#define REG_HBP_H_BITS 0x0F + +#define REG_ADDR_V_F_PORCH_CFG_L 0x0023 +#define REG_ADDR_V_F_PORCH_CFG_H 0x0024 +// data definition +#define REG_VFP_H_BITS 0x0F + +#define REG_ADDR_V_B_PORCH_CFG_L 0x0025 +#define REG_ADDR_V_B_PORCH_CFG_H 0x0026 +// data definition +#define REG_VBP_H_BITS 0x0F + +#define DSC_DEBUG_2 0x007C +// bit definitions +#define FIFO_OF_ERR_BUF00 _BIT7 +#define FIFO_OF_ERR_BUF01 _BIT6 +#define FIFO_OF_ERR_BUF10 _BIT5 +#define FIFO_OF_ERR_BUF11 _BIT4 +#define FIFO_UF_ERR_BUF00 _BIT3 +#define FIFO_UF_ERR_BUF01 _BIT2 +#define FIFO_UF_ERR_BUF10 _BIT1 +#define FIFO_UF_ERR_BUF11 _BIT0 + +#define PPS_REG_0 0x0080 +#define PPS_REG_96 0x00e0 +#define PPS_REG_97 0x00e1 +#define PPS_REG_98 0x00e2 +#define PPS_REG_99 0x00e3 +#define PPS_REG_100 0x00e4 +#define PPS_REG_101 0x00e5 +#define PPS_REG_102 0x00e6 +#define PPS_REG_103 0x00e7 +#define PPS_REG_104 0x00e8 +#define PPS_REG_105 0x00e9 +#define PPS_REG_106 0x00ea +#define PPS_REG_107 0x00eb +#define PPS_REG_108 0x00ec +#define PPS_REG_109 0x00ed +#define PPS_REG_110 0x00ee +#define PPS_REG_111 0x00ef + +#define R_MISC_DEBUG 0x00FB +#define PRE_FILLER_BUF0_15_8 0x00FC +#define PRE_FILLER_BUF0_7_0 0x00FD +#define PRE_FILLER_BUF1_15_8 0x00FE +#define PRE_FILLER_BUF1_7_0 0x00FF + +//************************************************* +//EDID 0x0300 buffer register offset_addr definition +//************************************************* +#define EDID_EXTENSION_BUF 0x0080 + +// Other EDID information +#define EDID_LENGTH 128 +#define EDID_EXTENSION_LENGTH 128 +// EDID position +#define EDID_MANUFACTURER_ID_H 0x08 +#define EDID_MANUFACTURER_ID_L 0x09 +#define EDID_PRODUCT_ID_L 0x0A +#define EDID_PRODUCT_ID_H 0x0B +#define EDID_SERIAL_NUM_L 0x0C +#define EDID_SERIAL_NUM_H 0x0E +#define EDID_WEEK 0x10 +#define EDID_YEAR 0x11 + +#define EDID_DB1_PIXEL_CLOCK_L 0x36 +#define EDID_DB1_PIXEL_CLOCK_H 0x37 + +#define EDID_DB1_BASE 0x36 +#define EDID_DB2_BASE 0x48 +#define EDID_DB3_BASE 0x5A +#define EDID_DB4_BASE 0x6C + +#define EDID_DB_MAX 4 +#define EDID_DB_SIZE 18 +#define EDID_DB_DUMMY 0x10 + +#define EDID_PIXEL_CLK_L 0 +#define EDID_PIXEL_CLK_H 1 +#define EDID_HACTIVE_L 2 +#define EDID_HBP_L 3 +#define EDID_HACT_HBP_H 4 +#define EDID_VACTIVE_L 5 +#define EDID_VBP_L 6 +#define EDID_VACT_VBP_H 7 +#define EDID_HFP_L 8 +#define EDID_HSYNC_L 9 +#define EDID_VFP_VSYNC_L 10 +#define EDID_HFP_HSYNC_VFP_VSYNC_H 11 +#define EDID_H_DISPLAY_SIZE 12 +#define EDID_FEATURES_BITMAP 17 + +#define EDID_EXTERN_PANEL_DATA 0x49 +#define EDID_VFP_MAX_VALUE 63 + +//************************************************* +//SLAVEID_MIPI_CTRL 0x0800 register offset_addr definition +//************************************************* +#define R_MIPI_TX_PORT_PD 0x0000 +// bit definition +#define MIPI_PORT_3_PD _BIT7 +#define MIPI_PORT_2_PD _BIT6 +#define MIPI_PORT_1_PD _BIT5 +#define MIPI_PORT_0_PD _BIT4 + +#define R_MIP_TX_PHY_TIMER_0 0x0001 +#define R_MIP_TX_PHY_TIMER_1 0x0002 +#define R_MIP_TX_PHY_TIMER_2 0x0003 +#define R_MIP_TX_PHY_TIMER_3 0x0004 +#define R_MIP_TX_PHY_TIMER_4 0x0005 +#define R_MIP_TX_PHY_TIMER_5 0x0006 +#define R_MIP_TX_PHY_TIMER_6 0x0007 +#define R_MIP_TX_PHY_TIMER_7 0x0008 +#define R_MIP_TX_PHY_TIMER_8 0x0009 +#define R_MIP_TX_PHY_TIMER_9 0x000A +#define R_MIP_TX_PHY_TIMER_10 0x000B +#define R_MIP_TX_PHY_TIMER_11 0x000C +#define R_MIP_TX_PHY_TIMER_12 0x000D +#define R_MIP_TX_PHY_TIMER_13 0x000E +#define R_MIP_TX_TLPX_COUNTER 0x0018 +#define R_MIP_TX_SELECT 0x0019 +#define R_MIP_TX_STATE 0x0022 +// bit definition +#define MIPI_TX_STABLE_STATE _BIT0 + +#define R_MIP_TX_INT 0x0023 +// bit definition +#define MIPI_PORT_3_INT _BIT5 +#define MIPI_PORT_2_INT _BIT4 +#define MIPI_PORT_1_INT _BIT3 +#define MIPI_PORT_0_INT _BIT2 +#define MIPI_STABLE_INT _BIT1 +#define MIPI_JITTER_INT _BIT0 + +#define R_MIP_TX_INT_MASK 0x0024 +// bit definition +#define MIPI_STABLE_INT_MASK _BIT1 +#define MIPI_JITTER_INT_MASK _BIT0 + +#define R_MIP_TX_INT_CLR 0x0025 +// bit definition +#define MIPI_STABLE_INT_CLR _BIT1 +#define MIPI_JITTER_INT_CLR _BIT0 + +#define R_MIP_TX_PHY_TIMER_14 0x002A +#define R_MIP_TX_PHY_TIMER_15 0x002B +#define R_MIP_TX_PHY_TIMER_16 0x002C +#define R_MIP_TX_PHY_TIMER_17 0x002D +#define R_MIP_TX_PHY_TIMER_18 0x002E +#define R_MIP_TX_PHY_TIMER_19 0x002F +#define R_MIP_TX_PHY_TIMER_20 0x0030 +#define R_MIP_TX_PHY_TIMER_21 0x0031 +#define R_MIPI_PHY 0x003A + +//************************************************* +//SLAVEID_SERDES 0x0A00 register offset_addr definition +//************************************************* +#define SERDES_REG_2 0x0002 +#define SERDES_REG_3 0x0003 +// bit definition +#define REG_BYPASS_SIGNAL_DET _BIT5 +#define REG_BYPASS_STATE _BIT4 + +#define SERDES_REG_7 0x0007 +#define SERDES_REG_9 0x0009 +// bit definition +#define REG_LANE_OK_SW _BIT4 +#define SWAP_AUX_R _BIT5 +#define SWAP_AUX_T _BIT6 + +#define DPPLL_REG2 0x0022 +// bit definition +#define EXT_INTR_OUTPUT _BIT2 + +#define SERDES_REG_38 0x0026 +// data definition +#define ALL_SET_LANE0 0x00 +#define ALL_SET_LANE1 0x55 +#define ALL_SET_LANE2 0xAA +#define ALL_SET_LANE3 0xFF +// data definition +#define PHY_SINK_TEST_LANE_SEL_0 0x00 +#define PHY_SINK_TEST_LANE_SEL_1 0x10 +#define PHY_SINK_TEST_LANE_SEL_2 0x20 +#define PHY_SINK_TEST_LANE_SEL_3 0x30 + +#define SERDES_SET_1_RX_REG1 0x002B +#define SERDES_SET_2_RX_REG2 0x002C +#define SERDES_SET_5_RX_REG5 0x002F + +#define SERDES_SET_7 0x0031 +#define SERDES_SET_8_RX_REG8 0x0032 +#define SERDES_SET_9_RX_REG9 0x0033 +#define SERDES_SET_13_RX_REG13 0x0037 +#define SERDES_SET_14_RX_REG14 0x0038 +#define SERDES_SET_15_RX_REG15 0x0039 +#define SERDES_POWER_CONTROL 0x003C +// bit definition +#define OCM_LOAD_DONE _BIT4 +#define PD_V10_AUX_PHY _BIT3 +#define PD_V10_DPRX _BIT2 +#define PD_V10_APLL _BIT1 +#define PD_V10_MIPI _BIT0 + + +#define REG0_0_RX_REG0 0x003E +#define REG0_1_RX_REG0 0x003F +#define REG0_2_RX_REG0 0x0040 +#define REG0_3_RX_REG0 0x0041 +#define REG7_0_RX_REG7 0x0042 +#define REG7_1_RX_REG7 0x0043 +#define REG7_2_RX_REG7 0x0044 +#define REG7_3_RX_REG7 0x0045 +#define REG16_0_RX_REG16 0x0046 +#define REG16_1_RX_REG16 0x0047 +#define REG16_2_RX_REG16 0x0048 +#define REG16_3_RX_REG16 0x0049 +#define REG_CR 0x004A + +//************************************************* +//SLAVEID_DP_TOP 0xA000 register offset_addr definition +//************************************************* +#define ADDR_PWD_SEL 0x0000 +#define HDCP1_PWD_SW_EN _BIT4 + +#define ADDR_PWD_CTRL_0 0x0004 +// bit definition +#define PWD_AUX_CH _BIT4 + +#define ADDR_PWD_CTRL_1 0x0008 +// bit definition +#define PWD_HDCP2 _BIT5 +#define PWD_HDCP1 _BIT4 +#define PWD_AUDPLL _BIT3 +#define PWD_AUD _BIT2 + +#define ADDR_RST_SEL_0 0x0010 +// bit definition +#define HDCP1DEC_AUTO_RST _BIT5 +#define HDCP1AUTH_AUTO_RST _BIT4 +#define AUD_AUTO_RST _BIT3 + +#define ADDR_RST_SEL_1 0x0014 +// bit definition +#define MAIN_VID_CHANGE_RST_EN _BIT4 +#define ALIGN_STATUS_RST_EN _BIT3 +#define LANE_DEC_RST_EN _BIT2 +#define AUDIOPLL_AUTO_RST _BIT1 +#define VIDEOPLL_AUTO_RST _BIT0 + + +#define ADDR_RST_CTRL_0 0x0018 +// bit definition +#define RST_SW_RESET _BIT1 +#define RST_LOGIC_RESET _BIT0 + +#define ADDR_RST_CTRL_1 0x001C +// bit definition +#define RST_SERDESCTRL_RESET _BIT7 +#define RST_HDCP2_RESET _BIT6 +#define RST_HDCP1DEC_RESET _BIT5 +#define RST_HDCP1AUTH_RESET _BIT4 +#define RST_VID_RESET _BIT3 +#define RST_MAINLINK_RESET _BIT2 +#define RST_RXTOP_RESET _BIT1 +#define RST_AUX_CH_RESET _BIT0 + +#define ADDR_RST_CTRL_2 0x0020 +// bit definition +#define RST_VIDEOPLLPRE_RESET _BIT4 +#define RST_VIDEOPLL_RESET _BIT3 +#define RST_AUDIOPLL_RESET _BIT2 +#define RST_AUDIOMNADJ_RESET _BIT1 +#define RST_AUDIO_RESET _BIT0 + +#define ADDR_MISC_CTRL 0x0030 +#define ADDR_POWER_STATUS 0x0040 +// bit definition +#define AUX_STATUS _BIT2 +#define MAIN_LINK_STATUS _BIT1 +#define VIDEO_STATUS _BIT0 + +#define ADDR_SW_INTR_CTRL 0x0044 +// bit definition +#define PLL_INTR _BIT7 +#define PLL_INTR_MASK _BIT6 +#define SOFT_INTR _BIT0 + +#define ADDR_INTR 0x0048 +#define ADDR_INTR_MASK 0x004c +#define MAIN_LINK_INTR _BIT0 +#define DP_IP_INTR _BIT1 +#define DPCD_INTR _BIT2 +#define AUDIO_INTR _BIT3 +#define VIDEO_INTR _BIT4 +#define SERDES_INTR _BIT5 +#define HDCP1_INTR _BIT6 +#define VIDEO_ON_INT _BIT7 + +//************************************************* +//SLAVEID_DPCD 0x1000 register offset_addr definition +//************************************************* +#define DPCD_REV 0x0000 +#define MAX_LINK_RATE 0x0001 +#define MAX_LANE_COUNT 0x0002 +#define MAX_DOWNSPREAD 0x0003 +#define DP_PWR_VOLTAGE_CAP 0x0004 +#define DOWN_STREAM_PORT_PRESENT 0x0005 +#define MAIN_LINK_CHANNEL_CODING 0x0006 +#define DOWN_STREAM_PORT_COUNT 0x0007 +#define RECEIVE_PORT0_CAP_0 0x0008 +#define RECEIVE_PORT0_CAP_1 0x0009 +#define RECEIVE_PORT1_CAP_0 0x000A +#define eDP_CONFIGURATION_CAP 0x000D +#define TRAINING_AUX_RD_INTERVAL 0x000E + +#define LINK_BW_SET 0x0100 +// data definition +#define DPCD_BW_1P62G 0x06 +#define DPCD_BW_2P7G 0x0A +#define DPCD_BW_5P4G 0x14 +#define DPCD_BW_6P75G 0x1E + +#define LANE_COUNT_SET 0x0101 +#define TRAINING_PATTERN_SET 0x0102 +// data definition +#define TRAINING_PATTERN_SELECT 0x03 +#define LT_NOT_IN_PROGRESS 0x00 +#define LT_PATTERN_SEQUENCE1 0x01 +#define LT_PATTERN_SEQUENCE2 0x02 + +#define SINK_COUNT 0x0200 +#define LANE_ALIGN_STATUS_UPDATED 0x0204 +// bit definition +#define INTERLANE_ALIGN_DONE _BIT0 + +#define TEST_SINK_MISC 0x0246 +// data definition +#define TEST_CRC_COUNT 0x0F +// bit definition +#define TEST_CRC_SUPPORTED _BIT5 + +#define TEST_SINK 0x0270 +// bit definition +#define TEST_SINK_START _BIT0 +#define PHY_SINK_TEST_LANE_SEL 0x30 +#define PHY_SINK_TEST_LANE_EN _BIT7 +#define PHY_SINK_TEST_LANE_SEL_POS 4 + +#define DPCD_SINK_IEEE_OUI_FIRST 0x0400 +#define DPCD_SINK_IEEE_OUI_SECOND 0x0401 +#define DPCD_SINK_IEEE_OUI_THIRD 0x0402 + +#define ADDR_BW_CHANGE 0x04F1 +#define DPCD_BW_CHANDE _BIT6 + +#define AUX_FLOATING_ZONE 0x04F2 +#define FLOATING_DPCD_0_INTR_0 _BIT0 + +#define DPCD_INTR0 0x04F4 +#define DPCD_INTR1 0x04F5 +#define DPCD_INTR2 0x04F6 +#define DPCD_INTR3 0x04F7 + +#define DPCD_BRANCH_IEEE_OUI_FIRST 0x0500 +#define DPCD_BRANCH_IEEE_OUI_SECOND 0x0501 +#define DPCD_BRANCH_IEEE_OUI_THIRD 0x0502 + +#define DPCD_POWEWR_STATE 0x0600 +// data difinition +#define POWER_STATE_MASK 0x07 +#define POWER_STATE_NORMAL 0x01 +#define POWER_STATE_SLEEP 0x02 +#define POWER_STATE_STANDBY 0x05 + +//************************************************* +//SLAVEID_MAIN_LINK 0x2000 register offset_addr definition +//************************************************* +#define ADDR_HSTART_DBG 0x0048 +#define ADDR_HSW_DBG 0x0050 +#define ADDR_HTOTAL_DBG 0x0058 +#define ADDR_HWIDTH15_8_DBG 0x0060 +#define ADDR_HWIDTH7_0_DBG 0x0064 +#define ADDR_VHEIGHT15_8_DBG 0x0068 +#define ADDR_VHEIGHT7_0_DBG 0x006C +#define ADDR_VSW_DBG 0x0078 +#define ADDR_VTOTAL_DBG 0x0080 +#define ADDR_MAIN_LINK_CTRL 0x009C +// bit definition +#define FORCE_MN_READY _BIT7 +#define FORCE_M_N _BIT6 +#define FIELD_INV_EN _BIT5 +#define MAIN_WRITE_EN _BIT4 +#define REFILL_ON_FIFO_ERROR _BIT3 +#define RESET_ON_FIFO_ERROR _BIT2 +#define OVERFLOW_COMPENSATE_EN _BIT1 +#define UNDERFLOW_COMPENSATE_EN _BIT0 + +#define ADDR_M_FORCE_VALUE_3 0x00A0 +#define ADDR_M_FORCE_VALUE_2 0x00A4 +#define ADDR_M_FORCE_VALUE_1 0x00A8 +#define ADDR_N_FORCE_VALUE_3 0x00AC +#define ADDR_N_FORCE_VALUE_2 0x00B0 +#define ADDR_N_FORCE_VALUE_1 0x00B4 +#define ADDR_VIDEO_ACTIVE_MASK 0x00D4 +// bit defintion +#define VIDEO_UNMUTE_JUDGE_MASK _BIT2 +#define MSA_JUDGE_MASK _BIT1 +#define VIDEO_M_READY_MASK _BIT0 + +#define ADDR_VIDEO_FIFO_PRE_FILLER_L 0x00E8 +#define ADDR_VIDEO_FIFO_PRE_FILLER_H 0x00EC +#define ADDR_AVI_INFOR 0x0100 +#define ADDR_LINK_LAYER_STATE_2 0x0284 +// bit definition +#define MAIN_DATA_OUT_EN _BIT4 + +#define ADDR_VID_M_RPT_23_16 0x0290 +#define ADDR_VID_M_RPT_15_8 0x0294 +#define ADDR_VID_M_RPT_7_0 0x0298 +#define ADDR_NVID_23_16 0x029C +#define ADDR_NVID_15_8 0x02A0 +#define ADDR_NVID_7_0 0x02A4 +#define ADDR_AUD_PACK_STATUS 0x02B4 +// data definition +#define AUDIO_CHANNEL_COUNT 0x07 + +#define ADDR_MAIN_LINK_INTR0 0x02C0 +#define ADDR_MAIN_LINK_INTR1 0x02C4 +#define ADDR_MAIN_LINK_INTR2 0x02C8 +// bit definition +#define BE_CNT_ERR_IN_FRAME_INTR _BIT1 +#define BS_CNT_ERR_IN_FRAME_INTR _BIT0 + +#define ADDR_MAIN_LINK_INTR0_MASK 0x02D0 +// bit definition +#define M_N_AUD_IND _BIT6 +#define VSC_PACKAGE_CHANGE _BIT5 +#define SPD_INFO_CHANGE _BIT4 +#define AVI_INFO_CHANGE _BIT3 +#define MPEG_INFO_CHANGE _BIT2 +#define OTHER_INFO_RECEIVED_INT _BIT1 +#define AUDIO_INFO_CHANGE _BIT0 + +#define ADDR_MAIN_LINK_INTR1_MASK 0x02D4 +// bit definition +#define MSA_UPDATE_INTR _BIT6 +#define AUDIO_CH_COUNT_CHANGE_INT _BIT5 +#define AUDIO_M_N_CHANGE_INT _BIT4 +#define MAIN_LOST_FLAG _BIT3 +#define VOTING_ERROR _BIT2 +#define MAIN_FIFO_OVFL _BIT1 +#define MAIN_FIFO_UNFL _BIT0 + +#define ADDR_MAIN_LINK_INTR2_MASK 0x02D8 +// bit defintion +#define BE_CNT_ERR_IN_LINE_INTR _BIT3 +#define BS_CNT_ERR_IN_LINE_INTR _BIT2 +#define BE_CNT_ERR_IN_FRAME_INTR _BIT1 +#define BS_CNT_ERR_IN_FRAME_INTR _BIT0 + +#define ADDR_MAIN_LINK_INTR0_AEC 0x02E0 +#define ADDR_MAIN_LINK_INTR1_AEC 0x02E4 +#define ADDR_MAIN_LINK_INTR2_AEC 0x02E8 +#define ADDR_MAIN_LINK_STATUS_0 0x02F0 +// bit definition +#define VIDEO_MN_READY_INT _BIT1 +#define VIDEO_UNMUTE_INT _BIT0 + + +//************************************************* +//SLAVEID_DP_IP 0x3000 register offset_addr definition +//************************************************* +#define ADDR_SYSTEM_CTRL_0 0x0028 +// bit definition +#define SYNC_STATUS_SEL _BIT5 +#define ALIGN_LATCH_LOW _BIT4 +#define FORCE_HPD_VALUE _BIT3 +#define FORCE_HPD_EN _BIT2 +#define POL_MAKE_SEL _BIT1 +#define CHK_DU_EN _BIT0 + +#define ADDR_SYSTEM_CTRL_1 0x002C +#define ADDR_SYSTEM_STATUS_1 0x0038 +// bit definition +#define AUX_CMD_RCV _BIT1 +#define AUX_CMD_REPLY _BIT0 + +#define ADDR_RCD_PN_CONVERTE 0x0064 +// bit definition +#define BYPASS_RC_PAT_CHK _BIT5 + +#define ADDR_HDCP2_CTRL 0x0140 +// bit definition +#define HDCP2_FW_EN _BIT5 +#define HDCP_VERSION _BIT6 + +#define ADDR_HDCP2_DEBUG_2 0x014C +#define ADDR_HDCP2_STATUS 0x0154 +// bit definition +#define ENCRYPT_STATUS _BIT1 + +#define ADDR_FLOATING_DPCD_ZONE_0_L 0x0180 +#define ADDR_FLOATING_DPCD_ZONE_0_H 0x0184 +#define ADDR_FLOATING_DPCD_ZONE_1_L 0x0188 +#define ADDR_FLOATING_DPCD_ZONE_1_H 0x018C +#define ADDR_FLOATING_DPCD_ZONE_2_L 0x0190 +#define ADDR_FLOATING_DPCD_ZONE_2_H 0x0194 +#define ADDR_FLOATING_DPCD_ZONE_3_L 0x0198 +#define ADDR_FLOATING_DPCD_ZONE_3_H 0x019C + +#define ADDR_HPD_ACTIVE_WAIT_TIMER 0x0270 + +#define ADDR_AUX_CH_STATUS 0x030C +// bit definition +#define AUX_ADO_INTR_LAT _BIT3 +#define AUX_TIMEOUT_LAT _BIT2 +#define AUX_MII_ERR_LAT _BIT1 +#define AUX_LEN_ERR_LAT _BIT0 + +#define ADDR_DPIP_INTR 0x0320 +#define ADDR_DPIP_INTR_MASK 0x0330 +// bit definition +#define ALIGN_STATUS_UNLOCK_INTR _BIT3 +#define LINK_TRAINING_FAIL_INTR _BIT2 +#define AUX_CH_ERR_INTR _BIT1 +#define VID_FORMAT_CHANGE _BIT0 + +#define ADDR_DPIP_INTR_AEC 0x0340 +#define ADDR_AUX_ADDR_L 0x0368 +// bit definition +#define AUX_ADDR_ERROR 0x40 + +//************************************************* +//SLAVEID_AUDIO 0x5000 register offset_addr definition +//************************************************* +#define ADDR_AUD_MCTRL 0x000C +// bit definition +#define AUD_SOFTMUTE_EN _BIT7 +#define HARD_MUTE_EN _BIT3 +#define AUD_MUTEDONE_INT_ON_UNMUTE _BIT2 +#define AUD_UNMUTE_ON_DE _BIT1 +#define AUD_MUTE _BIT0 + +#define ADDR_AUD_INTR 0x0010 +#define ADDR_AUD_INTR_MASK 0x0014 +// bit definition +#define AUD_CH_STATUS_CHANGE_INT _BIT7 +#define VBID_AUD_MUTE_INTR _BIT6 +#define AUD_FIFO_UND_INT _BIT5 +#define AUD_FIFO_OVER_INT _BIT4 +#define AUD_LINKERR_INT _BIT3 +#define AUD_DECERR_INT _BIT2 +#define AUD_SPDIFERR_INT _BIT1 +#define AUD_MUTEDONE_INT _BIT0 + +#define ADDR_AUD_AAC 0x0044 +// bit definition +#define AAC_EN _BIT0 + +#define ADDR_AUD_INTR_AEC 0x0048 +#define ADDR_AUD_ACR_CTRL_1 0x0050 +// bit definition +#define MAUD_SEL _BIT1 +#define NAUD_SEL _BIT0 + +#define ADDR_NAUD_SVAL_7_0 0x0054 +#define ADDR_NAUD_SVAL_15_8 0x0058 +#define ADDR_NAUD_SVAL_23_16 0x005C +#define ADDR_MAUD_SVAL_7_0 0x0084 +#define ADDR_MAUD_SVAL_15_8 0x0088 +#define ADDR_MAUD_SVAL_23_16 0x008C + +#define ADDR_DBG_AUD_SAMPLE_CNT_7_0 0x00C8 +#define ADDR_DBG_AUD_SAMPLE_CNT_15_8 0x00CC +#define ADDR_AUD_CTRL1 0x00DC +#define ADDR_AUD_CTRL2 0x00E0 +#define ADDR_AUD_AFC_CTRL 0x0100 +// bit definition +#define RE_COUNT_EN _BIT2 +#define STEP_SCALE _BIT1 +#define AFC_FUNC_EN _BIT0 + +#define ADDR_AUD_CTRL4 0x0248 +#define ADDR_DBG_AUD_CS_3 0x0270 +// bit definition +#define AUD_SAMPLE_RATE 0x0F + +//************************************************* +//SLAVEID_VIDEO 0x6000 register offset_addr definition +//************************************************* +#define ADDR_ACT_PIX_LOW 0x0010 +#define ADDR_ACT_PIX_HIGH 0x0014 +#define ADDR_ACT_LINE_LOW 0x0018 +#define ADDR_ACT_LINE_HIGH 0x001C +#define ADDR_VID_STABLE_DET 0x0040 +// bit definition +#define VID_STRM_STABLE_STATUS _BIT2 + +#define ADDR_VID_INT 0x00C0 +#define ADDR_VID_INT_MASK 0x00C4 +// bit definition +#define VSYNC_DET_INT _BIT5 +#define V_RES_CHANGED _BIT4 +#define H_RES_CHANGED _BIT3 +#define SYNC_POL_CHANGED _BIT2 +#define VID_TYPE_CHANGED _BIT1 +#define VID_STRM_STABLE_INT _BIT0 + +//************************************************* +//SLAVEID_PLL 0x7000 register offset_addr definition +//************************************************* +#define ADDR_VPLL_ANALOG_4 0x0010 +#define ADDR_VPLL_CTRL_0 0x0014 +#define ADDR_VPLL_CTRL_2 0x001C +#define ADDR_VPLL_CTRL_3 0x0020 +#define ADDR_PLL_INTR 0x0090 +#define ADDR_PLL_INTR_MASK 0x0094 +// bit definition +#define VPLL_UNLOCK_INT _BIT3 +#define APLL_UNLOCK_INT _BIT2 +#define APLL_INTP_N_CHG _BIT1 +#define APLL_INTP_FS_CHG _BIT0 + +#define ADDR_PLL_INTR_AEC 0x0098 + +//************************************************* +//MIPI TX 0xC000 register offset_addr definition +//************************************************* +#define PWR_UP 0x0004 +#define CLKMGR_CFG 0x0008 +#define DPI_COLOR_CODING 0x0010 +#define DPI_CFG_POL 0x0014 +#define PCKHDL_CFG 0x002C +#define GEN_VCID 0x0030 +#define MODE_CFG 0x0034 +#define VID_MODE_CFG 0x0038 +#define VID_PKT_SIZE 0x003C +#define VID_HSA_TIME 0x0048 +#define VID_HBP_TIME 0x004C +#define VID_HLINE_TIME 0x0050 +#define VID_VSA_LINES 0x0054 +#define VID_VBP_LINES 0x0058 +#define VID_VFP_LINES 0x005C +#define VID_VACTIVE_LINES 0x0060 +#define CMD_MODE_CFG 0x0068 +#define LPCLK_CTRL 0x0094 +#define PHY_TMR_LPCLK_CFG 0x0098 +#define PHY_TMR_CFG 0x009C +#define PHY_RSTZ 0x00A0 +#define PHY_IF_CFG 0x00A4 +#define INT_MSK0 0x00C4 +#define INT_MSK1 0x00C8 +#define GEN_HDR 0x006C +#define GEN_PLD_DATA 0x0070 +#define CMD_PKT_STATUS 0x0074 +// bit definition +#define gen_pld_w_empty _BIT2 + +#define DSC_PARAMETER 0x00F0 + +// MIPI DCS packet types +#define DCS_WRITE_NO_PARAM 0x05 +#define DCS_WRITE_ONE_PARAM 0x15 +#define DCS_WRITE_LONG 0x39 +#define DCS_SET_RETURN_SIZE 0x37 +#define DCS_READ 0x06 + +#endif /* __ANX7580_H__ */ \ No newline at end of file diff --git a/drivers/gpu/drm/drm_panel_orientation_quirks.c b/drivers/gpu/drm/drm_panel_orientation_quirks.c index 0cb646cb04ee..014101e92b5a 100644 --- a/drivers/gpu/drm/drm_panel_orientation_quirks.c +++ b/drivers/gpu/drm/drm_panel_orientation_quirks.c @@ -395,6 +395,13 @@ static const struct dmi_system_id orientation_data[] = { DMI_EXACT_MATCH(DMI_PRODUCT_VERSION, "1"), }, .driver_data = (void *)&lcd800x1280_rightside_up, + }, { /* Valve Steam Deck */ + .matches = { + DMI_EXACT_MATCH(DMI_SYS_VENDOR, "Valve"), + DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "Galileo"), + DMI_EXACT_MATCH(DMI_PRODUCT_VERSION, "1"), + }, + .driver_data = (void *)&lcd800x1280_rightside_up, }, { /* VIOS LTH17 */ .matches = { DMI_EXACT_MATCH(DMI_SYS_VENDOR, "VIOS"), diff --git a/drivers/net/wireless/ath/ath11k/Makefile b/drivers/net/wireless/ath/ath11k/Makefile index cc47e0114595..6bbaa96dc526 100644 --- a/drivers/net/wireless/ath/ath11k/Makefile +++ b/drivers/net/wireless/ath/ath11k/Makefile @@ -17,7 +17,9 @@ ath11k-y += core.o \ peer.o \ dbring.o \ hw.o \ - pcic.o + pcic.o \ + wow.o \ + unitest.o ath11k-$(CONFIG_ATH11K_DEBUGFS) += debugfs.o debugfs_htt_stats.o debugfs_sta.o ath11k-$(CONFIG_NL80211_TESTMODE) += testmode.o @@ -30,7 +32,7 @@ obj-$(CONFIG_ATH11K_AHB) += ath11k_ahb.o ath11k_ahb-y += ahb.o obj-$(CONFIG_ATH11K_PCI) += ath11k_pci.o -ath11k_pci-y += mhi.o pci.o +ath11k_pci-y += mhi.o pci.o coredump_fw.o coredump.o # for tracing framework to find trace.h CFLAGS_trace.o := -I$(src) diff --git a/drivers/net/wireless/ath/ath11k/ahb.c b/drivers/net/wireless/ath/ath11k/ahb.c index 76f275ca53e9..fa460d3fe5e2 100644 --- a/drivers/net/wireless/ath/ath11k/ahb.c +++ b/drivers/net/wireless/ath/ath11k/ahb.c @@ -1,7 +1,7 @@ // SPDX-License-Identifier: BSD-3-Clause-Clear /* * Copyright (c) 2018-2019 The Linux Foundation. All rights reserved. - * Copyright (c) 2022 Qualcomm Innovation Center, Inc. All rights reserved. + * Copyright (c) 2022-2023 Qualcomm Innovation Center, Inc. All rights reserved. */ #include <linux/module.h> @@ -1140,6 +1140,7 @@ static int ath11k_ahb_probe(struct platform_device *pdev) ab->hif.ops = hif_ops; ab->pdev = pdev; ab->hw_rev = hw_rev; + ab->fw_mode = ATH11K_FIRMWARE_MODE_NORMAL; platform_set_drvdata(pdev, ab); ret = ath11k_pcic_register_pci_ops(ab, pci_ops); diff --git a/drivers/net/wireless/ath/ath11k/ce.c b/drivers/net/wireless/ath/ath11k/ce.c index f2da95fd4253..6abcacbcb672 100644 --- a/drivers/net/wireless/ath/ath11k/ce.c +++ b/drivers/net/wireless/ath/ath11k/ce.c @@ -627,9 +627,9 @@ ath11k_ce_alloc_ring(struct ath11k_base *ab, int nentries, int desc_sz) * coherent DMA are unsupported */ ce_ring->base_addr_owner_space_unaligned = - dma_alloc_coherent(ab->dev, - nentries * desc_sz + CE_DESC_RING_ALIGN, - &base_addr, GFP_KERNEL); + ath11k_core_dma_alloc_coherent(ab->dev, + nentries * desc_sz + CE_DESC_RING_ALIGN, + &base_addr, GFP_KERNEL | GFP_DMA32); if (!ce_ring->base_addr_owner_space_unaligned) { kfree(ce_ring); return ERR_PTR(-ENOMEM); diff --git a/drivers/net/wireless/ath/ath11k/core.c b/drivers/net/wireless/ath/ath11k/core.c index 893fefadbba9..7e4dafdafb80 100644 --- a/drivers/net/wireless/ath/ath11k/core.c +++ b/drivers/net/wireless/ath/ath11k/core.c @@ -1,7 +1,7 @@ // SPDX-License-Identifier: BSD-3-Clause-Clear /* * Copyright (c) 2018-2019 The Linux Foundation. All rights reserved. - * Copyright (c) 2021-2022 Qualcomm Innovation Center, Inc. All rights reserved. + * Copyright (c) 2021-2023 Qualcomm Innovation Center, Inc. All rights reserved. */ #include <linux/module.h> @@ -9,6 +9,7 @@ #include <linux/remoteproc.h> #include <linux/firmware.h> #include <linux/of.h> +#include <linux/dma-direct.h> #include "core.h" #include "dp_tx.h" @@ -16,6 +17,7 @@ #include "debug.h" #include "hif.h" #include "wow.h" +#include "wmi.h" unsigned int ath11k_debug_mask; EXPORT_SYMBOL(ath11k_debug_mask); @@ -32,6 +34,10 @@ module_param_named(frame_mode, ath11k_frame_mode, uint, 0644); MODULE_PARM_DESC(frame_mode, "Datapath frame mode (0: raw, 1: native wifi (default), 2: ethernet)"); +unsigned int ath11k_ftm_mode; +module_param_named(ftm_mode, ath11k_ftm_mode, uint, 0444); +MODULE_PARM_DESC(ftm_mode, "Boots up in factory test mode"); + static const struct ath11k_hw_params ath11k_hw_params[] = { { .hw_rev = ATH11K_HW_IPQ8074, @@ -115,6 +121,7 @@ static const struct ath11k_hw_params ath11k_hw_params[] = { .tcl_ring_retry = true, .tx_ring_size = DP_TCL_DATA_RING_SIZE, .smp2p_wow_exit = false, + .coex_isolation = false, }, { .hw_rev = ATH11K_HW_IPQ6018_HW10, @@ -195,6 +202,8 @@ static const struct ath11k_hw_params ath11k_hw_params[] = { .tcl_ring_retry = true, .tx_ring_size = DP_TCL_DATA_RING_SIZE, .smp2p_wow_exit = false, + .coex_isolation = false, + .support_fw_mac_sequence = false, }, { .name = "qca6390 hw2.0", @@ -277,6 +286,8 @@ static const struct ath11k_hw_params ath11k_hw_params[] = { .tcl_ring_retry = true, .tx_ring_size = DP_TCL_DATA_RING_SIZE, .smp2p_wow_exit = false, + .coex_isolation = false, + .support_fw_mac_sequence = true, }, { .name = "qcn9074 hw1.0", @@ -356,6 +367,8 @@ static const struct ath11k_hw_params ath11k_hw_params[] = { .tcl_ring_retry = true, .tx_ring_size = DP_TCL_DATA_RING_SIZE, .smp2p_wow_exit = false, + .coex_isolation = false, + .support_fw_mac_sequence = false, }, { .name = "wcn6855 hw2.0", @@ -402,6 +415,77 @@ static const struct ath11k_hw_params ath11k_hw_params[] = { .idle_ps = true, .supports_sta_ps = true, .cold_boot_calib = false, + .fw_mem_mode = 0, + .num_vdevs = 16 + 1, + .num_peers = 512, + .supports_suspend = true, + .hal_desc_sz = sizeof(struct hal_rx_desc_wcn6855), + .supports_regdb = true, + .fix_l1ss = false, + .credit_flow = true, + .max_tx_ring = DP_TCL_NUM_RING_MAX_QCA6390, + .hal_params = &ath11k_hw_hal_params_qca6390, + .supports_dynamic_smps_6ghz = false, + .alloc_cacheable_memory = false, + .supports_rssi_stats = true, + .fw_wmi_diag_event = true, + .current_cc_support = true, + .dbr_debug_support = false, + .coex_isolation = false, + .global_reset = true, + .bios_sar_capa = &ath11k_hw_sar_capa_wcn6855, + .m3_fw_support = true, + .fixed_bdf_addr = false, + .fixed_mem_region = false, + .static_window_map = false, + .hybrid_bus_type = false, + .fixed_fw_mem = false, + .support_off_channel_tx = true, + }, + { + .name = "qca206x hw2.1", + .hw_rev = ATH11K_HW_QCA206X_HW21, + .fw = { + .dir = "QCA206X/hw2.1", + .board_size = 256 * 1024, + .cal_offset = 128 * 1024, + }, + .max_radios = 3, + .bdf_addr = 0x4B0C0000, + .hw_ops = &wcn6855_ops, + .ring_mask = &ath11k_hw_ring_mask_qca6390, + .internal_sleep_clock = true, + .regs = &wcn6855_regs, + .qmi_service_ins_id = ATH11K_QMI_WLFW_SERVICE_INS_ID_V01_QCA6390, + .host_ce_config = ath11k_host_ce_config_qca6390, + .ce_count = 9, + .target_ce_config = ath11k_target_ce_config_wlan_qca6390, + .target_ce_count = 9, + .svc_to_ce_map = ath11k_target_service_to_ce_map_wlan_qca6390, + .svc_to_ce_map_len = 14, + .single_pdev_only = true, + .rxdma1_enable = false, + .num_rxmda_per_pdev = 2, + .rx_mac_buf_ring = true, + .vdev_start_delay = true, + .htt_peer_map_v2 = false, + + .spectral = { + .fft_sz = 0, + .fft_pad_sz = 0, + .summary_pad_sz = 0, + .fft_hdr_len = 0, + .max_fft_bins = 0, + }, + + .interface_modes = BIT(NL80211_IFTYPE_STATION) | + BIT(NL80211_IFTYPE_AP), + .supports_monitor = false, + .full_monitor_mode = false, + .supports_shadow_regs = true, + .idle_ps = true, + .supports_sta_ps = true, + .cold_boot_calib = false, .cbcal_restart_fw = false, .fw_mem_mode = 0, .num_vdevs = 16 + 1, @@ -438,6 +522,8 @@ static const struct ath11k_hw_params ath11k_hw_params[] = { .tcl_ring_retry = true, .tx_ring_size = DP_TCL_DATA_RING_SIZE, .smp2p_wow_exit = false, + .coex_isolation = true, + .support_fw_mac_sequence = true, }, { .name = "wcn6855 hw2.1", @@ -479,6 +565,7 @@ static const struct ath11k_hw_params ath11k_hw_params[] = { .interface_modes = BIT(NL80211_IFTYPE_STATION) | BIT(NL80211_IFTYPE_AP), .supports_monitor = false, + .full_monitor_mode = false, .supports_shadow_regs = true, .idle_ps = true, .supports_sta_ps = true, @@ -519,6 +606,8 @@ static const struct ath11k_hw_params ath11k_hw_params[] = { .tcl_ring_retry = true, .tx_ring_size = DP_TCL_DATA_RING_SIZE, .smp2p_wow_exit = false, + .coex_isolation = false, + .support_fw_mac_sequence = true, }, { .name = "wcn6750 hw1.0", @@ -597,9 +686,45 @@ static const struct ath11k_hw_params ath11k_hw_params[] = { .tcl_ring_retry = false, .tx_ring_size = DP_TCL_DATA_RING_SIZE_WCN6750, .smp2p_wow_exit = true, + .coex_isolation = false, + .support_fw_mac_sequence = true, }, }; +static const struct dma_map_ops *ath11k_core_get_dma_ops(struct device *dev) +{ + if (dev->dma_ops) + return dev->dma_ops; + return NULL; +} + +void *ath11k_core_dma_alloc_coherent(struct device *dev, size_t size, + dma_addr_t *dma_handle, gfp_t flag) +{ + unsigned long attrs = (flag & __GFP_NOWARN) ? DMA_ATTR_NO_WARN : 0; + const struct dma_map_ops *ops = ath11k_core_get_dma_ops(dev); + void *cpu_addr; + + WARN_ON_ONCE(!dev->coherent_dma_mask); + + /* + * DMA allocations can never be turned back into a page pointer, so + * requesting compound pages doesn't make sense (and can't even be + * supported at all by various backends). + */ + if (WARN_ON_ONCE(flag & __GFP_COMP)) + return NULL; + + if (!ops) + cpu_addr = dma_direct_alloc(dev, size, dma_handle, flag, attrs); + else if (ops->alloc) + cpu_addr = ops->alloc(dev, size, dma_handle, flag, attrs); + else + return NULL; + + return cpu_addr; +} + static inline struct ath11k_pdev *ath11k_core_get_single_pdev(struct ath11k_base *ab) { WARN_ON(!ab->hw_params.single_pdev_only); @@ -1290,6 +1415,11 @@ static int ath11k_core_soc_create(struct ath11k_base *ab) { int ret; + if (ath11k_ftm_mode) { + ab->fw_mode = ATH11K_FIRMWARE_MODE_FTM; + ath11k_info(ab, "Booting in ftm mode\n"); + } + ret = ath11k_qmi_init_service(ab); if (ret) { ath11k_err(ab, "failed to initialize qmi :%d\n", ret); @@ -1384,6 +1514,30 @@ static void ath11k_core_pdev_destroy(struct ath11k_base *ab) ath11k_debugfs_pdev_destroy(ab); } +static int ath11k_core_config_coex_isolation(struct ath11k_base *ab) +{ + struct ath11k *ar = ath11k_ab_to_ar(ab, 0); + struct wmi_coex_config_params param; + + memset(¶m, 0, sizeof(struct wmi_coex_config_params)); + param.config_type = WMI_COEX_CONFIG_ANTENNA_ISOLATION; + param.config_arg1 = WMI_COEX_ISOLATION_ARG1_DEFAUT; + + return ath11k_wmi_send_coex_config(ar, ¶m); +} + +static int ath11k_core_config_btc_mode(struct ath11k_base *ab) +{ + struct ath11k *ar = ath11k_ab_to_ar(ab, 0); + struct wmi_coex_config_params param; + + memset(¶m, 0, sizeof(struct wmi_coex_config_params)); + param.config_type = WMI_COEX_CONFIG_BTC_MODE; + param.config_arg1 = WMI_COEX_BTC_MODE_ARG1_DEFAULT; + + return ath11k_wmi_send_coex_config(ar, ¶m); +} + static int ath11k_core_start(struct ath11k_base *ab) { int ret; @@ -1481,6 +1635,22 @@ static int ath11k_core_start(struct ath11k_base *ab) goto err_reo_cleanup; } + if (ab->hw_params.coex_isolation) { + ret = ath11k_core_config_coex_isolation(ab); + if (ret) { + ath11k_err(ab, "failed to set coex isolation: %d\n", + ret); + goto err_reo_cleanup; + } + } + + ret = ath11k_core_config_btc_mode(ab); + if (ret) { + ath11k_err(ab, "failed to set btc mode: %d\n", + ret); + goto err_reo_cleanup; + } + return 0; err_reo_cleanup: @@ -1516,7 +1686,7 @@ int ath11k_core_qmi_firmware_ready(struct ath11k_base *ab) { int ret; - ret = ath11k_core_start_firmware(ab, ATH11K_FIRMWARE_MODE_NORMAL); + ret = ath11k_core_start_firmware(ab, ab->fw_mode); if (ret) { ath11k_err(ab, "failed to start firmware: %d\n", ret); return ret; @@ -1630,6 +1800,7 @@ void ath11k_core_halt(struct ath11k *ar) ath11k_mac_scan_finish(ar); ath11k_mac_peer_cleanup_all(ar); cancel_delayed_work_sync(&ar->scan.timeout); + cancel_work_sync(&ar->channel_update_work); cancel_work_sync(&ar->regd_update_work); cancel_work_sync(&ab->update_11d_work); @@ -1668,7 +1839,7 @@ static void ath11k_update_11d(struct work_struct *work) } } -static void ath11k_core_pre_reconfigure_recovery(struct ath11k_base *ab) +void ath11k_core_pre_reconfigure_recovery(struct ath11k_base *ab) { struct ath11k *ar; struct ath11k_pdev *pdev; @@ -1681,7 +1852,8 @@ static void ath11k_core_pre_reconfigure_recovery(struct ath11k_base *ab) for (i = 0; i < ab->num_radios; i++) { pdev = &ab->pdevs[i]; ar = pdev->ar; - if (!ar || ar->state == ATH11K_STATE_OFF) + if (!ar || ar->state == ATH11K_STATE_OFF || + ar->state == ATH11K_STATE_TM) continue; ieee80211_stop_queues(ar->hw); @@ -1746,7 +1918,11 @@ static void ath11k_core_post_reconfigure_recovery(struct ath11k_base *ab) ath11k_warn(ab, "device is wedged, will not restart radio %d\n", i); break; + case ATH11K_STATE_TM: + ath11k_warn(ab, "fw mode reset done radio %d\n", i); + break; } + mutex_unlock(&ar->conf_mutex); } complete(&ab->driver_recovery); @@ -1757,9 +1933,6 @@ static void ath11k_core_restart(struct work_struct *work) struct ath11k_base *ab = container_of(work, struct ath11k_base, restart_work); int ret; - if (!ab->is_reset) - ath11k_core_pre_reconfigure_recovery(ab); - ret = ath11k_core_reconfigure_on_crash(ab); if (ret) { ath11k_err(ab, "failed to reconfigure driver on crash recovery\n"); diff --git a/drivers/net/wireless/ath/ath11k/core.h b/drivers/net/wireless/ath/ath11k/core.h index bd06536f82a6..0fa7eec855d3 100644 --- a/drivers/net/wireless/ath/ath11k/core.h +++ b/drivers/net/wireless/ath/ath11k/core.h @@ -1,7 +1,7 @@ /* SPDX-License-Identifier: BSD-3-Clause-Clear */ /* * Copyright (c) 2018-2019 The Linux Foundation. All rights reserved. - * Copyright (c) 2021-2022 Qualcomm Innovation Center, Inc. All rights reserved. + * Copyright (c) 2021-2023 Qualcomm Innovation Center, Inc. All rights reserved. */ #ifndef ATH11K_CORE_H @@ -142,6 +142,7 @@ enum ath11k_hw_rev { ATH11K_HW_WCN6855_HW20, ATH11K_HW_WCN6855_HW21, ATH11K_HW_WCN6750_HW10, + ATH11K_HW_QCA206X_HW21, }; enum ath11k_firmware_mode { @@ -269,6 +270,7 @@ enum ath11k_dev_flags { ATH11K_FLAG_FIXED_MEM_RGN, ATH11K_FLAG_DEVICE_INIT_DONE, ATH11K_FLAG_MULTI_MSI_VECTORS, + ATH11K_FLAG_FTM_SEGMENTED, }; enum ath11k_monitor_flags { @@ -301,6 +303,43 @@ struct ath11k_rekey_data { bool enable_offload; }; +/** + * struct chan_power_info - TPE containing power info per channel chunk + * @chan_cfreq: channel center freq (MHz) + * e.g. + * channel 37/20 MHz, it is 6135 + * channel 37/40 MHz, it is 6125 + * channel 37/80 MHz, it is 6145 + * channel 37/160 MHz, it is 6185 + * @tx_power: transmit power (dBm) + */ +struct chan_power_info { + u16 chan_cfreq; + s8 tx_power; +}; + +/** + * struct reg_tpc_power_info - regulatory TPC power info + * @is_psd_power: is PSD power or not + * @eirp_power: Maximum EIRP power (dBm), valid only if power is PSD + * @power_type_6g: type of power (SP/LPI/VLP) + * @num_pwr_levels: number of power levels + * @reg_max: Array of maximum TX power (dBm) per PSD value + * @ap_constraint_power: AP constraint power (dBm) + * @tpe: TPE values processed from TPE IE + * @chan_power_info: power info to send to firmware + */ +struct ath11k_reg_tpc_power_info { + bool is_psd_power; + u8 eirp_power; + enum wmi_reg_6ghz_ap_type power_type_6g; + u8 num_pwr_levels; + u8 reg_max[IEEE80211_MAX_NUM_PWR_LEVEL]; + u8 ap_constraint_power; + s8 tpe[IEEE80211_MAX_NUM_PWR_LEVEL]; + struct chan_power_info chan_power_info[IEEE80211_MAX_NUM_PWR_LEVEL]; +}; + struct ath11k_vif { u32 vdev_id; enum wmi_vdev_type vdev_type; @@ -358,6 +397,7 @@ struct ath11k_vif { #ifdef CONFIG_ATH11K_DEBUGFS struct dentry *debugfs_twt; #endif /* CONFIG_ATH11K_DEBUGFS */ + struct ath11k_reg_tpc_power_info reg_tpc_info; }; struct ath11k_vif_iter { @@ -521,6 +561,7 @@ enum ath11k_state { ATH11K_STATE_RESTARTING, ATH11K_STATE_RESTARTED, ATH11K_STATE_WEDGED, + ATH11K_STATE_TM, /* Add other states as required */ }; @@ -531,6 +572,12 @@ enum ath11k_state { #define ATH11K_INVALID_RSSI_EMPTY -128 +struct ath11k_ftm_event_obj { + u32 data_pos; + u32 expected_seq; + u8 *eventdata; +}; + struct ath11k_fw_stats { struct dentry *debugfs_fwstats; u32 pdev_id; @@ -685,6 +732,10 @@ struct ath11k { struct completion bss_survey_done; struct work_struct regd_update_work; + struct work_struct channel_update_work; + struct list_head channel_update_queue; + /* protects channel_update_queue data */ + spinlock_t channel_update_lock; struct work_struct wmi_mgmt_tx_work; struct sk_buff_head wmi_mgmt_tx_queue; @@ -700,6 +751,8 @@ struct ath11k { u32 last_ppdu_id; u32 cached_ppdu_id; int monitor_vdev_id; + struct completion fw_mode_reset; + u8 ftm_msgref; #ifdef CONFIG_ATH11K_DEBUGFS struct ath11k_debug debug; #endif @@ -723,6 +776,7 @@ struct ath11k { /* protected by conf_mutex */ bool ps_state_enable; bool ps_timekeeper_enable; + s8 max_allowed_tx_power; }; struct ath11k_band_cap { @@ -826,9 +880,20 @@ struct ath11k_msi_config { u16 hw_rev; }; +struct fw_remote_mem { + size_t size; + void *vaddr; +}; + +struct fw_remote_crash_data { + u8 *remote_buf; + size_t remote_buf_len; +}; + /* Master structure to hold the hw data which may be used in core module */ struct ath11k_base { enum ath11k_hw_rev hw_rev; + enum ath11k_firmware_mode fw_mode; struct platform_device *pdev; struct device *dev; struct ath11k_qmi qmi; @@ -907,6 +972,7 @@ struct ath11k_base { * This may or may not be used during the runtime */ struct ieee80211_regdomain *new_regd[MAX_RADIOS]; + struct cur_regulatory_info *reg_info_store; /* Current DFS Regulatory */ enum ath11k_dfs_region dfs_region; @@ -938,6 +1004,7 @@ struct ath11k_base { u32 fw_crash_counter; } stats; u32 pktlog_defs_checksum; + struct ath11k_ftm_event_obj ftm_event_obj; struct ath11k_dbring_cap *db_caps; u32 num_db_cap; @@ -968,6 +1035,9 @@ struct ath11k_base { const struct ath11k_pci_ops *ops; } pci; + struct fw_remote_mem remote_mem[ATH11K_QMI_WLANFW_MAX_NUM_MEM_SEG_V01]; + struct fw_remote_crash_data remote_crash_data; + /* must be last */ u8 drv_priv[] __aligned(sizeof(void *)); }; @@ -1158,6 +1228,7 @@ int ath11k_core_check_smbios(struct ath11k_base *ab); void ath11k_core_halt(struct ath11k *ar); int ath11k_core_resume(struct ath11k_base *ab); int ath11k_core_suspend(struct ath11k_base *ab); +void ath11k_core_pre_reconfigure_recovery(struct ath11k_base *ab); const struct firmware *ath11k_core_firmware_request(struct ath11k_base *ab, const char *filename); @@ -1222,4 +1293,6 @@ static inline const char *ath11k_bus_str(enum ath11k_bus bus) return "unknown"; } +void *ath11k_core_dma_alloc_coherent(struct device *dev, size_t size, + dma_addr_t *dma_handle, gfp_t flag); #endif /* _CORE_H_ */ diff --git a/drivers/net/wireless/ath/ath11k/coredump.c b/drivers/net/wireless/ath/ath11k/coredump.c new file mode 100644 index 000000000000..32d37dda570c --- /dev/null +++ b/drivers/net/wireless/ath/ath11k/coredump.c @@ -0,0 +1,159 @@ +// SPDX-License-Identifier: BSD-3-Clause-Clear +/* + * Copyright (c) 2020 The Linux Foundation. All rights reserved. + */ + +#include <linux/devcoredump.h> +#include "coredump.h" +#include "pci.h" +#include "debug.h" + +static struct ath11k_dump_file_data * +ath11k_coredump_build(struct ath11k_mhi_fw_crash_data *crash_data, + struct fw_remote_crash_data *remote_crash_data, + struct register_crash_data *reg_crash_data) +{ + struct ath11k_dump_file_data *dump_data; + struct ath11k_tlv_dump_data *dump_tlv; + size_t hdr_len = sizeof(*dump_data); + size_t len, sofar = 0; + unsigned char *buf; + struct timespec64 timestamp; + + len = hdr_len; + + len += sizeof(*dump_tlv) + crash_data->paging_dump_buf_len; + len += sizeof(*dump_tlv) + crash_data->ramdump_buf_len; + len += sizeof(*dump_tlv) + remote_crash_data->remote_buf_len; + len += sizeof(*dump_tlv) + reg_crash_data->reg_buf_len; + len += sizeof(*dump_tlv) + reg_crash_data->reg_rddm_buf_len; + sofar += hdr_len; + + buf = vzalloc(len); + if (!buf) + return NULL; + + dump_data = (struct ath11k_dump_file_data *)(buf); + strlcpy(dump_data->df_magic, "ATH11K-FW-DUMP", + sizeof(dump_data->df_magic)); + dump_data->len = cpu_to_le32(len); + dump_data->version = cpu_to_le32(ATH11K_FW_CRASH_DUMP_VERSION); + guid_gen(&dump_data->guid); + ktime_get_real_ts64(×tamp); + dump_data->tv_sec = cpu_to_le64(timestamp.tv_sec); + dump_data->tv_nsec = cpu_to_le64(timestamp.tv_nsec); + + /* Gather FW paging dump */ + dump_tlv = (struct ath11k_tlv_dump_data *)(buf + sofar); + dump_tlv->type = cpu_to_le32(ATH11K_FW_CRASH_PAGING_DATA); + dump_tlv->tlv_len = cpu_to_le32(crash_data->paging_dump_buf_len); + memcpy(dump_tlv->tlv_data, crash_data->paging_dump_buf, + crash_data->paging_dump_buf_len); + sofar += sizeof(*dump_tlv) + crash_data->paging_dump_buf_len; + + /* Gather RDDM dump */ + dump_tlv = (struct ath11k_tlv_dump_data *)(buf + sofar); + dump_tlv->type = cpu_to_le32(ATH11K_FW_CRASH_RDDM_DATA); + dump_tlv->tlv_len = cpu_to_le32(crash_data->ramdump_buf_len); + memcpy(dump_tlv->tlv_data, crash_data->ramdump_buf, + crash_data->ramdump_buf_len); + sofar += sizeof(*dump_tlv) + crash_data->ramdump_buf_len; + + /* gather remote memory */ + dump_tlv = (struct ath11k_tlv_dump_data *)(buf + sofar); + dump_tlv->type = cpu_to_le32(ATH11K_FW_REMOTE_MEM_DATA); + dump_tlv->tlv_len = cpu_to_le32(remote_crash_data->remote_buf_len); + memcpy(dump_tlv->tlv_data, remote_crash_data->remote_buf, + remote_crash_data->remote_buf_len); + sofar += sizeof(*dump_tlv) + remote_crash_data->remote_buf_len; + + /* gather register memory */ + dump_tlv = (struct ath11k_tlv_dump_data *)(buf + sofar); + dump_tlv->type = cpu_to_le32(ATH11K_FW_REGISTER_DATA); + dump_tlv->tlv_len = cpu_to_le32(reg_crash_data->reg_buf_len); + memcpy(dump_tlv->tlv_data, reg_crash_data->reg_buf, + reg_crash_data->reg_buf_len); + sofar += sizeof(*dump_tlv) + reg_crash_data->reg_buf_len; + + /* gather register for rddm fail */ + dump_tlv = (struct ath11k_tlv_dump_data *)(buf + sofar); + dump_tlv->type = cpu_to_le32(ATH11K_FW_REGISTER_RDDM_DATA); + dump_tlv->tlv_len = cpu_to_le32(reg_crash_data->reg_rddm_buf_len); + memcpy(dump_tlv->tlv_data, reg_crash_data->reg_rddm_buf, + reg_crash_data->reg_rddm_buf_len); + sofar += sizeof(*dump_tlv) + reg_crash_data->reg_rddm_buf_len; + + return dump_data; +} + +static int ath11k_coredump_submit(struct ath11k_pci *ab_pci) +{ + struct ath11k_dump_file_data *dump; + + dump = ath11k_coredump_build(&ab_pci->mhi_fw_crash_data, + &ab_pci->ab->remote_crash_data, + &ab_pci->reg_data); + if (!dump) + return -ENODATA; + + dev_coredumpv(ab_pci->mhi_ctrl->cntrl_dev, dump, + le32_to_cpu(dump->len), GFP_KERNEL); + + return 0; +} + +static void ath11k_coredump_buf_release(struct ath11k_pci *ab_pci) +{ + struct fw_remote_crash_data *remote = &ab_pci->ab->remote_crash_data; + struct ath11k_mhi_fw_crash_data *mhi = &ab_pci->mhi_fw_crash_data; + struct register_crash_data *reg = &ab_pci->reg_data; + + if (remote->remote_buf) { + vfree(remote->remote_buf); + remote->remote_buf = NULL; + } + + if (mhi->ramdump_buf) { + vfree(mhi->ramdump_buf); + mhi->ramdump_buf = NULL; + } + + if (mhi->paging_dump_buf) { + vfree(mhi->paging_dump_buf); + mhi->paging_dump_buf = NULL; + } + + if (reg->reg_buf) { + vfree(reg->reg_buf); + reg->reg_buf = NULL; + } + + if (reg->reg_rddm_buf) { + vfree(reg->reg_rddm_buf); + reg->reg_rddm_buf = NULL; + } +} + +void ath11k_mhi_pm_rddm_worker(struct work_struct *work) +{ + struct ath11k_pci *ab_pci = container_of(work, + struct ath11k_pci, + rddm_worker); + struct ath11k_base *ab = ab_pci->ab; + + mhi_download_rddm_image(ab_pci->mhi_ctrl, false); + + ath11k_coredump_fw_rddm_dump(ab_pci, ab_pci->mhi_ctrl); + ath11k_coredump_fw_paging_dump(ab_pci, ab_pci->mhi_ctrl); + ath11k_qmi_remote_dump(ab_pci->ab); + ath11k_pci_register_dump(ab_pci); + + ath11k_coredump_submit(ab_pci); + ath11k_coredump_buf_release(ab_pci); + + if (test_bit(ATH11K_FLAG_UNREGISTERING, &ab->dev_flags)) + return; + + ath11k_info(ab, "start to reset for rddm\n"); + queue_work(ab->workqueue_aux, &ab->reset_work); +} diff --git a/drivers/net/wireless/ath/ath11k/coredump.h b/drivers/net/wireless/ath/ath11k/coredump.h new file mode 100644 index 000000000000..d8cae2e5d45f --- /dev/null +++ b/drivers/net/wireless/ath/ath11k/coredump.h @@ -0,0 +1,48 @@ +/* SPDX-License-Identifier: BSD-3-Clause-Clear */ +/* + * Copyright (c) 2020 The Linux Foundation. All rights reserved. + */ + +#ifndef _COREDUMP_H_ +#define _COREDUMP_H_ + +#define ATH11K_FW_CRASH_DUMP_VERSION 1 + +enum ath11k_fw_crash_dump_type { + ATH11K_FW_CRASH_PAGING_DATA, + ATH11K_FW_CRASH_RDDM_DATA, + ATH11K_FW_REMOTE_MEM_DATA, + ATH11K_FW_REGISTER_DATA, + ATH11K_FW_REGISTER_RDDM_DATA, + ATH11K_FW_CRASH_DUMP_MAX, +}; + +struct ath11k_tlv_dump_data { + /* see ath11k_fw_crash_dump_type above */ + __le32 type; + /* in bytes */ + __le32 tlv_len; + /* pad to 32-bit boundaries as needed */ + u8 tlv_data[]; +} __packed; + +struct ath11k_dump_file_data { + /* "ATH11K-FW-DUMP" */ + char df_magic[16]; + __le32 len; + /* file dump version */ + __le32 version; + guid_t guid; + /* time-of-day stamp */ + __le64 tv_sec; + /* time-of-day stamp, nano-seconds */ + __le64 tv_nsec; + /* room for growth w/out changing binary format */ + u8 unused[8]; + /* struct ath11k_tlv_dump_data + more */ + u8 data[0]; +} __packed; + +void ath11k_mhi_pm_rddm_worker(struct work_struct *work); + +#endif diff --git a/drivers/net/wireless/ath/ath11k/coredump_fw.c b/drivers/net/wireless/ath/ath11k/coredump_fw.c new file mode 100644 index 000000000000..73ae511b6ce7 --- /dev/null +++ b/drivers/net/wireless/ath/ath11k/coredump_fw.c @@ -0,0 +1,105 @@ +// SPDX-License-Identifier: BSD-3-Clause-Clear +/* + * Copyright (c) 2020 The Linux Foundation. All rights reserved. + */ + +#include "coredump_fw.h" +#include "pci.h" +#include "debug.h" + +static size_t ath11k_get_paging_buf_len(struct mhi_controller *mhi_cntrl) +{ + struct image_info *img = mhi_cntrl->fbc_image; + int i = 0; + size_t len = 0; + u32 entries = img->entries; + size_t seg_size = mhi_cntrl->seg_len; + + for (i = 0; i < entries; ++i) { + size_t vec_size = seg_size; + + if (i == entries - 1) + vec_size = sizeof(struct ath11k_vec_entry) * i; + len += vec_size; + } + + return len; +} + +int ath11k_coredump_fw_paging_dump(struct ath11k_pci *ab_pci, struct mhi_controller *mhi_cntrl) +{ + struct image_info *img = mhi_cntrl->fbc_image; + struct ath11k_mhi_fw_crash_data *crash_data = &ab_pci->mhi_fw_crash_data; + char *buf = NULL; + unsigned int size = 0; + int seg = 0; + u32 offset = 0; + u32 fw_vec_entry_num = img->entries - 1; + size_t paging_dump_buf_len = ath11k_get_paging_buf_len(mhi_cntrl); + + crash_data->paging_dump_buf_len = paging_dump_buf_len; + dev_info(&mhi_cntrl->mhi_dev->dev, + "%s FW paging dump buffer len=%lu\n", + __func__, paging_dump_buf_len); + crash_data->paging_dump_buf = vzalloc(paging_dump_buf_len); + if (!crash_data->paging_dump_buf) + return -ENOMEM; + + for (seg = 0; seg < fw_vec_entry_num; seg++) { + buf = img->mhi_buf[seg].buf; + size = img->mhi_buf[seg].len; + memcpy(crash_data->paging_dump_buf + offset, buf, size); + offset += size; + } + + buf = crash_data->paging_dump_buf + offset; + size = img->mhi_buf[img->entries - 1].len; + ath11k_info(ab_pci->ab, "to write last block: mem: 0x%p, size: 0x%x\n", + buf, size); + memcpy(buf, img->mhi_buf[img->entries - 1].buf, size); + + return 0; +} + +int ath11k_coredump_fw_rddm_dump(struct ath11k_pci *ab_pci, struct mhi_controller *mhi_cntrl) +{ + struct ath11k_mhi_fw_crash_data *crash_data = &ab_pci->mhi_fw_crash_data; + struct image_info *img = mhi_cntrl->rddm_image; + char *buf = NULL; + unsigned int size = 0; + int seg = 0; + u32 offset = 0; + u32 rddm_vec_entry_num; + u32 entries = mhi_cntrl->rddm_image->entries; + + rddm_vec_entry_num = DIV_ROUND_UP(RDDM_DUMP_SIZE, + mhi_cntrl->seg_len); + crash_data->ramdump_buf_len = (entries - 1) * mhi_cntrl->seg_len + + rddm_vec_entry_num * sizeof(struct ath11k_vec_entry); + + ath11k_info(ab_pci->ab, "rddm_vec_entry_num=%d entries=%d\n", + rddm_vec_entry_num, img->entries); + + crash_data->ramdump_buf = vzalloc(crash_data->ramdump_buf_len); + if (!crash_data->ramdump_buf) + return -ENOMEM; + + for (seg = 0; seg < rddm_vec_entry_num; seg++) { + buf = img->mhi_buf[seg].buf; + size = img->mhi_buf[seg].len; + ath11k_info(ab_pci->ab, + "write rddm memory: mem: 0x%p, size: 0x%x\n", + buf, size); + memcpy(crash_data->ramdump_buf + offset, buf, size); + offset += size; + } + + buf = crash_data->ramdump_buf + offset; + size = img->mhi_buf[img->entries - 1].len; + ath11k_info(ab_pci->ab, + "to write vector table block: mem: 0x%p, size: 0x%x\n", + buf, size); + memcpy(buf, img->mhi_buf[img->entries - 1].buf, size); + + return 0; +} diff --git a/drivers/net/wireless/ath/ath11k/coredump_fw.h b/drivers/net/wireless/ath/ath11k/coredump_fw.h new file mode 100644 index 000000000000..e254ceec7d93 --- /dev/null +++ b/drivers/net/wireless/ath/ath11k/coredump_fw.h @@ -0,0 +1,30 @@ +/* SPDX-License-Identifier: BSD-3-Clause-Clear */ +/* + * Copyright (c) 2020 The Linux Foundation. All rights reserved. + */ + +#ifndef _COREDUMP_FW_H_ +#define _COREDUMP_FW_H_ + +#include <linux/mhi.h> + +struct ath11k_pci; + +#define RDDM_DUMP_SIZE 0x420000 + +struct ath11k_vec_entry { + u64 dma_addr; + u64 size; +}; + +struct ath11k_mhi_fw_crash_data { + u8 *paging_dump_buf; + size_t paging_dump_buf_len; + u8 *ramdump_buf; + size_t ramdump_buf_len; +}; + +int ath11k_coredump_fw_paging_dump(struct ath11k_pci *ab_pci, struct mhi_controller *mhi_cntrl); +int ath11k_coredump_fw_rddm_dump(struct ath11k_pci *ab_pci, struct mhi_controller *mhi_cntrl); + +#endif diff --git a/drivers/net/wireless/ath/ath11k/debug.h b/drivers/net/wireless/ath/ath11k/debug.h index 91545640c47b..2553cf902093 100644 --- a/drivers/net/wireless/ath/ath11k/debug.h +++ b/drivers/net/wireless/ath/ath11k/debug.h @@ -1,6 +1,7 @@ /* SPDX-License-Identifier: BSD-3-Clause-Clear */ /* * Copyright (c) 2018-2019 The Linux Foundation. All rights reserved. + * Copyright (c) 2023 Qualcomm Innovation Center, Inc. All rights reserved. */ #ifndef _ATH11K_DEBUG_H_ @@ -33,6 +34,7 @@ __printf(2, 3) void ath11k_err(struct ath11k_base *ab, const char *fmt, ...); __printf(2, 3) void ath11k_warn(struct ath11k_base *ab, const char *fmt, ...); extern unsigned int ath11k_debug_mask; +extern unsigned int ath11k_ftm_mode; #ifdef CONFIG_ATH11K_DEBUG __printf(3, 4) void __ath11k_dbg(struct ath11k_base *ab, diff --git a/drivers/net/wireless/ath/ath11k/debugfs.c b/drivers/net/wireless/ath/ath11k/debugfs.c index 5bb6fd17fdf6..7fad6e447229 100644 --- a/drivers/net/wireless/ath/ath11k/debugfs.c +++ b/drivers/net/wireless/ath/ath11k/debugfs.c @@ -468,6 +468,78 @@ static const struct file_operations fops_bcn_stats = { .llseek = default_llseek, }; +static ssize_t ath11k_debugfs_write_coex_config(struct file *file, + const char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct ath11k_base *ab = file->private_data; + struct ath11k *ar = ath11k_ab_to_ar(ab, 0); + char buf[64] = {0}; + ssize_t ret; + int rc; + u32 config_type, config_arg1; + char sep[] = " "; + char *token, *cur; + struct wmi_coex_config_params param; + + if (*ppos != 0 || count >= sizeof(buf) || count == 0) { + ret = -EINVAL; + goto out; + } + + ret = simple_write_to_buffer(buf, sizeof(buf) - 1, ppos, user_buf, count); + if (ret < 0) + goto out; + + /* drop the possible '\n' from the end */ + if (buf[*ppos - 1] == '\n') + buf[*ppos - 1] = '\0'; + + ath11k_info(ab, "%s: %s\n", __func__, buf); + ret = count; + cur = buf; + + token = strsep(&cur, sep); + rc = kstrtou32(token, 0, &config_type); + if (rc) { + ath11k_warn(ab, "%s convert error: config_type %s\n", __func__, token); + ret = -EFAULT; + goto out; + } + + token = strim(cur); + rc = kstrtou32(token, 0, &config_arg1); + if (rc) { + ath11k_warn(ab, "%s convert error: config_arg1 %s\n", __func__, token); + ret = -EFAULT; + goto out; + } + + + memset(¶m, 0, sizeof(struct wmi_coex_config_params)); + param.config_type = config_type; + param.config_arg1 = config_arg1; + + mutex_lock(&ar->conf_mutex); + + rc = ath11k_mac_send_coex_config(ar, ¶m); + if (rc) { + ath11k_warn(ab, "failed to send coex config using debugfs %d\n", rc); + ret = -EFAULT; + } + + mutex_unlock(&ar->conf_mutex); +out: + return ret; +} + +static const struct file_operations fops_coex_config = { + .write = ath11k_debugfs_write_coex_config, + .open = simple_open, + .owner = THIS_MODULE, + .llseek = default_llseek, +}; + static ssize_t ath11k_read_simulate_fw_crash(struct file *file, char __user *user_buf, size_t count, loff_t *ppos) @@ -532,6 +604,10 @@ static ssize_t ath11k_write_simulate_fw_crash(struct file *file, ath11k_info(ab, "user requested hw restart\n"); queue_work(ab->workqueue_aux, &ab->reset_work); ret = 0; + } else if (!strcmp(buf, "mhi-rddm")) { + ath11k_info(ab, "force target rddm\n"); + ath11k_hif_force_rddm(ab); + ret = 0; } else { ret = -EINVAL; goto exit; @@ -957,9 +1033,12 @@ static ssize_t ath11k_read_sram_dump(struct file *file, static int ath11k_release_sram_dump(struct inode *inode, struct file *file) { + struct ath11k_base *ab = inode->i_private; vfree(file->private_data); file->private_data = NULL; + debugfs_create_file("coex_config", 0600, ab->debugfs_soc, ab, + &fops_coex_config); return 0; } diff --git a/drivers/net/wireless/ath/ath11k/dp.c b/drivers/net/wireless/ath/ath11k/dp.c index d070bcb3fe24..6b6d0ce4f790 100644 --- a/drivers/net/wireless/ath/ath11k/dp.c +++ b/drivers/net/wireless/ath/ath11k/dp.c @@ -254,9 +254,9 @@ int ath11k_dp_srng_setup(struct ath11k_base *ab, struct dp_srng *ring, } if (!cached) - ring->vaddr_unaligned = dma_alloc_coherent(ab->dev, ring->size, + ring->vaddr_unaligned = ath11k_core_dma_alloc_coherent(ab->dev, ring->size, &ring->paddr_unaligned, - GFP_KERNEL); + GFP_KERNEL | GFP_DMA32); if (!ring->vaddr_unaligned) return -ENOMEM; @@ -527,9 +527,9 @@ static int ath11k_dp_scatter_idle_link_desc_setup(struct ath11k_base *ab, return -EINVAL; for (i = 0; i < num_scatter_buf; i++) { - slist[i].vaddr = dma_alloc_coherent(ab->dev, + slist[i].vaddr = ath11k_core_dma_alloc_coherent(ab->dev, HAL_WBM_IDLE_SCATTER_BUF_SIZE_MAX, - &slist[i].paddr, GFP_KERNEL); + &slist[i].paddr, GFP_KERNEL | GFP_DMA32); if (!slist[i].vaddr) { ret = -ENOMEM; goto err; @@ -607,9 +607,9 @@ static int ath11k_dp_link_desc_bank_alloc(struct ath11k_base *ab, desc_sz = last_bank_sz; desc_bank[i].vaddr_unaligned = - dma_alloc_coherent(ab->dev, desc_sz, + ath11k_core_dma_alloc_coherent(ab->dev, desc_sz, &desc_bank[i].paddr_unaligned, - GFP_KERNEL); + GFP_KERNEL | GFP_DMA32); if (!desc_bank[i].vaddr_unaligned) { ret = -ENOMEM; goto err; diff --git a/drivers/net/wireless/ath/ath11k/dp_rx.c b/drivers/net/wireless/ath/ath11k/dp_rx.c index 38be646bc021..9e4ab2ae7912 100644 --- a/drivers/net/wireless/ath/ath11k/dp_rx.c +++ b/drivers/net/wireless/ath/ath11k/dp_rx.c @@ -691,13 +691,18 @@ void ath11k_dp_reo_cmd_list_cleanup(struct ath11k_base *ab) struct ath11k_dp *dp = &ab->dp; struct dp_reo_cmd *cmd, *tmp; struct dp_reo_cache_flush_elem *cmd_cache, *tmp_cache; + struct dp_rx_tid *rx_tid; spin_lock_bh(&dp->reo_cmd_lock); list_for_each_entry_safe(cmd, tmp, &dp->reo_cmd_list, list) { list_del(&cmd->list); - dma_unmap_single(ab->dev, cmd->data.paddr, - cmd->data.size, DMA_BIDIRECTIONAL); - kfree(cmd->data.vaddr); + rx_tid = &cmd->data; + if (rx_tid->vaddr) { + dma_unmap_single(ab->dev, rx_tid->paddr, + rx_tid->size, DMA_BIDIRECTIONAL); + kfree(rx_tid->vaddr); + rx_tid->vaddr = NULL; + } kfree(cmd); } @@ -705,9 +710,13 @@ void ath11k_dp_reo_cmd_list_cleanup(struct ath11k_base *ab) &dp->reo_cmd_cache_flush_list, list) { list_del(&cmd_cache->list); dp->reo_cmd_cache_flush_count--; - dma_unmap_single(ab->dev, cmd_cache->data.paddr, - cmd_cache->data.size, DMA_BIDIRECTIONAL); - kfree(cmd_cache->data.vaddr); + rx_tid = &cmd_cache->data; + if (rx_tid->vaddr) { + dma_unmap_single(ab->dev, rx_tid->paddr, + rx_tid->size, DMA_BIDIRECTIONAL); + kfree(rx_tid->vaddr); + rx_tid->vaddr = NULL; + } kfree(cmd_cache); } spin_unlock_bh(&dp->reo_cmd_lock); @@ -721,10 +730,12 @@ static void ath11k_dp_reo_cmd_free(struct ath11k_dp *dp, void *ctx, if (status != HAL_REO_CMD_SUCCESS) ath11k_warn(dp->ab, "failed to flush rx tid hw desc, tid %d status %d\n", rx_tid->tid, status); - - dma_unmap_single(dp->ab->dev, rx_tid->paddr, rx_tid->size, - DMA_BIDIRECTIONAL); - kfree(rx_tid->vaddr); + if (rx_tid->vaddr) { + dma_unmap_single(dp->ab->dev, rx_tid->paddr, rx_tid->size, + DMA_BIDIRECTIONAL); + kfree(rx_tid->vaddr); + rx_tid->vaddr = NULL; + } } static void ath11k_dp_reo_cache_flush(struct ath11k_base *ab, @@ -763,6 +774,7 @@ static void ath11k_dp_reo_cache_flush(struct ath11k_base *ab, dma_unmap_single(ab->dev, rx_tid->paddr, rx_tid->size, DMA_BIDIRECTIONAL); kfree(rx_tid->vaddr); + rx_tid->vaddr = NULL; } } @@ -815,6 +827,7 @@ free_desc: dma_unmap_single(ab->dev, rx_tid->paddr, rx_tid->size, DMA_BIDIRECTIONAL); kfree(rx_tid->vaddr); + rx_tid->vaddr = NULL; } void ath11k_peer_rx_tid_delete(struct ath11k *ar, @@ -827,6 +840,8 @@ void ath11k_peer_rx_tid_delete(struct ath11k *ar, if (!rx_tid->active) return; + rx_tid->active = false; + cmd.flag = HAL_REO_CMD_FLG_NEED_STATUS; cmd.addr_lo = lower_32_bits(rx_tid->paddr); cmd.addr_hi = upper_32_bits(rx_tid->paddr); @@ -841,9 +856,11 @@ void ath11k_peer_rx_tid_delete(struct ath11k *ar, dma_unmap_single(ar->ab->dev, rx_tid->paddr, rx_tid->size, DMA_BIDIRECTIONAL); kfree(rx_tid->vaddr); + rx_tid->vaddr = NULL; } - rx_tid->active = false; + rx_tid->paddr = 0; + rx_tid->size = 0; } static int ath11k_dp_rx_link_desc_return(struct ath11k_base *ab, @@ -990,6 +1007,7 @@ static void ath11k_dp_rx_tid_mem_free(struct ath11k_base *ab, dma_unmap_single(ab->dev, rx_tid->paddr, rx_tid->size, DMA_BIDIRECTIONAL); kfree(rx_tid->vaddr); + rx_tid->vaddr = NULL; rx_tid->active = false; @@ -1090,7 +1108,8 @@ int ath11k_peer_rx_tid_setup(struct ath11k *ar, const u8 *peer_mac, int vdev_id, return ret; err_mem_free: - kfree(vaddr); + kfree(rx_tid->vaddr); + rx_tid->vaddr = NULL; return ret; } @@ -3609,7 +3628,7 @@ static int ath11k_dp_rx_frag_h_mpdu(struct ath11k *ar, goto out_unlock; } - if (frag_no > __fls(rx_tid->rx_frag_bitmap)) + if (!rx_tid->rx_frag_bitmap || (frag_no > __fls(rx_tid->rx_frag_bitmap))) __skb_queue_tail(&rx_tid->rx_frags, msdu); else ath11k_dp_rx_h_sort_frags(ar, &rx_tid->rx_frags, msdu); diff --git a/drivers/net/wireless/ath/ath11k/hal.c b/drivers/net/wireless/ath/ath11k/hal.c index 2fd224480d45..3f9f5cfe9d7c 100644 --- a/drivers/net/wireless/ath/ath11k/hal.c +++ b/drivers/net/wireless/ath/ath11k/hal.c @@ -8,6 +8,7 @@ #include "debug.h" #include "hal_desc.h" #include "hif.h" +#include "core.h" static const struct hal_srng_config hw_srng_config_template[] = { /* TODO: max_rings can populated by querying HW capabilities */ @@ -196,8 +197,8 @@ static int ath11k_hal_alloc_cont_rdp(struct ath11k_base *ab) size_t size; size = sizeof(u32) * HAL_SRNG_RING_ID_MAX; - hal->rdp.vaddr = dma_alloc_coherent(ab->dev, size, &hal->rdp.paddr, - GFP_KERNEL); + hal->rdp.vaddr = ath11k_core_dma_alloc_coherent(ab->dev, size, &hal->rdp.paddr, + GFP_KERNEL | GFP_DMA32); if (!hal->rdp.vaddr) return -ENOMEM; @@ -224,8 +225,8 @@ static int ath11k_hal_alloc_cont_wrp(struct ath11k_base *ab) size_t size; size = sizeof(u32) * HAL_SRNG_NUM_LMAC_RINGS; - hal->wrp.vaddr = dma_alloc_coherent(ab->dev, size, &hal->wrp.paddr, - GFP_KERNEL); + hal->wrp.vaddr = ath11k_core_dma_alloc_coherent(ab->dev, size, &hal->wrp.paddr, + GFP_KERNEL | GFP_DMA32); if (!hal->wrp.vaddr) return -ENOMEM; diff --git a/drivers/net/wireless/ath/ath11k/hif.h b/drivers/net/wireless/ath/ath11k/hif.h index 659b80d2abd4..7eca43a91a42 100644 --- a/drivers/net/wireless/ath/ath11k/hif.h +++ b/drivers/net/wireless/ath/ath11k/hif.h @@ -30,6 +30,7 @@ struct ath11k_hif_ops { void (*ce_irq_enable)(struct ath11k_base *ab); void (*ce_irq_disable)(struct ath11k_base *ab); void (*get_ce_msi_idx)(struct ath11k_base *ab, u32 ce_id, u32 *msi_idx); + int (*target_crash)(struct ath11k_base *ab); }; static inline void ath11k_hif_ce_irq_enable(struct ath11k_base *ab) @@ -90,6 +91,13 @@ static inline int ath11k_hif_resume(struct ath11k_base *ab) return 0; } +static inline int ath11k_hif_force_rddm(struct ath11k_base *ab) +{ + if (ab->hif.ops->target_crash) + return ab->hif.ops->target_crash(ab); + return 0; +} + static inline u32 ath11k_hif_read32(struct ath11k_base *sc, u32 address) { return sc->hif.ops->read32(sc, address); diff --git a/drivers/net/wireless/ath/ath11k/hw.c b/drivers/net/wireless/ath/ath11k/hw.c index dbcc0c4035b6..3b764d501416 100644 --- a/drivers/net/wireless/ath/ath11k/hw.c +++ b/drivers/net/wireless/ath/ath11k/hw.c @@ -100,6 +100,7 @@ static void ath11k_init_wmi_config_qca6390(struct ath11k_base *ab, config->num_wow_filters = 0x16; config->num_keep_alive_pattern = 0; config->flag1 |= WMI_RSRC_CFG_FLAG1_BSS_CHANNEL_INFO_64; + config->host_service_flags |= WMI_RSRC_CFG_HOST_SERVICE_FLAG_NAN_IFACE_SUPPORT; } static void ath11k_hw_ipq8074_reo_setup(struct ath11k_base *ab) @@ -844,6 +845,18 @@ static u32 ath11k_hw_wcn6750_get_tcl_ring_selector(struct sk_buff *skb) return skb_get_hash(skb); } +bool ath11k_hw_supports_6g_cc_ext(struct ath11k *ar) +{ + return (test_bit(WMI_TLV_SERVICE_REG_CC_EXT_EVENT_SUPPORT, + ar->ab->wmi_ab.svc_map)) && ar->supports_6ghz; +} + +bool ath11k_hw_supports_tpc_ext(struct ath11k *ar) +{ + return ath11k_hw_supports_6g_cc_ext(ar) && + test_bit(WMI_TLV_SERVICE_EXT_TPC_REG_SUPPORT, ar->ab->wmi_ab.svc_map); +} + const struct ath11k_hw_ops ipq8074_ops = { .get_hw_mac_from_pdev_id = ath11k_hw_ipq8074_mac_from_pdev_id, .wmi_init_config = ath11k_init_wmi_config_ipq8074, diff --git a/drivers/net/wireless/ath/ath11k/hw.h b/drivers/net/wireless/ath/ath11k/hw.h index 8a3f24862edc..95a833382ebb 100644 --- a/drivers/net/wireless/ath/ath11k/hw.h +++ b/drivers/net/wireless/ath/ath11k/hw.h @@ -219,6 +219,12 @@ struct ath11k_hw_params { bool tcl_ring_retry; u32 tx_ring_size; bool smp2p_wow_exit; + bool wakeup_mhi; + u32 rfkill_pin; + u32 rfkill_cfg; + u32 rfkill_on_level; + bool coex_isolation; + bool support_fw_mac_sequence; }; struct ath11k_hw_ops { @@ -308,6 +314,9 @@ static inline int ath11k_hw_mac_id_to_srng_id(struct ath11k_hw_params *hw, return 0; } +bool ath11k_hw_supports_6g_cc_ext(struct ath11k *ar); +bool ath11k_hw_supports_tpc_ext(struct ath11k *ar); + struct ath11k_fw_ie { __le32 id; __le32 len; diff --git a/drivers/net/wireless/ath/ath11k/mac.c b/drivers/net/wireless/ath/ath11k/mac.c index cb77dd6ce966..e7b3b0dfe8f3 100644 --- a/drivers/net/wireless/ath/ath11k/mac.c +++ b/drivers/net/wireless/ath/ath11k/mac.c @@ -1,7 +1,7 @@ // SPDX-License-Identifier: BSD-3-Clause-Clear /* * Copyright (c) 2018-2019 The Linux Foundation. All rights reserved. - * Copyright (c) 2021-2022 Qualcomm Innovation Center, Inc. All rights reserved. + * Copyright (c) 2021-2023 Qualcomm Innovation Center, Inc. All rights reserved. */ #include <net/mac80211.h> @@ -23,6 +23,7 @@ #include "debugfs_sta.h" #include "hif.h" #include "wow.h" +#include "unitest.h" #define CHAN2G(_channel, _freq, _flags) { \ .band = NL80211_BAND_2GHZ, \ @@ -250,6 +251,10 @@ static const u32 ath11k_smps_map[] = { [WLAN_HT_CAP_SM_PS_DISABLED] = WMI_PEER_SMPS_PS_NONE, }; +static struct wiphy_vendor_command ath11k_vendor_cmds[] = { + ath11k_unit_test_command, +}; + static int ath11k_start_vdev_delay(struct ieee80211_hw *hw, struct ieee80211_vif *vif); @@ -625,6 +630,17 @@ struct ath11k *ath11k_mac_get_ar_by_vdev_id(struct ath11k_base *ab, u32 vdev_id) return NULL; } +enum wmi_vdev_type ath11k_mac_get_ar_vdev_type(struct ath11k *ar) +{ + struct ath11k_vif *arvif; + + list_for_each_entry(arvif, &ar->arvifs, list) { + return arvif->vdev_type; + } + + return WMI_VDEV_TYPE_UNSPEC; +} + struct ath11k *ath11k_mac_get_ar_by_pdev_id(struct ath11k_base *ab, u32 pdev_id) { int i; @@ -639,7 +655,10 @@ struct ath11k *ath11k_mac_get_ar_by_pdev_id(struct ath11k_base *ab, u32 pdev_id) return NULL; for (i = 0; i < ab->num_radios; i++) { - pdev = rcu_dereference(ab->pdevs_active[i]); + if (ab->fw_mode == ATH11K_FIRMWARE_MODE_FTM) + pdev = &ab->pdevs[i]; + else + pdev = rcu_dereference(ab->pdevs_active[i]); if (pdev && pdev->pdev_id == pdev_id) return (pdev->ar ? pdev->ar : NULL); @@ -2695,6 +2714,138 @@ static int ath11k_setup_peer_smps(struct ath11k *ar, struct ath11k_vif *arvif, ath11k_smps_map[smps]); } +static bool ath11k_mac_set_he_txbf_conf(struct ath11k_vif *arvif) +{ + struct ath11k *ar = arvif->ar; + u32 param, value; + int ret; + + if (!arvif->vif->bss_conf.he_support) + return true; + + param = WMI_VDEV_PARAM_SET_HEMU_MODE; + value = 0; + if (arvif->vif->bss_conf.he_su_beamformer) { + value |= FIELD_PREP(HE_MODE_SU_TX_BFER, HE_SU_BFER_ENABLE); + if (arvif->vif->bss_conf.he_mu_beamformer && + arvif->vdev_type == WMI_VDEV_TYPE_AP) + value |= FIELD_PREP(HE_MODE_MU_TX_BFER, HE_MU_BFER_ENABLE); + } + + if (arvif->vif->type != NL80211_IFTYPE_MESH_POINT) { + value |= FIELD_PREP(HE_MODE_DL_OFDMA, HE_DL_MUOFDMA_ENABLE) | + FIELD_PREP(HE_MODE_UL_OFDMA, HE_UL_MUOFDMA_ENABLE); + + if (arvif->vif->bss_conf.he_full_ul_mumimo) + value |= FIELD_PREP(HE_MODE_UL_MUMIMO, HE_UL_MUMIMO_ENABLE); + + if (arvif->vif->bss_conf.he_su_beamformee) + value |= FIELD_PREP(HE_MODE_SU_TX_BFEE, HE_SU_BFEE_ENABLE); + } + + ret = ath11k_wmi_vdev_set_param_cmd(ar, arvif->vdev_id, param, value); + if (ret) { + ath11k_warn(ar->ab, "failed to set vdev %d HE MU mode: %d\n", + arvif->vdev_id, ret); + return false; + } + + param = WMI_VDEV_PARAM_SET_HE_SOUNDING_MODE; + value = FIELD_PREP(HE_VHT_SOUNDING_MODE, HE_VHT_SOUNDING_MODE_ENABLE) | + FIELD_PREP(HE_TRIG_NONTRIG_SOUNDING_MODE, + HE_TRIG_NONTRIG_SOUNDING_MODE_ENABLE); + ret = ath11k_wmi_vdev_set_param_cmd(ar, arvif->vdev_id, + param, value); + if (ret) { + ath11k_warn(ar->ab, "failed to set vdev %d sounding mode: %d\n", + arvif->vdev_id, ret); + return false; + } + return true; +} + +static bool ath11k_mac_vif_recalc_sta_he_txbf(struct ath11k *ar, + struct ieee80211_vif *vif, + struct ieee80211_sta_he_cap *he_cap) +{ + struct ath11k_vif *arvif = (void *)vif->drv_priv; + struct ieee80211_he_cap_elem he_cap_elem = {0}; + struct ieee80211_sta_he_cap *cap_band = NULL; + struct cfg80211_chan_def def; + u32 param = WMI_VDEV_PARAM_SET_HEMU_MODE; + u32 hemode = 0; + int ret; + + if (!vif->bss_conf.he_support) + return true; + + if (vif->type != NL80211_IFTYPE_STATION) + return false; + + if (WARN_ON(ath11k_mac_vif_chan(vif, &def))) + return false; + + if (def.chan->band == NL80211_BAND_2GHZ) + cap_band = &ar->mac.iftype[NL80211_BAND_2GHZ][vif->type].he_cap; + else + cap_band = &ar->mac.iftype[NL80211_BAND_5GHZ][vif->type].he_cap; + + memcpy(&he_cap_elem, &cap_band->he_cap_elem, sizeof(he_cap_elem)); + + if (HECAP_PHY_SUBFME_GET(he_cap_elem.phy_cap_info)) { + if (HECAP_PHY_SUBFMR_GET(he_cap->he_cap_elem.phy_cap_info)) + hemode |= FIELD_PREP(HE_MODE_SU_TX_BFEE, HE_SU_BFEE_ENABLE); + if (HECAP_PHY_MUBFMR_GET(he_cap->he_cap_elem.phy_cap_info)) + hemode |= FIELD_PREP(HE_MODE_MU_TX_BFEE, HE_MU_BFEE_ENABLE); + } + + if (vif->type != NL80211_IFTYPE_MESH_POINT) { + hemode |= FIELD_PREP(HE_MODE_DL_OFDMA, HE_DL_MUOFDMA_ENABLE) | + FIELD_PREP(HE_MODE_UL_OFDMA, HE_UL_MUOFDMA_ENABLE); + + if (HECAP_PHY_ULMUMIMO_GET(he_cap_elem.phy_cap_info)) + if (HECAP_PHY_ULMUMIMO_GET(he_cap->he_cap_elem.phy_cap_info)) + hemode |= FIELD_PREP(HE_MODE_UL_MUMIMO, + HE_UL_MUMIMO_ENABLE); + + if (FIELD_GET(HE_MODE_MU_TX_BFEE, hemode)) + hemode |= FIELD_PREP(HE_MODE_SU_TX_BFEE, HE_SU_BFEE_ENABLE); + + if (FIELD_GET(HE_MODE_MU_TX_BFER, hemode)) + hemode |= FIELD_PREP(HE_MODE_SU_TX_BFER, HE_SU_BFER_ENABLE); + } + + ath11k_info(ar->ab, "mac0-5 cap %x-%x-%x-%x-%x-%x\n", + he_cap_elem.mac_cap_info[0], + he_cap_elem.mac_cap_info[1], + he_cap_elem.mac_cap_info[2], + he_cap_elem.mac_cap_info[3], + he_cap_elem.mac_cap_info[4], + he_cap_elem.mac_cap_info[5]); + ath11k_info(ar->ab, "phy0-5 cap %x-%x-%x-%x-%x-%x\n", + he_cap_elem.phy_cap_info[0], + he_cap_elem.phy_cap_info[1], + he_cap_elem.phy_cap_info[2], + he_cap_elem.phy_cap_info[3], + he_cap_elem.phy_cap_info[4], + he_cap_elem.phy_cap_info[5]); + ath11k_info(ar->ab, "phy6-10 cap %x-%x-%x-%x-%x\n", + he_cap_elem.phy_cap_info[6], + he_cap_elem.phy_cap_info[7], + he_cap_elem.phy_cap_info[8], + he_cap_elem.phy_cap_info[9], + he_cap_elem.phy_cap_info[10]); + ath11k_info(ar->ab, "WMI_VDEV_PARAM_SET_HEMU_MODE 3 0x%x\n", hemode); + ret = ath11k_wmi_vdev_set_param_cmd(ar, arvif->vdev_id, param, hemode); + if (ret) { + ath11k_warn(ar->ab, "failed to submit vdev param txbf 0x%x: %d\n", + hemode, ret); + return false; + } + + return true; +} + static void ath11k_bss_assoc(struct ieee80211_hw *hw, struct ieee80211_vif *vif, struct ieee80211_bss_conf *bss_conf) @@ -2705,6 +2856,7 @@ static void ath11k_bss_assoc(struct ieee80211_hw *hw, struct ieee80211_sta *ap_sta; struct ath11k_peer *peer; bool is_auth = false; + struct ieee80211_sta_he_cap he_cap; int ret; lockdep_assert_held(&ar->conf_mutex); @@ -2722,11 +2874,21 @@ static void ath11k_bss_assoc(struct ieee80211_hw *hw, return; } + /* he_cap here is updated at assoc success for sta mode only */ + he_cap = ap_sta->deflink.he_cap; + ath11k_peer_assoc_prepare(ar, vif, ap_sta, &peer_arg, false); rcu_read_unlock(); + if (!ath11k_mac_vif_recalc_sta_he_txbf(ar, vif, &he_cap)) { + ath11k_warn(ar->ab, "failed to recalc he txbf for vdev %i on bss %pM\n", + arvif->vdev_id, bss_conf->bssid); + return; + } + peer_arg.is_assoc = true; + ret = ath11k_wmi_send_peer_assoc_cmd(ar, &peer_arg); if (ret) { ath11k_warn(ar->ab, "failed to run peer assoc for %pM vdev %i: %d\n", @@ -3090,6 +3252,16 @@ static int ath11k_mac_config_obss_pd(struct ath11k *ar, return 0; } +static bool ath11k_mac_supports_station_tpc(struct ath11k *ar, + struct ath11k_vif *arvif, + const struct cfg80211_chan_def *chandef) +{ + return ath11k_hw_supports_tpc_ext(ar) && + arvif->vdev_type == WMI_VDEV_TYPE_STA && + chandef->chan && + chandef->chan->band == NL80211_BAND_6GHZ; +} + static void ath11k_mac_op_bss_info_changed(struct ieee80211_hw *hw, struct ieee80211_vif *vif, struct ieee80211_bss_conf *info, @@ -3198,6 +3370,8 @@ static void ath11k_mac_op_bss_info_changed(struct ieee80211_hw *hw, ether_addr_copy(arvif->bssid, info->bssid); if (changed & BSS_CHANGED_BEACON_ENABLED) { + if (info->enable_beacon) + ath11k_mac_set_he_txbf_conf(arvif); ath11k_control_beaconing(arvif, info); if (arvif->is_up && vif->bss_conf.he_support && @@ -3288,8 +3462,13 @@ static void ath11k_mac_op_bss_info_changed(struct ieee80211_hw *hw, ath11k_dbg(ar->ab, ATH11K_DBG_MAC, "mac vdev_id %i txpower %d\n", arvif->vdev_id, info->txpower); - arvif->txpower = info->txpower; - ath11k_mac_txpower_recalc(ar); + if (ath11k_mac_supports_station_tpc(ar, arvif, &info->chandef)) { + ath11k_dbg(ar->ab, ATH11K_DBG_MAC, + "discard tx power, change to set TPC power\n"); + } else { + arvif->txpower = info->txpower; + ath11k_mac_txpower_recalc(ar); + } } if (changed & BSS_CHANGED_PS && @@ -3613,6 +3792,18 @@ static int ath11k_mac_op_hw_scan(struct ieee80211_hw *hw, int ret = 0; int i; + /* Firmwares advertising the support of triggering 11D algorithm + * on the scan results of a regular scan expects driver to send + * WMI_11D_SCAN_START_CMDID before sending WMI_START_SCAN_CMDID. + * With this feature, separate 11D scan can be avoided since + * regdomain can be determined with the scan results of the + * regular scan. + */ + if (ar->state_11d == ATH11K_11D_PREPARING && + test_bit(WMI_TLV_SERVICE_SUPPORT_11D_FOR_HOST_SCAN, + ar->ab->wmi_ab.svc_map)) + ath11k_mac_11d_scan_start(ar, arvif->vdev_id); + mutex_lock(&ar->conf_mutex); spin_lock_bh(&ar->data_lock); @@ -5009,8 +5200,6 @@ static int ath11k_mac_set_txbf_conf(struct ath11k_vif *arvif) if (vht_cap & (IEEE80211_VHT_CAP_SU_BEAMFORMEE_CAPABLE)) { nsts = vht_cap & IEEE80211_VHT_CAP_BEAMFORMEE_STS_MASK; nsts >>= IEEE80211_VHT_CAP_BEAMFORMEE_STS_SHIFT; - if (nsts > (ar->num_rx_chains - 1)) - nsts = ar->num_rx_chains - 1; value |= SM(nsts, WMI_TXBF_STS_CAP_OFFSET); } @@ -5051,7 +5240,7 @@ static int ath11k_mac_set_txbf_conf(struct ath11k_vif *arvif) static void ath11k_set_vht_txbf_cap(struct ath11k *ar, u32 *vht_cap) { bool subfer, subfee; - int sound_dim = 0, nsts = 0; + int sound_dim = 0; subfer = !!(*vht_cap & (IEEE80211_VHT_CAP_SU_BEAMFORMER_CAPABLE)); subfee = !!(*vht_cap & (IEEE80211_VHT_CAP_SU_BEAMFORMEE_CAPABLE)); @@ -5061,11 +5250,6 @@ static void ath11k_set_vht_txbf_cap(struct ath11k *ar, u32 *vht_cap) subfer = false; } - if (ar->num_rx_chains < 2) { - *vht_cap &= ~(IEEE80211_VHT_CAP_SU_BEAMFORMEE_CAPABLE); - subfee = false; - } - /* If SU Beaformer is not set, then disable MU Beamformer Capability */ if (!subfer) *vht_cap &= ~(IEEE80211_VHT_CAP_MU_BEAMFORMER_CAPABLE); @@ -5078,9 +5262,7 @@ static void ath11k_set_vht_txbf_cap(struct ath11k *ar, u32 *vht_cap) sound_dim >>= IEEE80211_VHT_CAP_SOUNDING_DIMENSIONS_SHIFT; *vht_cap &= ~IEEE80211_VHT_CAP_SOUNDING_DIMENSIONS_MASK; - nsts = (*vht_cap & IEEE80211_VHT_CAP_BEAMFORMEE_STS_MASK); - nsts >>= IEEE80211_VHT_CAP_BEAMFORMEE_STS_SHIFT; - *vht_cap &= ~IEEE80211_VHT_CAP_BEAMFORMEE_STS_MASK; + /* TODO: Need to check invalid STS and Sound_dim values set by FW? */ /* Enable Sounding Dimension Field only if SU BF is enabled */ if (subfer) { @@ -5092,15 +5274,9 @@ static void ath11k_set_vht_txbf_cap(struct ath11k *ar, u32 *vht_cap) *vht_cap |= sound_dim; } - /* Enable Beamformee STS Field only if SU BF is enabled */ - if (subfee) { - if (nsts > (ar->num_rx_chains - 1)) - nsts = ar->num_rx_chains - 1; - - nsts <<= IEEE80211_VHT_CAP_BEAMFORMEE_STS_SHIFT; - nsts &= IEEE80211_VHT_CAP_BEAMFORMEE_STS_MASK; - *vht_cap |= nsts; - } + /* Use the STS advertised by FW unless SU Beamformee is not supported*/ + if (!subfee) + *vht_cap &= ~(IEEE80211_VHT_CAP_BEAMFORMEE_STS_MASK); } static struct ieee80211_sta_vht_cap @@ -5316,6 +5492,43 @@ static __le16 ath11k_mac_setup_he_6ghz_cap(struct ath11k_pdev_cap *pcap, return cpu_to_le16(bcap->he_6ghz_capa); } +static void ath11k_mac_set_hemcsmap(struct ath11k *ar, + struct ath11k_pdev_cap *cap, + struct ieee80211_sta_he_cap *he_cap, + int band) +{ + u16 txmcs_map, rxmcs_map; + u32 i; + + rxmcs_map = 0; + txmcs_map = 0; + for (i = 0; i < 8; i++) { + if (i < ar->num_tx_chains && + (ar->cfg_tx_chainmask >> cap->tx_chain_mask_shift) & BIT(i)) + txmcs_map |= IEEE80211_HE_MCS_SUPPORT_0_11 << (i * 2); + else + txmcs_map |= IEEE80211_HE_MCS_NOT_SUPPORTED << (i * 2); + + if (i < ar->num_rx_chains && + (ar->cfg_rx_chainmask >> cap->tx_chain_mask_shift) & BIT(i)) + rxmcs_map |= IEEE80211_HE_MCS_SUPPORT_0_11 << (i * 2); + else + rxmcs_map |= IEEE80211_HE_MCS_NOT_SUPPORTED << (i * 2); + } + he_cap->he_mcs_nss_supp.rx_mcs_80 = + cpu_to_le16(rxmcs_map & 0xffff); + he_cap->he_mcs_nss_supp.tx_mcs_80 = + cpu_to_le16(txmcs_map & 0xffff); + he_cap->he_mcs_nss_supp.rx_mcs_160 = + cpu_to_le16(rxmcs_map & 0xffff); + he_cap->he_mcs_nss_supp.tx_mcs_160 = + cpu_to_le16(txmcs_map & 0xffff); + he_cap->he_mcs_nss_supp.rx_mcs_80p80 = + cpu_to_le16(rxmcs_map & 0xffff); + he_cap->he_mcs_nss_supp.tx_mcs_80p80 = + cpu_to_le16(txmcs_map & 0xffff); +} + static int ath11k_mac_copy_he_cap(struct ath11k *ar, struct ath11k_pdev_cap *cap, struct ieee80211_sband_iftype_data *data, @@ -5373,18 +5586,7 @@ static int ath11k_mac_copy_he_cap(struct ath11k *ar, break; } - he_cap->he_mcs_nss_supp.rx_mcs_80 = - cpu_to_le16(band_cap->he_mcs & 0xffff); - he_cap->he_mcs_nss_supp.tx_mcs_80 = - cpu_to_le16(band_cap->he_mcs & 0xffff); - he_cap->he_mcs_nss_supp.rx_mcs_160 = - cpu_to_le16((band_cap->he_mcs >> 16) & 0xffff); - he_cap->he_mcs_nss_supp.tx_mcs_160 = - cpu_to_le16((band_cap->he_mcs >> 16) & 0xffff); - he_cap->he_mcs_nss_supp.rx_mcs_80p80 = - cpu_to_le16((band_cap->he_mcs >> 16) & 0xffff); - he_cap->he_mcs_nss_supp.tx_mcs_80p80 = - cpu_to_le16((band_cap->he_mcs >> 16) & 0xffff); + ath11k_mac_set_hemcsmap(ar, cap, he_cap, band); memset(he_cap->ppe_thres, 0, sizeof(he_cap->ppe_thres)); if (he_cap_elem->phy_cap_info[6] & @@ -5794,6 +5996,11 @@ static int ath11k_mac_op_start(struct ieee80211_hw *hw) struct ath11k_pdev *pdev = ar->pdev; int ret; + if (ath11k_ftm_mode) { + ath11k_err(ab, "fail to start mac operations in ftm mode\n"); + return -EWOULDBLOCK; + } + ath11k_mac_drain_tx(ar); mutex_lock(&ar->conf_mutex); @@ -5808,6 +6015,7 @@ static int ath11k_mac_op_start(struct ieee80211_hw *hw) case ATH11K_STATE_RESTARTED: case ATH11K_STATE_WEDGED: case ATH11K_STATE_ON: + case ATH11K_STATE_TM: WARN_ON(1); ret = -EINVAL; goto err; @@ -5918,6 +6126,7 @@ static void ath11k_mac_op_stop(struct ieee80211_hw *hw) { struct ath11k *ar = hw->priv; struct htt_ppdu_stats_info *ppdu_stats, *tmp; + struct scan_chan_list_params *params, *tmp_ch; int ret; ath11k_mac_drain_tx(ar); @@ -5933,6 +6142,7 @@ static void ath11k_mac_op_stop(struct ieee80211_hw *hw) mutex_unlock(&ar->conf_mutex); cancel_delayed_work_sync(&ar->scan.timeout); + cancel_work_sync(&ar->channel_update_work); cancel_work_sync(&ar->regd_update_work); cancel_work_sync(&ar->ab->update_11d_work); @@ -5948,6 +6158,13 @@ static void ath11k_mac_op_stop(struct ieee80211_hw *hw) } spin_unlock_bh(&ar->data_lock); + spin_lock_bh(&ar->channel_update_lock); + list_for_each_entry_safe(params, tmp_ch, &ar->channel_update_queue, list) { + list_del(¶ms->list); + kfree(params); + } + spin_unlock_bh(&ar->channel_update_lock); + rcu_assign_pointer(ar->ab->pdevs_active[ar->pdev_idx], NULL); synchronize_rcu(); @@ -5982,69 +6199,6 @@ ath11k_mac_setup_vdev_create_params(struct ath11k_vif *arvif, } } -static u32 -ath11k_mac_prepare_he_mode(struct ath11k_pdev *pdev, u32 viftype) -{ - struct ath11k_pdev_cap *pdev_cap = &pdev->cap; - struct ath11k_band_cap *cap_band = NULL; - u32 *hecap_phy_ptr = NULL; - u32 hemode = 0; - - if (pdev->cap.supported_bands & WMI_HOST_WLAN_2G_CAP) - cap_band = &pdev_cap->band[NL80211_BAND_2GHZ]; - else - cap_band = &pdev_cap->band[NL80211_BAND_5GHZ]; - - hecap_phy_ptr = &cap_band->he_cap_phy_info[0]; - - hemode = FIELD_PREP(HE_MODE_SU_TX_BFEE, HE_SU_BFEE_ENABLE) | - FIELD_PREP(HE_MODE_SU_TX_BFER, HECAP_PHY_SUBFMR_GET(hecap_phy_ptr)) | - FIELD_PREP(HE_MODE_UL_MUMIMO, HECAP_PHY_ULMUMIMO_GET(hecap_phy_ptr)); - - /* TODO WDS and other modes */ - if (viftype == NL80211_IFTYPE_AP) { - hemode |= FIELD_PREP(HE_MODE_MU_TX_BFER, - HECAP_PHY_MUBFMR_GET(hecap_phy_ptr)) | - FIELD_PREP(HE_MODE_DL_OFDMA, HE_DL_MUOFDMA_ENABLE) | - FIELD_PREP(HE_MODE_UL_OFDMA, HE_UL_MUOFDMA_ENABLE); - } else { - hemode |= FIELD_PREP(HE_MODE_MU_TX_BFEE, HE_MU_BFEE_ENABLE); - } - - return hemode; -} - -static int ath11k_set_he_mu_sounding_mode(struct ath11k *ar, - struct ath11k_vif *arvif) -{ - u32 param_id, param_value; - struct ath11k_base *ab = ar->ab; - int ret = 0; - - param_id = WMI_VDEV_PARAM_SET_HEMU_MODE; - param_value = ath11k_mac_prepare_he_mode(ar->pdev, arvif->vif->type); - ret = ath11k_wmi_vdev_set_param_cmd(ar, arvif->vdev_id, - param_id, param_value); - if (ret) { - ath11k_warn(ab, "failed to set vdev %d HE MU mode: %d param_value %x\n", - arvif->vdev_id, ret, param_value); - return ret; - } - param_id = WMI_VDEV_PARAM_SET_HE_SOUNDING_MODE; - param_value = - FIELD_PREP(HE_VHT_SOUNDING_MODE, HE_VHT_SOUNDING_MODE_ENABLE) | - FIELD_PREP(HE_TRIG_NONTRIG_SOUNDING_MODE, - HE_TRIG_NONTRIG_SOUNDING_MODE_ENABLE); - ret = ath11k_wmi_vdev_set_param_cmd(ar, arvif->vdev_id, - param_id, param_value); - if (ret) { - ath11k_warn(ab, "failed to set vdev %d HE MU mode: %d\n", - arvif->vdev_id, ret); - return ret; - } - return ret; -} - static void ath11k_mac_op_update_vif_offload(struct ieee80211_hw *hw, struct ieee80211_vif *vif) { @@ -6211,6 +6365,40 @@ void ath11k_mac_11d_scan_stop_all(struct ath11k_base *ab) } } +static int ath11k_mac_vdev_delete(struct ath11k *ar, struct ath11k_vif *arvif) +{ + unsigned long time_left; + struct ieee80211_vif *vif = arvif->vif; + int ret = 0; + + lockdep_assert_held(&ar->conf_mutex); + + reinit_completion(&ar->vdev_delete_done); + + ret = ath11k_wmi_vdev_delete(ar, arvif->vdev_id); + if (ret) { + ath11k_warn(ar->ab, "failed to delete WMI vdev %d: %d\n", + arvif->vdev_id, ret); + return ret; + } + + time_left = wait_for_completion_timeout(&ar->vdev_delete_done, + ATH11K_VDEV_DELETE_TIMEOUT_HZ); + if (time_left == 0) { + ath11k_warn(ar->ab, "Timeout in receiving vdev delete response\n"); + return -ETIMEDOUT; + } + + ar->ab->free_vdev_map |= 1LL << (arvif->vdev_id); + ar->allocated_vdev_map &= ~(1LL << arvif->vdev_id); + ar->num_created_vdevs--; + + ath11k_dbg(ar->ab, ATH11K_DBG_MAC, "vdev %pM deleted, vdev_id %d\n", + vif->addr, arvif->vdev_id); + + return ret; +} + static int ath11k_mac_op_add_interface(struct ieee80211_hw *hw, struct ieee80211_vif *vif) { @@ -6433,6 +6621,14 @@ static int ath11k_mac_op_add_interface(struct ieee80211_hw *hw, ath11k_debugfs_add_interface(arvif); + if (ath11k_hw_supports_6g_cc_ext(ar)) { + struct cur_regulatory_info *reg_info; + + reg_info = &ab->reg_info_store[ar->pdev_idx]; + ath11k_dbg(ab, ATH11K_DBG_MAC, "mac interface added to change reg rules\n"); + ath11k_reg_handle_chan_list(ab, reg_info, IEEE80211_REG_LPI_AP); + } + mutex_unlock(&ar->conf_mutex); return 0; @@ -6448,10 +6644,7 @@ err_peer_del: } err_vdev_del: - ath11k_wmi_vdev_delete(ar, arvif->vdev_id); - ar->num_created_vdevs--; - ar->allocated_vdev_map &= ~(1LL << arvif->vdev_id); - ab->free_vdev_map |= 1LL << arvif->vdev_id; + ath11k_mac_vdev_delete(ar, arvif); spin_lock_bh(&ar->data_lock); list_del(&arvif->list); spin_unlock_bh(&ar->data_lock); @@ -6480,7 +6673,6 @@ static void ath11k_mac_op_remove_interface(struct ieee80211_hw *hw, struct ath11k *ar = hw->priv; struct ath11k_vif *arvif = ath11k_vif_to_arvif(vif); struct ath11k_base *ab = ar->ab; - unsigned long time_left; int ret; int i; @@ -6501,29 +6693,13 @@ static void ath11k_mac_op_remove_interface(struct ieee80211_hw *hw, arvif->vdev_id, ret); } - reinit_completion(&ar->vdev_delete_done); - - ret = ath11k_wmi_vdev_delete(ar, arvif->vdev_id); + ret = ath11k_mac_vdev_delete(ar, arvif); if (ret) { - ath11k_warn(ab, "failed to delete WMI vdev %d: %d\n", + ath11k_warn(ab, "failed to delete vdev %d: %d\n", arvif->vdev_id, ret); goto err_vdev_del; } - time_left = wait_for_completion_timeout(&ar->vdev_delete_done, - ATH11K_VDEV_DELETE_TIMEOUT_HZ); - if (time_left == 0) { - ath11k_warn(ab, "Timeout in receiving vdev delete response\n"); - goto err_vdev_del; - } - - ab->free_vdev_map |= 1LL << (arvif->vdev_id); - ar->allocated_vdev_map &= ~(1LL << arvif->vdev_id); - ar->num_created_vdevs--; - - ath11k_dbg(ab, ATH11K_DBG_MAC, "vdev %pM deleted, vdev_id %d\n", - vif->addr, arvif->vdev_id); - if (arvif->vdev_type == WMI_VDEV_TYPE_MONITOR) { clear_bit(ATH11K_FLAG_MONITOR_VDEV_CREATED, &ar->monitor_flags); ar->monitor_vdev_id = -1; @@ -6702,7 +6878,6 @@ ath11k_mac_vdev_start_restart(struct ath11k_vif *arvif, struct ath11k_base *ab = ar->ab; struct wmi_vdev_start_req_arg arg = {}; const struct cfg80211_chan_def *chandef = &ctx->def; - int he_support = arvif->vif->bss_conf.he_support; int ret = 0; lockdep_assert_held(&ar->conf_mutex); @@ -6743,15 +6918,6 @@ ath11k_mac_vdev_start_restart(struct ath11k_vif *arvif, spin_lock_bh(&ab->base_lock); arg.regdomain = ar->ab->dfs_region; spin_unlock_bh(&ab->base_lock); - - if (he_support) { - ret = ath11k_set_he_mu_sounding_mode(ar, arvif); - if (ret) { - ath11k_warn(ar->ab, "failed to set he mode vdev %i\n", - arg.vdev_id); - return ret; - } - } } arg.channel.passive |= !!(chandef->chan->flags & IEEE80211_CHAN_NO_IR); @@ -6775,6 +6941,12 @@ ath11k_mac_vdev_start_restart(struct ath11k_vif *arvif, return ret; } + if (ath11k_mac_supports_station_tpc(ar, arvif, chandef)) { + ath11k_mac_fill_reg_tpc_info(ar, arvif->vif, &arvif->chanctx); + ath11k_wmi_send_vdev_set_tpc_power(ar, arvif->vdev_id, + &arvif->reg_tpc_info); + } + if (!restart) ar->num_started_vdevs++; @@ -7088,6 +7260,469 @@ static int ath11k_start_vdev_delay(struct ieee80211_hw *hw, return 0; } +static u8 ath11k_mac_get_tpe_count(u8 txpwr_intrprt, u8 txpwr_cnt) +{ + switch (txpwr_intrprt) { + /* Refer "Table 9-276-Meaning of Maximum Transmit Power Count subfield + * if the Maximum Transmit Power Interpretation subfield is 0 or 2" of + * "IEEE Std 802.11ax 2021". + */ + case IEEE80211_TPE_LOCAL_EIRP: + case IEEE80211_TPE_REG_CLIENT_EIRP: + txpwr_cnt = txpwr_cnt <= 3 ? txpwr_cnt : 3; + txpwr_cnt = txpwr_cnt + 1; + break; + /* Refer "Table 9-277-Meaning of Maximum Transmit Power Count subfield + * if Maximum Transmit Power Interpretation subfield is 1 or 3" of + * "IEEE Std 802.11ax 2021". + */ + case IEEE80211_TPE_LOCAL_EIRP_PSD: + case IEEE80211_TPE_REG_CLIENT_EIRP_PSD: + txpwr_cnt = txpwr_cnt <= 4 ? txpwr_cnt : 4; + txpwr_cnt = txpwr_cnt ? (BIT(txpwr_cnt - 1)) : 1; + break; + } + + return txpwr_cnt; +} + +static u8 ath11k_mac_get_num_pwr_levels(struct cfg80211_chan_def *chan_def) +{ + if (chan_def->chan->flags & IEEE80211_CHAN_PSD) { + switch (chan_def->width) { + case NL80211_CHAN_WIDTH_20: + return 1; + case NL80211_CHAN_WIDTH_40: + return 2; + case NL80211_CHAN_WIDTH_80: + return 4; + case NL80211_CHAN_WIDTH_80P80: + case NL80211_CHAN_WIDTH_160: + return 8; + default: + return 1; + } + } else { + switch (chan_def->width) { + case NL80211_CHAN_WIDTH_20: + return 1; + case NL80211_CHAN_WIDTH_40: + return 2; + case NL80211_CHAN_WIDTH_80: + return 3; + case NL80211_CHAN_WIDTH_80P80: + case NL80211_CHAN_WIDTH_160: + return 4; + default: + return 1; + } + } +} + +static u16 ath11k_mac_get_6g_start_frequency(struct cfg80211_chan_def *chan_def) +{ + u16 diff_seq; + + /* It is to get the lowest channel number's center frequency of the chan. + * For example, + * bandwidth=40 MHz, center frequency is 5965, lowest channel is 1 + * with center frequency 5955, its diff is 5965 - 5955 = 10. + * bandwidth=80 MHz, center frequency is 5985, lowest channel is 1 + * with center frequency 5955, its diff is 5985 - 5955 = 30. + * bandwidth=160 MHz, center frequency is 6025, lowest channel is 1 + * with center frequency 5955, its diff is 6025 - 5955 = 70. + */ + switch (chan_def->width) { + case NL80211_CHAN_WIDTH_160: + diff_seq = 70; + break; + case NL80211_CHAN_WIDTH_80: + case NL80211_CHAN_WIDTH_80P80: + diff_seq = 30; + break; + case NL80211_CHAN_WIDTH_40: + diff_seq = 10; + break; + default: + diff_seq = 0; + } + + return chan_def->center_freq1 - diff_seq; +} + +static u16 ath11k_mac_get_seg_freq(struct cfg80211_chan_def *chan_def, + u16 start_seq, u8 seq) +{ + u16 seg_seq; + + /* It is to get the center frequency of the specific bandwidth. + * start_seq means the lowest channel number's center frequency. + * seq 0/1/2/3 means 20 MHz/40 MHz/80 MHz/160 MHz&80P80. + * For example, + * lowest channel is 1, its center frequency 5955, + * center frequency is 5955 when bandwidth=20 MHz, its diff is 5955 - 5955 = 0. + * lowest channel is 1, its center frequency 5955, + * center frequency is 5965 when bandwidth=40 MHz, its diff is 5965 - 5955 = 10. + * lowest channel is 1, its center frequency 5955, + * center frequency is 5985 when bandwidth=80 MHz, its diff is 5985 - 5955 = 30. + * lowest channel is 1, its center frequency 5955, + * center frequency is 6025 when bandwidth=160 MHz, its diff is 6025 - 5955 = 70. + */ + if (chan_def->width == NL80211_CHAN_WIDTH_80P80 && seq == 3) + return chan_def->center_freq2; + + seg_seq = 10 * (BIT(seq) - 1); + return seg_seq + start_seq; +} + +static void ath11k_mac_get_psd_channel(struct ath11k *ar, + u16 step_freq, + u16 *start_freq, + u16 *center_freq, + u8 i, + struct ieee80211_channel **temp_chan, + s8 *tx_power) +{ + /* It is to get the the center frequency for each 20 MHz. + * For example, if the chan is 160 MHz and center frequency is 6025, + * then it include 8 channels, they are 1/5/9/13/17/21/25/29, + * channel number 1's center frequency is 5955, it is parameter start_freq. + * parameter i is the step of the 8 channels. i is 0~7 for the 8 channels. + * the channel 1/5/9/13/17/21/25/29 maps i=0/1/2/3/4/5/6/7, + * and maps its center frequency is 5955/5975/5995/6015/6035/6055/6075/6095, + * the gap is 20 for each channel, parameter step_freq means the gap. + * after get the center frequency of each channel, it is easy to find the + * struct ieee80211_channel of it and get the max_reg_power. + */ + *center_freq = *start_freq + i * step_freq; + *temp_chan = ieee80211_get_channel(ar->hw->wiphy, *center_freq); + *tx_power = (*temp_chan)->max_reg_power; +} + +static void ath11k_mac_get_eirp_power(struct ath11k *ar, + u16 *start_freq, + u16 *center_freq, + u8 i, + struct ieee80211_channel **temp_chan, + struct cfg80211_chan_def *def, + s8 *tx_power) +{ + /* It is to get the the center frequency for 20 MHz/40 MHz/80 MHz/ + * 160 MHz&80P80 bandwidth, and then plus 10 to the center frequency, + * it is the center frequency of a channel number. + * For example, when configured channel number is 1. + * center frequency is 5965 when bandwidth=40 MHz, after plus 10, it is 5975, + * then it is channel number 5. + * center frequency is 5985 when bandwidth=80 MHz, after plus 10, it is 5995, + * then it is channel number 9. + * center frequency is 6025 when bandwidth=160 MHz, after plus 10, it is 6035, + * then it is channel number 17. + * after get the center frequency of each channel, it is easy to find the + * struct ieee80211_channel of it and get the max_reg_power. + */ + *center_freq = ath11k_mac_get_seg_freq(def, *start_freq, i); + + /* For the 20 MHz, its center frequency is same with same channel */ + if (i != 0) + *center_freq += 10; + + *temp_chan = ieee80211_get_channel(ar->hw->wiphy, *center_freq); + *tx_power = (*temp_chan)->max_reg_power; +} + +void ath11k_mac_fill_reg_tpc_info(struct ath11k *ar, + struct ieee80211_vif *vif, + struct ieee80211_chanctx_conf *ctx) +{ + struct ath11k_base *ab = ar->ab; + struct ath11k_vif *arvif = (void *)vif->drv_priv; + struct ieee80211_bss_conf *bss_conf = &vif->bss_conf; + struct ath11k_reg_tpc_power_info *reg_tpc_info = &arvif->reg_tpc_info; + struct ieee80211_channel *chan, *temp_chan; + u8 pwr_lvl_idx, num_pwr_levels, pwr_reduction; + bool is_psd_power = false, is_tpe_present = false; + s8 max_tx_power[IEEE80211_MAX_NUM_PWR_LEVEL], + psd_power, tx_power, eirp_power; + u16 oper_freq, start_freq, center_freq; + + chan = ctx->def.chan; + oper_freq = ctx->def.chan->center_freq; + start_freq = ath11k_mac_get_6g_start_frequency(&ctx->def); + pwr_reduction = bss_conf->pwr_reduction; + + if (arvif->reg_tpc_info.num_pwr_levels) { + is_tpe_present = true; + num_pwr_levels = arvif->reg_tpc_info.num_pwr_levels; + } else { + num_pwr_levels = ath11k_mac_get_num_pwr_levels(&ctx->def); + } + + for (pwr_lvl_idx = 0; pwr_lvl_idx < num_pwr_levels; pwr_lvl_idx++) { + /* STA received TPE IE*/ + if (is_tpe_present) { + /* local power is PSD power*/ + if (chan->flags & IEEE80211_CHAN_PSD) { + /* Connecting AP is psd power */ + if (reg_tpc_info->is_psd_power) { + is_psd_power = true; + ath11k_mac_get_psd_channel(ar, 20, + &start_freq, + ¢er_freq, + pwr_lvl_idx, + &temp_chan, + &tx_power); + psd_power = temp_chan->psd; + eirp_power = tx_power; + max_tx_power[pwr_lvl_idx] = + min_t(s8, + psd_power, + reg_tpc_info->tpe[pwr_lvl_idx]); + /* Connecting AP is not psd power */ + } else { + ath11k_mac_get_eirp_power(ar, + &start_freq, + ¢er_freq, + pwr_lvl_idx, + &temp_chan, + &ctx->def, + &tx_power); + psd_power = temp_chan->psd; + /* convert psd power to EIRP power based + * on channel width + */ + tx_power = + min_t(s8, tx_power, + psd_power + 13 + pwr_lvl_idx * 3); + max_tx_power[pwr_lvl_idx] = + min_t(s8, + tx_power, + reg_tpc_info->tpe[pwr_lvl_idx]); + } + /* local power is not PSD power */ + } else { + /* Connecting AP is psd power */ + if (reg_tpc_info->is_psd_power) { + is_psd_power = true; + ath11k_mac_get_psd_channel(ar, 20, + &start_freq, + ¢er_freq, + pwr_lvl_idx, + &temp_chan, + &tx_power); + eirp_power = tx_power; + max_tx_power[pwr_lvl_idx] = + reg_tpc_info->tpe[pwr_lvl_idx]; + /* Connecting AP is not psd power */ + } else { + ath11k_mac_get_eirp_power(ar, + &start_freq, + ¢er_freq, + pwr_lvl_idx, + &temp_chan, + &ctx->def, + &tx_power); + max_tx_power[pwr_lvl_idx] = + min_t(s8, + tx_power, + reg_tpc_info->tpe[pwr_lvl_idx]); + } + } + /* STA not received TPE IE */ + } else { + /* local power is PSD power*/ + if (chan->flags & IEEE80211_CHAN_PSD) { + is_psd_power = true; + ath11k_mac_get_psd_channel(ar, 20, + &start_freq, + ¢er_freq, + pwr_lvl_idx, + &temp_chan, + &tx_power); + psd_power = temp_chan->psd; + eirp_power = tx_power; + max_tx_power[pwr_lvl_idx] = psd_power; + } else { + ath11k_mac_get_eirp_power(ar, + &start_freq, + ¢er_freq, + pwr_lvl_idx, + &temp_chan, + &ctx->def, + &tx_power); + max_tx_power[pwr_lvl_idx] = + min_t(s8, + tx_power, + reg_tpc_info->tpe[pwr_lvl_idx]); + } + } + + if (is_psd_power) { + /* If AP local power constraint is present */ + if (pwr_reduction) + eirp_power = eirp_power - pwr_reduction; + + /* If firmware updated max tx power is non zero, then take + * the min of firmware updated ap tx power + * and max power derived from above mentioned parameters. + */ + ath11k_dbg(ab, ATH11K_DBG_MAC, + "eirp power : %d firmware report power : %d\n", + eirp_power, ar->max_allowed_tx_power); + if (ar->max_allowed_tx_power) + eirp_power = min_t(s8, + eirp_power, + ar->max_allowed_tx_power); + } else { + /* If AP local power constraint is present */ + if (pwr_reduction) + max_tx_power[pwr_lvl_idx] = + max_tx_power[pwr_lvl_idx] - pwr_reduction; + /* If firmware updated max tx power is non zero, then take + * the min of firmware updated ap tx power + * and max power derived from above mentioned parameters. + */ + if (ar->max_allowed_tx_power) + max_tx_power[pwr_lvl_idx] = + min_t(s8, + max_tx_power[pwr_lvl_idx], + ar->max_allowed_tx_power); + } + reg_tpc_info->chan_power_info[pwr_lvl_idx].chan_cfreq = center_freq; + reg_tpc_info->chan_power_info[pwr_lvl_idx].tx_power = + max_tx_power[pwr_lvl_idx]; + } + + reg_tpc_info->num_pwr_levels = num_pwr_levels; + reg_tpc_info->is_psd_power = is_psd_power; + reg_tpc_info->eirp_power = eirp_power; + reg_tpc_info->power_type_6g = + ath11k_ieee80211_ap_pwr_type_convert(vif->bss_conf.power_type); +} + +static void ath11k_mac_parse_tx_pwr_env(struct ath11k *ar, + struct ieee80211_vif *vif, + struct ieee80211_chanctx_conf *ctx) +{ + struct ath11k_base *ab = ar->ab; + struct ath11k_vif *arvif = (void *)vif->drv_priv; + struct ieee80211_bss_conf *bss_conf = &vif->bss_conf; + struct ieee80211_tx_pwr_env *single_tpe; + enum wmi_reg_6ghz_client_type client_type; + struct cur_regulatory_info *reg_info; + int i; + u8 pwr_count, pwr_interpret, pwr_category; + u8 psd_index = 0, non_psd_index = 0, local_tpe_count = 0, reg_tpe_count = 0; + bool use_local_tpe, non_psd_set = false, psd_set = false; + + reg_info = &ab->reg_info_store[ar->pdev_idx]; + client_type = reg_info->client_type; + + for (i = 0; i < bss_conf->tx_pwr_env_num; i++) { + single_tpe = &bss_conf->tx_pwr_env[i]; + pwr_category = u8_get_bits(single_tpe->tx_power_info, + IEEE80211_TX_PWR_ENV_INFO_CATEGORY); + pwr_interpret = u8_get_bits(single_tpe->tx_power_info, + IEEE80211_TX_PWR_ENV_INFO_INTERPRET); + + if (pwr_category == client_type) { + if (pwr_interpret == IEEE80211_TPE_LOCAL_EIRP || + pwr_interpret == IEEE80211_TPE_LOCAL_EIRP_PSD) + local_tpe_count++; + else if (pwr_interpret == IEEE80211_TPE_REG_CLIENT_EIRP || + pwr_interpret == IEEE80211_TPE_REG_CLIENT_EIRP_PSD) + reg_tpe_count++; + } + } + + if (!reg_tpe_count && !local_tpe_count) { + ath11k_warn(ab, + "no transmit power envelope match client power type %d\n", + client_type); + return; + } else if (!reg_tpe_count) { + use_local_tpe = true; + } else { + use_local_tpe = false; + } + + for (i = 0; i < bss_conf->tx_pwr_env_num; i++) { + single_tpe = &bss_conf->tx_pwr_env[i]; + pwr_category = u8_get_bits(single_tpe->tx_power_info, + IEEE80211_TX_PWR_ENV_INFO_CATEGORY); + pwr_interpret = u8_get_bits(single_tpe->tx_power_info, + IEEE80211_TX_PWR_ENV_INFO_INTERPRET); + + if (pwr_category != client_type) + continue; + + /* get local transmit power envelope */ + if (use_local_tpe) { + if (pwr_interpret == IEEE80211_TPE_LOCAL_EIRP) { + non_psd_index = i; + non_psd_set = true; + } else if (pwr_interpret == IEEE80211_TPE_LOCAL_EIRP_PSD) { + psd_index = i; + psd_set = true; + } + /* get regulatory transmit power envelope */ + } else { + if (pwr_interpret == IEEE80211_TPE_REG_CLIENT_EIRP) { + non_psd_index = i; + non_psd_set = true; + } else if (pwr_interpret == IEEE80211_TPE_REG_CLIENT_EIRP_PSD) { + psd_index = i; + psd_set = true; + } + } + } + + if (non_psd_set && !psd_set) { + single_tpe = &bss_conf->tx_pwr_env[non_psd_index]; + pwr_count = u8_get_bits(single_tpe->tx_power_info, + IEEE80211_TX_PWR_ENV_INFO_COUNT); + pwr_interpret = u8_get_bits(single_tpe->tx_power_info, + IEEE80211_TX_PWR_ENV_INFO_INTERPRET); + arvif->reg_tpc_info.is_psd_power = false; + arvif->reg_tpc_info.eirp_power = 0; + + arvif->reg_tpc_info.num_pwr_levels = + ath11k_mac_get_tpe_count(pwr_interpret, pwr_count); + for (i = 0; i < arvif->reg_tpc_info.num_pwr_levels; i++) { + ath11k_dbg(ab, ATH11K_DBG_MAC, + "non PSD power[%d] : %d\n", + i, single_tpe->tx_power[i]); + arvif->reg_tpc_info.tpe[i] = single_tpe->tx_power[i] / 2; + } + } + + if (psd_set) { + single_tpe = &bss_conf->tx_pwr_env[psd_index]; + pwr_count = u8_get_bits(single_tpe->tx_power_info, + IEEE80211_TX_PWR_ENV_INFO_COUNT); + pwr_interpret = u8_get_bits(single_tpe->tx_power_info, + IEEE80211_TX_PWR_ENV_INFO_INTERPRET); + arvif->reg_tpc_info.is_psd_power = true; + + if (pwr_count == 0) { + ath11k_dbg(ab, ATH11K_DBG_MAC, + "TPE PSD power : %d\n", single_tpe->tx_power[0]); + arvif->reg_tpc_info.num_pwr_levels = + ath11k_mac_get_num_pwr_levels(&ctx->def); + for (i = 0; i < arvif->reg_tpc_info.num_pwr_levels; i++) + arvif->reg_tpc_info.tpe[i] = single_tpe->tx_power[0] / 2; + } else { + arvif->reg_tpc_info.num_pwr_levels = + ath11k_mac_get_tpe_count(pwr_interpret, pwr_count); + for (i = 0; i < arvif->reg_tpc_info.num_pwr_levels; i++) { + ath11k_dbg(ab, ATH11K_DBG_MAC, + "TPE PSD power[%d] : %d\n", + i, single_tpe->tx_power[i]); + arvif->reg_tpc_info.tpe[i] = single_tpe->tx_power[i] / 2; + } + } + } +} + static int ath11k_mac_op_assign_vif_chanctx(struct ieee80211_hw *hw, struct ieee80211_vif *vif, @@ -7099,6 +7734,8 @@ ath11k_mac_op_assign_vif_chanctx(struct ieee80211_hw *hw, struct ath11k_vif *arvif = (void *)vif->drv_priv; int ret; struct peer_create_params param; + struct cur_regulatory_info *reg_info; + enum ieee80211_ap_reg_power power_type; mutex_lock(&ar->conf_mutex); @@ -7106,6 +7743,20 @@ ath11k_mac_op_assign_vif_chanctx(struct ieee80211_hw *hw, "mac chanctx assign ptr %pK vdev_id %i\n", ctx, arvif->vdev_id); + if (ath11k_hw_supports_6g_cc_ext(ar) && + ctx->def.chan->band == NL80211_BAND_6GHZ && + arvif->vdev_type == WMI_VDEV_TYPE_STA) { + reg_info = &ab->reg_info_store[ar->pdev_idx]; + power_type = vif->bss_conf.power_type; + ath11k_dbg(ab, ATH11K_DBG_MAC, "mac chanctx power type %d\n", + power_type); + if (power_type == IEEE80211_REG_UNSET_AP) + power_type = IEEE80211_REG_LPI_AP; + ath11k_reg_handle_chan_list(ab, reg_info, power_type); + arvif->chanctx = *ctx; + ath11k_mac_parse_tx_pwr_env(ar, vif, ctx); + } + /* for QCA6390 bss peer must be created before vdev_start */ if (ab->hw_params.vdev_start_delay && arvif->vdev_type != WMI_VDEV_TYPE_AP && @@ -7977,6 +8628,7 @@ ath11k_mac_op_reconfig_complete(struct ieee80211_hw *hw, struct ath11k *ar = hw->priv; struct ath11k_base *ab = ar->ab; int recovery_count; + struct ath11k_vif *arvif; if (reconfig_type != IEEE80211_RECONFIG_TYPE_RESTART) return; @@ -8012,6 +8664,12 @@ ath11k_mac_op_reconfig_complete(struct ieee80211_hw *hw, ath11k_dbg(ab, ATH11K_DBG_BOOT, "reset success\n"); } } + if (ar->ab->hw_params.support_fw_mac_sequence) { + list_for_each_entry(arvif, &ar->arvifs, list) { + if (arvif->is_up && arvif->vdev_type == WMI_VDEV_TYPE_STA) + ieee80211_hw_restart_disconnect(arvif->vif); + } + } } mutex_unlock(&ar->conf_mutex); @@ -8774,6 +9432,31 @@ static int ath11k_mac_setup_channels_rates(struct ath11k *ar, return 0; } +static void ath11k_mac_setup_mac_address_list(struct ath11k *ar) +{ + struct mac_address *addresses; + u16 n_addresses; + int i; + + if (!ar->ab->hw_params.single_pdev_only || ar->ab->hw_params.num_rxmda_per_pdev < 2) + return; + + n_addresses = 3; + addresses = kcalloc(n_addresses, sizeof(*addresses), GFP_KERNEL); + if (!addresses) + return; + + memcpy(addresses[0].addr, ar->mac_addr, ETH_ALEN); + for (i = 1; i < n_addresses; i++) { + memcpy(addresses[i].addr, ar->mac_addr, ETH_ALEN); + addresses[i].addr[0] |= 0x2; + addresses[i].addr[0] += (i - 1) << 4; + } + + ar->hw->wiphy->addresses = addresses; + ar->hw->wiphy->n_addresses = n_addresses; +} + static int ath11k_mac_setup_iface_combinations(struct ath11k *ar) { struct ath11k_base *ab = ar->ab; @@ -8793,28 +9476,43 @@ static int ath11k_mac_setup_iface_combinations(struct ath11k *ar) return -ENOMEM; } - limits[0].max = 1; - limits[0].types |= BIT(NL80211_IFTYPE_STATION); - - limits[1].max = 16; - limits[1].types |= BIT(NL80211_IFTYPE_AP); + if (ab->hw_params.single_pdev_only && ar->ab->hw_params.num_rxmda_per_pdev > 1) { + limits[0].max = 2; + limits[0].types |= BIT(NL80211_IFTYPE_STATION); - if (IS_ENABLED(CONFIG_MAC80211_MESH) && - ab->hw_params.interface_modes & BIT(NL80211_IFTYPE_MESH_POINT)) - limits[1].types |= BIT(NL80211_IFTYPE_MESH_POINT); + limits[1].max = 1; + limits[1].types |= BIT(NL80211_IFTYPE_AP); - combinations[0].limits = limits; - combinations[0].n_limits = n_limits; - combinations[0].max_interfaces = 16; - combinations[0].num_different_channels = 1; - combinations[0].beacon_int_infra_match = true; - combinations[0].beacon_int_min_gcd = 100; - combinations[0].radar_detect_widths = BIT(NL80211_CHAN_WIDTH_20_NOHT) | - BIT(NL80211_CHAN_WIDTH_20) | - BIT(NL80211_CHAN_WIDTH_40) | - BIT(NL80211_CHAN_WIDTH_80) | - BIT(NL80211_CHAN_WIDTH_80P80) | - BIT(NL80211_CHAN_WIDTH_160); + combinations[0].limits = limits; + combinations[0].n_limits = 2; + combinations[0].max_interfaces = 3; + combinations[0].num_different_channels = 2; + combinations[0].beacon_int_infra_match = true; + combinations[0].beacon_int_min_gcd = 100; + } else { + limits[0].max = 1; + limits[0].types |= BIT(NL80211_IFTYPE_STATION); + + limits[1].max = 16; + limits[1].types |= BIT(NL80211_IFTYPE_AP); + + if (IS_ENABLED(CONFIG_MAC80211_MESH) && + ab->hw_params.interface_modes & BIT(NL80211_IFTYPE_MESH_POINT)) + limits[1].types |= BIT(NL80211_IFTYPE_MESH_POINT); + + combinations[0].limits = limits; + combinations[0].n_limits = 2; + combinations[0].max_interfaces = 16; + combinations[0].num_different_channels = 1; + combinations[0].beacon_int_infra_match = true; + combinations[0].beacon_int_min_gcd = 100; + combinations[0].radar_detect_widths = BIT(NL80211_CHAN_WIDTH_20_NOHT) | + BIT(NL80211_CHAN_WIDTH_20) | + BIT(NL80211_CHAN_WIDTH_40) | + BIT(NL80211_CHAN_WIDTH_80) | + BIT(NL80211_CHAN_WIDTH_80P80) | + BIT(NL80211_CHAN_WIDTH_160); + } ar->hw->wiphy->iface_combinations = combinations; ar->hw->wiphy->n_iface_combinations = 1; @@ -8861,6 +9559,7 @@ static const struct wiphy_iftype_ext_capab ath11k_iftypes_ext_capa[] = { static void __ath11k_mac_unregister(struct ath11k *ar) { + cancel_work_sync(&ar->channel_update_work); cancel_work_sync(&ar->regd_update_work); ieee80211_unregister_hw(ar->hw); @@ -8875,6 +9574,8 @@ static void __ath11k_mac_unregister(struct ath11k *ar) kfree(ar->hw->wiphy->iface_combinations[0].limits); kfree(ar->hw->wiphy->iface_combinations); + kfree(ar->hw->wiphy->addresses); + SET_IEEE80211_DEV(ar->hw, NULL); } @@ -8917,6 +9618,7 @@ static int __ath11k_mac_register(struct ath11k *ar) ath11k_pdev_caps_update(ar); SET_IEEE80211_PERM_ADDR(ar->hw, ar->mac_addr); + ath11k_mac_setup_mac_address_list(ar); SET_IEEE80211_DEV(ar->hw, ab->dev); @@ -9054,6 +9756,8 @@ static int __ath11k_mac_register(struct ath11k *ar) ar->hw->wiphy->iftype_ext_capab = ath11k_iftypes_ext_capa; ar->hw->wiphy->num_iftype_ext_capab = ARRAY_SIZE(ath11k_iftypes_ext_capa); + ar->hw->wiphy->vendor_commands = ath11k_vendor_cmds; + ar->hw->wiphy->n_vendor_commands = ARRAY_SIZE(ath11k_vendor_cmds); if (ar->supports_6ghz) { wiphy_ext_feature_set(ar->hw->wiphy, @@ -9245,6 +9949,9 @@ int ath11k_mac_allocate(struct ath11k_base *ab) INIT_DELAYED_WORK(&ar->scan.timeout, ath11k_scan_timeout_work); INIT_WORK(&ar->regd_update_work, ath11k_regd_update_work); + INIT_WORK(&ar->channel_update_work, ath11k_regd_update_chan_list_work); + INIT_LIST_HEAD(&ar->channel_update_queue); + spin_lock_init(&ar->channel_update_lock); INIT_WORK(&ar->wmi_mgmt_tx_work, ath11k_mgmt_over_wmi_tx_work); skb_queue_head_init(&ar->wmi_mgmt_tx_queue); @@ -9315,3 +10022,27 @@ int ath11k_mac_vif_set_keepalive(struct ath11k_vif *arvif, return 0; } + +static int ath11k_mac_vif_send_coex_config(struct ath11k_vif *arvif, + struct wmi_coex_config_params *param) +{ + return ath11k_wmi_send_coex_config(arvif->ar, param); +} + +int ath11k_mac_send_coex_config(struct ath11k *ar, + struct wmi_coex_config_params *param) +{ + struct ath11k_vif *arvif; + int ret; + + lockdep_assert_held(&ar->conf_mutex); + + list_for_each_entry(arvif, &ar->arvifs, list) { + param->vdev_id = arvif->vdev_id; + ret = ath11k_mac_vif_send_coex_config(arvif, param); + if (ret) + return ret; + } + + return 0; +} diff --git a/drivers/net/wireless/ath/ath11k/mac.h b/drivers/net/wireless/ath/ath11k/mac.h index 0231783ad754..205140e315be 100644 --- a/drivers/net/wireless/ath/ath11k/mac.h +++ b/drivers/net/wireless/ath/ath11k/mac.h @@ -158,7 +158,7 @@ struct ath11k_vif *ath11k_mac_get_vif_up(struct ath11k_base *ab); struct ath11k *ath11k_mac_get_ar_by_vdev_id(struct ath11k_base *ab, u32 vdev_id); struct ath11k *ath11k_mac_get_ar_by_pdev_id(struct ath11k_base *ab, u32 pdev_id); - +enum wmi_vdev_type ath11k_mac_get_ar_vdev_type(struct ath11k *ar); void ath11k_mac_drain_tx(struct ath11k *ar); void ath11k_mac_peer_cleanup_all(struct ath11k *ar); int ath11k_mac_tx_mgmt_pending_free(int buf_id, void *skb, void *ctx); @@ -175,4 +175,9 @@ int ath11k_mac_wait_tx_complete(struct ath11k *ar); int ath11k_mac_vif_set_keepalive(struct ath11k_vif *arvif, enum wmi_sta_keepalive_method method, u32 interval); +int ath11k_mac_send_coex_config(struct ath11k *ar, + struct wmi_coex_config_params *param); +void ath11k_mac_fill_reg_tpc_info(struct ath11k *ar, + struct ieee80211_vif *vif, + struct ieee80211_chanctx_conf *ctx); #endif diff --git a/drivers/net/wireless/ath/ath11k/mhi.c b/drivers/net/wireless/ath/ath11k/mhi.c index a62ee05c5409..e3ade8e5ce43 100644 --- a/drivers/net/wireless/ath/ath11k/mhi.c +++ b/drivers/net/wireless/ath/ath11k/mhi.c @@ -324,6 +324,7 @@ static void ath11k_mhi_op_status_cb(struct mhi_controller *mhi_cntrl, enum mhi_callback cb) { struct ath11k_base *ab = dev_get_drvdata(mhi_cntrl->cntrl_dev); + struct ath11k_pci *ar_pci = ath11k_pci_priv(ab); ath11k_dbg(ab, ATH11K_DBG_BOOT, "mhi notify status reason %s\n", ath11k_mhi_op_callback_to_str(cb)); @@ -334,7 +335,7 @@ static void ath11k_mhi_op_status_cb(struct mhi_controller *mhi_cntrl, break; case MHI_CB_EE_RDDM: if (!(test_bit(ATH11K_FLAG_UNREGISTERING, &ab->dev_flags))) - queue_work(ab->workqueue_aux, &ab->reset_work); + queue_work(ab->workqueue_aux, &ar_pci->rddm_worker); break; default: break; @@ -414,7 +415,7 @@ int ath11k_mhi_register(struct ath11k_pci *ab_pci) goto free_controller; } else { mhi_ctrl->iova_start = 0; - mhi_ctrl->iova_stop = 0xFFFFFFFF; + mhi_ctrl->iova_stop = 0xFFFFFFFFF; } mhi_ctrl->rddm_size = RDDM_DUMP_SIZE; @@ -434,6 +435,7 @@ int ath11k_mhi_register(struct ath11k_pci *ab_pci) case ATH11K_HW_QCA6390_HW20: case ATH11K_HW_WCN6855_HW20: case ATH11K_HW_WCN6855_HW21: + case ATH11K_HW_QCA206X_HW21: ath11k_mhi_config = &ath11k_mhi_config_qca6390; break; default: @@ -525,3 +527,18 @@ int ath11k_mhi_resume(struct ath11k_pci *ab_pci) return 0; } + + +int ath11k_mhi_force_rddm(struct ath11k_pci *ab_pci) +{ + struct ath11k_base *ab = ab_pci->ab; + int ret; + + ret = mhi_force_rddm_mode(ab_pci->mhi_ctrl); + if (ret) { + ath11k_warn(ab, "failed to resume mhi: %d", ret); + return ret; + } + + return 0; +} diff --git a/drivers/net/wireless/ath/ath11k/mhi.h b/drivers/net/wireless/ath/ath11k/mhi.h index 8d9f852da695..215428b87fc4 100644 --- a/drivers/net/wireless/ath/ath11k/mhi.h +++ b/drivers/net/wireless/ath/ath11k/mhi.h @@ -25,5 +25,6 @@ void ath11k_mhi_clear_vector(struct ath11k_base *ab); int ath11k_mhi_suspend(struct ath11k_pci *ar_pci); int ath11k_mhi_resume(struct ath11k_pci *ar_pci); +int ath11k_mhi_force_rddm(struct ath11k_pci *ab_pci); #endif diff --git a/drivers/net/wireless/ath/ath11k/pci.c b/drivers/net/wireless/ath/ath11k/pci.c index 3c6005ab9a71..5af419aa10ca 100644 --- a/drivers/net/wireless/ath/ath11k/pci.c +++ b/drivers/net/wireless/ath/ath11k/pci.c @@ -1,7 +1,7 @@ // SPDX-License-Identifier: BSD-3-Clause-Clear /* * Copyright (c) 2019-2020 The Linux Foundation. All rights reserved. - * Copyright (c) 2021-2022, Qualcomm Innovation Center, Inc. All rights reserved. + * Copyright (c) 2021-2023 Qualcomm Innovation Center, Inc. All rights reserved. */ #include <linux/module.h> @@ -15,9 +15,10 @@ #include "mhi.h" #include "debug.h" #include "pcic.h" +#include "coredump.h" #define ATH11K_PCI_BAR_NUM 0 -#define ATH11K_PCI_DMA_MASK 32 +#define ATH11K_PCI_DMA_MASK 36 #define TCSR_SOC_HW_VERSION 0x0224 #define TCSR_SOC_HW_VERSION_MAJOR_MASK GENMASK(11, 8) @@ -27,6 +28,8 @@ #define QCN9074_DEVICE_ID 0x1104 #define WCN6855_DEVICE_ID 0x1103 +#define SUB_VERSION 0x1910010 + static const struct pci_device_id ath11k_pci_id_table[] = { { PCI_VDEVICE(QCOM, QCA6390_DEVICE_ID) }, { PCI_VDEVICE(QCOM, WCN6855_DEVICE_ID) }, @@ -36,6 +39,124 @@ static const struct pci_device_id ath11k_pci_id_table[] = { MODULE_DEVICE_TABLE(pci, ath11k_pci_id_table); +static struct cnss_pci_reg register_rddm_fail_debug[] = { + {"PCIE_BHI_VERSION_LOWER", HWIO_PCIE_PCIE_BHI_VERSION_LOWER_ADDR, 0}, + {"PCIE_BHI_VERSION_UPPER", HWIO_PCIE_PCIE_BHI_VERSION_UPPER_ADDR, 0}, + {"PCIE_BHI_IMGADDR_LOWER", HWIO_PCIE_PCIE_BHI_IMGADDR_LOWER_ADDR, 0}, + {"PCIE_BHI_IMGADDR_UPPER", HWIO_PCIE_PCIE_BHI_IMGADDR_UPPER_ADDR, 0}, + {"PCIE_BHI_IMGSIZE", HWIO_PCIE_PCIE_BHI_IMGSIZE_ADDR, 0}, + {"PCIE_BHI_IMGTXDB", HWIO_PCIE_PCIE_BHI_IMGTXDB_ADDR, 0}, + {"PCIE_BHI_INTVEC", HWIO_PCIE_PCIE_BHI_INTVEC_ADDR, 0}, + {"PCIE_BHI_EXECENV", HWIO_PCIE_PCIE_BHI_EXECENV_ADDR, 0}, + {"PCIE_BHI_STATUS", HWIO_PCIE_PCIE_BHI_STATUS_ADDR, 0}, + {"PCIE_BHI_ERRCODE", HWIO_PCIE_PCIE_BHI_ERRCODE_ADDR, 0}, + {"PCIE_BHI_ERRDBG1", HWIO_PCIE_PCIE_BHI_ERRDBG1_ADDR, 0}, + {"PCIE_BHI_ERRDBG2", HWIO_PCIE_PCIE_BHI_ERRDBG2_ADDR, 0}, + {"PCIE_BHI_ERRDBG3", HWIO_PCIE_PCIE_BHI_ERRDBG3_ADDR, 0}, + {"PCIE_BHI_SERIALNUM", HWIO_PCIE_PCIE_BHI_SERIALNUM_ADDR, 0}, + {"PCIE_BHI_SBLANTIROLLVER", HWIO_PCIE_PCIE_BHI_SBLANTIROLLVER_ADDR, 0}, + {"PCIE_BHI_NUMSEG", HWIO_PCIE_PCIE_BHI_NUMSEG_ADDR, 0}, + {"PCIE_BHI_MSMHWID_0", HWIO_PCIE_PCIE_BHI_MSMHWID_0_ADDR, 0}, + {"PCIE_BHI_MSMHWID_1", HWIO_PCIE_PCIE_BHI_MSMHWID_1_ADDR, 0}, + {"PCIE_BHI_MSMHWID_2", HWIO_PCIE_PCIE_BHI_MSMHWID_2_ADDR, 0}, + {"PCIE_BHI_MSMHWID_3", HWIO_PCIE_PCIE_BHI_MSMHWID_3_ADDR, 0}, + {"PCIE_BHI_MSMHWID_4", HWIO_PCIE_PCIE_BHI_MSMHWID_4_ADDR, 0}, + {"PCIE_BHI_MSMHWID_5", HWIO_PCIE_PCIE_BHI_MSMHWID_5_ADDR, 0}, + {"PCIE_BHI_OEMPKHASH_0", HWIO_PCIE_PCIE_BHI_OEMPKHASH_0_ADDR, 0}, + {"PCIE_BHI_OEMPKHASH_1", HWIO_PCIE_PCIE_BHI_OEMPKHASH_1_ADDR, 0}, + {"PCIE_BHI_OEMPKHASH_2", HWIO_PCIE_PCIE_BHI_OEMPKHASH_2_ADDR, 0}, + {"PCIE_BHI_OEMPKHASH_3", HWIO_PCIE_PCIE_BHI_OEMPKHASH_3_ADDR, 0}, + {"PCIE_BHI_OEMPKHASH_4", HWIO_PCIE_PCIE_BHI_OEMPKHASH_4_ADDR, 0}, + {"PCIE_BHI_OEMPKHASH_5", HWIO_PCIE_PCIE_BHI_OEMPKHASH_5_ADDR, 0}, + {"PCIE_BHI_OEMPKHASH_6", HWIO_PCIE_PCIE_BHI_OEMPKHASH_6_ADDR, 0}, + {"PCIE_BHI_OEMPKHASH_7", HWIO_PCIE_PCIE_BHI_OEMPKHASH_7_ADDR, 0}, + {"PCIE_BHI_OEMPKHASH_8", HWIO_PCIE_PCIE_BHI_OEMPKHASH_8_ADDR, 0}, + {"PCIE_BHI_OEMPKHASH_9", HWIO_PCIE_PCIE_BHI_OEMPKHASH_9_ADDR, 0}, + {"PCIE_BHI_TXVECDB", HWIO_PCIE_PCIE_BHI_TXVECDB_ADDR, 0}, + {"PCIE_BHI_TXVECSTATUS", HWIO_PCIE_PCIE_BHI_TXVECSTATUS_ADDR, 0}, + {"PCIE_BHI_RXVECDB", HWIO_PCIE_PCIE_BHI_RXVECDB_ADDR, 0}, + {"PCIE_BHI_RXVECSTATUS", HWIO_PCIE_PCIE_BHI_RXVECSTATUS_ADDR, 0}, + /* After dump this register, recovery will fail for QCA6490 */ + //{"PCIE_WLAON_RESET_DBG_SW_ENTRY", WLAON_RESET_DBG_SW_ENTRY, 0}, + {NULL}, +}; + +static struct cnss_pci_reg register_to_dump[] = { + {"QDSS_APB_DEC_CS_QDSSCSR_ETRIRQCTRL", QDSS_APB_DEC_CS_QDSSCSR_ETRIRQCTRL, 0}, + {"QDSS_APB_DEC_CS_QDSSCSR_PRESERVEETF", QDSS_APB_DEC_CS_QDSSCSR_PRESERVEETF, 0}, + {"QDSS_APB_DEC_CS_QDSSCSR_PRESERVEETR0", QDSS_APB_DEC_CS_QDSSCSR_PRESERVEETR0, 0}, + {"QDSS_APB_DEC_CS_QDSSCSR_PRESERVEETR1", QDSS_APB_DEC_CS_QDSSCSR_PRESERVEETR1, 0}, + {"Q6SS_PRIVCSR_QDSP6SS_QTMR_V1_CNTP_CTL_0", Q6SS_PRIVCSR_QDSP6SS_QTMR_V1_CNTP_CTL_0, 0}, + {"Q6SS_PRIVCSR_QDSP6SS_QTMR_V1_CNTP_CTL_1", Q6SS_PRIVCSR_QDSP6SS_QTMR_V1_CNTP_CTL_1, 0}, + {"Q6SS_PRIVCSR_QDSP6SS_QTMR_V1_CNTP_CTL_2", Q6SS_PRIVCSR_QDSP6SS_QTMR_V1_CNTP_CTL_2, 0}, + {NULL}, +}; + +#define LINE_LEN_MAX 80 + +static int ath11k_register_dump(struct ath11k_base *ab, + u8 **buf, + size_t *buf_len, + struct cnss_pci_reg *regs, + size_t num) +{ + u32 i; + u32 offset = 0; + size_t line_len; + ssize_t len; + char (*line_ptr)[LINE_LEN_MAX]; + + *buf_len = 0; + + line_ptr = (char (*)[LINE_LEN_MAX])vzalloc(num * LINE_LEN_MAX); + if (!line_ptr) + return -ENOMEM; + + for (i = 0; regs[i].name; i++) { + regs[i].value = ath11k_pcic_read32(ab, regs[i].offset); + ath11k_info(ab, "%s[0x%x] = 0x%x\n", + regs[i].name, regs[i].offset, regs[i].value); + len = snprintf(line_ptr[i], LINE_LEN_MAX, "%s[0x%x] = 0x%x\n", + regs[i].name, regs[i].offset, regs[i].value); + *buf_len += len; + } + + ath11k_info(ab, "%s buf len=%lu\n", __func__, *buf_len); + *buf = vzalloc(*buf_len); + if (!*buf) + return -ENOMEM; + + for (i = 0; i < num; ++i) { + line_len = strlen(line_ptr[i]); + memcpy(*buf + offset, line_ptr[i], line_len); + offset += line_len; + } + + vfree(line_ptr); + + return 0; +} + +void ath11k_pci_register_dump(struct ath11k_pci *ab_pci) +{ + size_t num; + struct register_crash_data *crash_data = &ab_pci->reg_data; + + num = sizeof(register_to_dump) / sizeof(struct cnss_pci_reg) - 1; + ath11k_register_dump(ab_pci->ab, + &crash_data->reg_buf, + &crash_data->reg_buf_len, + register_to_dump, + num); + + num = sizeof(register_rddm_fail_debug) / sizeof(struct cnss_pci_reg) - 1; + ath11k_register_dump(ab_pci->ab, + &crash_data->reg_rddm_buf, + &crash_data->reg_rddm_buf_len, + register_rddm_fail_debug, + num); +} + static int ath11k_pci_bus_wake_up(struct ath11k_base *ab) { struct ath11k_pci *ab_pci = ath11k_pci_priv(ab); @@ -107,7 +228,12 @@ static u32 ath11k_pci_window_read32(struct ath11k_base *ab, u32 offset) struct ath11k_pci *ab_pci = ath11k_pci_priv(ab); u32 window_start, val; - window_start = ath11k_pci_get_window_start(ab, offset); + if (ab->hw_params.static_window_map) + window_start = ath11k_pci_get_window_start(ab, offset); + else + window_start = ATH11K_PCI_WINDOW_START; + + //window_start = ath11k_pci_get_window_start(ab, offset); if (window_start == ATH11K_PCI_WINDOW_START) { spin_lock_bh(&ab_pci->window_lock); @@ -667,19 +793,21 @@ static int ath11k_pci_start(struct ath11k_base *ab) { struct ath11k_pci *ab_pci = ath11k_pci_priv(ab); - /* TODO: for now don't restore ASPM in case of single MSI - * vector as MHI register reading in M2 causes system hang. - */ - if (test_bit(ATH11K_FLAG_MULTI_MSI_VECTORS, &ab->dev_flags)) - ath11k_pci_aspm_restore(ab_pci); - else - ath11k_info(ab, "leaving PCI ASPM disabled to avoid MHI M2 problems\n"); + ath11k_pci_aspm_restore(ab_pci); ath11k_pcic_start(ab); return 0; } +static int ath11k_pci_force_rddm(struct ath11k_base *ab) +{ + struct ath11k_pci *ar_pci; + + ar_pci = ath11k_pci_priv(ab); + return ath11k_mhi_force_rddm(ar_pci); +} + static const struct ath11k_hif_ops ath11k_pci_hif_ops = { .start = ath11k_pci_start, .stop = ath11k_pcic_stop, @@ -698,6 +826,7 @@ static const struct ath11k_hif_ops ath11k_pci_hif_ops = { .ce_irq_enable = ath11k_pci_hif_ce_irq_enable, .ce_irq_disable = ath11k_pci_hif_ce_irq_disable, .get_ce_msi_idx = ath11k_pcic_get_ce_msi_idx, + .target_crash = ath11k_pci_force_rddm, }; static void ath11k_pci_read_hw_version(struct ath11k_base *ab, u32 *major, u32 *minor) @@ -731,6 +860,8 @@ static int ath11k_pci_probe(struct pci_dev *pdev, u32 soc_hw_version_major, soc_hw_version_minor, addr; const struct ath11k_pci_ops *pci_ops; int ret; + u32 sub_version; + int ops_init = 0; ab = ath11k_core_alloc(&pdev->dev, sizeof(*ab_pci), ATH11K_BUS_PCI); @@ -746,8 +877,10 @@ static int ath11k_pci_probe(struct pci_dev *pdev, ab_pci->ab = ab; ab_pci->pdev = pdev; ab->hif.ops = &ath11k_pci_hif_ops; + ab->fw_mode = ATH11K_FIRMWARE_MODE_NORMAL; pci_set_drvdata(pdev, ab); spin_lock_init(&ab_pci->window_lock); + INIT_WORK(&ab_pci->rddm_worker, ath11k_mhi_pm_rddm_worker); /* Set fixed_mem_region to true for platforms support reserved memory * from DT. If memory is reserved from DT for FW, ath11k driver need not @@ -776,8 +909,8 @@ static int ath11k_pci_probe(struct pci_dev *pdev, case QCA6390_DEVICE_ID: ath11k_pci_read_hw_version(ab, &soc_hw_version_major, &soc_hw_version_minor); - switch (soc_hw_version_major) { - case 2: + switch (soc_hw_version_major) { + case 2: ab->hw_rev = ATH11K_HW_QCA6390_HW20; break; default: @@ -797,6 +930,13 @@ static int ath11k_pci_probe(struct pci_dev *pdev, ab->id.bdf_search = ATH11K_BDF_SEARCH_BUS_AND_BOARD; ath11k_pci_read_hw_version(ab, &soc_hw_version_major, &soc_hw_version_minor); + pci_ops = &ath11k_pci_ops_qca6390; + ret = ath11k_pcic_register_pci_ops(ab, pci_ops); + if (ret) { + ath11k_err(ab, "failed to register PCI ops: %d\n", ret); + goto err_pci_free_region; + } + ops_init = 1; switch (soc_hw_version_major) { case 2: switch (soc_hw_version_minor) { @@ -806,7 +946,19 @@ static int ath11k_pci_probe(struct pci_dev *pdev, break; case 0x10: case 0x11: - ab->hw_rev = ATH11K_HW_WCN6855_HW21; + //ab->hw_rev = ATH11K_HW_WCN6855_HW21; + sub_version = ath11k_pcic_read32(ab, SUB_VERSION); + ath11k_dbg(ab, ATH11K_DBG_PCI, "sub_version 0x%x\n", sub_version); + switch (sub_version) { + case 0x1019A0E1: + case 0x1019B0E1: + case 0x1019C0E1: + case 0x1019D0E1: + ab->hw_rev = ATH11K_HW_QCA206X_HW21; + break; + default: + ab->hw_rev = ATH11K_HW_WCN6855_HW21; + } break; default: goto unsupported_wcn6855_soc; @@ -820,7 +972,6 @@ unsupported_wcn6855_soc: goto err_pci_free_region; } - pci_ops = &ath11k_pci_ops_qca6390; break; default: dev_err(&pdev->dev, "Unknown PCI device found: 0x%x\n", @@ -829,11 +980,13 @@ unsupported_wcn6855_soc: goto err_pci_free_region; } - ret = ath11k_pcic_register_pci_ops(ab, pci_ops); - if (ret) { - ath11k_err(ab, "failed to register PCI ops: %d\n", ret); - goto err_pci_free_region; - } + if(ops_init == 1){ + ret = ath11k_pcic_register_pci_ops(ab, pci_ops); + if (ret) { + ath11k_err(ab, "failed to register PCI ops: %d\n", ret); + goto err_pci_free_region; + } + } ret = ath11k_pcic_init_msi_config(ab); if (ret) { @@ -851,10 +1004,16 @@ unsupported_wcn6855_soc: if (ret) goto err_pci_disable_msi; + ret = ath11k_pci_set_irq_affinity_hint(ab_pci, cpumask_of(0)); + if (ret) { + ath11k_err(ab, "failed to set irq affinity %d\n", ret); + goto err_pci_disable_msi; + } + ret = ath11k_mhi_register(ab_pci); if (ret) { ath11k_err(ab, "failed to register mhi: %d\n", ret); - goto err_pci_disable_msi; + goto err_irq_affinity_cleanup; } ret = ath11k_hal_srng_init(ab); @@ -875,12 +1034,6 @@ unsupported_wcn6855_soc: goto err_ce_free; } - ret = ath11k_pci_set_irq_affinity_hint(ab_pci, cpumask_of(0)); - if (ret) { - ath11k_err(ab, "failed to set irq affinity %d\n", ret); - goto err_free_irq; - } - /* kernel may allocate a dummy vector before request_irq and * then allocate a real vector when request_irq is called. * So get msi_data here again to avoid spurious interrupt @@ -889,19 +1042,16 @@ unsupported_wcn6855_soc: ret = ath11k_pci_config_msi_data(ab_pci); if (ret) { ath11k_err(ab, "failed to config msi_data: %d\n", ret); - goto err_irq_affinity_cleanup; + goto err_free_irq; } ret = ath11k_core_init(ab); if (ret) { ath11k_err(ab, "failed to init core: %d\n", ret); - goto err_irq_affinity_cleanup; + goto err_free_irq; } return 0; -err_irq_affinity_cleanup: - ath11k_pci_set_irq_affinity_hint(ab_pci, NULL); - err_free_irq: ath11k_pcic_free_irq(ab); @@ -914,6 +1064,9 @@ err_hal_srng_deinit: err_mhi_unregister: ath11k_mhi_unregister(ab_pci); +err_irq_affinity_cleanup: + ath11k_pci_set_irq_affinity_hint(ab_pci, NULL); + err_pci_disable_msi: ath11k_pci_free_msi(ab_pci); @@ -1017,6 +1170,7 @@ static struct pci_driver ath11k_pci_driver = { static int ath11k_pci_init(void) { int ret; + u32 sub_version; ret = pci_register_driver(&ath11k_pci_driver); if (ret) diff --git a/drivers/net/wireless/ath/ath11k/pci.h b/drivers/net/wireless/ath/ath11k/pci.h index e9a01f344ec6..d39c8d3cf8e4 100644 --- a/drivers/net/wireless/ath/ath11k/pci.h +++ b/drivers/net/wireless/ath/ath11k/pci.h @@ -9,11 +9,13 @@ #include <linux/mhi.h> #include "core.h" +#include "coredump_fw.h" #define PCIE_SOC_GLOBAL_RESET 0x3008 #define PCIE_SOC_GLOBAL_RESET_V 1 #define WLAON_WARM_SW_ENTRY 0x1f80504 +#define WLAON_RESET_DBG_SW_ENTRY 0x01F80508 #define WLAON_SOC_RESET_CAUSE_REG 0x01f8060c #define PCIE_Q6_COOKIE_ADDR 0x01f80500 @@ -53,6 +55,65 @@ #define WLAON_QFPROM_PWR_CTRL_REG 0x01f8031c #define QFPROM_PWR_CTRL_VDD4BLOW_MASK 0x4 +#define HWIO_PCIE_PCIE_BHI_VERSION_LOWER_ADDR (0x1e0e200) +#define HWIO_PCIE_PCIE_BHI_VERSION_UPPER_ADDR (0x1e0e204) +#define HWIO_PCIE_PCIE_BHI_IMGADDR_LOWER_ADDR (0x1e0e208) +#define HWIO_PCIE_PCIE_BHI_IMGADDR_UPPER_ADDR (0x1e0e20c) +#define HWIO_PCIE_PCIE_BHI_IMGSIZE_ADDR (0x1e0e210) +#define HWIO_PCIE_PCIE_BHI_IMGTXDB_ADDR (0x1e0e218) +#define HWIO_PCIE_PCIE_BHI_INTVEC_ADDR (0x1e0e220) +#define HWIO_PCIE_PCIE_BHI_EXECENV_ADDR (0x1e0e228) +#define HWIO_PCIE_PCIE_BHI_STATUS_ADDR (0x1e0e22c) +#define HWIO_PCIE_PCIE_BHI_ERRCODE_ADDR (0x1e0e230) +#define HWIO_PCIE_PCIE_BHI_ERRDBG1_ADDR (0x1e0e234) +#define HWIO_PCIE_PCIE_BHI_ERRDBG2_ADDR (0x1e0e238) +#define HWIO_PCIE_PCIE_BHI_ERRDBG3_ADDR (0x1e0e23c) +#define HWIO_PCIE_PCIE_BHI_SERIALNUM_ADDR (0x1e0e240) +#define HWIO_PCIE_PCIE_BHI_SBLANTIROLLVER_ADDR (0x1e0e244) +#define HWIO_PCIE_PCIE_BHI_NUMSEG_ADDR (0x1e0e248) +#define HWIO_PCIE_PCIE_BHI_MSMHWID_0_ADDR (0x1e0e24c) +#define HWIO_PCIE_PCIE_BHI_MSMHWID_1_ADDR (0x1e0e250) +#define HWIO_PCIE_PCIE_BHI_MSMHWID_2_ADDR (0x1e0e254) +#define HWIO_PCIE_PCIE_BHI_MSMHWID_3_ADDR (0x1e0e258) +#define HWIO_PCIE_PCIE_BHI_MSMHWID_4_ADDR (0x1e0e25c) +#define HWIO_PCIE_PCIE_BHI_MSMHWID_5_ADDR (0x1e0e260) +#define HWIO_PCIE_PCIE_BHI_OEMPKHASH_0_ADDR (0x1e0e264) +#define HWIO_PCIE_PCIE_BHI_OEMPKHASH_1_ADDR (0x1e0e268) +#define HWIO_PCIE_PCIE_BHI_OEMPKHASH_2_ADDR (0x1e0e26c) +#define HWIO_PCIE_PCIE_BHI_OEMPKHASH_3_ADDR (0x1e0e270) +#define HWIO_PCIE_PCIE_BHI_OEMPKHASH_4_ADDR (0x1e0e274) +#define HWIO_PCIE_PCIE_BHI_OEMPKHASH_5_ADDR (0x1e0e278) +#define HWIO_PCIE_PCIE_BHI_OEMPKHASH_6_ADDR (0x1e0e27c) +#define HWIO_PCIE_PCIE_BHI_OEMPKHASH_7_ADDR (0x1e0e280) +#define HWIO_PCIE_PCIE_BHI_OEMPKHASH_8_ADDR (0x1e0e284) +#define HWIO_PCIE_PCIE_BHI_OEMPKHASH_9_ADDR (0x1e0e288) +#define HWIO_PCIE_PCIE_BHI_TXVECDB_ADDR (0x1e0e360) +#define HWIO_PCIE_PCIE_BHI_TXVECSTATUS_ADDR (0x1e0e368) +#define HWIO_PCIE_PCIE_BHI_RXVECDB_ADDR (0x1e0e394) +#define HWIO_PCIE_PCIE_BHI_RXVECSTATUS_ADDR (0x1e0e39c) + +#define QDSS_APB_DEC_CS_QDSSCSR_ETRIRQCTRL (0x1C0106C) +#define QDSS_APB_DEC_CS_QDSSCSR_PRESERVEETF (0x1C01070) +#define QDSS_APB_DEC_CS_QDSSCSR_PRESERVEETR0 (0x1C01074) +#define QDSS_APB_DEC_CS_QDSSCSR_PRESERVEETR1 (0x1C01078) +#define Q6SS_PRIVCSR_QDSP6SS_QTMR_V1_CNTP_CTL_0 (0x00DA102C) +#define Q6SS_PRIVCSR_QDSP6SS_QTMR_V1_CNTP_CTL_1 (0x00DA202C) +#define Q6SS_PRIVCSR_QDSP6SS_QTMR_V1_CNTP_CTL_2 (0x00DA302C) + +struct cnss_pci_reg { + char *name; + u32 offset; + u32 value; +}; + +struct register_crash_data { + u8 *reg_buf; + size_t reg_buf_len; + u8 *reg_rddm_buf; + size_t reg_rddm_buf_len; +}; + + enum ath11k_pci_flags { ATH11K_PCI_ASPM_RESTORE, }; @@ -72,6 +133,10 @@ struct ath11k_pci { /* enum ath11k_pci_flags */ unsigned long flags; u16 link_ctl; + struct register_crash_data reg_data; + struct ath11k_mhi_fw_crash_data mhi_fw_crash_data; + struct work_struct rddm_worker; + }; static inline struct ath11k_pci *ath11k_pci_priv(struct ath11k_base *ab) @@ -80,4 +145,5 @@ static inline struct ath11k_pci *ath11k_pci_priv(struct ath11k_base *ab) } int ath11k_pci_get_msi_irq(struct ath11k_base *ab, unsigned int vector); +void ath11k_pci_register_dump(struct ath11k_pci *ab_pci); #endif diff --git a/drivers/net/wireless/ath/ath11k/pcic.c b/drivers/net/wireless/ath/ath11k/pcic.c index 380f9d37b644..b2d740313ce0 100644 --- a/drivers/net/wireless/ath/ath11k/pcic.c +++ b/drivers/net/wireless/ath/ath11k/pcic.c @@ -115,6 +115,17 @@ static const struct ath11k_msi_config ath11k_msi_config[] = { }, .hw_rev = ATH11K_HW_WCN6750_HW10, }, + { + .total_vectors = 32, + .total_users = 4, + .users = (struct ath11k_msi_user[]) { + { .name = "MHI", .num_vectors = 3, .base_vector = 0 }, + { .name = "CE", .num_vectors = 10, .base_vector = 3 }, + { .name = "WAKE", .num_vectors = 1, .base_vector = 13 }, + { .name = "DP", .num_vectors = 18, .base_vector = 14 }, + }, + .hw_rev = ATH11K_HW_QCA206X_HW21, + }, }; int ath11k_pcic_init_msi_config(struct ath11k_base *ab) @@ -218,9 +229,16 @@ int ath11k_pcic_read(struct ath11k_base *ab, void *buf, u32 start, u32 end) if (wakeup_required && ab->pci.ops->wakeup) { ret = ab->pci.ops->wakeup(ab); if (ret) { - ath11k_warn(ab, "failed to wakeup for read from 0x%x: %d\n", - start, ret); - return ret; + ath11k_warn(ab, + "wakeup failed, data may be invalid: %d", + ret); + /* Even though wakeup() failed, continue processing rather + * than returning because some parts of the data may still + * be valid and useful in some cases, e.g. could give us + * some clues on firmware crash. + * Mislead due to invalid data could be avoided because we + * are aware of the wakeup failure. + */ } } @@ -453,18 +471,17 @@ void ath11k_pcic_ext_irq_enable(struct ath11k_base *ab) { int i; - set_bit(ATH11K_FLAG_EXT_IRQ_ENABLED, &ab->dev_flags); - for (i = 0; i < ATH11K_EXT_IRQ_GRP_NUM_MAX; i++) { struct ath11k_ext_irq_grp *irq_grp = &ab->ext_irq_grp[i]; if (!irq_grp->napi_enabled) { - dev_set_threaded(&irq_grp->napi_ndev, true); napi_enable(&irq_grp->napi); irq_grp->napi_enabled = true; } ath11k_pcic_ext_grp_enable(irq_grp); } + + set_bit(ATH11K_FLAG_EXT_IRQ_ENABLED, &ab->dev_flags); } EXPORT_SYMBOL(ath11k_pcic_ext_irq_enable); @@ -592,7 +609,10 @@ static int ath11k_pcic_ext_irq_config(struct ath11k_base *ab) ath11k_dbg(ab, ATH11K_DBG_PCI, "irq:%d group:%d\n", irq, i); - irq_set_status_flags(irq, IRQ_DISABLE_UNLAZY); + if (!test_bit(ATH11K_FLAG_MULTI_MSI_VECTORS, &ab->dev_flags)) + irq_set_status_flags(irq, IRQ_DISABLE_UNLAZY | IRQ_MOVE_PCNTXT); + else + irq_set_status_flags(irq, IRQ_DISABLE_UNLAZY); ret = request_irq(irq, ath11k_pcic_ext_interrupt_handler, irq_flags, "DP_EXT_IRQ", irq_grp); if (ret) { @@ -642,6 +662,8 @@ int ath11k_pcic_config_irq(struct ath11k_base *ab) tasklet_setup(&ce_pipe->intr_tq, ath11k_pcic_ce_tasklet); + if (!test_bit(ATH11K_FLAG_MULTI_MSI_VECTORS, &ab->dev_flags)) + irq_set_status_flags(irq, IRQ_MOVE_PCNTXT); ret = request_irq(irq, ath11k_pcic_ce_interrupt_handler, irq_flags, irq_name[irq_idx], ce_pipe); if (ret) { diff --git a/drivers/net/wireless/ath/ath11k/qmi.c b/drivers/net/wireless/ath/ath11k/qmi.c index 01b02c03fa89..f85b7563fa9e 100644 --- a/drivers/net/wireless/ath/ath11k/qmi.c +++ b/drivers/net/wireless/ath/ath11k/qmi.c @@ -1971,6 +1971,51 @@ static void ath11k_qmi_free_target_mem_chunk(struct ath11k_base *ab) } } +static size_t ath11k_qmi_get_remote_buf_len(struct fw_remote_mem *fw_mem) +{ + unsigned int i; + size_t len = 0; + + for (i = 0; i < ATH11K_QMI_WLANFW_MAX_NUM_MEM_SEG_V01; i++) { + if (fw_mem[i].vaddr && fw_mem[i].size) + len += fw_mem[i].size; + } + return len; +} + +int ath11k_qmi_remote_dump(struct ath11k_base *ab) +{ + struct fw_remote_crash_data *crash_data = &ab->remote_crash_data; + struct fw_remote_mem *fw_mem = ab->remote_mem; + u8 i; + u32 offset = 0; + + crash_data->remote_buf_len = ath11k_qmi_get_remote_buf_len(fw_mem); + ath11k_info(ab, "%s remote buffer len=%lu\n", __func__, crash_data->remote_buf_len); + crash_data->remote_buf = vzalloc(crash_data->remote_buf_len); + if (!crash_data->remote_buf) + return -ENOMEM; + + for (i = 0; i < ATH11K_QMI_WLANFW_MAX_NUM_MEM_SEG_V01; i++) { + if (fw_mem[i].vaddr && fw_mem[i].size) { + ath11k_info(ab, "remote mem: 0x%p, size: 0x%lx\n", fw_mem[i].vaddr, + fw_mem[i].size); + memcpy(crash_data->remote_buf + offset, fw_mem[i].vaddr, fw_mem[i].size); + offset += fw_mem[i].size; + } + } + return 0; +} +EXPORT_SYMBOL(ath11k_qmi_remote_dump); + +static void ath11k_qmi_set_remote_mem(struct fw_remote_mem *fw_mem, + void *vaddr, size_t size, + uint32_t segnum) +{ + fw_mem[segnum].vaddr = vaddr; + fw_mem[segnum].size = size; +} + static int ath11k_qmi_alloc_target_mem_chunk(struct ath11k_base *ab) { int i; @@ -1995,10 +2040,10 @@ static int ath11k_qmi_alloc_target_mem_chunk(struct ath11k_base *ab) chunk->vaddr = NULL; } - chunk->vaddr = dma_alloc_coherent(ab->dev, + chunk->vaddr = ath11k_core_dma_alloc_coherent(ab->dev, chunk->size, &chunk->paddr, - GFP_KERNEL | __GFP_NOWARN); + GFP_KERNEL | __GFP_NOWARN | GFP_DMA32); if (!chunk->vaddr) { if (ab->qmi.mem_seg_count <= ATH11K_QMI_FW_MEM_REQ_SEGMENT_CNT) { ath11k_dbg(ab, ATH11K_DBG_QMI, @@ -2015,6 +2060,15 @@ static int ath11k_qmi_alloc_target_mem_chunk(struct ath11k_base *ab) chunk->type); return -EINVAL; } + + if (chunk->type == QMI_WLANFW_MEM_TYPE_DDR_V01) { + ath11k_qmi_set_remote_mem(ab->remote_mem, + chunk->vaddr, + chunk->size, + i); + ath11k_info(ab, "vaddr=0x%p size=0x%x\n", chunk->vaddr, chunk->size); + } + chunk->prev_type = chunk->type; chunk->prev_size = chunk->size; } @@ -2510,9 +2564,9 @@ static int ath11k_qmi_m3_load(struct ath11k_base *ab) if (m3_mem->vaddr || m3_mem->size) goto skip_m3_alloc; - m3_mem->vaddr = dma_alloc_coherent(ab->dev, + m3_mem->vaddr = ath11k_core_dma_alloc_coherent(ab->dev, fw->size, &m3_mem->paddr, - GFP_KERNEL); + GFP_KERNEL | GFP_DMA32); if (!m3_mem->vaddr) { ath11k_err(ab, "failed to allocate memory for M3 with size %zu\n", fw->size); @@ -3169,6 +3223,9 @@ static void ath11k_qmi_driver_event_work(struct work_struct *work) case ATH11K_QMI_EVENT_SERVER_EXIT: set_bit(ATH11K_FLAG_CRASH_FLUSH, &ab->dev_flags); set_bit(ATH11K_FLAG_RECOVERY, &ab->dev_flags); + + if (!ab->is_reset) + ath11k_core_pre_reconfigure_recovery(ab); break; case ATH11K_QMI_EVENT_REQUEST_MEM: ret = ath11k_qmi_event_mem_request(qmi); @@ -3269,6 +3326,9 @@ int ath11k_qmi_init_service(struct ath11k_base *ab) spin_lock_init(&ab->qmi.event_lock); INIT_WORK(&ab->qmi.event_work, ath11k_qmi_driver_event_work); + memset(ab->remote_mem, 0, + sizeof(struct fw_remote_mem) * ATH11K_QMI_WLANFW_MAX_NUM_MEM_SEG_V01); + ret = qmi_add_lookup(&ab->qmi.handle, ATH11K_QMI_WLFW_SERVICE_ID_V01, ATH11K_QMI_WLFW_SERVICE_VERS_V01, ab->qmi.service_ins_id); diff --git a/drivers/net/wireless/ath/ath11k/qmi.h b/drivers/net/wireless/ath/ath11k/qmi.h index 0909d53cefeb..e4fa39ddbdfc 100644 --- a/drivers/net/wireless/ath/ath11k/qmi.h +++ b/drivers/net/wireless/ath/ath11k/qmi.h @@ -519,5 +519,6 @@ void ath11k_qmi_msg_recv_work(struct work_struct *work); void ath11k_qmi_deinit_service(struct ath11k_base *ab); int ath11k_qmi_init_service(struct ath11k_base *ab); void ath11k_qmi_free_resource(struct ath11k_base *ab); +int ath11k_qmi_remote_dump(struct ath11k_base *ab); #endif diff --git a/drivers/net/wireless/ath/ath11k/reg.c b/drivers/net/wireless/ath/ath11k/reg.c index 6fae4e61ede7..9f28c82c2acb 100644 --- a/drivers/net/wireless/ath/ath11k/reg.c +++ b/drivers/net/wireless/ath/ath11k/reg.c @@ -55,6 +55,17 @@ ath11k_reg_notifier(struct wiphy *wiphy, struct regulatory_request *request) ath11k_dbg(ar->ab, ATH11K_DBG_REG, "Regulatory Notification received for %s\n", wiphy_name(wiphy)); + if ((request->initiator == NL80211_REGDOM_SET_BY_DRIVER) && + (ar->state == ATH11K_STATE_ON)) { + ath11k_dbg(ar->ab, ATH11K_DBG_REG, + "dynamically updated by driver\n"); + ret = ath11k_reg_update_chan_list(ar, true); + if (ret) + ath11k_warn(ar->ab, "failed to update channel list: %d\n", ret); + + return; + } + /* Currently supporting only General User Hints. Cell base user * hints to be handled later. * Hints from other sources like Core, Beacons are not expected for @@ -112,32 +123,7 @@ int ath11k_reg_update_chan_list(struct ath11k *ar, bool wait) struct channel_param *ch; enum nl80211_band band; int num_channels = 0; - int i, ret, left; - - if (wait && ar->state_11d != ATH11K_11D_IDLE) { - left = wait_for_completion_timeout(&ar->completed_11d_scan, - ATH11K_SCAN_TIMEOUT_HZ); - if (!left) { - ath11k_dbg(ar->ab, ATH11K_DBG_REG, - "failed to receive 11d scan complete: timed out\n"); - ar->state_11d = ATH11K_11D_IDLE; - } - ath11k_dbg(ar->ab, ATH11K_DBG_REG, - "reg 11d scan wait left time %d\n", left); - } - - if (wait && - (ar->scan.state == ATH11K_SCAN_STARTING || - ar->scan.state == ATH11K_SCAN_RUNNING)) { - left = wait_for_completion_timeout(&ar->scan.completed, - ATH11K_SCAN_TIMEOUT_HZ); - if (!left) - ath11k_dbg(ar->ab, ATH11K_DBG_REG, - "failed to receive hw scan complete: timed out\n"); - - ath11k_dbg(ar->ab, ATH11K_DBG_REG, - "reg hw scan wait left time %d\n", left); - } + int i, ret = 0; if (ar->state == ATH11K_STATE_RESTARTING) return 0; @@ -219,8 +205,15 @@ int ath11k_reg_update_chan_list(struct ath11k *ar, bool wait) } } - ret = ath11k_wmi_send_scan_chan_list_cmd(ar, params); - kfree(params); + if (wait) { + spin_lock_bh(&ar->channel_update_lock); + list_add_tail(¶ms->list, &ar->channel_update_queue); + spin_unlock_bh(&ar->channel_update_lock); + queue_work(ar->ab->workqueue, &ar->channel_update_work); + } else { + ret = ath11k_wmi_send_scan_chan_list_cmd(ar, params); + kfree(params); + } return ret; } @@ -294,12 +287,6 @@ int ath11k_regd_update(struct ath11k *ar) if (ret) goto err; - if (ar->state == ATH11K_STATE_ON) { - ret = ath11k_reg_update_chan_list(ar, true); - if (ret) - goto err; - } - return 0; err: ath11k_warn(ab, "failed to perform regd update : %d\n", ret); @@ -413,6 +400,10 @@ static void ath11k_reg_intersect_rules(struct ieee80211_reg_rule *rule1, /* Use the flags of both the rules */ new_rule->flags = rule1->flags | rule2->flags; + if ((rule1->flags & NL80211_RRF_PSD) && (rule2->flags & NL80211_RRF_PSD)) + new_rule->psd = min_t(s8, rule1->psd, rule2->psd); + else + new_rule->flags &= ~NL80211_RRF_PSD; /* To be safe, lts use the max cac timeout of both rules */ new_rule->dfs_cac_ms = max_t(u32, rule1->dfs_cac_ms, @@ -516,13 +507,14 @@ ath11k_reg_adjust_bw(u16 start_freq, u16 end_freq, u16 max_bw) static void ath11k_reg_update_rule(struct ieee80211_reg_rule *reg_rule, u32 start_freq, u32 end_freq, u32 bw, u32 ant_gain, u32 reg_pwr, - u32 reg_flags) + s8 psd, u32 reg_flags) { reg_rule->freq_range.start_freq_khz = MHZ_TO_KHZ(start_freq); reg_rule->freq_range.end_freq_khz = MHZ_TO_KHZ(end_freq); reg_rule->freq_range.max_bandwidth_khz = MHZ_TO_KHZ(bw); reg_rule->power_rule.max_antenna_gain = DBI_TO_MBI(ant_gain); reg_rule->power_rule.max_eirp = DBM_TO_MBM(reg_pwr); + reg_rule->psd = psd; reg_rule->flags = reg_flags; } @@ -552,7 +544,7 @@ ath11k_reg_update_weather_radar_band(struct ath11k_base *ab, reg_rule->start_freq, ETSI_WEATHER_RADAR_BAND_LOW, bw, reg_rule->ant_gain, reg_rule->reg_power, - flags); + reg_rule->psd_eirp, flags); ath11k_dbg(ab, ATH11K_DBG_REG, "\t%d. (%d - %d @ %d) (%d, %d) (%d ms) (FLAGS %d)\n", @@ -573,7 +565,7 @@ ath11k_reg_update_weather_radar_band(struct ath11k_base *ab, ath11k_reg_update_rule(regd->reg_rules + i, start_freq, end_freq, bw, reg_rule->ant_gain, - reg_rule->reg_power, flags); + reg_rule->reg_power, reg_rule->psd_eirp, flags); regd->reg_rules[i].dfs_cac_ms = ETSI_WEATHER_RADAR_BAND_CAC_TIMEOUT; @@ -594,7 +586,7 @@ ath11k_reg_update_weather_radar_band(struct ath11k_base *ab, ETSI_WEATHER_RADAR_BAND_HIGH, reg_rule->end_freq, bw, reg_rule->ant_gain, reg_rule->reg_power, - flags); + reg_rule->psd_eirp, flags); ath11k_dbg(ab, ATH11K_DBG_REG, "\t%d. (%d - %d @ %d) (%d, %d) (%d ms) (FLAGS %d)\n", @@ -607,19 +599,64 @@ ath11k_reg_update_weather_radar_band(struct ath11k_base *ab, *rule_idx = i; } +enum wmi_reg_6ghz_ap_type +ath11k_ieee80211_ap_pwr_type_convert(enum ieee80211_ap_reg_power power_type) +{ + switch (power_type) { + case IEEE80211_REG_LPI_AP: + return WMI_REG_INDOOR_AP; + case IEEE80211_REG_SP_AP: + return WMI_REG_STANDARD_POWER_AP; + case IEEE80211_REG_VLP_AP: + return WMI_REG_VERY_LOW_POWER_AP; + default: + return WMI_REG_MAX_AP_TYPE; + } +} + struct ieee80211_regdomain * ath11k_reg_build_regd(struct ath11k_base *ab, - struct cur_regulatory_info *reg_info, bool intersect) + struct cur_regulatory_info *reg_info, bool intersect, + enum wmi_vdev_type vdev_type, + enum ieee80211_ap_reg_power power_type) { struct ieee80211_regdomain *tmp_regd, *default_regd, *new_regd = NULL; - struct cur_reg_rule *reg_rule; - u8 i = 0, j = 0; + struct cur_reg_rule *reg_rule, *reg_rule_6ghz; + u8 i = 0, j = 0, k = 0; u8 num_rules; u16 max_bw; - u32 flags; + u32 flags, reg_6ghz_number, max_bw_6ghz; char alpha2[3]; - num_rules = reg_info->num_5g_reg_rules + reg_info->num_2g_reg_rules; + num_rules = reg_info->num_5ghz_reg_rules + reg_info->num_2ghz_reg_rules; + + if (reg_info->is_ext_reg_event) { + if (vdev_type == WMI_VDEV_TYPE_STA) { + enum wmi_reg_6ghz_ap_type ap_type; + + ap_type = ath11k_ieee80211_ap_pwr_type_convert(power_type); + + if (ap_type == WMI_REG_MAX_AP_TYPE) + ap_type = WMI_REG_INDOOR_AP; + reg_6ghz_number = reg_info->num_6ghz_rules_client + [ap_type][WMI_REG_DEFAULT_CLIENT]; + if (reg_6ghz_number == 0) { + ap_type = WMI_REG_INDOOR_AP; + reg_6ghz_number = reg_info->num_6ghz_rules_client + [ap_type][WMI_REG_DEFAULT_CLIENT]; + } + reg_rule_6ghz = reg_info->reg_rules_6ghz_client_ptr + [ap_type][WMI_REG_DEFAULT_CLIENT]; + max_bw_6ghz = reg_info->max_bw_6ghz_client + [ap_type][WMI_REG_DEFAULT_CLIENT]; + } else { + reg_6ghz_number = reg_info->num_6ghz_rules_ap[WMI_REG_INDOOR_AP]; + reg_rule_6ghz = + reg_info->reg_rules_6ghz_ap_ptr[WMI_REG_INDOOR_AP]; + max_bw_6ghz = reg_info->max_bw_6ghz_ap[WMI_REG_INDOOR_AP]; + } + num_rules += reg_6ghz_number; + } if (!num_rules) goto ret; @@ -640,24 +677,24 @@ ath11k_reg_build_regd(struct ath11k_base *ab, tmp_regd->dfs_region = ath11k_map_fw_dfs_region(reg_info->dfs_region); ath11k_dbg(ab, ATH11K_DBG_REG, - "\r\nCountry %s, CFG Regdomain %s FW Regdomain %d, num_reg_rules %d\n", + "Country %s, CFG Regdomain %s FW Regdomain %d, num_reg_rules %d\n", alpha2, ath11k_reg_get_regdom_str(tmp_regd->dfs_region), reg_info->dfs_region, num_rules); /* Update reg_rules[] below. Firmware is expected to - * send these rules in order(2G rules first and then 5G) + * send these rules in order(2 GHz rules first and then 5 GHz) */ for (; i < num_rules; i++) { - if (reg_info->num_2g_reg_rules && - (i < reg_info->num_2g_reg_rules)) { - reg_rule = reg_info->reg_rules_2g_ptr + i; + if (reg_info->num_2ghz_reg_rules && + (i < reg_info->num_2ghz_reg_rules)) { + reg_rule = reg_info->reg_rules_2ghz_ptr + i; max_bw = min_t(u16, reg_rule->max_bw, - reg_info->max_bw_2g); + reg_info->max_bw_2ghz); flags = 0; - } else if (reg_info->num_5g_reg_rules && - (j < reg_info->num_5g_reg_rules)) { - reg_rule = reg_info->reg_rules_5g_ptr + j++; + } else if (reg_info->num_5ghz_reg_rules && + (j < reg_info->num_5ghz_reg_rules)) { + reg_rule = reg_info->reg_rules_5ghz_ptr + j++; max_bw = min_t(u16, reg_rule->max_bw, - reg_info->max_bw_5g); + reg_info->max_bw_5ghz); /* FW doesn't pass NL80211_RRF_AUTO_BW flag for * BW Auto correction, we can enable this by default @@ -666,6 +703,13 @@ ath11k_reg_build_regd(struct ath11k_base *ab, * per other BW rule flags we pass from here */ flags = NL80211_RRF_AUTO_BW; + } else if (reg_info->is_ext_reg_event && reg_6ghz_number && + (k < reg_6ghz_number)) { + reg_rule = reg_rule_6ghz + k++; + max_bw = min_t(u16, reg_rule->max_bw, max_bw_6ghz); + flags = NL80211_RRF_AUTO_BW; + if (reg_rule->psd_flag) + flags |= NL80211_RRF_PSD; } else { break; } @@ -676,7 +720,7 @@ ath11k_reg_build_regd(struct ath11k_base *ab, reg_rule->start_freq, reg_rule->end_freq, max_bw, reg_rule->ant_gain, reg_rule->reg_power, - flags); + reg_rule->psd_eirp, flags); /* Update dfs cac timeout if the dfs domain is ETSI and the * new rule covers weather radar band. @@ -693,12 +737,21 @@ ath11k_reg_build_regd(struct ath11k_base *ab, continue; } - ath11k_dbg(ab, ATH11K_DBG_REG, - "\t%d. (%d - %d @ %d) (%d, %d) (%d ms) (FLAGS %d)\n", - i + 1, reg_rule->start_freq, reg_rule->end_freq, - max_bw, reg_rule->ant_gain, reg_rule->reg_power, - tmp_regd->reg_rules[i].dfs_cac_ms, - flags); + if (reg_info->is_ext_reg_event) { + ath11k_dbg(ab, ATH11K_DBG_REG, + "\t%d. (%d - %d @ %d) (%d, %d) (%d ms) (FLAGS %d) (%d, %d)\n", + i + 1, reg_rule->start_freq, reg_rule->end_freq, + max_bw, reg_rule->ant_gain, reg_rule->reg_power, + tmp_regd->reg_rules[i].dfs_cac_ms, flags, + reg_rule->psd_flag, reg_rule->psd_eirp); + } else { + ath11k_dbg(ab, ATH11K_DBG_REG, + "\t%d. (%d - %d @ %d) (%d, %d) (%d ms) (FLAGS %d)\n", + i + 1, reg_rule->start_freq, reg_rule->end_freq, + max_bw, reg_rule->ant_gain, reg_rule->reg_power, + tmp_regd->reg_rules[i].dfs_cac_ms, + flags); + } } tmp_regd->n_reg_rules = i; @@ -723,6 +776,50 @@ ret: return new_regd; } +void ath11k_regd_update_chan_list_work(struct work_struct *work) +{ + struct ath11k *ar = container_of(work, struct ath11k, + channel_update_work); + struct scan_chan_list_params *params, *tmp; + int left; + + spin_lock_bh(&ar->channel_update_lock); + + list_for_each_entry_safe(params, tmp, &ar->channel_update_queue, list) { + list_del(¶ms->list); + spin_unlock_bh(&ar->channel_update_lock); + + if (ar->state_11d != ATH11K_11D_IDLE) { + left = wait_for_completion_timeout(&ar->completed_11d_scan, + ATH11K_SCAN_TIMEOUT_HZ); + if (!left) { + ath11k_dbg(ar->ab, ATH11K_DBG_REG, + "failed to receive 11d scan complete: timed out\n"); + ar->state_11d = ATH11K_11D_IDLE; + } + ath11k_dbg(ar->ab, ATH11K_DBG_REG, + "reg 11d scan wait left time %d\n", left); + } + + if ((ar->scan.state == ATH11K_SCAN_STARTING || + ar->scan.state == ATH11K_SCAN_RUNNING)) { + left = wait_for_completion_timeout(&ar->scan.completed, + ATH11K_SCAN_TIMEOUT_HZ); + if (!left) + ath11k_dbg(ar->ab, ATH11K_DBG_REG, + "failed to receive hw scan complete: timed out\n"); + + ath11k_dbg(ar->ab, ATH11K_DBG_REG, + "reg hw scan wait left time %d\n", left); + } + + ath11k_wmi_send_scan_chan_list_cmd(ar, params); + kfree(params); + spin_lock_bh(&ar->channel_update_lock); + } + spin_unlock_bh(&ar->channel_update_lock); +} + void ath11k_regd_update_work(struct work_struct *work) { struct ath11k *ar = container_of(work, struct ath11k, @@ -743,6 +840,7 @@ void ath11k_regd_update_work(struct work_struct *work) void ath11k_reg_init(struct ath11k *ar) { ar->hw->wiphy->regulatory_flags = REGULATORY_WIPHY_SELF_MANAGED; + ar->hw->wiphy->flags |= WIPHY_FLAG_NOTIFY_REGDOM_BY_DRIVER; ar->hw->wiphy->reg_notifier = ath11k_reg_notifier; } @@ -750,6 +848,12 @@ void ath11k_reg_free(struct ath11k_base *ab) { int i; + for (i = 0; i < ab->num_radios; i++) + ath11k_reg_reset_info(&ab->reg_info_store[i]); + + kfree(ab->reg_info_store); + ab->reg_info_store = NULL; + for (i = 0; i < ab->hw_params.max_radios; i++) { kfree(ab->default_regd[i]); kfree(ab->new_regd[i]); diff --git a/drivers/net/wireless/ath/ath11k/reg.h b/drivers/net/wireless/ath/ath11k/reg.h index 2f284f26378d..fef5927c622f 100644 --- a/drivers/net/wireless/ath/ath11k/reg.h +++ b/drivers/net/wireless/ath/ath11k/reg.h @@ -28,9 +28,14 @@ enum ath11k_dfs_region { void ath11k_reg_init(struct ath11k *ar); void ath11k_reg_free(struct ath11k_base *ab); void ath11k_regd_update_work(struct work_struct *work); +void ath11k_regd_update_chan_list_work(struct work_struct *work); struct ieee80211_regdomain * ath11k_reg_build_regd(struct ath11k_base *ab, - struct cur_regulatory_info *reg_info, bool intersect); + struct cur_regulatory_info *reg_info, bool intersect, + enum wmi_vdev_type vdev_type, + enum ieee80211_ap_reg_power power_type); +enum wmi_reg_6ghz_ap_type +ath11k_ieee80211_ap_pwr_type_convert(enum ieee80211_ap_reg_power power_type); int ath11k_regd_update(struct ath11k *ar); int ath11k_reg_update_chan_list(struct ath11k *ar, bool wait); #endif diff --git a/drivers/net/wireless/ath/ath11k/testmode.c b/drivers/net/wireless/ath/ath11k/testmode.c index 4bf1931adbaa..5603938f6c36 100644 --- a/drivers/net/wireless/ath/ath11k/testmode.c +++ b/drivers/net/wireless/ath/ath11k/testmode.c @@ -1,6 +1,7 @@ // SPDX-License-Identifier: BSD-3-Clause-Clear /* * Copyright (c) 2018-2019 The Linux Foundation. All rights reserved. + * Copyright (c) 2023 Qualcomm Innovation Center, Inc. All rights reserved. */ #include "testmode.h" @@ -11,6 +12,9 @@ #include "core.h" #include "testmode_i.h" +#define ATH11K_FTM_SEGHDR_CURRENT_SEQ GENMASK(3, 0) +#define ATH11K_FTM_SEGHDR_TOTAL_SEGMENTS GENMASK(7, 4) + static const struct nla_policy ath11k_tm_policy[ATH11K_TM_ATTR_MAX + 1] = { [ATH11K_TM_ATTR_CMD] = { .type = NLA_U32 }, [ATH11K_TM_ATTR_DATA] = { .type = NLA_BINARY, @@ -20,58 +24,163 @@ static const struct nla_policy ath11k_tm_policy[ATH11K_TM_ATTR_MAX + 1] = { [ATH11K_TM_ATTR_VERSION_MINOR] = { .type = NLA_U32 }, }; -/* Returns true if callee consumes the skb and the skb should be discarded. - * Returns false if skb is not used. Does not sleep. +static struct ath11k *ath11k_tm_get_ar(struct ath11k_base *ab) +{ + struct ath11k_pdev *pdev; + struct ath11k *ar = NULL; + int i; + + for (i = 0; i < ab->num_radios; i++) { + pdev = &ab->pdevs[i]; + ar = pdev->ar; + if (ar) { + if (ar->state == ATH11K_STATE_TM) + break; + } + } + + return ar; +} + +/* This function handles unsegmented events. Data in various events are aggregated + * in application layer, this event is unsegmented from host perspective. */ -bool ath11k_tm_event_wmi(struct ath11k *ar, u32 cmd_id, struct sk_buff *skb) +void ath11k_tm_wmi_event_unsegmented(struct ath11k_base *ab, u32 cmd_id, + struct sk_buff *skb) { struct sk_buff *nl_skb; - bool consumed; - int ret; + struct ath11k *ar; - ath11k_dbg(ar->ab, ATH11K_DBG_TESTMODE, - "testmode event wmi cmd_id %d skb %pK skb->len %d\n", - cmd_id, skb, skb->len); + ath11k_dbg(ab, ATH11K_DBG_TESTMODE, + "testmode event wmi cmd_id %d skb length %d\n", + cmd_id, skb->len); + ath11k_dbg_dump(ab, ATH11K_DBG_TESTMODE, NULL, "", skb->data, skb->len); - ath11k_dbg_dump(ar->ab, ATH11K_DBG_TESTMODE, NULL, "", skb->data, skb->len); + ar = ath11k_tm_get_ar(ab); + if (!ar) { + ath11k_warn(ab, "testmode event not handled due to invalid pdev\n"); + return; + } spin_lock_bh(&ar->data_lock); - consumed = true; - nl_skb = cfg80211_testmode_alloc_event_skb(ar->hw->wiphy, - 2 * sizeof(u32) + skb->len, + 2 * nla_total_size(sizeof(u32)) + + nla_total_size(skb->len), GFP_ATOMIC); if (!nl_skb) { - ath11k_warn(ar->ab, + ath11k_warn(ab, "failed to allocate skb for testmode wmi event\n"); goto out; } - ret = nla_put_u32(nl_skb, ATH11K_TM_ATTR_CMD, ATH11K_TM_CMD_WMI); - if (ret) { - ath11k_warn(ar->ab, - "failed to put testmode wmi event cmd attribute: %d\n", - ret); + if (nla_put_u32(nl_skb, ATH11K_TM_ATTR_CMD, ATH11K_TM_CMD_WMI) || + nla_put_u32(nl_skb, ATH11K_TM_ATTR_WMI_CMDID, cmd_id) || + nla_put(nl_skb, ATH11K_TM_ATTR_DATA, skb->len, skb->data)) { + ath11k_warn(ab, "failed to populate testmode unsegmented event\n"); kfree_skb(nl_skb); goto out; } - ret = nla_put_u32(nl_skb, ATH11K_TM_ATTR_WMI_CMDID, cmd_id); - if (ret) { - ath11k_warn(ar->ab, - "failed to put testmode wmi even cmd_id: %d\n", - ret); - kfree_skb(nl_skb); + cfg80211_testmode_event(nl_skb, GFP_ATOMIC); + spin_unlock_bh(&ar->data_lock); + return; + +out: + spin_unlock_bh(&ar->data_lock); + ath11k_warn(ab, "Failed to send testmode event to higher layers\n"); +} + +/* This function handles segmented events. + * Data of various events received from fw is aggregated and + * sent to application layer + */ +int ath11k_tm_process_event(struct ath11k_base *ab, u32 cmd_id, + const struct wmi_ftm_event_msg *ftm_msg, + u16 length) +{ + struct sk_buff *nl_skb; + int ret = 0; + struct ath11k *ar; + u8 const *buf_pos; + u16 datalen; + u8 total_segments, current_seq; + u32 data_pos; + u32 pdev_id; + + ath11k_dbg(ab, ATH11K_DBG_TESTMODE, + "testmode event wmi cmd_id %d ftm event msg %pK datalen %d\n", + cmd_id, ftm_msg, length); + ath11k_dbg_dump(ab, ATH11K_DBG_TESTMODE, NULL, "", ftm_msg, length); + pdev_id = DP_HW2SW_MACID(ftm_msg->seg_hdr.pdev_id); + + if (pdev_id >= ab->num_radios) { + ath11k_warn(ab, "testmode event not handled due to invalid pdev id\n"); + return -EINVAL; + } + + ar = ab->pdevs[pdev_id].ar; + if (!ar) { + ath11k_warn(ab, "testmode event not handled due to absence of pdev\n"); + return -ENODEV; + } + + current_seq = FIELD_GET(ATH11K_FTM_SEGHDR_CURRENT_SEQ, + ftm_msg->seg_hdr.segmentinfo); + total_segments = FIELD_GET(ATH11K_FTM_SEGHDR_TOTAL_SEGMENTS, + ftm_msg->seg_hdr.segmentinfo); + datalen = length - (sizeof(struct wmi_ftm_seg_hdr)); + buf_pos = ftm_msg->data; + + spin_lock_bh(&ar->data_lock); + if (current_seq == 0) { + ab->ftm_event_obj.expected_seq = 0; + ab->ftm_event_obj.data_pos = 0; + } + + data_pos = ab->ftm_event_obj.data_pos; + + if ((data_pos + datalen) > ATH11K_FTM_EVENT_MAX_BUF_LENGTH) { + ath11k_warn(ab, + "Invalid event length date_pos[%d] datalen[%d]\n", + data_pos, datalen); + ret = -EINVAL; goto out; } - ret = nla_put(nl_skb, ATH11K_TM_ATTR_DATA, skb->len, skb->data); - if (ret) { - ath11k_warn(ar->ab, - "failed to copy skb to testmode wmi event: %d\n", - ret); + memcpy(&ab->ftm_event_obj.eventdata[data_pos], buf_pos, datalen); + data_pos += datalen; + + if (++ab->ftm_event_obj.expected_seq != total_segments) { + ab->ftm_event_obj.data_pos = data_pos; + ath11k_dbg(ab, ATH11K_DBG_TESTMODE, + "partial data received current_seq[%d], total_seg[%d]\n", + current_seq, total_segments); + goto out; + } + + ath11k_dbg(ab, ATH11K_DBG_TESTMODE, + "total data length[%d] = [%d]\n", + data_pos, ftm_msg->seg_hdr.len); + nl_skb = cfg80211_testmode_alloc_event_skb(ar->hw->wiphy, + 2 * nla_total_size(sizeof(u32)) + + nla_total_size(data_pos), + GFP_ATOMIC); + if (!nl_skb) { + ath11k_warn(ab, + "failed to allocate skb for testmode wmi event\n"); + ret = -ENOMEM; + goto out; + } + + if (nla_put_u32(nl_skb, ATH11K_TM_ATTR_CMD, + ATH11K_TM_CMD_WMI_FTM) || + nla_put_u32(nl_skb, ATH11K_TM_ATTR_WMI_CMDID, cmd_id) || + nla_put(nl_skb, ATH11K_TM_ATTR_DATA, data_pos, + &ab->ftm_event_obj.eventdata[0])) { + ath11k_warn(ab, "failed to populate testmode event"); kfree_skb(nl_skb); + ret = -ENOBUFS; goto out; } @@ -79,14 +188,12 @@ bool ath11k_tm_event_wmi(struct ath11k *ar, u32 cmd_id, struct sk_buff *skb) out: spin_unlock_bh(&ar->data_lock); - - return consumed; + return ret; } static int ath11k_tm_cmd_get_version(struct ath11k *ar, struct nlattr *tb[]) { struct sk_buff *skb; - int ret; ath11k_dbg(ar->ab, ATH11K_DBG_TESTMODE, "testmode cmd get version_major %d version_minor %d\n", @@ -98,21 +205,50 @@ static int ath11k_tm_cmd_get_version(struct ath11k *ar, struct nlattr *tb[]) if (!skb) return -ENOMEM; - ret = nla_put_u32(skb, ATH11K_TM_ATTR_VERSION_MAJOR, - ATH11K_TESTMODE_VERSION_MAJOR); - if (ret) { + if (nla_put_u32(skb, ATH11K_TM_ATTR_VERSION_MAJOR, + ATH11K_TESTMODE_VERSION_MAJOR) || + nla_put_u32(skb, ATH11K_TM_ATTR_VERSION_MINOR, + ATH11K_TESTMODE_VERSION_MINOR)) { kfree_skb(skb); - return ret; + return -ENOBUFS; } - ret = nla_put_u32(skb, ATH11K_TM_ATTR_VERSION_MINOR, - ATH11K_TESTMODE_VERSION_MINOR); - if (ret) { - kfree_skb(skb); - return ret; + return cfg80211_testmode_reply(skb); +} + +static int ath11k_tm_cmd_testmode_start(struct ath11k *ar, struct nlattr *tb[]) +{ + int ret; + + ath11k_dbg(ar->ab, ATH11K_DBG_TESTMODE, " enter testmode cmd fw start\n"); + mutex_lock(&ar->conf_mutex); + + if (ar->state == ATH11K_STATE_TM) { + ret = -EALREADY; + goto err; } - return cfg80211_testmode_reply(skb); + /* start utf only when the driver is not in use */ + if (ar->state != ATH11K_STATE_OFF) { + ret = -EBUSY; + goto err; + } + + ar->ab->ftm_event_obj.eventdata = + kzalloc(ATH11K_FTM_EVENT_MAX_BUF_LENGTH, GFP_KERNEL); + if (!ar->ab->ftm_event_obj.eventdata) { + ret = -ENOMEM; + goto err; + } + + ar->state = ATH11K_STATE_TM; + ar->ftm_msgref = 0; + mutex_unlock(&ar->conf_mutex); + ath11k_dbg(ar->ab, ATH11K_DBG_TESTMODE, " enter testmode cmd started\n"); + return 0; +err: + mutex_unlock(&ar->conf_mutex); + return ret; } static int ath11k_tm_cmd_wmi(struct ath11k *ar, struct nlattr *tb[]) @@ -125,11 +261,6 @@ static int ath11k_tm_cmd_wmi(struct ath11k *ar, struct nlattr *tb[]) mutex_lock(&ar->conf_mutex); - if (ar->state != ATH11K_STATE_ON) { - ret = -ENETDOWN; - goto out; - } - if (!tb[ATH11K_TM_ATTR_DATA]) { ret = -EINVAL; goto out; @@ -142,11 +273,17 @@ static int ath11k_tm_cmd_wmi(struct ath11k *ar, struct nlattr *tb[]) buf = nla_data(tb[ATH11K_TM_ATTR_DATA]); buf_len = nla_len(tb[ATH11K_TM_ATTR_DATA]); + if (!buf_len) { + ath11k_warn(ar->ab, "No data present in testmode command\n"); + ret = -EINVAL; + goto out; + } + cmd_id = nla_get_u32(tb[ATH11K_TM_ATTR_WMI_CMDID]); ath11k_dbg(ar->ab, ATH11K_DBG_TESTMODE, - "testmode cmd wmi cmd_id %d buf %pK buf_len %d\n", - cmd_id, buf, buf_len); + "testmode cmd wmi cmd_id %d buf length %d\n", + cmd_id, buf_len); ath11k_dbg_dump(ar->ab, ATH11K_DBG_TESTMODE, NULL, "", buf, buf_len); @@ -173,10 +310,91 @@ out: return ret; } +static int ath11k_tm_cmd_process_ftm(struct ath11k *ar, struct nlattr *tb[]) +{ + struct ath11k_pdev_wmi *wmi = ar->wmi; + struct sk_buff *skb; + u32 cmd_id, buf_len, hdr_info; + int ret; + void *buf; + u8 segnumber = 0, seginfo; + u16 chunk_len, total_bytes, num_segments; + u8 *bufpos; + struct wmi_ftm_cmd *ftm_cmd; + + mutex_lock(&ar->conf_mutex); + ath11k_dbg(ar->ab, ATH11K_DBG_TESTMODE, "ar->state %d\n", ar->state); + + if (ar->state != ATH11K_STATE_TM) { + ret = -ENETDOWN; + goto out; + } + + if (!tb[ATH11K_TM_ATTR_DATA]) { + ret = -EINVAL; + goto out; + } + + buf = nla_data(tb[ATH11K_TM_ATTR_DATA]); + buf_len = nla_len(tb[ATH11K_TM_ATTR_DATA]); + cmd_id = WMI_PDEV_UTF_CMDID; + ath11k_dbg(ar->ab, ATH11K_DBG_TESTMODE, + "testmode cmd wmi cmd_id %d buffer length %d\n", + cmd_id, buf_len); + ath11k_dbg_dump(ar->ab, ATH11K_DBG_TESTMODE, NULL, "", buf, buf_len); + bufpos = buf; + total_bytes = buf_len; + num_segments = total_bytes / MAX_WMI_UTF_LEN; + + if (buf_len - (num_segments * MAX_WMI_UTF_LEN)) + num_segments++; + + while (buf_len) { + if (buf_len > MAX_WMI_UTF_LEN) + chunk_len = MAX_WMI_UTF_LEN; /* MAX message */ + else + chunk_len = buf_len; + + skb = ath11k_wmi_alloc_skb(wmi->wmi_ab, (chunk_len + + sizeof(struct wmi_ftm_cmd))); + if (!skb) { + ret = -ENOMEM; + goto out; + } + + ftm_cmd = (struct wmi_ftm_cmd *)skb->data; + hdr_info = FIELD_PREP(WMI_TLV_TAG, WMI_TAG_ARRAY_BYTE) | + FIELD_PREP(WMI_TLV_LEN, (chunk_len + + sizeof(struct wmi_ftm_seg_hdr))); + ftm_cmd->tlv_header = hdr_info; + ftm_cmd->seg_hdr.len = total_bytes; + ftm_cmd->seg_hdr.msgref = ar->ftm_msgref; + seginfo = FIELD_PREP(ATH11K_FTM_SEGHDR_TOTAL_SEGMENTS, num_segments) | + FIELD_PREP(ATH11K_FTM_SEGHDR_CURRENT_SEQ, segnumber); + ftm_cmd->seg_hdr.segmentinfo = seginfo; + segnumber++; + memcpy(&ftm_cmd->data, bufpos, chunk_len); + ret = ath11k_wmi_cmd_send(wmi, skb, cmd_id); + if (ret) { + ath11k_warn(ar->ab, "ftm wmi command fail: %d\n", ret); + goto out; + } + + buf_len -= chunk_len; + bufpos += chunk_len; + } + ++ar->ftm_msgref; + ret = 0; +out: + mutex_unlock(&ar->conf_mutex); + return ret; +} + int ath11k_tm_cmd(struct ieee80211_hw *hw, struct ieee80211_vif *vif, void *data, int len) { struct ath11k *ar = hw->priv; + struct ath11k_base *ab = ar->ab; struct nlattr *tb[ATH11K_TM_ATTR_MAX + 1]; int ret; @@ -191,8 +409,15 @@ int ath11k_tm_cmd(struct ieee80211_hw *hw, struct ieee80211_vif *vif, switch (nla_get_u32(tb[ATH11K_TM_ATTR_CMD])) { case ATH11K_TM_CMD_GET_VERSION: return ath11k_tm_cmd_get_version(ar, tb); + case ATH11K_TM_CMD_TESTMODE_START: + return ath11k_tm_cmd_testmode_start(ar, tb); case ATH11K_TM_CMD_WMI: return ath11k_tm_cmd_wmi(ar, tb); + case ATH11K_TM_CMD_WMI_FTM: + set_bit(ATH11K_FLAG_FTM_SEGMENTED, &ab->dev_flags); + return ath11k_tm_cmd_process_ftm(ar, tb); + case ATH11K_TM_CMD_TESTMODE_STOP: + return 0; default: return -EOPNOTSUPP; } diff --git a/drivers/net/wireless/ath/ath11k/testmode.h b/drivers/net/wireless/ath/ath11k/testmode.h index aaa122ed9069..a944086cf283 100644 --- a/drivers/net/wireless/ath/ath11k/testmode.h +++ b/drivers/net/wireless/ath/ath11k/testmode.h @@ -1,22 +1,34 @@ /* SPDX-License-Identifier: BSD-3-Clause-Clear */ /* * Copyright (c) 2018-2019 The Linux Foundation. All rights reserved. + * Copyright (c) 2023 Qualcomm Innovation Center, Inc. All rights reserved. */ #include "core.h" #ifdef CONFIG_NL80211_TESTMODE -bool ath11k_tm_event_wmi(struct ath11k *ar, u32 cmd_id, struct sk_buff *skb); +void ath11k_tm_wmi_event_unsegmented(struct ath11k_base *ab, u32 cmd_id, + struct sk_buff *skb); +int ath11k_tm_process_event(struct ath11k_base *ab, u32 cmd_id, + const struct wmi_ftm_event_msg *ftm_msg, + u16 length); int ath11k_tm_cmd(struct ieee80211_hw *hw, struct ieee80211_vif *vif, void *data, int len); #else -static inline bool ath11k_tm_event_wmi(struct ath11k *ar, u32 cmd_id, - struct sk_buff *skb) +static inline void ath11k_tm_wmi_event_unsegmented(struct ath11k_base *ab, + u32 cmd_id, + struct sk_buff *skb) { - return false; +} + +static inline int ath11k_tm_process_event(struct ath11k_base *ab, u32 cmd_id, + const struct wmi_ftm_event_msg *msg, + u16 length) +{ + return 0; } static inline int ath11k_tm_cmd(struct ieee80211_hw *hw, diff --git a/drivers/net/wireless/ath/ath11k/testmode_i.h b/drivers/net/wireless/ath/ath11k/testmode_i.h index 4bae2a9eeea4..2a03b4029d88 100644 --- a/drivers/net/wireless/ath/ath11k/testmode_i.h +++ b/drivers/net/wireless/ath/ath11k/testmode_i.h @@ -1,6 +1,7 @@ /* SPDX-License-Identifier: BSD-3-Clause-Clear */ /* * Copyright (c) 2018-2019 The Linux Foundation. All rights reserved. + * Copyright (c) 2023 Qualcomm Innovation Center, Inc. All rights reserved. */ /* "API" level of the ath11k testmode interface. Bump it after every @@ -14,6 +15,7 @@ #define ATH11K_TESTMODE_VERSION_MINOR 0 #define ATH11K_TM_DATA_MAX_LEN 5000 +#define ATH11K_FTM_EVENT_MAX_BUF_LENGTH 2048 enum ath11k_tm_attr { __ATH11K_TM_ATTR_INVALID = 0, @@ -47,4 +49,21 @@ enum ath11k_tm_cmd { * ATH11K_TM_ATTR_DATA. */ ATH11K_TM_CMD_WMI = 1, + + /* Boots the UTF firmware, the netdev interface must be down at the + * time. + */ + ATH11K_TM_CMD_TESTMODE_START = 2, + + /* Shuts down the UTF firmware and puts the driver back into OFF + * state. + */ + ATH11K_TM_CMD_TESTMODE_STOP = 3, + + /* The command used to transmit a FTM WMI command to the firmware + * and the event to receive WMI events from the firmware.The data + * received only contain the payload, Need to add the tlv + * header and send the cmd to fw with commandid WMI_PDEV_UTF_CMDID. + */ + ATH11K_TM_CMD_WMI_FTM = 4, }; diff --git a/drivers/net/wireless/ath/ath11k/unitest.c b/drivers/net/wireless/ath/ath11k/unitest.c new file mode 100644 index 000000000000..541925af0fd0 --- /dev/null +++ b/drivers/net/wireless/ath/ath11k/unitest.c @@ -0,0 +1,96 @@ +// SPDX-License-Identifier: BSD-3-Clause-Clear +/** + * Copyright (c) 2020 The Linux Foundation. All rights reserved. + */ + +#include <net/netlink.h> +#include "debug.h" +#include "unitest.h" + +const struct nla_policy ath11k_unitest_policy[UNITEST_MAX + 1] = { + [UNITEST_MODULE_ID] = {.type = NLA_U32}, + [UNITEST_ARGS_NUM] = {.type = NLA_U32}, + [UNITEST_ARGS] = {.type = NLA_BINARY, + .len = MAX_UNITEST_MEMORY_LEN}, +}; + +/** + * ath11k_unit_test() - send unit test command + * @wiphy: wiphy structure pointer + * @wdev: Wireless device structure pointer + * @data: Pointer to the data received + * @data_len: Length of @data + * + * Return: 0 on success; errno on failure + */ + +int ath11k_unit_test(struct wiphy *wiphy, struct wireless_dev *wdev, + const void *data, int data_len) +{ + struct ieee80211_hw *hw = wiphy_to_ieee80211_hw(wiphy); + struct ath11k *ar = hw->priv; + struct ath11k_base *ab = ar->ab; + struct nlattr *tb[UNITEST_MAX + 1]; + int status = 0; + u32 module_id, num_args, temp_num_args, len; + bool sta_found = false; + struct ath11k_vif *arvif; + struct unit_test_cmd utest_cmd = {0}; + + list_for_each_entry(arvif, &ar->arvifs, list) { + if (arvif->vdev_type != WMI_VDEV_TYPE_STA || + arvif->vdev_subtype != WMI_VDEV_SUBTYPE_NONE) + continue; + sta_found = true; + break; + } + if (!sta_found) { + ath11k_warn(ar->ab, "no sta found."); + return -EINVAL; + } + + utest_cmd.vdev_id = arvif->vdev_id; + + if (nla_parse(tb, UNITEST_MAX, data, data_len, ath11k_unitest_policy, NULL)) { + ath11k_warn(ab, "Invalid ATTR"); + return -EINVAL; + } + + if (!tb[UNITEST_MODULE_ID]) { + ath11k_warn(ab, "attr unitest module id failed"); + return -EINVAL; + } + module_id = nla_get_u32(tb[UNITEST_MODULE_ID]); + utest_cmd.module_id = module_id; + + if (!tb[UNITEST_ARGS_NUM]) { + ath11k_warn(ab, "attr unitest args num failed"); + return -EINVAL; + } + num_args = nla_get_u32(tb[UNITEST_ARGS_NUM]); + utest_cmd.num_args = num_args; + if (num_args > UNIT_TEST_MAX_NUM_ARGS) { + ath11k_warn(ab, "num args exceed"); + return -EINVAL; + } + + if (!tb[UNITEST_ARGS]) { + ath11k_warn(ab, "attr unitest args failed"); + return -EINVAL; + } + len = nla_len(tb[UNITEST_ARGS]); + temp_num_args = len / sizeof(u32); + if (num_args != temp_num_args) { + ath11k_warn(ab, "num args mismatch"); + return -EINVAL; + } + nla_memcpy(utest_cmd.args, tb[UNITEST_ARGS], len); + + status = ath11k_wmi_set_unit_test(ar, &utest_cmd); + if (status) { + ath11k_warn(ab, "Unable to post unit test message (status-%d)", status); + return -EINVAL; + } + + return 0; +} diff --git a/drivers/net/wireless/ath/ath11k/unitest.h b/drivers/net/wireless/ath/ath11k/unitest.h new file mode 100644 index 000000000000..9fee1cd6873b --- /dev/null +++ b/drivers/net/wireless/ath/ath11k/unitest.h @@ -0,0 +1,44 @@ +/* SPDX-License-Identifier: BSD-3-Clause-Clear */ +/** + * Copyright (c) 2020 The Linux Foundation. All rights reserved. + */ + +#ifndef _UNIT_TEST_H_ +#define _UNIT_TEST_H_ + +#define VENDOR_ID 0x001374 +#define QCA_NL80211_VENDOR_UNITEST_SUBCMD 84 +#define MAX_UNITEST_MEMORY_LEN 128 + +enum qca_wlan_vendor_attr_unit_test { + QCA_WLAN_VENDOR_ATTR_UNIT_TEST_INVALID = 0, + QCA_WLAN_VENDOR_ATTR_UNIT_TEST_MODULE_ID, + QCA_WLAN_VENDOR_ATTR_UNIT_TEST_ARGS_NUM, + QCA_WLAN_VENDOR_ATTR_UNIT_TEST_ARGS, + /* keep last */ + QCA_WLAN_VENDOR_ATTR_UNIT_TEST_AFTER_LAST, + QCA_WLAN_VENDOR_ATTR_UNIT_TEST_MAX = + QCA_WLAN_VENDOR_ATTR_UNIT_TEST_AFTER_LAST - 1, +}; + +#define UNITEST_INVALID QCA_WLAN_VENDOR_ATTR_UNIT_TEST_INVALID +#define UNITEST_MODULE_ID QCA_WLAN_VENDOR_ATTR_UNIT_TEST_MODULE_ID +#define UNITEST_ARGS QCA_WLAN_VENDOR_ATTR_UNIT_TEST_ARGS +#define UNITEST_ARGS_NUM QCA_WLAN_VENDOR_ATTR_UNIT_TEST_ARGS_NUM +#define UNITEST_MAX QCA_WLAN_VENDOR_ATTR_UNIT_TEST_MAX + +#define ath11k_unit_test_command \ +{\ + .info.vendor_id = VENDOR_ID,\ + .info.subcmd = QCA_NL80211_VENDOR_UNITEST_SUBCMD,\ + .flags = WIPHY_VENDOR_CMD_NEED_WDEV |\ + WIPHY_VENDOR_CMD_NEED_NETDEV |\ + WIPHY_VENDOR_CMD_NEED_RUNNING,\ + .doit = ath11k_unit_test,\ + .policy = VENDOR_CMD_RAW_DATA,\ + .maxattr = QCA_WLAN_VENDOR_ATTR_UNIT_TEST_MAX,\ +} + +int ath11k_unit_test(struct wiphy *wiphy, struct wireless_dev *wdev, + const void *data, int data_len); +#endif diff --git a/drivers/net/wireless/ath/ath11k/wmi.c b/drivers/net/wireless/ath/ath11k/wmi.c index 3e0a47f4a3eb..74fe5633936f 100644 --- a/drivers/net/wireless/ath/ath11k/wmi.c +++ b/drivers/net/wireless/ath/ath11k/wmi.c @@ -1,7 +1,7 @@ // SPDX-License-Identifier: BSD-3-Clause-Clear /* * Copyright (c) 2018-2019 The Linux Foundation. All rights reserved. - * Copyright (c) 2021, Qualcomm Innovation Center, Inc. All rights reserved. + * Copyright (c) 2021, 2023 Qualcomm Innovation Center, Inc. All rights reserved. */ #include <linux/skbuff.h> #include <linux/ctype.h> @@ -19,6 +19,7 @@ #include "mac.h" #include "hw.h" #include "peer.h" +#include "testmode.h" struct wmi_tlv_policy { size_t min_len; @@ -82,6 +83,12 @@ struct wmi_tlv_fw_stats_parse { bool chain_rssi_done; }; +struct wmi_tlv_mgmt_rx_parse { + const struct wmi_mgmt_rx_hdr *fixed; + const u8 *frame_buf; + bool frame_buf_done; +}; + static const struct wmi_tlv_policy wmi_tlv_policies[] = { [WMI_TAG_ARRAY_BYTE] = { .min_len = 0 }, @@ -105,6 +112,8 @@ static const struct wmi_tlv_policy wmi_tlv_policies[] = { = { .min_len = sizeof(struct wmi_vdev_stopped_event) }, [WMI_TAG_REG_CHAN_LIST_CC_EVENT] = { .min_len = sizeof(struct wmi_reg_chan_list_cc_event) }, + [WMI_TAG_REG_CHAN_LIST_CC_EXT_EVENT] + = { .min_len = sizeof(struct wmi_reg_chan_list_cc_ext_event) }, [WMI_TAG_MGMT_RX_HDR] = { .min_len = sizeof(struct wmi_mgmt_rx_hdr) }, [WMI_TAG_MGMT_TX_COMPL_EVENT] @@ -863,7 +872,8 @@ static void ath11k_wmi_put_wmi_channel(struct wmi_channel *chan, chan->band_center_freq2 = arg->channel.band_center_freq1; - } else if (arg->channel.mode == MODE_11AC_VHT80_80) { + } else if ((arg->channel.mode == MODE_11AC_VHT80_80) || + (arg->channel.mode == MODE_11AX_HE80_80)) { chan->band_center_freq2 = arg->channel.band_center_freq2; } else { chan->band_center_freq2 = 0; @@ -2338,6 +2348,69 @@ int ath11k_wmi_send_scan_start_cmd(struct ath11k *ar, return ret; } +int ath11k_wmi_send_vdev_set_tpc_power(struct ath11k *ar, + u32 vdev_id, + struct ath11k_reg_tpc_power_info *param) +{ + struct ath11k_pdev_wmi *wmi = ar->wmi; + struct wmi_vdev_set_tpc_power_cmd *cmd; + struct wmi_vdev_ch_power_info *ch; + struct sk_buff *skb; + struct wmi_tlv *tlv; + u8 *ptr; + int i, ret, len; + + len = sizeof(*cmd) + TLV_HDR_SIZE; + len += (sizeof(struct wmi_vdev_ch_power_info) * param->num_pwr_levels); + + skb = ath11k_wmi_alloc_skb(wmi->wmi_ab, len); + if (!skb) + return -ENOMEM; + + ptr = skb->data; + + cmd = (struct wmi_vdev_set_tpc_power_cmd *)ptr; + cmd->tlv_header = FIELD_PREP(WMI_TLV_TAG, WMI_TAG_VDEV_SET_TPC_POWER_CMD) | + FIELD_PREP(WMI_TLV_LEN, sizeof(*cmd) - TLV_HDR_SIZE); + cmd->vdev_id = vdev_id; + cmd->psd_power = param->is_psd_power; + cmd->eirp_power = param->eirp_power; + cmd->power_type_6ghz = param->power_type_6g; + ath11k_dbg(ar->ab, ATH11K_DBG_WMI, + "wmi tpc vdev_id %d is_psd_power %d eirp_power %d power_type_6g %d\n", + vdev_id, param->is_psd_power, param->eirp_power, param->power_type_6g); + + ptr += sizeof(*cmd); + tlv = (struct wmi_tlv *)ptr; + tlv->header = FIELD_PREP(WMI_TLV_TAG, WMI_TAG_ARRAY_STRUCT) | + FIELD_PREP(WMI_TLV_LEN, param->num_pwr_levels * sizeof(*ch)); + + ptr += TLV_HDR_SIZE; + ch = (struct wmi_vdev_ch_power_info *)ptr; + + for (i = 0; i < param->num_pwr_levels; i++, ch++) { + ch->tlv_header = FIELD_PREP(WMI_TLV_TAG, + WMI_TAG_VDEV_CH_POWER_INFO) | + FIELD_PREP(WMI_TLV_LEN, + sizeof(*ch) - TLV_HDR_SIZE); + + ch->chan_cfreq = param->chan_power_info[i].chan_cfreq; + ch->tx_power = param->chan_power_info[i].tx_power; + + ath11k_dbg(ar->ab, ATH11K_DBG_WMI, + "wmi tpc chan_cfreq %d tx_power %d\n", + ch->chan_cfreq, ch->tx_power); + } + + ret = ath11k_wmi_cmd_send(wmi, skb, + WMI_VDEV_SET_TPC_POWER_CMDID); + if (ret) { + ath11k_warn(ar->ab, "failed to send WMI_VDEV_SET_TPC_POWER_CMDID\n"); + dev_kfree_skb(skb); + } + return ret; +} + int ath11k_wmi_send_scan_stop_cmd(struct ath11k *ar, struct scan_cancel_param *param) { @@ -3966,6 +4039,11 @@ ath11k_wmi_copy_resource_config(struct wmi_resource_config *wmi_cfg, wmi_cfg->sched_params = tg_cfg->sched_params; wmi_cfg->twt_ap_pdev_count = tg_cfg->twt_ap_pdev_count; wmi_cfg->twt_ap_sta_count = tg_cfg->twt_ap_sta_count; + wmi_cfg->host_service_flags = tg_cfg->host_service_flags; + wmi_cfg->host_service_flags &= + ~(1 << WMI_CFG_HOST_SERVICE_FLAG_REG_CC_EXT); + wmi_cfg->host_service_flags |= (tg_cfg->is_reg_cc_ext_event_supported << + WMI_CFG_HOST_SERVICE_FLAG_REG_CC_EXT); } static int ath11k_init_cmd_send(struct ath11k_pdev_wmi *wmi, @@ -4184,6 +4262,10 @@ int ath11k_wmi_cmd_init(struct ath11k_base *ab) ab->hw_params.hw_ops->wmi_init_config(ab, &config); + if (test_bit(WMI_TLV_SERVICE_REG_CC_EXT_EVENT_SUPPORT, + ab->wmi_ab.svc_map)) + config.is_reg_cc_ext_event_supported = 1; + memcpy(&wmi_sc->wlan_resource_config, &config, sizeof(config)); init_param.res_cfg = &wmi_sc->wlan_resource_config; @@ -4627,6 +4709,11 @@ static int ath11k_wmi_tlv_ext_soc_hal_reg_caps_parse(struct ath11k_base *soc, soc->pdevs[0].pdev_id = 0; } + if (!soc->reg_info_store) + soc->reg_info_store = kcalloc(soc->num_radios, + sizeof(*soc->reg_info_store), + GFP_ATOMIC); + return 0; } @@ -4902,11 +4989,32 @@ static int ath11k_pull_vdev_start_resp_tlv(struct ath11k_base *ab, struct sk_buf vdev_rsp->mac_id = ev->mac_id; vdev_rsp->cfgd_tx_streams = ev->cfgd_tx_streams; vdev_rsp->cfgd_rx_streams = ev->cfgd_rx_streams; + vdev_rsp->max_allowed_tx_power = ev->max_allowed_tx_power; kfree(tb); return 0; } +static void ath11k_print_reg_rule(struct ath11k_base *ab, const char *band, + u32 num_reg_rules, + struct cur_reg_rule *reg_rule_ptr) +{ + struct cur_reg_rule *reg_rule = reg_rule_ptr; + u32 count; + + ath11k_dbg(ab, ATH11K_DBG_WMI, "number of reg rules in %s band: %d\n", + band, num_reg_rules); + + for (count = 0; count < num_reg_rules; count++) { + ath11k_dbg(ab, ATH11K_DBG_WMI, + "reg rule %d: (%d - %d @ %d) (%d, %d) (FLAGS %d)\n", + count + 1, reg_rule->start_freq, reg_rule->end_freq, + reg_rule->max_bw, reg_rule->ant_gain, + reg_rule->reg_power, reg_rule->flags); + reg_rule++; + } +} + static struct cur_reg_rule *create_reg_rules_from_wmi(u32 num_reg_rules, struct wmi_regulatory_rule_struct *wmi_reg_rule) @@ -4951,7 +5059,7 @@ static int ath11k_pull_reg_chan_list_update_ev(struct ath11k_base *ab, const void **tb; const struct wmi_reg_chan_list_cc_event *chan_list_event_hdr; struct wmi_regulatory_rule_struct *wmi_reg_rule; - u32 num_2g_reg_rules, num_5g_reg_rules; + u32 num_2ghz_reg_rules, num_5ghz_reg_rules; int ret; ath11k_dbg(ab, ATH11K_DBG_WMI, "processing regulatory channel list\n"); @@ -4970,10 +5078,10 @@ static int ath11k_pull_reg_chan_list_update_ev(struct ath11k_base *ab, return -EPROTO; } - reg_info->num_2g_reg_rules = chan_list_event_hdr->num_2g_reg_rules; - reg_info->num_5g_reg_rules = chan_list_event_hdr->num_5g_reg_rules; + reg_info->num_2ghz_reg_rules = chan_list_event_hdr->num_2ghz_reg_rules; + reg_info->num_5ghz_reg_rules = chan_list_event_hdr->num_5ghz_reg_rules; - if (!(reg_info->num_2g_reg_rules + reg_info->num_5g_reg_rules)) { + if (!(reg_info->num_2ghz_reg_rules + reg_info->num_5ghz_reg_rules)) { ath11k_warn(ab, "No regulatory rules available in the event info\n"); kfree(tb); return -EINVAL; @@ -4987,61 +5095,68 @@ static int ath11k_pull_reg_chan_list_update_ev(struct ath11k_base *ab, reg_info->phy_id = chan_list_event_hdr->phy_id; reg_info->ctry_code = chan_list_event_hdr->country_id; reg_info->reg_dmn_pair = chan_list_event_hdr->domain_code; - if (chan_list_event_hdr->status_code == WMI_REG_SET_CC_STATUS_PASS) - reg_info->status_code = REG_SET_CC_STATUS_PASS; - else if (chan_list_event_hdr->status_code == WMI_REG_CURRENT_ALPHA2_NOT_FOUND) - reg_info->status_code = REG_CURRENT_ALPHA2_NOT_FOUND; - else if (chan_list_event_hdr->status_code == WMI_REG_INIT_ALPHA2_NOT_FOUND) - reg_info->status_code = REG_INIT_ALPHA2_NOT_FOUND; - else if (chan_list_event_hdr->status_code == WMI_REG_SET_CC_CHANGE_NOT_ALLOWED) - reg_info->status_code = REG_SET_CC_CHANGE_NOT_ALLOWED; - else if (chan_list_event_hdr->status_code == WMI_REG_SET_CC_STATUS_NO_MEMORY) - reg_info->status_code = REG_SET_CC_STATUS_NO_MEMORY; - else if (chan_list_event_hdr->status_code == WMI_REG_SET_CC_STATUS_FAIL) - reg_info->status_code = REG_SET_CC_STATUS_FAIL; - - reg_info->min_bw_2g = chan_list_event_hdr->min_bw_2g; - reg_info->max_bw_2g = chan_list_event_hdr->max_bw_2g; - reg_info->min_bw_5g = chan_list_event_hdr->min_bw_5g; - reg_info->max_bw_5g = chan_list_event_hdr->max_bw_5g; - - num_2g_reg_rules = reg_info->num_2g_reg_rules; - num_5g_reg_rules = reg_info->num_5g_reg_rules; ath11k_dbg(ab, ATH11K_DBG_WMI, - "%s:cc %s dsf %d BW: min_2g %d max_2g %d min_5g %d max_5g %d", - __func__, reg_info->alpha2, reg_info->dfs_region, - reg_info->min_bw_2g, reg_info->max_bw_2g, - reg_info->min_bw_5g, reg_info->max_bw_5g); + "status_code %s", + ath11k_cc_status_to_str(reg_info->status_code)); + + reg_info->status_code = + ath11k_wmi_cc_setting_code_to_reg(chan_list_event_hdr->status_code); + + reg_info->is_ext_reg_event = false; + + reg_info->min_bw_2ghz = chan_list_event_hdr->min_bw_2ghz; + reg_info->max_bw_2ghz = chan_list_event_hdr->max_bw_2ghz; + reg_info->min_bw_5ghz = chan_list_event_hdr->min_bw_5ghz; + reg_info->max_bw_5ghz = chan_list_event_hdr->max_bw_5ghz; + + num_2ghz_reg_rules = reg_info->num_2ghz_reg_rules; + num_5ghz_reg_rules = reg_info->num_5ghz_reg_rules; + + ath11k_dbg(ab, ATH11K_DBG_WMI, + "cc %s dsf %d BW: min_2ghz %d max_2ghz %d min_5ghz %d max_5ghz %d", + reg_info->alpha2, reg_info->dfs_region, + reg_info->min_bw_2ghz, reg_info->max_bw_2ghz, + reg_info->min_bw_5ghz, reg_info->max_bw_5ghz); ath11k_dbg(ab, ATH11K_DBG_WMI, - "%s: num_2g_reg_rules %d num_5g_reg_rules %d", __func__, - num_2g_reg_rules, num_5g_reg_rules); + "num_2ghz_reg_rules %d num_5ghz_reg_rules %d", + num_2ghz_reg_rules, num_5ghz_reg_rules); wmi_reg_rule = (struct wmi_regulatory_rule_struct *)((u8 *)chan_list_event_hdr + sizeof(*chan_list_event_hdr) + sizeof(struct wmi_tlv)); - if (num_2g_reg_rules) { - reg_info->reg_rules_2g_ptr = create_reg_rules_from_wmi(num_2g_reg_rules, - wmi_reg_rule); - if (!reg_info->reg_rules_2g_ptr) { + if (num_2ghz_reg_rules) { + reg_info->reg_rules_2ghz_ptr = + create_reg_rules_from_wmi(num_2ghz_reg_rules, + wmi_reg_rule); + if (!reg_info->reg_rules_2ghz_ptr) { kfree(tb); - ath11k_warn(ab, "Unable to Allocate memory for 2g rules\n"); + ath11k_warn(ab, "Unable to Allocate memory for 2 GHz rules\n"); return -ENOMEM; } + + ath11k_print_reg_rule(ab, "2 GHz", + num_2ghz_reg_rules, + reg_info->reg_rules_2ghz_ptr); } - if (num_5g_reg_rules) { - wmi_reg_rule += num_2g_reg_rules; - reg_info->reg_rules_5g_ptr = create_reg_rules_from_wmi(num_5g_reg_rules, - wmi_reg_rule); - if (!reg_info->reg_rules_5g_ptr) { + if (num_5ghz_reg_rules) { + wmi_reg_rule += num_2ghz_reg_rules; + reg_info->reg_rules_5ghz_ptr = + create_reg_rules_from_wmi(num_5ghz_reg_rules, + wmi_reg_rule); + if (!reg_info->reg_rules_5ghz_ptr) { kfree(tb); - ath11k_warn(ab, "Unable to Allocate memory for 5g rules\n"); + ath11k_warn(ab, "Unable to Allocate memory for 5 GHz rules\n"); return -ENOMEM; } + + ath11k_print_reg_rule(ab, "5 GHz", + num_5ghz_reg_rules, + reg_info->reg_rules_5ghz_ptr); } ath11k_dbg(ab, ATH11K_DBG_WMI, "processed regulatory channel list\n"); @@ -5050,6 +5165,429 @@ static int ath11k_pull_reg_chan_list_update_ev(struct ath11k_base *ab, return 0; } +static struct cur_reg_rule +*create_ext_reg_rules_from_wmi(u32 num_reg_rules, + struct wmi_regulatory_ext_rule *wmi_reg_rule) +{ + struct cur_reg_rule *reg_rule_ptr; + u32 count; + + reg_rule_ptr = kcalloc(num_reg_rules, sizeof(*reg_rule_ptr), GFP_ATOMIC); + + if (!reg_rule_ptr) + return NULL; + + for (count = 0; count < num_reg_rules; count++) { + reg_rule_ptr[count].start_freq = + u32_get_bits(wmi_reg_rule[count].freq_info, + REG_RULE_START_FREQ); + reg_rule_ptr[count].end_freq = + u32_get_bits(wmi_reg_rule[count].freq_info, + REG_RULE_END_FREQ); + reg_rule_ptr[count].max_bw = + u32_get_bits(wmi_reg_rule[count].bw_pwr_info, + REG_RULE_MAX_BW); + reg_rule_ptr[count].reg_power = + u32_get_bits(wmi_reg_rule[count].bw_pwr_info, + REG_RULE_REG_PWR); + reg_rule_ptr[count].ant_gain = + u32_get_bits(wmi_reg_rule[count].bw_pwr_info, + REG_RULE_ANT_GAIN); + reg_rule_ptr[count].flags = + u32_get_bits(wmi_reg_rule[count].flag_info, + REG_RULE_FLAGS); + reg_rule_ptr[count].psd_flag = + u32_get_bits(wmi_reg_rule[count].psd_power_info, + REG_RULE_PSD_INFO); + reg_rule_ptr[count].psd_eirp = + u32_get_bits(wmi_reg_rule[count].psd_power_info, + REG_RULE_PSD_EIRP); + } + + return reg_rule_ptr; +} + +static u8 +ath11k_invalid_5ghz_reg_ext_rules_from_wmi(u32 num_reg_rules, + const struct wmi_regulatory_ext_rule *rule) +{ + u8 num_invalid_5ghz_rules = 0; + u32 count, start_freq; + + for (count = 0; count < num_reg_rules; count++) { + start_freq = u32_get_bits(rule[count].freq_info, + REG_RULE_START_FREQ); + + if (start_freq >= ATH11K_MIN_6G_FREQ) + num_invalid_5ghz_rules++; + } + + return num_invalid_5ghz_rules; +} + +static int ath11k_pull_reg_chan_list_ext_update_ev(struct ath11k_base *ab, + struct sk_buff *skb, + struct cur_regulatory_info *reg_info) +{ + const void **tb; + const struct wmi_reg_chan_list_cc_ext_event *ev; + struct wmi_regulatory_ext_rule *ext_wmi_reg_rule; + u32 num_2ghz_reg_rules, num_5ghz_reg_rules; + u32 num_6ghz_reg_rules_ap[WMI_REG_CURRENT_MAX_AP_TYPE]; + u32 num_6ghz_client[WMI_REG_CURRENT_MAX_AP_TYPE][WMI_REG_MAX_CLIENT_TYPE]; + u32 total_reg_rules = 0; + int ret, i, j, num_invalid_5ghz_ext_rules = 0; + + ath11k_dbg(ab, ATH11K_DBG_WMI, "processing regulatory ext channel list\n"); + + tb = ath11k_wmi_tlv_parse_alloc(ab, skb->data, skb->len, GFP_ATOMIC); + if (IS_ERR(tb)) { + ret = PTR_ERR(tb); + ath11k_warn(ab, "failed to parse tlv: %d\n", ret); + return ret; + } + + ev = tb[WMI_TAG_REG_CHAN_LIST_CC_EXT_EVENT]; + if (!ev) { + ath11k_warn(ab, "failed to fetch reg chan list ext update ev\n"); + kfree(tb); + return -EPROTO; + } + + reg_info->num_2ghz_reg_rules = ev->num_2ghz_reg_rules; + reg_info->num_5ghz_reg_rules = ev->num_5ghz_reg_rules; + reg_info->num_6ghz_rules_ap[WMI_REG_INDOOR_AP] = + ev->num_6ghz_reg_rules_ap_lpi; + reg_info->num_6ghz_rules_ap[WMI_REG_STANDARD_POWER_AP] = + ev->num_6ghz_reg_rules_ap_sp; + reg_info->num_6ghz_rules_ap[WMI_REG_VERY_LOW_POWER_AP] = + ev->num_6ghz_reg_rules_ap_vlp; + + for (i = 0; i < WMI_REG_MAX_CLIENT_TYPE; i++) { + reg_info->num_6ghz_rules_client[WMI_REG_INDOOR_AP][i] = + ev->num_6ghz_reg_rules_client_lpi[i]; + reg_info->num_6ghz_rules_client[WMI_REG_STANDARD_POWER_AP][i] = + ev->num_6ghz_reg_rules_client_sp[i]; + reg_info->num_6ghz_rules_client[WMI_REG_VERY_LOW_POWER_AP][i] = + ev->num_6ghz_reg_rules_client_vlp[i]; + } + + num_2ghz_reg_rules = reg_info->num_2ghz_reg_rules; + num_5ghz_reg_rules = reg_info->num_5ghz_reg_rules; + + total_reg_rules += num_2ghz_reg_rules; + total_reg_rules += num_5ghz_reg_rules; + + if ((num_2ghz_reg_rules > MAX_REG_RULES) || + (num_5ghz_reg_rules > MAX_REG_RULES)) { + ath11k_warn(ab, "Num reg rules for 2.4 GHz/5 GHz exceeds max limit (num_2ghz_reg_rules: %d num_5ghz_reg_rules: %d max_rules: %d)\n", + num_2ghz_reg_rules, num_5ghz_reg_rules, MAX_REG_RULES); + kfree(tb); + return -EINVAL; + } + + for (i = 0; i < WMI_REG_CURRENT_MAX_AP_TYPE; i++) { + num_6ghz_reg_rules_ap[i] = reg_info->num_6ghz_rules_ap[i]; + + if (num_6ghz_reg_rules_ap[i] > MAX_6GHZ_REG_RULES) { + ath11k_warn(ab, "Num 6 GHz reg rules for AP mode(%d) exceeds max limit (num_6ghz_reg_rules_ap: %d, max_rules: %d)\n", + i, num_6ghz_reg_rules_ap[i], MAX_6GHZ_REG_RULES); + kfree(tb); + return -EINVAL; + } + + total_reg_rules += num_6ghz_reg_rules_ap[i]; + } + + for (i = 0; i < WMI_REG_MAX_CLIENT_TYPE; i++) { + num_6ghz_client[WMI_REG_INDOOR_AP][i] = + reg_info->num_6ghz_rules_client[WMI_REG_INDOOR_AP][i]; + total_reg_rules += num_6ghz_client[WMI_REG_INDOOR_AP][i]; + + num_6ghz_client[WMI_REG_STANDARD_POWER_AP][i] = + reg_info->num_6ghz_rules_client[WMI_REG_STANDARD_POWER_AP][i]; + total_reg_rules += num_6ghz_client[WMI_REG_STANDARD_POWER_AP][i]; + + num_6ghz_client[WMI_REG_VERY_LOW_POWER_AP][i] = + reg_info->num_6ghz_rules_client[WMI_REG_VERY_LOW_POWER_AP][i]; + total_reg_rules += num_6ghz_client[WMI_REG_VERY_LOW_POWER_AP][i]; + + if ((num_6ghz_client[WMI_REG_INDOOR_AP][i] > MAX_6GHZ_REG_RULES) || + (num_6ghz_client[WMI_REG_STANDARD_POWER_AP][i] > + MAX_6GHZ_REG_RULES) || + (num_6ghz_client[WMI_REG_VERY_LOW_POWER_AP][i] > + MAX_6GHZ_REG_RULES)) { + ath11k_warn(ab, + "Num 6 GHz client reg rules exceeds max limit, for client(type: %d)\n", + i); + kfree(tb); + return -EINVAL; + } + } + + if (!total_reg_rules) { + ath11k_warn(ab, "No reg rules available\n"); + kfree(tb); + return -EINVAL; + } + + memcpy(reg_info->alpha2, &ev->alpha2, REG_ALPHA2_LEN); + + reg_info->dfs_region = ev->dfs_region; + reg_info->phybitmap = ev->phybitmap; + reg_info->num_phy = ev->num_phy; + reg_info->phy_id = ev->phy_id; + reg_info->ctry_code = ev->country_id; + reg_info->reg_dmn_pair = ev->domain_code; + + ath11k_dbg(ab, ATH11K_DBG_WMI, + "status_code %s", + ath11k_cc_status_to_str(reg_info->status_code)); + + reg_info->status_code = + ath11k_wmi_cc_setting_code_to_reg(ev->status_code); + + reg_info->is_ext_reg_event = true; + + reg_info->min_bw_2ghz = ev->min_bw_2ghz; + reg_info->max_bw_2ghz = ev->max_bw_2ghz; + reg_info->min_bw_5ghz = ev->min_bw_5ghz; + reg_info->max_bw_5ghz = ev->max_bw_5ghz; + + reg_info->min_bw_6ghz_ap[WMI_REG_INDOOR_AP] = + ev->min_bw_6ghz_ap_lpi; + reg_info->max_bw_6ghz_ap[WMI_REG_INDOOR_AP] = + ev->max_bw_6ghz_ap_lpi; + reg_info->min_bw_6ghz_ap[WMI_REG_STANDARD_POWER_AP] = + ev->min_bw_6ghz_ap_sp; + reg_info->max_bw_6ghz_ap[WMI_REG_STANDARD_POWER_AP] = + ev->max_bw_6ghz_ap_sp; + reg_info->min_bw_6ghz_ap[WMI_REG_VERY_LOW_POWER_AP] = + ev->min_bw_6ghz_ap_vlp; + reg_info->max_bw_6ghz_ap[WMI_REG_VERY_LOW_POWER_AP] = + ev->max_bw_6ghz_ap_vlp; + + ath11k_dbg(ab, ATH11K_DBG_WMI, + "6 GHz AP BW: LPI (%d - %d), SP (%d - %d), VLP (%d - %d)\n", + reg_info->min_bw_6ghz_ap[WMI_REG_INDOOR_AP], + reg_info->max_bw_6ghz_ap[WMI_REG_INDOOR_AP], + reg_info->min_bw_6ghz_ap[WMI_REG_STANDARD_POWER_AP], + reg_info->max_bw_6ghz_ap[WMI_REG_STANDARD_POWER_AP], + reg_info->min_bw_6ghz_ap[WMI_REG_VERY_LOW_POWER_AP], + reg_info->max_bw_6ghz_ap[WMI_REG_VERY_LOW_POWER_AP]); + + for (i = 0; i < WMI_REG_MAX_CLIENT_TYPE; i++) { + reg_info->min_bw_6ghz_client[WMI_REG_INDOOR_AP][i] = + ev->min_bw_6ghz_client_lpi[i]; + reg_info->max_bw_6ghz_client[WMI_REG_INDOOR_AP][i] = + ev->max_bw_6ghz_client_lpi[i]; + reg_info->min_bw_6ghz_client[WMI_REG_STANDARD_POWER_AP][i] = + ev->min_bw_6ghz_client_sp[i]; + reg_info->max_bw_6ghz_client[WMI_REG_STANDARD_POWER_AP][i] = + ev->max_bw_6ghz_client_sp[i]; + reg_info->min_bw_6ghz_client[WMI_REG_VERY_LOW_POWER_AP][i] = + ev->min_bw_6ghz_client_vlp[i]; + reg_info->max_bw_6ghz_client[WMI_REG_VERY_LOW_POWER_AP][i] = + ev->max_bw_6ghz_client_vlp[i]; + + ath11k_dbg(ab, ATH11K_DBG_WMI, + "6 GHz %s BW: LPI (%d - %d), SP (%d - %d), VLP (%d - %d)\n", + ath11k_6ghz_client_type_to_str(i), + reg_info->min_bw_6ghz_client[WMI_REG_INDOOR_AP][i], + reg_info->max_bw_6ghz_client[WMI_REG_INDOOR_AP][i], + reg_info->min_bw_6ghz_client[WMI_REG_STANDARD_POWER_AP][i], + reg_info->max_bw_6ghz_client[WMI_REG_STANDARD_POWER_AP][i], + reg_info->min_bw_6ghz_client[WMI_REG_VERY_LOW_POWER_AP][i], + reg_info->max_bw_6ghz_client[WMI_REG_VERY_LOW_POWER_AP][i]); + } + + ath11k_dbg(ab, ATH11K_DBG_WMI, + "cc_ext %s dsf %d BW: min_2ghz %d max_2ghz %d min_5ghz %d max_5ghz %d", + reg_info->alpha2, reg_info->dfs_region, + reg_info->min_bw_2ghz, reg_info->max_bw_2ghz, + reg_info->min_bw_5ghz, reg_info->max_bw_5ghz); + + ath11k_dbg(ab, ATH11K_DBG_WMI, + "num_2ghz_reg_rules %d num_5ghz_reg_rules %d", + num_2ghz_reg_rules, num_5ghz_reg_rules); + + ath11k_dbg(ab, ATH11K_DBG_WMI, + "num_6ghz_reg_rules_ap_lpi: %d num_6ghz_reg_rules_ap_sp: %d num_6ghz_reg_rules_ap_vlp: %d", + num_6ghz_reg_rules_ap[WMI_REG_INDOOR_AP], + num_6ghz_reg_rules_ap[WMI_REG_STANDARD_POWER_AP], + num_6ghz_reg_rules_ap[WMI_REG_VERY_LOW_POWER_AP]); + + j = WMI_REG_DEFAULT_CLIENT; + ath11k_dbg(ab, ATH11K_DBG_WMI, + "6 GHz Regular client: num_6ghz_reg_rules_lpi: %d num_6ghz_reg_rules_sp: %d num_6ghz_reg_rules_vlp: %d", + num_6ghz_client[WMI_REG_INDOOR_AP][j], + num_6ghz_client[WMI_REG_STANDARD_POWER_AP][j], + num_6ghz_client[WMI_REG_VERY_LOW_POWER_AP][j]); + + j = WMI_REG_SUBORDINATE_CLIENT; + ath11k_dbg(ab, ATH11K_DBG_WMI, + "6 GHz Subordinate client: num_6ghz_reg_rules_lpi: %d num_6ghz_reg_rules_sp: %d num_6ghz_reg_rules_vlp: %d", + num_6ghz_client[WMI_REG_INDOOR_AP][j], + num_6ghz_client[WMI_REG_STANDARD_POWER_AP][j], + num_6ghz_client[WMI_REG_VERY_LOW_POWER_AP][j]); + + ext_wmi_reg_rule = + (struct wmi_regulatory_ext_rule *)((u8 *)ev + sizeof(*ev) + + sizeof(struct wmi_tlv)); + if (num_2ghz_reg_rules) { + reg_info->reg_rules_2ghz_ptr = + create_ext_reg_rules_from_wmi(num_2ghz_reg_rules, + ext_wmi_reg_rule); + + if (!reg_info->reg_rules_2ghz_ptr) { + kfree(tb); + ath11k_warn(ab, "Unable to Allocate memory for 2 GHz rules\n"); + return -ENOMEM; + } + + ath11k_print_reg_rule(ab, "2 GHz", + num_2ghz_reg_rules, + reg_info->reg_rules_2ghz_ptr); + } + + ext_wmi_reg_rule += num_2ghz_reg_rules; + + /* Firmware might include 6 GHz reg rule in 5 GHz rule list + * for few countries along with separate 6 GHz rule. + * Having same 6 GHz reg rule in 5 GHz and 6 GHz rules list + * causes intersect check to be true, and same rules will be + * shown multiple times in iw cmd. + * Hence, avoid parsing 6 GHz rule from 5 GHz reg rule list + */ + num_invalid_5ghz_ext_rules = + ath11k_invalid_5ghz_reg_ext_rules_from_wmi(num_5ghz_reg_rules, + ext_wmi_reg_rule); + + if (num_invalid_5ghz_ext_rules) { + ath11k_dbg(ab, ATH11K_DBG_WMI, + "CC: %s 5 GHz reg rules number %d from fw, %d number of invalid 5 GHz rules", + reg_info->alpha2, reg_info->num_5ghz_reg_rules, + num_invalid_5ghz_ext_rules); + + num_5ghz_reg_rules = num_5ghz_reg_rules - num_invalid_5ghz_ext_rules; + reg_info->num_5ghz_reg_rules = num_5ghz_reg_rules; + } + + if (num_5ghz_reg_rules) { + reg_info->reg_rules_5ghz_ptr = + create_ext_reg_rules_from_wmi(num_5ghz_reg_rules, + ext_wmi_reg_rule); + + if (!reg_info->reg_rules_5ghz_ptr) { + kfree(tb); + ath11k_warn(ab, "Unable to Allocate memory for 5 GHz rules\n"); + return -ENOMEM; + } + + ath11k_print_reg_rule(ab, "5 GHz", + num_5ghz_reg_rules, + reg_info->reg_rules_5ghz_ptr); + } + + /* We have adjusted the number of 5 GHz reg rules above. But still those + * many rules needs to be adjusted in ext_wmi_reg_rule. + * + * NOTE: num_invalid_5ghz_ext_rules will be 0 for rest other cases. + */ + ext_wmi_reg_rule += (num_5ghz_reg_rules + num_invalid_5ghz_ext_rules); + + for (i = 0; i < WMI_REG_CURRENT_MAX_AP_TYPE; i++) { + reg_info->reg_rules_6ghz_ap_ptr[i] = + create_ext_reg_rules_from_wmi(num_6ghz_reg_rules_ap[i], + ext_wmi_reg_rule); + + if (!reg_info->reg_rules_6ghz_ap_ptr[i]) { + kfree(tb); + ath11k_warn(ab, "Unable to Allocate memory for 6 GHz AP rules\n"); + return -ENOMEM; + } + + ath11k_print_reg_rule(ab, ath11k_6ghz_ap_type_to_str(i), + num_6ghz_reg_rules_ap[i], + reg_info->reg_rules_6ghz_ap_ptr[i]); + + ext_wmi_reg_rule += num_6ghz_reg_rules_ap[i]; + } + + for (j = 0; j < WMI_REG_CURRENT_MAX_AP_TYPE; j++) { + ath11k_dbg(ab, ATH11K_DBG_WMI, + "6 GHz AP type %s", ath11k_6ghz_ap_type_to_str(j)); + + for (i = 0; i < WMI_REG_MAX_CLIENT_TYPE; i++) { + reg_info->reg_rules_6ghz_client_ptr[j][i] = + create_ext_reg_rules_from_wmi(num_6ghz_client[j][i], + ext_wmi_reg_rule); + + if (!reg_info->reg_rules_6ghz_client_ptr[j][i]) { + kfree(tb); + ath11k_warn(ab, "Unable to Allocate memory for 6 GHz client rules\n"); + return -ENOMEM; + } + + ath11k_print_reg_rule(ab, + ath11k_6ghz_client_type_to_str(i), + num_6ghz_client[j][i], + reg_info->reg_rules_6ghz_client_ptr[j][i]); + + ext_wmi_reg_rule += num_6ghz_client[j][i]; + } + } + + reg_info->client_type = ev->client_type; + reg_info->rnr_tpe_usable = ev->rnr_tpe_usable; + reg_info->unspecified_ap_usable = + ev->unspecified_ap_usable; + reg_info->domain_code_6ghz_ap[WMI_REG_INDOOR_AP] = + ev->domain_code_6ghz_ap_lpi; + reg_info->domain_code_6ghz_ap[WMI_REG_STANDARD_POWER_AP] = + ev->domain_code_6ghz_ap_sp; + reg_info->domain_code_6ghz_ap[WMI_REG_VERY_LOW_POWER_AP] = + ev->domain_code_6ghz_ap_vlp; + + ath11k_dbg(ab, ATH11K_DBG_WMI, + "6 GHz reg info client type %s rnr_tpe_usable %d unspecified_ap_usable %d AP sub domain: lpi %s, sp %s, vlp %s\n", + ath11k_6ghz_client_type_to_str(reg_info->client_type), + reg_info->rnr_tpe_usable, + reg_info->unspecified_ap_usable, + ath11k_sub_reg_6ghz_to_str(ev->domain_code_6ghz_ap_lpi), + ath11k_sub_reg_6ghz_to_str(ev->domain_code_6ghz_ap_sp), + ath11k_sub_reg_6ghz_to_str(ev->domain_code_6ghz_ap_vlp)); + + for (i = 0; i < WMI_REG_MAX_CLIENT_TYPE; i++) { + reg_info->domain_code_6ghz_client[WMI_REG_INDOOR_AP][i] = + ev->domain_code_6ghz_client_lpi[i]; + reg_info->domain_code_6ghz_client[WMI_REG_STANDARD_POWER_AP][i] = + ev->domain_code_6ghz_client_sp[i]; + reg_info->domain_code_6ghz_client[WMI_REG_VERY_LOW_POWER_AP][i] = + ev->domain_code_6ghz_client_vlp[i]; + + ath11k_dbg(ab, ATH11K_DBG_WMI, + "6 GHz client type %s client sub domain: lpi %s, sp %s, vlp %s\n", + ath11k_6ghz_client_type_to_str(i), + ath11k_sub_reg_6ghz_to_str(ev->domain_code_6ghz_client_lpi[i]), + ath11k_sub_reg_6ghz_to_str(ev->domain_code_6ghz_client_sp[i]), + ath11k_sub_reg_6ghz_to_str(ev->domain_code_6ghz_client_vlp[i]) + ); + } + + reg_info->domain_code_6ghz_super_id = ev->domain_code_6ghz_super_id; + + ath11k_dbg(ab, ATH11K_DBG_WMI, + "6 GHz client_type %s 6 GHz super domain %s", + ath11k_6ghz_client_type_to_str(reg_info->client_type), + ath11k_super_reg_6ghz_to_str(reg_info->domain_code_6ghz_super_id)); + + ath11k_dbg(ab, ATH11K_DBG_WMI, "processed regulatory ext channel list\n"); + + kfree(tb); + return 0; +} + static int ath11k_pull_peer_del_resp_ev(struct ath11k_base *ab, struct sk_buff *skb, struct wmi_peer_delete_resp_event *peer_del_resp) { @@ -5165,28 +5703,49 @@ static int ath11k_pull_vdev_stopped_param_tlv(struct ath11k_base *ab, struct sk_ return 0; } +static int ath11k_wmi_tlv_mgmt_rx_parse(struct ath11k_base *ab, + u16 tag, u16 len, + const void *ptr, void *data) +{ + struct wmi_tlv_mgmt_rx_parse *parse = data; + + switch (tag) { + case WMI_TAG_MGMT_RX_HDR: + parse->fixed = ptr; + break; + case WMI_TAG_ARRAY_BYTE: + if (!parse->frame_buf_done) { + parse->frame_buf = ptr; + parse->frame_buf_done = true; + } + break; + } + return 0; +} + static int ath11k_pull_mgmt_rx_params_tlv(struct ath11k_base *ab, struct sk_buff *skb, struct mgmt_rx_event_params *hdr) { - const void **tb; + struct wmi_tlv_mgmt_rx_parse parse = { }; const struct wmi_mgmt_rx_hdr *ev; const u8 *frame; int ret; - tb = ath11k_wmi_tlv_parse_alloc(ab, skb->data, skb->len, GFP_ATOMIC); - if (IS_ERR(tb)) { - ret = PTR_ERR(tb); - ath11k_warn(ab, "failed to parse tlv: %d\n", ret); + ret = ath11k_wmi_tlv_iter(ab, skb->data, skb->len, + ath11k_wmi_tlv_mgmt_rx_parse, + &parse); + if (ret) { + ath11k_warn(ab, "failed to parse mgmt rx tlv %d\n", + ret); return ret; } - ev = tb[WMI_TAG_MGMT_RX_HDR]; - frame = tb[WMI_TAG_ARRAY_BYTE]; + ev = parse.fixed; + frame = parse.frame_buf; if (!ev || !frame) { ath11k_warn(ab, "failed to fetch mgmt rx hdr"); - kfree(tb); return -EPROTO; } @@ -5205,7 +5764,6 @@ static int ath11k_pull_mgmt_rx_params_tlv(struct ath11k_base *ab, if (skb->len < (frame - skb->data) + hdr->buf_len) { ath11k_warn(ab, "invalid length in mgmt rx hdr ev"); - kfree(tb); return -EPROTO; } @@ -5217,7 +5775,6 @@ static int ath11k_pull_mgmt_rx_params_tlv(struct ath11k_base *ab, ath11k_ce_byte_swap(skb->data, hdr->buf_len); - kfree(tb); return 0; } @@ -6491,25 +7048,34 @@ static bool ath11k_reg_is_world_alpha(char *alpha) return false; } -static int ath11k_reg_chan_list_event(struct ath11k_base *ab, struct sk_buff *skb) +void ath11k_reg_reset_info(struct cur_regulatory_info *reg_info) { - struct cur_regulatory_info *reg_info = NULL; - struct ieee80211_regdomain *regd = NULL; - bool intersect = false; - int ret = 0, pdev_idx; - struct ath11k *ar; + int i, j; - reg_info = kzalloc(sizeof(*reg_info), GFP_ATOMIC); - if (!reg_info) { - ret = -ENOMEM; - goto fallback; - } + if (reg_info) { + kfree(reg_info->reg_rules_2ghz_ptr); - ret = ath11k_pull_reg_chan_list_update_ev(ab, skb, reg_info); - if (ret) { - ath11k_warn(ab, "failed to extract regulatory info from received event\n"); - goto fallback; + kfree(reg_info->reg_rules_5ghz_ptr); + + for (i = 0; i < WMI_REG_CURRENT_MAX_AP_TYPE; i++) { + kfree(reg_info->reg_rules_6ghz_ap_ptr[i]); + for (j = 0; j < WMI_REG_MAX_CLIENT_TYPE; j++) + kfree(reg_info->reg_rules_6ghz_client_ptr[i][j]); + } + + memset(reg_info, 0, sizeof(*reg_info)); } +} + +int ath11k_reg_handle_chan_list(struct ath11k_base *ab, + struct cur_regulatory_info *reg_info, + enum ieee80211_ap_reg_power power_type) +{ + struct ieee80211_regdomain *regd; + bool intersect = false; + int pdev_idx; + struct ath11k *ar; + enum wmi_vdev_type vdev_type; if (reg_info->status_code != REG_SET_CC_STATUS_PASS) { /* In case of failure to set the requested ctry, @@ -6517,7 +7083,7 @@ static int ath11k_reg_chan_list_event(struct ath11k_base *ab, struct sk_buff *sk * and return from here. */ ath11k_warn(ab, "Failed to set the requested Country regulatory setting\n"); - goto mem_free; + return -EINVAL; } pdev_idx = reg_info->phy_id; @@ -6525,13 +7091,13 @@ static int ath11k_reg_chan_list_event(struct ath11k_base *ab, struct sk_buff *sk /* Avoid default reg rule updates sent during FW recovery if * it is already available */ - spin_lock(&ab->base_lock); + spin_lock_bh(&ab->base_lock); if (test_bit(ATH11K_FLAG_RECOVERY, &ab->dev_flags) && ab->default_regd[pdev_idx]) { - spin_unlock(&ab->base_lock); - goto mem_free; + spin_unlock_bh(&ab->base_lock); + goto retfail; } - spin_unlock(&ab->base_lock); + spin_unlock_bh(&ab->base_lock); if (pdev_idx >= ab->num_radios) { /* Process the event for phy0 only if single_pdev_only @@ -6540,7 +7106,7 @@ static int ath11k_reg_chan_list_event(struct ath11k_base *ab, struct sk_buff *sk */ if (ab->hw_params.single_pdev_only && pdev_idx < ab->hw_params.num_rxmda_per_pdev) - goto mem_free; + goto retfail; else goto fallback; } @@ -6551,7 +7117,7 @@ static int ath11k_reg_chan_list_event(struct ath11k_base *ab, struct sk_buff *sk if (ab->default_regd[pdev_idx] && !ab->new_regd[pdev_idx] && !memcmp((char *)ab->default_regd[pdev_idx]->alpha2, (char *)reg_info->alpha2, 2)) - goto mem_free; + goto retfail; /* Intersect new rules with default regd if a new country setting was * requested, i.e a default regd was already set during initialization @@ -6563,13 +7129,25 @@ static int ath11k_reg_chan_list_event(struct ath11k_base *ab, struct sk_buff *sk !ath11k_reg_is_world_alpha((char *)reg_info->alpha2)) intersect = true; - regd = ath11k_reg_build_regd(ab, reg_info, intersect); + ar = ab->pdevs[pdev_idx].ar; + vdev_type = ath11k_mac_get_ar_vdev_type(ar); + + ath11k_dbg(ab, ATH11K_DBG_WMI, + "wmi handle chan list power type %d vdev type %d intersect %d\n", + power_type, vdev_type, intersect); + + regd = ath11k_reg_build_regd(ab, reg_info, intersect, vdev_type, power_type); if (!regd) { ath11k_warn(ab, "failed to build regd from reg_info\n"); goto fallback; } - spin_lock(&ab->base_lock); + if (power_type == IEEE80211_REG_UNSET_AP) { + ath11k_reg_reset_info(&ab->reg_info_store[pdev_idx]); + ab->reg_info_store[pdev_idx] = *reg_info; + } + + spin_lock_bh(&ab->base_lock); if (ab->default_regd[pdev_idx]) { /* The initial rules from FW after WMI Init is to build * the default regd. From then on, any rules updated for @@ -6589,9 +7167,9 @@ static int ath11k_reg_chan_list_event(struct ath11k_base *ab, struct sk_buff *sk ab->default_regd[pdev_idx] = regd; } ab->dfs_region = reg_info->dfs_region; - spin_unlock(&ab->base_lock); + spin_unlock_bh(&ab->base_lock); - goto mem_free; + return 0; fallback: /* Fallback to older reg (by sending previous country setting @@ -6603,12 +7181,45 @@ fallback: */ /* TODO: This is rare, but still should also be handled */ WARN_ON(1); -mem_free: - if (reg_info) { - kfree(reg_info->reg_rules_2g_ptr); - kfree(reg_info->reg_rules_5g_ptr); - kfree(reg_info); + +retfail: + + return -EINVAL; +} + +static int ath11k_reg_chan_list_event(struct ath11k_base *ab, struct sk_buff *skb, + enum wmi_reg_chan_list_cmd_type id) +{ + struct cur_regulatory_info *reg_info; + int ret; + + reg_info = kzalloc(sizeof(*reg_info), GFP_ATOMIC); + if (!reg_info) + return -ENOMEM; + + if (id == WMI_REG_CHAN_LIST_CC_ID) + ret = ath11k_pull_reg_chan_list_update_ev(ab, skb, reg_info); + else + ret = ath11k_pull_reg_chan_list_ext_update_ev(ab, skb, reg_info); + + if (ret) { + ath11k_warn(ab, "failed to extract regulatory info from received event\n"); + goto mem_free; + } + + ret = ath11k_reg_handle_chan_list(ab, reg_info, IEEE80211_REG_UNSET_AP); + if (ret) { + ath11k_dbg(ab, ATH11K_DBG_WMI, + "failed to process regulatory info from received event\n"); + goto mem_free; } + + kfree(reg_info); + return 0; + +mem_free: + ath11k_reg_reset_info(reg_info); + kfree(reg_info); return ret; } @@ -6762,7 +7373,7 @@ static void ath11k_vdev_start_resp_event(struct ath11k_base *ab, struct sk_buff } ar->last_wmi_vdev_start_status = 0; - + ar->max_allowed_tx_power = vdev_start_resp.max_allowed_tx_power; status = vdev_start_resp.status; if (WARN_ON_ONCE(status)) { @@ -7750,6 +8361,37 @@ exit: } static void +ath11k_wmi_tm_event_segmented(struct ath11k_base *ab, u32 cmd_id, + struct sk_buff *skb) +{ + const void **tb; + const struct wmi_ftm_event_msg *ev; + u16 length; + int ret; + + tb = ath11k_wmi_tlv_parse_alloc(ab, skb->data, skb->len, GFP_ATOMIC); + if (IS_ERR(tb)) { + ret = PTR_ERR(tb); + ath11k_warn(ab, "failed to parse ftm event tlv: %d\n", ret); + return; + } + + ev = tb[WMI_TAG_ARRAY_BYTE]; + if (!ev) { + ath11k_warn(ab, "failed to fetch ftm msg\n"); + kfree(tb); + return; + } + + length = skb->len - TLV_HDR_SIZE; + ret = ath11k_tm_process_event(ab, cmd_id, ev, length); + if (ret) + ath11k_warn(ab, "Failed to process ftm event\n"); + + kfree(tb); +} + +static void ath11k_wmi_pdev_temperature_event(struct ath11k_base *ab, struct sk_buff *skb) { @@ -8044,7 +8686,10 @@ static void ath11k_wmi_tlv_op_rx(struct ath11k_base *ab, struct sk_buff *skb) ath11k_service_ready_ext2_event(ab, skb); break; case WMI_REG_CHAN_LIST_CC_EVENTID: - ath11k_reg_chan_list_event(ab, skb); + ath11k_reg_chan_list_event(ab, skb, WMI_REG_CHAN_LIST_CC_ID); + break; + case WMI_REG_CHAN_LIST_CC_EXT_EVENTID: + ath11k_reg_chan_list_event(ab, skb, WMI_REG_CHAN_LIST_CC_EXT_ID); break; case WMI_READY_EVENTID: ath11k_ready_event(ab, skb); @@ -8101,6 +8746,12 @@ static void ath11k_wmi_tlv_op_rx(struct ath11k_base *ab, struct sk_buff *skb) case WMI_PDEV_CSA_SWITCH_COUNT_STATUS_EVENTID: ath11k_wmi_pdev_csa_switch_count_status_event(ab, skb); break; + case WMI_PDEV_UTF_EVENTID: + if (test_bit(ATH11K_FLAG_FTM_SEGMENTED, &ab->dev_flags)) + ath11k_wmi_tm_event_segmented(ab, id, skb); + else + ath11k_tm_wmi_event_unsegmented(ab, id, skb); + break; case WMI_PDEV_TEMPERATURE_EVENTID: ath11k_wmi_pdev_temperature_event(ab, skb); break; @@ -9170,3 +9821,76 @@ int ath11k_wmi_sta_keepalive(struct ath11k *ar, return ath11k_wmi_cmd_send(wmi, skb, WMI_STA_KEEPALIVE_CMDID); } + +int ath11k_wmi_set_unit_test(struct ath11k *ar, struct unit_test_cmd *unit_test) +{ + struct ath11k_pdev_wmi *wmi = ar->wmi; + struct sk_buff *skb; + struct wmi_unit_test_cmd_fixed_param *cmd; + u32 len, args_tlv_len; + u8 *buf_ptr; + u32 *args; + struct wmi_tlv *tlv; + u32 i; + + args_tlv_len = TLV_HDR_SIZE + unit_test->num_args * sizeof(u32); + + len = sizeof(struct wmi_unit_test_cmd_fixed_param) + args_tlv_len; + skb = ath11k_wmi_alloc_skb(wmi->wmi_ab, len); + if (!skb) + return -ENOMEM; + + cmd = (struct wmi_unit_test_cmd_fixed_param *)skb->data; + buf_ptr = (u8 *)cmd; + cmd->tlv_header = FIELD_PREP(WMI_TLV_TAG, + WMI_TAG_UNIT_TEST_CMD) | + FIELD_PREP(WMI_TLV_LEN, sizeof(*cmd) - TLV_HDR_SIZE); + cmd->vdev_id = unit_test->vdev_id; + cmd->module_id = unit_test->module_id; + cmd->num_args = unit_test->num_args; + + buf_ptr += sizeof(*cmd); + + tlv = (struct wmi_tlv *)buf_ptr; + tlv->header = FIELD_PREP(WMI_TLV_TAG, WMI_TAG_ARRAY_UINT32) | + FIELD_PREP(WMI_TLV_LEN, unit_test->num_args * sizeof(u32)); + args = (u32 *)(buf_ptr + TLV_HDR_SIZE); + ath11k_info(ar->ab, "module id = 0x%x, num args = %u", + unit_test->module_id, unit_test->num_args); + for (i = 0; (i < unit_test->num_args && i < UNIT_TEST_MAX_NUM_ARGS); i++) { + args[i] = unit_test->args[i]; + ath11k_info(ar->ab, "0x%x,", unit_test->args[i]); + } + + return ath11k_wmi_cmd_send(wmi, skb, WMI_UNIT_TEST_CMDID); +} + +int ath11k_wmi_send_coex_config(struct ath11k *ar, + struct wmi_coex_config_params *param) +{ + struct ath11k_pdev_wmi *wmi = ar->wmi; + struct wmi_coex_config_cmd *cmd; + struct sk_buff *skb; + + skb = ath11k_wmi_alloc_skb(wmi->wmi_ab, sizeof(*cmd)); + if (!skb) + return -ENOMEM; + + cmd = (struct wmi_coex_config_cmd *)skb->data; + cmd->tlv_header = FIELD_PREP(WMI_TLV_TAG, WMI_TAG_COEX_CONFIG_CMD) | + FIELD_PREP(WMI_TLV_LEN, sizeof(*cmd) - TLV_HDR_SIZE); + cmd->vdev_id = param->vdev_id; + cmd->config_type = param->config_type; + cmd->config_arg1 = param->config_arg1; + cmd->config_arg2 = param->config_arg2; + cmd->config_arg3 = param->config_arg3; + cmd->config_arg4 = param->config_arg4; + cmd->config_arg5 = param->config_arg5; + cmd->config_arg6 = param->config_arg6; + ath11k_warn(ar->ab, "wmi send coex cfg vdev %d type %u args %u %u %u %u %u %u\n", + cmd->vdev_id, cmd->config_type, cmd->config_arg1, + cmd->config_arg2, cmd->config_arg3, cmd->config_arg4, + cmd->config_arg5, cmd->config_arg6); + + return ath11k_wmi_cmd_send(wmi, skb, WMI_COEX_CONFIG_CMDID); +} diff --git a/drivers/net/wireless/ath/ath11k/wmi.h b/drivers/net/wireless/ath/ath11k/wmi.h index 8f2c07d70a4a..e47f621ebb99 100644 --- a/drivers/net/wireless/ath/ath11k/wmi.h +++ b/drivers/net/wireless/ath/ath11k/wmi.h @@ -1,6 +1,7 @@ /* SPDX-License-Identifier: BSD-3-Clause-Clear */ /* * Copyright (c) 2018-2019 The Linux Foundation. All rights reserved. + * Copyright (c) 2023 Qualcomm Innovation Center, Inc. All rights reserved. */ #ifndef ATH11K_WMI_H @@ -14,6 +15,8 @@ struct ath11k; struct ath11k_fw_stats; struct ath11k_fw_dbglog; struct ath11k_vif; +struct ath11k_base; +struct ath11k_reg_tpc_power_info; #define PSOC_HOST_MAX_NUM_SS (8) @@ -68,6 +71,7 @@ struct wmi_tlv { #define WMI_APPEND_TO_EXISTING_CHAN_LIST_FLAG 1 +#define MAX_WMI_UTF_LEN 252 #define WMI_BA_MODE_BUFFER_SIZE_256 3 /* * HW mode config type replicated from FW header @@ -317,6 +321,36 @@ enum wmi_tlv_cmd_id { WMI_VDEV_SET_CUSTOM_AGGR_SIZE_CMDID, WMI_VDEV_ENCRYPT_DECRYPT_DATA_REQ_CMDID, WMI_VDEV_ADD_MAC_ADDR_TO_RX_FILTER_CMDID, + /** WMI commands related to dbg arp stats */ + WMI_VDEV_SET_ARP_STAT_CMDID, + WMI_VDEV_GET_ARP_STAT_CMDID, + /** get tx power for the current vdev */ + WMI_VDEV_GET_TX_POWER_CMDID, + /* limit STA offchannel activity */ + WMI_VDEV_LIMIT_OFFCHAN_CMDID, + /** To set custom software retries per-AC for vdev */ + WMI_VDEV_SET_CUSTOM_SW_RETRY_TH_CMDID, + /** To set chainmask configuration for vdev */ + WMI_VDEV_CHAINMASK_CONFIG_CMDID, + WMI_VDEV_GET_BCN_RECEPTION_STATS_CMDID, + /* request LTE-Coex info */ + WMI_VDEV_GET_MWS_COEX_INFO_CMDID, + /** delete all peer (excluding bss peer) */ + WMI_VDEV_DELETE_ALL_PEER_CMDID, + /* To set bss max idle time related parameters */ + WMI_VDEV_BSS_MAX_IDLE_TIME_CMDID, + /** Indicates firmware to trigger Audio sync */ + WMI_VDEV_AUDIO_SYNC_TRIGGER_CMDID, + /** Gives Qtimer value to firmware */ + WMI_VDEV_AUDIO_SYNC_QTIMER_CMDID, + /** Preferred channel list for each vdev */ + WMI_VDEV_SET_PCL_CMDID, + /** VDEV_GET_BIG_DATA_CMD IS DEPRECATED - DO NOT USE */ + WMI_VDEV_GET_BIG_DATA_CMDID, + /** Get per vdev BIG DATA stats phase 2 */ + WMI_VDEV_GET_BIG_DATA_P2_CMDID, + /** set TPC PSD/non-PSD power */ + WMI_VDEV_SET_TPC_POWER_CMDID, WMI_PEER_CREATE_CMDID = WMI_TLV_CMD(WMI_GRP_PEER), WMI_PEER_DELETE_CMDID, WMI_PEER_FLUSH_TIDS_CMDID, @@ -797,6 +831,7 @@ enum wmi_tlv_event_id { WMI_RMC_NEW_LEADER_EVENTID = WMI_TLV_CMD(WMI_GRP_RMC), WMI_REG_CHAN_LIST_CC_EVENTID = WMI_TLV_CMD(WMI_GRP_REGULATORY), WMI_11D_NEW_COUNTRY_EVENTID, + WMI_REG_CHAN_LIST_CC_EXT_EVENTID, WMI_NDI_CAP_RSP_EVENTID = WMI_TLV_CMD(WMI_GRP_PROTOTYPE), WMI_NDP_INITIATOR_RSP_EVENTID, WMI_NDP_RESPONDER_RSP_EVENTID, @@ -1864,6 +1899,10 @@ enum wmi_tlv_tag { WMI_TAG_PDEV_SRG_OBSS_BSSID_ENABLE_BITMAP_CMD, WMI_TAG_PDEV_NON_SRG_OBSS_COLOR_ENABLE_BITMAP_CMD, WMI_TAG_PDEV_NON_SRG_OBSS_BSSID_ENABLE_BITMAP_CMD, + WMI_TAG_REGULATORY_RULE_EXT_STRUCT = 0x3A9, + WMI_TAG_REG_CHAN_LIST_CC_EXT_EVENT, + WMI_TAG_VDEV_SET_TPC_POWER_CMD = 0x3B5, + WMI_TAG_VDEV_CH_POWER_INFO, WMI_TAG_PDEV_SET_BIOS_SAR_TABLE_CMD = 0x3D8, WMI_TAG_PDEV_SET_BIOS_GEO_TABLE_CMD, WMI_TAG_MAX @@ -2095,7 +2134,10 @@ enum wmi_tlv_service { /* The second 128 bits */ WMI_MAX_EXT_SERVICE = 256, + WMI_TLV_SERVICE_EXT_TPC_REG_SUPPORT = 280, + WMI_TLV_SERVICE_REG_CC_EXT_EVENT_SUPPORT = 281, WMI_TLV_SERVICE_BIOS_SAR_SUPPORT = 326, + WMI_TLV_SERVICE_SUPPORT_11D_FOR_HOST_SCAN = 357, /* The third 128 bits */ WMI_MAX_EXT2_SERVICE = 384 @@ -2309,6 +2351,9 @@ struct wmi_init_cmd { } __packed; #define WMI_RSRC_CFG_FLAG1_BSS_CHANNEL_INFO_64 BIT(5) +#define WMI_RSRC_CFG_HOST_SERVICE_FLAG_NAN_IFACE_SUPPORT BIT(0) + +#define WMI_CFG_HOST_SERVICE_FLAG_REG_CC_EXT 4 struct wmi_resource_config { u32 tlv_header; @@ -2369,6 +2414,15 @@ struct wmi_resource_config { u32 sched_params; u32 twt_ap_pdev_count; u32 twt_ap_sta_count; + u32 max_nlo_ssids; + u32 num_packet_filters; + u32 num_max_sta_vdevs; + u32 max_bssid_indicator; + u32 ul_resp_config; + u32 msdu_flow_override_config0; + u32 msdu_flow_override_config1; + u32 flags2; + u32 host_service_flags; } __packed; struct wmi_service_ready_event { @@ -2851,36 +2905,40 @@ struct rx_reorder_queue_remove_params { #define REG_RULE_MAX_BW 0x0000ffff #define REG_RULE_REG_PWR 0x00ff0000 #define REG_RULE_ANT_GAIN 0xff000000 +#define REG_RULE_PSD_INFO BIT(0) +#define REG_RULE_PSD_EIRP 0xff0000 #define WMI_VDEV_PARAM_TXBF_SU_TX_BFEE BIT(0) #define WMI_VDEV_PARAM_TXBF_MU_TX_BFEE BIT(1) #define WMI_VDEV_PARAM_TXBF_SU_TX_BFER BIT(2) #define WMI_VDEV_PARAM_TXBF_MU_TX_BFER BIT(3) -#define HECAP_PHYDWORD_0 0 -#define HECAP_PHYDWORD_1 1 -#define HECAP_PHYDWORD_2 2 +#define HE_PHYCAP_BYTE_0 0 +#define HE_PHYCAP_BYTE_1 1 +#define HE_PHYCAP_BYTE_2 2 +#define HE_PHYCAP_BYTE_3 3 +#define HE_PHYCAP_BYTE_4 4 -#define HECAP_PHY_SU_BFER BIT(31) +#define HECAP_PHY_SU_BFER BIT(7) #define HECAP_PHY_SU_BFEE BIT(0) #define HECAP_PHY_MU_BFER BIT(1) -#define HECAP_PHY_UL_MUMIMO BIT(22) -#define HECAP_PHY_UL_MUOFDMA BIT(23) +#define HECAP_PHY_UL_MUMIMO BIT(6) +#define HECAP_PHY_UL_MUOFDMA BIT(7) #define HECAP_PHY_SUBFMR_GET(hecap_phy) \ - FIELD_GET(HECAP_PHY_SU_BFER, hecap_phy[HECAP_PHYDWORD_0]) + FIELD_GET(HECAP_PHY_SU_BFER, hecap_phy[HE_PHYCAP_BYTE_3]) #define HECAP_PHY_SUBFME_GET(hecap_phy) \ - FIELD_GET(HECAP_PHY_SU_BFEE, hecap_phy[HECAP_PHYDWORD_1]) + FIELD_GET(HECAP_PHY_SU_BFEE, hecap_phy[HE_PHYCAP_BYTE_4]) #define HECAP_PHY_MUBFMR_GET(hecap_phy) \ - FIELD_GET(HECAP_PHY_MU_BFER, hecap_phy[HECAP_PHYDWORD_1]) + FIELD_GET(HECAP_PHY_MU_BFER, hecap_phy[HE_PHYCAP_BYTE_4]) #define HECAP_PHY_ULMUMIMO_GET(hecap_phy) \ - FIELD_GET(HECAP_PHY_UL_MUMIMO, hecap_phy[HECAP_PHYDWORD_0]) + FIELD_GET(HECAP_PHY_UL_MUMIMO, hecap_phy[HE_PHYCAP_BYTE_2]) #define HECAP_PHY_ULOFDMA_GET(hecap_phy) \ - FIELD_GET(HECAP_PHY_UL_MUOFDMA, hecap_phy[HECAP_PHYDWORD_0]) + FIELD_GET(HECAP_PHY_UL_MUOFDMA, hecap_phy[HE_PHYCAP_BYTE_2]) #define HE_MODE_SU_TX_BFEE BIT(0) #define HE_MODE_SU_TX_BFER BIT(1) @@ -2893,8 +2951,11 @@ struct rx_reorder_queue_remove_params { #define HE_DL_MUOFDMA_ENABLE 1 #define HE_UL_MUOFDMA_ENABLE 1 #define HE_DL_MUMIMO_ENABLE 1 +#define HE_UL_MUMIMO_ENABLE 1 #define HE_MU_BFEE_ENABLE 1 #define HE_SU_BFEE_ENABLE 1 +#define HE_MU_BFER_ENABLE 1 +#define HE_SU_BFER_ENABLE 1 #define HE_VHT_SOUNDING_MODE_ENABLE 1 #define HE_SU_MU_SOUNDING_MODE_ENABLE 1 @@ -3115,6 +3176,31 @@ struct wlan_ssid { u8 ssid[WLAN_SSID_MAX_LEN]; }; +struct wmi_vdev_ch_power_info { + u32 tlv_header; + u32 chan_cfreq; /* Channel center frequency (MHz) */ + /* Unit: dBm, either PSD/EIRP power for this frequency or + * incremental for non-PSD BW + */ + u32 tx_power; +} __packed; + +struct wmi_vdev_set_tpc_power_cmd { + u32 tlv_header; + u32 vdev_id; + u32 psd_power; /* Value: 0 or 1, is PSD power or not */ + u32 eirp_power; /* Maximum EIRP power (dBm units), valid only if power is PSD */ + u32 power_type_6ghz; /* Type: WMI_6GHZ_REG_TYPE, used for halphy CTL lookup */ + /* This fixed_param TLV is followed by the below TLVs: + * num_pwr_levels of wmi_vdev_ch_power_info + * For PSD power, it is the PSD/EIRP power of the frequency (20 MHz chunks). + * For non-PSD power, the power values are for 20, 40, and till + * BSS BW power levels. + * The num_pwr_levels will be checked by sw how many elements present + * in the variable-length array. + */ +} __packed; + #define WMI_IE_BITMAP_SIZE 8 /* prefix used by scan requestor ids on the host */ @@ -3509,6 +3595,24 @@ struct wmi_get_pdev_temperature_cmd { u32 pdev_id; } __packed; +struct wmi_ftm_seg_hdr { + u32 len; + u32 msgref; + u32 segmentinfo; + u32 pdev_id; +} __packed; + +struct wmi_ftm_cmd { + u32 tlv_header; + struct wmi_ftm_seg_hdr seg_hdr; + u8 data[]; +} __packed; + +struct wmi_ftm_event_msg { + struct wmi_ftm_seg_hdr seg_hdr; + u8 data[]; +} __packed; + #define WMI_BEACON_TX_BUFFER_SIZE 512 struct wmi_bcn_tmpl_cmd { @@ -3681,6 +3785,7 @@ struct wmi_stop_scan_cmd { }; struct scan_chan_list_params { + struct list_head list; u32 pdev_id; u16 nallchans; struct channel_param ch_param[]; @@ -4039,6 +4144,7 @@ struct wmi_he_rate_set { #define MAX_REG_RULES 10 #define REG_ALPHA2_LEN 2 +#define MAX_6GHZ_REG_RULES 5 enum wmi_start_event_param { WMI_VDEV_START_RESP_EVENT = 0, @@ -4058,6 +4164,7 @@ struct wmi_vdev_start_resp_event { }; u32 cfgd_tx_streams; u32 cfgd_rx_streams; + s32 max_allowed_tx_power; } __packed; /* VDEV start response status codes */ @@ -4069,16 +4176,6 @@ enum wmi_vdev_start_resp_status_code { WMI_VDEV_START_RESPONSE_INVALID_REGDOMAIN = 4, }; -; -enum cc_setting_code { - REG_SET_CC_STATUS_PASS = 0, - REG_CURRENT_ALPHA2_NOT_FOUND = 1, - REG_INIT_ALPHA2_NOT_FOUND = 2, - REG_SET_CC_CHANGE_NOT_ALLOWED = 3, - REG_SET_CC_STATUS_NO_MEMORY = 4, - REG_SET_CC_STATUS_FAIL = 5, -}; - /* Regaulatory Rule Flags Passed by FW */ #define REGULATORY_CHAN_DISABLED BIT(0) #define REGULATORY_CHAN_NO_IR BIT(1) @@ -4092,15 +4189,216 @@ enum cc_setting_code { #define REGULATORY_CHAN_NO_20MHZ BIT(11) #define REGULATORY_CHAN_NO_10MHZ BIT(12) -enum { +enum wmi_reg_chan_list_cmd_type { + WMI_REG_CHAN_LIST_CC_ID = 0, + WMI_REG_CHAN_LIST_CC_EXT_ID = 1, +}; + +enum wmi_reg_cc_setting_code { WMI_REG_SET_CC_STATUS_PASS = 0, WMI_REG_CURRENT_ALPHA2_NOT_FOUND = 1, WMI_REG_INIT_ALPHA2_NOT_FOUND = 2, WMI_REG_SET_CC_CHANGE_NOT_ALLOWED = 3, WMI_REG_SET_CC_STATUS_NO_MEMORY = 4, WMI_REG_SET_CC_STATUS_FAIL = 5, + + /* add new setting code above, update in + * @enum cc_setting_code as well. + * Also handle it in ath11k_wmi_cc_setting_code_to_reg() + */ +}; + +enum cc_setting_code { + REG_SET_CC_STATUS_PASS = 0, + REG_CURRENT_ALPHA2_NOT_FOUND = 1, + REG_INIT_ALPHA2_NOT_FOUND = 2, + REG_SET_CC_CHANGE_NOT_ALLOWED = 3, + REG_SET_CC_STATUS_NO_MEMORY = 4, + REG_SET_CC_STATUS_FAIL = 5, + + /* add new setting code above, update in + * @enum wmi_reg_cc_setting_code as well. + * Also handle it in ath11k_cc_status_to_str() + */ +}; + +static inline enum cc_setting_code +ath11k_wmi_cc_setting_code_to_reg(enum wmi_reg_cc_setting_code status_code) +{ + switch (status_code) { + case WMI_REG_SET_CC_STATUS_PASS: + return REG_SET_CC_STATUS_PASS; + case WMI_REG_CURRENT_ALPHA2_NOT_FOUND: + return REG_CURRENT_ALPHA2_NOT_FOUND; + case WMI_REG_INIT_ALPHA2_NOT_FOUND: + return REG_INIT_ALPHA2_NOT_FOUND; + case WMI_REG_SET_CC_CHANGE_NOT_ALLOWED: + return REG_SET_CC_CHANGE_NOT_ALLOWED; + case WMI_REG_SET_CC_STATUS_NO_MEMORY: + return REG_SET_CC_STATUS_NO_MEMORY; + case WMI_REG_SET_CC_STATUS_FAIL: + return REG_SET_CC_STATUS_FAIL; + } + + return REG_SET_CC_STATUS_FAIL; +} + +static inline const char *ath11k_cc_status_to_str(enum cc_setting_code code) +{ + switch (code) { + case REG_SET_CC_STATUS_PASS: + return "REG_SET_CC_STATUS_PASS"; + case REG_CURRENT_ALPHA2_NOT_FOUND: + return "REG_CURRENT_ALPHA2_NOT_FOUND"; + case REG_INIT_ALPHA2_NOT_FOUND: + return "REG_INIT_ALPHA2_NOT_FOUND"; + case REG_SET_CC_CHANGE_NOT_ALLOWED: + return "REG_SET_CC_CHANGE_NOT_ALLOWED"; + case REG_SET_CC_STATUS_NO_MEMORY: + return "REG_SET_CC_STATUS_NO_MEMORY"; + case REG_SET_CC_STATUS_FAIL: + return "REG_SET_CC_STATUS_FAIL"; + } + + return "Unknown CC status"; +} + +enum wmi_reg_6ghz_ap_type { + WMI_REG_INDOOR_AP = 0, + WMI_REG_STANDARD_POWER_AP = 1, + WMI_REG_VERY_LOW_POWER_AP = 2, + + /* add AP type above, handle in ath11k_6ghz_ap_type_to_str() + */ + WMI_REG_CURRENT_MAX_AP_TYPE, + WMI_REG_MAX_AP_TYPE = 7, }; +static inline const char * +ath11k_6ghz_ap_type_to_str(enum wmi_reg_6ghz_ap_type type) +{ + switch (type) { + case WMI_REG_INDOOR_AP: + return "INDOOR AP"; + case WMI_REG_STANDARD_POWER_AP: + return "STANDARD POWER AP"; + case WMI_REG_VERY_LOW_POWER_AP: + return "VERY LOW POWER AP"; + case WMI_REG_CURRENT_MAX_AP_TYPE: + return "CURRENT_MAX_AP_TYPE"; + case WMI_REG_MAX_AP_TYPE: + return "MAX_AP_TYPE"; + } + + return "unknown 6 GHz AP type"; +} + +enum wmi_reg_6ghz_client_type { + WMI_REG_DEFAULT_CLIENT = 0, + WMI_REG_SUBORDINATE_CLIENT = 1, + WMI_REG_MAX_CLIENT_TYPE = 2, + + /* add client type above, handle it in + * ath11k_6ghz_client_type_to_str() + */ +}; + +static inline const char * +ath11k_6ghz_client_type_to_str(enum wmi_reg_6ghz_client_type type) +{ + switch (type) { + case WMI_REG_DEFAULT_CLIENT: + return "DEFAULT CLIENT"; + case WMI_REG_SUBORDINATE_CLIENT: + return "SUBORDINATE CLIENT"; + case WMI_REG_MAX_CLIENT_TYPE: + return "MAX_CLIENT_TYPE"; + } + + return "unknown 6 GHz client type"; +} + +enum reg_subdomains_6ghz { + EMPTY_6GHZ = 0x0, + FCC1_CLIENT_LPI_REGULAR_6GHZ = 0x01, + FCC1_CLIENT_SP_6GHZ = 0x02, + FCC1_AP_LPI_6GHZ = 0x03, + FCC1_CLIENT_LPI_SUBORDINATE = FCC1_AP_LPI_6GHZ, + FCC1_AP_SP_6GHZ = 0x04, + ETSI1_LPI_6GHZ = 0x10, + ETSI1_VLP_6GHZ = 0x11, + ETSI2_LPI_6GHZ = 0x12, + ETSI2_VLP_6GHZ = 0x13, + APL1_LPI_6GHZ = 0x20, + APL1_VLP_6GHZ = 0x21, + + /* add sub-domain above, handle it in + * ath11k_sub_reg_6ghz_to_str() + */ +}; + +static inline const char * +ath11k_sub_reg_6ghz_to_str(enum reg_subdomains_6ghz sub_id) +{ + switch (sub_id) { + case EMPTY_6GHZ: + return "N/A"; + case FCC1_CLIENT_LPI_REGULAR_6GHZ: + return "FCC1_CLIENT_LPI_REGULAR_6GHZ"; + case FCC1_CLIENT_SP_6GHZ: + return "FCC1_CLIENT_SP_6GHZ"; + case FCC1_AP_LPI_6GHZ: + return "FCC1_AP_LPI_6GHZ/FCC1_CLIENT_LPI_SUBORDINATE"; + case FCC1_AP_SP_6GHZ: + return "FCC1_AP_SP_6GHZ"; + case ETSI1_LPI_6GHZ: + return "ETSI1_LPI_6GHZ"; + case ETSI1_VLP_6GHZ: + return "ETSI1_VLP_6GHZ"; + case ETSI2_LPI_6GHZ: + return "ETSI2_LPI_6GHZ"; + case ETSI2_VLP_6GHZ: + return "ETSI2_VLP_6GHZ"; + case APL1_LPI_6GHZ: + return "APL1_LPI_6GHZ"; + case APL1_VLP_6GHZ: + return "APL1_VLP_6GHZ"; + } + + return "unknown sub reg id"; +} + +enum reg_super_domain_6ghz { + FCC1_6GHZ = 0x01, + ETSI1_6GHZ = 0x02, + ETSI2_6GHZ = 0x03, + APL1_6GHZ = 0x04, + FCC1_6GHZ_CL = 0x05, + + /* add super domain above, handle it in + * ath11k_super_reg_6ghz_to_str() + */ +}; + +static inline const char * +ath11k_super_reg_6ghz_to_str(enum reg_super_domain_6ghz domain_id) +{ + switch (domain_id) { + case FCC1_6GHZ: + return "FCC1_6GHZ"; + case ETSI1_6GHZ: + return "ETSI1_6GHZ"; + case ETSI2_6GHZ: + return "ETSI2_6GHZ"; + case APL1_6GHZ: + return "APL1_6GHZ"; + case FCC1_6GHZ_CL: + return "FCC1_6GHZ_CL"; + } + + return "unknown domain id"; +} + struct cur_reg_rule { u16 start_freq; u16 end_freq; @@ -4108,6 +4406,8 @@ struct cur_reg_rule { u8 reg_power; u8 ant_gain; u16 flags; + bool psd_flag; + s8 psd_eirp; }; struct cur_regulatory_info { @@ -4119,14 +4419,30 @@ struct cur_regulatory_info { u8 alpha2[REG_ALPHA2_LEN + 1]; u32 dfs_region; u32 phybitmap; - u32 min_bw_2g; - u32 max_bw_2g; - u32 min_bw_5g; - u32 max_bw_5g; - u32 num_2g_reg_rules; - u32 num_5g_reg_rules; - struct cur_reg_rule *reg_rules_2g_ptr; - struct cur_reg_rule *reg_rules_5g_ptr; + u32 min_bw_2ghz; + u32 max_bw_2ghz; + u32 min_bw_5ghz; + u32 max_bw_5ghz; + u32 num_2ghz_reg_rules; + u32 num_5ghz_reg_rules; + struct cur_reg_rule *reg_rules_2ghz_ptr; + struct cur_reg_rule *reg_rules_5ghz_ptr; + bool is_ext_reg_event; + enum wmi_reg_6ghz_client_type client_type; + bool rnr_tpe_usable; + bool unspecified_ap_usable; + u8 domain_code_6ghz_ap[WMI_REG_CURRENT_MAX_AP_TYPE]; + u8 domain_code_6ghz_client[WMI_REG_CURRENT_MAX_AP_TYPE][WMI_REG_MAX_CLIENT_TYPE]; + u32 domain_code_6ghz_super_id; + u32 min_bw_6ghz_ap[WMI_REG_CURRENT_MAX_AP_TYPE]; + u32 max_bw_6ghz_ap[WMI_REG_CURRENT_MAX_AP_TYPE]; + u32 min_bw_6ghz_client[WMI_REG_CURRENT_MAX_AP_TYPE][WMI_REG_MAX_CLIENT_TYPE]; + u32 max_bw_6ghz_client[WMI_REG_CURRENT_MAX_AP_TYPE][WMI_REG_MAX_CLIENT_TYPE]; + u32 num_6ghz_rules_ap[WMI_REG_CURRENT_MAX_AP_TYPE]; + u32 num_6ghz_rules_client[WMI_REG_CURRENT_MAX_AP_TYPE][WMI_REG_MAX_CLIENT_TYPE]; + struct cur_reg_rule *reg_rules_6ghz_ap_ptr[WMI_REG_CURRENT_MAX_AP_TYPE]; + struct cur_reg_rule *reg_rules_6ghz_client_ptr + [WMI_REG_CURRENT_MAX_AP_TYPE][WMI_REG_MAX_CLIENT_TYPE]; }; struct wmi_reg_chan_list_cc_event { @@ -4138,12 +4454,12 @@ struct wmi_reg_chan_list_cc_event { u32 domain_code; u32 dfs_region; u32 phybitmap; - u32 min_bw_2g; - u32 max_bw_2g; - u32 min_bw_5g; - u32 max_bw_5g; - u32 num_2g_reg_rules; - u32 num_5g_reg_rules; + u32 min_bw_2ghz; + u32 max_bw_2ghz; + u32 min_bw_5ghz; + u32 max_bw_5ghz; + u32 num_2ghz_reg_rules; + u32 num_5ghz_reg_rules; } __packed; struct wmi_regulatory_rule_struct { @@ -4153,6 +4469,61 @@ struct wmi_regulatory_rule_struct { u32 flag_info; }; +#define WMI_REG_CLIENT_MAX 4 + +struct wmi_reg_chan_list_cc_ext_event { + u32 status_code; + u32 phy_id; + u32 alpha2; + u32 num_phy; + u32 country_id; + u32 domain_code; + u32 dfs_region; + u32 phybitmap; + u32 min_bw_2ghz; + u32 max_bw_2ghz; + u32 min_bw_5ghz; + u32 max_bw_5ghz; + u32 num_2ghz_reg_rules; + u32 num_5ghz_reg_rules; + u32 client_type; + u32 rnr_tpe_usable; + u32 unspecified_ap_usable; + u32 domain_code_6ghz_ap_lpi; + u32 domain_code_6ghz_ap_sp; + u32 domain_code_6ghz_ap_vlp; + u32 domain_code_6ghz_client_lpi[WMI_REG_CLIENT_MAX]; + u32 domain_code_6ghz_client_sp[WMI_REG_CLIENT_MAX]; + u32 domain_code_6ghz_client_vlp[WMI_REG_CLIENT_MAX]; + u32 domain_code_6ghz_super_id; + u32 min_bw_6ghz_ap_sp; + u32 max_bw_6ghz_ap_sp; + u32 min_bw_6ghz_ap_lpi; + u32 max_bw_6ghz_ap_lpi; + u32 min_bw_6ghz_ap_vlp; + u32 max_bw_6ghz_ap_vlp; + u32 min_bw_6ghz_client_sp[WMI_REG_CLIENT_MAX]; + u32 max_bw_6ghz_client_sp[WMI_REG_CLIENT_MAX]; + u32 min_bw_6ghz_client_lpi[WMI_REG_CLIENT_MAX]; + u32 max_bw_6ghz_client_lpi[WMI_REG_CLIENT_MAX]; + u32 min_bw_6ghz_client_vlp[WMI_REG_CLIENT_MAX]; + u32 max_bw_6ghz_client_vlp[WMI_REG_CLIENT_MAX]; + u32 num_6ghz_reg_rules_ap_sp; + u32 num_6ghz_reg_rules_ap_lpi; + u32 num_6ghz_reg_rules_ap_vlp; + u32 num_6ghz_reg_rules_client_sp[WMI_REG_CLIENT_MAX]; + u32 num_6ghz_reg_rules_client_lpi[WMI_REG_CLIENT_MAX]; + u32 num_6ghz_reg_rules_client_vlp[WMI_REG_CLIENT_MAX]; +} __packed; + +struct wmi_regulatory_ext_rule { + u32 tlv_header; + u32 freq_info; + u32 bw_pwr_info; + u32 flag_info; + u32 psd_power_info; +} __packed; + struct wmi_vdev_delete_resp_event { u32 vdev_id; } __packed; @@ -4624,6 +4995,7 @@ struct ath11k_targ_cap { }; enum wmi_vdev_type { + WMI_VDEV_TYPE_UNSPEC = 0, WMI_VDEV_TYPE_AP = 1, WMI_VDEV_TYPE_STA = 2, WMI_VDEV_TYPE_IBSS = 3, @@ -5346,6 +5718,16 @@ struct target_resource_config { u32 sched_params; u32 twt_ap_pdev_count; u32 twt_ap_sta_count; + u32 max_nlo_ssids; + u32 num_packet_filters; + u32 num_max_sta_vdevs; + u32 max_bssid_indicator; + u32 ul_resp_config; + u32 msdu_flow_override_config0; + u32 msdu_flow_override_config1; + u32 flags2; + u32 host_service_flags; + u8 is_reg_cc_ext_event_supported; }; enum wmi_debug_log_param { @@ -5966,6 +6348,105 @@ enum wmi_sta_keepalive_method { #define WMI_STA_KEEPALIVE_INTERVAL_DEFAULT 30 #define WMI_STA_KEEPALIVE_INTERVAL_DISABLE 0 +#define UNIT_TEST_MAX_NUM_ARGS 8 + +struct unit_test_cmd { + u32 vdev_id; + u32 module_id; + u32 num_args; + u32 args[UNIT_TEST_MAX_NUM_ARGS]; +}; + +struct wmi_unit_test_cmd_fixed_param { + u32 tlv_header; + u32 vdev_id; + u32 module_id; + u32 num_args; + u32 diag_token; + /** + * TLV (tag length value) parameters follow the wmi_unit_test_cmd_fixed_param + * structure. The TLV's are: + * u32 args[]; + */ +} __packed; + +enum wmi_coex_config_type { + WMI_COEX_CONFIG_PAGE_P2P_TDM = 1, + WMI_COEX_CONFIG_PAGE_STA_TDM = 2, + WMI_COEX_CONFIG_PAGE_SAP_TDM = 3, + WMI_COEX_CONFIG_DURING_WLAN_CONN = 4, + WMI_COEX_CONFIG_BTC_ENABLE = 5, + WMI_COEX_CONFIG_COEX_DBG = 6, + WMI_COEX_CONFIG_PAGE_P2P_STA_TDM = 7, + WMI_COEX_CONFIG_INQUIRY_P2P_TDM = 8, + WMI_COEX_CONFIG_INQUIRY_STA_TDM = 9, + WMI_COEX_CONFIG_INQUIRY_SAP_TDM = 10, + WMI_COEX_CONFIG_INQUIRY_P2P_STA_TDM = 11, + WMI_COEX_CONFIG_TX_POWER = 12, + WMI_COEX_CONFIG_PTA_CONFIG = 13, + WMI_COEX_CONFIG_AP_TDM = 14, + WMI_COEX_CONFIG_WLAN_SCAN_PRIORITY = 15, + WMI_COEX_CONFIG_WLAN_PKT_PRIORITY = 16, + WMI_COEX_CONFIG_PTA_INTERFACE = 17, + WMI_COEX_CONFIG_BTC_DUTYCYCLE = 18, + WMI_COEX_CONFIG_HANDOVER_RSSI = 19, + WMI_COEX_CONFIG_PTA_BT_INFO = 20, + WMI_COEX_CONFIG_SINK_WLAN_TDM = 21, + WMI_COEX_CONFIG_COEX_ENABLE_MCC_TDM = 22, + WMI_COEX_CONFIG_LOWRSSI_A2DPOPP_TDM = 23, + WMI_COEX_CONFIG_BTC_MODE = 24, + WMI_COEX_CONFIG_ANTENNA_ISOLATION = 25, + WMI_COEX_CONFIG_BT_LOW_RSSI_THRESHOLD = 26, + WMI_COEX_CONFIG_BT_INTERFERENCE_LEVEL = 27, + WMI_COEX_CONFIG_WLAN_OVER_ZBLOW = 28, + WMI_COEX_CONFIG_WLAN_MGMT_OVER_BT_A2DP = 29, + WMI_COEX_CONFIG_WLAN_CONN_OVER_LE = 30, + WMI_COEX_CONFIG_LE_OVER_WLAN_TRAFFIC = 31, + WMI_COEX_CONFIG_THREE_WAY_COEX_RESET = 32, + WMI_COEX_CONFIG_THREE_WAY_DELAY_PARA = 33, + WMI_COEX_CONFIG_THREE_WAY_COEX_START = 34, + WMI_COEX_CONFIG_MPTA_HELPER_ENABLE = 35, + WMI_COEX_CONFIG_MPTA_HELPER_ZIGBEE_STATE = 36, + WMI_COEX_CONFIG_MPTA_HELPER_INT_OCS_PARAMS = 37, + WMI_COEX_CONFIG_MPTA_HELPER_MON_OCS_PARAMS = 38, + WMI_COEX_CONFIG_MPTA_HELPER_INT_MON_DURATION = 39, + WMI_COEX_CONFIG_MPTA_HELPER_ZIGBEE_CHANNEL = 40, + WMI_COEX_CONFIG_MPTA_HELPER_WLAN_MUTE_DURATION = 41, + WMI_COEX_CONFIG_BT_SCO_ALLOW_WLAN_2G_SCAN = 42, + WMI_COEX_CONFIG_ENABLE_2ND_HARMONIC_WAR = 43, + WMI_COEX_CONFIG_BTCOEX_SEPARATE_CHAIN_MODE = 44, + WMI_COEX_CONFIG_ENABLE_TPUT_SHAPING = 45, + WMI_COEX_CONFIG_ENABLE_TXBF = 46, + WMI_COEX_CONFIG_FORCED_ALGO = 47, + WMI_COEX_CONFIG_LE_SCAN_POLICY = 48, +}; + +struct wmi_coex_config_params { + u32 vdev_id; + u32 config_type; + u32 config_arg1; + u32 config_arg2; + u32 config_arg3; + u32 config_arg4; + u32 config_arg5; + u32 config_arg6; +}; + +struct wmi_coex_config_cmd { + u32 tlv_header; + u32 vdev_id; + u32 config_type; + u32 config_arg1; + u32 config_arg2; + u32 config_arg3; + u32 config_arg4; + u32 config_arg5; + u32 config_arg6; +} __packed; + +#define WMI_COEX_ISOLATION_ARG1_DEFAUT 30 +#define WMI_COEX_BTC_MODE_ARG1_DEFAULT 1 + int ath11k_wmi_cmd_send(struct ath11k_pdev_wmi *wmi, struct sk_buff *skb, u32 cmd_id); struct sk_buff *ath11k_wmi_alloc_skb(struct ath11k_wmi_base *wmi_sc, u32 len); @@ -6129,6 +6610,8 @@ int ath11k_wmi_scan_prob_req_oui(struct ath11k *ar, const u8 mac_addr[ETH_ALEN]); int ath11k_wmi_fw_dbglog_cfg(struct ath11k *ar, u32 *module_id_bitmap, struct ath11k_fw_dbglog *dbglog); +int ath11k_wmi_set_unit_test(struct ath11k *ar, struct unit_test_cmd *unit_test); +int ath11k_wmi_send_coex_config(struct ath11k *ar, struct wmi_coex_config_params *param); int ath11k_wmi_wow_config_pno(struct ath11k *ar, u32 vdev_id, struct wmi_pno_scan_req *pno_scan); int ath11k_wmi_wow_del_pattern(struct ath11k *ar, u32 vdev_id, u32 pattern_id); @@ -6151,4 +6634,11 @@ int ath11k_wmi_pdev_set_bios_geo_table_param(struct ath11k *ar); int ath11k_wmi_sta_keepalive(struct ath11k *ar, const struct wmi_sta_keepalive_arg *arg); +void ath11k_reg_reset_info(struct cur_regulatory_info *reg_info); +int ath11k_reg_handle_chan_list(struct ath11k_base *ab, + struct cur_regulatory_info *reg_info, + enum ieee80211_ap_reg_power power_type); +int ath11k_wmi_send_vdev_set_tpc_power(struct ath11k *ar, + u32 vdev_id, + struct ath11k_reg_tpc_power_info *param); #endif diff --git a/drivers/net/wireless/ath/ath11k/wow.c b/drivers/net/wireless/ath/ath11k/wow.c index 1dec23b0699c..e94777f217cd 100644 --- a/drivers/net/wireless/ath/ath11k/wow.c +++ b/drivers/net/wireless/ath/ath11k/wow.c @@ -1,7 +1,7 @@ // SPDX-License-Identifier: BSD-3-Clause-Clear /* * Copyright (c) 2020 The Linux Foundation. All rights reserved. - * Copyright (c) 2022, Qualcomm Innovation Center, Inc. All rights reserved. + * Copyright (c) 2022-2023 Qualcomm Innovation Center, Inc. All rights reserved. */ #include <linux/delay.h> @@ -838,6 +838,7 @@ exit: case ATH11K_STATE_RESTARTING: case ATH11K_STATE_RESTARTED: case ATH11K_STATE_WEDGED: + case ATH11K_STATE_TM: ath11k_warn(ar->ab, "encountered unexpected device state %d on resume, cannot recover\n", ar->state); ret = -EIO; diff --git a/drivers/usb/core/hcd-pci.c b/drivers/usb/core/hcd-pci.c index 9b77f49b3560..6445528f8756 100644 --- a/drivers/usb/core/hcd-pci.c +++ b/drivers/usb/core/hcd-pci.c @@ -415,12 +415,15 @@ static int check_root_hub_suspended(struct device *dev) return 0; } -static int suspend_common(struct device *dev, bool do_wakeup) +static int suspend_common(struct device *dev, pm_message_t msg) { struct pci_dev *pci_dev = to_pci_dev(dev); struct usb_hcd *hcd = pci_get_drvdata(pci_dev); + bool do_wakeup; int retval; + do_wakeup = PMSG_IS_AUTO(msg) ? true : device_may_wakeup(dev); + /* Root hub suspend should have stopped all downstream traffic, * and all bus master traffic. And done so for both the interface * and the stub usb_device (which we check here). But maybe it @@ -447,7 +450,7 @@ static int suspend_common(struct device *dev, bool do_wakeup) (retval == 0 && do_wakeup && hcd->shared_hcd && HCD_WAKEUP_PENDING(hcd->shared_hcd))) { if (hcd->driver->pci_resume) - hcd->driver->pci_resume(hcd, false); + hcd->driver->pci_resume(hcd, msg); retval = -EBUSY; } if (retval) @@ -470,7 +473,7 @@ static int suspend_common(struct device *dev, bool do_wakeup) return retval; } -static int resume_common(struct device *dev, int event) +static int resume_common(struct device *dev, pm_message_t msg) { struct pci_dev *pci_dev = to_pci_dev(dev); struct usb_hcd *hcd = pci_get_drvdata(pci_dev); @@ -498,12 +501,11 @@ static int resume_common(struct device *dev, int event) * No locking is needed because PCI controller drivers do not * get unbound during system resume. */ - if (pci_dev->class == CL_EHCI && event != PM_EVENT_AUTO_RESUME) + if (pci_dev->class == CL_EHCI && msg.event != PM_EVENT_AUTO_RESUME) for_each_companion(pci_dev, hcd, ehci_wait_for_companions); - retval = hcd->driver->pci_resume(hcd, - event == PM_EVENT_RESTORE); + retval = hcd->driver->pci_resume(hcd, msg); if (retval) { dev_err(dev, "PCI post-resume error %d!\n", retval); usb_hc_died(hcd); @@ -516,7 +518,7 @@ static int resume_common(struct device *dev, int event) static int hcd_pci_suspend(struct device *dev) { - return suspend_common(dev, device_may_wakeup(dev)); + return suspend_common(dev, PMSG_SUSPEND); } static int hcd_pci_suspend_noirq(struct device *dev) @@ -566,12 +568,12 @@ static int hcd_pci_resume_noirq(struct device *dev) static int hcd_pci_resume(struct device *dev) { - return resume_common(dev, PM_EVENT_RESUME); + return resume_common(dev, PMSG_RESUME); } static int hcd_pci_restore(struct device *dev) { - return resume_common(dev, PM_EVENT_RESTORE); + return resume_common(dev, PMSG_RESTORE); } #else @@ -588,7 +590,7 @@ static int hcd_pci_runtime_suspend(struct device *dev) { int retval; - retval = suspend_common(dev, true); + retval = suspend_common(dev, PMSG_AUTO_SUSPEND); if (retval == 0) powermac_set_asic(to_pci_dev(dev), 0); dev_dbg(dev, "hcd_pci_runtime_suspend: %d\n", retval); @@ -600,7 +602,7 @@ static int hcd_pci_runtime_resume(struct device *dev) int retval; powermac_set_asic(to_pci_dev(dev), 1); - retval = resume_common(dev, PM_EVENT_AUTO_RESUME); + retval = resume_common(dev, PMSG_AUTO_RESUME); dev_dbg(dev, "hcd_pci_runtime_resume: %d\n", retval); return retval; } diff --git a/drivers/usb/host/ehci-pci.c b/drivers/usb/host/ehci-pci.c index 17f8b6ea0c35..4370a2744852 100644 --- a/drivers/usb/host/ehci-pci.c +++ b/drivers/usb/host/ehci-pci.c @@ -354,10 +354,11 @@ done: * Also they depend on separate root hub suspend/resume. */ -static int ehci_pci_resume(struct usb_hcd *hcd, bool hibernated) +static int ehci_pci_resume(struct usb_hcd *hcd, pm_message_t msg) { struct ehci_hcd *ehci = hcd_to_ehci(hcd); struct pci_dev *pdev = to_pci_dev(hcd->self.controller); + bool hibernated = (msg.event == PM_EVENT_RESTORE); if (ehci_resume(hcd, hibernated) != 0) (void) ehci_pci_reinit(ehci, pdev); diff --git a/drivers/usb/host/ohci-pci.c b/drivers/usb/host/ohci-pci.c index d7b4f40f9ff4..900ea0d368e0 100644 --- a/drivers/usb/host/ohci-pci.c +++ b/drivers/usb/host/ohci-pci.c @@ -301,6 +301,12 @@ static struct pci_driver ohci_pci_driver = { #endif }; +#ifdef CONFIG_PM +static int ohci_pci_resume(struct usb_hcd *hcd, pm_message_t msg) +{ + return ohci_resume(hcd, msg.event == PM_EVENT_RESTORE); +} +#endif static int __init ohci_pci_init(void) { if (usb_disabled()) @@ -311,7 +317,7 @@ static int __init ohci_pci_init(void) #ifdef CONFIG_PM /* Entries for the PCI suspend/resume callbacks are special */ ohci_pci_hc_driver.pci_suspend = ohci_suspend; - ohci_pci_hc_driver.pci_resume = ohci_resume; + ohci_pci_hc_driver.pci_resume = ohci_pci_resume; #endif return pci_register_driver(&ohci_pci_driver); diff --git a/drivers/usb/host/uhci-pci.c b/drivers/usb/host/uhci-pci.c index 7bd2fddde770..5edf6a08cf82 100644 --- a/drivers/usb/host/uhci-pci.c +++ b/drivers/usb/host/uhci-pci.c @@ -169,7 +169,7 @@ static void uhci_shutdown(struct pci_dev *pdev) #ifdef CONFIG_PM -static int uhci_pci_resume(struct usb_hcd *hcd, bool hibernated); +static int uhci_pci_resume(struct usb_hcd *hcd, pm_message_t state); static int uhci_pci_suspend(struct usb_hcd *hcd, bool do_wakeup) { @@ -204,14 +204,15 @@ done_okay: /* Check for race with a wakeup request */ if (do_wakeup && HCD_WAKEUP_PENDING(hcd)) { - uhci_pci_resume(hcd, false); + uhci_pci_resume(hcd, PMSG_SUSPEND); rc = -EBUSY; } return rc; } -static int uhci_pci_resume(struct usb_hcd *hcd, bool hibernated) +static int uhci_pci_resume(struct usb_hcd *hcd, pm_message_t msg) { + bool hibernated = (msg.event == PM_EVENT_RESTORE); struct uhci_hcd *uhci = hcd_to_uhci(hcd); dev_dbg(uhci_dev(uhci), "%s\n", __func__); diff --git a/drivers/usb/host/xhci-histb.c b/drivers/usb/host/xhci-histb.c index 08369857686e..91ce97821de5 100644 --- a/drivers/usb/host/xhci-histb.c +++ b/drivers/usb/host/xhci-histb.c @@ -367,7 +367,7 @@ static int __maybe_unused xhci_histb_resume(struct device *dev) if (!device_may_wakeup(dev)) xhci_histb_host_enable(histb); - return xhci_resume(xhci, 0); + return xhci_resume(xhci, PMSG_RESUME); } static const struct dev_pm_ops xhci_histb_pm_ops = { diff --git a/drivers/usb/host/xhci-pci.c b/drivers/usb/host/xhci-pci.c index 2aed88c28ef6..959213bed6ef 100644 --- a/drivers/usb/host/xhci-pci.c +++ b/drivers/usb/host/xhci-pci.c @@ -646,7 +646,7 @@ static int xhci_pci_suspend(struct usb_hcd *hcd, bool do_wakeup) return ret; } -static int xhci_pci_resume(struct usb_hcd *hcd, bool hibernated) +static int xhci_pci_resume(struct usb_hcd *hcd, pm_message_t msg) { struct xhci_hcd *xhci = hcd_to_xhci(hcd); struct pci_dev *pdev = to_pci_dev(hcd->self.controller); @@ -681,7 +681,7 @@ static int xhci_pci_resume(struct usb_hcd *hcd, bool hibernated) if (xhci->quirks & XHCI_PME_STUCK_QUIRK) xhci_pme_quirk(hcd); - retval = xhci_resume(xhci, hibernated); + retval = xhci_resume(xhci, msg); return retval; } diff --git a/drivers/usb/host/xhci-plat.c b/drivers/usb/host/xhci-plat.c index 5fb55bf19493..820b77cd7eac 100644 --- a/drivers/usb/host/xhci-plat.c +++ b/drivers/usb/host/xhci-plat.c @@ -464,7 +464,7 @@ static int __maybe_unused xhci_plat_resume(struct device *dev) if (ret) return ret; - ret = xhci_resume(xhci, 0); + ret = xhci_resume(xhci, PMSG_RESUME); if (ret) return ret; @@ -493,7 +493,7 @@ static int __maybe_unused xhci_plat_runtime_resume(struct device *dev) struct usb_hcd *hcd = dev_get_drvdata(dev); struct xhci_hcd *xhci = hcd_to_xhci(hcd); - return xhci_resume(xhci, 0); + return xhci_resume(xhci, PMSG_AUTO_RESUME); } static const struct dev_pm_ops xhci_plat_pm_ops = { diff --git a/drivers/usb/host/xhci-tegra.c b/drivers/usb/host/xhci-tegra.c index 51eabc5e8770..bac9a447fffd 100644 --- a/drivers/usb/host/xhci-tegra.c +++ b/drivers/usb/host/xhci-tegra.c @@ -2085,7 +2085,7 @@ static int tegra_xusb_exit_elpg(struct tegra_xusb *tegra, bool runtime) if (wakeup) tegra_xhci_disable_phy_sleepwalk(tegra); - err = xhci_resume(xhci, 0); + err = xhci_resume(xhci, runtime ? PMSG_AUTO_RESUME : PMSG_RESUME); if (err < 0) { dev_err(tegra->dev, "failed to resume XHCI: %d\n", err); goto disable_phy; diff --git a/drivers/usb/host/xhci.c b/drivers/usb/host/xhci.c index c02ad4f76bb3..e72a04f6153b 100644 --- a/drivers/usb/host/xhci.c +++ b/drivers/usb/host/xhci.c @@ -1124,8 +1124,9 @@ EXPORT_SYMBOL_GPL(xhci_suspend); * This is called when the machine transition from S3/S4 mode. * */ -int xhci_resume(struct xhci_hcd *xhci, bool hibernated) +int xhci_resume(struct xhci_hcd *xhci, pm_message_t msg) { + bool hibernated = (msg.event == PM_EVENT_RESTORE); u32 command, temp = 0; struct usb_hcd *hcd = xhci_to_hcd(xhci); int retval = 0; @@ -1282,7 +1283,7 @@ int xhci_resume(struct xhci_hcd *xhci, bool hibernated) * the first wake signalling failed, give it that chance. */ pending_portevent = xhci_pending_portevent(xhci); - if (!pending_portevent) { + if (!pending_portevent && msg.event == PM_EVENT_AUTO_RESUME) { msleep(120); pending_portevent = xhci_pending_portevent(xhci); } diff --git a/drivers/usb/host/xhci.h b/drivers/usb/host/xhci.h index 1354310cb37b..13f2d8e6a6d2 100644 --- a/drivers/usb/host/xhci.h +++ b/drivers/usb/host/xhci.h @@ -2133,7 +2133,7 @@ int xhci_disable_slot(struct xhci_hcd *xhci, u32 slot_id); int xhci_ext_cap_init(struct xhci_hcd *xhci); int xhci_suspend(struct xhci_hcd *xhci, bool do_wakeup); -int xhci_resume(struct xhci_hcd *xhci, bool hibernated); +int xhci_resume(struct xhci_hcd *xhci, pm_message_t msg); irqreturn_t xhci_irq(struct usb_hcd *hcd); irqreturn_t xhci_msi_irq(int irq, void *hcd); |