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