Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 5 additions & 1 deletion include/tinysd/sdio.h
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,10 @@
#define TSD_SDIO_BUS_WIDTH_4BIT 2
#define TSD_SDIO_BUS_WIDTH_8BIT 3

/** Card capability register fields. */
#define TSD_SDIO_CAPABILITY_4BLS TSD_BIT(7)
#define TSD_SDIO_CAPABILITY_LSC TSD_BIT(6)

/** Bus speed select register fields. */
#define TSD_SDIO_SPEED_EHS TSD_BIT(1)
#define TSD_SDIO_SPEED_SHS TSD_BIT(0)
Expand Down Expand Up @@ -149,7 +153,7 @@ int tsd_sdio_identify(struct tsd_host *host, struct tsd_sdio_cis *cis);
*
* @param card the card
* @param host the host the card sits on, started by the caller
* @param width the bus width, 1 or 4
* @param width the bus width the host can run, 1 or 4
* @param clock_khz the highest clock in kHz, held lower when the card cannot run it
* @return 0 or a negative tsd_error
*/
Expand Down
21 changes: 18 additions & 3 deletions src/sdio_init.c
Original file line number Diff line number Diff line change
Expand Up @@ -227,13 +227,28 @@ static int enumerate_card(struct tsd_sdio *card, struct tsd_host *host)
// Switch the card and then the host between 1-bit and 4-bit mode
static int set_bus_width(struct tsd_sdio *card, uint8_t width)
{
// Read the card capability register
uint8_t capability;
int ret = tsd_sdio_read_f0(card, TSD_SDIO_CCCR_CARD_CAPABILITY, &capability);
if (ret < 0)
return ret;

// Keep a low speed card on one data line unless it supports four
if ((capability & TSD_SDIO_CAPABILITY_LSC) && !(capability & TSD_SDIO_CAPABILITY_4BLS))
width = 1;

// Pick the width field's value
uint8_t field = TSD_SDIO_BUS_WIDTH_1BIT;
if (width == 4)
field = TSD_SDIO_BUS_WIDTH_4BIT;

// Read the bus interface control register and set its width field
uint8_t interface;
int ret = tsd_sdio_read_f0(card, TSD_SDIO_CCCR_BUS_INTERFACE_CONTROL, &interface);
ret = tsd_sdio_read_f0(card, TSD_SDIO_CCCR_BUS_INTERFACE_CONTROL, &interface);
if (ret < 0)
return ret;
interface = (interface & ~TSD_SDIO_BUS_WIDTH) |
TSD_FIELD_PREP(TSD_SDIO_BUS_WIDTH, width == 4 ? TSD_SDIO_BUS_WIDTH_4BIT : TSD_SDIO_BUS_WIDTH_1BIT);
interface &= ~TSD_SDIO_BUS_WIDTH;
interface |= TSD_FIELD_PREP(TSD_SDIO_BUS_WIDTH, field);

// Switch the card first, so it is listening on four lines when the host starts driving them
ret = tsd_sdio_write_f0(card, TSD_SDIO_CCCR_BUS_INTERFACE_CONTROL, interface);
Expand Down
15 changes: 15 additions & 0 deletions test/test_sdio.c
Original file line number Diff line number Diff line change
Expand Up @@ -183,6 +183,20 @@ static void test_low_speed(void)
TEST_ASSERT_EQUAL_UINT32(400, host.clock_khz);
}

// Test that a low speed card stays on one data line unless it supports four
static void test_low_speed_width(void)
{
uint8_t *interface = &host.f0[TSD_SDIO_CCCR_BUS_INTERFACE_CONTROL];

host.f0[TSD_SDIO_CCCR_CARD_CAPABILITY] = TSD_SDIO_CAPABILITY_LSC;
TEST_ASSERT_EQUAL(0, tsd_sdio_init(&card, &host.base, 4, 400));
TEST_ASSERT_EQUAL(TSD_SDIO_BUS_WIDTH_1BIT, TSD_FIELD_GET(TSD_SDIO_BUS_WIDTH, *interface));

host.f0[TSD_SDIO_CCCR_CARD_CAPABILITY] = TSD_SDIO_CAPABILITY_LSC | TSD_SDIO_CAPABILITY_4BLS;
TEST_ASSERT_EQUAL(0, tsd_sdio_init(&card, &host.base, 4, 400));
TEST_ASSERT_EQUAL(TSD_SDIO_BUS_WIDTH_4BIT, TSD_FIELD_GET(TSD_SDIO_BUS_WIDTH, *interface));
}

// Test that a block size past a function's limit is refused
static void test_block_size_limit(void)
{
Expand Down Expand Up @@ -214,6 +228,7 @@ int main(int argc, char **argv)
RUN_TEST(test_response_flags);
RUN_TEST(test_high_speed);
RUN_TEST(test_low_speed);
RUN_TEST(test_low_speed_width);
RUN_TEST(test_block_size_limit);
RUN_TEST(test_byte_mode_limit);

Expand Down
Loading