Merge "ath10k: Vote for hardware resources for WCN3990"

This commit is contained in:
Linux Build Service Account 2017-10-05 16:09:44 -07:00 committed by Gerrit - the friendly Code Review server
commit 0c90b04235
4 changed files with 412 additions and 3 deletions

View file

@ -11,13 +11,24 @@ Required properties:
- compatible: "qcom,wcn3990-wifi"; - compatible: "qcom,wcn3990-wifi";
- reg: Memory regions defined as starting address and size - reg: Memory regions defined as starting address and size
- reg-names: Names of the memory regions defined in reg entry - 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 - 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: Example:
msm_ath10k_wlan: qcom,msm_ath10k_wlan@18800000 { msm_ath10k_wlan: qcom,msm_ath10k_wlan@18800000 {
compatible = "qcom,wcn3990-wifi"; compatible = "qcom,wcn3990-wifi";
reg = <0x18800000 0x800000>; reg = <0x18800000 0x800000>;
reg-names = "membase"; reg-names = "membase";
clocks = <&clock_gcc clk_aggre2_noc_clk>;
clock-names = "smmu_aggre2_noc_clk";
interrupts = interrupts =
<0 130 0 /* CE0 */ >, <0 130 0 /* CE0 */ >,
<0 131 0 /* CE1 */ >, <0 131 0 /* CE1 */ >,
@ -31,4 +42,10 @@ Example:
<0 139 0 /* CE9 */ >, <0 139 0 /* CE9 */ >,
<0 140 0 /* CE10 */ >, <0 140 0 /* CE10 */ >,
<0 141 0 /* CE11 */ >; <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>;
}; };

View file

@ -3106,6 +3106,8 @@
compatible = "qcom,wcn3990-wifi"; compatible = "qcom,wcn3990-wifi";
reg = <0x18800000 0x800000>; reg = <0x18800000 0x800000>;
reg-names = "membase"; reg-names = "membase";
clocks = <&clock_gcc clk_rf_clk2_pin>;
clock-names = "cxo_ref_clk_pin";
interrupts = interrupts =
<0 413 0 /* CE0 */ >, <0 413 0 /* CE0 */ >,
<0 414 0 /* CE1 */ >, <0 414 0 /* CE1 */ >,
@ -3119,6 +3121,12 @@
<0 423 0 /* CE9 */ >, <0 423 0 /* CE9 */ >,
<0 424 0 /* CE10 */ >, <0 424 0 /* CE10 */ >,
<0 425 0 /* CE11 */ >; <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 { qcom,icnss@18800000 {

View file

@ -27,6 +27,8 @@
#include "qmi.h" #include "qmi.h"
#include <linux/of.h> #include <linux/of.h>
#include <linux/platform_device.h> #include <linux/platform_device.h>
#include <linux/regulator/consumer.h>
#include <linux/clk.h>
#define WCN3990_MAX_IRQ 12 #define WCN3990_MAX_IRQ 12
@ -48,6 +50,7 @@ const char *ce_name[WCN3990_MAX_IRQ] = {
#define ATH10K_SNOC_TARGET_WAIT 3000 #define ATH10K_SNOC_TARGET_WAIT 3000
#define ATH10K_SNOC_NUM_WARM_RESET_ATTEMPTS 3 #define ATH10K_SNOC_NUM_WARM_RESET_ATTEMPTS 3
#define SNOC_HIF_POWER_DOWN_DELAY 30 #define SNOC_HIF_POWER_DOWN_DELAY 30
#define ATH10K_MAX_PROP_SIZE 32
static void ath10k_snoc_buffer_cleanup(struct ath10k *ar); static void ath10k_snoc_buffer_cleanup(struct ath10k *ar);
static int ath10k_snoc_request_irq(struct ath10k *ar); static int ath10k_snoc_request_irq(struct ath10k *ar);
@ -1248,6 +1251,326 @@ int ath10k_snoc_pm_notifier(struct notifier_block *nb,
return NOTIFY_DONE; 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 = { static const struct ath10k_hif_ops ath10k_snoc_hif_ops = {
.tx_sg = ath10k_snoc_hif_tx_sg, .tx_sg = ath10k_snoc_hif_tx_sg,
.start = ath10k_snoc_hif_start, .start = ath10k_snoc_hif_start,
@ -1275,6 +1598,7 @@ static int ath10k_snoc_probe(struct platform_device *pdev)
enum ath10k_hw_rev hw_rev; enum ath10k_hw_rev hw_rev;
struct device *dev; struct device *dev;
u32 chip_id; u32 chip_id;
u32 i;
dev = &pdev->dev; dev = &pdev->dev;
hw_rev = ATH10K_HW_WCN3990; hw_rev = ATH10K_HW_WCN3990;
@ -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, setup_timer(&ar_snoc->rx_post_retry, ath10k_snoc_rx_replenish_retry,
(unsigned long)ar); (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); ret = ath10k_snoc_claim(ar);
if (ret) { if (ret) {
ath10k_err(ar, "failed to claim device: %d\n", 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); ret = ath10k_snoc_bus_configure(ar);
if (ret) { if (ret) {
ath10k_err(ar, "failed to configure bus: %d\n", 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); ret = ath10k_snoc_alloc_pipes(ar);
if (ret) { if (ret) {
ath10k_err(ar, "failed to allocate copy engine pipes: %d\n", ath10k_err(ar, "failed to allocate copy engine pipes: %d\n",
ret); ret);
goto err_stop_qmi_service; goto err_hw_power_off;
} }
netif_napi_add(&ar->napi_dev, &ar->napi, ath10k_snoc_napi_poll, netif_napi_add(&ar->napi_dev, &ar->napi, ath10k_snoc_napi_poll,
@ -1359,6 +1704,9 @@ err_free_irq:
err_free_pipes: err_free_pipes:
ath10k_snoc_free_pipes(ar); ath10k_snoc_free_pipes(ar);
err_hw_power_off:
ath10k_hw_power_off(ar);
err_stop_qmi_service: err_stop_qmi_service:
ath10k_snoc_stop_qmi_service(ar); ath10k_snoc_stop_qmi_service(ar);
@ -1389,6 +1737,7 @@ static int ath10k_snoc_remove(struct platform_device *pdev)
ath10k_snoc_release_resource(ar); ath10k_snoc_release_resource(ar);
ath10k_snoc_free_pipes(ar); ath10k_snoc_free_pipes(ar);
ath10k_snoc_stop_qmi_service(ar); ath10k_snoc_stop_qmi_service(ar);
ath10k_hw_power_off(ar);
ath10k_core_destroy(ar); ath10k_core_destroy(ar);
return 0; return 0;

View file

@ -17,6 +17,7 @@
#include "ce.h" #include "ce.h"
#include "pci.h" #include "pci.h"
#include "qmi.h" #include "qmi.h"
#include <linux/kernel.h>
#include <soc/qcom/service-locator.h> #include <soc/qcom/service-locator.h>
#define ATH10K_SNOC_RX_POST_RETRY_MS 50 #define ATH10K_SNOC_RX_POST_RETRY_MS 50
#define CE_POLL_PIPE 4 #define CE_POLL_PIPE 4
@ -112,6 +113,38 @@ struct ath10k_snoc_ce_irq {
u32 irq_line; 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 /* struct ath10k_snoc: SNOC info struct
* @dev: device structure * @dev: device structure
* @ar:ath10k base structure * @ar:ath10k base structure
@ -157,6 +190,8 @@ struct ath10k_snoc {
atomic_t fw_crashed; atomic_t fw_crashed;
atomic_t pm_ops_inprogress; atomic_t pm_ops_inprogress;
struct ath10k_snoc_qmi_config qmi_cfg; 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 { struct ath10k_event_pd_down_data {