On 05/18/2016 11:45 AM, Kishon Vijay Abraham I wrote: > omap hsmmc host controller has ADMA2 feature. Enable it here > for better read and write throughput. Add a new dt binding > "ti,use_adma" to enable ADMA2. > > Signed-off-by: Kishon Vijay Abraham I <kishon@xxxxxx> > --- > .../devicetree/bindings/mmc/ti-omap-hsmmc.txt | 1 + > drivers/mmc/host/omap_hsmmc.c | 320 ++++++++++++++++---- > include/linux/platform_data/hsmmc-omap.h | 1 + > 3 files changed, 256 insertions(+), 66 deletions(-) > > diff --git a/Documentation/devicetree/bindings/mmc/ti-omap-hsmmc.txt b/Documentation/devicetree/bindings/mmc/ti-omap-hsmmc.txt > index 74166a0..eb5ceec2 100644 > --- a/Documentation/devicetree/bindings/mmc/ti-omap-hsmmc.txt > +++ b/Documentation/devicetree/bindings/mmc/ti-omap-hsmmc.txt > @@ -28,6 +28,7 @@ specifier is required. > dma-names: List of DMA request names. These strings correspond > 1:1 with the DMA specifiers listed in dmas. The string naming is > to be "rx" and "tx" for RX and TX DMA requests, respectively. > +ti,use_adma: enable adma2 feature > > Examples: > > diff --git a/drivers/mmc/host/omap_hsmmc.c b/drivers/mmc/host/omap_hsmmc.c > index cc916d5..b4a7d18 100644 > --- a/drivers/mmc/host/omap_hsmmc.c > +++ b/drivers/mmc/host/omap_hsmmc.c > @@ -66,6 +66,8 @@ > #define OMAP_HSMMC_ISE 0x0138 > #define OMAP_HSMMC_AC12 0x013C > #define OMAP_HSMMC_CAPA 0x0140 > +#define OMAP_HSMMC_ADMAES 0x0154 > +#define OMAP_HSMMC_ADMASAL 0x0158 > > #define VS18 (1 << 26) > #define VS30 (1 << 25) > @@ -76,6 +78,7 @@ > #define SDVS_MASK 0x00000E00 > #define SDVSCLR 0xFFFFF1FF > #define SDVSDET 0x00000400 > +#define DMA_SELECT (2 << 3) > #define AUTOIDLE 0x1 > #define SDBP (1 << 8) > #define DTO 0xe > @@ -97,6 +100,7 @@ > #define FOUR_BIT (1 << 1) > #define HSPE (1 << 2) > #define IWE (1 << 24) > +#define DMA_MASTER (1 << 20) > #define DDR (1 << 19) > #define CLKEXTFREE (1 << 16) > #define CTPL (1 << 11) > @@ -127,10 +131,11 @@ > #define DCRC_EN (1 << 21) > #define DEB_EN (1 << 22) > #define ACE_EN (1 << 24) > +#define ADMAE_EN (1 << 24) > #define CERR_EN (1 << 28) > #define BADA_EN (1 << 29) > > -#define INT_EN_MASK (BADA_EN | CERR_EN | ACE_EN | DEB_EN | DCRC_EN |\ > +#define INT_EN_MASK (BADA_EN | CERR_EN | ADMAE_EN | ACE_EN | DEB_EN | DCRC_EN |\ > DTO_EN | CIE_EN | CEB_EN | CCRC_EN | CTO_EN | \ > BRR_EN | BWR_EN | TC_EN | CC_EN) > > @@ -168,6 +173,25 @@ > #define OMAP_HSMMC_WRITE(base, reg, val) \ > __raw_writel((val), (base) + OMAP_HSMMC_##reg) > > +struct omap_hsmmc_adma_desc { > + u8 attr; > + u8 reserved; > + u16 len; > + u32 addr; > +} __packed; > + > +#define ADMA_MAX_LEN 65532 > + > +/* Decriptor table defines */ > +#define ADMA_DESC_ATTR_VALID BIT(0) > +#define ADMA_DESC_ATTR_END BIT(1) > +#define ADMA_DESC_ATTR_INT BIT(2) > +#define ADMA_DESC_ATTR_ACT1 BIT(4) > +#define ADMA_DESC_ATTR_ACT2 BIT(5) > + > +#define ADMA_DESC_TRANSFER_DATA ADMA_DESC_ATTR_ACT2 > +#define ADMA_DESC_LINK_DESC (ADMA_DESC_ATTR_ACT1 | ADMA_DESC_ATTR_ACT2) > + > struct omap_hsmmc_next { > unsigned int dma_len; > s32 cookie; > @@ -213,6 +237,9 @@ struct omap_hsmmc_host { > struct omap_hsmmc_next next_data; > struct omap_hsmmc_platform_data *pdata; > > + struct omap_hsmmc_adma_desc *adma_desc_table; > + dma_addr_t adma_desc_table_addr; > + > /* return MMC cover switch state, can be NULL if not supported. > * > * possible return values: > @@ -951,6 +978,19 @@ static struct dma_chan *omap_hsmmc_get_dma_chan(struct omap_hsmmc_host *host, > return data->flags & MMC_DATA_WRITE ? host->tx_chan : host->rx_chan; > } > > +static void omap_hsmmc_adma_cleanup(struct omap_hsmmc_host *host) > +{ > + u32 val; > + > + val = OMAP_HSMMC_READ(host->base, HCTL); > + val &= ~DMA_SELECT; > + OMAP_HSMMC_WRITE(host->base, HCTL, val); > + > + val = OMAP_HSMMC_READ(host->base, CON); > + val &= ~DMA_MASTER; > + OMAP_HSMMC_WRITE(host->base, CON, val); > +} > + > static void omap_hsmmc_request_done(struct omap_hsmmc_host *host, struct mmc_request *mrq) > { > int dma_ch; > @@ -963,8 +1003,11 @@ static void omap_hsmmc_request_done(struct omap_hsmmc_host *host, struct mmc_req > > omap_hsmmc_disable_irq(host); > /* Do not complete the request if DMA is still in progress */ > - if (mrq->data && dma_ch != -1) > + if (host->pdata->controller_flags == OMAP_HSMMC_USE_ADMA) host->pdata->controller_flags & OMAP_HSMMC_USE_ADMA and I would probably name the flag to OMAP_HSMMC_MASTER_DMA_SUPPORTED > + omap_hsmmc_adma_cleanup(host); > + else if (mrq->data && dma_ch != -1) > return; > + > host->mrq = NULL; > mmc_request_done(host->mmc, mrq); > pm_runtime_mark_last_busy(host->dev); > @@ -1052,15 +1095,22 @@ static void omap_hsmmc_dma_cleanup(struct omap_hsmmc_host *host, int errno) > host->dma_ch = -1; > spin_unlock_irqrestore(&host->irq_lock, flags); > > - if (dma_ch != -1) { > - struct dma_chan *chan = omap_hsmmc_get_dma_chan(host, host->data); > - > - dmaengine_terminate_all(chan); > - dma_unmap_sg(chan->device->dev, > - host->data->sg, host->data->sg_len, > + if (!(host->pdata->controller_flags & OMAP_HSMMC_USE_ADMA)) { it might be better to use the same order in the driver for Master and Slave DMA cases: if (host->pdata->controller_flags & OMAP_HSMMC_USE_ADMA) { /* Master DMA case */ } else { /* Slave DMA case */ } It will make the driver easier to read. > + if (dma_ch != -1) { > + struct dma_chan *chan = omap_hsmmc_get_dma_chan(host, > + host->data); > + dmaengine_terminate_all(chan); > + dma_unmap_sg(chan->device->dev, > + host->data->sg, host->data->sg_len, > omap_hsmmc_get_dma_dir(host, host->data)); > > + host->data->host_cookie = 0; > + } > + } else { > + dma_unmap_sg(host->dev, host->data->sg, host->data->sg_len, > + omap_hsmmc_get_dma_dir(host, host->data)); > host->data->host_cookie = 0; > + > } > host->data = NULL; > } > @@ -1191,6 +1241,14 @@ static void omap_hsmmc_do_irq(struct omap_hsmmc_host *host, int status) > } > dev_dbg(mmc_dev(host->mmc), "AC12 err: 0x%x\n", ac12); > } > + > + if (status & ADMAE_EN) { > + u32 val; > + > + val = OMAP_HSMMC_READ(host->base, ADMAES); > + dev_dbg(mmc_dev(host->mmc), "ADMA error status: 0x%x\n", > + val); > + } > } > > OMAP_HSMMC_WRITE(host->base, STAT, status); > @@ -1378,6 +1436,7 @@ static int omap_hsmmc_pre_dma_transfer(struct omap_hsmmc_host *host, > struct dma_chan *chan) > { > int dma_len; > + struct device *dev; > > if (!next && data->host_cookie && > data->host_cookie != host->next_data.cookie) { > @@ -1387,9 +1446,14 @@ static int omap_hsmmc_pre_dma_transfer(struct omap_hsmmc_host *host, > data->host_cookie = 0; > } > > + if (chan) > + dev = chan->device->dev; > + else > + dev = mmc_dev(host->mmc); > + > /* Check if next job is already prepared */ > if (next || data->host_cookie != host->next_data.cookie) { > - dma_len = dma_map_sg(chan->device->dev, data->sg, data->sg_len, > + dma_len = dma_map_sg(dev, data->sg, data->sg_len, > omap_hsmmc_get_dma_dir(host, data)); > > } else { > @@ -1516,6 +1580,7 @@ static void omap_hsmmc_start_dma_transfer(struct omap_hsmmc_host *host) > { > struct mmc_request *req = host->mrq; > struct dma_chan *chan; > + int val; > > if (!req->data) > return; > @@ -1523,10 +1588,66 @@ static void omap_hsmmc_start_dma_transfer(struct omap_hsmmc_host *host) > | (req->data->blocks << 16)); > set_data_timeout(host, req->data->timeout_ns, > req->data->timeout_clks); > - chan = omap_hsmmc_get_dma_chan(host, req->data); > - dma_async_issue_pending(chan); > + > + if (host->pdata->controller_flags & OMAP_HSMMC_USE_ADMA) { > + val = OMAP_HSMMC_READ(host->base, HCTL); > + val |= DMA_SELECT; > + OMAP_HSMMC_WRITE(host->base, HCTL, val); > + > + val = OMAP_HSMMC_READ(host->base, CON); > + val |= DMA_MASTER; > + OMAP_HSMMC_WRITE(host->base, CON, val); > + > + OMAP_HSMMC_WRITE(host->base, ADMASAL, > + (u32)host->adma_desc_table_addr); > + } else { > + chan = omap_hsmmc_get_dma_chan(host, req->data); > + dma_async_issue_pending(chan); > + } > +} > + > +static int omap_hsmmc_write_adma_desc(struct omap_hsmmc_host *host, void *desc, > + dma_addr_t addr, u16 len, u8 attr) > +{ > + struct omap_hsmmc_adma_desc *dma_desc = desc; > + > + dma_desc->len = len; > + dma_desc->addr = (u32)addr; > + dma_desc->reserved = 0; > + dma_desc->attr = attr; > + > + return 0; > } > > +static int omap_hsmmc_setup_adma_transfer(struct omap_hsmmc_host *host, > + struct mmc_request *req) > +{ > + struct mmc_data *data = req->data; > + struct scatterlist *sg; > + int i; > + int len; > + int ret; > + dma_addr_t addr; > + struct omap_hsmmc_adma_desc *dma_desc; > + > + ret = omap_hsmmc_pre_dma_transfer(host, data, NULL, NULL); > + if (ret) > + return ret; > + > + dma_desc = host->adma_desc_table; > + for_each_sg(data->sg, sg, host->dma_len, i) { > + addr = sg_dma_address(sg); > + len = sg_dma_len(sg); > + WARN_ON(len > ADMA_MAX_LEN); > + omap_hsmmc_write_adma_desc(host, dma_desc, addr, len, > + ADMA_DESC_ATTR_VALID | > + ADMA_DESC_TRANSFER_DATA); > + dma_desc++; > + } > + omap_hsmmc_write_adma_desc(host, dma_desc, 0, 0, ADMA_DESC_ATTR_END); > + > + return 0; > +} Would be nice to group the ADMA functions in one section, not scattering it around the driver. > /* > * Configure block length for MMC/SD cards and initiate the transfer. > */ > @@ -1547,10 +1668,18 @@ omap_hsmmc_prepare_data(struct omap_hsmmc_host *host, struct mmc_request *req) > return 0; > } > > - ret = omap_hsmmc_setup_dma_transfer(host, req); > - if (ret != 0) { > - dev_err(mmc_dev(host->mmc), "MMC start dma failure\n"); > - return ret; > + if (host->pdata->controller_flags & OMAP_HSMMC_USE_ADMA) { > + ret = omap_hsmmc_setup_adma_transfer(host, req); > + if (ret != 0) { > + dev_err(mmc_dev(host->mmc), "MMC adma setup failed\n"); > + return ret; > + } > + } else { > + ret = omap_hsmmc_setup_dma_transfer(host, req); > + if (ret != 0) { > + dev_err(mmc_dev(host->mmc), "MMC start dma failure\n"); > + return ret; the error checking can go outside > + } > } > return 0; if (ret != 0) dev_err(mmc_dev(host->mmc), "MMC start dma failure\n"); return ret; if compiler complains about ret not beeing initialized int rer = 0; > } > @@ -1560,11 +1689,18 @@ static void omap_hsmmc_post_req(struct mmc_host *mmc, struct mmc_request *mrq, > { > struct omap_hsmmc_host *host = mmc_priv(mmc); > struct mmc_data *data = mrq->data; > + struct device *dev; > + struct dma_chan *c; > > if (data->host_cookie) { > - struct dma_chan *c = omap_hsmmc_get_dma_chan(host, data); > + if (!(host->pdata->controller_flags & OMAP_HSMMC_USE_ADMA)) { > + c = omap_hsmmc_get_dma_chan(host, mrq->data); > + dev = c->device->dev; > + } else { > + dev = mmc_dev(mmc); > + } > > - dma_unmap_sg(c->device->dev, data->sg, data->sg_len, > + dma_unmap_sg(dev, data->sg, data->sg_len, > omap_hsmmc_get_dma_dir(host, data)); > data->host_cookie = 0; > } > @@ -1574,13 +1710,15 @@ static void omap_hsmmc_pre_req(struct mmc_host *mmc, struct mmc_request *mrq, > bool is_first_req) > { > struct omap_hsmmc_host *host = mmc_priv(mmc); > + struct dma_chan *c = NULL; > > if (mrq->data->host_cookie) { > mrq->data->host_cookie = 0; > return ; > } > > - struct dma_chan *c = omap_hsmmc_get_dma_chan(host, mrq->data); > + if (!(host->pdata->controller_flags & OMAP_HSMMC_USE_ADMA)) > + c = omap_hsmmc_get_dma_chan(host, mrq->data); > > if (omap_hsmmc_pre_dma_transfer(host, mrq->data, > &host->next_data, c)) > @@ -1967,6 +2105,9 @@ static struct omap_hsmmc_platform_data *of_get_hsmmc_pdata(struct device *dev) > if (of_find_property(np, "ti,dual-volt", NULL)) > pdata->controller_flags |= OMAP_HSMMC_SUPPORTS_DUAL_VOLT; > > + if (of_find_property(np, "ti,use_adma", NULL)) > + pdata->controller_flags |= OMAP_HSMMC_USE_ADMA; > + > pdata->gpio_cd = -EINVAL; > pdata->gpio_cod = -EINVAL; > pdata->gpio_wp = -EINVAL; > @@ -1992,6 +2133,84 @@ static inline struct omap_hsmmc_platform_data > } > #endif > > +static int omap_hsmmc_adma_init(struct omap_hsmmc_host *host) > +{ > + struct mmc_host *mmc = host->mmc; > + > + host->adma_desc_table = dma_alloc_coherent(host->dev, mmc->max_segs + 1, > + &host->adma_desc_table_addr, > + GFP_KERNEL); > + if (!host->adma_desc_table) { > + dev_err(host->dev, "failed to allocate adma desc table\n"); > + return -ENOMEM; Fall back to Slave DMA? > + } > + > + return 0; > +} > + > +static void omap_hsmmc_adma_exit(struct omap_hsmmc_host *host) > +{ > + struct mmc_host *mmc = host->mmc; > + > + dma_free_coherent(host->dev, mmc->max_segs + 1, > + host->adma_desc_table, host->adma_desc_table_addr); > +} > + > +static int omap_hsmmc_dma_init(struct omap_hsmmc_host *host) > +{ > + dma_cap_mask_t mask; > + unsigned int tx_req, rx_req; > + struct resource *res; > + struct platform_device *pdev = to_platform_device(host->dev); > + > + dma_cap_zero(mask); > + dma_cap_set(DMA_SLAVE, mask); > + > + if (!pdev->dev.of_node) { > + res = platform_get_resource_byname(pdev, IORESOURCE_DMA, "tx"); > + if (!res) { > + dev_err(mmc_dev(host->mmc), "cannot get DMA TX channel\n"); > + return -ENXIO; > + } > + tx_req = res->start; > + > + res = platform_get_resource_byname(pdev, IORESOURCE_DMA, "rx"); > + if (!res) { > + dev_err(mmc_dev(host->mmc), "cannot get DMA RX channel\n"); > + return -ENXIO; > + } > + rx_req = res->start; > + } > + > + host->rx_chan = > + dma_request_slave_channel_compat(mask, omap_dma_filter_fn, > + &rx_req, &pdev->dev, "rx"); > + > + if (!host->rx_chan) { > + dev_err(mmc_dev(host->mmc), "unable to obtain RX DMA engine channel\n"); > + return -ENXIO; > + } > + > + host->tx_chan = > + dma_request_slave_channel_compat(mask, omap_dma_filter_fn, > + &tx_req, &pdev->dev, "tx"); > + > + if (!host->tx_chan) { > + dev_err(mmc_dev(host->mmc), "unable to obtain TX DMA engine channel\n"); > + return -ENXIO; > + } > + > + return 0; > +} > + > +static void omap_hsmmc_dma_exit(struct omap_hsmmc_host *host) > +{ > + if (host->tx_chan) > + dma_release_channel(host->tx_chan); > + if (host->rx_chan) > + dma_release_channel(host->rx_chan); > +} > + > static int omap_hsmmc_probe(struct platform_device *pdev) > { > struct omap_hsmmc_platform_data *pdata = pdev->dev.platform_data; > @@ -2000,8 +2219,6 @@ static int omap_hsmmc_probe(struct platform_device *pdev) > struct resource *res; > int ret, irq; > const struct of_device_id *match; > - dma_cap_mask_t mask; > - unsigned tx_req, rx_req; > const struct omap_mmc_of_data *data; > void __iomem *base; > > @@ -2114,7 +2331,10 @@ static int omap_hsmmc_probe(struct platform_device *pdev) > mmc->max_blk_size = 512; /* Block Length at max can be 1024 */ > mmc->max_blk_count = 0xFFFF; /* No. of Blocks is 16 bits */ > mmc->max_req_size = mmc->max_blk_size * mmc->max_blk_count; > - mmc->max_seg_size = mmc->max_req_size; > + if (host->pdata->controller_flags & OMAP_HSMMC_USE_ADMA) > + mmc->max_seg_size = ADMA_MAX_LEN; > + else > + mmc->max_seg_size = mmc->max_req_size; > > mmc->caps |= MMC_CAP_MMC_HIGHSPEED | MMC_CAP_SD_HIGHSPEED | > MMC_CAP_WAIT_WHILE_BUSY | MMC_CAP_ERASE; > @@ -2130,46 +2350,12 @@ static int omap_hsmmc_probe(struct platform_device *pdev) > > omap_hsmmc_conf_bus_power(host); > > - if (!pdev->dev.of_node) { > - res = platform_get_resource_byname(pdev, IORESOURCE_DMA, "tx"); > - if (!res) { > - dev_err(mmc_dev(host->mmc), "cannot get DMA TX channel\n"); > - ret = -ENXIO; > - goto err_irq; > - } > - tx_req = res->start; > - > - res = platform_get_resource_byname(pdev, IORESOURCE_DMA, "rx"); > - if (!res) { > - dev_err(mmc_dev(host->mmc), "cannot get DMA RX channel\n"); > - ret = -ENXIO; > - goto err_irq; > - } > - rx_req = res->start; > - } > - > - dma_cap_zero(mask); > - dma_cap_set(DMA_SLAVE, mask); > - > - host->rx_chan = > - dma_request_slave_channel_compat(mask, omap_dma_filter_fn, > - &rx_req, &pdev->dev, "rx"); > - > - if (!host->rx_chan) { > - dev_err(mmc_dev(host->mmc), "unable to obtain RX DMA engine channel\n"); > - ret = -ENXIO; > - goto err_irq; > - } > - > - host->tx_chan = > - dma_request_slave_channel_compat(mask, omap_dma_filter_fn, > - &tx_req, &pdev->dev, "tx"); > - > - if (!host->tx_chan) { > - dev_err(mmc_dev(host->mmc), "unable to obtain TX DMA engine channel\n"); > - ret = -ENXIO; > + if (host->pdata->controller_flags & OMAP_HSMMC_USE_ADMA) > + ret = omap_hsmmc_adma_init(host); > + else > + ret = omap_hsmmc_dma_init(host); > + if (ret) > goto err_irq; > - } > > /* Request IRQ for MMC operations */ > ret = devm_request_irq(&pdev->dev, host->irq, omap_hsmmc_irq, 0, > @@ -2225,11 +2411,11 @@ err_slot_name: > mmc_remove_host(mmc); > err_irq: > device_init_wakeup(&pdev->dev, false); > - if (host->tx_chan) > - dma_release_channel(host->tx_chan); > - if (host->rx_chan) > - dma_release_channel(host->rx_chan); > pm_runtime_dont_use_autosuspend(host->dev); > + if (host->pdata->controller_flags & OMAP_HSMMC_USE_ADMA) > + omap_hsmmc_adma_exit(host); > + else > + omap_hsmmc_dma_exit(host); > pm_runtime_put_sync(host->dev); > pm_runtime_disable(host->dev); > if (host->dbclk) > @@ -2248,8 +2434,10 @@ static int omap_hsmmc_remove(struct platform_device *pdev) > pm_runtime_get_sync(host->dev); > mmc_remove_host(host->mmc); > > - dma_release_channel(host->tx_chan); > - dma_release_channel(host->rx_chan); > + if (host->pdata->controller_flags & OMAP_HSMMC_USE_ADMA) > + omap_hsmmc_adma_exit(host); > + else > + omap_hsmmc_dma_exit(host); > > pm_runtime_dont_use_autosuspend(host->dev); > pm_runtime_put_sync(host->dev); > diff --git a/include/linux/platform_data/hsmmc-omap.h b/include/linux/platform_data/hsmmc-omap.h > index 8e981be..e26013d 100644 > --- a/include/linux/platform_data/hsmmc-omap.h > +++ b/include/linux/platform_data/hsmmc-omap.h > @@ -27,6 +27,7 @@ > #define OMAP_HSMMC_SUPPORTS_DUAL_VOLT BIT(0) > #define OMAP_HSMMC_BROKEN_MULTIBLOCK_READ BIT(1) > #define OMAP_HSMMC_SWAKEUP_MISSING BIT(2) > +#define OMAP_HSMMC_USE_ADMA BIT(3) > > struct omap_hsmmc_dev_attr { > u8 flags; > -- Péter -- To unsubscribe from this list: send the line "unsubscribe linux-mmc" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html