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