// SPDX-License-Identifier: GPL-2.0-only /* * Copyright (c) Rajat Khandelwal * * Maxim MAX30208 digital temperature sensor with 0.1°C accuracy * (7-bit I2C slave address (0x50 - 0x53)) */ #include #include #include #include #include #include #define MAX30208_STATUS 0x00 #define MAX30208_STATUS_TEMP_RDY BIT(0) #define MAX30208_INT_ENABLE 0x01 #define MAX30208_INT_ENABLE_TEMP_RDY BIT(0) #define MAX30208_FIFO_OVF_CNTR 0x06 #define MAX30208_FIFO_DATA_CNTR 0x07 #define MAX30208_FIFO_DATA 0x08 #define MAX30208_FIFO_CONFIG 0x0a #define MAX30208_FIFO_CONFIG_RO BIT(1) #define MAX30208_SYSTEM_CTRL 0x0c #define MAX30208_SYSTEM_CTRL_RESET 0x01 #define MAX30208_TEMP_SENSOR_SETUP 0x14 #define MAX30208_TEMP_SENSOR_SETUP_CONV BIT(0) struct max30208_data { struct i2c_client *client; struct iio_dev *indio_dev; struct mutex lock; /* Lock to prevent concurrent reads of temperature readings */ }; static const struct iio_chan_spec max30208_channels[] = { { .type = IIO_TEMP, .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_SCALE), }, }; /** * max30208_request() - Request a reading * @data: Struct comprising member elements of the device * * Requests a reading from the device and waits until the conversion is ready. */ static int max30208_request(struct max30208_data *data) { /* * Sensor can take up to 500 ms to respond so execute a total of * 10 retries to give the device sufficient time. */ int retries = 10; u8 regval; int ret; ret = i2c_smbus_read_byte_data(data->client, MAX30208_TEMP_SENSOR_SETUP); if (ret < 0) return ret; regval = ret | MAX30208_TEMP_SENSOR_SETUP_CONV; ret = i2c_smbus_write_byte_data(data->client, MAX30208_TEMP_SENSOR_SETUP, regval); if (ret) return ret; while (retries--) { ret = i2c_smbus_read_byte_data(data->client, MAX30208_STATUS); if (ret < 0) return ret; if (ret & MAX30208_STATUS_TEMP_RDY) return 0; msleep(50); } dev_err(&data->client->dev, "Temperature conversion failed\n"); return -ETIMEDOUT; } static int max30208_update_temp(struct max30208_data *data) { u8 data_count; int ret; mutex_lock(&data->lock); ret = max30208_request(data); if (ret) goto unlock; ret = i2c_smbus_read_byte_data(data->client, MAX30208_FIFO_OVF_CNTR); if (ret < 0) goto unlock; else if (!ret) { ret = i2c_smbus_read_byte_data(data->client, MAX30208_FIFO_DATA_CNTR); if (ret < 0) goto unlock; data_count = ret; } else data_count = 1; while (data_count) { ret = i2c_smbus_read_word_swapped(data->client, MAX30208_FIFO_DATA); if (ret < 0) goto unlock; data_count--; } unlock: mutex_unlock(&data->lock); return ret; } /** * max30208_config_setup() - Set up FIFO configuration register * @data: Struct comprising member elements of the device * * Sets the rollover bit to '1' to enable overwriting FIFO during overflow. */ static int max30208_config_setup(struct max30208_data *data) { u8 regval; int ret; ret = i2c_smbus_read_byte_data(data->client, MAX30208_FIFO_CONFIG); if (ret < 0) return ret; regval = ret | MAX30208_FIFO_CONFIG_RO; ret = i2c_smbus_write_byte_data(data->client, MAX30208_FIFO_CONFIG, regval); if (ret) return ret; return 0; } static int max30208_read(struct iio_dev *indio_dev, struct iio_chan_spec const *chan, int *val, int *val2, long mask) { struct max30208_data *data = iio_priv(indio_dev); int ret; switch (mask) { case IIO_CHAN_INFO_RAW: ret = max30208_update_temp(data); if (ret < 0) return ret; *val = sign_extend32(ret, 15); return IIO_VAL_INT; case IIO_CHAN_INFO_SCALE: *val = 5; return IIO_VAL_INT; default: return -EINVAL; } } static const struct iio_info max30208_info = { .read_raw = max30208_read, }; static int max30208_probe(struct i2c_client *i2c) { struct device *dev = &i2c->dev; struct max30208_data *data; struct iio_dev *indio_dev; int ret; indio_dev = devm_iio_device_alloc(dev, sizeof(*data)); if (!indio_dev) return -ENOMEM; data = iio_priv(indio_dev); data->client = i2c; mutex_init(&data->lock); indio_dev->name = "max30208"; indio_dev->channels = max30208_channels; indio_dev->num_channels = ARRAY_SIZE(max30208_channels); indio_dev->info = &max30208_info; indio_dev->modes = INDIO_DIRECT_MODE; ret = i2c_smbus_write_byte_data(data->client, MAX30208_SYSTEM_CTRL, MAX30208_SYSTEM_CTRL_RESET); if (ret) { dev_err(dev, "Failure in performing reset\n"); return ret; } msleep(50); ret = max30208_config_setup(data); if (ret) return ret; ret = devm_iio_device_register(dev, indio_dev); if (ret) { dev_err(dev, "Failed to register IIO device\n"); return ret; } return 0; } static const struct i2c_device_id max30208_id_table[] = { { "max30208" }, { } }; MODULE_DEVICE_TABLE(i2c, max30208_id_table); static const struct acpi_device_id max30208_acpi_match[] = { { "MAX30208" }, { } }; MODULE_DEVICE_TABLE(acpi, max30208_acpi_match); static const struct of_device_id max30208_of_match[] = { { .compatible = "maxim,max30208" }, { } }; MODULE_DEVICE_TABLE(of, max30208_of_match); static struct i2c_driver max30208_driver = { .driver = { .name = "max30208", .of_match_table = max30208_of_match, .acpi_match_table = max30208_acpi_match, }, .probe = max30208_probe, .id_table = max30208_id_table, }; module_i2c_driver(max30208_driver); MODULE_AUTHOR("Rajat Khandelwal "); MODULE_DESCRIPTION("Maxim MAX30208 digital temperature sensor"); MODULE_LICENSE("GPL");