www.pudn.com > s3c2440_kernel2.4.18_module_mmc.rar > mmc_bus.c


/*
 * drivers/mmc/mmc_bus.c
 *
 * MMC bus protocol interfaces 
 *
 * Copyright (C) 2001-2003 MIZI Research, Inc.
 *
 * Author: Yong-iL Joh 
 * $Id: mmc_bus.c,v 1.2 2004/01/26 08:29:56 laputa Exp $
 *
 * Revision History:
 *
 * 2001-XX-XX Yong-iL Joh 
 * - initial release
 *
 * 2002-07-25 Chan Gyun Jeong 
 * - code cleanup 
 *
 * 2002-12-07 Chan Gyun Jeong 
 * - rough restructuring for S3C2410 SD Controller
 *
 */

#include 
#include 
#include 
#include 
#include 
#include 
#ifdef CONFIG_PROC_FS
#include 
#endif

#include "mmc.h"

#define MMC_RETRIES_MAX		20
#define MMC_RETRIES_SD_CHECK	2

#define MMC_OP_COND_DELAY	set_current_state(TASK_INTERRUPTIBLE); \
				schedule_timeout(HZ/10)

static DECLARE_MUTEX(mmc_slots_mutex);
static struct mmc_slot *mmc_slots[MAX_MMC_SLOTS];
static struct mmc_notifier *mmc_notifiers;

const int mmc_res_len[] = {
	0,
	MMC_RES_LEN_SHORT,
	MMC_RES_LEN_SHORT,
	MMC_RES_LEN_LONG,
	MMC_RES_LEN_SHORT,
	MMC_RES_LEN_SHORT,
	MMC_RES_LEN_SHORT,
	MMC_RES_LEN_SHORT,
};

static int mmc_do_transfer_1blk(struct mmc_slot *slot, int rd, u_long from, 
				u_char *buf)
{
	int ret;
	struct mmc_cmd cmd;

	DEBUG2(3, "[%s] from: %lu, read: %d\n", __FUNCTION__, from, rd);

#if 0
	/* I don't know how to do
	   if there exists difference between read_len and write_len */
	if (!rd && (slot->read_len != slot->write_len))
		return -EINVAL;

	if ((from % slot->read_len) || (from >= slot->sizes))
		return -EINVAL;
#endif

	ret = down_interruptible(&slot->mutex);
	if (ret < 0) {
		return ret;
	}

	/* check card-state. if card-state != StandBy, return BUSY */
	cmd.cmd = MMC_CMD13;
	cmd.arg = (slot->rca << 16) & 0xffff0000;
	cmd.res_type = MMC_RES_TYPE_R1;
	cmd.res_flag = 0;
	cmd.t_res = MMC_TIME_NCR_MAX;
	cmd.t_fin = MMC_TIME_NRC_MIN;
	ret = slot->send_cmd(slot, &cmd);
	DEBUG2(2, "sent CMD13\n");
	if (ret < 0) {
		DEBUG2(1, "CMD13 failed, ret = %d\n", ret);
		goto err;
	}
	mmc_str2r1(&(slot->r1), cmd.res);
	if (slot->r1 & (R1_cc_err | R1_err)) {
		DEBUG2(1, "CMD13 failed, R1(0x%08x)\n", slot->r1);
		ret = -EIO;
		goto err;
	}
	if (!(slot->r1 & STATE_STBY)) {
		DEBUG2(1, "CMD13 failed, R1(0x%08x): Busy\n", slot->r1);
		ret = -EBUSY;
		goto err;
	}

	/* SELECT CARD & set card state from Stand-by to Transfer */
	cmd.cmd = MMC_CMD7;
	cmd.arg = (slot->rca << 16) & 0xffff0000;
	cmd.res_type = MMC_RES_TYPE_R1;
	cmd.res_flag = 0;
	cmd.t_res = MMC_TIME_NCR_MAX;
	cmd.t_fin = MMC_TIME_NRC_MIN;
	ret = slot->send_cmd(slot, &cmd);
	DEBUG2(2, "sent CMD7\n");
	if (ret < 0) {
		DEBUG2(1, "CMD7 failed, ret = %d\n", ret);
		goto err;
	}
	mmc_str2r1(&(slot->r1), cmd.res);
	if (slot->r1 & (R1_cc_err | R1_err)) {
		DEBUG2(1, "CMD7 failed, R1(0x%08x)\n", slot->r1);
		ret = -EIO;
		goto err;
	}

	ret = 0;
	if (rd) {
		/* adtc, 31:0 dadr, R1, READ_SINGLE_BLOCK */
		/* set card state from Transfer to Sending-data */
		cmd.cmd = MMC_CMD17;
		cmd.arg = from;
		cmd.res_type = MMC_RES_TYPE_R1;
		cmd.res_flag = MMC_RES_FLAG_RDATA;
		cmd.data = buf;
		cmd.data_len = slot->read_len;
		cmd.t_res = MMC_TIME_NCR_MAX;
		cmd.t_fin = MMC_TIME_NAC_MIN;
		ret = slot->send_cmd(slot, &cmd);
		DEBUG2(2, "sent CMD17\n");
		if (ret < 0) {
			DEBUG2(1, "CMD17 failed, ret = %d\n", ret);
		}
	} else {
		/* adtc, 31:0 dadr, R1, WRITE_SINGLE_BLOCK */
		/* set card state from Transfer to Receive-data */
		cmd.cmd = MMC_CMD24;
		cmd.arg = from;
		cmd.res_type = MMC_RES_TYPE_R1;
		cmd.res_flag = MMC_RES_FLAG_WDATA;
		cmd.data = buf;
		cmd.data_len = slot->write_len;
		cmd.t_res = MMC_TIME_NCR_MAX;
		cmd.t_fin = MMC_TIME_NWR_MIN;
		ret = slot->send_cmd(slot, &cmd);
		DEBUG2(2, "sent CMD24\n");
		if (ret < 0) {
			DEBUG2(1, "CMD24 failed, ret = %d\n", ret);
		}
	}

	/* DESELECT CARD */
	cmd.cmd = MMC_CMD7;
	cmd.arg = 0;
	cmd.res_type = MMC_RES_TYPE_NONE;
	cmd.res_flag = 0;
	cmd.t_fin = MMC_TIME_NRC_MIN;
	slot->send_cmd(slot, &cmd);
	DEBUG2(2, "sent CMD7(DESELECT)\n");

 err:
	up(&slot->mutex);
	return ret;
}

inline static int mmc_reset(struct mmc_slot *slot)
{
	int ret;
	struct mmc_cmd cmd;

	/* go IDLE state */
	cmd.cmd = MMC_CMD0;
	cmd.arg = 0;
	cmd.res_type = MMC_RES_TYPE_NONE;
	cmd.res_flag = 0;
	cmd.t_fin = MMC_TIME_NCC_MIN;
	ret = slot->send_cmd(slot, &cmd);
	DEBUG2(2, "sent CMD0\n");
	if (ret < 0) {
		DEBUG2(1, "CMD0 failed, ret = %d\n", ret);
		return ret;
	}

	/* wait for reset */
	if (slot->wait_for_reset) {
		slot->wait_for_reset(slot);
	}

	return 0;
}

inline static int mmc_identify(struct mmc_slot *slot)
{
	int ret;
	int retries = MMC_RETRIES_MAX;
	struct mmc_cmd cmd;

 retry_send_op_cond:
	/* SEND_OP_COND */
	slot->ocr = MMC_VDD_27_36;
	cmd.cmd = MMC_CMD1;
	cmd.arg = slot->ocr;
	cmd.res_type = MMC_RES_TYPE_R3;
	cmd.res_flag = MMC_RES_FLAG_NOCRC;
	cmd.t_res = MMC_TIME_NCR_MAX;
	cmd.t_fin = MMC_TIME_NRC_MIN;
	ret = slot->send_cmd(slot, &cmd);
	DEBUG2(2, "sent CMD1\n");
	if (ret < 0) {
		DEBUG2(1, "CMD1 failed, ret = %d\n", ret);
		if (ret != -ENODEV && retries--) {
			ret = mmc_reset(slot);
			if (ret < 0) {
				return ret;
			}
			MMC_OP_COND_DELAY;
			goto retry_send_op_cond;
		}
		return -ENODEV;
	}
	mmc_str2ocr(&(slot->ocr), cmd.res);
	if (!(slot->ocr & MMC_nCARD_BUSY)) {
		DEBUG2(1, "CMD1 failed, ocr = 0x%08x\n", slot->ocr);
		if (retries--) {
			MMC_OP_COND_DELAY;
			goto retry_send_op_cond;
		}
		return -ENODEV;
	}
	DEBUG2(3, "ocr = %08x\n", slot->ocr);

	/* ALL_SEND_CID */
	cmd.cmd = MMC_CMD2;
	cmd.arg = 0;
	cmd.res_type = MMC_RES_TYPE_R2;
	cmd.res_flag = 0;
	cmd.t_res = MMC_TIME_NID_MAX;
	cmd.t_fin = MMC_TIME_NRC_MIN;
	ret = slot->send_cmd(slot, &cmd);
	DEBUG2(2, "sent CMD2\n");
	if (ret < 0) {
		DEBUG2(1, "CMD2 failed, ret = %d\n", ret);
		return ret;
	}
	mmc_str2cid(&(slot->cid), cmd.res);
	DEBUG2(3, "mid = 0x%02x, oid = 0x%04x, pnm = %s, prv = %d\n"
	       "psn = 0x%08x, mdt = 0x%02x\n",
	       slot->cid.mid, slot->cid.oid, slot->cid.pnm,
	       slot->cid.prv, slot->cid.psn, slot->cid.mdt);

	/* SET_RELATIVE_ADDR, Stand-by State */
	slot->rca = 1 + slot->id; /* slot id begins at 0 */
	cmd.cmd = MMC_CMD3;
	cmd.arg = (slot->rca << 16) & 0xffff0000;
	cmd.res_type = MMC_RES_TYPE_R1;
	cmd.res_flag = 0;
	cmd.t_res = MMC_TIME_NCR_MAX;
	cmd.t_fin = MMC_TIME_NRC_MIN;
	ret = slot->send_cmd(slot, &cmd);
	DEBUG2(2, "sent CMD3\n");
	if (ret < 0) {
		DEBUG2(1, "CMD3 failed, ret = %d\n", ret);
		return ret;
	}
	mmc_str2r1(&(slot->r1), cmd.res);
	if (!(slot->r1 & STATE_IDENT)) {
		DEBUG2(1, "CMD3 failed, r1 = 0x%08x\n", slot->r1);
		return -ENODEV;
	}
	DEBUG2(3, "R1 = %08x\n", slot->r1);

	/* SEND_CSD, Stand-by State */
	cmd.cmd = MMC_CMD9;
	cmd.arg = (slot->rca << 16) & 0xffff0000;
	cmd.res_type = MMC_RES_TYPE_R2;
	cmd.res_flag = 0;
	cmd.t_res = MMC_TIME_NCR_MAX;
	cmd.t_fin = MMC_TIME_NRC_MIN;
	ret = slot->send_cmd(slot, &cmd);
	DEBUG2(2, "sent CMD9\n");
	if (ret < 0) {
		DEBUG2(1, "CMD9 failed, ret = %d\n", ret);
		return ret;
	}
	mmc_str2csd(&(slot->csd), cmd.res);
	DEBUG2(3, "csd(%d) spec(%d) taac.man(%d) taac.exp(%d) nsac(%d)\n"
	       "tran.man(%d) tran.exp(%d) ccc(%03x) read_len(%d)\n"
	       "read_part(%d) write_mis(%d) read_mis(%d) dsr(%d) c_size(%d)\n"
	       "vcc_r_min(%d) vcc_r_max(%d) vcc_w_min(%d) vcc_w_max(%d)\n"
	       "c_size_mult(%d) er_size(%d) er_grp_size(%d) wp_grp_size(%d)\n"
	       "wp_grp_en(%d) dflt_ecc(%d) r2w_factor(%d) write_len(%d)\n"
	       "write_part(%d) ffmt_grp(%d) copy(%d) perm_wp(%d) tmp_wp(%d)\n"
	       "ffmt(%d) ecc(%d)\n",
	       slot->csd.csd, slot->csd.spec_vers, slot->csd.taac.man, 
	       slot->csd.taac.exp, slot->csd.nsac, slot->csd.tran_speed.man, 
	       slot->csd.tran_speed.exp, slot->csd.ccc, slot->csd.read_len, 
	       slot->csd.read_part, slot->csd.write_mis, slot->csd.read_mis,
	       slot->csd.dsr, slot->csd.c_size, slot->csd.vcc_r_min, 
	       slot->csd.vcc_r_max, slot->csd.vcc_w_min, slot->csd.vcc_w_max,
	       slot->csd.c_size_mult, slot->csd.er_size, 
	       slot->csd.er_grp_size, slot->csd.wp_grp_size, 
	       slot->csd.wp_grp_en, slot->csd.dflt_ecc, slot->csd.r2w_factor, 
	       slot->csd.write_len, slot->csd.write_part, slot->csd.ffmt_grp,
	       slot->csd.copy, slot->csd.perm_wp, slot->csd.tmp_wp,
	       slot->csd.ffmt, slot->csd.ecc);

	/* Get CSD value */
	mmc_get_CSD_info(slot, &(slot->csd));

	/* Set high clock rate for the normal data transfer */
	slot->set_clock(slot, MMC_MMC_CLOCK_HIGH);

	return 0;
}

static int card_identify(struct mmc_slot *slot)
{
	int ret;
	int retry = MMC_RETRIES_SD_CHECK;
	struct mmc_cmd cmd;

	/* Initialize some slot variables */
	slot->rca = 0;

	/* Power up */
	if (slot->power_up) {
		slot->power_up(slot);
	}

	/* Set low clock rate for the indentification */
	slot->set_clock(slot, MMC_MMC_CLOCK_LOW);

 retry_card_check:
	/* go IDLE state */
	ret = mmc_reset(slot);
	if (ret < 0) {
		return ret;
	}

	/* check whether it's a MMC or a SD card. */
	/* APP_CMD */
	cmd.cmd = MMC_CMD55;
	cmd.arg = (slot->rca << 16) & 0xffff0000;
	cmd.res_type = MMC_RES_TYPE_R1;
	cmd.res_flag = 0;
	cmd.t_res = MMC_TIME_NCR_MAX;
	cmd.t_fin = MMC_TIME_NRC_MIN;
	ret = slot->send_cmd(slot, &cmd);
	DEBUG2(2, "sent CMD55\n");
	if (ret < 0) {
		if (ret == -ENODEV) {
			return ret;
		}
		if (retry--) {
			DEBUG2(1, "retry to check MMC or SD\n");
			goto retry_card_check;
		}
		DEBUG2(1, "identify MultiMediaCard\n");
		ret = mmc_identify(slot);
	} else {
                DEBUG2(1, " SD Memory Card IDentified. Use SDMC Driver\n");
                printk("SD Memory Card IDentified. Use SDMC Driver\n");
                ret = SD_CARD;
        }
                                                                                
	return ret;
}

#ifdef CONFIG_PROC_FS
static int mmc_read_proc(char *page, char **start, off_t off,
                         int count, int *eof, void *data)
{
	char *p = page;
	int len;
	struct mmc_slot *slot = (struct mmc_slot *)data;

	p += sprintf(p, "read-only\t: %s\n"
			"card type\t: %s\n"
			"product name\t: %s\n"
			"card size\t: %ldMB\n",
			slot->readonly ? "yes":"no", "MMC",
			slot->cid.pnm, 
			slot->size/(1024 * 1024));

	len = (p - page) - off;
	if (len < 0) len = 0;

	*eof = (len <= count) ? 1 : 0;
	*start = page + off;

	return len;
}
#endif

int add_mmc_device(struct mmc_slot *slot)
{
	int i;

	DEBUG2(2, "[%s] slot id: %d\n", __FUNCTION__, slot->id);

	down(&mmc_slots_mutex);
	slot->stat = 0;
	i = card_identify(slot);
	if (i) {
		up(&mmc_slots_mutex);
		return i;
	}

	init_MUTEX(&slot->mutex);
	slot->transfer1b = mmc_do_transfer_1blk;
	slot->stat = 1;

	for (i = 0; i < MAX_MMC_SLOTS; i++) {
		if (!mmc_slots[i]) {
			struct mmc_notifier *not=mmc_notifiers;

			mmc_slots[i] = slot;
			while (not) {
				(*(not->add))(slot);
				not = not->next;
                        }
			up(&mmc_slots_mutex);
			MOD_INC_USE_COUNT;
#ifdef CONFIG_PROC_FS
			create_proc_read_entry ("driver/mmc", 0, NULL, 
						mmc_read_proc, (void *)slot);
#endif
			return 0;
		}
	}
	up(&mmc_slots_mutex);
	return -ENOSPC;
}

int del_mmc_device(struct mmc_slot *slot)
{
	int i;

	DEBUG2(2, "[%s] slot id: %d\n", __FUNCTION__, slot->id);

	down(&mmc_slots_mutex);

	/* Power down */
	if (slot->power_down) {
		slot->power_down(slot);
	}

	for (i = 0; i < MAX_MMC_SLOTS; i++) {
		if (mmc_slots[i] == slot) {
			struct mmc_notifier *not=mmc_notifiers;

#ifdef CONFIG_PROC_FS
			remove_proc_entry ("driver/mmc", NULL);
#endif
			while (not) {
				(*(not->remove))(slot);
				not = not->next;
			}
			mmc_slots[i] = NULL;
			up (&mmc_slots_mutex);
			MOD_DEC_USE_COUNT;
			return 0;
		}
	}
	up(&mmc_slots_mutex);
	return -EINVAL;
}

int reidentify_mmc_device(struct mmc_slot *slot)
{
	int ret;

	down(&mmc_slots_mutex);
	ret = card_identify(slot);
	up(&mmc_slots_mutex);
	return ret;
}

void register_mmc_user(struct mmc_notifier *new)
{
	int i;

	down(&mmc_slots_mutex);
	new->next = mmc_notifiers;
	mmc_notifiers = new;

	MOD_INC_USE_COUNT;

	for (i=0; i< MAX_MMC_SLOTS; i++)
		if (mmc_slots[i])
			new->add(mmc_slots[i]);
	up(&mmc_slots_mutex);
}

int unregister_mmc_user(struct mmc_notifier *old)
{
	struct mmc_notifier **prev = &mmc_notifiers;
	struct mmc_notifier *cur;
	int i;

	down(&mmc_slots_mutex);
	while ((cur = *prev)) {
		if (cur == old) {
			*prev = cur->next;

			MOD_DEC_USE_COUNT;

			for (i=0; i< MAX_MMC_SLOTS; i++)
				if (mmc_slots[i])
					old->remove(mmc_slots[i]);

			up(&mmc_slots_mutex);
			return 0;
		}
		prev = &cur->next;
	}
	up(&mmc_slots_mutex);
	return 1;
}

static int __init init_mmc_bus(void)
{
	int i;

	for (i = 0; i < MAX_MMC_SLOTS; i++) {
		mmc_slots[i] = NULL;
	}
	mmc_notifiers = NULL;

	return 0;
}

static void __exit exit_mmc_bus(void)
{

}

module_init(init_mmc_bus);
module_exit(exit_mmc_bus);

EXPORT_SYMBOL(add_mmc_device);
EXPORT_SYMBOL(del_mmc_device);
EXPORT_SYMBOL(reidentify_mmc_device);
EXPORT_SYMBOL(register_mmc_user);
EXPORT_SYMBOL(unregister_mmc_user);
EXPORT_SYMBOL(mmc_res_len);

MODULE_AUTHOR("Yong-iL Joh ");
MODULE_LICENSE("Not GPL, Proprietary License");
MODULE_DESCRIPTION("MMC bus protocol interfaces");