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