xref: /minix3/minix/drivers/sensors/bmp085/bmp085.c (revision 7c48de6cc4c6d56f2277d378dba01dbac8a8c3b9)
1433d6423SLionel Sambuc /* Driver for the BMP085 Preassure and Temperature Sensor */
2433d6423SLionel Sambuc 
3433d6423SLionel Sambuc #include <minix/ds.h>
4433d6423SLionel Sambuc #include <minix/drivers.h>
5433d6423SLionel Sambuc #include <minix/i2c.h>
6433d6423SLionel Sambuc #include <minix/i2cdriver.h>
7433d6423SLionel Sambuc #include <minix/chardriver.h>
8433d6423SLionel Sambuc #include <minix/log.h>
9433d6423SLionel Sambuc 
10433d6423SLionel Sambuc /* Control Register for triggering a measurement */
11433d6423SLionel Sambuc #define CTRL_REG 0xf4
12433d6423SLionel Sambuc 
13433d6423SLionel Sambuc /* temperature sensor - it only has one 'mode' - conversion time 4.5 ms */
14433d6423SLionel Sambuc #define CMD_TRIG_T 0x2e
15433d6423SLionel Sambuc #define UDELAY_T (4500)
16433d6423SLionel Sambuc 
17433d6423SLionel Sambuc /* pressure sensor - ultra low power mode - conversion time 4.5 ms */
18433d6423SLionel Sambuc #define CMD_TRIG_P_ULP 0x34
19433d6423SLionel Sambuc #define MODE_ULP 0x00
20433d6423SLionel Sambuc #define UDELAY_ULP (4500)
21433d6423SLionel Sambuc 
22433d6423SLionel Sambuc /* pressure sensor - standard mode - conversion time 7.5 ms */
23433d6423SLionel Sambuc #define CMD_TRIG_P_STD 0x74
24433d6423SLionel Sambuc #define MODE_STD 0x01
25433d6423SLionel Sambuc #define UDELAY_STD (7500)
26433d6423SLionel Sambuc 
27433d6423SLionel Sambuc /* pressure sensor - high resolution mode - conversion time 13.5 ms */
28433d6423SLionel Sambuc #define CMD_TRIG_P_HR 0xb4
29433d6423SLionel Sambuc #define MODE_HR 0x02
30433d6423SLionel Sambuc #define UDELAY_HR (13500)
31433d6423SLionel Sambuc 
32433d6423SLionel Sambuc /* pressure sensor - ultra high resolution mode - conversion time 25.5 ms */
33433d6423SLionel Sambuc #define CMD_TRIG_P_UHR 0xf4
34433d6423SLionel Sambuc #define MODE_UHR 0x03
35433d6423SLionel Sambuc #define UDELAY_UHR (25500)
36433d6423SLionel Sambuc 
37433d6423SLionel Sambuc /* Values for the different modes of operation */
38433d6423SLionel Sambuc struct pressure_cmd
39433d6423SLionel Sambuc {
40433d6423SLionel Sambuc 	uint8_t cmd;
41433d6423SLionel Sambuc 	uint8_t mode;
42433d6423SLionel Sambuc 	uint16_t udelay;
43433d6423SLionel Sambuc };
44433d6423SLionel Sambuc 
45433d6423SLionel Sambuc /* Table of available modes and their parameters. */
46433d6423SLionel Sambuc static struct pressure_cmd pressure_cmds[4] = {
47433d6423SLionel Sambuc 	{CMD_TRIG_P_ULP, MODE_ULP, UDELAY_ULP},
48433d6423SLionel Sambuc 	{CMD_TRIG_P_STD, MODE_STD, UDELAY_STD},
49433d6423SLionel Sambuc 	{CMD_TRIG_P_HR, MODE_HR, UDELAY_HR},
50433d6423SLionel Sambuc 	{CMD_TRIG_P_UHR, MODE_UHR, UDELAY_UHR}
51433d6423SLionel Sambuc };
52433d6423SLionel Sambuc 
53433d6423SLionel Sambuc /* Default to standard mode.
54433d6423SLionel Sambuc  * There isn't code to configure the resolution at runtime, but it should
55433d6423SLionel Sambuc  * easy to implement by setting p_cmd to the right element of pressure_cmds.
56433d6423SLionel Sambuc  */
57433d6423SLionel Sambuc static struct pressure_cmd *p_cmd = &pressure_cmds[MODE_STD];
58433d6423SLionel Sambuc 
59433d6423SLionel Sambuc /* Chip Identification */
60433d6423SLionel Sambuc #define CHIPID_REG 0xd0
61433d6423SLionel Sambuc #define BMP085_CHIPID 0x55
62433d6423SLionel Sambuc 
63433d6423SLionel Sambuc /*
64433d6423SLionel Sambuc  * There is also a version register at 0xd1, but documentation seems to be
65433d6423SLionel Sambuc  * lacking. The sample code says high 4 bytes are AL version and low 4 are ML.
66433d6423SLionel Sambuc  */
67433d6423SLionel Sambuc 
68433d6423SLionel Sambuc /* Calibration coefficients
69433d6423SLionel Sambuc  *
70433d6423SLionel Sambuc  * These are unique to each chip and must be read when starting the driver.
71433d6423SLionel Sambuc  * Validate them by checking that none are 0x0000 nor 0xffff. Types and
72433d6423SLionel Sambuc  * names are from the datasheet.
73433d6423SLionel Sambuc  */
74433d6423SLionel Sambuc struct calibration
75433d6423SLionel Sambuc {
76433d6423SLionel Sambuc 	int16_t ac1;
77433d6423SLionel Sambuc 	int16_t ac2;
78433d6423SLionel Sambuc 	int16_t ac3;
79433d6423SLionel Sambuc 	uint16_t ac4;
80433d6423SLionel Sambuc 	uint16_t ac5;
81433d6423SLionel Sambuc 	uint16_t ac6;
82433d6423SLionel Sambuc 	int16_t b1;
83433d6423SLionel Sambuc 	int16_t b2;
84433d6423SLionel Sambuc 	int16_t mb;
85433d6423SLionel Sambuc 	int16_t mc;
86433d6423SLionel Sambuc 	int16_t md;
87433d6423SLionel Sambuc } cal;
88433d6423SLionel Sambuc 
89433d6423SLionel Sambuc /* Register locations for calibration coefficients */
90433d6423SLionel Sambuc #define AC1_MSB_REG 0xaa
91433d6423SLionel Sambuc #define AC1_LSB_REG 0xab
92433d6423SLionel Sambuc #define AC2_MSB_REG 0xac
93433d6423SLionel Sambuc #define AC2_LSB_REG 0xad
94433d6423SLionel Sambuc #define AC3_MSB_REG 0xae
95433d6423SLionel Sambuc #define AC3_LSB_REG 0xaf
96433d6423SLionel Sambuc #define AC4_MSB_REG 0xb0
97433d6423SLionel Sambuc #define AC4_LSB_REG 0xb1
98433d6423SLionel Sambuc #define AC5_MSB_REG 0xb2
99433d6423SLionel Sambuc #define AC5_LSB_REG 0xb3
100433d6423SLionel Sambuc #define AC6_MSB_REG 0xb4
101433d6423SLionel Sambuc #define AC6_LSB_REG 0xb5
102433d6423SLionel Sambuc #define B1_MSB_REG 0xb6
103433d6423SLionel Sambuc #define B1_LSB_REG 0xb7
104433d6423SLionel Sambuc #define B2_MSB_REG 0xb8
105433d6423SLionel Sambuc #define B2_LSB_REG 0xb9
106433d6423SLionel Sambuc #define MB_MSB_REG 0xba
107433d6423SLionel Sambuc #define MB_LSB_REG 0xbb
108433d6423SLionel Sambuc #define MC_MSB_REG 0xbc
109433d6423SLionel Sambuc #define MC_LSB_REG 0xbd
110433d6423SLionel Sambuc #define MD_MSB_REG 0xbe
111433d6423SLionel Sambuc #define MD_LSB_REG 0xbf
112433d6423SLionel Sambuc 
113433d6423SLionel Sambuc #define CAL_COEF_FIRST AC1_MSB_REG
114433d6423SLionel Sambuc #define CAL_COEF_LAST MD_LSB_REG
115433d6423SLionel Sambuc 
116*7c48de6cSDavid van Moolenbroek #define CAL_COEF_IS_VALID(x) (x != 0x0000 && (uint16_t)x != 0xffff)
117433d6423SLionel Sambuc 
118433d6423SLionel Sambuc #define SENSOR_VAL_MSB_REG 0xf6
119433d6423SLionel Sambuc #define SENSOR_VAL_LSB_REG 0xf7
120433d6423SLionel Sambuc #define SENSOR_VAL_XLSB_REG 0xf8
121433d6423SLionel Sambuc 
122433d6423SLionel Sambuc /* logging - use with log_warn(), log_info(), log_debug(), log_trace(), etc */
123433d6423SLionel Sambuc static struct log log = {
124433d6423SLionel Sambuc 	.name = "bmp085",
125433d6423SLionel Sambuc 	.log_level = LEVEL_INFO,
126433d6423SLionel Sambuc 	.log_func = default_log
127433d6423SLionel Sambuc };
128433d6423SLionel Sambuc 
129433d6423SLionel Sambuc /* Only one valid slave address. It isn't configurable. */
130433d6423SLionel Sambuc static i2c_addr_t valid_addrs[5] = {
131433d6423SLionel Sambuc 	0x77, 0x00
132433d6423SLionel Sambuc };
133433d6423SLionel Sambuc 
134433d6423SLionel Sambuc /* Buffer to store output string returned when reading from device file. */
135433d6423SLionel Sambuc #define BUFFER_LEN 64
136433d6423SLionel Sambuc char buffer[BUFFER_LEN + 1];
137433d6423SLionel Sambuc 
138433d6423SLionel Sambuc /* the bus that this device is on (counting starting at 1) */
139433d6423SLionel Sambuc static uint32_t bus;
140433d6423SLionel Sambuc 
141433d6423SLionel Sambuc /* slave address of the device */
142433d6423SLionel Sambuc static i2c_addr_t address;
143433d6423SLionel Sambuc 
144433d6423SLionel Sambuc /* endpoint for the driver for the bus itself. */
145433d6423SLionel Sambuc static endpoint_t bus_endpoint;
146433d6423SLionel Sambuc 
147433d6423SLionel Sambuc /* main device functions */
148433d6423SLionel Sambuc static int bmp085_init(void);
149433d6423SLionel Sambuc static int version_check(void);
150433d6423SLionel Sambuc static int read_cal_coef(void);
151433d6423SLionel Sambuc static int measure(int32_t * temperature, int32_t * pressure);
152433d6423SLionel Sambuc 
153433d6423SLionel Sambuc /* libchardriver callbacks */
154433d6423SLionel Sambuc static ssize_t bmp085_read(devminor_t minor, u64_t position, endpoint_t endpt,
155433d6423SLionel Sambuc     cp_grant_id_t grant, size_t size, int flags, cdev_id_t id);
156433d6423SLionel Sambuc static void bmp085_other(message * m, int ipc_status);
157433d6423SLionel Sambuc 
158433d6423SLionel Sambuc /* Entry points to this driver from libchardriver. */
159433d6423SLionel Sambuc static struct chardriver bmp085_tab = {
160433d6423SLionel Sambuc 	.cdr_read	= bmp085_read,
161433d6423SLionel Sambuc 	.cdr_other	= bmp085_other
162433d6423SLionel Sambuc };
163433d6423SLionel Sambuc 
164433d6423SLionel Sambuc /*
165433d6423SLionel Sambuc  * Initialize the driver. Checks the CHIPID against a known value and
166433d6423SLionel Sambuc  * reads the calibration coefficients.
167433d6423SLionel Sambuc  *
168433d6423SLionel Sambuc  * The chip does have a soft reset register (0xe0), but there
169433d6423SLionel Sambuc  * doesn't appear to be any documentation or example usage for it.
170433d6423SLionel Sambuc  */
171433d6423SLionel Sambuc static int
bmp085_init(void)172433d6423SLionel Sambuc bmp085_init(void)
173433d6423SLionel Sambuc {
174433d6423SLionel Sambuc 	int r;
175433d6423SLionel Sambuc 	int32_t t, p;
176433d6423SLionel Sambuc 
177433d6423SLionel Sambuc 	r = version_check();
178433d6423SLionel Sambuc 	if (r != OK) {
179433d6423SLionel Sambuc 		return EXIT_FAILURE;
180433d6423SLionel Sambuc 	}
181433d6423SLionel Sambuc 
182433d6423SLionel Sambuc 	r = read_cal_coef();
183433d6423SLionel Sambuc 	if (r != OK) {
184433d6423SLionel Sambuc 		return EXIT_FAILURE;
185433d6423SLionel Sambuc 	}
186433d6423SLionel Sambuc 
187433d6423SLionel Sambuc 	return OK;
188433d6423SLionel Sambuc }
189433d6423SLionel Sambuc 
190433d6423SLionel Sambuc static int
version_check(void)191433d6423SLionel Sambuc version_check(void)
192433d6423SLionel Sambuc {
193433d6423SLionel Sambuc 	int r;
194433d6423SLionel Sambuc 	uint8_t chipid;
195433d6423SLionel Sambuc 
196433d6423SLionel Sambuc 	r = i2creg_read8(bus_endpoint, address, CHIPID_REG, &chipid);
197433d6423SLionel Sambuc 	if (r != OK) {
198433d6423SLionel Sambuc 		log_warn(&log, "Couldn't read CHIPID\n");
199433d6423SLionel Sambuc 		return -1;
200433d6423SLionel Sambuc 	}
201433d6423SLionel Sambuc 
202433d6423SLionel Sambuc 	if (chipid != BMP085_CHIPID) {
203433d6423SLionel Sambuc 		log_warn(&log, "Bad CHIPID\n");
204433d6423SLionel Sambuc 		return -1;
205433d6423SLionel Sambuc 	}
206433d6423SLionel Sambuc 
207433d6423SLionel Sambuc 	log_debug(&log, "CHIPID OK\n");
208433d6423SLionel Sambuc 
209433d6423SLionel Sambuc 	return OK;
210433d6423SLionel Sambuc }
211433d6423SLionel Sambuc 
212433d6423SLionel Sambuc /*
213433d6423SLionel Sambuc  * Read the calibration data from the chip. Each individual chip has a unique
214433d6423SLionel Sambuc  * set of calibration parameters that get used to compute the true temperature
215433d6423SLionel Sambuc  * and pressure.
216433d6423SLionel Sambuc  */
217433d6423SLionel Sambuc static int
read_cal_coef(void)218433d6423SLionel Sambuc read_cal_coef(void)
219433d6423SLionel Sambuc {
220433d6423SLionel Sambuc 	int r;
221433d6423SLionel Sambuc 
222433d6423SLionel Sambuc 	/* Populate the calibration struct with values */
223433d6423SLionel Sambuc 	r = i2creg_read16(bus_endpoint, address, AC1_MSB_REG, &cal.ac1);
224433d6423SLionel Sambuc 	if (r != OK) {
225433d6423SLionel Sambuc 		return -1;
226433d6423SLionel Sambuc 	}
227433d6423SLionel Sambuc 	log_debug(&log, "cal.ac1 = %d\n", cal.ac1);
228433d6423SLionel Sambuc 
229433d6423SLionel Sambuc 	r = i2creg_read16(bus_endpoint, address, AC2_MSB_REG, &cal.ac2);
230433d6423SLionel Sambuc 	if (r != OK) {
231433d6423SLionel Sambuc 		return -1;
232433d6423SLionel Sambuc 	}
233433d6423SLionel Sambuc 	log_debug(&log, "cal.ac2 = %d\n", cal.ac2);
234433d6423SLionel Sambuc 
235433d6423SLionel Sambuc 	r = i2creg_read16(bus_endpoint, address, AC3_MSB_REG, &cal.ac3);
236433d6423SLionel Sambuc 	if (r != OK) {
237433d6423SLionel Sambuc 		return -1;
238433d6423SLionel Sambuc 	}
239433d6423SLionel Sambuc 	log_debug(&log, "cal.ac3 = %d\n", cal.ac3);
240433d6423SLionel Sambuc 
241433d6423SLionel Sambuc 	r = i2creg_read16(bus_endpoint, address, AC4_MSB_REG, &cal.ac4);
242433d6423SLionel Sambuc 	if (r != OK) {
243433d6423SLionel Sambuc 		return -1;
244433d6423SLionel Sambuc 	}
245433d6423SLionel Sambuc 	log_debug(&log, "cal.ac4 = %u\n", cal.ac4);
246433d6423SLionel Sambuc 
247433d6423SLionel Sambuc 	r = i2creg_read16(bus_endpoint, address, AC5_MSB_REG, &cal.ac5);
248433d6423SLionel Sambuc 	if (r != OK) {
249433d6423SLionel Sambuc 		return -1;
250433d6423SLionel Sambuc 	}
251433d6423SLionel Sambuc 	log_debug(&log, "cal.ac5 = %u\n", cal.ac5);
252433d6423SLionel Sambuc 
253433d6423SLionel Sambuc 	r = i2creg_read16(bus_endpoint, address, AC6_MSB_REG, &cal.ac6);
254433d6423SLionel Sambuc 	if (r != OK) {
255433d6423SLionel Sambuc 		return -1;
256433d6423SLionel Sambuc 	}
257433d6423SLionel Sambuc 	log_debug(&log, "cal.ac6 = %u\n", cal.ac6);
258433d6423SLionel Sambuc 
259433d6423SLionel Sambuc 	r = i2creg_read16(bus_endpoint, address, B1_MSB_REG, &cal.b1);
260433d6423SLionel Sambuc 	if (r != OK) {
261433d6423SLionel Sambuc 		return -1;
262433d6423SLionel Sambuc 	}
263433d6423SLionel Sambuc 	log_debug(&log, "cal.b1 = %d\n", cal.b1);
264433d6423SLionel Sambuc 
265433d6423SLionel Sambuc 	r = i2creg_read16(bus_endpoint, address, B2_MSB_REG, &cal.b2);
266433d6423SLionel Sambuc 	if (r != OK) {
267433d6423SLionel Sambuc 		return -1;
268433d6423SLionel Sambuc 	}
269433d6423SLionel Sambuc 	log_debug(&log, "cal.b2 = %d\n", cal.b2);
270433d6423SLionel Sambuc 
271433d6423SLionel Sambuc 	r = i2creg_read16(bus_endpoint, address, MB_MSB_REG, &cal.mb);
272433d6423SLionel Sambuc 	if (r != OK) {
273433d6423SLionel Sambuc 		return -1;
274433d6423SLionel Sambuc 	}
275433d6423SLionel Sambuc 	log_debug(&log, "cal.mb = %d\n", cal.mb);
276433d6423SLionel Sambuc 
277433d6423SLionel Sambuc 	r = i2creg_read16(bus_endpoint, address, MC_MSB_REG, &cal.mc);
278433d6423SLionel Sambuc 	if (r != OK) {
279433d6423SLionel Sambuc 		return -1;
280433d6423SLionel Sambuc 	}
281433d6423SLionel Sambuc 	log_debug(&log, "cal.mc = %d\n", cal.mc);
282433d6423SLionel Sambuc 
283433d6423SLionel Sambuc 	r = i2creg_read16(bus_endpoint, address, MD_MSB_REG, &cal.md);
284433d6423SLionel Sambuc 	if (r != OK) {
285433d6423SLionel Sambuc 		return -1;
286433d6423SLionel Sambuc 	}
287433d6423SLionel Sambuc 	log_debug(&log, "cal.md = %d\n", cal.md);
288433d6423SLionel Sambuc 
289433d6423SLionel Sambuc 	/* Validate. Data sheet says values should not be 0x0000 nor 0xffff */
290433d6423SLionel Sambuc 	if (!CAL_COEF_IS_VALID(cal.ac1) ||
291433d6423SLionel Sambuc 	    !CAL_COEF_IS_VALID(cal.ac2) ||
292433d6423SLionel Sambuc 	    !CAL_COEF_IS_VALID(cal.ac3) ||
293433d6423SLionel Sambuc 	    !CAL_COEF_IS_VALID(cal.ac4) ||
294433d6423SLionel Sambuc 	    !CAL_COEF_IS_VALID(cal.ac5) ||
295433d6423SLionel Sambuc 	    !CAL_COEF_IS_VALID(cal.ac6) ||
296433d6423SLionel Sambuc 	    !CAL_COEF_IS_VALID(cal.b1) ||
297433d6423SLionel Sambuc 	    !CAL_COEF_IS_VALID(cal.b2) ||
298433d6423SLionel Sambuc 	    !CAL_COEF_IS_VALID(cal.mb) ||
299433d6423SLionel Sambuc 	    !CAL_COEF_IS_VALID(cal.mc) || !CAL_COEF_IS_VALID(cal.md)) {
300433d6423SLionel Sambuc 
301433d6423SLionel Sambuc 		log_warn(&log, "Invalid calibration data found on chip.\n");
302433d6423SLionel Sambuc 		return -1;
303433d6423SLionel Sambuc 	}
304433d6423SLionel Sambuc 
305433d6423SLionel Sambuc 	log_debug(&log, "Read Cal Data OK\n");
306433d6423SLionel Sambuc 
307433d6423SLionel Sambuc 	return OK;
308433d6423SLionel Sambuc }
309433d6423SLionel Sambuc 
310433d6423SLionel Sambuc /*
311433d6423SLionel Sambuc  * Measure the uncompensated temperature and uncompensated pressure from the
312433d6423SLionel Sambuc  * chip and apply the formulas to determine the true temperature and pressure.
313433d6423SLionel Sambuc  * Note, the data sheet is light on the details when it comes to defining the
314433d6423SLionel Sambuc  * meaning of each variable, so this function has a lot of cryptic names in it.
315433d6423SLionel Sambuc  */
316433d6423SLionel Sambuc static int
measure(int32_t * temperature,int32_t * pressure)317433d6423SLionel Sambuc measure(int32_t * temperature, int32_t * pressure)
318433d6423SLionel Sambuc {
319433d6423SLionel Sambuc 	int r;
320433d6423SLionel Sambuc 
321433d6423SLionel Sambuc 	/* Types are given in the datasheet. Their long translates to 32-bits */
322433d6423SLionel Sambuc 
323433d6423SLionel Sambuc 	int16_t ut;		/* uncompensated temperature */
324433d6423SLionel Sambuc 	int32_t up;		/* uncompensated pressure */
325433d6423SLionel Sambuc 	int32_t x1;
326433d6423SLionel Sambuc 	int32_t x2;
327433d6423SLionel Sambuc 	int32_t x3;
328433d6423SLionel Sambuc 	int32_t b3;
329433d6423SLionel Sambuc 	uint32_t b4;
330433d6423SLionel Sambuc 	int32_t b5;
331433d6423SLionel Sambuc 	int32_t b6;
332433d6423SLionel Sambuc 	uint32_t b7;
333433d6423SLionel Sambuc 	int32_t t;		/* true temperature (in 0.1C) */
334433d6423SLionel Sambuc 	int32_t p;		/* true pressure (in Pa) */
335433d6423SLionel Sambuc 
336433d6423SLionel Sambuc 	log_debug(&log, "Triggering Temp Reading...\n");
337433d6423SLionel Sambuc 
338433d6423SLionel Sambuc 	/* trigger temperature reading */
339433d6423SLionel Sambuc 	r = i2creg_write8(bus_endpoint, address, CTRL_REG, CMD_TRIG_T);
340433d6423SLionel Sambuc 	if (r != OK) {
341433d6423SLionel Sambuc 		log_warn(&log, "Failed to trigger temperature reading.\n");
342433d6423SLionel Sambuc 		return -1;
343433d6423SLionel Sambuc 	}
344433d6423SLionel Sambuc 
345433d6423SLionel Sambuc 	/* wait for sampling to be completed. */
346433d6423SLionel Sambuc 	micro_delay(UDELAY_T);
347433d6423SLionel Sambuc 
348433d6423SLionel Sambuc 	/* read the uncompensated temperature */
349433d6423SLionel Sambuc 	r = i2creg_read16(bus_endpoint, address, SENSOR_VAL_MSB_REG, &ut);
350433d6423SLionel Sambuc 	if (r != OK) {
351433d6423SLionel Sambuc 		log_warn(&log, "Failed to read temperature.\n");
352433d6423SLionel Sambuc 		return -1;
353433d6423SLionel Sambuc 	}
354433d6423SLionel Sambuc 
355433d6423SLionel Sambuc 	log_debug(&log, "ut = %d\n", ut);
356433d6423SLionel Sambuc 
357433d6423SLionel Sambuc 	log_debug(&log, "Triggering Pressure Reading...\n");
358433d6423SLionel Sambuc 
359433d6423SLionel Sambuc 	/* trigger pressure reading */
360433d6423SLionel Sambuc 	r = i2creg_write8(bus_endpoint, address, CTRL_REG, p_cmd->cmd);
361433d6423SLionel Sambuc 	if (r != OK) {
362433d6423SLionel Sambuc 		log_warn(&log, "Failed to trigger pressure reading.\n");
363433d6423SLionel Sambuc 		return -1;
364433d6423SLionel Sambuc 	}
365433d6423SLionel Sambuc 
366433d6423SLionel Sambuc 	/* wait for sampling to be completed. */
367433d6423SLionel Sambuc 	micro_delay(p_cmd->udelay);
368433d6423SLionel Sambuc 
369433d6423SLionel Sambuc 	/* read the uncompensated pressure */
370433d6423SLionel Sambuc 	r = i2creg_read24(bus_endpoint, address, SENSOR_VAL_MSB_REG, &up);
371433d6423SLionel Sambuc 	if (r != OK) {
372433d6423SLionel Sambuc 		log_warn(&log, "Failed to read pressure.\n");
373433d6423SLionel Sambuc 		return -1;
374433d6423SLionel Sambuc 	}
375433d6423SLionel Sambuc 
376433d6423SLionel Sambuc 	/* shift by 8 - oversampling setting */
377433d6423SLionel Sambuc 	up = (up >> (8 - p_cmd->mode));
378433d6423SLionel Sambuc 
379433d6423SLionel Sambuc 	log_debug(&log, "up = %d\n", up);
380433d6423SLionel Sambuc 
381433d6423SLionel Sambuc 	/* convert uncompensated temperature to true temperature */
382433d6423SLionel Sambuc 	x1 = ((ut - cal.ac6) * cal.ac5) / (1 << 15);
383433d6423SLionel Sambuc 	x2 = (cal.mc * (1 << 11)) / (x1 + cal.md);
384433d6423SLionel Sambuc 	b5 = x1 + x2;
385433d6423SLionel Sambuc 	t = (b5 + 8) / (1 << 4);
386433d6423SLionel Sambuc 
387433d6423SLionel Sambuc 	/* save the result */
388433d6423SLionel Sambuc 	*temperature = t;
389433d6423SLionel Sambuc 
390433d6423SLionel Sambuc 	log_debug(&log, "t = %d\n", t);
391433d6423SLionel Sambuc 
392433d6423SLionel Sambuc 	/* Convert uncompensated pressure to true pressure.
393433d6423SLionel Sambuc 	 * This is really how the data sheet suggests doing it.
394433d6423SLionel Sambuc 	 * There is no alternative approach suggested. Other open
395433d6423SLionel Sambuc 	 * source drivers I've found use this method.
396433d6423SLionel Sambuc 	 */
397433d6423SLionel Sambuc 	b6 = b5 - 4000;
398433d6423SLionel Sambuc 	x1 = ((cal.b2 * ((b6 * b6) >> 12)) >> 11);
399433d6423SLionel Sambuc 	x2 = ((cal.ac2 * b6) >> 11);
400433d6423SLionel Sambuc 	x3 = x1 + x2;
401433d6423SLionel Sambuc 	b3 = (((((cal.ac1 * 4) + x3) << p_cmd->mode) + 2) >> 2);
402433d6423SLionel Sambuc 	x1 = ((cal.ac3 * b6) >> 13);
403433d6423SLionel Sambuc 	x2 = ((cal.b1 * ((b6 * b6) >> 12)) >> 16);
404433d6423SLionel Sambuc 	x3 = (((x1 + x2) + 2) >> 2);
405433d6423SLionel Sambuc 	b4 = ((cal.ac4 * ((uint32_t) (x3 + 32768))) >> 15);
406433d6423SLionel Sambuc 	b7 = ((uint32_t) up - b3) * (50000 >> p_cmd->mode);
407433d6423SLionel Sambuc 	p = (b7 < 0x80000000) ? (b7 * 2) / b4 : (b7 / b4) * 2;
408433d6423SLionel Sambuc 	x1 = (p >> 8) * (p >> 8);
409433d6423SLionel Sambuc 	x1 = ((x1 * 3038) >> 16);
410433d6423SLionel Sambuc 	x2 = ((-7357 * p) >> 16);
411433d6423SLionel Sambuc 	p = p + ((x1 + x2 + 3791) >> 4);
412433d6423SLionel Sambuc 
413433d6423SLionel Sambuc 	*pressure = p;
414433d6423SLionel Sambuc 
415433d6423SLionel Sambuc 	log_debug(&log, "p = %d\n", p);
416433d6423SLionel Sambuc 
417433d6423SLionel Sambuc 	return OK;
418433d6423SLionel Sambuc }
419433d6423SLionel Sambuc 
420433d6423SLionel Sambuc static ssize_t
bmp085_read(devminor_t UNUSED (minor),u64_t position,endpoint_t endpt,cp_grant_id_t grant,size_t size,int UNUSED (flags),cdev_id_t UNUSED (id))421433d6423SLionel Sambuc bmp085_read(devminor_t UNUSED(minor), u64_t position, endpoint_t endpt,
422433d6423SLionel Sambuc     cp_grant_id_t grant, size_t size, int UNUSED(flags), cdev_id_t UNUSED(id))
423433d6423SLionel Sambuc {
424433d6423SLionel Sambuc 	u64_t dev_size;
425433d6423SLionel Sambuc 	int r;
426433d6423SLionel Sambuc 	uint32_t temperature, pressure;
427433d6423SLionel Sambuc 
428433d6423SLionel Sambuc 	r = measure(&temperature, &pressure);
429433d6423SLionel Sambuc 	if (r != OK) {
430433d6423SLionel Sambuc 		return EIO;
431433d6423SLionel Sambuc 	}
432433d6423SLionel Sambuc 
433433d6423SLionel Sambuc 	memset(buffer, '\0', BUFFER_LEN + 1);
434433d6423SLionel Sambuc 	snprintf(buffer, BUFFER_LEN, "%-16s: %d.%01d\n%-16s: %d\n",
435433d6423SLionel Sambuc 	    "TEMPERATURE", temperature / 10, temperature % 10, "PRESSURE",
436433d6423SLionel Sambuc 	    pressure);
437433d6423SLionel Sambuc 
438433d6423SLionel Sambuc 	log_trace(&log, "%s", buffer);
439433d6423SLionel Sambuc 
440433d6423SLionel Sambuc 	dev_size = (u64_t)strlen(buffer);
441433d6423SLionel Sambuc 	if (position >= dev_size) return 0;
442433d6423SLionel Sambuc 	if (position + size > dev_size)
443433d6423SLionel Sambuc 		size = (size_t)(dev_size - position);
444433d6423SLionel Sambuc 
445433d6423SLionel Sambuc 	r = sys_safecopyto(endpt, grant, 0,
446433d6423SLionel Sambuc 	    (vir_bytes)(buffer + (size_t)position), size);
447433d6423SLionel Sambuc 
448433d6423SLionel Sambuc 	return (r != OK) ? r : size;
449433d6423SLionel Sambuc }
450433d6423SLionel Sambuc 
451433d6423SLionel Sambuc static void
bmp085_other(message * m,int ipc_status)452433d6423SLionel Sambuc bmp085_other(message * m, int ipc_status)
453433d6423SLionel Sambuc {
454433d6423SLionel Sambuc 	int r;
455433d6423SLionel Sambuc 
456433d6423SLionel Sambuc 	if (is_ipc_notify(ipc_status)) {
457433d6423SLionel Sambuc 		if (m->m_source == DS_PROC_NR) {
458433d6423SLionel Sambuc 			log_debug(&log,
459433d6423SLionel Sambuc 			    "bus driver changed state, update endpoint\n");
460433d6423SLionel Sambuc 			i2cdriver_handle_bus_update(&bus_endpoint, bus,
461433d6423SLionel Sambuc 			    address);
462433d6423SLionel Sambuc 		}
463433d6423SLionel Sambuc 		return;
464433d6423SLionel Sambuc 	}
465433d6423SLionel Sambuc 
466433d6423SLionel Sambuc 	log_warn(&log, "Invalid message type (0x%x)\n", m->m_type);
467433d6423SLionel Sambuc }
468433d6423SLionel Sambuc 
469433d6423SLionel Sambuc static int
sef_cb_lu_state_save(int UNUSED (result),int UNUSED (flags))470e1f889d2SCristiano Giuffrida sef_cb_lu_state_save(int UNUSED(result), int UNUSED(flags))
471433d6423SLionel Sambuc {
472433d6423SLionel Sambuc 	ds_publish_u32("bus", bus, DSF_OVERWRITE);
473433d6423SLionel Sambuc 	ds_publish_u32("address", address, DSF_OVERWRITE);
474433d6423SLionel Sambuc 	return OK;
475433d6423SLionel Sambuc }
476433d6423SLionel Sambuc 
477433d6423SLionel Sambuc static int
lu_state_restore(void)478433d6423SLionel Sambuc lu_state_restore(void)
479433d6423SLionel Sambuc {
480433d6423SLionel Sambuc 	/* Restore the state. */
481433d6423SLionel Sambuc 	u32_t value;
482433d6423SLionel Sambuc 
483433d6423SLionel Sambuc 	ds_retrieve_u32("bus", &value);
484433d6423SLionel Sambuc 	ds_delete_u32("bus");
485433d6423SLionel Sambuc 	bus = (int) value;
486433d6423SLionel Sambuc 
487433d6423SLionel Sambuc 	ds_retrieve_u32("address", &value);
488433d6423SLionel Sambuc 	ds_delete_u32("address");
489433d6423SLionel Sambuc 	address = (int) value;
490433d6423SLionel Sambuc 
491433d6423SLionel Sambuc 	return OK;
492433d6423SLionel Sambuc }
493433d6423SLionel Sambuc 
494433d6423SLionel Sambuc static int
sef_cb_init(int type,sef_init_info_t * UNUSED (info))495433d6423SLionel Sambuc sef_cb_init(int type, sef_init_info_t * UNUSED(info))
496433d6423SLionel Sambuc {
497433d6423SLionel Sambuc 	int r;
498433d6423SLionel Sambuc 
499433d6423SLionel Sambuc 	if (type == SEF_INIT_LU) {
500433d6423SLionel Sambuc 		/* Restore the state. */
501433d6423SLionel Sambuc 		lu_state_restore();
502433d6423SLionel Sambuc 	}
503433d6423SLionel Sambuc 
504433d6423SLionel Sambuc 	/* look-up the endpoint for the bus driver */
505433d6423SLionel Sambuc 	bus_endpoint = i2cdriver_bus_endpoint(bus);
506433d6423SLionel Sambuc 	if (bus_endpoint == 0) {
507433d6423SLionel Sambuc 		log_warn(&log, "Couldn't find bus driver.\n");
508433d6423SLionel Sambuc 		return EXIT_FAILURE;
509433d6423SLionel Sambuc 	}
510433d6423SLionel Sambuc 
511433d6423SLionel Sambuc 	/* claim the device */
512433d6423SLionel Sambuc 	r = i2cdriver_reserve_device(bus_endpoint, address);
513433d6423SLionel Sambuc 	if (r != OK) {
514433d6423SLionel Sambuc 		log_warn(&log, "Couldn't reserve device 0x%x (r=%d)\n",
515433d6423SLionel Sambuc 		    address, r);
516433d6423SLionel Sambuc 		return EXIT_FAILURE;
517433d6423SLionel Sambuc 	}
518433d6423SLionel Sambuc 
519433d6423SLionel Sambuc 	r = bmp085_init();
520433d6423SLionel Sambuc 	if (r != OK) {
521433d6423SLionel Sambuc 		log_warn(&log, "Couldn't initialize device\n");
522433d6423SLionel Sambuc 		return EXIT_FAILURE;
523433d6423SLionel Sambuc 	}
524433d6423SLionel Sambuc 
525433d6423SLionel Sambuc 	if (type != SEF_INIT_LU) {
526433d6423SLionel Sambuc 
527433d6423SLionel Sambuc 		/* sign up for updates about the i2c bus going down/up */
528433d6423SLionel Sambuc 		r = i2cdriver_subscribe_bus_updates(bus);
529433d6423SLionel Sambuc 		if (r != OK) {
530433d6423SLionel Sambuc 			log_warn(&log, "Couldn't subscribe to bus updates\n");
531433d6423SLionel Sambuc 			return EXIT_FAILURE;
532433d6423SLionel Sambuc 		}
533433d6423SLionel Sambuc 
534433d6423SLionel Sambuc 		i2cdriver_announce(bus);
535433d6423SLionel Sambuc 		log_debug(&log, "announced\n");
536433d6423SLionel Sambuc 	}
537433d6423SLionel Sambuc 
538433d6423SLionel Sambuc 	return OK;
539433d6423SLionel Sambuc }
540433d6423SLionel Sambuc 
541433d6423SLionel Sambuc static void
sef_local_startup(void)542433d6423SLionel Sambuc sef_local_startup(void)
543433d6423SLionel Sambuc {
544433d6423SLionel Sambuc 	/*
545433d6423SLionel Sambuc 	 * Register init callbacks. Use the same function for all event types
546433d6423SLionel Sambuc 	 */
547433d6423SLionel Sambuc 	sef_setcb_init_fresh(sef_cb_init);
548433d6423SLionel Sambuc 	sef_setcb_init_lu(sef_cb_init);
549433d6423SLionel Sambuc 	sef_setcb_init_restart(sef_cb_init);
550433d6423SLionel Sambuc 
551433d6423SLionel Sambuc 	/*
552433d6423SLionel Sambuc 	 * Register live update callbacks.
553433d6423SLionel Sambuc 	 */
554433d6423SLionel Sambuc 	sef_setcb_lu_state_save(sef_cb_lu_state_save);
555433d6423SLionel Sambuc 
556433d6423SLionel Sambuc 	/* Let SEF perform startup. */
557433d6423SLionel Sambuc 	sef_startup();
558433d6423SLionel Sambuc }
559433d6423SLionel Sambuc 
560433d6423SLionel Sambuc int
main(int argc,char * argv[])561433d6423SLionel Sambuc main(int argc, char *argv[])
562433d6423SLionel Sambuc {
563433d6423SLionel Sambuc 	int r;
564433d6423SLionel Sambuc 
565433d6423SLionel Sambuc 	env_setargs(argc, argv);
566433d6423SLionel Sambuc 
567433d6423SLionel Sambuc 	r = i2cdriver_env_parse(&bus, &address, valid_addrs);
568433d6423SLionel Sambuc 	if (r < 0) {
569433d6423SLionel Sambuc 		log_warn(&log, "Expecting -args 'bus=X address=0x77'\n");
570433d6423SLionel Sambuc 		log_warn(&log, "Example -args 'bus=1 address=0x77'\n");
571433d6423SLionel Sambuc 		return EXIT_FAILURE;
572433d6423SLionel Sambuc 	} else if (r > 0) {
573433d6423SLionel Sambuc 		log_warn(&log,
574433d6423SLionel Sambuc 		    "Invalid slave address for device, expecting 0x77\n");
575433d6423SLionel Sambuc 		return EXIT_FAILURE;
576433d6423SLionel Sambuc 	}
577433d6423SLionel Sambuc 
578433d6423SLionel Sambuc 	sef_local_startup();
579433d6423SLionel Sambuc 
580433d6423SLionel Sambuc 	chardriver_task(&bmp085_tab);
581433d6423SLionel Sambuc 
582433d6423SLionel Sambuc 	return 0;
583433d6423SLionel Sambuc }
584