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

Commit f09a0560 authored by Shantanu Jain's avatar Shantanu Jain Committed by Sudhakar Manapati
Browse files

input: focaltech: release pinctrl resources on probe failure



devm_pinctrl_get() does not release the ownership of mux function
of pins in the associated pin-group whenever a failure occurs in
driver probe routine. i.e struct pin_desc's mux_owner field is
still marked as being in use after a failure occurs in probe.
As a result of this, if another driver tries to acquire the
ownership of same pin, it gets an error while applying that
setting. To fix this, explicitly release the mux function
ownership of the the pin, by adding a new pin-group in pinctrl
DT and a new pinctrl state in touch device's DT node. This new
pin-group does not have a function setting (qcom,pin-func property).
This new state is explicitly activated during a probe failure
and driver remove routine to release the mux function ownership.

The patch also reorganizes the pinctrl related code in driver.

This patch is propagated from msm-3.10 kernel.
(commit: acecc29921e4e69721df875cde116bcb3a46392c
input: focaltech: release pinctrl resources on probe failure)

Change-Id: I16a97fefc64dd171deb800b481aa74a797c9ad55
Signed-off-by: default avatarShantanu Jain <shjain@codeaurora.org>
parent 7e2ea02b
Loading
Loading
Loading
Loading
+4 −1
Original line number Diff line number Diff line
@@ -43,6 +43,8 @@ Required properties:
			config defined in pin groups of interrupt and reset gpio.
			"pmx_ts_suspend" : Disabled configuration of pins, this should specify sleep
			config defined in pin groups of interrupt and reset gpio.
			"pmx_ts_release" : Release configuration of pins, this should specify
			release config defined in pin groups of interrupt and reset gpio.

Optional properties:

@@ -73,9 +75,10 @@ Example:
			interrupts = <1 0x2>;
			vdd-supply = <&pm8110_l19>;
			vcc_i2c-supply = <&pm8110_l14>;
			pinctrl-names = "pmx_ts_active","pmx_ts_suspend";
			pinctrl-names = "pmx_ts_active","pmx_ts_suspend","pmx_ts_release";
			pinctrl-0 = <&ts_int_active &ts_reset_active>;
			pinctrl-1 = <&ts_int_suspend &ts_reset_suspend>;
			pinctrl-2 = <&ts_release>;
			focaltech,name = "ft6x06";
			focaltech,family-id = <0x06>;
			focaltech,reset-gpio = <&msmgpio 0 0x00>;
+69 −60
Original line number Diff line number Diff line
@@ -168,6 +168,10 @@
#define FT_MAGIC_BLOADER_GZF_30	0x7ff4
#define FT_MAGIC_BLOADER_GZF	0x7bf4

#define PINCTRL_STATE_ACTIVE	"pmx_ts_active"
#define PINCTRL_STATE_SUSPEND	"pmx_ts_suspend"
#define PINCTRL_STATE_RELEASE	"pmx_ts_release"

enum {
	FT_BLOADER_VERSION_LZ4 = 0,
	FT_BLOADER_VERSION_Z7 = 1,
@@ -222,8 +226,9 @@ struct ft5x06_ts_data {
	struct early_suspend early_suspend;
#endif
	struct pinctrl *ts_pinctrl;
	struct pinctrl_state *gpio_state_active;
	struct pinctrl_state *gpio_state_suspend;
	struct pinctrl_state *pinctrl_state_active;
	struct pinctrl_state *pinctrl_state_suspend;
	struct pinctrl_state *pinctrl_state_release;
};

static int ft5x06_i2c_read(struct i2c_client *client, char *writebuf,
@@ -519,63 +524,52 @@ static int ft5x06_ts_pinctrl_init(struct ft5x06_ts_data *ft5x06_data)
	/* Get pinctrl if target uses pinctrl */
	ft5x06_data->ts_pinctrl = devm_pinctrl_get(&(ft5x06_data->client->dev));
	if (IS_ERR_OR_NULL(ft5x06_data->ts_pinctrl)) {
		dev_dbg(&ft5x06_data->client->dev,
			"Target does not use pinctrl\n");
		retval = PTR_ERR(ft5x06_data->ts_pinctrl);
		ft5x06_data->ts_pinctrl = NULL;
		return retval;
	}

	ft5x06_data->gpio_state_active
		= pinctrl_lookup_state(ft5x06_data->ts_pinctrl,
			"pmx_ts_active");
	if (IS_ERR_OR_NULL(ft5x06_data->gpio_state_active)) {
		dev_dbg(&ft5x06_data->client->dev,
			"Can not get ts default pinstate\n");
		retval = PTR_ERR(ft5x06_data->gpio_state_active);
		ft5x06_data->ts_pinctrl = NULL;
		return retval;
			"Target does not use pinctrl %d\n", retval);
		goto err_pinctrl_get;
	}

	ft5x06_data->gpio_state_suspend
	ft5x06_data->pinctrl_state_active
		= pinctrl_lookup_state(ft5x06_data->ts_pinctrl,
			"pmx_ts_suspend");
	if (IS_ERR_OR_NULL(ft5x06_data->gpio_state_suspend)) {
				PINCTRL_STATE_ACTIVE);
	if (IS_ERR_OR_NULL(ft5x06_data->pinctrl_state_active)) {
		retval = PTR_ERR(ft5x06_data->pinctrl_state_active);
		dev_err(&ft5x06_data->client->dev,
			"Can not get ts sleep pinstate\n");
		retval = PTR_ERR(ft5x06_data->gpio_state_suspend);
		ft5x06_data->ts_pinctrl = NULL;
		return retval;
			"Can not lookup %s pinstate %d\n",
			PINCTRL_STATE_ACTIVE, retval);
		goto err_pinctrl_lookup;
	}

	return 0;
}

static int ft5x06_ts_pinctrl_select(struct ft5x06_ts_data *ft5x06_data,
						bool on)
{
	struct pinctrl_state *pins_state;
	int ret;

	pins_state = on ? ft5x06_data->gpio_state_active
		: ft5x06_data->gpio_state_suspend;
	if (!IS_ERR_OR_NULL(pins_state)) {
		ret = pinctrl_select_state(ft5x06_data->ts_pinctrl, pins_state);
		if (ret) {
	ft5x06_data->pinctrl_state_suspend
		= pinctrl_lookup_state(ft5x06_data->ts_pinctrl,
			PINCTRL_STATE_SUSPEND);
	if (IS_ERR_OR_NULL(ft5x06_data->pinctrl_state_suspend)) {
		retval = PTR_ERR(ft5x06_data->pinctrl_state_suspend);
		dev_err(&ft5x06_data->client->dev,
				"can not set %s pins\n",
				on ? "pmx_ts_active" : "pmx_ts_suspend");
			return ret;
			"Can not lookup %s pinstate %d\n",
			PINCTRL_STATE_SUSPEND, retval);
		goto err_pinctrl_lookup;
	}
	} else {
		dev_err(&ft5x06_data->client->dev,
			"not a valid '%s' pinstate\n",
				on ? "pmx_ts_active" : "pmx_ts_suspend");

	ft5x06_data->pinctrl_state_release
		= pinctrl_lookup_state(ft5x06_data->ts_pinctrl,
			PINCTRL_STATE_RELEASE);
	if (IS_ERR_OR_NULL(ft5x06_data->pinctrl_state_release)) {
		retval = PTR_ERR(ft5x06_data->pinctrl_state_release);
		dev_dbg(&ft5x06_data->client->dev,
			"Can not lookup %s pinstate %d\n",
			PINCTRL_STATE_RELEASE, retval);
	}

	return 0;
}

err_pinctrl_lookup:
	devm_pinctrl_put(ft5x06_data->ts_pinctrl);
err_pinctrl_get:
	ft5x06_data->ts_pinctrl = NULL;
	return retval;
}

#ifdef CONFIG_PM
static int ft5x06_ts_suspend(struct device *dev)
@@ -1610,8 +1604,14 @@ static int ft5x06_ts_probe(struct i2c_client *client,

	err = ft5x06_ts_pinctrl_init(data);
	if (!err && data->ts_pinctrl) {
		err = ft5x06_ts_pinctrl_select(data, true);
		if (err < 0)
		err = pinctrl_select_state(data->ts_pinctrl,
					data->pinctrl_state_active);
		if (err < 0) {
			dev_err(&client->dev,
				"failed to select pin to active state");
			goto pinctrl_deinit;
		}
	} else {
		goto pwr_off;
	}

@@ -1619,7 +1619,7 @@ static int ft5x06_ts_probe(struct i2c_client *client,
		err = gpio_request(pdata->irq_gpio, "ft5x06_irq_gpio");
		if (err) {
			dev_err(&client->dev, "irq gpio request failed");
			goto pwr_off;
			goto err_gpio_req;
		}
		err = gpio_direction_input(pdata->irq_gpio);
		if (err) {
@@ -1792,18 +1792,21 @@ irq_free:
free_reset_gpio:
	if (gpio_is_valid(pdata->reset_gpio))
		gpio_free(pdata->reset_gpio);
	if (data->ts_pinctrl) {
		err = ft5x06_ts_pinctrl_select(data, false);
		if (err < 0)
			pr_err("Cannot get idle pinctrl state\n");
	}
free_irq_gpio:
	if (gpio_is_valid(pdata->irq_gpio))
		gpio_free(pdata->irq_gpio);
err_gpio_req:
pinctrl_deinit:
	if (data->ts_pinctrl) {
		err = ft5x06_ts_pinctrl_select(data, false);
		if (err < 0)
			pr_err("Cannot get idle pinctrl state\n");
		if (IS_ERR_OR_NULL(data->pinctrl_state_release)) {
			devm_pinctrl_put(data->ts_pinctrl);
			data->ts_pinctrl = NULL;
		} else {
			err = pinctrl_select_state(data->ts_pinctrl,
					data->pinctrl_state_release);
			if (err)
				pr_err("failed to select relase pinctrl state\n");
		}
	}
pwr_off:
	if (pdata->power_on)
@@ -1848,9 +1851,15 @@ static int ft5x06_ts_remove(struct i2c_client *client)
		gpio_free(data->pdata->irq_gpio);

	if (data->ts_pinctrl) {
		retval = ft5x06_ts_pinctrl_select(data, false);
		if (IS_ERR_OR_NULL(data->pinctrl_state_release)) {
			devm_pinctrl_put(data->ts_pinctrl);
			data->ts_pinctrl = NULL;
		} else {
			retval = pinctrl_select_state(data->ts_pinctrl,
					data->pinctrl_state_release);
			if (retval < 0)
			pr_err("Cannot get idle pinctrl state\n");
				pr_err("failed to select release pinctrl state\n");
		}
	}

	if (data->pdata->power_on)