255 lines · c
1// SPDX-License-Identifier: GPL-2.0-only2/*3 * mpl115.c - Support for Freescale MPL115A pressure/temperature sensor4 *5 * Copyright (c) 2014 Peter Meerwald <pmeerw@pmeerw.net>6 *7 * TODO: synchronization with system suspend8 */9 10#include <linux/module.h>11#include <linux/iio/iio.h>12#include <linux/delay.h>13#include <linux/gpio/consumer.h>14 15#include "mpl115.h"16 17#define MPL115_PADC 0x00 /* pressure ADC output value, MSB first, 10 bit */18#define MPL115_TADC 0x02 /* temperature ADC output value, MSB first, 10 bit */19#define MPL115_A0 0x04 /* 12 bit integer, 3 bit fraction */20#define MPL115_B1 0x06 /* 2 bit integer, 13 bit fraction */21#define MPL115_B2 0x08 /* 1 bit integer, 14 bit fraction */22#define MPL115_C12 0x0a /* 0 bit integer, 13 bit fraction */23#define MPL115_CONVERT 0x12 /* convert temperature and pressure */24 25struct mpl115_data {26 struct device *dev;27 struct mutex lock;28 s16 a0;29 s16 b1, b2;30 s16 c12;31 struct gpio_desc *shutdown;32 const struct mpl115_ops *ops;33};34 35static int mpl115_request(struct mpl115_data *data)36{37 int ret = data->ops->write(data->dev, MPL115_CONVERT, 0);38 39 if (ret < 0)40 return ret;41 42 usleep_range(3000, 4000);43 44 return 0;45}46 47static int mpl115_comp_pressure(struct mpl115_data *data, int *val, int *val2)48{49 int ret;50 u16 padc, tadc;51 int a1, y1, pcomp;52 unsigned kpa;53 54 mutex_lock(&data->lock);55 ret = mpl115_request(data);56 if (ret < 0)57 goto done;58 59 ret = data->ops->read(data->dev, MPL115_PADC);60 if (ret < 0)61 goto done;62 padc = ret >> 6;63 64 ret = data->ops->read(data->dev, MPL115_TADC);65 if (ret < 0)66 goto done;67 tadc = ret >> 6;68 69 /* see Freescale AN3785 */70 a1 = data->b1 + ((data->c12 * tadc) >> 11);71 y1 = (data->a0 << 10) + a1 * padc;72 73 /* compensated pressure with 4 fractional bits */74 pcomp = (y1 + ((data->b2 * (int) tadc) >> 1)) >> 9;75 76 kpa = pcomp * (115 - 50) / 1023 + (50 << 4);77 *val = kpa >> 4;78 *val2 = (kpa & 15) * (1000000 >> 4);79done:80 mutex_unlock(&data->lock);81 return ret;82}83 84static int mpl115_read_temp(struct mpl115_data *data)85{86 int ret;87 88 mutex_lock(&data->lock);89 ret = mpl115_request(data);90 if (ret < 0)91 goto done;92 ret = data->ops->read(data->dev, MPL115_TADC);93done:94 mutex_unlock(&data->lock);95 return ret;96}97 98static int mpl115_read_raw(struct iio_dev *indio_dev,99 struct iio_chan_spec const *chan,100 int *val, int *val2, long mask)101{102 struct mpl115_data *data = iio_priv(indio_dev);103 int ret;104 105 switch (mask) {106 case IIO_CHAN_INFO_PROCESSED:107 pm_runtime_get_sync(data->dev);108 ret = mpl115_comp_pressure(data, val, val2);109 if (ret < 0)110 return ret;111 pm_runtime_mark_last_busy(data->dev);112 pm_runtime_put_autosuspend(data->dev);113 114 return IIO_VAL_INT_PLUS_MICRO;115 case IIO_CHAN_INFO_RAW:116 pm_runtime_get_sync(data->dev);117 /* temperature -5.35 C / LSB, 472 LSB is 25 C */118 ret = mpl115_read_temp(data);119 if (ret < 0)120 return ret;121 pm_runtime_mark_last_busy(data->dev);122 pm_runtime_put_autosuspend(data->dev);123 *val = ret >> 6;124 125 return IIO_VAL_INT;126 case IIO_CHAN_INFO_OFFSET:127 *val = -605;128 *val2 = 750000;129 return IIO_VAL_INT_PLUS_MICRO;130 case IIO_CHAN_INFO_SCALE:131 *val = -186;132 *val2 = 915888;133 return IIO_VAL_INT_PLUS_MICRO;134 }135 return -EINVAL;136}137 138static const struct iio_chan_spec mpl115_channels[] = {139 {140 .type = IIO_PRESSURE,141 .info_mask_separate = BIT(IIO_CHAN_INFO_PROCESSED),142 },143 {144 .type = IIO_TEMP,145 .info_mask_separate = BIT(IIO_CHAN_INFO_RAW),146 .info_mask_shared_by_type =147 BIT(IIO_CHAN_INFO_OFFSET) | BIT(IIO_CHAN_INFO_SCALE),148 },149};150 151static const struct iio_info mpl115_info = {152 .read_raw = &mpl115_read_raw,153};154 155int mpl115_probe(struct device *dev, const char *name,156 const struct mpl115_ops *ops)157{158 struct mpl115_data *data;159 struct iio_dev *indio_dev;160 int ret;161 162 indio_dev = devm_iio_device_alloc(dev, sizeof(*data));163 if (!indio_dev)164 return -ENOMEM;165 166 data = iio_priv(indio_dev);167 data->dev = dev;168 data->ops = ops;169 mutex_init(&data->lock);170 171 indio_dev->info = &mpl115_info;172 indio_dev->name = name;173 indio_dev->modes = INDIO_DIRECT_MODE;174 indio_dev->channels = mpl115_channels;175 indio_dev->num_channels = ARRAY_SIZE(mpl115_channels);176 177 ret = data->ops->init(data->dev);178 if (ret)179 return ret;180 181 dev_set_drvdata(dev, indio_dev);182 183 ret = data->ops->read(data->dev, MPL115_A0);184 if (ret < 0)185 return ret;186 data->a0 = ret;187 ret = data->ops->read(data->dev, MPL115_B1);188 if (ret < 0)189 return ret;190 data->b1 = ret;191 ret = data->ops->read(data->dev, MPL115_B2);192 if (ret < 0)193 return ret;194 data->b2 = ret;195 ret = data->ops->read(data->dev, MPL115_C12);196 if (ret < 0)197 return ret;198 data->c12 = ret;199 200 data->shutdown = devm_gpiod_get_optional(dev, "shutdown",201 GPIOD_OUT_LOW);202 if (IS_ERR(data->shutdown))203 return dev_err_probe(dev, PTR_ERR(data->shutdown),204 "cannot get shutdown gpio\n");205 206 if (data->shutdown) {207 /* Enable runtime PM */208 pm_runtime_get_noresume(dev);209 pm_runtime_set_active(dev);210 pm_runtime_enable(dev);211 212 /*213 * As the device takes 3 ms to come up with a fresh214 * reading after power-on and 5 ms to actually power-on,215 * do not shut it down unnecessarily. Set autosuspend to216 * 2000 ms.217 */218 pm_runtime_set_autosuspend_delay(dev, 2000);219 pm_runtime_use_autosuspend(dev);220 pm_runtime_put(dev);221 222 dev_dbg(dev, "low-power mode enabled");223 } else224 dev_dbg(dev, "low-power mode disabled");225 226 return devm_iio_device_register(dev, indio_dev);227}228EXPORT_SYMBOL_NS_GPL(mpl115_probe, IIO_MPL115);229 230static int mpl115_runtime_suspend(struct device *dev)231{232 struct mpl115_data *data = iio_priv(dev_get_drvdata(dev));233 234 gpiod_set_value(data->shutdown, 1);235 236 return 0;237}238 239static int mpl115_runtime_resume(struct device *dev)240{241 struct mpl115_data *data = iio_priv(dev_get_drvdata(dev));242 243 gpiod_set_value(data->shutdown, 0);244 usleep_range(5000, 6000);245 246 return 0;247}248 249EXPORT_NS_RUNTIME_DEV_PM_OPS(mpl115_dev_pm_ops, mpl115_runtime_suspend,250 mpl115_runtime_resume, NULL, IIO_MPL115);251 252MODULE_AUTHOR("Peter Meerwald <pmeerw@pmeerw.net>");253MODULE_DESCRIPTION("Freescale MPL115 pressure/temperature driver");254MODULE_LICENSE("GPL");255