2 * LSM303DLHC I2C magnetometer.
4 * Copyright (C) 2021 Linaro Ltd.
5 * Written by Kevin Townsend <kevin.townsend@linaro.org>
7 * Based on: https://www.st.com/resource/en/datasheet/lsm303dlhc.pdf
9 * SPDX-License-Identifier: GPL-2.0-or-later
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.
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.)
20 * Get and set functions for 'temperature' assume that 1 = 0.001 C, so 23.6 C
21 * would be equal to 23600.
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"
31 #include "qemu/bswap.h"
33 enum LSM303DLHCMagReg
{
34 LSM303DLHC_MAG_REG_CRA
= 0x00,
35 LSM303DLHC_MAG_REG_CRB
= 0x01,
36 LSM303DLHC_MAG_REG_MR
= 0x02,
37 LSM303DLHC_MAG_REG_OUT_X_H
= 0x03,
38 LSM303DLHC_MAG_REG_OUT_X_L
= 0x04,
39 LSM303DLHC_MAG_REG_OUT_Z_H
= 0x05,
40 LSM303DLHC_MAG_REG_OUT_Z_L
= 0x06,
41 LSM303DLHC_MAG_REG_OUT_Y_H
= 0x07,
42 LSM303DLHC_MAG_REG_OUT_Y_L
= 0x08,
43 LSM303DLHC_MAG_REG_SR
= 0x09,
44 LSM303DLHC_MAG_REG_IRA
= 0x0A,
45 LSM303DLHC_MAG_REG_IRB
= 0x0B,
46 LSM303DLHC_MAG_REG_IRC
= 0x0C,
47 LSM303DLHC_MAG_REG_TEMP_OUT_H
= 0x31,
48 LSM303DLHC_MAG_REG_TEMP_OUT_L
= 0x32
51 typedef struct LSM303DLHCMagState
{
67 int16_t temperature_lock
;
73 #define TYPE_LSM303DLHC_MAG "lsm303dlhc_mag"
74 OBJECT_DECLARE_SIMPLE_TYPE(LSM303DLHCMagState
, LSM303DLHC_MAG
)
77 * Conversion factor from Gauss to sensor values for each GN gain setting,
78 * in units "lsb per Gauss" (see data sheet table 3). There is no documented
79 * behaviour if the GN setting in CRB is incorrectly set to 0b000;
80 * we arbitrarily make it the same as 0b001.
82 uint32_t xy_gain
[] = { 1100, 1100, 855, 670, 450, 400, 330, 230 };
83 uint32_t z_gain
[] = { 980, 980, 760, 600, 400, 355, 295, 205 };
85 static void lsm303dlhc_mag_get_x(Object
*obj
, Visitor
*v
, const char *name
,
86 void *opaque
, Error
**errp
)
88 LSM303DLHCMagState
*s
= LSM303DLHC_MAG(obj
);
89 int gm
= extract32(s
->crb
, 5, 3);
91 /* Convert to uT where 1000 = 1 uT. Conversion factor depends on gain. */
92 int64_t value
= muldiv64(s
->x
, 100000, xy_gain
[gm
]);
93 visit_type_int(v
, name
, &value
, errp
);
96 static void lsm303dlhc_mag_get_y(Object
*obj
, Visitor
*v
, const char *name
,
97 void *opaque
, Error
**errp
)
99 LSM303DLHCMagState
*s
= LSM303DLHC_MAG(obj
);
100 int gm
= extract32(s
->crb
, 5, 3);
102 /* Convert to uT where 1000 = 1 uT. Conversion factor depends on gain. */
103 int64_t value
= muldiv64(s
->y
, 100000, xy_gain
[gm
]);
104 visit_type_int(v
, name
, &value
, errp
);
107 static void lsm303dlhc_mag_get_z(Object
*obj
, Visitor
*v
, const char *name
,
108 void *opaque
, Error
**errp
)
110 LSM303DLHCMagState
*s
= LSM303DLHC_MAG(obj
);
111 int gm
= extract32(s
->crb
, 5, 3);
113 /* Convert to uT where 1000 = 1 uT. Conversion factor depends on gain. */
114 int64_t value
= muldiv64(s
->z
, 100000, z_gain
[gm
]);
115 visit_type_int(v
, name
, &value
, errp
);
118 static void lsm303dlhc_mag_set_x(Object
*obj
, Visitor
*v
, const char *name
,
119 void *opaque
, Error
**errp
)
121 LSM303DLHCMagState
*s
= LSM303DLHC_MAG(obj
);
124 int gm
= extract32(s
->crb
, 5, 3);
126 if (!visit_type_int(v
, name
, &value
, errp
)) {
130 reg
= muldiv64(value
, xy_gain
[gm
], 100000);
132 /* Make sure we are within a 12-bit limit. */
133 if (reg
> 2047 || reg
< -2048) {
134 error_setg(errp
, "value %" PRId64
" out of register's range", value
);
141 static void lsm303dlhc_mag_set_y(Object
*obj
, Visitor
*v
, const char *name
,
142 void *opaque
, Error
**errp
)
144 LSM303DLHCMagState
*s
= LSM303DLHC_MAG(obj
);
147 int gm
= extract32(s
->crb
, 5, 3);
149 if (!visit_type_int(v
, name
, &value
, errp
)) {
153 reg
= muldiv64(value
, xy_gain
[gm
], 100000);
155 /* Make sure we are within a 12-bit limit. */
156 if (reg
> 2047 || reg
< -2048) {
157 error_setg(errp
, "value %" PRId64
" out of register's range", value
);
164 static void lsm303dlhc_mag_set_z(Object
*obj
, Visitor
*v
, const char *name
,
165 void *opaque
, Error
**errp
)
167 LSM303DLHCMagState
*s
= LSM303DLHC_MAG(obj
);
170 int gm
= extract32(s
->crb
, 5, 3);
172 if (!visit_type_int(v
, name
, &value
, errp
)) {
176 reg
= muldiv64(value
, z_gain
[gm
], 100000);
178 /* Make sure we are within a 12-bit limit. */
179 if (reg
> 2047 || reg
< -2048) {
180 error_setg(errp
, "value %" PRId64
" out of register's range", value
);
188 * Get handler for the temperature property.
190 static void lsm303dlhc_mag_get_temperature(Object
*obj
, Visitor
*v
,
191 const char *name
, void *opaque
,
194 LSM303DLHCMagState
*s
= LSM303DLHC_MAG(obj
);
197 /* Convert to 1 lsb = 0.125 C to 1 = 0.001 C for 'temperature' property. */
198 value
= s
->temperature
* 125;
200 visit_type_int(v
, name
, &value
, errp
);
204 * Set handler for the temperature property.
206 static void lsm303dlhc_mag_set_temperature(Object
*obj
, Visitor
*v
,
207 const char *name
, void *opaque
,
210 LSM303DLHCMagState
*s
= LSM303DLHC_MAG(obj
);
213 if (!visit_type_int(v
, name
, &value
, errp
)) {
217 /* Input temperature is in 0.001 C units. Convert to 1 lsb = 0.125 C. */
220 if (value
> 2047 || value
< -2048) {
221 error_setg(errp
, "value %" PRId64
" lsb is out of range", value
);
225 s
->temperature
= (int16_t)value
;
229 * Callback handler whenever a 'I2C_START_RECV' (read) event is received.
231 static void lsm303dlhc_mag_read(LSM303DLHCMagState
*s
)
234 * Set the LOCK bit whenever a new read attempt is made. This will be
235 * cleared in I2C_FINISH. Note that DRDY is always set to 1 in this driver.
240 * Copy the current X/Y/Z and temp. values into the locked registers so
241 * that 'mag-x', 'mag-y', 'mag-z' and 'temperature' can continue to be
242 * updated via QOM, etc., without corrupting the current read event.
247 s
->temperature_lock
= s
->temperature
;
251 * Callback handler whenever a 'I2C_FINISH' event is received.
253 static void lsm303dlhc_mag_finish(LSM303DLHCMagState
*s
)
256 * Clear the LOCK bit when the read attempt terminates.
257 * This bit is initially set in the I2C_START_RECV handler.
263 * Callback handler when a device attempts to write to a register.
265 static void lsm303dlhc_mag_write(LSM303DLHCMagState
*s
)
267 switch (s
->pointer
) {
268 case LSM303DLHC_MAG_REG_CRA
:
271 case LSM303DLHC_MAG_REG_CRB
:
272 /* Make sure gain is at least 1, falling back to 1 on an error. */
273 if (s
->buf
>> 5 == 0) {
278 case LSM303DLHC_MAG_REG_MR
:
281 case LSM303DLHC_MAG_REG_SR
:
284 case LSM303DLHC_MAG_REG_IRA
:
287 case LSM303DLHC_MAG_REG_IRB
:
290 case LSM303DLHC_MAG_REG_IRC
:
294 qemu_log_mask(LOG_GUEST_ERROR
, "reg is read-only: 0x%02X", s
->buf
);
300 * Low-level master-to-slave transaction handler.
302 static int lsm303dlhc_mag_send(I2CSlave
*i2c
, uint8_t data
)
304 LSM303DLHCMagState
*s
= LSM303DLHC_MAG(i2c
);
307 /* First byte is the reg pointer */
310 } else if (s
->len
== 1) {
311 /* Second byte is the new register value. */
313 lsm303dlhc_mag_write(s
);
315 g_assert_not_reached();
322 * Low-level slave-to-master transaction handler (read attempts).
324 static uint8_t lsm303dlhc_mag_recv(I2CSlave
*i2c
)
326 LSM303DLHCMagState
*s
= LSM303DLHC_MAG(i2c
);
329 switch (s
->pointer
) {
330 case LSM303DLHC_MAG_REG_CRA
:
333 case LSM303DLHC_MAG_REG_CRB
:
336 case LSM303DLHC_MAG_REG_MR
:
339 case LSM303DLHC_MAG_REG_OUT_X_H
:
340 resp
= (uint8_t)(s
->x_lock
>> 8);
342 case LSM303DLHC_MAG_REG_OUT_X_L
:
343 resp
= (uint8_t)(s
->x_lock
);
345 case LSM303DLHC_MAG_REG_OUT_Z_H
:
346 resp
= (uint8_t)(s
->z_lock
>> 8);
348 case LSM303DLHC_MAG_REG_OUT_Z_L
:
349 resp
= (uint8_t)(s
->z_lock
);
351 case LSM303DLHC_MAG_REG_OUT_Y_H
:
352 resp
= (uint8_t)(s
->y_lock
>> 8);
354 case LSM303DLHC_MAG_REG_OUT_Y_L
:
355 resp
= (uint8_t)(s
->y_lock
);
357 case LSM303DLHC_MAG_REG_SR
:
360 case LSM303DLHC_MAG_REG_IRA
:
363 case LSM303DLHC_MAG_REG_IRB
:
366 case LSM303DLHC_MAG_REG_IRC
:
369 case LSM303DLHC_MAG_REG_TEMP_OUT_H
:
370 /* Check if the temperature sensor is enabled or not (CRA & 0x80). */
372 resp
= (uint8_t)(s
->temperature_lock
>> 8);
377 case LSM303DLHC_MAG_REG_TEMP_OUT_L
:
379 resp
= (uint8_t)(s
->temperature_lock
& 0xff);
390 * The address pointer on the LSM303DLHC auto-increments whenever a byte
391 * is read, without the master device having to request the next address.
393 * The auto-increment process has the following logic:
395 * - if (s->pointer == 8) then s->pointer = 3
396 * - else: if (s->pointer == 12) then s->pointer = 0
397 * - else: s->pointer += 1
399 * Reading an invalid address return 0.
401 if (s
->pointer
== LSM303DLHC_MAG_REG_OUT_Y_L
) {
402 s
->pointer
= LSM303DLHC_MAG_REG_OUT_X_H
;
403 } else if (s
->pointer
== LSM303DLHC_MAG_REG_IRC
) {
404 s
->pointer
= LSM303DLHC_MAG_REG_CRA
;
413 * Bus state change handler.
415 static int lsm303dlhc_mag_event(I2CSlave
*i2c
, enum i2c_event event
)
417 LSM303DLHCMagState
*s
= LSM303DLHC_MAG(i2c
);
423 lsm303dlhc_mag_read(s
);
426 lsm303dlhc_mag_finish(s
);
439 * Device data description using VMSTATE macros.
441 static const VMStateDescription vmstate_lsm303dlhc_mag
= {
442 .name
= "LSM303DLHC_MAG",
444 .minimum_version_id
= 0,
445 .fields
= (VMStateField
[]) {
447 VMSTATE_I2C_SLAVE(parent_obj
, LSM303DLHCMagState
),
448 VMSTATE_UINT8(len
, LSM303DLHCMagState
),
449 VMSTATE_UINT8(buf
, LSM303DLHCMagState
),
450 VMSTATE_UINT8(pointer
, LSM303DLHCMagState
),
451 VMSTATE_UINT8(cra
, LSM303DLHCMagState
),
452 VMSTATE_UINT8(crb
, LSM303DLHCMagState
),
453 VMSTATE_UINT8(mr
, LSM303DLHCMagState
),
454 VMSTATE_INT16(x
, LSM303DLHCMagState
),
455 VMSTATE_INT16(z
, LSM303DLHCMagState
),
456 VMSTATE_INT16(y
, LSM303DLHCMagState
),
457 VMSTATE_INT16(x_lock
, LSM303DLHCMagState
),
458 VMSTATE_INT16(z_lock
, LSM303DLHCMagState
),
459 VMSTATE_INT16(y_lock
, LSM303DLHCMagState
),
460 VMSTATE_UINT8(sr
, LSM303DLHCMagState
),
461 VMSTATE_UINT8(ira
, LSM303DLHCMagState
),
462 VMSTATE_UINT8(irb
, LSM303DLHCMagState
),
463 VMSTATE_UINT8(irc
, LSM303DLHCMagState
),
464 VMSTATE_INT16(temperature
, LSM303DLHCMagState
),
465 VMSTATE_INT16(temperature_lock
, LSM303DLHCMagState
),
466 VMSTATE_END_OF_LIST()
471 * Put the device into post-reset default state.
473 static void lsm303dlhc_mag_default_cfg(LSM303DLHCMagState
*s
)
475 /* Set the device into is default reset state. */
477 s
->pointer
= 0; /* Current register. */
478 s
->buf
= 0; /* Shared buffer. */
479 s
->cra
= 0x10; /* Temp Enabled = 0, Data Rate = 15.0 Hz. */
480 s
->crb
= 0x20; /* Gain = +/- 1.3 Gauss. */
481 s
->mr
= 0x3; /* Operating Mode = Sleep. */
488 s
->sr
= 0x1; /* DRDY = 1. */
492 s
->temperature
= 0; /* Default to 0 degrees C (0/8 lsb = 0 C). */
493 s
->temperature_lock
= 0;
497 * Callback handler when DeviceState 'reset' is set to true.
499 static void lsm303dlhc_mag_reset(DeviceState
*dev
)
501 I2CSlave
*i2c
= I2C_SLAVE(dev
);
502 LSM303DLHCMagState
*s
= LSM303DLHC_MAG(i2c
);
504 /* Set the device into its default reset state. */
505 lsm303dlhc_mag_default_cfg(s
);
509 * Initialisation of any public properties.
511 static void lsm303dlhc_mag_initfn(Object
*obj
)
513 object_property_add(obj
, "mag-x", "int",
514 lsm303dlhc_mag_get_x
,
515 lsm303dlhc_mag_set_x
, NULL
, NULL
);
517 object_property_add(obj
, "mag-y", "int",
518 lsm303dlhc_mag_get_y
,
519 lsm303dlhc_mag_set_y
, NULL
, NULL
);
521 object_property_add(obj
, "mag-z", "int",
522 lsm303dlhc_mag_get_z
,
523 lsm303dlhc_mag_set_z
, NULL
, NULL
);
525 object_property_add(obj
, "temperature", "int",
526 lsm303dlhc_mag_get_temperature
,
527 lsm303dlhc_mag_set_temperature
, NULL
, NULL
);
531 * Set the virtual method pointers (bus state change, tx/rx, etc.).
533 static void lsm303dlhc_mag_class_init(ObjectClass
*klass
, void *data
)
535 DeviceClass
*dc
= DEVICE_CLASS(klass
);
536 I2CSlaveClass
*k
= I2C_SLAVE_CLASS(klass
);
538 dc
->reset
= lsm303dlhc_mag_reset
;
539 dc
->vmsd
= &vmstate_lsm303dlhc_mag
;
540 k
->event
= lsm303dlhc_mag_event
;
541 k
->recv
= lsm303dlhc_mag_recv
;
542 k
->send
= lsm303dlhc_mag_send
;
545 static const TypeInfo lsm303dlhc_mag_info
= {
546 .name
= TYPE_LSM303DLHC_MAG
,
547 .parent
= TYPE_I2C_SLAVE
,
548 .instance_size
= sizeof(LSM303DLHCMagState
),
549 .instance_init
= lsm303dlhc_mag_initfn
,
550 .class_init
= lsm303dlhc_mag_class_init
,
553 static void lsm303dlhc_mag_register_types(void)
555 type_register_static(&lsm303dlhc_mag_info
);
558 type_init(lsm303dlhc_mag_register_types
)