📄 ftsdc010.c
字号:
/* drivers/block/CPESD/ftsdc010.c ******************************************************************************* * FTSDC010 Device Driver * * Copyright (C) 2005 GM Corp. (http://www.grain-media.com) * * All Rights Reserved * * Porting to Linux 2.6 on 20050815 * Author: Chris Lee, I-Jui Sung, Peter Liao (support APB DMA) * Version: 0.2 * History: * 0.1 new creation * 0.2 Porting to meet the style of linux dma * 0.3 modify dma usage to virtual irq of dma interrupt * 0.4 (20050701) Improve r/w performance * 0.5 Porting to Linux 2.6 and replace busy_loop checking with timer's timeout * Todo: ******************************************************************************* */#include "config.h"#include <linux/kernel.h>#include <linux/module.h>#include <linux/sched.h>#include <linux/delay.h>#include <linux/init.h>#include <linux/ioport.h>#include <linux/hdreg.h> /* HDIO_GETGEO */#include <linux/fs.h>#include <linux/blkdev.h>#include <linux/buffer_head.h> /* invalidate_bdev */#include <linux/bio.h>#include <linux/pci.h>#include <asm/io.h>#include <asm/uaccess.h>#include <asm/arch/spec.h>#include <asm/arch/irqs.h>#include <asm/signal.h>extern int a320_get_ahb_clk(void);extern asmlinkage long sys_kill(int pid, int sig);static pid_t mpid;#define IPMODULE SDC#define IPNAME FTSDC010#ifdef CONFIG_FTSDC010_USE_APBDMA#define SD_DMA_CHANNEL 0#endif#ifdef CONFIG_FTSDC010_USE_AHBDMA#define SD_DMA_CHANNEL 6#endif#ifdef CONFIG_FTSDC010_USE_APBDMA#include "asm/arch/apb_dma.h"#define CONFIG_FTSDC010_USE_APBDMA_POLL 1#undef CONFIG_FTSDC010_USE_APBDMA_POLL#endif#ifdef CONFIG_FTSDC010_USE_AHBDMA#include "asm/arch/ahb_dma.h"#endif/* Define CONFIG_FTSDC010_USE_TIMER_DELAY if you want to use software timer instead of busy loop checking */#define CONFIG_FTSDC010_USE_TIMER_DELAY//#undef CONFIG_FTSDC010_USE_TIMER_DELAY#include "ftsdc010.h"#include <linux/interrupt.h>MODULE_AUTHOR("GM Corp.");MODULE_LICENSE("GM License");/* data window register */uint SDC_READ_FIFO_LEN = 4;uint SDC_WRITE_FIFO_LEN = 4;/* options */#define FORCE_PCI_CONSISTENCY 1 /* define it to 1 if met consistency problems */#define KERNEL_SECTOR_SIZE 512 /* Use this when we refer to kernel related sector size */static int hardsect_size = 512, error_flag = 0;#define NOT_DETECT 0#define AUTO_DETECT 1static uint SD_MODE = NOT_DETECT;module_param(SD_MODE, int, 0);MODULE_PARM_DESC(SD_MODE, "SD auto detect");/*------------------------------------------------------------------------------ * Predefine for block device */#define MAJOR_NR sd_major /* force definitions on in blk.h */static int sd_major=0;//SD_MAJOR; /* must be declared before including blk.h */#define SD_SHIFT 4 /* max 16 partitions */#define DEVICE_NAME "SDC" /* name for messaging */#define DEVICE_REQUEST sd_request#define DEVICE_NR(device) (MINOR(device) >> SD_SHIFT)//#define DEVICE_INTR sd_intrptr /* pointer to the bottom half */#define DEVICE_NO_RANDOM /* no entropy to contribute */#define DEVICE_OFF(d) /* do-nothing *//*#include <linux/blk.h>#include <linux/blkpg.h>*/ /* blk_ioctl() *//*------------------------------------------------------------------------------ * Macro definition */#define FTSDC_VA_BASE IP_VA_BASE(0)#define FTSDC_PA_BASE IP_PA_BASE(0)#define FTSDC_IRQ IP_IRQ(0)#define SDC_W_REG(offset, value) outl(value, IP_VA_BASE(0) + offset)#define SDC_R_REG(offset) inl(IP_VA_BASE(0) + offset)/*------------------------------------------------------------------------------ * Global variable *//* The following items are obtained through kmalloc() in sd_module_init() */struct block_device_operations sd_fops;/* our device structure */typedef struct sd_dev { int size; /* device size in sectors */ int usage; /* # of users currently */ int media_change; /* Flag: media changed? */ struct gendisk *gd; /* The gendisk structure */ spinlock_t lock; /* For mutual exclusion */ struct request_queue *queue; /* The device request queue */ int card_state;}sd_dev_t;static sd_dev_t *sd_devices = NULL;static sd_card_t sd_card_info;int sector_offset,Do_onetime;#ifdef CONFIG_FTSDC010_USE_APBDMA /* only used for dma mode */dma_addr_t dma_buf=0;wait_queue_head_t sd_dma_queue;static apb_dma_parm_t parm;int bh_busy=0;#endif#ifdef CONFIG_FTSDC010_USE_AHBDMA /* only used for dma mode */dma_addr_t dma_buf=0;wait_queue_head_t sd_dma_queue;static ahb_dma_parm_t parm;int bh_busy=0;#endifint sync_mode=0;static uint first_run = 0;uint sd_err_code;#define FILE_FORMAT_HARD_DISK_LIKE 0#define FILE_FORMAT_FLOPPY_LIKE 1#define FILE_FORMAT_UNIVERSAL 2#define FILE_FORMAT_UNKNOW 3#define FILE_FORMAT_RESERVED 4#define K 1000uint TAAC_TimeUnitTable[] = { // unit is ns 1, 10, 100, 1 * K, 10 * K, 100 * K, 1 * K * K, 10 * K * K};uint TRANS_SPEED_RateUintTable[] = { 100 * K, 1 * K * K, 10 * K * K, 100 * K * K};uint TRANS_SPEED_TimeValueTable_u[] = { // unit=1/10 0, 10, 12, 13, 15, 20, 25, 30, 35, 40, 45, 50, 55, 60, 70, 80};uint VDD_CURR_MIN_Table_u[] = { // unit=1/10 5, 10, 50, 100, 250, 350, 600, 1000};uint VDD_CURR_MAX_Table_u[] = { 1, 5, 10, 25, 35, 45, 80, 200};uint TAAC_TimeValueTable_u[] = { // unit=1/10 0, 10, 12, 13, 15, 20, 25, 30, 35, 40, 45, 50, 55, 60, 70, 80};/*------------------------------------------------------------------------------ * Local declaration of function */static int sd_revalidate(struct gendisk *gd);static int sd_media_changed(struct gendisk *gd);int sd_card_setup(void);int sd_check_err(uint status);int sd_get_scr(sd_card_t *info, uint *scr);int sd_set_transfer_state(sd_card_t *info);uint sd_block_size_convert(uint size);int sd_read_sector(sd_card_t *info, uint addr, uint count, unchar *buf);void sd_reset_host_controller(void);/*------------------------------------------------------------------------------ * Local function *//* * SD host controller operation */#ifndef CONFIG_FTSDC010_USE_TIMER_DELAYint sdc_send_cmd(uint cmd, uint arg, uint *rsp){ int i; uint status, count = 0; /* clear command relative bits of status register */ SDC_W_REG(SDC_CLEAR_REG, SDC_STATUS_REG_RSP_CRC_FAIL | SDC_STATUS_REG_RSP_TIMEOUT | SDC_STATUS_REG_RSP_CRC_OK| SDC_STATUS_REG_CMD_SEND); /* write argument to arugument register if necessary */ SDC_W_REG(SDC_ARGU_REG, arg); /* send command */ SDC_W_REG(SDC_CMD_REG, cmd | SDC_CMD_REG_CMD_EN); /* wait for the CMD_SEND bit of status register is set */ while (count++ < SDC_GET_STATUS_RETRY_COUNT) { status = SDC_R_REG(SDC_STATUS_REG); if (!(cmd & SDC_CMD_REG_NEED_RSP)) { /* if this command does not need response, wait command sent flag */ if (status & SDC_STATUS_REG_CMD_SEND) { /* clear command sent bit */ SDC_W_REG(SDC_CLEAR_REG, SDC_STATUS_REG_CMD_SEND); sd_err_code = ERR_NO_ERROR; return TRUE; } } else { /* if this command needs response */ if (status & SDC_STATUS_REG_RSP_TIMEOUT) { /* clear response timeout bit */ SDC_W_REG(SDC_CLEAR_REG, SDC_STATUS_REG_RSP_TIMEOUT); sd_err_code = ERR_RSP_TIMEOUT_ERROR; printk("%s() ERR_RSP_TIMEOUT_ERROR\n", __func__); return FALSE; } else if (status & SDC_STATUS_REG_RSP_CRC_FAIL) { /* clear response fail bit */ SDC_W_REG(SDC_CLEAR_REG, SDC_STATUS_REG_RSP_CRC_FAIL); sd_err_code = ERR_RSP_CRC_ERROR; printk("%s() ERR_RSP_CRC_ERROR\n", __func__); return FALSE; } else if (status & SDC_STATUS_REG_RSP_CRC_OK) { /* clear response OK bit */ SDC_W_REG(SDC_CLEAR_REG, SDC_STATUS_REG_RSP_CRC_OK); /* if it is long response */ if (cmd & SDC_CMD_REG_LONG_RSP) for (i = 0; i < 4; i++, rsp++) *rsp = SDC_R_REG(SDC_RESPONSE0_REG + (i * 4)); else *rsp = SDC_R_REG(SDC_RESPONSE0_REG); sd_err_code = ERR_NO_ERROR; return TRUE; } } } sd_err_code = ERR_SEND_COMMAND_TIMEOUT; P_DEBUG("%s() ERR_SEND_COMMAND_TIMEOUT\n", __func__); return FALSE;}#elseint sdc_send_cmd(uint cmd, uint arg, uint *rsp){ int i; uint status; unsigned long timeout = jiffies + SDC_GET_STATUS_RETRY_TIMEOUT_COUNT; P_DEBUG("SD Cmd is %d(%#x)\n",cmd&0x3f, cmd&0x7C0); /* clear command relative bits of status register */ SDC_W_REG(SDC_CLEAR_REG, SDC_STATUS_REG_RSP_CRC_FAIL | SDC_STATUS_REG_RSP_TIMEOUT | SDC_STATUS_REG_RSP_CRC_OK| SDC_STATUS_REG_CMD_SEND); /* write argument to arugument register if necessary */ SDC_W_REG(SDC_ARGU_REG, arg); /* send command */ SDC_W_REG(SDC_CMD_REG, cmd | SDC_CMD_REG_CMD_EN); /* wait for the CMD_SEND bit of status register is set */ while (time_before(jiffies, timeout)) { status = SDC_R_REG(SDC_STATUS_REG); if (!(cmd & SDC_CMD_REG_NEED_RSP)) { /* if this command does not need response, wait command sent flag */ if (status & SDC_STATUS_REG_CMD_SEND) { /* clear command sent bit */ SDC_W_REG(SDC_CLEAR_REG, SDC_STATUS_REG_CMD_SEND); sd_err_code = ERR_NO_ERROR; return TRUE; } } else { /* if this command needs response */ if (status & SDC_STATUS_REG_RSP_TIMEOUT) { /* clear response timeout bit */ SDC_W_REG(SDC_CLEAR_REG, SDC_STATUS_REG_RSP_TIMEOUT); sd_err_code = ERR_RSP_TIMEOUT_ERROR; printk("%s() ERR_RSP_TIMEOUT_ERROR\n", __func__); return FALSE; } else if (status & SDC_STATUS_REG_RSP_CRC_FAIL) { /* clear response fail bit */ SDC_W_REG(SDC_CLEAR_REG, SDC_STATUS_REG_RSP_CRC_FAIL); sd_err_code = ERR_RSP_CRC_ERROR; printk("%s() ERR_RSP_CRC_ERROR\n", __func__); error_flag = 1; return FALSE; } else if (status & SDC_STATUS_REG_RSP_CRC_OK) { /* clear response OK bit */ SDC_W_REG(SDC_CLEAR_REG, SDC_STATUS_REG_RSP_CRC_OK); /* if it is long response */ if (cmd & SDC_CMD_REG_LONG_RSP) for (i = 0; i < 4; i++, rsp++) *rsp = SDC_R_REG(SDC_RESPONSE0_REG + (i * 4)); else *rsp = SDC_R_REG(SDC_RESPONSE0_REG); sd_err_code = ERR_NO_ERROR; return TRUE; } } } sd_err_code = ERR_SEND_COMMAND_TIMEOUT; P_DEBUG("%s() ERR_SEND_COMMAND_TIMEOUT\n", __func__); return FALSE;}#endifint sdc_check_tx_ready(void){ uint status; #ifndef CONFIG_FTSDC010_USE_TIMER_DELAY int count = 0; while (count++ < SDC_GET_STATUS_RETRY_COUNT) { status = SDC_R_REG(SDC_STATUS_REG); if (status & SDC_STATUS_REG_FIFO_UNDERRUN) { /* clear FIFO underrun bit */ SDC_W_REG(SDC_CLEAR_REG, SDC_STATUS_REG_FIFO_UNDERRUN); return TRUE; } else if (status & SDC_STATUS_REG_DATA_TIMEOUT) { /* clear data timeout bit */ printk("Wait Write FIFO TimeOut\n"); SDC_W_REG(SDC_CLEAR_REG, SDC_STATUS_REG_DATA_TIMEOUT); sd_err_code = ERR_DATA_TIMEOUT_ERROR; return FALSE; } } #else unsigned long timeout = jiffies + SDC_GET_STATUS_RETRY_TIMEOUT_COUNT; while (time_before(jiffies, timeout)) { status = SDC_R_REG(SDC_STATUS_REG); if (status & SDC_STATUS_REG_FIFO_UNDERRUN) { /* clear FIFO underrun bit */ SDC_W_REG(SDC_CLEAR_REG, SDC_STATUS_REG_FIFO_UNDERRUN); return TRUE; } else if (status & SDC_STATUS_REG_DATA_TIMEOUT) { /* clear data timeout bit */ printk("Wait Write FIFO TimeOut\n"); SDC_W_REG(SDC_CLEAR_REG, SDC_STATUS_REG_DATA_TIMEOUT); sd_err_code = ERR_DATA_TIMEOUT_ERROR; return FALSE; } } #endif sd_err_code = ERR_WAIT_UNDERRUN_TIMEOUT; P_DEBUG("%s() ERR_WAIT_UNDERRUN_TIMEOUT\n", __func__); return FALSE;}int sdc_check_rx_ready(void){ uint status; #ifndef CONFIG_FTSDC010_USE_TIMER_DELAY int count = 0; while (count++ < SDC_GET_STATUS_RETRY_COUNT) { status = SDC_R_REG(SDC_STATUS_REG); if (status & SDC_STATUS_REG_FIFO_OVERRUN) { /* clear FIFO overrun bit */ SDC_W_REG(SDC_CLEAR_REG, SDC_STATUS_REG_FIFO_OVERRUN); return TRUE; } else if (status & SDC_STATUS_REG_DATA_TIMEOUT) { /* clear data timeout bit */ printk("Wait Read FIFO TimeOut\n"); SDC_W_REG(SDC_CLEAR_REG, SDC_STATUS_REG_DATA_TIMEOUT); sd_err_code = ERR_DATA_TIMEOUT_ERROR; return FALSE; } } #else unsigned long timeout = jiffies + SDC_GET_STATUS_RETRY_TIMEOUT_COUNT; while (time_before(jiffies, timeout)) { status = SDC_R_REG(SDC_STATUS_REG); if (status & SDC_STATUS_REG_FIFO_OVERRUN) { /* clear FIFO overrun bit */ SDC_W_REG(SDC_CLEAR_REG, SDC_STATUS_REG_FIFO_OVERRUN); return TRUE; } else if (status & SDC_STATUS_REG_DATA_TIMEOUT) { /* clear data timeout bit */ printk("Wait Read FIFO TimeOut\n"); SDC_W_REG(SDC_CLEAR_REG, SDC_STATUS_REG_DATA_TIMEOUT); sd_err_code = ERR_DATA_TIMEOUT_ERROR; return FALSE; } } #endif sd_err_code = ERR_WAIT_OVERRUN_TIMEOUT; P_DEBUG("%s() ERR_WAIT_OVERRUN_TIMEOUT\n", __func__); return FALSE;}int sdc_check_data_crc(void){ uint status=0; #ifndef CONFIG_FTSDC010_USE_TIMER_DELAY int count = 0; while (count++ < SDC_GET_STATUS_RETRY_COUNT) { status = SDC_R_REG(SDC_STATUS_REG); if (status & SDC_STATUS_REG_DATA_CRC_OK) { P_DEBUGG("%s : receive data ok, status=0x%x\n", __func__, status); /* clear data CRC OK bit */ SDC_W_REG(SDC_CLEAR_REG, SDC_STATUS_REG_DATA_CRC_OK); return TRUE; } else if (status & SDC_STATUS_REG_DATA_CRC_FAIL) { /* clear data CRC fail bit */ SDC_W_REG(SDC_CLEAR_REG, SDC_STATUS_REG_DATA_CRC_FAIL); sd_err_code = ERR_DATA_CRC_ERROR; printk("%s() ERR_DATA_CRC_ERROR\n", __func__); return FALSE; } else if (status & SDC_STATUS_REG_DATA_TIMEOUT) { /* clear data timeout bit */ SDC_W_REG(SDC_CLEAR_REG, SDC_STATUS_REG_DATA_TIMEOUT);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -