/* * linux/drivers/mmc/host/msm_sdcc_dml.c - Qualcomm MSM SDCC DML Driver * * Copyright (c) 2011, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and * only version 2 as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * */ #include #include #include #include "msm_sdcc_dml.h" /* * DML registers definations */ /* DML config register defination */ #define DML_CONFIG 0x0000 #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) /* DML status register defination */ #define DML_STATUS 0x0004 #define PRODUCER_IDLE (1 << 0) #define CONSUMER_IDLE (1 << 16) /* * DML SW RESET register defination * NOTE: write to this register resets the DML core. * All internal state information will be lost and all * register values will be reset as well */ #define DML_SW_RESET 0x0008 /* * DML PRODUCER START register defination * NOTE: A write to this register triggers the DML * Producer state machine. No SW register values will be * altered. */ #define DML_PRODUCER_START 0x000C /* * DML CONSUMER START register defination * NOTE: A write to this register triggers the DML * Consumer state machine. No SW register values will be * altered. */ #define DML_CONSUMER_START 0x0010 /* * DML producer pipe logical size register defination * NOTE: This register holds the size of the producer pipe * (in units of bytes) _to_ which the peripheral can * keep writing data to when its the PRODUCER. */ #define DML_PRODUCER_PIPE_LOGICAL_SIZE 0x0014 /* * DML producer pipe logical size register defination * NOTE: This register holds the size of the consumer pipe * (in units of bytes) _from_ which the peripheral * can keep _reading_ data from when its the CONSUMER. */ #define DML_CONSUMER_PIPE_LOGICAL_SIZE 0x00018 /* * DML PIPE ID register * This register holds pipe IDs that services * the producer and consumer side of the peripheral */ #define DML_PIPE_ID 0x0001C #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) /* * DML Producer trackers register defination. * This register is for debug purposes only. They reflect * the value of the producer block and transaction counters * when read. The values may be dynamically changing when * a transaction is in progress. */ #define DML_PRODUCER_TRACKERS 0x00020 #define PROD_BLOCK_CNT_SHFT 0 #define PROD_BLOCK_CNT_MSK 0xffff #define PROD_TRANS_CNT_SHFT 16 #define PROD_TRANS_CNT_MSK (0xffff << 16) /* * DML Producer BAM block size register defination. * This regsiter holds the block size, in units of bytes, * associated with the Producer BAM. The DML asserts the * block_end side band signal to the BAM whenever the producer * side of the peripheral has generated the said amount of data. * This register value should be an integral multiple of the * Producer CRCI Block Size. */ #define DML_PRODUCER_BAM_BLOCK_SIZE 0x00024 /* * DML Producer BAM Transaction size defination. * This regsiter holds the transaction size, in units of bytes, * associated with the Producer BAM. The DML asserts the transaction_end * side band signal to the BAM whenever the producer side of the peripheral * has generated the said amount of data. */ #define DML_PRODUCER_BAM_TRANS_SIZE 0x00028 /* * DML Direct mode base address defination * This register is used whenever the DIRECT_MODE bit * in config register is set. */ #define DML_DIRECT_MODE_BASE_ADDR 0x002C #define PRODUCER_BASE_ADDR_BSHFT 0 #define PRODUCER_BASE_ADDR_BMSK 0xffff #define CONSUMER_BASE_ADDR_BSHFT 16 #define CONSUMER_BASE_ADDR_BMSK (0xffff << 16) /* * DMA Debug and status register defination. * These are the read-only registers useful debugging. */ #define DML_DEBUG 0x0030 #define DML_BAM_SIDE_STATUS_1 0x0034 #define DML_BAM_SIDE_STATUS_2 0x0038 /* other definations */ #define PRODUCER_PIPE_LOGICAL_SIZE 4096 #define CONSUMER_PIPE_LOGICAL_SIZE 4096 #ifdef CONFIG_MMC_MSM_SPS_SUPPORT /** * Initialize DML HW connected with SDCC core * */ int msmsdcc_dml_init(struct msmsdcc_host *host) { int rc = 0; u32 config = 0; void __iomem *dml_base; if (!host->dml_base) { host->dml_base = ioremap(host->dml_memres->start, resource_size(host->dml_memres)); if (!host->dml_base) { pr_err("%s: DML ioremap() failed!!! %pr\n", mmc_hostname(host->mmc), host->dml_memres); rc = -ENOMEM; goto out; } pr_info("%s: Qualcomm MSM SDCC-DML %pr\n", mmc_hostname(host->mmc), host->dml_memres); } dml_base = host->dml_base; /* Reset the DML block */ writel_relaxed(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_relaxed(config, (dml_base + DML_CONFIG)); /* * Initialize the logical BAM pipe size for producer * and consumer. */ writel_relaxed(PRODUCER_PIPE_LOGICAL_SIZE, (dml_base + DML_PRODUCER_PIPE_LOGICAL_SIZE)); writel_relaxed(CONSUMER_PIPE_LOGICAL_SIZE, (dml_base + DML_CONSUMER_PIPE_LOGICAL_SIZE)); /* Initialize Producer/consumer pipe id */ writel_relaxed(host->sps.src_pipe_index | (host->sps.dest_pipe_index << CONSUMER_PIPE_ID_SHFT), (dml_base + DML_PIPE_ID)); mb(); out: return rc; } /** * Soft reset DML HW * */ void msmsdcc_dml_reset(struct msmsdcc_host *host) { /* Reset the DML block */ writel_relaxed(1, (host->dml_base + DML_SW_RESET)); mb(); } /** * Checks if DML HW is busy or not? * */ bool msmsdcc_is_dml_busy(struct msmsdcc_host *host) { return !(readl_relaxed(host->dml_base + DML_STATUS) & PRODUCER_IDLE) || !(readl_relaxed(host->dml_base + DML_STATUS) & CONSUMER_IDLE); } /** * Start data transfer. * */ void msmsdcc_dml_start_xfer(struct msmsdcc_host *host, struct mmc_data *data) { u32 config; void __iomem *dml_base = host->dml_base; if (data->flags & MMC_DATA_READ) { /* Read operation: configure DML for producer operation */ /* Set producer CRCI-x and disable consumer CRCI */ config = readl_relaxed(dml_base + DML_CONFIG); config = (config & ~PRODUCER_CRCI_MSK) | PRODUCER_CRCI_X_SEL; config = (config & ~CONSUMER_CRCI_MSK) | CONSUMER_CRCI_DIS; writel_relaxed(config, (dml_base + DML_CONFIG)); /* Set the Producer BAM block size */ writel_relaxed(data->blksz, (dml_base + DML_PRODUCER_BAM_BLOCK_SIZE)); /* Set Producer BAM Transaction size */ writel_relaxed(host->curr.xfer_size, (dml_base + DML_PRODUCER_BAM_TRANS_SIZE)); /* Set Producer Transaction End bit */ writel_relaxed((readl_relaxed(dml_base + DML_CONFIG) | PRODUCER_TRANS_END_EN), (dml_base + DML_CONFIG)); /* Trigger producer */ writel_relaxed(1, (dml_base + DML_PRODUCER_START)); } else { /* Write operation: configure DML for consumer operation */ /* Set consumer CRCI-x and disable producer CRCI*/ config = readl_relaxed(dml_base + DML_CONFIG); config = (config & ~CONSUMER_CRCI_MSK) | CONSUMER_CRCI_X_SEL; config = (config & ~PRODUCER_CRCI_MSK) | PRODUCER_CRCI_DIS; writel_relaxed(config, (dml_base + DML_CONFIG)); /* Clear Producer Transaction End bit */ writel_relaxed((readl_relaxed(dml_base + DML_CONFIG) & ~PRODUCER_TRANS_END_EN), (dml_base + DML_CONFIG)); /* Trigger consumer */ writel_relaxed(1, (dml_base + DML_CONSUMER_START)); } mb(); } /** * Deinitialize DML HW connected with SDCC core * */ void msmsdcc_dml_exit(struct msmsdcc_host *host) { /* Put DML block in reset state before exiting */ msmsdcc_dml_reset(host); iounmap(host->dml_base); } #endif /* CONFIG_MMC_MSM_SPS_SUPPORT */