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