1 /*
2 * LSM303DLHC I2C magnetometer.
3 *
4 * Copyright (C) 2021 Linaro Ltd.
5 * Written by Kevin Townsend <kevin.townsend@linaro.org>
6 *
7 * Based on: https://www.st.com/resource/en/datasheet/lsm303dlhc.pdf
8 *
9 * SPDX-License-Identifier: GPL-2.0-or-later
10 */
11
12 /*
13 * The I2C address associated with this device is set on the command-line when
14 * initialising the machine, but the following address is standard: 0x1E.
15 *
16 * Get and set functions for 'mag-x', 'mag-y' and 'mag-z' assume that
17 * 1 = 0.001 uT. (NOTE the 1 gauss = 100 uT, so setting a value of 100,000
18 * would be equal to 1 gauss or 100 uT.)
19 *
20 * Get and set functions for 'temperature' assume that 1 = 0.001 C, so 23.6 C
21 * would be equal to 23600.
22 */
23
24 #include "qemu/osdep.h"
25 #include "hw/i2c/i2c.h"
26 #include "migration/vmstate.h"
27 #include "qapi/error.h"
28 #include "qapi/visitor.h"
29 #include "qemu/module.h"
30 #include "qemu/log.h"
31
32 enum LSM303DLHCMagReg {
33 LSM303DLHC_MAG_REG_CRA = 0x00,
34 LSM303DLHC_MAG_REG_CRB = 0x01,
35 LSM303DLHC_MAG_REG_MR = 0x02,
36 LSM303DLHC_MAG_REG_OUT_X_H = 0x03,
37 LSM303DLHC_MAG_REG_OUT_X_L = 0x04,
38 LSM303DLHC_MAG_REG_OUT_Z_H = 0x05,
39 LSM303DLHC_MAG_REG_OUT_Z_L = 0x06,
40 LSM303DLHC_MAG_REG_OUT_Y_H = 0x07,
41 LSM303DLHC_MAG_REG_OUT_Y_L = 0x08,
42 LSM303DLHC_MAG_REG_SR = 0x09,
43 LSM303DLHC_MAG_REG_IRA = 0x0A,
44 LSM303DLHC_MAG_REG_IRB = 0x0B,
45 LSM303DLHC_MAG_REG_IRC = 0x0C,
46 LSM303DLHC_MAG_REG_TEMP_OUT_H = 0x31,
47 LSM303DLHC_MAG_REG_TEMP_OUT_L = 0x32
48 };
49
50 typedef struct LSM303DLHCMagState {
51 I2CSlave parent_obj;
52 uint8_t cra;
53 uint8_t crb;
54 uint8_t mr;
55 int16_t x;
56 int16_t z;
57 int16_t y;
58 int16_t x_lock;
59 int16_t z_lock;
60 int16_t y_lock;
61 uint8_t sr;
62 uint8_t ira;
63 uint8_t irb;
64 uint8_t irc;
65 int16_t temperature;
66 int16_t temperature_lock;
67 uint8_t len;
68 uint8_t buf;
69 uint8_t pointer;
70 } LSM303DLHCMagState;
71
72 #define TYPE_LSM303DLHC_MAG "lsm303dlhc_mag"
73 OBJECT_DECLARE_SIMPLE_TYPE(LSM303DLHCMagState, LSM303DLHC_MAG)
74
75 /*
76 * Conversion factor from Gauss to sensor values for each GN gain setting,
77 * in units "lsb per Gauss" (see data sheet table 3). There is no documented
78 * behaviour if the GN setting in CRB is incorrectly set to 0b000;
79 * we arbitrarily make it the same as 0b001.
80 */
81 uint32_t xy_gain[] = { 1100, 1100, 855, 670, 450, 400, 330, 230 };
82 uint32_t z_gain[] = { 980, 980, 760, 600, 400, 355, 295, 205 };
83
lsm303dlhc_mag_get_x(Object * obj,Visitor * v,const char * name,void * opaque,Error ** errp)84 static void lsm303dlhc_mag_get_x(Object *obj, Visitor *v, const char *name,
85 void *opaque, Error **errp)
86 {
87 LSM303DLHCMagState *s = LSM303DLHC_MAG(obj);
88 int gm = extract32(s->crb, 5, 3);
89
90 /* Convert to uT where 1000 = 1 uT. Conversion factor depends on gain. */
91 int64_t value = muldiv64(s->x, 100000, xy_gain[gm]);
92 visit_type_int(v, name, &value, errp);
93 }
94
lsm303dlhc_mag_get_y(Object * obj,Visitor * v,const char * name,void * opaque,Error ** errp)95 static void lsm303dlhc_mag_get_y(Object *obj, Visitor *v, const char *name,
96 void *opaque, Error **errp)
97 {
98 LSM303DLHCMagState *s = LSM303DLHC_MAG(obj);
99 int gm = extract32(s->crb, 5, 3);
100
101 /* Convert to uT where 1000 = 1 uT. Conversion factor depends on gain. */
102 int64_t value = muldiv64(s->y, 100000, xy_gain[gm]);
103 visit_type_int(v, name, &value, errp);
104 }
105
lsm303dlhc_mag_get_z(Object * obj,Visitor * v,const char * name,void * opaque,Error ** errp)106 static void lsm303dlhc_mag_get_z(Object *obj, Visitor *v, const char *name,
107 void *opaque, Error **errp)
108 {
109 LSM303DLHCMagState *s = LSM303DLHC_MAG(obj);
110 int gm = extract32(s->crb, 5, 3);
111
112 /* Convert to uT where 1000 = 1 uT. Conversion factor depends on gain. */
113 int64_t value = muldiv64(s->z, 100000, z_gain[gm]);
114 visit_type_int(v, name, &value, errp);
115 }
116
lsm303dlhc_mag_set_x(Object * obj,Visitor * v,const char * name,void * opaque,Error ** errp)117 static void lsm303dlhc_mag_set_x(Object *obj, Visitor *v, const char *name,
118 void *opaque, Error **errp)
119 {
120 LSM303DLHCMagState *s = LSM303DLHC_MAG(obj);
121 int64_t value;
122 int64_t reg;
123 int gm = extract32(s->crb, 5, 3);
124
125 if (!visit_type_int(v, name, &value, errp)) {
126 return;
127 }
128
129 reg = muldiv64(value, xy_gain[gm], 100000);
130
131 /* Make sure we are within a 12-bit limit. */
132 if (reg > 2047 || reg < -2048) {
133 error_setg(errp, "value %" PRId64 " out of register's range", value);
134 return;
135 }
136
137 s->x = (int16_t)reg;
138 }
139
lsm303dlhc_mag_set_y(Object * obj,Visitor * v,const char * name,void * opaque,Error ** errp)140 static void lsm303dlhc_mag_set_y(Object *obj, Visitor *v, const char *name,
141 void *opaque, Error **errp)
142 {
143 LSM303DLHCMagState *s = LSM303DLHC_MAG(obj);
144 int64_t value;
145 int64_t reg;
146 int gm = extract32(s->crb, 5, 3);
147
148 if (!visit_type_int(v, name, &value, errp)) {
149 return;
150 }
151
152 reg = muldiv64(value, xy_gain[gm], 100000);
153
154 /* Make sure we are within a 12-bit limit. */
155 if (reg > 2047 || reg < -2048) {
156 error_setg(errp, "value %" PRId64 " out of register's range", value);
157 return;
158 }
159
160 s->y = (int16_t)reg;
161 }
162
lsm303dlhc_mag_set_z(Object * obj,Visitor * v,const char * name,void * opaque,Error ** errp)163 static void lsm303dlhc_mag_set_z(Object *obj, Visitor *v, const char *name,
164 void *opaque, Error **errp)
165 {
166 LSM303DLHCMagState *s = LSM303DLHC_MAG(obj);
167 int64_t value;
168 int64_t reg;
169 int gm = extract32(s->crb, 5, 3);
170
171 if (!visit_type_int(v, name, &value, errp)) {
172 return;
173 }
174
175 reg = muldiv64(value, z_gain[gm], 100000);
176
177 /* Make sure we are within a 12-bit limit. */
178 if (reg > 2047 || reg < -2048) {
179 error_setg(errp, "value %" PRId64 " out of register's range", value);
180 return;
181 }
182
183 s->z = (int16_t)reg;
184 }
185
186 /*
187 * Get handler for the temperature property.
188 */
lsm303dlhc_mag_get_temperature(Object * obj,Visitor * v,const char * name,void * opaque,Error ** errp)189 static void lsm303dlhc_mag_get_temperature(Object *obj, Visitor *v,
190 const char *name, void *opaque,
191 Error **errp)
192 {
193 LSM303DLHCMagState *s = LSM303DLHC_MAG(obj);
194 int64_t value;
195
196 /* Convert to 1 lsb = 0.125 C to 1 = 0.001 C for 'temperature' property. */
197 value = s->temperature * 125;
198
199 visit_type_int(v, name, &value, errp);
200 }
201
202 /*
203 * Set handler for the temperature property.
204 */
lsm303dlhc_mag_set_temperature(Object * obj,Visitor * v,const char * name,void * opaque,Error ** errp)205 static void lsm303dlhc_mag_set_temperature(Object *obj, Visitor *v,
206 const char *name, void *opaque,
207 Error **errp)
208 {
209 LSM303DLHCMagState *s = LSM303DLHC_MAG(obj);
210 int64_t value;
211
212 if (!visit_type_int(v, name, &value, errp)) {
213 return;
214 }
215
216 /* Input temperature is in 0.001 C units. Convert to 1 lsb = 0.125 C. */
217 value /= 125;
218
219 if (value > 2047 || value < -2048) {
220 error_setg(errp, "value %" PRId64 " lsb is out of range", value);
221 return;
222 }
223
224 s->temperature = (int16_t)value;
225 }
226
227 /*
228 * Callback handler whenever a 'I2C_START_RECV' (read) event is received.
229 */
lsm303dlhc_mag_read(LSM303DLHCMagState * s)230 static void lsm303dlhc_mag_read(LSM303DLHCMagState *s)
231 {
232 /*
233 * Set the LOCK bit whenever a new read attempt is made. This will be
234 * cleared in I2C_FINISH. Note that DRDY is always set to 1 in this driver.
235 */
236 s->sr = 0x3;
237
238 /*
239 * Copy the current X/Y/Z and temp. values into the locked registers so
240 * that 'mag-x', 'mag-y', 'mag-z' and 'temperature' can continue to be
241 * updated via QOM, etc., without corrupting the current read event.
242 */
243 s->x_lock = s->x;
244 s->z_lock = s->z;
245 s->y_lock = s->y;
246 s->temperature_lock = s->temperature;
247 }
248
249 /*
250 * Callback handler whenever a 'I2C_FINISH' event is received.
251 */
lsm303dlhc_mag_finish(LSM303DLHCMagState * s)252 static void lsm303dlhc_mag_finish(LSM303DLHCMagState *s)
253 {
254 /*
255 * Clear the LOCK bit when the read attempt terminates.
256 * This bit is initially set in the I2C_START_RECV handler.
257 */
258 s->sr = 0x1;
259 }
260
261 /*
262 * Callback handler when a device attempts to write to a register.
263 */
lsm303dlhc_mag_write(LSM303DLHCMagState * s)264 static void lsm303dlhc_mag_write(LSM303DLHCMagState *s)
265 {
266 switch (s->pointer) {
267 case LSM303DLHC_MAG_REG_CRA:
268 s->cra = s->buf;
269 break;
270 case LSM303DLHC_MAG_REG_CRB:
271 /* Make sure gain is at least 1, falling back to 1 on an error. */
272 if (s->buf >> 5 == 0) {
273 s->buf = 1 << 5;
274 }
275 s->crb = s->buf;
276 break;
277 case LSM303DLHC_MAG_REG_MR:
278 s->mr = s->buf;
279 break;
280 case LSM303DLHC_MAG_REG_SR:
281 s->sr = s->buf;
282 break;
283 case LSM303DLHC_MAG_REG_IRA:
284 s->ira = s->buf;
285 break;
286 case LSM303DLHC_MAG_REG_IRB:
287 s->irb = s->buf;
288 break;
289 case LSM303DLHC_MAG_REG_IRC:
290 s->irc = s->buf;
291 break;
292 default:
293 qemu_log_mask(LOG_GUEST_ERROR, "reg is read-only: 0x%02X", s->buf);
294 break;
295 }
296 }
297
298 /*
299 * Low-level master-to-slave transaction handler.
300 */
lsm303dlhc_mag_send(I2CSlave * i2c,uint8_t data)301 static int lsm303dlhc_mag_send(I2CSlave *i2c, uint8_t data)
302 {
303 LSM303DLHCMagState *s = LSM303DLHC_MAG(i2c);
304
305 if (s->len == 0) {
306 /* First byte is the reg pointer */
307 s->pointer = data;
308 s->len++;
309 } else if (s->len == 1) {
310 /* Second byte is the new register value. */
311 s->buf = data;
312 lsm303dlhc_mag_write(s);
313 } else {
314 g_assert_not_reached();
315 }
316
317 return 0;
318 }
319
320 /*
321 * Low-level slave-to-master transaction handler (read attempts).
322 */
lsm303dlhc_mag_recv(I2CSlave * i2c)323 static uint8_t lsm303dlhc_mag_recv(I2CSlave *i2c)
324 {
325 LSM303DLHCMagState *s = LSM303DLHC_MAG(i2c);
326 uint8_t resp;
327
328 switch (s->pointer) {
329 case LSM303DLHC_MAG_REG_CRA:
330 resp = s->cra;
331 break;
332 case LSM303DLHC_MAG_REG_CRB:
333 resp = s->crb;
334 break;
335 case LSM303DLHC_MAG_REG_MR:
336 resp = s->mr;
337 break;
338 case LSM303DLHC_MAG_REG_OUT_X_H:
339 resp = (uint8_t)(s->x_lock >> 8);
340 break;
341 case LSM303DLHC_MAG_REG_OUT_X_L:
342 resp = (uint8_t)(s->x_lock);
343 break;
344 case LSM303DLHC_MAG_REG_OUT_Z_H:
345 resp = (uint8_t)(s->z_lock >> 8);
346 break;
347 case LSM303DLHC_MAG_REG_OUT_Z_L:
348 resp = (uint8_t)(s->z_lock);
349 break;
350 case LSM303DLHC_MAG_REG_OUT_Y_H:
351 resp = (uint8_t)(s->y_lock >> 8);
352 break;
353 case LSM303DLHC_MAG_REG_OUT_Y_L:
354 resp = (uint8_t)(s->y_lock);
355 break;
356 case LSM303DLHC_MAG_REG_SR:
357 resp = s->sr;
358 break;
359 case LSM303DLHC_MAG_REG_IRA:
360 resp = s->ira;
361 break;
362 case LSM303DLHC_MAG_REG_IRB:
363 resp = s->irb;
364 break;
365 case LSM303DLHC_MAG_REG_IRC:
366 resp = s->irc;
367 break;
368 case LSM303DLHC_MAG_REG_TEMP_OUT_H:
369 /* Check if the temperature sensor is enabled or not (CRA & 0x80). */
370 if (s->cra & 0x80) {
371 resp = (uint8_t)(s->temperature_lock >> 8);
372 } else {
373 resp = 0;
374 }
375 break;
376 case LSM303DLHC_MAG_REG_TEMP_OUT_L:
377 if (s->cra & 0x80) {
378 resp = (uint8_t)(s->temperature_lock & 0xff);
379 } else {
380 resp = 0;
381 }
382 break;
383 default:
384 resp = 0;
385 break;
386 }
387
388 /*
389 * The address pointer on the LSM303DLHC auto-increments whenever a byte
390 * is read, without the master device having to request the next address.
391 *
392 * The auto-increment process has the following logic:
393 *
394 * - if (s->pointer == 8) then s->pointer = 3
395 * - else: if (s->pointer == 12) then s->pointer = 0
396 * - else: s->pointer += 1
397 *
398 * Reading an invalid address return 0.
399 */
400 if (s->pointer == LSM303DLHC_MAG_REG_OUT_Y_L) {
401 s->pointer = LSM303DLHC_MAG_REG_OUT_X_H;
402 } else if (s->pointer == LSM303DLHC_MAG_REG_IRC) {
403 s->pointer = LSM303DLHC_MAG_REG_CRA;
404 } else {
405 s->pointer++;
406 }
407
408 return resp;
409 }
410
411 /*
412 * Bus state change handler.
413 */
lsm303dlhc_mag_event(I2CSlave * i2c,enum i2c_event event)414 static int lsm303dlhc_mag_event(I2CSlave *i2c, enum i2c_event event)
415 {
416 LSM303DLHCMagState *s = LSM303DLHC_MAG(i2c);
417
418 switch (event) {
419 case I2C_START_SEND:
420 break;
421 case I2C_START_RECV:
422 lsm303dlhc_mag_read(s);
423 break;
424 case I2C_FINISH:
425 lsm303dlhc_mag_finish(s);
426 break;
427 case I2C_NACK:
428 break;
429 default:
430 return -1;
431 }
432
433 s->len = 0;
434 return 0;
435 }
436
437 /*
438 * Device data description using VMSTATE macros.
439 */
440 static const VMStateDescription vmstate_lsm303dlhc_mag = {
441 .name = "LSM303DLHC_MAG",
442 .version_id = 0,
443 .minimum_version_id = 0,
444 .fields = (const VMStateField[]) {
445
446 VMSTATE_I2C_SLAVE(parent_obj, LSM303DLHCMagState),
447 VMSTATE_UINT8(len, LSM303DLHCMagState),
448 VMSTATE_UINT8(buf, LSM303DLHCMagState),
449 VMSTATE_UINT8(pointer, LSM303DLHCMagState),
450 VMSTATE_UINT8(cra, LSM303DLHCMagState),
451 VMSTATE_UINT8(crb, LSM303DLHCMagState),
452 VMSTATE_UINT8(mr, LSM303DLHCMagState),
453 VMSTATE_INT16(x, LSM303DLHCMagState),
454 VMSTATE_INT16(z, LSM303DLHCMagState),
455 VMSTATE_INT16(y, LSM303DLHCMagState),
456 VMSTATE_INT16(x_lock, LSM303DLHCMagState),
457 VMSTATE_INT16(z_lock, LSM303DLHCMagState),
458 VMSTATE_INT16(y_lock, LSM303DLHCMagState),
459 VMSTATE_UINT8(sr, LSM303DLHCMagState),
460 VMSTATE_UINT8(ira, LSM303DLHCMagState),
461 VMSTATE_UINT8(irb, LSM303DLHCMagState),
462 VMSTATE_UINT8(irc, LSM303DLHCMagState),
463 VMSTATE_INT16(temperature, LSM303DLHCMagState),
464 VMSTATE_INT16(temperature_lock, LSM303DLHCMagState),
465 VMSTATE_END_OF_LIST()
466 }
467 };
468
469 /*
470 * Put the device into post-reset default state.
471 */
lsm303dlhc_mag_default_cfg(LSM303DLHCMagState * s)472 static void lsm303dlhc_mag_default_cfg(LSM303DLHCMagState *s)
473 {
474 /* Set the device into is default reset state. */
475 s->len = 0;
476 s->pointer = 0; /* Current register. */
477 s->buf = 0; /* Shared buffer. */
478 s->cra = 0x10; /* Temp Enabled = 0, Data Rate = 15.0 Hz. */
479 s->crb = 0x20; /* Gain = +/- 1.3 Gauss. */
480 s->mr = 0x3; /* Operating Mode = Sleep. */
481 s->x = 0;
482 s->z = 0;
483 s->y = 0;
484 s->x_lock = 0;
485 s->z_lock = 0;
486 s->y_lock = 0;
487 s->sr = 0x1; /* DRDY = 1. */
488 s->ira = 0x48;
489 s->irb = 0x34;
490 s->irc = 0x33;
491 s->temperature = 0; /* Default to 0 degrees C (0/8 lsb = 0 C). */
492 s->temperature_lock = 0;
493 }
494
495 /*
496 * Callback handler when DeviceState 'reset' is set to true.
497 */
lsm303dlhc_mag_reset(DeviceState * dev)498 static void lsm303dlhc_mag_reset(DeviceState *dev)
499 {
500 I2CSlave *i2c = I2C_SLAVE(dev);
501 LSM303DLHCMagState *s = LSM303DLHC_MAG(i2c);
502
503 /* Set the device into its default reset state. */
504 lsm303dlhc_mag_default_cfg(s);
505 }
506
507 /*
508 * Initialisation of any public properties.
509 */
lsm303dlhc_mag_initfn(Object * obj)510 static void lsm303dlhc_mag_initfn(Object *obj)
511 {
512 object_property_add(obj, "mag-x", "int",
513 lsm303dlhc_mag_get_x,
514 lsm303dlhc_mag_set_x, NULL, NULL);
515
516 object_property_add(obj, "mag-y", "int",
517 lsm303dlhc_mag_get_y,
518 lsm303dlhc_mag_set_y, NULL, NULL);
519
520 object_property_add(obj, "mag-z", "int",
521 lsm303dlhc_mag_get_z,
522 lsm303dlhc_mag_set_z, NULL, NULL);
523
524 object_property_add(obj, "temperature", "int",
525 lsm303dlhc_mag_get_temperature,
526 lsm303dlhc_mag_set_temperature, NULL, NULL);
527 }
528
529 /*
530 * Set the virtual method pointers (bus state change, tx/rx, etc.).
531 */
lsm303dlhc_mag_class_init(ObjectClass * klass,const void * data)532 static void lsm303dlhc_mag_class_init(ObjectClass *klass, const void *data)
533 {
534 DeviceClass *dc = DEVICE_CLASS(klass);
535 I2CSlaveClass *k = I2C_SLAVE_CLASS(klass);
536
537 device_class_set_legacy_reset(dc, lsm303dlhc_mag_reset);
538 dc->vmsd = &vmstate_lsm303dlhc_mag;
539 k->event = lsm303dlhc_mag_event;
540 k->recv = lsm303dlhc_mag_recv;
541 k->send = lsm303dlhc_mag_send;
542 }
543
544 static const TypeInfo lsm303dlhc_mag_info = {
545 .name = TYPE_LSM303DLHC_MAG,
546 .parent = TYPE_I2C_SLAVE,
547 .instance_size = sizeof(LSM303DLHCMagState),
548 .instance_init = lsm303dlhc_mag_initfn,
549 .class_init = lsm303dlhc_mag_class_init,
550 };
551
lsm303dlhc_mag_register_types(void)552 static void lsm303dlhc_mag_register_types(void)
553 {
554 type_register_static(&lsm303dlhc_mag_info);
555 }
556
557 type_init(lsm303dlhc_mag_register_types)
558