From patchwork Mon Jun 4 08:08:20 2012 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: "Peter A. G. Crosthwaite" X-Patchwork-Id: 162669 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Received: from lists.gnu.org (lists.gnu.org [208.118.235.17]) (using TLSv1 with cipher AES256-SHA (256/256 bits)) (Client did not present a certificate) by ozlabs.org (Postfix) with ESMTPS id EF7F2B6FF8 for ; Mon, 4 Jun 2012 18:09:13 +1000 (EST) Received: from localhost ([::1]:43126 helo=lists.gnu.org) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1SbSLe-0003HT-7y for incoming@patchwork.ozlabs.org; Mon, 04 Jun 2012 04:09:10 -0400 Received: from eggs.gnu.org ([208.118.235.92]:48360) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1SbSLC-0002c2-Rr for qemu-devel@nongnu.org; Mon, 04 Jun 2012 04:08:49 -0400 Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1SbSL9-0002iH-5N for qemu-devel@nongnu.org; Mon, 04 Jun 2012 04:08:42 -0400 Received: from mail-pb0-f45.google.com ([209.85.160.45]:58258) by eggs.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1SbSL8-0002i3-Rs for qemu-devel@nongnu.org; Mon, 04 Jun 2012 04:08:39 -0400 Received: by pbbro12 with SMTP id ro12so6151366pbb.4 for ; Mon, 04 Jun 2012 01:08:36 -0700 (PDT) X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20120113; h=from:to:cc:subject:date:message-id:x-mailer:in-reply-to:references :in-reply-to:references:x-gm-message-state; bh=pqhZpIz25WW3qlUJ4yH3+h5WAp8+aCuUK+cgj9mzPcE=; b=RoW4SEorJ/bT5ZAcimb6q4fXiodzEhwYGsK4JBR6CMvFB5F3wuNPX+hKErBlMkmkPv 2wQFwNQ2FxzvjFFTUFuY2i//ViNLoxH7JoEUt+1FURXOCLEXLK5WBTZzFuAhFCI5nQxB povvtHOL0s6Lm0EmVGw5X8ZyiZW3oDSkzJSj0iq/7TTRcgloqGWnbPd0A53ZFi0JNRK3 NF8gIMakvjF6bTvbaMHnnl+ctn9RcBpTExJTCfdzxXsqTHPgrbM691OweyHLd3v9IV1k aMn5eB/jPKqAsCPDxgrXi0H6BFNXucSA3b9XC5e7YH7PXw45wBHQmDh1AKurEvVE/gE4 E0eA== Received: by 10.68.231.164 with SMTP id th4mr36617480pbc.97.1338797316579; Mon, 04 Jun 2012 01:08:36 -0700 (PDT) Received: from localhost ([124.148.20.9]) by mx.google.com with ESMTPS id rv9sm12485815pbc.43.2012.06.04.01.08.32 (version=TLSv1/SSLv3 cipher=OTHER); Mon, 04 Jun 2012 01:08:35 -0700 (PDT) From: "Peter A. G. Crosthwaite" To: qemu-devel@nongnu.org, paul@codesourcery.com, edgar.iglesias@gmail.com, peter.maydell@linaro.org, stefanha@gmail.com Date: Mon, 4 Jun 2012 18:08:20 +1000 Message-Id: X-Mailer: git-send-email 1.7.3.2 In-Reply-To: References: In-Reply-To: References: X-Gm-Message-State: ALoCoQkS7ngYpcuaDP4tuvoV3v3ahOBiB1fZxA2SwEAuuIDdU6LmVJmJlu8uAgokLxG1Z+Eh6HwD X-detected-operating-system: by eggs.gnu.org: Genre and OS details not recognized. X-Received-From: 209.85.160.45 Cc: peter.crosthwaite@petalogix.com, john.williams@petalogix.com Subject: [Qemu-devel] [PATCH V4 1/5] SSI: Built in multiple device support X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.14 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: qemu-devel-bounces+incoming=patchwork.ozlabs.org@nongnu.org Sender: qemu-devel-bounces+incoming=patchwork.ozlabs.org@nongnu.org Added support for multiple devices attached to a single SSI bus (Previously SSI masters with multiple slaves were emulated as multiple point to point SSI busses) Signed-off-by: Peter A. G. Crosthwaite --- changed from v3: added ssi_create_slave_noinit changed from v2: This patch is new (totally rewitten replacement of (1/4) from v2) hw/spitz.c | 8 ++-- hw/ssi.c | 107 ++++++++++++++++++++++++++++++++++++++++++++++++-------- hw/ssi.h | 28 ++++++++++++-- hw/stellaris.c | 6 ++-- hw/tosa.c | 2 +- hw/z2.c | 2 +- 6 files changed, 125 insertions(+), 28 deletions(-) diff --git a/hw/spitz.c b/hw/spitz.c index 1d6d2b0..f63a9bf 100644 --- a/hw/spitz.c +++ b/hw/spitz.c @@ -669,18 +669,18 @@ static void spitz_ssp_attach(PXA2xxState *cpu) DeviceState *dev; void *bus; - mux = ssi_create_slave(cpu->ssp[CORGI_SSP_PORT - 1], "corgi-ssp"); + mux = ssi_create_slave(cpu->ssp[CORGI_SSP_PORT - 1], "corgi-ssp", 0); bus = qdev_get_child_bus(mux, "ssi0"); - ssi_create_slave(bus, "spitz-lcdtg"); + ssi_create_slave(bus, "spitz-lcdtg", 0); bus = qdev_get_child_bus(mux, "ssi1"); - dev = ssi_create_slave(bus, "ads7846"); + dev = ssi_create_slave(bus, "ads7846", 0); qdev_connect_gpio_out(dev, 0, qdev_get_gpio_in(cpu->gpio, SPITZ_GPIO_TP_INT)); bus = qdev_get_child_bus(mux, "ssi2"); - max1111 = ssi_create_slave(bus, "max1111"); + max1111 = ssi_create_slave(bus, "max1111", 0); max111x_set_input(max1111, MAX1111_BATT_VOLT, SPITZ_BATTERY_VOLT); max111x_set_input(max1111, MAX1111_BATT_TEMP, 0); max111x_set_input(max1111, MAX1111_ACIN_VOLT, SPITZ_CHARGEON_ACIN); diff --git a/hw/ssi.c b/hw/ssi.c index 8f2d9bc..af7e887 100644 --- a/hw/ssi.c +++ b/hw/ssi.c @@ -2,6 +2,8 @@ * QEMU Synchronous Serial Interface support * * Copyright (c) 2009 CodeSourcery. + * Copyright (c) 2012 Peter A.G. Crosthwaite (peter.crosthwaite@petalogix.com) + * Copyright (c) 2012 PetaLogix Pty Ltd. * Written by Paul Brook * * This code is licensed under the GNU GPL v2. @@ -14,24 +16,33 @@ struct SSIBus { BusState qbus; + int32_t selected_slave; }; static struct BusInfo ssi_bus_info = { .name = "SSI", .size = sizeof(SSIBus), + .props = (Property[]) { + DEFINE_PROP_INT32("slave_select", struct SSISlave, slave_select, 0), + DEFINE_PROP_END_OF_LIST(), + } +}; + +static const VMStateDescription vmstate_ssi_bus = { + .name = "ssi_bus", + .version_id = 1, + .minimum_version_id = 1, + .minimum_version_id_old = 1, + .fields = (VMStateField[]) { + VMSTATE_INT32(selected_slave, SSIBus), + VMSTATE_END_OF_LIST() + } }; static int ssi_slave_init(DeviceState *dev) { SSISlave *s = SSI_SLAVE(dev); SSISlaveClass *ssc = SSI_SLAVE_GET_CLASS(s); - SSIBus *bus; - - bus = FROM_QBUS(SSIBus, qdev_get_parent_bus(dev)); - if (QTAILQ_FIRST(&bus->qbus.children) != dev - || QTAILQ_NEXT(dev, sibling) != NULL) { - hw_error("Too many devices on SSI bus"); - } return ssc->init(s); } @@ -46,40 +57,106 @@ static void ssi_slave_class_init(ObjectClass *klass, void *data) static TypeInfo ssi_slave_info = { .name = TYPE_SSI_SLAVE, .parent = TYPE_DEVICE, + .instance_size = sizeof(struct SSISlave), .class_init = ssi_slave_class_init, .class_size = sizeof(SSISlaveClass), .abstract = true, }; -DeviceState *ssi_create_slave(SSIBus *bus, const char *name) +DeviceState *ssi_create_slave_no_init(SSIBus *bus, const char *name, + int32_t slave_select) { DeviceState *dev; dev = qdev_create(&bus->qbus, name); + qdev_prop_set_int32(dev, "slave_select", slave_select); + return dev; +} + +DeviceState *ssi_create_slave(SSIBus *bus, const char *name, + int32_t slave_select) +{ + DeviceState *dev; + dev = ssi_create_slave_no_init(bus, name, slave_select); qdev_init_nofail(dev); return dev; + } SSIBus *ssi_create_bus(DeviceState *parent, const char *name) { - BusState *bus; - bus = qbus_create(&ssi_bus_info, parent, name); - return FROM_QBUS(SSIBus, bus); + SSIBus *bus; + + bus = FROM_QBUS(SSIBus, qbus_create(&ssi_bus_info, parent, name)); + vmstate_register(NULL, -1, &vmstate_ssi_bus, bus); + return bus; +} + +static SSISlave *get_current_slave(SSIBus *bus) +{ + DeviceState *qdev; + + QTAILQ_FOREACH(qdev, &bus->qbus.children, sibling) { + SSISlave *candidate = SSI_SLAVE_FROM_QDEV(qdev); + if (candidate->slave_select == bus->selected_slave) { + return candidate; + } + } + + return NULL; +} + +void ssi_select_slave(SSIBus *bus, int32_t selected_slave) +{ + SSISlave *slave; + SSISlaveClass *ssc; + + if (bus->selected_slave == selected_slave) { + return; + } + + slave = get_current_slave(bus); + if (slave) { + ssc = SSI_SLAVE_GET_CLASS(slave); + if (ssc->set_cs) { + ssc->set_cs(slave, 0); + } + } + bus->selected_slave = selected_slave; + + slave = get_current_slave(bus); + if (slave) { + ssc = SSI_SLAVE_GET_CLASS(slave); + if (ssc->set_cs) { + ssc->set_cs(slave, 1); + } + } + } uint32_t ssi_transfer(SSIBus *bus, uint32_t val) { - DeviceState *dev; SSISlave *slave; SSISlaveClass *ssc; - dev = QTAILQ_FIRST(&bus->qbus.children); - if (!dev) { + + slave = get_current_slave(bus); + if (!slave) { return 0; } - slave = SSI_SLAVE(dev); ssc = SSI_SLAVE_GET_CLASS(slave); return ssc->transfer(slave, val); } +const VMStateDescription vmstate_ssi_slave = { + .name = "SSISlave", + .version_id = 1, + .minimum_version_id = 1, + .minimum_version_id_old = 1, + .fields = (VMStateField[]) { + VMSTATE_INT32(slave_select, SSISlave), + VMSTATE_END_OF_LIST() + } +}; + static void ssi_slave_register_types(void) { type_register_static(&ssi_slave_info); diff --git a/hw/ssi.h b/hw/ssi.h index 06657d7..7f0ea36 100644 --- a/hw/ssi.h +++ b/hw/ssi.h @@ -1,10 +1,10 @@ /* QEMU Synchronous Serial Interface support. */ -/* In principle SSI is a point-point interface. As such the qemu - implementation has a single slave device on a "bus". +/* In principle SSI is a point-point interface. However it is fairly common for boards to have multiple slaves connected to a single master, and select devices with an external - chip select. This is implemented in qemu by having an explicit mux device. + chip select. SSI busses can therefore have any number of slaves, + of which a master can select using ssi_select_slave(). It is assumed that master and slave are both using the same transfer width. */ @@ -29,22 +29,42 @@ typedef struct SSISlaveClass { int (*init)(SSISlave *dev); uint32_t (*transfer)(SSISlave *dev, uint32_t val); + int (*set_cs)(SSISlave *dev, int state); } SSISlaveClass; struct SSISlave { DeviceState qdev; + + int32_t slave_select; }; #define SSI_SLAVE_FROM_QDEV(dev) DO_UPCAST(SSISlave, qdev, dev) #define FROM_SSI_SLAVE(type, dev) DO_UPCAST(type, ssidev, dev) -DeviceState *ssi_create_slave(SSIBus *bus, const char *name); +DeviceState *ssi_create_slave(SSIBus *bus, const char *name, + int32_t slave_select); +DeviceState *ssi_create_slave_no_init(SSIBus *bus, const char *name, + int32_t slave_select); /* Master interface. */ SSIBus *ssi_create_bus(DeviceState *parent, const char *name); +#define SSI_SLAVE_SELECT_NONE (-1) + +void ssi_select_slave(SSIBus *bus, int32_t selected_slave); + uint32_t ssi_transfer(SSIBus *bus, uint32_t val); +extern const VMStateDescription vmstate_ssi_slave; + +#define VMSTATE_SSI_SLAVE(_field, _state) { \ + .name = (stringify(_field)), \ + .size = sizeof(SSISlave), \ + .vmsd = &vmstate_ssi_slave, \ + .flags = VMS_STRUCT, \ + .offset = vmstate_offset_value(_state, _field, SSISlave), \ +} + /* max111x.c */ void max111x_set_input(DeviceState *dev, int line, uint8_t value); diff --git a/hw/stellaris.c b/hw/stellaris.c index 562fbbf..e0600a1 100644 --- a/hw/stellaris.c +++ b/hw/stellaris.c @@ -1309,14 +1309,14 @@ static void stellaris_init(const char *kernel_filename, const char *cpu_model, void *bus; bus = qdev_get_child_bus(dev, "ssi"); - mux = ssi_create_slave(bus, "evb6965-ssi"); + mux = ssi_create_slave(bus, "evb6965-ssi", 0); gpio_out[GPIO_D][0] = qdev_get_gpio_in(mux, 0); bus = qdev_get_child_bus(mux, "ssi0"); - ssi_create_slave(bus, "ssi-sd"); + ssi_create_slave(bus, "ssi-sd", 0); bus = qdev_get_child_bus(mux, "ssi1"); - dev = ssi_create_slave(bus, "ssd0323"); + dev = ssi_create_slave(bus, "ssd0323", 0); gpio_out[GPIO_C][7] = qdev_get_gpio_in(dev, 0); /* Make sure the select pin is high. */ diff --git a/hw/tosa.c b/hw/tosa.c index 6baa17d..3986810 100644 --- a/hw/tosa.c +++ b/hw/tosa.c @@ -196,7 +196,7 @@ static void tosa_tg_init(PXA2xxState *cpu) { i2c_bus *bus = pxa2xx_i2c_bus(cpu->i2c[0]); i2c_create_slave(bus, "tosa_dac", DAC_BASE); - ssi_create_slave(cpu->ssp[1], "tosa-ssp"); + ssi_create_slave(cpu->ssp[1], "tosa-ssp", 0); } diff --git a/hw/z2.c b/hw/z2.c index 654ac55..2a0ecf5 100644 --- a/hw/z2.c +++ b/hw/z2.c @@ -346,7 +346,7 @@ static void z2_init(ram_addr_t ram_size, type_register_static(&zipit_lcd_info); type_register_static(&aer915_info); - z2_lcd = ssi_create_slave(cpu->ssp[1], "zipit-lcd"); + z2_lcd = ssi_create_slave(cpu->ssp[1], "zipit-lcd", 0); bus = pxa2xx_i2c_bus(cpu->i2c[0]); i2c_create_slave(bus, "aer915", 0x55); wm = i2c_create_slave(bus, "wm8750", 0x1b);