Linux MultiMedia Card development
 help / color / mirror / Atom feed
* Re: [PATCH] mmc: rtsx_usb_sdmmc: Handle runtime PM while changing led
From: Ulf Hansson @ 2016-09-20  9:34 UTC (permalink / raw)
  To: Alan Stern
  Cc: linux-mmc, Ritesh Raj Sarraf, USB list, Micky Ching, Roger Tseng,
	Wei WANG
In-Reply-To: <Pine.LNX.4.44L0.1609191348440.1458-100000@iolanthe.rowland.org>

On 19 September 2016 at 20:02, Alan Stern <stern@rowland.harvard.edu> wrote:
> On Mon, 19 Sep 2016, Ulf Hansson wrote:
>
>> On 18 September 2016 at 04:30, Alan Stern <stern@rowland.harvard.edu> wrote:
>> > On Sat, 17 Sep 2016, Ulf Hansson wrote:
>> >
>> >> Each access of the parent device (usb device) needs to be done in runtime
>> >> resumed state. Currently this isn't case while changing the leds, so let's
>> >> add pm_runtime_get_sync() and pm_runtime_put() around these calls.
>> >>
>> >> Signed-off-by: Ulf Hansson <ulf.hansson@linaro.org>
>> >> ---
>> >>
>> >> While discussing an issue[1] related to runtime PM, I found out that this
>> >> minor change at least improves the behavior that has been observed.
>> >>
>> >> [1]
>> >> http://www.spinics.net/lists/linux-usb/msg144634.html
>> >>
>> >> ---
>> >>  drivers/mmc/host/rtsx_usb_sdmmc.c | 2 ++
>> >>  1 file changed, 2 insertions(+)
>> >>
>> >> diff --git a/drivers/mmc/host/rtsx_usb_sdmmc.c b/drivers/mmc/host/rtsx_usb_sdmmc.c
>> >> index 6c71fc9..a59c7fa 100644
>> >> --- a/drivers/mmc/host/rtsx_usb_sdmmc.c
>> >> +++ b/drivers/mmc/host/rtsx_usb_sdmmc.c
>> >> @@ -1314,6 +1314,7 @@ static void rtsx_usb_update_led(struct work_struct *work)
>> >>               container_of(work, struct rtsx_usb_sdmmc, led_work);
>> >>       struct rtsx_ucr *ucr = host->ucr;
>> >>
>> >> +     pm_runtime_get_sync(sdmmc_dev(host));
>> >>       mutex_lock(&ucr->dev_mutex);
>> >>
>> >>       if (host->led.brightness == LED_OFF)
>> >> @@ -1322,6 +1323,7 @@ static void rtsx_usb_update_led(struct work_struct *work)
>> >>               rtsx_usb_turn_on_led(ucr);
>> >>
>> >>       mutex_unlock(&ucr->dev_mutex);
>> >> +     pm_runtime_put(sdmmc_dev(host));
>> >>  }
>> >>  #endif
>> >
>> > The missing aspect here is that this won't stop the parent USB device
>> > from going into autosuspend every 2 seconds and then resuming shortly
>> > afterward.  There are two ways of preventing this:
>> >
>> >         Call usb_mark_last_busy() at appropriate places.
>> >
>> >         Enable autosuspend for the sdmmc device.
>> >
>> > The second approach would also prevent the sdmmc device from going into
>> > autosuspend as soon as the LED update is finished.  Maybe that's okay,
>> > but if going into suspend is a lightweight procedure then you may want
>> > to prevent it.
>> >
>>
>> We can for sure enable autosuspend for the sdmmc device, although as
>> soon as an SD card will be detected the rtsx driver will increase the
>> runtime PM usage count. The count is decreased when the card is
>> removed (or failed to be initialized), thus runtime suspend is
>> prevented as long as there is a functional card inserted.
>
> Which means that autosuspend matters only when a card isn't present,
> and the host is polled every second or so to see whether a card has
> been inserted.
>
> Under those circumstances you probably don't want to use autosuspend.
> That is, resuming before each poll and suspending afterward may use
> less energy than staying at full power all the time.
>
>> I am wondering what you think would be a good autosuspend timeout in
>> this case? It seems to me that the only thing the rtsx driver really
>> care about is to tell the parent device that it needs to be runtime
>> resumed during a certain timeframe, more or less it would like to
>> inherit the parents settings.
>>
>> Other mmc hosts, not being usb-mmc devices, are using an autosuspend
>> timeout of ~50-100ms but that doesn't seem like good value here,
>> right?
>
> Well, if you decide to let the device go into runtime suspend between
> polls then there's no reason to use autosuspend at all.  Once a poll
> has ended, you know there won't be any more activity until the next
> poll.

We could change that, as currently the approach in the mmc core isn't
that sophisticated. I even think this has been discussed earlier for
the very similar reasons regards polling card detect mode.

I guess the main reason to why we yet have changed this, is because
mmc host drivers are using an autosuspend timeout of ~50-100 ms, so in
the end it haven't been such a big deal.

>
> On the other hand, if you decide to keep the device at full power all
> the time during polling, then any autosuspend timeout larger than 1000
> ms would do what you want.
>
> Mostly I'm concerned about how this will interact with the USB runtime
> PM.  The thing is, suspending the sdmmc device doesn't save any energy,
> whereas suspending the USB device does.

Yes, I agree.

My concern is also 2s autosuspend timeout which is set for the usb
device. Somehow I feel we need to be able "share" more information
between a parent-child relationship, in this case between the sdmmc
device and the usb device.

An observation I made, is when the sdmmc device gets runtime resumed
(pm_runtime_get_sync()), the parent device (the usb device) may also
become runtime resumed (unless it's already). In this sequence, but
*only* when actually runtime resuming the usb device, the runtime PM
core decides to update the last busy mark for the usb device. Should
it really do that?

Moreover, I am curious about the 2s usb timeout. Why isn't that chosen
to something like ~100ms instead? Is there is a long latency to
runtime resume the usb device or because we fear to wear out the HW,
which may be powered on/off too frequently?

If we assume that the usb device shouldn't be used with a timeout less
than 2s, then I think we have two options:

*) As the mmc polling timeout is 1s, there is really no point in
trying to runtime suspend the usb device, it may just be left runtime
resumed all the time. Wasting power, of course!
**) Add an interface to allow dynamically changes of the mmc polling
timeout to better suit the current user.

[...]

Kind regards
Uffe

^ permalink raw reply

* [PATCH] mmc: card: do away with indirection pointer
From: Linus Walleij @ 2016-09-20  9:33 UTC (permalink / raw)
  To: linux-mmc, Ulf Hansson; +Cc: Linus Walleij

We have enough vtables in the kernel as it is, we don't need
this one to create even more artificial separation of concerns.

As is proved by the Makefile:

obj-$(CONFIG_MMC_BLOCK)         += mmc_block.o
mmc_block-objs                  := block.o queue.o

block.c and queue.c are baked into the same mmc_block.o object.
So why would one of these objects access a function in the
other object by dereferencing a pointer?

Create a new block.h header file for the single shared function
from block to queue and remove the function pointer and just
call the queue request function.

Apart from making the code more readable, this also makes link
optimizations possible and probably speeds up the call as well.

Signed-off-by: Linus Walleij <linus.walleij@linaro.org>
---
 drivers/mmc/card/block.c | 3 +--
 drivers/mmc/card/queue.c | 4 +++-
 drivers/mmc/card/queue.h | 2 --
 3 files changed, 4 insertions(+), 5 deletions(-)

diff --git a/drivers/mmc/card/block.c b/drivers/mmc/card/block.c
index 2206d4477dbb..15acf96147f3 100644
--- a/drivers/mmc/card/block.c
+++ b/drivers/mmc/card/block.c
@@ -2144,7 +2144,7 @@ static int mmc_blk_issue_rw_rq(struct mmc_queue *mq, struct request *rqc)
 	return 0;
 }
 
-static int mmc_blk_issue_rq(struct mmc_queue *mq, struct request *req)
+int mmc_blk_issue_rq(struct mmc_queue *mq, struct request *req)
 {
 	int ret;
 	struct mmc_blk_data *md = mq->data;
@@ -2265,7 +2265,6 @@ again:
 	if (ret)
 		goto err_putdisk;
 
-	md->queue.issue_fn = mmc_blk_issue_rq;
 	md->queue.data = md;
 
 	md->disk->major	= MMC_BLOCK_MAJOR;
diff --git a/drivers/mmc/card/queue.c b/drivers/mmc/card/queue.c
index 708057261b38..8037f73a109a 100644
--- a/drivers/mmc/card/queue.c
+++ b/drivers/mmc/card/queue.c
@@ -19,7 +19,9 @@
 
 #include <linux/mmc/card.h>
 #include <linux/mmc/host.h>
+
 #include "queue.h"
+#include "block.h"
 
 #define MMC_QUEUE_BOUNCESZ	65536
 
@@ -68,7 +70,7 @@ static int mmc_queue_thread(void *d)
 			bool req_is_special = mmc_req_is_special(req);
 
 			set_current_state(TASK_RUNNING);
-			mq->issue_fn(mq, req);
+			mmc_blk_issue_rq(mq, req);
 			cond_resched();
 			if (mq->flags & MMC_QUEUE_NEW_REQUEST) {
 				mq->flags &= ~MMC_QUEUE_NEW_REQUEST;
diff --git a/drivers/mmc/card/queue.h b/drivers/mmc/card/queue.h
index fee5e1271465..3c15a75bae86 100644
--- a/drivers/mmc/card/queue.h
+++ b/drivers/mmc/card/queue.h
@@ -57,8 +57,6 @@ struct mmc_queue {
 	unsigned int		flags;
 #define MMC_QUEUE_SUSPENDED	(1 << 0)
 #define MMC_QUEUE_NEW_REQUEST	(1 << 1)
-
-	int			(*issue_fn)(struct mmc_queue *, struct request *);
 	void			*data;
 	struct request_queue	*queue;
 	struct mmc_queue_req	mqrq[2];
-- 
2.7.4


^ permalink raw reply related

* Re: [PATCH v3 2/6] mmc: rtsx_pci: use new macro for R1 without CRC
From: Jaehoon Chung @ 2016-09-20  9:29 UTC (permalink / raw)
  To: Wolfram Sang, linux-mmc; +Cc: linux-renesas-soc, Simon Horman, Dirk Behme
In-Reply-To: <20160919205750.4766-3-wsa+renesas@sang-engineering.com>

Hi Wolfram,

Add the commit message.

On 09/20/2016 05:57 AM, Wolfram Sang wrote:
> Signed-off-by: Wolfram Sang <wsa+renesas@sang-engineering.com>
> ---
>  drivers/mmc/host/rtsx_pci_sdmmc.c | 2 +-
>  1 file changed, 1 insertion(+), 1 deletion(-)
> 
> diff --git a/drivers/mmc/host/rtsx_pci_sdmmc.c b/drivers/mmc/host/rtsx_pci_sdmmc.c
> index 396c9b7e4121b0..3ccaa1415f33b2 100644
> --- a/drivers/mmc/host/rtsx_pci_sdmmc.c
> +++ b/drivers/mmc/host/rtsx_pci_sdmmc.c
> @@ -126,7 +126,7 @@ static int sd_response_type(struct mmc_command *cmd)
>  		return SD_RSP_TYPE_R0;
>  	case MMC_RSP_R1:
>  		return SD_RSP_TYPE_R1;
> -	case MMC_RSP_R1 & ~MMC_RSP_CRC:
> +	case MMC_RSP_R1_NO_CRC:
>  		return SD_RSP_TYPE_R1 | SD_NO_CHECK_CRC7;
>  	case MMC_RSP_R1B:
>  		return SD_RSP_TYPE_R1b;
> 


^ permalink raw reply

* Re: [PATCH v3 1/6] mmc: add define for R1 response without CRC
From: Jaehoon Chung @ 2016-09-20  9:25 UTC (permalink / raw)
  To: Wolfram Sang, linux-mmc; +Cc: linux-renesas-soc, Simon Horman, Dirk Behme
In-Reply-To: <20160919205750.4766-2-wsa+renesas@sang-engineering.com>

Hi Wolfram,

On 09/20/2016 05:57 AM, Wolfram Sang wrote:
> The core uses it for polling. Give drivers a proper define handle this
> case like for other response types.
> 
> Signed-off-by: Wolfram Sang <wsa+renesas@sang-engineering.com>
> ---
>  include/linux/mmc/core.h | 3 +++
>  1 file changed, 3 insertions(+)
> 
> diff --git a/include/linux/mmc/core.h b/include/linux/mmc/core.h
> index b01e77de1a74de..4caee099b63a28 100644
> --- a/include/linux/mmc/core.h
> +++ b/include/linux/mmc/core.h
> @@ -55,6 +55,9 @@ struct mmc_command {
>  #define MMC_RSP_R6	(MMC_RSP_PRESENT|MMC_RSP_CRC|MMC_RSP_OPCODE)
>  #define MMC_RSP_R7	(MMC_RSP_PRESENT|MMC_RSP_CRC|MMC_RSP_OPCODE)
>  
> +/* Can be used by core to poll after switch to MMC HS mode */
> +#define MMC_RSP_R1_NO_CRC	(MMC_RSP_PRESENT|MMC_RSP_OPCODE)

MMC_RSP_RQ_NO_CRC is described at Specification?

Best Regards,
Jaehoon Chung

> +
>  #define mmc_resp_type(cmd)	((cmd)->flags & (MMC_RSP_PRESENT|MMC_RSP_136|MMC_RSP_CRC|MMC_RSP_BUSY|MMC_RSP_OPCODE))
>  
>  /*
> 


^ permalink raw reply

* Re: mmc: dw_mmc: log spamming
From: Jaehoon Chung @ 2016-09-20  6:30 UTC (permalink / raw)
  To: Tobias Jakobi, sw0312.kim; +Cc: Ulf Hansson, linux-mmc@vger.kernel.org
In-Reply-To: <57DFB75A.3030106@math.uni-bielefeld.de>

Hi Tobias,

On 09/19/2016 07:00 PM, Tobias Jakobi wrote:
> Hey Jaehoon,
> 
> 
> Jaehoon Chung wrote:
>> Hi Tobias,
>>
>> On 09/19/2016 04:41 PM, Tobias Jakobi wrote:
>>> Hello Jaehoon,
>>>
>>>
>>> Jaehoon Chung wrote:
>>>> Hi Tobias,
>>>>
>>>> CC'd mmc mailing.
>>>>
>>>> On 09/19/2016 10:03 AM, Seung-Woo Kim wrote:
>>>>> Hello Jaehoon,
>>>>>
>>>>> On 2016년 09월 19일 09:32, Jaehoon Chung wrote:
>>>>>> Hi Tobias,
>>>>>>
>>>>>> On 09/16/2016 02:29 AM, Tobias Jakobi wrote:
>>>>>>> Hello everyone,
>>>>>>>
>>>>>>> I'm experiencing massive kernel log spamming by dw_mmc, the commit that
>>>>>>> causes this is the following one.
>>>>>>>
>>>>>>> 65257a0deed5aee66b4e3708944f0be62a64cabc
>>>>>>>
>>>>>>> https://git.kernel.org/cgit/linux/kernel/git/torvalds/linux.git/commit/?id=65257a0deed5aee66b4e3708944f0be62a64cabc
>>>>>>>
>>>>>>> I've briefly checked the commit and I think the rationale behind
>>>>>>> removing the check is incorrect. While MMC_CLKGATE was certainly
>>>>>>> removed, runtime PM has "replaced" it (the commit
>>>>>>> 9eadcc0581a8ccaf4c2378aa1c193fb164304f1d even mentions this).
>>>>>>>
>>>>>>> This is on an Exynos4412 board, kernel is 4.8-rc6. Nothing is connected
>>>>>>> to the eMMC connector.
>>>>>>
>>>>>> Thanks for reporting this.
>>>>>>
>>>>>> Seung-Woo, Could you check your patch? I will also check this.
>>>>
>>>> Did you test after reverting this commit? or previous version is working fine?
>>> yes, reverting the commit fixes the log spamming. I'm aware though that
>>> the check is more of less broken (undefined behaviour because of
>>> bit-shifting with large values), so I have also tried the following
>>> thing: Just checking against 'div' instead of 'clock << div'. Anyway,
>>> this also works for me.
>>>
>>>
>>>> Which exynos4412 board do you use? I think it's related with "broken-cd".
>>>> (If you can share which board and dts you are using, we can check in more detail.)
>>> This is an Odroid-X2 board.
>>>
>>
>> When i have checked on odroid-u3, it's reproduced very easy.
> good to hear! I also worry that I'm the only one with these issues on
> the X2. :)
> 
> Anyway, do you seen any other message on the U3 that are related to the
> eMMC. I'm asking because I have also experience some warning/error from
> the regulator subsystem:
>> Sep 15 17:19:56 chidori kernel: [    4.976690] vddf_emmc_2.85V: voltage operation not allowed
>> Sep 15 17:19:56 chidori kernel: [    4.995417] mmc_host mmc1: Bus speed (slot 0) = 50000000Hz (slot req 400000Hz, actual 396825HZ div = 63)
> 
> I'm sending you a patch shortly which fixes the warning, but I'm not
> sure if this is the right approach. Maybe you can take a look?

I didn't see this..what is vddf_emmc_2.85V? I can't find this anywhere.

> 
> 
>>>> As you mentioned, you didn't insert the eMMC card on board.
>>>> Then it should be polling whether card is inserted/removed. (If broken-cd is set...)
>>> I can check the callstack leading to dw_mci_setup_bus() I guess. Would
>>> that should make it easier for you guys to understand where the issue
>>> originates from?
>>
>> Not need to share the callstack. Because i understood what is problem.
>> But checking 'div' instead of 'clock << div' should also have the unexpected behavior.
> Why is that? If I understood the initial problem correctly, then
> shifting with 'div' produces undefined behaviour since we don't know an
> upper bound for it. That's not the case if we just compare old and new
> divisor.

This problem is because of "polling" method. ("broken-cd" property in device-tree)
If can't find the eMMC card, mmc_rescan_try_freq() is running four times.
Because it's looping with freqs array size in mmc_rescan().
At that time, clock and current_speed is 400KHz..and if didn't find any cards, clock is set to 0.
After HZ, mmc_rescan is running again..and repeat above behavior.

I didn't consider about this case. (removable case and polling method.)

I think we can fix the below code..Could you check the below?
I have tested with exynos4412/exynos5422 boards.
If there is no issue, i will send the patch. Or if there is a problem, let me know, plz.


diff --git a/drivers/mmc/host/dw_mmc.c b/drivers/mmc/host/dw_mmc.c
index 22dacae..41306d1 100644
--- a/drivers/mmc/host/dw_mmc.c
+++ b/drivers/mmc/host/dw_mmc.c
@@ -1112,11 +1112,12 @@ static void dw_mci_setup_bus(struct dw_mci_slot *slot, bool force_clkinit)

                div = (host->bus_hz != clock) ? DIV_ROUND_UP(div, 2) : 0;

-               dev_info(&slot->mmc->class_dev,
-                        "Bus speed (slot %d) = %dHz (slot req %dHz, actual %dHZ div = %d)\n",
-                        slot->id, host->bus_hz, clock,
-                        div ? ((host->bus_hz / div) >> 1) :
-                        host->bus_hz, div);
+               if (clock != slot->__clk_old || force_clkinit)
+                       dev_info(&slot->mmc->class_dev,
+                                "Bus speed (slot %d) = %dHz (slot req %dHz, actual %dHZ div = %d)\n",
+                                slot->id, host->bus_hz, clock,
+                                div ? ((host->bus_hz / div) >> 1) :
+                                host->bus_hz, div);

                /* disable clock */
                mci_writel(host, CLKENA, 0);
@@ -1139,6 +1140,9 @@ static void dw_mci_setup_bus(struct dw_mci_slot *slot, bool force_clkinit)

                /* inform CIU */
                mci_send_cmd(slot, sdmmc_cmd_bits, 0);
+
+               /* keep the last clock value that requested from core */
+               slot->__clk_old = clock;
        }

        host->current_speed = clock;
diff --git a/drivers/mmc/host/dw_mmc.h b/drivers/mmc/host/dw_mmc.h
index 9e740bc..0f12e15 100644
--- a/drivers/mmc/host/dw_mmc.h
+++ b/drivers/mmc/host/dw_mmc.h
@@ -249,6 +249,8 @@ extern int dw_mci_resume(struct dw_mci *host);
  * @queue_node: List node for placing this node in the @queue list of
  *     &struct dw_mci.
  * @clock: Clock rate configured by set_ios(). Protected by host->lock.
+ * @__clk_old: The last clock value that requested from core.
+ *     Keeping track of this helps us to avoid spamming the console.
  * @flags: Random state bits associated with the slot.
  * @id: Number of this slot.
  * @sdio_id: Number of this slot in the SDIO interrupt registers.
@@ -263,6 +265,7 @@ struct dw_mci_slot {
        struct list_head        queue_node;

        unsigned int            clock;
+       unsigned int            __clk_old;

        unsigned long           flags;
 #define DW_MMC_CARD_PRESENT    0


Best Regards,
Jaehoon Chung

> 
> 
>> If needs to fix, i want to go ahead the correct way at this time.
>> Anyway, thanks for reporting this!
> 
> Also thanks, and let me know if I can test anything else.
> 
> With best wishes,
> Tobias
> 
> 
>> Best Regards,
>> Jaehoon Chung
>>
>>>
>>>
>>>> I think it's not related with runtime PM.
>>>>
>>>> Best Regards,
>>>> Jaehoon Chung
>>>
>>> WIth best wishes,
>>> Tobias
>>>
>>>
>>>
>>>
>>>>> Ok, I will check on Exynos4412 SpC boards.
>>>>>
>>>>> By the way, to check no condition case, when I posted after v2[1], I
>>>>> checked with Exynos5422 and Exynos5433 SoC boards and they didn't show
>>>>> duplicated log, so I agreed not to check condition for logging.
>>>>>
>>>>> [1] https://patchwork.kernel.org/patch/9182469/
>>>>>
>>>>> Best Regards,
>>>>> - Seung-Woo Kim
>>>>>
>>>>>>
>>>>>> Best Regards,
>>>>>> Jaehoon Chung
>>>>>>
>>>>>>>
>>>>>>> With best wishes,
>>>>>>> Tobias
>>>>>>>
>>>>>>>
>>>>>>>
>>>>>>
>>>>>>
>>>>>>
>>>>>
>>>>
>>>
>>>
>>>
>>>
>>
> 
> 
> 
> 


^ permalink raw reply related

* Re: [PATCH v3 1/2] Documentation: mmc: sdhci-of-arasan: add description of power domain
From: Ziyuan Xu @ 2016-09-20  2:29 UTC (permalink / raw)
  To: Ulf Hansson
  Cc: Heiko Stübner, Rob Herring,
	devicetree-u79uwXL29TY76Z2rM5mHXA@public.gmane.org,
	open list:ARM/Rockchip SoC..., linux-mmc, Shawn Lin,
	Doug Anderson, zhangqing
In-Reply-To: <CAPDyKFrgdsppLg9RkCsSh4nhjUiJR6D4u+E8qnzfbtNxhwLOmw-JsoAwUIsXosN+BqQ9rBEUg@public.gmane.org>

hi Ulf,


On 2016年09月17日 16:49, Ulf Hansson wrote:
> On 17 September 2016 at 04:32, Ziyuan Xu <xzy.xu-TNX95d0MmH7DzftRWevZcw@public.gmane.org> wrote:
>> Add power domain as a optional property for sdhci-of-arasan, which can
>> be turned off in the so-called unused condition, such as suspend and
>> remove. Aim to lower power requirements.
> I assume runtime suspend make sense as well?

Sorry to reply this mail after a long time.

On rk3399 platform, there is a eMMC-phy which is in charge of eMMC's 
logic interface. We should guarantee that phy is ready before cmd/data 
transfer.
I mean that I need to consider about the resume latency, so that I can 
land RPM for arasan-of-sdhci.
I intend to use auto-suspend for arasan-of-sdhci.

>
> Moreover, perhaps you could share a little information here about what
> needs to be done at the driver level which controls the mmc IP. More
> precisely, what do you need to do at runtime_suspend, suspend,
> runtime_resume, and resume?

Invoke sdhci_runtime_suspend_host, disable relevant clock in 
rumtime_suspend, deprocessing in runtime_resume.
Is that ok?

>
> Kind regards
> Uffe
>
>> Signed-off-by: Ziyuan Xu <xzy.xu-TNX95d0MmH7DzftRWevZcw@public.gmane.org>
>>
>> Acked-by: Rob Herring <robh-DgEjT+Ai2ygdnm+yROfE0A@public.gmane.org>
>> ---
>>
>> Changes in v3:
>> - fix a typo
>> - add Rob's ack-tag
>>
>> Changes in v2:
>> - fix a typo
>> - add an example code for power-domain
>>
>>   Documentation/devicetree/bindings/mmc/arasan,sdhci.txt | 7 ++++++-
>>   1 file changed, 6 insertions(+), 1 deletion(-)
>>
>> diff --git a/Documentation/devicetree/bindings/mmc/arasan,sdhci.txt b/Documentation/devicetree/bindings/mmc/arasan,sdhci.txt
>> index 3404afa..eb84d29 100644
>> --- a/Documentation/devicetree/bindings/mmc/arasan,sdhci.txt
>> +++ b/Documentation/devicetree/bindings/mmc/arasan,sdhci.txt
>> @@ -1,12 +1,14 @@
>>   Device Tree Bindings for the Arasan SDHCI Controller
>>
>> -  The bindings follow the mmc[1], clock[2], interrupt[3] and phy[4] bindings.
>> +  The bindings follow the mmc[1], clock[2], interrupt[3], phy[4] and power
>> +  domain[5] bindings.
>>     Only deviations are documented here.
>>
>>     [1] Documentation/devicetree/bindings/mmc/mmc.txt
>>     [2] Documentation/devicetree/bindings/clock/clock-bindings.txt
>>     [3] Documentation/devicetree/bindings/interrupt-controller/interrupts.txt
>>     [4] Documentation/devicetree/bindings/phy/phy-bindings.txt
>> +  [5] Documentation/devicetree/bindings/power/power_domain.txt
>>
>>   Required Properties:
>>     - compatible: Compatibility string.  One of:
>> @@ -36,6 +38,8 @@ Optional Properties:
>>     - #clock-cells: If specified this should be the value <0>.  With this property
>>       in place we will export a clock representing the Card Clock.  This clock
>>       is expected to be consumed by our PHY.  You must also specify
>> +  - power-domains: A phandle and PM domain as specifier defined by bindings
>> +    of the power controller specified by phandle.
>>
>>   Example:
>>          sdhci@e0100000 {
>> @@ -71,5 +75,6 @@ Example:
>>                  phys = <&emmc_phy>;
>>                  phy-names = "phy_arasan";
>>                  #clock-cells = <0>;
>> +               power-domains = <&power RK3399_PD_EMMC>;
>>                  status = "disabled";
>>          };
>> --
>> 2.9.2
>>
>>
>> --
>> To unsubscribe from this list: send the line "unsubscribe linux-mmc" in
>> the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
>> More majordomo info at  http://vger.kernel.org/majordomo-info.html
> --
> To unsubscribe from this list: send the line "unsubscribe linux-mmc" in
> the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html
>
>
>


--
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 v3 5/6] arm64: dts: r8a7795: salvator: enable on-board eMMC
From: Wolfram Sang @ 2016-09-19 20:57 UTC (permalink / raw)
  To: linux-mmc; +Cc: linux-renesas-soc, Simon Horman, Dirk Behme, Wolfram Sang
In-Reply-To: <20160919205750.4766-1-wsa+renesas@sang-engineering.com>

Signed-off-by: Wolfram Sang <wsa+renesas@sang-engineering.com>
---
 arch/arm64/boot/dts/renesas/r8a7795-salvator-x.dts | 43 ++++++++++++++++++++++
 1 file changed, 43 insertions(+)

diff --git a/arch/arm64/boot/dts/renesas/r8a7795-salvator-x.dts b/arch/arm64/boot/dts/renesas/r8a7795-salvator-x.dts
index 8964b1ea3cea69..8e98aeae54c066 100644
--- a/arch/arm64/boot/dts/renesas/r8a7795-salvator-x.dts
+++ b/arch/arm64/boot/dts/renesas/r8a7795-salvator-x.dts
@@ -62,6 +62,24 @@
 		clock-frequency = <24576000>;
 	};
 
+	reg_1p8v: regulator0 {
+		compatible = "regulator-fixed";
+		regulator-name = "fixed-1.8V";
+		regulator-min-microvolt = <1800000>;
+		regulator-max-microvolt = <1800000>;
+		regulator-boot-on;
+		regulator-always-on;
+	};
+
+	reg_3p3v: regulator1 {
+		compatible = "regulator-fixed";
+		regulator-name = "fixed-3.3V";
+		regulator-min-microvolt = <3300000>;
+		regulator-max-microvolt = <3300000>;
+		regulator-boot-on;
+		regulator-always-on;
+	};
+
 	vcc_sdhi0: regulator-vcc-sdhi0 {
 		compatible = "regulator-fixed";
 
@@ -246,6 +264,18 @@
 		power-source = <1800>;
 	};
 
+	sdhi2_pins: sd2 {
+		groups = "sdhi2_data8", "sdhi2_ctrl";
+		function = "sdhi2";
+		power-source = <3300>;
+	};
+
+	sdhi2_pins_uhs: sd2_uhs {
+		groups = "sdhi2_data8", "sdhi2_ctrl";
+		function = "sdhi2";
+		power-source = <1800>;
+	};
+
 	sdhi3_pins: sd3 {
 		groups = "sdhi3_data4", "sdhi3_ctrl";
 		function = "sdhi3";
@@ -398,6 +428,19 @@
 	status = "okay";
 };
 
+&sdhi2 {
+	/* used for on-board 8bit eMMC */
+	pinctrl-0 = <&sdhi2_pins>;
+	pinctrl-1 = <&sdhi2_pins_uhs>;
+	pinctrl-names = "default", "state_uhs";
+
+	vmmc-supply = <&reg_3p3v>;
+	vqmmc-supply = <&reg_1p8v>;
+	bus-width = <8>;
+	non-removable;
+	status = "okay";
+};
+
 &sdhi3 {
 	pinctrl-0 = <&sdhi3_pins>;
 	pinctrl-1 = <&sdhi3_pins_uhs>;
-- 
2.9.3


^ permalink raw reply related

* [PATCH v3 1/6] mmc: add define for R1 response without CRC
From: Wolfram Sang @ 2016-09-19 20:57 UTC (permalink / raw)
  To: linux-mmc; +Cc: linux-renesas-soc, Simon Horman, Dirk Behme, Wolfram Sang
In-Reply-To: <20160919205750.4766-1-wsa+renesas@sang-engineering.com>

The core uses it for polling. Give drivers a proper define handle this
case like for other response types.

Signed-off-by: Wolfram Sang <wsa+renesas@sang-engineering.com>
---
 include/linux/mmc/core.h | 3 +++
 1 file changed, 3 insertions(+)

diff --git a/include/linux/mmc/core.h b/include/linux/mmc/core.h
index b01e77de1a74de..4caee099b63a28 100644
--- a/include/linux/mmc/core.h
+++ b/include/linux/mmc/core.h
@@ -55,6 +55,9 @@ struct mmc_command {
 #define MMC_RSP_R6	(MMC_RSP_PRESENT|MMC_RSP_CRC|MMC_RSP_OPCODE)
 #define MMC_RSP_R7	(MMC_RSP_PRESENT|MMC_RSP_CRC|MMC_RSP_OPCODE)
 
+/* Can be used by core to poll after switch to MMC HS mode */
+#define MMC_RSP_R1_NO_CRC	(MMC_RSP_PRESENT|MMC_RSP_OPCODE)
+
 #define mmc_resp_type(cmd)	((cmd)->flags & (MMC_RSP_PRESENT|MMC_RSP_136|MMC_RSP_CRC|MMC_RSP_BUSY|MMC_RSP_OPCODE))
 
 /*
-- 
2.9.3


^ permalink raw reply related

* [PATCH v3 6/6] arm64: dts: r8a7796: salvator: enable on board eMMC
From: Wolfram Sang @ 2016-09-19 20:57 UTC (permalink / raw)
  To: linux-mmc; +Cc: linux-renesas-soc, Simon Horman, Dirk Behme, Wolfram Sang
In-Reply-To: <20160919205750.4766-1-wsa+renesas@sang-engineering.com>

Signed-off-by: Wolfram Sang <wsa+renesas@sang-engineering.com>
---
 arch/arm64/boot/dts/renesas/r8a7796-salvator-x.dts | 44 +++++++++++++++++++++-
 1 file changed, 43 insertions(+), 1 deletion(-)

diff --git a/arch/arm64/boot/dts/renesas/r8a7796-salvator-x.dts b/arch/arm64/boot/dts/renesas/r8a7796-salvator-x.dts
index 91440eb2237e72..8cda70db2745ae 100644
--- a/arch/arm64/boot/dts/renesas/r8a7796-salvator-x.dts
+++ b/arch/arm64/boot/dts/renesas/r8a7796-salvator-x.dts
@@ -31,6 +31,24 @@
 		reg = <0x0 0x48000000 0x0 0x78000000>;
 	};
 
+	reg_1p8v: regulator0 {
+		compatible = "regulator-fixed";
+		regulator-name = "fixed-1.8V";
+		regulator-min-microvolt = <1800000>;
+		regulator-max-microvolt = <1800000>;
+		regulator-boot-on;
+		regulator-always-on;
+	};
+
+	reg_3p3v: regulator1 {
+		compatible = "regulator-fixed";
+		regulator-name = "fixed-3.3V";
+		regulator-min-microvolt = <3300000>;
+		regulator-max-microvolt = <3300000>;
+		regulator-boot-on;
+		regulator-always-on;
+	};
+
 	vcc_sdhi0: regulator-vcc-sdhi0 {
 		compatible = "regulator-fixed";
 
@@ -78,7 +96,6 @@
 		states = <3300000 1
 			  1800000 0>;
 	};
-
 };
 
 &pfc {
@@ -106,6 +123,18 @@
 		power-source = <1800>;
 	};
 
+	sdhi2_pins: sd2 {
+		groups = "sdhi2_data8", "sdhi2_ctrl";
+		function = "sdhi2";
+		power-source = <3300>;
+	};
+
+	sdhi2_pins_uhs: sd2_uhs {
+		groups = "sdhi2_data8", "sdhi2_ctrl";
+		function = "sdhi2";
+		power-source = <1800>;
+	};
+
 	sdhi3_pins: sd3 {
 		groups = "sdhi3_data4", "sdhi3_ctrl";
 		function = "sdhi3";
@@ -138,6 +167,19 @@
 	status = "okay";
 };
 
+&sdhi2 {
+	/* used for on-board 8bit eMMC */
+	pinctrl-0 = <&sdhi2_pins>;
+	pinctrl-1 = <&sdhi2_pins_uhs>;
+	pinctrl-names = "default", "state_uhs";
+
+	vmmc-supply = <&reg_3p3v>;
+	vqmmc-supply = <&reg_1p8v>;
+	bus-width = <8>;
+	non-removable;
+	status = "okay";
+};
+
 &sdhi3 {
 	pinctrl-0 = <&sdhi3_pins>;
 	pinctrl-1 = <&sdhi3_pins_uhs>;
-- 
2.9.3

^ permalink raw reply related

* [PATCH v3 4/6] mmc: tmio: add eMMC support
From: Wolfram Sang @ 2016-09-19 20:57 UTC (permalink / raw)
  To: linux-mmc; +Cc: linux-renesas-soc, Simon Horman, Dirk Behme, Wolfram Sang
In-Reply-To: <20160919205750.4766-1-wsa+renesas@sang-engineering.com>

We need to add R1 without CRC support, refactor the bus width routine a
little and extend a quirk check. To support "non-removable;" we need a
workaround which will be hopefully removed when reworking PM soon.

Signed-off-by: Wolfram Sang <wsa+renesas@sang-engineering.com>
---
 drivers/mmc/host/tmio_mmc.h     |  3 +++
 drivers/mmc/host/tmio_mmc_pio.c | 38 ++++++++++++++++++++++++++------------
 2 files changed, 29 insertions(+), 12 deletions(-)

diff --git a/drivers/mmc/host/tmio_mmc.h b/drivers/mmc/host/tmio_mmc.h
index 4b501f2d529f6e..637581faf756b1 100644
--- a/drivers/mmc/host/tmio_mmc.h
+++ b/drivers/mmc/host/tmio_mmc.h
@@ -79,6 +79,9 @@
 #define	CLK_CTL_DIV_MASK	0xff
 #define	CLK_CTL_SCLKEN		BIT(8)
 
+#define CARD_OPT_WIDTH8		BIT(13)
+#define CARD_OPT_WIDTH		BIT(15)
+
 #define TMIO_BBS		512		/* Boot block size */
 
 /* Definitions for values the CTRL_SDIO_STATUS register can take. */
diff --git a/drivers/mmc/host/tmio_mmc_pio.c b/drivers/mmc/host/tmio_mmc_pio.c
index 46b5a456243b84..a0f05eb4f34490 100644
--- a/drivers/mmc/host/tmio_mmc_pio.c
+++ b/drivers/mmc/host/tmio_mmc_pio.c
@@ -340,7 +340,9 @@ static int tmio_mmc_start_command(struct tmio_mmc_host *host, struct mmc_command
 
 	switch (mmc_resp_type(cmd)) {
 	case MMC_RSP_NONE: c |= RESP_NONE; break;
-	case MMC_RSP_R1:   c |= RESP_R1;   break;
+	case MMC_RSP_R1:
+	case MMC_RSP_R1_NO_CRC:
+			   c |= RESP_R1;   break;
 	case MMC_RSP_R1B:  c |= RESP_R1B;  break;
 	case MMC_RSP_R2:   c |= RESP_R2;   break;
 	case MMC_RSP_R3:   c |= RESP_R3;   break;
@@ -737,12 +739,13 @@ static int tmio_mmc_start_data(struct tmio_mmc_host *host,
 	pr_debug("setup data transfer: blocksize %08x  nr_blocks %d\n",
 		 data->blksz, data->blocks);
 
-	/* Some hardware cannot perform 2 byte requests in 4 bit mode */
-	if (host->mmc->ios.bus_width == MMC_BUS_WIDTH_4) {
+	/* Some hardware cannot perform 2 byte requests in 4/8 bit mode */
+	if (host->mmc->ios.bus_width == MMC_BUS_WIDTH_4 ||
+	    host->mmc->ios.bus_width == MMC_BUS_WIDTH_8) {
 		int blksz_2bytes = pdata->flags & TMIO_MMC_BLKSZ_2BYTES;
 
 		if (data->blksz < 2 || (data->blksz < 4 && !blksz_2bytes)) {
-			pr_err("%s: %d byte block unsupported in 4 bit mode\n",
+			pr_err("%s: %d byte block unsupported in 4/8 bit mode\n",
 			       mmc_hostname(host->mmc), data->blksz);
 			return -EINVAL;
 		}
@@ -922,14 +925,16 @@ static void tmio_mmc_power_off(struct tmio_mmc_host *host)
 static void tmio_mmc_set_bus_width(struct tmio_mmc_host *host,
 				unsigned char bus_width)
 {
-	switch (bus_width) {
-	case MMC_BUS_WIDTH_1:
-		sd_ctrl_write16(host, CTL_SD_MEM_CARD_OPT, 0x80e0);
-		break;
-	case MMC_BUS_WIDTH_4:
-		sd_ctrl_write16(host, CTL_SD_MEM_CARD_OPT, 0x00e0);
-		break;
-	}
+	u16 reg = sd_ctrl_read16(host, CTL_SD_MEM_CARD_OPT)
+				& ~(CARD_OPT_WIDTH | CARD_OPT_WIDTH8);
+
+	/* reg now applies to MMC_BUS_WIDTH_4 */
+	if (bus_width == MMC_BUS_WIDTH_1)
+		reg |= CARD_OPT_WIDTH;
+	else if (bus_width == MMC_BUS_WIDTH_8)
+		reg |= CARD_OPT_WIDTH8;
+
+	sd_ctrl_write16(host, CTL_SD_MEM_CARD_OPT, reg);
 }
 
 /* Set MMC clock / power.
@@ -1149,6 +1154,15 @@ int tmio_mmc_host_probe(struct tmio_mmc_host *_host,
 				  !mmc_card_is_removable(mmc) ||
 				  mmc->slot.cd_irq >= 0);
 
+	/*
+	 * On Gen2+, eMMC with NONREMOVABLE currently fails because native
+	 * hotplug gets disabled. It seems RuntimePM related yet we need further
+	 * research. Since we are planning a PM overhaul anyway, let's enforce
+	 * for now the device being active by enabling native hotplug always.
+	 */
+	if (pdata->flags & TMIO_MMC_MIN_RCAR2)
+		_host->native_hotplug = true;
+
 	if (tmio_mmc_clk_enable(_host) < 0) {
 		mmc->f_max = pdata->hclk;
 		mmc->f_min = mmc->f_max / 512;
-- 
2.9.3

^ permalink raw reply related

* [PATCH v3 3/6] mmc: rtsx_usb: use new macro for R1 without CRC
From: Wolfram Sang @ 2016-09-19 20:57 UTC (permalink / raw)
  To: linux-mmc; +Cc: linux-renesas-soc, Simon Horman, Dirk Behme, Wolfram Sang
In-Reply-To: <20160919205750.4766-1-wsa+renesas@sang-engineering.com>

Signed-off-by: Wolfram Sang <wsa+renesas@sang-engineering.com>
---
 drivers/mmc/host/rtsx_usb_sdmmc.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/drivers/mmc/host/rtsx_usb_sdmmc.c b/drivers/mmc/host/rtsx_usb_sdmmc.c
index 6c71fc9f76c7ec..4106295527b9d0 100644
--- a/drivers/mmc/host/rtsx_usb_sdmmc.c
+++ b/drivers/mmc/host/rtsx_usb_sdmmc.c
@@ -324,7 +324,7 @@ static void sd_send_cmd_get_rsp(struct rtsx_usb_sdmmc *host,
 	case MMC_RSP_R1:
 		rsp_type = SD_RSP_TYPE_R1;
 		break;
-	case MMC_RSP_R1 & ~MMC_RSP_CRC:
+	case MMC_RSP_R1_NO_CRC:
 		rsp_type = SD_RSP_TYPE_R1 | SD_NO_CHECK_CRC7;
 		break;
 	case MMC_RSP_R1B:
-- 
2.9.3

^ permalink raw reply related

* [PATCH v3 2/6] mmc: rtsx_pci: use new macro for R1 without CRC
From: Wolfram Sang @ 2016-09-19 20:57 UTC (permalink / raw)
  To: linux-mmc; +Cc: linux-renesas-soc, Simon Horman, Dirk Behme, Wolfram Sang
In-Reply-To: <20160919205750.4766-1-wsa+renesas@sang-engineering.com>

Signed-off-by: Wolfram Sang <wsa+renesas@sang-engineering.com>
---
 drivers/mmc/host/rtsx_pci_sdmmc.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/drivers/mmc/host/rtsx_pci_sdmmc.c b/drivers/mmc/host/rtsx_pci_sdmmc.c
index 396c9b7e4121b0..3ccaa1415f33b2 100644
--- a/drivers/mmc/host/rtsx_pci_sdmmc.c
+++ b/drivers/mmc/host/rtsx_pci_sdmmc.c
@@ -126,7 +126,7 @@ static int sd_response_type(struct mmc_command *cmd)
 		return SD_RSP_TYPE_R0;
 	case MMC_RSP_R1:
 		return SD_RSP_TYPE_R1;
-	case MMC_RSP_R1 & ~MMC_RSP_CRC:
+	case MMC_RSP_R1_NO_CRC:
 		return SD_RSP_TYPE_R1 | SD_NO_CHECK_CRC7;
 	case MMC_RSP_R1B:
 		return SD_RSP_TYPE_R1b;
-- 
2.9.3

^ permalink raw reply related

* [PATCH v3 0/6] tmio: add support for eMMC with 8 bit bus width
From: Wolfram Sang @ 2016-09-19 20:57 UTC (permalink / raw)
  To: linux-mmc; +Cc: linux-renesas-soc, Simon Horman, Dirk Behme, Wolfram Sang

This series enables SDHI instances on R-Car Gen3 to access eMMC with 8 bit bus
width. I think the patch descriptions speak for themselves.

I decided to not protect this new feature with a flag because it needs
specifically to be enabled by setting the bus width to 8. No legacy platform
does that.

Note that I decided to use the pattern that pinctrl-0 is 3.3v and
pinctrl-1 is 1.8v, although the eMMC is fixed at 1.8v. I tried a few ways to
only use pinctrl-0 being 1.8v here, but they all ended up to be confusing for
users IMO, so I sticked to the most consistent solution after all.

Changes since V2:

* correct node name for sd2_uhs in both devicetrees

Changes since V1:

* merged the two distinct series (drivers + DTS) into one: patches 1-4 are for
  Ulf, I will ping Simon for patches 5+6 when the time is ready.

* DTS now contains 'non-removable' and we have a software workaround for now.

These patches are based on top of Simon's sdr104-v7 patches but they apply to
current mmc/next as well. A branch can be found here:

git://git.kernel.org/pub/scm/linux/kernel/git/wsa/linux.git renesas/topic/sdhi-8bit-emmc

Please review, comment, apply...

   Wolfram

Wolfram Sang (6):
  mmc: add define for R1 response without CRC
  mmc: rtsx_pci: use new macro for R1 without CRC
  mmc: rtsx_usb: use new macro for R1 without CRC
  mmc: tmio: add eMMC support
  arm64: dts: r8a7795: salvator: enable on-board eMMC
  arm64: dts: r8a7796: salvator: enable on board eMMC

 arch/arm64/boot/dts/renesas/r8a7795-salvator-x.dts | 43 +++++++++++++++++++++
 arch/arm64/boot/dts/renesas/r8a7796-salvator-x.dts | 44 +++++++++++++++++++++-
 drivers/mmc/host/rtsx_pci_sdmmc.c                  |  2 +-
 drivers/mmc/host/rtsx_usb_sdmmc.c                  |  2 +-
 drivers/mmc/host/tmio_mmc.h                        |  3 ++
 drivers/mmc/host/tmio_mmc_pio.c                    | 38 +++++++++++++------
 include/linux/mmc/core.h                           |  3 ++
 7 files changed, 120 insertions(+), 15 deletions(-)

-- 
2.9.3

^ permalink raw reply

* Re: [PATCH v2 5/6] arm64: dts: r8a7795: salvator: enable on-board eMMC
From: Wolfram Sang @ 2016-09-19 20:29 UTC (permalink / raw)
  To: Sergei Shtylyov
  Cc: Wolfram Sang, linux-mmc, linux-renesas-soc, Simon Horman,
	Dirk Behme
In-Reply-To: <3abe65a3-0047-f39d-2639-b39b98081c42@cogentembedded.com>

[-- Attachment #1: Type: text/plain, Size: 411 bytes --]


> >+	sdhi2_pins: sd2 {
> >+		groups = "sdhi2_data8", "sdhi2_ctrl";
> >+		function = "sdhi2";
> >+		power-source = <3300>;
> >+	};
> >+
> >+	sdhi2_pins_uhs: sd2 {
> >+		groups = "sdhi2_data8", "sdhi2_ctrl";
> >+		function = "sdhi2";
> >+		power-source = <1800>;
> >+	};
> >+
> 
>    I'm afraid you're just overriding the props in the first "sd2" node with
> this...

Oops, thanks, will fix!


[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 819 bytes --]

^ permalink raw reply

* Re: [PATCH v2 4/6] mmc: tmio: add eMMC support
From: Wolfram Sang @ 2016-09-19 20:26 UTC (permalink / raw)
  To: Sergei Shtylyov
  Cc: Wolfram Sang, linux-mmc, linux-renesas-soc, Simon Horman,
	Dirk Behme
In-Reply-To: <ddb477a7-4b3c-e67a-22fc-93432c4c75cb@cogentembedded.com>

[-- Attachment #1: Type: text/plain, Size: 366 bytes --]


> >+	u16 reg = sd_ctrl_read16(host, CTL_SD_MEM_CARD_OPT)
> >+				& ~(CARD_OPT_WIDTH | CARD_OPT_WIDTH8);
> >+
> >+	/* reg now applies to MMC_BUS_WIDTH_4 */
> >+	if (bus_width == MMC_BUS_WIDTH_1)
> >+		reg |= CARD_OPT_WIDTH;
> >+	else if (bus_width == MMC_BUS_WIDTH_8)
> >+		reg |= CARD_OPT_WIDTH8;
> 
>    Why not *switch*?

Didn't look better to me.


[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 819 bytes --]

^ permalink raw reply

* [PATCH v9] mmc: OCTEON: Add host driver for OCTEON MMC controller.
From: Steven J. Hill @ 2016-09-19 20:24 UTC (permalink / raw)
  To: linux-mips, linux-mmc; +Cc: Ulf Hansson, Mark Rutland, David Daney

The OCTEON MMC controller is currently found on cn61XX and cn71XX
devices. Device parameters are configured from device tree data.
eMMC, MMC and SD devices are supported. Tested on Cavium CN7130.

Signed-off-by: Steven J. Hill <steven.hill@cavium.com>
Acked-by: David Daney <david.daney@cavium.com>

---
v9:
- Implemented changes found in mailing list discussions:

  http://marc.info/?l=linux-mmc&m=147196443305853&w=4
  http://marc.info/?l=linux-mmc&m=147198719513204&w=4
  http://marc.info/?l=linux-mmc&m=147198158311531&w=4

v8:
- Convert driver to use readq()/writeq() functions.
- Work around legacy u-boot device trees.
- Enable EMMC interrupts properly.
- Support DDR signalling. The timings are tighter, so
  there may be failures with some FDT settings.
- Quiesce the device by calling mmc_remove_host() and
  mmc_free_host() when unloading the driver.
- Set MIO_BOOT_CTL on cn70xx to select proper mmc mode
  which is part of acquiring the bus.
- Use of_property_read_u32() helper for cleaner device
  tree accesses.
- Properly implement multi-block DMA. The Octeon's DMA
  enginer cannot do scatter-gather.
- Add driver parameters to limit multi-block transfers.
- Use octeon_bootbus_sem.
- Improve GPIO support and make actual requests to use the
  GPIO lines before using and freeing them after.

Signed-off-by: Steven J. Hill <Steven.Hill@cavium.com>
---
 arch/mips/cavium-octeon/Makefile     |    1 +
 arch/mips/cavium-octeon/octeon-mmc.c |  117 ++++
 drivers/mmc/host/Kconfig             |   10 +
 drivers/mmc/host/Makefile            |    1 +
 drivers/mmc/host/octeon_mmc.c        | 1122 ++++++++++++++++++++++++++++++++++
 include/linux/mmc/octeon_mmc.h       |   93 +++
 6 files changed, 1344 insertions(+)
 create mode 100644 arch/mips/cavium-octeon/octeon-mmc.c
 create mode 100644 drivers/mmc/host/octeon_mmc.c
 create mode 100644 include/linux/mmc/octeon_mmc.h

diff --git a/arch/mips/cavium-octeon/Makefile b/arch/mips/cavium-octeon/Makefile
index 2a59265..5f09d26 100644
--- a/arch/mips/cavium-octeon/Makefile
+++ b/arch/mips/cavium-octeon/Makefile
@@ -18,3 +18,4 @@ obj-y += crypto/
 obj-$(CONFIG_MTD)		      += flash_setup.o
 obj-$(CONFIG_SMP)		      += smp.o
 obj-$(CONFIG_OCTEON_ILM)	      += oct_ilm.o
+obj-$(CONFIG_MMC_OCTEON)	      += octeon-mmc.o
diff --git a/arch/mips/cavium-octeon/octeon-mmc.c b/arch/mips/cavium-octeon/octeon-mmc.c
new file mode 100644
index 0000000..61ca1c6
--- /dev/null
+++ b/arch/mips/cavium-octeon/octeon-mmc.c
@@ -0,0 +1,117 @@
+/*
+ * Driver for MMC and SSD cards for Cavium OCTEON SOCs.
+ *
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License.  See the file "COPYING" in the main directory of this archive
+ * for more details.
+ *
+ * Copyright (C) 2012-2016 Cavium Inc.
+ */
+#include <linux/init.h>
+#include <linux/module.h>
+
+#include <linux/mmc/octeon_mmc.h>
+
+#define CVMX_MIO_BOOT_CTL      CVMX_ADD_IO_SEG(0x00011800000000D0ull)
+
+/*
+ * The functions below are used for the EMMC-17978 workaround.
+ *
+ * Due to an imperfection in the design of the MMC bus hardware,
+ * the 2nd to last cache block of a DMA read must be locked into the L2 Cache.
+ * Otherwise, data corruption may occur.
+ */
+
+static inline void *phys_to_ptr(u64 address)
+{
+	return (void *)(address | (1ull<<63)); /* XKPHYS */
+}
+
+/**
+ * Lock a single line into L2. The line is zeroed before locking
+ * to make sure no dram accesses are made.
+ *
+ * @addr   Physical address to lock
+ */
+static void l2c_lock_line(u64 addr)
+{
+	char *addr_ptr = phys_to_ptr(addr);
+	asm volatile (
+		"cache 31, %[line]"	/* Unlock the line */
+		:: [line] "m" (*addr_ptr));
+}
+
+/**
+ * Unlock a single line in the L2 cache.
+ *
+ * @addr	Physical address to unlock
+ *
+ * Return Zero on success
+ */
+static void l2c_unlock_line(u64 addr)
+{
+	char *addr_ptr = phys_to_ptr(addr);
+	asm volatile (
+		"cache 23, %[line]"	/* Unlock the line */
+		:: [line] "m" (*addr_ptr));
+}
+
+/**
+ * Locks a memory region in the L2 cache
+ *
+ * @start - start address to begin locking
+ * @len - length in bytes to lock
+ */
+void l2c_lock_mem_region(u64 start, u64 len)
+{
+	u64 end;
+
+	/* Round start/end to cache line boundaries */
+	end = ALIGN(start + len - 1, CVMX_CACHE_LINE_SIZE);
+	start = ALIGN(start, CVMX_CACHE_LINE_SIZE);
+
+	while (start <= end) {
+		l2c_lock_line(start);
+		start += CVMX_CACHE_LINE_SIZE;
+	}
+	asm volatile("sync");
+}
+
+/**
+ * Unlock a memory region in the L2 cache
+ *
+ * @start - start address to unlock
+ * @len - length to unlock in bytes
+ */
+void l2c_unlock_mem_region(u64 start, u64 len)
+{
+	u64 end;
+
+	/* Round start/end to cache line boundaries */
+	end = ALIGN(start + len - 1, CVMX_CACHE_LINE_SIZE);
+	start = ALIGN(start, CVMX_CACHE_LINE_SIZE);
+
+	while (start <= end) {
+		l2c_unlock_line(start);
+		start += CVMX_CACHE_LINE_SIZE;
+	}
+}
+
+void octeon_mmc_acquire_bus(struct octeon_mmc_host *host)
+{
+	if (!host->has_ciu3) {
+		/* Switch the MMC controller onto the bus. */
+		down(&octeon_bootbus_sem);
+		writeq(0, (void __iomem *)CVMX_MIO_BOOT_CTL);
+	} else {
+		down(&host->mmc_serializer);
+	}
+}
+
+void octeon_mmc_release_bus(struct octeon_mmc_host *host)
+{
+	if (!host->has_ciu3)
+		up(&octeon_bootbus_sem);
+	else
+		up(&host->mmc_serializer);
+}
diff --git a/drivers/mmc/host/Kconfig b/drivers/mmc/host/Kconfig
index 5274f50..63ac742 100644
--- a/drivers/mmc/host/Kconfig
+++ b/drivers/mmc/host/Kconfig
@@ -322,6 +322,16 @@ config MMC_SDHCI_IPROC

 	  If unsure, say N.

+config MMC_OCTEON
+	tristate "Cavium OCTEON Multimedia Card Interface support"
+	depends on CAVIUM_OCTEON_SOC
+	help
+	  This selects Cavium OCTEON Multimedia card Interface.
+	  If you have an OCTEON board with a Multimedia Card slot,
+	  say Y or M here.
+
+	  If unsure, say N.
+
 config MMC_MOXART
 	tristate "MOXART SD/MMC Host Controller support"
 	depends on ARCH_MOXART && MMC
diff --git a/drivers/mmc/host/Makefile b/drivers/mmc/host/Makefile
index e2bdaaf..ae6bfb7 100644
--- a/drivers/mmc/host/Makefile
+++ b/drivers/mmc/host/Makefile
@@ -21,6 +21,7 @@ obj-$(CONFIG_MMC_SDHCI_SPEAR)	+= sdhci-spear.o
 obj-$(CONFIG_MMC_WBSD)		+= wbsd.o
 obj-$(CONFIG_MMC_AU1X)		+= au1xmmc.o
 obj-$(CONFIG_MMC_MTK)		+= mtk-sd.o
+obj-$(CONFIG_MMC_OCTEON)	+= octeon_mmc.o
 obj-$(CONFIG_MMC_OMAP)		+= omap.o
 obj-$(CONFIG_MMC_OMAP_HS)	+= omap_hsmmc.o
 obj-$(CONFIG_MMC_ATMELMCI)	+= atmel-mci.o
diff --git a/drivers/mmc/host/octeon_mmc.c b/drivers/mmc/host/octeon_mmc.c
new file mode 100644
index 0000000..73f5155
--- /dev/null
+++ b/drivers/mmc/host/octeon_mmc.c
@@ -0,0 +1,1122 @@
+/*
+ * Driver for MMC and SSD cards for Cavium OCTEON SOCs.
+ *
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License.  See the file "COPYING" in the main directory of this archive
+ * for more details.
+ *
+ * Copyright (C) 2012-2016 Cavium Inc.
+ */
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/delay.h>
+#include <linux/interrupt.h>
+#include <linux/of_platform.h>
+#include <linux/gpio/consumer.h>
+
+#include <linux/mmc/slot-gpio.h>
+#include <linux/mmc/octeon_mmc.h>
+
+#define DRV_NAME	"octeon_mmc"
+
+/*
+ * The OCTEON MMC host hardware assumes that all commands have fixed
+ * command and response types.  These are correct if MMC devices are
+ * being used.  However, non-MMC devices like SD use command and
+ * response types that are unexpected by the host hardware.
+ *
+ * The command and response types can be overridden by supplying an
+ * XOR value that is applied to the type.  We calculate the XOR value
+ * from the values in this table and the flags passed from the MMC
+ * core.
+ */
+static struct octeon_mmc_cr_type octeon_mmc_cr_types[] = {
+	{0, 0},		/* CMD0 */
+	{0, 3},		/* CMD1 */
+	{0, 2},		/* CMD2 */
+	{0, 1},		/* CMD3 */
+	{0, 0},		/* CMD4 */
+	{0, 1},		/* CMD5 */
+	{0, 1},		/* CMD6 */
+	{0, 1},		/* CMD7 */
+	{1, 1},		/* CMD8 */
+	{0, 2},		/* CMD9 */
+	{0, 2},		/* CMD10 */
+	{1, 1},		/* CMD11 */
+	{0, 1},		/* CMD12 */
+	{0, 1},		/* CMD13 */
+	{1, 1},		/* CMD14 */
+	{0, 0},		/* CMD15 */
+	{0, 1},		/* CMD16 */
+	{1, 1},		/* CMD17 */
+	{1, 1},		/* CMD18 */
+	{3, 1},		/* CMD19 */
+	{2, 1},		/* CMD20 */
+	{0, 0},		/* CMD21 */
+	{0, 0},		/* CMD22 */
+	{0, 1},		/* CMD23 */
+	{2, 1},		/* CMD24 */
+	{2, 1},		/* CMD25 */
+	{2, 1},		/* CMD26 */
+	{2, 1},		/* CMD27 */
+	{0, 1},		/* CMD28 */
+	{0, 1},		/* CMD29 */
+	{1, 1},		/* CMD30 */
+	{1, 1},		/* CMD31 */
+	{0, 0},		/* CMD32 */
+	{0, 0},		/* CMD33 */
+	{0, 0},		/* CMD34 */
+	{0, 1},		/* CMD35 */
+	{0, 1},		/* CMD36 */
+	{0, 0},		/* CMD37 */
+	{0, 1},		/* CMD38 */
+	{0, 4},		/* CMD39 */
+	{0, 5},		/* CMD40 */
+	{0, 0},		/* CMD41 */
+	{2, 1},		/* CMD42 */
+	{0, 0},		/* CMD43 */
+	{0, 0},		/* CMD44 */
+	{0, 0},		/* CMD45 */
+	{0, 0},		/* CMD46 */
+	{0, 0},		/* CMD47 */
+	{0, 0},		/* CMD48 */
+	{0, 0},		/* CMD49 */
+	{0, 0},		/* CMD50 */
+	{0, 0},		/* CMD51 */
+	{0, 0},		/* CMD52 */
+	{0, 0},		/* CMD53 */
+	{0, 0},		/* CMD54 */
+	{0, 1},		/* CMD55 */
+	{0xff, 0xff},	/* CMD56 */
+	{0, 0},		/* CMD57 */
+	{0, 0},		/* CMD58 */
+	{0, 0},		/* CMD59 */
+	{0, 0},		/* CMD60 */
+	{0, 0},		/* CMD61 */
+	{0, 0},		/* CMD62 */
+	{0, 0}		/* CMD63 */
+};
+
+static struct octeon_mmc_cr_mods octeon_mmc_get_cr_mods(struct mmc_command *cmd)
+{
+	struct octeon_mmc_cr_type *cr;
+	u8 desired_ctype, hardware_ctype;
+	u8 desired_rtype, hardware_rtype;
+	struct octeon_mmc_cr_mods r;
+
+	desired_ctype = desired_rtype = 0;
+
+	cr = octeon_mmc_cr_types + (cmd->opcode & 0x3f);
+	hardware_ctype = cr->ctype;
+	hardware_rtype = cr->rtype;
+	if (cmd->opcode == MMC_GEN_CMD)
+		hardware_ctype = (cmd->arg & 1) ? 1 : 2;
+
+	switch (mmc_cmd_type(cmd)) {
+	case MMC_CMD_ADTC:
+		desired_ctype = (cmd->data->flags & MMC_DATA_WRITE) ? 2 : 1;
+		break;
+	case MMC_CMD_AC:
+	case MMC_CMD_BC:
+	case MMC_CMD_BCR:
+		desired_ctype = 0;
+		break;
+	}
+
+	switch (mmc_resp_type(cmd)) {
+	case MMC_RSP_NONE:
+		desired_rtype = 0;
+		break;
+	case MMC_RSP_R1:/* MMC_RSP_R5, MMC_RSP_R6, MMC_RSP_R7 */
+	case MMC_RSP_R1B:
+		desired_rtype = 1;
+		break;
+	case MMC_RSP_R2:
+		desired_rtype = 2;
+		break;
+	case MMC_RSP_R3: /* MMC_RSP_R4 */
+		desired_rtype = 3;
+		break;
+	}
+	r.ctype_xor = desired_ctype ^ hardware_ctype;
+	r.rtype_xor = desired_rtype ^ hardware_rtype;
+	return r;
+}
+
+static bool octeon_mmc_switch_val_changed(struct octeon_mmc_slot *slot,
+					  u64 new_val)
+{
+	/* Match BUS_ID, HS_TIMING, BUS_WIDTH, POWER_CLASS, CLK_HI, CLK_LO */
+	u64 m = 0x3001070fffffffffull;
+	return (slot->cached_switch & m) != (new_val & m);
+}
+
+static unsigned int octeon_mmc_timeout_to_wdog(struct octeon_mmc_slot *slot,
+					       unsigned int ns)
+{
+	u64 bt = (u64)slot->clock * (u64)ns;
+	return (unsigned int)(bt / 1000000000);
+}
+
+static irqreturn_t octeon_mmc_interrupt(int irq, void *dev_id)
+{
+	struct octeon_mmc_host *host = dev_id;
+	union cvmx_mio_emm_int emm_int;
+	struct mmc_request	*req;
+	bool host_done;
+	union cvmx_mio_emm_rsp_sts rsp_sts;
+	unsigned long flags = 0;
+
+	if (host->need_irq_handler_lock)
+		spin_lock_irqsave(&host->irq_handler_lock, flags);
+	else
+		__acquire(&host->irq_handler_lock);
+	emm_int.u64 = readq(host->base + OCT_MIO_EMM_INT);
+	req = host->current_req;
+	writeq(emm_int.u64, host->base + OCT_MIO_EMM_INT);
+
+	if (!req)
+		goto out;
+
+	rsp_sts.u64 = readq(host->base + OCT_MIO_EMM_RSP_STS);
+
+	if (host->dma_err_pending) {
+		host->current_req = NULL;
+		host->dma_err_pending = false;
+		req->done(req);
+		host_done = true;
+		goto no_req_done;
+	}
+
+	if (!host->dma_active && emm_int.s.buf_done && req->data) {
+		unsigned int type = (rsp_sts.u64 >> 7) & 3;
+
+		if (type == 1) {
+			/* Read */
+			int dbuf = rsp_sts.s.dbuf;
+			struct sg_mapping_iter *smi = &host->smi;
+			unsigned int data_len =
+				req->data->blksz * req->data->blocks;
+			unsigned int bytes_xfered;
+			u64 dat = 0;
+			int shift = -1;
+
+			/* Auto inc from offset zero */
+			writeq((u64)(0x10000 | (dbuf << 6)),
+			       host->base + OCT_MIO_EMM_BUF_IDX);
+
+			for (bytes_xfered = 0; bytes_xfered < data_len;) {
+				if (smi->consumed >= smi->length) {
+					if (!sg_miter_next(smi))
+						break;
+					smi->consumed = 0;
+				}
+				if (shift < 0) {
+					dat = readq(host->base +
+						    OCT_MIO_EMM_BUF_DAT);
+					shift = 56;
+				}
+
+				while (smi->consumed < smi->length &&
+					shift >= 0) {
+					((u8 *)(smi->addr))[smi->consumed] =
+						(dat >> shift) & 0xff;
+					bytes_xfered++;
+					smi->consumed++;
+					shift -= 8;
+				}
+			}
+			sg_miter_stop(smi);
+			req->data->bytes_xfered = bytes_xfered;
+			req->data->error = 0;
+		} else if (type == 2) {
+			/* write */
+			req->data->bytes_xfered = req->data->blksz *
+				req->data->blocks;
+			req->data->error = 0;
+		}
+	}
+	host_done = emm_int.s.cmd_done || emm_int.s.dma_done ||
+		emm_int.s.cmd_err || emm_int.s.dma_err;
+	if (host_done && req->done) {
+		if (rsp_sts.s.rsp_bad_sts ||
+		    rsp_sts.s.rsp_crc_err ||
+		    rsp_sts.s.rsp_timeout ||
+		    rsp_sts.s.blk_crc_err ||
+		    rsp_sts.s.blk_timeout ||
+		    rsp_sts.s.dbuf_err) {
+			req->cmd->error = -EILSEQ;
+		} else {
+			req->cmd->error = 0;
+		}
+
+		if (host->dma_active && req->data) {
+			req->data->error = 0;
+			req->data->bytes_xfered = req->data->blocks *
+				req->data->blksz;
+			if (!(req->data->flags & MMC_DATA_WRITE) &&
+				req->data->sg_len > 1) {
+				size_t r = sg_copy_from_buffer(req->data->sg,
+					req->data->sg_len, host->linear_buf,
+					req->data->bytes_xfered);
+				WARN_ON(r != req->data->bytes_xfered);
+			}
+		}
+		if (rsp_sts.s.rsp_val) {
+			u64 rsp_hi;
+			u64 rsp_lo = readq(host->base + OCT_MIO_EMM_RSP_LO);
+
+			switch (rsp_sts.s.rsp_type) {
+			case 1:
+			case 3:
+				req->cmd->resp[0] = (rsp_lo >> 8) & 0xffffffff;
+				req->cmd->resp[1] = 0;
+				req->cmd->resp[2] = 0;
+				req->cmd->resp[3] = 0;
+				break;
+			case 2:
+				req->cmd->resp[3] = rsp_lo & 0xffffffff;
+				req->cmd->resp[2] = (rsp_lo >> 32) & 0xffffffff;
+				rsp_hi = readq(host->base + OCT_MIO_EMM_RSP_HI);
+				req->cmd->resp[1] = rsp_hi & 0xffffffff;
+				req->cmd->resp[0] = (rsp_hi >> 32) & 0xffffffff;
+			default:
+				break;
+			}
+		}
+		if (emm_int.s.dma_err && rsp_sts.s.dma_pend) {
+			/* Try to clean up failed DMA */
+			union cvmx_mio_emm_dma emm_dma;
+
+			emm_dma.u64 = readq(host->base + OCT_MIO_EMM_DMA);
+			emm_dma.s.dma_val = 1;
+			emm_dma.s.dat_null = 1;
+			emm_dma.s.bus_id = rsp_sts.s.bus_id;
+			writeq(emm_dma.u64, host->base + OCT_MIO_EMM_DMA);
+			host->dma_err_pending = true;
+			host_done = false;
+			goto no_req_done;
+		}
+
+		host->current_req = NULL;
+		req->done(req);
+	}
+no_req_done:
+	if (host->n_minus_one) {
+		l2c_unlock_mem_region(host->n_minus_one, 512);
+		host->n_minus_one = 0;
+	}
+	if (host_done)
+		octeon_mmc_release_bus(host);
+out:
+	if (host->need_irq_handler_lock)
+		spin_unlock_irqrestore(&host->irq_handler_lock, flags);
+	else
+		__release(&host->irq_handler_lock);
+	return IRQ_RETVAL(emm_int.u64 != 0);
+}
+
+static void octeon_mmc_switch_to(struct octeon_mmc_slot	*slot)
+{
+	struct octeon_mmc_host *host = slot->host;
+	struct octeon_mmc_slot *old_slot;
+	union cvmx_mio_emm_switch sw;
+	union cvmx_mio_emm_sample samp;
+
+	octeon_mmc_acquire_bus(host);
+	if (slot->bus_id == host->last_slot)
+		goto out;
+
+	if (host->last_slot >= 0 && host->slot[host->last_slot]) {
+		old_slot = host->slot[host->last_slot];
+		old_slot->cached_switch = readq(host->base + OCT_MIO_EMM_SWITCH);
+		old_slot->cached_rca = readq(host->base + OCT_MIO_EMM_RCA);
+	}
+	writeq(slot->cached_rca, host->base + OCT_MIO_EMM_RCA);
+	sw.u64 = slot->cached_switch;
+	sw.s.bus_id = 0;
+	writeq(sw.u64, host->base + OCT_MIO_EMM_SWITCH);
+	sw.s.bus_id = slot->bus_id;
+	writeq(sw.u64, host->base + OCT_MIO_EMM_SWITCH);
+
+	samp.u64 = 0;
+	samp.s.cmd_cnt = slot->cmd_cnt;
+	samp.s.dat_cnt = slot->dat_cnt;
+	writeq(samp.u64, host->base + OCT_MIO_EMM_SAMPLE);
+out:
+	host->last_slot = slot->bus_id;
+}
+
+static void octeon_mmc_dma_request(struct mmc_host *mmc,
+				   struct mmc_request *mrq)
+{
+	struct octeon_mmc_slot	*slot;
+	struct octeon_mmc_host	*host;
+	struct mmc_command *cmd;
+	struct mmc_data *data;
+	union cvmx_mio_emm_int emm_int;
+	union cvmx_mio_emm_dma emm_dma;
+	union cvmx_mio_ndf_dma_cfg dma_cfg;
+
+	cmd = mrq->cmd;
+	if (mrq->data == NULL || mrq->data->sg == NULL || !mrq->data->sg_len ||
+	    mrq->stop == NULL || mrq->stop->opcode != MMC_STOP_TRANSMISSION) {
+		dev_err(&mmc->card->dev,
+			"Error: octeon_mmc_dma_request no data\n");
+		cmd->error = -EINVAL;
+		if (mrq->done)
+			mrq->done(mrq);
+		return;
+	}
+
+	slot = mmc_priv(mmc);
+	host = slot->host;
+
+	/* Only a single user of the bootbus at a time. */
+	octeon_mmc_switch_to(slot);
+
+	data = mrq->data;
+
+	if (data->timeout_ns)
+		writeq(octeon_mmc_timeout_to_wdog(slot, data->timeout_ns),
+		       host->base + OCT_MIO_EMM_WDOG);
+
+	WARN_ON(host->current_req);
+	host->current_req = mrq;
+
+	host->sg_idx = 0;
+
+	WARN_ON(data->blksz * data->blocks > host->linear_buf_size);
+
+	if ((data->flags & MMC_DATA_WRITE) && data->sg_len > 1) {
+		size_t r = sg_copy_to_buffer(data->sg, data->sg_len,
+			 host->linear_buf, data->blksz * data->blocks);
+		WARN_ON(data->blksz * data->blocks != r);
+	}
+
+	dma_cfg.u64 = 0;
+	dma_cfg.s.en = 1;
+	dma_cfg.s.rw = (data->flags & MMC_DATA_WRITE) ? 1 : 0;
+#ifdef __LITTLE_ENDIAN
+	dma_cfg.s.endian = 1;
+#endif
+	dma_cfg.s.size = ((data->blksz * data->blocks) / 8) - 1;
+	if (!host->big_dma_addr) {
+		if (data->sg_len > 1)
+			dma_cfg.s.adr = virt_to_phys(host->linear_buf);
+		else
+			dma_cfg.s.adr = sg_phys(data->sg);
+	}
+	writeq(dma_cfg.u64, host->ndf_base + OCT_MIO_NDF_DMA_CFG);
+	if (host->big_dma_addr) {
+		u64 addr;
+
+		if (data->sg_len > 1)
+			addr = virt_to_phys(host->linear_buf);
+		else
+			addr = sg_phys(data->sg);
+		writeq(addr, host->ndf_base + OCT_MIO_EMM_DMA_ADR);
+	}
+
+	/*
+	 * Our MMC host hardware does not issue single commands,
+	 * because that would require the driver and the MMC core
+	 * to do work to determine the proper sequence of commands.
+	 * Instead, our hardware is superior to most other MMC bus
+	 * hosts. The sequence of MMC commands required to execute
+	 * a transfer are issued automatically by the bus hardware.
+	 *
+	 * - David Daney <ddaney@cavium.com>
+	 */
+	emm_dma.u64 = 0;
+	emm_dma.s.bus_id = slot->bus_id;
+	emm_dma.s.dma_val = 1;
+	emm_dma.s.sector = mmc_card_blockaddr(mmc->card) ? 1 : 0;
+	emm_dma.s.rw = (data->flags & MMC_DATA_WRITE) ? 1 : 0;
+	if (mmc_card_mmc(mmc->card) ||
+	    (mmc_card_sd(mmc->card) &&
+		(mmc->card->scr.cmds & SD_SCR_CMD23_SUPPORT)))
+		emm_dma.s.multi = 1;
+	emm_dma.s.block_cnt = data->blocks;
+	emm_dma.s.card_addr = cmd->arg;
+
+	emm_int.u64 = 0;
+	emm_int.s.dma_done = 1;
+	emm_int.s.cmd_err = 1;
+	emm_int.s.dma_err = 1;
+
+	/* Clear the bit. */
+	writeq(emm_int.u64, host->base + OCT_MIO_EMM_INT);
+	if (!host->has_ciu3)
+		writeq(emm_int.u64, host->base + OCT_MIO_EMM_INT_EN);
+	host->dma_active = true;
+
+	if ((OCTEON_IS_MODEL(OCTEON_CN6XXX) ||
+		OCTEON_IS_MODEL(OCTEON_CNF7XXX)) &&
+	    cmd->opcode == MMC_WRITE_MULTIPLE_BLOCK &&
+	    (data->blksz * data->blocks) > 1024) {
+		host->n_minus_one = dma_cfg.s.adr +
+			(data->blksz * data->blocks) - 1024;
+		l2c_lock_mem_region(host->n_minus_one, 512);
+	}
+
+	/*
+	 * If we have a valid SD card in the slot, we
+	 * set the response bit mask to check for CRC
+	 * errors and timeouts only. Otherwise, use the
+	 * default power reset value.
+	 */
+	if (mmc->card && mmc_card_sd(mmc->card))
+		writeq(0x00b00000ull, host->base + OCT_MIO_EMM_STS_MASK);
+	else
+		writeq(0xe4390080ull, host->base + OCT_MIO_EMM_STS_MASK);
+	writeq(emm_dma.u64, host->base + OCT_MIO_EMM_DMA);
+}
+
+static void octeon_mmc_request(struct mmc_host *mmc, struct mmc_request *mrq)
+{
+	struct octeon_mmc_slot	*slot;
+	struct octeon_mmc_host	*host;
+	struct mmc_command *cmd;
+	union cvmx_mio_emm_int emm_int;
+	union cvmx_mio_emm_cmd emm_cmd;
+	struct octeon_mmc_cr_mods mods;
+
+	cmd = mrq->cmd;
+
+	if (cmd->opcode == MMC_READ_MULTIPLE_BLOCK ||
+		cmd->opcode == MMC_WRITE_MULTIPLE_BLOCK) {
+		octeon_mmc_dma_request(mmc, mrq);
+		return;
+	}
+
+	mods = octeon_mmc_get_cr_mods(cmd);
+
+	slot = mmc_priv(mmc);
+	host = slot->host;
+
+	/* Only a single user of the bootbus at a time. */
+	octeon_mmc_switch_to(slot);
+
+	WARN_ON(host->current_req);
+	host->current_req = mrq;
+
+	emm_int.u64 = 0;
+	emm_int.s.cmd_done = 1;
+	emm_int.s.cmd_err = 1;
+	if (cmd->data) {
+		if (cmd->data->flags & MMC_DATA_READ) {
+			sg_miter_start(&host->smi, mrq->data->sg,
+				       mrq->data->sg_len,
+				       SG_MITER_ATOMIC | SG_MITER_TO_SG);
+		} else {
+			struct sg_mapping_iter *smi = &host->smi;
+			unsigned int data_len =
+				mrq->data->blksz * mrq->data->blocks;
+			unsigned int bytes_xfered;
+			u64 dat = 0;
+			int shift = 56;
+			/*
+			 * Copy data to the xmit buffer before
+			 * issuing the command
+			 */
+			sg_miter_start(smi, mrq->data->sg,
+				       mrq->data->sg_len, SG_MITER_FROM_SG);
+			/* Auto inc from offset zero, dbuf zero */
+			writeq(0x10000ull, host->base + OCT_MIO_EMM_BUF_IDX);
+
+			for (bytes_xfered = 0; bytes_xfered < data_len;) {
+				if (smi->consumed >= smi->length) {
+					if (!sg_miter_next(smi))
+						break;
+					smi->consumed = 0;
+				}
+
+				while (smi->consumed < smi->length &&
+					shift >= 0) {
+
+					dat |= (u64)(((u8 *)(smi->addr))
+						[smi->consumed]) << shift;
+					bytes_xfered++;
+					smi->consumed++;
+					shift -= 8;
+				}
+				if (shift < 0) {
+					writeq(dat, host->base +
+					       OCT_MIO_EMM_BUF_DAT);
+					shift = 56;
+					dat = 0;
+				}
+			}
+			sg_miter_stop(smi);
+		}
+		if (cmd->data->timeout_ns)
+			writeq(octeon_mmc_timeout_to_wdog(slot,
+			       cmd->data->timeout_ns),
+			       host->base + OCT_MIO_EMM_WDOG);
+	} else {
+		writeq(((u64)slot->clock * 850ull) / 1000ull,
+		       host->base + OCT_MIO_EMM_WDOG);
+	}
+	/* Clear the bit. */
+	writeq(emm_int.u64, host->base + OCT_MIO_EMM_INT);
+	writeq(emm_int.u64, host->base + OCT_MIO_EMM_INT_EN);
+	host->dma_active = false;
+
+	emm_cmd.u64 = 0;
+	emm_cmd.s.cmd_val = 1;
+	emm_cmd.s.ctype_xor = mods.ctype_xor;
+	emm_cmd.s.rtype_xor = mods.rtype_xor;
+	if (mmc_cmd_type(cmd) == MMC_CMD_ADTC)
+		emm_cmd.s.offset = 64 -
+			((cmd->data->blksz * cmd->data->blocks) / 8);
+	emm_cmd.s.bus_id = slot->bus_id;
+	emm_cmd.s.cmd_idx = cmd->opcode;
+	emm_cmd.s.arg = cmd->arg;
+	writeq(0, host->base + OCT_MIO_EMM_STS_MASK);
+	writeq(emm_cmd.u64, host->base + OCT_MIO_EMM_CMD);
+}
+
+static void octeon_mmc_reset_bus(struct octeon_mmc_slot *slot)
+{
+	union cvmx_mio_emm_cfg emm_cfg;
+	union cvmx_mio_emm_switch emm_switch;
+	u64 wdog = 0;
+
+	emm_cfg.u64 = readq(slot->host->base + OCT_MIO_EMM_CFG);
+	emm_switch.u64 = readq(slot->host->base + OCT_MIO_EMM_SWITCH);
+	wdog = readq(slot->host->base + OCT_MIO_EMM_WDOG);
+
+	emm_switch.s.switch_exe = 0;
+	emm_switch.s.switch_err0 = 0;
+	emm_switch.s.switch_err1 = 0;
+	emm_switch.s.switch_err2 = 0;
+	emm_switch.s.bus_id = 0;
+	writeq(emm_switch.u64, slot->host->base + OCT_MIO_EMM_SWITCH);
+	emm_switch.s.bus_id = slot->bus_id;
+	writeq(emm_switch.u64, slot->host->base + OCT_MIO_EMM_SWITCH);
+
+	slot->cached_switch = emm_switch.u64;
+
+	msleep(20);
+
+	writeq(wdog, slot->host->base + OCT_MIO_EMM_WDOG);
+}
+
+static void octeon_mmc_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
+{
+	struct octeon_mmc_slot	*slot;
+	struct octeon_mmc_host	*host;
+	int bus_width;
+	int clock;
+	int power_class = 10;
+	int clk_period;
+	int timeout = 2000;
+	union cvmx_mio_emm_switch emm_switch;
+	union cvmx_mio_emm_rsp_sts emm_sts;
+
+	slot = mmc_priv(mmc);
+	host = slot->host;
+
+	/* Only a single user of the bootbus at a time. */
+	octeon_mmc_switch_to(slot);
+
+	/*
+	 * Reset the chip on each POWER_OFF.
+	 */
+	if (ios->power_mode == MMC_POWER_OFF) {
+		octeon_mmc_reset_bus(slot);
+		gpiod_set_value_cansleep(host->global_pwr_gpiod, 0);
+	} else
+		gpiod_set_value_cansleep(host->global_pwr_gpiod, 1);
+
+	switch (ios->bus_width) {
+	case MMC_BUS_WIDTH_8:
+		bus_width = 2;
+		break;
+	case MMC_BUS_WIDTH_4:
+		bus_width = 1;
+		break;
+	case MMC_BUS_WIDTH_1:
+		bus_width = 0;
+		break;
+	default:
+		bus_width = 0;
+		break;
+	}
+	slot->bus_width = bus_width;
+
+	if (ios->clock) {
+		/* Change the clock frequency. */
+		clock = ios->clock;
+		if (clock > 52000000)
+			clock = 52000000;
+		slot->clock = clock;
+		clk_period = (octeon_get_io_clock_rate() + clock - 1) /
+			(2 * clock);
+
+		emm_switch.u64 = 0;
+		emm_switch.s.hs_timing = (ios->timing == MMC_TIMING_MMC_HS);
+		emm_switch.s.bus_width = bus_width;
+		emm_switch.s.power_class = power_class;
+		emm_switch.s.clk_hi = clk_period;
+		emm_switch.s.clk_lo = clk_period;
+
+		if (!octeon_mmc_switch_val_changed(slot, emm_switch.u64))
+			goto out;
+
+		writeq(((u64)clock * 850ull) / 1000ull,
+		       host->base + OCT_MIO_EMM_WDOG);
+		writeq(emm_switch.u64, host->base + OCT_MIO_EMM_SWITCH);
+		emm_switch.s.bus_id = slot->bus_id;
+		writeq(emm_switch.u64, host->base + OCT_MIO_EMM_SWITCH);
+		slot->cached_switch = emm_switch.u64;
+
+		do {
+			emm_sts.u64 = readq(host->base + OCT_MIO_EMM_RSP_STS);
+			if (!emm_sts.s.switch_val)
+				break;
+			udelay(100);
+		} while (timeout-- > 0);
+
+		if (timeout <= 0)
+			goto out;
+	}
+out:
+	octeon_mmc_release_bus(host);
+}
+
+static const struct mmc_host_ops octeon_mmc_ops = {
+	.request        = octeon_mmc_request,
+	.set_ios        = octeon_mmc_set_ios,
+	.get_ro		= mmc_gpio_get_ro,
+	.get_cd		= mmc_gpio_get_cd,
+};
+
+static void octeon_mmc_set_clock(struct octeon_mmc_slot *slot,
+				 unsigned int clock)
+{
+	struct mmc_host *mmc = slot->mmc;
+
+	clock = min(clock, mmc->f_max);
+	clock = max(clock, mmc->f_min);
+	slot->clock = clock;
+}
+
+static int octeon_mmc_initlowlevel(struct octeon_mmc_slot *slot)
+{
+	union cvmx_mio_emm_switch emm_switch;
+	struct octeon_mmc_host *host = slot->host;
+
+	/* Enable this bus slot. */
+	host->emm_cfg |= (1ull << slot->bus_id);
+	writeq(host->emm_cfg, slot->host->base + OCT_MIO_EMM_CFG);
+
+	/* Program initial clock speed and power. */
+	octeon_mmc_set_clock(slot, slot->mmc->f_min);
+	emm_switch.u64 = 0;
+	emm_switch.s.power_class = 10;
+	emm_switch.s.clk_hi = (slot->sclock / slot->clock) / 2;
+	emm_switch.s.clk_lo = (slot->sclock / slot->clock) / 2;
+	writeq(emm_switch.u64, host->base + OCT_MIO_EMM_SWITCH);
+
+	/* Make the changes take effect on this bus slot. */
+	emm_switch.s.bus_id = slot->bus_id;
+	writeq(emm_switch.u64, host->base + OCT_MIO_EMM_SWITCH);
+	slot->cached_switch = emm_switch.u64;
+
+	/*
+	 * Set watchdog timeout value and default reset value
+	 * for the mask register. Finally, set the CARD_RCA
+	 * bit so that we can get the card address relative
+	 * to the CMD register for CMD7 transactions.
+	 */
+	writeq(((u64)slot->clock * 850ull) / 1000ull,
+	       host->base + OCT_MIO_EMM_WDOG);
+	writeq(0xe4390080ull, host->base + OCT_MIO_EMM_STS_MASK);
+	writeq(1, host->base + OCT_MIO_EMM_RCA);
+	return 0;
+}
+
+static int octeon_mmc_slot_probe(struct platform_device *slot_pdev,
+				 struct octeon_mmc_host *host)
+{
+	struct mmc_host *mmc;
+	struct octeon_mmc_slot *slot;
+	struct device *dev = &slot_pdev->dev;
+	struct device_node *node = slot_pdev->dev.of_node;
+	u32 id, bus_width, cmd_skew, dat_skew;
+	u64 clock_period;
+	int ret;
+
+	ret = of_property_read_u32(node, "reg", &id);
+	if (ret) {
+		dev_err(dev, "Missing or invalid reg property on %s\n",
+			of_node_full_name(node));
+		return ret;
+	}
+
+	if (id >= OCTEON_MAX_MMC || host->slot[id]) {
+		dev_err(dev, "Invalid reg property on %s\n",
+			of_node_full_name(node));
+		return -EINVAL;
+	}
+
+	mmc = mmc_alloc_host(sizeof(struct octeon_mmc_slot), dev);
+	if (!mmc) {
+		dev_err(dev, "alloc host failed\n");
+		return -ENOMEM;
+	}
+
+	slot = mmc_priv(mmc);
+	slot->mmc = mmc;
+	slot->host = host;
+
+	ret = mmc_of_parse(mmc);
+	if (ret)
+		goto err;
+
+	/*
+	 * The "cavium,bus-max-width" property is DEPRECATED and should
+	 * not be used. We handle it here to support older firmware.
+	 * Going forward, the standard "bus-width" property is used
+	 * instead of the Cavium-specific property.
+	 */
+	if (!(mmc->caps & (MMC_CAP_8_BIT_DATA | MMC_CAP_4_BIT_DATA))) {
+		/* Try legacy "cavium,bus-max-width" property. */
+		ret = of_property_read_u32(node, "cavium,bus-max-width",
+					   &bus_width);
+		if (ret) {
+			/* No bus width specified, use default. */
+			bus_width = 8;
+			dev_info(dev, "Default width 8 used for slot %u\n", id);
+		}
+	} else {
+		/* Hosts capable of 8-bit transfers can also do 4 bits */
+		bus_width = (mmc->caps & MMC_CAP_8_BIT_DATA) ? 8 : 4;
+	}
+
+	switch (bus_width) {
+	case 8:
+		slot->bus_width = (MMC_BUS_WIDTH_8 - 1);
+		mmc->caps = MMC_CAP_8_BIT_DATA | MMC_CAP_4_BIT_DATA;
+		break;
+	case 4:
+		slot->bus_width = (MMC_BUS_WIDTH_4 - 1);
+		mmc->caps = MMC_CAP_4_BIT_DATA;
+		break;
+	case 1:
+		slot->bus_width = MMC_BUS_WIDTH_1;
+		break;
+	default:
+		dev_err(dev, "Invalid bus width for slot %u\n", id);
+		ret = -EINVAL;
+		goto err;
+	}
+
+	/*
+	 * The "spi-max-frequency" property is DEPRECATED and should
+	 * not be used. We handle it here to support older firmware.
+	 * Going forward, the standard "max-frequency" property is
+	 * used instead of the Cavium-specific property.
+	 */
+	if (mmc->f_max == 0) {
+		/* Try legacy "spi-max-frequency" property. */
+		ret = of_property_read_u32(node, "spi-max-frequency",
+					   &mmc->f_max);
+		if (ret) {
+			/* No frequency properties found, use default. */
+			mmc->f_max = 52000000;
+			dev_info(dev, "Default %u frequency used for slot %u\n",
+				 mmc->f_max, id);
+		}
+	} else if (mmc->f_max > 52000000)
+		mmc->f_max = 52000000;
+
+	/* Set minimum frequency. */
+	mmc->f_min = 400000;
+
+	/* Octeon-specific DT properties. */
+	ret = of_property_read_u32(node, "cavium,cmd-clk-skew", &cmd_skew);
+	if (ret)
+		cmd_skew = 0;
+	ret = of_property_read_u32(node, "cavium,dat-clk-skew", &dat_skew);
+	if (ret)
+		dat_skew = 0;
+
+	/*
+	 * We only have a 3.3v supply, so we are calling this mostly
+	 * to get a sane OCR mask for other parts of the MMC subsytem.
+	 */
+	ret = mmc_of_parse_voltage(node, &mmc->ocr_avail);
+	if (ret == -EINVAL)
+		goto err;
+
+	/*
+	 * We do not have a voltage regulator, just a single
+	 * GPIO line to control power to all of the slots. It
+	 * is registered in the platform code. We can, however,
+	 * still set the POWER_OFF capability as long as the
+	 * GPIO was registered correctly.
+	 */
+	if (!IS_ERR(host->global_pwr_gpiod)) {
+		mmc->caps |= MMC_CAP_POWER_OFF_CARD;
+		dev_info(dev, "Got GLOBAL POWER GPIO\n");
+	}
+	else
+		dev_info(dev, "Did not get GLOBAL POWER GPIO\n");
+
+	/* Set up host parameters. */
+	mmc->ops = &octeon_mmc_ops;
+
+	/*
+	 * We only have a 3.3v supply, we cannot support any
+	 & of the UHS modes. We do support the high speed DDR
+	 * modes up to 52MHz.
+	 */
+	mmc->caps |= MMC_CAP_MMC_HIGHSPEED | MMC_CAP_SD_HIGHSPEED |
+		     MMC_CAP_ERASE;
+
+	mmc->max_segs = 64;
+	mmc->max_seg_size = host->linear_buf_size;
+	mmc->max_req_size = host->linear_buf_size;
+	mmc->max_blk_size = 512;
+	mmc->max_blk_count = mmc->max_req_size / 512;
+
+	slot->clock = mmc->f_min;
+	slot->sclock = octeon_get_io_clock_rate();
+
+	/* Period in picoseconds. */
+	clock_period = 1000000000000ull / slot->sclock;
+	slot->cmd_cnt = (cmd_skew + clock_period / 2) / clock_period;
+	slot->dat_cnt = (dat_skew + clock_period / 2) / clock_period;
+
+	slot->bus_id = id;
+	slot->cached_rca = 1;
+
+	/* Only a single user of the bootbus at a time. */
+	host->slot[id] = slot;
+	octeon_mmc_switch_to(slot);
+
+	/* Initialize MMC Block. */
+	octeon_mmc_initlowlevel(slot);
+
+	octeon_mmc_release_bus(host);
+
+	ret = mmc_add_host(mmc);
+	if (ret) {
+		dev_err(dev, "mmc_add_host() returned %d\n", ret);
+		goto err;
+	}
+
+	return 0;
+
+err:
+	slot->host->slot[id] = NULL;
+
+	gpiod_set_value_cansleep(host->global_pwr_gpiod, 0);
+
+	mmc_free_host(slot->mmc);
+	return ret;
+}
+
+static int octeon_mmc_slot_remove(struct octeon_mmc_slot *slot)
+{
+	mmc_remove_host(slot->mmc);
+	slot->host->slot[slot->bus_id] = NULL;
+	gpiod_set_value_cansleep(slot->host->global_pwr_gpiod, 0);
+	mmc_free_host(slot->mmc);
+
+	return 0;
+}
+
+static int octeon_mmc_probe(struct platform_device *pdev)
+{
+	struct octeon_mmc_host *host;
+	struct resource	*res;
+	void __iomem *base;
+	int mmc_irq[9];
+	int i;
+	int ret = 0;
+	struct device_node *node = pdev->dev.of_node;
+	struct device_node *cn;
+	u64 t;
+
+	host = devm_kzalloc(&pdev->dev, sizeof(*host), GFP_KERNEL);
+	if (!host) {
+		dev_err(&pdev->dev, "devm_kzalloc failed\n");
+		return -ENOMEM;
+	}
+
+	spin_lock_init(&host->irq_handler_lock);
+	sema_init(&host->mmc_serializer, 1);
+
+	if (of_device_is_compatible(node, "cavium,octeon-7890-mmc")) {
+		host->big_dma_addr = true;
+		host->need_irq_handler_lock = true;
+		host->has_ciu3 = true;
+		/*
+		 * First seven are the EMM_INT bits 0..6, then two for
+		 * the EMM_DMA_INT bits
+		 */
+		for (i = 0; i < 9; i++) {
+			mmc_irq[i] = platform_get_irq(pdev, i);
+			if (mmc_irq[i] < 0)
+				return mmc_irq[i];
+
+			/* work around legacy u-boot device trees */
+			irq_set_irq_type(mmc_irq[i], IRQ_TYPE_EDGE_RISING);
+		}
+	} else {
+		host->big_dma_addr = false;
+		host->need_irq_handler_lock = false;
+		host->has_ciu3 = false;
+		/* First one is EMM second NDF_DMA */
+		for (i = 0; i < 2; i++) {
+			mmc_irq[i] = platform_get_irq(pdev, i);
+			if (mmc_irq[i] < 0)
+				return mmc_irq[i];
+		}
+	}
+	host->last_slot = -1;
+
+	/* 256KB DMA linearized buffer (maximum transfer size). */
+	host->linear_buf_size = (1 << 18);
+	host->linear_buf = devm_kzalloc(&pdev->dev, host->linear_buf_size,
+					GFP_KERNEL);
+
+	if (!host->linear_buf) {
+		dev_err(&pdev->dev, "devm_kzalloc failed\n");
+		return -ENOMEM;
+	}
+
+	host->pdev = pdev;
+
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	if (!res) {
+		dev_err(&pdev->dev, "Platform resource[0] is missing\n");
+		return -ENXIO;
+	}
+	base = devm_ioremap_resource(&pdev->dev, res);
+	if (IS_ERR(base))
+		return PTR_ERR(base);
+	host->base = (void __iomem *)base;
+
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
+	if (!res) {
+		dev_err(&pdev->dev, "Platform resource[1] is missing\n");
+		return -EINVAL;
+	}
+	base = devm_ioremap_resource(&pdev->dev, res);
+	if (IS_ERR(base))
+		return PTR_ERR(base);
+	host->ndf_base = (void __iomem *)base;
+
+	/*
+	 * Clear out any pending interrupts that may be left over from
+	 * bootloader.
+	 */
+	t = readq(host->base + OCT_MIO_EMM_INT);
+	writeq(t, host->base + OCT_MIO_EMM_INT);
+	if (host->has_ciu3) {
+		/* Only CMD_DONE, DMA_DONE, CMD_ERR, DMA_ERR */
+		for (i = 1; i <= 4; i++) {
+			ret = devm_request_irq(&pdev->dev, mmc_irq[i],
+					       octeon_mmc_interrupt,
+					       0, DRV_NAME, host);
+			if (ret < 0) {
+				dev_err(&pdev->dev, "Error: devm_request_irq %d\n",
+					mmc_irq[i]);
+				return ret;
+			}
+		}
+	} else {
+		ret = devm_request_irq(&pdev->dev, mmc_irq[0],
+				       octeon_mmc_interrupt, 0, DRV_NAME, host);
+		if (ret < 0) {
+			dev_err(&pdev->dev, "Error: devm_request_irq %d\n",
+				mmc_irq[0]);
+			return ret;
+		}
+	}
+
+	host->global_pwr_gpiod = devm_gpiod_get_optional(&pdev->dev, "power",
+								GPIOD_OUT_HIGH);
+	if (IS_ERR(host->global_pwr_gpiod)) {
+		dev_err(&host->pdev->dev, "Invalid POWER GPIO\n");
+		return PTR_ERR(host->global_pwr_gpiod);
+	}
+
+	platform_set_drvdata(pdev, host);
+
+	for_each_child_of_node(node, cn) {
+		struct platform_device *slot_pdev;
+
+		slot_pdev = of_platform_device_create(cn, NULL, &pdev->dev);
+		ret = octeon_mmc_slot_probe(slot_pdev, host);
+		if (ret) {
+			dev_err(&host->pdev->dev, "Error populating slots\n");
+			gpiod_set_value_cansleep(host->global_pwr_gpiod, 0);
+			return ret;
+		}
+	}
+
+	return 0;
+}
+
+static int octeon_mmc_remove(struct platform_device *pdev)
+{
+	union cvmx_mio_ndf_dma_cfg ndf_dma_cfg;
+	struct octeon_mmc_host *host = platform_get_drvdata(pdev);
+	int i;
+
+	for (i = 0; i < OCTEON_MAX_MMC; i++) {
+		if (host->slot[i])
+			octeon_mmc_slot_remove(host->slot[i]);
+	}
+
+	ndf_dma_cfg.u64 = readq(host->ndf_base + OCT_MIO_NDF_DMA_CFG);
+	ndf_dma_cfg.s.en = 0;
+	writeq(ndf_dma_cfg.u64, host->ndf_base + OCT_MIO_NDF_DMA_CFG);
+
+	gpiod_set_value_cansleep(host->global_pwr_gpiod, 0);
+
+	return 0;
+}
+
+static const struct of_device_id octeon_mmc_match[] = {
+	{
+		.compatible = "cavium,octeon-6130-mmc",
+	},
+	{
+		.compatible = "cavium,octeon-7890-mmc",
+	},
+	{},
+};
+MODULE_DEVICE_TABLE(of, octeon_mmc_match);
+
+static struct platform_driver octeon_mmc_driver = {
+	.probe		= octeon_mmc_probe,
+	.remove		= octeon_mmc_remove,
+	.driver		= {
+		.name	= DRV_NAME,
+		.of_match_table = octeon_mmc_match,
+	},
+};
+
+static int __init octeon_mmc_init(void)
+{
+	return platform_driver_register(&octeon_mmc_driver);
+}
+
+static void __exit octeon_mmc_cleanup(void)
+{
+	platform_driver_unregister(&octeon_mmc_driver);
+}
+
+module_init(octeon_mmc_init);
+module_exit(octeon_mmc_cleanup);
+
+MODULE_AUTHOR("Cavium Inc. <support@cavium.com>");
+MODULE_DESCRIPTION("low-level driver for Cavium OCTEON MMC/SSD card");
+MODULE_LICENSE("GPL");
diff --git a/include/linux/mmc/octeon_mmc.h b/include/linux/mmc/octeon_mmc.h
new file mode 100644
index 0000000..d650ab1
--- /dev/null
+++ b/include/linux/mmc/octeon_mmc.h
@@ -0,0 +1,93 @@
+/*
+ * Driver for MMC and SSD cards for Cavium OCTEON SOCs.
+ *
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License.  See the file "COPYING" in the main directory of this archive
+ * for more details.
+ *
+ * Copyright (C) 2012-2016 Cavium Inc.
+ */
+#include <linux/io.h>
+#include <linux/scatterlist.h>
+#include <linux/mmc/host.h>
+
+#include <asm/octeon/octeon.h>
+
+#define OCTEON_MAX_MMC			4
+
+#define OCT_MIO_NDF_DMA_CFG		0x00
+#define OCT_MIO_EMM_DMA_ADR		0x08
+
+#define OCT_MIO_EMM_CFG			0x00
+#define OCT_MIO_EMM_SWITCH		0x48
+#define OCT_MIO_EMM_DMA			0x50
+#define OCT_MIO_EMM_CMD			0x58
+#define OCT_MIO_EMM_RSP_STS		0x60
+#define OCT_MIO_EMM_RSP_LO		0x68
+#define OCT_MIO_EMM_RSP_HI		0x70
+#define OCT_MIO_EMM_INT			0x78
+#define OCT_MIO_EMM_INT_EN		0x80
+#define OCT_MIO_EMM_WDOG		0x88
+#define OCT_MIO_EMM_SAMPLE		0x90
+#define OCT_MIO_EMM_STS_MASK		0x98
+#define OCT_MIO_EMM_RCA			0xa0
+#define OCT_MIO_EMM_BUF_IDX		0xe0
+#define OCT_MIO_EMM_BUF_DAT		0xe8
+
+struct octeon_mmc_host {
+	void __iomem	*base;
+	void __iomem	*ndf_base;
+	u64	emm_cfg;
+	u64	n_minus_one;  /* OCTEON II workaround location */
+	int	last_slot;
+
+	struct semaphore mmc_serializer;
+	struct mmc_request	*current_req;
+	unsigned int		linear_buf_size;
+	void			*linear_buf;
+	struct sg_mapping_iter smi;
+	int sg_idx;
+	bool dma_active;
+
+	struct platform_device	*pdev;
+	struct gpio_desc *global_pwr_gpiod;
+	bool dma_err_pending;
+	bool big_dma_addr;
+	bool need_irq_handler_lock;
+	spinlock_t irq_handler_lock;
+	bool has_ciu3;
+
+	struct octeon_mmc_slot	*slot[OCTEON_MAX_MMC];
+};
+
+struct octeon_mmc_slot {
+	struct mmc_host         *mmc;	/* slot-level mmc_core object */
+	struct octeon_mmc_host	*host;	/* common hw for all 4 slots */
+
+	unsigned int		clock;
+	unsigned int		sclock;
+
+	u64			cached_switch;
+	u64			cached_rca;
+
+	unsigned int		cmd_cnt; /* sample delay */
+	unsigned int		dat_cnt; /* sample delay */
+
+	int			bus_width;
+	int			bus_id;
+};
+
+struct octeon_mmc_cr_type {
+	u8 ctype;
+	u8 rtype;
+};
+
+struct octeon_mmc_cr_mods {
+	u8 ctype_xor;
+	u8 rtype_xor;
+};
+
+extern void l2c_lock_mem_region(u64 start, u64 len);
+extern void l2c_unlock_mem_region(u64 start, u64 len);
+extern void octeon_mmc_acquire_bus(struct octeon_mmc_host *host);
+extern void octeon_mmc_release_bus(struct octeon_mmc_host *host);
-- 
1.9.1

^ permalink raw reply related

* Re: [PATCH] mmc: rtsx_usb_sdmmc: Handle runtime PM while changing led
From: Alan Stern @ 2016-09-19 18:02 UTC (permalink / raw)
  To: Ulf Hansson
  Cc: linux-mmc, Ritesh Raj Sarraf, USB list, Micky Ching, Roger Tseng,
	Wei WANG
In-Reply-To: <CAPDyKFpFObRkvUC5kOKznE3FAGL6H_Hufa7ZEFWpmB694AY9ow-JsoAwUIsXosN+BqQ9rBEUg@public.gmane.org>

On Mon, 19 Sep 2016, Ulf Hansson wrote:

> On 18 September 2016 at 04:30, Alan Stern <stern-nwvwT67g6+6dFdvTe/nMLpVzexx5G7lz@public.gmane.org> wrote:
> > On Sat, 17 Sep 2016, Ulf Hansson wrote:
> >
> >> Each access of the parent device (usb device) needs to be done in runtime
> >> resumed state. Currently this isn't case while changing the leds, so let's
> >> add pm_runtime_get_sync() and pm_runtime_put() around these calls.
> >>
> >> Signed-off-by: Ulf Hansson <ulf.hansson-QSEj5FYQhm4dnm+yROfE0A@public.gmane.org>
> >> ---
> >>
> >> While discussing an issue[1] related to runtime PM, I found out that this
> >> minor change at least improves the behavior that has been observed.
> >>
> >> [1]
> >> http://www.spinics.net/lists/linux-usb/msg144634.html
> >>
> >> ---
> >>  drivers/mmc/host/rtsx_usb_sdmmc.c | 2 ++
> >>  1 file changed, 2 insertions(+)
> >>
> >> diff --git a/drivers/mmc/host/rtsx_usb_sdmmc.c b/drivers/mmc/host/rtsx_usb_sdmmc.c
> >> index 6c71fc9..a59c7fa 100644
> >> --- a/drivers/mmc/host/rtsx_usb_sdmmc.c
> >> +++ b/drivers/mmc/host/rtsx_usb_sdmmc.c
> >> @@ -1314,6 +1314,7 @@ static void rtsx_usb_update_led(struct work_struct *work)
> >>               container_of(work, struct rtsx_usb_sdmmc, led_work);
> >>       struct rtsx_ucr *ucr = host->ucr;
> >>
> >> +     pm_runtime_get_sync(sdmmc_dev(host));
> >>       mutex_lock(&ucr->dev_mutex);
> >>
> >>       if (host->led.brightness == LED_OFF)
> >> @@ -1322,6 +1323,7 @@ static void rtsx_usb_update_led(struct work_struct *work)
> >>               rtsx_usb_turn_on_led(ucr);
> >>
> >>       mutex_unlock(&ucr->dev_mutex);
> >> +     pm_runtime_put(sdmmc_dev(host));
> >>  }
> >>  #endif
> >
> > The missing aspect here is that this won't stop the parent USB device
> > from going into autosuspend every 2 seconds and then resuming shortly
> > afterward.  There are two ways of preventing this:
> >
> >         Call usb_mark_last_busy() at appropriate places.
> >
> >         Enable autosuspend for the sdmmc device.
> >
> > The second approach would also prevent the sdmmc device from going into
> > autosuspend as soon as the LED update is finished.  Maybe that's okay,
> > but if going into suspend is a lightweight procedure then you may want
> > to prevent it.
> >
> 
> We can for sure enable autosuspend for the sdmmc device, although as
> soon as an SD card will be detected the rtsx driver will increase the
> runtime PM usage count. The count is decreased when the card is
> removed (or failed to be initialized), thus runtime suspend is
> prevented as long as there is a functional card inserted.

Which means that autosuspend matters only when a card isn't present, 
and the host is polled every second or so to see whether a card has 
been inserted.

Under those circumstances you probably don't want to use autosuspend.  
That is, resuming before each poll and suspending afterward may use 
less energy than staying at full power all the time.

> I am wondering what you think would be a good autosuspend timeout in
> this case? It seems to me that the only thing the rtsx driver really
> care about is to tell the parent device that it needs to be runtime
> resumed during a certain timeframe, more or less it would like to
> inherit the parents settings.
> 
> Other mmc hosts, not being usb-mmc devices, are using an autosuspend
> timeout of ~50-100ms but that doesn't seem like good value here,
> right?

Well, if you decide to let the device go into runtime suspend between
polls then there's no reason to use autosuspend at all.  Once a poll 
has ended, you know there won't be any more activity until the next 
poll.

On the other hand, if you decide to keep the device at full power all 
the time during polling, then any autosuspend timeout larger than 1000 
ms would do what you want.

Mostly I'm concerned about how this will interact with the USB runtime
PM.  The thing is, suspending the sdmmc device doesn't save any energy,
whereas suspending the USB device does.

Let's see how well everything works with the patch to the rtsx memstick
driver.

Alan Stern

--
To unsubscribe from this list: send the line "unsubscribe linux-usb" 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: xHCI problem? [was Re: Erratic USB device behavior and device loss]
From: Alan Stern @ 2016-09-19 17:48 UTC (permalink / raw)
  To: Ulf Hansson, Alex Dubov; +Cc: Ritesh Raj Sarraf, USB list, linux-mmc
In-Reply-To: <CAPDyKFr1EAUd0E-VQh_Y3ptCNM4WymvqsyQ8Ev756Wrb+93V6g@mail.gmail.com>

On Mon, 19 Sep 2016, Ulf Hansson wrote:

> On 18 September 2016 at 03:42, Alan Stern <stern@rowland.harvard.edu> wrote:

> > Well, this is pretty clear:
> >
> > Sep 17 15:55:52 learner kernel: CPU: 1 PID: 535 Comm: rtsx_usb_ms_1 Tainted: G     U          4.8.0-rc6ulf1alan1+ #19
> > Sep 17 15:55:52 learner kernel: Hardware name: LENOVO 20344/INVALID, BIOS 96CN31WW(V1.17) 07/21/2015
> > Sep 17 15:55:52 learner kernel:  0000000000000000 ffffffff81314be5 ffff8802476746c0 0000000002400000
> > Sep 17 15:55:52 learner kernel:  ffffffffa016f719 00000000523bec00 ffff88025f255780 ffff88024feff600
> > Sep 17 15:55:52 learner kernel:  0000000000018080 0000000000000000 ffff88025f258080 ffffffff815a0e60
> > Sep 17 15:55:52 learner kernel: Call Trace:
> > Sep 17 15:55:52 learner kernel:  [<ffffffff81314be5>] ? dump_stack+0x7d/0xb8
> > Sep 17 15:55:52 learner kernel:  [<ffffffffa016f719>] ? usb_hcd_submit_urb+0x3c9/0xad0 [usbcore]
> > Sep 17 15:55:52 learner kernel:  [<ffffffff815a0e60>] ? _raw_spin_lock_irqsave+0x20/0x47
> > Sep 17 15:55:52 learner kernel:  [<ffffffff810d5c8b>] ? lock_timer_base.isra.24+0x7b/0xa0
> > Sep 17 15:55:52 learner kernel:  [<ffffffff810d5d59>] ? try_to_del_timer_sync+0x49/0x60
> > Sep 17 15:55:52 learner kernel:  [<ffffffffa017180d>] ? usb_start_wait_urb+0x5d/0x140 [usbcore]
> > Sep 17 15:55:52 learner kernel:  [<ffffffffa00ee2be>] ? rtsx_usb_send_cmd+0x5e/0x80 [rtsx_usb]
> > Sep 17 15:55:52 learner kernel:  [<ffffffffa00ee4a7>] ? rtsx_usb_read_register+0x67/0xb0 [rtsx_usb]
> > Sep 17 15:55:52 learner kernel:  [<ffffffffa0b15ac1>] ? rtsx_usb_detect_ms_card+0x61/0xe0 [rtsx_usb_ms]
> > Sep 17 15:55:52 learner kernel:  [<ffffffffa0b15a60>] ? rtsx_usb_ms_set_param+0x770/0x770 [rtsx_usb_ms]
> > Sep 17 15:55:52 learner kernel:  [<ffffffff8108ee0d>] ? kthread+0xbd/0xe0
> > Sep 17 15:55:52 learner kernel:  [<ffffffff81024741>] ? __switch_to+0x2b1/0x6a0
> > Sep 17 15:55:52 learner kernel:  [<ffffffff815a118f>] ? ret_from_fork+0x1f/0x40
> > Sep 17 15:55:52 learner kernel:  [<ffffffff8108ed50>] ? kthread_create_on_node+0x180/0x180
> >
> > This is the rtsx_usb_detect_ms_card() routine in
> > drivers/memstick/host/rtsx_usb_ms.c, which runs as a kthread.  It
> > doesn't do any runtime PM.  So it looks like the bug is present in both
> > the MMC and MemoryStick interfaces.
> 
> I think the problem is even worse in the MemoryStick case, as the
> memstick core doesn't help with runtime PM. I am pretty sure there are
> other cases when the MemoryStick driver accesses the usb device
> without first runtime resuming it.

Maybe we should get a MemoryStick maintainer involved in this thread.  
I CC'ed Alex Dubov.

Alex, the problem here is that drivers/memstick/host/rtsx_usb_ms.c
tries to communicate with the host USB device while it is runtime
suspended.

> Of course we could start simple an fix the bug observed above and see
> if that solves the reported problem. Alan, do you want to post to
> patch or you want me?

This ought to help.  Ritesh, please apply this patch on top of the 
two earlier ones and let's see what happens.

Alan Stern



Index: usb-4.x/drivers/memstick/host/rtsx_usb_ms.c
===================================================================
--- usb-4.x.orig/drivers/memstick/host/rtsx_usb_ms.c
+++ usb-4.x/drivers/memstick/host/rtsx_usb_ms.c
@@ -681,6 +681,7 @@ static int rtsx_usb_detect_ms_card(void
 	int err;
 
 	for (;;) {
+		pm_runtime_get_sync(ms_dev(host));
 		mutex_lock(&ucr->dev_mutex);
 
 		/* Check pending MS card changes */
@@ -703,6 +704,7 @@ static int rtsx_usb_detect_ms_card(void
 		}
 
 poll_again:
+		pm_runtime_put(ms_dev(host));
 		if (host->eject)
 			break;
 


^ permalink raw reply

* Re: [PATCH v2 4/6] mmc: tmio: add eMMC support
From: Sergei Shtylyov @ 2016-09-19 17:31 UTC (permalink / raw)
  To: Wolfram Sang, linux-mmc; +Cc: linux-renesas-soc, Simon Horman, Dirk Behme
In-Reply-To: <20160919124921.8172-5-wsa+renesas@sang-engineering.com>

Hello.

On 09/19/2016 03:49 PM, Wolfram Sang wrote:

> We need to add R1 without CRC support, refactor the bus width routine a
> little and extend a quirk check. To support "non-removable;" we need a
> workaround which will be hopefully removed when reworking PM soon.
>
> Signed-off-by: Wolfram Sang <wsa+renesas@sang-engineering.com>
> ---
>  drivers/mmc/host/tmio_mmc.h     |  3 +++
>  drivers/mmc/host/tmio_mmc_pio.c | 38 ++++++++++++++++++++++++++------------
>  2 files changed, 29 insertions(+), 12 deletions(-)
>
> diff --git a/drivers/mmc/host/tmio_mmc.h b/drivers/mmc/host/tmio_mmc.h
> index 4b501f2d529f6e..637581faf756b1 100644
> --- a/drivers/mmc/host/tmio_mmc.h
> +++ b/drivers/mmc/host/tmio_mmc.h
> @@ -79,6 +79,9 @@
>  #define	CLK_CTL_DIV_MASK	0xff
>  #define	CLK_CTL_SCLKEN		BIT(8)
>
> +#define CARD_OPT_WIDTH8		BIT(13)
> +#define CARD_OPT_WIDTH		BIT(15)
> +
>  #define TMIO_BBS		512		/* Boot block size */
>
>  /* Definitions for values the CTRL_SDIO_STATUS register can take. */
> diff --git a/drivers/mmc/host/tmio_mmc_pio.c b/drivers/mmc/host/tmio_mmc_pio.c
> index 46b5a456243b84..a0f05eb4f34490 100644
> --- a/drivers/mmc/host/tmio_mmc_pio.c
> +++ b/drivers/mmc/host/tmio_mmc_pio.c
[...]
> @@ -922,14 +925,16 @@ static void tmio_mmc_power_off(struct tmio_mmc_host *host)
>  static void tmio_mmc_set_bus_width(struct tmio_mmc_host *host,
>  				unsigned char bus_width)
>  {
> -	switch (bus_width) {
> -	case MMC_BUS_WIDTH_1:
> -		sd_ctrl_write16(host, CTL_SD_MEM_CARD_OPT, 0x80e0);
> -		break;
> -	case MMC_BUS_WIDTH_4:
> -		sd_ctrl_write16(host, CTL_SD_MEM_CARD_OPT, 0x00e0);
> -		break;
> -	}
> +	u16 reg = sd_ctrl_read16(host, CTL_SD_MEM_CARD_OPT)
> +				& ~(CARD_OPT_WIDTH | CARD_OPT_WIDTH8);
> +
> +	/* reg now applies to MMC_BUS_WIDTH_4 */
> +	if (bus_width == MMC_BUS_WIDTH_1)
> +		reg |= CARD_OPT_WIDTH;
> +	else if (bus_width == MMC_BUS_WIDTH_8)
> +		reg |= CARD_OPT_WIDTH8;

    Why not *switch*?

[...]

MBR, Sergei

^ permalink raw reply

* Re: [PATCH v2 5/6] arm64: dts: r8a7795: salvator: enable on-board eMMC
From: Sergei Shtylyov @ 2016-09-19 17:25 UTC (permalink / raw)
  To: Wolfram Sang, linux-mmc; +Cc: linux-renesas-soc, Simon Horman, Dirk Behme
In-Reply-To: <20160919124921.8172-6-wsa+renesas@sang-engineering.com>

On 09/19/2016 03:49 PM, Wolfram Sang wrote:

> Signed-off-by: Wolfram Sang <wsa+renesas@sang-engineering.com>
> ---
>  arch/arm64/boot/dts/renesas/r8a7795-salvator-x.dts | 43 ++++++++++++++++++++++
>  1 file changed, 43 insertions(+)
>
> diff --git a/arch/arm64/boot/dts/renesas/r8a7795-salvator-x.dts b/arch/arm64/boot/dts/renesas/r8a7795-salvator-x.dts
> index 8964b1ea3cea69..7780d02f716a34 100644
> --- a/arch/arm64/boot/dts/renesas/r8a7795-salvator-x.dts
> +++ b/arch/arm64/boot/dts/renesas/r8a7795-salvator-x.dts
[...]
> @@ -246,6 +264,18 @@
>  		power-source = <1800>;
>  	};
>
> +	sdhi2_pins: sd2 {
> +		groups = "sdhi2_data8", "sdhi2_ctrl";
> +		function = "sdhi2";
> +		power-source = <3300>;
> +	};
> +
> +	sdhi2_pins_uhs: sd2 {
> +		groups = "sdhi2_data8", "sdhi2_ctrl";
> +		function = "sdhi2";
> +		power-source = <1800>;
> +	};
> +

    I'm afraid you're just overriding the props in the first "sd2" node with 
this...

[...]

MBR, Sergei


^ permalink raw reply

* Re: RTS5227 errors on mount
From: Don Zickus @ 2016-09-19 13:41 UTC (permalink / raw)
  To: Ulf Hansson; +Cc: linux-mmc
In-Reply-To: <CAPDyKFoACQUieOP7CFpmJQPGS4DG-=bKobXhnqsvd_Y1vddt8w@mail.gmail.com>

On Sat, Sep 17, 2016 at 11:03:55AM +0200, Ulf Hansson wrote:
> On 15 September 2016 at 17:11, Don Zickus <dzickus@redhat.com> wrote:
> > Hi Ulf,
> >
> > My emails to Micky seem to get blocked.  Is there a better person to contact
> > about the rts5227 chip?
> 
> Sorry, I don't know.
> 
> Do you think this is a regression? Care to do a bisect?

I can try bisecting but I don't think it is a regression.  The hardware does
recover correctly (aborts the transaction, reset the device, and retry).  I
was just trying to see if there was some incorrect calculations made when
setting voltage levels or other chip configurations.

But I don't have the specs to know how things are supposed to work.

For right now it is just an' error' message that appears in our log that I
was trying to get rid of.  I don't see any loss of data, which is good.

I was hoping Micky would just be able to give a quick answer or something.

I will poke at bisection to see if anything useful comes up.

Cheers,
Don

^ permalink raw reply

* [PATCH v2 6/6] arm64: dts: r8a7796: salvator: enable on board eMMC
From: Wolfram Sang @ 2016-09-19 12:49 UTC (permalink / raw)
  To: linux-mmc; +Cc: linux-renesas-soc, Simon Horman, Dirk Behme, Wolfram Sang
In-Reply-To: <20160919124921.8172-1-wsa+renesas@sang-engineering.com>

Signed-off-by: Wolfram Sang <wsa+renesas@sang-engineering.com>
---
 arch/arm64/boot/dts/renesas/r8a7796-salvator-x.dts | 44 +++++++++++++++++++++-
 1 file changed, 43 insertions(+), 1 deletion(-)

diff --git a/arch/arm64/boot/dts/renesas/r8a7796-salvator-x.dts b/arch/arm64/boot/dts/renesas/r8a7796-salvator-x.dts
index 91440eb2237e72..7b14cb7538b22f 100644
--- a/arch/arm64/boot/dts/renesas/r8a7796-salvator-x.dts
+++ b/arch/arm64/boot/dts/renesas/r8a7796-salvator-x.dts
@@ -31,6 +31,24 @@
 		reg = <0x0 0x48000000 0x0 0x78000000>;
 	};
 
+	reg_1p8v: regulator0 {
+		compatible = "regulator-fixed";
+		regulator-name = "fixed-1.8V";
+		regulator-min-microvolt = <1800000>;
+		regulator-max-microvolt = <1800000>;
+		regulator-boot-on;
+		regulator-always-on;
+	};
+
+	reg_3p3v: regulator1 {
+		compatible = "regulator-fixed";
+		regulator-name = "fixed-3.3V";
+		regulator-min-microvolt = <3300000>;
+		regulator-max-microvolt = <3300000>;
+		regulator-boot-on;
+		regulator-always-on;
+	};
+
 	vcc_sdhi0: regulator-vcc-sdhi0 {
 		compatible = "regulator-fixed";
 
@@ -78,7 +96,6 @@
 		states = <3300000 1
 			  1800000 0>;
 	};
-
 };
 
 &pfc {
@@ -106,6 +123,18 @@
 		power-source = <1800>;
 	};
 
+	sdhi2_pins: sd2 {
+		groups = "sdhi2_data8", "sdhi2_ctrl";
+		function = "sdhi2";
+		power-source = <3300>;
+	};
+
+	sdhi2_pins_uhs: sd2 {
+		groups = "sdhi2_data8", "sdhi2_ctrl";
+		function = "sdhi2";
+		power-source = <1800>;
+	};
+
 	sdhi3_pins: sd3 {
 		groups = "sdhi3_data4", "sdhi3_ctrl";
 		function = "sdhi3";
@@ -138,6 +167,19 @@
 	status = "okay";
 };
 
+&sdhi2 {
+	/* used for on-board 8bit eMMC */
+	pinctrl-0 = <&sdhi2_pins>;
+	pinctrl-1 = <&sdhi2_pins_uhs>;
+	pinctrl-names = "default", "state_uhs";
+
+	vmmc-supply = <&reg_3p3v>;
+	vqmmc-supply = <&reg_1p8v>;
+	bus-width = <8>;
+	non-removable;
+	status = "okay";
+};
+
 &sdhi3 {
 	pinctrl-0 = <&sdhi3_pins>;
 	pinctrl-1 = <&sdhi3_pins_uhs>;
-- 
2.9.3


^ permalink raw reply related

* [PATCH v2 4/6] mmc: tmio: add eMMC support
From: Wolfram Sang @ 2016-09-19 12:49 UTC (permalink / raw)
  To: linux-mmc; +Cc: linux-renesas-soc, Simon Horman, Dirk Behme, Wolfram Sang
In-Reply-To: <20160919124921.8172-1-wsa+renesas@sang-engineering.com>

We need to add R1 without CRC support, refactor the bus width routine a
little and extend a quirk check. To support "non-removable;" we need a
workaround which will be hopefully removed when reworking PM soon.

Signed-off-by: Wolfram Sang <wsa+renesas@sang-engineering.com>
---
 drivers/mmc/host/tmio_mmc.h     |  3 +++
 drivers/mmc/host/tmio_mmc_pio.c | 38 ++++++++++++++++++++++++++------------
 2 files changed, 29 insertions(+), 12 deletions(-)

diff --git a/drivers/mmc/host/tmio_mmc.h b/drivers/mmc/host/tmio_mmc.h
index 4b501f2d529f6e..637581faf756b1 100644
--- a/drivers/mmc/host/tmio_mmc.h
+++ b/drivers/mmc/host/tmio_mmc.h
@@ -79,6 +79,9 @@
 #define	CLK_CTL_DIV_MASK	0xff
 #define	CLK_CTL_SCLKEN		BIT(8)
 
+#define CARD_OPT_WIDTH8		BIT(13)
+#define CARD_OPT_WIDTH		BIT(15)
+
 #define TMIO_BBS		512		/* Boot block size */
 
 /* Definitions for values the CTRL_SDIO_STATUS register can take. */
diff --git a/drivers/mmc/host/tmio_mmc_pio.c b/drivers/mmc/host/tmio_mmc_pio.c
index 46b5a456243b84..a0f05eb4f34490 100644
--- a/drivers/mmc/host/tmio_mmc_pio.c
+++ b/drivers/mmc/host/tmio_mmc_pio.c
@@ -340,7 +340,9 @@ static int tmio_mmc_start_command(struct tmio_mmc_host *host, struct mmc_command
 
 	switch (mmc_resp_type(cmd)) {
 	case MMC_RSP_NONE: c |= RESP_NONE; break;
-	case MMC_RSP_R1:   c |= RESP_R1;   break;
+	case MMC_RSP_R1:
+	case MMC_RSP_R1_NO_CRC:
+			   c |= RESP_R1;   break;
 	case MMC_RSP_R1B:  c |= RESP_R1B;  break;
 	case MMC_RSP_R2:   c |= RESP_R2;   break;
 	case MMC_RSP_R3:   c |= RESP_R3;   break;
@@ -737,12 +739,13 @@ static int tmio_mmc_start_data(struct tmio_mmc_host *host,
 	pr_debug("setup data transfer: blocksize %08x  nr_blocks %d\n",
 		 data->blksz, data->blocks);
 
-	/* Some hardware cannot perform 2 byte requests in 4 bit mode */
-	if (host->mmc->ios.bus_width == MMC_BUS_WIDTH_4) {
+	/* Some hardware cannot perform 2 byte requests in 4/8 bit mode */
+	if (host->mmc->ios.bus_width == MMC_BUS_WIDTH_4 ||
+	    host->mmc->ios.bus_width == MMC_BUS_WIDTH_8) {
 		int blksz_2bytes = pdata->flags & TMIO_MMC_BLKSZ_2BYTES;
 
 		if (data->blksz < 2 || (data->blksz < 4 && !blksz_2bytes)) {
-			pr_err("%s: %d byte block unsupported in 4 bit mode\n",
+			pr_err("%s: %d byte block unsupported in 4/8 bit mode\n",
 			       mmc_hostname(host->mmc), data->blksz);
 			return -EINVAL;
 		}
@@ -922,14 +925,16 @@ static void tmio_mmc_power_off(struct tmio_mmc_host *host)
 static void tmio_mmc_set_bus_width(struct tmio_mmc_host *host,
 				unsigned char bus_width)
 {
-	switch (bus_width) {
-	case MMC_BUS_WIDTH_1:
-		sd_ctrl_write16(host, CTL_SD_MEM_CARD_OPT, 0x80e0);
-		break;
-	case MMC_BUS_WIDTH_4:
-		sd_ctrl_write16(host, CTL_SD_MEM_CARD_OPT, 0x00e0);
-		break;
-	}
+	u16 reg = sd_ctrl_read16(host, CTL_SD_MEM_CARD_OPT)
+				& ~(CARD_OPT_WIDTH | CARD_OPT_WIDTH8);
+
+	/* reg now applies to MMC_BUS_WIDTH_4 */
+	if (bus_width == MMC_BUS_WIDTH_1)
+		reg |= CARD_OPT_WIDTH;
+	else if (bus_width == MMC_BUS_WIDTH_8)
+		reg |= CARD_OPT_WIDTH8;
+
+	sd_ctrl_write16(host, CTL_SD_MEM_CARD_OPT, reg);
 }
 
 /* Set MMC clock / power.
@@ -1149,6 +1154,15 @@ int tmio_mmc_host_probe(struct tmio_mmc_host *_host,
 				  !mmc_card_is_removable(mmc) ||
 				  mmc->slot.cd_irq >= 0);
 
+	/*
+	 * On Gen2+, eMMC with NONREMOVABLE currently fails because native
+	 * hotplug gets disabled. It seems RuntimePM related yet we need further
+	 * research. Since we are planning a PM overhaul anyway, let's enforce
+	 * for now the device being active by enabling native hotplug always.
+	 */
+	if (pdata->flags & TMIO_MMC_MIN_RCAR2)
+		_host->native_hotplug = true;
+
 	if (tmio_mmc_clk_enable(_host) < 0) {
 		mmc->f_max = pdata->hclk;
 		mmc->f_min = mmc->f_max / 512;
-- 
2.9.3


^ permalink raw reply related

* [PATCH v2 2/6] mmc: rtsx_pci: use new macro for R1 without CRC
From: Wolfram Sang @ 2016-09-19 12:49 UTC (permalink / raw)
  To: linux-mmc; +Cc: linux-renesas-soc, Simon Horman, Dirk Behme, Wolfram Sang
In-Reply-To: <20160919124921.8172-1-wsa+renesas@sang-engineering.com>

Signed-off-by: Wolfram Sang <wsa+renesas@sang-engineering.com>
---
 drivers/mmc/host/rtsx_pci_sdmmc.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/drivers/mmc/host/rtsx_pci_sdmmc.c b/drivers/mmc/host/rtsx_pci_sdmmc.c
index 396c9b7e4121b0..3ccaa1415f33b2 100644
--- a/drivers/mmc/host/rtsx_pci_sdmmc.c
+++ b/drivers/mmc/host/rtsx_pci_sdmmc.c
@@ -126,7 +126,7 @@ static int sd_response_type(struct mmc_command *cmd)
 		return SD_RSP_TYPE_R0;
 	case MMC_RSP_R1:
 		return SD_RSP_TYPE_R1;
-	case MMC_RSP_R1 & ~MMC_RSP_CRC:
+	case MMC_RSP_R1_NO_CRC:
 		return SD_RSP_TYPE_R1 | SD_NO_CHECK_CRC7;
 	case MMC_RSP_R1B:
 		return SD_RSP_TYPE_R1b;
-- 
2.9.3


^ permalink raw reply related

* [PATCH v2 1/6] mmc: add define for R1 response without CRC
From: Wolfram Sang @ 2016-09-19 12:49 UTC (permalink / raw)
  To: linux-mmc; +Cc: linux-renesas-soc, Simon Horman, Dirk Behme, Wolfram Sang
In-Reply-To: <20160919124921.8172-1-wsa+renesas@sang-engineering.com>

The core uses it for polling. Give drivers a proper define handle this
case like for other response types.

Signed-off-by: Wolfram Sang <wsa+renesas@sang-engineering.com>
---
 include/linux/mmc/core.h | 3 +++
 1 file changed, 3 insertions(+)

diff --git a/include/linux/mmc/core.h b/include/linux/mmc/core.h
index b01e77de1a74de..4caee099b63a28 100644
--- a/include/linux/mmc/core.h
+++ b/include/linux/mmc/core.h
@@ -55,6 +55,9 @@ struct mmc_command {
 #define MMC_RSP_R6	(MMC_RSP_PRESENT|MMC_RSP_CRC|MMC_RSP_OPCODE)
 #define MMC_RSP_R7	(MMC_RSP_PRESENT|MMC_RSP_CRC|MMC_RSP_OPCODE)
 
+/* Can be used by core to poll after switch to MMC HS mode */
+#define MMC_RSP_R1_NO_CRC	(MMC_RSP_PRESENT|MMC_RSP_OPCODE)
+
 #define mmc_resp_type(cmd)	((cmd)->flags & (MMC_RSP_PRESENT|MMC_RSP_136|MMC_RSP_CRC|MMC_RSP_BUSY|MMC_RSP_OPCODE))
 
 /*
-- 
2.9.3


^ permalink raw reply related


This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox