brintos

brintos / linux-shallow public Read only

0
0
Text · 5.5 KiB · 720469f Raw
252 lines · c
1// SPDX-License-Identifier: GPL-2.0-only2 3/*4 * Copyright (c) Rajat Khandelwal <rajat.khandelwal@linux.intel.com>5 *6 * Maxim MAX30208 digital temperature sensor with 0.1°C accuracy7 * (7-bit I2C slave address (0x50 - 0x53))8 */9 10#include <linux/bitops.h>11#include <linux/delay.h>12#include <linux/iio/iio.h>13#include <linux/i2c.h>14#include <linux/module.h>15#include <linux/types.h>16 17#define MAX30208_STATUS			0x0018#define MAX30208_STATUS_TEMP_RDY	BIT(0)19#define MAX30208_INT_ENABLE		0x0120#define MAX30208_INT_ENABLE_TEMP_RDY	BIT(0)21 22#define MAX30208_FIFO_OVF_CNTR		0x0623#define MAX30208_FIFO_DATA_CNTR		0x0724#define MAX30208_FIFO_DATA		0x0825 26#define MAX30208_FIFO_CONFIG		0x0a27#define MAX30208_FIFO_CONFIG_RO		BIT(1)28 29#define MAX30208_SYSTEM_CTRL		0x0c30#define MAX30208_SYSTEM_CTRL_RESET	0x0131 32#define MAX30208_TEMP_SENSOR_SETUP	0x1433#define MAX30208_TEMP_SENSOR_SETUP_CONV	BIT(0)34 35struct max30208_data {36	struct i2c_client *client;37	struct mutex lock; /* Lock to prevent concurrent reads of temperature readings */38};39 40static const struct iio_chan_spec max30208_channels[] = {41	{42		.type = IIO_TEMP,43		.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_SCALE),44	},45};46 47/**48 * max30208_request() - Request a reading49 * @data: Struct comprising member elements of the device50 *51 * Requests a reading from the device and waits until the conversion is ready.52 */53static int max30208_request(struct max30208_data *data)54{55	/*56	 * Sensor can take up to 500 ms to respond so execute a total of57	 * 10 retries to give the device sufficient time.58	 */59	int retries = 10;60	u8 regval;61	int ret;62 63	ret = i2c_smbus_read_byte_data(data->client, MAX30208_TEMP_SENSOR_SETUP);64	if (ret < 0)65		return ret;66 67	regval = ret | MAX30208_TEMP_SENSOR_SETUP_CONV;68 69	ret = i2c_smbus_write_byte_data(data->client, MAX30208_TEMP_SENSOR_SETUP, regval);70	if (ret)71		return ret;72 73	while (retries--) {74		ret = i2c_smbus_read_byte_data(data->client, MAX30208_STATUS);75		if (ret < 0)76			return ret;77 78		if (ret & MAX30208_STATUS_TEMP_RDY)79			return 0;80 81		msleep(50);82	}83	dev_err(&data->client->dev, "Temperature conversion failed\n");84 85	return -ETIMEDOUT;86}87 88static int max30208_update_temp(struct max30208_data *data)89{90	u8 data_count;91	int ret;92 93	mutex_lock(&data->lock);94 95	ret = max30208_request(data);96	if (ret)97		goto unlock;98 99	ret = i2c_smbus_read_byte_data(data->client, MAX30208_FIFO_OVF_CNTR);100	if (ret < 0)101		goto unlock;102	else if (!ret) {103		ret = i2c_smbus_read_byte_data(data->client, MAX30208_FIFO_DATA_CNTR);104		if (ret < 0)105			goto unlock;106 107		data_count = ret;108	} else109		data_count = 1;110 111	while (data_count) {112		ret = i2c_smbus_read_word_swapped(data->client, MAX30208_FIFO_DATA);113		if (ret < 0)114			goto unlock;115 116		data_count--;117	}118 119unlock:120	mutex_unlock(&data->lock);121	return ret;122}123 124/**125 * max30208_config_setup() - Set up FIFO configuration register126 * @data: Struct comprising member elements of the device127 *128 * Sets the rollover bit to '1' to enable overwriting FIFO during overflow.129 */130static int max30208_config_setup(struct max30208_data *data)131{132	u8 regval;133	int ret;134 135	ret = i2c_smbus_read_byte_data(data->client, MAX30208_FIFO_CONFIG);136	if (ret < 0)137		return ret;138 139	regval = ret | MAX30208_FIFO_CONFIG_RO;140 141	ret = i2c_smbus_write_byte_data(data->client, MAX30208_FIFO_CONFIG, regval);142	if (ret)143		return ret;144 145	return 0;146}147 148static int max30208_read(struct iio_dev *indio_dev,149			 struct iio_chan_spec const *chan,150			 int *val, int *val2, long mask)151{152	struct max30208_data *data = iio_priv(indio_dev);153	int ret;154 155	switch (mask) {156	case IIO_CHAN_INFO_RAW:157		ret = max30208_update_temp(data);158		if (ret < 0)159			return ret;160 161		*val = sign_extend32(ret, 15);162		return IIO_VAL_INT;163 164	case IIO_CHAN_INFO_SCALE:165		*val = 5;166		return IIO_VAL_INT;167 168	default:169		return -EINVAL;170	}171}172 173static const struct iio_info max30208_info = {174	.read_raw = max30208_read,175};176 177static int max30208_probe(struct i2c_client *i2c)178{179	struct device *dev = &i2c->dev;180	struct max30208_data *data;181	struct iio_dev *indio_dev;182	int ret;183 184	indio_dev = devm_iio_device_alloc(dev, sizeof(*data));185	if (!indio_dev)186		return -ENOMEM;187 188	data = iio_priv(indio_dev);189	data->client = i2c;190	mutex_init(&data->lock);191 192	indio_dev->name = "max30208";193	indio_dev->channels = max30208_channels;194	indio_dev->num_channels = ARRAY_SIZE(max30208_channels);195	indio_dev->info = &max30208_info;196	indio_dev->modes = INDIO_DIRECT_MODE;197 198	ret = i2c_smbus_write_byte_data(data->client, MAX30208_SYSTEM_CTRL,199					MAX30208_SYSTEM_CTRL_RESET);200	if (ret) {201		dev_err(dev, "Failure in performing reset\n");202		return ret;203	}204 205	msleep(50);206 207	ret = max30208_config_setup(data);208	if (ret)209		return ret;210 211	ret = devm_iio_device_register(dev, indio_dev);212	if (ret) {213		dev_err(dev, "Failed to register IIO device\n");214		return ret;215	}216 217	return 0;218}219 220static const struct i2c_device_id max30208_id_table[] = {221	{ "max30208" },222	{ }223};224MODULE_DEVICE_TABLE(i2c, max30208_id_table);225 226static const struct acpi_device_id max30208_acpi_match[] = {227	{ "MAX30208" },228	{ }229};230MODULE_DEVICE_TABLE(acpi, max30208_acpi_match);231 232static const struct of_device_id max30208_of_match[] = {233	{ .compatible = "maxim,max30208" },234	{ }235};236MODULE_DEVICE_TABLE(of, max30208_of_match);237 238static struct i2c_driver max30208_driver = {239	.driver = {240		.name = "max30208",241		.of_match_table = max30208_of_match,242		.acpi_match_table = max30208_acpi_match,243	},244	.probe = max30208_probe,245	.id_table = max30208_id_table,246};247module_i2c_driver(max30208_driver);248 249MODULE_AUTHOR("Rajat Khandelwal <rajat.khandelwal@linux.intel.com>");250MODULE_DESCRIPTION("Maxim MAX30208 digital temperature sensor");251MODULE_LICENSE("GPL");252