Re: [PATCH v5 07/12] mmc: sdhci-xenon: Add support to PHYs of Marvell Xenon SDHC.

2017-01-27 Thread Ziji Hu
Hi Ulf,

On 2017/1/26 19:29, Ulf Hansson wrote:
> [...]
> 
>> +
>> +static inline bool temp_stage_hs200_to_hs400(struct sdhci_host *host,
>> +struct sdhci_xenon_priv *priv)
>> +{
>> +   /*
>> +* Tmep stages from HS200 to HS400
>> +* from HS200 to HS in 200MHz
>> +* from 200MHz to 52MHz
>> +*/
>> +   if (((priv->timing == MMC_TIMING_MMC_HS200) &&
>> +(host->timing == MMC_TIMING_MMC_HS)) ||
>> +   ((host->timing == MMC_TIMING_MMC_HS) &&
>> +(priv->clock > host->clock)))
>> +   return true;
>> +
>> +   return false;
>> +}
>> +
>> +static inline bool temp_stage_hs400_to_h200(struct sdhci_host *host,
>> +   struct sdhci_xenon_priv *priv)
>> +{
>> +   /*
>> +* Temp stages from HS400 t0 HS200:
>> +* from 200MHz to 52MHz in HS400
>> +* from HS400 to HS DDR in 52MHz
>> +* from HS DDR to HS in 52MHz
>> +* from HS to HS200 in 52MHz
>> +*/
>> +   if (((priv->timing == MMC_TIMING_MMC_HS400) &&
>> +((host->clock == MMC_HIGH_52_MAX_DTR) ||
>> + (host->timing == MMC_TIMING_MMC_DDR52))) ||
>> +   ((priv->timing == MMC_TIMING_MMC_DDR52) &&
>> +(host->timing == MMC_TIMING_MMC_HS)) ||
>> +   ((host->timing == MMC_TIMING_MMC_HS200) &&
>> +(host->clock == MMC_HIGH_52_MAX_DTR)))
>> +   return true;
>> +
>> +   return false;
>> +}
> 
> Both the above functions seems to be really fragile to use. If
> anything changes in the core layer related to these sequences, you may
> end up getting a wrong validated expression.
> 
> Perhaps an option would be to add one or two new mmc host ops, which
> could be called during the related hs200/400 sequences that could make
> this less fragile for you? What do you think?
> 

Yes, they both look fragile to use.
In my own opinion, hs200->hs400 sequence might be safe since it has been
defined and fixed by eMMC spec. I personally think it is unlikely to be
adjusted again. Please correct me if I am wrong.
However, as you said, hs400->hs200 sequence will have issue if it is
changed in core layer, since such a sequence is not from eMMC spec.

But, in my opinion, adding additional mmc host ops might not be helpful.
The above two functions are part of Xenon PHY adjustment. Those two
functions help save some PHY setting steps in hs200->hs400/hs400->hs200
sequence. But other PHY settings are still necessary,
which should be executed each time when mmc core layer set ios.
Neither of them can be extracted out and put into another mmc host ops.

Instead, it will helps us improve the above two if we can add a flag to
indicate that eMMC is in a temporary status in hs200->hs400 or hs400->hs200
sequence.

>> +
>> +/*
>> + * If eMMC PHY Slow Mode is required in lower speed mode in SDR mode
>> + * (SDLCK < 55MHz), enable Slow Mode to bypass eMMC PHY.
>> + * SDIO slower SDR mode also requires Slow Mode.
>> + *
>> + * If Slow Mode is enabled, return true.
>> + * Otherwise, return false.
>> + */
>> +static bool emmc_phy_slow_mode(struct sdhci_host *host,
>> +  unsigned char timing)
>> +{
>> +   struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
>> +   struct sdhci_xenon_priv *priv = sdhci_pltfm_priv(pltfm_host);
>> +   struct emmc_phy_params *params = priv->phy_params;
>> +   struct xenon_emmc_phy_regs *phy_regs = priv->emmc_phy_regs;
>> +   u32 reg;
>> +
>> +   /* Skip temp stages from HS200 to HS400 */
>> +   if (temp_stage_hs200_to_hs400(host, priv))
>> +   return false;
>> +
>> +   /* Skip temp stages from HS400 t0 HS200 */
>> +   if (temp_stage_hs400_to_h200(host, priv))
>> +   return false;
>> +
>> +   reg = sdhci_readl(host, phy_regs->timing_adj);
>> +   /* Enable Slow Mode for SDIO in slower SDR mode */
>> +   if ((priv->init_card_type == MMC_TYPE_SDIO) &&
> 
> Ah, noticed that there is some specific things going on here for SDIO
> here as well. So perhaps you do need to implement the ->init_card()
> ops anyway. Which I suggested not to, for patch6. :-)
> 
> Although, there is one problem with using ->init_card(). That is
> particularly for removable cards, as mmc_rescan() doesn't invoke
> ->init_card() when it removes a card, which means, you may use old and
> thus wrong information about what kind of card in the next card
> detection attempt. So perhaps ->init_card() needs to be called from
> the core before it even figured out what kind of card it is trying to
> detect, as to allow the callbacks to reset some data.
> 
> Or perhaps you can think of another clever way!?
>

It is a good question.

Our Xenon requires to set SDIO Mode and PHY Slow Mode for SDIO card.
Both of them are configed in function emmc_phy_set(). In 

Re: [PATCH v5 07/12] mmc: sdhci-xenon: Add support to PHYs of Marvell Xenon SDHC.

2017-01-27 Thread Ziji Hu
Hi Ulf,

On 2017/1/26 19:29, Ulf Hansson wrote:
> [...]
> 
>> +
>> +static inline bool temp_stage_hs200_to_hs400(struct sdhci_host *host,
>> +struct sdhci_xenon_priv *priv)
>> +{
>> +   /*
>> +* Tmep stages from HS200 to HS400
>> +* from HS200 to HS in 200MHz
>> +* from 200MHz to 52MHz
>> +*/
>> +   if (((priv->timing == MMC_TIMING_MMC_HS200) &&
>> +(host->timing == MMC_TIMING_MMC_HS)) ||
>> +   ((host->timing == MMC_TIMING_MMC_HS) &&
>> +(priv->clock > host->clock)))
>> +   return true;
>> +
>> +   return false;
>> +}
>> +
>> +static inline bool temp_stage_hs400_to_h200(struct sdhci_host *host,
>> +   struct sdhci_xenon_priv *priv)
>> +{
>> +   /*
>> +* Temp stages from HS400 t0 HS200:
>> +* from 200MHz to 52MHz in HS400
>> +* from HS400 to HS DDR in 52MHz
>> +* from HS DDR to HS in 52MHz
>> +* from HS to HS200 in 52MHz
>> +*/
>> +   if (((priv->timing == MMC_TIMING_MMC_HS400) &&
>> +((host->clock == MMC_HIGH_52_MAX_DTR) ||
>> + (host->timing == MMC_TIMING_MMC_DDR52))) ||
>> +   ((priv->timing == MMC_TIMING_MMC_DDR52) &&
>> +(host->timing == MMC_TIMING_MMC_HS)) ||
>> +   ((host->timing == MMC_TIMING_MMC_HS200) &&
>> +(host->clock == MMC_HIGH_52_MAX_DTR)))
>> +   return true;
>> +
>> +   return false;
>> +}
> 
> Both the above functions seems to be really fragile to use. If
> anything changes in the core layer related to these sequences, you may
> end up getting a wrong validated expression.
> 
> Perhaps an option would be to add one or two new mmc host ops, which
> could be called during the related hs200/400 sequences that could make
> this less fragile for you? What do you think?
> 

Yes, they both look fragile to use.
In my own opinion, hs200->hs400 sequence might be safe since it has been
defined and fixed by eMMC spec. I personally think it is unlikely to be
adjusted again. Please correct me if I am wrong.
However, as you said, hs400->hs200 sequence will have issue if it is
changed in core layer, since such a sequence is not from eMMC spec.

But, in my opinion, adding additional mmc host ops might not be helpful.
The above two functions are part of Xenon PHY adjustment. Those two
functions help save some PHY setting steps in hs200->hs400/hs400->hs200
sequence. But other PHY settings are still necessary,
which should be executed each time when mmc core layer set ios.
Neither of them can be extracted out and put into another mmc host ops.

Instead, it will helps us improve the above two if we can add a flag to
indicate that eMMC is in a temporary status in hs200->hs400 or hs400->hs200
sequence.

>> +
>> +/*
>> + * If eMMC PHY Slow Mode is required in lower speed mode in SDR mode
>> + * (SDLCK < 55MHz), enable Slow Mode to bypass eMMC PHY.
>> + * SDIO slower SDR mode also requires Slow Mode.
>> + *
>> + * If Slow Mode is enabled, return true.
>> + * Otherwise, return false.
>> + */
>> +static bool emmc_phy_slow_mode(struct sdhci_host *host,
>> +  unsigned char timing)
>> +{
>> +   struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
>> +   struct sdhci_xenon_priv *priv = sdhci_pltfm_priv(pltfm_host);
>> +   struct emmc_phy_params *params = priv->phy_params;
>> +   struct xenon_emmc_phy_regs *phy_regs = priv->emmc_phy_regs;
>> +   u32 reg;
>> +
>> +   /* Skip temp stages from HS200 to HS400 */
>> +   if (temp_stage_hs200_to_hs400(host, priv))
>> +   return false;
>> +
>> +   /* Skip temp stages from HS400 t0 HS200 */
>> +   if (temp_stage_hs400_to_h200(host, priv))
>> +   return false;
>> +
>> +   reg = sdhci_readl(host, phy_regs->timing_adj);
>> +   /* Enable Slow Mode for SDIO in slower SDR mode */
>> +   if ((priv->init_card_type == MMC_TYPE_SDIO) &&
> 
> Ah, noticed that there is some specific things going on here for SDIO
> here as well. So perhaps you do need to implement the ->init_card()
> ops anyway. Which I suggested not to, for patch6. :-)
> 
> Although, there is one problem with using ->init_card(). That is
> particularly for removable cards, as mmc_rescan() doesn't invoke
> ->init_card() when it removes a card, which means, you may use old and
> thus wrong information about what kind of card in the next card
> detection attempt. So perhaps ->init_card() needs to be called from
> the core before it even figured out what kind of card it is trying to
> detect, as to allow the callbacks to reset some data.
> 
> Or perhaps you can think of another clever way!?
>

It is a good question.

Our Xenon requires to set SDIO Mode and PHY Slow Mode for SDIO card.
Both of them are configed in function emmc_phy_set(). In 

Re: [PATCH v5 07/12] mmc: sdhci-xenon: Add support to PHYs of Marvell Xenon SDHC.

2017-01-26 Thread Ulf Hansson
[...]

> +
> +static inline bool temp_stage_hs200_to_hs400(struct sdhci_host *host,
> +struct sdhci_xenon_priv *priv)
> +{
> +   /*
> +* Tmep stages from HS200 to HS400
> +* from HS200 to HS in 200MHz
> +* from 200MHz to 52MHz
> +*/
> +   if (((priv->timing == MMC_TIMING_MMC_HS200) &&
> +(host->timing == MMC_TIMING_MMC_HS)) ||
> +   ((host->timing == MMC_TIMING_MMC_HS) &&
> +(priv->clock > host->clock)))
> +   return true;
> +
> +   return false;
> +}
> +
> +static inline bool temp_stage_hs400_to_h200(struct sdhci_host *host,
> +   struct sdhci_xenon_priv *priv)
> +{
> +   /*
> +* Temp stages from HS400 t0 HS200:
> +* from 200MHz to 52MHz in HS400
> +* from HS400 to HS DDR in 52MHz
> +* from HS DDR to HS in 52MHz
> +* from HS to HS200 in 52MHz
> +*/
> +   if (((priv->timing == MMC_TIMING_MMC_HS400) &&
> +((host->clock == MMC_HIGH_52_MAX_DTR) ||
> + (host->timing == MMC_TIMING_MMC_DDR52))) ||
> +   ((priv->timing == MMC_TIMING_MMC_DDR52) &&
> +(host->timing == MMC_TIMING_MMC_HS)) ||
> +   ((host->timing == MMC_TIMING_MMC_HS200) &&
> +(host->clock == MMC_HIGH_52_MAX_DTR)))
> +   return true;
> +
> +   return false;
> +}

Both the above functions seems to be really fragile to use. If
anything changes in the core layer related to these sequences, you may
end up getting a wrong validated expression.

Perhaps an option would be to add one or two new mmc host ops, which
could be called during the related hs200/400 sequences that could make
this less fragile for you? What do you think?

> +
> +/*
> + * If eMMC PHY Slow Mode is required in lower speed mode in SDR mode
> + * (SDLCK < 55MHz), enable Slow Mode to bypass eMMC PHY.
> + * SDIO slower SDR mode also requires Slow Mode.
> + *
> + * If Slow Mode is enabled, return true.
> + * Otherwise, return false.
> + */
> +static bool emmc_phy_slow_mode(struct sdhci_host *host,
> +  unsigned char timing)
> +{
> +   struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
> +   struct sdhci_xenon_priv *priv = sdhci_pltfm_priv(pltfm_host);
> +   struct emmc_phy_params *params = priv->phy_params;
> +   struct xenon_emmc_phy_regs *phy_regs = priv->emmc_phy_regs;
> +   u32 reg;
> +
> +   /* Skip temp stages from HS200 to HS400 */
> +   if (temp_stage_hs200_to_hs400(host, priv))
> +   return false;
> +
> +   /* Skip temp stages from HS400 t0 HS200 */
> +   if (temp_stage_hs400_to_h200(host, priv))
> +   return false;
> +
> +   reg = sdhci_readl(host, phy_regs->timing_adj);
> +   /* Enable Slow Mode for SDIO in slower SDR mode */
> +   if ((priv->init_card_type == MMC_TYPE_SDIO) &&

Ah, noticed that there is some specific things going on here for SDIO
here as well. So perhaps you do need to implement the ->init_card()
ops anyway. Which I suggested not to, for patch6. :-)

Although, there is one problem with using ->init_card(). That is
particularly for removable cards, as mmc_rescan() doesn't invoke
->init_card() when it removes a card, which means, you may use old and
thus wrong information about what kind of card in the next card
detection attempt. So perhaps ->init_card() needs to be called from
the core before it even figured out what kind of card it is trying to
detect, as to allow the callbacks to reset some data.

Or perhaps you can think of another clever way!?


> +   ((timing == MMC_TIMING_UHS_SDR25) ||
> +(timing == MMC_TIMING_UHS_SDR12) ||
> +(timing == MMC_TIMING_SD_HS) ||
> +(timing == MMC_TIMING_LEGACY))) {
> +   reg |= XENON_TIMING_ADJUST_SLOW_MODE;
> +   sdhci_writel(host, reg, phy_regs->timing_adj);
> +   return true;
> +   }
> +
> +   /* Check if Slow Mode is required in lower speed mode in SDR mode */
> +   if (((timing == MMC_TIMING_UHS_SDR50) ||
> +(timing == MMC_TIMING_UHS_SDR25) ||
> +(timing == MMC_TIMING_UHS_SDR12) ||
> +(timing == MMC_TIMING_SD_HS) ||
> +(timing == MMC_TIMING_MMC_HS) ||
> +(timing == MMC_TIMING_LEGACY)) && params->slow_mode) {
> +   reg |= XENON_TIMING_ADJUST_SLOW_MODE;
> +   sdhci_writel(host, reg, phy_regs->timing_adj);
> +   return true;
> +   }
> +
> +   reg &= ~XENON_TIMING_ADJUST_SLOW_MODE;
> +   sdhci_writel(host, reg, phy_regs->timing_adj);
> +   return false;
> +}
> +

[...]

Kind regards
Uffe


Re: [PATCH v5 07/12] mmc: sdhci-xenon: Add support to PHYs of Marvell Xenon SDHC.

2017-01-26 Thread Ulf Hansson
[...]

> +
> +static inline bool temp_stage_hs200_to_hs400(struct sdhci_host *host,
> +struct sdhci_xenon_priv *priv)
> +{
> +   /*
> +* Tmep stages from HS200 to HS400
> +* from HS200 to HS in 200MHz
> +* from 200MHz to 52MHz
> +*/
> +   if (((priv->timing == MMC_TIMING_MMC_HS200) &&
> +(host->timing == MMC_TIMING_MMC_HS)) ||
> +   ((host->timing == MMC_TIMING_MMC_HS) &&
> +(priv->clock > host->clock)))
> +   return true;
> +
> +   return false;
> +}
> +
> +static inline bool temp_stage_hs400_to_h200(struct sdhci_host *host,
> +   struct sdhci_xenon_priv *priv)
> +{
> +   /*
> +* Temp stages from HS400 t0 HS200:
> +* from 200MHz to 52MHz in HS400
> +* from HS400 to HS DDR in 52MHz
> +* from HS DDR to HS in 52MHz
> +* from HS to HS200 in 52MHz
> +*/
> +   if (((priv->timing == MMC_TIMING_MMC_HS400) &&
> +((host->clock == MMC_HIGH_52_MAX_DTR) ||
> + (host->timing == MMC_TIMING_MMC_DDR52))) ||
> +   ((priv->timing == MMC_TIMING_MMC_DDR52) &&
> +(host->timing == MMC_TIMING_MMC_HS)) ||
> +   ((host->timing == MMC_TIMING_MMC_HS200) &&
> +(host->clock == MMC_HIGH_52_MAX_DTR)))
> +   return true;
> +
> +   return false;
> +}

Both the above functions seems to be really fragile to use. If
anything changes in the core layer related to these sequences, you may
end up getting a wrong validated expression.

Perhaps an option would be to add one or two new mmc host ops, which
could be called during the related hs200/400 sequences that could make
this less fragile for you? What do you think?

> +
> +/*
> + * If eMMC PHY Slow Mode is required in lower speed mode in SDR mode
> + * (SDLCK < 55MHz), enable Slow Mode to bypass eMMC PHY.
> + * SDIO slower SDR mode also requires Slow Mode.
> + *
> + * If Slow Mode is enabled, return true.
> + * Otherwise, return false.
> + */
> +static bool emmc_phy_slow_mode(struct sdhci_host *host,
> +  unsigned char timing)
> +{
> +   struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
> +   struct sdhci_xenon_priv *priv = sdhci_pltfm_priv(pltfm_host);
> +   struct emmc_phy_params *params = priv->phy_params;
> +   struct xenon_emmc_phy_regs *phy_regs = priv->emmc_phy_regs;
> +   u32 reg;
> +
> +   /* Skip temp stages from HS200 to HS400 */
> +   if (temp_stage_hs200_to_hs400(host, priv))
> +   return false;
> +
> +   /* Skip temp stages from HS400 t0 HS200 */
> +   if (temp_stage_hs400_to_h200(host, priv))
> +   return false;
> +
> +   reg = sdhci_readl(host, phy_regs->timing_adj);
> +   /* Enable Slow Mode for SDIO in slower SDR mode */
> +   if ((priv->init_card_type == MMC_TYPE_SDIO) &&

Ah, noticed that there is some specific things going on here for SDIO
here as well. So perhaps you do need to implement the ->init_card()
ops anyway. Which I suggested not to, for patch6. :-)

Although, there is one problem with using ->init_card(). That is
particularly for removable cards, as mmc_rescan() doesn't invoke
->init_card() when it removes a card, which means, you may use old and
thus wrong information about what kind of card in the next card
detection attempt. So perhaps ->init_card() needs to be called from
the core before it even figured out what kind of card it is trying to
detect, as to allow the callbacks to reset some data.

Or perhaps you can think of another clever way!?


> +   ((timing == MMC_TIMING_UHS_SDR25) ||
> +(timing == MMC_TIMING_UHS_SDR12) ||
> +(timing == MMC_TIMING_SD_HS) ||
> +(timing == MMC_TIMING_LEGACY))) {
> +   reg |= XENON_TIMING_ADJUST_SLOW_MODE;
> +   sdhci_writel(host, reg, phy_regs->timing_adj);
> +   return true;
> +   }
> +
> +   /* Check if Slow Mode is required in lower speed mode in SDR mode */
> +   if (((timing == MMC_TIMING_UHS_SDR50) ||
> +(timing == MMC_TIMING_UHS_SDR25) ||
> +(timing == MMC_TIMING_UHS_SDR12) ||
> +(timing == MMC_TIMING_SD_HS) ||
> +(timing == MMC_TIMING_MMC_HS) ||
> +(timing == MMC_TIMING_LEGACY)) && params->slow_mode) {
> +   reg |= XENON_TIMING_ADJUST_SLOW_MODE;
> +   sdhci_writel(host, reg, phy_regs->timing_adj);
> +   return true;
> +   }
> +
> +   reg &= ~XENON_TIMING_ADJUST_SLOW_MODE;
> +   sdhci_writel(host, reg, phy_regs->timing_adj);
> +   return false;
> +}
> +

[...]

Kind regards
Uffe


[PATCH v5 07/12] mmc: sdhci-xenon: Add support to PHYs of Marvell Xenon SDHC.

2017-01-11 Thread Gregory CLEMENT
From: Hu Ziji 

Marvell Xenon eMMC/SD/SDIO Host Controller contains PHY.
Multiple types of PHYs are supported.

Add support to multiple types of PHYs init and configuration.
Add register definitions of PHYs.

Xenon PHY cannot fit in kernel common PHY framework.
Xenon SDHC PHY register is a part of Xenon SDHC register set.
Besides, MMC initialization has to call several PHY functions to
complete timing setting.
Those PHY setting functions have to access SDHC registers and know
current MMC setting, such as bus width, clock frequency and
speed mode.
As a result, implement Xenon PHY in MMC host directory.

Signed-off-by: Hu Ziji 
Tested-by: Russell King 
Signed-off-by: Gregory CLEMENT 
---
 drivers/mmc/host/Makefile  |   2 +-
 drivers/mmc/host/sdhci-xenon-phy.c | 790 ++-
 drivers/mmc/host/sdhci-xenon.c |   3 +-
 drivers/mmc/host/sdhci-xenon.h |  37 +-
 4 files changed, 830 insertions(+), 2 deletions(-)
 create mode 100644 drivers/mmc/host/sdhci-xenon-phy.c

diff --git a/drivers/mmc/host/Makefile b/drivers/mmc/host/Makefile
index b0a2ab4b256e..893b48db5513 100644
--- a/drivers/mmc/host/Makefile
+++ b/drivers/mmc/host/Makefile
@@ -84,4 +84,4 @@ ifeq ($(CONFIG_CB710_DEBUG),y)
 endif
 
 obj-$(CONFIG_MMC_SDHCI_XENON)  += sdhci-xenon-driver.o
-sdhci-xenon-driver-y   += sdhci-xenon.o
+sdhci-xenon-driver-y   += sdhci-xenon.o sdhci-xenon-phy.o
diff --git a/drivers/mmc/host/sdhci-xenon-phy.c 
b/drivers/mmc/host/sdhci-xenon-phy.c
new file mode 100644
index ..f410712d24ea
--- /dev/null
+++ b/drivers/mmc/host/sdhci-xenon-phy.c
@@ -0,0 +1,790 @@
+/*
+ * PHY support for Xenon SDHC
+ *
+ * Copyright (C) 2016 Marvell, All Rights Reserved.
+ *
+ * Author: Hu Ziji 
+ * Date:   2016-8-24
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation version 2.
+ */
+
+#include 
+#include 
+#include 
+
+#include "sdhci-pltfm.h"
+#include "sdhci-xenon.h"
+
+/* Register base for eMMC PHY 5.0 Version */
+#define XENON_EMMC_5_0_PHY_REG_BASE0x0160
+/* Register base for eMMC PHY 5.1 Version */
+#define XENON_EMMC_PHY_REG_BASE0x0170
+
+#define XENON_EMMC_PHY_TIMING_ADJUST   XENON_EMMC_PHY_REG_BASE
+#define XENON_EMMC_5_0_PHY_TIMING_ADJUST   XENON_EMMC_5_0_PHY_REG_BASE
+#define XENON_TIMING_ADJUST_SLOW_MODE  BIT(29)
+#define XENON_TIMING_ADJUST_SDIO_MODE  BIT(28)
+#define XENON_OUTPUT_QSN_PHASE_SELECT  BIT(17)
+#define XENON_SAMPL_INV_QSP_PHASE_SELECT   BIT(18)
+#define XENON_SAMPL_INV_QSP_PHASE_SELECT_SHIFT 18
+#define XENON_PHY_INITIALIZAIONBIT(31)
+#define XENON_WAIT_CYCLE_BEFORE_USING_MASK 0xF
+#define XENON_WAIT_CYCLE_BEFORE_USING_SHIFT12
+#define XENON_FC_SYNC_EN_DURATION_MASK 0xF
+#define XENON_FC_SYNC_EN_DURATION_SHIFT8
+#define XENON_FC_SYNC_RST_EN_DURATION_MASK 0xF
+#define XENON_FC_SYNC_RST_EN_DURATION_SHIFT4
+#define XENON_FC_SYNC_RST_DURATION_MASK0xF
+#define XENON_FC_SYNC_RST_DURATION_SHIFT   0
+
+#define XENON_EMMC_PHY_FUNC_CONTROL(XENON_EMMC_PHY_REG_BASE + 0x4)
+#define XENON_EMMC_5_0_PHY_FUNC_CONTROL\
+   (XENON_EMMC_5_0_PHY_REG_BASE + 0x4)
+#define XENON_ASYNC_DDRMODE_MASK   BIT(23)
+#define XENON_ASYNC_DDRMODE_SHIFT  23
+#define XENON_CMD_DDR_MODE BIT(16)
+#define XENON_DQ_DDR_MODE_SHIFT8
+#define XENON_DQ_DDR_MODE_MASK 0xFF
+#define XENON_DQ_ASYNC_MODEBIT(4)
+
+#define XENON_EMMC_PHY_PAD_CONTROL (XENON_EMMC_PHY_REG_BASE + 0x8)
+#define XENON_EMMC_5_0_PHY_PAD_CONTROL \
+   (XENON_EMMC_5_0_PHY_REG_BASE + 0x8)
+#define XENON_REC_EN_SHIFT 24
+#define XENON_REC_EN_MASK  0xF
+#define XENON_FC_DQ_RECEN  BIT(24)
+#define XENON_FC_CMD_RECEN BIT(25)
+#define XENON_FC_QSP_RECEN BIT(26)
+#define XENON_FC_QSN_RECEN BIT(27)
+#define XENON_OEN_QSN  BIT(28)
+#define XENON_AUTO_RECEN_CTRL  BIT(30)
+#define XENON_FC_ALL_CMOS_RECEIVER 0xF000
+
+#define XENON_EMMC5_FC_QSP_PD  BIT(18)
+#define XENON_EMMC5_FC_QSP_PU  BIT(22)
+#define XENON_EMMC5_FC_CMD_PD  BIT(17)
+#define XENON_EMMC5_FC_CMD_PU  BIT(21)
+#define XENON_EMMC5_FC_DQ_PD   BIT(16)
+#define XENON_EMMC5_FC_DQ_PU   BIT(20)
+
+#define XENON_EMMC_PHY_PAD_CONTROL1(XENON_EMMC_PHY_REG_BASE + 0xC)
+#define XENON_EMMC5_1_FC_QSP_PDBIT(9)
+#define 

[PATCH v5 07/12] mmc: sdhci-xenon: Add support to PHYs of Marvell Xenon SDHC.

2017-01-11 Thread Gregory CLEMENT
From: Hu Ziji 

Marvell Xenon eMMC/SD/SDIO Host Controller contains PHY.
Multiple types of PHYs are supported.

Add support to multiple types of PHYs init and configuration.
Add register definitions of PHYs.

Xenon PHY cannot fit in kernel common PHY framework.
Xenon SDHC PHY register is a part of Xenon SDHC register set.
Besides, MMC initialization has to call several PHY functions to
complete timing setting.
Those PHY setting functions have to access SDHC registers and know
current MMC setting, such as bus width, clock frequency and
speed mode.
As a result, implement Xenon PHY in MMC host directory.

Signed-off-by: Hu Ziji 
Tested-by: Russell King 
Signed-off-by: Gregory CLEMENT 
---
 drivers/mmc/host/Makefile  |   2 +-
 drivers/mmc/host/sdhci-xenon-phy.c | 790 ++-
 drivers/mmc/host/sdhci-xenon.c |   3 +-
 drivers/mmc/host/sdhci-xenon.h |  37 +-
 4 files changed, 830 insertions(+), 2 deletions(-)
 create mode 100644 drivers/mmc/host/sdhci-xenon-phy.c

diff --git a/drivers/mmc/host/Makefile b/drivers/mmc/host/Makefile
index b0a2ab4b256e..893b48db5513 100644
--- a/drivers/mmc/host/Makefile
+++ b/drivers/mmc/host/Makefile
@@ -84,4 +84,4 @@ ifeq ($(CONFIG_CB710_DEBUG),y)
 endif
 
 obj-$(CONFIG_MMC_SDHCI_XENON)  += sdhci-xenon-driver.o
-sdhci-xenon-driver-y   += sdhci-xenon.o
+sdhci-xenon-driver-y   += sdhci-xenon.o sdhci-xenon-phy.o
diff --git a/drivers/mmc/host/sdhci-xenon-phy.c 
b/drivers/mmc/host/sdhci-xenon-phy.c
new file mode 100644
index ..f410712d24ea
--- /dev/null
+++ b/drivers/mmc/host/sdhci-xenon-phy.c
@@ -0,0 +1,790 @@
+/*
+ * PHY support for Xenon SDHC
+ *
+ * Copyright (C) 2016 Marvell, All Rights Reserved.
+ *
+ * Author: Hu Ziji 
+ * Date:   2016-8-24
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation version 2.
+ */
+
+#include 
+#include 
+#include 
+
+#include "sdhci-pltfm.h"
+#include "sdhci-xenon.h"
+
+/* Register base for eMMC PHY 5.0 Version */
+#define XENON_EMMC_5_0_PHY_REG_BASE0x0160
+/* Register base for eMMC PHY 5.1 Version */
+#define XENON_EMMC_PHY_REG_BASE0x0170
+
+#define XENON_EMMC_PHY_TIMING_ADJUST   XENON_EMMC_PHY_REG_BASE
+#define XENON_EMMC_5_0_PHY_TIMING_ADJUST   XENON_EMMC_5_0_PHY_REG_BASE
+#define XENON_TIMING_ADJUST_SLOW_MODE  BIT(29)
+#define XENON_TIMING_ADJUST_SDIO_MODE  BIT(28)
+#define XENON_OUTPUT_QSN_PHASE_SELECT  BIT(17)
+#define XENON_SAMPL_INV_QSP_PHASE_SELECT   BIT(18)
+#define XENON_SAMPL_INV_QSP_PHASE_SELECT_SHIFT 18
+#define XENON_PHY_INITIALIZAIONBIT(31)
+#define XENON_WAIT_CYCLE_BEFORE_USING_MASK 0xF
+#define XENON_WAIT_CYCLE_BEFORE_USING_SHIFT12
+#define XENON_FC_SYNC_EN_DURATION_MASK 0xF
+#define XENON_FC_SYNC_EN_DURATION_SHIFT8
+#define XENON_FC_SYNC_RST_EN_DURATION_MASK 0xF
+#define XENON_FC_SYNC_RST_EN_DURATION_SHIFT4
+#define XENON_FC_SYNC_RST_DURATION_MASK0xF
+#define XENON_FC_SYNC_RST_DURATION_SHIFT   0
+
+#define XENON_EMMC_PHY_FUNC_CONTROL(XENON_EMMC_PHY_REG_BASE + 0x4)
+#define XENON_EMMC_5_0_PHY_FUNC_CONTROL\
+   (XENON_EMMC_5_0_PHY_REG_BASE + 0x4)
+#define XENON_ASYNC_DDRMODE_MASK   BIT(23)
+#define XENON_ASYNC_DDRMODE_SHIFT  23
+#define XENON_CMD_DDR_MODE BIT(16)
+#define XENON_DQ_DDR_MODE_SHIFT8
+#define XENON_DQ_DDR_MODE_MASK 0xFF
+#define XENON_DQ_ASYNC_MODEBIT(4)
+
+#define XENON_EMMC_PHY_PAD_CONTROL (XENON_EMMC_PHY_REG_BASE + 0x8)
+#define XENON_EMMC_5_0_PHY_PAD_CONTROL \
+   (XENON_EMMC_5_0_PHY_REG_BASE + 0x8)
+#define XENON_REC_EN_SHIFT 24
+#define XENON_REC_EN_MASK  0xF
+#define XENON_FC_DQ_RECEN  BIT(24)
+#define XENON_FC_CMD_RECEN BIT(25)
+#define XENON_FC_QSP_RECEN BIT(26)
+#define XENON_FC_QSN_RECEN BIT(27)
+#define XENON_OEN_QSN  BIT(28)
+#define XENON_AUTO_RECEN_CTRL  BIT(30)
+#define XENON_FC_ALL_CMOS_RECEIVER 0xF000
+
+#define XENON_EMMC5_FC_QSP_PD  BIT(18)
+#define XENON_EMMC5_FC_QSP_PU  BIT(22)
+#define XENON_EMMC5_FC_CMD_PD  BIT(17)
+#define XENON_EMMC5_FC_CMD_PU  BIT(21)
+#define XENON_EMMC5_FC_DQ_PD   BIT(16)
+#define XENON_EMMC5_FC_DQ_PU   BIT(20)
+
+#define XENON_EMMC_PHY_PAD_CONTROL1(XENON_EMMC_PHY_REG_BASE + 0xC)
+#define XENON_EMMC5_1_FC_QSP_PDBIT(9)
+#define XENON_EMMC5_1_FC_QSP_PUBIT(25)
+#define XENON_EMMC5_1_FC_CMD_PDBIT(8)
+#define