dps310.c (5939B)
1// SPDX-License-Identifier: GPL-2.0-or-later 2/* 3 * Copyright 2017-2021 Joel Stanley <joel@jms.id.au>, IBM Corporation 4 * 5 * Infineon DPS310 temperature and humidity sensor 6 * 7 * https://www.infineon.com/cms/en/product/sensor/pressure-sensors/pressure-sensors-for-iot/dps310/ 8 */ 9 10#include "qemu/osdep.h" 11#include "qemu/log.h" 12#include "hw/hw.h" 13#include "hw/i2c/i2c.h" 14#include "qapi/error.h" 15#include "qapi/visitor.h" 16#include "migration/vmstate.h" 17 18#define NUM_REGISTERS 0x33 19 20typedef struct DPS310State { 21 /*< private >*/ 22 I2CSlave i2c; 23 24 /*< public >*/ 25 uint8_t regs[NUM_REGISTERS]; 26 27 uint8_t len; 28 uint8_t pointer; 29 30} DPS310State; 31 32#define TYPE_DPS310 "dps310" 33#define DPS310(obj) OBJECT_CHECK(DPS310State, (obj), TYPE_DPS310) 34 35#define DPS310_PRS_B2 0x00 36#define DPS310_PRS_B1 0x01 37#define DPS310_PRS_B0 0x02 38#define DPS310_TMP_B2 0x03 39#define DPS310_TMP_B1 0x04 40#define DPS310_TMP_B0 0x05 41#define DPS310_PRS_CFG 0x06 42#define DPS310_TMP_CFG 0x07 43#define DPS310_TMP_RATE_BITS (0x70) 44#define DPS310_MEAS_CFG 0x08 45#define DPS310_MEAS_CTRL_BITS (0x07) 46#define DPS310_PRESSURE_EN BIT(0) 47#define DPS310_TEMP_EN BIT(1) 48#define DPS310_BACKGROUND BIT(2) 49#define DPS310_PRS_RDY BIT(4) 50#define DPS310_TMP_RDY BIT(5) 51#define DPS310_SENSOR_RDY BIT(6) 52#define DPS310_COEF_RDY BIT(7) 53#define DPS310_CFG_REG 0x09 54#define DPS310_RESET 0x0c 55#define DPS310_RESET_MAGIC (BIT(0) | BIT(3)) 56#define DPS310_COEF_BASE 0x10 57#define DPS310_COEF_LAST 0x21 58#define DPS310_COEF_SRC 0x28 59 60static void dps310_reset(DeviceState *dev) 61{ 62 DPS310State *s = DPS310(dev); 63 64 static const uint8_t regs_reset_state[sizeof(s->regs)] = { 65 0xfe, 0x2f, 0xee, 0x02, 0x69, 0xa6, 0x00, 0x80, 0xc7, 0x00, 0x00, 0x00, 66 0x00, 0x10, 0x00, 0x00, 0x0e, 0x1e, 0xdd, 0x13, 0xca, 0x5f, 0x21, 0x52, 67 0xf9, 0xc6, 0x04, 0xd1, 0xdb, 0x47, 0x00, 0x5b, 0xfb, 0x3a, 0x00, 0x00, 68 0x20, 0x49, 0x4e, 0xa5, 0x90, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 69 0x60, 0x15, 0x02 70 }; 71 72 memcpy(s->regs, regs_reset_state, sizeof(s->regs)); 73 s->pointer = 0; 74 75 /* TODO: assert these after some timeout ? */ 76 s->regs[DPS310_MEAS_CFG] = DPS310_COEF_RDY | DPS310_SENSOR_RDY 77 | DPS310_TMP_RDY | DPS310_PRS_RDY; 78} 79 80static uint8_t dps310_read(DPS310State *s, uint8_t reg) 81{ 82 if (reg >= sizeof(s->regs)) { 83 qemu_log_mask(LOG_GUEST_ERROR, "%s: register 0x%02x out of bounds\n", 84 __func__, s->pointer); 85 return 0xFF; 86 } 87 88 switch (reg) { 89 case DPS310_PRS_B2: 90 case DPS310_PRS_B1: 91 case DPS310_PRS_B0: 92 case DPS310_TMP_B2: 93 case DPS310_TMP_B1: 94 case DPS310_TMP_B0: 95 case DPS310_PRS_CFG: 96 case DPS310_TMP_CFG: 97 case DPS310_MEAS_CFG: 98 case DPS310_CFG_REG: 99 case DPS310_COEF_BASE...DPS310_COEF_LAST: 100 case DPS310_COEF_SRC: 101 case 0x32: /* Undocumented register to indicate workaround not required */ 102 return s->regs[reg]; 103 default: 104 qemu_log_mask(LOG_UNIMP, "%s: register 0x%02x unimplemented\n", 105 __func__, reg); 106 return 0xFF; 107 } 108} 109 110static void dps310_write(DPS310State *s, uint8_t reg, uint8_t data) 111{ 112 if (reg >= sizeof(s->regs)) { 113 qemu_log_mask(LOG_GUEST_ERROR, "%s: register %d out of bounds\n", 114 __func__, s->pointer); 115 return; 116 } 117 118 switch (reg) { 119 case DPS310_RESET: 120 if (data == DPS310_RESET_MAGIC) { 121 device_cold_reset(DEVICE(s)); 122 } 123 break; 124 case DPS310_PRS_CFG: 125 case DPS310_TMP_CFG: 126 case DPS310_MEAS_CFG: 127 case DPS310_CFG_REG: 128 s->regs[reg] = data; 129 break; 130 default: 131 qemu_log_mask(LOG_UNIMP, "%s: register 0x%02x unimplemented\n", 132 __func__, reg); 133 return; 134 } 135} 136 137static uint8_t dps310_rx(I2CSlave *i2c) 138{ 139 DPS310State *s = DPS310(i2c); 140 141 if (s->len == 1) { 142 return dps310_read(s, s->pointer++); 143 } else { 144 return 0xFF; 145 } 146} 147 148static int dps310_tx(I2CSlave *i2c, uint8_t data) 149{ 150 DPS310State *s = DPS310(i2c); 151 152 if (s->len == 0) { 153 /* 154 * first byte is the register pointer for a read or write 155 * operation 156 */ 157 s->pointer = data; 158 s->len++; 159 } else if (s->len == 1) { 160 dps310_write(s, s->pointer++, data); 161 } 162 163 return 0; 164} 165 166static int dps310_event(I2CSlave *i2c, enum i2c_event event) 167{ 168 DPS310State *s = DPS310(i2c); 169 170 switch (event) { 171 case I2C_START_SEND: 172 s->pointer = 0xFF; 173 s->len = 0; 174 break; 175 case I2C_START_RECV: 176 if (s->len != 1) { 177 qemu_log_mask(LOG_GUEST_ERROR, "%s: invalid recv sequence\n", 178 __func__); 179 } 180 break; 181 default: 182 break; 183 } 184 185 return 0; 186} 187 188static const VMStateDescription vmstate_dps310 = { 189 .name = "DPS310", 190 .version_id = 0, 191 .minimum_version_id = 0, 192 .fields = (VMStateField[]) { 193 VMSTATE_UINT8(len, DPS310State), 194 VMSTATE_UINT8_ARRAY(regs, DPS310State, NUM_REGISTERS), 195 VMSTATE_UINT8(pointer, DPS310State), 196 VMSTATE_I2C_SLAVE(i2c, DPS310State), 197 VMSTATE_END_OF_LIST() 198 } 199}; 200 201static void dps310_class_init(ObjectClass *klass, void *data) 202{ 203 DeviceClass *dc = DEVICE_CLASS(klass); 204 I2CSlaveClass *k = I2C_SLAVE_CLASS(klass); 205 206 k->event = dps310_event; 207 k->recv = dps310_rx; 208 k->send = dps310_tx; 209 dc->reset = dps310_reset; 210 dc->vmsd = &vmstate_dps310; 211} 212 213static const TypeInfo dps310_info = { 214 .name = TYPE_DPS310, 215 .parent = TYPE_I2C_SLAVE, 216 .instance_size = sizeof(DPS310State), 217 .class_init = dps310_class_init, 218}; 219 220static void dps310_register_types(void) 221{ 222 type_register_static(&dps310_info); 223} 224 225type_init(dps310_register_types)