]> asedeno.scripts.mit.edu Git - linux.git/blobdiff - drivers/net/wireless/ath/ath10k/snoc.c
ath10k: Enable MSA region dump support for WCN3990
[linux.git] / drivers / net / wireless / ath / ath10k / snoc.c
index b491361e6ed4540f2b53ee782d5a2c5ec8b2ef0b..cd22c8654aa9c55ab75faf923a4512c011157e92 100644 (file)
@@ -9,9 +9,11 @@
 #include <linux/of.h>
 #include <linux/of_device.h>
 #include <linux/platform_device.h>
+#include <linux/property.h>
 #include <linux/regulator/consumer.h>
 
 #include "ce.h"
+#include "coredump.h"
 #include "debug.h"
 #include "hif.h"
 #include "htc.h"
@@ -36,15 +38,15 @@ static char *const ce_name[] = {
        "WLAN_CE_11",
 };
 
-static struct ath10k_vreg_info vreg_cfg[] = {
-       {NULL, "vdd-0.8-cx-mx", 800000, 850000, 0, 0, false},
-       {NULL, "vdd-1.8-xo", 1800000, 1850000, 0, 0, false},
-       {NULL, "vdd-1.3-rfa", 1300000, 1350000, 0, 0, false},
-       {NULL, "vdd-3.3-ch0", 3300000, 3350000, 0, 0, false},
+static const char * const ath10k_regulators[] = {
+       "vdd-0.8-cx-mx",
+       "vdd-1.8-xo",
+       "vdd-1.3-rfa",
+       "vdd-3.3-ch0",
 };
 
-static struct ath10k_clk_info clk_cfg[] = {
-       {NULL, "cxo_ref_clk_pin", 0, false},
+static const char * const ath10k_clocks[] = {
+       "cxo_ref_clk_pin",
 };
 
 static void ath10k_snoc_htc_tx_cb(struct ath10k_ce_pipe *ce_state);
@@ -976,8 +978,7 @@ static int ath10k_snoc_wlan_enable(struct ath10k *ar,
                                  sizeof(struct ath10k_svc_pipe_cfg);
        cfg.ce_svc_cfg = (struct ath10k_svc_pipe_cfg *)
                &target_service_to_ce_map_wlan;
-       cfg.num_shadow_reg_cfg = sizeof(target_shadow_reg_cfg_map) /
-                                       sizeof(struct ath10k_shadow_reg_cfg);
+       cfg.num_shadow_reg_cfg = ARRAY_SIZE(target_shadow_reg_cfg_map);
        cfg.shadow_reg_cfg = (struct ath10k_shadow_reg_cfg *)
                &target_shadow_reg_cfg_map;
 
@@ -1257,10 +1258,29 @@ static int ath10k_snoc_resource_init(struct ath10k *ar)
                ar_snoc->ce_irqs[i].irq_line = res->start;
        }
 
+       ret = device_property_read_u32(&pdev->dev, "qcom,xo-cal-data",
+                                      &ar_snoc->xo_cal_data);
+       ath10k_dbg(ar, ATH10K_DBG_SNOC, "snoc xo-cal-data return %d\n", ret);
+       if (ret == 0) {
+               ar_snoc->xo_cal_supported = true;
+               ath10k_dbg(ar, ATH10K_DBG_SNOC, "xo cal data %x\n",
+                          ar_snoc->xo_cal_data);
+       }
+       ret = 0;
+
 out:
        return ret;
 }
 
+static void ath10k_snoc_quirks_init(struct ath10k *ar)
+{
+       struct ath10k_snoc *ar_snoc = ath10k_snoc_priv(ar);
+       struct device *dev = &ar_snoc->dev->dev;
+
+       if (of_property_read_bool(dev->of_node, "qcom,snoc-host-cap-8bit-quirk"))
+               set_bit(ATH10K_SNOC_FLAG_8BIT_HOST_CAP_QUIRK, &ar_snoc->flags);
+}
+
 int ath10k_snoc_fw_indication(struct ath10k *ar, u64 type)
 {
        struct ath10k_snoc *ar_snoc = ath10k_snoc_priv(ar);
@@ -1337,296 +1357,102 @@ static void ath10k_snoc_release_resource(struct ath10k *ar)
                ath10k_ce_free_pipe(ar, i);
 }
 
-static int ath10k_get_vreg_info(struct ath10k *ar, struct device *dev,
-                               struct ath10k_vreg_info *vreg_info)
-{
-       struct regulator *reg;
-       int ret = 0;
-
-       reg = devm_regulator_get_optional(dev, vreg_info->name);
-
-       if (IS_ERR(reg)) {
-               ret = PTR_ERR(reg);
-
-               if (ret  == -EPROBE_DEFER) {
-                       ath10k_err(ar, "EPROBE_DEFER for regulator: %s\n",
-                                  vreg_info->name);
-                       return ret;
-               }
-               if (vreg_info->required) {
-                       ath10k_err(ar, "Regulator %s doesn't exist: %d\n",
-                                  vreg_info->name, ret);
-                       return ret;
-               }
-               ath10k_dbg(ar, ATH10K_DBG_SNOC,
-                          "Optional regulator %s doesn't exist: %d\n",
-                          vreg_info->name, ret);
-               goto done;
-       }
-
-       vreg_info->reg = reg;
-
-done:
-       ath10k_dbg(ar, ATH10K_DBG_SNOC,
-                  "snog vreg %s min_v %u max_v %u load_ua %u settle_delay %lu\n",
-                  vreg_info->name, vreg_info->min_v, vreg_info->max_v,
-                  vreg_info->load_ua, vreg_info->settle_delay);
-
-       return 0;
-}
-
-static int ath10k_get_clk_info(struct ath10k *ar, struct device *dev,
-                              struct ath10k_clk_info *clk_info)
-{
-       struct clk *handle;
-       int ret = 0;
-
-       handle = devm_clk_get(dev, clk_info->name);
-       if (IS_ERR(handle)) {
-               ret = PTR_ERR(handle);
-               if (clk_info->required) {
-                       ath10k_err(ar, "snoc clock %s isn't available: %d\n",
-                                  clk_info->name, ret);
-                       return ret;
-               }
-               ath10k_dbg(ar, ATH10K_DBG_SNOC, "snoc ignoring clock %s: %d\n",
-                          clk_info->name,
-                          ret);
-               return 0;
-       }
-
-       ath10k_dbg(ar, ATH10K_DBG_SNOC, "snoc clock %s freq %u\n",
-                  clk_info->name, clk_info->freq);
-
-       clk_info->handle = handle;
-
-       return ret;
-}
-
-static int __ath10k_snoc_vreg_on(struct ath10k *ar,
-                                struct ath10k_vreg_info *vreg_info)
-{
-       int ret;
-
-       ath10k_dbg(ar, ATH10K_DBG_SNOC, "snoc regulator %s being enabled\n",
-                  vreg_info->name);
-
-       ret = regulator_set_voltage(vreg_info->reg, vreg_info->min_v,
-                                   vreg_info->max_v);
-       if (ret) {
-               ath10k_err(ar,
-                          "failed to set regulator %s voltage-min: %d voltage-max: %d\n",
-                          vreg_info->name, vreg_info->min_v, vreg_info->max_v);
-               return ret;
-       }
-
-       if (vreg_info->load_ua) {
-               ret = regulator_set_load(vreg_info->reg, vreg_info->load_ua);
-               if (ret < 0) {
-                       ath10k_err(ar, "failed to set regulator %s load: %d\n",
-                                  vreg_info->name, vreg_info->load_ua);
-                       goto err_set_load;
-               }
-       }
-
-       ret = regulator_enable(vreg_info->reg);
-       if (ret) {
-               ath10k_err(ar, "failed to enable regulator %s\n",
-                          vreg_info->name);
-               goto err_enable;
-       }
-
-       if (vreg_info->settle_delay)
-               udelay(vreg_info->settle_delay);
-
-       return 0;
-
-err_enable:
-       regulator_set_load(vreg_info->reg, 0);
-err_set_load:
-       regulator_set_voltage(vreg_info->reg, 0, vreg_info->max_v);
-
-       return ret;
-}
-
-static int __ath10k_snoc_vreg_off(struct ath10k *ar,
-                                 struct ath10k_vreg_info *vreg_info)
+static int ath10k_hw_power_on(struct ath10k *ar)
 {
+       struct ath10k_snoc *ar_snoc = ath10k_snoc_priv(ar);
        int ret;
 
-       ath10k_dbg(ar, ATH10K_DBG_SNOC, "snoc regulator %s being disabled\n",
-                  vreg_info->name);
+       ath10k_dbg(ar, ATH10K_DBG_SNOC, "soc power on\n");
 
-       ret = regulator_disable(vreg_info->reg);
+       ret = regulator_bulk_enable(ar_snoc->num_vregs, ar_snoc->vregs);
        if (ret)
-               ath10k_err(ar, "failed to disable regulator %s\n",
-                          vreg_info->name);
-
-       ret = regulator_set_load(vreg_info->reg, 0);
-       if (ret < 0)
-               ath10k_err(ar, "failed to set load %s\n", vreg_info->name);
+               return ret;
 
-       ret = regulator_set_voltage(vreg_info->reg, 0, vreg_info->max_v);
+       ret = clk_bulk_prepare_enable(ar_snoc->num_clks, ar_snoc->clks);
        if (ret)
-               ath10k_err(ar, "failed to set voltage %s\n", vreg_info->name);
+               goto vreg_off;
 
        return ret;
-}
-
-static int ath10k_snoc_vreg_on(struct ath10k *ar)
-{
-       struct ath10k_snoc *ar_snoc = ath10k_snoc_priv(ar);
-       struct ath10k_vreg_info *vreg_info;
-       int ret = 0;
-       int i;
-
-       for (i = 0; i < ARRAY_SIZE(vreg_cfg); i++) {
-               vreg_info = &ar_snoc->vreg[i];
-
-               if (!vreg_info->reg)
-                       continue;
-
-               ret = __ath10k_snoc_vreg_on(ar, vreg_info);
-               if (ret)
-                       goto err_reg_config;
-       }
-
-       return 0;
-
-err_reg_config:
-       for (i = i - 1; i >= 0; i--) {
-               vreg_info = &ar_snoc->vreg[i];
-
-               if (!vreg_info->reg)
-                       continue;
-
-               __ath10k_snoc_vreg_off(ar, vreg_info);
-       }
 
+vreg_off:
+       regulator_bulk_disable(ar_snoc->num_vregs, ar_snoc->vregs);
        return ret;
 }
 
-static int ath10k_snoc_vreg_off(struct ath10k *ar)
+static int ath10k_hw_power_off(struct ath10k *ar)
 {
        struct ath10k_snoc *ar_snoc = ath10k_snoc_priv(ar);
-       struct ath10k_vreg_info *vreg_info;
-       int ret = 0;
-       int i;
-
-       for (i = ARRAY_SIZE(vreg_cfg) - 1; i >= 0; i--) {
-               vreg_info = &ar_snoc->vreg[i];
 
-               if (!vreg_info->reg)
-                       continue;
+       ath10k_dbg(ar, ATH10K_DBG_SNOC, "soc power off\n");
 
-               ret = __ath10k_snoc_vreg_off(ar, vreg_info);
-       }
+       clk_bulk_disable_unprepare(ar_snoc->num_clks, ar_snoc->clks);
 
-       return ret;
+       return regulator_bulk_disable(ar_snoc->num_vregs, ar_snoc->vregs);
 }
 
-static int ath10k_snoc_clk_init(struct ath10k *ar)
+static void ath10k_msa_dump_memory(struct ath10k *ar,
+                                  struct ath10k_fw_crash_data *crash_data)
 {
        struct ath10k_snoc *ar_snoc = ath10k_snoc_priv(ar);
-       struct ath10k_clk_info *clk_info;
-       int ret = 0;
-       int i;
-
-       for (i = 0; i < ARRAY_SIZE(clk_cfg); i++) {
-               clk_info = &ar_snoc->clk[i];
-
-               if (!clk_info->handle)
-                       continue;
-
-               ath10k_dbg(ar, ATH10K_DBG_SNOC, "snoc clock %s being enabled\n",
-                          clk_info->name);
+       const struct ath10k_hw_mem_layout *mem_layout;
+       const struct ath10k_mem_region *current_region;
+       struct ath10k_dump_ram_data_hdr *hdr;
+       size_t buf_len;
+       u8 *buf;
 
-               if (clk_info->freq) {
-                       ret = clk_set_rate(clk_info->handle, clk_info->freq);
-
-                       if (ret) {
-                               ath10k_err(ar, "failed to set clock %s freq %u\n",
-                                          clk_info->name, clk_info->freq);
-                               goto err_clock_config;
-                       }
-               }
-
-               ret = clk_prepare_enable(clk_info->handle);
-               if (ret) {
-                       ath10k_err(ar, "failed to enable clock %s\n",
-                                  clk_info->name);
-                       goto err_clock_config;
-               }
-       }
-
-       return 0;
-
-err_clock_config:
-       for (i = i - 1; i >= 0; i--) {
-               clk_info = &ar_snoc->clk[i];
-
-               if (!clk_info->handle)
-                       continue;
-
-               clk_disable_unprepare(clk_info->handle);
-       }
+       if (!crash_data && !crash_data->ramdump_buf)
+               return;
 
-       return ret;
-}
+       mem_layout = ath10k_coredump_get_mem_layout(ar);
+       if (!mem_layout)
+               return;
 
-static int ath10k_snoc_clk_deinit(struct ath10k *ar)
-{
-       struct ath10k_snoc *ar_snoc = ath10k_snoc_priv(ar);
-       struct ath10k_clk_info *clk_info;
-       int i;
+       current_region = &mem_layout->region_table.regions[0];
 
-       for (i = 0; i < ARRAY_SIZE(clk_cfg); i++) {
-               clk_info = &ar_snoc->clk[i];
+       buf = crash_data->ramdump_buf;
+       buf_len = crash_data->ramdump_buf_len;
+       memset(buf, 0, buf_len);
 
-               if (!clk_info->handle)
-                       continue;
+       /* Reserve space for the header. */
+       hdr = (void *)buf;
+       buf += sizeof(*hdr);
+       buf_len -= sizeof(*hdr);
 
-               ath10k_dbg(ar, ATH10K_DBG_SNOC, "snoc clock %s being disabled\n",
-                          clk_info->name);
+       hdr->region_type = cpu_to_le32(current_region->type);
+       hdr->start = cpu_to_le32((unsigned long)ar_snoc->qmi->msa_va);
+       hdr->length = cpu_to_le32(ar_snoc->qmi->msa_mem_size);
 
-               clk_disable_unprepare(clk_info->handle);
+       if (current_region->len < ar_snoc->qmi->msa_mem_size) {
+               memcpy(buf, ar_snoc->qmi->msa_va, current_region->len);
+               ath10k_warn(ar, "msa dump length is less than msa size %x, %x\n",
+                           current_region->len, ar_snoc->qmi->msa_mem_size);
+       } else {
+               memcpy(buf, ar_snoc->qmi->msa_va, ar_snoc->qmi->msa_mem_size);
        }
-
-       return 0;
 }
 
-static int ath10k_hw_power_on(struct ath10k *ar)
+void ath10k_snoc_fw_crashed_dump(struct ath10k *ar)
 {
-       int ret;
+       struct ath10k_fw_crash_data *crash_data;
+       char guid[UUID_STRING_LEN + 1];
 
-       ath10k_dbg(ar, ATH10K_DBG_SNOC, "soc power on\n");
+       mutex_lock(&ar->dump_mutex);
 
-       ret = ath10k_snoc_vreg_on(ar);
-       if (ret)
-               return ret;
+       spin_lock_bh(&ar->data_lock);
+       ar->stats.fw_crash_counter++;
+       spin_unlock_bh(&ar->data_lock);
 
-       ret = ath10k_snoc_clk_init(ar);
-       if (ret)
-               goto vreg_off;
+       crash_data = ath10k_coredump_new(ar);
 
-       return ret;
-
-vreg_off:
-       ath10k_snoc_vreg_off(ar);
-       return ret;
-}
-
-static int ath10k_hw_power_off(struct ath10k *ar)
-{
-       int ret;
-
-       ath10k_dbg(ar, ATH10K_DBG_SNOC, "soc power off\n");
-
-       ath10k_snoc_clk_deinit(ar);
-
-       ret = ath10k_snoc_vreg_off(ar);
+       if (crash_data)
+               scnprintf(guid, sizeof(guid), "%pUl", &crash_data->guid);
+       else
+               scnprintf(guid, sizeof(guid), "n/a");
 
-       return ret;
+       ath10k_err(ar, "firmware crashed! (guid %s)\n", guid);
+       ath10k_print_driver_info(ar);
+       ath10k_msa_dump_memory(ar, crash_data);
+       mutex_unlock(&ar->dump_mutex);
 }
 
 static const struct of_device_id ath10k_snoc_dt_match[] = {
@@ -1678,6 +1504,8 @@ static int ath10k_snoc_probe(struct platform_device *pdev)
        ar->ce_priv = &ar_snoc->ce;
        msa_size = drv_data->msa_size;
 
+       ath10k_snoc_quirks_init(ar);
+
        ret = ath10k_snoc_resource_init(ar);
        if (ret) {
                ath10k_warn(ar, "failed to initialize resource: %d\n", ret);
@@ -1695,20 +1523,37 @@ static int ath10k_snoc_probe(struct platform_device *pdev)
                goto err_release_resource;
        }
 
-       ar_snoc->vreg = vreg_cfg;
-       for (i = 0; i < ARRAY_SIZE(vreg_cfg); i++) {
-               ret = ath10k_get_vreg_info(ar, dev, &ar_snoc->vreg[i]);
-               if (ret)
-                       goto err_free_irq;
+       ar_snoc->num_vregs = ARRAY_SIZE(ath10k_regulators);
+       ar_snoc->vregs = devm_kcalloc(&pdev->dev, ar_snoc->num_vregs,
+                                     sizeof(*ar_snoc->vregs), GFP_KERNEL);
+       if (!ar_snoc->vregs) {
+               ret = -ENOMEM;
+               goto err_free_irq;
        }
+       for (i = 0; i < ar_snoc->num_vregs; i++)
+               ar_snoc->vregs[i].supply = ath10k_regulators[i];
 
-       ar_snoc->clk = clk_cfg;
-       for (i = 0; i < ARRAY_SIZE(clk_cfg); i++) {
-               ret = ath10k_get_clk_info(ar, dev, &ar_snoc->clk[i]);
-               if (ret)
-                       goto err_free_irq;
+       ret = devm_regulator_bulk_get(&pdev->dev, ar_snoc->num_vregs,
+                                     ar_snoc->vregs);
+       if (ret < 0)
+               goto err_free_irq;
+
+       ar_snoc->num_clks = ARRAY_SIZE(ath10k_clocks);
+       ar_snoc->clks = devm_kcalloc(&pdev->dev, ar_snoc->num_clks,
+                                    sizeof(*ar_snoc->clks), GFP_KERNEL);
+       if (!ar_snoc->clks) {
+               ret = -ENOMEM;
+               goto err_free_irq;
        }
 
+       for (i = 0; i < ar_snoc->num_clks; i++)
+               ar_snoc->clks[i].id = ath10k_clocks[i];
+
+       ret = devm_clk_bulk_get_optional(&pdev->dev, ar_snoc->num_clks,
+                                        ar_snoc->clks);
+       if (ret)
+               goto err_free_irq;
+
        ret = ath10k_hw_power_on(ar);
        if (ret) {
                ath10k_err(ar, "failed to power on device: %d\n", ret);