Commit a6a793f9 authored by Govind Singh's avatar Govind Singh Committed by Kalle Valo

ath10k: vote for hardware resources for WCN3990

Add clock and regulator votes for WCN3990 WLAN
chipset.
Signed-off-by: default avatarGovind Singh <govinds@codeaurora.org>
Signed-off-by: default avatarKalle Valo <kvalo@codeaurora.org>
parent ea66b12e
...@@ -24,6 +24,8 @@ ...@@ -24,6 +24,8 @@
#include <linux/of.h> #include <linux/of.h>
#include <linux/of_device.h> #include <linux/of_device.h>
#include <linux/platform_device.h> #include <linux/platform_device.h>
#include <linux/regulator/consumer.h>
#include <linux/clk.h>
#define WCN3990_CE_ATTR_FLAGS 0 #define WCN3990_CE_ATTR_FLAGS 0
#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
...@@ -43,6 +45,17 @@ static char *const ce_name[] = { ...@@ -43,6 +45,17 @@ static char *const ce_name[] = {
"WLAN_CE_11", "WLAN_CE_11",
}; };
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},
};
static struct ath10k_wcn3990_clk_info clk_cfg[] = {
{NULL, "cxo_ref_clk_pin", 0, false},
};
static void ath10k_snoc_htc_tx_cb(struct ath10k_ce_pipe *ce_state); static void ath10k_snoc_htc_tx_cb(struct ath10k_ce_pipe *ce_state);
static void ath10k_snoc_htt_tx_cb(struct ath10k_ce_pipe *ce_state); static void ath10k_snoc_htt_tx_cb(struct ath10k_ce_pipe *ce_state);
static void ath10k_snoc_htc_rx_cb(struct ath10k_ce_pipe *ce_state); static void ath10k_snoc_htc_rx_cb(struct ath10k_ce_pipe *ce_state);
...@@ -969,6 +982,277 @@ static void ath10k_snoc_release_resource(struct ath10k *ar) ...@@ -969,6 +982,277 @@ static void ath10k_snoc_release_resource(struct ath10k *ar)
ath10k_ce_free_pipe(ar, i); ath10k_ce_free_pipe(ar, i);
} }
static int ath10k_get_vreg_info(struct ath10k *ar, struct device *dev,
struct ath10k_wcn3990_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_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, "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_wcn3990_vreg_on(struct ath10k *ar)
{
struct ath10k_snoc *ar_snoc = ath10k_snoc_priv(ar);
struct ath10k_wcn3990_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;
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);
goto err_reg_config;
}
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_reg_config;
}
}
ret = regulator_enable(vreg_info->reg);
if (ret) {
ath10k_err(ar, "failed to enable regulator %s\n",
vreg_info->name);
goto err_reg_config;
}
if (vreg_info->settle_delay)
udelay(vreg_info->settle_delay);
}
return 0;
err_reg_config:
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)
{
struct ath10k_snoc *ar_snoc = ath10k_snoc_priv(ar);
struct ath10k_wcn3990_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, "snoc regulator %s being disabled\n",
vreg_info->name);
ret = regulator_disable(vreg_info->reg);
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);
ret = regulator_set_voltage(vreg_info->reg, 0,
vreg_info->max_v);
if (ret)
ath10k_err(ar, "failed to set voltage %s\n",
vreg_info->name);
}
return ret;
}
static int ath10k_wcn3990_clk_init(struct ath10k *ar)
{
struct ath10k_snoc *ar_snoc = ath10k_snoc_priv(ar);
struct ath10k_wcn3990_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);
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 >= 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_snoc *ar_snoc = ath10k_snoc_priv(ar);
struct ath10k_wcn3990_clk_info *clk_info;
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 disabled\n",
clk_info->name);
clk_disable_unprepare(clk_info->handle);
}
return 0;
}
static int ath10k_hw_power_on(struct ath10k *ar)
{
int ret;
ath10k_dbg(ar, ATH10K_DBG_SNOC, "soc power on\n");
ret = ath10k_wcn3990_vreg_on(ar);
if (ret)
return ret;
ret = ath10k_wcn3990_clk_init(ar);
if (ret)
goto vreg_off;
return ret;
vreg_off:
ath10k_wcn3990_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_wcn3990_clk_deinit(ar);
ret = ath10k_wcn3990_vreg_off(ar);
return ret;
}
static const struct of_device_id ath10k_snoc_dt_match[] = { static const struct of_device_id ath10k_snoc_dt_match[] = {
{ .compatible = "qcom,wcn3990-wifi", { .compatible = "qcom,wcn3990-wifi",
.data = &drv_priv, .data = &drv_priv,
...@@ -985,6 +1269,7 @@ static int ath10k_snoc_probe(struct platform_device *pdev) ...@@ -985,6 +1269,7 @@ static int ath10k_snoc_probe(struct platform_device *pdev)
struct device *dev; struct device *dev;
struct ath10k *ar; struct ath10k *ar;
int ret; int ret;
u32 i;
of_id = of_match_device(ath10k_snoc_dt_match, &pdev->dev); of_id = of_match_device(ath10k_snoc_dt_match, &pdev->dev);
if (!of_id) { if (!of_id) {
...@@ -1031,16 +1316,41 @@ static int ath10k_snoc_probe(struct platform_device *pdev) ...@@ -1031,16 +1316,41 @@ static int ath10k_snoc_probe(struct platform_device *pdev)
ath10k_warn(ar, "failed to request irqs: %d\n", ret); ath10k_warn(ar, "failed to request irqs: %d\n", ret);
goto err_release_resource; 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->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 = ath10k_hw_power_on(ar);
if (ret) {
ath10k_err(ar, "failed to power on device: %d\n", ret);
goto err_free_irq;
}
ret = ath10k_core_register(ar, drv_data->hw_rev); ret = ath10k_core_register(ar, drv_data->hw_rev);
if (ret) { if (ret) {
ath10k_err(ar, "failed to register driver core: %d\n", ret); ath10k_err(ar, "failed to register driver core: %d\n", ret);
goto err_free_irq; goto err_hw_power_off;
} }
ath10k_dbg(ar, ATH10K_DBG_SNOC, "snoc probe\n"); ath10k_dbg(ar, ATH10K_DBG_SNOC, "snoc probe\n");
ath10k_warn(ar, "Warning: SNOC support is still work-in-progress, it will not work properly!"); ath10k_warn(ar, "Warning: SNOC support is still work-in-progress, it will not work properly!");
return 0; return 0;
err_hw_power_off:
ath10k_hw_power_off(ar);
err_free_irq: err_free_irq:
ath10k_snoc_free_irq(ar); ath10k_snoc_free_irq(ar);
...@@ -1059,6 +1369,7 @@ static int ath10k_snoc_remove(struct platform_device *pdev) ...@@ -1059,6 +1369,7 @@ static int ath10k_snoc_remove(struct platform_device *pdev)
ath10k_dbg(ar, ATH10K_DBG_SNOC, "snoc remove\n"); ath10k_dbg(ar, ATH10K_DBG_SNOC, "snoc remove\n");
ath10k_core_unregister(ar); ath10k_core_unregister(ar);
ath10k_hw_power_off(ar);
ath10k_snoc_free_irq(ar); ath10k_snoc_free_irq(ar);
ath10k_snoc_release_resource(ar); ath10k_snoc_release_resource(ar);
ath10k_core_destroy(ar); ath10k_core_destroy(ar);
......
...@@ -52,6 +52,23 @@ struct ath10k_snoc_ce_irq { ...@@ -52,6 +52,23 @@ 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;
};
struct ath10k_snoc { struct ath10k_snoc {
struct platform_device *dev; struct platform_device *dev;
struct ath10k *ar; struct ath10k *ar;
...@@ -63,6 +80,8 @@ struct ath10k_snoc { ...@@ -63,6 +80,8 @@ struct ath10k_snoc {
struct ath10k_snoc_ce_irq ce_irqs[CE_COUNT_MAX]; struct ath10k_snoc_ce_irq ce_irqs[CE_COUNT_MAX];
struct ath10k_ce ce; struct ath10k_ce ce;
struct timer_list rx_post_retry; struct timer_list rx_post_retry;
struct ath10k_wcn3990_vreg_info *vreg;
struct ath10k_wcn3990_clk_info *clk;
}; };
static inline struct ath10k_snoc *ath10k_snoc_priv(struct ath10k *ar) static inline struct ath10k_snoc *ath10k_snoc_priv(struct ath10k *ar)
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment