Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit da7806f9 authored by Linus Torvalds's avatar Linus Torvalds
Browse files
* 'msm-mmc_sdcc' of git://codeaurora.org/quic/kernel/dwalker/linux-msm:
  drivers: mmc: msm_sdcc: Add EMBEDDED_SDIO support
  mmc: msm_sdcc: Fix issue where clocks could be disabled mid transaction
  mmc: msm_sdcc: Fix the dma exec function to use the proper delays
  mmc: msm_sdcc: Don't set host->curr.mrq until after we're sure the busclk timer won't fire
  mmc: msm_sdcc: Enable busclk idle timer for power savings
  mmc: msm_sdcc: Don't disable interrupts while suspending
  mmc: msm_sdcc: Fix issue where we might not end a sucessfull request
  mmc: msm_sdcc: Featurize busclock power save and disable it by default
  mmc: msm_sdcc: Fix bug where busclk expiry timer was not properly disabled
  mmc: msm_sdcc: Reduce command timeouts and improve reliability.
  mmc: msm_sdcc: Schedule clock disable after probe
  mmc: msm_sdcc: Wrap readl/writel calls with appropriate clk delays
  mmc: msm_sdcc: Driver clocking/irq improvements
  msm: Add 'execute' datamover callback
  mmc: msm_sdcc: Snoop SDIO_CCCR_ABORT register
  mmc: msm_sdcc: Clean up clock management and add a 10us delay after enabling clocks
parents d515e86e 1cd22969
Loading
Loading
Loading
Loading
+5 −0
Original line number Diff line number Diff line
@@ -69,6 +69,8 @@ void msm_dmov_enqueue_cmd(unsigned id, struct msm_dmov_cmd *cmd)
			writel(DMOV_CONFIG_IRQ_EN, DMOV_CONFIG(id));
		}
#endif
		if (cmd->execute_func)
			cmd->execute_func(cmd);
		PRINT_IO("msm_dmov_enqueue_cmd(%d), start command, status %x\n", id, status);
		list_add_tail(&cmd->list, &active_commands[id]);
		if (!channel_active)
@@ -116,6 +118,7 @@ int msm_dmov_exec_cmd(unsigned id, unsigned int cmdptr)

	cmd.dmov_cmd.cmdptr = cmdptr;
	cmd.dmov_cmd.complete_func = dmov_exec_cmdptr_complete_func;
	cmd.dmov_cmd.execute_func = NULL;
	cmd.id = id;
	init_completion(&cmd.complete);

@@ -221,6 +224,8 @@ static irqreturn_t msm_datamover_irq_handler(int irq, void *dev_id)
				cmd = list_entry(ready_commands[id].next, typeof(*cmd), list);
				list_del(&cmd->list);
				list_add_tail(&cmd->list, &active_commands[id]);
				if (cmd->execute_func)
					cmd->execute_func(cmd);
				PRINT_FLOW("msm_datamover_irq_handler id %d, start command\n", id);
				writel(cmd->cmdptr, DMOV_CMD_PTR(id));
			}
+2 −0
Original line number Diff line number Diff line
@@ -28,6 +28,8 @@ struct msm_dmov_cmd {
	void (*complete_func)(struct msm_dmov_cmd *cmd,
			      unsigned int result,
			      struct msm_dmov_errdata *err);
	void (*execute_func)(struct msm_dmov_cmd *cmd);
	void *data;
};

void msm_dmov_enqueue_cmd(unsigned id, struct msm_dmov_cmd *cmd);
+287 −185
Original line number Diff line number Diff line
@@ -3,6 +3,7 @@
 *
 *  Copyright (C) 2007 Google Inc,
 *  Copyright (C) 2003 Deep Blue Solutions, Ltd, All Rights Reserved.
 *  Copyright (C) 2009, Code Aurora Forum. 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 as
@@ -26,6 +27,7 @@
#include <linux/log2.h>
#include <linux/mmc/host.h>
#include <linux/mmc/card.h>
#include <linux/mmc/sdio.h>
#include <linux/clk.h>
#include <linux/scatterlist.h>
#include <linux/platform_device.h>
@@ -47,6 +49,8 @@

#define DRIVER_NAME "msm-sdcc"

#define BUSCLK_PWRSAVE 1
#define BUSCLK_TIMEOUT (HZ)
static unsigned int msmsdcc_fmin = 144000;
static unsigned int msmsdcc_fmax = 50000000;
static unsigned int msmsdcc_4bit = 1;
@@ -57,6 +61,67 @@ static unsigned int msmsdcc_sdioirq;
#define PIO_SPINMAX 30
#define CMD_SPINMAX 20


static inline void
msmsdcc_disable_clocks(struct msmsdcc_host *host, int deferr)
{
	WARN_ON(!host->clks_on);

	BUG_ON(host->curr.mrq);

	if (deferr) {
		mod_timer(&host->busclk_timer, jiffies + BUSCLK_TIMEOUT);
	} else {
		del_timer_sync(&host->busclk_timer);
		/* Need to check clks_on again in case the busclk
		 * timer fired
		 */
		if (host->clks_on) {
			clk_disable(host->clk);
			clk_disable(host->pclk);
			host->clks_on = 0;
		}
	}
}

static inline int
msmsdcc_enable_clocks(struct msmsdcc_host *host)
{
	int rc;

	del_timer_sync(&host->busclk_timer);

	if (!host->clks_on) {
		rc = clk_enable(host->pclk);
		if (rc)
			return rc;
		rc = clk_enable(host->clk);
		if (rc) {
			clk_disable(host->pclk);
			return rc;
		}
		udelay(1 + ((3 * USEC_PER_SEC) /
		       (host->clk_rate ? host->clk_rate : msmsdcc_fmin)));
		host->clks_on = 1;
	}
	return 0;
}

static inline unsigned int
msmsdcc_readl(struct msmsdcc_host *host, unsigned int reg)
{
	return readl(host->base + reg);
}

static inline void
msmsdcc_writel(struct msmsdcc_host *host, u32 data, unsigned int reg)
{
	writel(data, host->base + reg);
	/* 3 clk delay required! */
	udelay(1 + ((3 * USEC_PER_SEC) /
	       (host->clk_rate ? host->clk_rate : msmsdcc_fmin)));
}

static void
msmsdcc_start_command(struct msmsdcc_host *host, struct mmc_command *cmd,
		      u32 c);
@@ -64,8 +129,6 @@ msmsdcc_start_command(struct msmsdcc_host *host, struct mmc_command *cmd,
static void
msmsdcc_request_end(struct msmsdcc_host *host, struct mmc_request *mrq)
{
	writel(0, host->base + MMCICOMMAND);

	BUG_ON(host->curr.data);

	host->curr.mrq = NULL;
@@ -76,6 +139,9 @@ msmsdcc_request_end(struct msmsdcc_host *host, struct mmc_request *mrq)
	if (mrq->cmd->error == -ETIMEDOUT)
		mdelay(5);

#if BUSCLK_PWRSAVE
	msmsdcc_disable_clocks(host, 1);
#endif
	/*
	 * Need to drop the host lock here; mmc_request_done may call
	 * back into the driver...
@@ -88,7 +154,6 @@ msmsdcc_request_end(struct msmsdcc_host *host, struct mmc_request *mrq)
static void
msmsdcc_stop_data(struct msmsdcc_host *host)
{
	writel(0, host->base + MMCIDATACTRL);
	host->curr.data = NULL;
	host->curr.got_dataend = host->curr.got_datablkend = 0;
}
@@ -109,6 +174,31 @@ uint32_t msmsdcc_fifo_addr(struct msmsdcc_host *host)
	return 0;
}

static inline void
msmsdcc_start_command_exec(struct msmsdcc_host *host, u32 arg, u32 c) {
       msmsdcc_writel(host, arg, MMCIARGUMENT);
       msmsdcc_writel(host, c, MMCICOMMAND);
}

static void
msmsdcc_dma_exec_func(struct msm_dmov_cmd *cmd)
{
	struct msmsdcc_host *host = (struct msmsdcc_host *)cmd->data;

	msmsdcc_writel(host, host->cmd_timeout, MMCIDATATIMER);
	msmsdcc_writel(host, (unsigned int)host->curr.xfer_size,
		       MMCIDATALENGTH);
	msmsdcc_writel(host, host->cmd_pio_irqmask, MMCIMASK1);
	msmsdcc_writel(host, host->cmd_datactrl, MMCIDATACTRL);

	if (host->cmd_cmd) {
		msmsdcc_start_command_exec(host,
					   (u32) host->cmd_cmd->arg,
					   (u32) host->cmd_c);
	}
	host->dma.active = 1;
}

static void
msmsdcc_dma_complete_func(struct msm_dmov_cmd *cmd,
			  unsigned int result,
@@ -121,8 +211,11 @@ msmsdcc_dma_complete_func(struct msm_dmov_cmd *cmd,
	struct mmc_request	*mrq;

	spin_lock_irqsave(&host->lock, flags);
	host->dma.active = 0;

	mrq = host->curr.mrq;
	BUG_ON(!mrq);
	WARN_ON(!mrq->data);

	if (!(result & DMOV_RSLT_VALID)) {
		pr_err("msmsdcc: Invalid DataMover result\n");
@@ -146,7 +239,6 @@ msmsdcc_dma_complete_func(struct msm_dmov_cmd *cmd,
		if (!mrq->data->error)
			mrq->data->error = -EIO;
	}
	host->dma.busy = 0;
	dma_unmap_sg(mmc_dev(host->mmc), host->dma.sg, host->dma.num_ents,
		     host->dma.dir);

@@ -159,6 +251,7 @@ msmsdcc_dma_complete_func(struct msm_dmov_cmd *cmd,
	}

	host->dma.sg = NULL;
	host->dma.busy = 0;

	if ((host->curr.got_dataend && host->curr.got_datablkend)
	     || mrq->data->error) {
@@ -172,12 +265,14 @@ msmsdcc_dma_complete_func(struct msm_dmov_cmd *cmd,
		if (!mrq->data->error)
			host->curr.data_xfered = host->curr.xfer_size;
		if (!mrq->data->stop || mrq->cmd->error) {
			writel(0, host->base + MMCICOMMAND);
			host->curr.mrq = NULL;
			host->curr.cmd = NULL;
			mrq->data->bytes_xfered = host->curr.data_xfered;

			spin_unlock_irqrestore(&host->lock, flags);
#if BUSCLK_PWRSAVE
			msmsdcc_disable_clocks(host, 1);
#endif
			mmc_request_done(host->mmc, mrq);
			return;
		} else
@@ -218,6 +313,8 @@ static int msmsdcc_config_dma(struct msmsdcc_host *host, struct mmc_data *data)
	host->dma.sg = data->sg;
	host->dma.num_ents = data->sg_len;

       BUG_ON(host->dma.num_ents > NR_SG); /* Prevent memory corruption */

	nc = host->dma.nc;

	switch (host->pdev_id) {
@@ -246,21 +343,14 @@ static int msmsdcc_config_dma(struct msmsdcc_host *host, struct mmc_data *data)

	host->curr.user_pages = 0;

	n = dma_map_sg(mmc_dev(host->mmc), host->dma.sg,
		       host->dma.num_ents, host->dma.dir);

	if (n != host->dma.num_ents) {
		pr_err("%s: Unable to map in all sg elements\n",
		       mmc_hostname(host->mmc));
		host->dma.sg = NULL;
		host->dma.num_ents = 0;
		return -ENOMEM;
	}

	box = &nc->cmd[0];
	for (i = 0; i < host->dma.num_ents; i++) {
		box->cmd = CMD_MODE_BOX;

	/* Initialize sg dma address */
	sg->dma_address = page_to_dma(mmc_dev(host->mmc), sg_page(sg))
				+ sg->offset;

	if (i == (host->dma.num_ents - 1))
			box->cmd |= CMD_LC;
		rows = (sg_dma_len(sg) % MCI_FIFOSIZE) ?
@@ -300,15 +390,70 @@ static int msmsdcc_config_dma(struct msmsdcc_host *host, struct mmc_data *data)
			       DMOV_CMD_ADDR(host->dma.cmdptr_busaddr);
	host->dma.hdr.complete_func = msmsdcc_dma_complete_func;

	n = dma_map_sg(mmc_dev(host->mmc), host->dma.sg,
			host->dma.num_ents, host->dma.dir);
/* dsb inside dma_map_sg will write nc out to mem as well */

	if (n != host->dma.num_ents) {
		printk(KERN_ERR "%s: Unable to map in all sg elements\n",
			mmc_hostname(host->mmc));
		host->dma.sg = NULL;
		host->dma.num_ents = 0;
		return -ENOMEM;
	}

	return 0;
}

static int
snoop_cccr_abort(struct mmc_command *cmd)
{
	if ((cmd->opcode == 52) &&
	    (cmd->arg & 0x80000000) &&
	    (((cmd->arg >> 9) & 0x1ffff) == SDIO_CCCR_ABORT))
		return 1;
	return 0;
}

static void
msmsdcc_start_command_deferred(struct msmsdcc_host *host,
				struct mmc_command *cmd, u32 *c)
{
	*c |= (cmd->opcode | MCI_CPSM_ENABLE);

	if (cmd->flags & MMC_RSP_PRESENT) {
		if (cmd->flags & MMC_RSP_136)
			*c |= MCI_CPSM_LONGRSP;
		*c |= MCI_CPSM_RESPONSE;
	}

	if (/*interrupt*/0)
		*c |= MCI_CPSM_INTERRUPT;

	if ((((cmd->opcode == 17) || (cmd->opcode == 18))  ||
	     ((cmd->opcode == 24) || (cmd->opcode == 25))) ||
	      (cmd->opcode == 53))
		*c |= MCI_CSPM_DATCMD;

	if (cmd == cmd->mrq->stop)
		*c |= MCI_CSPM_MCIABORT;

	if (snoop_cccr_abort(cmd))
		*c |= MCI_CSPM_MCIABORT;

	if (host->curr.cmd != NULL) {
		printk(KERN_ERR "%s: Overlapping command requests\n",
			mmc_hostname(host->mmc));
	}
	host->curr.cmd = cmd;
}

static void
msmsdcc_start_data(struct msmsdcc_host *host, struct mmc_data *data)
msmsdcc_start_data(struct msmsdcc_host *host, struct mmc_data *data,
			struct mmc_command *cmd, u32 c)
{
	unsigned int datactrl, timeout;
	unsigned long long clks;
	void __iomem *base = host->base;
	unsigned int pio_irqmask = 0;

	host->curr.data = data;
@@ -320,13 +465,6 @@ msmsdcc_start_data(struct msmsdcc_host *host, struct mmc_data *data)

	memset(&host->pio, 0, sizeof(host->pio));

	clks = (unsigned long long)data->timeout_ns * host->clk_rate;
	do_div(clks, NSEC_PER_SEC);
	timeout = data->timeout_clks + (unsigned int)clks;
	writel(timeout, base + MMCIDATATIMER);

	writel(host->curr.xfer_size, base + MMCIDATALENGTH);

	datactrl = MCI_DPSM_ENABLE | (data->blksz << 4);

	if (!msmsdcc_config_dma(host, data))
@@ -347,47 +485,51 @@ msmsdcc_start_data(struct msmsdcc_host *host, struct mmc_data *data)
	if (data->flags & MMC_DATA_READ)
		datactrl |= MCI_DPSM_DIRECTION;

	writel(pio_irqmask, base + MMCIMASK1);
	writel(datactrl, base + MMCIDATACTRL);
	clks = (unsigned long long)data->timeout_ns * host->clk_rate;
	do_div(clks, NSEC_PER_SEC);
	timeout = data->timeout_clks + (unsigned int)clks*2 ;

	if (datactrl & MCI_DPSM_DMAENABLE) {
		/* Save parameters for the exec function */
		host->cmd_timeout = timeout;
		host->cmd_pio_irqmask = pio_irqmask;
		host->cmd_datactrl = datactrl;
		host->cmd_cmd = cmd;

		host->dma.hdr.execute_func = msmsdcc_dma_exec_func;
		host->dma.hdr.data = (void *)host;
		host->dma.busy = 1;

		if (cmd) {
			msmsdcc_start_command_deferred(host, cmd, &c);
			host->cmd_c = c;
		}
		msm_dmov_enqueue_cmd(host->dma.channel, &host->dma.hdr);
	} else {
		msmsdcc_writel(host, timeout, MMCIDATATIMER);

		msmsdcc_writel(host, host->curr.xfer_size, MMCIDATALENGTH);

		msmsdcc_writel(host, pio_irqmask, MMCIMASK1);
		msmsdcc_writel(host, datactrl, MMCIDATACTRL);

		if (cmd) {
			/* Daisy-chain the command if requested */
			msmsdcc_start_command(host, cmd, c);
		}
	}
}

static void
msmsdcc_start_command(struct msmsdcc_host *host, struct mmc_command *cmd, u32 c)
{
	void __iomem *base = host->base;

	if (readl(base + MMCICOMMAND) & MCI_CPSM_ENABLE) {
		writel(0, base + MMCICOMMAND);
		udelay(2 + ((5 * 1000000) / host->clk_rate));
	}

	c |= cmd->opcode | MCI_CPSM_ENABLE;

	if (cmd->flags & MMC_RSP_PRESENT) {
		if (cmd->flags & MMC_RSP_136)
			c |= MCI_CPSM_LONGRSP;
		c |= MCI_CPSM_RESPONSE;
	}

	if (cmd->opcode == 17 || cmd->opcode == 18 ||
	    cmd->opcode == 24 || cmd->opcode == 25 ||
	    cmd->opcode == 53)
		c |= MCI_CSPM_DATCMD;

	if (cmd == cmd->mrq->stop)
		c |= MCI_CSPM_MCIABORT;

	host->curr.cmd = cmd;

	host->stats.cmds++;

	writel(cmd->arg, base + MMCIARGUMENT);
	writel(c, base + MMCICOMMAND);
	msmsdcc_start_command_deferred(host, cmd, &c);
	msmsdcc_start_command_exec(host, cmd->arg, c);
}

static void
@@ -421,13 +563,11 @@ msmsdcc_data_err(struct msmsdcc_host *host, struct mmc_data *data,
static int
msmsdcc_pio_read(struct msmsdcc_host *host, char *buffer, unsigned int remain)
{
	void __iomem	*base = host->base;
	uint32_t	*ptr = (uint32_t *) buffer;
	int		count = 0;

	while (readl(base + MMCISTATUS) & MCI_RXDATAAVLBL) {

		*ptr = readl(base + MMCIFIFO + (count % MCI_FIFOSIZE));
	while (msmsdcc_readl(host, MMCISTATUS) & MCI_RXDATAAVLBL) {
		*ptr = msmsdcc_readl(host, MMCIFIFO + (count % MCI_FIFOSIZE));
		ptr++;
		count += sizeof(uint32_t);

@@ -459,7 +599,7 @@ msmsdcc_pio_write(struct msmsdcc_host *host, char *buffer,
		if (remain == 0)
			break;

		status = readl(base + MMCISTATUS);
		status = msmsdcc_readl(host, MMCISTATUS);
	} while (status & MCI_TXFIFOHALFEMPTY);

	return ptr - buffer;
@@ -469,7 +609,7 @@ static int
msmsdcc_spin_on_status(struct msmsdcc_host *host, uint32_t mask, int maxspin)
{
	while (maxspin) {
		if ((readl(host->base + MMCISTATUS) & mask))
		if ((msmsdcc_readl(host, MMCISTATUS) & mask))
			return 0;
		udelay(1);
		--maxspin;
@@ -477,14 +617,13 @@ msmsdcc_spin_on_status(struct msmsdcc_host *host, uint32_t mask, int maxspin)
	return -ETIMEDOUT;
}

static int
static irqreturn_t
msmsdcc_pio_irq(int irq, void *dev_id)
{
	struct msmsdcc_host	*host = dev_id;
	void __iomem		*base = host->base;
	uint32_t		status;

	status = readl(base + MMCISTATUS);
	status = msmsdcc_readl(host, MMCISTATUS);

	do {
		unsigned long flags;
@@ -539,14 +678,14 @@ msmsdcc_pio_irq(int irq, void *dev_id)
			host->pio.sg_off = 0;
		}

		status = readl(base + MMCISTATUS);
		status = msmsdcc_readl(host, MMCISTATUS);
	} while (1);

	if (status & MCI_RXACTIVE && host->curr.xfer_remain < MCI_FIFOSIZE)
		writel(MCI_RXDATAAVLBLMASK, base + MMCIMASK1);
		msmsdcc_writel(host, MCI_RXDATAAVLBLMASK, MMCIMASK1);

	if (!host->curr.xfer_remain)
		writel(0, base + MMCIMASK1);
		msmsdcc_writel(host, 0, MMCIMASK1);

	return IRQ_HANDLED;
}
@@ -554,15 +693,13 @@ msmsdcc_pio_irq(int irq, void *dev_id)
static void msmsdcc_do_cmdirq(struct msmsdcc_host *host, uint32_t status)
{
	struct mmc_command *cmd = host->curr.cmd;
	void __iomem	   *base = host->base;

	host->curr.cmd = NULL;
	cmd->resp[0] = readl(base + MMCIRESPONSE0);
	cmd->resp[1] = readl(base + MMCIRESPONSE1);
	cmd->resp[2] = readl(base + MMCIRESPONSE2);
	cmd->resp[3] = readl(base + MMCIRESPONSE3);
	cmd->resp[0] = msmsdcc_readl(host, MMCIRESPONSE0);
	cmd->resp[1] = msmsdcc_readl(host, MMCIRESPONSE1);
	cmd->resp[2] = msmsdcc_readl(host, MMCIRESPONSE2);
	cmd->resp[3] = msmsdcc_readl(host, MMCIRESPONSE3);

	del_timer(&host->command_timer);
	if (status & MCI_CMDTIMEOUT) {
		cmd->error = -ETIMEDOUT;
	} else if (status & MCI_CMDCRCFAIL &&
@@ -580,8 +717,10 @@ static void msmsdcc_do_cmdirq(struct msmsdcc_host *host, uint32_t status)
			msmsdcc_request_end(host, cmd->mrq);
		} else /* host->data == NULL */
			msmsdcc_request_end(host, cmd->mrq);
	} else if (!(cmd->data->flags & MMC_DATA_READ))
		msmsdcc_start_data(host, cmd->data);
	} else if (cmd->data)
		if (!(cmd->data->flags & MMC_DATA_READ))
			msmsdcc_start_data(host, cmd->data,
						NULL, 0);
}

static void
@@ -590,6 +729,11 @@ msmsdcc_handle_irq_data(struct msmsdcc_host *host, u32 status,
{
	struct mmc_data *data = host->curr.data;

	if (status & (MCI_CMDSENT | MCI_CMDRESPEND | MCI_CMDCRCFAIL |
	              MCI_CMDTIMEOUT) && host->curr.cmd) {
		msmsdcc_do_cmdirq(host, status);
	}

	if (!data)
		return;

@@ -602,6 +746,7 @@ msmsdcc_handle_irq_data(struct msmsdcc_host *host, u32 status,
			msm_dmov_stop_cmd(host->dma.channel,
					  &host->dma.hdr, 0);
		else {
			if (host->curr.data)
				msmsdcc_stop_data(host);
			if (!data->stop)
				msmsdcc_request_end(host, data->mrq);
@@ -657,17 +802,18 @@ msmsdcc_irq(int irq, void *dev_id)
	spin_lock(&host->lock);

	do {
		status = readl(base + MMCISTATUS);
		status = msmsdcc_readl(host, MMCISTATUS);
		status &= (msmsdcc_readl(host, MMCIMASK0) |
					      MCI_DATABLOCKENDMASK);
		msmsdcc_writel(host, status, MMCICLEAR);

		status &= (readl(base + MMCIMASK0) | MCI_DATABLOCKENDMASK);
		writel(status, base + MMCICLEAR);
		if (status & MCI_SDIOINTR)
			status &= ~MCI_SDIOINTR;

		msmsdcc_handle_irq_data(host, status, base);
		if (!status)
			break;

		if (status & (MCI_CMDSENT | MCI_CMDRESPEND | MCI_CMDCRCFAIL |
			      MCI_CMDTIMEOUT) && host->curr.cmd) {
			msmsdcc_do_cmdirq(host, status);
		}
		msmsdcc_handle_irq_data(host, status, base);

		if (status & MCI_SDIOINTOPER) {
			cardint = 1;
@@ -714,24 +860,27 @@ msmsdcc_request(struct mmc_host *mmc, struct mmc_request *mrq)
		return;
	}

	msmsdcc_enable_clocks(host);

	host->curr.mrq = mrq;

	if (mrq->data && mrq->data->flags & MMC_DATA_READ)
		msmsdcc_start_data(host, mrq->data);

		/* Queue/read data, daisy-chain command when data starts */
		msmsdcc_start_data(host, mrq->data, mrq->cmd, 0);
	else
		msmsdcc_start_command(host, mrq->cmd, 0);

	if (host->cmdpoll && !msmsdcc_spin_on_status(host,
				MCI_CMDRESPEND|MCI_CMDCRCFAIL|MCI_CMDTIMEOUT,
				CMD_SPINMAX)) {
		uint32_t status = readl(host->base + MMCISTATUS);
		uint32_t status = msmsdcc_readl(host, MMCISTATUS);
		msmsdcc_do_cmdirq(host, status);
		writel(MCI_CMDRESPEND | MCI_CMDCRCFAIL | MCI_CMDTIMEOUT,
		       host->base + MMCICLEAR);
		msmsdcc_writel(host,
			       MCI_CMDRESPEND | MCI_CMDCRCFAIL | MCI_CMDTIMEOUT,
			       MMCICLEAR);
		host->stats.cmdpoll_hits++;
	} else {
		host->stats.cmdpoll_misses++;
		mod_timer(&host->command_timer, jiffies + HZ);
	}
	spin_unlock_irqrestore(&host->lock, flags);
}
@@ -742,14 +891,13 @@ msmsdcc_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
	struct msmsdcc_host *host = mmc_priv(mmc);
	u32 clk = 0, pwr = 0;
	int rc;
	unsigned long flags;

	if (ios->clock) {
	spin_lock_irqsave(&host->lock, flags);

		if (!host->clks_on) {
			clk_enable(host->pclk);
			clk_enable(host->clk);
			host->clks_on = 1;
		}
	msmsdcc_enable_clocks(host);

	if (ios->clock) {
		if (ios->clock != host->clk_rate) {
			rc = clk_set_rate(host->clk, ios->clock);
			if (rc < 0)
@@ -787,18 +935,16 @@ msmsdcc_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
	if (ios->bus_mode == MMC_BUSMODE_OPENDRAIN)
		pwr |= MCI_OD;

	writel(clk, host->base + MMCICLOCK);
	msmsdcc_writel(host, clk, MMCICLOCK);

	if (host->pwr != pwr) {
		host->pwr = pwr;
		writel(pwr, host->base + MMCIPOWER);
	}

	if (!(clk & MCI_CLK_ENABLE) && host->clks_on) {
		clk_disable(host->clk);
		clk_disable(host->pclk);
		host->clks_on = 0;
		msmsdcc_writel(host, pwr, MMCIPOWER);
	}
#if BUSCLK_PWRSAVE
	msmsdcc_disable_clocks(host, 1);
#endif
	spin_unlock_irqrestore(&host->lock, flags);
}

static void msmsdcc_enable_sdio_irq(struct mmc_host *mmc, int enable)
@@ -809,13 +955,13 @@ static void msmsdcc_enable_sdio_irq(struct mmc_host *mmc, int enable)

	spin_lock_irqsave(&host->lock, flags);
	if (msmsdcc_sdioirq == 1) {
		status = readl(host->base + MMCIMASK0);
		status = msmsdcc_readl(host, MMCIMASK0);
		if (enable)
			status |= MCI_SDIOINTOPERMASK;
		else
			status &= ~MCI_SDIOINTOPERMASK;
		host->saved_irq0mask = status;
		writel(status, host->base + MMCIMASK0);
		msmsdcc_writel(host, status, MMCIMASK0);
	}
	spin_unlock_irqrestore(&host->lock, flags);
}
@@ -875,42 +1021,13 @@ msmsdcc_status_notify_cb(int card_present, void *dev_id)
	msmsdcc_check_status((unsigned long) host);
}

/*
 * called when a command expires.
 * Dump some debugging, and then error
 * out the transaction.
 */
static void
msmsdcc_command_expired(unsigned long _data)
msmsdcc_busclk_expired(unsigned long _data)
{
	struct msmsdcc_host	*host = (struct msmsdcc_host *) _data;
	struct mmc_request	*mrq;
	unsigned long		flags;

	spin_lock_irqsave(&host->lock, flags);
	mrq = host->curr.mrq;

	if (!mrq) {
		pr_info("%s: Command expiry misfire\n",
			mmc_hostname(host->mmc));
		spin_unlock_irqrestore(&host->lock, flags);
		return;
	}

	pr_err("%s: Command timeout (%p %p %p %p)\n",
	       mmc_hostname(host->mmc), mrq, mrq->cmd,
	       mrq->data, host->dma.sg);

	mrq->cmd->error = -ETIMEDOUT;
	msmsdcc_stop_data(host);

	writel(0, host->base + MMCICOMMAND);

	host->curr.mrq = NULL;
	host->curr.cmd = NULL;

	spin_unlock_irqrestore(&host->lock, flags);
	mmc_request_done(host->mmc, mrq);
	if (host->clks_on)
		msmsdcc_disable_clocks(host, 0);
}

static int
@@ -1012,6 +1129,7 @@ msmsdcc_probe(struct platform_device *pdev)
	host->pdev_id = pdev->id;
	host->plat = plat;
	host->mmc = mmc;
	host->curr.cmd = NULL;

	host->cmdpoll = 1;

@@ -1027,36 +1145,35 @@ msmsdcc_probe(struct platform_device *pdev)
	host->dmares = dmares;
	spin_lock_init(&host->lock);

#ifdef CONFIG_MMC_EMBEDDED_SDIO
	if (plat->embedded_sdio)
		mmc_set_embedded_sdio_data(mmc,
					   &plat->embedded_sdio->cis,
					   &plat->embedded_sdio->cccr,
					   plat->embedded_sdio->funcs,
					   plat->embedded_sdio->num_funcs);
#endif

	/*
	 * Setup DMA
	 */
	msmsdcc_init_dma(host);

	/*
	 * Setup main peripheral bus clock
	 */
	/* Get our clocks */
	host->pclk = clk_get(&pdev->dev, "sdc_pclk");
	if (IS_ERR(host->pclk)) {
		ret = PTR_ERR(host->pclk);
		goto host_free;
	}

	ret = clk_enable(host->pclk);
	if (ret)
		goto pclk_put;

	host->pclk_rate = clk_get_rate(host->pclk);

	/*
	 * Setup SDC MMC clock
	 */
	host->clk = clk_get(&pdev->dev, "sdc_clk");
	if (IS_ERR(host->clk)) {
		ret = PTR_ERR(host->clk);
		goto pclk_disable;
		goto pclk_put;
	}

	ret = clk_enable(host->clk);
	/* Enable clocks */
	ret = msmsdcc_enable_clocks(host);
	if (ret)
		goto clk_put;

@@ -1066,10 +1183,9 @@ msmsdcc_probe(struct platform_device *pdev)
		goto clk_disable;
	}

	host->pclk_rate = clk_get_rate(host->pclk);
	host->clk_rate = clk_get_rate(host->clk);

	host->clks_on = 1;

	/*
	 * Setup MMC host structure
	 */
@@ -1092,10 +1208,10 @@ msmsdcc_probe(struct platform_device *pdev)
	mmc->max_req_size = 33554432;	/* MCI_DATA_LENGTH is 25 bits */
	mmc->max_seg_size = mmc->max_req_size;

	writel(0, host->base + MMCIMASK0);
	writel(0x5e007ff, host->base + MMCICLEAR); /* Add: 1 << 25 */
	msmsdcc_writel(host, 0, MMCIMASK0);
	msmsdcc_writel(host, 0x5e007ff, MMCICLEAR);

	writel(MCI_IRQENABLE, host->base + MMCIMASK0);
	msmsdcc_writel(host, MCI_IRQENABLE, MMCIMASK0);
	host->saved_irq0mask = MCI_IRQENABLE;

	/*
@@ -1137,13 +1253,9 @@ msmsdcc_probe(struct platform_device *pdev)
		host->eject = !host->oldstat;
	}

	/*
	 * Setup a command timer. We currently need this due to
	 * some 'strange' timeout / error handling situations.
	 */
	init_timer(&host->command_timer);
	host->command_timer.data = (unsigned long) host;
	host->command_timer.function = msmsdcc_command_expired;
	init_timer(&host->busclk_timer);
	host->busclk_timer.data = (unsigned long) host;
	host->busclk_timer.function = msmsdcc_busclk_expired;

	ret = request_irq(cmd_irqres->start, msmsdcc_irq, IRQF_SHARED,
			  DRIVER_NAME " (cmd)", host);
@@ -1181,6 +1293,9 @@ msmsdcc_probe(struct platform_device *pdev)
	if (host->timer.function)
		pr_info("%s: Polling status mode enabled\n", mmc_hostname(mmc));

#if BUSCLK_PWRSAVE
	msmsdcc_disable_clocks(host, 1);
#endif
	return 0;
 cmd_irq_free:
	free_irq(cmd_irqres->start, host);
@@ -1188,11 +1303,9 @@ msmsdcc_probe(struct platform_device *pdev)
	if (host->stat_irq)
		free_irq(host->stat_irq, host);
 clk_disable:
	clk_disable(host->clk);
	msmsdcc_disable_clocks(host, 0);
 clk_put:
	clk_put(host->clk);
 pclk_disable:
	clk_disable(host->pclk);
 pclk_put:
	clk_put(host->pclk);
 host_free:
@@ -1215,15 +1328,10 @@ msmsdcc_suspend(struct platform_device *dev, pm_message_t state)

		if (mmc->card && mmc->card->type != MMC_TYPE_SDIO)
			rc = mmc_suspend_host(mmc, state);
		if (!rc) {
			writel(0, host->base + MMCIMASK0);

			if (host->clks_on) {
				clk_disable(host->clk);
				clk_disable(host->pclk);
				host->clks_on = 0;
			}
		}
		if (!rc)
			msmsdcc_writel(host, 0, MMCIMASK0);
		if (host->clks_on)
			msmsdcc_disable_clocks(host, 0);
	}
	return rc;
}
@@ -1232,27 +1340,21 @@ static int
msmsdcc_resume(struct platform_device *dev)
{
	struct mmc_host *mmc = mmc_get_drvdata(dev);
	unsigned long flags;

	if (mmc) {
		struct msmsdcc_host *host = mmc_priv(mmc);

		spin_lock_irqsave(&host->lock, flags);
		msmsdcc_enable_clocks(host);

		if (!host->clks_on) {
			clk_enable(host->pclk);
			clk_enable(host->clk);
			host->clks_on = 1;
		}

		writel(host->saved_irq0mask, host->base + MMCIMASK0);

		spin_unlock_irqrestore(&host->lock, flags);
		msmsdcc_writel(host, host->saved_irq0mask, MMCIMASK0);

		if (mmc->card && mmc->card->type != MMC_TYPE_SDIO)
			mmc_resume_host(mmc);
		if (host->stat_irq)
			enable_irq(host->stat_irq);
#if BUSCLK_PWRSAVE
		msmsdcc_disable_clocks(host, 1);
#endif
	}
	return 0;
}
+14 −1
Original line number Diff line number Diff line
@@ -171,6 +171,7 @@ struct msmsdcc_dma_data {
	int				channel;
	struct msmsdcc_host		*host;
	int				busy; /* Set if DM is busy */
	int				active;
};

struct msmsdcc_pio_data {
@@ -213,7 +214,7 @@ struct msmsdcc_host {
	struct clk		*clk;		/* main MMC bus clock */
	struct clk		*pclk;		/* SDCC peripheral bus clock */
	unsigned int		clks_on;	/* set if clocks are enabled */
	struct timer_list	command_timer;
	struct timer_list	busclk_timer;

	unsigned int		eject;		/* eject state */

@@ -233,6 +234,18 @@ struct msmsdcc_host {
	struct msmsdcc_pio_data	pio;
	int			cmdpoll;
	struct msmsdcc_stats	stats;

#ifdef CONFIG_MMC_MSM7X00A_RESUME_IN_WQ
	struct work_struct	resume_task;
#endif

	/* Command parameters */
	unsigned int		cmd_timeout;
	unsigned int		cmd_pio_irqmask;
	unsigned int		cmd_datactrl;
	struct mmc_command	*cmd_cmd;
	u32			cmd_c;

};

#endif