xref: /openbmc/qemu/hw/misc/auxbus.c (revision 10df8ff1)
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 "hw/misc/auxbus.h"
33 #include "hw/i2c/i2c.h"
34 #include "monitor/monitor.h"
35 #include "qapi/error.h"
36 
37 #ifndef DEBUG_AUX
38 #define DEBUG_AUX 0
39 #endif
40 
41 #define DPRINTF(fmt, ...) do {                                                 \
42     if (DEBUG_AUX) {                                                           \
43         qemu_log("aux: " fmt , ## __VA_ARGS__);                                \
44     }                                                                          \
45 } while (0)
46 
47 #define TYPE_AUXTOI2C "aux-to-i2c-bridge"
48 #define AUXTOI2C(obj) OBJECT_CHECK(AUXTOI2CState, (obj), TYPE_AUXTOI2C)
49 
50 static void aux_slave_dev_print(Monitor *mon, DeviceState *dev, int indent);
51 static inline I2CBus *aux_bridge_get_i2c_bus(AUXTOI2CState *bridge);
52 
53 /* aux-bus implementation (internal not public) */
54 static void aux_bus_class_init(ObjectClass *klass, void *data)
55 {
56     BusClass *k = BUS_CLASS(klass);
57 
58     /* AUXSlave has an MMIO so we need to change the way we print information
59      * in monitor.
60      */
61     k->print_dev = aux_slave_dev_print;
62 }
63 
64 AUXBus *aux_init_bus(DeviceState *parent, const char *name)
65 {
66     AUXBus *bus;
67     Object *auxtoi2c;
68 
69     bus = AUX_BUS(qbus_create(TYPE_AUX_BUS, parent, name));
70     auxtoi2c = object_new_with_props(TYPE_AUXTOI2C, OBJECT(bus), "i2c",
71                                      &error_abort, NULL);
72     qdev_set_parent_bus(DEVICE(auxtoi2c), BUS(bus));
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_map_slave(AUXSlave *aux_dev, hwaddr addr)
84 {
85     DeviceState *dev = DEVICE(aux_dev);
86     AUXBus *bus = AUX_BUS(qdev_get_parent_bus(dev));
87     memory_region_add_subregion(bus->aux_io, addr, aux_dev->mmio);
88 }
89 
90 static bool aux_bus_is_bridge(AUXBus *bus, DeviceState *dev)
91 {
92     return (dev == DEVICE(bus->bridge));
93 }
94 
95 I2CBus *aux_get_i2c_bus(AUXBus *bus)
96 {
97     return aux_bridge_get_i2c_bus(bus->bridge);
98 }
99 
100 AUXReply aux_request(AUXBus *bus, AUXCommand cmd, uint32_t address,
101                       uint8_t len, uint8_t *data)
102 {
103     AUXReply ret = AUX_NACK;
104     I2CBus *i2c_bus = aux_get_i2c_bus(bus);
105     size_t i;
106     bool is_write = false;
107 
108     DPRINTF("request at address 0x%" PRIX32 ", command %u, len %u\n", address,
109             cmd, len);
110 
111     switch (cmd) {
112     /*
113      * Forward the request on the AUX bus..
114      */
115     case WRITE_AUX:
116     case READ_AUX:
117         is_write = cmd == READ_AUX ? false : true;
118         for (i = 0; i < len; i++) {
119             if (!address_space_rw(&bus->aux_addr_space, address++,
120                                   MEMTXATTRS_UNSPECIFIED, data++, 1,
121                                   is_write)) {
122                 ret = AUX_I2C_ACK;
123             } else {
124                 ret = AUX_NACK;
125                 break;
126             }
127         }
128         break;
129     /*
130      * Classic I2C transactions..
131      */
132     case READ_I2C:
133     case WRITE_I2C:
134         is_write = cmd == READ_I2C ? false : true;
135         if (i2c_bus_busy(i2c_bus)) {
136             i2c_end_transfer(i2c_bus);
137         }
138 
139         if (i2c_start_transfer(i2c_bus, address, is_write)) {
140             ret = AUX_I2C_NACK;
141             break;
142         }
143 
144         ret = AUX_I2C_ACK;
145         while (len > 0) {
146             if (i2c_send_recv(i2c_bus, data++, is_write) < 0) {
147                 ret = AUX_I2C_NACK;
148                 break;
149             }
150             len--;
151         }
152         i2c_end_transfer(i2c_bus);
153         break;
154     /*
155      * I2C MOT transactions.
156      *
157      * Here we send a start when:
158      *  - We didn't start transaction yet.
159      *  - We had a READ and we do a WRITE.
160      *  - We changed the address.
161      */
162     case WRITE_I2C_MOT:
163     case READ_I2C_MOT:
164         is_write = cmd == READ_I2C_MOT ? false : true;
165         ret = AUX_I2C_NACK;
166         if (!i2c_bus_busy(i2c_bus)) {
167             /*
168              * No transactions started..
169              */
170             if (i2c_start_transfer(i2c_bus, address, is_write)) {
171                 break;
172             }
173         } else if ((address != bus->last_i2c_address) ||
174                    (bus->last_transaction != cmd)) {
175             /*
176              * Transaction started but we need to restart..
177              */
178             i2c_end_transfer(i2c_bus);
179             if (i2c_start_transfer(i2c_bus, address, is_write)) {
180                 break;
181             }
182         }
183 
184         bus->last_transaction = cmd;
185         bus->last_i2c_address = address;
186         while (len > 0) {
187             if (i2c_send_recv(i2c_bus, data++, is_write) < 0) {
188                 i2c_end_transfer(i2c_bus);
189                 break;
190             }
191             len--;
192         }
193         if (len == 0) {
194             ret = AUX_I2C_ACK;
195         }
196         break;
197     default:
198         DPRINTF("Not implemented!\n");
199         return AUX_NACK;
200     }
201 
202     DPRINTF("reply: %u\n", ret);
203     return ret;
204 }
205 
206 static const TypeInfo aux_bus_info = {
207     .name = TYPE_AUX_BUS,
208     .parent = TYPE_BUS,
209     .instance_size = sizeof(AUXBus),
210     .class_init = aux_bus_class_init
211 };
212 
213 /* aux-i2c implementation (internal not public) */
214 struct AUXTOI2CState {
215     /*< private >*/
216     DeviceState parent_obj;
217 
218     /*< public >*/
219     I2CBus *i2c_bus;
220 };
221 
222 static void aux_bridge_class_init(ObjectClass *oc, void *data)
223 {
224     DeviceClass *dc = DEVICE_CLASS(oc);
225 
226     /* This device is private and is created only once for each
227      * aux-bus in aux_init_bus(..). So don't allow the user to add one.
228      */
229     dc->user_creatable = false;
230 }
231 
232 static void aux_bridge_init(Object *obj)
233 {
234     AUXTOI2CState *s = AUXTOI2C(obj);
235 
236     s->i2c_bus = i2c_init_bus(DEVICE(obj), "aux-i2c");
237 }
238 
239 static inline I2CBus *aux_bridge_get_i2c_bus(AUXTOI2CState *bridge)
240 {
241     return bridge->i2c_bus;
242 }
243 
244 static const TypeInfo aux_to_i2c_type_info = {
245     .name = TYPE_AUXTOI2C,
246     .parent = TYPE_DEVICE,
247     .class_init = aux_bridge_class_init,
248     .instance_size = sizeof(AUXTOI2CState),
249     .instance_init = aux_bridge_init
250 };
251 
252 /* aux-slave implementation */
253 static void aux_slave_dev_print(Monitor *mon, DeviceState *dev, int indent)
254 {
255     AUXBus *bus = AUX_BUS(qdev_get_parent_bus(dev));
256     AUXSlave *s;
257 
258     /* Don't print anything if the device is I2C "bridge". */
259     if (aux_bus_is_bridge(bus, dev)) {
260         return;
261     }
262 
263     s = AUX_SLAVE(dev);
264 
265     monitor_printf(mon, "%*smemory " TARGET_FMT_plx "/" TARGET_FMT_plx "\n",
266                    indent, "",
267                    object_property_get_uint(OBJECT(s->mmio), "addr", NULL),
268                    memory_region_size(s->mmio));
269 }
270 
271 DeviceState *aux_create_slave(AUXBus *bus, const char *type)
272 {
273     DeviceState *dev;
274 
275     dev = DEVICE(object_new(type));
276     assert(dev);
277     qdev_set_parent_bus(dev, &bus->qbus);
278     return dev;
279 }
280 
281 void aux_init_mmio(AUXSlave *aux_slave, MemoryRegion *mmio)
282 {
283     assert(!aux_slave->mmio);
284     aux_slave->mmio = mmio;
285 }
286 
287 static void aux_slave_class_init(ObjectClass *klass, void *data)
288 {
289     DeviceClass *k = DEVICE_CLASS(klass);
290 
291     set_bit(DEVICE_CATEGORY_MISC, k->categories);
292     k->bus_type = TYPE_AUX_BUS;
293 }
294 
295 static const TypeInfo aux_slave_type_info = {
296     .name = TYPE_AUX_SLAVE,
297     .parent = TYPE_DEVICE,
298     .instance_size = sizeof(AUXSlave),
299     .abstract = true,
300     .class_init = aux_slave_class_init,
301 };
302 
303 static void aux_register_types(void)
304 {
305     type_register_static(&aux_bus_info);
306     type_register_static(&aux_slave_type_info);
307     type_register_static(&aux_to_i2c_type_info);
308 }
309 
310 type_init(aux_register_types)
311