summaryrefslogtreecommitdiff
path: root/btpower.c
diff options
context:
space:
mode:
authorCheney Ni <cheneyni@google.com>2021-12-07 18:57:49 +0800
committerCheney Ni <cheneyni@google.com>2021-12-11 00:26:49 +0800
commitfa2c58038c8083f5ede9055d0a64f8a65b00371f (patch)
tree8e96b955e5b56e3cbada79ec30a95c4d12ba6607 /btpower.c
parent7d3d070b5e3a78cdaf6a396e30b13c7962fb753f (diff)
downloadqcom-fa2c58038c8083f5ede9055d0a64f8a65b00371f.tar.gz
btpower: acquire GPIO while probing
Allocate all necessary GPIO pins specified at the device tree while this driver probing, error out if any failure, and release them while removing the driver. There are two macro helpers for setting and getting GPIO values. Bug: 202113218 Test: manually Signed-off-by: Cheney Ni <cheneyni@google.com> Change-Id: Ie0fbe12352eb73d983cf1a6fe4bddd74d5b55b00
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];