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