summaryrefslogtreecommitdiff
path: root/drivers
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@linux-foundation.org>2014-04-09 08:39:39 -0700
committerLinus Torvalds <torvalds@linux-foundation.org>2014-04-09 08:39:39 -0700
commit97e18dc007546fce8e99098480b921a02ebb3037 (patch)
treef38c022d034e0172e83f6972983577f790f24dac /drivers
parent042f7b7cbd1e531278a09c449563165ba1f07673 (diff)
parentc67480173f72e883235dd0ad09d90156c8f87600 (diff)
Merge tag 'mmc-updates-for-3.15-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/cjb/mmc
Pull MMC updates from Chris Ball: "MMC highlights for 3.15: Core: - CONFIG_MMC_UNSAFE_RESUME=y is now default behavior - DT bindings for SDHCI UHS, eMMC HS200, high-speed DDR, at 1.8/1.2V - Add GPIO descriptor based slot-gpio card detect API Drivers: - dw_mmc: Refactor SOCFPGA support as a variant inside dw_mmc-pltfm.c - mmci: Support HW busy detection on ux500 - omap: Support MMC_ERASE - omap_hsmmc: Support MMC_PM_KEEP_POWER, MMC_PM_WAKE_SDIO_IRQ, (a)cmd23 - rtsx: Support pre-req/post-req async - sdhci: Add support for Realtek RTS5250 controllers - sdhci-acpi: Add support for 80860F16, fix 80860F14/SDIO card detect - sdhci-msm: Add new driver for Qualcomm SDHCI chipset support - sdhci-pxav3: Add support for Marvell Armada 380 and 385 SoCs" * tag 'mmc-updates-for-3.15-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/cjb/mmc: (102 commits) mmc: sdhci-acpi: Intel SDIO has broken card detect mmc: sdhci-pxav3: add support for the Armada 38x SDHCI controller mmc: sdhci-msm: Add platform_execute_tuning implementation mmc: sdhci-msm: Initial support for Qualcomm chipsets mmc: sdhci-msm: Qualcomm SDHCI binding documentation sdhci: only reprogram retuning timer when flag is set mmc: rename ARCH_BCM to ARCH_BCM_MOBILE mmc: sdhci: Allow for irq being shared mmc: sdhci-acpi: Add device id 80860F16 mmc: sdhci-acpi: Fix broken card detect for ACPI HID 80860F14 mmc: slot-gpio: Add GPIO descriptor based CD GPIO API mmc: slot-gpio: Split out CD IRQ request into a separate function mmc: slot-gpio: Record GPIO descriptors instead of GPIO numbers Revert "dts: socfpga: Add support for SD/MMC on the SOCFPGA platform" mmc: sdhci-spear: use generic card detection gpio support mmc: sdhci-spear: remove support for power gpio mmc: sdhci-spear: simplify resource handling mmc: sdhci-spear: fix platform_data usage mmc: sdhci-spear: fix error handling paths for DT mmc: sdhci-bcm-kona: fix build errors when built-in ...
Diffstat (limited to 'drivers')
-rw-r--r--drivers/mfd/rtsx_pcr.c132
-rw-r--r--drivers/mmc/card/block.c181
-rw-r--r--drivers/mmc/core/Kconfig15
-rw-r--r--drivers/mmc/core/bus.c12
-rw-r--r--drivers/mmc/core/core.c87
-rw-r--r--drivers/mmc/core/host.c18
-rw-r--r--drivers/mmc/core/mmc.c65
-rw-r--r--drivers/mmc/core/mmc_ops.c64
-rw-r--r--drivers/mmc/core/sd.c23
-rw-r--r--drivers/mmc/core/slot-gpio.c180
-rw-r--r--drivers/mmc/host/Kconfig23
-rw-r--r--drivers/mmc/host/Makefile2
-rw-r--r--drivers/mmc/host/davinci_mmc.c4
-rw-r--r--drivers/mmc/host/dw_mmc-k3.c2
-rw-r--r--drivers/mmc/host/dw_mmc-pltfm.c12
-rw-r--r--drivers/mmc/host/dw_mmc-socfpga.c138
-rw-r--r--drivers/mmc/host/dw_mmc.c2
-rw-r--r--drivers/mmc/host/dw_mmc.h3
-rw-r--r--drivers/mmc/host/mmci.c54
-rw-r--r--drivers/mmc/host/mmci.h2
-rw-r--r--drivers/mmc/host/omap.c93
-rw-r--r--drivers/mmc/host/omap_hsmmc.c242
-rw-r--r--drivers/mmc/host/rtsx_pci_sdmmc.c529
-rw-r--r--drivers/mmc/host/sdhci-acpi.c80
-rw-r--r--drivers/mmc/host/sdhci-bcm-kona.c39
-rw-r--r--drivers/mmc/host/sdhci-dove.c2
-rw-r--r--drivers/mmc/host/sdhci-msm.c618
-rw-r--r--drivers/mmc/host/sdhci-pci.c20
-rw-r--r--drivers/mmc/host/sdhci-pxav3.c68
-rw-r--r--drivers/mmc/host/sdhci-s3c.c170
-rw-r--r--drivers/mmc/host/sdhci-spear.c199
-rw-r--r--drivers/mmc/host/sdhci.c24
-rw-r--r--drivers/mmc/host/sh_mobile_sdhi.c50
-rw-r--r--drivers/mmc/host/tmio_mmc.c30
-rw-r--r--drivers/mmc/host/tmio_mmc.h7
-rw-r--r--drivers/mmc/host/tmio_mmc_pio.c7
-rw-r--r--drivers/mmc/host/ushc.c2
-rw-r--r--drivers/mmc/host/wmt-sdmmc.c4
-rw-r--r--drivers/regulator/Kconfig9
-rw-r--r--drivers/regulator/Makefile1
-rw-r--r--drivers/regulator/pbias-regulator.c255
41 files changed, 2334 insertions, 1134 deletions
diff --git a/drivers/mfd/rtsx_pcr.c b/drivers/mfd/rtsx_pcr.c
index 1d15735f9ef9..c9de3d598ea5 100644
--- a/drivers/mfd/rtsx_pcr.c
+++ b/drivers/mfd/rtsx_pcr.c
@@ -338,58 +338,28 @@ int rtsx_pci_transfer_data(struct rtsx_pcr *pcr, struct scatterlist *sglist,
int num_sg, bool read, int timeout)
{
struct completion trans_done;
- u8 dir;
- int err = 0, i, count;
+ int err = 0, count;
long timeleft;
unsigned long flags;
- struct scatterlist *sg;
- enum dma_data_direction dma_dir;
- u32 val;
- dma_addr_t addr;
- unsigned int len;
-
- dev_dbg(&(pcr->pci->dev), "--> %s: num_sg = %d\n", __func__, num_sg);
-
- /* don't transfer data during abort processing */
- if (pcr->remove_pci)
- return -EINVAL;
-
- if ((sglist == NULL) || (num_sg <= 0))
- return -EINVAL;
- if (read) {
- dir = DEVICE_TO_HOST;
- dma_dir = DMA_FROM_DEVICE;
- } else {
- dir = HOST_TO_DEVICE;
- dma_dir = DMA_TO_DEVICE;
- }
-
- count = dma_map_sg(&(pcr->pci->dev), sglist, num_sg, dma_dir);
+ count = rtsx_pci_dma_map_sg(pcr, sglist, num_sg, read);
if (count < 1) {
dev_err(&(pcr->pci->dev), "scatterlist map failed\n");
return -EINVAL;
}
dev_dbg(&(pcr->pci->dev), "DMA mapping count: %d\n", count);
- val = ((u32)(dir & 0x01) << 29) | TRIG_DMA | ADMA_MODE;
- pcr->sgi = 0;
- for_each_sg(sglist, sg, count, i) {
- addr = sg_dma_address(sg);
- len = sg_dma_len(sg);
- rtsx_pci_add_sg_tbl(pcr, addr, len, i == count - 1);
- }
spin_lock_irqsave(&pcr->lock, flags);
pcr->done = &trans_done;
pcr->trans_result = TRANS_NOT_READY;
init_completion(&trans_done);
- rtsx_pci_writel(pcr, RTSX_HDBAR, pcr->host_sg_tbl_addr);
- rtsx_pci_writel(pcr, RTSX_HDBCTLR, val);
spin_unlock_irqrestore(&pcr->lock, flags);
+ rtsx_pci_dma_transfer(pcr, sglist, count, read);
+
timeleft = wait_for_completion_interruptible_timeout(
&trans_done, msecs_to_jiffies(timeout));
if (timeleft <= 0) {
@@ -413,7 +383,7 @@ out:
pcr->done = NULL;
spin_unlock_irqrestore(&pcr->lock, flags);
- dma_unmap_sg(&(pcr->pci->dev), sglist, num_sg, dma_dir);
+ rtsx_pci_dma_unmap_sg(pcr, sglist, num_sg, read);
if ((err < 0) && (err != -ENODEV))
rtsx_pci_stop_cmd(pcr);
@@ -425,6 +395,73 @@ out:
}
EXPORT_SYMBOL_GPL(rtsx_pci_transfer_data);
+int rtsx_pci_dma_map_sg(struct rtsx_pcr *pcr, struct scatterlist *sglist,
+ int num_sg, bool read)
+{
+ enum dma_data_direction dir = read ? DMA_FROM_DEVICE : DMA_TO_DEVICE;
+
+ if (pcr->remove_pci)
+ return -EINVAL;
+
+ if ((sglist == NULL) || num_sg < 1)
+ return -EINVAL;
+
+ return dma_map_sg(&(pcr->pci->dev), sglist, num_sg, dir);
+}
+EXPORT_SYMBOL_GPL(rtsx_pci_dma_map_sg);
+
+int rtsx_pci_dma_unmap_sg(struct rtsx_pcr *pcr, struct scatterlist *sglist,
+ int num_sg, bool read)
+{
+ enum dma_data_direction dir = read ? DMA_FROM_DEVICE : DMA_TO_DEVICE;
+
+ if (pcr->remove_pci)
+ return -EINVAL;
+
+ if (sglist == NULL || num_sg < 1)
+ return -EINVAL;
+
+ dma_unmap_sg(&(pcr->pci->dev), sglist, num_sg, dir);
+ return num_sg;
+}
+EXPORT_SYMBOL_GPL(rtsx_pci_dma_unmap_sg);
+
+int rtsx_pci_dma_transfer(struct rtsx_pcr *pcr, struct scatterlist *sglist,
+ int sg_count, bool read)
+{
+ struct scatterlist *sg;
+ dma_addr_t addr;
+ unsigned int len;
+ int i;
+ u32 val;
+ u8 dir = read ? DEVICE_TO_HOST : HOST_TO_DEVICE;
+ unsigned long flags;
+
+ if (pcr->remove_pci)
+ return -EINVAL;
+
+ if ((sglist == NULL) || (sg_count < 1))
+ return -EINVAL;
+
+ val = ((u32)(dir & 0x01) << 29) | TRIG_DMA | ADMA_MODE;
+ pcr->sgi = 0;
+ for_each_sg(sglist, sg, sg_count, i) {
+ addr = sg_dma_address(sg);
+ len = sg_dma_len(sg);
+ rtsx_pci_add_sg_tbl(pcr, addr, len, i == sg_count - 1);
+ }
+
+ spin_lock_irqsave(&pcr->lock, flags);
+
+ rtsx_pci_writel(pcr, RTSX_HDBAR, pcr->host_sg_tbl_addr);
+ rtsx_pci_writel(pcr, RTSX_HDBCTLR, val);
+
+ spin_unlock_irqrestore(&pcr->lock, flags);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(rtsx_pci_dma_transfer);
+
int rtsx_pci_read_ppbuf(struct rtsx_pcr *pcr, u8 *buf, int buf_len)
{
int err;
@@ -836,6 +873,8 @@ static irqreturn_t rtsx_pci_isr(int irq, void *dev_id)
int_reg = rtsx_pci_readl(pcr, RTSX_BIPR);
/* Clear interrupt flag */
rtsx_pci_writel(pcr, RTSX_BIPR, int_reg);
+ dev_dbg(&pcr->pci->dev, "=========== BIPR 0x%8x ==========\n", int_reg);
+
if ((int_reg & pcr->bier) == 0) {
spin_unlock(&pcr->lock);
return IRQ_NONE;
@@ -866,17 +905,28 @@ static irqreturn_t rtsx_pci_isr(int irq, void *dev_id)
}
if (int_reg & (NEED_COMPLETE_INT | DELINK_INT)) {
- if (int_reg & (TRANS_FAIL_INT | DELINK_INT)) {
+ if (int_reg & (TRANS_FAIL_INT | DELINK_INT))
pcr->trans_result = TRANS_RESULT_FAIL;
- if (pcr->done)
- complete(pcr->done);
- } else if (int_reg & TRANS_OK_INT) {
+ else if (int_reg & TRANS_OK_INT)
pcr->trans_result = TRANS_RESULT_OK;
- if (pcr->done)
- complete(pcr->done);
+
+ if (pcr->done)
+ complete(pcr->done);
+
+ if (int_reg & SD_EXIST) {
+ struct rtsx_slot *slot = &pcr->slots[RTSX_SD_CARD];
+ if (slot && slot->done_transfer)
+ slot->done_transfer(slot->p_dev);
+ }
+
+ if (int_reg & MS_EXIST) {
+ struct rtsx_slot *slot = &pcr->slots[RTSX_SD_CARD];
+ if (slot && slot->done_transfer)
+ slot->done_transfer(slot->p_dev);
}
}
+
if (pcr->card_inserted || pcr->card_removed)
schedule_delayed_work(&pcr->carddet_work,
msecs_to_jiffies(200));
diff --git a/drivers/mmc/card/block.c b/drivers/mmc/card/block.c
index 7b5424f398ac..452782bffebc 100644
--- a/drivers/mmc/card/block.c
+++ b/drivers/mmc/card/block.c
@@ -415,8 +415,7 @@ static int ioctl_do_sanitize(struct mmc_card *card)
{
int err;
- if (!(mmc_can_sanitize(card) &&
- (card->host->caps2 & MMC_CAP2_SANITIZE))) {
+ if (!mmc_can_sanitize(card)) {
pr_warn("%s: %s - SANITIZE is not supported\n",
mmc_hostname(card->host), __func__);
err = -EOPNOTSUPP;
@@ -722,19 +721,6 @@ static u32 mmc_sd_num_wr_blocks(struct mmc_card *card)
return result;
}
-static int send_stop(struct mmc_card *card, u32 *status)
-{
- struct mmc_command cmd = {0};
- int err;
-
- cmd.opcode = MMC_STOP_TRANSMISSION;
- cmd.flags = MMC_RSP_SPI_R1B | MMC_RSP_R1B | MMC_CMD_AC;
- err = mmc_wait_for_cmd(card->host, &cmd, 5);
- if (err == 0)
- *status = cmd.resp[0];
- return err;
-}
-
static int get_card_status(struct mmc_card *card, u32 *status, int retries)
{
struct mmc_command cmd = {0};
@@ -750,6 +736,99 @@ static int get_card_status(struct mmc_card *card, u32 *status, int retries)
return err;
}
+static int card_busy_detect(struct mmc_card *card, unsigned int timeout_ms,
+ bool hw_busy_detect, struct request *req, int *gen_err)
+{
+ unsigned long timeout = jiffies + msecs_to_jiffies(timeout_ms);
+ int err = 0;
+ u32 status;
+
+ do {
+ err = get_card_status(card, &status, 5);
+ if (err) {
+ pr_err("%s: error %d requesting status\n",
+ req->rq_disk->disk_name, err);
+ return err;
+ }
+
+ if (status & R1_ERROR) {
+ pr_err("%s: %s: error sending status cmd, status %#x\n",
+ req->rq_disk->disk_name, __func__, status);
+ *gen_err = 1;
+ }
+
+ /* We may rely on the host hw to handle busy detection.*/
+ if ((card->host->caps & MMC_CAP_WAIT_WHILE_BUSY) &&
+ hw_busy_detect)
+ break;
+
+ /*
+ * Timeout if the device never becomes ready for data and never
+ * leaves the program state.
+ */
+ if (time_after(jiffies, timeout)) {
+ pr_err("%s: Card stuck in programming state! %s %s\n",
+ mmc_hostname(card->host),
+ req->rq_disk->disk_name, __func__);
+ return -ETIMEDOUT;
+ }
+
+ /*
+ * Some cards mishandle the status bits,
+ * so make sure to check both the busy
+ * indication and the card state.
+ */
+ } while (!(status & R1_READY_FOR_DATA) ||
+ (R1_CURRENT_STATE(status) == R1_STATE_PRG));
+
+ return err;
+}
+
+static int send_stop(struct mmc_card *card, unsigned int timeout_ms,
+ struct request *req, int *gen_err, u32 *stop_status)
+{
+ struct mmc_host *host = card->host;
+ struct mmc_command cmd = {0};
+ int err;
+ bool use_r1b_resp = rq_data_dir(req) == WRITE;
+
+ /*
+ * Normally we use R1B responses for WRITE, but in cases where the host
+ * has specified a max_busy_timeout we need to validate it. A failure
+ * means we need to prevent the host from doing hw busy detection, which
+ * is done by converting to a R1 response instead.
+ */
+ if (host->max_busy_timeout && (timeout_ms > host->max_busy_timeout))
+ use_r1b_resp = false;
+
+ cmd.opcode = MMC_STOP_TRANSMISSION;
+ if (use_r1b_resp) {
+ cmd.flags = MMC_RSP_SPI_R1B | MMC_RSP_R1B | MMC_CMD_AC;
+ cmd.busy_timeout = timeout_ms;
+ } else {
+ cmd.flags = MMC_RSP_SPI_R1 | MMC_RSP_R1 | MMC_CMD_AC;
+ }
+
+ err = mmc_wait_for_cmd(host, &cmd, 5);
+ if (err)
+ return err;
+
+ *stop_status = cmd.resp[0];
+
+ /* No need to check card status in case of READ. */
+ if (rq_data_dir(req) == READ)
+ return 0;
+
+ if (!mmc_host_is_spi(host) &&
+ (*stop_status & R1_ERROR)) {
+ pr_err("%s: %s: general error sending stop command, resp %#x\n",
+ req->rq_disk->disk_name, __func__, *stop_status);
+ *gen_err = 1;
+ }
+
+ return card_busy_detect(card, timeout_ms, use_r1b_resp, req, gen_err);
+}
+
#define ERR_NOMEDIUM 3
#define ERR_RETRY 2
#define ERR_ABORT 1
@@ -866,26 +945,21 @@ static int mmc_blk_cmd_recovery(struct mmc_card *card, struct request *req,
*/
if (R1_CURRENT_STATE(status) == R1_STATE_DATA ||
R1_CURRENT_STATE(status) == R1_STATE_RCV) {
- err = send_stop(card, &stop_status);
- if (err)
+ err = send_stop(card,
+ DIV_ROUND_UP(brq->data.timeout_ns, 1000000),
+ req, gen_err, &stop_status);
+ if (err) {
pr_err("%s: error %d sending stop command\n",
req->rq_disk->disk_name, err);
-
- /*
- * If the stop cmd also timed out, the card is probably
- * not present, so abort. Other errors are bad news too.
- */
- if (err)
+ /*
+ * If the stop cmd also timed out, the card is probably
+ * not present, so abort. Other errors are bad news too.
+ */
return ERR_ABORT;
+ }
+
if (stop_status & R1_CARD_ECC_FAILED)
*ecc_err = 1;
- if (!mmc_host_is_spi(card->host) && rq_data_dir(req) != READ)
- if (stop_status & R1_ERROR) {
- pr_err("%s: %s: general error sending stop command, stop cmd response %#x\n",
- req->rq_disk->disk_name, __func__,
- stop_status);
- *gen_err = 1;
- }
}
/* Check for set block count errors */
@@ -1157,8 +1231,7 @@ static int mmc_blk_err_check(struct mmc_card *card,
* program mode, which we have to wait for it to complete.
*/
if (!mmc_host_is_spi(card->host) && rq_data_dir(req) != READ) {
- u32 status;
- unsigned long timeout;
+ int err;
/* Check stop command response */
if (brq->stop.resp[0] & R1_ERROR) {
@@ -1168,39 +1241,10 @@ static int mmc_blk_err_check(struct mmc_card *card,
gen_err = 1;
}
- timeout = jiffies + msecs_to_jiffies(MMC_BLK_TIMEOUT_MS);
- do {
- int err = get_card_status(card, &status, 5);
- if (err) {
- pr_err("%s: error %d requesting status\n",
- req->rq_disk->disk_name, err);
- return MMC_BLK_CMD_ERR;
- }
-
- if (status & R1_ERROR) {
- pr_err("%s: %s: general error sending status command, card status %#x\n",
- req->rq_disk->disk_name, __func__,
- status);
- gen_err = 1;
- }
-
- /* Timeout if the device never becomes ready for data
- * and never leaves the program state.
- */
- if (time_after(jiffies, timeout)) {
- pr_err("%s: Card stuck in programming state!"\
- " %s %s\n", mmc_hostname(card->host),
- req->rq_disk->disk_name, __func__);
-
- return MMC_BLK_CMD_ERR;
- }
- /*
- * Some cards mishandle the status bits,
- * so make sure to check both the busy
- * indication and the card state.
- */
- } while (!(status & R1_READY_FOR_DATA) ||
- (R1_CURRENT_STATE(status) == R1_STATE_PRG));
+ err = card_busy_detect(card, MMC_BLK_TIMEOUT_MS, false, req,
+ &gen_err);
+ if (err)
+ return MMC_BLK_CMD_ERR;
}
/* if general error occurs, retry the write operation. */
@@ -1335,7 +1379,6 @@ static void mmc_blk_rw_rq_prep(struct mmc_queue_req *mqrq,
brq->data.blksz = 512;
brq->stop.opcode = MMC_STOP_TRANSMISSION;
brq->stop.arg = 0;
- brq->stop.flags = MMC_RSP_SPI_R1B | MMC_RSP_R1B | MMC_CMD_AC;
brq->data.blocks = blk_rq_sectors(req);
/*
@@ -1378,9 +1421,15 @@ static void mmc_blk_rw_rq_prep(struct mmc_queue_req *mqrq,
if (rq_data_dir(req) == READ) {
brq->cmd.opcode = readcmd;
brq->data.flags |= MMC_DATA_READ;
+ if (brq->mrq.stop)
+ brq->stop.flags = MMC_RSP_SPI_R1 | MMC_RSP_R1 |
+ MMC_CMD_AC;
} else {
brq->cmd.opcode = writecmd;
brq->data.flags |= MMC_DATA_WRITE;
+ if (brq->mrq.stop)
+ brq->stop.flags = MMC_RSP_SPI_R1B | MMC_RSP_R1B |
+ MMC_CMD_AC;
}
if (do_rel_wr)
diff --git a/drivers/mmc/core/Kconfig b/drivers/mmc/core/Kconfig
index 269d072ef55e..9ebee72d9c3f 100644
--- a/drivers/mmc/core/Kconfig
+++ b/drivers/mmc/core/Kconfig
@@ -2,21 +2,6 @@
# MMC core configuration
#
-config MMC_UNSAFE_RESUME
- bool "Assume MMC/SD cards are non-removable (DANGEROUS)"
- help
- If you say Y here, the MMC layer will assume that all cards
- stayed in their respective slots during the suspend. The
- normal behaviour is to remove them at suspend and
- redetecting them at resume. Breaking this assumption will
- in most cases result in data corruption.
-
- This option is usually just for embedded systems which use
- a MMC/SD card for rootfs. Most people should say N here.
-
- This option sets a default which can be overridden by the
- module parameter "removable=0" or "removable=1".
-
config MMC_CLKGATE
bool "MMC host clock gating"
help
diff --git a/drivers/mmc/core/bus.c b/drivers/mmc/core/bus.c
index 64145a32b917..824644875d41 100644
--- a/drivers/mmc/core/bus.c
+++ b/drivers/mmc/core/bus.c
@@ -185,24 +185,16 @@ static int mmc_runtime_suspend(struct device *dev)
{
struct mmc_card *card = mmc_dev_to_card(dev);
struct mmc_host *host = card->host;
- int ret = 0;
- if (host->bus_ops->runtime_suspend)
- ret = host->bus_ops->runtime_suspend(host);
-
- return ret;
+ return host->bus_ops->runtime_suspend(host);
}
static int mmc_runtime_resume(struct device *dev)
{
struct mmc_card *card = mmc_dev_to_card(dev);
struct mmc_host *host = card->host;
- int ret = 0;
- if (host->bus_ops->runtime_resume)
- ret = host->bus_ops->runtime_resume(host);
-
- return ret;
+ return host->bus_ops->runtime_resume(host);
}
static int mmc_runtime_idle(struct device *dev)
diff --git a/drivers/mmc/core/core.c b/drivers/mmc/core/core.c
index 098374b1ab2b..acbc3f2aaaf9 100644
--- a/drivers/mmc/core/core.c
+++ b/drivers/mmc/core/core.c
@@ -34,6 +34,7 @@
#include <linux/mmc/host.h>
#include <linux/mmc/mmc.h>
#include <linux/mmc/sd.h>
+#include <linux/mmc/slot-gpio.h>
#include "core.h"
#include "bus.h"
@@ -65,23 +66,6 @@ bool use_spi_crc = 1;
module_param(use_spi_crc, bool, 0);
/*
- * We normally treat cards as removed during suspend if they are not
- * known to be on a non-removable bus, to avoid the risk of writing
- * back data to a different card after resume. Allow this to be
- * overridden if necessary.
- */
-#ifdef CONFIG_MMC_UNSAFE_RESUME
-bool mmc_assume_removable;
-#else
-bool mmc_assume_removable = 1;
-#endif
-EXPORT_SYMBOL(mmc_assume_removable);
-module_param_named(removable, mmc_assume_removable, bool, 0644);
-MODULE_PARM_DESC(
- removable,
- "MMC/SD cards are removable and may be removed during suspend");
-
-/*
* Internal function. Schedule delayed work in the MMC work queue.
*/
static int mmc_schedule_delayed_work(struct delayed_work *work,
@@ -302,7 +286,8 @@ void mmc_start_bkops(struct mmc_card *card, bool from_exception)
}
err = __mmc_switch(card, EXT_CSD_CMD_SET_NORMAL,
- EXT_CSD_BKOPS_START, 1, timeout, use_busy_signal, true);
+ EXT_CSD_BKOPS_START, 1, timeout,
+ use_busy_signal, true, false);
if (err) {
pr_warn("%s: Error %d starting bkops\n",
mmc_hostname(card->host), err);
@@ -1950,7 +1935,7 @@ static int mmc_do_erase(struct mmc_card *card, unsigned int from,
cmd.opcode = MMC_ERASE;
cmd.arg = arg;
cmd.flags = MMC_RSP_SPI_R1B | MMC_RSP_R1B | MMC_CMD_AC;
- cmd.cmd_timeout_ms = mmc_erase_timeout(card, arg, qty);
+ cmd.busy_timeout = mmc_erase_timeout(card, arg, qty);
err = mmc_wait_for_cmd(card->host, &cmd, 0);
if (err) {
pr_err("mmc_erase: erase error %d, status %#x\n",
@@ -2137,7 +2122,7 @@ static unsigned int mmc_do_calc_max_discard(struct mmc_card *card,
y = 0;
for (x = 1; x && x <= max_qty && max_qty - x >= qty; x <<= 1) {
timeout = mmc_erase_timeout(card, arg, qty + x);
- if (timeout > host->max_discard_to)
+ if (timeout > host->max_busy_timeout)
break;
if (timeout < last_timeout)
break;
@@ -2169,7 +2154,7 @@ unsigned int mmc_calc_max_discard(struct mmc_card *card)
struct mmc_host *host = card->host;
unsigned int max_discard, max_trim;
- if (!host->max_discard_to)
+ if (!host->max_busy_timeout)
return UINT_MAX;
/*
@@ -2189,7 +2174,7 @@ unsigned int mmc_calc_max_discard(struct mmc_card *card)
max_discard = 0;
}
pr_debug("%s: calculated max. discard sectors %u for timeout %u ms\n",
- mmc_hostname(host), max_discard, host->max_discard_to);
+ mmc_hostname(host), max_discard, host->max_busy_timeout);
return max_discard;
}
EXPORT_SYMBOL(mmc_calc_max_discard);
@@ -2248,9 +2233,6 @@ static int mmc_do_hw_reset(struct mmc_host *host, int check)
{
struct mmc_card *card = host->card;
- if (!host->bus_ops->power_restore)
- return -EOPNOTSUPP;
-
if (!(host->caps & MMC_CAP_HW_RESET) || !host->ops->hw_reset)
return -EOPNOTSUPP;
@@ -2352,7 +2334,7 @@ int _mmc_detect_card_removed(struct mmc_host *host)
{
int ret;
- if ((host->caps & MMC_CAP_NONREMOVABLE) || !host->bus_ops->alive)
+ if (host->caps & MMC_CAP_NONREMOVABLE)
return 0;
if (!host->card || mmc_card_removed(host->card))
@@ -2435,7 +2417,7 @@ void mmc_rescan(struct work_struct *work)
* if there is a _removable_ card registered, check whether it is
* still present
*/
- if (host->bus_ops && host->bus_ops->detect && !host->bus_dead
+ if (host->bus_ops && !host->bus_dead
&& !(host->caps & MMC_CAP_NONREMOVABLE))
host->bus_ops->detect(host);
@@ -2490,6 +2472,7 @@ void mmc_start_host(struct mmc_host *host)
mmc_power_off(host);
else
mmc_power_up(host, host->ocr_avail);
+ mmc_gpiod_request_cd_irq(host);
_mmc_detect_change(host, 0, false);
}
@@ -2501,6 +2484,8 @@ void mmc_stop_host(struct mmc_host *host)
host->removed = 1;
spin_unlock_irqrestore(&host->lock, flags);
#endif
+ if (host->slot.cd_irq >= 0)
+ disable_irq(host->slot.cd_irq);
host->rescan_disable = 1;
cancel_delayed_work_sync(&host->detect);
@@ -2537,7 +2522,7 @@ int mmc_power_save_host(struct mmc_host *host)
mmc_bus_get(host);
- if (!host->bus_ops || host->bus_dead || !host->bus_ops->power_restore) {
+ if (!host->bus_ops || host->bus_dead) {
mmc_bus_put(host);
return -EINVAL;
}
@@ -2563,7 +2548,7 @@ int mmc_power_restore_host(struct mmc_host *host)
mmc_bus_get(host);
- if (!host->bus_ops || host->bus_dead || !host->bus_ops->power_restore) {
+ if (!host->bus_ops || host->bus_dead) {
mmc_bus_put(host);
return -EINVAL;
}
@@ -2582,12 +2567,8 @@ EXPORT_SYMBOL(mmc_power_restore_host);
*/
int mmc_flush_cache(struct mmc_card *card)
{
- struct mmc_host *host = card->host;
int err = 0;
- if (!(host->caps2 & MMC_CAP2_CACHE_CTRL))
- return err;
-
if (mmc_card_mmc(card) &&
(card->ext_csd.cache_size > 0) &&
(card->ext_csd.cache_ctrl & 1)) {
@@ -2602,44 +2583,6 @@ int mmc_flush_cache(struct mmc_card *card)
}
EXPORT_SYMBOL(mmc_flush_cache);
-/*
- * Turn the cache ON/OFF.
- * Turning the cache OFF shall trigger flushing of the data
- * to the non-volatile storage.
- * This function should be called with host claimed
- */
-int mmc_cache_ctrl(struct mmc_host *host, u8 enable)
-{
- struct mmc_card *card = host->card;
- unsigned int timeout;
- int err = 0;
-
- if (!(host->caps2 & MMC_CAP2_CACHE_CTRL) ||
- mmc_card_is_removable(host))
- return err;
-
- if (card && mmc_card_mmc(card) &&
- (card->ext_csd.cache_size > 0)) {
- enable = !!enable;
-
- if (card->ext_csd.cache_ctrl ^ enable) {
- timeout = enable ? card->ext_csd.generic_cmd6_time : 0;
- err = mmc_switch(card, EXT_CSD_CMD_SET_NORMAL,
- EXT_CSD_CACHE_CTRL, enable, timeout);
- if (err)
- pr_err("%s: cache %s error %d\n",
- mmc_hostname(card->host),
- enable ? "on" : "off",
- err);
- else
- card->ext_csd.cache_ctrl = enable;
- }
- }
-
- return err;
-}
-EXPORT_SYMBOL(mmc_cache_ctrl);
-
#ifdef CONFIG_PM
/* Do the card removal on suspend if card is assumed removeable
@@ -2668,7 +2611,7 @@ int mmc_pm_notify(struct notifier_block *notify_block,
/* Validate prerequisites for suspend */
if (host->bus_ops->pre_suspend)
err = host->bus_ops->pre_suspend(host);
- if (!err && host->bus_ops->suspend)
+ if (!err)
break;
/* Calling bus_ops->remove() with a claimed host can deadlock */
diff --git a/drivers/mmc/core/host.c b/drivers/mmc/core/host.c
index 114f6bdfbef3..fdea825dbb24 100644
--- a/drivers/mmc/core/host.c
+++ b/drivers/mmc/core/host.c
@@ -419,6 +419,16 @@ int mmc_of_parse(struct mmc_host *host)
host->caps |= MMC_CAP_SD_HIGHSPEED;
if (of_find_property(np, "cap-mmc-highspeed", &len))
host->caps |= MMC_CAP_MMC_HIGHSPEED;
+ if (of_find_property(np, "sd-uhs-sdr12", &len))
+ host->caps |= MMC_CAP_UHS_SDR12;
+ if (of_find_property(np, "sd-uhs-sdr25", &len))
+ host->caps |= MMC_CAP_UHS_SDR25;
+ if (of_find_property(np, "sd-uhs-sdr50", &len))
+ host->caps |= MMC_CAP_UHS_SDR50;
+ if (of_find_property(np, "sd-uhs-sdr104", &len))
+ host->caps |= MMC_CAP_UHS_SDR104;
+ if (of_find_property(np, "sd-uhs-ddr50", &len))
+ host->caps |= MMC_CAP_UHS_DDR50;
if (of_find_property(np, "cap-power-off-card", &len))
host->caps |= MMC_CAP_POWER_OFF_CARD;
if (of_find_property(np, "cap-sdio-irq", &len))
@@ -429,6 +439,14 @@ int mmc_of_parse(struct mmc_host *host)
host->pm_caps |= MMC_PM_KEEP_POWER;
if (of_find_property(np, "enable-sdio-wakeup", &len))
host->pm_caps |= MMC_PM_WAKE_SDIO_IRQ;
+ if (of_find_property(np, "mmc-ddr-1_8v", &len))
+ host->caps |= MMC_CAP_1_8V_DDR;
+ if (of_find_property(np, "mmc-ddr-1_2v", &len))
+ host->caps |= MMC_CAP_1_2V_DDR;
+ if (of_find_property(np, "mmc-hs200-1_8v", &len))
+ host->caps2 |= MMC_CAP2_HS200_1_8V_SDR;
+ if (of_find_property(np, "mmc-hs200-1_2v", &len))
+ host->caps2 |= MMC_CAP2_HS200_1_2V_SDR;
return 0;
diff --git a/drivers/mmc/core/mmc.c b/drivers/mmc/core/mmc.c
index 98e9eb0f6643..1ab5f3a0af5b 100644
--- a/drivers/mmc/core/mmc.c
+++ b/drivers/mmc/core/mmc.c
@@ -856,8 +856,10 @@ static int mmc_select_hs200(struct mmc_card *card)
/* switch to HS200 mode if bus width set successfully */
if (!err)
- err = mmc_switch(card, EXT_CSD_CMD_SET_NORMAL,
- EXT_CSD_HS_TIMING, 2, 0);
+ err = __mmc_switch(card, EXT_CSD_CMD_SET_NORMAL,
+ EXT_CSD_HS_TIMING, 2,
+ card->ext_csd.generic_cmd6_time,
+ true, true, true);
err:
return err;
}
@@ -1074,9 +1076,10 @@ static int mmc_init_card(struct mmc_host *host, u32 ocr,
host->caps2 & MMC_CAP2_HS200)
err = mmc_select_hs200(card);
else if (host->caps & MMC_CAP_MMC_HIGHSPEED)
- err = mmc_switch(card, EXT_CSD_CMD_SET_NORMAL,
- EXT_CSD_HS_TIMING, 1,
- card->ext_csd.generic_cmd6_time);
+ err = __mmc_switch(card, EXT_CSD_CMD_SET_NORMAL,
+ EXT_CSD_HS_TIMING, 1,
+ card->ext_csd.generic_cmd6_time,
+ true, true, true);
if (err && err != -EBADMSG)
goto free_card;
@@ -1287,8 +1290,7 @@ static int mmc_init_card(struct mmc_host *host, u32 ocr,
* If cache size is higher than 0, this indicates
* the existence of cache and it can be turned on.
*/
- if ((host->caps2 & MMC_CAP2_CACHE_CTRL) &&
- card->ext_csd.cache_size > 0) {
+ if (card->ext_csd.cache_size > 0) {
err = mmc_switch(card, EXT_CSD_CMD_SET_NORMAL,
EXT_CSD_CACHE_CTRL, 1,
card->ext_csd.generic_cmd6_time);
@@ -1356,11 +1358,9 @@ static int mmc_sleep(struct mmc_host *host)
{
struct mmc_command cmd = {0};
struct mmc_card *card = host->card;
+ unsigned int timeout_ms = DIV_ROUND_UP(card->ext_csd.sa_timeout, 10000);
int err;
- if (host->caps2 & MMC_CAP2_NO_SLEEP_CMD)
- return 0;
-
err = mmc_deselect_cards(host);
if (err)
return err;
@@ -1369,7 +1369,19 @@ static int mmc_sleep(struct mmc_host *host)
cmd.arg = card->rca << 16;
cmd.arg |= 1 << 15;
- cmd.flags = MMC_RSP_R1B | MMC_CMD_AC;
+ /*
+ * If the max_busy_timeout of the host is specified, validate it against
+ * the sleep cmd timeout. A failure means we need to prevent the host
+ * from doing hw busy detection, which is done by converting to a R1
+ * response instead of a R1B.
+ */
+ if (host->max_busy_timeout && (timeout_ms > host->max_busy_timeout)) {
+ cmd.flags = MMC_RSP_R1 | MMC_CMD_AC;
+ } else {
+ cmd.flags = MMC_RSP_R1B | MMC_CMD_AC;
+ cmd.busy_timeout = timeout_ms;
+ }
+
err = mmc_wait_for_cmd(host, &cmd, 0);
if (err)
return err;
@@ -1380,8 +1392,8 @@ static int mmc_sleep(struct mmc_host *host)
* SEND_STATUS command to poll the status because that command (and most
* others) is invalid while the card sleeps.
*/
- if (!(host->caps & MMC_CAP_WAIT_WHILE_BUSY))
- mmc_delay(DIV_ROUND_UP(card->ext_csd.sa_timeout, 10000));
+ if (!cmd.busy_timeout || !(host->caps & MMC_CAP_WAIT_WHILE_BUSY))
+ mmc_delay(timeout_ms);
return err;
}
@@ -1404,7 +1416,7 @@ static int mmc_poweroff_notify(struct mmc_card *card, unsigned int notify_type)
err = __mmc_switch(card, EXT_CSD_CMD_SET_NORMAL,
EXT_CSD_POWER_OFF_NOTIFICATION,
- notify_type, timeout, true, false);
+ notify_type, timeout, true, false, false);
if (err)
pr_err("%s: Power Off Notification timed out, %u\n",
mmc_hostname(card->host), timeout);
@@ -1484,7 +1496,7 @@ static int _mmc_suspend(struct mmc_host *host, bool is_suspend)
goto out;
}
- err = mmc_cache_ctrl(host, 0);
+ err = mmc_flush_cache(host->card);
if (err)
goto out;
@@ -1636,16 +1648,6 @@ static int mmc_power_restore(struct mmc_host *host)
static const struct mmc_bus_ops mmc_ops = {
.remove = mmc_remove,
.detect = mmc_detect,
- .suspend = NULL,
- .resume = NULL,
- .power_restore = mmc_power_restore,
- .alive = mmc_alive,
- .shutdown = mmc_shutdown,
-};
-
-static const struct mmc_bus_ops mmc_ops_unsafe = {
- .remove = mmc_remove,
- .detect = mmc_detect,
.suspend = mmc_suspend,
.resume = mmc_resume,
.runtime_suspend = mmc_runtime_suspend,
@@ -1655,17 +1657,6 @@ static const struct mmc_bus_ops mmc_ops_unsafe = {
.shutdown = mmc_shutdown,
};
-static void mmc_attach_bus_ops(struct mmc_host *host)
-{
- const struct mmc_bus_ops *bus_ops;
-
- if (!mmc_card_is_removable(host))
- bus_ops = &mmc_ops_unsafe;
- else
- bus_ops = &mmc_ops;
- mmc_attach_bus(host, bus_ops);
-}
-
/*
* Starting point for MMC card init.
*/
@@ -1685,7 +1676,7 @@ int mmc_attach_mmc(struct mmc_host *host)
if (err)
return err;
- mmc_attach_bus_ops(host);
+ mmc_attach_bus(host, &mmc_ops);
if (host->ocr_avail_mmc)
host->ocr_avail = host->ocr_avail_mmc;
diff --git a/drivers/mmc/core/mmc_ops.c b/drivers/mmc/core/mmc_ops.c
index e5b5eeb548d1..f51b5ba3bbea 100644
--- a/drivers/mmc/core/mmc_ops.c
+++ b/drivers/mmc/core/mmc_ops.c
@@ -405,20 +405,30 @@ int mmc_spi_set_crc(struct mmc_host *host, int use_crc)
* timeout of zero implies maximum possible timeout
* @use_busy_signal: use the busy signal as response type
* @send_status: send status cmd to poll for busy
+ * @ignore_crc: ignore CRC errors when sending status cmd to poll for busy
*
* Modifies the EXT_CSD register for selected card.
*/
int __mmc_switch(struct mmc_card *card, u8 set, u8 index, u8 value,
- unsigned int timeout_ms, bool use_busy_signal, bool send_status)
+ unsigned int timeout_ms, bool use_busy_signal, bool send_status,
+ bool ignore_crc)
{
+ struct mmc_host *host = card->host;
int err;
struct mmc_command cmd = {0};
unsigned long timeout;
u32 status = 0;
- bool ignore_crc = false;
+ bool use_r1b_resp = use_busy_signal;
- BUG_ON(!card);
- BUG_ON(!card->host);
+ /*
+ * If the cmd timeout and the max_busy_timeout of the host are both
+ * specified, let's validate them. A failure means we need to prevent
+ * the host from doing hw busy detection, which is done by converting
+ * to a R1 response instead of a R1B.
+ */
+ if (timeout_ms && host->max_busy_timeout &&
+ (timeout_ms > host->max_busy_timeout))
+ use_r1b_resp = false;
cmd.opcode = MMC_SWITCH;
cmd.arg = (MMC_SWITCH_MODE_WRITE_BYTE << 24) |
@@ -426,17 +436,21 @@ int __mmc_switch(struct mmc_card *card, u8 set, u8 index, u8 value,
(value << 8) |
set;
cmd.flags = MMC_CMD_AC;
- if (use_busy_signal)
+ if (use_r1b_resp) {
cmd.flags |= MMC_RSP_SPI_R1B | MMC_RSP_R1B;
- else
+ /*
+ * A busy_timeout of zero means the host can decide to use
+ * whatever value it finds suitable.
+ */
+ cmd.busy_timeout = timeout_ms;
+ } else {
cmd.flags |= MMC_RSP_SPI_R1 | MMC_RSP_R1;
+ }
-
- cmd.cmd_timeout_ms = timeout_ms;
if (index == EXT_CSD_SANITIZE_START)
cmd.sanitize_busy = true;
- err = mmc_wait_for_cmd(card->host, &cmd, MMC_CMD_RETRIES);
+ err = mmc_wait_for_cmd(host, &cmd, MMC_CMD_RETRIES);
if (err)
return err;
@@ -445,24 +459,27 @@ int __mmc_switch(struct mmc_card *card, u8 set, u8 index, u8 value,
return 0;
/*
- * Must check status to be sure of no errors
- * If CMD13 is to check the busy completion of the timing change,
- * disable the check of CRC error.
+ * CRC errors shall only be ignored in cases were CMD13 is used to poll
+ * to detect busy completion.
*/
- if (index == EXT_CSD_HS_TIMING &&
- !(card->host->caps & MMC_CAP_WAIT_WHILE_BUSY))
- ignore_crc = true;
+ if ((host->caps & MMC_CAP_WAIT_WHILE_BUSY) && use_r1b_resp)
+ ignore_crc = false;
+
+ /* We have an unspecified cmd timeout, use the fallback value. */
+ if (!timeout_ms)
+ timeout_ms = MMC_OPS_TIMEOUT_MS;
- timeout = jiffies + msecs_to_jiffies(MMC_OPS_TIMEOUT_MS);
+ /* Must check status to be sure of no errors. */
+ timeout = jiffies + msecs_to_jiffies(timeout_ms);
do {
if (send_status) {
err = __mmc_send_status(card, &status, ignore_crc);
if (err)
return err;
}
- if (card->host->caps & MMC_CAP_WAIT_WHILE_BUSY)
+ if ((host->caps & MMC_CAP_WAIT_WHILE_BUSY) && use_r1b_resp)
break;
- if (mmc_host_is_spi(card->host))
+ if (mmc_host_is_spi(host))
break;
/*
@@ -478,18 +495,18 @@ int __mmc_switch(struct mmc_card *card, u8 set, u8 index, u8 value,
/* Timeout if the device never leaves the program state. */
if (time_after(jiffies, timeout)) {
pr_err("%s: Card stuck in programming state! %s\n",
- mmc_hostname(card->host), __func__);
+ mmc_hostname(host), __func__);
return -ETIMEDOUT;
}
} while (R1_CURRENT_STATE(status) == R1_STATE_PRG);
- if (mmc_host_is_spi(card->host)) {
+ if (mmc_host_is_spi(host)) {
if (status & R1_SPI_ILLEGAL_COMMAND)
return -EBADMSG;
} else {
if (status & 0xFDFFA000)
- pr_warning("%s: unexpected status %#x after "
- "switch", mmc_hostname(card->host), status);
+ pr_warn("%s: unexpected status %#x after switch\n",
+ mmc_hostname(host), status);
if (status & R1_SWITCH_ERROR)
return -EBADMSG;
}
@@ -501,7 +518,8 @@ EXPORT_SYMBOL_GPL(__mmc_switch);
int mmc_switch(struct mmc_card *card, u8 set, u8 index, u8 value,
unsigned int timeout_ms)
{
- return __mmc_switch(card, set, index, value, timeout_ms, true, true);
+ return __mmc_switch(card, set, index, value, timeout_ms, true, true,
+ false);
}
EXPORT_SYMBOL_GPL(mmc_switch);
diff --git a/drivers/mmc/core/sd.c b/drivers/mmc/core/sd.c
index 692fdb177294..2dd359d2242f 100644
--- a/drivers/mmc/core/sd.c
+++ b/drivers/mmc/core/sd.c
@@ -1209,16 +1209,6 @@ static int mmc_sd_power_restore(struct mmc_host *host)
static const struct mmc_bus_ops mmc_sd_ops = {
.remove = mmc_sd_remove,
.detect = mmc_sd_detect,
- .suspend = NULL,
- .resume = NULL,
- .power_restore = mmc_sd_power_restore,
- .alive = mmc_sd_alive,
- .shutdown = mmc_sd_suspend,
-};
-
-static const struct mmc_bus_ops mmc_sd_ops_unsafe = {
- .remove = mmc_sd_remove,
- .detect = mmc_sd_detect,
.runtime_suspend = mmc_sd_runtime_suspend,
.runtime_resume = mmc_sd_runtime_resume,
.suspend = mmc_sd_suspend,
@@ -1228,17 +1218,6 @@ static const struct mmc_bus_ops mmc_sd_ops_unsafe = {
.shutdown = mmc_sd_suspend,
};
-static void mmc_sd_attach_bus_ops(struct mmc_host *host)
-{
- const struct mmc_bus_ops *bus_ops;
-
- if (!mmc_card_is_removable(host))
- bus_ops = &mmc_sd_ops_unsafe;
- else
- bus_ops = &mmc_sd_ops;
- mmc_attach_bus(host, bus_ops);
-}
-
/*
* Starting point for SD card init.
*/
@@ -1254,7 +1233,7 @@ int mmc_attach_sd(struct mmc_host *host)
if (err)
return err;
- mmc_sd_attach_bus_ops(host);
+ mmc_attach_bus(host, &mmc_sd_ops);
if (host->ocr_avail_sd)
host->ocr_avail = host->ocr_avail_sd;
diff --git a/drivers/mmc/core/slot-gpio.c b/drivers/mmc/core/slot-gpio.c
index 46596b71a32f..f7650b899e3d 100644
--- a/drivers/mmc/core/slot-gpio.c
+++ b/drivers/mmc/core/slot-gpio.c
@@ -10,6 +10,7 @@
#include <linux/err.h>
#include <linux/gpio.h>
+#include <linux/gpio/consumer.h>
#include <linux/interrupt.h>
#include <linux/jiffies.h>
#include <linux/mmc/host.h>
@@ -18,8 +19,10 @@
#include <linux/slab.h>
struct mmc_gpio {
- int ro_gpio;
- int cd_gpio;
+ struct gpio_desc *ro_gpio;
+ struct gpio_desc *cd_gpio;
+ bool override_ro_active_level;
+ bool override_cd_active_level;
char *ro_label;
char cd_label[0];
};
@@ -57,8 +60,6 @@ static int mmc_gpio_alloc(struct mmc_host *host)
ctx->ro_label = ctx->cd_label + len;
snprintf(ctx->cd_label, len, "%s cd", dev_name(host->parent));
snprintf(ctx->ro_label, len, "%s ro", dev_name(host->parent));
- ctx->cd_gpio = -EINVAL;
- ctx->ro_gpio = -EINVAL;
host->slot.handler_priv = ctx;
}
}
@@ -72,11 +73,14 @@ int mmc_gpio_get_ro(struct mmc_host *host)
{
struct mmc_gpio *ctx = host->slot.handler_priv;
- if (!ctx || !gpio_is_valid(ctx->ro_gpio))
+ if (!ctx || !ctx->ro_gpio)
return -ENOSYS;
- return !gpio_get_value_cansleep(ctx->ro_gpio) ^
- !!(host->caps2 & MMC_CAP2_RO_ACTIVE_HIGH);
+ if (ctx->override_ro_active_level)
+ return !gpiod_get_raw_value_cansleep(ctx->ro_gpio) ^
+ !!(host->caps2 & MMC_CAP2_RO_ACTIVE_HIGH);
+
+ return gpiod_get_value_cansleep(ctx->ro_gpio);
}
EXPORT_SYMBOL(mmc_gpio_get_ro);
@@ -84,11 +88,14 @@ int mmc_gpio_get_cd(struct mmc_host *host)
{
struct mmc_gpio *ctx = host->slot.handler_priv;
- if (!ctx || !gpio_is_valid(ctx->cd_gpio))
+ if (!ctx || !ctx->cd_gpio)
return -ENOSYS;
- return !gpio_get_value_cansleep(ctx->cd_gpio) ^
- !!(host->caps2 & MMC_CAP2_CD_ACTIVE_HIGH);
+ if (ctx->override_cd_active_level)
+ return !gpiod_get_raw_value_cansleep(ctx->cd_gpio) ^
+ !!(host->caps2 & MMC_CAP2_CD_ACTIVE_HIGH);
+
+ return gpiod_get_value_cansleep(ctx->cd_gpio);
}
EXPORT_SYMBOL(mmc_gpio_get_cd);
@@ -125,12 +132,47 @@ int mmc_gpio_request_ro(struct mmc_host *host, unsigned int gpio)
if (ret < 0)
return ret;
- ctx->ro_gpio = gpio;
+ ctx->override_ro_active_level = true;
+ ctx->ro_gpio = gpio_to_desc(gpio);
return 0;
}
EXPORT_SYMBOL(mmc_gpio_request_ro);
+void mmc_gpiod_request_cd_irq(struct mmc_host *host)
+{
+ struct mmc_gpio *ctx = host->slot.handler_priv;
+ int ret, irq;
+
+ if (host->slot.cd_irq >= 0 || !ctx || !ctx->cd_gpio)
+ return;
+
+ irq = gpiod_to_irq(ctx->cd_gpio);
+
+ /*
+ * Even if gpiod_to_irq() returns a valid IRQ number, the platform might
+ * still prefer to poll, e.g., because that IRQ number is already used
+ * by another unit and cannot be shared.
+ */
+ if (irq >= 0 && host->caps & MMC_CAP_NEEDS_POLL)
+ irq = -EINVAL;
+
+ if (irq >= 0) {
+ ret = devm_request_threaded_irq(&host->class_dev, irq,
+ NULL, mmc_gpio_cd_irqt,
+ IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING | IRQF_ONESHOT,
+ ctx->cd_label, host);
+ if (ret < 0)
+ irq = ret;
+ }
+
+ host->slot.cd_irq = irq;
+
+ if (irq < 0)
+ host->caps |= MMC_CAP_NEEDS_POLL;
+}
+EXPORT_SYMBOL(mmc_gpiod_request_cd_irq);
+
/**
* mmc_gpio_request_cd - request a gpio for card-detection
* @host: mmc host
@@ -154,7 +196,6 @@ int mmc_gpio_request_cd(struct mmc_host *host, unsigned int gpio,
unsigned int debounce)
{
struct mmc_gpio *ctx;
- int irq = gpio_to_irq(gpio);
int ret;
ret = mmc_gpio_alloc(host);
@@ -179,29 +220,10 @@ int mmc_gpio_request_cd(struct mmc_host *host, unsigned int gpio,
return ret;
}
- /*
- * Even if gpio_to_irq() returns a valid IRQ number, the platform might
- * still prefer to poll, e.g., because that IRQ number is already used
- * by another unit and cannot be shared.
- */
- if (irq >= 0 && host->caps & MMC_CAP_NEEDS_POLL)
- irq = -EINVAL;
-
- if (irq >= 0) {
- ret = devm_request_threaded_irq(&host->class_dev, irq,
- NULL, mmc_gpio_cd_irqt,
- IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING | IRQF_ONESHOT,
- ctx->cd_label, host);
- if (ret < 0)
- irq = ret;
- }
-
- host->slot.cd_irq = irq;
-
- if (irq < 0)
- host->caps |= MMC_CAP_NEEDS_POLL;
+ ctx->override_cd_active_level = true;
+ ctx->cd_gpio = gpio_to_desc(gpio);
- ctx->cd_gpio = gpio;
+ mmc_gpiod_request_cd_irq(host);
return 0;
}
@@ -219,11 +241,11 @@ void mmc_gpio_free_ro(struct mmc_host *host)
struct mmc_gpio *ctx = host->slot.handler_priv;
int gpio;
- if (!ctx || !gpio_is_valid(ctx->ro_gpio))
+ if (!ctx || !ctx->ro_gpio)
return;
- gpio = ctx->ro_gpio;
- ctx->ro_gpio = -EINVAL;
+ gpio = desc_to_gpio(ctx->ro_gpio);
+ ctx->ro_gpio = NULL;
devm_gpio_free(&host->class_dev, gpio);
}
@@ -241,7 +263,7 @@ void mmc_gpio_free_cd(struct mmc_host *host)
struct mmc_gpio *ctx = host->slot.handler_priv;
int gpio;
- if (!ctx || !gpio_is_valid(ctx->cd_gpio))
+ if (!ctx || !ctx->cd_gpio)
return;
if (host->slot.cd_irq >= 0) {
@@ -249,9 +271,87 @@ void mmc_gpio_free_cd(struct mmc_host *host)
host->slot.cd_irq = -EINVAL;
}
- gpio = ctx->cd_gpio;
- ctx->cd_gpio = -EINVAL;
+ gpio = desc_to_gpio(ctx->cd_gpio);
+ ctx->cd_gpio = NULL;
devm_gpio_free(&host->class_dev, gpio);
}
EXPORT_SYMBOL(mmc_gpio_free_cd);
+
+/**
+ * mmc_gpiod_request_cd - request a gpio descriptor for card-detection
+ * @host: mmc host
+ * @con_id: function within the GPIO consumer
+ * @idx: index of the GPIO to obtain in the consumer
+ * @override_active_level: ignore %GPIO_ACTIVE_LOW flag
+ * @debounce: debounce time in microseconds
+ *
+ * Use this function in place of mmc_gpio_request_cd() to use the GPIO
+ * descriptor API. Note that it is paired with mmc_gpiod_free_cd() not
+ * mmc_gpio_free_cd(). Note also that it must be called prior to mmc_add_host()
+ * otherwise the caller must also call mmc_gpiod_request_cd_irq().
+ *
+ * Returns zero on success, else an error.
+ */
+int mmc_gpiod_request_cd(struct mmc_host *host, const char *con_id,
+ unsigned int idx, bool override_active_level,
+ unsigned int debounce)
+{
+ struct mmc_gpio *ctx;
+ struct gpio_desc *desc;
+ int ret;
+
+ ret = mmc_gpio_alloc(host);
+ if (ret < 0)
+ return ret;
+
+ ctx = host->slot.handler_priv;
+
+ if (!con_id)
+ con_id = ctx->cd_label;
+
+ desc = devm_gpiod_get_index(host->parent, con_id, idx);
+ if (IS_ERR(desc))
+ return PTR_ERR(desc);
+
+ ret = gpiod_direction_input(desc);
+ if (ret < 0)
+ return ret;
+
+ if (debounce) {
+ ret = gpiod_set_debounce(desc, debounce);
+ if (ret < 0)
+ return ret;
+ }
+
+ ctx->override_cd_active_level = override_active_level;
+ ctx->cd_gpio = desc;
+
+ return 0;
+}
+EXPORT_SYMBOL(mmc_gpiod_request_cd);
+
+/**
+ * mmc_gpiod_free_cd - free the card-detection gpio descriptor
+ * @host: mmc host
+ *
+ * It's provided only for cases that client drivers need to manually free
+ * up the card-detection gpio requested by mmc_gpiod_request_cd().
+ */
+void mmc_gpiod_free_cd(struct mmc_host *host)
+{
+ struct mmc_gpio *ctx = host->slot.handler_priv;
+
+ if (!ctx || !ctx->cd_gpio)
+ return;
+
+ if (host->slot.cd_irq >= 0) {
+ devm_free_irq(&host->class_dev, host->slot.cd_irq, host);
+ host->slot.cd_irq = -EINVAL;
+ }
+
+ devm_gpiod_put(&host->class_dev, ctx->cd_gpio);
+
+ ctx->cd_gpio = NULL;
+}
+EXPORT_SYMBOL(mmc_gpiod_free_cd);
diff --git a/drivers/mmc/host/Kconfig b/drivers/mmc/host/Kconfig
index 1384f67abe21..8aaf8c1f3f63 100644
--- a/drivers/mmc/host/Kconfig
+++ b/drivers/mmc/host/Kconfig
@@ -263,7 +263,7 @@ config MMC_SDHCI_S3C_DMA
config MMC_SDHCI_BCM_KONA
tristate "SDHCI support on Broadcom KONA platform"
- depends on ARCH_BCM
+ depends on ARCH_BCM_MOBILE
select MMC_SDHCI_PLTFM
help
This selects the Broadcom Kona Secure Digital Host Controller
@@ -334,6 +334,19 @@ config MMC_ATMELMCI
If unsure, say N.
+config MMC_SDHCI_MSM
+ tristate "Qualcomm SDHCI Controller Support"
+ depends on ARCH_QCOM
+ depends on MMC_SDHCI_PLTFM
+ help
+ This selects the Secure Digital Host Controller Interface (SDHCI)
+ support present in Qualcomm SOCs. The controller supports
+ SD/MMC/SDIO devices.
+
+ If you have a controller with this interface, say Y or M here.
+
+ If unsure, say N.
+
config MMC_MSM
tristate "Qualcomm SDCC Controller Support"
depends on MMC && (ARCH_MSM7X00A || ARCH_MSM7X30 || ARCH_QSD8X50)
@@ -580,14 +593,6 @@ config MMC_DW_EXYNOS
Synopsys DesignWare Memory Card Interface driver. Select this option
for platforms based on Exynos4 and Exynos5 SoC's.
-config MMC_DW_SOCFPGA
- tristate "SOCFPGA specific extensions for Synopsys DW Memory Card Interface"
- depends on MMC_DW && MFD_SYSCON
- select MMC_DW_PLTFM
- help
- This selects support for Altera SoCFPGA specific extensions to the
- Synopsys DesignWare Memory Card Interface driver.
-
config MMC_DW_K3
tristate "K3 specific extensions for Synopsys DW Memory Card Interface"
depends on MMC_DW
diff --git a/drivers/mmc/host/Makefile b/drivers/mmc/host/Makefile
index 3483b6b6b880..0c8aa5e1e304 100644
--- a/drivers/mmc/host/Makefile
+++ b/drivers/mmc/host/Makefile
@@ -43,7 +43,6 @@ obj-$(CONFIG_SDH_BFIN) += bfin_sdh.o
obj-$(CONFIG_MMC_DW) += dw_mmc.o
obj-$(CONFIG_MMC_DW_PLTFM) += dw_mmc-pltfm.o
obj-$(CONFIG_MMC_DW_EXYNOS) += dw_mmc-exynos.o
-obj-$(CONFIG_MMC_DW_SOCFPGA) += dw_mmc-socfpga.o
obj-$(CONFIG_MMC_DW_K3) += dw_mmc-k3.o
obj-$(CONFIG_MMC_DW_PCI) += dw_mmc-pci.o
obj-$(CONFIG_MMC_SH_MMCIF) += sh_mmcif.o
@@ -64,6 +63,7 @@ obj-$(CONFIG_MMC_SDHCI_OF_ESDHC) += sdhci-of-esdhc.o
obj-$(CONFIG_MMC_SDHCI_OF_HLWD) += sdhci-of-hlwd.o
obj-$(CONFIG_MMC_SDHCI_BCM_KONA) += sdhci-bcm-kona.o
obj-$(CONFIG_MMC_SDHCI_BCM2835) += sdhci-bcm2835.o
+obj-$(CONFIG_MMC_SDHCI_MSM) += sdhci-msm.o
ifeq ($(CONFIG_CB710_DEBUG),y)
CFLAGS-cb710-mmc += -DDEBUG
diff --git a/drivers/mmc/host/davinci_mmc.c b/drivers/mmc/host/davinci_mmc.c
index d6153740b77f..5d4c5e0fba2f 100644
--- a/drivers/mmc/host/davinci_mmc.c
+++ b/drivers/mmc/host/davinci_mmc.c
@@ -1192,7 +1192,7 @@ static struct davinci_mmc_config
struct device_node *np;
struct davinci_mmc_config *pdata = pdev->dev.platform_data;
const struct of_device_id *match =
- of_match_device(of_match_ptr(davinci_mmc_dt_ids), &pdev->dev);
+ of_match_device(davinci_mmc_dt_ids, &pdev->dev);
u32 data;
np = pdev->dev.of_node;
@@ -1468,7 +1468,7 @@ static struct platform_driver davinci_mmcsd_driver = {
.name = "davinci_mmc",
.owner = THIS_MODULE,
.pm = davinci_mmcsd_pm_ops,
- .of_match_table = of_match_ptr(davinci_mmc_dt_ids),
+ .of_match_table = davinci_mmc_dt_ids,
},
.remove = __exit_p(davinci_mmcsd_remove),
.id_table = davinci_mmc_devtype,
diff --git a/drivers/mmc/host/dw_mmc-k3.c b/drivers/mmc/host/dw_mmc-k3.c
index f567c219cff4..650f9cc3f7a6 100644
--- a/drivers/mmc/host/dw_mmc-k3.c
+++ b/drivers/mmc/host/dw_mmc-k3.c
@@ -50,6 +50,7 @@ static int dw_mci_k3_probe(struct platform_device *pdev)
return dw_mci_pltfm_register(pdev, drv_data);
}
+#ifdef CONFIG_PM_SLEEP
static int dw_mci_k3_suspend(struct device *dev)
{
struct dw_mci *host = dev_get_drvdata(dev);
@@ -75,6 +76,7 @@ static int dw_mci_k3_resume(struct device *dev)
return dw_mci_resume(host);
}
+#endif /* CONFIG_PM_SLEEP */
static SIMPLE_DEV_PM_OPS(dw_mci_k3_pmops, dw_mci_k3_suspend, dw_mci_k3_resume);
diff --git a/drivers/mmc/host/dw_mmc-pltfm.c b/drivers/mmc/host/dw_mmc-pltfm.c
index 5c4965655297..d4a47a9f5584 100644
--- a/drivers/mmc/host/dw_mmc-pltfm.c
+++ b/drivers/mmc/host/dw_mmc-pltfm.c
@@ -25,13 +25,17 @@
#include "dw_mmc.h"
#include "dw_mmc-pltfm.h"
-static void dw_mci_rockchip_prepare_command(struct dw_mci *host, u32 *cmdr)
+static void dw_mci_pltfm_prepare_command(struct dw_mci *host, u32 *cmdr)
{
*cmdr |= SDMMC_CMD_USE_HOLD_REG;
}
static const struct dw_mci_drv_data rockchip_drv_data = {
- .prepare_command = dw_mci_rockchip_prepare_command,
+ .prepare_command = dw_mci_pltfm_prepare_command,
+};
+
+static const struct dw_mci_drv_data socfpga_drv_data = {
+ .prepare_command = dw_mci_pltfm_prepare_command,
};
int dw_mci_pltfm_register(struct platform_device *pdev,
@@ -92,6 +96,8 @@ static const struct of_device_id dw_mci_pltfm_match[] = {
{ .compatible = "snps,dw-mshc", },
{ .compatible = "rockchip,rk2928-dw-mshc",
.data = &rockchip_drv_data },
+ { .compatible = "altr,socfpga-dw-mshc",
+ .data = &socfpga_drv_data },
{},
};
MODULE_DEVICE_TABLE(of, dw_mci_pltfm_match);
@@ -123,7 +129,7 @@ static struct platform_driver dw_mci_pltfm_driver = {
.remove = dw_mci_pltfm_remove,
.driver = {
.name = "dw_mmc",
- .of_match_table = of_match_ptr(dw_mci_pltfm_match),
+ .of_match_table = dw_mci_pltfm_match,
.pm = &dw_mci_pltfm_pmops,
},
};
diff --git a/drivers/mmc/host/dw_mmc-socfpga.c b/drivers/mmc/host/dw_mmc-socfpga.c
deleted file mode 100644
index 3e8e53ae3302..000000000000
--- a/drivers/mmc/host/dw_mmc-socfpga.c
+++ /dev/null
@@ -1,138 +0,0 @@
-/*
- * Altera SoCFPGA Specific Extensions for Synopsys DW Multimedia Card Interface
- * driver
- *
- * Copyright (C) 2012, Samsung Electronics Co., Ltd.
- * Copyright (C) 2013 Altera Corporation
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * Taken from dw_mmc-exynos.c
- */
-#include <linux/clk.h>
-#include <linux/mfd/syscon.h>
-#include <linux/mmc/host.h>
-#include <linux/mmc/dw_mmc.h>
-#include <linux/module.h>
-#include <linux/of.h>
-#include <linux/platform_device.h>
-#include <linux/regmap.h>
-
-#include "dw_mmc.h"
-#include "dw_mmc-pltfm.h"
-
-#define SYSMGR_SDMMCGRP_CTRL_OFFSET 0x108
-#define DRV_CLK_PHASE_SHIFT_SEL_MASK 0x7
-#define SYSMGR_SDMMC_CTRL_SET(smplsel, drvsel) \
- ((((smplsel) & 0x7) << 3) | (((drvsel) & 0x7) << 0))
-
-/* SOCFPGA implementation specific driver private data */
-struct dw_mci_socfpga_priv_data {
- u8 ciu_div; /* card interface unit divisor */
- u32 hs_timing; /* bitmask for CIU clock phase shift */
- struct regmap *sysreg; /* regmap for system manager register */
-};
-
-static int dw_mci_socfpga_priv_init(struct dw_mci *host)
-{
- return 0;
-}
-
-static int dw_mci_socfpga_setup_clock(struct dw_mci *host)
-{
- struct dw_mci_socfpga_priv_data *priv = host->priv;
-
- clk_disable_unprepare(host->ciu_clk);
- regmap_write(priv->sysreg, SYSMGR_SDMMCGRP_CTRL_OFFSET,
- priv->hs_timing);
- clk_prepare_enable(host->ciu_clk);
-
- host->bus_hz /= (priv->ciu_div + 1);
- return 0;
-}
-
-static void dw_mci_socfpga_prepare_command(struct dw_mci *host, u32 *cmdr)
-{
- struct dw_mci_socfpga_priv_data *priv = host->priv;
-
- if (priv->hs_timing & DRV_CLK_PHASE_SHIFT_SEL_MASK)
- *cmdr |= SDMMC_CMD_USE_HOLD_REG;
-}
-
-static int dw_mci_socfpga_parse_dt(struct dw_mci *host)
-{
- struct dw_mci_socfpga_priv_data *priv;
- struct device_node *np = host->dev->of_node;
- u32 timing[2];
- u32 div = 0;
- int ret;
-
- priv = devm_kzalloc(host->dev, sizeof(*priv), GFP_KERNEL);
- if (!priv) {
- dev_err(host->dev, "mem alloc failed for private data\n");
- return -ENOMEM;
- }
-
- priv->sysreg = syscon_regmap_lookup_by_compatible("altr,sys-mgr");
- if (IS_ERR(priv->sysreg)) {
- dev_err(host->dev, "regmap for altr,sys-mgr lookup failed.\n");
- return PTR_ERR(priv->sysreg);
- }
-
- ret = of_property_read_u32(np, "altr,dw-mshc-ciu-div", &div);
- if (ret)
- dev_info(host->dev, "No dw-mshc-ciu-div specified, assuming 1");
- priv->ciu_div = div;
-
- ret = of_property_read_u32_array(np,
- "altr,dw-mshc-sdr-timing", timing, 2);
- if (ret)
- return ret;
-
- priv->hs_timing = SYSMGR_SDMMC_CTRL_SET(timing[0], timing[1]);
- host->priv = priv;
- return 0;
-}
-
-static const struct dw_mci_drv_data socfpga_drv_data = {
- .init = dw_mci_socfpga_priv_init,
- .setup_clock = dw_mci_socfpga_setup_clock,
- .prepare_command = dw_mci_socfpga_prepare_command,
- .parse_dt = dw_mci_socfpga_parse_dt,
-};
-
-static const struct of_device_id dw_mci_socfpga_match[] = {
- { .compatible = "altr,socfpga-dw-mshc",
- .data = &socfpga_drv_data, },
- {},
-};
-MODULE_DEVICE_TABLE(of, dw_mci_socfpga_match);
-
-static int dw_mci_socfpga_probe(struct platform_device *pdev)
-{
- const struct dw_mci_drv_data *drv_data;
- const struct of_device_id *match;
-
- match = of_match_node(dw_mci_socfpga_match, pdev->dev.of_node);
- drv_data = match->data;
- return dw_mci_pltfm_register(pdev, drv_data);
-}
-
-static struct platform_driver dw_mci_socfpga_pltfm_driver = {
- .probe = dw_mci_socfpga_probe,
- .remove = __exit_p(dw_mci_pltfm_remove),
- .driver = {
- .name = "dwmmc_socfpga",
- .of_match_table = dw_mci_socfpga_match,
- .pm = &dw_mci_pltfm_pmops,
- },
-};
-
-module_platform_driver(dw_mci_socfpga_pltfm_driver);
-
-MODULE_DESCRIPTION("Altera SOCFPGA Specific DW-MSHC Driver Extension");
-MODULE_LICENSE("GPL v2");
-MODULE_ALIAS("platform:dwmmc-socfpga");
diff --git a/drivers/mmc/host/dw_mmc.c b/drivers/mmc/host/dw_mmc.c
index c204b7d1532c..cced599d5aeb 100644
--- a/drivers/mmc/host/dw_mmc.c
+++ b/drivers/mmc/host/dw_mmc.c
@@ -1345,7 +1345,7 @@ static void dw_mci_tasklet_func(unsigned long priv)
if (!err) {
if (!data->stop || mrq->sbc) {
- if (mrq->sbc)
+ if (mrq->sbc && data->stop)
data->stop->error = 0;
dw_mci_request_end(host, mrq);
goto unlock;
diff --git a/drivers/mmc/host/dw_mmc.h b/drivers/mmc/host/dw_mmc.h
index 6bf24ab917e6..68349779c396 100644
--- a/drivers/mmc/host/dw_mmc.h
+++ b/drivers/mmc/host/dw_mmc.h
@@ -185,7 +185,7 @@
extern int dw_mci_probe(struct dw_mci *host);
extern void dw_mci_remove(struct dw_mci *host);
-#ifdef CONFIG_PM
+#ifdef CONFIG_PM_SLEEP
extern int dw_mci_suspend(struct dw_mci *host);
extern int dw_mci_resume(struct dw_mci *host);
#endif
@@ -244,6 +244,7 @@ struct dw_mci_tuning_data {
* @prepare_command: handle CMD register extensions.
* @set_ios: handle bus specific extensions.
* @parse_dt: parse implementation specific device tree properties.
+ * @execute_tuning: implementation specific tuning procedure.
*
* Provide controller implementation specific extensions. The usage of this
* data structure is fully optional and usage of each member in this structure
diff --git a/drivers/mmc/host/mmci.c b/drivers/mmc/host/mmci.c
index b93122636531..771c60ab4a32 100644
--- a/drivers/mmc/host/mmci.c
+++ b/drivers/mmc/host/mmci.c
@@ -921,6 +921,29 @@ mmci_cmd_irq(struct mmci_host *host, struct mmc_command *cmd,
{
void __iomem *base = host->base;
bool sbc = (cmd == host->mrq->sbc);
+ bool busy_resp = host->variant->busy_detect &&
+ (cmd->flags & MMC_RSP_BUSY);
+
+ /* Check if we need to wait for busy completion. */
+ if (host->busy_status && (status & MCI_ST_CARDBUSY))
+ return;
+
+ /* Enable busy completion if needed and supported. */
+ if (!host->busy_status && busy_resp &&
+ !(status & (MCI_CMDCRCFAIL|MCI_CMDTIMEOUT)) &&
+ (readl(base + MMCISTATUS) & MCI_ST_CARDBUSY)) {
+ writel(readl(base + MMCIMASK0) | MCI_ST_BUSYEND,
+ base + MMCIMASK0);
+ host->busy_status = status & (MCI_CMDSENT|MCI_CMDRESPEND);
+ return;
+ }
+
+ /* At busy completion, mask the IRQ and complete the request. */
+ if (host->busy_status) {
+ writel(readl(base + MMCIMASK0) & ~MCI_ST_BUSYEND,
+ base + MMCIMASK0);
+ host->busy_status = 0;
+ }
host->cmd = NULL;
@@ -1139,20 +1162,30 @@ static irqreturn_t mmci_irq(int irq, void *dev_id)
status &= ~MCI_IRQ1MASK;
}
+ /*
+ * We intentionally clear the MCI_ST_CARDBUSY IRQ here (if it's
+ * enabled) since the HW seems to be triggering the IRQ on both
+ * edges while monitoring DAT0 for busy completion.
+ */
status &= readl(host->base + MMCIMASK0);
writel(status, host->base + MMCICLEAR);
dev_dbg(mmc_dev(host->mmc), "irq0 (data+cmd) %08x\n", status);
+ cmd = host->cmd;
+ if ((status|host->busy_status) & (MCI_CMDCRCFAIL|MCI_CMDTIMEOUT|
+ MCI_CMDSENT|MCI_CMDRESPEND) && cmd)
+ mmci_cmd_irq(host, cmd, status);
+
data = host->data;
if (status & (MCI_DATACRCFAIL|MCI_DATATIMEOUT|MCI_STARTBITERR|
MCI_TXUNDERRUN|MCI_RXOVERRUN|MCI_DATAEND|
MCI_DATABLOCKEND) && data)
mmci_data_irq(host, data, status);
- cmd = host->cmd;
- if (status & (MCI_CMDCRCFAIL|MCI_CMDTIMEOUT|MCI_CMDSENT|MCI_CMDRESPEND) && cmd)
- mmci_cmd_irq(host, cmd, status);
+ /* Don't poll for busy completion in irq context. */
+ if (host->busy_status)
+ status &= ~MCI_ST_CARDBUSY;
ret = 1;
} while (status);
@@ -1503,12 +1536,6 @@ static int mmci_probe(struct amba_device *dev,
goto clk_disable;
}
- if (variant->busy_detect) {
- mmci_ops.card_busy = mmci_card_busy;
- mmci_write_datactrlreg(host, MCI_ST_DPSM_BUSYMODE);
- }
-
- mmc->ops = &mmci_ops;
/*
* The ARM and ST versions of the block have slightly different
* clock divider equations which means that the minimum divider
@@ -1542,6 +1569,15 @@ static int mmci_probe(struct amba_device *dev,
mmc->caps = plat->capabilities;
mmc->caps2 = plat->capabilities2;
+ if (variant->busy_detect) {
+ mmci_ops.card_busy = mmci_card_busy;
+ mmci_write_datactrlreg(host, MCI_ST_DPSM_BUSYMODE);
+ mmc->caps |= MMC_CAP_WAIT_WHILE_BUSY;
+ mmc->max_busy_timeout = 0;
+ }
+
+ mmc->ops = &mmci_ops;
+
/* We support these PM capabilities. */
mmc->pm_caps = MMC_PM_KEEP_POWER;
diff --git a/drivers/mmc/host/mmci.h b/drivers/mmc/host/mmci.h
index 84c0e59b792a..58b1b8896bf2 100644
--- a/drivers/mmc/host/mmci.h
+++ b/drivers/mmc/host/mmci.h
@@ -140,6 +140,7 @@
/* Extended status bits for the ST Micro variants */
#define MCI_ST_SDIOITMASK (1 << 22)
#define MCI_ST_CEATAENDMASK (1 << 23)
+#define MCI_ST_BUSYEND (1 << 24)
#define MMCIMASK1 0x040
#define MMCIFIFOCNT 0x048
@@ -187,6 +188,7 @@ struct mmci_host {
u32 pwr_reg;
u32 clk_reg;
u32 datactrl_reg;
+ u32 busy_status;
bool vqmmc_enabled;
struct mmci_platform_data *plat;
struct variant_data *variant;
diff --git a/drivers/mmc/host/omap.c b/drivers/mmc/host/omap.c
index 98b6b6ef7e5c..5c2e58b29305 100644
--- a/drivers/mmc/host/omap.c
+++ b/drivers/mmc/host/omap.c
@@ -26,6 +26,7 @@
#include <linux/omap-dma.h>
#include <linux/mmc/host.h>
#include <linux/mmc/card.h>
+#include <linux/mmc/mmc.h>
#include <linux/clk.h>
#include <linux/scatterlist.h>
#include <linux/slab.h>
@@ -130,7 +131,6 @@ struct mmc_omap_host {
u32 dma_rx_burst;
struct dma_chan *dma_tx;
u32 dma_tx_burst;
- struct resource *mem_res;
void __iomem *virt_base;
unsigned int phys_base;
int irq;
@@ -153,7 +153,6 @@ struct mmc_omap_host {
u32 total_bytes_left;
unsigned features;
- unsigned use_dma:1;
unsigned brs_received:1, dma_done:1;
unsigned dma_in_use:1;
spinlock_t dma_lock;
@@ -338,6 +337,7 @@ mmc_omap_start_command(struct mmc_omap_host *host, struct mmc_command *cmd)
u32 cmdreg;
u32 resptype;
u32 cmdtype;
+ u16 irq_mask;
host->cmd = cmd;
@@ -390,12 +390,14 @@ mmc_omap_start_command(struct mmc_omap_host *host, struct mmc_command *cmd)
OMAP_MMC_WRITE(host, CTO, 200);
OMAP_MMC_WRITE(host, ARGL, cmd->arg & 0xffff);
OMAP_MMC_WRITE(host, ARGH, cmd->arg >> 16);
- OMAP_MMC_WRITE(host, IE,
- OMAP_MMC_STAT_A_EMPTY | OMAP_MMC_STAT_A_FULL |
- OMAP_MMC_STAT_CMD_CRC | OMAP_MMC_STAT_CMD_TOUT |
- OMAP_MMC_STAT_DATA_CRC | OMAP_MMC_STAT_DATA_TOUT |
- OMAP_MMC_STAT_END_OF_CMD | OMAP_MMC_STAT_CARD_ERR |
- OMAP_MMC_STAT_END_OF_DATA);
+ irq_mask = OMAP_MMC_STAT_A_EMPTY | OMAP_MMC_STAT_A_FULL |
+ OMAP_MMC_STAT_CMD_CRC | OMAP_MMC_STAT_CMD_TOUT |
+ OMAP_MMC_STAT_DATA_CRC | OMAP_MMC_STAT_DATA_TOUT |
+ OMAP_MMC_STAT_END_OF_CMD | OMAP_MMC_STAT_CARD_ERR |
+ OMAP_MMC_STAT_END_OF_DATA;
+ if (cmd->opcode == MMC_ERASE)
+ irq_mask &= ~OMAP_MMC_STAT_DATA_TOUT;
+ OMAP_MMC_WRITE(host, IE, irq_mask);
OMAP_MMC_WRITE(host, CMD, cmdreg);
}
@@ -945,7 +947,7 @@ static void
mmc_omap_prepare_data(struct mmc_omap_host *host, struct mmc_request *req)
{
struct mmc_data *data = req->data;
- int i, use_dma, block_size;
+ int i, use_dma = 1, block_size;
unsigned sg_len;
host->data = data;
@@ -970,13 +972,10 @@ mmc_omap_prepare_data(struct mmc_omap_host *host, struct mmc_request *req)
sg_len = (data->blocks == 1) ? 1 : data->sg_len;
/* Only do DMA for entire blocks */
- use_dma = host->use_dma;
- if (use_dma) {
- for (i = 0; i < sg_len; i++) {
- if ((data->sg[i].length % block_size) != 0) {
- use_dma = 0;
- break;
- }
+ for (i = 0; i < sg_len; i++) {
+ if ((data->sg[i].length % block_size) != 0) {
+ use_dma = 0;
+ break;
}
}
@@ -1239,7 +1238,7 @@ static int mmc_omap_new_slot(struct mmc_omap_host *host, int id)
mmc->caps = 0;
if (host->pdata->slots[id].wires >= 4)
- mmc->caps |= MMC_CAP_4_BIT_DATA;
+ mmc->caps |= MMC_CAP_4_BIT_DATA | MMC_CAP_ERASE;
mmc->ops = &mmc_omap_ops;
mmc->f_min = 400000;
@@ -1262,6 +1261,13 @@ static int mmc_omap_new_slot(struct mmc_omap_host *host, int id)
mmc->max_req_size = mmc->max_blk_size * mmc->max_blk_count;
mmc->max_seg_size = mmc->max_req_size;
+ if (slot->pdata->get_cover_state != NULL) {
+ setup_timer(&slot->cover_timer, mmc_omap_cover_timer,
+ (unsigned long)slot);
+ tasklet_init(&slot->cover_tasklet, mmc_omap_cover_handler,
+ (unsigned long)slot);
+ }
+
r = mmc_add_host(mmc);
if (r < 0)
goto err_remove_host;
@@ -1278,11 +1284,6 @@ static int mmc_omap_new_slot(struct mmc_omap_host *host, int id)
&dev_attr_cover_switch);
if (r < 0)
goto err_remove_slot_name;
-
- setup_timer(&slot->cover_timer, mmc_omap_cover_timer,
- (unsigned long)slot);
- tasklet_init(&slot->cover_tasklet, mmc_omap_cover_handler,
- (unsigned long)slot);
tasklet_schedule(&slot->cover_tasklet);
}
@@ -1333,21 +1334,19 @@ static int mmc_omap_probe(struct platform_device *pdev)
return -EPROBE_DEFER;
}
- res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ host = devm_kzalloc(&pdev->dev, sizeof(struct mmc_omap_host),
+ GFP_KERNEL);
+ if (host == NULL)
+ return -ENOMEM;
+
irq = platform_get_irq(pdev, 0);
- if (res == NULL || irq < 0)
+ if (irq < 0)
return -ENXIO;
- res = request_mem_region(res->start, resource_size(res),
- pdev->name);
- if (res == NULL)
- return -EBUSY;
-
- host = kzalloc(sizeof(struct mmc_omap_host), GFP_KERNEL);
- if (host == NULL) {
- ret = -ENOMEM;
- goto err_free_mem_region;
- }
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ host->virt_base = devm_ioremap_resource(&pdev->dev, res);
+ if (IS_ERR(host->virt_base))
+ return PTR_ERR(host->virt_base);
INIT_WORK(&host->slot_release_work, mmc_omap_slot_release_work);
INIT_WORK(&host->send_stop_work, mmc_omap_send_stop_work);
@@ -1369,20 +1368,11 @@ static int mmc_omap_probe(struct platform_device *pdev)
platform_set_drvdata(pdev, host);
host->id = pdev->id;
- host->mem_res = res;
- host->irq = irq;
- host->use_dma = 1;
host->irq = irq;
- host->phys_base = host->mem_res->start;
- host->virt_base = ioremap(res->start, resource_size(res));
- if (!host->virt_base)
- goto err_ioremap;
-
+ host->phys_base = res->start;
host->iclk = clk_get(&pdev->dev, "ick");
- if (IS_ERR(host->iclk)) {
- ret = PTR_ERR(host->iclk);
- goto err_free_mmc_host;
- }
+ if (IS_ERR(host->iclk))
+ return PTR_ERR(host->iclk);
clk_enable(host->iclk);
host->fclk = clk_get(&pdev->dev, "fck");
@@ -1460,12 +1450,6 @@ err_free_dma:
err_free_iclk:
clk_disable(host->iclk);
clk_put(host->iclk);
-err_free_mmc_host:
- iounmap(host->virt_base);
-err_ioremap:
- kfree(host);
-err_free_mem_region:
- release_mem_region(res->start, resource_size(res));
return ret;
}
@@ -1493,13 +1477,8 @@ static int mmc_omap_remove(struct platform_device *pdev)
if (host->dma_rx)
dma_release_channel(host->dma_rx);
- iounmap(host->virt_base);
- release_mem_region(pdev->resource[0].start,
- pdev->resource[0].end - pdev->resource[0].start + 1);
destroy_workqueue(host->mmc_omap_wq);
- kfree(host);
-
return 0;
}
diff --git a/drivers/mmc/host/omap_hsmmc.c b/drivers/mmc/host/omap_hsmmc.c
index dbd32ad3b749..e91ee21549d0 100644
--- a/drivers/mmc/host/omap_hsmmc.c
+++ b/drivers/mmc/host/omap_hsmmc.c
@@ -45,6 +45,7 @@
/* OMAP HSMMC Host Controller Registers */
#define OMAP_HSMMC_SYSSTATUS 0x0014
#define OMAP_HSMMC_CON 0x002C
+#define OMAP_HSMMC_SDMASA 0x0100
#define OMAP_HSMMC_BLK 0x0104
#define OMAP_HSMMC_ARG 0x0108
#define OMAP_HSMMC_CMD 0x010C
@@ -58,6 +59,7 @@
#define OMAP_HSMMC_STAT 0x0130
#define OMAP_HSMMC_IE 0x0134
#define OMAP_HSMMC_ISE 0x0138
+#define OMAP_HSMMC_AC12 0x013C
#define OMAP_HSMMC_CAPA 0x0140
#define VS18 (1 << 26)
@@ -81,6 +83,7 @@
#define DTO_MASK 0x000F0000
#define DTO_SHIFT 16
#define INIT_STREAM (1 << 1)
+#define ACEN_ACMD23 (2 << 2)
#define DP_SELECT (1 << 21)
#define DDIR (1 << 4)
#define DMAE 0x1
@@ -97,7 +100,6 @@
#define SRC (1 << 25)
#define SRD (1 << 26)
#define SOFTRESET (1 << 1)
-#define RESETDONE (1 << 0)
/* Interrupt masks for IE and ISE register */
#define CC_EN (1 << 0)
@@ -112,13 +114,21 @@
#define DTO_EN (1 << 20)
#define DCRC_EN (1 << 21)
#define DEB_EN (1 << 22)
+#define ACE_EN (1 << 24)
#define CERR_EN (1 << 28)
#define BADA_EN (1 << 29)
-#define INT_EN_MASK (BADA_EN | CERR_EN | DEB_EN | DCRC_EN |\
+#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)
+#define CNI (1 << 7)
+#define ACIE (1 << 4)
+#define ACEB (1 << 3)
+#define ACCE (1 << 2)
+#define ACTO (1 << 1)
+#define ACNE (1 << 0)
+
#define MMC_AUTOSUSPEND_DELAY 100
#define MMC_TIMEOUT_MS 20 /* 20 mSec */
#define MMC_TIMEOUT_US 20000 /* 20000 micro Sec */
@@ -126,6 +136,11 @@
#define OMAP_MMC_MAX_CLOCK 52000000
#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 AUTO_CMD23 (1 << 1) /* Auto CMD23 support */
/*
* 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
@@ -164,7 +179,8 @@ struct omap_hsmmc_host {
*/
struct regulator *vcc;
struct regulator *vcc_aux;
- int pbias_disable;
+ struct regulator *pbias;
+ bool pbias_enabled;
void __iomem *base;
resource_size_t mapbase;
spinlock_t irq_lock; /* Prevent races with irq handler */
@@ -188,10 +204,19 @@ struct omap_hsmmc_host {
int reqs_blocked;
int use_reg;
int req_in_progress;
+ unsigned long clk_rate;
+ unsigned int flags;
struct omap_hsmmc_next next_data;
struct omap_mmc_platform_data *pdata;
};
+struct omap_mmc_of_data {
+ u32 reg_offset;
+ u8 controller_flags;
+};
+
+static void omap_hsmmc_start_dma_transfer(struct omap_hsmmc_host *host);
+
static int omap_hsmmc_card_detect(struct device *dev, int slot)
{
struct omap_hsmmc_host *host = dev_get_drvdata(dev);
@@ -261,17 +286,19 @@ static int omap_hsmmc_set_power(struct device *dev, int slot, int power_on,
*/
if (!host->vcc)
return 0;
- /*
- * With DT, never turn OFF the regulator for MMC1. This is because
- * the pbias cell programming support is still missing when
- * booting with Device tree
- */
- if (host->pbias_disable && !vdd)
- return 0;
if (mmc_slot(host).before_set_reg)
mmc_slot(host).before_set_reg(dev, slot, power_on, vdd);
+ if (host->pbias) {
+ if (host->pbias_enabled == 1) {
+ ret = regulator_disable(host->pbias);
+ if (!ret)
+ host->pbias_enabled = 0;
+ }
+ regulator_set_voltage(host->pbias, VDD_3V0, VDD_3V0);
+ }
+
/*
* Assume Vcc regulator is used only to power the card ... OMAP
* VDDS is used to power the pins, optionally with a transceiver to
@@ -286,11 +313,12 @@ static int omap_hsmmc_set_power(struct device *dev, int slot, int power_on,
* chips/cards need an interface voltage rail too.
*/
if (power_on) {
- ret = mmc_regulator_set_ocr(host->mmc, host->vcc, vdd);
+ if (host->vcc)
+ ret = mmc_regulator_set_ocr(host->mmc, host->vcc, vdd);
/* Enable interface voltage rail, if needed */
if (ret == 0 && host->vcc_aux) {
ret = regulator_enable(host->vcc_aux);
- if (ret < 0)
+ if (ret < 0 && host->vcc)
ret = mmc_regulator_set_ocr(host->mmc,
host->vcc, 0);
}
@@ -298,16 +326,34 @@ static int omap_hsmmc_set_power(struct device *dev, int slot, int power_on,
/* Shut down the rail */
if (host->vcc_aux)
ret = regulator_disable(host->vcc_aux);
- if (!ret) {
+ if (host->vcc) {
/* Then proceed to shut down the local regulator */
ret = mmc_regulator_set_ocr(host->mmc,
host->vcc, 0);
}
}
+ if (host->pbias) {
+ if (vdd <= VDD_165_195)
+ ret = regulator_set_voltage(host->pbias, VDD_1V8,
+ VDD_1V8);
+ else
+ ret = regulator_set_voltage(host->pbias, VDD_3V0,
+ VDD_3V0);
+ if (ret < 0)
+ goto error_set_power;
+
+ if (host->pbias_enabled == 0) {
+ ret = regulator_enable(host->pbias);
+ if (!ret)
+ host->pbias_enabled = 1;
+ }
+ }
+
if (mmc_slot(host).after_set_reg)
mmc_slot(host).after_set_reg(dev, slot, power_on, vdd);
+error_set_power:
return ret;
}
@@ -316,12 +362,12 @@ static int omap_hsmmc_reg_get(struct omap_hsmmc_host *host)
struct regulator *reg;
int ocr_value = 0;
- reg = regulator_get(host->dev, "vmmc");
+ reg = devm_regulator_get(host->dev, "vmmc");
if (IS_ERR(reg)) {
- dev_err(host->dev, "vmmc regulator missing\n");
+ dev_err(host->dev, "unable to get vmmc regulator %ld\n",
+ PTR_ERR(reg));
return PTR_ERR(reg);
} else {
- mmc_slot(host).set_power = omap_hsmmc_set_power;
host->vcc = reg;
ocr_value = mmc_regulator_get_ocrmask(reg);
if (!mmc_slot(host).ocr_mask) {
@@ -334,31 +380,29 @@ static int omap_hsmmc_reg_get(struct omap_hsmmc_host *host)
return -EINVAL;
}
}
+ }
+ mmc_slot(host).set_power = omap_hsmmc_set_power;
- /* Allow an aux regulator */
- reg = regulator_get(host->dev, "vmmc_aux");
- host->vcc_aux = IS_ERR(reg) ? NULL : reg;
+ /* Allow an aux regulator */
+ reg = devm_regulator_get_optional(host->dev, "vmmc_aux");
+ host->vcc_aux = IS_ERR(reg) ? NULL : reg;
- /* For eMMC do not power off when not in sleep state */
- if (mmc_slot(host).no_regulator_off_init)
- return 0;
- /*
- * UGLY HACK: workaround regulator framework bugs.
- * When the bootloader leaves a supply active, it's
- * initialized with zero usecount ... and we can't
- * disable it without first enabling it. Until the
- * framework is fixed, we need a workaround like this
- * (which is safe for MMC, but not in general).
- */
- if (regulator_is_enabled(host->vcc) > 0 ||
- (host->vcc_aux && regulator_is_enabled(host->vcc_aux))) {
- int vdd = ffs(mmc_slot(host).ocr_mask) - 1;
+ reg = devm_regulator_get_optional(host->dev, "pbias");
+ host->pbias = IS_ERR(reg) ? NULL : reg;
- mmc_slot(host).set_power(host->dev, host->slot_id,
- 1, vdd);
- mmc_slot(host).set_power(host->dev, host->slot_id,
- 0, 0);
- }
+ /* For eMMC do not power off when not in sleep state */
+ if (mmc_slot(host).no_regulator_off_init)
+ return 0;
+ /*
+ * To disable boot_on regulator, enable regulator
+ * to increase usecount and then disable it.
+ */
+ if ((host->vcc && regulator_is_enabled(host->vcc) > 0) ||
+ (host->vcc_aux && regulator_is_enabled(host->vcc_aux))) {
+ int vdd = ffs(mmc_slot(host).ocr_mask) - 1;
+
+ mmc_slot(host).set_power(host->dev, host->slot_id, 1, vdd);
+ mmc_slot(host).set_power(host->dev, host->slot_id, 0, 0);
}
return 0;
@@ -366,8 +410,6 @@ static int omap_hsmmc_reg_get(struct omap_hsmmc_host *host)
static void omap_hsmmc_reg_put(struct omap_hsmmc_host *host)
{
- regulator_put(host->vcc);
- regulator_put(host->vcc_aux);
mmc_slot(host).set_power = NULL;
}
@@ -605,9 +647,6 @@ static int omap_hsmmc_context_restore(struct omap_hsmmc_host *host)
u32 hctl, capa;
unsigned long timeout;
- if (!OMAP_HSMMC_READ(host->base, SYSSTATUS) & RESETDONE)
- return 1;
-
if (host->con == OMAP_HSMMC_READ(host->base, CON) &&
host->hctl == OMAP_HSMMC_READ(host->base, HCTL) &&
host->sysctl == OMAP_HSMMC_READ(host->base, SYSCTL) &&
@@ -787,6 +826,11 @@ omap_hsmmc_start_command(struct omap_hsmmc_host *host, struct mmc_command *cmd,
cmdreg = (cmd->opcode << 24) | (resptype << 16) | (cmdtype << 22);
+ if ((host->flags & AUTO_CMD23) && mmc_op_multi(cmd->opcode) &&
+ host->mrq->sbc) {
+ cmdreg |= ACEN_ACMD23;
+ OMAP_HSMMC_WRITE(host->base, SDMASA, host->mrq->sbc->arg);
+ }
if (data) {
cmdreg |= DP_SELECT | MSBS | BCE;
if (data->flags & MMC_DATA_READ)
@@ -864,11 +908,10 @@ omap_hsmmc_xfer_done(struct omap_hsmmc_host *host, struct mmc_data *data)
else
data->bytes_xfered = 0;
- if (!data->stop) {
+ if (data->stop && (data->error || !host->mrq->sbc))
+ omap_hsmmc_start_command(host, data->stop, NULL);
+ else
omap_hsmmc_request_done(host, data->mrq);
- return;
- }
- omap_hsmmc_start_command(host, data->stop, NULL);
}
/*
@@ -879,6 +922,14 @@ omap_hsmmc_cmd_done(struct omap_hsmmc_host *host, struct mmc_command *cmd)
{
host->cmd = NULL;
+ if (host->mrq->sbc && (host->cmd == host->mrq->sbc) &&
+ !host->mrq->sbc->error && !(host->flags & AUTO_CMD23)) {
+ omap_hsmmc_start_dma_transfer(host);
+ omap_hsmmc_start_command(host, host->mrq->cmd,
+ host->mrq->data);
+ return;
+ }
+
if (cmd->flags & MMC_RSP_PRESENT) {
if (cmd->flags & MMC_RSP_136) {
/* response type 2 */
@@ -892,7 +943,7 @@ omap_hsmmc_cmd_done(struct omap_hsmmc_host *host, struct mmc_command *cmd)
}
}
if ((host->data == NULL && !host->response_busy) || cmd->error)
- omap_hsmmc_request_done(host, cmd->mrq);
+ omap_hsmmc_request_done(host, host->mrq);
}
/*
@@ -1015,6 +1066,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;
data = host->data;
dev_vdbg(mmc_dev(host->mmc), "IRQ Status is %x\n", status);
@@ -1029,6 +1081,20 @@ static void omap_hsmmc_do_irq(struct omap_hsmmc_host *host, int status)
else if (status & (CCRC_EN | DCRC_EN))
hsmmc_command_incomplete(host, -EILSEQ, end_cmd);
+ if (status & ACE_EN) {
+ u32 ac12;
+ ac12 = OMAP_HSMMC_READ(host->base, AC12);
+ if (!(ac12 & ACNE) && host->mrq->sbc) {
+ end_cmd = 1;
+ if (ac12 & ACTO)
+ error = -ETIMEDOUT;
+ else if (ac12 & (ACCE | ACEB | ACIE))
+ error = -EILSEQ;
+ host->mrq->sbc->error = error;
+ hsmmc_command_incomplete(host, error, end_cmd);
+ }
+ dev_dbg(mmc_dev(host->mmc), "AC12 err: 0x%x\n", ac12);
+ }
if (host->data || host->response_busy) {
end_trans = !end_cmd;
host->response_busy = 0;
@@ -1236,8 +1302,7 @@ static int omap_hsmmc_pre_dma_transfer(struct omap_hsmmc_host *host,
}
/* Check if next job is already prepared */
- if (next ||
- (!next && data->host_cookie != host->next_data.cookie)) {
+ if (next || data->host_cookie != host->next_data.cookie) {
dma_len = dma_map_sg(chan->device->dev, data->sg, data->sg_len,
omap_hsmmc_get_dma_dir(host, data));
@@ -1262,7 +1327,7 @@ static int omap_hsmmc_pre_dma_transfer(struct omap_hsmmc_host *host,
/*
* Routine to configure and start DMA for the MMC card
*/
-static int omap_hsmmc_start_dma_transfer(struct omap_hsmmc_host *host,
+static int omap_hsmmc_setup_dma_transfer(struct omap_hsmmc_host *host,
struct mmc_request *req)
{
struct dma_slave_config cfg;
@@ -1321,8 +1386,6 @@ static int omap_hsmmc_start_dma_transfer(struct omap_hsmmc_host *host,
host->dma_ch = 1;
- dma_async_issue_pending(chan);
-
return 0;
}
@@ -1338,7 +1401,7 @@ static void set_data_timeout(struct omap_hsmmc_host *host,
if (clkd == 0)
clkd = 1;
- cycle_ns = 1000000000 / (clk_get_rate(host->fclk) / clkd);
+ cycle_ns = 1000000000 / (host->clk_rate / clkd);
timeout = timeout_ns / cycle_ns;
timeout += timeout_clks;
if (timeout) {
@@ -1363,6 +1426,21 @@ static void set_data_timeout(struct omap_hsmmc_host *host,
OMAP_HSMMC_WRITE(host->base, SYSCTL, reg);
}
+static void omap_hsmmc_start_dma_transfer(struct omap_hsmmc_host *host)
+{
+ struct mmc_request *req = host->mrq;
+ struct dma_chan *chan;
+
+ if (!req->data)
+ return;
+ OMAP_HSMMC_WRITE(host->base, BLK, (req->data->blksz)
+ | (req->data->blocks << 16));
+ set_data_timeout(host, req->data->timeout_ns,
+ req->data->timeout_clks);
+ chan = omap_hsmmc_get_dma_chan(host, req->data);
+ dma_async_issue_pending(chan);
+}
+
/*
* Configure block length for MMC/SD cards and initiate the transfer.
*/
@@ -1383,12 +1461,8 @@ omap_hsmmc_prepare_data(struct omap_hsmmc_host *host, struct mmc_request *req)
return 0;
}
- OMAP_HSMMC_WRITE(host->base, BLK, (req->data->blksz)
- | (req->data->blocks << 16));
- set_data_timeout(host, req->data->timeout_ns, req->data->timeout_clks);
-
if (host->use_dma) {
- ret = omap_hsmmc_start_dma_transfer(host, req);
+ ret = omap_hsmmc_setup_dma_transfer(host, req);
if (ret != 0) {
dev_err(mmc_dev(host->mmc), "MMC start dma failure\n");
return ret;
@@ -1462,6 +1536,7 @@ static void omap_hsmmc_request(struct mmc_host *mmc, struct mmc_request *req)
host->reqs_blocked = 0;
WARN_ON(host->mrq != NULL);
host->mrq = req;
+ host->clk_rate = clk_get_rate(host->fclk);
err = omap_hsmmc_prepare_data(host, req);
if (err) {
req->cmd->error = err;
@@ -1471,7 +1546,12 @@ static void omap_hsmmc_request(struct mmc_host *mmc, struct mmc_request *req)
mmc_request_done(mmc, req);
return;
}
+ if (req->sbc && !(host->flags & AUTO_CMD23)) {
+ omap_hsmmc_start_command(host, req->sbc, NULL);
+ return;
+ }
+ omap_hsmmc_start_dma_transfer(host);
omap_hsmmc_start_command(host, req->cmd, req->data);
}
@@ -1509,13 +1589,7 @@ static void omap_hsmmc_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
* of external transceiver; but they all handle 1.8V.
*/
if ((OMAP_HSMMC_READ(host->base, HCTL) & SDVSDET) &&
- (ios->vdd == DUAL_VOLT_OCR_BIT) &&
- /*
- * With pbias cell programming missing, this
- * can't be allowed on MMC1 when booting with device
- * tree.
- */
- !host->pbias_disable) {
+ (ios->vdd == DUAL_VOLT_OCR_BIT)) {
/*
* The mmc_select_voltage fn of the core does
* not seem to set the power_mode to
@@ -1678,18 +1752,29 @@ static void omap_hsmmc_debugfs(struct mmc_host *mmc)
#endif
#ifdef CONFIG_OF
-static u16 omap4_reg_offset = 0x100;
+static const struct omap_mmc_of_data omap3_pre_es3_mmc_of_data = {
+ /* See 35xx errata 2.1.1.128 in SPRZ278F */
+ .controller_flags = OMAP_HSMMC_BROKEN_MULTIBLOCK_READ,
+};
+
+static const struct omap_mmc_of_data omap4_mmc_of_data = {
+ .reg_offset = 0x100,
+};
static const struct of_device_id omap_mmc_of_match[] = {
{
.compatible = "ti,omap2-hsmmc",
},
{
+ .compatible = "ti,omap3-pre-es3-hsmmc",
+ .data = &omap3_pre_es3_mmc_of_data,
+ },
+ {
.compatible = "ti,omap3-hsmmc",
},
{
.compatible = "ti,omap4-hsmmc",
- .data = &omap4_reg_offset,
+ .data = &omap4_mmc_of_data,
},
{},
};
@@ -1709,7 +1794,7 @@ static struct omap_mmc_platform_data *of_get_hsmmc_pdata(struct device *dev)
pdata = devm_kzalloc(dev, sizeof(*pdata), GFP_KERNEL);
if (!pdata)
- return NULL; /* out of memory */
+ return ERR_PTR(-ENOMEM); /* out of memory */
if (of_find_property(np, "ti,dual-volt", NULL))
pdata->controller_flags |= OMAP_HSMMC_SUPPORTS_DUAL_VOLT;
@@ -1738,13 +1823,19 @@ static struct omap_mmc_platform_data *of_get_hsmmc_pdata(struct device *dev)
if (of_find_property(np, "ti,needs-special-hs-handling", NULL))
pdata->slots[0].features |= HSMMC_HAS_HSPE_SUPPORT;
+ if (of_find_property(np, "keep-power-in-suspend", NULL))
+ pdata->slots[0].pm_caps |= MMC_PM_KEEP_POWER;
+
+ if (of_find_property(np, "enable-sdio-wakeup", NULL))
+ pdata->slots[0].pm_caps |= MMC_PM_WAKE_SDIO_IRQ;
+
return pdata;
}
#else
static inline struct omap_mmc_platform_data
*of_get_hsmmc_pdata(struct device *dev)
{
- return NULL;
+ return ERR_PTR(-EINVAL);
}
#endif
@@ -1759,6 +1850,7 @@ static int omap_hsmmc_probe(struct platform_device *pdev)
dma_cap_mask_t mask;
unsigned tx_req, rx_req;
struct pinctrl *pinctrl;
+ const struct omap_mmc_of_data *data;
match = of_match_device(of_match_ptr(omap_mmc_of_match), &pdev->dev);
if (match) {
@@ -1768,8 +1860,9 @@ static int omap_hsmmc_probe(struct platform_device *pdev)
return PTR_ERR(pdata);
if (match->data) {
- const u16 *offsetp = match->data;
- pdata->reg_offset = *offsetp;
+ data = match->data;
+ pdata->reg_offset = data->reg_offset;
+ pdata->controller_flags |= data->controller_flags;
}
}
@@ -1814,6 +1907,7 @@ static int omap_hsmmc_probe(struct platform_device *pdev)
host->base = ioremap(host->mapbase, SZ_4K);
host->power_mode = MMC_POWER_OFF;
host->next_data.cookie = 1;
+ host->pbias_enabled = 0;
platform_set_drvdata(pdev, host);
@@ -1847,10 +1941,6 @@ static int omap_hsmmc_probe(struct platform_device *pdev)
omap_hsmmc_context_save(host);
- /* This can be removed once we support PBIAS with DT */
- if (host->dev->of_node && res->start == 0x4809c000)
- host->pbias_disable = 1;
-
host->dbclk = clk_get(&pdev->dev, "mmchsdb_fck");
/*
* MMC can still work without debounce clock.
diff --git a/drivers/mmc/host/rtsx_pci_sdmmc.c b/drivers/mmc/host/rtsx_pci_sdmmc.c
index c46feda07d56..5fb994f9a653 100644
--- a/drivers/mmc/host/rtsx_pci_sdmmc.c
+++ b/drivers/mmc/host/rtsx_pci_sdmmc.c
@@ -31,14 +31,9 @@
#include <linux/mfd/rtsx_pci.h>
#include <asm/unaligned.h>
-/* SD Tuning Data Structure
- * Record continuous timing phase path
- */
-struct timing_phase_path {
- int start;
- int end;
- int mid;
- int len;
+struct realtek_next {
+ unsigned int sg_count;
+ s32 cookie;
};
struct realtek_pci_sdmmc {
@@ -46,9 +41,18 @@ struct realtek_pci_sdmmc {
struct rtsx_pcr *pcr;
struct mmc_host *mmc;
struct mmc_request *mrq;
-
- struct mutex host_mutex;
-
+ struct mmc_command *cmd;
+ struct mmc_data *data;
+
+ spinlock_t lock;
+ struct timer_list timer;
+ struct tasklet_struct cmd_tasklet;
+ struct tasklet_struct data_tasklet;
+ struct tasklet_struct finish_tasklet;
+
+ u8 rsp_type;
+ u8 rsp_len;
+ int sg_count;
u8 ssc_depth;
unsigned int clock;
bool vpclk;
@@ -58,8 +62,13 @@ struct realtek_pci_sdmmc {
int power_state;
#define SDMMC_POWER_ON 1
#define SDMMC_POWER_OFF 0
+
+ struct realtek_next next_data;
};
+static int sd_start_multi_rw(struct realtek_pci_sdmmc *host,
+ struct mmc_request *mrq);
+
static inline struct device *sdmmc_dev(struct realtek_pci_sdmmc *host)
{
return &(host->pdev->dev);
@@ -96,6 +105,95 @@ static void sd_print_debug_regs(struct realtek_pci_sdmmc *host)
#define sd_print_debug_regs(host)
#endif /* DEBUG */
+static void sd_isr_done_transfer(struct platform_device *pdev)
+{
+ struct realtek_pci_sdmmc *host = platform_get_drvdata(pdev);
+
+ spin_lock(&host->lock);
+ if (host->cmd)
+ tasklet_schedule(&host->cmd_tasklet);
+ if (host->data)
+ tasklet_schedule(&host->data_tasklet);
+ spin_unlock(&host->lock);
+}
+
+static void sd_request_timeout(unsigned long host_addr)
+{
+ struct realtek_pci_sdmmc *host = (struct realtek_pci_sdmmc *)host_addr;
+ unsigned long flags;
+
+ spin_lock_irqsave(&host->lock, flags);
+
+ if (!host->mrq) {
+ dev_err(sdmmc_dev(host), "error: no request exist\n");
+ goto out;
+ }
+
+ if (host->cmd)
+ host->cmd->error = -ETIMEDOUT;
+ if (host->data)
+ host->data->error = -ETIMEDOUT;
+
+ dev_dbg(sdmmc_dev(host), "timeout for request\n");
+
+out:
+ tasklet_schedule(&host->finish_tasklet);
+ spin_unlock_irqrestore(&host->lock, flags);
+}
+
+static void sd_finish_request(unsigned long host_addr)
+{
+ struct realtek_pci_sdmmc *host = (struct realtek_pci_sdmmc *)host_addr;
+ struct rtsx_pcr *pcr = host->pcr;
+ struct mmc_request *mrq;
+ struct mmc_command *cmd;
+ struct mmc_data *data;
+ unsigned long flags;
+ bool any_error;
+
+ spin_lock_irqsave(&host->lock, flags);
+
+ del_timer(&host->timer);
+ mrq = host->mrq;
+ if (!mrq) {
+ dev_err(sdmmc_dev(host), "error: no request need finish\n");
+ goto out;
+ }
+
+ cmd = mrq->cmd;
+ data = mrq->data;
+
+ any_error = (mrq->sbc && mrq->sbc->error) ||
+ (mrq->stop && mrq->stop->error) ||
+ (cmd && cmd->error) || (data && data->error);
+
+ if (any_error) {
+ rtsx_pci_stop_cmd(pcr);
+ sd_clear_error(host);
+ }
+
+ if (data) {
+ if (any_error)
+ data->bytes_xfered = 0;
+ else
+ data->bytes_xfered = data->blocks * data->blksz;
+
+ if (!data->host_cookie)
+ rtsx_pci_dma_unmap_sg(pcr, data->sg, data->sg_len,
+ data->flags & MMC_DATA_READ);
+
+ }
+
+ host->mrq = NULL;
+ host->cmd = NULL;
+ host->data = NULL;
+
+out:
+ spin_unlock_irqrestore(&host->lock, flags);
+ mutex_unlock(&pcr->pcr_mutex);
+ mmc_request_done(host->mmc, mrq);
+}
+
static int sd_read_data(struct realtek_pci_sdmmc *host, u8 *cmd, u16 byte_cnt,
u8 *buf, int buf_len, int timeout)
{
@@ -213,8 +311,7 @@ static int sd_write_data(struct realtek_pci_sdmmc *host, u8 *cmd, u16 byte_cnt,
return 0;
}
-static void sd_send_cmd_get_rsp(struct realtek_pci_sdmmc *host,
- struct mmc_command *cmd)
+static void sd_send_cmd(struct realtek_pci_sdmmc *host, struct mmc_command *cmd)
{
struct rtsx_pcr *pcr = host->pcr;
u8 cmd_idx = (u8)cmd->opcode;
@@ -222,11 +319,14 @@ static void sd_send_cmd_get_rsp(struct realtek_pci_sdmmc *host,
int err = 0;
int timeout = 100;
int i;
- u8 *ptr;
- int stat_idx = 0;
u8 rsp_type;
int rsp_len = 5;
- bool clock_toggled = false;
+ unsigned long flags;
+
+ if (host->cmd)
+ dev_err(sdmmc_dev(host), "error: cmd already exist\n");
+
+ host->cmd = cmd;
dev_dbg(sdmmc_dev(host), "%s: SD/MMC CMD %d, arg = 0x%08x\n",
__func__, cmd_idx, arg);
@@ -261,6 +361,8 @@ static void sd_send_cmd_get_rsp(struct realtek_pci_sdmmc *host,
err = -EINVAL;
goto out;
}
+ host->rsp_type = rsp_type;
+ host->rsp_len = rsp_len;
if (rsp_type == SD_RSP_TYPE_R1b)
timeout = 3000;
@@ -270,8 +372,6 @@ static void sd_send_cmd_get_rsp(struct realtek_pci_sdmmc *host,
0xFF, SD_CLK_TOGGLE_EN);
if (err < 0)
goto out;
-
- clock_toggled = true;
}
rtsx_pci_init_cmd(pcr);
@@ -295,25 +395,60 @@ static void sd_send_cmd_get_rsp(struct realtek_pci_sdmmc *host,
/* Read data from ping-pong buffer */
for (i = PPBUF_BASE2; i < PPBUF_BASE2 + 16; i++)
rtsx_pci_add_cmd(pcr, READ_REG_CMD, (u16)i, 0, 0);
- stat_idx = 16;
} else if (rsp_type != SD_RSP_TYPE_R0) {
/* Read data from SD_CMDx registers */
for (i = SD_CMD0; i <= SD_CMD4; i++)
rtsx_pci_add_cmd(pcr, READ_REG_CMD, (u16)i, 0, 0);
- stat_idx = 5;
}
rtsx_pci_add_cmd(pcr, READ_REG_CMD, SD_STAT1, 0, 0);
- err = rtsx_pci_send_cmd(pcr, timeout);
- if (err < 0) {
- sd_print_debug_regs(host);
- sd_clear_error(host);
- dev_dbg(sdmmc_dev(host),
- "rtsx_pci_send_cmd error (err = %d)\n", err);
+ mod_timer(&host->timer, jiffies + msecs_to_jiffies(timeout));
+
+ spin_lock_irqsave(&pcr->lock, flags);
+ pcr->trans_result = TRANS_NOT_READY;
+ rtsx_pci_send_cmd_no_wait(pcr);
+ spin_unlock_irqrestore(&pcr->lock, flags);
+
+ return;
+
+out:
+ cmd->error = err;
+ tasklet_schedule(&host->finish_tasklet);
+}
+
+static void sd_get_rsp(unsigned long host_addr)
+{
+ struct realtek_pci_sdmmc *host = (struct realtek_pci_sdmmc *)host_addr;
+ struct rtsx_pcr *pcr = host->pcr;
+ struct mmc_command *cmd;
+ int i, err = 0, stat_idx;
+ u8 *ptr, rsp_type;
+ unsigned long flags;
+
+ spin_lock_irqsave(&host->lock, flags);
+
+ cmd = host->cmd;
+ host->cmd = NULL;
+
+ if (!cmd) {
+ dev_err(sdmmc_dev(host), "error: cmd not exist\n");
goto out;
}
+ spin_lock(&pcr->lock);
+ if (pcr->trans_result == TRANS_NO_DEVICE)
+ err = -ENODEV;
+ else if (pcr->trans_result != TRANS_RESULT_OK)
+ err = -EINVAL;
+ spin_unlock(&pcr->lock);
+
+ if (err < 0)
+ goto out;
+
+ rsp_type = host->rsp_type;
+ stat_idx = host->rsp_len;
+
if (rsp_type == SD_RSP_TYPE_R0) {
err = 0;
goto out;
@@ -350,26 +485,106 @@ static void sd_send_cmd_get_rsp(struct realtek_pci_sdmmc *host,
cmd->resp[0]);
}
+ if (cmd == host->mrq->sbc) {
+ sd_send_cmd(host, host->mrq->cmd);
+ spin_unlock_irqrestore(&host->lock, flags);
+ return;
+ }
+
+ if (cmd == host->mrq->stop)
+ goto out;
+
+ if (cmd->data) {
+ sd_start_multi_rw(host, host->mrq);
+ spin_unlock_irqrestore(&host->lock, flags);
+ return;
+ }
+
out:
cmd->error = err;
- if (err && clock_toggled)
- rtsx_pci_write_register(pcr, SD_BUS_STAT,
- SD_CLK_TOGGLE_EN | SD_CLK_FORCE_STOP, 0);
+ tasklet_schedule(&host->finish_tasklet);
+ spin_unlock_irqrestore(&host->lock, flags);
}
-static int sd_rw_multi(struct realtek_pci_sdmmc *host, struct mmc_request *mrq)
+static int sd_pre_dma_transfer(struct realtek_pci_sdmmc *host,
+ struct mmc_data *data, struct realtek_next *next)
+{
+ struct rtsx_pcr *pcr = host->pcr;
+ int read = data->flags & MMC_DATA_READ;
+ int sg_count = 0;
+
+ if (!next && data->host_cookie &&
+ data->host_cookie != host->next_data.cookie) {
+ dev_err(sdmmc_dev(host),
+ "error: invalid cookie data[%d] host[%d]\n",
+ data->host_cookie, host->next_data.cookie);
+ data->host_cookie = 0;
+ }
+
+ if (next || (!next && data->host_cookie != host->next_data.cookie))
+ sg_count = rtsx_pci_dma_map_sg(pcr,
+ data->sg, data->sg_len, read);
+ else
+ sg_count = host->next_data.sg_count;
+
+ if (next) {
+ next->sg_count = sg_count;
+ if (++next->cookie < 0)
+ next->cookie = 1;
+ data->host_cookie = next->cookie;
+ }
+
+ return sg_count;
+}
+
+static void sdmmc_pre_req(struct mmc_host *mmc, struct mmc_request *mrq,
+ bool is_first_req)
+{
+ struct realtek_pci_sdmmc *host = mmc_priv(mmc);
+ struct mmc_data *data = mrq->data;
+
+ if (data->host_cookie) {
+ dev_err(sdmmc_dev(host),
+ "error: descard already cookie data[%d]\n",
+ data->host_cookie);
+ data->host_cookie = 0;
+ }
+
+ dev_dbg(sdmmc_dev(host), "dma sg prepared: %d\n",
+ sd_pre_dma_transfer(host, data, &host->next_data));
+}
+
+static void sdmmc_post_req(struct mmc_host *mmc, struct mmc_request *mrq,
+ int err)
+{
+ struct realtek_pci_sdmmc *host = mmc_priv(mmc);
+ struct rtsx_pcr *pcr = host->pcr;
+ struct mmc_data *data = mrq->data;
+ int read = data->flags & MMC_DATA_READ;
+
+ rtsx_pci_dma_unmap_sg(pcr, data->sg, data->sg_len, read);
+ data->host_cookie = 0;
+}
+
+static int sd_start_multi_rw(struct realtek_pci_sdmmc *host,
+ struct mmc_request *mrq)
{
struct rtsx_pcr *pcr = host->pcr;
struct mmc_host *mmc = host->mmc;
struct mmc_card *card = mmc->card;
struct mmc_data *data = mrq->data;
int uhs = mmc_card_uhs(card);
- int read = (data->flags & MMC_DATA_READ) ? 1 : 0;
+ int read = data->flags & MMC_DATA_READ;
u8 cfg2, trans_mode;
int err;
size_t data_len = data->blksz * data->blocks;
+ if (host->data)
+ dev_err(sdmmc_dev(host), "error: data already exist\n");
+
+ host->data = data;
+
if (read) {
cfg2 = SD_CALCULATE_CRC7 | SD_CHECK_CRC16 |
SD_NO_WAIT_BUSY_END | SD_CHECK_CRC7 | SD_RSP_LEN_0;
@@ -420,17 +635,56 @@ static int sd_rw_multi(struct realtek_pci_sdmmc *host, struct mmc_request *mrq)
rtsx_pci_add_cmd(pcr, CHECK_REG_CMD, SD_TRANSFER,
SD_TRANSFER_END, SD_TRANSFER_END);
+ mod_timer(&host->timer, jiffies + 10 * HZ);
rtsx_pci_send_cmd_no_wait(pcr);
- err = rtsx_pci_transfer_data(pcr, data->sg, data->sg_len, read, 10000);
+ err = rtsx_pci_dma_transfer(pcr, data->sg, host->sg_count, read);
if (err < 0) {
- sd_clear_error(host);
- return err;
+ data->error = err;
+ tasklet_schedule(&host->finish_tasklet);
}
-
return 0;
}
+static void sd_finish_multi_rw(unsigned long host_addr)
+{
+ struct realtek_pci_sdmmc *host = (struct realtek_pci_sdmmc *)host_addr;
+ struct rtsx_pcr *pcr = host->pcr;
+ struct mmc_data *data;
+ int err = 0;
+ unsigned long flags;
+
+ spin_lock_irqsave(&host->lock, flags);
+
+ if (!host->data) {
+ dev_err(sdmmc_dev(host), "error: no data exist\n");
+ goto out;
+ }
+
+ data = host->data;
+ host->data = NULL;
+
+ if (pcr->trans_result == TRANS_NO_DEVICE)
+ err = -ENODEV;
+ else if (pcr->trans_result != TRANS_RESULT_OK)
+ err = -EINVAL;
+
+ if (err < 0) {
+ data->error = err;
+ goto out;
+ }
+
+ if (!host->mrq->sbc && data->stop) {
+ sd_send_cmd(host, data->stop);
+ spin_unlock_irqrestore(&host->lock, flags);
+ return;
+ }
+
+out:
+ tasklet_schedule(&host->finish_tasklet);
+ spin_unlock_irqrestore(&host->lock, flags);
+}
+
static inline void sd_enable_initial_mode(struct realtek_pci_sdmmc *host)
{
rtsx_pci_write_register(host->pcr, SD_CFG1,
@@ -511,85 +765,47 @@ static int sd_change_phase(struct realtek_pci_sdmmc *host,
return 0;
}
-static u8 sd_search_final_phase(struct realtek_pci_sdmmc *host, u32 phase_map)
+static inline u32 test_phase_bit(u32 phase_map, unsigned int bit)
{
- struct timing_phase_path path[MAX_PHASE + 1];
- int i, j, cont_path_cnt;
- int new_block, max_len, final_path_idx;
- u8 final_phase = 0xFF;
+ bit %= RTSX_PHASE_MAX;
+ return phase_map & (1 << bit);
+}
- /* Parse phase_map, take it as a bit-ring */
- cont_path_cnt = 0;
- new_block = 1;
- j = 0;
- for (i = 0; i < MAX_PHASE + 1; i++) {
- if (phase_map & (1 << i)) {
- if (new_block) {
- new_block = 0;
- j = cont_path_cnt++;
- path[j].start = i;
- path[j].end = i;
- } else {
- path[j].end = i;
- }
- } else {
- new_block = 1;
- if (cont_path_cnt) {
- /* Calculate path length and middle point */
- int idx = cont_path_cnt - 1;
- path[idx].len =
- path[idx].end - path[idx].start + 1;
- path[idx].mid =
- path[idx].start + path[idx].len / 2;
- }
- }
- }
+static int sd_get_phase_len(u32 phase_map, unsigned int start_bit)
+{
+ int i;
- if (cont_path_cnt == 0) {
- dev_dbg(sdmmc_dev(host), "No continuous phase path\n");
- goto finish;
- } else {
- /* Calculate last continuous path length and middle point */
- int idx = cont_path_cnt - 1;
- path[idx].len = path[idx].end - path[idx].start + 1;
- path[idx].mid = path[idx].start + path[idx].len / 2;
+ for (i = 0; i < RTSX_PHASE_MAX; i++) {
+ if (test_phase_bit(phase_map, start_bit + i) == 0)
+ return i;
}
+ return RTSX_PHASE_MAX;
+}
+
+static u8 sd_search_final_phase(struct realtek_pci_sdmmc *host, u32 phase_map)
+{
+ int start = 0, len = 0;
+ int start_final = 0, len_final = 0;
+ u8 final_phase = 0xFF;
- /* Connect the first and last continuous paths if they are adjacent */
- if (!path[0].start && (path[cont_path_cnt - 1].end == MAX_PHASE)) {
- /* Using negative index */
- path[0].start = path[cont_path_cnt - 1].start - MAX_PHASE - 1;
- path[0].len += path[cont_path_cnt - 1].len;
- path[0].mid = path[0].start + path[0].len / 2;
- /* Convert negative middle point index to positive one */
- if (path[0].mid < 0)
- path[0].mid += MAX_PHASE + 1;
- cont_path_cnt--;
+ if (phase_map == 0) {
+ dev_err(sdmmc_dev(host), "phase error: [map:%x]\n", phase_map);
+ return final_phase;
}
- /* Choose the longest continuous phase path */
- max_len = 0;
- final_phase = 0;
- final_path_idx = 0;
- for (i = 0; i < cont_path_cnt; i++) {
- if (path[i].len > max_len) {
- max_len = path[i].len;
- final_phase = (u8)path[i].mid;
- final_path_idx = i;
+ while (start < RTSX_PHASE_MAX) {
+ len = sd_get_phase_len(phase_map, start);
+ if (len_final < len) {
+ start_final = start;
+ len_final = len;
}
-
- dev_dbg(sdmmc_dev(host), "path[%d].start = %d\n",
- i, path[i].start);
- dev_dbg(sdmmc_dev(host), "path[%d].end = %d\n",
- i, path[i].end);
- dev_dbg(sdmmc_dev(host), "path[%d].len = %d\n",
- i, path[i].len);
- dev_dbg(sdmmc_dev(host), "path[%d].mid = %d\n",
- i, path[i].mid);
+ start += len ? len : 1;
}
-finish:
- dev_dbg(sdmmc_dev(host), "Final chosen phase: %d\n", final_phase);
+ final_phase = (start_final + len_final / 2) % RTSX_PHASE_MAX;
+ dev_dbg(sdmmc_dev(host), "phase: [map:%x] [maxlen:%d] [final:%d]\n",
+ phase_map, len_final, final_phase);
+
return final_phase;
}
@@ -635,7 +851,7 @@ static int sd_tuning_phase(struct realtek_pci_sdmmc *host,
int err, i;
u32 raw_phase_map = 0;
- for (i = MAX_PHASE; i >= 0; i--) {
+ for (i = 0; i < RTSX_PHASE_MAX; i++) {
err = sd_tuning_rx_cmd(host, opcode, (u8)i);
if (err == 0)
raw_phase_map |= 1 << i;
@@ -685,6 +901,13 @@ static int sd_tuning_rx(struct realtek_pci_sdmmc *host, u8 opcode)
return 0;
}
+static inline bool sd_use_muti_rw(struct mmc_command *cmd)
+{
+ return mmc_op_multi(cmd->opcode) ||
+ (cmd->opcode == MMC_READ_SINGLE_BLOCK) ||
+ (cmd->opcode == MMC_WRITE_BLOCK);
+}
+
static void sdmmc_request(struct mmc_host *mmc, struct mmc_request *mrq)
{
struct realtek_pci_sdmmc *host = mmc_priv(mmc);
@@ -693,6 +916,14 @@ static void sdmmc_request(struct mmc_host *mmc, struct mmc_request *mrq)
struct mmc_data *data = mrq->data;
unsigned int data_size = 0;
int err;
+ unsigned long flags;
+
+ mutex_lock(&pcr->pcr_mutex);
+ spin_lock_irqsave(&host->lock, flags);
+
+ if (host->mrq)
+ dev_err(sdmmc_dev(host), "error: request already exist\n");
+ host->mrq = mrq;
if (host->eject) {
cmd->error = -ENOMEDIUM;
@@ -705,8 +936,6 @@ static void sdmmc_request(struct mmc_host *mmc, struct mmc_request *mrq)
goto finish;
}
- mutex_lock(&pcr->pcr_mutex);
-
rtsx_pci_start_run(pcr);
rtsx_pci_switch_clock(pcr, host->clock, host->ssc_depth,
@@ -715,46 +944,28 @@ static void sdmmc_request(struct mmc_host *mmc, struct mmc_request *mrq)
rtsx_pci_write_register(pcr, CARD_SHARE_MODE,
CARD_SHARE_MASK, CARD_SHARE_48_SD);
- mutex_lock(&host->host_mutex);
- host->mrq = mrq;
- mutex_unlock(&host->host_mutex);
-
if (mrq->data)
data_size = data->blocks * data->blksz;
- if (!data_size || mmc_op_multi(cmd->opcode) ||
- (cmd->opcode == MMC_READ_SINGLE_BLOCK) ||
- (cmd->opcode == MMC_WRITE_BLOCK)) {
- sd_send_cmd_get_rsp(host, cmd);
-
- if (!cmd->error && data_size) {
- sd_rw_multi(host, mrq);
+ if (sd_use_muti_rw(cmd))
+ host->sg_count = sd_pre_dma_transfer(host, data, NULL);
- if (mmc_op_multi(cmd->opcode) && mrq->stop)
- sd_send_cmd_get_rsp(host, mrq->stop);
- }
+ if (!data_size || sd_use_muti_rw(cmd)) {
+ if (mrq->sbc)
+ sd_send_cmd(host, mrq->sbc);
+ else
+ sd_send_cmd(host, cmd);
+ spin_unlock_irqrestore(&host->lock, flags);
} else {
+ spin_unlock_irqrestore(&host->lock, flags);
sd_normal_rw(host, mrq);
+ tasklet_schedule(&host->finish_tasklet);
}
-
- if (mrq->data) {
- if (cmd->error || data->error)
- data->bytes_xfered = 0;
- else
- data->bytes_xfered = data->blocks * data->blksz;
- }
-
- mutex_unlock(&pcr->pcr_mutex);
+ return;
finish:
- if (cmd->error)
- dev_dbg(sdmmc_dev(host), "cmd->error = %d\n", cmd->error);
-
- mutex_lock(&host->host_mutex);
- host->mrq = NULL;
- mutex_unlock(&host->host_mutex);
-
- mmc_request_done(mmc, mrq);
+ tasklet_schedule(&host->finish_tasklet);
+ spin_unlock_irqrestore(&host->lock, flags);
}
static int sd_set_bus_width(struct realtek_pci_sdmmc *host,
@@ -1189,6 +1400,8 @@ out:
}
static const struct mmc_host_ops realtek_pci_sdmmc_ops = {
+ .pre_req = sdmmc_pre_req,
+ .post_req = sdmmc_post_req,
.request = sdmmc_request,
.set_ios = sdmmc_set_ios,
.get_ro = sdmmc_get_ro,
@@ -1252,6 +1465,7 @@ static int rtsx_pci_sdmmc_drv_probe(struct platform_device *pdev)
struct realtek_pci_sdmmc *host;
struct rtsx_pcr *pcr;
struct pcr_handle *handle = pdev->dev.platform_data;
+ unsigned long host_addr;
if (!handle)
return -ENXIO;
@@ -1275,8 +1489,15 @@ static int rtsx_pci_sdmmc_drv_probe(struct platform_device *pdev)
pcr->slots[RTSX_SD_CARD].p_dev = pdev;
pcr->slots[RTSX_SD_CARD].card_event = rtsx_pci_sdmmc_card_event;
- mutex_init(&host->host_mutex);
+ host_addr = (unsigned long)host;
+ host->next_data.cookie = 1;
+ setup_timer(&host->timer, sd_request_timeout, host_addr);
+ tasklet_init(&host->cmd_tasklet, sd_get_rsp, host_addr);
+ tasklet_init(&host->data_tasklet, sd_finish_multi_rw, host_addr);
+ tasklet_init(&host->finish_tasklet, sd_finish_request, host_addr);
+ spin_lock_init(&host->lock);
+ pcr->slots[RTSX_SD_CARD].done_transfer = sd_isr_done_transfer;
realtek_init_host(host);
mmc_add_host(mmc);
@@ -1289,6 +1510,8 @@ static int rtsx_pci_sdmmc_drv_remove(struct platform_device *pdev)
struct realtek_pci_sdmmc *host = platform_get_drvdata(pdev);
struct rtsx_pcr *pcr;
struct mmc_host *mmc;
+ struct mmc_request *mrq;
+ unsigned long flags;
if (!host)
return 0;
@@ -1296,25 +1519,37 @@ static int rtsx_pci_sdmmc_drv_remove(struct platform_device *pdev)
pcr = host->pcr;
pcr->slots[RTSX_SD_CARD].p_dev = NULL;
pcr->slots[RTSX_SD_CARD].card_event = NULL;
+ pcr->slots[RTSX_SD_CARD].done_transfer = NULL;
mmc = host->mmc;
- host->eject = true;
+ mrq = host->mrq;
- mutex_lock(&host->host_mutex);
+ spin_lock_irqsave(&host->lock, flags);
if (host->mrq) {
dev_dbg(&(pdev->dev),
"%s: Controller removed during transfer\n",
mmc_hostname(mmc));
- rtsx_pci_complete_unfinished_transfer(pcr);
+ if (mrq->sbc)
+ mrq->sbc->error = -ENOMEDIUM;
+ if (mrq->cmd)
+ mrq->cmd->error = -ENOMEDIUM;
+ if (mrq->stop)
+ mrq->stop->error = -ENOMEDIUM;
+ if (mrq->data)
+ mrq->data->error = -ENOMEDIUM;
- host->mrq->cmd->error = -ENOMEDIUM;
- if (host->mrq->stop)
- host->mrq->stop->error = -ENOMEDIUM;
- mmc_request_done(mmc, host->mrq);
+ tasklet_schedule(&host->finish_tasklet);
}
- mutex_unlock(&host->host_mutex);
+ spin_unlock_irqrestore(&host->lock, flags);
+
+ del_timer_sync(&host->timer);
+ tasklet_kill(&host->cmd_tasklet);
+ tasklet_kill(&host->data_tasklet);
+ tasklet_kill(&host->finish_tasklet);
mmc_remove_host(mmc);
+ host->eject = true;
+
mmc_free_host(mmc);
dev_dbg(&(pdev->dev),
diff --git a/drivers/mmc/host/sdhci-acpi.c b/drivers/mmc/host/sdhci-acpi.c
index 9ce17f6e4014..ebb3f392b589 100644
--- a/drivers/mmc/host/sdhci-acpi.c
+++ b/drivers/mmc/host/sdhci-acpi.c
@@ -31,7 +31,6 @@
#include <linux/bitops.h>
#include <linux/types.h>
#include <linux/err.h>
-#include <linux/gpio/consumer.h>
#include <linux/interrupt.h>
#include <linux/acpi.h>
#include <linux/pm.h>
@@ -40,13 +39,15 @@
#include <linux/mmc/host.h>
#include <linux/mmc/pm.h>
+#include <linux/mmc/slot-gpio.h>
#include <linux/mmc/sdhci.h>
#include "sdhci.h"
enum {
- SDHCI_ACPI_SD_CD = BIT(0),
- SDHCI_ACPI_RUNTIME_PM = BIT(1),
+ SDHCI_ACPI_SD_CD = BIT(0),
+ SDHCI_ACPI_RUNTIME_PM = BIT(1),
+ SDHCI_ACPI_SD_CD_OVERRIDE_LEVEL = BIT(2),
};
struct sdhci_acpi_chip {
@@ -121,6 +122,7 @@ static const struct sdhci_acpi_slot sdhci_acpi_slot_int_emmc = {
};
static const struct sdhci_acpi_slot sdhci_acpi_slot_int_sdio = {
+ .quirks = SDHCI_QUIRK_BROKEN_CARD_DETECTION,
.quirks2 = SDHCI_QUIRK2_HOST_OFF_CARD_ON,
.caps = MMC_CAP_NONREMOVABLE | MMC_CAP_POWER_OFF_CARD,
.flags = SDHCI_ACPI_RUNTIME_PM,
@@ -128,7 +130,8 @@ static const struct sdhci_acpi_slot sdhci_acpi_slot_int_sdio = {
};
static const struct sdhci_acpi_slot sdhci_acpi_slot_int_sd = {
- .flags = SDHCI_ACPI_SD_CD | SDHCI_ACPI_RUNTIME_PM,
+ .flags = SDHCI_ACPI_SD_CD | SDHCI_ACPI_SD_CD_OVERRIDE_LEVEL |
+ SDHCI_ACPI_RUNTIME_PM,
.quirks2 = SDHCI_QUIRK2_CARD_ON_NEEDS_BUS_ON,
};
@@ -141,6 +144,7 @@ struct sdhci_acpi_uid_slot {
static const struct sdhci_acpi_uid_slot sdhci_acpi_uids[] = {
{ "80860F14" , "1" , &sdhci_acpi_slot_int_emmc },
{ "80860F14" , "3" , &sdhci_acpi_slot_int_sd },
+ { "80860F16" , NULL, &sdhci_acpi_slot_int_sd },
{ "INT33BB" , "2" , &sdhci_acpi_slot_int_sdio },
{ "INT33C6" , NULL, &sdhci_acpi_slot_int_sdio },
{ "INT3436" , NULL, &sdhci_acpi_slot_int_sdio },
@@ -150,6 +154,7 @@ static const struct sdhci_acpi_uid_slot sdhci_acpi_uids[] = {
static const struct acpi_device_id sdhci_acpi_ids[] = {
{ "80860F14" },
+ { "80860F16" },
{ "INT33BB" },
{ "INT33C6" },
{ "INT3436" },
@@ -192,59 +197,6 @@ static const struct sdhci_acpi_slot *sdhci_acpi_get_slot(acpi_handle handle,
return slot;
}
-#ifdef CONFIG_PM_RUNTIME
-
-static irqreturn_t sdhci_acpi_sd_cd(int irq, void *dev_id)
-{
- mmc_detect_change(dev_id, msecs_to_jiffies(200));
- return IRQ_HANDLED;
-}
-
-static int sdhci_acpi_add_own_cd(struct device *dev, struct mmc_host *mmc)
-{
- struct gpio_desc *desc;
- unsigned long flags;
- int err, irq;
-
- desc = devm_gpiod_get_index(dev, "sd_cd", 0);
- if (IS_ERR(desc)) {
- err = PTR_ERR(desc);
- goto out;
- }
-
- err = gpiod_direction_input(desc);
- if (err)
- goto out_free;
-
- irq = gpiod_to_irq(desc);
- if (irq < 0) {
- err = irq;
- goto out_free;
- }
-
- flags = IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING;
- err = devm_request_irq(dev, irq, sdhci_acpi_sd_cd, flags, "sd_cd", mmc);
- if (err)
- goto out_free;
-
- return 0;
-
-out_free:
- devm_gpiod_put(dev, desc);
-out:
- dev_warn(dev, "failed to setup card detect wake up\n");
- return err;
-}
-
-#else
-
-static int sdhci_acpi_add_own_cd(struct device *dev, struct mmc_host *mmc)
-{
- return 0;
-}
-
-#endif
-
static int sdhci_acpi_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
@@ -332,15 +284,19 @@ static int sdhci_acpi_probe(struct platform_device *pdev)
host->mmc->caps2 |= MMC_CAP2_NO_PRESCAN_POWERUP;
- err = sdhci_add_host(host);
- if (err)
- goto err_free;
-
if (sdhci_acpi_flag(c, SDHCI_ACPI_SD_CD)) {
- if (sdhci_acpi_add_own_cd(dev, host->mmc))
+ bool v = sdhci_acpi_flag(c, SDHCI_ACPI_SD_CD_OVERRIDE_LEVEL);
+
+ if (mmc_gpiod_request_cd(host->mmc, NULL, 0, v, 0)) {
+ dev_warn(dev, "failed to setup card detect gpio\n");
c->use_runtime_pm = false;
+ }
}
+ err = sdhci_add_host(host);
+ if (err)
+ goto err_free;
+
if (c->use_runtime_pm) {
pm_runtime_set_active(dev);
pm_suspend_ignore_children(dev, 1);
diff --git a/drivers/mmc/host/sdhci-bcm-kona.c b/drivers/mmc/host/sdhci-bcm-kona.c
index 7a190fe4dff1..6f166e63b817 100644
--- a/drivers/mmc/host/sdhci-bcm-kona.c
+++ b/drivers/mmc/host/sdhci-bcm-kona.c
@@ -54,6 +54,7 @@
struct sdhci_bcm_kona_dev {
struct mutex write_lock; /* protect back to back writes */
+ struct clk *external_clk;
};
@@ -257,6 +258,24 @@ static int sdhci_bcm_kona_probe(struct platform_device *pdev)
goto err_pltfm_free;
}
+ /* Get and enable the external clock */
+ kona_dev->external_clk = devm_clk_get(dev, NULL);
+ if (IS_ERR(kona_dev->external_clk)) {
+ dev_err(dev, "Failed to get external clock\n");
+ ret = PTR_ERR(kona_dev->external_clk);
+ goto err_pltfm_free;
+ }
+
+ if (clk_set_rate(kona_dev->external_clk, host->mmc->f_max) != 0) {
+ dev_err(dev, "Failed to set rate external clock\n");
+ goto err_pltfm_free;
+ }
+
+ if (clk_prepare_enable(kona_dev->external_clk) != 0) {
+ dev_err(dev, "Failed to enable external clock\n");
+ goto err_pltfm_free;
+ }
+
dev_dbg(dev, "non-removable=%c\n",
(host->mmc->caps & MMC_CAP_NONREMOVABLE) ? 'Y' : 'N');
dev_dbg(dev, "cd_gpio %c, wp_gpio %c\n",
@@ -271,7 +290,7 @@ static int sdhci_bcm_kona_probe(struct platform_device *pdev)
ret = sdhci_bcm_kona_sd_reset(host);
if (ret)
- goto err_pltfm_free;
+ goto err_clk_disable;
sdhci_bcm_kona_sd_init(host);
@@ -307,6 +326,9 @@ err_remove_host:
err_reset:
sdhci_bcm_kona_sd_reset(host);
+err_clk_disable:
+ clk_disable_unprepare(kona_dev->external_clk);
+
err_pltfm_free:
sdhci_pltfm_free(pdev);
@@ -314,9 +336,20 @@ err_pltfm_free:
return ret;
}
-static int __exit sdhci_bcm_kona_remove(struct platform_device *pdev)
+static int sdhci_bcm_kona_remove(struct platform_device *pdev)
{
- return sdhci_pltfm_unregister(pdev);
+ struct sdhci_host *host = platform_get_drvdata(pdev);
+ struct sdhci_pltfm_host *pltfm_priv = sdhci_priv(host);
+ struct sdhci_bcm_kona_dev *kona_dev = sdhci_pltfm_priv(pltfm_priv);
+ int dead = (readl(host->ioaddr + SDHCI_INT_STATUS) == 0xffffffff);
+
+ sdhci_remove_host(host, dead);
+
+ clk_disable_unprepare(kona_dev->external_clk);
+
+ sdhci_pltfm_free(pdev);
+
+ return 0;
}
static struct platform_driver sdhci_bcm_kona_driver = {
diff --git a/drivers/mmc/host/sdhci-dove.c b/drivers/mmc/host/sdhci-dove.c
index 8424839660f8..736d7a2eb7ec 100644
--- a/drivers/mmc/host/sdhci-dove.c
+++ b/drivers/mmc/host/sdhci-dove.c
@@ -208,7 +208,7 @@ static struct platform_driver sdhci_dove_driver = {
.name = "sdhci-dove",
.owner = THIS_MODULE,
.pm = SDHCI_PLTFM_PMOPS,
- .of_match_table = of_match_ptr(sdhci_dove_of_match_table),
+ .of_match_table = sdhci_dove_of_match_table,
},
.probe = sdhci_dove_probe,
.remove = sdhci_dove_remove,
diff --git a/drivers/mmc/host/sdhci-msm.c b/drivers/mmc/host/sdhci-msm.c
new file mode 100644
index 000000000000..acb0e9eb55f1
--- /dev/null
+++ b/drivers/mmc/host/sdhci-msm.c
@@ -0,0 +1,618 @@
+/*
+ * drivers/mmc/host/sdhci-msm.c - Qualcomm SDHCI Platform driver
+ *
+ * Copyright (c) 2013-2014, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/of_device.h>
+#include <linux/regulator/consumer.h>
+#include <linux/delay.h>
+#include <linux/mmc/mmc.h>
+#include <linux/slab.h>
+
+#include "sdhci-pltfm.h"
+
+#define CORE_HC_MODE 0x78
+#define HC_MODE_EN 0x1
+#define CORE_POWER 0x0
+#define CORE_SW_RST BIT(7)
+
+#define MAX_PHASES 16
+#define CORE_DLL_LOCK BIT(7)
+#define CORE_DLL_EN BIT(16)
+#define CORE_CDR_EN BIT(17)
+#define CORE_CK_OUT_EN BIT(18)
+#define CORE_CDR_EXT_EN BIT(19)
+#define CORE_DLL_PDN BIT(29)
+#define CORE_DLL_RST BIT(30)
+#define CORE_DLL_CONFIG 0x100
+#define CORE_DLL_STATUS 0x108
+
+#define CORE_VENDOR_SPEC 0x10c
+#define CORE_CLK_PWRSAVE BIT(1)
+
+#define CDR_SELEXT_SHIFT 20
+#define CDR_SELEXT_MASK (0xf << CDR_SELEXT_SHIFT)
+#define CMUX_SHIFT_PHASE_SHIFT 24
+#define CMUX_SHIFT_PHASE_MASK (7 << CMUX_SHIFT_PHASE_SHIFT)
+
+static const u32 tuning_block_64[] = {
+ 0x00ff0fff, 0xccc3ccff, 0xffcc3cc3, 0xeffefffe,
+ 0xddffdfff, 0xfbfffbff, 0xff7fffbf, 0xefbdf777,
+ 0xf0fff0ff, 0x3cccfc0f, 0xcfcc33cc, 0xeeffefff,
+ 0xfdfffdff, 0xffbfffdf, 0xfff7ffbb, 0xde7b7ff7
+};
+
+static const u32 tuning_block_128[] = {
+ 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
+};
+
+struct sdhci_msm_host {
+ struct platform_device *pdev;
+ void __iomem *core_mem; /* MSM SDCC mapped address */
+ struct clk *clk; /* main SD/MMC bus clock */
+ struct clk *pclk; /* SDHC peripheral bus clock */
+ struct clk *bus_clk; /* SDHC bus voter clock */
+ struct mmc_host *mmc;
+ struct sdhci_pltfm_data sdhci_msm_pdata;
+};
+
+/* Platform specific tuning */
+static inline int msm_dll_poll_ck_out_en(struct sdhci_host *host, u8 poll)
+{
+ u32 wait_cnt = 50;
+ u8 ck_out_en;
+ struct mmc_host *mmc = host->mmc;
+
+ /* Poll for CK_OUT_EN bit. max. poll time = 50us */
+ ck_out_en = !!(readl_relaxed(host->ioaddr + CORE_DLL_CONFIG) &
+ CORE_CK_OUT_EN);
+
+ while (ck_out_en != poll) {
+ if (--wait_cnt == 0) {
+ dev_err(mmc_dev(mmc), "%s: CK_OUT_EN bit is not %d\n",
+ mmc_hostname(mmc), poll);
+ return -ETIMEDOUT;
+ }
+ udelay(1);
+
+ ck_out_en = !!(readl_relaxed(host->ioaddr + CORE_DLL_CONFIG) &
+ CORE_CK_OUT_EN);
+ }
+
+ return 0;
+}
+
+static int msm_config_cm_dll_phase(struct sdhci_host *host, u8 phase)
+{
+ int rc;
+ static const u8 grey_coded_phase_table[] = {
+ 0x0, 0x1, 0x3, 0x2, 0x6, 0x7, 0x5, 0x4,
+ 0xc, 0xd, 0xf, 0xe, 0xa, 0xb, 0x9, 0x8
+ };
+ unsigned long flags;
+ u32 config;
+ struct mmc_host *mmc = host->mmc;
+
+ spin_lock_irqsave(&host->lock, flags);
+
+ config = readl_relaxed(host->ioaddr + CORE_DLL_CONFIG);
+ config &= ~(CORE_CDR_EN | CORE_CK_OUT_EN);
+ config |= (CORE_CDR_EXT_EN | CORE_DLL_EN);
+ writel_relaxed(config, host->ioaddr + CORE_DLL_CONFIG);
+
+ /* Wait until CK_OUT_EN bit of DLL_CONFIG register becomes '0' */
+ rc = msm_dll_poll_ck_out_en(host, 0);
+ if (rc)
+ goto err_out;
+
+ /*
+ * Write the selected DLL clock output phase (0 ... 15)
+ * to CDR_SELEXT bit field of DLL_CONFIG register.
+ */
+ config = readl_relaxed(host->ioaddr + CORE_DLL_CONFIG);
+ config &= ~CDR_SELEXT_MASK;
+ config |= grey_coded_phase_table[phase] << CDR_SELEXT_SHIFT;
+ writel_relaxed(config, host->ioaddr + CORE_DLL_CONFIG);
+
+ /* Set CK_OUT_EN bit of DLL_CONFIG register to 1. */
+ writel_relaxed((readl_relaxed(host->ioaddr + CORE_DLL_CONFIG)
+ | CORE_CK_OUT_EN), host->ioaddr + CORE_DLL_CONFIG);
+
+ /* Wait until CK_OUT_EN bit of DLL_CONFIG register becomes '1' */
+ rc = msm_dll_poll_ck_out_en(host, 1);
+ if (rc)
+ goto err_out;
+
+ config = readl_relaxed(host->ioaddr + CORE_DLL_CONFIG);
+ config |= CORE_CDR_EN;
+ config &= ~CORE_CDR_EXT_EN;
+ writel_relaxed(config, host->ioaddr + CORE_DLL_CONFIG);
+ goto out;
+
+err_out:
+ dev_err(mmc_dev(mmc), "%s: Failed to set DLL phase: %d\n",
+ mmc_hostname(mmc), phase);
+out:
+ spin_unlock_irqrestore(&host->lock, flags);
+ return rc;
+}
+
+/*
+ * Find out the greatest range of consecuitive selected
+ * DLL clock output phases that can be used as sampling
+ * setting for SD3.0 UHS-I card read operation (in SDR104
+ * timing mode) or for eMMC4.5 card read operation (in HS200
+ * timing mode).
+ * Select the 3/4 of the range and configure the DLL with the
+ * selected DLL clock output phase.
+ */
+
+static int msm_find_most_appropriate_phase(struct sdhci_host *host,
+ u8 *phase_table, u8 total_phases)
+{
+ int ret;
+ u8 ranges[MAX_PHASES][MAX_PHASES] = { {0}, {0} };
+ u8 phases_per_row[MAX_PHASES] = { 0 };
+ int row_index = 0, col_index = 0, selected_row_index = 0, curr_max = 0;
+ int i, cnt, phase_0_raw_index = 0, phase_15_raw_index = 0;
+ bool phase_0_found = false, phase_15_found = false;
+ struct mmc_host *mmc = host->mmc;
+
+ if (!total_phases || (total_phases > MAX_PHASES)) {
+ dev_err(mmc_dev(mmc), "%s: Invalid argument: total_phases=%d\n",
+ mmc_hostname(mmc), total_phases);
+ return -EINVAL;
+ }
+
+ for (cnt = 0; cnt < total_phases; cnt++) {
+ ranges[row_index][col_index] = phase_table[cnt];
+ phases_per_row[row_index] += 1;
+ col_index++;
+
+ if ((cnt + 1) == total_phases) {
+ continue;
+ /* check if next phase in phase_table is consecutive or not */
+ } else if ((phase_table[cnt] + 1) != phase_table[cnt + 1]) {
+ row_index++;
+ col_index = 0;
+ }
+ }
+
+ if (row_index >= MAX_PHASES)
+ return -EINVAL;
+
+ /* Check if phase-0 is present in first valid window? */
+ if (!ranges[0][0]) {
+ phase_0_found = true;
+ phase_0_raw_index = 0;
+ /* Check if cycle exist between 2 valid windows */
+ for (cnt = 1; cnt <= row_index; cnt++) {
+ if (phases_per_row[cnt]) {
+ for (i = 0; i < phases_per_row[cnt]; i++) {
+ if (ranges[cnt][i] == 15) {
+ phase_15_found = true;
+ phase_15_raw_index = cnt;
+ break;
+ }
+ }
+ }
+ }
+ }
+
+ /* If 2 valid windows form cycle then merge them as single window */
+ if (phase_0_found && phase_15_found) {
+ /* number of phases in raw where phase 0 is present */
+ u8 phases_0 = phases_per_row[phase_0_raw_index];
+ /* number of phases in raw where phase 15 is present */
+ u8 phases_15 = phases_per_row[phase_15_raw_index];
+
+ if (phases_0 + phases_15 >= MAX_PHASES)
+ /*
+ * If there are more than 1 phase windows then total
+ * number of phases in both the windows should not be
+ * more than or equal to MAX_PHASES.
+ */
+ return -EINVAL;
+
+ /* Merge 2 cyclic windows */
+ i = phases_15;
+ for (cnt = 0; cnt < phases_0; cnt++) {
+ ranges[phase_15_raw_index][i] =
+ ranges[phase_0_raw_index][cnt];
+ if (++i >= MAX_PHASES)
+ break;
+ }
+
+ phases_per_row[phase_0_raw_index] = 0;
+ phases_per_row[phase_15_raw_index] = phases_15 + phases_0;
+ }
+
+ for (cnt = 0; cnt <= row_index; cnt++) {
+ if (phases_per_row[cnt] > curr_max) {
+ curr_max = phases_per_row[cnt];
+ selected_row_index = cnt;
+ }
+ }
+
+ i = (curr_max * 3) / 4;
+ if (i)
+ i--;
+
+ ret = ranges[selected_row_index][i];
+
+ if (ret >= MAX_PHASES) {
+ ret = -EINVAL;
+ dev_err(mmc_dev(mmc), "%s: Invalid phase selected=%d\n",
+ mmc_hostname(mmc), ret);
+ }
+
+ return ret;
+}
+
+static inline void msm_cm_dll_set_freq(struct sdhci_host *host)
+{
+ u32 mclk_freq = 0, config;
+
+ /* Program the MCLK value to MCLK_FREQ bit field */
+ if (host->clock <= 112000000)
+ mclk_freq = 0;
+ else if (host->clock <= 125000000)
+ mclk_freq = 1;
+ else if (host->clock <= 137000000)
+ mclk_freq = 2;
+ else if (host->clock <= 150000000)
+ mclk_freq = 3;
+ else if (host->clock <= 162000000)
+ mclk_freq = 4;
+ else if (host->clock <= 175000000)
+ mclk_freq = 5;
+ else if (host->clock <= 187000000)
+ mclk_freq = 6;
+ else if (host->clock <= 200000000)
+ mclk_freq = 7;
+
+ config = readl_relaxed(host->ioaddr + CORE_DLL_CONFIG);
+ config &= ~CMUX_SHIFT_PHASE_MASK;
+ config |= mclk_freq << CMUX_SHIFT_PHASE_SHIFT;
+ writel_relaxed(config, host->ioaddr + CORE_DLL_CONFIG);
+}
+
+/* Initialize the DLL (Programmable Delay Line) */
+static int msm_init_cm_dll(struct sdhci_host *host)
+{
+ struct mmc_host *mmc = host->mmc;
+ int wait_cnt = 50;
+ unsigned long flags;
+
+ spin_lock_irqsave(&host->lock, flags);
+
+ /*
+ * Make sure that clock is always enabled when DLL
+ * tuning is in progress. Keeping PWRSAVE ON may
+ * turn off the clock.
+ */
+ writel_relaxed((readl_relaxed(host->ioaddr + CORE_VENDOR_SPEC)
+ & ~CORE_CLK_PWRSAVE), host->ioaddr + CORE_VENDOR_SPEC);
+
+ /* Write 1 to DLL_RST bit of DLL_CONFIG register */
+ writel_relaxed((readl_relaxed(host->ioaddr + CORE_DLL_CONFIG)
+ | CORE_DLL_RST), host->ioaddr + CORE_DLL_CONFIG);
+
+ /* Write 1 to DLL_PDN bit of DLL_CONFIG register */
+ writel_relaxed((readl_relaxed(host->ioaddr + CORE_DLL_CONFIG)
+ | CORE_DLL_PDN), host->ioaddr + CORE_DLL_CONFIG);
+ msm_cm_dll_set_freq(host);
+
+ /* Write 0 to DLL_RST bit of DLL_CONFIG register */
+ writel_relaxed((readl_relaxed(host->ioaddr + CORE_DLL_CONFIG)
+ & ~CORE_DLL_RST), host->ioaddr + CORE_DLL_CONFIG);
+
+ /* Write 0 to DLL_PDN bit of DLL_CONFIG register */
+ writel_relaxed((readl_relaxed(host->ioaddr + CORE_DLL_CONFIG)
+ & ~CORE_DLL_PDN), host->ioaddr + CORE_DLL_CONFIG);
+
+ /* Set DLL_EN bit to 1. */
+ writel_relaxed((readl_relaxed(host->ioaddr + CORE_DLL_CONFIG)
+ | CORE_DLL_EN), host->ioaddr + CORE_DLL_CONFIG);
+
+ /* Set CK_OUT_EN bit to 1. */
+ writel_relaxed((readl_relaxed(host->ioaddr + CORE_DLL_CONFIG)
+ | CORE_CK_OUT_EN), host->ioaddr + CORE_DLL_CONFIG);
+
+ /* Wait until DLL_LOCK bit of DLL_STATUS register becomes '1' */
+ while (!(readl_relaxed(host->ioaddr + CORE_DLL_STATUS) &
+ CORE_DLL_LOCK)) {
+ /* max. wait for 50us sec for LOCK bit to be set */
+ if (--wait_cnt == 0) {
+ dev_err(mmc_dev(mmc), "%s: DLL failed to LOCK\n",
+ mmc_hostname(mmc));
+ spin_unlock_irqrestore(&host->lock, flags);
+ return -ETIMEDOUT;
+ }
+ udelay(1);
+ }
+
+ spin_unlock_irqrestore(&host->lock, flags);
+ return 0;
+}
+
+static int sdhci_msm_execute_tuning(struct sdhci_host *host, u32 opcode)
+{
+ int tuning_seq_cnt = 3;
+ u8 phase, *data_buf, tuned_phases[16], tuned_phase_cnt = 0;
+ const u32 *tuning_block_pattern = tuning_block_64;
+ int size = sizeof(tuning_block_64); /* Pattern size in bytes */
+ int rc;
+ struct mmc_host *mmc = host->mmc;
+ struct mmc_ios ios = host->mmc->ios;
+
+ /*
+ * Tuning is required for SDR104, HS200 and HS400 cards and
+ * if clock frequency is greater than 100MHz in these modes.
+ */
+ if (host->clock <= 100 * 1000 * 1000 ||
+ !((ios.timing == MMC_TIMING_MMC_HS200) ||
+ (ios.timing == MMC_TIMING_UHS_SDR104)))
+ return 0;
+
+ if ((opcode == MMC_SEND_TUNING_BLOCK_HS200) &&
+ (mmc->ios.bus_width == MMC_BUS_WIDTH_8)) {
+ tuning_block_pattern = tuning_block_128;
+ size = sizeof(tuning_block_128);
+ }
+
+ data_buf = kmalloc(size, GFP_KERNEL);
+ if (!data_buf)
+ return -ENOMEM;
+
+retry:
+ /* First of all reset the tuning block */
+ rc = msm_init_cm_dll(host);
+ if (rc)
+ goto out;
+
+ phase = 0;
+ do {
+ struct mmc_command cmd = { 0 };
+ struct mmc_data data = { 0 };
+ struct mmc_request mrq = {
+ .cmd = &cmd,
+ .data = &data
+ };
+ struct scatterlist sg;
+
+ /* Set the phase in delay line hw block */
+ rc = msm_config_cm_dll_phase(host, phase);
+ if (rc)
+ goto out;
+
+ cmd.opcode = opcode;
+ cmd.flags = MMC_RSP_R1 | MMC_CMD_ADTC;
+
+ data.blksz = size;
+ data.blocks = 1;
+ data.flags = MMC_DATA_READ;
+ data.timeout_ns = NSEC_PER_SEC; /* 1 second */
+
+ data.sg = &sg;
+ data.sg_len = 1;
+ sg_init_one(&sg, data_buf, size);
+ memset(data_buf, 0, size);
+ mmc_wait_for_req(mmc, &mrq);
+
+ if (!cmd.error && !data.error &&
+ !memcmp(data_buf, tuning_block_pattern, size)) {
+ /* Tuning is successful at this tuning point */
+ tuned_phases[tuned_phase_cnt++] = phase;
+ dev_dbg(mmc_dev(mmc), "%s: Found good phase = %d\n",
+ mmc_hostname(mmc), phase);
+ }
+ } while (++phase < ARRAY_SIZE(tuned_phases));
+
+ if (tuned_phase_cnt) {
+ rc = msm_find_most_appropriate_phase(host, tuned_phases,
+ tuned_phase_cnt);
+ if (rc < 0)
+ goto out;
+ else
+ phase = rc;
+
+ /*
+ * Finally set the selected phase in delay
+ * line hw block.
+ */
+ rc = msm_config_cm_dll_phase(host, phase);
+ if (rc)
+ goto out;
+ dev_dbg(mmc_dev(mmc), "%s: Setting the tuning phase to %d\n",
+ mmc_hostname(mmc), phase);
+ } else {
+ if (--tuning_seq_cnt)
+ goto retry;
+ /* Tuning failed */
+ dev_dbg(mmc_dev(mmc), "%s: No tuning point found\n",
+ mmc_hostname(mmc));
+ rc = -EIO;
+ }
+
+out:
+ kfree(data_buf);
+ return rc;
+}
+
+static const struct of_device_id sdhci_msm_dt_match[] = {
+ { .compatible = "qcom,sdhci-msm-v4" },
+ {},
+};
+
+MODULE_DEVICE_TABLE(of, sdhci_msm_dt_match);
+
+static struct sdhci_ops sdhci_msm_ops = {
+ .platform_execute_tuning = sdhci_msm_execute_tuning,
+};
+
+static int sdhci_msm_probe(struct platform_device *pdev)
+{
+ struct sdhci_host *host;
+ struct sdhci_pltfm_host *pltfm_host;
+ struct sdhci_msm_host *msm_host;
+ struct resource *core_memres;
+ int ret;
+ u16 host_version;
+
+ msm_host = devm_kzalloc(&pdev->dev, sizeof(*msm_host), GFP_KERNEL);
+ if (!msm_host)
+ return -ENOMEM;
+
+ msm_host->sdhci_msm_pdata.ops = &sdhci_msm_ops;
+ host = sdhci_pltfm_init(pdev, &msm_host->sdhci_msm_pdata, 0);
+ if (IS_ERR(host))
+ return PTR_ERR(host);
+
+ pltfm_host = sdhci_priv(host);
+ pltfm_host->priv = msm_host;
+ msm_host->mmc = host->mmc;
+ msm_host->pdev = pdev;
+
+ ret = mmc_of_parse(host->mmc);
+ if (ret)
+ goto pltfm_free;
+
+ sdhci_get_of_property(pdev);
+
+ /* Setup SDCC bus voter clock. */
+ msm_host->bus_clk = devm_clk_get(&pdev->dev, "bus");
+ if (!IS_ERR(msm_host->bus_clk)) {
+ /* Vote for max. clk rate for max. performance */
+ ret = clk_set_rate(msm_host->bus_clk, INT_MAX);
+ if (ret)
+ goto pltfm_free;
+ ret = clk_prepare_enable(msm_host->bus_clk);
+ if (ret)
+ goto pltfm_free;
+ }
+
+ /* Setup main peripheral bus clock */
+ msm_host->pclk = devm_clk_get(&pdev->dev, "iface");
+ if (IS_ERR(msm_host->pclk)) {
+ ret = PTR_ERR(msm_host->pclk);
+ dev_err(&pdev->dev, "Perpheral clk setup failed (%d)\n", ret);
+ goto bus_clk_disable;
+ }
+
+ ret = clk_prepare_enable(msm_host->pclk);
+ if (ret)
+ goto bus_clk_disable;
+
+ /* Setup SDC MMC clock */
+ msm_host->clk = devm_clk_get(&pdev->dev, "core");
+ if (IS_ERR(msm_host->clk)) {
+ ret = PTR_ERR(msm_host->clk);
+ dev_err(&pdev->dev, "SDC MMC clk setup failed (%d)\n", ret);
+ goto pclk_disable;
+ }
+
+ ret = clk_prepare_enable(msm_host->clk);
+ if (ret)
+ goto pclk_disable;
+
+ core_memres = platform_get_resource(pdev, IORESOURCE_MEM, 1);
+ msm_host->core_mem = devm_ioremap_resource(&pdev->dev, core_memres);
+
+ if (IS_ERR(msm_host->core_mem)) {
+ dev_err(&pdev->dev, "Failed to remap registers\n");
+ ret = PTR_ERR(msm_host->core_mem);
+ goto clk_disable;
+ }
+
+ /* Reset the core and Enable SDHC mode */
+ writel_relaxed(readl_relaxed(msm_host->core_mem + CORE_POWER) |
+ CORE_SW_RST, msm_host->core_mem + CORE_POWER);
+
+ /* SW reset can take upto 10HCLK + 15MCLK cycles. (min 40us) */
+ usleep_range(1000, 5000);
+ if (readl(msm_host->core_mem + CORE_POWER) & CORE_SW_RST) {
+ dev_err(&pdev->dev, "Stuck in reset\n");
+ ret = -ETIMEDOUT;
+ goto clk_disable;
+ }
+
+ /* Set HC_MODE_EN bit in HC_MODE register */
+ writel_relaxed(HC_MODE_EN, (msm_host->core_mem + CORE_HC_MODE));
+
+ host->quirks |= SDHCI_QUIRK_BROKEN_CARD_DETECTION;
+ host->quirks |= SDHCI_QUIRK_SINGLE_POWER_WRITE;
+
+ host_version = readw_relaxed((host->ioaddr + SDHCI_HOST_VERSION));
+ dev_dbg(&pdev->dev, "Host Version: 0x%x Vendor Version 0x%x\n",
+ host_version, ((host_version & SDHCI_VENDOR_VER_MASK) >>
+ SDHCI_VENDOR_VER_SHIFT));
+
+ ret = sdhci_add_host(host);
+ if (ret)
+ goto clk_disable;
+
+ return 0;
+
+clk_disable:
+ clk_disable_unprepare(msm_host->clk);
+pclk_disable:
+ clk_disable_unprepare(msm_host->pclk);
+bus_clk_disable:
+ if (!IS_ERR(msm_host->bus_clk))
+ clk_disable_unprepare(msm_host->bus_clk);
+pltfm_free:
+ sdhci_pltfm_free(pdev);
+ return ret;
+}
+
+static int sdhci_msm_remove(struct platform_device *pdev)
+{
+ struct sdhci_host *host = platform_get_drvdata(pdev);
+ struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
+ struct sdhci_msm_host *msm_host = pltfm_host->priv;
+ int dead = (readl_relaxed(host->ioaddr + SDHCI_INT_STATUS) ==
+ 0xffffffff);
+
+ sdhci_remove_host(host, dead);
+ sdhci_pltfm_free(pdev);
+ clk_disable_unprepare(msm_host->clk);
+ clk_disable_unprepare(msm_host->pclk);
+ if (!IS_ERR(msm_host->bus_clk))
+ clk_disable_unprepare(msm_host->bus_clk);
+ return 0;
+}
+
+static struct platform_driver sdhci_msm_driver = {
+ .probe = sdhci_msm_probe,
+ .remove = sdhci_msm_remove,
+ .driver = {
+ .name = "sdhci_msm",
+ .owner = THIS_MODULE,
+ .of_match_table = sdhci_msm_dt_match,
+ },
+};
+
+module_platform_driver(sdhci_msm_driver);
+
+MODULE_DESCRIPTION("Qualcomm Secure Digital Host Controller Interface driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/mmc/host/sdhci-pci.c b/drivers/mmc/host/sdhci-pci.c
index 0955777b6c7e..fdc612120362 100644
--- a/drivers/mmc/host/sdhci-pci.c
+++ b/drivers/mmc/host/sdhci-pci.c
@@ -610,6 +610,18 @@ static const struct sdhci_pci_fixes sdhci_via = {
.probe = via_probe,
};
+static int rtsx_probe_slot(struct sdhci_pci_slot *slot)
+{
+ slot->host->mmc->caps2 |= MMC_CAP2_HS200;
+ return 0;
+}
+
+static const struct sdhci_pci_fixes sdhci_rtsx = {
+ .quirks2 = SDHCI_QUIRK2_PRESET_VALUE_BROKEN |
+ SDHCI_QUIRK2_BROKEN_DDR50,
+ .probe_slot = rtsx_probe_slot,
+};
+
static const struct pci_device_id pci_ids[] = {
{
.vendor = PCI_VENDOR_ID_RICOH,
@@ -732,6 +744,14 @@ static const struct pci_device_id pci_ids[] = {
},
{
+ .vendor = PCI_VENDOR_ID_REALTEK,
+ .device = 0x5250,
+ .subvendor = PCI_ANY_ID,
+ .subdevice = PCI_ANY_ID,
+ .driver_data = (kernel_ulong_t)&sdhci_rtsx,
+ },
+
+ {
.vendor = PCI_VENDOR_ID_INTEL,
.device = PCI_DEVICE_ID_INTEL_MRST_SD0,
.subvendor = PCI_ANY_ID,
diff --git a/drivers/mmc/host/sdhci-pxav3.c b/drivers/mmc/host/sdhci-pxav3.c
index 793dacd3b841..2fd73b38c303 100644
--- a/drivers/mmc/host/sdhci-pxav3.c
+++ b/drivers/mmc/host/sdhci-pxav3.c
@@ -34,6 +34,7 @@
#include <linux/of_gpio.h>
#include <linux/pm.h>
#include <linux/pm_runtime.h>
+#include <linux/mbus.h>
#include "sdhci.h"
#include "sdhci-pltfm.h"
@@ -57,6 +58,60 @@
#define SDCE_MISC_INT (1<<2)
#define SDCE_MISC_INT_EN (1<<1)
+/*
+ * These registers are relative to the second register region, for the
+ * MBus bridge.
+ */
+#define SDHCI_WINDOW_CTRL(i) (0x80 + ((i) << 3))
+#define SDHCI_WINDOW_BASE(i) (0x84 + ((i) << 3))
+#define SDHCI_MAX_WIN_NUM 8
+
+static int mv_conf_mbus_windows(struct platform_device *pdev,
+ const struct mbus_dram_target_info *dram)
+{
+ int i;
+ void __iomem *regs;
+ struct resource *res;
+
+ if (!dram) {
+ dev_err(&pdev->dev, "no mbus dram info\n");
+ return -EINVAL;
+ }
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
+ if (!res) {
+ dev_err(&pdev->dev, "cannot get mbus registers\n");
+ return -EINVAL;
+ }
+
+ regs = ioremap(res->start, resource_size(res));
+ if (!regs) {
+ dev_err(&pdev->dev, "cannot map mbus registers\n");
+ return -ENOMEM;
+ }
+
+ for (i = 0; i < SDHCI_MAX_WIN_NUM; i++) {
+ writel(0, regs + SDHCI_WINDOW_CTRL(i));
+ writel(0, regs + SDHCI_WINDOW_BASE(i));
+ }
+
+ for (i = 0; i < dram->num_cs; i++) {
+ const struct mbus_dram_window *cs = dram->cs + i;
+
+ /* Write size, attributes and target id to control register */
+ writel(((cs->size - 1) & 0xffff0000) |
+ (cs->mbus_attr << 8) |
+ (dram->mbus_dram_target_id << 4) | 1,
+ regs + SDHCI_WINDOW_CTRL(i));
+ /* Write base address to base register */
+ writel(cs->base, regs + SDHCI_WINDOW_BASE(i));
+ }
+
+ iounmap(regs);
+
+ return 0;
+}
+
static void pxav3_set_private_registers(struct sdhci_host *host, u8 mask)
{
struct platform_device *pdev = to_platform_device(mmc_dev(host->mmc));
@@ -187,6 +242,9 @@ static const struct of_device_id sdhci_pxav3_of_match[] = {
{
.compatible = "mrvl,pxav3-mmc",
},
+ {
+ .compatible = "marvell,armada-380-sdhci",
+ },
{},
};
MODULE_DEVICE_TABLE(of, sdhci_pxav3_of_match);
@@ -219,6 +277,7 @@ static int sdhci_pxav3_probe(struct platform_device *pdev)
struct sdhci_pltfm_host *pltfm_host;
struct sdhci_pxa_platdata *pdata = pdev->dev.platform_data;
struct device *dev = &pdev->dev;
+ struct device_node *np = pdev->dev.of_node;
struct sdhci_host *host = NULL;
struct sdhci_pxa *pxa = NULL;
const struct of_device_id *match;
@@ -235,6 +294,14 @@ static int sdhci_pxav3_probe(struct platform_device *pdev)
kfree(pxa);
return PTR_ERR(host);
}
+
+ if (of_device_is_compatible(np, "marvell,armada-380-sdhci")) {
+ ret = mv_conf_mbus_windows(pdev, mv_mbus_dram_info());
+ if (ret < 0)
+ goto err_mbus_win;
+ }
+
+
pltfm_host = sdhci_priv(host);
pltfm_host->priv = pxa;
@@ -321,6 +388,7 @@ err_add_host:
clk_disable_unprepare(clk);
clk_put(clk);
err_clk_get:
+err_mbus_win:
sdhci_pltfm_free(pdev);
kfree(pxa);
return ret;
diff --git a/drivers/mmc/host/sdhci-s3c.c b/drivers/mmc/host/sdhci-s3c.c
index 6debda952155..d61eb5a70833 100644
--- a/drivers/mmc/host/sdhci-s3c.c
+++ b/drivers/mmc/host/sdhci-s3c.c
@@ -51,12 +51,13 @@ struct sdhci_s3c {
struct platform_device *pdev;
struct resource *ioarea;
struct s3c_sdhci_platdata *pdata;
- unsigned int cur_clk;
+ int cur_clk;
int ext_cd_irq;
int ext_cd_gpio;
struct clk *clk_io;
struct clk *clk_bus[MAX_BUS_CLK];
+ unsigned long clk_rates[MAX_BUS_CLK];
};
/**
@@ -77,32 +78,6 @@ static inline struct sdhci_s3c *to_s3c(struct sdhci_host *host)
}
/**
- * get_curclk - convert ctrl2 register to clock source number
- * @ctrl2: Control2 register value.
- */
-static u32 get_curclk(u32 ctrl2)
-{
- ctrl2 &= S3C_SDHCI_CTRL2_SELBASECLK_MASK;
- ctrl2 >>= S3C_SDHCI_CTRL2_SELBASECLK_SHIFT;
-
- return ctrl2;
-}
-
-static void sdhci_s3c_check_sclk(struct sdhci_host *host)
-{
- struct sdhci_s3c *ourhost = to_s3c(host);
- u32 tmp = readl(host->ioaddr + S3C_SDHCI_CONTROL2);
-
- if (get_curclk(tmp) != ourhost->cur_clk) {
- dev_dbg(&ourhost->pdev->dev, "restored ctrl2 clock setting\n");
-
- tmp &= ~S3C_SDHCI_CTRL2_SELBASECLK_MASK;
- tmp |= ourhost->cur_clk << S3C_SDHCI_CTRL2_SELBASECLK_SHIFT;
- writel(tmp, host->ioaddr + S3C_SDHCI_CONTROL2);
- }
-}
-
-/**
* sdhci_s3c_get_max_clk - callback to get maximum clock frequency.
* @host: The SDHCI host instance.
*
@@ -111,20 +86,11 @@ static void sdhci_s3c_check_sclk(struct sdhci_host *host)
static unsigned int sdhci_s3c_get_max_clk(struct sdhci_host *host)
{
struct sdhci_s3c *ourhost = to_s3c(host);
- struct clk *busclk;
- unsigned int rate, max;
- int clk;
-
- /* note, a reset will reset the clock source */
-
- sdhci_s3c_check_sclk(host);
-
- for (max = 0, clk = 0; clk < MAX_BUS_CLK; clk++) {
- busclk = ourhost->clk_bus[clk];
- if (!busclk)
- continue;
+ unsigned long rate, max = 0;
+ int src;
- rate = clk_get_rate(busclk);
+ for (src = 0; src < MAX_BUS_CLK; src++) {
+ rate = ourhost->clk_rates[src];
if (rate > max)
max = rate;
}
@@ -144,9 +110,9 @@ static unsigned int sdhci_s3c_consider_clock(struct sdhci_s3c *ourhost,
{
unsigned long rate;
struct clk *clksrc = ourhost->clk_bus[src];
- int div;
+ int shift;
- if (!clksrc)
+ if (IS_ERR(clksrc))
return UINT_MAX;
/*
@@ -158,17 +124,24 @@ static unsigned int sdhci_s3c_consider_clock(struct sdhci_s3c *ourhost,
return wanted - rate;
}
- rate = clk_get_rate(clksrc);
+ rate = ourhost->clk_rates[src];
- for (div = 1; div < 256; div *= 2) {
- if ((rate / div) <= wanted)
+ for (shift = 0; shift <= 8; ++shift) {
+ if ((rate >> shift) <= wanted)
break;
}
+ if (shift > 8) {
+ dev_dbg(&ourhost->pdev->dev,
+ "clk %d: rate %ld, min rate %lu > wanted %u\n",
+ src, rate, rate / 256, wanted);
+ return UINT_MAX;
+ }
+
dev_dbg(&ourhost->pdev->dev, "clk %d: rate %ld, want %d, got %ld\n",
- src, rate, wanted, rate / div);
+ src, rate, wanted, rate >> shift);
- return wanted - (rate / div);
+ return wanted - (rate >> shift);
}
/**
@@ -209,20 +182,22 @@ static void sdhci_s3c_set_clock(struct sdhci_host *host, unsigned int clock)
struct clk *clk = ourhost->clk_bus[best_src];
clk_prepare_enable(clk);
- clk_disable_unprepare(ourhost->clk_bus[ourhost->cur_clk]);
-
- /* turn clock off to card before changing clock source */
- writew(0, host->ioaddr + SDHCI_CLOCK_CONTROL);
+ if (ourhost->cur_clk >= 0)
+ clk_disable_unprepare(
+ ourhost->clk_bus[ourhost->cur_clk]);
ourhost->cur_clk = best_src;
- host->max_clk = clk_get_rate(clk);
-
- ctrl = readl(host->ioaddr + S3C_SDHCI_CONTROL2);
- ctrl &= ~S3C_SDHCI_CTRL2_SELBASECLK_MASK;
- ctrl |= best_src << S3C_SDHCI_CTRL2_SELBASECLK_SHIFT;
- writel(ctrl, host->ioaddr + S3C_SDHCI_CONTROL2);
+ host->max_clk = ourhost->clk_rates[best_src];
}
+ /* turn clock off to card before changing clock source */
+ writew(0, host->ioaddr + SDHCI_CLOCK_CONTROL);
+
+ ctrl = readl(host->ioaddr + S3C_SDHCI_CONTROL2);
+ ctrl &= ~S3C_SDHCI_CTRL2_SELBASECLK_MASK;
+ ctrl |= best_src << S3C_SDHCI_CTRL2_SELBASECLK_SHIFT;
+ writel(ctrl, host->ioaddr + S3C_SDHCI_CONTROL2);
+
/* reprogram default hardware configuration */
writel(S3C64XX_SDHCI_CONTROL4_DRIVE_9mA,
host->ioaddr + S3C64XX_SDHCI_CONTROL4);
@@ -254,17 +229,17 @@ static void sdhci_s3c_set_clock(struct sdhci_host *host, unsigned int clock)
static unsigned int sdhci_s3c_get_min_clock(struct sdhci_host *host)
{
struct sdhci_s3c *ourhost = to_s3c(host);
- unsigned int delta, min = UINT_MAX;
+ unsigned long rate, min = ULONG_MAX;
int src;
for (src = 0; src < MAX_BUS_CLK; src++) {
- delta = sdhci_s3c_consider_clock(ourhost, src, 0);
- if (delta == UINT_MAX)
+ rate = ourhost->clk_rates[src] / 256;
+ if (!rate)
continue;
- /* delta is a negative value in this case */
- if (-delta < min)
- min = -delta;
+ if (rate < min)
+ min = rate;
}
+
return min;
}
@@ -272,20 +247,44 @@ static unsigned int sdhci_s3c_get_min_clock(struct sdhci_host *host)
static unsigned int sdhci_cmu_get_max_clock(struct sdhci_host *host)
{
struct sdhci_s3c *ourhost = to_s3c(host);
+ unsigned long rate, max = 0;
+ int src;
- return clk_round_rate(ourhost->clk_bus[ourhost->cur_clk], UINT_MAX);
+ for (src = 0; src < MAX_BUS_CLK; src++) {
+ struct clk *clk;
+
+ clk = ourhost->clk_bus[src];
+ if (IS_ERR(clk))
+ continue;
+
+ rate = clk_round_rate(clk, ULONG_MAX);
+ if (rate > max)
+ max = rate;
+ }
+
+ return max;
}
/* sdhci_cmu_get_min_clock - callback to get minimal supported clock value. */
static unsigned int sdhci_cmu_get_min_clock(struct sdhci_host *host)
{
struct sdhci_s3c *ourhost = to_s3c(host);
+ unsigned long rate, min = ULONG_MAX;
+ int src;
- /*
- * initial clock can be in the frequency range of
- * 100KHz-400KHz, so we set it as max value.
- */
- return clk_round_rate(ourhost->clk_bus[ourhost->cur_clk], 400000);
+ for (src = 0; src < MAX_BUS_CLK; src++) {
+ struct clk *clk;
+
+ clk = ourhost->clk_bus[src];
+ if (IS_ERR(clk))
+ continue;
+
+ rate = clk_round_rate(clk, 0);
+ if (rate < min)
+ min = rate;
+ }
+
+ return min;
}
/* sdhci_cmu_set_clock - callback on clock change.*/
@@ -552,6 +551,7 @@ static int sdhci_s3c_probe(struct platform_device *pdev)
sc->host = host;
sc->pdev = pdev;
sc->pdata = pdata;
+ sc->cur_clk = -1;
platform_set_drvdata(pdev, host);
@@ -566,25 +566,18 @@ static int sdhci_s3c_probe(struct platform_device *pdev)
clk_prepare_enable(sc->clk_io);
for (clks = 0, ptr = 0; ptr < MAX_BUS_CLK; ptr++) {
- struct clk *clk;
char name[14];
snprintf(name, 14, "mmc_busclk.%d", ptr);
- clk = devm_clk_get(dev, name);
- if (IS_ERR(clk))
+ sc->clk_bus[ptr] = devm_clk_get(dev, name);
+ if (IS_ERR(sc->clk_bus[ptr]))
continue;
clks++;
- sc->clk_bus[ptr] = clk;
-
- /*
- * save current clock index to know which clock bus
- * is used later in overriding functions.
- */
- sc->cur_clk = ptr;
+ sc->clk_rates[ptr] = clk_get_rate(sc->clk_bus[ptr]);
dev_info(dev, "clock source %d: %s (%ld Hz)\n",
- ptr, name, clk_get_rate(clk));
+ ptr, name, sc->clk_rates[ptr]);
}
if (clks == 0) {
@@ -593,10 +586,6 @@ static int sdhci_s3c_probe(struct platform_device *pdev)
goto err_no_busclks;
}
-#ifndef CONFIG_PM_RUNTIME
- clk_prepare_enable(sc->clk_bus[sc->cur_clk]);
-#endif
-
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
host->ioaddr = devm_ioremap_resource(&pdev->dev, res);
if (IS_ERR(host->ioaddr)) {
@@ -709,10 +698,6 @@ static int sdhci_s3c_probe(struct platform_device *pdev)
return 0;
err_req_regs:
-#ifndef CONFIG_PM_RUNTIME
- clk_disable_unprepare(sc->clk_bus[sc->cur_clk]);
-#endif
-
err_no_busclks:
clk_disable_unprepare(sc->clk_io);
@@ -743,9 +728,6 @@ static int sdhci_s3c_remove(struct platform_device *pdev)
pm_runtime_dont_use_autosuspend(&pdev->dev);
pm_runtime_disable(&pdev->dev);
-#ifndef CONFIG_PM_RUNTIME
- clk_disable_unprepare(sc->clk_bus[sc->cur_clk]);
-#endif
clk_disable_unprepare(sc->clk_io);
sdhci_free_host(host);
@@ -779,7 +761,8 @@ static int sdhci_s3c_runtime_suspend(struct device *dev)
ret = sdhci_runtime_suspend_host(host);
- clk_disable_unprepare(ourhost->clk_bus[ourhost->cur_clk]);
+ if (ourhost->cur_clk >= 0)
+ clk_disable_unprepare(ourhost->clk_bus[ourhost->cur_clk]);
clk_disable_unprepare(busclk);
return ret;
}
@@ -792,7 +775,8 @@ static int sdhci_s3c_runtime_resume(struct device *dev)
int ret;
clk_prepare_enable(busclk);
- clk_prepare_enable(ourhost->clk_bus[ourhost->cur_clk]);
+ if (ourhost->cur_clk >= 0)
+ clk_prepare_enable(ourhost->clk_bus[ourhost->cur_clk]);
ret = sdhci_runtime_resume_host(host);
return ret;
}
diff --git a/drivers/mmc/host/sdhci-spear.c b/drivers/mmc/host/sdhci-spear.c
index 2dba9f8d1760..0316dec3f006 100644
--- a/drivers/mmc/host/sdhci-spear.c
+++ b/drivers/mmc/host/sdhci-spear.c
@@ -27,6 +27,7 @@
#include <linux/slab.h>
#include <linux/mmc/host.h>
#include <linux/mmc/sdhci-spear.h>
+#include <linux/mmc/slot-gpio.h>
#include <linux/io.h>
#include "sdhci.h"
@@ -40,36 +41,6 @@ static const struct sdhci_ops sdhci_pltfm_ops = {
/* Nothing to do for now. */
};
-/* gpio card detection interrupt handler */
-static irqreturn_t sdhci_gpio_irq(int irq, void *dev_id)
-{
- struct platform_device *pdev = dev_id;
- struct sdhci_host *host = platform_get_drvdata(pdev);
- struct spear_sdhci *sdhci = dev_get_platdata(&pdev->dev);
- unsigned long gpio_irq_type;
- int val;
-
- val = gpio_get_value(sdhci->data->card_int_gpio);
-
- /* val == 1 -> card removed, val == 0 -> card inserted */
- /* if card removed - set irq for low level, else vice versa */
- gpio_irq_type = val ? IRQF_TRIGGER_LOW : IRQF_TRIGGER_HIGH;
- irq_set_irq_type(irq, gpio_irq_type);
-
- if (sdhci->data->card_power_gpio >= 0) {
- if (!sdhci->data->power_always_enb) {
- /* if card inserted, give power, otherwise remove it */
- val = sdhci->data->power_active_high ? !val : val ;
- gpio_set_value(sdhci->data->card_power_gpio, val);
- }
- }
-
- /* inform sdhci driver about card insertion/removal */
- tasklet_schedule(&host->card_tasklet);
-
- return IRQ_HANDLED;
-}
-
#ifdef CONFIG_OF
static struct sdhci_plat_data *sdhci_probe_config_dt(struct platform_device *pdev)
{
@@ -84,14 +55,12 @@ static struct sdhci_plat_data *sdhci_probe_config_dt(struct platform_device *pde
/* If pdata is required */
if (cd_gpio != -1) {
pdata = devm_kzalloc(&pdev->dev, sizeof(*pdata), GFP_KERNEL);
- if (!pdata) {
+ if (!pdata)
dev_err(&pdev->dev, "DT: kzalloc failed\n");
- return ERR_PTR(-ENOMEM);
- }
+ else
+ pdata->card_int_gpio = cd_gpio;
}
- pdata->card_int_gpio = cd_gpio;
-
return pdata;
}
#else
@@ -107,41 +76,44 @@ static int sdhci_probe(struct platform_device *pdev)
struct sdhci_host *host;
struct resource *iomem;
struct spear_sdhci *sdhci;
+ struct device *dev;
int ret;
- iomem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- if (!iomem) {
- ret = -ENOMEM;
- dev_dbg(&pdev->dev, "memory resource not defined\n");
+ dev = pdev->dev.parent ? pdev->dev.parent : &pdev->dev;
+ host = sdhci_alloc_host(dev, sizeof(*sdhci));
+ if (IS_ERR(host)) {
+ ret = PTR_ERR(host);
+ dev_dbg(&pdev->dev, "cannot allocate memory for sdhci\n");
goto err;
}
- if (!devm_request_mem_region(&pdev->dev, iomem->start,
- resource_size(iomem), "spear-sdhci")) {
- ret = -EBUSY;
- dev_dbg(&pdev->dev, "cannot request region\n");
- goto err;
+ iomem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ host->ioaddr = devm_ioremap_resource(&pdev->dev, iomem);
+ if (IS_ERR(host->ioaddr)) {
+ ret = PTR_ERR(host->ioaddr);
+ dev_dbg(&pdev->dev, "unable to map iomem: %d\n", ret);
+ goto err_host;
}
- sdhci = devm_kzalloc(&pdev->dev, sizeof(*sdhci), GFP_KERNEL);
- if (!sdhci) {
- ret = -ENOMEM;
- dev_dbg(&pdev->dev, "cannot allocate memory for sdhci\n");
- goto err;
- }
+ host->hw_name = "sdhci";
+ host->ops = &sdhci_pltfm_ops;
+ host->irq = platform_get_irq(pdev, 0);
+ host->quirks = SDHCI_QUIRK_BROKEN_ADMA;
+
+ sdhci = sdhci_priv(host);
/* clk enable */
- sdhci->clk = clk_get(&pdev->dev, NULL);
+ sdhci->clk = devm_clk_get(&pdev->dev, NULL);
if (IS_ERR(sdhci->clk)) {
ret = PTR_ERR(sdhci->clk);
dev_dbg(&pdev->dev, "Error getting clock\n");
- goto err;
+ goto err_host;
}
ret = clk_prepare_enable(sdhci->clk);
if (ret) {
dev_dbg(&pdev->dev, "Error enabling clock\n");
- goto put_clk;
+ goto err_host;
}
ret = clk_set_rate(sdhci->clk, 50000000);
@@ -153,118 +125,42 @@ static int sdhci_probe(struct platform_device *pdev)
sdhci->data = sdhci_probe_config_dt(pdev);
if (IS_ERR(sdhci->data)) {
dev_err(&pdev->dev, "DT: Failed to get pdata\n");
- return -ENODEV;
+ goto disable_clk;
}
} else {
sdhci->data = dev_get_platdata(&pdev->dev);
}
- pdev->dev.platform_data = sdhci;
-
- if (pdev->dev.parent)
- host = sdhci_alloc_host(pdev->dev.parent, 0);
- else
- host = sdhci_alloc_host(&pdev->dev, 0);
-
- if (IS_ERR(host)) {
- ret = PTR_ERR(host);
- dev_dbg(&pdev->dev, "error allocating host\n");
- goto disable_clk;
- }
-
- host->hw_name = "sdhci";
- host->ops = &sdhci_pltfm_ops;
- host->irq = platform_get_irq(pdev, 0);
- host->quirks = SDHCI_QUIRK_BROKEN_ADMA;
-
- host->ioaddr = devm_ioremap(&pdev->dev, iomem->start,
- resource_size(iomem));
- if (!host->ioaddr) {
- ret = -ENOMEM;
- dev_dbg(&pdev->dev, "failed to remap registers\n");
- goto free_host;
+ /*
+ * It is optional to use GPIOs for sdhci card detection. If
+ * sdhci->data is NULL, then use original sdhci lines otherwise
+ * GPIO lines. We use the built-in GPIO support for this.
+ */
+ if (sdhci->data && sdhci->data->card_int_gpio >= 0) {
+ ret = mmc_gpio_request_cd(host->mmc,
+ sdhci->data->card_int_gpio, 0);
+ if (ret < 0) {
+ dev_dbg(&pdev->dev,
+ "failed to request card-detect gpio%d\n",
+ sdhci->data->card_int_gpio);
+ goto disable_clk;
+ }
}
ret = sdhci_add_host(host);
if (ret) {
dev_dbg(&pdev->dev, "error adding host\n");
- goto free_host;
+ goto disable_clk;
}
platform_set_drvdata(pdev, host);
- /*
- * It is optional to use GPIOs for sdhci Power control & sdhci card
- * interrupt detection. If sdhci->data is NULL, then use original sdhci
- * lines otherwise GPIO lines.
- * If GPIO is selected for power control, then power should be disabled
- * after card removal and should be enabled when card insertion
- * interrupt occurs
- */
- if (!sdhci->data)
- return 0;
-
- if (sdhci->data->card_power_gpio >= 0) {
- int val = 0;
-
- ret = devm_gpio_request(&pdev->dev,
- sdhci->data->card_power_gpio, "sdhci");
- if (ret < 0) {
- dev_dbg(&pdev->dev, "gpio request fail: %d\n",
- sdhci->data->card_power_gpio);
- goto set_drvdata;
- }
-
- if (sdhci->data->power_always_enb)
- val = sdhci->data->power_active_high;
- else
- val = !sdhci->data->power_active_high;
-
- ret = gpio_direction_output(sdhci->data->card_power_gpio, val);
- if (ret) {
- dev_dbg(&pdev->dev, "gpio set direction fail: %d\n",
- sdhci->data->card_power_gpio);
- goto set_drvdata;
- }
- }
-
- if (sdhci->data->card_int_gpio >= 0) {
- ret = devm_gpio_request(&pdev->dev, sdhci->data->card_int_gpio,
- "sdhci");
- if (ret < 0) {
- dev_dbg(&pdev->dev, "gpio request fail: %d\n",
- sdhci->data->card_int_gpio);
- goto set_drvdata;
- }
-
- ret = gpio_direction_input(sdhci->data->card_int_gpio);
- if (ret) {
- dev_dbg(&pdev->dev, "gpio set direction fail: %d\n",
- sdhci->data->card_int_gpio);
- goto set_drvdata;
- }
- ret = devm_request_irq(&pdev->dev,
- gpio_to_irq(sdhci->data->card_int_gpio),
- sdhci_gpio_irq, IRQF_TRIGGER_LOW,
- mmc_hostname(host->mmc), pdev);
- if (ret) {
- dev_dbg(&pdev->dev, "gpio request irq fail: %d\n",
- sdhci->data->card_int_gpio);
- goto set_drvdata;
- }
-
- }
-
return 0;
-set_drvdata:
- sdhci_remove_host(host, 1);
-free_host:
- sdhci_free_host(host);
disable_clk:
clk_disable_unprepare(sdhci->clk);
-put_clk:
- clk_put(sdhci->clk);
+err_host:
+ sdhci_free_host(host);
err:
dev_err(&pdev->dev, "spear-sdhci probe failed: %d\n", ret);
return ret;
@@ -273,7 +169,7 @@ err:
static int sdhci_remove(struct platform_device *pdev)
{
struct sdhci_host *host = platform_get_drvdata(pdev);
- struct spear_sdhci *sdhci = dev_get_platdata(&pdev->dev);
+ struct spear_sdhci *sdhci = sdhci_priv(host);
int dead = 0;
u32 scratch;
@@ -282,9 +178,8 @@ static int sdhci_remove(struct platform_device *pdev)
dead = 1;
sdhci_remove_host(host, dead);
- sdhci_free_host(host);
clk_disable_unprepare(sdhci->clk);
- clk_put(sdhci->clk);
+ sdhci_free_host(host);
return 0;
}
@@ -293,7 +188,7 @@ static int sdhci_remove(struct platform_device *pdev)
static int sdhci_suspend(struct device *dev)
{
struct sdhci_host *host = dev_get_drvdata(dev);
- struct spear_sdhci *sdhci = dev_get_platdata(dev);
+ struct spear_sdhci *sdhci = sdhci_priv(host);
int ret;
ret = sdhci_suspend_host(host);
@@ -306,7 +201,7 @@ static int sdhci_suspend(struct device *dev)
static int sdhci_resume(struct device *dev)
{
struct sdhci_host *host = dev_get_drvdata(dev);
- struct spear_sdhci *sdhci = dev_get_platdata(dev);
+ struct spear_sdhci *sdhci = sdhci_priv(host);
int ret;
ret = clk_enable(sdhci->clk);
diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c
index 9ddef4763541..9a79fc4b60ca 100644
--- a/drivers/mmc/host/sdhci.c
+++ b/drivers/mmc/host/sdhci.c
@@ -675,12 +675,12 @@ static u8 sdhci_calc_timeout(struct sdhci_host *host, struct mmc_command *cmd)
return 0xE;
/* Unspecified timeout, assume max */
- if (!data && !cmd->cmd_timeout_ms)
+ if (!data && !cmd->busy_timeout)
return 0xE;
/* timeout in us */
if (!data)
- target_timeout = cmd->cmd_timeout_ms * 1000;
+ target_timeout = cmd->busy_timeout * 1000;
else {
target_timeout = data->timeout_ns / 1000;
if (host->clock)
@@ -1019,8 +1019,8 @@ void sdhci_send_command(struct sdhci_host *host, struct mmc_command *cmd)
}
timeout = jiffies;
- if (!cmd->data && cmd->cmd_timeout_ms > 9000)
- timeout += DIV_ROUND_UP(cmd->cmd_timeout_ms, 1000) * HZ + HZ;
+ if (!cmd->data && cmd->busy_timeout > 9000)
+ timeout += DIV_ROUND_UP(cmd->busy_timeout, 1000) * HZ + HZ;
else
timeout += 10 * HZ;
mod_timer(&host->timer, timeout);
@@ -2026,12 +2026,11 @@ out:
host->tuning_count * HZ);
/* Tuning mode 1 limits the maximum data length to 4MB */
mmc->max_blk_count = (4 * 1024 * 1024) / mmc->max_blk_size;
- } else {
+ } else if (host->flags & SDHCI_USING_RETUNING_TIMER) {
host->flags &= ~SDHCI_NEEDS_RETUNING;
/* Reload the new initial value for timer */
- if (host->tuning_mode == SDHCI_TUNING_MODE_1)
- mod_timer(&host->tuning_timer, jiffies +
- host->tuning_count * HZ);
+ mod_timer(&host->tuning_timer, jiffies +
+ host->tuning_count * HZ);
}
/*
@@ -2434,9 +2433,7 @@ static irqreturn_t sdhci_irq(int irq, void *dev_id)
if (host->runtime_suspended) {
spin_unlock(&host->lock);
- pr_warning("%s: got irq while runtime suspended\n",
- mmc_hostname(host->mmc));
- return IRQ_HANDLED;
+ return IRQ_NONE;
}
intmask = sdhci_readl(host, SDHCI_INT_STATUS);
@@ -2941,7 +2938,7 @@ int sdhci_add_host(struct sdhci_host *host)
if (host->quirks & SDHCI_QUIRK_DATA_TIMEOUT_USES_SDCLK)
host->timeout_clk = mmc->f_max / 1000;
- mmc->max_discard_to = (1 << 27) / host->timeout_clk;
+ mmc->max_busy_timeout = (1 << 27) / host->timeout_clk;
mmc->caps |= MMC_CAP_SDIO_IRQ | MMC_CAP_ERASE | MMC_CAP_CMD23;
@@ -3020,7 +3017,8 @@ int sdhci_add_host(struct sdhci_host *host)
} else if (caps[1] & SDHCI_SUPPORT_SDR50)
mmc->caps |= MMC_CAP_UHS_SDR50;
- if (caps[1] & SDHCI_SUPPORT_DDR50)
+ if ((caps[1] & SDHCI_SUPPORT_DDR50) &&
+ !(host->quirks2 & SDHCI_QUIRK2_BROKEN_DDR50))
mmc->caps |= MMC_CAP_UHS_DDR50;
/* Does the host need tuning for SDR50? */
diff --git a/drivers/mmc/host/sh_mobile_sdhi.c b/drivers/mmc/host/sh_mobile_sdhi.c
index 2d6ce257a273..91058dabd11a 100644
--- a/drivers/mmc/host/sh_mobile_sdhi.c
+++ b/drivers/mmc/host/sh_mobile_sdhi.c
@@ -37,6 +37,8 @@
struct sh_mobile_sdhi_of_data {
unsigned long tmio_flags;
+ unsigned long capabilities;
+ unsigned long capabilities2;
};
static const struct sh_mobile_sdhi_of_data sh_mobile_sdhi_of_cfg[] = {
@@ -45,6 +47,31 @@ static const struct sh_mobile_sdhi_of_data sh_mobile_sdhi_of_cfg[] = {
},
};
+static const struct sh_mobile_sdhi_of_data of_rcar_gen1_compatible = {
+ .tmio_flags = TMIO_MMC_HAS_IDLE_WAIT | TMIO_MMC_WRPROTECT_DISABLE,
+ .capabilities = MMC_CAP_SD_HIGHSPEED | MMC_CAP_SDIO_IRQ,
+};
+
+static const struct sh_mobile_sdhi_of_data of_rcar_gen2_compatible = {
+ .tmio_flags = TMIO_MMC_HAS_IDLE_WAIT | TMIO_MMC_WRPROTECT_DISABLE,
+ .capabilities = MMC_CAP_SD_HIGHSPEED | MMC_CAP_SDIO_IRQ,
+ .capabilities2 = MMC_CAP2_NO_MULTI_READ,
+};
+
+static const struct of_device_id sh_mobile_sdhi_of_match[] = {
+ { .compatible = "renesas,sdhi-shmobile" },
+ { .compatible = "renesas,sdhi-sh7372" },
+ { .compatible = "renesas,sdhi-sh73a0", .data = &sh_mobile_sdhi_of_cfg[0], },
+ { .compatible = "renesas,sdhi-r8a73a4", .data = &sh_mobile_sdhi_of_cfg[0], },
+ { .compatible = "renesas,sdhi-r8a7740", .data = &sh_mobile_sdhi_of_cfg[0], },
+ { .compatible = "renesas,sdhi-r8a7778", .data = &of_rcar_gen1_compatible, },
+ { .compatible = "renesas,sdhi-r8a7779", .data = &of_rcar_gen1_compatible, },
+ { .compatible = "renesas,sdhi-r8a7790", .data = &of_rcar_gen2_compatible, },
+ { .compatible = "renesas,sdhi-r8a7791", .data = &of_rcar_gen2_compatible, },
+ {},
+};
+MODULE_DEVICE_TABLE(of, sh_mobile_sdhi_of_match);
+
struct sh_mobile_sdhi {
struct clk *clk;
struct tmio_mmc_data mmc_data;
@@ -114,19 +141,6 @@ static const struct sh_mobile_sdhi_ops sdhi_ops = {
.cd_wakeup = sh_mobile_sdhi_cd_wakeup,
};
-static const struct of_device_id sh_mobile_sdhi_of_match[] = {
- { .compatible = "renesas,sdhi-shmobile" },
- { .compatible = "renesas,sdhi-sh7372" },
- { .compatible = "renesas,sdhi-sh73a0", .data = &sh_mobile_sdhi_of_cfg[0], },
- { .compatible = "renesas,sdhi-r8a73a4", .data = &sh_mobile_sdhi_of_cfg[0], },
- { .compatible = "renesas,sdhi-r8a7740", .data = &sh_mobile_sdhi_of_cfg[0], },
- { .compatible = "renesas,sdhi-r8a7778", .data = &sh_mobile_sdhi_of_cfg[0], },
- { .compatible = "renesas,sdhi-r8a7779", .data = &sh_mobile_sdhi_of_cfg[0], },
- { .compatible = "renesas,sdhi-r8a7790", .data = &sh_mobile_sdhi_of_cfg[0], },
- {},
-};
-MODULE_DEVICE_TABLE(of, sh_mobile_sdhi_of_match);
-
static int sh_mobile_sdhi_probe(struct platform_device *pdev)
{
const struct of_device_id *of_id =
@@ -212,6 +226,8 @@ static int sh_mobile_sdhi_probe(struct platform_device *pdev)
if (of_id && of_id->data) {
const struct sh_mobile_sdhi_of_data *of_data = of_id->data;
mmc_data->flags |= of_data->tmio_flags;
+ mmc_data->capabilities |= of_data->capabilities;
+ mmc_data->capabilities2 |= of_data->capabilities2;
}
/* SD control register space size is 0x100, 0x200 for bus_shift=1 */
@@ -316,10 +332,10 @@ static int sh_mobile_sdhi_remove(struct platform_device *pdev)
}
static const struct dev_pm_ops tmio_mmc_dev_pm_ops = {
- .suspend = tmio_mmc_host_suspend,
- .resume = tmio_mmc_host_resume,
- .runtime_suspend = tmio_mmc_host_runtime_suspend,
- .runtime_resume = tmio_mmc_host_runtime_resume,
+ SET_SYSTEM_SLEEP_PM_OPS(tmio_mmc_host_suspend, tmio_mmc_host_resume)
+ SET_RUNTIME_PM_OPS(tmio_mmc_host_runtime_suspend,
+ tmio_mmc_host_runtime_resume,
+ NULL)
};
static struct platform_driver sh_mobile_sdhi_driver = {
diff --git a/drivers/mmc/host/tmio_mmc.c b/drivers/mmc/host/tmio_mmc.c
index 1900abb04236..cfad844730d8 100644
--- a/drivers/mmc/host/tmio_mmc.c
+++ b/drivers/mmc/host/tmio_mmc.c
@@ -23,38 +23,37 @@
#include "tmio_mmc.h"
-#ifdef CONFIG_PM
-static int tmio_mmc_suspend(struct platform_device *dev, pm_message_t state)
+#ifdef CONFIG_PM_SLEEP
+static int tmio_mmc_suspend(struct device *dev)
{
- const struct mfd_cell *cell = mfd_get_cell(dev);
+ struct platform_device *pdev = to_platform_device(dev);
+ const struct mfd_cell *cell = mfd_get_cell(pdev);
int ret;
- ret = tmio_mmc_host_suspend(&dev->dev);
+ ret = tmio_mmc_host_suspend(dev);
/* Tell MFD core it can disable us now.*/
if (!ret && cell->disable)
- cell->disable(dev);
+ cell->disable(pdev);
return ret;
}
-static int tmio_mmc_resume(struct platform_device *dev)
+static int tmio_mmc_resume(struct device *dev)
{
- const struct mfd_cell *cell = mfd_get_cell(dev);
+ struct platform_device *pdev = to_platform_device(dev);
+ const struct mfd_cell *cell = mfd_get_cell(pdev);
int ret = 0;
/* Tell the MFD core we are ready to be enabled */
if (cell->resume)
- ret = cell->resume(dev);
+ ret = cell->resume(pdev);
if (!ret)
- ret = tmio_mmc_host_resume(&dev->dev);
+ ret = tmio_mmc_host_resume(dev);
return ret;
}
-#else
-#define tmio_mmc_suspend NULL
-#define tmio_mmc_resume NULL
#endif
static int tmio_mmc_probe(struct platform_device *pdev)
@@ -134,15 +133,18 @@ static int tmio_mmc_remove(struct platform_device *pdev)
/* ------------------- device registration ----------------------- */
+static const struct dev_pm_ops tmio_mmc_dev_pm_ops = {
+ SET_SYSTEM_SLEEP_PM_OPS(tmio_mmc_suspend, tmio_mmc_resume)
+};
+
static struct platform_driver tmio_mmc_driver = {
.driver = {
.name = "tmio-mmc",
.owner = THIS_MODULE,
+ .pm = &tmio_mmc_dev_pm_ops,
},
.probe = tmio_mmc_probe,
.remove = tmio_mmc_remove,
- .suspend = tmio_mmc_suspend,
- .resume = tmio_mmc_resume,
};
module_platform_driver(tmio_mmc_driver);
diff --git a/drivers/mmc/host/tmio_mmc.h b/drivers/mmc/host/tmio_mmc.h
index aaa9c7e9e730..100ffe0b2faf 100644
--- a/drivers/mmc/host/tmio_mmc.h
+++ b/drivers/mmc/host/tmio_mmc.h
@@ -162,16 +162,15 @@ static inline void tmio_mmc_abort_dma(struct tmio_mmc_host *host)
}
#endif
-#ifdef CONFIG_PM
+#ifdef CONFIG_PM_SLEEP
int tmio_mmc_host_suspend(struct device *dev);
int tmio_mmc_host_resume(struct device *dev);
-#else
-#define tmio_mmc_host_suspend NULL
-#define tmio_mmc_host_resume NULL
#endif
+#ifdef CONFIG_PM_RUNTIME
int tmio_mmc_host_runtime_suspend(struct device *dev);
int tmio_mmc_host_runtime_resume(struct device *dev);
+#endif
static inline u16 sd_ctrl_read16(struct tmio_mmc_host *host, int addr)
{
diff --git a/drivers/mmc/host/tmio_mmc_pio.c b/drivers/mmc/host/tmio_mmc_pio.c
index 8d8abf23a611..faf0924e71cb 100644
--- a/drivers/mmc/host/tmio_mmc_pio.c
+++ b/drivers/mmc/host/tmio_mmc_pio.c
@@ -1142,7 +1142,7 @@ void tmio_mmc_host_remove(struct tmio_mmc_host *host)
}
EXPORT_SYMBOL(tmio_mmc_host_remove);
-#ifdef CONFIG_PM
+#ifdef CONFIG_PM_SLEEP
int tmio_mmc_host_suspend(struct device *dev)
{
struct mmc_host *mmc = dev_get_drvdata(dev);
@@ -1165,9 +1165,9 @@ int tmio_mmc_host_resume(struct device *dev)
return 0;
}
EXPORT_SYMBOL(tmio_mmc_host_resume);
+#endif
-#endif /* CONFIG_PM */
-
+#ifdef CONFIG_PM_RUNTIME
int tmio_mmc_host_runtime_suspend(struct device *dev)
{
return 0;
@@ -1184,5 +1184,6 @@ int tmio_mmc_host_runtime_resume(struct device *dev)
return 0;
}
EXPORT_SYMBOL(tmio_mmc_host_runtime_resume);
+#endif
MODULE_LICENSE("GPL v2");
diff --git a/drivers/mmc/host/ushc.c b/drivers/mmc/host/ushc.c
index c0105a2e269a..d2c386f09d69 100644
--- a/drivers/mmc/host/ushc.c
+++ b/drivers/mmc/host/ushc.c
@@ -504,7 +504,7 @@ static int ushc_probe(struct usb_interface *intf, const struct usb_device_id *id
ret = -ENOMEM;
goto err;
}
- ushc->csw = kzalloc(sizeof(struct ushc_cbw), GFP_KERNEL);
+ ushc->csw = kzalloc(sizeof(struct ushc_csw), GFP_KERNEL);
if (ushc->csw == NULL) {
ret = -ENOMEM;
goto err;
diff --git a/drivers/mmc/host/wmt-sdmmc.c b/drivers/mmc/host/wmt-sdmmc.c
index e902ed7846b0..498d1f799085 100644
--- a/drivers/mmc/host/wmt-sdmmc.c
+++ b/drivers/mmc/host/wmt-sdmmc.c
@@ -757,7 +757,7 @@ static int wmt_mci_probe(struct platform_device *pdev)
struct device_node *np = pdev->dev.of_node;
const struct of_device_id *of_id =
of_match_device(wmt_mci_dt_ids, &pdev->dev);
- const struct wmt_mci_caps *wmt_caps = of_id->data;
+ const struct wmt_mci_caps *wmt_caps;
int ret;
int regular_irq, dma_irq;
@@ -766,6 +766,8 @@ static int wmt_mci_probe(struct platform_device *pdev)
return -EFAULT;
}
+ wmt_caps = of_id->data;
+
if (!np) {
dev_err(&pdev->dev, "Missing SDMMC description in devicetree\n");
return -EFAULT;
diff --git a/drivers/regulator/Kconfig b/drivers/regulator/Kconfig
index 1cd8584a7b88..903eb37f047a 100644
--- a/drivers/regulator/Kconfig
+++ b/drivers/regulator/Kconfig
@@ -392,6 +392,15 @@ config REGULATOR_PALMAS
on the muxing. This is handled automatically in the driver by
reading the mux info from OTP.
+config REGULATOR_PBIAS
+ tristate "PBIAS OMAP regulator driver"
+ depends on (ARCH_OMAP || COMPILE_TEST) && MFD_SYSCON
+ help
+ Say y here to support pbias regulator for mmc1:SD card i/o
+ on OMAP SoCs.
+ This driver provides support for OMAP pbias modelled
+ regulators.
+
config REGULATOR_PCAP
tristate "Motorola PCAP2 regulator driver"
depends on EZX_PCAP
diff --git a/drivers/regulator/Makefile b/drivers/regulator/Makefile
index f0fe0c50b59c..12ef277a48b4 100644
--- a/drivers/regulator/Makefile
+++ b/drivers/regulator/Makefile
@@ -55,6 +55,7 @@ obj-$(CONFIG_REGULATOR_MC13XXX_CORE) += mc13xxx-regulator-core.o
obj-$(CONFIG_REGULATOR_PALMAS) += palmas-regulator.o
obj-$(CONFIG_REGULATOR_PFUZE100) += pfuze100-regulator.o
obj-$(CONFIG_REGULATOR_TPS51632) += tps51632-regulator.o
+obj-$(CONFIG_REGULATOR_PBIAS) += pbias-regulator.o
obj-$(CONFIG_REGULATOR_PCAP) += pcap-regulator.o
obj-$(CONFIG_REGULATOR_PCF50633) += pcf50633-regulator.o
obj-$(CONFIG_REGULATOR_RC5T583) += rc5t583-regulator.o
diff --git a/drivers/regulator/pbias-regulator.c b/drivers/regulator/pbias-regulator.c
new file mode 100644
index 000000000000..ded3b3574209
--- /dev/null
+++ b/drivers/regulator/pbias-regulator.c
@@ -0,0 +1,255 @@
+/*
+ * pbias-regulator.c
+ *
+ * Copyright (C) 2014 Texas Instruments Incorporated - http://www.ti.com/
+ * Author: Balaji T K <balajitk@ti.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation version 2.
+ *
+ * This program is distributed "as is" WITHOUT ANY WARRANTY of any
+ * kind, whether express or implied; without even the implied warranty
+ * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/err.h>
+#include <linux/io.h>
+#include <linux/module.h>
+#include <linux/mfd/syscon.h>
+#include <linux/platform_device.h>
+#include <linux/regulator/driver.h>
+#include <linux/regulator/machine.h>
+#include <linux/regulator/of_regulator.h>
+#include <linux/regmap.h>
+#include <linux/slab.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+
+struct pbias_reg_info {
+ u32 enable;
+ u32 enable_mask;
+ u32 vmode;
+ unsigned int enable_time;
+ char *name;
+};
+
+struct pbias_regulator_data {
+ struct regulator_desc desc;
+ void __iomem *pbias_addr;
+ unsigned int pbias_reg;
+ struct regulator_dev *dev;
+ struct regmap *syscon;
+ const struct pbias_reg_info *info;
+ int voltage;
+};
+
+static int pbias_regulator_set_voltage(struct regulator_dev *dev,
+ int min_uV, int max_uV, unsigned *selector)
+{
+ struct pbias_regulator_data *data = rdev_get_drvdata(dev);
+ const struct pbias_reg_info *info = data->info;
+ int ret, vmode;
+
+ if (min_uV <= 1800000)
+ vmode = 0;
+ else if (min_uV > 1800000)
+ vmode = info->vmode;
+
+ ret = regmap_update_bits(data->syscon, data->pbias_reg,
+ info->vmode, vmode);
+
+ return ret;
+}
+
+static int pbias_regulator_get_voltage(struct regulator_dev *rdev)
+{
+ struct pbias_regulator_data *data = rdev_get_drvdata(rdev);
+ const struct pbias_reg_info *info = data->info;
+ int value, voltage;
+
+ regmap_read(data->syscon, data->pbias_reg, &value);
+ value &= info->vmode;
+
+ voltage = value ? 3000000 : 1800000;
+
+ return voltage;
+}
+
+static int pbias_regulator_enable(struct regulator_dev *rdev)
+{
+ struct pbias_regulator_data *data = rdev_get_drvdata(rdev);
+ const struct pbias_reg_info *info = data->info;
+ int ret;
+
+ ret = regmap_update_bits(data->syscon, data->pbias_reg,
+ info->enable_mask, info->enable);
+
+ return ret;
+}
+
+static int pbias_regulator_disable(struct regulator_dev *rdev)
+{
+ struct pbias_regulator_data *data = rdev_get_drvdata(rdev);
+ const struct pbias_reg_info *info = data->info;
+ int ret;
+
+ ret = regmap_update_bits(data->syscon, data->pbias_reg,
+ info->enable_mask, 0);
+ return ret;
+}
+
+static int pbias_regulator_is_enable(struct regulator_dev *rdev)
+{
+ struct pbias_regulator_data *data = rdev_get_drvdata(rdev);
+ const struct pbias_reg_info *info = data->info;
+ int value;
+
+ regmap_read(data->syscon, data->pbias_reg, &value);
+
+ return (value & info->enable_mask) == info->enable_mask;
+}
+
+static struct regulator_ops pbias_regulator_voltage_ops = {
+ .set_voltage = pbias_regulator_set_voltage,
+ .get_voltage = pbias_regulator_get_voltage,
+ .enable = pbias_regulator_enable,
+ .disable = pbias_regulator_disable,
+ .is_enabled = pbias_regulator_is_enable,
+};
+
+static const struct pbias_reg_info pbias_mmc_omap2430 = {
+ .enable = BIT(1),
+ .enable_mask = BIT(1),
+ .vmode = BIT(0),
+ .enable_time = 100,
+ .name = "pbias_mmc_omap2430"
+};
+
+static const struct pbias_reg_info pbias_sim_omap3 = {
+ .enable = BIT(9),
+ .enable_mask = BIT(9),
+ .vmode = BIT(8),
+ .enable_time = 100,
+ .name = "pbias_sim_omap3"
+};
+
+static const struct pbias_reg_info pbias_mmc_omap4 = {
+ .enable = BIT(26) | BIT(22),
+ .enable_mask = BIT(26) | BIT(25) | BIT(22),
+ .vmode = BIT(21),
+ .enable_time = 100,
+ .name = "pbias_mmc_omap4"
+};
+
+static const struct pbias_reg_info pbias_mmc_omap5 = {
+ .enable = BIT(27) | BIT(26),
+ .enable_mask = BIT(27) | BIT(25) | BIT(26),
+ .vmode = BIT(21),
+ .enable_time = 100,
+ .name = "pbias_mmc_omap5"
+};
+
+static struct of_regulator_match pbias_matches[] = {
+ { .name = "pbias_mmc_omap2430", .driver_data = (void *)&pbias_mmc_omap2430},
+ { .name = "pbias_sim_omap3", .driver_data = (void *)&pbias_sim_omap3},
+ { .name = "pbias_mmc_omap4", .driver_data = (void *)&pbias_mmc_omap4},
+ { .name = "pbias_mmc_omap5", .driver_data = (void *)&pbias_mmc_omap5},
+};
+#define PBIAS_NUM_REGS ARRAY_SIZE(pbias_matches)
+
+static const struct of_device_id pbias_of_match[] = {
+ { .compatible = "ti,pbias-omap", },
+ {},
+};
+MODULE_DEVICE_TABLE(of, pbias_of_match);
+
+static int pbias_regulator_probe(struct platform_device *pdev)
+{
+ struct device_node *np = pdev->dev.of_node;
+ struct pbias_regulator_data *drvdata;
+ struct resource *res;
+ struct regulator_config cfg = { };
+ struct regmap *syscon;
+ const struct pbias_reg_info *info;
+ int ret = 0;
+ int count, idx, data_idx = 0;
+
+ count = of_regulator_match(&pdev->dev, np, pbias_matches,
+ PBIAS_NUM_REGS);
+ if (count < 0)
+ return count;
+
+ drvdata = devm_kzalloc(&pdev->dev, sizeof(struct pbias_regulator_data)
+ * count, GFP_KERNEL);
+ if (drvdata == NULL) {
+ dev_err(&pdev->dev, "Failed to allocate device data\n");
+ return -ENOMEM;
+ }
+
+ syscon = syscon_regmap_lookup_by_phandle(np, "syscon");
+ if (IS_ERR(syscon))
+ return PTR_ERR(syscon);
+
+ cfg.dev = &pdev->dev;
+
+ for (idx = 0; idx < PBIAS_NUM_REGS && data_idx < count; idx++) {
+ if (!pbias_matches[idx].init_data ||
+ !pbias_matches[idx].of_node)
+ continue;
+
+ info = pbias_matches[idx].driver_data;
+ if (!info)
+ return -ENODEV;
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!res)
+ return -EINVAL;
+
+ drvdata[data_idx].pbias_reg = res->start;
+ drvdata[data_idx].syscon = syscon;
+ drvdata[data_idx].info = info;
+ drvdata[data_idx].desc.name = info->name;
+ drvdata[data_idx].desc.owner = THIS_MODULE;
+ drvdata[data_idx].desc.type = REGULATOR_VOLTAGE;
+ drvdata[data_idx].desc.ops = &pbias_regulator_voltage_ops;
+ drvdata[data_idx].desc.n_voltages = 2;
+ drvdata[data_idx].desc.enable_time = info->enable_time;
+
+ cfg.init_data = pbias_matches[idx].init_data;
+ cfg.driver_data = &drvdata[data_idx];
+ cfg.of_node = pbias_matches[idx].of_node;
+
+ drvdata[data_idx].dev = devm_regulator_register(&pdev->dev,
+ &drvdata[data_idx].desc, &cfg);
+ if (IS_ERR(drvdata[data_idx].dev)) {
+ ret = PTR_ERR(drvdata[data_idx].dev);
+ dev_err(&pdev->dev,
+ "Failed to register regulator: %d\n", ret);
+ goto err_regulator;
+ }
+ data_idx++;
+ }
+
+ platform_set_drvdata(pdev, drvdata);
+
+err_regulator:
+ return ret;
+}
+
+static struct platform_driver pbias_regulator_driver = {
+ .probe = pbias_regulator_probe,
+ .driver = {
+ .name = "pbias-regulator",
+ .owner = THIS_MODULE,
+ .of_match_table = of_match_ptr(pbias_of_match),
+ },
+};
+
+module_platform_driver(pbias_regulator_driver);
+
+MODULE_AUTHOR("Balaji T K <balajitk@ti.com>");
+MODULE_DESCRIPTION("pbias voltage regulator");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:pbias-regulator");