Statistics
| Branch: | Revision:

root / hw / max7310.c @ adb86c37

History | View | Annotate | Download (4.3 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 "vl.h"
11

    
12
struct max7310_s {
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
};
25

    
26
void max7310_reset(i2c_slave *i2c)
27
{
28
    struct max7310_s *s = (struct max7310_s *) 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
    struct max7310_s *s = (struct max7310_s *) 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
    struct max7310_s *s = (struct max7310_s *) 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
    struct max7310_s *s = (struct max7310_s *) 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
        if (s->len == 1)
137
#ifdef VERBOSE
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_gpio_set(void *opaque, int line, int level)
147
{
148
    struct max7310_s *s = (struct max7310_s *) opaque;
149
    if (line >= sizeof(s->handler) / sizeof(*s->handler) || line  < 0)
150
        cpu_abort(cpu_single_env, "bad GPIO line");
151

    
152
    if (level)
153
        s->level |= s->direction & (1 << line);
154
    else
155
        s->level &= ~(s->direction & (1 << line));
156
}
157

    
158
/* MAX7310 is SMBus-compatible (can be used with only SMBus protocols),
159
 * but also accepts sequences that are not SMBus so return an I2C device.  */
160
struct i2c_slave *max7310_init(i2c_bus *bus)
161
{
162
    struct max7310_s *s = (struct max7310_s *)
163
            i2c_slave_init(bus, 0, sizeof(struct max7310_s));
164
    s->i2c.event = max7310_event;
165
    s->i2c.recv = max7310_rx;
166
    s->i2c.send = max7310_tx;
167
    s->gpio_in = qemu_allocate_irqs(max7310_gpio_set, s,
168
                    sizeof(s->handler) / sizeof(*s->handler));
169

    
170
    max7310_reset(&s->i2c);
171

    
172
    return &s->i2c;
173
}
174

    
175
qemu_irq *max7310_gpio_in_get(i2c_slave *i2c)
176
{
177
    struct max7310_s *s = (struct max7310_s *) i2c;
178
    return s->gpio_in;
179
}
180

    
181
void max7310_gpio_out_set(i2c_slave *i2c, int line, qemu_irq handler)
182
{
183
    struct max7310_s *s = (struct max7310_s *) i2c;
184
    if (line >= sizeof(s->handler) / sizeof(*s->handler) || line  < 0)
185
        cpu_abort(cpu_single_env, "bad GPIO line");
186

    
187
    s->handler[line] = handler;
188
}