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