Statistics
| Branch: | Revision:

root / hw / max7310.c @ 074f2fff

History | View | Annotate | Download (5.2 kB)

1
/*
2
 * MAX7310 8-port GPIO expansion chip.
3
 *
4
 * Copyright (c) 2006 Openedhand Ltd.
5
 * Written by Andrzej Zaborowski <balrog@zabor.org>
6
 *
7
 * This file is licensed under GNU GPL.
8
 */
9

    
10
#include "i2c.h"
11

    
12
typedef struct {
13
    i2c_slave i2c;
14
    int i2c_command_byte;
15
    int len;
16

    
17
    uint8_t level;
18
    uint8_t direction;
19
    uint8_t polarity;
20
    uint8_t status;
21
    uint8_t command;
22
    qemu_irq handler[8];
23
    qemu_irq *gpio_in;
24
} MAX7310State;
25

    
26
void max7310_reset(i2c_slave *i2c)
27
{
28
    MAX7310State *s = (MAX7310State *) i2c;
29
    s->level &= s->direction;
30
    s->direction = 0xff;
31
    s->polarity = 0xf0;
32
    s->status = 0x01;
33
    s->command = 0x00;
34
}
35

    
36
static int max7310_rx(i2c_slave *i2c)
37
{
38
    MAX7310State *s = (MAX7310State *) i2c;
39

    
40
    switch (s->command) {
41
    case 0x00:        /* Input port */
42
        return s->level ^ s->polarity;
43
        break;
44

    
45
    case 0x01:        /* Output port */
46
        return s->level & ~s->direction;
47
        break;
48

    
49
    case 0x02:        /* Polarity inversion */
50
        return s->polarity;
51

    
52
    case 0x03:        /* Configuration */
53
        return s->direction;
54

    
55
    case 0x04:        /* Timeout */
56
        return s->status;
57
        break;
58

    
59
    case 0xff:        /* Reserved */
60
        return 0xff;
61

    
62
    default:
63
#ifdef VERBOSE
64
        printf("%s: unknown register %02x\n", __FUNCTION__, s->command);
65
#endif
66
        break;
67
    }
68
    return 0xff;
69
}
70

    
71
static int max7310_tx(i2c_slave *i2c, uint8_t data)
72
{
73
    MAX7310State *s = (MAX7310State *) i2c;
74
    uint8_t diff;
75
    int line;
76

    
77
    if (s->len ++ > 1) {
78
#ifdef VERBOSE
79
        printf("%s: message too long (%i bytes)\n", __FUNCTION__, s->len);
80
#endif
81
        return 1;
82
    }
83

    
84
    if (s->i2c_command_byte) {
85
        s->command = data;
86
        s->i2c_command_byte = 0;
87
        return 0;
88
    }
89

    
90
    switch (s->command) {
91
    case 0x01:        /* Output port */
92
        for (diff = (data ^ s->level) & ~s->direction; diff;
93
                        diff &= ~(1 << line)) {
94
            line = ffs(diff) - 1;
95
            if (s->handler[line])
96
                qemu_set_irq(s->handler[line], (data >> line) & 1);
97
        }
98
        s->level = (s->level & s->direction) | (data & ~s->direction);
99
        break;
100

    
101
    case 0x02:        /* Polarity inversion */
102
        s->polarity = data;
103
        break;
104

    
105
    case 0x03:        /* Configuration */
106
        s->level &= ~(s->direction ^ data);
107
        s->direction = data;
108
        break;
109

    
110
    case 0x04:        /* Timeout */
111
        s->status = data;
112
        break;
113

    
114
    case 0x00:        /* Input port - ignore writes */
115
        break;
116
    default:
117
#ifdef VERBOSE
118
        printf("%s: unknown register %02x\n", __FUNCTION__, s->command);
119
#endif
120
        return 1;
121
    }
122

    
123
    return 0;
124
}
125

    
126
static void max7310_event(i2c_slave *i2c, enum i2c_event event)
127
{
128
    MAX7310State *s = (MAX7310State *) i2c;
129
    s->len = 0;
130

    
131
    switch (event) {
132
    case I2C_START_SEND:
133
        s->i2c_command_byte = 1;
134
        break;
135
    case I2C_FINISH:
136
#ifdef VERBOSE
137
        if (s->len == 1)
138
            printf("%s: message too short (%i bytes)\n", __FUNCTION__, s->len);
139
#endif
140
        break;
141
    default:
142
        break;
143
    }
144
}
145

    
146
static void max7310_save(QEMUFile *f, void *opaque)
147
{
148
    MAX7310State *s = (MAX7310State *) opaque;
149

    
150
    qemu_put_be32(f, s->i2c_command_byte);
151
    qemu_put_be32(f, s->len);
152

    
153
    qemu_put_8s(f, &s->level);
154
    qemu_put_8s(f, &s->direction);
155
    qemu_put_8s(f, &s->polarity);
156
    qemu_put_8s(f, &s->status);
157
    qemu_put_8s(f, &s->command);
158

    
159
    i2c_slave_save(f, &s->i2c);
160
}
161

    
162
static int max7310_load(QEMUFile *f, void *opaque, int version_id)
163
{
164
    MAX7310State *s = (MAX7310State *) opaque;
165

    
166
    s->i2c_command_byte = qemu_get_be32(f);
167
    s->len = qemu_get_be32(f);
168

    
169
    qemu_get_8s(f, &s->level);
170
    qemu_get_8s(f, &s->direction);
171
    qemu_get_8s(f, &s->polarity);
172
    qemu_get_8s(f, &s->status);
173
    qemu_get_8s(f, &s->command);
174

    
175
    i2c_slave_load(f, &s->i2c);
176
    return 0;
177
}
178

    
179
static void max7310_gpio_set(void *opaque, int line, int level)
180
{
181
    MAX7310State *s = (MAX7310State *) opaque;
182
    if (line >= ARRAY_SIZE(s->handler) || line  < 0)
183
        hw_error("bad GPIO line");
184

    
185
    if (level)
186
        s->level |= s->direction & (1 << line);
187
    else
188
        s->level &= ~(s->direction & (1 << line));
189
}
190

    
191
/* MAX7310 is SMBus-compatible (can be used with only SMBus protocols),
192
 * but also accepts sequences that are not SMBus so return an I2C device.  */
193
static void max7310_init(i2c_slave *i2c)
194
{
195
    MAX7310State *s = FROM_I2C_SLAVE(MAX7310State, i2c);
196

    
197
    s->gpio_in = qemu_allocate_irqs(max7310_gpio_set, s,
198
                    ARRAY_SIZE(s->handler));
199

    
200
    max7310_reset(&s->i2c);
201

    
202
    register_savevm("max7310", -1, 0, max7310_save, max7310_load, s);
203
}
204

    
205
qemu_irq *max7310_gpio_in_get(i2c_slave *i2c)
206
{
207
    MAX7310State *s = (MAX7310State *) i2c;
208
    return s->gpio_in;
209
}
210

    
211
void max7310_gpio_out_set(i2c_slave *i2c, int line, qemu_irq handler)
212
{
213
    MAX7310State *s = (MAX7310State *) i2c;
214
    if (line >= ARRAY_SIZE(s->handler) || line  < 0)
215
        hw_error("bad GPIO line");
216

    
217
    s->handler[line] = handler;
218
}
219

    
220
static I2CSlaveInfo max7310_info = {
221
    .qdev.name = "max7310",
222
    .qdev.size = sizeof(MAX7310State),
223
    .init = max7310_init,
224
    .event = max7310_event,
225
    .recv = max7310_rx,
226
    .send = max7310_tx
227
};
228

    
229
static void max7310_register_devices(void)
230
{
231
    i2c_register_slave(&max7310_info);
232
}
233

    
234
device_init(max7310_register_devices)