Skip to content

Commit 56f2001

Browse files
Martin Vychodilpacucha42
Martin Vychodil
authored andcommitted
sdmmc/sdspi: allow custom setup of SD card frequency
In order to allow flexible setup of SD card frequency, sdmmc_host_t.max_freq_khz is used as a limit Closes espressif/arduino-esp32#6225
1 parent 45d1582 commit 56f2001

File tree

14 files changed

+194
-49
lines changed

14 files changed

+194
-49
lines changed

components/driver/include/driver/sdmmc_host.h

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@ extern "C" {
4545
.io_int_enable = sdmmc_host_io_int_enable, \
4646
.io_int_wait = sdmmc_host_io_int_wait, \
4747
.command_timeout_ms = 0, \
48+
.get_real_freq = &sdmmc_host_get_real_freq \
4849
}
4950

5051
/**
@@ -259,6 +260,23 @@ esp_err_t sdmmc_host_io_int_wait(int slot, TickType_t timeout_ticks);
259260
*/
260261
esp_err_t sdmmc_host_deinit(void);
261262

263+
/**
264+
* @brief Provides a real frequency used for an SD card installed on specific slot
265+
* of SD/MMC host controller
266+
*
267+
* This function calculates real working frequency given by current SD/MMC host
268+
* controller setup for required slot: it reads associated host and card dividers
269+
* from corresponding SDMMC registers, calculates respective frequency and stores
270+
* the value into the 'real_freq_khz' parameter
271+
*
272+
* @param slot slot number (SDMMC_HOST_SLOT_0 or SDMMC_HOST_SLOT_1)
273+
* @param[out] real_freq_khz output parameter for the result frequency (in kHz)
274+
* @return
275+
* - ESP_OK on success
276+
* - ESP_ERR_INVALID_ARG on real_freq_khz == NULL or invalid slot number used
277+
*/
278+
esp_err_t sdmmc_host_get_real_freq(int slot, int* real_freq_khz);
279+
262280
#ifdef __cplusplus
263281
}
264282
#endif

components/driver/include/driver/sdmmc_types.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -183,6 +183,7 @@ typedef struct {
183183
esp_err_t (*io_int_enable)(int slot); /*!< Host function to enable SDIO interrupt line */
184184
esp_err_t (*io_int_wait)(int slot, TickType_t timeout_ticks); /*!< Host function to wait for SDIO interrupt line to be active */
185185
int command_timeout_ms; /*!< timeout, in milliseconds, of a single command. Set to 0 to use the default value. */
186+
esp_err_t (*get_real_freq)(int slot, int* real_freq); /*!< Host function to provide real working freq, based on SDMMC controller setup */
186187
} sdmmc_host_t;
187188

188189
/**
@@ -202,6 +203,7 @@ typedef struct {
202203
sdmmc_ext_csd_t ext_csd; /*!< decoded EXT_CSD (Extended Card Specific Data) register value */
203204
uint16_t rca; /*!< RCA (Relative Card Address) */
204205
uint16_t max_freq_khz; /*!< Maximum frequency, in kHz, supported by the card */
206+
int real_freq_khz; /*!< Real working frequency, in kHz, configured on the host controller */
205207
uint32_t is_mem : 1; /*!< Bit indicates if the card is a memory card */
206208
uint32_t is_sdio : 1; /*!< Bit indicates if the card is an IO card */
207209
uint32_t is_mmc : 1; /*!< Bit indicates if the card is MMC */

components/driver/include/driver/sdspi_host.h

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,7 @@ typedef int sdspi_dev_handle_t;
5050
.io_int_enable = &sdspi_host_io_int_enable, \
5151
.io_int_wait = &sdspi_host_io_int_wait, \
5252
.command_timeout_ms = 0, \
53+
.get_real_freq = &sdspi_host_get_real_freq \
5354
}
5455

5556
/**
@@ -156,6 +157,18 @@ esp_err_t sdspi_host_do_transaction(sdspi_dev_handle_t handle, sdmmc_command_t *
156157
*/
157158
esp_err_t sdspi_host_set_card_clk(sdspi_dev_handle_t host, uint32_t freq_khz);
158159

160+
/**
161+
* @brief Calculate working frequency for specific device
162+
*
163+
* @param handle SDSPI device handle
164+
* @param[out] real_freq_khz output parameter to hold the calculated frequency (in kHz)
165+
*
166+
* @return
167+
* - ESP_ERR_INVALID_ARG : ``handle`` is NULL or invalid or ``real_freq_khz`` parameter is NULL
168+
* - ESP_OK : Success
169+
*/
170+
esp_err_t sdspi_host_get_real_freq(sdspi_dev_handle_t handle, int* real_freq_khz);
171+
159172
/**
160173
* @brief Release resources allocated using sdspi_host_init
161174
*

components/driver/include/driver/spi_master.h

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -332,6 +332,18 @@ esp_err_t spi_device_acquire_bus(spi_device_handle_t device, TickType_t wait);
332332
*/
333333
void spi_device_release_bus(spi_device_handle_t dev);
334334

335+
/**
336+
* @brief Calculate working frequency for specific device
337+
*
338+
* @param handle SPI device handle
339+
* @param[out] freq_khz output parameter to hold calculated frequency in kHz
340+
*
341+
* @return
342+
* - ESP_ERR_INVALID_ARG : ``handle`` or ``freq_khz`` parameter is NULL
343+
* - ESP_OK : Success
344+
*/
345+
esp_err_t spi_device_get_actual_freq(spi_device_handle_t handle, int* freq_khz);
346+
335347
/**
336348
* @brief Calculate the working frequency that is most close to desired frequency.
337349
*

components/driver/sdmmc_host.c

Lines changed: 55 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -171,38 +171,58 @@ static void sdmmc_host_clock_update_command(int slot)
171171
}
172172
}
173173

174+
void sdmmc_host_get_clk_dividers(const uint32_t freq_khz, int *host_div, int *card_div)
175+
{
176+
// Calculate new dividers
177+
if (freq_khz >= SDMMC_FREQ_HIGHSPEED) {
178+
*host_div = 4; // 160 MHz / 4 = 40 MHz
179+
*card_div = 0;
180+
} else if (freq_khz == SDMMC_FREQ_DEFAULT) {
181+
*host_div = 8; // 160 MHz / 8 = 20 MHz
182+
*card_div = 0;
183+
} else if (freq_khz == SDMMC_FREQ_PROBING) {
184+
*host_div = 10; // 160 MHz / 10 / (20 * 2) = 400 kHz
185+
*card_div = 20;
186+
} else {
187+
/*
188+
* for custom frequencies use maximum range of host divider (1-16), find the closest <= div. combination
189+
* if exceeded, combine with the card divider to keep reasonable precision (applies mainly to low frequencies)
190+
* effective frequency range: 400 kHz - 32 MHz (32.1 - 39.9 MHz cannot be covered with given divider scheme)
191+
*/
192+
*host_div = (2 * APB_CLK_FREQ) / (freq_khz * 1000);
193+
if (*host_div > 15 ) {
194+
*host_div = 2;
195+
*card_div = APB_CLK_FREQ / (2 * freq_khz * 1000);
196+
if ( (APB_CLK_FREQ % (2 * freq_khz * 1000)) > 0 ) {
197+
(*card_div)++;
198+
}
199+
} else if ( ((2 * APB_CLK_FREQ) % (freq_khz * 1000)) > 0 ) {
200+
(*host_div)++;
201+
}
202+
}
203+
}
204+
205+
static int sdmmc_host_calc_freq(const int host_div, const int card_div)
206+
{
207+
return 2 * APB_CLK_FREQ / host_div / ((card_div == 0) ? 1 : card_div * 2) / 1000;
208+
}
209+
174210
esp_err_t sdmmc_host_set_card_clk(int slot, uint32_t freq_khz)
175211
{
176212
if (!(slot == 0 || slot == 1)) {
177213
return ESP_ERR_INVALID_ARG;
178214
}
179-
const int clk40m = 40000;
180215

181216
// Disable clock first
182217
SDMMC.clkena.cclk_enable &= ~BIT(slot);
183218
sdmmc_host_clock_update_command(slot);
184219

185220
int host_div = 0; /* clock divider of the host (SDMMC.clock) */
186221
int card_div = 0; /* 1/2 of card clock divider (SDMMC.clkdiv) */
222+
sdmmc_host_get_clk_dividers(freq_khz, &host_div, &card_div);
187223

188-
// Calculate new dividers
189-
if (freq_khz >= SDMMC_FREQ_HIGHSPEED) {
190-
host_div = 4; // 160 MHz / 4 = 40 MHz
191-
card_div = 0;
192-
} else if (freq_khz == SDMMC_FREQ_DEFAULT) {
193-
host_div = 8; // 160 MHz / 8 = 20 MHz
194-
card_div = 0;
195-
} else if (freq_khz == SDMMC_FREQ_PROBING) {
196-
host_div = 10; // 160 MHz / 10 / (20 * 2) = 400 kHz
197-
card_div = 20;
198-
} else {
199-
host_div = 2;
200-
card_div = (clk40m + freq_khz * 2 - 1) / (freq_khz * 2); // round up
201-
}
202-
203-
ESP_LOGD(TAG, "slot=%d host_div=%d card_div=%d freq=%dkHz",
204-
slot, host_div, card_div,
205-
2 * APB_CLK_FREQ / host_div / ((card_div == 0) ? 1 : card_div * 2) / 1000);
224+
int real_freq = sdmmc_host_calc_freq(host_div, card_div);
225+
ESP_LOGD(TAG, "slot=%d host_div=%d card_div=%d freq=%dkHz (max %" PRIu32 "kHz)", slot, host_div, card_div, real_freq, freq_khz);
206226

207227
// Program CLKDIV and CLKSRC, send them to the CIU
208228
switch(slot) {
@@ -236,6 +256,22 @@ esp_err_t sdmmc_host_set_card_clk(int slot, uint32_t freq_khz)
236256
return ESP_OK;
237257
}
238258

259+
esp_err_t sdmmc_host_get_real_freq(int slot, int* real_freq_khz)
260+
{
261+
if (real_freq_khz == NULL) {
262+
return ESP_ERR_INVALID_ARG;
263+
}
264+
if (!(slot == 0 || slot == 1)) {
265+
return ESP_ERR_INVALID_ARG;
266+
}
267+
268+
int host_div = SDMMC.clock.div_factor_p + 1;
269+
int card_div = slot == 0 ? SDMMC.clkdiv.div0 : SDMMC.clkdiv.div1;
270+
*real_freq_khz = sdmmc_host_calc_freq(host_div, card_div);
271+
272+
return ESP_OK;
273+
}
274+
239275
esp_err_t sdmmc_host_start_command(int slot, sdmmc_hw_cmd_t cmd, uint32_t arg) {
240276
if (!(slot == 0 || slot == 1)) {
241277
return ESP_ERR_INVALID_ARG;

components/driver/sdspi_host.c

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -299,6 +299,16 @@ esp_err_t sdspi_host_set_card_clk(sdspi_dev_handle_t handle, uint32_t freq_khz)
299299
return configure_spi_dev(slot, freq_khz * 1000);
300300
}
301301

302+
esp_err_t sdspi_host_get_real_freq(sdspi_dev_handle_t handle, int* real_freq_khz)
303+
{
304+
slot_info_t *slot = get_slot_info(handle);
305+
if (slot == NULL) {
306+
return ESP_ERR_INVALID_ARG;
307+
}
308+
309+
return spi_device_get_actual_freq(slot->spi_handle, real_freq_khz);
310+
}
311+
302312
static void gpio_intr(void* arg)
303313
{
304314
BaseType_t awoken = pdFALSE;

components/driver/spi_master.c

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -462,6 +462,19 @@ esp_err_t spi_bus_remove_device(spi_device_handle_t handle)
462462
return ESP_OK;
463463
}
464464

465+
esp_err_t spi_device_get_actual_freq(spi_device_handle_t handle, int* freq_khz)
466+
{
467+
if ((spi_device_t*)handle == NULL || freq_khz == NULL) {
468+
return ESP_ERR_INVALID_ARG;
469+
}
470+
471+
int dev_required_freq = ((spi_device_t*)handle)->cfg.clock_speed_hz;
472+
int dev_duty_cycle = ((spi_device_t*)handle)->cfg.duty_cycle_pos;
473+
*freq_khz = spi_get_actual_clock(esp_clk_apb_freq(), dev_required_freq, dev_duty_cycle);
474+
475+
return ESP_OK;
476+
}
477+
465478
int spi_get_actual_clock(int fapb, int hz, int duty_cycle)
466479
{
467480
return spi_hal_master_cal_clock(fapb, hz, duty_cycle);

components/sdmmc/sdmmc_common.c

Lines changed: 18 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -197,32 +197,16 @@ esp_err_t sdmmc_init_host_frequency(sdmmc_card_t* card)
197197
{
198198
assert(card->max_freq_khz <= card->host.max_freq_khz);
199199

200-
/* Find highest frequency in the following list,
201-
* which is below card->max_freq_khz.
202-
*/
203-
const uint32_t freq_values[] = {
204-
SDMMC_FREQ_52M,
205-
SDMMC_FREQ_HIGHSPEED,
206-
SDMMC_FREQ_26M,
207-
SDMMC_FREQ_DEFAULT
208-
//NOTE: in sdspi mode, 20MHz may not work. in that case, add 10MHz here.
209-
};
210-
const int n_freq_values = sizeof(freq_values) / sizeof(freq_values[0]);
211-
212-
uint32_t selected_freq = SDMMC_FREQ_PROBING;
213-
for (int i = 0; i < n_freq_values; ++i) {
214-
uint32_t freq = freq_values[i];
215-
if (card->max_freq_khz >= freq) {
216-
selected_freq = freq;
217-
break;
200+
if (card->max_freq_khz > SDMMC_FREQ_PROBING) {
201+
esp_err_t err = (*card->host.set_card_clk)(card->host.slot, card->max_freq_khz);
202+
if (err != ESP_OK) {
203+
ESP_LOGE(TAG, "failed to switch bus frequency (0x%x)", err);
204+
return err;
218205
}
219-
}
220206

221-
ESP_LOGD(TAG, "%s: using %d kHz bus frequency", __func__, selected_freq);
222-
if (selected_freq > SDMMC_FREQ_PROBING) {
223-
esp_err_t err = (*card->host.set_card_clk)(card->host.slot, selected_freq);
207+
err = (*card->host.get_real_freq)(card->host.slot, &(card->real_freq_khz));
224208
if (err != ESP_OK) {
225-
ESP_LOGE(TAG, "failed to switch bus frequency (0x%x)", err);
209+
ESP_LOGE(TAG, "failed to get real working frequency (0x%x)", err);
226210
return err;
227211
}
228212
}
@@ -258,7 +242,9 @@ void sdmmc_card_print_info(FILE* stream, const sdmmc_card_t* card)
258242
bool print_scr = false;
259243
bool print_csd = false;
260244
const char* type;
245+
261246
fprintf(stream, "Name: %s\n", card->cid.name);
247+
262248
if (card->is_sdio) {
263249
type = "SDIO";
264250
print_scr = true;
@@ -271,12 +257,17 @@ void sdmmc_card_print_info(FILE* stream, const sdmmc_card_t* card)
271257
print_csd = true;
272258
}
273259
fprintf(stream, "Type: %s\n", type);
274-
if (card->max_freq_khz < 1000) {
275-
fprintf(stream, "Speed: %d kHz\n", card->max_freq_khz);
260+
261+
if (card->real_freq_khz == 0) {
262+
fprintf(stream, "Speed: N/A\n");
276263
} else {
277-
fprintf(stream, "Speed: %d MHz%s\n", card->max_freq_khz / 1000,
278-
card->is_ddr ? ", DDR" : "");
264+
const char *freq_unit = card->real_freq_khz < 1000 ? "kHz" : "MHz";
265+
const float freq = card->real_freq_khz < 1000 ? card->real_freq_khz : card->real_freq_khz / 1000.0;
266+
const char *max_freq_unit = card->max_freq_khz < 1000 ? "kHz" : "MHz";
267+
const float max_freq = card->max_freq_khz < 1000 ? card->max_freq_khz : card->max_freq_khz / 1000.0;
268+
fprintf(stream, "Speed: %.2f %s (limit: %.2f %s)%s\n", freq, freq_unit, max_freq, max_freq_unit, card->is_ddr ? ", DDR" : "");
279269
}
270+
280271
fprintf(stream, "Size: %lluMB\n", ((uint64_t) card->csd.capacity) * card->csd.sector_size / (1024 * 1024));
281272

282273
if (print_csd) {

components/sdmmc/sdmmc_sd.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -292,7 +292,7 @@ esp_err_t sdmmc_enable_hs_mode_and_check(sdmmc_card_t* card)
292292
return ESP_ERR_NOT_SUPPORTED;
293293
}
294294

295-
card->max_freq_khz = SDMMC_FREQ_HIGHSPEED;
295+
card->max_freq_khz = MIN(card->host.max_freq_khz, SDMMC_FREQ_HIGHSPEED);
296296
return ESP_OK;
297297
}
298298

components/sdmmc/test/test_sd.c

Lines changed: 33 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -132,6 +132,17 @@ static void probe_sd(int slot, int width, int freq_khz, int ddr)
132132
free(card);
133133
sd_test_board_power_off();
134134
}
135+
136+
extern void sdmmc_host_get_clk_dividers(const int freq_khz, int *host_div, int *card_div);
137+
138+
static void sd_test_check_clk_dividers(const int freq_khz, const int expected_host_div, const int expected_card_div)
139+
{
140+
printf(" %6d | %2d | %2d\n", freq_khz, expected_host_div, expected_card_div);
141+
int host_divider, card_divider;
142+
sdmmc_host_get_clk_dividers(freq_khz, &host_divider, &card_divider);
143+
TEST_ASSERT_EQUAL(host_divider, expected_host_div);
144+
TEST_ASSERT_EQUAL(card_divider, expected_card_div);
145+
}
135146
#endif //WITH_SD_TEST || WITH_EMMC_TEST
136147

137148
#if WITH_SD_TEST
@@ -140,6 +151,8 @@ TEST_CASE("probe SD, slot 1, 4-bit", "[sd][test_env=UT_T1_SDMODE]")
140151
probe_sd(SDMMC_HOST_SLOT_1, 4, SDMMC_FREQ_PROBING, 0);
141152
probe_sd(SDMMC_HOST_SLOT_1, 4, SDMMC_FREQ_DEFAULT, 0);
142153
probe_sd(SDMMC_HOST_SLOT_1, 4, SDMMC_FREQ_HIGHSPEED, 0);
154+
//custom frequency test
155+
probe_sd(SDMMC_HOST_SLOT_1, 4, 10000, 0);
143156
}
144157

145158
TEST_CASE("probe SD, slot 1, 1-bit", "[sd][test_env=UT_T1_SDMODE]")
@@ -163,6 +176,21 @@ TEST_CASE("probe SD, slot 0, 1-bit", "[sd][ignore]")
163176
probe_sd(SDMMC_HOST_SLOT_0, 1, SDMMC_FREQ_DEFAULT, 0);
164177
probe_sd(SDMMC_HOST_SLOT_0, 1, SDMMC_FREQ_HIGHSPEED, 0);
165178
}
179+
180+
TEST_CASE("SD clock dividers calculation", "[sd][test_env=UT_T1_SDMODE]")
181+
{
182+
printf("Frequency (kHz) | Expected host.div | Expected card.div\n");
183+
sd_test_check_clk_dividers(SDMMC_FREQ_PROBING, 10, 20);
184+
sd_test_check_clk_dividers(SDMMC_FREQ_DEFAULT, 8, 0);
185+
sd_test_check_clk_dividers(SDMMC_FREQ_HIGHSPEED, 4, 0);
186+
sd_test_check_clk_dividers(36000, 5, 0);
187+
sd_test_check_clk_dividers(30000, 6, 0);
188+
sd_test_check_clk_dividers(16000, 10, 0);
189+
sd_test_check_clk_dividers(10000, 2, 4);
190+
sd_test_check_clk_dividers(6000, 2, 7);
191+
sd_test_check_clk_dividers(1000, 2, 40);
192+
sd_test_check_clk_dividers(600, 2, 67);
193+
}
166194
#endif //WITH_SD_TEST
167195

168196
#if WITH_EMMC_TEST
@@ -218,10 +246,11 @@ static void test_sdspi_deinit_bus(spi_host_device_t host)
218246
TEST_ESP_OK(err);
219247
}
220248

221-
static void probe_core(int slot)
249+
static void probe_core(int slot, int freq_khz)
222250
{
223251
sdmmc_host_t config = SDSPI_HOST_DEFAULT();
224252
config.slot = slot;
253+
config.max_freq_khz = freq_khz;
225254

226255
sdmmc_card_t* card = malloc(sizeof(sdmmc_card_t));
227256
TEST_ASSERT_NOT_NULL(card);
@@ -242,7 +271,7 @@ static void probe_spi(int freq_khz, int pin_miso, int pin_mosi, int pin_sck, int
242271
TEST_ESP_OK(sdspi_host_init());
243272
TEST_ESP_OK(sdspi_host_init_device(&dev_config, &handle));
244273

245-
probe_core(handle);
274+
probe_core(handle, freq_khz);
246275

247276
TEST_ESP_OK(sdspi_host_deinit());
248277
test_sdspi_deinit_bus(dev_config.host_id);
@@ -253,6 +282,8 @@ static void probe_spi(int freq_khz, int pin_miso, int pin_mosi, int pin_sck, int
253282
TEST_CASE("probe SD in SPI mode", "[sd][test_env=UT_T1_SPIMODE]")
254283
{
255284
probe_spi(SDMMC_FREQ_DEFAULT, SDSPI_TEST_MISO_PIN, SDSPI_TEST_MOSI_PIN, SDSPI_TEST_SCLK_PIN, SDSPI_TEST_CS_PIN);
285+
//custom frequency test
286+
probe_spi(10000, SDSPI_TEST_MISO_PIN, SDSPI_TEST_MOSI_PIN, SDSPI_TEST_SCLK_PIN, SDSPI_TEST_CS_PIN);
256287
}
257288

258289
// No runner for this

0 commit comments

Comments
 (0)