dslinux/linux-2.6.x/arch/arm/mach-nds head.S Kconfig m3cf_c.c m3cf_s.S Makefile sccf_c.c sccf_s.S
amadeus
dslinux_amadeus at user.in-berlin.de
Fri Oct 6 23:50:10 CEST 2006
Update of /cvsroot/dslinux/dslinux/linux-2.6.x/arch/arm/mach-nds
In directory antilope:/tmp/cvs-serv32346/linux-2.6.x/arch/arm/mach-nds
Modified Files:
head.S Kconfig Makefile
Added Files:
m3cf_c.c m3cf_s.S sccf_c.c sccf_s.S
Log Message:
Add CF drivers for supercard and M3.
Index: head.S
===================================================================
RCS file: /cvsroot/dslinux/dslinux/linux-2.6.x/arch/arm/mach-nds/head.S,v
retrieving revision 1.22
retrieving revision 1.23
diff -u -d -r1.22 -r1.23
--- head.S 10 Sep 2006 19:13:07 -0000 1.22
+++ head.S 6 Oct 2006 21:50:07 -0000 1.23
@@ -205,10 +205,10 @@
@ use this RAM.
@-------------------------------------------------------------------------
#ifdef CONFIG_NDS_ROM8BIT
-#if defined(CONFIG_MMC_SCSD) || defined(CONFIG_IDE_NDS_SUPERCARD)
+#if defined(CONFIG_MMC_SCSD) || defined(CONFIG_IDE_NDS_SUPERCARD) || defined(CONFIG_NDS_BLK_SCCF)
sc_set_ram @ swith to RAM mode, restore @SC_LOCK from R2
#endif
-#if defined(CONFIG_MMC_M3SD) || defined(CONFIG_IDE_NDS_M3)
+#if defined(CONFIG_MMC_M3SD) || defined(CONFIG_IDE_NDS_M3) || defined(CONFIG_NDS_BLK_M3CF)
m3_set_ram @ switch to RAM mode
#endif
@ more activation for other cards here
--- NEW FILE: m3cf_s.S ---
/*
* linux/arch/arm/mach-nds/m3cf_s.S - M3 CF driver
*
* Copyright (C) 2006 Amadeus, 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 version 2 as
* published by the Free Software Foundation.
*
* This driver must be in main memory.
*/
/* common macros for all NDS GBA ROM device drivers */
#include <asm/arch/gbarom-macro.S>
.TEXT
/*****************************************************************************/
#define REG_M3CF_STS 0x080C0000 @ Status of the CF Card / Device control
#define REG_M3CF_CMD 0x088E0000 @ Commands sent to control chip and status return
#define REG_M3CF_ERR 0x08820000 @ Errors / Features
#define REG_M3CF_SEC 0x08840000 @ Number of sector to transfer
#define REG_M3CF_LBA1 0x08860000 @ 1st byte of sector address
#define REG_M3CF_LBA2 0x08880000 @ 2nd byte of sector address
#define REG_M3CF_LBA3 0x088A0000 @ 3rd byte of sector address
#define REG_M3CF_LBA4 0x088C0000 @ last nibble of sector address | 0xE0
#define REG_M3CF_DATA 0x08800000 @ Pointer to buffer of CF data transered from card
#define CF_STS_INSERTED 0x50
#define CF_STS_READY 0x58
#define CF_STS_BUSY 0x80
/*****************************************************************************/
@ Test if the card is present.
@ This test is tricky because if it's NOT this card,
@ we are not allowed to destroy contents of GBA ROM space.
@ R0: Return != 0 if present.
.ALIGN
.GLOBAL m3cf_detect_card
m3cf_detect_card:
gba_prefix
@ read old value @ REG_M3CF_LBA1 (in RAM)
ldr r3, =REG_M3CF_LBA1
ldrh r1, [r3]
@ now switch to IO mode
m3_set_io
@ test if the lower 8 bit of LBA1 are read- and writable
ldr r3, =REG_M3CF_LBA1
ldrb ip, [r3]
eor ip, ip, #0xFF @ invert lower 8 bit of LBA1
strh ip, [r3] @ store complement in LBA1
ldrb r0, [r3]
teq ip, r0 @ are they the same?
movne r0, #0 @ failure code
bne m3cf_detect_exit @ no: no CF
@ make sure the register is 8 bit, not 16
ldr ip, =0xAA55
strh ip, [r3]
ldrh r0, [r3]
teq ip, r0 @ are they the same?
moveq r0, #0 @ yes: can't be a CF card
movne r0, #1 @ positive detection
m3cf_detect_exit:
@ switch back to RAM
m3_set_ram
@ restore RAM contents @ REG_M3CF_LBA1 from R1
ldr r3, =REG_M3CF_LBA1
strh r1, [r3]
gba_suffix
mov pc, lr
/*****************************************************************************/
@ Wait until CF card is ready for a new command.
@ Set sector count to 1.
@ Write sector address to card.
@ Write a command to card.
@ Wait until card has executed the command.
@ Transfer a 256 word block of word data to/from card register.
@ Wait until card has finished the command.
@ R0: buffer address with sector number and command.
@ Return: R0 = 1 if timeout waiting for finish
@ 2 if timeout waiting for ready
@ 3 if timeout after command
@ 0 if all OK
.ALIGN
.GLOBAL m3cf_transfer
m3cf_transfer:
gba_prefix
@ now switch to IO mode
m3_set_io
@ Wait until card is ready for commands
mov r3, #0x1000
ldr ip, =REG_M3CF_STS
m3cf_transfer_ready:
subs r3, r3, #1
moveq r0, #2 @ exit R0=2
beq m3cf_transfer_exit
ldrb r1, [ip]
and r1, #CF_STS_INSERTED
teq r1, #CF_STS_INSERTED
bne m3cf_transfer_ready
@ set one sector to read
ldr ip, =REG_M3CF_SEC
mov r3, #1
strh r3, [ip]
@ set sector address
ldrh r3, [r0], #2
ldr ip, =REG_M3CF_LBA1
strh r3, [ip]
ldrh r3, [r0], #2
ldr ip, =REG_M3CF_LBA2
strh r3, [ip]
ldrh r3, [r0], #2
ldr ip, =REG_M3CF_LBA3
strh r3, [ip]
ldrh r3, [r0], #2
ldr ip, =REG_M3CF_LBA4
strh r3, [ip]
@ send command
ldrh r3, [r0]
ldr ip, =REG_M3CF_CMD
strh r3, [ip]
@ Wait until card is ready for transfer
mov r3, #0x100000 @ long timeout, can't stop here
ldr ip, =REG_M3CF_STS
m3cf_transfer_wait:
subs r3, r3, #1
moveq r0, #3 @ exit R0=3
beq m3cf_transfer_exit
ldrb r1, [ip]
teq r1, #CF_STS_READY
bne m3cf_transfer_wait
@ transfer the data
ldr ip, =REG_M3CF_DATA
mov r3, #0x100
@ which command?
ldrh r1, [r0], #2
teq r1, #0x30 @ test for WRITE command
beq m3cf_write_loop
@ read the data
m3cf_read_loop:
ldrh r1, [ip]
strh r1, [r0], #2
subs r3, r3, #1
bne m3cf_read_loop
m3cf_transfer_ok:
@ Wait until CF card is finished
mov r3, #0x100000 @ long timeout, can't stop here
ldr ip, =REG_M3CF_CMD
m3cf_transfer_busy:
subs r3, r3, #1
moveq r0, #1 @ exit R0=1
beq m3cf_transfer_exit
ldrb r1, [ip]
tst r1, #CF_STS_BUSY
bne m3cf_transfer_busy
@ all OK
mov r0, #0 @ exit R0=0
m3cf_transfer_exit:
@ switch back to RAM
m3_set_ram
gba_suffix
mov pc, lr
@ write the data
m3cf_write_loop:
ldrh r1, [r0], #2
strh r1, [ip]
subs r3, r3, #1
bne m3cf_write_loop
b m3cf_transfer_ok
/*****************************************************************************/
.END
/*****************************************************************************/
Index: Kconfig
===================================================================
RCS file: /cvsroot/dslinux/dslinux/linux-2.6.x/arch/arm/mach-nds/Kconfig,v
retrieving revision 1.5
retrieving revision 1.6
diff -u -d -r1.5 -r1.6
--- Kconfig 3 Jul 2006 20:23:29 -0000 1.5
+++ Kconfig 6 Oct 2006 21:50:07 -0000 1.6
@@ -49,5 +49,22 @@
Saying Y here will enable the code to use the GBA ROM memory
area as general purpose RAM. You need a GBA cardridge with RAM
in the GBA ROM area, and a special gnu compiler for this.
- If unsure, say N.
+ If unsure, say N.
+
+config NDS_BLK_M3CF
+ bool "Block device support for the M3 Compact Flash Adaptor"
+ depends on ARCH_NDS
+ default n
+ help
+ Saying Y here will include a block driver for the M3 CF card.
+ If unsure, say N.
+
+config NDS_BLK_SCCF
+ bool "Block device support for the Supercard Compact Flash Adaptor"
+ depends on ARCH_NDS
+ default n
+ help
+ Saying Y here will include a block driver for the Supercard CF.
+ If unsure, say N.
+
endmenu
--- NEW FILE: m3cf_c.c ---
/*
* M3 Compact Flash Adapter for Nintendo DS driver.
*
* Copyright (c) 2006 Amadeus
*/
/*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 as published by
* the Free Software Foundation.
*/
#include <linux/config.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/kernel.h> /* printk() */
#include <linux/fs.h> /* everything... */
#include <linux/errno.h> /* error codes */
#include <linux/types.h> /* size_t */
#include <linux/fcntl.h> /* O_ACCMODE */
#include <linux/hdreg.h> /* HDIO_GETGEO */
#include <linux/genhd.h>
#include <linux/blkdev.h>
#include <linux/buffer_head.h> /* invalidate_bdev */
#include <linux/bio.h>
MODULE_LICENSE("GPL");
#define M3CF_MAJOR 3 /* use normal IDE major */
/*
* Minor number and partition management.
*/
#define M3CF_MINORS 16
/*
* We do only 512 byte sectors.
*/
#define KERNEL_SECTOR_SIZE 512
/*******************************************************************************/
/* Assembler functions from m3cf_s.S */
extern int m3cf_detect_card(void);
extern int m3cf_transfer(u16 *data);
/*******************************************************************************/
/*
* The internal representation of our device.
*/
static struct {
unsigned long num_sectors; /* number of sectors of the card */
struct request_queue *queue; /* The device request queue */
struct gendisk *gd; /* The gendisk structure */
} m3cf_device;
/* data buffer in main memory */
static u16 cf_data_buf[(KERNEL_SECTOR_SIZE/2)+5];
/* Transfer a block of data with retry */
static int m3cf_transfer_retry(void)
{
int retry;
for (retry = 1000; retry; retry--) {
switch (m3cf_transfer(cf_data_buf)) {
case 0: /* all OK */
return 0;
case 1: /* timeout waiting for finish */
printk(KERN_ERR "m3cf: Timeout after data\n");
return 1;
case 2: /* retry waiting for ready */
break;
case 3: /* timeout after command */
printk(KERN_ERR "m3cf: Timeout after command\n");
return 1;
}
}
printk(KERN_ERR "m3cf: Timeout error\n");
return 1;
}
/* Read the CF descriptor data */
static unsigned long m3cf_identify(void)
{
cf_data_buf[0] = 0;
cf_data_buf[1] = 0;
cf_data_buf[2] = 0;
cf_data_buf[3] = 0xE0;
cf_data_buf[4] = 0xEC; /* Identify Device */
if (m3cf_transfer_retry()) {
// timeout
printk( KERN_ERR "Timeout in Identify Device\n");
return 0;
}
return (cf_data_buf[5+7] << 16) | cf_data_buf[5+8];
}
/*
* Handle an I/O request.
*/
static void m3cf_handle(unsigned long sector,
unsigned long nsect,
char *buffer, int write)
{
if (write) {
for ( ; nsect; nsect--) {
cf_data_buf[0] = sector & 0xFF;
cf_data_buf[1] = (sector >> 8) & 0xFF;
cf_data_buf[2] = (sector >> 16) & 0xFF;
cf_data_buf[3] = ((sector >> 24) & 0x0F) | 0xE0;
cf_data_buf[4] = 0x30; /* WRITE */
memcpy(&cf_data_buf[5], buffer, KERNEL_SECTOR_SIZE);
if (m3cf_transfer_retry()) {
// timeout
printk( KERN_ERR "Timeout in write transfer\n");
return;
}
buffer += KERNEL_SECTOR_SIZE;
sector++;
}
} else {
for ( ; nsect; nsect--) {
cf_data_buf[0] = sector & 0xFF;
cf_data_buf[1] = (sector >> 8) & 0xFF;
cf_data_buf[2] = (sector >> 16) & 0xFF;
cf_data_buf[3] = ((sector >> 24) & 0x0F) | 0xE0;
cf_data_buf[4] = 0x20; /* READ */
if (m3cf_transfer_retry()) {
// timeout
printk( KERN_ERR "Timeout in read transfer\n");
return;
}
memcpy(buffer, &cf_data_buf[5], KERNEL_SECTOR_SIZE);
buffer += KERNEL_SECTOR_SIZE;
sector++;
}
}
}
/*
* Transfer a single BIO.
*/
static int m3cf_xfer_bio(struct bio *bio)
{
int i;
struct bio_vec *bvec;
sector_t sector = bio->bi_sector;
/* Do each segment independently. */
bio_for_each_segment(bvec, bio, i) {
char *buffer = __bio_kmap_atomic(bio, i, KM_USER0);
m3cf_handle(sector, bio_cur_sectors(bio),
buffer, bio_data_dir(bio) == WRITE);
sector += bio_cur_sectors(bio);
__bio_kunmap_atomic(bio, KM_USER0);
}
return 0; /* Always "succeed" */
}
/*
* The direct make request version.
*/
static int m3cf_make_request(request_queue_t *q, struct bio *bio)
{
int status;
status = m3cf_xfer_bio(bio);
bio_endio(bio, bio->bi_size, status);
return 0;
}
/*
* Open and close.
*/
static int m3cf_open(struct inode *inode, struct file *filp)
{
return 0;
}
static int m3cf_release(struct inode *inode, struct file *filp)
{
return 0;
}
/*
* The ioctl() implementation
*/
int m3cf_ioctl (struct inode *inode, struct file *filp,
unsigned int cmd, unsigned long arg)
{
struct hd_geometry geo;
switch(cmd) {
case HDIO_GETGEO:
/*
* Get geometry: since we are a LBA, we have to make
* up something plausible. So we claim 32 sectors, 8 heads,
* and calculate the corresponding number of cylinders.
*/
geo.cylinders = m3cf_device.num_sectors >> 8;
geo.heads = 8;
geo.sectors = 32;
geo.start = 4;
if (copy_to_user((void __user *) arg, &geo, sizeof(geo)))
return -EFAULT;
return 0;
}
return -ENOTTY; /* unknown command */
}
/*
* The device operations structure.
*/
static struct block_device_operations m3cf_ops = {
.owner = THIS_MODULE,
.open = m3cf_open,
.release = m3cf_release,
.ioctl = m3cf_ioctl
};
/* Initialize the driver */
static int __init m3cf_init(void)
{
int i;
/* Is a M3CF detected? */
if (!m3cf_detect_card()) {
/* no: what else can we do? */
return -ENODEV;
}
printk(KERN_INFO "M3CF detected\n");
/* Get registered. */
i = register_blkdev(M3CF_MAJOR, "m3cf");
if (i) {
printk(KERN_ERR "m3cf: unable to get major number\n");
return -EBUSY;
}
/* Init device structure */
memset(&m3cf_device, 0, sizeof (m3cf_device));
/* Init the block queue */
m3cf_device.queue = blk_alloc_queue(GFP_KERNEL);
if (m3cf_device.queue == NULL) {
printk(KERN_ERR "m3cf: unable to allocate block queue\n");
unregister_blkdev(M3CF_MAJOR, "m3cf");
return -ENOMEM;
}
blk_queue_make_request(m3cf_device.queue, m3cf_make_request);
m3cf_device.queue->queuedata = &m3cf_device;
/* Init the gendisk structure. */
m3cf_device.gd = alloc_disk(M3CF_MINORS);
if (!m3cf_device.gd) {
printk (KERN_ERR "m3cf: alloc_disk failure\n");
blk_put_queue(m3cf_device.queue);
unregister_blkdev(M3CF_MAJOR, "m3cf");
return -ENOMEM;
}
m3cf_device.gd->major = M3CF_MAJOR;
m3cf_device.gd->first_minor = 0;
m3cf_device.gd->fops = &m3cf_ops;
m3cf_device.gd->queue = m3cf_device.queue;
snprintf (m3cf_device.gd->disk_name, 32, "hda");
/* Calculate the size of the CF card */
m3cf_device.num_sectors = m3cf_identify();
set_capacity(m3cf_device.gd, m3cf_device.num_sectors);
add_disk(m3cf_device.gd);
return 0;
}
/* unregister the driver */
static void __exit m3cf_exit(void)
{
if (m3cf_device.gd) {
del_gendisk(m3cf_device.gd);
put_disk(m3cf_device.gd);
}
if (m3cf_device.queue) {
blk_put_queue(m3cf_device.queue);
}
unregister_blkdev(M3CF_MAJOR, "m3cf");
}
module_init(m3cf_init);
module_exit(m3cf_exit);
--- NEW FILE: sccf_s.S ---
/*
* linux/arch/arm/mach-nds/sccf_s.S - Supercard CF driver
*
* Copyright (C) 2006 Amadeus, 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 version 2 as
* published by the Free Software Foundation.
*
* This driver must be in main memory.
*/
/* common macros for all NDS GBA ROM device drivers */
#include <asm/arch/gbarom-macro.S>
.TEXT
/*****************************************************************************/
#define REG_SCCF_STS 0x098C0000 @ Status of the CF Card / Device control
#define REG_SCCF_CMD 0x090E0000 @ Commands sent to control chip and status return
#define REG_SCCF_ERR 0x09020000 @ Errors / Features
#define REG_SCCF_SEC 0x09040000 @ Number of sector to transfer
#define REG_SCCF_LBA1 0x09060000 @ 1st byte of sector address
#define REG_SCCF_LBA2 0x09080000 @ 2nd byte of sector address
#define REG_SCCF_LBA3 0x090A0000 @ 3rd byte of sector address
#define REG_SCCF_LBA4 0x090C0000 @ last nibble of sector address | 0xE0
#define REG_SCCF_DATA 0x09000000 @ Pointer to buffer of CF data transered from card
#define SC_SD_CMD 0x09800000
#define CF_STS_INSERTED 0x50
#define CF_STS_READY 0x58
#define CF_STS_BUSY 0x80
/*****************************************************************************/
@ Test if the card is present.
@ This test is tricky because if it's NOT this card,
@ we are not allowed to destroy contents of GBA ROM space.
@ R0: Return != 0 if present.
.ALIGN
.GLOBAL sccf_detect_card
sccf_detect_card:
stmfd sp!,{r4-r5} @ use additional registers
gba_prefix
@ there is a special problem: if we have a supercard lite,
@ accessing REG_SCCF_LBA1 is bad for the scsd driver.
@ So we have to check for a supercard SD first.
@ read old value @ SC_SD_CMD (in RAM)
ldr r3, =SC_SD_CMD
ldrh r4, [r3]
@ store opposite of requested value in SC_SD_CMD (in RAM)
mov r0, #-2
strh r0, [r3]
@ read old value @ REG_SCCF_LBA1 (in RAM)
ldr r3, =REG_SCCF_LBA1
ldrh r5, [r3]
@ now switch to IO mode (save old value @SC_LOCK in R2)
sc_set_io
@ now read SC_SD_CMD (in IO)
ldr r3, =SC_SD_CMD
ldrh r0, [r3]
tst r0, #0x300 @ both bits must be 0
bne sccf_detect_testcf @ if not: test for CF
tst r0, #0x001 @ bit must be 1
beq sccf_detect_testcf @ if not: test for CF
@ it's a supercard SD. So signal failure.
mov r0, #0
b sccf_detect_exit
@ it's no supercard SD. So we can test for CF.
sccf_detect_testcf:
@ test if the lower 8 bit of LBA1 are read- and writable
ldr r3, =REG_SCCF_LBA1
ldrb ip, [r3]
eor ip, ip, #0xFF @ invert lower 8 bit of LBA1
strh ip, [r3] @ store complement in LBA1
ldrb r0, [r3]
teq ip, r0 @ are they the same?
movne r0, #0 @ failure code
bne sccf_detect_exit @ no: no CF
@ make sure the register is 8 bit, not 16
ldr ip, =0xAA55
strh ip, [r3]
ldrh r0, [r3]
teq ip, r0 @ are they the same?
moveq r0, #0 @ yes: can't be a CF card
movne r0, #1 @ positive detection
sccf_detect_exit:
@ switch back to RAM (restore old value @SC_LOCK from R2)
sc_set_ram
@ restore RAM contents @ REG_SCCF_LBA1 from R5
ldr r3, =REG_SCCF_LBA1
strh r5, [r3]
@ restore RAM contents @ SC_SD_CMD from R4
ldr r3, =SC_SD_CMD
strh r4, [r3]
gba_suffix
ldmfd sp!,{r4-r5} @ restore used registers
mov pc, lr
/*****************************************************************************/
@ Wait until CF card is ready for a new command.
@ Set sector count to 1.
@ Write sector address to card.
@ Write a command to card.
@ Wait until card has executed the command.
@ Transfer a 256 word block of word data to/from card register.
@ Wait until card has finished the command.
@ R0: buffer address with sector number and command.
@ Return: R0 = 1 if timeout waiting for finish
@ 2 if timeout waiting for ready
@ 3 if timeout after command
@ 0 if all OK
.ALIGN
.GLOBAL sccf_transfer
sccf_transfer:
gba_prefix
@ now switch to IO mode (save old value @SC_LOCK in R2)
sc_set_io
@ Wait until card is ready for commands
mov r3, #0x1000
ldr ip, =REG_SCCF_STS
sccf_transfer_ready:
subs r3, r3, #1
moveq r0, #2 @ exit R0=2
beq sccf_transfer_exit
ldrb r1, [ip]
and r1, #CF_STS_INSERTED
teq r1, #CF_STS_INSERTED
bne sccf_transfer_ready
@ set one sector to read
ldr ip, =REG_SCCF_SEC
mov r3, #1
strh r3, [ip]
@ set sector address
ldrh r3, [r0], #2
ldr ip, =REG_SCCF_LBA1
strh r3, [ip]
ldrh r3, [r0], #2
ldr ip, =REG_SCCF_LBA2
strh r3, [ip]
ldrh r3, [r0], #2
ldr ip, =REG_SCCF_LBA3
strh r3, [ip]
ldrh r3, [r0], #2
ldr ip, =REG_SCCF_LBA4
strh r3, [ip]
@ send command
ldrh r3, [r0]
ldr ip, =REG_SCCF_CMD
strh r3, [ip]
@ Wait until card is ready for transfer
mov r3, #0x100000 @ long timeout, can't stop here
ldr ip, =REG_SCCF_STS
sccf_transfer_wait:
subs r3, r3, #1
moveq r0, #3 @ exit R0=3
beq sccf_transfer_exit
ldrb r1, [ip]
teq r1, #CF_STS_READY
bne sccf_transfer_wait
@ transfer the data
ldr ip, =REG_SCCF_DATA
mov r3, #0x100
@ which command?
ldrh r1, [r0], #2
teq r1, #0x30 @ test for WRITE command
beq sccf_write_loop
@ read the data
sccf_read_loop:
ldrh r1, [ip]
strh r1, [r0], #2
subs r3, r3, #1
bne sccf_read_loop
sccf_transfer_ok:
@ Wait until CF card is finished
mov r3, #0x100000 @ long timeout, can't stop here
ldr ip, =REG_SCCF_CMD
sccf_transfer_busy:
subs r3, r3, #1
moveq r0, #1 @ exit R0=1
beq sccf_transfer_exit
ldrb r1, [ip]
tst r1, #CF_STS_BUSY
bne sccf_transfer_busy
@ all OK
mov r0, #0 @ exit R0=0
sccf_transfer_exit:
@ switch back to RAM (restore old value @SC_LOCK from R2)
sc_set_ram
gba_suffix
mov pc, lr
@ write the data
sccf_write_loop:
ldrh r1, [r0], #2
strh r1, [ip]
subs r3, r3, #1
bne sccf_write_loop
b sccf_transfer_ok
/*****************************************************************************/
.END
/*****************************************************************************/
Index: Makefile
===================================================================
RCS file: /cvsroot/dslinux/dslinux/linux-2.6.x/arch/arm/mach-nds/Makefile,v
retrieving revision 1.3
retrieving revision 1.4
diff -u -d -r1.3 -r1.4
--- Makefile 24 Apr 2006 22:05:05 -0000 1.3
+++ Makefile 6 Oct 2006 21:50:07 -0000 1.4
@@ -8,6 +8,10 @@
obj-$(CONFIG_NDS_TEXT_CONSOLE) += console.o
obj-$(CONFIG_NDS_SOUNDTEST) += soundtest.o
+obj-$(CONFIG_NDS_BLK_M3CF) += m3cf.o
+obj-$(CONFIG_NDS_BLK_SCCF) += sccf.o
+m3cf-y := m3cf_c.o m3cf_s.o
+sccf-y := sccf_c.o sccf_s.o
extra-y += arm7/head.o
extra-y += arm7/main.o
--- NEW FILE: sccf_c.c ---
/*
* Supercard Compact Flash Adapter for Nintendo DS driver.
*
* Copyright (c) 2006 Amadeus
*/
/*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 as published by
* the Free Software Foundation.
*/
#include <linux/config.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/kernel.h> /* printk() */
#include <linux/fs.h> /* everything... */
#include <linux/errno.h> /* error codes */
#include <linux/types.h> /* size_t */
#include <linux/fcntl.h> /* O_ACCMODE */
#include <linux/hdreg.h> /* HDIO_GETGEO */
#include <linux/genhd.h>
#include <linux/blkdev.h>
#include <linux/buffer_head.h> /* invalidate_bdev */
#include <linux/bio.h>
MODULE_LICENSE("GPL");
#define SCCF_MAJOR 3 /* use normal IDE major */
/*
* Minor number and partition management.
*/
#define SCCF_MINORS 16
/*
* We do only 512 byte sectors.
*/
#define KERNEL_SECTOR_SIZE 512
/*******************************************************************************/
/* Assembler functions from sccf_s.S */
extern int sccf_detect_card(void);
extern int sccf_transfer(u16 *data);
/*******************************************************************************/
/*
* The internal representation of our device.
*/
static struct {
unsigned long num_sectors; /* number of sectors of the card */
struct request_queue *queue; /* The device request queue */
struct gendisk *gd; /* The gendisk structure */
} sccf_device;
/* data buffer in main memory */
static u16 cf_data_buf[(KERNEL_SECTOR_SIZE/2)+5];
/* Transfer a block of data with retry */
static int sccf_transfer_retry(void)
{
int retry;
for (retry = 1000; retry; retry--) {
switch (sccf_transfer(cf_data_buf)) {
case 0: /* all OK */
return 0;
case 1: /* timeout waiting for finish */
printk(KERN_ERR "sccf: Timeout after data\n");
return 1;
case 2: /* retry waiting for ready */
break;
case 3: /* timeout after command */
printk(KERN_ERR "sccf: Timeout after command\n");
return 1;
}
}
printk(KERN_ERR "sccf: Timeout error\n");
return 1;
}
/* Read the CF descriptor data */
static unsigned long sccf_identify(void)
{
cf_data_buf[0] = 0;
cf_data_buf[1] = 0;
cf_data_buf[2] = 0;
cf_data_buf[3] = 0xE0;
cf_data_buf[4] = 0xEC; /* Identify Device */
if (sccf_transfer_retry()) {
// timeout
printk( KERN_ERR "Timeout in Identify Device\n");
return 0;
}
return (cf_data_buf[5+7] << 16) | cf_data_buf[5+8];
}
/*
* Handle an I/O request.
*/
static void sccf_handle(unsigned long sector,
unsigned long nsect,
char *buffer, int write)
{
if (write) {
for ( ; nsect; nsect--) {
cf_data_buf[0] = sector & 0xFF;
cf_data_buf[1] = (sector >> 8) & 0xFF;
cf_data_buf[2] = (sector >> 16) & 0xFF;
cf_data_buf[3] = ((sector >> 24) & 0x0F) | 0xE0;
cf_data_buf[4] = 0x30; /* WRITE */
memcpy(&cf_data_buf[5], buffer, KERNEL_SECTOR_SIZE);
if (sccf_transfer_retry()) {
// timeout
printk( KERN_ERR "Timeout in write transfer\n");
return;
}
buffer += KERNEL_SECTOR_SIZE;
sector++;
}
} else {
for ( ; nsect; nsect--) {
cf_data_buf[0] = sector & 0xFF;
cf_data_buf[1] = (sector >> 8) & 0xFF;
cf_data_buf[2] = (sector >> 16) & 0xFF;
cf_data_buf[3] = ((sector >> 24) & 0x0F) | 0xE0;
cf_data_buf[4] = 0x20; /* READ */
if (sccf_transfer_retry()) {
// timeout
printk( KERN_ERR "Timeout in read transfer\n");
return;
}
memcpy(buffer, &cf_data_buf[5], KERNEL_SECTOR_SIZE);
buffer += KERNEL_SECTOR_SIZE;
sector++;
}
}
}
/*
* Transfer a single BIO.
*/
static int sccf_xfer_bio(struct bio *bio)
{
int i;
struct bio_vec *bvec;
sector_t sector = bio->bi_sector;
/* Do each segment independently. */
bio_for_each_segment(bvec, bio, i) {
char *buffer = __bio_kmap_atomic(bio, i, KM_USER0);
sccf_handle(sector, bio_cur_sectors(bio),
buffer, bio_data_dir(bio) == WRITE);
sector += bio_cur_sectors(bio);
__bio_kunmap_atomic(bio, KM_USER0);
}
return 0; /* Always "succeed" */
}
/*
* The direct make request version.
*/
static int sccf_make_request(request_queue_t *q, struct bio *bio)
{
int status;
status = sccf_xfer_bio(bio);
bio_endio(bio, bio->bi_size, status);
return 0;
}
/*
* Open and close.
*/
static int sccf_open(struct inode *inode, struct file *filp)
{
return 0;
}
static int sccf_release(struct inode *inode, struct file *filp)
{
return 0;
}
/*
* The ioctl() implementation
*/
int sccf_ioctl (struct inode *inode, struct file *filp,
unsigned int cmd, unsigned long arg)
{
struct hd_geometry geo;
switch(cmd) {
case HDIO_GETGEO:
/*
* Get geometry: since we are a LBA, we have to make
* up something plausible. So we claim 32 sectors, 8 heads,
* and calculate the corresponding number of cylinders.
*/
geo.cylinders = sccf_device.num_sectors >> 8;
geo.heads = 8;
geo.sectors = 32;
geo.start = 4;
if (copy_to_user((void __user *) arg, &geo, sizeof(geo)))
return -EFAULT;
return 0;
}
return -ENOTTY; /* unknown command */
}
/*
* The device operations structure.
*/
static struct block_device_operations sccf_ops = {
.owner = THIS_MODULE,
.open = sccf_open,
.release = sccf_release,
.ioctl = sccf_ioctl
};
/* Initialize the driver */
static int __init sccf_init(void)
{
int i;
/* Is a sccf detected? */
if (!sccf_detect_card()) {
/* no: what else can we do? */
return -ENODEV;
}
printk(KERN_INFO "sccf detected\n");
/* Get registered. */
i = register_blkdev(SCCF_MAJOR, "sccf");
if (i) {
printk(KERN_ERR "sccf: unable to get major number\n");
return -EBUSY;
}
/* Init device structure */
memset(&sccf_device, 0, sizeof (sccf_device));
/* Init the block queue */
sccf_device.queue = blk_alloc_queue(GFP_KERNEL);
if (sccf_device.queue == NULL) {
printk(KERN_ERR "sccf: unable to allocate block queue\n");
unregister_blkdev(SCCF_MAJOR, "sccf");
return -ENOMEM;
}
blk_queue_make_request(sccf_device.queue, sccf_make_request);
sccf_device.queue->queuedata = &sccf_device;
/* Init the gendisk structure. */
sccf_device.gd = alloc_disk(SCCF_MINORS);
if (!sccf_device.gd) {
printk (KERN_ERR "sccf: alloc_disk failure\n");
blk_put_queue(sccf_device.queue);
unregister_blkdev(SCCF_MAJOR, "sccf");
return -ENOMEM;
}
sccf_device.gd->major = SCCF_MAJOR;
sccf_device.gd->first_minor = 0;
sccf_device.gd->fops = &sccf_ops;
sccf_device.gd->queue = sccf_device.queue;
snprintf (sccf_device.gd->disk_name, 32, "hda");
/* Calculate the size of the CF card */
sccf_device.num_sectors = sccf_identify();
set_capacity(sccf_device.gd, sccf_device.num_sectors);
add_disk(sccf_device.gd);
return 0;
}
/* unregister the driver */
static void __exit sccf_exit(void)
{
if (sccf_device.gd) {
del_gendisk(sccf_device.gd);
put_disk(sccf_device.gd);
}
if (sccf_device.queue) {
blk_put_queue(sccf_device.queue);
}
unregister_blkdev(SCCF_MAJOR, "sccf");
}
module_init(sccf_init);
module_exit(sccf_exit);
More information about the dslinux-commit
mailing list