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