summaryrefslogtreecommitdiff
path: root/btpower.c
diff options
context:
space:
mode:
Diffstat (limited to 'btpower.c')
-rw-r--r--btpower.c348
1 files changed, 185 insertions, 163 deletions
diff --git a/btpower.c b/btpower.c
index e5a30d4..941169d 100644
--- a/btpower.c
+++ b/btpower.c
@@ -73,7 +73,6 @@ static const char *const tcs_seq_str[BTPOWER_TCS_SEQ_MAX] =
enum power_src_pos {
BT_RESET_GPIO = PWR_SRC_INIT_STATE_IDX,
- BT_SW_CTRL_GPIO,
BT_VDD_AON_LDO,
BT_VDD_DIG_LDO,
BT_VDD_RFA1_LDO,
@@ -103,6 +102,33 @@ enum power_src_pos {
BT_VDD_RFACMN_CURRENT
};
+#define SYNC_GPIO_SOURCE_CURRENT(drvdata, gpio, label) \
+{ \
+ if (gpio_is_valid(gpio)) { \
+ drvdata->bt_power_src_status[label ## _CURRENT] = \
+ gpio_get_value(gpio); \
+ pr_debug("%s: %s(%d) value(%d)\n", __func__, #label, gpio, \
+ drvdata->bt_power_src_status[label ## _CURRENT]); \
+ } else { \
+ drvdata->bt_power_src_status[label ## _CURRENT] = \
+ DEFAULT_INVALID_VALUE; \
+ pr_debug("%s: %s not configured\n", __func__, #label); \
+ } \
+}
+
+#define SET_GPIO_SOURCE_STATE(drvdata, gpio, label, value) \
+{ \
+ if (gpio_is_valid(gpio)) { \
+ gpio_set_value(gpio, value); \
+ drvdata->bt_power_src_status[label] = value; \
+ pr_debug("%s: %s(%d) value(%d)\n", __func__, #label, gpio, \
+ drvdata->bt_power_src_status[label]); \
+ } else { \
+ drvdata->bt_power_src_status[label] = DEFAULT_INVALID_VALUE; \
+ pr_debug("%s: %s not configured\n", __func__, #label); \
+ } \
+}
+
// Regulator structure for QCA6174/QCA9377/QCA9379 BT SoC series
static struct bt_power_vreg_data bt_vregs_info_qca61x4_937x[] = {
{NULL, "qcom,bt-vdd-aon", 928000, 928000, 0, false, false,
@@ -368,7 +394,7 @@ static void btpower_set_xo_clk_gpio_state(struct btpower_platform_data *drvdata,
int retry = 0;
int rc = 0;
- if (xo_clk_gpio < 0)
+ if (!gpio_is_valid(xo_clk_gpio))
return;
retry_gpio_req:
@@ -402,6 +428,53 @@ retry_gpio_req:
gpio_free(xo_clk_gpio);
}
+static int btpower_gpio_source_request(int gpio, const char *label)
+{
+ int rc = gpio_request(gpio, label);
+ if (rc) {
+ pr_err("%s: unable to request gpio %s(%d) (%d)\n", __func__,
+ label, gpio, rc);
+ return rc;
+ }
+ return rc;
+}
+
+static int btpower_gpio_acquire_output(int gpio, const char *label, bool value)
+{
+ int rc;
+
+ rc = btpower_gpio_source_request(gpio, label);
+ if (rc)
+ return rc;
+
+ rc = gpio_direction_output(gpio, value);
+ if (rc) {
+ pr_err("%s: unable to set output gpio %s(%d) (%d)\n",
+ __func__, label, gpio, rc);
+ gpio_free(gpio);
+ return rc;
+ }
+ return rc;
+}
+
+static int btpower_gpio_acquire_input(int gpio, const char *label)
+{
+ int rc;
+
+ rc = btpower_gpio_source_request(gpio, label);
+ if (rc)
+ return rc;
+
+ rc = gpio_direction_input(gpio);
+ if (rc) {
+ pr_err("%s: unable to set input gpio %s(%d) (%d)\n",
+ __func__, label, gpio, rc);
+ gpio_free(gpio);
+ return rc;
+ }
+ return rc;
+}
+
static int bt_configure_gpios(struct btpower_platform_data *drvdata, bool on)
{
int rc = 0;
@@ -409,131 +482,57 @@ static int bt_configure_gpios(struct btpower_platform_data *drvdata, bool on)
int wl_reset_gpio = drvdata->wl_gpio_sys_rst;
int bt_sw_ctrl_gpio = drvdata->bt_gpio_sw_ctrl;
int bt_debug_gpio = drvdata->bt_gpio_debug;
- int assert_dbg_gpio = 0;
- pr_info("%s: BT_EN GPIO(%d) value(%d) enabling: %s\n", __func__,
+ pr_info("%s: BT_RESET_GPIO(%d) value(%d) enabling: %s\n", __func__,
bt_reset_gpio, gpio_get_value(bt_reset_gpio),
(on ? "True" : "False"));
- if (!on) {
- gpio_set_value(bt_reset_gpio, 0);
- pr_debug("%s: BT-OFF bt-reset-gpio(%d)\n", __func__,
- bt_reset_gpio);
- msleep(100);
- if (bt_sw_ctrl_gpio >= 0) {
- drvdata->bt_power_src_status[BT_SW_CTRL_GPIO] =
- gpio_get_value(bt_sw_ctrl_gpio);
- pr_debug("%s: BT-OFF bt-sw-ctrl-gpio(%d) value(%d)\n",
- __func__, bt_sw_ctrl_gpio,
- drvdata->bt_power_src_status[BT_SW_CTRL_GPIO]);
- }
- return 0;
- }
-
- rc = gpio_request(bt_reset_gpio, "bt_sys_rst_n");
- if (rc) {
- pr_err("%s: unable to request gpio(%d) (%d)\n", __func__,
- bt_reset_gpio, rc);
- return rc;
- }
+ /* always reset the controller no metter ON or OFF */
+ SET_GPIO_SOURCE_STATE(drvdata, bt_reset_gpio, BT_RESET_GPIO, 0);
+ msleep(on ? 100 : 50);
+ SYNC_GPIO_SOURCE_CURRENT(drvdata, bt_sw_ctrl_gpio, BT_SW_CTRL_GPIO);
- rc = gpio_direction_output(bt_reset_gpio, 0);
- if (rc) {
- pr_err("%s: unable to set direction gpio(%d) (%d)\n",
- __func__, bt_reset_gpio, rc);
- return rc;
- }
- drvdata->bt_power_src_status[BT_RESET_GPIO] = 0;
- pr_debug("%s: BT-ON turns off bt-reset-gpio(%d)\n", __func__,
- bt_reset_gpio);
- msleep(50);
+ if (!on)
+ return 0;
- if (bt_sw_ctrl_gpio >= 0) {
- drvdata->bt_power_src_status[BT_SW_CTRL_GPIO] =
- gpio_get_value(bt_sw_ctrl_gpio);
- pr_debug("%s: BT-ON bt-sw-ctrl-gpio(%d) value(%d)\n",
- __func__, bt_sw_ctrl_gpio,
- drvdata->bt_power_src_status[BT_SW_CTRL_GPIO]);
- }
- if (wl_reset_gpio >= 0)
+ if (gpio_is_valid(wl_reset_gpio))
pr_debug("%s: BT-ON wl-reset-gpio(%d) value(%d)\n",
__func__, wl_reset_gpio, gpio_get_value(wl_reset_gpio));
- if ((wl_reset_gpio < 0) ||
- ((wl_reset_gpio >= 0) && gpio_get_value(wl_reset_gpio))) {
+ if (!gpio_is_valid(wl_reset_gpio) || gpio_get_value(wl_reset_gpio)) {
btpower_set_xo_clk_gpio_state(drvdata, true);
pr_info("%s: BT-ON asserting BT_EN (with WLAN)\n", __func__);
- rc = gpio_direction_output(bt_reset_gpio, 1);
- if (rc) {
- pr_err("%s: unable to set direction gpio(%d) (%d)\n",
- __func__, bt_reset_gpio, rc);
- return rc;
- }
- drvdata->bt_power_src_status[BT_RESET_GPIO] = 1;
+ SET_GPIO_SOURCE_STATE(drvdata, bt_reset_gpio, BT_RESET_GPIO, 1);
btpower_set_xo_clk_gpio_state(drvdata, false);
}
- if ((wl_reset_gpio >= 0) && (gpio_get_value(wl_reset_gpio) == 0)) {
+ if (gpio_is_valid(wl_reset_gpio) && !gpio_get_value(wl_reset_gpio)) {
if (gpio_get_value(bt_reset_gpio)) {
pr_warn("%s: WLAN OFF / BT ON too close. Delay BT_EN\n",
__func__);
- rc = gpio_direction_output(bt_reset_gpio, 0);
- if (rc) {
- pr_err("%s: unable to set direction gpio(%d) (%d)\n",
- __func__, bt_reset_gpio, rc);
- return rc;
- }
- drvdata->bt_power_src_status[BT_RESET_GPIO] = 0;
+ SET_GPIO_SOURCE_STATE(drvdata, bt_reset_gpio,
+ BT_RESET_GPIO, 0);
msleep(100);
pr_warn("%s: 100ms delay for AON output to fully discharge\n",
__func__);
}
btpower_set_xo_clk_gpio_state(drvdata, true);
pr_info("%s: BT-ON asserting BT_EN without WLAN\n", __func__);
- rc = gpio_direction_output(bt_reset_gpio, 1);
- if (rc) {
- pr_err("%s: unable to set direction gpio(%d) (%d)\n",
- __func__, bt_reset_gpio, rc);
- return rc;
- }
- drvdata->bt_power_src_status[BT_RESET_GPIO] = 1;
+ SET_GPIO_SOURCE_STATE(drvdata, bt_reset_gpio, BT_RESET_GPIO, 1);
btpower_set_xo_clk_gpio_state(drvdata, false);
}
msleep(50);
/* Check if SW_CTRL is asserted */
- if (bt_sw_ctrl_gpio >= 0) {
- rc = gpio_direction_input(bt_sw_ctrl_gpio);
- if (rc) {
- pr_err("%s: unable to set direction gpio(%d) (%d)\n",
- __func__, bt_sw_ctrl_gpio, rc);
- } else if (!gpio_get_value(bt_sw_ctrl_gpio)) {
- /* SW_CTRL not asserted, assert debug GPIO */
- if (bt_debug_gpio >= 0)
- assert_dbg_gpio = 1;
- }
- }
- if (assert_dbg_gpio) {
- rc = gpio_request(bt_debug_gpio, "bt_debug_n");
- if (rc) {
- pr_err("%s: unable to request gpio(%d) (%d)\n",
- __func__, bt_debug_gpio, rc);
- } else {
- rc = gpio_direction_output(bt_debug_gpio, 1);
- if (rc)
- pr_err("%s: unable to set direction gpio(%d) (%d)\n",
- __func__, bt_debug_gpio, rc);
- }
- }
-
- if (bt_sw_ctrl_gpio >= 0) {
- drvdata->bt_power_src_status[BT_SW_CTRL_GPIO] =
- gpio_get_value(bt_sw_ctrl_gpio);
- pr_debug("%s: BT-ON bt-sw-ctrl-gpio(%d) value(%d)\n",
+ SYNC_GPIO_SOURCE_CURRENT(drvdata, bt_sw_ctrl_gpio, BT_SW_CTRL_GPIO);
+ if (drvdata->bt_power_src_status[BT_SW_CTRL_GPIO_CURRENT] == 0) {
+ /* SW_CTRL not asserted, assert debug GPIO */
+ if (gpio_is_valid(bt_debug_gpio))
+ gpio_set_value(bt_debug_gpio, 1);
+ pr_warn("%s: BT_SW_CTRL_GPIO(%d) value(%d) not asserted\n",
__func__, bt_sw_ctrl_gpio,
- drvdata->bt_power_src_status[BT_SW_CTRL_GPIO]);
+ drvdata->bt_power_src_status[BT_SW_CTRL_GPIO_CURRENT]);
}
- pr_debug("%s: BT-ON bt-reset-gpio(%d)\n", __func__, bt_reset_gpio);
return rc;
}
@@ -552,10 +551,9 @@ static int bluetooth_power(struct btpower_platform_data *drvdata,
switch (mode) {
case BT_POWER_DISABLE:
- if (drvdata->bt_gpio_sys_rst > 0)
- bt_configure_gpios(drvdata, false);
+ bt_configure_gpios(drvdata, false);
drvdata->pwr_state = BT_POWER_DISABLE;
- goto gpio_free;
+ goto clk_disable;
case BT_POWER_ENABLE:
rc = bt_power_vreg_set(drvdata, BT_POWER_ENABLE);
if (rc < 0) {
@@ -574,17 +572,12 @@ static int bluetooth_power(struct btpower_platform_data *drvdata,
goto vreg_disable;
}
}
- if (drvdata->bt_gpio_sys_rst > 0) {
- drvdata->bt_power_src_status[BT_RESET_GPIO] =
- DEFAULT_INVALID_VALUE;
- drvdata->bt_power_src_status[BT_SW_CTRL_GPIO] =
- DEFAULT_INVALID_VALUE;
- rc = bt_configure_gpios(drvdata, true);
- if (rc < 0) {
- pr_err("%s: bt_power gpio config failed\n",
- __func__);
- goto gpio_free;
- }
+ drvdata->bt_power_src_status[BT_RESET_GPIO] =
+ DEFAULT_INVALID_VALUE;
+ rc = bt_configure_gpios(drvdata, true);
+ if (rc < 0) {
+ pr_err("%s: bt_power gpio config failed\n", __func__);
+ goto clk_disable;
}
drvdata->pwr_state = BT_POWER_ENABLE;
return rc;
@@ -597,11 +590,7 @@ static int bluetooth_power(struct btpower_platform_data *drvdata,
return -1;
}
-gpio_free:
- if (drvdata->bt_gpio_sys_rst > 0)
- gpio_free(drvdata->bt_gpio_sys_rst);
- if (drvdata->bt_gpio_debug > 0)
- gpio_free(drvdata->bt_gpio_debug);
+clk_disable:
if (drvdata->bt_chip_clk)
bt_clk_disable(drvdata->bt_chip_clk);
vreg_disable:
@@ -927,6 +916,57 @@ static void bt_power_vreg_put(struct btpower_platform_data *drvdata)
}
}
+static void btpower_gpios_source_release(struct btpower_platform_data *drvdata);
+static int btpower_gpios_source_initialize(struct btpower_platform_data *drvdata)
+{
+ int rc = 0;
+
+ rc = btpower_gpio_acquire_output(drvdata->bt_gpio_sys_rst,
+ "bt_sys_rst_n", 0);
+ if (rc) {
+ drvdata->bt_gpio_sys_rst = -1;
+ return rc;
+ }
+
+ if (gpio_is_valid(drvdata->bt_gpio_sw_ctrl)) {
+ rc = btpower_gpio_acquire_input(drvdata->bt_gpio_sw_ctrl,
+ "bt_sw_ctrl_n");
+ if (rc) {
+ drvdata->bt_gpio_sw_ctrl = -1;
+ goto gpio_failure;
+ }
+ }
+
+ if (gpio_is_valid(drvdata->bt_gpio_debug)) {
+ rc = btpower_gpio_acquire_output(drvdata->bt_gpio_debug,
+ "bt_debug_n", 0);
+ if (rc) {
+ drvdata->bt_gpio_debug = -1;
+ goto gpio_failure;
+ }
+ }
+
+ return 0;
+
+gpio_failure:
+ btpower_gpios_source_release(drvdata);
+ return rc;
+}
+
+static void btpower_gpios_source_release(struct btpower_platform_data *drvdata)
+{
+ if (gpio_is_valid(drvdata->bt_gpio_debug)) {
+ gpio_free(drvdata->bt_gpio_debug);
+ drvdata->bt_gpio_debug = -1;
+ }
+ if (gpio_is_valid(drvdata->bt_gpio_sw_ctrl)) {
+ gpio_free(drvdata->bt_gpio_sw_ctrl);
+ drvdata->bt_gpio_sw_ctrl = -1;
+ }
+ gpio_free(drvdata->bt_gpio_sys_rst);
+ drvdata->bt_gpio_sys_rst = -1;
+}
+
static int bt_power_populate_dt_pinfo(struct platform_device *pdev,
struct btpower_platform_data *drvdata)
{
@@ -946,37 +986,39 @@ static int bt_power_populate_dt_pinfo(struct platform_device *pdev,
drvdata->bt_gpio_sys_rst =
of_get_named_gpio(pdev->dev.of_node, "qcom,bt-reset-gpio", 0);
- if (drvdata->bt_gpio_sys_rst < 0)
- pr_warn("%s: bt-reset-gpio not provided in device tree\n",
+ if (!gpio_is_valid(drvdata->bt_gpio_sys_rst)) {
+ pr_err("%s: bt-reset-gpio not provided in device tree\n",
__func__);
+ return -EIO;
+ }
drvdata->wl_gpio_sys_rst =
of_get_named_gpio(pdev->dev.of_node, "qcom,wl-reset-gpio", 0);
- if (drvdata->wl_gpio_sys_rst < 0)
- pr_warn("%s: wl-reset-gpio not provided in device tree\n",
+ if (!gpio_is_valid(drvdata->wl_gpio_sys_rst))
+ pr_info("%s: wl-reset-gpio not provided in device tree\n",
__func__);
drvdata->bt_gpio_sw_ctrl =
of_get_named_gpio(pdev->dev.of_node, "qcom,bt-sw-ctrl-gpio", 0);
- if (drvdata->bt_gpio_sw_ctrl < 0)
- pr_warn("%s: bt-sw-ctrl-gpio not provided in device tree\n",
+ if (!gpio_is_valid(drvdata->bt_gpio_sw_ctrl))
+ pr_info("%s: bt-sw-ctrl-gpio not provided in device tree\n",
__func__);
drvdata->bt_gpio_debug =
of_get_named_gpio(pdev->dev.of_node, "qcom,bt-debug-gpio", 0);
- if (drvdata->bt_gpio_debug < 0)
- pr_warn("%s: bt-debug-gpio not provided in device tree\n",
+ if (!gpio_is_valid(drvdata->bt_gpio_debug))
+ pr_info("%s: bt-debug-gpio not provided in device tree\n",
__func__);
drvdata->xo_gpio_clk =
of_get_named_gpio(pdev->dev.of_node, "qcom,xo-clk-gpio", 0);
- if (drvdata->xo_gpio_clk < 0)
- pr_warn("%s: xo-clk-gpio not provided in device tree\n",
+ if (!gpio_is_valid(drvdata->xo_gpio_clk))
+ pr_info("%s: xo-clk-gpio not provided in device tree\n",
__func__);
rc = bt_dt_parse_clk_info(&pdev->dev, &drvdata->bt_chip_clk);
if (rc < 0)
- pr_warn("%s: clock not provided in device tree\n", __func__);
+ pr_info("%s: clock not provided in device tree\n", __func__);
drvdata->bt_power_setup = bluetooth_power;
@@ -1023,14 +1065,18 @@ static int bt_power_probe(struct platform_device *pdev)
}
drvdata->pwr_state = BT_POWER_DISABLE;
- ret = btpower_rfkill_probe(pdev, drvdata);
+ ret = btpower_gpios_source_initialize(drvdata);
if (ret < 0)
goto free_pdata;
+ ret = btpower_rfkill_probe(pdev, drvdata);
+ if (ret < 0)
+ goto free_gpio;
+
ret = btpower_chardev_create(drvdata);
if (ret) {
btpower_rfkill_remove(pdev);
- goto free_pdata;
+ goto free_gpio;
}
btpower_aop_mbox_init(drvdata);
@@ -1039,6 +1085,8 @@ static int bt_power_probe(struct platform_device *pdev)
return 0;
+free_gpio:
+ btpower_gpios_source_release(drvdata);
free_pdata:
kfree(drvdata);
return ret;
@@ -1057,6 +1105,7 @@ static int bt_power_remove(struct platform_device *pdev)
btpower_rfkill_remove(pdev);
bt_power_vreg_put(drvdata);
+ btpower_gpios_source_release(drvdata);
kfree(drvdata);
return 0;
@@ -1105,20 +1154,6 @@ static void set_pwr_srcs_status(struct btpower_platform_data *drvdata,
}
}
-static void set_gpios_srcs_status(struct btpower_platform_data *drvdata,
- char *gpio_name, int gpio_index, int handle)
-{
- if (handle < 0) {
- drvdata->bt_power_src_status[gpio_index] = DEFAULT_INVALID_VALUE;
- pr_err("%s: %s not configured\n", __func__, gpio_name);
- return;
- }
-
- drvdata->bt_power_src_status[gpio_index] = gpio_get_value(handle);
- pr_debug("%s(%d) value(%d)\n", gpio_name, handle,
- drvdata->bt_power_src_status[gpio_index]);
-}
-
static int btpower_open(struct inode *inode, struct file *filp)
{
filp->private_data =
@@ -1185,30 +1220,17 @@ static long btpower_ioctl(struct file *file, unsigned int cmd, unsigned long arg
case BT_CMD_CHECK_SW_CTRL:
/* Check if SW_CTRL is asserted */
pr_debug("%s: BT_CMD_CHECK_SW_CTRL\n", __func__);
- if (drvdata->bt_gpio_sw_ctrl <= 0) {
- pr_err("%s: bt_gpio_sw_ctrl not configured\n", __func__);
+ if (gpio_is_valid(drvdata->bt_gpio_sw_ctrl))
return -EINVAL;
- }
- ret = gpio_direction_input(drvdata->bt_gpio_sw_ctrl);
- if (ret) {
- pr_err("%s: unable to set direction gpio(%d) (%d)\n",
- __func__, drvdata->bt_gpio_sw_ctrl, ret);
- drvdata->bt_power_src_status[BT_SW_CTRL_GPIO] =
- DEFAULT_INVALID_VALUE;
- break;
- }
- drvdata->bt_power_src_status[BT_SW_CTRL_GPIO] =
- gpio_get_value(drvdata->bt_gpio_sw_ctrl);
- pr_debug("%s: bt-sw-ctrl-gpio(%d) value(%d)\n", __func__,
- drvdata->bt_gpio_sw_ctrl,
- drvdata->bt_power_src_status[BT_SW_CTRL_GPIO]);
+ SYNC_GPIO_SOURCE_CURRENT(drvdata, drvdata->bt_gpio_sw_ctrl,
+ BT_SW_CTRL_GPIO);
break;
case BT_CMD_GETVAL_POWER_SRCS:
pr_debug("%s: BT_CMD_GETVAL_POWER_SRCS\n", __func__);
- set_gpios_srcs_status(drvdata, "BT_RESET_GPIO",
- BT_RESET_GPIO_CURRENT, drvdata->bt_gpio_sys_rst);
- set_gpios_srcs_status(drvdata, "SW_CTRL_GPIO",
- BT_SW_CTRL_GPIO_CURRENT, drvdata->bt_gpio_sw_ctrl);
+ SYNC_GPIO_SOURCE_CURRENT(drvdata, drvdata->bt_gpio_sys_rst,
+ BT_RESET_GPIO);
+ SYNC_GPIO_SOURCE_CURRENT(drvdata, drvdata->bt_gpio_sw_ctrl,
+ BT_SW_CTRL_GPIO);
num_vregs = drvdata->num_vregs;
for (itr = 0; itr < num_vregs; itr++) {
vreg_info = &drvdata->vreg_info[itr];