#include <linux/bluetooth-power.h>
#include <linux/slab.h>
#include <linux/regulator/consumer.h>
+#include <linux/clk.h>
#include <net/cnss.h>
#include "btfm_slim.h"
#include <linux/fs.h>
return rc;
}
+static int bt_clk_enable(struct bt_power_clk_data *clk)
+{
+ int rc = 0;
+
+ BT_PWR_DBG("%s", clk->name);
+
+ /* Get the clock handle for vreg */
+ if (!clk->clk || clk->is_enabled) {
+ BT_PWR_ERR("error - node: %p, clk->is_enabled:%d",
+ clk->clk, clk->is_enabled);
+ return -EINVAL;
+ }
+
+ rc = clk_prepare_enable(clk->clk);
+ if (rc) {
+ BT_PWR_ERR("failed to enable %s, rc(%d)\n", clk->name, rc);
+ return rc;
+ }
+
+ clk->is_enabled = true;
+ return rc;
+}
+
+static int bt_clk_disable(struct bt_power_clk_data *clk)
+{
+ int rc = 0;
+
+ BT_PWR_DBG("%s", clk->name);
+
+ /* Get the clock handle for vreg */
+ if (!clk->clk || !clk->is_enabled) {
+ BT_PWR_ERR("error - node: %p, clk->is_enabled:%d",
+ clk->clk, clk->is_enabled);
+ return -EINVAL;
+ }
+ clk_disable_unprepare(clk->clk);
+
+ clk->is_enabled = false;
+ return rc;
+}
+
static int bt_configure_gpios(int on)
{
int rc = 0;
goto chip_pwd_fail;
}
}
+ /* Parse dt_info and check if a target requires clock voting.
+ * Enable BT clock when BT is on and disable it when BT is off
+ */
+ if (bt_power_pdata->bt_chip_clk) {
+ rc = bt_clk_enable(bt_power_pdata->bt_chip_clk);
+ if (rc < 0) {
+ BT_PWR_ERR("bt_power gpio config failed");
+ goto clk_fail;
+ }
+ }
if (bt_power_pdata->bt_gpio_sys_rst > 0) {
rc = bt_configure_gpios(on);
if (rc < 0) {
}
}
} else {
- bt_configure_gpios(on);
+ if (bt_power_pdata->bt_gpio_sys_rst > 0)
+ bt_configure_gpios(on);
gpio_fail:
if (bt_power_pdata->bt_gpio_sys_rst > 0)
gpio_free(bt_power_pdata->bt_gpio_sys_rst);
- bt_vreg_disable(bt_power_pdata->bt_chip_pwd);
+ if (bt_power_pdata->bt_chip_clk)
+ bt_clk_disable(bt_power_pdata->bt_chip_clk);
+clk_fail:
+ if (bt_power_pdata->bt_chip_pwd)
+ bt_vreg_disable(bt_power_pdata->bt_chip_pwd);
chip_pwd_fail:
- bt_vreg_disable(bt_power_pdata->bt_vdd_ldo);
+ if (bt_power_pdata->bt_vdd_ldo)
+ bt_vreg_disable(bt_power_pdata->bt_vdd_ldo);
vdd_ldo_fail:
- bt_vreg_disable(bt_power_pdata->bt_vdd_pa);
+ if (bt_power_pdata->bt_vdd_pa)
+ bt_vreg_disable(bt_power_pdata->bt_vdd_pa);
vdd_pa_fail:
- bt_vreg_disable(bt_power_pdata->bt_vdd_core);
+ if (bt_power_pdata->bt_vdd_core)
+ bt_vreg_disable(bt_power_pdata->bt_vdd_core);
vdd_core_fail:
- bt_vreg_disable(bt_power_pdata->bt_vdd_xtal);
+ if (bt_power_pdata->bt_vdd_xtal)
+ bt_vreg_disable(bt_power_pdata->bt_vdd_xtal);
vdd_xtal_fail:
- bt_vreg_disable(bt_power_pdata->bt_vdd_io);
+ if (bt_power_pdata->bt_vdd_io)
+ bt_vreg_disable(bt_power_pdata->bt_vdd_io);
}
out:
return rc;
BT_PWR_DBG("vreg dev tree parse for %s", vreg_name);
+ *vreg_data = NULL;
snprintf(prop_name, MAX_PROP_SIZE, "%s-supply", vreg_name);
if (of_parse_phandle(np, prop_name, 0)) {
vreg = devm_kzalloc(dev, sizeof(*vreg), GFP_KERNEL);
return ret;
}
+static int bt_dt_parse_clk_info(struct device *dev,
+ struct bt_power_clk_data **clk_data)
+{
+ int ret = -EINVAL;
+ struct bt_power_clk_data *clk = NULL;
+ struct device_node *np = dev->of_node;
+
+ BT_PWR_DBG("");
+
+ *clk_data = NULL;
+ if (of_parse_phandle(np, "clocks", 0)) {
+ clk = devm_kzalloc(dev, sizeof(*clk), GFP_KERNEL);
+ if (!clk) {
+ BT_PWR_ERR("No memory for clocks");
+ ret = -ENOMEM;
+ goto err;
+ }
+
+ /* Allocated 20 bytes size buffer for clock name string */
+ clk->name = devm_kzalloc(dev, 20, GFP_KERNEL);
+
+ /* Parse clock name from node */
+ ret = of_property_read_string_index(np, "clock-names", 0,
+ &(clk->name));
+ if (ret < 0) {
+ BT_PWR_ERR("reading \"clock-names\" failed");
+ return ret;
+ }
+
+ clk->clk = devm_clk_get(dev, clk->name);
+ if (IS_ERR(clk->clk)) {
+ ret = PTR_ERR(clk->clk);
+ BT_PWR_ERR("failed to get %s, ret (%d)",
+ clk->name, ret);
+ clk->clk = NULL;
+ return ret;
+ }
+
+ *clk_data = clk;
+ } else {
+ BT_PWR_ERR("clocks is not provided in device tree");
+ }
+
+err:
+ return ret;
+}
+
static int bt_power_populate_dt_pinfo(struct platform_device *pdev)
{
int rc;
"qca,bt-chip-pwd");
if (rc < 0)
BT_PWR_ERR("bt-chip-pwd not provided in device tree");
+
+ rc = bt_dt_parse_clk_info(&pdev->dev,
+ &bt_power_pdata->bt_chip_clk);
+ if (rc < 0)
+ BT_PWR_ERR("clock not provided in device tree");
}
bt_power_pdata->bt_power_setup = bluetooth_power;