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