root / hw / tmp105.c @ b4b784fe
History | View | Annotate | Download (6 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 |
uint8_t 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 |
uint8_t 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_post_save(void *opaque) |
177 |
{ |
178 |
TMP105State *s = opaque; |
179 |
s->faults = tmp105_faultq[(s->config >> 3) & 3]; /* F */ |
180 |
} |
181 |
|
182 |
static int tmp105_post_load(void *opaque, int version_id) |
183 |
{ |
184 |
TMP105State *s = opaque; |
185 |
|
186 |
tmp105_interrupt_update(s); |
187 |
return 0; |
188 |
} |
189 |
|
190 |
static const VMStateDescription vmstate_tmp105 = { |
191 |
.name = "TMP105",
|
192 |
.version_id = 0,
|
193 |
.minimum_version_id = 0,
|
194 |
.minimum_version_id_old = 0,
|
195 |
.post_save = tmp105_post_save, |
196 |
.post_load = tmp105_post_load, |
197 |
.fields = (VMStateField []) { |
198 |
VMSTATE_UINT8(len, TMP105State), |
199 |
VMSTATE_UINT8_ARRAY(buf, TMP105State, 2),
|
200 |
VMSTATE_UINT8(pointer, TMP105State), |
201 |
VMSTATE_UINT8(config, TMP105State), |
202 |
VMSTATE_INT16(temperature, TMP105State), |
203 |
VMSTATE_INT16_ARRAY(limit, TMP105State, 2),
|
204 |
VMSTATE_UINT8(alarm, TMP105State), |
205 |
VMSTATE_I2C_SLAVE(i2c, TMP105State), |
206 |
VMSTATE_END_OF_LIST() |
207 |
} |
208 |
}; |
209 |
|
210 |
static void tmp105_reset(i2c_slave *i2c) |
211 |
{ |
212 |
TMP105State *s = (TMP105State *) i2c; |
213 |
|
214 |
s->temperature = 0;
|
215 |
s->pointer = 0;
|
216 |
s->config = 0;
|
217 |
s->faults = tmp105_faultq[(s->config >> 3) & 3]; |
218 |
s->alarm = 0;
|
219 |
|
220 |
tmp105_interrupt_update(s); |
221 |
} |
222 |
|
223 |
static int tmp105_init(i2c_slave *i2c) |
224 |
{ |
225 |
TMP105State *s = FROM_I2C_SLAVE(TMP105State, i2c); |
226 |
|
227 |
qdev_init_gpio_out(&i2c->qdev, &s->pin, 1);
|
228 |
|
229 |
tmp105_reset(&s->i2c); |
230 |
|
231 |
return 0; |
232 |
} |
233 |
|
234 |
static I2CSlaveInfo tmp105_info = {
|
235 |
.qdev.name = "tmp105",
|
236 |
.qdev.size = sizeof(TMP105State),
|
237 |
.qdev.vmsd = &vmstate_tmp105, |
238 |
.init = tmp105_init, |
239 |
.event = tmp105_event, |
240 |
.recv = tmp105_rx, |
241 |
.send = tmp105_tx |
242 |
}; |
243 |
|
244 |
static void tmp105_register_devices(void) |
245 |
{ |
246 |
i2c_register_slave(&tmp105_info); |
247 |
} |
248 |
|
249 |
device_init(tmp105_register_devices) |