1 /*
2 * Copyright 2022 NXP
3 *
4 * SPDX-License-Identifier: Apache-2.0
5 */
6
7 #include <zephyr/kernel.h>
8 #include <zephyr/drivers/sdhc.h>
9 #include <zephyr/device.h>
10 #include <zephyr/ztest.h>
11
12 static const struct device *const sdhc_dev = DEVICE_DT_GET(DT_ALIAS(sdhc0));
13 static struct sdhc_host_props props;
14 static struct sdhc_io io;
15
16 #define SDHC_FREQUENCY_SLIP 10000000
17
18 K_SEM_DEFINE(card_sem, 0, 1);
19
20 /* Prepare IO settings for card */
sdhc_power_on(void)21 static void *sdhc_power_on(void)
22 {
23 int ret;
24
25 ret = sdhc_get_host_props(sdhc_dev, &props);
26 zassert_equal(ret, 0, "SDHC host props api call failed");
27
28 io.clock = props.f_min;
29 io.bus_mode = SDHC_BUSMODE_PUSHPULL;
30 io.power_mode = SDHC_POWER_ON;
31 io.bus_width = SDHC_BUS_WIDTH1BIT;
32 io.timing = SDHC_TIMING_LEGACY;
33 io.signal_voltage = SD_VOL_3_3_V;
34
35 ret = sdhc_set_io(sdhc_dev, &io);
36 zassert_equal(ret, 0, "Setting io configuration failed");
37 k_msleep(props.power_delay);
38 return NULL;
39 }
40
41 /* Resets SD host controller, verifies API */
ZTEST(sdhc,test_reset)42 ZTEST(sdhc, test_reset)
43 {
44 int ret;
45
46 zassert_true(device_is_ready(sdhc_dev), "SDHC device is not ready");
47
48 ret = sdhc_hw_reset(sdhc_dev);
49 zassert_equal(ret, 0, "SDHC HW reset failed");
50 }
51
52 /* Gets host properties, verifies all properties are set */
ZTEST(sdhc,test_host_props)53 ZTEST(sdhc, test_host_props)
54 {
55 int ret;
56
57 zassert_true(device_is_ready(sdhc_dev), "SDHC device is not ready");
58
59 /* Set all host properties to 0xFF */
60 props.f_max = 0xFF;
61 props.f_min = 0xFF;
62 props.power_delay = 0xFF;
63 props.max_current_330 = 0xFF;
64 props.max_current_300 = 0xFF;
65 props.max_current_180 = 0xFF;
66 ret = sdhc_get_host_props(sdhc_dev, &props);
67 zassert_equal(ret, 0, "SDHC host props api call failed");
68
69 zassert_not_equal(props.f_max, 0xFF, "props structure not initialized");
70 zassert_not_equal(props.f_min, 0xFF, "props structure not initialized");
71 zassert_not_equal(props.power_delay, 0xFF, "props structure not initialized");
72 zassert_not_equal(props.max_current_330, 0xFF, "props structure not initialized");
73 zassert_not_equal(props.max_current_300, 0xFF, "props structure not initialized");
74 zassert_not_equal(props.max_current_180, 0xFF, "props structure not initialized");
75 }
76
77 /* Verify that driver rejects frequencies outside of claimed range */
ZTEST(sdhc,test_set_io)78 ZTEST(sdhc, test_set_io)
79 {
80 int ret;
81
82 zassert_true(device_is_ready(sdhc_dev), "SDHC device is not ready");
83
84 io.clock = props.f_min;
85 io.bus_mode = SDHC_BUSMODE_PUSHPULL;
86 io.power_mode = SDHC_POWER_ON;
87 io.bus_width = SDHC_BUS_WIDTH1BIT;
88 io.timing = SDHC_TIMING_LEGACY;
89 io.signal_voltage = SD_VOL_3_3_V;
90
91 ret = sdhc_set_io(sdhc_dev, &io);
92 zassert_equal(ret, 0, "IO configuration failed");
93
94 /* Verify that IO configuration fails with high frequency */
95 /* Since SDHC may select nearby frequency, increase frequency
96 * by a large margin over the max.
97 */
98 io.clock = props.f_max + SDHC_FREQUENCY_SLIP;
99 ret = sdhc_set_io(sdhc_dev, &io);
100 zassert_not_equal(ret, 0, "Invalid io configuration should not succeed");
101 }
102
sdhc_interrupt_cb(const struct device * dev,int source,const void * data)103 void sdhc_interrupt_cb(const struct device *dev, int source, const void *data)
104 {
105 ARG_UNUSED(data);
106
107 /* Check that the device pointer is correct */
108 zassert_equal(dev, sdhc_dev, "Incorrect device pointer in interrupt callback");
109 zassert_equal(source, SDHC_INT_INSERTED, "Got unexpected SDHC interrupt");
110 k_sem_give(&card_sem);
111 }
112
113
114 /*
115 * Verify that the driver can detect a present SD card
116 */
ZTEST(sdhc,test_card_presence)117 ZTEST(sdhc, test_card_presence)
118 {
119 int ret;
120
121 zassert_true(device_is_ready(sdhc_dev), "SDHC device is not ready");
122
123 ret = sdhc_card_present(sdhc_dev);
124 if (ret == 0) {
125 /* Card not in slot, test card insertion interrupt */
126 TC_PRINT("Waiting for card to be present in slot\n");
127 ret = sdhc_enable_interrupt(sdhc_dev, sdhc_interrupt_cb,
128 SDHC_INT_INSERTED, NULL);
129 zassert_equal(ret, 0, "Could not install card insertion interrupt");
130 /* Wait for card insertion */
131 ret = k_sem_take(&card_sem, K_FOREVER);
132 /* Delay now that card is in slot */
133 k_msleep(props.power_delay);
134 zassert_equal(ret, 0, "Card insertion interrupt did not fire");
135 ret = sdhc_card_present(sdhc_dev);
136 }
137 zassert_equal(ret, 1, "Card is not reported as present, is one connected?");
138 }
139
140 /* Verify that the driver can send commands to SD card, by reading interface
141 * condition. This follows the first part of the SD initialization defined in
142 * the SD specification.
143 */
ZTEST(sdhc,test_card_if_cond)144 ZTEST(sdhc, test_card_if_cond)
145 {
146 struct sdhc_command cmd;
147 int ret, resp;
148 int check_pattern = SD_IF_COND_CHECK;
149
150 zassert_true(device_is_ready(sdhc_dev), "SDHC device is not ready");
151
152 /* Toggle power to card, to clear state */
153 io.power_mode = SDHC_POWER_OFF;
154 ret = sdhc_set_io(sdhc_dev, &io);
155 zassert_equal(ret, 0, "Setting io configuration failed");
156 k_msleep(props.power_delay);
157 io.power_mode = SDHC_POWER_ON;
158 ret = sdhc_set_io(sdhc_dev, &io);
159 zassert_equal(ret, 0, "Setting io configuration failed");
160 k_msleep(props.power_delay);
161
162
163 memset(&cmd, 0, sizeof(struct sdhc_command));
164 cmd.opcode = SD_GO_IDLE_STATE;
165 cmd.response_type = (SD_RSP_TYPE_NONE | SD_SPI_RSP_TYPE_R1);
166 cmd.timeout_ms = 200;
167
168 ret = sdhc_request(sdhc_dev, &cmd, NULL);
169 zassert_equal(ret, 0, "Card reset command failed");
170
171 cmd.opcode = SD_SEND_IF_COND;
172 /* Indicate 3.3V support, plus check pattern */
173 cmd.arg = SD_IF_COND_VHS_3V3 | check_pattern;
174 cmd.response_type = (SD_RSP_TYPE_R7 | SD_SPI_RSP_TYPE_R7);
175 cmd.timeout_ms = 500;
176 cmd.retries = 3;
177
178 ret = sdhc_request(sdhc_dev, &cmd, NULL);
179 zassert_equal(ret, 0, "Read Interface condition failed");
180 if (props.is_spi) {
181 resp = cmd.response[1];
182 } else {
183 resp = cmd.response[0];
184 }
185 if ((resp & 0xFF) == check_pattern) {
186 /* Although both responses are valid in SD spec, most
187 * modern cards are SDHC or better, and should respond as such.
188 */
189 TC_PRINT("Found SDHC/SDXC card\n");
190 } else if (resp & 0x4) {
191 /* An illegal command response indicates an SDSC card */
192 TC_PRINT("Found SDSC card\n");
193 } else {
194 zassert_unreachable("Invalid response to SD interface condition");
195 }
196 }
197
198 ZTEST_SUITE(sdhc, NULL, sdhc_power_on, NULL, NULL, NULL);
199