[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
[Qemu-devel] [PATCH 3/3] qtest: Add PIIX4 SMBus support for libqos
From: |
Marc Marí |
Subject: |
[Qemu-devel] [PATCH 3/3] qtest: Add PIIX4 SMBus support for libqos |
Date: |
Mon, 9 Jun 2014 10:55:33 +0200 |
Add support for the SMBus protocol on the PIIX4 chipset. Add a test for
EEPROMs using this interface.
Signed-off-by: Marc Marí <address@hidden>
---
tests/Makefile | 5 +
tests/eeprom-test.c | 58 ++++++++++++
tests/libqos/i2c.h | 5 +-
tests/libqos/smbus-piix4.c | 220 ++++++++++++++++++++++++++++++++++++++++++++
4 files changed, 287 insertions(+), 1 deletion(-)
create mode 100644 tests/eeprom-test.c
create mode 100644 tests/libqos/smbus-piix4.c
diff --git a/tests/Makefile b/tests/Makefile
index 361bb7b..e49c779 100644
--- a/tests/Makefile
+++ b/tests/Makefile
@@ -152,6 +152,8 @@ gcov-files-i386-y += hw/pci-bridge/i82801b11.c
check-qtest-i386-y += tests/ioh3420-test$(EXESUF)
gcov-files-i386-y += hw/pci-bridge/ioh3420.c
check-qtest-i386-y += tests/usb-hcd-ehci-test$(EXESUF)
+gcov-files-i386-y += hw/i2c/smbus_eeprom.c
+check-qtest-i386-y += tests/eeprom-test$(EXESUF)
gcov-files-i386-y += hw/usb/hcd-ehci.c
gcov-files-i386-y += hw/usb/hcd-uhci.c
gcov-files-i386-y += hw/usb/dev-hid.c
@@ -281,6 +283,7 @@ libqos-obj-y += tests/libqos/i2c.o
libqos-pc-obj-y = $(libqos-obj-y) tests/libqos/pci-pc.o
libqos-pc-obj-y += tests/libqos/malloc-pc.o
libqos-omap-obj-y = $(libqos-obj-y) tests/libqos/i2c-omap.o
+libqos-piix4-obj-y = $(libqos-obj-y) tests/libqos/smbus-piix4.o
tests/rtc-test$(EXESUF): tests/rtc-test.o
tests/m48t59-test$(EXESUF): tests/m48t59-test.o
@@ -322,9 +325,11 @@ tests/es1370-test$(EXESUF): tests/es1370-test.o
tests/intel-hda-test$(EXESUF): tests/intel-hda-test.o
tests/ioh3420-test$(EXESUF): tests/ioh3420-test.o
tests/usb-hcd-ehci-test$(EXESUF): tests/usb-hcd-ehci-test.o $(libqos-pc-obj-y)
+tests/eeprom-test$(EXESUF): tests/eeprom-test.o $(libqos-piix4-obj-y)
tests/qemu-iotests/socket_scm_helper$(EXESUF):
tests/qemu-iotests/socket_scm_helper.o
tests/test-qemu-opts$(EXESUF): tests/test-qemu-opts.o libqemuutil.a
libqemustub.a
+
# QTest rules
TARGETS=$(patsubst %-softmmu,%, $(filter %-softmmu,$(TARGET_DIRS)))
diff --git a/tests/eeprom-test.c b/tests/eeprom-test.c
new file mode 100644
index 0000000..6f01dd2
--- /dev/null
+++ b/tests/eeprom-test.c
@@ -0,0 +1,58 @@
+/*
+ * QTest testcase for the SMBus EEPROM device
+ *
+ * Copyright (c) 2014 Marc Marí
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2 or later.
+ * See the COPYING file in the top-level directory.
+ */
+
+#include <glib.h>
+#include <stdio.h>
+
+#include "libqtest.h"
+#include "libqos/i2c.h"
+
+#define PIIX4_SMBUS_BASE 0x0000b100
+
+#define EEPROM_TEST_ID "eeprom-test"
+#define EEPROM_TEST_ADDR 0x50
+
+static I2CAdapter *i2c;
+
+static void send_and_receive(void)
+{
+ uint8_t i;
+ uint8_t buf = 0;
+ uint64_t big_buf = 0;
+
+ for (i = EEPROM_TEST_ADDR; i < EEPROM_TEST_ADDR+8; ++i) {
+ i2c_recv(i2c, i, &buf, 1);
+ g_assert_cmphex(buf, ==, 0);
+
+ i2c_recv(i2c, i, (uint8_t *)&big_buf, 4);
+ g_assert_cmphex(big_buf, ==, 0);
+ }
+}
+
+int main(int argc, char **argv)
+{
+ QTestState *s = NULL;
+ int ret;
+
+ g_test_init(&argc, &argv, NULL);
+
+ s = qtest_start(NULL);
+ i2c = piix4_smbus_create(PIIX4_SMBUS_BASE);
+
+ qtest_add_func("/eeprom/tx-rx", send_and_receive);
+
+ ret = g_test_run();
+
+ if (s) {
+ qtest_quit(s);
+ }
+ g_free(i2c);
+
+ return ret;
+}
diff --git a/tests/libqos/i2c.h b/tests/libqos/i2c.h
index 1ce9af4..9fd3a1c 100644
--- a/tests/libqos/i2c.h
+++ b/tests/libqos/i2c.h
@@ -24,7 +24,10 @@ void i2c_send(I2CAdapter *i2c, uint8_t addr,
void i2c_recv(I2CAdapter *i2c, uint8_t addr,
uint8_t *buf, uint16_t len);
-/* libi2c-omap.c */
+/* i2c-omap.c */
I2CAdapter *omap_i2c_create(uint64_t addr);
+/* smbus-piix4.c */
+I2CAdapter *piix4_smbus_create(uint64_t addr);
+
#endif
diff --git a/tests/libqos/smbus-piix4.c b/tests/libqos/smbus-piix4.c
new file mode 100644
index 0000000..2e04968
--- /dev/null
+++ b/tests/libqos/smbus-piix4.c
@@ -0,0 +1,220 @@
+/*
+ * QTest I2C driver
+ *
+ * Copyright (c) 2014 Marc Marí
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2 or later.
+ * See the COPYING file in the top-level directory.
+ */
+#include "libqos/i2c.h"
+
+#include <glib.h>
+
+#include "libqtest.h"
+
+#define SMBUS_TIMEOUT 100000
+
+enum PIIX4SMBUSRegisters {
+ PIIX4_SMBUS_BA = 0x90, /* Base address (in [15:4]). 32 bits */
+ PIIX4_SMBUS_STAT = 0x00, /* Status. 8 bits */
+ PIIX4_SMBUS_CNT = 0x02, /* Control. 8 bits */
+ PIIX4_SMBUS_CMD = 0x03, /* Command. 8 bits */
+ PIIX4_SMBUS_ADD = 0x04, /* Address. 8 bits */
+ PIIX4_SMBUS_DAT0 = 0x05, /* Data 0. 8 bits */
+ PIIX4_SMBUS_DAT1 = 0x06, /* Data 1. 8 bits */
+ PIIX4_SMBUS_BLKDAT = 0x07, /* Block data. 8 bits */
+};
+
+enum PIIX4SMBUSSTATBits {
+ PIIX4_SMBUS_CNT_INTEREN = 1 << 0, /* Enable interrupts */
+ PIIX4_SMBUS_CNT_KILL = 1 << 1, /* Stop the current transaction */
+ PIIX4_SMBUS_CNT_PROT = 7 << 2, /* Type of command */
+ PIIX4_SMBUS_CNT_START = 1 << 6, /* Start execution */
+ PIIX4_SMBUS_STAT_BUSY = 1 << 0, /* Host busy */
+};
+
+enum PIIX4SMBUSCommands {
+ PIIX4_SMBUS_QRW = 0x00, /* Quick read or write */
+ PIIX4_SMBUS_BRW = 0x04, /* Byte read or write */
+ PIIX4_SMBUS_BDRW = 0x08, /* Byte data read or write */
+ PIIX4_SMBUS_WDRW = 0x0C, /* Word data read or write */
+ PIIX4_SMBUS_BLRW = 0x14, /* Block read or write */
+ PIIX4_SMBUS_WR = 0x0, /* Write */
+ PIIX4_SMBUS_RD = 0x1, /* Read */
+};
+
+typedef struct {
+ I2CAdapter parent;
+ uint64_t addr;
+} PIIX4I2C;
+
+static int piix4_smbus_wait_ready(uint64_t addr)
+{
+ uint64_t loops;
+ uint8_t status;
+ loops = SMBUS_TIMEOUT;
+ do {
+ clock_step(10);
+ status = inb(addr + PIIX4_SMBUS_STAT);
+ if ((status & 0x1) == 0) {
+ break; /* It has ended */
+ }
+ } while (--loops);
+
+ if (loops != 0) {
+ return 0;
+ } else {
+ return -1;
+ }
+}
+
+static int piix4_smbus_wait_done(uint64_t addr)
+{
+ uint64_t loops;
+ uint8_t status;
+ loops = SMBUS_TIMEOUT;
+ do {
+ clock_step(10);
+ status = inb(addr + PIIX4_SMBUS_STAT);
+ if ((status & 0x1) != 0) {
+ continue; /* It has not started yet */
+ }
+ if (status & 0xfe) {
+ break; /* One of the interrupt flags is set */
+ }
+ } while (--loops);
+
+ if (loops != 0) {
+ return 0;
+ } else {
+ return -1;
+ }
+}
+
+static void piix4_smbus_recv(I2CAdapter *i2c, uint8_t addr,
+ uint8_t *buf, uint16_t len)
+{
+ uint8_t stat;
+ uint8_t size;
+ PIIX4I2C *s = (PIIX4I2C *)i2c;
+
+ /* Wait to be ready */
+ g_assert_cmpint(piix4_smbus_wait_ready(s->addr), == , 0);
+
+ /* Clear interrupts */
+ outb(s->addr + PIIX4_SMBUS_STAT, 0x1e);
+
+ /* Write device */
+ outb(s->addr + PIIX4_SMBUS_ADD, (addr << 1) | PIIX4_SMBUS_RD);
+
+ /* Clear data */
+ outb(s->addr + PIIX4_SMBUS_DAT0, 0);
+
+ /* Start */
+ if (len == 1) {
+ outb(s->addr + PIIX4_SMBUS_CNT,
+ PIIX4_SMBUS_BRW | PIIX4_SMBUS_CNT_START);
+ } else {
+ outb(s->addr + PIIX4_SMBUS_CNT,
+ PIIX4_SMBUS_BLRW | PIIX4_SMBUS_CNT_START);
+ }
+
+ /* Wait to be done */
+ piix4_smbus_wait_done(s->addr);
+
+ /* Wait end */
+ stat = inb(s->addr + PIIX4_SMBUS_STAT);
+ g_assert_cmphex((stat & 0x3e), ==, 2); /* Only interrupt enabled
+
+ /* Read */
+ if (len == 1) {
+ buf[0] = inb(s->addr + PIIX4_SMBUS_DAT0);
+ } else {
+ while (len > 0) {
+ size = inb(s->addr + PIIX4_SMBUS_DAT0);
+ if (size == 0) {
+ break;
+ }
+ g_assert_cmpuint((len-size), <, 0);
+ while (size > 0) {
+ buf[0] = readb(s->addr + PIIX4_SMBUS_BLKDAT);
+ ++buf;
+ --size;
+ }
+
+ len -= size;
+ }
+ }
+}
+
+static void piix4_smbus_send(I2CAdapter *i2c, uint8_t addr,
+ const uint8_t *buf, uint16_t len)
+{
+ uint8_t stat;
+ uint8_t size;
+ PIIX4I2C *s = (PIIX4I2C *)i2c;
+
+ /* Wait to be ready */
+ g_assert_cmpint(piix4_smbus_wait_ready(s->addr), ==, 0);
+
+ /* Clear interrupts */
+ outb(s->addr + PIIX4_SMBUS_STAT, 0x1e);
+
+ /* Write device */
+ outb(s->addr + PIIX4_SMBUS_ADD, (addr << 1) | PIIX4_SMBUS_WR);
+
+ if (len == 1) {
+ /* Write */
+ outb(s->addr + PIIX4_SMBUS_DAT0, buf[0]);
+
+ stat = inb(s->addr + PIIX4_SMBUS_DAT0);
+ g_assert_cmphex(buf[0], ==, stat);
+
+ /* Start */
+ outb(s->addr + PIIX4_SMBUS_CNT,
+ PIIX4_SMBUS_BRW | PIIX4_SMBUS_CNT_START);
+
+ /* Wait to be done */
+ piix4_smbus_wait_done(s->addr);
+
+ /* Wait end */
+ stat = inb(s->addr + PIIX4_SMBUS_STAT);
+ g_assert_cmphex((stat & 0x3e), ==, 2); /* Only interrupt enabled
+ } else {
+ /* Write 32 bytes */
+ while (len > 0) {
+ size = 0;
+ while (len > 0 && size < 32) {
+ outb(s->addr + PIIX4_SMBUS_BLKDAT, buf[0]);
+ ++buf;
+ ++size;
+ --len;
+ }
+ outb(s->addr + PIIX4_SMBUS_DAT0, size);
+
+ /* Start */
+ outb(s->addr + PIIX4_SMBUS_CNT,
+ PIIX4_SMBUS_BLRW | PIIX4_SMBUS_CNT_START);
+
+ /* Wait to be done */
+ piix4_smbus_wait_done(s->addr);
+
+ /* Wait end */
+ stat = inb(s->addr + PIIX4_SMBUS_STAT);
+ g_assert_cmphex((stat & 0x3e), ==, 2);
+ }
+ }
+}
+
+I2CAdapter *piix4_smbus_create(uint64_t addr)
+{
+ PIIX4I2C *s = g_malloc0(sizeof(*s));
+ I2CAdapter *i2c = (I2CAdapter *)s;
+
+ s->addr = addr;
+
+ i2c->send = piix4_smbus_send;
+ i2c->recv = piix4_smbus_recv;
+
+ return i2c;
+}
--
1.7.10.4