mmc: dw_mmc: Add IDMAC 64-bit address mode support
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. Signed-off-by: Prabu Thangamuthu <prabu.t@synopsys.com> Reviewed-by: Alim Akhtar <alim.akhtar@samsung.com> Acked-by: Jaehoon Chung <jh80.chung@samsung.com> Signed-off-by: Ulf Hansson <ulf.hansson@linaro.org>
This commit is contained in:
parent
e765bfa22a
commit
69d99fdcfd
|
@ -61,6 +61,24 @@
|
|||
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)
|
||||
|
@ -414,31 +432,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;
|
||||
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]);
|
||||
u32 mem_addr = sg_dma_address(&data->sg[i]);
|
||||
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;
|
||||
/*
|
||||
* 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);
|
||||
|
||||
/* Buffer length */
|
||||
IDMAC_SET_BUFFER1_SIZE(desc, length);
|
||||
/* Physical address to DMA to/from */
|
||||
desc->des4 = mem_addr & 0xffffffff;
|
||||
desc->des5 = mem_addr >> 32;
|
||||
}
|
||||
|
||||
/* Physical address to DMA to/from */
|
||||
desc->des2 = mem_addr;
|
||||
/* 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_64addr);
|
||||
desc->des0 &= ~(IDMAC_DES0_CH | IDMAC_DES0_DIC);
|
||||
desc->des0 |= IDMAC_DES0_LD;
|
||||
|
||||
} else {
|
||||
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]);
|
||||
|
||||
/*
|
||||
* 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 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 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;
|
||||
|
||||
wmb();
|
||||
}
|
||||
|
||||
|
@ -470,29 +524,71 @@ 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;
|
||||
|
||||
/* Number of descriptors in the ring buffer */
|
||||
host->ring_size = PAGE_SIZE / sizeof(struct idmac_desc);
|
||||
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->des3 = host->sg_dma + (sizeof(struct idmac_desc) * (i + 1));
|
||||
/* 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;
|
||||
|
||||
/* Set the last descriptor as the end-of-ring descriptor */
|
||||
p->des3 = host->sg_dma;
|
||||
p->des0 = IDMAC_DES0_ER;
|
||||
p->des7 = (u64)(host->sg_dma +
|
||||
(sizeof(struct idmac_desc_64addr) *
|
||||
(i + 1))) >> 32;
|
||||
/* Initialize reserved and buffer size fields to "0" */
|
||||
p->des1 = 0;
|
||||
p->des2 = 0;
|
||||
p->des3 = 0;
|
||||
}
|
||||
|
||||
/* Set the last descriptor as the end-of-ring descriptor */
|
||||
p->des6 = host->sg_dma & 0xffffffff;
|
||||
p->des7 = (u64)host->sg_dma >> 32;
|
||||
p->des0 = IDMAC_DES0_ER;
|
||||
|
||||
} else {
|
||||
struct idmac_desc *p;
|
||||
/* Number of descriptors in the ring buffer */
|
||||
host->ring_size = PAGE_SIZE / sizeof(struct idmac_desc);
|
||||
|
||||
/* 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;
|
||||
}
|
||||
|
||||
dw_mci_idmac_reset(host);
|
||||
|
||||
/* 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, (u64)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;
|
||||
}
|
||||
|
||||
|
@ -2066,11 +2162,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
|
||||
|
||||
|
@ -2245,6 +2352,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);
|
||||
|
|
|
@ -55,6 +55,17 @@
|
|||
#define SDMMC_BUFADDR 0x098
|
||||
#define SDMMC_CDTHRCTL 0x100
|
||||
#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
|
||||
|
|
|
@ -54,6 +54,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.
|
||||
|
@ -139,6 +140,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;
|
||||
|
|
Loading…
Reference in New Issue