From a08358dce9044c8c184e81b2be5be270e3a89c7e Mon Sep 17 00:00:00 2001 From: Martin Stein Date: Thu, 22 Oct 2015 17:06:00 +0200 Subject: [PATCH] sd_card & imx53: fix multiblock write termination The manual termination of multi-block writes via "Stop Transmission" commands seems to leave the card in a busy state sometimes. This causes errors on subsequent commands. Thus, we have to synchronize manually with the card-internal state via "Send State" commands. Additionally, the method for issuing the manual "Stop Transmission" commands was refined. Ref #1497 --- repos/os/src/drivers/sd_card/sd_card.h | 36 +++++++++++ .../src/drivers/sd_card/spec/imx53/esdhcv2.h | 61 ++++++++++++++++++- 2 files changed, 96 insertions(+), 1 deletion(-) diff --git a/repos/os/src/drivers/sd_card/sd_card.h b/repos/os/src/drivers/sd_card/sd_card.h index d18e62a02..25e2539a6 100644 --- a/repos/os/src/drivers/sd_card/sd_card.h +++ b/repos/os/src/drivers/sd_card/sd_card.h @@ -22,6 +22,34 @@ namespace Sd_card { using namespace Genode; + /** + * Structure of the first word of a native mode R1 response + */ + struct R1_response_0 : Register<32> + { + struct Ready_for_data : Bitfield<8, 1> { }; + struct State : Bitfield<9, 4> + { + enum { PROGRAM = 7 }; + }; + struct Error : Bitfield<19, 1> { }; + + /** + * Return wether the card is ready for data + * + * \param resp R1 response, word 0 + */ + static bool card_ready(access_t const resp) + { + /* + * Check both ready bit and state because not all cards handle + * the status bits correctly. + */ + return Ready_for_data::get(resp) && + State::get(resp) != State::PROGRAM; + } + }; + /** * Returned by 'Sd_send_op_cond' */ @@ -149,6 +177,14 @@ namespace Sd_card { } }; + struct Send_status : Command<13, RESPONSE_48_BIT> + { + struct Arg : Sd_card::Arg + { + struct Rca : Bitfield<16, 16> { }; + }; + }; + struct Select_card : Command<7, RESPONSE_48_BIT> { struct Arg : Sd_card::Arg diff --git a/repos/os/src/drivers/sd_card/spec/imx53/esdhcv2.h b/repos/os/src/drivers/sd_card/spec/imx53/esdhcv2.h index a50959549..dec19f2f9 100644 --- a/repos/os/src/drivers/sd_card/spec/imx53/esdhcv2.h +++ b/repos/os/src/drivers/sd_card/spec/imx53/esdhcv2.h @@ -177,6 +177,8 @@ struct Esdhcv2 : Genode::Mmio _48BIT_BUSY = 3, }; }; + struct Cccen : Bitfield<19, 1> { }; + struct Cicen : Bitfield<20, 1> { }; struct Dpsel : Bitfield<21, 1> { }; struct Cmdtyp : Bitfield<22, 2> { @@ -626,6 +628,57 @@ struct Esdhcv2_controller : private Esdhcv2, public Sd_card::Host_controller return true; } + int _wait_for_card_ready_mbw() + { + /* + * Poll card status + * + * The maximum number of attempts and the delay between two attempts are + * freely chosen. + */ + using namespace Sd_card; + unsigned attempts = 5; + unsigned constexpr attempts_delay_us = 100000; + while (1) { + if (!attempts) { + PERR("Reading card status after multiblock write failed"); + return -1; + } + /* assemble argument register value */ + Send_status::Arg::access_t cmdarg = 0; + Send_status::Arg::Rca::set(cmdarg, _card_info.rca()); + + /* assemble command register value */ + Xfertyp::access_t xfertyp = 0; + Xfertyp::Cmdinx::set(xfertyp, Send_status::INDEX); + Xfertyp::Cicen::set(xfertyp, 1); + Xfertyp::Cccen::set(xfertyp, 1); + Xfertyp::Rsptyp::set(xfertyp, Xfertyp::Rsptyp::_48BIT); + Xfertyp::Msbsel::set(xfertyp, 1); + Xfertyp::Bcen::set(xfertyp, 1); + Xfertyp::Dmaen::set(xfertyp, 1); + + /* send command as soon as the host allows it */ + if (_wait_for_cmd_allowed()) { return -1; } + write(cmdarg); + write(xfertyp); + + /* wait for command completion */ + if (_wait_for_cmd_complete()) { return -1; } + + /* check for errors */ + R1_response_0::access_t const resp = read(); + if (R1_response_0::Error::get(resp)) { + PERR("Reading card status after multiblock write failed"); + return -1; + } + /* if card is in a ready state, return success, retry otherwise */ + if (R1_response_0::card_ready(resp)) { break; } + _delayer.usleep(attempts_delay_us); + } + return 0; + } + /** * Abort transmission by manually issuing stop command * @@ -637,7 +690,12 @@ struct Esdhcv2_controller : private Esdhcv2, public Sd_card::Host_controller Xfertyp::access_t xfertyp = 0; Xfertyp::Cmdinx::set(xfertyp, Sd_card::Stop_transmission::INDEX); Xfertyp::Cmdtyp::set(xfertyp, Xfertyp::Cmdtyp::ABORT_CMD12); - Xfertyp::Rsptyp::set(xfertyp, Xfertyp::Rsptyp::_48BIT); + Xfertyp::Cccen::set(xfertyp, 1); + Xfertyp::Cicen::set(xfertyp, 1); + Xfertyp::Rsptyp::set(xfertyp, Xfertyp::Rsptyp::_48BIT_BUSY); + Xfertyp::Msbsel::set(xfertyp, 1); + Xfertyp::Bcen::set(xfertyp, 1); + Xfertyp::Dmaen::set(xfertyp, 1); write(xfertyp); return _wait_for_cmd_complete(); } @@ -686,6 +744,7 @@ struct Esdhcv2_controller : private Esdhcv2, public Sd_card::Host_controller * manually. */ if (!_abort_transmission()) { return false; } + if (_wait_for_card_ready_mbw()) { return false; } } return true; }