summaryrefslogtreecommitdiff
path: root/drivers/iio/pressure/sdp500.c
blob: 6ff32e3fa637286c35e704a9dcd0e848da22d7d0 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
// SPDX-License-Identifier: GPL-2.0-only
/*
 * Driver for Sensirion sdp500 and sdp510 pressure sensors
 *
 * Datasheet: https://sensirion.com/resource/datasheet/sdp600
 */

#include <linux/i2c.h>
#include <linux/crc8.h>
#include <linux/iio/iio.h>
#include <linux/mod_devicetable.h>
#include <linux/regulator/consumer.h>
#include <asm/unaligned.h>

#define SDP500_CRC8_POLYNOMIAL  0x31   /* x8+x5+x4+1 (normalized to 0x31) */
#define SDP500_READ_SIZE        3

#define SDP500_I2C_START_MEAS 0xF1

struct sdp500_data {
	struct device *dev;
};

DECLARE_CRC8_TABLE(sdp500_crc8_table);

static int sdp500_start_measurement(struct sdp500_data *data)
{
	struct i2c_client *client = to_i2c_client(data->dev);

	return i2c_smbus_write_byte(client, SDP500_I2C_START_MEAS);
}

static const struct iio_chan_spec sdp500_channels[] = {
	{
		.type = IIO_PRESSURE,
		.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
			BIT(IIO_CHAN_INFO_SCALE),
	},
};

static int sdp500_read_raw(struct iio_dev *indio_dev,
			  struct iio_chan_spec const *chan,
			  int *val, int *val2, long mask)
{
	int ret;
	u8 rxbuf[SDP500_READ_SIZE];
	u8 received_crc, calculated_crc;
	struct sdp500_data *data = iio_priv(indio_dev);
	struct i2c_client *client = to_i2c_client(data->dev);

	switch (mask) {
	case IIO_CHAN_INFO_RAW:
		ret = i2c_master_recv(client, rxbuf, SDP500_READ_SIZE);
		if (ret < 0) {
			dev_err(data->dev, "Failed to receive data");
			return ret;
		}
		if (ret != SDP500_READ_SIZE) {
			dev_err(data->dev, "Data is received wrongly");
			return -EIO;
		}

		received_crc = rxbuf[2];
		calculated_crc = crc8(sdp500_crc8_table, rxbuf,
				      sizeof(rxbuf) - 1, 0x00);
		if (received_crc != calculated_crc) {
			dev_err(data->dev,
				"calculated crc = 0x%.2X, received 0x%.2X",
				calculated_crc, received_crc);
			return -EIO;
		}

		*val = get_unaligned_be16(rxbuf);
		return IIO_VAL_INT;
	case IIO_CHAN_INFO_SCALE:
		*val = 1;
		*val2 = 60;

		return IIO_VAL_FRACTIONAL;
	default:
		return -EINVAL;
	}
}

static const struct iio_info sdp500_info = {
	.read_raw = &sdp500_read_raw,
};

static int sdp500_probe(struct i2c_client *client)
{
	struct iio_dev *indio_dev;
	struct sdp500_data *data;
	struct device *dev = &client->dev;
	int ret;
	u8 rxbuf[SDP500_READ_SIZE];

	ret = devm_regulator_get_enable(dev, "vdd");
	if (ret)
		return dev_err_probe(dev, ret,
			"Failed to get and enable regulator\n");

	indio_dev = devm_iio_device_alloc(dev, sizeof(*data));
	if (!indio_dev)
		return -ENOMEM;

	/* has to be done before the first i2c communication */
	crc8_populate_msb(sdp500_crc8_table, SDP500_CRC8_POLYNOMIAL);

	data = iio_priv(indio_dev);
	data->dev = dev;

	indio_dev->name = "sdp500";
	indio_dev->channels = sdp500_channels;
	indio_dev->info = &sdp500_info;
	indio_dev->modes = INDIO_DIRECT_MODE;
	indio_dev->num_channels = ARRAY_SIZE(sdp500_channels);

	ret = sdp500_start_measurement(data);
	if (ret)
		return dev_err_probe(dev, ret, "Failed to start measurement");

	/* First measurement is not correct, read it out to get rid of it */
	i2c_master_recv(client, rxbuf, SDP500_READ_SIZE);

	ret = devm_iio_device_register(dev, indio_dev);
	if (ret)
		return dev_err_probe(dev, ret, "Failed to register indio_dev");

	return 0;
}

static const struct i2c_device_id sdp500_id[] = {
	{ "sdp500" },
	{ }
};
MODULE_DEVICE_TABLE(i2c, sdp500_id);

static const struct of_device_id sdp500_of_match[] = {
	{ .compatible = "sensirion,sdp500" },
	{ }
};
MODULE_DEVICE_TABLE(of, sdp500_of_match);

static struct i2c_driver sdp500_driver = {
	.driver = {
		.name = "sensirion,sdp500",
		.of_match_table = sdp500_of_match,
	},
	.probe = sdp500_probe,
	.id_table = sdp500_id,
};
module_i2c_driver(sdp500_driver);

MODULE_AUTHOR("Thomas Sioutas <thomas.sioutas@prodrive-technologies.com>");
MODULE_DESCRIPTION("Driver for Sensirion SDP500 differential pressure sensor");
MODULE_LICENSE("GPL");