Revert r5274 which breaks savevm/loadvm
[qemu] / hw / tmp105.c
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
18  * along with this program; if not, write to the Free Software
19  * Foundation, Inc., 59 Temple Place, Suite 330, Boston,
20  * MA 02111-1307 USA
21  */
22
23 #include "hw.h"
24 #include "i2c.h"
25
26 struct tmp105_s {
27     i2c_slave i2c;
28     int len;
29     uint8_t buf[2];
30     qemu_irq pin;
31
32     uint8_t pointer;
33     uint8_t config;
34     int16_t temperature;
35     int16_t limit[2];
36     int faults;
37     int alarm;
38 };
39
40 static void tmp105_interrupt_update(struct tmp105_s *s)
41 {
42     qemu_set_irq(s->pin, s->alarm ^ ((~s->config >> 2) & 1));   /* POL */
43 }
44
45 static void tmp105_alarm_update(struct tmp105_s *s)
46 {
47     if ((s->config >> 0) & 1) {                                 /* SD */
48         if ((s->config >> 7) & 1)                               /* OS */
49             s->config &= ~(1 << 7);                             /* OS */
50         else
51             return;
52     }
53
54     if ((s->config >> 1) & 1) {                                 /* TM */
55         if (s->temperature >= s->limit[1])
56             s->alarm = 1;
57         else if (s->temperature < s->limit[0])
58             s->alarm = 1;
59     } else {
60         if (s->temperature >= s->limit[1])
61             s->alarm = 1;
62         else if (s->temperature < s->limit[0])
63             s->alarm = 0;
64     }
65
66     tmp105_interrupt_update(s);
67 }
68
69 /* Units are 0.001 centigrades relative to 0 C.  */
70 void tmp105_set(i2c_slave *i2c, int temp)
71 {
72     struct tmp105_s *s = (struct tmp105_s *) i2c;
73
74     if (temp >= 128000 || temp < -128000) {
75         fprintf(stderr, "%s: values is out of range (%i.%03i C)\n",
76                         __FUNCTION__, temp / 1000, temp % 1000);
77         exit(-1);
78     }
79
80     s->temperature = ((int16_t) (temp * 0x800 / 128000)) << 4;
81
82     tmp105_alarm_update(s);
83 }
84
85 static const int tmp105_faultq[4] = { 1, 2, 4, 6 };
86
87 static void tmp105_read(struct tmp105_s *s)
88 {
89     s->len = 0;
90
91     if ((s->config >> 1) & 1) {                                 /* TM */
92         s->alarm = 0;
93         tmp105_interrupt_update(s);
94     }
95
96     switch (s->pointer & 3) {
97     case 0:     /* Temperature */
98         s->buf[s->len ++] = (((uint16_t) s->temperature) >> 8);
99         s->buf[s->len ++] = (((uint16_t) s->temperature) >> 0) &
100                 (0xf0 << ((~s->config >> 5) & 3));              /* R */
101         break;
102
103     case 1:     /* Configuration */
104         s->buf[s->len ++] = s->config;
105         break;
106
107     case 2:     /* T_LOW */
108         s->buf[s->len ++] = ((uint16_t) s->limit[0]) >> 8;
109         s->buf[s->len ++] = ((uint16_t) s->limit[0]) >> 0;
110         break;
111
112     case 3:     /* T_HIGH */
113         s->buf[s->len ++] = ((uint16_t) s->limit[1]) >> 8;
114         s->buf[s->len ++] = ((uint16_t) s->limit[1]) >> 0;
115         break;
116     }
117 }
118
119 static void tmp105_write(struct tmp105_s *s)
120 {
121     switch (s->pointer & 3) {
122     case 0:     /* Temperature */
123         break;
124
125     case 1:     /* Configuration */
126         if (s->buf[0] & ~s->config & (1 << 0))                  /* SD */
127             printf("%s: TMP105 shutdown\n", __FUNCTION__);
128         s->config = s->buf[0];
129         s->faults = tmp105_faultq[(s->config >> 3) & 3];        /* F */
130         tmp105_alarm_update(s);
131         break;
132
133     case 2:     /* T_LOW */
134     case 3:     /* T_HIGH */
135         if (s->len >= 3)
136             s->limit[s->pointer & 1] = (int16_t)
137                     ((((uint16_t) s->buf[0]) << 8) | s->buf[1]);
138         tmp105_alarm_update(s);
139         break;
140     }
141 }
142
143 static int tmp105_rx(i2c_slave *i2c)
144 {
145     struct tmp105_s *s = (struct tmp105_s *) i2c;
146
147     if (s->len < 2)
148         return s->buf[s->len ++];
149     else
150         return 0xff;
151 }
152
153 static int tmp105_tx(i2c_slave *i2c, uint8_t data)
154 {
155     struct tmp105_s *s = (struct tmp105_s *) i2c;
156
157     if (!s->len ++)
158         s->pointer = data;
159     else {
160         if (s->len <= 2)
161             s->buf[s->len - 1] = data;
162         tmp105_write(s);
163     }
164
165     return 0;
166 }
167
168 static void tmp105_event(i2c_slave *i2c, enum i2c_event event)
169 {
170     struct tmp105_s *s = (struct tmp105_s *) i2c;
171
172     if (event == I2C_START_RECV)
173         tmp105_read(s);
174
175     s->len = 0;
176 }
177
178 static void tmp105_save(QEMUFile *f, void *opaque)
179 {
180     struct tmp105_s *s = (struct tmp105_s *) opaque;
181
182     qemu_put_byte(f, s->len);
183     qemu_put_8s(f, &s->buf[0]);
184     qemu_put_8s(f, &s->buf[1]);
185
186     qemu_put_8s(f, &s->pointer);
187     qemu_put_8s(f, &s->config);
188     qemu_put_be16s(f, (uint16_t *) &s->temperature);
189     qemu_put_be16s(f, (uint16_t *) &s->limit[0]);
190     qemu_put_be16s(f, (uint16_t *) &s->limit[1]);
191     qemu_put_byte(f, s->alarm);
192     s->faults = tmp105_faultq[(s->config >> 3) & 3];            /* F */
193
194     i2c_slave_save(f, &s->i2c);
195 }
196
197 static int tmp105_load(QEMUFile *f, void *opaque, int version_id)
198 {
199     struct tmp105_s *s = (struct tmp105_s *) opaque;
200
201     s->len = qemu_get_byte(f);
202     qemu_get_8s(f, &s->buf[0]);
203     qemu_get_8s(f, &s->buf[1]);
204
205     qemu_get_8s(f, &s->pointer);
206     qemu_get_8s(f, &s->config);
207     qemu_get_be16s(f, (uint16_t *) &s->temperature);
208     qemu_get_be16s(f, (uint16_t *) &s->limit[0]);
209     qemu_get_be16s(f, (uint16_t *) &s->limit[1]);
210     s->alarm = qemu_get_byte(f);
211
212     tmp105_interrupt_update(s);
213
214     i2c_slave_load(f, &s->i2c);
215     return 0;
216 }
217
218 void tmp105_reset(i2c_slave *i2c)
219 {
220     struct tmp105_s *s = (struct tmp105_s *) i2c;
221
222     s->temperature = 0;
223     s->pointer = 0;
224     s->config = 0;
225     s->faults = tmp105_faultq[(s->config >> 3) & 3];
226     s->alarm = 0;
227
228     tmp105_interrupt_update(s);
229 }
230
231 struct i2c_slave *tmp105_init(i2c_bus *bus, qemu_irq alarm)
232 {
233     struct tmp105_s *s = (struct tmp105_s *)
234             i2c_slave_init(bus, 0, sizeof(struct tmp105_s));
235
236     s->i2c.event = tmp105_event;
237     s->i2c.recv = tmp105_rx;
238     s->i2c.send = tmp105_tx;
239     s->pin = alarm;
240
241     tmp105_reset(&s->i2c);
242
243     register_savevm("TMP105", -1, 0, tmp105_save, tmp105_load, s);
244
245     return &s->i2c;
246 }