diff mbox series

[v2,9/9] mmc: sh_mmcif: Use sg_miter for PIO

Message ID 20240127-mmc-proper-kmap-v2-9-d8e732aa97d1@linaro.org (mailing list archive)
State New, archived
Headers show
Series mmc: Use proper sg_miter for scatterlists | expand

Commit Message

Linus Walleij Jan. 27, 2024, 12:19 a.m. UTC
Use sg_miter iterator instead of sg_virt() and custom code
to loop over the scatterlist. The memory iterator will do
bounce buffering if the page happens to be located in high memory,
which the driver may or may not be using.

Suggested-by: Christoph Hellwig <hch@lst.de>
Link: https://lore.kernel.org/linux-mmc/20240122073423.GA25859@lst.de/
Signed-off-by: Linus Walleij <linus.walleij@linaro.org>
---
 drivers/mmc/host/sh_mmcif.c | 102 +++++++++++++++++++++++++++-----------------
 1 file changed, 63 insertions(+), 39 deletions(-)

Comments

Geert Uytterhoeven Feb. 20, 2024, 9:03 p.m. UTC | #1
Hi Linus,

On Sat, 27 Jan 2024, Linus Walleij wrote:
> Use sg_miter iterator instead of sg_virt() and custom code
> to loop over the scatterlist. The memory iterator will do
> bounce buffering if the page happens to be located in high memory,
> which the driver may or may not be using.
>
> Suggested-by: Christoph Hellwig <hch@lst.de>
> Link: https://lore.kernel.org/linux-mmc/20240122073423.GA25859@lst.de/
> Signed-off-by: Linus Walleij <linus.walleij@linaro.org>

Thanks for your patch, which is now commit 27b57277d9ba3a90 ("mmc:
sh_mmcif: Use sg_miter for PIO") in mmc/next.

I have bisected the following BUGs on R-Mobile APE6 (also seen on
R-Mobile A1 and SH-Mobile AG5) to this commit:

     sh_mobile_sdhi ee120000.mmc: mmc1 base at 0xee120000, max clock rate 12 MHz
     mmc2: new high speed MMC card at address 0001
     sh_mobile_sdhi ee100000.mmc: mmc0 base at 0xee100000, max clock rate 88 MHz
     mmcblk2: mmc2:0001 MMC08G 7.33 GiB
     BUG: sleeping function called from invalid context at kernel/workqueue.c:3347
     in_atomic(): 1, irqs_disabled(): 0, non_block: 0, pid: 35, name: irq/151-ee20000
     preempt_count: 1, expected: 0
     no locks held by irq/151-ee20000/35.
     CPU: 0 PID: 35 Comm: irq/151-ee20000 Not tainted 6.8.0-rc4-ape6evm-00017-g27b57277d9ba #491
     Hardware name: Generic R8A73A4 (Flattened Device Tree)
      unwind_backtrace from show_stack+0x10/0x14
      show_stack from dump_stack_lvl+0x68/0x90
      dump_stack_lvl from __might_resched+0x1ac/0x228
      __might_resched from __flush_work+0x20c/0x2e4
      __flush_work from __cancel_work_timer+0x118/0x198
      __cancel_work_timer from sh_mmcif_irqt+0x38/0x8f8
      sh_mmcif_irqt from irq_thread_fn+0x1c/0x58
      irq_thread_fn from irq_thread+0x10c/0x218
      irq_thread from kthread+0xf0/0x100
      kthread from ret_from_fork+0x14/0x28
     Exception stack(0xf0959fb0 to 0xf0959ff8)
     9fa0:                                     00000000 00000000 00000000 00000000
     9fc0: 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000
     9fe0: 00000000 00000000 00000000 00000000 00000013 00000000
     BUG: scheduling while atomic: irq/151-ee20000/35/0x00000002
     no locks held by irq/151-ee20000/35.
     CPU: 0 PID: 35 Comm: irq/151-ee20000 Tainted: G        W          6.8.0-rc4-ape6evm-00017-g27b57277d9ba #491
     Hardware name: Generic R8A73A4 (Flattened Device Tree)
      unwind_backtrace from show_stack+0x10/0x14
      show_stack from dump_stack_lvl+0x68/0x90
      dump_stack_lvl from __schedule_bug+0x5c/0x7c
      __schedule_bug from __schedule+0xa0/0x9bc
      __schedule from schedule+0x64/0x94
      schedule from irq_thread+0x1dc/0x218
      irq_thread from kthread+0xf0/0x100
      kthread from ret_from_fork+0x14/0x28
     Exception stack(0xf0959fb0 to 0xf0959ff8)
     9fa0:                                     00000000 00000000 00000000 00000000
     9fc0: 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000
     9fe0: 00000000 00000000 00000000 00000000 00000013 00000000
     sh_mmcif ee200000.mmc: Timeout waiting for 2 on CMD18

Reverting this commit fixes the issue.

> --- a/drivers/mmc/host/sh_mmcif.c
> +++ b/drivers/mmc/host/sh_mmcif.c
> @@ -227,14 +227,12 @@ struct sh_mmcif_host {
> 	bool dying;
> 	long timeout;
> 	void __iomem *addr;
> -	u32 *pio_ptr;
> 	spinlock_t lock;		/* protect sh_mmcif_host::state */
> 	enum sh_mmcif_state state;
> 	enum sh_mmcif_wait_for wait_for;
> 	struct delayed_work timeout_work;
> 	size_t blocksize;
> -	int sg_idx;
> -	int sg_blkidx;
> +	struct sg_mapping_iter sg_miter;
> 	bool power;
> 	bool ccs_enable;		/* Command Completion Signal support */
> 	bool clk_ctrl2_enable;
> @@ -600,32 +598,17 @@ static int sh_mmcif_error_manage(struct sh_mmcif_host *host)
> 	return ret;
> }
>
> -static bool sh_mmcif_next_block(struct sh_mmcif_host *host, u32 *p)
> -{
> -	struct mmc_data *data = host->mrq->data;
> -
> -	host->sg_blkidx += host->blocksize;
> -
> -	/* data->sg->length must be a multiple of host->blocksize? */
> -	BUG_ON(host->sg_blkidx > data->sg->length);
> -
> -	if (host->sg_blkidx == data->sg->length) {
> -		host->sg_blkidx = 0;
> -		if (++host->sg_idx < data->sg_len)
> -			host->pio_ptr = sg_virt(++data->sg);
> -	} else {
> -		host->pio_ptr = p;
> -	}
> -
> -	return host->sg_idx != data->sg_len;
> -}
> -
> static void sh_mmcif_single_read(struct sh_mmcif_host *host,
> 				 struct mmc_request *mrq)
> {
> +	struct mmc_data *data = mrq->data;
> +
> 	host->blocksize = (sh_mmcif_readl(host->addr, MMCIF_CE_BLOCK_SET) &
> 			   BLOCK_SIZE_MASK) + 3;
>
> +	sg_miter_start(&host->sg_miter, data->sg, data->sg_len,
> +		       SG_MITER_ATOMIC | SG_MITER_TO_SG);
> +
> 	host->wait_for = MMCIF_WAIT_FOR_READ;
>
> 	/* buf read enable */
> @@ -634,20 +617,32 @@ static void sh_mmcif_single_read(struct sh_mmcif_host *host,
>
> static bool sh_mmcif_read_block(struct sh_mmcif_host *host)
> {
> +	struct sg_mapping_iter *sgm = &host->sg_miter;
> 	struct device *dev = sh_mmcif_host_to_dev(host);
> 	struct mmc_data *data = host->mrq->data;
> -	u32 *p = sg_virt(data->sg);
> +	u32 *p;
> 	int i;
>
> 	if (host->sd_error) {
> +		sg_miter_stop(sgm);
> 		data->error = sh_mmcif_error_manage(host);
> 		dev_dbg(dev, "%s(): %d\n", __func__, data->error);
> 		return false;
> 	}
>
> +	if (!sg_miter_next(sgm)) {
> +		/* This should not happen on single blocks */
> +		sg_miter_stop(sgm);
> +		return false;
> +	}
> +
> +	p = sgm->addr;
> +
> 	for (i = 0; i < host->blocksize / 4; i++)
> 		*p++ = sh_mmcif_readl(host->addr, MMCIF_CE_DATA);
>
> +	sg_miter_stop(&host->sg_miter);
> +
> 	/* buffer read end */
> 	sh_mmcif_bitset(host, MMCIF_CE_INT_MASK, MASK_MBUFRE);
> 	host->wait_for = MMCIF_WAIT_FOR_READ_END;
> @@ -666,34 +661,40 @@ static void sh_mmcif_multi_read(struct sh_mmcif_host *host,
> 	host->blocksize = sh_mmcif_readl(host->addr, MMCIF_CE_BLOCK_SET) &
> 		BLOCK_SIZE_MASK;
>
> +	sg_miter_start(&host->sg_miter, data->sg, data->sg_len,
> +		       SG_MITER_ATOMIC | SG_MITER_TO_SG);
> +
> 	host->wait_for = MMCIF_WAIT_FOR_MREAD;
> -	host->sg_idx = 0;
> -	host->sg_blkidx = 0;
> -	host->pio_ptr = sg_virt(data->sg);
>
> 	sh_mmcif_bitset(host, MMCIF_CE_INT_MASK, MASK_MBUFREN);
> }
>
> static bool sh_mmcif_mread_block(struct sh_mmcif_host *host)
> {
> +	struct sg_mapping_iter *sgm = &host->sg_miter;
> 	struct device *dev = sh_mmcif_host_to_dev(host);
> 	struct mmc_data *data = host->mrq->data;
> -	u32 *p = host->pio_ptr;
> +	u32 *p;
> 	int i;
>
> 	if (host->sd_error) {
> +		sg_miter_stop(sgm);
> 		data->error = sh_mmcif_error_manage(host);
> 		dev_dbg(dev, "%s(): %d\n", __func__, data->error);
> 		return false;
> 	}
>
> -	BUG_ON(!data->sg->length);
> +	if (!sg_miter_next(sgm)) {
> +		sg_miter_stop(sgm);
> +		return false;
> +	}
> +
> +	p = sgm->addr;
>
> 	for (i = 0; i < host->blocksize / 4; i++)
> 		*p++ = sh_mmcif_readl(host->addr, MMCIF_CE_DATA);
>
> -	if (!sh_mmcif_next_block(host, p))
> -		return false;
> +	sgm->consumed = host->blocksize;
>
> 	sh_mmcif_bitset(host, MMCIF_CE_INT_MASK, MASK_MBUFREN);
>
> @@ -703,9 +704,14 @@ static bool sh_mmcif_mread_block(struct sh_mmcif_host *host)
> static void sh_mmcif_single_write(struct sh_mmcif_host *host,
> 					struct mmc_request *mrq)
> {
> +	struct mmc_data *data = mrq->data;
> +
> 	host->blocksize = (sh_mmcif_readl(host->addr, MMCIF_CE_BLOCK_SET) &
> 			   BLOCK_SIZE_MASK) + 3;
>
> +	sg_miter_start(&host->sg_miter, data->sg, data->sg_len,
> +		       SG_MITER_ATOMIC | SG_MITER_FROM_SG);
> +
> 	host->wait_for = MMCIF_WAIT_FOR_WRITE;
>
> 	/* buf write enable */
> @@ -714,20 +720,32 @@ static void sh_mmcif_single_write(struct sh_mmcif_host *host,
>
> static bool sh_mmcif_write_block(struct sh_mmcif_host *host)
> {
> +	struct sg_mapping_iter *sgm = &host->sg_miter;
> 	struct device *dev = sh_mmcif_host_to_dev(host);
> 	struct mmc_data *data = host->mrq->data;
> -	u32 *p = sg_virt(data->sg);
> +	u32 *p;
> 	int i;
>
> 	if (host->sd_error) {
> +		sg_miter_stop(sgm);
> 		data->error = sh_mmcif_error_manage(host);
> 		dev_dbg(dev, "%s(): %d\n", __func__, data->error);
> 		return false;
> 	}
>
> +	if (!sg_miter_next(sgm)) {
> +		/* This should not happen on single blocks */
> +		sg_miter_stop(sgm);
> +		return false;
> +	}
> +
> +	p = sgm->addr;
> +
> 	for (i = 0; i < host->blocksize / 4; i++)
> 		sh_mmcif_writel(host->addr, MMCIF_CE_DATA, *p++);
>
> +	sg_miter_stop(&host->sg_miter);
> +
> 	/* buffer write end */
> 	sh_mmcif_bitset(host, MMCIF_CE_INT_MASK, MASK_MDTRANE);
> 	host->wait_for = MMCIF_WAIT_FOR_WRITE_END;
> @@ -746,34 +764,40 @@ static void sh_mmcif_multi_write(struct sh_mmcif_host *host,
> 	host->blocksize = sh_mmcif_readl(host->addr, MMCIF_CE_BLOCK_SET) &
> 		BLOCK_SIZE_MASK;
>
> +	sg_miter_start(&host->sg_miter, data->sg, data->sg_len,
> +		       SG_MITER_ATOMIC | SG_MITER_FROM_SG);
> +
> 	host->wait_for = MMCIF_WAIT_FOR_MWRITE;
> -	host->sg_idx = 0;
> -	host->sg_blkidx = 0;
> -	host->pio_ptr = sg_virt(data->sg);
>
> 	sh_mmcif_bitset(host, MMCIF_CE_INT_MASK, MASK_MBUFWEN);
> }
>
> static bool sh_mmcif_mwrite_block(struct sh_mmcif_host *host)
> {
> +	struct sg_mapping_iter *sgm = &host->sg_miter;
> 	struct device *dev = sh_mmcif_host_to_dev(host);
> 	struct mmc_data *data = host->mrq->data;
> -	u32 *p = host->pio_ptr;
> +	u32 *p;
> 	int i;
>
> 	if (host->sd_error) {
> +		sg_miter_stop(sgm);
> 		data->error = sh_mmcif_error_manage(host);
> 		dev_dbg(dev, "%s(): %d\n", __func__, data->error);
> 		return false;
> 	}
>
> -	BUG_ON(!data->sg->length);
> +	if (!sg_miter_next(sgm)) {
> +		sg_miter_stop(sgm);
> +		return false;
> +	}
> +
> +	p = sgm->addr;
>
> 	for (i = 0; i < host->blocksize / 4; i++)
> 		sh_mmcif_writel(host->addr, MMCIF_CE_DATA, *p++);
>
> -	if (!sh_mmcif_next_block(host, p))
> -		return false;
> +	sgm->consumed = host->blocksize;
>
> 	sh_mmcif_bitset(host, MMCIF_CE_INT_MASK, MASK_MBUFWEN);
>

Gr{oetje,eeting}s,

 						Geert

--
Geert Uytterhoeven -- There's lots of Linux beyond ia32 -- geert@linux-m68k.org

In personal conversations with technical people, I call myself a hacker. But
when I'm talking to journalists I just say "programmer" or something like that.
 							    -- Linus Torvalds
Linus Walleij Feb. 20, 2024, 11 p.m. UTC | #2
Hi Geert,

sorry for the mess!

On Tue, Feb 20, 2024 at 10:03 PM Geert Uytterhoeven
<geert@linux-m68k.org> wrote:

>      sh_mobile_sdhi ee120000.mmc: mmc1 base at 0xee120000, max clock rate 12 MHz
>      mmc2: new high speed MMC card at address 0001
>      sh_mobile_sdhi ee100000.mmc: mmc0 base at 0xee100000, max clock rate 88 MHz
>      mmcblk2: mmc2:0001 MMC08G 7.33 GiB

Hey it reads some blocks...

>      BUG: sleeping function called from invalid context at kernel/workqueue.c:3347
>      in_atomic(): 1, irqs_disabled(): 0, non_block: 0, pid: 35, name: irq/151-ee20000
(...)
>       __might_resched from __flush_work+0x20c/0x2e4
>       __flush_work from __cancel_work_timer+0x118/0x198
>       __cancel_work_timer from sh_mmcif_irqt+0x38/0x8f8
>       sh_mmcif_irqt from irq_thread_fn+0x1c/0x58

Actually that is the thread so the message is a bit confusing, the irq thread
isn't atomic.

I wonder if it is caused by this:

> > +     sg_miter_start(&host->sg_miter, data->sg, data->sg_len,
> > +                    SG_MITER_ATOMIC | SG_MITER_TO_SG);

...because I don't need to ask for atomic miter here, since the poll
functions are actually called in process context.

I've sent a patch, can you test?
https://lore.kernel.org/linux-mmc/20240220-fix-sh-mmcif-v1-1-b9d08a787c1f@linaro.org/T/#u

Yours,
Linus Walleij
Geert Uytterhoeven Feb. 21, 2024, 9:50 a.m. UTC | #3
Hi Linus,

On Wed, Feb 21, 2024 at 12:01 AM Linus Walleij <linus.walleij@linaro.org> wrote:
> On Tue, Feb 20, 2024 at 10:03 PM Geert Uytterhoeven
> <geert@linux-m68k.org> wrote:
>
> >      sh_mobile_sdhi ee120000.mmc: mmc1 base at 0xee120000, max clock rate 12 MHz
> >      mmc2: new high speed MMC card at address 0001
> >      sh_mobile_sdhi ee100000.mmc: mmc0 base at 0xee100000, max clock rate 88 MHz
> >      mmcblk2: mmc2:0001 MMC08G 7.33 GiB
>
> Hey it reads some blocks...
>
> >      BUG: sleeping function called from invalid context at kernel/workqueue.c:3347
> >      in_atomic(): 1, irqs_disabled(): 0, non_block: 0, pid: 35, name: irq/151-ee20000
> (...)
> >       __might_resched from __flush_work+0x20c/0x2e4
> >       __flush_work from __cancel_work_timer+0x118/0x198
> >       __cancel_work_timer from sh_mmcif_irqt+0x38/0x8f8
> >       sh_mmcif_irqt from irq_thread_fn+0x1c/0x58
>
> Actually that is the thread so the message is a bit confusing, the irq thread
> isn't atomic.
>
> I wonder if it is caused by this:
>
> > > +     sg_miter_start(&host->sg_miter, data->sg, data->sg_len,
> > > +                    SG_MITER_ATOMIC | SG_MITER_TO_SG);
>
> ...because I don't need to ask for atomic miter here, since the poll
> functions are actually called in process context.
>
> I've sent a patch, can you test?
> https://lore.kernel.org/linux-mmc/20240220-fix-sh-mmcif-v1-1-b9d08a787c1f@linaro.org/T/#u

While that patch fixes the BUG, it does not make the eMMC work fully.
It spews:

    sh_mmcif ee200000.mmc: Timeout waiting for 2 on CMD18

and no or limited data is read ("hd /dev/mmcblk..." blocks after no
or two lines of output).

I still need to revert 27b57277d9ba to restore proper operation.

Gr{oetje,eeting}s,

                        Geert
Linus Walleij Feb. 21, 2024, 9:25 p.m. UTC | #4
On Wed, Feb 21, 2024 at 10:50 AM Geert Uytterhoeven
<geert@linux-m68k.org> wrote:

> > I've sent a patch, can you test?
> > https://lore.kernel.org/linux-mmc/20240220-fix-sh-mmcif-v1-1-b9d08a787c1f@linaro.org/T/#u
>
> While that patch fixes the BUG, it does not make the eMMC work fully.
> It spews:
>
>     sh_mmcif ee200000.mmc: Timeout waiting for 2 on CMD18
>
> and no or limited data is read ("hd /dev/mmcblk..." blocks after no
> or two lines of output).
>
> I still need to revert 27b57277d9ba to restore proper operation.

Halfway there. I looked at the code again and now I think I found the
problem causing CMD18 to time out.

I've send a new 2-patch series:
https://lore.kernel.org/linux-mmc/20240221-fix-sh-mmcif-v2-0-5e521eb25ae4@linaro.org/

Yours,
Linus Walleij
Geert Uytterhoeven Feb. 22, 2024, 9:20 a.m. UTC | #5
Hi Linus,

On Wed, Feb 21, 2024 at 10:25 PM Linus Walleij <linus.walleij@linaro.org> wrote:
> On Wed, Feb 21, 2024 at 10:50 AM Geert Uytterhoeven
> <geert@linux-m68k.org> wrote:
> > > I've sent a patch, can you test?
> > > https://lore.kernel.org/linux-mmc/20240220-fix-sh-mmcif-v1-1-b9d08a787c1f@linaro.org/T/#u
> >
> > While that patch fixes the BUG, it does not make the eMMC work fully.
> > It spews:
> >
> >     sh_mmcif ee200000.mmc: Timeout waiting for 2 on CMD18
> >
> > and no or limited data is read ("hd /dev/mmcblk..." blocks after no
> > or two lines of output).
> >
> > I still need to revert 27b57277d9ba to restore proper operation.
>
> Halfway there. I looked at the code again and now I think I found the
> problem causing CMD18 to time out.
>
> I've send a new 2-patch series:
> https://lore.kernel.org/linux-mmc/20240221-fix-sh-mmcif-v2-0-5e521eb25ae4@linaro.org/

Thanks, life's good again ;-)

Gr{oetje,eeting}s,

                        Geert
diff mbox series

Patch

diff --git a/drivers/mmc/host/sh_mmcif.c b/drivers/mmc/host/sh_mmcif.c
index 077d711e964e..1ef6e153e5a3 100644
--- a/drivers/mmc/host/sh_mmcif.c
+++ b/drivers/mmc/host/sh_mmcif.c
@@ -227,14 +227,12 @@  struct sh_mmcif_host {
 	bool dying;
 	long timeout;
 	void __iomem *addr;
-	u32 *pio_ptr;
 	spinlock_t lock;		/* protect sh_mmcif_host::state */
 	enum sh_mmcif_state state;
 	enum sh_mmcif_wait_for wait_for;
 	struct delayed_work timeout_work;
 	size_t blocksize;
-	int sg_idx;
-	int sg_blkidx;
+	struct sg_mapping_iter sg_miter;
 	bool power;
 	bool ccs_enable;		/* Command Completion Signal support */
 	bool clk_ctrl2_enable;
@@ -600,32 +598,17 @@  static int sh_mmcif_error_manage(struct sh_mmcif_host *host)
 	return ret;
 }
 
-static bool sh_mmcif_next_block(struct sh_mmcif_host *host, u32 *p)
-{
-	struct mmc_data *data = host->mrq->data;
-
-	host->sg_blkidx += host->blocksize;
-
-	/* data->sg->length must be a multiple of host->blocksize? */
-	BUG_ON(host->sg_blkidx > data->sg->length);
-
-	if (host->sg_blkidx == data->sg->length) {
-		host->sg_blkidx = 0;
-		if (++host->sg_idx < data->sg_len)
-			host->pio_ptr = sg_virt(++data->sg);
-	} else {
-		host->pio_ptr = p;
-	}
-
-	return host->sg_idx != data->sg_len;
-}
-
 static void sh_mmcif_single_read(struct sh_mmcif_host *host,
 				 struct mmc_request *mrq)
 {
+	struct mmc_data *data = mrq->data;
+
 	host->blocksize = (sh_mmcif_readl(host->addr, MMCIF_CE_BLOCK_SET) &
 			   BLOCK_SIZE_MASK) + 3;
 
+	sg_miter_start(&host->sg_miter, data->sg, data->sg_len,
+		       SG_MITER_ATOMIC | SG_MITER_TO_SG);
+
 	host->wait_for = MMCIF_WAIT_FOR_READ;
 
 	/* buf read enable */
@@ -634,20 +617,32 @@  static void sh_mmcif_single_read(struct sh_mmcif_host *host,
 
 static bool sh_mmcif_read_block(struct sh_mmcif_host *host)
 {
+	struct sg_mapping_iter *sgm = &host->sg_miter;
 	struct device *dev = sh_mmcif_host_to_dev(host);
 	struct mmc_data *data = host->mrq->data;
-	u32 *p = sg_virt(data->sg);
+	u32 *p;
 	int i;
 
 	if (host->sd_error) {
+		sg_miter_stop(sgm);
 		data->error = sh_mmcif_error_manage(host);
 		dev_dbg(dev, "%s(): %d\n", __func__, data->error);
 		return false;
 	}
 
+	if (!sg_miter_next(sgm)) {
+		/* This should not happen on single blocks */
+		sg_miter_stop(sgm);
+		return false;
+	}
+
+	p = sgm->addr;
+
 	for (i = 0; i < host->blocksize / 4; i++)
 		*p++ = sh_mmcif_readl(host->addr, MMCIF_CE_DATA);
 
+	sg_miter_stop(&host->sg_miter);
+
 	/* buffer read end */
 	sh_mmcif_bitset(host, MMCIF_CE_INT_MASK, MASK_MBUFRE);
 	host->wait_for = MMCIF_WAIT_FOR_READ_END;
@@ -666,34 +661,40 @@  static void sh_mmcif_multi_read(struct sh_mmcif_host *host,
 	host->blocksize = sh_mmcif_readl(host->addr, MMCIF_CE_BLOCK_SET) &
 		BLOCK_SIZE_MASK;
 
+	sg_miter_start(&host->sg_miter, data->sg, data->sg_len,
+		       SG_MITER_ATOMIC | SG_MITER_TO_SG);
+
 	host->wait_for = MMCIF_WAIT_FOR_MREAD;
-	host->sg_idx = 0;
-	host->sg_blkidx = 0;
-	host->pio_ptr = sg_virt(data->sg);
 
 	sh_mmcif_bitset(host, MMCIF_CE_INT_MASK, MASK_MBUFREN);
 }
 
 static bool sh_mmcif_mread_block(struct sh_mmcif_host *host)
 {
+	struct sg_mapping_iter *sgm = &host->sg_miter;
 	struct device *dev = sh_mmcif_host_to_dev(host);
 	struct mmc_data *data = host->mrq->data;
-	u32 *p = host->pio_ptr;
+	u32 *p;
 	int i;
 
 	if (host->sd_error) {
+		sg_miter_stop(sgm);
 		data->error = sh_mmcif_error_manage(host);
 		dev_dbg(dev, "%s(): %d\n", __func__, data->error);
 		return false;
 	}
 
-	BUG_ON(!data->sg->length);
+	if (!sg_miter_next(sgm)) {
+		sg_miter_stop(sgm);
+		return false;
+	}
+
+	p = sgm->addr;
 
 	for (i = 0; i < host->blocksize / 4; i++)
 		*p++ = sh_mmcif_readl(host->addr, MMCIF_CE_DATA);
 
-	if (!sh_mmcif_next_block(host, p))
-		return false;
+	sgm->consumed = host->blocksize;
 
 	sh_mmcif_bitset(host, MMCIF_CE_INT_MASK, MASK_MBUFREN);
 
@@ -703,9 +704,14 @@  static bool sh_mmcif_mread_block(struct sh_mmcif_host *host)
 static void sh_mmcif_single_write(struct sh_mmcif_host *host,
 					struct mmc_request *mrq)
 {
+	struct mmc_data *data = mrq->data;
+
 	host->blocksize = (sh_mmcif_readl(host->addr, MMCIF_CE_BLOCK_SET) &
 			   BLOCK_SIZE_MASK) + 3;
 
+	sg_miter_start(&host->sg_miter, data->sg, data->sg_len,
+		       SG_MITER_ATOMIC | SG_MITER_FROM_SG);
+
 	host->wait_for = MMCIF_WAIT_FOR_WRITE;
 
 	/* buf write enable */
@@ -714,20 +720,32 @@  static void sh_mmcif_single_write(struct sh_mmcif_host *host,
 
 static bool sh_mmcif_write_block(struct sh_mmcif_host *host)
 {
+	struct sg_mapping_iter *sgm = &host->sg_miter;
 	struct device *dev = sh_mmcif_host_to_dev(host);
 	struct mmc_data *data = host->mrq->data;
-	u32 *p = sg_virt(data->sg);
+	u32 *p;
 	int i;
 
 	if (host->sd_error) {
+		sg_miter_stop(sgm);
 		data->error = sh_mmcif_error_manage(host);
 		dev_dbg(dev, "%s(): %d\n", __func__, data->error);
 		return false;
 	}
 
+	if (!sg_miter_next(sgm)) {
+		/* This should not happen on single blocks */
+		sg_miter_stop(sgm);
+		return false;
+	}
+
+	p = sgm->addr;
+
 	for (i = 0; i < host->blocksize / 4; i++)
 		sh_mmcif_writel(host->addr, MMCIF_CE_DATA, *p++);
 
+	sg_miter_stop(&host->sg_miter);
+
 	/* buffer write end */
 	sh_mmcif_bitset(host, MMCIF_CE_INT_MASK, MASK_MDTRANE);
 	host->wait_for = MMCIF_WAIT_FOR_WRITE_END;
@@ -746,34 +764,40 @@  static void sh_mmcif_multi_write(struct sh_mmcif_host *host,
 	host->blocksize = sh_mmcif_readl(host->addr, MMCIF_CE_BLOCK_SET) &
 		BLOCK_SIZE_MASK;
 
+	sg_miter_start(&host->sg_miter, data->sg, data->sg_len,
+		       SG_MITER_ATOMIC | SG_MITER_FROM_SG);
+
 	host->wait_for = MMCIF_WAIT_FOR_MWRITE;
-	host->sg_idx = 0;
-	host->sg_blkidx = 0;
-	host->pio_ptr = sg_virt(data->sg);
 
 	sh_mmcif_bitset(host, MMCIF_CE_INT_MASK, MASK_MBUFWEN);
 }
 
 static bool sh_mmcif_mwrite_block(struct sh_mmcif_host *host)
 {
+	struct sg_mapping_iter *sgm = &host->sg_miter;
 	struct device *dev = sh_mmcif_host_to_dev(host);
 	struct mmc_data *data = host->mrq->data;
-	u32 *p = host->pio_ptr;
+	u32 *p;
 	int i;
 
 	if (host->sd_error) {
+		sg_miter_stop(sgm);
 		data->error = sh_mmcif_error_manage(host);
 		dev_dbg(dev, "%s(): %d\n", __func__, data->error);
 		return false;
 	}
 
-	BUG_ON(!data->sg->length);
+	if (!sg_miter_next(sgm)) {
+		sg_miter_stop(sgm);
+		return false;
+	}
+
+	p = sgm->addr;
 
 	for (i = 0; i < host->blocksize / 4; i++)
 		sh_mmcif_writel(host->addr, MMCIF_CE_DATA, *p++);
 
-	if (!sh_mmcif_next_block(host, p))
-		return false;
+	sgm->consumed = host->blocksize;
 
 	sh_mmcif_bitset(host, MMCIF_CE_INT_MASK, MASK_MBUFWEN);