qemu

FORK: QEMU emulator
git clone https://git.neptards.moe/neptards/qemu.git
Log | Files | Refs | Submodules | LICENSE

auxbus.c (9212B)


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