From patchwork Fri Mar 30 06:37:10 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: 149549 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 A4419B6F9F for ; Fri, 30 Mar 2012 17:38:07 +1100 (EST) Received: from localhost ([::1]:55934 helo=lists.gnu.org) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1SDVTJ-0000kS-Cz for incoming@patchwork.ozlabs.org; Fri, 30 Mar 2012 02:38:05 -0400 Received: from eggs.gnu.org ([208.118.235.92]:34539) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1SDVSn-0008Rb-MW for qemu-devel@nongnu.org; Fri, 30 Mar 2012 02:37:35 -0400 Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1SDVSl-00042q-5h for qemu-devel@nongnu.org; Fri, 30 Mar 2012 02:37:33 -0400 Received: from mail-iy0-f173.google.com ([209.85.210.173]:58765) by eggs.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1SDVSk-0003yv-Vl for qemu-devel@nongnu.org; Fri, 30 Mar 2012 02:37:31 -0400 Received: by mail-iy0-f173.google.com with SMTP id j26so695412iaf.4 for ; Thu, 29 Mar 2012 23:37:30 -0700 (PDT) X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20120113; h=mime-version:from:to:cc:subject:date:message-id:x-mailer :in-reply-to:references:in-reply-to:references:x-gm-message-state; bh=UMnrEFYX4GhP09q3PdazBd2MWQviCOJh2wbyTqLl5iU=; b=DIrDSvDcH24G8ToB31Hr048fsLVe4Bdvv9AGUJl3+nKRzqnmtCoMIKzEESXhloeRHq BGGtk6VoKIjc13kXvifFH0dxMwymmMpuYkfDY6+FN2tiWAnYR+RbmJ4HX0KWXbFtwvIN hOyxyWx0+wdCQf63gVg2w/LA3pwVWodDJWvHD8a7E9N5IMrWbW0YPsfsO7Rsz7d4aAsD bJhnRxn47oASxZOsDioasCChJewNO0q+5KOyZSQasR7MEbWf+4W+JH8Qts5UzQnmOxwv JZfwotZfCUKmeUtFkmOQQgvkupJkYgB0WYJG/7ZIeceQoYJFPSs8e30zvkpn3Y8zauni a4kw== MIME-Version: 1.0 Received: by 10.43.52.74 with SMTP id vl10mr499864icb.55.1333089449988; Thu, 29 Mar 2012 23:37:29 -0700 (PDT) Received: from localhost ([124.148.20.9]) by mx.google.com with ESMTPS id ke7sm1380701igc.10.2012.03.29.23.37.24 (version=TLSv1/SSLv3 cipher=OTHER); Thu, 29 Mar 2012 23:37:28 -0700 (PDT) From: "Peter A. G. Crosthwaite" To: qemu-devel@nongnu.org, paul@codesourcery.com, edgar.iglesias@gmail.com Date: Fri, 30 Mar 2012 16:37:10 +1000 Message-Id: <9f6137506576a94fb95a58143e33db964d9c6a53.1333088411.git.peter.crosthwaite@petalogix.com> X-Mailer: git-send-email 1.7.3.2 In-Reply-To: References: In-Reply-To: References: X-Gm-Message-State: ALoCoQlhxM/Y4t0ZAoe8zgtjEPML9zVbn1uug4VPXXQQ4VHl3Jqd/Avu3DC/+mdEYjySRmcfIqEq X-detected-operating-system: by eggs.gnu.org: Genre and OS details not recognized. X-Received-From: 209.85.210.173 Cc: peter.crosthwaite@petalogix.com, john.williams@petalogix.com Subject: [Qemu-devel] [RFC PATCH v1 1/4] SPI: initial 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 defined spi bus and spi slave QOM interfaces. Inspired by and loosley based on existing I2C framework. Signed-off-by: Peter A. G. Crosthwaite --- Makefile.target | 1 + hw/spi.c | 175 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ hw/spi.h | 86 +++++++++++++++++++++++++++ 3 files changed, 262 insertions(+), 0 deletions(-) create mode 100644 hw/spi.c create mode 100644 hw/spi.h diff --git a/Makefile.target b/Makefile.target index 44b2e83..8fd3718 100644 --- a/Makefile.target +++ b/Makefile.target @@ -320,6 +320,7 @@ obj-mips-$(CONFIG_FULONG) += bonito.o vt82c686.o mips_fulong2e.o obj-microblaze-y = petalogix_s3adsp1800_mmu.o obj-microblaze-y += petalogix_ml605_mmu.o obj-microblaze-y += microblaze_boot.o +obj-microblaze-y += spi.o obj-microblaze-y += microblaze_pic_cpu.o obj-microblaze-y += xilinx_intc.o diff --git a/hw/spi.c b/hw/spi.c new file mode 100644 index 0000000..3afef07 --- /dev/null +++ b/hw/spi.c @@ -0,0 +1,175 @@ +/* + * QEMU SPI bus interface. + * + * Copyright (C) 2012 Peter A. G. Crosthwaite + * Copyright (C) 2012 PetaLogix + * + * 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. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, see . + */ + +#include "spi.h" + +static struct BusInfo spi_bus_info = { + .name = "SPI", + .size = sizeof(spi_bus) +}; + +static const VMStateDescription vmstate_spi_bus = { + .name = "spi_bus", + .version_id = 1, + .minimum_version_id = 1, + .minimum_version_id_old = 1, + .fields = (VMStateField[]) { + VMSTATE_UINT8(cur_slave, spi_bus), + VMSTATE_END_OF_LIST() + } +}; + +spi_bus *spi_init_bus(DeviceState *parent, int num_slaves, const char *name) +{ + spi_bus *bus; + + bus = FROM_QBUS(spi_bus, qbus_create(&spi_bus_info, parent, name)); + if (num_slaves >= SPI_BUS_NO_CS) { + hw_error("too many slaves on spi bus: %d\n", num_slaves); + } + bus->num_slaves = num_slaves; + bus->slaves = g_malloc0(sizeof(*bus->slaves) * num_slaves); + vmstate_register(NULL, -1, &vmstate_spi_bus, bus); + return bus; +} + +int spi_attach_slave(spi_bus *bus, SPISlave *slave, int cs) +{ + if (bus->slaves[cs]) { + return 1; + } + bus->slaves[cs] = slave; + return 0; +} + +int spi_set_cs(spi_bus *bus, int cs) +{ + SPISlave *dev; + SPISlaveClass *klass; + + if (bus->cur_slave == cs) { + return 0; + } + + if (bus->cur_slave != SPI_BUS_NO_CS) { + dev = bus->slaves[bus->cur_slave]; + dev->cs = 0; + klass = SPI_SLAVE_GET_CLASS(dev); + klass->cs(dev, 0); + } + + if (cs >= bus->num_slaves && cs != SPI_BUS_NO_CS) { + hw_error("attempted to assert non existent spi CS line: %d\n", cs); + } + + bus->cur_slave = (uint8_t)cs; + + if (cs != SPI_BUS_NO_CS) { + dev = bus->slaves[cs]; + dev->cs = 1; + klass = SPI_SLAVE_GET_CLASS(dev); + klass->cs(dev, 1); + } + return 0; +}; + +int spi_get_cs(spi_bus *bus) +{ + return bus->cur_slave; +} + +SpiSlaveState spi_get_state(spi_bus *bus) +{ + SPISlave *dev; + SPISlaveClass *klass; + + if (bus->cur_slave == SPI_BUS_NO_CS) { + return SPI_NO_CS; + } + dev = bus->slaves[bus->cur_slave]; + klass = SPI_SLAVE_GET_CLASS(dev); + + return klass->get_state(dev); +} + +SpiSlaveState spi_send(spi_bus *bus, uint32_t data, int len) +{ + SPISlave *dev; + SPISlaveClass *klass; + + if (bus->cur_slave == SPI_BUS_NO_CS) { + return SPI_NO_CS; + } + dev = bus->slaves[bus->cur_slave]; + klass = SPI_SLAVE_GET_CLASS(dev); + + return klass->send(dev, data, len); +} + +SpiSlaveState spi_recv(spi_bus *bus, uint32_t *data) +{ + SPISlave *dev; + SPISlaveClass *klass; + + if (bus->cur_slave == SPI_BUS_NO_CS) { + return SPI_NO_CS; + } + dev = bus->slaves[bus->cur_slave]; + klass = SPI_SLAVE_GET_CLASS(dev); + + return klass->recv(dev, data); +} + +const VMStateDescription vmstate_spi_slave = { + .name = "SPISlave", + .version_id = 1, + .minimum_version_id = 1, + .minimum_version_id_old = 1, + .fields = (VMStateField[]) { + VMSTATE_UINT8(cs, SPISlave), + VMSTATE_END_OF_LIST() + } +}; + +static int spi_slave_qdev_init(DeviceState *dev) +{ + SPISlave *s = SPI_SLAVE_FROM_QDEV(dev); + SPISlaveClass *sc = SPI_SLAVE_GET_CLASS(s); + + return sc->init(s); +} + +static void spi_slave_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *k = DEVICE_CLASS(klass); + k->init = spi_slave_qdev_init; + k->bus_info = &spi_bus_info; +} + +static TypeInfo spi_slave_type_info = { + .name = TYPE_SPI_SLAVE, + .parent = TYPE_DEVICE, + .instance_size = sizeof(SPISlave), + .abstract = true, + .class_size = sizeof(SPISlaveClass), + .class_init = spi_slave_class_init, +}; + +static void spi_slave_register_types(void) +{ + type_register_static(&spi_slave_type_info); +} + +type_init(spi_slave_register_types) diff --git a/hw/spi.h b/hw/spi.h new file mode 100644 index 0000000..668e9b0 --- /dev/null +++ b/hw/spi.h @@ -0,0 +1,86 @@ +#ifndef QEMU_SPI_H +#define QEMU_SPI_H + +#include "qdev.h" + +/* pass to spi_set_cs to deslect all devices on bus */ + +#define SPI_BUS_NO_CS 0xFF + +/* state of a SPI device, + * SPI_NO_CS -> the CS pin in de-asserted -> device is tristated + * SPI_IDLE -> CS is asserted and device ready to recv + * SPI_DATA_PENDING -> CS is asserted and the device has pushed data to master + */ + +typedef enum { + SPI_NO_CS, + SPI_IDLE, + SPI_DATA_PENDING +} SpiSlaveState; + +typedef struct SPISlave { + DeviceState qdev; + uint8_t cs; +} SPISlave; + +#define TYPE_SPI_SLAVE "spi-slave" +#define SPI_SLAVE(obj) \ + OBJECT_CHECK(SPISlave, (obj), TYPE_SPI_SLAVE) +#define SPI_SLAVE_CLASS(klass) \ + OBJECT_CLASS_CHECK(SPISlaveClass, (klass), TYPE_SPI_SLAVE) +#define SPI_SLAVE_GET_CLASS(obj) \ + OBJECT_GET_CLASS(SPISlaveClass, (obj), TYPE_SPI_SLAVE) + +typedef struct SPISlaveClass { + DeviceClass parent_class; + + /* Callbacks provided by the device. */ + int (*init)(SPISlave *s); + + /* change the cs pin state */ + void (*cs)(SPISlave *s, uint8_t select); + + /* Master to slave. */ + SpiSlaveState (*send)(SPISlave *s, uint32_t data, int len); + + /* Slave to master. */ + SpiSlaveState (*recv)(SPISlave *s, uint32_t *data); + + /* poll the spi device state */ + SpiSlaveState (*get_state)(SPISlave *s); +} SPISlaveClass; + +#define SPI_SLAVE_FROM_QDEV(dev) DO_UPCAST(SPISlave, qdev, dev) +#define FROM_SPI_SLAVE(type, dev) DO_UPCAST(type, spi, dev) + +extern const VMStateDescription vmstate_spi_slave; + +#define VMSTATE_SPI_SLAVE(_field, _state) { \ + .name = (stringify(_field)), \ + .size = sizeof(SPISlave), \ + .vmsd = &vmstate_spi_slave, \ + .flags = VMS_STRUCT, \ + .offset = vmstate_offset_value(_state, _field, SPISlave), \ +} + +typedef struct spi_bus { + BusState qbus; + SPISlave **slaves; + uint8_t num_slaves; + uint8_t cur_slave; +} spi_bus; + +/* create a new spi bus */ +spi_bus *spi_init_bus(DeviceState *parent, int num_slaves, const char *name); +int spi_attach_slave(spi_bus *bus, SPISlave *s, int cs); + +/* change the chip select. Return 1 on failure. */ +int spi_set_cs(spi_bus *bus, int cs); +int spi_get_cs(spi_bus *bus); +SpiSlaveState spi_get_state(spi_bus *bus); + +SpiSlaveState spi_send(spi_bus *bus, uint32_t data, int len); +SpiSlaveState spi_recv(spi_bus *bus, uint32_t *data); + +#endif