summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorStephen Rothwell <sfr@canb.auug.org.au>2016-11-22 15:01:27 +1100
committerStephen Rothwell <sfr@canb.auug.org.au>2016-11-22 15:01:27 +1100
commitcc435766769f86d8fcf95160057ae3760c54cde9 (patch)
tree7f8198863b84c5722a94640a37752b3809ed9059
parent43527bdfe1a71b370f833bafbdcc0226a384052c (diff)
parente2081b37d910c5e6c6925d9b4d0a7a6283f84ec5 (diff)
Merge remote-tracking branch 'mmc/next'
-rw-r--r--Documentation/devicetree/bindings/mmc/amlogic,meson-gx.txt32
-rw-r--r--Documentation/devicetree/bindings/mmc/brcm,sdhci-iproc.txt9
-rw-r--r--Documentation/devicetree/bindings/mmc/sdhci.txt13
-rw-r--r--Documentation/devicetree/bindings/mmc/tmio_mmc.txt1
-rw-r--r--Documentation/devicetree/bindings/soc/fsl/guts.txt (renamed from Documentation/devicetree/bindings/powerpc/fsl/guts.txt)3
-rw-r--r--MAINTAINERS12
-rw-r--r--arch/arm64/boot/dts/freescale/fsl-ls2080a.dtsi6
-rw-r--r--drivers/mmc/card/block.c101
-rw-r--r--drivers/mmc/card/mmc_test.c46
-rw-r--r--drivers/mmc/card/queue.c43
-rw-r--r--drivers/mmc/card/queue.h6
-rw-r--r--drivers/mmc/core/core.c44
-rw-r--r--drivers/mmc/core/mmc.c13
-rw-r--r--drivers/mmc/core/mmc_ops.c138
-rw-r--r--drivers/mmc/core/mmc_ops.h2
-rw-r--r--drivers/mmc/core/slot-gpio.c8
-rw-r--r--drivers/mmc/host/Kconfig11
-rw-r--r--drivers/mmc/host/Makefile1
-rw-r--r--drivers/mmc/host/davinci_mmc.c130
-rw-r--r--drivers/mmc/host/dw_mmc-exynos.c24
-rw-r--r--drivers/mmc/host/dw_mmc-k3.c39
-rw-r--r--drivers/mmc/host/dw_mmc-pci.c29
-rw-r--r--drivers/mmc/host/dw_mmc-pltfm.c28
-rw-r--r--drivers/mmc/host/dw_mmc-rockchip.c42
-rw-r--r--drivers/mmc/host/dw_mmc.c47
-rw-r--r--drivers/mmc/host/dw_mmc.h6
-rw-r--r--drivers/mmc/host/meson-gx-mmc.c851
-rw-r--r--drivers/mmc/host/mmci.c125
-rw-r--r--drivers/mmc/host/mmci.h71
-rw-r--r--drivers/mmc/host/mtk-sd.c1
-rw-r--r--drivers/mmc/host/rtsx_usb_sdmmc.c3
-rw-r--r--drivers/mmc/host/s3cmci.c15
-rw-r--r--drivers/mmc/host/sdhci-iproc.c35
-rw-r--r--drivers/mmc/host/sdhci-msm.c68
-rw-r--r--drivers/mmc/host/sdhci-of-at91.c1
-rw-r--r--drivers/mmc/host/sdhci-of-esdhc.c34
-rw-r--r--drivers/mmc/host/sdhci-pltfm.h2
-rw-r--r--drivers/mmc/host/sdhci.c43
-rw-r--r--drivers/mmc/host/sdhci.h3
-rw-r--r--drivers/mmc/host/sh_mobile_sdhi.c274
-rw-r--r--drivers/mmc/host/sunxi-mmc.c15
-rw-r--r--drivers/mmc/host/tmio_mmc.h32
-rw-r--r--drivers/mmc/host/tmio_mmc_pio.c119
-rw-r--r--drivers/mmc/host/wbsd.c11
-rw-r--r--drivers/soc/Kconfig3
-rw-r--r--drivers/soc/fsl/Kconfig18
-rw-r--r--drivers/soc/fsl/Makefile1
-rw-r--r--drivers/soc/fsl/guts.c239
-rw-r--r--include/linux/fsl/guts.h125
-rw-r--r--include/linux/mfd/tmio.h5
-rw-r--r--include/linux/mmc/card.h12
-rw-r--r--include/linux/mmc/core.h15
-rw-r--r--include/linux/mmc/host.h7
-rw-r--r--include/linux/mmc/slot-gpio.h1
-rw-r--r--include/uapi/linux/mmc/ioctl.h2
55 files changed, 2431 insertions, 534 deletions
diff --git a/Documentation/devicetree/bindings/mmc/amlogic,meson-gx.txt b/Documentation/devicetree/bindings/mmc/amlogic,meson-gx.txt
new file mode 100644
index 000000000000..7f95ec400863
--- /dev/null
+++ b/Documentation/devicetree/bindings/mmc/amlogic,meson-gx.txt
@@ -0,0 +1,32 @@
+Amlogic SD / eMMC controller for S905/GXBB family SoCs
+
+The MMC 5.1 compliant host controller on Amlogic provides the
+interface for SD, eMMC and SDIO devices.
+
+This file documents the properties in addition to those available in
+the MMC core bindings, documented by mmc.txt.
+
+Required properties:
+- compatible : contains one of:
+ - "amlogic,meson-gx-mmc"
+ - "amlogic,meson-gxbb-mmc"
+ - "amlogic,meson-gxl-mmc"
+ - "amlogic,meson-gxm-mmc"
+- clocks : A list of phandle + clock-specifier pairs for the clocks listed in clock-names.
+- clock-names: Should contain the following:
+ "core" - Main peripheral bus clock
+ "clkin0" - Parent clock of internal mux
+ "clkin1" - Other parent clock of internal mux
+ The driver has an interal mux clock which switches between clkin0 and clkin1 depending on the
+ clock rate requested by the MMC core.
+
+Example:
+
+ sd_emmc_a: mmc@70000 {
+ compatible = "amlogic,meson-gxbb-mmc";
+ reg = <0x0 0x70000 0x0 0x2000>;
+ interrupts = < GIC_SPI 216 IRQ_TYPE_EDGE_RISING>;
+ clocks = <&clkc CLKID_SD_EMMC_A>, <&xtal>, <&clkc CLKID_FCLK_DIV2>;
+ clock-names = "core", "clkin0", "clkin1";
+ pinctrl-0 = <&emmc_pins>;
+ };
diff --git a/Documentation/devicetree/bindings/mmc/brcm,sdhci-iproc.txt b/Documentation/devicetree/bindings/mmc/brcm,sdhci-iproc.txt
index be56d2bd474a..954561d09a8e 100644
--- a/Documentation/devicetree/bindings/mmc/brcm,sdhci-iproc.txt
+++ b/Documentation/devicetree/bindings/mmc/brcm,sdhci-iproc.txt
@@ -7,6 +7,15 @@ Required properties:
- compatible : Should be one of the following
"brcm,bcm2835-sdhci"
"brcm,sdhci-iproc-cygnus"
+ "brcm,sdhci-iproc"
+
+Use brcm2835-sdhci for Rasperry PI.
+
+Use sdhci-iproc-cygnus for Broadcom SDHCI Controllers
+restricted to 32bit host accesses to SDHCI registers.
+
+Use sdhci-iproc for Broadcom SDHCI Controllers that allow standard
+8, 16, 32-bit host access to SDHCI register.
- clocks : The clock feeding the SDHCI controller.
diff --git a/Documentation/devicetree/bindings/mmc/sdhci.txt b/Documentation/devicetree/bindings/mmc/sdhci.txt
new file mode 100644
index 000000000000..1c95a1a555c3
--- /dev/null
+++ b/Documentation/devicetree/bindings/mmc/sdhci.txt
@@ -0,0 +1,13 @@
+The properties specific for SD host controllers. For properties shared by MMC
+host controllers refer to the mmc[1] bindings.
+
+ [1] Documentation/devicetree/bindings/mmc/mmc.txt
+
+Optional properties:
+- sdhci-caps-mask: The sdhci capabilities register is incorrect. This 64bit
+ property corresponds to the bits in the sdhci capabilty register. If the bit
+ is on in the mask then the bit is incorrect in the register and should be
+ turned off, before applying sdhci-caps.
+- sdhci-caps: The sdhci capabilities register is incorrect. This 64bit
+ property corresponds to the bits in the sdhci capability register. If the
+ bit is on in the property then the bit should be turned on.
diff --git a/Documentation/devicetree/bindings/mmc/tmio_mmc.txt b/Documentation/devicetree/bindings/mmc/tmio_mmc.txt
index 13df9c2399c3..08b3a3009144 100644
--- a/Documentation/devicetree/bindings/mmc/tmio_mmc.txt
+++ b/Documentation/devicetree/bindings/mmc/tmio_mmc.txt
@@ -13,6 +13,7 @@ Required properties:
- compatible: "renesas,sdhi-shmobile" - a generic sh-mobile SDHI unit
"renesas,sdhi-sh7372" - SDHI IP on SH7372 SoC
"renesas,sdhi-sh73a0" - SDHI IP on SH73A0 SoC
+ "renesas,sdhi-r7s72100" - SDHI IP on R7S72100 SoC
"renesas,sdhi-r8a73a4" - SDHI IP on R8A73A4 SoC
"renesas,sdhi-r8a7740" - SDHI IP on R8A7740 SoC
"renesas,sdhi-r8a7778" - SDHI IP on R8A7778 SoC
diff --git a/Documentation/devicetree/bindings/powerpc/fsl/guts.txt b/Documentation/devicetree/bindings/soc/fsl/guts.txt
index b71b2039e112..07adca914d3d 100644
--- a/Documentation/devicetree/bindings/powerpc/fsl/guts.txt
+++ b/Documentation/devicetree/bindings/soc/fsl/guts.txt
@@ -25,6 +25,9 @@ Recommended properties:
- fsl,liodn-bits : Indicates the number of defined bits in the LIODN
registers, for those SOCs that have a PAMU device.
+ - little-endian : Indicates that the global utilities block is little
+ endian. The default is big endian.
+
Examples:
global-utilities@e0000 { /* global utilities block */
compatible = "fsl,mpc8548-guts";
diff --git a/MAINTAINERS b/MAINTAINERS
index d8bb3ce3875a..2b0d7578a83c 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -1037,6 +1037,7 @@ F: arch/arm/mach-meson/
F: arch/arm/boot/dts/meson*
F: arch/arm64/boot/dts/amlogic/
F: drivers/pinctrl/meson/
+F: drivers/mmc/host/meson*
N: meson
ARM/Annapurna Labs ALPINE ARCHITECTURE
@@ -5097,9 +5098,18 @@ S: Maintained
F: drivers/net/ethernet/freescale/fman
F: Documentation/devicetree/bindings/powerpc/fsl/fman.txt
+FREESCALE SOC DRIVERS
+M: Scott Wood <oss@buserror.net>
+L: linuxppc-dev@lists.ozlabs.org
+L: linux-arm-kernel@lists.infradead.org
+S: Maintained
+F: drivers/soc/fsl/
+F: include/linux/fsl/
+
FREESCALE QUICC ENGINE LIBRARY
+M: Qiang Zhao <qiang.zhao@nxp.com>
L: linuxppc-dev@lists.ozlabs.org
-S: Orphan
+S: Maintained
F: drivers/soc/fsl/qe/
F: include/soc/fsl/*qe*.h
F: include/soc/fsl/*ucc*.h
diff --git a/arch/arm64/boot/dts/freescale/fsl-ls2080a.dtsi b/arch/arm64/boot/dts/freescale/fsl-ls2080a.dtsi
index aa5a0fbf2cdf..e5935f28848c 100644
--- a/arch/arm64/boot/dts/freescale/fsl-ls2080a.dtsi
+++ b/arch/arm64/boot/dts/freescale/fsl-ls2080a.dtsi
@@ -222,6 +222,12 @@
clocks = <&sysclk>;
};
+ dcfg: dcfg@1e00000 {
+ compatible = "fsl,ls2080a-dcfg", "syscon";
+ reg = <0x0 0x1e00000 0x0 0x10000>;
+ little-endian;
+ };
+
tmu: tmu@1f80000 {
compatible = "fsl,qoriq-tmu";
reg = <0x0 0x1f80000 0x0 0x10000>;
diff --git a/drivers/mmc/card/block.c b/drivers/mmc/card/block.c
index ae9cbe6101be..40214887a910 100644
--- a/drivers/mmc/card/block.c
+++ b/drivers/mmc/card/block.c
@@ -854,7 +854,7 @@ static int get_card_status(struct mmc_card *card, u32 *status, int retries)
}
static int card_busy_detect(struct mmc_card *card, unsigned int timeout_ms,
- bool hw_busy_detect, struct request *req, int *gen_err)
+ bool hw_busy_detect, struct request *req, bool *gen_err)
{
unsigned long timeout = jiffies + msecs_to_jiffies(timeout_ms);
int err = 0;
@@ -871,7 +871,7 @@ static int card_busy_detect(struct mmc_card *card, unsigned int timeout_ms,
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;
+ *gen_err = true;
}
/* We may rely on the host hw to handle busy detection.*/
@@ -902,7 +902,7 @@ static int card_busy_detect(struct mmc_card *card, unsigned int timeout_ms,
}
static int send_stop(struct mmc_card *card, unsigned int timeout_ms,
- struct request *req, int *gen_err, u32 *stop_status)
+ struct request *req, bool *gen_err, u32 *stop_status)
{
struct mmc_host *host = card->host;
struct mmc_command cmd = {0};
@@ -940,7 +940,7 @@ static int send_stop(struct mmc_card *card, unsigned int timeout_ms,
(*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;
+ *gen_err = true;
}
return card_busy_detect(card, timeout_ms, use_r1b_resp, req, gen_err);
@@ -1014,7 +1014,7 @@ static int mmc_blk_cmd_error(struct request *req, const char *name, int error,
* Otherwise we don't understand what happened, so abort.
*/
static int mmc_blk_cmd_recovery(struct mmc_card *card, struct request *req,
- struct mmc_blk_request *brq, int *ecc_err, int *gen_err)
+ struct mmc_blk_request *brq, bool *ecc_err, bool *gen_err)
{
bool prev_cmd_status_valid = true;
u32 status, stop_status = 0;
@@ -1053,7 +1053,7 @@ static int mmc_blk_cmd_recovery(struct mmc_card *card, struct request *req,
if ((status & R1_CARD_ECC_FAILED) ||
(brq->stop.resp[0] & R1_CARD_ECC_FAILED) ||
(brq->cmd.resp[0] & R1_CARD_ECC_FAILED))
- *ecc_err = 1;
+ *ecc_err = true;
/* Flag General errors */
if (!mmc_host_is_spi(card->host) && rq_data_dir(req) != READ)
@@ -1062,7 +1062,7 @@ static int mmc_blk_cmd_recovery(struct mmc_card *card, struct request *req,
pr_err("%s: %s: general error sending stop or status command, stop cmd response %#x, card status %#x\n",
req->rq_disk->disk_name, __func__,
brq->stop.resp[0], status);
- *gen_err = 1;
+ *gen_err = true;
}
/*
@@ -1085,7 +1085,7 @@ static int mmc_blk_cmd_recovery(struct mmc_card *card, struct request *req,
}
if (stop_status & R1_CARD_ECC_FAILED)
- *ecc_err = 1;
+ *ecc_err = true;
}
/* Check for set block count errors */
@@ -1154,7 +1154,7 @@ static inline void mmc_blk_reset_success(struct mmc_blk_data *md, int type)
int mmc_access_rpmb(struct mmc_queue *mq)
{
- struct mmc_blk_data *md = mq->data;
+ struct mmc_blk_data *md = mq->blkdata;
/*
* If this is a RPMB partition access, return ture
*/
@@ -1166,7 +1166,7 @@ int mmc_access_rpmb(struct mmc_queue *mq)
static int mmc_blk_issue_discard_rq(struct mmc_queue *mq, struct request *req)
{
- struct mmc_blk_data *md = mq->data;
+ struct mmc_blk_data *md = mq->blkdata;
struct mmc_card *card = md->queue.card;
unsigned int from, nr, arg;
int err = 0, type = MMC_BLK_DISCARD;
@@ -1210,7 +1210,7 @@ out:
static int mmc_blk_issue_secdiscard_rq(struct mmc_queue *mq,
struct request *req)
{
- struct mmc_blk_data *md = mq->data;
+ struct mmc_blk_data *md = mq->blkdata;
struct mmc_card *card = md->queue.card;
unsigned int from, nr, arg;
int err = 0, type = MMC_BLK_SECDISCARD;
@@ -1276,7 +1276,7 @@ out:
static int mmc_blk_issue_flush(struct mmc_queue *mq, struct request *req)
{
- struct mmc_blk_data *md = mq->data;
+ struct mmc_blk_data *md = mq->blkdata;
struct mmc_card *card = md->queue.card;
int ret = 0;
@@ -1320,15 +1320,16 @@ static inline void mmc_apply_rel_rw(struct mmc_blk_request *brq,
R1_CC_ERROR | /* Card controller error */ \
R1_ERROR) /* General/unknown error */
-static int mmc_blk_err_check(struct mmc_card *card,
- struct mmc_async_req *areq)
+static enum mmc_blk_status mmc_blk_err_check(struct mmc_card *card,
+ struct mmc_async_req *areq)
{
struct mmc_queue_req *mq_mrq = container_of(areq, struct mmc_queue_req,
mmc_active);
struct mmc_blk_request *brq = &mq_mrq->brq;
struct request *req = mq_mrq->req;
int need_retune = card->host->need_retune;
- int ecc_err = 0, gen_err = 0;
+ bool ecc_err = false;
+ bool gen_err = false;
/*
* sbc.error indicates a problem with the set block count
@@ -1378,7 +1379,7 @@ static int mmc_blk_err_check(struct mmc_card *card,
pr_err("%s: %s: general error sending stop command, stop cmd response %#x\n",
req->rq_disk->disk_name, __func__,
brq->stop.resp[0]);
- gen_err = 1;
+ gen_err = true;
}
err = card_busy_detect(card, MMC_BLK_TIMEOUT_MS, false, req,
@@ -1432,14 +1433,58 @@ static int mmc_blk_err_check(struct mmc_card *card,
return MMC_BLK_SUCCESS;
}
-static int mmc_blk_packed_err_check(struct mmc_card *card,
- struct mmc_async_req *areq)
+static int mmc_packed_init(struct mmc_queue *mq, struct mmc_card *card)
+{
+ struct mmc_queue_req *mqrq_cur = &mq->mqrq[0];
+ struct mmc_queue_req *mqrq_prev = &mq->mqrq[1];
+ int ret = 0;
+
+
+ mqrq_cur->packed = kzalloc(sizeof(struct mmc_packed), GFP_KERNEL);
+ if (!mqrq_cur->packed) {
+ pr_warn("%s: unable to allocate packed cmd for mqrq_cur\n",
+ mmc_card_name(card));
+ ret = -ENOMEM;
+ goto out;
+ }
+
+ mqrq_prev->packed = kzalloc(sizeof(struct mmc_packed), GFP_KERNEL);
+ if (!mqrq_prev->packed) {
+ pr_warn("%s: unable to allocate packed cmd for mqrq_prev\n",
+ mmc_card_name(card));
+ kfree(mqrq_cur->packed);
+ mqrq_cur->packed = NULL;
+ ret = -ENOMEM;
+ goto out;
+ }
+
+ INIT_LIST_HEAD(&mqrq_cur->packed->list);
+ INIT_LIST_HEAD(&mqrq_prev->packed->list);
+
+out:
+ return ret;
+}
+
+static void mmc_packed_clean(struct mmc_queue *mq)
+{
+ struct mmc_queue_req *mqrq_cur = &mq->mqrq[0];
+ struct mmc_queue_req *mqrq_prev = &mq->mqrq[1];
+
+ kfree(mqrq_cur->packed);
+ mqrq_cur->packed = NULL;
+ kfree(mqrq_prev->packed);
+ mqrq_prev->packed = NULL;
+}
+
+static enum mmc_blk_status mmc_blk_packed_err_check(struct mmc_card *card,
+ struct mmc_async_req *areq)
{
struct mmc_queue_req *mq_rq = container_of(areq, struct mmc_queue_req,
mmc_active);
struct request *req = mq_rq->req;
struct mmc_packed *packed = mq_rq->packed;
- int err, check, status;
+ enum mmc_blk_status status, check;
+ int err;
u8 *ext_csd;
packed->retries--;
@@ -1488,7 +1533,7 @@ static void mmc_blk_rw_rq_prep(struct mmc_queue_req *mqrq,
u32 readcmd, writecmd;
struct mmc_blk_request *brq = &mqrq->brq;
struct request *req = mqrq->req;
- struct mmc_blk_data *md = mq->data;
+ struct mmc_blk_data *md = mq->blkdata;
bool do_data_tag;
/*
@@ -1661,7 +1706,7 @@ static u8 mmc_blk_prep_packed_list(struct mmc_queue *mq, struct request *req)
struct request_queue *q = mq->queue;
struct mmc_card *card = mq->card;
struct request *cur = req, *next = NULL;
- struct mmc_blk_data *md = mq->data;
+ struct mmc_blk_data *md = mq->blkdata;
struct mmc_queue_req *mqrq = mq->mqrq_cur;
bool en_rel_wr = card->ext_csd.rel_param & EXT_CSD_WR_REL_PARAM_EN;
unsigned int req_sectors = 0, phys_segments = 0;
@@ -1734,9 +1779,7 @@ static u8 mmc_blk_prep_packed_list(struct mmc_queue *mq, struct request *req)
!IS_ALIGNED(blk_rq_sectors(next), 8))
break;
- if (req_op(next) == REQ_OP_DISCARD ||
- req_op(next) == REQ_OP_SECURE_ERASE ||
- req_op(next) == REQ_OP_FLUSH)
+ if (mmc_req_is_special(next))
break;
if (rq_data_dir(cur) != rq_data_dir(next))
@@ -1784,7 +1827,7 @@ static void mmc_blk_packed_hdr_wrq_prep(struct mmc_queue_req *mqrq,
struct mmc_blk_request *brq = &mqrq->brq;
struct request *req = mqrq->req;
struct request *prq;
- struct mmc_blk_data *md = mq->data;
+ struct mmc_blk_data *md = mq->blkdata;
struct mmc_packed *packed = mqrq->packed;
bool do_rel_wr, do_data_tag;
__le32 *packed_cmd_hdr;
@@ -1955,7 +1998,7 @@ static void mmc_blk_revert_packed_req(struct mmc_queue *mq,
static int mmc_blk_issue_rw_rq(struct mmc_queue *mq, struct request *rqc)
{
- struct mmc_blk_data *md = mq->data;
+ struct mmc_blk_data *md = mq->blkdata;
struct mmc_card *card = md->queue.card;
struct mmc_blk_request *brq = &mq->mqrq_cur->brq;
int ret = 1, disable_multi = 0, retry = 0, type, retune_retry_done = 0;
@@ -1994,7 +2037,7 @@ static int mmc_blk_issue_rw_rq(struct mmc_queue *mq, struct request *rqc)
areq = &mq->mqrq_cur->mmc_active;
} else
areq = NULL;
- areq = mmc_start_req(card->host, areq, (int *) &status);
+ areq = mmc_start_req(card->host, areq, &status);
if (!areq) {
if (status == MMC_BLK_NEW_REQUEST)
mq->flags |= MMC_QUEUE_NEW_REQUEST;
@@ -2148,7 +2191,7 @@ static int mmc_blk_issue_rw_rq(struct mmc_queue *mq, struct request *rqc)
int mmc_blk_issue_rq(struct mmc_queue *mq, struct request *req)
{
int ret;
- struct mmc_blk_data *md = mq->data;
+ struct mmc_blk_data *md = mq->blkdata;
struct mmc_card *card = md->queue.card;
struct mmc_host *host = card->host;
unsigned long flags;
@@ -2266,7 +2309,7 @@ again:
if (ret)
goto err_putdisk;
- md->queue.data = md;
+ md->queue.blkdata = md;
md->disk->major = MMC_BLOCK_MAJOR;
md->disk->first_minor = devidx * perdev_minors;
diff --git a/drivers/mmc/card/mmc_test.c b/drivers/mmc/card/mmc_test.c
index 3678220964fe..5ba6d77b9723 100644
--- a/drivers/mmc/card/mmc_test.c
+++ b/drivers/mmc/card/mmc_test.c
@@ -736,15 +736,28 @@ static int mmc_test_check_result(struct mmc_test_card *test,
return ret;
}
-static int mmc_test_check_result_async(struct mmc_card *card,
+static enum mmc_blk_status mmc_test_check_result_async(struct mmc_card *card,
struct mmc_async_req *areq)
{
struct mmc_test_async_req *test_async =
container_of(areq, struct mmc_test_async_req, areq);
+ int ret;
mmc_test_wait_busy(test_async->test);
- return mmc_test_check_result(test_async->test, areq->mrq);
+ /*
+ * FIXME: this would earlier just casts a regular error code,
+ * either of the kernel type -ERRORCODE or the local test framework
+ * RESULT_* errorcode, into an enum mmc_blk_status and return as
+ * result check. Instead, convert it to some reasonable type by just
+ * returning either MMC_BLK_SUCCESS or MMC_BLK_CMD_ERR.
+ * If possible, a reasonable error code should be returned.
+ */
+ ret = mmc_test_check_result(test_async->test, areq->mrq);
+ if (ret)
+ return MMC_BLK_CMD_ERR;
+
+ return MMC_BLK_SUCCESS;
}
/*
@@ -817,8 +830,9 @@ static int mmc_test_nonblock_transfer(struct mmc_test_card *test,
struct mmc_async_req *done_areq;
struct mmc_async_req *cur_areq = &test_areq[0].areq;
struct mmc_async_req *other_areq = &test_areq[1].areq;
+ enum mmc_blk_status status;
int i;
- int ret;
+ int ret = RESULT_OK;
test_areq[0].test = test;
test_areq[1].test = test;
@@ -834,10 +848,12 @@ static int mmc_test_nonblock_transfer(struct mmc_test_card *test,
for (i = 0; i < count; i++) {
mmc_test_prepare_mrq(test, cur_areq->mrq, sg, sg_len, dev_addr,
blocks, blksz, write);
- done_areq = mmc_start_req(test->card->host, cur_areq, &ret);
+ done_areq = mmc_start_req(test->card->host, cur_areq, &status);
- if (ret || (!done_areq && i > 0))
+ if (status != MMC_BLK_SUCCESS || (!done_areq && i > 0)) {
+ ret = RESULT_FAIL;
goto err;
+ }
if (done_areq) {
if (done_areq->mrq == &mrq2)
@@ -851,7 +867,9 @@ static int mmc_test_nonblock_transfer(struct mmc_test_card *test,
dev_addr += blocks;
}
- done_areq = mmc_start_req(test->card->host, NULL, &ret);
+ done_areq = mmc_start_req(test->card->host, NULL, &status);
+ if (status != MMC_BLK_SUCCESS)
+ ret = RESULT_FAIL;
return ret;
err:
@@ -2351,6 +2369,7 @@ static int mmc_test_ongoing_transfer(struct mmc_test_card *test,
struct mmc_request *mrq;
unsigned long timeout;
bool expired = false;
+ enum mmc_blk_status blkstat = MMC_BLK_SUCCESS;
int ret = 0, cmd_ret;
u32 status = 0;
int count = 0;
@@ -2378,9 +2397,11 @@ static int mmc_test_ongoing_transfer(struct mmc_test_card *test,
/* Start ongoing data request */
if (use_areq) {
- mmc_start_req(host, &test_areq.areq, &ret);
- if (ret)
+ mmc_start_req(host, &test_areq.areq, &blkstat);
+ if (blkstat != MMC_BLK_SUCCESS) {
+ ret = RESULT_FAIL;
goto out_free;
+ }
} else {
mmc_wait_for_req(host, mrq);
}
@@ -2413,10 +2434,13 @@ static int mmc_test_ongoing_transfer(struct mmc_test_card *test,
} while (repeat_cmd && R1_CURRENT_STATE(status) != R1_STATE_TRAN);
/* Wait for data request to complete */
- if (use_areq)
- mmc_start_req(host, NULL, &ret);
- else
+ if (use_areq) {
+ mmc_start_req(host, NULL, &blkstat);
+ if (blkstat != MMC_BLK_SUCCESS)
+ ret = RESULT_FAIL;
+ } else {
mmc_wait_for_req_done(test->card->host, mrq);
+ }
/*
* For cap_cmd_during_tfr request, upper layer must send stop if
diff --git a/drivers/mmc/card/queue.c b/drivers/mmc/card/queue.c
index 8a67f1c2ce21..48d8485b067c 100644
--- a/drivers/mmc/card/queue.c
+++ b/drivers/mmc/card/queue.c
@@ -362,49 +362,6 @@ void mmc_cleanup_queue(struct mmc_queue *mq)
}
EXPORT_SYMBOL(mmc_cleanup_queue);
-int mmc_packed_init(struct mmc_queue *mq, struct mmc_card *card)
-{
- struct mmc_queue_req *mqrq_cur = &mq->mqrq[0];
- struct mmc_queue_req *mqrq_prev = &mq->mqrq[1];
- int ret = 0;
-
-
- mqrq_cur->packed = kzalloc(sizeof(struct mmc_packed), GFP_KERNEL);
- if (!mqrq_cur->packed) {
- pr_warn("%s: unable to allocate packed cmd for mqrq_cur\n",
- mmc_card_name(card));
- ret = -ENOMEM;
- goto out;
- }
-
- mqrq_prev->packed = kzalloc(sizeof(struct mmc_packed), GFP_KERNEL);
- if (!mqrq_prev->packed) {
- pr_warn("%s: unable to allocate packed cmd for mqrq_prev\n",
- mmc_card_name(card));
- kfree(mqrq_cur->packed);
- mqrq_cur->packed = NULL;
- ret = -ENOMEM;
- goto out;
- }
-
- INIT_LIST_HEAD(&mqrq_cur->packed->list);
- INIT_LIST_HEAD(&mqrq_prev->packed->list);
-
-out:
- return ret;
-}
-
-void mmc_packed_clean(struct mmc_queue *mq)
-{
- struct mmc_queue_req *mqrq_cur = &mq->mqrq[0];
- struct mmc_queue_req *mqrq_prev = &mq->mqrq[1];
-
- kfree(mqrq_cur->packed);
- mqrq_cur->packed = NULL;
- kfree(mqrq_prev->packed);
- mqrq_prev->packed = NULL;
-}
-
/**
* mmc_queue_suspend - suspend a MMC request queue
* @mq: MMC queue to suspend
diff --git a/drivers/mmc/card/queue.h b/drivers/mmc/card/queue.h
index 342f1e3f301e..334c9306070f 100644
--- a/drivers/mmc/card/queue.h
+++ b/drivers/mmc/card/queue.h
@@ -11,6 +11,7 @@ static inline bool mmc_req_is_special(struct request *req)
struct request;
struct task_struct;
+struct mmc_blk_data;
struct mmc_blk_request {
struct mmc_request mrq;
@@ -57,7 +58,7 @@ struct mmc_queue {
unsigned int flags;
#define MMC_QUEUE_SUSPENDED (1 << 0)
#define MMC_QUEUE_NEW_REQUEST (1 << 1)
- void *data;
+ struct mmc_blk_data *blkdata;
struct request_queue *queue;
struct mmc_queue_req mqrq[2];
struct mmc_queue_req *mqrq_cur;
@@ -75,9 +76,6 @@ extern unsigned int mmc_queue_map_sg(struct mmc_queue *,
extern void mmc_queue_bounce_pre(struct mmc_queue_req *);
extern void mmc_queue_bounce_post(struct mmc_queue_req *);
-extern int mmc_packed_init(struct mmc_queue *, struct mmc_card *);
-extern void mmc_packed_clean(struct mmc_queue *);
-
extern int mmc_access_rpmb(struct mmc_queue *);
#endif
diff --git a/drivers/mmc/core/core.c b/drivers/mmc/core/core.c
index 2553d903a82b..50bb9a16380d 100644
--- a/drivers/mmc/core/core.c
+++ b/drivers/mmc/core/core.c
@@ -497,13 +497,13 @@ static int __mmc_start_req(struct mmc_host *host, struct mmc_request *mrq)
*
* Returns enum mmc_blk_status after checking errors.
*/
-static int mmc_wait_for_data_req_done(struct mmc_host *host,
+static enum mmc_blk_status mmc_wait_for_data_req_done(struct mmc_host *host,
struct mmc_request *mrq,
struct mmc_async_req *next_req)
{
struct mmc_command *cmd;
struct mmc_context_info *context_info = &host->context_info;
- int err;
+ enum mmc_blk_status status;
unsigned long flags;
while (1) {
@@ -520,9 +520,9 @@ static int mmc_wait_for_data_req_done(struct mmc_host *host,
if (!cmd->error || !cmd->retries ||
mmc_card_removed(host->card)) {
- err = host->areq->err_check(host->card,
- host->areq);
- break; /* return err */
+ status = host->areq->err_check(host->card,
+ host->areq);
+ break; /* return status */
} else {
mmc_retune_recheck(host);
pr_info("%s: req failed (CMD%u): %d, retrying...\n",
@@ -540,7 +540,7 @@ static int mmc_wait_for_data_req_done(struct mmc_host *host,
}
}
mmc_retune_release(host);
- return err;
+ return status;
}
void mmc_wait_for_req_done(struct mmc_host *host, struct mmc_request *mrq)
@@ -658,9 +658,10 @@ static void mmc_post_req(struct mmc_host *host, struct mmc_request *mrq,
* is returned without waiting. NULL is not an error condition.
*/
struct mmc_async_req *mmc_start_req(struct mmc_host *host,
- struct mmc_async_req *areq, int *error)
+ struct mmc_async_req *areq,
+ enum mmc_blk_status *ret_stat)
{
- int err = 0;
+ enum mmc_blk_status status = MMC_BLK_SUCCESS;
int start_err = 0;
struct mmc_async_req *data = host->areq;
@@ -669,10 +670,10 @@ struct mmc_async_req *mmc_start_req(struct mmc_host *host,
mmc_pre_req(host, areq->mrq, !host->areq);
if (host->areq) {
- err = mmc_wait_for_data_req_done(host, host->areq->mrq, areq);
- if (err == MMC_BLK_NEW_REQUEST) {
- if (error)
- *error = err;
+ status = mmc_wait_for_data_req_done(host, host->areq->mrq, areq);
+ if (status == MMC_BLK_NEW_REQUEST) {
+ if (ret_stat)
+ *ret_stat = status;
/*
* The previous request was not completed,
* nothing to return
@@ -699,23 +700,23 @@ struct mmc_async_req *mmc_start_req(struct mmc_host *host,
}
}
- if (!err && areq)
+ if (status == MMC_BLK_SUCCESS && areq)
start_err = __mmc_start_data_req(host, areq->mrq);
if (host->areq)
mmc_post_req(host, host->areq->mrq, 0);
/* Cancel a prepared request if it was not started. */
- if ((err || start_err) && areq)
+ if ((status != MMC_BLK_SUCCESS || start_err) && areq)
mmc_post_req(host, areq->mrq, -EINVAL);
- if (err)
+ if (status != MMC_BLK_SUCCESS)
host->areq = NULL;
else
host->areq = areq;
- if (error)
- *error = err;
+ if (ret_stat)
+ *ret_stat = status;
return data;
}
EXPORT_SYMBOL(mmc_start_req);
@@ -2824,12 +2825,11 @@ void mmc_start_host(struct mmc_host *host)
host->rescan_disable = 0;
host->ios.power_mode = MMC_POWER_UNDEFINED;
- mmc_claim_host(host);
- if (host->caps2 & MMC_CAP2_NO_PRESCAN_POWERUP)
- mmc_power_off(host);
- else
+ if (!(host->caps2 & MMC_CAP2_NO_PRESCAN_POWERUP)) {
+ mmc_claim_host(host);
mmc_power_up(host, host->ocr_avail);
- mmc_release_host(host);
+ mmc_release_host(host);
+ }
mmc_gpiod_request_cd_irq(host);
_mmc_detect_change(host, 0, false);
diff --git a/drivers/mmc/core/mmc.c b/drivers/mmc/core/mmc.c
index df19777068a6..9355366d3259 100644
--- a/drivers/mmc/core/mmc.c
+++ b/drivers/mmc/core/mmc.c
@@ -1003,19 +1003,6 @@ static int mmc_select_bus_width(struct mmc_card *card)
return err;
}
-/* Caller must hold re-tuning */
-static int mmc_switch_status(struct mmc_card *card)
-{
- u32 status;
- int err;
-
- err = mmc_send_status(card, &status);
- if (err)
- return err;
-
- return mmc_switch_status_error(card->host, status);
-}
-
/*
* Switch to the high-speed mode
*/
diff --git a/drivers/mmc/core/mmc_ops.c b/drivers/mmc/core/mmc_ops.c
index ad6e9798e949..481bbdbae6e7 100644
--- a/drivers/mmc/core/mmc_ops.c
+++ b/drivers/mmc/core/mmc_ops.c
@@ -440,7 +440,7 @@ int mmc_spi_set_crc(struct mmc_host *host, int use_crc)
return err;
}
-int mmc_switch_status_error(struct mmc_host *host, u32 status)
+static int mmc_switch_status_error(struct mmc_host *host, u32 status)
{
if (mmc_host_is_spi(host)) {
if (status & R1_SPI_ILLEGAL_COMMAND)
@@ -455,6 +455,74 @@ int mmc_switch_status_error(struct mmc_host *host, u32 status)
return 0;
}
+/* Caller must hold re-tuning */
+int mmc_switch_status(struct mmc_card *card)
+{
+ u32 status;
+ int err;
+
+ err = mmc_send_status(card, &status);
+ if (err)
+ return err;
+
+ return mmc_switch_status_error(card->host, status);
+}
+
+static int mmc_poll_for_busy(struct mmc_card *card, unsigned int timeout_ms,
+ bool send_status, bool ignore_crc)
+{
+ struct mmc_host *host = card->host;
+ int err;
+ unsigned long timeout;
+ u32 status = 0;
+ bool expired = false;
+ bool busy = false;
+
+ /* We have an unspecified cmd timeout, use the fallback value. */
+ if (!timeout_ms)
+ timeout_ms = MMC_OPS_TIMEOUT_MS;
+
+ /*
+ * In cases when not allowed to poll by using CMD13 or because we aren't
+ * capable of polling by using ->card_busy(), then rely on waiting the
+ * stated timeout to be sufficient.
+ */
+ if (!send_status && !host->ops->card_busy) {
+ mmc_delay(timeout_ms);
+ return 0;
+ }
+
+ timeout = jiffies + msecs_to_jiffies(timeout_ms) + 1;
+ do {
+ /*
+ * Due to the possibility of being preempted while polling,
+ * check the expiration time first.
+ */
+ expired = time_after(jiffies, timeout);
+
+ if (host->ops->card_busy) {
+ busy = host->ops->card_busy(host);
+ } else {
+ err = __mmc_send_status(card, &status, ignore_crc);
+ if (err)
+ return err;
+ busy = R1_CURRENT_STATE(status) == R1_STATE_PRG;
+ }
+
+ /* Timeout if the device still remains busy. */
+ if (expired && busy) {
+ pr_err("%s: Card stuck being busy! %s\n",
+ mmc_hostname(host), __func__);
+ return -ETIMEDOUT;
+ }
+ } while (busy);
+
+ if (host->ops->card_busy && send_status)
+ return mmc_switch_status(card);
+
+ return mmc_switch_status_error(host, status);
+}
+
/**
* __mmc_switch - modify EXT_CSD register
* @card: the MMC card associated with the data transfer
@@ -476,11 +544,7 @@ int __mmc_switch(struct mmc_card *card, u8 set, u8 index, u8 value,
struct mmc_host *host = card->host;
int err;
struct mmc_command cmd = {0};
- unsigned long timeout;
- u32 status = 0;
bool use_r1b_resp = use_busy_signal;
- bool expired = false;
- bool busy = false;
mmc_retune_hold(host);
@@ -522,62 +586,16 @@ int __mmc_switch(struct mmc_card *card, u8 set, u8 index, u8 value,
if (!use_busy_signal)
goto out;
- /*
- * CRC errors shall only be ignored in cases were CMD13 is used to poll
- * to detect busy completion.
- */
- 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;
-
- /* Must check status to be sure of no errors. */
- timeout = jiffies + msecs_to_jiffies(timeout_ms) + 1;
- do {
- /*
- * Due to the possibility of being preempted after
- * sending the status command, check the expiration
- * time first.
- */
- expired = time_after(jiffies, timeout);
- if (send_status) {
- err = __mmc_send_status(card, &status, ignore_crc);
- if (err)
- goto out;
- }
- if ((host->caps & MMC_CAP_WAIT_WHILE_BUSY) && use_r1b_resp)
- break;
- if (host->ops->card_busy) {
- if (!host->ops->card_busy(host))
- break;
- busy = true;
- }
- if (mmc_host_is_spi(host))
- break;
-
- /*
- * We are not allowed to issue a status command and the host
- * does'nt support MMC_CAP_WAIT_WHILE_BUSY, then we can only
- * rely on waiting for the stated timeout to be sufficient.
- */
- if (!send_status && !host->ops->card_busy) {
- mmc_delay(timeout_ms);
- goto out;
- }
-
- /* Timeout if the device never leaves the program state. */
- if (expired &&
- (R1_CURRENT_STATE(status) == R1_STATE_PRG || busy)) {
- pr_err("%s: Card stuck in programming state! %s\n",
- mmc_hostname(host), __func__);
- err = -ETIMEDOUT;
- goto out;
- }
- } while (R1_CURRENT_STATE(status) == R1_STATE_PRG || busy);
+ /*If SPI or used HW busy detection above, then we don't need to poll. */
+ if (((host->caps & MMC_CAP_WAIT_WHILE_BUSY) && use_r1b_resp) ||
+ mmc_host_is_spi(host)) {
+ if (send_status)
+ err = mmc_switch_status(card);
+ goto out;
+ }
- err = mmc_switch_status_error(host, status);
+ /* Let's try to poll to find out when the command is completed. */
+ err = mmc_poll_for_busy(card, timeout_ms, send_status, ignore_crc);
out:
mmc_retune_release(host);
diff --git a/drivers/mmc/core/mmc_ops.h b/drivers/mmc/core/mmc_ops.h
index f1b8e81aaa28..7f6c0e98e7f5 100644
--- a/drivers/mmc/core/mmc_ops.h
+++ b/drivers/mmc/core/mmc_ops.h
@@ -27,7 +27,7 @@ int mmc_spi_set_crc(struct mmc_host *host, int use_crc);
int mmc_bus_test(struct mmc_card *card, u8 bus_width);
int mmc_send_hpi_cmd(struct mmc_card *card, u32 *status);
int mmc_can_ext_csd(struct mmc_card *card);
-int mmc_switch_status_error(struct mmc_host *host, u32 status);
+int mmc_switch_status(struct mmc_card *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,
bool ignore_crc);
diff --git a/drivers/mmc/core/slot-gpio.c b/drivers/mmc/core/slot-gpio.c
index 27117ba47073..babe591aea96 100644
--- a/drivers/mmc/core/slot-gpio.c
+++ b/drivers/mmc/core/slot-gpio.c
@@ -258,6 +258,14 @@ int mmc_gpiod_request_cd(struct mmc_host *host, const char *con_id,
}
EXPORT_SYMBOL(mmc_gpiod_request_cd);
+bool mmc_can_gpio_cd(struct mmc_host *host)
+{
+ struct mmc_gpio *ctx = host->slot.handler_priv;
+
+ return ctx->cd_gpio ? true : false;
+}
+EXPORT_SYMBOL(mmc_can_gpio_cd);
+
/**
* mmc_gpiod_request_ro - request a gpio descriptor for write protection
* @host: mmc host
diff --git a/drivers/mmc/host/Kconfig b/drivers/mmc/host/Kconfig
index 5274f503a39a..4128a3c5fa23 100644
--- a/drivers/mmc/host/Kconfig
+++ b/drivers/mmc/host/Kconfig
@@ -144,6 +144,7 @@ config MMC_SDHCI_OF_ESDHC
depends on MMC_SDHCI_PLTFM
depends on PPC || ARCH_MXC || ARCH_LAYERSCAPE
select MMC_SDHCI_IO_ACCESSORS
+ select FSL_GUTS
help
This selects the Freescale eSDHC controller support.
@@ -322,6 +323,16 @@ config MMC_SDHCI_IPROC
If unsure, say N.
+config MMC_MESON_GX
+ tristate "Amlogic S905/GX* SD/MMC Host Controller support"
+ depends on ARCH_MESON && MMC
+ help
+ This selects support for the Amlogic SD/MMC Host Controller
+ found on the S905/GX* family of SoCs. This controller is
+ MMC 5.1 compliant and supports SD, eMMC and SDIO interfaces.
+
+ If you have a controller with this interface, say Y here.
+
config MMC_MOXART
tristate "MOXART SD/MMC Host Controller support"
depends on ARCH_MOXART && MMC
diff --git a/drivers/mmc/host/Makefile b/drivers/mmc/host/Makefile
index e2bdaaf43184..e609bf04346b 100644
--- a/drivers/mmc/host/Makefile
+++ b/drivers/mmc/host/Makefile
@@ -53,6 +53,7 @@ obj-$(CONFIG_MMC_JZ4740) += jz4740_mmc.o
obj-$(CONFIG_MMC_VUB300) += vub300.o
obj-$(CONFIG_MMC_USHC) += ushc.o
obj-$(CONFIG_MMC_WMT) += wmt-sdmmc.o
+obj-$(CONFIG_MMC_MESON_GX) += meson-gx-mmc.o
obj-$(CONFIG_MMC_MOXART) += moxart-mmc.o
obj-$(CONFIG_MMC_SUNXI) += sunxi-mmc.o
obj-$(CONFIG_MMC_USDHI6ROL0) += usdhi6rol0.o
diff --git a/drivers/mmc/host/davinci_mmc.c b/drivers/mmc/host/davinci_mmc.c
index 8fa478c3b0db..36b5af8eadb8 100644
--- a/drivers/mmc/host/davinci_mmc.c
+++ b/drivers/mmc/host/davinci_mmc.c
@@ -35,6 +35,7 @@
#include <linux/mmc/mmc.h>
#include <linux/of.h>
#include <linux/of_device.h>
+#include <linux/mmc/slot-gpio.h>
#include <linux/platform_data/mmc-davinci.h>
@@ -1029,9 +1030,10 @@ static int mmc_davinci_get_cd(struct mmc_host *mmc)
struct platform_device *pdev = to_platform_device(mmc->parent);
struct davinci_mmc_config *config = pdev->dev.platform_data;
- if (!config || !config->get_cd)
- return -ENOSYS;
- return config->get_cd(pdev->id);
+ if (config && config->get_cd)
+ return config->get_cd(pdev->id);
+
+ return mmc_gpio_get_cd(mmc);
}
static int mmc_davinci_get_ro(struct mmc_host *mmc)
@@ -1039,9 +1041,10 @@ static int mmc_davinci_get_ro(struct mmc_host *mmc)
struct platform_device *pdev = to_platform_device(mmc->parent);
struct davinci_mmc_config *config = pdev->dev.platform_data;
- if (!config || !config->get_ro)
- return -ENOSYS;
- return config->get_ro(pdev->id);
+ if (config && config->get_ro)
+ return config->get_ro(pdev->id);
+
+ return mmc_gpio_get_ro(mmc);
}
static void mmc_davinci_enable_sdio_irq(struct mmc_host *mmc, int enable)
@@ -1159,49 +1162,53 @@ static const struct of_device_id davinci_mmc_dt_ids[] = {
};
MODULE_DEVICE_TABLE(of, davinci_mmc_dt_ids);
-static struct davinci_mmc_config
- *mmc_parse_pdata(struct platform_device *pdev)
+static int mmc_davinci_parse_pdata(struct mmc_host *mmc)
{
- struct device_node *np;
+ struct platform_device *pdev = to_platform_device(mmc->parent);
struct davinci_mmc_config *pdata = pdev->dev.platform_data;
- const struct of_device_id *match =
- of_match_device(davinci_mmc_dt_ids, &pdev->dev);
- u32 data;
-
- np = pdev->dev.of_node;
- if (!np)
- return pdata;
-
- pdata = devm_kzalloc(&pdev->dev, sizeof(*pdata), GFP_KERNEL);
- if (!pdata) {
- dev_err(&pdev->dev, "Failed to allocate memory for struct davinci_mmc_config\n");
- goto nodata;
- }
+ struct mmc_davinci_host *host;
+ int ret;
- if (match)
- pdev->id_entry = match->data;
+ if (!pdata)
+ return -EINVAL;
- if (of_property_read_u32(np, "max-frequency", &pdata->max_freq))
- dev_info(&pdev->dev, "'max-frequency' property not specified, defaulting to 25MHz\n");
+ host = mmc_priv(mmc);
+ if (!host)
+ return -EINVAL;
- of_property_read_u32(np, "bus-width", &data);
- switch (data) {
- case 1:
- case 4:
- case 8:
- pdata->wires = data;
- break;
- default:
- pdata->wires = 1;
- dev_info(&pdev->dev, "Unsupported buswidth, defaulting to 1 bit\n");
- }
-nodata:
- return pdata;
+ if (pdata && pdata->nr_sg)
+ host->nr_sg = pdata->nr_sg - 1;
+
+ if (pdata && (pdata->wires == 4 || pdata->wires == 0))
+ mmc->caps |= MMC_CAP_4_BIT_DATA;
+
+ if (pdata && (pdata->wires == 8))
+ mmc->caps |= (MMC_CAP_4_BIT_DATA | MMC_CAP_8_BIT_DATA);
+
+ mmc->f_min = 312500;
+ mmc->f_max = 25000000;
+ if (pdata && pdata->max_freq)
+ mmc->f_max = pdata->max_freq;
+ if (pdata && pdata->caps)
+ mmc->caps |= pdata->caps;
+
+ /* Register a cd gpio, if there is not one, enable polling */
+ ret = mmc_gpiod_request_cd(mmc, "cd", 0, false, 0, NULL);
+ if (ret == -EPROBE_DEFER)
+ return ret;
+ else if (ret)
+ mmc->caps |= MMC_CAP_NEEDS_POLL;
+
+ ret = mmc_gpiod_request_ro(mmc, "wp", 0, false, 0, NULL);
+ if (ret == -EPROBE_DEFER)
+ return ret;
+
+ return 0;
}
static int __init davinci_mmcsd_probe(struct platform_device *pdev)
{
- struct davinci_mmc_config *pdata = NULL;
+ const struct of_device_id *match;
struct mmc_davinci_host *host = NULL;
struct mmc_host *mmc = NULL;
struct resource *r, *mem = NULL;
@@ -1209,12 +1216,6 @@ static int __init davinci_mmcsd_probe(struct platform_device *pdev)
size_t mem_size;
const struct platform_device_id *id_entry;
- pdata = mmc_parse_pdata(pdev);
- if (pdata == NULL) {
- dev_err(&pdev->dev, "Couldn't get platform data\n");
- return -ENOENT;
- }
-
r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (!r)
return -ENODEV;
@@ -1253,14 +1254,28 @@ static int __init davinci_mmcsd_probe(struct platform_device *pdev)
host->mmc_input_clk = clk_get_rate(host->clk);
- init_mmcsd_host(host);
-
- if (pdata->nr_sg)
- host->nr_sg = pdata->nr_sg - 1;
+ match = of_match_device(davinci_mmc_dt_ids, &pdev->dev);
+ if (match) {
+ pdev->id_entry = match->data;
+ ret = mmc_of_parse(mmc);
+ if (ret) {
+ dev_err(&pdev->dev,
+ "could not parse of data: %d\n", ret);
+ goto parse_fail;
+ }
+ } else {
+ ret = mmc_davinci_parse_pdata(mmc);
+ if (ret) {
+ dev_err(&pdev->dev,
+ "could not parse platform data: %d\n", ret);
+ goto parse_fail;
+ } }
if (host->nr_sg > MAX_NR_SG || !host->nr_sg)
host->nr_sg = MAX_NR_SG;
+ init_mmcsd_host(host);
+
host->use_dma = use_dma;
host->mmc_irq = irq;
host->sdio_irq = platform_get_irq(pdev, 1);
@@ -1273,27 +1288,13 @@ static int __init davinci_mmcsd_probe(struct platform_device *pdev)
host->use_dma = 0;
}
- /* REVISIT: someday, support IRQ-driven card detection. */
- mmc->caps |= MMC_CAP_NEEDS_POLL;
mmc->caps |= MMC_CAP_WAIT_WHILE_BUSY;
- if (pdata && (pdata->wires == 4 || pdata->wires == 0))
- mmc->caps |= MMC_CAP_4_BIT_DATA;
-
- if (pdata && (pdata->wires == 8))
- mmc->caps |= (MMC_CAP_4_BIT_DATA | MMC_CAP_8_BIT_DATA);
-
id_entry = platform_get_device_id(pdev);
if (id_entry)
host->version = id_entry->driver_data;
mmc->ops = &mmc_davinci_ops;
- mmc->f_min = 312500;
- mmc->f_max = 25000000;
- if (pdata && pdata->max_freq)
- mmc->f_max = pdata->max_freq;
- if (pdata && pdata->caps)
- mmc->caps |= pdata->caps;
mmc->ocr_avail = MMC_VDD_32_33 | MMC_VDD_33_34;
/* With no iommu coalescing pages, each phys_seg is a hw_seg.
@@ -1354,6 +1355,7 @@ mmc_add_host_fail:
mmc_davinci_cpufreq_deregister(host);
cpu_freq_fail:
davinci_release_dma_channels(host);
+parse_fail:
dma_probe_defer:
clk_disable_unprepare(host->clk);
clk_prepare_enable_fail:
diff --git a/drivers/mmc/host/dw_mmc-exynos.c b/drivers/mmc/host/dw_mmc-exynos.c
index 7ab3d749b5ae..e2439a108873 100644
--- a/drivers/mmc/host/dw_mmc-exynos.c
+++ b/drivers/mmc/host/dw_mmc-exynos.c
@@ -17,6 +17,7 @@
#include <linux/mmc/mmc.h>
#include <linux/of.h>
#include <linux/of_gpio.h>
+#include <linux/pm_runtime.h>
#include <linux/slab.h>
#include "dw_mmc.h"
@@ -161,20 +162,13 @@ static void dw_mci_exynos_set_clksel_timing(struct dw_mci *host, u32 timing)
set_bit(DW_MMC_CARD_NO_USE_HOLD, &host->cur_slot->flags);
}
-#ifdef CONFIG_PM_SLEEP
-static int dw_mci_exynos_suspend(struct device *dev)
-{
- struct dw_mci *host = dev_get_drvdata(dev);
-
- return dw_mci_suspend(host);
-}
-
-static int dw_mci_exynos_resume(struct device *dev)
+#ifdef CONFIG_PM
+static int dw_mci_exynos_runtime_resume(struct device *dev)
{
struct dw_mci *host = dev_get_drvdata(dev);
dw_mci_exynos_config_smu(host);
- return dw_mci_resume(host);
+ return dw_mci_runtime_resume(dev);
}
/**
@@ -211,10 +205,8 @@ static int dw_mci_exynos_resume_noirq(struct device *dev)
return 0;
}
#else
-#define dw_mci_exynos_suspend NULL
-#define dw_mci_exynos_resume NULL
#define dw_mci_exynos_resume_noirq NULL
-#endif /* CONFIG_PM_SLEEP */
+#endif /* CONFIG_PM */
static void dw_mci_exynos_config_hs400(struct dw_mci *host, u32 timing)
{
@@ -531,7 +523,11 @@ static int dw_mci_exynos_probe(struct platform_device *pdev)
}
static const struct dev_pm_ops dw_mci_exynos_pmops = {
- SET_SYSTEM_SLEEP_PM_OPS(dw_mci_exynos_suspend, dw_mci_exynos_resume)
+ SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend,
+ pm_runtime_force_resume)
+ SET_RUNTIME_PM_OPS(dw_mci_runtime_suspend,
+ dw_mci_exynos_runtime_resume,
+ NULL)
.resume_noirq = dw_mci_exynos_resume_noirq,
.thaw_noirq = dw_mci_exynos_resume_noirq,
.restore_noirq = dw_mci_exynos_resume_noirq,
diff --git a/drivers/mmc/host/dw_mmc-k3.c b/drivers/mmc/host/dw_mmc-k3.c
index 624789496dce..9821e6bd5d5e 100644
--- a/drivers/mmc/host/dw_mmc-k3.c
+++ b/drivers/mmc/host/dw_mmc-k3.c
@@ -15,6 +15,7 @@
#include <linux/module.h>
#include <linux/of_address.h>
#include <linux/platform_device.h>
+#include <linux/pm_runtime.h>
#include <linux/regmap.h>
#include <linux/regulator/consumer.h>
@@ -162,35 +163,13 @@ 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);
- int ret;
-
- ret = dw_mci_suspend(host);
- if (!ret)
- clk_disable_unprepare(host->ciu_clk);
-
- return ret;
-}
-
-static int dw_mci_k3_resume(struct device *dev)
-{
- struct dw_mci *host = dev_get_drvdata(dev);
- int ret;
-
- ret = clk_prepare_enable(host->ciu_clk);
- if (ret) {
- dev_err(host->dev, "failed to enable ciu clock\n");
- return ret;
- }
-
- 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);
+static const struct dev_pm_ops dw_mci_k3_dev_pm_ops = {
+ SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend,
+ pm_runtime_force_resume)
+ SET_RUNTIME_PM_OPS(dw_mci_runtime_suspend,
+ dw_mci_runtime_resume,
+ NULL)
+};
static struct platform_driver dw_mci_k3_pltfm_driver = {
.probe = dw_mci_k3_probe,
@@ -198,7 +177,7 @@ static struct platform_driver dw_mci_k3_pltfm_driver = {
.driver = {
.name = "dwmmc_k3",
.of_match_table = dw_mci_k3_match,
- .pm = &dw_mci_k3_pmops,
+ .pm = &dw_mci_k3_dev_pm_ops,
},
};
diff --git a/drivers/mmc/host/dw_mmc-pci.c b/drivers/mmc/host/dw_mmc-pci.c
index 4c69fbd29811..ab82796b01e2 100644
--- a/drivers/mmc/host/dw_mmc-pci.c
+++ b/drivers/mmc/host/dw_mmc-pci.c
@@ -14,6 +14,7 @@
#include <linux/io.h>
#include <linux/irq.h>
#include <linux/pci.h>
+#include <linux/pm_runtime.h>
#include <linux/slab.h>
#include <linux/mmc/host.h>
#include <linux/mmc/mmc.h>
@@ -79,25 +80,13 @@ static void dw_mci_pci_remove(struct pci_dev *pdev)
dw_mci_remove(host);
}
-#ifdef CONFIG_PM_SLEEP
-static int dw_mci_pci_suspend(struct device *dev)
-{
- struct pci_dev *pdev = to_pci_dev(dev);
- struct dw_mci *host = pci_get_drvdata(pdev);
-
- return dw_mci_suspend(host);
-}
-
-static int dw_mci_pci_resume(struct device *dev)
-{
- struct pci_dev *pdev = to_pci_dev(dev);
- struct dw_mci *host = pci_get_drvdata(pdev);
-
- return dw_mci_resume(host);
-}
-#endif /* CONFIG_PM_SLEEP */
-
-static SIMPLE_DEV_PM_OPS(dw_mci_pci_pmops, dw_mci_pci_suspend, dw_mci_pci_resume);
+static const struct dev_pm_ops dw_mci_pci_dev_pm_ops = {
+ SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend,
+ pm_runtime_force_resume)
+ SET_RUNTIME_PM_OPS(dw_mci_runtime_suspend,
+ dw_mci_runtime_resume,
+ NULL)
+};
static const struct pci_device_id dw_mci_pci_id[] = {
{ PCI_DEVICE(SYNOPSYS_DW_MCI_VENDOR_ID, SYNOPSYS_DW_MCI_DEVICE_ID) },
@@ -111,7 +100,7 @@ static struct pci_driver dw_mci_pci_driver = {
.probe = dw_mci_pci_probe,
.remove = dw_mci_pci_remove,
.driver = {
- .pm = &dw_mci_pci_pmops
+ .pm = &dw_mci_pci_dev_pm_ops,
},
};
diff --git a/drivers/mmc/host/dw_mmc-pltfm.c b/drivers/mmc/host/dw_mmc-pltfm.c
index dbbc4303bdd0..1236d49ba36e 100644
--- a/drivers/mmc/host/dw_mmc-pltfm.c
+++ b/drivers/mmc/host/dw_mmc-pltfm.c
@@ -16,6 +16,7 @@
#include <linux/io.h>
#include <linux/irq.h>
#include <linux/platform_device.h>
+#include <linux/pm_runtime.h>
#include <linux/slab.h>
#include <linux/mmc/host.h>
#include <linux/mmc/mmc.h>
@@ -58,26 +59,13 @@ int dw_mci_pltfm_register(struct platform_device *pdev,
}
EXPORT_SYMBOL_GPL(dw_mci_pltfm_register);
-#ifdef CONFIG_PM_SLEEP
-/*
- * TODO: we should probably disable the clock to the card in the suspend path.
- */
-static int dw_mci_pltfm_suspend(struct device *dev)
-{
- struct dw_mci *host = dev_get_drvdata(dev);
-
- return dw_mci_suspend(host);
-}
-
-static int dw_mci_pltfm_resume(struct device *dev)
-{
- struct dw_mci *host = dev_get_drvdata(dev);
-
- return dw_mci_resume(host);
-}
-#endif /* CONFIG_PM_SLEEP */
-
-SIMPLE_DEV_PM_OPS(dw_mci_pltfm_pmops, dw_mci_pltfm_suspend, dw_mci_pltfm_resume);
+const struct dev_pm_ops dw_mci_pltfm_pmops = {
+ SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend,
+ pm_runtime_force_resume)
+ SET_RUNTIME_PM_OPS(dw_mci_runtime_suspend,
+ dw_mci_runtime_resume,
+ NULL)
+};
EXPORT_SYMBOL_GPL(dw_mci_pltfm_pmops);
static const struct of_device_id dw_mci_pltfm_match[] = {
diff --git a/drivers/mmc/host/dw_mmc-rockchip.c b/drivers/mmc/host/dw_mmc-rockchip.c
index 25eae359a5ea..9a46e4694227 100644
--- a/drivers/mmc/host/dw_mmc-rockchip.c
+++ b/drivers/mmc/host/dw_mmc-rockchip.c
@@ -13,6 +13,8 @@
#include <linux/mmc/host.h>
#include <linux/mmc/dw_mmc.h>
#include <linux/of_address.h>
+#include <linux/mmc/slot-gpio.h>
+#include <linux/pm_runtime.h>
#include <linux/slab.h>
#include "dw_mmc.h"
@@ -325,6 +327,7 @@ static int dw_mci_rockchip_probe(struct platform_device *pdev)
{
const struct dw_mci_drv_data *drv_data;
const struct of_device_id *match;
+ int ret;
if (!pdev->dev.of_node)
return -ENODEV;
@@ -332,16 +335,49 @@ static int dw_mci_rockchip_probe(struct platform_device *pdev)
match = of_match_node(dw_mci_rockchip_match, pdev->dev.of_node);
drv_data = match->data;
- return dw_mci_pltfm_register(pdev, drv_data);
+ pm_runtime_get_noresume(&pdev->dev);
+ pm_runtime_set_active(&pdev->dev);
+ pm_runtime_enable(&pdev->dev);
+ pm_runtime_set_autosuspend_delay(&pdev->dev, 50);
+ pm_runtime_use_autosuspend(&pdev->dev);
+
+ ret = dw_mci_pltfm_register(pdev, drv_data);
+ if (ret) {
+ pm_runtime_disable(&pdev->dev);
+ pm_runtime_set_suspended(&pdev->dev);
+ pm_runtime_put_noidle(&pdev->dev);
+ return ret;
+ }
+
+ pm_runtime_put_autosuspend(&pdev->dev);
+
+ return 0;
}
+static int dw_mci_rockchip_remove(struct platform_device *pdev)
+{
+ pm_runtime_get_sync(&pdev->dev);
+ pm_runtime_disable(&pdev->dev);
+ pm_runtime_put_noidle(&pdev->dev);
+
+ return dw_mci_pltfm_remove(pdev);
+}
+
+static const struct dev_pm_ops dw_mci_rockchip_dev_pm_ops = {
+ SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend,
+ pm_runtime_force_resume)
+ SET_RUNTIME_PM_OPS(dw_mci_runtime_suspend,
+ dw_mci_runtime_resume,
+ NULL)
+};
+
static struct platform_driver dw_mci_rockchip_pltfm_driver = {
.probe = dw_mci_rockchip_probe,
- .remove = dw_mci_pltfm_remove,
+ .remove = dw_mci_rockchip_remove,
.driver = {
.name = "dwmmc_rockchip",
.of_match_table = dw_mci_rockchip_match,
- .pm = &dw_mci_pltfm_pmops,
+ .pm = &dw_mci_rockchip_dev_pm_ops,
},
};
diff --git a/drivers/mmc/host/dw_mmc.c b/drivers/mmc/host/dw_mmc.c
index 50a674be6655..d426fa41bcce 100644
--- a/drivers/mmc/host/dw_mmc.c
+++ b/drivers/mmc/host/dw_mmc.c
@@ -1058,6 +1058,7 @@ static int dw_mci_submit_data_dma(struct dw_mci *host, struct mmc_data *data)
spin_unlock_irqrestore(&host->irq_lock, irqflags);
if (host->dma_ops->start(host, sg_len)) {
+ host->dma_ops->stop(host);
/* We can't do DMA, try PIO for this one */
dev_dbg(host->dev,
"%s: fall back to PIO mode for current transfer\n",
@@ -3266,28 +3267,42 @@ EXPORT_SYMBOL(dw_mci_remove);
-#ifdef CONFIG_PM_SLEEP
-/*
- * TODO: we should probably disable the clock to the card in the suspend path.
- */
-int dw_mci_suspend(struct dw_mci *host)
+#ifdef CONFIG_PM
+int dw_mci_runtime_suspend(struct device *dev)
{
+ struct dw_mci *host = dev_get_drvdata(dev);
+
if (host->use_dma && host->dma_ops->exit)
host->dma_ops->exit(host);
+ clk_disable_unprepare(host->ciu_clk);
+
+ if (host->cur_slot &&
+ (mmc_can_gpio_cd(host->cur_slot->mmc) ||
+ !mmc_card_is_removable(host->cur_slot->mmc)))
+ clk_disable_unprepare(host->biu_clk);
+
return 0;
}
-EXPORT_SYMBOL(dw_mci_suspend);
+EXPORT_SYMBOL(dw_mci_runtime_suspend);
-int dw_mci_resume(struct dw_mci *host)
+int dw_mci_runtime_resume(struct device *dev)
{
- int i, ret;
+ int i, ret = 0;
+ struct dw_mci *host = dev_get_drvdata(dev);
- if (!dw_mci_ctrl_reset(host, SDMMC_CTRL_ALL_RESET_FLAGS)) {
- ret = -ENODEV;
- return ret;
+ if (host->cur_slot &&
+ (mmc_can_gpio_cd(host->cur_slot->mmc) ||
+ !mmc_card_is_removable(host->cur_slot->mmc))) {
+ ret = clk_prepare_enable(host->biu_clk);
+ if (ret)
+ return ret;
}
+ ret = clk_prepare_enable(host->ciu_clk);
+ if (ret)
+ return ret;
+
if (host->use_dma && host->dma_ops->init)
host->dma_ops->init(host);
@@ -3295,8 +3310,8 @@ int dw_mci_resume(struct dw_mci *host)
* Restore the initial value at FIFOTH register
* And Invalidate the prev_blksz with zero
*/
- mci_writel(host, FIFOTH, host->fifoth_val);
- host->prev_blksz = 0;
+ mci_writel(host, FIFOTH, host->fifoth_val);
+ host->prev_blksz = 0;
/* Put in max timeout */
mci_writel(host, TMOUT, 0xFFFFFFFF);
@@ -3321,10 +3336,10 @@ int dw_mci_resume(struct dw_mci *host)
/* Now that slots are all setup, we can enable card detect */
dw_mci_enable_cd(host);
- return 0;
+ return ret;
}
-EXPORT_SYMBOL(dw_mci_resume);
-#endif /* CONFIG_PM_SLEEP */
+EXPORT_SYMBOL(dw_mci_runtime_resume);
+#endif /* CONFIG_PM */
static int __init dw_mci_init(void)
{
diff --git a/drivers/mmc/host/dw_mmc.h b/drivers/mmc/host/dw_mmc.h
index e8cd2dec3263..4a6ae750feeb 100644
--- a/drivers/mmc/host/dw_mmc.h
+++ b/drivers/mmc/host/dw_mmc.h
@@ -234,9 +234,9 @@
extern int dw_mci_probe(struct dw_mci *host);
extern void dw_mci_remove(struct dw_mci *host);
-#ifdef CONFIG_PM_SLEEP
-extern int dw_mci_suspend(struct dw_mci *host);
-extern int dw_mci_resume(struct dw_mci *host);
+#ifdef CONFIG_PM
+extern int dw_mci_runtime_suspend(struct device *device);
+extern int dw_mci_runtime_resume(struct device *device);
#endif
/**
diff --git a/drivers/mmc/host/meson-gx-mmc.c b/drivers/mmc/host/meson-gx-mmc.c
new file mode 100644
index 000000000000..b352760c041e
--- /dev/null
+++ b/drivers/mmc/host/meson-gx-mmc.c
@@ -0,0 +1,851 @@
+/*
+ * Amlogic SD/eMMC driver for the GX/S905 family SoCs
+ *
+ * Copyright (c) 2016 BayLibre, SAS.
+ * Author: Kevin Hilman <khilman@baylibre.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of version 2 of the GNU General Public License 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.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
+ * The full GNU General Public License is included in this distribution
+ * in the file called COPYING.
+ */
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/device.h>
+#include <linux/of_device.h>
+#include <linux/platform_device.h>
+#include <linux/ioport.h>
+#include <linux/spinlock.h>
+#include <linux/dma-mapping.h>
+#include <linux/mmc/host.h>
+#include <linux/mmc/mmc.h>
+#include <linux/mmc/sdio.h>
+#include <linux/mmc/slot-gpio.h>
+#include <linux/io.h>
+#include <linux/clk.h>
+#include <linux/clk-provider.h>
+#include <linux/regulator/consumer.h>
+
+#define DRIVER_NAME "meson-gx-mmc"
+
+#define SD_EMMC_CLOCK 0x0
+#define CLK_DIV_SHIFT 0
+#define CLK_DIV_WIDTH 6
+#define CLK_DIV_MASK 0x3f
+#define CLK_DIV_MAX 63
+#define CLK_SRC_SHIFT 6
+#define CLK_SRC_WIDTH 2
+#define CLK_SRC_MASK 0x3
+#define CLK_SRC_XTAL 0 /* external crystal */
+#define CLK_SRC_XTAL_RATE 24000000
+#define CLK_SRC_PLL 1 /* FCLK_DIV2 */
+#define CLK_SRC_PLL_RATE 1000000000
+#define CLK_PHASE_SHIFT 8
+#define CLK_PHASE_MASK 0x3
+#define CLK_PHASE_0 0
+#define CLK_PHASE_90 1
+#define CLK_PHASE_180 2
+#define CLK_PHASE_270 3
+#define CLK_ALWAYS_ON BIT(24)
+
+#define SD_EMMC_DElAY 0x4
+#define SD_EMMC_ADJUST 0x8
+#define SD_EMMC_CALOUT 0x10
+#define SD_EMMC_START 0x40
+#define START_DESC_INIT BIT(0)
+#define START_DESC_BUSY BIT(1)
+#define START_DESC_ADDR_SHIFT 2
+#define START_DESC_ADDR_MASK (~0x3)
+
+#define SD_EMMC_CFG 0x44
+#define CFG_BUS_WIDTH_SHIFT 0
+#define CFG_BUS_WIDTH_MASK 0x3
+#define CFG_BUS_WIDTH_1 0x0
+#define CFG_BUS_WIDTH_4 0x1
+#define CFG_BUS_WIDTH_8 0x2
+#define CFG_DDR BIT(2)
+#define CFG_BLK_LEN_SHIFT 4
+#define CFG_BLK_LEN_MASK 0xf
+#define CFG_RESP_TIMEOUT_SHIFT 8
+#define CFG_RESP_TIMEOUT_MASK 0xf
+#define CFG_RC_CC_SHIFT 12
+#define CFG_RC_CC_MASK 0xf
+#define CFG_STOP_CLOCK BIT(22)
+#define CFG_CLK_ALWAYS_ON BIT(18)
+#define CFG_AUTO_CLK BIT(23)
+
+#define SD_EMMC_STATUS 0x48
+#define STATUS_BUSY BIT(31)
+
+#define SD_EMMC_IRQ_EN 0x4c
+#define IRQ_EN_MASK 0x3fff
+#define IRQ_RXD_ERR_SHIFT 0
+#define IRQ_RXD_ERR_MASK 0xff
+#define IRQ_TXD_ERR BIT(8)
+#define IRQ_DESC_ERR BIT(9)
+#define IRQ_RESP_ERR BIT(10)
+#define IRQ_RESP_TIMEOUT BIT(11)
+#define IRQ_DESC_TIMEOUT BIT(12)
+#define IRQ_END_OF_CHAIN BIT(13)
+#define IRQ_RESP_STATUS BIT(14)
+#define IRQ_SDIO BIT(15)
+
+#define SD_EMMC_CMD_CFG 0x50
+#define SD_EMMC_CMD_ARG 0x54
+#define SD_EMMC_CMD_DAT 0x58
+#define SD_EMMC_CMD_RSP 0x5c
+#define SD_EMMC_CMD_RSP1 0x60
+#define SD_EMMC_CMD_RSP2 0x64
+#define SD_EMMC_CMD_RSP3 0x68
+
+#define SD_EMMC_RXD 0x94
+#define SD_EMMC_TXD 0x94
+#define SD_EMMC_LAST_REG SD_EMMC_TXD
+
+#define SD_EMMC_CFG_BLK_SIZE 512 /* internal buffer max: 512 bytes */
+#define SD_EMMC_CFG_RESP_TIMEOUT 256 /* in clock cycles */
+#define SD_EMMC_CFG_CMD_GAP 16 /* in clock cycles */
+#define MUX_CLK_NUM_PARENTS 2
+
+struct meson_host {
+ struct device *dev;
+ struct mmc_host *mmc;
+ struct mmc_request *mrq;
+ struct mmc_command *cmd;
+
+ spinlock_t lock;
+ void __iomem *regs;
+ int irq;
+ u32 ocr_mask;
+ struct clk *core_clk;
+ struct clk_mux mux;
+ struct clk *mux_clk;
+ struct clk *mux_parent[MUX_CLK_NUM_PARENTS];
+ unsigned long mux_parent_rate[MUX_CLK_NUM_PARENTS];
+
+ struct clk_divider cfg_div;
+ struct clk *cfg_div_clk;
+
+ unsigned int bounce_buf_size;
+ void *bounce_buf;
+ dma_addr_t bounce_dma_addr;
+
+ bool vqmmc_enabled;
+};
+
+struct sd_emmc_desc {
+ u32 cmd_cfg;
+ u32 cmd_arg;
+ u32 cmd_data;
+ u32 cmd_resp;
+};
+#define CMD_CFG_LENGTH_SHIFT 0
+#define CMD_CFG_LENGTH_MASK 0x1ff
+#define CMD_CFG_BLOCK_MODE BIT(9)
+#define CMD_CFG_R1B BIT(10)
+#define CMD_CFG_END_OF_CHAIN BIT(11)
+#define CMD_CFG_TIMEOUT_SHIFT 12
+#define CMD_CFG_TIMEOUT_MASK 0xf
+#define CMD_CFG_NO_RESP BIT(16)
+#define CMD_CFG_NO_CMD BIT(17)
+#define CMD_CFG_DATA_IO BIT(18)
+#define CMD_CFG_DATA_WR BIT(19)
+#define CMD_CFG_RESP_NOCRC BIT(20)
+#define CMD_CFG_RESP_128 BIT(21)
+#define CMD_CFG_RESP_NUM BIT(22)
+#define CMD_CFG_DATA_NUM BIT(23)
+#define CMD_CFG_CMD_INDEX_SHIFT 24
+#define CMD_CFG_CMD_INDEX_MASK 0x3f
+#define CMD_CFG_ERROR BIT(30)
+#define CMD_CFG_OWNER BIT(31)
+
+#define CMD_DATA_MASK (~0x3)
+#define CMD_DATA_BIG_ENDIAN BIT(1)
+#define CMD_DATA_SRAM BIT(0)
+#define CMD_RESP_MASK (~0x1)
+#define CMD_RESP_SRAM BIT(0)
+
+static int meson_mmc_clk_set(struct meson_host *host, unsigned long clk_rate)
+{
+ struct mmc_host *mmc = host->mmc;
+ int ret = 0;
+ u32 cfg;
+
+ if (clk_rate) {
+ if (WARN_ON(clk_rate > mmc->f_max))
+ clk_rate = mmc->f_max;
+ else if (WARN_ON(clk_rate < mmc->f_min))
+ clk_rate = mmc->f_min;
+ }
+
+ if (clk_rate == mmc->actual_clock)
+ return 0;
+
+ /* stop clock */
+ cfg = readl(host->regs + SD_EMMC_CFG);
+ if (!(cfg & CFG_STOP_CLOCK)) {
+ cfg |= CFG_STOP_CLOCK;
+ writel(cfg, host->regs + SD_EMMC_CFG);
+ }
+
+ dev_dbg(host->dev, "change clock rate %u -> %lu\n",
+ mmc->actual_clock, clk_rate);
+
+ if (clk_rate == 0) {
+ mmc->actual_clock = 0;
+ return 0;
+ }
+
+ ret = clk_set_rate(host->cfg_div_clk, clk_rate);
+ if (ret)
+ dev_warn(host->dev, "Unable to set cfg_div_clk to %lu. ret=%d\n",
+ clk_rate, ret);
+ else if (clk_rate && clk_rate != clk_get_rate(host->cfg_div_clk))
+ dev_warn(host->dev, "divider requested rate %lu != actual rate %lu: ret=%d\n",
+ clk_rate, clk_get_rate(host->cfg_div_clk), ret);
+ else
+ mmc->actual_clock = clk_rate;
+
+ /* (re)start clock, if non-zero */
+ if (!ret && clk_rate) {
+ cfg = readl(host->regs + SD_EMMC_CFG);
+ cfg &= ~CFG_STOP_CLOCK;
+ writel(cfg, host->regs + SD_EMMC_CFG);
+ }
+
+ return ret;
+}
+
+/*
+ * The SD/eMMC IP block has an internal mux and divider used for
+ * generating the MMC clock. Use the clock framework to create and
+ * manage these clocks.
+ */
+static int meson_mmc_clk_init(struct meson_host *host)
+{
+ struct clk_init_data init;
+ char clk_name[32];
+ int i, ret = 0;
+ const char *mux_parent_names[MUX_CLK_NUM_PARENTS];
+ unsigned int mux_parent_count = 0;
+ const char *clk_div_parents[1];
+ unsigned int f_min = UINT_MAX;
+ u32 clk_reg, cfg;
+
+ /* get the mux parents */
+ for (i = 0; i < MUX_CLK_NUM_PARENTS; i++) {
+ char name[16];
+
+ snprintf(name, sizeof(name), "clkin%d", i);
+ host->mux_parent[i] = devm_clk_get(host->dev, name);
+ if (IS_ERR(host->mux_parent[i])) {
+ ret = PTR_ERR(host->mux_parent[i]);
+ if (PTR_ERR(host->mux_parent[i]) != -EPROBE_DEFER)
+ dev_err(host->dev, "Missing clock %s\n", name);
+ host->mux_parent[i] = NULL;
+ return ret;
+ }
+
+ host->mux_parent_rate[i] = clk_get_rate(host->mux_parent[i]);
+ mux_parent_names[i] = __clk_get_name(host->mux_parent[i]);
+ mux_parent_count++;
+ if (host->mux_parent_rate[i] < f_min)
+ f_min = host->mux_parent_rate[i];
+ }
+
+ /* cacluate f_min based on input clocks, and max divider value */
+ if (f_min != UINT_MAX)
+ f_min = DIV_ROUND_UP(CLK_SRC_XTAL_RATE, CLK_DIV_MAX);
+ else
+ f_min = 4000000; /* default min: 400 MHz */
+ host->mmc->f_min = f_min;
+
+ /* create the mux */
+ snprintf(clk_name, sizeof(clk_name), "%s#mux", dev_name(host->dev));
+ init.name = clk_name;
+ init.ops = &clk_mux_ops;
+ init.flags = 0;
+ init.parent_names = mux_parent_names;
+ init.num_parents = mux_parent_count;
+
+ host->mux.reg = host->regs + SD_EMMC_CLOCK;
+ host->mux.shift = CLK_SRC_SHIFT;
+ host->mux.mask = CLK_SRC_MASK;
+ host->mux.flags = 0;
+ host->mux.table = NULL;
+ host->mux.hw.init = &init;
+
+ host->mux_clk = devm_clk_register(host->dev, &host->mux.hw);
+ if (WARN_ON(IS_ERR(host->mux_clk)))
+ return PTR_ERR(host->mux_clk);
+
+ /* create the divider */
+ snprintf(clk_name, sizeof(clk_name), "%s#div", dev_name(host->dev));
+ init.name = devm_kstrdup(host->dev, clk_name, GFP_KERNEL);
+ init.ops = &clk_divider_ops;
+ init.flags = CLK_SET_RATE_PARENT;
+ clk_div_parents[0] = __clk_get_name(host->mux_clk);
+ init.parent_names = clk_div_parents;
+ init.num_parents = ARRAY_SIZE(clk_div_parents);
+
+ host->cfg_div.reg = host->regs + SD_EMMC_CLOCK;
+ host->cfg_div.shift = CLK_DIV_SHIFT;
+ host->cfg_div.width = CLK_DIV_WIDTH;
+ host->cfg_div.hw.init = &init;
+ host->cfg_div.flags = CLK_DIVIDER_ONE_BASED |
+ CLK_DIVIDER_ROUND_CLOSEST | CLK_DIVIDER_ALLOW_ZERO;
+
+ host->cfg_div_clk = devm_clk_register(host->dev, &host->cfg_div.hw);
+ if (WARN_ON(PTR_ERR_OR_ZERO(host->cfg_div_clk)))
+ return PTR_ERR(host->cfg_div_clk);
+
+ /* init SD_EMMC_CLOCK to sane defaults w/min clock rate */
+ clk_reg = 0;
+ clk_reg |= CLK_PHASE_180 << CLK_PHASE_SHIFT;
+ clk_reg |= CLK_SRC_XTAL << CLK_SRC_SHIFT;
+ clk_reg |= CLK_DIV_MAX << CLK_DIV_SHIFT;
+ clk_reg &= ~CLK_ALWAYS_ON;
+ writel(clk_reg, host->regs + SD_EMMC_CLOCK);
+
+ /* Ensure clock starts in "auto" mode, not "always on" */
+ cfg = readl(host->regs + SD_EMMC_CFG);
+ cfg &= ~CFG_CLK_ALWAYS_ON;
+ cfg |= CFG_AUTO_CLK;
+ writel(cfg, host->regs + SD_EMMC_CFG);
+
+ ret = clk_prepare_enable(host->cfg_div_clk);
+ if (!ret)
+ ret = meson_mmc_clk_set(host, f_min);
+
+ if (!ret)
+ clk_disable_unprepare(host->cfg_div_clk);
+
+ return ret;
+}
+
+static void meson_mmc_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
+{
+ struct meson_host *host = mmc_priv(mmc);
+ u32 bus_width;
+ u32 val, orig;
+
+ /*
+ * GPIO regulator, only controls switching between 1v8 and
+ * 3v3, doesn't support MMC_POWER_OFF, MMC_POWER_ON.
+ */
+ switch (ios->power_mode) {
+ case MMC_POWER_OFF:
+ if (!IS_ERR(mmc->supply.vmmc))
+ mmc_regulator_set_ocr(mmc, mmc->supply.vmmc, 0);
+
+ if (!IS_ERR(mmc->supply.vqmmc) && host->vqmmc_enabled) {
+ regulator_disable(mmc->supply.vqmmc);
+ host->vqmmc_enabled = false;
+ }
+
+ break;
+
+ case MMC_POWER_UP:
+ if (!IS_ERR(mmc->supply.vmmc))
+ mmc_regulator_set_ocr(mmc, mmc->supply.vmmc, ios->vdd);
+ break;
+
+ case MMC_POWER_ON:
+ if (!IS_ERR(mmc->supply.vqmmc) && !host->vqmmc_enabled) {
+ int ret = regulator_enable(mmc->supply.vqmmc);
+
+ if (ret < 0)
+ dev_err(mmc_dev(mmc),
+ "failed to enable vqmmc regulator\n");
+ else
+ host->vqmmc_enabled = true;
+ }
+
+ break;
+ }
+
+
+ meson_mmc_clk_set(host, ios->clock);
+
+ /* Bus width */
+ val = readl(host->regs + SD_EMMC_CFG);
+ switch (ios->bus_width) {
+ case MMC_BUS_WIDTH_1:
+ bus_width = CFG_BUS_WIDTH_1;
+ break;
+ case MMC_BUS_WIDTH_4:
+ bus_width = CFG_BUS_WIDTH_4;
+ break;
+ case MMC_BUS_WIDTH_8:
+ bus_width = CFG_BUS_WIDTH_8;
+ break;
+ default:
+ dev_err(host->dev, "Invalid ios->bus_width: %u. Setting to 4.\n",
+ ios->bus_width);
+ bus_width = CFG_BUS_WIDTH_4;
+ return;
+ }
+
+ val = readl(host->regs + SD_EMMC_CFG);
+ orig = val;
+
+ val &= ~(CFG_BUS_WIDTH_MASK << CFG_BUS_WIDTH_SHIFT);
+ val |= bus_width << CFG_BUS_WIDTH_SHIFT;
+
+ val &= ~(CFG_BLK_LEN_MASK << CFG_BLK_LEN_SHIFT);
+ val |= ilog2(SD_EMMC_CFG_BLK_SIZE) << CFG_BLK_LEN_SHIFT;
+
+ val &= ~(CFG_RESP_TIMEOUT_MASK << CFG_RESP_TIMEOUT_SHIFT);
+ val |= ilog2(SD_EMMC_CFG_RESP_TIMEOUT) << CFG_RESP_TIMEOUT_SHIFT;
+
+ val &= ~(CFG_RC_CC_MASK << CFG_RC_CC_SHIFT);
+ val |= ilog2(SD_EMMC_CFG_CMD_GAP) << CFG_RC_CC_SHIFT;
+
+ writel(val, host->regs + SD_EMMC_CFG);
+
+ if (val != orig)
+ dev_dbg(host->dev, "%s: SD_EMMC_CFG: 0x%08x -> 0x%08x\n",
+ __func__, orig, val);
+}
+
+static int meson_mmc_request_done(struct mmc_host *mmc, struct mmc_request *mrq)
+{
+ struct meson_host *host = mmc_priv(mmc);
+
+ WARN_ON(host->mrq != mrq);
+
+ host->mrq = NULL;
+ host->cmd = NULL;
+ mmc_request_done(host->mmc, mrq);
+
+ return 0;
+}
+
+static void meson_mmc_start_cmd(struct mmc_host *mmc, struct mmc_command *cmd)
+{
+ struct meson_host *host = mmc_priv(mmc);
+ struct sd_emmc_desc *desc, desc_tmp;
+ u32 cfg;
+ u8 blk_len, cmd_cfg_timeout;
+ unsigned int xfer_bytes = 0;
+
+ /* Setup descriptors */
+ dma_rmb();
+ desc = &desc_tmp;
+ memset(desc, 0, sizeof(struct sd_emmc_desc));
+
+ desc->cmd_cfg |= (cmd->opcode & CMD_CFG_CMD_INDEX_MASK) <<
+ CMD_CFG_CMD_INDEX_SHIFT;
+ desc->cmd_cfg |= CMD_CFG_OWNER; /* owned by CPU */
+ desc->cmd_arg = cmd->arg;
+
+ /* Response */
+ if (cmd->flags & MMC_RSP_PRESENT) {
+ desc->cmd_cfg &= ~CMD_CFG_NO_RESP;
+ if (cmd->flags & MMC_RSP_136)
+ desc->cmd_cfg |= CMD_CFG_RESP_128;
+ desc->cmd_cfg |= CMD_CFG_RESP_NUM;
+ desc->cmd_resp = 0;
+
+ if (!(cmd->flags & MMC_RSP_CRC))
+ desc->cmd_cfg |= CMD_CFG_RESP_NOCRC;
+
+ if (cmd->flags & MMC_RSP_BUSY)
+ desc->cmd_cfg |= CMD_CFG_R1B;
+ } else {
+ desc->cmd_cfg |= CMD_CFG_NO_RESP;
+ }
+
+ /* data? */
+ if (cmd->data) {
+ desc->cmd_cfg |= CMD_CFG_DATA_IO;
+ if (cmd->data->blocks > 1) {
+ desc->cmd_cfg |= CMD_CFG_BLOCK_MODE;
+ desc->cmd_cfg |=
+ (cmd->data->blocks & CMD_CFG_LENGTH_MASK) <<
+ CMD_CFG_LENGTH_SHIFT;
+
+ /* check if block-size matches, if not update */
+ cfg = readl(host->regs + SD_EMMC_CFG);
+ blk_len = cfg & (CFG_BLK_LEN_MASK << CFG_BLK_LEN_SHIFT);
+ blk_len >>= CFG_BLK_LEN_SHIFT;
+ if (blk_len != ilog2(cmd->data->blksz)) {
+ dev_warn(host->dev, "%s: update blk_len %d -> %d\n",
+ __func__, blk_len,
+ ilog2(cmd->data->blksz));
+ blk_len = ilog2(cmd->data->blksz);
+ cfg &= ~(CFG_BLK_LEN_MASK << CFG_BLK_LEN_SHIFT);
+ cfg |= blk_len << CFG_BLK_LEN_SHIFT;
+ writel(cfg, host->regs + SD_EMMC_CFG);
+ }
+ } else {
+ desc->cmd_cfg &= ~CMD_CFG_BLOCK_MODE;
+ desc->cmd_cfg |=
+ (cmd->data->blksz & CMD_CFG_LENGTH_MASK) <<
+ CMD_CFG_LENGTH_SHIFT;
+ }
+
+ cmd->data->bytes_xfered = 0;
+ xfer_bytes = cmd->data->blksz * cmd->data->blocks;
+ if (cmd->data->flags & MMC_DATA_WRITE) {
+ desc->cmd_cfg |= CMD_CFG_DATA_WR;
+ WARN_ON(xfer_bytes > host->bounce_buf_size);
+ sg_copy_to_buffer(cmd->data->sg, cmd->data->sg_len,
+ host->bounce_buf, xfer_bytes);
+ cmd->data->bytes_xfered = xfer_bytes;
+ dma_wmb();
+ } else {
+ desc->cmd_cfg &= ~CMD_CFG_DATA_WR;
+ }
+
+ if (xfer_bytes > 0) {
+ desc->cmd_cfg &= ~CMD_CFG_DATA_NUM;
+ desc->cmd_data = host->bounce_dma_addr & CMD_DATA_MASK;
+ } else {
+ /* write data to data_addr */
+ desc->cmd_cfg |= CMD_CFG_DATA_NUM;
+ desc->cmd_data = 0;
+ }
+
+ cmd_cfg_timeout = 12;
+ } else {
+ desc->cmd_cfg &= ~CMD_CFG_DATA_IO;
+ cmd_cfg_timeout = 10;
+ }
+ desc->cmd_cfg |= (cmd_cfg_timeout & CMD_CFG_TIMEOUT_MASK) <<
+ CMD_CFG_TIMEOUT_SHIFT;
+
+ host->cmd = cmd;
+
+ /* Last descriptor */
+ desc->cmd_cfg |= CMD_CFG_END_OF_CHAIN;
+ writel(desc->cmd_cfg, host->regs + SD_EMMC_CMD_CFG);
+ writel(desc->cmd_data, host->regs + SD_EMMC_CMD_DAT);
+ writel(desc->cmd_resp, host->regs + SD_EMMC_CMD_RSP);
+ wmb(); /* ensure descriptor is written before kicked */
+ writel(desc->cmd_arg, host->regs + SD_EMMC_CMD_ARG);
+}
+
+static void meson_mmc_request(struct mmc_host *mmc, struct mmc_request *mrq)
+{
+ struct meson_host *host = mmc_priv(mmc);
+
+ WARN_ON(host->mrq != NULL);
+
+ /* Stop execution */
+ writel(0, host->regs + SD_EMMC_START);
+
+ /* clear, ack, enable all interrupts */
+ writel(0, host->regs + SD_EMMC_IRQ_EN);
+ writel(IRQ_EN_MASK, host->regs + SD_EMMC_STATUS);
+ writel(IRQ_EN_MASK, host->regs + SD_EMMC_IRQ_EN);
+
+ host->mrq = mrq;
+
+ if (mrq->sbc)
+ meson_mmc_start_cmd(mmc, mrq->sbc);
+ else
+ meson_mmc_start_cmd(mmc, mrq->cmd);
+}
+
+static int meson_mmc_read_resp(struct mmc_host *mmc, struct mmc_command *cmd)
+{
+ struct meson_host *host = mmc_priv(mmc);
+
+ if (cmd->flags & MMC_RSP_136) {
+ cmd->resp[0] = readl(host->regs + SD_EMMC_CMD_RSP3);
+ cmd->resp[1] = readl(host->regs + SD_EMMC_CMD_RSP2);
+ cmd->resp[2] = readl(host->regs + SD_EMMC_CMD_RSP1);
+ cmd->resp[3] = readl(host->regs + SD_EMMC_CMD_RSP);
+ } else if (cmd->flags & MMC_RSP_PRESENT) {
+ cmd->resp[0] = readl(host->regs + SD_EMMC_CMD_RSP);
+ }
+
+ return 0;
+}
+
+static irqreturn_t meson_mmc_irq(int irq, void *dev_id)
+{
+ struct meson_host *host = dev_id;
+ struct mmc_request *mrq;
+ struct mmc_command *cmd = host->cmd;
+ u32 irq_en, status, raw_status;
+ irqreturn_t ret = IRQ_HANDLED;
+
+ if (WARN_ON(!host))
+ return IRQ_NONE;
+
+ mrq = host->mrq;
+
+ if (WARN_ON(!mrq))
+ return IRQ_NONE;
+
+ if (WARN_ON(!cmd))
+ return IRQ_NONE;
+
+ spin_lock(&host->lock);
+ irq_en = readl(host->regs + SD_EMMC_IRQ_EN);
+ raw_status = readl(host->regs + SD_EMMC_STATUS);
+ status = raw_status & irq_en;
+
+ if (!status) {
+ dev_warn(host->dev, "Spurious IRQ! status=0x%08x, irq_en=0x%08x\n",
+ raw_status, irq_en);
+ ret = IRQ_NONE;
+ goto out;
+ }
+
+ cmd->error = 0;
+ if (status & IRQ_RXD_ERR_MASK) {
+ dev_dbg(host->dev, "Unhandled IRQ: RXD error\n");
+ cmd->error = -EILSEQ;
+ }
+ if (status & IRQ_TXD_ERR) {
+ dev_dbg(host->dev, "Unhandled IRQ: TXD error\n");
+ cmd->error = -EILSEQ;
+ }
+ if (status & IRQ_DESC_ERR)
+ dev_dbg(host->dev, "Unhandled IRQ: Descriptor error\n");
+ if (status & IRQ_RESP_ERR) {
+ dev_dbg(host->dev, "Unhandled IRQ: Response error\n");
+ cmd->error = -EILSEQ;
+ }
+ if (status & IRQ_RESP_TIMEOUT) {
+ dev_dbg(host->dev, "Unhandled IRQ: Response timeout\n");
+ cmd->error = -ETIMEDOUT;
+ }
+ if (status & IRQ_DESC_TIMEOUT) {
+ dev_dbg(host->dev, "Unhandled IRQ: Descriptor timeout\n");
+ cmd->error = -ETIMEDOUT;
+ }
+ if (status & IRQ_SDIO)
+ dev_dbg(host->dev, "Unhandled IRQ: SDIO.\n");
+
+ if (status & (IRQ_END_OF_CHAIN | IRQ_RESP_STATUS))
+ ret = IRQ_WAKE_THREAD;
+ else {
+ dev_warn(host->dev, "Unknown IRQ! status=0x%04x: MMC CMD%u arg=0x%08x flags=0x%08x stop=%d\n",
+ status, cmd->opcode, cmd->arg,
+ cmd->flags, mrq->stop ? 1 : 0);
+ if (cmd->data) {
+ struct mmc_data *data = cmd->data;
+
+ dev_warn(host->dev, "\tblksz %u blocks %u flags 0x%08x (%s%s)",
+ data->blksz, data->blocks, data->flags,
+ data->flags & MMC_DATA_WRITE ? "write" : "",
+ data->flags & MMC_DATA_READ ? "read" : "");
+ }
+ }
+
+out:
+ /* ack all (enabled) interrupts */
+ writel(status, host->regs + SD_EMMC_STATUS);
+
+ if (ret == IRQ_HANDLED) {
+ meson_mmc_read_resp(host->mmc, cmd);
+ meson_mmc_request_done(host->mmc, cmd->mrq);
+ }
+
+ spin_unlock(&host->lock);
+ return ret;
+}
+
+static irqreturn_t meson_mmc_irq_thread(int irq, void *dev_id)
+{
+ struct meson_host *host = dev_id;
+ struct mmc_request *mrq = host->mrq;
+ struct mmc_command *cmd = host->cmd;
+ struct mmc_data *data;
+ unsigned int xfer_bytes;
+ int ret = IRQ_HANDLED;
+
+ if (WARN_ON(!mrq))
+ ret = IRQ_NONE;
+
+ if (WARN_ON(!cmd))
+ ret = IRQ_NONE;
+
+ data = cmd->data;
+ if (data) {
+ xfer_bytes = data->blksz * data->blocks;
+ if (data->flags & MMC_DATA_READ) {
+ WARN_ON(xfer_bytes > host->bounce_buf_size);
+ sg_copy_from_buffer(data->sg, data->sg_len,
+ host->bounce_buf, xfer_bytes);
+ data->bytes_xfered = xfer_bytes;
+ }
+ }
+
+ meson_mmc_read_resp(host->mmc, cmd);
+ if (!data || !data->stop || mrq->sbc)
+ meson_mmc_request_done(host->mmc, mrq);
+ else
+ meson_mmc_start_cmd(host->mmc, data->stop);
+
+ return ret;
+}
+
+/*
+ * NOTE: we only need this until the GPIO/pinctrl driver can handle
+ * interrupts. For now, the MMC core will use this for polling.
+ */
+static int meson_mmc_get_cd(struct mmc_host *mmc)
+{
+ int status = mmc_gpio_get_cd(mmc);
+
+ if (status == -ENOSYS)
+ return 1; /* assume present */
+
+ return status;
+}
+
+static const struct mmc_host_ops meson_mmc_ops = {
+ .request = meson_mmc_request,
+ .set_ios = meson_mmc_set_ios,
+ .get_cd = meson_mmc_get_cd,
+};
+
+static int meson_mmc_probe(struct platform_device *pdev)
+{
+ struct resource *res;
+ struct meson_host *host;
+ struct mmc_host *mmc;
+ int ret;
+
+ mmc = mmc_alloc_host(sizeof(struct meson_host), &pdev->dev);
+ if (!mmc)
+ return -ENOMEM;
+ host = mmc_priv(mmc);
+ host->mmc = mmc;
+ host->dev = &pdev->dev;
+ dev_set_drvdata(&pdev->dev, host);
+
+ spin_lock_init(&host->lock);
+
+ /* Get regulators and the supported OCR mask */
+ host->vqmmc_enabled = false;
+ ret = mmc_regulator_get_supply(mmc);
+ if (ret == -EPROBE_DEFER)
+ goto free_host;
+
+ ret = mmc_of_parse(mmc);
+ if (ret) {
+ dev_warn(&pdev->dev, "error parsing DT: %d\n", ret);
+ goto free_host;
+ }
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ host->regs = devm_ioremap_resource(&pdev->dev, res);
+ if (IS_ERR(host->regs)) {
+ ret = PTR_ERR(host->regs);
+ goto free_host;
+ }
+
+ host->irq = platform_get_irq(pdev, 0);
+ if (host->irq == 0) {
+ dev_err(&pdev->dev, "failed to get interrupt resource.\n");
+ ret = -EINVAL;
+ goto free_host;
+ }
+
+ host->core_clk = devm_clk_get(&pdev->dev, "core");
+ if (IS_ERR(host->core_clk)) {
+ ret = PTR_ERR(host->core_clk);
+ goto free_host;
+ }
+
+ ret = clk_prepare_enable(host->core_clk);
+ if (ret)
+ goto free_host;
+
+ ret = meson_mmc_clk_init(host);
+ if (ret)
+ goto free_host;
+
+ /* Stop execution */
+ writel(0, host->regs + SD_EMMC_START);
+
+ /* clear, ack, enable all interrupts */
+ writel(0, host->regs + SD_EMMC_IRQ_EN);
+ writel(IRQ_EN_MASK, host->regs + SD_EMMC_STATUS);
+
+ ret = devm_request_threaded_irq(&pdev->dev, host->irq,
+ meson_mmc_irq, meson_mmc_irq_thread,
+ IRQF_SHARED, DRIVER_NAME, host);
+ if (ret)
+ goto free_host;
+
+ /* data bounce buffer */
+ host->bounce_buf_size = SZ_512K;
+ host->bounce_buf =
+ dma_alloc_coherent(host->dev, host->bounce_buf_size,
+ &host->bounce_dma_addr, GFP_KERNEL);
+ if (host->bounce_buf == NULL) {
+ dev_err(host->dev, "Unable to map allocate DMA bounce buffer.\n");
+ ret = -ENOMEM;
+ goto free_host;
+ }
+
+ mmc->ops = &meson_mmc_ops;
+ mmc_add_host(mmc);
+
+ return 0;
+
+free_host:
+ clk_disable_unprepare(host->cfg_div_clk);
+ clk_disable_unprepare(host->core_clk);
+ mmc_free_host(mmc);
+ return ret;
+}
+
+static int meson_mmc_remove(struct platform_device *pdev)
+{
+ struct meson_host *host = dev_get_drvdata(&pdev->dev);
+
+ if (WARN_ON(!host))
+ return 0;
+
+ if (host->bounce_buf)
+ dma_free_coherent(host->dev, host->bounce_buf_size,
+ host->bounce_buf, host->bounce_dma_addr);
+
+ clk_disable_unprepare(host->cfg_div_clk);
+ clk_disable_unprepare(host->core_clk);
+
+ mmc_free_host(host->mmc);
+ return 0;
+}
+
+static const struct of_device_id meson_mmc_of_match[] = {
+ { .compatible = "amlogic,meson-gx-mmc", },
+ { .compatible = "amlogic,meson-gxbb-mmc", },
+ { .compatible = "amlogic,meson-gxl-mmc", },
+ { .compatible = "amlogic,meson-gxm-mmc", },
+ {}
+};
+MODULE_DEVICE_TABLE(of, meson_mmc_of_match);
+
+static struct platform_driver meson_mmc_driver = {
+ .probe = meson_mmc_probe,
+ .remove = meson_mmc_remove,
+ .driver = {
+ .name = DRIVER_NAME,
+ .of_match_table = of_match_ptr(meson_mmc_of_match),
+ },
+};
+
+module_platform_driver(meson_mmc_driver);
+
+MODULE_DESCRIPTION("Amlogic S905*/GX* SD/eMMC driver");
+MODULE_AUTHOR("Kevin Hilman <khilman@baylibre.com>");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/mmc/host/mmci.c b/drivers/mmc/host/mmci.c
index df990bb8c873..6af3ee7b452a 100644
--- a/drivers/mmc/host/mmci.c
+++ b/drivers/mmc/host/mmci.c
@@ -71,7 +71,12 @@ static unsigned int fmax = 515633;
* @f_max: maximum clk frequency supported by the controller.
* @signal_direction: input/out direction of bus signals can be indicated
* @pwrreg_clkgate: MMCIPOWER register must be used to gate the clock
- * @busy_detect: true if busy detection on dat0 is supported
+ * @busy_detect: true if the variant supports busy detection on DAT0.
+ * @busy_dpsm_flag: bitmask enabling busy detection in the DPSM
+ * @busy_detect_flag: bitmask identifying the bit in the MMCISTATUS register
+ * indicating that the card is busy
+ * @busy_detect_mask: bitmask identifying the bit in the MMCIMASK0 to mask for
+ * getting busy end detection interrupts
* @pwrreg_nopower: bits in MMCIPOWER don't controls ext. power supply
* @explicit_mclk_control: enable explicit mclk control in driver.
* @qcom_fifo: enables qcom specific fifo pio read logic.
@@ -98,6 +103,9 @@ struct variant_data {
bool signal_direction;
bool pwrreg_clkgate;
bool busy_detect;
+ u32 busy_dpsm_flag;
+ u32 busy_detect_flag;
+ u32 busy_detect_mask;
bool pwrreg_nopower;
bool explicit_mclk_control;
bool qcom_fifo;
@@ -137,7 +145,7 @@ static struct variant_data variant_u300 = {
.clkreg_enable = MCI_ST_U300_HWFCEN,
.clkreg_8bit_bus_enable = MCI_ST_8BIT_BUS,
.datalength_bits = 16,
- .datactrl_mask_sdio = MCI_ST_DPSM_SDIOEN,
+ .datactrl_mask_sdio = MCI_DPSM_ST_SDIOEN,
.st_sdio = true,
.pwrreg_powerup = MCI_PWR_ON,
.f_max = 100000000,
@@ -152,7 +160,7 @@ static struct variant_data variant_nomadik = {
.clkreg = MCI_CLK_ENABLE,
.clkreg_8bit_bus_enable = MCI_ST_8BIT_BUS,
.datalength_bits = 24,
- .datactrl_mask_sdio = MCI_ST_DPSM_SDIOEN,
+ .datactrl_mask_sdio = MCI_DPSM_ST_SDIOEN,
.st_sdio = true,
.st_clkdiv = true,
.pwrreg_powerup = MCI_PWR_ON,
@@ -170,7 +178,7 @@ static struct variant_data variant_ux500 = {
.clkreg_8bit_bus_enable = MCI_ST_8BIT_BUS,
.clkreg_neg_edge_enable = MCI_ST_UX500_NEG_EDGE,
.datalength_bits = 24,
- .datactrl_mask_sdio = MCI_ST_DPSM_SDIOEN,
+ .datactrl_mask_sdio = MCI_DPSM_ST_SDIOEN,
.st_sdio = true,
.st_clkdiv = true,
.pwrreg_powerup = MCI_PWR_ON,
@@ -178,6 +186,9 @@ static struct variant_data variant_ux500 = {
.signal_direction = true,
.pwrreg_clkgate = true,
.busy_detect = true,
+ .busy_dpsm_flag = MCI_DPSM_ST_BUSYMODE,
+ .busy_detect_flag = MCI_ST_CARDBUSY,
+ .busy_detect_mask = MCI_ST_BUSYENDMASK,
.pwrreg_nopower = true,
};
@@ -188,9 +199,9 @@ static struct variant_data variant_ux500v2 = {
.clkreg_enable = MCI_ST_UX500_HWFCEN,
.clkreg_8bit_bus_enable = MCI_ST_8BIT_BUS,
.clkreg_neg_edge_enable = MCI_ST_UX500_NEG_EDGE,
- .datactrl_mask_ddrmode = MCI_ST_DPSM_DDRMODE,
+ .datactrl_mask_ddrmode = MCI_DPSM_ST_DDRMODE,
.datalength_bits = 24,
- .datactrl_mask_sdio = MCI_ST_DPSM_SDIOEN,
+ .datactrl_mask_sdio = MCI_DPSM_ST_SDIOEN,
.st_sdio = true,
.st_clkdiv = true,
.blksz_datactrl16 = true,
@@ -199,6 +210,9 @@ static struct variant_data variant_ux500v2 = {
.signal_direction = true,
.pwrreg_clkgate = true,
.busy_detect = true,
+ .busy_dpsm_flag = MCI_DPSM_ST_BUSYMODE,
+ .busy_detect_flag = MCI_ST_CARDBUSY,
+ .busy_detect_mask = MCI_ST_BUSYENDMASK,
.pwrreg_nopower = true,
};
@@ -210,7 +224,7 @@ static struct variant_data variant_qcom = {
MCI_QCOM_CLK_SELECT_IN_FBCLK,
.clkreg_8bit_bus_enable = MCI_QCOM_CLK_WIDEBUS_8,
.datactrl_mask_ddrmode = MCI_QCOM_CLK_SELECT_IN_DDR_MODE,
- .data_cmd_enable = MCI_QCOM_CSPM_DATCMD,
+ .data_cmd_enable = MCI_CPSM_QCOM_DATCMD,
.blksz_datactrl4 = true,
.datalength_bits = 24,
.pwrreg_powerup = MCI_PWR_UP,
@@ -220,6 +234,7 @@ static struct variant_data variant_qcom = {
.qcom_dml = true,
};
+/* Busy detection for the ST Micro variant */
static int mmci_card_busy(struct mmc_host *mmc)
{
struct mmci_host *host = mmc_priv(mmc);
@@ -227,7 +242,7 @@ static int mmci_card_busy(struct mmc_host *mmc)
int busy = 0;
spin_lock_irqsave(&host->lock, flags);
- if (readl(host->base + MMCISTATUS) & MCI_ST_CARDBUSY)
+ if (readl(host->base + MMCISTATUS) & host->variant->busy_detect_flag)
busy = 1;
spin_unlock_irqrestore(&host->lock, flags);
@@ -294,8 +309,8 @@ static void mmci_write_pwrreg(struct mmci_host *host, u32 pwr)
*/
static void mmci_write_datactrlreg(struct mmci_host *host, u32 datactrl)
{
- /* Keep ST Micro busy mode if enabled */
- datactrl |= host->datactrl_reg & MCI_ST_DPSM_BUSYMODE;
+ /* Keep busy mode in DPSM if enabled */
+ datactrl |= host->datactrl_reg & host->variant->busy_dpsm_flag;
if (host->datactrl_reg != datactrl) {
host->datactrl_reg = datactrl;
@@ -973,37 +988,66 @@ mmci_cmd_irq(struct mmci_host *host, struct mmc_command *cmd,
unsigned int status)
{
void __iomem *base = host->base;
- bool sbc, busy_resp;
+ bool sbc;
if (!cmd)
return;
sbc = (cmd == host->mrq->sbc);
- busy_resp = host->variant->busy_detect && (cmd->flags & MMC_RSP_BUSY);
- if (!((status|host->busy_status) & (MCI_CMDCRCFAIL|MCI_CMDTIMEOUT|
- MCI_CMDSENT|MCI_CMDRESPEND)))
+ /*
+ * We need to be one of these interrupts to be considered worth
+ * handling. Note that we tag on any latent IRQs postponed
+ * due to waiting for busy status.
+ */
+ if (!((status|host->busy_status) &
+ (MCI_CMDCRCFAIL|MCI_CMDTIMEOUT|MCI_CMDSENT|MCI_CMDRESPEND)))
return;
- /* Check if we need to wait for busy completion. */
- if (host->busy_status && (status & MCI_ST_CARDBUSY))
- return;
+ /*
+ * ST Micro variant: handle busy detection.
+ */
+ if (host->variant->busy_detect) {
+ bool busy_resp = !!(cmd->flags & MMC_RSP_BUSY);
- /* 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;
- }
+ /* We are busy with a command, return */
+ if (host->busy_status &&
+ (status & host->variant->busy_detect_flag))
+ return;
+
+ /*
+ * We were not busy, but we now got a busy response on
+ * something that was not an error, and we double-check
+ * that the special busy status bit is still set before
+ * proceeding.
+ */
+ if (!host->busy_status && busy_resp &&
+ !(status & (MCI_CMDCRCFAIL|MCI_CMDTIMEOUT)) &&
+ (readl(base + MMCISTATUS) & host->variant->busy_detect_flag)) {
+ /* Unmask the busy IRQ */
+ writel(readl(base + MMCIMASK0) |
+ host->variant->busy_detect_mask,
+ base + MMCIMASK0);
+ /*
+ * Now cache the last response status code (until
+ * the busy bit goes low), and return.
+ */
+ 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;
+ /*
+ * At this point we are not busy with a command, we have
+ * not received a new busy request, mask the busy IRQ and
+ * fall through to process the IRQ.
+ */
+ if (host->busy_status) {
+ writel(readl(base + MMCIMASK0) &
+ ~host->variant->busy_detect_mask,
+ base + MMCIMASK0);
+ host->busy_status = 0;
+ }
}
host->cmd = NULL;
@@ -1257,9 +1301,11 @@ static irqreturn_t mmci_irq(int irq, void *dev_id)
mmci_data_irq(host, host->data, status);
}
- /* Don't poll for busy completion in irq context. */
- if (host->busy_status)
- status &= ~MCI_ST_CARDBUSY;
+ /*
+ * Don't poll for busy completion in irq context.
+ */
+ if (host->variant->busy_detect && host->busy_status)
+ status &= ~host->variant->busy_detect_flag;
ret = 1;
} while (status);
@@ -1612,9 +1658,18 @@ static int mmci_probe(struct amba_device *dev,
/* We support these capabilities. */
mmc->caps |= MMC_CAP_CMD23;
+ /*
+ * Enable busy detection.
+ */
if (variant->busy_detect) {
mmci_ops.card_busy = mmci_card_busy;
- mmci_write_datactrlreg(host, MCI_ST_DPSM_BUSYMODE);
+ /*
+ * Not all variants have a flag to enable busy detection
+ * in the DPSM, but if they do, set it here.
+ */
+ if (variant->busy_dpsm_flag)
+ mmci_write_datactrlreg(host,
+ host->variant->busy_dpsm_flag);
mmc->caps |= MMC_CAP_WAIT_WHILE_BUSY;
mmc->max_busy_timeout = 0;
}
diff --git a/drivers/mmc/host/mmci.h b/drivers/mmc/host/mmci.h
index a1f5e4f49e2a..56322c6afba4 100644
--- a/drivers/mmc/host/mmci.h
+++ b/drivers/mmc/host/mmci.h
@@ -51,25 +51,27 @@
#define MCI_QCOM_CLK_SELECT_IN_DDR_MODE (BIT(14) | BIT(15))
#define MMCIARGUMENT 0x008
-#define MMCICOMMAND 0x00c
-#define MCI_CPSM_RESPONSE (1 << 6)
-#define MCI_CPSM_LONGRSP (1 << 7)
-#define MCI_CPSM_INTERRUPT (1 << 8)
-#define MCI_CPSM_PENDING (1 << 9)
-#define MCI_CPSM_ENABLE (1 << 10)
-/* Argument flag extenstions in the ST Micro versions */
-#define MCI_ST_SDIO_SUSP (1 << 11)
-#define MCI_ST_ENCMD_COMPL (1 << 12)
-#define MCI_ST_NIEN (1 << 13)
-#define MCI_ST_CE_ATACMD (1 << 14)
-/* Modified on Qualcomm Integrations */
-#define MCI_QCOM_CSPM_DATCMD BIT(12)
-#define MCI_QCOM_CSPM_MCIABORT BIT(13)
-#define MCI_QCOM_CSPM_CCSENABLE BIT(14)
-#define MCI_QCOM_CSPM_CCSDISABLE BIT(15)
-#define MCI_QCOM_CSPM_AUTO_CMD19 BIT(16)
-#define MCI_QCOM_CSPM_AUTO_CMD21 BIT(21)
+/* The command register controls the Command Path State Machine (CPSM) */
+#define MMCICOMMAND 0x00c
+#define MCI_CPSM_RESPONSE BIT(6)
+#define MCI_CPSM_LONGRSP BIT(7)
+#define MCI_CPSM_INTERRUPT BIT(8)
+#define MCI_CPSM_PENDING BIT(9)
+#define MCI_CPSM_ENABLE BIT(10)
+/* Command register flag extenstions in the ST Micro versions */
+#define MCI_CPSM_ST_SDIO_SUSP BIT(11)
+#define MCI_CPSM_ST_ENCMD_COMPL BIT(12)
+#define MCI_CPSM_ST_NIEN BIT(13)
+#define MCI_CPSM_ST_CE_ATACMD BIT(14)
+/* Command register flag extensions in the Qualcomm versions */
+#define MCI_CPSM_QCOM_PROGENA BIT(11)
+#define MCI_CPSM_QCOM_DATCMD BIT(12)
+#define MCI_CPSM_QCOM_MCIABORT BIT(13)
+#define MCI_CPSM_QCOM_CCSENABLE BIT(14)
+#define MCI_CPSM_QCOM_CCSDISABLE BIT(15)
+#define MCI_CPSM_QCOM_AUTO_CMD19 BIT(16)
+#define MCI_CPSM_QCOM_AUTO_CMD21 BIT(21)
#define MMCIRESPCMD 0x010
#define MMCIRESPONSE0 0x014
@@ -78,22 +80,27 @@
#define MMCIRESPONSE3 0x020
#define MMCIDATATIMER 0x024
#define MMCIDATALENGTH 0x028
+
+/* The data control register controls the Data Path State Machine (DPSM) */
#define MMCIDATACTRL 0x02c
-#define MCI_DPSM_ENABLE (1 << 0)
-#define MCI_DPSM_DIRECTION (1 << 1)
-#define MCI_DPSM_MODE (1 << 2)
-#define MCI_DPSM_DMAENABLE (1 << 3)
-#define MCI_DPSM_BLOCKSIZE (1 << 4)
+#define MCI_DPSM_ENABLE BIT(0)
+#define MCI_DPSM_DIRECTION BIT(1)
+#define MCI_DPSM_MODE BIT(2)
+#define MCI_DPSM_DMAENABLE BIT(3)
+#define MCI_DPSM_BLOCKSIZE BIT(4)
/* Control register extensions in the ST Micro U300 and Ux500 versions */
-#define MCI_ST_DPSM_RWSTART (1 << 8)
-#define MCI_ST_DPSM_RWSTOP (1 << 9)
-#define MCI_ST_DPSM_RWMOD (1 << 10)
-#define MCI_ST_DPSM_SDIOEN (1 << 11)
+#define MCI_DPSM_ST_RWSTART BIT(8)
+#define MCI_DPSM_ST_RWSTOP BIT(9)
+#define MCI_DPSM_ST_RWMOD BIT(10)
+#define MCI_DPSM_ST_SDIOEN BIT(11)
/* Control register extensions in the ST Micro Ux500 versions */
-#define MCI_ST_DPSM_DMAREQCTL (1 << 12)
-#define MCI_ST_DPSM_DBOOTMODEEN (1 << 13)
-#define MCI_ST_DPSM_BUSYMODE (1 << 14)
-#define MCI_ST_DPSM_DDRMODE (1 << 15)
+#define MCI_DPSM_ST_DMAREQCTL BIT(12)
+#define MCI_DPSM_ST_DBOOTMODEEN BIT(13)
+#define MCI_DPSM_ST_BUSYMODE BIT(14)
+#define MCI_DPSM_ST_DDRMODE BIT(15)
+/* Control register extensions in the Qualcomm versions */
+#define MCI_DPSM_QCOM_DATA_PEND BIT(17)
+#define MCI_DPSM_QCOM_RX_DATA_PEND BIT(20)
#define MMCIDATACNT 0x030
#define MMCISTATUS 0x034
@@ -167,7 +174,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 MCI_ST_BUSYENDMASK (1 << 24)
#define MMCIMASK1 0x040
#define MMCIFIFOCNT 0x048
diff --git a/drivers/mmc/host/mtk-sd.c b/drivers/mmc/host/mtk-sd.c
index 84e9afcb5c09..86af0b199a54 100644
--- a/drivers/mmc/host/mtk-sd.c
+++ b/drivers/mmc/host/mtk-sd.c
@@ -1713,6 +1713,7 @@ static const struct of_device_id msdc_of_ids[] = {
{ .compatible = "mediatek,mt8135-mmc", },
{}
};
+MODULE_DEVICE_TABLE(of, msdc_of_ids);
static struct platform_driver mt_msdc_driver = {
.probe = msdc_drv_probe,
diff --git a/drivers/mmc/host/rtsx_usb_sdmmc.c b/drivers/mmc/host/rtsx_usb_sdmmc.c
index 6e9c0f8fddb1..dc1abd14acbc 100644
--- a/drivers/mmc/host/rtsx_usb_sdmmc.c
+++ b/drivers/mmc/host/rtsx_usb_sdmmc.c
@@ -1374,6 +1374,8 @@ static int rtsx_usb_sdmmc_drv_probe(struct platform_device *pdev)
mutex_init(&host->host_mutex);
rtsx_usb_init_host(host);
+ pm_runtime_use_autosuspend(&pdev->dev);
+ pm_runtime_set_autosuspend_delay(&pdev->dev, 50);
pm_runtime_enable(&pdev->dev);
#ifdef RTSX_USB_USE_LEDS_CLASS
@@ -1428,6 +1430,7 @@ static int rtsx_usb_sdmmc_drv_remove(struct platform_device *pdev)
mmc_free_host(mmc);
pm_runtime_disable(&pdev->dev);
+ pm_runtime_dont_use_autosuspend(&pdev->dev);
platform_set_drvdata(pdev, NULL);
dev_dbg(&(pdev->dev),
diff --git a/drivers/mmc/host/s3cmci.c b/drivers/mmc/host/s3cmci.c
index c531deef3258..932a4b1fed33 100644
--- a/drivers/mmc/host/s3cmci.c
+++ b/drivers/mmc/host/s3cmci.c
@@ -28,7 +28,6 @@
#include <mach/dma.h>
#include <mach/gpio-samsung.h>
-#include <linux/platform_data/dma-s3c24xx.h>
#include <linux/platform_data/mmc-s3cmci.h>
#include "s3cmci.h"
@@ -1682,19 +1681,13 @@ static int s3cmci_probe(struct platform_device *pdev)
gpio_direction_input(host->pdata->gpio_wprotect);
}
- /* depending on the dma state, get a dma channel to use. */
+ /* Depending on the dma state, get a DMA channel to use. */
if (s3cmci_host_usedma(host)) {
- dma_cap_mask_t mask;
-
- dma_cap_zero(mask);
- dma_cap_set(DMA_SLAVE, mask);
-
- host->dma = dma_request_slave_channel_compat(mask,
- s3c24xx_dma_filter, (void *)DMACH_SDI, &pdev->dev, "rx-tx");
- if (!host->dma) {
+ host->dma = dma_request_chan(&pdev->dev, "rx-tx");
+ ret = PTR_ERR_OR_ZERO(host->dma);
+ if (ret) {
dev_err(&pdev->dev, "cannot get DMA channel.\n");
- ret = -EBUSY;
goto probe_free_gpio_wp;
}
}
diff --git a/drivers/mmc/host/sdhci-iproc.c b/drivers/mmc/host/sdhci-iproc.c
index 726246665850..d7046d67415a 100644
--- a/drivers/mmc/host/sdhci-iproc.c
+++ b/drivers/mmc/host/sdhci-iproc.c
@@ -143,6 +143,14 @@ static void sdhci_iproc_writeb(struct sdhci_host *host, u8 val, int reg)
}
static const struct sdhci_ops sdhci_iproc_ops = {
+ .set_clock = sdhci_set_clock,
+ .get_max_clock = sdhci_pltfm_clk_get_max_clock,
+ .set_bus_width = sdhci_set_bus_width,
+ .reset = sdhci_reset,
+ .set_uhs_signaling = sdhci_set_uhs_signaling,
+};
+
+static const struct sdhci_ops sdhci_iproc_32only_ops = {
.read_l = sdhci_iproc_readl,
.read_w = sdhci_iproc_readw,
.read_b = sdhci_iproc_readb,
@@ -156,6 +164,28 @@ static const struct sdhci_ops sdhci_iproc_ops = {
.set_uhs_signaling = sdhci_set_uhs_signaling,
};
+static const struct sdhci_pltfm_data sdhci_iproc_cygnus_pltfm_data = {
+ .quirks = SDHCI_QUIRK_DATA_TIMEOUT_USES_SDCLK,
+ .quirks2 = SDHCI_QUIRK2_ACMD23_BROKEN,
+ .ops = &sdhci_iproc_32only_ops,
+};
+
+static const struct sdhci_iproc_data iproc_cygnus_data = {
+ .pdata = &sdhci_iproc_cygnus_pltfm_data,
+ .caps = ((0x1 << SDHCI_MAX_BLOCK_SHIFT)
+ & SDHCI_MAX_BLOCK_MASK) |
+ SDHCI_CAN_VDD_330 |
+ SDHCI_CAN_VDD_180 |
+ SDHCI_CAN_DO_SUSPEND |
+ SDHCI_CAN_DO_HISPD |
+ SDHCI_CAN_DO_ADMA2 |
+ SDHCI_CAN_DO_SDMA,
+ .caps1 = SDHCI_DRIVER_TYPE_C |
+ SDHCI_DRIVER_TYPE_D |
+ SDHCI_SUPPORT_DDR50,
+ .mmc_caps = MMC_CAP_1_8V_DDR,
+};
+
static const struct sdhci_pltfm_data sdhci_iproc_pltfm_data = {
.quirks = SDHCI_QUIRK_DATA_TIMEOUT_USES_SDCLK,
.quirks2 = SDHCI_QUIRK2_ACMD23_BROKEN,
@@ -182,7 +212,7 @@ static const struct sdhci_pltfm_data sdhci_bcm2835_pltfm_data = {
.quirks = SDHCI_QUIRK_BROKEN_CARD_DETECTION |
SDHCI_QUIRK_DATA_TIMEOUT_USES_SDCLK |
SDHCI_QUIRK_MISSING_CAPS,
- .ops = &sdhci_iproc_ops,
+ .ops = &sdhci_iproc_32only_ops,
};
static const struct sdhci_iproc_data bcm2835_data = {
@@ -194,7 +224,8 @@ static const struct sdhci_iproc_data bcm2835_data = {
static const struct of_device_id sdhci_iproc_of_match[] = {
{ .compatible = "brcm,bcm2835-sdhci", .data = &bcm2835_data },
- { .compatible = "brcm,sdhci-iproc-cygnus", .data = &iproc_data },
+ { .compatible = "brcm,sdhci-iproc-cygnus", .data = &iproc_cygnus_data},
+ { .compatible = "brcm,sdhci-iproc", .data = &iproc_data },
{ }
};
MODULE_DEVICE_TABLE(of, sdhci_iproc_of_match);
diff --git a/drivers/mmc/host/sdhci-msm.c b/drivers/mmc/host/sdhci-msm.c
index 90ed2e12d345..b78d72f28864 100644
--- a/drivers/mmc/host/sdhci-msm.c
+++ b/drivers/mmc/host/sdhci-msm.c
@@ -18,6 +18,7 @@
#include <linux/of_device.h>
#include <linux/delay.h>
#include <linux/mmc/mmc.h>
+#include <linux/pm_runtime.h>
#include <linux/slab.h>
#include "sdhci-pltfm.h"
@@ -68,6 +69,7 @@
#define CMUX_SHIFT_PHASE_SHIFT 24
#define CMUX_SHIFT_PHASE_MASK (7 << CMUX_SHIFT_PHASE_SHIFT)
+#define MSM_MMC_AUTOSUSPEND_DELAY_MS 50
struct sdhci_msm_host {
struct platform_device *pdev;
void __iomem *core_mem; /* MSM SDCC mapped address */
@@ -659,12 +661,26 @@ static int sdhci_msm_probe(struct platform_device *pdev)
goto clk_disable;
}
+ pm_runtime_get_noresume(&pdev->dev);
+ pm_runtime_set_active(&pdev->dev);
+ pm_runtime_enable(&pdev->dev);
+ pm_runtime_set_autosuspend_delay(&pdev->dev,
+ MSM_MMC_AUTOSUSPEND_DELAY_MS);
+ pm_runtime_use_autosuspend(&pdev->dev);
+
ret = sdhci_add_host(host);
if (ret)
- goto clk_disable;
+ goto pm_runtime_disable;
+
+ pm_runtime_mark_last_busy(&pdev->dev);
+ pm_runtime_put_autosuspend(&pdev->dev);
return 0;
+pm_runtime_disable:
+ pm_runtime_disable(&pdev->dev);
+ pm_runtime_set_suspended(&pdev->dev);
+ pm_runtime_put_noidle(&pdev->dev);
clk_disable:
clk_disable_unprepare(msm_host->clk);
pclk_disable:
@@ -686,6 +702,11 @@ static int sdhci_msm_remove(struct platform_device *pdev)
0xffffffff);
sdhci_remove_host(host, dead);
+
+ pm_runtime_get_sync(&pdev->dev);
+ pm_runtime_disable(&pdev->dev);
+ pm_runtime_put_noidle(&pdev->dev);
+
clk_disable_unprepare(msm_host->clk);
clk_disable_unprepare(msm_host->pclk);
if (!IS_ERR(msm_host->bus_clk))
@@ -694,12 +715,57 @@ static int sdhci_msm_remove(struct platform_device *pdev)
return 0;
}
+#ifdef CONFIG_PM
+static int sdhci_msm_runtime_suspend(struct device *dev)
+{
+ struct sdhci_host *host = dev_get_drvdata(dev);
+ struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
+ struct sdhci_msm_host *msm_host = sdhci_pltfm_priv(pltfm_host);
+
+ clk_disable_unprepare(msm_host->clk);
+ clk_disable_unprepare(msm_host->pclk);
+
+ return 0;
+}
+
+static int sdhci_msm_runtime_resume(struct device *dev)
+{
+ struct sdhci_host *host = dev_get_drvdata(dev);
+ struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
+ struct sdhci_msm_host *msm_host = sdhci_pltfm_priv(pltfm_host);
+ int ret;
+
+ ret = clk_prepare_enable(msm_host->clk);
+ if (ret) {
+ dev_err(dev, "clk_enable failed for core_clk: %d\n", ret);
+ return ret;
+ }
+ ret = clk_prepare_enable(msm_host->pclk);
+ if (ret) {
+ dev_err(dev, "clk_enable failed for iface_clk: %d\n", ret);
+ clk_disable_unprepare(msm_host->clk);
+ return ret;
+ }
+
+ return 0;
+}
+#endif
+
+static const struct dev_pm_ops sdhci_msm_pm_ops = {
+ SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend,
+ pm_runtime_force_resume)
+ SET_RUNTIME_PM_OPS(sdhci_msm_runtime_suspend,
+ sdhci_msm_runtime_resume,
+ NULL)
+};
+
static struct platform_driver sdhci_msm_driver = {
.probe = sdhci_msm_probe,
.remove = sdhci_msm_remove,
.driver = {
.name = "sdhci_msm",
.of_match_table = sdhci_msm_dt_match,
+ .pm = &sdhci_msm_pm_ops,
},
};
diff --git a/drivers/mmc/host/sdhci-of-at91.c b/drivers/mmc/host/sdhci-of-at91.c
index a9b7fc06c434..2f9ad213377a 100644
--- a/drivers/mmc/host/sdhci-of-at91.c
+++ b/drivers/mmc/host/sdhci-of-at91.c
@@ -100,6 +100,7 @@ static const struct of_device_id sdhci_at91_dt_match[] = {
{ .compatible = "atmel,sama5d2-sdhci", .data = &soc_data_sama5d2 },
{}
};
+MODULE_DEVICE_TABLE(of, sdhci_at91_dt_match);
#ifdef CONFIG_PM
static int sdhci_at91_runtime_suspend(struct device *dev)
diff --git a/drivers/mmc/host/sdhci-of-esdhc.c b/drivers/mmc/host/sdhci-of-esdhc.c
index fb71c866eacc..9a6eb4492172 100644
--- a/drivers/mmc/host/sdhci-of-esdhc.c
+++ b/drivers/mmc/host/sdhci-of-esdhc.c
@@ -18,6 +18,7 @@
#include <linux/of.h>
#include <linux/delay.h>
#include <linux/module.h>
+#include <linux/sys_soc.h>
#include <linux/mmc/host.h>
#include "sdhci-pltfm.h"
#include "sdhci-esdhc.h"
@@ -28,6 +29,7 @@
struct sdhci_esdhc {
u8 vendor_ver;
u8 spec_ver;
+ bool quirk_incorrect_hostver;
};
/**
@@ -66,6 +68,20 @@ static u32 esdhc_readl_fixup(struct sdhci_host *host,
return ret;
}
}
+ /*
+ * The DAT[3:0] line signal levels and the CMD line signal level are
+ * not compatible with standard SDHC register. The line signal levels
+ * DAT[7:0] are at bits 31:24 and the command line signal level is at
+ * bit 23. All other bits are the same as in the standard SDHC
+ * register.
+ */
+ if (spec_reg == SDHCI_PRESENT_STATE) {
+ ret = value & 0x000fffff;
+ ret |= (value >> 4) & SDHCI_DATA_LVL_MASK;
+ ret |= (value << 1) & SDHCI_CMD_LVL;
+ return ret;
+ }
+
ret = value;
return ret;
}
@@ -73,6 +89,8 @@ static u32 esdhc_readl_fixup(struct sdhci_host *host,
static u16 esdhc_readw_fixup(struct sdhci_host *host,
int spec_reg, u32 value)
{
+ struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
+ struct sdhci_esdhc *esdhc = sdhci_pltfm_priv(pltfm_host);
u16 ret;
int shift = (spec_reg & 0x2) * 8;
@@ -80,6 +98,12 @@ static u16 esdhc_readw_fixup(struct sdhci_host *host,
ret = value & 0xffff;
else
ret = (value >> shift) & 0xffff;
+ /* Workaround for T4240-R1.0-R2.0 eSDHC which has incorrect
+ * vendor version and spec version information.
+ */
+ if ((spec_reg == SDHCI_HOST_VERSION) &&
+ (esdhc->quirk_incorrect_hostver))
+ ret = (VENDOR_V_23 << SDHCI_VENDOR_VER_SHIFT) | SDHCI_SPEC_200;
return ret;
}
@@ -558,6 +582,12 @@ static const struct sdhci_pltfm_data sdhci_esdhc_le_pdata = {
.ops = &sdhci_esdhc_le_ops,
};
+static struct soc_device_attribute soc_incorrect_hostver[] = {
+ { .family = "QorIQ T4240", .revision = "1.0", },
+ { .family = "QorIQ T4240", .revision = "2.0", },
+ { },
+};
+
static void esdhc_init(struct platform_device *pdev, struct sdhci_host *host)
{
struct sdhci_pltfm_host *pltfm_host;
@@ -571,6 +601,10 @@ static void esdhc_init(struct platform_device *pdev, struct sdhci_host *host)
esdhc->vendor_ver = (host_ver & SDHCI_VENDOR_VER_MASK) >>
SDHCI_VENDOR_VER_SHIFT;
esdhc->spec_ver = host_ver & SDHCI_SPEC_VER_MASK;
+ if (soc_device_match(soc_incorrect_hostver))
+ esdhc->quirk_incorrect_hostver = true;
+ else
+ esdhc->quirk_incorrect_hostver = false;
}
static int sdhci_esdhc_probe(struct platform_device *pdev)
diff --git a/drivers/mmc/host/sdhci-pltfm.h b/drivers/mmc/host/sdhci-pltfm.h
index 3280f2077959..957839d0fe37 100644
--- a/drivers/mmc/host/sdhci-pltfm.h
+++ b/drivers/mmc/host/sdhci-pltfm.h
@@ -106,7 +106,7 @@ extern unsigned int sdhci_pltfm_clk_get_max_clock(struct sdhci_host *host);
static inline void *sdhci_pltfm_priv(struct sdhci_pltfm_host *host)
{
- return (void *)host->private;
+ return host->private;
}
extern const struct dev_pm_ops sdhci_pltfm_pmops;
diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c
index 42ef3ebb1d8c..d08d50724619 100644
--- a/drivers/mmc/host/sdhci.c
+++ b/drivers/mmc/host/sdhci.c
@@ -22,6 +22,7 @@
#include <linux/scatterlist.h>
#include <linux/regulator/consumer.h>
#include <linux/pm_runtime.h>
+#include <linux/of.h>
#include <linux/leds.h>
@@ -1623,7 +1624,14 @@ static void sdhci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
ctrl = sdhci_readb(host, SDHCI_HOST_CONTROL);
if ((ios->timing == MMC_TIMING_SD_HS ||
- ios->timing == MMC_TIMING_MMC_HS)
+ ios->timing == MMC_TIMING_MMC_HS ||
+ ios->timing == MMC_TIMING_MMC_HS400 ||
+ ios->timing == MMC_TIMING_MMC_HS200 ||
+ ios->timing == MMC_TIMING_MMC_DDR52 ||
+ ios->timing == MMC_TIMING_UHS_SDR50 ||
+ ios->timing == MMC_TIMING_UHS_SDR104 ||
+ ios->timing == MMC_TIMING_UHS_DDR50 ||
+ ios->timing == MMC_TIMING_UHS_SDR25)
&& !(host->quirks & SDHCI_QUIRK_NO_HISPD_BIT))
ctrl |= SDHCI_CTRL_HISPD;
else
@@ -1632,16 +1640,6 @@ static void sdhci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
if (host->version >= SDHCI_SPEC_300) {
u16 clk, ctrl_2;
- /* In case of UHS-I modes, set High Speed Enable */
- if ((ios->timing == MMC_TIMING_MMC_HS400) ||
- (ios->timing == MMC_TIMING_MMC_HS200) ||
- (ios->timing == MMC_TIMING_MMC_DDR52) ||
- (ios->timing == MMC_TIMING_UHS_SDR50) ||
- (ios->timing == MMC_TIMING_UHS_SDR104) ||
- (ios->timing == MMC_TIMING_UHS_DDR50) ||
- (ios->timing == MMC_TIMING_UHS_SDR25))
- ctrl |= SDHCI_CTRL_HISPD;
-
if (!host->preset_enabled) {
sdhci_writeb(host, ctrl, SDHCI_HOST_CONTROL);
/*
@@ -3010,6 +3008,8 @@ static int sdhci_set_dma_mask(struct sdhci_host *host)
void __sdhci_read_caps(struct sdhci_host *host, u16 *ver, u32 *caps, u32 *caps1)
{
u16 v;
+ u64 dt_caps_mask = 0;
+ u64 dt_caps = 0;
if (host->read_caps)
return;
@@ -3024,18 +3024,35 @@ void __sdhci_read_caps(struct sdhci_host *host, u16 *ver, u32 *caps, u32 *caps1)
sdhci_do_reset(host, SDHCI_RESET_ALL);
+ of_property_read_u64(mmc_dev(host->mmc)->of_node,
+ "sdhci-caps-mask", &dt_caps_mask);
+ of_property_read_u64(mmc_dev(host->mmc)->of_node,
+ "sdhci-caps", &dt_caps);
+
v = ver ? *ver : sdhci_readw(host, SDHCI_HOST_VERSION);
host->version = (v & SDHCI_SPEC_VER_MASK) >> SDHCI_SPEC_VER_SHIFT;
if (host->quirks & SDHCI_QUIRK_MISSING_CAPS)
return;
- host->caps = caps ? *caps : sdhci_readl(host, SDHCI_CAPABILITIES);
+ if (caps) {
+ host->caps = *caps;
+ } else {
+ host->caps = sdhci_readl(host, SDHCI_CAPABILITIES);
+ host->caps &= ~lower_32_bits(dt_caps_mask);
+ host->caps |= lower_32_bits(dt_caps);
+ }
if (host->version < SDHCI_SPEC_300)
return;
- host->caps1 = caps1 ? *caps1 : sdhci_readl(host, SDHCI_CAPABILITIES_1);
+ if (caps1) {
+ host->caps1 = *caps1;
+ } else {
+ host->caps1 = sdhci_readl(host, SDHCI_CAPABILITIES_1);
+ host->caps1 &= ~upper_32_bits(dt_caps_mask);
+ host->caps1 |= upper_32_bits(dt_caps);
+ }
}
EXPORT_SYMBOL_GPL(__sdhci_read_caps);
diff --git a/drivers/mmc/host/sdhci.h b/drivers/mmc/host/sdhci.h
index 766df17fb7eb..9c357760b1cd 100644
--- a/drivers/mmc/host/sdhci.h
+++ b/drivers/mmc/host/sdhci.h
@@ -73,6 +73,7 @@
#define SDHCI_DATA_LVL_MASK 0x00F00000
#define SDHCI_DATA_LVL_SHIFT 20
#define SDHCI_DATA_0_LVL_MASK 0x00100000
+#define SDHCI_CMD_LVL 0x01000000
#define SDHCI_HOST_CONTROL 0x28
#define SDHCI_CTRL_LED 0x01
@@ -655,7 +656,7 @@ extern void sdhci_free_host(struct sdhci_host *host);
static inline void *sdhci_priv(struct sdhci_host *host)
{
- return (void *)host->private;
+ return host->private;
}
extern void sdhci_card_detect(struct sdhci_host *host);
diff --git a/drivers/mmc/host/sh_mobile_sdhi.c b/drivers/mmc/host/sh_mobile_sdhi.c
index 49edff7fee49..d46c2d00c182 100644
--- a/drivers/mmc/host/sh_mobile_sdhi.c
+++ b/drivers/mmc/host/sh_mobile_sdhi.c
@@ -47,31 +47,69 @@
#define host_to_priv(host) container_of((host)->pdata, struct sh_mobile_sdhi, mmc_data)
+struct sh_mobile_sdhi_scc {
+ unsigned long clk_rate; /* clock rate for SDR104 */
+ u32 tap; /* sampling clock position for SDR104 */
+};
+
struct sh_mobile_sdhi_of_data {
unsigned long tmio_flags;
+ u32 tmio_ocr_mask;
unsigned long capabilities;
unsigned long capabilities2;
enum dma_slave_buswidth dma_buswidth;
dma_addr_t dma_rx_offset;
unsigned bus_shift;
+ int scc_offset;
+ struct sh_mobile_sdhi_scc *taps;
+ int taps_num;
};
static const struct sh_mobile_sdhi_of_data of_default_cfg = {
.tmio_flags = TMIO_MMC_HAS_IDLE_WAIT,
};
+static const struct sh_mobile_sdhi_of_data of_rz_compatible = {
+ .tmio_flags = TMIO_MMC_HAS_IDLE_WAIT | TMIO_MMC_32BIT_DATA_PORT,
+ .tmio_ocr_mask = MMC_VDD_32_33,
+ .capabilities = MMC_CAP_SD_HIGHSPEED | MMC_CAP_SDIO_IRQ,
+};
+
static const struct sh_mobile_sdhi_of_data of_rcar_gen1_compatible = {
.tmio_flags = TMIO_MMC_HAS_IDLE_WAIT | TMIO_MMC_WRPROTECT_DISABLE |
TMIO_MMC_CLK_ACTUAL,
.capabilities = MMC_CAP_SD_HIGHSPEED | MMC_CAP_SDIO_IRQ,
};
+/* Definitions for sampling clocks */
+static struct sh_mobile_sdhi_scc rcar_gen2_scc_taps[] = {
+ {
+ .clk_rate = 156000000,
+ .tap = 0x00000703,
+ },
+ {
+ .clk_rate = 0,
+ .tap = 0x00000300,
+ },
+};
+
static const struct sh_mobile_sdhi_of_data of_rcar_gen2_compatible = {
.tmio_flags = TMIO_MMC_HAS_IDLE_WAIT | TMIO_MMC_WRPROTECT_DISABLE |
TMIO_MMC_CLK_ACTUAL | TMIO_MMC_MIN_RCAR2,
.capabilities = MMC_CAP_SD_HIGHSPEED | MMC_CAP_SDIO_IRQ,
.dma_buswidth = DMA_SLAVE_BUSWIDTH_4_BYTES,
.dma_rx_offset = 0x2000,
+ .scc_offset = 0x0300,
+ .taps = rcar_gen2_scc_taps,
+ .taps_num = ARRAY_SIZE(rcar_gen2_scc_taps),
+};
+
+/* Definitions for sampling clocks */
+static struct sh_mobile_sdhi_scc rcar_gen3_scc_taps[] = {
+ {
+ .clk_rate = 0,
+ .tap = 0x00000300,
+ },
};
static const struct sh_mobile_sdhi_of_data of_rcar_gen3_compatible = {
@@ -79,6 +117,9 @@ static const struct sh_mobile_sdhi_of_data of_rcar_gen3_compatible = {
TMIO_MMC_CLK_ACTUAL | TMIO_MMC_MIN_RCAR2,
.capabilities = MMC_CAP_SD_HIGHSPEED | MMC_CAP_SDIO_IRQ,
.bus_shift = 2,
+ .scc_offset = 0x1000,
+ .taps = rcar_gen3_scc_taps,
+ .taps_num = ARRAY_SIZE(rcar_gen3_scc_taps),
};
static const struct of_device_id sh_mobile_sdhi_of_match[] = {
@@ -86,6 +127,7 @@ static const struct of_device_id sh_mobile_sdhi_of_match[] = {
{ .compatible = "renesas,sdhi-sh73a0", .data = &of_default_cfg, },
{ .compatible = "renesas,sdhi-r8a73a4", .data = &of_default_cfg, },
{ .compatible = "renesas,sdhi-r8a7740", .data = &of_default_cfg, },
+ { .compatible = "renesas,sdhi-r7s72100", .data = &of_rz_compatible, },
{ .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, },
@@ -105,6 +147,7 @@ struct sh_mobile_sdhi {
struct tmio_mmc_dma dma_priv;
struct pinctrl *pinctrl;
struct pinctrl_state *pins_default, *pins_uhs;
+ void __iomem *scc_ctl;
};
static void sh_mobile_sdhi_sdbuf_width(struct tmio_mmc_host *host, int width)
@@ -255,6 +298,201 @@ static int sh_mobile_sdhi_start_signal_voltage_switch(struct mmc_host *mmc,
return pinctrl_select_state(priv->pinctrl, pin_state);
}
+/* SCC registers */
+#define SH_MOBILE_SDHI_SCC_DTCNTL 0x000
+#define SH_MOBILE_SDHI_SCC_TAPSET 0x002
+#define SH_MOBILE_SDHI_SCC_DT2FF 0x004
+#define SH_MOBILE_SDHI_SCC_CKSEL 0x006
+#define SH_MOBILE_SDHI_SCC_RVSCNTL 0x008
+#define SH_MOBILE_SDHI_SCC_RVSREQ 0x00A
+
+/* Definitions for values the SH_MOBILE_SDHI_SCC_DTCNTL register */
+#define SH_MOBILE_SDHI_SCC_DTCNTL_TAPEN BIT(0)
+#define SH_MOBILE_SDHI_SCC_DTCNTL_TAPNUM_SHIFT 16
+#define SH_MOBILE_SDHI_SCC_DTCNTL_TAPNUM_MASK 0xff
+
+/* Definitions for values the SH_MOBILE_SDHI_SCC_CKSEL register */
+#define SH_MOBILE_SDHI_SCC_CKSEL_DTSEL BIT(0)
+/* Definitions for values the SH_MOBILE_SDHI_SCC_RVSCNTL register */
+#define SH_MOBILE_SDHI_SCC_RVSCNTL_RVSEN BIT(0)
+/* Definitions for values the SH_MOBILE_SDHI_SCC_RVSREQ register */
+#define SH_MOBILE_SDHI_SCC_RVSREQ_RVSERR BIT(2)
+
+static inline u32 sd_scc_read32(struct tmio_mmc_host *host,
+ struct sh_mobile_sdhi *priv, int addr)
+{
+ return readl(priv->scc_ctl + (addr << host->bus_shift));
+}
+
+static inline void sd_scc_write32(struct tmio_mmc_host *host,
+ struct sh_mobile_sdhi *priv,
+ int addr, u32 val)
+{
+ writel(val, priv->scc_ctl + (addr << host->bus_shift));
+}
+
+static unsigned int sh_mobile_sdhi_init_tuning(struct tmio_mmc_host *host)
+{
+ struct sh_mobile_sdhi *priv;
+
+ if (!(host->mmc->caps & MMC_CAP_UHS_SDR104))
+ return 0;
+
+ priv = host_to_priv(host);
+
+ /* set sampling clock selection range */
+ sd_scc_write32(host, priv, SH_MOBILE_SDHI_SCC_DTCNTL,
+ 0x8 << SH_MOBILE_SDHI_SCC_DTCNTL_TAPNUM_SHIFT);
+
+ /* Initialize SCC */
+ sd_ctrl_write32_as_16_and_16(host, CTL_STATUS, 0x0);
+
+ sd_scc_write32(host, priv, SH_MOBILE_SDHI_SCC_DTCNTL,
+ SH_MOBILE_SDHI_SCC_DTCNTL_TAPEN |
+ sd_scc_read32(host, priv, SH_MOBILE_SDHI_SCC_DTCNTL));
+
+ sd_ctrl_write16(host, CTL_SD_CARD_CLK_CTL, ~CLK_CTL_SCLKEN &
+ sd_ctrl_read16(host, CTL_SD_CARD_CLK_CTL));
+
+ sd_scc_write32(host, priv, SH_MOBILE_SDHI_SCC_CKSEL,
+ SH_MOBILE_SDHI_SCC_CKSEL_DTSEL |
+ sd_scc_read32(host, priv, SH_MOBILE_SDHI_SCC_CKSEL));
+
+ sd_ctrl_write16(host, CTL_SD_CARD_CLK_CTL, CLK_CTL_SCLKEN |
+ sd_ctrl_read16(host, CTL_SD_CARD_CLK_CTL));
+
+ sd_scc_write32(host, priv, SH_MOBILE_SDHI_SCC_RVSCNTL,
+ ~SH_MOBILE_SDHI_SCC_RVSCNTL_RVSEN &
+ sd_scc_read32(host, priv, SH_MOBILE_SDHI_SCC_RVSCNTL));
+
+ sd_scc_write32(host, priv, SH_MOBILE_SDHI_SCC_DT2FF, host->scc_tappos);
+
+ /* Read TAPNUM */
+ return (sd_scc_read32(host, priv, SH_MOBILE_SDHI_SCC_DTCNTL) >>
+ SH_MOBILE_SDHI_SCC_DTCNTL_TAPNUM_SHIFT) &
+ SH_MOBILE_SDHI_SCC_DTCNTL_TAPNUM_MASK;
+}
+
+static void sh_mobile_sdhi_prepare_tuning(struct tmio_mmc_host *host,
+ unsigned long tap)
+{
+ struct sh_mobile_sdhi *priv = host_to_priv(host);
+
+ /* Set sampling clock position */
+ sd_scc_write32(host, priv, SH_MOBILE_SDHI_SCC_TAPSET, tap);
+}
+
+#define SH_MOBILE_SDHI_MAX_TAP 3
+
+static int sh_mobile_sdhi_select_tuning(struct tmio_mmc_host *host)
+{
+ struct sh_mobile_sdhi *priv = host_to_priv(host);
+ unsigned long tap_cnt; /* counter of tuning success */
+ unsigned long tap_set; /* tap position */
+ unsigned long tap_start;/* start position of tuning success */
+ unsigned long tap_end; /* end position of tuning success */
+ unsigned long ntap; /* temporary counter of tuning success */
+ unsigned long i;
+
+ /* Clear SCC_RVSREQ */
+ sd_scc_write32(host, priv, SH_MOBILE_SDHI_SCC_RVSREQ, 0);
+
+ /*
+ * Find the longest consecutive run of successful probes. If that
+ * is more than SH_MOBILE_SDHI_MAX_TAP probes long then use the
+ * center index as the tap.
+ */
+ tap_cnt = 0;
+ ntap = 0;
+ tap_start = 0;
+ tap_end = 0;
+ for (i = 0; i < host->tap_num * 2; i++) {
+ if (test_bit(i, host->taps))
+ ntap++;
+ else {
+ if (ntap > tap_cnt) {
+ tap_start = i - ntap;
+ tap_end = i - 1;
+ tap_cnt = ntap;
+ }
+ ntap = 0;
+ }
+ }
+
+ if (ntap > tap_cnt) {
+ tap_start = i - ntap;
+ tap_end = i - 1;
+ tap_cnt = ntap;
+ }
+
+ if (tap_cnt >= SH_MOBILE_SDHI_MAX_TAP)
+ tap_set = (tap_start + tap_end) / 2 % host->tap_num;
+ else
+ return -EIO;
+
+ /* Set SCC */
+ sd_scc_write32(host, priv, SH_MOBILE_SDHI_SCC_TAPSET, tap_set);
+
+ /* Enable auto re-tuning */
+ sd_scc_write32(host, priv, SH_MOBILE_SDHI_SCC_RVSCNTL,
+ SH_MOBILE_SDHI_SCC_RVSCNTL_RVSEN |
+ sd_scc_read32(host, priv, SH_MOBILE_SDHI_SCC_RVSCNTL));
+
+ return 0;
+}
+
+
+static bool sh_mobile_sdhi_check_scc_error(struct tmio_mmc_host *host)
+{
+ struct sh_mobile_sdhi *priv;
+
+ if (!(host->mmc->caps & MMC_CAP_UHS_SDR104))
+ return 0;
+
+ priv = host_to_priv(host);
+
+ /* Check SCC error */
+ if (sd_scc_read32(host, priv, SH_MOBILE_SDHI_SCC_RVSCNTL) &
+ SH_MOBILE_SDHI_SCC_RVSCNTL_RVSEN &&
+ sd_scc_read32(host, priv, SH_MOBILE_SDHI_SCC_RVSREQ) &
+ SH_MOBILE_SDHI_SCC_RVSREQ_RVSERR) {
+ /* Clear SCC error */
+ sd_scc_write32(host, priv, SH_MOBILE_SDHI_SCC_RVSREQ, 0);
+ return true;
+ }
+
+ return false;
+}
+
+static void sh_mobile_sdhi_hw_reset(struct tmio_mmc_host *host)
+{
+ struct sh_mobile_sdhi *priv;
+
+ if (!(host->mmc->caps & MMC_CAP_UHS_SDR104))
+ return;
+
+ priv = host_to_priv(host);
+
+ /* Reset SCC */
+ sd_ctrl_write16(host, CTL_SD_CARD_CLK_CTL, ~CLK_CTL_SCLKEN &
+ sd_ctrl_read16(host, CTL_SD_CARD_CLK_CTL));
+
+ sd_scc_write32(host, priv, SH_MOBILE_SDHI_SCC_CKSEL,
+ ~SH_MOBILE_SDHI_SCC_CKSEL_DTSEL &
+ sd_scc_read32(host, priv, SH_MOBILE_SDHI_SCC_CKSEL));
+
+ sd_ctrl_write16(host, CTL_SD_CARD_CLK_CTL, CLK_CTL_SCLKEN |
+ sd_ctrl_read16(host, CTL_SD_CARD_CLK_CTL));
+
+ sd_scc_write32(host, priv, SH_MOBILE_SDHI_SCC_RVSCNTL,
+ ~SH_MOBILE_SDHI_SCC_RVSCNTL_RVSEN &
+ sd_scc_read32(host, priv, SH_MOBILE_SDHI_SCC_RVSCNTL));
+
+ sd_scc_write32(host, priv, SH_MOBILE_SDHI_SCC_RVSCNTL,
+ ~SH_MOBILE_SDHI_SCC_RVSCNTL_RVSEN &
+ sd_scc_read32(host, priv, SH_MOBILE_SDHI_SCC_RVSCNTL));
+}
+
static int sh_mobile_sdhi_wait_idle(struct tmio_mmc_host *host)
{
int timeout = 1000;
@@ -325,7 +563,7 @@ static int sh_mobile_sdhi_probe(struct platform_device *pdev)
struct tmio_mmc_data *mmd = pdev->dev.platform_data;
struct tmio_mmc_host *host;
struct resource *res;
- int irq, ret, i = 0;
+ int irq, ret, i;
struct tmio_mmc_dma *dma_priv;
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
@@ -364,6 +602,7 @@ static int sh_mobile_sdhi_probe(struct platform_device *pdev)
const struct sh_mobile_sdhi_of_data *of_data = of_id->data;
mmc_data->flags |= of_data->tmio_flags;
+ mmc_data->ocr_mask = of_data->tmio_ocr_mask;
mmc_data->capabilities |= of_data->capabilities;
mmc_data->capabilities2 |= of_data->capabilities2;
mmc_data->dma_rx_offset = of_data->dma_rx_offset;
@@ -384,6 +623,11 @@ static int sh_mobile_sdhi_probe(struct platform_device *pdev)
host->card_busy = sh_mobile_sdhi_card_busy;
host->start_signal_voltage_switch =
sh_mobile_sdhi_start_signal_voltage_switch;
+ host->init_tuning = sh_mobile_sdhi_init_tuning;
+ host->prepare_tuning = sh_mobile_sdhi_prepare_tuning;
+ host->select_tuning = sh_mobile_sdhi_select_tuning;
+ host->check_scc_error = sh_mobile_sdhi_check_scc_error;
+ host->hw_reset = sh_mobile_sdhi_hw_reset;
}
/* Orginally registers were 16 bit apart, could be 32 or 64 nowadays */
@@ -424,6 +668,34 @@ static int sh_mobile_sdhi_probe(struct platform_device *pdev)
if (ret < 0)
goto efree;
+ if (host->mmc->caps & MMC_CAP_UHS_SDR104) {
+ host->mmc->caps |= MMC_CAP_HW_RESET;
+
+ if (of_id && of_id->data) {
+ const struct sh_mobile_sdhi_of_data *of_data;
+ const struct sh_mobile_sdhi_scc *taps;
+ bool hit = false;
+
+ of_data = of_id->data;
+ taps = of_data->taps;
+
+ for (i = 0; i < of_data->taps_num; i++) {
+ if (taps[i].clk_rate == 0 ||
+ taps[i].clk_rate == host->mmc->f_max) {
+ host->scc_tappos = taps->tap;
+ hit = true;
+ break;
+ }
+ }
+
+ if (!hit)
+ dev_warn(&host->pdev->dev, "Unknown clock rate for SDR104\n");
+
+ priv->scc_ctl = host->ctl + of_data->scc_offset;
+ }
+ }
+
+ i = 0;
while (1) {
irq = platform_get_irq(pdev, i);
if (irq < 0)
diff --git a/drivers/mmc/host/sunxi-mmc.c b/drivers/mmc/host/sunxi-mmc.c
index c0a5c676d0e8..b1d1303389a7 100644
--- a/drivers/mmc/host/sunxi-mmc.c
+++ b/drivers/mmc/host/sunxi-mmc.c
@@ -822,10 +822,13 @@ static void sunxi_mmc_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
break;
case MMC_POWER_UP:
- host->ferror = mmc_regulator_set_ocr(mmc, mmc->supply.vmmc,
- ios->vdd);
- if (host->ferror)
- return;
+ if (!IS_ERR(mmc->supply.vmmc)) {
+ host->ferror = mmc_regulator_set_ocr(mmc,
+ mmc->supply.vmmc,
+ ios->vdd);
+ if (host->ferror)
+ return;
+ }
if (!IS_ERR(mmc->supply.vqmmc)) {
host->ferror = regulator_enable(mmc->supply.vqmmc);
@@ -847,7 +850,9 @@ static void sunxi_mmc_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
case MMC_POWER_OFF:
dev_dbg(mmc_dev(mmc), "power off!\n");
sunxi_mmc_reset_host(host);
- mmc_regulator_set_ocr(mmc, mmc->supply.vmmc, 0);
+ if (!IS_ERR(mmc->supply.vmmc))
+ mmc_regulator_set_ocr(mmc, mmc->supply.vmmc, 0);
+
if (!IS_ERR(mmc->supply.vqmmc) && host->vqmmc_enabled)
regulator_disable(mmc->supply.vqmmc);
host->vqmmc_enabled = false;
diff --git a/drivers/mmc/host/tmio_mmc.h b/drivers/mmc/host/tmio_mmc.h
index 8e126afd988c..9e20bcf3aa8d 100644
--- a/drivers/mmc/host/tmio_mmc.h
+++ b/drivers/mmc/host/tmio_mmc.h
@@ -153,9 +153,12 @@ struct tmio_mmc_host {
struct mutex ios_lock; /* protect set_ios() context */
bool native_hotplug;
bool sdio_irq_enabled;
+ u32 scc_tappos;
- int (*write16_hook)(struct tmio_mmc_host *host, int addr);
+ /* Mandatory callback */
int (*clk_enable)(struct tmio_mmc_host *host);
+
+ /* Optional callbacks */
unsigned int (*clk_update)(struct tmio_mmc_host *host,
unsigned int new_clock);
void (*clk_disable)(struct tmio_mmc_host *host);
@@ -164,6 +167,21 @@ struct tmio_mmc_host {
int (*card_busy)(struct mmc_host *mmc);
int (*start_signal_voltage_switch)(struct mmc_host *mmc,
struct mmc_ios *ios);
+ int (*write16_hook)(struct tmio_mmc_host *host, int addr);
+ void (*hw_reset)(struct tmio_mmc_host *host);
+ void (*prepare_tuning)(struct tmio_mmc_host *host, unsigned long tap);
+ bool (*check_scc_error)(struct tmio_mmc_host *host);
+
+ /*
+ * Mandatory callback for tuning to occur which is optional for SDR50
+ * and mandatory for SDR104.
+ */
+ unsigned int (*init_tuning)(struct tmio_mmc_host *host);
+ int (*select_tuning)(struct tmio_mmc_host *host);
+
+ /* Tuning values: 1 for success, 0 for failure */
+ DECLARE_BITMAP(taps, BITS_PER_BYTE * sizeof(long));
+ unsigned int tap_num;
};
struct tmio_mmc_host *tmio_mmc_host_alloc(struct platform_device *pdev);
@@ -245,6 +263,12 @@ static inline u32 sd_ctrl_read16_and_16_as_32(struct tmio_mmc_host *host, int ad
readw(host->ctl + ((addr + 2) << host->bus_shift)) << 16;
}
+static inline void sd_ctrl_read32_rep(struct tmio_mmc_host *host, int addr,
+ u32 *buf, int count)
+{
+ readsl(host->ctl + (addr << host->bus_shift), buf, count);
+}
+
static inline void sd_ctrl_write16(struct tmio_mmc_host *host, int addr, u16 val)
{
/* If there is a hook and it returns non-zero then there
@@ -267,4 +291,10 @@ static inline void sd_ctrl_write32_as_16_and_16(struct tmio_mmc_host *host, int
writew(val >> 16, host->ctl + ((addr + 2) << host->bus_shift));
}
+static inline void sd_ctrl_write32_rep(struct tmio_mmc_host *host, int addr,
+ const u32 *buf, int count)
+{
+ writesl(host->ctl + (addr << host->bus_shift), buf, count);
+}
+
#endif
diff --git a/drivers/mmc/host/tmio_mmc_pio.c b/drivers/mmc/host/tmio_mmc_pio.c
index 700567603107..2064fa1a5bf1 100644
--- a/drivers/mmc/host/tmio_mmc_pio.c
+++ b/drivers/mmc/host/tmio_mmc_pio.c
@@ -22,7 +22,6 @@
* TODO:
* Investigate using a workqueue for PIO transfers
* Eliminate FIXMEs
- * SDIO support
* Better Power management
* Handle MMC errors better
* double buffer support
@@ -36,6 +35,7 @@
#include <linux/io.h>
#include <linux/irq.h>
#include <linux/mfd/tmio.h>
+#include <linux/mmc/card.h>
#include <linux/mmc/host.h>
#include <linux/mmc/mmc.h>
#include <linux/mmc/slot-gpio.h>
@@ -298,6 +298,9 @@ static void tmio_mmc_finish_request(struct tmio_mmc_host *host)
if (mrq->cmd->error || (mrq->data && mrq->data->error))
tmio_mmc_abort_dma(host);
+ if (host->check_scc_error)
+ host->check_scc_error(host);
+
mmc_request_done(host->mmc, mrq);
}
@@ -393,6 +396,36 @@ static void tmio_mmc_transfer_data(struct tmio_mmc_host *host,
/*
* Transfer the data
*/
+ if (host->pdata->flags & TMIO_MMC_32BIT_DATA_PORT) {
+ u8 data[4] = { };
+
+ if (is_read)
+ sd_ctrl_read32_rep(host, CTL_SD_DATA_PORT, (u32 *)buf,
+ count >> 2);
+ else
+ sd_ctrl_write32_rep(host, CTL_SD_DATA_PORT, (u32 *)buf,
+ count >> 2);
+
+ /* if count was multiple of 4 */
+ if (!(count & 0x3))
+ return;
+
+ buf8 = (u8 *)(buf + (count >> 2));
+ count %= 4;
+
+ if (is_read) {
+ sd_ctrl_read32_rep(host, CTL_SD_DATA_PORT,
+ (u32 *)data, 1);
+ memcpy(buf8, data, count);
+ } else {
+ memcpy(data, buf8, count);
+ sd_ctrl_write32_rep(host, CTL_SD_DATA_PORT,
+ (u32 *)data, 1);
+ }
+
+ return;
+ }
+
if (is_read)
sd_ctrl_read16_rep(host, CTL_SD_DATA_PORT, buf, count >> 1);
else
@@ -522,7 +555,7 @@ void tmio_mmc_do_data_irq(struct tmio_mmc_host *host)
schedule_work(&host->done);
}
-static void tmio_mmc_data_irq(struct tmio_mmc_host *host)
+static void tmio_mmc_data_irq(struct tmio_mmc_host *host, unsigned int stat)
{
struct mmc_data *data;
spin_lock(&host->lock);
@@ -531,6 +564,9 @@ static void tmio_mmc_data_irq(struct tmio_mmc_host *host)
if (!data)
goto out;
+ if (stat & TMIO_STAT_CRCFAIL || stat & TMIO_STAT_STOPBIT_ERR ||
+ stat & TMIO_STAT_TXUNDERRUN)
+ data->error = -EILSEQ;
if (host->chan_tx && (data->flags & MMC_DATA_WRITE) && !host->force_pio) {
u32 status = sd_ctrl_read16_and_16_as_32(host, CTL_STATUS);
bool done = false;
@@ -579,8 +615,6 @@ static void tmio_mmc_cmd_irq(struct tmio_mmc_host *host,
goto out;
}
- host->cmd = NULL;
-
/* This controller is sicker than the PXA one. Not only do we need to
* drop the top 8 bits of the first response word, we also need to
* modify the order of the response for short response command types.
@@ -600,14 +634,16 @@ static void tmio_mmc_cmd_irq(struct tmio_mmc_host *host,
if (stat & TMIO_STAT_CMDTIMEOUT)
cmd->error = -ETIMEDOUT;
- else if (stat & TMIO_STAT_CRCFAIL && cmd->flags & MMC_RSP_CRC)
+ else if ((stat & TMIO_STAT_CRCFAIL && cmd->flags & MMC_RSP_CRC) ||
+ stat & TMIO_STAT_STOPBIT_ERR ||
+ stat & TMIO_STAT_CMD_IDX_ERR)
cmd->error = -EILSEQ;
/* If there is data to handle we enable data IRQs here, and
* we will ultimatley finish the request in the data_end handler.
* If theres no data or we encountered an error, finish now.
*/
- if (host->data && !cmd->error) {
+ if (host->data && (!cmd->error || cmd->error == -EILSEQ)) {
if (host->data->flags & MMC_DATA_READ) {
if (host->force_pio || !host->chan_rx)
tmio_mmc_enable_mmc_irqs(host, TMIO_MASK_READOP);
@@ -668,7 +704,7 @@ static bool __tmio_mmc_sdcard_irq(struct tmio_mmc_host *host,
/* Data transfer completion */
if (ireg & TMIO_STAT_DATAEND) {
tmio_mmc_ack_mmc_irqs(host, TMIO_STAT_DATAEND);
- tmio_mmc_data_irq(host);
+ tmio_mmc_data_irq(host, status);
return true;
}
@@ -687,7 +723,7 @@ static void tmio_mmc_sdio_irq(int irq, void *devid)
return;
status = sd_ctrl_read16(host, CTL_SDIO_STATUS);
- ireg = status & TMIO_SDIO_MASK_ALL & ~host->sdcard_irq_mask;
+ ireg = status & TMIO_SDIO_MASK_ALL & ~host->sdio_irq_mask;
sdio_status = status & ~TMIO_SDIO_MASK_ALL;
if (pdata->flags & TMIO_MMC_SDIO_STATUS_QUIRK)
@@ -756,6 +792,63 @@ static int tmio_mmc_start_data(struct tmio_mmc_host *host,
return 0;
}
+static void tmio_mmc_hw_reset(struct mmc_host *mmc)
+{
+ struct tmio_mmc_host *host = mmc_priv(mmc);
+
+ if (host->hw_reset)
+ host->hw_reset(host);
+}
+
+static int tmio_mmc_execute_tuning(struct mmc_host *mmc, u32 opcode)
+{
+ struct tmio_mmc_host *host = mmc_priv(mmc);
+ int i, ret = 0;
+
+ if (!host->tap_num) {
+ if (!host->init_tuning || !host->select_tuning)
+ /* Tuning is not supported */
+ goto out;
+
+ host->tap_num = host->init_tuning(host);
+ if (!host->tap_num)
+ /* Tuning is not supported */
+ goto out;
+ }
+
+ if (host->tap_num * 2 >= sizeof(host->taps) * BITS_PER_BYTE) {
+ dev_warn_once(&host->pdev->dev,
+ "Too many taps, skipping tuning. Please consider updating size of taps field of tmio_mmc_host\n");
+ goto out;
+ }
+
+ bitmap_zero(host->taps, host->tap_num * 2);
+
+ /* Issue CMD19 twice for each tap */
+ for (i = 0; i < 2 * host->tap_num; i++) {
+ if (host->prepare_tuning)
+ host->prepare_tuning(host, i % host->tap_num);
+
+ ret = mmc_send_tuning(mmc, opcode, NULL);
+ if (ret && ret != -EILSEQ)
+ goto out;
+ if (ret == 0)
+ set_bit(i, host->taps);
+
+ mdelay(1);
+ }
+
+ ret = host->select_tuning(host);
+
+out:
+ if (ret < 0) {
+ dev_warn(&host->pdev->dev, "Tuning procedure failed\n");
+ tmio_mmc_hw_reset(mmc);
+ }
+
+ return ret;
+}
+
/* Process requests from the MMC layer */
static void tmio_mmc_request(struct mmc_host *mmc, struct mmc_request *mrq)
{
@@ -972,6 +1065,8 @@ static struct mmc_host_ops tmio_mmc_ops = {
.get_cd = mmc_gpio_get_cd,
.enable_sdio_irq = tmio_mmc_enable_sdio_irq,
.multi_io_quirk = tmio_multi_io_quirk,
+ .hw_reset = tmio_mmc_hw_reset,
+ .execute_tuning = tmio_mmc_execute_tuning,
};
static int tmio_mmc_init_ocr(struct tmio_mmc_host *host)
@@ -1218,6 +1313,11 @@ int tmio_mmc_host_runtime_suspend(struct device *dev)
}
EXPORT_SYMBOL(tmio_mmc_host_runtime_suspend);
+static bool tmio_mmc_can_retune(struct tmio_mmc_host *host)
+{
+ return host->tap_num && mmc_can_retune(host->mmc);
+}
+
int tmio_mmc_host_runtime_resume(struct device *dev)
{
struct mmc_host *mmc = dev_get_drvdata(dev);
@@ -1231,6 +1331,9 @@ int tmio_mmc_host_runtime_resume(struct device *dev)
tmio_mmc_enable_dma(host, true);
+ if (tmio_mmc_can_retune(host) && host->select_tuning(host))
+ dev_warn(&host->pdev->dev, "Tuning selection failed\n");
+
return 0;
}
EXPORT_SYMBOL(tmio_mmc_host_runtime_resume);
diff --git a/drivers/mmc/host/wbsd.c b/drivers/mmc/host/wbsd.c
index c3fd16d997ca..80a3b11f3217 100644
--- a/drivers/mmc/host/wbsd.c
+++ b/drivers/mmc/host/wbsd.c
@@ -1395,23 +1395,25 @@ static void wbsd_request_dma(struct wbsd_host *host, int dma)
*/
host->dma_addr = dma_map_single(mmc_dev(host->mmc), host->dma_buffer,
WBSD_DMA_SIZE, DMA_BIDIRECTIONAL);
+ if (dma_mapping_error(mmc_dev(host->mmc), host->dma_addr))
+ goto kfree;
/*
* ISA DMA must be aligned on a 64k basis.
*/
if ((host->dma_addr & 0xffff) != 0)
- goto kfree;
+ goto unmap;
/*
* ISA cannot access memory above 16 MB.
*/
else if (host->dma_addr >= 0x1000000)
- goto kfree;
+ goto unmap;
host->dma = dma;
return;
-kfree:
+unmap:
/*
* If we've gotten here then there is some kind of alignment bug
*/
@@ -1421,6 +1423,7 @@ kfree:
WBSD_DMA_SIZE, DMA_BIDIRECTIONAL);
host->dma_addr = 0;
+kfree:
kfree(host->dma_buffer);
host->dma_buffer = NULL;
@@ -1434,7 +1437,7 @@ err:
static void wbsd_release_dma(struct wbsd_host *host)
{
- if (host->dma_addr) {
+ if (!dma_mapping_error(mmc_dev(host->mmc), host->dma_addr)) {
dma_unmap_single(mmc_dev(host->mmc), host->dma_addr,
WBSD_DMA_SIZE, DMA_BIDIRECTIONAL);
}
diff --git a/drivers/soc/Kconfig b/drivers/soc/Kconfig
index e6e90e80519a..f31bceb69c0d 100644
--- a/drivers/soc/Kconfig
+++ b/drivers/soc/Kconfig
@@ -1,8 +1,7 @@
menu "SOC (System On Chip) specific Drivers"
source "drivers/soc/bcm/Kconfig"
-source "drivers/soc/fsl/qbman/Kconfig"
-source "drivers/soc/fsl/qe/Kconfig"
+source "drivers/soc/fsl/Kconfig"
source "drivers/soc/mediatek/Kconfig"
source "drivers/soc/qcom/Kconfig"
source "drivers/soc/rockchip/Kconfig"
diff --git a/drivers/soc/fsl/Kconfig b/drivers/soc/fsl/Kconfig
new file mode 100644
index 000000000000..7a9fb9baa66d
--- /dev/null
+++ b/drivers/soc/fsl/Kconfig
@@ -0,0 +1,18 @@
+#
+# Freescale SOC drivers
+#
+
+source "drivers/soc/fsl/qbman/Kconfig"
+source "drivers/soc/fsl/qe/Kconfig"
+
+config FSL_GUTS
+ bool
+ select SOC_BUS
+ help
+ The global utilities block controls power management, I/O device
+ enabling, power-onreset(POR) configuration monitoring, alternate
+ function selection for multiplexed signals,and clock control.
+ This driver is to manage and access global utilities block.
+ Initially only reading SVR and registering soc device are supported.
+ Other guts accesses, such as reading RCW, should eventually be moved
+ into this driver as well.
diff --git a/drivers/soc/fsl/Makefile b/drivers/soc/fsl/Makefile
index 75e1f5334821..44b3bebef24a 100644
--- a/drivers/soc/fsl/Makefile
+++ b/drivers/soc/fsl/Makefile
@@ -5,3 +5,4 @@
obj-$(CONFIG_FSL_DPAA) += qbman/
obj-$(CONFIG_QUICC_ENGINE) += qe/
obj-$(CONFIG_CPM) += qe/
+obj-$(CONFIG_FSL_GUTS) += guts.o
diff --git a/drivers/soc/fsl/guts.c b/drivers/soc/fsl/guts.c
new file mode 100644
index 000000000000..6af7a11f09a5
--- /dev/null
+++ b/drivers/soc/fsl/guts.c
@@ -0,0 +1,239 @@
+/*
+ * Freescale QorIQ Platforms GUTS Driver
+ *
+ * Copyright (C) 2016 Freescale Semiconductor, Inc.
+ *
+ * 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.
+ */
+
+#include <linux/io.h>
+#include <linux/slab.h>
+#include <linux/module.h>
+#include <linux/of_fdt.h>
+#include <linux/sys_soc.h>
+#include <linux/of_address.h>
+#include <linux/platform_device.h>
+#include <linux/fsl/guts.h>
+
+struct guts {
+ struct ccsr_guts __iomem *regs;
+ bool little_endian;
+};
+
+struct fsl_soc_die_attr {
+ char *die;
+ u32 svr;
+ u32 mask;
+};
+
+static struct guts *guts;
+static struct soc_device_attribute soc_dev_attr;
+static struct soc_device *soc_dev;
+
+
+/* SoC die attribute definition for QorIQ platform */
+static const struct fsl_soc_die_attr fsl_soc_die[] = {
+ /*
+ * Power Architecture-based SoCs T Series
+ */
+
+ /* Die: T4240, SoC: T4240/T4160/T4080 */
+ { .die = "T4240",
+ .svr = 0x82400000,
+ .mask = 0xfff00000,
+ },
+ /* Die: T1040, SoC: T1040/T1020/T1042/T1022 */
+ { .die = "T1040",
+ .svr = 0x85200000,
+ .mask = 0xfff00000,
+ },
+ /* Die: T2080, SoC: T2080/T2081 */
+ { .die = "T2080",
+ .svr = 0x85300000,
+ .mask = 0xfff00000,
+ },
+ /* Die: T1024, SoC: T1024/T1014/T1023/T1013 */
+ { .die = "T1024",
+ .svr = 0x85400000,
+ .mask = 0xfff00000,
+ },
+
+ /*
+ * ARM-based SoCs LS Series
+ */
+
+ /* Die: LS1043A, SoC: LS1043A/LS1023A */
+ { .die = "LS1043A",
+ .svr = 0x87920000,
+ .mask = 0xffff0000,
+ },
+ /* Die: LS2080A, SoC: LS2080A/LS2040A/LS2085A */
+ { .die = "LS2080A",
+ .svr = 0x87010000,
+ .mask = 0xff3f0000,
+ },
+ /* Die: LS1088A, SoC: LS1088A/LS1048A/LS1084A/LS1044A */
+ { .die = "LS1088A",
+ .svr = 0x87030000,
+ .mask = 0xff3f0000,
+ },
+ /* Die: LS1012A, SoC: LS1012A */
+ { .die = "LS1012A",
+ .svr = 0x87040000,
+ .mask = 0xffff0000,
+ },
+ /* Die: LS1046A, SoC: LS1046A/LS1026A */
+ { .die = "LS1046A",
+ .svr = 0x87070000,
+ .mask = 0xffff0000,
+ },
+ /* Die: LS2088A, SoC: LS2088A/LS2048A/LS2084A/LS2044A */
+ { .die = "LS2088A",
+ .svr = 0x87090000,
+ .mask = 0xff3f0000,
+ },
+ /* Die: LS1021A, SoC: LS1021A/LS1020A/LS1022A */
+ { .die = "LS1021A",
+ .svr = 0x87000000,
+ .mask = 0xfff70000,
+ },
+ { },
+};
+
+static const struct fsl_soc_die_attr *fsl_soc_die_match(
+ u32 svr, const struct fsl_soc_die_attr *matches)
+{
+ while (matches->svr) {
+ if (matches->svr == (svr & matches->mask))
+ return matches;
+ matches++;
+ };
+ return NULL;
+}
+
+u32 fsl_guts_get_svr(void)
+{
+ u32 svr = 0;
+
+ if (!guts || !guts->regs)
+ return svr;
+
+ if (guts->little_endian)
+ svr = ioread32(&guts->regs->svr);
+ else
+ svr = ioread32be(&guts->regs->svr);
+
+ return svr;
+}
+EXPORT_SYMBOL(fsl_guts_get_svr);
+
+static int fsl_guts_probe(struct platform_device *pdev)
+{
+ struct device_node *root, *np = pdev->dev.of_node;
+ struct device *dev = &pdev->dev;
+ struct resource *res;
+ const struct fsl_soc_die_attr *soc_die;
+ const char *machine;
+ u32 svr;
+
+ /* Initialize guts */
+ guts = devm_kzalloc(dev, sizeof(*guts), GFP_KERNEL);
+ if (!guts)
+ return -ENOMEM;
+
+ guts->little_endian = of_property_read_bool(np, "little-endian");
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ guts->regs = devm_ioremap_resource(dev, res);
+ if (IS_ERR(guts->regs))
+ return PTR_ERR(guts->regs);
+
+ /* Register soc device */
+ root = of_find_node_by_path("/");
+ if (of_property_read_string(root, "model", &machine))
+ of_property_read_string_index(root, "compatible", 0, &machine);
+ of_node_put(root);
+ if (machine)
+ soc_dev_attr.machine = devm_kstrdup(dev, machine, GFP_KERNEL);
+
+ svr = fsl_guts_get_svr();
+ soc_die = fsl_soc_die_match(svr, fsl_soc_die);
+ if (soc_die) {
+ soc_dev_attr.family = devm_kasprintf(dev, GFP_KERNEL,
+ "QorIQ %s", soc_die->die);
+ } else {
+ soc_dev_attr.family = devm_kasprintf(dev, GFP_KERNEL, "QorIQ");
+ }
+ soc_dev_attr.soc_id = devm_kasprintf(dev, GFP_KERNEL,
+ "svr:0x%08x", svr);
+ soc_dev_attr.revision = devm_kasprintf(dev, GFP_KERNEL, "%d.%d",
+ (svr >> 4) & 0xf, svr & 0xf);
+
+ soc_dev = soc_device_register(&soc_dev_attr);
+ if (IS_ERR(soc_dev))
+ return PTR_ERR(soc_dev);
+
+ pr_info("Machine: %s\n", soc_dev_attr.machine);
+ pr_info("SoC family: %s\n", soc_dev_attr.family);
+ pr_info("SoC ID: %s, Revision: %s\n",
+ soc_dev_attr.soc_id, soc_dev_attr.revision);
+ return 0;
+}
+
+static int fsl_guts_remove(struct platform_device *dev)
+{
+ soc_device_unregister(soc_dev);
+ return 0;
+}
+
+/*
+ * Table for matching compatible strings, for device tree
+ * guts node, for Freescale QorIQ SOCs.
+ */
+static const struct of_device_id fsl_guts_of_match[] = {
+ { .compatible = "fsl,qoriq-device-config-1.0", },
+ { .compatible = "fsl,qoriq-device-config-2.0", },
+ { .compatible = "fsl,p1010-guts", },
+ { .compatible = "fsl,p1020-guts", },
+ { .compatible = "fsl,p1021-guts", },
+ { .compatible = "fsl,p1022-guts", },
+ { .compatible = "fsl,p1023-guts", },
+ { .compatible = "fsl,p2020-guts", },
+ { .compatible = "fsl,bsc9131-guts", },
+ { .compatible = "fsl,bsc9132-guts", },
+ { .compatible = "fsl,mpc8536-guts", },
+ { .compatible = "fsl,mpc8544-guts", },
+ { .compatible = "fsl,mpc8548-guts", },
+ { .compatible = "fsl,mpc8568-guts", },
+ { .compatible = "fsl,mpc8569-guts", },
+ { .compatible = "fsl,mpc8572-guts", },
+ { .compatible = "fsl,ls1021a-dcfg", },
+ { .compatible = "fsl,ls1043a-dcfg", },
+ { .compatible = "fsl,ls2080a-dcfg", },
+ {}
+};
+MODULE_DEVICE_TABLE(of, fsl_guts_of_match);
+
+static struct platform_driver fsl_guts_driver = {
+ .driver = {
+ .name = "fsl-guts",
+ .of_match_table = fsl_guts_of_match,
+ },
+ .probe = fsl_guts_probe,
+ .remove = fsl_guts_remove,
+};
+
+static int __init fsl_guts_init(void)
+{
+ return platform_driver_register(&fsl_guts_driver);
+}
+core_initcall(fsl_guts_init);
+
+static void __exit fsl_guts_exit(void)
+{
+ platform_driver_unregister(&fsl_guts_driver);
+}
+module_exit(fsl_guts_exit);
diff --git a/include/linux/fsl/guts.h b/include/linux/fsl/guts.h
index 649e9171a9b3..3efa3b861d44 100644
--- a/include/linux/fsl/guts.h
+++ b/include/linux/fsl/guts.h
@@ -29,83 +29,112 @@
* #ifdefs.
*/
struct ccsr_guts {
- __be32 porpllsr; /* 0x.0000 - POR PLL Ratio Status Register */
- __be32 porbmsr; /* 0x.0004 - POR Boot Mode Status Register */
- __be32 porimpscr; /* 0x.0008 - POR I/O Impedance Status and Control Register */
- __be32 pordevsr; /* 0x.000c - POR I/O Device Status Register */
- __be32 pordbgmsr; /* 0x.0010 - POR Debug Mode Status Register */
- __be32 pordevsr2; /* 0x.0014 - POR device status register 2 */
+ u32 porpllsr; /* 0x.0000 - POR PLL Ratio Status Register */
+ u32 porbmsr; /* 0x.0004 - POR Boot Mode Status Register */
+ u32 porimpscr; /* 0x.0008 - POR I/O Impedance Status and
+ * Control Register
+ */
+ u32 pordevsr; /* 0x.000c - POR I/O Device Status Register */
+ u32 pordbgmsr; /* 0x.0010 - POR Debug Mode Status Register */
+ u32 pordevsr2; /* 0x.0014 - POR device status register 2 */
u8 res018[0x20 - 0x18];
- __be32 porcir; /* 0x.0020 - POR Configuration Information Register */
+ u32 porcir; /* 0x.0020 - POR Configuration Information
+ * Register
+ */
u8 res024[0x30 - 0x24];
- __be32 gpiocr; /* 0x.0030 - GPIO Control Register */
+ u32 gpiocr; /* 0x.0030 - GPIO Control Register */
u8 res034[0x40 - 0x34];
- __be32 gpoutdr; /* 0x.0040 - General-Purpose Output Data Register */
+ u32 gpoutdr; /* 0x.0040 - General-Purpose Output Data
+ * Register
+ */
u8 res044[0x50 - 0x44];
- __be32 gpindr; /* 0x.0050 - General-Purpose Input Data Register */
+ u32 gpindr; /* 0x.0050 - General-Purpose Input Data
+ * Register
+ */
u8 res054[0x60 - 0x54];
- __be32 pmuxcr; /* 0x.0060 - Alternate Function Signal Multiplex Control */
- __be32 pmuxcr2; /* 0x.0064 - Alternate function signal multiplex control 2 */
- __be32 dmuxcr; /* 0x.0068 - DMA Mux Control Register */
+ u32 pmuxcr; /* 0x.0060 - Alternate Function Signal
+ * Multiplex Control
+ */
+ u32 pmuxcr2; /* 0x.0064 - Alternate function signal
+ * multiplex control 2
+ */
+ u32 dmuxcr; /* 0x.0068 - DMA Mux Control Register */
u8 res06c[0x70 - 0x6c];
- __be32 devdisr; /* 0x.0070 - Device Disable Control */
+ u32 devdisr; /* 0x.0070 - Device Disable Control */
#define CCSR_GUTS_DEVDISR_TB1 0x00001000
#define CCSR_GUTS_DEVDISR_TB0 0x00004000
- __be32 devdisr2; /* 0x.0074 - Device Disable Control 2 */
+ u32 devdisr2; /* 0x.0074 - Device Disable Control 2 */
u8 res078[0x7c - 0x78];
- __be32 pmjcr; /* 0x.007c - 4 Power Management Jog Control Register */
- __be32 powmgtcsr; /* 0x.0080 - Power Management Status and Control Register */
- __be32 pmrccr; /* 0x.0084 - Power Management Reset Counter Configuration Register */
- __be32 pmpdccr; /* 0x.0088 - Power Management Power Down Counter Configuration Register */
- __be32 pmcdr; /* 0x.008c - 4Power management clock disable register */
- __be32 mcpsumr; /* 0x.0090 - Machine Check Summary Register */
- __be32 rstrscr; /* 0x.0094 - Reset Request Status and Control Register */
- __be32 ectrstcr; /* 0x.0098 - Exception reset control register */
- __be32 autorstsr; /* 0x.009c - Automatic reset status register */
- __be32 pvr; /* 0x.00a0 - Processor Version Register */
- __be32 svr; /* 0x.00a4 - System Version Register */
+ u32 pmjcr; /* 0x.007c - 4 Power Management Jog Control
+ * Register
+ */
+ u32 powmgtcsr; /* 0x.0080 - Power Management Status and
+ * Control Register
+ */
+ u32 pmrccr; /* 0x.0084 - Power Management Reset Counter
+ * Configuration Register
+ */
+ u32 pmpdccr; /* 0x.0088 - Power Management Power Down Counter
+ * Configuration Register
+ */
+ u32 pmcdr; /* 0x.008c - 4Power management clock disable
+ * register
+ */
+ u32 mcpsumr; /* 0x.0090 - Machine Check Summary Register */
+ u32 rstrscr; /* 0x.0094 - Reset Request Status and
+ * Control Register
+ */
+ u32 ectrstcr; /* 0x.0098 - Exception reset control register */
+ u32 autorstsr; /* 0x.009c - Automatic reset status register */
+ u32 pvr; /* 0x.00a0 - Processor Version Register */
+ u32 svr; /* 0x.00a4 - System Version Register */
u8 res0a8[0xb0 - 0xa8];
- __be32 rstcr; /* 0x.00b0 - Reset Control Register */
+ u32 rstcr; /* 0x.00b0 - Reset Control Register */
u8 res0b4[0xc0 - 0xb4];
- __be32 iovselsr; /* 0x.00c0 - I/O voltage select status register
+ u32 iovselsr; /* 0x.00c0 - I/O voltage select status register
Called 'elbcvselcr' on 86xx SOCs */
u8 res0c4[0x100 - 0xc4];
- __be32 rcwsr[16]; /* 0x.0100 - Reset Control Word Status registers
+ u32 rcwsr[16]; /* 0x.0100 - Reset Control Word Status registers
There are 16 registers */
u8 res140[0x224 - 0x140];
- __be32 iodelay1; /* 0x.0224 - IO delay control register 1 */
- __be32 iodelay2; /* 0x.0228 - IO delay control register 2 */
+ u32 iodelay1; /* 0x.0224 - IO delay control register 1 */
+ u32 iodelay2; /* 0x.0228 - IO delay control register 2 */
u8 res22c[0x604 - 0x22c];
- __be32 pamubypenr; /* 0x.604 - PAMU bypass enable register */
+ u32 pamubypenr; /* 0x.604 - PAMU bypass enable register */
u8 res608[0x800 - 0x608];
- __be32 clkdvdr; /* 0x.0800 - Clock Divide Register */
+ u32 clkdvdr; /* 0x.0800 - Clock Divide Register */
u8 res804[0x900 - 0x804];
- __be32 ircr; /* 0x.0900 - Infrared Control Register */
+ u32 ircr; /* 0x.0900 - Infrared Control Register */
u8 res904[0x908 - 0x904];
- __be32 dmacr; /* 0x.0908 - DMA Control Register */
+ u32 dmacr; /* 0x.0908 - DMA Control Register */
u8 res90c[0x914 - 0x90c];
- __be32 elbccr; /* 0x.0914 - eLBC Control Register */
+ u32 elbccr; /* 0x.0914 - eLBC Control Register */
u8 res918[0xb20 - 0x918];
- __be32 ddr1clkdr; /* 0x.0b20 - DDR1 Clock Disable Register */
- __be32 ddr2clkdr; /* 0x.0b24 - DDR2 Clock Disable Register */
- __be32 ddrclkdr; /* 0x.0b28 - DDR Clock Disable Register */
+ u32 ddr1clkdr; /* 0x.0b20 - DDR1 Clock Disable Register */
+ u32 ddr2clkdr; /* 0x.0b24 - DDR2 Clock Disable Register */
+ u32 ddrclkdr; /* 0x.0b28 - DDR Clock Disable Register */
u8 resb2c[0xe00 - 0xb2c];
- __be32 clkocr; /* 0x.0e00 - Clock Out Select Register */
+ u32 clkocr; /* 0x.0e00 - Clock Out Select Register */
u8 rese04[0xe10 - 0xe04];
- __be32 ddrdllcr; /* 0x.0e10 - DDR DLL Control Register */
+ u32 ddrdllcr; /* 0x.0e10 - DDR DLL Control Register */
u8 rese14[0xe20 - 0xe14];
- __be32 lbcdllcr; /* 0x.0e20 - LBC DLL Control Register */
- __be32 cpfor; /* 0x.0e24 - L2 charge pump fuse override register */
+ u32 lbcdllcr; /* 0x.0e20 - LBC DLL Control Register */
+ u32 cpfor; /* 0x.0e24 - L2 charge pump fuse override
+ * register
+ */
u8 rese28[0xf04 - 0xe28];
- __be32 srds1cr0; /* 0x.0f04 - SerDes1 Control Register 0 */
- __be32 srds1cr1; /* 0x.0f08 - SerDes1 Control Register 0 */
+ u32 srds1cr0; /* 0x.0f04 - SerDes1 Control Register 0 */
+ u32 srds1cr1; /* 0x.0f08 - SerDes1 Control Register 0 */
u8 resf0c[0xf2c - 0xf0c];
- __be32 itcr; /* 0x.0f2c - Internal transaction control register */
+ u32 itcr; /* 0x.0f2c - Internal transaction control
+ * register
+ */
u8 resf30[0xf40 - 0xf30];
- __be32 srds2cr0; /* 0x.0f40 - SerDes2 Control Register 0 */
- __be32 srds2cr1; /* 0x.0f44 - SerDes2 Control Register 0 */
+ u32 srds2cr0; /* 0x.0f40 - SerDes2 Control Register 0 */
+ u32 srds2cr1; /* 0x.0f44 - SerDes2 Control Register 0 */
} __attribute__ ((packed));
+u32 fsl_guts_get_svr(void);
/* Alternate function signal multiplex control */
#define MPC85xx_PMUXCR_QE(x) (0x8000 >> (x))
diff --git a/include/linux/mfd/tmio.h b/include/linux/mfd/tmio.h
index 7a26286db895..fba44abd05ba 100644
--- a/include/linux/mfd/tmio.h
+++ b/include/linux/mfd/tmio.h
@@ -100,6 +100,11 @@
#define TMIO_MMC_SDIO_STATUS_QUIRK (1 << 8)
/*
+ * Some controllers have a 32-bit wide data port register
+ */
+#define TMIO_MMC_32BIT_DATA_PORT (1 << 9)
+
+/*
* Some controllers allows to set SDx actual clock
*/
#define TMIO_MMC_CLK_ACTUAL (1 << 10)
diff --git a/include/linux/mmc/card.h b/include/linux/mmc/card.h
index 73fad83acbcb..e49a3ff9d0e0 100644
--- a/include/linux/mmc/card.h
+++ b/include/linux/mmc/card.h
@@ -207,18 +207,6 @@ struct sdio_func_tuple;
#define SDIO_MAX_FUNCS 7
-enum mmc_blk_status {
- MMC_BLK_SUCCESS = 0,
- MMC_BLK_PARTIAL,
- MMC_BLK_CMD_ERR,
- MMC_BLK_RETRY,
- MMC_BLK_ABORT,
- MMC_BLK_DATA_ERR,
- MMC_BLK_ECC_ERR,
- MMC_BLK_NOMEDIUM,
- MMC_BLK_NEW_REQUEST,
-};
-
/* The number of MMC physical partitions. These consist of:
* boot partitions (2), general purpose partitions (4) and
* RPMB partition (1) in MMC v4.4.
diff --git a/include/linux/mmc/core.h b/include/linux/mmc/core.h
index 2b953eb8ceae..0ce928b3ce90 100644
--- a/include/linux/mmc/core.h
+++ b/include/linux/mmc/core.h
@@ -15,6 +15,18 @@ struct request;
struct mmc_data;
struct mmc_request;
+enum mmc_blk_status {
+ MMC_BLK_SUCCESS = 0,
+ MMC_BLK_PARTIAL,
+ MMC_BLK_CMD_ERR,
+ MMC_BLK_RETRY,
+ MMC_BLK_ABORT,
+ MMC_BLK_DATA_ERR,
+ MMC_BLK_ECC_ERR,
+ MMC_BLK_NOMEDIUM,
+ MMC_BLK_NEW_REQUEST,
+};
+
struct mmc_command {
u32 opcode;
u32 arg;
@@ -150,7 +162,8 @@ struct mmc_async_req;
extern int mmc_stop_bkops(struct mmc_card *);
extern int mmc_read_bkops_status(struct mmc_card *);
extern struct mmc_async_req *mmc_start_req(struct mmc_host *,
- struct mmc_async_req *, int *);
+ struct mmc_async_req *,
+ enum mmc_blk_status *);
extern int mmc_interrupt_hpi(struct mmc_card *);
extern void mmc_wait_for_req(struct mmc_host *, struct mmc_request *);
extern void mmc_wait_for_req_done(struct mmc_host *host,
diff --git a/include/linux/mmc/host.h b/include/linux/mmc/host.h
index 0b2439441cc8..68639295148d 100644
--- a/include/linux/mmc/host.h
+++ b/include/linux/mmc/host.h
@@ -173,7 +173,7 @@ struct mmc_async_req {
* Check error status of completed mmc request.
* Returns 0 if success otherwise non zero.
*/
- int (*err_check) (struct mmc_card *, struct mmc_async_req *);
+ enum mmc_blk_status (*err_check)(struct mmc_card *, struct mmc_async_req *);
};
/**
@@ -546,6 +546,11 @@ static inline void mmc_retune_recheck(struct mmc_host *host)
host->retune_now = 1;
}
+static inline bool mmc_can_retune(struct mmc_host *host)
+{
+ return host->can_retune == 1;
+}
+
void mmc_retune_pause(struct mmc_host *host);
void mmc_retune_unpause(struct mmc_host *host);
diff --git a/include/linux/mmc/slot-gpio.h b/include/linux/mmc/slot-gpio.h
index 3945a8c9d3cb..a7972cd3bc14 100644
--- a/include/linux/mmc/slot-gpio.h
+++ b/include/linux/mmc/slot-gpio.h
@@ -29,5 +29,6 @@ int mmc_gpiod_request_ro(struct mmc_host *host, const char *con_id,
void mmc_gpio_set_cd_isr(struct mmc_host *host,
irqreturn_t (*isr)(int irq, void *dev_id));
void mmc_gpiod_request_cd_irq(struct mmc_host *host);
+bool mmc_can_gpio_cd(struct mmc_host *host);
#endif
diff --git a/include/uapi/linux/mmc/ioctl.h b/include/uapi/linux/mmc/ioctl.h
index 7e385b83b9d8..700a55156eee 100644
--- a/include/uapi/linux/mmc/ioctl.h
+++ b/include/uapi/linux/mmc/ioctl.h
@@ -69,6 +69,6 @@ struct mmc_ioc_multi_cmd {
* is enforced per ioctl call. For larger data transfers, use the normal
* block device operations.
*/
-#define MMC_IOC_MAX_BYTES (512L * 256)
+#define MMC_IOC_MAX_BYTES (512L * 1024)
#define MMC_IOC_MAX_CMDS 255
#endif /* LINUX_MMC_IOCTL_H */