18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * mpl115.c - Support for Freescale MPL115A pressure/temperature sensor 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright (c) 2014 Peter Meerwald <pmeerw@pmeerw.net> 68c2ecf20Sopenharmony_ci * 78c2ecf20Sopenharmony_ci * TODO: shutdown pin 88c2ecf20Sopenharmony_ci */ 98c2ecf20Sopenharmony_ci 108c2ecf20Sopenharmony_ci#include <linux/module.h> 118c2ecf20Sopenharmony_ci#include <linux/iio/iio.h> 128c2ecf20Sopenharmony_ci#include <linux/delay.h> 138c2ecf20Sopenharmony_ci 148c2ecf20Sopenharmony_ci#include "mpl115.h" 158c2ecf20Sopenharmony_ci 168c2ecf20Sopenharmony_ci#define MPL115_PADC 0x00 /* pressure ADC output value, MSB first, 10 bit */ 178c2ecf20Sopenharmony_ci#define MPL115_TADC 0x02 /* temperature ADC output value, MSB first, 10 bit */ 188c2ecf20Sopenharmony_ci#define MPL115_A0 0x04 /* 12 bit integer, 3 bit fraction */ 198c2ecf20Sopenharmony_ci#define MPL115_B1 0x06 /* 2 bit integer, 13 bit fraction */ 208c2ecf20Sopenharmony_ci#define MPL115_B2 0x08 /* 1 bit integer, 14 bit fraction */ 218c2ecf20Sopenharmony_ci#define MPL115_C12 0x0a /* 0 bit integer, 13 bit fraction */ 228c2ecf20Sopenharmony_ci#define MPL115_CONVERT 0x12 /* convert temperature and pressure */ 238c2ecf20Sopenharmony_ci 248c2ecf20Sopenharmony_cistruct mpl115_data { 258c2ecf20Sopenharmony_ci struct device *dev; 268c2ecf20Sopenharmony_ci struct mutex lock; 278c2ecf20Sopenharmony_ci s16 a0; 288c2ecf20Sopenharmony_ci s16 b1, b2; 298c2ecf20Sopenharmony_ci s16 c12; 308c2ecf20Sopenharmony_ci const struct mpl115_ops *ops; 318c2ecf20Sopenharmony_ci}; 328c2ecf20Sopenharmony_ci 338c2ecf20Sopenharmony_cistatic int mpl115_request(struct mpl115_data *data) 348c2ecf20Sopenharmony_ci{ 358c2ecf20Sopenharmony_ci int ret = data->ops->write(data->dev, MPL115_CONVERT, 0); 368c2ecf20Sopenharmony_ci 378c2ecf20Sopenharmony_ci if (ret < 0) 388c2ecf20Sopenharmony_ci return ret; 398c2ecf20Sopenharmony_ci 408c2ecf20Sopenharmony_ci usleep_range(3000, 4000); 418c2ecf20Sopenharmony_ci 428c2ecf20Sopenharmony_ci return 0; 438c2ecf20Sopenharmony_ci} 448c2ecf20Sopenharmony_ci 458c2ecf20Sopenharmony_cistatic int mpl115_comp_pressure(struct mpl115_data *data, int *val, int *val2) 468c2ecf20Sopenharmony_ci{ 478c2ecf20Sopenharmony_ci int ret; 488c2ecf20Sopenharmony_ci u16 padc, tadc; 498c2ecf20Sopenharmony_ci int a1, y1, pcomp; 508c2ecf20Sopenharmony_ci unsigned kpa; 518c2ecf20Sopenharmony_ci 528c2ecf20Sopenharmony_ci mutex_lock(&data->lock); 538c2ecf20Sopenharmony_ci ret = mpl115_request(data); 548c2ecf20Sopenharmony_ci if (ret < 0) 558c2ecf20Sopenharmony_ci goto done; 568c2ecf20Sopenharmony_ci 578c2ecf20Sopenharmony_ci ret = data->ops->read(data->dev, MPL115_PADC); 588c2ecf20Sopenharmony_ci if (ret < 0) 598c2ecf20Sopenharmony_ci goto done; 608c2ecf20Sopenharmony_ci padc = ret >> 6; 618c2ecf20Sopenharmony_ci 628c2ecf20Sopenharmony_ci ret = data->ops->read(data->dev, MPL115_TADC); 638c2ecf20Sopenharmony_ci if (ret < 0) 648c2ecf20Sopenharmony_ci goto done; 658c2ecf20Sopenharmony_ci tadc = ret >> 6; 668c2ecf20Sopenharmony_ci 678c2ecf20Sopenharmony_ci /* see Freescale AN3785 */ 688c2ecf20Sopenharmony_ci a1 = data->b1 + ((data->c12 * tadc) >> 11); 698c2ecf20Sopenharmony_ci y1 = (data->a0 << 10) + a1 * padc; 708c2ecf20Sopenharmony_ci 718c2ecf20Sopenharmony_ci /* compensated pressure with 4 fractional bits */ 728c2ecf20Sopenharmony_ci pcomp = (y1 + ((data->b2 * (int) tadc) >> 1)) >> 9; 738c2ecf20Sopenharmony_ci 748c2ecf20Sopenharmony_ci kpa = pcomp * (115 - 50) / 1023 + (50 << 4); 758c2ecf20Sopenharmony_ci *val = kpa >> 4; 768c2ecf20Sopenharmony_ci *val2 = (kpa & 15) * (1000000 >> 4); 778c2ecf20Sopenharmony_cidone: 788c2ecf20Sopenharmony_ci mutex_unlock(&data->lock); 798c2ecf20Sopenharmony_ci return ret; 808c2ecf20Sopenharmony_ci} 818c2ecf20Sopenharmony_ci 828c2ecf20Sopenharmony_cistatic int mpl115_read_temp(struct mpl115_data *data) 838c2ecf20Sopenharmony_ci{ 848c2ecf20Sopenharmony_ci int ret; 858c2ecf20Sopenharmony_ci 868c2ecf20Sopenharmony_ci mutex_lock(&data->lock); 878c2ecf20Sopenharmony_ci ret = mpl115_request(data); 888c2ecf20Sopenharmony_ci if (ret < 0) 898c2ecf20Sopenharmony_ci goto done; 908c2ecf20Sopenharmony_ci ret = data->ops->read(data->dev, MPL115_TADC); 918c2ecf20Sopenharmony_cidone: 928c2ecf20Sopenharmony_ci mutex_unlock(&data->lock); 938c2ecf20Sopenharmony_ci return ret; 948c2ecf20Sopenharmony_ci} 958c2ecf20Sopenharmony_ci 968c2ecf20Sopenharmony_cistatic int mpl115_read_raw(struct iio_dev *indio_dev, 978c2ecf20Sopenharmony_ci struct iio_chan_spec const *chan, 988c2ecf20Sopenharmony_ci int *val, int *val2, long mask) 998c2ecf20Sopenharmony_ci{ 1008c2ecf20Sopenharmony_ci struct mpl115_data *data = iio_priv(indio_dev); 1018c2ecf20Sopenharmony_ci int ret; 1028c2ecf20Sopenharmony_ci 1038c2ecf20Sopenharmony_ci switch (mask) { 1048c2ecf20Sopenharmony_ci case IIO_CHAN_INFO_PROCESSED: 1058c2ecf20Sopenharmony_ci ret = mpl115_comp_pressure(data, val, val2); 1068c2ecf20Sopenharmony_ci if (ret < 0) 1078c2ecf20Sopenharmony_ci return ret; 1088c2ecf20Sopenharmony_ci return IIO_VAL_INT_PLUS_MICRO; 1098c2ecf20Sopenharmony_ci case IIO_CHAN_INFO_RAW: 1108c2ecf20Sopenharmony_ci /* temperature -5.35 C / LSB, 472 LSB is 25 C */ 1118c2ecf20Sopenharmony_ci ret = mpl115_read_temp(data); 1128c2ecf20Sopenharmony_ci if (ret < 0) 1138c2ecf20Sopenharmony_ci return ret; 1148c2ecf20Sopenharmony_ci *val = ret >> 6; 1158c2ecf20Sopenharmony_ci return IIO_VAL_INT; 1168c2ecf20Sopenharmony_ci case IIO_CHAN_INFO_OFFSET: 1178c2ecf20Sopenharmony_ci *val = -605; 1188c2ecf20Sopenharmony_ci *val2 = 750000; 1198c2ecf20Sopenharmony_ci return IIO_VAL_INT_PLUS_MICRO; 1208c2ecf20Sopenharmony_ci case IIO_CHAN_INFO_SCALE: 1218c2ecf20Sopenharmony_ci *val = -186; 1228c2ecf20Sopenharmony_ci *val2 = 915888; 1238c2ecf20Sopenharmony_ci return IIO_VAL_INT_PLUS_MICRO; 1248c2ecf20Sopenharmony_ci } 1258c2ecf20Sopenharmony_ci return -EINVAL; 1268c2ecf20Sopenharmony_ci} 1278c2ecf20Sopenharmony_ci 1288c2ecf20Sopenharmony_cistatic const struct iio_chan_spec mpl115_channels[] = { 1298c2ecf20Sopenharmony_ci { 1308c2ecf20Sopenharmony_ci .type = IIO_PRESSURE, 1318c2ecf20Sopenharmony_ci .info_mask_separate = BIT(IIO_CHAN_INFO_PROCESSED), 1328c2ecf20Sopenharmony_ci }, 1338c2ecf20Sopenharmony_ci { 1348c2ecf20Sopenharmony_ci .type = IIO_TEMP, 1358c2ecf20Sopenharmony_ci .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), 1368c2ecf20Sopenharmony_ci .info_mask_shared_by_type = 1378c2ecf20Sopenharmony_ci BIT(IIO_CHAN_INFO_OFFSET) | BIT(IIO_CHAN_INFO_SCALE), 1388c2ecf20Sopenharmony_ci }, 1398c2ecf20Sopenharmony_ci}; 1408c2ecf20Sopenharmony_ci 1418c2ecf20Sopenharmony_cistatic const struct iio_info mpl115_info = { 1428c2ecf20Sopenharmony_ci .read_raw = &mpl115_read_raw, 1438c2ecf20Sopenharmony_ci}; 1448c2ecf20Sopenharmony_ci 1458c2ecf20Sopenharmony_ciint mpl115_probe(struct device *dev, const char *name, 1468c2ecf20Sopenharmony_ci const struct mpl115_ops *ops) 1478c2ecf20Sopenharmony_ci{ 1488c2ecf20Sopenharmony_ci struct mpl115_data *data; 1498c2ecf20Sopenharmony_ci struct iio_dev *indio_dev; 1508c2ecf20Sopenharmony_ci int ret; 1518c2ecf20Sopenharmony_ci 1528c2ecf20Sopenharmony_ci indio_dev = devm_iio_device_alloc(dev, sizeof(*data)); 1538c2ecf20Sopenharmony_ci if (!indio_dev) 1548c2ecf20Sopenharmony_ci return -ENOMEM; 1558c2ecf20Sopenharmony_ci 1568c2ecf20Sopenharmony_ci data = iio_priv(indio_dev); 1578c2ecf20Sopenharmony_ci data->dev = dev; 1588c2ecf20Sopenharmony_ci data->ops = ops; 1598c2ecf20Sopenharmony_ci mutex_init(&data->lock); 1608c2ecf20Sopenharmony_ci 1618c2ecf20Sopenharmony_ci indio_dev->info = &mpl115_info; 1628c2ecf20Sopenharmony_ci indio_dev->name = name; 1638c2ecf20Sopenharmony_ci indio_dev->modes = INDIO_DIRECT_MODE; 1648c2ecf20Sopenharmony_ci indio_dev->channels = mpl115_channels; 1658c2ecf20Sopenharmony_ci indio_dev->num_channels = ARRAY_SIZE(mpl115_channels); 1668c2ecf20Sopenharmony_ci 1678c2ecf20Sopenharmony_ci ret = data->ops->init(data->dev); 1688c2ecf20Sopenharmony_ci if (ret) 1698c2ecf20Sopenharmony_ci return ret; 1708c2ecf20Sopenharmony_ci 1718c2ecf20Sopenharmony_ci ret = data->ops->read(data->dev, MPL115_A0); 1728c2ecf20Sopenharmony_ci if (ret < 0) 1738c2ecf20Sopenharmony_ci return ret; 1748c2ecf20Sopenharmony_ci data->a0 = ret; 1758c2ecf20Sopenharmony_ci ret = data->ops->read(data->dev, MPL115_B1); 1768c2ecf20Sopenharmony_ci if (ret < 0) 1778c2ecf20Sopenharmony_ci return ret; 1788c2ecf20Sopenharmony_ci data->b1 = ret; 1798c2ecf20Sopenharmony_ci ret = data->ops->read(data->dev, MPL115_B2); 1808c2ecf20Sopenharmony_ci if (ret < 0) 1818c2ecf20Sopenharmony_ci return ret; 1828c2ecf20Sopenharmony_ci data->b2 = ret; 1838c2ecf20Sopenharmony_ci ret = data->ops->read(data->dev, MPL115_C12); 1848c2ecf20Sopenharmony_ci if (ret < 0) 1858c2ecf20Sopenharmony_ci return ret; 1868c2ecf20Sopenharmony_ci data->c12 = ret; 1878c2ecf20Sopenharmony_ci 1888c2ecf20Sopenharmony_ci return devm_iio_device_register(dev, indio_dev); 1898c2ecf20Sopenharmony_ci} 1908c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(mpl115_probe); 1918c2ecf20Sopenharmony_ci 1928c2ecf20Sopenharmony_ciMODULE_AUTHOR("Peter Meerwald <pmeerw@pmeerw.net>"); 1938c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Freescale MPL115 pressure/temperature driver"); 1948c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 195