qemu

FORK: QEMU emulator
git clone https://git.neptards.moe/neptards/qemu.git
Log | Files | Refs | Submodules | LICENSE

tmp421.c (11220B)


      1 /*
      2  * Texas Instruments TMP421 temperature sensor.
      3  *
      4  * Copyright (c) 2016 IBM Corporation.
      5  *
      6  * Largely inspired by :
      7  *
      8  * Texas Instruments TMP105 temperature sensor.
      9  *
     10  * Copyright (C) 2008 Nokia Corporation
     11  * Written by Andrzej Zaborowski <andrew@openedhand.com>
     12  *
     13  * This program is free software; you can redistribute it and/or
     14  * modify it under the terms of the GNU General Public License as
     15  * published by the Free Software Foundation; either version 2 or
     16  * (at your option) version 3 of the License.
     17  *
     18  * This program is distributed in the hope that it will be useful,
     19  * but WITHOUT ANY WARRANTY; without even the implied warranty of
     20  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
     21  * GNU General Public License for more details.
     22  *
     23  * You should have received a copy of the GNU General Public License along
     24  * with this program; if not, see <http://www.gnu.org/licenses/>.
     25  */
     26 
     27 #include "qemu/osdep.h"
     28 #include "hw/i2c/i2c.h"
     29 #include "migration/vmstate.h"
     30 #include "qapi/error.h"
     31 #include "qapi/visitor.h"
     32 #include "qemu/module.h"
     33 #include "qom/object.h"
     34 
     35 /* Manufacturer / Device ID's */
     36 #define TMP421_MANUFACTURER_ID          0x55
     37 #define TMP421_DEVICE_ID                0x21
     38 #define TMP422_DEVICE_ID                0x22
     39 #define TMP423_DEVICE_ID                0x23
     40 
     41 typedef struct DeviceInfo {
     42     int model;
     43     const char *name;
     44 } DeviceInfo;
     45 
     46 static const DeviceInfo devices[] = {
     47     { TMP421_DEVICE_ID, "tmp421" },
     48     { TMP422_DEVICE_ID, "tmp422" },
     49     { TMP423_DEVICE_ID, "tmp423" },
     50 };
     51 
     52 struct TMP421State {
     53     /*< private >*/
     54     I2CSlave i2c;
     55     /*< public >*/
     56 
     57     int16_t temperature[4];
     58 
     59     uint8_t status;
     60     uint8_t config[2];
     61     uint8_t rate;
     62 
     63     uint8_t len;
     64     uint8_t buf[2];
     65     uint8_t pointer;
     66 
     67 };
     68 
     69 struct TMP421Class {
     70     I2CSlaveClass parent_class;
     71     DeviceInfo *dev;
     72 };
     73 
     74 #define TYPE_TMP421 "tmp421-generic"
     75 OBJECT_DECLARE_TYPE(TMP421State, TMP421Class, TMP421)
     76 
     77 
     78 /* the TMP421 registers */
     79 #define TMP421_STATUS_REG               0x08
     80 #define    TMP421_STATUS_BUSY             (1 << 7)
     81 #define TMP421_CONFIG_REG_1             0x09
     82 #define    TMP421_CONFIG_RANGE            (1 << 2)
     83 #define    TMP421_CONFIG_SHUTDOWN         (1 << 6)
     84 #define TMP421_CONFIG_REG_2             0x0A
     85 #define    TMP421_CONFIG_RC               (1 << 2)
     86 #define    TMP421_CONFIG_LEN              (1 << 3)
     87 #define    TMP421_CONFIG_REN              (1 << 4)
     88 #define    TMP421_CONFIG_REN2             (1 << 5)
     89 #define    TMP421_CONFIG_REN3             (1 << 6)
     90 
     91 #define TMP421_CONVERSION_RATE_REG      0x0B
     92 #define TMP421_ONE_SHOT                 0x0F
     93 
     94 #define TMP421_RESET                    0xFC
     95 #define TMP421_MANUFACTURER_ID_REG      0xFE
     96 #define TMP421_DEVICE_ID_REG            0xFF
     97 
     98 #define TMP421_TEMP_MSB0                0x00
     99 #define TMP421_TEMP_MSB1                0x01
    100 #define TMP421_TEMP_MSB2                0x02
    101 #define TMP421_TEMP_MSB3                0x03
    102 #define TMP421_TEMP_LSB0                0x10
    103 #define TMP421_TEMP_LSB1                0x11
    104 #define TMP421_TEMP_LSB2                0x12
    105 #define TMP421_TEMP_LSB3                0x13
    106 
    107 static const int32_t mins[2] = { -40000, -55000 };
    108 static const int32_t maxs[2] = { 127000, 150000 };
    109 
    110 static void tmp421_get_temperature(Object *obj, Visitor *v, const char *name,
    111                                    void *opaque, Error **errp)
    112 {
    113     TMP421State *s = TMP421(obj);
    114     bool ext_range = (s->config[0] & TMP421_CONFIG_RANGE);
    115     int offset = ext_range * 64 * 256;
    116     int64_t value;
    117     int tempid;
    118 
    119     if (sscanf(name, "temperature%d", &tempid) != 1) {
    120         error_setg(errp, "error reading %s: %s", name, g_strerror(errno));
    121         return;
    122     }
    123 
    124     if (tempid >= 4 || tempid < 0) {
    125         error_setg(errp, "error reading %s", name);
    126         return;
    127     }
    128 
    129     value = ((s->temperature[tempid] - offset) * 1000 + 128) / 256;
    130 
    131     visit_type_int(v, name, &value, errp);
    132 }
    133 
    134 /* Units are 0.001 centigrades relative to 0 C.  s->temperature is 8.8
    135  * fixed point, so units are 1/256 centigrades.  A simple ratio will do.
    136  */
    137 static void tmp421_set_temperature(Object *obj, Visitor *v, const char *name,
    138                                    void *opaque, Error **errp)
    139 {
    140     TMP421State *s = TMP421(obj);
    141     int64_t temp;
    142     bool ext_range = (s->config[0] & TMP421_CONFIG_RANGE);
    143     int offset = ext_range * 64 * 256;
    144     int tempid;
    145 
    146     if (!visit_type_int(v, name, &temp, errp)) {
    147         return;
    148     }
    149 
    150     if (temp >= maxs[ext_range] || temp < mins[ext_range]) {
    151         error_setg(errp, "value %" PRId64 ".%03" PRIu64 " C is out of range",
    152                    temp / 1000, temp % 1000);
    153         return;
    154     }
    155 
    156     if (sscanf(name, "temperature%d", &tempid) != 1) {
    157         error_setg(errp, "error reading %s: %s", name, g_strerror(errno));
    158         return;
    159     }
    160 
    161     if (tempid >= 4 || tempid < 0) {
    162         error_setg(errp, "error reading %s", name);
    163         return;
    164     }
    165 
    166     s->temperature[tempid] = (int16_t) ((temp * 256 - 128) / 1000) + offset;
    167 }
    168 
    169 static void tmp421_read(TMP421State *s)
    170 {
    171     TMP421Class *sc = TMP421_GET_CLASS(s);
    172 
    173     s->len = 0;
    174 
    175     switch (s->pointer) {
    176     case TMP421_MANUFACTURER_ID_REG:
    177         s->buf[s->len++] = TMP421_MANUFACTURER_ID;
    178         break;
    179     case TMP421_DEVICE_ID_REG:
    180         s->buf[s->len++] = sc->dev->model;
    181         break;
    182     case TMP421_CONFIG_REG_1:
    183         s->buf[s->len++] = s->config[0];
    184         break;
    185     case TMP421_CONFIG_REG_2:
    186         s->buf[s->len++] = s->config[1];
    187         break;
    188     case TMP421_CONVERSION_RATE_REG:
    189         s->buf[s->len++] = s->rate;
    190         break;
    191     case TMP421_STATUS_REG:
    192         s->buf[s->len++] = s->status;
    193         break;
    194 
    195         /* FIXME: check for channel enablement in config registers */
    196     case TMP421_TEMP_MSB0:
    197         s->buf[s->len++] = (((uint16_t) s->temperature[0]) >> 8);
    198         s->buf[s->len++] = (((uint16_t) s->temperature[0]) >> 0) & 0xf0;
    199         break;
    200     case TMP421_TEMP_MSB1:
    201         s->buf[s->len++] = (((uint16_t) s->temperature[1]) >> 8);
    202         s->buf[s->len++] = (((uint16_t) s->temperature[1]) >> 0) & 0xf0;
    203         break;
    204     case TMP421_TEMP_MSB2:
    205         s->buf[s->len++] = (((uint16_t) s->temperature[2]) >> 8);
    206         s->buf[s->len++] = (((uint16_t) s->temperature[2]) >> 0) & 0xf0;
    207         break;
    208     case TMP421_TEMP_MSB3:
    209         s->buf[s->len++] = (((uint16_t) s->temperature[3]) >> 8);
    210         s->buf[s->len++] = (((uint16_t) s->temperature[3]) >> 0) & 0xf0;
    211         break;
    212     case TMP421_TEMP_LSB0:
    213         s->buf[s->len++] = (((uint16_t) s->temperature[0]) >> 0) & 0xf0;
    214         break;
    215     case TMP421_TEMP_LSB1:
    216         s->buf[s->len++] = (((uint16_t) s->temperature[1]) >> 0) & 0xf0;
    217         break;
    218     case TMP421_TEMP_LSB2:
    219         s->buf[s->len++] = (((uint16_t) s->temperature[2]) >> 0) & 0xf0;
    220         break;
    221     case TMP421_TEMP_LSB3:
    222         s->buf[s->len++] = (((uint16_t) s->temperature[3]) >> 0) & 0xf0;
    223         break;
    224     }
    225 }
    226 
    227 static void tmp421_reset(I2CSlave *i2c);
    228 
    229 static void tmp421_write(TMP421State *s)
    230 {
    231     switch (s->pointer) {
    232     case TMP421_CONVERSION_RATE_REG:
    233         s->rate = s->buf[0];
    234         break;
    235     case TMP421_CONFIG_REG_1:
    236         s->config[0] = s->buf[0];
    237         break;
    238     case TMP421_CONFIG_REG_2:
    239         s->config[1] = s->buf[0];
    240         break;
    241     case TMP421_RESET:
    242         tmp421_reset(I2C_SLAVE(s));
    243         break;
    244     }
    245 }
    246 
    247 static uint8_t tmp421_rx(I2CSlave *i2c)
    248 {
    249     TMP421State *s = TMP421(i2c);
    250 
    251     if (s->len < 2) {
    252         return s->buf[s->len++];
    253     } else {
    254         return 0xff;
    255     }
    256 }
    257 
    258 static int tmp421_tx(I2CSlave *i2c, uint8_t data)
    259 {
    260     TMP421State *s = TMP421(i2c);
    261 
    262     if (s->len == 0) {
    263         /* first byte is the register pointer for a read or write
    264          * operation */
    265         s->pointer = data;
    266         s->len++;
    267     } else if (s->len == 1) {
    268         /* second byte is the data to write. The device only supports
    269          * one byte writes */
    270         s->buf[0] = data;
    271         tmp421_write(s);
    272     }
    273 
    274     return 0;
    275 }
    276 
    277 static int tmp421_event(I2CSlave *i2c, enum i2c_event event)
    278 {
    279     TMP421State *s = TMP421(i2c);
    280 
    281     if (event == I2C_START_RECV) {
    282         tmp421_read(s);
    283     }
    284 
    285     s->len = 0;
    286     return 0;
    287 }
    288 
    289 static const VMStateDescription vmstate_tmp421 = {
    290     .name = "TMP421",
    291     .version_id = 0,
    292     .minimum_version_id = 0,
    293     .fields = (VMStateField[]) {
    294         VMSTATE_UINT8(len, TMP421State),
    295         VMSTATE_UINT8_ARRAY(buf, TMP421State, 2),
    296         VMSTATE_UINT8(pointer, TMP421State),
    297         VMSTATE_UINT8_ARRAY(config, TMP421State, 2),
    298         VMSTATE_UINT8(status, TMP421State),
    299         VMSTATE_UINT8(rate, TMP421State),
    300         VMSTATE_INT16_ARRAY(temperature, TMP421State, 4),
    301         VMSTATE_I2C_SLAVE(i2c, TMP421State),
    302         VMSTATE_END_OF_LIST()
    303     }
    304 };
    305 
    306 static void tmp421_reset(I2CSlave *i2c)
    307 {
    308     TMP421State *s = TMP421(i2c);
    309     TMP421Class *sc = TMP421_GET_CLASS(s);
    310 
    311     memset(s->temperature, 0, sizeof(s->temperature));
    312     s->pointer = 0;
    313 
    314     s->config[0] = 0; /* TMP421_CONFIG_RANGE */
    315 
    316      /* resistance correction and channel enablement */
    317     switch (sc->dev->model) {
    318     case TMP421_DEVICE_ID:
    319         s->config[1] = 0x1c;
    320         break;
    321     case TMP422_DEVICE_ID:
    322         s->config[1] = 0x3c;
    323         break;
    324     case TMP423_DEVICE_ID:
    325         s->config[1] = 0x7c;
    326         break;
    327     }
    328 
    329     s->rate = 0x7;       /* 8Hz */
    330     s->status = 0;
    331 }
    332 
    333 static void tmp421_realize(DeviceState *dev, Error **errp)
    334 {
    335     TMP421State *s = TMP421(dev);
    336 
    337     tmp421_reset(&s->i2c);
    338 }
    339 
    340 static void tmp421_class_init(ObjectClass *klass, void *data)
    341 {
    342     DeviceClass *dc = DEVICE_CLASS(klass);
    343     I2CSlaveClass *k = I2C_SLAVE_CLASS(klass);
    344     TMP421Class *sc = TMP421_CLASS(klass);
    345 
    346     dc->realize = tmp421_realize;
    347     k->event = tmp421_event;
    348     k->recv = tmp421_rx;
    349     k->send = tmp421_tx;
    350     dc->vmsd = &vmstate_tmp421;
    351     sc->dev = (DeviceInfo *) data;
    352 
    353     object_class_property_add(klass, "temperature0", "int",
    354                               tmp421_get_temperature,
    355                               tmp421_set_temperature, NULL, NULL);
    356     object_class_property_add(klass, "temperature1", "int",
    357                               tmp421_get_temperature,
    358                               tmp421_set_temperature, NULL, NULL);
    359     object_class_property_add(klass, "temperature2", "int",
    360                               tmp421_get_temperature,
    361                               tmp421_set_temperature, NULL, NULL);
    362     object_class_property_add(klass, "temperature3", "int",
    363                               tmp421_get_temperature,
    364                               tmp421_set_temperature, NULL, NULL);
    365 }
    366 
    367 static const TypeInfo tmp421_info = {
    368     .name          = TYPE_TMP421,
    369     .parent        = TYPE_I2C_SLAVE,
    370     .instance_size = sizeof(TMP421State),
    371     .class_size    = sizeof(TMP421Class),
    372     .abstract      = true,
    373 };
    374 
    375 static void tmp421_register_types(void)
    376 {
    377     int i;
    378 
    379     type_register_static(&tmp421_info);
    380     for (i = 0; i < ARRAY_SIZE(devices); ++i) {
    381         TypeInfo ti = {
    382             .name       = devices[i].name,
    383             .parent     = TYPE_TMP421,
    384             .class_init = tmp421_class_init,
    385             .class_data = (void *) &devices[i],
    386         };
    387         type_register(&ti);
    388     }
    389 }
    390 
    391 type_init(tmp421_register_types)