xref: /openbmc/qemu/hw/misc/auxbus.c (revision 581e109d)
1 /*
2  * auxbus.c
3  *
4  *  Copyright 2015 : GreenSocs Ltd
5  *      http://www.greensocs.com/ , email: info@greensocs.com
6  *
7  *  Developed by :
8  *  Frederic Konrad   <fred.konrad@greensocs.com>
9  *
10  * This program is free software; you can redistribute it and/or modify
11  * it under the terms of the GNU General Public License as published by
12  * the Free Software Foundation, either version 2 of the License, or
13  * (at your option)any later version.
14  *
15  * This program is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18  * GNU General Public License for more details.
19  *
20  * You should have received a copy of the GNU General Public License along
21  * with this program; if not, see <http://www.gnu.org/licenses/>.
22  *
23  */
24 
25 /*
26  * This is an implementation of the AUX bus for VESA Display Port v1.1a.
27  */
28 
29 #include "qemu/osdep.h"
30 #include "qemu/units.h"
31 #include "qemu/log.h"
32 #include "qemu/module.h"
33 #include "hw/misc/auxbus.h"
34 #include "hw/i2c/i2c.h"
35 #include "monitor/monitor.h"
36 #include "qapi/error.h"
37 
38 #ifndef DEBUG_AUX
39 #define DEBUG_AUX 0
40 #endif
41 
42 #define DPRINTF(fmt, ...) do {                                                 \
43     if (DEBUG_AUX) {                                                           \
44         qemu_log("aux: " fmt , ## __VA_ARGS__);                                \
45     }                                                                          \
46 } while (0)
47 
48 #define TYPE_AUXTOI2C "aux-to-i2c-bridge"
49 #define AUXTOI2C(obj) OBJECT_CHECK(AUXTOI2CState, (obj), TYPE_AUXTOI2C)
50 
51 static void aux_slave_dev_print(Monitor *mon, DeviceState *dev, int indent);
52 static inline I2CBus *aux_bridge_get_i2c_bus(AUXTOI2CState *bridge);
53 
54 /* aux-bus implementation (internal not public) */
55 static void aux_bus_class_init(ObjectClass *klass, void *data)
56 {
57     BusClass *k = BUS_CLASS(klass);
58 
59     /* AUXSlave has an MMIO so we need to change the way we print information
60      * in monitor.
61      */
62     k->print_dev = aux_slave_dev_print;
63 }
64 
65 AUXBus *aux_bus_init(DeviceState *parent, const char *name)
66 {
67     AUXBus *bus;
68     Object *auxtoi2c;
69 
70     bus = AUX_BUS(qbus_create(TYPE_AUX_BUS, parent, name));
71     auxtoi2c = object_new_with_props(TYPE_AUXTOI2C, OBJECT(bus), "i2c",
72                                      &error_abort, NULL);
73 
74     bus->bridge = AUXTOI2C(auxtoi2c);
75 
76     /* Memory related. */
77     bus->aux_io = g_malloc(sizeof(*bus->aux_io));
78     memory_region_init(bus->aux_io, OBJECT(bus), "aux-io", 1 * MiB);
79     address_space_init(&bus->aux_addr_space, bus->aux_io, "aux-io");
80     return bus;
81 }
82 
83 void aux_bus_realize(AUXBus *bus)
84 {
85     qdev_realize(DEVICE(bus->bridge), BUS(bus), &error_fatal);
86 }
87 
88 void aux_map_slave(AUXSlave *aux_dev, hwaddr addr)
89 {
90     DeviceState *dev = DEVICE(aux_dev);
91     AUXBus *bus = AUX_BUS(qdev_get_parent_bus(dev));
92     memory_region_add_subregion(bus->aux_io, addr, aux_dev->mmio);
93 }
94 
95 static bool aux_bus_is_bridge(AUXBus *bus, DeviceState *dev)
96 {
97     return (dev == DEVICE(bus->bridge));
98 }
99 
100 I2CBus *aux_get_i2c_bus(AUXBus *bus)
101 {
102     return aux_bridge_get_i2c_bus(bus->bridge);
103 }
104 
105 AUXReply aux_request(AUXBus *bus, AUXCommand cmd, uint32_t address,
106                       uint8_t len, uint8_t *data)
107 {
108     AUXReply ret = AUX_NACK;
109     I2CBus *i2c_bus = aux_get_i2c_bus(bus);
110     size_t i;
111     bool is_write = false;
112 
113     DPRINTF("request at address 0x%" PRIX32 ", command %u, len %u\n", address,
114             cmd, len);
115 
116     switch (cmd) {
117     /*
118      * Forward the request on the AUX bus..
119      */
120     case WRITE_AUX:
121     case READ_AUX:
122         is_write = cmd == READ_AUX ? false : true;
123         for (i = 0; i < len; i++) {
124             if (!address_space_rw(&bus->aux_addr_space, address++,
125                                   MEMTXATTRS_UNSPECIFIED, data++, 1,
126                                   is_write)) {
127                 ret = AUX_I2C_ACK;
128             } else {
129                 ret = AUX_NACK;
130                 break;
131             }
132         }
133         break;
134     /*
135      * Classic I2C transactions..
136      */
137     case READ_I2C:
138     case WRITE_I2C:
139         is_write = cmd == READ_I2C ? false : true;
140         if (i2c_bus_busy(i2c_bus)) {
141             i2c_end_transfer(i2c_bus);
142         }
143 
144         if (i2c_start_transfer(i2c_bus, address, is_write)) {
145             ret = AUX_I2C_NACK;
146             break;
147         }
148 
149         ret = AUX_I2C_ACK;
150         while (len > 0) {
151             if (i2c_send_recv(i2c_bus, data++, is_write) < 0) {
152                 ret = AUX_I2C_NACK;
153                 break;
154             }
155             len--;
156         }
157         i2c_end_transfer(i2c_bus);
158         break;
159     /*
160      * I2C MOT transactions.
161      *
162      * Here we send a start when:
163      *  - We didn't start transaction yet.
164      *  - We had a READ and we do a WRITE.
165      *  - We changed the address.
166      */
167     case WRITE_I2C_MOT:
168     case READ_I2C_MOT:
169         is_write = cmd == READ_I2C_MOT ? false : true;
170         ret = AUX_I2C_NACK;
171         if (!i2c_bus_busy(i2c_bus)) {
172             /*
173              * No transactions started..
174              */
175             if (i2c_start_transfer(i2c_bus, address, is_write)) {
176                 break;
177             }
178         } else if ((address != bus->last_i2c_address) ||
179                    (bus->last_transaction != cmd)) {
180             /*
181              * Transaction started but we need to restart..
182              */
183             i2c_end_transfer(i2c_bus);
184             if (i2c_start_transfer(i2c_bus, address, is_write)) {
185                 break;
186             }
187         }
188 
189         bus->last_transaction = cmd;
190         bus->last_i2c_address = address;
191         while (len > 0) {
192             if (i2c_send_recv(i2c_bus, data++, is_write) < 0) {
193                 i2c_end_transfer(i2c_bus);
194                 break;
195             }
196             len--;
197         }
198         if (len == 0) {
199             ret = AUX_I2C_ACK;
200         }
201         break;
202     default:
203         qemu_log_mask(LOG_UNIMP, "AUX cmd=%u not implemented\n", cmd);
204         return AUX_NACK;
205     }
206 
207     DPRINTF("reply: %u\n", ret);
208     return ret;
209 }
210 
211 static const TypeInfo aux_bus_info = {
212     .name = TYPE_AUX_BUS,
213     .parent = TYPE_BUS,
214     .instance_size = sizeof(AUXBus),
215     .class_init = aux_bus_class_init
216 };
217 
218 /* aux-i2c implementation (internal not public) */
219 struct AUXTOI2CState {
220     /*< private >*/
221     DeviceState parent_obj;
222 
223     /*< public >*/
224     I2CBus *i2c_bus;
225 };
226 
227 static void aux_bridge_class_init(ObjectClass *oc, void *data)
228 {
229     DeviceClass *dc = DEVICE_CLASS(oc);
230 
231     /* This device is private and is created only once for each
232      * aux-bus in aux_bus_init(..). So don't allow the user to add one.
233      */
234     dc->user_creatable = false;
235 }
236 
237 static void aux_bridge_init(Object *obj)
238 {
239     AUXTOI2CState *s = AUXTOI2C(obj);
240 
241     s->i2c_bus = i2c_init_bus(DEVICE(obj), "aux-i2c");
242 }
243 
244 static inline I2CBus *aux_bridge_get_i2c_bus(AUXTOI2CState *bridge)
245 {
246     return bridge->i2c_bus;
247 }
248 
249 static const TypeInfo aux_to_i2c_type_info = {
250     .name = TYPE_AUXTOI2C,
251     .parent = TYPE_AUX_SLAVE,
252     .class_init = aux_bridge_class_init,
253     .instance_size = sizeof(AUXTOI2CState),
254     .instance_init = aux_bridge_init
255 };
256 
257 /* aux-slave implementation */
258 static void aux_slave_dev_print(Monitor *mon, DeviceState *dev, int indent)
259 {
260     AUXBus *bus = AUX_BUS(qdev_get_parent_bus(dev));
261     AUXSlave *s;
262 
263     /* Don't print anything if the device is I2C "bridge". */
264     if (aux_bus_is_bridge(bus, dev)) {
265         return;
266     }
267 
268     s = AUX_SLAVE(dev);
269 
270     monitor_printf(mon, "%*smemory " TARGET_FMT_plx "/" TARGET_FMT_plx "\n",
271                    indent, "",
272                    object_property_get_uint(OBJECT(s->mmio), "addr", NULL),
273                    memory_region_size(s->mmio));
274 }
275 
276 void aux_init_mmio(AUXSlave *aux_slave, MemoryRegion *mmio)
277 {
278     assert(!aux_slave->mmio);
279     aux_slave->mmio = mmio;
280 }
281 
282 static void aux_slave_class_init(ObjectClass *klass, void *data)
283 {
284     DeviceClass *k = DEVICE_CLASS(klass);
285 
286     set_bit(DEVICE_CATEGORY_MISC, k->categories);
287     k->bus_type = TYPE_AUX_BUS;
288 }
289 
290 static const TypeInfo aux_slave_type_info = {
291     .name = TYPE_AUX_SLAVE,
292     .parent = TYPE_DEVICE,
293     .instance_size = sizeof(AUXSlave),
294     .abstract = true,
295     .class_init = aux_slave_class_init,
296 };
297 
298 static void aux_register_types(void)
299 {
300     type_register_static(&aux_bus_info);
301     type_register_static(&aux_slave_type_info);
302     type_register_static(&aux_to_i2c_type_info);
303 }
304 
305 type_init(aux_register_types)
306