/* * physmap.c - mapper for AMD Elan SC520 CDP eval board. * based on Mark Langsdorf's net e520 file. * * Modified for the SC520 CDP by Vipin Malik * * based on pnc2000 by Crossnet Co. * * Copyright (C) 2000 Mark Langsdorf (mark.langsdorf@amd.com) * * This code is under the GPL, version 2.0 * * * Basically, this is a physical memory map driver with 2 Flash banks * w/ 2 partitions each. * * If this code is used on custom designed boards, the * WINDOW_ADDR and WINDOW_SIZE parameters may have to * be changed, as well as the size of partition 2. * */ #include #include #include #include #include #include #include #include #include /* Changes for the SC520 eval board -Vipin Malik NOTE: This code assumes equal size, contigious flash banks. */ #define NUMBANKS 2 /* The Embedded Systems BIOS decodes the first FLASH starting at 0x8400000. This is a *terrible* place for it bacause accessing the flash at this location causes the A22 address line to be high (that's what 0x8400000 binary's out to be). But this is the highest order address line on the raw flash devices themselves!! This causes the top HALF of the flash to be accessed first. Beyond the physical limits of the flash, the flash chip aliases over (to 0x880000 which causes the bottom half to be accessed. This splits the flash into two and inverts it! If you then try to acces this from another program that does NOT do this insanity, then you *will* access the first half of the flash, but not find what you expect there. That stuff is in the *second* half! Starting to address the flash at 0x8800000 here solves this problem. This of course assumes that the PAR register for ROMCS0# is wide enough to address at least 12Megs of memory starting at 0x8400000. */ #define WINDOW_ADDR_BANK0 0x8800000 /* #define WINDOW_ADDR_BANK0 0x8400000 */ #define WINDOW_SIZE 0x0800000 /* BANK1 is assumed to start where BANK0 ends.*/ #define WINDOW_ADDR_BANK1 WINDOW_ADDR_BANK0 + WINDOW_SIZE /* * MAP DRIVER STUFF */ static struct mtd_info *mymtd[2] = {NULL, NULL}; __u8 physmap_read8(struct map_info *map, unsigned long ofs) { return readb(map->map_priv_1 + ofs); } __u16 physmap_read16(struct map_info *map, unsigned long ofs) { return readw(map->map_priv_1 + ofs); } __u32 physmap_read32(struct map_info *map, unsigned long ofs) { return readl(map->map_priv_1 + ofs); } void physmap_copy_from(struct map_info *map, void *to, unsigned long from, ssize_t len) { memcpy_fromio(to, map->map_priv_1 + from, len); } void physmap_write8(struct map_info *map, __u8 d, unsigned long adr) { writeb(d, map->map_priv_1 + adr); } void physmap_write16(struct map_info *map, __u16 d, unsigned long adr) { writew(d, map->map_priv_1 + adr); } void physmap_write32(struct map_info *map, __u32 d, unsigned long adr) { writel(d, map->map_priv_1 + adr); } void physmap_copy_to(struct map_info *map, unsigned long to, const void *from, ssize_t len) { memcpy_toio(map->map_priv_1 + to, from, len); } /* Each struct below defines one flash bank. Then each bank is partitioned into 2 partitions of equal (4MB) each. This can easily be changed as long as each partition starts/ends on sector boundary. */ struct map_info physmap_map[NUMBANKS] = { { "Elan SC520 Physically mapped flash BANK 0", WINDOW_SIZE, 4, /* Bus width in octets. 4= 32bit */ physmap_read8, physmap_read16, physmap_read32, physmap_copy_from, physmap_write8, physmap_write16, physmap_write32, physmap_copy_to, 0, /* set_vpp */ 0, /* map_priv_1 */ 0, /* map_priv_2 */ 0, /* fldrv_priv ?? */ 0 /* fldrv_destroy */ }, { "Elan SC520 Physically mapped flash BANK 1", WINDOW_SIZE, 4, /* Bus width in octets. 4= 32bit */ physmap_read8, physmap_read16, physmap_read32, physmap_copy_from, physmap_write8, physmap_write16, physmap_write32, physmap_copy_to, 0, /* set_vpp */ 0, /* map_priv_1 */ 0, /* map_priv_2 */ 0, /* fldrv_priv ?? */ 0 /* fldrv_destroy */ } }; /* * MTD 'PARTITIONING' STUFF */ #define NUM_PARTITIONS 1 /* The following will give you: /dev/mtd0: partition1 of flash BANK0 /dev/mtd1: partition2 of flash BANK0 /dev/mtd2: partition1 of flash BANK1 /dev/mtd3: partition2 of flash BANK1 */ static struct mtd_partition physmap_partitions[NUM_PARTITIONS] = { { name: "Partition 1", /* The first partition */ size: 0x800000, offset: 0 } }; #if LINUX_VERSION_CODE < 0x20212 && defined(MODULE) #define init_physmap init_module #define cleanup_physmap cleanup_module #endif mod_init_t init_physmap(void) { #define ROMCS_REG_RSV_BIT_MASK (unsigned short)0x1E37 unsigned long iomapadr; int i, cnt; unsigned short romcs1, romcs2; volatile unsigned char dump; /* Print out all the PAR registers to see whick register sets the ROMCS0# and ROMCS1#.*/ iomapadr = (unsigned long) ioremap_nocache(0xFFFEF000, 0xFFF); for(cnt=0; cnt <= 15; cnt++){ printk("PAR%i=0x%lxh\n", cnt, *((unsigned long *)(iomapadr+(cnt*4)+0x88))); } /*Now set PAR8 to 0x8800000 and PAR9 to 0x9000000 */ *((unsigned long *)(iomapadr+(8*4)+0x88)) = (unsigned long)0xaa1fc880; /* PAR8, cache disabled, 0x80h 64KB pages */ *((unsigned long *)(iomapadr+(9*4)+0x88)) = (unsigned long)0xca1fc900; /* PAR9, cache disabled, 0x80h 64KB pages */ /* Now set PAR10 (which was orig unused for selecting 32KB in GP mem space using GPCS4# (for accessing the FRAM) */ *((volatile unsigned long *)(iomapadr+(10*4)+0x88)) = (unsigned long)0x502000D0; /* PAR10,GP memory,GPCS4 @0xD0000,32KB */ asm volatile ("wbinvd\n\t"); /* invalidate the cache as the ELAN book instructs. Hope this is the correct way. */ printk("PAR%i=0x%lxh\n", 8, *((unsigned long *)(iomapadr+(8*4)+0x88))); printk("PAR%i=0x%lxh\n", 9, *((unsigned long *)(iomapadr+(9*4)+0x88))); printk("PAR%i=0x%lxh\n", 10, *((unsigned long *)(iomapadr+(10*4)+0x88))); /* Get the ROMCS1 and ROMCS0 chipselect registers.*/ romcs1 = *((unsigned short *)(iomapadr+0x54)); romcs2 = *((unsigned short *)(iomapadr+0x56)); printk("ROMCS1=0x%xh\n", romcs1); printk("ROMCS2=0x%xh\n", romcs2); *((volatile unsigned short *)(iomapadr+0x54)) = (romcs1 |(unsigned short)0x0007)&ROMCS_REG_RSV_BIT_MASK; /* 7 wait states*/ *((volatile unsigned short *)(iomapadr+0x56)) = (romcs2 |(unsigned short)0x0007)&ROMCS_REG_RSV_BIT_MASK; /* 7 wait states*/ /* Get the ROMCS1 and ROMCS0 chipselect registers.*/ romcs1 = *((unsigned short *)(iomapadr+0x54)); romcs2 = *((unsigned short *)(iomapadr+0x56)); printk("ROMCS1=0x%xh\n", romcs1); printk("ROMCS2=0x%xh\n", romcs2); printk("GPECHO = 0x%xh\n", *((unsigned char *)(iomapadr+0xC00))); printk("GPCSDW = 0x%xh\n", *((unsigned char *)(iomapadr+0xC01))); printk("GPCSQUAL = 0x%xh\n", *((unsigned short *)(iomapadr+0xC02))); printk("GPCSRT = 0x%xh\n", *((unsigned char *)(iomapadr+0xC08))); printk("GPCSPW = 0x%xh\n", *((unsigned char *)(iomapadr+0xC09))); printk("GPCSOFF = 0x%xh\n", *((unsigned char *)(iomapadr+0xC0A))); printk("GPRDW = 0x%xh\n", *((unsigned char *)(iomapadr+0xC0B))); printk("GPRDOFF = 0x%xh\n", *((unsigned char *)(iomapadr+0xC0C))); printk("GPWRW = 0x%xh\n", *((unsigned char *)(iomapadr+0xC0D))); printk("GPWROFF = 0x%xh\n", *((unsigned char *)(iomapadr+0xC0E))); printk("GPALEW = 0x%xh\n", *((unsigned char *)(iomapadr+0xC0F))); printk("GPALEOFF = 0x%xh\n", *((unsigned char *)(iomapadr+0xC10))); printk("CSPFS = 0x%xh\n", *((unsigned char *)(iomapadr+0xC24))); iounmap((void *) iomapadr); /* Free the iomapping */ /* ************* The actual FLASH mtd stuff starts here ************** */ iomapadr = (unsigned long) ioremap_nocache(WINDOW_ADDR_BANK0, WINDOW_SIZE * NUMBANKS); if (!iomapadr) { printk("physmap.c:MTD:Failed to ioremap the physical memory!\n"); return -EIO; } else printk("physmap.c:MTD:mapped physical memory to %08lx\n", iomapadr); for(i = 0; i < NUMBANKS; i++){ printk(KERN_INFO "AMD Elan Sc520 flash mapping defined: %x at %x\n", WINDOW_SIZE, WINDOW_ADDR_BANK0 + (i * WINDOW_SIZE)); /* assumes equally sized windows */ physmap_map[i].map_priv_1 = iomapadr + (i * WINDOW_SIZE); /* assumes equally sized windows */ mymtd[i] = (struct mtd_info *)do_cfi_probe(&physmap_map[i]); if (mymtd[i]) { printk(KERN_INFO "physmap flash module installed for flash BANK%i\n", i); #ifdef MODULE mymtd[i]->module = THIS_MODULE; #endif if( add_mtd_partitions(mymtd[i], physmap_partitions, NUM_PARTITIONS) < 0){ goto ERR_JMPOUT; } }else{ goto ERR_JMPOUT; } } return 0; /* Bad stuff has happened. Clean up and return error. */ ERR_JMPOUT: iounmap((void *) iomapadr); /* give back our memory mapping */ for( i = 0; i < NUMBANKS; i++){ physmap_map[i].map_priv_1 = 0; } inter_module_put("cfi_probe"); return -ENXIO; } mod_exit_t cleanup_physmap(void) { int i; for (i=0; i<2; i++) { if (mymtd[i]) { del_mtd_partitions(mymtd[i]); map_destroy(mymtd[i]); } } if (physmap_map[0].map_priv_1) { iounmap((void *) physmap_map[0].map_priv_1); for( i = 0; i < NUMBANKS; i++){ physmap_map[i].map_priv_1 = 0; } } } module_init(init_physmap); module_exit(cleanup_physmap);