qemu

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

tmp105.c (8708B)


      1 /*
      2  * Texas Instruments TMP105 temperature sensor.
      3  *
      4  * Copyright (C) 2008 Nokia Corporation
      5  * Written by Andrzej Zaborowski <andrew@openedhand.com>
      6  *
      7  * This program is free software; you can redistribute it and/or
      8  * modify it under the terms of the GNU General Public License as
      9  * published by the Free Software Foundation; either version 2 or
     10  * (at your option) version 3 of the License.
     11  *
     12  * This program is distributed in the hope that it will be useful,
     13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
     14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
     15  * GNU General Public License for more details.
     16  *
     17  * You should have received a copy of the GNU General Public License along
     18  * with this program; if not, see <http://www.gnu.org/licenses/>.
     19  */
     20 
     21 #include "qemu/osdep.h"
     22 #include "hw/i2c/i2c.h"
     23 #include "hw/irq.h"
     24 #include "migration/vmstate.h"
     25 #include "hw/sensor/tmp105.h"
     26 #include "qapi/error.h"
     27 #include "qapi/visitor.h"
     28 #include "qemu/module.h"
     29 
     30 static void tmp105_interrupt_update(TMP105State *s)
     31 {
     32     qemu_set_irq(s->pin, s->alarm ^ ((~s->config >> 2) & 1));	/* POL */
     33 }
     34 
     35 static void tmp105_alarm_update(TMP105State *s)
     36 {
     37     if ((s->config >> 0) & 1) {					/* SD */
     38         if ((s->config >> 7) & 1)				/* OS */
     39             s->config &= ~(1 << 7);				/* OS */
     40         else
     41             return;
     42     }
     43 
     44     if (s->config >> 1 & 1) {
     45         /*
     46          * TM == 1 : Interrupt mode. We signal Alert when the
     47          * temperature rises above T_high, and expect the guest to clear
     48          * it (eg by reading a device register).
     49          */
     50         if (s->detect_falling) {
     51             if (s->temperature < s->limit[0]) {
     52                 s->alarm = 1;
     53                 s->detect_falling = false;
     54             }
     55         } else {
     56             if (s->temperature >= s->limit[1]) {
     57                 s->alarm = 1;
     58                 s->detect_falling = true;
     59             }
     60         }
     61     } else {
     62         /*
     63          * TM == 0 : Comparator mode. We signal Alert when the temperature
     64          * rises above T_high, and stop signalling it when the temperature
     65          * falls below T_low.
     66          */
     67         if (s->detect_falling) {
     68             if (s->temperature < s->limit[0]) {
     69                 s->alarm = 0;
     70                 s->detect_falling = false;
     71             }
     72         } else {
     73             if (s->temperature >= s->limit[1]) {
     74                 s->alarm = 1;
     75                 s->detect_falling = true;
     76             }
     77         }
     78     }
     79 
     80     tmp105_interrupt_update(s);
     81 }
     82 
     83 static void tmp105_get_temperature(Object *obj, Visitor *v, const char *name,
     84                                    void *opaque, Error **errp)
     85 {
     86     TMP105State *s = TMP105(obj);
     87     int64_t value = s->temperature * 1000 / 256;
     88 
     89     visit_type_int(v, name, &value, errp);
     90 }
     91 
     92 /* Units are 0.001 centigrades relative to 0 C.  s->temperature is 8.8
     93  * fixed point, so units are 1/256 centigrades.  A simple ratio will do.
     94  */
     95 static void tmp105_set_temperature(Object *obj, Visitor *v, const char *name,
     96                                    void *opaque, Error **errp)
     97 {
     98     TMP105State *s = TMP105(obj);
     99     int64_t temp;
    100 
    101     if (!visit_type_int(v, name, &temp, errp)) {
    102         return;
    103     }
    104     if (temp >= 128000 || temp < -128000) {
    105         error_setg(errp, "value %" PRId64 ".%03" PRIu64 " C is out of range",
    106                    temp / 1000, temp % 1000);
    107         return;
    108     }
    109 
    110     s->temperature = (int16_t) (temp * 256 / 1000);
    111 
    112     tmp105_alarm_update(s);
    113 }
    114 
    115 static const int tmp105_faultq[4] = { 1, 2, 4, 6 };
    116 
    117 static void tmp105_read(TMP105State *s)
    118 {
    119     s->len = 0;
    120 
    121     if ((s->config >> 1) & 1) {					/* TM */
    122         s->alarm = 0;
    123         tmp105_interrupt_update(s);
    124     }
    125 
    126     switch (s->pointer & 3) {
    127     case TMP105_REG_TEMPERATURE:
    128         s->buf[s->len ++] = (((uint16_t) s->temperature) >> 8);
    129         s->buf[s->len ++] = (((uint16_t) s->temperature) >> 0) &
    130                 (0xf0 << ((~s->config >> 5) & 3));		/* R */
    131         break;
    132 
    133     case TMP105_REG_CONFIG:
    134         s->buf[s->len ++] = s->config;
    135         break;
    136 
    137     case TMP105_REG_T_LOW:
    138         s->buf[s->len ++] = ((uint16_t) s->limit[0]) >> 8;
    139         s->buf[s->len ++] = ((uint16_t) s->limit[0]) >> 0;
    140         break;
    141 
    142     case TMP105_REG_T_HIGH:
    143         s->buf[s->len ++] = ((uint16_t) s->limit[1]) >> 8;
    144         s->buf[s->len ++] = ((uint16_t) s->limit[1]) >> 0;
    145         break;
    146     }
    147 }
    148 
    149 static void tmp105_write(TMP105State *s)
    150 {
    151     switch (s->pointer & 3) {
    152     case TMP105_REG_TEMPERATURE:
    153         break;
    154 
    155     case TMP105_REG_CONFIG:
    156         if (s->buf[0] & ~s->config & (1 << 0))			/* SD */
    157             printf("%s: TMP105 shutdown\n", __func__);
    158         s->config = s->buf[0];
    159         s->faults = tmp105_faultq[(s->config >> 3) & 3];	/* F */
    160         tmp105_alarm_update(s);
    161         break;
    162 
    163     case TMP105_REG_T_LOW:
    164     case TMP105_REG_T_HIGH:
    165         if (s->len >= 3)
    166             s->limit[s->pointer & 1] = (int16_t)
    167                     ((((uint16_t) s->buf[0]) << 8) | s->buf[1]);
    168         tmp105_alarm_update(s);
    169         break;
    170     }
    171 }
    172 
    173 static uint8_t tmp105_rx(I2CSlave *i2c)
    174 {
    175     TMP105State *s = TMP105(i2c);
    176 
    177     if (s->len < 2) {
    178         return s->buf[s->len ++];
    179     } else {
    180         return 0xff;
    181     }
    182 }
    183 
    184 static int tmp105_tx(I2CSlave *i2c, uint8_t data)
    185 {
    186     TMP105State *s = TMP105(i2c);
    187 
    188     if (s->len == 0) {
    189         s->pointer = data;
    190         s->len++;
    191     } else {
    192         if (s->len <= 2) {
    193             s->buf[s->len - 1] = data;
    194         }
    195         s->len++;
    196         tmp105_write(s);
    197     }
    198 
    199     return 0;
    200 }
    201 
    202 static int tmp105_event(I2CSlave *i2c, enum i2c_event event)
    203 {
    204     TMP105State *s = TMP105(i2c);
    205 
    206     if (event == I2C_START_RECV) {
    207         tmp105_read(s);
    208     }
    209 
    210     s->len = 0;
    211     return 0;
    212 }
    213 
    214 static int tmp105_post_load(void *opaque, int version_id)
    215 {
    216     TMP105State *s = opaque;
    217 
    218     s->faults = tmp105_faultq[(s->config >> 3) & 3];		/* F */
    219 
    220     tmp105_interrupt_update(s);
    221     return 0;
    222 }
    223 
    224 static bool detect_falling_needed(void *opaque)
    225 {
    226     TMP105State *s = opaque;
    227 
    228     /*
    229      * We only need to migrate the detect_falling bool if it's set;
    230      * for migration from older machines we assume that it is false
    231      * (ie temperature is not out of range).
    232      */
    233     return s->detect_falling;
    234 }
    235 
    236 static const VMStateDescription vmstate_tmp105_detect_falling = {
    237     .name = "TMP105/detect-falling",
    238     .version_id = 1,
    239     .minimum_version_id = 1,
    240     .needed = detect_falling_needed,
    241     .fields = (VMStateField[]) {
    242         VMSTATE_BOOL(detect_falling, TMP105State),
    243         VMSTATE_END_OF_LIST()
    244     }
    245 };
    246 
    247 static const VMStateDescription vmstate_tmp105 = {
    248     .name = "TMP105",
    249     .version_id = 0,
    250     .minimum_version_id = 0,
    251     .post_load = tmp105_post_load,
    252     .fields = (VMStateField[]) {
    253         VMSTATE_UINT8(len, TMP105State),
    254         VMSTATE_UINT8_ARRAY(buf, TMP105State, 2),
    255         VMSTATE_UINT8(pointer, TMP105State),
    256         VMSTATE_UINT8(config, TMP105State),
    257         VMSTATE_INT16(temperature, TMP105State),
    258         VMSTATE_INT16_ARRAY(limit, TMP105State, 2),
    259         VMSTATE_UINT8(alarm, TMP105State),
    260         VMSTATE_I2C_SLAVE(i2c, TMP105State),
    261         VMSTATE_END_OF_LIST()
    262     },
    263     .subsections = (const VMStateDescription*[]) {
    264         &vmstate_tmp105_detect_falling,
    265         NULL
    266     }
    267 };
    268 
    269 static void tmp105_reset(I2CSlave *i2c)
    270 {
    271     TMP105State *s = TMP105(i2c);
    272 
    273     s->temperature = 0;
    274     s->pointer = 0;
    275     s->config = 0;
    276     s->faults = tmp105_faultq[(s->config >> 3) & 3];
    277     s->alarm = 0;
    278     s->detect_falling = false;
    279 
    280     s->limit[0] = 0x4b00; /* T_LOW, 75 degrees C */
    281     s->limit[1] = 0x5000; /* T_HIGH, 80 degrees C */
    282 
    283     tmp105_interrupt_update(s);
    284 }
    285 
    286 static void tmp105_realize(DeviceState *dev, Error **errp)
    287 {
    288     I2CSlave *i2c = I2C_SLAVE(dev);
    289     TMP105State *s = TMP105(i2c);
    290 
    291     qdev_init_gpio_out(&i2c->qdev, &s->pin, 1);
    292 
    293     tmp105_reset(&s->i2c);
    294 }
    295 
    296 static void tmp105_initfn(Object *obj)
    297 {
    298     object_property_add(obj, "temperature", "int",
    299                         tmp105_get_temperature,
    300                         tmp105_set_temperature, NULL, NULL);
    301 }
    302 
    303 static void tmp105_class_init(ObjectClass *klass, void *data)
    304 {
    305     DeviceClass *dc = DEVICE_CLASS(klass);
    306     I2CSlaveClass *k = I2C_SLAVE_CLASS(klass);
    307 
    308     dc->realize = tmp105_realize;
    309     k->event = tmp105_event;
    310     k->recv = tmp105_rx;
    311     k->send = tmp105_tx;
    312     dc->vmsd = &vmstate_tmp105;
    313 }
    314 
    315 static const TypeInfo tmp105_info = {
    316     .name          = TYPE_TMP105,
    317     .parent        = TYPE_I2C_SLAVE,
    318     .instance_size = sizeof(TMP105State),
    319     .instance_init = tmp105_initfn,
    320     .class_init    = tmp105_class_init,
    321 };
    322 
    323 static void tmp105_register_types(void)
    324 {
    325     type_register_static(&tmp105_info);
    326 }
    327 
    328 type_init(tmp105_register_types)