All of lore.kernel.org
 help / color / mirror / Atom feed
From: fred.konrad@greensocs.com
To: qemu-devel@nongnu.org
Cc: peter.crosthwaite@xilinx.com, mark.burton@greensocs.com,
	hyunk@xilinx.com, fred.konrad@greensocs.com
Subject: [Qemu-devel] [PATCH 1/8] Introduce AUX bus.
Date: Wed, 13 May 2015 21:11:59 +0200	[thread overview]
Message-ID: <1431544326-13372-2-git-send-email-fred.konrad@greensocs.com> (raw)
In-Reply-To: <1431544326-13372-1-git-send-email-fred.konrad@greensocs.com>

From: KONRAD Frederic <fred.konrad@greensocs.com>

This introduces a new bus: aux-bus.

It contains an address space for aux slaves devices and a bridge to an I2C bus
for I2C through AUX transactions.

Signed-off-by: KONRAD Frederic <fred.konrad@greensocs.com>
---
 hw/misc/Makefile.objs |   1 +
 hw/misc/aux.c         | 421 ++++++++++++++++++++++++++++++++++++++++++++++++++
 include/hw/aux.h      | 127 +++++++++++++++
 3 files changed, 549 insertions(+)
 create mode 100644 hw/misc/aux.c
 create mode 100644 include/hw/aux.h

diff --git a/hw/misc/Makefile.objs b/hw/misc/Makefile.objs
index 4aa76ff..61eb59b 100644
--- a/hw/misc/Makefile.objs
+++ b/hw/misc/Makefile.objs
@@ -40,3 +40,4 @@ obj-$(CONFIG_STM32F2XX_SYSCFG) += stm32f2xx_syscfg.o
 
 obj-$(CONFIG_PVPANIC) += pvpanic.o
 obj-$(CONFIG_EDU) += edu.o
+obj-y += aux.o
diff --git a/hw/misc/aux.c b/hw/misc/aux.c
new file mode 100644
index 0000000..ad64acd
--- /dev/null
+++ b/hw/misc/aux.c
@@ -0,0 +1,421 @@
+/*
+ * aux.c
+ *
+ *  Copyright 2015 : GreenSocs Ltd
+ *      http://www.greensocs.com/ , email: info@greensocs.com
+ *
+ *  Developed by :
+ *  Frederic Konrad   <fred.konrad@greensocs.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 2 of the License, or
+ * (at your option)any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+/*
+ * This is an implementation of the AUX bus for VESA Display Port v1.1a.
+ */
+
+#include "hw/aux.h"
+#include "hw/i2c/i2c.h"
+#include "monitor/monitor.h"
+
+/* #define DEBUG_AUX */
+
+#ifdef DEBUG_AUX
+#define DPRINTF(fmt, ...)\
+do { printf("aux: " fmt , ## __VA_ARGS__); } while (0)
+#else
+#define DPRINTF(fmt, ...)do {} while (0)
+#endif
+
+#define TYPE_AUXTOI2C "aux-to-i2c-bridge"
+#define AUXTOI2C(obj) OBJECT_CHECK(AUXTOI2CState, (obj), TYPE_AUXTOI2C)
+
+typedef struct AUXTOI2CState AUXTOI2CState;
+
+struct AUXBus {
+    BusState qbus;
+    AUXSlave *current_dev;
+    AUXSlave *dev;
+    uint32_t last_i2c_address;
+    aux_command last_transaction;
+
+    AUXTOI2CState *bridge;
+
+    MemoryRegion *aux_io;
+    AddressSpace aux_addr_space;
+};
+
+static Property aux_props[] = {
+    DEFINE_PROP_UINT64("address", struct AUXSlave, address, 0),
+    DEFINE_PROP_END_OF_LIST(),
+};
+
+#define TYPE_AUX_BUS "aux-bus"
+#define AUX_BUS(obj) OBJECT_CHECK(AUXBus, (obj), TYPE_AUX_BUS)
+
+static void aux_slave_dev_print(Monitor *mon, DeviceState *dev, int indent);
+
+static void aux_bus_class_init(ObjectClass *klass, void *data)
+{
+    /*
+     * AUXSlave has an mmio so we need to change the way we print information
+     * in monitor.
+     */
+    BusClass *k = BUS_CLASS(klass);
+    k->print_dev = aux_slave_dev_print;
+}
+
+static const TypeInfo aux_bus_info = {
+    .name = TYPE_AUX_BUS,
+    .parent = TYPE_BUS,
+    .instance_size = sizeof(AUXBus),
+    .class_init = aux_bus_class_init
+};
+
+AUXBus *aux_init_bus(DeviceState *parent, const char *name)
+{
+    AUXBus *bus;
+
+    bus = AUX_BUS(qbus_create(TYPE_AUX_BUS, parent, name));
+
+    /*
+     * Create the bridge.
+     */
+    bus->bridge = AUXTOI2C(qdev_create(BUS(bus), TYPE_AUXTOI2C));
+
+    /*
+     * Memory related.
+     */
+    bus->aux_io = g_malloc(sizeof(*bus->aux_io));
+    memory_region_init(bus->aux_io, OBJECT(bus), "aux-io", (1 << 20));
+    address_space_init(&bus->aux_addr_space, bus->aux_io, "aux-io");
+    return bus;
+}
+
+static void aux_bus_map_device(AUXBus *bus, AUXSlave *dev)
+{
+    memory_region_add_subregion(bus->aux_io, dev->address, dev->mmio);
+}
+
+void aux_set_slave_address(AUXSlave *dev, uint32_t address)
+{
+    qdev_prop_set_uint64(DEVICE(dev), "address", address);
+}
+
+static bool aux_bus_is_bridge(AUXBus *bus, DeviceState *dev)
+{
+    return (dev == DEVICE(bus->bridge));
+}
+
+/*
+ * Make a native request on the AUX bus.
+ */
+static aux_reply aux_native_request(AUXBus *bus, aux_command cmd,
+                                    uint32_t address, uint8_t len,
+                                    uint8_t *data)
+{
+    /*
+     * Transactions on aux address map are 1bytes len time.
+     */
+    aux_reply ret = AUX_NACK;
+    size_t i;
+
+    switch (cmd) {
+    case READ_AUX:
+        for (i = 0; i < len; i++) {
+            if (!address_space_rw(&bus->aux_addr_space, address++,
+                                  MEMTXATTRS_UNSPECIFIED, data++, 1, false)) {
+                ret = AUX_I2C_ACK;
+            } else {
+                ret = AUX_NACK;
+                break;
+            }
+        }
+    break;
+    case WRITE_AUX:
+        for (i = 0; i < len; i++) {
+            if (!address_space_rw(&bus->aux_addr_space, address++,
+                                  MEMTXATTRS_UNSPECIFIED, data++, 1, true)) {
+                ret = AUX_I2C_ACK;
+            } else {
+                ret = AUX_NACK;
+                break;
+            }
+        }
+    break;
+    default:
+        abort();
+    break;
+    }
+
+    return ret;
+}
+
+aux_reply aux_request(AUXBus *bus, aux_command cmd, uint32_t address,
+                      uint8_t len, uint8_t *data)
+{
+    DPRINTF("request at address 0x%5.5X, command %u, len %u\n", address, cmd,
+            len);
+
+    int temp;
+    aux_reply ret = AUX_NACK;
+    I2CBus *i2c_bus = aux_get_i2c_bus(bus);
+
+    switch (cmd) {
+    /*
+     * Forward the request on the AUX bus..
+     */
+    case WRITE_AUX:
+    case READ_AUX:
+        ret = aux_native_request(bus, cmd, address, len, data);
+    break;
+    /*
+     * Classic I2C transactions..
+     */
+    case READ_I2C:
+        if (i2c_bus_busy(i2c_bus)) {
+            i2c_end_transfer(i2c_bus);
+        }
+
+        if (i2c_start_transfer(i2c_bus, address, 1)) {
+            ret = AUX_I2C_NACK;
+            break;
+        }
+
+        while (len > 0) {
+            temp = i2c_recv(i2c_bus);
+
+            if (temp < 0) {
+                ret = AUX_I2C_NACK;
+                i2c_end_transfer(i2c_bus);
+                break;
+            }
+
+            *data++ = temp;
+            len--;
+        }
+        i2c_end_transfer(i2c_bus);
+        ret = AUX_I2C_ACK;
+    break;
+    case WRITE_I2C:
+        if (i2c_bus_busy(i2c_bus)) {
+            i2c_end_transfer(i2c_bus);
+        }
+
+        if (i2c_start_transfer(i2c_bus, address, 0)) {
+            ret = AUX_I2C_NACK;
+            break;
+        }
+
+        while (len > 0) {
+            if (!i2c_send(i2c_bus, *data++)) {
+                ret = AUX_I2C_NACK;
+                i2c_end_transfer(i2c_bus);
+                break;
+            }
+            len--;
+        }
+        i2c_end_transfer(i2c_bus);
+        ret = AUX_I2C_ACK;
+    break;
+    /*
+     * I2C MOT transactions.
+     *
+     * Here we send a start when:
+     *  - We didn't start transaction yet.
+     *  - We had a READ and we do a WRITE.
+     *  - We change the address.
+     */
+    case WRITE_I2C_MOT:
+        if (!i2c_bus_busy(i2c_bus)) {
+            /*
+             * No transactions started..
+             */
+            if (i2c_start_transfer(i2c_bus, address, 0)) {
+                ret = AUX_I2C_NACK;
+                break;
+            }
+        } else if ((address != bus->last_i2c_address) ||
+                   (bus->last_transaction == READ_I2C_MOT)) {
+            /*
+             * Transaction started but we need to restart..
+             */
+            i2c_end_transfer(i2c_bus);
+            if (i2c_start_transfer(i2c_bus, address, 0)) {
+                ret = AUX_I2C_NACK;
+                break;
+            }
+        }
+
+        while (len > 0) {
+            if (!i2c_send(i2c_bus, *data++)) {
+                ret = AUX_I2C_NACK;
+                i2c_end_transfer(i2c_bus);
+                break;
+            }
+            len--;
+        }
+        bus->last_transaction = WRITE_I2C_MOT;
+        bus->last_i2c_address = address;
+        ret = AUX_I2C_ACK;
+    break;
+    case READ_I2C_MOT:
+        if (!i2c_bus_busy(i2c_bus)) {
+            /*
+             * No transactions started..
+             */
+            if (i2c_start_transfer(i2c_bus, address, 0)) {
+                ret = AUX_I2C_NACK;
+                break;
+            }
+        } else if (address != bus->last_i2c_address) {
+            /*
+             * Transaction started but we need to restart..
+             */
+            i2c_end_transfer(i2c_bus);
+            if (i2c_start_transfer(i2c_bus, address, 0)) {
+                ret = AUX_I2C_NACK;
+                break;
+            }
+        }
+
+        while (len > 0) {
+            temp = i2c_recv(i2c_bus);
+
+            if (temp < 0) {
+                ret = AUX_I2C_NACK;
+                i2c_end_transfer(i2c_bus);
+                break;
+            }
+
+            *data++ = temp;
+            len--;
+        }
+        bus->last_transaction = READ_I2C_MOT;
+        bus->last_i2c_address = address;
+        ret = AUX_I2C_ACK;
+    break;
+    default:
+        DPRINTF("Not implemented!\n");
+        ret = AUX_NACK;
+    break;
+    }
+
+    DPRINTF("reply: %u\n", ret);
+    return ret;
+}
+
+/*
+ * AUX to I2C bridge.
+ */
+struct AUXTOI2CState {
+    DeviceState parent_obj;
+    I2CBus *i2c_bus;
+};
+
+I2CBus *aux_get_i2c_bus(AUXBus *bus)
+{
+    return bus->bridge->i2c_bus;
+}
+
+static void aux_bridge_init(Object *obj)
+{
+    AUXTOI2CState *s = AUXTOI2C(obj);
+    /*
+     * Create the I2C Bus.
+     */
+    s->i2c_bus = i2c_init_bus(DEVICE(obj), "aux-i2c");
+}
+
+static const TypeInfo aux_to_i2c_type_info = {
+    .name = TYPE_AUXTOI2C,
+    .parent = TYPE_DEVICE,
+    .instance_size = sizeof(AUXTOI2CState),
+    .instance_init = aux_bridge_init
+};
+
+/*
+ * AUX Slave.
+ */
+static void aux_slave_dev_print(Monitor *mon, DeviceState *dev, int indent)
+{
+    AUXBus *bus = AUX_BUS(qdev_get_parent_bus(dev));
+    hwaddr size;
+    AUXSlave *s;
+
+    /*
+     * Don't print anything if the device is I2C "bridge".
+     */
+    if (aux_bus_is_bridge(bus, dev)) {
+        return;
+    }
+
+    s = AUX_SLAVE(dev);
+
+    size = memory_region_size(s->mmio);
+    monitor_printf(mon, "%*smemory " TARGET_FMT_plx "/" TARGET_FMT_plx "\n",
+                   indent, "", s->address, size);
+}
+
+static int aux_slave_qdev_init(DeviceState *dev)
+{
+    AUXSlave *s = AUX_SLAVE(dev);
+    AUXSlaveClass *sc = AUX_SLAVE_GET_CLASS(s);
+
+    return sc->init(s);
+}
+
+DeviceState *aux_create_slave(AUXBus *bus, const char *name, uint32_t addr)
+{
+    DeviceState *dev;
+
+    dev = qdev_create(&bus->qbus, name);
+    qdev_prop_set_uint64(dev, "address", addr);
+    qdev_init_nofail(dev);
+    aux_bus_map_device(AUX_BUS(qdev_get_parent_bus(dev)), AUX_SLAVE(dev));
+    return dev;
+}
+
+void aux_init_mmio(AUXSlave *aux_slave, MemoryRegion *mmio)
+{
+    aux_slave->mmio = mmio;
+}
+
+static void aux_slave_class_init(ObjectClass *klass, void *data)
+{
+    DeviceClass *k = DEVICE_CLASS(klass);
+    k->init = aux_slave_qdev_init;
+    set_bit(DEVICE_CATEGORY_MISC, k->categories);
+    k->bus_type = TYPE_AUX_BUS;
+    k->props = aux_props;
+}
+
+static const TypeInfo aux_slave_type_info = {
+    .name = TYPE_AUX_SLAVE,
+    .parent = TYPE_DEVICE,
+    .instance_size = sizeof(AUXSlave),
+    .abstract = true,
+    .class_size = sizeof(AUXSlaveClass),
+    .class_init = aux_slave_class_init,
+};
+
+static void aux_slave_register_types(void)
+{
+    type_register_static(&aux_bus_info);
+    type_register_static(&aux_slave_type_info);
+    type_register_static(&aux_to_i2c_type_info);
+}
+
+type_init(aux_slave_register_types)
diff --git a/include/hw/aux.h b/include/hw/aux.h
new file mode 100644
index 0000000..b9be4a4
--- /dev/null
+++ b/include/hw/aux.h
@@ -0,0 +1,127 @@
+/*
+ * aux.h
+ *
+ *  Copyright (C)2014 : GreenSocs Ltd
+ *      http://www.greensocs.com/ , email: info@greensocs.com
+ *
+ *  Developed by :
+ *  Frederic Konrad   <fred.konrad@greensocs.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 2 of the License, or
+ * (at your option)any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#ifndef QEMU_AUX_H
+#define QEMU_AUX_H
+
+#include "hw/qdev.h"
+
+enum aux_command {
+    WRITE_I2C = 0,
+    READ_I2C = 1,
+    WRITE_I2C_STATUS = 2,
+    WRITE_I2C_MOT = 4,
+    READ_I2C_MOT = 5,
+    WRITE_AUX = 8,
+    READ_AUX = 9
+};
+
+enum aux_reply {
+    AUX_I2C_ACK = 0,
+    AUX_NACK = 1,
+    AUX_DEFER = 2,
+    AUX_I2C_NACK = 4,
+    AUX_I2C_DEFER = 8
+};
+
+typedef struct AUXBus AUXBus;
+typedef struct AUXSlave AUXSlave;
+typedef enum aux_command aux_command;
+typedef enum aux_reply aux_reply;
+
+#define TYPE_AUX_SLAVE "aux-slave"
+#define AUX_SLAVE(obj) \
+     OBJECT_CHECK(AUXSlave, (obj), TYPE_AUX_SLAVE)
+#define AUX_SLAVE_CLASS(klass) \
+     OBJECT_CLASS_CHECK(AUXSlaveClass, (klass), TYPE_AUX_SLAVE)
+#define AUX_SLAVE_GET_CLASS(obj) \
+     OBJECT_GET_CLASS(AUXSlaveClass, (obj), TYPE_AUX_SLAVE)
+
+struct AUXSlave {
+    /* < private > */
+    DeviceState parent_obj;
+
+    /* address of the device on the aux bus. */
+    hwaddr address;
+    /* memory region associated. */
+    MemoryRegion *mmio;
+};
+
+typedef struct AUXSlaveClass {
+    DeviceClass parent_class;
+
+    /* Callbacks provided by the device.  */
+    int (*init)(AUXSlave *dev);
+} AUXSlaveClass;
+
+/*
+ * \func aux_init_bus
+ * \brief Init an aux bus.
+ * \param parent The device where this bus is located.
+ * \param name The name of the bus.
+ * \return The new aux bus.
+ */
+AUXBus *aux_init_bus(DeviceState *parent, const char *name);
+
+/*
+ * \func aux_slave_set_address
+ * \brief Set the address of the slave on the aux bus.
+ * \param dev The aux slave device.
+ * \param address The address to give to the slave.
+ */
+void aux_set_slave_address(AUXSlave *dev, uint32_t address);
+
+/*
+ * \func aux_request
+ * \brief Make a request on the bus.
+ * \param bus Ths bus where the request happen.
+ * \param cmd The command requested.
+ * \param address The 20bits address of the slave.
+ * \param len The length of the read or write.
+ * \param data The data array which will be filled or read during transfer.
+ * \return Return the reply of the request.
+ */
+aux_reply aux_request(AUXBus *bus, aux_command cmd, uint32_t address,
+                              uint8_t len, uint8_t *data);
+
+/*
+ * \func aux_get_i2c_bus
+ * \brief Get the i2c bus for I2C over AUX command.
+ * \param bus The aux bus.
+ * \return Return the i2c bus associated.
+ */
+I2CBus *aux_get_i2c_bus(AUXBus *bus);
+
+/*
+ * \func aux_init_mmio
+ * \brief Init an mmio for an aux slave, must be called after
+ *        memory_region_init_io.
+ * \param aux_slave The aux slave.
+ * \param mmio The mmio to be registered.
+ */
+void aux_init_mmio(AUXSlave *aux_slave, MemoryRegion *mmio);
+
+DeviceState *aux_create_slave(AUXBus *bus, const char *name, uint32_t addr);
+
+#endif /* !QEMU_AUX_H */
-- 
1.9.0

  reply	other threads:[~2015-05-13 19:12 UTC|newest]

Thread overview: 20+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2015-05-13 19:11 [Qemu-devel] [PATCH 0/8] Xilinx DisplayPort fred.konrad
2015-05-13 19:11 ` fred.konrad [this message]
2015-05-13 19:12 ` [Qemu-devel] [PATCH 2/8] i2c: implement broadcast write fred.konrad
2015-05-14  3:58   ` Peter Crosthwaite
2015-05-13 19:12 ` [Qemu-devel] [PATCH 3/8] console: add qemu_alloc_display_format fred.konrad
2015-05-18  7:34   ` Gerd Hoffmann
2015-05-18  7:51     ` Frederic Konrad
2015-05-18 11:17       ` Gerd Hoffmann
2015-05-18 11:56         ` Frederic Konrad
2015-05-13 19:12 ` [Qemu-devel] [PATCH 4/8] introduce dpcd module fred.konrad
2015-05-14  4:10   ` Peter Crosthwaite
2015-05-18 13:57     ` Frederic Konrad
2015-05-13 19:12 ` [Qemu-devel] [PATCH 5/8] hw/i2c-ddc.c: Implement DDC I2C slave fred.konrad
2015-05-13 19:12 ` [Qemu-devel] [PATCH 6/8] Introduce xilinx dpdma fred.konrad
2015-05-18  8:17   ` Peter Crosthwaite
2015-05-18  8:43     ` Frederic Konrad
2015-05-13 19:12 ` [Qemu-devel] [PATCH 7/8] Introduce xilinx dp fred.konrad
2015-05-13 19:12 ` [Qemu-devel] [PATCH 8/8] arm: xlnx-zynqmp: Add DisplayPort and DPDMA fred.konrad
2015-05-14  3:30   ` Peter Crosthwaite
2015-05-18  6:58     ` Frederic Konrad

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=1431544326-13372-2-git-send-email-fred.konrad@greensocs.com \
    --to=fred.konrad@greensocs.com \
    --cc=hyunk@xilinx.com \
    --cc=mark.burton@greensocs.com \
    --cc=peter.crosthwaite@xilinx.com \
    --cc=qemu-devel@nongnu.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.