atmel_mci: Fix data timeout value

Calculate the data timeout based on values from the CSD instead of
just using a hardcoded DTOR value. This is a backport of a similar fix
in BSP 2.0, with one additional fix: the DTOCYC value is rounded up
instead of down.

Signed-off-by: Haavard Skinnemoen <hskinnemoen@atmel.com>
diff --git a/cpu/at32ap/atmel_mci.c b/cpu/at32ap/atmel_mci.c
index bdca1c1..cf48be1 100644
--- a/cpu/at32ap/atmel_mci.c
+++ b/cpu/at32ap/atmel_mci.c
@@ -56,6 +56,7 @@
 #define MMC_DEFAULT_RCA		1
 
 static unsigned int mmc_rca;
+static int mmc_card_is_sd;
 static block_dev_desc_t mmc_blkdev;
 
 block_dev_desc_t *mmc_get_dev(int dev)
@@ -373,6 +374,7 @@
 	mmc_rca = resp[0] >> 16;
 	if (verbose)
 		printf("SD Card detected (RCA %u)\n", mmc_rca);
+	mmc_card_is_sd = 1;
 	return 0;
 }
 
@@ -407,6 +409,59 @@
 	return ret;
 }
 
+static void mci_set_data_timeout(struct mmc_csd *csd)
+{
+	static const unsigned int dtomul_to_shift[] = {
+		0, 4, 7, 8, 10, 12, 16, 20,
+	};
+	static const unsigned int taac_exp[] = {
+		1, 10, 100, 1000, 10000, 100000, 1000000, 10000000,
+	};
+	static const unsigned int taac_mant[] = {
+		0,  10, 12, 13, 15, 60, 25, 30,
+		35, 40, 45, 50, 55, 60, 70, 80,
+	};
+	unsigned int timeout_ns, timeout_clks;
+	unsigned int e, m;
+	unsigned int dtocyc, dtomul;
+	unsigned int shift;
+	u32 dtor;
+
+	e = csd->taac & 0x07;
+	m = (csd->taac >> 3) & 0x0f;
+
+	timeout_ns = (taac_exp[e] * taac_mant[m] + 9) / 10;
+	timeout_clks = csd->nsac * 100;
+
+	timeout_clks += (((timeout_ns + 9) / 10)
+			 * ((CFG_MMC_CLK_PP + 99999) / 100000) + 9999) / 10000;
+	if (!mmc_card_is_sd)
+		timeout_clks *= 10;
+	else
+		timeout_clks *= 100;
+
+	dtocyc = timeout_clks;
+	dtomul = 0;
+	while (dtocyc > 15 && dtomul < 8) {
+		dtomul++;
+		shift = dtomul_to_shift[dtomul];
+		dtocyc = (timeout_clks + (1 << shift) - 1) >> shift;
+	}
+
+	if (dtomul >= 8) {
+		dtomul = 7;
+		dtocyc = 15;
+		puts("Warning: Using maximum data timeout\n");
+	}
+
+	dtor = (MMCI_BF(DTOMUL, dtomul)
+		| MMCI_BF(DTOCYC, dtocyc));
+	mmci_writel(DTOR, dtor);
+
+	printf("mmc: Using %u cycles data timeout (DTOR=0x%x)\n",
+	       dtocyc << shift, dtor);
+}
+
 int mmc_init(int verbose)
 {
 	struct mmc_cid cid;
@@ -421,6 +476,8 @@
 	mmci_writel(IDR, ~0UL);
 	mci_set_mode(CFG_MMC_CLK_OD, MMC_DEFAULT_BLKLEN);
 
+	mmc_card_is_sd = 0;
+
 	ret = sd_init_card(&cid, verbose);
 	if (ret) {
 		mmc_rca = MMC_DEFAULT_RCA;
@@ -436,6 +493,8 @@
 	if (verbose)
 		mmc_dump_csd(&csd);
 
+	mci_set_data_timeout(&csd);
+
 	/* Initialize the blockdev structure */
 	mmc_blkdev.if_type = IF_TYPE_MMC;
 	mmc_blkdev.part_type = PART_TYPE_DOS;