diff --git a/Documentation/devicetree/bindings/cnss/cnss-wlan.txt b/Documentation/devicetree/bindings/cnss/cnss-wlan.txt index 06a60e2bfcdf..58e1101cd481 100644 --- a/Documentation/devicetree/bindings/cnss/cnss-wlan.txt +++ b/Documentation/devicetree/bindings/cnss/cnss-wlan.txt @@ -14,6 +14,7 @@ Required properties: - compatible: "qcom,cnss" for QCA6174 device "qcom,cnss-qca6290" for QCA6290 device "qcom,cnss-qca6390" for QCA6390 device + "qcom,cnss-qca-converged" for converged QCA devices - wlan-en-gpio: WLAN_EN GPIO signal specified by the chip specifications - vdd-wlan-supply: phandle to the regulator device tree node - pinctrl-names: Names corresponding to the numbered pinctrl states @@ -35,6 +36,10 @@ Optional properties: - vdd-wlan-xtal-aon-supply: phandle to the LDO-4 regulator. This is needed on platforms where XTAL regulator depends on always on regulator in VDDmin. + - vdd-wlan-ctrl1-supply: phandle to the DBU1 - 1.8V for QCA6595 or 3.3V for + QCA6174 on auto platform. + - vdd-wlan-ctrl2-supply: phandle to the DBU4 - 2.2V for QCA6595 or 3.85V for + QCA6696 on auto platform. - vdd-wlan-core-supply: phandle to the 1.3V CORE regulator for QCA6174 - vdd-wlan-sp2t-supply: phandle to the 2.7V SP2T regulator for QCA6174 - qcom,smmu-s1-enable: Boolean property to decide whether to enable SMMU @@ -56,7 +61,32 @@ Optional properties: WLAN_EN pin is a gpio or fixed regulator. - qcom,mhi: phandle to indicate the device which needs MHI support. - qcom,cap-tsf-gpio: WLAN_TSF_CAPTURED GPIO signal specified by the chip - specifications, should be drived depending on products + specifications, should be drived depending on products. + - cnss-daemon-support: Boolean property to decide whether cnss_daemon + userspace QMI client is supported. + - qcom,converged-dt: Boolean property to decide whether it supports multiple + chips. + - qcom,bus-type: U32 property to specify the bus type, is required when + 'qcom,converged-dt' being present. + - wlan_vregs: String array to decide which vregs are required, is required + when 'qcom,converged-dt' being present. + - -supply: phandle to the regulator device tree node. + optional "supply-name" is "vdd-wlan-rfa". + - qcom,-info: Specifies configuration for supply. Should be + specified in . + - pcie-disable-l1: Boolean property to decide whether to disable PCIe L1 PM. + - pcie-disable-l1ss: Boolean property to decide whether to disable PCIe L1ss. + - qcom,set-wlaon-pwr-ctrl: Boolean property to indicate if set + WLAON_QFPROM_PWR_CTRL_REG register during power on + and off sequences. + +List of chip specific sub nodes: + - chip_cfg@X: represent chip specific configurations + Each sub node has the following required properties: + - wlan_vregs: String array to decide which vregs are required, property + being present but no values means no additional vregs. + - supported-ids: U32 array to decide which device ids are supported by + sub node. Example: @@ -76,3 +106,27 @@ Example: qcom,mhi = <&mhi_wlan>; qcom,cap-tsf-gpio = <&tlmm 126 1>; }; + + qcom,cnss-qca-converged { + compatible = "qcom,cnss-qca-converged"; + qcom,converged-dt; + qcom,bus-type=<0>; + + vdd-wlan-ctrl1-supply = <&vreg_conn_pa>; + vdd-wlan-ctrl2-supply = <&vreg_conn_1p8>; + vdd-wlan-supply = <&vreg_wlan>; + wlan_vregs = "vdd-wlan-ctrl1", "vdd-wlan-ctrl2"; + qcom,vdd-wlan-ctrl1-info = <0 0 0 0>; + qcom,vdd-wlan-ctrl2-info = <0 0 0 0>; + wlan-en-gpio = <&tlmm 169 0>; + pinctrl-names = "wlan_en_active", "wlan_en_sleep"; + pinctrl-0 = <&cnss_wlan_en_active>; + pinctrl-1 = <&cnss_wlan_en_sleep>; + qcom,wlan-rc-num = <0>; + + chip_cfg@1 { + supported-ids = <0x003e>; + wlan_vregs = "vdd-wlan"; + qcom,vdd-wlan-info = <0 0 0 10>; + }; + }; diff --git a/drivers/net/wireless/cnss2/Makefile b/drivers/net/wireless/cnss2/Makefile index ba279b538808..52d0aa0d09c7 100644 --- a/drivers/net/wireless/cnss2/Makefile +++ b/drivers/net/wireless/cnss2/Makefile @@ -6,4 +6,5 @@ cnss2-y += debug.o cnss2-y += pci.o cnss2-y += usb.o cnss2-y += power.o +cnss2-$(CONFIG_CNSS2_DEBUG) += genl.o cnss2-$(CONFIG_CNSS2_QMI) += qmi.o wlan_firmware_service_v01.o diff --git a/drivers/net/wireless/cnss2/bus.c b/drivers/net/wireless/cnss2/bus.c index c8f633ddd569..fe5572a5fbad 100644 --- a/drivers/net/wireless/cnss2/bus.c +++ b/drivers/net/wireless/cnss2/bus.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2018, The Linux Foundation. All rights reserved. +/* Copyright (c) 2018-2020, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -29,40 +29,52 @@ enum cnss_dev_bus_type cnss_get_dev_bus_type(struct device *dev) return CNSS_BUS_NONE; } -enum cnss_dev_bus_type cnss_get_bus_type(unsigned long device_id) +enum cnss_dev_bus_type cnss_get_bus_type(struct cnss_plat_data *plat_priv) { - switch (device_id) { + int ret; + struct device *dev; + u32 bus_type = CNSS_BUS_NONE; + + if (plat_priv->is_converged_dt) { + dev = &plat_priv->plat_dev->dev; + ret = of_property_read_u32(dev->of_node, "qcom,bus-type", + &bus_type); + if (!ret) + cnss_pr_dbg("Got bus type[%u] from dt\n", bus_type); + else + cnss_pr_err("No bus type for converged dt\n"); + + return bus_type; + } + + /* Get bus type according to device id if it's not converged DT */ + switch (plat_priv->device_id) { case QCA6174_DEVICE_ID: - case QCA6290_EMULATION_DEVICE_ID: case QCA6290_DEVICE_ID: - return CNSS_BUS_PCI; + case QCA6390_DEVICE_ID: + case QCN7605_DEVICE_ID: + bus_type = CNSS_BUS_PCI; + break; case QCN7605_COMPOSITE_DEVICE_ID: case QCN7605_STANDALONE_DEVICE_ID: - return CNSS_BUS_USB; + bus_type = CNSS_BUS_USB; + break; default: - cnss_pr_err("Unknown device_id: 0x%lx\n", device_id); - return CNSS_BUS_NONE; + cnss_pr_err("Unknown device: 0x%lx\n", plat_priv->device_id); + break; } + + return bus_type; } bool cnss_bus_req_mem_ind_valid(struct cnss_plat_data *plat_priv) { - if (cnss_get_bus_type(plat_priv->device_id) == CNSS_BUS_USB) + if (cnss_get_bus_type(plat_priv) == CNSS_BUS_USB) return false; else return true; } -bool cnss_bus_dev_cal_rep_valid(struct cnss_plat_data *plat_priv) -{ - bool ret = false; - - if (cnss_get_bus_type(plat_priv->device_id) == CNSS_BUS_USB) - ret = true; - - return ret; -} - void *cnss_bus_dev_to_bus_priv(struct device *dev) { if (!dev) @@ -161,6 +173,37 @@ int cnss_bus_alloc_fw_mem(struct cnss_plat_data *plat_priv) } } +int cnss_bus_alloc_qdss_mem(struct cnss_plat_data *plat_priv) +{ + if (!plat_priv) + return -ENODEV; + + switch (plat_priv->bus_type) { + case CNSS_BUS_PCI: + return cnss_pci_alloc_qdss_mem(plat_priv->bus_priv); + default: + cnss_pr_err("Unsupported bus type: %d\n", + plat_priv->bus_type); + return -EINVAL; + } +} + +void cnss_bus_free_qdss_mem(struct cnss_plat_data *plat_priv) +{ + if (!plat_priv) + return; + + switch (plat_priv->bus_type) { + case CNSS_BUS_PCI: + cnss_pci_free_qdss_mem(plat_priv->bus_priv); + return; + default: + cnss_pr_err("Unsupported bus type: %d\n", + plat_priv->bus_type); + return; + } +} + u32 cnss_bus_get_wake_irq(struct cnss_plat_data *plat_priv) { if (!plat_priv) @@ -388,3 +431,18 @@ int cnss_bus_update_status(struct cnss_plat_data *plat_priv, return -EINVAL; } } + +int cnss_get_msi_assignment(struct cnss_plat_data *plat_priv, + char *msi_name, + int *num_vectors, u32 *user_base_data, + u32 *base_vector) +{ + struct cnss_pci_data *pci_priv; + + pci_priv = plat_priv->bus_priv; + return cnss_get_user_msi_assignment(&pci_priv->pci_dev->dev, + msi_name, + num_vectors, + user_base_data, + base_vector); +} diff --git a/drivers/net/wireless/cnss2/bus.h b/drivers/net/wireless/cnss2/bus.h index 3d3bc2e6fd21..97eebf9f6d89 100644 --- a/drivers/net/wireless/cnss2/bus.h +++ b/drivers/net/wireless/cnss2/bus.h @@ -1,4 +1,4 @@ -/* Copyright (c) 2018, The Linux Foundation. All rights reserved. +/* Copyright (c) 2018-2020, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -22,27 +22,28 @@ #define QCA6174_REV3_2_VERSION 0x5030000 #define QCA6290_VENDOR_ID 0x17CB #define QCA6290_DEVICE_ID 0x1100 -#define QCA6290_EMULATION_VENDOR_ID 0x168C -#define QCA6290_EMULATION_DEVICE_ID 0xABCD #define QCA6390_VENDOR_ID 0x17CB -#define QCA6390_EMULATION_DEVICE_ID 0x0108 #define QCA6390_DEVICE_ID 0x1101 +#define QCN7605_VENDOR_ID 0x17CB +#define QCN7605_DEVICE_ID 0x1102 #define QCN7605_USB_VENDOR_ID 0x05C6 -#define QCN7605_COMPOSITE_DEVICE_ID QCN7605_COMPOSITE_PRODUCT_ID -#define QCN7605_STANDALONE_DEVICE_ID QCN7605_STANDALONE_PRODUCT_ID - #define QCN7605_STANDALONE_PRODUCT_ID 0x9900 #define QCN7605_COMPOSITE_PRODUCT_ID 0x9901 +#define QCN7605_COMPOSITE_DEVICE_ID QCN7605_COMPOSITE_PRODUCT_ID +#define QCN7605_STANDALONE_DEVICE_ID QCN7605_STANDALONE_PRODUCT_ID + enum cnss_dev_bus_type cnss_get_dev_bus_type(struct device *dev); -enum cnss_dev_bus_type cnss_get_bus_type(unsigned long device_id); +enum cnss_dev_bus_type cnss_get_bus_type(struct cnss_plat_data *plat_priv); void *cnss_bus_dev_to_bus_priv(struct device *dev); struct cnss_plat_data *cnss_bus_dev_to_plat_priv(struct device *dev); int cnss_bus_init(struct cnss_plat_data *plat_priv); void cnss_bus_deinit(struct cnss_plat_data *plat_priv); int cnss_bus_load_m3(struct cnss_plat_data *plat_priv); int cnss_bus_alloc_fw_mem(struct cnss_plat_data *plat_priv); +int cnss_bus_alloc_qdss_mem(struct cnss_plat_data *plat_priv); +void cnss_bus_free_qdss_mem(struct cnss_plat_data *plat_priv); u32 cnss_bus_get_wake_irq(struct cnss_plat_data *plat_priv); int cnss_bus_force_fw_assert_hdlr(struct cnss_plat_data *plat_priv); void cnss_bus_fw_boot_timeout_hdlr(unsigned long data); @@ -60,8 +61,10 @@ int cnss_bus_call_driver_modem_status(struct cnss_plat_data *plat_priv, int modem_current_status); int cnss_bus_update_status(struct cnss_plat_data *plat_priv, enum cnss_driver_status status); -int cnss_bus_recovery_update_status(struct cnss_plat_data *plat_priv); bool cnss_bus_req_mem_ind_valid(struct cnss_plat_data *plat_priv); -bool cnss_bus_dev_cal_rep_valid(struct cnss_plat_data *plat_priv); - +int cnss_get_msi_assignment(struct cnss_plat_data *plat_priv, + char *msi_name, + int *num_vectors, + u32 *user_base_data, + u32 *base_vector); #endif /* _CNSS_BUS_H */ diff --git a/drivers/net/wireless/cnss2/debug.c b/drivers/net/wireless/cnss2/debug.c index 30e37a08d291..e61ae0266c4a 100644 --- a/drivers/net/wireless/cnss2/debug.c +++ b/drivers/net/wireless/cnss2/debug.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2016-2018, The Linux Foundation. All rights reserved. +/* Copyright (c) 2016-2020, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -85,6 +85,12 @@ static int cnss_stats_show_state(struct seq_file *s, case CNSS_DRIVER_UNLOADING: seq_puts(s, "DRIVER_UNLOADING"); continue; + case CNSS_DRIVER_IDLE_RESTART: + seq_puts(s, "IDLE_RESTART"); + continue; + case CNSS_DRIVER_IDLE_SHUTDOWN: + seq_puts(s, "IDLE_SHUTDOWN"); + continue; case CNSS_DRIVER_PROBED: seq_puts(s, "DRIVER_PROBED"); continue; @@ -100,6 +106,9 @@ static int cnss_stats_show_state(struct seq_file *s, case CNSS_DRIVER_DEBUG: seq_puts(s, "DRIVER_DEBUG"); continue; + case CNSS_IN_SUSPEND_RESUME: + seq_puts(s, "IN_SUSPEND_RESUME"); + continue; } seq_printf(s, "UNKNOWN-%d", i); @@ -504,6 +513,145 @@ static const struct file_operations cnss_runtime_pm_debug_fops = { .llseek = seq_lseek, }; +static ssize_t cnss_control_params_debug_write(struct file *fp, + const char __user *user_buf, + size_t count, loff_t *off) +{ + struct cnss_plat_data *plat_priv = + ((struct seq_file *)fp->private_data)->private; + char buf[64]; + char *sptr, *token; + char *cmd; + u32 val; + unsigned int len = 0; + const char *delim = " "; + + if (!plat_priv) + return -ENODEV; + + len = min(count, sizeof(buf) - 1); + if (copy_from_user(buf, user_buf, len)) + return -EFAULT; + + buf[len] = '\0'; + sptr = buf; + + token = strsep(&sptr, delim); + if (!token) + return -EINVAL; + if (!sptr) + return -EINVAL; + cmd = token; + + token = strsep(&sptr, delim); + if (!token) + return -EINVAL; + if (kstrtou32(token, 0, &val)) + return -EINVAL; + + if (strcmp(cmd, "quirks") == 0) + plat_priv->ctrl_params.quirks = val; + else if (strcmp(cmd, "mhi_timeout") == 0) + plat_priv->ctrl_params.mhi_timeout = val; + else if (strcmp(cmd, "qmi_timeout") == 0) + plat_priv->ctrl_params.qmi_timeout = val; + else if (strcmp(cmd, "bdf_type") == 0) + plat_priv->ctrl_params.bdf_type = val; + else + return -EINVAL; + + return count; +} + +static int cnss_show_quirks_state(struct seq_file *s, + struct cnss_plat_data *plat_priv) +{ + enum cnss_debug_quirks i; + int skip = 0; + unsigned long state; + + seq_printf(s, "quirks: 0x%lx (", plat_priv->ctrl_params.quirks); + for (i = 0, state = plat_priv->ctrl_params.quirks; + state != 0; state >>= 1, i++) { + if (!(state & 0x1)) + continue; + if (skip++) + seq_puts(s, " | "); + + switch (i) { + case LINK_DOWN_SELF_RECOVERY: + seq_puts(s, "LINK_DOWN_SELF_RECOVERY"); + continue; + case SKIP_DEVICE_BOOT: + seq_puts(s, "SKIP_DEVICE_BOOT"); + continue; + case USE_CORE_ONLY_FW: + seq_puts(s, "USE_CORE_ONLY_FW"); + continue; + case SKIP_RECOVERY: + seq_puts(s, "SKIP_RECOVERY"); + continue; + case QMI_BYPASS: + seq_puts(s, "QMI_BYPASS"); + continue; + case ENABLE_WALTEST: + seq_puts(s, "WALTEST"); + continue; + case ENABLE_PCI_LINK_DOWN_PANIC: + seq_puts(s, "PCI_LINK_DOWN_PANIC"); + continue; + case FBC_BYPASS: + seq_puts(s, "FBC_BYPASS"); + continue; + case ENABLE_DAEMON_SUPPORT: + seq_puts(s, "DAEMON_SUPPORT"); + continue; + case IGNORE_PCI_LINK_FAILURE: + seq_puts(s, "IGNORE_PCI_LINK_FAILURE"); + continue; + } + + seq_printf(s, "UNKNOWN-%d", i); + } + seq_puts(s, ")\n"); + return 0; +} + +static int cnss_control_params_debug_show(struct seq_file *s, void *data) +{ + struct cnss_plat_data *cnss_priv = s->private; + + seq_puts(s, "\nUsage: echo > /cnss/control_params\n"); + seq_puts(s, " can be one of below:\n"); + seq_puts(s, "quirks: Debug quirks for driver\n"); + seq_puts(s, "mhi_timeout: Timeout for MHI operation in milliseconds\n"); + seq_puts(s, "qmi_timeout: Timeout for QMI message in milliseconds\n"); + seq_puts(s, "bdf_type: Type of board data file to be downloaded\n"); + + seq_puts(s, "\nCurrent value:\n"); + cnss_show_quirks_state(s, cnss_priv); + seq_printf(s, "mhi_timeout: %u\n", cnss_priv->ctrl_params.mhi_timeout); + seq_printf(s, "qmi_timeout: %u\n", cnss_priv->ctrl_params.qmi_timeout); + seq_printf(s, "bdf_type: %u\n", cnss_priv->ctrl_params.bdf_type); + + return 0; +} + +static int cnss_control_params_debug_open(struct inode *inode, + struct file *file) +{ + return single_open(file, cnss_control_params_debug_show, + inode->i_private); +} + +static const struct file_operations cnss_control_params_debug_fops = { + .read = seq_read, + .write = cnss_control_params_debug_write, + .open = cnss_control_params_debug_open, + .owner = THIS_MODULE, + .llseek = seq_lseek, +}; + #ifdef CONFIG_CNSS2_DEBUG static int cnss_create_debug_only_node(struct cnss_plat_data *plat_priv) { @@ -517,6 +665,8 @@ static int cnss_create_debug_only_node(struct cnss_plat_data *plat_priv) &cnss_reg_write_debug_fops); debugfs_create_file("runtime_pm", 0600, root_dentry, plat_priv, &cnss_runtime_pm_debug_fops); + debugfs_create_file("control_params", 0600, root_dentry, plat_priv, + &cnss_control_params_debug_fops); return 0; } diff --git a/drivers/net/wireless/cnss2/debug.h b/drivers/net/wireless/cnss2/debug.h index bf2e755f8ee0..8b555cc4ba46 100644 --- a/drivers/net/wireless/cnss2/debug.h +++ b/drivers/net/wireless/cnss2/debug.h @@ -1,4 +1,4 @@ -/* Copyright (c) 2016-2018, The Linux Foundation. All rights reserved. +/* Copyright (c) 2016-2020, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -63,6 +63,9 @@ extern void *cnss_ipc_log_context; } while (0) #endif +#define cnss_fatal_err(_fmt, ...) \ + cnss_pr_err("fatal: " _fmt, ##__VA_ARGS__) + int cnss_debug_init(void); void cnss_debug_deinit(void); int cnss_debugfs_create(struct cnss_plat_data *plat_priv); diff --git a/drivers/net/wireless/cnss2/genl.c b/drivers/net/wireless/cnss2/genl.c new file mode 100644 index 000000000000..2c1258745c5b --- /dev/null +++ b/drivers/net/wireless/cnss2/genl.c @@ -0,0 +1,213 @@ +/* Copyright (c) 2019-2020, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#define pr_fmt(fmt) "cnss_genl: " fmt + +#include +#include +#include +#include + +#include "main.h" +#include "debug.h" +#include "genl.h" + +#define CNSS_GENL_FAMILY_NAME "cnss-genl" +#define CNSS_GENL_MCAST_GROUP_NAME "cnss-genl-grp" +#define CNSS_GENL_VERSION 1 +#define CNSS_GENL_DATA_LEN_MAX (15 * 1024) +#define CNSS_GENL_STR_LEN_MAX 16 + +enum { + CNSS_GENL_ATTR_MSG_UNSPEC, + CNSS_GENL_ATTR_MSG_TYPE, + CNSS_GENL_ATTR_MSG_FILE_NAME, + CNSS_GENL_ATTR_MSG_TOTAL_SIZE, + CNSS_GENL_ATTR_MSG_SEG_ID, + CNSS_GENL_ATTR_MSG_END, + CNSS_GENL_ATTR_MSG_DATA_LEN, + CNSS_GENL_ATTR_MSG_DATA, + __CNSS_GENL_ATTR_MAX, +}; + +#define CNSS_GENL_ATTR_MAX (__CNSS_GENL_ATTR_MAX - 1) + +enum { + CNSS_GENL_CMD_UNSPEC, + CNSS_GENL_CMD_MSG, + __CNSS_GENL_CMD_MAX, +}; + +#define CNSS_GENL_CMD_MAX (__CNSS_GENL_CMD_MAX - 1) + +static struct nla_policy cnss_genl_msg_policy[CNSS_GENL_ATTR_MAX + 1] = { + [CNSS_GENL_ATTR_MSG_TYPE] = { .type = NLA_U8 }, + [CNSS_GENL_ATTR_MSG_FILE_NAME] = { .type = NLA_NUL_STRING, + .len = CNSS_GENL_STR_LEN_MAX }, + [CNSS_GENL_ATTR_MSG_TOTAL_SIZE] = { .type = NLA_U32 }, + [CNSS_GENL_ATTR_MSG_SEG_ID] = { .type = NLA_U32 }, + [CNSS_GENL_ATTR_MSG_END] = { .type = NLA_U8 }, + [CNSS_GENL_ATTR_MSG_DATA_LEN] = { .type = NLA_U32 }, + [CNSS_GENL_ATTR_MSG_DATA] = { .type = NLA_BINARY, + .len = CNSS_GENL_DATA_LEN_MAX }, +}; + +static int cnss_genl_process_msg(struct sk_buff *skb, struct genl_info *info) +{ + return 0; +} + +static struct genl_ops cnss_genl_ops[] = { + { + .cmd = CNSS_GENL_CMD_MSG, + .policy = cnss_genl_msg_policy, + .doit = cnss_genl_process_msg, + }, +}; + +static struct genl_multicast_group cnss_genl_mcast_grp[] = { + { + .name = CNSS_GENL_MCAST_GROUP_NAME, + }, +}; + +static struct genl_family cnss_genl_family = { + .id = 0, + .hdrsize = 0, + .name = CNSS_GENL_FAMILY_NAME, + .version = CNSS_GENL_VERSION, + .maxattr = CNSS_GENL_ATTR_MAX, + .module = THIS_MODULE, + .ops = cnss_genl_ops, + .n_ops = ARRAY_SIZE(cnss_genl_ops), + .mcgrps = cnss_genl_mcast_grp, + .n_mcgrps = ARRAY_SIZE(cnss_genl_mcast_grp), +}; + +static int cnss_genl_send_data(u8 type, char *file_name, u32 total_size, + u32 seg_id, u8 end, u32 data_len, u8 *msg_buff) +{ + struct sk_buff *skb = NULL; + void *msg_header = NULL; + int ret = 0; + char filename[CNSS_GENL_STR_LEN_MAX + 1]; + + cnss_pr_dbg("type: %u, file_name %s, total_size: %x, seg_id %u, end %u, data_len %u\n", + type, file_name, total_size, seg_id, end, data_len); + + if (!file_name) + strlcpy(filename, "default", sizeof(filename)); + else + strlcpy(filename, file_name, sizeof(filename)); + + skb = genlmsg_new(NLMSG_HDRLEN + + nla_total_size(sizeof(type)) + + nla_total_size(strlen(filename) + 1) + + nla_total_size(sizeof(total_size)) + + nla_total_size(sizeof(seg_id)) + + nla_total_size(sizeof(end)) + + nla_total_size(sizeof(data_len)) + + nla_total_size(data_len), GFP_KERNEL); + if (!skb) + return -ENOMEM; + + msg_header = genlmsg_put(skb, 0, 0, + &cnss_genl_family, 0, + CNSS_GENL_CMD_MSG); + if (!msg_header) { + ret = -ENOMEM; + goto fail; + } + + ret = nla_put_u8(skb, CNSS_GENL_ATTR_MSG_TYPE, type); + if (ret < 0) + goto fail; + ret = nla_put_string(skb, CNSS_GENL_ATTR_MSG_FILE_NAME, filename); + if (ret < 0) + goto fail; + ret = nla_put_u32(skb, CNSS_GENL_ATTR_MSG_TOTAL_SIZE, total_size); + if (ret < 0) + goto fail; + ret = nla_put_u32(skb, CNSS_GENL_ATTR_MSG_SEG_ID, seg_id); + if (ret < 0) + goto fail; + ret = nla_put_u8(skb, CNSS_GENL_ATTR_MSG_END, end); + if (ret < 0) + goto fail; + ret = nla_put_u32(skb, CNSS_GENL_ATTR_MSG_DATA_LEN, data_len); + if (ret < 0) + goto fail; + ret = nla_put(skb, CNSS_GENL_ATTR_MSG_DATA, data_len, msg_buff); + if (ret < 0) + goto fail; + + genlmsg_end(skb, msg_header); + ret = genlmsg_multicast(&cnss_genl_family, skb, 0, 0, GFP_KERNEL); + if (ret < 0) + cnss_pr_err("Fail to send genl msg: %d\n", ret); + + return ret; +fail: + cnss_pr_err("Fail to generate genl msg: %d\n", ret); + if (skb) + nlmsg_free(skb); + return ret; +} + +int cnss_genl_send_msg(void *buff, u8 type, char *file_name, u32 total_size) +{ + int ret = 0; + u8 *msg_buff = buff; + u32 remaining = total_size; + u32 seg_id = 0; + u32 data_len = 0; + u8 end = 0; + + cnss_pr_dbg("type: %u, total_size: %x\n", type, total_size); + + while (remaining) { + if (remaining > CNSS_GENL_DATA_LEN_MAX) { + data_len = CNSS_GENL_DATA_LEN_MAX; + } else { + data_len = remaining; + end = 1; + } + ret = cnss_genl_send_data(type, file_name, total_size, + seg_id, end, data_len, msg_buff); + if (ret < 0) { + cnss_pr_err("fail to send genl data, ret %d\n", ret); + return ret; + } + + remaining -= data_len; + msg_buff += data_len; + seg_id++; + } + + return ret; +} + +int cnss_genl_init(void) +{ + int ret = 0; + + ret = genl_register_family(&cnss_genl_family); + if (ret != 0) + cnss_pr_err("genl_register_family fail: %d\n", ret); + + return ret; +} + +void cnss_genl_exit(void) +{ + genl_unregister_family(&cnss_genl_family); +} diff --git a/drivers/net/wireless/cnss2/genl.h b/drivers/net/wireless/cnss2/genl.h new file mode 100644 index 000000000000..fc9450913cd1 --- /dev/null +++ b/drivers/net/wireless/cnss2/genl.h @@ -0,0 +1,43 @@ +/* Copyright (c) 2019-2020, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef __CNSS_GENL_H__ +#define __CNSS_GENL_H__ + +enum cnss_genl_msg_type { + CNSS_GENL_MSG_TYPE_UNSPEC, + CNSS_GENL_MSG_TYPE_QDSS, +}; + +#ifdef CONFIG_CNSS2_DEBUG +int cnss_genl_init(void); +void cnss_genl_exit(void); +int cnss_genl_send_msg(void *buff, u8 type, + char *file_name, u32 total_size); +#else +static inline int cnss_genl_init(void) +{ + return 0; +} + +static inline void cnss_genl_exit(void) +{ +} + +static inline int cnss_genl_send_msg(void *buff, u8 type, + char *file_name, u32 total_size) +{ + return 0; +} +#endif + +#endif diff --git a/drivers/net/wireless/cnss2/main.c b/drivers/net/wireless/cnss2/main.c index c115a0ea3ebf..dd0066abe35b 100644 --- a/drivers/net/wireless/cnss2/main.c +++ b/drivers/net/wireless/cnss2/main.c @@ -25,6 +25,7 @@ #include "main.h" #include "bus.h" #include "debug.h" +#include "genl.h" #define CNSS_DUMP_FORMAT_VER 0x11 #define CNSS_DUMP_FORMAT_VER_V2 0x22 @@ -38,28 +39,18 @@ #define FW_ASSERT_TIMEOUT 5000 #define CNSS_EVENT_PENDING 2989 +#ifdef CONFIG_CNSS_EMULATION +#define CNSS_MHI_TIMEOUT_DEFAULT 90000 +#else +#define CNSS_MHI_TIMEOUT_DEFAULT 0 +#endif +#define CNSS_QMI_TIMEOUT_DEFAULT 10000 +#define CNSS_BDF_TYPE_DEFAULT CNSS_BDF_ELF + static struct cnss_plat_data *plat_env; static DECLARE_RWSEM(cnss_pm_sem); -static bool qmi_bypass; -#ifdef CONFIG_CNSS2_DEBUG -module_param(qmi_bypass, bool, 0600); -MODULE_PARM_DESC(qmi_bypass, "Bypass QMI from platform driver"); -#endif - -static bool enable_waltest; -#ifdef CONFIG_CNSS2_DEBUG -module_param(enable_waltest, bool, 0600); -MODULE_PARM_DESC(enable_waltest, "Enable to handle firmware waltest"); -#endif - -unsigned long quirks; -#ifdef CONFIG_CNSS2_DEBUG -module_param(quirks, ulong, 0600); -MODULE_PARM_DESC(quirks, "Debug quirks for the driver"); -#endif - static struct cnss_fw_files FW_FILES_QCA6174_FW_3_0 = { "qwlan30.bin", "bdwlan30.bin", "otp30.bin", "utf30.bin", "utfbd30.bin", "epping30.bin", "evicted30.bin" @@ -185,9 +176,11 @@ int cnss_request_bus_bandwidth(struct device *dev, int bandwidth) switch (bandwidth) { case CNSS_BUS_WIDTH_NONE: + case CNSS_BUS_WIDTH_IDLE: case CNSS_BUS_WIDTH_LOW: case CNSS_BUS_WIDTH_MEDIUM: case CNSS_BUS_WIDTH_HIGH: + case CNSS_BUS_WIDTH_VERY_HIGH: ret = msm_bus_scale_client_update_request( bus_bw_info->bus_client, bandwidth); if (!ret) @@ -252,10 +245,13 @@ int cnss_wlan_enable(struct device *dev, if (!plat_priv) return -ENODEV; + if (!plat_priv) + return -ENODEV; + if (plat_priv->device_id == QCA6174_DEVICE_ID) return 0; - if (qmi_bypass) + if (test_bit(QMI_BYPASS, &plat_priv->ctrl_params.quirks)) return 0; if (!config || !host_version) { @@ -269,6 +265,9 @@ int cnss_wlan_enable(struct device *dev, if (mode == CNSS_WALTEST || mode == CNSS_CCPM) goto skip_cfg; + if (plat_priv->device_id == QCN7605_DEVICE_ID) + config->send_msi_ce = true; + ret = cnss_wlfw_wlan_cfg_send_sync(plat_priv, config, host_version); if (ret) goto out; @@ -286,17 +285,19 @@ int cnss_wlan_disable(struct device *dev, enum cnss_driver_mode mode) if (!plat_priv) return -ENODEV; + if (!plat_priv) + return -ENODEV; + if (plat_priv->device_id == QCA6174_DEVICE_ID) return 0; - if (qmi_bypass) + if (test_bit(QMI_BYPASS, &plat_priv->ctrl_params.quirks)) return 0; return cnss_wlfw_wlan_mode_send_sync(plat_priv, CNSS_OFF); } EXPORT_SYMBOL(cnss_wlan_disable); -#ifdef CONFIG_CNSS2_DEBUG int cnss_athdiag_read(struct device *dev, u32 offset, u32 mem_type, u32 data_len, u8 *output) { @@ -354,21 +355,6 @@ out: return ret; } EXPORT_SYMBOL(cnss_athdiag_write); -#else -int cnss_athdiag_read(struct device *dev, u32 offset, u32 mem_type, - u32 data_len, u8 *output) -{ - return -EPERM; -} -EXPORT_SYMBOL(cnss_athdiag_read); - -int cnss_athdiag_write(struct device *dev, u32 offset, u32 mem_type, - u32 data_len, u8 *input) -{ - return -EPERM; -} -EXPORT_SYMBOL(cnss_athdiag_write); -#endif int cnss_set_fw_log_mode(struct device *dev, u8 fw_log_mode) { @@ -376,6 +362,9 @@ int cnss_set_fw_log_mode(struct device *dev, u8 fw_log_mode) if (!plat_priv) return -ENODEV; + if (!plat_priv) + return -ENODEV; + if (plat_priv->device_id == QCA6174_DEVICE_ID) return 0; @@ -383,16 +372,6 @@ int cnss_set_fw_log_mode(struct device *dev, u8 fw_log_mode) } EXPORT_SYMBOL(cnss_set_fw_log_mode); -bool *cnss_get_qmi_bypass(void) -{ - return &qmi_bypass; -} - -unsigned long *cnss_get_debug_quirks(void) -{ - return &quirks; -} - static int cnss_fw_mem_ready_hdlr(struct cnss_plat_data *plat_priv) { int ret = 0; @@ -406,10 +385,19 @@ static int cnss_fw_mem_ready_hdlr(struct cnss_plat_data *plat_priv) if (ret) goto out; - ret = cnss_wlfw_bdf_dnld_send_sync(plat_priv); + cnss_wlfw_bdf_dnld_send_sync(plat_priv, CNSS_BDF_REGDB); + + if (plat_priv->device_id == QCN7605_DEVICE_ID) + plat_priv->ctrl_params.bdf_type = CNSS_BDF_BIN; + + ret = cnss_wlfw_bdf_dnld_send_sync(plat_priv, + plat_priv->ctrl_params.bdf_type); if (ret) goto out; + if (plat_priv->device_id == QCN7605_DEVICE_ID) + return 0; + ret = cnss_bus_load_m3(plat_priv); if (ret) goto out; @@ -439,17 +427,14 @@ static int cnss_fw_ready_hdlr(struct cnss_plat_data *plat_priv) clear_bit(CNSS_DRIVER_RECOVERY, &plat_priv->driver_state); } - if (enable_waltest) { + if (test_bit(ENABLE_WALTEST, &plat_priv->ctrl_params.quirks)) { ret = cnss_wlfw_wlan_mode_send_sync(plat_priv, CNSS_WALTEST); } else if (test_bit(CNSS_COLD_BOOT_CAL, &plat_priv->driver_state)) { ret = cnss_wlfw_wlan_mode_send_sync(plat_priv, CNSS_CALIBRATION); - } else if (test_bit(CNSS_DRIVER_LOADING, &plat_priv->driver_state) || - test_bit(CNSS_DRIVER_RECOVERY, &plat_priv->driver_state)) { - ret = cnss_bus_call_driver_probe(plat_priv); } else { - complete(&plat_priv->power_up_complete); + ret = cnss_bus_call_driver_probe(plat_priv); } if (ret && test_bit(CNSS_DEV_ERR_NOTIFY, &plat_priv->driver_state)) @@ -511,6 +496,16 @@ static char *cnss_driver_event_to_str(enum cnss_driver_event_type type) return "POWER_UP"; case CNSS_DRIVER_EVENT_POWER_DOWN: return "POWER_DOWN"; + case CNSS_DRIVER_EVENT_IDLE_RESTART: + return "IDLE_RESTART"; + case CNSS_DRIVER_EVENT_IDLE_SHUTDOWN: + return "IDLE_SHUTDOWN"; + case CNSS_DRIVER_EVENT_QDSS_TRACE_REQ_MEM: + return "QDSS_TRACE_REQ_MEM"; + case CNSS_DRIVER_EVENT_QDSS_TRACE_SAVE: + return "QDSS_TRACE_SAVE"; + case CNSS_DRIVER_EVENT_QDSS_TRACE_FREE: + return "QDSS_TRACE_FREE"; case CNSS_DRIVER_EVENT_MAX: return "EVENT_MAX"; } @@ -590,7 +585,14 @@ out: unsigned int cnss_get_boot_timeout(struct device *dev) { - return cnss_get_qmi_timeout(); + struct cnss_plat_data *plat_priv = cnss_bus_dev_to_plat_priv(dev); + + if (!plat_priv) { + cnss_pr_err("plat_priv is NULL\n"); + return 0; + } + + return cnss_get_qmi_timeout(plat_priv); } EXPORT_SYMBOL(cnss_get_boot_timeout); @@ -653,13 +655,79 @@ EXPORT_SYMBOL(cnss_power_down); int cnss_idle_restart(struct device *dev) { + struct cnss_plat_data *plat_priv = cnss_bus_dev_to_plat_priv(dev); + unsigned int timeout; + int ret = 0; + + if (!plat_priv) { + cnss_pr_err("plat_priv is NULL\n"); + return -ENODEV; + } + + cnss_pr_dbg("Doing idle restart\n"); + + ret = cnss_driver_event_post(plat_priv, + CNSS_DRIVER_EVENT_IDLE_RESTART, + CNSS_EVENT_SYNC_UNINTERRUPTIBLE, NULL); + if (ret) + goto out; + + if (plat_priv->device_id == QCA6174_DEVICE_ID) { + ret = cnss_bus_call_driver_probe(plat_priv); + goto out; + } + + timeout = cnss_get_boot_timeout(dev); + + reinit_completion(&plat_priv->power_up_complete); + ret = wait_for_completion_timeout(&plat_priv->power_up_complete, + msecs_to_jiffies(timeout) << 2); + if (!ret) { + cnss_pr_err("Timeout waiting for idle restart to complete\n"); + ret = -EAGAIN; + goto out; + } + return 0; + +out: + return ret; } EXPORT_SYMBOL(cnss_idle_restart); int cnss_idle_shutdown(struct device *dev) { - return 0; + struct cnss_plat_data *plat_priv = cnss_bus_dev_to_plat_priv(dev); + int ret; + + if (!plat_priv) { + cnss_pr_err("plat_priv is NULL\n"); + return -ENODEV; + } + + if (test_bit(CNSS_IN_SUSPEND_RESUME, &plat_priv->driver_state)) { + cnss_pr_dbg("System suspend or resume in progress, ignore idle shutdown\n"); + return -EAGAIN; + } + + cnss_pr_dbg("Doing idle shutdown\n"); + + if (!test_bit(CNSS_DRIVER_RECOVERY, &plat_priv->driver_state) && + !test_bit(CNSS_DEV_ERR_NOTIFY, &plat_priv->driver_state)) + goto skip_wait; + + reinit_completion(&plat_priv->recovery_complete); + ret = wait_for_completion_timeout(&plat_priv->recovery_complete, + RECOVERY_TIMEOUT); + if (!ret) { + cnss_pr_err("Timeout waiting for recovery to complete\n"); + CNSS_ASSERT(0); + } + +skip_wait: + return cnss_driver_event_post(plat_priv, + CNSS_DRIVER_EVENT_IDLE_SHUTDOWN, + CNSS_EVENT_SYNC_UNINTERRUPTIBLE, NULL); } EXPORT_SYMBOL(cnss_idle_shutdown); @@ -686,6 +754,8 @@ out: static void cnss_put_resources(struct cnss_plat_data *plat_priv) { + cnss_put_pinctrl(plat_priv); + cnss_put_vreg(plat_priv); } static int cnss_modem_notifier_nb(struct notifier_block *nb, @@ -780,9 +850,9 @@ static void cnss_unregister_esoc(struct cnss_plat_data *plat_priv) esoc_info = &plat_priv->esoc_info; if (esoc_info->notify_modem_status) - subsys_notif_unregister_notifier(esoc_info-> - modem_notify_handler, - &plat_priv->modem_nb); + subsys_notif_unregister_notifier( + esoc_info->modem_notify_handler, + &plat_priv->modem_nb); if (esoc_info->esoc_desc) devm_unregister_esoc_client(dev, esoc_info->esoc_desc); } @@ -803,7 +873,7 @@ static int cnss_subsys_powerup(const struct subsys_desc *subsys_desc) } if (!plat_priv->driver_state) { - cnss_pr_dbg("Powerup is ignored\n"); + cnss_pr_dbg("subsys powerup is ignored\n"); return 0; } @@ -827,7 +897,7 @@ static int cnss_subsys_shutdown(const struct subsys_desc *subsys_desc, } if (!plat_priv->driver_state) { - cnss_pr_dbg("shutdown is ignored\n"); + cnss_pr_dbg("subsys shutdown is ignored\n"); return 0; } @@ -921,14 +991,15 @@ static int cnss_do_recovery(struct cnss_plat_data *plat_priv, if (plat_priv->device_id == QCA6174_DEVICE_ID) goto self_recovery; - if (test_bit(SKIP_RECOVERY, &quirks)) { + if (test_bit(SKIP_RECOVERY, &plat_priv->ctrl_params.quirks)) { cnss_pr_dbg("Skip device recovery\n"); return 0; } switch (reason) { case CNSS_REASON_LINK_DOWN: - if (test_bit(LINK_DOWN_SELF_RECOVERY, &quirks)) + if (test_bit(LINK_DOWN_SELF_RECOVERY, + &plat_priv->ctrl_params.quirks)) goto self_recovery; break; case CNSS_REASON_RDDM: @@ -980,16 +1051,19 @@ static int cnss_driver_recovery_hdlr(struct cnss_plat_data *plat_priv, goto out; } - if (test_bit(CNSS_DRIVER_UNLOADING, &plat_priv->driver_state)) { - cnss_pr_err("Driver unload is in progress, ignore recovery\n"); + if (test_bit(CNSS_DRIVER_UNLOADING, &plat_priv->driver_state) || + test_bit(CNSS_DRIVER_IDLE_SHUTDOWN, &plat_priv->driver_state)) { + cnss_pr_err("Driver unload or idle shutdown is in progress, ignore recovery\n"); ret = -EINVAL; goto out; } switch (plat_priv->device_id) { case QCA6174_DEVICE_ID: - if (test_bit(CNSS_DRIVER_LOADING, &plat_priv->driver_state)) { - cnss_pr_err("Driver load is in progress, ignore recovery\n"); + if (test_bit(CNSS_DRIVER_LOADING, &plat_priv->driver_state) || + test_bit(CNSS_DRIVER_IDLE_RESTART, + &plat_priv->driver_state)) { + cnss_pr_err("Driver load or idle restart is in progress, ignore recovery\n"); ret = -EINVAL; goto out; } @@ -1027,6 +1101,12 @@ void cnss_schedule_recovery(struct device *dev, cnss_bus_update_status(plat_priv, CNSS_FW_DOWN); + if (test_bit(CNSS_DRIVER_UNLOADING, &plat_priv->driver_state) || + test_bit(CNSS_DRIVER_IDLE_SHUTDOWN, &plat_priv->driver_state)) { + cnss_pr_dbg("Driver unload or idle shutdown is in progress, ignore schedule recovery\n"); + return; + } + if (in_interrupt() || irqs_disabled()) gfp = GFP_ATOMIC; @@ -1055,18 +1135,45 @@ int cnss_force_fw_assert(struct device *dev) return -EOPNOTSUPP; } + if (cnss_pci_is_device_down(dev)) { + cnss_pr_info("Device is already in bad state, ignore force assert\n"); + return 0; + } + if (test_bit(CNSS_DRIVER_RECOVERY, &plat_priv->driver_state)) { cnss_pr_info("Recovery is already in progress, ignore forced FW assert\n"); return 0; } - cnss_driver_event_post(plat_priv, - CNSS_DRIVER_EVENT_FORCE_FW_ASSERT, - 0, NULL); + if (in_interrupt() || irqs_disabled()) + cnss_driver_event_post(plat_priv, + CNSS_DRIVER_EVENT_FORCE_FW_ASSERT, + 0, NULL); + else + cnss_bus_force_fw_assert_hdlr(plat_priv); return 0; } EXPORT_SYMBOL(cnss_force_fw_assert); +static int cnss_wlfw_server_arrive_hdlr(struct cnss_plat_data *plat_priv) +{ + int ret; + unsigned int bdf_type; + + ret = cnss_wlfw_server_arrive(plat_priv); + if (ret) + goto out; + + if (!cnss_bus_req_mem_ind_valid(plat_priv)) { + ret = cnss_wlfw_tgt_cap_send_sync(plat_priv); + if (ret) + goto out; + bdf_type = plat_priv->ctrl_params.bdf_type; + ret = cnss_wlfw_bdf_dnld_send_sync(plat_priv, bdf_type); + } +out: + return ret; +} int cnss_force_collect_rddm(struct device *dev) { @@ -1083,14 +1190,27 @@ int cnss_force_collect_rddm(struct device *dev) return -EOPNOTSUPP; } + if (cnss_pci_is_device_down(dev)) { + cnss_pr_info("Device is already in bad state, ignore force collect rddm\n"); + return 0; + } + if (test_bit(CNSS_DRIVER_RECOVERY, &plat_priv->driver_state)) { cnss_pr_info("Recovery is already in progress, ignore forced collect rddm\n"); return 0; } - cnss_driver_event_post(plat_priv, - CNSS_DRIVER_EVENT_FORCE_FW_ASSERT, - 0, NULL); + if (test_bit(CNSS_DRIVER_LOADING, &plat_priv->driver_state) || + test_bit(CNSS_DRIVER_UNLOADING, &plat_priv->driver_state) || + test_bit(CNSS_DRIVER_IDLE_RESTART, &plat_priv->driver_state) || + test_bit(CNSS_DRIVER_IDLE_SHUTDOWN, &plat_priv->driver_state)) { + cnss_pr_info("Loading/Unloading/idle restart/shutdown is in progress, ignore forced collect rddm\n"); + return 0; + } + + ret = cnss_bus_force_fw_assert_hdlr(plat_priv); + if (ret) + return ret; reinit_completion(&plat_priv->rddm_complete); ret = wait_for_completion_timeout @@ -1123,30 +1243,6 @@ int cnss_qmi_send(struct device *dev, int type, void *cmd, } EXPORT_SYMBOL(cnss_qmi_send); -static int cnss_wlfw_server_arrive_hdlr(struct cnss_plat_data *plat_priv) -{ - int ret; - - ret = cnss_wlfw_server_arrive(plat_priv); - if (ret) - goto out; - - if (!cnss_bus_req_mem_ind_valid(plat_priv)) { - ret = cnss_wlfw_tgt_cap_send_sync(plat_priv); - if (ret) - goto out; - - ret = cnss_wlfw_bdf_dnld_send_sync(plat_priv); - if (ret) - goto out; - /*cnss driver sends meta data report and waits for FW_READY*/ - if (cnss_bus_dev_cal_rep_valid(plat_priv)) - ret = cnss_wlfw_cal_report_send_sync(plat_priv); - } -out: - return ret; -} - static int cnss_cold_boot_cal_start_hdlr(struct cnss_plat_data *plat_priv) { int ret = 0; @@ -1177,7 +1273,13 @@ static int cnss_cold_boot_cal_done_hdlr(struct cnss_plat_data *plat_priv) plat_priv->cal_done = true; cnss_wlfw_wlan_mode_send_sync(plat_priv, CNSS_OFF); + if (plat_priv->device_id == QCN7605_DEVICE_ID || + plat_priv->device_id == QCN7605_STANDALONE_DEVICE_ID || + plat_priv->device_id == QCN7605_COMPOSITE_DEVICE_ID) + goto skip_shutdown; cnss_bus_dev_shutdown(plat_priv); + +skip_shutdown: complete(&plat_priv->cal_complete); clear_bit(CNSS_COLD_BOOT_CAL, &plat_priv->driver_state); @@ -1186,7 +1288,13 @@ static int cnss_cold_boot_cal_done_hdlr(struct cnss_plat_data *plat_priv) static int cnss_power_up_hdlr(struct cnss_plat_data *plat_priv) { - return cnss_bus_dev_powerup(plat_priv); + int ret; + + ret = cnss_bus_dev_powerup(plat_priv); + if (ret) + clear_bit(CNSS_DRIVER_IDLE_RESTART, &plat_priv->driver_state); + + return ret; } static int cnss_power_down_hdlr(struct cnss_plat_data *plat_priv) @@ -1196,6 +1304,109 @@ static int cnss_power_down_hdlr(struct cnss_plat_data *plat_priv) return 0; } +static int cnss_qdss_trace_req_mem_hdlr(struct cnss_plat_data *plat_priv) +{ + int ret = 0; + + ret = cnss_bus_alloc_qdss_mem(plat_priv); + if (ret < 0) + return ret; + + return cnss_wlfw_qdss_trace_mem_info_send_sync(plat_priv); +} + +static void *cnss_qdss_trace_pa_to_va(struct cnss_plat_data *plat_priv, + u64 pa, u32 size, int *seg_id) +{ + int i = 0; + struct cnss_fw_mem *qdss_mem = plat_priv->qdss_mem; + u64 offset = 0; + void *va = NULL; + u64 local_pa; + u32 local_size; + + for (i = 0; i < plat_priv->qdss_mem_seg_len; i++) { + local_pa = (u64)qdss_mem[i].pa; + local_size = (u32)qdss_mem[i].size; + if (pa == local_pa && size <= local_size) { + va = qdss_mem[i].va; + break; + } + if (pa > local_pa && + pa < local_pa + local_size && + pa + size <= local_pa + local_size) { + offset = pa - local_pa; + va = qdss_mem[i].va + offset; + break; + } + } + + *seg_id = i; + return va; +} + +static int cnss_qdss_trace_save_hdlr(struct cnss_plat_data *plat_priv, + void *data) +{ + struct cnss_qmi_event_qdss_trace_save_data *event_data = data; + struct cnss_fw_mem *qdss_mem = plat_priv->qdss_mem; + int ret = 0; + int i; + void *va = NULL; + u64 pa; + u32 size; + int seg_id = 0; + + if (!plat_priv->qdss_mem_seg_len) { + cnss_pr_err("Memory for QDSS trace is not available\n"); + return -ENOMEM; + } + + if (event_data->mem_seg_len == 0) { + for (i = 0; i < plat_priv->qdss_mem_seg_len; i++) { + ret = cnss_genl_send_msg(qdss_mem[i].va, + CNSS_GENL_MSG_TYPE_QDSS, + event_data->file_name, + qdss_mem[i].size); + if (ret < 0) { + cnss_pr_err("Fail to save QDSS data: %d\n", + ret); + break; + } + } + } else { + for (i = 0; i < event_data->mem_seg_len; i++) { + pa = event_data->mem_seg[i].addr; + size = event_data->mem_seg[i].size; + va = cnss_qdss_trace_pa_to_va(plat_priv, pa, + size, &seg_id); + if (!va) { + cnss_pr_err("Fail to find matching va for pa 0x%llx\n", + pa); + ret = -EINVAL; + break; + } + ret = cnss_genl_send_msg(va, CNSS_GENL_MSG_TYPE_QDSS, + event_data->file_name, size); + if (ret < 0) { + cnss_pr_err("Fail to save QDSS data: %d\n", + ret); + break; + } + } + } + + kfree(data); + return ret; +} + +static int cnss_qdss_trace_free_hdlr(struct cnss_plat_data *plat_priv) +{ + cnss_bus_free_qdss_mem(plat_priv); + + return 0; +} + static void cnss_driver_event_work(struct work_struct *work) { struct cnss_plat_data *plat_priv = @@ -1266,12 +1477,30 @@ static void cnss_driver_event_work(struct work_struct *work) case CNSS_DRIVER_EVENT_FORCE_FW_ASSERT: ret = cnss_bus_force_fw_assert_hdlr(plat_priv); break; + case CNSS_DRIVER_EVENT_IDLE_RESTART: + set_bit(CNSS_DRIVER_IDLE_RESTART, + &plat_priv->driver_state); + /* fall through */ case CNSS_DRIVER_EVENT_POWER_UP: ret = cnss_power_up_hdlr(plat_priv); break; + case CNSS_DRIVER_EVENT_IDLE_SHUTDOWN: + set_bit(CNSS_DRIVER_IDLE_SHUTDOWN, + &plat_priv->driver_state); + /* fall through */ case CNSS_DRIVER_EVENT_POWER_DOWN: ret = cnss_power_down_hdlr(plat_priv); break; + case CNSS_DRIVER_EVENT_QDSS_TRACE_REQ_MEM: + ret = cnss_qdss_trace_req_mem_hdlr(plat_priv); + break; + case CNSS_DRIVER_EVENT_QDSS_TRACE_SAVE: + ret = cnss_qdss_trace_save_hdlr(plat_priv, + event->data); + break; + case CNSS_DRIVER_EVENT_QDSS_TRACE_FREE: + ret = cnss_qdss_trace_free_hdlr(plat_priv); + break; default: cnss_pr_err("Invalid driver event type: %d", event->type); @@ -1378,10 +1607,13 @@ static int cnss_register_ramdump_v1(struct cnss_plat_data *plat_priv) subsys_info = &plat_priv->subsys_info; ramdump_info = &plat_priv->ramdump_info; - if (of_property_read_u32(dev->of_node, "qcom,wlan-ramdump-dynamic", + if (of_property_read_u32(plat_priv->dev_node, + "qcom,wlan-ramdump-dynamic", &ramdump_size) == 0) { - ramdump_info->ramdump_va = dma_alloc_coherent(dev, ramdump_size, - &ramdump_info->ramdump_pa, GFP_KERNEL); + ramdump_info->ramdump_va = + dma_alloc_coherent(dev, ramdump_size, + &ramdump_info->ramdump_pa, + GFP_KERNEL); if (ramdump_info->ramdump_va) ramdump_info->ramdump_size = ramdump_size; @@ -1441,14 +1673,14 @@ static int cnss_register_ramdump_v2(struct cnss_plat_data *plat_priv) struct cnss_ramdump_info_v2 *info_v2; struct cnss_dump_data *dump_data; struct msm_dump_entry dump_entry; - struct device *dev = &plat_priv->plat_dev->dev; u32 ramdump_size = 0; subsys_info = &plat_priv->subsys_info; info_v2 = &plat_priv->ramdump_info_v2; dump_data = &info_v2->dump_data; - if (of_property_read_u32(dev->of_node, "qcom,wlan-ramdump-dynamic", + if (of_property_read_u32(plat_priv->dev_node, + "qcom,wlan-ramdump-dynamic", &ramdump_size) == 0) info_v2->ramdump_size = ramdump_size; @@ -1512,16 +1744,14 @@ int cnss_register_ramdump(struct cnss_plat_data *plat_priv) case QCA6174_DEVICE_ID: ret = cnss_register_ramdump_v1(plat_priv); break; - case QCA6290_EMULATION_DEVICE_ID: case QCA6290_DEVICE_ID: - case QCA6390_EMULATION_DEVICE_ID: case QCA6390_DEVICE_ID: + case QCN7605_DEVICE_ID: ret = cnss_register_ramdump_v2(plat_priv); break; case QCN7605_COMPOSITE_DEVICE_ID: case QCN7605_STANDALONE_DEVICE_ID: break; - default: cnss_pr_err("Unknown device ID: 0x%lx\n", plat_priv->device_id); ret = -ENODEV; @@ -1536,10 +1766,9 @@ void cnss_unregister_ramdump(struct cnss_plat_data *plat_priv) case QCA6174_DEVICE_ID: cnss_unregister_ramdump_v1(plat_priv); break; - case QCA6290_EMULATION_DEVICE_ID: case QCA6290_DEVICE_ID: - case QCA6390_EMULATION_DEVICE_ID: case QCA6390_DEVICE_ID: + case QCN7605_DEVICE_ID: cnss_unregister_ramdump_v2(plat_priv); break; case QCN7605_COMPOSITE_DEVICE_ID: @@ -1600,7 +1829,7 @@ static ssize_t cnss_fs_ready_store(struct device *dev, cnss_pr_dbg("File system is ready, fs_ready is %d, count is %zu\n", fs_ready, count); - if (qmi_bypass) { + if (test_bit(QMI_BYPASS, &plat_priv->ctrl_params.quirks)) { cnss_pr_dbg("QMI is bypassed.\n"); return count; } @@ -1611,10 +1840,9 @@ static ssize_t cnss_fs_ready_store(struct device *dev, } switch (plat_priv->device_id) { - case QCA6290_EMULATION_DEVICE_ID: case QCA6290_DEVICE_ID: - case QCA6390_EMULATION_DEVICE_ID: case QCA6390_DEVICE_ID: + case QCN7605_DEVICE_ID: break; default: cnss_pr_err("Not supported for device ID 0x%lx\n", @@ -1691,6 +1919,7 @@ static int cnss_misc_init(struct cnss_plat_data *plat_priv) init_completion(&plat_priv->power_up_complete); init_completion(&plat_priv->cal_complete); init_completion(&plat_priv->rddm_complete); + init_completion(&plat_priv->recovery_complete); mutex_init(&plat_priv->dev_lock); return 0; @@ -1698,6 +1927,7 @@ static int cnss_misc_init(struct cnss_plat_data *plat_priv) static void cnss_misc_deinit(struct cnss_plat_data *plat_priv) { + complete_all(&plat_priv->recovery_complete); complete_all(&plat_priv->rddm_complete); complete_all(&plat_priv->cal_complete); complete_all(&plat_priv->power_up_complete); @@ -1706,10 +1936,33 @@ static void cnss_misc_deinit(struct cnss_plat_data *plat_priv) del_timer(&plat_priv->fw_boot_timer); } +static void cnss_init_control_params(struct cnss_plat_data *plat_priv) +{ + if (of_property_read_bool(plat_priv->plat_dev->dev.of_node, + "cnss-daemon-support")) + plat_priv->ctrl_params.quirks |= BIT(ENABLE_DAEMON_SUPPORT); + + plat_priv->ctrl_params.mhi_timeout = CNSS_MHI_TIMEOUT_DEFAULT; + plat_priv->ctrl_params.qmi_timeout = CNSS_QMI_TIMEOUT_DEFAULT; + plat_priv->ctrl_params.bdf_type = CNSS_BDF_TYPE_DEFAULT; +} + +static void cnss_get_wlaon_pwr_ctrl_info(struct cnss_plat_data *plat_priv) +{ + struct device *dev = &plat_priv->plat_dev->dev; + + plat_priv->set_wlaon_pwr_ctrl = + of_property_read_bool(dev->of_node, "qcom,set-wlaon-pwr-ctrl"); + + cnss_pr_dbg("set_wlaon_pwr_ctrl is %d\n", + plat_priv->set_wlaon_pwr_ctrl); +} + static const struct platform_device_id cnss_platform_id_table[] = { { .name = "qca6174", .driver_data = QCA6174_DEVICE_ID, }, { .name = "qca6290", .driver_data = QCA6290_DEVICE_ID, }, { .name = "qca6390", .driver_data = QCA6390_DEVICE_ID, }, + { .name = "qcaconv", .driver_data = 0}, }; static const struct of_device_id cnss_of_match_table[] = { @@ -1722,10 +1975,56 @@ static const struct of_device_id cnss_of_match_table[] = { { .compatible = "qcom,cnss-qca6390", .data = (void *)&cnss_platform_id_table[2]}, + { + .compatible = "qcom,cnss-qca-converged", + .data = (void *)&cnss_platform_id_table[3]}, { }, }; MODULE_DEVICE_TABLE(of, cnss_of_match_table); +struct cnss_fw_path { + unsigned long device_id; + const char path[CNSS_FW_PATH_MAX_LEN]; +}; + +static const struct cnss_fw_path cnss_fw_path_table[] = { + { QCA6174_DEVICE_ID, "qca6174/" }, + { QCA6290_DEVICE_ID, "qca6290/" }, + { QCA6390_DEVICE_ID, "qca6390/" }, + { QCN7605_DEVICE_ID, "qcn7605/" }, + { 0, "" } +}; + +const char *cnss_get_fw_path(struct cnss_plat_data *plat_priv) +{ + const struct cnss_fw_path *fw_path; + const char *path; + int size = ARRAY_SIZE(cnss_fw_path_table); + + if (!plat_priv->is_converged_dt) { + path = cnss_fw_path_table[size - 1].path; + } else { + fw_path = cnss_fw_path_table; + while (fw_path->device_id && + fw_path->device_id != plat_priv->device_id) { + fw_path++; + } + + path = fw_path->path; + } + + cnss_pr_dbg("get firmware path[%s] for device[0x%lx]\n", + path, plat_priv->device_id); + return path; +} + +static inline bool +cnss_is_converged_dt(struct cnss_plat_data *plat_priv) +{ + return of_property_read_bool(plat_priv->plat_dev->dev.of_node, + "qcom,converged-dt"); +} + static int cnss_probe(struct platform_device *plat_dev) { int ret = 0; @@ -1756,16 +2055,25 @@ static int cnss_probe(struct platform_device *plat_dev) } plat_priv->plat_dev = plat_dev; + plat_priv->dev_node = NULL; plat_priv->device_id = device_id->driver_data; - plat_priv->bus_type = cnss_get_bus_type(plat_priv->device_id); + plat_priv->is_converged_dt = cnss_is_converged_dt(plat_priv); + cnss_pr_dbg("Probing platform driver from %s DT\n", + plat_priv->is_converged_dt ? "converged" : "single"); + + plat_priv->bus_type = cnss_get_bus_type(plat_priv); cnss_set_plat_priv(plat_dev, plat_priv); platform_set_drvdata(plat_dev, plat_priv); + INIT_LIST_HEAD(&plat_priv->vreg_list); + + cnss_get_wlaon_pwr_ctrl_info(plat_priv); + cnss_init_control_params(plat_priv); ret = cnss_get_resources(plat_priv); if (ret) goto reset_ctx; - if (!test_bit(SKIP_DEVICE_BOOT, &quirks)) { + if (!test_bit(SKIP_DEVICE_BOOT, &plat_priv->ctrl_params.quirks)) { ret = cnss_power_on_device(plat_priv); if (ret) goto free_res; @@ -1803,6 +2111,10 @@ static int cnss_probe(struct platform_device *plat_dev) if (ret) goto destroy_debugfs; + ret = cnss_genl_init(); + if (ret < 0) + cnss_pr_err("CNSS genl init failed %d\n", ret); + cnss_pr_info("Platform driver probed successfully.\n"); return 0; @@ -1820,10 +2132,10 @@ unreg_bus_scale: unreg_esoc: cnss_unregister_esoc(plat_priv); deinit_bus: - if (!test_bit(SKIP_DEVICE_BOOT, &quirks)) + if (!test_bit(SKIP_DEVICE_BOOT, &plat_priv->ctrl_params.quirks)) cnss_bus_deinit(plat_priv); power_off: - if (!test_bit(SKIP_DEVICE_BOOT, &quirks)) + if (!test_bit(SKIP_DEVICE_BOOT, &plat_priv->ctrl_params.quirks)) cnss_power_off_device(plat_priv); free_res: cnss_put_resources(plat_priv); @@ -1838,6 +2150,7 @@ static int cnss_remove(struct platform_device *plat_dev) { struct cnss_plat_data *plat_priv = platform_get_drvdata(plat_dev); + cnss_genl_exit(); cnss_misc_deinit(plat_priv); cnss_debugfs_destroy(plat_priv); cnss_qmi_deinit(plat_priv); diff --git a/drivers/net/wireless/cnss2/main.h b/drivers/net/wireless/cnss2/main.h index 31aa35b9ad97..185e52028812 100644 --- a/drivers/net/wireless/cnss2/main.h +++ b/drivers/net/wireless/cnss2/main.h @@ -1,4 +1,4 @@ -/* Copyright (c) 2016-2018, The Linux Foundation. All rights reserved. +/* Copyright (c) 2016-2020, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -27,20 +27,22 @@ #define QMI_WLFW_MAX_TIMESTAMP_LEN 32 #define QMI_WLFW_MAX_NUM_MEM_SEG 32 #define CNSS_RDDM_TIMEOUT_MS 20000 +#define RECOVERY_TIMEOUT 60000 #define CNSS_EVENT_SYNC BIT(0) #define CNSS_EVENT_UNINTERRUPTIBLE BIT(1) #define CNSS_EVENT_SYNC_UNINTERRUPTIBLE (CNSS_EVENT_SYNC | \ CNSS_EVENT_UNINTERRUPTIBLE) +#define CNSS_FW_PATH_MAX_LEN 32 + enum cnss_dev_bus_type { CNSS_BUS_NONE = -1, CNSS_BUS_PCI, CNSS_BUS_USB, }; -struct cnss_vreg_info { - struct regulator *reg; +struct cnss_vreg_cfg { const char *name; u32 min_uv; u32 max_uv; @@ -48,11 +50,19 @@ struct cnss_vreg_info { u32 delay_us; }; +struct cnss_vreg_info { + struct list_head list; + struct regulator *reg; + struct cnss_vreg_cfg cfg; + u32 enabled; +}; + struct cnss_pinctrl_info { struct pinctrl *pinctrl; struct pinctrl_state *bootstrap_active; struct pinctrl_state *wlan_en_active; struct pinctrl_state *wlan_en_sleep; + u32 activated; }; struct cnss_subsys_info { @@ -161,6 +171,11 @@ enum cnss_driver_event_type { CNSS_DRIVER_EVENT_FORCE_FW_ASSERT, CNSS_DRIVER_EVENT_POWER_UP, CNSS_DRIVER_EVENT_POWER_DOWN, + CNSS_DRIVER_EVENT_IDLE_RESTART, + CNSS_DRIVER_EVENT_IDLE_SHUTDOWN, + CNSS_DRIVER_EVENT_QDSS_TRACE_REQ_MEM, + CNSS_DRIVER_EVENT_QDSS_TRACE_SAVE, + CNSS_DRIVER_EVENT_QDSS_TRACE_FREE, CNSS_DRIVER_EVENT_CAL_UPDATE, CNSS_DRIVER_EVENT_CAL_DOWNLOAD, CNSS_DRIVER_EVENT_MAX, @@ -173,11 +188,14 @@ enum cnss_driver_state { CNSS_COLD_BOOT_CAL, CNSS_DRIVER_LOADING, CNSS_DRIVER_UNLOADING, + CNSS_DRIVER_IDLE_RESTART, + CNSS_DRIVER_IDLE_SHUTDOWN, CNSS_DRIVER_PROBED, CNSS_DRIVER_RECOVERY, CNSS_FW_BOOT_RECOVERY, CNSS_DEV_ERR_NOTIFY, CNSS_DRIVER_DEBUG, + CNSS_IN_SUSPEND_RESUME, }; struct cnss_recovery_data { @@ -208,13 +226,49 @@ enum cnss_debug_quirks { SKIP_DEVICE_BOOT, USE_CORE_ONLY_FW, SKIP_RECOVERY, + QMI_BYPASS, + ENABLE_WALTEST, + ENABLE_PCI_LINK_DOWN_PANIC, + FBC_BYPASS, + ENABLE_DAEMON_SUPPORT, + IGNORE_PCI_LINK_FAILURE, +}; + +enum cnss_bdf_type { + CNSS_BDF_BIN, + CNSS_BDF_ELF, + CNSS_BDF_REGDB = 4, + CNSS_BDF_DUMMY = 255, +}; + +struct cnss_control_params { + unsigned long quirks; + unsigned int mhi_timeout; + unsigned int qmi_timeout; + unsigned int bdf_type; +}; + +enum cnss_ce_index { + CNSS_CE_00, + CNSS_CE_01, + CNSS_CE_02, + CNSS_CE_03, + CNSS_CE_04, + CNSS_CE_05, + CNSS_CE_06, + CNSS_CE_07, + CNSS_CE_08, + CNSS_CE_09, + CNSS_CE_10, + CNSS_CE_11, + CNSS_CE_COMMON, }; struct cnss_plat_data { struct platform_device *plat_dev; void *bus_priv; enum cnss_dev_bus_type bus_type; - struct cnss_vreg_info *vreg_info; + struct list_head vreg_list; struct cnss_pinctrl_info pinctrl_info; struct cnss_subsys_info subsys_info; struct cnss_ramdump_info ramdump_info; @@ -224,6 +278,7 @@ struct cnss_plat_data { struct notifier_block modem_nb; struct cnss_platform_cap cap; struct pm_qos_request qos_request; + struct cnss_device_version device_version; unsigned long device_id; enum cnss_driver_status driver_status; u32 recovery_count; @@ -242,6 +297,9 @@ struct cnss_plat_data { u32 fw_mem_seg_len; struct cnss_fw_mem fw_mem[QMI_WLFW_MAX_NUM_MEM_SEG]; struct cnss_fw_mem m3_mem; + u32 qdss_mem_seg_len; + struct cnss_fw_mem qdss_mem[QMI_WLFW_MAX_NUM_MEM_SEG]; + u32 *qdss_reg; struct cnss_pin_connect_result pin_result; struct dentry *root_dentry; atomic_t pm_count; @@ -254,18 +312,23 @@ struct cnss_plat_data { u32 diag_reg_read_len; u8 *diag_reg_read_buf; bool cal_done; - char firmware_name[13]; + char firmware_name[CNSS_FW_PATH_MAX_LEN]; struct completion rddm_complete; + struct completion recovery_complete; + struct cnss_control_params ctrl_params; + u32 is_converged_dt; + struct device_node *dev_node; + u8 set_wlaon_pwr_ctrl; }; struct cnss_plat_data *cnss_get_plat_priv(struct platform_device *plat_dev); -bool *cnss_get_qmi_bypass(void); -unsigned long *cnss_get_debug_quirks(void); int cnss_driver_event_post(struct cnss_plat_data *plat_priv, enum cnss_driver_event_type type, u32 flags, void *data); int cnss_get_vreg(struct cnss_plat_data *plat_priv); int cnss_get_pinctrl(struct cnss_plat_data *plat_priv); +void cnss_put_vreg(struct cnss_plat_data *plat_priv); +void cnss_put_pinctrl(struct cnss_plat_data *plat_priv); int cnss_power_on_device(struct cnss_plat_data *plat_priv); void cnss_power_off_device(struct cnss_plat_data *plat_priv); int cnss_register_subsys(struct cnss_plat_data *plat_priv); @@ -273,7 +336,7 @@ void cnss_unregister_subsys(struct cnss_plat_data *plat_priv); int cnss_register_ramdump(struct cnss_plat_data *plat_priv); void cnss_unregister_ramdump(struct cnss_plat_data *plat_priv); void cnss_set_pin_connect_status(struct cnss_plat_data *plat_priv); -u32 cnss_get_wake_msi(struct cnss_plat_data *plat_priv); -bool *cnss_get_qmi_bypass(void); -bool is_qcn7605_device(u16 device_id); +const char *cnss_get_fw_path(struct cnss_plat_data *plat_priv); +int cnss_dev_specific_power_on(struct cnss_plat_data *plat_priv); + #endif /* _CNSS_MAIN_H */ diff --git a/drivers/net/wireless/cnss2/pci.c b/drivers/net/wireless/cnss2/pci.c index 607369dede2d..b3b19e4e92a6 100644 --- a/drivers/net/wireless/cnss2/pci.c +++ b/drivers/net/wireless/cnss2/pci.c @@ -14,7 +14,7 @@ #include #include #include -#include +#include #include #include #include @@ -43,28 +43,297 @@ #define MHI_NODE_NAME "qcom,mhi" #define MHI_MSI_NAME "MHI" -#define MAX_M3_FILE_NAME_LENGTH 13 #define DEFAULT_M3_FILE_NAME "m3.bin" #define DEFAULT_FW_FILE_NAME "amss.bin" +#define FW_V2_FILE_NAME "amss20.bin" +#define FW_V2_NUMBER 2 #define WAKE_MSI_NAME "WAKE" #define FW_ASSERT_TIMEOUT 5000 +#define DEV_RDDM_TIMEOUT 5000 + +#ifdef CONFIG_CNSS_EMULATION +#define EMULATION_HW 1 +#else +#define EMULATION_HW 0 +#endif static DEFINE_SPINLOCK(pci_link_down_lock); static DEFINE_SPINLOCK(pci_reg_window_lock); -static unsigned int pci_link_down_panic; -module_param(pci_link_down_panic, uint, 0600); -MODULE_PARM_DESC(pci_link_down_panic, - "Trigger kernel panic when PCI link down is detected"); +#define MHI_TIMEOUT_OVERWRITE_MS (plat_priv->ctrl_params.mhi_timeout) -static bool fbc_bypass; -#ifdef CONFIG_CNSS2_DEBUG -module_param(fbc_bypass, bool, 0600); -MODULE_PARM_DESC(fbc_bypass, - "Bypass firmware download when loading WLAN driver"); -#endif +#define QCA6390_PCIE_REMAP_BAR_CTRL_OFFSET 0x310C + +#define QCA6390_CE_SRC_RING_REG_BASE 0xA00000 +#define QCA6390_CE_DST_RING_REG_BASE 0xA01000 +#define QCA6390_CE_COMMON_REG_BASE 0xA18000 + +#define QCA6390_CE_SRC_RING_BASE_LSB_OFFSET 0x0 +#define QCA6390_CE_SRC_RING_BASE_MSB_OFFSET 0x4 +#define QCA6390_CE_SRC_RING_ID_OFFSET 0x8 +#define QCA6390_CE_SRC_RING_MISC_OFFSET 0x10 +#define QCA6390_CE_SRC_CTRL_OFFSET 0x58 +#define QCA6390_CE_SRC_R0_CE_CH_SRC_IS_OFFSET 0x5C +#define QCA6390_CE_SRC_RING_HP_OFFSET 0x400 +#define QCA6390_CE_SRC_RING_TP_OFFSET 0x404 + +#define QCA6390_CE_DEST_RING_BASE_LSB_OFFSET 0x0 +#define QCA6390_CE_DEST_RING_BASE_MSB_OFFSET 0x4 +#define QCA6390_CE_DEST_RING_ID_OFFSET 0x8 +#define QCA6390_CE_DEST_RING_MISC_OFFSET 0x10 +#define QCA6390_CE_DEST_CTRL_OFFSET 0xB0 +#define QCA6390_CE_CH_DST_IS_OFFSET 0xB4 +#define QCA6390_CE_CH_DEST_CTRL2_OFFSET 0xB8 +#define QCA6390_CE_DEST_RING_HP_OFFSET 0x400 +#define QCA6390_CE_DEST_RING_TP_OFFSET 0x404 + +#define QCA6390_CE_STATUS_RING_BASE_LSB_OFFSET 0x58 +#define QCA6390_CE_STATUS_RING_BASE_MSB_OFFSET 0x5C +#define QCA6390_CE_STATUS_RING_ID_OFFSET 0x60 +#define QCA6390_CE_STATUS_RING_MISC_OFFSET 0x68 +#define QCA6390_CE_STATUS_RING_HP_OFFSET 0x408 +#define QCA6390_CE_STATUS_RING_TP_OFFSET 0x40C + +#define QCA6390_CE_COMMON_GXI_ERR_INTS 0x14 +#define QCA6390_CE_COMMON_GXI_ERR_STATS 0x18 +#define QCA6390_CE_COMMON_GXI_WDOG_STATUS 0x2C +#define QCA6390_CE_COMMON_TARGET_IE_0 0x48 +#define QCA6390_CE_COMMON_TARGET_IE_1 0x4C + +#define QCA6390_CE_REG_INTERVAL 0x2000 + +#define SHADOW_REG_COUNT 36 +#define QCA6390_PCIE_SHADOW_REG_VALUE_0 0x1E03024 +#define QCA6390_PCIE_SHADOW_REG_VALUE_35 0x1E030B0 + +#define SHADOW_REG_INTER_COUNT 43 +#define QCA6390_PCIE_SHADOW_REG_INTER_0 0x1E05000 +#define QCA6390_PCIE_SHADOW_REG_HUNG 0x1E050A8 + +#define QDSS_APB_DEC_CSR_BASE 0x1C01000 + +#define QDSS_APB_DEC_CSR_ETRIRQCTRL_OFFSET 0x6C +#define QDSS_APB_DEC_CSR_PRESERVEETF_OFFSET 0x70 +#define QDSS_APB_DEC_CSR_PRESERVEETR0_OFFSET 0x74 +#define QDSS_APB_DEC_CSR_PRESERVEETR1_OFFSET 0x78 + +#define MAX_UNWINDOWED_ADDRESS 0x80000 +#define WINDOW_ENABLE_BIT 0x40000000 +#define WINDOW_SHIFT 19 +#define WINDOW_VALUE_MASK 0x3F +#define WINDOW_START MAX_UNWINDOWED_ADDRESS +#define WINDOW_RANGE_MASK 0x7FFFF + +#define WLAON_PWR_CTRL_SHUTDOWN_DELAY_MIN_US 1000 +#define WLAON_PWR_CTRL_SHUTDOWN_DELAY_MAX_US 2000 +#define QFPROM_PWR_CTRL_VDD4BLOW_SW_EN_MASK 0x4 +#define QFPROM_PWR_CTRL_SHUTDOWN_EN_MASK 0x1 + +#define FORCE_WAKE_DELAY_MIN_US 4000 +#define FORCE_WAKE_DELAY_MAX_US 6000 +#define FORCE_WAKE_DELAY_TIMEOUT_US 60000 + +#define QCA6390_WLAON_QFPROM_PWR_CTRL_REG 0x1F8031C + +#define POWER_ON_RETRY_MAX_TIMES 3 +#define POWER_ON_RETRY_DELAY_MS 200 + +#define LINK_TRAINING_RETRY_MAX_TIMES 3 + +static struct cnss_pci_reg ce_src[] = { + { "SRC_RING_BASE_LSB", QCA6390_CE_SRC_RING_BASE_LSB_OFFSET }, + { "SRC_RING_BASE_MSB", QCA6390_CE_SRC_RING_BASE_MSB_OFFSET }, + { "SRC_RING_ID", QCA6390_CE_SRC_RING_ID_OFFSET }, + { "SRC_RING_MISC", QCA6390_CE_SRC_RING_MISC_OFFSET }, + { "SRC_CTRL", QCA6390_CE_SRC_CTRL_OFFSET }, + { "SRC_R0_CE_CH_SRC_IS", QCA6390_CE_SRC_R0_CE_CH_SRC_IS_OFFSET }, + { "SRC_RING_HP", QCA6390_CE_SRC_RING_HP_OFFSET }, + { "SRC_RING_TP", QCA6390_CE_SRC_RING_TP_OFFSET }, + { NULL }, +}; + +static struct cnss_pci_reg ce_dst[] = { + { "DEST_RING_BASE_LSB", QCA6390_CE_DEST_RING_BASE_LSB_OFFSET }, + { "DEST_RING_BASE_MSB", QCA6390_CE_DEST_RING_BASE_MSB_OFFSET }, + { "DEST_RING_ID", QCA6390_CE_DEST_RING_ID_OFFSET }, + { "DEST_RING_MISC", QCA6390_CE_DEST_RING_MISC_OFFSET }, + { "DEST_CTRL", QCA6390_CE_DEST_CTRL_OFFSET }, + { "CE_CH_DST_IS", QCA6390_CE_CH_DST_IS_OFFSET }, + { "CE_CH_DEST_CTRL2", QCA6390_CE_CH_DEST_CTRL2_OFFSET }, + { "DEST_RING_HP", QCA6390_CE_DEST_RING_HP_OFFSET }, + { "DEST_RING_TP", QCA6390_CE_DEST_RING_TP_OFFSET }, + { "STATUS_RING_BASE_LSB", QCA6390_CE_STATUS_RING_BASE_LSB_OFFSET }, + { "STATUS_RING_BASE_MSB", QCA6390_CE_STATUS_RING_BASE_MSB_OFFSET }, + { "STATUS_RING_ID", QCA6390_CE_STATUS_RING_ID_OFFSET }, + { "STATUS_RING_MISC", QCA6390_CE_STATUS_RING_MISC_OFFSET }, + { "STATUS_RING_HP", QCA6390_CE_STATUS_RING_HP_OFFSET }, + { "STATUS_RING_TP", QCA6390_CE_STATUS_RING_TP_OFFSET }, + { NULL }, +}; + +static struct cnss_pci_reg ce_cmn[] = { + { "GXI_ERR_INTS", QCA6390_CE_COMMON_GXI_ERR_INTS }, + { "GXI_ERR_STATS", QCA6390_CE_COMMON_GXI_ERR_STATS }, + { "GXI_WDOG_STATUS", QCA6390_CE_COMMON_GXI_WDOG_STATUS }, + { "TARGET_IE_0", QCA6390_CE_COMMON_TARGET_IE_0 }, + { "TARGET_IE_1", QCA6390_CE_COMMON_TARGET_IE_1 }, + { NULL }, +}; + +static struct cnss_pci_reg qdss_csr[] = { + { "QDSSCSR_ETRIRQCTRL", QDSS_APB_DEC_CSR_ETRIRQCTRL_OFFSET }, + { "QDSSCSR_PRESERVEETF", QDSS_APB_DEC_CSR_PRESERVEETF_OFFSET }, + { "QDSSCSR_PRESERVEETR0", QDSS_APB_DEC_CSR_PRESERVEETR0_OFFSET }, + { "QDSSCSR_PRESERVEETR1", QDSS_APB_DEC_CSR_PRESERVEETR1_OFFSET }, + { NULL }, +}; + +static int cnss_pci_check_link_status(struct cnss_pci_data *pci_priv) +{ + u16 device_id; + + if (pci_priv->pci_link_state == PCI_LINK_DOWN) { + cnss_pr_dbg("PCIe link is suspended\n"); + return -EIO; + } + + if (pci_priv->pci_link_down_ind) { + cnss_pr_err("PCIe link is down\n"); + return -EIO; + } + + pci_read_config_word(pci_priv->pci_dev, PCI_DEVICE_ID, &device_id); + if (device_id != pci_priv->device_id) { + cnss_fatal_err("PCI device ID mismatch, link possibly down, current read ID: 0x%x, record ID: 0x%x\n", + device_id, pci_priv->device_id); + return -EIO; + } + + return 0; +} + +static void cnss_pci_select_window(struct cnss_pci_data *pci_priv, u32 offset) +{ + u32 window = (offset >> WINDOW_SHIFT) & WINDOW_VALUE_MASK; + + if (window != pci_priv->remap_window) { + writel_relaxed(WINDOW_ENABLE_BIT | window, + QCA6390_PCIE_REMAP_BAR_CTRL_OFFSET + + pci_priv->bar); + pci_priv->remap_window = window; + cnss_pr_dbg("Config PCIe remap window register to 0x%x\n", + WINDOW_ENABLE_BIT | window); + } +} + +static int cnss_pci_reg_read(struct cnss_pci_data *pci_priv, + u32 offset, u32 *val) +{ + int ret; + + ret = cnss_pci_check_link_status(pci_priv); + if (ret) + return ret; + + if (pci_priv->pci_dev->device == QCA6174_DEVICE_ID || + offset < MAX_UNWINDOWED_ADDRESS) { + *val = readl_relaxed(pci_priv->bar + offset); + return 0; + } + + spin_lock_bh(&pci_reg_window_lock); + cnss_pci_select_window(pci_priv, offset); + + *val = readl_relaxed(pci_priv->bar + WINDOW_START + + (offset & WINDOW_RANGE_MASK)); + spin_unlock_bh(&pci_reg_window_lock); + + return 0; +} + +static int cnss_pci_reg_write(struct cnss_pci_data *pci_priv, u32 offset, + u32 val) +{ + int ret; + + if (!in_interrupt() && !irqs_disabled()) { + ret = cnss_pci_check_link_status(pci_priv); + if (ret) + return ret; + } + + if (pci_priv->pci_dev->device == QCA6174_DEVICE_ID || + offset < MAX_UNWINDOWED_ADDRESS) { + writel_relaxed(val, pci_priv->bar + offset); + return 0; + } + + spin_lock_bh(&pci_reg_window_lock); + cnss_pci_select_window(pci_priv, offset); + + writel_relaxed(val, pci_priv->bar + WINDOW_START + + (offset & WINDOW_RANGE_MASK)); + spin_unlock_bh(&pci_reg_window_lock); + + return 0; +} + +static void cnss_pci_disable_l1(struct cnss_pci_data *pci_priv) +{ + struct cnss_plat_data *plat_priv = pci_priv->plat_priv; + struct pci_dev *pdev = pci_priv->pci_dev; + bool disable_l1; + u32 lnkctl_offset; + u32 val; + + disable_l1 = of_property_read_bool(plat_priv->dev_node, + "pcie-disable-l1"); + cnss_pr_dbg("disable_l1 %d\n", disable_l1); + + if (!disable_l1) + return; + + lnkctl_offset = pdev->pcie_cap + PCI_EXP_LNKCTL; + pci_read_config_dword(pdev, lnkctl_offset, &val); + cnss_pr_dbg("lnkctl 0x%x\n", val); + + val &= ~PCI_EXP_LNKCTL_ASPM_L1; + pci_write_config_dword(pdev, lnkctl_offset, val); +} + +static void cnss_pci_disable_l1ss(struct cnss_pci_data *pci_priv) +{ + struct cnss_plat_data *plat_priv = pci_priv->plat_priv; + struct pci_dev *pdev = pci_priv->pci_dev; + bool disable_l1ss; + u32 l1ss_cap_id_offset; + u32 l1ss_ctl1_offset; + u32 val; + + disable_l1ss = of_property_read_bool(plat_priv->dev_node, + "pcie-disable-l1ss"); + cnss_pr_dbg("disable_l1ss %d\n", disable_l1ss); + + if (!disable_l1ss) + return; + + l1ss_cap_id_offset = pci_find_ext_capability(pdev, PCI_EXT_CAP_ID_L1SS); + if (!l1ss_cap_id_offset) { + cnss_pr_dbg("could not find L1ss capability register\n"); + return; + } + + l1ss_ctl1_offset = l1ss_cap_id_offset + PCI_L1SS_CTL1; + + pci_read_config_dword(pdev, l1ss_ctl1_offset, &val); + cnss_pr_dbg("l1ss_ctl1 0x%x\n", val); + + val &= ~(PCI_L1SS_CTL1_PCIPM_L1_1 | PCI_L1SS_CTL1_PCIPM_L1_2 | + PCI_L1SS_CTL1_ASPM_L1_1 | PCI_L1SS_CTL1_ASPM_L1_2); + pci_write_config_dword(pdev, l1ss_ctl1_offset, val); +} static int cnss_set_pci_config_space(struct cnss_pci_data *pci_priv, bool save) { @@ -94,6 +363,8 @@ static int cnss_set_pci_config_space(struct cnss_pci_data *pci_priv, bool save) &pci_priv->saved_state); pci_restore_state(pci_dev); } + + cnss_pci_disable_l1ss(pci_priv); } return 0; @@ -103,7 +374,9 @@ static int cnss_set_pci_link(struct cnss_pci_data *pci_priv, bool link_up) { int ret = 0; struct pci_dev *pci_dev = pci_priv->pci_dev; + int retry = 0; +retry: ret = msm_pcie_pm_control(link_up ? MSM_PCIE_RESUME : MSM_PCIE_SUSPEND, pci_dev->bus->number, @@ -112,6 +385,12 @@ static int cnss_set_pci_link(struct cnss_pci_data *pci_priv, bool link_up) if (ret) { cnss_pr_err("Failed to %s PCI link with default option, err = %d\n", link_up ? "resume" : "suspend", ret); + + if (link_up && retry++ < LINK_TRAINING_RETRY_MAX_TIMES) { + cnss_pr_dbg("Retry PCI link training #%d\n", retry); + goto retry; + } + return ret; } @@ -131,6 +410,8 @@ int cnss_suspend_pci_link(struct cnss_pci_data *pci_priv) goto out; } + pci_clear_master(pci_priv->pci_dev); + ret = cnss_set_pci_config_space(pci_priv, SAVE_PCI_CONFIG_SPACE); if (ret) goto out; @@ -167,8 +448,10 @@ int cnss_resume_pci_link(struct cnss_pci_data *pci_priv) } ret = cnss_set_pci_link(pci_priv, PCI_LINK_UP); - if (ret) + if (ret) { + ret = -EAGAIN; goto out; + } pci_priv->pci_link_state = PCI_LINK_UP; @@ -216,13 +499,16 @@ int cnss_pci_link_down(struct device *dev) unsigned long flags; struct pci_dev *pci_dev = to_pci_dev(dev); struct cnss_pci_data *pci_priv = cnss_get_pci_priv(pci_dev); + struct cnss_plat_data *plat_priv; if (!pci_priv) { cnss_pr_err("pci_priv is NULL!\n"); return -EINVAL; } - if (pci_link_down_panic) + plat_priv = pci_priv->plat_priv; + if (test_bit(ENABLE_PCI_LINK_DOWN_PANIC, + &plat_priv->ctrl_params.quirks)) panic("cnss: PCI link is down!\n"); spin_lock_irqsave(&pci_link_down_lock, flags); @@ -307,6 +593,7 @@ int cnss_pci_call_driver_probe(struct cnss_pci_data *pci_priv) goto out; } clear_bit(CNSS_DRIVER_RECOVERY, &plat_priv->driver_state); + complete(&plat_priv->recovery_complete); } else if (test_bit(CNSS_DRIVER_LOADING, &plat_priv->driver_state)) { ret = pci_priv->driver_ops->probe(pci_priv->pci_dev, pci_priv->pci_device_id); @@ -318,6 +605,20 @@ int cnss_pci_call_driver_probe(struct cnss_pci_data *pci_priv) clear_bit(CNSS_DRIVER_RECOVERY, &plat_priv->driver_state); clear_bit(CNSS_DRIVER_LOADING, &plat_priv->driver_state); set_bit(CNSS_DRIVER_PROBED, &plat_priv->driver_state); + } else if (test_bit(CNSS_DRIVER_IDLE_RESTART, + &plat_priv->driver_state)) { + ret = pci_priv->driver_ops->idle_restart(pci_priv->pci_dev, + pci_priv->pci_device_id); + if (ret) { + cnss_pr_err("Failed to idle restart host driver, err = %d\n", + ret); + goto out; + } + clear_bit(CNSS_DRIVER_RECOVERY, &plat_priv->driver_state); + clear_bit(CNSS_DRIVER_IDLE_RESTART, &plat_priv->driver_state); + complete(&plat_priv->power_up_complete); + } else { + complete(&plat_priv->power_up_complete); } return 0; @@ -329,6 +630,7 @@ out: int cnss_pci_call_driver_remove(struct cnss_pci_data *pci_priv) { struct cnss_plat_data *plat_priv; + int ret; if (!pci_priv) return -ENODEV; @@ -350,9 +652,20 @@ int cnss_pci_call_driver_remove(struct cnss_pci_data *pci_priv) if (test_bit(CNSS_DRIVER_RECOVERY, &plat_priv->driver_state) && test_bit(CNSS_DRIVER_PROBED, &plat_priv->driver_state)) { pci_priv->driver_ops->shutdown(pci_priv->pci_dev); - } else if (test_bit(CNSS_DRIVER_UNLOADING, &plat_priv->driver_state)) { + } else if (test_bit(CNSS_DRIVER_UNLOADING, &plat_priv->driver_state) && + test_bit(CNSS_DRIVER_PROBED, &plat_priv->driver_state)) { pci_priv->driver_ops->remove(pci_priv->pci_dev); clear_bit(CNSS_DRIVER_PROBED, &plat_priv->driver_state); + clear_bit(CNSS_DEV_ERR_NOTIFY, &plat_priv->driver_state); + } else if (test_bit(CNSS_DRIVER_IDLE_SHUTDOWN, + &plat_priv->driver_state)) { + ret = pci_priv->driver_ops->idle_shutdown(pci_priv->pci_dev); + if (ret == -EAGAIN) { + clear_bit(CNSS_DRIVER_IDLE_SHUTDOWN, + &plat_priv->driver_state); + return ret; + } + clear_bit(CNSS_DEV_ERR_NOTIFY, &plat_priv->driver_state); } return 0; @@ -394,6 +707,57 @@ int cnss_pci_update_status(struct cnss_pci_data *pci_priv, return 0; } +static void cnss_pci_dump_shadow_reg(struct cnss_pci_data *pci_priv) +{ + int i, j = 0, array_size = SHADOW_REG_COUNT + SHADOW_REG_INTER_COUNT; + gfp_t gfp = GFP_KERNEL; + u32 reg_offset; + + if (cnss_pci_check_link_status(pci_priv)) + return; + + if (in_interrupt() || irqs_disabled()) + gfp = GFP_ATOMIC; + + if (!pci_priv->debug_reg) { + pci_priv->debug_reg = devm_kzalloc(&pci_priv->pci_dev->dev, + sizeof(*pci_priv->debug_reg) + * array_size, gfp); + if (!pci_priv->debug_reg) + return; + } + + cnss_pr_dbg("Start to dump shadow registers\n"); + + for (i = 0; i < SHADOW_REG_COUNT; i++, j++) { + reg_offset = QCA6390_PCIE_SHADOW_REG_VALUE_0 + i * 4; + pci_priv->debug_reg[j].offset = reg_offset; + if (cnss_pci_reg_read(pci_priv, reg_offset, + &pci_priv->debug_reg[j].val)) + return; + } + + for (i = 0; i < SHADOW_REG_INTER_COUNT; i++, j++) { + reg_offset = QCA6390_PCIE_SHADOW_REG_INTER_0 + i * 4; + pci_priv->debug_reg[j].offset = reg_offset; + if (cnss_pci_reg_read(pci_priv, reg_offset, + &pci_priv->debug_reg[j].val)) + return; + } +} + +#ifdef CONFIG_CNSS2_DEBUG +static void cnss_pci_collect_dump(struct cnss_pci_data *pci_priv) +{ + cnss_pci_collect_dump_info(pci_priv, false); + CNSS_ASSERT(0); +} +#else +static void cnss_pci_collect_dump(struct cnss_pci_data *pci_priv) +{ +} +#endif + static int cnss_qca6174_powerup(struct cnss_pci_data *pci_priv) { int ret = 0; @@ -431,7 +795,9 @@ static int cnss_qca6174_shutdown(struct cnss_pci_data *pci_priv) cnss_pm_request_resume(pci_priv); - cnss_pci_call_driver_remove(pci_priv); + ret = cnss_pci_call_driver_remove(pci_priv); + if (ret == -EAGAIN) + goto out; cnss_request_bus_bandwidth(&plat_priv->plat_dev->dev, CNSS_BUS_WIDTH_NONE); @@ -445,7 +811,9 @@ static int cnss_qca6174_shutdown(struct cnss_pci_data *pci_priv) cnss_power_off_device(plat_priv); clear_bit(CNSS_DRIVER_UNLOADING, &plat_priv->driver_state); + clear_bit(CNSS_DRIVER_IDLE_SHUTDOWN, &plat_priv->driver_state); +out: return ret; } @@ -474,11 +842,107 @@ static int cnss_qca6174_ramdump(struct cnss_pci_data *pci_priv) return ret; } +static int cnss_pci_force_wake_get(struct cnss_pci_data *pci_priv) +{ + struct device *dev = &pci_priv->pci_dev->dev; + u32 timeout = 0; + int ret; + + ret = cnss_pci_force_wake_request(dev); + if (ret) { + cnss_pr_err("Failed to request force wake\n"); + return ret; + } + + while (!cnss_pci_is_device_awake(dev) && + timeout <= FORCE_WAKE_DELAY_TIMEOUT_US) { + usleep_range(FORCE_WAKE_DELAY_MIN_US, FORCE_WAKE_DELAY_MAX_US); + timeout += FORCE_WAKE_DELAY_MAX_US; + } + + if (cnss_pci_is_device_awake(dev) != true) { + cnss_pr_err("Timed out to request force wake\n"); + cnss_pci_force_wake_release(dev); + return -ETIMEDOUT; + } + + return 0; +} + +static int cnss_pci_force_wake_put(struct cnss_pci_data *pci_priv) +{ + struct device *dev = &pci_priv->pci_dev->dev; + int ret; + + ret = cnss_pci_force_wake_release(dev); + if (ret) + cnss_pr_err("Failed to release force wake\n"); + + return ret; +} + +static void cnss_pci_set_wlaon_pwr_ctrl(struct cnss_pci_data *pci_priv, + bool set_vddd4blow, bool set_shutdown, + bool do_force_wake) +{ + struct cnss_plat_data *plat_priv = pci_priv->plat_priv; + int ret; + u32 val; + + if (!plat_priv->set_wlaon_pwr_ctrl) + return; + + if (do_force_wake) + if (cnss_pci_force_wake_get(pci_priv)) + return; + + ret = cnss_pci_reg_read(pci_priv, QCA6390_WLAON_QFPROM_PWR_CTRL_REG, + &val); + if (ret) { + cnss_pr_err("Failed to read register offset 0x%x, err = %d\n", + QCA6390_WLAON_QFPROM_PWR_CTRL_REG, ret); + goto force_wake_put; + } + + cnss_pr_dbg("Read register offset 0x%x, val = 0x%x\n", + QCA6390_WLAON_QFPROM_PWR_CTRL_REG, val); + + if (set_vddd4blow) + val |= QFPROM_PWR_CTRL_VDD4BLOW_SW_EN_MASK; + else + val &= ~QFPROM_PWR_CTRL_VDD4BLOW_SW_EN_MASK; + + if (set_shutdown) + val |= QFPROM_PWR_CTRL_SHUTDOWN_EN_MASK; + else + val &= ~QFPROM_PWR_CTRL_SHUTDOWN_EN_MASK; + + ret = cnss_pci_reg_write(pci_priv, QCA6390_WLAON_QFPROM_PWR_CTRL_REG, + val); + if (ret) { + cnss_pr_err("Failed to write register offset 0x%x, err = %d\n", + QCA6390_WLAON_QFPROM_PWR_CTRL_REG, ret); + goto force_wake_put; + } + + cnss_pr_dbg("Write val 0x%x to register offset 0x%x\n", val, + QCA6390_WLAON_QFPROM_PWR_CTRL_REG); + + if (set_shutdown) + usleep_range(WLAON_PWR_CTRL_SHUTDOWN_DELAY_MIN_US, + WLAON_PWR_CTRL_SHUTDOWN_DELAY_MAX_US); + +force_wake_put: + if (do_force_wake) + cnss_pci_force_wake_put(pci_priv); +} + static int cnss_qca6290_powerup(struct cnss_pci_data *pci_priv) { int ret = 0; struct cnss_plat_data *plat_priv = pci_priv->plat_priv; unsigned int timeout; + int retry = 0; if (plat_priv->ramdump_info_v2.dump_data_valid || test_bit(CNSS_DRIVER_RECOVERY, &plat_priv->driver_state)) { @@ -486,6 +950,7 @@ static int cnss_qca6290_powerup(struct cnss_pci_data *pci_priv) cnss_pci_clear_dump_info(pci_priv); } +retry: ret = cnss_power_on_device(plat_priv); if (ret) { cnss_pr_err("Failed to power on device, err = %d\n", ret); @@ -495,14 +960,28 @@ static int cnss_qca6290_powerup(struct cnss_pci_data *pci_priv) ret = cnss_resume_pci_link(pci_priv); if (ret) { cnss_pr_err("Failed to resume PCI link, err = %d\n", ret); + if (test_bit(IGNORE_PCI_LINK_FAILURE, + &plat_priv->ctrl_params.quirks)) { + cnss_pr_dbg("Ignore PCI link resume failure\n"); + ret = 0; + goto out; + } + if (ret == -EAGAIN && retry++ < POWER_ON_RETRY_MAX_TIMES) { + cnss_power_off_device(plat_priv); + cnss_pr_dbg("Retry to resume PCI link #%d\n", retry); + msleep(POWER_ON_RETRY_DELAY_MS * retry); + goto retry; + } goto power_off; } + cnss_pci_set_wlaon_pwr_ctrl(pci_priv, false, false, false); timeout = cnss_get_boot_timeout(&pci_priv->pci_dev->dev); ret = cnss_pci_start_mhi(pci_priv); if (ret) { - cnss_pr_err("Failed to start MHI, err = %d\n", ret); + cnss_fatal_err("Failed to start MHI, err = %d\n", ret); + CNSS_ASSERT(0); if (!test_bit(CNSS_DEV_ERR_NOTIFY, &plat_priv->driver_state) && !pci_priv->pci_link_down_ind && timeout) mod_timer(&plat_priv->fw_boot_timer, @@ -510,7 +989,7 @@ static int cnss_qca6290_powerup(struct cnss_pci_data *pci_priv) return 0; } - if (test_bit(USE_CORE_ONLY_FW, cnss_get_debug_quirks())) { + if (test_bit(USE_CORE_ONLY_FW, &plat_priv->ctrl_params.quirks)) { clear_bit(CNSS_FW_BOOT_RECOVERY, &plat_priv->driver_state); clear_bit(CNSS_DRIVER_RECOVERY, &plat_priv->driver_state); return 0; @@ -518,7 +997,7 @@ static int cnss_qca6290_powerup(struct cnss_pci_data *pci_priv) cnss_set_pin_connect_status(plat_priv); - if (*cnss_get_qmi_bypass()) { + if (test_bit(QMI_BYPASS, &plat_priv->ctrl_params.quirks)) { ret = cnss_pci_call_driver_probe(pci_priv); if (ret) goto stop_mhi; @@ -530,6 +1009,7 @@ static int cnss_qca6290_powerup(struct cnss_pci_data *pci_priv) return 0; stop_mhi: + cnss_pci_set_wlaon_pwr_ctrl(pci_priv, false, true, true); cnss_pci_stop_mhi(pci_priv); cnss_suspend_pci_link(pci_priv); power_off: @@ -542,16 +1022,32 @@ static int cnss_qca6290_shutdown(struct cnss_pci_data *pci_priv) { int ret = 0; struct cnss_plat_data *plat_priv = pci_priv->plat_priv; + int do_force_wake = true; cnss_pm_request_resume(pci_priv); - cnss_pci_call_driver_remove(pci_priv); + ret = cnss_pci_call_driver_remove(pci_priv); + if (ret == -EAGAIN) + goto out; cnss_request_bus_bandwidth(&plat_priv->plat_dev->dev, CNSS_BUS_WIDTH_NONE); cnss_pci_set_monitor_wake_intr(pci_priv, false); cnss_pci_set_auto_suspended(pci_priv, 0); + if ((test_bit(CNSS_DRIVER_LOADING, &plat_priv->driver_state) || + test_bit(CNSS_DRIVER_UNLOADING, &plat_priv->driver_state) || + test_bit(CNSS_DRIVER_IDLE_RESTART, &plat_priv->driver_state) || + test_bit(CNSS_DRIVER_IDLE_SHUTDOWN, &plat_priv->driver_state)) && + test_bit(CNSS_DEV_ERR_NOTIFY, &plat_priv->driver_state)) { + del_timer(&pci_priv->dev_rddm_timer); + cnss_pci_collect_dump(pci_priv); + } + + if (test_bit(CNSS_DEV_ERR_NOTIFY, &plat_priv->driver_state)) + do_force_wake = false; + + cnss_pci_set_wlaon_pwr_ctrl(pci_priv, false, true, do_force_wake); cnss_pci_stop_mhi(pci_priv); ret = cnss_suspend_pci_link(pci_priv); @@ -560,10 +1056,14 @@ static int cnss_qca6290_shutdown(struct cnss_pci_data *pci_priv) cnss_power_off_device(plat_priv); + pci_priv->remap_window = 0; + clear_bit(CNSS_FW_READY, &plat_priv->driver_state); clear_bit(CNSS_FW_MEM_READY, &plat_priv->driver_state); clear_bit(CNSS_DRIVER_UNLOADING, &plat_priv->driver_state); + clear_bit(CNSS_DRIVER_IDLE_SHUTDOWN, &plat_priv->driver_state); +out: return ret; } @@ -574,16 +1074,6 @@ static void cnss_qca6290_crash_shutdown(struct cnss_pci_data *pci_priv) cnss_pr_dbg("Crash shutdown with driver_state 0x%lx\n", plat_priv->driver_state); - if (test_bit(CNSS_DRIVER_RECOVERY, &plat_priv->driver_state)) { - cnss_pr_dbg("Ignore crash shutdown\n"); - return; - } - - if (test_bit(CNSS_MHI_RDDM_DONE, &plat_priv->driver_state)) { - cnss_pr_dbg("RDDM already collected, return\n"); - return; - } - cnss_pci_collect_dump_info(pci_priv, true); } @@ -638,10 +1128,9 @@ int cnss_pci_dev_powerup(struct cnss_pci_data *pci_priv) case QCA6174_DEVICE_ID: ret = cnss_qca6174_powerup(pci_priv); break; - case QCA6290_EMULATION_DEVICE_ID: case QCA6290_DEVICE_ID: - case QCA6390_EMULATION_DEVICE_ID: case QCA6390_DEVICE_ID: + case QCN7605_DEVICE_ID: ret = cnss_qca6290_powerup(pci_priv); break; default: @@ -666,10 +1155,9 @@ int cnss_pci_dev_shutdown(struct cnss_pci_data *pci_priv) case QCA6174_DEVICE_ID: ret = cnss_qca6174_shutdown(pci_priv); break; - case QCA6290_EMULATION_DEVICE_ID: case QCA6290_DEVICE_ID: - case QCA6390_EMULATION_DEVICE_ID: case QCA6390_DEVICE_ID: + case QCN7605_DEVICE_ID: ret = cnss_qca6290_shutdown(pci_priv); break; default: @@ -694,10 +1182,9 @@ int cnss_pci_dev_crash_shutdown(struct cnss_pci_data *pci_priv) case QCA6174_DEVICE_ID: cnss_qca6174_crash_shutdown(pci_priv); break; - case QCA6290_EMULATION_DEVICE_ID: case QCA6290_DEVICE_ID: - case QCA6390_EMULATION_DEVICE_ID: case QCA6390_DEVICE_ID: + case QCN7605_DEVICE_ID: cnss_qca6290_crash_shutdown(pci_priv); break; default: @@ -722,10 +1209,9 @@ int cnss_pci_dev_ramdump(struct cnss_pci_data *pci_priv) case QCA6174_DEVICE_ID: ret = cnss_qca6174_ramdump(pci_priv); break; - case QCA6290_EMULATION_DEVICE_ID: case QCA6290_DEVICE_ID: - case QCA6390_EMULATION_DEVICE_ID: case QCA6390_DEVICE_ID: + case QCN7605_DEVICE_ID: ret = cnss_qca6290_ramdump(pci_priv); break; default: @@ -799,12 +1285,26 @@ EXPORT_SYMBOL(cnss_wlan_register_driver); void cnss_wlan_unregister_driver(struct cnss_wlan_driver *driver_ops) { struct cnss_plat_data *plat_priv = cnss_bus_dev_to_plat_priv(NULL); + int ret = 0; if (!plat_priv) { cnss_pr_err("plat_priv is NULL\n"); return; } + if (!test_bit(CNSS_DRIVER_RECOVERY, &plat_priv->driver_state) && + !test_bit(CNSS_DEV_ERR_NOTIFY, &plat_priv->driver_state)) + goto skip_wait; + + reinit_completion(&plat_priv->recovery_complete); + ret = wait_for_completion_timeout(&plat_priv->recovery_complete, + RECOVERY_TIMEOUT); + if (!ret) { + cnss_pr_err("Timeout waiting for recovery to complete\n"); + CNSS_ASSERT(0); + } + +skip_wait: cnss_driver_event_post(plat_priv, CNSS_DRIVER_EVENT_UNREGISTER_DRIVER, CNSS_EVENT_SYNC_UNINTERRUPTIBLE, NULL); @@ -840,14 +1340,32 @@ int cnss_pci_unregister_driver_hdlr(struct cnss_pci_data *pci_priv) return 0; } +static int cnss_pci_smmu_fault_handler(struct iommu_domain *domain, + struct device *dev, unsigned long iova, + int flags, void *handler_token) +{ + struct cnss_pci_data *pci_priv = handler_token; + + cnss_pr_err("SMMU fault happened with IOVA 0x%lx\n", iova); + + if (!pci_priv) { + cnss_pr_err("pci_priv is NULL\n"); + return -ENODEV; + } + + cnss_force_fw_assert(&pci_priv->pci_dev->dev); + + /* IOMMU driver requires non-zero return value to print debug info. */ + return -EINVAL; +} + static int cnss_pci_init_smmu(struct cnss_pci_data *pci_priv) { int ret = 0; struct device *dev; struct dma_iommu_mapping *mapping; - int atomic_ctx = 1; - int s1_bypass = 1; - int fast = 1; + int atomic_ctx = 1, s1_bypass = 1, fast = 1, cb_stall_disable = 1, + no_cfre = 1, non_fatal_faults = 1; cnss_pr_dbg("Initializing SMMU\n"); @@ -869,8 +1387,8 @@ static int cnss_pci_init_smmu(struct cnss_pci_data *pci_priv) DOMAIN_ATTR_ATOMIC, &atomic_ctx); if (ret) { - pr_err("Failed to set SMMU atomic_ctx attribute, err = %d\n", - ret); + cnss_pr_err("Failed to set SMMU atomic_ctx attribute, err = %d\n", + ret); goto release_mapping; } @@ -878,24 +1396,54 @@ static int cnss_pci_init_smmu(struct cnss_pci_data *pci_priv) DOMAIN_ATTR_FAST, &fast); if (ret) { - pr_err("Failed to set SMMU fast attribute, err = %d\n", - ret); + cnss_pr_err("Failed to set SMMU fast attribute, err = %d\n", + ret); goto release_mapping; } + + ret = iommu_domain_set_attr(mapping->domain, + DOMAIN_ATTR_CB_STALL_DISABLE, + &cb_stall_disable); + if (ret) { + cnss_pr_err("Failed to set SMMU cb_stall_disable attribute, err = %d\n", + ret); + goto release_mapping; + } + + ret = iommu_domain_set_attr(mapping->domain, + DOMAIN_ATTR_NO_CFRE, + &no_cfre); + if (ret) { + cnss_pr_err("Failed to set SMMU no_cfre attribute, err = %d\n", + ret); + goto release_mapping; + } + + ret = iommu_domain_set_attr(mapping->domain, + DOMAIN_ATTR_NON_FATAL_FAULTS, + &non_fatal_faults); + if (ret) { + cnss_pr_err("Failed to set SMMU non_fatal_faults attribute, err = %d\n", + ret); + goto release_mapping; + } + + iommu_set_fault_handler(mapping->domain, + cnss_pci_smmu_fault_handler, pci_priv); } else { ret = iommu_domain_set_attr(mapping->domain, DOMAIN_ATTR_S1_BYPASS, &s1_bypass); if (ret) { - pr_err("Failed to set SMMU s1_bypass attribute, err = %d\n", - ret); + cnss_pr_err("Failed to set SMMU s1_bypass attribute, err = %d\n", + ret); goto release_mapping; } } ret = arm_iommu_attach_device(dev, mapping); if (ret) { - pr_err("Failed to attach SMMU device, err = %d\n", ret); + cnss_pr_err("Failed to attach SMMU device, err = %d\n", ret); goto release_mapping; } @@ -921,6 +1469,7 @@ static void cnss_pci_event_cb(struct msm_pcie_notify *notify) unsigned long flags; struct pci_dev *pci_dev; struct cnss_pci_data *pci_priv; + struct cnss_plat_data *plat_priv; if (!notify) return; @@ -933,9 +1482,11 @@ static void cnss_pci_event_cb(struct msm_pcie_notify *notify) if (!pci_priv) return; + plat_priv = pci_priv->plat_priv; switch (notify->event) { case MSM_PCIE_EVENT_LINKDOWN: - if (pci_link_down_panic) + if (test_bit(ENABLE_PCI_LINK_DOWN_PANIC, + &plat_priv->ctrl_params.quirks)) panic("cnss: PCI link is down!\n"); spin_lock_irqsave(&pci_link_down_lock, flags); @@ -947,7 +1498,7 @@ static void cnss_pci_event_cb(struct msm_pcie_notify *notify) pci_priv->pci_link_down_ind = true; spin_unlock_irqrestore(&pci_link_down_lock, flags); - cnss_pr_err("PCI link down, schedule recovery!\n"); + cnss_fatal_err("PCI link down, schedule recovery!\n"); if (pci_dev->device == QCA6174_DEVICE_ID) disable_irq(pci_dev->irq); cnss_schedule_recovery(&pci_dev->dev, CNSS_REASON_LINK_DOWN); @@ -996,12 +1547,19 @@ static int cnss_pci_suspend(struct device *dev) struct pci_dev *pci_dev = to_pci_dev(dev); struct cnss_pci_data *pci_priv = cnss_get_pci_priv(pci_dev); struct cnss_wlan_driver *driver_ops; + struct cnss_plat_data *plat_priv; pm_message_t state = { .event = PM_EVENT_SUSPEND }; if (!pci_priv) goto out; + plat_priv = pci_priv->plat_priv; + if (!plat_priv) + goto out; + + set_bit(CNSS_IN_SUSPEND_RESUME, &plat_priv->driver_state); + driver_ops = pci_priv->driver_ops; if (driver_ops && driver_ops->suspend) { ret = driver_ops->suspend(pci_dev, state); @@ -1009,21 +1567,20 @@ static int cnss_pci_suspend(struct device *dev) cnss_pr_err("Failed to suspend host driver, err = %d\n", ret); ret = -EAGAIN; - goto out; + goto clear_flag; } } - if (pci_priv->pci_link_state == PCI_LINK_UP) { - if (pci_priv->device_id != QCA6174_DEVICE_ID) { - ret = cnss_pci_set_mhi_state(pci_priv, - CNSS_MHI_SUSPEND); - if (ret) { - if (driver_ops && driver_ops->resume) - driver_ops->resume(pci_dev); - ret = -EAGAIN; - goto out; - } + if (pci_priv->pci_link_state == PCI_LINK_UP && !pci_priv->disable_pc) { + ret = cnss_pci_set_mhi_state(pci_priv, CNSS_MHI_SUSPEND); + if (ret) { + if (driver_ops && driver_ops->resume) + driver_ops->resume(pci_dev); + ret = -EAGAIN; + goto out; } + + pci_clear_master(pci_dev); cnss_set_pci_config_space(pci_priv, SAVE_PCI_CONFIG_SPACE); pci_disable_device(pci_dev); @@ -1038,6 +1595,8 @@ static int cnss_pci_suspend(struct device *dev) return 0; +clear_flag: + clear_bit(CNSS_IN_SUSPEND_RESUME, &plat_priv->driver_state); out: return ret; } @@ -1047,15 +1606,20 @@ static int cnss_pci_resume(struct device *dev) int ret = 0; struct pci_dev *pci_dev = to_pci_dev(dev); struct cnss_pci_data *pci_priv = cnss_get_pci_priv(pci_dev); + struct cnss_plat_data *plat_priv; struct cnss_wlan_driver *driver_ops; if (!pci_priv) goto out; + plat_priv = pci_priv->plat_priv; + if (!plat_priv) + goto out; + if (pci_priv->pci_link_down_ind) goto out; - if (pci_priv->pci_link_state == PCI_LINK_UP) { + if (pci_priv->pci_link_state == PCI_LINK_UP && !pci_priv->disable_pc) { ret = pci_enable_device(pci_dev); if (ret) cnss_pr_err("Failed to enable PCI device, err = %d\n", @@ -1077,7 +1641,7 @@ static int cnss_pci_resume(struct device *dev) ret); } - return 0; + clear_bit(CNSS_IN_SUSPEND_RESUME, &plat_priv->driver_state); out: return ret; @@ -1097,10 +1661,8 @@ static int cnss_pci_suspend_noirq(struct device *dev) if (driver_ops && driver_ops->suspend_noirq) ret = driver_ops->suspend_noirq(pci_dev); - ret = cnss_set_pci_link(pci_priv, PCI_LINK_DOWN); - if (ret) - goto out; - pci_priv->pci_link_state = PCI_LINK_DOWN; + if (pci_priv->disable_pc && !pci_dev->state_saved) + pci_save_state(pci_dev); out: return ret; @@ -1118,13 +1680,9 @@ static int cnss_pci_resume_noirq(struct device *dev) driver_ops = pci_priv->driver_ops; if (driver_ops && driver_ops->resume_noirq && - !pci_priv->pci_link_down_ind) { - ret = cnss_set_pci_link(pci_priv, PCI_LINK_UP); - if (ret) - goto out; - pci_priv->pci_link_state = PCI_LINK_UP; + !pci_priv->pci_link_down_ind) ret = driver_ops->resume_noirq(pci_dev); - } + out: return ret; } @@ -1150,6 +1708,8 @@ static int cnss_pci_runtime_suspend(struct device *dev) if (driver_ops && driver_ops->runtime_ops && driver_ops->runtime_ops->runtime_suspend) ret = driver_ops->runtime_ops->runtime_suspend(pci_dev); + else + ret = cnss_auto_suspend(dev); cnss_pr_info("Runtime suspend status: %d\n", ret); @@ -1177,6 +1737,8 @@ static int cnss_pci_runtime_resume(struct device *dev) if (driver_ops && driver_ops->runtime_ops && driver_ops->runtime_ops->runtime_resume) ret = driver_ops->runtime_ops->runtime_resume(pci_dev); + else + ret = cnss_auto_resume(dev); cnss_pr_info("Runtime resume status: %d\n", ret); @@ -1195,11 +1757,23 @@ static int cnss_pci_runtime_idle(struct device *dev) int cnss_wlan_pm_control(struct device *dev, bool vote) { struct pci_dev *pci_dev = to_pci_dev(dev); + struct cnss_pci_data *pci_priv = cnss_get_pci_priv(pci_dev); + int ret = 0; - return msm_pcie_pm_control(vote ? MSM_PCIE_DISABLE_PC : - MSM_PCIE_ENABLE_PC, - pci_dev->bus->number, pci_dev, - NULL, PM_OPTIONS_DEFAULT); + if (!pci_priv) + return -ENODEV; + + ret = msm_pcie_pm_control(vote ? MSM_PCIE_DISABLE_PC : + MSM_PCIE_ENABLE_PC, + pci_dev->bus->number, pci_dev, + NULL, PM_OPTIONS_DEFAULT); + if (ret) + return ret; + + pci_priv->disable_pc = vote; + cnss_pr_dbg("%s PCIe power collapse\n", vote ? "disable" : "enable"); + + return 0; } EXPORT_SYMBOL(cnss_wlan_pm_control); @@ -1277,6 +1851,7 @@ int cnss_auto_suspend(struct device *dev) goto out; } + pci_clear_master(pci_dev); cnss_set_pci_config_space(pci_priv, SAVE_PCI_CONFIG_SPACE); pci_disable_device(pci_dev); @@ -1370,7 +1945,6 @@ int cnss_pm_request_resume(struct cnss_pci_data *pci_priv) return pm_request_resume(&pci_dev->dev); } -#ifdef CONFIG_CNSS_QCA6390 int cnss_pci_force_wake_request(struct device *dev) { struct pci_dev *pci_dev = to_pci_dev(dev); @@ -1438,25 +2012,6 @@ int cnss_pci_force_wake_release(struct device *dev) return 0; } EXPORT_SYMBOL(cnss_pci_force_wake_release); -#else -int cnss_pci_force_wake_request(struct device *dev) -{ - return 0; -} -EXPORT_SYMBOL(cnss_pci_force_wake_request); - -int cnss_pci_is_device_awake(struct device *dev) -{ - return true; -} -EXPORT_SYMBOL(cnss_pci_is_device_awake); - -int cnss_pci_force_wake_release(struct device *dev) -{ - return 0; -} -EXPORT_SYMBOL(cnss_pci_force_wake_release); -#endif int cnss_pci_alloc_fw_mem(struct cnss_pci_data *pci_priv) { @@ -1482,6 +2037,63 @@ int cnss_pci_alloc_fw_mem(struct cnss_pci_data *pci_priv) return 0; } +int cnss_pci_alloc_qdss_mem(struct cnss_pci_data *pci_priv) +{ + struct cnss_plat_data *plat_priv = pci_priv->plat_priv; + struct cnss_fw_mem *qdss_mem = plat_priv->qdss_mem; + int i, j; + + for (i = 0; i < plat_priv->qdss_mem_seg_len; i++) { + if (!qdss_mem[i].va && qdss_mem[i].size) { + qdss_mem[i].va = + dma_alloc_coherent(&pci_priv->pci_dev->dev, + qdss_mem[i].size, + &qdss_mem[i].pa, + GFP_KERNEL); + if (!qdss_mem[i].va) { + cnss_pr_err("Failed to allocate QDSS memory for FW, size: 0x%zx, type: %u, chuck-ID: %d\n", + qdss_mem[i].size, + qdss_mem[i].type, i); + break; + } + } + } + + /* Best-effort allocation for QDSS trace */ + if (i < plat_priv->qdss_mem_seg_len) { + for (j = i; j < plat_priv->qdss_mem_seg_len; j++) { + qdss_mem[j].type = 0; + qdss_mem[j].size = 0; + } + plat_priv->qdss_mem_seg_len = i; + } + + return 0; +} + +void cnss_pci_free_qdss_mem(struct cnss_pci_data *pci_priv) +{ + struct cnss_plat_data *plat_priv = pci_priv->plat_priv; + struct cnss_fw_mem *qdss_mem = plat_priv->qdss_mem; + int i; + + for (i = 0; i < plat_priv->qdss_mem_seg_len; i++) { + if (qdss_mem[i].va && qdss_mem[i].size) { + cnss_pr_dbg("Freeing memory for QDSS: pa: %pa, size: 0x%zx, type: %u\n", + &qdss_mem[i].pa, qdss_mem[i].size, + qdss_mem[i].type); + dma_free_coherent(&pci_priv->pci_dev->dev, + qdss_mem[i].size, qdss_mem[i].va, + qdss_mem[i].pa); + qdss_mem[i].va = NULL; + qdss_mem[i].pa = 0; + qdss_mem[i].size = 0; + qdss_mem[i].type = 0; + } + } + plat_priv->qdss_mem_seg_len = 0; +} + static void cnss_pci_free_fw_mem(struct cnss_pci_data *pci_priv) { struct cnss_plat_data *plat_priv = pci_priv->plat_priv; @@ -1510,12 +2122,14 @@ int cnss_pci_load_m3(struct cnss_pci_data *pci_priv) { struct cnss_plat_data *plat_priv = pci_priv->plat_priv; struct cnss_fw_mem *m3_mem = &plat_priv->m3_mem; - char filename[MAX_M3_FILE_NAME_LENGTH]; + char filename[CNSS_FW_PATH_MAX_LEN]; const struct firmware *fw_entry; int ret = 0; if (!m3_mem->va && !m3_mem->size) { - snprintf(filename, sizeof(filename), DEFAULT_M3_FILE_NAME); + snprintf(filename, sizeof(filename), + "%s" DEFAULT_M3_FILE_NAME, + cnss_get_fw_path(plat_priv)); ret = request_firmware(&fw_entry, filename, &pci_priv->pci_dev->dev); @@ -1564,7 +2178,7 @@ void cnss_pci_fw_boot_timeout_hdlr(struct cnss_pci_data *pci_priv) if (!pci_priv) return; - cnss_pr_err("Timeout waiting for FW ready indication\n"); + cnss_fatal_err("Timeout waiting for FW ready indication\n"); cnss_schedule_recovery(&pci_priv->pci_dev->dev, CNSS_REASON_TIMEOUT); @@ -1628,13 +2242,21 @@ EXPORT_SYMBOL(cnss_smmu_map); int cnss_get_soc_info(struct device *dev, struct cnss_soc_info *info) { struct cnss_pci_data *pci_priv = cnss_get_pci_priv(to_pci_dev(dev)); + struct cnss_plat_data *plat_priv; if (!pci_priv) return -ENODEV; + plat_priv = pci_priv->plat_priv; + if (!plat_priv) + return -ENODEV; + info->va = pci_priv->bar; info->pa = pci_resource_start(pci_priv->pci_dev, PCI_BAR_NUM); + memcpy(&info->device_version, &plat_priv->device_version, + sizeof(plat_priv->device_version)); + return 0; } EXPORT_SYMBOL(cnss_get_soc_info); @@ -1698,11 +2320,6 @@ static int cnss_pci_enable_msi(struct cnss_pci_data *pci_priv) } pci_priv->msi_ep_base_data = msi_desc->msg.data; - if (!pci_priv->msi_ep_base_data) { - cnss_pr_err("Got 0 MSI base data!\n"); - CNSS_ASSERT(0); - } - cnss_pr_dbg("MSI base data is %d\n", pci_priv->msi_ep_base_data); return 0; @@ -1887,32 +2504,21 @@ static void cnss_pci_disable_bus(struct cnss_pci_data *pci_priv) pci_disable_device(pci_dev); } -#ifndef CONFIG_MHI_BUS -void cnss_pci_collect_dump_info(struct cnss_pci_data *pci_priv, bool in_panic) +static int cnss_mhi_pm_runtime_get(struct mhi_controller *mhi_ctrl, void *priv) { + struct cnss_pci_data *pci_priv = priv; + + return pm_runtime_get(&pci_priv->pci_dev->dev); } -static inline int cnss_pci_register_mhi(struct cnss_pci_data *pci_priv) +static void cnss_mhi_pm_runtime_put_noidle(struct mhi_controller *mhi_ctrl, + void *priv) { - return -EINVAL; + struct cnss_pci_data *pci_priv = priv; + + pm_runtime_put_noidle(&pci_priv->pci_dev->dev); } -static inline void cnss_pci_unregister_mhi(struct cnss_pci_data *pci_priv) -{ -} - -inline int cnss_pci_set_mhi_state(struct cnss_pci_data *pci_priv, - enum cnss_mhi_state mhi_state) -{ - return -EINVAL; -} - -inline int cnss_pci_force_fw_assert_hdlr(struct cnss_pci_data *pci_priv) -{ - return -EINVAL; -} - -#else static char *cnss_mhi_state_to_str(enum cnss_mhi_state mhi_state) { switch (mhi_state) { @@ -1939,19 +2545,119 @@ static char *cnss_mhi_state_to_str(enum cnss_mhi_state mhi_state) } }; -static int cnss_mhi_pm_runtime_get(struct mhi_controller *mhi_ctrl, void *priv) +static void cnss_pci_dump_qdss_reg(struct cnss_pci_data *pci_priv) { - struct cnss_pci_data *pci_priv = priv; + struct cnss_plat_data *plat_priv = pci_priv->plat_priv; + int i, array_size = ARRAY_SIZE(qdss_csr) - 1; + gfp_t gfp = GFP_KERNEL; + u32 reg_offset; - return pm_runtime_get(&pci_priv->pci_dev->dev); + if (in_interrupt() || irqs_disabled()) + gfp = GFP_ATOMIC; + + if (!plat_priv->qdss_reg) { + plat_priv->qdss_reg = devm_kzalloc(&pci_priv->pci_dev->dev, + sizeof(*plat_priv->qdss_reg) + * array_size, gfp); + if (!plat_priv->qdss_reg) + return; + } + + for (i = 0; qdss_csr[i].name; i++) { + reg_offset = QDSS_APB_DEC_CSR_BASE + qdss_csr[i].offset; + if (cnss_pci_reg_read(pci_priv, reg_offset, + &plat_priv->qdss_reg[i])) + return; + cnss_pr_dbg("%s[0x%x] = 0x%x\n", qdss_csr[i].name, reg_offset, + plat_priv->qdss_reg[i]); + } } -static void cnss_mhi_pm_runtime_put_noidle(struct mhi_controller *mhi_ctrl, - void *priv) +static void cnss_pci_dump_ce_reg(struct cnss_pci_data *pci_priv, + enum cnss_ce_index ce) { - struct cnss_pci_data *pci_priv = priv; + int i; + u32 ce_base = ce * QCA6390_CE_REG_INTERVAL; + u32 reg_offset, val; - pm_runtime_put_noidle(&pci_priv->pci_dev->dev); + switch (ce) { + case CNSS_CE_09: + case CNSS_CE_10: + for (i = 0; ce_src[i].name; i++) { + reg_offset = QCA6390_CE_SRC_RING_REG_BASE + + ce_base + ce_src[i].offset; + if (cnss_pci_reg_read(pci_priv, reg_offset, &val)) + return; + cnss_pr_dbg("CE_%02d_%s[0x%x] = 0x%x\n", + ce, ce_src[i].name, reg_offset, val); + } + + for (i = 0; ce_dst[i].name; i++) { + reg_offset = QCA6390_CE_DST_RING_REG_BASE + + ce_base + ce_dst[i].offset; + if (cnss_pci_reg_read(pci_priv, reg_offset, &val)) + return; + cnss_pr_dbg("CE_%02d_%s[0x%x] = 0x%x\n", + ce, ce_dst[i].name, reg_offset, val); + } + break; + case CNSS_CE_COMMON: + for (i = 0; ce_cmn[i].name; i++) { + reg_offset = QCA6390_CE_COMMON_REG_BASE + + ce_cmn[i].offset; + if (cnss_pci_reg_read(pci_priv, reg_offset, &val)) + return; + cnss_pr_dbg("CE_COMMON_%s[0x%x] = 0x%x\n", + ce_cmn[i].name, reg_offset, val); + } + break; + default: + cnss_pr_err("Unsupported CE[%d] registers dump\n", ce); + } +} + +static void cnss_pci_dump_registers(struct cnss_pci_data *pci_priv) +{ + cnss_pr_dbg("Start to dump debug registers\n"); + + if (cnss_pci_check_link_status(pci_priv)) + return; + + mhi_debug_reg_dump(pci_priv->mhi_ctrl); + cnss_pci_dump_ce_reg(pci_priv, CNSS_CE_COMMON); + cnss_pci_dump_ce_reg(pci_priv, CNSS_CE_09); + cnss_pci_dump_ce_reg(pci_priv, CNSS_CE_10); +} + +int cnss_pci_force_fw_assert_hdlr(struct cnss_pci_data *pci_priv) +{ + int ret; + struct cnss_plat_data *plat_priv; + + if (!pci_priv) + return -ENODEV; + + plat_priv = pci_priv->plat_priv; + if (!plat_priv) + return -ENODEV; + + cnss_pci_dump_shadow_reg(pci_priv); + + ret = cnss_pci_set_mhi_state(pci_priv, CNSS_MHI_TRIGGER_RDDM); + if (ret) { + cnss_fatal_err("Failed to trigger RDDM, err = %d\n", ret); + cnss_pci_dump_registers(pci_priv); + cnss_schedule_recovery(&pci_priv->pci_dev->dev, + CNSS_REASON_DEFAULT); + return ret; + } + + if (!test_bit(CNSS_DEV_ERR_NOTIFY, &plat_priv->driver_state)) { + mod_timer(&plat_priv->fw_boot_timer, + jiffies + msecs_to_jiffies(FW_ASSERT_TIMEOUT)); + } + + return 0; } void cnss_pci_collect_dump_info(struct cnss_pci_data *pci_priv, bool in_panic) @@ -1965,9 +2671,21 @@ void cnss_pci_collect_dump_info(struct cnss_pci_data *pci_priv, bool in_panic) struct cnss_fw_mem *fw_mem = plat_priv->fw_mem; int ret, i; + if (test_bit(CNSS_MHI_RDDM_DONE, &pci_priv->mhi_state)) { + cnss_pr_dbg("RAM dump is already collected, skip\n"); + return; + } + + if (cnss_pci_check_link_status(pci_priv)) + return; + + cnss_pci_dump_qdss_reg(pci_priv); + ret = mhi_download_rddm_img(pci_priv->mhi_ctrl, in_panic); if (ret) { - cnss_pr_err("Failed to download RDDM image, err = %d\n", ret); + cnss_fatal_err("Failed to download RDDM image, err = %d\n", + ret); + cnss_pci_dump_registers(pci_priv); return; } @@ -2030,6 +2748,54 @@ void cnss_pci_collect_dump_info(struct cnss_pci_data *pci_priv, bool in_panic) complete(&plat_priv->rddm_complete); } +void cnss_pci_clear_dump_info(struct cnss_pci_data *pci_priv) +{ + struct cnss_plat_data *plat_priv = pci_priv->plat_priv; + + plat_priv->ramdump_info_v2.dump_data.nentries = 0; + plat_priv->ramdump_info_v2.dump_data_valid = false; +} + +static char *cnss_mhi_notify_status_to_str(enum MHI_CB status) +{ + switch (status) { + case MHI_CB_IDLE: + return "IDLE"; + case MHI_CB_EE_RDDM: + return "RDDM"; + case MHI_CB_SYS_ERROR: + return "SYS_ERROR"; + case MHI_CB_FATAL_ERROR: + return "FATAL_ERROR"; + default: + return "UNKNOWN"; + } +}; + +static void cnss_dev_rddm_timeout_hdlr(unsigned long data) +{ + struct cnss_pci_data *pci_priv = (struct cnss_pci_data *)data; + + if (!pci_priv) + return; + + cnss_fatal_err("Timeout waiting for RDDM notification\n"); + + cnss_schedule_recovery(&pci_priv->pci_dev->dev, CNSS_REASON_TIMEOUT); +} + +static int cnss_mhi_link_status(struct mhi_controller *mhi_ctrl, void *priv) +{ + struct cnss_pci_data *pci_priv = priv; + + if (!pci_priv) { + cnss_pr_err("pci_priv is NULL\n"); + return -EINVAL; + } + + return cnss_pci_check_link_status(pci_priv); +} + static void cnss_mhi_notify_status(struct mhi_controller *mhi_ctrl, void *priv, enum MHI_CB reason) { @@ -2044,15 +2810,27 @@ static void cnss_mhi_notify_status(struct mhi_controller *mhi_ctrl, void *priv, plat_priv = pci_priv->plat_priv; - cnss_pr_dbg("MHI status cb is called with reason %d\n", reason); - - if (test_bit(CNSS_DRIVER_UNLOADING, &plat_priv->driver_state)) { - cnss_pr_dbg("Driver unload is in progress, ignore device error\n"); - return; - } + cnss_pr_dbg("MHI status cb is called with reason %s(%d)\n", + cnss_mhi_notify_status_to_str(reason), reason); switch (reason) { + case MHI_CB_IDLE: + return; + case MHI_CB_FATAL_ERROR: + set_bit(CNSS_DEV_ERR_NOTIFY, &plat_priv->driver_state); + del_timer(&plat_priv->fw_boot_timer); + cnss_pci_update_status(pci_priv, CNSS_FW_DOWN); + cnss_reason = CNSS_REASON_DEFAULT; + break; + case MHI_CB_SYS_ERROR: + set_bit(CNSS_DEV_ERR_NOTIFY, &plat_priv->driver_state); + del_timer(&plat_priv->fw_boot_timer); + mod_timer(&pci_priv->dev_rddm_timer, + jiffies + msecs_to_jiffies(DEV_RDDM_TIMEOUT)); + cnss_pci_update_status(pci_priv, CNSS_FW_DOWN); + return; case MHI_CB_EE_RDDM: + del_timer(&pci_priv->dev_rddm_timer); cnss_reason = CNSS_REASON_RDDM; break; default: @@ -2060,9 +2838,6 @@ static void cnss_mhi_notify_status(struct mhi_controller *mhi_ctrl, void *priv, return; } - set_bit(CNSS_DEV_ERR_NOTIFY, &plat_priv->driver_state); - del_timer(&plat_priv->fw_boot_timer); - cnss_schedule_recovery(&pci_priv->pci_dev->dev, cnss_reason); } @@ -2095,24 +2870,31 @@ static int cnss_pci_get_mhi_msi(struct cnss_pci_data *pci_priv) return 0; } -static int cnss_mhi_link_status(struct mhi_controller *mhi_ctrl, void *priv) +static void cnss_pci_update_fw_name(struct cnss_pci_data *pci_priv) { - struct cnss_pci_data *pci_priv = priv; - u16 device_id; + struct cnss_plat_data *plat_priv = pci_priv->plat_priv; + struct mhi_controller *mhi_ctrl = pci_priv->mhi_ctrl; - if (!pci_priv) { - cnss_pr_err("pci_priv is NULL\n"); - return -EINVAL; + plat_priv->device_version.family_number = mhi_ctrl->family_number; + plat_priv->device_version.device_number = mhi_ctrl->device_number; + plat_priv->device_version.major_version = mhi_ctrl->major_version; + plat_priv->device_version.minor_version = mhi_ctrl->minor_version; + + cnss_pr_dbg("Get device version info, family number: 0x%x, device number: 0x%x, major version: 0x%x, minor version: 0x%x\n", + plat_priv->device_version.family_number, + plat_priv->device_version.device_number, + plat_priv->device_version.major_version, + plat_priv->device_version.minor_version); + + if (pci_priv->device_id == QCA6390_DEVICE_ID && + plat_priv->device_version.major_version >= FW_V2_NUMBER) { + snprintf(plat_priv->firmware_name, + sizeof(plat_priv->firmware_name), + "%s" FW_V2_FILE_NAME, cnss_get_fw_path(plat_priv)); + mhi_ctrl->fw_image = plat_priv->firmware_name; } - pci_read_config_word(pci_priv->pci_dev, PCI_DEVICE_ID, &device_id); - if (device_id != pci_priv->device_id) { - cnss_pr_err("PCI device ID mismatch, link possibly down, current read ID: 0x%x, record ID: 0x%x\n", - device_id, pci_priv->device_id); - return -EIO; - } - - return 0; + cnss_pr_dbg("Firmware name is %s\n", mhi_ctrl->fw_image); } static int cnss_pci_register_mhi(struct cnss_pci_data *pci_priv) @@ -2132,7 +2914,7 @@ static int cnss_pci_register_mhi(struct cnss_pci_data *pci_priv) mhi_ctrl->priv_data = pci_priv; mhi_ctrl->dev = &pci_dev->dev; - mhi_ctrl->of_node = (&plat_priv->plat_dev->dev)->of_node; + mhi_ctrl->of_node = plat_priv->dev_node; mhi_ctrl->dev_id = pci_priv->device_id; mhi_ctrl->domain = pci_domain_nr(pci_dev->bus); mhi_ctrl->bus = pci_dev->bus->number; @@ -2165,7 +2947,11 @@ static int cnss_pci_register_mhi(struct cnss_pci_data *pci_priv) mhi_ctrl->runtime_put = cnss_mhi_pm_runtime_put_noidle; mhi_ctrl->rddm_size = pci_priv->plat_priv->ramdump_info_v2.ramdump_size; - mhi_ctrl->sbl_size = SZ_512K; + if (pci_priv->device_id == QCN7605_DEVICE_ID) + mhi_ctrl->sbl_size = SZ_256K; + else + mhi_ctrl->sbl_size = SZ_512K; + mhi_ctrl->seg_len = SZ_512K; mhi_ctrl->fbc_download = true; @@ -2255,6 +3041,7 @@ static void cnss_pci_set_mhi_state_bit(struct cnss_pci_data *pci_priv, case CNSS_MHI_POWER_OFF: case CNSS_MHI_FORCE_POWER_OFF: clear_bit(CNSS_MHI_POWER_ON, &pci_priv->mhi_state); + clear_bit(CNSS_MHI_TRIGGER_RDDM, &pci_priv->mhi_state); clear_bit(CNSS_MHI_RDDM_DONE, &pci_priv->mhi_state); break; case CNSS_MHI_SUSPEND: @@ -2264,6 +3051,7 @@ static void cnss_pci_set_mhi_state_bit(struct cnss_pci_data *pci_priv, clear_bit(CNSS_MHI_SUSPEND, &pci_priv->mhi_state); break; case CNSS_MHI_TRIGGER_RDDM: + set_bit(CNSS_MHI_TRIGGER_RDDM, &pci_priv->mhi_state); break; case CNSS_MHI_RDDM_DONE: set_bit(CNSS_MHI_RDDM_DONE, &pci_priv->mhi_state); @@ -2346,60 +3134,23 @@ out: return ret; } - -int cnss_pci_force_fw_assert_hdlr(struct cnss_pci_data *pci_priv) -{ - int ret; - struct cnss_plat_data *plat_priv; - - if (!pci_priv) - return -ENODEV; - - plat_priv = pci_priv->plat_priv; - if (!plat_priv) - return -ENODEV; - - if (test_bit(CNSS_MHI_RDDM_DONE, &pci_priv->mhi_state)) - return 0; - - ret = cnss_pci_set_mhi_state(pci_priv, CNSS_MHI_TRIGGER_RDDM); - if (ret) { - cnss_pr_err("Failed to trigger RDDM, err = %d\n", ret); - cnss_schedule_recovery(&pci_priv->pci_dev->dev, - CNSS_REASON_DEFAULT); - return ret; - } - - if (!test_bit(CNSS_DEV_ERR_NOTIFY, &plat_priv->driver_state)) { - mod_timer(&plat_priv->fw_boot_timer, - jiffies + msecs_to_jiffies(FW_ASSERT_TIMEOUT)); - } - - return 0; -} -#endif - -void cnss_pci_clear_dump_info(struct cnss_pci_data *pci_priv) -{ - struct cnss_plat_data *plat_priv = pci_priv->plat_priv; - - plat_priv->ramdump_info_v2.dump_data.nentries = 0; - plat_priv->ramdump_info_v2.dump_data_valid = false; -} - - int cnss_pci_start_mhi(struct cnss_pci_data *pci_priv) { int ret = 0; + struct cnss_plat_data *plat_priv; if (!pci_priv) { cnss_pr_err("pci_priv is NULL!\n"); return -ENODEV; } - if (fbc_bypass) + plat_priv = pci_priv->plat_priv; + if (test_bit(FBC_BYPASS, &plat_priv->ctrl_params.quirks)) return 0; + if (MHI_TIMEOUT_OVERWRITE_MS) + pci_priv->mhi_ctrl->timeout_ms = MHI_TIMEOUT_OVERWRITE_MS; + ret = cnss_pci_set_mhi_state(pci_priv, CNSS_MHI_INIT); if (ret) goto out; @@ -2423,10 +3174,9 @@ void cnss_pci_stop_mhi(struct cnss_pci_data *pci_priv) return; } - if (fbc_bypass) - return; - plat_priv = pci_priv->plat_priv; + if (test_bit(FBC_BYPASS, &plat_priv->ctrl_params.quirks)) + return; cnss_pci_set_mhi_state_bit(pci_priv, CNSS_MHI_RESUME); if (!pci_priv->pci_link_down_ind) @@ -2441,13 +3191,143 @@ void cnss_pci_stop_mhi(struct cnss_pci_data *pci_priv) cnss_pci_set_mhi_state(pci_priv, CNSS_MHI_DEINIT); } +static int cnss_pci_get_dev_cfg_node(struct cnss_plat_data *plat_priv) +{ + struct device_node *child; + u32 id, i; + int id_n, ret; + + if (!plat_priv->is_converged_dt) { + plat_priv->dev_node = plat_priv->plat_dev->dev.of_node; + return 0; + } + + if (!plat_priv->device_id) { + cnss_pr_err("Invalid device id\n"); + return -EINVAL; + } + + for_each_available_child_of_node(plat_priv->plat_dev->dev.of_node, + child) { + if (strcmp(child->name, "chip_cfg")) + continue; + + id_n = of_property_count_u32_elems(child, "supported-ids"); + if (id_n <= 0) { + cnss_pr_err("Device id is NOT set\n"); + return -EINVAL; + } + + for (i = 0; i < id_n; i++) { + ret = of_property_read_u32_index(child, + "supported-ids", + i, &id); + if (ret) { + cnss_pr_err("Failed to read supported ids\n"); + return -EINVAL; + } + + if (id == plat_priv->device_id) { + plat_priv->dev_node = child; + cnss_pr_dbg("got node[%s@%d] for device[0x%x]\n", + child->name, i, id); + return 0; + } + } + } + + return -EINVAL; +} + +/* For converged dt, property 'reg' is declared in sub node, + * won't be parsed during probe. + */ +static int cnss_pci_get_smmu_cfg(struct cnss_plat_data *plat_priv) +{ + struct resource res_tmp; + struct cnss_pci_data *pci_priv; + struct resource *res; + int index; + int ret; + struct device_node *dev_node; + + dev_node = (plat_priv->dev_node ? + plat_priv->dev_node : plat_priv->plat_dev->dev.of_node); + + if (plat_priv->is_converged_dt) { + index = of_property_match_string(dev_node, "reg-names", + "smmu_iova_base"); + if (index < 0) { + ret = -ENODATA; + goto out; + } + ret = of_address_to_resource(dev_node, index, &res_tmp); + if (ret) + goto out; + + res = &res_tmp; + } else { + res = platform_get_resource_byname(plat_priv->plat_dev, + IORESOURCE_MEM, + "smmu_iova_base"); + if (!res) { + ret = -ENODATA; + goto out; + } + } + + pci_priv = plat_priv->bus_priv; + if (of_property_read_bool(dev_node, "qcom,smmu-s1-enable")) + pci_priv->smmu_s1_enable = true; + + pci_priv->smmu_iova_start = res->start; + pci_priv->smmu_iova_len = resource_size(res); + cnss_pr_dbg("smmu_iova_start: %pa, smmu_iova_len: %zu\n", + &pci_priv->smmu_iova_start, + pci_priv->smmu_iova_len); + + if (plat_priv->is_converged_dt) { + index = of_property_match_string(dev_node, "reg-names", + "smmu_iova_ipa"); + if (index < 0) { + ret = -ENODATA; + goto out; + } + + ret = of_address_to_resource(dev_node, index, &res_tmp); + if (ret) + goto out; + + res = &res_tmp; + } else { + res = platform_get_resource_byname(plat_priv->plat_dev, + IORESOURCE_MEM, + "smmu_iova_ipa"); + if (!res) { + ret = -ENODATA; + goto out; + } + } + + pci_priv->smmu_iova_ipa_start = res->start; + pci_priv->smmu_iova_ipa_len = resource_size(res); + cnss_pr_dbg("%s - smmu_iova_ipa_start: %pa, smmu_iova_ipa_len: %zu\n", + (plat_priv->is_converged_dt ? + "converged dt" : "single dt"), + &pci_priv->smmu_iova_ipa_start, + pci_priv->smmu_iova_ipa_len); + return 0; + +out: + return ret; +} + static int cnss_pci_probe(struct pci_dev *pci_dev, const struct pci_device_id *id) { int ret = 0; struct cnss_pci_data *pci_priv; struct cnss_plat_data *plat_priv = cnss_bus_dev_to_plat_priv(NULL); - struct resource *res; cnss_pr_dbg("PCI is probing, vendor ID: 0x%x, device ID: 0x%x\n", id->vendor, pci_dev->device); @@ -2467,8 +3347,16 @@ static int cnss_pci_probe(struct pci_dev *pci_dev, cnss_set_pci_priv(pci_dev, pci_priv); plat_priv->device_id = pci_dev->device; plat_priv->bus_priv = pci_priv; - snprintf(plat_priv->firmware_name, sizeof(plat_priv->firmware_name), - DEFAULT_FW_FILE_NAME); + + ret = cnss_pci_get_dev_cfg_node(plat_priv); + if (ret) { + cnss_pr_err("Failed to get device cfg node, err = %d\n", ret); + goto reset_ctx; + } + + ret = cnss_dev_specific_power_on(plat_priv); + if (ret) + goto reset_ctx; ret = cnss_register_subsys(plat_priv); if (ret) @@ -2478,30 +3366,8 @@ static int cnss_pci_probe(struct pci_dev *pci_dev, if (ret) goto unregister_subsys; - res = platform_get_resource_byname(plat_priv->plat_dev, IORESOURCE_MEM, - "smmu_iova_base"); - if (res) { - if (of_property_read_bool(plat_priv->plat_dev->dev.of_node, - "qcom,smmu-s1-enable")) - pci_priv->smmu_s1_enable = true; - - pci_priv->smmu_iova_start = res->start; - pci_priv->smmu_iova_len = resource_size(res); - cnss_pr_dbg("smmu_iova_start: %pa, smmu_iova_len: %zu\n", - &pci_priv->smmu_iova_start, - pci_priv->smmu_iova_len); - - res = platform_get_resource_byname(plat_priv->plat_dev, - IORESOURCE_MEM, - "smmu_iova_ipa"); - if (res) { - pci_priv->smmu_iova_ipa_start = res->start; - pci_priv->smmu_iova_ipa_len = resource_size(res); - cnss_pr_dbg("smmu_iova_ipa_start: %pa, smmu_iova_ipa_len: %zu\n", - &pci_priv->smmu_iova_ipa_start, - pci_priv->smmu_iova_ipa_len); - } - + ret = cnss_pci_get_smmu_cfg(plat_priv); + if (!ret) { ret = cnss_pci_init_smmu(pci_priv); if (ret) { cnss_pr_err("Failed to init SMMU, err = %d\n", ret); @@ -2519,6 +3385,8 @@ static int cnss_pci_probe(struct pci_dev *pci_dev, if (ret) goto dereg_pci_event; + cnss_pci_disable_l1(pci_priv); + pci_save_state(pci_dev); pci_priv->default_state = pci_store_saved_state(pci_dev); @@ -2532,18 +3400,36 @@ static int cnss_pci_probe(struct pci_dev *pci_dev, ret); cnss_power_off_device(plat_priv); break; - case QCA6290_EMULATION_DEVICE_ID: case QCA6290_DEVICE_ID: - case QCA6390_EMULATION_DEVICE_ID: case QCA6390_DEVICE_ID: + cnss_pci_set_wlaon_pwr_ctrl(pci_priv, false, false, false); + case QCN7605_DEVICE_ID: + setup_timer(&pci_priv->dev_rddm_timer, + cnss_dev_rddm_timeout_hdlr, + (unsigned long)pci_priv); + ret = cnss_pci_enable_msi(pci_priv); if (ret) goto disable_bus; + + snprintf(plat_priv->firmware_name, + sizeof(plat_priv->firmware_name), + "%s" DEFAULT_FW_FILE_NAME, + cnss_get_fw_path(plat_priv)); + ret = cnss_pci_register_mhi(pci_priv); if (ret) { cnss_pci_disable_msi(pci_priv); goto disable_bus; } + /* Update fw name according to different chip subtype */ + cnss_pci_update_fw_name(pci_priv); + + if (EMULATION_HW) + break; + if (pci_dev->device != QCN7605_DEVICE_ID) + cnss_pci_set_wlaon_pwr_ctrl(pci_priv, false, + true, false); ret = cnss_suspend_pci_link(pci_priv); if (ret) cnss_pr_err("Failed to suspend PCI link, err = %d\n", @@ -2584,14 +3470,14 @@ static void cnss_pci_remove(struct pci_dev *pci_dev) cnss_pci_free_m3_mem(pci_priv); cnss_pci_free_fw_mem(pci_priv); + cnss_pci_free_qdss_mem(pci_priv); switch (pci_dev->device) { - case QCA6290_EMULATION_DEVICE_ID: case QCA6290_DEVICE_ID: - case QCA6390_EMULATION_DEVICE_ID: case QCA6390_DEVICE_ID: cnss_pci_unregister_mhi(pci_priv); cnss_pci_disable_msi(pci_priv); + del_timer(&pci_priv->dev_rddm_timer); break; default: break; @@ -2610,12 +3496,9 @@ static void cnss_pci_remove(struct pci_dev *pci_dev) static const struct pci_device_id cnss_pci_id_table[] = { { QCA6174_VENDOR_ID, QCA6174_DEVICE_ID, PCI_ANY_ID, PCI_ANY_ID }, - { QCA6290_EMULATION_VENDOR_ID, QCA6290_EMULATION_DEVICE_ID, - PCI_ANY_ID, PCI_ANY_ID }, { QCA6290_VENDOR_ID, QCA6290_DEVICE_ID, PCI_ANY_ID, PCI_ANY_ID }, - { QCA6390_VENDOR_ID, QCA6390_EMULATION_DEVICE_ID, PCI_ANY_ID, - PCI_ANY_ID }, { QCA6390_VENDOR_ID, QCA6390_DEVICE_ID, PCI_ANY_ID, PCI_ANY_ID }, + { QCN7605_VENDOR_ID, QCN7605_DEVICE_ID, PCI_ANY_ID, PCI_ANY_ID}, { 0 } }; MODULE_DEVICE_TABLE(pci, cnss_pci_id_table); @@ -2643,6 +3526,7 @@ int cnss_pci_init(struct cnss_plat_data *plat_priv) int ret = 0; struct device *dev = &plat_priv->plat_dev->dev; u32 rc_num; + int retry = 0; ret = of_property_read_u32(dev->of_node, "qcom,wlan-rc-num", &rc_num); if (ret) { @@ -2650,11 +3534,17 @@ int cnss_pci_init(struct cnss_plat_data *plat_priv) goto out; } +retry: ret = msm_pcie_enumerate(rc_num); if (ret) { cnss_pr_err("Failed to enable PCIe RC%x, err = %d\n", rc_num, ret); - goto out; + if (retry++ < LINK_TRAINING_RETRY_MAX_TIMES) { + cnss_pr_dbg("Retry PCI link training #%d\n", retry); + goto retry; + } else { + goto out; + } } ret = pci_register_driver(&cnss_pci_driver); @@ -2664,7 +3554,16 @@ int cnss_pci_init(struct cnss_plat_data *plat_priv) goto out; } + if (!plat_priv->bus_priv) { + cnss_pr_err("Failed to probe pci driver\n"); + ret = -ENODEV; + goto deinit; + } + return 0; + +deinit: + pci_unregister_driver(&cnss_pci_driver); out: return ret; } diff --git a/drivers/net/wireless/cnss2/pci.h b/drivers/net/wireless/cnss2/pci.h index 8c0a3688b6a1..790151875bc1 100644 --- a/drivers/net/wireless/cnss2/pci.h +++ b/drivers/net/wireless/cnss2/pci.h @@ -1,4 +1,4 @@ -/* Copyright (c) 2016-2019, The Linux Foundation. All rights reserved. +/* Copyright (c) 2016-2020, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -46,6 +46,16 @@ struct cnss_msi_config { struct cnss_msi_user *users; }; +struct cnss_pci_reg { + char *name; + u32 offset; +}; + +struct cnss_pci_debug_reg { + u32 offset; + u32 val; +}; + struct cnss_pci_data { struct pci_dev *pci_dev; struct cnss_plat_data *plat_priv; @@ -70,10 +80,12 @@ struct cnss_pci_data { void __iomem *bar; struct cnss_msi_config *msi_config; u32 msi_ep_base_data; -#ifdef CONFIG_MHI_BUS struct mhi_controller *mhi_ctrl; -#endif unsigned long mhi_state; + u32 remap_window; + struct timer_list dev_rddm_timer; + bool disable_pc; + struct cnss_pci_debug_reg *debug_reg; }; static inline void cnss_set_pci_priv(struct pci_dev *pci_dev, void *data) @@ -126,6 +138,8 @@ int cnss_resume_pci_link(struct cnss_pci_data *pci_priv); int cnss_pci_init(struct cnss_plat_data *plat_priv); void cnss_pci_deinit(struct cnss_plat_data *plat_priv); int cnss_pci_alloc_fw_mem(struct cnss_pci_data *pci_priv); +int cnss_pci_alloc_qdss_mem(struct cnss_pci_data *pci_priv); +void cnss_pci_free_qdss_mem(struct cnss_pci_data *pci_priv); int cnss_pci_load_m3(struct cnss_pci_data *pci_priv); int cnss_pci_set_mhi_state(struct cnss_pci_data *pci_priv, enum cnss_mhi_state state); diff --git a/drivers/net/wireless/cnss2/power.c b/drivers/net/wireless/cnss2/power.c index 8ed1507bde11..d402015ba8bd 100644 --- a/drivers/net/wireless/cnss2/power.c +++ b/drivers/net/wireless/cnss2/power.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2016-2017, The Linux Foundation. All rights reserved. +/* Copyright (c) 2016-2020, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -18,19 +18,25 @@ #include "main.h" #include "debug.h" -static struct cnss_vreg_info cnss_vreg_info[] = { - {NULL, "vdd-wlan-core", 1300000, 1300000, 0, 0}, - {NULL, "vdd-wlan-io", 1800000, 1800000, 0, 0}, - {NULL, "vdd-wlan-xtal-aon", 0, 0, 0, 0}, - {NULL, "vdd-wlan-xtal", 1800000, 1800000, 0, 2}, - {NULL, "vdd-wlan", 0, 0, 0, 0}, - {NULL, "vdd-wlan-sp2t", 2700000, 2700000, 0, 0}, - {NULL, "wlan-ant-switch", 2700000, 2700000, 20000, 0}, - {NULL, "wlan-soc-swreg", 1200000, 1200000, 0, 0}, - {NULL, "vdd-wlan-en", 0, 0, 0, 10}, +static struct cnss_vreg_cfg cnss_vreg_list[] = { + {"vdd-wlan-core", 1300000, 1300000, 0, 0}, + {"vdd-wlan-io", 1800000, 1800000, 0, 0}, + {"vdd-wlan-xtal-aon", 0, 0, 0, 0}, + {"vdd-wlan-xtal", 1800000, 1800000, 0, 2}, + {"vdd-wlan", 0, 0, 0, 0}, + {"vdd-wlan-aon", 1055000, 1055000, 0, 0}, + {"vdd-wlan-rfa1", 1350000, 1350000, 0, 0}, + {"vdd-wlan-rfa2", 2040000, 2040000, 0, 0}, + {"vdd-wlan-rfa3", 1900000, 1900000, 0, 0}, + {"vdd-wlan-ctrl1", 0, 0, 0, 0}, + {"vdd-wlan-ctrl2", 0, 0, 0, 0}, + {"vdd-wlan-sp2t", 2700000, 2700000, 0, 0}, + {"wlan-ant-switch", 2700000, 2700000, 20000, 0}, + {"wlan-soc-swreg", 1200000, 1200000, 0, 0}, + {"vdd-wlan-en", 0, 0, 0, 10}, }; -#define CNSS_VREG_INFO_SIZE ARRAY_SIZE(cnss_vreg_info) +#define CNSS_VREG_INFO_SIZE ARRAY_SIZE(cnss_vreg_list) #define MAX_PROP_SIZE 32 #define BOOTSTRAP_GPIO "qcom,enable-bootstrap-gpio" @@ -38,71 +44,114 @@ static struct cnss_vreg_info cnss_vreg_info[] = { #define WLAN_EN_GPIO "wlan-en-gpio" #define WLAN_EN_ACTIVE "wlan_en_active" #define WLAN_EN_SLEEP "wlan_en_sleep" +#define WLAN_VREGS_PROP "wlan_vregs" #define BOOTSTRAP_DELAY 1000 #define WLAN_ENABLE_DELAY 1000 +/* For converged dt node, get the required vregs from property 'wlan_vregs', + * which is string array; if the property is present but no value is set, + * means no additional wlan verg is required. + * For non-converged dt, go through all vregs in static array 'cnss_vreg_list'. + */ int cnss_get_vreg(struct cnss_plat_data *plat_priv) { int ret = 0; int i; - struct cnss_vreg_info *vreg_info; + struct cnss_vreg_info *vreg; struct device *dev; struct regulator *reg; const __be32 *prop; - char prop_name[MAX_PROP_SIZE]; - int len; + char prop_name[MAX_PROP_SIZE] = {0}; + int len, id_n; + struct device_node *dt_node; - dev = &plat_priv->plat_dev->dev; - - plat_priv->vreg_info = devm_kzalloc(dev, sizeof(cnss_vreg_info), - GFP_KERNEL); - if (!plat_priv->vreg_info) { - ret = -ENOMEM; - goto out; + if (!list_empty(&plat_priv->vreg_list) && + !plat_priv->is_converged_dt) { + cnss_pr_dbg("Vregs have already been updated\n"); + return 0; } - memcpy(plat_priv->vreg_info, cnss_vreg_info, sizeof(cnss_vreg_info)); + dev = &plat_priv->plat_dev->dev; + dt_node = (plat_priv->dev_node ? plat_priv->dev_node : dev->of_node); - for (i = 0; i < CNSS_VREG_INFO_SIZE; i++) { - vreg_info = &plat_priv->vreg_info[i]; - reg = devm_regulator_get_optional(dev, vreg_info->name); - if (IS_ERR(reg)) { - ret = PTR_ERR(reg); - if (ret == -ENODEV) - continue; - else if (ret == -EPROBE_DEFER) - cnss_pr_info("EPROBE_DEFER for regulator: %s\n", - vreg_info->name); - else - cnss_pr_err("Failed to get regulator %s, err = %d\n", - vreg_info->name, ret); + if (plat_priv->is_converged_dt) { + id_n = of_property_count_strings(dt_node, WLAN_VREGS_PROP); + if (id_n <= 0) { + if (id_n == -ENODATA) { + cnss_pr_dbg("No additional vregs for: %s:%lx\n", + dt_node->name, + plat_priv->device_id); + return 0; + } + + cnss_pr_err("property %s is invalid or missed: %s:%lx\n", + WLAN_VREGS_PROP, dt_node->name, + plat_priv->device_id); + return -EINVAL; + } + } else { + id_n = CNSS_VREG_INFO_SIZE; + } + + for (i = 0; i < id_n; i++) { + vreg = devm_kzalloc(dev, sizeof(*vreg), GFP_KERNEL); + if (!vreg) { + ret = -ENOMEM; goto out; } - vreg_info->reg = reg; + if (plat_priv->is_converged_dt) { + ret = of_property_read_string_index(dt_node, + WLAN_VREGS_PROP, i, + &vreg->cfg.name); + if (ret) { + devm_kfree(dev, vreg); + cnss_pr_err("Failed to read vreg ids\n"); + goto out; + } + } else { + memcpy(&vreg->cfg, &cnss_vreg_list[i], + sizeof(vreg->cfg)); + } + reg = devm_regulator_get_optional(dev, vreg->cfg.name); + if (IS_ERR(reg)) { + ret = PTR_ERR(reg); + if (ret == -ENODEV) { + devm_kfree(dev, vreg); + continue; + } + else if (ret == -EPROBE_DEFER) + cnss_pr_info("EPROBE_DEFER for regulator: %s\n", + vreg->cfg.name); + else + cnss_pr_err("Failed to get regulator %s, err = %d\n", + vreg->cfg.name, ret); + + devm_kfree(dev, vreg); + goto out; + } + + vreg->reg = reg; snprintf(prop_name, MAX_PROP_SIZE, "qcom,%s-info", - vreg_info->name); - - prop = of_get_property(dev->of_node, prop_name, &len); - cnss_pr_dbg("Got regulator info, name: %s, len: %d\n", - prop_name, len); - + vreg->cfg.name); + prop = of_get_property(dt_node, prop_name, &len); if (!prop || len != (4 * sizeof(__be32))) { cnss_pr_dbg("Property %s %s, use default\n", prop_name, prop ? "invalid format" : "doesn't exist"); } else { - vreg_info->min_uv = be32_to_cpup(&prop[0]); - vreg_info->max_uv = be32_to_cpup(&prop[1]); - vreg_info->load_ua = be32_to_cpup(&prop[2]); - vreg_info->delay_us = be32_to_cpup(&prop[3]); + vreg->cfg.min_uv = be32_to_cpup(&prop[0]); + vreg->cfg.max_uv = be32_to_cpup(&prop[1]); + vreg->cfg.load_ua = be32_to_cpup(&prop[2]); + vreg->cfg.delay_us = be32_to_cpup(&prop[3]); } + list_add_tail(&vreg->list, &plat_priv->vreg_list); cnss_pr_dbg("Got regulator: %s, min_uv: %u, max_uv: %u, load_ua: %u, delay_us: %u\n", - vreg_info->name, vreg_info->min_uv, - vreg_info->max_uv, vreg_info->load_ua, - vreg_info->delay_us); + vreg->cfg.name, vreg->cfg.min_uv, + vreg->cfg.max_uv, vreg->cfg.load_ua, + vreg->cfg.delay_us); } return 0; @@ -110,121 +159,148 @@ out: return ret; } +void cnss_put_vreg(struct cnss_plat_data *plat_priv) +{ + struct device *dev; + struct cnss_vreg_info *vreg; + + dev = &plat_priv->plat_dev->dev; + while (!list_empty(&plat_priv->vreg_list)) { + vreg = list_first_entry(&plat_priv->vreg_list, + struct cnss_vreg_info, list); + list_del(&vreg->list); + + if (IS_ERR_OR_NULL(vreg->reg)) + continue; + + cnss_pr_dbg("Put regulator: %s\n", vreg->cfg.name); + devm_regulator_put(vreg->reg); + devm_kfree(dev, vreg); + } +} + static int cnss_vreg_on(struct cnss_plat_data *plat_priv) { int ret = 0; - struct cnss_vreg_info *vreg_info; - int i; + struct cnss_vreg_info *vreg; if (!plat_priv) { cnss_pr_err("plat_priv is NULL!\n"); return -ENODEV; } - for (i = 0; i < CNSS_VREG_INFO_SIZE; i++) { - vreg_info = &plat_priv->vreg_info[i]; - - if (!vreg_info->reg) + list_for_each_entry(vreg, &plat_priv->vreg_list, list) { + if (IS_ERR_OR_NULL(vreg->reg)) continue; - cnss_pr_dbg("Regulator %s is being enabled\n", vreg_info->name); + if (vreg->enabled) { + cnss_pr_dbg("Regulator %s is already enabled\n", + vreg->cfg.name); + continue; + } - if (vreg_info->min_uv != 0 && vreg_info->max_uv != 0) { - ret = regulator_set_voltage(vreg_info->reg, - vreg_info->min_uv, - vreg_info->max_uv); + cnss_pr_dbg("Regulator %s is being enabled\n", + vreg->cfg.name); + + if (vreg->cfg.min_uv != 0 && vreg->cfg.max_uv != 0) { + ret = regulator_set_voltage(vreg->reg, + vreg->cfg.min_uv, + vreg->cfg.max_uv); if (ret) { cnss_pr_err("Failed to set voltage for regulator %s, min_uv: %u, max_uv: %u, err = %d\n", - vreg_info->name, vreg_info->min_uv, - vreg_info->max_uv, ret); + vreg->cfg.name, + vreg->cfg.min_uv, + vreg->cfg.max_uv, ret); break; } } - if (vreg_info->load_ua) { - ret = regulator_set_load(vreg_info->reg, - vreg_info->load_ua); + if (vreg->cfg.load_ua) { + ret = regulator_set_load(vreg->reg, vreg->cfg.load_ua); if (ret < 0) { cnss_pr_err("Failed to set load for regulator %s, load: %u, err = %d\n", - vreg_info->name, vreg_info->load_ua, + vreg->cfg.name, vreg->cfg.load_ua, ret); break; } } - if (vreg_info->delay_us) - udelay(vreg_info->delay_us); + if (vreg->cfg.delay_us) + udelay(vreg->cfg.delay_us); - ret = regulator_enable(vreg_info->reg); + ret = regulator_enable(vreg->reg); if (ret) { cnss_pr_err("Failed to enable regulator %s, err = %d\n", - vreg_info->name, ret); + vreg->cfg.name, ret); break; } + vreg->enabled = true; } - if (ret) { - for (; i >= 0; i--) { - vreg_info = &plat_priv->vreg_info[i]; + if (!ret) + return 0; - if (!vreg_info->reg) - continue; + list_for_each_entry_continue_reverse(vreg, &plat_priv->vreg_list, + list) { + if (IS_ERR_OR_NULL(vreg->reg) || !vreg->enabled) + continue; - regulator_disable(vreg_info->reg); - if (vreg_info->load_ua) - regulator_set_load(vreg_info->reg, 0); - if (vreg_info->min_uv != 0 && vreg_info->max_uv != 0) - regulator_set_voltage(vreg_info->reg, 0, - vreg_info->max_uv); - } - - return ret; + regulator_disable(vreg->reg); + if (vreg->cfg.load_ua) + regulator_set_load(vreg->reg, 0); + if (vreg->cfg.min_uv != 0 && vreg->cfg.max_uv != 0) + regulator_set_voltage(vreg->reg, 0, vreg->cfg.max_uv); + vreg->enabled = false; } - return 0; + return ret; } static int cnss_vreg_off(struct cnss_plat_data *plat_priv) { int ret = 0; - struct cnss_vreg_info *vreg_info; - int i; + struct cnss_vreg_info *vreg; if (!plat_priv) { cnss_pr_err("plat_priv is NULL!\n"); return -ENODEV; } - for (i = CNSS_VREG_INFO_SIZE - 1; i >= 0; i--) { - vreg_info = &plat_priv->vreg_info[i]; - - if (!vreg_info->reg) + list_for_each_entry_reverse(vreg, &plat_priv->vreg_list, list) { + if (IS_ERR_OR_NULL(vreg->reg)) continue; - cnss_pr_dbg("Regulator %s is being disabled\n", - vreg_info->name); + if (!vreg->enabled) { + cnss_pr_dbg("Regulator %s is already disabled\n", + vreg->cfg.name); + continue; + } - ret = regulator_disable(vreg_info->reg); + cnss_pr_dbg("Regulator %s is being disabled\n", + vreg->cfg.name); + + ret = regulator_disable(vreg->reg); if (ret) cnss_pr_err("Failed to disable regulator %s, err = %d\n", - vreg_info->name, ret); + vreg->cfg.name, ret); - if (vreg_info->load_ua) { - ret = regulator_set_load(vreg_info->reg, 0); + if (vreg->cfg.load_ua) { + ret = regulator_set_load(vreg->reg, 0); if (ret < 0) cnss_pr_err("Failed to set load for regulator %s, err = %d\n", - vreg_info->name, ret); + vreg->cfg.name, ret); } - if (vreg_info->min_uv != 0 && vreg_info->max_uv != 0) { - ret = regulator_set_voltage(vreg_info->reg, 0, - vreg_info->max_uv); + if (vreg->cfg.min_uv != 0 && vreg->cfg.max_uv != 0) { + ret = regulator_set_voltage(vreg->reg, 0, + vreg->cfg.max_uv); if (ret) cnss_pr_err("Failed to set voltage for regulator %s, err = %d\n", - vreg_info->name, ret); + vreg->cfg.name, ret); } + vreg->enabled = false; } return ret; @@ -285,6 +361,18 @@ out: return ret; } +void cnss_put_pinctrl(struct cnss_plat_data *plat_priv) +{ + struct pinctrl *pinctrl; + + pinctrl = plat_priv->pinctrl_info.pinctrl; + if (IS_ERR_OR_NULL(pinctrl)) + return; + + devm_pinctrl_put(pinctrl); + memset(&plat_priv->pinctrl_info, 0, sizeof(plat_priv->pinctrl_info)); +} + static int cnss_select_pinctrl_state(struct cnss_plat_data *plat_priv, bool state) { @@ -300,10 +388,15 @@ static int cnss_select_pinctrl_state(struct cnss_plat_data *plat_priv, pinctrl_info = &plat_priv->pinctrl_info; if (state) { + if (pinctrl_info->activated) { + cnss_pr_dbg("Pinctrl is already activated\n"); + goto out; + } + if (!IS_ERR_OR_NULL(pinctrl_info->bootstrap_active)) { - ret = pinctrl_select_state(pinctrl_info->pinctrl, - pinctrl_info-> - bootstrap_active); + ret = pinctrl_select_state( + pinctrl_info->pinctrl, + pinctrl_info->bootstrap_active); if (ret) { cnss_pr_err("Failed to select bootstrap active state, err = %d\n", ret); @@ -313,9 +406,9 @@ static int cnss_select_pinctrl_state(struct cnss_plat_data *plat_priv, } if (!IS_ERR_OR_NULL(pinctrl_info->wlan_en_active)) { - ret = pinctrl_select_state(pinctrl_info->pinctrl, - pinctrl_info-> - wlan_en_active); + ret = pinctrl_select_state( + pinctrl_info->pinctrl, + pinctrl_info->wlan_en_active); if (ret) { cnss_pr_err("Failed to select wlan_en active state, err = %d\n", ret); @@ -323,7 +416,13 @@ static int cnss_select_pinctrl_state(struct cnss_plat_data *plat_priv, } udelay(WLAN_ENABLE_DELAY); } + pinctrl_info->activated = true; } else { + if (!pinctrl_info->activated) { + cnss_pr_dbg("Pinctrl is already de-activated\n"); + goto out; + } + if (!IS_ERR_OR_NULL(pinctrl_info->wlan_en_sleep)) { ret = pinctrl_select_state(pinctrl_info->pinctrl, pinctrl_info->wlan_en_sleep); @@ -333,6 +432,7 @@ static int cnss_select_pinctrl_state(struct cnss_plat_data *plat_priv, goto out; } } + pinctrl_info->activated = false; } return 0; @@ -384,3 +484,18 @@ void cnss_set_pin_connect_status(struct cnss_plat_data *plat_priv) plat_priv->pin_result.host_pin_result = pin_status; } + +/* If it's converged dt, get device specific regulators and enable them. */ +int cnss_dev_specific_power_on(struct cnss_plat_data *plat_priv) +{ + int ret; + + if (!plat_priv->is_converged_dt) + return 0; + + ret = cnss_get_vreg(plat_priv); + if (ret) + return ret; + + return cnss_power_on_device(plat_priv); +} diff --git a/drivers/net/wireless/cnss2/qmi.c b/drivers/net/wireless/cnss2/qmi.c index e4d71a7c2bdc..5392bf3c1f82 100644 --- a/drivers/net/wireless/cnss2/qmi.c +++ b/drivers/net/wireless/cnss2/qmi.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2015-2018, The Linux Foundation. All rights reserved. +/* Copyright (c) 2015-2018, 2020 The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -22,18 +22,14 @@ #define WLFW_SERVICE_INS_ID_V01 1 #define WLFW_CLIENT_ID 0x4b4e454c -#define MAX_BDF_FILE_NAME 11 +#define BDF_FILE_NAME_PREFIX "bdwlan" #define ELF_BDF_FILE_NAME "bdwlan.elf" #define ELF_BDF_FILE_NAME_PREFIX "bdwlan.e" #define BIN_BDF_FILE_NAME "bdwlan.bin" #define BIN_BDF_FILE_NAME_PREFIX "bdwlan.b" +#define REGDB_FILE_NAME "regdb.bin" #define DUMMY_BDF_FILE_NAME "bdwlan.dmy" - -enum cnss_bdf_type { - CNSS_BDF_BIN, - CNSS_BDF_ELF, - CNSS_BDF_DUMMY = 255, -}; +#define CE_MSI_NAME "CE" #ifdef CONFIG_CNSS2_DEBUG static unsigned int qmi_timeout = 10000; @@ -249,6 +245,13 @@ static int cnss_wlfw_ind_register_send_sync(struct cnss_plat_data *plat_priv) req.initiate_cal_update_enable_valid = 1; req.initiate_cal_update_enable = 1; + req.qdss_trace_req_mem_enable_valid = 1; + req.qdss_trace_req_mem_enable = 1; + req.qdss_trace_save_enable_valid = 1; + req.qdss_trace_save_enable = 1; + req.qdss_trace_free_enable_valid = 1; + req.qdss_trace_free_enable = 1; + req_desc.max_msg_len = WLFW_IND_REGISTER_REQ_MSG_V01_MAX_MSG_LEN; req_desc.msg_id = QMI_WLFW_IND_REGISTER_REQ_V01; req_desc.ei_array = wlfw_ind_register_req_msg_v01_ei; @@ -512,56 +515,80 @@ out: return ret; } -int cnss_wlfw_bdf_dnld_send_sync(struct cnss_plat_data *plat_priv) +int cnss_wlfw_bdf_dnld_send_sync(struct cnss_plat_data *plat_priv, + u32 bdf_type) { struct wlfw_bdf_download_req_msg_v01 *req; struct wlfw_bdf_download_resp_msg_v01 resp; struct msg_desc req_desc, resp_desc; - char filename[MAX_BDF_FILE_NAME]; + char filename[CNSS_FW_PATH_MAX_LEN]; const struct firmware *fw_entry; const u8 *temp; unsigned int remaining; int ret = 0; + const char *fw_path; cnss_pr_dbg("Sending BDF download message, state: 0x%lx\n", plat_priv->driver_state); req = kzalloc(sizeof(*req), GFP_KERNEL); - if (!req) { - ret = -ENOMEM; - goto out; - } + if (!req) + return -ENOMEM; + fw_path = cnss_get_fw_path(plat_priv); switch (bdf_type) { case CNSS_BDF_ELF: if (plat_priv->board_info.board_id == 0xFF) - snprintf(filename, sizeof(filename), ELF_BDF_FILE_NAME); + snprintf(filename, sizeof(filename), + "%s" ELF_BDF_FILE_NAME, fw_path); + else if (plat_priv->board_info.board_id < 0xFF) + snprintf(filename, sizeof(filename), + "%s" ELF_BDF_FILE_NAME_PREFIX "%02x", + fw_path, plat_priv->board_info.board_id); else snprintf(filename, sizeof(filename), - ELF_BDF_FILE_NAME_PREFIX "%02x", - plat_priv->board_info.board_id); + "%s" BDF_FILE_NAME_PREFIX "%02x.e%02x", + fw_path, + plat_priv->board_info.board_id >> 8 & 0xFF, + plat_priv->board_info.board_id & 0xFF); break; case CNSS_BDF_BIN: if (plat_priv->board_info.board_id == 0xFF) - snprintf(filename, sizeof(filename), BIN_BDF_FILE_NAME); + snprintf(filename, sizeof(filename), + "%s" BIN_BDF_FILE_NAME, fw_path); + else if (plat_priv->board_info.board_id < 0xFF) + snprintf(filename, sizeof(filename), + "%s" BIN_BDF_FILE_NAME_PREFIX "%02x", + fw_path, plat_priv->board_info.board_id); else snprintf(filename, sizeof(filename), - BIN_BDF_FILE_NAME_PREFIX "%02x", - plat_priv->board_info.board_id); + "%s" BDF_FILE_NAME_PREFIX "%02x.b%02x", + fw_path, + plat_priv->board_info.board_id >> 8 & 0xFF, + plat_priv->board_info.board_id & 0xFF); + break; + case CNSS_BDF_REGDB: + snprintf(filename, sizeof(filename), REGDB_FILE_NAME); break; case CNSS_BDF_DUMMY: cnss_pr_dbg("CNSS_BDF_DUMMY is set, sending dummy BDF\n"); - snprintf(filename, sizeof(filename), DUMMY_BDF_FILE_NAME); + snprintf(filename, sizeof(filename), + "%s" DUMMY_BDF_FILE_NAME, fw_path); temp = DUMMY_BDF_FILE_NAME; - remaining = MAX_BDF_FILE_NAME; + remaining = CNSS_FW_PATH_MAX_LEN; goto bypass_bdf; default: - cnss_pr_err("Invalid BDF type: %d\n", bdf_type); + cnss_pr_err("Invalid BDF type: %d\n", + plat_priv->ctrl_params.bdf_type); ret = -EINVAL; goto err_req_fw; } - - ret = request_firmware(&fw_entry, filename, &plat_priv->plat_dev->dev); + if (bdf_type == CNSS_BDF_REGDB) + ret = request_firmware_direct(&fw_entry, filename, + &plat_priv->plat_dev->dev); + else + ret = request_firmware(&fw_entry, filename, + &plat_priv->plat_dev->dev); if (ret) { cnss_pr_err("Failed to load BDF: %s\n", filename); goto err_req_fw; @@ -593,7 +620,7 @@ bypass_bdf: req->data_valid = 1; req->end_valid = 1; req->bdf_type_valid = 1; - req->bdf_type = bdf_type; + req->bdf_type = plat_priv->ctrl_params.bdf_type; if (remaining > QMI_WLFW_MAX_DATA_SIZE_V01) { req->data_len = QMI_WLFW_MAX_DATA_SIZE_V01; @@ -608,7 +635,7 @@ bypass_bdf: req, sizeof(*req), &resp_desc, &resp, sizeof(resp), QMI_WLFW_TIMEOUT_MS); if (ret < 0) { - cnss_pr_err("Failed to send BDF download request, err = %d\n", + cnss_pr_err("Failed to wait for response of BDF download request, err: %d\n", ret); goto err_send; } @@ -625,14 +652,19 @@ bypass_bdf: req->seg_id++; } -err_send: if (bdf_type != CNSS_BDF_DUMMY) release_firmware(fw_entry); -err_req_fw: + kfree(req); -out: - if (ret) + return 0; + +err_send: + if (plat_priv->ctrl_params.bdf_type != CNSS_BDF_DUMMY) + release_firmware(fw_entry); +err_req_fw: + if (bdf_type != CNSS_BDF_REGDB) CNSS_ASSERT(0); + kfree(req); return ret; } @@ -763,7 +795,7 @@ int cnss_wlfw_wlan_cfg_send_sync(struct cnss_plat_data *plat_priv, struct wlfw_wlan_cfg_req_msg_v01 req; struct wlfw_wlan_cfg_resp_msg_v01 resp; struct msg_desc req_desc, resp_desc; - u32 i; + u32 i, ce_id, num_vectors, user_base_data, base_vector; int ret = 0; cnss_pr_dbg("Sending WLAN config message, state: 0x%lx\n", @@ -803,6 +835,18 @@ int cnss_wlfw_wlan_cfg_send_sync(struct cnss_plat_data *plat_priv, req.svc_cfg[i].pipe_num = config->ce_svc_cfg[i].pipe_num; } + if (config->num_shadow_reg_cfg) { + req.shadow_reg_valid = 1; + if (config->num_shadow_reg_cfg > + QMI_WLFW_MAX_NUM_SHADOW_REG_V01) + req.shadow_reg_len = QMI_WLFW_MAX_NUM_SHADOW_REG_V01; + else + req.shadow_reg_len = config->num_shadow_reg_cfg; + memcpy(req.shadow_reg, config->shadow_reg_cfg, + sizeof(struct wlfw_shadow_reg_cfg_s_v01) + * req.shadow_reg_len); + } + req.shadow_reg_v2_valid = 1; if (config->num_shadow_reg_v2_cfg > QMI_WLFW_MAX_NUM_SHADOW_REG_V2_V01) @@ -814,6 +858,31 @@ int cnss_wlfw_wlan_cfg_send_sync(struct cnss_plat_data *plat_priv, sizeof(struct wlfw_shadow_reg_v2_cfg_s_v01) * req.shadow_reg_v2_len); + if (config->rri_over_ddr_cfg_valid) { + req.rri_over_ddr_cfg_valid = 1; + req.rri_over_ddr_cfg.base_addr_low = + config->rri_over_ddr_cfg.base_addr_low; + req.rri_over_ddr_cfg.base_addr_high = + config->rri_over_ddr_cfg.base_addr_high; + } + if (config->send_msi_ce) { + ret = cnss_get_msi_assignment(plat_priv, + CE_MSI_NAME, + &num_vectors, + &user_base_data, + &base_vector); + if (!ret) { + req.msi_cfg_valid = 1; + req.msi_cfg_len = QMI_WLFW_MAX_NUM_CE_V01; + for (ce_id = 0; ce_id < QMI_WLFW_MAX_NUM_CE_V01; + ce_id++) { + req.msi_cfg[ce_id].ce_id = ce_id; + req.msi_cfg[ce_id].msi_vector = + (ce_id % num_vectors) + base_vector; + } + } + } + req_desc.max_msg_len = WLFW_WLAN_CFG_REQ_MSG_V01_MAX_MSG_LEN; req_desc.msg_id = QMI_WLFW_WLAN_CFG_REQ_V01; req_desc.ei_array = wlfw_wlan_cfg_req_msg_v01_ei; @@ -896,7 +965,7 @@ int cnss_wlfw_athdiag_read_send_sync(struct cnss_plat_data *plat_priv, } if (resp->resp.result != QMI_RESULT_SUCCESS_V01) { - cnss_pr_err("athdiag read request failed, result: %d, err: %d\n", + cnss_pr_err("Athdiag read request failed, result: %d, err: %d\n", resp->resp.result, resp->resp.error); ret = resp->resp.result; goto out; @@ -1031,8 +1100,9 @@ out: return ret; } -int cnss_wlfw_cal_report_send_sync(struct cnss_plat_data *plat_priv) +int cnss_wlfw_qdss_trace_mem_info_send_sync(struct cnss_plat_data *plat_priv) { + /* not ready in kernel 4.9 */ return 0; } @@ -1087,7 +1157,7 @@ static void cnss_wlfw_clnt_ind(struct qmi_handle *handle, } } -unsigned int cnss_get_qmi_timeout(void) +unsigned int cnss_get_qmi_timeout(struct cnss_plat_data *plat_priv) { cnss_pr_dbg("QMI timeout is %u ms\n", QMI_WLFW_TIMEOUT_MS); diff --git a/drivers/net/wireless/cnss2/qmi.h b/drivers/net/wireless/cnss2/qmi.h index 4a71216abad4..4689802b83e4 100644 --- a/drivers/net/wireless/cnss2/qmi.h +++ b/drivers/net/wireless/cnss2/qmi.h @@ -1,4 +1,4 @@ -/* Copyright (c) 2015-2018, The Linux Foundation. All rights reserved. +/* Copyright (c) 2015-2020, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -15,17 +15,33 @@ struct cnss_plat_data; +#define QDSS_TRACE_SEG_LEN_MAX 32 +#define QDSS_TRACE_FILE_NAME_MAX 16 + +struct cnss_mem_seg { + u64 addr; + u32 size; +}; + +struct cnss_qmi_event_qdss_trace_save_data { + u32 total_size; + u32 mem_seg_len; + struct cnss_mem_seg mem_seg[QDSS_TRACE_SEG_LEN_MAX]; + char file_name[QDSS_TRACE_FILE_NAME_MAX + 1]; +}; + #ifdef CONFIG_CNSS2_QMI #include "wlan_firmware_service_v01.h" int cnss_qmi_init(struct cnss_plat_data *plat_priv); void cnss_qmi_deinit(struct cnss_plat_data *plat_priv); -unsigned int cnss_get_qmi_timeout(void); +unsigned int cnss_get_qmi_timeout(struct cnss_plat_data *plat_priv); int cnss_wlfw_server_arrive(struct cnss_plat_data *plat_priv); int cnss_wlfw_server_exit(struct cnss_plat_data *plat_priv); int cnss_wlfw_respond_mem_send_sync(struct cnss_plat_data *plat_priv); int cnss_wlfw_tgt_cap_send_sync(struct cnss_plat_data *plat_priv); -int cnss_wlfw_bdf_dnld_send_sync(struct cnss_plat_data *plat_priv); +int cnss_wlfw_bdf_dnld_send_sync(struct cnss_plat_data *plat_priv, + u32 bdf_type); int cnss_wlfw_m3_dnld_send_sync(struct cnss_plat_data *plat_priv); int cnss_wlfw_wlan_mode_send_sync(struct cnss_plat_data *plat_priv, enum cnss_driver_mode mode); @@ -40,8 +56,7 @@ int cnss_wlfw_athdiag_write_send_sync(struct cnss_plat_data *plat_priv, u32 data_len, u8 *data); int cnss_wlfw_ini_send_sync(struct cnss_plat_data *plat_priv, u8 fw_log_mode); -int cnss_wlfw_cal_report_send_sync(struct cnss_plat_data *plat_priv); - +int cnss_wlfw_qdss_trace_mem_info_send_sync(struct cnss_plat_data *plat_priv); #else #define QMI_WLFW_TIMEOUT_MS 10000 @@ -54,12 +69,14 @@ static inline void cnss_qmi_deinit(struct cnss_plat_data *plat_priv) { } -static inline unsigned int cnss_get_qmi_timeout(void) +static inline +unsigned int cnss_get_qmi_timeout(struct cnss_plat_data *plat_priv) { return QMI_WLFW_TIMEOUT_MS; } -static inline int cnss_wlfw_server_arrive(struct cnss_plat_data *plat_priv) +static inline int cnss_wlfw_server_arrive(struct cnss_plat_data *plat_priv, + void *data) { return 0; } @@ -80,7 +97,8 @@ static inline int cnss_wlfw_tgt_cap_send_sync(struct cnss_plat_data *plat_priv) return 0; } -static inline int cnss_wlfw_bdf_dnld_send_sync(struct cnss_plat_data *plat_priv) +static inline int cnss_wlfw_bdf_dnld_send_sync(struct cnss_plat_data *plat_priv, + u32 bdf_type) { return 0; } @@ -129,10 +147,10 @@ int cnss_wlfw_ini_send_sync(struct cnss_plat_data *plat_priv, } static inline -int cnss_wlfw_cal_report_send_sync(struct cnss_plat_data *plat_priv) +int cnss_wlfw_qdss_trace_mem_info_send_sync(struct cnss_plat_data *plat_priv) { return 0; } - #endif /* CONFIG_CNSS2_QMI */ + #endif /* _CNSS_QMI_H */ diff --git a/drivers/net/wireless/cnss2/utils.c b/drivers/net/wireless/cnss2/utils.c index 9ffe386e3677..e621b64d25f4 100644 --- a/drivers/net/wireless/cnss2/utils.c +++ b/drivers/net/wireless/cnss2/utils.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2016-2017, The Linux Foundation. All rights reserved. +/* Copyright (c) 2016-2017, 2020 The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -31,7 +31,7 @@ static struct cnss_dfs_nol_info { int cnss_set_wlan_unsafe_channel(u16 *unsafe_ch_list, u16 ch_count) { mutex_lock(&unsafe_channel_list_lock); - if ((!unsafe_ch_list) || (ch_count > CNSS_MAX_CH_NUM)) { + if (!unsafe_ch_list || ch_count > CNSS_MAX_CH_NUM) { mutex_unlock(&unsafe_channel_list_lock); return -EINVAL; } diff --git a/drivers/net/wireless/cnss2/wlan_firmware_service_v01.c b/drivers/net/wireless/cnss2/wlan_firmware_service_v01.c index 5c60f529d11f..0f75ac373daf 100644 --- a/drivers/net/wireless/cnss2/wlan_firmware_service_v01.c +++ b/drivers/net/wireless/cnss2/wlan_firmware_service_v01.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2015-2018, The Linux Foundation. All rights reserved. +/* Copyright (c) 2015-2020, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -144,6 +144,57 @@ static struct elem_info wlfw_shadow_reg_v2_cfg_s_v01_ei[] = { }, }; +static struct elem_info wlfw_rri_over_ddr_cfg_s_v01_ei[] = { + { + .data_type = QMI_UNSIGNED_4_BYTE, + .elem_len = 1, + .elem_size = sizeof(u32), + .is_array = NO_ARRAY, + .tlv_type = 0, + .offset = offsetof(struct wlfw_rri_over_ddr_cfg_s_v01, + base_addr_low), + }, + { + .data_type = QMI_UNSIGNED_4_BYTE, + .elem_len = 1, + .elem_size = sizeof(u32), + .is_array = NO_ARRAY, + .tlv_type = 0, + .offset = offsetof(struct wlfw_rri_over_ddr_cfg_s_v01, + base_addr_high), + }, + { + .data_type = QMI_EOTI, + .is_array = NO_ARRAY, + .tlv_type = QMI_COMMON_TLV_TYPE, + }, +}; + +static struct elem_info wlfw_msi_cfg_s_v01_ei[] = { + { + .data_type = QMI_UNSIGNED_2_BYTE, + .elem_len = 1, + .elem_size = sizeof(u16), + .is_array = NO_ARRAY, + .tlv_type = 0, + .offset = offsetof(struct wlfw_msi_cfg_s_v01, + ce_id), + }, + { + .data_type = QMI_UNSIGNED_2_BYTE, + .elem_len = 1, + .elem_size = sizeof(u16), + .is_array = NO_ARRAY, + .tlv_type = 0, + .offset = offsetof(struct wlfw_msi_cfg_s_v01, + msi_vector), + }, + { + .data_type = QMI_EOTI, + .is_array = NO_ARRAY, + .tlv_type = QMI_COMMON_TLV_TYPE, + }, +}; static struct elem_info wlfw_memory_region_info_s_v01_ei[] = { { .data_type = QMI_UNSIGNED_8_BYTE, @@ -607,6 +658,60 @@ struct elem_info wlfw_ind_register_req_msg_v01_ei[] = { .offset = offsetof(struct wlfw_ind_register_req_msg_v01, cal_done_enable), }, + { + .data_type = QMI_OPT_FLAG, + .elem_len = 1, + .elem_size = sizeof(u8), + .is_array = NO_ARRAY, + .tlv_type = 0x1C, + .offset = offsetof(struct wlfw_ind_register_req_msg_v01, + qdss_trace_req_mem_enable_valid), + }, + { + .data_type = QMI_UNSIGNED_1_BYTE, + .elem_len = 1, + .elem_size = sizeof(u8), + .is_array = NO_ARRAY, + .tlv_type = 0x1C, + .offset = offsetof(struct wlfw_ind_register_req_msg_v01, + qdss_trace_req_mem_enable), + }, + { + .data_type = QMI_OPT_FLAG, + .elem_len = 1, + .elem_size = sizeof(u8), + .is_array = NO_ARRAY, + .tlv_type = 0x1D, + .offset = offsetof(struct wlfw_ind_register_req_msg_v01, + qdss_trace_save_enable_valid), + }, + { + .data_type = QMI_UNSIGNED_1_BYTE, + .elem_len = 1, + .elem_size = sizeof(u8), + .is_array = NO_ARRAY, + .tlv_type = 0x1D, + .offset = offsetof(struct wlfw_ind_register_req_msg_v01, + qdss_trace_save_enable), + }, + { + .data_type = QMI_OPT_FLAG, + .elem_len = 1, + .elem_size = sizeof(u8), + .is_array = NO_ARRAY, + .tlv_type = 0x1E, + .offset = offsetof(struct wlfw_ind_register_req_msg_v01, + qdss_trace_free_enable_valid), + }, + { + .data_type = QMI_UNSIGNED_1_BYTE, + .elem_len = 1, + .elem_size = sizeof(u8), + .is_array = NO_ARRAY, + .tlv_type = 0x1E, + .offset = offsetof(struct wlfw_ind_register_req_msg_v01, + qdss_trace_free_enable), + }, { .data_type = QMI_EOTI, .is_array = NO_ARRAY, @@ -921,6 +1026,53 @@ struct elem_info wlfw_wlan_cfg_req_msg_v01_ei[] = { shadow_reg_v2), .ei_array = wlfw_shadow_reg_v2_cfg_s_v01_ei, }, + { + .data_type = QMI_OPT_FLAG, + .elem_len = 1, + .elem_size = sizeof(u8), + .is_array = NO_ARRAY, + .tlv_type = 0x15, + .offset = offsetof(struct wlfw_wlan_cfg_req_msg_v01, + rri_over_ddr_cfg_valid), + }, + { + .data_type = QMI_STRUCT, + .elem_len = 1, + .elem_size = sizeof(struct wlfw_rri_over_ddr_cfg_s_v01), + .is_array = NO_ARRAY, + .tlv_type = 0x15, + .offset = offsetof(struct wlfw_wlan_cfg_req_msg_v01, + rri_over_ddr_cfg), + .ei_array = wlfw_rri_over_ddr_cfg_s_v01_ei, + }, + { + .data_type = QMI_OPT_FLAG, + .elem_len = 1, + .elem_size = sizeof(u8), + .is_array = NO_ARRAY, + .tlv_type = 0x16, + .offset = offsetof(struct wlfw_wlan_cfg_req_msg_v01, + msi_cfg_valid), + }, + { + .data_type = QMI_DATA_LEN, + .elem_len = 1, + .elem_size = sizeof(u8), + .is_array = NO_ARRAY, + .tlv_type = 0x16, + .offset = offsetof(struct wlfw_wlan_cfg_req_msg_v01, + msi_cfg_len), + }, + { + .data_type = QMI_STRUCT, + .elem_len = QMI_WLFW_MAX_NUM_CE_V01, + .elem_size = sizeof(struct wlfw_msi_cfg_s_v01), + .is_array = VAR_LEN_ARRAY, + .tlv_type = 0x16, + .offset = offsetof(struct wlfw_wlan_cfg_req_msg_v01, + msi_cfg), + .ei_array = wlfw_msi_cfg_s_v01_ei, + }, { .data_type = QMI_EOTI, .is_array = NO_ARRAY, @@ -1077,6 +1229,42 @@ struct elem_info wlfw_cap_resp_msg_v01_ei[] = { .offset = offsetof(struct wlfw_cap_resp_msg_v01, num_macs), }, + { + .data_type = QMI_OPT_FLAG, + .elem_len = 1, + .elem_size = sizeof(u8), + .is_array = NO_ARRAY, + .tlv_type = 0x16, + .offset = offsetof(struct wlfw_cap_resp_msg_v01, + voltage_mv_valid), + }, + { + .data_type = QMI_UNSIGNED_4_BYTE, + .elem_len = 1, + .elem_size = sizeof(u32), + .is_array = NO_ARRAY, + .tlv_type = 0x16, + .offset = offsetof(struct wlfw_cap_resp_msg_v01, + voltage_mv), + }, + { + .data_type = QMI_OPT_FLAG, + .elem_len = 1, + .elem_size = sizeof(u8), + .is_array = NO_ARRAY, + .tlv_type = 0x17, + .offset = offsetof(struct wlfw_cap_resp_msg_v01, + time_freq_hz_valid), + }, + { + .data_type = QMI_UNSIGNED_4_BYTE, + .elem_len = 1, + .elem_size = sizeof(u32), + .is_array = NO_ARRAY, + .tlv_type = 0x17, + .offset = offsetof(struct wlfw_cap_resp_msg_v01, + time_freq_hz), + }, { .data_type = QMI_EOTI, .is_array = NO_ARRAY, @@ -1316,9 +1504,9 @@ struct elem_info wlfw_initiate_cal_download_ind_msg_v01_ei[] = { .elem_size = sizeof(u8), .is_array = NO_ARRAY, .tlv_type = 0x10, - .offset = offsetof( - struct wlfw_initiate_cal_download_ind_msg_v01, - total_size_valid), + .offset = offsetof(struct + wlfw_initiate_cal_download_ind_msg_v01, + total_size_valid), }, { .data_type = QMI_UNSIGNED_4_BYTE, @@ -1326,9 +1514,9 @@ struct elem_info wlfw_initiate_cal_download_ind_msg_v01_ei[] = { .elem_size = sizeof(u32), .is_array = NO_ARRAY, .tlv_type = 0x10, - .offset = offsetof( - struct wlfw_initiate_cal_download_ind_msg_v01, - total_size), + .offset = offsetof(struct + wlfw_initiate_cal_download_ind_msg_v01, + total_size), }, { .data_type = QMI_OPT_FLAG, @@ -1336,9 +1524,9 @@ struct elem_info wlfw_initiate_cal_download_ind_msg_v01_ei[] = { .elem_size = sizeof(u8), .is_array = NO_ARRAY, .tlv_type = 0x11, - .offset = offsetof( - struct wlfw_initiate_cal_download_ind_msg_v01, - cal_data_location_valid), + .offset = offsetof(struct + wlfw_initiate_cal_download_ind_msg_v01, + cal_data_location_valid), }, { .data_type = QMI_UNSIGNED_4_BYTE, @@ -1346,9 +1534,9 @@ struct elem_info wlfw_initiate_cal_download_ind_msg_v01_ei[] = { .elem_size = sizeof(u32), .is_array = NO_ARRAY, .tlv_type = 0x11, - .offset = offsetof( - struct wlfw_initiate_cal_download_ind_msg_v01, - cal_data_location), + .offset = offsetof(struct + wlfw_initiate_cal_download_ind_msg_v01, + cal_data_location), }, { .data_type = QMI_EOTI, @@ -1538,8 +1726,8 @@ struct elem_info wlfw_initiate_cal_update_ind_msg_v01_ei[] = { .is_array = NO_ARRAY, .tlv_type = 0x10, .offset = offsetof(struct - wlfw_initiate_cal_update_ind_msg_v01, - cal_data_location_valid), + wlfw_initiate_cal_update_ind_msg_v01, + cal_data_location_valid), }, { .data_type = QMI_UNSIGNED_4_BYTE, @@ -1548,8 +1736,8 @@ struct elem_info wlfw_initiate_cal_update_ind_msg_v01_ei[] = { .is_array = NO_ARRAY, .tlv_type = 0x10, .offset = offsetof(struct - wlfw_initiate_cal_update_ind_msg_v01, - cal_data_location), + wlfw_initiate_cal_update_ind_msg_v01, + cal_data_location), }, { .data_type = QMI_EOTI, @@ -2719,3 +2907,554 @@ struct elem_info wlfw_cal_done_ind_msg_v01_ei[] = { .tlv_type = QMI_COMMON_TLV_TYPE, }, }; + +struct elem_info wlfw_qdss_trace_req_mem_ind_msg_v01_ei[] = { + { + .data_type = QMI_DATA_LEN, + .elem_len = 1, + .elem_size = sizeof(u8), + .is_array = NO_ARRAY, + .tlv_type = 0x01, + .offset = offsetof(struct + wlfw_qdss_trace_req_mem_ind_msg_v01, + mem_seg_len), + }, + { + .data_type = QMI_STRUCT, + .elem_len = QMI_WLFW_MAX_NUM_MEM_SEG_V01, + .elem_size = sizeof(struct wlfw_mem_seg_s_v01), + .is_array = VAR_LEN_ARRAY, + .tlv_type = 0x01, + .offset = offsetof(struct + wlfw_qdss_trace_req_mem_ind_msg_v01, + mem_seg), + .ei_array = wlfw_mem_seg_s_v01_ei, + }, + { + .data_type = QMI_EOTI, + .is_array = NO_ARRAY, + .tlv_type = QMI_COMMON_TLV_TYPE, + }, +}; + +struct elem_info wlfw_qdss_trace_mem_info_req_msg_v01_ei[] = { + { + .data_type = QMI_DATA_LEN, + .elem_len = 1, + .elem_size = sizeof(u8), + .is_array = NO_ARRAY, + .tlv_type = 0x01, + .offset = offsetof(struct + wlfw_qdss_trace_mem_info_req_msg_v01, + mem_seg_len), + }, + { + .data_type = QMI_STRUCT, + .elem_len = QMI_WLFW_MAX_NUM_MEM_SEG_V01, + .elem_size = sizeof(struct wlfw_mem_seg_resp_s_v01), + .is_array = VAR_LEN_ARRAY, + .tlv_type = 0x01, + .offset = offsetof(struct + wlfw_qdss_trace_mem_info_req_msg_v01, + mem_seg), + .ei_array = wlfw_mem_seg_resp_s_v01_ei, + }, + { + .data_type = QMI_EOTI, + .is_array = NO_ARRAY, + .tlv_type = QMI_COMMON_TLV_TYPE, + }, +}; + +struct elem_info wlfw_qdss_trace_mem_info_resp_msg_v01_ei[] = { + { + .data_type = QMI_STRUCT, + .elem_len = 1, + .elem_size = sizeof(struct qmi_response_type_v01), + .is_array = NO_ARRAY, + .tlv_type = 0x02, + .offset = offsetof(struct + wlfw_qdss_trace_mem_info_resp_msg_v01, + resp), + .ei_array = qmi_response_type_v01_ei, + }, + { + .data_type = QMI_EOTI, + .is_array = NO_ARRAY, + .tlv_type = QMI_COMMON_TLV_TYPE, + }, +}; + +struct elem_info wlfw_qdss_trace_save_ind_msg_v01_ei[] = { + { + .data_type = QMI_UNSIGNED_4_BYTE, + .elem_len = 1, + .elem_size = sizeof(u32), + .is_array = NO_ARRAY, + .tlv_type = 0x01, + .offset = offsetof(struct + wlfw_qdss_trace_save_ind_msg_v01, + source), + }, + { + .data_type = QMI_UNSIGNED_4_BYTE, + .elem_len = 1, + .elem_size = sizeof(u32), + .is_array = NO_ARRAY, + .tlv_type = 0x02, + .offset = offsetof(struct + wlfw_qdss_trace_save_ind_msg_v01, + total_size), + }, + { + .data_type = QMI_OPT_FLAG, + .elem_len = 1, + .elem_size = sizeof(u8), + .is_array = NO_ARRAY, + .tlv_type = 0x10, + .offset = offsetof(struct + wlfw_qdss_trace_save_ind_msg_v01, + mem_seg_valid), + }, + { + .data_type = QMI_DATA_LEN, + .elem_len = 1, + .elem_size = sizeof(u8), + .is_array = NO_ARRAY, + .tlv_type = 0x10, + .offset = offsetof(struct + wlfw_qdss_trace_save_ind_msg_v01, + mem_seg_len), + }, + { + .data_type = QMI_STRUCT, + .elem_len = QMI_WLFW_MAX_NUM_MEM_SEG_V01, + .elem_size = sizeof(struct wlfw_mem_seg_resp_s_v01), + .is_array = VAR_LEN_ARRAY, + .tlv_type = 0x10, + .offset = offsetof(struct + wlfw_qdss_trace_save_ind_msg_v01, + mem_seg), + .ei_array = wlfw_mem_seg_resp_s_v01_ei, + }, + { + .data_type = QMI_OPT_FLAG, + .elem_len = 1, + .elem_size = sizeof(u8), + .is_array = NO_ARRAY, + .tlv_type = 0x11, + .offset = offsetof(struct + wlfw_qdss_trace_save_ind_msg_v01, + file_name_valid), + }, + { + .data_type = QMI_STRING, + .elem_len = QMI_WLFW_MAX_STR_LEN_V01 + 1, + .elem_size = sizeof(char), + .is_array = NO_ARRAY, + .tlv_type = 0x11, + .offset = offsetof(struct + wlfw_qdss_trace_save_ind_msg_v01, + file_name), + }, + { + .data_type = QMI_EOTI, + .is_array = NO_ARRAY, + .tlv_type = QMI_COMMON_TLV_TYPE, + }, +}; + +struct elem_info wlfw_qdss_trace_data_req_msg_v01_ei[] = { + { + .data_type = QMI_UNSIGNED_4_BYTE, + .elem_len = 1, + .elem_size = sizeof(u32), + .is_array = NO_ARRAY, + .tlv_type = 0x01, + .offset = offsetof(struct + wlfw_qdss_trace_data_req_msg_v01, + seg_id), + }, + { + .data_type = QMI_EOTI, + .is_array = NO_ARRAY, + .tlv_type = QMI_COMMON_TLV_TYPE, + }, +}; + +struct elem_info wlfw_qdss_trace_data_resp_msg_v01_ei[] = { + { + .data_type = QMI_STRUCT, + .elem_len = 1, + .elem_size = sizeof(struct qmi_response_type_v01), + .is_array = NO_ARRAY, + .tlv_type = 0x02, + .offset = offsetof(struct + wlfw_qdss_trace_data_resp_msg_v01, + resp), + .ei_array = qmi_response_type_v01_ei, + }, + { + .data_type = QMI_OPT_FLAG, + .elem_len = 1, + .elem_size = sizeof(u8), + .is_array = NO_ARRAY, + .tlv_type = 0x10, + .offset = offsetof(struct + wlfw_qdss_trace_data_resp_msg_v01, + total_size_valid), + }, + { + .data_type = QMI_UNSIGNED_4_BYTE, + .elem_len = 1, + .elem_size = sizeof(u32), + .is_array = NO_ARRAY, + .tlv_type = 0x10, + .offset = offsetof(struct + wlfw_qdss_trace_data_resp_msg_v01, + total_size), + }, + { + .data_type = QMI_OPT_FLAG, + .elem_len = 1, + .elem_size = sizeof(u8), + .is_array = NO_ARRAY, + .tlv_type = 0x11, + .offset = offsetof(struct + wlfw_qdss_trace_data_resp_msg_v01, + seg_id_valid), + }, + { + .data_type = QMI_UNSIGNED_4_BYTE, + .elem_len = 1, + .elem_size = sizeof(u32), + .is_array = NO_ARRAY, + .tlv_type = 0x11, + .offset = offsetof(struct + wlfw_qdss_trace_data_resp_msg_v01, + seg_id), + }, + { + .data_type = QMI_OPT_FLAG, + .elem_len = 1, + .elem_size = sizeof(u8), + .is_array = NO_ARRAY, + .tlv_type = 0x12, + .offset = offsetof(struct + wlfw_qdss_trace_data_resp_msg_v01, + data_valid), + }, + { + .data_type = QMI_DATA_LEN, + .elem_len = 1, + .elem_size = sizeof(u16), + .is_array = NO_ARRAY, + .tlv_type = 0x12, + .offset = offsetof(struct + wlfw_qdss_trace_data_resp_msg_v01, + data_len), + }, + { + .data_type = QMI_UNSIGNED_1_BYTE, + .elem_len = QMI_WLFW_MAX_DATA_SIZE_V01, + .elem_size = sizeof(u8), + .is_array = VAR_LEN_ARRAY, + .tlv_type = 0x12, + .offset = offsetof(struct + wlfw_qdss_trace_data_resp_msg_v01, + data), + }, + { + .data_type = QMI_OPT_FLAG, + .elem_len = 1, + .elem_size = sizeof(u8), + .is_array = NO_ARRAY, + .tlv_type = 0x13, + .offset = offsetof(struct + wlfw_qdss_trace_data_resp_msg_v01, + end_valid), + }, + { + .data_type = QMI_UNSIGNED_1_BYTE, + .elem_len = 1, + .elem_size = sizeof(u8), + .is_array = NO_ARRAY, + .tlv_type = 0x13, + .offset = offsetof(struct + wlfw_qdss_trace_data_resp_msg_v01, + end), + }, + { + .data_type = QMI_EOTI, + .is_array = NO_ARRAY, + .tlv_type = QMI_COMMON_TLV_TYPE, + }, +}; + +struct elem_info wlfw_qdss_trace_config_download_req_msg_v01_ei[] = { + { + .data_type = QMI_OPT_FLAG, + .elem_len = 1, + .elem_size = sizeof(u8), + .is_array = NO_ARRAY, + .tlv_type = 0x10, + .offset = offsetof(struct + wlfw_qdss_trace_config_download_req_msg_v01, + total_size_valid), + }, + { + .data_type = QMI_UNSIGNED_4_BYTE, + .elem_len = 1, + .elem_size = sizeof(u32), + .is_array = NO_ARRAY, + .tlv_type = 0x10, + .offset = offsetof(struct + wlfw_qdss_trace_config_download_req_msg_v01, + total_size), + }, + { + .data_type = QMI_OPT_FLAG, + .elem_len = 1, + .elem_size = sizeof(u8), + .is_array = NO_ARRAY, + .tlv_type = 0x11, + .offset = offsetof(struct + wlfw_qdss_trace_config_download_req_msg_v01, + seg_id_valid), + }, + { + .data_type = QMI_UNSIGNED_4_BYTE, + .elem_len = 1, + .elem_size = sizeof(u32), + .is_array = NO_ARRAY, + .tlv_type = 0x11, + .offset = offsetof(struct + wlfw_qdss_trace_config_download_req_msg_v01, + seg_id), + }, + { + .data_type = QMI_OPT_FLAG, + .elem_len = 1, + .elem_size = sizeof(u8), + .is_array = NO_ARRAY, + .tlv_type = 0x12, + .offset = offsetof(struct + wlfw_qdss_trace_config_download_req_msg_v01, + data_valid), + }, + { + .data_type = QMI_DATA_LEN, + .elem_len = 1, + .elem_size = sizeof(u16), + .is_array = NO_ARRAY, + .tlv_type = 0x12, + .offset = offsetof(struct + wlfw_qdss_trace_config_download_req_msg_v01, + data_len), + }, + { + .data_type = QMI_UNSIGNED_1_BYTE, + .elem_len = QMI_WLFW_MAX_DATA_SIZE_V01, + .elem_size = sizeof(u8), + .is_array = VAR_LEN_ARRAY, + .tlv_type = 0x12, + .offset = offsetof(struct + wlfw_qdss_trace_config_download_req_msg_v01, + data), + }, + { + .data_type = QMI_OPT_FLAG, + .elem_len = 1, + .elem_size = sizeof(u8), + .is_array = NO_ARRAY, + .tlv_type = 0x13, + .offset = offsetof(struct + wlfw_qdss_trace_config_download_req_msg_v01, + end_valid), + }, + { + .data_type = QMI_UNSIGNED_1_BYTE, + .elem_len = 1, + .elem_size = sizeof(u8), + .is_array = NO_ARRAY, + .tlv_type = 0x13, + .offset = offsetof(struct + wlfw_qdss_trace_config_download_req_msg_v01, + end), + }, + { + .data_type = QMI_EOTI, + .is_array = NO_ARRAY, + .tlv_type = QMI_COMMON_TLV_TYPE, + }, +}; + +struct elem_info wlfw_qdss_trace_config_download_resp_msg_v01_ei[] = { + { + .data_type = QMI_STRUCT, + .elem_len = 1, + .elem_size = sizeof(struct qmi_response_type_v01), + .is_array = NO_ARRAY, + .tlv_type = 0x02, + .offset = offsetof(struct + wlfw_qdss_trace_config_download_resp_msg_v01, + resp), + .ei_array = qmi_response_type_v01_ei, + }, + { + .data_type = QMI_EOTI, + .is_array = NO_ARRAY, + .tlv_type = QMI_COMMON_TLV_TYPE, + }, +}; + +struct elem_info wlfw_qdss_trace_mode_req_msg_v01_ei[] = { + { + .data_type = QMI_OPT_FLAG, + .elem_len = 1, + .elem_size = sizeof(u8), + .is_array = NO_ARRAY, + .tlv_type = 0x10, + .offset = offsetof(struct + wlfw_qdss_trace_mode_req_msg_v01, + mode_valid), + }, + { + .data_type = QMI_SIGNED_4_BYTE_ENUM, + .elem_len = 1, + .elem_size = sizeof(enum wlfw_qdss_trace_mode_enum_v01), + .is_array = NO_ARRAY, + .tlv_type = 0x10, + .offset = offsetof(struct + wlfw_qdss_trace_mode_req_msg_v01, + mode), + }, + { + .data_type = QMI_OPT_FLAG, + .elem_len = 1, + .elem_size = sizeof(u8), + .is_array = NO_ARRAY, + .tlv_type = 0x11, + .offset = offsetof(struct + wlfw_qdss_trace_mode_req_msg_v01, + option_valid), + }, + { + .data_type = QMI_UNSIGNED_8_BYTE, + .elem_len = 1, + .elem_size = sizeof(u64), + .is_array = NO_ARRAY, + .tlv_type = 0x11, + .offset = offsetof(struct + wlfw_qdss_trace_mode_req_msg_v01, + option), + }, + { + .data_type = QMI_EOTI, + .is_array = NO_ARRAY, + .tlv_type = QMI_COMMON_TLV_TYPE, + }, +}; + +struct elem_info wlfw_qdss_trace_mode_resp_msg_v01_ei[] = { + { + .data_type = QMI_STRUCT, + .elem_len = 1, + .elem_size = sizeof(struct qmi_response_type_v01), + .is_array = NO_ARRAY, + .tlv_type = 0x02, + .offset = offsetof(struct + wlfw_qdss_trace_mode_resp_msg_v01, + resp), + .ei_array = qmi_response_type_v01_ei, + }, + { + .data_type = QMI_EOTI, + .is_array = NO_ARRAY, + .tlv_type = QMI_COMMON_TLV_TYPE, + }, +}; + +struct elem_info wlfw_qdss_trace_free_ind_msg_v01_ei[] = { + { + .data_type = QMI_OPT_FLAG, + .elem_len = 1, + .elem_size = sizeof(u8), + .is_array = NO_ARRAY, + .tlv_type = 0x10, + .offset = offsetof(struct + wlfw_qdss_trace_free_ind_msg_v01, + mem_seg_valid), + }, + { + .data_type = QMI_DATA_LEN, + .elem_len = 1, + .elem_size = sizeof(u8), + .is_array = NO_ARRAY, + .tlv_type = 0x10, + .offset = offsetof(struct + wlfw_qdss_trace_free_ind_msg_v01, + mem_seg_len), + }, + { + .data_type = QMI_STRUCT, + .elem_len = QMI_WLFW_MAX_NUM_MEM_SEG_V01, + .elem_size = sizeof(struct wlfw_mem_seg_resp_s_v01), + .is_array = VAR_LEN_ARRAY, + .tlv_type = 0x10, + .offset = offsetof(struct + wlfw_qdss_trace_free_ind_msg_v01, + mem_seg), + .ei_array = wlfw_mem_seg_resp_s_v01_ei, + }, + { + .data_type = QMI_EOTI, + .is_array = NO_ARRAY, + .tlv_type = QMI_COMMON_TLV_TYPE, + }, +}; + +struct elem_info wlfw_shutdown_req_msg_v01_ei[] = { + { + .data_type = QMI_OPT_FLAG, + .elem_len = 1, + .elem_size = sizeof(u8), + .is_array = NO_ARRAY, + .tlv_type = 0x10, + .offset = offsetof(struct wlfw_shutdown_req_msg_v01, + shutdown_valid), + }, + { + .data_type = QMI_UNSIGNED_1_BYTE, + .elem_len = 1, + .elem_size = sizeof(u8), + .is_array = NO_ARRAY, + .tlv_type = 0x10, + .offset = offsetof(struct wlfw_shutdown_req_msg_v01, + shutdown), + }, + { + .data_type = QMI_EOTI, + .is_array = NO_ARRAY, + .tlv_type = QMI_COMMON_TLV_TYPE, + }, +}; + +struct elem_info wlfw_shutdown_resp_msg_v01_ei[] = { + { + .data_type = QMI_STRUCT, + .elem_len = 1, + .elem_size = sizeof(struct qmi_response_type_v01), + .is_array = NO_ARRAY, + .tlv_type = 0x02, + .offset = offsetof(struct wlfw_shutdown_resp_msg_v01, + resp), + .ei_array = qmi_response_type_v01_ei, + }, + { + .data_type = QMI_EOTI, + .is_array = NO_ARRAY, + .tlv_type = QMI_COMMON_TLV_TYPE, + }, +}; + diff --git a/drivers/net/wireless/cnss2/wlan_firmware_service_v01.h b/drivers/net/wireless/cnss2/wlan_firmware_service_v01.h index 2ef74280e7b1..04df84fec1cb 100644 --- a/drivers/net/wireless/cnss2/wlan_firmware_service_v01.h +++ b/drivers/net/wireless/cnss2/wlan_firmware_service_v01.h @@ -1,4 +1,4 @@ -/* Copyright (c) 2015-2018, The Linux Foundation. All rights reserved. +/* Copyright (c) 2015-2018, 2020 The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -22,6 +22,7 @@ #define QMI_WLFW_BDF_DOWNLOAD_REQ_V01 0x0025 #define QMI_WLFW_FW_MEM_READY_IND_V01 0x0037 +#define QMI_WLFW_QDSS_TRACE_CONFIG_DOWNLOAD_REQ_V01 0x0044 #define QMI_WLFW_INITIATE_CAL_UPDATE_IND_V01 0x002A #define QMI_WLFW_CAL_DONE_IND_V01 0x003E #define QMI_WLFW_HOST_CAP_REQ_V01 0x0034 @@ -36,6 +37,9 @@ #define QMI_WLFW_XO_CAL_IND_V01 0x003D #define QMI_WLFW_INI_RESP_V01 0x002F #define QMI_WLFW_CAL_REPORT_RESP_V01 0x0026 +#define QMI_WLFW_QDSS_TRACE_MEM_INFO_REQ_V01 0x0040 +#define QMI_WLFW_QDSS_TRACE_REQ_MEM_IND_V01 0x003F +#define QMI_WLFW_SHUTDOWN_RESP_V01 0x0043 #define QMI_WLFW_MAC_ADDR_RESP_V01 0x0033 #define QMI_WLFW_INITIATE_CAL_DOWNLOAD_IND_V01 0x0028 #define QMI_WLFW_HOST_CAP_RESP_V01 0x0034 @@ -44,29 +48,38 @@ #define QMI_WLFW_WLAN_MODE_REQ_V01 0x0022 #define QMI_WLFW_IND_REGISTER_REQ_V01 0x0020 #define QMI_WLFW_WLAN_CFG_RESP_V01 0x0023 +#define QMI_WLFW_QDSS_TRACE_MODE_REQ_V01 0x0045 #define QMI_WLFW_REQUEST_MEM_IND_V01 0x0035 +#define QMI_WLFW_QDSS_TRACE_CONFIG_DOWNLOAD_RESP_V01 0x0044 #define QMI_WLFW_REJUVENATE_IND_V01 0x0039 #define QMI_WLFW_DYNAMIC_FEATURE_MASK_REQ_V01 0x003B #define QMI_WLFW_ATHDIAG_WRITE_REQ_V01 0x0031 #define QMI_WLFW_WLAN_MODE_RESP_V01 0x0022 #define QMI_WLFW_RESPOND_MEM_REQ_V01 0x0036 +#define QMI_WLFW_QDSS_TRACE_MODE_RESP_V01 0x0045 #define QMI_WLFW_PIN_CONNECT_RESULT_IND_V01 0x002C #define QMI_WLFW_FW_READY_IND_V01 0x0021 +#define QMI_WLFW_QDSS_TRACE_SAVE_IND_V01 0x0041 +#define QMI_WLFW_QDSS_TRACE_MEM_INFO_RESP_V01 0x0040 #define QMI_WLFW_MSA_READY_RESP_V01 0x002E +#define QMI_WLFW_QDSS_TRACE_DATA_REQ_V01 0x0042 #define QMI_WLFW_CAL_UPDATE_REQ_V01 0x0029 #define QMI_WLFW_INI_REQ_V01 0x002F #define QMI_WLFW_BDF_DOWNLOAD_RESP_V01 0x0025 #define QMI_WLFW_REJUVENATE_ACK_RESP_V01 0x003A #define QMI_WLFW_MSA_INFO_RESP_V01 0x002D #define QMI_WLFW_MSA_READY_REQ_V01 0x002E +#define QMI_WLFW_QDSS_TRACE_DATA_RESP_V01 0x0042 #define QMI_WLFW_CAP_RESP_V01 0x0024 #define QMI_WLFW_REJUVENATE_ACK_REQ_V01 0x003A #define QMI_WLFW_ATHDIAG_READ_RESP_V01 0x0030 +#define QMI_WLFW_SHUTDOWN_REQ_V01 0x0043 #define QMI_WLFW_VBATT_REQ_V01 0x0032 #define QMI_WLFW_MAC_ADDR_REQ_V01 0x0033 #define QMI_WLFW_RESPOND_MEM_RESP_V01 0x0036 #define QMI_WLFW_VBATT_RESP_V01 0x0032 #define QMI_WLFW_MSA_INFO_REQ_V01 0x002D +#define QMI_WLFW_QDSS_TRACE_FREE_IND_V01 0x0046 #define QMI_WLFW_CAL_DOWNLOAD_REQ_V01 0x0027 #define QMI_WLFW_ATHDIAG_READ_REQ_V01 0x0030 #define QMI_WLFW_WLAN_CFG_REQ_V01 0x0023 @@ -129,9 +142,17 @@ enum wlfw_mem_type_enum_v01 { QMI_WLFW_MEM_M3_V01 = 3, QMI_WLFW_MEM_CAL_V01 = 4, QMI_WLFW_MEM_DPD_V01 = 5, + QMI_WLFW_MEM_QDSS_V01 = 6, WLFW_MEM_TYPE_ENUM_MAX_VAL_V01 = INT_MAX, }; +enum wlfw_qdss_trace_mode_enum_v01 { + WLFW_QDSS_TRACE_MODE_ENUM_MIN_VAL_V01 = INT_MIN, + QMI_WLFW_QDSS_TRACE_OFF_V01 = 0, + QMI_WLFW_QDSS_TRACE_ON_V01 = 1, + WLFW_QDSS_TRACE_MODE_ENUM_MAX_VAL_V01 = INT_MAX, +}; + #define QMI_WLFW_CE_ATTR_FLAGS_V01 ((u32)0x00) #define QMI_WLFW_CE_ATTR_NO_SNOOP_V01 ((u32)0x01) #define QMI_WLFW_CE_ATTR_BYTE_SWAP_DATA_V01 ((u32)0x02) @@ -170,6 +191,16 @@ struct wlfw_shadow_reg_v2_cfg_s_v01 { u32 addr; }; +struct wlfw_rri_over_ddr_cfg_s_v01 { + u32 base_addr_low; + u32 base_addr_high; +}; + +struct wlfw_msi_cfg_s_v01 { + u16 ce_id; + u16 msi_vector; +}; + struct wlfw_memory_region_info_s_v01 { u64 region_addr; u32 size; @@ -239,9 +270,15 @@ struct wlfw_ind_register_req_msg_v01 { u8 xo_cal_enable; u8 cal_done_enable_valid; u8 cal_done_enable; + u8 qdss_trace_req_mem_enable_valid; + u8 qdss_trace_req_mem_enable; + u8 qdss_trace_save_enable_valid; + u8 qdss_trace_save_enable; + u8 qdss_trace_free_enable_valid; + u8 qdss_trace_free_enable; }; -#define WLFW_IND_REGISTER_REQ_MSG_V01_MAX_MSG_LEN 54 +#define WLFW_IND_REGISTER_REQ_MSG_V01_MAX_MSG_LEN 66 extern struct elem_info wlfw_ind_register_req_msg_v01_ei[]; struct wlfw_ind_register_resp_msg_v01 { @@ -312,9 +349,14 @@ struct wlfw_wlan_cfg_req_msg_v01 { u32 shadow_reg_v2_len; struct wlfw_shadow_reg_v2_cfg_s_v01 shadow_reg_v2[QMI_WLFW_MAX_NUM_SHADOW_REG_V2_V01]; + u8 rri_over_ddr_cfg_valid; + struct wlfw_rri_over_ddr_cfg_s_v01 rri_over_ddr_cfg; + u8 msi_cfg_valid; + u32 msi_cfg_len; + struct wlfw_msi_cfg_s_v01 msi_cfg[QMI_WLFW_MAX_NUM_CE_V01]; }; -#define WLFW_WLAN_CFG_REQ_MSG_V01_MAX_MSG_LEN 803 +#define WLFW_WLAN_CFG_REQ_MSG_V01_MAX_MSG_LEN 866 extern struct elem_info wlfw_wlan_cfg_req_msg_v01_ei[]; struct wlfw_wlan_cfg_resp_msg_v01 { @@ -345,9 +387,13 @@ struct wlfw_cap_resp_msg_v01 { char fw_build_id[QMI_WLFW_MAX_BUILD_ID_LEN_V01 + 1]; u8 num_macs_valid; u8 num_macs; + u8 voltage_mv_valid; + u32 voltage_mv; + u8 time_freq_hz_valid; + u32 time_freq_hz; }; -#define WLFW_CAP_RESP_MSG_V01_MAX_MSG_LEN 207 +#define WLFW_CAP_RESP_MSG_V01_MAX_MSG_LEN 221 extern struct elem_info wlfw_cap_resp_msg_v01_ei[]; struct wlfw_bdf_download_req_msg_v01 { @@ -737,4 +783,126 @@ struct wlfw_cal_done_ind_msg_v01 { #define WLFW_CAL_DONE_IND_MSG_V01_MAX_MSG_LEN 0 extern struct elem_info wlfw_cal_done_ind_msg_v01_ei[]; +struct wlfw_qdss_trace_req_mem_ind_msg_v01 { + u32 mem_seg_len; + struct wlfw_mem_seg_s_v01 mem_seg[QMI_WLFW_MAX_NUM_MEM_SEG_V01]; +}; + +#define WLFW_QDSS_TRACE_REQ_MEM_IND_MSG_V01_MAX_MSG_LEN 1124 +extern struct elem_info wlfw_qdss_trace_req_mem_ind_msg_v01_ei[]; + +struct wlfw_qdss_trace_mem_info_req_msg_v01 { + u32 mem_seg_len; + struct wlfw_mem_seg_resp_s_v01 mem_seg[QMI_WLFW_MAX_NUM_MEM_SEG_V01]; +}; + +#define WLFW_QDSS_TRACE_MEM_INFO_REQ_MSG_V01_MAX_MSG_LEN 548 +extern struct elem_info wlfw_qdss_trace_mem_info_req_msg_v01_ei[]; + +struct wlfw_qdss_trace_mem_info_resp_msg_v01 { + struct qmi_response_type_v01 resp; +}; + +#define WLFW_QDSS_TRACE_MEM_INFO_RESP_MSG_V01_MAX_MSG_LEN 7 +extern struct elem_info wlfw_qdss_trace_mem_info_resp_msg_v01_ei[]; + +struct wlfw_qdss_trace_save_ind_msg_v01 { + u32 source; + u32 total_size; + u8 mem_seg_valid; + u32 mem_seg_len; + struct wlfw_mem_seg_resp_s_v01 mem_seg[QMI_WLFW_MAX_NUM_MEM_SEG_V01]; + u8 file_name_valid; + char file_name[QMI_WLFW_MAX_STR_LEN_V01 + 1]; +}; + +#define WLFW_QDSS_TRACE_SAVE_IND_MSG_V01_MAX_MSG_LEN 581 +extern struct elem_info wlfw_qdss_trace_save_ind_msg_v01_ei[]; + +struct wlfw_qdss_trace_data_req_msg_v01 { + u32 seg_id; +}; + +#define WLFW_QDSS_TRACE_DATA_REQ_MSG_V01_MAX_MSG_LEN 7 +extern struct elem_info wlfw_qdss_trace_data_req_msg_v01_ei[]; + +struct wlfw_qdss_trace_data_resp_msg_v01 { + struct qmi_response_type_v01 resp; + u8 total_size_valid; + u32 total_size; + u8 seg_id_valid; + u32 seg_id; + u8 data_valid; + u32 data_len; + u8 data[QMI_WLFW_MAX_DATA_SIZE_V01]; + u8 end_valid; + u8 end; +}; + +#define WLFW_QDSS_TRACE_DATA_RESP_MSG_V01_MAX_MSG_LEN 6174 +extern struct elem_info wlfw_qdss_trace_data_resp_msg_v01_ei[]; + +struct wlfw_qdss_trace_config_download_req_msg_v01 { + u8 total_size_valid; + u32 total_size; + u8 seg_id_valid; + u32 seg_id; + u8 data_valid; + u32 data_len; + u8 data[QMI_WLFW_MAX_DATA_SIZE_V01]; + u8 end_valid; + u8 end; +}; + +#define WLFW_QDSS_TRACE_CONFIG_DOWNLOAD_REQ_MSG_V01_MAX_MSG_LEN 6167 +extern struct elem_info wlfw_qdss_trace_config_download_req_msg_v01_ei[]; + +struct wlfw_qdss_trace_config_download_resp_msg_v01 { + struct qmi_response_type_v01 resp; +}; + +#define WLFW_QDSS_TRACE_CONFIG_DOWNLOAD_RESP_MSG_V01_MAX_MSG_LEN 7 +extern struct elem_info wlfw_qdss_trace_config_download_resp_msg_v01_ei[]; + +struct wlfw_qdss_trace_mode_req_msg_v01 { + u8 mode_valid; + enum wlfw_qdss_trace_mode_enum_v01 mode; + u8 option_valid; + u64 option; +}; + +#define WLFW_QDSS_TRACE_MODE_REQ_MSG_V01_MAX_MSG_LEN 18 +extern struct elem_info wlfw_qdss_trace_mode_req_msg_v01_ei[]; + +struct wlfw_qdss_trace_mode_resp_msg_v01 { + struct qmi_response_type_v01 resp; +}; + +#define WLFW_QDSS_TRACE_MODE_RESP_MSG_V01_MAX_MSG_LEN 7 +extern struct elem_info wlfw_qdss_trace_mode_resp_msg_v01_ei[]; + +struct wlfw_qdss_trace_free_ind_msg_v01 { + u8 mem_seg_valid; + u32 mem_seg_len; + struct wlfw_mem_seg_resp_s_v01 mem_seg[QMI_WLFW_MAX_NUM_MEM_SEG_V01]; +}; + +#define WLFW_QDSS_TRACE_FREE_IND_MSG_V01_MAX_MSG_LEN 548 +extern struct elem_info wlfw_qdss_trace_free_ind_msg_v01_ei[]; + +struct wlfw_shutdown_req_msg_v01 { + u8 shutdown_valid; + u8 shutdown; +}; + +#define WLFW_SHUTDOWN_REQ_MSG_V01_MAX_MSG_LEN 4 +extern struct elem_info wlfw_shutdown_req_msg_v01_ei[]; + +struct wlfw_shutdown_resp_msg_v01 { + struct qmi_response_type_v01 resp; +}; + +#define WLFW_SHUTDOWN_RESP_MSG_V01_MAX_MSG_LEN 7 +extern struct elem_info wlfw_shutdown_resp_msg_v01_ei[]; + #endif diff --git a/include/net/cnss2.h b/include/net/cnss2.h index d316a3be4d26..0c47377c5586 100644 --- a/include/net/cnss2.h +++ b/include/net/cnss2.h @@ -27,9 +27,11 @@ enum cnss_bus_width_type { CNSS_BUS_WIDTH_NONE, + CNSS_BUS_WIDTH_IDLE, CNSS_BUS_WIDTH_LOW, CNSS_BUS_WIDTH_MEDIUM, - CNSS_BUS_WIDTH_HIGH + CNSS_BUS_WIDTH_HIGH, + CNSS_BUS_WIDTH_VERY_HIGH, }; enum cnss_platform_cap_flag { @@ -158,6 +160,7 @@ struct cnss_wlan_enable_cfg { struct cnss_shadow_reg_v2_cfg *shadow_reg_v2_cfg; bool rri_over_ddr_cfg_valid; struct cnss_rri_over_ddr_cfg rri_over_ddr_cfg; + bool send_msi_ce; }; enum cnss_driver_mode {