Re: [Qemu-devel] [PATCH v9 10/16] sdhci: check the Spec v1 capabilities correctness

2018-01-23 Thread Alistair Francis
On Mon, Jan 22, 2018 at 6:08 PM, Philippe Mathieu-Daudé  wrote:
> Incorrect value will throw an error.
>
> Note than Spec v2 is supported by default.
>
> Signed-off-by: Philippe Mathieu-Daudé 

Reviewed-by: Alistair Francis 

Alistair

> ---
>  hw/sd/sdhci-internal.h | 21 ++-
>  hw/sd/sdhci.c  | 97 
> +-
>  hw/sd/trace-events |  1 +
>  3 files changed, 117 insertions(+), 2 deletions(-)
>
> diff --git a/hw/sd/sdhci-internal.h b/hw/sd/sdhci-internal.h
> index 577ca9da54..c5e26bf8f3 100644
> --- a/hw/sd/sdhci-internal.h
> +++ b/hw/sd/sdhci-internal.h
> @@ -86,6 +86,9 @@
>
>  /* R/W Host control Register 0x0 */
>  #define SDHC_HOSTCTL   0x28
> +FIELD(SDHC_HOSTCTL, LED_CTRL,  0, 1);
> +FIELD(SDHC_HOSTCTL, DATATRANSFERWIDTH, 1, 1); /* SD mode only */
> +FIELD(SDHC_HOSTCTL, HIGH_SPEED,2, 1);
>  #define SDHC_CTRL_DMA_CHECK_MASK   0x18
>  #define SDHC_CTRL_SDMA 0x00
>  #define SDHC_CTRL_ADMA1_32 0x08
> @@ -96,6 +99,7 @@
>  /* R/W Power Control Register 0x0 */
>  #define SDHC_PWRCON0x29
>  #define SDHC_POWER_ON  (1 << 0)
> +FIELD(SDHC_PWRCON, BUS_VOLTAGE,1, 3);
>
>  /* R/W Block Gap Control Register 0x0 */
>  #define SDHC_BLKGAP0x2A
> @@ -118,6 +122,7 @@
>
>  /* R/W Timeout Control Register 0x0 */
>  #define SDHC_TIMEOUTCON0x2E
> +FIELD(SDHC_TIMEOUTCON, COUNTER,0, 4);
>
>  /* R/W Software Reset Register 0x0 */
>  #define SDHC_SWRST 0x2F
> @@ -174,17 +179,31 @@
>
>  /* ROC Auto CMD12 error status register 0x0 */
>  #define SDHC_ACMD12ERRSTS  0x3C
> +FIELD(SDHC_ACMD12ERRSTS, TIMEOUT_ERR,  1, 1);
> +FIELD(SDHC_ACMD12ERRSTS, CRC_ERR,  2, 1);
> +FIELD(SDHC_ACMD12ERRSTS, INDEX_ERR,4, 1);
>
>  /* HWInit Capabilities Register 0x05E80080 */
>  #define SDHC_CAPAB 0x40
> -#define SDHC_CAN_DO_DMA0x0040
>  #define SDHC_CAN_DO_ADMA2  0x0008
>  #define SDHC_CAN_DO_ADMA1  0x0010
>  #define SDHC_64_BIT_BUS_SUPPORT(1 << 28)
> +FIELD(SDHC_CAPAB, TOCLKFREQ,   0, 6);
> +FIELD(SDHC_CAPAB, TOUNIT,  7, 1);
> +FIELD(SDHC_CAPAB, BASECLKFREQ, 8, 8);
>  FIELD(SDHC_CAPAB, MAXBLOCKLENGTH, 16, 2);
> +FIELD(SDHC_CAPAB, HIGHSPEED,  21, 1);
> +FIELD(SDHC_CAPAB, SDMA,   22, 1);
> +FIELD(SDHC_CAPAB, SUSPRESUME, 23, 1);
> +FIELD(SDHC_CAPAB, V33,24, 1);
> +FIELD(SDHC_CAPAB, V30,25, 1);
> +FIELD(SDHC_CAPAB, V18,26, 1);
>
>  /* HWInit Maximum Current Capabilities Register 0x0 */
>  #define SDHC_MAXCURR   0x48
> +FIELD(SDHC_MAXCURR, V33_VDD1,  0, 8);
> +FIELD(SDHC_MAXCURR, V30_VDD1,  8, 8);
> +FIELD(SDHC_MAXCURR, V18_VDD1, 16, 8);
>
>  /* W Force Event Auto CMD12 Error Interrupt Register 0x */
>  #define SDHC_FEAER 0x50
> diff --git a/hw/sd/sdhci.c b/hw/sd/sdhci.c
> index dce1a49af1..91dfd684d8 100644
> --- a/hw/sd/sdhci.c
> +++ b/hw/sd/sdhci.c
> @@ -23,6 +23,7 @@
>   */
>
>  #include "qemu/osdep.h"
> +#include "qemu/error-report.h"
>  #include "qapi/error.h"
>  #include "hw/hw.h"
>  #include "sysemu/block-backend.h"
> @@ -64,6 +65,92 @@ static inline unsigned int sdhci_get_fifolen(SDHCIState *s)
>  return 1 << (9 + FIELD_EX32(s->capareg, SDHC_CAPAB, MAXBLOCKLENGTH));
>  }
>
> +/* return true on error */
> +static bool sdhci_check_capab_freq_range(SDHCIState *s, const char *desc,
> + uint8_t freq, Error **errp)
> +{
> +switch (freq) {
> +case 0:
> +case 10 ... 63:
> +break;
> +default:
> +error_setg(errp, "SD %s clock frequency can have value"
> +   "in range 0-63 only", desc);
> +return true;
> +}
> +return false;
> +}
> +
> +static void sdhci_check_capareg(SDHCIState *s, Error **errp)
> +{
> +uint64_t msk = s->capareg;
> +uint32_t val;
> +bool y;
> +
> +switch (s->sd_spec_version) {
> +case 2: /* default version */
> +
> +/* fallback */
> +case 1:
> +y = FIELD_EX64(s->capareg, SDHC_CAPAB, TOUNIT);
> +msk = FIELD_DP64(msk, SDHC_CAPAB, TOUNIT, 0);
> +
> +val = FIELD_EX64(s->capareg, SDHC_CAPAB, TOCLKFREQ);
> +trace_sdhci_capareg(y ? "timeout (MHz)" : "Timeout (KHz)", val);
> +if (sdhci_check_capab_freq_range(s, "timeout", val, errp)) {
> +return;
> +}
> +msk = FIELD_DP64(msk, SDHC_CAPAB, TOCLKFREQ, 0);
> +
> +val = FIELD_EX64(s->capareg, SDHC_CAPAB, BASECLKFREQ);
> +trace_sdhci_capareg(y ? "base (MHz)" : "Base (KHz)", val);
> +if (sdhci_check_capab_freq_range(s, "base", val, errp)) {
> +return;
> +}
> +msk = FIELD_DP64(msk, SDHC_CAPAB, BASECLKFREQ, 0);
> +
> +val = FIELD_EX64(s->capareg, SDHC_CA

[Qemu-devel] [PATCH v9 10/16] sdhci: check the Spec v1 capabilities correctness

2018-01-22 Thread Philippe Mathieu-Daudé
Incorrect value will throw an error.

Note than Spec v2 is supported by default.

Signed-off-by: Philippe Mathieu-Daudé 
---
 hw/sd/sdhci-internal.h | 21 ++-
 hw/sd/sdhci.c  | 97 +-
 hw/sd/trace-events |  1 +
 3 files changed, 117 insertions(+), 2 deletions(-)

diff --git a/hw/sd/sdhci-internal.h b/hw/sd/sdhci-internal.h
index 577ca9da54..c5e26bf8f3 100644
--- a/hw/sd/sdhci-internal.h
+++ b/hw/sd/sdhci-internal.h
@@ -86,6 +86,9 @@
 
 /* R/W Host control Register 0x0 */
 #define SDHC_HOSTCTL   0x28
+FIELD(SDHC_HOSTCTL, LED_CTRL,  0, 1);
+FIELD(SDHC_HOSTCTL, DATATRANSFERWIDTH, 1, 1); /* SD mode only */
+FIELD(SDHC_HOSTCTL, HIGH_SPEED,2, 1);
 #define SDHC_CTRL_DMA_CHECK_MASK   0x18
 #define SDHC_CTRL_SDMA 0x00
 #define SDHC_CTRL_ADMA1_32 0x08
@@ -96,6 +99,7 @@
 /* R/W Power Control Register 0x0 */
 #define SDHC_PWRCON0x29
 #define SDHC_POWER_ON  (1 << 0)
+FIELD(SDHC_PWRCON, BUS_VOLTAGE,1, 3);
 
 /* R/W Block Gap Control Register 0x0 */
 #define SDHC_BLKGAP0x2A
@@ -118,6 +122,7 @@
 
 /* R/W Timeout Control Register 0x0 */
 #define SDHC_TIMEOUTCON0x2E
+FIELD(SDHC_TIMEOUTCON, COUNTER,0, 4);
 
 /* R/W Software Reset Register 0x0 */
 #define SDHC_SWRST 0x2F
@@ -174,17 +179,31 @@
 
 /* ROC Auto CMD12 error status register 0x0 */
 #define SDHC_ACMD12ERRSTS  0x3C
+FIELD(SDHC_ACMD12ERRSTS, TIMEOUT_ERR,  1, 1);
+FIELD(SDHC_ACMD12ERRSTS, CRC_ERR,  2, 1);
+FIELD(SDHC_ACMD12ERRSTS, INDEX_ERR,4, 1);
 
 /* HWInit Capabilities Register 0x05E80080 */
 #define SDHC_CAPAB 0x40
-#define SDHC_CAN_DO_DMA0x0040
 #define SDHC_CAN_DO_ADMA2  0x0008
 #define SDHC_CAN_DO_ADMA1  0x0010
 #define SDHC_64_BIT_BUS_SUPPORT(1 << 28)
+FIELD(SDHC_CAPAB, TOCLKFREQ,   0, 6);
+FIELD(SDHC_CAPAB, TOUNIT,  7, 1);
+FIELD(SDHC_CAPAB, BASECLKFREQ, 8, 8);
 FIELD(SDHC_CAPAB, MAXBLOCKLENGTH, 16, 2);
+FIELD(SDHC_CAPAB, HIGHSPEED,  21, 1);
+FIELD(SDHC_CAPAB, SDMA,   22, 1);
+FIELD(SDHC_CAPAB, SUSPRESUME, 23, 1);
+FIELD(SDHC_CAPAB, V33,24, 1);
+FIELD(SDHC_CAPAB, V30,25, 1);
+FIELD(SDHC_CAPAB, V18,26, 1);
 
 /* HWInit Maximum Current Capabilities Register 0x0 */
 #define SDHC_MAXCURR   0x48
+FIELD(SDHC_MAXCURR, V33_VDD1,  0, 8);
+FIELD(SDHC_MAXCURR, V30_VDD1,  8, 8);
+FIELD(SDHC_MAXCURR, V18_VDD1, 16, 8);
 
 /* W Force Event Auto CMD12 Error Interrupt Register 0x */
 #define SDHC_FEAER 0x50
diff --git a/hw/sd/sdhci.c b/hw/sd/sdhci.c
index dce1a49af1..91dfd684d8 100644
--- a/hw/sd/sdhci.c
+++ b/hw/sd/sdhci.c
@@ -23,6 +23,7 @@
  */
 
 #include "qemu/osdep.h"
+#include "qemu/error-report.h"
 #include "qapi/error.h"
 #include "hw/hw.h"
 #include "sysemu/block-backend.h"
@@ -64,6 +65,92 @@ static inline unsigned int sdhci_get_fifolen(SDHCIState *s)
 return 1 << (9 + FIELD_EX32(s->capareg, SDHC_CAPAB, MAXBLOCKLENGTH));
 }
 
+/* return true on error */
+static bool sdhci_check_capab_freq_range(SDHCIState *s, const char *desc,
+ uint8_t freq, Error **errp)
+{
+switch (freq) {
+case 0:
+case 10 ... 63:
+break;
+default:
+error_setg(errp, "SD %s clock frequency can have value"
+   "in range 0-63 only", desc);
+return true;
+}
+return false;
+}
+
+static void sdhci_check_capareg(SDHCIState *s, Error **errp)
+{
+uint64_t msk = s->capareg;
+uint32_t val;
+bool y;
+
+switch (s->sd_spec_version) {
+case 2: /* default version */
+
+/* fallback */
+case 1:
+y = FIELD_EX64(s->capareg, SDHC_CAPAB, TOUNIT);
+msk = FIELD_DP64(msk, SDHC_CAPAB, TOUNIT, 0);
+
+val = FIELD_EX64(s->capareg, SDHC_CAPAB, TOCLKFREQ);
+trace_sdhci_capareg(y ? "timeout (MHz)" : "Timeout (KHz)", val);
+if (sdhci_check_capab_freq_range(s, "timeout", val, errp)) {
+return;
+}
+msk = FIELD_DP64(msk, SDHC_CAPAB, TOCLKFREQ, 0);
+
+val = FIELD_EX64(s->capareg, SDHC_CAPAB, BASECLKFREQ);
+trace_sdhci_capareg(y ? "base (MHz)" : "Base (KHz)", val);
+if (sdhci_check_capab_freq_range(s, "base", val, errp)) {
+return;
+}
+msk = FIELD_DP64(msk, SDHC_CAPAB, BASECLKFREQ, 0);
+
+val = FIELD_EX64(s->capareg, SDHC_CAPAB, MAXBLOCKLENGTH);
+if (val >= 0b11) {
+error_setg(errp, "block size can be 512, 1024 or 2048 only");
+return;
+}
+trace_sdhci_capareg("max block length", sdhci_get_fifolen(s));
+msk = FIELD_DP64(msk, SDHC_CAPAB, MAXBLOCKLENGTH, 0);
+
+val = FIELD_EX64(s->capareg, SDHC_CAPAB, HIGHSPEED);
+