* Re: [RFC 1/2] sdhci: Add device tree property sd-broken-highspeed
From: Julia Cartwright @ 2016-10-05 21:22 UTC (permalink / raw)
To: Rob Herring
Cc: Ulf Hansson, Zach Brown, Adrian Hunter, Mark Rutland, linux-mmc,
devicetree-u79uwXL29TY76Z2rM5mHXA@public.gmane.org,
linux-kernel-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
In-Reply-To: <CAL_JsqLjtn2-FaO_h9JWOsZqttQ35yV6KOnQz2Z-cMr=0GHJCg-JsoAwUIsXosN+BqQ9rBEUg@public.gmane.org>
On Wed, Oct 05, 2016 at 03:03:44PM -0500, Rob Herring wrote:
> On Wed, Oct 5, 2016 at 1:33 PM, Ulf Hansson <ulf.hansson-QSEj5FYQhm4dnm+yROfE0A@public.gmane.org> wrote:
> > On 23 September 2016 at 22:01, Zach Brown <zach.brown-acOepvfBmUk@public.gmane.org> wrote:
> >> Certain board configurations can make highspeed malfunction due to
> >> timing issues. In these cases a way is needed to force the controller
> >> and card into standard speed even if they otherwise appear to be capable
> >> of highspeed.
> >>
> >> The sd-broken-highspeed property will let the sdhci driver know that
> >> highspeed will not work.
> >>
> >> Signed-off-by: Zach Brown <zach.brown-acOepvfBmUk@public.gmane.org>
> >> ---
> >> Documentation/devicetree/bindings/mmc/mmc.txt | 2 ++
> >> 1 file changed, 2 insertions(+)
> >>
> >> diff --git a/Documentation/devicetree/bindings/mmc/mmc.txt b/Documentation/devicetree/bindings/mmc/mmc.txt
> >> index 8a37782..59332ea 100644
> >> --- a/Documentation/devicetree/bindings/mmc/mmc.txt
> >> +++ b/Documentation/devicetree/bindings/mmc/mmc.txt
> >> @@ -52,6 +52,8 @@ Optional properties:
> >> - no-sdio: controller is limited to send sdio cmd during initialization
> >> - no-sd: controller is limited to send sd cmd during initialization
> >> - no-mmc: controller is limited to send mmc cmd during initialization
> >> +- sd-broken-highspeed: Highspeed is broken, even if the controller and card
> >> + themselves claim they support highspeed.
> >
> > Regarding a broken card, that is managed via the card quirks and not in DT.
> >
> > If this is about a controller limitation, we already have the option
> > to describe what it supports, so we don't need an option to tell what
> > it *not* supports.
> >
> > For example "cap-sd-highspeed" tells whether the controller supports
> > SD high-speed, please use that instead.
>
> If a controller has a capability register and it lies (perhaps the
> board has limitations that the SoC does not), then you may need to
> disable a feature.
That's precisely the case here. This is a board-level problem, not a
card or controller problem. As Zach mentioned in the cover letter, the
trace length between controller and card on some of our boards is too
long to meet high-speed timings, even though both card and controller
advertise it.
Thanks,
Julia
--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
^ permalink raw reply
* Re: Enabling MMC BKOPs in kernel based on host caps
From: Alex Lemberg @ 2016-10-05 22:09 UTC (permalink / raw)
To: Ravikumar, Shawn Lin, Ravikumar Kattekola,
linux-mmc@vger.kernel.org
In-Reply-To: <dfcdad0d-0780-0641-cb0a-46557334fa16@ti.com>
Hi Ravikumar,
The reason for enabling Auto/Manual BKOPS is understandable.
Personally I don’t think that user space should play with a storage device
BKOPS settings, it should be a matter of storage device and a driver decision.
But as Shown mentioned, there is a discussion on this topic.
Also, recently I have submitted a patch for letting device more time
to complete its BKOPS on runtime Suspend:
http://www.spinics.net/lists/linux-mmc/msg38952.html
Thanks,
Alex
On 10/5/16, 1:55 PM, "Ravikumar" <a0131654@ti.com> wrote:
>Hi Shawn,
>
>
>On Wednesday 05 October 2016 03:07 PM, Shawn Lin wrote:
>> Hi Ravikumar,
>>
>> + Alex,
>>
>> 在 2016/10/3 18:43, Ravikumar Kattekola 写道:
>>> Hi all,
>>> I’ve seen an eMMC failure due to pending background operations on
>>> a certain OMAP device since bkops enable bit was not set.
>>> Further investigation showed me that someone already posted patch to
>>> enable Background operations in kernel based on a host capability
>>> check (Caps2 & BK_OPS_EN)
>>> but was turned down quoting that it should be enabled from user space
>>> using mmc-utils.
>>>
>>> Enabling this would add one additional check for exception event in
>>> the response R1 or R1B (only on hosts that explicitly set BK_OPS_EN
>>> in caps2).
>>> But not enabling this could lead to a system failure especially when
>>> the Filesystem is on eMMC and the card stops responding due to
>>> pending critical bkops.
>>>
>>> I would like to ask for expert opinion on ‘why is it a bad idea to
>>> enable bkops in kernel?’
>>
>> Some discussion about the similar topic could be found here:
>> https://patchwork.kernel.org/patch/9157121/
>>
>>
>>> It’s a one time programmable bit but if it helps in keeping system
>>> functional why not do it?
>>
>> Actually BKOPS_EN is not OTP bit.. Quoted from Ulf "I don't have any
>> issue to allow all non-OTP registers bits to be written." So I guess
>> you could do this, although it needs more discussion there.
>In the spec for 4.5, jesd84_B45 it does say not whether the ENABLE bit
>is OTP.
>But in 5.1 spec, jesd84-b51, it says MANUAL_EN is R/W which means OTP
>and readable.
>As I read form mmc-utils -help
>" mmc bkops enable <device>
> Enable the eMMC BKOPS feature on <device>.
> NOTE! This is a one-time programmable (unreversible)
>change "
>>
>> But it's persistent EXT_CSD register and we get used to control it from
>> userspace, which is the policy we have been sticking to when writing to
>> persistent EXT_CSD registers. I guess that is nothing about "right and
>> wrong", just a rule for us in case someone wants to set the persistent
>> bit in kernel but setting other persistent bits from user-space, which
>> is prone to mess up the mmc core. Or, someone will sent mail to the list
>> asking "why is it a good idea to enable bkops in kernel" ? :)
>So there's no functional problem/reason that stops us from enabling
>BKOPS (Manual) in
>kernel except for consistency with other persistent registers.
>Since not enabling BKOPS could lead to a functional failure /
>non-responsive system
>at a later point of time I guess this could be exempted.
>what do you think?
>
>As user I would choose functional safety and reliability over performance.
>Hence it would make sense to have the bkops (at least manual) be enabled
>by default,
>especially in Automotive applications.
>
>>
>>> I haven’t measured the performance impact but I don’t see a reason
>>> for major drop because the frequency of critical bkops events would
>>> be less.
>>>
>>> Regards,
>>> RK
>>> --
>>> To unsubscribe from this list: send the line "unsubscribe linux-mmc" in
>>> the body of a message to majordomo@vger.kernel.org
>>> More majordomo info at http://vger.kernel.org/majordomo-info.html
>>>
>>
>>
>Thanks for your response.
>
>Regards,
>RK
>
^ permalink raw reply
* Re: [RFC 1/2] sdhci: Add device tree property sd-broken-highspeed
From: Shawn Lin @ 2016-10-06 1:34 UTC (permalink / raw)
To: Julia Cartwright, Rob Herring
Cc: shawn.lin, Ulf Hansson, Zach Brown, Adrian Hunter, Mark Rutland,
linux-mmc, devicetree@vger.kernel.org,
linux-kernel@vger.kernel.org
In-Reply-To: <20161005212223.GK10625@jcartwri.amer.corp.natinst.com>
On 2016/10/6 5:22, Julia Cartwright wrote:
> On Wed, Oct 05, 2016 at 03:03:44PM -0500, Rob Herring wrote:
>> On Wed, Oct 5, 2016 at 1:33 PM, Ulf Hansson <ulf.hansson@linaro.org> wrote:
>>> On 23 September 2016 at 22:01, Zach Brown <zach.brown@ni.com> wrote:
>>>> Certain board configurations can make highspeed malfunction due to
>>>> timing issues. In these cases a way is needed to force the controller
>>>> and card into standard speed even if they otherwise appear to be capable
>>>> of highspeed.
>>>>
>>>> The sd-broken-highspeed property will let the sdhci driver know that
>>>> highspeed will not work.
>>>>
>>>> Signed-off-by: Zach Brown <zach.brown@ni.com>
>>>> ---
>>>> Documentation/devicetree/bindings/mmc/mmc.txt | 2 ++
>>>> 1 file changed, 2 insertions(+)
>>>>
>>>> diff --git a/Documentation/devicetree/bindings/mmc/mmc.txt b/Documentation/devicetree/bindings/mmc/mmc.txt
>>>> index 8a37782..59332ea 100644
>>>> --- a/Documentation/devicetree/bindings/mmc/mmc.txt
>>>> +++ b/Documentation/devicetree/bindings/mmc/mmc.txt
>>>> @@ -52,6 +52,8 @@ Optional properties:
>>>> - no-sdio: controller is limited to send sdio cmd during initialization
>>>> - no-sd: controller is limited to send sd cmd during initialization
>>>> - no-mmc: controller is limited to send mmc cmd during initialization
>>>> +- sd-broken-highspeed: Highspeed is broken, even if the controller and card
>>>> + themselves claim they support highspeed.
>>>
>>> Regarding a broken card, that is managed via the card quirks and not in DT.
>>>
>>> If this is about a controller limitation, we already have the option
>>> to describe what it supports, so we don't need an option to tell what
>>> it *not* supports.
>>>
>>> For example "cap-sd-highspeed" tells whether the controller supports
>>> SD high-speed, please use that instead.
>>
>> If a controller has a capability register and it lies (perhaps the
>> board has limitations that the SoC does not), then you may need to
>> disable a feature.
>
> That's precisely the case here. This is a board-level problem, not a
> card or controller problem. As Zach mentioned in the cover letter, the
> trace length between controller and card on some of our boards is too
> long to meet high-speed timings, even though both card and controller
> advertise it.
IIRC, I saw the same problem while using sdhci to bring up a
industrial board for vehicle. The trace length is so long that
I have to limit the max-frequency to make it works properly when
running at hishspeed.
So could you try to limit the max-frequency in your DT to see
if it could work for you? I guess it should work as once reducing
the frequency, the timing per cycle will be large enough to meet
the spec. At least that helped me solve my problem.
For further consideration, I deployed a mechanism called "tuning for
non UHS or non-hs200/400" for my donwstream tree at that time. The basic
concept is to ask devices to send ext_csd(or send status for SD case)
*repeatedly*. Some hosts, i.e. sdhci-of-arasan or dw_mmc-rockchip, can
manually set rx delay via some clock unit registers to capture the
working sample window and select the middle point of the longest good
phase region. The same for retune. But it is a little complicated, and
could only be applicable to the hosts who could adjust the rx delay
manually when claiming the caps of MMC_CAPS_CAN_TUNING_FOR_HS, whatever
the name is...
I don't know if it is worth to add this, and I don't know if it's
a legit way. Anyway, I just share my thought(experience) for you and
linux-mmc to think more about how to deal with the case you meet rather
than sacrificing the performence by removing highspeed or reducing
frequency..
>
> Thanks,
> Julia
> --
> To unsubscribe from this list: send the line "unsubscribe linux-mmc" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at http://vger.kernel.org/majordomo-info.html
>
--
Best Regards
Shawn Lin
^ permalink raw reply
* Re: [RFC 1/2] sdhci: Add device tree property sd-broken-highspeed
From: Adrian Hunter @ 2016-10-06 6:13 UTC (permalink / raw)
To: Shawn Lin, Julia Cartwright, Rob Herring
Cc: Ulf Hansson, Zach Brown, Mark Rutland, linux-mmc,
devicetree-u79uwXL29TY76Z2rM5mHXA@public.gmane.org,
linux-kernel-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
In-Reply-To: <5972c8f4-23b5-7dbb-b87c-7be7ec5b1a17-TNX95d0MmH7DzftRWevZcw@public.gmane.org>
On 06/10/16 04:34, Shawn Lin wrote:
> On 2016/10/6 5:22, Julia Cartwright wrote:
>> On Wed, Oct 05, 2016 at 03:03:44PM -0500, Rob Herring wrote:
>>> On Wed, Oct 5, 2016 at 1:33 PM, Ulf Hansson <ulf.hansson-QSEj5FYQhm4dnm+yROfE0A@public.gmane.org> wrote:
>>>> On 23 September 2016 at 22:01, Zach Brown <zach.brown-acOepvfBmUk@public.gmane.org> wrote:
>>>>> Certain board configurations can make highspeed malfunction due to
>>>>> timing issues. In these cases a way is needed to force the controller
>>>>> and card into standard speed even if they otherwise appear to be capable
>>>>> of highspeed.
>>>>>
>>>>> The sd-broken-highspeed property will let the sdhci driver know that
>>>>> highspeed will not work.
>>>>>
>>>>> Signed-off-by: Zach Brown <zach.brown-acOepvfBmUk@public.gmane.org>
>>>>> ---
>>>>> Documentation/devicetree/bindings/mmc/mmc.txt | 2 ++
>>>>> 1 file changed, 2 insertions(+)
>>>>>
>>>>> diff --git a/Documentation/devicetree/bindings/mmc/mmc.txt
>>>>> b/Documentation/devicetree/bindings/mmc/mmc.txt
>>>>> index 8a37782..59332ea 100644
>>>>> --- a/Documentation/devicetree/bindings/mmc/mmc.txt
>>>>> +++ b/Documentation/devicetree/bindings/mmc/mmc.txt
>>>>> @@ -52,6 +52,8 @@ Optional properties:
>>>>> - no-sdio: controller is limited to send sdio cmd during initialization
>>>>> - no-sd: controller is limited to send sd cmd during initialization
>>>>> - no-mmc: controller is limited to send mmc cmd during initialization
>>>>> +- sd-broken-highspeed: Highspeed is broken, even if the controller and
>>>>> card
>>>>> + themselves claim they support highspeed.
>>>>
>>>> Regarding a broken card, that is managed via the card quirks and not in DT.
>>>>
>>>> If this is about a controller limitation, we already have the option
>>>> to describe what it supports, so we don't need an option to tell what
>>>> it *not* supports.
>>>>
>>>> For example "cap-sd-highspeed" tells whether the controller supports
>>>> SD high-speed, please use that instead.
>>>
>>> If a controller has a capability register and it lies (perhaps the
>>> board has limitations that the SoC does not), then you may need to
>>> disable a feature.
>>
>> That's precisely the case here. This is a board-level problem, not a
>> card or controller problem. As Zach mentioned in the cover letter, the
>> trace length between controller and card on some of our boards is too
>> long to meet high-speed timings, even though both card and controller
>> advertise it.
>
> IIRC, I saw the same problem while using sdhci to bring up a
> industrial board for vehicle. The trace length is so long that
> I have to limit the max-frequency to make it works properly when
> running at hishspeed.
>
> So could you try to limit the max-frequency in your DT to see
> if it could work for you? I guess it should work as once reducing
> the frequency, the timing per cycle will be large enough to meet
> the spec. At least that helped me solve my problem.
>
> For further consideration, I deployed a mechanism called "tuning for
> non UHS or non-hs200/400" for my donwstream tree at that time. The basic
> concept is to ask devices to send ext_csd(or send status for SD case)
> *repeatedly*. Some hosts, i.e. sdhci-of-arasan or dw_mmc-rockchip, can
> manually set rx delay via some clock unit registers to capture the
> working sample window and select the middle point of the longest good
> phase region. The same for retune. But it is a little complicated, and
> could only be applicable to the hosts who could adjust the rx delay
> manually when claiming the caps of MMC_CAPS_CAN_TUNING_FOR_HS, whatever
> the name is...
>
> I don't know if it is worth to add this, and I don't know if it's
> a legit way. Anyway, I just share my thought(experience) for you and
> linux-mmc to think more about how to deal with the case you meet rather
> than sacrificing the performence by removing highspeed or reducing
> frequency..
>
As Shawn points out, using max-frequency is a possibility. Did you consider
that?
There are 2 high speed caps:
cap-sd-highspeed
cap-mmc-highspeed
Yet patch in 2, the effect of sd-broken-highspeed is to suppress highspeed
for both SD and MMC. Should it then be called just broken-highspeed?
--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
^ permalink raw reply
* Re: [RFC 2/2] sdhci: Prevent SD from doing highspeed timing when sd-broken-highspeed property is set
From: Adrian Hunter @ 2016-10-06 6:39 UTC (permalink / raw)
To: Zach Brown, ulf.hansson-QSEj5FYQhm4dnm+yROfE0A
Cc: robh+dt-DgEjT+Ai2ygdnm+yROfE0A, mark.rutland-5wv7dgnIgG8,
linux-mmc-u79uwXL29TY76Z2rM5mHXA,
devicetree-u79uwXL29TY76Z2rM5mHXA,
linux-kernel-u79uwXL29TY76Z2rM5mHXA
In-Reply-To: <1474660869-15532-3-git-send-email-zach.brown-acOepvfBmUk@public.gmane.org>
On 23/09/16 23:01, Zach Brown wrote:
> From: Chen Yee Chew <chen.yee.chew-acOepvfBmUk@public.gmane.org>
>
> When the sd-broken-highspeed property is set the sdhci driver will not
> go into highspeed mode even if the controller and card appear to
> otherwise support highspeed mode.
>
> This is useful in cases where the controller and card support highspeed,
> but the board configuration or some other issue make highspeed
> impossible.
>
> Signed-off-by: Chen Yee Chew <chen.yee.chew-acOepvfBmUk@public.gmane.org>
> Reviewed-by: Keng Soon Cheah <keng.soon.cheah-acOepvfBmUk@public.gmane.org>
> Reviewed-by: Joe Hershberger <joe.hershberger-acOepvfBmUk@public.gmane.org>
> Signed-off-by: Zach Brown <zach.brown-acOepvfBmUk@public.gmane.org>
> ---
> drivers/mmc/host/sdhci-pltfm.c | 3 +++
> drivers/mmc/host/sdhci.c | 3 ++-
> drivers/mmc/host/sdhci.h | 2 ++
> 3 files changed, 7 insertions(+), 1 deletion(-)
>
> diff --git a/drivers/mmc/host/sdhci-pltfm.c b/drivers/mmc/host/sdhci-pltfm.c
> index ad49bfa..7482706 100644
> --- a/drivers/mmc/host/sdhci-pltfm.c
> +++ b/drivers/mmc/host/sdhci-pltfm.c
> @@ -87,6 +87,9 @@ void sdhci_get_of_property(struct platform_device *pdev)
> if (of_get_property(np, "broken-cd", NULL))
> host->quirks |= SDHCI_QUIRK_BROKEN_CARD_DETECTION;
>
> + if (of_get_property(np, "sd-broken-highspeed", NULL))
> + host->quirks2 |= SDHCI_QUIRK2_BROKEN_HISPD;
> +
We don't really want new quirks.
> if (of_get_property(np, "no-1-8-v", NULL))
> host->quirks2 |= SDHCI_QUIRK2_NO_1_8_V;
>
> diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c
> index 4805566..4b0969c 100644
> --- a/drivers/mmc/host/sdhci.c
> +++ b/drivers/mmc/host/sdhci.c
> @@ -3274,7 +3274,8 @@ int sdhci_setup_host(struct sdhci_host *host)
> if (host->quirks2 & SDHCI_QUIRK2_HOST_NO_CMD23)
> mmc->caps &= ~MMC_CAP_CMD23;
>
> - if (host->caps & SDHCI_CAN_DO_HISPD)
> + if ((host->caps & SDHCI_CAN_DO_HISPD) &&
> + !(host->quirks2 & SDHCI_QUIRK2_BROKEN_HISPD))
Is there any reason this can't just be:
if ((host->caps & SDHCI_CAN_DO_HISPD) &&
!(of_property_read_bool(mmc_dev(mmc)->of_node, "broken-highspeed")))
AFAICT it does the right thing whether or not OF is configured or of_node is null.
> mmc->caps |= MMC_CAP_SD_HIGHSPEED | MMC_CAP_MMC_HIGHSPEED;
>
> if ((host->quirks & SDHCI_QUIRK_BROKEN_CARD_DETECTION) &&
> diff --git a/drivers/mmc/host/sdhci.h b/drivers/mmc/host/sdhci.h
> index c722cd2..3d0fdda 100644
> --- a/drivers/mmc/host/sdhci.h
> +++ b/drivers/mmc/host/sdhci.h
> @@ -424,6 +424,8 @@ struct sdhci_host {
> #define SDHCI_QUIRK2_ACMD23_BROKEN (1<<14)
> /* Broken Clock divider zero in controller */
> #define SDHCI_QUIRK2_CLOCK_DIV_ZERO_BROKEN (1<<15)
> +/* Highspeed is broken even if it appears otherwise */
> +#define SDHCI_QUIRK2_BROKEN_HISPD (1<<16)
>
> int irq; /* Device IRQ */
> void __iomem *ioaddr; /* Mapped address */
>
--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
^ permalink raw reply
* Re: [RFC 1/2] sdhci: Add device tree property sd-broken-highspeed
From: Ulf Hansson @ 2016-10-06 8:03 UTC (permalink / raw)
To: Rob Herring
Cc: Zach Brown, Adrian Hunter, Mark Rutland, linux-mmc,
devicetree-u79uwXL29TY76Z2rM5mHXA@public.gmane.org,
linux-kernel-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
In-Reply-To: <CAL_JsqLjtn2-FaO_h9JWOsZqttQ35yV6KOnQz2Z-cMr=0GHJCg-JsoAwUIsXosN+BqQ9rBEUg@public.gmane.org>
On 5 October 2016 at 22:03, Rob Herring <robh+dt-DgEjT+Ai2ygdnm+yROfE0A@public.gmane.org> wrote:
> On Wed, Oct 5, 2016 at 1:33 PM, Ulf Hansson <ulf.hansson-QSEj5FYQhm4dnm+yROfE0A@public.gmane.org> wrote:
>> On 23 September 2016 at 22:01, Zach Brown <zach.brown-acOepvfBmUk@public.gmane.org> wrote:
>>> Certain board configurations can make highspeed malfunction due to
>>> timing issues. In these cases a way is needed to force the controller
>>> and card into standard speed even if they otherwise appear to be capable
>>> of highspeed.
>>>
>>> The sd-broken-highspeed property will let the sdhci driver know that
>>> highspeed will not work.
>>>
>>> Signed-off-by: Zach Brown <zach.brown-acOepvfBmUk@public.gmane.org>
>>> ---
>>> Documentation/devicetree/bindings/mmc/mmc.txt | 2 ++
>>> 1 file changed, 2 insertions(+)
>>>
>>> diff --git a/Documentation/devicetree/bindings/mmc/mmc.txt b/Documentation/devicetree/bindings/mmc/mmc.txt
>>> index 8a37782..59332ea 100644
>>> --- a/Documentation/devicetree/bindings/mmc/mmc.txt
>>> +++ b/Documentation/devicetree/bindings/mmc/mmc.txt
>>> @@ -52,6 +52,8 @@ Optional properties:
>>> - no-sdio: controller is limited to send sdio cmd during initialization
>>> - no-sd: controller is limited to send sd cmd during initialization
>>> - no-mmc: controller is limited to send mmc cmd during initialization
>>> +- sd-broken-highspeed: Highspeed is broken, even if the controller and card
>>> + themselves claim they support highspeed.
>>
>> Regarding a broken card, that is managed via the card quirks and not in DT.
>>
>> If this is about a controller limitation, we already have the option
>> to describe what it supports, so we don't need an option to tell what
>> it *not* supports.
>>
>> For example "cap-sd-highspeed" tells whether the controller supports
>> SD high-speed, please use that instead.
>
> If a controller has a capability register and it lies (perhaps the
> board has limitations that the SoC does not), then you may need to
> disable a feature.
I understand, although the SDHCI capabilities register is broken for
most SDHCI variants. In principle, we would more or less have to add a
*-broken binding for each bit in that register. I don't like that!
Maybe a better option is to add a "sdhci-cap-broken" or perhaps
"sdhci-cap-speed-modes-broken", which tells the driver to not rely on
the capabilities register and instead find out what *is* supported by
looking at the other mmc generic DT bindings.
What do you think of that?
Kind regards
Uffe
--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
^ permalink raw reply
* [PATCH] sdhci-esdhc-imx: Correct two register accesses
From: Aaron Brice @ 2016-10-06 22:54 UTC (permalink / raw)
To: david.russell, adrian.hunter, ulf.hansson, aisheng.dong
Cc: linux-mmc, linux-kernel, linux-arm-kernel
- The DMA error interrupt bit is in a different position as
compared to the sdhci standard. This is accounted for in
many cases, but not handled in the case of clearing the
INT_STATUS register by writing a 1 to that location.
- The HOST_CONTROL register is very different as compared to
the sdhci standard. This is accounted for in the write
case, but not when read back out (which it is in the sdhci
code).
Signed-off-by: Dave Russell <david.russell@datasoft.com>
Signed-off-by: Aaron Brice <aaron.brice@datasoft.com>
---
drivers/mmc/host/sdhci-esdhc-imx.c | 23 ++++++++++++++++++++++-
1 file changed, 22 insertions(+), 1 deletion(-)
diff --git a/drivers/mmc/host/sdhci-esdhc-imx.c b/drivers/mmc/host/sdhci-esdhc-imx.c
index 1f54fd8..d61ef16 100644
--- a/drivers/mmc/host/sdhci-esdhc-imx.c
+++ b/drivers/mmc/host/sdhci-esdhc-imx.c
@@ -346,7 +346,8 @@ static void esdhc_writel_le(struct sdhci_host *host, u32 val, int reg)
struct pltfm_imx_data *imx_data = sdhci_pltfm_priv(pltfm_host);
u32 data;
- if (unlikely(reg == SDHCI_INT_ENABLE || reg == SDHCI_SIGNAL_ENABLE)) {
+ if (unlikely(reg == SDHCI_INT_ENABLE || reg == SDHCI_SIGNAL_ENABLE ||
+ reg == SDHCI_INT_STATUS)) {
if ((val & SDHCI_INT_CARD_INT) && !esdhc_is_usdhc(imx_data)) {
/*
* Clear and then set D3CD bit to avoid missing the
@@ -555,6 +556,25 @@ static void esdhc_writew_le(struct sdhci_host *host, u16 val, int reg)
esdhc_clrset_le(host, 0xffff, val, reg);
}
+static u8 esdhc_readb_le(struct sdhci_host *host, int reg)
+{
+ u8 ret;
+ u32 long_val;
+
+ switch (reg) {
+ case SDHCI_HOST_CONTROL:
+ long_val = readl(host->ioaddr + reg);
+
+ ret = long_val & SDHCI_CTRL_LED;
+ ret |= (long_val >> 5) & SDHCI_CTRL_DMA_MASK;
+ ret |= (long_val & ESDHC_CTRL_4BITBUS);
+ ret |= (long_val & ESDHC_CTRL_8BITBUS) << 3;
+ return ret;
+ }
+
+ return readb(host->ioaddr + reg);
+}
+
static void esdhc_writeb_le(struct sdhci_host *host, u8 val, int reg)
{
struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
@@ -947,6 +967,7 @@ static void esdhc_set_timeout(struct sdhci_host *host, struct mmc_command *cmd)
static struct sdhci_ops sdhci_esdhc_ops = {
.read_l = esdhc_readl_le,
.read_w = esdhc_readw_le,
+ .read_b = esdhc_readb_le,
.write_l = esdhc_writel_le,
.write_w = esdhc_writew_le,
.write_b = esdhc_writeb_le,
--
2.7.4
^ permalink raw reply related
* Re: [RFC PATCH v2 1/4] mmc: dw_mmc: add runtime PM callback
From: Jaehoon Chung @ 2016-10-07 4:12 UTC (permalink / raw)
To: Shawn Lin, Ulf Hansson; +Cc: linux-mmc, Doug Anderson, linux-rockchip
In-Reply-To: <1475225317-2815-2-git-send-email-shawn.lin@rock-chips.com>
Hi Shawn,
On 09/30/2016 05:48 PM, Shawn Lin wrote:
> This patch add dw_mci_runtime_suspend/resume interfaces
> and expose it to dw_mci variant driver to support runtime
> PM.
>
> Signed-off-by: Shawn Lin <shawn.lin@rock-chips.com>
>
> ---
>
> drivers/mmc/host/dw_mmc.c | 60 +++++++++++++++++++++++++++++++++++++++++++++--
> drivers/mmc/host/dw_mmc.h | 4 +++-
> 2 files changed, 61 insertions(+), 3 deletions(-)
>
> diff --git a/drivers/mmc/host/dw_mmc.c b/drivers/mmc/host/dw_mmc.c
> index 4fcbc40..54b860e 100644
> --- a/drivers/mmc/host/dw_mmc.c
> +++ b/drivers/mmc/host/dw_mmc.c
> @@ -3266,7 +3266,7 @@ EXPORT_SYMBOL(dw_mci_remove);
>
>
>
> -#ifdef CONFIG_PM_SLEEP
> +#ifdef CONFIG_PM
> /*
> * TODO: we should probably disable the clock to the card in the suspend path.
> */
> @@ -3324,7 +3324,63 @@ int dw_mci_resume(struct dw_mci *host)
> return 0;
> }
> EXPORT_SYMBOL(dw_mci_resume);
> -#endif /* CONFIG_PM_SLEEP */
> +
> +int dw_mci_runtime_suspend(struct dw_mci *host)
> +{
> + printk("dw_mci_runtime_suspend\n");
Maybe I think you will remove this..if you send the patch, not RFC.
> +
> + if (host->use_dma && host->dma_ops->exit)
> + host->dma_ops->exit(host);
Can just call dw_mci_suspend()
> +
> + clk_disable_unprepare(host->ciu_clk);
> +
> + return 0;
> +}
> +EXPORT_SYMBOL(dw_mci_runtime_suspend);
> +
> +int dw_mci_runtime_resume(struct dw_mci *host)
> +{
> + int ret = 0;
> + int i;
Can make one line.
> +
> + printk("dw_mci_runtime_resume\n");
> +
> + ret = clk_prepare_enable(host->ciu_clk);
> + if (ret)
> + return ret;
> +
> + if (host->use_dma && host->dma_ops->init)
> + host->dma_ops->init(host);
> +
> + mci_writel(host, FIFOTH, host->fifoth_val);
> + host->prev_blksz = 0;
> +
> + mci_writel(host, TMOUT, 0xFFFFFFFF);
> + mci_writel(host, RINTSTS, 0xFFFFFFFF);
> + mci_writel(host, INTMASK, SDMMC_INT_CMD_DONE |
> + SDMMC_INT_DATA_OVER |
> + SDMMC_INT_TXDR | SDMMC_INT_RXDR |
> + DW_MCI_ERROR_FLAGS);
> + mci_writel(host, CTRL, SDMMC_CTRL_INT_ENABLE);
> +
> + for (i = 0; i < host->num_slots; i++) {
> + struct dw_mci_slot *slot = host->slot[i];
> +
> + if (!slot)
> + continue;
> +
> + if (slot->mmc->pm_flags & MMC_PM_KEEP_POWER) {
> + dw_mci_set_ios(slot->mmc, &slot->mmc->ios);
> + dw_mci_setup_bus(slot, true);
> + }
> + }
> +
> + dw_mci_enable_cd(host);
Some part of this function can be reused with codes in dw_mci_resume().
Best Regards,
Jaehoon Chung
> +
> + return ret;
> +}
> +EXPORT_SYMBOL(dw_mci_runtime_resume);
> +#endif /* CONFIG_PM */
>
> static int __init dw_mci_init(void)
> {
> diff --git a/drivers/mmc/host/dw_mmc.h b/drivers/mmc/host/dw_mmc.h
> index e8cd2de..baa7261 100644
> --- a/drivers/mmc/host/dw_mmc.h
> +++ b/drivers/mmc/host/dw_mmc.h
> @@ -234,9 +234,11 @@
>
> extern int dw_mci_probe(struct dw_mci *host);
> extern void dw_mci_remove(struct dw_mci *host);
> -#ifdef CONFIG_PM_SLEEP
> +#ifdef CONFIG_PM
> extern int dw_mci_suspend(struct dw_mci *host);
> extern int dw_mci_resume(struct dw_mci *host);
> +extern int dw_mci_runtime_suspend(struct dw_mci *host);
> +extern int dw_mci_runtime_resume(struct dw_mci *host);
> #endif
>
> /**
>
^ permalink raw reply
* Re: [RFC PATCH v2 4/4] mmc: dw_mmc-rockchip: disable biu clk and genpd if possible
From: Jaehoon Chung @ 2016-10-07 4:15 UTC (permalink / raw)
To: Shawn Lin, Ulf Hansson
Cc: linux-rockchip-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
linux-mmc-u79uwXL29TY76Z2rM5mHXA, Doug Anderson
In-Reply-To: <1475225317-2815-5-git-send-email-shawn.lin-TNX95d0MmH7DzftRWevZcw@public.gmane.org>
On 09/30/2016 05:48 PM, Shawn Lin wrote:
> We could disable biu clk and power-off genpd if gpio
> card detect available.
>
> Signed-off-by: Shawn Lin <shawn.lin-TNX95d0MmH7DzftRWevZcw@public.gmane.org>
> ---
>
> drivers/mmc/host/dw_mmc-rockchip.c | 18 +++++++++++++++++-
> 1 file changed, 17 insertions(+), 1 deletion(-)
>
> diff --git a/drivers/mmc/host/dw_mmc-rockchip.c b/drivers/mmc/host/dw_mmc-rockchip.c
> index 3ad041c..e7b3b6d 100644
> --- a/drivers/mmc/host/dw_mmc-rockchip.c
> +++ b/drivers/mmc/host/dw_mmc-rockchip.c
> @@ -13,6 +13,7 @@
> #include <linux/mmc/host.h>
> #include <linux/mmc/dw_mmc.h>
> #include <linux/of_address.h>
> +#include <linux/mmc/slot-gpio.h>
> #include <linux/pm_runtime.h>
> #include <linux/slab.h>
>
> @@ -366,14 +367,29 @@ static int dw_mci_rockchip_remove(struct platform_device *pdev)
> static int dw_mci_rockchip_runtime_suspend(struct device *dev)
> {
> struct dw_mci *host = dev_get_drvdata(dev);
> + int ret;
>
> - return dw_mci_runtime_suspend(host);
> + ret = dw_mci_runtime_suspend(host);
> + if (ret)
> + return ret;
> +
> + if (mmc_can_gpio_cd(host->slot[0]->mmc)) {
Can use cur_slot instead of slot[0]? And doesn't need to check "host->slot[]" or "host->cur_slot"?
Best Regards,
Jaehoon Chung
> + clk_disable_unprepare(host->biu_clk);
> + pm_runtime_put(dev);
> + }
> +
> + return 0;
> }
>
> static int dw_mci_rockchip_runtime_resume(struct device *dev)
> {
> struct dw_mci *host = dev_get_drvdata(dev);
>
> + if (mmc_can_gpio_cd(host->slot[0]->mmc)) {
> + pm_runtime_get_sync(dev);
> + clk_prepare_enable(host->biu_clk);
> + }
> +
> return dw_mci_runtime_resume(host);
> }
> #endif /* CONFIG_PM */
>
^ permalink raw reply
* [PATCH] mmc: sdhci: put together into one condition checking
From: Jaehoon Chung @ 2016-10-07 5:08 UTC (permalink / raw)
To: linux-mmc; +Cc: ulf.hansson, adrian.hunter, Jaehoon Chung
value of ios->timing is not related with SDCHI v3.0.
If Controller version is v3.0, SDHCI_QUIRK_NO_HISPD_BIT is meaningless.
To prevent the setting wrong bit moves into one codntion checking.
(e.g sdhci-s3c doesn't use SDHCI_CTRL_HISPD bit, instead using this bit as
other purpose.)
Signed-off-by: Jaehoon Chung <jh80.chung@samsung.com>
---
drivers/mmc/host/sdhci.c | 19 ++++++++-----------
1 file changed, 8 insertions(+), 11 deletions(-)
diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c
index 4805566..f854c66 100644
--- a/drivers/mmc/host/sdhci.c
+++ b/drivers/mmc/host/sdhci.c
@@ -1619,7 +1619,14 @@ static void sdhci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
ctrl = sdhci_readb(host, SDHCI_HOST_CONTROL);
if ((ios->timing == MMC_TIMING_SD_HS ||
- ios->timing == MMC_TIMING_MMC_HS)
+ ios->timing == MMC_TIMING_MMC_HS ||
+ ios->timing == MMC_TIMING_MMC_HS400 ||
+ ios->timing == MMC_TIMING_MMC_HS200 ||
+ ios->timing == MMC_TIMING_MMC_DDR52 ||
+ ios->timing == MMC_TIMING_UHS_SDR50 ||
+ ios->timing == MMC_TIMING_UHS_SDR104 ||
+ ios->timing == MMC_TIMING_UHS_DDR50 ||
+ ios->timing == MMC_TIMING_UHS_SDR25)
&& !(host->quirks & SDHCI_QUIRK_NO_HISPD_BIT))
ctrl |= SDHCI_CTRL_HISPD;
else
@@ -1628,16 +1635,6 @@ static void sdhci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
if (host->version >= SDHCI_SPEC_300) {
u16 clk, ctrl_2;
- /* In case of UHS-I modes, set High Speed Enable */
- if ((ios->timing == MMC_TIMING_MMC_HS400) ||
- (ios->timing == MMC_TIMING_MMC_HS200) ||
- (ios->timing == MMC_TIMING_MMC_DDR52) ||
- (ios->timing == MMC_TIMING_UHS_SDR50) ||
- (ios->timing == MMC_TIMING_UHS_SDR104) ||
- (ios->timing == MMC_TIMING_UHS_DDR50) ||
- (ios->timing == MMC_TIMING_UHS_SDR25))
- ctrl |= SDHCI_CTRL_HISPD;
-
if (!host->preset_enabled) {
sdhci_writeb(host, ctrl, SDHCI_HOST_CONTROL);
/*
--
1.9.1
^ permalink raw reply related
* Re: [RFC PATCH v2 4/4] mmc: dw_mmc-rockchip: disable biu clk and genpd if possible
From: Shawn Lin @ 2016-10-07 9:01 UTC (permalink / raw)
To: Jaehoon Chung, Ulf Hansson
Cc: linux-rockchip-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
shawn.lin-TNX95d0MmH7DzftRWevZcw,
linux-mmc-u79uwXL29TY76Z2rM5mHXA, Doug Anderson
In-Reply-To: <b9180674-648a-d2d1-0221-cec90b73adab-Sze3O3UU22JBDgjK7y7TUQ@public.gmane.org>
在 2016/10/7 12:15, Jaehoon Chung 写道:
> On 09/30/2016 05:48 PM, Shawn Lin wrote:
>> We could disable biu clk and power-off genpd if gpio
>> card detect available.
>>
>> Signed-off-by: Shawn Lin <shawn.lin@rock-chips.com>
>> ---
>>
>> drivers/mmc/host/dw_mmc-rockchip.c | 18 +++++++++++++++++-
>> 1 file changed, 17 insertions(+), 1 deletion(-)
>>
>> diff --git a/drivers/mmc/host/dw_mmc-rockchip.c b/drivers/mmc/host/dw_mmc-rockchip.c
>> index 3ad041c..e7b3b6d 100644
>> --- a/drivers/mmc/host/dw_mmc-rockchip.c
>> +++ b/drivers/mmc/host/dw_mmc-rockchip.c
>> @@ -13,6 +13,7 @@
>> #include <linux/mmc/host.h>
>> #include <linux/mmc/dw_mmc.h>
>> #include <linux/of_address.h>
>> +#include <linux/mmc/slot-gpio.h>
>> #include <linux/pm_runtime.h>
>> #include <linux/slab.h>
>>
>> @@ -366,14 +367,29 @@ static int dw_mci_rockchip_remove(struct platform_device *pdev)
>> static int dw_mci_rockchip_runtime_suspend(struct device *dev)
>> {
>> struct dw_mci *host = dev_get_drvdata(dev);
>> + int ret;
>>
>> - return dw_mci_runtime_suspend(host);
>> + ret = dw_mci_runtime_suspend(host);
>> + if (ret)
>> + return ret;
>> +
>> + if (mmc_can_gpio_cd(host->slot[0]->mmc)) {
>
> Can use cur_slot instead of slot[0]? And doesn't need to check "host->slot[]" or "host->cur_slot"?
yes, we could. I will fix this for RFC-V3. Also I think we could
disable biu_clk and genpd for non-removable devices since in these
cases, we don't have interaction from devices 2 host.. But for sd,
we should consider the card detect interrupt.
>
> Best Regards,
> Jaehoon Chung
>
>> + clk_disable_unprepare(host->biu_clk);
>> + pm_runtime_put(dev);
>> + }
>> +
>> + return 0;
>> }
>>
>> static int dw_mci_rockchip_runtime_resume(struct device *dev)
>> {
>> struct dw_mci *host = dev_get_drvdata(dev);
>>
>> + if (mmc_can_gpio_cd(host->slot[0]->mmc)) {
>> + pm_runtime_get_sync(dev);
>> + clk_prepare_enable(host->biu_clk);
>> + }
>> +
>> return dw_mci_runtime_resume(host);
>> }
>> #endif /* CONFIG_PM */
>>
>
>
> _______________________________________________
> Linux-rockchip mailing list
> Linux-rockchip@lists.infradead.org
> http://lists.infradead.org/mailman/listinfo/linux-rockchip
>
--
Best Regards
Shawn Lin
_______________________________________________
Linux-rockchip mailing list
Linux-rockchip@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-rockchip
^ permalink raw reply
* Re: [RFC PATCH v2 1/4] mmc: dw_mmc: add runtime PM callback
From: Shawn Lin @ 2016-10-07 9:19 UTC (permalink / raw)
To: Jaehoon Chung, Ulf Hansson
Cc: linux-rockchip-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
shawn.lin-TNX95d0MmH7DzftRWevZcw,
linux-mmc-u79uwXL29TY76Z2rM5mHXA, Doug Anderson
In-Reply-To: <2dd84d82-a14b-7d5e-80f0-89e6c0b1aaae-Sze3O3UU22JBDgjK7y7TUQ@public.gmane.org>
在 2016/10/7 12:12, Jaehoon Chung 写道:
> Hi Shawn,
>
> On 09/30/2016 05:48 PM, Shawn Lin wrote:
>> This patch add dw_mci_runtime_suspend/resume interfaces
>> and expose it to dw_mci variant driver to support runtime
>> PM.
>>
>> Signed-off-by: Shawn Lin <shawn.lin@rock-chips.com>
>>
>> ---
>>
>> drivers/mmc/host/dw_mmc.c | 60 +++++++++++++++++++++++++++++++++++++++++++++--
>> drivers/mmc/host/dw_mmc.h | 4 +++-
>> 2 files changed, 61 insertions(+), 3 deletions(-)
>>
>> diff --git a/drivers/mmc/host/dw_mmc.c b/drivers/mmc/host/dw_mmc.c
>> index 4fcbc40..54b860e 100644
>> --- a/drivers/mmc/host/dw_mmc.c
>> +++ b/drivers/mmc/host/dw_mmc.c
>> @@ -3266,7 +3266,7 @@ EXPORT_SYMBOL(dw_mci_remove);
>>
>>
>>
>> -#ifdef CONFIG_PM_SLEEP
>> +#ifdef CONFIG_PM
>> /*
>> * TODO: we should probably disable the clock to the card in the suspend path.
>> */
>> @@ -3324,7 +3324,63 @@ int dw_mci_resume(struct dw_mci *host)
>> return 0;
>> }
>> EXPORT_SYMBOL(dw_mci_resume);
>> -#endif /* CONFIG_PM_SLEEP */
>> +
>> +int dw_mci_runtime_suspend(struct dw_mci *host)
>> +{
>> + printk("dw_mci_runtime_suspend\n");
>
> Maybe I think you will remove this..if you send the patch, not RFC.
yep, it just for me to print info for testing the rpm governer.
I will remove this when sending non-RFC one.
>
>> +
>> + if (host->use_dma && host->dma_ops->exit)
>> + host->dma_ops->exit(host);
>
> Can just call dw_mci_suspend()
If we deploy rpm properly, we don't need system PM at all..
To be frank, I was wanna remove dw_mci_suspend/resume pairs,
and only leave the runtime pairs here. As you could see that
runtime PM and system PM does the same thing, so personally
I realize we should help dw_mmc variant drivers migrate to use
runtime PM. That is what I did for dw_mmc-rockchip. If we are
not sure should we enable rpm for i.e., exynos, or k3, we don't
add pm_runtime_* for them for the first step and leave it to
the owners/users to make the decision. The following code should
be enough to help we achieve the first step.
static const struct dev_pm_ops dw_mci_XXXXXX_dev_pm_ops = {
SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend,
pm_runtime_force_resume)
SET_RUNTIME_PM_OPS(dw_mci_runtime_suspend,
dw_mci_runtime_resume,
NULL)
};
If there are some special ops for system PM like exynos, we could wrap
new dw_mci_exynos_runtime_resume there..
The same for dw_mci_resume.
What's your opinion? Seems Ulf was too busy to make comments
here. But I believe it is in the right direction.
>
>> +
>> + clk_disable_unprepare(host->ciu_clk);
>> +
>> + return 0;
>> +}
>> +EXPORT_SYMBOL(dw_mci_runtime_suspend);
>> +
>> +int dw_mci_runtime_resume(struct dw_mci *host)
>> +{
>> + int ret = 0;
>> + int i;
>
> Can make one line.
>
>> +
>> + printk("dw_mci_runtime_resume\n");
>> +
>> + ret = clk_prepare_enable(host->ciu_clk);
>> + if (ret)
>> + return ret;
>> +
>> + if (host->use_dma && host->dma_ops->init)
>> + host->dma_ops->init(host);
>> +
>> + mci_writel(host, FIFOTH, host->fifoth_val);
>> + host->prev_blksz = 0;
>> +
>> + mci_writel(host, TMOUT, 0xFFFFFFFF);
>> + mci_writel(host, RINTSTS, 0xFFFFFFFF);
>> + mci_writel(host, INTMASK, SDMMC_INT_CMD_DONE |
>> + SDMMC_INT_DATA_OVER |
>> + SDMMC_INT_TXDR | SDMMC_INT_RXDR |
>> + DW_MCI_ERROR_FLAGS);
>> + mci_writel(host, CTRL, SDMMC_CTRL_INT_ENABLE);
>> +
>> + for (i = 0; i < host->num_slots; i++) {
>> + struct dw_mci_slot *slot = host->slot[i];
>> +
>> + if (!slot)
>> + continue;
>> +
>> + if (slot->mmc->pm_flags & MMC_PM_KEEP_POWER) {
>> + dw_mci_set_ios(slot->mmc, &slot->mmc->ios);
>> + dw_mci_setup_bus(slot, true);
>> + }
>> + }
>> +
>> + dw_mci_enable_cd(host);
>
> Some part of this function can be reused with codes in dw_mci_resume().
>
> Best Regards,
> Jaehoon Chung
>
>> +
>> + return ret;
>> +}
>> +EXPORT_SYMBOL(dw_mci_runtime_resume);
>> +#endif /* CONFIG_PM */
>>
>> static int __init dw_mci_init(void)
>> {
>> diff --git a/drivers/mmc/host/dw_mmc.h b/drivers/mmc/host/dw_mmc.h
>> index e8cd2de..baa7261 100644
>> --- a/drivers/mmc/host/dw_mmc.h
>> +++ b/drivers/mmc/host/dw_mmc.h
>> @@ -234,9 +234,11 @@
>>
>> extern int dw_mci_probe(struct dw_mci *host);
>> extern void dw_mci_remove(struct dw_mci *host);
>> -#ifdef CONFIG_PM_SLEEP
>> +#ifdef CONFIG_PM
>> extern int dw_mci_suspend(struct dw_mci *host);
>> extern int dw_mci_resume(struct dw_mci *host);
>> +extern int dw_mci_runtime_suspend(struct dw_mci *host);
>> +extern int dw_mci_runtime_resume(struct dw_mci *host);
>> #endif
>>
>> /**
>>
>
> --
> To unsubscribe from this list: send the line "unsubscribe linux-mmc" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at http://vger.kernel.org/majordomo-info.html
>
--
Best Regards
Shawn Lin
_______________________________________________
Linux-rockchip mailing list
Linux-rockchip@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-rockchip
^ permalink raw reply
* Re: Enabling MMC BKOPs in kernel based on host caps
From: Ravikumar @ 2016-10-07 14:05 UTC (permalink / raw)
To: Alex Lemberg, Shawn Lin, Ravikumar Kattekola,
linux-mmc@vger.kernel.org
In-Reply-To: <4B194B80-8DE7-4296-914E-00FC1C0A4D66@sandisk.com>
Hi Alex,
On Thursday 06 October 2016 03:39 AM, Alex Lemberg wrote:
> Hi Ravikumar,
>
> The reason for enabling Auto/Manual BKOPS is understandable.
> Personally I don’t think that user space should play with a storage device
> BKOPS settings, it should be a matter of storage device and a driver decision.
> But as Shown mentioned, there is a discussion on this topic.
> Also, recently I have submitted a patch for letting device more time
> to complete its BKOPS on runtime Suspend:
> http://www.spinics.net/lists/linux-mmc/msg38952.html
>
> Thanks,
> Alex
Thanks for sharing this information.
I totally agree with you, it should be the driver who makes the decision.
I'm still wondering why there's a concern even after adding a platform
level check.
Should the code to enable BKOPS be wrapped inside a CONFIG_ENABLE_BKOPS
in order to give the individual an option to change the default behavior?
Thanks and Regards,
RK
>
> On 10/5/16, 1:55 PM, "Ravikumar" <a0131654@ti.com> wrote:
>
>> Hi Shawn,
>>
>>
>> On Wednesday 05 October 2016 03:07 PM, Shawn Lin wrote:
>>> Hi Ravikumar,
>>>
>>> + Alex,
>>>
>>> 在 2016/10/3 18:43, Ravikumar Kattekola 写道:
>>>> Hi all,
>>>> I’ve seen an eMMC failure due to pending background operations on
>>>> a certain OMAP device since bkops enable bit was not set.
>>>> Further investigation showed me that someone already posted patch to
>>>> enable Background operations in kernel based on a host capability
>>>> check (Caps2 & BK_OPS_EN)
>>>> but was turned down quoting that it should be enabled from user space
>>>> using mmc-utils.
>>>>
>>>> Enabling this would add one additional check for exception event in
>>>> the response R1 or R1B (only on hosts that explicitly set BK_OPS_EN
>>>> in caps2).
>>>> But not enabling this could lead to a system failure especially when
>>>> the Filesystem is on eMMC and the card stops responding due to
>>>> pending critical bkops.
>>>>
>>>> I would like to ask for expert opinion on ‘why is it a bad idea to
>>>> enable bkops in kernel?’
>>> Some discussion about the similar topic could be found here:
>>> https://patchwork.kernel.org/patch/9157121/
>>>
>>>
>>>> It’s a one time programmable bit but if it helps in keeping system
>>>> functional why not do it?
>>> Actually BKOPS_EN is not OTP bit.. Quoted from Ulf "I don't have any
>>> issue to allow all non-OTP registers bits to be written." So I guess
>>> you could do this, although it needs more discussion there.
>> In the spec for 4.5, jesd84_B45 it does say not whether the ENABLE bit
>> is OTP.
>> But in 5.1 spec, jesd84-b51, it says MANUAL_EN is R/W which means OTP
>> and readable.
>> As I read form mmc-utils -help
>> " mmc bkops enable <device>
>> Enable the eMMC BKOPS feature on <device>.
>> NOTE! This is a one-time programmable (unreversible)
>> change "
>>> But it's persistent EXT_CSD register and we get used to control it from
>>> userspace, which is the policy we have been sticking to when writing to
>>> persistent EXT_CSD registers. I guess that is nothing about "right and
>>> wrong", just a rule for us in case someone wants to set the persistent
>>> bit in kernel but setting other persistent bits from user-space, which
>>> is prone to mess up the mmc core. Or, someone will sent mail to the list
>>> asking "why is it a good idea to enable bkops in kernel" ? :)
>> So there's no functional problem/reason that stops us from enabling
>> BKOPS (Manual) in
>> kernel except for consistency with other persistent registers.
>> Since not enabling BKOPS could lead to a functional failure /
>> non-responsive system
>> at a later point of time I guess this could be exempted.
>> what do you think?
>>
>> As user I would choose functional safety and reliability over performance.
>> Hence it would make sense to have the bkops (at least manual) be enabled
>> by default,
>> especially in Automotive applications.
>>
>>>> I haven’t measured the performance impact but I don’t see a reason
>>>> for major drop because the frequency of critical bkops events would
>>>> be less.
>>>>
>>>> Regards,
>>>> RK
>>>> --
>>>> To unsubscribe from this list: send the line "unsubscribe linux-mmc" in
>>>> the body of a message to majordomo@vger.kernel.org
>>>> More majordomo info at http://vger.kernel.org/majordomo-info.html
>>>>
>>>
>> Thanks for your response.
>>
>> Regards,
>> RK
>>
^ permalink raw reply
* [PATCH 0/10] mmc: Add support to Marvell Xenon SD Host Controller
From: Gregory CLEMENT @ 2016-10-07 15:22 UTC (permalink / raw)
To: Ulf Hansson, Adrian Hunter, linux-mmc-u79uwXL29TY76Z2rM5mHXA
Cc: Jason Cooper, Andrew Lunn, Sebastian Hesselbarth, Gregory CLEMENT,
Rob Herring, devicetree-u79uwXL29TY76Z2rM5mHXA, Thomas Petazzoni,
linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r, Ziji Hu,
Jack(SH) Zhu, Jimmy Xu, Jisheng Zhang, Nadav Haklai, Ryan Gao,
Doug Jones, Shiwu Zhang, Victor Gu, Wei(SOCP) Liu, Wilson Ding,
Xueping Liu, Hilbert Zhang
Hello,
This series adds support for the SDHCI Xenon controller which can be
currently found on the Armada 37xx and the Armada 7K/8K but will be
also used in more Marvell SoC (and not only the mvebu ones actually).
A few months ago a series has been sent to support this controller:
http://lists.infradead.org/pipermail/linux-arm-kernel/2016-June/434925.html
However, since this first version, the engineer team in charge of this
controller wished to have a better involvement in the mainline support
of the driver. This new series is the result the Marvell development
with the aim of supporting more feature that only the ones needed for
the Armada 3720 SoC.
About the hardware and the driver itself, according to Ziji Hu:
"Marvell Xenon SDHC is widely used in Marvell products.
It supports SD/SDIO/eMMC.
It conforms to SD Physical Layer Specification Version 3.01 and
is designed according to the guidelines provided in the SD Host
Controller Standard Specification Version 3.00.
Xenon SDHC IP contains PHY. There are tree types of Xenon PHY in use.
Each Xenon SDHC only contain one type of PHY."
Thanks,
Gregory
Gregory CLEMENT (3):
arm64: dts: marvell: add eMMC support for Armada 37xx
arm64: dts: marvell: add sdhci support for Armada 7K/8K
arm64: configs: enable SDHCI driver for Xenon
Ziji Hu (7):
mmc: sdhci: Export sdhci_set_ios() from sdhci.c
mmc: sdhci: Export sdhci_start_signal_voltage_switch() in sdhci.c
mmc: sdhci: Export sdhci_execute_tuning() in sdhci.c
MAINTAINERS: add entry for Marvell Xenon MMC Host Controller drivers
dt: bindings: Add bindings for Marvell Xenon SD Host Controller
mmc: sdhci-xenon: Add Marvell Xenon SDHC core functionality
mmc: sdhci-xenon: Add support to PHYs of Marvell Xenon SDHC
Documentation/devicetree/bindings/mmc/marvell,sdhci-xenon.txt | 164 +-
MAINTAINERS | 8 +-
arch/arm64/boot/dts/marvell/armada-3720-db.dts | 7 +-
arch/arm64/boot/dts/marvell/armada-37xx.dtsi | 11 +-
arch/arm64/boot/dts/marvell/armada-7040-db.dts | 7 +-
arch/arm64/boot/dts/marvell/armada-ap806.dtsi | 9 +-
arch/arm64/configs/defconfig | 1 +-
drivers/mmc/host/Kconfig | 9 +-
drivers/mmc/host/Makefile | 3 +-
drivers/mmc/host/sdhci-xenon-phy.c | 1141 +++++++-
drivers/mmc/host/sdhci-xenon-phy.h | 157 +-
drivers/mmc/host/sdhci-xenon.c | 603 ++++-
drivers/mmc/host/sdhci-xenon.h | 151 +-
drivers/mmc/host/sdhci.c | 11 +-
drivers/mmc/host/sdhci.h | 4 +-
15 files changed, 2282 insertions(+), 4 deletions(-)
create mode 100644 Documentation/devicetree/bindings/mmc/marvell,sdhci-xenon.txt
create mode 100644 drivers/mmc/host/sdhci-xenon-phy.c
create mode 100644 drivers/mmc/host/sdhci-xenon-phy.h
create mode 100644 drivers/mmc/host/sdhci-xenon.c
create mode 100644 drivers/mmc/host/sdhci-xenon.h
base-commit: 32b7d4a95f79deb8b73e8b303a54dfdef4a3d845
--
git-series 0.8.10
--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
^ permalink raw reply
* [PATCH 1/10] mmc: sdhci: Export sdhci_set_ios() from sdhci.c
From: Gregory CLEMENT @ 2016-10-07 15:22 UTC (permalink / raw)
To: Ulf Hansson, Adrian Hunter, linux-mmc-u79uwXL29TY76Z2rM5mHXA
Cc: Jason Cooper, Andrew Lunn, Sebastian Hesselbarth, Gregory CLEMENT,
Rob Herring, devicetree-u79uwXL29TY76Z2rM5mHXA, Thomas Petazzoni,
linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r, Ziji Hu,
Jack(SH) Zhu, Jimmy Xu, Jisheng Zhang, Nadav Haklai, Ryan Gao,
Doug Jones, Shiwu Zhang, Victor Gu, Wei(SOCP) Liu, Wilson Ding,
Xueping Liu, Hilbert Zhang
In-Reply-To: <cover.990e3503c30c18ecf50dd48bce3d52811f03ca22.1475853198.git-series.gregory.clement-wi1+55ScJUtKEb57/3fJTNBPR1lH4CV8@public.gmane.org>
From: Ziji Hu <huziji-eYqpPyKDWXRBDgjK7y7TUQ@public.gmane.org>
Export sdhci_set_ios() in sdhci.c.
Thus vendor sdhci driver can implement its own set_ios() routine.
Signed-off-by: Hu Ziji <huziji-eYqpPyKDWXRBDgjK7y7TUQ@public.gmane.org>
Reviewed-by: Gregory CLEMENT <gregory.clement-wi1+55ScJUtKEb57/3fJTNBPR1lH4CV8@public.gmane.org>
Signed-off-by: Gregory CLEMENT <gregory.clement-wi1+55ScJUtKEb57/3fJTNBPR1lH4CV8@public.gmane.org>
---
drivers/mmc/host/sdhci.c | 3 ++-
drivers/mmc/host/sdhci.h | 1 +
2 files changed, 3 insertions(+), 1 deletion(-)
diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c
index 48055666c655..d4bb818c52d5 100644
--- a/drivers/mmc/host/sdhci.c
+++ b/drivers/mmc/host/sdhci.c
@@ -1562,7 +1562,7 @@ void sdhci_set_uhs_signaling(struct sdhci_host *host, unsigned timing)
}
EXPORT_SYMBOL_GPL(sdhci_set_uhs_signaling);
-static void sdhci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
+void sdhci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
{
struct sdhci_host *host = mmc_priv(mmc);
unsigned long flags;
@@ -1719,6 +1719,7 @@ static void sdhci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
mmiowb();
spin_unlock_irqrestore(&host->lock, flags);
}
+EXPORT_SYMBOL_GPL(sdhci_set_ios);
static int sdhci_get_cd(struct mmc_host *mmc)
{
diff --git a/drivers/mmc/host/sdhci.h b/drivers/mmc/host/sdhci.h
index c722cd23205c..21dc80b8ae3d 100644
--- a/drivers/mmc/host/sdhci.h
+++ b/drivers/mmc/host/sdhci.h
@@ -686,6 +686,7 @@ void sdhci_set_power(struct sdhci_host *host, unsigned char mode,
void sdhci_set_bus_width(struct sdhci_host *host, int width);
void sdhci_reset(struct sdhci_host *host, u8 mask);
void sdhci_set_uhs_signaling(struct sdhci_host *host, unsigned timing);
+void sdhci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios);
#ifdef CONFIG_PM
extern int sdhci_suspend_host(struct sdhci_host *host);
--
git-series 0.8.10
--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
^ permalink raw reply related
* [PATCH 2/10] mmc: sdhci: Export sdhci_start_signal_voltage_switch() in sdhci.c
From: Gregory CLEMENT @ 2016-10-07 15:22 UTC (permalink / raw)
To: Ulf Hansson, Adrian Hunter, linux-mmc
Cc: Jason Cooper, Andrew Lunn, Sebastian Hesselbarth, Gregory CLEMENT,
Rob Herring, devicetree, Thomas Petazzoni, linux-arm-kernel,
Ziji Hu, Jack(SH) Zhu, Jimmy Xu, Jisheng Zhang, Nadav Haklai,
Ryan Gao, Doug Jones, Shiwu Zhang, Victor Gu, Wei(SOCP) Liu,
Wilson Ding, Xueping Liu, Hilbert Zhang
In-Reply-To: <cover.990e3503c30c18ecf50dd48bce3d52811f03ca22.1475853198.git-series.gregory.clement@free-electrons.com>
From: Ziji Hu <huziji@marvell.com>
Export sdhci_start_signal_voltage_switch() from sdhci.c.
Thus vendor sdhci driver can implement its own signal voltage
switch routine.
Signed-off-by: Hu Ziji <huziji@marvell.com>
Reviewed-by: Gregory CLEMENT <gregory.clement@free-electrons.com>
Signed-off-by: Gregory CLEMENT <gregory.clement@free-electrons.com>
---
drivers/mmc/host/sdhci.c | 5 +++--
drivers/mmc/host/sdhci.h | 2 ++
2 files changed, 5 insertions(+), 2 deletions(-)
diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c
index d4bb818c52d5..2250ea22231f 100644
--- a/drivers/mmc/host/sdhci.c
+++ b/drivers/mmc/host/sdhci.c
@@ -1828,8 +1828,8 @@ static void sdhci_enable_sdio_irq(struct mmc_host *mmc, int enable)
spin_unlock_irqrestore(&host->lock, flags);
}
-static int sdhci_start_signal_voltage_switch(struct mmc_host *mmc,
- struct mmc_ios *ios)
+int sdhci_start_signal_voltage_switch(struct mmc_host *mmc,
+ struct mmc_ios *ios)
{
struct sdhci_host *host = mmc_priv(mmc);
u16 ctrl;
@@ -1921,6 +1921,7 @@ static int sdhci_start_signal_voltage_switch(struct mmc_host *mmc,
return 0;
}
}
+EXPORT_SYMBOL_GPL(sdhci_start_signal_voltage_switch);
static int sdhci_card_busy(struct mmc_host *mmc)
{
diff --git a/drivers/mmc/host/sdhci.h b/drivers/mmc/host/sdhci.h
index 21dc80b8ae3d..c38ab65b9a97 100644
--- a/drivers/mmc/host/sdhci.h
+++ b/drivers/mmc/host/sdhci.h
@@ -687,6 +687,8 @@ void sdhci_set_bus_width(struct sdhci_host *host, int width);
void sdhci_reset(struct sdhci_host *host, u8 mask);
void sdhci_set_uhs_signaling(struct sdhci_host *host, unsigned timing);
void sdhci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios);
+int sdhci_start_signal_voltage_switch(struct mmc_host *mmc,
+ struct mmc_ios *ios);
#ifdef CONFIG_PM
extern int sdhci_suspend_host(struct sdhci_host *host);
--
git-series 0.8.10
^ permalink raw reply related
* [PATCH 3/10] mmc: sdhci: Export sdhci_execute_tuning() in sdhci.c
From: Gregory CLEMENT @ 2016-10-07 15:22 UTC (permalink / raw)
To: Ulf Hansson, Adrian Hunter, linux-mmc
Cc: Hilbert Zhang, Andrew Lunn, Romain Perier, Liuliu Zhao, Peng Zhu,
Nadav Haklai, Ziji Hu, Victor Gu, Doug Jones, Jisheng Zhang,
Yehuda Yitschak, Marcin Wojtas, Xueping Liu, Shiwu Zhang, Yu Cao,
Sebastian Hesselbarth, devicetree, Jason Cooper, Hanna Hawa,
Kostya Porotchkin, Rob Herring, Ryan Gao, Gregory CLEMENT,
Wei(SOCP) Liu, linux-arm-kernel
In-Reply-To: <cover.990e3503c30c18ecf50dd48bce3d52811f03ca22.1475853198.git-series.gregory.clement@free-electrons.com>
From: Ziji Hu <huziji@marvell.com>
Export sdhci_execute_tuning() from sdhci.c.
Thus vendor sdhci driver can execute its own tuning process.
Signed-off-by: Hu Ziji <huziji@marvell.com>
Reviewed-by: Gregory CLEMENT <gregory.clement@free-electrons.com>
Signed-off-by: Gregory CLEMENT <gregory.clement@free-electrons.com>
---
drivers/mmc/host/sdhci.c | 3 ++-
drivers/mmc/host/sdhci.h | 1 +
2 files changed, 3 insertions(+), 1 deletion(-)
diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c
index 2250ea22231f..330119cd5a93 100644
--- a/drivers/mmc/host/sdhci.c
+++ b/drivers/mmc/host/sdhci.c
@@ -1946,7 +1946,7 @@ static int sdhci_prepare_hs400_tuning(struct mmc_host *mmc, struct mmc_ios *ios)
return 0;
}
-static int sdhci_execute_tuning(struct mmc_host *mmc, u32 opcode)
+int sdhci_execute_tuning(struct mmc_host *mmc, u32 opcode)
{
struct sdhci_host *host = mmc_priv(mmc);
u16 ctrl;
@@ -2135,6 +2135,7 @@ static int sdhci_execute_tuning(struct mmc_host *mmc, u32 opcode)
spin_unlock_irqrestore(&host->lock, flags);
return err;
}
+EXPORT_SYMBOL_GPL(sdhci_execute_tuning);
static int sdhci_select_drive_strength(struct mmc_card *card,
unsigned int max_dtr, int host_drv,
diff --git a/drivers/mmc/host/sdhci.h b/drivers/mmc/host/sdhci.h
index c38ab65b9a97..035274ddea54 100644
--- a/drivers/mmc/host/sdhci.h
+++ b/drivers/mmc/host/sdhci.h
@@ -689,6 +689,7 @@ void sdhci_set_uhs_signaling(struct sdhci_host *host, unsigned timing);
void sdhci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios);
int sdhci_start_signal_voltage_switch(struct mmc_host *mmc,
struct mmc_ios *ios);
+int sdhci_execute_tuning(struct mmc_host *mmc, u32 opcode);
#ifdef CONFIG_PM
extern int sdhci_suspend_host(struct sdhci_host *host);
--
git-series 0.8.10
^ permalink raw reply related
* [PATCH 4/10] MAINTAINERS: add entry for Marvell Xenon MMC Host Controller drivers
From: Gregory CLEMENT @ 2016-10-07 15:22 UTC (permalink / raw)
To: Ulf Hansson, Adrian Hunter, linux-mmc
Cc: Jason Cooper, Andrew Lunn, Sebastian Hesselbarth, Gregory CLEMENT,
Rob Herring, devicetree, Thomas Petazzoni, linux-arm-kernel,
Ziji Hu, Jack(SH) Zhu, Jimmy Xu, Jisheng Zhang, Nadav Haklai,
Ryan Gao, Doug Jones, Shiwu Zhang, Victor Gu, Wei(SOCP) Liu,
Wilson Ding, Xueping Liu, Hilbert Zhang
In-Reply-To: <cover.990e3503c30c18ecf50dd48bce3d52811f03ca22.1475853198.git-series.gregory.clement@free-electrons.com>
From: Ziji Hu <huziji@marvell.com>
Add maintainer entry for Marvell Xenon eMMC/SD/SDIO Host
Controller drivers.
Signed-off-by: Hu Ziji <huziji@marvell.com>
Reviewed-by: Gregory CLEMENT <gregory.clement@free-electrons.com>
Signed-off-by: Gregory CLEMENT <gregory.clement@free-electrons.com>
---
MAINTAINERS | 5 +++++
1 file changed, 5 insertions(+), 0 deletions(-)
diff --git a/MAINTAINERS b/MAINTAINERS
index 207eaefd3b97..89adcd57aa25 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -7578,6 +7578,11 @@ M: Nicolas Pitre <nico@fluxnic.net>
S: Odd Fixes
F: drivers/mmc/host/mvsdio.*
+MARVELL XENON MMC/SD/SDIO HOST CONTROLLER DRIVER
+M: Ziji Hu <huziji@marvell.com>
+L: linux-mmc@vger.kernel.org
+S: Supported
+
MATROX FRAMEBUFFER DRIVER
L: linux-fbdev@vger.kernel.org
S: Orphan
--
git-series 0.8.10
^ permalink raw reply related
* [PATCH 5/10] dt: bindings: Add bindings for Marvell Xenon SD Host Controller
From: Gregory CLEMENT @ 2016-10-07 15:22 UTC (permalink / raw)
To: Ulf Hansson, Adrian Hunter, linux-mmc-u79uwXL29TY76Z2rM5mHXA
Cc: Jason Cooper, Andrew Lunn, Sebastian Hesselbarth, Gregory CLEMENT,
Rob Herring, devicetree-u79uwXL29TY76Z2rM5mHXA, Thomas Petazzoni,
linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r, Ziji Hu,
Jack(SH) Zhu, Jimmy Xu, Jisheng Zhang, Nadav Haklai, Ryan Gao,
Doug Jones, Shiwu Zhang, Victor Gu, Wei(SOCP) Liu, Wilson Ding,
Xueping Liu, Hilbert Zhang
In-Reply-To: <cover.990e3503c30c18ecf50dd48bce3d52811f03ca22.1475853198.git-series.gregory.clement-wi1+55ScJUtKEb57/3fJTNBPR1lH4CV8@public.gmane.org>
From: Ziji Hu <huziji-eYqpPyKDWXRBDgjK7y7TUQ@public.gmane.org>
Marvell Xenon SDHC can support eMMC/SD/SDIO.
Add Xenon-specific properties.
Also add properties for Xenon PHY setting.
Signed-off-by: Hu Ziji <huziji-eYqpPyKDWXRBDgjK7y7TUQ@public.gmane.org>
Reviewed-by: Gregory CLEMENT <gregory.clement-wi1+55ScJUtKEb57/3fJTNBPR1lH4CV8@public.gmane.org>
Signed-off-by: Gregory CLEMENT <gregory.clement-wi1+55ScJUtKEb57/3fJTNBPR1lH4CV8@public.gmane.org>
---
Documentation/devicetree/bindings/mmc/marvell,sdhci-xenon.txt | 164 +++++++-
MAINTAINERS | 1 +-
2 files changed, 165 insertions(+), 0 deletions(-)
create mode 100644 Documentation/devicetree/bindings/mmc/marvell,sdhci-xenon.txt
diff --git a/Documentation/devicetree/bindings/mmc/marvell,sdhci-xenon.txt b/Documentation/devicetree/bindings/mmc/marvell,sdhci-xenon.txt
new file mode 100644
index 000000000000..8b25ad28ebbd
--- /dev/null
+++ b/Documentation/devicetree/bindings/mmc/marvell,sdhci-xenon.txt
@@ -0,0 +1,164 @@
+Marvell's Xenon SDHCI Controller device tree bindings
+This file documents differences between the core mmc properties
+described by mmc.txt and the properties used by the Xenon implementation.
+
+A single Xenon IP can support multiple slots.
+Each slot acts as an independent SDHC. It owns independent resources, such
+as register sets clock and PHY.
+Each slot should have an independent device tree node.
+
+Required Properties:
+- compatible: should be "marvell,sdhci-xenon" or "marvell,armada-3700-sdhci".
+
+- Input Clock Name
+ Some SOCs require additional clock for AXI bus.
+ The input clock for Xenon IP core should be named as "core".
+ The optional AXI clock should be named as "axi".
+ - clocks = <&core_clk>, <&axi_clock>;
+ - clock-names = "core", "axi";
+
+- Register Set Size
+ Different Xenon SDHC release has different register set size.
+ The specific size should also refer to the SOC implementation.
+
+Optional Properties:
+- Slot Index
+ A single Xenon IP can support multiple slots.
+ During initialization, each slot should set corresponding setting bit in
+ some Xenon-specific registers. The corresponding bit is determined by
+ this property.
+ - xenon,slotno = <slot_index>;
+ If this property is not provided, Xenon IP should contain only one slot
+ and the slot index will be 0x0 by default.
+
+- PHY Type
+ Xenon support mutilple types of PHYs.
+ To select eMMC 5.1 PHY, set:
+ - xenon,phy-type = "emmc 5.1 phy"
+ eMMC 5.1 PHY is the default choice if this property is not provided.
+ To select eMMC 5.0 PHY, set:
+ - xenon,phy-type = "emmc 5.0 phy"
+ To select SDH PHY, set:
+ - xenon,phy-type = "sdh phy"
+ Please note that eMMC PHY is a general PHY for eMMC/SD/SDIO, other than for
+ eMMC only.
+
+- Customized eMMC PHY Parameters
+ Some boards require different values of some specific eMMC PHY parameters.
+ Some SOCs also require specific workaround to set eMMC PHY.
+ These properties enable diverse boards to customize the eMMC PHY.
+ The supported eMMC PHY parameters are listed in below. All those properties
+ are only available for eMMC PHY 5.1 and eMMC PHY 5.0.
+ ZNR
+ valid range = [0:0x1F].
+ ZNR is set as 0xF by default if this property is not provided.
+ - xenon,phy-znr = <value>;
+
+ ZPR
+ valid range = [0:0x1F].
+ ZPR is set as 0xF by default if this property is not provided.
+ - xenon,phy-zpr = <value>;
+
+ Number of successful tuning times
+ Set the number of required consecutive successful sampling points used to
+ identify a valid sampling window, in tuning process.
+ Valid range = [1:7]. Set as 0x4 by default if this property is not provided.
+ - xenon,phy-nr-tun-times = <nr_times>;
+
+ Divider for TUN_STEP
+ Set the divider for calculating TUN_STEP.
+ Set as 64 by default if this property is not provided.
+ - xenon,phy-tun-step-divider = <divider>;
+
+ Force PHY into slow mode.
+ Only available when bus frequency lower than 50MHz in SDR mde.
+ Disabled by default. Please do not enable it unless it is necessary.
+ - xenon,phy-slow-mode;
+
+- Mask Conflict Error Report
+ Disable Conflict Error alert on some SOC. Disabled by default.
+ xenon,mask-conflict-err;
+
+- Re-tuning Counter
+ Xenon SDHC SOC usually doesn't provide re-tuning counter in
+ Capabilities Register 3 Bit[11:8].
+ This property provides the re-tuning counter.
+ xenon,tuning-count = <count>;
+ If this property is not set, default re-tuning counter will
+ be set as 0x9 in driver.
+
+- SOC PHY PAD Voltage Control register
+ Some SOCs have SOC PHY PAD Voltage Control register outside Xenon IP.
+ This register sets SOC PHY PAD Voltage to keep aligh with Vccq.
+ Two properties provide information of this control register.
+ These two properties are only valid when "marvell,armada-3700-sdhci"
+ is selected. Both of them must be provided when "marvell,armada-3700-sdhci"
+ is selected.
+ - xenon,pad-type
+ Two types: "sd" and "fixed-1-8v".
+ If "sd" is slected, SOC PHY PAD is set as 3.3V at the beginning and is
+ switched to 1.8V when SD in UHS-I.
+ If "fixed-1-8v" is slected, SOC PHY PAD is fixed 1.8V, such as for eMMC.
+ - reg
+ Physical address and size of SOC PHY PAD register.
+ Append after Xenon SDHC register space, as a second register field.
+
+ Please follow the examples with compatible "marvell,armada-3700-sdhci"
+ in below.
+
+Example:
+- For eMMC slot:
+
+ sdhci@aa0000 {
+ compatible = "marvell,sdhci-xenon";
+ reg = <0xaa0000 0x1000>;
+ interrupts = <GIC_SPI 13 IRQ_TYPE_LEVEL_HIGH>
+ clocks = <&emmcclk>;
+ clock-names = "core";
+ xenon,slotno = <0>;
+ xenon,phy-type = "emmc 5.1 phy";
+ bus-width = <8>;
+ tuning-count = <11>;
+ };
+
+- For SD/SDIO slot:
+
+ sdhci@ab0000 {
+ compatible = "marvell,sdhci-xenon";
+ reg = <0xab0000 0x1000>;
+ interrupts = <GIC_SPI 55 IRQ_TYPE_LEVEL_HIGH>
+ vqmmc-supply = <&sd_regulator>;
+ clocks = <&sdclk>;
+ clock-names = "core";
+ bus-width = <4>;
+ tuning-count = <9>;
+ };
+
+- For eMMC slot with compatible "marvell,armada-3700-sdhci":
+
+ sdhci@aa0000 {
+ compatible = "marvell,armada-3700-sdhci";
+ reg = <0xaa0000 0x1000>,
+ <phy_addr 0x4>;
+ interrupts = <GIC_SPI 13 IRQ_TYPE_LEVEL_HIGH>
+ clocks = <&emmcclk>;
+ clock-names = "core";
+ bus-width = <8>;
+
+ xenon,pad-type = "fixed-1-8v";
+ };
+
+- For SD/SDIO slot with compatible "marvell,armada-3700-sdhci":
+
+ sdhci@ab0000 {
+ compatible = "marvell,armada-3700-sdhci";
+ reg = <0xab0000 0x1000>,
+ <phy_addr 0x4>;
+ interrupts = <GIC_SPI 55 IRQ_TYPE_LEVEL_HIGH>
+ vqmmc-supply = <&sd_regulator>;
+ clocks = <&sdclk>;
+ clock-names = "core";
+ bus-width = <4>;
+
+ xenon,pad-type = "sd";
+ };
diff --git a/MAINTAINERS b/MAINTAINERS
index 89adcd57aa25..4aa0eac9bfc7 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -7582,6 +7582,7 @@ MARVELL XENON MMC/SD/SDIO HOST CONTROLLER DRIVER
M: Ziji Hu <huziji-eYqpPyKDWXRBDgjK7y7TUQ@public.gmane.org>
L: linux-mmc-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
S: Supported
+F: Documentation/devicetree/bindings/mmc/marvell,sdhci-xenon.txt
MATROX FRAMEBUFFER DRIVER
L: linux-fbdev-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
--
git-series 0.8.10
--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
^ permalink raw reply related
* [PATCH 6/10] mmc: sdhci-xenon: Add Marvell Xenon SDHC core functionality
From: Gregory CLEMENT @ 2016-10-07 15:22 UTC (permalink / raw)
To: Ulf Hansson, Adrian Hunter, linux-mmc-u79uwXL29TY76Z2rM5mHXA
Cc: Jason Cooper, Andrew Lunn, Sebastian Hesselbarth, Gregory CLEMENT,
Rob Herring, devicetree-u79uwXL29TY76Z2rM5mHXA, Thomas Petazzoni,
linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r, Ziji Hu,
Jack(SH) Zhu, Jimmy Xu, Jisheng Zhang, Nadav Haklai, Ryan Gao,
Doug Jones, Shiwu Zhang, Victor Gu, Wei(SOCP) Liu, Wilson Ding,
Xueping Liu, Hilbert Zhang
In-Reply-To: <cover.990e3503c30c18ecf50dd48bce3d52811f03ca22.1475853198.git-series.gregory.clement-wi1+55ScJUtKEb57/3fJTNBPR1lH4CV8@public.gmane.org>
From: Ziji Hu <huziji-eYqpPyKDWXRBDgjK7y7TUQ@public.gmane.org>
Add Xenon eMMC/SD/SDIO host controller core functionality.
Add Xenon specific intialization process.
Add Xenon specific mmc_host_ops APIs.
Add Xenon specific register definitions.
Add CONFIG_MMC_SDHCI_XENON support in drivers/mmc/host/Kconfig.
Marvell Xenon SDHC conforms to SD Physical Layer Specification
Version 3.01 and is designed according to the guidelines provided
in the SD Host Controller Standard Specification Version 3.00.
Signed-off-by: Hu Ziji <huziji-eYqpPyKDWXRBDgjK7y7TUQ@public.gmane.org>
Reviewed-by: Gregory CLEMENT <gregory.clement-wi1+55ScJUtKEb57/3fJTNBPR1lH4CV8@public.gmane.org>
Signed-off-by: Gregory CLEMENT <gregory.clement-wi1+55ScJUtKEb57/3fJTNBPR1lH4CV8@public.gmane.org>
---
MAINTAINERS | 1 +-
drivers/mmc/host/Kconfig | 9 +-
drivers/mmc/host/Makefile | 3 +-
drivers/mmc/host/sdhci-xenon.c | 599 ++++++++++++++++++++++++++++++++++-
drivers/mmc/host/sdhci-xenon.h | 134 ++++++++-
5 files changed, 746 insertions(+), 0 deletions(-)
create mode 100644 drivers/mmc/host/sdhci-xenon.c
create mode 100644 drivers/mmc/host/sdhci-xenon.h
diff --git a/MAINTAINERS b/MAINTAINERS
index 4aa0eac9bfc7..859420e5dfd3 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -7582,6 +7582,7 @@ MARVELL XENON MMC/SD/SDIO HOST CONTROLLER DRIVER
M: Ziji Hu <huziji-eYqpPyKDWXRBDgjK7y7TUQ@public.gmane.org>
L: linux-mmc-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
S: Supported
+F: drivers/mmc/host/sdhci-xenon.*
F: Documentation/devicetree/bindings/mmc/marvell,sdhci-xenon.txt
MATROX FRAMEBUFFER DRIVER
diff --git a/drivers/mmc/host/Kconfig b/drivers/mmc/host/Kconfig
index 5274f503a39a..85a53623526a 100644
--- a/drivers/mmc/host/Kconfig
+++ b/drivers/mmc/host/Kconfig
@@ -798,3 +798,12 @@ config MMC_SDHCI_BRCMSTB
Broadcom STB SoCs.
If unsure, say Y.
+
+config MMC_SDHCI_XENON
+ tristate "Marvell Xenon eMMC/SD/SDIO SDHCI driver"
+ depends on MMC_SDHCI && MMC_SDHCI_PLTFM
+ help
+ This selects Marvell Xenon eMMC/SD/SDIO SDHCI.
+ If you have a machine with integrated Marvell Xenon SDHC IP,
+ say Y or M here.
+ If unsure, say N.
diff --git a/drivers/mmc/host/Makefile b/drivers/mmc/host/Makefile
index e2bdaaf43184..75eaf743486c 100644
--- a/drivers/mmc/host/Makefile
+++ b/drivers/mmc/host/Makefile
@@ -80,3 +80,6 @@ obj-$(CONFIG_MMC_SDHCI_BRCMSTB) += sdhci-brcmstb.o
ifeq ($(CONFIG_CB710_DEBUG),y)
CFLAGS-cb710-mmc += -DDEBUG
endif
+
+obj-$(CONFIG_MMC_SDHCI_XENON) += sdhci-xenon-driver.o
+sdhci-xenon-driver-y += sdhci-xenon.o
diff --git a/drivers/mmc/host/sdhci-xenon.c b/drivers/mmc/host/sdhci-xenon.c
new file mode 100644
index 000000000000..03ba183494d3
--- /dev/null
+++ b/drivers/mmc/host/sdhci-xenon.c
@@ -0,0 +1,599 @@
+/*
+ * Driver for Marvell SOC Platform Group Xenon SDHC as a platform device
+ *
+ * Copyright (C) 2016 Marvell, All Rights Reserved.
+ *
+ * Author: Hu Ziji <huziji-eYqpPyKDWXRBDgjK7y7TUQ@public.gmane.org>
+ * 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.
+ *
+ * Inspired by Jisheng Zhang <jszhang-eYqpPyKDWXRBDgjK7y7TUQ@public.gmane.org>
+ * Special thanks to Video BG4 project team.
+ */
+
+#include <linux/delay.h>
+#include <linux/err.h>
+#include <linux/io.h>
+#include <linux/mmc/host.h>
+#include <linux/mmc/mmc.h>
+#include <linux/mmc/sdio.h>
+#include <linux/mmc/card.h>
+#include <linux/mmc/host.h>
+#include <linux/module.h>
+#include <linux/of.h>
+
+#include "sdhci-pltfm.h"
+#include "sdhci.h"
+#include "sdhci-xenon.h"
+
+/* Set SDCLK-off-while-idle */
+static void xenon_set_sdclk_off_idle(struct sdhci_host *host,
+ unsigned char slot_idx, bool enable)
+{
+ u32 reg;
+ u32 mask;
+
+ reg = sdhci_readl(host, SDHC_SYS_OP_CTRL);
+ /* Get the bit shift basing on the slot index */
+ mask = (0x1 << (SDCLK_IDLEOFF_ENABLE_SHIFT + slot_idx));
+ if (enable)
+ reg |= mask;
+ else
+ reg &= ~mask;
+
+ sdhci_writel(host, reg, SDHC_SYS_OP_CTRL);
+}
+
+/* Enable/Disable the Auto Clock Gating function */
+static void xenon_set_acg(struct sdhci_host *host, bool enable)
+{
+ u32 reg;
+
+ reg = sdhci_readl(host, SDHC_SYS_OP_CTRL);
+ if (enable)
+ reg &= ~AUTO_CLKGATE_DISABLE_MASK;
+ else
+ reg |= AUTO_CLKGATE_DISABLE_MASK;
+ sdhci_writel(host, reg, SDHC_SYS_OP_CTRL);
+}
+
+/* Enable this slot */
+static void xenon_enable_slot(struct sdhci_host *host,
+ unsigned char slot_idx)
+{
+ u32 reg;
+
+ reg = sdhci_readl(host, SDHC_SYS_OP_CTRL);
+ reg |= (BIT(slot_idx) << SLOT_ENABLE_SHIFT);
+ sdhci_writel(host, reg, SDHC_SYS_OP_CTRL);
+
+ /*
+ * Manually set the flag which all the slots require,
+ * including SD, eMMC, SDIO
+ */
+ host->mmc->caps |= MMC_CAP_WAIT_WHILE_BUSY;
+}
+
+/* Disable this slot */
+static void xenon_disable_slot(struct sdhci_host *host,
+ unsigned char slot_idx)
+{
+ u32 reg;
+
+ reg = sdhci_readl(host, SDHC_SYS_OP_CTRL);
+ reg &= ~(BIT(slot_idx) << SLOT_ENABLE_SHIFT);
+ sdhci_writel(host, reg, SDHC_SYS_OP_CTRL);
+}
+
+/* Enable Parallel Transfer Mode */
+static void xenon_enable_slot_parallel_tran(struct sdhci_host *host,
+ unsigned char slot_idx)
+{
+ u32 reg;
+
+ reg = sdhci_readl(host, SDHC_SYS_EXT_OP_CTRL);
+ reg |= BIT(slot_idx);
+ sdhci_writel(host, reg, SDHC_SYS_EXT_OP_CTRL);
+}
+
+static void xenon_slot_tuning_setup(struct sdhci_host *host)
+{
+ struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
+ struct sdhci_xenon_priv *priv = sdhci_pltfm_priv(pltfm_host);
+ u32 reg;
+
+ /* Disable the Re-Tuning Request functionality */
+ reg = sdhci_readl(host, SDHC_SLOT_RETUNING_REQ_CTRL);
+ reg &= ~RETUNING_COMPATIBLE;
+ sdhci_writel(host, reg, SDHC_SLOT_RETUNING_REQ_CTRL);
+
+ /* Disbale the Re-tuning Event Signal Enable */
+ reg = sdhci_readl(host, SDHCI_SIGNAL_ENABLE);
+ reg &= ~SDHCI_INT_RETUNE;
+ sdhci_writel(host, reg, SDHCI_SIGNAL_ENABLE);
+
+ /* Force to use Tuning Mode 1 */
+ host->tuning_mode = SDHCI_TUNING_MODE_1;
+ /* Set re-tuning period */
+ host->tuning_count = 1 << (priv->tuning_count - 1);
+}
+
+/*
+ * Operations inside struct sdhci_ops
+ */
+/* Recover the Register Setting cleared during SOFTWARE_RESET_ALL */
+static void sdhci_xenon_reset_exit(struct sdhci_host *host,
+ unsigned char slot_idx, u8 mask)
+{
+ /* Only SOFTWARE RESET ALL will clear the register setting */
+ if (!(mask & SDHCI_RESET_ALL))
+ return;
+
+ /* Disable tuning request and auto-retuing again */
+ xenon_slot_tuning_setup(host);
+
+ xenon_set_acg(host, true);
+
+ xenon_set_sdclk_off_idle(host, slot_idx, false);
+}
+
+static void sdhci_xenon_reset(struct sdhci_host *host, u8 mask)
+{
+ struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
+ struct sdhci_xenon_priv *priv = sdhci_pltfm_priv(pltfm_host);
+
+ sdhci_reset(host, mask);
+ sdhci_xenon_reset_exit(host, priv->slot_idx, mask);
+}
+
+/*
+ * Xenon defines different values for HS200 and SDR104
+ * in Host_Control_2
+ */
+static void xenon_set_uhs_signaling(struct sdhci_host *host,
+ unsigned int timing)
+{
+ u16 ctrl_2;
+
+ ctrl_2 = sdhci_readw(host, SDHCI_HOST_CONTROL2);
+ /* Select Bus Speed Mode for host */
+ ctrl_2 &= ~SDHCI_CTRL_UHS_MASK;
+ if (timing == MMC_TIMING_MMC_HS200)
+ ctrl_2 |= XENON_SDHCI_CTRL_HS200;
+ else if (timing == MMC_TIMING_UHS_SDR104)
+ ctrl_2 |= SDHCI_CTRL_UHS_SDR104;
+ else if (timing == MMC_TIMING_UHS_SDR12)
+ ctrl_2 |= SDHCI_CTRL_UHS_SDR12;
+ else if (timing == MMC_TIMING_UHS_SDR25)
+ ctrl_2 |= SDHCI_CTRL_UHS_SDR25;
+ else if (timing == MMC_TIMING_UHS_SDR50)
+ ctrl_2 |= SDHCI_CTRL_UHS_SDR50;
+ else if ((timing == MMC_TIMING_UHS_DDR50) ||
+ (timing == MMC_TIMING_MMC_DDR52))
+ ctrl_2 |= SDHCI_CTRL_UHS_DDR50;
+ else if (timing == MMC_TIMING_MMC_HS400)
+ ctrl_2 |= XENON_SDHCI_CTRL_HS400;
+ sdhci_writew(host, ctrl_2, SDHCI_HOST_CONTROL2);
+}
+
+static const struct sdhci_ops sdhci_xenon_ops = {
+ .set_clock = sdhci_set_clock,
+ .set_bus_width = sdhci_set_bus_width,
+ .reset = sdhci_xenon_reset,
+ .set_uhs_signaling = xenon_set_uhs_signaling,
+ .get_max_clock = sdhci_pltfm_clk_get_max_clock,
+};
+
+static const struct sdhci_pltfm_data sdhci_xenon_pdata = {
+ .ops = &sdhci_xenon_ops,
+ .quirks = SDHCI_QUIRK_NO_ENDATTR_IN_NOPDESC |
+ SDHCI_QUIRK_MULTIBLOCK_READ_ACMD12 |
+ SDHCI_QUIRK_NO_SIMULT_VDD_AND_POWER |
+ SDHCI_QUIRK_CAP_CLOCK_BASE_BROKEN,
+};
+
+/*
+ * Xenon Specific Operations in mmc_host_ops
+ */
+static void xenon_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
+{
+ struct sdhci_host *host = mmc_priv(mmc);
+ struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
+ struct sdhci_xenon_priv *priv = sdhci_pltfm_priv(pltfm_host);
+ unsigned long flags;
+
+ /*
+ * HS400/HS200/eMMC HS doesn't have Preset Value register.
+ * However, sdhci_set_ios will read HS400/HS200 Preset register.
+ * Disable Preset Value register for HS400/HS200.
+ * eMMC HS with preset_enabled set will trigger a bug in
+ * get_preset_value().
+ */
+ spin_lock_irqsave(&host->lock, flags);
+ if ((ios->timing == MMC_TIMING_MMC_HS400) ||
+ (ios->timing == MMC_TIMING_MMC_HS200) ||
+ (ios->timing == MMC_TIMING_MMC_HS)) {
+ host->preset_enabled = false;
+ host->quirks2 |= SDHCI_QUIRK2_PRESET_VALUE_BROKEN;
+ } else {
+ host->quirks2 &= ~SDHCI_QUIRK2_PRESET_VALUE_BROKEN;
+ }
+ spin_unlock_irqrestore(&host->lock, flags);
+
+ sdhci_set_ios(mmc, ios);
+
+ if (host->clock > DEFAULT_SDCLK_FREQ) {
+ spin_lock_irqsave(&host->lock, flags);
+ xenon_set_sdclk_off_idle(host, priv->slot_idx, true);
+ spin_unlock_irqrestore(&host->lock, flags);
+ }
+}
+
+static int __emmc_signal_voltage_switch(struct mmc_host *mmc,
+ const unsigned char signal_voltage)
+{
+ u32 ctrl;
+ unsigned char voltage_code;
+ struct sdhci_host *host = mmc_priv(mmc);
+
+ if (signal_voltage == MMC_SIGNAL_VOLTAGE_330)
+ voltage_code = EMMC_VCCQ_3_3V;
+ else if (signal_voltage == MMC_SIGNAL_VOLTAGE_180)
+ voltage_code = EMMC_VCCQ_1_8V;
+ else
+ return -EINVAL;
+
+ /*
+ * This host is for eMMC, XENON self-defined
+ * eMMC slot control register should be accessed
+ * instead of Host Control 2
+ */
+ ctrl = sdhci_readl(host, SDHC_SLOT_EMMC_CTRL);
+ ctrl &= ~EMMC_VCCQ_MASK;
+ ctrl |= voltage_code;
+ sdhci_writel(host, ctrl, SDHC_SLOT_EMMC_CTRL);
+
+ /* There is no standard to determine this waiting period */
+ usleep_range(1000, 2000);
+
+ /* Check whether io voltage switch is done */
+ ctrl = sdhci_readl(host, SDHC_SLOT_EMMC_CTRL);
+ ctrl &= EMMC_VCCQ_MASK;
+ /*
+ * This bit is set only when regulator feeds back the voltage switch
+ * results to Xenon SDHC.
+ * However, in actaul implementation, regulator might not provide
+ * this feedback.
+ * Thus we shall not rely on this bit to determine if switch failed.
+ * If the bit is not set, just throw a message.
+ * Besides, error code should not be returned.
+ */
+ if (ctrl != voltage_code)
+ dev_info(mmc_dev(mmc), "fail to detect eMMC signal voltage stable\n");
+ return 0;
+}
+
+static int xenon_emmc_signal_voltage_switch(struct mmc_host *mmc,
+ struct mmc_ios *ios)
+{
+ unsigned char voltage = ios->signal_voltage;
+
+ if ((voltage == MMC_SIGNAL_VOLTAGE_330) ||
+ (voltage == MMC_SIGNAL_VOLTAGE_180))
+ return __emmc_signal_voltage_switch(mmc, voltage);
+
+ dev_err(mmc_dev(mmc), "Unsupported signal voltage: %d\n",
+ voltage);
+ return -EINVAL;
+}
+
+static int xenon_start_signal_voltage_switch(struct mmc_host *mmc,
+ struct mmc_ios *ios)
+{
+ struct sdhci_host *host = mmc_priv(mmc);
+ struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
+ struct sdhci_xenon_priv *priv = sdhci_pltfm_priv(pltfm_host);
+
+ /*
+ * Before SD/SDIO set signal voltage, SD bus clock should be
+ * disabled. However, sdhci_set_clock will also disable the Internal
+ * clock in mmc_set_signal_voltage().
+ * If Internal clock is disabled, the 3.3V/1.8V bit can not be updated.
+ * Thus here manually enable internal clock.
+ *
+ * After switch completes, it is unnecessary to disable internal clock,
+ * since keeping internal clock active obeys SD spec.
+ */
+ enable_xenon_internal_clk(host);
+
+ if (priv->card_candidate) {
+ if (mmc_card_mmc(priv->card_candidate))
+ return xenon_emmc_signal_voltage_switch(mmc, ios);
+ }
+
+ return sdhci_start_signal_voltage_switch(mmc, ios);
+}
+
+/*
+ * After determining which slot is used for SDIO,
+ * some additional task is required.
+ */
+static void xenon_init_card(struct mmc_host *mmc, struct mmc_card *card)
+{
+ struct sdhci_host *host = mmc_priv(mmc);
+ u32 reg;
+ u8 slot_idx;
+ struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
+ struct sdhci_xenon_priv *priv = sdhci_pltfm_priv(pltfm_host);
+
+ /* Link the card for delay adjustment */
+ priv->card_candidate = card;
+ /* Set tuning functionality of this slot */
+ xenon_slot_tuning_setup(host);
+
+ slot_idx = priv->slot_idx;
+ if (!mmc_card_sdio(card)) {
+ /* Re-enable the Auto-CMD12 cap flag. */
+ host->quirks |= SDHCI_QUIRK_MULTIBLOCK_READ_ACMD12;
+ host->flags |= SDHCI_AUTO_CMD12;
+
+ /* Clear SDIO Card Inserted indication */
+ reg = sdhci_readl(host, SDHC_SYS_CFG_INFO);
+ reg &= ~(1 << (slot_idx + SLOT_TYPE_SDIO_SHIFT));
+ sdhci_writel(host, reg, SDHC_SYS_CFG_INFO);
+
+ if (mmc_card_mmc(card)) {
+ mmc->caps |= MMC_CAP_NONREMOVABLE;
+ if (!(host->quirks2 & SDHCI_QUIRK2_NO_1_8_V))
+ mmc->caps |= MMC_CAP_1_8V_DDR;
+ /*
+ * Force to clear BUS_TEST to
+ * skip bus_test_pre and bus_test_post
+ */
+ mmc->caps &= ~MMC_CAP_BUS_WIDTH_TEST;
+ mmc->caps2 |= MMC_CAP2_HC_ERASE_SZ |
+ MMC_CAP2_PACKED_CMD;
+ if (mmc->caps & MMC_CAP_8_BIT_DATA)
+ mmc->caps2 |= MMC_CAP2_HS400_1_8V;
+ }
+ } else {
+ /*
+ * Delete the Auto-CMD12 cap flag.
+ * Otherwise, when sending multi-block CMD53,
+ * Driver will set Transfer Mode Register to enable Auto CMD12.
+ * However, SDIO device cannot recognize CMD12.
+ * Thus SDHC will time-out for waiting for CMD12 response.
+ */
+ host->quirks &= ~SDHCI_QUIRK_MULTIBLOCK_READ_ACMD12;
+ host->flags &= ~SDHCI_AUTO_CMD12;
+
+ /*
+ * Set SDIO Card Inserted indication
+ * to inform that the current slot is for SDIO
+ */
+ reg = sdhci_readl(host, SDHC_SYS_CFG_INFO);
+ reg |= (1 << (slot_idx + SLOT_TYPE_SDIO_SHIFT));
+ sdhci_writel(host, reg, SDHC_SYS_CFG_INFO);
+ }
+}
+
+static int xenon_execute_tuning(struct mmc_host *mmc, u32 opcode)
+{
+ struct sdhci_host *host = mmc_priv(mmc);
+
+ if (host->timing == MMC_TIMING_UHS_DDR50)
+ return 0;
+
+ return sdhci_execute_tuning(mmc, opcode);
+}
+
+static void xenon_replace_mmc_host_ops(struct sdhci_host *host)
+{
+ host->mmc_host_ops.set_ios = xenon_set_ios;
+ host->mmc_host_ops.start_signal_voltage_switch =
+ xenon_start_signal_voltage_switch;
+ host->mmc_host_ops.init_card = xenon_init_card;
+ host->mmc_host_ops.execute_tuning = xenon_execute_tuning;
+}
+
+static int xenon_probe_dt(struct platform_device *pdev)
+{
+ struct device_node *np = pdev->dev.of_node;
+ struct sdhci_host *host = platform_get_drvdata(pdev);
+ struct mmc_host *mmc = host->mmc;
+ struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
+ struct sdhci_xenon_priv *priv = sdhci_pltfm_priv(pltfm_host);
+ int err;
+ u32 slot_idx, nr_slot;
+ u32 tuning_count;
+ u32 reg;
+
+ /* Standard MMC property */
+ err = mmc_of_parse(mmc);
+ if (err)
+ return err;
+
+ /* Standard SDHCI property */
+ sdhci_get_of_property(pdev);
+
+ /*
+ * Xenon Specific property:
+ * slotno: the index of slot. Refer to SDHC_SYS_CFG_INFO register
+ * tuning-count: the interval between re-tuning
+ * PHY type: "sdhc phy", "emmc phy 5.0" or "emmc phy 5.1"
+ */
+ if (!of_property_read_u32(np, "xenon,slotno", &slot_idx)) {
+ nr_slot = sdhci_readl(host, SDHC_SYS_CFG_INFO);
+ nr_slot &= NR_SUPPORTED_SLOT_MASK;
+ if (unlikely(slot_idx > nr_slot)) {
+ dev_err(mmc_dev(mmc), "Slot Index %d exceeds Number of slots %d\n",
+ slot_idx, nr_slot);
+ return -EINVAL;
+ }
+ } else {
+ priv->slot_idx = 0x0;
+ }
+
+ if (!of_property_read_u32(np, "xenon,tuning-count", &tuning_count)) {
+ if (unlikely(tuning_count >= TMR_RETUN_NO_PRESENT)) {
+ dev_err(mmc_dev(mmc), "Wrong Re-tuning Count. Set default value %d\n",
+ DEF_TUNING_COUNT);
+ tuning_count = DEF_TUNING_COUNT;
+ }
+ } else {
+ priv->tuning_count = DEF_TUNING_COUNT;
+ }
+
+ if (of_property_read_bool(np, "xenon,mask-conflict-err")) {
+ reg = sdhci_readl(host, SDHC_SYS_EXT_OP_CTRL);
+ reg |= MASK_CMD_CONFLICT_ERROR;
+ sdhci_writel(host, reg, SDHC_SYS_EXT_OP_CTRL);
+ }
+
+ return err;
+}
+
+static int xenon_slot_probe(struct sdhci_host *host)
+{
+ struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
+ struct sdhci_xenon_priv *priv = sdhci_pltfm_priv(pltfm_host);
+ u8 slot_idx = priv->slot_idx;
+
+ /* Enable slot */
+ xenon_enable_slot(host, slot_idx);
+
+ /* Enable ACG */
+ xenon_set_acg(host, true);
+
+ /* Enable Parallel Transfer Mode */
+ xenon_enable_slot_parallel_tran(host, slot_idx);
+
+ priv->timing = MMC_TIMING_FAKE;
+ priv->clock = 0;
+
+ return 0;
+}
+
+static void xenon_slot_remove(struct sdhci_host *host)
+{
+ struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
+ struct sdhci_xenon_priv *priv = sdhci_pltfm_priv(pltfm_host);
+ u8 slot_idx = priv->slot_idx;
+
+ /* disable slot */
+ xenon_disable_slot(host, slot_idx);
+}
+
+static int sdhci_xenon_probe(struct platform_device *pdev)
+{
+ struct sdhci_pltfm_host *pltfm_host;
+ struct sdhci_host *host;
+ struct clk *clk, *axi_clk;
+ struct sdhci_xenon_priv *priv;
+ int err;
+
+ host = sdhci_pltfm_init(pdev, &sdhci_xenon_pdata,
+ sizeof(struct sdhci_xenon_priv));
+ if (IS_ERR(host))
+ return PTR_ERR(host);
+
+ pltfm_host = sdhci_priv(host);
+ priv = sdhci_pltfm_priv(pltfm_host);
+
+ xenon_set_acg(host, false);
+
+ /*
+ * Link Xenon specific mmc_host_ops function,
+ * to replace standard ones in sdhci_ops.
+ */
+ xenon_replace_mmc_host_ops(host);
+
+ clk = devm_clk_get(&pdev->dev, "core");
+ if (IS_ERR(clk)) {
+ dev_err(&pdev->dev, "Failed to setup input clk.\n");
+ err = PTR_ERR(clk);
+ goto free_pltfm;
+ }
+ clk_prepare_enable(clk);
+ pltfm_host->clk = clk;
+
+ /*
+ * Some SOCs require additional clock to
+ * manage AXI bus clock.
+ * It is optional.
+ */
+ axi_clk = devm_clk_get(&pdev->dev, "axi");
+ if (!IS_ERR(axi_clk)) {
+ clk_prepare_enable(axi_clk);
+ priv->axi_clk = axi_clk;
+ }
+
+ err = xenon_probe_dt(pdev);
+ if (err)
+ goto err_clk;
+
+ err = xenon_slot_probe(host);
+ if (err)
+ goto err_clk;
+
+ err = sdhci_add_host(host);
+ if (err)
+ goto remove_slot;
+
+ return 0;
+
+remove_slot:
+ xenon_slot_remove(host);
+err_clk:
+ clk_disable_unprepare(pltfm_host->clk);
+ if (!IS_ERR(axi_clk))
+ clk_disable_unprepare(axi_clk);
+free_pltfm:
+ sdhci_pltfm_free(pdev);
+ return err;
+}
+
+static int sdhci_xenon_remove(struct platform_device *pdev)
+{
+ struct sdhci_host *host = platform_get_drvdata(pdev);
+ struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
+ struct sdhci_xenon_priv *priv = sdhci_pltfm_priv(pltfm_host);
+ int dead = (readl(host->ioaddr + SDHCI_INT_STATUS) == 0xFFFFFFFF);
+
+ xenon_slot_remove(host);
+
+ sdhci_remove_host(host, dead);
+
+ clk_disable_unprepare(pltfm_host->clk);
+ clk_disable_unprepare(priv->axi_clk);
+
+ sdhci_pltfm_free(pdev);
+
+ return 0;
+}
+
+static const struct of_device_id sdhci_xenon_dt_ids[] = {
+ { .compatible = "marvell,sdhci-xenon",},
+ { .compatible = "marvell,armada-3700-sdhci",},
+ {}
+};
+MODULE_DEVICE_TABLE(of, sdhci_xenon_dt_ids);
+
+static struct platform_driver sdhci_xenon_driver = {
+ .driver = {
+ .name = "sdhci-xenon",
+ .of_match_table = sdhci_xenon_dt_ids,
+ .pm = &sdhci_pltfm_pmops,
+ },
+ .probe = sdhci_xenon_probe,
+ .remove = sdhci_xenon_remove,
+};
+
+module_platform_driver(sdhci_xenon_driver);
+
+MODULE_DESCRIPTION("SDHCI platform driver for Marvell Xenon SDHC");
+MODULE_AUTHOR("Hu Ziji <huziji-eYqpPyKDWXRBDgjK7y7TUQ@public.gmane.org>");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/mmc/host/sdhci-xenon.h b/drivers/mmc/host/sdhci-xenon.h
new file mode 100644
index 000000000000..c2370493fbe8
--- /dev/null
+++ b/drivers/mmc/host/sdhci-xenon.h
@@ -0,0 +1,134 @@
+/*
+ * Copyright (C) 2016 Marvell, All Rights Reserved.
+ *
+ * Author: Hu Ziji <huziji-eYqpPyKDWXRBDgjK7y7TUQ@public.gmane.org>
+ * 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.
+ */
+#ifndef SDHCI_XENON_H_
+#define SDHCI_XENON_H_
+
+#include <linux/clk.h>
+#include <linux/mmc/card.h>
+#include <linux/of.h>
+#include "sdhci.h"
+
+/* Register Offset of SD Host Controller SOCP self-defined register */
+#define SDHC_SYS_CFG_INFO 0x0104
+#define SLOT_TYPE_SDIO_SHIFT 24
+#define SLOT_TYPE_EMMC_MASK 0xFF
+#define SLOT_TYPE_EMMC_SHIFT 16
+#define SLOT_TYPE_SD_SDIO_MMC_MASK 0xFF
+#define SLOT_TYPE_SD_SDIO_MMC_SHIFT 8
+#define NR_SUPPORTED_SLOT_MASK 0x7
+
+#define SDHC_SYS_OP_CTRL 0x0108
+#define AUTO_CLKGATE_DISABLE_MASK BIT(20)
+#define SDCLK_IDLEOFF_ENABLE_SHIFT 8
+#define SLOT_ENABLE_SHIFT 0
+
+#define SDHC_SYS_EXT_OP_CTRL 0x010C
+#define MASK_CMD_CONFLICT_ERROR BIT(8)
+
+#define SDHC_SLOT_OP_STATUS_CTRL 0x0128
+#define DELAY_90_DEGREE_MASK_EMMC5 BIT(7)
+#define DELAY_90_DEGREE_SHIFT_EMMC5 7
+#define EMMC_5_0_PHY_FIXED_DELAY_MASK 0x7F
+#define EMMC_PHY_FIXED_DELAY_MASK 0xFF
+#define EMMC_PHY_FIXED_DELAY_WINDOW_MIN (EMMC_PHY_FIXED_DELAY_MASK >> 3)
+#define SDH_PHY_FIXED_DELAY_MASK 0x1FF
+#define SDH_PHY_FIXED_DELAY_WINDOW_MIN (SDH_PHY_FIXED_DELAY_MASK >> 4)
+
+#define TUN_CONSECUTIVE_TIMES_SHIFT 16
+#define TUN_CONSECUTIVE_TIMES_MASK 0x7
+#define TUN_CONSECUTIVE_TIMES 0x4
+#define TUNING_STEP_SHIFT 12
+#define TUNING_STEP_MASK 0xF
+#define TUNING_STEP_DIVIDER BIT(6)
+
+#define FORCE_SEL_INVERSE_CLK_SHIFT 11
+
+#define SDHC_SLOT_EMMC_CTRL 0x0130
+#define ENABLE_DATA_STROBE BIT(24)
+#define SET_EMMC_RSTN BIT(16)
+#define DISABLE_RD_DATA_CRC BIT(14)
+#define DISABLE_CRC_STAT_TOKEN BIT(13)
+#define EMMC_VCCQ_MASK 0x3
+#define EMMC_VCCQ_1_8V 0x1
+#define EMMC_VCCQ_3_3V 0x3
+
+#define SDHC_SLOT_RETUNING_REQ_CTRL 0x0144
+/* retuning compatible */
+#define RETUNING_COMPATIBLE 0x1
+
+#define SDHC_SLOT_EXT_PRESENT_STATE 0x014C
+#define LOCK_STATE 0x1
+
+#define SDHC_SLOT_DLL_CUR_DLY_VAL 0x0150
+
+/* Tuning Parameter */
+#define TMR_RETUN_NO_PRESENT 0xF
+#define DEF_TUNING_COUNT 0x9
+
+#define MMC_TIMING_FAKE 0xFF
+
+#define DEFAULT_SDCLK_FREQ (400000)
+
+/* Xenon specific Mode Select value */
+#define XENON_SDHCI_CTRL_HS200 0x5
+#define XENON_SDHCI_CTRL_HS400 0x6
+
+struct sdhci_xenon_priv {
+ /*
+ * The bus_width, timing, and clock fields in below
+ * record the current setting of Xenon SDHC.
+ * Driver will call a Sampling Fixed Delay Adjustment
+ * if any setting is changed.
+ */
+ unsigned char bus_width;
+ unsigned char timing;
+ unsigned char tuning_count;
+ unsigned int clock;
+ struct clk *axi_clk;
+
+ /* Slot idx */
+ u8 slot_idx;
+
+ /*
+ * When initializing card, Xenon has to determine card type and
+ * adjust Sampling Fixed delay.
+ * However, at that time, card structure is not linked to mmc_host.
+ * Thus a card pointer is added here to provide
+ * the delay adjustment function with the card structure
+ * of the card during initialization
+ */
+ struct mmc_card *card_candidate;
+};
+
+static inline int enable_xenon_internal_clk(struct sdhci_host *host)
+{
+ u32 reg;
+ u8 timeout;
+
+ reg = sdhci_readl(host, SDHCI_CLOCK_CONTROL);
+ reg |= SDHCI_CLOCK_INT_EN;
+ sdhci_writel(host, reg, SDHCI_CLOCK_CONTROL);
+ /* Wait max 20 ms */
+ timeout = 20;
+ while (!((reg = sdhci_readw(host, SDHCI_CLOCK_CONTROL))
+ & SDHCI_CLOCK_INT_STABLE)) {
+ if (timeout == 0) {
+ pr_err("%s: Internal clock never stabilised.\n",
+ mmc_hostname(host->mmc));
+ return -ETIMEDOUT;
+ }
+ timeout--;
+ mdelay(1);
+ }
+
+ return 0;
+}
+#endif
--
git-series 0.8.10
--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
^ permalink raw reply related
* [PATCH 8/10] arm64: dts: marvell: add eMMC support for Armada 37xx
From: Gregory CLEMENT @ 2016-10-07 15:22 UTC (permalink / raw)
To: Ulf Hansson, Adrian Hunter, linux-mmc-u79uwXL29TY76Z2rM5mHXA
Cc: Jason Cooper, Andrew Lunn, Sebastian Hesselbarth, Gregory CLEMENT,
Rob Herring, devicetree-u79uwXL29TY76Z2rM5mHXA, Thomas Petazzoni,
linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r, Ziji Hu,
Jack(SH) Zhu, Jimmy Xu, Jisheng Zhang, Nadav Haklai, Ryan Gao,
Doug Jones, Shiwu Zhang, Victor Gu, Wei(SOCP) Liu, Wilson Ding,
Xueping Liu, Hilbert Zhang
In-Reply-To: <cover.990e3503c30c18ecf50dd48bce3d52811f03ca22.1475853198.git-series.gregory.clement-wi1+55ScJUtKEb57/3fJTNBPR1lH4CV8@public.gmane.org>
Add the eMMC support for Armada 37xx SoC and enable it in the Armada 3720
DB board.
Signed-off-by: Gregory CLEMENT <gregory.clement-wi1+55ScJUtKEb57/3fJTNBPR1lH4CV8@public.gmane.org>
---
arch/arm64/boot/dts/marvell/armada-3720-db.dts | 7 +++++++
arch/arm64/boot/dts/marvell/armada-37xx.dtsi | 11 +++++++++++
2 files changed, 18 insertions(+), 0 deletions(-)
diff --git a/arch/arm64/boot/dts/marvell/armada-3720-db.dts b/arch/arm64/boot/dts/marvell/armada-3720-db.dts
index 1372e9a6aaa4..5b9cff4d5d7a 100644
--- a/arch/arm64/boot/dts/marvell/armada-3720-db.dts
+++ b/arch/arm64/boot/dts/marvell/armada-3720-db.dts
@@ -72,6 +72,13 @@
status = "okay";
};
+&sdhci0 {
+ non-removable;
+ bus-width = <8>;
+ xenon,pad-type = "fixed-1-8v";
+ status = "okay";
+};
+
/* CON31 */
&usb3 {
status = "okay";
diff --git a/arch/arm64/boot/dts/marvell/armada-37xx.dtsi b/arch/arm64/boot/dts/marvell/armada-37xx.dtsi
index c4762538ec01..0c4cafe92e66 100644
--- a/arch/arm64/boot/dts/marvell/armada-37xx.dtsi
+++ b/arch/arm64/boot/dts/marvell/armada-37xx.dtsi
@@ -161,6 +161,17 @@
};
};
+ sdhci0: sdhci@d8000 {
+ compatible = "marvell,armada-3700-sdhci",
+ "marvell,sdhci-xenon";
+ reg = <0xd8000 0x300
+ 0x17808 0x4>;
+ interrupts = <GIC_SPI 26 IRQ_TYPE_LEVEL_HIGH>;
+ clocks = <&nb_perih_clk 0>;
+ clock-names = "core";
+ status = "disabled";
+ };
+
sata: sata@e0000 {
compatible = "marvell,armada-3700-ahci";
reg = <0xe0000 0x2000>;
--
git-series 0.8.10
--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
^ permalink raw reply related
* [PATCH 9/10] arm64: dts: marvell: add sdhci support for Armada 7K/8K
From: Gregory CLEMENT @ 2016-10-07 15:22 UTC (permalink / raw)
To: Ulf Hansson, Adrian Hunter, linux-mmc-u79uwXL29TY76Z2rM5mHXA
Cc: Jason Cooper, Andrew Lunn, Sebastian Hesselbarth, Gregory CLEMENT,
Rob Herring, devicetree-u79uwXL29TY76Z2rM5mHXA, Thomas Petazzoni,
linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r, Ziji Hu,
Jack(SH) Zhu, Jimmy Xu, Jisheng Zhang, Nadav Haklai, Ryan Gao,
Doug Jones, Shiwu Zhang, Victor Gu, Wei(SOCP) Liu, Wilson Ding,
Xueping Liu, Hilbert Zhang
In-Reply-To: <cover.990e3503c30c18ecf50dd48bce3d52811f03ca22.1475853198.git-series.gregory.clement-wi1+55ScJUtKEb57/3fJTNBPR1lH4CV8@public.gmane.org>
Also enable it on the Armada 7040 DB board
Signed-off-by: Gregory CLEMENT <gregory.clement-wi1+55ScJUtKEb57/3fJTNBPR1lH4CV8@public.gmane.org>
---
arch/arm64/boot/dts/marvell/armada-7040-db.dts | 7 +++++++
arch/arm64/boot/dts/marvell/armada-ap806.dtsi | 9 +++++++++
2 files changed, 16 insertions(+), 0 deletions(-)
diff --git a/arch/arm64/boot/dts/marvell/armada-7040-db.dts b/arch/arm64/boot/dts/marvell/armada-7040-db.dts
index 070b589680c5..f7f978a12a49 100644
--- a/arch/arm64/boot/dts/marvell/armada-7040-db.dts
+++ b/arch/arm64/boot/dts/marvell/armada-7040-db.dts
@@ -146,3 +146,10 @@
&cpm_usb3_1 {
status = "okay";
};
+
+&sdhci0 {
+ status = "okay";
+ bus-width = <4>;
+ no-1-8-v;
+ non-removable;
+};
diff --git a/arch/arm64/boot/dts/marvell/armada-ap806.dtsi b/arch/arm64/boot/dts/marvell/armada-ap806.dtsi
index 7b6136182ad0..ef2ce6be7205 100644
--- a/arch/arm64/boot/dts/marvell/armada-ap806.dtsi
+++ b/arch/arm64/boot/dts/marvell/armada-ap806.dtsi
@@ -229,6 +229,15 @@
};
+ sdhci0: sdhci@6e0000 {
+ compatible = "marvell,sdhci-xenon";
+ reg = <0x6e0000 0x300>;
+ interrupts = <GIC_SPI 16 IRQ_TYPE_LEVEL_HIGH>;
+ clock-names = "core";
+ clocks = <&cpm_syscon0 1 4>;
+ status = "disabled";
+ };
+
ap_syscon: system-controller@6f4000 {
compatible = "marvell,ap806-system-controller",
"syscon";
--
git-series 0.8.10
--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
^ permalink raw reply related
* [PATCH 10/10] arm64: configs: enable SDHCI driver for Xenon
From: Gregory CLEMENT @ 2016-10-07 15:22 UTC (permalink / raw)
To: Ulf Hansson, Adrian Hunter, linux-mmc-u79uwXL29TY76Z2rM5mHXA
Cc: Jason Cooper, Andrew Lunn, Sebastian Hesselbarth, Gregory CLEMENT,
Rob Herring, devicetree-u79uwXL29TY76Z2rM5mHXA, Thomas Petazzoni,
linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r, Ziji Hu,
Jack(SH) Zhu, Jimmy Xu, Jisheng Zhang, Nadav Haklai, Ryan Gao,
Doug Jones, Shiwu Zhang, Victor Gu, Wei(SOCP) Liu, Wilson Ding,
Xueping Liu, Hilbert Zhang
In-Reply-To: <cover.990e3503c30c18ecf50dd48bce3d52811f03ca22.1475853198.git-series.gregory.clement-wi1+55ScJUtKEb57/3fJTNBPR1lH4CV8@public.gmane.org>
This patch enables the driver for the SDHCI controller found on the
Marvell Armada 3700 and 7K/8K ARM64 SoCs.
Signed-off-by: Gregory CLEMENT <gregory.clement-wi1+55ScJUtKEb57/3fJTNBPR1lH4CV8@public.gmane.org>
---
arch/arm64/configs/defconfig | 1 +
1 file changed, 1 insertion(+), 0 deletions(-)
diff --git a/arch/arm64/configs/defconfig b/arch/arm64/configs/defconfig
index dab2cb0c1f1c..2d1f5ee62b18 100644
--- a/arch/arm64/configs/defconfig
+++ b/arch/arm64/configs/defconfig
@@ -353,6 +353,7 @@ CONFIG_MMC_DW=y
CONFIG_MMC_DW_EXYNOS=y
CONFIG_MMC_DW_K3=y
CONFIG_MMC_SUNXI=y
+CONFIG_MMC_SDHCI_XENON=y
CONFIG_NEW_LEDS=y
CONFIG_LEDS_CLASS=y
CONFIG_LEDS_GPIO=y
--
git-series 0.8.10
--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
^ permalink raw reply related
* [PATCH 7/10] mmc: sdhci-xenon: Add support to PHYs of Marvell Xenon SDHC
From: Gregory CLEMENT @ 2016-10-07 15:22 UTC (permalink / raw)
To: Ulf Hansson, Adrian Hunter, linux-mmc
Cc: Jason Cooper, Andrew Lunn, Sebastian Hesselbarth, Gregory CLEMENT,
Rob Herring, devicetree, Thomas Petazzoni, linux-arm-kernel,
Ziji Hu, Jack(SH) Zhu, Jimmy Xu, Jisheng Zhang, Nadav Haklai,
Ryan Gao, Doug Jones, Shiwu Zhang, Victor Gu, Wei(SOCP) Liu,
Wilson Ding, Xueping Liu, Hilbert Zhang
In-Reply-To: <cover.990e3503c30c18ecf50dd48bce3d52811f03ca22.1475853198.git-series.gregory.clement@free-electrons.com>
From: Ziji Hu <huziji@marvell.com>
Marvell Xenon eMMC/SD/SDIO Host Controller contains PHY.
Three types of PHYs are supported.
Add support to multiple types of PHYs init and configuration.
Add register definitions of PHYs.
Signed-off-by: Hu Ziji <huziji@marvell.com>
Reviewed-by: Gregory CLEMENT <gregory.clement@free-electrons.com>
Signed-off-by: Gregory CLEMENT <gregory.clement@free-electrons.com>
---
MAINTAINERS | 1 +-
drivers/mmc/host/Makefile | 2 +-
drivers/mmc/host/sdhci-xenon-phy.c | 1141 +++++++++++++++++++++++++++++-
drivers/mmc/host/sdhci-xenon-phy.h | 157 ++++-
drivers/mmc/host/sdhci-xenon.c | 4 +-
drivers/mmc/host/sdhci-xenon.h | 17 +-
6 files changed, 1321 insertions(+), 1 deletion(-)
create mode 100644 drivers/mmc/host/sdhci-xenon-phy.c
create mode 100644 drivers/mmc/host/sdhci-xenon-phy.h
diff --git a/MAINTAINERS b/MAINTAINERS
index 859420e5dfd3..b5673c2ee5f2 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -7583,6 +7583,7 @@ M: Ziji Hu <huziji@marvell.com>
L: linux-mmc@vger.kernel.org
S: Supported
F: drivers/mmc/host/sdhci-xenon.*
+F: drivers/mmc/host/sdhci-xenon-phy.*
F: Documentation/devicetree/bindings/mmc/marvell,sdhci-xenon.txt
MATROX FRAMEBUFFER DRIVER
diff --git a/drivers/mmc/host/Makefile b/drivers/mmc/host/Makefile
index 75eaf743486c..4f2854556ff7 100644
--- a/drivers/mmc/host/Makefile
+++ b/drivers/mmc/host/Makefile
@@ -82,4 +82,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 000000000000..4eb8fea1bec9
--- /dev/null
+++ b/drivers/mmc/host/sdhci-xenon-phy.c
@@ -0,0 +1,1141 @@
+/*
+ * PHY support for Xenon SDHC
+ *
+ * Copyright (C) 2016 Marvell, All Rights Reserved.
+ *
+ * Author: Hu Ziji <huziji@marvell.com>
+ * 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 <linux/slab.h>
+#include <linux/delay.h>
+#include <linux/of_address.h>
+#include <linux/mmc/host.h>
+#include <linux/mmc/mmc.h>
+#include <linux/mmc/card.h>
+#include <linux/mmc/sdio.h>
+
+#include "sdhci.h"
+#include "sdhci-pltfm.h"
+#include "sdhci-xenon.h"
+
+static const char * const phy_types[] = {
+ "sdh phy",
+ "emmc 5.0 phy",
+ "emmc 5.1 phy"
+};
+
+enum phy_type_enum {
+ SDH_PHY,
+ EMMC_5_0_PHY,
+ EMMC_5_1_PHY,
+ NR_PHY_TYPES
+};
+
+struct soc_pad_ctrl_table {
+ const char *soc;
+ void (*set_soc_pad)(struct sdhci_host *host,
+ unsigned char signal_voltage);
+};
+
+struct soc_pad_ctrl {
+ /* Register address of SOC PHY PAD ctrl */
+ void __iomem *reg;
+ /* SOC PHY PAD ctrl type */
+ enum soc_pad_ctrl_type pad_type;
+ /* SOC specific operation to set SOC PHY PAD */
+ void (*set_soc_pad)(struct sdhci_host *host,
+ unsigned char signal_voltage);
+};
+
+static struct xenon_emmc_phy_regs xenon_emmc_5_0_phy_regs = {
+ .timing_adj = EMMC_5_0_PHY_TIMING_ADJUST,
+ .func_ctrl = EMMC_5_0_PHY_FUNC_CONTROL,
+ .pad_ctrl = EMMC_5_0_PHY_PAD_CONTROL,
+ .pad_ctrl2 = EMMC_5_0_PHY_PAD_CONTROL2,
+ .dll_ctrl = EMMC_5_0_PHY_DLL_CONTROL,
+ .logic_timing_adj = EMMC_5_0_PHY_LOGIC_TIMING_ADJUST,
+ .delay_mask = EMMC_5_0_PHY_FIXED_DELAY_MASK,
+ .dll_update = DLL_UPDATE_STROBE_5_0,
+};
+
+static struct xenon_emmc_phy_regs xenon_emmc_5_1_phy_regs = {
+ .timing_adj = EMMC_PHY_TIMING_ADJUST,
+ .func_ctrl = EMMC_PHY_FUNC_CONTROL,
+ .pad_ctrl = EMMC_PHY_PAD_CONTROL,
+ .pad_ctrl2 = EMMC_PHY_PAD_CONTROL2,
+ .dll_ctrl = EMMC_PHY_DLL_CONTROL,
+ .logic_timing_adj = EMMC_PHY_LOGIC_TIMING_ADJUST,
+ .delay_mask = EMMC_PHY_FIXED_DELAY_MASK,
+ .dll_update = DLL_UPDATE,
+};
+
+static int xenon_delay_adj_test(struct mmc_card *card);
+
+/*
+ * eMMC PHY configuration and operations
+ */
+struct emmc_phy_params {
+ bool slow_mode;
+
+ u8 znr;
+ u8 zpr;
+
+ /* Nr of consecutive Sampling Points of a Valid Sampling Window */
+ u8 nr_tun_times;
+ /* Divider for calculating Tuning Step */
+ u8 tun_step_divider;
+
+ struct soc_pad_ctrl pad_ctrl;
+};
+
+static void xenon_emmc_phy_strobe_delay_adj(struct sdhci_host *host,
+ struct mmc_card *card);
+static int xenon_emmc_phy_fix_sampl_delay_adj(struct sdhci_host *host,
+ struct mmc_card *card);
+static void xenon_emmc_phy_set(struct sdhci_host *host,
+ unsigned char timing);
+static void xenon_emmc_set_soc_pad(struct sdhci_host *host,
+ unsigned char signal_voltage);
+
+static const struct xenon_phy_ops emmc_phy_ops = {
+ .strobe_delay_adj = xenon_emmc_phy_strobe_delay_adj,
+ .fix_sampl_delay_adj = xenon_emmc_phy_fix_sampl_delay_adj,
+ .phy_set = xenon_emmc_phy_set,
+ .set_soc_pad = xenon_emmc_set_soc_pad,
+};
+
+static int alloc_emmc_phy(struct sdhci_xenon_priv *priv)
+{
+ struct emmc_phy_params *params;
+
+ params = kzalloc(sizeof(*params), GFP_KERNEL);
+ if (!params)
+ return -ENOMEM;
+
+ priv->phy_params = params;
+ priv->phy_ops = &emmc_phy_ops;
+ if (priv->phy_type == EMMC_5_0_PHY)
+ priv->emmc_phy_regs = &xenon_emmc_5_0_phy_regs;
+ else
+ priv->emmc_phy_regs = &xenon_emmc_5_1_phy_regs;
+
+ return 0;
+}
+
+static int xenon_emmc_phy_init(struct sdhci_host *host)
+{
+ u32 reg;
+ u32 wait, clock;
+ struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
+ struct sdhci_xenon_priv *priv = sdhci_pltfm_priv(pltfm_host);
+ struct xenon_emmc_phy_regs *phy_regs = priv->emmc_phy_regs;
+
+ reg = sdhci_readl(host, phy_regs->timing_adj);
+ reg |= PHY_INITIALIZAION;
+ sdhci_writel(host, reg, phy_regs->timing_adj);
+
+ /* Add duration of FC_SYNC_RST */
+ wait = ((reg >> FC_SYNC_RST_DURATION_SHIFT) &
+ FC_SYNC_RST_DURATION_MASK);
+ /* Add interval between FC_SYNC_EN and FC_SYNC_RST */
+ wait += ((reg >> FC_SYNC_RST_EN_DURATION_SHIFT) &
+ FC_SYNC_RST_EN_DURATION_MASK);
+ /* Add duration of asserting FC_SYNC_EN */
+ wait += ((reg >> FC_SYNC_EN_DURATION_SHIFT) &
+ FC_SYNC_EN_DURATION_MASK);
+ /* Add duration of waiting for PHY */
+ wait += ((reg >> WAIT_CYCLE_BEFORE_USING_SHIFT) &
+ WAIT_CYCLE_BEFORE_USING_MASK);
+ /* 4 addtional bus clock and 4 AXI bus clock are required */
+ wait += 8;
+ wait <<= 20;
+
+ clock = host->clock;
+ if (!clock)
+ /* Use the possibly slowest bus frequency value */
+ clock = LOWEST_SDCLK_FREQ;
+ /* get the wait time */
+ wait /= clock;
+ wait++;
+ /* wait for host eMMC PHY init completes */
+ udelay(wait);
+
+ reg = sdhci_readl(host, phy_regs->timing_adj);
+ reg &= PHY_INITIALIZAION;
+ if (reg) {
+ dev_err(mmc_dev(host->mmc), "eMMC PHY init cannot complete after %d us\n",
+ wait);
+ return -ETIMEDOUT;
+ }
+
+ return 0;
+}
+
+#define ARMADA_3700_SOC_PAD_1_8V 0x1
+#define ARMADA_3700_SOC_PAD_3_3V 0x0
+
+static void armada_3700_soc_pad_voltage_set(struct sdhci_host *host,
+ unsigned char signal_voltage)
+{
+ 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;
+
+ if (params->pad_ctrl.pad_type == SOC_PAD_FIXED_1_8V) {
+ writel(ARMADA_3700_SOC_PAD_1_8V, params->pad_ctrl.reg);
+ } else if (params->pad_ctrl.pad_type == SOC_PAD_SD) {
+ if (signal_voltage == MMC_SIGNAL_VOLTAGE_180)
+ writel(ARMADA_3700_SOC_PAD_1_8V, params->pad_ctrl.reg);
+ else if (signal_voltage == MMC_SIGNAL_VOLTAGE_330)
+ writel(ARMADA_3700_SOC_PAD_3_3V, params->pad_ctrl.reg);
+ }
+}
+
+static void xenon_emmc_set_soc_pad(struct sdhci_host *host,
+ unsigned char signal_voltage)
+{
+ 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;
+
+ if (!params->pad_ctrl.reg)
+ return;
+
+ if (params->pad_ctrl.set_soc_pad)
+ params->pad_ctrl.set_soc_pad(host, signal_voltage);
+}
+
+static int emmc_phy_set_fix_sampl_delay(struct sdhci_host *host,
+ unsigned int delay,
+ bool invert,
+ bool delay_90_degree)
+{
+ u32 reg;
+ unsigned long flags;
+ struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
+ struct sdhci_xenon_priv *priv = sdhci_pltfm_priv(pltfm_host);
+ struct xenon_emmc_phy_regs *phy_regs = priv->emmc_phy_regs;
+ int ret = 0;
+
+ spin_lock_irqsave(&host->lock, flags);
+
+ /* Setup Sampling fix delay */
+ reg = sdhci_readl(host, SDHC_SLOT_OP_STATUS_CTRL);
+ reg &= ~phy_regs->delay_mask;
+ reg |= delay & phy_regs->delay_mask;
+ sdhci_writel(host, reg, SDHC_SLOT_OP_STATUS_CTRL);
+
+ if (priv->phy_type == EMMC_5_0_PHY) {
+ /* set 90 degree phase if necessary */
+ reg &= ~DELAY_90_DEGREE_MASK_EMMC5;
+ reg |= (delay_90_degree << DELAY_90_DEGREE_SHIFT_EMMC5);
+ sdhci_writel(host, reg, SDHC_SLOT_OP_STATUS_CTRL);
+ }
+
+ /* Disable SDCLK */
+ reg = sdhci_readl(host, SDHCI_CLOCK_CONTROL);
+ reg &= ~(SDHCI_CLOCK_CARD_EN | SDHCI_CLOCK_INT_EN);
+ sdhci_writel(host, reg, SDHCI_CLOCK_CONTROL);
+
+ udelay(200);
+
+ if (priv->phy_type == EMMC_5_1_PHY) {
+ /* set 90 degree phase if necessary */
+ reg = sdhci_readl(host, EMMC_PHY_FUNC_CONTROL);
+ reg &= ~ASYNC_DDRMODE_MASK;
+ reg |= (delay_90_degree << ASYNC_DDRMODE_SHIFT);
+ sdhci_writel(host, reg, EMMC_PHY_FUNC_CONTROL);
+ }
+
+ /* Setup Inversion of Sampling edge */
+ reg = sdhci_readl(host, phy_regs->timing_adj);
+ reg &= ~SAMPL_INV_QSP_PHASE_SELECT;
+ reg |= (invert << SAMPL_INV_QSP_PHASE_SELECT_SHIFT);
+ sdhci_writel(host, reg, phy_regs->timing_adj);
+
+ /* Enable SD internal clock */
+ ret = enable_xenon_internal_clk(host);
+ if (ret)
+ goto out;
+
+ /* Enable SDCLK */
+ reg = sdhci_readl(host, SDHCI_CLOCK_CONTROL);
+ reg |= SDHCI_CLOCK_CARD_EN;
+ sdhci_writel(host, reg, SDHCI_CLOCK_CONTROL);
+
+ udelay(200);
+
+ /*
+ * Has to re-initialize eMMC PHY here to active PHY
+ * because later get status cmd will be issued.
+ */
+ ret = xenon_emmc_phy_init(host);
+
+out:
+ spin_unlock_irqrestore(&host->lock, flags);
+ return ret;
+}
+
+static int emmc_phy_do_fix_sampl_delay(struct sdhci_host *host,
+ struct mmc_card *card,
+ unsigned int delay,
+ bool invert, bool quarter)
+{
+ int ret;
+
+ emmc_phy_set_fix_sampl_delay(host, delay, invert, quarter);
+
+ ret = xenon_delay_adj_test(card);
+ if (ret) {
+ dev_dbg(mmc_dev(host->mmc),
+ "fail when sampling fix delay = %d, phase = %d degree\n",
+ delay, invert * 180 + quarter * 90);
+ return -1;
+ }
+ return 0;
+}
+
+static int xenon_emmc_phy_fix_sampl_delay_adj(struct sdhci_host *host,
+ struct mmc_card *card)
+{
+ enum sampl_fix_delay_phase phase;
+ int idx, nr_pair;
+ int ret;
+ unsigned int delay;
+ unsigned int min_delay, max_delay;
+ bool invert, quarter;
+ struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
+ struct sdhci_xenon_priv *priv = sdhci_pltfm_priv(pltfm_host);
+ struct xenon_emmc_phy_regs *phy_regs = priv->emmc_phy_regs;
+ u32 coarse_step, fine_step;
+ const enum sampl_fix_delay_phase delay_edge[] = {
+ PHASE_0_DEGREE,
+ PHASE_180_DEGREE,
+ PHASE_90_DEGREE,
+ PHASE_270_DEGREE
+ };
+
+ coarse_step = phy_regs->delay_mask >> 1;
+ fine_step = coarse_step >> 2;
+
+ nr_pair = ARRAY_SIZE(delay_edge);
+
+ for (idx = 0; idx < nr_pair; idx++) {
+ phase = delay_edge[idx];
+ invert = (phase & 0x2) ? true : false;
+ quarter = (phase & 0x1) ? true : false;
+
+ /* increase delay value to get fix delay */
+ for (min_delay = 0;
+ min_delay <= phy_regs->delay_mask;
+ min_delay += coarse_step) {
+ ret = emmc_phy_do_fix_sampl_delay(host, card, min_delay,
+ invert, quarter);
+ if (!ret)
+ break;
+ }
+
+ if (ret) {
+ dev_dbg(mmc_dev(host->mmc),
+ "Fail to set Sampling Fixed Delay with phase = %d degree\n",
+ phase * 90);
+ continue;
+ }
+
+ for (max_delay = min_delay + fine_step;
+ max_delay < phy_regs->delay_mask;
+ max_delay += fine_step) {
+ ret = emmc_phy_do_fix_sampl_delay(host, card, max_delay,
+ invert, quarter);
+ if (ret) {
+ max_delay -= fine_step;
+ break;
+ }
+ }
+
+ if (!ret) {
+ ret = emmc_phy_do_fix_sampl_delay(host, card,
+ phy_regs->delay_mask,
+ invert, quarter);
+ if (!ret)
+ max_delay = phy_regs->delay_mask;
+ }
+
+ /*
+ * Sampling Fixed Delay line window should be large enough,
+ * thus the sampling point (the middle of the window)
+ * can work when environment varies.
+ * However, there is no clear conclusion how large the window
+ * should be.
+ */
+ if ((max_delay - min_delay) <=
+ EMMC_PHY_FIXED_DELAY_WINDOW_MIN) {
+ dev_info(mmc_dev(host->mmc),
+ "The window size %d with phase = %d degree is too small\n",
+ max_delay - min_delay, phase * 90);
+ continue;
+ }
+
+ delay = (min_delay + max_delay) / 2;
+ emmc_phy_set_fix_sampl_delay(host, delay, invert, quarter);
+ dev_dbg(mmc_dev(host->mmc),
+ "sampling fix delay = %d with phase = %d degree\n",
+ delay, phase * 90);
+ return 0;
+ }
+
+ return -EIO;
+}
+
+static int xenon_emmc_phy_enable_dll(struct sdhci_host *host)
+{
+ u32 reg;
+ struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
+ struct sdhci_xenon_priv *priv = sdhci_pltfm_priv(pltfm_host);
+ struct xenon_emmc_phy_regs *phy_regs = priv->emmc_phy_regs;
+ u8 timeout;
+
+ if (WARN_ON(host->clock <= MMC_HIGH_52_MAX_DTR))
+ return -EINVAL;
+
+ reg = sdhci_readl(host, phy_regs->dll_ctrl);
+ if (reg & DLL_ENABLE)
+ return 0;
+
+ /* Enable DLL */
+ reg = sdhci_readl(host, phy_regs->dll_ctrl);
+ reg |= (DLL_ENABLE | DLL_FAST_LOCK);
+
+ /*
+ * Set Phase as 90 degree, which is most common value.
+ * Might set another value if necessary.
+ * The granularity is 1 degree.
+ */
+ reg &= ~((DLL_PHASE_MASK << DLL_PHSEL0_SHIFT) |
+ (DLL_PHASE_MASK << DLL_PHSEL1_SHIFT));
+ reg |= ((DLL_PHASE_90_DEGREE << DLL_PHSEL0_SHIFT) |
+ (DLL_PHASE_90_DEGREE << DLL_PHSEL1_SHIFT));
+
+ reg &= ~DLL_BYPASS_EN;
+ reg |= phy_regs->dll_update;
+ if (priv->phy_type == EMMC_5_1_PHY)
+ reg &= ~DLL_REFCLK_SEL;
+ sdhci_writel(host, reg, phy_regs->dll_ctrl);
+
+ /* Wait max 32 ms */
+ timeout = 32;
+ while (!(sdhci_readw(host, SDHC_SLOT_EXT_PRESENT_STATE) & LOCK_STATE)) {
+ if (!timeout) {
+ dev_err(mmc_dev(host->mmc), "Wait for DLL Lock time-out\n");
+ return -ETIMEDOUT;
+ }
+ timeout--;
+ mdelay(1);
+ }
+ return 0;
+}
+
+static int __emmc_phy_config_tuning(struct sdhci_host *host)
+{
+ 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;
+ u32 reg, tuning_step;
+ int ret;
+ unsigned long flags;
+
+ if (WARN_ON(host->clock <= MMC_HIGH_52_MAX_DTR))
+ return -EINVAL;
+
+ spin_lock_irqsave(&host->lock, flags);
+
+ ret = xenon_emmc_phy_enable_dll(host);
+ if (ret) {
+ spin_unlock_irqrestore(&host->lock, flags);
+ return ret;
+ }
+
+ reg = sdhci_readl(host, SDHC_SLOT_DLL_CUR_DLY_VAL);
+ tuning_step = reg / params->tun_step_divider;
+ if (unlikely(tuning_step > TUNING_STEP_MASK)) {
+ dev_warn(mmc_dev(host->mmc),
+ "HS200 TUNING_STEP %d is larger than MAX value\n",
+ tuning_step);
+ tuning_step = TUNING_STEP_MASK;
+ }
+
+ reg = sdhci_readl(host, SDHC_SLOT_OP_STATUS_CTRL);
+ reg &= ~(TUN_CONSECUTIVE_TIMES_MASK << TUN_CONSECUTIVE_TIMES_SHIFT);
+ reg |= (params->nr_tun_times << TUN_CONSECUTIVE_TIMES_SHIFT);
+ reg &= ~(TUNING_STEP_MASK << TUNING_STEP_SHIFT);
+ reg |= (tuning_step << TUNING_STEP_SHIFT);
+ sdhci_writel(host, reg, SDHC_SLOT_OP_STATUS_CTRL);
+
+ spin_unlock_irqrestore(&host->lock, flags);
+ return 0;
+}
+
+static int xenon_emmc_phy_config_tuning(struct sdhci_host *host)
+{
+ return __emmc_phy_config_tuning(host);
+}
+
+static void xenon_emmc_phy_strobe_delay_adj(struct sdhci_host *host,
+ struct mmc_card *card)
+{
+ u32 reg;
+ struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
+ struct sdhci_xenon_priv *priv = sdhci_pltfm_priv(pltfm_host);
+ unsigned long flags;
+
+ if (host->clock <= MMC_HIGH_52_MAX_DTR)
+ return;
+
+ dev_dbg(mmc_dev(host->mmc), "starts HS400 strobe delay adjustment\n");
+
+ spin_lock_irqsave(&host->lock, flags);
+
+ xenon_emmc_phy_enable_dll(host);
+
+ /* Enable SDHC Data Strobe */
+ reg = sdhci_readl(host, SDHC_SLOT_EMMC_CTRL);
+ reg |= ENABLE_DATA_STROBE;
+ sdhci_writel(host, reg, SDHC_SLOT_EMMC_CTRL);
+
+ /* Set Data Strobe Pull down */
+ if (priv->phy_type == EMMC_5_0_PHY) {
+ reg = sdhci_readl(host, EMMC_5_0_PHY_PAD_CONTROL);
+ reg |= EMMC5_FC_QSP_PD;
+ reg &= ~EMMC5_FC_QSP_PU;
+ sdhci_writel(host, reg, EMMC_5_0_PHY_PAD_CONTROL);
+ } else {
+ reg = sdhci_readl(host, EMMC_PHY_PAD_CONTROL1);
+ reg |= EMMC5_1_FC_QSP_PD;
+ reg &= ~EMMC5_1_FC_QSP_PU;
+ sdhci_writel(host, reg, EMMC_PHY_PAD_CONTROL1);
+ }
+ spin_unlock_irqrestore(&host->lock, flags);
+}
+
+#define LOGIC_TIMING_VALUE 0x00AA8977
+
+static void xenon_emmc_phy_set(struct sdhci_host *host,
+ unsigned char timing)
+{
+ u32 reg;
+ 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;
+ struct mmc_card *card = priv->card_candidate;
+ unsigned long flags;
+
+ dev_dbg(mmc_dev(host->mmc), "eMMC PHY setting starts\n");
+
+ spin_lock_irqsave(&host->lock, flags);
+
+ /* Setup pad, set bit[28] and bits[26:24] */
+ reg = sdhci_readl(host, phy_regs->pad_ctrl);
+ reg |= (FC_DQ_RECEN | FC_CMD_RECEN | FC_QSP_RECEN | OEN_QSN);
+ /*
+ * All FC_XX_RECEIVCE should be set as CMOS Type
+ */
+ reg |= FC_ALL_CMOS_RECEIVER;
+ sdhci_writel(host, reg, phy_regs->pad_ctrl);
+
+ /* Set CMD and DQ Pull Up */
+ if (priv->phy_type == EMMC_5_0_PHY) {
+ reg = sdhci_readl(host, EMMC_5_0_PHY_PAD_CONTROL);
+ reg |= (EMMC5_FC_CMD_PU | EMMC5_FC_DQ_PU);
+ reg &= ~(EMMC5_FC_CMD_PD | EMMC5_FC_DQ_PD);
+ sdhci_writel(host, reg, EMMC_5_0_PHY_PAD_CONTROL);
+ } else {
+ reg = sdhci_readl(host, EMMC_PHY_PAD_CONTROL1);
+ reg |= (EMMC5_1_FC_CMD_PU | EMMC5_1_FC_DQ_PU);
+ reg &= ~(EMMC5_1_FC_CMD_PD | EMMC5_1_FC_DQ_PD);
+ sdhci_writel(host, reg, EMMC_PHY_PAD_CONTROL1);
+ }
+
+ if ((timing == MMC_TIMING_LEGACY) || !card)
+ goto phy_init;
+
+ /*
+ * FIXME: should depends on the specific board timing.
+ */
+ if ((timing == MMC_TIMING_MMC_HS400) ||
+ (timing == MMC_TIMING_MMC_HS200) ||
+ (timing == MMC_TIMING_UHS_SDR50) ||
+ (timing == MMC_TIMING_UHS_SDR104) ||
+ (timing == MMC_TIMING_UHS_DDR50) ||
+ (timing == MMC_TIMING_UHS_SDR25) ||
+ (timing == MMC_TIMING_MMC_DDR52)) {
+ reg = sdhci_readl(host, phy_regs->timing_adj);
+ reg &= ~OUTPUT_QSN_PHASE_SELECT;
+ sdhci_writel(host, reg, phy_regs->timing_adj);
+ }
+
+ /*
+ * If SDIO card, set SDIO Mode
+ * Otherwise, clear SDIO Mode and Slow Mode
+ */
+ if (mmc_card_sdio(card)) {
+ reg = sdhci_readl(host, phy_regs->timing_adj);
+ reg |= TIMING_ADJUST_SDIO_MODE;
+
+ if ((timing == MMC_TIMING_UHS_SDR25) ||
+ (timing == MMC_TIMING_UHS_SDR12) ||
+ (timing == MMC_TIMING_SD_HS) ||
+ (timing == MMC_TIMING_LEGACY))
+ reg |= TIMING_ADJUST_SLOW_MODE;
+
+ sdhci_writel(host, reg, phy_regs->timing_adj);
+ } else {
+ reg = sdhci_readl(host, phy_regs->timing_adj);
+ reg &= ~(TIMING_ADJUST_SDIO_MODE | TIMING_ADJUST_SLOW_MODE);
+ sdhci_writel(host, reg, phy_regs->timing_adj);
+ }
+
+ 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 = sdhci_readl(host, phy_regs->timing_adj);
+ reg |= TIMING_ADJUST_SLOW_MODE;
+ sdhci_writel(host, reg, phy_regs->timing_adj);
+ }
+
+ /*
+ * Set preferred ZNR and ZPR value
+ * The ZNR and ZPR value vary between different boards.
+ * Define them both in sdhci-xenon-emmc-phy.h.
+ */
+ reg = sdhci_readl(host, phy_regs->pad_ctrl2);
+ reg &= ~((ZNR_MASK << ZNR_SHIFT) | ZPR_MASK);
+ reg |= ((params->znr << ZNR_SHIFT) | params->zpr);
+ sdhci_writel(host, reg, phy_regs->pad_ctrl2);
+
+ /*
+ * When setting EMMC_PHY_FUNC_CONTROL register,
+ * SD clock should be disabled
+ */
+ reg = sdhci_readl(host, SDHCI_CLOCK_CONTROL);
+ reg &= ~SDHCI_CLOCK_CARD_EN;
+ sdhci_writew(host, reg, SDHCI_CLOCK_CONTROL);
+
+ if ((timing == MMC_TIMING_UHS_DDR50) ||
+ (timing == MMC_TIMING_MMC_HS400) ||
+ (timing == MMC_TIMING_MMC_DDR52)) {
+ reg = sdhci_readl(host, phy_regs->func_ctrl);
+ reg |= (DQ_DDR_MODE_MASK << DQ_DDR_MODE_SHIFT) | CMD_DDR_MODE;
+ sdhci_writel(host, reg, phy_regs->func_ctrl);
+ }
+
+ if (timing == MMC_TIMING_MMC_HS400) {
+ reg = sdhci_readl(host, phy_regs->func_ctrl);
+ reg &= ~DQ_ASYNC_MODE;
+ sdhci_writel(host, reg, phy_regs->func_ctrl);
+ }
+
+ /* Enable bus clock */
+ reg = sdhci_readl(host, SDHCI_CLOCK_CONTROL);
+ reg |= SDHCI_CLOCK_CARD_EN;
+ sdhci_writew(host, reg, SDHCI_CLOCK_CONTROL);
+
+ if (timing == MMC_TIMING_MMC_HS400)
+ /* Hardware team recommend a value for HS400 */
+ sdhci_writel(host, LOGIC_TIMING_VALUE,
+ phy_regs->logic_timing_adj);
+
+phy_init:
+ xenon_emmc_phy_init(host);
+
+ spin_unlock_irqrestore(&host->lock, flags);
+
+ dev_dbg(mmc_dev(host->mmc), "eMMC PHY setting completes\n");
+}
+
+static int get_dt_pad_ctrl_data(struct sdhci_host *host,
+ struct device_node *np,
+ struct emmc_phy_params *params)
+{
+ int ret = 0;
+ const char *name;
+ struct resource iomem;
+
+ if (of_device_is_compatible(np, "marvell,armada-3700-sdhci"))
+ params->pad_ctrl.set_soc_pad = armada_3700_soc_pad_voltage_set;
+ else
+ return 0;
+
+ if (of_address_to_resource(np, 1, &iomem)) {
+ dev_err(mmc_dev(host->mmc), "Unable to find SOC PAD ctrl register address for %s\n",
+ np->name);
+ return -EINVAL;
+ }
+
+ params->pad_ctrl.reg = devm_ioremap_resource(mmc_dev(host->mmc),
+ &iomem);
+ if (IS_ERR(params->pad_ctrl.reg)) {
+ dev_err(mmc_dev(host->mmc), "Unable to get SOC PHY PAD ctrl regiser for %s\n",
+ np->name);
+ return PTR_ERR(params->pad_ctrl.reg);
+ }
+
+ ret = of_property_read_string(np, "xenon,pad-type", &name);
+ if (ret) {
+ dev_err(mmc_dev(host->mmc), "Unable to determine SOC PHY PAD ctrl type\n");
+ return ret;
+ }
+ if (!strcmp(name, "sd")) {
+ params->pad_ctrl.pad_type = SOC_PAD_SD;
+ } else if (!strcmp(name, "fixed-1-8v")) {
+ params->pad_ctrl.pad_type = SOC_PAD_FIXED_1_8V;
+ } else {
+ dev_err(mmc_dev(host->mmc), "Unsupported SOC PHY PAD ctrl type %s\n",
+ name);
+ return -EINVAL;
+ }
+
+ return ret;
+}
+
+static int emmc_phy_parse_param_dt(struct sdhci_host *host,
+ struct device_node *np,
+ struct emmc_phy_params *params)
+{
+ u32 value;
+
+ if (of_property_read_bool(np, "xenon,phy-slow-mode"))
+ params->slow_mode = true;
+ else
+ params->slow_mode = false;
+
+ if (!of_property_read_u32(np, "xenon,phy-znr", &value))
+ params->znr = value & ZNR_MASK;
+ else
+ params->znr = ZNR_DEF_VALUE;
+
+ if (!of_property_read_u32(np, "xenon,phy-zpr", &value))
+ params->zpr = value & ZPR_MASK;
+ else
+ params->zpr = ZPR_DEF_VALUE;
+
+ if (!of_property_read_u32(np, "xenon,phy-nr-tun-times", &value))
+ params->nr_tun_times = value & TUN_CONSECUTIVE_TIMES_MASK;
+ else
+ params->nr_tun_times = TUN_CONSECUTIVE_TIMES;
+
+ if (!of_property_read_u32(np, "xenon,phy-tun-step-divider", &value))
+ params->tun_step_divider = value & 0xFF;
+ else
+ params->tun_step_divider = TUNING_STEP_DIVIDER;
+
+ return get_dt_pad_ctrl_data(host, np, params);
+}
+
+/*
+ * SDH PHY configuration and operations
+ */
+static int xenon_sdh_phy_set_fix_sampl_delay(struct sdhci_host *host,
+ unsigned int delay, bool invert)
+{
+ u32 reg;
+ unsigned long flags;
+ int ret;
+
+ if (invert)
+ invert = 0x1;
+ else
+ invert = 0x0;
+
+ spin_lock_irqsave(&host->lock, flags);
+
+ /* Disable SDCLK */
+ reg = sdhci_readl(host, SDHCI_CLOCK_CONTROL);
+ reg &= ~(SDHCI_CLOCK_CARD_EN | SDHCI_CLOCK_INT_EN);
+ sdhci_writel(host, reg, SDHCI_CLOCK_CONTROL);
+
+ udelay(200);
+
+ /* Setup Sampling fix delay */
+ reg = sdhci_readl(host, SDHC_SLOT_OP_STATUS_CTRL);
+ reg &= ~(SDH_PHY_FIXED_DELAY_MASK |
+ (0x1 << FORCE_SEL_INVERSE_CLK_SHIFT));
+ reg |= ((delay & SDH_PHY_FIXED_DELAY_MASK) |
+ (invert << FORCE_SEL_INVERSE_CLK_SHIFT));
+ sdhci_writel(host, reg, SDHC_SLOT_OP_STATUS_CTRL);
+
+ /* Enable SD internal clock */
+ ret = enable_xenon_internal_clk(host);
+
+ /* Enable SDCLK */
+ reg = sdhci_readl(host, SDHCI_CLOCK_CONTROL);
+ reg |= SDHCI_CLOCK_CARD_EN;
+ sdhci_writel(host, reg, SDHCI_CLOCK_CONTROL);
+
+ udelay(200);
+
+ spin_unlock_irqrestore(&host->lock, flags);
+ return ret;
+}
+
+static int sdh_phy_do_fix_sampl_delay(struct sdhci_host *host,
+ struct mmc_card *card,
+ unsigned int delay, bool invert)
+{
+ int ret;
+
+ xenon_sdh_phy_set_fix_sampl_delay(host, delay, invert);
+
+ ret = xenon_delay_adj_test(card);
+ if (ret) {
+ dev_dbg(mmc_dev(host->mmc),
+ "fail when sampling fix delay = %d, phase = %d degree\n",
+ delay, invert * 180);
+ return -1;
+ }
+ return 0;
+}
+
+#define SDH_PHY_COARSE_FIX_DELAY (SDH_PHY_FIXED_DELAY_MASK / 2)
+#define SDH_PHY_FINE_FIX_DELAY (SDH_PHY_COARSE_FIX_DELAY / 4)
+
+static int xenon_sdh_phy_fix_sampl_delay_adj(struct sdhci_host *host,
+ struct mmc_card *card)
+{
+ u32 reg;
+ bool dll_enable = false;
+ unsigned int min_delay, max_delay, delay;
+ const bool sampl_edge[] = {
+ false,
+ true,
+ };
+ int i, nr;
+ int ret;
+
+ if (host->clock > HIGH_SPEED_MAX_DTR) {
+ /* Enable DLL when SDCLK is higher than 50MHz */
+ reg = sdhci_readl(host, SDH_PHY_SLOT_DLL_CTRL);
+ if (!(reg & SDH_PHY_ENABLE_DLL)) {
+ reg |= (SDH_PHY_ENABLE_DLL | SDH_PHY_FAST_LOCK_EN);
+ sdhci_writel(host, reg, SDH_PHY_SLOT_DLL_CTRL);
+ mdelay(1);
+
+ reg = sdhci_readl(host, SDH_PHY_SLOT_DLL_PHASE_SEL);
+ reg |= SDH_PHY_DLL_UPDATE_TUNING;
+ sdhci_writel(host, reg, SDH_PHY_SLOT_DLL_PHASE_SEL);
+ }
+ dll_enable = true;
+ }
+
+ nr = dll_enable ? ARRAY_SIZE(sampl_edge) : 1;
+ for (i = 0; i < nr; i++) {
+ for (min_delay = 0; min_delay <= SDH_PHY_FIXED_DELAY_MASK;
+ min_delay += SDH_PHY_COARSE_FIX_DELAY) {
+ ret = sdh_phy_do_fix_sampl_delay(host, card, min_delay,
+ sampl_edge[i]);
+ if (!ret)
+ break;
+ }
+
+ if (ret) {
+ dev_dbg(mmc_dev(host->mmc),
+ "Fail to set Fixed Sampling Delay with %s edge\n",
+ sampl_edge[i] ? "negative" : "positive");
+ continue;
+ }
+
+ for (max_delay = min_delay + SDH_PHY_FINE_FIX_DELAY;
+ max_delay < SDH_PHY_FIXED_DELAY_MASK;
+ max_delay += SDH_PHY_FINE_FIX_DELAY) {
+ ret = sdh_phy_do_fix_sampl_delay(host, card, max_delay,
+ sampl_edge[i]);
+ if (ret) {
+ max_delay -= SDH_PHY_FINE_FIX_DELAY;
+ break;
+ }
+ }
+
+ if (!ret) {
+ delay = SDH_PHY_FIXED_DELAY_MASK;
+ ret = sdh_phy_do_fix_sampl_delay(host, card, delay,
+ sampl_edge[i]);
+ if (!ret)
+ max_delay = SDH_PHY_FIXED_DELAY_MASK;
+ }
+
+ if ((max_delay - min_delay) <= SDH_PHY_FIXED_DELAY_WINDOW_MIN) {
+ dev_info(mmc_dev(host->mmc),
+ "The window size %d with %s edge is too small\n",
+ max_delay - min_delay,
+ sampl_edge[i] ? "negative" : "positive");
+ continue;
+ }
+
+ delay = (min_delay + max_delay) / 2;
+ xenon_sdh_phy_set_fix_sampl_delay(host, delay, sampl_edge[i]);
+ dev_dbg(mmc_dev(host->mmc), "sampling fix delay = %d with %s edge\n",
+ delay, sampl_edge[i] ? "negative" : "positive");
+ return 0;
+ }
+ return -EIO;
+}
+
+static const struct xenon_phy_ops sdh_phy_ops = {
+ .fix_sampl_delay_adj = xenon_sdh_phy_fix_sampl_delay_adj,
+};
+
+static int alloc_sdh_phy(struct sdhci_xenon_priv *priv)
+{
+ priv->phy_params = NULL;
+ priv->phy_ops = &sdh_phy_ops;
+ return 0;
+}
+
+/*
+ * Common functions for all PHYs
+ */
+void xenon_soc_pad_ctrl(struct sdhci_host *host,
+ unsigned char signal_voltage)
+{
+ struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
+ struct sdhci_xenon_priv *priv = sdhci_pltfm_priv(pltfm_host);
+
+ if (priv->phy_ops->set_soc_pad)
+ priv->phy_ops->set_soc_pad(host, signal_voltage);
+}
+
+static int __xenon_emmc_delay_adj_test(struct mmc_card *card)
+{
+ int err;
+ u8 *ext_csd = NULL;
+
+ err = mmc_get_ext_csd(card, &ext_csd);
+ kfree(ext_csd);
+
+ return err;
+}
+
+static int __xenon_sdio_delay_adj_test(struct mmc_card *card)
+{
+ struct mmc_command cmd = {0};
+ int err;
+
+ cmd.opcode = SD_IO_RW_DIRECT;
+ cmd.flags = MMC_RSP_R5 | MMC_CMD_AC;
+
+ err = mmc_wait_for_cmd(card->host, &cmd, 0);
+ if (err)
+ return err;
+
+ if (cmd.resp[0] & R5_ERROR)
+ return -EIO;
+ if (cmd.resp[0] & R5_FUNCTION_NUMBER)
+ return -EINVAL;
+ if (cmd.resp[0] & R5_OUT_OF_RANGE)
+ return -ERANGE;
+ return 0;
+}
+
+static int __xenon_sd_delay_adj_test(struct mmc_card *card)
+{
+ struct mmc_command cmd = {0};
+ int err;
+
+ cmd.opcode = MMC_SEND_STATUS;
+ cmd.arg = card->rca << 16;
+ cmd.flags = MMC_RSP_R1 | MMC_CMD_AC;
+
+ err = mmc_wait_for_cmd(card->host, &cmd, 0);
+ return err;
+}
+
+static int xenon_delay_adj_test(struct mmc_card *card)
+{
+ WARN_ON(!card);
+ WARN_ON(!card->host);
+
+ if (mmc_card_mmc(card))
+ return __xenon_emmc_delay_adj_test(card);
+ else if (mmc_card_sd(card))
+ return __xenon_sd_delay_adj_test(card);
+ else if (mmc_card_sdio(card))
+ return __xenon_sdio_delay_adj_test(card);
+ else
+ return -EINVAL;
+}
+
+static void xenon_phy_set(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);
+
+ if (priv->phy_ops->phy_set)
+ priv->phy_ops->phy_set(host, timing);
+}
+
+static void xenon_hs400_strobe_delay_adj(struct sdhci_host *host,
+ struct mmc_card *card)
+{
+ struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
+ struct sdhci_xenon_priv *priv = sdhci_pltfm_priv(pltfm_host);
+
+ if (WARN_ON(!mmc_card_hs400(card)))
+ return;
+
+ /* Enable the DLL to automatically adjust HS400 strobe delay.
+ */
+ if (priv->phy_ops->strobe_delay_adj)
+ priv->phy_ops->strobe_delay_adj(host, card);
+}
+
+static int xenon_fix_sampl_delay_adj(struct sdhci_host *host,
+ struct mmc_card *card)
+{
+ struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
+ struct sdhci_xenon_priv *priv = sdhci_pltfm_priv(pltfm_host);
+
+ if (priv->phy_ops->fix_sampl_delay_adj)
+ return priv->phy_ops->fix_sampl_delay_adj(host, card);
+
+ return 0;
+}
+
+/*
+ * xenon_delay_adj should not be called inside IRQ context,
+ * either Hard IRQ or Softirq.
+ */
+static int xenon_hs_delay_adj(struct sdhci_host *host,
+ struct mmc_card *card)
+{
+ struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
+ struct sdhci_xenon_priv *priv = sdhci_pltfm_priv(pltfm_host);
+ int ret = 0;
+
+ if (WARN_ON(host->clock <= DEFAULT_SDCLK_FREQ))
+ return -EINVAL;
+
+ if (mmc_card_hs400(card)) {
+ xenon_hs400_strobe_delay_adj(host, card);
+ return 0;
+ }
+
+ if (((priv->phy_type == EMMC_5_1_PHY) ||
+ (priv->phy_type == EMMC_5_0_PHY)) &&
+ (mmc_card_hs200(card) ||
+ (host->timing == MMC_TIMING_UHS_SDR104))) {
+ ret = xenon_emmc_phy_config_tuning(host);
+ if (!ret)
+ return 0;
+ }
+
+ ret = xenon_fix_sampl_delay_adj(host, card);
+ if (ret)
+ dev_err(mmc_dev(host->mmc), "fails sampling fixed delay adjustment\n");
+ return ret;
+}
+
+int xenon_phy_adj(struct sdhci_host *host, struct mmc_ios *ios)
+{
+ struct mmc_host *mmc = host->mmc;
+ struct mmc_card *card;
+ int ret = 0;
+ struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
+ struct sdhci_xenon_priv *priv = sdhci_pltfm_priv(pltfm_host);
+
+ if (!host->clock) {
+ priv->clock = 0;
+ return 0;
+ }
+
+ /*
+ * The timing, frequency or bus width is changed,
+ * better to set eMMC PHY based on current setting
+ * and adjust Xenon SDHC delay.
+ */
+ if ((host->clock == priv->clock) &&
+ (ios->bus_width == priv->bus_width) &&
+ (ios->timing == priv->timing))
+ return 0;
+
+ xenon_phy_set(host, ios->timing);
+
+ /* Update the record */
+ priv->bus_width = ios->bus_width;
+ /* Temp stage from HS200 to HS400 */
+ if (((priv->timing == MMC_TIMING_MMC_HS200) &&
+ (ios->timing == MMC_TIMING_MMC_HS)) ||
+ ((ios->timing == MMC_TIMING_MMC_HS) &&
+ (priv->clock > host->clock))) {
+ priv->timing = ios->timing;
+ priv->clock = host->clock;
+ return 0;
+ }
+ priv->timing = ios->timing;
+ priv->clock = host->clock;
+
+ /* Legacy mode is a special case */
+ if (ios->timing == MMC_TIMING_LEGACY)
+ return 0;
+
+ card = priv->card_candidate;
+ if (unlikely(!card)) {
+ dev_warn(mmc_dev(mmc), "card is not present\n");
+ return -EINVAL;
+ }
+
+ if (host->clock > DEFAULT_SDCLK_FREQ)
+ ret = xenon_hs_delay_adj(host, card);
+ return ret;
+}
+
+static int add_xenon_phy(struct device_node *np, struct sdhci_host *host,
+ const char *phy_name)
+{
+ struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
+ struct sdhci_xenon_priv *priv = sdhci_pltfm_priv(pltfm_host);
+ int i, ret;
+
+ for (i = 0; i < NR_PHY_TYPES; i++) {
+ if (!strcmp(phy_name, phy_types[i])) {
+ priv->phy_type = i;
+ break;
+ }
+ }
+ if (i == NR_PHY_TYPES) {
+ dev_err(mmc_dev(host->mmc),
+ "Unable to determine PHY name %s. Use default eMMC 5.1 PHY\n",
+ phy_name);
+ priv->phy_type = EMMC_5_1_PHY;
+ }
+
+ if (priv->phy_type == SDH_PHY) {
+ return alloc_sdh_phy(priv);
+ } else if ((priv->phy_type == EMMC_5_0_PHY) ||
+ (priv->phy_type == EMMC_5_1_PHY)) {
+ ret = alloc_emmc_phy(priv);
+ if (ret)
+ return ret;
+ return emmc_phy_parse_param_dt(host, np, priv->phy_params);
+ }
+
+ return -EINVAL;
+}
+
+int xenon_phy_parse_dt(struct device_node *np, struct sdhci_host *host)
+{
+ const char *phy_type = NULL;
+
+ if (!of_property_read_string(np, "xenon,phy-type", &phy_type))
+ return add_xenon_phy(np, host, phy_type);
+
+ dev_err(mmc_dev(host->mmc), "Fail to get Xenon PHY type. Use default eMMC 5.1 PHY\n");
+ return add_xenon_phy(np, host, "emmc 5.1 phy");
+}
diff --git a/drivers/mmc/host/sdhci-xenon-phy.h b/drivers/mmc/host/sdhci-xenon-phy.h
new file mode 100644
index 000000000000..4373c71d3b7b
--- /dev/null
+++ b/drivers/mmc/host/sdhci-xenon-phy.h
@@ -0,0 +1,157 @@
+/* linux/drivers/mmc/host/sdhci-xenon-phy.h
+ *
+ * Author: Hu Ziji <huziji@marvell.com>
+ * Date: 2016-8-24
+ *
+ * Copyright (C) 2016 Marvell, All Rights Reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or (at
+ * your option) any later version.
+ */
+#ifndef SDHCI_XENON_PHY_H_
+#define SDHCI_XENON_PHY_H_
+
+#include <linux/types.h>
+#include "sdhci.h"
+
+/* Register base for eMMC PHY 5.0 Version */
+#define EMMC_5_0_PHY_REG_BASE 0x0160
+/* Register base for eMMC PHY 5.1 Version */
+#define EMMC_PHY_REG_BASE 0x0170
+
+#define EMMC_PHY_TIMING_ADJUST EMMC_PHY_REG_BASE
+#define EMMC_5_0_PHY_TIMING_ADJUST EMMC_5_0_PHY_REG_BASE
+#define TIMING_ADJUST_SLOW_MODE BIT(29)
+#define TIMING_ADJUST_SDIO_MODE BIT(28)
+#define OUTPUT_QSN_PHASE_SELECT BIT(17)
+#define SAMPL_INV_QSP_PHASE_SELECT BIT(18)
+#define SAMPL_INV_QSP_PHASE_SELECT_SHIFT 18
+#define PHY_INITIALIZAION BIT(31)
+#define WAIT_CYCLE_BEFORE_USING_MASK 0xF
+#define WAIT_CYCLE_BEFORE_USING_SHIFT 12
+#define FC_SYNC_EN_DURATION_MASK 0xF
+#define FC_SYNC_EN_DURATION_SHIFT 8
+#define FC_SYNC_RST_EN_DURATION_MASK 0xF
+#define FC_SYNC_RST_EN_DURATION_SHIFT 4
+#define FC_SYNC_RST_DURATION_MASK 0xF
+#define FC_SYNC_RST_DURATION_SHIFT 0
+
+#define EMMC_PHY_FUNC_CONTROL (EMMC_PHY_REG_BASE + 0x4)
+#define EMMC_5_0_PHY_FUNC_CONTROL (EMMC_5_0_PHY_REG_BASE + 0x4)
+#define ASYNC_DDRMODE_MASK BIT(23)
+#define ASYNC_DDRMODE_SHIFT 23
+#define CMD_DDR_MODE BIT(16)
+#define DQ_DDR_MODE_SHIFT 8
+#define DQ_DDR_MODE_MASK 0xFF
+#define DQ_ASYNC_MODE BIT(4)
+
+#define EMMC_PHY_PAD_CONTROL (EMMC_PHY_REG_BASE + 0x8)
+#define EMMC_5_0_PHY_PAD_CONTROL (EMMC_5_0_PHY_REG_BASE + 0x8)
+#define REC_EN_SHIFT 24
+#define REC_EN_MASK 0xF
+#define FC_DQ_RECEN BIT(24)
+#define FC_CMD_RECEN BIT(25)
+#define FC_QSP_RECEN BIT(26)
+#define FC_QSN_RECEN BIT(27)
+#define OEN_QSN BIT(28)
+#define AUTO_RECEN_CTRL BIT(30)
+#define FC_ALL_CMOS_RECEIVER 0xF000
+
+#define EMMC5_FC_QSP_PD BIT(18)
+#define EMMC5_FC_QSP_PU BIT(22)
+#define EMMC5_FC_CMD_PD BIT(17)
+#define EMMC5_FC_CMD_PU BIT(21)
+#define EMMC5_FC_DQ_PD BIT(16)
+#define EMMC5_FC_DQ_PU BIT(20)
+
+#define EMMC_PHY_PAD_CONTROL1 (EMMC_PHY_REG_BASE + 0xC)
+#define EMMC5_1_FC_QSP_PD BIT(9)
+#define EMMC5_1_FC_QSP_PU BIT(25)
+#define EMMC5_1_FC_CMD_PD BIT(8)
+#define EMMC5_1_FC_CMD_PU BIT(24)
+#define EMMC5_1_FC_DQ_PD 0xFF
+#define EMMC5_1_FC_DQ_PU (0xFF << 16)
+
+#define EMMC_PHY_PAD_CONTROL2 (EMMC_PHY_REG_BASE + 0x10)
+#define EMMC_5_0_PHY_PAD_CONTROL2 (EMMC_5_0_PHY_REG_BASE + 0xC)
+#define ZNR_MASK 0x1F
+#define ZNR_SHIFT 8
+#define ZPR_MASK 0x1F
+/* Perferred ZNR and ZPR value vary between different boards.
+ * The specific ZNR and ZPR value should be defined here
+ * according to board actual timing.
+ */
+#define ZNR_DEF_VALUE 0xF
+#define ZPR_DEF_VALUE 0xF
+
+#define EMMC_PHY_DLL_CONTROL (EMMC_PHY_REG_BASE + 0x14)
+#define EMMC_5_0_PHY_DLL_CONTROL (EMMC_5_0_PHY_REG_BASE + 0x10)
+#define DLL_ENABLE BIT(31)
+#define DLL_UPDATE_STROBE_5_0 BIT(30)
+#define DLL_REFCLK_SEL BIT(30)
+#define DLL_UPDATE BIT(23)
+#define DLL_PHSEL1_SHIFT 24
+#define DLL_PHSEL0_SHIFT 16
+#define DLL_PHASE_MASK 0x3F
+#define DLL_PHASE_90_DEGREE 0x1F
+#define DLL_FAST_LOCK BIT(5)
+#define DLL_GAIN2X BIT(3)
+#define DLL_BYPASS_EN BIT(0)
+
+#define EMMC_5_0_PHY_LOGIC_TIMING_ADJUST (EMMC_5_0_PHY_REG_BASE + 0x14)
+#define EMMC_PHY_LOGIC_TIMING_ADJUST (EMMC_PHY_REG_BASE + 0x18)
+
+enum sampl_fix_delay_phase {
+ PHASE_0_DEGREE = 0x0,
+ PHASE_90_DEGREE = 0x1,
+ PHASE_180_DEGREE = 0x2,
+ PHASE_270_DEGREE = 0x3,
+};
+
+#define SDH_PHY_SLOT_DLL_CTRL (0x0138)
+#define SDH_PHY_ENABLE_DLL BIT(1)
+#define SDH_PHY_FAST_LOCK_EN BIT(5)
+
+#define SDH_PHY_SLOT_DLL_PHASE_SEL (0x013C)
+#define SDH_PHY_DLL_UPDATE_TUNING BIT(15)
+
+enum soc_pad_ctrl_type {
+ SOC_PAD_SD,
+ SOC_PAD_FIXED_1_8V,
+};
+
+/*
+ * List offset of PHY registers and some special register values
+ * in eMMC PHY 5.0 or eMMC PHY 5.1
+ */
+struct xenon_emmc_phy_regs {
+ /* Offset of Timing Adjust register */
+ u16 timing_adj;
+ /* Offset of Func Control register */
+ u16 func_ctrl;
+ /* Offset of Pad Control register */
+ u16 pad_ctrl;
+ /* Offset of Pad Control register */
+ u16 pad_ctrl2;
+ /* Offset of DLL Control register */
+ u16 dll_ctrl;
+ /* Offset of Logic Timing Adjust register */
+ u16 logic_timing_adj;
+ /* Max value of eMMC Fixed Sampling Delay */
+ u32 delay_mask;
+ /* DLL Update Enable bit */
+ u32 dll_update;
+};
+
+struct xenon_phy_ops {
+ void (*strobe_delay_adj)(struct sdhci_host *host,
+ struct mmc_card *card);
+ int (*fix_sampl_delay_adj)(struct sdhci_host *host,
+ struct mmc_card *card);
+ void (*phy_set)(struct sdhci_host *host, unsigned char timing);
+ void (*set_soc_pad)(struct sdhci_host *host,
+ unsigned char signal_voltage);
+};
+#endif
diff --git a/drivers/mmc/host/sdhci-xenon.c b/drivers/mmc/host/sdhci-xenon.c
index 03ba183494d3..4d7d871544fc 100644
--- a/drivers/mmc/host/sdhci-xenon.c
+++ b/drivers/mmc/host/sdhci-xenon.c
@@ -224,6 +224,7 @@ static void xenon_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
spin_unlock_irqrestore(&host->lock, flags);
sdhci_set_ios(mmc, ios);
+ xenon_phy_adj(host, ios);
if (host->clock > DEFAULT_SDCLK_FREQ) {
spin_lock_irqsave(&host->lock, flags);
@@ -309,6 +310,8 @@ static int xenon_start_signal_voltage_switch(struct mmc_host *mmc,
*/
enable_xenon_internal_clk(host);
+ xenon_soc_pad_ctrl(host, ios->signal_voltage);
+
if (priv->card_candidate) {
if (mmc_card_mmc(priv->card_candidate))
return xenon_emmc_signal_voltage_switch(mmc, ios);
@@ -453,6 +456,7 @@ static int xenon_probe_dt(struct platform_device *pdev)
sdhci_writel(host, reg, SDHC_SYS_EXT_OP_CTRL);
}
+ err = xenon_phy_parse_dt(np, host);
return err;
}
diff --git a/drivers/mmc/host/sdhci-xenon.h b/drivers/mmc/host/sdhci-xenon.h
index c2370493fbe8..06e5261a563c 100644
--- a/drivers/mmc/host/sdhci-xenon.h
+++ b/drivers/mmc/host/sdhci-xenon.h
@@ -15,6 +15,7 @@
#include <linux/mmc/card.h>
#include <linux/of.h>
#include "sdhci.h"
+#include "sdhci-xenon-phy.h"
/* Register Offset of SD Host Controller SOCP self-defined register */
#define SDHC_SYS_CFG_INFO 0x0104
@@ -76,6 +77,7 @@
#define MMC_TIMING_FAKE 0xFF
#define DEFAULT_SDCLK_FREQ (400000)
+#define LOWEST_SDCLK_FREQ (100000)
/* Xenon specific Mode Select value */
#define XENON_SDHCI_CTRL_HS200 0x5
@@ -97,6 +99,15 @@ struct sdhci_xenon_priv {
/* Slot idx */
u8 slot_idx;
+ int phy_type;
+ /*
+ * Contains board-specific PHY parameters
+ * passed from device tree.
+ */
+ void *phy_params;
+ const struct xenon_phy_ops *phy_ops;
+ struct xenon_emmc_phy_regs *emmc_phy_regs;
+
/*
* When initializing card, Xenon has to determine card type and
* adjust Sampling Fixed delay.
@@ -131,4 +142,10 @@ static inline int enable_xenon_internal_clk(struct sdhci_host *host)
return 0;
}
+
+int xenon_phy_adj(struct sdhci_host *host, struct mmc_ios *ios);
+int xenon_phy_parse_dt(struct device_node *np,
+ struct sdhci_host *host);
+void xenon_soc_pad_ctrl(struct sdhci_host *host,
+ unsigned char signal_voltage);
#endif
--
git-series 0.8.10
^ permalink raw reply related
* Re: [RFC 1/2] sdhci: Add device tree property sd-broken-highspeed
From: Zach Brown @ 2016-10-07 18:56 UTC (permalink / raw)
To: Adrian Hunter
Cc: Shawn Lin, Julia Cartwright, Rob Herring, Ulf Hansson,
Mark Rutland, linux-mmc, devicetree@vger.kernel.org,
linux-kernel@vger.kernel.org
In-Reply-To: <7acb34a9-d323-b112-f796-92c2a962743c@intel.com>
On Thu, Oct 06, 2016 at 09:13:24AM +0300, Adrian Hunter wrote:
> On 06/10/16 04:34, Shawn Lin wrote:
> > On 2016/10/6 5:22, Julia Cartwright wrote:
> >> On Wed, Oct 05, 2016 at 03:03:44PM -0500, Rob Herring wrote:
> >>> On Wed, Oct 5, 2016 at 1:33 PM, Ulf Hansson <ulf.hansson@linaro.org> wrote:
> >>>> On 23 September 2016 at 22:01, Zach Brown <zach.brown@ni.com> wrote:
> >>>>> Certain board configurations can make highspeed malfunction due to
> >>>>> timing issues. In these cases a way is needed to force the controller
> >>>>> and card into standard speed even if they otherwise appear to be capable
> >>>>> of highspeed.
> >>>>>
> >>>>> The sd-broken-highspeed property will let the sdhci driver know that
> >>>>> highspeed will not work.
> >>>>>
> >>>>> Signed-off-by: Zach Brown <zach.brown@ni.com>
> >>>>> ---
> >>>>> Documentation/devicetree/bindings/mmc/mmc.txt | 2 ++
> >>>>> 1 file changed, 2 insertions(+)
> >>>>>
> >>>>> diff --git a/Documentation/devicetree/bindings/mmc/mmc.txt
> >>>>> b/Documentation/devicetree/bindings/mmc/mmc.txt
> >>>>> index 8a37782..59332ea 100644
> >>>>> --- a/Documentation/devicetree/bindings/mmc/mmc.txt
> >>>>> +++ b/Documentation/devicetree/bindings/mmc/mmc.txt
> >>>>> @@ -52,6 +52,8 @@ Optional properties:
> >>>>> - no-sdio: controller is limited to send sdio cmd during initialization
> >>>>> - no-sd: controller is limited to send sd cmd during initialization
> >>>>> - no-mmc: controller is limited to send mmc cmd during initialization
> >>>>> +- sd-broken-highspeed: Highspeed is broken, even if the controller and
> >>>>> card
> >>>>> + themselves claim they support highspeed.
> >>>>
> >>>> Regarding a broken card, that is managed via the card quirks and not in DT.
> >>>>
> >>>> If this is about a controller limitation, we already have the option
> >>>> to describe what it supports, so we don't need an option to tell what
> >>>> it *not* supports.
> >>>>
> >>>> For example "cap-sd-highspeed" tells whether the controller supports
> >>>> SD high-speed, please use that instead.
> >>>
> >>> If a controller has a capability register and it lies (perhaps the
> >>> board has limitations that the SoC does not), then you may need to
> >>> disable a feature.
> >>
> >> That's precisely the case here. This is a board-level problem, not a
> >> card or controller problem. As Zach mentioned in the cover letter, the
> >> trace length between controller and card on some of our boards is too
> >> long to meet high-speed timings, even though both card and controller
> >> advertise it.
> >
> > IIRC, I saw the same problem while using sdhci to bring up a
> > industrial board for vehicle. The trace length is so long that
> > I have to limit the max-frequency to make it works properly when
> > running at hishspeed.
> >
> > So could you try to limit the max-frequency in your DT to see
> > if it could work for you? I guess it should work as once reducing
> > the frequency, the timing per cycle will be large enough to meet
> > the spec. At least that helped me solve my problem.
> >
> > For further consideration, I deployed a mechanism called "tuning for
> > non UHS or non-hs200/400" for my donwstream tree at that time. The basic
> > concept is to ask devices to send ext_csd(or send status for SD case)
> > *repeatedly*. Some hosts, i.e. sdhci-of-arasan or dw_mmc-rockchip, can
> > manually set rx delay via some clock unit registers to capture the
> > working sample window and select the middle point of the longest good
> > phase region. The same for retune. But it is a little complicated, and
> > could only be applicable to the hosts who could adjust the rx delay
> > manually when claiming the caps of MMC_CAPS_CAN_TUNING_FOR_HS, whatever
> > the name is...
> >
> > I don't know if it is worth to add this, and I don't know if it's
> > a legit way. Anyway, I just share my thought(experience) for you and
> > linux-mmc to think more about how to deal with the case you meet rather
> > than sacrificing the performence by removing highspeed or reducing
> > frequency..
> >
>
> As Shawn points out, using max-frequency is a possibility. Did you consider
> that?
>
I hadn't. So I looked into it and, as I stated the issue using max-frequency
would work, but I forgot to add that, because we pass the signal through an
fpga it has to be standard speed. If the card and controller are not in
standard speed then it won't pass hold time. Specifically we need the data to
change on the falling edge of the clock.
I'll add this justification to the commit message.
> There are 2 high speed caps:
> cap-sd-highspeed
> cap-mmc-highspeed
>
> Yet patch in 2, the effect of sd-broken-highspeed is to suppress highspeed
> for both SD and MMC. Should it then be called just broken-highspeed?
>
I agree, broken-highspeed makes more sense. Will change in the next version.
^ permalink raw reply
page: next (older) | prev (newer) | latest
- recent:[subjects (threaded)|topics (new)|topics (active)]
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox