OpenCores
URL https://opencores.org/ocsvn/test_project/test_project/trunk

Subversion Repositories test_project

[/] [test_project/] [trunk/] [linux_sd_driver/] [drivers/] [mmc/] [host/] [mmc_ocores.c] - Rev 72

Go to most recent revision | Compare with Previous | Blame | View Log

/* -*- linux-c -*-
 *
 * OpenCores MMC Controller driver
 *
 * Copyright (C) 2009 ORSoC, All Rights Reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published
 * by the Free Software Foundation; version 2 of the License.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License along
 * with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
 */
#include <linux/module.h>
#include <linux/init.h>
#include <linux/ioport.h>
#include <linux/platform_device.h>
#include <linux/delay.h>
#include <linux/interrupt.h>
#include <linux/err.h>
#include <linux/mmc/host.h>
 
#include <asm/board.h>
#include <asm/io.h>
#include <asm/mmc.h>
#include <asm/system.h>
#include "mmc_ocores.h"
 
#define DRIVER_NAME "mmc-ocores"
 
#define NR_SG	1
 
 
 
struct ocores_host {
	struct mmc_host		*mmc;
	spinlock_t		lock;
	struct resource		*res;
	void __iomem		*base;
	unsigned int		cmdat;
	unsigned int		power_mode;
	struct ocores_platform_data *pdata;
	unsigned int		word_cnt;
	struct mmc_request	*mrq;
	struct mmc_command	*cmd;
	struct mmc_data		*data;
	int irq_cmd;
	int irq_dat;
	unsigned int flags;
	struct tasklet_struct finish_cmd;
	struct {
		unsigned int normal_int_status;
		unsigned int error_int_status;
	} registers;
};
 
struct ocores_host *oc_host;
 
static void ocores_tasklet_finish_cmd(unsigned long param);
static inline void CMD_IRQ_ON(struct ocores_host *host, u32 mask)
{
	u32 val = readl(host->base  + SD_NOMAL_INT_SIGNAL_ENABLE);
	printk(KERN_ALERT "Int mask = %08x\n", val);
	val |= mask;
	writel (val, host->base  + SD_NOMAL_INT_SIGNAL_ENABLE);
	printk(KERN_ALERT "Int mask = %08x\n", val);
}
 
static inline void CMD_IRQ_OFF(struct ocores_host *host, u32 mask)
{
	u32 val = readl(host->base  + SD_NOMAL_INT_SIGNAL_ENABLE);
	val  &= ~mask;
	writel (val, host->base  + SD_NOMAL_INT_SIGNAL_ENABLE);
}
 
static void ocores_start_cmd(struct ocores_host *host, struct mmc_command *cmd)
{
	unsigned int cmd_arg, cmd_command=0;
 
	//struct mmc_data *data = cmd->data;
	//WARN_ON(host->cmd != NULL);
	host->cmd = cmd;
 
	//Set up command
	cmd_arg = cmd->arg;
	cmd_command |= cmd->opcode << 8;
	cmd_command |= host->word_cnt << 6;
 
	if ( mmc_resp_type(cmd) == MMC_RSP_CRC  )
		cmd_command |= CRCE;
	if ( mmc_resp_type(cmd) == MMC_RSP_OPCODE  )
		cmd_command |= CICE;
 
	switch (mmc_resp_type(cmd)) {
	case MMC_RSP_NONE:
		cmd_command |= MMCOC_RSP_NONE;
		break;
	case MMC_RSP_R1:
		cmd_command |= MMCOC_RSP_48;
		break;
	case MMC_RSP_R1B:
		cmd_command |= MMCOC_RSP_48;
		break;
	case MMC_RSP_R2:
		cmd_command |= MMCOC_RSP_136;
		break;
	case MMC_RSP_R3:
		cmd_command |= MMCOC_RSP_48;
		break;
	default:
		printk(KERN_INFO "mmc_ocores: unhandled response type %02x\n",
		       mmc_resp_type(cmd));
	}
 
     /*If data
    //	if ( data->blksz & 0x3 ) {
			pr_debug("Unsupported block size\n");
			cmd->error = -EINVAL;
			mmc_request_done(host->mmc, host->request);
			return;
		} */
 
	printk(KERN_ALERT "%s: cmd_arg = %08x\n", __FUNCTION__, cmd_arg);
	printk(KERN_ALERT "%s: cmd_command   = %08x\n", __FUNCTION__, cmd_command);
	oc_host=host;
 
	//XXX:Remove after uppdated bitfil with Timeout
	if (cmd->opcode == 8  || cmd->opcode ==  5 )
	{
		host->mrq->cmd->error = -ETIMEDOUT;
		mmc_request_done(host->mmc, host->mrq);
	}
	else
	{
		CMD_IRQ_ON (host,(ECC|EEI));
		writel(cmd_command, host->base + SD_COMMAND);
		wmb();
		writel(cmd_arg, host->base + SD_ARG);
	}
}
 
static void ocores_process_next(struct ocores_host *host)
{
	host->word_cnt=0;
	if (!(host->flags & FL_SENT_COMMAND)) {
		host->flags |= FL_SENT_COMMAND;
		ocores_start_cmd(host, host->mrq->cmd);
	}
	else if ((!(host->flags & FL_SENT_STOP)) && host->mrq->stop) {
		host->flags |= FL_SENT_STOP;
		ocores_start_cmd(host, host->mrq->stop);
	}
}
 
static void ocores_request(struct mmc_host *mmc, struct mmc_request *mrq)
{
	struct ocores_host *host = mmc_priv(mmc);
 
	//unsigned int cmdr, mr;
 
	printk(KERN_ALERT "%s: mrq->cmd->opcode = %08x\n", __FUNCTION__, mrq->cmd->opcode);
	printk(KERN_ALERT "%s: mrq->cmd->arg    = %08x\n", __FUNCTION__, mrq->cmd->arg);
 
	//WARN_ON(host->mrq != NULL);
 
	host->mrq = mrq;
	host->flags = 0;
 
	ocores_process_next(host);
 
 
	printk(KERN_ALERT "%s: exit\n", __FUNCTION__);
}
 
 
 
static int ocores_get_ro(struct mmc_host *mmc)
{
	/* struct ocores_host *host = mmc_priv(mmc); */
 
	printk(KERN_ALERT "%s: enter\n", __FUNCTION__);
 
	/* if (host->pdata && host->pdata->get_ro) */
	/* 	return host->pdata->get_ro(mmc_dev(mmc)); */
	/* /\* Host doesn't support read only detection so assume writeable *\/ */
	return 0;
}
 
static void ocores_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
{
	struct ocores_host *host = mmc_priv(mmc);
	int clk_div, cmd_timeout;
	/* struct ocores_host *host = mmc_priv(mmc); */
 
	printk(KERN_ALERT "%s: clock = 0x%08x\n", __FUNCTION__, ios->clock);
	printk(KERN_ALERT "%s: vdd = 0x%04x\n", __FUNCTION__, ios->vdd);
	printk(KERN_ALERT "%s: bus_mode = 0x%02x\n", __FUNCTION__, ios->bus_mode);
	printk(KERN_ALERT "%s: power_mode = 0x%02x\n", __FUNCTION__, ios->power_mode);
	printk(KERN_ALERT "%s: bus_width = 0x%02x\n", __FUNCTION__, ios->bus_width);
	printk(KERN_ALERT "%s: timing = 0x%02x\n", __FUNCTION__, ios->timing);
 
	//Set clock divider and timeout registers
    	printk(KERN_ALERT "%s: host->base = %p\n", __FUNCTION__, host->base);
	if (ios->clock == 0) {
		//ocores_mci_write (host, SD_SOFTWARE_RST, SD_DISABLE);
	}
	else
	{
		writel(SD_DISABLE, host->base + SD_SOFTWARE_RST);
		clk_div = ((SYS_CLK / ios->clock)-2 )/ 2;
		cmd_timeout = ((SYS_CLK/ios->clock) * 512);
 
		printk(KERN_ALERT " clk_div = 0x%02x\n", clk_div);
		printk(KERN_ALERT " cmd_timeout = 0x%02x\n", cmd_timeout);
 
		writel (clk_div, host->base  + SD_CLOCK_DIVIDER);
		writel (cmd_timeout, host->base  + SD_TIMEOUT);
 
 
		writel(SD_ENABLE, host->base + SD_SOFTWARE_RST);
	}
 
}
 
 
 
static irqreturn_t ocores_irq_cmd(int irq, void *devid)
{
	 struct ocores_host *host = oc_host;
 
	 disable_irq(host->irq_cmd);
 
	 	//printk(KERN_ALERT "%s: IRQ START***** Normal In  = %08x\n", __FUNCTION__, readl(host->base + SD_NORMAL_INT_STATUS));
 
	 host->registers.normal_int_status  = readl(host->base + SD_NORMAL_INT_STATUS);
	 rmb();
	 host->registers.error_int_status  = readl(host->base + SD_ERROR_INT_STATUS);
 
	 writel(0,host->base + SD_NORMAL_INT_STATUS);
	 writel(0,host->base + SD_ERROR_INT_STATUS);
 
	 //printk(KERN_ALERT "%s: IRQ END***** Error In  = %08x\n", __FUNCTION__, readl(host->base + SD_ERROR_INT_STATUS));
	 tasklet_schedule(&host->finish_cmd);
	 CMD_IRQ_OFF (host,(ECC|EEI));
	 enable_irq(host->irq_cmd);
 
	 return IRQ_HANDLED;
}
 
static irqreturn_t ocores_irq_dat(int irq, void *dev_id)
{
 
}
 
static const struct mmc_host_ops ocores_ops = {
	.request		= ocores_request,
	.get_ro			= ocores_get_ro,
	.set_ios		= ocores_set_ios,
	/* .enable_sdio_irq	= ocores_enable_sdio_irq, */
};
 
static int ocores_probe(struct platform_device *pdev)
{
	struct mmc_host *mmc;
	struct ocores_host *host = NULL;
	struct resource *r;
	int ret;
 
	printk(KERN_ALERT "%s: enter\n", __FUNCTION__);
 
	r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
 
	printk(KERN_ALERT "%s: resource %x, %x\n", __FUNCTION__, r->start, r->end);
 
	r = request_mem_region(r->start, r->end - r->start, DRIVER_NAME);
	if (!r) {
		return -EBUSY;
	}
 
	mmc = mmc_alloc_host(sizeof(struct ocores_host), &pdev->dev);
	if (!mmc) {
		ret = -ENOMEM;
		goto out;
	}
 
 
	mmc->ops = &ocores_ops;
 
	mmc->ocr_avail = MMC_VDD_32_33 | MMC_VDD_33_34;
	mmc->caps = MMC_CAP_4_BIT_DATA;
	mmc->f_min = 700000;  //SYS_CLK  60; 0.7 Mhz
	mmc->f_max = 4166666;  //SYS_CLK;   4.166 666 mhz
 
	mmc->max_blk_count = 8;
	mmc->max_hw_segs = mmc->max_blk_count;
	mmc->max_blk_size = MMCOC_MAX_BLOCK_SIZE;
	mmc->max_seg_size = mmc->max_blk_count * mmc->max_blk_size;
	mmc->max_req_size = mmc->max_seg_size;
	mmc->max_phys_segs = mmc->max_hw_segs;
 
	host = mmc_priv(mmc);
	host->mmc = mmc;
	host->cmdat = 0;
	host->registers.normal_int_status =0;
 
 
	tasklet_init(&host->finish_cmd, ocores_tasklet_finish_cmd,
		     (unsigned long) host);
 
	spin_lock_init(&host->lock);
	host->res = r;
 
	host->base = ioremap(r->start, r->end - r->start +1);
	/* host->base = (void *)r->start; */
	if (!host->base) {
		ret = -ENOMEM;
		goto out;
	}
	host->pdata = pdev->dev.platform_data;
	mmc->ocr_avail = host->pdata->ocr_mask;
 
	host->irq_cmd = platform_get_irq_byname(pdev, "cmd_irq");
	ret = request_irq(host->irq_cmd, ocores_irq_cmd, IRQF_DISABLED, DRIVER_NAME, host);
	printk(KERN_ALERT "%s: IRQ dat resource  %x\n", __FUNCTION__, host->irq_cmd );
	printk(KERN_ALERT "%s: RET cmd irq %x\n", __FUNCTION__, ret);
	if (ret)
		goto out;
	disable_irq(host->irq_cmd);
 
	host->irq_dat = platform_get_irq_byname(pdev, "dat_irq");
	ret = request_irq(host->irq_dat, ocores_irq_dat, IRQF_DISABLED, DRIVER_NAME, host);
	printk(KERN_ALERT "%s: IRQ dat resource  %x\n", __FUNCTION__, host->irq_dat );
	printk(KERN_ALERT "%s: RET Dat irq  %x\n", __FUNCTION__, ret);
	if (ret)
		goto out;
	disable_irq(host->irq_dat);
 
 
 
	enable_irq(host->irq_cmd);
   	enable_irq(host->irq_dat);
	printk(KERN_ALERT "%s: host->base = %p\n", __FUNCTION__, host->base);
	printk(KERN_ALERT "%s: SD_BLOCK = %08x\n", __FUNCTION__, readl(host->base + SD_BLOCK));
	printk(KERN_ALERT "%s: host->pdata->ocr_mask = %08x\n", __FUNCTION__, host->pdata->ocr_mask);
	oc_host=host;
	mmc_add_host(mmc);
 
	printk(KERN_ALERT "%s: exit\n", __FUNCTION__);
 
	return 0;
 
 out:
  	printk(KERN_ALERT "%s: ERROR REQUESTINING RESOURCES\n", __FUNCTION__);
	if (mmc) {
		mmc_free_host(mmc);
	}
	release_resource(r);
 
	return ret;
}
 
static void ocores_tasklet_finish_cmd(unsigned long param)
{
	struct ocores_host *host = (struct ocores_host *) param;
 
	printk(KERN_ALERT " TASKLET RUNNS************\n");
 
	printk(KERN_ALERT "%s: TASKLET RUNNS****** Normal INT = %08x\n", __FUNCTION__,  host->registers.normal_int_status);
	printk(KERN_ALERT "%s: TASKLET RUNNS****** Error INT = %08x\n", __FUNCTION__,  host->registers.error_int_status);
 
	//Check For Transmissions errors
	if ((host->registers.normal_int_status & EI) == EI)
	{
		printk(KERN_ALERT "TRANSMISSION ERROR DETECTED");
		switch ( host->registers.error_int_status )
     	 	{
		case (CTE):
			pr_debug("Card took too long to respond\n");
			host->mrq->cmd->error = -ETIMEDOUT ;
     	 		break;
		case (CCRC  ):
			pr_debug(" CRC  problem with the received or sent data\n");
			host->mrq->cmd->error = -EILSEQ;
     	 		break;
		case (CIE  ):
			pr_debug("Index problem with the received or sent data\n");
			host->mrq->cmd->error = -EILSEQ;
     	 		break;
		}
	}
	else
	{
		if ( mmc_resp_type(host->mrq->cmd) == MMCOC_RSP_136      )   //Long response
		{
			printk(KERN_ALERT "Long Response, Word Cnt  * = %08x\n ",host->word_cnt);
			host->word_cnt+=1;
			switch(host->word_cnt-1)
			{
	    		case (0):
	        		host->mrq->cmd->resp[3]  =  readl(host->base + SD_RESP1);
				ocores_start_cmd(host, host->mrq->cmd);
				break;
	        	case (1):
	        		host->mrq->cmd->resp[2]  =  readl(host->base + SD_RESP1);
	        		ocores_start_cmd(host, host->mrq->cmd);
				break;
	        	case (2):
	        		host->mrq->cmd->resp[1]  =  readl(host->base + SD_RESP1);
	        		ocores_start_cmd(host, host->mrq->cmd);
				break;
	        	case (3):
	        		host->mrq->cmd->resp[0]  =  readl(host->base + SD_RESP1);
	        		mmc_request_done(host->mmc, host->mrq);
				break;
			}
		}
		else  //Short response
		{
			host->mrq->cmd->error = 0 ;
			host->mrq->cmd->resp[0] = readl(host->base + SD_RESP1);
			printk(KERN_ALERT "Short Response CMD RSP * = %08x\n", host->mrq->cmd->resp[0]);
			mmc_request_done(host->mmc, host->mrq);
		}
	}
 
}
 
static int ocores_remove(struct platform_device *pdev)
{
	struct mmc_host *mmc = platform_get_drvdata(pdev);
 
	printk(KERN_ALERT "%s: enter\n", __FUNCTION__);
 
	platform_set_drvdata(pdev, NULL);
 
	if (mmc) {
		struct ocores_host *host = mmc_priv(mmc);
 
		mmc_remove_host(mmc);
 
		release_resource(host->res);
 
		mmc_free_host(mmc);
	}
 
	printk(KERN_ALERT "%s: exit\n", __FUNCTION__);
 
	return 0;
}
 
#ifdef CONFIG_PM
static int ocores_suspend(struct platform_device *dev, pm_message_t state)
{
	struct mmc_host *mmc = platform_get_drvdata(dev);
	int ret = 0;
 
	printk(KERN_ALERT "%s: enter\n", __FUNCTION__);
 
	if (mmc) {
		ret = mmc_suspend_host(mmc, state);
	}
 
	printk(KERN_ALERT "%s: exit\n", __FUNCTION__);
 
	return ret;
}
 
static int ocores_resume(struct platform_device *dev)
{
	struct mmc_host *mmc = platform_get_drvdata(dev);
	int ret = 0;
 
	printk(KERN_ALERT "%s: enter\n", __FUNCTION__);
 
	if (mmc) {
		ret = mmc_resume_host(mmc);
	}
 
	printk(KERN_ALERT "%s: exit\n", __FUNCTION__);
 
	return ret;
}
#else
#define ocores_suspend	NULL
#define ocores_resume	NULL
#endif
 
static struct platform_driver ocores_driver = {
	.probe		= ocores_probe,
	.remove		= ocores_remove,
	.suspend	= ocores_suspend,
	.resume		= ocores_resume,
	.driver		= {
		.name	= DRIVER_NAME,
	},
};
 
static int __init ocores_init(void)
{
	printk(KERN_ALERT "%s: registering ocores platform_driver\n", __FUNCTION__);
 
	return platform_driver_register(&ocores_driver);
}
 
static void __exit ocores_exit(void)
{
	printk(KERN_ALERT "%s: unregistering ocores platform_driver\n", __FUNCTION__);
 
	platform_driver_unregister(&ocores_driver);
}
 
module_init(ocores_init);
module_exit(ocores_exit);
 
MODULE_DESCRIPTION("OpenCores Multimedia Card Interface Driver");
MODULE_LICENSE("GPL");
 

Go to most recent revision | Compare with Previous | Blame | View Log

powered by: WebSVN 2.1.0

© copyright 1999-2024 OpenCores.org, equivalent to Oliscience, all rights reserved. OpenCores®, registered trademark.