master
c 169 lines 5.07 KB
Raw
1 /*
2 * QTest testcase for the PCA9552 LED blinker
3 *
4 * Copyright (c) 2017-2018, IBM Corporation.
5 *
6 * This work is licensed under the terms of the GNU GPL, version 2 or later.
7 * See the COPYING file in the top-level directory.
8 */
9
10 #include "qemu/osdep.h"
11
12 #include "libqtest.h"
13 #include "libqos/qgraph.h"
14 #include "libqos/i2c.h"
15 #include "hw/gpio/pca9552_regs.h"
16
17 #define PCA9552_TEST_ID "pca9552-test"
18 #define PCA9552_TEST_ADDR 0x60
19
20 static void pca9552_init(QI2CDevice *i2cdev)
21 {
22 /* Switch on LEDs 0 and 12 */
23 i2c_set8(i2cdev, PCA9552_LS0, 0x54);
24 i2c_set8(i2cdev, PCA9552_LS3, 0x54);
25 }
26
27 static void receive_autoinc(void *obj, void *data, QGuestAllocator *alloc)
28 {
29 QI2CDevice *i2cdev = (QI2CDevice *)obj;
30 uint8_t resp;
31 uint8_t reg = PCA9552_LS0 | PCA9552_AUTOINC;
32
33 pca9552_init(i2cdev);
34
35 qi2c_send(i2cdev, &reg, 1);
36
37 /* PCA9552_LS0 */
38 qi2c_recv(i2cdev, &resp, 1);
39 g_assert_cmphex(resp, ==, 0x54);
40
41 /* PCA9552_LS1 */
42 qi2c_recv(i2cdev, &resp, 1);
43 g_assert_cmphex(resp, ==, 0x55);
44
45 /* PCA9552_LS2 */
46 qi2c_recv(i2cdev, &resp, 1);
47 g_assert_cmphex(resp, ==, 0x55);
48
49 /* PCA9552_LS3 */
50 qi2c_recv(i2cdev, &resp, 1);
51 g_assert_cmphex(resp, ==, 0x54);
52 }
53
54 static void send_and_receive(void *obj, void *data, QGuestAllocator *alloc)
55 {
56 QI2CDevice *i2cdev = (QI2CDevice *)obj;
57 uint8_t value;
58
59 value = i2c_get8(i2cdev, PCA9552_LS0);
60 g_assert_cmphex(value, ==, 0x55);
61
62 value = i2c_get8(i2cdev, PCA9552_INPUT0);
63 g_assert_cmphex(value, ==, 0xFF);
64
65 pca9552_init(i2cdev);
66
67 value = i2c_get8(i2cdev, PCA9552_LS0);
68 g_assert_cmphex(value, ==, 0x54);
69
70 value = i2c_get8(i2cdev, PCA9552_INPUT0);
71 g_assert_cmphex(value, ==, 0xFE);
72
73 value = i2c_get8(i2cdev, PCA9552_LS3);
74 g_assert_cmphex(value, ==, 0x54);
75
76 value = i2c_get8(i2cdev, PCA9552_INPUT1);
77 g_assert_cmphex(value, ==, 0xEF);
78 }
79
80 /* Verify the power-on reset defaults. */
81 static void test_reset_defaults(void *obj, void *data, QGuestAllocator *alloc)
82 {
83 QI2CDevice *i2cdev = (QI2CDevice *)obj;
84
85 /* Prescalers, PWM duty cycles and LED selectors (all LEDs off) */
86 g_assert_cmphex(i2c_get8(i2cdev, PCA9552_PSC0), ==, 0xFF);
87 g_assert_cmphex(i2c_get8(i2cdev, PCA9552_PWM0), ==, 0x80);
88 g_assert_cmphex(i2c_get8(i2cdev, PCA9552_PSC1), ==, 0xFF);
89 g_assert_cmphex(i2c_get8(i2cdev, PCA9552_PWM1), ==, 0x80);
90 g_assert_cmphex(i2c_get8(i2cdev, PCA9552_LS0), ==, 0x55);
91 g_assert_cmphex(i2c_get8(i2cdev, PCA9552_LS1), ==, 0x55);
92 g_assert_cmphex(i2c_get8(i2cdev, PCA9552_LS2), ==, 0x55);
93 g_assert_cmphex(i2c_get8(i2cdev, PCA9552_LS3), ==, 0x55);
94
95 /* All LEDs off, so every pin floats high through its pull-up */
96 g_assert_cmphex(i2c_get8(i2cdev, PCA9552_INPUT0), ==, 0xFF);
97 g_assert_cmphex(i2c_get8(i2cdev, PCA9552_INPUT1), ==, 0xFF);
98 }
99
100 /*
101 * The PCA9552 only advances the command pointer when the AI bit is set, and
102 * it wraps modulo the full 10-register map.
103 */
104 static void test_autoinc_requires_ai_bit(void *obj, void *data,
105 QGuestAllocator *alloc)
106 {
107 QI2CDevice *i2cdev = (QI2CDevice *)obj;
108 uint8_t reg;
109 uint8_t resp;
110
111 /*
112 * With the AI bit, reading from LS3 (register 9) rolls over to INPUT0
113 * (register 0), not to a sibling in a register pair. All LEDs are off
114 * after reset so the input ports read 0xFF.
115 */
116 reg = PCA9552_LS3 | PCA9552_AUTOINC;
117 qi2c_send(i2cdev, &reg, 1);
118 qi2c_recv(i2cdev, &resp, 1); /* LS3 */
119 g_assert_cmphex(resp, ==, 0x55);
120 qi2c_recv(i2cdev, &resp, 1); /* wraps to INPUT0 */
121 g_assert_cmphex(resp, ==, 0xFF);
122 qi2c_recv(i2cdev, &resp, 1); /* INPUT1 */
123 g_assert_cmphex(resp, ==, 0xFF);
124
125 /*
126 * Without the AI bit the pointer must not advance: repeated reads keep
127 * returning the same register.
128 */
129 i2c_set8(i2cdev, PCA9552_LS0, 0x54);
130 reg = PCA9552_LS0;
131 qi2c_send(i2cdev, &reg, 1);
132 qi2c_recv(i2cdev, &resp, 1);
133 g_assert_cmphex(resp, ==, 0x54);
134 qi2c_recv(i2cdev, &resp, 1);
135 g_assert_cmphex(resp, ==, 0x54);
136 }
137
138 /*
139 * The PCA9552 decodes a 4-bit command and has no register past LS3 (9), so
140 * addressing register 0x0A reads back 0xFF.
141 */
142 static void test_command_out_of_range(void *obj, void *data,
143 QGuestAllocator *alloc)
144 {
145 QI2CDevice *i2cdev = (QI2CDevice *)obj;
146
147 g_assert_cmphex(i2c_get8(i2cdev, 0x0A), ==, 0xFF);
148 }
149
150 static void pca9552_register_nodes(void)
151 {
152 QOSGraphEdgeOptions opts = {
153 .extra_device_opts = "address=0x60"
154 };
155 add_qi2c_address(&opts, &(QI2CAddress) { 0x60 });
156
157 qos_node_create_driver("pca9552", i2c_device_create);
158 qos_node_consumes("pca9552", "i2c-bus", &opts);
159
160 qos_add_test("tx-rx", "pca9552", send_and_receive, NULL);
161 qos_add_test("rx-autoinc", "pca9552", receive_autoinc, NULL);
162 qos_add_test("reset-defaults", "pca9552", test_reset_defaults, NULL);
163 qos_add_test("autoinc-requires-ai-bit", "pca9552",
164 test_autoinc_requires_ai_bit, NULL);
165 qos_add_test("command-out-of-range", "pca9552", test_command_out_of_range,
166 NULL);
167 }
168
169 libqos_init(pca9552_register_nodes);