root / hw / tmp105.c @ a050e24d
History | View | Annotate | Download (6.2 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
|
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 |
static int tmp105_iid = 0; |
232 |
|
233 |
struct i2c_slave *tmp105_init(i2c_bus *bus, qemu_irq alarm)
|
234 |
{ |
235 |
struct tmp105_s *s = (struct tmp105_s *) |
236 |
i2c_slave_init(bus, 0, sizeof(struct tmp105_s)); |
237 |
|
238 |
s->i2c.event = tmp105_event; |
239 |
s->i2c.recv = tmp105_rx; |
240 |
s->i2c.send = tmp105_tx; |
241 |
s->pin = alarm; |
242 |
|
243 |
tmp105_reset(&s->i2c); |
244 |
|
245 |
register_savevm("TMP105", tmp105_iid ++, 0, |
246 |
tmp105_save, tmp105_load, s); |
247 |
|
248 |
return &s->i2c;
|
249 |
} |