Loading Documentation/devicetree/bindings/soc/qcom/bg_rsb.txt 0 → 100644 +19 −0 Original line number Diff line number Diff line Qualcomm technologies, Inc. bg-rsb BG-RSB : bg-rsb is used to communicate with Blackghost over Glink to configure the RSB events. bg-rsb enable/disable LDO11 and LDO15 before making any communication to BG regarding RSB. It also provides an input device, which is used to send the RSB/Button events to input framework. Required properties: - compatible : should be "qcom,bg-rsb" - vdd-ldo1-supply : for powering main supply - vdd-ldo2-supply : for powering sensor Example: qcom,bg-rsb { compatible = "qcom,bg-rsb"; vdd-ldo1-supply = <&pm660_l11>; vdd-ldo2-supply = <&pm660_l15>; }; drivers/soc/qcom/Kconfig +9 −0 Original line number Diff line number Diff line Loading @@ -1031,6 +1031,15 @@ config MSM_BAM_DMUX communication between G-Link/bg_com_dev and BG processor over SPI. This handle the interrupts raised by BG and notify the G-link with interrupt event and event data. config MSM_BGRSB bool "Provide support for rsb events on Blackghost chipset" help BGRSB communicates to BG over rpmsg driver for RSB configuration and enable/disable on device power state change. It enables/disables the regulator specific to RSB. Sends the side band events generated by BG to input framework. config MSM_PIL_SSR_BG tristate "MSM Subsystem Blackghost(BG) Support" depends on MSM_PIL && MSM_SUBSYSTEM_RESTART Loading drivers/soc/qcom/Makefile +1 −0 Original line number Diff line number Diff line Loading @@ -71,6 +71,7 @@ obj-$(CONFIG_QCOM_SMCINVOKE) += smcinvoke.o obj-$(CONFIG_SDX_EXT_IPC) += sdx_ext_ipc.o obj-$(CONFIG_MSM_PIL_SSR_BG) += subsys-pil-bg.o obj-$(CONFIG_QTI_NOTIFY_SIDEBAND) += sideband_notify.o obj-$(CONFIG_MSM_BGRSB) += bg_rsb.o ifdef CONFIG_MSM_SUBSYSTEM_RESTART obj-y += subsystem_notif.o Loading drivers/soc/qcom/bg_rsb.c 0 → 100644 +891 −0 Original line number Diff line number Diff line /* Copyright (c) 2017-2020, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and * only version 2 as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * */ #define pr_fmt(msg) "bgrsb: %s: " msg, __func__ #include "bgrsb.h" struct bgrsb_priv { void *handle; struct input_dev *input; struct mutex glink_mutex; struct mutex rsb_state_mutex; enum bgrsb_state bgrsb_current_state; void *lhndl; struct work_struct bg_up_work; struct work_struct bg_down_work; struct work_struct rsb_up_work; struct work_struct rsb_down_work; struct work_struct rsb_calibration_work; struct work_struct bttn_configr_work; struct workqueue_struct *bgrsb_wq; struct bgrsb_regulator rgltr; enum ldo_task ldo_action; void *bgwear_subsys_handle; struct completion bg_resp_cmplt; struct completion wrk_cmplt; struct completion bg_lnikup_cmplt; struct completion tx_done; struct device *ldev; struct wakeup_source bgrsb_ws; wait_queue_head_t link_state_wait; uint32_t calbrtion_intrvl; uint32_t calbrtion_cpi; uint8_t bttn_configs; int msmrsb_gpio; bool rsb_rpmsg; bool rsb_use_msm_gpio; bool is_in_twm; bool calibration_needed; bool is_calibrd; bool is_cnfgrd; bool blk_rsb_cmnds; bool pending_enable; }; static void *bgrsb_drv; static int bgrsb_enable(struct bgrsb_priv *dev, bool enable); void bgrsb_send_input(struct event *evnt) { uint8_t press_code; uint8_t value; struct bgrsb_priv *dev = container_of(bgrsb_drv, struct bgrsb_priv, lhndl); pr_debug("%s: Called\n", __func__); if (!evnt) { pr_err("%s: No event received\n", __func__); return; } if (evnt->sub_id == 1) { input_report_rel(dev->input, REL_WHEEL, evnt->evnt_data); input_sync(dev->input); } else if (evnt->sub_id == 2) { press_code = (uint8_t) evnt->evnt_data; value = (uint8_t) (evnt->evnt_data >> 8); switch (press_code) { case 0x1: if (value == 0) { input_report_key(dev->input, KEY_VOLUMEDOWN, 1); input_sync(dev->input); } else { input_report_key(dev->input, KEY_VOLUMEDOWN, 0); input_sync(dev->input); } break; case 0x2: if (value == 0) { input_report_key(dev->input, KEY_VOLUMEUP, 1); input_sync(dev->input); } else { input_report_key(dev->input, KEY_VOLUMEUP, 0); input_sync(dev->input); } break; case 0x3: if (value == 0) { input_report_key(dev->input, KEY_POWER, 1); input_sync(dev->input); } else { input_report_key(dev->input, KEY_POWER, 0); input_sync(dev->input); } break; default: pr_info("event: type[%d] , data: %d\n", evnt->sub_id, evnt->evnt_data); } } pr_debug("%s: Ended\n", __func__); } EXPORT_SYMBOL(bgrsb_send_input); static int bgrsb_init_regulators(struct device *pdev) { struct regulator *reg11; struct regulator *reg15; struct bgrsb_priv *dev = dev_get_drvdata(pdev); reg11 = devm_regulator_get(pdev, "vdd-ldo1"); if (IS_ERR_OR_NULL(reg11)) { pr_err("Unable to get regulator for LDO-11\n"); return PTR_ERR(reg11); } reg15 = devm_regulator_get(pdev, "vdd-ldo2"); if (IS_ERR_OR_NULL(reg15)) { pr_err("Unable to get regulator for LDO-15\n"); return PTR_ERR(reg15); } dev->rgltr.regldo11 = reg11; dev->rgltr.regldo15 = reg15; return 0; } static int bgrsb_set_ldo(struct bgrsb_priv *dev, enum ldo_task ldo_action) { int ret = 0; bool value; switch (ldo_action) { case BGRSB_HW_TURN_ON: ret = regulator_set_voltage(dev->rgltr.regldo11, BGRSB_LDO11_VTG_MIN_UV, BGRSB_LDO11_VTG_MAX_UV); if (ret) { pr_err("Failed to request LDO-11 voltage %d\n", ret); goto err_ret; } ret = regulator_enable(dev->rgltr.regldo11); if (ret) { pr_err("Failed to enable LDO-11 %d\n", ret); goto err_ret; } break; case BGRSB_ENABLE_WHEEL_EVENTS: if (dev->rsb_use_msm_gpio == true) { if (!gpio_is_valid(dev->msmrsb_gpio)) { pr_err("gpio %d is not valid\n", dev->msmrsb_gpio); ret = -ENXIO; goto err_ret; } /* Sleep 50ms for h/w to detect signal */ msleep(50); gpio_set_value(dev->msmrsb_gpio, 1); value = gpio_get_value(dev->msmrsb_gpio); if (value == true) { pr_debug("gpio %d set properly\n", dev->msmrsb_gpio); } else { pr_debug("gpio %d set failed\n", dev->msmrsb_gpio); ret = -ENXIO; goto err_ret; } } else { ret = regulator_set_voltage(dev->rgltr.regldo15, BGRSB_LDO15_VTG_MIN_UV, BGRSB_LDO15_VTG_MAX_UV); if (ret) { pr_err("Request failed LDO-15 %d\n", ret); goto err_ret; } ret = regulator_enable(dev->rgltr.regldo15); if (ret) { pr_err("LDO-15 not enabled%d\n", ret); goto err_ret; } } break; case BGRSB_HW_TURN_OFF: ret = regulator_disable(dev->rgltr.regldo11); if (ret) { pr_err("Failed to disable LDO-11 %d\n", ret); goto err_ret; } break; case BGRSB_DISABLE_WHEEL_EVENTS: if (dev->rsb_use_msm_gpio == true) { if (!gpio_is_valid(dev->msmrsb_gpio)) { pr_err("Invalid gpio %d\n", dev->msmrsb_gpio); ret = -ENXIO; goto err_ret; } /* Sleep 50ms for h/w to detect signal */ msleep(50); gpio_set_value(dev->msmrsb_gpio, 0); } else { ret = regulator_disable(dev->rgltr.regldo15); if (ret) { pr_err("Failed to disable LDO-15 %d\n", ret); goto err_ret; } regulator_set_load(dev->rgltr.regldo15, 0); } break; default: ret = -EINVAL; } err_ret: return ret; } static void bgrsb_bgdown_work(struct work_struct *work) { int ret = 0; struct bgrsb_priv *dev = container_of(work, struct bgrsb_priv, bg_down_work); mutex_lock(&dev->rsb_state_mutex); if (dev->bgrsb_current_state == BGRSB_STATE_RSB_ENABLED) { ret = bgrsb_set_ldo(dev, BGRSB_DISABLE_WHEEL_EVENTS); if (ret == 0) dev->bgrsb_current_state = BGRSB_STATE_RSB_CONFIGURED; else pr_err("Failed to unvote LDO-15 on BG down\n"); } if (dev->bgrsb_current_state == BGRSB_STATE_RSB_CONFIGURED) { ret = bgrsb_set_ldo(dev, BGRSB_HW_TURN_OFF); if (ret == 0) dev->bgrsb_current_state = BGRSB_STATE_INIT; else pr_err("Failed to unvote LDO-11 on BG down\n"); } dev->is_cnfgrd = false; dev->blk_rsb_cmnds = false; pr_debug("RSB current state is : %d\n", dev->bgrsb_current_state); if (dev->bgrsb_current_state == BGRSB_STATE_INIT) { if (dev->is_calibrd) dev->calibration_needed = true; } mutex_unlock(&dev->rsb_state_mutex); } static int bgrsb_tx_msg(struct bgrsb_priv *dev, void *msg, size_t len) { int rc = 0; __pm_stay_awake(&dev->bgrsb_ws); mutex_lock(&dev->glink_mutex); if (!dev->rsb_rpmsg) { pr_err("bgrsb-rpmsg is not probed yet, waiting for it to be probed\n"); goto err_ret; } if (rc != 0) pr_err("bgrsb_rpmsg_tx_msg failed %d\n", rc); err_ret: mutex_unlock(&dev->glink_mutex); __pm_relax(&dev->bgrsb_ws); return rc; } static int bgrsb_enable(struct bgrsb_priv *dev, bool enable) { struct bgrsb_msg req = {0}; req.cmd_id = 0x02; req.data = enable ? 0x01 : 0x00; return bgrsb_tx_msg(dev, &req, BGRSB_MSG_SIZE); } static int bgrsb_configr_rsb(struct bgrsb_priv *dev, bool enable) { struct bgrsb_msg req = {0}; req.cmd_id = 0x01; req.data = enable ? 0x01 : 0x00; return bgrsb_tx_msg(dev, &req, BGRSB_MSG_SIZE); } void bgrsb_notify_glink_channel_state(bool state) { struct bgrsb_priv *dev = container_of(bgrsb_drv, struct bgrsb_priv, lhndl); pr_debug("%s: RSB-CTRL channel state: %d\n", __func__, state); dev->rsb_rpmsg = state; } EXPORT_SYMBOL(bgrsb_notify_glink_channel_state); static void bgrsb_bgup_work(struct work_struct *work) { int ret = 0; struct bgrsb_priv *dev = container_of(work, struct bgrsb_priv, bg_up_work); mutex_lock(&dev->rsb_state_mutex); ret = bgrsb_set_ldo(dev, BGRSB_HW_TURN_ON); if (ret == 0) { if (!dev->rsb_rpmsg) pr_err("bgrsb-rpmsg is not probed yet\n"); ret = wait_event_timeout(dev->link_state_wait, (dev->rsb_rpmsg == true), msecs_to_jiffies(TIMEOUT_MS)); if (ret == 0) { pr_err("channel connection time out %d\n", ret); goto unlock; } pr_debug("bgrsb-rpmsg is probed\n"); ret = bgrsb_configr_rsb(dev, true); if (ret != 0) { pr_err("BG failed to configure RSB %d\n", ret); if (bgrsb_set_ldo(dev, BGRSB_HW_TURN_OFF) == 0) dev->bgrsb_current_state = BGRSB_STATE_INIT; goto unlock; } dev->is_cnfgrd = true; dev->bgrsb_current_state = BGRSB_STATE_RSB_CONFIGURED; pr_debug("RSB Cofigured\n"); if (dev->pending_enable) queue_work(dev->bgrsb_wq, &dev->rsb_up_work); } unlock: mutex_unlock(&dev->rsb_state_mutex); } /** * ssr_bg_cb(): callback function is called. * @arg1: a notifier_block. * @arg2: opcode that defines the event. * @arg3: void pointer. * * by ssr framework when BG goes down, up and during * ramdump collection. It handles BG shutdown and * power up events. * * Return: NOTIFY_DONE. */ static int ssr_bgrsb_cb(struct notifier_block *this, unsigned long opcode, void *data) { struct bgrsb_priv *dev = container_of(bgrsb_drv, struct bgrsb_priv, lhndl); switch (opcode) { case SUBSYS_BEFORE_SHUTDOWN: if (dev->bgrsb_current_state == BGRSB_STATE_RSB_ENABLED) dev->pending_enable = true; queue_work(dev->bgrsb_wq, &dev->bg_down_work); break; case SUBSYS_AFTER_POWERUP: if (dev->bgrsb_current_state == BGRSB_STATE_INIT) queue_work(dev->bgrsb_wq, &dev->bg_up_work); break; } return NOTIFY_DONE; } static struct notifier_block ssr_bg_nb = { .notifier_call = ssr_bgrsb_cb, .priority = 0, }; /** * bgrsb_ssr_register(): callback function is called. * @arg1: pointer to bgrsb_priv structure. * * ssr_register checks that domain id should be in range * and register SSR framework for value at domain id. * * Return: 0 for success and -ENODEV otherwise. */ static int bgrsb_ssr_register(struct bgrsb_priv *dev) { struct notifier_block *nb; if (!dev) return -ENODEV; nb = &ssr_bg_nb; dev->bgwear_subsys_handle = subsys_notif_register_notifier(BGRSB_BGWEAR_SUBSYS, nb); if (!dev->bgwear_subsys_handle) { dev->bgwear_subsys_handle = NULL; return -ENODEV; } return 0; } static void bgrsb_enable_rsb(struct work_struct *work) { int rc = 0; struct bgrsb_priv *dev = container_of(work, struct bgrsb_priv, rsb_up_work); mutex_lock(&dev->rsb_state_mutex); if (dev->bgrsb_current_state == BGRSB_STATE_RSB_ENABLED) { pr_debug("RSB is already enabled\n"); goto unlock; } if (dev->bgrsb_current_state != BGRSB_STATE_RSB_CONFIGURED) { pr_err("BG is not yet configured for RSB\n"); dev->pending_enable = true; goto unlock; } rc = bgrsb_set_ldo(dev, BGRSB_ENABLE_WHEEL_EVENTS); if (rc == 0) { rc = bgrsb_enable(dev, true); if (rc != 0) { pr_err("Failed to send enable command to BG %d\n", rc); bgrsb_set_ldo(dev, BGRSB_DISABLE_WHEEL_EVENTS); dev->bgrsb_current_state = BGRSB_STATE_RSB_CONFIGURED; goto unlock; } } dev->bgrsb_current_state = BGRSB_STATE_RSB_ENABLED; dev->pending_enable = false; pr_debug("RSB Enabled\n"); if (dev->calibration_needed) { dev->calibration_needed = false; queue_work(dev->bgrsb_wq, &dev->rsb_calibration_work); } unlock: mutex_unlock(&dev->rsb_state_mutex); } static void bgrsb_disable_rsb(struct work_struct *work) { int rc = 0; struct bgrsb_priv *dev = container_of(work, struct bgrsb_priv, rsb_down_work); mutex_lock(&dev->rsb_state_mutex); dev->pending_enable = false; if (dev->bgrsb_current_state == BGRSB_STATE_RSB_ENABLED) { rc = bgrsb_enable(dev, false); if (rc != 0) { pr_err("Failed to send disable command to BG\n"); goto unlock; } rc = bgrsb_set_ldo(dev, BGRSB_DISABLE_WHEEL_EVENTS); if (rc != 0) goto unlock; dev->bgrsb_current_state = BGRSB_STATE_RSB_CONFIGURED; pr_debug("RSB Disabled\n"); } unlock: mutex_unlock(&dev->rsb_state_mutex); } static void bgrsb_calibration(struct work_struct *work) { int rc = 0; struct bgrsb_msg req = {0}; struct bgrsb_priv *dev = container_of(work, struct bgrsb_priv, rsb_calibration_work); mutex_lock(&dev->rsb_state_mutex); if (!dev->is_cnfgrd) { pr_err("RSB is not configured\n"); goto unlock; } req.cmd_id = 0x03; req.data = dev->calbrtion_cpi; rc = bgrsb_tx_msg(dev, &req, 5); if (rc != 0) { pr_err("Failed to send resolution value to BG %d\n", rc); goto unlock; } req.cmd_id = 0x04; req.data = dev->calbrtion_intrvl; rc = bgrsb_tx_msg(dev, &req, 5); if (rc != 0) { pr_err("Failed to send interval value to BG %d\n", rc); goto unlock; } dev->is_calibrd = true; pr_debug("RSB Calibrated\n"); unlock: mutex_unlock(&dev->rsb_state_mutex); } static void bgrsb_buttn_configration(struct work_struct *work) { int rc = 0; struct bgrsb_msg req = {0}; struct bgrsb_priv *dev = container_of(work, struct bgrsb_priv, bttn_configr_work); mutex_lock(&dev->rsb_state_mutex); if (!dev->is_cnfgrd) { pr_err("RSB is not configured\n"); goto unlock; } req.cmd_id = 0x05; req.data = dev->bttn_configs; rc = bgrsb_tx_msg(dev, &req, 5); if (rc != 0) { pr_err("configuration cmnd failed %d\n", rc); goto unlock; } dev->bttn_configs = 0; pr_debug("RSB Button configured\n"); unlock: mutex_unlock(&dev->rsb_state_mutex); } static int bgrsb_handle_cmd_in_ssr(struct bgrsb_priv *dev, char *str) { long val; int ret; char *tmp; tmp = strsep(&str, ":"); if (!tmp) return -EINVAL; ret = kstrtol(tmp, 10, &val); if (ret < 0) return ret; if (val == BGRSB_POWER_ENABLE) dev->pending_enable = true; else if (val == BGRSB_POWER_DISABLE) dev->pending_enable = false; return 0; } static int split_bg_work(struct bgrsb_priv *dev, char *str) { long val; int ret; char *tmp; tmp = strsep(&str, ":"); if (!tmp) return -EINVAL; ret = kstrtol(tmp, 10, &val); if (ret < 0) return ret; switch (val) { case BGRSB_POWER_DISABLE: queue_work(dev->bgrsb_wq, &dev->rsb_down_work); break; case BGRSB_POWER_ENABLE: queue_work(dev->bgrsb_wq, &dev->rsb_up_work); break; case BGRSB_POWER_CALIBRATION: tmp = strsep(&str, ":"); if (!tmp) return -EINVAL; ret = kstrtol(tmp, 10, &val); if (ret < 0) return ret; dev->calbrtion_intrvl = (uint32_t)val; tmp = strsep(&str, ":"); if (!tmp) return -EINVAL; ret = kstrtol(tmp, 10, &val); if (ret < 0) return ret; dev->calbrtion_cpi = (uint32_t)val; queue_work(dev->bgrsb_wq, &dev->rsb_calibration_work); break; case BGRSB_BTTN_CONFIGURE: tmp = strsep(&str, ":"); if (!tmp) return -EINVAL; ret = kstrtol(tmp, 10, &val); if (ret < 0) return ret; dev->bttn_configs = (uint8_t)val; queue_work(dev->bgrsb_wq, &dev->bttn_configr_work); break; case BGRSB_IN_TWM: dev->is_in_twm = true; case BGRSB_GLINK_POWER_DISABLE: break; case BGRSB_OUT_TWM: dev->is_in_twm = false; case BGRSB_GLINK_POWER_ENABLE: break; } return 0; } static int store_enable(struct device *pdev, struct device_attribute *attr, const char *buff, size_t count) { int rc; struct bgrsb_priv *dev = dev_get_drvdata(pdev); char *arr; if (dev->blk_rsb_cmnds) { pr_err("Device is in TWM state\n"); return count; } arr = kstrdup(buff, GFP_KERNEL); if (!arr) return -ENOMEM; rc = split_bg_work(dev, arr); if (!dev->is_cnfgrd) { bgrsb_handle_cmd_in_ssr(dev, arr); kfree(arr); return -ENOMEDIUM; } if (rc != 0) pr_err("Not able to process request\n"); kfree(arr); return count; } static int show_enable(struct device *dev, struct device_attribute *attr, char *buff) { return 0; } static struct device_attribute dev_attr_rsb = { .attr = { .name = "enable", .mode = 00660, }, .show = show_enable, .store = store_enable, }; static int bgrsb_init(struct bgrsb_priv *dev) { bgrsb_drv = &dev->lhndl; mutex_init(&dev->glink_mutex); mutex_init(&dev->rsb_state_mutex); dev->ldo_action = BGRSB_NO_ACTION; dev->bgrsb_wq = create_singlethread_workqueue("bg-work-queue"); if (!dev->bgrsb_wq) { pr_err("Failed to init BG-RSB work-queue\n"); return -ENOMEM; } init_waitqueue_head(&dev->link_state_wait); /* set default bgrsb state */ dev->bgrsb_current_state = BGRSB_STATE_INIT; /* Init all works */ INIT_WORK(&dev->bg_up_work, bgrsb_bgup_work); INIT_WORK(&dev->bg_down_work, bgrsb_bgdown_work); INIT_WORK(&dev->rsb_up_work, bgrsb_enable_rsb); INIT_WORK(&dev->rsb_down_work, bgrsb_disable_rsb); INIT_WORK(&dev->rsb_calibration_work, bgrsb_calibration); INIT_WORK(&dev->bttn_configr_work, bgrsb_buttn_configration); return 0; } static int bg_rsb_probe(struct platform_device *pdev) { struct bgrsb_priv *dev; struct input_dev *input; struct device_node *node; int rc; unsigned int rsb_gpio; node = pdev->dev.of_node; dev = devm_kzalloc(&pdev->dev, sizeof(*dev), GFP_KERNEL); if (!dev) return -ENOMEM; /* Add wake lock for PM suspend */ wakeup_source_init(&dev->bgrsb_ws, "BGRSB_wake_lock"); dev->bgrsb_current_state = BGRSB_STATE_UNKNOWN; rc = bgrsb_init(dev); if (rc) goto err_ret_dev; /* Set up input device */ input = devm_input_allocate_device(&pdev->dev); if (!input) goto err_ret_dev; input_set_capability(input, EV_REL, REL_WHEEL); input_set_capability(input, EV_KEY, KEY_VOLUMEUP); input_set_capability(input, EV_KEY, KEY_VOLUMEDOWN); input->name = "bg-spi"; rc = input_register_device(input); if (rc) { pr_err("Input device registration failed\n"); goto err_ret_inp; } dev->input = input; /* register device for bg-wear ssr */ rc = bgrsb_ssr_register(dev); if (rc) { pr_err("Failed to register for bg ssr\n"); goto err_ret_inp; } rc = device_create_file(&pdev->dev, &dev_attr_rsb); if (rc) { pr_err("Not able to create the file bg-rsb/enable\n"); goto err_ret_inp; } dev->rsb_use_msm_gpio = of_property_read_bool(node, "qcom,rsb-use-msm-gpio"); if (dev->rsb_use_msm_gpio == true) { rsb_gpio = of_get_named_gpio(node, "qcom,bg-rsb-gpio", 0); pr_debug("gpio %d is configured\n", rsb_gpio); if (!gpio_is_valid(rsb_gpio)) { pr_err("gpio %d found is not valid\n", rsb_gpio); goto err_ret; } if (gpio_request(rsb_gpio, "msm_rsb_gpio")) { pr_err("gpio %d request failed\n", rsb_gpio); goto err_ret; } if (gpio_direction_output(rsb_gpio, 1)) { pr_err("gpio %d direction not set\n", rsb_gpio); goto err_ret; } pr_debug("rsb gpio successfully requested\n"); dev->msmrsb_gpio = rsb_gpio; } dev_set_drvdata(&pdev->dev, dev); rc = bgrsb_init_regulators(&pdev->dev); if (rc) { pr_err("Failed to set regulators\n"); goto err_ret_inp; } pr_debug("RSB probe successfully\n"); return 0; err_ret: return 0; err_ret_inp: input_free_device(input); err_ret_dev: devm_kfree(&pdev->dev, dev); return -ENODEV; } static int bg_rsb_remove(struct platform_device *pdev) { struct bgrsb_priv *dev = platform_get_drvdata(pdev); destroy_workqueue(dev->bgrsb_wq); input_free_device(dev->input); wakeup_source_trash(&dev->bgrsb_ws); return 0; } static int bg_rsb_resume(struct device *pldev) { struct platform_device *pdev = to_platform_device(pldev); struct bgrsb_priv *dev = platform_get_drvdata(pdev); mutex_lock(&dev->rsb_state_mutex); if (dev->bgrsb_current_state == BGRSB_STATE_RSB_CONFIGURED) goto ret_success; if (dev->bgrsb_current_state == BGRSB_STATE_INIT) { if (dev->is_cnfgrd && bgrsb_set_ldo(dev, BGRSB_HW_TURN_ON) == 0) { dev->bgrsb_current_state = BGRSB_STATE_RSB_CONFIGURED; pr_debug("RSB Cofigured\n"); goto ret_success; } pr_err("RSB failed to resume\n"); } mutex_unlock(&dev->rsb_state_mutex); return -EINVAL; ret_success: mutex_unlock(&dev->rsb_state_mutex); return 0; } static int bg_rsb_suspend(struct device *pldev) { struct platform_device *pdev = to_platform_device(pldev); struct bgrsb_priv *dev = platform_get_drvdata(pdev); mutex_lock(&dev->rsb_state_mutex); if (dev->bgrsb_current_state == BGRSB_STATE_INIT) goto ret_success; if (dev->bgrsb_current_state == BGRSB_STATE_RSB_ENABLED) { if (bgrsb_set_ldo(dev, BGRSB_DISABLE_WHEEL_EVENTS) != 0) goto ret_err; } if (bgrsb_set_ldo(dev, BGRSB_HW_TURN_OFF) == 0) { dev->bgrsb_current_state = BGRSB_STATE_INIT; pr_debug("RSB Init\n"); goto ret_success; } ret_err: pr_err("RSB failed to suspend\n"); mutex_unlock(&dev->rsb_state_mutex); return -EINVAL; ret_success: mutex_unlock(&dev->rsb_state_mutex); return 0; } static const struct of_device_id bg_rsb_of_match[] = { { .compatible = "qcom,bg-rsb", }, { } }; static const struct dev_pm_ops pm_rsb = { .resume = bg_rsb_resume, .suspend = bg_rsb_suspend, }; static struct platform_driver bg_rsb_driver = { .driver = { .name = "bg-rsb", .of_match_table = bg_rsb_of_match, .pm = &pm_rsb, }, .probe = bg_rsb_probe, .remove = bg_rsb_remove, }; module_platform_driver(bg_rsb_driver); MODULE_DESCRIPTION("SoC BG RSB driver"); MODULE_LICENSE("GPL v2"); Loading
Documentation/devicetree/bindings/soc/qcom/bg_rsb.txt 0 → 100644 +19 −0 Original line number Diff line number Diff line Qualcomm technologies, Inc. bg-rsb BG-RSB : bg-rsb is used to communicate with Blackghost over Glink to configure the RSB events. bg-rsb enable/disable LDO11 and LDO15 before making any communication to BG regarding RSB. It also provides an input device, which is used to send the RSB/Button events to input framework. Required properties: - compatible : should be "qcom,bg-rsb" - vdd-ldo1-supply : for powering main supply - vdd-ldo2-supply : for powering sensor Example: qcom,bg-rsb { compatible = "qcom,bg-rsb"; vdd-ldo1-supply = <&pm660_l11>; vdd-ldo2-supply = <&pm660_l15>; };
drivers/soc/qcom/Kconfig +9 −0 Original line number Diff line number Diff line Loading @@ -1031,6 +1031,15 @@ config MSM_BAM_DMUX communication between G-Link/bg_com_dev and BG processor over SPI. This handle the interrupts raised by BG and notify the G-link with interrupt event and event data. config MSM_BGRSB bool "Provide support for rsb events on Blackghost chipset" help BGRSB communicates to BG over rpmsg driver for RSB configuration and enable/disable on device power state change. It enables/disables the regulator specific to RSB. Sends the side band events generated by BG to input framework. config MSM_PIL_SSR_BG tristate "MSM Subsystem Blackghost(BG) Support" depends on MSM_PIL && MSM_SUBSYSTEM_RESTART Loading
drivers/soc/qcom/Makefile +1 −0 Original line number Diff line number Diff line Loading @@ -71,6 +71,7 @@ obj-$(CONFIG_QCOM_SMCINVOKE) += smcinvoke.o obj-$(CONFIG_SDX_EXT_IPC) += sdx_ext_ipc.o obj-$(CONFIG_MSM_PIL_SSR_BG) += subsys-pil-bg.o obj-$(CONFIG_QTI_NOTIFY_SIDEBAND) += sideband_notify.o obj-$(CONFIG_MSM_BGRSB) += bg_rsb.o ifdef CONFIG_MSM_SUBSYSTEM_RESTART obj-y += subsystem_notif.o Loading
drivers/soc/qcom/bg_rsb.c 0 → 100644 +891 −0 Original line number Diff line number Diff line /* Copyright (c) 2017-2020, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and * only version 2 as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * */ #define pr_fmt(msg) "bgrsb: %s: " msg, __func__ #include "bgrsb.h" struct bgrsb_priv { void *handle; struct input_dev *input; struct mutex glink_mutex; struct mutex rsb_state_mutex; enum bgrsb_state bgrsb_current_state; void *lhndl; struct work_struct bg_up_work; struct work_struct bg_down_work; struct work_struct rsb_up_work; struct work_struct rsb_down_work; struct work_struct rsb_calibration_work; struct work_struct bttn_configr_work; struct workqueue_struct *bgrsb_wq; struct bgrsb_regulator rgltr; enum ldo_task ldo_action; void *bgwear_subsys_handle; struct completion bg_resp_cmplt; struct completion wrk_cmplt; struct completion bg_lnikup_cmplt; struct completion tx_done; struct device *ldev; struct wakeup_source bgrsb_ws; wait_queue_head_t link_state_wait; uint32_t calbrtion_intrvl; uint32_t calbrtion_cpi; uint8_t bttn_configs; int msmrsb_gpio; bool rsb_rpmsg; bool rsb_use_msm_gpio; bool is_in_twm; bool calibration_needed; bool is_calibrd; bool is_cnfgrd; bool blk_rsb_cmnds; bool pending_enable; }; static void *bgrsb_drv; static int bgrsb_enable(struct bgrsb_priv *dev, bool enable); void bgrsb_send_input(struct event *evnt) { uint8_t press_code; uint8_t value; struct bgrsb_priv *dev = container_of(bgrsb_drv, struct bgrsb_priv, lhndl); pr_debug("%s: Called\n", __func__); if (!evnt) { pr_err("%s: No event received\n", __func__); return; } if (evnt->sub_id == 1) { input_report_rel(dev->input, REL_WHEEL, evnt->evnt_data); input_sync(dev->input); } else if (evnt->sub_id == 2) { press_code = (uint8_t) evnt->evnt_data; value = (uint8_t) (evnt->evnt_data >> 8); switch (press_code) { case 0x1: if (value == 0) { input_report_key(dev->input, KEY_VOLUMEDOWN, 1); input_sync(dev->input); } else { input_report_key(dev->input, KEY_VOLUMEDOWN, 0); input_sync(dev->input); } break; case 0x2: if (value == 0) { input_report_key(dev->input, KEY_VOLUMEUP, 1); input_sync(dev->input); } else { input_report_key(dev->input, KEY_VOLUMEUP, 0); input_sync(dev->input); } break; case 0x3: if (value == 0) { input_report_key(dev->input, KEY_POWER, 1); input_sync(dev->input); } else { input_report_key(dev->input, KEY_POWER, 0); input_sync(dev->input); } break; default: pr_info("event: type[%d] , data: %d\n", evnt->sub_id, evnt->evnt_data); } } pr_debug("%s: Ended\n", __func__); } EXPORT_SYMBOL(bgrsb_send_input); static int bgrsb_init_regulators(struct device *pdev) { struct regulator *reg11; struct regulator *reg15; struct bgrsb_priv *dev = dev_get_drvdata(pdev); reg11 = devm_regulator_get(pdev, "vdd-ldo1"); if (IS_ERR_OR_NULL(reg11)) { pr_err("Unable to get regulator for LDO-11\n"); return PTR_ERR(reg11); } reg15 = devm_regulator_get(pdev, "vdd-ldo2"); if (IS_ERR_OR_NULL(reg15)) { pr_err("Unable to get regulator for LDO-15\n"); return PTR_ERR(reg15); } dev->rgltr.regldo11 = reg11; dev->rgltr.regldo15 = reg15; return 0; } static int bgrsb_set_ldo(struct bgrsb_priv *dev, enum ldo_task ldo_action) { int ret = 0; bool value; switch (ldo_action) { case BGRSB_HW_TURN_ON: ret = regulator_set_voltage(dev->rgltr.regldo11, BGRSB_LDO11_VTG_MIN_UV, BGRSB_LDO11_VTG_MAX_UV); if (ret) { pr_err("Failed to request LDO-11 voltage %d\n", ret); goto err_ret; } ret = regulator_enable(dev->rgltr.regldo11); if (ret) { pr_err("Failed to enable LDO-11 %d\n", ret); goto err_ret; } break; case BGRSB_ENABLE_WHEEL_EVENTS: if (dev->rsb_use_msm_gpio == true) { if (!gpio_is_valid(dev->msmrsb_gpio)) { pr_err("gpio %d is not valid\n", dev->msmrsb_gpio); ret = -ENXIO; goto err_ret; } /* Sleep 50ms for h/w to detect signal */ msleep(50); gpio_set_value(dev->msmrsb_gpio, 1); value = gpio_get_value(dev->msmrsb_gpio); if (value == true) { pr_debug("gpio %d set properly\n", dev->msmrsb_gpio); } else { pr_debug("gpio %d set failed\n", dev->msmrsb_gpio); ret = -ENXIO; goto err_ret; } } else { ret = regulator_set_voltage(dev->rgltr.regldo15, BGRSB_LDO15_VTG_MIN_UV, BGRSB_LDO15_VTG_MAX_UV); if (ret) { pr_err("Request failed LDO-15 %d\n", ret); goto err_ret; } ret = regulator_enable(dev->rgltr.regldo15); if (ret) { pr_err("LDO-15 not enabled%d\n", ret); goto err_ret; } } break; case BGRSB_HW_TURN_OFF: ret = regulator_disable(dev->rgltr.regldo11); if (ret) { pr_err("Failed to disable LDO-11 %d\n", ret); goto err_ret; } break; case BGRSB_DISABLE_WHEEL_EVENTS: if (dev->rsb_use_msm_gpio == true) { if (!gpio_is_valid(dev->msmrsb_gpio)) { pr_err("Invalid gpio %d\n", dev->msmrsb_gpio); ret = -ENXIO; goto err_ret; } /* Sleep 50ms for h/w to detect signal */ msleep(50); gpio_set_value(dev->msmrsb_gpio, 0); } else { ret = regulator_disable(dev->rgltr.regldo15); if (ret) { pr_err("Failed to disable LDO-15 %d\n", ret); goto err_ret; } regulator_set_load(dev->rgltr.regldo15, 0); } break; default: ret = -EINVAL; } err_ret: return ret; } static void bgrsb_bgdown_work(struct work_struct *work) { int ret = 0; struct bgrsb_priv *dev = container_of(work, struct bgrsb_priv, bg_down_work); mutex_lock(&dev->rsb_state_mutex); if (dev->bgrsb_current_state == BGRSB_STATE_RSB_ENABLED) { ret = bgrsb_set_ldo(dev, BGRSB_DISABLE_WHEEL_EVENTS); if (ret == 0) dev->bgrsb_current_state = BGRSB_STATE_RSB_CONFIGURED; else pr_err("Failed to unvote LDO-15 on BG down\n"); } if (dev->bgrsb_current_state == BGRSB_STATE_RSB_CONFIGURED) { ret = bgrsb_set_ldo(dev, BGRSB_HW_TURN_OFF); if (ret == 0) dev->bgrsb_current_state = BGRSB_STATE_INIT; else pr_err("Failed to unvote LDO-11 on BG down\n"); } dev->is_cnfgrd = false; dev->blk_rsb_cmnds = false; pr_debug("RSB current state is : %d\n", dev->bgrsb_current_state); if (dev->bgrsb_current_state == BGRSB_STATE_INIT) { if (dev->is_calibrd) dev->calibration_needed = true; } mutex_unlock(&dev->rsb_state_mutex); } static int bgrsb_tx_msg(struct bgrsb_priv *dev, void *msg, size_t len) { int rc = 0; __pm_stay_awake(&dev->bgrsb_ws); mutex_lock(&dev->glink_mutex); if (!dev->rsb_rpmsg) { pr_err("bgrsb-rpmsg is not probed yet, waiting for it to be probed\n"); goto err_ret; } if (rc != 0) pr_err("bgrsb_rpmsg_tx_msg failed %d\n", rc); err_ret: mutex_unlock(&dev->glink_mutex); __pm_relax(&dev->bgrsb_ws); return rc; } static int bgrsb_enable(struct bgrsb_priv *dev, bool enable) { struct bgrsb_msg req = {0}; req.cmd_id = 0x02; req.data = enable ? 0x01 : 0x00; return bgrsb_tx_msg(dev, &req, BGRSB_MSG_SIZE); } static int bgrsb_configr_rsb(struct bgrsb_priv *dev, bool enable) { struct bgrsb_msg req = {0}; req.cmd_id = 0x01; req.data = enable ? 0x01 : 0x00; return bgrsb_tx_msg(dev, &req, BGRSB_MSG_SIZE); } void bgrsb_notify_glink_channel_state(bool state) { struct bgrsb_priv *dev = container_of(bgrsb_drv, struct bgrsb_priv, lhndl); pr_debug("%s: RSB-CTRL channel state: %d\n", __func__, state); dev->rsb_rpmsg = state; } EXPORT_SYMBOL(bgrsb_notify_glink_channel_state); static void bgrsb_bgup_work(struct work_struct *work) { int ret = 0; struct bgrsb_priv *dev = container_of(work, struct bgrsb_priv, bg_up_work); mutex_lock(&dev->rsb_state_mutex); ret = bgrsb_set_ldo(dev, BGRSB_HW_TURN_ON); if (ret == 0) { if (!dev->rsb_rpmsg) pr_err("bgrsb-rpmsg is not probed yet\n"); ret = wait_event_timeout(dev->link_state_wait, (dev->rsb_rpmsg == true), msecs_to_jiffies(TIMEOUT_MS)); if (ret == 0) { pr_err("channel connection time out %d\n", ret); goto unlock; } pr_debug("bgrsb-rpmsg is probed\n"); ret = bgrsb_configr_rsb(dev, true); if (ret != 0) { pr_err("BG failed to configure RSB %d\n", ret); if (bgrsb_set_ldo(dev, BGRSB_HW_TURN_OFF) == 0) dev->bgrsb_current_state = BGRSB_STATE_INIT; goto unlock; } dev->is_cnfgrd = true; dev->bgrsb_current_state = BGRSB_STATE_RSB_CONFIGURED; pr_debug("RSB Cofigured\n"); if (dev->pending_enable) queue_work(dev->bgrsb_wq, &dev->rsb_up_work); } unlock: mutex_unlock(&dev->rsb_state_mutex); } /** * ssr_bg_cb(): callback function is called. * @arg1: a notifier_block. * @arg2: opcode that defines the event. * @arg3: void pointer. * * by ssr framework when BG goes down, up and during * ramdump collection. It handles BG shutdown and * power up events. * * Return: NOTIFY_DONE. */ static int ssr_bgrsb_cb(struct notifier_block *this, unsigned long opcode, void *data) { struct bgrsb_priv *dev = container_of(bgrsb_drv, struct bgrsb_priv, lhndl); switch (opcode) { case SUBSYS_BEFORE_SHUTDOWN: if (dev->bgrsb_current_state == BGRSB_STATE_RSB_ENABLED) dev->pending_enable = true; queue_work(dev->bgrsb_wq, &dev->bg_down_work); break; case SUBSYS_AFTER_POWERUP: if (dev->bgrsb_current_state == BGRSB_STATE_INIT) queue_work(dev->bgrsb_wq, &dev->bg_up_work); break; } return NOTIFY_DONE; } static struct notifier_block ssr_bg_nb = { .notifier_call = ssr_bgrsb_cb, .priority = 0, }; /** * bgrsb_ssr_register(): callback function is called. * @arg1: pointer to bgrsb_priv structure. * * ssr_register checks that domain id should be in range * and register SSR framework for value at domain id. * * Return: 0 for success and -ENODEV otherwise. */ static int bgrsb_ssr_register(struct bgrsb_priv *dev) { struct notifier_block *nb; if (!dev) return -ENODEV; nb = &ssr_bg_nb; dev->bgwear_subsys_handle = subsys_notif_register_notifier(BGRSB_BGWEAR_SUBSYS, nb); if (!dev->bgwear_subsys_handle) { dev->bgwear_subsys_handle = NULL; return -ENODEV; } return 0; } static void bgrsb_enable_rsb(struct work_struct *work) { int rc = 0; struct bgrsb_priv *dev = container_of(work, struct bgrsb_priv, rsb_up_work); mutex_lock(&dev->rsb_state_mutex); if (dev->bgrsb_current_state == BGRSB_STATE_RSB_ENABLED) { pr_debug("RSB is already enabled\n"); goto unlock; } if (dev->bgrsb_current_state != BGRSB_STATE_RSB_CONFIGURED) { pr_err("BG is not yet configured for RSB\n"); dev->pending_enable = true; goto unlock; } rc = bgrsb_set_ldo(dev, BGRSB_ENABLE_WHEEL_EVENTS); if (rc == 0) { rc = bgrsb_enable(dev, true); if (rc != 0) { pr_err("Failed to send enable command to BG %d\n", rc); bgrsb_set_ldo(dev, BGRSB_DISABLE_WHEEL_EVENTS); dev->bgrsb_current_state = BGRSB_STATE_RSB_CONFIGURED; goto unlock; } } dev->bgrsb_current_state = BGRSB_STATE_RSB_ENABLED; dev->pending_enable = false; pr_debug("RSB Enabled\n"); if (dev->calibration_needed) { dev->calibration_needed = false; queue_work(dev->bgrsb_wq, &dev->rsb_calibration_work); } unlock: mutex_unlock(&dev->rsb_state_mutex); } static void bgrsb_disable_rsb(struct work_struct *work) { int rc = 0; struct bgrsb_priv *dev = container_of(work, struct bgrsb_priv, rsb_down_work); mutex_lock(&dev->rsb_state_mutex); dev->pending_enable = false; if (dev->bgrsb_current_state == BGRSB_STATE_RSB_ENABLED) { rc = bgrsb_enable(dev, false); if (rc != 0) { pr_err("Failed to send disable command to BG\n"); goto unlock; } rc = bgrsb_set_ldo(dev, BGRSB_DISABLE_WHEEL_EVENTS); if (rc != 0) goto unlock; dev->bgrsb_current_state = BGRSB_STATE_RSB_CONFIGURED; pr_debug("RSB Disabled\n"); } unlock: mutex_unlock(&dev->rsb_state_mutex); } static void bgrsb_calibration(struct work_struct *work) { int rc = 0; struct bgrsb_msg req = {0}; struct bgrsb_priv *dev = container_of(work, struct bgrsb_priv, rsb_calibration_work); mutex_lock(&dev->rsb_state_mutex); if (!dev->is_cnfgrd) { pr_err("RSB is not configured\n"); goto unlock; } req.cmd_id = 0x03; req.data = dev->calbrtion_cpi; rc = bgrsb_tx_msg(dev, &req, 5); if (rc != 0) { pr_err("Failed to send resolution value to BG %d\n", rc); goto unlock; } req.cmd_id = 0x04; req.data = dev->calbrtion_intrvl; rc = bgrsb_tx_msg(dev, &req, 5); if (rc != 0) { pr_err("Failed to send interval value to BG %d\n", rc); goto unlock; } dev->is_calibrd = true; pr_debug("RSB Calibrated\n"); unlock: mutex_unlock(&dev->rsb_state_mutex); } static void bgrsb_buttn_configration(struct work_struct *work) { int rc = 0; struct bgrsb_msg req = {0}; struct bgrsb_priv *dev = container_of(work, struct bgrsb_priv, bttn_configr_work); mutex_lock(&dev->rsb_state_mutex); if (!dev->is_cnfgrd) { pr_err("RSB is not configured\n"); goto unlock; } req.cmd_id = 0x05; req.data = dev->bttn_configs; rc = bgrsb_tx_msg(dev, &req, 5); if (rc != 0) { pr_err("configuration cmnd failed %d\n", rc); goto unlock; } dev->bttn_configs = 0; pr_debug("RSB Button configured\n"); unlock: mutex_unlock(&dev->rsb_state_mutex); } static int bgrsb_handle_cmd_in_ssr(struct bgrsb_priv *dev, char *str) { long val; int ret; char *tmp; tmp = strsep(&str, ":"); if (!tmp) return -EINVAL; ret = kstrtol(tmp, 10, &val); if (ret < 0) return ret; if (val == BGRSB_POWER_ENABLE) dev->pending_enable = true; else if (val == BGRSB_POWER_DISABLE) dev->pending_enable = false; return 0; } static int split_bg_work(struct bgrsb_priv *dev, char *str) { long val; int ret; char *tmp; tmp = strsep(&str, ":"); if (!tmp) return -EINVAL; ret = kstrtol(tmp, 10, &val); if (ret < 0) return ret; switch (val) { case BGRSB_POWER_DISABLE: queue_work(dev->bgrsb_wq, &dev->rsb_down_work); break; case BGRSB_POWER_ENABLE: queue_work(dev->bgrsb_wq, &dev->rsb_up_work); break; case BGRSB_POWER_CALIBRATION: tmp = strsep(&str, ":"); if (!tmp) return -EINVAL; ret = kstrtol(tmp, 10, &val); if (ret < 0) return ret; dev->calbrtion_intrvl = (uint32_t)val; tmp = strsep(&str, ":"); if (!tmp) return -EINVAL; ret = kstrtol(tmp, 10, &val); if (ret < 0) return ret; dev->calbrtion_cpi = (uint32_t)val; queue_work(dev->bgrsb_wq, &dev->rsb_calibration_work); break; case BGRSB_BTTN_CONFIGURE: tmp = strsep(&str, ":"); if (!tmp) return -EINVAL; ret = kstrtol(tmp, 10, &val); if (ret < 0) return ret; dev->bttn_configs = (uint8_t)val; queue_work(dev->bgrsb_wq, &dev->bttn_configr_work); break; case BGRSB_IN_TWM: dev->is_in_twm = true; case BGRSB_GLINK_POWER_DISABLE: break; case BGRSB_OUT_TWM: dev->is_in_twm = false; case BGRSB_GLINK_POWER_ENABLE: break; } return 0; } static int store_enable(struct device *pdev, struct device_attribute *attr, const char *buff, size_t count) { int rc; struct bgrsb_priv *dev = dev_get_drvdata(pdev); char *arr; if (dev->blk_rsb_cmnds) { pr_err("Device is in TWM state\n"); return count; } arr = kstrdup(buff, GFP_KERNEL); if (!arr) return -ENOMEM; rc = split_bg_work(dev, arr); if (!dev->is_cnfgrd) { bgrsb_handle_cmd_in_ssr(dev, arr); kfree(arr); return -ENOMEDIUM; } if (rc != 0) pr_err("Not able to process request\n"); kfree(arr); return count; } static int show_enable(struct device *dev, struct device_attribute *attr, char *buff) { return 0; } static struct device_attribute dev_attr_rsb = { .attr = { .name = "enable", .mode = 00660, }, .show = show_enable, .store = store_enable, }; static int bgrsb_init(struct bgrsb_priv *dev) { bgrsb_drv = &dev->lhndl; mutex_init(&dev->glink_mutex); mutex_init(&dev->rsb_state_mutex); dev->ldo_action = BGRSB_NO_ACTION; dev->bgrsb_wq = create_singlethread_workqueue("bg-work-queue"); if (!dev->bgrsb_wq) { pr_err("Failed to init BG-RSB work-queue\n"); return -ENOMEM; } init_waitqueue_head(&dev->link_state_wait); /* set default bgrsb state */ dev->bgrsb_current_state = BGRSB_STATE_INIT; /* Init all works */ INIT_WORK(&dev->bg_up_work, bgrsb_bgup_work); INIT_WORK(&dev->bg_down_work, bgrsb_bgdown_work); INIT_WORK(&dev->rsb_up_work, bgrsb_enable_rsb); INIT_WORK(&dev->rsb_down_work, bgrsb_disable_rsb); INIT_WORK(&dev->rsb_calibration_work, bgrsb_calibration); INIT_WORK(&dev->bttn_configr_work, bgrsb_buttn_configration); return 0; } static int bg_rsb_probe(struct platform_device *pdev) { struct bgrsb_priv *dev; struct input_dev *input; struct device_node *node; int rc; unsigned int rsb_gpio; node = pdev->dev.of_node; dev = devm_kzalloc(&pdev->dev, sizeof(*dev), GFP_KERNEL); if (!dev) return -ENOMEM; /* Add wake lock for PM suspend */ wakeup_source_init(&dev->bgrsb_ws, "BGRSB_wake_lock"); dev->bgrsb_current_state = BGRSB_STATE_UNKNOWN; rc = bgrsb_init(dev); if (rc) goto err_ret_dev; /* Set up input device */ input = devm_input_allocate_device(&pdev->dev); if (!input) goto err_ret_dev; input_set_capability(input, EV_REL, REL_WHEEL); input_set_capability(input, EV_KEY, KEY_VOLUMEUP); input_set_capability(input, EV_KEY, KEY_VOLUMEDOWN); input->name = "bg-spi"; rc = input_register_device(input); if (rc) { pr_err("Input device registration failed\n"); goto err_ret_inp; } dev->input = input; /* register device for bg-wear ssr */ rc = bgrsb_ssr_register(dev); if (rc) { pr_err("Failed to register for bg ssr\n"); goto err_ret_inp; } rc = device_create_file(&pdev->dev, &dev_attr_rsb); if (rc) { pr_err("Not able to create the file bg-rsb/enable\n"); goto err_ret_inp; } dev->rsb_use_msm_gpio = of_property_read_bool(node, "qcom,rsb-use-msm-gpio"); if (dev->rsb_use_msm_gpio == true) { rsb_gpio = of_get_named_gpio(node, "qcom,bg-rsb-gpio", 0); pr_debug("gpio %d is configured\n", rsb_gpio); if (!gpio_is_valid(rsb_gpio)) { pr_err("gpio %d found is not valid\n", rsb_gpio); goto err_ret; } if (gpio_request(rsb_gpio, "msm_rsb_gpio")) { pr_err("gpio %d request failed\n", rsb_gpio); goto err_ret; } if (gpio_direction_output(rsb_gpio, 1)) { pr_err("gpio %d direction not set\n", rsb_gpio); goto err_ret; } pr_debug("rsb gpio successfully requested\n"); dev->msmrsb_gpio = rsb_gpio; } dev_set_drvdata(&pdev->dev, dev); rc = bgrsb_init_regulators(&pdev->dev); if (rc) { pr_err("Failed to set regulators\n"); goto err_ret_inp; } pr_debug("RSB probe successfully\n"); return 0; err_ret: return 0; err_ret_inp: input_free_device(input); err_ret_dev: devm_kfree(&pdev->dev, dev); return -ENODEV; } static int bg_rsb_remove(struct platform_device *pdev) { struct bgrsb_priv *dev = platform_get_drvdata(pdev); destroy_workqueue(dev->bgrsb_wq); input_free_device(dev->input); wakeup_source_trash(&dev->bgrsb_ws); return 0; } static int bg_rsb_resume(struct device *pldev) { struct platform_device *pdev = to_platform_device(pldev); struct bgrsb_priv *dev = platform_get_drvdata(pdev); mutex_lock(&dev->rsb_state_mutex); if (dev->bgrsb_current_state == BGRSB_STATE_RSB_CONFIGURED) goto ret_success; if (dev->bgrsb_current_state == BGRSB_STATE_INIT) { if (dev->is_cnfgrd && bgrsb_set_ldo(dev, BGRSB_HW_TURN_ON) == 0) { dev->bgrsb_current_state = BGRSB_STATE_RSB_CONFIGURED; pr_debug("RSB Cofigured\n"); goto ret_success; } pr_err("RSB failed to resume\n"); } mutex_unlock(&dev->rsb_state_mutex); return -EINVAL; ret_success: mutex_unlock(&dev->rsb_state_mutex); return 0; } static int bg_rsb_suspend(struct device *pldev) { struct platform_device *pdev = to_platform_device(pldev); struct bgrsb_priv *dev = platform_get_drvdata(pdev); mutex_lock(&dev->rsb_state_mutex); if (dev->bgrsb_current_state == BGRSB_STATE_INIT) goto ret_success; if (dev->bgrsb_current_state == BGRSB_STATE_RSB_ENABLED) { if (bgrsb_set_ldo(dev, BGRSB_DISABLE_WHEEL_EVENTS) != 0) goto ret_err; } if (bgrsb_set_ldo(dev, BGRSB_HW_TURN_OFF) == 0) { dev->bgrsb_current_state = BGRSB_STATE_INIT; pr_debug("RSB Init\n"); goto ret_success; } ret_err: pr_err("RSB failed to suspend\n"); mutex_unlock(&dev->rsb_state_mutex); return -EINVAL; ret_success: mutex_unlock(&dev->rsb_state_mutex); return 0; } static const struct of_device_id bg_rsb_of_match[] = { { .compatible = "qcom,bg-rsb", }, { } }; static const struct dev_pm_ops pm_rsb = { .resume = bg_rsb_resume, .suspend = bg_rsb_suspend, }; static struct platform_driver bg_rsb_driver = { .driver = { .name = "bg-rsb", .of_match_table = bg_rsb_of_match, .pm = &pm_rsb, }, .probe = bg_rsb_probe, .remove = bg_rsb_remove, }; module_platform_driver(bg_rsb_driver); MODULE_DESCRIPTION("SoC BG RSB driver"); MODULE_LICENSE("GPL v2");