Hi Prabu,

Do you have any performance result or other?

Best Regards,
Jaehoon Chung

On 08/21/2013 06:33 PM, Prabu Thangamuthu wrote:
> Synopsys DW_MMC IP core supports Internal DMA Controller with 64-bit address 
> mode from IP version 2.70a onwards.
> Updated the driver to support IDMAC 64-bit addressing mode.
> 
> Tested the features in DW_MMC IP core v2.70a and v2.40a with HAPS-51 setup 
> and driver is working fine.
> 
> Signed-off-by: Prabu Thangamuthu <[email protected]>
> ---
> Change log v4:
>       - Add the dynamic support for 32-bit and 64-bit address mode based on 
> hw configuration.
>       - Removed the CONFIG_MMC_DW_IDMAC_64BIT_ADDRESS macro.
>       
> Change log v3:
>       -Add the condition check to find the IDMAC configuration mismatches 
> between sw & hw.
>       -Driver should not use the IDMAC if any conflict between sw & hw 
> configurations
>        since the register offsets are different for both 32-bit and 64-bit 
> configurations.
>       -Reused the existing code.
>       
> Change log v2:
>       -Add the configuration.
> 
>  drivers/mmc/host/dw_mmc.c  |  195 
> +++++++++++++++++++++++++++++++++++---------
>  drivers/mmc/host/dw_mmc.h  |   11 +++
>  include/linux/mmc/dw_mmc.h |    2 +
>  3 files changed, 169 insertions(+), 39 deletions(-)
> 
> diff --git a/drivers/mmc/host/dw_mmc.c b/drivers/mmc/host/dw_mmc.c
> index ee5f167..523b3d7 100644
> --- a/drivers/mmc/host/dw_mmc.c
> +++ b/drivers/mmc/host/dw_mmc.c
> @@ -55,7 +55,23 @@
>                                SDMMC_IDMAC_INT_CES | SDMMC_IDMAC_INT_DU | \
>                                SDMMC_IDMAC_INT_FBE | SDMMC_IDMAC_INT_RI | \
>                                SDMMC_IDMAC_INT_TI)
> +struct idmac_desc_64addr {
> +     u32             des0;   /* Control Descriptor */
> +
> +     u32             des1;   /* Reserved */
> +
> +     u32             des2;   /*Buffer sizes */
> +#define IDMAC_64ADDR_SET_BUFFER1_SIZE(d, s) \
> +     ((d)->des2 = ((d)->des2 & 0x03ffe000) | ((s) & 0x1fff))
> +
> +     u32             des3;   /* Reserved */
> +
> +     u32             des4;   /* Lower 32-bits of Buffer Address Pointer 1*/
> +     u32             des5;   /* Upper 32-bits of Buffer Address Pointer 1*/
>  
> +     u32             des6;   /* Lower 32-bits of Next Descriptor Address */
> +     u32             des7;   /* Upper 32-bits of Next Descriptor Address */
> +};
>  struct idmac_desc {
>       u32             des0;   /* Control Descriptor */
>  #define IDMAC_DES0_DIC       BIT(1)
> @@ -369,30 +385,67 @@ static void dw_mci_translate_sglist(struct dw_mci 
> *host, struct mmc_data *data,
>                                   unsigned int sg_len)
>  {
>       int i;
> -     struct idmac_desc *desc = host->sg_cpu;
>  
> -     for (i = 0; i < sg_len; i++, desc++) {
> -             unsigned int length = sg_dma_len(&data->sg[i]);
> -             u32 mem_addr = sg_dma_address(&data->sg[i]);
> +     if (host->dma_64bit_address == 1) {
> +             struct idmac_desc_64addr *desc = host->sg_cpu;
> +
> +             for (i = 0; i < sg_len; i++, desc++) {
> +                     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;
> +             }
> +
> +             /* Set first descriptor */
> +             desc = host->sg_cpu;
> +             desc->des0 |= IDMAC_DES0_FD;
>  
> -             /* Set the OWN bit and disable interrupts for this descriptor */
> -             desc->des0 = IDMAC_DES0_OWN | IDMAC_DES0_DIC | IDMAC_DES0_CH;
> +             /* 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;
>  
> -             /* Buffer length */
> -             IDMAC_SET_BUFFER1_SIZE(desc, length);
> +     } else {
> +             struct idmac_desc *desc = host->sg_cpu;
>  
> -             /* Physical address to DMA to/from */
> -             desc->des2 = mem_addr;
> -     }
> +             for (i = 0; i < sg_len; i++, desc++) {
> +                     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 = IDMAC_DES0_OWN | IDMAC_DES0_DIC |
> +                                             IDMAC_DES0_CH;
> +                     /* Buffer length */
> +                     IDMAC_SET_BUFFER1_SIZE(desc, length);
> +
> +                     /* Physical address to DMA to/from */
> +                     desc->des2 = mem_addr;
> +             }
>  
> -     /* Set first descriptor */
> -     desc = host->sg_cpu;
> -     desc->des0 |= IDMAC_DES0_FD;
> +             /* Set first descriptor */
> +             desc = host->sg_cpu;
> +             desc->des0 |= IDMAC_DES0_FD;
>  
> -     /* Set last descriptor */
> -     desc = host->sg_cpu + (i - 1) * sizeof(struct idmac_desc);
> -     desc->des0 &= ~(IDMAC_DES0_CH | IDMAC_DES0_DIC);
> -     desc->des0 |= IDMAC_DES0_LD;
> +             /* Set last descriptor */
> +             desc = host->sg_cpu + (i - 1) * sizeof(struct idmac_desc);
> +             desc->des0 &= ~(IDMAC_DES0_CH | IDMAC_DES0_DIC);
> +             desc->des0 |= IDMAC_DES0_LD;
> +     }
>  
>       wmb();
>  }
> @@ -421,29 +474,66 @@ static void dw_mci_idmac_start_dma(struct dw_mci *host, 
> unsigned int sg_len)
>  
>  static int dw_mci_idmac_init(struct dw_mci *host)
>  {
> -     struct idmac_desc *p;
>       int i;
> +     if (host->dma_64bit_address == 1) {
> +             struct idmac_desc_64addr *p;
> +             /* Number of descriptors in the ring buffer */
> +             host->ring_size = PAGE_SIZE / sizeof(struct idmac_desc_64addr);
> +
> +             /* Forward link the descriptor list */
> +             for (i = 0, p = host->sg_cpu; i < host->ring_size - 1;
> +                                                             i++, p++) {
> +                     p->des6 = (host->sg_dma +
> +                                     (sizeof(struct idmac_desc_64addr) *
> +                                                     (i + 1))) & 0xffffffff;
> +
> +                     p->des7 = (host->sg_dma +
> +                                     (sizeof(struct idmac_desc_64addr) *
> +                                                     (i + 1))) >> 32;
> +             }
>  
> -     /* Number of descriptors in the ring buffer */
> -     host->ring_size = PAGE_SIZE / sizeof(struct idmac_desc);
> +             /* Set the last descriptor as the end-of-ring descriptor */
> +             p->des6 = host->sg_dma & 0xffffffff;
> +             p->des7 = host->sg_dma >> 32;
> +             p->des0 = IDMAC_DES0_ER;
>  
> -     /* Forward link the descriptor list */
> -     for (i = 0, p = host->sg_cpu; i < host->ring_size - 1; i++, p++)
> -             p->des3 = host->sg_dma + (sizeof(struct idmac_desc) * (i + 1));
> +     } else {
> +             struct idmac_desc *p;
> +             /* Number of descriptors in the ring buffer */
> +             host->ring_size = PAGE_SIZE / sizeof(struct idmac_desc);
>  
> -     /* Set the last descriptor as the end-of-ring descriptor */
> -     p->des3 = host->sg_dma;
> -     p->des0 = IDMAC_DES0_ER;
> +             /* Forward link the descriptor list */
> +             for (i = 0, p = host->sg_cpu; i < host->ring_size - 1; i++, p++)
> +                     p->des3 = host->sg_dma + (sizeof(struct idmac_desc) *
> +                                                             (i + 1));
> +
> +             /* Set the last descriptor as the end-of-ring descriptor */
> +             p->des3 = host->sg_dma;
> +             p->des0 = IDMAC_DES0_ER;
> +     }
>  
>       mci_writel(host, BMOD, SDMMC_IDMAC_SWRESET);
>  
> -     /* Mask out interrupts - get Tx & Rx complete only */
> -     mci_writel(host, IDSTS, IDMAC_INT_CLR);
> -     mci_writel(host, IDINTEN, SDMMC_IDMAC_INT_NI | SDMMC_IDMAC_INT_RI |
> -                SDMMC_IDMAC_INT_TI);
> +     if (host->dma_64bit_address == 1) {
> +             /* Mask out interrupts - get Tx & Rx complete only */
> +             mci_writel(host, IDSTS64, IDMAC_INT_CLR);
> +             mci_writel(host, IDINTEN64, SDMMC_IDMAC_INT_NI |
> +                             SDMMC_IDMAC_INT_RI | SDMMC_IDMAC_INT_TI);
> +
> +             /* Set the descriptor base address */
> +             mci_writel(host, DBADDRL, host->sg_dma & 0xffffffff);
> +             mci_writel(host, DBADDRU, host->sg_dma >> 32);
> +
> +     } else {
> +             /* Mask out interrupts - get Tx & Rx complete only */
> +             mci_writel(host, IDSTS, IDMAC_INT_CLR);
> +             mci_writel(host, IDINTEN, SDMMC_IDMAC_INT_NI |
> +                             SDMMC_IDMAC_INT_RI | SDMMC_IDMAC_INT_TI);
> +
> +             /* Set the descriptor base address */
> +             mci_writel(host, DBADDR, host->sg_dma);
> +     }
>  
> -     /* Set the descriptor base address */
> -     mci_writel(host, DBADDR, host->sg_dma);
>       return 0;
>  }
>  
> @@ -1677,11 +1767,22 @@ static irqreturn_t dw_mci_interrupt(int irq, void 
> *dev_id)
>  
>  #ifdef CONFIG_MMC_DW_IDMAC
>       /* Handle DMA interrupts */
> -     pending = mci_readl(host, IDSTS);
> -     if (pending & (SDMMC_IDMAC_INT_TI | SDMMC_IDMAC_INT_RI)) {
> -             mci_writel(host, IDSTS, SDMMC_IDMAC_INT_TI | 
> SDMMC_IDMAC_INT_RI);
> -             mci_writel(host, IDSTS, SDMMC_IDMAC_INT_NI);
> -             host->dma_ops->complete(host);
> +     if (host->dma_64bit_address == 1) {
> +             pending = mci_readl(host, IDSTS64);
> +             if (pending & (SDMMC_IDMAC_INT_TI | SDMMC_IDMAC_INT_RI)) {
> +                     mci_writel(host, IDSTS64, SDMMC_IDMAC_INT_TI |
> +                                                     SDMMC_IDMAC_INT_RI);
> +                     mci_writel(host, IDSTS64, SDMMC_IDMAC_INT_NI);
> +                     host->dma_ops->complete(host);
> +             }
> +     } else {
> +             pending = mci_readl(host, IDSTS);
> +             if (pending & (SDMMC_IDMAC_INT_TI | SDMMC_IDMAC_INT_RI)) {
> +                     mci_writel(host, IDSTS, SDMMC_IDMAC_INT_TI |
> +                                                     SDMMC_IDMAC_INT_RI);
> +                     mci_writel(host, IDSTS, SDMMC_IDMAC_INT_NI);
> +                     host->dma_ops->complete(host);
> +             }
>       }
>  #endif
>  
> @@ -2036,6 +2137,22 @@ static void dw_mci_cleanup_slot(struct dw_mci_slot 
> *slot, unsigned int id)
>  
>  static void dw_mci_init_dma(struct dw_mci *host)
>  {
> +     int addr_config;
> +     /* Check ADDR_CONFIG bit in HCON to find IDMAC address bus width */
> +     addr_config = (mci_readl(host, HCON) >> 27) & 0x01;
> +
> +     if (addr_config == 1) {
> +             /* host supports IDMAC in 64-bit address mode */
> +             host->dma_64bit_address = 1;
> +             dev_info(host->dev, "IDMAC supports 64-bit address mode.\n");
> +             if (!dma_set_mask(host->dev, DMA_BIT_MASK(64)))
> +                     dma_set_coherent_mask(host->dev, DMA_BIT_MASK(64));
> +     } else {
> +             /* host supports IDMAC in 32-bit address mode */
> +             host->dma_64bit_address = 0;
> +             dev_info(host->dev, "IDMAC supports 32-bit address mode.\n");
> +     }
> +
>       /* Alloc memory for sg translation */
>       host->sg_cpu = dmam_alloc_coherent(host->dev, PAGE_SIZE,
>                                         &host->sg_dma, GFP_KERNEL);
> diff --git a/drivers/mmc/host/dw_mmc.h b/drivers/mmc/host/dw_mmc.h
> index 81b2994..7414656 100644
> --- a/drivers/mmc/host/dw_mmc.h
> +++ b/drivers/mmc/host/dw_mmc.h
> @@ -54,6 +54,17 @@
>  #define SDMMC_DSCADDR                0x094
>  #define SDMMC_BUFADDR                0x098
>  #define SDMMC_DATA(x)                (x)
> +/*
> +* Registers to support idmac 64-bit address mode
> +*/
> +#define SDMMC_DBADDRL                0x088
> +#define SDMMC_DBADDRU                0x08c
> +#define SDMMC_IDSTS64                0x090
> +#define SDMMC_IDINTEN64              0x094
> +#define SDMMC_DSCADDRL               0x098
> +#define SDMMC_DSCADDRU               0x09c
> +#define SDMMC_BUFADDRL               0x0A0
> +#define SDMMC_BUFADDRU               0x0A4
>  
>  /*
>   * Data offset is difference according to Version
> diff --git a/include/linux/mmc/dw_mmc.h b/include/linux/mmc/dw_mmc.h
> index 198f0fa..7460b04 100644
> --- a/include/linux/mmc/dw_mmc.h
> +++ b/include/linux/mmc/dw_mmc.h
> @@ -51,6 +51,7 @@ struct mmc_data;
>   *   transfer is in progress.
>   * @use_dma: Whether DMA channel is initialized or not.
>   * @using_dma: Whether DMA is in use for the current transfer.
> + * @dma_64bit_address: Whether DMA supports 64-bit address mode or not.
>   * @sg_dma: Bus address of DMA buffer.
>   * @sg_cpu: Virtual address of DMA buffer.
>   * @dma_ops: Pointer to platform-specific DMA callbacks.
> @@ -134,6 +135,7 @@ struct dw_mci {
>       /* DMA interface members*/
>       int                     use_dma;
>       int                     using_dma;
> +     int                     dma_64bit_address;
>  
>       dma_addr_t              sg_dma;
>       void                    *sg_cpu;
> 

--
To unsubscribe from this list: send the line "unsubscribe linux-mmc" in
the body of a message to [email protected]
More majordomo info at  http://vger.kernel.org/majordomo-info.html

Reply via email to