root / hw / tmp105.c @ e0dd114c
History | View | Annotate | Download (6.1 kB)
1 | 7e7c5e4c | balrog | /*
|
---|---|---|---|
2 | 7e7c5e4c | balrog | * Texas Instruments TMP105 temperature sensor.
|
3 | 7e7c5e4c | balrog | *
|
4 | 7e7c5e4c | balrog | * Copyright (C) 2008 Nokia Corporation
|
5 | 7e7c5e4c | balrog | * Written by Andrzej Zaborowski <andrew@openedhand.com>
|
6 | 7e7c5e4c | balrog | *
|
7 | 7e7c5e4c | balrog | * This program is free software; you can redistribute it and/or
|
8 | 7e7c5e4c | balrog | * modify it under the terms of the GNU General Public License as
|
9 | 7e7c5e4c | balrog | * published by the Free Software Foundation; either version 2 or
|
10 | 7e7c5e4c | balrog | * (at your option) version 3 of the License.
|
11 | 7e7c5e4c | balrog | *
|
12 | 7e7c5e4c | balrog | * This program is distributed in the hope that it will be useful,
|
13 | 7e7c5e4c | balrog | * but WITHOUT ANY WARRANTY; without even the implied warranty of
|
14 | 7e7c5e4c | balrog | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
15 | 7e7c5e4c | balrog | * GNU General Public License for more details.
|
16 | 7e7c5e4c | balrog | *
|
17 | fad6cb1a | aurel32 | * You should have received a copy of the GNU General Public License along
|
18 | fad6cb1a | aurel32 | * with this program; if not, write to the Free Software Foundation, Inc.,
|
19 | fad6cb1a | aurel32 | * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
20 | 7e7c5e4c | balrog | */
|
21 | 7e7c5e4c | balrog | |
22 | 7e7c5e4c | balrog | #include "hw.h" |
23 | 7e7c5e4c | balrog | #include "i2c.h" |
24 | 7e7c5e4c | balrog | |
25 | 7e7c5e4c | balrog | struct tmp105_s {
|
26 | 7e7c5e4c | balrog | i2c_slave i2c; |
27 | 7e7c5e4c | balrog | int len;
|
28 | 7e7c5e4c | balrog | uint8_t buf[2];
|
29 | 7e7c5e4c | balrog | qemu_irq pin; |
30 | 7e7c5e4c | balrog | |
31 | 7e7c5e4c | balrog | uint8_t pointer; |
32 | 7e7c5e4c | balrog | uint8_t config; |
33 | 7e7c5e4c | balrog | int16_t temperature; |
34 | 7e7c5e4c | balrog | int16_t limit[2];
|
35 | 7e7c5e4c | balrog | int faults;
|
36 | 7e7c5e4c | balrog | int alarm;
|
37 | 7e7c5e4c | balrog | }; |
38 | 7e7c5e4c | balrog | |
39 | 7e7c5e4c | balrog | static void tmp105_interrupt_update(struct tmp105_s *s) |
40 | 7e7c5e4c | balrog | { |
41 | 7e7c5e4c | balrog | qemu_set_irq(s->pin, s->alarm ^ ((~s->config >> 2) & 1)); /* POL */ |
42 | 7e7c5e4c | balrog | } |
43 | 7e7c5e4c | balrog | |
44 | 7e7c5e4c | balrog | static void tmp105_alarm_update(struct tmp105_s *s) |
45 | 7e7c5e4c | balrog | { |
46 | 7e7c5e4c | balrog | if ((s->config >> 0) & 1) { /* SD */ |
47 | 7e7c5e4c | balrog | if ((s->config >> 7) & 1) /* OS */ |
48 | 7e7c5e4c | balrog | s->config &= ~(1 << 7); /* OS */ |
49 | 7e7c5e4c | balrog | else
|
50 | 7e7c5e4c | balrog | return;
|
51 | 7e7c5e4c | balrog | } |
52 | 7e7c5e4c | balrog | |
53 | 7e7c5e4c | balrog | if ((s->config >> 1) & 1) { /* TM */ |
54 | 7e7c5e4c | balrog | if (s->temperature >= s->limit[1]) |
55 | 7e7c5e4c | balrog | s->alarm = 1;
|
56 | 7e7c5e4c | balrog | else if (s->temperature < s->limit[0]) |
57 | 7e7c5e4c | balrog | s->alarm = 1;
|
58 | 7e7c5e4c | balrog | } else {
|
59 | 7e7c5e4c | balrog | if (s->temperature >= s->limit[1]) |
60 | 7e7c5e4c | balrog | s->alarm = 1;
|
61 | 7e7c5e4c | balrog | else if (s->temperature < s->limit[0]) |
62 | 7e7c5e4c | balrog | s->alarm = 0;
|
63 | 7e7c5e4c | balrog | } |
64 | 7e7c5e4c | balrog | |
65 | 7e7c5e4c | balrog | tmp105_interrupt_update(s); |
66 | 7e7c5e4c | balrog | } |
67 | 7e7c5e4c | balrog | |
68 | 7e7c5e4c | balrog | /* Units are 0.001 centigrades relative to 0 C. */
|
69 | 7e7c5e4c | balrog | void tmp105_set(i2c_slave *i2c, int temp) |
70 | 7e7c5e4c | balrog | { |
71 | 7e7c5e4c | balrog | struct tmp105_s *s = (struct tmp105_s *) i2c; |
72 | 7e7c5e4c | balrog | |
73 | 7e7c5e4c | balrog | if (temp >= 128000 || temp < -128000) { |
74 | 7e7c5e4c | balrog | fprintf(stderr, "%s: values is out of range (%i.%03i C)\n",
|
75 | 7e7c5e4c | balrog | __FUNCTION__, temp / 1000, temp % 1000); |
76 | 7e7c5e4c | balrog | exit(-1);
|
77 | 7e7c5e4c | balrog | } |
78 | 7e7c5e4c | balrog | |
79 | 7e7c5e4c | balrog | s->temperature = ((int16_t) (temp * 0x800 / 128000)) << 4; |
80 | 7e7c5e4c | balrog | |
81 | 7e7c5e4c | balrog | tmp105_alarm_update(s); |
82 | 7e7c5e4c | balrog | } |
83 | 7e7c5e4c | balrog | |
84 | 7e7c5e4c | balrog | static const int tmp105_faultq[4] = { 1, 2, 4, 6 }; |
85 | 7e7c5e4c | balrog | |
86 | 7e7c5e4c | balrog | static void tmp105_read(struct tmp105_s *s) |
87 | 7e7c5e4c | balrog | { |
88 | 7e7c5e4c | balrog | s->len = 0;
|
89 | 7e7c5e4c | balrog | |
90 | 7e7c5e4c | balrog | if ((s->config >> 1) & 1) { /* TM */ |
91 | 7e7c5e4c | balrog | s->alarm = 0;
|
92 | 7e7c5e4c | balrog | tmp105_interrupt_update(s); |
93 | 7e7c5e4c | balrog | } |
94 | 7e7c5e4c | balrog | |
95 | 7e7c5e4c | balrog | switch (s->pointer & 3) { |
96 | 7e7c5e4c | balrog | case 0: /* Temperature */ |
97 | 7e7c5e4c | balrog | s->buf[s->len ++] = (((uint16_t) s->temperature) >> 8);
|
98 | 7e7c5e4c | balrog | s->buf[s->len ++] = (((uint16_t) s->temperature) >> 0) &
|
99 | 7e7c5e4c | balrog | (0xf0 << ((~s->config >> 5) & 3)); /* R */ |
100 | 7e7c5e4c | balrog | break;
|
101 | 7e7c5e4c | balrog | |
102 | 7e7c5e4c | balrog | case 1: /* Configuration */ |
103 | 7e7c5e4c | balrog | s->buf[s->len ++] = s->config; |
104 | 7e7c5e4c | balrog | break;
|
105 | 7e7c5e4c | balrog | |
106 | 7e7c5e4c | balrog | case 2: /* T_LOW */ |
107 | 7e7c5e4c | balrog | s->buf[s->len ++] = ((uint16_t) s->limit[0]) >> 8; |
108 | 7e7c5e4c | balrog | s->buf[s->len ++] = ((uint16_t) s->limit[0]) >> 0; |
109 | 7e7c5e4c | balrog | break;
|
110 | 7e7c5e4c | balrog | |
111 | 7e7c5e4c | balrog | case 3: /* T_HIGH */ |
112 | 7e7c5e4c | balrog | s->buf[s->len ++] = ((uint16_t) s->limit[1]) >> 8; |
113 | 7e7c5e4c | balrog | s->buf[s->len ++] = ((uint16_t) s->limit[1]) >> 0; |
114 | 7e7c5e4c | balrog | break;
|
115 | 7e7c5e4c | balrog | } |
116 | 7e7c5e4c | balrog | } |
117 | 7e7c5e4c | balrog | |
118 | 7e7c5e4c | balrog | static void tmp105_write(struct tmp105_s *s) |
119 | 7e7c5e4c | balrog | { |
120 | 7e7c5e4c | balrog | switch (s->pointer & 3) { |
121 | 7e7c5e4c | balrog | case 0: /* Temperature */ |
122 | 7e7c5e4c | balrog | break;
|
123 | 7e7c5e4c | balrog | |
124 | 7e7c5e4c | balrog | case 1: /* Configuration */ |
125 | 7e7c5e4c | balrog | if (s->buf[0] & ~s->config & (1 << 0)) /* SD */ |
126 | 7e7c5e4c | balrog | printf("%s: TMP105 shutdown\n", __FUNCTION__);
|
127 | 7e7c5e4c | balrog | s->config = s->buf[0];
|
128 | 7e7c5e4c | balrog | s->faults = tmp105_faultq[(s->config >> 3) & 3]; /* F */ |
129 | 7e7c5e4c | balrog | tmp105_alarm_update(s); |
130 | 7e7c5e4c | balrog | break;
|
131 | 7e7c5e4c | balrog | |
132 | 7e7c5e4c | balrog | case 2: /* T_LOW */ |
133 | 7e7c5e4c | balrog | case 3: /* T_HIGH */ |
134 | 7e7c5e4c | balrog | if (s->len >= 3) |
135 | 7e7c5e4c | balrog | s->limit[s->pointer & 1] = (int16_t)
|
136 | 7e7c5e4c | balrog | ((((uint16_t) s->buf[0]) << 8) | s->buf[1]); |
137 | 7e7c5e4c | balrog | tmp105_alarm_update(s); |
138 | 7e7c5e4c | balrog | break;
|
139 | 7e7c5e4c | balrog | } |
140 | 7e7c5e4c | balrog | } |
141 | 7e7c5e4c | balrog | |
142 | 7e7c5e4c | balrog | static int tmp105_rx(i2c_slave *i2c) |
143 | 7e7c5e4c | balrog | { |
144 | 7e7c5e4c | balrog | struct tmp105_s *s = (struct tmp105_s *) i2c; |
145 | 7e7c5e4c | balrog | |
146 | 7e7c5e4c | balrog | if (s->len < 2) |
147 | 7e7c5e4c | balrog | return s->buf[s->len ++];
|
148 | 7e7c5e4c | balrog | else
|
149 | 7e7c5e4c | balrog | return 0xff; |
150 | 7e7c5e4c | balrog | } |
151 | 7e7c5e4c | balrog | |
152 | 7e7c5e4c | balrog | static int tmp105_tx(i2c_slave *i2c, uint8_t data) |
153 | 7e7c5e4c | balrog | { |
154 | 7e7c5e4c | balrog | struct tmp105_s *s = (struct tmp105_s *) i2c; |
155 | 7e7c5e4c | balrog | |
156 | 7e7c5e4c | balrog | if (!s->len ++)
|
157 | 7e7c5e4c | balrog | s->pointer = data; |
158 | 7e7c5e4c | balrog | else {
|
159 | 7e7c5e4c | balrog | if (s->len <= 2) |
160 | 7e7c5e4c | balrog | s->buf[s->len - 1] = data;
|
161 | 7e7c5e4c | balrog | tmp105_write(s); |
162 | 7e7c5e4c | balrog | } |
163 | 7e7c5e4c | balrog | |
164 | 7e7c5e4c | balrog | return 0; |
165 | 7e7c5e4c | balrog | } |
166 | 7e7c5e4c | balrog | |
167 | 7e7c5e4c | balrog | static void tmp105_event(i2c_slave *i2c, enum i2c_event event) |
168 | 7e7c5e4c | balrog | { |
169 | 7e7c5e4c | balrog | struct tmp105_s *s = (struct tmp105_s *) i2c; |
170 | 7e7c5e4c | balrog | |
171 | 7e7c5e4c | balrog | if (event == I2C_START_RECV)
|
172 | 7e7c5e4c | balrog | tmp105_read(s); |
173 | 7e7c5e4c | balrog | |
174 | 7e7c5e4c | balrog | s->len = 0;
|
175 | 7e7c5e4c | balrog | } |
176 | 7e7c5e4c | balrog | |
177 | 7e7c5e4c | balrog | static void tmp105_save(QEMUFile *f, void *opaque) |
178 | 7e7c5e4c | balrog | { |
179 | 7e7c5e4c | balrog | struct tmp105_s *s = (struct tmp105_s *) opaque; |
180 | 7e7c5e4c | balrog | |
181 | 7e7c5e4c | balrog | qemu_put_byte(f, s->len); |
182 | 7e7c5e4c | balrog | qemu_put_8s(f, &s->buf[0]);
|
183 | 7e7c5e4c | balrog | qemu_put_8s(f, &s->buf[1]);
|
184 | 7e7c5e4c | balrog | |
185 | 7e7c5e4c | balrog | qemu_put_8s(f, &s->pointer); |
186 | 7e7c5e4c | balrog | qemu_put_8s(f, &s->config); |
187 | b6c4f71f | blueswir1 | qemu_put_sbe16s(f, &s->temperature); |
188 | b6c4f71f | blueswir1 | qemu_put_sbe16s(f, &s->limit[0]);
|
189 | b6c4f71f | blueswir1 | qemu_put_sbe16s(f, &s->limit[1]);
|
190 | 7e7c5e4c | balrog | qemu_put_byte(f, s->alarm); |
191 | 7e7c5e4c | balrog | s->faults = tmp105_faultq[(s->config >> 3) & 3]; /* F */ |
192 | 7e7c5e4c | balrog | |
193 | 7e7c5e4c | balrog | i2c_slave_save(f, &s->i2c); |
194 | 7e7c5e4c | balrog | } |
195 | 7e7c5e4c | balrog | |
196 | 7e7c5e4c | balrog | static int tmp105_load(QEMUFile *f, void *opaque, int version_id) |
197 | 7e7c5e4c | balrog | { |
198 | 7e7c5e4c | balrog | struct tmp105_s *s = (struct tmp105_s *) opaque; |
199 | 7e7c5e4c | balrog | |
200 | 7e7c5e4c | balrog | s->len = qemu_get_byte(f); |
201 | 7e7c5e4c | balrog | qemu_get_8s(f, &s->buf[0]);
|
202 | 7e7c5e4c | balrog | qemu_get_8s(f, &s->buf[1]);
|
203 | 7e7c5e4c | balrog | |
204 | 7e7c5e4c | balrog | qemu_get_8s(f, &s->pointer); |
205 | 7e7c5e4c | balrog | qemu_get_8s(f, &s->config); |
206 | b6c4f71f | blueswir1 | qemu_get_sbe16s(f, &s->temperature); |
207 | b6c4f71f | blueswir1 | qemu_get_sbe16s(f, &s->limit[0]);
|
208 | b6c4f71f | blueswir1 | qemu_get_sbe16s(f, &s->limit[1]);
|
209 | 7e7c5e4c | balrog | s->alarm = qemu_get_byte(f); |
210 | 7e7c5e4c | balrog | |
211 | 7e7c5e4c | balrog | tmp105_interrupt_update(s); |
212 | 7e7c5e4c | balrog | |
213 | 7e7c5e4c | balrog | i2c_slave_load(f, &s->i2c); |
214 | 7e7c5e4c | balrog | return 0; |
215 | 7e7c5e4c | balrog | } |
216 | 7e7c5e4c | balrog | |
217 | 7e7c5e4c | balrog | void tmp105_reset(i2c_slave *i2c)
|
218 | 7e7c5e4c | balrog | { |
219 | 7e7c5e4c | balrog | struct tmp105_s *s = (struct tmp105_s *) i2c; |
220 | 7e7c5e4c | balrog | |
221 | 7e7c5e4c | balrog | s->temperature = 0;
|
222 | 7e7c5e4c | balrog | s->pointer = 0;
|
223 | 7e7c5e4c | balrog | s->config = 0;
|
224 | 7e7c5e4c | balrog | s->faults = tmp105_faultq[(s->config >> 3) & 3]; |
225 | 7e7c5e4c | balrog | s->alarm = 0;
|
226 | 7e7c5e4c | balrog | |
227 | 7e7c5e4c | balrog | tmp105_interrupt_update(s); |
228 | 7e7c5e4c | balrog | } |
229 | 7e7c5e4c | balrog | |
230 | 7e7c5e4c | balrog | struct i2c_slave *tmp105_init(i2c_bus *bus, qemu_irq alarm)
|
231 | 7e7c5e4c | balrog | { |
232 | 7e7c5e4c | balrog | struct tmp105_s *s = (struct tmp105_s *) |
233 | 7e7c5e4c | balrog | i2c_slave_init(bus, 0, sizeof(struct tmp105_s)); |
234 | 7e7c5e4c | balrog | |
235 | 7e7c5e4c | balrog | s->i2c.event = tmp105_event; |
236 | 7e7c5e4c | balrog | s->i2c.recv = tmp105_rx; |
237 | 7e7c5e4c | balrog | s->i2c.send = tmp105_tx; |
238 | 7e7c5e4c | balrog | s->pin = alarm; |
239 | 7e7c5e4c | balrog | |
240 | 7e7c5e4c | balrog | tmp105_reset(&s->i2c); |
241 | 7e7c5e4c | balrog | |
242 | 18be5187 | pbrook | register_savevm("TMP105", -1, 0, tmp105_save, tmp105_load, s); |
243 | 7e7c5e4c | balrog | |
244 | 7e7c5e4c | balrog | return &s->i2c;
|
245 | 7e7c5e4c | balrog | } |