/*
* linux/drivers/mmc/host/pxa.c - PXA MMCI driver
*
* Copyright (C) 2003 Russell King, 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
* published by the Free Software Foundation.
*
* This hardware is really sick:
* - No way to clear interrupts.
* - Have to turn off the clock whenever we touch the device.
* - Doesn't tell you how many data blocks were transferred.
* Yuck!
*
* 1 and 3 byte data transfers not supported
* max block length up to 1023
*/
#include <linux/module.h>
#include <linux/init.h>
#include <linux/ioport.h>
#include <linux/platform_device.h>
#include <linux/delay.h>
#include <linux/interrupt.h>
#include <linux/dma-mapping.h>
#include <linux/clk.h>
#include <linux/err.h>
#include <linux/mmc/host.h>
#include <linux/io.h>
#include <linux/regulator/consumer.h>
#include <linux/gpio.h>
#include <linux/gfp.h>
#include <asm/sizes.h>
#include <mach/hardware.h>
#include <mach/dma.h>
#include <mach/mmc.h>
#include "pxamci.h"
#define DRIVER_NAME "pxa2xx-mci"
#define NR_SG 1
#define CLKRT_OFF (~0)
#define mmc_has_26MHz() (cpu_is_pxa300() || cpu_is_pxa310() \
|| cpu_is_pxa935())
struct pxamci_host {
struct mmc_host *mmc;
spinlock_t lock;
struct resource *res;
void __iomem *base;
struct clk *clk;
unsigned long clkrate;
int irq;
int dma;
unsigned int clkrt;
unsigned int cmdat;
unsigned int imask;
unsigned int power_mode;
struct pxamci_platform_data *pdata;
struct mmc_request *mrq;
struct mmc_command *cmd;
struct mmc_data *data;
dma_addr_t sg_dma;
struct pxa_dma_desc *sg_cpu;
unsigned int dma_len;
unsigned int dma_dir;
unsigned int dma_drcmrrx;
unsigned int dma_drcmrtx;
struct regulator *vcc;
};
static inline void pxamci_init_ocr(struct pxamci_host *host)
{
#ifdef CONFIG_REGULATOR
host->vcc = regulator_get(mmc_dev(host->mmc), "vmmc");
if (IS_ERR(host->vcc))
host->vcc = NULL;
else {
host->mmc->ocr_avail = mmc_regulator_get_ocrmask(host->vcc);
if (host->pdata && host->pdata->ocr_mask)
dev_warn(mmc_dev(host->mmc),
"ocr_mask/setpower will not be used\n");
}
#endif
if (host->vcc == NULL) {
/* fall-back to platform data */
host->mmc->ocr_avail = host->pdata ?
host->pdata->ocr_mask :
MMC_VDD_32_33 | MMC_VDD_33_34;
}
}
static inline void pxamci_set_power(struct pxamci_host *host, unsigned int vdd)
{
int on;
#ifdef CONFIG_REGULATOR
if (host->vcc)
mmc_regulator_set_ocr(host->vcc, vdd);
#endif
if (!host->vcc && host->pdata &&
gpio_is_valid(host->pdata->gpio_power)) {
on = ((1 << vdd) & host->pdata->ocr_mask);
gpio_set_value(host->pdata->gpio_power,
!!on ^ host->pdata->gpio_power_invert);
}
if (!host->vcc && host->pdata && host->pdata->setpower)
host->pdata->setpower(mmc_dev(host->mmc), vdd);
}
static void pxamci_stop_clock(struct pxamci_host *host)
{
if (readl(host->base + MMC_STAT) & STAT_CLK_EN) {
unsigned long timeout = 10000;
unsigned int v;
writel(STOP_CLOCK, host->base + MMC_STRPCL);
do {
v = readl(host->base + MMC_STAT);
if (!(v & STAT_CLK_EN))
break;
udelay(1);
} while (timeout--);
if (v & STAT_CLK_EN)
dev_err(mmc_dev(host->mmc), "unable to stop clock\n");
}
}
static void pxamci_enable_irq(struct pxamci_host *host, unsigned int mask)
{
unsigned long flags;
spin_lock_irqsave(&host->lock, flags);
host->imask &= ~mask;
writel(host->imask, host->base + MMC_I_MASK);
spin_unlock_irqrestore(&host->lock, flags);
}
static void pxamci_disable_irq(struct pxamci_host *host, unsigned int mask)
{
unsigned long flags;
spin_lock_irqsave(&host->lock, flags);
host->imask |= mask;
writel(host->imask, host->base + MMC_I_MASK);
spin_unlock_irqrestore(&host->lock, flags);
}
static void pxamci_setup_data(struct pxamci_host *host, struct mmc_data *data)
{
unsigned int nob = data->blocks;
unsigned long long clks;
unsigned int timeout;
bool dalgn = 0;
u32 dcmd;
int i;
host->data = data;
if (data->flags & MMC_DATA_STREAM)
nob = 0xffff;
writel(nob, host->base + MMC_NOB);
writel(data->blksz, host->base + MMC_BLKLEN);
clks = <