diff -urN -X ignore orig/linux-2.4.21-pre4/drivers/block/Config.in new/linux-2.4.21-pre4/drivers/block/Config.in --- orig/linux-2.4.21-pre4/drivers/block/Config.in Wed Sep 11 21:44:45 2002 +++ new/linux-2.4.21-pre4/drivers/block/Config.in Wed Feb 11 15:06:31 2004 @@ -50,4 +50,8 @@ bool 'Per partition statistics in /proc/partitions' CONFIG_BLK_STATS +if [ "$CONFIG_HUMBLESOFT_NCARD" = "y" ]; then + tristate ' N-Card Multi Medea Card driver' CONFIG_NCARD_MMC +fi + endmenu diff -urN -X ignore orig/linux-2.4.21-pre4/drivers/block/Makefile new/linux-2.4.21-pre4/drivers/block/Makefile --- orig/linux-2.4.21-pre4/drivers/block/Makefile Wed Feb 26 09:53:48 2003 +++ new/linux-2.4.21-pre4/drivers/block/Makefile Wed Feb 11 15:27:44 2004 @@ -31,6 +31,7 @@ obj-$(CONFIG_BLK_DEV_DAC960) += DAC960.o obj-$(CONFIG_BLK_DEV_UMEM) += umem.o obj-$(CONFIG_BLK_DEV_NBD) += nbd.o +obj-$(CONFIG_NCARD_MMC) += ncard-mmc.o subdir-$(CONFIG_PARIDE) += paride diff -urN -X ignore orig/linux-2.4.21-pre4/drivers/block/ncard-mmc.c new/linux-2.4.21-pre4/drivers/block/ncard-mmc.c --- orig/linux-2.4.21-pre4/drivers/block/ncard-mmc.c Thu Jan 1 09:00:00 1970 +++ new/linux-2.4.21-pre4/drivers/block/ncard-mmc.c Wed Feb 11 16:58:49 2004 @@ -0,0 +1,800 @@ +/* ncard-mmc.c: A Driver for Multi Media Card on Humblesoft VR4131DIMM module board. */ + +static const char version[] = +"ncard-mmc.c:v1.00 2003/06/19 Hiroshi Narimatsu(nari@humblesoft.com)\n"; + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define MAJOR_NR mmc_major +static int mmc_major = 30; + +#define MMC_SHIFT 4 +#define MMC_DEVICE_MAX 1 +#define DEVICE_NR(device) (MINOR(device) >> MMC_SHIFT) +#define DEVICE_NAME "mmc" +#define DEVICE_INTR mmc_intr +#define DEVICE_NO_RANDOM +#define DEVICE_REQUEST mmc_request +#define DEVICE_OFF(d) + +#include +#include + +#include +#include +#include + +static void mmc_cs_set(int data); +static void mmc_write_byte(u8 data); +static u8 mmc_read_byte(void); +static void mmc_write(u8 *data, size_t size); +static int mmc_init(void); +static u8 mmc_cmd1(void); +static u8 mmc_cmd0(void); +static u8 mmc_crc7(const u8 *data, size_t len); +static u8 mmc_get_resp8(void); +static void mmc_write(u8 *data, size_t size); +static u8 mmc_cmd9(u8 csd[18]); +static u8 mmc_read16byte_cmd(u8 cmd, u8 data[18]); +static u8 get_crc7(const u8 *buff, int len); +static u16 get_ccitt_crc(const u8 *data, int size); +static void crc2str(u8 *data, const u16 *crc); +static u8 mmc_cmd17(u32 adr,u8 *data); +static u8 mmc_cmd24(u32 adr,u8 *data); +static u16 mmc_cmd13(void); +static void mmc_send_clk(size_t count); +static int mmc_spimode(void); + +static int mmc_devs = MMC_DEVICE_MAX; +static int *mmc_part_sizes; /* device size [kbyte] for each minor number */ +static int *mmc_part_hardsects; /* sector size [byte] for each minor number */ +static struct hd_struct *mmc_partitions; + +static int mmc_sizes[MMC_DEVICE_MAX]; +static int mmc_hardsects[MMC_DEVICE_MAX]; +static int mmc_card_initialized[MMC_DEVICE_MAX]; +static int mmc_changed[MMC_DEVICE_MAX]; + +static int mmc_open(struct inode *inode, struct file *filp); +static int mmc_release(struct inode *inode, struct file *filp); +static int mmc_ioctl(struct inode *inode, struct file *filp, + unsigned int cmd, unsigned long arg); +static int mmc_check_change(kdev_t i_rdev); +static int mmc_revalidate(kdev_t i_rdev); + +static struct block_device_operations mmc_bdops = { + .open = mmc_open, + .release = mmc_release, + .ioctl = mmc_ioctl, + .check_media_change = mmc_check_change, + .revalidate = mmc_revalidate, +}; + +static struct gendisk mmc_gendisk = { + .major = 0, + .major_name = "mmc", + .minor_shift = MMC_SHIFT, + .max_p = 1 << MMC_SHIFT, + .fops = &mmc_bdops, +}; + +static int mmc_transfer(const struct request *req) +{ + int size,i; + u32 ptr,off; + u8 r; + int minor = MINOR(req->rq_dev); + int num = DEVICE_NR(req->rq_dev); + int hardsect = mmc_hardsects[num]; + + ptr = (req->sector + mmc_partitions[minor].start_sect) * hardsect; + size = req->current_nr_sectors * hardsect; + +#if 0 + if(ptr + size > 0x2000000){ /* over 32Mbyte */ + static int count = 0; + if(count++ < 5) + printk(KERN_WARNING "mmc: request past end of device\n"); + return 0; + } +#endif + if(req->cmd == READ){ + off = 0; + for(i=0;icurrent_nr_sectors;i++){ + r = mmc_cmd17(ptr + off, req->buffer + off); + if(r != 0) return 0; + off += hardsect; + } + return 1; + } + else if(req->cmd == WRITE){ + off = 0; + for(i=0;icurrent_nr_sectors;i++){ + r = mmc_cmd24(ptr+off, req->buffer+off); + if(r != 0xe5) return 0; + off += hardsect; + mmc_cmd13(); + } + return 1; + } + else return 0; +} + +static void mmc_request(request_queue_t *q) +{ + int status; + + while(1) { + INIT_REQUEST; +#if 0 + printk("<1>mmc_request: %p: cmd %i sec%li (nr.%li)\n", + CURRENT, CURRENT->cmd, CURRENT->sector, + CURRENT->current_nr_sectors); +#endif + status = mmc_transfer(CURRENT); + + end_request(status); + } +} + +static int mmc_card_initialize(int num) +{ + u8 r,csd[40]; + u32 read_blk_len, c_size, c_size_mult, mult, block_len, block_nr; + + if(!mmc_spimode()) return -ENODEV; + + memset(csd, 0xaa, sizeof(csd)); + r = mmc_cmd9(csd); +#if 0 + { + int i,j; + for(j=0;j<18;j+=8){ + for(i=0;i<8 && i+j<18;i++) + printk(" %02x",csd[j+i]); + printk("\n"); + } + } +#endif + read_blk_len = csd[5] & 0x0f; + c_size = ((csd[6] & 0x03) << 10)|(csd[7] << 2)|((csd[8] & 0xc0) >> 6); + c_size_mult = ((csd[9] & 0x03) << 1) |((csd[10] &0x80) >> 7); + + block_len = 1 << read_blk_len; + mult = 1 << (c_size_mult + 2); + block_nr = (c_size+1) * mult; + + mmc_sizes[num] = block_nr * block_len / BLOCK_SIZE; + mmc_hardsects[num] = block_len; + mmc_card_initialized[num] = 1; + return 0; +} + +static int mmc_open(struct inode *inode, struct file *filp) +{ + int num; + num = DEVICE_NR(inode->i_rdev); + if(num >= mmc_devs || num < 0) return -ENODEV; + if(!mmc_card_initialized[num]){ + int r = mmc_card_initialize(num); + if(r < 0) return r; + } + MOD_INC_USE_COUNT; + return 0; +} + +static int mmc_release(struct inode *inode, struct file *filp) +{ + MOD_DEC_USE_COUNT; + return 0; +} + +static int mmc_ioctl(struct inode *inode, struct file *filp, + unsigned int cmd, unsigned long arg) +{ + int err; + long size; + struct hd_geometry geo; + int num = DEVICE_NR(inode->i_rdev); + + switch(cmd){ + case BLKGETSIZE: + if(!arg) return -EINVAL; + err = ! access_ok(VERIFY_WRITE, arg, sizeof(long)); + if(err) return -EFAULT; + size = mmc_gendisk.part[MINOR(inode->i_rdev)].nr_sects; + if(copy_to_user((long *)arg, &size, sizeof(long))) + return -EFAULT; + case BLKFLSBUF: /* flush */ + if (! capable(CAP_SYS_RAWIO)) return -EACCES; /* only root */ + fsync_dev(inode->i_rdev); + invalidate_buffers(inode->i_rdev); + return 0; + + case BLKRAGET: /* return the readahead value */ + err = ! access_ok(VERIFY_WRITE, arg, sizeof(long)); + if (err) return -EFAULT; + put_user(read_ahead[MAJOR(inode->i_rdev)],(long *) arg); + return 0; + + case BLKRASET: /* set the readahead value */ + if (!capable(CAP_SYS_RAWIO)) return -EACCES; + if (arg > 0xff) return -EINVAL; /* limit it */ + read_ahead[MAJOR(inode->i_rdev)] = arg; + return 0; + + case BLKRRPART: + return mmc_revalidate(inode->i_rdev); + + case HDIO_GETGEO: + err = !access_ok(VERIFY_WRITE, arg, sizeof(geo)); + if(err) return -EFAULT; + size = mmc_part_sizes[num] * BLOCK_SIZE / mmc_hardsects[num]; + geo.cylinders = (size & ~0x3f) >> 6; + geo.heads = 4; + geo.sectors = 16; + geo.start = 4; + if(copy_to_user((void *)arg, &geo,sizeof(geo))) + return -EFAULT; + return 0; + default: + return blk_ioctl(inode->i_rdev, cmd, arg); + } + return -ENOTTY; +} + +static int mmc_check_change(kdev_t i_rdev) +{ + int nr = DEVICE_NR(i_rdev); + return mmc_changed[nr]; +} + +static int mmc_revalidate(kdev_t i_rdev) +{ + int num = DEVICE_NR(i_rdev); + int part1 = (num << MMC_SHIFT) + 1; + int npart = (1 << MMC_SHIFT) - 1; + int i; + + mmc_card_initialize(num); + memset(mmc_gendisk.sizes + part1, 0, npart*sizeof(int)); + memset(mmc_gendisk.part + part1, 0, npart*sizeof(struct hd_struct)); + + for(i=0;i> 8 ^ *(data++)) & 0xffU] ^ + (crc << 8); + + return crc; +} + +static void crc2str(__u8 *data, const __u16 *crc) +{ + data[0] = 0xff & (crc[0] >> 8); + data[1] = 0xff & crc[0]; +} + + +#define USE_CSI + +#define MMC_CS (*(volatile u8 *)0xaa000080) /* output */ +#define MMC_DI (*(volatile u8 *)0xaa000082) /* output */ +#define MMC_CLK (*(volatile u8 *)0xaa000084) /* output */ +#define MMC_SEL (*(volatile u8 *)0xaa000086) /* output */ +#define MMC_DO (*(volatile u8 *)0xaa000080) /* input */ + +#define VR4131_CSI_MODEREG (*(volatile unsigned char *)0xaf0001a0) +#define VR4131_CSI_CLKSELREG (*(volatile unsigned char *)0xaf0001a1) +#define VR4131_CSI_SIRBREG (*(volatile unsigned short *)0xaf0001a2) +#define VR4131_CSI_SOTBREG (*(volatile unsigned short *)0xaf0001a4) +#define VR4131_CSI_SIRBEREG (*(volatile unsigned short *)0xaf0001a6) +#define VR4131_CSI_SOTBFREG (*(volatile unsigned short *)0xaf0001a8) +#define VR4131_CSI_SIOREG (*(volatile unsigned short *)0xaf0001aa) +#define VR4131_CSI_CNTREG (*(volatile unsigned short *)0xaf0001b0) +#define VR4131_CSI_INTREG (*(volatile unsigned short *)0xaf0001b2) +#define VR4131_CSI_IFIFOVREG (*(volatile unsigned short *)0xaf0001b4) +#define VR4131_CSI_OFIFOVREG (*(volatile unsigned short *)0xaf0001b6) +#define VR4131_CSI_IFIFOREG (*(volatile unsigned short *)0xaf0001b8) +#define VR4131_CSI_OFIFOREG (*(volatile unsigned short *)0xaf0001ba) +#define VR4131_CSI_FIFTRGREG (*(volatile unsigned short *)0xaf0001bc) + +static int verbose = 0; + +static void mmc_send_clk(size_t count) +{ + int i; + for(i=0;i 0; m >>= 1){ + MMC_CLK = 0; + MMC_DI = (data & m) ? 1 : 0; + MMC_CLK = 1; + } +} +#endif + +static void mmc_write(u8 *data, size_t size) +{ + int i; + for(i=0;i 10000) return 0xff; + } while(d == 0xff); + return d; +} + +static u8 mmc_crc7(const u8 *buff, size_t len) +{ + u8 crc7; + + crc7 = get_crc7(buff,len); + return (crc7 << 1) | 1; +} + + +static void mmc_noarg_cmd(u8 cmd, u8 data[6]) +{ + data[0] = 0x40 + cmd; + data[1] = data[2] = data[3] = data[4] = 0; + data[5] = mmc_crc7(data,5); +} + +static u8 mmc_cmd0(void) +{ + u8 data[6]; + u8 r; + + mmc_noarg_cmd(0,data); + mmc_cs_set(0); + mmc_write(data, 6); + r = mmc_get_resp8(); + mmc_send_clk(80); + mmc_cs_set(1); + return r; +} + +static u8 mmc_cmd1(void) +{ + u8 data[6],r; + + mmc_noarg_cmd(1,data); + mmc_cs_set(0); + mmc_write(data, 6); + r = mmc_get_resp8(); + mmc_cs_set(1); + return r; +} + +static u8 mmc_read16byte_cmd(u8 cmd, u8 data[16]) +{ + u8 r,buf[6],crc; + int i; + + mmc_noarg_cmd(cmd,buf); + mmc_cs_set(0); + mmc_write(buf, 6); + r = mmc_get_resp8(); + + { + u8 s; + s = mmc_get_resp8(); + if(verbose) printk(KERN_INFO "start byte=%02x\n",s); + } + + for(i=0;i<16;i++) + data[i] = mmc_read_byte(); + mmc_cs_set(1); + + crc = mmc_crc7(data, 15); + if(verbose) printk(KERN_INFO "crc=%02x\n",crc); + return r; +} + +static u8 mmc_cmd9(u8 csd[18]) /* CMD9: SEND_CSD */ +{ + return mmc_read16byte_cmd(9,csd); +} + +static u16 mmc_cmd13(void) /* CMD13: SEND STATUS */ +{ + u8 data[6],r1,r2; + + mmc_noarg_cmd(13,data); + mmc_cs_set(0); + mmc_write(data, 6); + r1 = mmc_get_resp8(); + r2 = mmc_read_byte(); + + mmc_cs_set(1); + return (r1 << 8)|r2; +} + +static u8 mmc_cmd17(u32 adr,u8 *data) /* CMD17: READ_SINGLE_BLOCK */ +{ + u8 cmd[6],r,s=0,crc[2]; + u16 crc16; + int i; + + cmd[0] = 0x40 + 17; + cmd[1] = adr >> 24; + cmd[2] = adr >> 16; + cmd[3] = adr >> 8; + cmd[4] = adr; + cmd[5] = mmc_crc7(cmd,5); + + mmc_cs_set(0); + mmc_write(cmd,6); + r = mmc_get_resp8(); + + do s = mmc_read_byte(); + while(s == 0xff); + + for(i=0;i<512;i++) + data[i] = mmc_read_byte(); + for(i=0;i<2;i++) + crc[i] = mmc_read_byte(); + mmc_cs_set(1); + crc16 = get_ccitt_crc(data,512); + return r; +} + +static u8 mmc_cmd24(u32 adr,u8 *data) /* CMD24: WRITE_BLOCK */ +{ + u8 cmd[6],r,crc[2],dr,d; + u16 crc16; + int i; + + cmd[0] = 0x40 + 24; + cmd[1] = adr >> 24; + cmd[2] = adr >> 16; + cmd[3] = adr >> 8; + cmd[4] = adr; + cmd[5] = mmc_crc7(cmd,5); + + mmc_cs_set(0); + mmc_write(cmd,6); + r = mmc_get_resp8(); + if(r != 0) return r; + + crc16 = get_ccitt_crc(data,512); + crc2str(crc, &crc16); + + mmc_write_byte(0xfe); /* start bit */ + for(i=0;i<512;i++) + mmc_write_byte(data[i]); + for(i=0;i<2;i++) + mmc_write_byte(crc[i]); + + dr = mmc_read_byte(); + if(verbose) printk(KERN_INFO "data response=%02x\n",dr); + + i=0; + do { + d = mmc_read_byte(); + if(verbose) printk(KERN_INFO "[%02x]",d); + i++; + } while( d != 0xff); + if(verbose) printk(KERN_INFO " busy loop count=%d\n",i); + + mmc_cs_set(1); + return dr | 0x80; +} + +static int mmc_init(void) +{ + int i; + +#ifdef USE_CSI + +#define CSI_CLOCK 0x1040 + vr41xx_clock_supply(CSI_CLOCK); + + i = 0; + while(VR4131_CSI_MODEREG & 1){ + if(i > 10000){ + printk(KERN_INFO "mmc_init:CSOT wait timeout.\n"); + return 0; + } + } + VR4131_CSI_CNTREG = 0x8000; + VR4131_CSI_CNTREG = 0x0000; + VR4131_CSI_MODEREG = 0x00; + VR4131_CSI_CLKSELREG = 1; /* clock 9.216MHz */ + VR4131_CSI_MODEREG = 0x80; + MMC_SEL = 1; /* use CSI(sin,sout,seclk) */ + +#endif + return 1; +} + +static int mmc_spimode(void) /* go into SPI Mode */ +{ + int i; + u8 r; + mmc_cs_set(1); + mmc_send_clk(80); + r = mmc_cmd0(); + if(verbose) printk(KERN_INFO "mmc_cmd0: r=%02x\n",r); + if(r == 0xff) return 0; + + r = mmc_cmd1(); + if(verbose) printk(KERN_INFO "mmc_cmd1: r=%02x\n",r); + if(r != 0x01) return 0; + + if(verbose) printk(KERN_INFO "mmc_cmd1 response:"); + for(i=0;r!=0x00 && i < 2000;i++){ + mmc_send_clk(80); + r = mmc_cmd1(); + if(verbose) printk(KERN_INFO "[%02x]",r); + if(r == 0xff) return 0; + } + if(verbose) printk(KERN_INFO "\n"); + mmc_send_clk(80); + return 1; +} + +/* + * Local variables: + * c-indent-level: 8 + * c-basic-offset: 8 + * tab-width: 8 + * compile-command: "cd ../..;make" + * End: + */