Statistics
| Branch: | Revision:

root / hw / max7310.c @ 0f921197

History | View | Annotate | Download (5 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 const VMStateDescription vmstate_max7310 = {
147
    .name = "max7310",
148
    .version_id = 0,
149
    .minimum_version_id = 0,
150
    .minimum_version_id_old = 0,
151
    .fields      = (VMStateField []) {
152
        VMSTATE_INT32(i2c_command_byte, MAX7310State),
153
        VMSTATE_INT32(len, MAX7310State),
154
        VMSTATE_UINT8(level, MAX7310State),
155
        VMSTATE_UINT8(direction, MAX7310State),
156
        VMSTATE_UINT8(polarity, MAX7310State),
157
        VMSTATE_UINT8(status, MAX7310State),
158
        VMSTATE_UINT8(command, MAX7310State),
159
        VMSTATE_I2C_SLAVE(i2c, MAX7310State),
160
        VMSTATE_END_OF_LIST()
161
    }
162
};
163

    
164
static void max7310_gpio_set(void *opaque, int line, int level)
165
{
166
    MAX7310State *s = (MAX7310State *) opaque;
167
    if (line >= ARRAY_SIZE(s->handler) || line  < 0)
168
        hw_error("bad GPIO line");
169

    
170
    if (level)
171
        s->level |= s->direction & (1 << line);
172
    else
173
        s->level &= ~(s->direction & (1 << line));
174
}
175

    
176
/* MAX7310 is SMBus-compatible (can be used with only SMBus protocols),
177
 * but also accepts sequences that are not SMBus so return an I2C device.  */
178
static int max7310_init(i2c_slave *i2c)
179
{
180
    MAX7310State *s = FROM_I2C_SLAVE(MAX7310State, i2c);
181

    
182
    s->gpio_in = qemu_allocate_irqs(max7310_gpio_set, s,
183
                    ARRAY_SIZE(s->handler));
184

    
185
    max7310_reset(&s->i2c);
186

    
187
    return 0;
188
}
189

    
190
qemu_irq *max7310_gpio_in_get(i2c_slave *i2c)
191
{
192
    MAX7310State *s = (MAX7310State *) i2c;
193
    return s->gpio_in;
194
}
195

    
196
void max7310_gpio_out_set(i2c_slave *i2c, int line, qemu_irq handler)
197
{
198
    MAX7310State *s = (MAX7310State *) i2c;
199
    if (line >= ARRAY_SIZE(s->handler) || line  < 0)
200
        hw_error("bad GPIO line");
201

    
202
    s->handler[line] = handler;
203
}
204

    
205
static I2CSlaveInfo max7310_info = {
206
    .qdev.name = "max7310",
207
    .qdev.size = sizeof(MAX7310State),
208
    .qdev.vmsd = &vmstate_max7310,
209
    .init = max7310_init,
210
    .event = max7310_event,
211
    .recv = max7310_rx,
212
    .send = max7310_tx
213
};
214

    
215
static void max7310_register_devices(void)
216
{
217
    i2c_register_slave(&max7310_info);
218
}
219

    
220
device_init(max7310_register_devices)