@@ -150,6 +150,7 @@ struct tmio_mmc_host {
struct mutex ios_lock; /* protect set_ios() context */
bool native_hotplug;
bool sdio_irq_enabled;
+ u32 scc_tappos;
/* Mandatory callback */
int (*clk_enable)(struct tmio_mmc_host *host);
@@ -165,6 +166,14 @@ struct tmio_mmc_host {
struct mmc_ios *ios);
int (*write16_hook)(struct tmio_mmc_host *host, int addr);
void (*hw_reset)(struct tmio_mmc_host *host);
+ void (*prepare_tuning)(struct tmio_mmc_host *host, unsigned long tap);
+ bool (*check_scc_error)(struct tmio_mmc_host *host);
+
+ /* Mandatory callback for tuning to occur which is
+ * optional for SDR50 and mandatory for SDR104 */
+ unsigned int (*init_tuning)(struct tmio_mmc_host *host);
+ int (*select_tuning)(struct tmio_mmc_host *host, bool *tap,
+ int tap_size);
};
struct tmio_mmc_host *tmio_mmc_host_alloc(struct platform_device *pdev);
@@ -36,6 +36,7 @@
#include <linux/io.h>
#include <linux/irq.h>
#include <linux/mfd/tmio.h>
+#include <linux/mmc/card.h>
#include <linux/mmc/host.h>
#include <linux/mmc/mmc.h>
#include <linux/mmc/slot-gpio.h>
@@ -298,6 +299,11 @@ static void tmio_mmc_finish_request(struct tmio_mmc_host *host)
if (mrq->cmd->error || (mrq->data && mrq->data->error))
tmio_mmc_abort_dma(host);
+ if (host->check_scc_error && host->check_scc_error(host)) {
+ mmc_retune_timer_stop(host->mmc);
+ mmc_retune_needed(host->mmc);
+ }
+
mmc_request_done(host->mmc, mrq);
}
@@ -762,9 +768,57 @@ static void tmio_mmc_hw_reset(struct mmc_host *mmc)
if (host->hw_reset)
host->hw_reset(host);
+}
+
+static int tmio_mmc_execute_tuning(struct mmc_host *mmc, u32 opcode)
+{
+ struct tmio_mmc_host *host = mmc_priv(mmc);
+ unsigned int num;
+ int i, ret = 0;
+ bool *tap;
+
+ if (!host->init_tuning || !host->select_tuning)
+ /* Tuning is not supported */
+ goto out;
+
+ num = host->init_tuning(host);
+ if (!num)
+ /* Tuning is not supported */
+ goto out;
+
+ tap = kmalloc(num * 2 * sizeof(*tap), GFP_KERNEL);
+ if (!tap) {
+ ret = -ENOMEM;
+ goto out;
+ }
+
+ /* Issue CMD19 twice for each tap */
+ for (i = 0; i < 2 * num; i++) {
+ if (host->prepare_tuning)
+ host->prepare_tuning(host, i % num);
+
+ ret = mmc_send_tuning(mmc, opcode, NULL);
+ if (ret && ret != -EILSEQ)
+ goto err_free;
+ tap[i] = (ret != 0);
+
+ mdelay(1);
+ }
+
+ ret = host->select_tuning(host, tap, num * 2);
+
+err_free:
+ kfree(tap);
+out:
+ if (ret < 0) {
+ dev_warn(&host->pdev->dev, "Tuning procedure failed\n");
+ mmc_hw_reset(mmc);
+ } else {
+ host->mmc->retune_period = 0;
+ }
+
+ return ret;
- mmc_retune_timer_stop(host->mmc);
- mmc_retune_needed(host->mmc);
}
/* Process requests from the MMC layer */
@@ -982,6 +1036,7 @@ static struct mmc_host_ops tmio_mmc_ops = {
.enable_sdio_irq = tmio_mmc_enable_sdio_irq,
.multi_io_quirk = tmio_multi_io_quirk,
.hw_reset = tmio_mmc_hw_reset,
+ .execute_tuning = tmio_mmc_execute_tuning,
};
static int tmio_mmc_init_ocr(struct tmio_mmc_host *host)
@@ -1207,6 +1262,9 @@ int tmio_mmc_host_runtime_suspend(struct device *dev)
struct mmc_host *mmc = dev_get_drvdata(dev);
struct tmio_mmc_host *host = mmc_priv(mmc);
+ mmc_retune_timer_stop(host->mmc);
+ mmc_retune_needed(host->mmc);
+
tmio_mmc_disable_mmc_irqs(host, TMIO_MASK_ALL);
if (host->clk_cache)