/*
|
/*
|
*
|
*
|
* Block driver for spiMaster to drive SD card
|
* Block driver for spiMaster to drive SD card
|
*
|
*
|
* Copyright (c) 2008 by:
|
* Copyright (c) 2008 by:
|
* Xianfeng Zeng <xianfeng.zeng@gmail.com>
|
* Xianfeng Zeng <xianfeng.zeng@gmail.com>
|
*
|
*
|
* This program is free software; you can redistribute it and/or modify
|
* This program is free software; you can redistribute it and/or modify
|
* it under the terms of the BSD Licence, GNU General Public License
|
* it under the terms of the BSD Licence, GNU General Public License
|
* as published by the Free Software Foundation; either version 2 of the
|
* as published by the Free Software Foundation; either version 2 of the
|
* License, or (at your option) any later version
|
* License, or (at your option) any later version
|
*
|
*
|
* ChangeLog:
|
* ChangeLog:
|
* 2009-11-28 15:11:18 Xianfeng Zeng
|
* 2009-11-28 15:11:18 Xianfeng Zeng
|
* Init.
|
* Init.
|
*
|
*
|
*/
|
*/
|
|
|
#include <linux/version.h>
|
#include <linux/version.h>
|
#include <linux/module.h>
|
#include <linux/module.h>
|
#include <linux/init.h>
|
#include <linux/init.h>
|
#include <linux/kernel.h>
|
#include <linux/kernel.h>
|
#include <linux/sched.h>
|
#include <linux/sched.h>
|
#include <linux/fs.h>
|
#include <linux/fs.h>
|
#include <linux/genhd.h>
|
#include <linux/genhd.h>
|
#include <linux/delay.h>
|
#include <linux/delay.h>
|
|
|
#include <linux/slab.h> /* kmalloc() */
|
#include <linux/slab.h> /* kmalloc() */
|
#include <linux/errno.h> /* error codes */
|
#include <linux/errno.h> /* error codes */
|
#include <linux/timer.h>
|
#include <linux/timer.h>
|
#include <linux/types.h> /* size_t */
|
#include <linux/types.h> /* size_t */
|
#include <linux/fcntl.h> /* O_ACCMODE */
|
#include <linux/fcntl.h> /* O_ACCMODE */
|
#include <linux/hdreg.h> /* HDIO_GETGEO */
|
#include <linux/hdreg.h> /* HDIO_GETGEO */
|
#include <linux/kdev_t.h>
|
#include <linux/kdev_t.h>
|
#include <linux/vmalloc.h>
|
#include <linux/vmalloc.h>
|
#include <linux/genhd.h>
|
#include <linux/genhd.h>
|
#include <linux/blkdev.h>
|
#include <linux/blkdev.h>
|
#include <linux/buffer_head.h> /* invalidate_bdev */
|
#include <linux/buffer_head.h> /* invalidate_bdev */
|
#include <linux/bio.h>
|
#include <linux/bio.h>
|
|
|
|
|
#include "sd_card.h"
|
#include "sd_card.h"
|
|
|
#define SDC_DEBUG 1
|
//#define SDC_DEBUG 1
|
|
|
#ifdef SDC_DEBUG
|
#ifdef SDC_DEBUG
|
# define SDC_PRINTK(fmt, args...) printk( KERN_WARNING "sd_card:" fmt, ## args)
|
# define SDC_PRINTK(fmt, args...) printk( KERN_WARNING "sd_card:" fmt, ## args)
|
#else
|
#else
|
# define SDC_PRINTK(fmt, args...)
|
# define SDC_PRINTK(fmt, args...)
|
#endif
|
#endif
|
|
|
#define DRIVER_NAME "sd_card"
|
#define DRIVER_NAME "sd_card"
|
#define SD_CARD_MINORS 8
|
#define SD_CARD_MINORS 8
|
#define SD_CARD_SIZE 1995440128
|
#define SD_CARD_SIZE 1995440128
|
|
//(512*1024*1024)
|
|
//1995440128
|
|
|
static int major = 8;
|
static int major = 8;
|
|
|
|
|
/**
|
/**
|
* ============================================================
|
* ============================================================
|
*
|
*
|
* ============================================================
|
* ============================================================
|
*/
|
*/
|
int spiMaster_init(void *vbase)
|
int spiMaster_init(void *vbase)
|
{
|
{
|
unsigned char data;
|
unsigned char data;
|
int i;
|
int i;
|
|
|
SDC_PRINTK("spiMaster_init begin\n");
|
SDC_PRINTK("spiMaster_init begin\n");
|
|
|
for (i = 0; i < 5; i++) {
|
for (i = 0; i < 5; i++) {
|
REG8(vbase + SD_TRANS_TYPE_REG) = SD_INIT_SD;
|
REG8(vbase + SD_TRANS_TYPE_REG) = SD_INIT_SD;
|
REG8(vbase + SD_TRANS_CTRL_REG) = 1; // TRANS_START;
|
REG8(vbase + SD_TRANS_CTRL_REG) = 1; // TRANS_START;
|
|
|
mdelay(1);
|
mdelay(1);
|
|
|
while (REG8(vbase + SD_TRANS_STS_REG) & 0x1) { // exit while !TRABS_BUSY
|
while (REG8(vbase + SD_TRANS_STS_REG) & 0x1) { // exit while !TRABS_BUSY
|
;
|
;
|
}
|
}
|
|
|
data = REG8(vbase + SD_TRANS_ERROR_REG) & 0x3;
|
data = REG8(vbase + SD_TRANS_ERROR_REG) & 0x3;
|
|
|
if (data == 0) {
|
if (data == 0) {
|
SDC_PRINTK("spiMaster_init done\n");
|
SDC_PRINTK("spiMaster_init done\n");
|
return 0;
|
return 0;
|
}
|
}
|
}
|
}
|
|
|
SDC_PRINTK("spiMaster_init failed\n");
|
SDC_PRINTK("spiMaster_init failed\n");
|
|
|
return data;
|
return data;
|
}
|
}
|
|
|
/**
|
/**
|
* ============================================================
|
* ============================================================
|
* Request Handling
|
* Request Handling
|
* ============================================================
|
* ============================================================
|
*/
|
*/
|
|
|
static int read_sd_block(void *sd_vbase, unsigned long sector,
|
static int read_sd_block(void *sd_vbase, unsigned long sector,
|
unsigned long nsect, char *buffer)
|
unsigned long nsect, char *buffer)
|
{
|
{
|
unsigned char data;
|
unsigned char data;
|
unsigned char transError;
|
unsigned char transError;
|
int i;
|
int i;
|
unsigned int buffer_offset = 0;
|
unsigned int buffer_offset = 0;
|
unsigned int blockCnt;
|
unsigned int blockCnt;
|
|
unsigned int start = sector << 9; /* x512*/
|
|
|
SDC_PRINTK("read_sd_block begin: sector=%d, nsect=%d\n", sector, nsect);
|
SDC_PRINTK("read_sd_block begin: sector=%ld, nsect=%ld\n", sector, nsect);
|
|
|
for (blockCnt = 0; blockCnt < nsect; blockCnt++) {
|
for (blockCnt = 0; blockCnt < nsect; blockCnt++) {
|
REG8(sd_vbase + SD_ADDR_7_0_REG) = 0;
|
REG8(sd_vbase + SD_ADDR_7_0_REG) = 0;
|
REG8(sd_vbase + SD_ADDR_15_8_REG) = (unsigned char) ((sector >> 8) & 0xff);
|
REG8(sd_vbase + SD_ADDR_15_8_REG) = (unsigned char) ((start >> 8) & 0xff);
|
REG8(sd_vbase + SD_ADDR_23_16_REG) = (unsigned char) ((sector >> 16) & 0xff);
|
REG8(sd_vbase + SD_ADDR_23_16_REG) = (unsigned char) ((start >> 16) & 0xff);
|
REG8(sd_vbase + SD_ADDR_31_24_REG) = (unsigned char) ((sector >> 24) & 0xff);
|
REG8(sd_vbase + SD_ADDR_31_24_REG) = (unsigned char) ((start >> 24) & 0xff);
|
|
|
REG8(sd_vbase + SD_TRANS_TYPE_REG) = SD_RW_READ_SD_BLOCK;
|
REG8(sd_vbase + SD_TRANS_TYPE_REG) = SD_RW_READ_SD_BLOCK;
|
REG8(sd_vbase + SD_RX_FIFO_CONTROL_REG) = 0x1; /* Clean the RX FIFO */
|
REG8(sd_vbase + SD_RX_FIFO_CONTROL_REG) = 0x1; /* Clean the RX FIFO */
|
REG8(sd_vbase + SD_TRANS_CTRL_REG) = 0x1; /* TRANS_START */
|
REG8(sd_vbase + SD_TRANS_CTRL_REG) = 0x1; /* TRANS_START */
|
|
|
|
|
while (REG8(sd_vbase + SD_TRANS_STS_REG) & 0x1) { /* exit while !TRABS_BUSY */
|
while (REG8(sd_vbase + SD_TRANS_STS_REG) & 0x1) { /* exit while !TRABS_BUSY */
|
;
|
;
|
}
|
}
|
|
|
transError = REG8(sd_vbase + SD_TRANS_ERROR_REG) & 0xc;
|
transError = REG8(sd_vbase + SD_TRANS_ERROR_REG) & 0xc;
|
if ( transError == SD_READ_NO_ERROR) {
|
if ( transError == SD_READ_NO_ERROR) {
|
for (i = 0; i < 512; i++) {
|
for (i = 0; i < 512; i++) {
|
data = REG8(sd_vbase + SD_RX_FIFO_DATA_REG) ;
|
data = REG8(sd_vbase + SD_RX_FIFO_DATA_REG) ;
|
REG8(buffer + buffer_offset + i) = data ;
|
//REG8(buffer + buffer_offset + i) = data ;
|
|
buffer[buffer_offset + i] = data ;
|
|
// printk("%x\t",data);
|
|
// if (i%16 == 0) {
|
|
// printk("\n");
|
|
// }
|
}
|
}
|
|
// printk("\n");
|
buffer_offset += 512;
|
buffer_offset += 512;
|
sector += 512;
|
start += 512;
|
} else {
|
} else {
|
SDC_PRINTK("read_sd_block failed. Re-try\n");
|
SDC_PRINTK("read_sd_block failed. Re-try\n");
|
spiMaster_init(sd_vbase); /* Init again and retry */
|
spiMaster_init(sd_vbase); /* Init again and retry */
|
blockCnt--; /* read the same block again */
|
blockCnt--; /* read the same block again */
|
}
|
}
|
}
|
}
|
|
|
SDC_PRINTK("read_sd_block done\n");
|
SDC_PRINTK("read_sd_block done\n");
|
|
|
return 0;
|
return 0;
|
}
|
}
|
|
|
static int write_sd_block(void *sd_vbase, unsigned long sector,
|
static int write_sd_block(void *sd_vbase, unsigned long sector,
|
unsigned long nsect, char *buffer)
|
unsigned long nsect, char *buffer)
|
{
|
{
|
int i;
|
int i;
|
unsigned char data;
|
unsigned char data;
|
unsigned int blockCnt;
|
unsigned int blockCnt;
|
char *p = buffer;
|
char *p = buffer;
|
|
unsigned int start = sector << 9; /* x512*/
|
|
|
SDC_PRINTK("write_sd_block begin: sector=%d, nsect=%d\n", sector, nsect);
|
|
|
SDC_PRINTK("write_sd_block begin: sector=%ld, nsect=%ld\n", sector, nsect);
|
|
|
for (blockCnt = 0; blockCnt < nsect; blockCnt++) {
|
for (blockCnt = 0; blockCnt < nsect; blockCnt++) {
|
|
|
/* Clean the TX FIFO */
|
/* Clean the TX FIFO */
|
REG8(sd_vbase + SD_TX_FIFO_CONTROL_REG) = 0x1;
|
REG8(sd_vbase + SD_TX_FIFO_CONTROL_REG) = 0x1;
|
|
|
REG8(sd_vbase + SD_ADDR_7_0_REG) = 0;
|
REG8(sd_vbase + SD_ADDR_7_0_REG) = 0;
|
REG8(sd_vbase + SD_ADDR_15_8_REG) = (unsigned char) ((sector >> 8) & 0xff);
|
REG8(sd_vbase + SD_ADDR_15_8_REG) = (unsigned char) ((start >> 8) & 0xff);
|
REG8(sd_vbase + SD_ADDR_23_16_REG) = (unsigned char) ((sector >> 16) & 0xff);
|
REG8(sd_vbase + SD_ADDR_23_16_REG) = (unsigned char) ((start >> 16) & 0xff);
|
REG8(sd_vbase + SD_ADDR_31_24_REG) = (unsigned char) ((sector >> 24) & 0xff);
|
REG8(sd_vbase + SD_ADDR_31_24_REG) = (unsigned char) ((start >> 24) & 0xff);
|
|
|
|
|
// Write data to TX_FIFO_DATA_REG
|
// Write data to TX_FIFO_DATA_REG
|
for (i = 0; i < 512; i++) {
|
for (i = 0; i < 512; i++) {
|
REG8(sd_vbase + SD_TX_FIFO_DATA_REG) = p[i];
|
REG8(sd_vbase + SD_TX_FIFO_DATA_REG) = p[i];
|
}
|
}
|
|
|
|
|
REG8(sd_vbase + SD_TRANS_TYPE_REG) = SD_RW_WRITE_SD_BLOCK;
|
REG8(sd_vbase + SD_TRANS_TYPE_REG) = SD_RW_WRITE_SD_BLOCK;
|
REG8(sd_vbase + SD_TRANS_CTRL_REG) = 1; /* TRANS_START */
|
REG8(sd_vbase + SD_TRANS_CTRL_REG) = 1; /* TRANS_START */
|
|
|
|
udelay(10);
|
while (REG8(sd_vbase + SD_TRANS_STS_REG) & 0x1) { /* exit while !TRABS_BUSY */
|
while (REG8(sd_vbase + SD_TRANS_STS_REG) & 0x1) { /* exit while !TRABS_BUSY */
|
;
|
;
|
}
|
}
|
|
|
data = REG8(sd_vbase + SD_TRANS_ERROR_REG) & 0x30;
|
data = REG8(sd_vbase + SD_TRANS_ERROR_REG) & 0x30;
|
if (data == 1) {
|
if (data == 1) {
|
SDC_PRINTK("WRITE_CMD_ERROR\n\r");
|
printk("sd_card:WRITE_CMD_ERROR\n\r");
|
} else if (data == 2) {
|
} else if (data == 2) {
|
SDC_PRINTK("WRITE_DATA_ERROR\n\r");
|
printk("sd_card:WRITE_DATA_ERROR\n\r");
|
} else if (data == 3) {
|
} else if (data == 3) {
|
SDC_PRINTK("WRITE_BUSY_ERROR\n\r");
|
printk("sd_card:WRITE_BUSY_ERROR\n\r");
|
}
|
}
|
|
|
sector += 512;
|
start += 512;
|
p += 512;
|
p += 512;
|
}
|
}
|
|
|
SDC_PRINTK("write_sd_block done\n");
|
SDC_PRINTK("write_sd_block done\n");
|
return 0;
|
return 0;
|
}
|
}
|
|
|
/*
|
/*
|
* Handle an I/O request.
|
* Handle an I/O request.
|
*/
|
*/
|
static void sd_card_transfer(struct sd_card_dev *dev, unsigned long sector,
|
static void sd_card_transfer(struct sd_card_dev *dev, unsigned long sector,
|
unsigned long nsect, char *buffer, int write)
|
unsigned long nsect, char *buffer, int write)
|
{
|
{
|
unsigned long offset = sector*512;
|
unsigned long offset = sector*512;
|
unsigned long nbytes = nsect*512;
|
unsigned long nbytes = nsect*512;
|
|
|
if ((offset + nbytes) > dev->size) {
|
if ((offset + nbytes) > dev->size) {
|
printk (KERN_NOTICE "Beyond-end write (%ld %ld)\n", offset, nbytes);
|
printk (KERN_NOTICE "Beyond-end write (%ld %ld)\n", offset, nbytes);
|
return;
|
return;
|
}
|
}
|
if (write)
|
if (write)
|
write_sd_block(dev->vir_base, sector, nsect, buffer);
|
write_sd_block(dev->vir_base, sector, nsect, buffer);
|
else
|
else
|
read_sd_block(dev->vir_base, sector, nsect, buffer);
|
read_sd_block(dev->vir_base, sector, nsect, buffer);
|
}
|
}
|
|
|
|
|
/*
|
/*
|
* The simple form of the request function.
|
* The simple form of the request function.
|
*/
|
*/
|
static void sd_card_request(request_queue_t *q)
|
static void sd_card_request(request_queue_t *q)
|
{
|
{
|
struct request *req;
|
struct request *req;
|
|
|
while ((req = elv_next_request(q)) != NULL) {
|
while ((req = elv_next_request(q)) != NULL) {
|
struct sd_card_dev *dev = req->rq_disk->private_data;
|
struct sd_card_dev *dev = req->rq_disk->private_data;
|
if (! blk_fs_request(req)) {
|
if (! blk_fs_request(req)) {
|
printk (KERN_NOTICE "Skip non-fs request\n");
|
printk (KERN_NOTICE "Skip non-fs request\n");
|
end_request(req, 0);
|
end_request(req, 0);
|
continue;
|
continue;
|
}
|
}
|
// printk (KERN_NOTICE "Req dev %d dir %ld sec %ld, nr %d f %lx\n",
|
// printk (KERN_NOTICE "Req dev %d dir %ld sec %ld, nr %d f %lx\n",
|
// dev - Devices, rq_data_dir(req),
|
// dev - Devices, rq_data_dir(req),
|
// req->sector, req->current_nr_sectors,
|
// req->sector, req->current_nr_sectors,
|
// req->flags);
|
// req->flags);
|
sd_card_transfer(dev, req->sector, req->current_nr_sectors,
|
sd_card_transfer(dev, req->sector, req->current_nr_sectors,
|
req->buffer, rq_data_dir(req));
|
req->buffer, rq_data_dir(req));
|
end_request(req, 1);
|
end_request(req, 1);
|
}
|
}
|
}
|
}
|
|
|
/**
|
/**
|
* ============================================================
|
* ============================================================
|
* Block device operations
|
* Block device operations
|
* ============================================================
|
* ============================================================
|
*/
|
*/
|
static int sd_card_open(struct inode *inode, struct file *filp)
|
static int sd_card_open(struct inode *inode, struct file *filp)
|
{
|
{
|
struct sd_card_dev *dev = inode->i_bdev->bd_disk->private_data;
|
struct sd_card_dev *dev = inode->i_bdev->bd_disk->private_data;
|
|
|
SDC_PRINTK("sd_card_open beging\n");
|
SDC_PRINTK("sd_card_open beging\n");
|
|
|
filp->private_data = dev;
|
filp->private_data = dev;
|
spin_lock(&dev->lock);
|
spin_lock(&dev->lock);
|
if (! dev->users)
|
if (! dev->users)
|
check_disk_change(inode->i_bdev);
|
check_disk_change(inode->i_bdev);
|
|
|
/* Init SD card controller */
|
/* Init SD card controller */
|
spiMaster_init(dev->vir_base);
|
spiMaster_init(dev->vir_base);
|
|
|
dev->users++;
|
dev->users++;
|
spin_unlock(&dev->lock);
|
spin_unlock(&dev->lock);
|
|
|
SDC_PRINTK("sd_card_open done\n");
|
SDC_PRINTK("sd_card_open done\n");
|
|
|
return 0;
|
return 0;
|
}
|
}
|
|
|
static int sd_card_release(struct inode *inode, struct file *filp)
|
static int sd_card_release(struct inode *inode, struct file *filp)
|
{
|
{
|
struct sd_card_dev *dev = inode->i_bdev->bd_disk->private_data;
|
struct sd_card_dev *dev = inode->i_bdev->bd_disk->private_data;
|
|
|
SDC_PRINTK("sd_card_release beging\n");
|
SDC_PRINTK("sd_card_release beging\n");
|
|
|
spin_lock(&dev->lock);
|
spin_lock(&dev->lock);
|
dev->users--;
|
dev->users--;
|
|
|
spin_unlock(&dev->lock);
|
spin_unlock(&dev->lock);
|
|
|
SDC_PRINTK("sd_card_release done\n");
|
SDC_PRINTK("sd_card_release done\n");
|
|
|
return 0;
|
return 0;
|
}
|
}
|
|
|
/*
|
/*
|
* Look for a (simulated) media change.
|
* Look for a (simulated) media change.
|
*/
|
*/
|
int sd_card_media_changed(struct gendisk *gd)
|
int sd_card_media_changed(struct gendisk *gd)
|
{
|
{
|
//struct sd_card_dev *dev = gd->private_data;
|
//struct sd_card_dev *dev = gd->private_data;
|
|
|
return 0; //dev->media_change;
|
return 0; //dev->media_change;
|
}
|
}
|
|
|
/*
|
/*
|
* Revalidate. WE DO NOT TAKE THE LOCK HERE, for fear of deadlocking
|
* Revalidate. WE DO NOT TAKE THE LOCK HERE, for fear of deadlocking
|
* with open. That needs to be reevaluated.
|
* with open. That needs to be reevaluated.
|
*/
|
*/
|
int sd_card_revalidate(struct gendisk *gd)
|
int sd_card_revalidate(struct gendisk *gd)
|
{
|
{
|
struct sd_card_dev *dev = gd->private_data;
|
struct sd_card_dev *dev = gd->private_data;
|
|
|
if (dev->media_change) {
|
if (dev->media_change) {
|
dev->media_change = 0;
|
dev->media_change = 0;
|
// memset (dev->data, 0, dev->size);
|
// memset (dev->data, 0, dev->size);
|
}
|
}
|
return 0;
|
return 0;
|
}
|
}
|
|
|
|
|
/*
|
/*
|
* The ioctl() implementation
|
* The ioctl() implementation
|
*/
|
*/
|
|
|
|
static int sd_card_getgeo(struct block_device *bdev,
|
|
struct hd_geometry *geo)
|
|
{
|
|
/*
|
|
* capacity heads sectors cylinders
|
|
* 0~16M 1 1 0~32768
|
|
* 16M~512M 1 32 1024~32768
|
|
* 512M~16G 32 32 1024~32768
|
|
* 16G~... 255 63 2088~...
|
|
*/
|
|
if (SD_CARD_SIZE < 16 * 1024 * 1024) {
|
|
geo->heads = 1;
|
|
geo->sectors = 1;
|
|
|
|
} else if (SD_CARD_SIZE < 512 * 1024 * 1024) {
|
|
geo->heads = 1;
|
|
geo->sectors = 32;
|
|
} else if (SD_CARD_SIZE < 16ULL * 1024 * 1024 * 1024) {
|
|
geo->heads = 32;
|
|
geo->sectors = 32;
|
|
} else {
|
|
geo->heads = 255;
|
|
geo->sectors = 63;
|
|
}
|
|
|
|
geo->cylinders = SD_CARD_SIZE>>9/geo->heads/geo->sectors;
|
|
|
|
return 0;
|
|
}
|
|
|
int sd_card_ioctl (struct inode *inode, struct file *filp,
|
int sd_card_ioctl (struct inode *inode, struct file *filp,
|
unsigned int cmd, unsigned long arg)
|
unsigned int cmd, unsigned long arg)
|
{
|
{
|
long size;
|
long size;
|
struct hd_geometry geo;
|
struct hd_geometry geo;
|
struct sd_card_dev *dev = filp->private_data;
|
struct sd_card_dev *dev = filp->private_data;
|
|
|
switch(cmd) {
|
switch(cmd) {
|
case HDIO_GETGEO:
|
case HDIO_GETGEO:
|
/*
|
/*
|
* Get geometry: since we are a virtual device, we have to make
|
* Get geometry: since we are a virtual device, we have to make
|
* up something plausible. So we claim 16 sectors, four heads,
|
* up something plausible. So we claim 16 sectors, four heads,
|
* and calculate the corresponding number of cylinders. We set the
|
* and calculate the corresponding number of cylinders. We set the
|
* start of data at sector four.
|
* start of data at sector four.
|
*/
|
*/
|
size = dev->size;
|
size = dev->size;
|
geo.cylinders = (size & ~0x3f) >> 6;
|
geo.cylinders = (size & ~0x3f) >> 6;
|
geo.heads = 62; //32;
|
geo.heads = 62; //32;
|
geo.sectors = 62; //63;
|
geo.sectors = 62; //63;
|
geo.start = get_start_sect(inode->i_bdev);
|
geo.start = get_start_sect(inode->i_bdev);
|
if (copy_to_user((void __user *) arg, &geo, sizeof(geo)))
|
if (copy_to_user((void __user *) arg, &geo, sizeof(geo)))
|
return -EFAULT;
|
return -EFAULT;
|
return 0;
|
return 0;
|
}
|
}
|
|
|
return -ENOTTY; /* unknown command */
|
return -ENOTTY; /* unknown command */
|
}
|
}
|
|
|
|
|
/*
|
/*
|
* The device operations structure.
|
* The device operations structure.
|
*/
|
*/
|
static struct block_device_operations sd_card_ops = {
|
static struct block_device_operations sd_card_ops = {
|
.owner = THIS_MODULE,
|
.owner = THIS_MODULE,
|
.open = sd_card_open,
|
.open = sd_card_open,
|
.release = sd_card_release,
|
.release = sd_card_release,
|
.media_changed = sd_card_media_changed,
|
.media_changed = sd_card_media_changed,
|
.revalidate_disk = sd_card_revalidate,
|
.revalidate_disk = sd_card_revalidate,
|
.ioctl = sd_card_ioctl
|
// .ioctl = sd_card_ioctl,
|
|
.getgeo = sd_card_getgeo
|
};
|
};
|
|
|
/**
|
/**
|
* ============================================================
|
* ============================================================
|
* Driver Entry and clean up
|
* Driver Entry and clean up
|
* ============================================================
|
* ============================================================
|
*/
|
*/
|
static int __init sd_card_init(void)
|
static int __init sd_card_init(void)
|
{
|
{
|
int ret;
|
int ret;
|
struct sd_card_dev *dev;
|
struct sd_card_dev *dev;
|
|
|
/* Put the init code here */
|
/* Put the init code here */
|
printk(KERN_INFO "SD Card Driver Enter.\n");
|
printk(KERN_INFO "SD Card Driver Enter.\n");
|
|
|
dev = (struct sd_card_dev *)kmalloc(sizeof(struct sd_card_dev), GFP_KERNEL);
|
dev = (struct sd_card_dev *)kmalloc(sizeof(struct sd_card_dev), GFP_KERNEL);
|
if (dev < 0) {
|
if (dev < 0) {
|
printk(KERN_WARNING "lack of memory\n");
|
printk(KERN_WARNING "lack of memory\n");
|
return -1;
|
return -1;
|
}
|
}
|
memset(dev, 0, sizeof(struct sd_card_dev));
|
memset(dev, 0, sizeof(struct sd_card_dev));
|
|
|
ret = register_blkdev(major, DRIVER_NAME);
|
ret = register_blkdev(major, DRIVER_NAME);
|
if (ret <0) {
|
if (ret <0) {
|
printk(KERN_WARNING "sd_card: unable to get major number\n");
|
printk(KERN_WARNING "sd_card: unable to get major number\n");
|
kfree(dev);
|
kfree(dev);
|
return -EBUSY;
|
return -EBUSY;
|
}
|
}
|
|
|
dev->size = SD_CARD_SIZE; //1015808000; /* 1GB */
|
dev->size = SD_CARD_SIZE; //1015808000; /* 1GB */
|
spin_lock_init(&dev->lock);
|
spin_lock_init(&dev->lock);
|
dev->queue = blk_init_queue(sd_card_request, &dev->lock);
|
dev->queue = blk_init_queue(sd_card_request, &dev->lock);
|
blk_queue_hardsect_size(dev->queue, 512);
|
blk_queue_hardsect_size(dev->queue, 512);
|
|
|
dev->vir_base = ioremap(SD_BASE_ADD, 0x2000);
|
dev->vir_base = ioremap(SD_BASE_ADD, 0x2000);
|
|
|
/* Reset spiMaster controller */
|
/* Reset spiMaster controller */
|
REG8(dev->vir_base + SD_TRANS_CTRL_REG) = 0x1; /* reset spiMaster */
|
REG8(dev->vir_base + SD_TRANS_CTRL_REG) = 0x1; /* reset spiMaster */
|
mdelay(1);
|
mdelay(1);
|
REG8(dev->vir_base + SD_TRANS_CTRL_REG) = 0x0;
|
REG8(dev->vir_base + SD_TRANS_CTRL_REG) = 0x0;
|
REG8(dev->vir_base + SD_CLK_DEL_REG) = 0x1;
|
REG8(dev->vir_base + SD_CLK_DEL_REG) = 0x1;
|
|
|
/* allocate gendsik and init it */
|
/* allocate gendsik and init it */
|
dev->gd = alloc_disk(SD_CARD_MINORS);
|
dev->gd = alloc_disk(SD_CARD_MINORS);
|
if (!dev->gd) {
|
if (!dev->gd) {
|
printk(KERN_WARNING "alloc_disk failed\n");
|
printk(KERN_WARNING "alloc_disk failed\n");
|
kfree(dev);
|
kfree(dev);
|
unregister_blkdev(major, DRIVER_NAME);
|
unregister_blkdev(major, DRIVER_NAME);
|
return -1;
|
return -1;
|
}
|
}
|
dev->gd->major = major;
|
dev->gd->major = major;
|
dev->gd->first_minor = 0;
|
dev->gd->first_minor = 0;
|
dev->gd->fops = &sd_card_ops;
|
dev->gd->fops = &sd_card_ops;
|
dev->gd->queue = dev->queue;
|
dev->gd->queue = dev->queue;
|
dev->gd->private_data = dev;
|
dev->gd->private_data = dev;
|
snprintf(dev->gd->disk_name, 32, "sd%c", 'a');
|
snprintf(dev->gd->disk_name, 32, "sd%c", 'a');
|
|
|
set_capacity(dev->gd, dev->size/512);
|
set_capacity(dev->gd, dev->size/512);
|
|
|
add_disk(dev->gd);
|
add_disk(dev->gd);
|
|
|
SDC_PRINTK("sd_card_init done\n");
|
SDC_PRINTK("sd_card_init done\n");
|
|
|
return 0;
|
return 0;
|
}
|
}
|
|
|
|
|
static void __exit sd_card_exit(void)
|
static void __exit sd_card_exit(void)
|
{
|
{
|
/* Put the driver clean up code here */
|
/* Put the driver clean up code here */
|
|
|
unregister_blkdev(major, DRIVER_NAME);
|
unregister_blkdev(major, DRIVER_NAME);
|
|
|
printk(KERN_INFO "SD Card Driver Exit.\n");
|
printk(KERN_INFO "SD Card Driver Exit.\n");
|
}
|
}
|
|
|
//module_init(sd_card_init);
|
//module_init(sd_card_init);
|
late_initcall(sd_card_init);
|
late_initcall(sd_card_init);
|
module_exit(sd_card_exit);
|
module_exit(sd_card_exit);
|
|
|
MODULE_LICENSE("Dual BSD/GPL");
|
MODULE_LICENSE("Dual BSD/GPL");
|
MODULE_AUTHOR("Xianfeng Zeng - Xianfeng.zeng@SierraAtlantic.com, http://www.LinuxExperts.cn");
|
MODULE_AUTHOR("Xianfeng Zeng - Xianfeng.zeng@SierraAtlantic.com, http://www.LinuxExperts.cn");
|
MODULE_DESCRIPTION("spiMaster driver");
|
MODULE_DESCRIPTION("spiMaster driver");
|
MODULE_VERSION("0.1");
|
MODULE_VERSION("0.1");
|
|
|
|
|