Loading drivers/bluetooth/btpower.c +253 −82 Original line number Diff line number Diff line Loading @@ -23,6 +23,7 @@ #include <linux/uaccess.h> #include <linux/btpower.h> #include <linux/of_device.h> #include <soc/qcom/cmd-db.h> #if defined CONFIG_BT_SLIM_QCA6390 || \ defined CONFIG_BT_SLIM_QCA6490 || \ Loading @@ -31,27 +32,75 @@ #endif #include <linux/fs.h> #define PWR_SRC_NOT_AVAILABLE -2 #define DEFAULT_INVALID_VALUE -1 #define PWR_SRC_INIT_STATE_IDX 0 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, BT_VDD_RFA2_LDO, BT_VDD_ASD_LDO, BT_VDD_XTAL_LDO, BT_VDD_PA_LDO, BT_VDD_CORE_LDO, BT_VDD_IO_LDO, BT_VDD_LDO, BT_VDD_RFA_0p8, BT_VDD_RFACMN, // these indexes GPIOs/regs value are fetched during crash. BT_RESET_GPIO_CURRENT, BT_SW_CTRL_GPIO_CURRENT, BT_VDD_AON_LDO_CURRENT, BT_VDD_DIG_LDO_CURRENT, BT_VDD_RFA1_LDO_CURRENT, BT_VDD_RFA2_LDO_CURRENT, BT_VDD_ASD_LDO_CURRENT, BT_VDD_XTAL_LDO_CURRENT, BT_VDD_PA_LDO_CURRENT, BT_VDD_CORE_LDO_CURRENT, BT_VDD_IO_LDO_CURRENT, BT_VDD_LDO_CURRENT, BT_VDD_RFA_0p8_CURRENT, BT_VDD_RFACMN_CURRENT }; // Regulator structure for QCA6390 and QCA6490 BT SoC series static struct bt_power_vreg_data bt_vregs_info_qca6x9x[] = { {NULL, "qcom,bt-vdd-io", 1800000, 1800000, 0, false, true}, {NULL, "qcom,bt-vdd-aon", 950000, 950000, 0, false, true}, {NULL, "qcom,bt-vdd-rfacmn", 950000, 950000, 0, false, true}, {NULL, "qcom,bt-vdd-io", 1800000, 1800000, 0, false, true, {BT_VDD_IO_LDO, BT_VDD_IO_LDO_CURRENT}}, {NULL, "qcom,bt-vdd-aon", 950000, 950000, 0, false, true, {BT_VDD_AON_LDO, BT_VDD_AON_LDO_CURRENT}}, {NULL, "qcom,bt-vdd-rfacmn", 950000, 950000, 0, false, true, {BT_VDD_RFACMN, BT_VDD_RFACMN_CURRENT}}, /* BT_CX_MX */ {NULL, "qcom,bt-vdd-dig", 950000, 952000, 0, false, true}, {NULL, "qcom,bt-vdd-rfa-0p8", 950000, 952000, 0, false, true}, {NULL, "qcom,bt-vdd-rfa1", 1900000, 1900000, 0, false, true}, {NULL, "qcom,bt-vdd-rfa2", 1900000, 1900000, 0, false, true}, {NULL, "qcom,bt-vdd-asd", 2800000, 2800000, 0, false, true}, {NULL, "qcom,bt-vdd-dig", 950000, 952000, 0, false, true, {BT_VDD_DIG_LDO, BT_VDD_DIG_LDO_CURRENT}}, {NULL, "qcom,bt-vdd-rfa-0p8", 950000, 952000, 0, false, true, {BT_VDD_RFA_0p8, BT_VDD_RFA_0p8_CURRENT}}, {NULL, "qcom,bt-vdd-rfa1", 1900000, 1900000, 0, false, true, {BT_VDD_RFA1_LDO, BT_VDD_RFA1_LDO_CURRENT}}, {NULL, "qcom,bt-vdd-rfa2", 1900000, 1900000, 0, false, true, {BT_VDD_RFA2_LDO, BT_VDD_RFA2_LDO_CURRENT}}, {NULL, "qcom,bt-vdd-asd", 2800000, 2800000, 0, false, true, {BT_VDD_ASD_LDO, BT_VDD_ASD_LDO_CURRENT}}, }; // Regulator structure for WCN399x BT SoC series static struct bt_power bt_vreg_info_wcn399x = { .compatible = "qcom,wcn3990", .vregs = (struct bt_power_vreg_data []) { {NULL, "qcom,bt-vdd-io", 1700000, 1900000, 0, false, false}, {NULL, "qcom,bt-vdd-core", 1304000, 1304000, 0, false, false}, {NULL, "qcom,bt-vdd-pa", 3000000, 3312000, 0, false, false}, {NULL, "qcom,bt-vdd-xtal", 1700000, 1900000, 0, false, false}, {NULL, "qcom,bt-vdd-io", 1700000, 1900000, 0, false, false, {BT_VDD_IO_LDO, BT_VDD_IO_LDO_CURRENT}}, {NULL, "qcom,bt-vdd-core", 1304000, 1304000, 0, false, false, {BT_VDD_CORE_LDO, BT_VDD_CORE_LDO_CURRENT}}, {NULL, "qcom,bt-vdd-pa", 3000000, 3312000, 0, false, false, {BT_VDD_PA_LDO, BT_VDD_PA_LDO_CURRENT}}, {NULL, "qcom,bt-vdd-xtal", 1700000, 1900000, 0, false, false, {BT_VDD_XTAL_LDO, BT_VDD_XTAL_LDO_CURRENT}}, }, .num_vregs = 4, }; Loading @@ -77,7 +126,12 @@ static const struct of_device_id bt_power_match_table[] = { }; static int bt_power_vreg_set(enum bt_power_modes mode); static int btpower_get_tcs_table_info(struct platform_device *plat_dev, struct bluetooth_power_platform_data *bt_power_pdata); static int btpower_enable_ipa_vreg(struct platform_device *plat_dev, struct bluetooth_power_platform_data *bt_power_pdata); static int bt_power_src_status[BT_POWER_SRC_SIZE]; static struct bluetooth_power_platform_data *bt_power_pdata; static struct platform_device *btpdev; static bool previous; Loading Loading @@ -260,14 +314,18 @@ static int bt_configure_gpios(int on) pr_err("%s: Unable to set direction\n", __func__); return rc; } bt_power_src_status[BT_RESET_GPIO] = gpio_get_value(bt_reset_gpio); msleep(50); pr_debug("BTON:Turn Bt Off bt-reset-gpio(%d) value(%d)\n", pr_info("BTON:Turn Bt Off bt-reset-gpio(%d) value(%d)\n", bt_reset_gpio, gpio_get_value(bt_reset_gpio)); if (bt_sw_ctrl_gpio >= 0) { pr_debug("BTON:Turn Bt Off\n"); pr_debug("bt-sw-ctrl-gpio(%d) value(%d)\n", pr_info("BTON:Turn Bt Off\n"); bt_power_src_status[BT_SW_CTRL_GPIO] = gpio_get_value(bt_sw_ctrl_gpio); pr_info("bt-sw-ctrl-gpio(%d) value(%d)\n", bt_sw_ctrl_gpio, gpio_get_value(bt_sw_ctrl_gpio)); bt_power_src_status[BT_SW_CTRL_GPIO]); } rc = gpio_direction_output(bt_reset_gpio, 1); Loading @@ -275,6 +333,8 @@ static int bt_configure_gpios(int on) pr_err("%s: Unable to set direction\n", __func__); return rc; } bt_power_src_status[BT_RESET_GPIO] = gpio_get_value(bt_reset_gpio); msleep(50); /* Check if SW_CTRL is asserted */ if (bt_sw_ctrl_gpio >= 0) { Loading Loading @@ -302,24 +362,26 @@ static int bt_configure_gpios(int on) pr_info("BTON:Turn Bt On bt-reset-gpio(%d) value(%d)\n", bt_reset_gpio, gpio_get_value(bt_reset_gpio)); if (bt_sw_ctrl_gpio >= 0) { pr_debug("BTON:Turn Bt On\n"); pr_debug("bt-sw-ctrl-gpio(%d) value(%d)\n", pr_info("BTON:Turn Bt On\n"); bt_power_src_status[BT_SW_CTRL_GPIO] = gpio_get_value(bt_sw_ctrl_gpio); pr_info("bt-sw-ctrl-gpio(%d) value(%d)\n", bt_sw_ctrl_gpio, gpio_get_value(bt_sw_ctrl_gpio)); bt_power_src_status[BT_SW_CTRL_GPIO]); } } else { gpio_set_value(bt_reset_gpio, 0); msleep(100); pr_debug("BT-OFF:bt-reset-gpio(%d) value(%d)\n", pr_info("BT-OFF:bt-reset-gpio(%d) value(%d)\n", bt_reset_gpio, gpio_get_value(bt_reset_gpio)); if (bt_sw_ctrl_gpio >= 0) { pr_debug("BT-OFF:bt-sw-ctrl-gpio(%d) value(%d)\n", pr_info("BT-OFF:bt-sw-ctrl-gpio(%d) value(%d)\n", bt_sw_ctrl_gpio, gpio_get_value(bt_sw_ctrl_gpio)); } } pr_debug("%s: bt_gpio= %d on: %d\n", __func__, bt_reset_gpio, on); pr_info("%s: bt_gpio= %d on: %d\n", __func__, bt_reset_gpio, on); return rc; } Loading Loading @@ -349,6 +411,10 @@ static int bluetooth_power(int on) } } if (bt_power_pdata->bt_gpio_sys_rst > 0) { bt_power_src_status[BT_RESET_GPIO] = DEFAULT_INVALID_VALUE; bt_power_src_status[BT_SW_CTRL_GPIO] = DEFAULT_INVALID_VALUE; rc = bt_configure_gpios(on); if (rc < 0) { pr_err("%s: bt_power gpio config failed\n", Loading Loading @@ -580,16 +646,25 @@ static int bt_power_vreg_get(struct platform_device *pdev) static int bt_power_vreg_set(enum bt_power_modes mode) { int num_vregs, i = 0, ret = 0; int log_indx; struct bt_power_vreg_data *vreg_info = NULL; num_vregs = bt_power_pdata->num_vregs; if (mode == BT_POWER_ENABLE) { for (; i < num_vregs; i++) { vreg_info = &bt_power_pdata->vreg_info[i]; log_indx = vreg_info->indx.init; if (vreg_info->reg) { bt_power_src_status[log_indx] = DEFAULT_INVALID_VALUE; ret = bt_vreg_enable(vreg_info); if (ret < 0) goto out; if (vreg_info->is_enabled) { bt_power_src_status[log_indx] = regulator_get_voltage( vreg_info->reg); } } } } else if (mode == BT_POWER_DISABLE) { Loading Loading @@ -672,9 +747,17 @@ static int bt_power_populate_dt_pinfo(struct platform_device *pdev) static int bt_power_probe(struct platform_device *pdev) { int ret = 0; int itr; pr_debug("%s\n", __func__); /* Fill whole array with -2 i.e NOT_AVAILABLE state by default * for any GPIO or Reg handle. */ for (itr = PWR_SRC_INIT_STATE_IDX; itr < BT_POWER_SRC_SIZE; ++itr) bt_power_src_status[itr] = PWR_SRC_NOT_AVAILABLE; bt_power_pdata = kzalloc(sizeof(*bt_power_pdata), GFP_KERNEL); if (!bt_power_pdata) Loading Loading @@ -708,6 +791,8 @@ static int bt_power_probe(struct platform_device *pdev) goto free_pdata; btpdev = pdev; if (btpower_get_tcs_table_info(pdev, bt_power_pdata) < 0) pr_err("%s: Failed to get TCS table info\n", __func__); return 0; Loading Loading @@ -747,46 +832,50 @@ int btpower_get_chipset_version(void) } EXPORT_SYMBOL(btpower_get_chipset_version); static long get_resource_value(char *res_name) static void set_pwr_srcs_status(struct bt_power_vreg_data *handle) { int ret = 0; int i = 0; long value = -1; struct bt_power_vreg_data *vreg; int num_vregs = bt_power_pdata->num_vregs; for (; i < num_vregs; i++) { vreg = &bt_power_pdata->vreg_info[i]; if (!vreg->reg) continue; else { if (strnstr(vreg->name, res_name, strlen(vreg->name))) break; } } if (i < num_vregs) { if (regulator_is_enabled(vreg->reg)) { value = (int)regulator_get_voltage(vreg->reg); pr_debug("%s:%s value(%d)\n", __func__, res_name, value); ret = value; int ldo_index; if (handle) { ldo_index = handle->indx.crash; bt_power_src_status[ldo_index] = DEFAULT_INVALID_VALUE; if (handle->is_enabled && (regulator_is_enabled(handle->reg))) { bt_power_src_status[ldo_index] = (int)regulator_get_voltage(handle->reg); pr_err("%s(%d) value(%d)\n", handle->name, handle, bt_power_src_status[ldo_index]); } else { pr_err("%s:%s not configure/enabled\n", __func__, res_name); ret = -EINVAL; pr_err("%s:%s is_enabled: %d\n", __func__, handle->name, handle->is_enabled); } } } return ret; static void set_gpios_srcs_status(char *gpio_name, int gpio_index, int handle) { if (handle >= 0) { bt_power_src_status[gpio_index] = DEFAULT_INVALID_VALUE; bt_power_src_status[gpio_index] = gpio_get_value(handle); pr_err("%s(%d) value(%d)\n", gpio_name, handle, bt_power_src_status[gpio_index]); } else { pr_err("%s: %s not configured\n", __func__, gpio_name); } } static long bt_ioctl(struct file *file, unsigned int cmd, unsigned long arg) { int ret = 0, pwr_cntrl = 0; int chipset_version = 0; long value = -1; int itr, num_vregs; struct bt_power_vreg_data *vreg_info = NULL; switch (cmd) { case BT_CMD_SLIM_TEST: Loading Loading @@ -834,43 +923,53 @@ static long bt_ioctl(struct file *file, unsigned int cmd, unsigned long arg) ret = -EFAULT; } break; case BT_CMD_GETVAL_RESET_GPIO: if (bt_power_pdata->bt_gpio_sys_rst > 0) { value = (long)gpio_get_value( bt_power_pdata->bt_gpio_sys_rst); pr_debug("%s:GET_RESET_GPIO(%d) value(%d)\n", bt_power_pdata->bt_gpio_sys_rst, __func__, value); ret = value; } else { pr_err("%s: RESET_GPIO not configured\n", __func__); ret = -EINVAL; } break; case BT_CMD_GETVAL_SW_CTRL_GPIO: case BT_CMD_CHECK_SW_CTRL: /* Check if SW_CTRL is asserted */ pr_info("BT_CMD_CHECK_SW_CTRL\n"); if (bt_power_pdata->bt_gpio_sw_ctrl > 0) { value = (long)gpio_get_value( bt_power_src_status[BT_SW_CTRL_GPIO] = DEFAULT_INVALID_VALUE; ret = gpio_direction_input( bt_power_pdata->bt_gpio_sw_ctrl); pr_debug("%s:GET_SWCTRL_GPIO(%d) value(%d)\n", if (ret) { pr_err("%s:gpio_direction_input api\n", __func__); pr_err("%s:failed for SW_CTRL:%d\n", __func__, ret); } else { bt_power_src_status[BT_SW_CTRL_GPIO] = gpio_get_value( bt_power_pdata->bt_gpio_sw_ctrl); pr_info("bt-sw-ctrl-gpio(%d) value(%d)\n", bt_power_pdata->bt_gpio_sw_ctrl, __func__, value); ret = value; bt_power_src_status[BT_SW_CTRL_GPIO]); } } else { pr_err("%s:SW_CTRL_GPIO not configured\n", __func__); ret = -EINVAL; pr_err("bt_gpio_sw_ctrl not configured\n"); return -EINVAL; } break; case BT_CMD_GETVAL_VDD_AON_LDO: ret = get_resource_value("vdd-aon"); break; case BT_CMD_GETVAL_VDD_DIG_LDO: ret = get_resource_value("vdd-dig"); break; case BT_CMD_GETVAL_VDD_RFA1_LDO: ret = get_resource_value("vdd-rfa1"); case BT_CMD_GETVAL_POWER_SRCS: pr_err("BT_CMD_GETVAL_POWER_SRCS\n"); set_gpios_srcs_status("BT_RESET_GPIO", BT_RESET_GPIO_CURRENT, bt_power_pdata->bt_gpio_sys_rst); set_gpios_srcs_status("SW_CTRL_GPIO", BT_SW_CTRL_GPIO_CURRENT, bt_power_pdata->bt_gpio_sw_ctrl); num_vregs = bt_power_pdata->num_vregs; for (itr = 0; itr < num_vregs; itr++) { vreg_info = &bt_power_pdata->vreg_info[itr]; set_pwr_srcs_status(vreg_info); } if (copy_to_user((void __user *)arg, bt_power_src_status, sizeof(bt_power_src_status))) { pr_err("%s: copy to user failed\n", __func__); ret = -EFAULT; } break; case BT_CMD_GETVAL_VDD_RFA2_LDO: ret = get_resource_value("vdd-rfa2"); case BT_CMD_SET_IPA_TCS_INFO: pr_err("%s: BT_CMD_SET_IPA_TCS_INFO\n", __func__); btpower_enable_ipa_vreg(btpdev, bt_power_pdata); break; default: return -ENOIOCTLCMD; Loading Loading @@ -935,6 +1034,78 @@ static int __init bluetooth_power_init(void) return ret; } static int btpower_get_tcs_table_info(struct platform_device *dev, struct bluetooth_power_platform_data *bt_power_pdata) { struct platform_device *plat_dev = dev; struct btpower_tcs_table_info *tcs_table_info = &bt_power_pdata->tcs_table_info; struct resource *res; resource_size_t addr_len; void __iomem *tcs_cmd_base_addr; int ret = -1; res = platform_get_resource_byname(plat_dev, IORESOURCE_MEM, "tcs_cmd"); if (!res) { pr_err("No TCS CMD entry found in DTSI\n"); goto out; } tcs_table_info->tcs_cmd_base_addr = res->start; addr_len = resource_size(res); pr_info("TCS CMD base address is %pa with length %pa\n", &tcs_table_info->tcs_cmd_base_addr, &addr_len); tcs_cmd_base_addr = devm_ioremap_resource(&plat_dev->dev, res); if (IS_ERR(tcs_cmd_base_addr)) { ret = PTR_ERR(tcs_cmd_base_addr); pr_err("Failed to map TCS CMD address, err = %d\n", ret); goto out; } tcs_table_info->tcs_cmd_base_addr_io = tcs_cmd_base_addr; return 0; out: return ret; } static int btpower_enable_ipa_vreg(struct platform_device *dev, struct bluetooth_power_platform_data *bt_power_pdata) { struct platform_device *plat_dev = dev; struct btpower_tcs_table_info *tcs_table_info = &bt_power_pdata->tcs_table_info; u32 offset, addr_val, data_val; void __iomem *tcs_cmd; int ret = 0; if (!tcs_table_info->tcs_cmd_base_addr_io) { pr_err("TCS command not configured\n"); return -EINVAL; } ret = of_property_read_u32(plat_dev->dev.of_node, "qcom,tcs_offset_ipa", &offset); if (ret) { pr_err("iPA failed to configure\n"); return -EINVAL; } tcs_cmd = tcs_table_info->tcs_cmd_base_addr_io + offset; addr_val = readl_relaxed(tcs_cmd); tcs_cmd += TCS_CMD_IO_ADDR_OFFSET; writel_relaxed(1, tcs_cmd); data_val = readl_relaxed(tcs_cmd); pr_info("Configure S3E TCS Addr : %x with Data: %d\n" , addr_val, data_val); return 0; } static void __exit bluetooth_power_exit(void) { platform_driver_unregister(&bt_power_driver); Loading include/linux/btpower.h +21 −7 Original line number Diff line number Diff line Loading @@ -18,6 +18,11 @@ enum bt_power_modes { BT_POWER_RETENTION }; struct log_index { int init; int crash; }; struct bt_power_vreg_data { struct regulator *reg; /* voltage regulator handle */ const char *name; /* regulator name */ Loading @@ -26,6 +31,7 @@ struct bt_power_vreg_data { u32 load_curr; /* current */ bool is_enabled; /* is this regulator enabled? */ bool is_retention_supp; /* does this regulator support retention mode */ struct log_index indx; /* Index for reg. w.r.t init & crash */ }; struct bt_power { Loading @@ -40,6 +46,10 @@ struct bt_power_clk_data { bool is_enabled; /* is this clock enabled? */ }; struct btpower_tcs_table_info { resource_size_t tcs_cmd_base_addr; void __iomem *tcs_cmd_base_addr_io; }; /* * Platform data for the bluetooth power driver. */ Loading @@ -53,6 +63,7 @@ struct bluetooth_power_platform_data { int (*bt_power_setup)(int id); /* Bluetooth power setup function */ char compatible[32]; /*Bluetooth SoC name */ int num_vregs; struct btpower_tcs_table_info tcs_table_info; }; int btpower_register_slimdev(struct device *dev); Loading @@ -62,10 +73,13 @@ int btpower_get_chipset_version(void); #define BT_CMD_PWR_CTRL 0xbfad #define BT_CMD_CHIPSET_VERS 0xbfae #define BT_CMD_GET_CHIPSET_ID 0xbfaf #define BT_CMD_GETVAL_RESET_GPIO 0xbfb5 #define BT_CMD_GETVAL_SW_CTRL_GPIO 0xbfb0 #define BT_CMD_GETVAL_VDD_AON_LDO 0xbfb1 #define BT_CMD_GETVAL_VDD_DIG_LDO 0xbfb2 #define BT_CMD_GETVAL_VDD_RFA1_LDO 0xbfb3 #define BT_CMD_GETVAL_VDD_RFA2_LDO 0xbfb4 #define BT_CMD_CHECK_SW_CTRL 0xbfb0 #define BT_CMD_GETVAL_POWER_SRCS 0xbfb1 #define BT_CMD_SET_IPA_TCS_INFO 0xbfc0 #define TCS_CMD_IO_ADDR_OFFSET 0x4 /* total number of power src */ #define BT_POWER_SRC_SIZE 28 #endif /* __LINUX_BLUETOOTH_POWER_H */ Loading
drivers/bluetooth/btpower.c +253 −82 Original line number Diff line number Diff line Loading @@ -23,6 +23,7 @@ #include <linux/uaccess.h> #include <linux/btpower.h> #include <linux/of_device.h> #include <soc/qcom/cmd-db.h> #if defined CONFIG_BT_SLIM_QCA6390 || \ defined CONFIG_BT_SLIM_QCA6490 || \ Loading @@ -31,27 +32,75 @@ #endif #include <linux/fs.h> #define PWR_SRC_NOT_AVAILABLE -2 #define DEFAULT_INVALID_VALUE -1 #define PWR_SRC_INIT_STATE_IDX 0 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, BT_VDD_RFA2_LDO, BT_VDD_ASD_LDO, BT_VDD_XTAL_LDO, BT_VDD_PA_LDO, BT_VDD_CORE_LDO, BT_VDD_IO_LDO, BT_VDD_LDO, BT_VDD_RFA_0p8, BT_VDD_RFACMN, // these indexes GPIOs/regs value are fetched during crash. BT_RESET_GPIO_CURRENT, BT_SW_CTRL_GPIO_CURRENT, BT_VDD_AON_LDO_CURRENT, BT_VDD_DIG_LDO_CURRENT, BT_VDD_RFA1_LDO_CURRENT, BT_VDD_RFA2_LDO_CURRENT, BT_VDD_ASD_LDO_CURRENT, BT_VDD_XTAL_LDO_CURRENT, BT_VDD_PA_LDO_CURRENT, BT_VDD_CORE_LDO_CURRENT, BT_VDD_IO_LDO_CURRENT, BT_VDD_LDO_CURRENT, BT_VDD_RFA_0p8_CURRENT, BT_VDD_RFACMN_CURRENT }; // Regulator structure for QCA6390 and QCA6490 BT SoC series static struct bt_power_vreg_data bt_vregs_info_qca6x9x[] = { {NULL, "qcom,bt-vdd-io", 1800000, 1800000, 0, false, true}, {NULL, "qcom,bt-vdd-aon", 950000, 950000, 0, false, true}, {NULL, "qcom,bt-vdd-rfacmn", 950000, 950000, 0, false, true}, {NULL, "qcom,bt-vdd-io", 1800000, 1800000, 0, false, true, {BT_VDD_IO_LDO, BT_VDD_IO_LDO_CURRENT}}, {NULL, "qcom,bt-vdd-aon", 950000, 950000, 0, false, true, {BT_VDD_AON_LDO, BT_VDD_AON_LDO_CURRENT}}, {NULL, "qcom,bt-vdd-rfacmn", 950000, 950000, 0, false, true, {BT_VDD_RFACMN, BT_VDD_RFACMN_CURRENT}}, /* BT_CX_MX */ {NULL, "qcom,bt-vdd-dig", 950000, 952000, 0, false, true}, {NULL, "qcom,bt-vdd-rfa-0p8", 950000, 952000, 0, false, true}, {NULL, "qcom,bt-vdd-rfa1", 1900000, 1900000, 0, false, true}, {NULL, "qcom,bt-vdd-rfa2", 1900000, 1900000, 0, false, true}, {NULL, "qcom,bt-vdd-asd", 2800000, 2800000, 0, false, true}, {NULL, "qcom,bt-vdd-dig", 950000, 952000, 0, false, true, {BT_VDD_DIG_LDO, BT_VDD_DIG_LDO_CURRENT}}, {NULL, "qcom,bt-vdd-rfa-0p8", 950000, 952000, 0, false, true, {BT_VDD_RFA_0p8, BT_VDD_RFA_0p8_CURRENT}}, {NULL, "qcom,bt-vdd-rfa1", 1900000, 1900000, 0, false, true, {BT_VDD_RFA1_LDO, BT_VDD_RFA1_LDO_CURRENT}}, {NULL, "qcom,bt-vdd-rfa2", 1900000, 1900000, 0, false, true, {BT_VDD_RFA2_LDO, BT_VDD_RFA2_LDO_CURRENT}}, {NULL, "qcom,bt-vdd-asd", 2800000, 2800000, 0, false, true, {BT_VDD_ASD_LDO, BT_VDD_ASD_LDO_CURRENT}}, }; // Regulator structure for WCN399x BT SoC series static struct bt_power bt_vreg_info_wcn399x = { .compatible = "qcom,wcn3990", .vregs = (struct bt_power_vreg_data []) { {NULL, "qcom,bt-vdd-io", 1700000, 1900000, 0, false, false}, {NULL, "qcom,bt-vdd-core", 1304000, 1304000, 0, false, false}, {NULL, "qcom,bt-vdd-pa", 3000000, 3312000, 0, false, false}, {NULL, "qcom,bt-vdd-xtal", 1700000, 1900000, 0, false, false}, {NULL, "qcom,bt-vdd-io", 1700000, 1900000, 0, false, false, {BT_VDD_IO_LDO, BT_VDD_IO_LDO_CURRENT}}, {NULL, "qcom,bt-vdd-core", 1304000, 1304000, 0, false, false, {BT_VDD_CORE_LDO, BT_VDD_CORE_LDO_CURRENT}}, {NULL, "qcom,bt-vdd-pa", 3000000, 3312000, 0, false, false, {BT_VDD_PA_LDO, BT_VDD_PA_LDO_CURRENT}}, {NULL, "qcom,bt-vdd-xtal", 1700000, 1900000, 0, false, false, {BT_VDD_XTAL_LDO, BT_VDD_XTAL_LDO_CURRENT}}, }, .num_vregs = 4, }; Loading @@ -77,7 +126,12 @@ static const struct of_device_id bt_power_match_table[] = { }; static int bt_power_vreg_set(enum bt_power_modes mode); static int btpower_get_tcs_table_info(struct platform_device *plat_dev, struct bluetooth_power_platform_data *bt_power_pdata); static int btpower_enable_ipa_vreg(struct platform_device *plat_dev, struct bluetooth_power_platform_data *bt_power_pdata); static int bt_power_src_status[BT_POWER_SRC_SIZE]; static struct bluetooth_power_platform_data *bt_power_pdata; static struct platform_device *btpdev; static bool previous; Loading Loading @@ -260,14 +314,18 @@ static int bt_configure_gpios(int on) pr_err("%s: Unable to set direction\n", __func__); return rc; } bt_power_src_status[BT_RESET_GPIO] = gpio_get_value(bt_reset_gpio); msleep(50); pr_debug("BTON:Turn Bt Off bt-reset-gpio(%d) value(%d)\n", pr_info("BTON:Turn Bt Off bt-reset-gpio(%d) value(%d)\n", bt_reset_gpio, gpio_get_value(bt_reset_gpio)); if (bt_sw_ctrl_gpio >= 0) { pr_debug("BTON:Turn Bt Off\n"); pr_debug("bt-sw-ctrl-gpio(%d) value(%d)\n", pr_info("BTON:Turn Bt Off\n"); bt_power_src_status[BT_SW_CTRL_GPIO] = gpio_get_value(bt_sw_ctrl_gpio); pr_info("bt-sw-ctrl-gpio(%d) value(%d)\n", bt_sw_ctrl_gpio, gpio_get_value(bt_sw_ctrl_gpio)); bt_power_src_status[BT_SW_CTRL_GPIO]); } rc = gpio_direction_output(bt_reset_gpio, 1); Loading @@ -275,6 +333,8 @@ static int bt_configure_gpios(int on) pr_err("%s: Unable to set direction\n", __func__); return rc; } bt_power_src_status[BT_RESET_GPIO] = gpio_get_value(bt_reset_gpio); msleep(50); /* Check if SW_CTRL is asserted */ if (bt_sw_ctrl_gpio >= 0) { Loading Loading @@ -302,24 +362,26 @@ static int bt_configure_gpios(int on) pr_info("BTON:Turn Bt On bt-reset-gpio(%d) value(%d)\n", bt_reset_gpio, gpio_get_value(bt_reset_gpio)); if (bt_sw_ctrl_gpio >= 0) { pr_debug("BTON:Turn Bt On\n"); pr_debug("bt-sw-ctrl-gpio(%d) value(%d)\n", pr_info("BTON:Turn Bt On\n"); bt_power_src_status[BT_SW_CTRL_GPIO] = gpio_get_value(bt_sw_ctrl_gpio); pr_info("bt-sw-ctrl-gpio(%d) value(%d)\n", bt_sw_ctrl_gpio, gpio_get_value(bt_sw_ctrl_gpio)); bt_power_src_status[BT_SW_CTRL_GPIO]); } } else { gpio_set_value(bt_reset_gpio, 0); msleep(100); pr_debug("BT-OFF:bt-reset-gpio(%d) value(%d)\n", pr_info("BT-OFF:bt-reset-gpio(%d) value(%d)\n", bt_reset_gpio, gpio_get_value(bt_reset_gpio)); if (bt_sw_ctrl_gpio >= 0) { pr_debug("BT-OFF:bt-sw-ctrl-gpio(%d) value(%d)\n", pr_info("BT-OFF:bt-sw-ctrl-gpio(%d) value(%d)\n", bt_sw_ctrl_gpio, gpio_get_value(bt_sw_ctrl_gpio)); } } pr_debug("%s: bt_gpio= %d on: %d\n", __func__, bt_reset_gpio, on); pr_info("%s: bt_gpio= %d on: %d\n", __func__, bt_reset_gpio, on); return rc; } Loading Loading @@ -349,6 +411,10 @@ static int bluetooth_power(int on) } } if (bt_power_pdata->bt_gpio_sys_rst > 0) { bt_power_src_status[BT_RESET_GPIO] = DEFAULT_INVALID_VALUE; bt_power_src_status[BT_SW_CTRL_GPIO] = DEFAULT_INVALID_VALUE; rc = bt_configure_gpios(on); if (rc < 0) { pr_err("%s: bt_power gpio config failed\n", Loading Loading @@ -580,16 +646,25 @@ static int bt_power_vreg_get(struct platform_device *pdev) static int bt_power_vreg_set(enum bt_power_modes mode) { int num_vregs, i = 0, ret = 0; int log_indx; struct bt_power_vreg_data *vreg_info = NULL; num_vregs = bt_power_pdata->num_vregs; if (mode == BT_POWER_ENABLE) { for (; i < num_vregs; i++) { vreg_info = &bt_power_pdata->vreg_info[i]; log_indx = vreg_info->indx.init; if (vreg_info->reg) { bt_power_src_status[log_indx] = DEFAULT_INVALID_VALUE; ret = bt_vreg_enable(vreg_info); if (ret < 0) goto out; if (vreg_info->is_enabled) { bt_power_src_status[log_indx] = regulator_get_voltage( vreg_info->reg); } } } } else if (mode == BT_POWER_DISABLE) { Loading Loading @@ -672,9 +747,17 @@ static int bt_power_populate_dt_pinfo(struct platform_device *pdev) static int bt_power_probe(struct platform_device *pdev) { int ret = 0; int itr; pr_debug("%s\n", __func__); /* Fill whole array with -2 i.e NOT_AVAILABLE state by default * for any GPIO or Reg handle. */ for (itr = PWR_SRC_INIT_STATE_IDX; itr < BT_POWER_SRC_SIZE; ++itr) bt_power_src_status[itr] = PWR_SRC_NOT_AVAILABLE; bt_power_pdata = kzalloc(sizeof(*bt_power_pdata), GFP_KERNEL); if (!bt_power_pdata) Loading Loading @@ -708,6 +791,8 @@ static int bt_power_probe(struct platform_device *pdev) goto free_pdata; btpdev = pdev; if (btpower_get_tcs_table_info(pdev, bt_power_pdata) < 0) pr_err("%s: Failed to get TCS table info\n", __func__); return 0; Loading Loading @@ -747,46 +832,50 @@ int btpower_get_chipset_version(void) } EXPORT_SYMBOL(btpower_get_chipset_version); static long get_resource_value(char *res_name) static void set_pwr_srcs_status(struct bt_power_vreg_data *handle) { int ret = 0; int i = 0; long value = -1; struct bt_power_vreg_data *vreg; int num_vregs = bt_power_pdata->num_vregs; for (; i < num_vregs; i++) { vreg = &bt_power_pdata->vreg_info[i]; if (!vreg->reg) continue; else { if (strnstr(vreg->name, res_name, strlen(vreg->name))) break; } } if (i < num_vregs) { if (regulator_is_enabled(vreg->reg)) { value = (int)regulator_get_voltage(vreg->reg); pr_debug("%s:%s value(%d)\n", __func__, res_name, value); ret = value; int ldo_index; if (handle) { ldo_index = handle->indx.crash; bt_power_src_status[ldo_index] = DEFAULT_INVALID_VALUE; if (handle->is_enabled && (regulator_is_enabled(handle->reg))) { bt_power_src_status[ldo_index] = (int)regulator_get_voltage(handle->reg); pr_err("%s(%d) value(%d)\n", handle->name, handle, bt_power_src_status[ldo_index]); } else { pr_err("%s:%s not configure/enabled\n", __func__, res_name); ret = -EINVAL; pr_err("%s:%s is_enabled: %d\n", __func__, handle->name, handle->is_enabled); } } } return ret; static void set_gpios_srcs_status(char *gpio_name, int gpio_index, int handle) { if (handle >= 0) { bt_power_src_status[gpio_index] = DEFAULT_INVALID_VALUE; bt_power_src_status[gpio_index] = gpio_get_value(handle); pr_err("%s(%d) value(%d)\n", gpio_name, handle, bt_power_src_status[gpio_index]); } else { pr_err("%s: %s not configured\n", __func__, gpio_name); } } static long bt_ioctl(struct file *file, unsigned int cmd, unsigned long arg) { int ret = 0, pwr_cntrl = 0; int chipset_version = 0; long value = -1; int itr, num_vregs; struct bt_power_vreg_data *vreg_info = NULL; switch (cmd) { case BT_CMD_SLIM_TEST: Loading Loading @@ -834,43 +923,53 @@ static long bt_ioctl(struct file *file, unsigned int cmd, unsigned long arg) ret = -EFAULT; } break; case BT_CMD_GETVAL_RESET_GPIO: if (bt_power_pdata->bt_gpio_sys_rst > 0) { value = (long)gpio_get_value( bt_power_pdata->bt_gpio_sys_rst); pr_debug("%s:GET_RESET_GPIO(%d) value(%d)\n", bt_power_pdata->bt_gpio_sys_rst, __func__, value); ret = value; } else { pr_err("%s: RESET_GPIO not configured\n", __func__); ret = -EINVAL; } break; case BT_CMD_GETVAL_SW_CTRL_GPIO: case BT_CMD_CHECK_SW_CTRL: /* Check if SW_CTRL is asserted */ pr_info("BT_CMD_CHECK_SW_CTRL\n"); if (bt_power_pdata->bt_gpio_sw_ctrl > 0) { value = (long)gpio_get_value( bt_power_src_status[BT_SW_CTRL_GPIO] = DEFAULT_INVALID_VALUE; ret = gpio_direction_input( bt_power_pdata->bt_gpio_sw_ctrl); pr_debug("%s:GET_SWCTRL_GPIO(%d) value(%d)\n", if (ret) { pr_err("%s:gpio_direction_input api\n", __func__); pr_err("%s:failed for SW_CTRL:%d\n", __func__, ret); } else { bt_power_src_status[BT_SW_CTRL_GPIO] = gpio_get_value( bt_power_pdata->bt_gpio_sw_ctrl); pr_info("bt-sw-ctrl-gpio(%d) value(%d)\n", bt_power_pdata->bt_gpio_sw_ctrl, __func__, value); ret = value; bt_power_src_status[BT_SW_CTRL_GPIO]); } } else { pr_err("%s:SW_CTRL_GPIO not configured\n", __func__); ret = -EINVAL; pr_err("bt_gpio_sw_ctrl not configured\n"); return -EINVAL; } break; case BT_CMD_GETVAL_VDD_AON_LDO: ret = get_resource_value("vdd-aon"); break; case BT_CMD_GETVAL_VDD_DIG_LDO: ret = get_resource_value("vdd-dig"); break; case BT_CMD_GETVAL_VDD_RFA1_LDO: ret = get_resource_value("vdd-rfa1"); case BT_CMD_GETVAL_POWER_SRCS: pr_err("BT_CMD_GETVAL_POWER_SRCS\n"); set_gpios_srcs_status("BT_RESET_GPIO", BT_RESET_GPIO_CURRENT, bt_power_pdata->bt_gpio_sys_rst); set_gpios_srcs_status("SW_CTRL_GPIO", BT_SW_CTRL_GPIO_CURRENT, bt_power_pdata->bt_gpio_sw_ctrl); num_vregs = bt_power_pdata->num_vregs; for (itr = 0; itr < num_vregs; itr++) { vreg_info = &bt_power_pdata->vreg_info[itr]; set_pwr_srcs_status(vreg_info); } if (copy_to_user((void __user *)arg, bt_power_src_status, sizeof(bt_power_src_status))) { pr_err("%s: copy to user failed\n", __func__); ret = -EFAULT; } break; case BT_CMD_GETVAL_VDD_RFA2_LDO: ret = get_resource_value("vdd-rfa2"); case BT_CMD_SET_IPA_TCS_INFO: pr_err("%s: BT_CMD_SET_IPA_TCS_INFO\n", __func__); btpower_enable_ipa_vreg(btpdev, bt_power_pdata); break; default: return -ENOIOCTLCMD; Loading Loading @@ -935,6 +1034,78 @@ static int __init bluetooth_power_init(void) return ret; } static int btpower_get_tcs_table_info(struct platform_device *dev, struct bluetooth_power_platform_data *bt_power_pdata) { struct platform_device *plat_dev = dev; struct btpower_tcs_table_info *tcs_table_info = &bt_power_pdata->tcs_table_info; struct resource *res; resource_size_t addr_len; void __iomem *tcs_cmd_base_addr; int ret = -1; res = platform_get_resource_byname(plat_dev, IORESOURCE_MEM, "tcs_cmd"); if (!res) { pr_err("No TCS CMD entry found in DTSI\n"); goto out; } tcs_table_info->tcs_cmd_base_addr = res->start; addr_len = resource_size(res); pr_info("TCS CMD base address is %pa with length %pa\n", &tcs_table_info->tcs_cmd_base_addr, &addr_len); tcs_cmd_base_addr = devm_ioremap_resource(&plat_dev->dev, res); if (IS_ERR(tcs_cmd_base_addr)) { ret = PTR_ERR(tcs_cmd_base_addr); pr_err("Failed to map TCS CMD address, err = %d\n", ret); goto out; } tcs_table_info->tcs_cmd_base_addr_io = tcs_cmd_base_addr; return 0; out: return ret; } static int btpower_enable_ipa_vreg(struct platform_device *dev, struct bluetooth_power_platform_data *bt_power_pdata) { struct platform_device *plat_dev = dev; struct btpower_tcs_table_info *tcs_table_info = &bt_power_pdata->tcs_table_info; u32 offset, addr_val, data_val; void __iomem *tcs_cmd; int ret = 0; if (!tcs_table_info->tcs_cmd_base_addr_io) { pr_err("TCS command not configured\n"); return -EINVAL; } ret = of_property_read_u32(plat_dev->dev.of_node, "qcom,tcs_offset_ipa", &offset); if (ret) { pr_err("iPA failed to configure\n"); return -EINVAL; } tcs_cmd = tcs_table_info->tcs_cmd_base_addr_io + offset; addr_val = readl_relaxed(tcs_cmd); tcs_cmd += TCS_CMD_IO_ADDR_OFFSET; writel_relaxed(1, tcs_cmd); data_val = readl_relaxed(tcs_cmd); pr_info("Configure S3E TCS Addr : %x with Data: %d\n" , addr_val, data_val); return 0; } static void __exit bluetooth_power_exit(void) { platform_driver_unregister(&bt_power_driver); Loading
include/linux/btpower.h +21 −7 Original line number Diff line number Diff line Loading @@ -18,6 +18,11 @@ enum bt_power_modes { BT_POWER_RETENTION }; struct log_index { int init; int crash; }; struct bt_power_vreg_data { struct regulator *reg; /* voltage regulator handle */ const char *name; /* regulator name */ Loading @@ -26,6 +31,7 @@ struct bt_power_vreg_data { u32 load_curr; /* current */ bool is_enabled; /* is this regulator enabled? */ bool is_retention_supp; /* does this regulator support retention mode */ struct log_index indx; /* Index for reg. w.r.t init & crash */ }; struct bt_power { Loading @@ -40,6 +46,10 @@ struct bt_power_clk_data { bool is_enabled; /* is this clock enabled? */ }; struct btpower_tcs_table_info { resource_size_t tcs_cmd_base_addr; void __iomem *tcs_cmd_base_addr_io; }; /* * Platform data for the bluetooth power driver. */ Loading @@ -53,6 +63,7 @@ struct bluetooth_power_platform_data { int (*bt_power_setup)(int id); /* Bluetooth power setup function */ char compatible[32]; /*Bluetooth SoC name */ int num_vregs; struct btpower_tcs_table_info tcs_table_info; }; int btpower_register_slimdev(struct device *dev); Loading @@ -62,10 +73,13 @@ int btpower_get_chipset_version(void); #define BT_CMD_PWR_CTRL 0xbfad #define BT_CMD_CHIPSET_VERS 0xbfae #define BT_CMD_GET_CHIPSET_ID 0xbfaf #define BT_CMD_GETVAL_RESET_GPIO 0xbfb5 #define BT_CMD_GETVAL_SW_CTRL_GPIO 0xbfb0 #define BT_CMD_GETVAL_VDD_AON_LDO 0xbfb1 #define BT_CMD_GETVAL_VDD_DIG_LDO 0xbfb2 #define BT_CMD_GETVAL_VDD_RFA1_LDO 0xbfb3 #define BT_CMD_GETVAL_VDD_RFA2_LDO 0xbfb4 #define BT_CMD_CHECK_SW_CTRL 0xbfb0 #define BT_CMD_GETVAL_POWER_SRCS 0xbfb1 #define BT_CMD_SET_IPA_TCS_INFO 0xbfc0 #define TCS_CMD_IO_ADDR_OFFSET 0x4 /* total number of power src */ #define BT_POWER_SRC_SIZE 28 #endif /* __LINUX_BLUETOOTH_POWER_H */