 907b5105f1
			
		
	
	
		907b5105f1
		
	
	
	
	
		
			
			Since commit a2ce7dbd917 ("meson: convert tests/qtest to meson"),
libqtest.h is under libqos/ directory, while libqtest.c is still in
qtest/. Move back to its original location to avoid mixing with libqos/.
Suggested-by: Thomas Huth <thuth@redhat.com>
Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>
Reviewed-by: Thomas Huth <thuth@redhat.com>
Reviewed-by: Stefan Berger <stefanb@linux.ibm.com>
		
	
			
		
			
				
	
	
		
			496 lines
		
	
	
		
			16 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			496 lines
		
	
	
		
			16 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
| /*
 | |
|  * QTests for Nuvoton NPCM7xx SMBus Modules.
 | |
|  *
 | |
|  * Copyright 2020 Google LLC
 | |
|  *
 | |
|  * 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.
 | |
|  *
 | |
|  * This program is distributed in the hope that it will be useful, but WITHOUT
 | |
|  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 | |
|  * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
 | |
|  * for more details.
 | |
|  */
 | |
| 
 | |
| #include "qemu/osdep.h"
 | |
| #include "qemu/bitops.h"
 | |
| #include "libqos/i2c.h"
 | |
| #include "libqtest.h"
 | |
| #include "hw/sensor/tmp105_regs.h"
 | |
| 
 | |
| #define NR_SMBUS_DEVICES    16
 | |
| #define SMBUS_ADDR(x)       (0xf0080000 + 0x1000 * (x))
 | |
| #define SMBUS_IRQ(x)        (64 + (x))
 | |
| 
 | |
| #define EVB_DEVICE_ADDR     0x48
 | |
| #define INVALID_DEVICE_ADDR 0x01
 | |
| 
 | |
| const int evb_bus_list[] = {0, 1, 2, 6};
 | |
| 
 | |
| /* Offsets */
 | |
| enum CommonRegister {
 | |
|     OFFSET_SDA     = 0x0,
 | |
|     OFFSET_ST      = 0x2,
 | |
|     OFFSET_CST     = 0x4,
 | |
|     OFFSET_CTL1    = 0x6,
 | |
|     OFFSET_ADDR1   = 0x8,
 | |
|     OFFSET_CTL2    = 0xa,
 | |
|     OFFSET_ADDR2   = 0xc,
 | |
|     OFFSET_CTL3    = 0xe,
 | |
|     OFFSET_CST2    = 0x18,
 | |
|     OFFSET_CST3    = 0x19,
 | |
| };
 | |
| 
 | |
| enum NPCM7xxSMBusBank0Register {
 | |
|     OFFSET_ADDR3   = 0x10,
 | |
|     OFFSET_ADDR7   = 0x11,
 | |
|     OFFSET_ADDR4   = 0x12,
 | |
|     OFFSET_ADDR8   = 0x13,
 | |
|     OFFSET_ADDR5   = 0x14,
 | |
|     OFFSET_ADDR9   = 0x15,
 | |
|     OFFSET_ADDR6   = 0x16,
 | |
|     OFFSET_ADDR10  = 0x17,
 | |
|     OFFSET_CTL4    = 0x1a,
 | |
|     OFFSET_CTL5    = 0x1b,
 | |
|     OFFSET_SCLLT   = 0x1c,
 | |
|     OFFSET_FIF_CTL = 0x1d,
 | |
|     OFFSET_SCLHT   = 0x1e,
 | |
| };
 | |
| 
 | |
| enum NPCM7xxSMBusBank1Register {
 | |
|     OFFSET_FIF_CTS  = 0x10,
 | |
|     OFFSET_FAIR_PER = 0x11,
 | |
|     OFFSET_TXF_CTL  = 0x12,
 | |
|     OFFSET_T_OUT    = 0x14,
 | |
|     OFFSET_TXF_STS  = 0x1a,
 | |
|     OFFSET_RXF_STS  = 0x1c,
 | |
|     OFFSET_RXF_CTL  = 0x1e,
 | |
| };
 | |
| 
 | |
| /* ST fields */
 | |
| #define ST_STP              BIT(7)
 | |
| #define ST_SDAST            BIT(6)
 | |
| #define ST_BER              BIT(5)
 | |
| #define ST_NEGACK           BIT(4)
 | |
| #define ST_STASTR           BIT(3)
 | |
| #define ST_NMATCH           BIT(2)
 | |
| #define ST_MODE             BIT(1)
 | |
| #define ST_XMIT             BIT(0)
 | |
| 
 | |
| /* CST fields */
 | |
| #define CST_ARPMATCH        BIT(7)
 | |
| #define CST_MATCHAF         BIT(6)
 | |
| #define CST_TGSCL           BIT(5)
 | |
| #define CST_TSDA            BIT(4)
 | |
| #define CST_GCMATCH         BIT(3)
 | |
| #define CST_MATCH           BIT(2)
 | |
| #define CST_BB              BIT(1)
 | |
| #define CST_BUSY            BIT(0)
 | |
| 
 | |
| /* CST2 fields */
 | |
| #define CST2_INSTTS         BIT(7)
 | |
| #define CST2_MATCH7F        BIT(6)
 | |
| #define CST2_MATCH6F        BIT(5)
 | |
| #define CST2_MATCH5F        BIT(4)
 | |
| #define CST2_MATCH4F        BIT(3)
 | |
| #define CST2_MATCH3F        BIT(2)
 | |
| #define CST2_MATCH2F        BIT(1)
 | |
| #define CST2_MATCH1F        BIT(0)
 | |
| 
 | |
| /* CST3 fields */
 | |
| #define CST3_EO_BUSY        BIT(7)
 | |
| #define CST3_MATCH10F       BIT(2)
 | |
| #define CST3_MATCH9F        BIT(1)
 | |
| #define CST3_MATCH8F        BIT(0)
 | |
| 
 | |
| /* CTL1 fields */
 | |
| #define CTL1_STASTRE        BIT(7)
 | |
| #define CTL1_NMINTE         BIT(6)
 | |
| #define CTL1_GCMEN          BIT(5)
 | |
| #define CTL1_ACK            BIT(4)
 | |
| #define CTL1_EOBINTE        BIT(3)
 | |
| #define CTL1_INTEN          BIT(2)
 | |
| #define CTL1_STOP           BIT(1)
 | |
| #define CTL1_START          BIT(0)
 | |
| 
 | |
| /* CTL2 fields */
 | |
| #define CTL2_SCLFRQ(rv)     extract8((rv), 1, 6)
 | |
| #define CTL2_ENABLE         BIT(0)
 | |
| 
 | |
| /* CTL3 fields */
 | |
| #define CTL3_SCL_LVL        BIT(7)
 | |
| #define CTL3_SDA_LVL        BIT(6)
 | |
| #define CTL3_BNK_SEL        BIT(5)
 | |
| #define CTL3_400K_MODE      BIT(4)
 | |
| #define CTL3_IDL_START      BIT(3)
 | |
| #define CTL3_ARPMEN         BIT(2)
 | |
| #define CTL3_SCLFRQ(rv)     extract8((rv), 0, 2)
 | |
| 
 | |
| /* ADDR fields */
 | |
| #define ADDR_EN             BIT(7)
 | |
| #define ADDR_A(rv)          extract8((rv), 0, 6)
 | |
| 
 | |
| /* FIF_CTL fields */
 | |
| #define FIF_CTL_FIFO_EN         BIT(4)
 | |
| 
 | |
| /* FIF_CTS fields */
 | |
| #define FIF_CTS_CLR_FIFO        BIT(6)
 | |
| #define FIF_CTS_RFTE_IE         BIT(3)
 | |
| #define FIF_CTS_RXF_TXE         BIT(1)
 | |
| 
 | |
| /* TXF_CTL fields */
 | |
| #define TXF_CTL_THR_TXIE        BIT(6)
 | |
| #define TXF_CTL_TX_THR(rv)      extract8((rv), 0, 5)
 | |
| 
 | |
| /* TXF_STS fields */
 | |
| #define TXF_STS_TX_THST         BIT(6)
 | |
| #define TXF_STS_TX_BYTES(rv)    extract8((rv), 0, 5)
 | |
| 
 | |
| /* RXF_CTL fields */
 | |
| #define RXF_CTL_THR_RXIE        BIT(6)
 | |
| #define RXF_CTL_LAST            BIT(5)
 | |
| #define RXF_CTL_RX_THR(rv)      extract8((rv), 0, 5)
 | |
| 
 | |
| /* RXF_STS fields */
 | |
| #define RXF_STS_RX_THST         BIT(6)
 | |
| #define RXF_STS_RX_BYTES(rv)    extract8((rv), 0, 5)
 | |
| 
 | |
| 
 | |
| static void choose_bank(QTestState *qts, uint64_t base_addr, uint8_t bank)
 | |
| {
 | |
|     uint8_t ctl3 = qtest_readb(qts, base_addr + OFFSET_CTL3);
 | |
| 
 | |
|     if (bank) {
 | |
|         ctl3 |= CTL3_BNK_SEL;
 | |
|     } else {
 | |
|         ctl3 &= ~CTL3_BNK_SEL;
 | |
|     }
 | |
| 
 | |
|     qtest_writeb(qts, base_addr + OFFSET_CTL3, ctl3);
 | |
| }
 | |
| 
 | |
| static void check_running(QTestState *qts, uint64_t base_addr)
 | |
| {
 | |
|     g_assert_true(qtest_readb(qts, base_addr + OFFSET_CST) & CST_BUSY);
 | |
|     g_assert_true(qtest_readb(qts, base_addr + OFFSET_CST) & CST_BB);
 | |
| }
 | |
| 
 | |
| static void check_stopped(QTestState *qts, uint64_t base_addr)
 | |
| {
 | |
|     uint8_t cst3;
 | |
| 
 | |
|     g_assert_cmphex(qtest_readb(qts, base_addr + OFFSET_ST), ==, 0);
 | |
|     g_assert_false(qtest_readb(qts, base_addr + OFFSET_CST) & CST_BUSY);
 | |
|     g_assert_false(qtest_readb(qts, base_addr + OFFSET_CST) & CST_BB);
 | |
| 
 | |
|     cst3 = qtest_readb(qts, base_addr + OFFSET_CST3);
 | |
|     g_assert_true(cst3 & CST3_EO_BUSY);
 | |
|     qtest_writeb(qts, base_addr + OFFSET_CST3, cst3);
 | |
|     cst3 = qtest_readb(qts, base_addr + OFFSET_CST3);
 | |
|     g_assert_false(cst3 & CST3_EO_BUSY);
 | |
| }
 | |
| 
 | |
| static void enable_bus(QTestState *qts, uint64_t base_addr)
 | |
| {
 | |
|     uint8_t ctl2 = qtest_readb(qts, base_addr + OFFSET_CTL2);
 | |
| 
 | |
|     ctl2 |= CTL2_ENABLE;
 | |
|     qtest_writeb(qts, base_addr + OFFSET_CTL2, ctl2);
 | |
|     g_assert_true(qtest_readb(qts, base_addr + OFFSET_CTL2) & CTL2_ENABLE);
 | |
| }
 | |
| 
 | |
| static void disable_bus(QTestState *qts, uint64_t base_addr)
 | |
| {
 | |
|     uint8_t ctl2 = qtest_readb(qts, base_addr + OFFSET_CTL2);
 | |
| 
 | |
|     ctl2 &= ~CTL2_ENABLE;
 | |
|     qtest_writeb(qts, base_addr + OFFSET_CTL2, ctl2);
 | |
|     g_assert_false(qtest_readb(qts, base_addr + OFFSET_CTL2) & CTL2_ENABLE);
 | |
| }
 | |
| 
 | |
| static void start_transfer(QTestState *qts, uint64_t base_addr)
 | |
| {
 | |
|     uint8_t ctl1;
 | |
| 
 | |
|     ctl1 = CTL1_START | CTL1_INTEN | CTL1_STASTRE;
 | |
|     qtest_writeb(qts, base_addr + OFFSET_CTL1, ctl1);
 | |
|     g_assert_cmphex(qtest_readb(qts, base_addr + OFFSET_CTL1), ==,
 | |
|                     CTL1_INTEN | CTL1_STASTRE | CTL1_INTEN);
 | |
|     g_assert_cmphex(qtest_readb(qts, base_addr + OFFSET_ST), ==,
 | |
|                     ST_MODE | ST_XMIT | ST_SDAST);
 | |
|     check_running(qts, base_addr);
 | |
| }
 | |
| 
 | |
| static void stop_transfer(QTestState *qts, uint64_t base_addr)
 | |
| {
 | |
|     uint8_t ctl1 = qtest_readb(qts, base_addr + OFFSET_CTL1);
 | |
| 
 | |
|     ctl1 &= ~(CTL1_START | CTL1_ACK);
 | |
|     ctl1 |= CTL1_STOP | CTL1_INTEN | CTL1_EOBINTE;
 | |
|     qtest_writeb(qts, base_addr + OFFSET_CTL1, ctl1);
 | |
|     ctl1 = qtest_readb(qts, base_addr + OFFSET_CTL1);
 | |
|     g_assert_false(ctl1 & CTL1_STOP);
 | |
| }
 | |
| 
 | |
| static void send_byte(QTestState *qts, uint64_t base_addr, uint8_t byte)
 | |
| {
 | |
|     g_assert_cmphex(qtest_readb(qts, base_addr + OFFSET_ST), ==,
 | |
|                     ST_MODE | ST_XMIT | ST_SDAST);
 | |
|     qtest_writeb(qts, base_addr + OFFSET_SDA, byte);
 | |
| }
 | |
| 
 | |
| static bool check_recv(QTestState *qts, uint64_t base_addr)
 | |
| {
 | |
|     uint8_t st, fif_ctl, rxf_ctl, rxf_sts;
 | |
|     bool fifo;
 | |
| 
 | |
|     st = qtest_readb(qts, base_addr + OFFSET_ST);
 | |
|     choose_bank(qts, base_addr, 0);
 | |
|     fif_ctl = qtest_readb(qts, base_addr + OFFSET_FIF_CTL);
 | |
|     fifo = fif_ctl & FIF_CTL_FIFO_EN;
 | |
|     if (!fifo) {
 | |
|         return st == (ST_MODE | ST_SDAST);
 | |
|     }
 | |
| 
 | |
|     choose_bank(qts, base_addr, 1);
 | |
|     rxf_ctl = qtest_readb(qts, base_addr + OFFSET_RXF_CTL);
 | |
|     rxf_sts = qtest_readb(qts, base_addr + OFFSET_RXF_STS);
 | |
| 
 | |
|     if ((rxf_ctl & RXF_CTL_THR_RXIE) && RXF_STS_RX_BYTES(rxf_sts) < 16) {
 | |
|         return st == ST_MODE;
 | |
|     } else {
 | |
|         return st == (ST_MODE | ST_SDAST);
 | |
|     }
 | |
| }
 | |
| 
 | |
| static uint8_t recv_byte(QTestState *qts, uint64_t base_addr)
 | |
| {
 | |
|     g_assert_true(check_recv(qts, base_addr));
 | |
|     return qtest_readb(qts, base_addr + OFFSET_SDA);
 | |
| }
 | |
| 
 | |
| static void send_address(QTestState *qts, uint64_t base_addr, uint8_t addr,
 | |
|                          bool recv, bool valid)
 | |
| {
 | |
|     uint8_t encoded_addr = (addr << 1) | (recv ? 1 : 0);
 | |
|     uint8_t st;
 | |
| 
 | |
|     qtest_writeb(qts, base_addr + OFFSET_SDA, encoded_addr);
 | |
|     st = qtest_readb(qts, base_addr + OFFSET_ST);
 | |
| 
 | |
|     if (valid) {
 | |
|         if (recv) {
 | |
|             g_assert_cmphex(st, ==, ST_MODE | ST_SDAST | ST_STASTR);
 | |
|         } else {
 | |
|             g_assert_cmphex(st, ==, ST_MODE | ST_XMIT | ST_SDAST | ST_STASTR);
 | |
|         }
 | |
| 
 | |
|         qtest_writeb(qts, base_addr + OFFSET_ST, ST_STASTR);
 | |
|         st = qtest_readb(qts, base_addr + OFFSET_ST);
 | |
|         if (recv) {
 | |
|             g_assert_true(check_recv(qts, base_addr));
 | |
|         } else {
 | |
|             g_assert_cmphex(st, ==, ST_MODE | ST_XMIT | ST_SDAST);
 | |
|         }
 | |
|     } else {
 | |
|         if (recv) {
 | |
|             g_assert_cmphex(st, ==, ST_MODE | ST_NEGACK);
 | |
|         } else {
 | |
|             g_assert_cmphex(st, ==, ST_MODE | ST_XMIT | ST_NEGACK);
 | |
|         }
 | |
|     }
 | |
| }
 | |
| 
 | |
| static void send_nack(QTestState *qts, uint64_t base_addr)
 | |
| {
 | |
|     uint8_t ctl1 = qtest_readb(qts, base_addr + OFFSET_CTL1);
 | |
| 
 | |
|     ctl1 &= ~(CTL1_START | CTL1_STOP);
 | |
|     ctl1 |= CTL1_ACK | CTL1_INTEN;
 | |
|     qtest_writeb(qts, base_addr + OFFSET_CTL1, ctl1);
 | |
| }
 | |
| 
 | |
| static void start_fifo_mode(QTestState *qts, uint64_t base_addr)
 | |
| {
 | |
|     choose_bank(qts, base_addr, 0);
 | |
|     qtest_writeb(qts, base_addr + OFFSET_FIF_CTL, FIF_CTL_FIFO_EN);
 | |
|     g_assert_true(qtest_readb(qts, base_addr + OFFSET_FIF_CTL) &
 | |
|                   FIF_CTL_FIFO_EN);
 | |
|     choose_bank(qts, base_addr, 1);
 | |
|     qtest_writeb(qts, base_addr + OFFSET_FIF_CTS,
 | |
|                  FIF_CTS_CLR_FIFO | FIF_CTS_RFTE_IE);
 | |
|     g_assert_cmphex(qtest_readb(qts, base_addr + OFFSET_FIF_CTS), ==,
 | |
|                     FIF_CTS_RFTE_IE);
 | |
|     g_assert_cmphex(qtest_readb(qts, base_addr + OFFSET_TXF_STS), ==, 0);
 | |
|     g_assert_cmphex(qtest_readb(qts, base_addr + OFFSET_RXF_STS), ==, 0);
 | |
| }
 | |
| 
 | |
| static void start_recv_fifo(QTestState *qts, uint64_t base_addr, uint8_t bytes)
 | |
| {
 | |
|     choose_bank(qts, base_addr, 1);
 | |
|     qtest_writeb(qts, base_addr + OFFSET_TXF_CTL, 0);
 | |
|     qtest_writeb(qts, base_addr + OFFSET_RXF_CTL,
 | |
|                  RXF_CTL_THR_RXIE | RXF_CTL_LAST | bytes);
 | |
| }
 | |
| 
 | |
| /* Check the SMBus's status is set correctly when disabled. */
 | |
| static void test_disable_bus(gconstpointer data)
 | |
| {
 | |
|     intptr_t index = (intptr_t)data;
 | |
|     uint64_t base_addr = SMBUS_ADDR(index);
 | |
|     QTestState *qts = qtest_init("-machine npcm750-evb");
 | |
| 
 | |
|     disable_bus(qts, base_addr);
 | |
|     g_assert_cmphex(qtest_readb(qts, base_addr + OFFSET_CTL1), ==, 0);
 | |
|     g_assert_cmphex(qtest_readb(qts, base_addr + OFFSET_ST), ==, 0);
 | |
|     g_assert_false(qtest_readb(qts, base_addr + OFFSET_CST3) & CST3_EO_BUSY);
 | |
|     g_assert_cmphex(qtest_readb(qts, base_addr + OFFSET_CST), ==, 0);
 | |
|     qtest_quit(qts);
 | |
| }
 | |
| 
 | |
| /* Check the SMBus returns a NACK for an invalid address. */
 | |
| static void test_invalid_addr(gconstpointer data)
 | |
| {
 | |
|     intptr_t index = (intptr_t)data;
 | |
|     uint64_t base_addr = SMBUS_ADDR(index);
 | |
|     int irq = SMBUS_IRQ(index);
 | |
|     QTestState *qts = qtest_init("-machine npcm750-evb");
 | |
| 
 | |
|     qtest_irq_intercept_in(qts, "/machine/soc/a9mpcore/gic");
 | |
|     enable_bus(qts, base_addr);
 | |
|     g_assert_false(qtest_get_irq(qts, irq));
 | |
|     start_transfer(qts, base_addr);
 | |
|     send_address(qts, base_addr, INVALID_DEVICE_ADDR, false, false);
 | |
|     g_assert_true(qtest_get_irq(qts, irq));
 | |
|     stop_transfer(qts, base_addr);
 | |
|     check_running(qts, base_addr);
 | |
|     qtest_writeb(qts, base_addr + OFFSET_ST, ST_NEGACK);
 | |
|     g_assert_false(qtest_readb(qts, base_addr + OFFSET_ST) & ST_NEGACK);
 | |
|     check_stopped(qts, base_addr);
 | |
|     qtest_quit(qts);
 | |
| }
 | |
| 
 | |
| /* Check the SMBus can send and receive bytes to a device in single mode. */
 | |
| static void test_single_mode(gconstpointer data)
 | |
| {
 | |
|     intptr_t index = (intptr_t)data;
 | |
|     uint64_t base_addr = SMBUS_ADDR(index);
 | |
|     int irq = SMBUS_IRQ(index);
 | |
|     uint8_t value = 0x60;
 | |
|     QTestState *qts = qtest_init("-machine npcm750-evb");
 | |
| 
 | |
|     qtest_irq_intercept_in(qts, "/machine/soc/a9mpcore/gic");
 | |
|     enable_bus(qts, base_addr);
 | |
| 
 | |
|     /* Sending */
 | |
|     g_assert_false(qtest_get_irq(qts, irq));
 | |
|     start_transfer(qts, base_addr);
 | |
|     g_assert_true(qtest_get_irq(qts, irq));
 | |
|     send_address(qts, base_addr, EVB_DEVICE_ADDR, false, true);
 | |
|     send_byte(qts, base_addr, TMP105_REG_CONFIG);
 | |
|     send_byte(qts, base_addr, value);
 | |
|     stop_transfer(qts, base_addr);
 | |
|     check_stopped(qts, base_addr);
 | |
| 
 | |
|     /* Receiving */
 | |
|     start_transfer(qts, base_addr);
 | |
|     send_address(qts, base_addr, EVB_DEVICE_ADDR, false, true);
 | |
|     send_byte(qts, base_addr, TMP105_REG_CONFIG);
 | |
|     start_transfer(qts, base_addr);
 | |
|     send_address(qts, base_addr, EVB_DEVICE_ADDR, true, true);
 | |
|     send_nack(qts, base_addr);
 | |
|     stop_transfer(qts, base_addr);
 | |
|     check_running(qts, base_addr);
 | |
|     g_assert_cmphex(recv_byte(qts, base_addr), ==, value);
 | |
|     check_stopped(qts, base_addr);
 | |
|     qtest_quit(qts);
 | |
| }
 | |
| 
 | |
| /* Check the SMBus can send and receive bytes in FIFO mode. */
 | |
| static void test_fifo_mode(gconstpointer data)
 | |
| {
 | |
|     intptr_t index = (intptr_t)data;
 | |
|     uint64_t base_addr = SMBUS_ADDR(index);
 | |
|     int irq = SMBUS_IRQ(index);
 | |
|     uint8_t value = 0x60;
 | |
|     QTestState *qts = qtest_init("-machine npcm750-evb");
 | |
| 
 | |
|     qtest_irq_intercept_in(qts, "/machine/soc/a9mpcore/gic");
 | |
|     enable_bus(qts, base_addr);
 | |
|     start_fifo_mode(qts, base_addr);
 | |
|     g_assert_false(qtest_get_irq(qts, irq));
 | |
| 
 | |
|     /* Sending */
 | |
|     start_transfer(qts, base_addr);
 | |
|     send_address(qts, base_addr, EVB_DEVICE_ADDR, false, true);
 | |
|     choose_bank(qts, base_addr, 1);
 | |
|     g_assert_true(qtest_readb(qts, base_addr + OFFSET_FIF_CTS) &
 | |
|                   FIF_CTS_RXF_TXE);
 | |
|     qtest_writeb(qts, base_addr + OFFSET_TXF_CTL, TXF_CTL_THR_TXIE);
 | |
|     send_byte(qts, base_addr, TMP105_REG_CONFIG);
 | |
|     send_byte(qts, base_addr, value);
 | |
|     g_assert_true(qtest_readb(qts, base_addr + OFFSET_FIF_CTS) &
 | |
|                   FIF_CTS_RXF_TXE);
 | |
|     g_assert_true(qtest_readb(qts, base_addr + OFFSET_TXF_STS) &
 | |
|                   TXF_STS_TX_THST);
 | |
|     g_assert_cmpuint(TXF_STS_TX_BYTES(
 | |
|                         qtest_readb(qts, base_addr + OFFSET_TXF_STS)), ==, 0);
 | |
|     g_assert_true(qtest_get_irq(qts, irq));
 | |
|     stop_transfer(qts, base_addr);
 | |
|     check_stopped(qts, base_addr);
 | |
| 
 | |
|     /* Receiving */
 | |
|     start_fifo_mode(qts, base_addr);
 | |
|     start_transfer(qts, base_addr);
 | |
|     send_address(qts, base_addr, EVB_DEVICE_ADDR, false, true);
 | |
|     send_byte(qts, base_addr, TMP105_REG_CONFIG);
 | |
|     start_transfer(qts, base_addr);
 | |
|     qtest_writeb(qts, base_addr + OFFSET_FIF_CTS, FIF_CTS_RXF_TXE);
 | |
|     start_recv_fifo(qts, base_addr, 1);
 | |
|     send_address(qts, base_addr, EVB_DEVICE_ADDR, true, true);
 | |
|     g_assert_false(qtest_readb(qts, base_addr + OFFSET_FIF_CTS) &
 | |
|                    FIF_CTS_RXF_TXE);
 | |
|     g_assert_true(qtest_readb(qts, base_addr + OFFSET_RXF_STS) &
 | |
|                   RXF_STS_RX_THST);
 | |
|     g_assert_cmpuint(RXF_STS_RX_BYTES(
 | |
|                         qtest_readb(qts, base_addr + OFFSET_RXF_STS)), ==, 1);
 | |
|     send_nack(qts, base_addr);
 | |
|     stop_transfer(qts, base_addr);
 | |
|     check_running(qts, base_addr);
 | |
|     g_assert_cmphex(recv_byte(qts, base_addr), ==, value);
 | |
|     g_assert_cmpuint(RXF_STS_RX_BYTES(
 | |
|                         qtest_readb(qts, base_addr + OFFSET_RXF_STS)), ==, 0);
 | |
|     check_stopped(qts, base_addr);
 | |
|     qtest_quit(qts);
 | |
| }
 | |
| 
 | |
| static void smbus_add_test(const char *name, int index, GTestDataFunc fn)
 | |
| {
 | |
|     g_autofree char *full_name = g_strdup_printf(
 | |
|             "npcm7xx_smbus[%d]/%s", index, name);
 | |
|     qtest_add_data_func(full_name, (void *)(intptr_t)index, fn);
 | |
| }
 | |
| #define add_test(name, td) smbus_add_test(#name, td, test_##name)
 | |
| 
 | |
| int main(int argc, char **argv)
 | |
| {
 | |
|     int i;
 | |
| 
 | |
|     g_test_init(&argc, &argv, NULL);
 | |
|     g_test_set_nonfatal_assertions();
 | |
| 
 | |
|     for (i = 0; i < NR_SMBUS_DEVICES; ++i) {
 | |
|         add_test(disable_bus, i);
 | |
|         add_test(invalid_addr, i);
 | |
|     }
 | |
| 
 | |
|     for (i = 0; i < ARRAY_SIZE(evb_bus_list); ++i) {
 | |
|         add_test(single_mode, evb_bus_list[i]);
 | |
|         add_test(fifo_mode, evb_bus_list[i]);
 | |
|     }
 | |
| 
 | |
|     return g_test_run();
 | |
| }
 |