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

Commit bba1594d authored by Russell King's avatar Russell King
Browse files

Merge branch 'mmci' into amba

parents 9e5ed094 7437cfa5
Loading
Loading
Loading
Loading
+8 −13
Original line number Diff line number Diff line
@@ -31,21 +31,13 @@
 * SDI 0 (MicroSD slot)
 */

/* MMCIPOWER bits */
#define MCI_DATA2DIREN		(1 << 2)
#define MCI_CMDDIREN		(1 << 3)
#define MCI_DATA0DIREN		(1 << 4)
#define MCI_DATA31DIREN		(1 << 5)
#define MCI_FBCLKEN		(1 << 7)

/* GPIO pins used by the sdi0 level shifter */
static int sdi0_en = -1;
static int sdi0_vsel = -1;

static u32 mop500_sdi0_vdd_handler(struct device *dev, unsigned int vdd,
				   unsigned char power_mode)
static int mop500_sdi0_ios_handler(struct device *dev, struct mmc_ios *ios)
{
	switch (power_mode) {
	switch (ios->power_mode) {
	case MMC_POWER_UP:
	case MMC_POWER_ON:
		/*
@@ -65,8 +57,7 @@ static u32 mop500_sdi0_vdd_handler(struct device *dev, unsigned int vdd,
		break;
	}

	return MCI_FBCLKEN | MCI_CMDDIREN | MCI_DATA0DIREN |
	       MCI_DATA2DIREN | MCI_DATA31DIREN;
	return 0;
}

#ifdef CONFIG_STE_DMA40
@@ -90,13 +81,17 @@ static struct stedma40_chan_cfg mop500_sdi0_dma_cfg_tx = {
#endif

static struct mmci_platform_data mop500_sdi0_data = {
	.vdd_handler	= mop500_sdi0_vdd_handler,
	.ios_handler	= mop500_sdi0_ios_handler,
	.ocr_mask	= MMC_VDD_29_30,
	.f_max		= 50000000,
	.capabilities	= MMC_CAP_4_BIT_DATA |
				MMC_CAP_SD_HIGHSPEED |
				MMC_CAP_MMC_HIGHSPEED,
	.gpio_wp	= -1,
	.sigdir		= MCI_ST_FBCLKEN |
				MCI_ST_CMDDIREN |
				MCI_ST_DATA0DIREN |
				MCI_ST_DATA2DIREN,
#ifdef CONFIG_STE_DMA40
	.dma_filter	= stedma40_filter,
	.dma_rx_param	= &mop500_sdi0_dma_cfg_rx,
+121 −45
Original line number Diff line number Diff line
@@ -53,6 +53,8 @@ static unsigned int fmax = 515633;
 * @sdio: variant supports SDIO
 * @st_clkdiv: true if using a ST-specific clock divider algorithm
 * @blksz_datactrl16: true if Block size is at b16..b30 position in datactrl register
 * @pwrreg_powerup: power up value for MMCIPOWER register
 * @signal_direction: input/out direction of bus signals can be indicated
 */
struct variant_data {
	unsigned int		clkreg;
@@ -63,18 +65,22 @@ struct variant_data {
	bool			sdio;
	bool			st_clkdiv;
	bool			blksz_datactrl16;
	u32			pwrreg_powerup;
	bool			signal_direction;
};

static struct variant_data variant_arm = {
	.fifosize		= 16 * 4,
	.fifohalfsize		= 8 * 4,
	.datalength_bits	= 16,
	.pwrreg_powerup		= MCI_PWR_UP,
};

static struct variant_data variant_arm_extended_fifo = {
	.fifosize		= 128 * 4,
	.fifohalfsize		= 64 * 4,
	.datalength_bits	= 16,
	.pwrreg_powerup		= MCI_PWR_UP,
};

static struct variant_data variant_u300 = {
@@ -83,6 +89,8 @@ static struct variant_data variant_u300 = {
	.clkreg_enable		= MCI_ST_U300_HWFCEN,
	.datalength_bits	= 16,
	.sdio			= true,
	.pwrreg_powerup		= MCI_PWR_ON,
	.signal_direction	= true,
};

static struct variant_data variant_ux500 = {
@@ -93,6 +101,8 @@ static struct variant_data variant_ux500 = {
	.datalength_bits	= 24,
	.sdio			= true,
	.st_clkdiv		= true,
	.pwrreg_powerup		= MCI_PWR_ON,
	.signal_direction	= true,
};

static struct variant_data variant_ux500v2 = {
@@ -104,8 +114,32 @@ static struct variant_data variant_ux500v2 = {
	.sdio			= true,
	.st_clkdiv		= true,
	.blksz_datactrl16	= true,
	.pwrreg_powerup		= MCI_PWR_ON,
	.signal_direction	= true,
};

/*
 * This must be called with host->lock held
 */
static void mmci_write_clkreg(struct mmci_host *host, u32 clk)
{
	if (host->clk_reg != clk) {
		host->clk_reg = clk;
		writel(clk, host->base + MMCICLOCK);
	}
}

/*
 * This must be called with host->lock held
 */
static void mmci_write_pwrreg(struct mmci_host *host, u32 pwr)
{
	if (host->pwr_reg != pwr) {
		host->pwr_reg = pwr;
		writel(pwr, host->base + MMCIPOWER);
	}
}

/*
 * This must be called with host->lock held
 */
@@ -153,7 +187,7 @@ static void mmci_set_clkreg(struct mmci_host *host, unsigned int desired)
	if (host->mmc->ios.bus_width == MMC_BUS_WIDTH_8)
		clk |= MCI_ST_8BIT_BUS;

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

static void
@@ -166,14 +200,10 @@ mmci_request_end(struct mmci_host *host, struct mmc_request *mrq)
	host->mrq = NULL;
	host->cmd = NULL;

	/*
	 * Need to drop the host lock here; mmc_request_done may call
	 * back into the driver...
	 */
	spin_unlock(&host->lock);
	pm_runtime_put(mmc_dev(host->mmc));
	mmc_request_done(host->mmc, mrq);
	spin_lock(&host->lock);

	pm_runtime_mark_last_busy(mmc_dev(host->mmc));
	pm_runtime_put_autosuspend(mmc_dev(host->mmc));
}

static void mmci_set_mask1(struct mmci_host *host, unsigned int mask)
@@ -607,6 +637,11 @@ static void mmci_start_data(struct mmci_host *host, struct mmc_data *data)
	if (data->flags & MMC_DATA_READ)
		datactrl |= MCI_DPSM_DIRECTION;

	/* The ST Micro variants has a special bit to enable SDIO */
	if (variant->sdio && host->mmc->card)
		if (mmc_card_sdio(host->mmc->card))
			datactrl |= MCI_ST_DPSM_SDIOEN;

	/*
	 * Attempt to use DMA operation mode, if this
	 * should fail, fall back to PIO mode
@@ -635,11 +670,6 @@ static void mmci_start_data(struct mmci_host *host, struct mmc_data *data)
		irqmask = MCI_TXFIFOHALFEMPTYMASK;
	}

	/* The ST Micro variants has a special bit to enable SDIO */
	if (variant->sdio && host->mmc->card)
		if (mmc_card_sdio(host->mmc->card))
			datactrl |= MCI_ST_DPSM_SDIOEN;

	writel(datactrl, base + MMCIDATACTRL);
	writel(readl(base + MMCIMASK0) & ~MCI_DATAENDMASK, base + MMCIMASK0);
	mmci_set_mask1(host, irqmask);
@@ -786,7 +816,24 @@ static int mmci_pio_read(struct mmci_host *host, char *buffer, unsigned int rema
		if (count <= 0)
			break;

		/*
		 * SDIO especially may want to send something that is
		 * not divisible by 4 (as opposed to card sectors
		 * etc). Therefore make sure to always read the last bytes
		 * while only doing full 32-bit reads towards the FIFO.
		 */
		if (unlikely(count & 0x3)) {
			if (count < 4) {
				unsigned char buf[4];
				readsl(base + MMCIFIFO, buf, 1);
				memcpy(ptr, buf, count);
			} else {
				readsl(base + MMCIFIFO, ptr, count >> 2);
				count &= ~0x3;
			}
		} else {
			readsl(base + MMCIFIFO, ptr, count >> 2);
		}

		ptr += count;
		remain -= count;
@@ -821,14 +868,13 @@ static int mmci_pio_write(struct mmci_host *host, char *buffer, unsigned int rem
		 */
		if (variant->sdio &&
		    mmc_card_sdio(host->mmc->card)) {
			u32 clk;
			if (count < 8)
				writel(readl(host->base + MMCICLOCK) &
					~variant->clkreg_enable,
					host->base + MMCICLOCK);
				clk = host->clk_reg & ~variant->clkreg_enable;
			else
				writel(readl(host->base + MMCICLOCK) |
					variant->clkreg_enable,
					host->base + MMCICLOCK);
				clk = host->clk_reg | variant->clkreg_enable;

			mmci_write_clkreg(host, clk);
		}

		/*
@@ -1015,10 +1061,17 @@ static void mmci_request(struct mmc_host *mmc, struct mmc_request *mrq)
static void mmci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
{
	struct mmci_host *host = mmc_priv(mmc);
	struct variant_data *variant = host->variant;
	u32 pwr = 0;
	unsigned long flags;
	int ret;

	pm_runtime_get_sync(mmc_dev(mmc));

	if (host->plat->ios_handler &&
		host->plat->ios_handler(mmc_dev(mmc), ios))
			dev_err(mmc_dev(mmc), "platform ios_handler failed\n");

	switch (ios->power_mode) {
	case MMC_POWER_OFF:
		if (host->vcc)
@@ -1035,22 +1088,38 @@ static void mmci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
				 * power should be rare so we print an error
				 * and return here.
				 */
				return;
				goto out;
			}
		}
		if (host->plat->vdd_handler)
			pwr |= host->plat->vdd_handler(mmc_dev(mmc), ios->vdd,
						       ios->power_mode);
		/* The ST version does not have this, fall through to POWER_ON */
		if (host->hw_designer != AMBA_VENDOR_ST) {
			pwr |= MCI_PWR_UP;
		/*
		 * The ST Micro variant doesn't have the PL180s MCI_PWR_UP
		 * and instead uses MCI_PWR_ON so apply whatever value is
		 * configured in the variant data.
		 */
		pwr |= variant->pwrreg_powerup;

		break;
		}
	case MMC_POWER_ON:
		pwr |= MCI_PWR_ON;
		break;
	}

	if (variant->signal_direction && ios->power_mode != MMC_POWER_OFF) {
		/*
		 * The ST Micro variant has some additional bits
		 * indicating signal direction for the signals in
		 * the SD/MMC bus and feedback-clock usage.
		 */
		pwr |= host->plat->sigdir;

		if (ios->bus_width == MMC_BUS_WIDTH_4)
			pwr &= ~MCI_ST_DATA74DIREN;
		else if (ios->bus_width == MMC_BUS_WIDTH_1)
			pwr &= (~MCI_ST_DATA74DIREN &
				~MCI_ST_DATA31DIREN &
				~MCI_ST_DATA2DIREN);
	}

	if (ios->bus_mode == MMC_BUSMODE_OPENDRAIN) {
		if (host->hw_designer != AMBA_VENDOR_ST)
			pwr |= MCI_ROD;
@@ -1066,13 +1135,13 @@ static void mmci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
	spin_lock_irqsave(&host->lock, flags);

	mmci_set_clkreg(host, ios->clock);

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

	spin_unlock_irqrestore(&host->lock, flags);

 out:
	pm_runtime_mark_last_busy(mmc_dev(mmc));
	pm_runtime_put_autosuspend(mmc_dev(mmc));
}

static int mmci_get_ro(struct mmc_host *mmc)
@@ -1345,6 +1414,8 @@ static int __devinit mmci_probe(struct amba_device *dev,

	mmci_dma_setup(host);

	pm_runtime_set_autosuspend_delay(&dev->dev, 50);
	pm_runtime_use_autosuspend(&dev->dev);
	pm_runtime_put(&dev->dev);

	mmc_add_host(mmc);
@@ -1429,43 +1500,49 @@ static int __devexit mmci_remove(struct amba_device *dev)
	return 0;
}

#ifdef CONFIG_PM
static int mmci_suspend(struct amba_device *dev, pm_message_t state)
#ifdef CONFIG_SUSPEND
static int mmci_suspend(struct device *dev)
{
	struct mmc_host *mmc = amba_get_drvdata(dev);
	struct amba_device *adev = to_amba_device(dev);
	struct mmc_host *mmc = amba_get_drvdata(adev);
	int ret = 0;

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

		ret = mmc_suspend_host(mmc);
		if (ret == 0)
		if (ret == 0) {
			pm_runtime_get_sync(dev);
			writel(0, host->base + MMCIMASK0);
		}
	}

	return ret;
}

static int mmci_resume(struct amba_device *dev)
static int mmci_resume(struct device *dev)
{
	struct mmc_host *mmc = amba_get_drvdata(dev);
	struct amba_device *adev = to_amba_device(dev);
	struct mmc_host *mmc = amba_get_drvdata(adev);
	int ret = 0;

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

		writel(MCI_IRQENABLE, host->base + MMCIMASK0);
		pm_runtime_put(dev);

		ret = mmc_resume_host(mmc);
	}

	return ret;
}
#else
#define mmci_suspend	NULL
#define mmci_resume	NULL
#endif

static const struct dev_pm_ops mmci_dev_pm_ops = {
	SET_SYSTEM_SLEEP_PM_OPS(mmci_suspend, mmci_resume)
};

static struct amba_id mmci_ids[] = {
	{
		.id	= 0x00041180,
@@ -1511,11 +1588,10 @@ MODULE_DEVICE_TABLE(amba, mmci_ids);
static struct amba_driver mmci_driver = {
	.drv		= {
		.name	= DRIVER_NAME,
		.pm	= &mmci_dev_pm_ops,
	},
	.probe		= mmci_probe,
	.remove		= __devexit_p(mmci_remove),
	.suspend	= mmci_suspend,
	.resume		= mmci_resume,
	.id_table	= mmci_ids,
};

+3 −12
Original line number Diff line number Diff line
@@ -13,16 +13,6 @@
#define MCI_PWR_ON		0x03
#define MCI_OD			(1 << 6)
#define MCI_ROD			(1 << 7)
/*
 * The ST Micro version does not have ROD and reuse the voltage registers
 * for direction settings
 */
#define MCI_ST_DATA2DIREN	(1 << 2)
#define MCI_ST_CMDDIREN		(1 << 3)
#define MCI_ST_DATA0DIREN	(1 << 4)
#define MCI_ST_DATA31DIREN	(1 << 5)
#define MCI_ST_FBCLKEN		(1 << 7)
#define MCI_ST_DATA74DIREN	(1 << 8)

#define MMCICLOCK		0x004
#define MCI_CLK_ENABLE		(1 << 8)
@@ -160,7 +150,7 @@
	(MCI_RXFIFOHALFFULLMASK | MCI_RXDATAAVLBLMASK | \
	 MCI_TXFIFOHALFEMPTYMASK)

#define NR_SG		16
#define NR_SG		128

struct clk;
struct variant_data;
@@ -189,7 +179,8 @@ struct mmci_host {

	unsigned int		mclk;
	unsigned int		cclk;
	u32			pwr;
	u32			pwr_reg;
	u32			clk_reg;
	struct mmci_platform_data *plat;
	struct variant_data	*variant;

+19 −3
Original line number Diff line number Diff line
@@ -6,6 +6,19 @@

#include <linux/mmc/host.h>


/*
 * These defines is places here due to access is needed from machine
 * configuration files. The ST Micro version does not have ROD and
 * reuse the voltage registers for direction settings.
 */
#define MCI_ST_DATA2DIREN	(1 << 2)
#define MCI_ST_CMDDIREN		(1 << 3)
#define MCI_ST_DATA0DIREN	(1 << 4)
#define MCI_ST_DATA31DIREN	(1 << 5)
#define MCI_ST_FBCLKEN		(1 << 7)
#define MCI_ST_DATA74DIREN	(1 << 8)

/* Just some dummy forwarding */
struct dma_chan;

@@ -18,7 +31,8 @@ struct dma_chan;
 * @ocr_mask: available voltages on the 4 pins from the block, this
 * is ignored if a regulator is used, see the MMC_VDD_* masks in
 * mmc/host.h
 * @vdd_handler: a callback function to translate a MMC_VDD_*
 * @ios_handler: a callback function to act on specfic ios changes,
 * used for example to control a levelshifter
 * mask into a value to be binary (or set some other custom bits
 * in MMCIPWR) or:ed and written into the MMCIPWR register of the
 * block.  May also control external power based on the power_mode.
@@ -31,6 +45,8 @@ struct dma_chan;
 * @capabilities: the capabilities of the block as implemented in
 * this platform, signify anything MMC_CAP_* from mmc/host.h
 * @capabilities2: more capabilities, MMC_CAP2_* from mmc/host.h
 * @sigdir: a bit field indicating for what bits in the MMC bus the host
 * should enable signal direction indication.
 * @dma_filter: function used to select an appropriate RX and TX
 * DMA channel to be used for DMA, if and only if you're deploying the
 * generic DMA engine
@@ -46,14 +62,14 @@ struct dma_chan;
struct mmci_platform_data {
	unsigned int f_max;
	unsigned int ocr_mask;
	u32 (*vdd_handler)(struct device *, unsigned int vdd,
			   unsigned char power_mode);
	int (*ios_handler)(struct device *, struct mmc_ios *);
	unsigned int (*status)(struct device *);
	int	gpio_wp;
	int	gpio_cd;
	bool	cd_invert;
	unsigned long capabilities;
	unsigned long capabilities2;
	u32 sigdir;
	bool (*dma_filter)(struct dma_chan *chan, void *filter_param);
	void *dma_rx_param;
	void *dma_tx_param;