/****************************************************************************** ** ** FILE NAME : amazon_s_mtd_map.c ** PROJECT : Amazon_S ** MODULES : NOR Flash ** ** DESCRIPTION : NOR Flash MTD Driver (Mapping) ** COPYRIGHT : Copyright (c) 2006 ** Infineon Technologies AG ** Am Campeon 1-12, 85579 Neubiberg, Germany ** ** This program is free software; you can redistribute it and/or modify ** it under the terms of the GNU General Public License as published by ** the Free Software Foundation; either version 2 of the License, or ** (at your option) any later version. ** ** HISTORY ** $Version $Date $Author $Comment ** 1.0 19/07/07 Teh Kok How First version for Danube ** 1.1 07/01/08 Wu Qi Ming Modified for Amazon_S project ** 1.2 17/04/08 Lei Chuanhua Clean up source code, and fix kernel 2.6 ** unlock address generation *******************************************************************************/ #ifndef AUTOCONF_INCLUDED #include #endif /* AUTOCONF_INCLUDED */ #include #include #include #include #include #include #include #include #include #include #include #ifdef CONFIG_MTD_PARTITIONS #define __AMAZON_S_MTD_MAP__ #include #endif /* CONFIG_MTD_PARTITIONS */ #define NUM_FLASH_BANKS 1 #define CONFIG_MTD_DATAWIDTH 2 #define IFX_MTD_DRV_VERSION "1.2.0" #ifdef EBU_PCI_SOFTWARE_ARBITOR #define AMAZON_S_PCI_REG32( addr ) (*(volatile u32 *)(addr)) #endif //#define DEBUG_AMAZON_S_MTD #ifdef DEBUG_AMAZON_S_MTD #define AMAZON_S_MTD_DMSG(fmt, args...) \ printk( KERN_ERR "[%s %d]: " fmt,__FUNCTION__, __LINE__, ## args) #else #define AMAZON_S_MTD_DMSG(fmt, args...) #endif #define AMAZON_S_MTD_EMSG(fmt, args...) \ printk( KERN_ERR "%s: " fmt,__FUNCTION__, ## args) /* trivial struct to describe partition information */ struct mtd_part_def { int nums; unsigned char *type; struct mtd_partition *mtd_part; }; /* static struct mtd_info *mymtd; */ static struct mtd_info *mtd_banks[NUM_FLASH_BANKS]; static struct map_info *map_banks[NUM_FLASH_BANKS]; static struct mtd_part_def part_banks[NUM_FLASH_BANKS]; static unsigned long num_banks; static unsigned long start_scan_addr; static int probing = 0; #define AMAZON_S_MTD_REG32( addr ) (*(volatile u32 *)(addr)) static void inline enable_ebu (void) { #ifdef EBU_PCI_SOFTWARE_ARBITOR u32 temp_buffer; /* Delay before enabling ebu */ for (temp_buffer = 0; temp_buffer < 9999; temp_buffer++); /* disable int/ext pci request */ temp_buffer = AMAZON_S_PCI_REG32 (PCI_CR_PC_ARB_REG); temp_buffer &= ~0x0C00; AMAZON_S_PCI_REG32 (PCI_CR_PC_ARB_REG) = temp_buffer; temp_buffer |= 0x3300; AMAZON_S_PCI_REG32 (PCI_CR_PC_ARB_REG) = temp_buffer; /* poll for pci bus idle */ temp_buffer = AMAZON_S_PCI_REG32 (PCI_CR_PC_ARB_REG); while ((temp_buffer & 0x300000) != 0x300000) { temp_buffer = AMAZON_S_PCI_REG32 (PCI_CR_PC_ARB_REG); }; AMAZON_S_PCI_REG32 (0xBE105400) = 0; /* unmask CFRAME_MASK */ /* changing PCI's Config Space via internal path */ /* might need to change to external path */ AMAZON_S_PCI_REG32 (0xBE105430) = 0x00000103; temp_buffer = AMAZON_S_PCI_REG32 (0xB700006C); temp_buffer &= (~0x00000001); AMAZON_S_PCI_REG32 (0xB700006C) = temp_buffer; AMAZON_S_PCI_REG32 (0xBE105430) = 0x01000103; temp_buffer = AMAZON_S_PCI_REG32 (0xB700006C); #endif } static void inline disable_ebu (void) { #ifdef EBU_PCI_SOFTWARE_ARBITOR u32 temp_buffer; //Delay before enabling ebu for (temp_buffer = 0; temp_buffer < 9999; temp_buffer++); AMAZON_S_PCI_REG32 (0xBE105400) = 0x2; /* unmask CFRAME_MASK */ /* changing PCI's Config Space via internal path */ /* might need to change to external path */ AMAZON_S_PCI_REG32 (0xbE105430) = 0x00000103; temp_buffer = AMAZON_S_PCI_REG32 (0xB700006C); temp_buffer |= 0x00000001; AMAZON_S_PCI_REG32 (0xB700006C) = temp_buffer; AMAZON_S_PCI_REG32 (0xbE105430) = 0x01000103; /* enable int/ext pci request */ temp_buffer = AMAZON_S_PCI_REG32 (PCI_CR_PC_ARB_REG); temp_buffer &= (~0x3300); temp_buffer |= 0x0C00; AMAZON_S_PCI_REG32 (PCI_CR_PC_ARB_REG) = temp_buffer; #endif } #if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,0) static __u8 amazon_s_read8 (struct map_info *map, unsigned long ofs) { u8 temp; enable_ebu (); temp = *((__u8 *) (map->map_priv_1 + ofs)); disable_ebu (); AMAZON_S_MTD_DMSG ("8: [%p + %x] ==> %x \n", map->map_priv_1, ofs, temp); return temp; } static __u16 amazon_s_read16 (struct map_info * map, unsigned long ofs) { u16 temp; /** * Modify the address offset to account the EBU address swap. * Needed only for probing. */ if (probing) ofs ^= 2; enable_ebu (); temp = *((__u16 *) (map->map_priv_1 + ofs)); disable_ebu (); AMAZON_S_MTD_DMSG ("16: [%p + %x] ==> %x \n", map->map_priv_1, ofs, temp); return temp; } static __u32 amazon_s_read32 (struct map_info * map, unsigned long ofs) { u32 temp; enable_ebu (); temp = *((__u32 *) (map->map_priv_1 + ofs)); disable_ebu (); AMAZON_S_MTD_DMSG ("32: [%p + %x] ==> %x \n", map->map_priv_1, ofs, temp); return temp; } static void amazon_s_write8 (struct map_info *map, __u8 d, unsigned long adr) { enable_ebu (); *((__u8 *) (map->map_priv_1 + adr)) = d; disable_ebu (); AMAZON_S_MTD_DMSG ("8: [%p + %x] <== %x \n", map->map_priv_1, adr, d); } static void amazon_s_write16 (struct map_info *map, __u16 d, unsigned long adr) { /** * Modify the address offset to account the EBU address swap. * Needed only for probing. */ if (probing) adr ^= 2; enable_ebu (); *((__u16 *) (map->map_priv_1 + adr)) = d; disable_ebu (); AMAZON_S_MTD_DMSG ("16: [%p + %x] <== %x \n", map->map_priv_1, adr, d); } static void amazon_s_write32 (struct map_info *map, __u32 d, unsigned long adr) { enable_ebu (); *((__u32 *) (map->map_priv_1 + adr)) = d; disable_ebu (); AMAZON_S_MTD_DMSG ("32: [%p + %x] <== %x \n", map->map_priv_1, adr, d); } #else /* Kernel 2.6.x */ static map_word amazon_s_read(struct map_info * map, unsigned long ofs) { map_word temp; if ( map_bankwidth_is_1(map) ){ enable_ebu(); temp.x[0] = *(__u8 *)(map->map_priv_1 + ofs); disable_ebu(); AMAZON_S_MTD_DMSG ("map_bankwidth_is_1 r8: [0x%08x + 0x%02x] ==> 0x%02x\n", (unsigned int)map->map_priv_1, (unsigned int)ofs, (unsigned int)temp.x[0]); } else if ( map_bankwidth_is_2(map) ){ /* WAR, kernel 2.4 and kernel 2.6 uses the hardcode address in probing */ if ( probing ) ofs ^= 2; enable_ebu(); temp.x[0] = *(__u16 *)(map->map_priv_1 + ofs); disable_ebu(); AMAZON_S_MTD_DMSG ("map_bankwidth_is_2 r16: [0x%08x + %x] ==> %x\n", (unsigned int)map->map_priv_1, ofs, (__u16)temp.x[0]); } else if ( map_bankwidth_is_4(map) ) { enable_ebu(); temp.x[0] = *(__u32 *)(map->map_priv_1 + ofs); disable_ebu(); AMAZON_S_MTD_DMSG ("map_bankwidth_is_4 r32: [0x%08x + 0x%02x] ==> 0x%08x\n", (unsigned int)map->map_priv_1, (unsigned int)ofs, (unsigned int)temp.x[0]); } else if ( map_bankwidth_is_large(map) ) { u8 *p = (u8 *)(map->map_priv_1 + ofs); u8 *d = (u8 *)temp.x; int i; AMAZON_S_MTD_DMSG("map_bankwidth_is_large\n"); enable_ebu(); for ( i = 0; i < map->bankwidth; i++ ) *d++ = *p++; disable_ebu(); } return temp; } static void amazon_s_copy_from (struct map_info *map, void *to, unsigned long from, ssize_t len) { #if defined(CONFIG_AMAZON_S) || defined(CONFIG_MIPS_AMAZON_S) /* WAR, EBU access that causes unaligned access exception */ u8 *p; u8 *q; AMAZON_S_MTD_DMSG ("from:%lux to:%p len:%d\n", from, to, len); enable_ebu (); from = (unsigned long) (from + map->map_priv_1); if ((((unsigned long) to) & 3) == (from & 3)) { memcpy_fromio (to, (void *) from, len); } else { p = (u8 *) (from); q = (u8 *) (to); while (len--) { *q++ = *p++; } } disable_ebu (); #else AMAZON_S_MTD_DMSG ("from:%lux to:%p len:%d\n", from, to, len); memcpy_fromio (to, (void *) (map->map_priv_1 + from), len); #endif } static void amazon_s_write(struct map_info *map, const map_word datum, unsigned long ofs) { if ( map_bankwidth_is_1(map) ) { enable_ebu(); *(__u8 *)(map->map_priv_1 + ofs) = (__u8)datum.x[0]; disable_ebu(); AMAZON_S_MTD_DMSG ("map_bankwidth_is_1 w8: [0x%08x + 0x%02x] <== 0x%02x\n", (unsigned int) map->map_priv_1, (unsigned int)ofs, (unsigned int)datum.x[0]); } else if ( map_bankwidth_is_2(map) ) { /* WAR, kernel 2.4 and kernel 2.6 uses the hardcode address in probing */ if ( probing ) ofs ^= 2; enable_ebu(); *( __u16 *)(map->map_priv_1 + ofs) = (__u16)datum.x[0]; wmb(); disable_ebu(); AMAZON_S_MTD_DMSG ("map_bankwidth_is_2 w16: [0x%08x + %x] <== %x\n", (unsigned int)map->map_priv_1, ofs, (__u16)datum.x[0]); } else if ( map_bankwidth_is_4(map) ){ enable_ebu(); *(__u32 *)(map->map_priv_1 + ofs) = datum.x[0]; disable_ebu(); AMAZON_S_MTD_DMSG ("map_bankwidth_is_4 w32: [0x%08x + 0x%02x] <== 0x%08x\n", (unsigned int)map->map_priv_1, (unsigned int)ofs, (unsigned int)datum.x[0]); } else if ( map_bankwidth_is_large(map) ) { u8 *p = (u8 *)datum.x; u8 *d = (u8 *)(map->map_priv_1 + ofs); int i; AMAZON_S_MTD_DMSG ("map_bankwidth_is_large\n"); enable_ebu(); for ( i = 0; i < map->bankwidth; i++ ) *d++ = *p++; disable_ebu(); } } static void amazon_s_copy_to (struct map_info *map, unsigned long to, const void *from, ssize_t len) { AMAZON_S_MTD_DMSG ("from:%x to:%x len:%d", (unsigned int)from, (unsigned int)to, len); enable_ebu (); memcpy_toio ((void *) (map->map_priv_1 + to), from, len); disable_ebu (); } #endif /* LINUX_VERSION_CODE < KERNEL_VERSION(2,6,0) */ struct map_info amazon_s_map = { .name = "Amazon_S Flash Bank 0", #if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,0) .read8 = amazon_s_read8, .read16 = amazon_s_read16, .read32 = amazon_s_read32, .copy_from = amazon_s_copy_from, .write8 = amazon_s_write8, .write16 = amazon_s_write16, .write32 = amazon_s_write32, .copy_to = amazon_s_copy_to #else .read = amazon_s_read, .copy_from = amazon_s_copy_from, .write = amazon_s_write, .copy_to = amazon_s_copy_to #endif /* LINUX_VERSION_CODE < KERNEL_VERSION(2,6,0) */ }; #define NB_OF(x) (sizeof(x)/sizeof(x[0])) /* These two global variable will be initialize by prom_init() in prom.c */ unsigned long flash_start = -1, flash_size; static inline void ifx_mtd_show_version(void) { printk("Infineon Technologies NOR flash MTD map driver version %s\n", IFX_MTD_DRV_VERSION ); } static int __init init_amazon_s_mtd (void) { int idx = 0, ret = 0; unsigned long mtd_size = 0; if (!flash_size) panic("[%s %s %d]: flash_size = 0 passed from U-Boot!!!\n", __FILE__, __func__, __LINE__); if (flash_start == -1) panic("[%s %s %d]: flash_start = -1 passed from U-Boot!!!\n", __FILE__, __func__, __LINE__); /* Configure EBU */ #ifdef CONFIG_MTD_AMAZON_S_LESS_WAIT_CYCLE if ( (*AMAZON_S_CGU_SYS & 0x18) == 0x10 ) *AMAZON_S_EBU_BUSCON0 = 0x1d5aa; // 393/196MHz else *AMAZON_S_EBU_BUSCON0 = 0x1d7fd; // 333/166MHz #else *AMAZON_S_EBU_BUSCON0 = 0x1d7ff; #endif amazon_s_map.bankwidth = CONFIG_MTD_DATAWIDTH; amazon_s_map.size = flash_size; /* Get flash physical address first, then map it to virtual address */ flash_start &= (~KSEG1); start_scan_addr = (unsigned long) ioremap (flash_start, flash_size); if (!start_scan_addr) { AMAZON_S_MTD_EMSG ("Failed to ioremap address:0x%lux\n", flash_start); return -EIO; } amazon_s_map.virt = (void __iomem *)start_scan_addr; AMAZON_S_MTD_DMSG ("start_scan_addr: 0x%lux, flash_start: 0x%lux flash_size %#x\n", start_scan_addr, flash_start, flash_size); amazon_s_map.map_priv_1 = (unsigned long)amazon_s_map.virt; for (idx = 0; idx < NUM_FLASH_BANKS; idx++) { if (mtd_size >= flash_size) break; printk ("%s: chip probing count %d\n", __FUNCTION__, idx); map_banks[idx] = (struct map_info *) kmalloc (sizeof (struct map_info), GFP_KERNEL); if (map_banks[idx] == NULL) { ret = -ENOMEM; goto error_mem; } memset ((void *) map_banks[idx], 0, sizeof (struct map_info)); map_banks[idx]->name = (char *) kmalloc (16, GFP_KERNEL); if (map_banks[idx]->name == NULL) { ret = -ENOMEM; goto error_mem; } memset ((void *) map_banks[idx]->name, 0, 16); sprintf ((char *)map_banks[idx]->name, "Amazon_S Bank %d", idx); map_banks[idx]->bankwidth = CONFIG_MTD_DATAWIDTH; #if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,0) map_banks[idx]->read8 = amazon_s_read8; map_banks[idx]->read16 = amazon_s_read16; map_banks[idx]->read32 = amazon_s_read32; map_banks[idx]->copy_from = amazon_s_copy_from; map_banks[idx]->write8 = amazon_s_write8; map_banks[idx]->write16 = amazon_s_write16; map_banks[idx]->write32 = amazon_s_write32; map_banks[idx]->copy_to = amazon_s_copy_to; #else map_banks[idx]->read = amazon_s_read; map_banks[idx]->copy_from = amazon_s_copy_from; map_banks[idx]->write = amazon_s_write; map_banks[idx]->copy_to = amazon_s_copy_to; #endif map_banks[idx]->map_priv_1 = start_scan_addr; map_banks[idx]->virt = (void __iomem *)start_scan_addr; /* make sure probing works */ map_banks[idx]->size = flash_size; /* start to probe flash chips */ probing = 1; mtd_banks[idx] = do_map_probe ("cfi_probe", map_banks[idx]); probing = 0; if (mtd_banks[idx]) { struct cfi_private *cfi; #if LINUX_VERSION_CODE <= KERNEL_VERSION(2,5,41) mtd_banks[idx]->module = THIS_MODULE; #else mtd_banks[idx]->owner = THIS_MODULE; #endif mtd_size += mtd_banks[idx]->size; num_banks++; cfi = (struct cfi_private *) map_banks[idx]->fldrv_priv; /* Swap the address for correct access via EBU * Kernel 2.6 has different unlock address geneartion from kernel 2.4.x */ #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0) /* 0x555, 0x2aa */ cfi->addr_unlock1 ^= 1; cfi->addr_unlock2 ^= 1; #else /* 0xaaa, 0x554 */ cfi->addr_unlock1 ^= 2; cfi->addr_unlock2 ^= 2; #endif printk ("%s: bank%ld, name:%s, size:%dB\n", __func__, num_banks, mtd_banks[idx]->name, mtd_banks[idx]->size); } } /* no supported flash chips found */ if (!num_banks) { printk ("Amazon_S: No support flash chips found!\n"); ret = -ENXIO; goto error_mem; } #ifdef CONFIG_MTD_PARTITIONS /* * Select Static partition definitions */ part_banks[0].mtd_part = amazon_s_partitions; part_banks[0].type = "static image"; part_banks[0].nums = NB_OF (amazon_s_partitions); for (idx = 0; idx < num_banks; idx++) { #ifdef CONFIG_MTD_CMDLINE_PARTS int n; char mtdid[16]; sprintf (mtdid, "%d", idx); n = parse_cmdline_partitions (mtd_banks[idx], &part_banks[idx].mtd_part, mtdid); AMAZON_S_MTD_DMSG ("%d command line partitions on bank %s\n", n, mtdid); if (n > 0) { part_banks[idx].type = "command line"; part_banks[idx].nums = n; } #endif if (part_banks[idx].nums == 0) { printk (KERN_NOTICE "AMAZON_S flash%d: " "no partition info available, " "registering whole flash at once\n", idx); add_mtd_device (mtd_banks[idx]); } else { printk (KERN_NOTICE "AMAZON_S flash%d: " "Using %s partition definition\n", idx, part_banks[idx].type); add_mtd_partitions (mtd_banks[idx], part_banks[idx].mtd_part, part_banks[idx].nums); } } #else printk (KERN_NOTICE "AMAZON_S flash: registering %d whole " "flash banks at once\n", num_banks); for (idx = 0; idx < num_banks; idx++) add_mtd_device (mtd_banks[idx]); #endif /* CONFIG_MTD_CMDLINE_PARTS */ ifx_mtd_show_version(); return 0; error_mem: for (idx = 0; idx < NUM_FLASH_BANKS; idx++) { if (map_banks[idx] != NULL) { if (map_banks[idx]->name != NULL) { kfree (map_banks[idx]->name); map_banks[idx]->name = NULL; } kfree (map_banks[idx]); map_banks[idx] = NULL; } } iounmap ((void *) start_scan_addr); return ret; } static void __exit cleanup_amazon_s_mtd (void) { unsigned int idx = 0; for (idx = 0; idx < num_banks; idx++) { /* destroy mtd_info previously allocated */ if (mtd_banks[idx]) { del_mtd_partitions (mtd_banks[idx]); map_destroy (mtd_banks[idx]); } /* release map_info not used anymore */ kfree (map_banks[idx]->name); kfree (map_banks[idx]); } if (start_scan_addr) { iounmap ((void *) start_scan_addr); start_scan_addr = 0; } } module_init (init_amazon_s_mtd); module_exit (cleanup_amazon_s_mtd); MODULE_LICENSE ("GPL"); MODULE_DESCRIPTION ("MTD map driver for AMAZON_S boards");