2014-11-13 12:57:00

by Kishon Vijay Abraham I

[permalink] [raw]
Subject: [RFC PATCH 0/3] DRA72: MMC HS200 support

Added HS200 to improve EMMC throughput for dra72.

With HS200
==============
Read throughput
./dd if=/dev/mmcblk0 of=/dev/null bs=1M count=100 oflag=sync
100+0 records in
100+0 records out
104857600 bytes (105 MB) copied, 1.46028 s, 71.8 MB/s

Write throughput
root@dra7xx-evm:/# ./dd if=/dev/zero of=/dev/mmcblk0 bs=1M count=100 oflag=sync
100+0 records in
100+0 records out
104857600 bytes (105 MB) copied, 9.34884 s, 11.2 MB/s


WithOUT HS200
==============
Read throughput
./dd if=/dev/mmcblk0 of=/dev/null bs=1M count=100 oflag=sync
100+0 records in
100+0 records out
104857600 bytes (105 MB) copied, 2.6423 s, 39.7 MB/s

Write throughput
./dd if=/dev/zero of=/dev/mmcblk0 bs=1M count=100 oflag=sync
100+0 records in
100+0 records out
104857600 bytes (105 MB) copied, 9.3556 s, 11.2 MB/s

Balaji T K (1):
mmc: omap_hsmmc: add tuning support

Kishon Vijay Abraham I (1):
ARM: dts: dra72-evm: Set the max-frequency to 192MHz

Viswanath Puttagunta (1):
mmc: omap_hsmmc: set host capabilities by reading MMCHS_CAPA2
register

arch/arm/boot/dts/dra72-evm.dts | 1 +
drivers/mmc/host/omap_hsmmc.c | 342 ++++++++++++++++++++++++++++++++++++++-
2 files changed, 341 insertions(+), 2 deletions(-)

--
1.7.9.5


2014-11-13 12:57:08

by Kishon Vijay Abraham I

[permalink] [raw]
Subject: [RFC PATCH 3/3] ARM: dts: dra72-evm: Set the max-frequency to 192MHz

Set the maximum operating frequency of MMC2 to 192MHz.

Signed-off-by: Kishon Vijay Abraham I <[email protected]>
---
arch/arm/boot/dts/dra72-evm.dts | 1 +
1 file changed, 1 insertion(+)

diff --git a/arch/arm/boot/dts/dra72-evm.dts b/arch/arm/boot/dts/dra72-evm.dts
index abbaaa7..5cc1110 100644
--- a/arch/arm/boot/dts/dra72-evm.dts
+++ b/arch/arm/boot/dts/dra72-evm.dts
@@ -364,6 +364,7 @@
vmmc-supply = <&evm_3v3>;
bus-width = <8>;
ti,non-removable;
+ max-frequency = <192000000>;
};

&dra7_pmx_core {
--
1.7.9.5

2014-11-13 12:57:37

by Kishon Vijay Abraham I

[permalink] [raw]
Subject: [RFC PATCH 2/3] mmc: omap_hsmmc: add tuning support

From: Balaji T K <[email protected]>

MMC tuning procedure is required to support SD card
UHS1-SDR104 mode and EMMC HS200 mode.

The tuning function omap_execute_tuning() will only
be called by the MMC/SD core if the corresponding
speed modes are supported by the OMAP silicon which
is set in the mmc host "caps" field.

Signed-off-by: Viswanath Puttagunta <[email protected]>
Signed-off-by: Sourav Poddar <[email protected]>
[ [email protected] : Set the functional clock to 192MHz if the contoller
supports HS200 ]
Signed-off-by: Kishon Vijay Abraham I <[email protected]>
---
drivers/mmc/host/omap_hsmmc.c | 325 ++++++++++++++++++++++++++++++++++++++++-
1 file changed, 322 insertions(+), 3 deletions(-)

diff --git a/drivers/mmc/host/omap_hsmmc.c b/drivers/mmc/host/omap_hsmmc.c
index 2e42ed3..675bd31d 100644
--- a/drivers/mmc/host/omap_hsmmc.c
+++ b/drivers/mmc/host/omap_hsmmc.c
@@ -22,6 +22,7 @@
#include <linux/dmaengine.h>
#include <linux/seq_file.h>
#include <linux/sizes.h>
+#include <linux/slab.h>
#include <linux/interrupt.h>
#include <linux/delay.h>
#include <linux/dma-mapping.h>
@@ -47,6 +48,7 @@
/* OMAP HSMMC Host Controller Registers */
#define OMAP_HSMMC_SYSSTATUS 0x0014
#define OMAP_HSMMC_CON 0x002C
+#define OMAP_HSMMC_DLL 0x0034
#define OMAP_HSMMC_SDMASA 0x0100
#define OMAP_HSMMC_BLK 0x0104
#define OMAP_HSMMC_ARG 0x0108
@@ -100,6 +102,7 @@
#define CLKEXTFREE (1 << 16)
#define CTPL (1 << 11)
#define DW8 (1 << 5)
+#define BRR (1 << 5)
#define OD 0x1
#define STAT_CLEAR 0xFFFFFFFF
#define INIT_STREAM_CMD 0x00000000
@@ -129,6 +132,20 @@
#define CERR_EN (1 << 28)
#define BADA_EN (1 << 29)

+#define V1V8_SIGEN (1 << 19)
+#define AC12_SCLK_SEL (1 << 23)
+#define AC12_UHSMC_MASK (7 << 16)
+#define AC12_UHSMC_SDR50 (2 << 16)
+#define AC12_UHSMC_SDR104 (3 << 16)
+#define DLL_LOCK (1 << 0)
+#define DLL_CALIB (1 << 1)
+#define DLL_UNLOCK_STICKY (1 << 2)
+#define DLL_SWT (1 << 20)
+#define DLL_FORCE_SR_C_MASK (0x7F << 13)
+#define DLL_FORCE_SR_C_SHIFT 13
+#define DLL_FORCE_VALUE (1 << 12)
+#define DLL_RESET (1 << 31)
+
#define INT_EN_MASK (BADA_EN | CERR_EN | ACE_EN | DEB_EN | DCRC_EN |\
DTO_EN | CIE_EN | CEB_EN | CCRC_EN | CTO_EN | \
BRR_EN | BWR_EN | TC_EN | CC_EN)
@@ -143,18 +160,23 @@
#define SDR50 (1 << 0)
#define SDR104 (1 << 1)
#define DDR50 (1 << 2)
+#define CAPA2_TSDR50 (1 << 13)

#define MMC_AUTOSUSPEND_DELAY 100
#define MMC_TIMEOUT_MS 20 /* 20 mSec */
#define MMC_TIMEOUT_US 20000 /* 20000 micro Sec */
#define OMAP_MMC_MIN_CLOCK 400000
#define OMAP_MMC_MAX_CLOCK 52000000
+#define MAX_PHASE_DELAY 0x7F
#define DRIVER_NAME "omap_hsmmc"

#define VDD_1V8 1800000 /* 180000 uV */
#define VDD_3V0 3000000 /* 300000 uV */
#define VDD_165_195 (ffs(MMC_VDD_165_195) - 1)

+#define EMMC_HSDDR_SD_SDR25_MAX 52000000
+#define SD_SDR50_MAX_FREQ 104000000
+
/*
* One controller can have multiple slots, like on some omap boards using
* omap.c controller driver. Luckily this is not currently done on any known
@@ -198,6 +220,7 @@ struct omap_hsmmc_host {
void __iomem *base;
resource_size_t mapbase;
spinlock_t irq_lock; /* Prevent races with irq handler */
+ struct completion buf_ready;
unsigned int dma_len;
unsigned int dma_sg_idx;
unsigned char bus_mode;
@@ -224,6 +247,13 @@ struct omap_hsmmc_host {
#define AUTO_CMD23 (1 << 0) /* Auto CMD23 support */
#define HSMMC_SDIO_IRQ_ENABLED (1 << 1) /* SDIO irq enabled */
#define HSMMC_WAKE_IRQ_ENABLED (1 << 2)
+
+ u32 *tuning_data;
+ int tuning_size;
+ int tuning_done;
+ int tuning_fsrc;
+ u32 tuning_uhsmc;
+ u32 tuning_opcode;
struct omap_hsmmc_next next_data;
struct omap_mmc_platform_data *pdata;
};
@@ -233,6 +263,48 @@ struct omap_mmc_of_data {
u8 controller_flags;
};

+static const u32 ref_tuning_4bits[] = {
+ 0x00FF0FFF, 0xCCC3CCFF, 0xFFCC3CC3, 0xEFFEFFFE,
+ 0xDDFFDFFF, 0xFBFFFBFF, 0xFF7FFFBF, 0xEFBDF777,
+ 0xF0FFF0FF, 0x3CCCFC0F, 0xCFCC33CC, 0xEEFFEFFF,
+ 0xFDFFFDFF, 0xFFBFFFDF, 0xFFF7FFBB, 0xDE7B7FF7
+};
+
+static const u32 ref_tuning_8bits[] = {
+ 0xFF00FFFF, 0x0000FFFF, 0xCCCCFFFF, 0xCCCC33CC,
+ 0xCC3333CC, 0xFFFFCCCC, 0xFFFFEEFF, 0xFFEEEEFF,
+ 0xFFDDFFFF, 0xDDDDFFFF, 0xBBFFFFFF, 0xBBFFFFFF,
+ 0xFFFFFFBB, 0XFFFFFF77, 0x77FF7777, 0xFFEEDDBB,
+ 0x00FFFFFF, 0x00FFFFFF, 0xCCFFFF00, 0xCC33CCCC,
+ 0x3333CCCC, 0xFFCCCCCC, 0xFFEEFFFF, 0xEEEEFFFF,
+ 0xDDFFFFFF, 0xDDFFFFFF, 0xFFFFFFDD, 0XFFFFFFBB,
+ 0xFFFFBBBB, 0xFFFF77FF, 0xFF7777FF, 0xEEDDBB77
+};
+
+static inline int omap_hsmmc_set_dll(struct omap_hsmmc_host *host, int count)
+{
+ int i;
+ u32 dll;
+
+ dll = OMAP_HSMMC_READ(host->base, DLL);
+ dll &= ~(DLL_FORCE_SR_C_MASK);
+ dll &= ~DLL_CALIB;
+ dll |= (count << DLL_FORCE_SR_C_SHIFT);
+ OMAP_HSMMC_WRITE(host->base, DLL, dll);
+ dll |= DLL_FORCE_VALUE;
+ OMAP_HSMMC_WRITE(host->base, DLL, dll);
+ dll |= DLL_CALIB;
+ OMAP_HSMMC_WRITE(host->base, DLL, dll);
+ for (i = 0; i < 1000; i++) {
+ if (OMAP_HSMMC_READ(host->base, DLL) & DLL_CALIB)
+ break;
+ }
+ dll &= ~DLL_CALIB;
+ dll = OMAP_HSMMC_READ(host->base, DLL);
+
+ return 0;
+}
+
static void omap_hsmmc_start_dma_transfer(struct omap_hsmmc_host *host);

static int omap_hsmmc_card_detect(struct device *dev, int slot)
@@ -531,7 +603,10 @@ static void omap_hsmmc_enable_irq(struct omap_hsmmc_host *host,
u32 irq_mask = INT_EN_MASK;
unsigned long flags;

- if (host->use_dma)
+ if ((cmd->opcode == MMC_SEND_TUNING_BLOCK) ||
+ (cmd->opcode == MMC_SEND_TUNING_BLOCK_HS200))
+ irq_mask |= BRR_EN;
+ else if (host->use_dma)
irq_mask &= ~(BRR_EN | BWR_EN);

/* Disable timeout for erases */
@@ -578,6 +653,33 @@ static u16 calc_divisor(struct omap_hsmmc_host *host, struct mmc_ios *ios)
return dsor;
}

+static inline int omap_hsmmc_restore_dll(struct omap_hsmmc_host *host)
+{
+ u32 ac12;
+ u32 dll;
+
+ ac12 = OMAP_HSMMC_READ(host->base, AC12);
+ ac12 |= host->tuning_uhsmc;
+ OMAP_HSMMC_WRITE(host->base, AC12, ac12);
+
+ dll = OMAP_HSMMC_READ(host->base, DLL);
+ dll |= DLL_FORCE_VALUE;
+ OMAP_HSMMC_WRITE(host->base, DLL, dll);
+
+ if (omap_hsmmc_set_dll(host, host->tuning_fsrc))
+ return -EIO;
+ return 0;
+}
+
+static inline void omap_hsmmc_save_dll(struct omap_hsmmc_host *host)
+{
+ u32 ac12;
+
+ ac12 = OMAP_HSMMC_READ(host->base, AC12);
+ ac12 &= ~AC12_UHSMC_MASK;
+ OMAP_HSMMC_WRITE(host->base, AC12, ac12);
+}
+
static void omap_hsmmc_set_clock(struct omap_hsmmc_host *host)
{
struct mmc_ios *ios = &host->mmc->ios;
@@ -589,6 +691,9 @@ static void omap_hsmmc_set_clock(struct omap_hsmmc_host *host)

omap_hsmmc_stop_clock(host);

+ if (host->mmc->caps2 & MMC_CAP2_HS200)
+ clk_set_rate(host->fclk, 192000000);
+
regval = OMAP_HSMMC_READ(host->base, SYSCTL);
regval = regval & ~(CLKD_MASK | DTO_MASK);
clkdiv = calc_divisor(host, ios);
@@ -667,7 +772,6 @@ static void omap_hsmmc_set_bus_mode(struct omap_hsmmc_host *host)
}

#ifdef CONFIG_PM
-
/*
* Restore the MMC host context, if it was lost as result of a
* power state change.
@@ -878,6 +982,12 @@ omap_hsmmc_start_command(struct omap_hsmmc_host *host, struct mmc_command *cmd,
if (host->use_dma)
cmdreg |= DMAE;

+ /* Tuning command is special. Data Present Select should be set */
+ if ((cmd->opcode == MMC_SEND_TUNING_BLOCK) ||
+ (cmd->opcode == MMC_SEND_TUNING_BLOCK_HS200)) {
+ cmdreg = (cmd->opcode << 24) | (resptype << 16) |
+ (cmdtype << 22) | DP_SELECT | DDIR;
+ }
host->req_in_progress = 1;

OMAP_HSMMC_WRITE(host->base, ARG, cmd->arg);
@@ -965,6 +1075,9 @@ omap_hsmmc_cmd_done(struct omap_hsmmc_host *host, struct mmc_command *cmd)
return;
}

+ if (host->cmd->opcode == MMC_SEND_TUNING_BLOCK)
+ return;
+
host->cmd = NULL;

if (cmd->flags & MMC_RSP_PRESENT) {
@@ -1104,6 +1217,7 @@ static void omap_hsmmc_do_irq(struct omap_hsmmc_host *host, int status)
struct mmc_data *data;
int end_cmd = 0, end_trans = 0;
int error = 0;
+ int i = 0;

data = host->data;
dev_vdbg(mmc_dev(host->mmc), "IRQ Status is %x\n", status);
@@ -1139,6 +1253,15 @@ static void omap_hsmmc_do_irq(struct omap_hsmmc_host *host, int status)
}

OMAP_HSMMC_WRITE(host->base, STAT, status);
+
+ if (status & BRR_EN) {
+ for (i = 0; i < host->tuning_size/4; i++)
+ host->tuning_data[i] =
+ OMAP_HSMMC_READ(host->base, DATA);
+ complete(&host->buf_ready);
+ return;
+ }
+
if (end_cmd || ((status & CC_EN) && host->cmd))
omap_hsmmc_cmd_done(host, host->cmd);
if ((end_trans || (status & TC_EN)) && host->mrq)
@@ -1844,6 +1967,189 @@ static int omap_hsmmc_multi_io_quirk(struct mmc_card *card,
return blk_size;
}

+static int omap_execute_tuning(struct mmc_host *mmc, u32 opcode)
+{
+ struct omap_hsmmc_host *host;
+ struct mmc_ios *ios = &mmc->ios;
+ const u32 *tuning_ref;
+ int phase_delay = 0;
+ int err = 0;
+ int count = 0;
+ int length = 0;
+ int note_index = 0xFF;
+ int max_index = 0;
+ int max_window = 0;
+ bool previous_match = false;
+ bool current_match;
+ u32 ac12, capa2, dll = 0;
+
+ host = mmc_priv(mmc);
+ switch (ios->bus_width) {
+ case MMC_BUS_WIDTH_8:
+ tuning_ref = ref_tuning_8bits;
+ host->tuning_size = sizeof(ref_tuning_8bits);
+ break;
+ case MMC_BUS_WIDTH_4:
+ tuning_ref = ref_tuning_4bits;
+ host->tuning_size = sizeof(ref_tuning_4bits);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ host->tuning_data = kzalloc(host->tuning_size, GFP_KERNEL);
+ if (!host->tuning_data)
+ return -ENOMEM;
+
+ host->tuning_done = 0;
+ /* clock tuning is not needed for upto 52MHz */
+ if (ios->clock <= EMMC_HSDDR_SD_SDR25_MAX)
+ return 0;
+
+ omap_hsmmc_stop_clock(host);
+
+ ac12 = OMAP_HSMMC_READ(host->base, AC12);
+ capa2 = OMAP_HSMMC_READ(host->base, CAPA2);
+
+ ac12 &= ~AC12_UHSMC_MASK;
+ OMAP_HSMMC_WRITE(host->base, AC12, ac12);
+
+ /*
+ * Host Controller needs tuning only in case of SDR104 mode
+ * and for SDR50 mode when Use Tuning for SDR50 is set in
+ * Capabilities register.
+ */
+ if (ios->clock <= SD_SDR50_MAX_FREQ) {
+ if (!(capa2 & CAPA2_TSDR50))
+ return 0;
+ ac12 |= AC12_UHSMC_SDR50;
+ } else {
+ ac12 |= AC12_UHSMC_SDR104;
+ }
+
+ ac12 |= AC12_UHSMC_SDR104;
+ ac12 |= V1V8_SIGEN;
+
+ /* Enable SDR50/SDR104 mode */
+ OMAP_HSMMC_WRITE(host->base, AC12, ac12);
+ omap_hsmmc_start_clock(host);
+
+ /* Start software tuning Procedure */
+ dll |= DLL_SWT;
+ OMAP_HSMMC_WRITE(host->base, DLL, dll);
+
+ while (phase_delay < MAX_PHASE_DELAY) {
+ struct mmc_command cmd = {0};
+ struct mmc_request mrq = {0};
+
+ if (phase_delay > MAX_PHASE_DELAY)
+ break;
+
+ omap_hsmmc_set_dll(host, phase_delay);
+
+ cmd.opcode = opcode;
+ cmd.arg = 0;
+ cmd.flags = MMC_RSP_R1 | MMC_CMD_ADTC;
+ cmd.retries = 0;
+ cmd.data = NULL;
+ cmd.error = 0;
+
+ mrq.cmd = &cmd;
+ host->mrq = &mrq;
+
+ OMAP_HSMMC_WRITE(host->base, BLK, host->tuning_size);
+ set_data_timeout(host, 50000000, 0);
+ omap_hsmmc_start_command(host, &cmd, NULL);
+
+ host->cmd = NULL;
+ host->mrq = NULL;
+
+ /* Wait for Buffer Read Ready interrupt */
+ err = wait_for_completion_timeout(&host->buf_ready,
+ msecs_to_jiffies(5000));
+ omap_hsmmc_disable_irq(host);
+ host->req_in_progress = 0;
+
+ if (err == 0) {
+ dev_err(mmc_dev(host->mmc),
+ "Tuning BRR timeout. phase_delay=%x",
+ phase_delay);
+ err = -ETIMEDOUT;
+ goto tuning_error;
+ }
+
+ current_match = true;
+ if (memcmp(host->tuning_data, tuning_ref, host->tuning_size))
+ current_match = false;
+ else
+ current_match = true;
+
+ if (current_match == true) {
+ if (previous_match == false) {
+ /* new window */
+ note_index = count;
+ length = 1;
+ } else {
+ length++;
+ }
+ previous_match = true;
+ if (length > max_window) {
+ max_index = note_index;
+ max_window = length;
+ }
+ } else {
+ previous_match = false;
+ }
+ phase_delay += 4;
+ count++;
+ }
+
+ if (!max_window) {
+ dev_err(mmc_dev(host->mmc), "Unable to find match\n");
+ err = -EIO;
+ goto tuning_error;
+ }
+
+ ac12 = OMAP_HSMMC_READ(host->base, AC12);
+ if (!(ac12 & AC12_SCLK_SEL)) {
+ err = -EIO;
+ goto tuning_error;
+ }
+
+ dll = OMAP_HSMMC_READ(host->base, DLL);
+ dll &= ~DLL_SWT;
+ OMAP_HSMMC_WRITE(host->base, DLL, dll);
+ count = 4 * (max_index + (max_window >> 1));
+ if (omap_hsmmc_set_dll(host, count)) {
+ err = -EIO;
+ goto tuning_error;
+ }
+ host->tuning_fsrc = count;
+ host->tuning_uhsmc = (OMAP_HSMMC_READ(host->base, AC12)
+ & AC12_UHSMC_MASK);
+ host->tuning_opcode = opcode;
+ host->tuning_done = 1;
+ omap_hsmmc_reset_controller_fsm(host, SRD);
+ omap_hsmmc_reset_controller_fsm(host, SRC);
+
+ return 0;
+
+tuning_error:
+ dev_err(mmc_dev(host->mmc),
+ "Tuning failed. Using fixed sampling clock\n");
+ ac12 = OMAP_HSMMC_READ(host->base, AC12);
+ ac12 &= ~(AC12_UHSMC_MASK | AC12_SCLK_SEL);
+ OMAP_HSMMC_WRITE(host->base, AC12, ac12);
+
+ dll = OMAP_HSMMC_READ(host->base, DLL);
+ dll &= ~(DLL_FORCE_VALUE | DLL_SWT);
+ OMAP_HSMMC_WRITE(host->base, DLL, dll);
+
+ omap_hsmmc_reset_controller_fsm(host, SRD);
+ omap_hsmmc_reset_controller_fsm(host, SRC);
+ return err;
+}
+
static struct mmc_host_ops omap_hsmmc_ops = {
.enable = omap_hsmmc_enable_fclk,
.disable = omap_hsmmc_disable_fclk,
@@ -1855,6 +2161,7 @@ static struct mmc_host_ops omap_hsmmc_ops = {
.get_ro = omap_hsmmc_get_ro,
.init_card = omap_hsmmc_init_card,
.enable_sdio_irq = omap_hsmmc_enable_sdio_irq,
+ .execute_tuning = omap_execute_tuning,
};

#ifdef CONFIG_DEBUG_FS
@@ -2107,6 +2414,7 @@ static int omap_hsmmc_probe(struct platform_device *pdev)
mmc->f_max = OMAP_MMC_MAX_CLOCK;

spin_lock_init(&host->irq_lock);
+ init_completion(&host->buf_ready);

host->fclk = devm_clk_get(&pdev->dev, "fck");
if (IS_ERR(host->fclk)) {
@@ -2159,7 +2467,7 @@ static int omap_hsmmc_probe(struct platform_device *pdev)

mmc->pm_caps = mmc_slot(host).pm_caps;

- reg = OMAP_HSMMC_READ(host->base, OMAP_HSMMC_CAPA2);
+ reg = OMAP_HSMMC_READ(host->base, CAPA2);

if (reg & SDR50)
mmc->caps |= MMC_CAP_UHS_DDR50;
@@ -2172,6 +2480,9 @@ static int omap_hsmmc_probe(struct platform_device *pdev)
if (reg & DDR50)
mmc->caps |= MMC_CAP_UHS_DDR50;

+ mmc->caps |= MMC_CAP_DRIVER_TYPE_A | MMC_CAP_DRIVER_TYPE_C |
+ MMC_CAP_DRIVER_TYPE_D;
+
omap_hsmmc_conf_bus_power(host);

if (!pdev->dev.of_node) {
@@ -2380,6 +2691,7 @@ static int omap_hsmmc_suspend(struct device *dev)
OMAP_HSMMC_WRITE(host->base, HCTL,
OMAP_HSMMC_READ(host->base, HCTL) & ~SDBP);
}
+ host->tuning_done = 0;

/* do not wake up due to sdio irq */
if ((host->mmc->caps & MMC_CAP_SDIO_IRQ) &&
@@ -2434,6 +2746,9 @@ static int omap_hsmmc_runtime_suspend(struct device *dev)
int ret = 0;

host = platform_get_drvdata(to_platform_device(dev));
+ if (host->tuning_done)
+ omap_hsmmc_restore_dll(host);
+
omap_hsmmc_context_save(host);
dev_dbg(dev, "disabled\n");

@@ -2480,6 +2795,10 @@ static int omap_hsmmc_runtime_resume(struct device *dev)

host = platform_get_drvdata(to_platform_device(dev));
omap_hsmmc_context_restore(host);
+
+ if (host->tuning_done)
+ omap_hsmmc_restore_dll(host);
+
dev_dbg(dev, "enabled\n");

spin_lock_irqsave(&host->irq_lock, flags);
--
1.7.9.5

2014-11-13 12:58:34

by Kishon Vijay Abraham I

[permalink] [raw]
Subject: [RFC PATCH 1/3] mmc: omap_hsmmc: set host capabilities by reading MMCHS_CAPA2 register

From: Viswanath Puttagunta <[email protected]>

set SDR104, SDR50, DDR50 and HS200 capability flags to caps/caps2 by reading
MMCHS_CAPA2 register.

Signed-off-by: Viswanath Puttagunta <[email protected]>
Signed-off-by: Sourav Poddar <[email protected]>
Signed-off-by: Kishon Vijay Abraham I <[email protected]>
Suggested-by: Misael Lopez Cruz <[email protected]>
---
drivers/mmc/host/omap_hsmmc.c | 19 +++++++++++++++++++
1 file changed, 19 insertions(+)

diff --git a/drivers/mmc/host/omap_hsmmc.c b/drivers/mmc/host/omap_hsmmc.c
index df27bb4..2e42ed3 100644
--- a/drivers/mmc/host/omap_hsmmc.c
+++ b/drivers/mmc/host/omap_hsmmc.c
@@ -64,6 +64,7 @@
#define OMAP_HSMMC_ISE 0x0138
#define OMAP_HSMMC_AC12 0x013C
#define OMAP_HSMMC_CAPA 0x0140
+#define OMAP_HSMMC_CAPA2 0x0144

#define VS18 (1 << 26)
#define VS30 (1 << 25)
@@ -139,6 +140,10 @@
#define ACTO (1 << 1)
#define ACNE (1 << 0)

+#define SDR50 (1 << 0)
+#define SDR104 (1 << 1)
+#define DDR50 (1 << 2)
+
#define MMC_AUTOSUSPEND_DELAY 100
#define MMC_TIMEOUT_MS 20 /* 20 mSec */
#define MMC_TIMEOUT_US 20000 /* 20000 micro Sec */
@@ -2028,6 +2033,7 @@ static int omap_hsmmc_probe(struct platform_device *pdev)
unsigned tx_req, rx_req;
const struct omap_mmc_of_data *data;
void __iomem *base;
+ u32 reg;

match = of_match_device(of_match_ptr(omap_mmc_of_match), &pdev->dev);
if (match) {
@@ -2153,6 +2159,19 @@ static int omap_hsmmc_probe(struct platform_device *pdev)

mmc->pm_caps = mmc_slot(host).pm_caps;

+ reg = OMAP_HSMMC_READ(host->base, OMAP_HSMMC_CAPA2);
+
+ if (reg & SDR50)
+ mmc->caps |= MMC_CAP_UHS_DDR50;
+
+ if (reg & SDR104) {
+ mmc->caps |= MMC_CAP_UHS_SDR104;
+ mmc->caps2 |= MMC_CAP2_HS200;
+ }
+
+ if (reg & DDR50)
+ mmc->caps |= MMC_CAP_UHS_DDR50;
+
omap_hsmmc_conf_bus_power(host);

if (!pdev->dev.of_node) {
--
1.7.9.5

2014-11-14 09:06:38

by Andreas Fenkart

[permalink] [raw]
Subject: Re: [RFC PATCH 2/3] mmc: omap_hsmmc: add tuning support

2014-11-13 13:56 GMT+01:00 Kishon Vijay Abraham I <[email protected]>:
> From: Balaji T K <[email protected]>
>
> MMC tuning procedure is required to support SD card
> UHS1-SDR104 mode and EMMC HS200 mode.
>
> The tuning function omap_execute_tuning() will only
> be called by the MMC/SD core if the corresponding
> speed modes are supported by the OMAP silicon which
> is set in the mmc host "caps" field.
>
> Signed-off-by: Viswanath Puttagunta <[email protected]>
> Signed-off-by: Sourav Poddar <[email protected]>
> [ [email protected] : Set the functional clock to 192MHz if the contoller
> supports HS200 ]
> Signed-off-by: Kishon Vijay Abraham I <[email protected]>
> ---
> drivers/mmc/host/omap_hsmmc.c | 325 ++++++++++++++++++++++++++++++++++++++++-
> 1 file changed, 322 insertions(+), 3 deletions(-)
>
> diff --git a/drivers/mmc/host/omap_hsmmc.c b/drivers/mmc/host/omap_hsmmc.c
> index 2e42ed3..675bd31d 100644
> --- a/drivers/mmc/host/omap_hsmmc.c
> +++ b/drivers/mmc/host/omap_hsmmc.c
> @@ -22,6 +22,7 @@
> #include <linux/dmaengine.h>
> #include <linux/seq_file.h>
> #include <linux/sizes.h>
> +#include <linux/slab.h>
> #include <linux/interrupt.h>
> #include <linux/delay.h>
> #include <linux/dma-mapping.h>
> @@ -47,6 +48,7 @@
> /* OMAP HSMMC Host Controller Registers */
> #define OMAP_HSMMC_SYSSTATUS 0x0014
> #define OMAP_HSMMC_CON 0x002C
> +#define OMAP_HSMMC_DLL 0x0034
> #define OMAP_HSMMC_SDMASA 0x0100
> #define OMAP_HSMMC_BLK 0x0104
> #define OMAP_HSMMC_ARG 0x0108
> @@ -100,6 +102,7 @@
> #define CLKEXTFREE (1 << 16)
> #define CTPL (1 << 11)
> #define DW8 (1 << 5)
> +#define BRR (1 << 5)
> #define OD 0x1
> #define STAT_CLEAR 0xFFFFFFFF
> #define INIT_STREAM_CMD 0x00000000
> @@ -129,6 +132,20 @@
> #define CERR_EN (1 << 28)
> #define BADA_EN (1 << 29)
>
> +#define V1V8_SIGEN (1 << 19)
> +#define AC12_SCLK_SEL (1 << 23)
> +#define AC12_UHSMC_MASK (7 << 16)
> +#define AC12_UHSMC_SDR50 (2 << 16)
> +#define AC12_UHSMC_SDR104 (3 << 16)
> +#define DLL_LOCK (1 << 0)
> +#define DLL_CALIB (1 << 1)
> +#define DLL_UNLOCK_STICKY (1 << 2)
> +#define DLL_SWT (1 << 20)
> +#define DLL_FORCE_SR_C_MASK (0x7F << 13)
> +#define DLL_FORCE_SR_C_SHIFT 13
> +#define DLL_FORCE_VALUE (1 << 12)
> +#define DLL_RESET (1 << 31)
> +
> #define INT_EN_MASK (BADA_EN | CERR_EN | ACE_EN | DEB_EN | DCRC_EN |\
> DTO_EN | CIE_EN | CEB_EN | CCRC_EN | CTO_EN | \
> BRR_EN | BWR_EN | TC_EN | CC_EN)
> @@ -143,18 +160,23 @@
> #define SDR50 (1 << 0)
> #define SDR104 (1 << 1)
> #define DDR50 (1 << 2)
> +#define CAPA2_TSDR50 (1 << 13)
>
> #define MMC_AUTOSUSPEND_DELAY 100
> #define MMC_TIMEOUT_MS 20 /* 20 mSec */
> #define MMC_TIMEOUT_US 20000 /* 20000 micro Sec */
> #define OMAP_MMC_MIN_CLOCK 400000
> #define OMAP_MMC_MAX_CLOCK 52000000
> +#define MAX_PHASE_DELAY 0x7F
> #define DRIVER_NAME "omap_hsmmc"
>
> #define VDD_1V8 1800000 /* 180000 uV */
> #define VDD_3V0 3000000 /* 300000 uV */
> #define VDD_165_195 (ffs(MMC_VDD_165_195) - 1)
>
> +#define EMMC_HSDDR_SD_SDR25_MAX 52000000
> +#define SD_SDR50_MAX_FREQ 104000000
> +
> /*
> * One controller can have multiple slots, like on some omap boards using
> * omap.c controller driver. Luckily this is not currently done on any known
> @@ -198,6 +220,7 @@ struct omap_hsmmc_host {
> void __iomem *base;
> resource_size_t mapbase;
> spinlock_t irq_lock; /* Prevent races with irq handler */
> + struct completion buf_ready;
> unsigned int dma_len;
> unsigned int dma_sg_idx;
> unsigned char bus_mode;
> @@ -224,6 +247,13 @@ struct omap_hsmmc_host {
> #define AUTO_CMD23 (1 << 0) /* Auto CMD23 support */
> #define HSMMC_SDIO_IRQ_ENABLED (1 << 1) /* SDIO irq enabled */
> #define HSMMC_WAKE_IRQ_ENABLED (1 << 2)
> +
> + u32 *tuning_data;
> + int tuning_size;
> + int tuning_done;
> + int tuning_fsrc;
> + u32 tuning_uhsmc;
> + u32 tuning_opcode;
> struct omap_hsmmc_next next_data;
> struct omap_mmc_platform_data *pdata;
> };
> @@ -233,6 +263,48 @@ struct omap_mmc_of_data {
> u8 controller_flags;
> };
>
> +static const u32 ref_tuning_4bits[] = {
> + 0x00FF0FFF, 0xCCC3CCFF, 0xFFCC3CC3, 0xEFFEFFFE,
> + 0xDDFFDFFF, 0xFBFFFBFF, 0xFF7FFFBF, 0xEFBDF777,
> + 0xF0FFF0FF, 0x3CCCFC0F, 0xCFCC33CC, 0xEEFFEFFF,
> + 0xFDFFFDFF, 0xFFBFFFDF, 0xFFF7FFBB, 0xDE7B7FF7
> +};
> +
> +static const u32 ref_tuning_8bits[] = {
> + 0xFF00FFFF, 0x0000FFFF, 0xCCCCFFFF, 0xCCCC33CC,
> + 0xCC3333CC, 0xFFFFCCCC, 0xFFFFEEFF, 0xFFEEEEFF,
> + 0xFFDDFFFF, 0xDDDDFFFF, 0xBBFFFFFF, 0xBBFFFFFF,
> + 0xFFFFFFBB, 0XFFFFFF77, 0x77FF7777, 0xFFEEDDBB,
> + 0x00FFFFFF, 0x00FFFFFF, 0xCCFFFF00, 0xCC33CCCC,
> + 0x3333CCCC, 0xFFCCCCCC, 0xFFEEFFFF, 0xEEEEFFFF,
> + 0xDDFFFFFF, 0xDDFFFFFF, 0xFFFFFFDD, 0XFFFFFFBB,
> + 0xFFFFBBBB, 0xFFFF77FF, 0xFF7777FF, 0xEEDDBB77
> +};

big endian
https://lkml.org/lkml/2014/9/2/678

> +
> +static inline int omap_hsmmc_set_dll(struct omap_hsmmc_host *host, int count)
> +{
> + int i;
> + u32 dll;
> +
> + dll = OMAP_HSMMC_READ(host->base, DLL);
> + dll &= ~(DLL_FORCE_SR_C_MASK);
> + dll &= ~DLL_CALIB;
> + dll |= (count << DLL_FORCE_SR_C_SHIFT);
> + OMAP_HSMMC_WRITE(host->base, DLL, dll);
> + dll |= DLL_FORCE_VALUE;
> + OMAP_HSMMC_WRITE(host->base, DLL, dll);
> + dll |= DLL_CALIB;
> + OMAP_HSMMC_WRITE(host->base, DLL, dll);
> + for (i = 0; i < 1000; i++) {
> + if (OMAP_HSMMC_READ(host->base, DLL) & DLL_CALIB)
> + break;
> + }
> + dll &= ~DLL_CALIB;
> + dll = OMAP_HSMMC_READ(host->base, DLL);
> +
> + return 0;
> +}
> +
> static void omap_hsmmc_start_dma_transfer(struct omap_hsmmc_host *host);
>
> static int omap_hsmmc_card_detect(struct device *dev, int slot)
> @@ -531,7 +603,10 @@ static void omap_hsmmc_enable_irq(struct omap_hsmmc_host *host,
> u32 irq_mask = INT_EN_MASK;
> unsigned long flags;
>
> - if (host->use_dma)
> + if ((cmd->opcode == MMC_SEND_TUNING_BLOCK) ||
> + (cmd->opcode == MMC_SEND_TUNING_BLOCK_HS200))
> + irq_mask |= BRR_EN;
> + else if (host->use_dma)
> irq_mask &= ~(BRR_EN | BWR_EN);
>
> /* Disable timeout for erases */
> @@ -578,6 +653,33 @@ static u16 calc_divisor(struct omap_hsmmc_host *host, struct mmc_ios *ios)
> return dsor;
> }
>
> +static inline int omap_hsmmc_restore_dll(struct omap_hsmmc_host *host)
> +{
> + u32 ac12;
> + u32 dll;
> +
> + ac12 = OMAP_HSMMC_READ(host->base, AC12);
> + ac12 |= host->tuning_uhsmc;
> + OMAP_HSMMC_WRITE(host->base, AC12, ac12);
> +
> + dll = OMAP_HSMMC_READ(host->base, DLL);
> + dll |= DLL_FORCE_VALUE;
> + OMAP_HSMMC_WRITE(host->base, DLL, dll);
> +
> + if (omap_hsmmc_set_dll(host, host->tuning_fsrc))
> + return -EIO;
> + return 0;
> +}
> +
> +static inline void omap_hsmmc_save_dll(struct omap_hsmmc_host *host)
> +{
> + u32 ac12;
> +
> + ac12 = OMAP_HSMMC_READ(host->base, AC12);
> + ac12 &= ~AC12_UHSMC_MASK;
> + OMAP_HSMMC_WRITE(host->base, AC12, ac12);
> +}

where is this function used?

> +
> static void omap_hsmmc_set_clock(struct omap_hsmmc_host *host)
> {
> struct mmc_ios *ios = &host->mmc->ios;
> @@ -589,6 +691,9 @@ static void omap_hsmmc_set_clock(struct omap_hsmmc_host *host)
>
> omap_hsmmc_stop_clock(host);
>
> + if (host->mmc->caps2 & MMC_CAP2_HS200)
> + clk_set_rate(host->fclk, 192000000);
> +
> regval = OMAP_HSMMC_READ(host->base, SYSCTL);
> regval = regval & ~(CLKD_MASK | DTO_MASK);
> clkdiv = calc_divisor(host, ios);
> @@ -667,7 +772,6 @@ static void omap_hsmmc_set_bus_mode(struct omap_hsmmc_host *host)
> }
>
> #ifdef CONFIG_PM
> -
> /*
> * Restore the MMC host context, if it was lost as result of a
> * power state change.
> @@ -878,6 +982,12 @@ omap_hsmmc_start_command(struct omap_hsmmc_host *host, struct mmc_command *cmd,
> if (host->use_dma)
> cmdreg |= DMAE;
>
> + /* Tuning command is special. Data Present Select should be set */
> + if ((cmd->opcode == MMC_SEND_TUNING_BLOCK) ||
> + (cmd->opcode == MMC_SEND_TUNING_BLOCK_HS200)) {
> + cmdreg = (cmd->opcode << 24) | (resptype << 16) |
> + (cmdtype << 22) | DP_SELECT | DDIR;
> + }
> host->req_in_progress = 1;
>
> OMAP_HSMMC_WRITE(host->base, ARG, cmd->arg);
> @@ -965,6 +1075,9 @@ omap_hsmmc_cmd_done(struct omap_hsmmc_host *host, struct mmc_command *cmd)
> return;
> }
>
> + if (host->cmd->opcode == MMC_SEND_TUNING_BLOCK)
> + return;
> +
> host->cmd = NULL;
>
> if (cmd->flags & MMC_RSP_PRESENT) {
> @@ -1104,6 +1217,7 @@ static void omap_hsmmc_do_irq(struct omap_hsmmc_host *host, int status)
> struct mmc_data *data;
> int end_cmd = 0, end_trans = 0;
> int error = 0;
> + int i = 0;
>
> data = host->data;
> dev_vdbg(mmc_dev(host->mmc), "IRQ Status is %x\n", status);
> @@ -1139,6 +1253,15 @@ static void omap_hsmmc_do_irq(struct omap_hsmmc_host *host, int status)
> }
>
> OMAP_HSMMC_WRITE(host->base, STAT, status);
> +
> + if (status & BRR_EN) {
> + for (i = 0; i < host->tuning_size/4; i++)
> + host->tuning_data[i] =
> + OMAP_HSMMC_READ(host->base, DATA);
> + complete(&host->buf_ready);
> + return;
> + }
> +
> if (end_cmd || ((status & CC_EN) && host->cmd))
> omap_hsmmc_cmd_done(host, host->cmd);
> if ((end_trans || (status & TC_EN)) && host->mrq)
> @@ -1844,6 +1967,189 @@ static int omap_hsmmc_multi_io_quirk(struct mmc_card *card,
> return blk_size;
> }
>
> +static int omap_execute_tuning(struct mmc_host *mmc, u32 opcode)
> +{
> + struct omap_hsmmc_host *host;
> + struct mmc_ios *ios = &mmc->ios;
> + const u32 *tuning_ref;
> + int phase_delay = 0;
> + int err = 0;
> + int count = 0;
> + int length = 0;
> + int note_index = 0xFF;
> + int max_index = 0;
> + int max_window = 0;
> + bool previous_match = false;
> + bool current_match;
> + u32 ac12, capa2, dll = 0;
> +
> + host = mmc_priv(mmc);
> + switch (ios->bus_width) {
> + case MMC_BUS_WIDTH_8:
> + tuning_ref = ref_tuning_8bits;
> + host->tuning_size = sizeof(ref_tuning_8bits);
> + break;
> + case MMC_BUS_WIDTH_4:
> + tuning_ref = ref_tuning_4bits;
> + host->tuning_size = sizeof(ref_tuning_4bits);
> + break;
> + default:
> + return -EINVAL;
> + }
> +
> + host->tuning_data = kzalloc(host->tuning_size, GFP_KERNEL);
> + if (!host->tuning_data)
> + return -ENOMEM;
> +
> + host->tuning_done = 0;
> + /* clock tuning is not needed for upto 52MHz */
> + if (ios->clock <= EMMC_HSDDR_SD_SDR25_MAX)
> + return 0;
> +
> + omap_hsmmc_stop_clock(host);
> +
> + ac12 = OMAP_HSMMC_READ(host->base, AC12);
> + capa2 = OMAP_HSMMC_READ(host->base, CAPA2);
> +
> + ac12 &= ~AC12_UHSMC_MASK;
> + OMAP_HSMMC_WRITE(host->base, AC12, ac12);
> +
> + /*
> + * Host Controller needs tuning only in case of SDR104 mode
> + * and for SDR50 mode when Use Tuning for SDR50 is set in
> + * Capabilities register.
> + */
> + if (ios->clock <= SD_SDR50_MAX_FREQ) {
> + if (!(capa2 & CAPA2_TSDR50))
> + return 0;
> + ac12 |= AC12_UHSMC_SDR50;
> + } else {
> + ac12 |= AC12_UHSMC_SDR104;
> + }
> +
> + ac12 |= AC12_UHSMC_SDR104;
> + ac12 |= V1V8_SIGEN;
> +
> + /* Enable SDR50/SDR104 mode */
> + OMAP_HSMMC_WRITE(host->base, AC12, ac12);
> + omap_hsmmc_start_clock(host);
> +
> + /* Start software tuning Procedure */
> + dll |= DLL_SWT;
> + OMAP_HSMMC_WRITE(host->base, DLL, dll);
> +
> + while (phase_delay < MAX_PHASE_DELAY) {
> + struct mmc_command cmd = {0};
> + struct mmc_request mrq = {0};
> +
> + if (phase_delay > MAX_PHASE_DELAY)
> + break;
> +
> + omap_hsmmc_set_dll(host, phase_delay);
> +
> + cmd.opcode = opcode;
> + cmd.arg = 0;
> + cmd.flags = MMC_RSP_R1 | MMC_CMD_ADTC;
> + cmd.retries = 0;
> + cmd.data = NULL;
> + cmd.error = 0;
> +
> + mrq.cmd = &cmd;
> + host->mrq = &mrq;
> +
> + OMAP_HSMMC_WRITE(host->base, BLK, host->tuning_size);
> + set_data_timeout(host, 50000000, 0);
> + omap_hsmmc_start_command(host, &cmd, NULL);
> +
> + host->cmd = NULL;
> + host->mrq = NULL;
> +
> + /* Wait for Buffer Read Ready interrupt */
> + err = wait_for_completion_timeout(&host->buf_ready,
> + msecs_to_jiffies(5000));
> + omap_hsmmc_disable_irq(host);
> + host->req_in_progress = 0;
> +
> + if (err == 0) {
> + dev_err(mmc_dev(host->mmc),
> + "Tuning BRR timeout. phase_delay=%x",
> + phase_delay);
> + err = -ETIMEDOUT;
> + goto tuning_error;
> + }
> +
> + current_match = true;
> + if (memcmp(host->tuning_data, tuning_ref, host->tuning_size))
> + current_match = false;
> + else
> + current_match = true;
> +
> + if (current_match == true) {
> + if (previous_match == false) {
> + /* new window */
> + note_index = count;
> + length = 1;
> + } else {
> + length++;
> + }
> + previous_match = true;
> + if (length > max_window) {
> + max_index = note_index;
> + max_window = length;
> + }
> + } else {
> + previous_match = false;
> + }
> + phase_delay += 4;
> + count++;
> + }
> +
> + if (!max_window) {
> + dev_err(mmc_dev(host->mmc), "Unable to find match\n");
> + err = -EIO;
> + goto tuning_error;
> + }
> +
> + ac12 = OMAP_HSMMC_READ(host->base, AC12);
> + if (!(ac12 & AC12_SCLK_SEL)) {
> + err = -EIO;
> + goto tuning_error;
> + }
> +
> + dll = OMAP_HSMMC_READ(host->base, DLL);
> + dll &= ~DLL_SWT;
> + OMAP_HSMMC_WRITE(host->base, DLL, dll);
> + count = 4 * (max_index + (max_window >> 1));
> + if (omap_hsmmc_set_dll(host, count)) {
> + err = -EIO;
> + goto tuning_error;
> + }
> + host->tuning_fsrc = count;
> + host->tuning_uhsmc = (OMAP_HSMMC_READ(host->base, AC12)
> + & AC12_UHSMC_MASK);
> + host->tuning_opcode = opcode;
> + host->tuning_done = 1;
> + omap_hsmmc_reset_controller_fsm(host, SRD);
> + omap_hsmmc_reset_controller_fsm(host, SRC);
> +
> + return 0;
> +
> +tuning_error:
> + dev_err(mmc_dev(host->mmc),
> + "Tuning failed. Using fixed sampling clock\n");
> + ac12 = OMAP_HSMMC_READ(host->base, AC12);
> + ac12 &= ~(AC12_UHSMC_MASK | AC12_SCLK_SEL);
> + OMAP_HSMMC_WRITE(host->base, AC12, ac12);
> +
> + dll = OMAP_HSMMC_READ(host->base, DLL);
> + dll &= ~(DLL_FORCE_VALUE | DLL_SWT);
> + OMAP_HSMMC_WRITE(host->base, DLL, dll);
> +
> + omap_hsmmc_reset_controller_fsm(host, SRD);
> + omap_hsmmc_reset_controller_fsm(host, SRC);
> + return err;
> +}
> +
> static struct mmc_host_ops omap_hsmmc_ops = {
> .enable = omap_hsmmc_enable_fclk,
> .disable = omap_hsmmc_disable_fclk,
> @@ -1855,6 +2161,7 @@ static struct mmc_host_ops omap_hsmmc_ops = {
> .get_ro = omap_hsmmc_get_ro,
> .init_card = omap_hsmmc_init_card,
> .enable_sdio_irq = omap_hsmmc_enable_sdio_irq,
> + .execute_tuning = omap_execute_tuning,
> };
>
> #ifdef CONFIG_DEBUG_FS
> @@ -2107,6 +2414,7 @@ static int omap_hsmmc_probe(struct platform_device *pdev)
> mmc->f_max = OMAP_MMC_MAX_CLOCK;
>
> spin_lock_init(&host->irq_lock);
> + init_completion(&host->buf_ready);
>
> host->fclk = devm_clk_get(&pdev->dev, "fck");
> if (IS_ERR(host->fclk)) {
> @@ -2159,7 +2467,7 @@ static int omap_hsmmc_probe(struct platform_device *pdev)
>
> mmc->pm_caps = mmc_slot(host).pm_caps;
>
> - reg = OMAP_HSMMC_READ(host->base, OMAP_HSMMC_CAPA2);
> + reg = OMAP_HSMMC_READ(host->base, CAPA2);
>
> if (reg & SDR50)
> mmc->caps |= MMC_CAP_UHS_DDR50;
> @@ -2172,6 +2480,9 @@ static int omap_hsmmc_probe(struct platform_device *pdev)
> if (reg & DDR50)
> mmc->caps |= MMC_CAP_UHS_DDR50;
>
> + mmc->caps |= MMC_CAP_DRIVER_TYPE_A | MMC_CAP_DRIVER_TYPE_C |
> + MMC_CAP_DRIVER_TYPE_D;
> +
> omap_hsmmc_conf_bus_power(host);
>
> if (!pdev->dev.of_node) {
> @@ -2380,6 +2691,7 @@ static int omap_hsmmc_suspend(struct device *dev)
> OMAP_HSMMC_WRITE(host->base, HCTL,
> OMAP_HSMMC_READ(host->base, HCTL) & ~SDBP);
> }
> + host->tuning_done = 0;
>
> /* do not wake up due to sdio irq */
> if ((host->mmc->caps & MMC_CAP_SDIO_IRQ) &&
> @@ -2434,6 +2746,9 @@ static int omap_hsmmc_runtime_suspend(struct device *dev)
> int ret = 0;
>
> host = platform_get_drvdata(to_platform_device(dev));
> + if (host->tuning_done)
> + omap_hsmmc_restore_dll(host);
> +
> omap_hsmmc_context_save(host);
> dev_dbg(dev, "disabled\n");
>
> @@ -2480,6 +2795,10 @@ static int omap_hsmmc_runtime_resume(struct device *dev)
>
> host = platform_get_drvdata(to_platform_device(dev));
> omap_hsmmc_context_restore(host);
> +
> + if (host->tuning_done)
> + omap_hsmmc_restore_dll(host);
> +
> dev_dbg(dev, "enabled\n");
>
> spin_lock_irqsave(&host->irq_lock, flags);
> --
> 1.7.9.5
>

2014-11-14 17:42:01

by Felipe Balbi

[permalink] [raw]
Subject: Re: [RFC PATCH 2/3] mmc: omap_hsmmc: add tuning support

Hi,

On Thu, Nov 13, 2014 at 06:26:18PM +0530, Kishon Vijay Abraham I wrote:
> @@ -233,6 +263,48 @@ struct omap_mmc_of_data {
> u8 controller_flags;
> };
>
> +static const u32 ref_tuning_4bits[] = {
> + 0x00FF0FFF, 0xCCC3CCFF, 0xFFCC3CC3, 0xEFFEFFFE,
> + 0xDDFFDFFF, 0xFBFFFBFF, 0xFF7FFFBF, 0xEFBDF777,
> + 0xF0FFF0FF, 0x3CCCFC0F, 0xCFCC33CC, 0xEEFFEFFF,
> + 0xFDFFFDFF, 0xFFBFFFDF, 0xFFF7FFBB, 0xDE7B7FF7
> +};
> +
> +static const u32 ref_tuning_8bits[] = {
> + 0xFF00FFFF, 0x0000FFFF, 0xCCCCFFFF, 0xCCCC33CC,
> + 0xCC3333CC, 0xFFFFCCCC, 0xFFFFEEFF, 0xFFEEEEFF,
> + 0xFFDDFFFF, 0xDDDDFFFF, 0xBBFFFFFF, 0xBBFFFFFF,
> + 0xFFFFFFBB, 0XFFFFFF77, 0x77FF7777, 0xFFEEDDBB,
> + 0x00FFFFFF, 0x00FFFFFF, 0xCCFFFF00, 0xCC33CCCC,
> + 0x3333CCCC, 0xFFCCCCCC, 0xFFEEFFFF, 0xEEEEFFFF,
> + 0xDDFFFFFF, 0xDDFFFFFF, 0xFFFFFFDD, 0XFFFFFFBB,
> + 0xFFFFBBBB, 0xFFFF77FF, 0xFF7777FF, 0xEEDDBB77
> +};

yet another feature we would've gotten for free if we were already using
sdhci. This is just a copy of what's on sdhci-msm.c, probably because
they license the same IP we do.

It would be much better to just convert omap-hsmmc.c into sdhci-omap.c
much like it was done for 8250-omap.c, with the added benefit that this
will incur no userland regressions due to devnode names.

--
balbi


Attachments:
(No filename) (1.24 kB)
signature.asc (819.00 B)
Digital signature
Download all attachments

2014-11-14 18:07:24

by Russell King - ARM Linux

[permalink] [raw]
Subject: Re: [RFC PATCH 2/3] mmc: omap_hsmmc: add tuning support

On Fri, Nov 14, 2014 at 11:41:52AM -0600, Felipe Balbi wrote:
> Hi,
>
> On Thu, Nov 13, 2014 at 06:26:18PM +0530, Kishon Vijay Abraham I wrote:
> > @@ -233,6 +263,48 @@ struct omap_mmc_of_data {
> > u8 controller_flags;
> > };
> >
> > +static const u32 ref_tuning_4bits[] = {
> > + 0x00FF0FFF, 0xCCC3CCFF, 0xFFCC3CC3, 0xEFFEFFFE,
> > + 0xDDFFDFFF, 0xFBFFFBFF, 0xFF7FFFBF, 0xEFBDF777,
> > + 0xF0FFF0FF, 0x3CCCFC0F, 0xCFCC33CC, 0xEEFFEFFF,
> > + 0xFDFFFDFF, 0xFFBFFFDF, 0xFFF7FFBB, 0xDE7B7FF7
> > +};
> > +
> > +static const u32 ref_tuning_8bits[] = {
> > + 0xFF00FFFF, 0x0000FFFF, 0xCCCCFFFF, 0xCCCC33CC,
> > + 0xCC3333CC, 0xFFFFCCCC, 0xFFFFEEFF, 0xFFEEEEFF,
> > + 0xFFDDFFFF, 0xDDDDFFFF, 0xBBFFFFFF, 0xBBFFFFFF,
> > + 0xFFFFFFBB, 0XFFFFFF77, 0x77FF7777, 0xFFEEDDBB,
> > + 0x00FFFFFF, 0x00FFFFFF, 0xCCFFFF00, 0xCC33CCCC,
> > + 0x3333CCCC, 0xFFCCCCCC, 0xFFEEFFFF, 0xEEEEFFFF,
> > + 0xDDFFFFFF, 0xDDFFFFFF, 0xFFFFFFDD, 0XFFFFFFBB,
> > + 0xFFFFBBBB, 0xFFFF77FF, 0xFF7777FF, 0xEEDDBB77
> > +};
>
> yet another feature we would've gotten for free if we were already using
> sdhci. This is just a copy of what's on sdhci-msm.c, probably because
> they license the same IP we do.
>
> It would be much better to just convert omap-hsmmc.c into sdhci-omap.c
> much like it was done for 8250-omap.c, with the added benefit that this
> will incur no userland regressions due to devnode names.

If someone does do that, sdhci needs more of the quirks killing - sdhci.c
/really/ needs to become a library which all these drivers use, rather
than interposing itself between the MMC core and the actual driver, and
the more that can be done the better. All the time that people expand
on the current disaster, that job is just going to get harder and harder
(because there'll be more and more sub-drivers, with more and more
sub-driver specific quirks.)

--
FTTC broadband for 0.8mile line: currently at 9.5Mbps down 400kbps up
according to speedtest.net.