/* * SMSC EMC141X temperature sensor. * * Copyright (c) 2020 Bytedance Corporation * Written by John Wang * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 or * (at your option) version 3 of the License. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, see . */ #include "qemu/osdep.h" #include "hw/i2c/i2c.h" #include "migration/vmstate.h" #include "qapi/error.h" #include "qapi/visitor.h" #include "qemu/module.h" #include "qom/object.h" #include "hw/sensor/emc141x_regs.h" #define SENSORS_COUNT_MAX 4 struct EMC141XState { I2CSlave parent_obj; struct { uint8_t raw_temp_min; uint8_t raw_temp_current; uint8_t raw_temp_max; } sensor[SENSORS_COUNT_MAX]; uint8_t len; uint8_t data; uint8_t pointer; }; struct EMC141XClass { I2CSlaveClass parent_class; uint8_t model; unsigned sensors_count; }; #define TYPE_EMC141X "emc141x" OBJECT_DECLARE_TYPE(EMC141XState, EMC141XClass, EMC141X) static void emc141x_get_temperature(Object *obj, Visitor *v, const char *name, void *opaque, Error **errp) { EMC141XState *s = EMC141X(obj); EMC141XClass *sc = EMC141X_GET_CLASS(s); int64_t value; unsigned tempid; if (sscanf(name, "temperature%u", &tempid) != 1) { error_setg(errp, "error reading %s: %s", name, g_strerror(errno)); return; } if (tempid >= sc->sensors_count) { error_setg(errp, "error reading %s", name); return; } value = s->sensor[tempid].raw_temp_current * 1000; visit_type_int(v, name, &value, errp); } static void emc141x_set_temperature(Object *obj, Visitor *v, const char *name, void *opaque, Error **errp) { EMC141XState *s = EMC141X(obj); EMC141XClass *sc = EMC141X_GET_CLASS(s); int64_t temp; unsigned tempid; if (!visit_type_int(v, name, &temp, errp)) { return; } if (sscanf(name, "temperature%u", &tempid) != 1) { error_setg(errp, "error reading %s: %s", name, g_strerror(errno)); return; } if (tempid >= sc->sensors_count) { error_setg(errp, "error reading %s", name); return; } s->sensor[tempid].raw_temp_current = temp / 1000; } static void emc141x_read(EMC141XState *s) { EMC141XClass *sc = EMC141X_GET_CLASS(s); switch (s->pointer) { case EMC141X_DEVICE_ID: s->data = sc->model; break; case EMC141X_MANUFACTURER_ID: s->data = MANUFACTURER_ID; break; case EMC141X_REVISION: s->data = REVISION; break; case EMC141X_TEMP_HIGH0: s->data = s->sensor[0].raw_temp_current; break; case EMC141X_TEMP_HIGH1: s->data = s->sensor[1].raw_temp_current; break; case EMC141X_TEMP_HIGH2: s->data = s->sensor[2].raw_temp_current; break; case EMC141X_TEMP_HIGH3: s->data = s->sensor[3].raw_temp_current; break; case EMC141X_TEMP_MAX_HIGH0: s->data = s->sensor[0].raw_temp_max; break; case EMC141X_TEMP_MAX_HIGH1: s->data = s->sensor[1].raw_temp_max; break; case EMC141X_TEMP_MAX_HIGH2: s->data = s->sensor[2].raw_temp_max; break; case EMC141X_TEMP_MAX_HIGH3: s->data = s->sensor[3].raw_temp_max; break; case EMC141X_TEMP_MIN_HIGH0: s->data = s->sensor[0].raw_temp_min; break; case EMC141X_TEMP_MIN_HIGH1: s->data = s->sensor[1].raw_temp_min; break; case EMC141X_TEMP_MIN_HIGH2: s->data = s->sensor[2].raw_temp_min; break; case EMC141X_TEMP_MIN_HIGH3: s->data = s->sensor[3].raw_temp_min; break; default: s->data = 0; } } static void emc141x_write(EMC141XState *s) { switch (s->pointer) { case EMC141X_TEMP_MAX_HIGH0: s->sensor[0].raw_temp_max = s->data; break; case EMC141X_TEMP_MAX_HIGH1: s->sensor[1].raw_temp_max = s->data; break; case EMC141X_TEMP_MAX_HIGH2: s->sensor[2].raw_temp_max = s->data; break; case EMC141X_TEMP_MAX_HIGH3: s->sensor[3].raw_temp_max = s->data; break; case EMC141X_TEMP_MIN_HIGH0: s->sensor[0].raw_temp_min = s->data; break; case EMC141X_TEMP_MIN_HIGH1: s->sensor[1].raw_temp_min = s->data; break; case EMC141X_TEMP_MIN_HIGH2: s->sensor[2].raw_temp_min = s->data; break; case EMC141X_TEMP_MIN_HIGH3: s->sensor[3].raw_temp_min = s->data; break; default: s->data = 0; } } static uint8_t emc141x_rx(I2CSlave *i2c) { EMC141XState *s = EMC141X(i2c); if (s->len == 0) { s->len++; return s->data; } else { return 0xff; } } static int emc141x_tx(I2CSlave *i2c, uint8_t data) { EMC141XState *s = EMC141X(i2c); if (s->len == 0) { /* first byte is the reg pointer */ s->pointer = data; s->len++; } else if (s->len == 1) { s->data = data; emc141x_write(s); } return 0; } static int emc141x_event(I2CSlave *i2c, enum i2c_event event) { EMC141XState *s = EMC141X(i2c); if (event == I2C_START_RECV) { emc141x_read(s); } s->len = 0; return 0; } static const VMStateDescription vmstate_emc141x = { .name = "EMC141X", .version_id = 0, .minimum_version_id = 0, .fields = (const VMStateField[]) { VMSTATE_UINT8(len, EMC141XState), VMSTATE_UINT8(data, EMC141XState), VMSTATE_UINT8(pointer, EMC141XState), VMSTATE_I2C_SLAVE(parent_obj, EMC141XState), VMSTATE_END_OF_LIST() } }; static void emc141x_reset(DeviceState *dev) { EMC141XState *s = EMC141X(dev); int i; for (i = 0; i < SENSORS_COUNT_MAX; i++) { s->sensor[i].raw_temp_max = 0x55; } s->pointer = 0; s->len = 0; } static void emc141x_initfn(Object *obj) { object_property_add(obj, "temperature0", "int", emc141x_get_temperature, emc141x_set_temperature, NULL, NULL); object_property_add(obj, "temperature1", "int", emc141x_get_temperature, emc141x_set_temperature, NULL, NULL); object_property_add(obj, "temperature2", "int", emc141x_get_temperature, emc141x_set_temperature, NULL, NULL); object_property_add(obj, "temperature3", "int", emc141x_get_temperature, emc141x_set_temperature, NULL, NULL); } static void emc141x_class_init(ObjectClass *klass, void *data) { DeviceClass *dc = DEVICE_CLASS(klass); I2CSlaveClass *k = I2C_SLAVE_CLASS(klass); device_class_set_legacy_reset(dc, emc141x_reset); k->event = emc141x_event; k->recv = emc141x_rx; k->send = emc141x_tx; dc->vmsd = &vmstate_emc141x; } static void emc1413_class_init(ObjectClass *klass, void *data) { EMC141XClass *ec = EMC141X_CLASS(klass); emc141x_class_init(klass, data); ec->model = EMC1413_DEVICE_ID; ec->sensors_count = 3; } static void emc1414_class_init(ObjectClass *klass, void *data) { EMC141XClass *ec = EMC141X_CLASS(klass); emc141x_class_init(klass, data); ec->model = EMC1414_DEVICE_ID; ec->sensors_count = 4; } static const TypeInfo emc141x_info = { .name = TYPE_EMC141X, .parent = TYPE_I2C_SLAVE, .instance_size = sizeof(EMC141XState), .class_size = sizeof(EMC141XClass), .instance_init = emc141x_initfn, .abstract = true, }; static const TypeInfo emc1413_info = { .name = "emc1413", .parent = TYPE_EMC141X, .class_init = emc1413_class_init, }; static const TypeInfo emc1414_info = { .name = "emc1414", .parent = TYPE_EMC141X, .class_init = emc1414_class_init, }; static void emc141x_register_types(void) { type_register_static(&emc141x_info); type_register_static(&emc1413_info); type_register_static(&emc1414_info); } type_init(emc141x_register_types)