Commit 6b8cffdc authored by Hans de Goede's avatar Hans de Goede Committed by Jonathan Cameron
Browse files

iio: accel: mxc4005: Reset chip on probe() and resume()

On some designs the chip is not properly reset when powered up at boot or
after a suspend/resume cycle.

Use the sw-reset feature to ensure that the chip is in a clean state
after probe() / resume() and in the case of resume() restore the settings
(scale, trigger-enabled).

Closes: https://bugzilla.kernel.org/show_bug.cgi?id=218578


Signed-off-by: default avatarHans de Goede <hdegoede@redhat.com>
Link: https://lore.kernel.org/r/20240326113700.56725-3-hdegoede@redhat.com


Cc: <Stable@vger.kernel.org>
Signed-off-by: default avatarJonathan Cameron <Jonathan.Cameron@huawei.com>
parent 57a15927
Loading
Loading
Loading
Loading
+68 −0
Original line number Diff line number Diff line
@@ -5,6 +5,7 @@
 * Copyright (c) 2014, Intel Corporation.
 */

#include <linux/delay.h>
#include <linux/module.h>
#include <linux/i2c.h>
#include <linux/iio/iio.h>
@@ -36,6 +37,7 @@

#define MXC4005_REG_INT_CLR1		0x01
#define MXC4005_REG_INT_CLR1_BIT_DRDYC	0x01
#define MXC4005_REG_INT_CLR1_SW_RST	0x10

#define MXC4005_REG_CONTROL		0x0D
#define MXC4005_REG_CONTROL_MASK_FSR	GENMASK(6, 5)
@@ -43,6 +45,9 @@

#define MXC4005_REG_DEVICE_ID		0x0E

/* Datasheet does not specify a reset time, this is a conservative guess */
#define MXC4005_RESET_TIME_US		2000

enum mxc4005_axis {
	AXIS_X,
	AXIS_Y,
@@ -66,6 +71,8 @@ struct mxc4005_data {
		s64 timestamp __aligned(8);
	} scan;
	bool trigger_enabled;
	unsigned int control;
	unsigned int int_mask1;
};

/*
@@ -349,6 +356,7 @@ static int mxc4005_set_trigger_state(struct iio_trigger *trig,
		return ret;
	}

	data->int_mask1 = val;
	data->trigger_enabled = state;
	mutex_unlock(&data->mutex);

@@ -384,6 +392,13 @@ static int mxc4005_chip_init(struct mxc4005_data *data)

	dev_dbg(data->dev, "MXC4005 chip id %02x\n", reg);

	ret = regmap_write(data->regmap, MXC4005_REG_INT_CLR1,
			   MXC4005_REG_INT_CLR1_SW_RST);
	if (ret < 0)
		return dev_err_probe(data->dev, ret, "resetting chip\n");

	fsleep(MXC4005_RESET_TIME_US);

	ret = regmap_write(data->regmap, MXC4005_REG_INT_MASK0, 0);
	if (ret < 0)
		return dev_err_probe(data->dev, ret, "writing INT_MASK0\n");
@@ -479,6 +494,58 @@ static int mxc4005_probe(struct i2c_client *client)
	return devm_iio_device_register(&client->dev, indio_dev);
}

static int mxc4005_suspend(struct device *dev)
{
	struct iio_dev *indio_dev = dev_get_drvdata(dev);
	struct mxc4005_data *data = iio_priv(indio_dev);
	int ret;

	/* Save control to restore it on resume */
	ret = regmap_read(data->regmap, MXC4005_REG_CONTROL, &data->control);
	if (ret < 0)
		dev_err(data->dev, "failed to read reg_control\n");

	return ret;
}

static int mxc4005_resume(struct device *dev)
{
	struct iio_dev *indio_dev = dev_get_drvdata(dev);
	struct mxc4005_data *data = iio_priv(indio_dev);
	int ret;

	ret = regmap_write(data->regmap, MXC4005_REG_INT_CLR1,
			   MXC4005_REG_INT_CLR1_SW_RST);
	if (ret) {
		dev_err(data->dev, "failed to reset chip: %d\n", ret);
		return ret;
	}

	fsleep(MXC4005_RESET_TIME_US);

	ret = regmap_write(data->regmap, MXC4005_REG_CONTROL, data->control);
	if (ret) {
		dev_err(data->dev, "failed to restore control register\n");
		return ret;
	}

	ret = regmap_write(data->regmap, MXC4005_REG_INT_MASK0, 0);
	if (ret) {
		dev_err(data->dev, "failed to restore interrupt 0 mask\n");
		return ret;
	}

	ret = regmap_write(data->regmap, MXC4005_REG_INT_MASK1, data->int_mask1);
	if (ret) {
		dev_err(data->dev, "failed to restore interrupt 1 mask\n");
		return ret;
	}

	return 0;
}

static DEFINE_SIMPLE_DEV_PM_OPS(mxc4005_pm_ops, mxc4005_suspend, mxc4005_resume);

static const struct acpi_device_id mxc4005_acpi_match[] = {
	{"MXC4005",	0},
	{"MXC6655",	0},
@@ -506,6 +573,7 @@ static struct i2c_driver mxc4005_driver = {
		.name = MXC4005_DRV_NAME,
		.acpi_match_table = mxc4005_acpi_match,
		.of_match_table = mxc4005_of_match,
		.pm = pm_sleep_ptr(&mxc4005_pm_ops),
	},
	.probe		= mxc4005_probe,
	.id_table	= mxc4005_id,