Merge "ath10k: Vote for hardware resources for WCN3990"
This commit is contained in:
commit
0c90b04235
4 changed files with 412 additions and 3 deletions
|
@ -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 */ >,
|
||||
|
@ -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>;
|
||||
};
|
||||
|
|
|
@ -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 */ >,
|
||||
|
@ -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 {
|
||||
|
|
|
@ -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
|
||||
|
||||
|
@ -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);
|
||||
|
@ -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,
|
||||
|
@ -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;
|
||||
|
@ -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,
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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;
|
||||
|
|
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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…
Add table
Reference in a new issue