Commit 89ad2be7 authored by Abhilash Kesavan's avatar Abhilash Kesavan Committed by Ulf Hansson

mmc: dw_mmc: exynos: Add support for exynos7

The Exynos7 has a DWMMC controller (v2.70a) which is different from
prior versions. This patch adds new compatible strings for exynos7.
This patch also fixes the CLKSEL register offset on exynos7.
Signed-off-by: default avatarAbhilash Kesavan <a.kesavan@samsung.com>
Signed-off-by: default avatarYuvaraj Kumar C D <yuvaraj.cd@samsung.com>
Tested-by: default avatarVivek Gautam <gautam.vivek@samsung.com>
Acked-by: default avatarJaehoon Chung <jh80.chung@samsung.com>
Signed-off-by: default avatarUlf Hansson <ulf.hansson@linaro.org>
parent 2d079c43
...@@ -18,6 +18,10 @@ Required Properties: ...@@ -18,6 +18,10 @@ Required Properties:
specific extensions. specific extensions.
- "samsung,exynos5420-dw-mshc": for controllers with Samsung Exynos5420 - "samsung,exynos5420-dw-mshc": for controllers with Samsung Exynos5420
specific extensions. specific extensions.
- "samsung,exynos7-dw-mshc": for controllers with Samsung Exynos7
specific extensions.
- "samsung,exynos7-dw-mshc-smu": for controllers with Samsung Exynos7
specific extensions having an SMU.
* samsung,dw-mshc-ciu-div: Specifies the divider value for the card interface * samsung,dw-mshc-ciu-div: Specifies the divider value for the card interface
unit (ciu) clock. This property is applicable only for Exynos5 SoC's and unit (ciu) clock. This property is applicable only for Exynos5 SoC's and
......
...@@ -25,6 +25,7 @@ ...@@ -25,6 +25,7 @@
#define NUM_PINS(x) (x + 2) #define NUM_PINS(x) (x + 2)
#define SDMMC_CLKSEL 0x09C #define SDMMC_CLKSEL 0x09C
#define SDMMC_CLKSEL64 0x0A8
#define SDMMC_CLKSEL_CCLK_SAMPLE(x) (((x) & 7) << 0) #define SDMMC_CLKSEL_CCLK_SAMPLE(x) (((x) & 7) << 0)
#define SDMMC_CLKSEL_CCLK_DRIVE(x) (((x) & 7) << 16) #define SDMMC_CLKSEL_CCLK_DRIVE(x) (((x) & 7) << 16)
#define SDMMC_CLKSEL_CCLK_DIVIDER(x) (((x) & 7) << 24) #define SDMMC_CLKSEL_CCLK_DIVIDER(x) (((x) & 7) << 24)
...@@ -65,6 +66,8 @@ enum dw_mci_exynos_type { ...@@ -65,6 +66,8 @@ enum dw_mci_exynos_type {
DW_MCI_TYPE_EXYNOS5250, DW_MCI_TYPE_EXYNOS5250,
DW_MCI_TYPE_EXYNOS5420, DW_MCI_TYPE_EXYNOS5420,
DW_MCI_TYPE_EXYNOS5420_SMU, DW_MCI_TYPE_EXYNOS5420_SMU,
DW_MCI_TYPE_EXYNOS7,
DW_MCI_TYPE_EXYNOS7_SMU,
}; };
/* Exynos implementation specific driver private data */ /* Exynos implementation specific driver private data */
...@@ -95,6 +98,12 @@ static struct dw_mci_exynos_compatible { ...@@ -95,6 +98,12 @@ static struct dw_mci_exynos_compatible {
}, { }, {
.compatible = "samsung,exynos5420-dw-mshc-smu", .compatible = "samsung,exynos5420-dw-mshc-smu",
.ctrl_type = DW_MCI_TYPE_EXYNOS5420_SMU, .ctrl_type = DW_MCI_TYPE_EXYNOS5420_SMU,
}, {
.compatible = "samsung,exynos7-dw-mshc",
.ctrl_type = DW_MCI_TYPE_EXYNOS7,
}, {
.compatible = "samsung,exynos7-dw-mshc-smu",
.ctrl_type = DW_MCI_TYPE_EXYNOS7_SMU,
}, },
}; };
...@@ -102,7 +111,8 @@ static int dw_mci_exynos_priv_init(struct dw_mci *host) ...@@ -102,7 +111,8 @@ static int dw_mci_exynos_priv_init(struct dw_mci *host)
{ {
struct dw_mci_exynos_priv_data *priv = host->priv; struct dw_mci_exynos_priv_data *priv = host->priv;
if (priv->ctrl_type == DW_MCI_TYPE_EXYNOS5420_SMU) { if (priv->ctrl_type == DW_MCI_TYPE_EXYNOS5420_SMU ||
priv->ctrl_type == DW_MCI_TYPE_EXYNOS7_SMU) {
mci_writel(host, MPSBEGIN0, 0); mci_writel(host, MPSBEGIN0, 0);
mci_writel(host, MPSEND0, DWMCI_BLOCK_NUM); mci_writel(host, MPSEND0, DWMCI_BLOCK_NUM);
mci_writel(host, MPSCTRL0, DWMCI_MPSCTRL_SECURE_WRITE_BIT | mci_writel(host, MPSCTRL0, DWMCI_MPSCTRL_SECURE_WRITE_BIT |
...@@ -153,11 +163,22 @@ static int dw_mci_exynos_resume(struct device *dev) ...@@ -153,11 +163,22 @@ static int dw_mci_exynos_resume(struct device *dev)
static int dw_mci_exynos_resume_noirq(struct device *dev) static int dw_mci_exynos_resume_noirq(struct device *dev)
{ {
struct dw_mci *host = dev_get_drvdata(dev); struct dw_mci *host = dev_get_drvdata(dev);
struct dw_mci_exynos_priv_data *priv = host->priv;
u32 clksel; u32 clksel;
clksel = mci_readl(host, CLKSEL); if (priv->ctrl_type == DW_MCI_TYPE_EXYNOS7 ||
if (clksel & SDMMC_CLKSEL_WAKEUP_INT) priv->ctrl_type == DW_MCI_TYPE_EXYNOS7_SMU)
mci_writel(host, CLKSEL, clksel); clksel = mci_readl(host, CLKSEL64);
else
clksel = mci_readl(host, CLKSEL);
if (clksel & SDMMC_CLKSEL_WAKEUP_INT) {
if (priv->ctrl_type == DW_MCI_TYPE_EXYNOS7 ||
priv->ctrl_type == DW_MCI_TYPE_EXYNOS7_SMU)
mci_writel(host, CLKSEL64, clksel);
else
mci_writel(host, CLKSEL, clksel);
}
return 0; return 0;
} }
...@@ -169,6 +190,7 @@ static int dw_mci_exynos_resume_noirq(struct device *dev) ...@@ -169,6 +190,7 @@ static int dw_mci_exynos_resume_noirq(struct device *dev)
static void dw_mci_exynos_prepare_command(struct dw_mci *host, u32 *cmdr) static void dw_mci_exynos_prepare_command(struct dw_mci *host, u32 *cmdr)
{ {
struct dw_mci_exynos_priv_data *priv = host->priv;
/* /*
* Exynos4412 and Exynos5250 extends the use of CMD register with the * Exynos4412 and Exynos5250 extends the use of CMD register with the
* use of bit 29 (which is reserved on standard MSHC controllers) for * use of bit 29 (which is reserved on standard MSHC controllers) for
...@@ -176,8 +198,14 @@ static void dw_mci_exynos_prepare_command(struct dw_mci *host, u32 *cmdr) ...@@ -176,8 +198,14 @@ static void dw_mci_exynos_prepare_command(struct dw_mci *host, u32 *cmdr)
* HOLD register should be bypassed in case there is no phase shift * HOLD register should be bypassed in case there is no phase shift
* applied on CMD/DATA that is sent to the card. * applied on CMD/DATA that is sent to the card.
*/ */
if (SDMMC_CLKSEL_GET_DRV_WD3(mci_readl(host, CLKSEL))) if (priv->ctrl_type == DW_MCI_TYPE_EXYNOS7 ||
*cmdr |= SDMMC_CMD_USE_HOLD_REG; priv->ctrl_type == DW_MCI_TYPE_EXYNOS7_SMU) {
if (SDMMC_CLKSEL_GET_DRV_WD3(mci_readl(host, CLKSEL64)))
*cmdr |= SDMMC_CMD_USE_HOLD_REG;
} else {
if (SDMMC_CLKSEL_GET_DRV_WD3(mci_readl(host, CLKSEL)))
*cmdr |= SDMMC_CMD_USE_HOLD_REG;
}
} }
static void dw_mci_exynos_set_ios(struct dw_mci *host, struct mmc_ios *ios) static void dw_mci_exynos_set_ios(struct dw_mci *host, struct mmc_ios *ios)
...@@ -188,12 +216,20 @@ static void dw_mci_exynos_set_ios(struct dw_mci *host, struct mmc_ios *ios) ...@@ -188,12 +216,20 @@ static void dw_mci_exynos_set_ios(struct dw_mci *host, struct mmc_ios *ios)
u8 div = priv->ciu_div + 1; u8 div = priv->ciu_div + 1;
if (ios->timing == MMC_TIMING_MMC_DDR52) { if (ios->timing == MMC_TIMING_MMC_DDR52) {
mci_writel(host, CLKSEL, priv->ddr_timing); if (priv->ctrl_type == DW_MCI_TYPE_EXYNOS7 ||
priv->ctrl_type == DW_MCI_TYPE_EXYNOS7_SMU)
mci_writel(host, CLKSEL64, priv->ddr_timing);
else
mci_writel(host, CLKSEL, priv->ddr_timing);
/* Should be double rate for DDR mode */ /* Should be double rate for DDR mode */
if (ios->bus_width == MMC_BUS_WIDTH_8) if (ios->bus_width == MMC_BUS_WIDTH_8)
wanted <<= 1; wanted <<= 1;
} else { } else {
mci_writel(host, CLKSEL, priv->sdr_timing); if (priv->ctrl_type == DW_MCI_TYPE_EXYNOS7 ||
priv->ctrl_type == DW_MCI_TYPE_EXYNOS7_SMU)
mci_writel(host, CLKSEL64, priv->sdr_timing);
else
mci_writel(host, CLKSEL, priv->sdr_timing);
} }
/* Don't care if wanted clock is zero */ /* Don't care if wanted clock is zero */
...@@ -265,26 +301,51 @@ static int dw_mci_exynos_parse_dt(struct dw_mci *host) ...@@ -265,26 +301,51 @@ static int dw_mci_exynos_parse_dt(struct dw_mci *host)
static inline u8 dw_mci_exynos_get_clksmpl(struct dw_mci *host) static inline u8 dw_mci_exynos_get_clksmpl(struct dw_mci *host)
{ {
return SDMMC_CLKSEL_CCLK_SAMPLE(mci_readl(host, CLKSEL)); struct dw_mci_exynos_priv_data *priv = host->priv;
if (priv->ctrl_type == DW_MCI_TYPE_EXYNOS7 ||
priv->ctrl_type == DW_MCI_TYPE_EXYNOS7_SMU)
return SDMMC_CLKSEL_CCLK_SAMPLE(mci_readl(host, CLKSEL64));
else
return SDMMC_CLKSEL_CCLK_SAMPLE(mci_readl(host, CLKSEL));
} }
static inline void dw_mci_exynos_set_clksmpl(struct dw_mci *host, u8 sample) static inline void dw_mci_exynos_set_clksmpl(struct dw_mci *host, u8 sample)
{ {
u32 clksel; u32 clksel;
clksel = mci_readl(host, CLKSEL); struct dw_mci_exynos_priv_data *priv = host->priv;
if (priv->ctrl_type == DW_MCI_TYPE_EXYNOS7 ||
priv->ctrl_type == DW_MCI_TYPE_EXYNOS7_SMU)
clksel = mci_readl(host, CLKSEL64);
else
clksel = mci_readl(host, CLKSEL);
clksel = (clksel & ~0x7) | SDMMC_CLKSEL_CCLK_SAMPLE(sample); clksel = (clksel & ~0x7) | SDMMC_CLKSEL_CCLK_SAMPLE(sample);
mci_writel(host, CLKSEL, clksel); if (priv->ctrl_type == DW_MCI_TYPE_EXYNOS7 ||
priv->ctrl_type == DW_MCI_TYPE_EXYNOS7_SMU)
mci_writel(host, CLKSEL64, clksel);
else
mci_writel(host, CLKSEL, clksel);
} }
static inline u8 dw_mci_exynos_move_next_clksmpl(struct dw_mci *host) static inline u8 dw_mci_exynos_move_next_clksmpl(struct dw_mci *host)
{ {
struct dw_mci_exynos_priv_data *priv = host->priv;
u32 clksel; u32 clksel;
u8 sample; u8 sample;
clksel = mci_readl(host, CLKSEL); if (priv->ctrl_type == DW_MCI_TYPE_EXYNOS7 ||
priv->ctrl_type == DW_MCI_TYPE_EXYNOS7_SMU)
clksel = mci_readl(host, CLKSEL64);
else
clksel = mci_readl(host, CLKSEL);
sample = (clksel + 1) & 0x7; sample = (clksel + 1) & 0x7;
clksel = (clksel & ~0x7) | sample; clksel = (clksel & ~0x7) | sample;
mci_writel(host, CLKSEL, clksel); if (priv->ctrl_type == DW_MCI_TYPE_EXYNOS7 ||
priv->ctrl_type == DW_MCI_TYPE_EXYNOS7_SMU)
mci_writel(host, CLKSEL64, clksel);
else
mci_writel(host, CLKSEL, clksel);
return sample; return sample;
} }
...@@ -411,6 +472,10 @@ static const struct of_device_id dw_mci_exynos_match[] = { ...@@ -411,6 +472,10 @@ static const struct of_device_id dw_mci_exynos_match[] = {
.data = &exynos_drv_data, }, .data = &exynos_drv_data, },
{ .compatible = "samsung,exynos5420-dw-mshc-smu", { .compatible = "samsung,exynos5420-dw-mshc-smu",
.data = &exynos_drv_data, }, .data = &exynos_drv_data, },
{ .compatible = "samsung,exynos7-dw-mshc",
.data = &exynos_drv_data, },
{ .compatible = "samsung,exynos7-dw-mshc-smu",
.data = &exynos_drv_data, },
{}, {},
}; };
MODULE_DEVICE_TABLE(of, dw_mci_exynos_match); MODULE_DEVICE_TABLE(of, dw_mci_exynos_match);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment