Return-Path: Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1752831AbbGHEO1 (ORCPT ); Wed, 8 Jul 2015 00:14:27 -0400 Received: from mailout2.samsung.com ([203.254.224.25]:44465 "EHLO mailout2.samsung.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1751066AbbGHEOH (ORCPT ); Wed, 8 Jul 2015 00:14:07 -0400 X-AuditID: cbfee68f-f793b6d000005f66-bf-559ca38dfd0a Message-id: <559CA38D.10700@samsung.com> Date: Wed, 08 Jul 2015 13:14:05 +0900 From: Jaehoon Chung User-Agent: Mozilla/5.0 (X11; Linux i686; rv:31.0) Gecko/20100101 Thunderbird/31.7.0 MIME-version: 1.0 To: Alexey Brodkin , linux-mmc@vger.kernel.org Cc: Seungwon Jeon , Jaehoon Chung , Ulf Hansson , arc-linux-dev@synopsys.com, linux-kernel@vger.kernel.org Subject: Re: [PATCH] mmc: dw_mmc: handle data blocks > than 4kB if IDMAC is used References: <1435220707-7250-1-git-send-email-abrodkin@synopsys.com> In-reply-to: <1435220707-7250-1-git-send-email-abrodkin@synopsys.com> Content-type: text/plain; charset=windows-1252 Content-transfer-encoding: 7bit X-Brightmail-Tracker: H4sIAAAAAAAAA+NgFprNIsWRmVeSWpSXmKPExsWyRsSkULd38ZxQg/WHrS3Wfb3NZPFjd5XF jV9trBaXd81hszjyv5/R4sP9i8wWx9eGO7B73Lm2h82jb8sqRo8t+z8zenzeJBfAEsVlk5Ka k1mWWqRvl8CVsetxC1PBEbOKAwc/szYwHtTsYuTkkBAwkdi4fSYjhC0mceHeerYuRi4OIYGl jBI/eh4zwxTtPviYBSKxiFFi8eXzrBDOA0aJs5ufs4FU8QpoSKxoPAw0ioODRUBV4uerOJAw m4COxPZvx5lAbFGBMIkzMzpYIMoFJX5Mvgdmiwh4S9zYcg5sJrPAFkaJ1y+/gTUICwRIHFrc CzZfSMBF4vDsjWCncgq4ShxZOZsNZBezgJ7E/YtaIGFmAXmJzWveMoPMkRA4xS7x4/FfsAUs AgIS3yYfYgGplxCQldh0AOoxSYmDK26wTGAUm4XkpFkIU2chmbqAkXkVo2hqQXJBcVJ6kbFe cWJucWleul5yfu4mRmCcnf73rH8H490D1ocYBTgYlXh4PWLmhAqxJpYVV+YeYjQFOmIis5Ro cj4wmvNK4g2NzYwsTE1MjY3MLc2UxHkXSv0MFhJITyxJzU5NLUgtii8qzUktPsTIxMEp1cCY n3csp7mD9dGqS7FtyycXM/xVlc/kkhHwv/pfzm1SAuP6kg8XsvMK1NLdN4g22Yh8Djyhrbvv 93KbYvsV67m1l/fItzubr9P3vVFVpB78oPXfrzNrM9I3725T52x/rrc8b3vNZneBuywnqh79 nNHxozOX3eiJp+9RPwEjabe17E0rXbcwz1FiKc5INNRiLipOBADfwn2hrgIAAA== X-Brightmail-Tracker: H4sIAAAAAAAAA+NgFnrKIsWRmVeSWpSXmKPExsVy+t9jAd3exXNCDaZeMLVY9/U2k8WP3VUW N361sVpc3jWHzeLI/35Giw/3LzJbHF8b7sDucefaHjaPvi2rGD227P/M6PF5k1wAS1QDo01G amJKapFCal5yfkpmXrqtkndwvHO8qZmBoa6hpYW5kkJeYm6qrZKLT4CuW2YO0AFKCmWJOaVA oYDE4mIlfTtME0JD3HQtYBojdH1DguB6jAzQQMIaxoxdj1uYCo6YVRw4+Jm1gfGgZhcjJ4eE gInE7oOPWSBsMYkL99azdTFycQgJLGKUWHz5PCuE84BR4uzm52wgVbwCGhIrGg8zdjFycLAI qEr8fBUHEmYT0JHY/u04E4gtKhAmcWZGBwtEuaDEj8n3wGwRAW+JG1vOgc1kFtjCKPH65Tew BmGBAIlDi3vB5gsJuEgcnr2REcTmFHCVOLJyNhvILmYBPYn7F7VAwswC8hKb17xlnsAoMAvJ ilkIVbOQVC1gZF7FKJpakFxQnJSea6hXnJhbXJqXrpecn7uJERzFz6R2MK5ssDjEKMDBqMTD +zFyTqgQa2JZcWXuIUYJDmYlEd4Ed6AQb0piZVVqUX58UWlOavEhRlOg/ycyS4km5wMTTF5J vKGxiZmRpZG5oYWRsbmSOO/JfJ9QIYH0xJLU7NTUgtQimD4mDk6pBkb3B8wb5xjHNR7Nsp6n cOCa2+1S9zUCFjcDdv2+YrjA+vpx/SlpIb9sNMrblgbliYUKZaV+/zd3w4HD77oLw9bwXK1Z Jf34ue9th60v31uezuN4q9/cNmnZ9SkzeLifLFM/Jfz/6tGzS9Ucn4e1fom8y7bcW2f32Tv6 LMp1af9nMxeeynw0eeUDJZbijERDLeai4kQAYc2HhfgCAAA= DLP-Filter: Pass X-MTR: 20000000000000000@CPGS X-CFilter-Loop: Reflected Sender: linux-kernel-owner@vger.kernel.org List-ID: X-Mailing-List: linux-kernel@vger.kernel.org Content-Length: 7016 Lines: 214 Hi, Alexey. On 06/25/2015 05:25 PM, Alexey Brodkin wrote: > As per DW MobileStorage databook "each descriptor can transfer up to 4kB > of data in chained mode", moreover buffer size that is put in "des1" is > limited to 13 bits, i.e. for example on attempt to > IDMAC_SET_BUFFER1_SIZE(desc, 8192) size value that's effectively written > will be 0. > > On the platform with 8kB PAGE_SIZE I see dw_mmc gets data blocks in > SG-list of 8kB size and that leads to unpredictable behavior of the > SD/MMC controller. I didn't see your problem, since i didn't test with 8K PAGE_SIZE. But I think your patch is reasonable. As possible, I want to know in more detail what unpredictable behavior. (Just stuck behavior?) > > In particular on write to FAT partition of SD-card the controller will > stuck in the middle of DMA transaction. > > Solution to the problem is simple - we need to pass large (> 4kB) data > buffers to the controller via multiple descriptors. And that's what > that change does. > > What's interesting I did try original driver on same platform but > configured with 4kB PAGE_SIZE and may confirm that data blocks passed > in SG-list to dw_mmc never exeed 4kB limit - that explains why nobody > ever faced a problem I did. > > Signed-off-by: Alexey Brodkin > Cc: Seungwon Jeon > Cc: Jaehoon Chung > Cc: Ulf Hansson > Cc: arc-linux-dev@synopsys.com > Cc: linux-kernel@vger.kernel.org > --- > drivers/mmc/host/dw_mmc.c | 109 ++++++++++++++++++++++++++++++---------------- > 1 file changed, 71 insertions(+), 38 deletions(-) > > diff --git a/drivers/mmc/host/dw_mmc.c b/drivers/mmc/host/dw_mmc.c > index 40e9d8e..e41fb74 100644 > --- a/drivers/mmc/host/dw_mmc.c > +++ b/drivers/mmc/host/dw_mmc.c > @@ -99,6 +99,9 @@ struct idmac_desc { > > __le32 des3; /* buffer 2 physical address */ > }; > + > +/* Each descriptor can transfer up to 4KB of data in chained mode */ > +#define DW_MCI_DESC_DATA_LENGTH 0x1000 > #endif /* CONFIG_MMC_DW_IDMAC */ > > static bool dw_mci_reset(struct dw_mci *host); > @@ -462,66 +465,96 @@ static void dw_mci_idmac_complete_dma(struct dw_mci *host) > static void dw_mci_translate_sglist(struct dw_mci *host, struct mmc_data *data, > unsigned int sg_len) > { > + unsigned int desc_len; > int i; > if (host->dma_64bit_address == 1) { > - struct idmac_desc_64addr *desc = host->sg_cpu; > + struct idmac_desc_64addr *desc_first, *desc_last, *desc; Why it needs desc_first? > + > + desc_first = desc_last = desc = host->sg_cpu; > > - for (i = 0; i < sg_len; i++, desc++) { > + for (i = 0; i < sg_len; i++) { > unsigned int length = sg_dma_len(&data->sg[i]); > u64 mem_addr = sg_dma_address(&data->sg[i]); > > - /* > - * Set the OWN bit and disable interrupts for this > - * descriptor > - */ > - desc->des0 = IDMAC_DES0_OWN | IDMAC_DES0_DIC | > - IDMAC_DES0_CH; > - /* Buffer length */ > - IDMAC_64ADDR_SET_BUFFER1_SIZE(desc, length); > - > - /* Physical address to DMA to/from */ > - desc->des4 = mem_addr & 0xffffffff; > - desc->des5 = mem_addr >> 32; > + for ( ; length ; desc++) { Is there no infinite loop case? Best Regards, Jaehoon Chung > + desc_len = (length <= DW_MCI_DESC_DATA_LENGTH) ? > + length : DW_MCI_DESC_DATA_LENGTH; > + > + length -= desc_len; > + > + /* > + * Set the OWN bit and disable interrupts > + * for this descriptor > + */ > + desc->des0 = IDMAC_DES0_OWN | IDMAC_DES0_DIC | > + IDMAC_DES0_CH; > + > + /* Buffer length */ > + IDMAC_64ADDR_SET_BUFFER1_SIZE(desc, desc_len); > + > + /* Physical address to DMA to/from */ > + desc->des4 = mem_addr & 0xffffffff; > + desc->des5 = mem_addr >> 32; > + > + /* Update physical address for the next desc */ > + mem_addr += desc_len; > + > + /* Save pointer to the last descriptor */ > + desc_last = desc; > + } > } > > /* Set first descriptor */ > - desc = host->sg_cpu; > - desc->des0 |= IDMAC_DES0_FD; > + desc_first->des0 |= IDMAC_DES0_FD; > > /* Set last descriptor */ > - desc = host->sg_cpu + (i - 1) * > - sizeof(struct idmac_desc_64addr); > - desc->des0 &= ~(IDMAC_DES0_CH | IDMAC_DES0_DIC); > - desc->des0 |= IDMAC_DES0_LD; > + desc_last->des0 &= ~(IDMAC_DES0_CH | IDMAC_DES0_DIC); > + desc_last->des0 |= IDMAC_DES0_LD; > > } else { > - struct idmac_desc *desc = host->sg_cpu; > + struct idmac_desc *desc_first, *desc_last, *desc; > + > + desc_first = desc_last = desc = host->sg_cpu; > > - for (i = 0; i < sg_len; i++, desc++) { > + for (i = 0; i < sg_len; i++) { > unsigned int length = sg_dma_len(&data->sg[i]); > u32 mem_addr = sg_dma_address(&data->sg[i]); > > - /* > - * Set the OWN bit and disable interrupts for this > - * descriptor > - */ > - desc->des0 = cpu_to_le32(IDMAC_DES0_OWN | > - IDMAC_DES0_DIC | IDMAC_DES0_CH); > - /* Buffer length */ > - IDMAC_SET_BUFFER1_SIZE(desc, length); > + for ( ; length ; desc++) { > + desc_len = (length <= DW_MCI_DESC_DATA_LENGTH) ? > + length : DW_MCI_DESC_DATA_LENGTH; > + > + length -= desc_len; > + > + /* > + * Set the OWN bit and disable interrupts > + * for this descriptor > + */ > + desc->des0 = cpu_to_le32(IDMAC_DES0_OWN | > + IDMAC_DES0_DIC | > + IDMAC_DES0_CH); > + > + /* Buffer length */ > + IDMAC_SET_BUFFER1_SIZE(desc, desc_len); > > - /* Physical address to DMA to/from */ > - desc->des2 = cpu_to_le32(mem_addr); > + /* Physical address to DMA to/from */ > + desc->des2 = cpu_to_le32(mem_addr); > + > + /* Update physical address for the next desc */ > + mem_addr += desc_len; > + > + /* Save pointer to the last descriptor */ > + desc_last = desc; > + } > } > > /* Set first descriptor */ > - desc = host->sg_cpu; > - desc->des0 |= cpu_to_le32(IDMAC_DES0_FD); > + desc_first->des0 |= cpu_to_le32(IDMAC_DES0_FD); > > /* Set last descriptor */ > - desc = host->sg_cpu + (i - 1) * sizeof(struct idmac_desc); > - desc->des0 &= cpu_to_le32(~(IDMAC_DES0_CH | IDMAC_DES0_DIC)); > - desc->des0 |= cpu_to_le32(IDMAC_DES0_LD); > + desc_last->des0 &= cpu_to_le32(~(IDMAC_DES0_CH | > + IDMAC_DES0_DIC)); > + desc_last->des0 |= cpu_to_le32(IDMAC_DES0_LD); > } > > wmb(); > @@ -2394,7 +2427,7 @@ static int dw_mci_init_slot(struct dw_mci *host, unsigned int id) > #ifdef CONFIG_MMC_DW_IDMAC > mmc->max_segs = host->ring_size; > mmc->max_blk_size = 65536; > - mmc->max_seg_size = 0x1000; > + mmc->max_seg_size = DW_MCI_DESC_DATA_LENGTH; > mmc->max_req_size = mmc->max_seg_size * host->ring_size; > mmc->max_blk_count = mmc->max_req_size / 512; > #else > -- To unsubscribe from this list: send the line "unsubscribe linux-kernel" in the body of a message to majordomo@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html Please read the FAQ at http://www.tux.org/lkml/