1 /*
2 * Copyright (c) 2021-2023 HPMicro
3 *
4 * SPDX-License-Identifier: BSD-3-Clause
5 *
6 */
7
8 #include "hpm_sdmmc_common.h"
9 #include "hpm_sdmmc_card.h"
10 #include <string.h>
11
sdmmc_go_idle_state(sdmmc_host_t * host,uint32_t argument)12 hpm_stat_t sdmmc_go_idle_state(sdmmc_host_t *host, uint32_t argument)
13 {
14 hpm_stat_t status = status_invalid_argument;
15
16 do {
17 HPM_BREAK_IF(host == NULL);
18 sdmmchost_cmd_t *host_cmd = &host->cmd;
19 (void) memset(host_cmd, 0, sizeof(sdmmchost_cmd_t));
20
21 host_cmd->cmd_index = sdmmc_cmd_go_idle_state;
22 host_cmd->cmd_argument = argument;
23 host_cmd->resp_type = sdxc_dev_resp_none;
24
25 status = sdmmchost_send_command(host, host_cmd);
26
27 } while (false);
28
29 return status;
30 }
31
sdmmc_go_inactive_state(sdmmc_host_t * host,uint16_t relative_addr)32 hpm_stat_t sdmmc_go_inactive_state(sdmmc_host_t *host, uint16_t relative_addr)
33 {
34 hpm_stat_t status = status_invalid_argument;
35
36 do {
37 HPM_BREAK_IF(host == NULL);
38 sdmmchost_cmd_t *host_cmd = &host->cmd;
39 (void) memset(host_cmd, 0, sizeof(sdmmchost_cmd_t));
40
41 host_cmd->cmd_index = sdmmc_cmd_go_inactive_state;
42 host_cmd->cmd_argument = ((uint32_t) relative_addr) << 16;
43 host_cmd->resp_type = sdxc_dev_resp_none;
44
45 status = sdmmchost_send_command(host, host_cmd);
46
47 } while (false);
48
49 return status;
50 }
51
sdmmc_select_card(sdmmc_host_t * host,uint16_t relative_addr,bool is_selected)52 hpm_stat_t sdmmc_select_card(sdmmc_host_t *host, uint16_t relative_addr, bool is_selected)
53 {
54 hpm_stat_t status = status_invalid_argument;
55
56 do {
57 HPM_BREAK_IF(host == NULL);
58 sdmmchost_cmd_t *host_cmd = &host->cmd;
59 (void) memset(host_cmd, 0, sizeof(sdmmchost_cmd_t));
60
61 host_cmd->cmd_index = sdmmc_cmd_select_card;
62 host_cmd->cmd_argument = ((uint32_t) relative_addr) << 16;
63
64 if (is_selected) {
65 host_cmd->resp_type = sdxc_dev_resp_r1b;
66 } else {
67 host_cmd->resp_type = sdxc_dev_resp_none;
68 }
69 status = sdmmchost_send_command(host, host_cmd);
70
71 } while (false);
72
73 return status;
74 }
75
sdmmc_send_application_command(sdmmc_host_t * host,uint16_t relative_addr)76 hpm_stat_t sdmmc_send_application_command(sdmmc_host_t *host, uint16_t relative_addr)
77 {
78 hpm_stat_t status = status_invalid_argument;
79
80 do {
81 HPM_BREAK_IF(host == NULL);
82 sdmmchost_cmd_t *host_cmd = &host->cmd;
83 (void) memset(host_cmd, 0, sizeof(sdmmchost_cmd_t));
84
85 host_cmd->cmd_index = sdmmc_cmd_app_cmd;
86 host_cmd->cmd_argument = ((uint32_t) relative_addr) << 16;
87
88 host_cmd->resp_type = sdxc_dev_resp_r1;
89 status = sdmmchost_send_command(host, host_cmd);
90
91 } while (false);
92
93 return status;
94 }
95
sdmmc_set_block_count(sdmmc_host_t * host,uint32_t block_count)96 hpm_stat_t sdmmc_set_block_count(sdmmc_host_t *host, uint32_t block_count)
97 {
98 hpm_stat_t status = status_invalid_argument;
99
100 do {
101 HPM_BREAK_IF(host == NULL);
102 sdmmchost_cmd_t *host_cmd = &host->cmd;
103 (void) memset(host_cmd, 0, sizeof(sdmmchost_cmd_t));
104
105 host_cmd->cmd_index = sdmmc_cmd_set_block_count;
106 host_cmd->cmd_argument = block_count;
107 host_cmd->resp_type = sdxc_dev_resp_r1;
108
109 status = sdmmchost_send_command(host, host_cmd);
110
111 } while (false);
112
113 return status;
114 }
115
sdmmc_set_block_size(sdmmc_host_t * host,uint32_t block_size)116 hpm_stat_t sdmmc_set_block_size(sdmmc_host_t *host, uint32_t block_size)
117 {
118 hpm_stat_t status = status_invalid_argument;
119
120 do {
121 HPM_BREAK_IF(host == NULL);
122 sdmmchost_cmd_t *host_cmd = &host->cmd;
123 (void) memset(host_cmd, 0, sizeof(sdmmchost_cmd_t));
124
125 host_cmd->cmd_index = sdmmc_cmd_set_block_length;
126 host_cmd->cmd_argument = block_size;
127 host_cmd->resp_type = sdxc_dev_resp_r1;
128
129 status = sdmmchost_send_command(host, host_cmd);
130
131 } while (false);
132
133 return status;
134 }
135
sdmmc_enable_auto_tuning(sdmmc_host_t * host)136 hpm_stat_t sdmmc_enable_auto_tuning(sdmmc_host_t *host)
137 {
138 hpm_stat_t status = status_invalid_argument;
139
140 do {
141 HPM_BREAK_IF((host == NULL) || (host->host_param.base == NULL));
142
143 SDXC_Type *base = host->host_param.base;
144
145 /* Prepare the Auto tuning environment */
146 sdxc_stop_clock_during_phase_code_change(base, true);
147 sdxc_set_post_change_delay(base, 3U);
148 sdxc_select_cardclk_delay_source(base, false);
149 sdxc_enable_power(base, true);
150
151 /* Start Auto tuning */
152 uint8_t tuning_cmd = (host->dev_type == sdmmc_dev_type_sd) ? 19U : 21U;
153 status = sdxc_perform_auto_tuning(host->host_param.base, tuning_cmd);
154 HPM_BREAK_IF(status != status_success);
155
156 } while (false);
157
158 return status;
159 }
160
extract_csd_field(const uint32_t * raw_csd,uint8_t end_offset,uint8_t start_offset)161 uint32_t extract_csd_field(const uint32_t *raw_csd, uint8_t end_offset, uint8_t start_offset)
162 {
163 uint32_t result = 0;
164
165 uint32_t start_word_index = start_offset / 32;
166 uint32_t end_word_index = end_offset / 32;
167 uint32_t end_offset_in_word = end_offset % 32;
168 uint32_t start_offset_in_word = start_offset % 32;
169
170 /* If all bits of the field are in the same raw_csd word */
171 if (start_word_index == end_word_index) {
172 uint32_t field_width = end_offset - start_offset + 1U;
173 uint32_t field_mask = ((1UL << field_width) - 1U) << start_offset;
174 result = (raw_csd[start_word_index] & field_mask) >> start_offset_in_word;
175 } else {
176 /* If the bits of the field crosses two raw_csd words */
177 uint32_t lsb_width = 32U - start_offset_in_word;
178 uint32_t result_lsb = raw_csd[start_word_index] >> start_offset_in_word;
179 uint32_t msb_width = end_offset_in_word + 1UL;
180 uint32_t result_msb = raw_csd[end_word_index] & ((1UL << msb_width) - 1U);
181 result = (result_msb << lsb_width) | result_lsb;
182 }
183
184 return result;
185 }
186