From c4a357691693776f5f941f29bdce704b29b156ba Mon Sep 17 00:00:00 2001 From: Srinivas Kandagatla Date: Mon, 2 Jun 2014 10:08:39 +0100 Subject: mmc: mmci: use NSEC_PER_SEC macro This patch replaces a constant used in calculating timeout with a proper macro. This is make code more readable. Signed-off-by: Srinivas Kandagatla Reviewed-by: Linus Walleij Signed-off-by: Ulf Hansson --- drivers/mmc/host/mmci.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/mmc/host/mmci.c b/drivers/mmc/host/mmci.c index 7ad463e9741c..c67120bf2e93 100644 --- a/drivers/mmc/host/mmci.c +++ b/drivers/mmc/host/mmci.c @@ -719,7 +719,7 @@ static void mmci_start_data(struct mmci_host *host, struct mmc_data *data) data->bytes_xfered = 0; clks = (unsigned long long)data->timeout_ns * host->cclk; - do_div(clks, 1000000000UL); + do_div(clks, NSEC_PER_SEC); timeout = data->timeout_clks + (unsigned int)clks; -- cgit v1.2.3-59-g8ed1b From 9681a4e88207bca452c39fdbf070fa7a8eea9a8a Mon Sep 17 00:00:00 2001 From: Srinivas Kandagatla Date: Mon, 2 Jun 2014 10:08:48 +0100 Subject: mmc: mmci: Add Qualcomm specific register defines. This patch adds a Qualcomm SD Card controller specific register variations to header file. Qualcomm SDCC controller is pl180, with slight changes in the register layout from standard pl180 register set. Signed-off-by: Srinivas Kandagatla Reviewed-by: Linus Walleij Signed-off-by: Ulf Hansson --- drivers/mmc/host/mmci.h | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/drivers/mmc/host/mmci.h b/drivers/mmc/host/mmci.h index 347d942d740b..d38a99df1820 100644 --- a/drivers/mmc/host/mmci.h +++ b/drivers/mmc/host/mmci.h @@ -41,6 +41,15 @@ /* Modified PL180 on Versatile Express platform */ #define MCI_ARM_HWFCEN (1 << 12) +/* Modified on Qualcomm Integrations */ +#define MCI_QCOM_CLK_WIDEBUS_8 (BIT(10) | BIT(11)) +#define MCI_QCOM_CLK_FLOWENA BIT(12) +#define MCI_QCOM_CLK_INVERTOUT BIT(13) + +/* select in latch data and command in */ +#define MCI_QCOM_CLK_SELECT_IN_FBCLK BIT(15) +#define MCI_QCOM_CLK_SELECT_IN_DDR_MODE (BIT(14) | BIT(15)) + #define MMCIARGUMENT 0x008 #define MMCICOMMAND 0x00c #define MCI_CPSM_RESPONSE (1 << 6) @@ -54,6 +63,14 @@ #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) + #define MMCIRESPCMD 0x010 #define MMCIRESPONSE0 0x014 #define MMCIRESPONSE1 0x018 -- cgit v1.2.3-59-g8ed1b From 6adb2a804a943a348eadfb09266571cd4557387c Mon Sep 17 00:00:00 2001 From: Srinivas Kandagatla Date: Mon, 2 Jun 2014 10:08:57 +0100 Subject: mmc: mmci: Add enough delay between writes to CMD register. On Qcom SD Card controller POWER, CLKCTRL, DATACTRL and COMMAND registers should be updated in MCLK domain, and writes to these registers must be separated by three MCLK cycles. This resitriction is not applicable for other registers. Any subsequent writes to these register will be ignored until 3 MCLK have passed. One usec delay between two CMD register writes is not sufficient in the card identification phase where the CCLK is very low. This patch replaces a static 1 usec delay to use mmci_reg_delay function which can provide correct delay depending on the cclk frequency. Without this patch the card is not detected. Signed-off-by: Srinivas Kandagatla Reviewed-by: Linus Walleij Signed-off-by: Ulf Hansson --- drivers/mmc/host/mmci.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/mmc/host/mmci.c b/drivers/mmc/host/mmci.c index c67120bf2e93..6409f31d328c 100644 --- a/drivers/mmc/host/mmci.c +++ b/drivers/mmc/host/mmci.c @@ -812,7 +812,7 @@ mmci_start_command(struct mmci_host *host, struct mmc_command *cmd, u32 c) if (readl(base + MMCICOMMAND) & MCI_CPSM_ENABLE) { writel(0, base + MMCICOMMAND); - udelay(1); + mmci_reg_delay(host); } c |= cmd->opcode | MCI_CPSM_ENABLE; -- cgit v1.2.3-59-g8ed1b From ff783233cc13eca99a3b59c0609c1ffc6164b7a2 Mon Sep 17 00:00:00 2001 From: Srinivas Kandagatla Date: Mon, 2 Jun 2014 10:09:06 +0100 Subject: mmc: mmci: Add Qcom datactrl register variant Instance of this IP on Qualcomm's SOCs has bit different layout for datactrl register. Bit position datactrl[16:4] hold the true block size instead of power of 2. Signed-off-by: Srinivas Kandagatla Reviewed-by: Linus Walleij Signed-off-by: Ulf Hansson --- drivers/mmc/host/mmci.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/drivers/mmc/host/mmci.c b/drivers/mmc/host/mmci.c index 6409f31d328c..714b96818196 100644 --- a/drivers/mmc/host/mmci.c +++ b/drivers/mmc/host/mmci.c @@ -60,6 +60,8 @@ static unsigned int fmax = 515633; * @sdio: variant supports SDIO * @st_clkdiv: true if using a ST-specific clock divider algorithm * @blksz_datactrl16: true if Block size is at b16..b30 position in datactrl register + * @blksz_datactrl4: true if Block size is at b4..b16 position in datactrl + * register * @pwrreg_powerup: power up value for MMCIPOWER register * @signal_direction: input/out direction of bus signals can be indicated * @pwrreg_clkgate: MMCIPOWER register must be used to gate the clock @@ -75,6 +77,7 @@ struct variant_data { bool sdio; bool st_clkdiv; bool blksz_datactrl16; + bool blksz_datactrl4; u32 pwrreg_powerup; bool signal_direction; bool pwrreg_clkgate; @@ -732,6 +735,8 @@ static void mmci_start_data(struct mmci_host *host, struct mmc_data *data) if (variant->blksz_datactrl16) datactrl = MCI_DPSM_ENABLE | (data->blksz << 16); + else if (variant->blksz_datactrl4) + datactrl = MCI_DPSM_ENABLE | (data->blksz << 4); else datactrl = MCI_DPSM_ENABLE | blksz_bits << 4; -- cgit v1.2.3-59-g8ed1b From e17dca2b2df30676adbbc18a1022884dc73187a5 Mon Sep 17 00:00:00 2001 From: Srinivas Kandagatla Date: Mon, 2 Jun 2014 10:09:15 +0100 Subject: mmc: mmci: add ddrmode mask to variant data This patch adds ddrmode mask to variant structure giving more flexibility to the driver to support more SOCs which have different datactrl register layout. Without this patch datactrl register is updated with incorrect ddrmode mask, resulting in failures on Qualcomm SD Card Controller. Signed-off-by: Srinivas Kandagatla Reviewed-by: Linus Walleij [Ulf Hansson] Resolved conflict Signed-off-by: Ulf Hansson --- drivers/mmc/host/mmci.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/drivers/mmc/host/mmci.c b/drivers/mmc/host/mmci.c index 714b96818196..187dae143cc1 100644 --- a/drivers/mmc/host/mmci.c +++ b/drivers/mmc/host/mmci.c @@ -59,6 +59,7 @@ static unsigned int fmax = 515633; * is asserted (likewise for RX) * @sdio: variant supports SDIO * @st_clkdiv: true if using a ST-specific clock divider algorithm + * @datactrl_mask_ddrmode: ddr mode mask in datactrl register. * @blksz_datactrl16: true if Block size is at b16..b30 position in datactrl register * @blksz_datactrl4: true if Block size is at b4..b16 position in datactrl * register @@ -74,6 +75,7 @@ struct variant_data { unsigned int datalength_bits; unsigned int fifosize; unsigned int fifohalfsize; + unsigned int datactrl_mask_ddrmode; bool sdio; bool st_clkdiv; bool blksz_datactrl16; @@ -152,6 +154,7 @@ static struct variant_data variant_ux500v2 = { .fifohalfsize = 8 * 4, .clkreg = MCI_CLK_ENABLE, .clkreg_enable = MCI_ST_UX500_HWFCEN, + .datactrl_mask_ddrmode = MCI_ST_DPSM_DDRMODE, .datalength_bits = 24, .sdio = true, .st_clkdiv = true, @@ -772,7 +775,7 @@ static void mmci_start_data(struct mmci_host *host, struct mmc_data *data) if (host->mmc->ios.timing == MMC_TIMING_UHS_DDR50 || host->mmc->ios.timing == MMC_TIMING_MMC_DDR52) - datactrl |= MCI_ST_DPSM_DDRMODE; + datactrl |= variant->datactrl_mask_ddrmode; /* * Attempt to use DMA operation mode, if this -- cgit v1.2.3-59-g8ed1b From e1412d85a8e50d2b22970df29a804477582a6637 Mon Sep 17 00:00:00 2001 From: Srinivas Kandagatla Date: Mon, 2 Jun 2014 10:09:23 +0100 Subject: mmc: mmci: add 8bit bus support in variant data This patch adds 8bit bus enable to variant structure giving more flexibility to the driver to support more SOCs which have different clock register layout. Without this patch other new SOCs like Qcom will have to add more code to special case them. Signed-off-by: Srinivas Kandagatla Reviewed-by: Linus Walleij [Ulf Hansson] Resolved conflict Signed-off-by: Ulf Hansson --- drivers/mmc/host/mmci.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/drivers/mmc/host/mmci.c b/drivers/mmc/host/mmci.c index 187dae143cc1..2fb893313044 100644 --- a/drivers/mmc/host/mmci.c +++ b/drivers/mmc/host/mmci.c @@ -52,6 +52,7 @@ static unsigned int fmax = 515633; * struct variant_data - MMCI variant-specific quirks * @clkreg: default value for MCICLOCK register * @clkreg_enable: enable value for MMCICLOCK register + * @clkreg_8bit_bus_enable: enable value for 8 bit bus * @datalength_bits: number of bits in the MMCIDATALENGTH register * @fifosize: number of bytes that can be written when MMCI_TXFIFOEMPTY * is asserted (likewise for RX) @@ -72,6 +73,7 @@ static unsigned int fmax = 515633; struct variant_data { unsigned int clkreg; unsigned int clkreg_enable; + unsigned int clkreg_8bit_bus_enable; unsigned int datalength_bits; unsigned int fifosize; unsigned int fifohalfsize; @@ -113,6 +115,7 @@ static struct variant_data variant_u300 = { .fifosize = 16 * 4, .fifohalfsize = 8 * 4, .clkreg_enable = MCI_ST_U300_HWFCEN, + .clkreg_8bit_bus_enable = MCI_ST_8BIT_BUS, .datalength_bits = 16, .sdio = true, .pwrreg_powerup = MCI_PWR_ON, @@ -139,6 +142,7 @@ static struct variant_data variant_ux500 = { .fifohalfsize = 8 * 4, .clkreg = MCI_CLK_ENABLE, .clkreg_enable = MCI_ST_UX500_HWFCEN, + .clkreg_8bit_bus_enable = MCI_ST_8BIT_BUS, .datalength_bits = 24, .sdio = true, .st_clkdiv = true, @@ -154,6 +158,7 @@ static struct variant_data variant_ux500v2 = { .fifohalfsize = 8 * 4, .clkreg = MCI_CLK_ENABLE, .clkreg_enable = MCI_ST_UX500_HWFCEN, + .clkreg_8bit_bus_enable = MCI_ST_8BIT_BUS, .datactrl_mask_ddrmode = MCI_ST_DPSM_DDRMODE, .datalength_bits = 24, .sdio = true, @@ -305,7 +310,7 @@ static void mmci_set_clkreg(struct mmci_host *host, unsigned int desired) if (host->mmc->ios.bus_width == MMC_BUS_WIDTH_4) clk |= MCI_4BIT_BUS; if (host->mmc->ios.bus_width == MMC_BUS_WIDTH_8) - clk |= MCI_ST_8BIT_BUS; + clk |= variant->clkreg_8bit_bus_enable; if (host->mmc->ios.timing == MMC_TIMING_UHS_DDR50 || host->mmc->ios.timing == MMC_TIMING_MMC_DDR52) -- cgit v1.2.3-59-g8ed1b From e8740644ab5f906e131596d7580701b2ca855210 Mon Sep 17 00:00:00 2001 From: Srinivas Kandagatla Date: Mon, 2 Jun 2014 10:09:30 +0100 Subject: mmc: mmci: add edge support to data and command out in variant data. This patch adds edge support for data and command out to variant structure giving more flexibility to the driver to support more SOCs which have different clock register layout. Without this patch other new SOCs like Qcom will have to add more code to special case them Signed-off-by: Srinivas Kandagatla Reviewed-by: Linus Walleij [Ulf Hansson] Resolved conflict Signed-off-by: Ulf Hansson --- drivers/mmc/host/mmci.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/drivers/mmc/host/mmci.c b/drivers/mmc/host/mmci.c index 2fb893313044..5228c5de334d 100644 --- a/drivers/mmc/host/mmci.c +++ b/drivers/mmc/host/mmci.c @@ -53,6 +53,7 @@ static unsigned int fmax = 515633; * @clkreg: default value for MCICLOCK register * @clkreg_enable: enable value for MMCICLOCK register * @clkreg_8bit_bus_enable: enable value for 8 bit bus + * @clkreg_neg_edge_enable: enable value for inverted data/cmd output * @datalength_bits: number of bits in the MMCIDATALENGTH register * @fifosize: number of bytes that can be written when MMCI_TXFIFOEMPTY * is asserted (likewise for RX) @@ -74,6 +75,7 @@ struct variant_data { unsigned int clkreg; unsigned int clkreg_enable; unsigned int clkreg_8bit_bus_enable; + unsigned int clkreg_neg_edge_enable; unsigned int datalength_bits; unsigned int fifosize; unsigned int fifohalfsize; @@ -143,6 +145,7 @@ static struct variant_data variant_ux500 = { .clkreg = MCI_CLK_ENABLE, .clkreg_enable = MCI_ST_UX500_HWFCEN, .clkreg_8bit_bus_enable = MCI_ST_8BIT_BUS, + .clkreg_neg_edge_enable = MCI_ST_UX500_NEG_EDGE, .datalength_bits = 24, .sdio = true, .st_clkdiv = true, @@ -159,6 +162,7 @@ static struct variant_data variant_ux500v2 = { .clkreg = MCI_CLK_ENABLE, .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, .datalength_bits = 24, .sdio = true, @@ -314,7 +318,7 @@ static void mmci_set_clkreg(struct mmci_host *host, unsigned int desired) if (host->mmc->ios.timing == MMC_TIMING_UHS_DDR50 || host->mmc->ios.timing == MMC_TIMING_MMC_DDR52) - clk |= MCI_ST_UX500_NEG_EDGE; + clk |= variant->clkreg_neg_edge_enable; mmci_write_clkreg(host, clk); } -- cgit v1.2.3-59-g8ed1b From ae7b0061f61e7c96884f4080b4e28544a0cedd76 Mon Sep 17 00:00:00 2001 From: Srinivas Kandagatla Date: Mon, 2 Jun 2014 10:09:39 +0100 Subject: mmc: mmci: Add support to data commands via variant structure. On some SOCs like Qcom there are explicit bits in the command register to specify if its a data transfer command or not. So this patch adds support to such bits in variant data, giving more flexibility to the driver. Signed-off-by: Srinivas Kandagatla Reviewed-by: Linus Walleij Signed-off-by: Ulf Hansson --- drivers/mmc/host/mmci.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/drivers/mmc/host/mmci.c b/drivers/mmc/host/mmci.c index 5228c5de334d..0a8cdac1f59c 100644 --- a/drivers/mmc/host/mmci.c +++ b/drivers/mmc/host/mmci.c @@ -59,6 +59,7 @@ static unsigned int fmax = 515633; * is asserted (likewise for RX) * @fifohalfsize: number of bytes that can be written when MCI_TXFIFOHALFEMPTY * is asserted (likewise for RX) + * @data_cmd_enable: enable value for data commands. * @sdio: variant supports SDIO * @st_clkdiv: true if using a ST-specific clock divider algorithm * @datactrl_mask_ddrmode: ddr mode mask in datactrl register. @@ -79,6 +80,7 @@ struct variant_data { unsigned int datalength_bits; unsigned int fifosize; unsigned int fifohalfsize; + unsigned int data_cmd_enable; unsigned int datactrl_mask_ddrmode; bool sdio; bool st_clkdiv; @@ -841,6 +843,9 @@ mmci_start_command(struct mmci_host *host, struct mmc_command *cmd, u32 c) if (/*interrupt*/0) c |= MCI_CPSM_INTERRUPT; + if (mmc_cmd_type(cmd) == MMC_CMD_ADTC) + c |= host->variant->data_cmd_enable; + host->cmd = cmd; writel(cmd->arg, base + MMCIARGUMENT); -- cgit v1.2.3-59-g8ed1b From dc6500bfe889321f7f4fd01e96062a80643d81c1 Mon Sep 17 00:00:00 2001 From: Srinivas Kandagatla Date: Mon, 2 Jun 2014 10:09:47 +0100 Subject: mmc: mmci: add f_max to variant structure Some of the controller have maximum supported frequency, This patch adds support in variant data structure to specify such restrictions. This gives more flexibility in calculating the f_max before passing it to mmc-core. Signed-off-by: Srinivas Kandagatla Reviewed-by: Linus Walleij Signed-off-by: Ulf Hansson --- drivers/mmc/host/mmci.c | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/drivers/mmc/host/mmci.c b/drivers/mmc/host/mmci.c index 0a8cdac1f59c..b588810fd1a4 100644 --- a/drivers/mmc/host/mmci.c +++ b/drivers/mmc/host/mmci.c @@ -67,6 +67,7 @@ static unsigned int fmax = 515633; * @blksz_datactrl4: true if Block size is at b4..b16 position in datactrl * register * @pwrreg_powerup: power up value for MMCIPOWER register + * @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 @@ -87,6 +88,7 @@ struct variant_data { bool blksz_datactrl16; bool blksz_datactrl4; u32 pwrreg_powerup; + u32 f_max; bool signal_direction; bool pwrreg_clkgate; bool busy_detect; @@ -98,6 +100,7 @@ static struct variant_data variant_arm = { .fifohalfsize = 8 * 4, .datalength_bits = 16, .pwrreg_powerup = MCI_PWR_UP, + .f_max = 100000000, }; static struct variant_data variant_arm_extended_fifo = { @@ -105,6 +108,7 @@ static struct variant_data variant_arm_extended_fifo = { .fifohalfsize = 64 * 4, .datalength_bits = 16, .pwrreg_powerup = MCI_PWR_UP, + .f_max = 100000000, }; static struct variant_data variant_arm_extended_fifo_hwfc = { @@ -113,6 +117,7 @@ static struct variant_data variant_arm_extended_fifo_hwfc = { .clkreg_enable = MCI_ARM_HWFCEN, .datalength_bits = 16, .pwrreg_powerup = MCI_PWR_UP, + .f_max = 100000000, }; static struct variant_data variant_u300 = { @@ -123,6 +128,7 @@ static struct variant_data variant_u300 = { .datalength_bits = 16, .sdio = true, .pwrreg_powerup = MCI_PWR_ON, + .f_max = 100000000, .signal_direction = true, .pwrreg_clkgate = true, .pwrreg_nopower = true, @@ -136,6 +142,7 @@ static struct variant_data variant_nomadik = { .sdio = true, .st_clkdiv = true, .pwrreg_powerup = MCI_PWR_ON, + .f_max = 100000000, .signal_direction = true, .pwrreg_clkgate = true, .pwrreg_nopower = true, @@ -152,6 +159,7 @@ static struct variant_data variant_ux500 = { .sdio = true, .st_clkdiv = true, .pwrreg_powerup = MCI_PWR_ON, + .f_max = 100000000, .signal_direction = true, .pwrreg_clkgate = true, .busy_detect = true, @@ -171,6 +179,7 @@ static struct variant_data variant_ux500v2 = { .st_clkdiv = true, .blksz_datactrl16 = true, .pwrreg_powerup = MCI_PWR_ON, + .f_max = 100000000, .signal_direction = true, .pwrreg_clkgate = true, .busy_detect = true, @@ -1473,8 +1482,8 @@ static int mmci_probe(struct amba_device *dev, * so we try to adjust the clock down to this, * (if possible). */ - if (host->mclk > 100000000) { - ret = clk_set_rate(host->clk, 100000000); + if (host->mclk > variant->f_max) { + ret = clk_set_rate(host->clk, variant->f_max); if (ret < 0) goto clk_disable; host->mclk = clk_get_rate(host->clk); -- cgit v1.2.3-59-g8ed1b From 3f4e6f7b9175e2914b82134c4a6a02825f4766db Mon Sep 17 00:00:00 2001 From: Srinivas Kandagatla Date: Mon, 2 Jun 2014 10:09:55 +0100 Subject: mmc: mmci: add explicit clk control On Controllers like Qcom SD card controller where cclk is mclk and mclk should be directly controlled by the driver. This patch adds support to control mclk directly in the driver, and also adds explicit_mclk_control flag in variant structure giving more flexibility to the driver. Signed-off-by: Srinivas Kandagatla Reviewed-by: Linus Walleij [Ulf Hansson] Fixed checkpatch warning Signed-off-by: Ulf Hansson --- drivers/mmc/host/mmci.c | 29 ++++++++++++++++++++++++++--- drivers/mmc/host/mmci.h | 2 ++ 2 files changed, 28 insertions(+), 3 deletions(-) diff --git a/drivers/mmc/host/mmci.c b/drivers/mmc/host/mmci.c index b588810fd1a4..2e6075fdce46 100644 --- a/drivers/mmc/host/mmci.c +++ b/drivers/mmc/host/mmci.c @@ -72,6 +72,7 @@ static unsigned int fmax = 515633; * @pwrreg_clkgate: MMCIPOWER register must be used to gate the clock * @busy_detect: true if busy detection on dat0 is supported * @pwrreg_nopower: bits in MMCIPOWER don't controls ext. power supply + * @explicit_mclk_control: enable explicit mclk control in driver. */ struct variant_data { unsigned int clkreg; @@ -93,6 +94,7 @@ struct variant_data { bool pwrreg_clkgate; bool busy_detect; bool pwrreg_nopower; + bool explicit_mclk_control; }; static struct variant_data variant_arm = { @@ -286,7 +288,9 @@ static void mmci_set_clkreg(struct mmci_host *host, unsigned int desired) host->cclk = 0; if (desired) { - if (desired >= host->mclk) { + if (variant->explicit_mclk_control) { + host->cclk = host->mclk; + } else if (desired >= host->mclk) { clk = MCI_CLK_BYPASS; if (variant->st_clkdiv) clk |= MCI_ST_UX500_NEG_EDGE; @@ -1327,6 +1331,17 @@ static void mmci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios) if (!ios->clock && variant->pwrreg_clkgate) pwr &= ~MCI_PWR_ON; + if (host->variant->explicit_mclk_control && + ios->clock != host->clock_cache) { + ret = clk_set_rate(host->clk, ios->clock); + if (ret < 0) + dev_err(mmc_dev(host->mmc), + "Error setting clock rate (%d)\n", ret); + else + host->mclk = clk_get_rate(host->clk); + } + host->clock_cache = ios->clock; + spin_lock_irqsave(&host->lock, flags); mmci_set_clkreg(host, ios->clock); @@ -1502,9 +1517,12 @@ static int mmci_probe(struct amba_device *dev, * The ARM and ST versions of the block have slightly different * clock divider equations which means that the minimum divider * differs too. + * on Qualcomm like controllers get the nearest minimum clock to 100Khz */ if (variant->st_clkdiv) mmc->f_min = DIV_ROUND_UP(host->mclk, 257); + else if (variant->explicit_mclk_control) + mmc->f_min = clk_round_rate(host->clk, 100000); else mmc->f_min = DIV_ROUND_UP(host->mclk, 512); /* @@ -1514,9 +1532,14 @@ static int mmci_probe(struct amba_device *dev, * the block, of course. */ if (mmc->f_max) - mmc->f_max = min(host->mclk, mmc->f_max); + mmc->f_max = variant->explicit_mclk_control ? + min(variant->f_max, mmc->f_max) : + min(host->mclk, mmc->f_max); else - mmc->f_max = min(host->mclk, fmax); + mmc->f_max = variant->explicit_mclk_control ? + fmax : min(host->mclk, fmax); + + dev_dbg(mmc_dev(mmc), "clocking block at %u Hz\n", mmc->f_max); /* Get regulators and the supported OCR mask */ diff --git a/drivers/mmc/host/mmci.h b/drivers/mmc/host/mmci.h index d38a99df1820..ef346170b241 100644 --- a/drivers/mmc/host/mmci.h +++ b/drivers/mmc/host/mmci.h @@ -208,6 +208,8 @@ struct mmci_host { spinlock_t lock; unsigned int mclk; + /* cached value of requested clk in set_ios */ + unsigned int clock_cache; unsigned int cclk; u32 pwr_reg; u32 pwr_reg_add; -- cgit v1.2.3-59-g8ed1b From 9c34b73deec147a042c4bd871a373f76af70e38c Mon Sep 17 00:00:00 2001 From: Srinivas Kandagatla Date: Mon, 2 Jun 2014 10:10:04 +0100 Subject: mmc: mmci: Add Qcom specific rx_fifocnt logic. MCIFIFOCNT register behaviour on Qcom chips is very different than the other pl180 integrations. MCIFIFOCNT register contains the number of words that are still waiting to be transferred through the FIFO. It keeps decrementing once the host CPU reads the MCIFIFO. With the existing logic and the MCIFIFOCNT behaviour, mmci_pio_read will loop forever, as the FIFOCNT register will always return transfer size before reading the FIFO. Also the data sheet states that "This register is only useful for debug purposes and should not be used for normal operation since it does not reflect data which may or may not be in the pipeline". This patch implements a qcom specific get_rx_fifocnt function which is implemented based on status register flags. Based on qcom_fifo flag in variant data structure, the corresponding get_rx_fifocnt function is selected. Signed-off-by: Srinivas Kandagatla Reviewed-by: Linus Walleij Signed-off-by: Ulf Hansson --- drivers/mmc/host/mmci.c | 30 ++++++++++++++++++++++++++++-- drivers/mmc/host/mmci.h | 1 + 2 files changed, 29 insertions(+), 2 deletions(-) diff --git a/drivers/mmc/host/mmci.c b/drivers/mmc/host/mmci.c index 2e6075fdce46..063136d89b42 100644 --- a/drivers/mmc/host/mmci.c +++ b/drivers/mmc/host/mmci.c @@ -73,6 +73,7 @@ static unsigned int fmax = 515633; * @busy_detect: true if busy detection on dat0 is supported * @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. */ struct variant_data { unsigned int clkreg; @@ -95,6 +96,7 @@ struct variant_data { bool busy_detect; bool pwrreg_nopower; bool explicit_mclk_control; + bool qcom_fifo; }; static struct variant_data variant_arm = { @@ -992,15 +994,34 @@ mmci_cmd_irq(struct mmci_host *host, struct mmc_command *cmd, } } +static int mmci_get_rx_fifocnt(struct mmci_host *host, u32 status, int remain) +{ + return remain - (readl(host->base + MMCIFIFOCNT) << 2); +} + +static int mmci_qcom_get_rx_fifocnt(struct mmci_host *host, u32 status, int r) +{ + /* + * on qcom SDCC4 only 8 words are used in each burst so only 8 addresses + * from the fifo range should be used + */ + if (status & MCI_RXFIFOHALFFULL) + return host->variant->fifohalfsize; + else if (status & MCI_RXDATAAVLBL) + return 4; + + return 0; +} + static int mmci_pio_read(struct mmci_host *host, char *buffer, unsigned int remain) { void __iomem *base = host->base; char *ptr = buffer; - u32 status; + u32 status = readl(host->base + MMCISTATUS); int host_remain = host->size; do { - int count = host_remain - (readl(base + MMCIFIFOCNT) << 2); + int count = host->get_rx_fifocnt(host, status, host_remain); if (count > remain) count = remain; @@ -1489,6 +1510,11 @@ static int mmci_probe(struct amba_device *dev, if (ret) goto host_free; + if (variant->qcom_fifo) + host->get_rx_fifocnt = mmci_qcom_get_rx_fifocnt; + else + host->get_rx_fifocnt = mmci_get_rx_fifocnt; + host->plat = plat; host->variant = variant; host->mclk = clk_get_rate(host->clk); diff --git a/drivers/mmc/host/mmci.h b/drivers/mmc/host/mmci.h index ef346170b241..a1f5e4f49e2a 100644 --- a/drivers/mmc/host/mmci.h +++ b/drivers/mmc/host/mmci.h @@ -229,6 +229,7 @@ struct mmci_host { /* pio stuff */ struct sg_mapping_iter sg_miter; unsigned int size; + int (*get_rx_fifocnt)(struct mmci_host *h, u32 status, int remain); #ifdef CONFIG_DMA_ENGINE /* DMA stuff */ -- cgit v1.2.3-59-g8ed1b From 55b604ae4b50023356064f43c24ea896ee8e400a Mon Sep 17 00:00:00 2001 From: Srinivas Kandagatla Date: Mon, 2 Jun 2014 10:10:13 +0100 Subject: mmc: mmci: Add Qualcomm Id to amba id table This patch adds a fake Qualcomm ID 0x00051180 to the amba_ids, as Qualcomm SDCC controller is pl180, but amba id registers read 0x0's. The plan is to remove SDCC driver totally and use mmci as the main SD controller driver for Qualcomm SOCs. Signed-off-by: Srinivas Kandagatla Reviewed-by: Linus Walleij Signed-off-by: Ulf Hansson --- drivers/mmc/host/mmci.c | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/drivers/mmc/host/mmci.c b/drivers/mmc/host/mmci.c index 063136d89b42..6483c5cc6735 100644 --- a/drivers/mmc/host/mmci.c +++ b/drivers/mmc/host/mmci.c @@ -190,6 +190,23 @@ static struct variant_data variant_ux500v2 = { .pwrreg_nopower = true, }; +static struct variant_data variant_qcom = { + .fifosize = 16 * 4, + .fifohalfsize = 8 * 4, + .clkreg = MCI_CLK_ENABLE, + .clkreg_enable = MCI_QCOM_CLK_FLOWENA | + 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, + .blksz_datactrl4 = true, + .datalength_bits = 24, + .pwrreg_powerup = MCI_PWR_UP, + .f_max = 208000000, + .explicit_mclk_control = true, + .qcom_fifo = true, +}; + static int mmci_card_busy(struct mmc_host *mmc) { struct mmci_host *host = mmc_priv(mmc); @@ -1832,6 +1849,12 @@ static struct amba_id mmci_ids[] = { .mask = 0xf0ffffff, .data = &variant_ux500v2, }, + /* Qualcomm variants */ + { + .id = 0x00051180, + .mask = 0x000fffff, + .data = &variant_qcom, + }, { 0, 0 }, }; -- cgit v1.2.3-59-g8ed1b From 2e42da59804aa57903393dca2d234561e6db41a0 Mon Sep 17 00:00:00 2001 From: Ulf Hansson Date: Wed, 28 May 2014 11:44:05 +0200 Subject: mmc: core: Remove redundant runtime_idle callback The runtime PM core handles a runtime_idle callback set to NULL as one returning 0. So, let's just set it to NULL instead. Signed-off-by: Ulf Hansson Acked-by: Jaehoon Chung --- drivers/mmc/core/bus.c | 10 +--------- 1 file changed, 1 insertion(+), 9 deletions(-) diff --git a/drivers/mmc/core/bus.c b/drivers/mmc/core/bus.c index d2dbf02022bd..8a1f1240e058 100644 --- a/drivers/mmc/core/bus.c +++ b/drivers/mmc/core/bus.c @@ -180,7 +180,6 @@ static int mmc_bus_resume(struct device *dev) #endif #ifdef CONFIG_PM_RUNTIME - static int mmc_runtime_suspend(struct device *dev) { struct mmc_card *card = mmc_dev_to_card(dev); @@ -196,17 +195,10 @@ static int mmc_runtime_resume(struct device *dev) return host->bus_ops->runtime_resume(host); } - -static int mmc_runtime_idle(struct device *dev) -{ - return 0; -} - #endif /* !CONFIG_PM_RUNTIME */ static const struct dev_pm_ops mmc_bus_pm_ops = { - SET_RUNTIME_PM_OPS(mmc_runtime_suspend, mmc_runtime_resume, - mmc_runtime_idle) + SET_RUNTIME_PM_OPS(mmc_runtime_suspend, mmc_runtime_resume, NULL) SET_SYSTEM_SLEEP_PM_OPS(mmc_bus_suspend, mmc_bus_resume) }; -- cgit v1.2.3-59-g8ed1b From 3a48edc4bd68f841c07c7bc86358d2f02133f247 Mon Sep 17 00:00:00 2001 From: Tim Kryger Date: Fri, 13 Jun 2014 10:13:56 -0700 Subject: mmc: sdhci: Use mmc core regulator infrastucture Switch the common SDHCI code over to use mmc_host's regulator pointers and remove the ones in the sdhci_host structure. Additionally, use the common mmc_regulator_get_supply function to get the regulators and set the ocr_avail mask. This change sets the ocr_avail directly based upon the voltage ranges supported which ensures ocr_avail is set correctly while allowing the use of regulators that can't provide exactly 1.8v, 3.0v, or 3.3v. Signed-off-by: Tim Kryger Signed-off-by: Markus Mayer Reviewed-by: Matt Porter Signed-off-by: Ulf Hansson --- drivers/mmc/host/sdhci.c | 97 ++++++++++++++++++----------------------------- include/linux/mmc/sdhci.h | 3 -- 2 files changed, 36 insertions(+), 64 deletions(-) diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c index 47055f3f01b8..ee524b06db14 100644 --- a/drivers/mmc/host/sdhci.c +++ b/drivers/mmc/host/sdhci.c @@ -1223,6 +1223,7 @@ EXPORT_SYMBOL_GPL(sdhci_set_clock); static void sdhci_set_power(struct sdhci_host *host, unsigned char mode, unsigned short vdd) { + struct mmc_host *mmc = host->mmc; u8 pwr = 0; if (mode != MMC_POWER_OFF) { @@ -1284,9 +1285,9 @@ static void sdhci_set_power(struct sdhci_host *host, unsigned char mode, mdelay(10); } - if (host->vmmc) { + if (!IS_ERR(mmc->supply.vmmc)) { spin_unlock_irq(&host->lock); - mmc_regulator_set_ocr(host->mmc, host->vmmc, vdd); + mmc_regulator_set_ocr(host->mmc, mmc->supply.vmmc, vdd); spin_lock_irq(&host->lock); } } @@ -1440,13 +1441,15 @@ static void sdhci_do_set_ios(struct sdhci_host *host, struct mmc_ios *ios) { unsigned long flags; u8 ctrl; + struct mmc_host *mmc = host->mmc; spin_lock_irqsave(&host->lock, flags); if (host->flags & SDHCI_DEVICE_DEAD) { spin_unlock_irqrestore(&host->lock, flags); - if (host->vmmc && ios->power_mode == MMC_POWER_OFF) - mmc_regulator_set_ocr(host->mmc, host->vmmc, 0); + if (!IS_ERR(mmc->supply.vmmc) && + ios->power_mode == MMC_POWER_OFF) + mmc_regulator_set_ocr(host->mmc, mmc->supply.vmmc, 0); return; } @@ -1707,6 +1710,7 @@ static void sdhci_enable_sdio_irq(struct mmc_host *mmc, int enable) static int sdhci_do_start_signal_voltage_switch(struct sdhci_host *host, struct mmc_ios *ios) { + struct mmc_host *mmc = host->mmc; u16 ctrl; int ret; @@ -1725,8 +1729,9 @@ static int sdhci_do_start_signal_voltage_switch(struct sdhci_host *host, ctrl &= ~SDHCI_CTRL_VDD_180; sdhci_writew(host, ctrl, SDHCI_HOST_CONTROL2); - if (host->vqmmc) { - ret = regulator_set_voltage(host->vqmmc, 2700000, 3600000); + if (!IS_ERR(mmc->supply.vqmmc)) { + ret = regulator_set_voltage(mmc->supply.vqmmc, 2700000, + 3600000); if (ret) { pr_warning("%s: Switching to 3.3V signalling voltage " " failed\n", mmc_hostname(host->mmc)); @@ -1746,8 +1751,8 @@ static int sdhci_do_start_signal_voltage_switch(struct sdhci_host *host, return -EAGAIN; case MMC_SIGNAL_VOLTAGE_180: - if (host->vqmmc) { - ret = regulator_set_voltage(host->vqmmc, + if (!IS_ERR(mmc->supply.vqmmc)) { + ret = regulator_set_voltage(mmc->supply.vqmmc, 1700000, 1950000); if (ret) { pr_warning("%s: Switching to 1.8V signalling voltage " @@ -1776,8 +1781,9 @@ static int sdhci_do_start_signal_voltage_switch(struct sdhci_host *host, return -EAGAIN; case MMC_SIGNAL_VOLTAGE_120: - if (host->vqmmc) { - ret = regulator_set_voltage(host->vqmmc, 1100000, 1300000); + if (!IS_ERR(mmc->supply.vqmmc)) { + ret = regulator_set_voltage(mmc->supply.vqmmc, 1100000, + 1300000); if (ret) { pr_warning("%s: Switching to 1.2V signalling voltage " " failed\n", mmc_hostname(host->mmc)); @@ -2962,25 +2968,22 @@ int sdhci_add_host(struct sdhci_host *host) !(host->mmc->caps & MMC_CAP_NONREMOVABLE)) mmc->caps |= MMC_CAP_NEEDS_POLL; + /* If there are external regulators, get them */ + if (mmc_regulator_get_supply(mmc) == -EPROBE_DEFER) + return -EPROBE_DEFER; + /* If vqmmc regulator and no 1.8V signalling, then there's no UHS */ - host->vqmmc = regulator_get_optional(mmc_dev(mmc), "vqmmc"); - if (IS_ERR_OR_NULL(host->vqmmc)) { - if (PTR_ERR(host->vqmmc) < 0) { - pr_info("%s: no vqmmc regulator found\n", - mmc_hostname(mmc)); - host->vqmmc = NULL; - } - } else { - ret = regulator_enable(host->vqmmc); - if (!regulator_is_supported_voltage(host->vqmmc, 1700000, - 1950000)) + if (!IS_ERR(mmc->supply.vqmmc)) { + ret = regulator_enable(mmc->supply.vqmmc); + if (!regulator_is_supported_voltage(mmc->supply.vqmmc, 1700000, + 1950000)) caps[1] &= ~(SDHCI_SUPPORT_SDR104 | SDHCI_SUPPORT_SDR50 | SDHCI_SUPPORT_DDR50); if (ret) { pr_warn("%s: Failed to enable vqmmc regulator: %d\n", mmc_hostname(mmc), ret); - host->vqmmc = NULL; + mmc->supply.vqmmc = NULL; } } @@ -3041,34 +3044,6 @@ int sdhci_add_host(struct sdhci_host *host) ocr_avail = 0; - host->vmmc = regulator_get_optional(mmc_dev(mmc), "vmmc"); - if (IS_ERR_OR_NULL(host->vmmc)) { - if (PTR_ERR(host->vmmc) < 0) { - pr_info("%s: no vmmc regulator found\n", - mmc_hostname(mmc)); - host->vmmc = NULL; - } - } - -#ifdef CONFIG_REGULATOR - /* - * Voltage range check makes sense only if regulator reports - * any voltage value. - */ - if (host->vmmc && regulator_get_voltage(host->vmmc) > 0) { - ret = regulator_is_supported_voltage(host->vmmc, 2700000, - 3600000); - if ((ret <= 0) || (!(caps[0] & SDHCI_CAN_VDD_330))) - caps[0] &= ~SDHCI_CAN_VDD_330; - if ((ret <= 0) || (!(caps[0] & SDHCI_CAN_VDD_300))) - caps[0] &= ~SDHCI_CAN_VDD_300; - ret = regulator_is_supported_voltage(host->vmmc, 1700000, - 1950000); - if ((ret <= 0) || (!(caps[0] & SDHCI_CAN_VDD_180))) - caps[0] &= ~SDHCI_CAN_VDD_180; - } -#endif /* CONFIG_REGULATOR */ - /* * According to SD Host Controller spec v3.00, if the Host System * can afford more than 150mA, Host Driver should set XPC to 1. Also @@ -3077,8 +3052,8 @@ int sdhci_add_host(struct sdhci_host *host) * value. */ max_current_caps = sdhci_readl(host, SDHCI_MAX_CURRENT); - if (!max_current_caps && host->vmmc) { - u32 curr = regulator_get_current_limit(host->vmmc); + if (!max_current_caps && !IS_ERR(mmc->supply.vmmc)) { + u32 curr = regulator_get_current_limit(mmc->supply.vmmc); if (curr > 0) { /* convert to SDHCI_MAX_CURRENT format */ @@ -3118,8 +3093,11 @@ int sdhci_add_host(struct sdhci_host *host) SDHCI_MAX_CURRENT_MULTIPLIER; } + if (mmc->ocr_avail) + ocr_avail &= mmc->ocr_avail; + if (host->ocr_mask) - ocr_avail = host->ocr_mask; + ocr_avail &= host->ocr_mask; mmc->ocr_avail = ocr_avail; mmc->ocr_avail_sdio = ocr_avail; @@ -3273,6 +3251,7 @@ EXPORT_SYMBOL_GPL(sdhci_add_host); void sdhci_remove_host(struct sdhci_host *host, int dead) { + struct mmc_host *mmc = host->mmc; unsigned long flags; if (dead) { @@ -3310,15 +3289,11 @@ void sdhci_remove_host(struct sdhci_host *host, int dead) tasklet_kill(&host->finish_tasklet); - if (host->vmmc) { - regulator_disable(host->vmmc); - regulator_put(host->vmmc); - } + if (!IS_ERR(mmc->supply.vmmc)) + regulator_disable(mmc->supply.vmmc); - if (host->vqmmc) { - regulator_disable(host->vqmmc); - regulator_put(host->vqmmc); - } + if (!IS_ERR(mmc->supply.vqmmc)) + regulator_disable(mmc->supply.vqmmc); if (host->adma_desc) dma_free_coherent(mmc_dev(host->mmc), ADMA_SIZE, diff --git a/include/linux/mmc/sdhci.h b/include/linux/mmc/sdhci.h index 08abe9941884..09ebe57d5ce9 100644 --- a/include/linux/mmc/sdhci.h +++ b/include/linux/mmc/sdhci.h @@ -104,9 +104,6 @@ struct sdhci_host { const struct sdhci_ops *ops; /* Low level hw interface */ - struct regulator *vmmc; /* Power regulator (vmmc) */ - struct regulator *vqmmc; /* Signaling regulator (vccq) */ - /* Internal data */ struct mmc_host *mmc; /* MMC structure */ u64 dma_mask; /* custom DMA mask */ -- cgit v1.2.3-59-g8ed1b From 8a125badbaf65d37cf09a866d1c8d496527da58b Mon Sep 17 00:00:00 2001 From: Markus Pargmann Date: Wed, 4 Jun 2014 15:24:29 +0200 Subject: mmc: sdhci: Remove unused ret variables Remove those unused ret variables to make it obvious that these function will not return any errors in the current implementation. Signed-off-by: Markus Pargmann Signed-off-by: Ulf Hansson --- drivers/mmc/host/sdhci.c | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c index ee524b06db14..c23a87285a95 100644 --- a/drivers/mmc/host/sdhci.c +++ b/drivers/mmc/host/sdhci.c @@ -2649,7 +2649,6 @@ static void sdhci_runtime_pm_bus_off(struct sdhci_host *host) int sdhci_runtime_suspend_host(struct sdhci_host *host) { unsigned long flags; - int ret = 0; /* Disable tuning since we are suspending */ if (host->flags & SDHCI_USING_RETUNING_TIMER) { @@ -2669,14 +2668,14 @@ int sdhci_runtime_suspend_host(struct sdhci_host *host) host->runtime_suspended = true; spin_unlock_irqrestore(&host->lock, flags); - return ret; + return 0; } EXPORT_SYMBOL_GPL(sdhci_runtime_suspend_host); int sdhci_runtime_resume_host(struct sdhci_host *host) { unsigned long flags; - int ret = 0, host_flags = host->flags; + int host_flags = host->flags; if (host_flags & (SDHCI_USE_SDMA | SDHCI_USE_ADMA)) { if (host->ops->enable_dma) @@ -2715,7 +2714,7 @@ int sdhci_runtime_resume_host(struct sdhci_host *host) spin_unlock_irqrestore(&host->lock, flags); - return ret; + return 0; } EXPORT_SYMBOL_GPL(sdhci_runtime_resume_host); -- cgit v1.2.3-59-g8ed1b From ce7eb68875967902890c752b341dd2b92b0699de Mon Sep 17 00:00:00 2001 From: Ben Dooks Date: Wed, 4 Jun 2014 12:42:08 +0100 Subject: mmc: sh-mmcif: update to print version and bus clock rate on probe Change the initial print to show chip version and the bus rate it is working at instead of the driver version. This is more useful information as we already know which driver version from the kernel it is in. Signed-off-by: Ben Dooks Signed-off-by: Ulf Hansson --- drivers/mmc/host/sh_mmcif.c | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/drivers/mmc/host/sh_mmcif.c b/drivers/mmc/host/sh_mmcif.c index 656fbba4c422..dc9a28a29c18 100644 --- a/drivers/mmc/host/sh_mmcif.c +++ b/drivers/mmc/host/sh_mmcif.c @@ -1469,16 +1469,17 @@ static int sh_mmcif_probe(struct platform_device *pdev) mutex_init(&host->thread_lock); - clk_disable_unprepare(host->hclk); ret = mmc_add_host(mmc); if (ret < 0) goto emmcaddh; dev_pm_qos_expose_latency_limit(&pdev->dev, 100); - dev_info(&pdev->dev, "driver version %s\n", DRIVER_VERSION); - dev_dbg(&pdev->dev, "chip ver H'%04x\n", - sh_mmcif_readl(host->addr, MMCIF_CE_VERSION) & 0x0000ffff); + dev_info(&pdev->dev, "Chip version 0x%04x, clock rate %luMHz\n", + sh_mmcif_readl(host->addr, MMCIF_CE_VERSION) & 0xffff, + clk_get_rate(host->hclk) / 1000000UL); + + clk_disable_unprepare(host->hclk); return ret; emmcaddh: -- cgit v1.2.3-59-g8ed1b From 18f55fcc9d0e6f40fdc7f81afcbe950da30fbcf9 Mon Sep 17 00:00:00 2001 From: Ben Dooks Date: Wed, 4 Jun 2014 12:42:09 +0100 Subject: mmc: sh-mmcif: use devm_ for ioremap Start tidying the probe/release code by using devm_ioremap_resource() to map the IO registers. Signed-off-by: Ben Dooks Signed-off-by: Ulf Hansson --- drivers/mmc/host/sh_mmcif.c | 24 ++++++------------------ 1 file changed, 6 insertions(+), 18 deletions(-) diff --git a/drivers/mmc/host/sh_mmcif.c b/drivers/mmc/host/sh_mmcif.c index dc9a28a29c18..e25821fe4e9e 100644 --- a/drivers/mmc/host/sh_mmcif.c +++ b/drivers/mmc/host/sh_mmcif.c @@ -1378,22 +1378,15 @@ static int sh_mmcif_probe(struct platform_device *pdev) dev_err(&pdev->dev, "Get irq error\n"); return -ENXIO; } + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - if (!res) { - dev_err(&pdev->dev, "platform_get_resource error.\n"); - return -ENXIO; - } - reg = ioremap(res->start, resource_size(res)); - if (!reg) { - dev_err(&pdev->dev, "ioremap error.\n"); - return -ENOMEM; - } + reg = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(reg)) + return PTR_ERR(reg); mmc = mmc_alloc_host(sizeof(struct sh_mmcif_host), &pdev->dev); - if (!mmc) { - ret = -ENOMEM; - goto ealloch; - } + if (!mmc) + return -ENOMEM; ret = mmc_of_parse(mmc); if (ret < 0) @@ -1498,8 +1491,6 @@ eclkget: pm_runtime_disable(&pdev->dev); eofparse: mmc_free_host(mmc); -ealloch: - iounmap(reg); return ret; } @@ -1524,9 +1515,6 @@ static int sh_mmcif_remove(struct platform_device *pdev) */ cancel_delayed_work_sync(&host->timeout_work); - if (host->addr) - iounmap(host->addr); - irq[0] = platform_get_irq(pdev, 0); irq[1] = platform_get_irq(pdev, 1); -- cgit v1.2.3-59-g8ed1b From 46991005e1f13e061359335b86d7847562feffde Mon Sep 17 00:00:00 2001 From: Ben Dooks Date: Wed, 4 Jun 2014 12:42:10 +0100 Subject: mmc: sh-mmcif: use devm_ for clock management Use the devm_clk_get() code to get the clock and allow it to be freed automatically on release. Signed-off-by: Ben Dooks Signed-off-by: Ulf Hansson --- drivers/mmc/host/sh_mmcif.c | 18 ++++++++---------- 1 file changed, 8 insertions(+), 10 deletions(-) diff --git a/drivers/mmc/host/sh_mmcif.c b/drivers/mmc/host/sh_mmcif.c index e25821fe4e9e..ec9bda30da73 100644 --- a/drivers/mmc/host/sh_mmcif.c +++ b/drivers/mmc/host/sh_mmcif.c @@ -1390,7 +1390,7 @@ static int sh_mmcif_probe(struct platform_device *pdev) ret = mmc_of_parse(mmc); if (ret < 0) - goto eofparse; + goto err_host; host = mmc_priv(mmc); host->mmc = mmc; @@ -1420,19 +1420,19 @@ static int sh_mmcif_probe(struct platform_device *pdev) pm_runtime_enable(&pdev->dev); host->power = false; - host->hclk = clk_get(&pdev->dev, NULL); + host->hclk = devm_clk_get(&pdev->dev, NULL); if (IS_ERR(host->hclk)) { ret = PTR_ERR(host->hclk); dev_err(&pdev->dev, "cannot get clock: %d\n", ret); - goto eclkget; + goto err_pm; } ret = sh_mmcif_clk_update(host); if (ret < 0) - goto eclkupdate; + goto err_pm; ret = pm_runtime_resume(&pdev->dev); if (ret < 0) - goto eresume; + goto err_clk; INIT_DELAYED_WORK(&host->timeout_work, mmcif_timeout_work); @@ -1483,13 +1483,11 @@ ereqirq1: free_irq(irq[0], host); ereqirq0: pm_runtime_suspend(&pdev->dev); -eresume: +err_clk: clk_disable_unprepare(host->hclk); -eclkupdate: - clk_put(host->hclk); -eclkget: +err_pm: pm_runtime_disable(&pdev->dev); -eofparse: +err_host: mmc_free_host(mmc); return ret; } -- cgit v1.2.3-59-g8ed1b From 6f4789e6a81f4678b8ffa73a6226be0f99955190 Mon Sep 17 00:00:00 2001 From: Ben Dooks Date: Wed, 4 Jun 2014 12:42:11 +0100 Subject: mmc: sh-mmcif: use devm_ for irq management Use devm_request_threaded_irq() for the host interrupt handlers so we do not have to worry about freeing them on exit or error. Tidies up the exit path code for the driver. Signed-off-by: Ben Dooks Signed-off-by: Ulf Hansson --- drivers/mmc/host/sh_mmcif.c | 26 ++++++++------------------ 1 file changed, 8 insertions(+), 18 deletions(-) diff --git a/drivers/mmc/host/sh_mmcif.c b/drivers/mmc/host/sh_mmcif.c index ec9bda30da73..a0776cec070c 100644 --- a/drivers/mmc/host/sh_mmcif.c +++ b/drivers/mmc/host/sh_mmcif.c @@ -1440,17 +1440,19 @@ static int sh_mmcif_probe(struct platform_device *pdev) sh_mmcif_writel(host->addr, MMCIF_CE_INT_MASK, MASK_ALL); name = irq[1] < 0 ? dev_name(&pdev->dev) : "sh_mmc:error"; - ret = request_threaded_irq(irq[0], sh_mmcif_intr, sh_mmcif_irqt, 0, name, host); + ret = devm_request_threaded_irq(&pdev->dev, irq[0], sh_mmcif_intr, + sh_mmcif_irqt, 0, name, host); if (ret) { dev_err(&pdev->dev, "request_irq error (%s)\n", name); - goto ereqirq0; + goto err_irq; } if (irq[1] >= 0) { - ret = request_threaded_irq(irq[1], sh_mmcif_intr, sh_mmcif_irqt, - 0, "sh_mmc:int", host); + ret = devm_request_threaded_irq(&pdev->dev, irq[1], + sh_mmcif_intr, sh_mmcif_irqt, + 0, "sh_mmc:int", host); if (ret) { dev_err(&pdev->dev, "request_irq error (sh_mmc:int)\n"); - goto ereqirq1; + goto err_irq; } } @@ -1477,11 +1479,7 @@ static int sh_mmcif_probe(struct platform_device *pdev) emmcaddh: erqcd: - if (irq[1] >= 0) - free_irq(irq[1], host); -ereqirq1: - free_irq(irq[0], host); -ereqirq0: +err_irq: pm_runtime_suspend(&pdev->dev); err_clk: clk_disable_unprepare(host->hclk); @@ -1495,7 +1493,6 @@ err_host: static int sh_mmcif_remove(struct platform_device *pdev) { struct sh_mmcif_host *host = platform_get_drvdata(pdev); - int irq[2]; host->dying = true; clk_prepare_enable(host->hclk); @@ -1513,13 +1510,6 @@ static int sh_mmcif_remove(struct platform_device *pdev) */ cancel_delayed_work_sync(&host->timeout_work); - irq[0] = platform_get_irq(pdev, 0); - irq[1] = platform_get_irq(pdev, 1); - - free_irq(irq[0], host); - if (irq[1] >= 0) - free_irq(irq[1], host); - clk_disable_unprepare(host->hclk); mmc_free_host(host->mmc); pm_runtime_put_sync(&pdev->dev); -- cgit v1.2.3-59-g8ed1b From 11a808522af3a22a55ace9167670e18b22bce83d Mon Sep 17 00:00:00 2001 From: Ben Dooks Date: Wed, 4 Jun 2014 12:42:12 +0100 Subject: mmc: sh-mmcif: no need to call pm_runtime_suspend on error The pm_runtime call should implicitly disable the device once the probe is over if there is no explicit reference gained. There is no need to call pm_runtime_suspend() before the pm_runtime_disable() call. Signed-off-by: Ben Dooks Signed-off-by: Ulf Hansson --- drivers/mmc/host/sh_mmcif.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/drivers/mmc/host/sh_mmcif.c b/drivers/mmc/host/sh_mmcif.c index a0776cec070c..80e200e59dda 100644 --- a/drivers/mmc/host/sh_mmcif.c +++ b/drivers/mmc/host/sh_mmcif.c @@ -1444,7 +1444,7 @@ static int sh_mmcif_probe(struct platform_device *pdev) sh_mmcif_irqt, 0, name, host); if (ret) { dev_err(&pdev->dev, "request_irq error (%s)\n", name); - goto err_irq; + goto err_clk; } if (irq[1] >= 0) { ret = devm_request_threaded_irq(&pdev->dev, irq[1], @@ -1452,7 +1452,7 @@ static int sh_mmcif_probe(struct platform_device *pdev) 0, "sh_mmc:int", host); if (ret) { dev_err(&pdev->dev, "request_irq error (sh_mmc:int)\n"); - goto err_irq; + goto err_clk; } } @@ -1479,8 +1479,6 @@ static int sh_mmcif_probe(struct platform_device *pdev) emmcaddh: erqcd: -err_irq: - pm_runtime_suspend(&pdev->dev); err_clk: clk_disable_unprepare(host->hclk); err_pm: -- cgit v1.2.3-59-g8ed1b From 7f67f3a2ca4c1d2665b9fbe1684e29bbba2d156b Mon Sep 17 00:00:00 2001 From: Ben Dooks Date: Wed, 4 Jun 2014 12:42:13 +0100 Subject: mmc: sh-mmcif: final error path cleanup Remove the error path items that are no longer needed. The mmc card-detect code cleans up after itself (and registers with devm) and the host error is the same as the clock disable. Signed-off-by: Ben Dooks Signed-off-by: Ulf Hansson --- drivers/mmc/host/sh_mmcif.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/drivers/mmc/host/sh_mmcif.c b/drivers/mmc/host/sh_mmcif.c index 80e200e59dda..0289b4ecccb3 100644 --- a/drivers/mmc/host/sh_mmcif.c +++ b/drivers/mmc/host/sh_mmcif.c @@ -1459,14 +1459,14 @@ static int sh_mmcif_probe(struct platform_device *pdev) if (pd && pd->use_cd_gpio) { ret = mmc_gpio_request_cd(mmc, pd->cd_gpio, 0); if (ret < 0) - goto erqcd; + goto err_clk; } mutex_init(&host->thread_lock); ret = mmc_add_host(mmc); if (ret < 0) - goto emmcaddh; + goto err_clk; dev_pm_qos_expose_latency_limit(&pdev->dev, 100); @@ -1477,8 +1477,6 @@ static int sh_mmcif_probe(struct platform_device *pdev) clk_disable_unprepare(host->hclk); return ret; -emmcaddh: -erqcd: err_clk: clk_disable_unprepare(host->hclk); err_pm: -- cgit v1.2.3-59-g8ed1b From be19c40577ee74f00c1242d8fd4447524f38c102 Mon Sep 17 00:00:00 2001 From: Alexander Stein Date: Tue, 20 May 2014 13:29:21 +0200 Subject: mmc: quirks: Fixup debug message There is no need for an output like this: > mmcblk mmc1:0001: calling add_quirk_mmc+0x0/0x20 Instead use this one: > mmcblk mmc1:0001: calling add_quirk_mmc Signed-off-by: Alexander Stein Signed-off-by: Ulf Hansson --- drivers/mmc/core/quirks.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/mmc/core/quirks.c b/drivers/mmc/core/quirks.c index 6c36fccaa1ec..dd1d1e0fe322 100644 --- a/drivers/mmc/core/quirks.c +++ b/drivers/mmc/core/quirks.c @@ -91,7 +91,7 @@ void mmc_fixup_device(struct mmc_card *card, const struct mmc_fixup *table) (f->cis_device == card->cis.device || f->cis_device == (u16) SDIO_ANY_ID) && rev >= f->rev_start && rev <= f->rev_end) { - dev_dbg(&card->dev, "calling %pF\n", f->vendor_fixup); + dev_dbg(&card->dev, "calling %pf\n", f->vendor_fixup); f->vendor_fixup(card, f->data); } } -- cgit v1.2.3-59-g8ed1b From 2cd3a2a54656f9c480b1c7272fc07635d575841b Mon Sep 17 00:00:00 2001 From: Andreas Fenkart Date: Thu, 29 May 2014 10:28:00 +0200 Subject: mmc: omap_hsmmc: Enable SDIO interrupt There have been various patches floating around for enabling the SDIO IRQ for hsmmc, but none of them ever got merged. Probably the reason for not merging the SDIO interrupt patches has been the lack of wake-up path for SDIO on some omaps that has also needed remuxing the SDIO DAT1 line to a GPIO making the patches complex. This patch adds the minimal SDIO IRQ support to hsmmc for omaps that do have the wake-up path. For those omaps, the DAT1 line need to have the wake-up enable bit set, and the wake-up interrupt is the same as for the MMC controller. This patch has been tested on am3730 es1.2 with mwifiex connected to MMC3 with mwifiex waking to Ethernet traffic from off-idle mode. Note that for omaps that do not have the SDIO wake-up path, this patch will not work for idle modes and further patches for remuxing DAT1 to GPIO are needed. Based on earlier patches [1][2] by David Vrabel , Steve Sakoman For now, only support SDIO interrupt if we are booted with a separate wake-irq configued via device tree. This is because omaps need the wake-irq for idle states, and some omaps need special quirks. And we don't want to add new legacy mux platform init code callbacks any longer as we are moving to DT based booting anyways. To use it, you need to specify the wake-irq using the interrupts-extended property. [1] http://www.sakoman.com/cgi-bin/gitweb.cgi?p=linux.git;a=commitdiff_plain;h=010810d22f6f49ac03da4ba384969432e0320453 [2] http://comments.gmane.org/gmane.linux.kernel.mmc/20446 Acked-by: Balaji T K Signed-off-by: Andreas Fenkart Signed-off-by: Tony Lindgren Signed-off-by: Ulf Hansson --- .../devicetree/bindings/mmc/ti-omap-hsmmc.txt | 1 + drivers/mmc/host/omap_hsmmc.c | 201 +++++++++++++++++++-- include/linux/platform_data/mmc-omap.h | 1 + 3 files changed, 191 insertions(+), 12 deletions(-) diff --git a/Documentation/devicetree/bindings/mmc/ti-omap-hsmmc.txt b/Documentation/devicetree/bindings/mmc/ti-omap-hsmmc.txt index ce8056116fb0..0233ba7951e5 100644 --- a/Documentation/devicetree/bindings/mmc/ti-omap-hsmmc.txt +++ b/Documentation/devicetree/bindings/mmc/ti-omap-hsmmc.txt @@ -12,6 +12,7 @@ Required properties: Should be "ti,omap3-hsmmc", for OMAP3 controllers Should be "ti,omap3-pre-es3-hsmmc" for OMAP3 controllers pre ES3.0 Should be "ti,omap4-hsmmc", for OMAP4 controllers + Should be "ti,am33xx-hsmmc", for AM335x controllers - ti,hwmods: Must be "mmc", n is controller instance starting 1 Optional properties: diff --git a/drivers/mmc/host/omap_hsmmc.c b/drivers/mmc/host/omap_hsmmc.c index 6b7b75585926..9446010a5dd9 100644 --- a/drivers/mmc/host/omap_hsmmc.c +++ b/drivers/mmc/host/omap_hsmmc.c @@ -29,6 +29,7 @@ #include #include #include +#include #include #include #include @@ -36,6 +37,7 @@ #include #include #include +#include #include #include #include @@ -106,6 +108,7 @@ #define TC_EN (1 << 1) #define BWR_EN (1 << 4) #define BRR_EN (1 << 5) +#define CIRQ_EN (1 << 8) #define ERR_EN (1 << 15) #define CTO_EN (1 << 16) #define CCRC_EN (1 << 17) @@ -140,7 +143,6 @@ #define VDD_3V0 3000000 /* 300000 uV */ #define VDD_165_195 (ffs(MMC_VDD_165_195) - 1) -#define AUTO_CMD23 (1 << 1) /* Auto CMD23 support */ /* * One controller can have multiple slots, like on some omap boards using * omap.c controller driver. Luckily this is not currently done on any known @@ -194,6 +196,7 @@ struct omap_hsmmc_host { u32 sysctl; u32 capa; int irq; + int wake_irq; int use_dma, dma_ch; struct dma_chan *tx_chan; struct dma_chan *rx_chan; @@ -206,6 +209,9 @@ struct omap_hsmmc_host { int req_in_progress; unsigned long clk_rate; unsigned int flags; +#define AUTO_CMD23 (1 << 0) /* Auto CMD23 support */ +#define HSMMC_SDIO_IRQ_ENABLED (1 << 1) /* SDIO irq enabled */ +#define HSMMC_WAKE_IRQ_ENABLED (1 << 2) struct omap_hsmmc_next next_data; struct omap_mmc_platform_data *pdata; }; @@ -510,27 +516,40 @@ static void omap_hsmmc_stop_clock(struct omap_hsmmc_host *host) static void omap_hsmmc_enable_irq(struct omap_hsmmc_host *host, struct mmc_command *cmd) { - unsigned int irq_mask; + u32 irq_mask = INT_EN_MASK; + unsigned long flags; if (host->use_dma) - irq_mask = INT_EN_MASK & ~(BRR_EN | BWR_EN); - else - irq_mask = INT_EN_MASK; + irq_mask &= ~(BRR_EN | BWR_EN); /* Disable timeout for erases */ if (cmd->opcode == MMC_ERASE) irq_mask &= ~DTO_EN; + spin_lock_irqsave(&host->irq_lock, flags); OMAP_HSMMC_WRITE(host->base, STAT, STAT_CLEAR); OMAP_HSMMC_WRITE(host->base, ISE, irq_mask); + + /* latch pending CIRQ, but don't signal MMC core */ + if (host->flags & HSMMC_SDIO_IRQ_ENABLED) + irq_mask |= CIRQ_EN; OMAP_HSMMC_WRITE(host->base, IE, irq_mask); + spin_unlock_irqrestore(&host->irq_lock, flags); } static void omap_hsmmc_disable_irq(struct omap_hsmmc_host *host) { - OMAP_HSMMC_WRITE(host->base, ISE, 0); - OMAP_HSMMC_WRITE(host->base, IE, 0); + u32 irq_mask = 0; + unsigned long flags; + + spin_lock_irqsave(&host->irq_lock, flags); + /* no transfer running but need to keep cirq if enabled */ + if (host->flags & HSMMC_SDIO_IRQ_ENABLED) + irq_mask |= CIRQ_EN; + OMAP_HSMMC_WRITE(host->base, ISE, irq_mask); + OMAP_HSMMC_WRITE(host->base, IE, irq_mask); OMAP_HSMMC_WRITE(host->base, STAT, STAT_CLEAR); + spin_unlock_irqrestore(&host->irq_lock, flags); } /* Calculate divisor for the given clock frequency */ @@ -681,7 +700,9 @@ static int omap_hsmmc_context_restore(struct omap_hsmmc_host *host) && time_before(jiffies, timeout)) ; - omap_hsmmc_disable_irq(host); + OMAP_HSMMC_WRITE(host->base, ISE, 0); + OMAP_HSMMC_WRITE(host->base, IE, 0); + OMAP_HSMMC_WRITE(host->base, STAT, STAT_CLEAR); /* Do not initialize card-specific things if the power is off */ if (host->power_mode == MMC_POWER_OFF) @@ -1118,8 +1139,12 @@ static irqreturn_t omap_hsmmc_irq(int irq, void *dev_id) int status; status = OMAP_HSMMC_READ(host->base, STAT); - while (status & INT_EN_MASK && host->req_in_progress) { - omap_hsmmc_do_irq(host, status); + while (status & (INT_EN_MASK | CIRQ_EN)) { + if (host->req_in_progress) + omap_hsmmc_do_irq(host, status); + + if (status & CIRQ_EN) + mmc_signal_sdio_irq(host->mmc); /* Flush posted write */ status = OMAP_HSMMC_READ(host->base, STAT); @@ -1128,6 +1153,22 @@ static irqreturn_t omap_hsmmc_irq(int irq, void *dev_id) return IRQ_HANDLED; } +static irqreturn_t omap_hsmmc_wake_irq(int irq, void *dev_id) +{ + struct omap_hsmmc_host *host = dev_id; + + /* cirq is level triggered, disable to avoid infinite loop */ + spin_lock(&host->irq_lock); + if (host->flags & HSMMC_WAKE_IRQ_ENABLED) { + disable_irq_nosync(host->wake_irq); + host->flags &= ~HSMMC_WAKE_IRQ_ENABLED; + } + spin_unlock(&host->irq_lock); + pm_request_resume(host->dev); /* no use counter */ + + return IRQ_HANDLED; +} + static void set_sd_bus_power(struct omap_hsmmc_host *host) { unsigned long i; @@ -1639,6 +1680,79 @@ static void omap_hsmmc_init_card(struct mmc_host *mmc, struct mmc_card *card) mmc_slot(host).init_card(card); } +static void omap_hsmmc_enable_sdio_irq(struct mmc_host *mmc, int enable) +{ + struct omap_hsmmc_host *host = mmc_priv(mmc); + u32 irq_mask; + unsigned long flags; + + spin_lock_irqsave(&host->irq_lock, flags); + + irq_mask = OMAP_HSMMC_READ(host->base, ISE); + if (enable) { + host->flags |= HSMMC_SDIO_IRQ_ENABLED; + irq_mask |= CIRQ_EN; + } else { + host->flags &= ~HSMMC_SDIO_IRQ_ENABLED; + irq_mask &= ~CIRQ_EN; + } + OMAP_HSMMC_WRITE(host->base, IE, irq_mask); + + /* + * if enable, piggy back detection on current request + * but always disable immediately + */ + if (!host->req_in_progress || !enable) + OMAP_HSMMC_WRITE(host->base, ISE, irq_mask); + + /* flush posted write */ + OMAP_HSMMC_READ(host->base, IE); + + spin_unlock_irqrestore(&host->irq_lock, flags); +} + +static int omap_hsmmc_configure_wake_irq(struct omap_hsmmc_host *host) +{ + struct mmc_host *mmc = host->mmc; + int ret; + + /* + * For omaps with wake-up path, wakeirq will be irq from pinctrl and + * for other omaps, wakeirq will be from GPIO (dat line remuxed to + * gpio). wakeirq is needed to detect sdio irq in runtime suspend state + * with functional clock disabled. + */ + if (!host->dev->of_node || !host->wake_irq) + return -ENODEV; + + /* Prevent auto-enabling of IRQ */ + irq_set_status_flags(host->wake_irq, IRQ_NOAUTOEN); + ret = devm_request_irq(host->dev, host->wake_irq, omap_hsmmc_wake_irq, + IRQF_TRIGGER_LOW | IRQF_ONESHOT, + mmc_hostname(mmc), host); + if (ret) { + dev_err(mmc_dev(host->mmc), "Unable to request wake IRQ\n"); + goto err; + } + + /* + * Some omaps don't have wake-up path from deeper idle states + * and need to remux SDIO DAT1 to GPIO for wake-up from idle. + */ + if (host->pdata->controller_flags & OMAP_HSMMC_SWAKEUP_MISSING) { + ret = -ENODEV; + devm_free_irq(host->dev, host->wake_irq, host); + goto err; + } + + return 0; + +err: + dev_warn(host->dev, "no SDIO IRQ support, falling back to polling\n"); + host->wake_irq = 0; + return ret; +} + static void omap_hsmmc_conf_bus_power(struct omap_hsmmc_host *host) { u32 hctl, capa, value; @@ -1691,7 +1805,7 @@ static const struct mmc_host_ops omap_hsmmc_ops = { .get_cd = omap_hsmmc_get_cd, .get_ro = omap_hsmmc_get_ro, .init_card = omap_hsmmc_init_card, - /* NYET -- enable_sdio_irq */ + .enable_sdio_irq = omap_hsmmc_enable_sdio_irq, }; #ifdef CONFIG_DEBUG_FS @@ -1761,6 +1875,10 @@ static const struct omap_mmc_of_data omap3_pre_es3_mmc_of_data = { static const struct omap_mmc_of_data omap4_mmc_of_data = { .reg_offset = 0x100, }; +static const struct omap_mmc_of_data am33xx_mmc_of_data = { + .reg_offset = 0x100, + .controller_flags = OMAP_HSMMC_SWAKEUP_MISSING, +}; static const struct of_device_id omap_mmc_of_match[] = { { @@ -1777,6 +1895,10 @@ static const struct of_device_id omap_mmc_of_match[] = { .compatible = "ti,omap4-hsmmc", .data = &omap4_mmc_of_data, }, + { + .compatible = "ti,am33xx-hsmmc", + .data = &am33xx_mmc_of_data, + }, {}, }; MODULE_DEVICE_TABLE(of, omap_mmc_of_match); @@ -1913,6 +2035,9 @@ static int omap_hsmmc_probe(struct platform_device *pdev) platform_set_drvdata(pdev, host); + if (pdev->dev.of_node) + host->wake_irq = irq_of_parse_and_map(pdev->dev.of_node, 1); + mmc->ops = &omap_hsmmc_ops; mmc->f_min = OMAP_MMC_MIN_CLOCK; @@ -2066,6 +2191,18 @@ static int omap_hsmmc_probe(struct platform_device *pdev) dev_warn(&pdev->dev, "pins are not configured from the driver\n"); + /* + * For now, only support SDIO interrupt if we have a separate + * wake-up interrupt configured from device tree. This is because + * the wake-up interrupt is needed for idle state and some + * platforms need special quirks. And we don't want to add new + * legacy mux platform init code callbacks any longer as we + * are moving to DT based booting anyways. + */ + ret = omap_hsmmc_configure_wake_irq(host); + if (!ret) + mmc->caps |= MMC_CAP_SDIO_IRQ; + omap_hsmmc_protect_card(host); mmc_add_host(mmc); @@ -2170,11 +2307,18 @@ static int omap_hsmmc_suspend(struct device *dev) pm_runtime_get_sync(host->dev); if (!(host->mmc->pm_flags & MMC_PM_KEEP_POWER)) { - omap_hsmmc_disable_irq(host); + OMAP_HSMMC_WRITE(host->base, ISE, 0); + OMAP_HSMMC_WRITE(host->base, IE, 0); + OMAP_HSMMC_WRITE(host->base, STAT, STAT_CLEAR); OMAP_HSMMC_WRITE(host->base, HCTL, OMAP_HSMMC_READ(host->base, HCTL) & ~SDBP); } + /* do not wake up due to sdio irq */ + if ((host->mmc->caps & MMC_CAP_SDIO_IRQ) && + !(host->mmc->pm_flags & MMC_PM_WAKE_SDIO_IRQ)) + disable_irq(host->wake_irq); + if (host->dbclk) clk_disable_unprepare(host->dbclk); @@ -2200,6 +2344,10 @@ static int omap_hsmmc_resume(struct device *dev) omap_hsmmc_protect_card(host); + if ((host->mmc->caps & MMC_CAP_SDIO_IRQ) && + !(host->mmc->pm_flags & MMC_PM_WAKE_SDIO_IRQ)) + enable_irq(host->wake_irq); + pm_runtime_mark_last_busy(host->dev); pm_runtime_put_autosuspend(host->dev); return 0; @@ -2215,22 +2363,51 @@ static int omap_hsmmc_resume(struct device *dev) static int omap_hsmmc_runtime_suspend(struct device *dev) { struct omap_hsmmc_host *host; + unsigned long flags; host = platform_get_drvdata(to_platform_device(dev)); omap_hsmmc_context_save(host); dev_dbg(dev, "disabled\n"); + spin_lock_irqsave(&host->irq_lock, flags); + if ((host->mmc->caps & MMC_CAP_SDIO_IRQ) && + (host->flags & HSMMC_SDIO_IRQ_ENABLED)) { + /* disable sdio irq handling to prevent race */ + OMAP_HSMMC_WRITE(host->base, ISE, 0); + OMAP_HSMMC_WRITE(host->base, IE, 0); + OMAP_HSMMC_WRITE(host->base, STAT, STAT_CLEAR); + + WARN_ON(host->flags & HSMMC_WAKE_IRQ_ENABLED); + enable_irq(host->wake_irq); + host->flags |= HSMMC_WAKE_IRQ_ENABLED; + } + spin_unlock_irqrestore(&host->irq_lock, flags); return 0; } static int omap_hsmmc_runtime_resume(struct device *dev) { struct omap_hsmmc_host *host; + unsigned long flags; host = platform_get_drvdata(to_platform_device(dev)); omap_hsmmc_context_restore(host); dev_dbg(dev, "enabled\n"); + spin_lock_irqsave(&host->irq_lock, flags); + if ((host->mmc->caps & MMC_CAP_SDIO_IRQ) && + (host->flags & HSMMC_SDIO_IRQ_ENABLED)) { + /* sdio irq flag can't change while in runtime suspend */ + if (host->flags & HSMMC_WAKE_IRQ_ENABLED) { + disable_irq_nosync(host->wake_irq); + host->flags &= ~HSMMC_WAKE_IRQ_ENABLED; + } + + OMAP_HSMMC_WRITE(host->base, STAT, STAT_CLEAR); + OMAP_HSMMC_WRITE(host->base, ISE, CIRQ_EN); + OMAP_HSMMC_WRITE(host->base, IE, CIRQ_EN); + } + spin_unlock_irqrestore(&host->irq_lock, flags); return 0; } diff --git a/include/linux/platform_data/mmc-omap.h b/include/linux/platform_data/mmc-omap.h index 2bf1b30cb5dc..51e70cf25cbc 100644 --- a/include/linux/platform_data/mmc-omap.h +++ b/include/linux/platform_data/mmc-omap.h @@ -28,6 +28,7 @@ */ #define OMAP_HSMMC_SUPPORTS_DUAL_VOLT BIT(0) #define OMAP_HSMMC_BROKEN_MULTIBLOCK_READ BIT(1) +#define OMAP_HSMMC_SWAKEUP_MISSING BIT(2) struct mmc_card; -- cgit v1.2.3-59-g8ed1b From bb0635f0b4d2bad44d315344527cfa2cd91f0f4f Mon Sep 17 00:00:00 2001 From: Andreas Fenkart Date: Thu, 29 May 2014 10:28:01 +0200 Subject: mmc: omap_hsmmc: Extend debugfs by SDIO IRQ handling, runtime state Add SDIO IRQ entries to debugfs entry. Note that PSTATE shows current state of data lines, incl. SDIO IRQ pending Signed-off-by: Andreas Fenkart Acked-by: Balaji T K Signed-off-by: Ulf Hansson --- drivers/mmc/host/omap_hsmmc.c | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/drivers/mmc/host/omap_hsmmc.c b/drivers/mmc/host/omap_hsmmc.c index 9446010a5dd9..ad3a2acd3664 100644 --- a/drivers/mmc/host/omap_hsmmc.c +++ b/drivers/mmc/host/omap_hsmmc.c @@ -56,6 +56,7 @@ #define OMAP_HSMMC_RSP54 0x0118 #define OMAP_HSMMC_RSP76 0x011C #define OMAP_HSMMC_DATA 0x0120 +#define OMAP_HSMMC_PSTATE 0x0124 #define OMAP_HSMMC_HCTL 0x0128 #define OMAP_HSMMC_SYSCTL 0x012C #define OMAP_HSMMC_STAT 0x0130 @@ -1815,13 +1816,23 @@ static int omap_hsmmc_regs_show(struct seq_file *s, void *data) struct mmc_host *mmc = s->private; struct omap_hsmmc_host *host = mmc_priv(mmc); - seq_printf(s, "mmc%d:\n ctx_loss:\t%d\n\nregs:\n", - mmc->index, host->context_loss); + seq_printf(s, "mmc%d:\n", mmc->index); + seq_printf(s, "sdio irq mode\t%s\n", + (mmc->caps & MMC_CAP_SDIO_IRQ) ? "interrupt" : "polling"); - pm_runtime_get_sync(host->dev); + if (mmc->caps & MMC_CAP_SDIO_IRQ) { + seq_printf(s, "sdio irq \t%s\n", + (host->flags & HSMMC_SDIO_IRQ_ENABLED) ? "enabled" + : "disabled"); + } + seq_printf(s, "ctx_loss:\t%d\n", host->context_loss); + pm_runtime_get_sync(host->dev); + seq_puts(s, "\nregs:\n"); seq_printf(s, "CON:\t\t0x%08x\n", OMAP_HSMMC_READ(host->base, CON)); + seq_printf(s, "PSTATE:\t\t0x%08x\n", + OMAP_HSMMC_READ(host->base, PSTATE)); seq_printf(s, "HCTL:\t\t0x%08x\n", OMAP_HSMMC_READ(host->base, HCTL)); seq_printf(s, "SYSCTL:\t\t0x%08x\n", -- cgit v1.2.3-59-g8ed1b From 5a52b08b05b01732e338aab7283b3ce4987d71fb Mon Sep 17 00:00:00 2001 From: Balaji T K Date: Thu, 29 May 2014 10:28:02 +0200 Subject: mmc: omap_hsmmc: enable wakeup event for sdio OMAP4 To detect sdio irqs properly without spurious events, OMAP4 needs IWE in CON and CTPL, CLKEXTFREE in HCTL to be set Tested-by: Andreas Fenkart Signed-off-by: Balaji T K Signed-off-by: Ulf Hansson --- drivers/mmc/host/omap_hsmmc.c | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/drivers/mmc/host/omap_hsmmc.c b/drivers/mmc/host/omap_hsmmc.c index ad3a2acd3664..b12a2882ec74 100644 --- a/drivers/mmc/host/omap_hsmmc.c +++ b/drivers/mmc/host/omap_hsmmc.c @@ -94,7 +94,10 @@ #define BCE (1 << 1) #define FOUR_BIT (1 << 1) #define HSPE (1 << 2) +#define IWE (1 << 24) #define DDR (1 << 19) +#define CLKEXTFREE (1 << 16) +#define CTPL (1 << 11) #define DW8 (1 << 5) #define OD 0x1 #define STAT_CLEAR 0xFFFFFFFF @@ -687,6 +690,9 @@ static int omap_hsmmc_context_restore(struct omap_hsmmc_host *host) capa = VS18; } + if (host->mmc->caps & MMC_CAP_SDIO_IRQ) + hctl |= IWE; + OMAP_HSMMC_WRITE(host->base, HCTL, OMAP_HSMMC_READ(host->base, HCTL) | hctl); @@ -1684,19 +1690,23 @@ static void omap_hsmmc_init_card(struct mmc_host *mmc, struct mmc_card *card) static void omap_hsmmc_enable_sdio_irq(struct mmc_host *mmc, int enable) { struct omap_hsmmc_host *host = mmc_priv(mmc); - u32 irq_mask; + u32 irq_mask, con; unsigned long flags; spin_lock_irqsave(&host->irq_lock, flags); + con = OMAP_HSMMC_READ(host->base, CON); irq_mask = OMAP_HSMMC_READ(host->base, ISE); if (enable) { host->flags |= HSMMC_SDIO_IRQ_ENABLED; irq_mask |= CIRQ_EN; + con |= CTPL | CLKEXTFREE; } else { host->flags &= ~HSMMC_SDIO_IRQ_ENABLED; irq_mask &= ~CIRQ_EN; + con &= ~(CTPL | CLKEXTFREE); } + OMAP_HSMMC_WRITE(host->base, CON, con); OMAP_HSMMC_WRITE(host->base, IE, irq_mask); /* @@ -1746,6 +1756,8 @@ static int omap_hsmmc_configure_wake_irq(struct omap_hsmmc_host *host) goto err; } + OMAP_HSMMC_WRITE(host->base, HCTL, + OMAP_HSMMC_READ(host->base, HCTL) | IWE); return 0; err: -- cgit v1.2.3-59-g8ed1b From f945901f9aaff859c6b3244628c766d1939e46e7 Mon Sep 17 00:00:00 2001 From: Andreas Fenkart Date: Thu, 29 May 2014 10:28:03 +0200 Subject: mmc: omap_hsmmc: abort runtime suspend if pending sdio irq detected On multicores, an sdio irq handler could be running in parallel to runtime suspend. In the worst case it could be waiting for the spinlock held by the runtime suspend. When runtime suspend is complete and the functional clock (fclk) turned off, the irq handler will continue and cause a SIGBUS on the first register access. Acked-by: Balaji T K Signed-off-by: Andreas Fenkart Signed-off-by: Ulf Hansson --- drivers/mmc/host/omap_hsmmc.c | 23 +++++++++++++++++++++-- 1 file changed, 21 insertions(+), 2 deletions(-) diff --git a/drivers/mmc/host/omap_hsmmc.c b/drivers/mmc/host/omap_hsmmc.c index b12a2882ec74..e97fb9cb24d5 100644 --- a/drivers/mmc/host/omap_hsmmc.c +++ b/drivers/mmc/host/omap_hsmmc.c @@ -107,6 +107,9 @@ #define SRD (1 << 26) #define SOFTRESET (1 << 1) +/* PSTATE */ +#define DLEV_DAT(x) (1 << (20 + (x))) + /* Interrupt masks for IE and ISE register */ #define CC_EN (1 << 0) #define TC_EN (1 << 1) @@ -2387,6 +2390,7 @@ static int omap_hsmmc_runtime_suspend(struct device *dev) { struct omap_hsmmc_host *host; unsigned long flags; + int ret = 0; host = platform_get_drvdata(to_platform_device(dev)); omap_hsmmc_context_save(host); @@ -2398,14 +2402,29 @@ static int omap_hsmmc_runtime_suspend(struct device *dev) /* disable sdio irq handling to prevent race */ OMAP_HSMMC_WRITE(host->base, ISE, 0); OMAP_HSMMC_WRITE(host->base, IE, 0); - OMAP_HSMMC_WRITE(host->base, STAT, STAT_CLEAR); + + if (!(OMAP_HSMMC_READ(host->base, PSTATE) & DLEV_DAT(1))) { + /* + * dat1 line low, pending sdio irq + * race condition: possible irq handler running on + * multi-core, abort + */ + dev_dbg(dev, "pending sdio irq, abort suspend\n"); + OMAP_HSMMC_WRITE(host->base, STAT, STAT_CLEAR); + OMAP_HSMMC_WRITE(host->base, ISE, CIRQ_EN); + OMAP_HSMMC_WRITE(host->base, IE, CIRQ_EN); + pm_runtime_mark_last_busy(dev); + ret = -EBUSY; + goto abort; + } WARN_ON(host->flags & HSMMC_WAKE_IRQ_ENABLED); enable_irq(host->wake_irq); host->flags |= HSMMC_WAKE_IRQ_ENABLED; } +abort: spin_unlock_irqrestore(&host->irq_lock, flags); - return 0; + return ret; } static int omap_hsmmc_runtime_resume(struct device *dev) -- cgit v1.2.3-59-g8ed1b From 97978a4439fb49d05fbc1dfd075e3c5fb22ea956 Mon Sep 17 00:00:00 2001 From: Andreas Fenkart Date: Thu, 29 May 2014 10:28:04 +0200 Subject: mmc: omap_hsmmc: switch default/idle pinctrl states in runtime hooks These are predefined states of the driver model. When not present, as if not set in the device tree, they become no-ops. Explicitly selecting the default state is not needed since the device core layer sets pin mux to "default" state before probe. This is not the simplest implementation, on AM335x at least, we could switch to idle at any point in the suspend hook, only the default state needs to be set before writing to the irq registers or an IRQ might get lost. Acked-by: Balaji T K Signed-off-by: Andreas Fenkart Signed-off-by: Ulf Hansson --- drivers/mmc/host/omap_hsmmc.c | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/drivers/mmc/host/omap_hsmmc.c b/drivers/mmc/host/omap_hsmmc.c index e97fb9cb24d5..5167e55c0849 100644 --- a/drivers/mmc/host/omap_hsmmc.c +++ b/drivers/mmc/host/omap_hsmmc.c @@ -1998,7 +1998,6 @@ static int omap_hsmmc_probe(struct platform_device *pdev) const struct of_device_id *match; dma_cap_mask_t mask; unsigned tx_req, rx_req; - struct pinctrl *pinctrl; const struct omap_mmc_of_data *data; void __iomem *base; @@ -2212,11 +2211,6 @@ static int omap_hsmmc_probe(struct platform_device *pdev) omap_hsmmc_disable_irq(host); - pinctrl = devm_pinctrl_get_select_default(&pdev->dev); - if (IS_ERR(pinctrl)) - dev_warn(&pdev->dev, - "pins are not configured from the driver\n"); - /* * For now, only support SDIO interrupt if we have a separate * wake-up interrupt configured from device tree. This is because @@ -2418,10 +2412,15 @@ static int omap_hsmmc_runtime_suspend(struct device *dev) goto abort; } + pinctrl_pm_select_idle_state(dev); + WARN_ON(host->flags & HSMMC_WAKE_IRQ_ENABLED); enable_irq(host->wake_irq); host->flags |= HSMMC_WAKE_IRQ_ENABLED; + } else { + pinctrl_pm_select_idle_state(dev); } + abort: spin_unlock_irqrestore(&host->irq_lock, flags); return ret; @@ -2445,9 +2444,14 @@ static int omap_hsmmc_runtime_resume(struct device *dev) host->flags &= ~HSMMC_WAKE_IRQ_ENABLED; } + pinctrl_pm_select_default_state(host->dev); + + /* irq lost, if pinmux incorrect */ OMAP_HSMMC_WRITE(host->base, STAT, STAT_CLEAR); OMAP_HSMMC_WRITE(host->base, ISE, CIRQ_EN); OMAP_HSMMC_WRITE(host->base, IE, CIRQ_EN); + } else { + pinctrl_pm_select_default_state(host->dev); } spin_unlock_irqrestore(&host->irq_lock, flags); return 0; -- cgit v1.2.3-59-g8ed1b From 455e5cd6f736478d63a076086c732d287623e366 Mon Sep 17 00:00:00 2001 From: Andreas Fenkart Date: Thu, 29 May 2014 10:28:05 +0200 Subject: mmc: omap_hsmmc: Pin remux workaround to support SDIO interrupt on AM335x The am335x can't detect pending cirq in PM runtime suspend. This patch reconfigures dat1 as a GPIO before going to suspend. SDIO interrupts are detected with the GPIO, the GPIO will only wake the module from suspend, SDIO irq detection will still happen through the IP block. Idea of remuxing the pins by Tony Lindgren. Code contributions from Tony Lindgren and Balaji T K Signed-off-by: Andreas Fenkart Signed-off-by: Tony Lindgren Acked-by: Balaji T K Signed-off-by: Ulf Hansson --- .../devicetree/bindings/mmc/ti-omap-hsmmc.txt | 53 ++++++++++++++++++++++ drivers/mmc/host/omap_hsmmc.c | 24 ++++++++-- 2 files changed, 74 insertions(+), 3 deletions(-) diff --git a/Documentation/devicetree/bindings/mmc/ti-omap-hsmmc.txt b/Documentation/devicetree/bindings/mmc/ti-omap-hsmmc.txt index 0233ba7951e5..76bf087bc889 100644 --- a/Documentation/devicetree/bindings/mmc/ti-omap-hsmmc.txt +++ b/Documentation/devicetree/bindings/mmc/ti-omap-hsmmc.txt @@ -57,3 +57,56 @@ Examples: &edma 25>; dma-names = "tx", "rx"; }; + +[workaround for missing swakeup on am33xx] + +This SOC is missing the swakeup line, it will not detect SDIO irq +while in suspend. + + ------ + | PRCM | + ------ + ^ | + swakeup | | fclk + | v + ------ ------- ----- + | card | -- CIRQ --> | hsmmc | -- IRQ --> | CPU | + ------ ------- ----- + +In suspend the fclk is off and the module is disfunctional. Even register reads +will fail. A small logic in the host will request fclk restore, when an +external event is detected. Once the clock is restored, the host detects the +event normally. Since am33xx doesn't have this line it never wakes from +suspend. + +The workaround is to reconfigure the dat1 line as a GPIO upon suspend. To make +this work, we need to set the named pinctrl states "default" and "idle". +Prepare idle to remux dat1 as a gpio, and default to remux it back as sdio +dat1. The MMC driver will then toggle between idle and default state during +runtime. + +In summary: +1. select matching 'compatible' section, see example below. +2. specify pinctrl states "default" and "idle", "sleep" is optional. +3. specify the gpio irq used for detecting sdio irq in suspend + +If configuration is incomplete, a warning message is emitted "falling back to +polling". Also check the "sdio irq mode" in /sys/kernel/debug/mmc0/regs. Mind +not every application needs SDIO irq, e.g. MMC cards. + + mmc1: mmc@48060100 { + compatible = "ti,am33xx-hsmmc"; + ... + pinctrl-names = "default", "idle", "sleep" + pinctrl-0 = <&mmc1_pins>; + pinctrl-1 = <&mmc1_idle>; + pinctrl-2 = <&mmc1_sleep>; + ... + interrupts-extended = <&intc 64 &gpio2 28 0>; + }; + + mmc1_idle : pinmux_cirq_pin { + pinctrl-single,pins = < + 0x0f8 0x3f /* GPIO2_28 */ + >; + }; diff --git a/drivers/mmc/host/omap_hsmmc.c b/drivers/mmc/host/omap_hsmmc.c index 5167e55c0849..965672663ef0 100644 --- a/drivers/mmc/host/omap_hsmmc.c +++ b/drivers/mmc/host/omap_hsmmc.c @@ -1754,15 +1754,33 @@ static int omap_hsmmc_configure_wake_irq(struct omap_hsmmc_host *host) * and need to remux SDIO DAT1 to GPIO for wake-up from idle. */ if (host->pdata->controller_flags & OMAP_HSMMC_SWAKEUP_MISSING) { - ret = -ENODEV; - devm_free_irq(host->dev, host->wake_irq, host); - goto err; + struct pinctrl *p = devm_pinctrl_get(host->dev); + if (!p) { + ret = -ENODEV; + goto err_free_irq; + } + if (IS_ERR(pinctrl_lookup_state(p, PINCTRL_STATE_DEFAULT))) { + dev_info(host->dev, "missing default pinctrl state\n"); + devm_pinctrl_put(p); + ret = -EINVAL; + goto err_free_irq; + } + + if (IS_ERR(pinctrl_lookup_state(p, PINCTRL_STATE_IDLE))) { + dev_info(host->dev, "missing idle pinctrl state\n"); + devm_pinctrl_put(p); + ret = -EINVAL; + goto err_free_irq; + } + devm_pinctrl_put(p); } OMAP_HSMMC_WRITE(host->base, HCTL, OMAP_HSMMC_READ(host->base, HCTL) | IWE); return 0; +err_free_irq: + devm_free_irq(host->dev, host->wake_irq, host); err: dev_warn(host->dev, "no SDIO IRQ support, falling back to polling\n"); host->wake_irq = 0; -- cgit v1.2.3-59-g8ed1b From 258c749e41c4f4ccf0c8a68b879a00c289ae9217 Mon Sep 17 00:00:00 2001 From: Sachin Kamat Date: Thu, 22 May 2014 09:36:45 +0530 Subject: mmc: moxart: Remove unneeded version.h inclusion version.h inclusion is not needed as suggested by versioncheck. Signed-off-by: Sachin Kamat Cc: Jonas Jensen Signed-off-by: Ulf Hansson --- drivers/mmc/host/moxart-mmc.c | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/mmc/host/moxart-mmc.c b/drivers/mmc/host/moxart-mmc.c index 74924a04026e..b4b1efbf6c16 100644 --- a/drivers/mmc/host/moxart-mmc.c +++ b/drivers/mmc/host/moxart-mmc.c @@ -13,7 +13,6 @@ * warranty of any kind, whether express or implied. */ -#include #include #include #include -- cgit v1.2.3-59-g8ed1b From 5e863662add1fc00bf088dc381b787edc0a0de5b Mon Sep 17 00:00:00 2001 From: Johan Rudholm Date: Fri, 23 May 2014 16:15:03 +0200 Subject: mmc: sd: warn if card stays busy during init The initialization of some SD-cards fails because the card never leaves the busy state. Aid trouble shooting by indicating this in the kernel log. Signed-off-by: Johan Rudholm Signed-off-by: Ulf Hansson --- drivers/mmc/core/sd_ops.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/mmc/core/sd_ops.c b/drivers/mmc/core/sd_ops.c index 274ef00b4463..48d0c93ba25a 100644 --- a/drivers/mmc/core/sd_ops.c +++ b/drivers/mmc/core/sd_ops.c @@ -184,6 +184,9 @@ int mmc_send_app_op_cond(struct mmc_host *host, u32 ocr, u32 *rocr) mmc_delay(10); } + if (!i) + pr_err("%s: card never left busy state\n", mmc_hostname(host)); + if (rocr && !mmc_host_is_spi(host)) *rocr = cmd.resp[0]; -- cgit v1.2.3-59-g8ed1b From 889c9e04f14253e9267bf72aafc298c81468c508 Mon Sep 17 00:00:00 2001 From: Axel Lin Date: Sun, 27 Apr 2014 18:22:51 +0800 Subject: mmc: wmt-sdmmc: Fix settting BM_EIGHTBIT_MODE bit in wmt_mci_set_ios() For MMC_BUS_WIDTH_8 case, current code missed setting BM_EIGHTBIT_MODE bit. Also has a small refactor to make the code looks better in readability. So the bit settings witch below logic: SDMMC_BUSMODE register: Set EIGHTBIT_MODE bit for 8 bit mode, Set FOURBIT_MODE bit for 4 bit mode. Clear both EIGHTBIT_MODE and FOURBIT_MODE bits for 1 bit mode. SDMMC_EXTCTRL register: Set EXT_EIGHTBIT bit for 8 bit mode, Clear EXT_EIGHTBIT bit for 1/4 bit mode. Add define for EXT_EIGHTBIT to avoid using magic number. BM_ONEBIT_MASK is no longer used, thus remove it. This patch is untested due to lack of platform with 8-bit hardware. However since the code is there, it's good to make the code match the document. Signed-off-by: Axel Lin Signed-off-by: Ulf Hansson --- drivers/mmc/host/wmt-sdmmc.c | 31 +++++++++++++++---------------- 1 file changed, 15 insertions(+), 16 deletions(-) diff --git a/drivers/mmc/host/wmt-sdmmc.c b/drivers/mmc/host/wmt-sdmmc.c index 282891a8e451..f064bcbe0068 100644 --- a/drivers/mmc/host/wmt-sdmmc.c +++ b/drivers/mmc/host/wmt-sdmmc.c @@ -72,7 +72,6 @@ #define BM_SPI_CS 0x20 #define BM_SD_POWER 0x40 #define BM_SOFT_RESET 0x80 -#define BM_ONEBIT_MASK 0xFD /* SDMMC_BLKLEN bit fields */ #define BLKL_CRCERR_ABORT 0x0800 @@ -120,6 +119,8 @@ #define STS2_DATARSP_BUSY 0x20 #define STS2_DIS_FORCECLK 0x80 +/* SDMMC_EXTCTRL bit fields */ +#define EXT_EIGHTBIT 0x04 /* MMC/SD DMA Controller Registers */ #define SDDMA_GCR 0x100 @@ -672,7 +673,7 @@ static void wmt_mci_request(struct mmc_host *mmc, struct mmc_request *req) static void wmt_mci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios) { struct wmt_mci_priv *priv; - u32 reg_tmp; + u32 busmode, extctrl; priv = mmc_priv(mmc); @@ -687,28 +688,26 @@ static void wmt_mci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios) if (ios->clock != 0) clk_set_rate(priv->clk_sdmmc, ios->clock); + busmode = readb(priv->sdmmc_base + SDMMC_BUSMODE); + extctrl = readb(priv->sdmmc_base + SDMMC_EXTCTRL); + + busmode &= ~(BM_EIGHTBIT_MODE | BM_FOURBIT_MODE); + extctrl &= ~EXT_EIGHTBIT; + switch (ios->bus_width) { case MMC_BUS_WIDTH_8: - reg_tmp = readb(priv->sdmmc_base + SDMMC_EXTCTRL); - writeb(reg_tmp | 0x04, priv->sdmmc_base + SDMMC_EXTCTRL); + busmode |= BM_EIGHTBIT_MODE; + extctrl |= EXT_EIGHTBIT; break; case MMC_BUS_WIDTH_4: - reg_tmp = readb(priv->sdmmc_base + SDMMC_BUSMODE); - writeb(reg_tmp | BM_FOURBIT_MODE, priv->sdmmc_base + - SDMMC_BUSMODE); - - reg_tmp = readb(priv->sdmmc_base + SDMMC_EXTCTRL); - writeb(reg_tmp & 0xFB, priv->sdmmc_base + SDMMC_EXTCTRL); + busmode |= BM_FOURBIT_MODE; break; case MMC_BUS_WIDTH_1: - reg_tmp = readb(priv->sdmmc_base + SDMMC_BUSMODE); - writeb(reg_tmp & BM_ONEBIT_MASK, priv->sdmmc_base + - SDMMC_BUSMODE); - - reg_tmp = readb(priv->sdmmc_base + SDMMC_EXTCTRL); - writeb(reg_tmp & 0xFB, priv->sdmmc_base + SDMMC_EXTCTRL); break; } + + writeb(busmode, priv->sdmmc_base + SDMMC_BUSMODE); + writeb(extctrl, priv->sdmmc_base + SDMMC_EXTCTRL); } static int wmt_mci_get_ro(struct mmc_host *mmc) -- cgit v1.2.3-59-g8ed1b From 5c87456b07ba278021e7dd66a32e9b4bc1acaa32 Mon Sep 17 00:00:00 2001 From: Axel Lin Date: Sun, 27 Apr 2014 18:25:31 +0800 Subject: mmc: wmt-sdmmc: Remove deprecated IRQF_DISABLED It's a NOOP since 2.6.35 and it will be removed one day. This is not trivial because current code uses hard coded 32 instead of IRQF_DISABLED in the request_irq call. Signed-off-by: Axel Lin Signed-off-by: Ulf Hansson --- drivers/mmc/host/wmt-sdmmc.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/mmc/host/wmt-sdmmc.c b/drivers/mmc/host/wmt-sdmmc.c index f064bcbe0068..54181b4f6e9e 100644 --- a/drivers/mmc/host/wmt-sdmmc.c +++ b/drivers/mmc/host/wmt-sdmmc.c @@ -829,7 +829,7 @@ static int wmt_mci_probe(struct platform_device *pdev) goto fail3; } - ret = request_irq(dma_irq, wmt_mci_dma_isr, 32, "sdmmc", priv); + ret = request_irq(dma_irq, wmt_mci_dma_isr, 0, "sdmmc", priv); if (ret) { dev_err(&pdev->dev, "Register DMA IRQ fail\n"); goto fail4; -- cgit v1.2.3-59-g8ed1b From 87a0f46a0e1c048ecf964e0ef020cca3d6daba2f Mon Sep 17 00:00:00 2001 From: Peter Griffin Date: Mon, 16 Jun 2014 12:06:36 +0100 Subject: mmc: core: Fix DT documentation of eMMC high-speed DDR 1.8/1.2V bindings Currently the documentation doesn't match the code in mmc_of_parse. This patch rectifies this. Signed-off-by: Peter Griffin Signed-off-by: Ulf Hansson --- Documentation/devicetree/bindings/mmc/mmc.txt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Documentation/devicetree/bindings/mmc/mmc.txt b/Documentation/devicetree/bindings/mmc/mmc.txt index 3c18001dfd5d..431716e37a39 100644 --- a/Documentation/devicetree/bindings/mmc/mmc.txt +++ b/Documentation/devicetree/bindings/mmc/mmc.txt @@ -34,8 +34,8 @@ Optional properties: - cap-power-off-card: powering off the card is safe - cap-sdio-irq: enable SDIO IRQ signalling on this interface - full-pwr-cycle: full power cycle of the card is supported -- mmc-highspeed-ddr-1_8v: eMMC high-speed DDR mode(1.8V I/O) is supported -- mmc-highspeed-ddr-1_2v: eMMC high-speed DDR mode(1.2V I/O) is supported +- mmc-ddr-1_8v: eMMC high-speed DDR mode(1.8V I/O) is supported +- mmc-ddr-1_2v: eMMC high-speed DDR mode(1.2V I/O) is supported - mmc-hs200-1_8v: eMMC HS200 mode(1.8V I/O) is supported - mmc-hs200-1_2v: eMMC HS200 mode(1.2V I/O) is supported - mmc-hs400-1_8v: eMMC HS400 mode(1.8V I/O) is supported -- cgit v1.2.3-59-g8ed1b From 52221610dd84dc3e9196554f0292ca9e8ab3541d Mon Sep 17 00:00:00 2001 From: Tim Kryger Date: Wed, 25 Jun 2014 00:25:34 -0700 Subject: mmc: sdhci: Improve external VDD regulator support A standard compliant SDHCI can itself supply VDD at 1.8, 3.0, or 3.3v. Several vendors ignore this and instead rely upon external regulators to supply VDD. While the external regulators typically can supply one of the standard SDHCI voltage levels, there is no real reason for this to be a hard requirement. This patch alters the SDHCI driver such that external VDD regulators that provide voltages other than the three mentioned above may be used so long as they can supply a voltage that meets the needs of the card. In the case that an external VDD regulator is provided, it is reasonable to ignore the voltage capabilities of the host controller and allow the external regulator to set the OCR mask. Additionally, there is no need to convert a VDD voltage request into one of the standard SDHCI voltage levels or program it in the host controller's power control register. Signed-off-by: Tim Kryger Tested-by: Sachin Kamat Signed-off-by: Ulf Hansson --- drivers/mmc/host/sdhci.c | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c index c23a87285a95..07a242675c0e 100644 --- a/drivers/mmc/host/sdhci.c +++ b/drivers/mmc/host/sdhci.c @@ -1226,6 +1226,13 @@ static void sdhci_set_power(struct sdhci_host *host, unsigned char mode, struct mmc_host *mmc = host->mmc; u8 pwr = 0; + if (!IS_ERR(mmc->supply.vmmc)) { + spin_unlock_irq(&host->lock); + mmc_regulator_set_ocr(host->mmc, mmc->supply.vmmc, vdd); + spin_lock_irq(&host->lock); + return; + } + if (mode != MMC_POWER_OFF) { switch (1 << vdd) { case MMC_VDD_165_195: @@ -1284,12 +1291,6 @@ static void sdhci_set_power(struct sdhci_host *host, unsigned char mode, if (host->quirks & SDHCI_QUIRK_DELAY_AFTER_POWER) mdelay(10); } - - if (!IS_ERR(mmc->supply.vmmc)) { - spin_unlock_irq(&host->lock); - mmc_regulator_set_ocr(host->mmc, mmc->supply.vmmc, vdd); - spin_lock_irq(&host->lock); - } } /*****************************************************************************\ @@ -3092,8 +3093,9 @@ int sdhci_add_host(struct sdhci_host *host) SDHCI_MAX_CURRENT_MULTIPLIER; } + /* If OCR set by external regulators, use it instead */ if (mmc->ocr_avail) - ocr_avail &= mmc->ocr_avail; + ocr_avail = mmc->ocr_avail; if (host->ocr_mask) ocr_avail &= host->ocr_mask; -- cgit v1.2.3-59-g8ed1b From c938a53aac076df4b081ed98aee08617e06baedf Mon Sep 17 00:00:00 2001 From: Ulf Hansson Date: Wed, 18 Jun 2014 09:14:08 +0200 Subject: mmc: sdhci: Remove blank line While merging the sdhci patchset from Russell King, somehow a blank line was left behind. Let's correct the formatting. Cc: Chris Ball Cc: Russell King Signed-off-by: Ulf Hansson --- drivers/mmc/host/sdhci.c | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c index 07a242675c0e..5cf4a864c3a4 100644 --- a/drivers/mmc/host/sdhci.c +++ b/drivers/mmc/host/sdhci.c @@ -1534,7 +1534,6 @@ static void sdhci_do_set_ios(struct sdhci_host *host, struct mmc_ios *ios) host->ops->set_clock(host, host->clock); } - /* Reset SD Clock Enable */ clk = sdhci_readw(host, SDHCI_CLOCK_CONTROL); clk &= ~SDHCI_CLOCK_CARD_EN; -- cgit v1.2.3-59-g8ed1b From 43e968cec79b6334cf7cb3e11184cce720541712 Mon Sep 17 00:00:00 2001 From: Derek Browne Date: Tue, 24 Jun 2014 06:56:36 -0700 Subject: mmc: sdhci-pci: SDIO host controller support for Intel Quark X1000 This patch is to enable SDIO host controller for Intel Quark X1000. Signed-off-by: Derek Browne Signed-off-by: Alvin (Weike) Chen Signed-off-by: Ulf Hansson --- drivers/mmc/host/sdhci-pci.c | 12 ++++++++++++ drivers/mmc/host/sdhci-pci.h | 1 + 2 files changed, 13 insertions(+) diff --git a/drivers/mmc/host/sdhci-pci.c b/drivers/mmc/host/sdhci-pci.c index 52c42fcc284c..1aeeefc71512 100644 --- a/drivers/mmc/host/sdhci-pci.c +++ b/drivers/mmc/host/sdhci-pci.c @@ -103,6 +103,10 @@ static const struct sdhci_pci_fixes sdhci_cafe = { SDHCI_QUIRK_BROKEN_TIMEOUT_VAL, }; +static const struct sdhci_pci_fixes sdhci_intel_qrk = { + .quirks = SDHCI_QUIRK_NO_HISPD_BIT, +}; + static int mrst_hc_probe_slot(struct sdhci_pci_slot *slot) { slot->host->mmc->caps |= MMC_CAP_8_BIT_DATA; @@ -751,6 +755,14 @@ static const struct pci_device_id pci_ids[] = { .driver_data = (kernel_ulong_t)&sdhci_rtsx, }, + { + .vendor = PCI_VENDOR_ID_INTEL, + .device = PCI_DEVICE_ID_INTEL_QRK_SD, + .subvendor = PCI_ANY_ID, + .subdevice = PCI_ANY_ID, + .driver_data = (kernel_ulong_t)&sdhci_intel_qrk, + }, + { .vendor = PCI_VENDOR_ID_INTEL, .device = PCI_DEVICE_ID_INTEL_MRST_SD0, diff --git a/drivers/mmc/host/sdhci-pci.h b/drivers/mmc/host/sdhci-pci.h index 6d718719659e..c101477ef3be 100644 --- a/drivers/mmc/host/sdhci-pci.h +++ b/drivers/mmc/host/sdhci-pci.h @@ -17,6 +17,7 @@ #define PCI_DEVICE_ID_INTEL_CLV_SDIO2 0x08fb #define PCI_DEVICE_ID_INTEL_CLV_EMMC0 0x08e5 #define PCI_DEVICE_ID_INTEL_CLV_EMMC1 0x08e6 +#define PCI_DEVICE_ID_INTEL_QRK_SD 0x08A7 /* * PCI registers -- cgit v1.2.3-59-g8ed1b From 62ce34b0baedc283267e2dbc5d79ebef062dbda1 Mon Sep 17 00:00:00 2001 From: Russell King Date: Thu, 26 Jun 2014 15:50:00 +0100 Subject: mmc: sdhci: avoid double-delay while transitioning to 1.8V The MMC core in mmc_set_signal_voltage() already provides for the delay required to switch to 1.8V, so there is no need for drivers to perform this wait themselves. Signed-off-by: Russell King Signed-off-by: Ulf Hansson --- drivers/mmc/host/sdhci.c | 3 --- 1 file changed, 3 deletions(-) diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c index 5cf4a864c3a4..ce092fbc9e47 100644 --- a/drivers/mmc/host/sdhci.c +++ b/drivers/mmc/host/sdhci.c @@ -1768,9 +1768,6 @@ static int sdhci_do_start_signal_voltage_switch(struct sdhci_host *host, ctrl |= SDHCI_CTRL_VDD_180; sdhci_writew(host, ctrl, SDHCI_HOST_CONTROL2); - /* Wait for 5ms */ - usleep_range(5000, 5500); - /* 1.8V regulator output should be stable within 5 ms */ ctrl = sdhci_readw(host, SDHCI_HOST_CONTROL2); if (ctrl & SDHCI_CTRL_VDD_180) -- cgit v1.2.3-59-g8ed1b From 4e743f1fc8bd24d1bc185e4d7b13f7b74c9c5423 Mon Sep 17 00:00:00 2001 From: Markus Mayer Date: Thu, 3 Jul 2014 13:27:42 -0700 Subject: mmc: sdhci: Replace host->mmc with mmc where possible After the switch to the MMC core regulator infrastucture, we already have a local "mmc" pointer in various functions. There is no longer a need to access the data structure via host->mmc. Signed-off-by: Markus Mayer Reviewed-by: Matt Porter Signed-off-by: Ulf Hansson --- drivers/mmc/host/sdhci.c | 30 +++++++++++++++--------------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c index ce092fbc9e47..37b2a9ae52ef 100644 --- a/drivers/mmc/host/sdhci.c +++ b/drivers/mmc/host/sdhci.c @@ -1228,7 +1228,7 @@ static void sdhci_set_power(struct sdhci_host *host, unsigned char mode, if (!IS_ERR(mmc->supply.vmmc)) { spin_unlock_irq(&host->lock); - mmc_regulator_set_ocr(host->mmc, mmc->supply.vmmc, vdd); + mmc_regulator_set_ocr(mmc, mmc->supply.vmmc, vdd); spin_lock_irq(&host->lock); return; } @@ -1450,7 +1450,7 @@ static void sdhci_do_set_ios(struct sdhci_host *host, struct mmc_ios *ios) spin_unlock_irqrestore(&host->lock, flags); if (!IS_ERR(mmc->supply.vmmc) && ios->power_mode == MMC_POWER_OFF) - mmc_regulator_set_ocr(host->mmc, mmc->supply.vmmc, 0); + mmc_regulator_set_ocr(mmc, mmc->supply.vmmc, 0); return; } @@ -1734,7 +1734,7 @@ static int sdhci_do_start_signal_voltage_switch(struct sdhci_host *host, 3600000); if (ret) { pr_warning("%s: Switching to 3.3V signalling voltage " - " failed\n", mmc_hostname(host->mmc)); + " failed\n", mmc_hostname(mmc)); return -EIO; } } @@ -1747,7 +1747,7 @@ static int sdhci_do_start_signal_voltage_switch(struct sdhci_host *host, return 0; pr_warning("%s: 3.3V regulator output did not became stable\n", - mmc_hostname(host->mmc)); + mmc_hostname(mmc)); return -EAGAIN; case MMC_SIGNAL_VOLTAGE_180: @@ -1756,7 +1756,7 @@ static int sdhci_do_start_signal_voltage_switch(struct sdhci_host *host, 1700000, 1950000); if (ret) { pr_warning("%s: Switching to 1.8V signalling voltage " - " failed\n", mmc_hostname(host->mmc)); + " failed\n", mmc_hostname(mmc)); return -EIO; } } @@ -1774,7 +1774,7 @@ static int sdhci_do_start_signal_voltage_switch(struct sdhci_host *host, return 0; pr_warning("%s: 1.8V regulator output did not became stable\n", - mmc_hostname(host->mmc)); + mmc_hostname(mmc)); return -EAGAIN; case MMC_SIGNAL_VOLTAGE_120: @@ -1783,7 +1783,7 @@ static int sdhci_do_start_signal_voltage_switch(struct sdhci_host *host, 1300000); if (ret) { pr_warning("%s: Switching to 1.2V signalling voltage " - " failed\n", mmc_hostname(host->mmc)); + " failed\n", mmc_hostname(mmc)); return -EIO; } } @@ -2822,12 +2822,12 @@ int sdhci_add_host(struct sdhci_host *host) * (128) and potentially one alignment transfer for * each of those entries. */ - host->adma_desc = dma_alloc_coherent(mmc_dev(host->mmc), + host->adma_desc = dma_alloc_coherent(mmc_dev(mmc), ADMA_SIZE, &host->adma_addr, GFP_KERNEL); host->align_buffer = kmalloc(128 * 4, GFP_KERNEL); if (!host->adma_desc || !host->align_buffer) { - dma_free_coherent(mmc_dev(host->mmc), ADMA_SIZE, + dma_free_coherent(mmc_dev(mmc), ADMA_SIZE, host->adma_desc, host->adma_addr); kfree(host->align_buffer); pr_warning("%s: Unable to allocate ADMA " @@ -2840,7 +2840,7 @@ int sdhci_add_host(struct sdhci_host *host) pr_warning("%s: unable to allocate aligned ADMA descriptor\n", mmc_hostname(mmc)); host->flags &= ~SDHCI_USE_ADMA; - dma_free_coherent(mmc_dev(host->mmc), ADMA_SIZE, + dma_free_coherent(mmc_dev(mmc), ADMA_SIZE, host->adma_desc, host->adma_addr); kfree(host->align_buffer); host->adma_desc = NULL; @@ -2855,7 +2855,7 @@ int sdhci_add_host(struct sdhci_host *host) */ if (!(host->flags & (SDHCI_USE_SDMA | SDHCI_USE_ADMA))) { host->dma_mask = DMA_BIT_MASK(64); - mmc_dev(host->mmc)->dma_mask = &host->dma_mask; + mmc_dev(mmc)->dma_mask = &host->dma_mask; } if (host->version >= SDHCI_SPEC_300) @@ -2961,7 +2961,7 @@ int sdhci_add_host(struct sdhci_host *host) mmc->caps |= MMC_CAP_SD_HIGHSPEED | MMC_CAP_MMC_HIGHSPEED; if ((host->quirks & SDHCI_QUIRK_BROKEN_CARD_DETECTION) && - !(host->mmc->caps & MMC_CAP_NONREMOVABLE)) + !(mmc->caps & MMC_CAP_NONREMOVABLE)) mmc->caps |= MMC_CAP_NEEDS_POLL; /* If there are external regulators, get them */ @@ -3258,7 +3258,7 @@ void sdhci_remove_host(struct sdhci_host *host, int dead) if (host->mrq) { pr_err("%s: Controller removed during " - " transfer!\n", mmc_hostname(host->mmc)); + " transfer!\n", mmc_hostname(mmc)); host->mrq->cmd->error = -ENOMEDIUM; tasklet_schedule(&host->finish_tasklet); @@ -3269,7 +3269,7 @@ void sdhci_remove_host(struct sdhci_host *host, int dead) sdhci_disable_card_detection(host); - mmc_remove_host(host->mmc); + mmc_remove_host(mmc); #ifdef SDHCI_USE_LEDS_CLASS led_classdev_unregister(&host->led); @@ -3293,7 +3293,7 @@ void sdhci_remove_host(struct sdhci_host *host, int dead) regulator_disable(mmc->supply.vqmmc); if (host->adma_desc) - dma_free_coherent(mmc_dev(host->mmc), ADMA_SIZE, + dma_free_coherent(mmc_dev(mmc), ADMA_SIZE, host->adma_desc, host->adma_addr); kfree(host->align_buffer); -- cgit v1.2.3-59-g8ed1b From 03a59437ef6b6ad7fb0165cb9b96c08d6bf057fc Mon Sep 17 00:00:00 2001 From: Romain Izard Date: Fri, 27 Jun 2014 10:51:07 +0200 Subject: mmc: Allow forward compatibility for eMMC As stated by the eMMC 5.0 specification, a chip should not be rejected only because of the revision stated in the EXT_CSD_REV field of the EXT_CSD register. Remove the control on this value, the control of the CSD_STRUCTURE field should be sufficient to reject future incompatible changes. Signed-off-by: Romain Izard Signed-off-by: Ulf Hansson --- drivers/mmc/core/mmc.c | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/drivers/mmc/core/mmc.c b/drivers/mmc/core/mmc.c index 793c6f7ddb04..1eda8dd8c867 100644 --- a/drivers/mmc/core/mmc.c +++ b/drivers/mmc/core/mmc.c @@ -324,13 +324,12 @@ static int mmc_read_ext_csd(struct mmc_card *card, u8 *ext_csd) } } + /* + * The EXT_CSD format is meant to be forward compatible. As long + * as CSD_STRUCTURE does not change, all values for EXT_CSD_REV + * are authorized, see JEDEC JESD84-B50 section B.8. + */ card->ext_csd.rev = ext_csd[EXT_CSD_REV]; - if (card->ext_csd.rev > 7) { - pr_err("%s: unrecognised EXT_CSD revision %d\n", - mmc_hostname(card->host), card->ext_csd.rev); - err = -EINVAL; - goto out; - } card->ext_csd.raw_sectors[0] = ext_csd[EXT_CSD_SEC_CNT + 0]; card->ext_csd.raw_sectors[1] = ext_csd[EXT_CSD_SEC_CNT + 1]; -- cgit v1.2.3-59-g8ed1b From d222c4c0ce170cd81a74988fd2eb99230519f9ae Mon Sep 17 00:00:00 2001 From: Vasily Khoruzhick Date: Mon, 30 Jun 2014 22:11:06 +0300 Subject: mmc: s3cmci: Move to clk_prepare_enable/clk_disable_unprepare Use clk_prepare_enable/clk_disable_unprepare to make the driver work properly with common clock framework. Signed-off-by: Vasily Khoruzhick Signed-off-by: Ulf Hansson --- drivers/mmc/host/s3cmci.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/mmc/host/s3cmci.c b/drivers/mmc/host/s3cmci.c index f23782683a7c..501ec210780c 100644 --- a/drivers/mmc/host/s3cmci.c +++ b/drivers/mmc/host/s3cmci.c @@ -1787,7 +1787,7 @@ static int s3cmci_probe(struct platform_device *pdev) goto probe_free_dma; } - ret = clk_enable(host->clk); + ret = clk_prepare_enable(host->clk); if (ret) { dev_err(&pdev->dev, "failed to enable clock source.\n"); goto clk_free; @@ -1845,7 +1845,7 @@ static int s3cmci_probe(struct platform_device *pdev) s3cmci_cpufreq_deregister(host); free_dmabuf: - clk_disable(host->clk); + clk_disable_unprepare(host->clk); clk_free: clk_put(host->clk); @@ -1897,7 +1897,7 @@ static void s3cmci_shutdown(struct platform_device *pdev) s3cmci_debugfs_remove(host); s3cmci_cpufreq_deregister(host); mmc_remove_host(mmc); - clk_disable(host->clk); + clk_disable_unprepare(host->clk); } static int s3cmci_remove(struct platform_device *pdev) -- cgit v1.2.3-59-g8ed1b From 6f726f495f2d5247e1614a70f9093d9f4d6380e4 Mon Sep 17 00:00:00 2001 From: Roman Peniaev Date: Tue, 10 Jun 2014 23:57:02 +0900 Subject: mmc: mxs: fix card detection in case of 'broken-cd' flag set In case of reboot my olinuxino imx23 board does not see mmc card any more. mmc_rescan is being called by delayed work in loop, but mxs_mmc_get_cd always returns 0, so we will never pass the card detection check and will not do further card inition. This patch is just an attempt to partially revert the patch a91fe279ae of Sascha Hauer, where it is claimed that upper layer will handle broken card detection using the polling logic and MMC_CAP_NEEDS_POLL capability, but seems it is not true, because upper logic still expects 1 from 'get_cd'. So, here we always return 1 (card present) in case of MMC_CAP_NEEDS_POLL capability set. Signed-off-by: Roman Pen CC: Chris Ball CC: Sascha Hauer CC: Fabio Estevam CC: Shawn Guo CC: Ulf Hansson CC: linux-mmc@vger.kernel.org CC: linux-kernel@vger.kernel.org Acked-by: Sascha Hauer Signed-off-by: Ulf Hansson --- drivers/mmc/host/mxs-mmc.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/mmc/host/mxs-mmc.c b/drivers/mmc/host/mxs-mmc.c index babfea03ba8a..140885a5a4e7 100644 --- a/drivers/mmc/host/mxs-mmc.c +++ b/drivers/mmc/host/mxs-mmc.c @@ -86,7 +86,8 @@ static int mxs_mmc_get_cd(struct mmc_host *mmc) if (ret >= 0) return ret; - present = !(readl(ssp->base + HW_SSP_STATUS(ssp)) & + present = mmc->caps & MMC_CAP_NEEDS_POLL || + !(readl(ssp->base + HW_SSP_STATUS(ssp)) & BM_SSP_STATUS_CARD_DETECT); if (mmc->caps2 & MMC_CAP2_CD_ACTIVE_HIGH) -- cgit v1.2.3-59-g8ed1b From 1b3f626e64b50e4ac3bca046c71cfbaec815670d Mon Sep 17 00:00:00 2001 From: Thierry Reding Date: Mon, 7 Jul 2014 14:07:42 +0200 Subject: mmc: tegra: Do not include asm/gpio.h This doesn't seem to be used any longer and removing the include fixes 64-bit ARM builds. Signed-off-by: Thierry Reding Signed-off-by: Ulf Hansson --- drivers/mmc/host/sdhci-tegra.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/drivers/mmc/host/sdhci-tegra.c b/drivers/mmc/host/sdhci-tegra.c index d93a063a36f3..33100d10d176 100644 --- a/drivers/mmc/host/sdhci-tegra.c +++ b/drivers/mmc/host/sdhci-tegra.c @@ -26,8 +26,6 @@ #include #include -#include - #include "sdhci-pltfm.h" /* Tegra SDHOST controller vendor register definitions */ -- cgit v1.2.3-59-g8ed1b From b45e4b5093298e5d42f4a80f95f08e511dc06767 Mon Sep 17 00:00:00 2001 From: Vasily Khoruzhick Date: Sun, 1 Jun 2014 20:22:09 +0300 Subject: mmc: s3cmci: port DMA code to dmaengine API Utilise new s3c24xx-dma dmaengine driver for DMA ops. Signed-off-by: Vasily Khoruzhick Acked-by: Arnd Bergmann Signed-off-by: Ulf Hansson --- drivers/mmc/host/Kconfig | 10 +-- drivers/mmc/host/s3cmci.c | 180 +++++++++++++--------------------------------- drivers/mmc/host/s3cmci.h | 4 +- 3 files changed, 52 insertions(+), 142 deletions(-) diff --git a/drivers/mmc/host/Kconfig b/drivers/mmc/host/Kconfig index a5652548230a..9682ad1e94b5 100644 --- a/drivers/mmc/host/Kconfig +++ b/drivers/mmc/host/Kconfig @@ -440,6 +440,7 @@ config MMC_SPI config MMC_S3C tristate "Samsung S3C SD/MMC Card Interface support" depends on ARCH_S3C24XX + depends on S3C24XX_DMAC help This selects a driver for the MCI interface found in Samsung's S3C2410, S3C2412, S3C2440, S3C2442 CPUs. @@ -477,15 +478,6 @@ config MMC_S3C_DMA working properly and needs to be debugged before this option is useful. -config MMC_S3C_PIODMA - bool "Support for both PIO and DMA" - help - Compile both the PIO and DMA transfer routines into the - driver and let the platform select at run-time which one - is best. - - See notes for the DMA option. - endchoice config MMC_SDRICOH_CS diff --git a/drivers/mmc/host/s3cmci.c b/drivers/mmc/host/s3cmci.c index 501ec210780c..e5516a226362 100644 --- a/drivers/mmc/host/s3cmci.c +++ b/drivers/mmc/host/s3cmci.c @@ -12,6 +12,7 @@ */ #include +#include #include #include #include @@ -27,6 +28,7 @@ #include #include +#include #include #include "s3cmci.h" @@ -140,10 +142,6 @@ static const int dbgmap_debug = dbg_err | dbg_debug; dev_dbg(&host->pdev->dev, args); \ } while (0) -static struct s3c2410_dma_client s3cmci_dma_client = { - .name = "s3c-mci", -}; - static void finalize_request(struct s3cmci_host *host); static void s3cmci_send_request(struct mmc_host *mmc); static void s3cmci_reset(struct s3cmci_host *host); @@ -256,25 +254,8 @@ static inline bool s3cmci_host_usedma(struct s3cmci_host *host) { #ifdef CONFIG_MMC_S3C_PIO return false; -#elif defined(CONFIG_MMC_S3C_DMA) +#else /* CONFIG_MMC_S3C_DMA */ return true; -#else - return host->dodma; -#endif -} - -/** - * s3cmci_host_canpio - return true if host has pio code available - * - * Return true if the driver has been compiled with the PIO support code - * available. - */ -static inline bool s3cmci_host_canpio(void) -{ -#ifdef CONFIG_MMC_S3C_PIO - return true; -#else - return false; #endif } @@ -841,60 +822,24 @@ static irqreturn_t s3cmci_irq_cd(int irq, void *dev_id) return IRQ_HANDLED; } -static void s3cmci_dma_done_callback(struct s3c2410_dma_chan *dma_ch, - void *buf_id, int size, - enum s3c2410_dma_buffresult result) +static void s3cmci_dma_done_callback(void *arg) { - struct s3cmci_host *host = buf_id; + struct s3cmci_host *host = arg; unsigned long iflags; - u32 mci_csta, mci_dsta, mci_fsta, mci_dcnt; - - mci_csta = readl(host->base + S3C2410_SDICMDSTAT); - mci_dsta = readl(host->base + S3C2410_SDIDSTA); - mci_fsta = readl(host->base + S3C2410_SDIFSTA); - mci_dcnt = readl(host->base + S3C2410_SDIDCNT); BUG_ON(!host->mrq); BUG_ON(!host->mrq->data); - BUG_ON(!host->dmatogo); spin_lock_irqsave(&host->complete_lock, iflags); - if (result != S3C2410_RES_OK) { - dbg(host, dbg_fail, "DMA FAILED: csta=0x%08x dsta=0x%08x " - "fsta=0x%08x dcnt:0x%08x result:0x%08x toGo:%u\n", - mci_csta, mci_dsta, mci_fsta, - mci_dcnt, result, host->dmatogo); - - goto fail_request; - } - - host->dmatogo--; - if (host->dmatogo) { - dbg(host, dbg_dma, "DMA DONE Size:%i DSTA:[%08x] " - "DCNT:[%08x] toGo:%u\n", - size, mci_dsta, mci_dcnt, host->dmatogo); - - goto out; - } - - dbg(host, dbg_dma, "DMA FINISHED Size:%i DSTA:%08x DCNT:%08x\n", - size, mci_dsta, mci_dcnt); + dbg(host, dbg_dma, "DMA FINISHED\n"); host->dma_complete = 1; host->complete_what = COMPLETION_FINALIZE; -out: tasklet_schedule(&host->pio_tasklet); spin_unlock_irqrestore(&host->complete_lock, iflags); - return; -fail_request: - host->mrq->data->error = -EINVAL; - host->complete_what = COMPLETION_FINALIZE; - clear_imask(host); - - goto out; } static void finalize_request(struct s3cmci_host *host) @@ -966,7 +911,7 @@ static void finalize_request(struct s3cmci_host *host) * DMA channel and the fifo to clear out any garbage. */ if (mrq->data->error != 0) { if (s3cmci_host_usedma(host)) - s3c2410_dma_ctrl(host->dma, S3C2410_DMAOP_FLUSH); + dmaengine_terminate_all(host->dma); if (host->is2440) { /* Clear failure register and reset fifo. */ @@ -992,29 +937,6 @@ request_done: mmc_request_done(host->mmc, mrq); } -static void s3cmci_dma_setup(struct s3cmci_host *host, - enum dma_data_direction source) -{ - static enum dma_data_direction last_source = -1; - static int setup_ok; - - if (last_source == source) - return; - - last_source = source; - - s3c2410_dma_devconfig(host->dma, source, - host->mem->start + host->sdidata); - - if (!setup_ok) { - s3c2410_dma_config(host->dma, 4); - s3c2410_dma_set_buffdone_fn(host->dma, - s3cmci_dma_done_callback); - s3c2410_dma_setflags(host->dma, S3C2410_DMAF_AUTOSTART); - setup_ok = 1; - } -} - static void s3cmci_send_command(struct s3cmci_host *host, struct mmc_command *cmd) { @@ -1162,43 +1084,45 @@ static int s3cmci_prepare_pio(struct s3cmci_host *host, struct mmc_data *data) static int s3cmci_prepare_dma(struct s3cmci_host *host, struct mmc_data *data) { - int dma_len, i; int rw = data->flags & MMC_DATA_WRITE; + struct dma_async_tx_descriptor *desc; + struct dma_slave_config conf = { + .src_addr = host->mem->start + host->sdidata, + .dst_addr = host->mem->start + host->sdidata, + .src_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES, + .dst_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES, + }; BUG_ON((data->flags & BOTH_DIR) == BOTH_DIR); - s3cmci_dma_setup(host, rw ? DMA_TO_DEVICE : DMA_FROM_DEVICE); - s3c2410_dma_ctrl(host->dma, S3C2410_DMAOP_FLUSH); - - dma_len = dma_map_sg(mmc_dev(host->mmc), data->sg, data->sg_len, - rw ? DMA_TO_DEVICE : DMA_FROM_DEVICE); - - if (dma_len == 0) - return -ENOMEM; - - host->dma_complete = 0; - host->dmatogo = dma_len; - - for (i = 0; i < dma_len; i++) { - int res; - - dbg(host, dbg_dma, "enqueue %i: %08x@%u\n", i, - sg_dma_address(&data->sg[i]), - sg_dma_len(&data->sg[i])); + /* Restore prescaler value */ + writel(host->prescaler, host->base + S3C2410_SDIPRE); - res = s3c2410_dma_enqueue(host->dma, host, - sg_dma_address(&data->sg[i]), - sg_dma_len(&data->sg[i])); + if (!rw) + conf.direction = DMA_DEV_TO_MEM; + else + conf.direction = DMA_MEM_TO_DEV; - if (res) { - s3c2410_dma_ctrl(host->dma, S3C2410_DMAOP_FLUSH); - return -EBUSY; - } - } + dma_map_sg(mmc_dev(host->mmc), data->sg, data->sg_len, + rw ? DMA_TO_DEVICE : DMA_FROM_DEVICE); - s3c2410_dma_ctrl(host->dma, S3C2410_DMAOP_START); + dmaengine_slave_config(host->dma, &conf); + desc = dmaengine_prep_slave_sg(host->dma, data->sg, data->sg_len, + conf.direction, + DMA_CTRL_ACK | DMA_PREP_INTERRUPT); + if (!desc) + goto unmap_exit; + desc->callback = s3cmci_dma_done_callback; + desc->callback_param = host; + dmaengine_submit(desc); + dma_async_issue_pending(host->dma); return 0; + +unmap_exit: + dma_unmap_sg(mmc_dev(host->mmc), data->sg, data->sg_len, + rw ? DMA_TO_DEVICE : DMA_FROM_DEVICE); + return -ENOMEM; } static void s3cmci_send_request(struct mmc_host *mmc) @@ -1676,10 +1600,6 @@ static int s3cmci_probe(struct platform_device *pdev) host->complete_what = COMPLETION_NONE; host->pio_active = XFER_NONE; -#ifdef CONFIG_MMC_S3C_PIODMA - host->dodma = host->pdata->use_dma; -#endif - host->mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); if (!host->mem) { dev_err(&pdev->dev, @@ -1765,17 +1685,17 @@ static int s3cmci_probe(struct platform_device *pdev) /* depending on the dma state, get a dma channel to use. */ if (s3cmci_host_usedma(host)) { - host->dma = s3c2410_dma_request(DMACH_SDI, &s3cmci_dma_client, - host); - if (host->dma < 0) { + 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) { dev_err(&pdev->dev, "cannot get DMA channel.\n"); - if (!s3cmci_host_canpio()) { - ret = -EBUSY; - goto probe_free_gpio_wp; - } else { - dev_warn(&pdev->dev, "falling back to PIO.\n"); - host->dodma = 0; - } + ret = -EBUSY; + goto probe_free_gpio_wp; } } @@ -1816,7 +1736,7 @@ static int s3cmci_probe(struct platform_device *pdev) mmc->max_segs = 128; dbg(host, dbg_debug, - "probe: mode:%s mapped mci_base:%p irq:%u irq_cd:%u dma:%u.\n", + "probe: mode:%s mapped mci_base:%p irq:%u irq_cd:%u dma:%p.\n", (host->is2440?"2440":""), host->base, host->irq, host->irq_cd, host->dma); @@ -1852,7 +1772,7 @@ static int s3cmci_probe(struct platform_device *pdev) probe_free_dma: if (s3cmci_host_usedma(host)) - s3c2410_dma_free(host->dma, &s3cmci_dma_client); + dma_release_channel(host->dma); probe_free_gpio_wp: if (!host->pdata->no_wprotect) @@ -1914,7 +1834,7 @@ static int s3cmci_remove(struct platform_device *pdev) tasklet_disable(&host->pio_tasklet); if (s3cmci_host_usedma(host)) - s3c2410_dma_free(host->dma, &s3cmci_dma_client); + dma_release_channel(host->dma); free_irq(host->irq, host); diff --git a/drivers/mmc/host/s3cmci.h b/drivers/mmc/host/s3cmci.h index c76b53dbeb61..cc2e46cb5c64 100644 --- a/drivers/mmc/host/s3cmci.h +++ b/drivers/mmc/host/s3cmci.h @@ -26,7 +26,7 @@ struct s3cmci_host { void __iomem *base; int irq; int irq_cd; - int dma; + struct dma_chan *dma; unsigned long clk_rate; unsigned long clk_div; @@ -36,8 +36,6 @@ struct s3cmci_host { int is2440; unsigned sdiimsk; unsigned sdidata; - int dodma; - int dmatogo; bool irq_disabled; bool irq_enabled; -- cgit v1.2.3-59-g8ed1b From bf7978483c0c119e0d650710ee713f0ed95939ca Mon Sep 17 00:00:00 2001 From: Geert Uytterhoeven Date: Wed, 9 Jul 2014 14:23:34 +0200 Subject: mmc: tmio-mmc: Add renesas, sdhi-r8a7791 to binding documentation The driver already supports the r8a7791 SoC, and "renesas,sdhi-r8a7791" is already in use. Signed-off-by: Geert Uytterhoeven Cc: Ian Molton Cc: Chris Ball Cc: linux-mmc@vger.kernel.org Signed-off-by: Ulf Hansson --- Documentation/devicetree/bindings/mmc/tmio_mmc.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/Documentation/devicetree/bindings/mmc/tmio_mmc.txt b/Documentation/devicetree/bindings/mmc/tmio_mmc.txt index 6a2a1160a70d..fa0f327cde01 100644 --- a/Documentation/devicetree/bindings/mmc/tmio_mmc.txt +++ b/Documentation/devicetree/bindings/mmc/tmio_mmc.txt @@ -18,6 +18,7 @@ Required properties: "renesas,sdhi-r8a7778" - SDHI IP on R8A7778 SoC "renesas,sdhi-r8a7779" - SDHI IP on R8A7779 SoC "renesas,sdhi-r8a7790" - SDHI IP on R8A7790 SoC + "renesas,sdhi-r8a7791" - SDHI IP on R8A7791 SoC Optional properties: - toshiba,mmc-wrprotect-disable: write-protect detection is unavailable -- cgit v1.2.3-59-g8ed1b From f52d9c4f459bdab128b1b033f26fb52d3418e0f5 Mon Sep 17 00:00:00 2001 From: Peter Griffin Date: Wed, 9 Jul 2014 16:07:32 +0100 Subject: mmc: sdhci-st: Intial support for ST SDHCI controller This platform driver adds initial support for the SDHCI host controller found on STMicroelectronics SoCs. It has been tested on STiH41x b2020 platforms currently. Signed-off-by: Giuseppe Cavallaro Signed-off-by: Peter Griffin Acked-by: Lee Jones Acked-by: Maxime Coquelin Signed-off-by: Ulf Hansson --- drivers/mmc/host/Kconfig | 12 +++ drivers/mmc/host/Makefile | 1 + drivers/mmc/host/sdhci-st.c | 176 ++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 189 insertions(+) create mode 100644 drivers/mmc/host/sdhci-st.c diff --git a/drivers/mmc/host/Kconfig b/drivers/mmc/host/Kconfig index 9682ad1e94b5..d95e75d915d4 100644 --- a/drivers/mmc/host/Kconfig +++ b/drivers/mmc/host/Kconfig @@ -290,6 +290,18 @@ config MMC_MOXART be found on some embedded hardware such as UC-7112-LX. If you have a controller with this interface, say Y here. +config MMC_SDHCI_ST + tristate "SDHCI support on STMicroelectronics SoC" + depends on ARCH_STI + depends on MMC_SDHCI_PLTFM + select MMC_SDHCI_IO_ACCESSORS + help + This selects the Secure Digital Host Controller Interface in + STMicroelectronics SoCs. + + If you have a controller with this interface, say Y or M here. + If unsure, say N. + config MMC_OMAP tristate "TI OMAP Multimedia Card Interface support" depends on ARCH_OMAP diff --git a/drivers/mmc/host/Makefile b/drivers/mmc/host/Makefile index 7f81ddf1dd2c..f211eede8db5 100644 --- a/drivers/mmc/host/Makefile +++ b/drivers/mmc/host/Makefile @@ -68,6 +68,7 @@ obj-$(CONFIG_MMC_SDHCI_OF_HLWD) += sdhci-of-hlwd.o obj-$(CONFIG_MMC_SDHCI_BCM_KONA) += sdhci-bcm-kona.o obj-$(CONFIG_MMC_SDHCI_BCM2835) += sdhci-bcm2835.o obj-$(CONFIG_MMC_SDHCI_MSM) += sdhci-msm.o +obj-$(CONFIG_MMC_SDHCI_ST) += sdhci-st.o ifeq ($(CONFIG_CB710_DEBUG),y) CFLAGS-cb710-mmc += -DDEBUG diff --git a/drivers/mmc/host/sdhci-st.c b/drivers/mmc/host/sdhci-st.c new file mode 100644 index 000000000000..328f348c7243 --- /dev/null +++ b/drivers/mmc/host/sdhci-st.c @@ -0,0 +1,176 @@ +/* + * Support for SDHCI on STMicroelectronics SoCs + * + * Copyright (C) 2014 STMicroelectronics Ltd + * Author: Giuseppe Cavallaro + * Contributors: Peter Griffin + * + * Based on sdhci-cns3xxx.c + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#include +#include +#include +#include +#include + +#include "sdhci-pltfm.h" + +static u32 sdhci_st_readl(struct sdhci_host *host, int reg) +{ + u32 ret; + + switch (reg) { + case SDHCI_CAPABILITIES: + ret = readl_relaxed(host->ioaddr + reg); + /* Support 3.3V and 1.8V */ + ret &= ~SDHCI_CAN_VDD_300; + break; + default: + ret = readl_relaxed(host->ioaddr + reg); + } + return ret; +} + +static const struct sdhci_ops sdhci_st_ops = { + .get_max_clock = sdhci_pltfm_clk_get_max_clock, + .set_clock = sdhci_set_clock, + .set_bus_width = sdhci_set_bus_width, + .read_l = sdhci_st_readl, + .reset = sdhci_reset, +}; + +static const struct sdhci_pltfm_data sdhci_st_pdata = { + .ops = &sdhci_st_ops, + .quirks = SDHCI_QUIRK_NO_ENDATTR_IN_NOPDESC | + SDHCI_QUIRK_CAP_CLOCK_BASE_BROKEN, +}; + + +static int sdhci_st_probe(struct platform_device *pdev) +{ + struct sdhci_host *host; + struct sdhci_pltfm_host *pltfm_host; + struct clk *clk; + int ret = 0; + u16 host_version; + + clk = devm_clk_get(&pdev->dev, "mmc"); + if (IS_ERR(clk)) { + dev_err(&pdev->dev, "Peripheral clk not found\n"); + return PTR_ERR(clk); + } + + host = sdhci_pltfm_init(pdev, &sdhci_st_pdata, 0); + if (IS_ERR(host)) { + dev_err(&pdev->dev, "Failed sdhci_pltfm_init\n"); + return PTR_ERR(host); + } + + ret = mmc_of_parse(host->mmc); + + if (ret) { + dev_err(&pdev->dev, "Failed mmc_of_parse\n"); + return ret; + } + + clk_prepare_enable(clk); + + pltfm_host = sdhci_priv(host); + pltfm_host->clk = clk; + + ret = sdhci_add_host(host); + if (ret) { + dev_err(&pdev->dev, "Failed sdhci_add_host\n"); + goto err_out; + } + + platform_set_drvdata(pdev, host); + + host_version = readw_relaxed((host->ioaddr + SDHCI_HOST_VERSION)); + + dev_info(&pdev->dev, "SDHCI ST Initialised: Host Version: 0x%x Vendor Version 0x%x\n", + ((host_version & SDHCI_SPEC_VER_MASK) >> SDHCI_SPEC_VER_SHIFT), + ((host_version & SDHCI_VENDOR_VER_MASK) >> + SDHCI_VENDOR_VER_SHIFT)); + + return 0; + +err_out: + clk_disable_unprepare(clk); + sdhci_pltfm_free(pdev); + + return ret; +} + +static int sdhci_st_remove(struct platform_device *pdev) +{ + struct sdhci_host *host = platform_get_drvdata(pdev); + struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); + + clk_disable_unprepare(pltfm_host->clk); + + return sdhci_pltfm_unregister(pdev); +} + +#ifdef CONFIG_PM_SLEEP +static int sdhci_st_suspend(struct device *dev) +{ + struct sdhci_host *host = dev_get_drvdata(dev); + struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); + int ret = sdhci_suspend_host(host); + + if (ret) + goto out; + + clk_disable_unprepare(pltfm_host->clk); +out: + return ret; +} + +static int sdhci_st_resume(struct device *dev) +{ + struct sdhci_host *host = dev_get_drvdata(dev); + struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); + + clk_prepare_enable(pltfm_host->clk); + + return sdhci_resume_host(host); +} +#endif + +static SIMPLE_DEV_PM_OPS(sdhci_st_pmops, sdhci_st_suspend, sdhci_st_resume); + +static const struct of_device_id st_sdhci_match[] = { + { .compatible = "st,sdhci" }, + {}, +}; + +MODULE_DEVICE_TABLE(of, st_sdhci_match); + +static struct platform_driver sdhci_st_driver = { + .probe = sdhci_st_probe, + .remove = sdhci_st_remove, + .driver = { + .name = "sdhci-st", + .pm = &sdhci_st_pmops, + .of_match_table = of_match_ptr(st_sdhci_match), + }, +}; + +module_platform_driver(sdhci_st_driver); + +MODULE_DESCRIPTION("SDHCI driver for STMicroelectronics SoCs"); +MODULE_AUTHOR("Giuseppe Cavallaro "); +MODULE_LICENSE("GPL v2"); +MODULE_ALIAS("platform:st-sdhci"); -- cgit v1.2.3-59-g8ed1b From 434936c3486c2b5a9d2011da633baa3096684b50 Mon Sep 17 00:00:00 2001 From: Peter Griffin Date: Wed, 9 Jul 2014 16:07:33 +0100 Subject: mmc: sdhci-st: ST Microelectronics SDHCI binding documentation. This patch adds the device tree binding documentation for the ST SDHCI driver. It documents the differences between the core properties described by mmc.txt and the properties used by the sdhci-st driver. Signed-off-by: Giuseppe Cavallaro Signed-off-by: Peter Griffin Acked-by: Lee Jones Acked-by: Maxime Coquelin Signed-off-by: Ulf Hansson --- Documentation/devicetree/bindings/mmc/sdhci-st.txt | 33 ++++++++++++++++++++++ 1 file changed, 33 insertions(+) create mode 100644 Documentation/devicetree/bindings/mmc/sdhci-st.txt diff --git a/Documentation/devicetree/bindings/mmc/sdhci-st.txt b/Documentation/devicetree/bindings/mmc/sdhci-st.txt new file mode 100644 index 000000000000..7527db447a35 --- /dev/null +++ b/Documentation/devicetree/bindings/mmc/sdhci-st.txt @@ -0,0 +1,33 @@ +* STMicroelectronics sdhci-st MMC/SD controller + +This file documents the differences between the core properties in +Documentation/devicetree/bindings/mmc/mmc.txt and the properties +used by the sdhci-st driver. + +Required properties: +- compatible : Must be "st,sdhci" +- clock-names : Should be "mmc" + See: Documentation/devicetree/bindings/resource-names.txt +- clocks : Phandle of the clock used by the sdhci controler + See: Documentation/devicetree/bindings/clock/clock-bindings.txt + +Optional properties: +- non-removable: non-removable slot + See: Documentation/devicetree/bindings/mmc/mmc.txt +- bus-width: Number of data lines + See: Documentation/devicetree/bindings/mmc/mmc.txt + +Example: + +mmc0: sdhci@fe81e000 { + compatible = "st,sdhci"; + status = "disabled"; + reg = <0xfe81e000 0x1000>; + interrupts = ; + interrupt-names = "mmcirq"; + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_mmc0>; + clock-names = "mmc"; + clocks = <&clk_s_a1_ls 1>; + bus-width = <8> +}; -- cgit v1.2.3-59-g8ed1b From c00c594d8882c64258150dbbf5ff1c328400c65a Mon Sep 17 00:00:00 2001 From: Geert Uytterhoeven Date: Wed, 9 Jul 2014 20:52:40 +0200 Subject: mmc: MMC_OMAP_HS should depend on HAS_DMA If NO_DMA=y: drivers/built-in.o: In function `omap_hsmmc_pre_dma_transfer': omap_hsmmc.c:(.text+0x5a0928): undefined reference to `dma_map_sg' drivers/built-in.o: In function `omap_hsmmc_dma_cleanup': omap_hsmmc.c:(.text+0x5a0e8e): undefined reference to `dma_unmap_sg' drivers/built-in.o: In function `omap_hsmmc_dma_callback': omap_hsmmc.c:(.text+0x5a1f58): undefined reference to `dma_unmap_sg' drivers/built-in.o: In function `omap_hsmmc_post_req': omap_hsmmc.c:(.text+0x5a2082): undefined reference to `dma_unmap_sg' Signed-off-by: Geert Uytterhoeven Acked-by: Laurent Pinchart Signed-off-by: Ulf Hansson --- drivers/mmc/host/Kconfig | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/mmc/host/Kconfig b/drivers/mmc/host/Kconfig index d95e75d915d4..545c03ab8a11 100644 --- a/drivers/mmc/host/Kconfig +++ b/drivers/mmc/host/Kconfig @@ -315,6 +315,7 @@ config MMC_OMAP config MMC_OMAP_HS tristate "TI OMAP High Speed Multimedia Card Interface support" + depends on HAS_DMA depends on ARCH_OMAP2PLUS || COMPILE_TEST help This selects the TI OMAP High Speed Multimedia card Interface. -- cgit v1.2.3-59-g8ed1b From 0e7d43786886b0f8419012800f4f1237ae409170 Mon Sep 17 00:00:00 2001 From: Geert Uytterhoeven Date: Wed, 9 Jul 2014 20:52:41 +0200 Subject: mmc: MMC_SH_MMCIF should depend on HAS_DMA If NO_DMA=y: drivers/built-in.o: In function `sh_mmcif_start_dma_tx': sh_mmcif.c:(.text+0x5a3286): undefined reference to `dma_map_sg' drivers/built-in.o: In function `sh_mmcif_start_dma_rx': sh_mmcif.c:(.text+0x5a33fc): undefined reference to `dma_map_sg' drivers/built-in.o: In function `sh_mmcif_end_cmd': sh_mmcif.c:(.text+0x5a3668): undefined reference to `dma_unmap_sg' Signed-off-by: Geert Uytterhoeven Acked-by: Laurent Pinchart Signed-off-by: Ulf Hansson --- drivers/mmc/host/Kconfig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/mmc/host/Kconfig b/drivers/mmc/host/Kconfig index 545c03ab8a11..b86f128fb8cc 100644 --- a/drivers/mmc/host/Kconfig +++ b/drivers/mmc/host/Kconfig @@ -628,7 +628,7 @@ config MMC_DW_PCI config MMC_SH_MMCIF tristate "SuperH Internal MMCIF support" - depends on MMC_BLOCK + depends on MMC_BLOCK && HAS_DMA depends on SUPERH || ARCH_SHMOBILE || COMPILE_TEST help This selects the MMC Host Interface controller (MMCIF). -- cgit v1.2.3-59-g8ed1b From 1ef94740483da84fd7de0b52442514cf6bf8c2e8 Mon Sep 17 00:00:00 2001 From: Geert Uytterhoeven Date: Wed, 9 Jul 2014 20:52:42 +0200 Subject: mmc: MMC_USDHI6ROL0 should depend on HAS_DMA If NO_DMA=y: drivers/built-in.o: In function `usdhi6_dma_setup': usdhi6rol0.c:(.text+0x5c35fc): undefined reference to `dma_map_sg' drivers/built-in.o: In function `usdhi6_dma_stop_unmap': usdhi6rol0.c:(.text+0x5c3738): undefined reference to `dma_unmap_sg' Signed-off-by: Geert Uytterhoeven Signed-off-by: Ulf Hansson --- drivers/mmc/host/Kconfig | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/mmc/host/Kconfig b/drivers/mmc/host/Kconfig index b86f128fb8cc..6b27f5479f71 100644 --- a/drivers/mmc/host/Kconfig +++ b/drivers/mmc/host/Kconfig @@ -702,6 +702,7 @@ config MMC_WMT config MMC_USDHI6ROL0 tristate "Renesas USDHI6ROL0 SD/SDIO Host Controller support" + depends on HAS_DMA help This selects support for the Renesas USDHI6ROL0 SD/SDIO Host Controller -- cgit v1.2.3-59-g8ed1b From d61b59461b0cd0106f03e566d537b9072029e059 Mon Sep 17 00:00:00 2001 From: Maurice Petallo Date: Tue, 8 Jul 2014 19:11:00 +0800 Subject: mmc: sdhci: Preset value not supported in Baytrail eMMC "SDHCI_QUIRK2_PRESET_VALUE_BROKEN" quirk is added to prohibit preset value enabling for Baytrail eMMC controller. Signed-off-by: Maurice Petallo Acked-by: Adrian Hunter Signed-off-by: Ulf Hansson --- drivers/mmc/host/sdhci-acpi.c | 1 + drivers/mmc/host/sdhci-pci.c | 1 + 2 files changed, 2 insertions(+) diff --git a/drivers/mmc/host/sdhci-acpi.c b/drivers/mmc/host/sdhci-acpi.c index 8ce3c28cb76e..840788ebdc7f 100644 --- a/drivers/mmc/host/sdhci-acpi.c +++ b/drivers/mmc/host/sdhci-acpi.c @@ -127,6 +127,7 @@ static const struct sdhci_acpi_slot sdhci_acpi_slot_int_emmc = { .caps = MMC_CAP_8_BIT_DATA | MMC_CAP_NONREMOVABLE | MMC_CAP_HW_RESET, .caps2 = MMC_CAP2_HC_ERASE_SZ, .flags = SDHCI_ACPI_RUNTIME_PM, + .quirks2 = SDHCI_QUIRK2_PRESET_VALUE_BROKEN, }; static const struct sdhci_acpi_slot sdhci_acpi_slot_int_sdio = { diff --git a/drivers/mmc/host/sdhci-pci.c b/drivers/mmc/host/sdhci-pci.c index 1aeeefc71512..dc336ee6751e 100644 --- a/drivers/mmc/host/sdhci-pci.c +++ b/drivers/mmc/host/sdhci-pci.c @@ -283,6 +283,7 @@ static int byt_sdio_probe_slot(struct sdhci_pci_slot *slot) static const struct sdhci_pci_fixes sdhci_intel_byt_emmc = { .allow_runtime_pm = true, .probe_slot = byt_emmc_probe_slot, + .quirks2 = SDHCI_QUIRK2_PRESET_VALUE_BROKEN, }; static const struct sdhci_pci_fixes sdhci_intel_byt_sdio = { -- cgit v1.2.3-59-g8ed1b From f25c33724d1512a72554c0ad4cb70b43ba15374e Mon Sep 17 00:00:00 2001 From: Maurice Petallo Date: Tue, 8 Jul 2014 19:11:01 +0800 Subject: mmc: sdhci: add DDR50 1.8V mode support for BayTrail eMMC Controller This is to enable DDR50 bus speed mode with 1.8V signaling capability for BayTrail ACPI and PCI mode eMMC Controller. Signed-off-by: Maurice Petallo Acked-by: Adrian Hunter Signed-off-by: Ulf Hansson --- drivers/mmc/host/sdhci-acpi.c | 3 ++- drivers/mmc/host/sdhci-pci.c | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/drivers/mmc/host/sdhci-acpi.c b/drivers/mmc/host/sdhci-acpi.c index 840788ebdc7f..8c5337002c51 100644 --- a/drivers/mmc/host/sdhci-acpi.c +++ b/drivers/mmc/host/sdhci-acpi.c @@ -124,7 +124,8 @@ static const struct sdhci_acpi_chip sdhci_acpi_chip_int = { static const struct sdhci_acpi_slot sdhci_acpi_slot_int_emmc = { .chip = &sdhci_acpi_chip_int, - .caps = MMC_CAP_8_BIT_DATA | MMC_CAP_NONREMOVABLE | MMC_CAP_HW_RESET, + .caps = MMC_CAP_8_BIT_DATA | MMC_CAP_NONREMOVABLE | + MMC_CAP_HW_RESET | MMC_CAP_1_8V_DDR, .caps2 = MMC_CAP2_HC_ERASE_SZ, .flags = SDHCI_ACPI_RUNTIME_PM, .quirks2 = SDHCI_QUIRK2_PRESET_VALUE_BROKEN, diff --git a/drivers/mmc/host/sdhci-pci.c b/drivers/mmc/host/sdhci-pci.c index dc336ee6751e..42f4633d0060 100644 --- a/drivers/mmc/host/sdhci-pci.c +++ b/drivers/mmc/host/sdhci-pci.c @@ -268,7 +268,7 @@ static void sdhci_pci_int_hw_reset(struct sdhci_host *host) static int byt_emmc_probe_slot(struct sdhci_pci_slot *slot) { slot->host->mmc->caps |= MMC_CAP_8_BIT_DATA | MMC_CAP_NONREMOVABLE | - MMC_CAP_HW_RESET; + MMC_CAP_HW_RESET | MMC_CAP_1_8V_DDR; slot->host->mmc->caps2 |= MMC_CAP2_HC_ERASE_SZ; slot->hw_reset = sdhci_pci_int_hw_reset; return 0; -- cgit v1.2.3-59-g8ed1b From 602bcdeb9a84cbf109246a267e91a966a85360d3 Mon Sep 17 00:00:00 2001 From: Georgi Djakov Date: Fri, 11 Jul 2014 20:48:11 +0300 Subject: mmc: sdhci-msm: Fix the binding example The DT binding example in the documentation is missing the -supply suffix for the vmmc and vqmmc regulators. Fix it. Signed-off-by: Georgi Djakov Signed-off-by: Ulf Hansson --- Documentation/devicetree/bindings/mmc/sdhci-msm.txt | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Documentation/devicetree/bindings/mmc/sdhci-msm.txt b/Documentation/devicetree/bindings/mmc/sdhci-msm.txt index 81b33b5b20fc..485483a63d8c 100644 --- a/Documentation/devicetree/bindings/mmc/sdhci-msm.txt +++ b/Documentation/devicetree/bindings/mmc/sdhci-msm.txt @@ -27,8 +27,8 @@ Example: bus-width = <8>; non-removable; - vmmc = <&pm8941_l20>; - vqmmc = <&pm8941_s3>; + vmmc-supply = <&pm8941_l20>; + vqmmc-supply = <&pm8941_s3>; pinctrl-names = "default"; pinctrl-0 = <&sdc1_clk &sdc1_cmd &sdc1_data>; @@ -44,8 +44,8 @@ Example: bus-width = <4>; cd-gpios = <&msmgpio 62 0x1>; - vmmc = <&pm8941_l21>; - vqmmc = <&pm8941_l13>; + vmmc-supply = <&pm8941_l21>; + vqmmc-supply = <&pm8941_l13>; pinctrl-names = "default"; pinctrl-0 = <&sdc2_clk &sdc2_cmd &sdc2_data>; -- cgit v1.2.3-59-g8ed1b From d30f01b0e8955391d5574cf52977c0592eb3f87d Mon Sep 17 00:00:00 2001 From: Georgi Djakov Date: Fri, 11 Jul 2014 20:48:12 +0300 Subject: mmc: sdhci-msm: Remove unnecessary header file inclusion The header is unused. Remove it. Signed-off-by: Georgi Djakov Signed-off-by: Ulf Hansson --- drivers/mmc/host/sdhci-msm.c | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/mmc/host/sdhci-msm.c b/drivers/mmc/host/sdhci-msm.c index 40573a58486a..1a6661ed6205 100644 --- a/drivers/mmc/host/sdhci-msm.c +++ b/drivers/mmc/host/sdhci-msm.c @@ -16,7 +16,6 @@ #include #include -#include #include #include #include -- cgit v1.2.3-59-g8ed1b From 6096d7a8c185f9267d6873ba770feb4948899e79 Mon Sep 17 00:00:00 2001 From: Georgi Djakov Date: Fri, 11 Jul 2014 20:48:13 +0300 Subject: mmc: sdhci-msm: Get COMPILE_TEST support Increase the build testing coverage. Signed-off-by: Georgi Djakov Signed-off-by: Ulf Hansson --- drivers/mmc/host/Kconfig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/mmc/host/Kconfig b/drivers/mmc/host/Kconfig index 6b27f5479f71..451135822464 100644 --- a/drivers/mmc/host/Kconfig +++ b/drivers/mmc/host/Kconfig @@ -356,7 +356,7 @@ config MMC_ATMELMCI config MMC_SDHCI_MSM tristate "Qualcomm SDHCI Controller Support" - depends on ARCH_QCOM + depends on ARCH_QCOM || (ARM && COMPILE_TEST) depends on MMC_SDHCI_PLTFM help This selects the Secure Digital Host Controller Interface (SDHCI) -- cgit v1.2.3-59-g8ed1b From 5204d00f06ac9af4ef9c469cce7f9bbe179739b1 Mon Sep 17 00:00:00 2001 From: Lukas Czerner Date: Wed, 18 Jun 2014 13:18:07 +0200 Subject: mmc: Do not advertise secure discard if it is blacklisted Currently when the device secure discard implementation is blacklisted (MMC_QUIRK_SEC_ERASE_TRIM_BROKEN quirk is set) instead of secure discard we're going to do normal discard, which is wrong. When the secure discard is known to be broken we should just disallow it entirely and not advertise this functionality to the user. Fix it. Also move mmc_fixup_device() in from of mmc_blk_alloc() so we can get quirks set before we attempt to set queue information. Signed-off-by: Lukas Czerner Acked-by: Jaehoon Chung Signed-off-by: Ulf Hansson --- drivers/mmc/card/block.c | 6 +++--- drivers/mmc/core/core.c | 3 ++- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/drivers/mmc/card/block.c b/drivers/mmc/card/block.c index 452782bffebc..ede41f05c392 100644 --- a/drivers/mmc/card/block.c +++ b/drivers/mmc/card/block.c @@ -2028,8 +2028,7 @@ static int mmc_blk_issue_rq(struct mmc_queue *mq, struct request *req) /* complete ongoing async transfer before issuing discard */ if (card->host->areq) mmc_blk_issue_rw_rq(mq, NULL); - if (req->cmd_flags & REQ_SECURE && - !(card->quirks & MMC_QUIRK_SEC_ERASE_TRIM_BROKEN)) + if (req->cmd_flags & REQ_SECURE) ret = mmc_blk_issue_secdiscard_rq(mq, req); else ret = mmc_blk_issue_discard_rq(mq, req); @@ -2432,6 +2431,8 @@ static int mmc_blk_probe(struct mmc_card *card) if (!(card->csd.cmdclass & CCC_BLOCK_READ)) return -ENODEV; + mmc_fixup_device(card, blk_fixups); + md = mmc_blk_alloc(card); if (IS_ERR(md)) return PTR_ERR(md); @@ -2446,7 +2447,6 @@ static int mmc_blk_probe(struct mmc_card *card) goto out; mmc_set_drvdata(card, md); - mmc_fixup_device(card, blk_fixups); if (mmc_add_disk(md)) goto out; diff --git a/drivers/mmc/core/core.c b/drivers/mmc/core/core.c index 7dc0c85fdb60..d03a080fb9cd 100644 --- a/drivers/mmc/core/core.c +++ b/drivers/mmc/core/core.c @@ -2102,7 +2102,8 @@ EXPORT_SYMBOL(mmc_can_sanitize); int mmc_can_secure_erase_trim(struct mmc_card *card) { - if (card->ext_csd.sec_feature_support & EXT_CSD_SEC_ER_EN) + if ((card->ext_csd.sec_feature_support & EXT_CSD_SEC_ER_EN) && + !(card->quirks & MMC_QUIRK_SEC_ERASE_TRIM_BROKEN)) return 1; return 0; } -- cgit v1.2.3-59-g8ed1b From 6b91f2d42ad5506f21f83e25ffd56f27981825a7 Mon Sep 17 00:00:00 2001 From: "Chuanxiao.Dong" Date: Tue, 15 Jul 2014 17:28:05 +0800 Subject: mmc: sdhci-pci: remove PCI PM functions in suspend/resume callback It is not required (in fact it even is not recommended) that a PCI driver's suspend() callback save the standard configuration registers of the device, prepare it for waking up the system, or put it into a low-power state. All of these operations can very well be taken care of by the PCI subsystem, without the driver's participation. Thus remove these PCI functions. For the device which has wake up capability, use device_init_wakeup to init the wake up capability so that PCI core will help to enable the wakeup for it. Signed-off-by: Chuanxiao Dong Signed-off-by: Ulf Hansson --- drivers/mmc/host/sdhci-pci.c | 23 ++++++----------------- 1 file changed, 6 insertions(+), 17 deletions(-) diff --git a/drivers/mmc/host/sdhci-pci.c b/drivers/mmc/host/sdhci-pci.c index 42f4633d0060..c3a1debc9289 100644 --- a/drivers/mmc/host/sdhci-pci.c +++ b/drivers/mmc/host/sdhci-pci.c @@ -1143,18 +1143,13 @@ static int sdhci_pci_suspend(struct device *dev) goto err_pci_suspend; } - pci_save_state(pdev); if (pm_flags & MMC_PM_KEEP_POWER) { - if (pm_flags & MMC_PM_WAKE_SDIO_IRQ) { - pci_pme_active(pdev, true); - pci_enable_wake(pdev, PCI_D3hot, 1); - } - pci_set_power_state(pdev, PCI_D3hot); - } else { - pci_enable_wake(pdev, PCI_D3hot, 0); - pci_disable_device(pdev); - pci_set_power_state(pdev, PCI_D3hot); - } + if (pm_flags & MMC_PM_WAKE_SDIO_IRQ) + device_init_wakeup(dev, true); + else + device_init_wakeup(dev, false); + } else + device_init_wakeup(dev, false); return 0; @@ -1175,12 +1170,6 @@ static int sdhci_pci_resume(struct device *dev) if (!chip) return 0; - pci_set_power_state(pdev, PCI_D0); - pci_restore_state(pdev); - ret = pci_enable_device(pdev); - if (ret) - return ret; - if (chip->fixes && chip->fixes->resume) { ret = chip->fixes->resume(chip); if (ret) -- cgit v1.2.3-59-g8ed1b From b4c27763d749ab3a4dddfcfd44dc5af15bb91a0e Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Wed, 16 Jul 2014 00:45:11 +0200 Subject: mmc: sh_mmcif: Document DT bindings The sh-mmcif driver implements DT support but the bindings are not documented. Document them. Cc: devicetree@vger.kernel.org Signed-off-by: Laurent Pinchart Signed-off-by: Ulf Hansson --- .../devicetree/bindings/mmc/renesas,mmcif.txt | 32 ++++++++++++++++++++++ 1 file changed, 32 insertions(+) create mode 100644 Documentation/devicetree/bindings/mmc/renesas,mmcif.txt diff --git a/Documentation/devicetree/bindings/mmc/renesas,mmcif.txt b/Documentation/devicetree/bindings/mmc/renesas,mmcif.txt new file mode 100644 index 000000000000..299081f94abd --- /dev/null +++ b/Documentation/devicetree/bindings/mmc/renesas,mmcif.txt @@ -0,0 +1,32 @@ +* Renesas Multi Media Card Interface (MMCIF) Controller + +This file documents differences between the core properties in mmc.txt +and the properties used by the MMCIF device. + + +Required properties: + +- compatible: must contain one of the following + - "renesas,mmcif-r8a7740" for the MMCIF found in r8a7740 SoCs + - "renesas,mmcif-r8a7790" for the MMCIF found in r8a7790 SoCs + - "renesas,mmcif-r8a7791" for the MMCIF found in r8a7791 SoCs + - "renesas,sh-mmcif" for the generic MMCIF + +- clocks: reference to the functional clock + +- dmas: reference to the DMA channels, one per channel name listed in the + dma-names property. +- dma-names: must contain "tx" for the transmit DMA channel and "rx" for the + receive DMA channel. + + +Example: R8A7790 (R-Car H2) MMCIF0 + + mmcif0: mmc@ee200000 { + compatible = "renesas,mmcif-r8a7790", "renesas,sh-mmcif"; + reg = <0 0xee200000 0 0x80>; + interrupts = <0 169 IRQ_TYPE_LEVEL_HIGH>; + clocks = <&mstp3_clks R8A7790_CLK_MMCIF0>; + dmas = <&dmac0 0xd1>, <&dmac0 0xd2>; + dma-names = "tx", "rx"; + }; -- cgit v1.2.3-59-g8ed1b From d25006e7e5045e76575e9f58903efc1a860a3ff1 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Wed, 16 Jul 2014 00:45:12 +0200 Subject: mmc: sh_mmcif: Fix DMA slave address configuration Commit e5a233cb647d749de2f188477c9a54b94d90477f ("mmc: sh_mmcif: Factorize DMA channel request and configuration code") incorrectly set the destination address for both slave channels instead of setting the source address for the receive channel. Fix that. Signed-off-by: Laurent Pinchart Signed-off-by: Ulf Hansson --- drivers/mmc/host/sh_mmcif.c | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/drivers/mmc/host/sh_mmcif.c b/drivers/mmc/host/sh_mmcif.c index 0289b4ecccb3..863d21ee4a2f 100644 --- a/drivers/mmc/host/sh_mmcif.c +++ b/drivers/mmc/host/sh_mmcif.c @@ -386,7 +386,7 @@ sh_mmcif_request_dma_one(struct sh_mmcif_host *host, struct sh_mmcif_plat_data *pdata, enum dma_transfer_direction direction) { - struct dma_slave_config cfg; + struct dma_slave_config cfg = { 0, }; struct dma_chan *chan; unsigned int slave_id; struct resource *res; @@ -417,8 +417,12 @@ sh_mmcif_request_dma_one(struct sh_mmcif_host *host, /* In the OF case the driver will get the slave ID from the DT */ cfg.slave_id = slave_id; cfg.direction = direction; - cfg.dst_addr = res->start + MMCIF_CE_DATA; - cfg.src_addr = 0; + + if (direction == DMA_DEV_TO_MEM) + cfg.src_addr = res->start + MMCIF_CE_DATA; + else + cfg.dst_addr = res->start + MMCIF_CE_DATA; + ret = dmaengine_slave_config(chan, &cfg); if (ret < 0) { dma_release_channel(chan); -- cgit v1.2.3-59-g8ed1b From e36152aa84cf68bd7f09acffd480cd2b6aa5480d Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Wed, 16 Jul 2014 00:45:13 +0200 Subject: mmc: sh_mmcif: Configure DMA slave bus width The data register is 4 bytes wide, hardcode the DMA transfer size to 4 bytes in both directions. Signed-off-by: Laurent Pinchart Signed-off-by: Ulf Hansson --- drivers/mmc/host/sh_mmcif.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/drivers/mmc/host/sh_mmcif.c b/drivers/mmc/host/sh_mmcif.c index 863d21ee4a2f..d11708c815d7 100644 --- a/drivers/mmc/host/sh_mmcif.c +++ b/drivers/mmc/host/sh_mmcif.c @@ -418,10 +418,13 @@ sh_mmcif_request_dma_one(struct sh_mmcif_host *host, cfg.slave_id = slave_id; cfg.direction = direction; - if (direction == DMA_DEV_TO_MEM) + if (direction == DMA_DEV_TO_MEM) { cfg.src_addr = res->start + MMCIF_CE_DATA; - else + cfg.src_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES; + } else { cfg.dst_addr = res->start + MMCIF_CE_DATA; + cfg.dst_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES; + } ret = dmaengine_slave_config(chan, &cfg); if (ret < 0) { -- cgit v1.2.3-59-g8ed1b From 39ab196243e8732ad68928be97129305b29afbbd Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Wed, 16 Jul 2014 00:45:14 +0200 Subject: mmc: tmio: Configure DMA slave bus width Even though some implementations support 4-bytes data register access, the tmio driver never configures the hardware in such a way and always performs 2-bytes accesses. Hardcode the DMA transfer size to 2 bytes. Signed-off-by: Laurent Pinchart Signed-off-by: Ulf Hansson --- drivers/mmc/host/tmio_mmc_dma.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/mmc/host/tmio_mmc_dma.c b/drivers/mmc/host/tmio_mmc_dma.c index 03e7b280cb4c..eb8f1d5c34b1 100644 --- a/drivers/mmc/host/tmio_mmc_dma.c +++ b/drivers/mmc/host/tmio_mmc_dma.c @@ -294,6 +294,7 @@ void tmio_mmc_request_dma(struct tmio_mmc_host *host, struct tmio_mmc_data *pdat cfg.slave_id = pdata->dma->slave_id_tx; cfg.direction = DMA_MEM_TO_DEV; cfg.dst_addr = res->start + (CTL_SD_DATA_PORT << host->pdata->bus_shift); + cfg.dst_addr_width = DMA_SLAVE_BUSWIDTH_2_BYTES; cfg.src_addr = 0; ret = dmaengine_slave_config(host->chan_tx, &cfg); if (ret < 0) @@ -312,6 +313,7 @@ void tmio_mmc_request_dma(struct tmio_mmc_host *host, struct tmio_mmc_data *pdat cfg.slave_id = pdata->dma->slave_id_rx; cfg.direction = DMA_DEV_TO_MEM; cfg.src_addr = cfg.dst_addr; + cfg.src_addr_width = DMA_SLAVE_BUSWIDTH_2_BYTES; cfg.dst_addr = 0; ret = dmaengine_slave_config(host->chan_rx, &cfg); if (ret < 0) -- cgit v1.2.3-59-g8ed1b From 3df5b28149df5c04f0fbb4efe15ef870ce93de93 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Wed, 16 Jul 2014 11:53:42 +0200 Subject: mmc: sdhci-pxav3: Use devm_* managed helpers This simplifies probe error and remove code paths. Signed-off-by: Laurent Pinchart Signed-off-by: Ulf Hansson --- drivers/mmc/host/sdhci-pxav3.c | 13 +++---------- 1 file changed, 3 insertions(+), 10 deletions(-) diff --git a/drivers/mmc/host/sdhci-pxav3.c b/drivers/mmc/host/sdhci-pxav3.c index f4f128947561..6f842fb8e6b8 100644 --- a/drivers/mmc/host/sdhci-pxav3.c +++ b/drivers/mmc/host/sdhci-pxav3.c @@ -288,15 +288,13 @@ static int sdhci_pxav3_probe(struct platform_device *pdev) int ret; struct clk *clk; - pxa = kzalloc(sizeof(struct sdhci_pxa), GFP_KERNEL); + pxa = devm_kzalloc(&pdev->dev, sizeof(struct sdhci_pxa), GFP_KERNEL); if (!pxa) return -ENOMEM; host = sdhci_pltfm_init(pdev, &sdhci_pxav3_pdata, 0); - if (IS_ERR(host)) { - kfree(pxa); + if (IS_ERR(host)) return PTR_ERR(host); - } if (of_device_is_compatible(np, "marvell,armada-380-sdhci")) { ret = mv_conf_mbus_windows(pdev, mv_mbus_dram_info()); @@ -308,7 +306,7 @@ static int sdhci_pxav3_probe(struct platform_device *pdev) pltfm_host = sdhci_priv(host); pltfm_host->priv = pxa; - clk = clk_get(dev, NULL); + clk = devm_clk_get(dev, NULL); if (IS_ERR(clk)) { dev_err(dev, "failed to get io clock\n"); ret = PTR_ERR(clk); @@ -389,11 +387,9 @@ err_add_host: pm_runtime_put_sync(&pdev->dev); pm_runtime_disable(&pdev->dev); clk_disable_unprepare(clk); - clk_put(clk); err_clk_get: err_mbus_win: sdhci_pltfm_free(pdev); - kfree(pxa); return ret; } @@ -401,17 +397,14 @@ static int sdhci_pxav3_remove(struct platform_device *pdev) { struct sdhci_host *host = platform_get_drvdata(pdev); struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); - struct sdhci_pxa *pxa = pltfm_host->priv; pm_runtime_get_sync(&pdev->dev); sdhci_remove_host(host, 1); pm_runtime_disable(&pdev->dev); clk_disable_unprepare(pltfm_host->clk); - clk_put(pltfm_host->clk); sdhci_pltfm_free(pdev); - kfree(pxa); return 0; } -- cgit v1.2.3-59-g8ed1b From 3a33a94ce27068c8fef63a4f9ab7cff1210e2a4e Mon Sep 17 00:00:00 2001 From: Sonny Rao Date: Mon, 4 Aug 2014 18:19:50 -0700 Subject: mmc: dw_mmc: change to use recommended reset procedure This patch changes the fifo reset code to follow the reset procedure outlined in the documentation of Synopsys Mobile storage host databook. Signed-off-by: Sonny Rao Signed-off-by: Yuvaraj Kumar C D Acked-by: Seungwon Jeon [sonnyrao: fix compile for !CONFIG_MMC_DW_IDMAC case] Acked-by: Jaehoon Chung Signed-off-by: Ulf Hansson --- drivers/mmc/host/dw_mmc.c | 87 ++++++++++++++++++++++++++++++++++------------- drivers/mmc/host/dw_mmc.h | 5 +++ 2 files changed, 69 insertions(+), 23 deletions(-) diff --git a/drivers/mmc/host/dw_mmc.c b/drivers/mmc/host/dw_mmc.c index 1ac227c603b7..39cf54f479d9 100644 --- a/drivers/mmc/host/dw_mmc.c +++ b/drivers/mmc/host/dw_mmc.c @@ -111,8 +111,7 @@ static const u8 tuning_blk_pattern_8bit[] = { 0xff, 0x77, 0x77, 0xff, 0x77, 0xbb, 0xdd, 0xee, }; -static inline bool dw_mci_fifo_reset(struct dw_mci *host); -static inline bool dw_mci_ctrl_all_reset(struct dw_mci *host); +static bool dw_mci_reset(struct dw_mci *host); #if defined(CONFIG_DEBUG_FS) static int dw_mci_req_show(struct seq_file *s, void *v) @@ -1235,7 +1234,7 @@ static int dw_mci_data_complete(struct dw_mci *host, struct mmc_data *data) * After an error, there may be data lingering * in the FIFO */ - dw_mci_fifo_reset(host); + dw_mci_reset(host); } else { data->bytes_xfered = data->blocks * data->blksz; data->error = 0; @@ -1352,7 +1351,7 @@ static void dw_mci_tasklet_func(unsigned long priv) /* CMD error in data command */ if (mrq->cmd->error && mrq->data) - dw_mci_fifo_reset(host); + dw_mci_reset(host); host->cmd = NULL; host->data = NULL; @@ -1963,14 +1962,8 @@ static void dw_mci_work_routine_card(struct work_struct *work) } /* Power down slot */ - if (present == 0) { - /* Clear down the FIFO */ - dw_mci_fifo_reset(host); -#ifdef CONFIG_MMC_DW_IDMAC - dw_mci_idmac_reset(host); -#endif - - } + if (present == 0) + dw_mci_reset(host); spin_unlock_bh(&host->lock); @@ -2208,8 +2201,11 @@ static bool dw_mci_ctrl_reset(struct dw_mci *host, u32 reset) return false; } -static inline bool dw_mci_fifo_reset(struct dw_mci *host) +static bool dw_mci_reset(struct dw_mci *host) { + u32 flags = SDMMC_CTRL_RESET | SDMMC_CTRL_FIFO_RESET; + bool ret = false; + /* * Reseting generates a block interrupt, hence setting * the scatter-gather pointer to NULL. @@ -2219,15 +2215,60 @@ static inline bool dw_mci_fifo_reset(struct dw_mci *host) host->sg = NULL; } - return dw_mci_ctrl_reset(host, SDMMC_CTRL_FIFO_RESET); -} + if (host->use_dma) + flags |= SDMMC_CTRL_DMA_RESET; -static inline bool dw_mci_ctrl_all_reset(struct dw_mci *host) -{ - return dw_mci_ctrl_reset(host, - SDMMC_CTRL_FIFO_RESET | - SDMMC_CTRL_RESET | - SDMMC_CTRL_DMA_RESET); + if (dw_mci_ctrl_reset(host, flags)) { + /* + * In all cases we clear the RAWINTS register to clear any + * interrupts. + */ + mci_writel(host, RINTSTS, 0xFFFFFFFF); + + /* if using dma we wait for dma_req to clear */ + if (host->use_dma) { + unsigned long timeout = jiffies + msecs_to_jiffies(500); + u32 status; + do { + status = mci_readl(host, STATUS); + if (!(status & SDMMC_STATUS_DMA_REQ)) + break; + cpu_relax(); + } while (time_before(jiffies, timeout)); + + if (status & SDMMC_STATUS_DMA_REQ) { + dev_err(host->dev, + "%s: Timeout waiting for dma_req to " + "clear during reset\n", __func__); + goto ciu_out; + } + + /* when using DMA next we reset the fifo again */ + if (!dw_mci_ctrl_reset(host, SDMMC_CTRL_FIFO_RESET)) + goto ciu_out; + } + } else { + /* if the controller reset bit did clear, then set clock regs */ + if (!(mci_readl(host, CTRL) & SDMMC_CTRL_RESET)) { + dev_err(host->dev, "%s: fifo/dma reset bits didn't " + "clear but ciu was reset, doing clock update\n", + __func__); + goto ciu_out; + } + } + +#if IS_ENABLED(CONFIG_MMC_DW_IDMAC) + /* It is also recommended that we reset and reprogram idmac */ + dw_mci_idmac_reset(host); +#endif + + ret = true; + +ciu_out: + /* After a CTRL reset we need to have CIU set clock registers */ + mci_send_cmd(host->cur_slot, SDMMC_CMD_UPD_CLK, 0); + + return ret; } #ifdef CONFIG_OF @@ -2425,7 +2466,7 @@ int dw_mci_probe(struct dw_mci *host) } /* Reset all blocks */ - if (!dw_mci_ctrl_all_reset(host)) + if (!dw_mci_ctrl_reset(host, SDMMC_CTRL_ALL_RESET_FLAGS)) return -ENODEV; host->dma_ops = host->pdata->dma_ops; @@ -2612,7 +2653,7 @@ int dw_mci_resume(struct dw_mci *host) } } - if (!dw_mci_ctrl_all_reset(host)) { + if (!dw_mci_ctrl_reset(host, SDMMC_CTRL_ALL_RESET_FLAGS)) { ret = -ENODEV; return ret; } diff --git a/drivers/mmc/host/dw_mmc.h b/drivers/mmc/host/dw_mmc.h index 738fa241d058..08fd956d81f3 100644 --- a/drivers/mmc/host/dw_mmc.h +++ b/drivers/mmc/host/dw_mmc.h @@ -129,6 +129,7 @@ #define SDMMC_CMD_INDX(n) ((n) & 0x1F) /* Status register defines */ #define SDMMC_GET_FCNT(x) (((x)>>17) & 0x1FFF) +#define SDMMC_STATUS_DMA_REQ BIT(31) /* FIFOTH register defines */ #define SDMMC_SET_FIFOTH(m, r, t) (((m) & 0x7) << 28 | \ ((r) & 0xFFF) << 16 | \ @@ -150,6 +151,10 @@ /* Card read threshold */ #define SDMMC_SET_RD_THLD(v, x) (((v) & 0x1FFF) << 16 | (x)) +/* All ctrl reset bits */ +#define SDMMC_CTRL_ALL_RESET_FLAGS \ + (SDMMC_CTRL_RESET | SDMMC_CTRL_FIFO_RESET | SDMMC_CTRL_DMA_RESET) + /* Register access macros */ #define mci_readl(dev, reg) \ __raw_readl((dev)->regs + SDMMC_##reg) -- cgit v1.2.3-59-g8ed1b From 1cb9da502835dad73dda772b20c1e792f4e71589 Mon Sep 17 00:00:00 2001 From: Ulf Hansson Date: Thu, 12 Jun 2014 14:42:23 +0200 Subject: mmc: mmci: Remove redundant check of status for DATA irq We don't need to verify the content of the status register twice, while we are about to handle a DATA irq. Instead let's leave all verification to be handled by mmci_data_irq(). Cc: Peter Maydell Cc: John Stultz Cc: Russell King Tested-by: Kees Cook Tested-by: John Stultz Cc: # v3.15+ Signed-off-by: Ulf Hansson --- drivers/mmc/host/mmci.c | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/drivers/mmc/host/mmci.c b/drivers/mmc/host/mmci.c index 6483c5cc6735..a723ed2883f9 100644 --- a/drivers/mmc/host/mmci.c +++ b/drivers/mmc/host/mmci.c @@ -888,6 +888,10 @@ static void mmci_data_irq(struct mmci_host *host, struct mmc_data *data, unsigned int status) { + /* Make sure we have data to handle */ + if (!data) + return; + /* First check for errors */ if (status & (MCI_DATACRCFAIL|MCI_DATATIMEOUT|MCI_STARTBITERR| MCI_TXUNDERRUN|MCI_RXOVERRUN)) { @@ -1206,7 +1210,6 @@ static irqreturn_t mmci_irq(int irq, void *dev_id) do { struct mmc_command *cmd; - struct mmc_data *data; status = readl(host->base + MMCISTATUS); @@ -1232,11 +1235,7 @@ static irqreturn_t mmci_irq(int irq, void *dev_id) MCI_CMDSENT|MCI_CMDRESPEND) && cmd) mmci_cmd_irq(host, cmd, status); - data = host->data; - if (status & (MCI_DATACRCFAIL|MCI_DATATIMEOUT|MCI_STARTBITERR| - MCI_TXUNDERRUN|MCI_RXOVERRUN|MCI_DATAEND| - MCI_DATABLOCKEND) && data) - mmci_data_irq(host, data, status); + mmci_data_irq(host, host->data, status); /* Don't poll for busy completion in irq context. */ if (host->busy_status) -- cgit v1.2.3-59-g8ed1b From ad82bfea44835da9633548e2031a1af4a9965c14 Mon Sep 17 00:00:00 2001 From: Ulf Hansson Date: Thu, 12 Jun 2014 15:01:57 +0200 Subject: mmc: mmci: Move all CMD irq handling to mmci_cmd_irq() This patch won't change the behavior of how mmci deals with CMD irqs. By moving code from mmci_irq() to mmci_cmd_irq(), we getter a better overview of what going on. Cc: Peter Maydell Cc: John Stultz Cc: Russell King Tested-by: Kees Cook Tested-by: John Stultz Cc: # v3.15+ Signed-off-by: Ulf Hansson --- drivers/mmc/host/mmci.c | 22 ++++++++++++---------- 1 file changed, 12 insertions(+), 10 deletions(-) diff --git a/drivers/mmc/host/mmci.c b/drivers/mmc/host/mmci.c index a723ed2883f9..5d20bfba3e49 100644 --- a/drivers/mmc/host/mmci.c +++ b/drivers/mmc/host/mmci.c @@ -960,9 +960,17 @@ mmci_cmd_irq(struct mmci_host *host, struct mmc_command *cmd, unsigned int status) { void __iomem *base = host->base; - bool sbc = (cmd == host->mrq->sbc); - bool busy_resp = host->variant->busy_detect && - (cmd->flags & MMC_RSP_BUSY); + bool sbc, busy_resp; + + 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))) + return; /* Check if we need to wait for busy completion. */ if (host->busy_status && (status & MCI_ST_CARDBUSY)) @@ -1209,8 +1217,6 @@ static irqreturn_t mmci_irq(int irq, void *dev_id) spin_lock(&host->lock); do { - struct mmc_command *cmd; - status = readl(host->base + MMCISTATUS); if (host->singleirq) { @@ -1230,11 +1236,7 @@ static irqreturn_t mmci_irq(int irq, void *dev_id) dev_dbg(mmc_dev(host->mmc), "irq0 (data+cmd) %08x\n", status); - cmd = host->cmd; - if ((status|host->busy_status) & (MCI_CMDCRCFAIL|MCI_CMDTIMEOUT| - MCI_CMDSENT|MCI_CMDRESPEND) && cmd) - mmci_cmd_irq(host, cmd, status); - + mmci_cmd_irq(host, host->cmd, status); mmci_data_irq(host, host->data, status); /* Don't poll for busy completion in irq context. */ -- cgit v1.2.3-59-g8ed1b From 7878289b269d41c8e611aa6d4519feae706e49f3 Mon Sep 17 00:00:00 2001 From: Ulf Hansson Date: Fri, 13 Jun 2014 13:21:38 +0200 Subject: mmc: mmci: Reverse IRQ handling for the arm_variant Commit "mmc: mmci: Handle CMD irq before DATA irq", caused an issue when using the ARM model of the PL181 and running QEMU. The bug was reported for the following QEMU version: $ qemu-system-arm -version QEMU emulator version 2.0.0 (Debian 2.0.0+dfsg-2ubuntu1.1), Copyright (c) 2003-2008 Fabrice Bellard To resolve the problem, let's restore the old behavior were the DATA irq is handled prior the CMD irq, but only for the arm_variant, which the problem was reported for. Reported-by: John Stultz Cc: Peter Maydell Cc: Russell King Tested-by: Kees Cook Tested-by: John Stultz Cc: # v3.15+ Signed-off-by: Ulf Hansson --- drivers/mmc/host/mmci.c | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/drivers/mmc/host/mmci.c b/drivers/mmc/host/mmci.c index 5d20bfba3e49..e4d470704150 100644 --- a/drivers/mmc/host/mmci.c +++ b/drivers/mmc/host/mmci.c @@ -74,6 +74,7 @@ static unsigned int fmax = 515633; * @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. + * @reversed_irq_handling: handle data irq before cmd irq. */ struct variant_data { unsigned int clkreg; @@ -97,6 +98,7 @@ struct variant_data { bool pwrreg_nopower; bool explicit_mclk_control; bool qcom_fifo; + bool reversed_irq_handling; }; static struct variant_data variant_arm = { @@ -105,6 +107,7 @@ static struct variant_data variant_arm = { .datalength_bits = 16, .pwrreg_powerup = MCI_PWR_UP, .f_max = 100000000, + .reversed_irq_handling = true, }; static struct variant_data variant_arm_extended_fifo = { @@ -1236,8 +1239,13 @@ static irqreturn_t mmci_irq(int irq, void *dev_id) dev_dbg(mmc_dev(host->mmc), "irq0 (data+cmd) %08x\n", status); - mmci_cmd_irq(host, host->cmd, status); - mmci_data_irq(host, host->data, status); + if (host->variant->reversed_irq_handling) { + mmci_data_irq(host, host->data, status); + mmci_cmd_irq(host, host->cmd, status); + } else { + mmci_cmd_irq(host, host->cmd, status); + mmci_data_irq(host, host->data, status); + } /* Don't poll for busy completion in irq context. */ if (host->busy_status) -- cgit v1.2.3-59-g8ed1b From 26375b5c8449927f740ce0e837e23f45c951fb80 Mon Sep 17 00:00:00 2001 From: Jaehoon Chung Date: Thu, 7 Aug 2014 16:37:58 +0900 Subject: mmc: dw_mmc: Slot quirk "disable-wp" is deprecated. Slot quirks "disable-wp" is deprecated. Instead, use the host quirk "disable-wp". (Because the slot-node is removed in dt-file.) Signed-off-by: Jaehoon Chung Tested-by: Sachin Kamat Acked-by: Seungwon Jeon Reviewed-by: Doug Anderson Tested-by: Doug Anderson Signed-off-by: Ulf Hansson --- drivers/mmc/host/dw_mmc.c | 11 +++++++++-- include/linux/mmc/dw_mmc.h | 2 ++ 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/drivers/mmc/host/dw_mmc.c b/drivers/mmc/host/dw_mmc.c index 39cf54f479d9..8f216edbdf08 100644 --- a/drivers/mmc/host/dw_mmc.c +++ b/drivers/mmc/host/dw_mmc.c @@ -996,7 +996,8 @@ static int dw_mci_get_ro(struct mmc_host *mmc) int gpio_ro = mmc_gpio_get_ro(mmc); /* Use platform get_ro function, else try on board write protect */ - if (slot->quirks & DW_MCI_SLOT_QUIRK_NO_WRITE_PROTECT) + if ((slot->quirks & DW_MCI_SLOT_QUIRK_NO_WRITE_PROTECT) || + (slot->host->quirks & DW_MCI_QUIRK_NO_WRITE_PROTECT)) read_only = 0; else if (!IS_ERR_VALUE(gpio_ro)) read_only = gpio_ro; @@ -2014,8 +2015,11 @@ static int dw_mci_of_get_slot_quirks(struct device *dev, u8 slot) /* get quirks */ for (idx = 0; idx < ARRAY_SIZE(of_slot_quirks); idx++) - if (of_get_property(np, of_slot_quirks[idx].quirk, NULL)) + if (of_get_property(np, of_slot_quirks[idx].quirk, NULL)) { + dev_warn(dev, "Slot quirk %s is deprecated\n", + of_slot_quirks[idx].quirk); quirks |= of_slot_quirks[idx].id; + } return quirks; } @@ -2279,6 +2283,9 @@ static struct dw_mci_of_quirks { { .quirk = "broken-cd", .id = DW_MCI_QUIRK_BROKEN_CARD_DETECTION, + }, { + .quirk = "disable-wp", + .id = DW_MCI_QUIRK_NO_WRITE_PROTECT, }, }; diff --git a/include/linux/mmc/dw_mmc.h b/include/linux/mmc/dw_mmc.h index babaea93bca6..29ce014ab421 100644 --- a/include/linux/mmc/dw_mmc.h +++ b/include/linux/mmc/dw_mmc.h @@ -213,6 +213,8 @@ struct dw_mci_dma_ops { #define DW_MCI_QUIRK_HIGHSPEED BIT(2) /* Unreliable card detection */ #define DW_MCI_QUIRK_BROKEN_CARD_DETECTION BIT(3) +/* No write protect */ +#define DW_MCI_QUIRK_NO_WRITE_PROTECT BIT(4) /* Slot level quirks */ /* This slot has no write protect */ -- cgit v1.2.3-59-g8ed1b From c83c8737e3edb33f60101d2d7692675d0cb6bdf1 Mon Sep 17 00:00:00 2001 From: Jaehoon Chung Date: Thu, 7 Aug 2014 16:37:59 +0900 Subject: mmc: dw_mmc: modify the dt-binding for removing slot-node and supports-highspeed Almost all SoCs use one slot per host controller. (Even if controller can support the multiple slot, Recommend to use one slot per host controller.) Don't use the slot-node and deprecate the "supports-highspeed" property. Instead, use the cap-mmc/sd-highspeed. Signed-off-by: Jaehoon Chung Reviewed-by: Tushar Behera Reviewed-by: Ulf Hansson Tested-by: Sachin Kamat Acked-by: Seungwon Jeon Reviewed-by: Doug Anderson Signed-off-by: Ulf Hansson --- .../devicetree/bindings/mmc/exynos-dw-mshc.txt | 17 +++++------------ Documentation/devicetree/bindings/mmc/k3-dw-mshc.txt | 12 +++++------- .../devicetree/bindings/mmc/synopsys-dw-mshc.txt | 12 +++++------- 3 files changed, 15 insertions(+), 26 deletions(-) diff --git a/Documentation/devicetree/bindings/mmc/exynos-dw-mshc.txt b/Documentation/devicetree/bindings/mmc/exynos-dw-mshc.txt index 532b1d440abc..6cd3525d0e09 100644 --- a/Documentation/devicetree/bindings/mmc/exynos-dw-mshc.txt +++ b/Documentation/devicetree/bindings/mmc/exynos-dw-mshc.txt @@ -46,13 +46,14 @@ Required Properties: - if CIU clock divider value is 0 (that is divide by 1), both tx and rx phase shift clocks should be 0. -Required properties for a slot: +Required properties for a slot (Deprecated - Recommend to use one slot per host): * gpios: specifies a list of gpios used for command, clock and data bus. The first gpio is the command line and the second gpio is the clock line. The rest of the gpios (depending on the bus-width property) are the data lines in no particular order. The format of the gpio specifier depends on the gpio controller. +(Deprecated - Refer to Documentation/devicetree/binding/pinctrl/samsung-pinctrl.txt) Example: @@ -69,21 +70,13 @@ Example: dwmmc0@12200000 { num-slots = <1>; - supports-highspeed; + cap-mmc-highspeed; + cap-sd-highspeed; broken-cd; fifo-depth = <0x80>; card-detect-delay = <200>; samsung,dw-mshc-ciu-div = <3>; samsung,dw-mshc-sdr-timing = <2 3>; samsung,dw-mshc-ddr-timing = <1 2>; - - slot@0 { - reg = <0>; - bus-width = <8>; - gpios = <&gpc0 0 2 0 3>, <&gpc0 1 2 0 3>, - <&gpc1 0 2 3 3>, <&gpc1 1 2 3 3>, - <&gpc1 2 2 3 3>, <&gpc1 3 2 3 3>, - <&gpc0 3 2 3 3>, <&gpc0 4 2 3 3>, - <&gpc0 5 2 3 3>, <&gpc0 6 2 3 3>; - }; + bus-width = <8>; }; diff --git a/Documentation/devicetree/bindings/mmc/k3-dw-mshc.txt b/Documentation/devicetree/bindings/mmc/k3-dw-mshc.txt index e5bc49f764d1..3b3544931437 100644 --- a/Documentation/devicetree/bindings/mmc/k3-dw-mshc.txt +++ b/Documentation/devicetree/bindings/mmc/k3-dw-mshc.txt @@ -34,13 +34,11 @@ Example: num-slots = <1>; vmmc-supply = <&ldo12>; fifo-depth = <0x100>; - supports-highspeed; pinctrl-names = "default"; pinctrl-0 = <&sd_pmx_pins &sd_cfg_func1 &sd_cfg_func2>; - slot@0 { - reg = <0>; - bus-width = <4>; - disable-wp; - cd-gpios = <&gpio10 3 0>; - }; + bus-width = <4>; + disable-wp; + cd-gpios = <&gpio10 3 0>; + cap-mmc-highspeed; + cap-sd-highspeed; }; diff --git a/Documentation/devicetree/bindings/mmc/synopsys-dw-mshc.txt b/Documentation/devicetree/bindings/mmc/synopsys-dw-mshc.txt index 2d4a7258a10d..346c6095a615 100644 --- a/Documentation/devicetree/bindings/mmc/synopsys-dw-mshc.txt +++ b/Documentation/devicetree/bindings/mmc/synopsys-dw-mshc.txt @@ -67,7 +67,8 @@ Optional properties: * card-detect-delay: Delay in milli-seconds before detecting card after card insert event. The default value is 0. -* supports-highspeed: Enables support for high speed cards (up to 50MHz) +* supports-highspeed (DEPRECATED): Enables support for high speed cards (up to 50MHz) + (use "cap-mmc-highspeed" or "cap-sd-highspeed" instead) * broken-cd: as documented in mmc core bindings. @@ -98,14 +99,11 @@ board specific portions as listed below. clock-frequency = <400000000>; clock-freq-min-max = <400000 200000000>; num-slots = <1>; - supports-highspeed; broken-cd; fifo-depth = <0x80>; card-detect-delay = <200>; vmmc-supply = <&buck8>; - - slot@0 { - reg = <0>; - bus-width = <8>; - }; + bus-width = <8>; + cap-mmc-highspeed; + cap-sd-highspeed; }; -- cgit v1.2.3-59-g8ed1b