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 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 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 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 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 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 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 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