xref: /openbmc/linux/drivers/hwmon/sht15.c (revision d5324e90)
1251eb40fSJonathan Cameron /*
2251eb40fSJonathan Cameron  * sht15.c - support for the SHT15 Temperature and Humidity Sensor
3251eb40fSJonathan Cameron  *
4edec5af7SVivien Didelot  * Portions Copyright (c) 2010-2012 Savoir-faire Linux Inc.
582c7465bSJerome Oufella  *          Jerome Oufella <jerome.oufella@savoirfairelinux.com>
6cc15c7ebSVivien Didelot  *          Vivien Didelot <vivien.didelot@savoirfairelinux.com>
7cc15c7ebSVivien Didelot  *
8251eb40fSJonathan Cameron  * Copyright (c) 2009 Jonathan Cameron
9251eb40fSJonathan Cameron  *
10251eb40fSJonathan Cameron  * Copyright (c) 2007 Wouter Horre
11251eb40fSJonathan Cameron  *
12251eb40fSJonathan Cameron  * This program is free software; you can redistribute it and/or modify
13251eb40fSJonathan Cameron  * it under the terms of the GNU General Public License version 2 as
14251eb40fSJonathan Cameron  * published by the Free Software Foundation.
15251eb40fSJonathan Cameron  *
1699a0378dSVivien Didelot  * For further information, see the Documentation/hwmon/sht15 file.
17251eb40fSJonathan Cameron  */
18251eb40fSJonathan Cameron 
19251eb40fSJonathan Cameron #include <linux/interrupt.h>
20251eb40fSJonathan Cameron #include <linux/irq.h>
21251eb40fSJonathan Cameron #include <linux/module.h>
22251eb40fSJonathan Cameron #include <linux/init.h>
23251eb40fSJonathan Cameron #include <linux/hwmon.h>
24251eb40fSJonathan Cameron #include <linux/hwmon-sysfs.h>
25251eb40fSJonathan Cameron #include <linux/mutex.h>
26251eb40fSJonathan Cameron #include <linux/platform_device.h>
27d43c36dcSAlexey Dobriyan #include <linux/sched.h>
28251eb40fSJonathan Cameron #include <linux/delay.h>
29251eb40fSJonathan Cameron #include <linux/jiffies.h>
30251eb40fSJonathan Cameron #include <linux/err.h>
31251eb40fSJonathan Cameron #include <linux/regulator/consumer.h>
325a0e3ad6STejun Heo #include <linux/slab.h>
3360063497SArun Sharma #include <linux/atomic.h>
3433836ee9Syalin wang #include <linux/bitrev.h>
3518673114SLinus Walleij #include <linux/gpio/consumer.h>
3618673114SLinus Walleij #include <linux/of.h>
37251eb40fSJonathan Cameron 
3899a0378dSVivien Didelot /* Commands */
3999a0378dSVivien Didelot #define SHT15_MEASURE_TEMP		0x03
4099a0378dSVivien Didelot #define SHT15_MEASURE_RH		0x05
41cc15c7ebSVivien Didelot #define SHT15_WRITE_STATUS		0x06
42cc15c7ebSVivien Didelot #define SHT15_READ_STATUS		0x07
43181148aeSVivien Didelot #define SHT15_SOFT_RESET		0x1E
44251eb40fSJonathan Cameron 
4599a0378dSVivien Didelot /* Min timings */
4699a0378dSVivien Didelot #define SHT15_TSCKL			100	/* (nsecs) clock low */
4799a0378dSVivien Didelot #define SHT15_TSCKH			100	/* (nsecs) clock high */
4899a0378dSVivien Didelot #define SHT15_TSU			150	/* (nsecs) data setup time */
49181148aeSVivien Didelot #define SHT15_TSRST			11	/* (msecs) soft reset time */
50251eb40fSJonathan Cameron 
51cc15c7ebSVivien Didelot /* Status Register Bits */
52cc15c7ebSVivien Didelot #define SHT15_STATUS_LOW_RESOLUTION	0x01
53cc15c7ebSVivien Didelot #define SHT15_STATUS_NO_OTP_RELOAD	0x02
54cc15c7ebSVivien Didelot #define SHT15_STATUS_HEATER		0x04
55cc15c7ebSVivien Didelot #define SHT15_STATUS_LOW_BATTERY	0x40
56cc15c7ebSVivien Didelot 
57edec5af7SVivien Didelot /* List of supported chips */
58edec5af7SVivien Didelot enum sht15_chips { sht10, sht11, sht15, sht71, sht75 };
59edec5af7SVivien Didelot 
6099a0378dSVivien Didelot /* Actions the driver may be doing */
6199a0378dSVivien Didelot enum sht15_state {
6299a0378dSVivien Didelot 	SHT15_READING_NOTHING,
6399a0378dSVivien Didelot 	SHT15_READING_TEMP,
6499a0378dSVivien Didelot 	SHT15_READING_HUMID
6599a0378dSVivien Didelot };
66251eb40fSJonathan Cameron 
67251eb40fSJonathan Cameron /**
6825985edcSLucas De Marchi  * struct sht15_temppair - elements of voltage dependent temp calc
69251eb40fSJonathan Cameron  * @vdd:	supply voltage in microvolts
70251eb40fSJonathan Cameron  * @d1:		see data sheet
71251eb40fSJonathan Cameron  */
72251eb40fSJonathan Cameron struct sht15_temppair {
73251eb40fSJonathan Cameron 	int vdd; /* microvolts */
74251eb40fSJonathan Cameron 	int d1;
75251eb40fSJonathan Cameron };
76251eb40fSJonathan Cameron 
7799a0378dSVivien Didelot /* Table 9 from datasheet - relates temperature calculation to supply voltage */
78251eb40fSJonathan Cameron static const struct sht15_temppair temppoints[] = {
79251eb40fSJonathan Cameron 	{ 2500000, -39400 },
80251eb40fSJonathan Cameron 	{ 3000000, -39600 },
81251eb40fSJonathan Cameron 	{ 3500000, -39700 },
82251eb40fSJonathan Cameron 	{ 4000000, -39800 },
83251eb40fSJonathan Cameron 	{ 5000000, -40100 },
84251eb40fSJonathan Cameron };
85251eb40fSJonathan Cameron 
8682c7465bSJerome Oufella /* Table from CRC datasheet, section 2.4 */
8782c7465bSJerome Oufella static const u8 sht15_crc8_table[] = {
8882c7465bSJerome Oufella 	0,	49,	98,	83,	196,	245,	166,	151,
8982c7465bSJerome Oufella 	185,	136,	219,	234,	125,	76,	31,	46,
9082c7465bSJerome Oufella 	67,	114,	33,	16,	135,	182,	229,	212,
9182c7465bSJerome Oufella 	250,	203,	152,	169,	62,	15,	92,	109,
9282c7465bSJerome Oufella 	134,	183,	228,	213,	66,	115,	32,	17,
9382c7465bSJerome Oufella 	63,	14,	93,	108,	251,	202,	153,	168,
9482c7465bSJerome Oufella 	197,	244,	167,	150,	1,	48,	99,	82,
9582c7465bSJerome Oufella 	124,	77,	30,	47,	184,	137,	218,	235,
9682c7465bSJerome Oufella 	61,	12,	95,	110,	249,	200,	155,	170,
9782c7465bSJerome Oufella 	132,	181,	230,	215,	64,	113,	34,	19,
9882c7465bSJerome Oufella 	126,	79,	28,	45,	186,	139,	216,	233,
9982c7465bSJerome Oufella 	199,	246,	165,	148,	3,	50,	97,	80,
10082c7465bSJerome Oufella 	187,	138,	217,	232,	127,	78,	29,	44,
10182c7465bSJerome Oufella 	2,	51,	96,	81,	198,	247,	164,	149,
10282c7465bSJerome Oufella 	248,	201,	154,	171,	60,	13,	94,	111,
10382c7465bSJerome Oufella 	65,	112,	35,	18,	133,	180,	231,	214,
10482c7465bSJerome Oufella 	122,	75,	24,	41,	190,	143,	220,	237,
10582c7465bSJerome Oufella 	195,	242,	161,	144,	7,	54,	101,	84,
10682c7465bSJerome Oufella 	57,	8,	91,	106,	253,	204,	159,	174,
10782c7465bSJerome Oufella 	128,	177,	226,	211,	68,	117,	38,	23,
10882c7465bSJerome Oufella 	252,	205,	158,	175,	56,	9,	90,	107,
10982c7465bSJerome Oufella 	69,	116,	39,	22,	129,	176,	227,	210,
11082c7465bSJerome Oufella 	191,	142,	221,	236,	123,	74,	25,	40,
11182c7465bSJerome Oufella 	6,	55,	100,	85,	194,	243,	160,	145,
11282c7465bSJerome Oufella 	71,	118,	37,	20,	131,	178,	225,	208,
11382c7465bSJerome Oufella 	254,	207,	156,	173,	58,	11,	88,	105,
11482c7465bSJerome Oufella 	4,	53,	102,	87,	192,	241,	162,	147,
11582c7465bSJerome Oufella 	189,	140,	223,	238,	121,	72,	27,	42,
11682c7465bSJerome Oufella 	193,	240,	163,	146,	5,	52,	103,	86,
11782c7465bSJerome Oufella 	120,	73,	26,	43,	188,	141,	222,	239,
11882c7465bSJerome Oufella 	130,	179,	224,	209,	70,	119,	36,	21,
11982c7465bSJerome Oufella 	59,	10,	89,	104,	255,	206,	157,	172
12082c7465bSJerome Oufella };
12182c7465bSJerome Oufella 
122251eb40fSJonathan Cameron /**
123251eb40fSJonathan Cameron  * struct sht15_data - device instance specific data
12418673114SLinus Walleij  * @sck:		clock GPIO line
12518673114SLinus Walleij  * @data:		data GPIO line
12699a0378dSVivien Didelot  * @read_work:		bh of interrupt handler.
12799a0378dSVivien Didelot  * @wait_queue:		wait queue for getting values from device.
12899a0378dSVivien Didelot  * @val_temp:		last temperature value read from device.
12999a0378dSVivien Didelot  * @val_humid:		last humidity value read from device.
130cc15c7ebSVivien Didelot  * @val_status:		last status register value read from device.
13182c7465bSJerome Oufella  * @checksum_ok:	last value read from the device passed CRC validation.
13282c7465bSJerome Oufella  * @checksumming:	flag used to enable the data validation with CRC.
13399a0378dSVivien Didelot  * @state:		state identifying the action the driver is doing.
13499a0378dSVivien Didelot  * @measurements_valid:	are the current stored measures valid (start condition).
135cc15c7ebSVivien Didelot  * @status_valid:	is the current stored status valid (start condition).
13699a0378dSVivien Didelot  * @last_measurement:	time of last measure.
137cc15c7ebSVivien Didelot  * @last_status:	time of last status reading.
13899a0378dSVivien Didelot  * @read_lock:		mutex to ensure only one read in progress at a time.
13999a0378dSVivien Didelot  * @dev:		associate device structure.
14099a0378dSVivien Didelot  * @hwmon_dev:		device associated with hwmon subsystem.
14199a0378dSVivien Didelot  * @reg:		associated regulator (if specified).
14299a0378dSVivien Didelot  * @nb:			notifier block to handle notifications of voltage
14399a0378dSVivien Didelot  *                      changes.
144142c0901SVivien Didelot  * @supply_uv:		local copy of supply voltage used to allow use of
14599a0378dSVivien Didelot  *                      regulator consumer if available.
146142c0901SVivien Didelot  * @supply_uv_valid:	indicates that an updated value has not yet been
14799a0378dSVivien Didelot  *			obtained from the regulator and so any calculations
148251eb40fSJonathan Cameron  *			based upon it will be invalid.
149142c0901SVivien Didelot  * @update_supply_work:	work struct that is used to update the supply_uv.
15099a0378dSVivien Didelot  * @interrupt_handled:	flag used to indicate a handler has been scheduled.
151251eb40fSJonathan Cameron  */
152251eb40fSJonathan Cameron struct sht15_data {
15318673114SLinus Walleij 	struct gpio_desc		*sck;
15418673114SLinus Walleij 	struct gpio_desc		*data;
155251eb40fSJonathan Cameron 	struct work_struct		read_work;
156251eb40fSJonathan Cameron 	wait_queue_head_t		wait_queue;
157251eb40fSJonathan Cameron 	uint16_t			val_temp;
158251eb40fSJonathan Cameron 	uint16_t			val_humid;
159cc15c7ebSVivien Didelot 	u8				val_status;
16082c7465bSJerome Oufella 	bool				checksum_ok;
16182c7465bSJerome Oufella 	bool				checksumming;
16299a0378dSVivien Didelot 	enum sht15_state		state;
16399a0378dSVivien Didelot 	bool				measurements_valid;
164cc15c7ebSVivien Didelot 	bool				status_valid;
16599a0378dSVivien Didelot 	unsigned long			last_measurement;
166cc15c7ebSVivien Didelot 	unsigned long			last_status;
167251eb40fSJonathan Cameron 	struct mutex			read_lock;
168251eb40fSJonathan Cameron 	struct device			*dev;
169251eb40fSJonathan Cameron 	struct device			*hwmon_dev;
170251eb40fSJonathan Cameron 	struct regulator		*reg;
171251eb40fSJonathan Cameron 	struct notifier_block		nb;
172142c0901SVivien Didelot 	int				supply_uv;
173142c0901SVivien Didelot 	bool				supply_uv_valid;
174251eb40fSJonathan Cameron 	struct work_struct		update_supply_work;
175251eb40fSJonathan Cameron 	atomic_t			interrupt_handled;
176251eb40fSJonathan Cameron };
177251eb40fSJonathan Cameron 
178251eb40fSJonathan Cameron /**
17982c7465bSJerome Oufella  * sht15_crc8() - compute crc8
18082c7465bSJerome Oufella  * @data:	sht15 specific data.
18182c7465bSJerome Oufella  * @value:	sht15 retrieved data.
182d5324e90SGuenter Roeck  * @len:	Length of retrieved data
18382c7465bSJerome Oufella  *
18482c7465bSJerome Oufella  * This implements section 2 of the CRC datasheet.
18582c7465bSJerome Oufella  */
18682c7465bSJerome Oufella static u8 sht15_crc8(struct sht15_data *data,
18782c7465bSJerome Oufella 		const u8 *value,
18882c7465bSJerome Oufella 		int len)
18982c7465bSJerome Oufella {
19033836ee9Syalin wang 	u8 crc = bitrev8(data->val_status & 0x0F);
19182c7465bSJerome Oufella 
19282c7465bSJerome Oufella 	while (len--) {
19382c7465bSJerome Oufella 		crc = sht15_crc8_table[*value ^ crc];
19482c7465bSJerome Oufella 		value++;
19582c7465bSJerome Oufella 	}
19682c7465bSJerome Oufella 
19782c7465bSJerome Oufella 	return crc;
19882c7465bSJerome Oufella }
19982c7465bSJerome Oufella 
20082c7465bSJerome Oufella /**
201251eb40fSJonathan Cameron  * sht15_connection_reset() - reset the comms interface
202251eb40fSJonathan Cameron  * @data:	sht15 specific data
203251eb40fSJonathan Cameron  *
204251eb40fSJonathan Cameron  * This implements section 3.4 of the data sheet
205251eb40fSJonathan Cameron  */
206412e29c1SVivien Didelot static int sht15_connection_reset(struct sht15_data *data)
207251eb40fSJonathan Cameron {
208412e29c1SVivien Didelot 	int i, err;
20999a0378dSVivien Didelot 
21018673114SLinus Walleij 	err = gpiod_direction_output(data->data, 1);
211412e29c1SVivien Didelot 	if (err)
212412e29c1SVivien Didelot 		return err;
213251eb40fSJonathan Cameron 	ndelay(SHT15_TSCKL);
21418673114SLinus Walleij 	gpiod_set_value(data->sck, 0);
215251eb40fSJonathan Cameron 	ndelay(SHT15_TSCKL);
216251eb40fSJonathan Cameron 	for (i = 0; i < 9; ++i) {
21718673114SLinus Walleij 		gpiod_set_value(data->sck, 1);
218251eb40fSJonathan Cameron 		ndelay(SHT15_TSCKH);
21918673114SLinus Walleij 		gpiod_set_value(data->sck, 0);
220251eb40fSJonathan Cameron 		ndelay(SHT15_TSCKL);
221251eb40fSJonathan Cameron 	}
222412e29c1SVivien Didelot 	return 0;
223251eb40fSJonathan Cameron }
22499a0378dSVivien Didelot 
225251eb40fSJonathan Cameron /**
226251eb40fSJonathan Cameron  * sht15_send_bit() - send an individual bit to the device
227251eb40fSJonathan Cameron  * @data:	device state data
228251eb40fSJonathan Cameron  * @val:	value of bit to be sent
22999a0378dSVivien Didelot  */
230251eb40fSJonathan Cameron static inline void sht15_send_bit(struct sht15_data *data, int val)
231251eb40fSJonathan Cameron {
23218673114SLinus Walleij 	gpiod_set_value(data->data, val);
233251eb40fSJonathan Cameron 	ndelay(SHT15_TSU);
23418673114SLinus Walleij 	gpiod_set_value(data->sck, 1);
235251eb40fSJonathan Cameron 	ndelay(SHT15_TSCKH);
23618673114SLinus Walleij 	gpiod_set_value(data->sck, 0);
237251eb40fSJonathan Cameron 	ndelay(SHT15_TSCKL); /* clock low time */
238251eb40fSJonathan Cameron }
239251eb40fSJonathan Cameron 
240251eb40fSJonathan Cameron /**
241251eb40fSJonathan Cameron  * sht15_transmission_start() - specific sequence for new transmission
242251eb40fSJonathan Cameron  * @data:	device state data
24399a0378dSVivien Didelot  *
244251eb40fSJonathan Cameron  * Timings for this are not documented on the data sheet, so very
245251eb40fSJonathan Cameron  * conservative ones used in implementation. This implements
246251eb40fSJonathan Cameron  * figure 12 on the data sheet.
24799a0378dSVivien Didelot  */
248412e29c1SVivien Didelot static int sht15_transmission_start(struct sht15_data *data)
249251eb40fSJonathan Cameron {
250412e29c1SVivien Didelot 	int err;
251412e29c1SVivien Didelot 
252251eb40fSJonathan Cameron 	/* ensure data is high and output */
25318673114SLinus Walleij 	err = gpiod_direction_output(data->data, 1);
254412e29c1SVivien Didelot 	if (err)
255412e29c1SVivien Didelot 		return err;
256251eb40fSJonathan Cameron 	ndelay(SHT15_TSU);
25718673114SLinus Walleij 	gpiod_set_value(data->sck, 0);
258251eb40fSJonathan Cameron 	ndelay(SHT15_TSCKL);
25918673114SLinus Walleij 	gpiod_set_value(data->sck, 1);
260251eb40fSJonathan Cameron 	ndelay(SHT15_TSCKH);
26118673114SLinus Walleij 	gpiod_set_value(data->data, 0);
262251eb40fSJonathan Cameron 	ndelay(SHT15_TSU);
26318673114SLinus Walleij 	gpiod_set_value(data->sck, 0);
264251eb40fSJonathan Cameron 	ndelay(SHT15_TSCKL);
26518673114SLinus Walleij 	gpiod_set_value(data->sck, 1);
266251eb40fSJonathan Cameron 	ndelay(SHT15_TSCKH);
26718673114SLinus Walleij 	gpiod_set_value(data->data, 1);
268251eb40fSJonathan Cameron 	ndelay(SHT15_TSU);
26918673114SLinus Walleij 	gpiod_set_value(data->sck, 0);
270251eb40fSJonathan Cameron 	ndelay(SHT15_TSCKL);
271412e29c1SVivien Didelot 	return 0;
272251eb40fSJonathan Cameron }
27399a0378dSVivien Didelot 
274251eb40fSJonathan Cameron /**
275251eb40fSJonathan Cameron  * sht15_send_byte() - send a single byte to the device
276251eb40fSJonathan Cameron  * @data:	device state
277251eb40fSJonathan Cameron  * @byte:	value to be sent
27899a0378dSVivien Didelot  */
279251eb40fSJonathan Cameron static void sht15_send_byte(struct sht15_data *data, u8 byte)
280251eb40fSJonathan Cameron {
281251eb40fSJonathan Cameron 	int i;
28299a0378dSVivien Didelot 
283251eb40fSJonathan Cameron 	for (i = 0; i < 8; i++) {
284251eb40fSJonathan Cameron 		sht15_send_bit(data, !!(byte & 0x80));
285251eb40fSJonathan Cameron 		byte <<= 1;
286251eb40fSJonathan Cameron 	}
287251eb40fSJonathan Cameron }
28899a0378dSVivien Didelot 
289251eb40fSJonathan Cameron /**
290251eb40fSJonathan Cameron  * sht15_wait_for_response() - checks for ack from device
291251eb40fSJonathan Cameron  * @data:	device state
29299a0378dSVivien Didelot  */
293251eb40fSJonathan Cameron static int sht15_wait_for_response(struct sht15_data *data)
294251eb40fSJonathan Cameron {
295412e29c1SVivien Didelot 	int err;
296412e29c1SVivien Didelot 
29718673114SLinus Walleij 	err = gpiod_direction_input(data->data);
298412e29c1SVivien Didelot 	if (err)
299412e29c1SVivien Didelot 		return err;
30018673114SLinus Walleij 	gpiod_set_value(data->sck, 1);
301251eb40fSJonathan Cameron 	ndelay(SHT15_TSCKH);
30218673114SLinus Walleij 	if (gpiod_get_value(data->data)) {
30318673114SLinus Walleij 		gpiod_set_value(data->sck, 0);
304251eb40fSJonathan Cameron 		dev_err(data->dev, "Command not acknowledged\n");
305412e29c1SVivien Didelot 		err = sht15_connection_reset(data);
306412e29c1SVivien Didelot 		if (err)
307412e29c1SVivien Didelot 			return err;
308251eb40fSJonathan Cameron 		return -EIO;
309251eb40fSJonathan Cameron 	}
31018673114SLinus Walleij 	gpiod_set_value(data->sck, 0);
311251eb40fSJonathan Cameron 	ndelay(SHT15_TSCKL);
312251eb40fSJonathan Cameron 	return 0;
313251eb40fSJonathan Cameron }
314251eb40fSJonathan Cameron 
315251eb40fSJonathan Cameron /**
316251eb40fSJonathan Cameron  * sht15_send_cmd() - Sends a command to the device.
317251eb40fSJonathan Cameron  * @data:	device state
318251eb40fSJonathan Cameron  * @cmd:	command byte to be sent
319251eb40fSJonathan Cameron  *
320251eb40fSJonathan Cameron  * On entry, sck is output low, data is output pull high
321251eb40fSJonathan Cameron  * and the interrupt disabled.
32299a0378dSVivien Didelot  */
323251eb40fSJonathan Cameron static int sht15_send_cmd(struct sht15_data *data, u8 cmd)
324251eb40fSJonathan Cameron {
325412e29c1SVivien Didelot 	int err;
32699a0378dSVivien Didelot 
327412e29c1SVivien Didelot 	err = sht15_transmission_start(data);
328412e29c1SVivien Didelot 	if (err)
329412e29c1SVivien Didelot 		return err;
330251eb40fSJonathan Cameron 	sht15_send_byte(data, cmd);
331412e29c1SVivien Didelot 	return sht15_wait_for_response(data);
332251eb40fSJonathan Cameron }
33399a0378dSVivien Didelot 
334251eb40fSJonathan Cameron /**
335181148aeSVivien Didelot  * sht15_soft_reset() - send a soft reset command
336181148aeSVivien Didelot  * @data:	sht15 specific data.
337181148aeSVivien Didelot  *
338181148aeSVivien Didelot  * As described in section 3.2 of the datasheet.
339181148aeSVivien Didelot  */
340181148aeSVivien Didelot static int sht15_soft_reset(struct sht15_data *data)
341181148aeSVivien Didelot {
342181148aeSVivien Didelot 	int ret;
343181148aeSVivien Didelot 
344181148aeSVivien Didelot 	ret = sht15_send_cmd(data, SHT15_SOFT_RESET);
345181148aeSVivien Didelot 	if (ret)
346181148aeSVivien Didelot 		return ret;
347181148aeSVivien Didelot 	msleep(SHT15_TSRST);
348cc15c7ebSVivien Didelot 	/* device resets default hardware status register value */
349cc15c7ebSVivien Didelot 	data->val_status = 0;
350181148aeSVivien Didelot 
351cc15c7ebSVivien Didelot 	return ret;
352cc15c7ebSVivien Didelot }
353cc15c7ebSVivien Didelot 
354cc15c7ebSVivien Didelot /**
35582c7465bSJerome Oufella  * sht15_ack() - send a ack
35682c7465bSJerome Oufella  * @data:	sht15 specific data.
35782c7465bSJerome Oufella  *
35882c7465bSJerome Oufella  * Each byte of data is acknowledged by pulling the data line
35982c7465bSJerome Oufella  * low for one clock pulse.
36082c7465bSJerome Oufella  */
361412e29c1SVivien Didelot static int sht15_ack(struct sht15_data *data)
36282c7465bSJerome Oufella {
363412e29c1SVivien Didelot 	int err;
364412e29c1SVivien Didelot 
36518673114SLinus Walleij 	err = gpiod_direction_output(data->data, 0);
366412e29c1SVivien Didelot 	if (err)
367412e29c1SVivien Didelot 		return err;
36882c7465bSJerome Oufella 	ndelay(SHT15_TSU);
36918673114SLinus Walleij 	gpiod_set_value(data->sck, 1);
37082c7465bSJerome Oufella 	ndelay(SHT15_TSU);
37118673114SLinus Walleij 	gpiod_set_value(data->sck, 0);
37282c7465bSJerome Oufella 	ndelay(SHT15_TSU);
37318673114SLinus Walleij 	gpiod_set_value(data->data, 1);
37482c7465bSJerome Oufella 
37518673114SLinus Walleij 	return gpiod_direction_input(data->data);
37682c7465bSJerome Oufella }
37782c7465bSJerome Oufella 
37882c7465bSJerome Oufella /**
379cc15c7ebSVivien Didelot  * sht15_end_transmission() - notify device of end of transmission
380cc15c7ebSVivien Didelot  * @data:	device state.
381cc15c7ebSVivien Didelot  *
382cc15c7ebSVivien Didelot  * This is basically a NAK (single clock pulse, data high).
383cc15c7ebSVivien Didelot  */
384412e29c1SVivien Didelot static int sht15_end_transmission(struct sht15_data *data)
385cc15c7ebSVivien Didelot {
386412e29c1SVivien Didelot 	int err;
387412e29c1SVivien Didelot 
38818673114SLinus Walleij 	err = gpiod_direction_output(data->data, 1);
389412e29c1SVivien Didelot 	if (err)
390412e29c1SVivien Didelot 		return err;
391cc15c7ebSVivien Didelot 	ndelay(SHT15_TSU);
39218673114SLinus Walleij 	gpiod_set_value(data->sck, 1);
393cc15c7ebSVivien Didelot 	ndelay(SHT15_TSCKH);
39418673114SLinus Walleij 	gpiod_set_value(data->sck, 0);
395cc15c7ebSVivien Didelot 	ndelay(SHT15_TSCKL);
396412e29c1SVivien Didelot 	return 0;
397cc15c7ebSVivien Didelot }
398cc15c7ebSVivien Didelot 
399cc15c7ebSVivien Didelot /**
400cc15c7ebSVivien Didelot  * sht15_read_byte() - Read a byte back from the device
401cc15c7ebSVivien Didelot  * @data:	device state.
402cc15c7ebSVivien Didelot  */
403cc15c7ebSVivien Didelot static u8 sht15_read_byte(struct sht15_data *data)
404cc15c7ebSVivien Didelot {
405cc15c7ebSVivien Didelot 	int i;
406cc15c7ebSVivien Didelot 	u8 byte = 0;
407cc15c7ebSVivien Didelot 
408cc15c7ebSVivien Didelot 	for (i = 0; i < 8; ++i) {
409cc15c7ebSVivien Didelot 		byte <<= 1;
41018673114SLinus Walleij 		gpiod_set_value(data->sck, 1);
411cc15c7ebSVivien Didelot 		ndelay(SHT15_TSCKH);
41218673114SLinus Walleij 		byte |= !!gpiod_get_value(data->data);
41318673114SLinus Walleij 		gpiod_set_value(data->sck, 0);
414cc15c7ebSVivien Didelot 		ndelay(SHT15_TSCKL);
415cc15c7ebSVivien Didelot 	}
416cc15c7ebSVivien Didelot 	return byte;
417cc15c7ebSVivien Didelot }
418cc15c7ebSVivien Didelot 
419cc15c7ebSVivien Didelot /**
420cc15c7ebSVivien Didelot  * sht15_send_status() - write the status register byte
421cc15c7ebSVivien Didelot  * @data:	sht15 specific data.
422cc15c7ebSVivien Didelot  * @status:	the byte to set the status register with.
423cc15c7ebSVivien Didelot  *
424cc15c7ebSVivien Didelot  * As described in figure 14 and table 5 of the datasheet.
425cc15c7ebSVivien Didelot  */
426cc15c7ebSVivien Didelot static int sht15_send_status(struct sht15_data *data, u8 status)
427cc15c7ebSVivien Didelot {
428412e29c1SVivien Didelot 	int err;
429cc15c7ebSVivien Didelot 
430412e29c1SVivien Didelot 	err = sht15_send_cmd(data, SHT15_WRITE_STATUS);
431412e29c1SVivien Didelot 	if (err)
432412e29c1SVivien Didelot 		return err;
43318673114SLinus Walleij 	err = gpiod_direction_output(data->data, 1);
434412e29c1SVivien Didelot 	if (err)
435412e29c1SVivien Didelot 		return err;
436cc15c7ebSVivien Didelot 	ndelay(SHT15_TSU);
437cc15c7ebSVivien Didelot 	sht15_send_byte(data, status);
438412e29c1SVivien Didelot 	err = sht15_wait_for_response(data);
439412e29c1SVivien Didelot 	if (err)
440412e29c1SVivien Didelot 		return err;
441cc15c7ebSVivien Didelot 
442cc15c7ebSVivien Didelot 	data->val_status = status;
443181148aeSVivien Didelot 	return 0;
444181148aeSVivien Didelot }
445181148aeSVivien Didelot 
446181148aeSVivien Didelot /**
447cc15c7ebSVivien Didelot  * sht15_update_status() - get updated status register from device if too old
448cc15c7ebSVivien Didelot  * @data:	device instance specific data.
449cc15c7ebSVivien Didelot  *
450cc15c7ebSVivien Didelot  * As described in figure 15 and table 5 of the datasheet.
451cc15c7ebSVivien Didelot  */
452cc15c7ebSVivien Didelot static int sht15_update_status(struct sht15_data *data)
453cc15c7ebSVivien Didelot {
454cc15c7ebSVivien Didelot 	int ret = 0;
455cc15c7ebSVivien Didelot 	u8 status;
45682c7465bSJerome Oufella 	u8 previous_config;
45782c7465bSJerome Oufella 	u8 dev_checksum = 0;
45882c7465bSJerome Oufella 	u8 checksum_vals[2];
459cc15c7ebSVivien Didelot 	int timeout = HZ;
460cc15c7ebSVivien Didelot 
461cc15c7ebSVivien Didelot 	mutex_lock(&data->read_lock);
462cc15c7ebSVivien Didelot 	if (time_after(jiffies, data->last_status + timeout)
463cc15c7ebSVivien Didelot 			|| !data->status_valid) {
464cc15c7ebSVivien Didelot 		ret = sht15_send_cmd(data, SHT15_READ_STATUS);
465cc15c7ebSVivien Didelot 		if (ret)
466412e29c1SVivien Didelot 			goto unlock;
467cc15c7ebSVivien Didelot 		status = sht15_read_byte(data);
468cc15c7ebSVivien Didelot 
46982c7465bSJerome Oufella 		if (data->checksumming) {
47082c7465bSJerome Oufella 			sht15_ack(data);
47133836ee9Syalin wang 			dev_checksum = bitrev8(sht15_read_byte(data));
47282c7465bSJerome Oufella 			checksum_vals[0] = SHT15_READ_STATUS;
47382c7465bSJerome Oufella 			checksum_vals[1] = status;
47482c7465bSJerome Oufella 			data->checksum_ok = (sht15_crc8(data, checksum_vals, 2)
47582c7465bSJerome Oufella 					== dev_checksum);
47682c7465bSJerome Oufella 		}
47782c7465bSJerome Oufella 
478412e29c1SVivien Didelot 		ret = sht15_end_transmission(data);
479412e29c1SVivien Didelot 		if (ret)
480412e29c1SVivien Didelot 			goto unlock;
481cc15c7ebSVivien Didelot 
48282c7465bSJerome Oufella 		/*
48382c7465bSJerome Oufella 		 * Perform checksum validation on the received data.
48482c7465bSJerome Oufella 		 * Specification mentions that in case a checksum verification
48582c7465bSJerome Oufella 		 * fails, a soft reset command must be sent to the device.
48682c7465bSJerome Oufella 		 */
48782c7465bSJerome Oufella 		if (data->checksumming && !data->checksum_ok) {
48882c7465bSJerome Oufella 			previous_config = data->val_status & 0x07;
48982c7465bSJerome Oufella 			ret = sht15_soft_reset(data);
49082c7465bSJerome Oufella 			if (ret)
491412e29c1SVivien Didelot 				goto unlock;
49282c7465bSJerome Oufella 			if (previous_config) {
49382c7465bSJerome Oufella 				ret = sht15_send_status(data, previous_config);
49482c7465bSJerome Oufella 				if (ret) {
49582c7465bSJerome Oufella 					dev_err(data->dev,
49682c7465bSJerome Oufella 						"CRC validation failed, unable "
49782c7465bSJerome Oufella 						"to restore device settings\n");
498412e29c1SVivien Didelot 					goto unlock;
49982c7465bSJerome Oufella 				}
50082c7465bSJerome Oufella 			}
50182c7465bSJerome Oufella 			ret = -EAGAIN;
502412e29c1SVivien Didelot 			goto unlock;
50382c7465bSJerome Oufella 		}
50482c7465bSJerome Oufella 
505cc15c7ebSVivien Didelot 		data->val_status = status;
506cc15c7ebSVivien Didelot 		data->status_valid = true;
507cc15c7ebSVivien Didelot 		data->last_status = jiffies;
508cc15c7ebSVivien Didelot 	}
509cc15c7ebSVivien Didelot 
510412e29c1SVivien Didelot unlock:
511412e29c1SVivien Didelot 	mutex_unlock(&data->read_lock);
512cc15c7ebSVivien Didelot 	return ret;
513cc15c7ebSVivien Didelot }
514cc15c7ebSVivien Didelot 
515cc15c7ebSVivien Didelot /**
51699a0378dSVivien Didelot  * sht15_measurement() - get a new value from device
517251eb40fSJonathan Cameron  * @data:		device instance specific data
518251eb40fSJonathan Cameron  * @command:		command sent to request value
519251eb40fSJonathan Cameron  * @timeout_msecs:	timeout after which comms are assumed
520251eb40fSJonathan Cameron  *			to have failed are reset.
52199a0378dSVivien Didelot  */
52299a0378dSVivien Didelot static int sht15_measurement(struct sht15_data *data,
523251eb40fSJonathan Cameron 			     int command,
524251eb40fSJonathan Cameron 			     int timeout_msecs)
525251eb40fSJonathan Cameron {
526251eb40fSJonathan Cameron 	int ret;
52782c7465bSJerome Oufella 	u8 previous_config;
52899a0378dSVivien Didelot 
529251eb40fSJonathan Cameron 	ret = sht15_send_cmd(data, command);
530251eb40fSJonathan Cameron 	if (ret)
531251eb40fSJonathan Cameron 		return ret;
532251eb40fSJonathan Cameron 
53318673114SLinus Walleij 	ret = gpiod_direction_input(data->data);
534412e29c1SVivien Didelot 	if (ret)
535412e29c1SVivien Didelot 		return ret;
536251eb40fSJonathan Cameron 	atomic_set(&data->interrupt_handled, 0);
537251eb40fSJonathan Cameron 
53818673114SLinus Walleij 	enable_irq(gpiod_to_irq(data->data));
53918673114SLinus Walleij 	if (gpiod_get_value(data->data) == 0) {
54018673114SLinus Walleij 		disable_irq_nosync(gpiod_to_irq(data->data));
54125985edcSLucas De Marchi 		/* Only relevant if the interrupt hasn't occurred. */
542251eb40fSJonathan Cameron 		if (!atomic_read(&data->interrupt_handled))
543251eb40fSJonathan Cameron 			schedule_work(&data->read_work);
544251eb40fSJonathan Cameron 	}
545251eb40fSJonathan Cameron 	ret = wait_event_timeout(data->wait_queue,
54699a0378dSVivien Didelot 				 (data->state == SHT15_READING_NOTHING),
547251eb40fSJonathan Cameron 				 msecs_to_jiffies(timeout_msecs));
548412e29c1SVivien Didelot 	if (data->state != SHT15_READING_NOTHING) { /* I/O error occurred */
549412e29c1SVivien Didelot 		data->state = SHT15_READING_NOTHING;
550412e29c1SVivien Didelot 		return -EIO;
551412e29c1SVivien Didelot 	} else if (ret == 0) { /* timeout occurred */
55218673114SLinus Walleij 		disable_irq_nosync(gpiod_to_irq(data->data));
553412e29c1SVivien Didelot 		ret = sht15_connection_reset(data);
554412e29c1SVivien Didelot 		if (ret)
555412e29c1SVivien Didelot 			return ret;
556251eb40fSJonathan Cameron 		return -ETIME;
557251eb40fSJonathan Cameron 	}
55882c7465bSJerome Oufella 
55982c7465bSJerome Oufella 	/*
56082c7465bSJerome Oufella 	 *  Perform checksum validation on the received data.
56182c7465bSJerome Oufella 	 *  Specification mentions that in case a checksum verification fails,
56282c7465bSJerome Oufella 	 *  a soft reset command must be sent to the device.
56382c7465bSJerome Oufella 	 */
56482c7465bSJerome Oufella 	if (data->checksumming && !data->checksum_ok) {
56582c7465bSJerome Oufella 		previous_config = data->val_status & 0x07;
56682c7465bSJerome Oufella 		ret = sht15_soft_reset(data);
56782c7465bSJerome Oufella 		if (ret)
56882c7465bSJerome Oufella 			return ret;
56982c7465bSJerome Oufella 		if (previous_config) {
57082c7465bSJerome Oufella 			ret = sht15_send_status(data, previous_config);
57182c7465bSJerome Oufella 			if (ret) {
57282c7465bSJerome Oufella 				dev_err(data->dev,
57382c7465bSJerome Oufella 					"CRC validation failed, unable "
57482c7465bSJerome Oufella 					"to restore device settings\n");
57582c7465bSJerome Oufella 				return ret;
57682c7465bSJerome Oufella 			}
57782c7465bSJerome Oufella 		}
57882c7465bSJerome Oufella 		return -EAGAIN;
57982c7465bSJerome Oufella 	}
58082c7465bSJerome Oufella 
581251eb40fSJonathan Cameron 	return 0;
582251eb40fSJonathan Cameron }
583251eb40fSJonathan Cameron 
584251eb40fSJonathan Cameron /**
58599a0378dSVivien Didelot  * sht15_update_measurements() - get updated measures from device if too old
586251eb40fSJonathan Cameron  * @data:	device state
58799a0378dSVivien Didelot  */
58899a0378dSVivien Didelot static int sht15_update_measurements(struct sht15_data *data)
589251eb40fSJonathan Cameron {
590251eb40fSJonathan Cameron 	int ret = 0;
591251eb40fSJonathan Cameron 	int timeout = HZ;
592251eb40fSJonathan Cameron 
593251eb40fSJonathan Cameron 	mutex_lock(&data->read_lock);
59499a0378dSVivien Didelot 	if (time_after(jiffies, data->last_measurement + timeout)
59599a0378dSVivien Didelot 	    || !data->measurements_valid) {
59699a0378dSVivien Didelot 		data->state = SHT15_READING_HUMID;
59799a0378dSVivien Didelot 		ret = sht15_measurement(data, SHT15_MEASURE_RH, 160);
598251eb40fSJonathan Cameron 		if (ret)
599412e29c1SVivien Didelot 			goto unlock;
60099a0378dSVivien Didelot 		data->state = SHT15_READING_TEMP;
60199a0378dSVivien Didelot 		ret = sht15_measurement(data, SHT15_MEASURE_TEMP, 400);
602251eb40fSJonathan Cameron 		if (ret)
603412e29c1SVivien Didelot 			goto unlock;
60499a0378dSVivien Didelot 		data->measurements_valid = true;
60599a0378dSVivien Didelot 		data->last_measurement = jiffies;
606251eb40fSJonathan Cameron 	}
607251eb40fSJonathan Cameron 
608412e29c1SVivien Didelot unlock:
609412e29c1SVivien Didelot 	mutex_unlock(&data->read_lock);
610251eb40fSJonathan Cameron 	return ret;
611251eb40fSJonathan Cameron }
612251eb40fSJonathan Cameron 
613251eb40fSJonathan Cameron /**
614251eb40fSJonathan Cameron  * sht15_calc_temp() - convert the raw reading to a temperature
615251eb40fSJonathan Cameron  * @data:	device state
616251eb40fSJonathan Cameron  *
617251eb40fSJonathan Cameron  * As per section 4.3 of the data sheet.
61899a0378dSVivien Didelot  */
619251eb40fSJonathan Cameron static inline int sht15_calc_temp(struct sht15_data *data)
620251eb40fSJonathan Cameron {
621328a2c22SJerome Oufella 	int d1 = temppoints[0].d1;
622cc15c7ebSVivien Didelot 	int d2 = (data->val_status & SHT15_STATUS_LOW_RESOLUTION) ? 40 : 10;
623251eb40fSJonathan Cameron 	int i;
624251eb40fSJonathan Cameron 
625328a2c22SJerome Oufella 	for (i = ARRAY_SIZE(temppoints) - 1; i > 0; i--)
626251eb40fSJonathan Cameron 		/* Find pointer to interpolate */
627142c0901SVivien Didelot 		if (data->supply_uv > temppoints[i - 1].vdd) {
628142c0901SVivien Didelot 			d1 = (data->supply_uv - temppoints[i - 1].vdd)
629251eb40fSJonathan Cameron 				* (temppoints[i].d1 - temppoints[i - 1].d1)
630251eb40fSJonathan Cameron 				/ (temppoints[i].vdd - temppoints[i - 1].vdd)
631251eb40fSJonathan Cameron 				+ temppoints[i - 1].d1;
632251eb40fSJonathan Cameron 			break;
633251eb40fSJonathan Cameron 		}
634251eb40fSJonathan Cameron 
635cc15c7ebSVivien Didelot 	return data->val_temp * d2 + d1;
636251eb40fSJonathan Cameron }
637251eb40fSJonathan Cameron 
638251eb40fSJonathan Cameron /**
639251eb40fSJonathan Cameron  * sht15_calc_humid() - using last temperature convert raw to humid
640251eb40fSJonathan Cameron  * @data:	device state
641251eb40fSJonathan Cameron  *
642251eb40fSJonathan Cameron  * This is the temperature compensated version as per section 4.2 of
643251eb40fSJonathan Cameron  * the data sheet.
64499a0378dSVivien Didelot  *
64599a0378dSVivien Didelot  * The sensor is assumed to be V3, which is compatible with V4.
64699a0378dSVivien Didelot  * Humidity conversion coefficients are shown in table 7 of the datasheet.
64799a0378dSVivien Didelot  */
648251eb40fSJonathan Cameron static inline int sht15_calc_humid(struct sht15_data *data)
649251eb40fSJonathan Cameron {
65099a0378dSVivien Didelot 	int rh_linear; /* milli percent */
651251eb40fSJonathan Cameron 	int temp = sht15_calc_temp(data);
652cc15c7ebSVivien Didelot 	int c2, c3;
653cc15c7ebSVivien Didelot 	int t2;
654251eb40fSJonathan Cameron 	const int c1 = -4;
655cc15c7ebSVivien Didelot 
656cc15c7ebSVivien Didelot 	if (data->val_status & SHT15_STATUS_LOW_RESOLUTION) {
657cc15c7ebSVivien Didelot 		c2 = 648000; /* x 10 ^ -6 */
658cc15c7ebSVivien Didelot 		c3 = -7200;  /* x 10 ^ -7 */
659cc15c7ebSVivien Didelot 		t2 = 1280;
660cc15c7ebSVivien Didelot 	} else {
661cc15c7ebSVivien Didelot 		c2 = 40500;  /* x 10 ^ -6 */
662cc15c7ebSVivien Didelot 		c3 = -28;    /* x 10 ^ -7 */
663cc15c7ebSVivien Didelot 		t2 = 80;
664cc15c7ebSVivien Didelot 	}
665251eb40fSJonathan Cameron 
66699a0378dSVivien Didelot 	rh_linear = c1 * 1000
667251eb40fSJonathan Cameron 		+ c2 * data->val_humid / 1000
668ccd32e73SVivien Didelot 		+ (data->val_humid * data->val_humid * c3) / 10000;
669cc15c7ebSVivien Didelot 	return (temp - 25000) * (10000 + t2 * data->val_humid)
67099a0378dSVivien Didelot 		/ 1000000 + rh_linear;
671251eb40fSJonathan Cameron }
672251eb40fSJonathan Cameron 
67399a0378dSVivien Didelot /**
674cc15c7ebSVivien Didelot  * sht15_show_status() - show status information in sysfs
675cc15c7ebSVivien Didelot  * @dev:	device.
676cc15c7ebSVivien Didelot  * @attr:	device attribute.
677cc15c7ebSVivien Didelot  * @buf:	sysfs buffer where information is written to.
678cc15c7ebSVivien Didelot  *
679cc15c7ebSVivien Didelot  * Will be called on read access to temp1_fault, humidity1_fault
680cc15c7ebSVivien Didelot  * and heater_enable sysfs attributes.
681cc15c7ebSVivien Didelot  * Returns number of bytes written into buffer, negative errno on error.
682cc15c7ebSVivien Didelot  */
683cc15c7ebSVivien Didelot static ssize_t sht15_show_status(struct device *dev,
684cc15c7ebSVivien Didelot 				 struct device_attribute *attr,
685cc15c7ebSVivien Didelot 				 char *buf)
686cc15c7ebSVivien Didelot {
687cc15c7ebSVivien Didelot 	int ret;
688cc15c7ebSVivien Didelot 	struct sht15_data *data = dev_get_drvdata(dev);
689cc15c7ebSVivien Didelot 	u8 bit = to_sensor_dev_attr(attr)->index;
690cc15c7ebSVivien Didelot 
691cc15c7ebSVivien Didelot 	ret = sht15_update_status(data);
692cc15c7ebSVivien Didelot 
693cc15c7ebSVivien Didelot 	return ret ? ret : sprintf(buf, "%d\n", !!(data->val_status & bit));
694cc15c7ebSVivien Didelot }
695cc15c7ebSVivien Didelot 
696cc15c7ebSVivien Didelot /**
697cc15c7ebSVivien Didelot  * sht15_store_heater() - change heater state via sysfs
698cc15c7ebSVivien Didelot  * @dev:	device.
699cc15c7ebSVivien Didelot  * @attr:	device attribute.
700cc15c7ebSVivien Didelot  * @buf:	sysfs buffer to read the new heater state from.
701cc15c7ebSVivien Didelot  * @count:	length of the data.
702cc15c7ebSVivien Didelot  *
703e9b6e9f3SVivien Didelot  * Will be called on write access to heater_enable sysfs attribute.
704cc15c7ebSVivien Didelot  * Returns number of bytes actually decoded, negative errno on error.
705cc15c7ebSVivien Didelot  */
706cc15c7ebSVivien Didelot static ssize_t sht15_store_heater(struct device *dev,
707cc15c7ebSVivien Didelot 				  struct device_attribute *attr,
708cc15c7ebSVivien Didelot 				  const char *buf, size_t count)
709cc15c7ebSVivien Didelot {
710cc15c7ebSVivien Didelot 	int ret;
711cc15c7ebSVivien Didelot 	struct sht15_data *data = dev_get_drvdata(dev);
712cc15c7ebSVivien Didelot 	long value;
713cc15c7ebSVivien Didelot 	u8 status;
714cc15c7ebSVivien Didelot 
715179c4fdbSFrans Meulenbroeks 	if (kstrtol(buf, 10, &value))
716cc15c7ebSVivien Didelot 		return -EINVAL;
717cc15c7ebSVivien Didelot 
718cc15c7ebSVivien Didelot 	mutex_lock(&data->read_lock);
719cc15c7ebSVivien Didelot 	status = data->val_status & 0x07;
720cc15c7ebSVivien Didelot 	if (!!value)
721cc15c7ebSVivien Didelot 		status |= SHT15_STATUS_HEATER;
722cc15c7ebSVivien Didelot 	else
723cc15c7ebSVivien Didelot 		status &= ~SHT15_STATUS_HEATER;
724cc15c7ebSVivien Didelot 
725cc15c7ebSVivien Didelot 	ret = sht15_send_status(data, status);
726cc15c7ebSVivien Didelot 	mutex_unlock(&data->read_lock);
727cc15c7ebSVivien Didelot 
728cc15c7ebSVivien Didelot 	return ret ? ret : count;
729cc15c7ebSVivien Didelot }
730cc15c7ebSVivien Didelot 
731cc15c7ebSVivien Didelot /**
73299a0378dSVivien Didelot  * sht15_show_temp() - show temperature measurement value in sysfs
73399a0378dSVivien Didelot  * @dev:	device.
73499a0378dSVivien Didelot  * @attr:	device attribute.
73599a0378dSVivien Didelot  * @buf:	sysfs buffer where measurement values are written to.
73699a0378dSVivien Didelot  *
73799a0378dSVivien Didelot  * Will be called on read access to temp1_input sysfs attribute.
73899a0378dSVivien Didelot  * Returns number of bytes written into buffer, negative errno on error.
73999a0378dSVivien Didelot  */
740251eb40fSJonathan Cameron static ssize_t sht15_show_temp(struct device *dev,
741251eb40fSJonathan Cameron 			       struct device_attribute *attr,
742251eb40fSJonathan Cameron 			       char *buf)
743251eb40fSJonathan Cameron {
744251eb40fSJonathan Cameron 	int ret;
745251eb40fSJonathan Cameron 	struct sht15_data *data = dev_get_drvdata(dev);
746251eb40fSJonathan Cameron 
747251eb40fSJonathan Cameron 	/* Technically no need to read humidity as well */
74899a0378dSVivien Didelot 	ret = sht15_update_measurements(data);
749251eb40fSJonathan Cameron 
750251eb40fSJonathan Cameron 	return ret ? ret : sprintf(buf, "%d\n",
751251eb40fSJonathan Cameron 				   sht15_calc_temp(data));
752251eb40fSJonathan Cameron }
753251eb40fSJonathan Cameron 
75499a0378dSVivien Didelot /**
75599a0378dSVivien Didelot  * sht15_show_humidity() - show humidity measurement value in sysfs
75699a0378dSVivien Didelot  * @dev:	device.
75799a0378dSVivien Didelot  * @attr:	device attribute.
75899a0378dSVivien Didelot  * @buf:	sysfs buffer where measurement values are written to.
75999a0378dSVivien Didelot  *
76099a0378dSVivien Didelot  * Will be called on read access to humidity1_input sysfs attribute.
76199a0378dSVivien Didelot  * Returns number of bytes written into buffer, negative errno on error.
76299a0378dSVivien Didelot  */
763251eb40fSJonathan Cameron static ssize_t sht15_show_humidity(struct device *dev,
764251eb40fSJonathan Cameron 				   struct device_attribute *attr,
765251eb40fSJonathan Cameron 				   char *buf)
766251eb40fSJonathan Cameron {
767251eb40fSJonathan Cameron 	int ret;
768251eb40fSJonathan Cameron 	struct sht15_data *data = dev_get_drvdata(dev);
769251eb40fSJonathan Cameron 
77099a0378dSVivien Didelot 	ret = sht15_update_measurements(data);
771251eb40fSJonathan Cameron 
772251eb40fSJonathan Cameron 	return ret ? ret : sprintf(buf, "%d\n", sht15_calc_humid(data));
77399a0378dSVivien Didelot }
77499a0378dSVivien Didelot 
775af3d387fSJulia Lawall static ssize_t name_show(struct device *dev,
776251eb40fSJonathan Cameron 			 struct device_attribute *attr,
777251eb40fSJonathan Cameron 			 char *buf)
778251eb40fSJonathan Cameron {
779251eb40fSJonathan Cameron 	struct platform_device *pdev = to_platform_device(dev);
780251eb40fSJonathan Cameron 	return sprintf(buf, "%s\n", pdev->name);
781251eb40fSJonathan Cameron }
782251eb40fSJonathan Cameron 
78399a0378dSVivien Didelot static SENSOR_DEVICE_ATTR(temp1_input, S_IRUGO,
78499a0378dSVivien Didelot 			  sht15_show_temp, NULL, 0);
78599a0378dSVivien Didelot static SENSOR_DEVICE_ATTR(humidity1_input, S_IRUGO,
78699a0378dSVivien Didelot 			  sht15_show_humidity, NULL, 0);
787cc15c7ebSVivien Didelot static SENSOR_DEVICE_ATTR(temp1_fault, S_IRUGO, sht15_show_status, NULL,
788cc15c7ebSVivien Didelot 			  SHT15_STATUS_LOW_BATTERY);
789cc15c7ebSVivien Didelot static SENSOR_DEVICE_ATTR(humidity1_fault, S_IRUGO, sht15_show_status, NULL,
790cc15c7ebSVivien Didelot 			  SHT15_STATUS_LOW_BATTERY);
791cc15c7ebSVivien Didelot static SENSOR_DEVICE_ATTR(heater_enable, S_IRUGO | S_IWUSR, sht15_show_status,
792cc15c7ebSVivien Didelot 			  sht15_store_heater, SHT15_STATUS_HEATER);
793af3d387fSJulia Lawall static DEVICE_ATTR_RO(name);
794251eb40fSJonathan Cameron static struct attribute *sht15_attrs[] = {
795251eb40fSJonathan Cameron 	&sensor_dev_attr_temp1_input.dev_attr.attr,
796251eb40fSJonathan Cameron 	&sensor_dev_attr_humidity1_input.dev_attr.attr,
797cc15c7ebSVivien Didelot 	&sensor_dev_attr_temp1_fault.dev_attr.attr,
798cc15c7ebSVivien Didelot 	&sensor_dev_attr_humidity1_fault.dev_attr.attr,
799cc15c7ebSVivien Didelot 	&sensor_dev_attr_heater_enable.dev_attr.attr,
800251eb40fSJonathan Cameron 	&dev_attr_name.attr,
801251eb40fSJonathan Cameron 	NULL,
802251eb40fSJonathan Cameron };
803251eb40fSJonathan Cameron 
804251eb40fSJonathan Cameron static const struct attribute_group sht15_attr_group = {
805251eb40fSJonathan Cameron 	.attrs = sht15_attrs,
806251eb40fSJonathan Cameron };
807251eb40fSJonathan Cameron 
808251eb40fSJonathan Cameron static irqreturn_t sht15_interrupt_fired(int irq, void *d)
809251eb40fSJonathan Cameron {
810251eb40fSJonathan Cameron 	struct sht15_data *data = d;
81199a0378dSVivien Didelot 
812251eb40fSJonathan Cameron 	/* First disable the interrupt */
813251eb40fSJonathan Cameron 	disable_irq_nosync(irq);
814251eb40fSJonathan Cameron 	atomic_inc(&data->interrupt_handled);
815251eb40fSJonathan Cameron 	/* Then schedule a reading work struct */
81699a0378dSVivien Didelot 	if (data->state != SHT15_READING_NOTHING)
817251eb40fSJonathan Cameron 		schedule_work(&data->read_work);
818251eb40fSJonathan Cameron 	return IRQ_HANDLED;
819251eb40fSJonathan Cameron }
820251eb40fSJonathan Cameron 
821251eb40fSJonathan Cameron static void sht15_bh_read_data(struct work_struct *work_s)
822251eb40fSJonathan Cameron {
823251eb40fSJonathan Cameron 	uint16_t val = 0;
82482c7465bSJerome Oufella 	u8 dev_checksum = 0;
82582c7465bSJerome Oufella 	u8 checksum_vals[3];
826251eb40fSJonathan Cameron 	struct sht15_data *data
827251eb40fSJonathan Cameron 		= container_of(work_s, struct sht15_data,
828251eb40fSJonathan Cameron 			       read_work);
82999a0378dSVivien Didelot 
830251eb40fSJonathan Cameron 	/* Firstly, verify the line is low */
83118673114SLinus Walleij 	if (gpiod_get_value(data->data)) {
83299a0378dSVivien Didelot 		/*
83399a0378dSVivien Didelot 		 * If not, then start the interrupt again - care here as could
83499a0378dSVivien Didelot 		 * have gone low in meantime so verify it hasn't!
835251eb40fSJonathan Cameron 		 */
836251eb40fSJonathan Cameron 		atomic_set(&data->interrupt_handled, 0);
83718673114SLinus Walleij 		enable_irq(gpiod_to_irq(data->data));
838c9e1498aSFrans Meulenbroeks 		/* If still not occurred or another handler was scheduled */
83918673114SLinus Walleij 		if (gpiod_get_value(data->data)
840251eb40fSJonathan Cameron 		    || atomic_read(&data->interrupt_handled))
841251eb40fSJonathan Cameron 			return;
842251eb40fSJonathan Cameron 	}
84399a0378dSVivien Didelot 
844251eb40fSJonathan Cameron 	/* Read the data back from the device */
845cc15c7ebSVivien Didelot 	val = sht15_read_byte(data);
846cc15c7ebSVivien Didelot 	val <<= 8;
847412e29c1SVivien Didelot 	if (sht15_ack(data))
848412e29c1SVivien Didelot 		goto wakeup;
849cc15c7ebSVivien Didelot 	val |= sht15_read_byte(data);
85099a0378dSVivien Didelot 
85182c7465bSJerome Oufella 	if (data->checksumming) {
85282c7465bSJerome Oufella 		/*
85382c7465bSJerome Oufella 		 * Ask the device for a checksum and read it back.
85482c7465bSJerome Oufella 		 * Note: the device sends the checksum byte reversed.
85582c7465bSJerome Oufella 		 */
856412e29c1SVivien Didelot 		if (sht15_ack(data))
857412e29c1SVivien Didelot 			goto wakeup;
85833836ee9Syalin wang 		dev_checksum = bitrev8(sht15_read_byte(data));
85982c7465bSJerome Oufella 		checksum_vals[0] = (data->state == SHT15_READING_TEMP) ?
86082c7465bSJerome Oufella 			SHT15_MEASURE_TEMP : SHT15_MEASURE_RH;
86182c7465bSJerome Oufella 		checksum_vals[1] = (u8) (val >> 8);
86282c7465bSJerome Oufella 		checksum_vals[2] = (u8) val;
86382c7465bSJerome Oufella 		data->checksum_ok
86482c7465bSJerome Oufella 			= (sht15_crc8(data, checksum_vals, 3) == dev_checksum);
86582c7465bSJerome Oufella 	}
86682c7465bSJerome Oufella 
867251eb40fSJonathan Cameron 	/* Tell the device we are done */
868412e29c1SVivien Didelot 	if (sht15_end_transmission(data))
869412e29c1SVivien Didelot 		goto wakeup;
870251eb40fSJonathan Cameron 
87199a0378dSVivien Didelot 	switch (data->state) {
872251eb40fSJonathan Cameron 	case SHT15_READING_TEMP:
873251eb40fSJonathan Cameron 		data->val_temp = val;
874251eb40fSJonathan Cameron 		break;
875251eb40fSJonathan Cameron 	case SHT15_READING_HUMID:
876251eb40fSJonathan Cameron 		data->val_humid = val;
877251eb40fSJonathan Cameron 		break;
87899a0378dSVivien Didelot 	default:
87999a0378dSVivien Didelot 		break;
880251eb40fSJonathan Cameron 	}
881251eb40fSJonathan Cameron 
88299a0378dSVivien Didelot 	data->state = SHT15_READING_NOTHING;
883412e29c1SVivien Didelot wakeup:
884251eb40fSJonathan Cameron 	wake_up(&data->wait_queue);
885251eb40fSJonathan Cameron }
886251eb40fSJonathan Cameron 
887251eb40fSJonathan Cameron static void sht15_update_voltage(struct work_struct *work_s)
888251eb40fSJonathan Cameron {
889251eb40fSJonathan Cameron 	struct sht15_data *data
890251eb40fSJonathan Cameron 		= container_of(work_s, struct sht15_data,
891251eb40fSJonathan Cameron 			       update_supply_work);
892142c0901SVivien Didelot 	data->supply_uv = regulator_get_voltage(data->reg);
893251eb40fSJonathan Cameron }
894251eb40fSJonathan Cameron 
895251eb40fSJonathan Cameron /**
896251eb40fSJonathan Cameron  * sht15_invalidate_voltage() - mark supply voltage invalid when notified by reg
897251eb40fSJonathan Cameron  * @nb:		associated notification structure
898251eb40fSJonathan Cameron  * @event:	voltage regulator state change event code
899251eb40fSJonathan Cameron  * @ignored:	function parameter - ignored here
900251eb40fSJonathan Cameron  *
901251eb40fSJonathan Cameron  * Note that as the notification code holds the regulator lock, we have
902251eb40fSJonathan Cameron  * to schedule an update of the supply voltage rather than getting it directly.
90399a0378dSVivien Didelot  */
904251eb40fSJonathan Cameron static int sht15_invalidate_voltage(struct notifier_block *nb,
905251eb40fSJonathan Cameron 				    unsigned long event,
906251eb40fSJonathan Cameron 				    void *ignored)
907251eb40fSJonathan Cameron {
908251eb40fSJonathan Cameron 	struct sht15_data *data = container_of(nb, struct sht15_data, nb);
909251eb40fSJonathan Cameron 
910251eb40fSJonathan Cameron 	if (event == REGULATOR_EVENT_VOLTAGE_CHANGE)
911142c0901SVivien Didelot 		data->supply_uv_valid = false;
912251eb40fSJonathan Cameron 	schedule_work(&data->update_supply_work);
913251eb40fSJonathan Cameron 
914251eb40fSJonathan Cameron 	return NOTIFY_OK;
915251eb40fSJonathan Cameron }
916251eb40fSJonathan Cameron 
9172f1736ffSMarco Franchi #ifdef CONFIG_OF
9182f1736ffSMarco Franchi static const struct of_device_id sht15_dt_match[] = {
9192f1736ffSMarco Franchi 	{ .compatible = "sensirion,sht15" },
9202f1736ffSMarco Franchi 	{ },
9212f1736ffSMarco Franchi };
9222f1736ffSMarco Franchi MODULE_DEVICE_TABLE(of, sht15_dt_match);
9232f1736ffSMarco Franchi #endif
9242f1736ffSMarco Franchi 
9256c931ae1SBill Pemberton static int sht15_probe(struct platform_device *pdev)
926251eb40fSJonathan Cameron {
9276edf3c30SVivien Didelot 	int ret;
92838fe7560SGuenter Roeck 	struct sht15_data *data;
929251eb40fSJonathan Cameron 
93038fe7560SGuenter Roeck 	data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL);
93138fe7560SGuenter Roeck 	if (!data)
93238fe7560SGuenter Roeck 		return -ENOMEM;
933251eb40fSJonathan Cameron 
934251eb40fSJonathan Cameron 	INIT_WORK(&data->read_work, sht15_bh_read_data);
935251eb40fSJonathan Cameron 	INIT_WORK(&data->update_supply_work, sht15_update_voltage);
936251eb40fSJonathan Cameron 	platform_set_drvdata(pdev, data);
937251eb40fSJonathan Cameron 	mutex_init(&data->read_lock);
938251eb40fSJonathan Cameron 	data->dev = &pdev->dev;
939251eb40fSJonathan Cameron 	init_waitqueue_head(&data->wait_queue);
940251eb40fSJonathan Cameron 
94199a0378dSVivien Didelot 	/*
94299a0378dSVivien Didelot 	 * If a regulator is available,
94399a0378dSVivien Didelot 	 * query what the supply voltage actually is!
94499a0378dSVivien Didelot 	 */
9459e059bacSMark Brown 	data->reg = devm_regulator_get_optional(data->dev, "vcc");
946251eb40fSJonathan Cameron 	if (!IS_ERR(data->reg)) {
947c7a78d2cSJean Delvare 		int voltage;
948c7a78d2cSJean Delvare 
949c7a78d2cSJean Delvare 		voltage = regulator_get_voltage(data->reg);
950c7a78d2cSJean Delvare 		if (voltage)
951142c0901SVivien Didelot 			data->supply_uv = voltage;
952c7a78d2cSJean Delvare 
9533e78080fSMark Brown 		ret = regulator_enable(data->reg);
9543e78080fSMark Brown 		if (ret != 0) {
9553e78080fSMark Brown 			dev_err(&pdev->dev,
9563e78080fSMark Brown 				"failed to enable regulator: %d\n", ret);
9573e78080fSMark Brown 			return ret;
9583e78080fSMark Brown 		}
9593e78080fSMark Brown 
96099a0378dSVivien Didelot 		/*
96199a0378dSVivien Didelot 		 * Setup a notifier block to update this if another device
96299a0378dSVivien Didelot 		 * causes the voltage to change
96399a0378dSVivien Didelot 		 */
964251eb40fSJonathan Cameron 		data->nb.notifier_call = &sht15_invalidate_voltage;
965251eb40fSJonathan Cameron 		ret = regulator_register_notifier(data->reg, &data->nb);
966181148aeSVivien Didelot 		if (ret) {
967181148aeSVivien Didelot 			dev_err(&pdev->dev,
968181148aeSVivien Didelot 				"regulator notifier request failed\n");
969181148aeSVivien Didelot 			regulator_disable(data->reg);
97038fe7560SGuenter Roeck 			return ret;
971181148aeSVivien Didelot 		}
972251eb40fSJonathan Cameron 	}
97399a0378dSVivien Didelot 
974251eb40fSJonathan Cameron 	/* Try requesting the GPIOs */
97518673114SLinus Walleij 	data->sck = devm_gpiod_get(&pdev->dev, "clk", GPIOD_OUT_LOW);
97618673114SLinus Walleij 	if (IS_ERR(data->sck)) {
97718673114SLinus Walleij 		ret = PTR_ERR(data->sck);
978412e29c1SVivien Didelot 		dev_err(&pdev->dev, "clock line GPIO request failed\n");
979181148aeSVivien Didelot 		goto err_release_reg;
980251eb40fSJonathan Cameron 	}
98118673114SLinus Walleij 	data->data = devm_gpiod_get(&pdev->dev, "data", GPIOD_IN);
98218673114SLinus Walleij 	if (IS_ERR(data->data)) {
98318673114SLinus Walleij 		ret = PTR_ERR(data->data);
984412e29c1SVivien Didelot 		dev_err(&pdev->dev, "data line GPIO request failed\n");
98538fe7560SGuenter Roeck 		goto err_release_reg;
986251eb40fSJonathan Cameron 	}
987251eb40fSJonathan Cameron 
98818673114SLinus Walleij 	ret = devm_request_irq(&pdev->dev, gpiod_to_irq(data->data),
989251eb40fSJonathan Cameron 			       sht15_interrupt_fired,
990251eb40fSJonathan Cameron 			       IRQF_TRIGGER_FALLING,
991251eb40fSJonathan Cameron 			       "sht15 data",
992251eb40fSJonathan Cameron 			       data);
993251eb40fSJonathan Cameron 	if (ret) {
99499a0378dSVivien Didelot 		dev_err(&pdev->dev, "failed to get irq for data line\n");
99538fe7560SGuenter Roeck 		goto err_release_reg;
996251eb40fSJonathan Cameron 	}
99718673114SLinus Walleij 	disable_irq_nosync(gpiod_to_irq(data->data));
998412e29c1SVivien Didelot 	ret = sht15_connection_reset(data);
999412e29c1SVivien Didelot 	if (ret)
1000412e29c1SVivien Didelot 		goto err_release_reg;
1001181148aeSVivien Didelot 	ret = sht15_soft_reset(data);
1002181148aeSVivien Didelot 	if (ret)
100338fe7560SGuenter Roeck 		goto err_release_reg;
1004181148aeSVivien Didelot 
1005181148aeSVivien Didelot 	ret = sysfs_create_group(&pdev->dev.kobj, &sht15_attr_group);
1006181148aeSVivien Didelot 	if (ret) {
1007181148aeSVivien Didelot 		dev_err(&pdev->dev, "sysfs create failed\n");
100838fe7560SGuenter Roeck 		goto err_release_reg;
1009181148aeSVivien Didelot 	}
1010251eb40fSJonathan Cameron 
1011251eb40fSJonathan Cameron 	data->hwmon_dev = hwmon_device_register(data->dev);
1012251eb40fSJonathan Cameron 	if (IS_ERR(data->hwmon_dev)) {
1013251eb40fSJonathan Cameron 		ret = PTR_ERR(data->hwmon_dev);
1014181148aeSVivien Didelot 		goto err_release_sysfs_group;
1015251eb40fSJonathan Cameron 	}
101699a0378dSVivien Didelot 
1017251eb40fSJonathan Cameron 	return 0;
1018251eb40fSJonathan Cameron 
1019181148aeSVivien Didelot err_release_sysfs_group:
1020181148aeSVivien Didelot 	sysfs_remove_group(&pdev->dev.kobj, &sht15_attr_group);
1021181148aeSVivien Didelot err_release_reg:
1022181148aeSVivien Didelot 	if (!IS_ERR(data->reg)) {
1023181148aeSVivien Didelot 		regulator_unregister_notifier(data->reg, &data->nb);
1024181148aeSVivien Didelot 		regulator_disable(data->reg);
1025181148aeSVivien Didelot 	}
1026251eb40fSJonathan Cameron 	return ret;
1027251eb40fSJonathan Cameron }
1028251eb40fSJonathan Cameron 
1029281dfd0bSBill Pemberton static int sht15_remove(struct platform_device *pdev)
1030251eb40fSJonathan Cameron {
1031251eb40fSJonathan Cameron 	struct sht15_data *data = platform_get_drvdata(pdev);
1032251eb40fSJonathan Cameron 
103399a0378dSVivien Didelot 	/*
103499a0378dSVivien Didelot 	 * Make sure any reads from the device are done and
103599a0378dSVivien Didelot 	 * prevent new ones beginning
103699a0378dSVivien Didelot 	 */
1037251eb40fSJonathan Cameron 	mutex_lock(&data->read_lock);
1038cc15c7ebSVivien Didelot 	if (sht15_soft_reset(data)) {
1039cc15c7ebSVivien Didelot 		mutex_unlock(&data->read_lock);
1040cc15c7ebSVivien Didelot 		return -EFAULT;
1041cc15c7ebSVivien Didelot 	}
1042251eb40fSJonathan Cameron 	hwmon_device_unregister(data->hwmon_dev);
1043251eb40fSJonathan Cameron 	sysfs_remove_group(&pdev->dev.kobj, &sht15_attr_group);
1044251eb40fSJonathan Cameron 	if (!IS_ERR(data->reg)) {
1045251eb40fSJonathan Cameron 		regulator_unregister_notifier(data->reg, &data->nb);
1046251eb40fSJonathan Cameron 		regulator_disable(data->reg);
1047251eb40fSJonathan Cameron 	}
1048251eb40fSJonathan Cameron 
1049251eb40fSJonathan Cameron 	mutex_unlock(&data->read_lock);
105099a0378dSVivien Didelot 
1051251eb40fSJonathan Cameron 	return 0;
1052251eb40fSJonathan Cameron }
1053251eb40fSJonathan Cameron 
10549c40723eSKrzysztof Kozlowski static const struct platform_device_id sht15_device_ids[] = {
1055edec5af7SVivien Didelot 	{ "sht10", sht10 },
1056edec5af7SVivien Didelot 	{ "sht11", sht11 },
1057edec5af7SVivien Didelot 	{ "sht15", sht15 },
1058edec5af7SVivien Didelot 	{ "sht71", sht71 },
1059edec5af7SVivien Didelot 	{ "sht75", sht75 },
1060edec5af7SVivien Didelot 	{ }
1061edec5af7SVivien Didelot };
1062edec5af7SVivien Didelot MODULE_DEVICE_TABLE(platform, sht15_device_ids);
1063edec5af7SVivien Didelot 
1064edec5af7SVivien Didelot static struct platform_driver sht15_driver = {
1065251eb40fSJonathan Cameron 	.driver = {
1066251eb40fSJonathan Cameron 		.name = "sht15",
10672f1736ffSMarco Franchi 		.of_match_table = of_match_ptr(sht15_dt_match),
1068251eb40fSJonathan Cameron 	},
1069251eb40fSJonathan Cameron 	.probe = sht15_probe,
10709e5e9b7aSBill Pemberton 	.remove = sht15_remove,
1071edec5af7SVivien Didelot 	.id_table = sht15_device_ids,
1072251eb40fSJonathan Cameron };
1073edec5af7SVivien Didelot module_platform_driver(sht15_driver);
1074251eb40fSJonathan Cameron 
1075251eb40fSJonathan Cameron MODULE_LICENSE("GPL");
1076edec5af7SVivien Didelot MODULE_DESCRIPTION("Sensirion SHT15 temperature and humidity sensor driver");
1077