Release 4.7 drivers/mmc/host/wmt-sdmmc.c
  
  
/*
 *  WM8505/WM8650 SD/MMC Host Controller
 *
 *  Copyright (C) 2010 Tony Prisk
 *  Copyright (C) 2008 WonderMedia Technologies, Inc.
 *
 *  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
 */
#include <linux/init.h>
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/ioport.h>
#include <linux/errno.h>
#include <linux/dma-mapping.h>
#include <linux/delay.h>
#include <linux/io.h>
#include <linux/irq.h>
#include <linux/clk.h>
#include <linux/gpio.h>
#include <linux/of.h>
#include <linux/of_address.h>
#include <linux/of_irq.h>
#include <linux/of_device.h>
#include <linux/mmc/host.h>
#include <linux/mmc/mmc.h>
#include <linux/mmc/sd.h>
#include <asm/byteorder.h>
#define DRIVER_NAME "wmt-sdhc"
/* MMC/SD controller registers */
#define SDMMC_CTLR			0x00
#define SDMMC_CMD			0x01
#define SDMMC_RSPTYPE			0x02
#define SDMMC_ARG			0x04
#define SDMMC_BUSMODE			0x08
#define SDMMC_BLKLEN			0x0C
#define SDMMC_BLKCNT			0x0E
#define SDMMC_RSP			0x10
#define SDMMC_CBCR			0x20
#define SDMMC_INTMASK0			0x24
#define SDMMC_INTMASK1			0x25
#define SDMMC_STS0			0x28
#define SDMMC_STS1			0x29
#define SDMMC_STS2			0x2A
#define SDMMC_STS3			0x2B
#define SDMMC_RSPTIMEOUT		0x2C
#define SDMMC_CLK			0x30	
/* VT8500 only */
#define SDMMC_EXTCTRL			0x34
#define SDMMC_SBLKLEN			0x38
#define SDMMC_DMATIMEOUT		0x3C
/* SDMMC_CTLR bit fields */
#define CTLR_CMD_START			0x01
#define CTLR_CMD_WRITE			0x04
#define CTLR_FIFO_RESET			0x08
/* SDMMC_BUSMODE bit fields */
#define BM_SPI_MODE			0x01
#define BM_FOURBIT_MODE			0x02
#define BM_EIGHTBIT_MODE		0x04
#define BM_SD_OFF			0x10
#define BM_SPI_CS			0x20
#define BM_SD_POWER			0x40
#define BM_SOFT_RESET			0x80
/* SDMMC_BLKLEN bit fields */
#define BLKL_CRCERR_ABORT		0x0800
#define BLKL_CD_POL_HIGH		0x1000
#define BLKL_GPI_CD			0x2000
#define BLKL_DATA3_CD			0x4000
#define BLKL_INT_ENABLE			0x8000
/* SDMMC_INTMASK0 bit fields */
#define INT0_MBLK_TRAN_DONE_INT_EN	0x10
#define INT0_BLK_TRAN_DONE_INT_EN	0x20
#define INT0_CD_INT_EN			0x40
#define INT0_DI_INT_EN			0x80
/* SDMMC_INTMASK1 bit fields */
#define INT1_CMD_RES_TRAN_DONE_INT_EN	0x02
#define INT1_CMD_RES_TOUT_INT_EN	0x04
#define INT1_MBLK_AUTO_STOP_INT_EN	0x08
#define INT1_DATA_TOUT_INT_EN		0x10
#define INT1_RESCRC_ERR_INT_EN		0x20
#define INT1_RCRC_ERR_INT_EN		0x40
#define INT1_WCRC_ERR_INT_EN		0x80
/* SDMMC_STS0 bit fields */
#define STS0_WRITE_PROTECT		0x02
#define STS0_CD_DATA3			0x04
#define STS0_CD_GPI			0x08
#define STS0_MBLK_DONE			0x10
#define STS0_BLK_DONE			0x20
#define STS0_CARD_DETECT		0x40
#define STS0_DEVICE_INS			0x80
/* SDMMC_STS1 bit fields */
#define STS1_SDIO_INT			0x01
#define STS1_CMDRSP_DONE		0x02
#define STS1_RSP_TIMEOUT		0x04
#define STS1_AUTOSTOP_DONE		0x08
#define STS1_DATA_TIMEOUT		0x10
#define STS1_RSP_CRC_ERR		0x20
#define STS1_RCRC_ERR			0x40
#define STS1_WCRC_ERR			0x80
/* SDMMC_STS2 bit fields */
#define STS2_CMD_RES_BUSY		0x10
#define STS2_DATARSP_BUSY		0x20
#define STS2_DIS_FORCECLK		0x80
/* SDMMC_EXTCTRL bit fields */
#define EXT_EIGHTBIT			0x04
/* MMC/SD DMA Controller Registers */
#define SDDMA_GCR			0x100
#define SDDMA_IER			0x104
#define SDDMA_ISR			0x108
#define SDDMA_DESPR			0x10C
#define SDDMA_RBR			0x110
#define SDDMA_DAR			0x114
#define SDDMA_BAR			0x118
#define SDDMA_CPR			0x11C
#define SDDMA_CCR			0x120
/* SDDMA_GCR bit fields */
#define DMA_GCR_DMA_EN			0x00000001
#define DMA_GCR_SOFT_RESET		0x00000100
/* SDDMA_IER bit fields */
#define DMA_IER_INT_EN			0x00000001
/* SDDMA_ISR bit fields */
#define DMA_ISR_INT_STS			0x00000001
/* SDDMA_RBR bit fields */
#define DMA_RBR_FORMAT			0x40000000
#define DMA_RBR_END			0x80000000
/* SDDMA_CCR bit fields */
#define DMA_CCR_RUN			0x00000080
#define DMA_CCR_IF_TO_PERIPHERAL	0x00000000
#define DMA_CCR_PERIPHERAL_TO_IF	0x00400000
/* SDDMA_CCR event status */
#define DMA_CCR_EVT_NO_STATUS		0x00000000
#define DMA_CCR_EVT_UNDERRUN		0x00000001
#define DMA_CCR_EVT_OVERRUN		0x00000002
#define DMA_CCR_EVT_DESP_READ		0x00000003
#define DMA_CCR_EVT_DATA_RW		0x00000004
#define DMA_CCR_EVT_EARLY_END		0x00000005
#define DMA_CCR_EVT_SUCCESS		0x0000000F
#define PDMA_READ			0x00
#define PDMA_WRITE			0x01
#define WMT_SD_POWER_OFF		0
#define WMT_SD_POWER_ON			1
struct wmt_dma_descriptor {
	
u32 flags;
	
u32 data_buffer_addr;
	
u32 branch_addr;
	
u32 reserved1;
};
struct wmt_mci_caps {
	
unsigned int	f_min;
	
unsigned int	f_max;
	
u32		ocr_avail;
	
u32		caps;
	
u32		max_seg_size;
	
u32		max_segs;
	
u32		max_blk_size;
};
struct wmt_mci_priv {
	
struct mmc_host *mmc;
	
void __iomem *sdmmc_base;
	
int irq_regular;
	
int irq_dma;
	
void *dma_desc_buffer;
	
dma_addr_t dma_desc_device_addr;
	
struct completion cmdcomp;
	
struct completion datacomp;
	
struct completion *comp_cmd;
	
struct completion *comp_dma;
	
struct mmc_request *req;
	
struct mmc_command *cmd;
	
struct clk *clk_sdmmc;
	
struct device *dev;
	
u8 power_inverted;
	
u8 cd_inverted;
};
static void wmt_set_sd_power(struct wmt_mci_priv *priv, int enable)
{
	u32 reg_tmp = readb(priv->sdmmc_base + SDMMC_BUSMODE);
	if (enable ^ priv->power_inverted)
		reg_tmp &= ~BM_SD_OFF;
	else
		reg_tmp |= BM_SD_OFF;
	writeb(reg_tmp, priv->sdmmc_base + SDMMC_BUSMODE);
}
Contributors
 | Person | Tokens | Prop | Commits | CommitProp | 
| tony prisk | tony prisk | 48 | 87.27% | 1 | 50.00% | 
| axel lin | axel lin | 7 | 12.73% | 1 | 50.00% | 
 | Total | 55 | 100.00% | 2 | 100.00% | 
static void wmt_mci_read_response(struct mmc_host *mmc)
{
	struct wmt_mci_priv *priv;
	int idx1, idx2;
	u8 tmp_resp;
	u32 response;
	priv = mmc_priv(mmc);
	for (idx1 = 0; idx1 < 4; idx1++) {
		response = 0;
		for (idx2 = 0; idx2 < 4; idx2++) {
			if ((idx1 == 3) && (idx2 == 3))
				tmp_resp = readb(priv->sdmmc_base + SDMMC_RSP);
			else
				tmp_resp = readb(priv->sdmmc_base + SDMMC_RSP +
						 (idx1*4) + idx2 + 1);
			response |= (tmp_resp << (idx2 * 8));
		}
		priv->cmd->resp[idx1] = cpu_to_be32(response);
	}
}
Contributors
 | Person | Tokens | Prop | Commits | CommitProp | 
| tony prisk | tony prisk | 141 | 100.00% | 1 | 100.00% | 
 | Total | 141 | 100.00% | 1 | 100.00% | 
static void wmt_mci_start_command(struct wmt_mci_priv *priv)
{
	u32 reg_tmp;
	reg_tmp = readb(priv->sdmmc_base + SDMMC_CTLR);
	writeb(reg_tmp | CTLR_CMD_START, priv->sdmmc_base + SDMMC_CTLR);
}
Contributors
 | Person | Tokens | Prop | Commits | CommitProp | 
| tony prisk | tony prisk | 38 | 100.00% | 1 | 100.00% | 
 | Total | 38 | 100.00% | 1 | 100.00% | 
static int wmt_mci_send_command(struct mmc_host *mmc, u8 command, u8 cmdtype,
				u32 arg, u8 rsptype)
{
	struct wmt_mci_priv *priv;
	u32 reg_tmp;
	priv = mmc_priv(mmc);
	/* write command, arg, resptype registers */
	writeb(command, priv->sdmmc_base + SDMMC_CMD);
	writel(arg, priv->sdmmc_base + SDMMC_ARG);
	writeb(rsptype, priv->sdmmc_base + SDMMC_RSPTYPE);
	/* reset response FIFO */
	reg_tmp = readb(priv->sdmmc_base + SDMMC_CTLR);
	writeb(reg_tmp | CTLR_FIFO_RESET, priv->sdmmc_base + SDMMC_CTLR);
	/* ensure clock enabled - VT3465 */
	wmt_set_sd_power(priv, WMT_SD_POWER_ON);
	/* clear status bits */
	writeb(0xFF, priv->sdmmc_base + SDMMC_STS0);
	writeb(0xFF, priv->sdmmc_base + SDMMC_STS1);
	writeb(0xFF, priv->sdmmc_base + SDMMC_STS2);
	writeb(0xFF, priv->sdmmc_base + SDMMC_STS3);
	/* set command type */
	reg_tmp = readb(priv->sdmmc_base + SDMMC_CTLR);
	writeb((reg_tmp & 0x0F) | (cmdtype << 4),
	       priv->sdmmc_base + SDMMC_CTLR);
	return 0;
}
Contributors
 | Person | Tokens | Prop | Commits | CommitProp | 
| tony prisk | tony prisk | 186 | 100.00% | 1 | 100.00% | 
 | Total | 186 | 100.00% | 1 | 100.00% | 
static void wmt_mci_disable_dma(struct wmt_mci_priv *priv)
{
	writel(DMA_ISR_INT_STS, priv->sdmmc_base + SDDMA_ISR);
	writel(0, priv->sdmmc_base + SDDMA_IER);
}
Contributors
 | Person | Tokens | Prop | Commits | CommitProp | 
| tony prisk | tony prisk | 33 | 100.00% | 1 | 100.00% | 
 | Total | 33 | 100.00% | 1 | 100.00% | 
static void wmt_complete_data_request(struct wmt_mci_priv *priv)
{
	struct mmc_request *req;
	req = priv->req;
	req->data->bytes_xfered = req->data->blksz * req->data->blocks;
	/* unmap the DMA pages used for write data */
	if (req->data->flags & MMC_DATA_WRITE)
		dma_unmap_sg(mmc_dev(priv->mmc), req->data->sg,
			     req->data->sg_len, DMA_TO_DEVICE);
	else
		dma_unmap_sg(mmc_dev(priv->mmc), req->data->sg,
			     req->data->sg_len, DMA_FROM_DEVICE);
	/* Check if the DMA ISR returned a data error */
	if ((req->cmd->error) || (req->data->error))
		mmc_request_done(priv->mmc, req);
	else {
		wmt_mci_read_response(priv->mmc);
		if (!req->data->stop) {
			/* single-block read/write requests end here */
			mmc_request_done(priv->mmc, req);
		} else {
			/*
                         * we change the priv->cmd variable so the response is
                         * stored in the stop struct rather than the original
                         * calling command struct
                         */
			priv->comp_cmd = &priv->cmdcomp;
			init_completion(priv->comp_cmd);
			priv->cmd = req->data->stop;
			wmt_mci_send_command(priv->mmc, req->data->stop->opcode,
					     7, req->data->stop->arg, 9);
			wmt_mci_start_command(priv);
		}
	}
}
Contributors
 | Person | Tokens | Prop | Commits | CommitProp | 
| tony prisk | tony prisk | 221 | 100.00% | 1 | 100.00% | 
 | Total | 221 | 100.00% | 1 | 100.00% | 
static irqreturn_t wmt_mci_dma_isr(int irq_num, void *data)
{
	struct wmt_mci_priv *priv;
	int status;
	priv = (struct wmt_mci_priv *)data;
	status = readl(priv->sdmmc_base + SDDMA_CCR) & 0x0F;
	if (status != DMA_CCR_EVT_SUCCESS) {
		dev_err(priv->dev, "DMA Error: Status = %d\n", status);
		priv->req->data->error = -ETIMEDOUT;
		complete(priv->comp_dma);
		return IRQ_HANDLED;
	}
	priv->req->data->error = 0;
	wmt_mci_disable_dma(priv);
	complete(priv->comp_dma);
	if (priv->comp_cmd) {
		if (completion_done(priv->comp_cmd)) {
			/*
                         * if the command (regular) interrupt has already
                         * completed, finish off the request otherwise we wait
                         * for the command interrupt and finish from there.
                         */
			wmt_complete_data_request(priv);
		}
	}
	return IRQ_HANDLED;
}
Contributors
 | Person | Tokens | Prop | Commits | CommitProp | 
| tony prisk | tony prisk | 133 | 100.00% | 1 | 100.00% | 
 | Total | 133 | 100.00% | 1 | 100.00% | 
static irqreturn_t wmt_mci_regular_isr(int irq_num, void *data)
{
	struct wmt_mci_priv *priv;
	u32 status0;
	u32 status1;
	u32 status2;
	u32 reg_tmp;
	int cmd_done;
	priv = (struct wmt_mci_priv *)data;
	cmd_done = 0;
	status0 = readb(priv->sdmmc_base + SDMMC_STS0);
	status1 = readb(priv->sdmmc_base + SDMMC_STS1);
	status2 = readb(priv->sdmmc_base + SDMMC_STS2);
	/* Check for card insertion */
	reg_tmp = readb(priv->sdmmc_base + SDMMC_INTMASK0);
	if ((reg_tmp & INT0_DI_INT_EN) && (status0 & STS0_DEVICE_INS)) {
		mmc_detect_change(priv->mmc, 0);
		if (priv->cmd)
			priv->cmd->error = -ETIMEDOUT;
		if (priv->comp_cmd)
			complete(priv->comp_cmd);
		if (priv->comp_dma) {
			wmt_mci_disable_dma(priv);
			complete(priv->comp_dma);
		}
		writeb(STS0_DEVICE_INS, priv->sdmmc_base + SDMMC_STS0);
		return IRQ_HANDLED;
	}
	if ((!priv->req->data) ||
	    ((priv->req->data->stop) && (priv->cmd == priv->req->data->stop))) {
		/* handle non-data & stop_transmission requests */
		if (status1 & STS1_CMDRSP_DONE) {
			priv->cmd->error = 0;
			cmd_done = 1;
		} else if ((status1 & STS1_RSP_TIMEOUT) ||
			   (status1 & STS1_DATA_TIMEOUT)) {
			priv->cmd->error = -ETIMEDOUT;
			cmd_done = 1;
		}
		if (cmd_done) {
			priv->comp_cmd = NULL;
			if (!priv->cmd->error)
				wmt_mci_read_response(priv->mmc);
			priv->cmd = NULL;
			mmc_request_done(priv->mmc, priv->req);
		}
	} else {
		/* handle data requests */
		if (status1 & STS1_CMDRSP_DONE) {
			if (priv->cmd)
				priv->cmd->error = 0;
			if (priv->comp_cmd)
				complete(priv->comp_cmd);
		}
		if ((status1 & STS1_RSP_TIMEOUT) ||
		    (status1 & STS1_DATA_TIMEOUT)) {
			if (priv->cmd)
				priv->cmd->error = -ETIMEDOUT;
			if (priv->comp_cmd)
				complete(priv->comp_cmd);
			if (priv->comp_dma) {
				wmt_mci_disable_dma(priv);
				complete(priv->comp_dma);
			}
		}
		if (priv->comp_dma) {
			/*
                         * If the dma interrupt has already completed, finish
                         * off the request; otherwise we wait for the DMA
                         * interrupt and finish from there.
                         */
			if (completion_done(priv->comp_dma))
				wmt_complete_data_request(priv);
		}
	}
	writeb(status0, priv->sdmmc_base + SDMMC_STS0);
	writeb(status1, priv->sdmmc_base + SDMMC_STS1);
	writeb(status2, priv->sdmmc_base + SDMMC_STS2);
	return IRQ_HANDLED;
}
Contributors
 | Person | Tokens | Prop | Commits | CommitProp | 
| tony prisk | tony prisk | 475 | 100.00% | 1 | 100.00% | 
 | Total | 475 | 100.00% | 1 | 100.00% | 
static void wmt_reset_hardware(struct mmc_host *mmc)
{
	struct wmt_mci_priv *priv;
	u32 reg_tmp;
	priv = mmc_priv(mmc);
	/* reset controller */
	reg_tmp = readb(priv->sdmmc_base + SDMMC_BUSMODE);
	writeb(reg_tmp | BM_SOFT_RESET, priv->sdmmc_base + SDMMC_BUSMODE);
	/* reset response FIFO */
	reg_tmp = readb(priv->sdmmc_base + SDMMC_CTLR);
	writeb(reg_tmp | CTLR_FIFO_RESET, priv->sdmmc_base + SDMMC_CTLR);
	/* enable GPI pin to detect card */
	writew(BLKL_INT_ENABLE | BLKL_GPI_CD, priv->sdmmc_base + SDMMC_BLKLEN);
	/* clear interrupt status */
	writeb(0xFF, priv->sdmmc_base + SDMMC_STS0);
	writeb(0xFF, priv->sdmmc_base + SDMMC_STS1);
	/* setup interrupts */
	writeb(INT0_CD_INT_EN | INT0_DI_INT_EN, priv->sdmmc_base +
	       SDMMC_INTMASK0);
	writeb(INT1_DATA_TOUT_INT_EN | INT1_CMD_RES_TRAN_DONE_INT_EN |
	       INT1_CMD_RES_TOUT_INT_EN, priv->sdmmc_base + SDMMC_INTMASK1);
	/* set the DMA timeout */
	writew(8191, priv->sdmmc_base + SDMMC_DMATIMEOUT);
	/* auto clock freezing enable */
	reg_tmp = readb(priv->sdmmc_base + SDMMC_STS2);
	writeb(reg_tmp | STS2_DIS_FORCECLK, priv->sdmmc_base + SDMMC_STS2);
	/* set a default clock speed of 400Khz */
	clk_set_rate(priv->clk_sdmmc, 400000);
}
Contributors
 | Person | Tokens | Prop | Commits | CommitProp | 
| tony prisk | tony prisk | 189 | 100.00% | 1 | 100.00% | 
 | Total | 189 | 100.00% | 1 | 100.00% | 
static int wmt_dma_init(struct mmc_host *mmc)
{
	struct wmt_mci_priv *priv;
	priv = mmc_priv(mmc);
	writel(DMA_GCR_SOFT_RESET, priv->sdmmc_base + SDDMA_GCR);
	writel(DMA_GCR_DMA_EN, priv->sdmmc_base + SDDMA_GCR);
	if ((readl(priv->sdmmc_base + SDDMA_GCR) & DMA_GCR_DMA_EN) != 0)
		return 0;
	else
		return 1;
}
Contributors
 | Person | Tokens | Prop | Commits | CommitProp | 
| tony prisk | tony prisk | 69 | 100.00% | 1 | 100.00% | 
 | Total | 69 | 100.00% | 1 | 100.00% | 
static void wmt_dma_init_descriptor(struct wmt_dma_descriptor *desc,
		u16 req_count, u32 buffer_addr, u32 branch_addr, int end)
{
	desc->flags = 0x40000000 | req_count;
	if (end)
		desc->flags |= 0x80000000;
	desc->data_buffer_addr = buffer_addr;
	desc->branch_addr = branch_addr;
}
Contributors
 | Person | Tokens | Prop | Commits | CommitProp | 
| tony prisk | tony prisk | 53 | 100.00% | 1 | 100.00% | 
 | Total | 53 | 100.00% | 1 | 100.00% | 
static void wmt_dma_config(struct mmc_host *mmc, u32 descaddr, u8 dir)
{
	struct wmt_mci_priv *priv;
	u32 reg_tmp;
	priv = mmc_priv(mmc);
	/* Enable DMA Interrupts */
	writel(DMA_IER_INT_EN, priv->sdmmc_base + SDDMA_IER);
	/* Write DMA Descriptor Pointer Register */
	writel(descaddr, priv->sdmmc_base + SDDMA_DESPR);
	writel(0x00, priv->sdmmc_base + SDDMA_CCR);
	if (dir == PDMA_WRITE) {
		reg_tmp = readl(priv->sdmmc_base + SDDMA_CCR);
		writel(reg_tmp & DMA_CCR_IF_TO_PERIPHERAL, priv->sdmmc_base +
		       SDDMA_CCR);
	} else {
		reg_tmp = readl(priv->sdmmc_base + SDDMA_CCR);
		writel(reg_tmp | DMA_CCR_PERIPHERAL_TO_IF, priv->sdmmc_base +
		       SDDMA_CCR);
	}
}
Contributors
 | Person | Tokens | Prop | Commits | CommitProp | 
| tony prisk | tony prisk | 126 | 100.00% | 1 | 100.00% | 
 | Total | 126 | 100.00% | 1 | 100.00% | 
static void wmt_dma_start(struct wmt_mci_priv *priv)
{
	u32 reg_tmp;
	reg_tmp = readl(priv->sdmmc_base + SDDMA_CCR);
	writel(reg_tmp | DMA_CCR_RUN, priv->sdmmc_base + SDDMA_CCR);
}
Contributors
 | Person | Tokens | Prop | Commits | CommitProp | 
| tony prisk | tony prisk | 38 | 100.00% | 1 | 100.00% | 
 | Total | 38 | 100.00% | 1 | 100.00% | 
static void wmt_mci_request(struct mmc_host *mmc, struct mmc_request *req)
{
	struct wmt_mci_priv *priv;
	struct wmt_dma_descriptor *desc;
	u8 command;
	u8 cmdtype;
	u32 arg;
	u8 rsptype;
	u32 reg_tmp;
	struct scatterlist *sg;
	int i;
	int sg_cnt;
	int offset;
	u32 dma_address;
	int desc_cnt;
	priv = mmc_priv(mmc);
	priv->req = req;
	/*
         * Use the cmd variable to pass a pointer to the resp[] structure
         * This is required on multi-block requests to pass the pointer to the
         * stop command
         */
	priv->cmd = req->cmd;
	command = req->cmd->opcode;
	arg = req->cmd->arg;
	rsptype = mmc_resp_type(req->cmd);
	cmdtype = 0;
	/* rsptype=7 only valid for SPI commands - should be =2 for SD */
	if (rsptype == 7)
		rsptype = 2;
	/* rsptype=21 is R1B, convert for controller */
	if (rsptype == 21)
		rsptype = 9;
	if (!req->data) {
		wmt_mci_send_command(mmc, command, cmdtype, arg, rsptype);
		wmt_mci_start_command(priv);
		/* completion is now handled in the regular_isr() */
	}
	if (req->data) {
		priv->comp_cmd = &priv->cmdcomp;
		init_completion(priv->comp_cmd);
		wmt_dma_init(mmc);
		/* set controller data length */
		reg_tmp = readw(priv->sdmmc_base + SDMMC_BLKLEN);
		writew((reg_tmp & 0xF800) | (req->data->blksz - 1),
		       priv->sdmmc_base + SDMMC_BLKLEN);
		/* set controller block count */
		writew(req->data->blocks, priv->sdmmc_base + SDMMC_BLKCNT);
		desc = (struct wmt_dma_descriptor *)priv->dma_desc_buffer;
		if (req->data->flags & MMC_DATA_WRITE) {
			sg_cnt = dma_map_sg(mmc_dev(mmc), req->data->sg,
					    req->data->sg_len, DMA_TO_DEVICE);
			cmdtype = 1;
			if (req->data->blocks > 1)
				cmdtype = 3;
		} else {
			sg_cnt = dma_map_sg(mmc_dev(mmc), req->data->sg,
					    req->data->sg_len, DMA_FROM_DEVICE);
			cmdtype = 2;
			if (req->data->blocks > 1)
				cmdtype = 4;
		}
		dma_address = priv->dma_desc_device_addr + 16;
		desc_cnt = 0;
		for_each_sg(req->data->sg, sg, sg_cnt, i) {
			offset = 0;
			while (offset < sg_dma_len(sg)) {
				wmt_dma_init_descriptor(desc, req->data->blksz,
						sg_dma_address(sg)+offset,
						dma_address, 0);
				desc++;
				desc_cnt++;
				offset += req->data->blksz;
				dma_address += 16;
				if (desc_cnt == req->data->blocks)
					break;
			}
		}
		desc--;
		desc->flags |= 0x80000000;
		if (req->data->flags & MMC_DATA_WRITE)
			wmt_dma_config(mmc, priv->dma_desc_device_addr,
				       PDMA_WRITE);
		else
			wmt_dma_config(mmc, priv->dma_desc_device_addr,
				       PDMA_READ);
		wmt_mci_send_command(mmc, command, cmdtype, arg, rsptype);
		priv->comp_dma = &priv->datacomp;
		init_completion(priv->comp_dma);
		wmt_dma_start(priv);
		wmt_mci_start_command(priv);
	}
}
Contributors
 | Person | Tokens | Prop | Commits | CommitProp | 
| tony prisk | tony prisk | 525 | 100.00% | 1 | 100.00% | 
 | Total | 525 | 100.00% | 1 | 100.00% | 
static void wmt_mci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
{
	struct wmt_mci_priv *priv;
	u32 busmode, extctrl;
	priv = mmc_priv(mmc);
	if (ios->power_mode == MMC_POWER_UP) {
		wmt_reset_hardware(mmc);
		wmt_set_sd_power(priv, WMT_SD_POWER_ON);
	}
	if (ios->power_mode == MMC_POWER_OFF)
		wmt_set_sd_power(priv, WMT_SD_POWER_OFF);
	if (ios->clock != 0)
		clk_set_rate(priv->clk_sdmmc, ios->clock);
	busmode = readb(priv->sdmmc_base + SDMMC_BUSMODE);
	extctrl = readb(priv->sdmmc_base + SDMMC_EXTCTRL);
	busmode &= ~(BM_EIGHTBIT_MODE | BM_FOURBIT_MODE);
	extctrl &= ~EXT_EIGHTBIT;
	switch (ios->bus_width) {
	case MMC_BUS_WIDTH_8:
		busmode |= BM_EIGHTBIT_MODE;
		extctrl |= EXT_EIGHTBIT;
		break;
	case MMC_BUS_WIDTH_4:
		busmode |= BM_FOURBIT_MODE;
		break;
	case MMC_BUS_WIDTH_1:
		break;
	}
	writeb(busmode, priv->sdmmc_base + SDMMC_BUSMODE);
	writeb(extctrl, priv->sdmmc_base + SDMMC_EXTCTRL);
}
Contributors
 | Person | Tokens | Prop | Commits | CommitProp | 
| tony prisk | tony prisk | 134 | 74.86% | 1 | 50.00% | 
| axel lin | axel lin | 45 | 25.14% | 1 | 50.00% | 
 | Total | 179 | 100.00% | 2 | 100.00% | 
static int wmt_mci_get_ro(struct mmc_host *mmc)
{
	struct wmt_mci_priv *priv = mmc_priv(mmc);
	return !(readb(priv->sdmmc_base + SDMMC_STS0) & STS0_WRITE_PROTECT);
}
Contributors
 | Person | Tokens | Prop | Commits | CommitProp | 
| tony prisk | tony prisk | 36 | 100.00% | 1 | 100.00% | 
 | Total | 36 | 100.00% | 1 | 100.00% | 
static int wmt_mci_get_cd(struct mmc_host *mmc)
{
	struct wmt_mci_priv *priv = mmc_priv(mmc);
	u32 cd = (readb(priv->sdmmc_base + SDMMC_STS0) & STS0_CD_GPI) >> 3;
	return !(cd ^ priv->cd_inverted);
}
Contributors
 | Person | Tokens | Prop | Commits | CommitProp | 
| tony prisk | tony prisk | 49 | 100.00% | 1 | 100.00% | 
 | Total | 49 | 100.00% | 1 | 100.00% | 
static struct mmc_host_ops wmt_mci_ops = {
	.request = wmt_mci_request,
	.set_ios = wmt_mci_set_ios,
	.get_ro = wmt_mci_get_ro,
	.get_cd = wmt_mci_get_cd,
};
/* Controller capabilities */
static struct wmt_mci_caps wm8505_caps = {
	.f_min = 390425,
	.f_max = 50000000,
	.ocr_avail = MMC_VDD_32_33 | MMC_VDD_33_34,
	.caps = MMC_CAP_4_BIT_DATA | MMC_CAP_MMC_HIGHSPEED |
		MMC_CAP_SD_HIGHSPEED,
	.max_seg_size = 65024,
	.max_segs = 128,
	.max_blk_size = 2048,
};
static const struct of_device_id wmt_mci_dt_ids[] = {
	{ .compatible = "wm,wm8505-sdhc", .data = &wm8505_caps },
	{ /* Sentinel */ },
};
static int wmt_mci_probe(struct platform_device *pdev)
{
	struct mmc_host *mmc;
	struct wmt_mci_priv *priv;
	struct device_node *np = pdev->dev.of_node;
	const struct of_device_id *of_id =
		of_match_device(wmt_mci_dt_ids, &pdev->dev);
	const struct wmt_mci_caps *wmt_caps;
	int ret;
	int regular_irq, dma_irq;
	if (!of_id || !of_id->data) {
		dev_err(&pdev->dev, "Controller capabilities data missing\n");
		return -EFAULT;
	}
	wmt_caps = of_id->data;
	if (!np) {
		dev_err(&pdev->dev, "Missing SDMMC description in devicetree\n");
		return -EFAULT;
	}
	regular_irq = irq_of_parse_and_map(np, 0);
	dma_irq = irq_of_parse_and_map(np, 1);
	if (!regular_irq || !dma_irq) {
		dev_err(&pdev->dev, "Getting IRQs failed!\n");
		ret = -ENXIO;
		goto fail1;
	}
	mmc = mmc_alloc_host(sizeof(struct wmt_mci_priv), &pdev->dev);
	if (!mmc) {
		dev_err(&pdev->dev, "Failed to allocate mmc_host\n");
		ret = -ENOMEM;
		goto fail1;
	}
	mmc->ops = &wmt_mci_ops;
	mmc->f_min = wmt_caps->f_min;
	mmc->f_max = wmt_caps->f_max;
	mmc->ocr_avail = wmt_caps->ocr_avail;
	mmc->caps = wmt_caps->caps;
	mmc->max_seg_size = wmt_caps->max_seg_size;
	mmc->max_segs = wmt_caps->max_segs;
	mmc->max_blk_size = wmt_caps->max_blk_size;
	mmc->max_req_size = (16*512*mmc->max_segs);
	mmc->max_blk_count = mmc->max_req_size / 512;
	priv = mmc_priv(mmc);
	priv->mmc = mmc;
	priv->dev = &pdev->dev;
	priv->power_inverted = 0;
	priv->cd_inverted = 0;
	if (of_get_property(np, "sdon-inverted", NULL))
		priv->power_inverted = 1;
	if (of_get_property(np, "cd-inverted", NULL))
		priv->cd_inverted = 1;
	priv->sdmmc_base = of_iomap(np, 0);
	if (!priv->sdmmc_base) {
		dev_err(&pdev->dev, "Failed to map IO space\n");
		ret = -ENOMEM;
		goto fail2;
	}
	priv->irq_regular = regular_irq;
	priv->irq_dma = dma_irq;
	ret = request_irq(regular_irq, wmt_mci_regular_isr, 0, "sdmmc", priv);
	if (ret) {
		dev_err(&pdev->dev, "Register regular IRQ fail\n");
		goto fail3;
	}
	ret = request_irq(dma_irq, wmt_mci_dma_isr, 0, "sdmmc", priv);
	if (ret) {
		dev_err(&pdev->dev, "Register DMA IRQ fail\n");
		goto fail4;
	}
	/* alloc some DMA buffers for descriptors/transfers */
	priv->dma_desc_buffer = dma_alloc_coherent(&pdev->dev,
						   mmc->max_blk_count * 16,
						   &priv->dma_desc_device_addr,
						   GFP_KERNEL);
	if (!priv->dma_desc_buffer) {
		dev_err(&pdev->dev, "DMA alloc fail\n");
		ret = -EPERM;
		goto fail5;
	}
	platform_set_drvdata(pdev, mmc);
	priv->clk_sdmmc = of_clk_get(np, 0);
	if (IS_ERR(priv->clk_sdmmc)) {
		dev_err(&pdev->dev, "Error getting clock\n");
		ret = PTR_ERR(priv->clk_sdmmc);
		goto fail5;
	}
	clk_prepare_enable(priv->clk_sdmmc);
	/* configure the controller to a known 'ready' state */
	wmt_reset_hardware(mmc);
	mmc_add_host(mmc);
	dev_info(&pdev->dev, "WMT SDHC Controller initialized\n");
	return 0;
fail5:
	free_irq(dma_irq, priv);
fail4:
	free_irq(regular_irq, priv);
fail3:
	iounmap(priv->sdmmc_base);
fail2:
	mmc_free_host(mmc);
fail1:
	return ret;
}
Contributors
 | Person | Tokens | Prop | Commits | CommitProp | 
| tony prisk | tony prisk | 641 | 98.77% | 1 | 25.00% | 
| sachin kamat | sachin kamat | 6 | 0.92% | 1 | 25.00% | 
| axel lin | axel lin | 2 | 0.31% | 2 | 50.00% | 
 | Total | 649 | 100.00% | 4 | 100.00% | 
static int wmt_mci_remove(struct platform_device *pdev)
{
	struct mmc_host *mmc;
	struct wmt_mci_priv *priv;
	struct resource *res;
	u32 reg_tmp;
	mmc = platform_get_drvdata(pdev);
	priv = mmc_priv(mmc);
	/* reset SD controller */
	reg_tmp = readb(priv->sdmmc_base + SDMMC_BUSMODE);
	writel(reg_tmp | BM_SOFT_RESET, priv->sdmmc_base + SDMMC_BUSMODE);
	reg_tmp = readw(priv->sdmmc_base + SDMMC_BLKLEN);
	writew(reg_tmp & ~(0xA000), priv->sdmmc_base + SDMMC_BLKLEN);
	writeb(0xFF, priv->sdmmc_base + SDMMC_STS0);
	writeb(0xFF, priv->sdmmc_base + SDMMC_STS1);
	/* release the dma buffers */
	dma_free_coherent(&pdev->dev, priv->mmc->max_blk_count * 16,
			  priv->dma_desc_buffer, priv->dma_desc_device_addr);
	mmc_remove_host(mmc);
	free_irq(priv->irq_regular, priv);
	free_irq(priv->irq_dma, priv);
	iounmap(priv->sdmmc_base);
	clk_disable_unprepare(priv->clk_sdmmc);
	clk_put(priv->clk_sdmmc);
	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
	release_mem_region(res->start, resource_size(res));
	mmc_free_host(mmc);
	dev_info(&pdev->dev, "WMT MCI device removed\n");
	return 0;
}
Contributors
 | Person | Tokens | Prop | Commits | CommitProp | 
| tony prisk | tony prisk | 224 | 98.68% | 1 | 50.00% | 
| alexandru gheorghiu | alexandru gheorghiu | 3 | 1.32% | 1 | 50.00% | 
 | Total | 227 | 100.00% | 2 | 100.00% | 
#ifdef CONFIG_PM
static int wmt_mci_suspend(struct device *dev)
{
	u32 reg_tmp;
	struct platform_device *pdev = to_platform_device(dev);
	struct mmc_host *mmc = platform_get_drvdata(pdev);
	struct wmt_mci_priv *priv;
	if (!mmc)
		return 0;
	priv = mmc_priv(mmc);
	reg_tmp = readb(priv->sdmmc_base + SDMMC_BUSMODE);
	writeb(reg_tmp | BM_SOFT_RESET, priv->sdmmc_base +
	       SDMMC_BUSMODE);
	reg_tmp = readw(priv->sdmmc_base + SDMMC_BLKLEN);
	writew(reg_tmp & 0x5FFF, priv->sdmmc_base + SDMMC_BLKLEN);
	writeb(0xFF, priv->sdmmc_base + SDMMC_STS0);
	writeb(0xFF, priv->sdmmc_base + SDMMC_STS1);
	clk_disable(priv->clk_sdmmc);
	return 0;
}
Contributors
 | Person | Tokens | Prop | Commits | CommitProp | 
| tony prisk | tony prisk | 133 | 99.25% | 1 | 50.00% | 
| ulf hansson | ulf hansson | 1 | 0.75% | 1 | 50.00% | 
 | Total | 134 | 100.00% | 2 | 100.00% | 
static int wmt_mci_resume(struct device *dev)
{
	u32 reg_tmp;
	struct platform_device *pdev = to_platform_device(dev);
	struct mmc_host *mmc = platform_get_drvdata(pdev);
	struct wmt_mci_priv *priv;
	if (mmc) {
		priv = mmc_priv(mmc);
		clk_enable(priv->clk_sdmmc);
		reg_tmp = readb(priv->sdmmc_base + SDMMC_BUSMODE);
		writeb(reg_tmp | BM_SOFT_RESET, priv->sdmmc_base +
		       SDMMC_BUSMODE);
		reg_tmp = readw(priv->sdmmc_base + SDMMC_BLKLEN);
		writew(reg_tmp | (BLKL_GPI_CD | BLKL_INT_ENABLE),
		       priv->sdmmc_base + SDMMC_BLKLEN);
		reg_tmp = readb(priv->sdmmc_base + SDMMC_INTMASK0);
		writeb(reg_tmp | INT0_DI_INT_EN, priv->sdmmc_base +
		       SDMMC_INTMASK0);
	}
	return 0;
}
Contributors
 | Person | Tokens | Prop | Commits | CommitProp | 
| tony prisk | tony prisk | 137 | 99.28% | 1 | 50.00% | 
| ulf hansson | ulf hansson | 1 | 0.72% | 1 | 50.00% | 
 | Total | 138 | 100.00% | 2 | 100.00% | 
static const struct dev_pm_ops wmt_mci_pm = {
	.suspend        = wmt_mci_suspend,
	.resume         = wmt_mci_resume,
};
#define wmt_mci_pm_ops (&wmt_mci_pm)
#else	/* !CONFIG_PM */
#define wmt_mci_pm_ops NULL
#endif
static struct platform_driver wmt_mci_driver = {
	.probe = wmt_mci_probe,
	.remove = wmt_mci_remove,
	.driver = {
		.name = DRIVER_NAME,
		.pm = wmt_mci_pm_ops,
		.of_match_table = wmt_mci_dt_ids,
        },
};
module_platform_driver(wmt_mci_driver);
MODULE_DESCRIPTION("Wondermedia MMC/SD Driver");
MODULE_AUTHOR("Tony Prisk");
MODULE_LICENSE("GPL v2");
MODULE_DEVICE_TABLE(of, wmt_mci_dt_ids);
Overall Contributors
 | Person | Tokens | Prop | Commits | CommitProp | 
| tony prisk | tony prisk | 4396 | 98.41% | 1 | 11.11% | 
| axel lin | axel lin | 59 | 1.32% | 4 | 44.44% | 
| sachin kamat | sachin kamat | 6 | 0.13% | 1 | 11.11% | 
| alexandru gheorghiu | alexandru gheorghiu | 3 | 0.07% | 1 | 11.11% | 
| ulf hansson | ulf hansson | 2 | 0.04% | 1 | 11.11% | 
| fabian frederick | fabian frederick | 1 | 0.02% | 1 | 11.11% | 
 | Total | 4467 | 100.00% | 9 | 100.00% | 
  
Information contained on this website is for historical information purposes only and does not indicate or represent copyright ownership.