diff options
author | Srinivas Kandagatla <srinivas.kandagatla@linaro.org> | 2014-06-18 17:49:10 +0100 |
---|---|---|
committer | Srinivas Kandagatla <srinivas.kandagatla@linaro.org> | 2014-06-18 17:49:10 +0100 |
commit | 2d9d90ddf52cbc555767b0ccf7ba3840c4e42bba (patch) | |
tree | 7752301e662bfd7f27990664b8e7ad0844e59fe7 | |
parent | f7bfc0df5b1f3ea0b7709a38e8fdb0bcbe38534d (diff) | |
parent | 4f3681f8a0fd9773e07648e05a0d3470389a6009 (diff) |
Merge branch 'tracking-qcomlt-sdcc' into integration-linux-qcomlt
* tracking-qcomlt-sdcc:
mmc: mmci: Add qcom dml support to the driver.
mmc: mmci: Add Qualcomm Id to amba id table
mmc: mmci: Add Qcom specific rx_fifocnt logic.
mmc: mmci: add explicit clk control
mmc: mmci: add f_max to variant structure
mmc: mmci: Add support to data commands via variant structure.
mmc: mmci: add edge support to data and command out in variant data.
mmc: mmci: add 8bit bus support in variant data
mmc: mmci: add ddrmode mask to variant data
mmc: mmci: Add Qcom datactrl register variant
mmc: mmci: Add enough delay between writes to CMD register.
mmc: mmci: Add Qualcomm specific register defines.
mmc: mmci: use NSEC_PER_SEC macro
-rw-r--r-- | drivers/mmc/host/Kconfig | 11 | ||||
-rw-r--r-- | drivers/mmc/host/Makefile | 1 | ||||
-rw-r--r-- | drivers/mmc/host/mmci.c | 146 | ||||
-rw-r--r-- | drivers/mmc/host/mmci.h | 20 | ||||
-rw-r--r-- | drivers/mmc/host/qcom_dml.c | 170 | ||||
-rw-r--r-- | drivers/mmc/host/qcom_dml.h | 17 |
6 files changed, 352 insertions, 13 deletions
diff --git a/drivers/mmc/host/Kconfig b/drivers/mmc/host/Kconfig index a5652548230a..2f210a5a1d24 100644 --- a/drivers/mmc/host/Kconfig +++ b/drivers/mmc/host/Kconfig @@ -14,6 +14,17 @@ config MMC_ARMMMCI If unsure, say N. +config MMC_QCOM_DML + tristate "Qualcomm Data Mover for SD Card Controller" + depends on MMC_ARMMMCI + default y + help + This selects the Qualcomm Data Mover lite/local on SD Card controller. + This option will enable the dma to work correctly, if you are using + Qcom SOCs and MMC, you would probably need this option to get DMA working. + + if unsure, say N. + config MMC_PXA tristate "Intel PXA25x/26x/27x Multimedia Card Interface support" depends on ARCH_PXA diff --git a/drivers/mmc/host/Makefile b/drivers/mmc/host/Makefile index 7f81ddf1dd2c..e6a14ac7075e 100644 --- a/drivers/mmc/host/Makefile +++ b/drivers/mmc/host/Makefile @@ -3,6 +3,7 @@ # obj-$(CONFIG_MMC_ARMMMCI) += mmci.o +obj-$(CONFIG_MMC_QCOM_DML) += qcom_dml.o obj-$(CONFIG_MMC_PXA) += pxamci.o obj-$(CONFIG_MMC_MXC) += mxcmmc.o obj-$(CONFIG_MMC_MXS) += mxs-mmc.o diff --git a/drivers/mmc/host/mmci.c b/drivers/mmc/host/mmci.c index 7ad463e9741c..ca9bb6de4379 100644 --- a/drivers/mmc/host/mmci.c +++ b/drivers/mmc/host/mmci.c @@ -43,6 +43,7 @@ #include <asm/sizes.h> #include "mmci.h" +#include "qcom_dml.h" #define DRIVER_NAME "mmci-pl18x" @@ -52,34 +53,53 @@ static unsigned int fmax = 515633; * struct variant_data - MMCI variant-specific quirks * @clkreg: default value for MCICLOCK register * @clkreg_enable: enable value for MMCICLOCK register + * @clkreg_8bit_bus_enable: enable value for 8 bit bus + * @clkreg_neg_edge_enable: enable value for inverted data/cmd output * @datalength_bits: number of bits in the MMCIDATALENGTH register * @fifosize: number of bytes that can be written when MMCI_TXFIFOEMPTY * is asserted (likewise for RX) * @fifohalfsize: number of bytes that can be written when MCI_TXFIFOHALFEMPTY * is asserted (likewise for RX) + * @data_cmd_enable: enable value for data commands. * @sdio: variant supports SDIO * @st_clkdiv: true if using a ST-specific clock divider algorithm + * @datactrl_mask_ddrmode: ddr mode mask in datactrl register. * @blksz_datactrl16: true if Block size is at b16..b30 position in datactrl register + * @blksz_datactrl4: true if Block size is at b4..b16 position in datactrl + * register * @pwrreg_powerup: power up value for MMCIPOWER register + * @f_max: maximum clk frequency supported by the controller. * @signal_direction: input/out direction of bus signals can be indicated * @pwrreg_clkgate: MMCIPOWER register must be used to gate the clock * @busy_detect: true if busy detection on dat0 is supported * @pwrreg_nopower: bits in MMCIPOWER don't controls ext. power supply + * @explicit_mclk_control: enable explicit mclk control in driver. + * @qcom_fifo: enables qcom specific fifo pio read logic. + * @qcom_dml: enables qcom specific dml glue for dma transfers. */ struct variant_data { unsigned int clkreg; unsigned int clkreg_enable; + unsigned int clkreg_8bit_bus_enable; + unsigned int clkreg_neg_edge_enable; unsigned int datalength_bits; unsigned int fifosize; unsigned int fifohalfsize; + unsigned int data_cmd_enable; + unsigned int datactrl_mask_ddrmode; bool sdio; bool st_clkdiv; bool blksz_datactrl16; + bool blksz_datactrl4; u32 pwrreg_powerup; + u32 f_max; bool signal_direction; bool pwrreg_clkgate; bool busy_detect; bool pwrreg_nopower; + bool explicit_mclk_control; + bool qcom_fifo; + bool qcom_dml; }; static struct variant_data variant_arm = { @@ -87,6 +107,7 @@ static struct variant_data variant_arm = { .fifohalfsize = 8 * 4, .datalength_bits = 16, .pwrreg_powerup = MCI_PWR_UP, + .f_max = 100000000, }; static struct variant_data variant_arm_extended_fifo = { @@ -94,6 +115,7 @@ static struct variant_data variant_arm_extended_fifo = { .fifohalfsize = 64 * 4, .datalength_bits = 16, .pwrreg_powerup = MCI_PWR_UP, + .f_max = 100000000, }; static struct variant_data variant_arm_extended_fifo_hwfc = { @@ -102,15 +124,18 @@ static struct variant_data variant_arm_extended_fifo_hwfc = { .clkreg_enable = MCI_ARM_HWFCEN, .datalength_bits = 16, .pwrreg_powerup = MCI_PWR_UP, + .f_max = 100000000, }; static struct variant_data variant_u300 = { .fifosize = 16 * 4, .fifohalfsize = 8 * 4, .clkreg_enable = MCI_ST_U300_HWFCEN, + .clkreg_8bit_bus_enable = MCI_ST_8BIT_BUS, .datalength_bits = 16, .sdio = true, .pwrreg_powerup = MCI_PWR_ON, + .f_max = 100000000, .signal_direction = true, .pwrreg_clkgate = true, .pwrreg_nopower = true, @@ -124,6 +149,7 @@ static struct variant_data variant_nomadik = { .sdio = true, .st_clkdiv = true, .pwrreg_powerup = MCI_PWR_ON, + .f_max = 100000000, .signal_direction = true, .pwrreg_clkgate = true, .pwrreg_nopower = true, @@ -134,10 +160,13 @@ static struct variant_data variant_ux500 = { .fifohalfsize = 8 * 4, .clkreg = MCI_CLK_ENABLE, .clkreg_enable = MCI_ST_UX500_HWFCEN, + .clkreg_8bit_bus_enable = MCI_ST_8BIT_BUS, + .clkreg_neg_edge_enable = MCI_ST_UX500_NEG_EDGE, .datalength_bits = 24, .sdio = true, .st_clkdiv = true, .pwrreg_powerup = MCI_PWR_ON, + .f_max = 100000000, .signal_direction = true, .pwrreg_clkgate = true, .busy_detect = true, @@ -149,17 +178,39 @@ static struct variant_data variant_ux500v2 = { .fifohalfsize = 8 * 4, .clkreg = MCI_CLK_ENABLE, .clkreg_enable = MCI_ST_UX500_HWFCEN, + .clkreg_8bit_bus_enable = MCI_ST_8BIT_BUS, + .clkreg_neg_edge_enable = MCI_ST_UX500_NEG_EDGE, + .datactrl_mask_ddrmode = MCI_ST_DPSM_DDRMODE, .datalength_bits = 24, .sdio = true, .st_clkdiv = true, .blksz_datactrl16 = true, .pwrreg_powerup = MCI_PWR_ON, + .f_max = 100000000, .signal_direction = true, .pwrreg_clkgate = true, .busy_detect = true, .pwrreg_nopower = true, }; +static struct variant_data variant_qcom = { + .fifosize = 16 * 4, + .fifohalfsize = 8 * 4, + .clkreg = MCI_CLK_ENABLE, + .clkreg_enable = MCI_QCOM_CLK_FLOWENA | + MCI_QCOM_CLK_SELECT_IN_FBCLK, + .clkreg_8bit_bus_enable = MCI_QCOM_CLK_WIDEBUS_8, + .datactrl_mask_ddrmode = MCI_QCOM_CLK_SELECT_IN_DDR_MODE, + .data_cmd_enable = MCI_QCOM_CSPM_DATCMD, + .blksz_datactrl4 = true, + .datalength_bits = 24, + .pwrreg_powerup = MCI_PWR_UP, + .f_max = 208000000, + .explicit_mclk_control = true, + .qcom_fifo = true, + .qcom_dml = true, +}; + static int mmci_card_busy(struct mmc_host *mmc) { struct mmci_host *host = mmc_priv(mmc); @@ -260,7 +311,9 @@ static void mmci_set_clkreg(struct mmci_host *host, unsigned int desired) host->cclk = 0; if (desired) { - if (desired >= host->mclk) { + if (variant->explicit_mclk_control) { + host->cclk = host->mclk; + } else if (desired >= host->mclk) { clk = MCI_CLK_BYPASS; if (variant->st_clkdiv) clk |= MCI_ST_UX500_NEG_EDGE; @@ -299,11 +352,11 @@ static void mmci_set_clkreg(struct mmci_host *host, unsigned int desired) if (host->mmc->ios.bus_width == MMC_BUS_WIDTH_4) clk |= MCI_4BIT_BUS; if (host->mmc->ios.bus_width == MMC_BUS_WIDTH_8) - clk |= MCI_ST_8BIT_BUS; + clk |= variant->clkreg_8bit_bus_enable; if (host->mmc->ios.timing == MMC_TIMING_UHS_DDR50 || host->mmc->ios.timing == MMC_TIMING_MMC_DDR52) - clk |= MCI_ST_UX500_NEG_EDGE; + clk |= variant->clkreg_neg_edge_enable; mmci_write_clkreg(host, clk); } @@ -520,6 +573,7 @@ static int __mmci_dma_prep_data(struct mmci_host *host, struct mmc_data *data, struct dma_async_tx_descriptor *desc; enum dma_data_direction buffer_dirn; int nr_sg; + u32 flags = DMA_CTRL_ACK; if (data->flags & MMC_DATA_READ) { conf.direction = DMA_DEV_TO_MEM; @@ -544,9 +598,12 @@ static int __mmci_dma_prep_data(struct mmci_host *host, struct mmc_data *data, if (nr_sg == 0) return -EINVAL; + if (host->variant->qcom_dml) + flags |= DMA_PREP_INTERRUPT; + dmaengine_slave_config(chan, &conf); desc = dmaengine_prep_slave_sg(chan, data->sg, nr_sg, - conf.direction, DMA_CTRL_ACK); + conf.direction, flags); if (!desc) goto unmap_exit; @@ -595,6 +652,9 @@ static int mmci_dma_start_data(struct mmci_host *host, unsigned int datactrl) dmaengine_submit(host->dma_desc_current); dma_async_issue_pending(host->dma_current); + if (host->variant->qcom_dml) + dml_start_xfer(host, data); + datactrl |= MCI_DPSM_DMAENABLE; /* Trigger the DMA transfer */ @@ -719,7 +779,7 @@ static void mmci_start_data(struct mmci_host *host, struct mmc_data *data) data->bytes_xfered = 0; clks = (unsigned long long)data->timeout_ns * host->cclk; - do_div(clks, 1000000000UL); + do_div(clks, NSEC_PER_SEC); timeout = data->timeout_clks + (unsigned int)clks; @@ -732,6 +792,8 @@ static void mmci_start_data(struct mmci_host *host, struct mmc_data *data) if (variant->blksz_datactrl16) datactrl = MCI_DPSM_ENABLE | (data->blksz << 16); + else if (variant->blksz_datactrl4) + datactrl = MCI_DPSM_ENABLE | (data->blksz << 4); else datactrl = MCI_DPSM_ENABLE | blksz_bits << 4; @@ -767,7 +829,7 @@ static void mmci_start_data(struct mmci_host *host, struct mmc_data *data) if (host->mmc->ios.timing == MMC_TIMING_UHS_DDR50 || host->mmc->ios.timing == MMC_TIMING_MMC_DDR52) - datactrl |= MCI_ST_DPSM_DDRMODE; + datactrl |= variant->datactrl_mask_ddrmode; /* * Attempt to use DMA operation mode, if this @@ -812,7 +874,7 @@ mmci_start_command(struct mmci_host *host, struct mmc_command *cmd, u32 c) if (readl(base + MMCICOMMAND) & MCI_CPSM_ENABLE) { writel(0, base + MMCICOMMAND); - udelay(1); + mmci_reg_delay(host); } c |= cmd->opcode | MCI_CPSM_ENABLE; @@ -824,6 +886,9 @@ mmci_start_command(struct mmci_host *host, struct mmc_command *cmd, u32 c) if (/*interrupt*/0) c |= MCI_CPSM_INTERRUPT; + if (mmc_cmd_type(cmd) == MMC_CMD_ADTC) + c |= host->variant->data_cmd_enable; + host->cmd = cmd; writel(cmd->arg, base + MMCIARGUMENT); @@ -957,15 +1022,34 @@ mmci_cmd_irq(struct mmci_host *host, struct mmc_command *cmd, } } +static int mmci_get_rx_fifocnt(struct mmci_host *host, u32 status, int remain) +{ + return remain - (readl(host->base + MMCIFIFOCNT) << 2); +} + +static int mmci_qcom_get_rx_fifocnt(struct mmci_host *host, u32 status, int r) +{ + /* + * on qcom SDCC4 only 8 words are used in each burst so only 8 addresses + * from the fifo range should be used + */ + if (status & MCI_RXFIFOHALFFULL) + return host->variant->fifohalfsize; + else if (status & MCI_RXDATAAVLBL) + return 4; + + return 0; +} + static int mmci_pio_read(struct mmci_host *host, char *buffer, unsigned int remain) { void __iomem *base = host->base; char *ptr = buffer; - u32 status; + u32 status = readl(host->base + MMCISTATUS); int host_remain = host->size; do { - int count = host_remain - (readl(base + MMCIFIFOCNT) << 2); + int count = host->get_rx_fifocnt(host, status, host_remain); if (count > remain) count = remain; @@ -1296,6 +1380,18 @@ static void mmci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios) if (!ios->clock && variant->pwrreg_clkgate) pwr &= ~MCI_PWR_ON; + if (host->variant->explicit_mclk_control && + ios->clock != host->clock_cache) { + int rc = clk_set_rate(host->clk, ios->clock); + if (rc < 0) { + dev_err(mmc_dev(host->mmc), + "Error setting clock rate (%d)\n", rc); + } else { + host->mclk = clk_get_rate(host->clk); + host->clock_cache = ios->clock; + } + } + spin_lock_irqsave(&host->lock, flags); mmci_set_clkreg(host, ios->clock); @@ -1443,6 +1539,11 @@ static int mmci_probe(struct amba_device *dev, if (ret) goto host_free; + if (variant->qcom_fifo) + host->get_rx_fifocnt = mmci_qcom_get_rx_fifocnt; + else + host->get_rx_fifocnt = mmci_get_rx_fifocnt; + host->plat = plat; host->variant = variant; host->mclk = clk_get_rate(host->clk); @@ -1451,8 +1552,8 @@ static int mmci_probe(struct amba_device *dev, * so we try to adjust the clock down to this, * (if possible). */ - if (host->mclk > 100000000) { - ret = clk_set_rate(host->clk, 100000000); + if (host->mclk > variant->f_max) { + ret = clk_set_rate(host->clk, variant->f_max); if (ret < 0) goto clk_disable; host->mclk = clk_get_rate(host->clk); @@ -1471,9 +1572,12 @@ static int mmci_probe(struct amba_device *dev, * The ARM and ST versions of the block have slightly different * clock divider equations which means that the minimum divider * differs too. + * on Qualcomm like controllers get the nearest minimum clock to 100Khz */ if (variant->st_clkdiv) mmc->f_min = DIV_ROUND_UP(host->mclk, 257); + else if (variant->explicit_mclk_control) + mmc->f_min = clk_round_rate(host->clk, 100000); else mmc->f_min = DIV_ROUND_UP(host->mclk, 512); /* @@ -1483,9 +1587,14 @@ static int mmci_probe(struct amba_device *dev, * the block, of course. */ if (mmc->f_max) - mmc->f_max = min(host->mclk, mmc->f_max); + mmc->f_max = variant->explicit_mclk_control ? + min(variant->f_max, mmc->f_max) : + min(host->mclk, mmc->f_max); else - mmc->f_max = min(host->mclk, fmax); + mmc->f_max = variant->explicit_mclk_control ? + fmax : min(host->mclk, fmax); + + dev_dbg(mmc_dev(mmc), "clocking block at %u Hz\n", mmc->f_max); /* Get regulators and the supported OCR mask */ @@ -1589,6 +1698,11 @@ static int mmci_probe(struct amba_device *dev, mmci_dma_setup(host); + if (variant->qcom_dml && host->dma_rx_channel && host->dma_tx_channel) { + if (dml_hw_init(host, np)) + variant->qcom_dml = false; + } + pm_runtime_set_autosuspend_delay(&dev->dev, 50); pm_runtime_use_autosuspend(&dev->dev); pm_runtime_put(&dev->dev); @@ -1752,6 +1866,12 @@ static struct amba_id mmci_ids[] = { .mask = 0xf0ffffff, .data = &variant_ux500v2, }, + /* Qualcomm variants */ + { + .id = 0x00051180, + .mask = 0x000fffff, + .data = &variant_qcom, + }, { 0, 0 }, }; diff --git a/drivers/mmc/host/mmci.h b/drivers/mmc/host/mmci.h index 347d942d740b..a1f5e4f49e2a 100644 --- a/drivers/mmc/host/mmci.h +++ b/drivers/mmc/host/mmci.h @@ -41,6 +41,15 @@ /* Modified PL180 on Versatile Express platform */ #define MCI_ARM_HWFCEN (1 << 12) +/* Modified on Qualcomm Integrations */ +#define MCI_QCOM_CLK_WIDEBUS_8 (BIT(10) | BIT(11)) +#define MCI_QCOM_CLK_FLOWENA BIT(12) +#define MCI_QCOM_CLK_INVERTOUT BIT(13) + +/* select in latch data and command in */ +#define MCI_QCOM_CLK_SELECT_IN_FBCLK BIT(15) +#define MCI_QCOM_CLK_SELECT_IN_DDR_MODE (BIT(14) | BIT(15)) + #define MMCIARGUMENT 0x008 #define MMCICOMMAND 0x00c #define MCI_CPSM_RESPONSE (1 << 6) @@ -54,6 +63,14 @@ #define MCI_ST_NIEN (1 << 13) #define MCI_ST_CE_ATACMD (1 << 14) +/* Modified on Qualcomm Integrations */ +#define MCI_QCOM_CSPM_DATCMD BIT(12) +#define MCI_QCOM_CSPM_MCIABORT BIT(13) +#define MCI_QCOM_CSPM_CCSENABLE BIT(14) +#define MCI_QCOM_CSPM_CCSDISABLE BIT(15) +#define MCI_QCOM_CSPM_AUTO_CMD19 BIT(16) +#define MCI_QCOM_CSPM_AUTO_CMD21 BIT(21) + #define MMCIRESPCMD 0x010 #define MMCIRESPONSE0 0x014 #define MMCIRESPONSE1 0x018 @@ -191,6 +208,8 @@ struct mmci_host { spinlock_t lock; unsigned int mclk; + /* cached value of requested clk in set_ios */ + unsigned int clock_cache; unsigned int cclk; u32 pwr_reg; u32 pwr_reg_add; @@ -210,6 +229,7 @@ struct mmci_host { /* pio stuff */ struct sg_mapping_iter sg_miter; unsigned int size; + int (*get_rx_fifocnt)(struct mmci_host *h, u32 status, int remain); #ifdef CONFIG_DMA_ENGINE /* DMA stuff */ diff --git a/drivers/mmc/host/qcom_dml.c b/drivers/mmc/host/qcom_dml.c new file mode 100644 index 000000000000..5d61e4ce13ea --- /dev/null +++ b/drivers/mmc/host/qcom_dml.c @@ -0,0 +1,170 @@ +#include <linux/of.h> +#include <linux/of_dma.h> +#include <linux/mmc/host.h> +#include <linux/mmc/card.h> +#include "mmci.h" + +/* DML config register defination */ +#define DML_CONFIG 0x00 +#define PRODUCER_CRCI_DIS 0x00 +#define PRODUCER_CRCI_X_SEL 0x01 +#define PRODUCER_CRCI_Y_SEL 0x02 +#define PRODUCER_CRCI_MSK 0x3 +#define CONSUMER_CRCI_DIS (0x00 << 2) +#define CONSUMER_CRCI_X_SEL (0x01 << 2) +#define CONSUMER_CRCI_Y_SEL (0x02 << 2) +#define CONSUMER_CRCI_MSK (0x3 << 2) +#define PRODUCER_TRANS_END_EN (1 << 4) +#define BYPASS (1 << 16) +#define DIRECT_MODE (1 << 17) +#define INFINITE_CONS_TRANS (1 << 18) + +#define DML_SW_RESET 0x08 +#define DML_PRODUCER_START 0x0C +#define DML_CONSUMER_START 0x10 +#define DML_PRODUCER_PIPE_LOGICAL_SIZE 0x14 +#define DML_CONSUMER_PIPE_LOGICAL_SIZE 0x18 +#define DML_PIPE_ID 0x1C +#define DML_PRODUCER_BAM_BLOCK_SIZE 0x24 +#define DML_PRODUCER_BAM_TRANS_SIZE 0x28 +#define PRODUCER_PIPE_ID_SHFT 0 +#define PRODUCER_PIPE_ID_MSK 0x1f +#define CONSUMER_PIPE_ID_SHFT 16 +#define CONSUMER_PIPE_ID_MSK (0x1f << 16) +/* other definations */ +#define PRODUCER_PIPE_LOGICAL_SIZE 4096 +#define CONSUMER_PIPE_LOGICAL_SIZE 4096 + +#define DML_OFFSET 0x800 + +void dml_start_xfer(struct mmci_host *host, struct mmc_data *data) +{ + u32 config; + void __iomem *dml_base; + dml_base = host->base + DML_OFFSET; + + if (data->flags & MMC_DATA_READ) { + /* Read operation: configure DML for producer operation */ + /* Set producer CRCI-x and disable consumer CRCI */ + config = readl(dml_base + DML_CONFIG); + config = (config & ~PRODUCER_CRCI_MSK) | PRODUCER_CRCI_X_SEL; + config = (config & ~CONSUMER_CRCI_MSK) | CONSUMER_CRCI_DIS; + writel(config, (dml_base + DML_CONFIG)); + + /* Set the Producer BAM block size */ + writel(data->blksz, (dml_base + + DML_PRODUCER_BAM_BLOCK_SIZE)); + + /* Set Producer BAM Transaction size */ + writel(data->blocks * data->blksz, + (dml_base + DML_PRODUCER_BAM_TRANS_SIZE)); + /* Set Producer Transaction End bit */ + writel((readl_relaxed(dml_base + DML_CONFIG) + | PRODUCER_TRANS_END_EN), + (dml_base + DML_CONFIG)); + /* Trigger producer */ + writel(1, (dml_base + DML_PRODUCER_START)); + } else { + /* Write operation: configure DML for consumer operation */ + /* Set consumer CRCI-x and disable producer CRCI*/ + config = readl(dml_base + DML_CONFIG); + config = (config & ~CONSUMER_CRCI_MSK) | CONSUMER_CRCI_X_SEL; + config = (config & ~PRODUCER_CRCI_MSK) | PRODUCER_CRCI_DIS; + + config = 0x4; + writel(config, (dml_base + DML_CONFIG)); + /* Clear Producer Transaction End bit */ + writel((readl_relaxed(dml_base + DML_CONFIG) + & ~PRODUCER_TRANS_END_EN), + (dml_base + DML_CONFIG)); + /* Trigger consumer */ + writel(1, (dml_base + DML_CONSUMER_START)); + } +} + +static int of_get_dml_pipe_index(struct device_node *np, const char *name) +{ + int count, i; + const char *s; + struct of_phandle_args dma_spec; + + if (!np || !name) + return -ENODEV; + + count = of_property_count_strings(np, "dma-names"); + if (count < 0) + return -ENODEV; + + for (i = 0; i < count; i++) { + + if (of_property_read_string_index(np, "dma-names", i, &s)) + continue; + + if (strcmp(name, s)) + continue; + + if (of_parse_phandle_with_args(np, "dmas", "#dma-cells", i, + &dma_spec)) + continue; + + if (dma_spec.args_count) + return dma_spec.args[0]; + } + + return -ENODEV; +} + +/* Initialize the dml hardware connectd to SD Card controller */ +int dml_hw_init(struct mmci_host *host, struct device_node *np) +{ + u32 config = 0; + void __iomem *dml_base; + u32 consumer_id = 0, producer_id = 0; + + consumer_id = of_get_dml_pipe_index(np, "tx"); + producer_id = of_get_dml_pipe_index(np, "rx"); + + if (IS_ERR_VALUE(producer_id) || IS_ERR_VALUE(consumer_id)) + return -ENODEV; + + dml_base = host->base + DML_OFFSET; + + /* Reset the DML block */ + writel(1, (dml_base + DML_SW_RESET)); + + /* Disable the producer and consumer CRCI */ + config = (PRODUCER_CRCI_DIS | CONSUMER_CRCI_DIS); + /* + * Disable the bypass mode. Bypass mode will only be used + * if data transfer is to happen in PIO mode and don't + * want the BAM interface to connect with SDCC-DML. + */ + config &= ~BYPASS; + /* + * Disable direct mode as we don't DML to MASTER the AHB bus. + * BAM connected with DML should MASTER the AHB bus. + */ + config &= ~DIRECT_MODE; + /* + * Disable infinite mode transfer as we won't be doing any + * infinite size data transfers. All data transfer will be + * of finite data size. + */ + config &= ~INFINITE_CONS_TRANS; + writel(config, (dml_base + DML_CONFIG)); + + /* + * Initialize the logical BAM pipe size for producer + * and consumer. + */ + writel(PRODUCER_PIPE_LOGICAL_SIZE, + (dml_base + DML_PRODUCER_PIPE_LOGICAL_SIZE)); + writel(CONSUMER_PIPE_LOGICAL_SIZE, + (dml_base + DML_CONSUMER_PIPE_LOGICAL_SIZE)); + + /* Initialize Producer/consumer pipe id */ + writel(producer_id | (consumer_id << CONSUMER_PIPE_ID_SHFT), + (dml_base + DML_PIPE_ID)); + + return 0; +} diff --git a/drivers/mmc/host/qcom_dml.h b/drivers/mmc/host/qcom_dml.h new file mode 100644 index 000000000000..d2c5aa45b20a --- /dev/null +++ b/drivers/mmc/host/qcom_dml.h @@ -0,0 +1,17 @@ +#ifndef __MMC_QCOM_DML_H__ +#define __MMC_QCOM_DML_H__ + +#ifdef CONFIG_MMC_QCOM_DML +int dml_hw_init(struct mmci_host *host, struct device_node *np); +void dml_start_xfer(struct mmci_host *host, struct mmc_data *data); +#else +static inline int dml_hw_init(struct mmci_host *host, struct device_node *np) +{ + return -ENOSYS; +} +static inline void dml_start_xfer(struct mmci_host *host, struct mmc_data *data) +{ +} +#endif /* CONFIG_MMC_QCOM_DML */ + +#endif /* __MMC_QCOM_DML_H__ */ |