Index: drivers/block/Kconfig =================================================================== RCS file: /cvs/eps/dev_eng/sw/products/Linux/PXAEngine/pxa/linux-2.6.17-rc5/drivers/block/Kconfig,v retrieving revision 1.1.1.1 retrieving revision 1.2 diff -c -3 -p -r1.1.1.1 -r1.2 *** drivers/block/Kconfig 29 May 2006 00:55:20 -0000 1.1.1.1 --- drivers/block/Kconfig 1 Jun 2006 17:05:41 -0000 1.2 *************** *** 4,9 **** --- 4,16 ---- menu "Block devices" + config BLK_DEV_LOGICPD_CF + bool "LogicPD memory-mapped CompactFlash card support" + depends on MACH_LOGICPD_PXA270 + ---help--- + If you want to use the memory-mapped comapct flash card on + the LogicPD SDK, say Y. + config BLK_DEV_FD tristate "Normal floppy disk support" depends on ARCH_MAY_HAVE_PC_FDC Index: drivers/block/Makefile =================================================================== RCS file: /cvs/eps/dev_eng/sw/products/Linux/PXAEngine/pxa/linux-2.6.17-rc5/drivers/block/Makefile,v retrieving revision 1.1.1.1 retrieving revision 1.2 diff -c -3 -p -r1.1.1.1 -r1.2 *** drivers/block/Makefile 29 May 2006 00:55:20 -0000 1.1.1.1 --- drivers/block/Makefile 1 Jun 2006 17:05:59 -0000 1.2 *************** *** 5,10 **** --- 5,12 ---- # Rewritten to use lists instead of if-statements. # + obj-$(CONFIG_BLK_DEV_LOGICPD_CF)+= lpd270-cf.o + obj-$(CONFIG_MAC_FLOPPY) += swim3.o obj-$(CONFIG_BLK_DEV_FD) += floppy.o obj-$(CONFIG_AMIGA_FLOPPY) += amiflop.o Index: drivers/block/lpd270-cf.c =================================================================== RCS file: drivers/block/lpd270-cf.c diff -N drivers/block/lpd270-cf.c *** /dev/null 1 Jan 1970 00:00:00 -0000 --- drivers/block/lpd270-cf.c 1 Jun 2006 16:23:35 -0000 1.1 *************** *** 0 **** --- 1,675 ---- + /* + * Support for LogicPD SDK Memory-mapped CompactFlash interface + * + * Copyright 2006 Logic Product Development + * + * This file is subject to the terms and conditions of the GNU General Public + * License. See the file COPYING in the main directory of this archive + * for more details. + */ + + + /* Uncomment the following if you want verbose error reports. */ + /* #define VERBOSE_ERRORS */ + + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include /* CMOS defines */ + #include + #include + #include + + #define REALLY_SLOW_IO + #include + #include + #include + #include + + #ifdef __arm__ + #undef HD_IRQ + #endif + #include + #ifdef __arm__ + #define HD_IRQ IRQ_HARDDISK + #endif + + #define DEBUG + + /* Hd controller regster ports */ + + #define HD_DATA 0x1f0 /* _CTL when writing */ + #define HD_ERROR 0x1f1 /* see err-bits */ + #define HD_NSECTOR 0x1f2 /* nr of sectors to read/write */ + #define HD_SECTOR 0x1f3 /* starting sector */ + #define HD_LCYL 0x1f4 /* starting cylinder */ + #define HD_HCYL 0x1f5 /* high byte of starting cyl */ + #define HD_CURENT 0x1f6 /* 101dhhhh , d=drive, hhhh=head */ + #define HD_STATUS 0x1f7 /* see status-bits */ + #define HD_FEATURE HD_ERROR /* same io address, read=error, write=feature */ + #define HD_PRECOMP HD_FEATURE /* obsolete use of this port - predates IDE */ + #define HD_COMMAND HD_STATUS /* same io address, read=status, write=cmd */ + + #define HD_CMD 0x3f6 /* used for resets */ + #define HD_ALTSTATUS 0x3f6 /* same as HD_STATUS but doesn't clear irq */ + + /* Bits of HD_STATUS */ + #define ERR_STAT 0x01 + #define INDEX_STAT 0x02 + #define ECC_STAT 0x04 /* Corrected error */ + #define DRQ_STAT 0x08 + #define SEEK_STAT 0x10 + #define SERVICE_STAT SEEK_STAT + #define WRERR_STAT 0x20 + #define READY_STAT 0x40 + #define BUSY_STAT 0x80 + + /* Bits for HD_ERROR */ + #define MARK_ERR 0x01 /* Bad address mark */ + #define TRK0_ERR 0x02 /* couldn't find track 0 */ + #define ABRT_ERR 0x04 /* Command aborted */ + #define MCR_ERR 0x08 /* media change request */ + #define ID_ERR 0x10 /* ID field not found */ + #define MC_ERR 0x20 /* media changed */ + #define ECC_ERR 0x40 /* Uncorrectable ECC error */ + #define BBD_ERR 0x80 /* pre-EIDE meaning: block marked bad */ + #define ICRC_ERR 0x80 /* new meaning: CRC error during transfer */ + + static DEFINE_SPINLOCK(hd_lock); + static struct request_queue *hd_queue; + + #define MAJOR_NR HD_MAJOR + #define QUEUE (hd_queue) + #define CURRENT elv_next_request(hd_queue) + + #define TIMEOUT_VALUE (6*HZ) + #define HD_DELAY 0 + + #define MAX_ERRORS 16 /* Max read/write errors/sector */ + #define RESET_FREQ 8 /* Reset controller every 8th retry */ + #define RECAL_FREQ 4 /* Recalibrate every 4th retry */ + #define MAX_HD 2 + + #define STAT_OK (READY_STAT|SEEK_STAT) + #define OK_STATUS(s) (((s)&(STAT_OK|(BUSY_STAT|WRERR_STAT|ERR_STAT)))==STAT_OK) + + static int driveno = 0; + static int debug = 0; + + // Start of CF registers + #define CPLD_ATA_REG_BASE 0x14001800 + static unsigned char *reg_base; + + static inline unsigned int read_reg(unsigned char *base, unsigned int reg) + { + volatile unsigned short val; + + if (reg & 1) + val = *((volatile unsigned short *)(base + reg - 1)) >> 8; + else + val = *((volatile unsigned short *)(base + reg)); + + if (debug) + printk("%s: %02x %04x \n", __FUNCTION__, reg, val); + + return val; + } + + static inline void write_reg(unsigned char *base, unsigned int reg, unsigned int val) + { + if (debug) + printk("%s: %02x %04x\n", __FUNCTION__, reg, val); + if (reg & 1) + *((volatile unsigned short *)(base + reg - 1)) = (val << 8); + else + *((volatile unsigned short *)(base + reg)) = val; + } + + #define CB_DATA 0x08 + #define CB_ERR 0x0d + #define CB_SC_SN 0x02 + #define CB_CYL 0x04 + #define CB_STAT 0x07 + #define CB_DH_CMD 0x06 + #define CB_ASTAT 0x0e + #define CB_DC 0x0e + #define CB_DA 0x0f + + #define CB_STAT_BSY 0x80 + #define CB_STAT_DRQ 0x08 + #define CB_STAT_SEEK 0x10 + #define CB_STAT_DF 0x20 + #define CB_STAT_READY 0x40 + #define CB_STAT_ERR 0x01 + #define CB_DC_HD15 0x08 + #define CB_DC_NIEN 0x02 + + #define CMD_IDENTIFY_DEVICE 0xec + #define CMD_READ_SECTORS 0x20 + #define CMD_WRITE_SECTORS 0x30 + + #define TIMEOUT 0x800000 + + int cfide_card_present(void) + { + unsigned char data, data1, data2; + + /* Flip Sector Count */ + data = read_reg(reg_base, CB_SC_SN); + data1 = (~data) & 0xff; + write_reg(reg_base, CB_SC_SN, data1); + + /* write to the data register to waggle the bus */ + write_reg(reg_base, CB_DATA, data); + + /* Read back the sector count and if it matches what we put there + then the CF is present */ + data2 = read_reg(reg_base, CB_SC_SN); + if (data2 == data1) + return 1; + else { + printk("data %02x data1 %02x data2 %02x\n", data, data1, data2); + return 0; + } + } + + /* + * This struct defines the HD's and their types. + */ + struct hd_i_struct { + unsigned int head,sect,cyl,wpcom,lzone,ctl; + int unit; + int recalibrate; + int special_op; + }; + + #ifdef HD_TYPE + static struct hd_i_struct hd_info[] = { HD_TYPE }; + static int NR_HD = ((sizeof (hd_info))/(sizeof (struct hd_i_struct))); + #else + static struct hd_i_struct hd_info[MAX_HD]; + static int NR_HD; + #endif + + static struct gendisk *hd_gendisk[MAX_HD]; + + + + #if (HD_DELAY > 0) + + #include + + unsigned long last_req; + + unsigned long read_timer(void) + { + unsigned long t, flags; + int i; + + spin_lock_irqsave(&i8253_lock, flags); + t = jiffies * 11932; + outb_p(0, 0x43); + i = inb_p(0x40); + i |= inb(0x40) << 8; + spin_unlock_irqrestore(&i8253_lock, flags); + return(t - i); + } + #endif + + static void __init hd_setup(char *str, int *ints) + { + int hdind = 0; + + if (ints[0] != 3) + return; + if (hd_info[0].head != 0) + hdind=1; + hd_info[hdind].head = ints[2]; + hd_info[hdind].sect = ints[3]; + hd_info[hdind].cyl = ints[1]; + hd_info[hdind].wpcom = 0; + hd_info[hdind].lzone = ints[1]; + hd_info[hdind].ctl = (ints[2] > 8 ? 8 : 0); + NR_HD = hdind+1; + } + + + + void cfide_wait_fin(void) + { + unsigned long timer; + + // printk("%s:%d\n", __FUNCTION__, __LINE__); + + udelay(500); // wait 500us + + for (timer = 0; + timer < TIMEOUT && (read_reg(reg_base, CB_STAT) & CB_STAT_BSY); ++timer) + yield(); + + if (timer == TIMEOUT) + printk("%s:%d\n", __FUNCTION__, __LINE__); + } + + void cfide_wait_drq(void) + { + unsigned long timer; + + for (timer = 0; + timer < TIMEOUT && !(read_reg(reg_base, CB_STAT) & CB_STAT_DRQ); ++timer) + yield(); + + if (timer == TIMEOUT) + printk("%s:%d\n", __FUNCTION__, __LINE__); + } + + static union { + struct hd_driveid id; + short sh[512/2]; + } info_buf; + + + /* Read cnt sectors from the flash, starting at lba, storing the data + at dest */ + static int cfide_read_sectors(uint8_t *dest, uint32_t lba, uint32_t cnt) + { + uint8_t sect, head, devHead, status, devCtrl; + uint16_t cyl; + uint32_t orig_lba = lba; + uint32_t i,j; + uint16_t data; + + // printk("%s: dest %p lba %u cnt %u\n", __FUNCTION__, dest, lba, cnt); + + if (lba + cnt > info_buf.id.lba_capacity) { + printk("%s: %u+%u is larger than %u\n", __FUNCTION__, lba, cnt, info_buf.id.lba_capacity); + return -EINVAL; + } + + if (cnt > 255) { + printk("%s: cnt %u is too large\n", __FUNCTION__, cnt); + return -EINVAL; + } + + + /* translate from LBA */ + sect = lba & 0xff; + lba >>= 8; + cyl = lba & 0xffff; + lba >>= 16; + head = (lba & 0x0f) | 0x40; + + devCtrl = CB_DC_HD15 | CB_DC_NIEN; + devHead = driveno | head; + + write_reg(reg_base, CB_DC, devCtrl); + write_reg(reg_base, CB_SC_SN, ((uint16_t)cnt & 0xff) | ((uint16_t)sect << 8)); + write_reg(reg_base, CB_CYL, cyl); + + write_reg(reg_base, CB_DH_CMD, devHead | (CMD_READ_SECTORS << 8)); + + for (j=0; j>8; + *dest++ = data; + #else + *dest++ = data; + *dest++ = data>>8; + #endif + } + + + cfide_wait_fin(); + + status = read_reg(reg_base, CB_STAT); + if (status & (CB_STAT_DF|CB_STAT_ERR)) { + printk("%s: error at block %d status %#x\n", __FUNCTION__, orig_lba+j, status); + break; + } + + } + + return 0; + } + + /* Write cnt sectors to the flash, starting at lba, reading the data + from src */ + static int cfide_write_sectors(uint8_t *src, uint32_t lba, uint32_t cnt) + { + uint8_t sect, head, devHead, status, devCtrl; + uint16_t cyl; + uint32_t orig_lba = lba; + uint32_t i,j; + uint16_t data; + + if (lba + cnt > info_buf.id.lba_capacity) { + printk("%s: %u+%u is larger than %u\n", __FUNCTION__, lba, cnt, info_buf.id.lba_capacity); + return -EINVAL; + } + + if (cnt > 255) { + printk("%s: cnt %u is too large\n", __FUNCTION__, cnt); + return -EINVAL; + } + + /* translate from LBA */ + sect = lba & 0xff; + lba >>= 8; + cyl = lba & 0xffff; + lba >>= 16; + head = (lba & 0x0f) | 0x40; + + devCtrl = CB_DC_HD15 | CB_DC_NIEN; + devHead = driveno | head; + + write_reg(reg_base, CB_DC, devCtrl); + write_reg(reg_base, CB_SC_SN, ((uint16_t)cnt & 0xff) | ((uint16_t)sect << 8)); + write_reg(reg_base, CB_CYL, cyl); + + write_reg(reg_base, CB_DH_CMD, devHead | (CMD_WRITE_SECTORS << 8)); + + for (j=0; jsector, req->current_nr_sectors, + req->buffer, rq_data_dir(req)); + end_request(req, 1); + } else { + printk (KERN_NOTICE "Skip non-fs request\n"); + end_request(req, 0); + continue; + } + } + } + + static int hd_getgeo(struct block_device *bdev, struct hd_geometry *geo) + { + struct hd_i_struct *disk = bdev->bd_disk->private_data; + + geo->heads = disk->head; + geo->sectors = disk->sect; + geo->cylinders = disk->cyl; + return 0; + } + + + static struct block_device_operations hd_fops = { + .getgeo = hd_getgeo, + }; + + void cfide_fetch_info(void) + { + int i; + + // printk("%s:%d\n", __FUNCTION__, __LINE__); + + /* Select the drive and wait for it to finish */ + driveno &= 1; + write_reg(reg_base, CB_DH_CMD, driveno); + cfide_wait_fin(); + + // printk("%s:%d\n", __FUNCTION__, __LINE__); + + write_reg(reg_base, CB_DH_CMD, driveno | (CMD_IDENTIFY_DEVICE << 8)); + cfide_wait_fin(); + + // printk("%s:%d\n", __FUNCTION__, __LINE__); + + + cfide_wait_drq(); + + // printk("%s:%d\n", __FUNCTION__, __LINE__); + + for (i=0; i<512; i+=2) + info_buf.sh[i/2] = read_reg(reg_base, CB_DATA); + + /* Fix lba_capcity */ + info_buf.id.lba_capacity = (info_buf.id.lba_capacity>>16) | (info_buf.id.lba_capacity<<16); + // printk("%s:%d lba_capacity %#x\n", __FUNCTION__, __LINE__, info_buf.id.lba_capacity); + } + + /* + * This is the hard disk IRQ description. The SA_INTERRUPT in sa_flags + * means we run the IRQ-handler with interrupts disabled: this is bad for + * interrupt latency, but anything else has led to problems on some + * machines. + * + * We enable interrupts in some of the routines after making sure it's + * safe. + */ + + static int __init hd_init(void) + { + int drive; + + printk("%s:%d MAJOR_NR %d\n", __FUNCTION__, __LINE__, MAJOR_NR); + + if (register_blkdev(MAJOR_NR,"hd")) + return -1; + + reg_base = (unsigned char *) ioremap_nocache(CPLD_ATA_REG_BASE, 0x1000); + if (!reg_base) { + printk("%s:%d\n", __FUNCTION__, __LINE__); + return -ENOMEM; + } + + /* If no card present, return */ + if (!cfide_card_present()) { + printk("No CompactFlash card detected\n"); + iounmap(reg_base); + return 0; + } + + /* Fetchthe device info */ + cfide_fetch_info(); + + hd_queue = blk_init_queue(do_hd_request, &hd_lock); + printk("%s:%d hd_queue %p\n", __FUNCTION__, __LINE__, hd_queue); + if (!hd_queue) { + unregister_blkdev(MAJOR_NR,"hd"); + return -ENOMEM; + } + + + blk_queue_max_sectors(hd_queue, 255); + blk_queue_hardsect_size(hd_queue, 512); + + #if 1 + hd_info[0].cyl = info_buf.id.cyls; + hd_info[0].head = info_buf.id.heads; + hd_info[0].wpcom = 0; + hd_info[0].ctl = 0; + hd_info[0].lzone = 0; + hd_info[0].sect = info_buf.id.sectors; + NR_HD++; + + printk("%s:%d NR_HD %d\n", __FUNCTION__, __LINE__, NR_HD); + + #else + #ifdef __i386__ + if (!NR_HD) { + extern struct drive_info drive_info; + unsigned char *BIOS = (unsigned char *) &drive_info; + unsigned long flags; + int cmos_disks; + + for (drive=0 ; drive<2 ; drive++) { + hd_info[drive].cyl = *(unsigned short *) BIOS; + hd_info[drive].head = *(2+BIOS); + hd_info[drive].wpcom = *(unsigned short *) (5+BIOS); + hd_info[drive].ctl = *(8+BIOS); + hd_info[drive].lzone = *(unsigned short *) (12+BIOS); + hd_info[drive].sect = *(14+BIOS); + #ifdef does_not_work_for_everybody_with_scsi_but_helps_ibm_vp + if (hd_info[drive].cyl && NR_HD == drive) + NR_HD++; + #endif + BIOS += 16; + } + + /* + We query CMOS about hard disks : it could be that + we have a SCSI/ESDI/etc controller that is BIOS + compatible with ST-506, and thus showing up in our + BIOS table, but not register compatible, and therefore + not present in CMOS. + + Furthermore, we will assume that our ST-506 drives + are the primary drives in the system, and + the ones reflected as drive 1 or 2. + + The first drive is stored in the high nibble of CMOS + byte 0x12, the second in the low nibble. This will be + either a 4 bit drive type or 0xf indicating use byte 0x19 + for an 8 bit type, drive 1, 0x1a for drive 2 in CMOS. + + Needless to say, a non-zero value means we have + an AT controller hard disk for that drive. + + Currently the rtc_lock is a bit academic since this + driver is non-modular, but someday... ? Paul G. + */ + + spin_lock_irqsave(&rtc_lock, flags); + cmos_disks = CMOS_READ(0x12); + spin_unlock_irqrestore(&rtc_lock, flags); + + if (cmos_disks & 0xf0) { + if (cmos_disks & 0x0f) + NR_HD = 2; + else + NR_HD = 1; + } + } + #endif /* __i386__ */ + #ifdef __arm__ + if (!NR_HD) { + /* We don't know anything about the drive. This means + * that you *MUST* specify the drive parameters to the + * kernel yourself. + */ + printk("hd: no drives specified - use hd=cyl,head,sectors" + " on kernel command line\n"); + } + #endif + #endif + if (!NR_HD) + goto out; + + for (drive=0 ; drive < NR_HD ; drive++) { + struct gendisk *disk = alloc_disk(64); + struct hd_i_struct *p = &hd_info[drive]; + if (!disk) + goto Enomem; + disk->major = MAJOR_NR; + disk->first_minor = drive << 6; + disk->fops = &hd_fops; + sprintf(disk->disk_name, "hd%c", 'a'+drive); + disk->private_data = p; + set_capacity(disk, p->head * p->sect * p->cyl); + disk->queue = hd_queue; + p->unit = drive; + hd_gendisk[drive] = disk; + printk ("%s: %luMB, CHS=%d/%d/%d\n", + disk->disk_name, (unsigned long)get_capacity(disk)/2048, + p->cyl, p->head, p->sect); + } + + /* Let them fly */ + for(drive=0; drive < NR_HD; drive++) + add_disk(hd_gendisk[drive]); + + return 0; + + out: + unregister_blkdev(MAJOR_NR,"hd"); + blk_cleanup_queue(hd_queue); + return -1; + Enomem: + while (drive--) + put_disk(hd_gendisk[drive]); + goto out; + } + + static int __init parse_hd_setup (char *line) { + int ints[6]; + + (void) get_options(line, ARRAY_SIZE(ints), ints); + hd_setup(NULL, ints); + + return 1; + } + __setup("hd=", parse_hd_setup); + + module_init(hd_init); + + /* + * Local variables: + * c-indent-level: 4 + * tab-width: 4 + * End: + */