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 "qemu/osdep.h" |
11 | #include "hw/hw.h" |
12 | #include "hw/i2c/i2c.h" |
13 | #include "hw/hw.h" |
14 | #include "hw/irq.h" |
15 | #include "migration/vmstate.h" |
16 | #include "qemu/module.h" |
17 | |
18 | #define TYPE_MAX7310 "max7310" |
19 | #define MAX7310(obj) OBJECT_CHECK(MAX7310State, (obj), TYPE_MAX7310) |
20 | |
21 | typedef struct MAX7310State { |
22 | I2CSlave parent_obj; |
23 | |
24 | int i2c_command_byte; |
25 | int len; |
26 | |
27 | uint8_t level; |
28 | uint8_t direction; |
29 | uint8_t polarity; |
30 | uint8_t status; |
31 | uint8_t command; |
32 | qemu_irq handler[8]; |
33 | qemu_irq *gpio_in; |
34 | } MAX7310State; |
35 | |
36 | static void max7310_reset(DeviceState *dev) |
37 | { |
38 | MAX7310State *s = MAX7310(dev); |
39 | |
40 | s->level &= s->direction; |
41 | s->direction = 0xff; |
42 | s->polarity = 0xf0; |
43 | s->status = 0x01; |
44 | s->command = 0x00; |
45 | } |
46 | |
47 | static uint8_t max7310_rx(I2CSlave *i2c) |
48 | { |
49 | MAX7310State *s = MAX7310(i2c); |
50 | |
51 | switch (s->command) { |
52 | case 0x00: /* Input port */ |
53 | return s->level ^ s->polarity; |
54 | break; |
55 | |
56 | case 0x01: /* Output port */ |
57 | return s->level & ~s->direction; |
58 | break; |
59 | |
60 | case 0x02: /* Polarity inversion */ |
61 | return s->polarity; |
62 | |
63 | case 0x03: /* Configuration */ |
64 | return s->direction; |
65 | |
66 | case 0x04: /* Timeout */ |
67 | return s->status; |
68 | break; |
69 | |
70 | case 0xff: /* Reserved */ |
71 | return 0xff; |
72 | |
73 | default: |
74 | #ifdef VERBOSE |
75 | printf("%s: unknown register %02x\n" , __func__, s->command); |
76 | #endif |
77 | break; |
78 | } |
79 | return 0xff; |
80 | } |
81 | |
82 | static int max7310_tx(I2CSlave *i2c, uint8_t data) |
83 | { |
84 | MAX7310State *s = MAX7310(i2c); |
85 | uint8_t diff; |
86 | int line; |
87 | |
88 | if (s->len ++ > 1) { |
89 | #ifdef VERBOSE |
90 | printf("%s: message too long (%i bytes)\n" , __func__, s->len); |
91 | #endif |
92 | return 1; |
93 | } |
94 | |
95 | if (s->i2c_command_byte) { |
96 | s->command = data; |
97 | s->i2c_command_byte = 0; |
98 | return 0; |
99 | } |
100 | |
101 | switch (s->command) { |
102 | case 0x01: /* Output port */ |
103 | for (diff = (data ^ s->level) & ~s->direction; diff; |
104 | diff &= ~(1 << line)) { |
105 | line = ctz32(diff); |
106 | if (s->handler[line]) |
107 | qemu_set_irq(s->handler[line], (data >> line) & 1); |
108 | } |
109 | s->level = (s->level & s->direction) | (data & ~s->direction); |
110 | break; |
111 | |
112 | case 0x02: /* Polarity inversion */ |
113 | s->polarity = data; |
114 | break; |
115 | |
116 | case 0x03: /* Configuration */ |
117 | s->level &= ~(s->direction ^ data); |
118 | s->direction = data; |
119 | break; |
120 | |
121 | case 0x04: /* Timeout */ |
122 | s->status = data; |
123 | break; |
124 | |
125 | case 0x00: /* Input port - ignore writes */ |
126 | break; |
127 | default: |
128 | #ifdef VERBOSE |
129 | printf("%s: unknown register %02x\n" , __func__, s->command); |
130 | #endif |
131 | return 1; |
132 | } |
133 | |
134 | return 0; |
135 | } |
136 | |
137 | static int max7310_event(I2CSlave *i2c, enum i2c_event event) |
138 | { |
139 | MAX7310State *s = MAX7310(i2c); |
140 | s->len = 0; |
141 | |
142 | switch (event) { |
143 | case I2C_START_SEND: |
144 | s->i2c_command_byte = 1; |
145 | break; |
146 | case I2C_FINISH: |
147 | #ifdef VERBOSE |
148 | if (s->len == 1) |
149 | printf("%s: message too short (%i bytes)\n" , __func__, s->len); |
150 | #endif |
151 | break; |
152 | default: |
153 | break; |
154 | } |
155 | |
156 | return 0; |
157 | } |
158 | |
159 | static const VMStateDescription vmstate_max7310 = { |
160 | .name = "max7310" , |
161 | .version_id = 0, |
162 | .minimum_version_id = 0, |
163 | .fields = (VMStateField[]) { |
164 | VMSTATE_INT32(i2c_command_byte, MAX7310State), |
165 | VMSTATE_INT32(len, MAX7310State), |
166 | VMSTATE_UINT8(level, MAX7310State), |
167 | VMSTATE_UINT8(direction, MAX7310State), |
168 | VMSTATE_UINT8(polarity, MAX7310State), |
169 | VMSTATE_UINT8(status, MAX7310State), |
170 | VMSTATE_UINT8(command, MAX7310State), |
171 | VMSTATE_I2C_SLAVE(parent_obj, MAX7310State), |
172 | VMSTATE_END_OF_LIST() |
173 | } |
174 | }; |
175 | |
176 | static void max7310_gpio_set(void *opaque, int line, int level) |
177 | { |
178 | MAX7310State *s = (MAX7310State *) opaque; |
179 | if (line >= ARRAY_SIZE(s->handler) || line < 0) |
180 | hw_error("bad GPIO line" ); |
181 | |
182 | if (level) |
183 | s->level |= s->direction & (1 << line); |
184 | else |
185 | s->level &= ~(s->direction & (1 << line)); |
186 | } |
187 | |
188 | /* MAX7310 is SMBus-compatible (can be used with only SMBus protocols), |
189 | * but also accepts sequences that are not SMBus so return an I2C device. */ |
190 | static void max7310_realize(DeviceState *dev, Error **errp) |
191 | { |
192 | I2CSlave *i2c = I2C_SLAVE(dev); |
193 | MAX7310State *s = MAX7310(dev); |
194 | |
195 | qdev_init_gpio_in(&i2c->qdev, max7310_gpio_set, 8); |
196 | qdev_init_gpio_out(&i2c->qdev, s->handler, 8); |
197 | } |
198 | |
199 | static void max7310_class_init(ObjectClass *klass, void *data) |
200 | { |
201 | DeviceClass *dc = DEVICE_CLASS(klass); |
202 | I2CSlaveClass *k = I2C_SLAVE_CLASS(klass); |
203 | |
204 | dc->realize = max7310_realize; |
205 | k->event = max7310_event; |
206 | k->recv = max7310_rx; |
207 | k->send = max7310_tx; |
208 | dc->reset = max7310_reset; |
209 | dc->vmsd = &vmstate_max7310; |
210 | } |
211 | |
212 | static const TypeInfo max7310_info = { |
213 | .name = TYPE_MAX7310, |
214 | .parent = TYPE_I2C_SLAVE, |
215 | .instance_size = sizeof(MAX7310State), |
216 | .class_init = max7310_class_init, |
217 | }; |
218 | |
219 | static void max7310_register_types(void) |
220 | { |
221 | type_register_static(&max7310_info); |
222 | } |
223 | |
224 | type_init(max7310_register_types) |
225 | |