#include <linux/mmc/host.h>
#include <linux/scatterlist.h>
#include <linux/io.h>
-#include <asm/intel_scu_ipc.h>
#include <linux/pm_runtime.h>
#include "sdhci.h"
#define MAX_SLOTS 8
-#define IPC_EMMC_MUTEX_CMD 0xEE
-
struct sdhci_pci_chip;
struct sdhci_pci_slot;
static int mfd_emmc_probe_slot(struct sdhci_pci_slot *slot)
{
- int ret;
- u32 mutex_var_addr[3];
- struct pci_dev *pdev;
-
slot->host->mmc->caps |= MMC_CAP_8_BIT_DATA;
- /*
- * for some card, it is should be none removable, e.g.eMMC card
- * So if we are a eMMC slot, the mmc host capability can be set
- * by MMC_CAP_NONREMOVABLE
- */
- slot->host->mmc->caps |= MMC_CAP_NONREMOVABLE;
-
- pdev = slot->chip->pdev;
- if (pdev->device != PCI_DEVICE_ID_INTEL_MFD_EMMC0)
- return 0;
-
- ret = intel_scu_ipc_command(IPC_EMMC_MUTEX_CMD, 0,
- NULL, 0, mutex_var_addr, 3);
- if (ret) {
- dev_err(&slot->chip->pdev->dev, "IPC error: %d\n", ret);
- slot->host->sram_addr = 0;
- } else {
- /* 3 housekeeping mutex variables, 12 bytes length */
- slot->host->sram_addr = ioremap_nocache(mutex_var_addr[0], 16);
- if (!slot->host->sram_addr) {
- dev_err(&slot->chip->pdev->dev, "ioremap failed!\n");
- } else {
- dev_info(&slot->chip->pdev->dev, "mapped addr: %p\n",
- slot->host->sram_addr);
- dev_info(&slot->chip->pdev->dev, "current eMMC owner:"
- " %d, IA req: %d, SCU req: %d\n",
- readl(slot->host->sram_addr +
- DEKKER_EMMC_OWNER_OFFSET),
- readl(slot->host->sram_addr +
- DEKKER_IA_REQ_OFFSET),
- readl(slot->host->sram_addr +
- DEKKER_SCU_REQ_OFFSET));
- }
- }
-
return 0;
}
-static void mfd_emmc_remove_slot(struct sdhci_pci_slot *slot, int dead)
-{
- struct pci_dev *pdev;
- if (dead)
- return;
-
- pdev = slot->chip->pdev;
- if (pdev->device == PCI_DEVICE_ID_INTEL_MFD_EMMC0) {
- if (slot->host->sram_addr)
- iounmap(slot->host->sram_addr);
- }
-}
-
static const struct sdhci_pci_fixes sdhci_intel_mrst_hc0 = {
.quirks = SDHCI_QUIRK_BROKEN_ADMA | SDHCI_QUIRK_NO_HISPD_BIT,
.probe_slot = mrst_hc_probe_slot,
static const struct sdhci_pci_fixes sdhci_intel_mfd_emmc = {
.quirks = SDHCI_QUIRK_NO_ENDATTR_IN_NOPDESC,
.probe_slot = mfd_emmc_probe_slot,
- .remove_slot = mfd_emmc_remove_slot,
};
/* O2Micro extra registers */
sdhci_clear_set_irqs(host, SDHCI_INT_ALL_MASK, ier);
}
-static void __sdhci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios);
+static void sdhci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios);
static void sdhci_init(struct sdhci_host *host, int soft)
{
if (soft) {
/* force clock reconfiguration */
host->clock = 0;
- __sdhci_set_ios(host->mmc, &host->mmc->ios);
+ sdhci_set_ios(host->mmc, &host->mmc->ios);
}
}
host->cmd = NULL;
sdhci_send_command(host, host->mrq->cmd);
} else {
+
/* Processed actual command. */
if (host->data && host->data_early)
sdhci_finish_data(host);
* *
\*****************************************************************************/
-/*
- * One of the Medfield eMMC controller (PCI device id 0x0823, SDIO3) is
- * a shared resource used by the SCU and the IA processors. SCU primarily
- * uses the eMMC host controller to access the eMMC device's Boot Partition,
- * while the IA CPU uses the eMMC host controller to access the eMMC device's
- * User Partition.
- *
- * After the SCU hands off the system to the IA processor, the IA processor
- * assumes ownership to the eMMC host controller. Due to absence of any
- * arbitration at the eMMC host controller, this could result in concurrent
- * eMMC host accesses resulting in bus contention and garbage data ending up
- * in either of the partitions.
- * To circumvent this from happening, eMMC host controller locking mechanism
- * is employed, where at any one given time, only one agent, SCU or IA, may be
- * allowed to access the host. This is achieved by implementing Dekker's
- * Algorithm (http://en.wikipedia.org/wiki/Dekker's_algorithm) between the
- * two processors.
- *
- * Before handing off the system to the IA processor, SCU must set up three
- * housekeeping mutex variables allocated in the shared SRAM as follows:
- *
- * eMMC_Owner = IA (SCU and IA processors - RW, 32bit)
- * IA_Req = FALSE (IA -RW, SCU - RO, 32bit)
- * SCU_Req = FALSE (IA - RO, SCU - R/W, 32bit)
- *
- * There is no hardware based access control to these variables and so code
- * executing on SCU and IA processors must follow below access rules
- * (Dekker's algorithm):
- *
- * -----------------------------------------
- * SCU Processor Implementation
- * -----------------------------------------
- * SCU_Req = TRUE;
- * while (IA_Req == TRUE) {
- * if (eMMC_Owner != SCU){
- * SCU_Req = FALSE;
- * while (eMMC_Owner != SCU);
- * SCU_Req = TRUE;
- * }
- * }
- * // SCU now performs eMMC transactions here
- * ...
- * // When done, relinquish control to IA
- * eMMC_Owner = IA;
- * SCU_Req = FALSE;
- *
- * -----------------------------------------
- * IA Processor Implementation
- * -----------------------------------------
- * IA_Req = TRUE;
- * while (SCU_Req == TRUE) {
- * if (eMMC_Owner != IA){
- * IA_Req = FALSE;
- * while (eMMC_Owner != IA);
- * IA_Req = TRUE;
- * }
- * }
- * //IA now performs eMMC transactions here
- * ...
- * //When done, relinquish control to SCU
- * eMMC_Owner = SCU;
- * IA_Req = FALSE;
- *
- * ----------------------------------------
-*/
-/**
- * __sdhci_acquire_ownership- implement the Dekker's algorithm on IA side
- * This function is only used for acquire ownership, not to re-cofnig host
- * controller. Since in some scenarios, re-config is not useless. We can
- * save some unused expenses.
- * @mmc: mmc host
- *
- * @return return value:
- * 0 - Acquried the ownership successfully. The last owner is IA
- * 1 - Acquried the ownership succesffully. The last owenr is SCU
- * -EBUSY - failed to acquire ownership within the timeout period
- */
-static int __sdhci_acquire_ownership(struct mmc_host *mmc)
-{
- struct sdhci_host *host;
- unsigned long t1, t2;
-
- host = mmc_priv(mmc);
-
- if (!host->sram_addr)
- return 0;
-
- atomic_inc(&host->usage_cnt);
-
- /* If IA has already hold the eMMC mutex, then just exit */
- if (readl(host->sram_addr + DEKKER_IA_REQ_OFFSET))
- return 0;
-
- DBG("Acquire ownership - eMMC owner: %d, IA req: %d, SCU req: %d\n",
- readl(host->sram_addr + DEKKER_EMMC_OWNER_OFFSET),
- readl(host->sram_addr + DEKKER_IA_REQ_OFFSET),
- readl(host->sram_addr + DEKKER_SCU_REQ_OFFSET));
-
- writel(1, host->sram_addr + DEKKER_IA_REQ_OFFSET);
-
- t1 = jiffies + 10 * HZ;
- t2 = 500;
-
- while (readl(host->sram_addr + DEKKER_SCU_REQ_OFFSET)) {
- if (readl(host->sram_addr + DEKKER_EMMC_OWNER_OFFSET) !=
- DEKKER_OWNER_IA) {
- writel(0, host->sram_addr + DEKKER_IA_REQ_OFFSET);
- while (t2) {
- if (readl(host->sram_addr +
- DEKKER_EMMC_OWNER_OFFSET) ==
- DEKKER_OWNER_IA)
- break;
- msleep(10);
- t2--;
- }
- if (t2)
- writel(1, host->sram_addr +
- DEKKER_IA_REQ_OFFSET);
- else
- goto timeout;
- }
- if (time_after(jiffies, t1))
- goto timeout;
-
- cpu_relax();
- }
-
- /*
- * if the last owner is SCU, will do the re-config host controller
- * in the next
- */
- return (readl(host->sram_addr + DEKKER_EMMC_OWNER_OFFSET) ==
- DEKKER_OWNER_IA) ? 1 : 0;
-
-timeout:
- printk(KERN_ERR "eMMC mutex timeout!\n"
- "Dump Dekker's house keeping variables -"
- "eMMC owner: %d, IA req: %d, SCU req: %d\n",
- readl(host->sram_addr + DEKKER_EMMC_OWNER_OFFSET),
- readl(host->sram_addr + DEKKER_IA_REQ_OFFSET),
- readl(host->sram_addr + DEKKER_SCU_REQ_OFFSET));
-
- /* Release eMMC mutex anyway */
- writel(DEKKER_OWNER_SCU, host->sram_addr + DEKKER_EMMC_OWNER_OFFSET);
- writel(0, host->sram_addr + DEKKER_IA_REQ_OFFSET);
-
- return -EBUSY;
-}
-
-static int sdhci_acquire_ownership(struct mmc_host *mmc)
-{
- int ret;
-
- ret = __sdhci_acquire_ownership(mmc);
- if (ret) {
- struct sdhci_host *host;
- host = mmc_priv(mmc);
- /* Re-config HC in case SCU has changed HC reg already */
- pm_runtime_get_sync(mmc->parent);
- /*
- * reinit host registers.
- * include reset host controller all,
- * reconfigure clock, pwr and other registers.
- */
- sdhci_init(host, 0);
- host->clock = 0;
- host->pwr = 0;
- __sdhci_set_ios(host->mmc, &host->mmc->ios);
- pm_runtime_put(mmc->parent);
- }
-
- return ret;
-}
-
-static void sdhci_release_ownership(struct mmc_host *mmc)
-{
- struct sdhci_host *host;
-
- host = mmc_priv(mmc);
-
- if (!host->sram_addr)
- return;
-
- if (atomic_dec_and_test(&host->usage_cnt)) {
- writel(DEKKER_OWNER_SCU,
- host->sram_addr + DEKKER_EMMC_OWNER_OFFSET);
- writel(0, host->sram_addr + DEKKER_IA_REQ_OFFSET);
- DBG("Exit ownership - "
- "eMMC owner: %d, IA req: %d, SCU req: %d\n",
- readl(host->sram_addr + DEKKER_EMMC_OWNER_OFFSET),
- readl(host->sram_addr + DEKKER_IA_REQ_OFFSET),
- readl(host->sram_addr + DEKKER_SCU_REQ_OFFSET));
- }
-}
-
static void sdhci_request(struct mmc_host *mmc, struct mmc_request *mrq)
{
struct sdhci_host *host;
spin_unlock_irqrestore(&host->lock, flags);
}
-static void __sdhci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
+static void sdhci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
{
struct sdhci_host *host;
unsigned long flags;
spin_unlock_irqrestore(&host->lock, flags);
}
-static void sdhci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
-{
- sdhci_acquire_ownership(mmc);
-
- pm_runtime_get_sync(mmc->parent);
-
- __sdhci_set_ios(mmc, ios);
-
- pm_runtime_put(mmc->parent);
-
- sdhci_release_ownership(mmc);
-}
-
static int check_ro(struct sdhci_host *host)
{
unsigned long flags;
host = (struct sdhci_host*)param;
- /*
- * If this tasklet gets rescheduled while running, it will
- * be run again afterwards but without any active request.
- */
+ /*
+ * If this tasklet gets rescheduled while running, it will
+ * be run again afterwards but without any active request.
+ */
if (!host->mrq)
return;
spin_unlock_irqrestore(&host->lock, flags);
mmc_request_done(host->mmc, mrq);
-
- sdhci_release_ownership(host->mmc);
}
static void sdhci_timeout_timer(unsigned long data)
{
int ret;
- sdhci_acquire_ownership(host->mmc);
-
sdhci_disable_card_detection(host);
/* Disable tuning since we are suspending */
ret = mmc_suspend_host(host->mmc);
if (ret)
- goto out;
+ return ret;
free_irq(host->irq, host);
if (host->vmmc)
ret = regulator_disable(host->vmmc);
-out:
- sdhci_release_ownership(host->mmc);
+
return ret;
}
{
int ret;
- /*
- * needn't to reconfigure host registers
- * since in the next, host will be re-configured
- */
- __sdhci_acquire_ownership(host->mmc);
-
if (host->vmmc) {
- ret = regulator_enable(host->vmmc);
+ int ret = regulator_enable(host->vmmc);
if (ret)
- goto out;
+ return ret;
}
ret = request_irq(host->irq, sdhci_irq, IRQF_SHARED,
mmc_hostname(host->mmc), host);
if (ret)
- goto out;
+ return ret;
sdhci_init(host, (host->mmc->pm_flags & MMC_PM_KEEP_POWER));
mmiowb();
if ((host->version >= SDHCI_SPEC_300) && host->tuning_count &&
(host->tuning_mode == SDHCI_TUNING_MODE_1))
host->flags |= SDHCI_NEEDS_RETUNING;
-out:
- sdhci_release_ownership(host->mmc);
+
return ret;
}
if ((ret = regulator_enable(host->vmmc)))
return ret;
- __sdhci_acquire_ownership(host->mmc);
-
- if (host->flags & (SDHCI_USE_SDMA | SDHCI_USE_ADMA)) {
- if (host->ops->enable_dma)
- host->ops->enable_dma(host);
- }
-
- sdhci_init(host, (host->mmc->pm_flags & MMC_PM_KEEP_POWER));
- host->pwr = 0; /* force power reprogram */
- host->clock = 0; /* force clock reprogram */
- mmiowb();
-
- sdhci_release_ownership(host->mmc);
-
return ret;
}
EXPORT_SYMBOL_GPL(sdhci_runtime_resume_host);
regulator_enable(host->vmmc);
}
- __sdhci_acquire_ownership(mmc);
-
sdhci_init(host, 0);
host->iosclock = 1; /* default clk_gate is 0 */
pm_runtime_get_noresume(host->mmc->parent);
sdhci_enable_card_detection(host);
- sdhci_release_ownership(mmc);
-
return 0;
#ifdef SDHCI_USE_LEDS_CLASS
reset:
sdhci_reset(host, SDHCI_RESET_ALL);
- sdhci_release_ownership(mmc);
free_irq(host->irq, host);
#endif
untasklet: