xref: /openbmc/linux/drivers/hwmon/sht15.c (revision ffdb3ba0)
139ea6ea3SFabio Estevam // SPDX-License-Identifier: GPL-2.0
2251eb40fSJonathan Cameron /*
3251eb40fSJonathan Cameron  * sht15.c - support for the SHT15 Temperature and Humidity Sensor
4251eb40fSJonathan Cameron  *
5edec5af7SVivien Didelot  * Portions Copyright (c) 2010-2012 Savoir-faire Linux Inc.
682c7465bSJerome Oufella  *          Jerome Oufella <jerome.oufella@savoirfairelinux.com>
7cc15c7ebSVivien Didelot  *          Vivien Didelot <vivien.didelot@savoirfairelinux.com>
8cc15c7ebSVivien Didelot  *
9251eb40fSJonathan Cameron  * Copyright (c) 2009 Jonathan Cameron
10251eb40fSJonathan Cameron  *
11251eb40fSJonathan Cameron  * Copyright (c) 2007 Wouter Horre
12251eb40fSJonathan Cameron  *
137ebd8b66SMauro Carvalho Chehab  * For further information, see the Documentation/hwmon/sht15.rst file.
14251eb40fSJonathan Cameron  */
15251eb40fSJonathan Cameron 
16251eb40fSJonathan Cameron #include <linux/interrupt.h>
17251eb40fSJonathan Cameron #include <linux/irq.h>
18251eb40fSJonathan Cameron #include <linux/module.h>
19251eb40fSJonathan Cameron #include <linux/init.h>
20251eb40fSJonathan Cameron #include <linux/hwmon.h>
21251eb40fSJonathan Cameron #include <linux/hwmon-sysfs.h>
22251eb40fSJonathan Cameron #include <linux/mutex.h>
23251eb40fSJonathan Cameron #include <linux/platform_device.h>
24d43c36dcSAlexey Dobriyan #include <linux/sched.h>
25251eb40fSJonathan Cameron #include <linux/delay.h>
26251eb40fSJonathan Cameron #include <linux/jiffies.h>
27251eb40fSJonathan Cameron #include <linux/err.h>
28251eb40fSJonathan Cameron #include <linux/regulator/consumer.h>
295a0e3ad6STejun Heo #include <linux/slab.h>
3060063497SArun Sharma #include <linux/atomic.h>
3133836ee9Syalin wang #include <linux/bitrev.h>
3218673114SLinus Walleij #include <linux/gpio/consumer.h>
3318673114SLinus Walleij #include <linux/of.h>
34251eb40fSJonathan Cameron 
3599a0378dSVivien Didelot /* Commands */
3699a0378dSVivien Didelot #define SHT15_MEASURE_TEMP		0x03
3799a0378dSVivien Didelot #define SHT15_MEASURE_RH		0x05
38cc15c7ebSVivien Didelot #define SHT15_WRITE_STATUS		0x06
39cc15c7ebSVivien Didelot #define SHT15_READ_STATUS		0x07
40181148aeSVivien Didelot #define SHT15_SOFT_RESET		0x1E
41251eb40fSJonathan Cameron 
4299a0378dSVivien Didelot /* Min timings */
4399a0378dSVivien Didelot #define SHT15_TSCKL			100	/* (nsecs) clock low */
4499a0378dSVivien Didelot #define SHT15_TSCKH			100	/* (nsecs) clock high */
4599a0378dSVivien Didelot #define SHT15_TSU			150	/* (nsecs) data setup time */
46181148aeSVivien Didelot #define SHT15_TSRST			11	/* (msecs) soft reset time */
47251eb40fSJonathan Cameron 
48cc15c7ebSVivien Didelot /* Status Register Bits */
49cc15c7ebSVivien Didelot #define SHT15_STATUS_LOW_RESOLUTION	0x01
50cc15c7ebSVivien Didelot #define SHT15_STATUS_NO_OTP_RELOAD	0x02
51cc15c7ebSVivien Didelot #define SHT15_STATUS_HEATER		0x04
52cc15c7ebSVivien Didelot #define SHT15_STATUS_LOW_BATTERY	0x40
53cc15c7ebSVivien Didelot 
54edec5af7SVivien Didelot /* List of supported chips */
55edec5af7SVivien Didelot enum sht15_chips { sht10, sht11, sht15, sht71, sht75 };
56edec5af7SVivien Didelot 
5799a0378dSVivien Didelot /* Actions the driver may be doing */
5899a0378dSVivien Didelot enum sht15_state {
5999a0378dSVivien Didelot 	SHT15_READING_NOTHING,
6099a0378dSVivien Didelot 	SHT15_READING_TEMP,
6199a0378dSVivien Didelot 	SHT15_READING_HUMID
6299a0378dSVivien Didelot };
63251eb40fSJonathan Cameron 
64251eb40fSJonathan Cameron /**
6525985edcSLucas De Marchi  * struct sht15_temppair - elements of voltage dependent temp calc
66251eb40fSJonathan Cameron  * @vdd:	supply voltage in microvolts
67251eb40fSJonathan Cameron  * @d1:		see data sheet
68251eb40fSJonathan Cameron  */
69251eb40fSJonathan Cameron struct sht15_temppair {
70251eb40fSJonathan Cameron 	int vdd; /* microvolts */
71251eb40fSJonathan Cameron 	int d1;
72251eb40fSJonathan Cameron };
73251eb40fSJonathan Cameron 
7499a0378dSVivien Didelot /* Table 9 from datasheet - relates temperature calculation to supply voltage */
75251eb40fSJonathan Cameron static const struct sht15_temppair temppoints[] = {
76251eb40fSJonathan Cameron 	{ 2500000, -39400 },
77251eb40fSJonathan Cameron 	{ 3000000, -39600 },
78251eb40fSJonathan Cameron 	{ 3500000, -39700 },
79251eb40fSJonathan Cameron 	{ 4000000, -39800 },
80251eb40fSJonathan Cameron 	{ 5000000, -40100 },
81251eb40fSJonathan Cameron };
82251eb40fSJonathan Cameron 
8382c7465bSJerome Oufella /* Table from CRC datasheet, section 2.4 */
8482c7465bSJerome Oufella static const u8 sht15_crc8_table[] = {
8582c7465bSJerome Oufella 	0,	49,	98,	83,	196,	245,	166,	151,
8682c7465bSJerome Oufella 	185,	136,	219,	234,	125,	76,	31,	46,
8782c7465bSJerome Oufella 	67,	114,	33,	16,	135,	182,	229,	212,
8882c7465bSJerome Oufella 	250,	203,	152,	169,	62,	15,	92,	109,
8982c7465bSJerome Oufella 	134,	183,	228,	213,	66,	115,	32,	17,
9082c7465bSJerome Oufella 	63,	14,	93,	108,	251,	202,	153,	168,
9182c7465bSJerome Oufella 	197,	244,	167,	150,	1,	48,	99,	82,
9282c7465bSJerome Oufella 	124,	77,	30,	47,	184,	137,	218,	235,
9382c7465bSJerome Oufella 	61,	12,	95,	110,	249,	200,	155,	170,
9482c7465bSJerome Oufella 	132,	181,	230,	215,	64,	113,	34,	19,
9582c7465bSJerome Oufella 	126,	79,	28,	45,	186,	139,	216,	233,
9682c7465bSJerome Oufella 	199,	246,	165,	148,	3,	50,	97,	80,
9782c7465bSJerome Oufella 	187,	138,	217,	232,	127,	78,	29,	44,
9882c7465bSJerome Oufella 	2,	51,	96,	81,	198,	247,	164,	149,
9982c7465bSJerome Oufella 	248,	201,	154,	171,	60,	13,	94,	111,
10082c7465bSJerome Oufella 	65,	112,	35,	18,	133,	180,	231,	214,
10182c7465bSJerome Oufella 	122,	75,	24,	41,	190,	143,	220,	237,
10282c7465bSJerome Oufella 	195,	242,	161,	144,	7,	54,	101,	84,
10382c7465bSJerome Oufella 	57,	8,	91,	106,	253,	204,	159,	174,
10482c7465bSJerome Oufella 	128,	177,	226,	211,	68,	117,	38,	23,
10582c7465bSJerome Oufella 	252,	205,	158,	175,	56,	9,	90,	107,
10682c7465bSJerome Oufella 	69,	116,	39,	22,	129,	176,	227,	210,
10782c7465bSJerome Oufella 	191,	142,	221,	236,	123,	74,	25,	40,
10882c7465bSJerome Oufella 	6,	55,	100,	85,	194,	243,	160,	145,
10982c7465bSJerome Oufella 	71,	118,	37,	20,	131,	178,	225,	208,
11082c7465bSJerome Oufella 	254,	207,	156,	173,	58,	11,	88,	105,
11182c7465bSJerome Oufella 	4,	53,	102,	87,	192,	241,	162,	147,
11282c7465bSJerome Oufella 	189,	140,	223,	238,	121,	72,	27,	42,
11382c7465bSJerome Oufella 	193,	240,	163,	146,	5,	52,	103,	86,
11482c7465bSJerome Oufella 	120,	73,	26,	43,	188,	141,	222,	239,
11582c7465bSJerome Oufella 	130,	179,	224,	209,	70,	119,	36,	21,
11682c7465bSJerome Oufella 	59,	10,	89,	104,	255,	206,	157,	172
11782c7465bSJerome Oufella };
11882c7465bSJerome Oufella 
119251eb40fSJonathan Cameron /**
120251eb40fSJonathan Cameron  * struct sht15_data - device instance specific data
12118673114SLinus Walleij  * @sck:		clock GPIO line
12218673114SLinus Walleij  * @data:		data GPIO line
12399a0378dSVivien Didelot  * @read_work:		bh of interrupt handler.
12499a0378dSVivien Didelot  * @wait_queue:		wait queue for getting values from device.
12599a0378dSVivien Didelot  * @val_temp:		last temperature value read from device.
12699a0378dSVivien Didelot  * @val_humid:		last humidity value read from device.
127cc15c7ebSVivien Didelot  * @val_status:		last status register value read from device.
12882c7465bSJerome Oufella  * @checksum_ok:	last value read from the device passed CRC validation.
12982c7465bSJerome Oufella  * @checksumming:	flag used to enable the data validation with CRC.
13099a0378dSVivien Didelot  * @state:		state identifying the action the driver is doing.
13199a0378dSVivien Didelot  * @measurements_valid:	are the current stored measures valid (start condition).
132cc15c7ebSVivien Didelot  * @status_valid:	is the current stored status valid (start condition).
13399a0378dSVivien Didelot  * @last_measurement:	time of last measure.
134cc15c7ebSVivien Didelot  * @last_status:	time of last status reading.
13599a0378dSVivien Didelot  * @read_lock:		mutex to ensure only one read in progress at a time.
13699a0378dSVivien Didelot  * @dev:		associate device structure.
13799a0378dSVivien Didelot  * @hwmon_dev:		device associated with hwmon subsystem.
13899a0378dSVivien Didelot  * @reg:		associated regulator (if specified).
13999a0378dSVivien Didelot  * @nb:			notifier block to handle notifications of voltage
14099a0378dSVivien Didelot  *                      changes.
141142c0901SVivien Didelot  * @supply_uv:		local copy of supply voltage used to allow use of
14299a0378dSVivien Didelot  *                      regulator consumer if available.
143142c0901SVivien Didelot  * @supply_uv_valid:	indicates that an updated value has not yet been
14499a0378dSVivien Didelot  *			obtained from the regulator and so any calculations
145251eb40fSJonathan Cameron  *			based upon it will be invalid.
146142c0901SVivien Didelot  * @update_supply_work:	work struct that is used to update the supply_uv.
14799a0378dSVivien Didelot  * @interrupt_handled:	flag used to indicate a handler has been scheduled.
148251eb40fSJonathan Cameron  */
149251eb40fSJonathan Cameron struct sht15_data {
15018673114SLinus Walleij 	struct gpio_desc		*sck;
15118673114SLinus Walleij 	struct gpio_desc		*data;
152251eb40fSJonathan Cameron 	struct work_struct		read_work;
153251eb40fSJonathan Cameron 	wait_queue_head_t		wait_queue;
154251eb40fSJonathan Cameron 	uint16_t			val_temp;
155251eb40fSJonathan Cameron 	uint16_t			val_humid;
156cc15c7ebSVivien Didelot 	u8				val_status;
15782c7465bSJerome Oufella 	bool				checksum_ok;
15882c7465bSJerome Oufella 	bool				checksumming;
15999a0378dSVivien Didelot 	enum sht15_state		state;
16099a0378dSVivien Didelot 	bool				measurements_valid;
161cc15c7ebSVivien Didelot 	bool				status_valid;
16299a0378dSVivien Didelot 	unsigned long			last_measurement;
163cc15c7ebSVivien Didelot 	unsigned long			last_status;
164251eb40fSJonathan Cameron 	struct mutex			read_lock;
165251eb40fSJonathan Cameron 	struct device			*dev;
166251eb40fSJonathan Cameron 	struct device			*hwmon_dev;
167251eb40fSJonathan Cameron 	struct regulator		*reg;
168251eb40fSJonathan Cameron 	struct notifier_block		nb;
169142c0901SVivien Didelot 	int				supply_uv;
170142c0901SVivien Didelot 	bool				supply_uv_valid;
171251eb40fSJonathan Cameron 	struct work_struct		update_supply_work;
172251eb40fSJonathan Cameron 	atomic_t			interrupt_handled;
173251eb40fSJonathan Cameron };
174251eb40fSJonathan Cameron 
175251eb40fSJonathan Cameron /**
17682c7465bSJerome Oufella  * sht15_crc8() - compute crc8
17782c7465bSJerome Oufella  * @data:	sht15 specific data.
17882c7465bSJerome Oufella  * @value:	sht15 retrieved data.
179d5324e90SGuenter Roeck  * @len:	Length of retrieved data
18082c7465bSJerome Oufella  *
18182c7465bSJerome Oufella  * This implements section 2 of the CRC datasheet.
18282c7465bSJerome Oufella  */
sht15_crc8(struct sht15_data * data,const u8 * value,int len)18382c7465bSJerome Oufella static u8 sht15_crc8(struct sht15_data *data,
18482c7465bSJerome Oufella 		const u8 *value,
18582c7465bSJerome Oufella 		int len)
18682c7465bSJerome Oufella {
18733836ee9Syalin wang 	u8 crc = bitrev8(data->val_status & 0x0F);
18882c7465bSJerome Oufella 
18982c7465bSJerome Oufella 	while (len--) {
19082c7465bSJerome Oufella 		crc = sht15_crc8_table[*value ^ crc];
19182c7465bSJerome Oufella 		value++;
19282c7465bSJerome Oufella 	}
19382c7465bSJerome Oufella 
19482c7465bSJerome Oufella 	return crc;
19582c7465bSJerome Oufella }
19682c7465bSJerome Oufella 
19782c7465bSJerome Oufella /**
198251eb40fSJonathan Cameron  * sht15_connection_reset() - reset the comms interface
199251eb40fSJonathan Cameron  * @data:	sht15 specific data
200251eb40fSJonathan Cameron  *
201251eb40fSJonathan Cameron  * This implements section 3.4 of the data sheet
202251eb40fSJonathan Cameron  */
sht15_connection_reset(struct sht15_data * data)203412e29c1SVivien Didelot static int sht15_connection_reset(struct sht15_data *data)
204251eb40fSJonathan Cameron {
205412e29c1SVivien Didelot 	int i, err;
20699a0378dSVivien Didelot 
20718673114SLinus Walleij 	err = gpiod_direction_output(data->data, 1);
208412e29c1SVivien Didelot 	if (err)
209412e29c1SVivien Didelot 		return err;
210251eb40fSJonathan Cameron 	ndelay(SHT15_TSCKL);
21118673114SLinus Walleij 	gpiod_set_value(data->sck, 0);
212251eb40fSJonathan Cameron 	ndelay(SHT15_TSCKL);
213251eb40fSJonathan Cameron 	for (i = 0; i < 9; ++i) {
21418673114SLinus Walleij 		gpiod_set_value(data->sck, 1);
215251eb40fSJonathan Cameron 		ndelay(SHT15_TSCKH);
21618673114SLinus Walleij 		gpiod_set_value(data->sck, 0);
217251eb40fSJonathan Cameron 		ndelay(SHT15_TSCKL);
218251eb40fSJonathan Cameron 	}
219412e29c1SVivien Didelot 	return 0;
220251eb40fSJonathan Cameron }
22199a0378dSVivien Didelot 
222251eb40fSJonathan Cameron /**
223251eb40fSJonathan Cameron  * sht15_send_bit() - send an individual bit to the device
224251eb40fSJonathan Cameron  * @data:	device state data
225251eb40fSJonathan Cameron  * @val:	value of bit to be sent
22699a0378dSVivien Didelot  */
sht15_send_bit(struct sht15_data * data,int val)227251eb40fSJonathan Cameron static inline void sht15_send_bit(struct sht15_data *data, int val)
228251eb40fSJonathan Cameron {
22918673114SLinus Walleij 	gpiod_set_value(data->data, val);
230251eb40fSJonathan Cameron 	ndelay(SHT15_TSU);
23118673114SLinus Walleij 	gpiod_set_value(data->sck, 1);
232251eb40fSJonathan Cameron 	ndelay(SHT15_TSCKH);
23318673114SLinus Walleij 	gpiod_set_value(data->sck, 0);
234251eb40fSJonathan Cameron 	ndelay(SHT15_TSCKL); /* clock low time */
235251eb40fSJonathan Cameron }
236251eb40fSJonathan Cameron 
237251eb40fSJonathan Cameron /**
238251eb40fSJonathan Cameron  * sht15_transmission_start() - specific sequence for new transmission
239251eb40fSJonathan Cameron  * @data:	device state data
24099a0378dSVivien Didelot  *
241251eb40fSJonathan Cameron  * Timings for this are not documented on the data sheet, so very
242251eb40fSJonathan Cameron  * conservative ones used in implementation. This implements
243251eb40fSJonathan Cameron  * figure 12 on the data sheet.
24499a0378dSVivien Didelot  */
sht15_transmission_start(struct sht15_data * data)245412e29c1SVivien Didelot static int sht15_transmission_start(struct sht15_data *data)
246251eb40fSJonathan Cameron {
247412e29c1SVivien Didelot 	int err;
248412e29c1SVivien Didelot 
249251eb40fSJonathan Cameron 	/* ensure data is high and output */
25018673114SLinus Walleij 	err = gpiod_direction_output(data->data, 1);
251412e29c1SVivien Didelot 	if (err)
252412e29c1SVivien Didelot 		return err;
253251eb40fSJonathan Cameron 	ndelay(SHT15_TSU);
25418673114SLinus Walleij 	gpiod_set_value(data->sck, 0);
255251eb40fSJonathan Cameron 	ndelay(SHT15_TSCKL);
25618673114SLinus Walleij 	gpiod_set_value(data->sck, 1);
257251eb40fSJonathan Cameron 	ndelay(SHT15_TSCKH);
25818673114SLinus Walleij 	gpiod_set_value(data->data, 0);
259251eb40fSJonathan Cameron 	ndelay(SHT15_TSU);
26018673114SLinus Walleij 	gpiod_set_value(data->sck, 0);
261251eb40fSJonathan Cameron 	ndelay(SHT15_TSCKL);
26218673114SLinus Walleij 	gpiod_set_value(data->sck, 1);
263251eb40fSJonathan Cameron 	ndelay(SHT15_TSCKH);
26418673114SLinus Walleij 	gpiod_set_value(data->data, 1);
265251eb40fSJonathan Cameron 	ndelay(SHT15_TSU);
26618673114SLinus Walleij 	gpiod_set_value(data->sck, 0);
267251eb40fSJonathan Cameron 	ndelay(SHT15_TSCKL);
268412e29c1SVivien Didelot 	return 0;
269251eb40fSJonathan Cameron }
27099a0378dSVivien Didelot 
271251eb40fSJonathan Cameron /**
272251eb40fSJonathan Cameron  * sht15_send_byte() - send a single byte to the device
273251eb40fSJonathan Cameron  * @data:	device state
274251eb40fSJonathan Cameron  * @byte:	value to be sent
27599a0378dSVivien Didelot  */
sht15_send_byte(struct sht15_data * data,u8 byte)276251eb40fSJonathan Cameron static void sht15_send_byte(struct sht15_data *data, u8 byte)
277251eb40fSJonathan Cameron {
278251eb40fSJonathan Cameron 	int i;
27999a0378dSVivien Didelot 
280251eb40fSJonathan Cameron 	for (i = 0; i < 8; i++) {
281251eb40fSJonathan Cameron 		sht15_send_bit(data, !!(byte & 0x80));
282251eb40fSJonathan Cameron 		byte <<= 1;
283251eb40fSJonathan Cameron 	}
284251eb40fSJonathan Cameron }
28599a0378dSVivien Didelot 
286251eb40fSJonathan Cameron /**
287251eb40fSJonathan Cameron  * sht15_wait_for_response() - checks for ack from device
288251eb40fSJonathan Cameron  * @data:	device state
28999a0378dSVivien Didelot  */
sht15_wait_for_response(struct sht15_data * data)290251eb40fSJonathan Cameron static int sht15_wait_for_response(struct sht15_data *data)
291251eb40fSJonathan Cameron {
292412e29c1SVivien Didelot 	int err;
293412e29c1SVivien Didelot 
29418673114SLinus Walleij 	err = gpiod_direction_input(data->data);
295412e29c1SVivien Didelot 	if (err)
296412e29c1SVivien Didelot 		return err;
29718673114SLinus Walleij 	gpiod_set_value(data->sck, 1);
298251eb40fSJonathan Cameron 	ndelay(SHT15_TSCKH);
29918673114SLinus Walleij 	if (gpiod_get_value(data->data)) {
30018673114SLinus Walleij 		gpiod_set_value(data->sck, 0);
301251eb40fSJonathan Cameron 		dev_err(data->dev, "Command not acknowledged\n");
302412e29c1SVivien Didelot 		err = sht15_connection_reset(data);
303412e29c1SVivien Didelot 		if (err)
304412e29c1SVivien Didelot 			return err;
305251eb40fSJonathan Cameron 		return -EIO;
306251eb40fSJonathan Cameron 	}
30718673114SLinus Walleij 	gpiod_set_value(data->sck, 0);
308251eb40fSJonathan Cameron 	ndelay(SHT15_TSCKL);
309251eb40fSJonathan Cameron 	return 0;
310251eb40fSJonathan Cameron }
311251eb40fSJonathan Cameron 
312251eb40fSJonathan Cameron /**
313251eb40fSJonathan Cameron  * sht15_send_cmd() - Sends a command to the device.
314251eb40fSJonathan Cameron  * @data:	device state
315251eb40fSJonathan Cameron  * @cmd:	command byte to be sent
316251eb40fSJonathan Cameron  *
317251eb40fSJonathan Cameron  * On entry, sck is output low, data is output pull high
318251eb40fSJonathan Cameron  * and the interrupt disabled.
31999a0378dSVivien Didelot  */
sht15_send_cmd(struct sht15_data * data,u8 cmd)320251eb40fSJonathan Cameron static int sht15_send_cmd(struct sht15_data *data, u8 cmd)
321251eb40fSJonathan Cameron {
322412e29c1SVivien Didelot 	int err;
32399a0378dSVivien Didelot 
324412e29c1SVivien Didelot 	err = sht15_transmission_start(data);
325412e29c1SVivien Didelot 	if (err)
326412e29c1SVivien Didelot 		return err;
327251eb40fSJonathan Cameron 	sht15_send_byte(data, cmd);
328412e29c1SVivien Didelot 	return sht15_wait_for_response(data);
329251eb40fSJonathan Cameron }
33099a0378dSVivien Didelot 
331251eb40fSJonathan Cameron /**
332181148aeSVivien Didelot  * sht15_soft_reset() - send a soft reset command
333181148aeSVivien Didelot  * @data:	sht15 specific data.
334181148aeSVivien Didelot  *
335181148aeSVivien Didelot  * As described in section 3.2 of the datasheet.
336181148aeSVivien Didelot  */
sht15_soft_reset(struct sht15_data * data)337181148aeSVivien Didelot static int sht15_soft_reset(struct sht15_data *data)
338181148aeSVivien Didelot {
339181148aeSVivien Didelot 	int ret;
340181148aeSVivien Didelot 
341181148aeSVivien Didelot 	ret = sht15_send_cmd(data, SHT15_SOFT_RESET);
342181148aeSVivien Didelot 	if (ret)
343181148aeSVivien Didelot 		return ret;
344181148aeSVivien Didelot 	msleep(SHT15_TSRST);
345cc15c7ebSVivien Didelot 	/* device resets default hardware status register value */
346cc15c7ebSVivien Didelot 	data->val_status = 0;
347181148aeSVivien Didelot 
348cc15c7ebSVivien Didelot 	return ret;
349cc15c7ebSVivien Didelot }
350cc15c7ebSVivien Didelot 
351cc15c7ebSVivien Didelot /**
35282c7465bSJerome Oufella  * sht15_ack() - send a ack
35382c7465bSJerome Oufella  * @data:	sht15 specific data.
35482c7465bSJerome Oufella  *
35582c7465bSJerome Oufella  * Each byte of data is acknowledged by pulling the data line
35682c7465bSJerome Oufella  * low for one clock pulse.
35782c7465bSJerome Oufella  */
sht15_ack(struct sht15_data * data)358412e29c1SVivien Didelot static int sht15_ack(struct sht15_data *data)
35982c7465bSJerome Oufella {
360412e29c1SVivien Didelot 	int err;
361412e29c1SVivien Didelot 
36218673114SLinus Walleij 	err = gpiod_direction_output(data->data, 0);
363412e29c1SVivien Didelot 	if (err)
364412e29c1SVivien Didelot 		return err;
36582c7465bSJerome Oufella 	ndelay(SHT15_TSU);
36618673114SLinus Walleij 	gpiod_set_value(data->sck, 1);
36782c7465bSJerome Oufella 	ndelay(SHT15_TSU);
36818673114SLinus Walleij 	gpiod_set_value(data->sck, 0);
36982c7465bSJerome Oufella 	ndelay(SHT15_TSU);
37018673114SLinus Walleij 	gpiod_set_value(data->data, 1);
37182c7465bSJerome Oufella 
37218673114SLinus Walleij 	return gpiod_direction_input(data->data);
37382c7465bSJerome Oufella }
37482c7465bSJerome Oufella 
37582c7465bSJerome Oufella /**
376cc15c7ebSVivien Didelot  * sht15_end_transmission() - notify device of end of transmission
377cc15c7ebSVivien Didelot  * @data:	device state.
378cc15c7ebSVivien Didelot  *
379cc15c7ebSVivien Didelot  * This is basically a NAK (single clock pulse, data high).
380cc15c7ebSVivien Didelot  */
sht15_end_transmission(struct sht15_data * data)381412e29c1SVivien Didelot static int sht15_end_transmission(struct sht15_data *data)
382cc15c7ebSVivien Didelot {
383412e29c1SVivien Didelot 	int err;
384412e29c1SVivien Didelot 
38518673114SLinus Walleij 	err = gpiod_direction_output(data->data, 1);
386412e29c1SVivien Didelot 	if (err)
387412e29c1SVivien Didelot 		return err;
388cc15c7ebSVivien Didelot 	ndelay(SHT15_TSU);
38918673114SLinus Walleij 	gpiod_set_value(data->sck, 1);
390cc15c7ebSVivien Didelot 	ndelay(SHT15_TSCKH);
39118673114SLinus Walleij 	gpiod_set_value(data->sck, 0);
392cc15c7ebSVivien Didelot 	ndelay(SHT15_TSCKL);
393412e29c1SVivien Didelot 	return 0;
394cc15c7ebSVivien Didelot }
395cc15c7ebSVivien Didelot 
396cc15c7ebSVivien Didelot /**
397cc15c7ebSVivien Didelot  * sht15_read_byte() - Read a byte back from the device
398cc15c7ebSVivien Didelot  * @data:	device state.
399cc15c7ebSVivien Didelot  */
sht15_read_byte(struct sht15_data * data)400cc15c7ebSVivien Didelot static u8 sht15_read_byte(struct sht15_data *data)
401cc15c7ebSVivien Didelot {
402cc15c7ebSVivien Didelot 	int i;
403cc15c7ebSVivien Didelot 	u8 byte = 0;
404cc15c7ebSVivien Didelot 
405cc15c7ebSVivien Didelot 	for (i = 0; i < 8; ++i) {
406cc15c7ebSVivien Didelot 		byte <<= 1;
40718673114SLinus Walleij 		gpiod_set_value(data->sck, 1);
408cc15c7ebSVivien Didelot 		ndelay(SHT15_TSCKH);
40918673114SLinus Walleij 		byte |= !!gpiod_get_value(data->data);
41018673114SLinus Walleij 		gpiod_set_value(data->sck, 0);
411cc15c7ebSVivien Didelot 		ndelay(SHT15_TSCKL);
412cc15c7ebSVivien Didelot 	}
413cc15c7ebSVivien Didelot 	return byte;
414cc15c7ebSVivien Didelot }
415cc15c7ebSVivien Didelot 
416cc15c7ebSVivien Didelot /**
417cc15c7ebSVivien Didelot  * sht15_send_status() - write the status register byte
418cc15c7ebSVivien Didelot  * @data:	sht15 specific data.
419cc15c7ebSVivien Didelot  * @status:	the byte to set the status register with.
420cc15c7ebSVivien Didelot  *
421cc15c7ebSVivien Didelot  * As described in figure 14 and table 5 of the datasheet.
422cc15c7ebSVivien Didelot  */
sht15_send_status(struct sht15_data * data,u8 status)423cc15c7ebSVivien Didelot static int sht15_send_status(struct sht15_data *data, u8 status)
424cc15c7ebSVivien Didelot {
425412e29c1SVivien Didelot 	int err;
426cc15c7ebSVivien Didelot 
427412e29c1SVivien Didelot 	err = sht15_send_cmd(data, SHT15_WRITE_STATUS);
428412e29c1SVivien Didelot 	if (err)
429412e29c1SVivien Didelot 		return err;
43018673114SLinus Walleij 	err = gpiod_direction_output(data->data, 1);
431412e29c1SVivien Didelot 	if (err)
432412e29c1SVivien Didelot 		return err;
433cc15c7ebSVivien Didelot 	ndelay(SHT15_TSU);
434cc15c7ebSVivien Didelot 	sht15_send_byte(data, status);
435412e29c1SVivien Didelot 	err = sht15_wait_for_response(data);
436412e29c1SVivien Didelot 	if (err)
437412e29c1SVivien Didelot 		return err;
438cc15c7ebSVivien Didelot 
439cc15c7ebSVivien Didelot 	data->val_status = status;
440181148aeSVivien Didelot 	return 0;
441181148aeSVivien Didelot }
442181148aeSVivien Didelot 
443181148aeSVivien Didelot /**
444cc15c7ebSVivien Didelot  * sht15_update_status() - get updated status register from device if too old
445cc15c7ebSVivien Didelot  * @data:	device instance specific data.
446cc15c7ebSVivien Didelot  *
447cc15c7ebSVivien Didelot  * As described in figure 15 and table 5 of the datasheet.
448cc15c7ebSVivien Didelot  */
sht15_update_status(struct sht15_data * data)449cc15c7ebSVivien Didelot static int sht15_update_status(struct sht15_data *data)
450cc15c7ebSVivien Didelot {
451cc15c7ebSVivien Didelot 	int ret = 0;
452cc15c7ebSVivien Didelot 	u8 status;
45382c7465bSJerome Oufella 	u8 previous_config;
45482c7465bSJerome Oufella 	u8 dev_checksum = 0;
45582c7465bSJerome Oufella 	u8 checksum_vals[2];
456cc15c7ebSVivien Didelot 	int timeout = HZ;
457cc15c7ebSVivien Didelot 
458cc15c7ebSVivien Didelot 	mutex_lock(&data->read_lock);
459cc15c7ebSVivien Didelot 	if (time_after(jiffies, data->last_status + timeout)
460cc15c7ebSVivien Didelot 			|| !data->status_valid) {
461cc15c7ebSVivien Didelot 		ret = sht15_send_cmd(data, SHT15_READ_STATUS);
462cc15c7ebSVivien Didelot 		if (ret)
463412e29c1SVivien Didelot 			goto unlock;
464cc15c7ebSVivien Didelot 		status = sht15_read_byte(data);
465cc15c7ebSVivien Didelot 
46682c7465bSJerome Oufella 		if (data->checksumming) {
46782c7465bSJerome Oufella 			sht15_ack(data);
46833836ee9Syalin wang 			dev_checksum = bitrev8(sht15_read_byte(data));
46982c7465bSJerome Oufella 			checksum_vals[0] = SHT15_READ_STATUS;
47082c7465bSJerome Oufella 			checksum_vals[1] = status;
47182c7465bSJerome Oufella 			data->checksum_ok = (sht15_crc8(data, checksum_vals, 2)
47282c7465bSJerome Oufella 					== dev_checksum);
47382c7465bSJerome Oufella 		}
47482c7465bSJerome Oufella 
475412e29c1SVivien Didelot 		ret = sht15_end_transmission(data);
476412e29c1SVivien Didelot 		if (ret)
477412e29c1SVivien Didelot 			goto unlock;
478cc15c7ebSVivien Didelot 
47982c7465bSJerome Oufella 		/*
48082c7465bSJerome Oufella 		 * Perform checksum validation on the received data.
48182c7465bSJerome Oufella 		 * Specification mentions that in case a checksum verification
48282c7465bSJerome Oufella 		 * fails, a soft reset command must be sent to the device.
48382c7465bSJerome Oufella 		 */
48482c7465bSJerome Oufella 		if (data->checksumming && !data->checksum_ok) {
48582c7465bSJerome Oufella 			previous_config = data->val_status & 0x07;
48682c7465bSJerome Oufella 			ret = sht15_soft_reset(data);
48782c7465bSJerome Oufella 			if (ret)
488412e29c1SVivien Didelot 				goto unlock;
48982c7465bSJerome Oufella 			if (previous_config) {
49082c7465bSJerome Oufella 				ret = sht15_send_status(data, previous_config);
49182c7465bSJerome Oufella 				if (ret) {
49282c7465bSJerome Oufella 					dev_err(data->dev,
49382c7465bSJerome Oufella 						"CRC validation failed, unable "
49482c7465bSJerome Oufella 						"to restore device settings\n");
495412e29c1SVivien Didelot 					goto unlock;
49682c7465bSJerome Oufella 				}
49782c7465bSJerome Oufella 			}
49882c7465bSJerome Oufella 			ret = -EAGAIN;
499412e29c1SVivien Didelot 			goto unlock;
50082c7465bSJerome Oufella 		}
50182c7465bSJerome Oufella 
502cc15c7ebSVivien Didelot 		data->val_status = status;
503cc15c7ebSVivien Didelot 		data->status_valid = true;
504cc15c7ebSVivien Didelot 		data->last_status = jiffies;
505cc15c7ebSVivien Didelot 	}
506cc15c7ebSVivien Didelot 
507412e29c1SVivien Didelot unlock:
508412e29c1SVivien Didelot 	mutex_unlock(&data->read_lock);
509cc15c7ebSVivien Didelot 	return ret;
510cc15c7ebSVivien Didelot }
511cc15c7ebSVivien Didelot 
512cc15c7ebSVivien Didelot /**
51399a0378dSVivien Didelot  * sht15_measurement() - get a new value from device
514251eb40fSJonathan Cameron  * @data:		device instance specific data
515251eb40fSJonathan Cameron  * @command:		command sent to request value
516251eb40fSJonathan Cameron  * @timeout_msecs:	timeout after which comms are assumed
517251eb40fSJonathan Cameron  *			to have failed are reset.
51899a0378dSVivien Didelot  */
sht15_measurement(struct sht15_data * data,int command,int timeout_msecs)51999a0378dSVivien Didelot static int sht15_measurement(struct sht15_data *data,
520251eb40fSJonathan Cameron 			     int command,
521251eb40fSJonathan Cameron 			     int timeout_msecs)
522251eb40fSJonathan Cameron {
523251eb40fSJonathan Cameron 	int ret;
52482c7465bSJerome Oufella 	u8 previous_config;
52599a0378dSVivien Didelot 
526251eb40fSJonathan Cameron 	ret = sht15_send_cmd(data, command);
527251eb40fSJonathan Cameron 	if (ret)
528251eb40fSJonathan Cameron 		return ret;
529251eb40fSJonathan Cameron 
53018673114SLinus Walleij 	ret = gpiod_direction_input(data->data);
531412e29c1SVivien Didelot 	if (ret)
532412e29c1SVivien Didelot 		return ret;
533251eb40fSJonathan Cameron 	atomic_set(&data->interrupt_handled, 0);
534251eb40fSJonathan Cameron 
53518673114SLinus Walleij 	enable_irq(gpiod_to_irq(data->data));
53618673114SLinus Walleij 	if (gpiod_get_value(data->data) == 0) {
53718673114SLinus Walleij 		disable_irq_nosync(gpiod_to_irq(data->data));
53825985edcSLucas De Marchi 		/* Only relevant if the interrupt hasn't occurred. */
539251eb40fSJonathan Cameron 		if (!atomic_read(&data->interrupt_handled))
540251eb40fSJonathan Cameron 			schedule_work(&data->read_work);
541251eb40fSJonathan Cameron 	}
542251eb40fSJonathan Cameron 	ret = wait_event_timeout(data->wait_queue,
54399a0378dSVivien Didelot 				 (data->state == SHT15_READING_NOTHING),
544251eb40fSJonathan Cameron 				 msecs_to_jiffies(timeout_msecs));
545412e29c1SVivien Didelot 	if (data->state != SHT15_READING_NOTHING) { /* I/O error occurred */
546412e29c1SVivien Didelot 		data->state = SHT15_READING_NOTHING;
547412e29c1SVivien Didelot 		return -EIO;
548412e29c1SVivien Didelot 	} else if (ret == 0) { /* timeout occurred */
54918673114SLinus Walleij 		disable_irq_nosync(gpiod_to_irq(data->data));
550412e29c1SVivien Didelot 		ret = sht15_connection_reset(data);
551412e29c1SVivien Didelot 		if (ret)
552412e29c1SVivien Didelot 			return ret;
553251eb40fSJonathan Cameron 		return -ETIME;
554251eb40fSJonathan Cameron 	}
55582c7465bSJerome Oufella 
55682c7465bSJerome Oufella 	/*
55782c7465bSJerome Oufella 	 *  Perform checksum validation on the received data.
55882c7465bSJerome Oufella 	 *  Specification mentions that in case a checksum verification fails,
55982c7465bSJerome Oufella 	 *  a soft reset command must be sent to the device.
56082c7465bSJerome Oufella 	 */
56182c7465bSJerome Oufella 	if (data->checksumming && !data->checksum_ok) {
56282c7465bSJerome Oufella 		previous_config = data->val_status & 0x07;
56382c7465bSJerome Oufella 		ret = sht15_soft_reset(data);
56482c7465bSJerome Oufella 		if (ret)
56582c7465bSJerome Oufella 			return ret;
56682c7465bSJerome Oufella 		if (previous_config) {
56782c7465bSJerome Oufella 			ret = sht15_send_status(data, previous_config);
56882c7465bSJerome Oufella 			if (ret) {
56982c7465bSJerome Oufella 				dev_err(data->dev,
57082c7465bSJerome Oufella 					"CRC validation failed, unable "
57182c7465bSJerome Oufella 					"to restore device settings\n");
57282c7465bSJerome Oufella 				return ret;
57382c7465bSJerome Oufella 			}
57482c7465bSJerome Oufella 		}
57582c7465bSJerome Oufella 		return -EAGAIN;
57682c7465bSJerome Oufella 	}
57782c7465bSJerome Oufella 
578251eb40fSJonathan Cameron 	return 0;
579251eb40fSJonathan Cameron }
580251eb40fSJonathan Cameron 
581251eb40fSJonathan Cameron /**
58299a0378dSVivien Didelot  * sht15_update_measurements() - get updated measures from device if too old
583251eb40fSJonathan Cameron  * @data:	device state
58499a0378dSVivien Didelot  */
sht15_update_measurements(struct sht15_data * data)58599a0378dSVivien Didelot static int sht15_update_measurements(struct sht15_data *data)
586251eb40fSJonathan Cameron {
587251eb40fSJonathan Cameron 	int ret = 0;
588251eb40fSJonathan Cameron 	int timeout = HZ;
589251eb40fSJonathan Cameron 
590251eb40fSJonathan Cameron 	mutex_lock(&data->read_lock);
59199a0378dSVivien Didelot 	if (time_after(jiffies, data->last_measurement + timeout)
59299a0378dSVivien Didelot 	    || !data->measurements_valid) {
59399a0378dSVivien Didelot 		data->state = SHT15_READING_HUMID;
59499a0378dSVivien Didelot 		ret = sht15_measurement(data, SHT15_MEASURE_RH, 160);
595251eb40fSJonathan Cameron 		if (ret)
596412e29c1SVivien Didelot 			goto unlock;
59799a0378dSVivien Didelot 		data->state = SHT15_READING_TEMP;
59899a0378dSVivien Didelot 		ret = sht15_measurement(data, SHT15_MEASURE_TEMP, 400);
599251eb40fSJonathan Cameron 		if (ret)
600412e29c1SVivien Didelot 			goto unlock;
60199a0378dSVivien Didelot 		data->measurements_valid = true;
60299a0378dSVivien Didelot 		data->last_measurement = jiffies;
603251eb40fSJonathan Cameron 	}
604251eb40fSJonathan Cameron 
605412e29c1SVivien Didelot unlock:
606412e29c1SVivien Didelot 	mutex_unlock(&data->read_lock);
607251eb40fSJonathan Cameron 	return ret;
608251eb40fSJonathan Cameron }
609251eb40fSJonathan Cameron 
610251eb40fSJonathan Cameron /**
611251eb40fSJonathan Cameron  * sht15_calc_temp() - convert the raw reading to a temperature
612251eb40fSJonathan Cameron  * @data:	device state
613251eb40fSJonathan Cameron  *
614251eb40fSJonathan Cameron  * As per section 4.3 of the data sheet.
61599a0378dSVivien Didelot  */
sht15_calc_temp(struct sht15_data * data)616251eb40fSJonathan Cameron static inline int sht15_calc_temp(struct sht15_data *data)
617251eb40fSJonathan Cameron {
618328a2c22SJerome Oufella 	int d1 = temppoints[0].d1;
619cc15c7ebSVivien Didelot 	int d2 = (data->val_status & SHT15_STATUS_LOW_RESOLUTION) ? 40 : 10;
620251eb40fSJonathan Cameron 	int i;
621251eb40fSJonathan Cameron 
622328a2c22SJerome Oufella 	for (i = ARRAY_SIZE(temppoints) - 1; i > 0; i--)
623251eb40fSJonathan Cameron 		/* Find pointer to interpolate */
624142c0901SVivien Didelot 		if (data->supply_uv > temppoints[i - 1].vdd) {
625142c0901SVivien Didelot 			d1 = (data->supply_uv - temppoints[i - 1].vdd)
626251eb40fSJonathan Cameron 				* (temppoints[i].d1 - temppoints[i - 1].d1)
627251eb40fSJonathan Cameron 				/ (temppoints[i].vdd - temppoints[i - 1].vdd)
628251eb40fSJonathan Cameron 				+ temppoints[i - 1].d1;
629251eb40fSJonathan Cameron 			break;
630251eb40fSJonathan Cameron 		}
631251eb40fSJonathan Cameron 
632cc15c7ebSVivien Didelot 	return data->val_temp * d2 + d1;
633251eb40fSJonathan Cameron }
634251eb40fSJonathan Cameron 
635251eb40fSJonathan Cameron /**
636251eb40fSJonathan Cameron  * sht15_calc_humid() - using last temperature convert raw to humid
637251eb40fSJonathan Cameron  * @data:	device state
638251eb40fSJonathan Cameron  *
639251eb40fSJonathan Cameron  * This is the temperature compensated version as per section 4.2 of
640251eb40fSJonathan Cameron  * the data sheet.
64199a0378dSVivien Didelot  *
64299a0378dSVivien Didelot  * The sensor is assumed to be V3, which is compatible with V4.
64399a0378dSVivien Didelot  * Humidity conversion coefficients are shown in table 7 of the datasheet.
64499a0378dSVivien Didelot  */
sht15_calc_humid(struct sht15_data * data)645251eb40fSJonathan Cameron static inline int sht15_calc_humid(struct sht15_data *data)
646251eb40fSJonathan Cameron {
64799a0378dSVivien Didelot 	int rh_linear; /* milli percent */
648251eb40fSJonathan Cameron 	int temp = sht15_calc_temp(data);
649cc15c7ebSVivien Didelot 	int c2, c3;
650cc15c7ebSVivien Didelot 	int t2;
651251eb40fSJonathan Cameron 	const int c1 = -4;
652cc15c7ebSVivien Didelot 
653cc15c7ebSVivien Didelot 	if (data->val_status & SHT15_STATUS_LOW_RESOLUTION) {
654cc15c7ebSVivien Didelot 		c2 = 648000; /* x 10 ^ -6 */
655cc15c7ebSVivien Didelot 		c3 = -7200;  /* x 10 ^ -7 */
656cc15c7ebSVivien Didelot 		t2 = 1280;
657cc15c7ebSVivien Didelot 	} else {
658cc15c7ebSVivien Didelot 		c2 = 40500;  /* x 10 ^ -6 */
659cc15c7ebSVivien Didelot 		c3 = -28;    /* x 10 ^ -7 */
660cc15c7ebSVivien Didelot 		t2 = 80;
661cc15c7ebSVivien Didelot 	}
662251eb40fSJonathan Cameron 
66399a0378dSVivien Didelot 	rh_linear = c1 * 1000
664251eb40fSJonathan Cameron 		+ c2 * data->val_humid / 1000
665ccd32e73SVivien Didelot 		+ (data->val_humid * data->val_humid * c3) / 10000;
666cc15c7ebSVivien Didelot 	return (temp - 25000) * (10000 + t2 * data->val_humid)
66799a0378dSVivien Didelot 		/ 1000000 + rh_linear;
668251eb40fSJonathan Cameron }
669251eb40fSJonathan Cameron 
67099a0378dSVivien Didelot /**
671*ffdb3ba0SRandy Dunlap  * sht15_status_show() - show status information in sysfs
672cc15c7ebSVivien Didelot  * @dev:	device.
673cc15c7ebSVivien Didelot  * @attr:	device attribute.
674cc15c7ebSVivien Didelot  * @buf:	sysfs buffer where information is written to.
675cc15c7ebSVivien Didelot  *
676cc15c7ebSVivien Didelot  * Will be called on read access to temp1_fault, humidity1_fault
677cc15c7ebSVivien Didelot  * and heater_enable sysfs attributes.
678cc15c7ebSVivien Didelot  * Returns number of bytes written into buffer, negative errno on error.
679cc15c7ebSVivien Didelot  */
sht15_status_show(struct device * dev,struct device_attribute * attr,char * buf)68041c9a49aSGuenter Roeck static ssize_t sht15_status_show(struct device *dev,
68141c9a49aSGuenter Roeck 				 struct device_attribute *attr, char *buf)
682cc15c7ebSVivien Didelot {
683cc15c7ebSVivien Didelot 	int ret;
684cc15c7ebSVivien Didelot 	struct sht15_data *data = dev_get_drvdata(dev);
685cc15c7ebSVivien Didelot 	u8 bit = to_sensor_dev_attr(attr)->index;
686cc15c7ebSVivien Didelot 
687cc15c7ebSVivien Didelot 	ret = sht15_update_status(data);
688cc15c7ebSVivien Didelot 
689cc15c7ebSVivien Didelot 	return ret ? ret : sprintf(buf, "%d\n", !!(data->val_status & bit));
690cc15c7ebSVivien Didelot }
691cc15c7ebSVivien Didelot 
692cc15c7ebSVivien Didelot /**
693*ffdb3ba0SRandy Dunlap  * sht15_status_store() - change heater state via sysfs
694cc15c7ebSVivien Didelot  * @dev:	device.
695cc15c7ebSVivien Didelot  * @attr:	device attribute.
696cc15c7ebSVivien Didelot  * @buf:	sysfs buffer to read the new heater state from.
697cc15c7ebSVivien Didelot  * @count:	length of the data.
698cc15c7ebSVivien Didelot  *
699e9b6e9f3SVivien Didelot  * Will be called on write access to heater_enable sysfs attribute.
700cc15c7ebSVivien Didelot  * Returns number of bytes actually decoded, negative errno on error.
701cc15c7ebSVivien Didelot  */
sht15_status_store(struct device * dev,struct device_attribute * attr,const char * buf,size_t count)70241c9a49aSGuenter Roeck static ssize_t sht15_status_store(struct device *dev,
703cc15c7ebSVivien Didelot 				  struct device_attribute *attr,
704cc15c7ebSVivien Didelot 				  const char *buf, size_t count)
705cc15c7ebSVivien Didelot {
706cc15c7ebSVivien Didelot 	int ret;
707cc15c7ebSVivien Didelot 	struct sht15_data *data = dev_get_drvdata(dev);
708cc15c7ebSVivien Didelot 	long value;
709cc15c7ebSVivien Didelot 	u8 status;
710cc15c7ebSVivien Didelot 
711179c4fdbSFrans Meulenbroeks 	if (kstrtol(buf, 10, &value))
712cc15c7ebSVivien Didelot 		return -EINVAL;
713cc15c7ebSVivien Didelot 
714cc15c7ebSVivien Didelot 	mutex_lock(&data->read_lock);
715cc15c7ebSVivien Didelot 	status = data->val_status & 0x07;
716cc15c7ebSVivien Didelot 	if (!!value)
717cc15c7ebSVivien Didelot 		status |= SHT15_STATUS_HEATER;
718cc15c7ebSVivien Didelot 	else
719cc15c7ebSVivien Didelot 		status &= ~SHT15_STATUS_HEATER;
720cc15c7ebSVivien Didelot 
721cc15c7ebSVivien Didelot 	ret = sht15_send_status(data, status);
722cc15c7ebSVivien Didelot 	mutex_unlock(&data->read_lock);
723cc15c7ebSVivien Didelot 
724cc15c7ebSVivien Didelot 	return ret ? ret : count;
725cc15c7ebSVivien Didelot }
726cc15c7ebSVivien Didelot 
727cc15c7ebSVivien Didelot /**
728*ffdb3ba0SRandy Dunlap  * sht15_temp_show() - show temperature measurement value in sysfs
72999a0378dSVivien Didelot  * @dev:	device.
73099a0378dSVivien Didelot  * @attr:	device attribute.
73199a0378dSVivien Didelot  * @buf:	sysfs buffer where measurement values are written to.
73299a0378dSVivien Didelot  *
73399a0378dSVivien Didelot  * Will be called on read access to temp1_input sysfs attribute.
73499a0378dSVivien Didelot  * Returns number of bytes written into buffer, negative errno on error.
73599a0378dSVivien Didelot  */
sht15_temp_show(struct device * dev,struct device_attribute * attr,char * buf)73641c9a49aSGuenter Roeck static ssize_t sht15_temp_show(struct device *dev,
73741c9a49aSGuenter Roeck 			       struct device_attribute *attr, char *buf)
738251eb40fSJonathan Cameron {
739251eb40fSJonathan Cameron 	int ret;
740251eb40fSJonathan Cameron 	struct sht15_data *data = dev_get_drvdata(dev);
741251eb40fSJonathan Cameron 
742251eb40fSJonathan Cameron 	/* Technically no need to read humidity as well */
74399a0378dSVivien Didelot 	ret = sht15_update_measurements(data);
744251eb40fSJonathan Cameron 
745251eb40fSJonathan Cameron 	return ret ? ret : sprintf(buf, "%d\n",
746251eb40fSJonathan Cameron 				   sht15_calc_temp(data));
747251eb40fSJonathan Cameron }
748251eb40fSJonathan Cameron 
74999a0378dSVivien Didelot /**
750*ffdb3ba0SRandy Dunlap  * sht15_humidity_show() - show humidity measurement value in sysfs
75199a0378dSVivien Didelot  * @dev:	device.
75299a0378dSVivien Didelot  * @attr:	device attribute.
75399a0378dSVivien Didelot  * @buf:	sysfs buffer where measurement values are written to.
75499a0378dSVivien Didelot  *
75599a0378dSVivien Didelot  * Will be called on read access to humidity1_input sysfs attribute.
75699a0378dSVivien Didelot  * Returns number of bytes written into buffer, negative errno on error.
75799a0378dSVivien Didelot  */
sht15_humidity_show(struct device * dev,struct device_attribute * attr,char * buf)75841c9a49aSGuenter Roeck static ssize_t sht15_humidity_show(struct device *dev,
75941c9a49aSGuenter Roeck 				   struct device_attribute *attr, char *buf)
760251eb40fSJonathan Cameron {
761251eb40fSJonathan Cameron 	int ret;
762251eb40fSJonathan Cameron 	struct sht15_data *data = dev_get_drvdata(dev);
763251eb40fSJonathan Cameron 
76499a0378dSVivien Didelot 	ret = sht15_update_measurements(data);
765251eb40fSJonathan Cameron 
766251eb40fSJonathan Cameron 	return ret ? ret : sprintf(buf, "%d\n", sht15_calc_humid(data));
76799a0378dSVivien Didelot }
76899a0378dSVivien Didelot 
name_show(struct device * dev,struct device_attribute * attr,char * buf)769af3d387fSJulia Lawall static ssize_t name_show(struct device *dev,
770251eb40fSJonathan Cameron 			 struct device_attribute *attr,
771251eb40fSJonathan Cameron 			 char *buf)
772251eb40fSJonathan Cameron {
773251eb40fSJonathan Cameron 	struct platform_device *pdev = to_platform_device(dev);
774251eb40fSJonathan Cameron 	return sprintf(buf, "%s\n", pdev->name);
775251eb40fSJonathan Cameron }
776251eb40fSJonathan Cameron 
77741c9a49aSGuenter Roeck static SENSOR_DEVICE_ATTR_RO(temp1_input, sht15_temp, 0);
77841c9a49aSGuenter Roeck static SENSOR_DEVICE_ATTR_RO(humidity1_input, sht15_humidity, 0);
77941c9a49aSGuenter Roeck static SENSOR_DEVICE_ATTR_RO(temp1_fault, sht15_status,
780cc15c7ebSVivien Didelot 			     SHT15_STATUS_LOW_BATTERY);
78141c9a49aSGuenter Roeck static SENSOR_DEVICE_ATTR_RO(humidity1_fault, sht15_status,
782cc15c7ebSVivien Didelot 			     SHT15_STATUS_LOW_BATTERY);
78341c9a49aSGuenter Roeck static SENSOR_DEVICE_ATTR_RW(heater_enable, sht15_status, SHT15_STATUS_HEATER);
784af3d387fSJulia Lawall static DEVICE_ATTR_RO(name);
785251eb40fSJonathan Cameron static struct attribute *sht15_attrs[] = {
786251eb40fSJonathan Cameron 	&sensor_dev_attr_temp1_input.dev_attr.attr,
787251eb40fSJonathan Cameron 	&sensor_dev_attr_humidity1_input.dev_attr.attr,
788cc15c7ebSVivien Didelot 	&sensor_dev_attr_temp1_fault.dev_attr.attr,
789cc15c7ebSVivien Didelot 	&sensor_dev_attr_humidity1_fault.dev_attr.attr,
790cc15c7ebSVivien Didelot 	&sensor_dev_attr_heater_enable.dev_attr.attr,
791251eb40fSJonathan Cameron 	&dev_attr_name.attr,
792251eb40fSJonathan Cameron 	NULL,
793251eb40fSJonathan Cameron };
794251eb40fSJonathan Cameron 
795251eb40fSJonathan Cameron static const struct attribute_group sht15_attr_group = {
796251eb40fSJonathan Cameron 	.attrs = sht15_attrs,
797251eb40fSJonathan Cameron };
798251eb40fSJonathan Cameron 
sht15_interrupt_fired(int irq,void * d)799251eb40fSJonathan Cameron static irqreturn_t sht15_interrupt_fired(int irq, void *d)
800251eb40fSJonathan Cameron {
801251eb40fSJonathan Cameron 	struct sht15_data *data = d;
80299a0378dSVivien Didelot 
803251eb40fSJonathan Cameron 	/* First disable the interrupt */
804251eb40fSJonathan Cameron 	disable_irq_nosync(irq);
805251eb40fSJonathan Cameron 	atomic_inc(&data->interrupt_handled);
806251eb40fSJonathan Cameron 	/* Then schedule a reading work struct */
80799a0378dSVivien Didelot 	if (data->state != SHT15_READING_NOTHING)
808251eb40fSJonathan Cameron 		schedule_work(&data->read_work);
809251eb40fSJonathan Cameron 	return IRQ_HANDLED;
810251eb40fSJonathan Cameron }
811251eb40fSJonathan Cameron 
sht15_bh_read_data(struct work_struct * work_s)812251eb40fSJonathan Cameron static void sht15_bh_read_data(struct work_struct *work_s)
813251eb40fSJonathan Cameron {
814251eb40fSJonathan Cameron 	uint16_t val = 0;
81582c7465bSJerome Oufella 	u8 dev_checksum = 0;
81682c7465bSJerome Oufella 	u8 checksum_vals[3];
817251eb40fSJonathan Cameron 	struct sht15_data *data
818251eb40fSJonathan Cameron 		= container_of(work_s, struct sht15_data,
819251eb40fSJonathan Cameron 			       read_work);
82099a0378dSVivien Didelot 
821251eb40fSJonathan Cameron 	/* Firstly, verify the line is low */
82218673114SLinus Walleij 	if (gpiod_get_value(data->data)) {
82399a0378dSVivien Didelot 		/*
82499a0378dSVivien Didelot 		 * If not, then start the interrupt again - care here as could
82599a0378dSVivien Didelot 		 * have gone low in meantime so verify it hasn't!
826251eb40fSJonathan Cameron 		 */
827251eb40fSJonathan Cameron 		atomic_set(&data->interrupt_handled, 0);
82818673114SLinus Walleij 		enable_irq(gpiod_to_irq(data->data));
829c9e1498aSFrans Meulenbroeks 		/* If still not occurred or another handler was scheduled */
83018673114SLinus Walleij 		if (gpiod_get_value(data->data)
831251eb40fSJonathan Cameron 		    || atomic_read(&data->interrupt_handled))
832251eb40fSJonathan Cameron 			return;
833251eb40fSJonathan Cameron 	}
83499a0378dSVivien Didelot 
835251eb40fSJonathan Cameron 	/* Read the data back from the device */
836cc15c7ebSVivien Didelot 	val = sht15_read_byte(data);
837cc15c7ebSVivien Didelot 	val <<= 8;
838412e29c1SVivien Didelot 	if (sht15_ack(data))
839412e29c1SVivien Didelot 		goto wakeup;
840cc15c7ebSVivien Didelot 	val |= sht15_read_byte(data);
84199a0378dSVivien Didelot 
84282c7465bSJerome Oufella 	if (data->checksumming) {
84382c7465bSJerome Oufella 		/*
84482c7465bSJerome Oufella 		 * Ask the device for a checksum and read it back.
84582c7465bSJerome Oufella 		 * Note: the device sends the checksum byte reversed.
84682c7465bSJerome Oufella 		 */
847412e29c1SVivien Didelot 		if (sht15_ack(data))
848412e29c1SVivien Didelot 			goto wakeup;
84933836ee9Syalin wang 		dev_checksum = bitrev8(sht15_read_byte(data));
85082c7465bSJerome Oufella 		checksum_vals[0] = (data->state == SHT15_READING_TEMP) ?
85182c7465bSJerome Oufella 			SHT15_MEASURE_TEMP : SHT15_MEASURE_RH;
85282c7465bSJerome Oufella 		checksum_vals[1] = (u8) (val >> 8);
85382c7465bSJerome Oufella 		checksum_vals[2] = (u8) val;
85482c7465bSJerome Oufella 		data->checksum_ok
85582c7465bSJerome Oufella 			= (sht15_crc8(data, checksum_vals, 3) == dev_checksum);
85682c7465bSJerome Oufella 	}
85782c7465bSJerome Oufella 
858251eb40fSJonathan Cameron 	/* Tell the device we are done */
859412e29c1SVivien Didelot 	if (sht15_end_transmission(data))
860412e29c1SVivien Didelot 		goto wakeup;
861251eb40fSJonathan Cameron 
86299a0378dSVivien Didelot 	switch (data->state) {
863251eb40fSJonathan Cameron 	case SHT15_READING_TEMP:
864251eb40fSJonathan Cameron 		data->val_temp = val;
865251eb40fSJonathan Cameron 		break;
866251eb40fSJonathan Cameron 	case SHT15_READING_HUMID:
867251eb40fSJonathan Cameron 		data->val_humid = val;
868251eb40fSJonathan Cameron 		break;
86999a0378dSVivien Didelot 	default:
87099a0378dSVivien Didelot 		break;
871251eb40fSJonathan Cameron 	}
872251eb40fSJonathan Cameron 
87399a0378dSVivien Didelot 	data->state = SHT15_READING_NOTHING;
874412e29c1SVivien Didelot wakeup:
875251eb40fSJonathan Cameron 	wake_up(&data->wait_queue);
876251eb40fSJonathan Cameron }
877251eb40fSJonathan Cameron 
sht15_update_voltage(struct work_struct * work_s)878251eb40fSJonathan Cameron static void sht15_update_voltage(struct work_struct *work_s)
879251eb40fSJonathan Cameron {
880251eb40fSJonathan Cameron 	struct sht15_data *data
881251eb40fSJonathan Cameron 		= container_of(work_s, struct sht15_data,
882251eb40fSJonathan Cameron 			       update_supply_work);
883142c0901SVivien Didelot 	data->supply_uv = regulator_get_voltage(data->reg);
884251eb40fSJonathan Cameron }
885251eb40fSJonathan Cameron 
886251eb40fSJonathan Cameron /**
887251eb40fSJonathan Cameron  * sht15_invalidate_voltage() - mark supply voltage invalid when notified by reg
888251eb40fSJonathan Cameron  * @nb:		associated notification structure
889251eb40fSJonathan Cameron  * @event:	voltage regulator state change event code
890251eb40fSJonathan Cameron  * @ignored:	function parameter - ignored here
891251eb40fSJonathan Cameron  *
892251eb40fSJonathan Cameron  * Note that as the notification code holds the regulator lock, we have
893251eb40fSJonathan Cameron  * to schedule an update of the supply voltage rather than getting it directly.
89499a0378dSVivien Didelot  */
sht15_invalidate_voltage(struct notifier_block * nb,unsigned long event,void * ignored)895251eb40fSJonathan Cameron static int sht15_invalidate_voltage(struct notifier_block *nb,
896251eb40fSJonathan Cameron 				    unsigned long event,
897251eb40fSJonathan Cameron 				    void *ignored)
898251eb40fSJonathan Cameron {
899251eb40fSJonathan Cameron 	struct sht15_data *data = container_of(nb, struct sht15_data, nb);
900251eb40fSJonathan Cameron 
901251eb40fSJonathan Cameron 	if (event == REGULATOR_EVENT_VOLTAGE_CHANGE)
902142c0901SVivien Didelot 		data->supply_uv_valid = false;
903251eb40fSJonathan Cameron 	schedule_work(&data->update_supply_work);
904251eb40fSJonathan Cameron 
905251eb40fSJonathan Cameron 	return NOTIFY_OK;
906251eb40fSJonathan Cameron }
907251eb40fSJonathan Cameron 
9082f1736ffSMarco Franchi #ifdef CONFIG_OF
9092f1736ffSMarco Franchi static const struct of_device_id sht15_dt_match[] = {
9102f1736ffSMarco Franchi 	{ .compatible = "sensirion,sht15" },
9112f1736ffSMarco Franchi 	{ },
9122f1736ffSMarco Franchi };
9132f1736ffSMarco Franchi MODULE_DEVICE_TABLE(of, sht15_dt_match);
9142f1736ffSMarco Franchi #endif
9152f1736ffSMarco Franchi 
sht15_probe(struct platform_device * pdev)9166c931ae1SBill Pemberton static int sht15_probe(struct platform_device *pdev)
917251eb40fSJonathan Cameron {
9186edf3c30SVivien Didelot 	int ret;
91938fe7560SGuenter Roeck 	struct sht15_data *data;
920251eb40fSJonathan Cameron 
92138fe7560SGuenter Roeck 	data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL);
92238fe7560SGuenter Roeck 	if (!data)
92338fe7560SGuenter Roeck 		return -ENOMEM;
924251eb40fSJonathan Cameron 
925251eb40fSJonathan Cameron 	INIT_WORK(&data->read_work, sht15_bh_read_data);
926251eb40fSJonathan Cameron 	INIT_WORK(&data->update_supply_work, sht15_update_voltage);
927251eb40fSJonathan Cameron 	platform_set_drvdata(pdev, data);
928251eb40fSJonathan Cameron 	mutex_init(&data->read_lock);
929251eb40fSJonathan Cameron 	data->dev = &pdev->dev;
930251eb40fSJonathan Cameron 	init_waitqueue_head(&data->wait_queue);
931251eb40fSJonathan Cameron 
93299a0378dSVivien Didelot 	/*
93399a0378dSVivien Didelot 	 * If a regulator is available,
93499a0378dSVivien Didelot 	 * query what the supply voltage actually is!
93599a0378dSVivien Didelot 	 */
9369e059bacSMark Brown 	data->reg = devm_regulator_get_optional(data->dev, "vcc");
937251eb40fSJonathan Cameron 	if (!IS_ERR(data->reg)) {
938c7a78d2cSJean Delvare 		int voltage;
939c7a78d2cSJean Delvare 
940c7a78d2cSJean Delvare 		voltage = regulator_get_voltage(data->reg);
941c7a78d2cSJean Delvare 		if (voltage)
942142c0901SVivien Didelot 			data->supply_uv = voltage;
943c7a78d2cSJean Delvare 
9443e78080fSMark Brown 		ret = regulator_enable(data->reg);
9453e78080fSMark Brown 		if (ret != 0) {
9463e78080fSMark Brown 			dev_err(&pdev->dev,
9473e78080fSMark Brown 				"failed to enable regulator: %d\n", ret);
9483e78080fSMark Brown 			return ret;
9493e78080fSMark Brown 		}
9503e78080fSMark Brown 
95199a0378dSVivien Didelot 		/*
95299a0378dSVivien Didelot 		 * Setup a notifier block to update this if another device
95399a0378dSVivien Didelot 		 * causes the voltage to change
95499a0378dSVivien Didelot 		 */
955251eb40fSJonathan Cameron 		data->nb.notifier_call = &sht15_invalidate_voltage;
956251eb40fSJonathan Cameron 		ret = regulator_register_notifier(data->reg, &data->nb);
957181148aeSVivien Didelot 		if (ret) {
958181148aeSVivien Didelot 			dev_err(&pdev->dev,
959181148aeSVivien Didelot 				"regulator notifier request failed\n");
960181148aeSVivien Didelot 			regulator_disable(data->reg);
96138fe7560SGuenter Roeck 			return ret;
962181148aeSVivien Didelot 		}
963251eb40fSJonathan Cameron 	}
96499a0378dSVivien Didelot 
965251eb40fSJonathan Cameron 	/* Try requesting the GPIOs */
96618673114SLinus Walleij 	data->sck = devm_gpiod_get(&pdev->dev, "clk", GPIOD_OUT_LOW);
96718673114SLinus Walleij 	if (IS_ERR(data->sck)) {
96818673114SLinus Walleij 		ret = PTR_ERR(data->sck);
969412e29c1SVivien Didelot 		dev_err(&pdev->dev, "clock line GPIO request failed\n");
970181148aeSVivien Didelot 		goto err_release_reg;
971251eb40fSJonathan Cameron 	}
97218673114SLinus Walleij 	data->data = devm_gpiod_get(&pdev->dev, "data", GPIOD_IN);
97318673114SLinus Walleij 	if (IS_ERR(data->data)) {
97418673114SLinus Walleij 		ret = PTR_ERR(data->data);
975412e29c1SVivien Didelot 		dev_err(&pdev->dev, "data line GPIO request failed\n");
97638fe7560SGuenter Roeck 		goto err_release_reg;
977251eb40fSJonathan Cameron 	}
978251eb40fSJonathan Cameron 
97918673114SLinus Walleij 	ret = devm_request_irq(&pdev->dev, gpiod_to_irq(data->data),
980251eb40fSJonathan Cameron 			       sht15_interrupt_fired,
981251eb40fSJonathan Cameron 			       IRQF_TRIGGER_FALLING,
982251eb40fSJonathan Cameron 			       "sht15 data",
983251eb40fSJonathan Cameron 			       data);
984251eb40fSJonathan Cameron 	if (ret) {
98599a0378dSVivien Didelot 		dev_err(&pdev->dev, "failed to get irq for data line\n");
98638fe7560SGuenter Roeck 		goto err_release_reg;
987251eb40fSJonathan Cameron 	}
98818673114SLinus Walleij 	disable_irq_nosync(gpiod_to_irq(data->data));
989412e29c1SVivien Didelot 	ret = sht15_connection_reset(data);
990412e29c1SVivien Didelot 	if (ret)
991412e29c1SVivien Didelot 		goto err_release_reg;
992181148aeSVivien Didelot 	ret = sht15_soft_reset(data);
993181148aeSVivien Didelot 	if (ret)
99438fe7560SGuenter Roeck 		goto err_release_reg;
995181148aeSVivien Didelot 
996181148aeSVivien Didelot 	ret = sysfs_create_group(&pdev->dev.kobj, &sht15_attr_group);
997181148aeSVivien Didelot 	if (ret) {
998181148aeSVivien Didelot 		dev_err(&pdev->dev, "sysfs create failed\n");
99938fe7560SGuenter Roeck 		goto err_release_reg;
1000181148aeSVivien Didelot 	}
1001251eb40fSJonathan Cameron 
1002251eb40fSJonathan Cameron 	data->hwmon_dev = hwmon_device_register(data->dev);
1003251eb40fSJonathan Cameron 	if (IS_ERR(data->hwmon_dev)) {
1004251eb40fSJonathan Cameron 		ret = PTR_ERR(data->hwmon_dev);
1005181148aeSVivien Didelot 		goto err_release_sysfs_group;
1006251eb40fSJonathan Cameron 	}
100799a0378dSVivien Didelot 
1008251eb40fSJonathan Cameron 	return 0;
1009251eb40fSJonathan Cameron 
1010181148aeSVivien Didelot err_release_sysfs_group:
1011181148aeSVivien Didelot 	sysfs_remove_group(&pdev->dev.kobj, &sht15_attr_group);
1012181148aeSVivien Didelot err_release_reg:
1013181148aeSVivien Didelot 	if (!IS_ERR(data->reg)) {
1014181148aeSVivien Didelot 		regulator_unregister_notifier(data->reg, &data->nb);
1015181148aeSVivien Didelot 		regulator_disable(data->reg);
1016181148aeSVivien Didelot 	}
1017251eb40fSJonathan Cameron 	return ret;
1018251eb40fSJonathan Cameron }
1019251eb40fSJonathan Cameron 
sht15_remove(struct platform_device * pdev)1020281dfd0bSBill Pemberton static int sht15_remove(struct platform_device *pdev)
1021251eb40fSJonathan Cameron {
1022251eb40fSJonathan Cameron 	struct sht15_data *data = platform_get_drvdata(pdev);
10237d4edcccSUwe Kleine-König 	int ret;
1024251eb40fSJonathan Cameron 
1025251eb40fSJonathan Cameron 	hwmon_device_unregister(data->hwmon_dev);
1026251eb40fSJonathan Cameron 	sysfs_remove_group(&pdev->dev.kobj, &sht15_attr_group);
10277d4edcccSUwe Kleine-König 
10287d4edcccSUwe Kleine-König 	ret = sht15_soft_reset(data);
10297d4edcccSUwe Kleine-König 	if (ret)
10307d4edcccSUwe Kleine-König 		dev_err(&pdev->dev, "Failed to reset device (%pe)\n", ERR_PTR(ret));
10317d4edcccSUwe Kleine-König 
1032251eb40fSJonathan Cameron 	if (!IS_ERR(data->reg)) {
1033251eb40fSJonathan Cameron 		regulator_unregister_notifier(data->reg, &data->nb);
1034251eb40fSJonathan Cameron 		regulator_disable(data->reg);
1035251eb40fSJonathan Cameron 	}
1036251eb40fSJonathan Cameron 
1037251eb40fSJonathan Cameron 	return 0;
1038251eb40fSJonathan Cameron }
1039251eb40fSJonathan Cameron 
10409c40723eSKrzysztof Kozlowski static const struct platform_device_id sht15_device_ids[] = {
1041edec5af7SVivien Didelot 	{ "sht10", sht10 },
1042edec5af7SVivien Didelot 	{ "sht11", sht11 },
1043edec5af7SVivien Didelot 	{ "sht15", sht15 },
1044edec5af7SVivien Didelot 	{ "sht71", sht71 },
1045edec5af7SVivien Didelot 	{ "sht75", sht75 },
1046edec5af7SVivien Didelot 	{ }
1047edec5af7SVivien Didelot };
1048edec5af7SVivien Didelot MODULE_DEVICE_TABLE(platform, sht15_device_ids);
1049edec5af7SVivien Didelot 
1050edec5af7SVivien Didelot static struct platform_driver sht15_driver = {
1051251eb40fSJonathan Cameron 	.driver = {
1052251eb40fSJonathan Cameron 		.name = "sht15",
10532f1736ffSMarco Franchi 		.of_match_table = of_match_ptr(sht15_dt_match),
1054251eb40fSJonathan Cameron 	},
1055251eb40fSJonathan Cameron 	.probe = sht15_probe,
10569e5e9b7aSBill Pemberton 	.remove = sht15_remove,
1057edec5af7SVivien Didelot 	.id_table = sht15_device_ids,
1058251eb40fSJonathan Cameron };
1059edec5af7SVivien Didelot module_platform_driver(sht15_driver);
1060251eb40fSJonathan Cameron 
1061251eb40fSJonathan Cameron MODULE_LICENSE("GPL");
1062edec5af7SVivien Didelot MODULE_DESCRIPTION("Sensirion SHT15 temperature and humidity sensor driver");
1063