From patchwork Mon Jul 4 12:22:53 2016 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: Peter Maydell X-Patchwork-Id: 9212573 Return-Path: Received: from mail.wl.linuxfoundation.org (pdx-wl-mail.web.codeaurora.org [172.30.200.125]) by pdx-korg-patchwork.web.codeaurora.org (Postfix) with ESMTP id 59FCD60752 for ; Mon, 4 Jul 2016 12:55:52 +0000 (UTC) Received: from mail.wl.linuxfoundation.org (localhost [127.0.0.1]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id 4A1FE286A7 for ; Mon, 4 Jul 2016 12:55:52 +0000 (UTC) Received: by mail.wl.linuxfoundation.org (Postfix, from userid 486) id 3EAFD286B5; Mon, 4 Jul 2016 12:55:52 +0000 (UTC) X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on pdx-wl-mail.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-6.9 required=2.0 tests=BAYES_00,RCVD_IN_DNSWL_HI autolearn=ham version=3.3.1 Received: from lists.gnu.org (lists.gnu.org [208.118.235.17]) (using TLSv1 with cipher AES256-SHA (256/256 bits)) (No client certificate requested) by mail.wl.linuxfoundation.org (Postfix) with ESMTPS id 520FB286A7 for ; Mon, 4 Jul 2016 12:55:51 +0000 (UTC) Received: from localhost ([::1]:47364 helo=lists.gnu.org) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1bK3Pi-000135-DP for patchwork-qemu-devel@patchwork.kernel.org; Mon, 04 Jul 2016 08:55:50 -0400 Received: from eggs.gnu.org ([2001:4830:134:3::10]:43515) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1bK2u5-0003Ab-Qw for qemu-devel@nongnu.org; Mon, 04 Jul 2016 08:23:11 -0400 Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1bK2u2-0004SM-EM for qemu-devel@nongnu.org; Mon, 04 Jul 2016 08:23:08 -0400 Received: from orth.archaic.org.uk ([2001:8b0:1d0::2]:58076) by eggs.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1bK2u2-0004PX-4C for qemu-devel@nongnu.org; Mon, 04 Jul 2016 08:23:06 -0400 Received: from pm215 by orth.archaic.org.uk with local (Exim 4.84_2) (envelope-from ) id 1bK2u1-0001C9-It for qemu-devel@nongnu.org; Mon, 04 Jul 2016 13:23:05 +0100 From: Peter Maydell To: qemu-devel@nongnu.org Date: Mon, 4 Jul 2016 13:22:53 +0100 Message-Id: <1467634974-32638-23-git-send-email-peter.maydell@linaro.org> X-Mailer: git-send-email 1.9.1 In-Reply-To: <1467634974-32638-1-git-send-email-peter.maydell@linaro.org> References: <1467634974-32638-1-git-send-email-peter.maydell@linaro.org> MIME-Version: 1.0 X-detected-operating-system: by eggs.gnu.org: GNU/Linux 2.2.x-3.x [generic] X-Received-From: 2001:8b0:1d0::2 Subject: [Qemu-devel] [PULL 22/23] ast2400: add SPI flash slaves X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.21 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: qemu-devel-bounces+patchwork-qemu-devel=patchwork.kernel.org@nongnu.org Sender: "Qemu-devel" X-Virus-Scanned: ClamAV using ClamSMTP From: Cédric Le Goater Each controller on the ast2400 has a memory range on which it maps its flash module slaves. Each slave is assigned a memory segment for its mapping that can be changed at bootime with the Segment Address Register. This is not supported in the current implementation so we are using the defaults provided by the specs. Each SPI flash slave can then be accessed in two modes: Command and User. When in User mode, accesses to the memory segment of the slaves are translated in SPI transfers. When in Command mode, the HW generates the SPI commands automatically and the memory segment is accessed as if doing a MMIO. Other SPI controllers call that mode linear addressing mode. For this purpose, we are adding below each crontoller an array of structs gathering for each SPI flash module, a segment rank, a MemoryRegion to handle the memory accesses and the associated SPI slave device, which should be a m25p80. Only the User mode is supported for now but we are preparing ground for the Command mode. The framework is sufficient to support Linux. Signed-off-by: Cédric Le Goater Message-id: 1467138270-32481-8-git-send-email-clg@kaod.org [PMM: Use g_new0() rather than g_malloc0()] Reviewed-by: Peter Maydell Signed-off-by: Peter Maydell --- hw/arm/ast2400.c | 5 ++ hw/ssi/aspeed_smc.c | 150 +++++++++++++++++++++++++++++++++++++++++++- include/hw/ssi/aspeed_smc.h | 21 +++++++ 3 files changed, 173 insertions(+), 3 deletions(-) diff --git a/hw/arm/ast2400.c b/hw/arm/ast2400.c index ed8fcb7..0555843 100644 --- a/hw/arm/ast2400.c +++ b/hw/arm/ast2400.c @@ -31,6 +31,9 @@ #define AST2400_TIMER_BASE 0x1E782000 #define AST2400_I2C_BASE 0x1E78A000 +#define AST2400_FMC_FLASH_BASE 0x20000000 +#define AST2400_SPI_FLASH_BASE 0x30000000 + #define AST2400_A0_SILICON_REV 0x02000303 static const int uart_irqs[] = { 9, 32, 33, 34, 10 }; @@ -168,6 +171,7 @@ static void ast2400_realize(DeviceState *dev, Error **errp) return; } sysbus_mmio_map(SYS_BUS_DEVICE(&s->smc), 0, AST2400_FMC_BASE); + sysbus_mmio_map(SYS_BUS_DEVICE(&s->smc), 1, AST2400_FMC_FLASH_BASE); sysbus_connect_irq(SYS_BUS_DEVICE(&s->smc), 0, qdev_get_gpio_in(DEVICE(&s->vic), 19)); @@ -180,6 +184,7 @@ static void ast2400_realize(DeviceState *dev, Error **errp) return; } sysbus_mmio_map(SYS_BUS_DEVICE(&s->spi), 0, AST2400_SPI_BASE); + sysbus_mmio_map(SYS_BUS_DEVICE(&s->spi), 1, AST2400_SPI_FLASH_BASE); } static void ast2400_class_init(ObjectClass *oc, void *data) diff --git a/hw/ssi/aspeed_smc.c b/hw/ssi/aspeed_smc.c index 537635e..a371e30 100644 --- a/hw/ssi/aspeed_smc.c +++ b/hw/ssi/aspeed_smc.c @@ -127,13 +127,129 @@ #define R_SPI_MISC_CTRL (0x10 / 4) #define R_SPI_TIMINGS (0x14 / 4) +/* + * Default segments mapping addresses and size for each slave per + * controller. These can be changed when board is initialized with the + * Segment Address Registers but they don't seem do be used on the + * field. + */ +static const AspeedSegments aspeed_segments_legacy[] = { + { 0x10000000, 32 * 1024 * 1024 }, +}; + +static const AspeedSegments aspeed_segments_fmc[] = { + { 0x20000000, 64 * 1024 * 1024 }, + { 0x24000000, 32 * 1024 * 1024 }, + { 0x26000000, 32 * 1024 * 1024 }, + { 0x28000000, 32 * 1024 * 1024 }, + { 0x2A000000, 32 * 1024 * 1024 } +}; + +static const AspeedSegments aspeed_segments_spi[] = { + { 0x30000000, 64 * 1024 * 1024 }, +}; + static const AspeedSMCController controllers[] = { { "aspeed.smc.smc", R_CONF, R_CE_CTRL, R_CTRL0, R_TIMINGS, - CONF_ENABLE_W0, 5 }, + CONF_ENABLE_W0, 5, aspeed_segments_legacy, 0x6000000 }, { "aspeed.smc.fmc", R_CONF, R_CE_CTRL, R_CTRL0, R_TIMINGS, - CONF_ENABLE_W0, 5 }, + CONF_ENABLE_W0, 5, aspeed_segments_fmc, 0x10000000 }, { "aspeed.smc.spi", R_SPI_CONF, 0xff, R_SPI_CTRL0, R_SPI_TIMINGS, - SPI_CONF_ENABLE_W0, 1 }, + SPI_CONF_ENABLE_W0, 1, aspeed_segments_spi, 0x10000000 }, +}; + +static uint64_t aspeed_smc_flash_default_read(void *opaque, hwaddr addr, + unsigned size) +{ + qemu_log_mask(LOG_GUEST_ERROR, "%s: To 0x%" HWADDR_PRIx " of size %u" + PRIx64 "\n", __func__, addr, size); + return 0; +} + +static void aspeed_smc_flash_default_write(void *opaque, hwaddr addr, + uint64_t data, unsigned size) +{ + qemu_log_mask(LOG_GUEST_ERROR, "%s: To 0x%" HWADDR_PRIx " of size %u: 0x%" + PRIx64 "\n", __func__, addr, size, data); +} + +static const MemoryRegionOps aspeed_smc_flash_default_ops = { + .read = aspeed_smc_flash_default_read, + .write = aspeed_smc_flash_default_write, + .endianness = DEVICE_LITTLE_ENDIAN, + .valid = { + .min_access_size = 1, + .max_access_size = 4, + }, +}; + +static inline int aspeed_smc_flash_mode(const AspeedSMCState *s, int cs) +{ + return s->regs[s->r_ctrl0 + cs] & CTRL_CMD_MODE_MASK; +} + +static inline bool aspeed_smc_is_usermode(const AspeedSMCState *s, int cs) +{ + return aspeed_smc_flash_mode(s, cs) == CTRL_USERMODE; +} + +static inline bool aspeed_smc_is_writable(const AspeedSMCState *s, int cs) +{ + return s->regs[s->r_conf] & (1 << (s->conf_enable_w0 + cs)); +} + +static uint64_t aspeed_smc_flash_read(void *opaque, hwaddr addr, unsigned size) +{ + AspeedSMCFlash *fl = opaque; + const AspeedSMCState *s = fl->controller; + uint64_t ret = 0; + int i; + + if (aspeed_smc_is_usermode(s, fl->id)) { + for (i = 0; i < size; i++) { + ret |= ssi_transfer(s->spi, 0x0) << (8 * i); + } + } else { + qemu_log_mask(LOG_UNIMP, "%s: usermode not implemented\n", + __func__); + ret = -1; + } + + return ret; +} + +static void aspeed_smc_flash_write(void *opaque, hwaddr addr, uint64_t data, + unsigned size) +{ + AspeedSMCFlash *fl = opaque; + const AspeedSMCState *s = fl->controller; + int i; + + if (!aspeed_smc_is_writable(s, fl->id)) { + qemu_log_mask(LOG_GUEST_ERROR, "%s: flash is not writable at 0x%" + HWADDR_PRIx "\n", __func__, addr); + return; + } + + if (!aspeed_smc_is_usermode(s, fl->id)) { + qemu_log_mask(LOG_UNIMP, "%s: usermode not implemented\n", + __func__); + return; + } + + for (i = 0; i < size; i++) { + ssi_transfer(s->spi, (data >> (8 * i)) & 0xff); + } +} + +static const MemoryRegionOps aspeed_smc_flash_ops = { + .read = aspeed_smc_flash_read, + .write = aspeed_smc_flash_write, + .endianness = DEVICE_LITTLE_ENDIAN, + .valid = { + .min_access_size = 1, + .max_access_size = 4, + }, }; static bool aspeed_smc_is_ce_stop_active(const AspeedSMCState *s, int cs) @@ -237,6 +353,8 @@ static void aspeed_smc_realize(DeviceState *dev, Error **errp) AspeedSMCState *s = ASPEED_SMC(dev); AspeedSMCClass *mc = ASPEED_SMC_GET_CLASS(s); int i; + char name[32]; + hwaddr offset = 0; s->ctrl = mc->ctrl; @@ -270,6 +388,32 @@ static void aspeed_smc_realize(DeviceState *dev, Error **errp) memory_region_init_io(&s->mmio, OBJECT(s), &aspeed_smc_ops, s, s->ctrl->name, ASPEED_SMC_R_MAX * 4); sysbus_init_mmio(sbd, &s->mmio); + + /* + * Memory region where flash modules are remapped + */ + snprintf(name, sizeof(name), "%s.flash", s->ctrl->name); + + memory_region_init_io(&s->mmio_flash, OBJECT(s), + &aspeed_smc_flash_default_ops, s, name, + s->ctrl->mapping_window_size); + sysbus_init_mmio(sbd, &s->mmio_flash); + + s->flashes = g_new0(AspeedSMCFlash, s->num_cs); + + for (i = 0; i < s->num_cs; ++i) { + AspeedSMCFlash *fl = &s->flashes[i]; + + snprintf(name, sizeof(name), "%s.%d", s->ctrl->name, i); + + fl->id = i; + fl->controller = s; + fl->size = s->ctrl->segments[i].size; + memory_region_init_io(&fl->mmio, OBJECT(s), &aspeed_smc_flash_ops, + fl, name, fl->size); + memory_region_add_subregion(&s->mmio_flash, offset, &fl->mmio); + offset += fl->size; + } } static const VMStateDescription vmstate_aspeed_smc = { diff --git a/include/hw/ssi/aspeed_smc.h b/include/hw/ssi/aspeed_smc.h index c4a4960..def3b45 100644 --- a/include/hw/ssi/aspeed_smc.h +++ b/include/hw/ssi/aspeed_smc.h @@ -27,6 +27,12 @@ #include "hw/ssi/ssi.h" +typedef struct AspeedSegments { + hwaddr addr; + uint32_t size; +} AspeedSegments; + +struct AspeedSMCState; typedef struct AspeedSMCController { const char *name; uint8_t r_conf; @@ -35,8 +41,20 @@ typedef struct AspeedSMCController { uint8_t r_timings; uint8_t conf_enable_w0; uint8_t max_slaves; + const AspeedSegments *segments; + uint32_t mapping_window_size; } AspeedSMCController; +typedef struct AspeedSMCFlash { + const struct AspeedSMCState *controller; + + uint8_t id; + uint32_t size; + + MemoryRegion mmio; + DeviceState *flash; +} AspeedSMCFlash; + #define TYPE_ASPEED_SMC "aspeed.smc" #define ASPEED_SMC(obj) OBJECT_CHECK(AspeedSMCState, (obj), TYPE_ASPEED_SMC) #define ASPEED_SMC_CLASS(klass) \ @@ -57,6 +75,7 @@ typedef struct AspeedSMCState { const AspeedSMCController *ctrl; MemoryRegion mmio; + MemoryRegion mmio_flash; qemu_irq irq; int irqline; @@ -74,6 +93,8 @@ typedef struct AspeedSMCState { uint8_t r_ctrl0; uint8_t r_timings; uint8_t conf_enable_w0; + + AspeedSMCFlash *flashes; } AspeedSMCState; #endif /* ASPEED_SMC_H */