omap24xx_mmc.c

来自「Linux Kernel 2.6.9 for OMAP1710」· C语言 代码 · 共 1,027 行 · 第 1/2 页

C
1,027
字号
	if (req->data == NULL) {		host->datadir = OMAP_MMC_DATADIR_NONE;		OMAP_MMC_WRITE(host->base, BLEN, 0);		OMAP_MMC_WRITE(host->base, NBLK, 0);		OMAP_MMC_WRITE(host->base, BUF, 0);		return;	}	DBG("MMC%d: Data xfer (%s %s), DTO %d cycles + %d ns, %d blocks of %d bytes\n", host->id, (req->data->flags & MMC_DATA_STREAM) ? "stream" : "block", (req->data->flags & MMC_DATA_WRITE) ? "write" : "read", req->data->timeout_clks, req->data->timeout_ns, req->data->blocks, 1 << req->data->blksz_bits);	/* Convert ns to clock cycles by assuming 20MHz frequency	 * 1 cycle at 20MHz = 500 ns	 */	timeout = req->data->timeout_clks + req->data->timeout_ns / 500;	if (timeout > 0xffff)		timeout = 0xffff;	OMAP_MMC_WRITE(host->base, DTO, timeout);	OMAP_MMC_WRITE(host->base, NBLK, req->data->blocks - 1);	OMAP_MMC_WRITE(host->base, BLEN, (1 << req->data->blksz_bits) - 1);	host->datadir = (req->data->flags & MMC_DATA_WRITE) ?	    OMAP_MMC_DATADIR_WRITE : OMAP_MMC_DATADIR_READ;	if (host->use_dma && mmc_omap_start_dma_transfer(host, req) == 0) {		host->buffer = NULL;		host->bytesleft = 0;	} else {		/* Revert to CPU copy */		OMAP_MMC_WRITE(host->base, BUF, 0x1f1f);		host->buffer = (u16 *) req->data->req->buffer;		host->bytesleft =		    req->data->blocks * (1 << req->data->blksz_bits);		host->dma_ch = -1;	}}static inline int is_broken_card(struct mmc_card *card){	int i;	struct mmc_cid *c = &card->cid;	static const struct broken_card_cid {		unsigned int manfid;		char prod_name[8];		unsigned char hwrev;		unsigned char fwrev;	} broken_cards[] = {		{	0x00150000, "\x30\x30\x30\x30\x30\x30\x15\x00", 0x06,			    0x03},};	for (i = 0; i < sizeof(broken_cards) / sizeof(broken_cards[0]); i++) {		const struct broken_card_cid *b = broken_cards + i;		if (b->manfid != c->manfid)			continue;		if (memcmp(b->prod_name, c->prod_name, sizeof(b->prod_name)) !=		    0)			continue;		if (b->hwrev != c->hwrev || b->fwrev != c->fwrev)			continue;		return 1;	}	return 0;}static void omap24xx_mmc_request(struct mmc_host *mmc, struct mmc_request *req){	struct mmc_omap_host *host = mmc_priv(mmc);	WARN_ON(host->mrq != NULL);	host->mrq = req;	/* Some cards (vendor left unnamed to protect the guilty) seem to	 * require this delay after power-up. Otherwise we'll get mysterious	 * data timeouts. */	if (req->cmd->opcode == MMC_SEND_CSD) {		struct mmc_card *card;		int broken_present = 0;		list_for_each_entry(card, &mmc->cards, node) {			if (is_broken_card(card)) {				broken_present = 1;				break;			}		}		if (broken_present) {			static int complained = 0;			if (!complained) {				printk(KERN_WARNING				       "MMC%d: Broken card workaround enabled\n",				       host->id);				complained = 1;			}			if (in_interrupt()) {				/* This is nasty */				printk(KERN_ERR				       "Sleeping in IRQ handler, FIXME please!\n");				dump_stack();				mdelay(100);			} else {				set_current_state(TASK_UNINTERRUPTIBLE);				schedule_timeout(100 * HZ / 1000);			}		}	}	mmc_omap_prepare_data(host, req);	mmc_omap_start_command(host, req->cmd);}/* * Turn the on/off the power to the MMC socket. */static void mmc_omap_power(struct mmc_omap_host *host, int on){	unsigned long reg_val;	if (on) {		reg_val = OMAP_MMC_READ(host->base, CON);		OMAP_MMC_WRITE(host->base, CON, reg_val | (0x1 << 11));	} else {		reg_val = OMAP_MMC_READ(host->base, CON);		OMAP_MMC_WRITE(host->base, CON, reg_val | (0x0 << 11));	}}/* * PRCM clk setup */static void mmc_clk_setup(int on){#if 0	unsigned long reg_val;	if (on) {		reg_val = omap_prcmreg_read(PRCM_CLK_EN_PLL);		omap_prcmreg_write(reg_val | (0xf << 0), PRCM_CLK_EN_PLL);		reg_val = omap_prcmreg_read(PRCM_CLK_SEL1_PLL);		omap_prcmreg_write(reg_val | (0 << 3), PRCM_CLK_SEL1_PLL);		reg_val = omap_prcmreg_read(PRCM_FCLK_EN1_CORE);		omap_prcmreg_write(reg_val | (1 << 26), PRCM_FCLK_EN1_CORE);		reg_val = omap_prcmreg_read(PRCM_FCLK_EN2_CORE);		omap_prcmreg_write(reg_val | (1 << 26), PRCM_FCLK_EN2_CORE);	} else {		reg_val = omap_prcmreg_read(PRCM_CLK_EN_PLL);		omap_prcmreg_write(reg_val | (0x0 << 0), PRCM_CLK_EN_PLL);		reg_val = omap_prcmreg_read(PRCM_CLK_SEL1_PLL);		omap_prcmreg_write(reg_val | (0 << 3), PRCM_CLK_SEL1_PLL);		reg_val = omap_prcmreg_read(PRCM_FCLK_EN1_CORE);		omap_prcmreg_write(reg_val | (0 << 26), PRCM_FCLK_EN1_CORE);		reg_val = omap_prcmreg_read(PRCM_FCLK_EN2_CORE);		omap_prcmreg_write(reg_val | (0 << 26), PRCM_FCLK_EN2_CORE);	}#endif}static void omap24xx_mmc_set_ios(struct mmc_host *mmc, struct mmc_ios *ios){	struct mmc_omap_host *host = mmc_priv(mmc);	int realclock;	int dsor = 0;	//host->id, ios->clock, ios->bus_mode, ios->power_mode,	//ios->vdd / 100, ios->vdd % 100);	DBG("MMC%d: set_ios: clock %dHz busmode %d powermode %d Vdd %d.%02d\n",	    host->id, ios->clock, ios->bus_mode, ios->power_mode,	    ios->vdd / 100, ios->vdd % 100);	if (ios->power_mode == MMC_POWER_UP && ios->clock < 400000) {		/* Fix for broken stack */		realclock = 400000;	} else {		realclock = ios->clock;	}	if (ios->clock == 0) {		/* Disable MMC_SD_CLK */		mmc_clk_setup(0);	} else {		/* Enable MMC_SD_CLK */		mmc_clk_setup(1);		dsor = MMC_REF_CLOCK / realclock;		if (dsor < 1)			dsor = 1;		if (MMC_REF_CLOCK / dsor > realclock)			dsor++;		if (dsor > 250)			dsor = 250;	}	switch (ios->power_mode) {	case MMC_POWER_OFF:		dsor |= 0 << 11;		break;	case MMC_POWER_UP:	case MMC_POWER_ON:		dsor |= 1 << 11;		break;	}	host->bus_mode = ios->bus_mode;	OMAP_MMC_WRITE(host->base, CON, dsor);	if (ios->power_mode == MMC_POWER_UP) {		int wait_counter = 0;		/* Send clock cycles, poll completion */		OMAP_MMC_WRITE(host->base, IE, 0);		OMAP_MMC_WRITE(host->base, STAT, 0xffff);		OMAP_MMC_WRITE(host->base, CMD, 1 << 7);		/* We wait for the mmc command to be completed. If its not done, then the following 		 * timeout mechanism will ensure that the system does not hang  		 */		while ((0 == (OMAP_MMC_READ(host->base, STAT) & 1))		       && (wait_counter < MMC_WAIT_TIMEOUT)) {			wait_counter++;			set_current_state(TASK_INTERRUPTIBLE);			schedule_timeout(10);		}		if (MMC_WAIT_TIMEOUT == wait_counter) {			printk(KERN_ERR			       "%s: Timed out waiting for cmd status - further results undefined\n",			       __FUNCTION__);			return;		}		OMAP_MMC_WRITE(host->base, STAT, 1);	}}static int omap24xx_mmc_probe(struct omap_dev *dev){	struct mmc_host *mmc;	struct mmc_omap_host *host;	static unsigned short card_status;	int ret = 0;	host = kmalloc(sizeof(struct mmc_omap_host), GFP_KERNEL);	if (!host) {		ret = -ENOMEM;		return ret;	}	memset(host, 0, sizeof(struct mmc_omap_host));	mmc =	    mmc_alloc_host(sizeof(struct mmc_omap_host), (struct device *)dev);	if (!mmc) {		ret = -ENOMEM;		goto err2;	}	host = mmc_priv(mmc);	host->mmc = mmc;	host->use_dma = OMAP_USE_DMA;	host->dma_ch = -1;	host->irq = OMAP_MMC_IRQ;	host->base = (unsigned long)ioremap(OMAP_MMC_BASE, MMC_REG_SIZE);	if (!host->base) {		printk("MMC: cannot map MMIO\n");		goto err2;	}	mmc->ops = &mmc_omap_ops;	mmc->f_min = 400000;	mmc->f_max = 24000000;	mmc->ocr_avail = MMC_VDD_33_34;	omap2_cfg_reg(H15_2420_MMC_CLKI);	omap2_cfg_reg(G19_2420_MMC_CLKO);	omap2_cfg_reg(H18_2420_MMC_CMD);	omap2_cfg_reg(F20_2420_MMC_DAT);	omap2_cfg_reg(F19_2420_MMC_DATDIR);	omap2_cfg_reg(G18_2420_MMC_CMDDIR);	ret = request_irq(host->irq, mmc_omap_irq, 0, DRIVER_NAME, host);	if (ret)		goto err2;#ifdef CONFIG_MMC_HOTPLUG	/* Request an irq for card detection	 */	ret = request_irq(MMC_HP_IRQ, mmc_omap_irq_cd, 0, DRIVER_NAME, host);	if (ret)		goto err2;#endif	dev_set_drvdata((struct device *)dev, host);	card_status = omap_get_gpio_datain(0);	if (card_status != 1)		mmc_add_host(mmc);	else		printk("\n MMC card not present\n");	return 0;      err2:	free_irq(host->irq, host);	kfree(host);	return ret;}static int omap24xx_mmc_remove(struct omap_dev *dev){	struct mmc_omap_host *host = dev_get_drvdata((struct device *)dev);	omap_set_drvdata(dev, NULL);	mmc_remove_host(host->mmc);	free_irq(MMC_HP_IRQ, host);	free_irq(host->irq, host);	mmc_omap_power(host, 0);	iounmap((void *)host->base);	kfree(host);	return 0;}#ifdef CONFIG_PMstatic int omap24xx_mmc_suspend(struct omap_dev *dev, u32 state){	return 0;}static int omap24xx_mmc_resume(struct omap_dev *dev){	return 0;}#else#define omap24xx_mmc_suspend        NULL#define omap24xx_mmc_resume         NULL#endifstatic void omap_24xx_release(struct device *dev){}static struct omap_driver omap24xx_mmc_driver = {	.drv = {		.name = DRIVER_NAME,		},	.devid = OMAP24xx_MMC_DEVID,	.busid = OMAP_BUS_L4,	.clocks = 0,	.probe = omap24xx_mmc_probe,	.remove = omap24xx_mmc_remove,	.suspend = omap24xx_mmc_suspend,	.resume = omap24xx_mmc_resume,};static struct omap_dev omap24xx_mmc_device = {	.name = "omap-mmc",	.devid = OMAP24xx_MMC_DEVID,	.dev = {		.release = omap_24xx_release,		},	.busid = OMAP_BUS_L4,	.mapbase = (void *)OMAP_MMC_BASE,	.res = {		.start = IO_ADDRESS(OMAP_MMC_BASE),		.end = IO_ADDRESS(OMAP_MMC_BASE) + 0x7f,		},	.irq = {		OMAP_MMC_IRQ,		},};#ifdef CONFIG_MMC_HOTPLUG/* card detect function - detects mmc card's presence by reading gpio datain reg */int card_detect_fn(void){	unsigned card_status;	int card_present;	card_status = omap_get_gpio_datain(GPIO_0);	if (card_status == 1)		card_present = REMOVE;	else		card_present = INSERT;	return card_present;}static struct hotplug_structure mmc_hp_str = {	.card_detect = &card_detect_fn,};/* Schedule a worker thread to add the host upon card insertion interrupt */static void start_state_machine(void){	if (in_interrupt()) {		cancel_delayed_work(&set_hp_work);		schedule_work(&set_hp_work);	} else {		BUG_ON(!in_interrupt());	}}/* Schedule a worker thread to remove the host upon card removal interrupt */static void stop_state_machine(void){	if (in_interrupt()) {		cancel_delayed_work(&stop_hp_work);		schedule_work(&stop_hp_work);	} else {		BUG_ON(!in_interrupt());	}}/* Worker thread to add the host */static void set_hp_handler(void *data){	mmc_hotplug_insert(saved_host->mmc);	run_sbin_hotplug(INSERT);}/* Worker thread to remove the host */static void stop_hp_handler(void *data){	mmc_hotplug_remove(saved_host->mmc);	run_sbin_hotplug(REMOVE);}/* Function to invoke /sbin/hotplug script passing ACTION and AGENT parameters */static void run_sbin_hotplug(int insert){	char *argv[] = {		hotplug_path,		"mmc",		NULL	};	char *envp[] = {		"HOME=/",		"PATH=/sbin:/bin:/usr/sbin:/usr/bin",		NULL,		NULL	};	if (!hotplug_path[0])		return;	envp[2] = (insert) ? "ACTION=add" : "ACTION=remove";	call_usermodehelper(argv[0], argv, envp, 0);}#endifstatic int __init omap24xx_mmc_init(void){	int ret;	ret = omap_driver_register(&omap24xx_mmc_driver);	if (ret != 0)		return ret;	ret = omap_device_register(&omap24xx_mmc_device);	if (ret != 0)		goto err1;#ifdef CONFIG_MMC_HOTPLUG	omap_mmc_pin_out(CONTROL_PADCONF_sdrc_a14, 0x3);	omap_set_gpio_debounce(GPIO_0, DEBOUNCE_ENABLE);	omap_set_gpio_debounce_time(GPIO_0, DEBOUNCE_TIME);	omap_set_gpio_direction(GPIO_0, OMAP2420_DIR_INPUT);	omap_set_gpio_edge_ctrl(GPIO_0, OMAP_GPIO_BOTH_EDGES);	gpio_unmask_irq(GPIO_0);	mmc_cd_register(&mmc_hp_str);#endif	return 0;      err1:	omap_driver_unregister(&omap24xx_mmc_driver);	return ret;}static void __exit omap24xx_mmc_cleanup(void){	omap_device_unregister(&omap24xx_mmc_device);	omap_driver_unregister(&omap24xx_mmc_driver);}module_init(omap24xx_mmc_init);module_exit(omap24xx_mmc_cleanup);MODULE_DESCRIPTION("OMAP Multimedia Card driver");MODULE_LICENSE("GPL");MODULE_AUTHOR("Texas Instruments");

⌨️ 快捷键说明

复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?