📄 sdcard.c
字号:
/*
* extdrv/interface/spi_sd/sdcard.c
*
* Copyright (c) 2006 Hisilicon Co., Ltd.
*
* 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; either version 2 of the License, or
* (at your option) any later version.
*
* 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.
*
*
* History:
* 2006-4-11 create this file
*/
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/init.h>
#include <linux/delay.h>
#include <linux/kernel.h>
#include <linux/fs.h>
#include <linux/errno.h>
#include <linux/types.h>
#include <linux/vmalloc.h>
#include <linux/genhd.h>
#include <linux/blkdev.h>
#include <linux/hdreg.h>
#include <linux/devfs_fs_kernel.h>
#include <linux/wait.h>
#include <linux/workqueue.h>
#include "sdcard.h"
#include "../peripheral/misc_gpio/misc_gpio.h"
#include "hi_ssp.h"
static void sd_do_work(void *);
static DECLARE_WORK(sd_work, sd_do_work, NULL);
static struct workqueue_struct *sd_workqueue;
static struct request_queue *Queue;
static struct sd_device Device;
static int hardsect_size = 512;
static int nsectors = 16*(2*1024);
static int minors=16;
static int thread_quit = 0;
/*
* sd_wakup routine.
*
*/
void sd_wakup(void)
{
wake_up(&Device.dma_wq);
}
/*
* sd_add_wait routine.
*
* @param wait: pointer wait_queue_t
*/
void sd_add_wait(wait_queue_t *wait)
{
add_wait_queue(&Device.dma_wq, wait);
set_current_state(TASK_INTERRUPTIBLE);
}
/*
* sd_rm_wait routine.
*
* @param wait: pointer wait_queue_t
*/
void sd_rm_wait(wait_queue_t *wait)
{
remove_wait_queue(&Device.dma_wq, wait);
}
/*
* write operation routine.
* @param dev: pointer sd_device
*
* @return value: 0-- write operation failture num-- num block of write operation successed
*
*/
static int sd_do_tran(struct sd_device *dev)
{
unsigned int offset,num,ret=0,retry=0;
char *buf;
offset=dev->waddr;
num=dev->wblock_num;
#ifdef CONFIG_SSP_DMA
buf=dev->writebuf;
#else
buf=&dev->writebuf[0];
#endif
if(num && num <=MAXNUM)
{
retry=0;
do
{
if(Device.sd_online==0)
{
ret=100;break;
}
#ifdef CONFIG_SSP_DMA
ret=sdcard_write(offset,0,num);
#else
ret=sdcard_write(offset,buf,num);
#endif
msleep(SSPDELAYW);
if(ret!=0)
{
/*
DBG(" sdcard_write error offset=%u num=%u ret=%u \n",offset/KERNEL_SECTOR_SIZE,num,ret);
*/
if(retry++ >10) break;
sdcard_init();
#ifdef CONFIG_SSP_DMA
hi_ssp_set_serialclock(0,10);
#else
hi_ssp_set_serialclock(0,6);
#endif
}
}while(ret!=0);
if(ret!=0)
{
dev->wblock_num=0;
dev->woffset=0;
if(dev->sd_error ==0)
DBG(" sdcard_write error offset=%u num=%u ret=%u \n",offset/KERNEL_SECTOR_SIZE,num,ret);
dev->sd_error=1;
return(num);
}
else
{
/*
DBG(" sdcard_write ok offset=%u num=%u \n",offset/KERNEL_SECTOR_SIZE,num);
*/
dev->wblock_num=0;
dev->woffset=0;
dev->sd_error=0;
}
}
return(num);
}
/*
* read operation routine.
* @param dev: pointer sd_device
*
* @return value: 0-- read operation failture num-- num block of read operation successed
*
*/
static int sd_do_tran_read(struct sd_device *dev)
{
unsigned int offset,num,i,ret=0,rbaddr=0,res=0,retry=0;
char *rb;
for(i=0;i<dev->readlist_offset;i++)
{
offset=dev->readlist[i][0];
num=dev->readlist[i][1];
if(i==0)
rbaddr=0;
else
rbaddr += KERNEL_SECTOR_SIZE * num;
retry=0;
do
{
if(Device.sd_online==0)
{
ret=100;break;
}
#ifdef CONFIG_SSP_DMA
rb = dev->readbuf+rbaddr;
ret=sdcard_read(offset,rbaddr,num);
#else
rb = & dev->readbuf[rbaddr];
ret=sdcard_read(offset,rb,num);
#endif
msleep(SSPDELAYR);
if(ret!=0)
{
/*
DBG(" sdcard_read error offset=%u num=%u ret=%u \n",offset/KERNEL_SECTOR_SIZE,num,ret);
*/
if(retry++ >10) break;
sdcard_init();
#ifdef CONFIG_SSP_DMA
hi_ssp_set_serialclock(0,10);
#else
hi_ssp_set_serialclock(0,6);
#endif
}
}while(ret!=0);
if(ret!=0)
{
dev->raddr = 0;
dev->readlist_offset=0;
if(dev->sd_error ==0)
DBG(" sdcard_read error offset=%u num=%u ret=%u \n",offset/KERNEL_SECTOR_SIZE,num,ret);
res += num;
dev->sd_error=1;
}
else
{
/*
DBG(" sdcard_read ok offset=%u num=%u \n",offset/KERNEL_SECTOR_SIZE,num);
*/
res += num;
dev->sd_error=0;
}
}
dev->raddr = 0;
dev->readlist_offset=0;
return(res);
}
/*
* save data writed to buf routine.
* @param dev : pointer sd_device
* @param sector: data start sector
* @param nsect : sector num
* @param buffer: data buffer
* @param write : write or read flag
*
* @return value: 0-- error 1--success
*
*/
static int sd_transfer(struct sd_device *dev, unsigned int sector,unsigned int nsect, char *buffer, int write)
{
unsigned int offset = sector*KERNEL_SECTOR_SIZE;
unsigned int nbytes = nsect*KERNEL_SECTOR_SIZE;
unsigned int i;
unsigned char *buf=buffer;
if ((offset + nbytes) > dev->size)
{
printk (KERN_NOTICE "sd: Beyond-end write (%u %u)\n", offset, nbytes);
return(0);
}
if (write)
{
if(dev->wblock_num == 0)
dev->waddr = offset;
for(i=0;i<nbytes;i++)
#ifdef CONFIG_SSP_DMA
*(dev->writebuf+i + dev->woffset) = *buf++;
#else
dev->writebuf[i + dev->woffset] = *buf++;
#endif
dev->wblock_num += nsect;
dev->woffset += nbytes;
buf = buffer;
}
else
{
if(dev->readlist_offset ==0)
{
dev->readlist[0][0]=offset;
dev->readlist[0][1]=nsect;
dev->readlist_offset++;
}
else
{
if(offset == (dev->readlist[dev->readlist_offset-1][0] + KERNEL_SECTOR_SIZE*dev->readlist[dev->readlist_offset-1][1]) )
{
dev->readlist[dev->readlist_offset-1][1]+=nsect;
}
else
{
dev->readlist[dev->readlist_offset][0]=offset;
dev->readlist[dev->readlist_offset][1]=nsect;
dev->readlist_offset++;
}
}
}
return(1);
}
/*
* copy read data to request queue routine.
* @param dev : pointer sd_device
* @param sector: data start sector
* @param nsect : sector num
* @param buffer: data buffer
* @param write : write or read flag
*
* @return value: 0-- error 1--success
*
*/
static int sd_transfer_read(struct sd_device *dev, unsigned int sector,unsigned int nsect, char *buffer, int write)
{
unsigned int offset = sector*KERNEL_SECTOR_SIZE;
unsigned int nbytes = nsect*KERNEL_SECTOR_SIZE;
unsigned int i;
unsigned char *buf=buffer;
if ((offset + nbytes) > dev->size)
{
printk (KERN_NOTICE "sd: Beyond-end write (%u %u)\n", offset, nbytes);
return(0);
}
for(i=0;i<nbytes;i++)
{
#ifdef CONFIG_SSP_DMA
*buf++ = *(dev->readbuf + dev->raddr++);
#else
*buf++ = dev->readbuf[dev->raddr++];
#endif
}
return(1);
}
/*
* sd xfer bio request routine.
* @param dev : pointer sd_device
* @param bio : pointer bio
*
* @return value: 0-- success
*
*/
static int sd_xfer_bio(struct sd_device *dev, struct bio *bio)
{
unsigned int i;
struct bio_vec *bvec;
sector_t sector = bio->bi_sector;
bio_for_each_segment(bvec, bio, i)
{
char *buffer = __bio_kmap_atomic(bio, i, KM_USER0);
sd_transfer(dev, sector, bio_cur_sectors(bio), buffer, bio_data_dir(bio) == WRITE);
sector += bio_cur_sectors(bio);
__bio_kunmap_atomic(bio, KM_USER0);
}
return 0;
}
/*
* sd xfer bio2 request routine.
* @param dev : pointer sd_device
* @param bio : pointer bio
*
* @return value: 0-- success
*
*/
static int sd_xfer_bio2(struct sd_device *dev, struct bio *bio)
{
unsigned int i;
struct bio_vec *bvec;
sector_t sector = bio->bi_sector;
bio_for_each_segment(bvec, bio, i)
{
char *buffer = __bio_kmap_atomic(bio, i, KM_USER0);
sd_transfer_read(dev, sector, bio_cur_sectors(bio), buffer, bio_data_dir(bio) == WRITE);
sector += bio_cur_sectors(bio);
__bio_kunmap_atomic(bio, KM_USER0);
}
return 0;
}
/*
* sd xfer request routine.
* @param dev : pointer sd_device
* @param req : pointer request
*
* @return value: success multiple block write/read num (sector num)
*
*/
static int sd_xfer_request(struct sd_device *dev, struct request *req)
{
struct bio *bio;
unsigned int nsect = 0,ret,wflag=0;
struct request *req2=req;
rq_for_each_bio(bio, req)
{
sd_xfer_bio(dev, bio);
nsect += bio->bi_size/KERNEL_SECTOR_SIZE;
if( bio_data_dir(bio) == WRITE)
wflag=1;
else
wflag=0;
}
if(wflag)
{
ret = sd_do_tran(&Device);
return ret;
}
else
{
ret = sd_do_tran_read(&Device);
if(ret)
{
nsect=0;
rq_for_each_bio(bio, req2)
{
sd_xfer_bio2(dev, bio);
nsect += bio->bi_size/KERNEL_SECTOR_SIZE;
}
}
return ret;
}
}
/*
* blk device request routine.
* @param q: pointer request_queue_t
*
*/
static void sd_request(request_queue_t *q)
{
wake_up(&Device.wq);
}
/*
* get geo.heads routine.
* @param size: sdcard size;
*
* @return value:heads
*
*/
static unsigned int get_heads(unsigned long size)
{
unsigned int temp=0;
if( size<(25*1000*1000) )
temp = 2;
else if( (25*1000*1000)<=size && size<(100*1000*1000) )
temp = 4;
else if( (100*1000*1000)<=size && size<(200*1000*1000) )
temp = 8;
else if( (200*1000*1000)<=size && size<(1000*1000*1000) )
temp = 16;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -