Loading Documentation/devicetree/bindings/net/wireless/qcom,wcn3990-wifi.txt +17 −0 Original line number Diff line number Diff line Loading @@ -11,13 +11,24 @@ Required properties: - compatible: "qcom,wcn3990-wifi"; - reg: Memory regions defined as starting address and size - reg-names: Names of the memory regions defined in reg entry - clocks: List of clock phandles - clock-names: List of clock names corresponding to the "clocks" property - interrupts: Copy engine interrupt table Optional properties: - <supply-name>-supply: phandle to the regulator device tree node optional "supply-name" is "vdd-0.8-cx-mx". - qcom,<supply>-config: Specifies voltage levels for supply. Should be specified in pairs (min, max), units uV. There can be optional load in uA and Regulator settle delay in uS. Example: msm_ath10k_wlan: qcom,msm_ath10k_wlan@18800000 { compatible = "qcom,wcn3990-wifi"; reg = <0x18800000 0x800000>; reg-names = "membase"; clocks = <&clock_gcc clk_aggre2_noc_clk>; clock-names = "smmu_aggre2_noc_clk"; interrupts = <0 130 0 /* CE0 */ >, <0 131 0 /* CE1 */ >, Loading @@ -31,4 +42,10 @@ Example: <0 139 0 /* CE9 */ >, <0 140 0 /* CE10 */ >, <0 141 0 /* CE11 */ >; vdd-0.8-cx-mx-supply = <&pm8998_l5>; vdd-1.8-xo-supply = <&pm8998_l7_pin_ctrl>; vdd-1.3-rfa-supply = <&pm8998_l17_pin_ctrl>; vdd-3.3-ch0-supply = <&pm8998_l25_pin_ctrl>; qcom,vdd-0.8-cx-mx-config = <800000 800000>; qcom,vdd-3.3-ch0-config = <3104000 3312000>; }; arch/arm/boot/dts/qcom/msm8998.dtsi +8 −0 Original line number Diff line number Diff line Loading @@ -3106,6 +3106,8 @@ compatible = "qcom,wcn3990-wifi"; reg = <0x18800000 0x800000>; reg-names = "membase"; clocks = <&clock_gcc clk_rf_clk2_pin>; clock-names = "cxo_ref_clk_pin"; interrupts = <0 413 0 /* CE0 */ >, <0 414 0 /* CE1 */ >, Loading @@ -3119,6 +3121,12 @@ <0 423 0 /* CE9 */ >, <0 424 0 /* CE10 */ >, <0 425 0 /* CE11 */ >; vdd-0.8-cx-mx-supply = <&pm8998_l5>; vdd-1.8-xo-supply = <&pm8998_l7_pin_ctrl>; vdd-1.3-rfa-supply = <&pm8998_l17_pin_ctrl>; vdd-3.3-ch0-supply = <&pm8998_l25_pin_ctrl>; qcom,vdd-0.8-cx-mx-config = <800000 800000>; qcom,vdd-3.3-ch0-config = <3104000 3312000>; }; qcom,icnss@18800000 { Loading drivers/net/wireless/ath/ath10k/snoc.c +352 −3 Original line number Diff line number Diff line Loading @@ -27,6 +27,8 @@ #include "qmi.h" #include <linux/of.h> #include <linux/platform_device.h> #include <linux/regulator/consumer.h> #include <linux/clk.h> #define WCN3990_MAX_IRQ 12 Loading @@ -48,6 +50,7 @@ const char *ce_name[WCN3990_MAX_IRQ] = { #define ATH10K_SNOC_TARGET_WAIT 3000 #define ATH10K_SNOC_NUM_WARM_RESET_ATTEMPTS 3 #define SNOC_HIF_POWER_DOWN_DELAY 30 #define ATH10K_MAX_PROP_SIZE 32 static void ath10k_snoc_buffer_cleanup(struct ath10k *ar); static int ath10k_snoc_request_irq(struct ath10k *ar); Loading Loading @@ -1248,6 +1251,326 @@ int ath10k_snoc_pm_notifier(struct notifier_block *nb, return NOTIFY_DONE; } static int ath10k_get_vreg_info(struct ath10k *ar, struct device *dev, struct ath10k_wcn3990_vreg_info *vreg_info) { int ret = 0; char prop_name[ATH10K_MAX_PROP_SIZE]; struct regulator *reg; const __be32 *prop; int len = 0; int i; reg = devm_regulator_get_optional(dev, vreg_info->name); if (PTR_ERR(reg) == -EPROBE_DEFER) { ath10k_err(ar, "EPROBE_DEFER for regulator: %s\n", vreg_info->name); ret = PTR_ERR(reg); goto out; } if (IS_ERR(reg)) { ret = PTR_ERR(reg); if (vreg_info->required) { ath10k_err(ar, "Regulator %s doesn't exist: %d\n", vreg_info->name, ret); goto out; } else { ath10k_dbg(ar, ATH10K_DBG_SNOC, "Optional regulator %s doesn't exist: %d\n", vreg_info->name, ret); goto done; } } vreg_info->reg = reg; snprintf(prop_name, ATH10K_MAX_PROP_SIZE, "qcom,%s-config", vreg_info->name); prop = of_get_property(dev->of_node, prop_name, &len); ath10k_dbg(ar, ATH10K_DBG_SNOC, "Got regulator cfg,prop: %s, len: %d\n", prop_name, len); if (!prop || len < (2 * sizeof(__be32))) { ath10k_dbg(ar, ATH10K_DBG_SNOC, "Property %s %s\n", prop_name, prop ? "invalid format" : "doesn't exist"); goto done; } for (i = 0; (i * sizeof(__be32)) < len; i++) { switch (i) { case 0: vreg_info->min_v = be32_to_cpup(&prop[0]); break; case 1: vreg_info->max_v = be32_to_cpup(&prop[1]); break; case 2: vreg_info->load_ua = be32_to_cpup(&prop[2]); break; case 3: vreg_info->settle_delay = be32_to_cpup(&prop[3]); break; default: ath10k_dbg(ar, ATH10K_DBG_SNOC, "%s, ignoring val %d\n", prop_name, i); break; } } done: ath10k_dbg(ar, ATH10K_DBG_SNOC, "vreg: %s, min_v: %u, max_v: %u, load: %u, delay: %lu\n", vreg_info->name, vreg_info->min_v, vreg_info->max_v, vreg_info->load_ua, vreg_info->settle_delay); return 0; out: return ret; } static int ath10k_get_clk_info(struct ath10k *ar, struct device *dev, struct ath10k_wcn3990_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, "Clock %s isn't available: %d\n", clk_info->name, ret); goto out; } else { ath10k_dbg(ar, ATH10K_DBG_SNOC, "Ignoring clk %s: %d\n", clk_info->name, ret); ret = 0; goto out; } } ath10k_dbg(ar, ATH10K_DBG_SNOC, "Clock: %s, freq: %u\n", clk_info->name, clk_info->freq); clk_info->handle = handle; out: return ret; } static int ath10k_wcn3990_vreg_on(struct ath10k *ar) { int ret = 0; struct ath10k_wcn3990_vreg_info *vreg_info; int i; struct ath10k_snoc *ar_snoc = ath10k_snoc_priv(ar); for (i = 0; i < ATH10K_WCN3990_VREG_INFO_SIZE; i++) { vreg_info = &ar_snoc->vreg[i]; if (!vreg_info->reg) continue; ath10k_dbg(ar, ATH10K_DBG_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, "vreg %s, set failed:min:%u,max:%u,ret: %d\n", vreg_info->name, vreg_info->min_v, vreg_info->max_v, ret); break; } if (vreg_info->load_ua) { ret = regulator_set_load(vreg_info->reg, vreg_info->load_ua); if (ret < 0) { ath10k_err(ar, "Reg %s, can't set load:%u,ret: %d\n", vreg_info->name, vreg_info->load_ua, ret); break; } } ret = regulator_enable(vreg_info->reg); if (ret) { ath10k_err(ar, "Regulator %s, can't enable: %d\n", vreg_info->name, ret); break; } if (vreg_info->settle_delay) udelay(vreg_info->settle_delay); } if (!ret) return 0; for (; i >= 0; i--) { vreg_info = &ar_snoc->vreg[i]; if (!vreg_info->reg) continue; regulator_disable(vreg_info->reg); regulator_set_load(vreg_info->reg, 0); regulator_set_voltage(vreg_info->reg, 0, vreg_info->max_v); } return ret; } static int ath10k_wcn3990_vreg_off(struct ath10k *ar) { int ret = 0; struct ath10k_wcn3990_vreg_info *vreg_info; int i; struct ath10k_snoc *ar_snoc = ath10k_snoc_priv(ar); for (i = ATH10K_WCN3990_VREG_INFO_SIZE - 1; i >= 0; i--) { vreg_info = &ar_snoc->vreg[i]; if (!vreg_info->reg) continue; ath10k_dbg(ar, ATH10K_DBG_SNOC, "Regulator %s being disabled\n", vreg_info->name); ret = regulator_disable(vreg_info->reg); if (ret) ath10k_err(ar, "Regulator %s, can't disable: %d\n", vreg_info->name, ret); ret = regulator_set_load(vreg_info->reg, 0); if (ret < 0) ath10k_err(ar, "Regulator %s, can't set load: %d\n", vreg_info->name, ret); ret = regulator_set_voltage(vreg_info->reg, 0, vreg_info->max_v); if (ret) ath10k_err(ar, "Regulator %s, can't set voltage: %d\n", vreg_info->name, ret); } return ret; } static int ath10k_wcn3990_clk_init(struct ath10k *ar) { struct ath10k_wcn3990_clk_info *clk_info; int i; int ret = 0; struct ath10k_snoc *ar_snoc = ath10k_snoc_priv(ar); for (i = 0; i < ATH10K_WCN3990_CLK_INFO_SIZE; i++) { clk_info = &ar_snoc->clk[i]; if (!clk_info->handle) continue; ath10k_dbg(ar, ATH10K_DBG_SNOC, "Clock %s being enabled\n", clk_info->name); if (clk_info->freq) { ret = clk_set_rate(clk_info->handle, clk_info->freq); if (ret) { ath10k_err(ar, "Clk %s,set err: %u,ret: %d\n", clk_info->name, clk_info->freq, ret); break; } } ret = clk_prepare_enable(clk_info->handle); if (ret) { ath10k_err(ar, "Clock %s, can't enable: %d\n", clk_info->name, ret); break; } } if (ret == 0) return 0; for (; i >= 0; i--) { clk_info = &ar_snoc->clk[i]; if (!clk_info->handle) continue; clk_disable_unprepare(clk_info->handle); } return ret; } static int ath10k_wcn3990_clk_deinit(struct ath10k *ar) { struct ath10k_wcn3990_clk_info *clk_info; int i; struct ath10k_snoc *ar_snoc = ath10k_snoc_priv(ar); for (i = 0; i < ATH10K_WCN3990_CLK_INFO_SIZE; i++) { clk_info = &ar_snoc->clk[i]; if (!clk_info->handle) continue; ath10k_dbg(ar, ATH10K_DBG_SNOC, "Clock %s being disabled\n", clk_info->name); clk_disable_unprepare(clk_info->handle); } return 0; } static int ath10k_hw_power_on(struct ath10k *ar) { int ret = 0; ath10k_dbg(ar, ATH10K_DBG_SNOC, "HW Power on\n"); ret = ath10k_wcn3990_vreg_on(ar); if (ret) goto out; ret = ath10k_wcn3990_clk_init(ar); if (ret) goto vreg_off; return ret; vreg_off: ath10k_wcn3990_vreg_off(ar); out: return ret; } static int ath10k_hw_power_off(struct ath10k *ar) { int ret = 0; ath10k_dbg(ar, ATH10K_DBG_SNOC, "HW Power off\n"); ath10k_wcn3990_clk_deinit(ar); ret = ath10k_wcn3990_vreg_off(ar); return ret; } static const struct ath10k_hif_ops ath10k_snoc_hif_ops = { .tx_sg = ath10k_snoc_hif_tx_sg, .start = ath10k_snoc_hif_start, Loading Loading @@ -1275,6 +1598,7 @@ static int ath10k_snoc_probe(struct platform_device *pdev) enum ath10k_hw_rev hw_rev; struct device *dev; u32 chip_id; u32 i; dev = &pdev->dev; hw_rev = ATH10K_HW_WCN3990; Loading Loading @@ -1308,22 +1632,43 @@ static int ath10k_snoc_probe(struct platform_device *pdev) setup_timer(&ar_snoc->rx_post_retry, ath10k_snoc_rx_replenish_retry, (unsigned long)ar); memcpy(ar_snoc->vreg, vreg_cfg, sizeof(vreg_cfg)); for (i = 0; i < ATH10K_WCN3990_VREG_INFO_SIZE; i++) { ret = ath10k_get_vreg_info(ar, dev, &ar_snoc->vreg[i]); if (ret) goto err_core_destroy; } memcpy(ar_snoc->clk, clk_cfg, sizeof(clk_cfg)); for (i = 0; i < ATH10K_WCN3990_CLK_INFO_SIZE; i++) { ret = ath10k_get_clk_info(ar, dev, &ar_snoc->clk[i]); if (ret) goto err_core_destroy; } ret = ath10k_hw_power_on(ar); if (ret) { ath10k_err(ar, "failed to power on device: %d\n", ret); goto err_stop_qmi_service; } ret = ath10k_snoc_claim(ar); if (ret) { ath10k_err(ar, "failed to claim device: %d\n", ret); goto err_stop_qmi_service; goto err_hw_power_off; } ret = ath10k_snoc_bus_configure(ar); if (ret) { ath10k_err(ar, "failed to configure bus: %d\n", ret); goto err_stop_qmi_service; goto err_hw_power_off; } ret = ath10k_snoc_alloc_pipes(ar); if (ret) { ath10k_err(ar, "failed to allocate copy engine pipes: %d\n", ret); goto err_stop_qmi_service; goto err_hw_power_off; } netif_napi_add(&ar->napi_dev, &ar->napi, ath10k_snoc_napi_poll, Loading Loading @@ -1359,6 +1704,9 @@ err_free_irq: err_free_pipes: ath10k_snoc_free_pipes(ar); err_hw_power_off: ath10k_hw_power_off(ar); err_stop_qmi_service: ath10k_snoc_stop_qmi_service(ar); Loading Loading @@ -1389,6 +1737,7 @@ static int ath10k_snoc_remove(struct platform_device *pdev) ath10k_snoc_release_resource(ar); ath10k_snoc_free_pipes(ar); ath10k_snoc_stop_qmi_service(ar); ath10k_hw_power_off(ar); ath10k_core_destroy(ar); return 0; Loading drivers/net/wireless/ath/ath10k/snoc.h +35 −0 Original line number Diff line number Diff line Loading @@ -17,6 +17,7 @@ #include "ce.h" #include "pci.h" #include "qmi.h" #include <linux/kernel.h> #include <soc/qcom/service-locator.h> #define ATH10K_SNOC_RX_POST_RETRY_MS 50 #define CE_POLL_PIPE 4 Loading Loading @@ -112,6 +113,38 @@ struct ath10k_snoc_ce_irq { u32 irq_line; }; struct ath10k_wcn3990_vreg_info { struct regulator *reg; const char *name; u32 min_v; u32 max_v; u32 load_ua; unsigned long settle_delay; bool required; }; struct ath10k_wcn3990_clk_info { struct clk *handle; const char *name; u32 freq; bool required; }; static struct ath10k_wcn3990_vreg_info vreg_cfg[] = { {NULL, "vdd-0.8-cx-mx", 800000, 800000, 0, 0, false}, {NULL, "vdd-1.8-xo", 1800000, 1800000, 0, 0, false}, {NULL, "vdd-1.3-rfa", 1304000, 1304000, 0, 0, false}, {NULL, "vdd-3.3-ch0", 3312000, 3312000, 0, 0, false}, }; #define ATH10K_WCN3990_VREG_INFO_SIZE ARRAY_SIZE(vreg_cfg) static struct ath10k_wcn3990_clk_info clk_cfg[] = { {NULL, "cxo_ref_clk_pin", 0, false}, }; #define ATH10K_WCN3990_CLK_INFO_SIZE ARRAY_SIZE(clk_cfg) /* struct ath10k_snoc: SNOC info struct * @dev: device structure * @ar:ath10k base structure Loading Loading @@ -157,6 +190,8 @@ struct ath10k_snoc { atomic_t fw_crashed; atomic_t pm_ops_inprogress; struct ath10k_snoc_qmi_config qmi_cfg; struct ath10k_wcn3990_vreg_info vreg[ATH10K_WCN3990_VREG_INFO_SIZE]; struct ath10k_wcn3990_clk_info clk[ATH10K_WCN3990_CLK_INFO_SIZE]; }; struct ath10k_event_pd_down_data { Loading Loading
Documentation/devicetree/bindings/net/wireless/qcom,wcn3990-wifi.txt +17 −0 Original line number Diff line number Diff line Loading @@ -11,13 +11,24 @@ Required properties: - compatible: "qcom,wcn3990-wifi"; - reg: Memory regions defined as starting address and size - reg-names: Names of the memory regions defined in reg entry - clocks: List of clock phandles - clock-names: List of clock names corresponding to the "clocks" property - interrupts: Copy engine interrupt table Optional properties: - <supply-name>-supply: phandle to the regulator device tree node optional "supply-name" is "vdd-0.8-cx-mx". - qcom,<supply>-config: Specifies voltage levels for supply. Should be specified in pairs (min, max), units uV. There can be optional load in uA and Regulator settle delay in uS. Example: msm_ath10k_wlan: qcom,msm_ath10k_wlan@18800000 { compatible = "qcom,wcn3990-wifi"; reg = <0x18800000 0x800000>; reg-names = "membase"; clocks = <&clock_gcc clk_aggre2_noc_clk>; clock-names = "smmu_aggre2_noc_clk"; interrupts = <0 130 0 /* CE0 */ >, <0 131 0 /* CE1 */ >, Loading @@ -31,4 +42,10 @@ Example: <0 139 0 /* CE9 */ >, <0 140 0 /* CE10 */ >, <0 141 0 /* CE11 */ >; vdd-0.8-cx-mx-supply = <&pm8998_l5>; vdd-1.8-xo-supply = <&pm8998_l7_pin_ctrl>; vdd-1.3-rfa-supply = <&pm8998_l17_pin_ctrl>; vdd-3.3-ch0-supply = <&pm8998_l25_pin_ctrl>; qcom,vdd-0.8-cx-mx-config = <800000 800000>; qcom,vdd-3.3-ch0-config = <3104000 3312000>; };
arch/arm/boot/dts/qcom/msm8998.dtsi +8 −0 Original line number Diff line number Diff line Loading @@ -3106,6 +3106,8 @@ compatible = "qcom,wcn3990-wifi"; reg = <0x18800000 0x800000>; reg-names = "membase"; clocks = <&clock_gcc clk_rf_clk2_pin>; clock-names = "cxo_ref_clk_pin"; interrupts = <0 413 0 /* CE0 */ >, <0 414 0 /* CE1 */ >, Loading @@ -3119,6 +3121,12 @@ <0 423 0 /* CE9 */ >, <0 424 0 /* CE10 */ >, <0 425 0 /* CE11 */ >; vdd-0.8-cx-mx-supply = <&pm8998_l5>; vdd-1.8-xo-supply = <&pm8998_l7_pin_ctrl>; vdd-1.3-rfa-supply = <&pm8998_l17_pin_ctrl>; vdd-3.3-ch0-supply = <&pm8998_l25_pin_ctrl>; qcom,vdd-0.8-cx-mx-config = <800000 800000>; qcom,vdd-3.3-ch0-config = <3104000 3312000>; }; qcom,icnss@18800000 { Loading
drivers/net/wireless/ath/ath10k/snoc.c +352 −3 Original line number Diff line number Diff line Loading @@ -27,6 +27,8 @@ #include "qmi.h" #include <linux/of.h> #include <linux/platform_device.h> #include <linux/regulator/consumer.h> #include <linux/clk.h> #define WCN3990_MAX_IRQ 12 Loading @@ -48,6 +50,7 @@ const char *ce_name[WCN3990_MAX_IRQ] = { #define ATH10K_SNOC_TARGET_WAIT 3000 #define ATH10K_SNOC_NUM_WARM_RESET_ATTEMPTS 3 #define SNOC_HIF_POWER_DOWN_DELAY 30 #define ATH10K_MAX_PROP_SIZE 32 static void ath10k_snoc_buffer_cleanup(struct ath10k *ar); static int ath10k_snoc_request_irq(struct ath10k *ar); Loading Loading @@ -1248,6 +1251,326 @@ int ath10k_snoc_pm_notifier(struct notifier_block *nb, return NOTIFY_DONE; } static int ath10k_get_vreg_info(struct ath10k *ar, struct device *dev, struct ath10k_wcn3990_vreg_info *vreg_info) { int ret = 0; char prop_name[ATH10K_MAX_PROP_SIZE]; struct regulator *reg; const __be32 *prop; int len = 0; int i; reg = devm_regulator_get_optional(dev, vreg_info->name); if (PTR_ERR(reg) == -EPROBE_DEFER) { ath10k_err(ar, "EPROBE_DEFER for regulator: %s\n", vreg_info->name); ret = PTR_ERR(reg); goto out; } if (IS_ERR(reg)) { ret = PTR_ERR(reg); if (vreg_info->required) { ath10k_err(ar, "Regulator %s doesn't exist: %d\n", vreg_info->name, ret); goto out; } else { ath10k_dbg(ar, ATH10K_DBG_SNOC, "Optional regulator %s doesn't exist: %d\n", vreg_info->name, ret); goto done; } } vreg_info->reg = reg; snprintf(prop_name, ATH10K_MAX_PROP_SIZE, "qcom,%s-config", vreg_info->name); prop = of_get_property(dev->of_node, prop_name, &len); ath10k_dbg(ar, ATH10K_DBG_SNOC, "Got regulator cfg,prop: %s, len: %d\n", prop_name, len); if (!prop || len < (2 * sizeof(__be32))) { ath10k_dbg(ar, ATH10K_DBG_SNOC, "Property %s %s\n", prop_name, prop ? "invalid format" : "doesn't exist"); goto done; } for (i = 0; (i * sizeof(__be32)) < len; i++) { switch (i) { case 0: vreg_info->min_v = be32_to_cpup(&prop[0]); break; case 1: vreg_info->max_v = be32_to_cpup(&prop[1]); break; case 2: vreg_info->load_ua = be32_to_cpup(&prop[2]); break; case 3: vreg_info->settle_delay = be32_to_cpup(&prop[3]); break; default: ath10k_dbg(ar, ATH10K_DBG_SNOC, "%s, ignoring val %d\n", prop_name, i); break; } } done: ath10k_dbg(ar, ATH10K_DBG_SNOC, "vreg: %s, min_v: %u, max_v: %u, load: %u, delay: %lu\n", vreg_info->name, vreg_info->min_v, vreg_info->max_v, vreg_info->load_ua, vreg_info->settle_delay); return 0; out: return ret; } static int ath10k_get_clk_info(struct ath10k *ar, struct device *dev, struct ath10k_wcn3990_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, "Clock %s isn't available: %d\n", clk_info->name, ret); goto out; } else { ath10k_dbg(ar, ATH10K_DBG_SNOC, "Ignoring clk %s: %d\n", clk_info->name, ret); ret = 0; goto out; } } ath10k_dbg(ar, ATH10K_DBG_SNOC, "Clock: %s, freq: %u\n", clk_info->name, clk_info->freq); clk_info->handle = handle; out: return ret; } static int ath10k_wcn3990_vreg_on(struct ath10k *ar) { int ret = 0; struct ath10k_wcn3990_vreg_info *vreg_info; int i; struct ath10k_snoc *ar_snoc = ath10k_snoc_priv(ar); for (i = 0; i < ATH10K_WCN3990_VREG_INFO_SIZE; i++) { vreg_info = &ar_snoc->vreg[i]; if (!vreg_info->reg) continue; ath10k_dbg(ar, ATH10K_DBG_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, "vreg %s, set failed:min:%u,max:%u,ret: %d\n", vreg_info->name, vreg_info->min_v, vreg_info->max_v, ret); break; } if (vreg_info->load_ua) { ret = regulator_set_load(vreg_info->reg, vreg_info->load_ua); if (ret < 0) { ath10k_err(ar, "Reg %s, can't set load:%u,ret: %d\n", vreg_info->name, vreg_info->load_ua, ret); break; } } ret = regulator_enable(vreg_info->reg); if (ret) { ath10k_err(ar, "Regulator %s, can't enable: %d\n", vreg_info->name, ret); break; } if (vreg_info->settle_delay) udelay(vreg_info->settle_delay); } if (!ret) return 0; for (; i >= 0; i--) { vreg_info = &ar_snoc->vreg[i]; if (!vreg_info->reg) continue; regulator_disable(vreg_info->reg); regulator_set_load(vreg_info->reg, 0); regulator_set_voltage(vreg_info->reg, 0, vreg_info->max_v); } return ret; } static int ath10k_wcn3990_vreg_off(struct ath10k *ar) { int ret = 0; struct ath10k_wcn3990_vreg_info *vreg_info; int i; struct ath10k_snoc *ar_snoc = ath10k_snoc_priv(ar); for (i = ATH10K_WCN3990_VREG_INFO_SIZE - 1; i >= 0; i--) { vreg_info = &ar_snoc->vreg[i]; if (!vreg_info->reg) continue; ath10k_dbg(ar, ATH10K_DBG_SNOC, "Regulator %s being disabled\n", vreg_info->name); ret = regulator_disable(vreg_info->reg); if (ret) ath10k_err(ar, "Regulator %s, can't disable: %d\n", vreg_info->name, ret); ret = regulator_set_load(vreg_info->reg, 0); if (ret < 0) ath10k_err(ar, "Regulator %s, can't set load: %d\n", vreg_info->name, ret); ret = regulator_set_voltage(vreg_info->reg, 0, vreg_info->max_v); if (ret) ath10k_err(ar, "Regulator %s, can't set voltage: %d\n", vreg_info->name, ret); } return ret; } static int ath10k_wcn3990_clk_init(struct ath10k *ar) { struct ath10k_wcn3990_clk_info *clk_info; int i; int ret = 0; struct ath10k_snoc *ar_snoc = ath10k_snoc_priv(ar); for (i = 0; i < ATH10K_WCN3990_CLK_INFO_SIZE; i++) { clk_info = &ar_snoc->clk[i]; if (!clk_info->handle) continue; ath10k_dbg(ar, ATH10K_DBG_SNOC, "Clock %s being enabled\n", clk_info->name); if (clk_info->freq) { ret = clk_set_rate(clk_info->handle, clk_info->freq); if (ret) { ath10k_err(ar, "Clk %s,set err: %u,ret: %d\n", clk_info->name, clk_info->freq, ret); break; } } ret = clk_prepare_enable(clk_info->handle); if (ret) { ath10k_err(ar, "Clock %s, can't enable: %d\n", clk_info->name, ret); break; } } if (ret == 0) return 0; for (; i >= 0; i--) { clk_info = &ar_snoc->clk[i]; if (!clk_info->handle) continue; clk_disable_unprepare(clk_info->handle); } return ret; } static int ath10k_wcn3990_clk_deinit(struct ath10k *ar) { struct ath10k_wcn3990_clk_info *clk_info; int i; struct ath10k_snoc *ar_snoc = ath10k_snoc_priv(ar); for (i = 0; i < ATH10K_WCN3990_CLK_INFO_SIZE; i++) { clk_info = &ar_snoc->clk[i]; if (!clk_info->handle) continue; ath10k_dbg(ar, ATH10K_DBG_SNOC, "Clock %s being disabled\n", clk_info->name); clk_disable_unprepare(clk_info->handle); } return 0; } static int ath10k_hw_power_on(struct ath10k *ar) { int ret = 0; ath10k_dbg(ar, ATH10K_DBG_SNOC, "HW Power on\n"); ret = ath10k_wcn3990_vreg_on(ar); if (ret) goto out; ret = ath10k_wcn3990_clk_init(ar); if (ret) goto vreg_off; return ret; vreg_off: ath10k_wcn3990_vreg_off(ar); out: return ret; } static int ath10k_hw_power_off(struct ath10k *ar) { int ret = 0; ath10k_dbg(ar, ATH10K_DBG_SNOC, "HW Power off\n"); ath10k_wcn3990_clk_deinit(ar); ret = ath10k_wcn3990_vreg_off(ar); return ret; } static const struct ath10k_hif_ops ath10k_snoc_hif_ops = { .tx_sg = ath10k_snoc_hif_tx_sg, .start = ath10k_snoc_hif_start, Loading Loading @@ -1275,6 +1598,7 @@ static int ath10k_snoc_probe(struct platform_device *pdev) enum ath10k_hw_rev hw_rev; struct device *dev; u32 chip_id; u32 i; dev = &pdev->dev; hw_rev = ATH10K_HW_WCN3990; Loading Loading @@ -1308,22 +1632,43 @@ static int ath10k_snoc_probe(struct platform_device *pdev) setup_timer(&ar_snoc->rx_post_retry, ath10k_snoc_rx_replenish_retry, (unsigned long)ar); memcpy(ar_snoc->vreg, vreg_cfg, sizeof(vreg_cfg)); for (i = 0; i < ATH10K_WCN3990_VREG_INFO_SIZE; i++) { ret = ath10k_get_vreg_info(ar, dev, &ar_snoc->vreg[i]); if (ret) goto err_core_destroy; } memcpy(ar_snoc->clk, clk_cfg, sizeof(clk_cfg)); for (i = 0; i < ATH10K_WCN3990_CLK_INFO_SIZE; i++) { ret = ath10k_get_clk_info(ar, dev, &ar_snoc->clk[i]); if (ret) goto err_core_destroy; } ret = ath10k_hw_power_on(ar); if (ret) { ath10k_err(ar, "failed to power on device: %d\n", ret); goto err_stop_qmi_service; } ret = ath10k_snoc_claim(ar); if (ret) { ath10k_err(ar, "failed to claim device: %d\n", ret); goto err_stop_qmi_service; goto err_hw_power_off; } ret = ath10k_snoc_bus_configure(ar); if (ret) { ath10k_err(ar, "failed to configure bus: %d\n", ret); goto err_stop_qmi_service; goto err_hw_power_off; } ret = ath10k_snoc_alloc_pipes(ar); if (ret) { ath10k_err(ar, "failed to allocate copy engine pipes: %d\n", ret); goto err_stop_qmi_service; goto err_hw_power_off; } netif_napi_add(&ar->napi_dev, &ar->napi, ath10k_snoc_napi_poll, Loading Loading @@ -1359,6 +1704,9 @@ err_free_irq: err_free_pipes: ath10k_snoc_free_pipes(ar); err_hw_power_off: ath10k_hw_power_off(ar); err_stop_qmi_service: ath10k_snoc_stop_qmi_service(ar); Loading Loading @@ -1389,6 +1737,7 @@ static int ath10k_snoc_remove(struct platform_device *pdev) ath10k_snoc_release_resource(ar); ath10k_snoc_free_pipes(ar); ath10k_snoc_stop_qmi_service(ar); ath10k_hw_power_off(ar); ath10k_core_destroy(ar); return 0; Loading
drivers/net/wireless/ath/ath10k/snoc.h +35 −0 Original line number Diff line number Diff line Loading @@ -17,6 +17,7 @@ #include "ce.h" #include "pci.h" #include "qmi.h" #include <linux/kernel.h> #include <soc/qcom/service-locator.h> #define ATH10K_SNOC_RX_POST_RETRY_MS 50 #define CE_POLL_PIPE 4 Loading Loading @@ -112,6 +113,38 @@ struct ath10k_snoc_ce_irq { u32 irq_line; }; struct ath10k_wcn3990_vreg_info { struct regulator *reg; const char *name; u32 min_v; u32 max_v; u32 load_ua; unsigned long settle_delay; bool required; }; struct ath10k_wcn3990_clk_info { struct clk *handle; const char *name; u32 freq; bool required; }; static struct ath10k_wcn3990_vreg_info vreg_cfg[] = { {NULL, "vdd-0.8-cx-mx", 800000, 800000, 0, 0, false}, {NULL, "vdd-1.8-xo", 1800000, 1800000, 0, 0, false}, {NULL, "vdd-1.3-rfa", 1304000, 1304000, 0, 0, false}, {NULL, "vdd-3.3-ch0", 3312000, 3312000, 0, 0, false}, }; #define ATH10K_WCN3990_VREG_INFO_SIZE ARRAY_SIZE(vreg_cfg) static struct ath10k_wcn3990_clk_info clk_cfg[] = { {NULL, "cxo_ref_clk_pin", 0, false}, }; #define ATH10K_WCN3990_CLK_INFO_SIZE ARRAY_SIZE(clk_cfg) /* struct ath10k_snoc: SNOC info struct * @dev: device structure * @ar:ath10k base structure Loading Loading @@ -157,6 +190,8 @@ struct ath10k_snoc { atomic_t fw_crashed; atomic_t pm_ops_inprogress; struct ath10k_snoc_qmi_config qmi_cfg; struct ath10k_wcn3990_vreg_info vreg[ATH10K_WCN3990_VREG_INFO_SIZE]; struct ath10k_wcn3990_clk_info clk[ATH10K_WCN3990_CLK_INFO_SIZE]; }; struct ath10k_event_pd_down_data { Loading