1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17#include "qemu/osdep.h"
18#include "qemu/bitops.h"
19#include "libqos/i2c.h"
20#include "libqtest.h"
21#include "hw/sensor/tmp105_regs.h"
22
23#define NR_SMBUS_DEVICES 16
24#define SMBUS_ADDR(x) (0xf0080000 + 0x1000 * (x))
25#define SMBUS_IRQ(x) (64 + (x))
26
27#define EVB_DEVICE_ADDR 0x48
28#define INVALID_DEVICE_ADDR 0x01
29
30const int evb_bus_list[] = {0, 1, 2, 6};
31
32
33enum CommonRegister {
34 OFFSET_SDA = 0x0,
35 OFFSET_ST = 0x2,
36 OFFSET_CST = 0x4,
37 OFFSET_CTL1 = 0x6,
38 OFFSET_ADDR1 = 0x8,
39 OFFSET_CTL2 = 0xa,
40 OFFSET_ADDR2 = 0xc,
41 OFFSET_CTL3 = 0xe,
42 OFFSET_CST2 = 0x18,
43 OFFSET_CST3 = 0x19,
44};
45
46enum NPCM7xxSMBusBank0Register {
47 OFFSET_ADDR3 = 0x10,
48 OFFSET_ADDR7 = 0x11,
49 OFFSET_ADDR4 = 0x12,
50 OFFSET_ADDR8 = 0x13,
51 OFFSET_ADDR5 = 0x14,
52 OFFSET_ADDR9 = 0x15,
53 OFFSET_ADDR6 = 0x16,
54 OFFSET_ADDR10 = 0x17,
55 OFFSET_CTL4 = 0x1a,
56 OFFSET_CTL5 = 0x1b,
57 OFFSET_SCLLT = 0x1c,
58 OFFSET_FIF_CTL = 0x1d,
59 OFFSET_SCLHT = 0x1e,
60};
61
62enum NPCM7xxSMBusBank1Register {
63 OFFSET_FIF_CTS = 0x10,
64 OFFSET_FAIR_PER = 0x11,
65 OFFSET_TXF_CTL = 0x12,
66 OFFSET_T_OUT = 0x14,
67 OFFSET_TXF_STS = 0x1a,
68 OFFSET_RXF_STS = 0x1c,
69 OFFSET_RXF_CTL = 0x1e,
70};
71
72
73#define ST_STP BIT(7)
74#define ST_SDAST BIT(6)
75#define ST_BER BIT(5)
76#define ST_NEGACK BIT(4)
77#define ST_STASTR BIT(3)
78#define ST_NMATCH BIT(2)
79#define ST_MODE BIT(1)
80#define ST_XMIT BIT(0)
81
82
83#define CST_ARPMATCH BIT(7)
84#define CST_MATCHAF BIT(6)
85#define CST_TGSCL BIT(5)
86#define CST_TSDA BIT(4)
87#define CST_GCMATCH BIT(3)
88#define CST_MATCH BIT(2)
89#define CST_BB BIT(1)
90#define CST_BUSY BIT(0)
91
92
93#define CST2_INSTTS BIT(7)
94#define CST2_MATCH7F BIT(6)
95#define CST2_MATCH6F BIT(5)
96#define CST2_MATCH5F BIT(4)
97#define CST2_MATCH4F BIT(3)
98#define CST2_MATCH3F BIT(2)
99#define CST2_MATCH2F BIT(1)
100#define CST2_MATCH1F BIT(0)
101
102
103#define CST3_EO_BUSY BIT(7)
104#define CST3_MATCH10F BIT(2)
105#define CST3_MATCH9F BIT(1)
106#define CST3_MATCH8F BIT(0)
107
108
109#define CTL1_STASTRE BIT(7)
110#define CTL1_NMINTE BIT(6)
111#define CTL1_GCMEN BIT(5)
112#define CTL1_ACK BIT(4)
113#define CTL1_EOBINTE BIT(3)
114#define CTL1_INTEN BIT(2)
115#define CTL1_STOP BIT(1)
116#define CTL1_START BIT(0)
117
118
119#define CTL2_SCLFRQ(rv) extract8((rv), 1, 6)
120#define CTL2_ENABLE BIT(0)
121
122
123#define CTL3_SCL_LVL BIT(7)
124#define CTL3_SDA_LVL BIT(6)
125#define CTL3_BNK_SEL BIT(5)
126#define CTL3_400K_MODE BIT(4)
127#define CTL3_IDL_START BIT(3)
128#define CTL3_ARPMEN BIT(2)
129#define CTL3_SCLFRQ(rv) extract8((rv), 0, 2)
130
131
132#define ADDR_EN BIT(7)
133#define ADDR_A(rv) extract8((rv), 0, 6)
134
135
136#define FIF_CTL_FIFO_EN BIT(4)
137
138
139#define FIF_CTS_CLR_FIFO BIT(6)
140#define FIF_CTS_RFTE_IE BIT(3)
141#define FIF_CTS_RXF_TXE BIT(1)
142
143
144#define TXF_CTL_THR_TXIE BIT(6)
145#define TXF_CTL_TX_THR(rv) extract8((rv), 0, 5)
146
147
148#define TXF_STS_TX_THST BIT(6)
149#define TXF_STS_TX_BYTES(rv) extract8((rv), 0, 5)
150
151
152#define RXF_CTL_THR_RXIE BIT(6)
153#define RXF_CTL_LAST BIT(5)
154#define RXF_CTL_RX_THR(rv) extract8((rv), 0, 5)
155
156
157#define RXF_STS_RX_THST BIT(6)
158#define RXF_STS_RX_BYTES(rv) extract8((rv), 0, 5)
159
160
161static void choose_bank(QTestState *qts, uint64_t base_addr, uint8_t bank)
162{
163 uint8_t ctl3 = qtest_readb(qts, base_addr + OFFSET_CTL3);
164
165 if (bank) {
166 ctl3 |= CTL3_BNK_SEL;
167 } else {
168 ctl3 &= ~CTL3_BNK_SEL;
169 }
170
171 qtest_writeb(qts, base_addr + OFFSET_CTL3, ctl3);
172}
173
174static void check_running(QTestState *qts, uint64_t base_addr)
175{
176 g_assert_true(qtest_readb(qts, base_addr + OFFSET_CST) & CST_BUSY);
177 g_assert_true(qtest_readb(qts, base_addr + OFFSET_CST) & CST_BB);
178}
179
180static void check_stopped(QTestState *qts, uint64_t base_addr)
181{
182 uint8_t cst3;
183
184 g_assert_cmphex(qtest_readb(qts, base_addr + OFFSET_ST), ==, 0);
185 g_assert_false(qtest_readb(qts, base_addr + OFFSET_CST) & CST_BUSY);
186 g_assert_false(qtest_readb(qts, base_addr + OFFSET_CST) & CST_BB);
187
188 cst3 = qtest_readb(qts, base_addr + OFFSET_CST3);
189 g_assert_true(cst3 & CST3_EO_BUSY);
190 qtest_writeb(qts, base_addr + OFFSET_CST3, cst3);
191 cst3 = qtest_readb(qts, base_addr + OFFSET_CST3);
192 g_assert_false(cst3 & CST3_EO_BUSY);
193}
194
195static void enable_bus(QTestState *qts, uint64_t base_addr)
196{
197 uint8_t ctl2 = qtest_readb(qts, base_addr + OFFSET_CTL2);
198
199 ctl2 |= CTL2_ENABLE;
200 qtest_writeb(qts, base_addr + OFFSET_CTL2, ctl2);
201 g_assert_true(qtest_readb(qts, base_addr + OFFSET_CTL2) & CTL2_ENABLE);
202}
203
204static void disable_bus(QTestState *qts, uint64_t base_addr)
205{
206 uint8_t ctl2 = qtest_readb(qts, base_addr + OFFSET_CTL2);
207
208 ctl2 &= ~CTL2_ENABLE;
209 qtest_writeb(qts, base_addr + OFFSET_CTL2, ctl2);
210 g_assert_false(qtest_readb(qts, base_addr + OFFSET_CTL2) & CTL2_ENABLE);
211}
212
213static void start_transfer(QTestState *qts, uint64_t base_addr)
214{
215 uint8_t ctl1;
216
217 ctl1 = CTL1_START | CTL1_INTEN | CTL1_STASTRE;
218 qtest_writeb(qts, base_addr + OFFSET_CTL1, ctl1);
219 g_assert_cmphex(qtest_readb(qts, base_addr + OFFSET_CTL1), ==,
220 CTL1_INTEN | CTL1_STASTRE | CTL1_INTEN);
221 g_assert_cmphex(qtest_readb(qts, base_addr + OFFSET_ST), ==,
222 ST_MODE | ST_XMIT | ST_SDAST);
223 check_running(qts, base_addr);
224}
225
226static void stop_transfer(QTestState *qts, uint64_t base_addr)
227{
228 uint8_t ctl1 = qtest_readb(qts, base_addr + OFFSET_CTL1);
229
230 ctl1 &= ~(CTL1_START | CTL1_ACK);
231 ctl1 |= CTL1_STOP | CTL1_INTEN | CTL1_EOBINTE;
232 qtest_writeb(qts, base_addr + OFFSET_CTL1, ctl1);
233 ctl1 = qtest_readb(qts, base_addr + OFFSET_CTL1);
234 g_assert_false(ctl1 & CTL1_STOP);
235}
236
237static void send_byte(QTestState *qts, uint64_t base_addr, uint8_t byte)
238{
239 g_assert_cmphex(qtest_readb(qts, base_addr + OFFSET_ST), ==,
240 ST_MODE | ST_XMIT | ST_SDAST);
241 qtest_writeb(qts, base_addr + OFFSET_SDA, byte);
242}
243
244static bool check_recv(QTestState *qts, uint64_t base_addr)
245{
246 uint8_t st, fif_ctl, rxf_ctl, rxf_sts;
247 bool fifo;
248
249 st = qtest_readb(qts, base_addr + OFFSET_ST);
250 choose_bank(qts, base_addr, 0);
251 fif_ctl = qtest_readb(qts, base_addr + OFFSET_FIF_CTL);
252 fifo = fif_ctl & FIF_CTL_FIFO_EN;
253 if (!fifo) {
254 return st == (ST_MODE | ST_SDAST);
255 }
256
257 choose_bank(qts, base_addr, 1);
258 rxf_ctl = qtest_readb(qts, base_addr + OFFSET_RXF_CTL);
259 rxf_sts = qtest_readb(qts, base_addr + OFFSET_RXF_STS);
260
261 if ((rxf_ctl & RXF_CTL_THR_RXIE) && RXF_STS_RX_BYTES(rxf_sts) < 16) {
262 return st == ST_MODE;
263 } else {
264 return st == (ST_MODE | ST_SDAST);
265 }
266}
267
268static uint8_t recv_byte(QTestState *qts, uint64_t base_addr)
269{
270 g_assert_true(check_recv(qts, base_addr));
271 return qtest_readb(qts, base_addr + OFFSET_SDA);
272}
273
274static void send_address(QTestState *qts, uint64_t base_addr, uint8_t addr,
275 bool recv, bool valid)
276{
277 uint8_t encoded_addr = (addr << 1) | (recv ? 1 : 0);
278 uint8_t st;
279
280 qtest_writeb(qts, base_addr + OFFSET_SDA, encoded_addr);
281 st = qtest_readb(qts, base_addr + OFFSET_ST);
282
283 if (valid) {
284 if (recv) {
285 g_assert_cmphex(st, ==, ST_MODE | ST_SDAST | ST_STASTR);
286 } else {
287 g_assert_cmphex(st, ==, ST_MODE | ST_XMIT | ST_SDAST | ST_STASTR);
288 }
289
290 qtest_writeb(qts, base_addr + OFFSET_ST, ST_STASTR);
291 st = qtest_readb(qts, base_addr + OFFSET_ST);
292 if (recv) {
293 g_assert_true(check_recv(qts, base_addr));
294 } else {
295 g_assert_cmphex(st, ==, ST_MODE | ST_XMIT | ST_SDAST);
296 }
297 } else {
298 if (recv) {
299 g_assert_cmphex(st, ==, ST_MODE | ST_NEGACK);
300 } else {
301 g_assert_cmphex(st, ==, ST_MODE | ST_XMIT | ST_NEGACK);
302 }
303 }
304}
305
306static void send_nack(QTestState *qts, uint64_t base_addr)
307{
308 uint8_t ctl1 = qtest_readb(qts, base_addr + OFFSET_CTL1);
309
310 ctl1 &= ~(CTL1_START | CTL1_STOP);
311 ctl1 |= CTL1_ACK | CTL1_INTEN;
312 qtest_writeb(qts, base_addr + OFFSET_CTL1, ctl1);
313}
314
315static void start_fifo_mode(QTestState *qts, uint64_t base_addr)
316{
317 choose_bank(qts, base_addr, 0);
318 qtest_writeb(qts, base_addr + OFFSET_FIF_CTL, FIF_CTL_FIFO_EN);
319 g_assert_true(qtest_readb(qts, base_addr + OFFSET_FIF_CTL) &
320 FIF_CTL_FIFO_EN);
321 choose_bank(qts, base_addr, 1);
322 qtest_writeb(qts, base_addr + OFFSET_FIF_CTS,
323 FIF_CTS_CLR_FIFO | FIF_CTS_RFTE_IE);
324 g_assert_cmphex(qtest_readb(qts, base_addr + OFFSET_FIF_CTS), ==,
325 FIF_CTS_RFTE_IE);
326 g_assert_cmphex(qtest_readb(qts, base_addr + OFFSET_TXF_STS), ==, 0);
327 g_assert_cmphex(qtest_readb(qts, base_addr + OFFSET_RXF_STS), ==, 0);
328}
329
330static void start_recv_fifo(QTestState *qts, uint64_t base_addr, uint8_t bytes)
331{
332 choose_bank(qts, base_addr, 1);
333 qtest_writeb(qts, base_addr + OFFSET_TXF_CTL, 0);
334 qtest_writeb(qts, base_addr + OFFSET_RXF_CTL,
335 RXF_CTL_THR_RXIE | RXF_CTL_LAST | bytes);
336}
337
338
339static void test_disable_bus(gconstpointer data)
340{
341 intptr_t index = (intptr_t)data;
342 uint64_t base_addr = SMBUS_ADDR(index);
343 QTestState *qts = qtest_init("-machine npcm750-evb");
344
345 disable_bus(qts, base_addr);
346 g_assert_cmphex(qtest_readb(qts, base_addr + OFFSET_CTL1), ==, 0);
347 g_assert_cmphex(qtest_readb(qts, base_addr + OFFSET_ST), ==, 0);
348 g_assert_false(qtest_readb(qts, base_addr + OFFSET_CST3) & CST3_EO_BUSY);
349 g_assert_cmphex(qtest_readb(qts, base_addr + OFFSET_CST), ==, 0);
350 qtest_quit(qts);
351}
352
353
354static void test_invalid_addr(gconstpointer data)
355{
356 intptr_t index = (intptr_t)data;
357 uint64_t base_addr = SMBUS_ADDR(index);
358 int irq = SMBUS_IRQ(index);
359 QTestState *qts = qtest_init("-machine npcm750-evb");
360
361 qtest_irq_intercept_in(qts, "/machine/soc/a9mpcore/gic");
362 enable_bus(qts, base_addr);
363 g_assert_false(qtest_get_irq(qts, irq));
364 start_transfer(qts, base_addr);
365 send_address(qts, base_addr, INVALID_DEVICE_ADDR, false, false);
366 g_assert_true(qtest_get_irq(qts, irq));
367 stop_transfer(qts, base_addr);
368 check_running(qts, base_addr);
369 qtest_writeb(qts, base_addr + OFFSET_ST, ST_NEGACK);
370 g_assert_false(qtest_readb(qts, base_addr + OFFSET_ST) & ST_NEGACK);
371 check_stopped(qts, base_addr);
372 qtest_quit(qts);
373}
374
375
376static void test_single_mode(gconstpointer data)
377{
378 intptr_t index = (intptr_t)data;
379 uint64_t base_addr = SMBUS_ADDR(index);
380 int irq = SMBUS_IRQ(index);
381 uint8_t value = 0x60;
382 QTestState *qts = qtest_init("-machine npcm750-evb");
383
384 qtest_irq_intercept_in(qts, "/machine/soc/a9mpcore/gic");
385 enable_bus(qts, base_addr);
386
387
388 g_assert_false(qtest_get_irq(qts, irq));
389 start_transfer(qts, base_addr);
390 g_assert_true(qtest_get_irq(qts, irq));
391 send_address(qts, base_addr, EVB_DEVICE_ADDR, false, true);
392 send_byte(qts, base_addr, TMP105_REG_CONFIG);
393 send_byte(qts, base_addr, value);
394 stop_transfer(qts, base_addr);
395 check_stopped(qts, base_addr);
396
397
398 start_transfer(qts, base_addr);
399 send_address(qts, base_addr, EVB_DEVICE_ADDR, false, true);
400 send_byte(qts, base_addr, TMP105_REG_CONFIG);
401 start_transfer(qts, base_addr);
402 send_address(qts, base_addr, EVB_DEVICE_ADDR, true, true);
403 send_nack(qts, base_addr);
404 stop_transfer(qts, base_addr);
405 check_running(qts, base_addr);
406 g_assert_cmphex(recv_byte(qts, base_addr), ==, value);
407 check_stopped(qts, base_addr);
408 qtest_quit(qts);
409}
410
411
412static void test_fifo_mode(gconstpointer data)
413{
414 intptr_t index = (intptr_t)data;
415 uint64_t base_addr = SMBUS_ADDR(index);
416 int irq = SMBUS_IRQ(index);
417 uint8_t value = 0x60;
418 QTestState *qts = qtest_init("-machine npcm750-evb");
419
420 qtest_irq_intercept_in(qts, "/machine/soc/a9mpcore/gic");
421 enable_bus(qts, base_addr);
422 start_fifo_mode(qts, base_addr);
423 g_assert_false(qtest_get_irq(qts, irq));
424
425
426 start_transfer(qts, base_addr);
427 send_address(qts, base_addr, EVB_DEVICE_ADDR, false, true);
428 choose_bank(qts, base_addr, 1);
429 g_assert_true(qtest_readb(qts, base_addr + OFFSET_FIF_CTS) &
430 FIF_CTS_RXF_TXE);
431 qtest_writeb(qts, base_addr + OFFSET_TXF_CTL, TXF_CTL_THR_TXIE);
432 send_byte(qts, base_addr, TMP105_REG_CONFIG);
433 send_byte(qts, base_addr, value);
434 g_assert_true(qtest_readb(qts, base_addr + OFFSET_FIF_CTS) &
435 FIF_CTS_RXF_TXE);
436 g_assert_true(qtest_readb(qts, base_addr + OFFSET_TXF_STS) &
437 TXF_STS_TX_THST);
438 g_assert_cmpuint(TXF_STS_TX_BYTES(
439 qtest_readb(qts, base_addr + OFFSET_TXF_STS)), ==, 0);
440 g_assert_true(qtest_get_irq(qts, irq));
441 stop_transfer(qts, base_addr);
442 check_stopped(qts, base_addr);
443
444
445 start_fifo_mode(qts, base_addr);
446 start_transfer(qts, base_addr);
447 send_address(qts, base_addr, EVB_DEVICE_ADDR, false, true);
448 send_byte(qts, base_addr, TMP105_REG_CONFIG);
449 start_transfer(qts, base_addr);
450 qtest_writeb(qts, base_addr + OFFSET_FIF_CTS, FIF_CTS_RXF_TXE);
451 start_recv_fifo(qts, base_addr, 1);
452 send_address(qts, base_addr, EVB_DEVICE_ADDR, true, true);
453 g_assert_false(qtest_readb(qts, base_addr + OFFSET_FIF_CTS) &
454 FIF_CTS_RXF_TXE);
455 g_assert_true(qtest_readb(qts, base_addr + OFFSET_RXF_STS) &
456 RXF_STS_RX_THST);
457 g_assert_cmpuint(RXF_STS_RX_BYTES(
458 qtest_readb(qts, base_addr + OFFSET_RXF_STS)), ==, 1);
459 send_nack(qts, base_addr);
460 stop_transfer(qts, base_addr);
461 check_running(qts, base_addr);
462 g_assert_cmphex(recv_byte(qts, base_addr), ==, value);
463 g_assert_cmpuint(RXF_STS_RX_BYTES(
464 qtest_readb(qts, base_addr + OFFSET_RXF_STS)), ==, 0);
465 check_stopped(qts, base_addr);
466 qtest_quit(qts);
467}
468
469static void smbus_add_test(const char *name, int index, GTestDataFunc fn)
470{
471 g_autofree char *full_name = g_strdup_printf(
472 "npcm7xx_smbus[%d]/%s", index, name);
473 qtest_add_data_func(full_name, (void *)(intptr_t)index, fn);
474}
475#define add_test(name, td) smbus_add_test(#name, td, test_##name)
476
477int main(int argc, char **argv)
478{
479 int i;
480
481 g_test_init(&argc, &argv, NULL);
482 g_test_set_nonfatal_assertions();
483
484 for (i = 0; i < NR_SMBUS_DEVICES; ++i) {
485 add_test(disable_bus, i);
486 add_test(invalid_addr, i);
487 }
488
489 for (i = 0; i < ARRAY_SIZE(evb_bus_list); ++i) {
490 add_test(single_mode, evb_bus_list[i]);
491 add_test(fifo_mode, evb_bus_list[i]);
492 }
493
494 return g_test_run();
495}
496