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