Statistics
| Branch: | Revision:

root / hw / tmp105.c @ 074f2fff

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 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
typedef struct {
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
} TMP105State;
38

    
39
static void tmp105_interrupt_update(TMP105State *s)
40
{
41
    qemu_set_irq(s->pin, s->alarm ^ ((~s->config >> 2) & 1));        /* POL */
42
}
43

    
44
static void tmp105_alarm_update(TMP105State *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
    TMP105State *s = (TMP105State *) 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(TMP105State *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(TMP105State *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
    TMP105State *s = (TMP105State *) 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
    TMP105State *s = (TMP105State *) 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
    TMP105State *s = (TMP105State *) 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
    TMP105State *s = (TMP105State *) 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
    TMP105State *s = (TMP105State *) 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
static void tmp105_reset(i2c_slave *i2c)
218
{
219
    TMP105State *s = (TMP105State *) 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
static void tmp105_init(i2c_slave *i2c)
231
{
232
    TMP105State *s = FROM_I2C_SLAVE(TMP105State, i2c);
233

    
234
    qdev_init_gpio_out(&i2c->qdev, &s->pin, 1);
235

    
236
    tmp105_reset(&s->i2c);
237

    
238
    register_savevm("TMP105", -1, 0, tmp105_save, tmp105_load, s);
239
}
240

    
241
static I2CSlaveInfo tmp105_info = {
242
    .qdev.name = "tmp105",
243
    .qdev.size = sizeof(TMP105State),
244
    .init = tmp105_init,
245
    .event = tmp105_event,
246
    .recv = tmp105_rx,
247
    .send = tmp105_tx
248
};
249

    
250
static void tmp105_register_devices(void)
251
{
252
    i2c_register_slave(&tmp105_info);
253
}
254

    
255
device_init(tmp105_register_devices)