#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/debugfs.h>
+#include <linux/dmaengine.h>
#include <linux/seq_file.h>
#include <linux/interrupt.h>
#include <linux/delay.h>
#include <linux/of.h>
#include <linux/of_gpio.h>
#include <linux/of_device.h>
+#include <linux/omap-dma.h>
#include <linux/mmc/host.h>
#include <linux/mmc/core.h>
#include <linux/mmc/mmc.h>
#include <linux/io.h>
-#include <linux/semaphore.h>
#include <linux/gpio.h>
#include <linux/regulator/consumer.h>
#include <linux/pm_runtime.h>
-#include <plat/dma.h>
#include <mach/hardware.h>
#include <plat/board.h>
#include <plat/mmc.h>
#include <plat/cpu.h>
/* OMAP HSMMC Host Controller Registers */
-#define OMAP_HSMMC_SYSCONFIG 0x0010
#define OMAP_HSMMC_SYSSTATUS 0x0014
#define OMAP_HSMMC_CON 0x002C
#define OMAP_HSMMC_BLK 0x0104
unsigned int dma_sg_idx;
unsigned char bus_mode;
unsigned char power_mode;
- u32 *buffer;
- u32 bytesleft;
int suspended;
int irq;
int use_dma, dma_ch;
- int dma_line_tx, dma_line_rx;
+ struct dma_chan *tx_chan;
+ struct dma_chan *rx_chan;
int slot_id;
int response_busy;
int context_loss;
- int vdd;
int protect_card;
int reqs_blocked;
int use_reg;
struct regulator *reg;
int ocr_value = 0;
- mmc_slot(host).set_power = omap_hsmmc_set_power;
-
reg = regulator_get(host->dev, "vmmc");
if (IS_ERR(reg)) {
dev_dbg(host->dev, "vmmc regulator missing\n");
+ return PTR_ERR(reg);
} else {
+ mmc_slot(host).set_power = omap_hsmmc_set_power;
host->vcc = reg;
ocr_value = mmc_regulator_get_ocrmask(reg);
if (!mmc_slot(host).ocr_mask) {
unsigned long regval;
unsigned long timeout;
- dev_dbg(mmc_dev(host->mmc), "Set clock to %uHz\n", ios->clock);
+ dev_vdbg(mmc_dev(host->mmc), "Set clock to %uHz\n", ios->clock);
omap_hsmmc_stop_clock(host);
if (host->context_loss == context_loss)
return 1;
- /* Wait for hardware reset */
- timeout = jiffies + msecs_to_jiffies(MMC_TIMEOUT_MS);
- while ((OMAP_HSMMC_READ(host->base, SYSSTATUS) & RESETDONE) != RESETDONE
- && time_before(jiffies, timeout))
- ;
-
- /* Do software reset */
- OMAP_HSMMC_WRITE(host->base, SYSCONFIG, SOFTRESET);
- timeout = jiffies + msecs_to_jiffies(MMC_TIMEOUT_MS);
- while ((OMAP_HSMMC_READ(host->base, SYSSTATUS) & RESETDONE) != RESETDONE
- && time_before(jiffies, timeout))
- ;
-
- OMAP_HSMMC_WRITE(host->base, SYSCONFIG,
- OMAP_HSMMC_READ(host->base, SYSCONFIG) | AUTOIDLE);
+ if (!OMAP_HSMMC_READ(host->base, SYSSTATUS) & RESETDONE)
+ return 1;
if (host->pdata->controller_flags & OMAP_HSMMC_SUPPORTS_DUAL_VOLT) {
if (host->power_mode != MMC_POWER_OFF &&
{
int cmdreg = 0, resptype = 0, cmdtype = 0;
- dev_dbg(mmc_dev(host->mmc), "%s: CMD%d, argument 0x%08x\n",
+ dev_vdbg(mmc_dev(host->mmc), "%s: CMD%d, argument 0x%08x\n",
mmc_hostname(host->mmc), cmd->opcode, cmd->arg);
host->cmd = cmd;
return DMA_FROM_DEVICE;
}
+static struct dma_chan *omap_hsmmc_get_dma_chan(struct omap_hsmmc_host *host,
+ struct mmc_data *data)
+{
+ return data->flags & MMC_DATA_WRITE ? host->tx_chan : host->rx_chan;
+}
+
static void omap_hsmmc_request_done(struct omap_hsmmc_host *host, struct mmc_request *mrq)
{
int dma_ch;
spin_unlock_irqrestore(&host->irq_lock, flags);
if (host->use_dma && dma_ch != -1) {
- dma_unmap_sg(mmc_dev(host->mmc), host->data->sg,
- host->data->sg_len,
+ 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));
- omap_free_dma(dma_ch);
+
host->data->host_cookie = 0;
}
host->data = NULL;
buf += len;
}
- dev_dbg(mmc_dev(host->mmc), "%s\n", res);
+ dev_vdbg(mmc_dev(host->mmc), "%s\n", res);
}
#else
static inline void omap_hsmmc_dbg_report_irq(struct omap_hsmmc_host *host,
__func__);
}
+static void hsmmc_command_incomplete(struct omap_hsmmc_host *host, int err)
+{
+ omap_hsmmc_reset_controller_fsm(host, SRC);
+ host->cmd->error = err;
+
+ if (host->data) {
+ omap_hsmmc_reset_controller_fsm(host, SRD);
+ omap_hsmmc_dma_cleanup(host, err);
+ }
+
+}
+
static void omap_hsmmc_do_irq(struct omap_hsmmc_host *host, int status)
{
struct mmc_data *data;
int end_cmd = 0, end_trans = 0;
- if (!host->req_in_progress) {
- do {
- OMAP_HSMMC_WRITE(host->base, STAT, status);
- /* Flush posted write */
- status = OMAP_HSMMC_READ(host->base, STAT);
- } while (status & INT_EN_MASK);
- return;
- }
-
data = host->data;
- dev_dbg(mmc_dev(host->mmc), "IRQ Status is %x\n", status);
+ dev_vdbg(mmc_dev(host->mmc), "IRQ Status is %x\n", status);
if (status & ERR) {
omap_hsmmc_dbg_report_irq(host, status);
- if ((status & CMD_TIMEOUT) ||
- (status & CMD_CRC)) {
- if (host->cmd) {
- if (status & CMD_TIMEOUT) {
- omap_hsmmc_reset_controller_fsm(host,
- SRC);
- host->cmd->error = -ETIMEDOUT;
- } else {
- host->cmd->error = -EILSEQ;
- }
- end_cmd = 1;
- }
- if (host->data || host->response_busy) {
- if (host->data)
- omap_hsmmc_dma_cleanup(host,
- -ETIMEDOUT);
- host->response_busy = 0;
- omap_hsmmc_reset_controller_fsm(host, SRD);
- }
- }
- if ((status & DATA_TIMEOUT) ||
- (status & DATA_CRC)) {
- if (host->data || host->response_busy) {
- int err = (status & DATA_TIMEOUT) ?
- -ETIMEDOUT : -EILSEQ;
-
- if (host->data)
- omap_hsmmc_dma_cleanup(host, err);
- else
- host->mrq->cmd->error = err;
- host->response_busy = 0;
- omap_hsmmc_reset_controller_fsm(host, SRD);
- end_trans = 1;
- }
- }
- if (status & CARD_ERR) {
- dev_dbg(mmc_dev(host->mmc),
- "Ignoring card err CMD%d\n", host->cmd->opcode);
- if (host->cmd)
- end_cmd = 1;
- if (host->data)
- end_trans = 1;
+ if (status & (CMD_TIMEOUT | DATA_TIMEOUT))
+ hsmmc_command_incomplete(host, -ETIMEDOUT);
+ else if (status & (CMD_CRC | DATA_CRC))
+ hsmmc_command_incomplete(host, -EILSEQ);
+
+ end_cmd = 1;
+ if (host->data || host->response_busy) {
+ end_trans = 1;
+ host->response_busy = 0;
}
}
- OMAP_HSMMC_WRITE(host->base, STAT, status);
-
if (end_cmd || ((status & CC) && host->cmd))
omap_hsmmc_cmd_done(host, host->cmd);
if ((end_trans || (status & TC)) && host->mrq)
int status;
status = OMAP_HSMMC_READ(host->base, STAT);
- do {
+ while (status & INT_EN_MASK && host->req_in_progress) {
omap_hsmmc_do_irq(host, status);
+
/* Flush posted write */
+ OMAP_HSMMC_WRITE(host->base, STAT, status);
status = OMAP_HSMMC_READ(host->base, STAT);
- } while (status & INT_EN_MASK);
+ }
return IRQ_HANDLED;
}
/* Disable the clocks */
pm_runtime_put_sync(host->dev);
if (host->dbclk)
- clk_disable(host->dbclk);
+ clk_disable_unprepare(host->dbclk);
/* Turn the power off */
ret = mmc_slot(host).set_power(host->dev, host->slot_id, 0, 0);
vdd);
pm_runtime_get_sync(host->dev);
if (host->dbclk)
- clk_enable(host->dbclk);
+ clk_prepare_enable(host->dbclk);
if (ret != 0)
goto err;
return IRQ_HANDLED;
}
-static int omap_hsmmc_get_dma_sync_dev(struct omap_hsmmc_host *host,
- struct mmc_data *data)
-{
- int sync_dev;
-
- if (data->flags & MMC_DATA_WRITE)
- sync_dev = host->dma_line_tx;
- else
- sync_dev = host->dma_line_rx;
- return sync_dev;
-}
-
-static void omap_hsmmc_config_dma_params(struct omap_hsmmc_host *host,
- struct mmc_data *data,
- struct scatterlist *sgl)
-{
- int blksz, nblk, dma_ch;
-
- dma_ch = host->dma_ch;
- if (data->flags & MMC_DATA_WRITE) {
- omap_set_dma_dest_params(dma_ch, 0, OMAP_DMA_AMODE_CONSTANT,
- (host->mapbase + OMAP_HSMMC_DATA), 0, 0);
- omap_set_dma_src_params(dma_ch, 0, OMAP_DMA_AMODE_POST_INC,
- sg_dma_address(sgl), 0, 0);
- } else {
- omap_set_dma_src_params(dma_ch, 0, OMAP_DMA_AMODE_CONSTANT,
- (host->mapbase + OMAP_HSMMC_DATA), 0, 0);
- omap_set_dma_dest_params(dma_ch, 0, OMAP_DMA_AMODE_POST_INC,
- sg_dma_address(sgl), 0, 0);
- }
-
- blksz = host->data->blksz;
- nblk = sg_dma_len(sgl) / blksz;
-
- omap_set_dma_transfer_params(dma_ch, OMAP_DMA_DATA_TYPE_S32,
- blksz / 4, nblk, OMAP_DMA_SYNC_FRAME,
- omap_hsmmc_get_dma_sync_dev(host, data),
- !(data->flags & MMC_DATA_WRITE));
-
- omap_start_dma(dma_ch);
-}
-
-/*
- * DMA call back function
- */
-static void omap_hsmmc_dma_cb(int lch, u16 ch_status, void *cb_data)
+static void omap_hsmmc_dma_callback(void *param)
{
- struct omap_hsmmc_host *host = cb_data;
+ struct omap_hsmmc_host *host = param;
+ struct dma_chan *chan;
struct mmc_data *data;
- int dma_ch, req_in_progress;
- unsigned long flags;
-
- if (!(ch_status & OMAP_DMA_BLOCK_IRQ)) {
- dev_warn(mmc_dev(host->mmc), "unexpected dma status %x\n",
- ch_status);
- return;
- }
+ int req_in_progress;
- spin_lock_irqsave(&host->irq_lock, flags);
+ spin_lock_irq(&host->irq_lock);
if (host->dma_ch < 0) {
- spin_unlock_irqrestore(&host->irq_lock, flags);
+ spin_unlock_irq(&host->irq_lock);
return;
}
data = host->mrq->data;
- host->dma_sg_idx++;
- if (host->dma_sg_idx < host->dma_len) {
- /* Fire up the next transfer. */
- omap_hsmmc_config_dma_params(host, data,
- data->sg + host->dma_sg_idx);
- spin_unlock_irqrestore(&host->irq_lock, flags);
- return;
- }
-
+ chan = omap_hsmmc_get_dma_chan(host, data);
if (!data->host_cookie)
- dma_unmap_sg(mmc_dev(host->mmc), data->sg, data->sg_len,
+ dma_unmap_sg(chan->device->dev,
+ data->sg, data->sg_len,
omap_hsmmc_get_dma_dir(host, data));
req_in_progress = host->req_in_progress;
- dma_ch = host->dma_ch;
host->dma_ch = -1;
- spin_unlock_irqrestore(&host->irq_lock, flags);
-
- omap_free_dma(dma_ch);
+ spin_unlock_irq(&host->irq_lock);
/* If DMA has finished after TC, complete the request */
if (!req_in_progress) {
static int omap_hsmmc_pre_dma_transfer(struct omap_hsmmc_host *host,
struct mmc_data *data,
- struct omap_hsmmc_next *next)
+ struct omap_hsmmc_next *next,
+ struct dma_chan *chan)
{
int dma_len;
/* Check if next job is already prepared */
if (next ||
(!next && data->host_cookie != host->next_data.cookie)) {
- dma_len = dma_map_sg(mmc_dev(host->mmc), data->sg,
- data->sg_len,
+ dma_len = dma_map_sg(chan->device->dev, data->sg, data->sg_len,
omap_hsmmc_get_dma_dir(host, data));
} else {
static int omap_hsmmc_start_dma_transfer(struct omap_hsmmc_host *host,
struct mmc_request *req)
{
- int dma_ch = 0, ret = 0, i;
+ struct dma_slave_config cfg;
+ struct dma_async_tx_descriptor *tx;
+ int ret = 0, i;
struct mmc_data *data = req->data;
+ struct dma_chan *chan;
/* Sanity check: all the SG entries must be aligned by block size. */
for (i = 0; i < data->sg_len; i++) {
BUG_ON(host->dma_ch != -1);
- ret = omap_request_dma(omap_hsmmc_get_dma_sync_dev(host, data),
- "MMC/SD", omap_hsmmc_dma_cb, host, &dma_ch);
- if (ret != 0) {
- dev_err(mmc_dev(host->mmc),
- "%s: omap_request_dma() failed with %d\n",
- mmc_hostname(host->mmc), ret);
+ chan = omap_hsmmc_get_dma_chan(host, data);
+
+ cfg.src_addr = host->mapbase + OMAP_HSMMC_DATA;
+ cfg.dst_addr = host->mapbase + OMAP_HSMMC_DATA;
+ cfg.src_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
+ cfg.dst_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
+ cfg.src_maxburst = data->blksz / 4;
+ cfg.dst_maxburst = data->blksz / 4;
+
+ ret = dmaengine_slave_config(chan, &cfg);
+ if (ret)
return ret;
- }
- ret = omap_hsmmc_pre_dma_transfer(host, data, NULL);
+
+ ret = omap_hsmmc_pre_dma_transfer(host, data, NULL, chan);
if (ret)
return ret;
- host->dma_ch = dma_ch;
- host->dma_sg_idx = 0;
+ tx = dmaengine_prep_slave_sg(chan, data->sg, data->sg_len,
+ data->flags & MMC_DATA_WRITE ? DMA_MEM_TO_DEV : DMA_DEV_TO_MEM,
+ DMA_PREP_INTERRUPT | DMA_CTRL_ACK);
+ if (!tx) {
+ dev_err(mmc_dev(host->mmc), "prep_slave_sg() failed\n");
+ /* FIXME: cleanup */
+ return -1;
+ }
+
+ tx->callback = omap_hsmmc_dma_callback;
+ tx->callback_param = host;
+
+ /* Does not fail */
+ dmaengine_submit(tx);
+
+ host->dma_ch = 1;
- omap_hsmmc_config_dma_params(host, data, data->sg);
+ dma_async_issue_pending(chan);
return 0;
}
struct omap_hsmmc_host *host = mmc_priv(mmc);
struct mmc_data *data = mrq->data;
- if (host->use_dma) {
- if (data->host_cookie)
- dma_unmap_sg(mmc_dev(host->mmc), data->sg,
- data->sg_len,
- omap_hsmmc_get_dma_dir(host, data));
+ if (host->use_dma && data->host_cookie) {
+ struct dma_chan *c = omap_hsmmc_get_dma_chan(host, data);
+
+ dma_unmap_sg(c->device->dev, data->sg, data->sg_len,
+ omap_hsmmc_get_dma_dir(host, data));
data->host_cookie = 0;
}
}
return ;
}
- if (host->use_dma)
+ if (host->use_dma) {
+ struct dma_chan *c = omap_hsmmc_get_dma_chan(host, mrq->data);
+
if (omap_hsmmc_pre_dma_transfer(host, mrq->data,
- &host->next_data))
+ &host->next_data, c))
mrq->data->host_cookie = 0;
+ }
}
/*
case MMC_POWER_OFF:
mmc_slot(host).set_power(host->dev, host->slot_id,
0, 0);
- host->vdd = 0;
break;
case MMC_POWER_UP:
mmc_slot(host).set_power(host->dev, host->slot_id,
1, ios->vdd);
- host->vdd = ios->vdd;
break;
case MMC_POWER_ON:
do_send_init_stream = 1;
value = OMAP_HSMMC_READ(host->base, CAPA);
OMAP_HSMMC_WRITE(host->base, CAPA, value | capa);
- /* Set the controller to AUTO IDLE mode */
- value = OMAP_HSMMC_READ(host->base, SYSCONFIG);
- OMAP_HSMMC_WRITE(host->base, SYSCONFIG, value | AUTOIDLE);
-
/* Set SD bus power bit */
set_sd_bus_power(host);
}
pm_runtime_get_sync(host->dev);
- seq_printf(s, "SYSCONFIG:\t0x%08x\n",
- OMAP_HSMMC_READ(host->base, SYSCONFIG));
seq_printf(s, "CON:\t\t0x%08x\n",
OMAP_HSMMC_READ(host->base, CON));
seq_printf(s, "HCTL:\t\t0x%08x\n",
struct resource *res;
int ret, irq;
const struct of_device_id *match;
+ dma_cap_mask_t mask;
+ unsigned tx_req, rx_req;
match = of_match_device(of_match_ptr(omap_mmc_of_match), &pdev->dev);
if (match) {
host->pdata = pdata;
host->dev = &pdev->dev;
host->use_dma = 1;
- host->dev->dma_mask = &pdata->dma_mask;
host->dma_ch = -1;
host->irq = irq;
host->slot_id = 0;
if (IS_ERR(host->dbclk)) {
dev_warn(mmc_dev(host->mmc), "Failed to get debounce clk\n");
host->dbclk = NULL;
- } else if (clk_enable(host->dbclk) != 0) {
+ } else if (clk_prepare_enable(host->dbclk) != 0) {
dev_warn(mmc_dev(host->mmc), "Failed to enable debounce clk\n");
clk_put(host->dbclk);
host->dbclk = NULL;
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;
}
- host->dma_line_tx = res->start;
+ 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_channel(mask, omap_dma_filter_fn, &rx_req);
+ if (!host->rx_chan) {
+ dev_err(mmc_dev(host->mmc), "unable to obtain RX DMA engine channel %u\n", rx_req);
+ ret = -ENXIO;
+ goto err_irq;
+ }
+
+ host->tx_chan = dma_request_channel(mask, omap_dma_filter_fn, &tx_req);
+ if (!host->tx_chan) {
+ dev_err(mmc_dev(host->mmc), "unable to obtain TX DMA engine channel %u\n", tx_req);
+ ret = -ENXIO;
goto err_irq;
}
- host->dma_line_rx = res->start;
/* Request IRQ for MMC operations */
ret = request_irq(host->irq, omap_hsmmc_irq, 0,
err_irq_cd_init:
free_irq(host->irq, host);
err_irq:
+ if (host->tx_chan)
+ dma_release_channel(host->tx_chan);
+ if (host->rx_chan)
+ dma_release_channel(host->rx_chan);
pm_runtime_put_sync(host->dev);
pm_runtime_disable(host->dev);
clk_put(host->fclk);
if (host->dbclk) {
- clk_disable(host->dbclk);
+ clk_disable_unprepare(host->dbclk);
clk_put(host->dbclk);
}
err1:
if (mmc_slot(host).card_detect_irq)
free_irq(mmc_slot(host).card_detect_irq, host);
+ if (host->tx_chan)
+ dma_release_channel(host->tx_chan);
+ if (host->rx_chan)
+ dma_release_channel(host->rx_chan);
+
pm_runtime_put_sync(host->dev);
pm_runtime_disable(host->dev);
clk_put(host->fclk);
if (host->dbclk) {
- clk_disable(host->dbclk);
+ clk_disable_unprepare(host->dbclk);
clk_put(host->dbclk);
}
}
if (host->dbclk)
- clk_disable(host->dbclk);
+ clk_disable_unprepare(host->dbclk);
err:
pm_runtime_put_sync(host->dev);
return ret;
pm_runtime_get_sync(host->dev);
if (host->dbclk)
- clk_enable(host->dbclk);
+ clk_prepare_enable(host->dbclk);
if (!(host->mmc->pm_flags & MMC_PM_KEEP_POWER))
omap_hsmmc_conf_bus_power(host);