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 "tmp105.h"
26 #include "qapi/error.h"
27 #include "qapi/visitor.h"
28 #include "qemu/module.h"
29 
tmp105_interrupt_update(TMP105State * s)30 static void tmp105_interrupt_update(TMP105State *s)
31 {
32     qemu_set_irq(s->pin, s->alarm ^ ((~s->config >> 2) & 1));	/* POL */
33 }
34 
tmp105_alarm_update(TMP105State * s)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) {					/* TM */
45         if (s->temperature >= s->limit[1])
46             s->alarm = 1;
47         else if (s->temperature < s->limit[0])
48             s->alarm = 1;
49     } else {
50         if (s->temperature >= s->limit[1])
51             s->alarm = 1;
52         else if (s->temperature < s->limit[0])
53             s->alarm = 0;
54     }
55 
56     tmp105_interrupt_update(s);
57 }
58 
tmp105_get_temperature(Object * obj,Visitor * v,const char * name,void * opaque,Error ** errp)59 static void tmp105_get_temperature(Object *obj, Visitor *v, const char *name,
60                                    void *opaque, Error **errp)
61 {
62     TMP105State *s = TMP105(obj);
63     int64_t value = s->temperature * 1000 / 256;
64 
65     visit_type_int(v, name, &value, errp);
66 }
67 
68 /* Units are 0.001 centigrades relative to 0 C.  s->temperature is 8.8
69  * fixed point, so units are 1/256 centigrades.  A simple ratio will do.
70  */
tmp105_set_temperature(Object * obj,Visitor * v,const char * name,void * opaque,Error ** errp)71 static void tmp105_set_temperature(Object *obj, Visitor *v, const char *name,
72                                    void *opaque, Error **errp)
73 {
74     TMP105State *s = TMP105(obj);
75     Error *local_err = NULL;
76     int64_t temp;
77 
78     visit_type_int(v, name, &temp, &local_err);
79     if (local_err) {
80         error_propagate(errp, local_err);
81         return;
82     }
83     if (temp >= 128000 || temp < -128000) {
84         error_setg(errp, "value %" PRId64 ".%03" PRIu64 " C is out of range",
85                    temp / 1000, temp % 1000);
86         return;
87     }
88 
89     s->temperature = (int16_t) (temp * 256 / 1000);
90 
91     tmp105_alarm_update(s);
92 }
93 
94 static const int tmp105_faultq[4] = { 1, 2, 4, 6 };
95 
tmp105_read(TMP105State * s)96 static void tmp105_read(TMP105State *s)
97 {
98     s->len = 0;
99 
100     if ((s->config >> 1) & 1) {					/* TM */
101         s->alarm = 0;
102         tmp105_interrupt_update(s);
103     }
104 
105     switch (s->pointer & 3) {
106     case TMP105_REG_TEMPERATURE:
107         s->buf[s->len ++] = (((uint16_t) s->temperature) >> 8);
108         s->buf[s->len ++] = (((uint16_t) s->temperature) >> 0) &
109                 (0xf0 << ((~s->config >> 5) & 3));		/* R */
110         break;
111 
112     case TMP105_REG_CONFIG:
113         s->buf[s->len ++] = s->config;
114         break;
115 
116     case TMP105_REG_T_LOW:
117         s->buf[s->len ++] = ((uint16_t) s->limit[0]) >> 8;
118         s->buf[s->len ++] = ((uint16_t) s->limit[0]) >> 0;
119         break;
120 
121     case TMP105_REG_T_HIGH:
122         s->buf[s->len ++] = ((uint16_t) s->limit[1]) >> 8;
123         s->buf[s->len ++] = ((uint16_t) s->limit[1]) >> 0;
124         break;
125     }
126 }
127 
tmp105_write(TMP105State * s)128 static void tmp105_write(TMP105State *s)
129 {
130     switch (s->pointer & 3) {
131     case TMP105_REG_TEMPERATURE:
132         break;
133 
134     case TMP105_REG_CONFIG:
135         if (s->buf[0] & ~s->config & (1 << 0))			/* SD */
136             printf("%s: TMP105 shutdown\n", __func__);
137         s->config = s->buf[0];
138         s->faults = tmp105_faultq[(s->config >> 3) & 3];	/* F */
139         tmp105_alarm_update(s);
140         break;
141 
142     case TMP105_REG_T_LOW:
143     case TMP105_REG_T_HIGH:
144         if (s->len >= 3)
145             s->limit[s->pointer & 1] = (int16_t)
146                     ((((uint16_t) s->buf[0]) << 8) | s->buf[1]);
147         tmp105_alarm_update(s);
148         break;
149     }
150 }
151 
tmp105_rx(I2CSlave * i2c)152 static uint8_t tmp105_rx(I2CSlave *i2c)
153 {
154     TMP105State *s = TMP105(i2c);
155 
156     if (s->len < 2) {
157         return s->buf[s->len ++];
158     } else {
159         return 0xff;
160     }
161 }
162 
tmp105_tx(I2CSlave * i2c,uint8_t data)163 static int tmp105_tx(I2CSlave *i2c, uint8_t data)
164 {
165     TMP105State *s = TMP105(i2c);
166 
167     if (s->len == 0) {
168         s->pointer = data;
169         s->len++;
170     } else {
171         if (s->len <= 2) {
172             s->buf[s->len - 1] = data;
173         }
174         s->len++;
175         tmp105_write(s);
176     }
177 
178     return 0;
179 }
180 
tmp105_event(I2CSlave * i2c,enum i2c_event event)181 static int tmp105_event(I2CSlave *i2c, enum i2c_event event)
182 {
183     TMP105State *s = TMP105(i2c);
184 
185     if (event == I2C_START_RECV) {
186         tmp105_read(s);
187     }
188 
189     s->len = 0;
190     return 0;
191 }
192 
tmp105_post_load(void * opaque,int version_id)193 static int tmp105_post_load(void *opaque, int version_id)
194 {
195     TMP105State *s = opaque;
196 
197     s->faults = tmp105_faultq[(s->config >> 3) & 3];		/* F */
198 
199     tmp105_interrupt_update(s);
200     return 0;
201 }
202 
203 static const VMStateDescription vmstate_tmp105 = {
204     .name = "TMP105",
205     .version_id = 0,
206     .minimum_version_id = 0,
207     .post_load = tmp105_post_load,
208     .fields = (VMStateField[]) {
209         VMSTATE_UINT8(len, TMP105State),
210         VMSTATE_UINT8_ARRAY(buf, TMP105State, 2),
211         VMSTATE_UINT8(pointer, TMP105State),
212         VMSTATE_UINT8(config, TMP105State),
213         VMSTATE_INT16(temperature, TMP105State),
214         VMSTATE_INT16_ARRAY(limit, TMP105State, 2),
215         VMSTATE_UINT8(alarm, TMP105State),
216         VMSTATE_I2C_SLAVE(i2c, TMP105State),
217         VMSTATE_END_OF_LIST()
218     }
219 };
220 
tmp105_reset(I2CSlave * i2c)221 static void tmp105_reset(I2CSlave *i2c)
222 {
223     TMP105State *s = TMP105(i2c);
224 
225     s->temperature = 0;
226     s->pointer = 0;
227     s->config = 0;
228     s->faults = tmp105_faultq[(s->config >> 3) & 3];
229     s->alarm = 0;
230 
231     tmp105_interrupt_update(s);
232 }
233 
tmp105_realize(DeviceState * dev,Error ** errp)234 static void tmp105_realize(DeviceState *dev, Error **errp)
235 {
236     I2CSlave *i2c = I2C_SLAVE(dev);
237     TMP105State *s = TMP105(i2c);
238 
239     qdev_init_gpio_out(&i2c->qdev, &s->pin, 1);
240 
241     tmp105_reset(&s->i2c);
242 }
243 
tmp105_initfn(Object * obj)244 static void tmp105_initfn(Object *obj)
245 {
246     object_property_add(obj, "temperature", "int",
247                         tmp105_get_temperature,
248                         tmp105_set_temperature, NULL, NULL, NULL);
249 }
250 
tmp105_class_init(ObjectClass * klass,void * data)251 static void tmp105_class_init(ObjectClass *klass, void *data)
252 {
253     DeviceClass *dc = DEVICE_CLASS(klass);
254     I2CSlaveClass *k = I2C_SLAVE_CLASS(klass);
255 
256     dc->realize = tmp105_realize;
257     k->event = tmp105_event;
258     k->recv = tmp105_rx;
259     k->send = tmp105_tx;
260     dc->vmsd = &vmstate_tmp105;
261 }
262 
263 static const TypeInfo tmp105_info = {
264     .name          = TYPE_TMP105,
265     .parent        = TYPE_I2C_SLAVE,
266     .instance_size = sizeof(TMP105State),
267     .instance_init = tmp105_initfn,
268     .class_init    = tmp105_class_init,
269 };
270 
tmp105_register_types(void)271 static void tmp105_register_types(void)
272 {
273     type_register_static(&tmp105_info);
274 }
275 
276 type_init(tmp105_register_types)
277