Statistics
| Branch: | Revision:

root / hw / tmp105.c @ b6c4f71f

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
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_sbe16s(f, &s->temperature);
189
    qemu_put_sbe16s(f, &s->limit[0]);
190
    qemu_put_sbe16s(f, &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_sbe16s(f, &s->temperature);
208
    qemu_get_sbe16s(f, &s->limit[0]);
209
    qemu_get_sbe16s(f, &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
struct i2c_slave *tmp105_init(i2c_bus *bus, qemu_irq alarm)
232
{
233
    struct tmp105_s *s = (struct tmp105_s *)
234
            i2c_slave_init(bus, 0, sizeof(struct tmp105_s));
235

    
236
    s->i2c.event = tmp105_event;
237
    s->i2c.recv = tmp105_rx;
238
    s->i2c.send = tmp105_tx;
239
    s->pin = alarm;
240

    
241
    tmp105_reset(&s->i2c);
242

    
243
    register_savevm("TMP105", -1, 0, tmp105_save, tmp105_load, s);
244

    
245
    return &s->i2c;
246
}