1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29#include "qemu/osdep.h"
30#include "qapi/error.h"
31#include "hw/misc/aux.h"
32#include "hw/i2c/i2c.h"
33#include "monitor/monitor.h"
34#include "qemu/log.h"
35
36#ifndef DEBUG_AUX
37#define DEBUG_AUX 0
38#endif
39
40#define DPRINTF(fmt, ...) do { \
41 if (DEBUG_AUX) { \
42 qemu_log("aux: " fmt , ## __VA_ARGS__); \
43 } \
44} while (0);
45
46#define TYPE_AUXTOI2C "aux-to-i2c-bridge"
47#define AUXTOI2C(obj) OBJECT_CHECK(AUXTOI2CState, (obj), TYPE_AUXTOI2C)
48
49#define TYPE_AUX_BUS "aux-bus"
50#define AUX_BUS(obj) OBJECT_CHECK(AUXBus, (obj), TYPE_AUX_BUS)
51
52static void aux_slave_dev_print(Monitor *mon, DeviceState *dev, int indent);
53
54static void aux_bus_class_init(ObjectClass *klass, void *data)
55{
56 BusClass *k = BUS_CLASS(klass);
57
58
59
60
61 k->print_dev = aux_slave_dev_print;
62}
63
64static const TypeInfo aux_bus_info = {
65 .name = TYPE_AUX_BUS,
66 .parent = TYPE_BUS,
67 .instance_size = sizeof(AUXBus),
68 .class_init = aux_bus_class_init
69};
70
71AUXBus *aux_init_bus(DeviceState *parent, const char *name)
72{
73 AUXBus *bus;
74
75 bus = AUX_BUS(qbus_create(TYPE_AUX_BUS, parent, name));
76 bus->bridge = AUXTOI2C(qdev_create(BUS(bus), TYPE_AUXTOI2C));
77
78
79 bus->aux_io = g_malloc(sizeof(*bus->aux_io));
80 memory_region_init(bus->aux_io, OBJECT(bus), "aux-io", (1 << 20));
81 address_space_init(&bus->aux_addr_space, bus->aux_io, "aux-io");
82 return bus;
83}
84
85static void aux_bus_map_device(AUXBus *bus, AUXSlave *dev, hwaddr addr)
86{
87 memory_region_add_subregion(bus->aux_io, addr, dev->mmio);
88}
89
90static bool aux_bus_is_bridge(AUXBus *bus, DeviceState *dev)
91{
92 return (dev == DEVICE(bus->bridge));
93}
94
95AUXReply aux_request(AUXBus *bus, AUXCommand cmd, uint32_t address,
96 uint8_t len, uint8_t *data)
97{
98 AUXReply ret = AUX_NACK;
99 I2CBus *i2c_bus = aux_get_i2c_bus(bus);
100 size_t i;
101 bool is_write = false;
102
103 DPRINTF("request at address 0x%" PRIX32 ", command %u, len %u\n", address,
104 cmd, len);
105
106 switch (cmd) {
107
108
109
110 case WRITE_AUX:
111 is_write = true;
112
113 case READ_AUX:
114 for (i = 0; i < len; i++) {
115 if (!address_space_rw(&bus->aux_addr_space, address++,
116 MEMTXATTRS_UNSPECIFIED, data++, 1,
117 is_write)) {
118 ret = AUX_I2C_ACK;
119 } else {
120 ret = AUX_NACK;
121 break;
122 }
123 }
124 break;
125
126
127
128 case READ_I2C:
129 case WRITE_I2C:
130 is_write = cmd == READ_I2C ? false : true;
131 if (i2c_bus_busy(i2c_bus)) {
132 i2c_end_transfer(i2c_bus);
133 }
134
135 if (i2c_start_transfer(i2c_bus, address, is_write)) {
136 ret = AUX_I2C_NACK;
137 break;
138 }
139
140 ret = AUX_I2C_ACK;
141 while (len > 0) {
142 if (i2c_send_recv(i2c_bus, data++, is_write) < 0) {
143 ret = AUX_I2C_NACK;
144 break;
145 }
146 len--;
147 }
148 i2c_end_transfer(i2c_bus);
149 break;
150
151
152
153
154
155
156
157
158 case WRITE_I2C_MOT:
159 case READ_I2C_MOT:
160 is_write = cmd == READ_I2C_MOT ? false : true;
161 if (!i2c_bus_busy(i2c_bus)) {
162
163
164
165 if (i2c_start_transfer(i2c_bus, address, is_write)) {
166 ret = AUX_I2C_NACK;
167 break;
168 }
169 } else if ((address != bus->last_i2c_address) ||
170 (bus->last_transaction != cmd)) {
171
172
173
174 i2c_end_transfer(i2c_bus);
175 if (i2c_start_transfer(i2c_bus, address, is_write)) {
176 ret = AUX_I2C_NACK;
177 break;
178 }
179 }
180
181 while (len > 0) {
182 if (i2c_send_recv(i2c_bus, data++, is_write) < 0) {
183 ret = AUX_I2C_NACK;
184 i2c_end_transfer(i2c_bus);
185 break;
186 }
187 len--;
188 }
189 bus->last_transaction = cmd;
190 bus->last_i2c_address = address;
191 ret = AUX_I2C_ACK;
192 break;
193 default:
194 DPRINTF("Not implemented!\n");
195 ret = AUX_NACK;
196 break;
197 }
198
199 DPRINTF("reply: %u\n", ret);
200 return ret;
201}
202
203
204
205
206struct AUXTOI2CState {
207
208 DeviceState parent_obj;
209
210 I2CBus *i2c_bus;
211};
212
213I2CBus *aux_get_i2c_bus(AUXBus *bus)
214{
215 return bus->bridge->i2c_bus;
216}
217
218static void aux_bridge_init(Object *obj)
219{
220 AUXTOI2CState *s = AUXTOI2C(obj);
221
222 s->i2c_bus = i2c_init_bus(DEVICE(obj), "aux-i2c");
223}
224
225static const TypeInfo aux_to_i2c_type_info = {
226 .name = TYPE_AUXTOI2C,
227 .parent = TYPE_DEVICE,
228 .instance_size = sizeof(AUXTOI2CState),
229 .instance_init = aux_bridge_init
230};
231
232
233
234
235static void aux_slave_dev_print(Monitor *mon, DeviceState *dev, int indent)
236{
237 AUXBus *bus = AUX_BUS(qdev_get_parent_bus(dev));
238 AUXSlave *s;
239
240
241 if (aux_bus_is_bridge(bus, dev)) {
242 return;
243 }
244
245 s = AUX_SLAVE(dev);
246
247 monitor_printf(mon, "%*smemory " TARGET_FMT_plx "/" TARGET_FMT_plx "\n",
248 indent, "",
249 object_property_get_int(OBJECT(s->mmio), "addr", NULL),
250 memory_region_size(s->mmio));
251}
252
253DeviceState *aux_create_slave(AUXBus *bus, const char *type, uint32_t addr)
254{
255 DeviceState *dev;
256
257 dev = DEVICE(object_new(type));
258 assert(dev);
259 qdev_set_parent_bus(dev, &bus->qbus);
260 qdev_init_nofail(dev);
261 aux_bus_map_device(AUX_BUS(qdev_get_parent_bus(dev)), AUX_SLAVE(dev), addr);
262 return dev;
263}
264
265void aux_init_mmio(AUXSlave *aux_slave, MemoryRegion *mmio)
266{
267 assert(!aux_slave->mmio);
268 aux_slave->mmio = mmio;
269}
270
271static void aux_slave_class_init(ObjectClass *klass, void *data)
272{
273 DeviceClass *k = DEVICE_CLASS(klass);
274
275 set_bit(DEVICE_CATEGORY_MISC, k->categories);
276 k->bus_type = TYPE_AUX_BUS;
277}
278
279static const TypeInfo aux_slave_type_info = {
280 .name = TYPE_AUX_SLAVE,
281 .parent = TYPE_DEVICE,
282 .instance_size = sizeof(AUXSlave),
283 .abstract = true,
284 .class_init = aux_slave_class_init,
285};
286
287static void aux_slave_register_types(void)
288{
289 type_register_static(&aux_bus_info);
290 type_register_static(&aux_slave_type_info);
291 type_register_static(&aux_to_i2c_type_info);
292}
293
294type_init(aux_slave_register_types)
295