Qemu-NUC980(十一):SPI Controller

概述

本文描述了添加NUC980 SPI控制器功能代码的步骤,在描述过程中,为了清晰的描述添加框架步骤,部分代码的细节被删除,详细的代码,请参考文末的工程链接。

添加步骤

1、修改hw/arm/Kconfig,如下所示:

+号部分为新增加内容

复制代码
diff --git a/hw/arm/Kconfig b/hw/arm/Kconfig
index 35c90c59..9fa2d4ba 100644
--- a/hw/arm/Kconfig
+++ b/hw/arm/Kconfig
@@ -124,6 +124,7 @@ config NUC980
     select NUC980_GPIO
     select NUC980_SD
     select NUC980_I2C
+    select NUC980_SPI

2、修改hw/arm/nuc980_soc.c,如下所示:

+号部分为新增加内容

复制代码
diff --git a/hw/arm/nuc980_soc.c b/hw/arm/nuc980_soc.c
index 1ab5b48d..85b8086a 100644
--- a/hw/arm/nuc980_soc.c
+++ b/hw/arm/nuc980_soc.c
@@ -94,6 +94,8 @@ static void nuc980_init(Object *obj)
     }
     /* sd */
     sysbus_init_child_obj(obj, "sd", &s->sd, sizeof(s->sd), TYPE_NUC980_SD);
+    /* spi0 */
+    sysbus_init_child_obj(obj, "spi0", &s->spi0, sizeof(s->spi0), TYPE_NUC980_SPI);
     /* i2c */
     for (i = 0; i < I2C_COUNT; i++) {
         snprintf(name, NAME_SIZE, "i2c%d", i + 1);
@@ -107,6 +109,8 @@ static void nuc980_realize(DeviceState *dev, Error **errp)
     NUC980State *s = NUC980(dev);
     Error *err = NULL;
     int i;
+    SSIBus *spi_bus;
+    DeviceState *spi_dev;
     /* By default A9,A15 and ARM1176 CPUs have EL3 enabled.  This board
      * does not currently support EL3 so the CPU EL3 property is disabled
      * before realization.
@@ -205,6 +209,19 @@ static void nuc980_realize(DeviceState *dev, Error **errp)
                            qdev_get_gpio_in(DEVICE(&s->aic),
                             i2c_table[i].irq));
     }
+    /* spi0 */
+    object_property_set_bool(OBJECT(&s->spi0), true, "realized", &err);
+    if (err) {
+        error_propagate(errp, err);
+        return;
+    }
+    sysbus_mmio_map(SYS_BUS_DEVICE(&s->spi0), 0, SPI0_BASE);
+    sysbus_connect_irq(SYS_BUS_DEVICE(&s->spi0), 0,
+                           qdev_get_gpio_in(DEVICE(&s->aic), SPI0_IRQ));
+    spi_dev = DEVICE(&s->spi0);
+    spi_bus = (SSIBus *)qdev_get_child_bus(spi_dev, "spi");
+    if (spi_bus)
+        ssi_create_slave(spi_bus, "w25q256fv");
 }

3、修改hw/ssi/Kconfig,如下所示:

+号部分为新增加内容

复制代码
--- a/hw/ssi/Kconfig
+++ b/hw/ssi/Kconfig
@@ -16,3 +16,7 @@ config XILINX_SPIPS
 config STM32F2XX_SPI
     bool
     select SSI
+
+config NUC980_SPI
+    bool
+    select SSI

4、修改hw/i2c/Makefile.objs,如下所示:

+号部分为新增加内容

复制代码
diff --git a/hw/ssi/Makefile.objs b/hw/ssi/Makefile.objs
index f5bcc65f..af879b51 100644
--- a/hw/ssi/Makefile.objs
+++ b/hw/ssi/Makefile.objs
@@ -8,3 +8,4 @@ common-obj-$(CONFIG_MSF2) += mss-spi.o
 
 obj-$(CONFIG_OMAP) += omap_spi.o
 obj-$(CONFIG_IMX) += imx_spi.o
+obj-$(CONFIG_NUC980_SPI) += nuc980_spi.o

5、添加hw/i2c/nuc980_i2c.c,如下所示:

+号部分为新增加内容

复制代码
new file mode 100644
index 00000000..b64f38aa
--- /dev/null
+++ b/hw/ssi/nuc980_spi.c
@@ -0,0 +1,259 @@
+/*
+ * NUC980 SOC System emulation.
+ *
+ * Copyright (c) 2023- yanl1229@163.com.
+ * Written by yanl1229
+ *
+ * This code is licensed under the GPL.
+ */
+#include "qemu/osdep.h"
+#include "qemu/log.h"
+#include "qemu/module.h"
+#include "hw/irq.h"
+#include "hw/ssi/nuc980_spi.h"
+#include "migration/vmstate.h"
+
+#ifndef QSPI_ERR_DEBUG
+#define QSPI_ERR_DEBUG 0
+#endif
+
+#define DB_PRINT_L(lvl, fmt, args...) do { \
+    if (QSPI_ERR_DEBUG >= lvl) { \
+        qemu_log("%s: " fmt, __func__, ## args); \
+    } \
+} while (0)
+
+#define DB_PRINT(fmt, args...) DB_PRINT_L(1, fmt, ## args)
+
+static void nuc980_spi_fifo_clear(NUC980SPIState *s)
+{
+    s->tx_fifo.fifo_pos = 0;
+    s->tx_fifo.fifo_len = 0;
+    s->rx_fifo.fifo_pos = 0;
+    s->rx_fifo.fifo_len = 0;
+}
+
+static void nuc980_spi_fifo_update(NUC980SPIState *s, int dir)
+{
+    spi_fifo *fifo;
+    
+}
+
+/* just support 8bit thransfer */
+static void nuc980_spi_fifo_push(NUC980SPIState *s, int dir, uint32_t value)
+{
+    nuc980_spi_fifo_update(s, dir);
+}
+
+static uint32_t nuc980_spi_fifo_pop(NUC980SPIState *s, int dir)
+{

+    return value;
+}
+
+static void nuc980_spi_reset(DeviceState *dev)
+{
+    NUC980SPIState *s = NUC980_SPI(dev);
+}
+
+static uint64_t nuc980_spi_read(void *opaque, hwaddr addr,
+                                     unsigned int size)
+{
+    NUC980SPIState *s = opaque;
+
+    return 0;
+}
+
+static void nuc980_spi_write(void *opaque, hwaddr addr,
+                                uint64_t val64, unsigned int size)
+{
+    NUC980SPIState *s = opaque;
+    uint32_t value = val64;
+    uint8_t rx;
+}
+
+static const MemoryRegionOps nuc980_spi_ops = {
+    .read = nuc980_spi_read,
+    .write = nuc980_spi_write,
+    .endianness = DEVICE_NATIVE_ENDIAN,
+};
+
+static const VMStateDescription vmstate_nuc980_spi = {
+    .name = TYPE_NUC980_SPI,
+    }
+};
+
+static void nuc980_spi_init(Object *obj)
+{
+    NUC980SPIState *s = NUC980_SPI(obj);
+    DeviceState *dev = DEVICE(obj);
+
+    memory_region_init_io(&s->mmio, obj, &nuc980_spi_ops, s,
+                          TYPE_NUC980_SPI, 0x1000);
+    sysbus_init_mmio(SYS_BUS_DEVICE(obj), &s->mmio);
+
+    sysbus_init_irq(SYS_BUS_DEVICE(obj), &s->irq);
+
+    s->ssi = ssi_create_bus(dev, "spi");
+}
+
+static void nuc980_spi_class_init(ObjectClass *klass, void *data)
+{
+    DeviceClass *dc = DEVICE_CLASS(klass);
+
+    dc->reset = nuc980_spi_reset;
+    dc->vmsd = &vmstate_nuc980_spi;
+}
+
+static const TypeInfo nuc980_spi_info = {
+    .name          = TYPE_NUC980_SPI,
+    .parent        = TYPE_SYS_BUS_DEVICE,
+    .instance_size = sizeof(NUC980SPIState),
+    .instance_init = nuc980_spi_init,
+    .class_init    = nuc980_spi_class_init,
+};
+
+static void nuc980_spi_register_types(void)
+{
+    type_register_static(&nuc980_spi_info);
+}
+
+type_init(nuc980_spi_register_types)
\ No newline at end of file

6、修改include/hw/arm/nuc980.h,如下所示:

+号部分为新增加内容

复制代码
diff --git a/include/hw/arm/nuc980.h b/include/hw/arm/nuc980.h
index 8fd8663a..038af1fd 100644
--- a/include/hw/arm/nuc980.h
+++ b/include/hw/arm/nuc980.h
@@ -22,6 +22,7 @@
 #include "hw/gpio/nuc980_gpio.h"
 #include "hw/sd/nuc980_sd.h"
 #include "hw/i2c/nuc980_i2c.h"
+#include "hw/ssi/nuc980_spi.h"
 
 #define SDRAM_BASE      0x0000000
 #define SDRAM_SIZE      (64 *1024 * 1024)
@@ -62,6 +63,9 @@
 #define I2C2_BASE     0xb0082000
 #define I2C3_BASE     0xb0083000
 
+#define SPI0_BASE       0xb0061000
+#define SPI0_IRQ        52
+
 #define AIC_BASE                0xb0042000
 
 #define SDH_BASE    0xb0018000
@@ -88,6 +92,7 @@ typedef struct NUC980State {
     NUC980GPIOState gpio[GPIO_COUNT];
     NUC980SDState sd;
     NUC980I2CState i2c[I2C_COUNT];
+    NUC980SPIState spi0;
 
 } NUC980State;

7、添加include/hw/ssi/nuc980_spi.h,如下所示:

+号部分为新增加内容

复制代码
new file mode 100644
index 00000000..8a839352
--- /dev/null
+++ b/include/hw/ssi/nuc980_spi.h
@@ -0,0 +1,73 @@
+/*
+ * NUC980 SOC System emulation.
+ *
+ * Copyright (c) 2023- yanl1229@163.com.
+ * Written by yanl1229
+ *
+ * This code is licensed under the GPL.
+ */
+#ifndef HW_NUC980_SPI_H
+#define HW_NUC980_SPI_H
+
+#include "hw/sysbus.h"
+#include "hw/hw.h"
+#include "hw/ssi/ssi.h"
+
+#define SPI_CTL     0x00
+#define SPI_CLKDIV  0x04
+#define SPI_SSCTL   0x08
+#define SPI_PDMACTl 0x0c
+#define SPI_FIFOCTL 0x10
+#define SPI_STATUS  0x14
+#define SPI_TX      0x20
+#define SPI_RX      0x30
+
+#define SPI_FIFOCTL_TXFULL  (1 << 17)
+#define SPI_FIFOCTL_TXEMPTY (1 << 16)
+
+#define SPI_FIFOCTL_RXFULL  (1 << 9)
+#define SPI_FIFOCTL_RXEMPTY (1 << 8)
+#define SPI_FIFOCTL_TXRST   (1 << 1)
+#define SPI_FIFOCTL_RXRST   (1 << 0)
+
+#define SPI_FIFOCTL_RST     (SPI_FIFOCTL_TXRST|SPI_FIFOCTL_RXRST)
+
+#define SPI_STATUS_TXRXRST  (1 << 23)
+
+#define TYPE_NUC980_SPI "nuc980-spi"
+#define NUC980_SPI(obj) \
+    OBJECT_CHECK(NUC980SPIState, (obj), TYPE_NUC980_SPI)
+
+#define FIFO_SIZE   4*8
+
+typedef struct spi_fifo {
+    int32_t fifo_pos;
+    int32_t fifo_len;
+    uint8_t fifo[FIFO_SIZE];
+}spi_fifo;
+
+typedef struct {
+    /* <private> */
+    SysBusDevice parent_obj;
+
+    /* <public> */
+    MemoryRegion mmio;
+
+    uint32_t spi_ctl;
+    uint32_t spi_clkdiv;
+    uint32_t spi_ssctl;
+    uint32_t spi_pdmactl;
+    uint32_t spi_fifoctl;
+    uint32_t spi_status;
+    uint32_t spi_tx;
+    uint32_t spi_rx;
+
+    spi_fifo tx_fifo;
+    spi_fifo rx_fifo;
+
+    qemu_irq irq;
+    SSIBus *ssi;
+
+} NUC980SPIState;
+
+#endif
\ No newline at end of file

总结

本文概要描述了使用qemu模拟NUC980 SPI控制器的框架代码,详细的代码修改,请参考文末的工程连接。

工程链接

https://gitee.com/yanl1229/qemu.git

相关推荐
RisunJan9 小时前
Linux命令-ipcrm命令(删除Linux系统中的进程间通信(IPC)资源)
linux·运维·服务器
Joren的学习记录9 小时前
【Linux运维大神系列】Kubernetes详解2(kubeadm部署k8s1.27单节点集群)
linux·运维·kubernetes
lbb 小魔仙9 小时前
【Linux】K8s 集群搭建避坑指南:基于 Linux 内核参数调优的生产级部署方案
linux·运维·kubernetes
老兵发新帖9 小时前
ubuntu服务器配置私钥登录
linux·服务器·ubuntu
vortex59 小时前
Linux 用户组查询命令详解
linux·运维·服务器
小杜今天学AI了吗9 小时前
如何配置 linux 系统的conda 环境
linux·运维·conda
oMcLin9 小时前
如何在Ubuntu 22.04 LTS上通过配置ZFS存储池,提升高吞吐量数据库的读写性能与可靠性?
linux·数据库·ubuntu
这就是佬们吗9 小时前
告别 Node.js 版本冲突:NVM 安装与使用全攻略
java·linux·前端·windows·node.js·mac·web
christine-rr9 小时前
linux常用命令(9)——查看系统与硬件信息
linux·运维·服务器·网络·后端