Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 86007830 authored by qctecmdr's avatar qctecmdr Committed by Gerrit - the friendly Code Review server
Browse files

Merge "sdm429w: add bg-rsg driver changes"

parents e8956bd6 c2657225
Loading
Loading
Loading
Loading
+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>;
	};
+9 −0
Original line number Diff line number Diff line
@@ -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
+1 −0
Original line number Diff line number Diff line
@@ -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
+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");