summaryrefslogtreecommitdiff
path: root/packages/linux/opensimpad/mmc-spi.patch
diff options
context:
space:
mode:
authorFrederic Bompart <frederic@unknown.openembedded.org>2005-11-11 14:51:32 +0000
committerOpenEmbedded Project <openembedded-devel@lists.openembedded.org>2005-11-11 14:51:32 +0000
commit5fb88e711e1f6614c358d6a331e81881ba11d1bc (patch)
treea5be76558dfac886e2ddfc1b505e3c4c77b2f172 /packages/linux/opensimpad/mmc-spi.patch
parentddfdc42ce0e9c07226c1a6390b03e5df4e731902 (diff)
opensimpad: MMC support + better support for SinusPad and 128M SIMpad
- There's now a config options to build the kernel for 128Mb SIMpad (as there was one for the SinusPad) - The .bb files automatically turn on these options depending on the total memory available. - The MMC driver was made by Clement Ballabriga <clement@asso.ups-tlse.fr> and Guylhem Aznar <simpad@externe.net>. Adding an MMC card (internal or external) to the SIMpad is described on these pages: http://externe.net/zaurus/simpad-bluetooth/ http://www.iral.com/~albertr/linux/simpad/mmc/
Diffstat (limited to 'packages/linux/opensimpad/mmc-spi.patch')
-rw-r--r--packages/linux/opensimpad/mmc-spi.patch885
1 files changed, 885 insertions, 0 deletions
diff --git a/packages/linux/opensimpad/mmc-spi.patch b/packages/linux/opensimpad/mmc-spi.patch
new file mode 100644
index 0000000000..1841b45fbd
--- /dev/null
+++ b/packages/linux/opensimpad/mmc-spi.patch
@@ -0,0 +1,885 @@
+
+#
+# Patch managed by http://www.holgerschurig.de/patcher.html
+#
+
+--- /dev/null
++++ linux-2.4.27/drivers/block/mmc.c
+@@ -0,0 +1,857 @@
++/*
++ * Copyright (c) Clément Ballabriga, 2005 - GPL
++ * Copyright (c) Guylhem Aznar, 2005 - GPL
++ *
++ * Please check http://externe.net/zaurus/simpad-bluetooth reference design first.
++ *
++ * Based on Madsuk/Rohde work on a MMC driver for the WRT54G.
++ *
++ * This is an ugly hack of a driver. I am surprised if it ever works!
++ * So please use a real driver or contribute one to the 2.4/2.6 mmc framework
++ */
++
++#include <linux/stddef.h>
++#include <linux/delay.h>
++#include <linux/timer.h>
++#include <linux/module.h>
++#include <linux/mm.h>
++#include <linux/init.h>
++#include <linux/fs.h>
++#include <linux/blkpg.h>
++#include <linux/hdreg.h>
++#include <linux/major.h>
++
++#include <asm/hardware.h>
++#include <asm/uaccess.h>
++#include <asm/io.h>
++
++/*
++ * *******************************************************************
++ *
++ * This is the only configurable part.
++ *
++ * *******************************************************************
++ *
++ */
++
++// #define DEBUG 1
++
++#define DEVICE_NAME "mmc"
++#define DEVICE_NR(device) (MINOR(device))
++#define DEVICE_ON(device)
++#define DEVICE_OFF(device)
++#define MAJOR_NR 121
++
++/* Let that include where it is or compilation fails on INIT_REQUEST/CURRENT */
++
++#include <linux/blk.h>
++
++MODULE_AUTHOR("Guylhem Aznar <mmc-driver @externe.net>");
++MODULE_DESCRIPTION("Driver for MMC/SD-Cards in SPI mode by GPIO");
++MODULE_SUPPORTED_DEVICE("Simpad");
++MODULE_LICENSE("GPL");
++
++/* Registers should be architecture independant - but it's not ! */
++
++#define MAP_START 0x90040000
++#define MAP_SIZE 0x00001000
++
++#define MY_GPLR 0
++#define MY_GPDR 1
++#define MY_GPSR 2
++#define MY_GPCR 3
++#define MY_GRER 4
++#define MY_GFER 5
++#define MY_GEDR 6
++#define MY_GAFR 7
++
++/*
++ * If you are using different GPIOs in your hardware hack, you must
++ * first make sure they are unused for other functions and then
++ * configure them here.
++ *
++ * On the simpad I use spare pins from the UART1 (internal serial port):
++ * - DCD (in) : GPIO 23 : DO
++ * - DTR (out) : GPIO 07 : CS
++ * - RI (in) : GPIO 19 : CLK
++ * - DSR (in) : GPIO 06 : DI
++ *
++ * Don't worry about in/out original function - the GPIOs will be
++ * reprogrammed.
++ */
++
++#define GPIO_SD_DO 23
++#define GPIO_SD_CS 7
++#define GPIO_SD_CLK 19
++#define GPIO_SD_DI 6
++
++/*
++ * *******************************************************************
++ *
++ * Do not change anything below !
++ *
++ * *******************************************************************
++ *
++ */
++
++
++/* GPIO states */
++#define LOW 0
++#define HIGH 1
++
++#define INPUT 0
++#define OUTPUT 1
++
++#define PRESENT 1
++#define ABSENT 0
++
++typedef unsigned int uint32;
++typedef unsigned long u32_t;
++typedef unsigned short u16_t;
++typedef unsigned char u8_t;
++
++/* we have only one device */
++static int hd_sizes[1 << 6];
++static int hd_blocksizes[1 << 6];
++static int hd_hardsectsizes[1 << 6];
++static int hd_maxsect[1 << 6];
++static struct hd_struct hd[1 << 6];
++
++static struct timer_list mmc_timer;
++
++/* start with no card */
++static int mmc_media_detect = 0;
++static int mmc_media_changed = 1;
++
++extern struct gendisk hd_gendisk;
++
++/* Use only one global device */
++typedef struct gpio_s gpio_t;
++struct gpio_s {
++ volatile u32_t *base;
++};
++
++static gpio_t gp = {
++ (void *) io_p2v(MAP_START)
++};
++
++/*
++ * *******************************************************************
++ *
++ * Begin GPIO hardware access functions.
++ *
++ * *******************************************************************
++ *
++ */
++
++gpio_t *gpio_open(void)
++{
++ static gpio_t tmp;
++ tmp.base = (void *) io_p2v(MAP_START);
++ return (&tmp);
++}
++
++void gpio_setdir(gpio_t * g, int num, int dir)
++{
++ if (dir == 1) {
++ g->base[MY_GPDR] |= (1 << num);
++ } else {
++ g->base[MY_GPDR] &= ~(1 << num);
++
++ }
++}
++
++void gpio_setalt(gpio_t * g, int num, int alt)
++{
++ if (alt == 1) {
++ g->base[MY_GAFR] |= (1 << num);
++ } else {
++ g->base[MY_GAFR] &= ~(1 << num);
++ }
++}
++
++int gpio_getdir(gpio_t * g, int num)
++{
++ return ((g->base[MY_GPDR] & (1 << num)) ? 1 : 0);
++}
++
++int gpio_getalt(gpio_t * g, int num)
++{
++ return ((g->base[MY_GAFR] & (1 << num)) ? 1 : 0);
++}
++
++static int gpio_read(gpio_t * g, int num)
++{
++ int what;
++
++ what=(g->base[MY_GPLR] & (1 << num)) ? 1 : 0;
++
++#ifdef DEBUG
++ if (num == GPIO_SD_DO) {
++ printk ("GPIO_SD_DO read: %u\n", what);
++ }
++#endif
++ return (what);
++}
++
++static int gpio_write(gpio_t * g, int num, int val)
++{
++#ifdef DEBUG
++ int check;
++#endif
++
++ if (val == 1) {
++ g->base[MY_GPSR] = 1 << num;
++ } else {
++ g->base[MY_GPCR] = 1 << num;
++ }
++#ifdef DEBUG
++ check=gpio_read(g,num);
++ if (check != val)
++ {
++ printk ("Error while write to %d: found %d after writing %d\n",num, check, val);
++ return (1);
++ }
++ else return(0);
++#endif
++
++}
++
++/*
++ * *******************************************************************
++ *
++ * Begin SPI hardware access functions.
++ *
++ * *******************************************************************
++ *
++ */
++static int mmc_spi_media_detect(void)
++{
++// FIXME: add card detection/test by SPI
++
++ return 1;
++}
++
++static int mmc_spi_hardware_init(void)
++{
++ /*unsigned char gpio_outen;*/
++
++ printk("mmc: GPIO init\n");
++
++ /* Now global
++ * gp = gpio_open(); */
++
++ /* Cut existing functions */
++ gpio_setalt(&gp, GPIO_SD_CLK, 0);
++ gpio_setalt(&gp, GPIO_SD_DI, 0);
++ gpio_setalt(&gp, GPIO_SD_DO, 0);
++ gpio_setalt(&gp, GPIO_SD_CS, 0);
++
++ /* Remap directions */
++ gpio_setdir(&gp, GPIO_SD_CLK, OUTPUT);
++ gpio_setdir(&gp, GPIO_SD_DI, OUTPUT);
++ gpio_setdir(&gp, GPIO_SD_DO, INPUT);
++ gpio_setdir(&gp, GPIO_SD_CS, OUTPUT);
++
++ printk("mmc: initialising MMC\n");
++
++ /* Start */
++ gpio_write(&gp, GPIO_SD_CLK, LOW);
++ gpio_write(&gp, GPIO_SD_DI, LOW);
++ gpio_write(&gp, GPIO_SD_CS, LOW);
++ return 0;
++}
++
++/* return what has been read, write the parameter */
++
++static unsigned char mmc_spi_readwrite(unsigned char data_out)
++{
++ int i;
++ unsigned char result = 0/*, tmp_data = 0*/;
++
++ for (i = 0; i < 8; i++) {
++ if (data_out & (0x01 << (7 - i)))
++ gpio_write(&gp, GPIO_SD_DI, HIGH);
++ else
++ gpio_write(&gp, GPIO_SD_DI, LOW);
++
++ gpio_write(&gp, GPIO_SD_CLK, HIGH);
++
++ result <<= 1;
++
++ if (gpio_read(&gp, GPIO_SD_DO) == 1)
++ result |= 1;
++
++ gpio_write(&gp, GPIO_SD_CLK, LOW);
++ }
++
++ return (result);
++}
++
++static int mmc_spi_card_init(void)
++{
++ unsigned char result = 0;
++ short i, j;
++ unsigned long flags;
++
++ save_flags(flags);
++ cli();
++
++ printk("GPIO_SD_CS dir: %u alt: %u\n", gpio_getdir(&gp, GPIO_SD_CS), gpio_getalt(&gp, GPIO_SD_CS));
++ printk("GPIO_SD_DI dir: %u alt: %u\n", gpio_getdir(&gp, GPIO_SD_DI), gpio_getalt(&gp, GPIO_SD_DI));
++ printk("GPIO_SD_DO dir: %u alt: %u\n", gpio_getdir(&gp, GPIO_SD_DO), gpio_getalt(&gp, GPIO_SD_DO));
++ printk("GPIO_SD_CS dir: %u alt: %u\n", gpio_getdir(&gp, GPIO_SD_CLK), gpio_getalt(&gp, GPIO_SD_CLK));
++
++ printk("mmc: card init 1/2\n");
++ gpio_write(&gp, GPIO_SD_CS, HIGH);
++ for (i = 0; i < 20; i++)
++ mmc_spi_readwrite(0xff);
++
++ gpio_write(&gp, GPIO_SD_CS, LOW);
++
++ mmc_spi_readwrite(0x40);
++ for (i = 0; i < 4; i++)
++ mmc_spi_readwrite(0x00);
++ mmc_spi_readwrite(0x95);
++ for (i = 0; i < 8; i++) {
++ result = mmc_spi_readwrite(0xff);
++ if (result == 0x01)
++ break;
++ }
++ gpio_write(&gp, GPIO_SD_CS, HIGH);
++ mmc_spi_readwrite(0xff);
++ if (result != 0x01) {
++ printk("mmc: card init %d error\n", result);
++ restore_flags(flags);
++ return (1);
++ }
++
++ printk("mmc: card init 2/2\n");
++ for (j = 0; j < 10000; j++) {
++ gpio_write(&gp, GPIO_SD_CS, LOW);
++
++ mmc_spi_readwrite(0x41);
++ for (i = 0; i < 4; i++)
++ mmc_spi_readwrite(0x00);
++ mmc_spi_readwrite(0xff);
++ for (i = 0; i < 8; i++) {
++ result = mmc_spi_readwrite(0xff);
++ if (result == 0x00)
++ break;
++ }
++ gpio_write(&gp, GPIO_SD_CS, HIGH);
++ mmc_spi_readwrite(0xff);
++ if (result == 0x00) {
++ restore_flags(flags);
++ printk("mmc: card init 3/3\n");
++ return (0);
++ }
++ }
++ restore_flags(flags);
++
++ return (2);
++}
++
++
++static int mmc_spi_card_config(void)
++{
++ unsigned char result = 0;
++ short i;
++ unsigned char csd[32];
++ unsigned int c_size;
++ unsigned int c_size_mult;
++ unsigned int mult;
++ unsigned int read_bl_len;
++ unsigned int blocknr = 0;
++ unsigned int block_len = 0;
++ unsigned int size = 0;
++
++ gpio_write(&gp, GPIO_SD_CS, LOW);
++ for (i = 0; i < 4; i++)
++ mmc_spi_readwrite(0xff);
++ mmc_spi_readwrite(0x49);
++ for (i = 0; i < 4; i++)
++ mmc_spi_readwrite(0x00);
++ mmc_spi_readwrite(0xff);
++ for (i = 0; i < 8; i++) {
++ result = mmc_spi_readwrite(0xff);
++ if (result == 0x00)
++ break;
++ }
++ if (result != 0x00) {
++ gpio_write(&gp, GPIO_SD_CS, HIGH);
++ mmc_spi_readwrite(0xff);
++ return (1);
++ }
++ for (i = 0; i < 8; i++) {
++ result = mmc_spi_readwrite(0xff);
++ if (result == 0xfe)
++ break;
++ }
++ if (result != 0xfe) {
++ gpio_write(&gp, GPIO_SD_CS, HIGH);
++ mmc_spi_readwrite(0xff);
++ return (2);
++ }
++ for (i = 0; i < 16; i++) {
++ result = mmc_spi_readwrite(0xff);
++ csd[i] = result;
++ }
++ for (i = 0; i < 2; i++) {
++ result = mmc_spi_readwrite(0xff);
++ }
++ gpio_write(&gp, GPIO_SD_CS, HIGH);
++ mmc_spi_readwrite(0xff);
++ if (result == 0x00)
++ return (3);
++
++ c_size = csd[8] + csd[7] * 256 + (csd[6] & 0x03) * 256 * 256;
++ c_size >>= 6;
++ c_size_mult = csd[10] + (csd[9] & 0x03) * 256;
++ c_size_mult >>= 7;
++ read_bl_len = csd[5] & 0x0f;
++ mult = 1;
++ mult <<= c_size_mult + 2;
++ blocknr = (c_size + 1) * mult;
++ block_len = 1;
++ block_len <<= read_bl_len;
++ size = block_len * blocknr;
++ size >>= 10;
++
++ for (i = 0; i < (1 << 6); i++) {
++ hd_blocksizes[i] = 1024;
++ hd_hardsectsizes[i] = block_len;
++ hd_maxsect[i] = 256;
++ }
++ hd_sizes[0] = size;
++ hd[0].nr_sects = blocknr;
++
++
++ printk("Size = %d, hardsectsize = %d, sectors = %d\n",
++ size, block_len, blocknr);
++
++ return 0;
++}
++
++
++/*
++ * *******************************************************************
++ *
++ * End of SPI hardware access functions.
++ *
++ * *******************************************************************
++ */
++
++
++static int mmc_write_block(unsigned int dest_addr, unsigned char *data)
++{
++ unsigned int address;
++ unsigned char result = 0;
++ unsigned char ab0, ab1, ab2, ab3;
++ int i;
++
++ address = dest_addr;
++
++ ab3 = 0xff & (address >> 24);
++ ab2 = 0xff & (address >> 16);
++ ab1 = 0xff & (address >> 8);
++ ab0 = 0xff & address;
++ gpio_write(&gp, GPIO_SD_CS, LOW);
++ for (i = 0; i < 4; i++)
++ mmc_spi_readwrite(0xff);
++ mmc_spi_readwrite(0x58);
++ mmc_spi_readwrite(ab3); /* msb */
++ mmc_spi_readwrite(ab2);
++ mmc_spi_readwrite(ab1);
++ mmc_spi_readwrite(ab0); /* lsb */
++ mmc_spi_readwrite(0xff);
++ for (i = 0; i < 8; i++) {
++ result = mmc_spi_readwrite(0xff);
++ if (result == 0x00)
++ break;
++ }
++ if (result != 0x00) {
++ gpio_write(&gp, GPIO_SD_CS, HIGH);
++ mmc_spi_readwrite(0xff);
++ return (1);
++ }
++
++ mmc_spi_readwrite(0xfe);
++ for (i = 0; i < 512; i++)
++ mmc_spi_readwrite(data[i]);
++ for (i = 0; i < 2; i++)
++ mmc_spi_readwrite(0xff);
++
++ for (i = 0; i < 1000000; i++) {
++ result = mmc_spi_readwrite(0xff);
++ if (result == 0xff)
++ break;
++ }
++ if (result != 0xff) {
++ gpio_write(&gp, GPIO_SD_CS, HIGH);
++ mmc_spi_readwrite(0xff);
++ return (3);
++ }
++ gpio_write(&gp, GPIO_SD_CS, HIGH);
++ mmc_spi_readwrite(0xff);
++ return (0);
++}
++
++static int mmc_read_block(unsigned char *data, unsigned int src_addr)
++{
++ unsigned int address;
++ unsigned char result = 0;
++ unsigned char ab0, ab1, ab2, ab3;
++ int i;
++
++ address = src_addr;
++
++ ab3 = 0xff & (address >> 24);
++ ab2 = 0xff & (address >> 16);
++ ab1 = 0xff & (address >> 8);
++ ab0 = 0xff & address;
++
++ gpio_write(&gp, GPIO_SD_CS, LOW);
++ for (i = 0; i < 4; i++)
++ mmc_spi_readwrite(0xff);
++ mmc_spi_readwrite(0x51);
++ mmc_spi_readwrite(ab3); /* msb */
++ mmc_spi_readwrite(ab2);
++ mmc_spi_readwrite(ab1);
++ mmc_spi_readwrite(ab0); /* lsb */
++
++ mmc_spi_readwrite(0xff);
++ for (i = 0; i < 8; i++) {
++ result = mmc_spi_readwrite(0xff);
++ if (result == 0x00)
++ break;
++ }
++ if (result != 0x00) {
++ gpio_write(&gp, GPIO_SD_CS, HIGH);
++ mmc_spi_readwrite(0xff);
++ return (1);
++ }
++ for (i = 0; i < 100000; i++) {
++ result = mmc_spi_readwrite(0xff);
++ if (result == 0xfe)
++ break;
++ }
++ if (result != 0xfe) {
++ gpio_write(&gp, GPIO_SD_CS, HIGH);
++ mmc_spi_readwrite(0xff);
++ return (2);
++ }
++ for (i = 0; i < 512; i++) {
++ result = mmc_spi_readwrite(0xff);
++ data[i] = result;
++ }
++ for (i = 0; i < 2; i++) {
++ result = mmc_spi_readwrite(0xff);
++ }
++ gpio_write(&gp, GPIO_SD_CS, HIGH);
++ mmc_spi_readwrite(0xff);
++
++ return (0);
++}
++
++static void mmc_request(request_queue_t * q)
++{
++ unsigned int mmc_address;
++ unsigned char *buffer_address;
++ int nr_sectors;
++ int i;
++ int cmd;
++ int result, code;
++
++ (void) q;
++ while (1) {
++ code = 1; // Default is success
++ INIT_REQUEST;
++ mmc_address =
++ (CURRENT->sector +
++ hd[MINOR(CURRENT->rq_dev)].start_sect) * hd_hardsectsizes[0];
++ buffer_address = CURRENT->buffer;
++ nr_sectors = CURRENT->current_nr_sectors;
++ cmd = CURRENT->cmd;
++ if (((CURRENT->sector + CURRENT->current_nr_sectors +
++ hd[MINOR(CURRENT->rq_dev)].start_sect) > hd[0].nr_sects)
++ || (mmc_media_detect == 0)) {
++ code = 0;
++ } else if (cmd == READ) {
++ spin_unlock_irq(&io_request_lock);
++ for (i = 0; i < nr_sectors; i++) {
++ result = mmc_read_block(buffer_address, mmc_address);
++ if (result != 0) {
++ printk("mmc: error %d in mmc_read_block\n", result);
++ code = 0;
++ break;
++ } else {
++ mmc_address += hd_hardsectsizes[0];
++ buffer_address += hd_hardsectsizes[0];
++ }
++ }
++ spin_lock_irq(&io_request_lock);
++ } else if (cmd == WRITE) {
++ spin_unlock_irq(&io_request_lock);
++ for (i = 0; i < nr_sectors; i++) {
++ result = mmc_write_block(mmc_address, buffer_address);
++ if (result != 0) {
++ printk("mmc: error %d in mmc_write_block\n", result);
++ code = 0;
++ break;
++ } else {
++ mmc_address += hd_hardsectsizes[0];
++ buffer_address += hd_hardsectsizes[0];
++ }
++ }
++ spin_lock_irq(&io_request_lock);
++ } else {
++ code = 0;
++ }
++ end_request(code);
++ }
++}
++
++
++static int mmc_open(struct inode *inode, struct file *filp)
++{
++ /*int device;*/
++ (void) filp;
++ mmc_media_detect = mmc_spi_media_detect();
++
++ if (mmc_media_detect == 0)
++ return -ENODEV;
++
++#if defined(MODULE)
++ MOD_INC_USE_COUNT;
++#endif
++ return 0;
++}
++
++static int mmc_release(struct inode *inode, struct file *filp)
++{
++ (void) filp;
++ fsync_dev(inode->i_rdev);
++ invalidate_buffers(inode->i_rdev);
++
++#if defined(MODULE)
++ MOD_DEC_USE_COUNT;
++#endif
++ return 0;
++}
++
++static int mmc_revalidate(kdev_t dev)
++{
++ int target, max_p, start, i;
++
++ mmc_media_detect = mmc_spi_media_detect();
++
++ if (mmc_media_detect == 0)
++ return -ENODEV;
++
++ target = DEVICE_NR(dev);
++
++ max_p = hd_gendisk.max_p;
++ start = target << 6;
++ for (i = max_p - 1; i >= 0; i--) {
++ int minor = start + i;
++ invalidate_device(MKDEV(MAJOR_NR, minor), 1);
++ hd_gendisk.part[minor].start_sect = 0;
++ hd_gendisk.part[minor].nr_sects = 0;
++ }
++
++ grok_partitions(&hd_gendisk, target, 1 << 6, hd_sizes[0] * 2);
++
++ return 0;
++}
++
++static int mmc_ioctl(struct inode *inode, struct file *filp,
++ unsigned int cmd, unsigned long arg)
++{
++ if (!inode || !inode->i_rdev)
++ return -EINVAL;
++
++ switch (cmd) {
++ case BLKGETSIZE:
++ return put_user(hd[MINOR(inode->i_rdev)].nr_sects,
++ (unsigned long *) arg);
++ case BLKGETSIZE64:
++ return put_user((u64) hd[MINOR(inode->i_rdev)].
++ nr_sects, (u64 *) arg);
++ case BLKRRPART:
++ if (!capable(CAP_SYS_ADMIN))
++ return -EACCES;
++
++ return mmc_revalidate(inode->i_rdev);
++ case HDIO_GETGEO:
++ {
++ struct hd_geometry *loc, g;
++ loc = (struct hd_geometry *) arg;
++ if (!loc)
++ return -EINVAL;
++ g.heads = 4;
++ g.sectors = 16;
++ g.cylinders = hd[0].nr_sects / (4 * 16);
++ g.start = hd[MINOR(inode->i_rdev)].start_sect;
++ return copy_to_user(loc, &g, sizeof(g)) ? -EFAULT : 0;
++ }
++ default:
++ return blk_ioctl(inode->i_rdev, cmd, arg);
++ }
++}
++
++
++/*
++static int mmc_check_media_change(kdev_t dev)
++{
++ (void) dev;
++ if (mmc_media_changed == 1) {
++ mmc_media_changed = 0;
++ return 1;
++ } else
++ return 0;
++}
++*/
++
++static struct block_device_operations mmc_bdops = {
++ open:mmc_open,
++ release:mmc_release,
++ ioctl:mmc_ioctl,
++/* FIXME: add media change support
++ * check_media_change: mmc_check_media_change,
++ * revalidate: mmc_revalidate,
++ */
++};
++
++static struct gendisk hd_gendisk = {
++ major:MAJOR_NR,
++ major_name:DEVICE_NAME,
++ minor_shift:6,
++ max_p:1 << 6,
++ part:hd,
++ sizes:hd_sizes,
++ fops:&mmc_bdops,
++};
++
++static int mmc_init(void)
++{
++ int result;
++
++ result = mmc_spi_hardware_init();
++
++ if (result != 0) {
++ printk("mmc: error %d in mmc_spi_hardware_init\n", result);
++ return -1;
++ }
++
++ result = mmc_spi_card_init();
++ if (result != 0) {
++ // Give it an extra shot
++ result = mmc_spi_card_init();
++ if (result != 0) {
++ printk("mmc: error %d in mmc_card_init\n", result);
++ return -1;
++ }
++ }
++
++ memset(hd_sizes, 0, sizeof(hd_sizes));
++ result = mmc_spi_card_config();
++ if (result != 0) {
++ printk("mmc: error %d in mmc_card_config\n", result);
++ return -1;
++ }
++
++
++ blk_size[MAJOR_NR] = hd_sizes;
++
++ memset(hd, 0, sizeof(hd));
++ hd[0].nr_sects = hd_sizes[0] * 2;
++
++ blksize_size[MAJOR_NR] = hd_blocksizes;
++ hardsect_size[MAJOR_NR] = hd_hardsectsizes;
++ max_sectors[MAJOR_NR] = hd_maxsect;
++
++ hd_gendisk.nr_real = 1;
++
++ register_disk(&hd_gendisk, MKDEV(MAJOR_NR, 0), 1 << 6,
++ &mmc_bdops, hd_sizes[0] * 2);
++
++ return 0;
++}
++
++static void mmc_exit(void)
++{
++ blk_size[MAJOR_NR] = NULL;
++ blksize_size[MAJOR_NR] = NULL;
++ hardsect_size[MAJOR_NR] = NULL;
++ max_sectors[MAJOR_NR] = NULL;
++ hd[0].nr_sects = 0;
++}
++
++static void mmc_check_media(void)
++{
++ int old_state, new_state;
++ int result;
++
++ old_state = mmc_media_detect;
++ new_state = mmc_spi_media_detect();
++
++ if (old_state != new_state) {
++ mmc_media_changed = 1;
++ if (new_state == PRESENT) {
++ result = mmc_init();
++ if (result != 0)
++ printk("mmc: error %d in mmc_init\n", result);
++ } else {
++ mmc_exit();
++ }
++ }
++
++ /* del_timer(&mmc_timer);
++ mmc_timer.expires = jiffies + 10*HZ;
++ add_timer(&mmc_timer); */
++}
++
++static int __init mmc_driver_init(void)
++{
++ int result;
++
++ result = devfs_register_blkdev(MAJOR_NR, DEVICE_NAME, &mmc_bdops);
++ if (result < 0) {
++ printk(KERN_WARNING "mmc: can't get major %d\n", MAJOR_NR);
++ return result;
++ }
++
++ blk_init_queue(BLK_DEFAULT_QUEUE(MAJOR_NR), mmc_request);
++
++ mmc_check_media();
++
++ /*init_timer(&mmc_timer);
++ mmc_timer.expires = jiffies + HZ;
++ mmc_timer.function = (void *)mmc_check_media;
++ add_timer(&mmc_timer); */
++
++
++ read_ahead[MAJOR_NR] = 8;
++ add_gendisk(&hd_gendisk);
++
++
++ return 0;
++}
++
++static void __exit mmc_driver_exit(void)
++{
++ int i;
++ del_timer(&mmc_timer);
++
++ for (i = 0; i < (1 << 6); i++)
++ fsync_dev(MKDEV(MAJOR_NR, i));
++
++ blk_cleanup_queue(BLK_DEFAULT_QUEUE(MAJOR_NR));
++ del_gendisk(&hd_gendisk);
++ devfs_unregister_blkdev(MAJOR_NR, DEVICE_NAME);
++ mmc_exit();
++}
++
++module_init(mmc_driver_init);
++module_exit(mmc_driver_exit);
+--- linux-2.4.27/drivers/block/Config.in~mmc-spi
++++ linux-2.4.27/drivers/block/Config.in
+@@ -4,6 +4,7 @@
+ mainmenu_option next_comment
+ comment 'Block devices'
+
++tristate 'MMC SPI driver' CONFIG_BLK_DEV_MMC
+ tristate 'Normal floppy disk support' CONFIG_BLK_DEV_FD
+ if [ "$CONFIG_AMIGA" = "y" ]; then
+ tristate 'Amiga floppy support' CONFIG_AMIGA_FLOPPY
+--- linux-2.4.27/drivers/block/Makefile~mmc-spi
++++ linux-2.4.27/drivers/block/Makefile
+@@ -15,6 +15,7 @@
+ obj-y := ll_rw_blk.o blkpg.o genhd.o elevator.o
+
+ obj-$(CONFIG_MAC_FLOPPY) += swim3.o
++obj-$(CONFIG_BLK_DEV_MMC) += mmc.o
+ obj-$(CONFIG_BLK_DEV_FD) += floppy.o
+ obj-$(CONFIG_AMIGA_FLOPPY) += amiflop.o
+ obj-$(CONFIG_ATARI_FLOPPY) += ataflop.o