162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * linux/drivers/mmc/host/au1xmmc.c - AU1XX0 MMC driver
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci *  Copyright (c) 2005, Advanced Micro Devices, Inc.
662306a36Sopenharmony_ci *
762306a36Sopenharmony_ci *  Developed with help from the 2.4.30 MMC AU1XXX controller including
862306a36Sopenharmony_ci *  the following copyright notices:
962306a36Sopenharmony_ci *     Copyright (c) 2003-2004 Embedded Edge, LLC.
1062306a36Sopenharmony_ci *     Portions Copyright (C) 2002 Embedix, Inc
1162306a36Sopenharmony_ci *     Copyright 2002 Hewlett-Packard Company
1262306a36Sopenharmony_ci
1362306a36Sopenharmony_ci *  2.6 version of this driver inspired by:
1462306a36Sopenharmony_ci *     (drivers/mmc/wbsd.c) Copyright (C) 2004-2005 Pierre Ossman,
1562306a36Sopenharmony_ci *     All Rights Reserved.
1662306a36Sopenharmony_ci *     (drivers/mmc/pxa.c) Copyright (C) 2003 Russell King,
1762306a36Sopenharmony_ci *     All Rights Reserved.
1862306a36Sopenharmony_ci *
1962306a36Sopenharmony_ci
2062306a36Sopenharmony_ci */
2162306a36Sopenharmony_ci
2262306a36Sopenharmony_ci/* Why don't we use the SD controllers' carddetect feature?
2362306a36Sopenharmony_ci *
2462306a36Sopenharmony_ci * From the AU1100 MMC application guide:
2562306a36Sopenharmony_ci * If the Au1100-based design is intended to support both MultiMediaCards
2662306a36Sopenharmony_ci * and 1- or 4-data bit SecureDigital cards, then the solution is to
2762306a36Sopenharmony_ci * connect a weak (560KOhm) pull-up resistor to connector pin 1.
2862306a36Sopenharmony_ci * In doing so, a MMC card never enters SPI-mode communications,
2962306a36Sopenharmony_ci * but now the SecureDigital card-detect feature of CD/DAT3 is ineffective
3062306a36Sopenharmony_ci * (the low to high transition will not occur).
3162306a36Sopenharmony_ci */
3262306a36Sopenharmony_ci
3362306a36Sopenharmony_ci#include <linux/clk.h>
3462306a36Sopenharmony_ci#include <linux/module.h>
3562306a36Sopenharmony_ci#include <linux/init.h>
3662306a36Sopenharmony_ci#include <linux/platform_device.h>
3762306a36Sopenharmony_ci#include <linux/mm.h>
3862306a36Sopenharmony_ci#include <linux/interrupt.h>
3962306a36Sopenharmony_ci#include <linux/dma-mapping.h>
4062306a36Sopenharmony_ci#include <linux/scatterlist.h>
4162306a36Sopenharmony_ci#include <linux/highmem.h>
4262306a36Sopenharmony_ci#include <linux/leds.h>
4362306a36Sopenharmony_ci#include <linux/mmc/host.h>
4462306a36Sopenharmony_ci#include <linux/slab.h>
4562306a36Sopenharmony_ci
4662306a36Sopenharmony_ci#include <asm/io.h>
4762306a36Sopenharmony_ci#include <asm/mach-au1x00/au1000.h>
4862306a36Sopenharmony_ci#include <asm/mach-au1x00/au1xxx_dbdma.h>
4962306a36Sopenharmony_ci#include <asm/mach-au1x00/au1100_mmc.h>
5062306a36Sopenharmony_ci
5162306a36Sopenharmony_ci#define DRIVER_NAME "au1xxx-mmc"
5262306a36Sopenharmony_ci
5362306a36Sopenharmony_ci/* Set this to enable special debugging macros */
5462306a36Sopenharmony_ci/* #define DEBUG */
5562306a36Sopenharmony_ci
5662306a36Sopenharmony_ci#ifdef DEBUG
5762306a36Sopenharmony_ci#define DBG(fmt, idx, args...)	\
5862306a36Sopenharmony_ci	pr_debug("au1xmmc(%d): DEBUG: " fmt, idx, ##args)
5962306a36Sopenharmony_ci#else
6062306a36Sopenharmony_ci#define DBG(fmt, idx, args...) do {} while (0)
6162306a36Sopenharmony_ci#endif
6262306a36Sopenharmony_ci
6362306a36Sopenharmony_ci/* Hardware definitions */
6462306a36Sopenharmony_ci#define AU1XMMC_DESCRIPTOR_COUNT 1
6562306a36Sopenharmony_ci
6662306a36Sopenharmony_ci/* max DMA seg size: 64KB on Au1100, 4MB on Au1200 */
6762306a36Sopenharmony_ci#define AU1100_MMC_DESCRIPTOR_SIZE 0x0000ffff
6862306a36Sopenharmony_ci#define AU1200_MMC_DESCRIPTOR_SIZE 0x003fffff
6962306a36Sopenharmony_ci
7062306a36Sopenharmony_ci#define AU1XMMC_OCR (MMC_VDD_27_28 | MMC_VDD_28_29 | MMC_VDD_29_30 | \
7162306a36Sopenharmony_ci		     MMC_VDD_30_31 | MMC_VDD_31_32 | MMC_VDD_32_33 | \
7262306a36Sopenharmony_ci		     MMC_VDD_33_34 | MMC_VDD_34_35 | MMC_VDD_35_36)
7362306a36Sopenharmony_ci
7462306a36Sopenharmony_ci/* This gives us a hard value for the stop command that we can write directly
7562306a36Sopenharmony_ci * to the command register.
7662306a36Sopenharmony_ci */
7762306a36Sopenharmony_ci#define STOP_CMD	\
7862306a36Sopenharmony_ci	(SD_CMD_RT_1B | SD_CMD_CT_7 | (0xC << SD_CMD_CI_SHIFT) | SD_CMD_GO)
7962306a36Sopenharmony_ci
8062306a36Sopenharmony_ci/* This is the set of interrupts that we configure by default. */
8162306a36Sopenharmony_ci#define AU1XMMC_INTERRUPTS 				\
8262306a36Sopenharmony_ci	(SD_CONFIG_SC | SD_CONFIG_DT | SD_CONFIG_RAT |	\
8362306a36Sopenharmony_ci	 SD_CONFIG_CR | SD_CONFIG_I)
8462306a36Sopenharmony_ci
8562306a36Sopenharmony_ci/* The poll event (looking for insert/remove events runs twice a second. */
8662306a36Sopenharmony_ci#define AU1XMMC_DETECT_TIMEOUT (HZ/2)
8762306a36Sopenharmony_ci
8862306a36Sopenharmony_cistruct au1xmmc_host {
8962306a36Sopenharmony_ci	struct mmc_host *mmc;
9062306a36Sopenharmony_ci	struct mmc_request *mrq;
9162306a36Sopenharmony_ci
9262306a36Sopenharmony_ci	u32 flags;
9362306a36Sopenharmony_ci	void __iomem *iobase;
9462306a36Sopenharmony_ci	u32 clock;
9562306a36Sopenharmony_ci	u32 bus_width;
9662306a36Sopenharmony_ci	u32 power_mode;
9762306a36Sopenharmony_ci
9862306a36Sopenharmony_ci	int status;
9962306a36Sopenharmony_ci
10062306a36Sopenharmony_ci	struct {
10162306a36Sopenharmony_ci		int len;
10262306a36Sopenharmony_ci		int dir;
10362306a36Sopenharmony_ci	} dma;
10462306a36Sopenharmony_ci
10562306a36Sopenharmony_ci	struct {
10662306a36Sopenharmony_ci		int index;
10762306a36Sopenharmony_ci		int offset;
10862306a36Sopenharmony_ci		int len;
10962306a36Sopenharmony_ci	} pio;
11062306a36Sopenharmony_ci
11162306a36Sopenharmony_ci	u32 tx_chan;
11262306a36Sopenharmony_ci	u32 rx_chan;
11362306a36Sopenharmony_ci
11462306a36Sopenharmony_ci	int irq;
11562306a36Sopenharmony_ci
11662306a36Sopenharmony_ci	struct tasklet_struct finish_task;
11762306a36Sopenharmony_ci	struct tasklet_struct data_task;
11862306a36Sopenharmony_ci	struct au1xmmc_platform_data *platdata;
11962306a36Sopenharmony_ci	struct platform_device *pdev;
12062306a36Sopenharmony_ci	struct resource *ioarea;
12162306a36Sopenharmony_ci	struct clk *clk;
12262306a36Sopenharmony_ci};
12362306a36Sopenharmony_ci
12462306a36Sopenharmony_ci/* Status flags used by the host structure */
12562306a36Sopenharmony_ci#define HOST_F_XMIT	0x0001
12662306a36Sopenharmony_ci#define HOST_F_RECV	0x0002
12762306a36Sopenharmony_ci#define HOST_F_DMA	0x0010
12862306a36Sopenharmony_ci#define HOST_F_DBDMA	0x0020
12962306a36Sopenharmony_ci#define HOST_F_ACTIVE	0x0100
13062306a36Sopenharmony_ci#define HOST_F_STOP	0x1000
13162306a36Sopenharmony_ci
13262306a36Sopenharmony_ci#define HOST_S_IDLE	0x0001
13362306a36Sopenharmony_ci#define HOST_S_CMD	0x0002
13462306a36Sopenharmony_ci#define HOST_S_DATA	0x0003
13562306a36Sopenharmony_ci#define HOST_S_STOP	0x0004
13662306a36Sopenharmony_ci
13762306a36Sopenharmony_ci/* Easy access macros */
13862306a36Sopenharmony_ci#define HOST_STATUS(h)	((h)->iobase + SD_STATUS)
13962306a36Sopenharmony_ci#define HOST_CONFIG(h)	((h)->iobase + SD_CONFIG)
14062306a36Sopenharmony_ci#define HOST_ENABLE(h)	((h)->iobase + SD_ENABLE)
14162306a36Sopenharmony_ci#define HOST_TXPORT(h)	((h)->iobase + SD_TXPORT)
14262306a36Sopenharmony_ci#define HOST_RXPORT(h)	((h)->iobase + SD_RXPORT)
14362306a36Sopenharmony_ci#define HOST_CMDARG(h)	((h)->iobase + SD_CMDARG)
14462306a36Sopenharmony_ci#define HOST_BLKSIZE(h)	((h)->iobase + SD_BLKSIZE)
14562306a36Sopenharmony_ci#define HOST_CMD(h)	((h)->iobase + SD_CMD)
14662306a36Sopenharmony_ci#define HOST_CONFIG2(h)	((h)->iobase + SD_CONFIG2)
14762306a36Sopenharmony_ci#define HOST_TIMEOUT(h)	((h)->iobase + SD_TIMEOUT)
14862306a36Sopenharmony_ci#define HOST_DEBUG(h)	((h)->iobase + SD_DEBUG)
14962306a36Sopenharmony_ci
15062306a36Sopenharmony_ci#define DMA_CHANNEL(h)	\
15162306a36Sopenharmony_ci	(((h)->flags & HOST_F_XMIT) ? (h)->tx_chan : (h)->rx_chan)
15262306a36Sopenharmony_ci
15362306a36Sopenharmony_cistatic inline int has_dbdma(void)
15462306a36Sopenharmony_ci{
15562306a36Sopenharmony_ci	switch (alchemy_get_cputype()) {
15662306a36Sopenharmony_ci	case ALCHEMY_CPU_AU1200:
15762306a36Sopenharmony_ci	case ALCHEMY_CPU_AU1300:
15862306a36Sopenharmony_ci		return 1;
15962306a36Sopenharmony_ci	default:
16062306a36Sopenharmony_ci		return 0;
16162306a36Sopenharmony_ci	}
16262306a36Sopenharmony_ci}
16362306a36Sopenharmony_ci
16462306a36Sopenharmony_cistatic inline void IRQ_ON(struct au1xmmc_host *host, u32 mask)
16562306a36Sopenharmony_ci{
16662306a36Sopenharmony_ci	u32 val = __raw_readl(HOST_CONFIG(host));
16762306a36Sopenharmony_ci	val |= mask;
16862306a36Sopenharmony_ci	__raw_writel(val, HOST_CONFIG(host));
16962306a36Sopenharmony_ci	wmb(); /* drain writebuffer */
17062306a36Sopenharmony_ci}
17162306a36Sopenharmony_ci
17262306a36Sopenharmony_cistatic inline void FLUSH_FIFO(struct au1xmmc_host *host)
17362306a36Sopenharmony_ci{
17462306a36Sopenharmony_ci	u32 val = __raw_readl(HOST_CONFIG2(host));
17562306a36Sopenharmony_ci
17662306a36Sopenharmony_ci	__raw_writel(val | SD_CONFIG2_FF, HOST_CONFIG2(host));
17762306a36Sopenharmony_ci	wmb(); /* drain writebuffer */
17862306a36Sopenharmony_ci	mdelay(1);
17962306a36Sopenharmony_ci
18062306a36Sopenharmony_ci	/* SEND_STOP will turn off clock control - this re-enables it */
18162306a36Sopenharmony_ci	val &= ~SD_CONFIG2_DF;
18262306a36Sopenharmony_ci
18362306a36Sopenharmony_ci	__raw_writel(val, HOST_CONFIG2(host));
18462306a36Sopenharmony_ci	wmb(); /* drain writebuffer */
18562306a36Sopenharmony_ci}
18662306a36Sopenharmony_ci
18762306a36Sopenharmony_cistatic inline void IRQ_OFF(struct au1xmmc_host *host, u32 mask)
18862306a36Sopenharmony_ci{
18962306a36Sopenharmony_ci	u32 val = __raw_readl(HOST_CONFIG(host));
19062306a36Sopenharmony_ci	val &= ~mask;
19162306a36Sopenharmony_ci	__raw_writel(val, HOST_CONFIG(host));
19262306a36Sopenharmony_ci	wmb(); /* drain writebuffer */
19362306a36Sopenharmony_ci}
19462306a36Sopenharmony_ci
19562306a36Sopenharmony_cistatic inline void SEND_STOP(struct au1xmmc_host *host)
19662306a36Sopenharmony_ci{
19762306a36Sopenharmony_ci	u32 config2;
19862306a36Sopenharmony_ci
19962306a36Sopenharmony_ci	WARN_ON(host->status != HOST_S_DATA);
20062306a36Sopenharmony_ci	host->status = HOST_S_STOP;
20162306a36Sopenharmony_ci
20262306a36Sopenharmony_ci	config2 = __raw_readl(HOST_CONFIG2(host));
20362306a36Sopenharmony_ci	__raw_writel(config2 | SD_CONFIG2_DF, HOST_CONFIG2(host));
20462306a36Sopenharmony_ci	wmb(); /* drain writebuffer */
20562306a36Sopenharmony_ci
20662306a36Sopenharmony_ci	/* Send the stop command */
20762306a36Sopenharmony_ci	__raw_writel(STOP_CMD, HOST_CMD(host));
20862306a36Sopenharmony_ci	wmb(); /* drain writebuffer */
20962306a36Sopenharmony_ci}
21062306a36Sopenharmony_ci
21162306a36Sopenharmony_cistatic void au1xmmc_set_power(struct au1xmmc_host *host, int state)
21262306a36Sopenharmony_ci{
21362306a36Sopenharmony_ci	if (host->platdata && host->platdata->set_power)
21462306a36Sopenharmony_ci		host->platdata->set_power(host->mmc, state);
21562306a36Sopenharmony_ci}
21662306a36Sopenharmony_ci
21762306a36Sopenharmony_cistatic int au1xmmc_card_inserted(struct mmc_host *mmc)
21862306a36Sopenharmony_ci{
21962306a36Sopenharmony_ci	struct au1xmmc_host *host = mmc_priv(mmc);
22062306a36Sopenharmony_ci
22162306a36Sopenharmony_ci	if (host->platdata && host->platdata->card_inserted)
22262306a36Sopenharmony_ci		return !!host->platdata->card_inserted(host->mmc);
22362306a36Sopenharmony_ci
22462306a36Sopenharmony_ci	return -ENOSYS;
22562306a36Sopenharmony_ci}
22662306a36Sopenharmony_ci
22762306a36Sopenharmony_cistatic int au1xmmc_card_readonly(struct mmc_host *mmc)
22862306a36Sopenharmony_ci{
22962306a36Sopenharmony_ci	struct au1xmmc_host *host = mmc_priv(mmc);
23062306a36Sopenharmony_ci
23162306a36Sopenharmony_ci	if (host->platdata && host->platdata->card_readonly)
23262306a36Sopenharmony_ci		return !!host->platdata->card_readonly(mmc);
23362306a36Sopenharmony_ci
23462306a36Sopenharmony_ci	return -ENOSYS;
23562306a36Sopenharmony_ci}
23662306a36Sopenharmony_ci
23762306a36Sopenharmony_cistatic void au1xmmc_finish_request(struct au1xmmc_host *host)
23862306a36Sopenharmony_ci{
23962306a36Sopenharmony_ci	struct mmc_request *mrq = host->mrq;
24062306a36Sopenharmony_ci
24162306a36Sopenharmony_ci	host->mrq = NULL;
24262306a36Sopenharmony_ci	host->flags &= HOST_F_ACTIVE | HOST_F_DMA;
24362306a36Sopenharmony_ci
24462306a36Sopenharmony_ci	host->dma.len = 0;
24562306a36Sopenharmony_ci	host->dma.dir = 0;
24662306a36Sopenharmony_ci
24762306a36Sopenharmony_ci	host->pio.index  = 0;
24862306a36Sopenharmony_ci	host->pio.offset = 0;
24962306a36Sopenharmony_ci	host->pio.len = 0;
25062306a36Sopenharmony_ci
25162306a36Sopenharmony_ci	host->status = HOST_S_IDLE;
25262306a36Sopenharmony_ci
25362306a36Sopenharmony_ci	mmc_request_done(host->mmc, mrq);
25462306a36Sopenharmony_ci}
25562306a36Sopenharmony_ci
25662306a36Sopenharmony_cistatic void au1xmmc_tasklet_finish(struct tasklet_struct *t)
25762306a36Sopenharmony_ci{
25862306a36Sopenharmony_ci	struct au1xmmc_host *host = from_tasklet(host, t, finish_task);
25962306a36Sopenharmony_ci	au1xmmc_finish_request(host);
26062306a36Sopenharmony_ci}
26162306a36Sopenharmony_ci
26262306a36Sopenharmony_cistatic int au1xmmc_send_command(struct au1xmmc_host *host,
26362306a36Sopenharmony_ci				struct mmc_command *cmd, struct mmc_data *data)
26462306a36Sopenharmony_ci{
26562306a36Sopenharmony_ci	u32 mmccmd = (cmd->opcode << SD_CMD_CI_SHIFT);
26662306a36Sopenharmony_ci
26762306a36Sopenharmony_ci	switch (mmc_resp_type(cmd)) {
26862306a36Sopenharmony_ci	case MMC_RSP_NONE:
26962306a36Sopenharmony_ci		break;
27062306a36Sopenharmony_ci	case MMC_RSP_R1:
27162306a36Sopenharmony_ci		mmccmd |= SD_CMD_RT_1;
27262306a36Sopenharmony_ci		break;
27362306a36Sopenharmony_ci	case MMC_RSP_R1B:
27462306a36Sopenharmony_ci		mmccmd |= SD_CMD_RT_1B;
27562306a36Sopenharmony_ci		break;
27662306a36Sopenharmony_ci	case MMC_RSP_R2:
27762306a36Sopenharmony_ci		mmccmd |= SD_CMD_RT_2;
27862306a36Sopenharmony_ci		break;
27962306a36Sopenharmony_ci	case MMC_RSP_R3:
28062306a36Sopenharmony_ci		mmccmd |= SD_CMD_RT_3;
28162306a36Sopenharmony_ci		break;
28262306a36Sopenharmony_ci	default:
28362306a36Sopenharmony_ci		pr_info("au1xmmc: unhandled response type %02x\n",
28462306a36Sopenharmony_ci			mmc_resp_type(cmd));
28562306a36Sopenharmony_ci		return -EINVAL;
28662306a36Sopenharmony_ci	}
28762306a36Sopenharmony_ci
28862306a36Sopenharmony_ci	if (data) {
28962306a36Sopenharmony_ci		if (data->flags & MMC_DATA_READ) {
29062306a36Sopenharmony_ci			if (data->blocks > 1)
29162306a36Sopenharmony_ci				mmccmd |= SD_CMD_CT_4;
29262306a36Sopenharmony_ci			else
29362306a36Sopenharmony_ci				mmccmd |= SD_CMD_CT_2;
29462306a36Sopenharmony_ci		} else if (data->flags & MMC_DATA_WRITE) {
29562306a36Sopenharmony_ci			if (data->blocks > 1)
29662306a36Sopenharmony_ci				mmccmd |= SD_CMD_CT_3;
29762306a36Sopenharmony_ci			else
29862306a36Sopenharmony_ci				mmccmd |= SD_CMD_CT_1;
29962306a36Sopenharmony_ci		}
30062306a36Sopenharmony_ci	}
30162306a36Sopenharmony_ci
30262306a36Sopenharmony_ci	__raw_writel(cmd->arg, HOST_CMDARG(host));
30362306a36Sopenharmony_ci	wmb(); /* drain writebuffer */
30462306a36Sopenharmony_ci
30562306a36Sopenharmony_ci	__raw_writel((mmccmd | SD_CMD_GO), HOST_CMD(host));
30662306a36Sopenharmony_ci	wmb(); /* drain writebuffer */
30762306a36Sopenharmony_ci
30862306a36Sopenharmony_ci	/* Wait for the command to go on the line */
30962306a36Sopenharmony_ci	while (__raw_readl(HOST_CMD(host)) & SD_CMD_GO)
31062306a36Sopenharmony_ci		/* nop */;
31162306a36Sopenharmony_ci
31262306a36Sopenharmony_ci	return 0;
31362306a36Sopenharmony_ci}
31462306a36Sopenharmony_ci
31562306a36Sopenharmony_cistatic void au1xmmc_data_complete(struct au1xmmc_host *host, u32 status)
31662306a36Sopenharmony_ci{
31762306a36Sopenharmony_ci	struct mmc_request *mrq = host->mrq;
31862306a36Sopenharmony_ci	struct mmc_data *data;
31962306a36Sopenharmony_ci	u32 crc;
32062306a36Sopenharmony_ci
32162306a36Sopenharmony_ci	WARN_ON((host->status != HOST_S_DATA) && (host->status != HOST_S_STOP));
32262306a36Sopenharmony_ci
32362306a36Sopenharmony_ci	if (host->mrq == NULL)
32462306a36Sopenharmony_ci		return;
32562306a36Sopenharmony_ci
32662306a36Sopenharmony_ci	data = mrq->cmd->data;
32762306a36Sopenharmony_ci
32862306a36Sopenharmony_ci	if (status == 0)
32962306a36Sopenharmony_ci		status = __raw_readl(HOST_STATUS(host));
33062306a36Sopenharmony_ci
33162306a36Sopenharmony_ci	/* The transaction is really over when the SD_STATUS_DB bit is clear */
33262306a36Sopenharmony_ci	while ((host->flags & HOST_F_XMIT) && (status & SD_STATUS_DB))
33362306a36Sopenharmony_ci		status = __raw_readl(HOST_STATUS(host));
33462306a36Sopenharmony_ci
33562306a36Sopenharmony_ci	data->error = 0;
33662306a36Sopenharmony_ci	dma_unmap_sg(mmc_dev(host->mmc), data->sg, data->sg_len, host->dma.dir);
33762306a36Sopenharmony_ci
33862306a36Sopenharmony_ci        /* Process any errors */
33962306a36Sopenharmony_ci	crc = (status & (SD_STATUS_WC | SD_STATUS_RC));
34062306a36Sopenharmony_ci	if (host->flags & HOST_F_XMIT)
34162306a36Sopenharmony_ci		crc |= ((status & 0x07) == 0x02) ? 0 : 1;
34262306a36Sopenharmony_ci
34362306a36Sopenharmony_ci	if (crc)
34462306a36Sopenharmony_ci		data->error = -EILSEQ;
34562306a36Sopenharmony_ci
34662306a36Sopenharmony_ci	/* Clear the CRC bits */
34762306a36Sopenharmony_ci	__raw_writel(SD_STATUS_WC | SD_STATUS_RC, HOST_STATUS(host));
34862306a36Sopenharmony_ci
34962306a36Sopenharmony_ci	data->bytes_xfered = 0;
35062306a36Sopenharmony_ci
35162306a36Sopenharmony_ci	if (!data->error) {
35262306a36Sopenharmony_ci		if (host->flags & (HOST_F_DMA | HOST_F_DBDMA)) {
35362306a36Sopenharmony_ci			u32 chan = DMA_CHANNEL(host);
35462306a36Sopenharmony_ci
35562306a36Sopenharmony_ci			chan_tab_t *c = *((chan_tab_t **)chan);
35662306a36Sopenharmony_ci			au1x_dma_chan_t *cp = c->chan_ptr;
35762306a36Sopenharmony_ci			data->bytes_xfered = cp->ddma_bytecnt;
35862306a36Sopenharmony_ci		} else
35962306a36Sopenharmony_ci			data->bytes_xfered =
36062306a36Sopenharmony_ci				(data->blocks * data->blksz) - host->pio.len;
36162306a36Sopenharmony_ci	}
36262306a36Sopenharmony_ci
36362306a36Sopenharmony_ci	au1xmmc_finish_request(host);
36462306a36Sopenharmony_ci}
36562306a36Sopenharmony_ci
36662306a36Sopenharmony_cistatic void au1xmmc_tasklet_data(struct tasklet_struct *t)
36762306a36Sopenharmony_ci{
36862306a36Sopenharmony_ci	struct au1xmmc_host *host = from_tasklet(host, t, data_task);
36962306a36Sopenharmony_ci
37062306a36Sopenharmony_ci	u32 status = __raw_readl(HOST_STATUS(host));
37162306a36Sopenharmony_ci	au1xmmc_data_complete(host, status);
37262306a36Sopenharmony_ci}
37362306a36Sopenharmony_ci
37462306a36Sopenharmony_ci#define AU1XMMC_MAX_TRANSFER 8
37562306a36Sopenharmony_ci
37662306a36Sopenharmony_cistatic void au1xmmc_send_pio(struct au1xmmc_host *host)
37762306a36Sopenharmony_ci{
37862306a36Sopenharmony_ci	struct mmc_data *data;
37962306a36Sopenharmony_ci	int sg_len, max, count;
38062306a36Sopenharmony_ci	unsigned char *sg_ptr, val;
38162306a36Sopenharmony_ci	u32 status;
38262306a36Sopenharmony_ci	struct scatterlist *sg;
38362306a36Sopenharmony_ci
38462306a36Sopenharmony_ci	data = host->mrq->data;
38562306a36Sopenharmony_ci
38662306a36Sopenharmony_ci	if (!(host->flags & HOST_F_XMIT))
38762306a36Sopenharmony_ci		return;
38862306a36Sopenharmony_ci
38962306a36Sopenharmony_ci	/* This is the pointer to the data buffer */
39062306a36Sopenharmony_ci	sg = &data->sg[host->pio.index];
39162306a36Sopenharmony_ci	sg_ptr = kmap_local_page(sg_page(sg)) + sg->offset + host->pio.offset;
39262306a36Sopenharmony_ci
39362306a36Sopenharmony_ci	/* This is the space left inside the buffer */
39462306a36Sopenharmony_ci	sg_len = data->sg[host->pio.index].length - host->pio.offset;
39562306a36Sopenharmony_ci
39662306a36Sopenharmony_ci	/* Check if we need less than the size of the sg_buffer */
39762306a36Sopenharmony_ci	max = (sg_len > host->pio.len) ? host->pio.len : sg_len;
39862306a36Sopenharmony_ci	if (max > AU1XMMC_MAX_TRANSFER)
39962306a36Sopenharmony_ci		max = AU1XMMC_MAX_TRANSFER;
40062306a36Sopenharmony_ci
40162306a36Sopenharmony_ci	for (count = 0; count < max; count++) {
40262306a36Sopenharmony_ci		status = __raw_readl(HOST_STATUS(host));
40362306a36Sopenharmony_ci
40462306a36Sopenharmony_ci		if (!(status & SD_STATUS_TH))
40562306a36Sopenharmony_ci			break;
40662306a36Sopenharmony_ci
40762306a36Sopenharmony_ci		val = sg_ptr[count];
40862306a36Sopenharmony_ci
40962306a36Sopenharmony_ci		__raw_writel((unsigned long)val, HOST_TXPORT(host));
41062306a36Sopenharmony_ci		wmb(); /* drain writebuffer */
41162306a36Sopenharmony_ci	}
41262306a36Sopenharmony_ci	kunmap_local(sg_ptr);
41362306a36Sopenharmony_ci
41462306a36Sopenharmony_ci	host->pio.len -= count;
41562306a36Sopenharmony_ci	host->pio.offset += count;
41662306a36Sopenharmony_ci
41762306a36Sopenharmony_ci	if (count == sg_len) {
41862306a36Sopenharmony_ci		host->pio.index++;
41962306a36Sopenharmony_ci		host->pio.offset = 0;
42062306a36Sopenharmony_ci	}
42162306a36Sopenharmony_ci
42262306a36Sopenharmony_ci	if (host->pio.len == 0) {
42362306a36Sopenharmony_ci		IRQ_OFF(host, SD_CONFIG_TH);
42462306a36Sopenharmony_ci
42562306a36Sopenharmony_ci		if (host->flags & HOST_F_STOP)
42662306a36Sopenharmony_ci			SEND_STOP(host);
42762306a36Sopenharmony_ci
42862306a36Sopenharmony_ci		tasklet_schedule(&host->data_task);
42962306a36Sopenharmony_ci	}
43062306a36Sopenharmony_ci}
43162306a36Sopenharmony_ci
43262306a36Sopenharmony_cistatic void au1xmmc_receive_pio(struct au1xmmc_host *host)
43362306a36Sopenharmony_ci{
43462306a36Sopenharmony_ci	struct mmc_data *data;
43562306a36Sopenharmony_ci	int max, count, sg_len = 0;
43662306a36Sopenharmony_ci	unsigned char *sg_ptr = NULL;
43762306a36Sopenharmony_ci	u32 status, val;
43862306a36Sopenharmony_ci	struct scatterlist *sg;
43962306a36Sopenharmony_ci
44062306a36Sopenharmony_ci	data = host->mrq->data;
44162306a36Sopenharmony_ci
44262306a36Sopenharmony_ci	if (!(host->flags & HOST_F_RECV))
44362306a36Sopenharmony_ci		return;
44462306a36Sopenharmony_ci
44562306a36Sopenharmony_ci	max = host->pio.len;
44662306a36Sopenharmony_ci
44762306a36Sopenharmony_ci	if (host->pio.index < host->dma.len) {
44862306a36Sopenharmony_ci		sg = &data->sg[host->pio.index];
44962306a36Sopenharmony_ci		sg_ptr = kmap_local_page(sg_page(sg)) + sg->offset + host->pio.offset;
45062306a36Sopenharmony_ci
45162306a36Sopenharmony_ci		/* This is the space left inside the buffer */
45262306a36Sopenharmony_ci		sg_len = sg_dma_len(&data->sg[host->pio.index]) - host->pio.offset;
45362306a36Sopenharmony_ci
45462306a36Sopenharmony_ci		/* Check if we need less than the size of the sg_buffer */
45562306a36Sopenharmony_ci		if (sg_len < max)
45662306a36Sopenharmony_ci			max = sg_len;
45762306a36Sopenharmony_ci	}
45862306a36Sopenharmony_ci
45962306a36Sopenharmony_ci	if (max > AU1XMMC_MAX_TRANSFER)
46062306a36Sopenharmony_ci		max = AU1XMMC_MAX_TRANSFER;
46162306a36Sopenharmony_ci
46262306a36Sopenharmony_ci	for (count = 0; count < max; count++) {
46362306a36Sopenharmony_ci		status = __raw_readl(HOST_STATUS(host));
46462306a36Sopenharmony_ci
46562306a36Sopenharmony_ci		if (!(status & SD_STATUS_NE))
46662306a36Sopenharmony_ci			break;
46762306a36Sopenharmony_ci
46862306a36Sopenharmony_ci		if (status & SD_STATUS_RC) {
46962306a36Sopenharmony_ci			DBG("RX CRC Error [%d + %d].\n", host->pdev->id,
47062306a36Sopenharmony_ci					host->pio.len, count);
47162306a36Sopenharmony_ci			break;
47262306a36Sopenharmony_ci		}
47362306a36Sopenharmony_ci
47462306a36Sopenharmony_ci		if (status & SD_STATUS_RO) {
47562306a36Sopenharmony_ci			DBG("RX Overrun [%d + %d]\n", host->pdev->id,
47662306a36Sopenharmony_ci					host->pio.len, count);
47762306a36Sopenharmony_ci			break;
47862306a36Sopenharmony_ci		}
47962306a36Sopenharmony_ci		else if (status & SD_STATUS_RU) {
48062306a36Sopenharmony_ci			DBG("RX Underrun [%d + %d]\n", host->pdev->id,
48162306a36Sopenharmony_ci					host->pio.len,	count);
48262306a36Sopenharmony_ci			break;
48362306a36Sopenharmony_ci		}
48462306a36Sopenharmony_ci
48562306a36Sopenharmony_ci		val = __raw_readl(HOST_RXPORT(host));
48662306a36Sopenharmony_ci
48762306a36Sopenharmony_ci		if (sg_ptr)
48862306a36Sopenharmony_ci			sg_ptr[count] = (unsigned char)(val & 0xFF);
48962306a36Sopenharmony_ci	}
49062306a36Sopenharmony_ci	if (sg_ptr)
49162306a36Sopenharmony_ci		kunmap_local(sg_ptr);
49262306a36Sopenharmony_ci
49362306a36Sopenharmony_ci	host->pio.len -= count;
49462306a36Sopenharmony_ci	host->pio.offset += count;
49562306a36Sopenharmony_ci
49662306a36Sopenharmony_ci	if (sg_len && count == sg_len) {
49762306a36Sopenharmony_ci		host->pio.index++;
49862306a36Sopenharmony_ci		host->pio.offset = 0;
49962306a36Sopenharmony_ci	}
50062306a36Sopenharmony_ci
50162306a36Sopenharmony_ci	if (host->pio.len == 0) {
50262306a36Sopenharmony_ci		/* IRQ_OFF(host, SD_CONFIG_RA | SD_CONFIG_RF); */
50362306a36Sopenharmony_ci		IRQ_OFF(host, SD_CONFIG_NE);
50462306a36Sopenharmony_ci
50562306a36Sopenharmony_ci		if (host->flags & HOST_F_STOP)
50662306a36Sopenharmony_ci			SEND_STOP(host);
50762306a36Sopenharmony_ci
50862306a36Sopenharmony_ci		tasklet_schedule(&host->data_task);
50962306a36Sopenharmony_ci	}
51062306a36Sopenharmony_ci}
51162306a36Sopenharmony_ci
51262306a36Sopenharmony_ci/* This is called when a command has been completed - grab the response
51362306a36Sopenharmony_ci * and check for errors.  Then start the data transfer if it is indicated.
51462306a36Sopenharmony_ci */
51562306a36Sopenharmony_cistatic void au1xmmc_cmd_complete(struct au1xmmc_host *host, u32 status)
51662306a36Sopenharmony_ci{
51762306a36Sopenharmony_ci	struct mmc_request *mrq = host->mrq;
51862306a36Sopenharmony_ci	struct mmc_command *cmd;
51962306a36Sopenharmony_ci	u32 r[4];
52062306a36Sopenharmony_ci	int i, trans;
52162306a36Sopenharmony_ci
52262306a36Sopenharmony_ci	if (!host->mrq)
52362306a36Sopenharmony_ci		return;
52462306a36Sopenharmony_ci
52562306a36Sopenharmony_ci	cmd = mrq->cmd;
52662306a36Sopenharmony_ci	cmd->error = 0;
52762306a36Sopenharmony_ci
52862306a36Sopenharmony_ci	if (cmd->flags & MMC_RSP_PRESENT) {
52962306a36Sopenharmony_ci		if (cmd->flags & MMC_RSP_136) {
53062306a36Sopenharmony_ci			r[0] = __raw_readl(host->iobase + SD_RESP3);
53162306a36Sopenharmony_ci			r[1] = __raw_readl(host->iobase + SD_RESP2);
53262306a36Sopenharmony_ci			r[2] = __raw_readl(host->iobase + SD_RESP1);
53362306a36Sopenharmony_ci			r[3] = __raw_readl(host->iobase + SD_RESP0);
53462306a36Sopenharmony_ci
53562306a36Sopenharmony_ci			/* The CRC is omitted from the response, so really
53662306a36Sopenharmony_ci			 * we only got 120 bytes, but the engine expects
53762306a36Sopenharmony_ci			 * 128 bits, so we have to shift things up.
53862306a36Sopenharmony_ci			 */
53962306a36Sopenharmony_ci			for (i = 0; i < 4; i++) {
54062306a36Sopenharmony_ci				cmd->resp[i] = (r[i] & 0x00FFFFFF) << 8;
54162306a36Sopenharmony_ci				if (i != 3)
54262306a36Sopenharmony_ci					cmd->resp[i] |= (r[i + 1] & 0xFF000000) >> 24;
54362306a36Sopenharmony_ci			}
54462306a36Sopenharmony_ci		} else {
54562306a36Sopenharmony_ci			/* Techincally, we should be getting all 48 bits of
54662306a36Sopenharmony_ci			 * the response (SD_RESP1 + SD_RESP2), but because
54762306a36Sopenharmony_ci			 * our response omits the CRC, our data ends up
54862306a36Sopenharmony_ci			 * being shifted 8 bits to the right.  In this case,
54962306a36Sopenharmony_ci			 * that means that the OSR data starts at bit 31,
55062306a36Sopenharmony_ci			 * so we can just read RESP0 and return that.
55162306a36Sopenharmony_ci			 */
55262306a36Sopenharmony_ci			cmd->resp[0] = __raw_readl(host->iobase + SD_RESP0);
55362306a36Sopenharmony_ci		}
55462306a36Sopenharmony_ci	}
55562306a36Sopenharmony_ci
55662306a36Sopenharmony_ci        /* Figure out errors */
55762306a36Sopenharmony_ci	if (status & (SD_STATUS_SC | SD_STATUS_WC | SD_STATUS_RC))
55862306a36Sopenharmony_ci		cmd->error = -EILSEQ;
55962306a36Sopenharmony_ci
56062306a36Sopenharmony_ci	trans = host->flags & (HOST_F_XMIT | HOST_F_RECV);
56162306a36Sopenharmony_ci
56262306a36Sopenharmony_ci	if (!trans || cmd->error) {
56362306a36Sopenharmony_ci		IRQ_OFF(host, SD_CONFIG_TH | SD_CONFIG_RA | SD_CONFIG_RF);
56462306a36Sopenharmony_ci		tasklet_schedule(&host->finish_task);
56562306a36Sopenharmony_ci		return;
56662306a36Sopenharmony_ci	}
56762306a36Sopenharmony_ci
56862306a36Sopenharmony_ci	host->status = HOST_S_DATA;
56962306a36Sopenharmony_ci
57062306a36Sopenharmony_ci	if ((host->flags & (HOST_F_DMA | HOST_F_DBDMA))) {
57162306a36Sopenharmony_ci		u32 channel = DMA_CHANNEL(host);
57262306a36Sopenharmony_ci
57362306a36Sopenharmony_ci		/* Start the DBDMA as soon as the buffer gets something in it */
57462306a36Sopenharmony_ci
57562306a36Sopenharmony_ci		if (host->flags & HOST_F_RECV) {
57662306a36Sopenharmony_ci			u32 mask = SD_STATUS_DB | SD_STATUS_NE;
57762306a36Sopenharmony_ci
57862306a36Sopenharmony_ci			while((status & mask) != mask)
57962306a36Sopenharmony_ci				status = __raw_readl(HOST_STATUS(host));
58062306a36Sopenharmony_ci		}
58162306a36Sopenharmony_ci
58262306a36Sopenharmony_ci		au1xxx_dbdma_start(channel);
58362306a36Sopenharmony_ci	}
58462306a36Sopenharmony_ci}
58562306a36Sopenharmony_ci
58662306a36Sopenharmony_cistatic void au1xmmc_set_clock(struct au1xmmc_host *host, int rate)
58762306a36Sopenharmony_ci{
58862306a36Sopenharmony_ci	unsigned int pbus = clk_get_rate(host->clk);
58962306a36Sopenharmony_ci	unsigned int divisor = ((pbus / rate) / 2) - 1;
59062306a36Sopenharmony_ci	u32 config;
59162306a36Sopenharmony_ci
59262306a36Sopenharmony_ci	config = __raw_readl(HOST_CONFIG(host));
59362306a36Sopenharmony_ci
59462306a36Sopenharmony_ci	config &= ~(SD_CONFIG_DIV);
59562306a36Sopenharmony_ci	config |= (divisor & SD_CONFIG_DIV) | SD_CONFIG_DE;
59662306a36Sopenharmony_ci
59762306a36Sopenharmony_ci	__raw_writel(config, HOST_CONFIG(host));
59862306a36Sopenharmony_ci	wmb(); /* drain writebuffer */
59962306a36Sopenharmony_ci}
60062306a36Sopenharmony_ci
60162306a36Sopenharmony_cistatic int au1xmmc_prepare_data(struct au1xmmc_host *host,
60262306a36Sopenharmony_ci				struct mmc_data *data)
60362306a36Sopenharmony_ci{
60462306a36Sopenharmony_ci	int datalen = data->blocks * data->blksz;
60562306a36Sopenharmony_ci
60662306a36Sopenharmony_ci	if (data->flags & MMC_DATA_READ)
60762306a36Sopenharmony_ci		host->flags |= HOST_F_RECV;
60862306a36Sopenharmony_ci	else
60962306a36Sopenharmony_ci		host->flags |= HOST_F_XMIT;
61062306a36Sopenharmony_ci
61162306a36Sopenharmony_ci	if (host->mrq->stop)
61262306a36Sopenharmony_ci		host->flags |= HOST_F_STOP;
61362306a36Sopenharmony_ci
61462306a36Sopenharmony_ci	host->dma.dir = DMA_BIDIRECTIONAL;
61562306a36Sopenharmony_ci
61662306a36Sopenharmony_ci	host->dma.len = dma_map_sg(mmc_dev(host->mmc), data->sg,
61762306a36Sopenharmony_ci				   data->sg_len, host->dma.dir);
61862306a36Sopenharmony_ci
61962306a36Sopenharmony_ci	if (host->dma.len == 0)
62062306a36Sopenharmony_ci		return -ETIMEDOUT;
62162306a36Sopenharmony_ci
62262306a36Sopenharmony_ci	__raw_writel(data->blksz - 1, HOST_BLKSIZE(host));
62362306a36Sopenharmony_ci
62462306a36Sopenharmony_ci	if (host->flags & (HOST_F_DMA | HOST_F_DBDMA)) {
62562306a36Sopenharmony_ci		int i;
62662306a36Sopenharmony_ci		u32 channel = DMA_CHANNEL(host);
62762306a36Sopenharmony_ci
62862306a36Sopenharmony_ci		au1xxx_dbdma_stop(channel);
62962306a36Sopenharmony_ci
63062306a36Sopenharmony_ci		for (i = 0; i < host->dma.len; i++) {
63162306a36Sopenharmony_ci			u32 ret = 0, flags = DDMA_FLAGS_NOIE;
63262306a36Sopenharmony_ci			struct scatterlist *sg = &data->sg[i];
63362306a36Sopenharmony_ci			int sg_len = sg->length;
63462306a36Sopenharmony_ci
63562306a36Sopenharmony_ci			int len = (datalen > sg_len) ? sg_len : datalen;
63662306a36Sopenharmony_ci
63762306a36Sopenharmony_ci			if (i == host->dma.len - 1)
63862306a36Sopenharmony_ci				flags = DDMA_FLAGS_IE;
63962306a36Sopenharmony_ci
64062306a36Sopenharmony_ci			if (host->flags & HOST_F_XMIT) {
64162306a36Sopenharmony_ci				ret = au1xxx_dbdma_put_source(channel,
64262306a36Sopenharmony_ci					sg_phys(sg), len, flags);
64362306a36Sopenharmony_ci			} else {
64462306a36Sopenharmony_ci				ret = au1xxx_dbdma_put_dest(channel,
64562306a36Sopenharmony_ci					sg_phys(sg), len, flags);
64662306a36Sopenharmony_ci			}
64762306a36Sopenharmony_ci
64862306a36Sopenharmony_ci			if (!ret)
64962306a36Sopenharmony_ci				goto dataerr;
65062306a36Sopenharmony_ci
65162306a36Sopenharmony_ci			datalen -= len;
65262306a36Sopenharmony_ci		}
65362306a36Sopenharmony_ci	} else {
65462306a36Sopenharmony_ci		host->pio.index = 0;
65562306a36Sopenharmony_ci		host->pio.offset = 0;
65662306a36Sopenharmony_ci		host->pio.len = datalen;
65762306a36Sopenharmony_ci
65862306a36Sopenharmony_ci		if (host->flags & HOST_F_XMIT)
65962306a36Sopenharmony_ci			IRQ_ON(host, SD_CONFIG_TH);
66062306a36Sopenharmony_ci		else
66162306a36Sopenharmony_ci			IRQ_ON(host, SD_CONFIG_NE);
66262306a36Sopenharmony_ci			/* IRQ_ON(host, SD_CONFIG_RA | SD_CONFIG_RF); */
66362306a36Sopenharmony_ci	}
66462306a36Sopenharmony_ci
66562306a36Sopenharmony_ci	return 0;
66662306a36Sopenharmony_ci
66762306a36Sopenharmony_cidataerr:
66862306a36Sopenharmony_ci	dma_unmap_sg(mmc_dev(host->mmc), data->sg, data->sg_len,
66962306a36Sopenharmony_ci			host->dma.dir);
67062306a36Sopenharmony_ci	return -ETIMEDOUT;
67162306a36Sopenharmony_ci}
67262306a36Sopenharmony_ci
67362306a36Sopenharmony_ci/* This actually starts a command or data transaction */
67462306a36Sopenharmony_cistatic void au1xmmc_request(struct mmc_host* mmc, struct mmc_request* mrq)
67562306a36Sopenharmony_ci{
67662306a36Sopenharmony_ci	struct au1xmmc_host *host = mmc_priv(mmc);
67762306a36Sopenharmony_ci	int ret = 0;
67862306a36Sopenharmony_ci
67962306a36Sopenharmony_ci	WARN_ON(irqs_disabled());
68062306a36Sopenharmony_ci	WARN_ON(host->status != HOST_S_IDLE);
68162306a36Sopenharmony_ci
68262306a36Sopenharmony_ci	host->mrq = mrq;
68362306a36Sopenharmony_ci	host->status = HOST_S_CMD;
68462306a36Sopenharmony_ci
68562306a36Sopenharmony_ci	/* fail request immediately if no card is present */
68662306a36Sopenharmony_ci	if (0 == au1xmmc_card_inserted(mmc)) {
68762306a36Sopenharmony_ci		mrq->cmd->error = -ENOMEDIUM;
68862306a36Sopenharmony_ci		au1xmmc_finish_request(host);
68962306a36Sopenharmony_ci		return;
69062306a36Sopenharmony_ci	}
69162306a36Sopenharmony_ci
69262306a36Sopenharmony_ci	if (mrq->data) {
69362306a36Sopenharmony_ci		FLUSH_FIFO(host);
69462306a36Sopenharmony_ci		ret = au1xmmc_prepare_data(host, mrq->data);
69562306a36Sopenharmony_ci	}
69662306a36Sopenharmony_ci
69762306a36Sopenharmony_ci	if (!ret)
69862306a36Sopenharmony_ci		ret = au1xmmc_send_command(host, mrq->cmd, mrq->data);
69962306a36Sopenharmony_ci
70062306a36Sopenharmony_ci	if (ret) {
70162306a36Sopenharmony_ci		mrq->cmd->error = ret;
70262306a36Sopenharmony_ci		au1xmmc_finish_request(host);
70362306a36Sopenharmony_ci	}
70462306a36Sopenharmony_ci}
70562306a36Sopenharmony_ci
70662306a36Sopenharmony_cistatic void au1xmmc_reset_controller(struct au1xmmc_host *host)
70762306a36Sopenharmony_ci{
70862306a36Sopenharmony_ci	/* Apply the clock */
70962306a36Sopenharmony_ci	__raw_writel(SD_ENABLE_CE, HOST_ENABLE(host));
71062306a36Sopenharmony_ci	wmb(); /* drain writebuffer */
71162306a36Sopenharmony_ci	mdelay(1);
71262306a36Sopenharmony_ci
71362306a36Sopenharmony_ci	__raw_writel(SD_ENABLE_R | SD_ENABLE_CE, HOST_ENABLE(host));
71462306a36Sopenharmony_ci	wmb(); /* drain writebuffer */
71562306a36Sopenharmony_ci	mdelay(5);
71662306a36Sopenharmony_ci
71762306a36Sopenharmony_ci	__raw_writel(~0, HOST_STATUS(host));
71862306a36Sopenharmony_ci	wmb(); /* drain writebuffer */
71962306a36Sopenharmony_ci
72062306a36Sopenharmony_ci	__raw_writel(0, HOST_BLKSIZE(host));
72162306a36Sopenharmony_ci	__raw_writel(0x001fffff, HOST_TIMEOUT(host));
72262306a36Sopenharmony_ci	wmb(); /* drain writebuffer */
72362306a36Sopenharmony_ci
72462306a36Sopenharmony_ci	__raw_writel(SD_CONFIG2_EN, HOST_CONFIG2(host));
72562306a36Sopenharmony_ci	wmb(); /* drain writebuffer */
72662306a36Sopenharmony_ci
72762306a36Sopenharmony_ci	__raw_writel(SD_CONFIG2_EN | SD_CONFIG2_FF, HOST_CONFIG2(host));
72862306a36Sopenharmony_ci	wmb(); /* drain writebuffer */
72962306a36Sopenharmony_ci	mdelay(1);
73062306a36Sopenharmony_ci
73162306a36Sopenharmony_ci	__raw_writel(SD_CONFIG2_EN, HOST_CONFIG2(host));
73262306a36Sopenharmony_ci	wmb(); /* drain writebuffer */
73362306a36Sopenharmony_ci
73462306a36Sopenharmony_ci	/* Configure interrupts */
73562306a36Sopenharmony_ci	__raw_writel(AU1XMMC_INTERRUPTS, HOST_CONFIG(host));
73662306a36Sopenharmony_ci	wmb(); /* drain writebuffer */
73762306a36Sopenharmony_ci}
73862306a36Sopenharmony_ci
73962306a36Sopenharmony_ci
74062306a36Sopenharmony_cistatic void au1xmmc_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
74162306a36Sopenharmony_ci{
74262306a36Sopenharmony_ci	struct au1xmmc_host *host = mmc_priv(mmc);
74362306a36Sopenharmony_ci	u32 config2;
74462306a36Sopenharmony_ci
74562306a36Sopenharmony_ci	if (ios->power_mode == MMC_POWER_OFF)
74662306a36Sopenharmony_ci		au1xmmc_set_power(host, 0);
74762306a36Sopenharmony_ci	else if (ios->power_mode == MMC_POWER_ON) {
74862306a36Sopenharmony_ci		au1xmmc_set_power(host, 1);
74962306a36Sopenharmony_ci	}
75062306a36Sopenharmony_ci
75162306a36Sopenharmony_ci	if (ios->clock && ios->clock != host->clock) {
75262306a36Sopenharmony_ci		au1xmmc_set_clock(host, ios->clock);
75362306a36Sopenharmony_ci		host->clock = ios->clock;
75462306a36Sopenharmony_ci	}
75562306a36Sopenharmony_ci
75662306a36Sopenharmony_ci	config2 = __raw_readl(HOST_CONFIG2(host));
75762306a36Sopenharmony_ci	switch (ios->bus_width) {
75862306a36Sopenharmony_ci	case MMC_BUS_WIDTH_8:
75962306a36Sopenharmony_ci		config2 |= SD_CONFIG2_BB;
76062306a36Sopenharmony_ci		break;
76162306a36Sopenharmony_ci	case MMC_BUS_WIDTH_4:
76262306a36Sopenharmony_ci		config2 &= ~SD_CONFIG2_BB;
76362306a36Sopenharmony_ci		config2 |= SD_CONFIG2_WB;
76462306a36Sopenharmony_ci		break;
76562306a36Sopenharmony_ci	case MMC_BUS_WIDTH_1:
76662306a36Sopenharmony_ci		config2 &= ~(SD_CONFIG2_WB | SD_CONFIG2_BB);
76762306a36Sopenharmony_ci		break;
76862306a36Sopenharmony_ci	}
76962306a36Sopenharmony_ci	__raw_writel(config2, HOST_CONFIG2(host));
77062306a36Sopenharmony_ci	wmb(); /* drain writebuffer */
77162306a36Sopenharmony_ci}
77262306a36Sopenharmony_ci
77362306a36Sopenharmony_ci#define STATUS_TIMEOUT (SD_STATUS_RAT | SD_STATUS_DT)
77462306a36Sopenharmony_ci#define STATUS_DATA_IN  (SD_STATUS_NE)
77562306a36Sopenharmony_ci#define STATUS_DATA_OUT (SD_STATUS_TH)
77662306a36Sopenharmony_ci
77762306a36Sopenharmony_cistatic irqreturn_t au1xmmc_irq(int irq, void *dev_id)
77862306a36Sopenharmony_ci{
77962306a36Sopenharmony_ci	struct au1xmmc_host *host = dev_id;
78062306a36Sopenharmony_ci	u32 status;
78162306a36Sopenharmony_ci
78262306a36Sopenharmony_ci	status = __raw_readl(HOST_STATUS(host));
78362306a36Sopenharmony_ci
78462306a36Sopenharmony_ci	if (!(status & SD_STATUS_I))
78562306a36Sopenharmony_ci		return IRQ_NONE;	/* not ours */
78662306a36Sopenharmony_ci
78762306a36Sopenharmony_ci	if (status & SD_STATUS_SI)	/* SDIO */
78862306a36Sopenharmony_ci		mmc_signal_sdio_irq(host->mmc);
78962306a36Sopenharmony_ci
79062306a36Sopenharmony_ci	if (host->mrq && (status & STATUS_TIMEOUT)) {
79162306a36Sopenharmony_ci		if (status & SD_STATUS_RAT)
79262306a36Sopenharmony_ci			host->mrq->cmd->error = -ETIMEDOUT;
79362306a36Sopenharmony_ci		else if (status & SD_STATUS_DT)
79462306a36Sopenharmony_ci			host->mrq->data->error = -ETIMEDOUT;
79562306a36Sopenharmony_ci
79662306a36Sopenharmony_ci		/* In PIO mode, interrupts might still be enabled */
79762306a36Sopenharmony_ci		IRQ_OFF(host, SD_CONFIG_NE | SD_CONFIG_TH);
79862306a36Sopenharmony_ci
79962306a36Sopenharmony_ci		/* IRQ_OFF(host, SD_CONFIG_TH | SD_CONFIG_RA | SD_CONFIG_RF); */
80062306a36Sopenharmony_ci		tasklet_schedule(&host->finish_task);
80162306a36Sopenharmony_ci	}
80262306a36Sopenharmony_ci#if 0
80362306a36Sopenharmony_ci	else if (status & SD_STATUS_DD) {
80462306a36Sopenharmony_ci		/* Sometimes we get a DD before a NE in PIO mode */
80562306a36Sopenharmony_ci		if (!(host->flags & HOST_F_DMA) && (status & SD_STATUS_NE))
80662306a36Sopenharmony_ci			au1xmmc_receive_pio(host);
80762306a36Sopenharmony_ci		else {
80862306a36Sopenharmony_ci			au1xmmc_data_complete(host, status);
80962306a36Sopenharmony_ci			/* tasklet_schedule(&host->data_task); */
81062306a36Sopenharmony_ci		}
81162306a36Sopenharmony_ci	}
81262306a36Sopenharmony_ci#endif
81362306a36Sopenharmony_ci	else if (status & SD_STATUS_CR) {
81462306a36Sopenharmony_ci		if (host->status == HOST_S_CMD)
81562306a36Sopenharmony_ci			au1xmmc_cmd_complete(host, status);
81662306a36Sopenharmony_ci
81762306a36Sopenharmony_ci	} else if (!(host->flags & HOST_F_DMA)) {
81862306a36Sopenharmony_ci		if ((host->flags & HOST_F_XMIT) && (status & STATUS_DATA_OUT))
81962306a36Sopenharmony_ci			au1xmmc_send_pio(host);
82062306a36Sopenharmony_ci		else if ((host->flags & HOST_F_RECV) && (status & STATUS_DATA_IN))
82162306a36Sopenharmony_ci			au1xmmc_receive_pio(host);
82262306a36Sopenharmony_ci
82362306a36Sopenharmony_ci	} else if (status & 0x203F3C70) {
82462306a36Sopenharmony_ci			DBG("Unhandled status %8.8x\n", host->pdev->id,
82562306a36Sopenharmony_ci				status);
82662306a36Sopenharmony_ci	}
82762306a36Sopenharmony_ci
82862306a36Sopenharmony_ci	__raw_writel(status, HOST_STATUS(host));
82962306a36Sopenharmony_ci	wmb(); /* drain writebuffer */
83062306a36Sopenharmony_ci
83162306a36Sopenharmony_ci	return IRQ_HANDLED;
83262306a36Sopenharmony_ci}
83362306a36Sopenharmony_ci
83462306a36Sopenharmony_ci/* 8bit memory DMA device */
83562306a36Sopenharmony_cistatic dbdev_tab_t au1xmmc_mem_dbdev = {
83662306a36Sopenharmony_ci	.dev_id		= DSCR_CMD0_ALWAYS,
83762306a36Sopenharmony_ci	.dev_flags	= DEV_FLAGS_ANYUSE,
83862306a36Sopenharmony_ci	.dev_tsize	= 0,
83962306a36Sopenharmony_ci	.dev_devwidth	= 8,
84062306a36Sopenharmony_ci	.dev_physaddr	= 0x00000000,
84162306a36Sopenharmony_ci	.dev_intlevel	= 0,
84262306a36Sopenharmony_ci	.dev_intpolarity = 0,
84362306a36Sopenharmony_ci};
84462306a36Sopenharmony_cistatic int memid;
84562306a36Sopenharmony_ci
84662306a36Sopenharmony_cistatic void au1xmmc_dbdma_callback(int irq, void *dev_id)
84762306a36Sopenharmony_ci{
84862306a36Sopenharmony_ci	struct au1xmmc_host *host = (struct au1xmmc_host *)dev_id;
84962306a36Sopenharmony_ci
85062306a36Sopenharmony_ci	/* Avoid spurious interrupts */
85162306a36Sopenharmony_ci	if (!host->mrq)
85262306a36Sopenharmony_ci		return;
85362306a36Sopenharmony_ci
85462306a36Sopenharmony_ci	if (host->flags & HOST_F_STOP)
85562306a36Sopenharmony_ci		SEND_STOP(host);
85662306a36Sopenharmony_ci
85762306a36Sopenharmony_ci	tasklet_schedule(&host->data_task);
85862306a36Sopenharmony_ci}
85962306a36Sopenharmony_ci
86062306a36Sopenharmony_cistatic int au1xmmc_dbdma_init(struct au1xmmc_host *host)
86162306a36Sopenharmony_ci{
86262306a36Sopenharmony_ci	struct resource *res;
86362306a36Sopenharmony_ci	int txid, rxid;
86462306a36Sopenharmony_ci
86562306a36Sopenharmony_ci	res = platform_get_resource(host->pdev, IORESOURCE_DMA, 0);
86662306a36Sopenharmony_ci	if (!res)
86762306a36Sopenharmony_ci		return -ENODEV;
86862306a36Sopenharmony_ci	txid = res->start;
86962306a36Sopenharmony_ci
87062306a36Sopenharmony_ci	res = platform_get_resource(host->pdev, IORESOURCE_DMA, 1);
87162306a36Sopenharmony_ci	if (!res)
87262306a36Sopenharmony_ci		return -ENODEV;
87362306a36Sopenharmony_ci	rxid = res->start;
87462306a36Sopenharmony_ci
87562306a36Sopenharmony_ci	if (!memid)
87662306a36Sopenharmony_ci		return -ENODEV;
87762306a36Sopenharmony_ci
87862306a36Sopenharmony_ci	host->tx_chan = au1xxx_dbdma_chan_alloc(memid, txid,
87962306a36Sopenharmony_ci				au1xmmc_dbdma_callback, (void *)host);
88062306a36Sopenharmony_ci	if (!host->tx_chan) {
88162306a36Sopenharmony_ci		dev_err(&host->pdev->dev, "cannot allocate TX DMA\n");
88262306a36Sopenharmony_ci		return -ENODEV;
88362306a36Sopenharmony_ci	}
88462306a36Sopenharmony_ci
88562306a36Sopenharmony_ci	host->rx_chan = au1xxx_dbdma_chan_alloc(rxid, memid,
88662306a36Sopenharmony_ci				au1xmmc_dbdma_callback, (void *)host);
88762306a36Sopenharmony_ci	if (!host->rx_chan) {
88862306a36Sopenharmony_ci		dev_err(&host->pdev->dev, "cannot allocate RX DMA\n");
88962306a36Sopenharmony_ci		au1xxx_dbdma_chan_free(host->tx_chan);
89062306a36Sopenharmony_ci		return -ENODEV;
89162306a36Sopenharmony_ci	}
89262306a36Sopenharmony_ci
89362306a36Sopenharmony_ci	au1xxx_dbdma_set_devwidth(host->tx_chan, 8);
89462306a36Sopenharmony_ci	au1xxx_dbdma_set_devwidth(host->rx_chan, 8);
89562306a36Sopenharmony_ci
89662306a36Sopenharmony_ci	au1xxx_dbdma_ring_alloc(host->tx_chan, AU1XMMC_DESCRIPTOR_COUNT);
89762306a36Sopenharmony_ci	au1xxx_dbdma_ring_alloc(host->rx_chan, AU1XMMC_DESCRIPTOR_COUNT);
89862306a36Sopenharmony_ci
89962306a36Sopenharmony_ci	/* DBDMA is good to go */
90062306a36Sopenharmony_ci	host->flags |= HOST_F_DMA | HOST_F_DBDMA;
90162306a36Sopenharmony_ci
90262306a36Sopenharmony_ci	return 0;
90362306a36Sopenharmony_ci}
90462306a36Sopenharmony_ci
90562306a36Sopenharmony_cistatic void au1xmmc_dbdma_shutdown(struct au1xmmc_host *host)
90662306a36Sopenharmony_ci{
90762306a36Sopenharmony_ci	if (host->flags & HOST_F_DMA) {
90862306a36Sopenharmony_ci		host->flags &= ~HOST_F_DMA;
90962306a36Sopenharmony_ci		au1xxx_dbdma_chan_free(host->tx_chan);
91062306a36Sopenharmony_ci		au1xxx_dbdma_chan_free(host->rx_chan);
91162306a36Sopenharmony_ci	}
91262306a36Sopenharmony_ci}
91362306a36Sopenharmony_ci
91462306a36Sopenharmony_cistatic void au1xmmc_enable_sdio_irq(struct mmc_host *mmc, int en)
91562306a36Sopenharmony_ci{
91662306a36Sopenharmony_ci	struct au1xmmc_host *host = mmc_priv(mmc);
91762306a36Sopenharmony_ci
91862306a36Sopenharmony_ci	if (en)
91962306a36Sopenharmony_ci		IRQ_ON(host, SD_CONFIG_SI);
92062306a36Sopenharmony_ci	else
92162306a36Sopenharmony_ci		IRQ_OFF(host, SD_CONFIG_SI);
92262306a36Sopenharmony_ci}
92362306a36Sopenharmony_ci
92462306a36Sopenharmony_cistatic const struct mmc_host_ops au1xmmc_ops = {
92562306a36Sopenharmony_ci	.request	= au1xmmc_request,
92662306a36Sopenharmony_ci	.set_ios	= au1xmmc_set_ios,
92762306a36Sopenharmony_ci	.get_ro		= au1xmmc_card_readonly,
92862306a36Sopenharmony_ci	.get_cd		= au1xmmc_card_inserted,
92962306a36Sopenharmony_ci	.enable_sdio_irq = au1xmmc_enable_sdio_irq,
93062306a36Sopenharmony_ci};
93162306a36Sopenharmony_ci
93262306a36Sopenharmony_cistatic int au1xmmc_probe(struct platform_device *pdev)
93362306a36Sopenharmony_ci{
93462306a36Sopenharmony_ci	struct mmc_host *mmc;
93562306a36Sopenharmony_ci	struct au1xmmc_host *host;
93662306a36Sopenharmony_ci	struct resource *r;
93762306a36Sopenharmony_ci	int ret, iflag;
93862306a36Sopenharmony_ci
93962306a36Sopenharmony_ci	mmc = mmc_alloc_host(sizeof(struct au1xmmc_host), &pdev->dev);
94062306a36Sopenharmony_ci	if (!mmc) {
94162306a36Sopenharmony_ci		dev_err(&pdev->dev, "no memory for mmc_host\n");
94262306a36Sopenharmony_ci		ret = -ENOMEM;
94362306a36Sopenharmony_ci		goto out0;
94462306a36Sopenharmony_ci	}
94562306a36Sopenharmony_ci
94662306a36Sopenharmony_ci	host = mmc_priv(mmc);
94762306a36Sopenharmony_ci	host->mmc = mmc;
94862306a36Sopenharmony_ci	host->platdata = pdev->dev.platform_data;
94962306a36Sopenharmony_ci	host->pdev = pdev;
95062306a36Sopenharmony_ci
95162306a36Sopenharmony_ci	ret = -ENODEV;
95262306a36Sopenharmony_ci	r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
95362306a36Sopenharmony_ci	if (!r) {
95462306a36Sopenharmony_ci		dev_err(&pdev->dev, "no mmio defined\n");
95562306a36Sopenharmony_ci		goto out1;
95662306a36Sopenharmony_ci	}
95762306a36Sopenharmony_ci
95862306a36Sopenharmony_ci	host->ioarea = request_mem_region(r->start, resource_size(r),
95962306a36Sopenharmony_ci					   pdev->name);
96062306a36Sopenharmony_ci	if (!host->ioarea) {
96162306a36Sopenharmony_ci		dev_err(&pdev->dev, "mmio already in use\n");
96262306a36Sopenharmony_ci		goto out1;
96362306a36Sopenharmony_ci	}
96462306a36Sopenharmony_ci
96562306a36Sopenharmony_ci	host->iobase = ioremap(r->start, 0x3c);
96662306a36Sopenharmony_ci	if (!host->iobase) {
96762306a36Sopenharmony_ci		dev_err(&pdev->dev, "cannot remap mmio\n");
96862306a36Sopenharmony_ci		goto out2;
96962306a36Sopenharmony_ci	}
97062306a36Sopenharmony_ci
97162306a36Sopenharmony_ci	host->irq = platform_get_irq(pdev, 0);
97262306a36Sopenharmony_ci	if (host->irq < 0) {
97362306a36Sopenharmony_ci		ret = host->irq;
97462306a36Sopenharmony_ci		goto out3;
97562306a36Sopenharmony_ci	}
97662306a36Sopenharmony_ci
97762306a36Sopenharmony_ci	mmc->ops = &au1xmmc_ops;
97862306a36Sopenharmony_ci
97962306a36Sopenharmony_ci	mmc->f_min =   450000;
98062306a36Sopenharmony_ci	mmc->f_max = 24000000;
98162306a36Sopenharmony_ci
98262306a36Sopenharmony_ci	mmc->max_blk_size = 2048;
98362306a36Sopenharmony_ci	mmc->max_blk_count = 512;
98462306a36Sopenharmony_ci
98562306a36Sopenharmony_ci	mmc->ocr_avail = AU1XMMC_OCR;
98662306a36Sopenharmony_ci	mmc->caps = MMC_CAP_4_BIT_DATA | MMC_CAP_SDIO_IRQ;
98762306a36Sopenharmony_ci	mmc->max_segs = AU1XMMC_DESCRIPTOR_COUNT;
98862306a36Sopenharmony_ci
98962306a36Sopenharmony_ci	iflag = IRQF_SHARED;	/* Au1100/Au1200: one int for both ctrls */
99062306a36Sopenharmony_ci
99162306a36Sopenharmony_ci	switch (alchemy_get_cputype()) {
99262306a36Sopenharmony_ci	case ALCHEMY_CPU_AU1100:
99362306a36Sopenharmony_ci		mmc->max_seg_size = AU1100_MMC_DESCRIPTOR_SIZE;
99462306a36Sopenharmony_ci		break;
99562306a36Sopenharmony_ci	case ALCHEMY_CPU_AU1200:
99662306a36Sopenharmony_ci		mmc->max_seg_size = AU1200_MMC_DESCRIPTOR_SIZE;
99762306a36Sopenharmony_ci		break;
99862306a36Sopenharmony_ci	case ALCHEMY_CPU_AU1300:
99962306a36Sopenharmony_ci		iflag = 0;	/* nothing is shared */
100062306a36Sopenharmony_ci		mmc->max_seg_size = AU1200_MMC_DESCRIPTOR_SIZE;
100162306a36Sopenharmony_ci		mmc->f_max = 52000000;
100262306a36Sopenharmony_ci		if (host->ioarea->start == AU1100_SD0_PHYS_ADDR)
100362306a36Sopenharmony_ci			mmc->caps |= MMC_CAP_8_BIT_DATA;
100462306a36Sopenharmony_ci		break;
100562306a36Sopenharmony_ci	}
100662306a36Sopenharmony_ci
100762306a36Sopenharmony_ci	ret = request_irq(host->irq, au1xmmc_irq, iflag, DRIVER_NAME, host);
100862306a36Sopenharmony_ci	if (ret) {
100962306a36Sopenharmony_ci		dev_err(&pdev->dev, "cannot grab IRQ\n");
101062306a36Sopenharmony_ci		goto out3;
101162306a36Sopenharmony_ci	}
101262306a36Sopenharmony_ci
101362306a36Sopenharmony_ci	host->clk = clk_get(&pdev->dev, ALCHEMY_PERIPH_CLK);
101462306a36Sopenharmony_ci	if (IS_ERR(host->clk)) {
101562306a36Sopenharmony_ci		dev_err(&pdev->dev, "cannot find clock\n");
101662306a36Sopenharmony_ci		ret = PTR_ERR(host->clk);
101762306a36Sopenharmony_ci		goto out_irq;
101862306a36Sopenharmony_ci	}
101962306a36Sopenharmony_ci
102062306a36Sopenharmony_ci	ret = clk_prepare_enable(host->clk);
102162306a36Sopenharmony_ci	if (ret) {
102262306a36Sopenharmony_ci		dev_err(&pdev->dev, "cannot enable clock\n");
102362306a36Sopenharmony_ci		goto out_clk;
102462306a36Sopenharmony_ci	}
102562306a36Sopenharmony_ci
102662306a36Sopenharmony_ci	host->status = HOST_S_IDLE;
102762306a36Sopenharmony_ci
102862306a36Sopenharmony_ci	/* board-specific carddetect setup, if any */
102962306a36Sopenharmony_ci	if (host->platdata && host->platdata->cd_setup) {
103062306a36Sopenharmony_ci		ret = host->platdata->cd_setup(mmc, 1);
103162306a36Sopenharmony_ci		if (ret) {
103262306a36Sopenharmony_ci			dev_warn(&pdev->dev, "board CD setup failed\n");
103362306a36Sopenharmony_ci			mmc->caps |= MMC_CAP_NEEDS_POLL;
103462306a36Sopenharmony_ci		}
103562306a36Sopenharmony_ci	} else
103662306a36Sopenharmony_ci		mmc->caps |= MMC_CAP_NEEDS_POLL;
103762306a36Sopenharmony_ci
103862306a36Sopenharmony_ci	/* platform may not be able to use all advertised caps */
103962306a36Sopenharmony_ci	if (host->platdata)
104062306a36Sopenharmony_ci		mmc->caps &= ~(host->platdata->mask_host_caps);
104162306a36Sopenharmony_ci
104262306a36Sopenharmony_ci	tasklet_setup(&host->data_task, au1xmmc_tasklet_data);
104362306a36Sopenharmony_ci
104462306a36Sopenharmony_ci	tasklet_setup(&host->finish_task, au1xmmc_tasklet_finish);
104562306a36Sopenharmony_ci
104662306a36Sopenharmony_ci	if (has_dbdma()) {
104762306a36Sopenharmony_ci		ret = au1xmmc_dbdma_init(host);
104862306a36Sopenharmony_ci		if (ret)
104962306a36Sopenharmony_ci			pr_info(DRIVER_NAME ": DBDMA init failed; using PIO\n");
105062306a36Sopenharmony_ci	}
105162306a36Sopenharmony_ci
105262306a36Sopenharmony_ci#ifdef CONFIG_LEDS_CLASS
105362306a36Sopenharmony_ci	if (host->platdata && host->platdata->led) {
105462306a36Sopenharmony_ci		struct led_classdev *led = host->platdata->led;
105562306a36Sopenharmony_ci		led->name = mmc_hostname(mmc);
105662306a36Sopenharmony_ci		led->brightness = LED_OFF;
105762306a36Sopenharmony_ci		led->default_trigger = mmc_hostname(mmc);
105862306a36Sopenharmony_ci		ret = led_classdev_register(mmc_dev(mmc), led);
105962306a36Sopenharmony_ci		if (ret)
106062306a36Sopenharmony_ci			goto out5;
106162306a36Sopenharmony_ci	}
106262306a36Sopenharmony_ci#endif
106362306a36Sopenharmony_ci
106462306a36Sopenharmony_ci	au1xmmc_reset_controller(host);
106562306a36Sopenharmony_ci
106662306a36Sopenharmony_ci	ret = mmc_add_host(mmc);
106762306a36Sopenharmony_ci	if (ret) {
106862306a36Sopenharmony_ci		dev_err(&pdev->dev, "cannot add mmc host\n");
106962306a36Sopenharmony_ci		goto out6;
107062306a36Sopenharmony_ci	}
107162306a36Sopenharmony_ci
107262306a36Sopenharmony_ci	platform_set_drvdata(pdev, host);
107362306a36Sopenharmony_ci
107462306a36Sopenharmony_ci	pr_info(DRIVER_NAME ": MMC Controller %d set up at %p"
107562306a36Sopenharmony_ci		" (mode=%s)\n", pdev->id, host->iobase,
107662306a36Sopenharmony_ci		host->flags & HOST_F_DMA ? "dma" : "pio");
107762306a36Sopenharmony_ci
107862306a36Sopenharmony_ci	return 0;	/* all ok */
107962306a36Sopenharmony_ci
108062306a36Sopenharmony_ciout6:
108162306a36Sopenharmony_ci#ifdef CONFIG_LEDS_CLASS
108262306a36Sopenharmony_ci	if (host->platdata && host->platdata->led)
108362306a36Sopenharmony_ci		led_classdev_unregister(host->platdata->led);
108462306a36Sopenharmony_ciout5:
108562306a36Sopenharmony_ci#endif
108662306a36Sopenharmony_ci	__raw_writel(0, HOST_ENABLE(host));
108762306a36Sopenharmony_ci	__raw_writel(0, HOST_CONFIG(host));
108862306a36Sopenharmony_ci	__raw_writel(0, HOST_CONFIG2(host));
108962306a36Sopenharmony_ci	wmb(); /* drain writebuffer */
109062306a36Sopenharmony_ci
109162306a36Sopenharmony_ci	if (host->flags & HOST_F_DBDMA)
109262306a36Sopenharmony_ci		au1xmmc_dbdma_shutdown(host);
109362306a36Sopenharmony_ci
109462306a36Sopenharmony_ci	tasklet_kill(&host->data_task);
109562306a36Sopenharmony_ci	tasklet_kill(&host->finish_task);
109662306a36Sopenharmony_ci
109762306a36Sopenharmony_ci	if (host->platdata && host->platdata->cd_setup &&
109862306a36Sopenharmony_ci	    !(mmc->caps & MMC_CAP_NEEDS_POLL))
109962306a36Sopenharmony_ci		host->platdata->cd_setup(mmc, 0);
110062306a36Sopenharmony_ci
110162306a36Sopenharmony_ci	clk_disable_unprepare(host->clk);
110262306a36Sopenharmony_ciout_clk:
110362306a36Sopenharmony_ci	clk_put(host->clk);
110462306a36Sopenharmony_ciout_irq:
110562306a36Sopenharmony_ci	free_irq(host->irq, host);
110662306a36Sopenharmony_ciout3:
110762306a36Sopenharmony_ci	iounmap((void *)host->iobase);
110862306a36Sopenharmony_ciout2:
110962306a36Sopenharmony_ci	release_resource(host->ioarea);
111062306a36Sopenharmony_ci	kfree(host->ioarea);
111162306a36Sopenharmony_ciout1:
111262306a36Sopenharmony_ci	mmc_free_host(mmc);
111362306a36Sopenharmony_ciout0:
111462306a36Sopenharmony_ci	return ret;
111562306a36Sopenharmony_ci}
111662306a36Sopenharmony_ci
111762306a36Sopenharmony_cistatic void au1xmmc_remove(struct platform_device *pdev)
111862306a36Sopenharmony_ci{
111962306a36Sopenharmony_ci	struct au1xmmc_host *host = platform_get_drvdata(pdev);
112062306a36Sopenharmony_ci
112162306a36Sopenharmony_ci	if (host) {
112262306a36Sopenharmony_ci		mmc_remove_host(host->mmc);
112362306a36Sopenharmony_ci
112462306a36Sopenharmony_ci#ifdef CONFIG_LEDS_CLASS
112562306a36Sopenharmony_ci		if (host->platdata && host->platdata->led)
112662306a36Sopenharmony_ci			led_classdev_unregister(host->platdata->led);
112762306a36Sopenharmony_ci#endif
112862306a36Sopenharmony_ci
112962306a36Sopenharmony_ci		if (host->platdata && host->platdata->cd_setup &&
113062306a36Sopenharmony_ci		    !(host->mmc->caps & MMC_CAP_NEEDS_POLL))
113162306a36Sopenharmony_ci			host->platdata->cd_setup(host->mmc, 0);
113262306a36Sopenharmony_ci
113362306a36Sopenharmony_ci		__raw_writel(0, HOST_ENABLE(host));
113462306a36Sopenharmony_ci		__raw_writel(0, HOST_CONFIG(host));
113562306a36Sopenharmony_ci		__raw_writel(0, HOST_CONFIG2(host));
113662306a36Sopenharmony_ci		wmb(); /* drain writebuffer */
113762306a36Sopenharmony_ci
113862306a36Sopenharmony_ci		tasklet_kill(&host->data_task);
113962306a36Sopenharmony_ci		tasklet_kill(&host->finish_task);
114062306a36Sopenharmony_ci
114162306a36Sopenharmony_ci		if (host->flags & HOST_F_DBDMA)
114262306a36Sopenharmony_ci			au1xmmc_dbdma_shutdown(host);
114362306a36Sopenharmony_ci
114462306a36Sopenharmony_ci		au1xmmc_set_power(host, 0);
114562306a36Sopenharmony_ci
114662306a36Sopenharmony_ci		clk_disable_unprepare(host->clk);
114762306a36Sopenharmony_ci		clk_put(host->clk);
114862306a36Sopenharmony_ci
114962306a36Sopenharmony_ci		free_irq(host->irq, host);
115062306a36Sopenharmony_ci		iounmap((void *)host->iobase);
115162306a36Sopenharmony_ci		release_resource(host->ioarea);
115262306a36Sopenharmony_ci		kfree(host->ioarea);
115362306a36Sopenharmony_ci
115462306a36Sopenharmony_ci		mmc_free_host(host->mmc);
115562306a36Sopenharmony_ci	}
115662306a36Sopenharmony_ci}
115762306a36Sopenharmony_ci
115862306a36Sopenharmony_ci#ifdef CONFIG_PM
115962306a36Sopenharmony_cistatic int au1xmmc_suspend(struct platform_device *pdev, pm_message_t state)
116062306a36Sopenharmony_ci{
116162306a36Sopenharmony_ci	struct au1xmmc_host *host = platform_get_drvdata(pdev);
116262306a36Sopenharmony_ci
116362306a36Sopenharmony_ci	__raw_writel(0, HOST_CONFIG2(host));
116462306a36Sopenharmony_ci	__raw_writel(0, HOST_CONFIG(host));
116562306a36Sopenharmony_ci	__raw_writel(0xffffffff, HOST_STATUS(host));
116662306a36Sopenharmony_ci	__raw_writel(0, HOST_ENABLE(host));
116762306a36Sopenharmony_ci	wmb(); /* drain writebuffer */
116862306a36Sopenharmony_ci
116962306a36Sopenharmony_ci	return 0;
117062306a36Sopenharmony_ci}
117162306a36Sopenharmony_ci
117262306a36Sopenharmony_cistatic int au1xmmc_resume(struct platform_device *pdev)
117362306a36Sopenharmony_ci{
117462306a36Sopenharmony_ci	struct au1xmmc_host *host = platform_get_drvdata(pdev);
117562306a36Sopenharmony_ci
117662306a36Sopenharmony_ci	au1xmmc_reset_controller(host);
117762306a36Sopenharmony_ci
117862306a36Sopenharmony_ci	return 0;
117962306a36Sopenharmony_ci}
118062306a36Sopenharmony_ci#else
118162306a36Sopenharmony_ci#define au1xmmc_suspend NULL
118262306a36Sopenharmony_ci#define au1xmmc_resume NULL
118362306a36Sopenharmony_ci#endif
118462306a36Sopenharmony_ci
118562306a36Sopenharmony_cistatic struct platform_driver au1xmmc_driver = {
118662306a36Sopenharmony_ci	.probe         = au1xmmc_probe,
118762306a36Sopenharmony_ci	.remove_new    = au1xmmc_remove,
118862306a36Sopenharmony_ci	.suspend       = au1xmmc_suspend,
118962306a36Sopenharmony_ci	.resume        = au1xmmc_resume,
119062306a36Sopenharmony_ci	.driver        = {
119162306a36Sopenharmony_ci		.name  = DRIVER_NAME,
119262306a36Sopenharmony_ci		.probe_type = PROBE_PREFER_ASYNCHRONOUS,
119362306a36Sopenharmony_ci	},
119462306a36Sopenharmony_ci};
119562306a36Sopenharmony_ci
119662306a36Sopenharmony_cistatic int __init au1xmmc_init(void)
119762306a36Sopenharmony_ci{
119862306a36Sopenharmony_ci	if (has_dbdma()) {
119962306a36Sopenharmony_ci		/* DSCR_CMD0_ALWAYS has a stride of 32 bits, we need a stride
120062306a36Sopenharmony_ci		* of 8 bits.  And since devices are shared, we need to create
120162306a36Sopenharmony_ci		* our own to avoid freaking out other devices.
120262306a36Sopenharmony_ci		*/
120362306a36Sopenharmony_ci		memid = au1xxx_ddma_add_device(&au1xmmc_mem_dbdev);
120462306a36Sopenharmony_ci		if (!memid)
120562306a36Sopenharmony_ci			pr_err("au1xmmc: cannot add memory dbdma\n");
120662306a36Sopenharmony_ci	}
120762306a36Sopenharmony_ci	return platform_driver_register(&au1xmmc_driver);
120862306a36Sopenharmony_ci}
120962306a36Sopenharmony_ci
121062306a36Sopenharmony_cistatic void __exit au1xmmc_exit(void)
121162306a36Sopenharmony_ci{
121262306a36Sopenharmony_ci	if (has_dbdma() && memid)
121362306a36Sopenharmony_ci		au1xxx_ddma_del_device(memid);
121462306a36Sopenharmony_ci
121562306a36Sopenharmony_ci	platform_driver_unregister(&au1xmmc_driver);
121662306a36Sopenharmony_ci}
121762306a36Sopenharmony_ci
121862306a36Sopenharmony_cimodule_init(au1xmmc_init);
121962306a36Sopenharmony_cimodule_exit(au1xmmc_exit);
122062306a36Sopenharmony_ci
122162306a36Sopenharmony_ciMODULE_AUTHOR("Advanced Micro Devices, Inc");
122262306a36Sopenharmony_ciMODULE_DESCRIPTION("MMC/SD driver for the Alchemy Au1XXX");
122362306a36Sopenharmony_ciMODULE_LICENSE("GPL");
122462306a36Sopenharmony_ciMODULE_ALIAS("platform:au1xxx-mmc");
1225