/****************************************************************************** ** ** FILE NAME : ifxmips_avm_sflash.c ** PROJECT : IFX UEIP ** MODULES : 25 types of Serial Flash ** ** DATE : 03 July 2009 ** AUTHOR : Lei Chuanhua ** DESCRIPTION : SPI Flash MTD Driver ** COPYRIGHT : Copyright (c) 2009 ** 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 ** $Date $Author $Comment ** 03,July 2009 Lei Chuanhua Initial UEIP release ** 14,July 2015 H.Schillert Initial AVM release *******************************************************************************/ #ifndef EXPORT_SYMTAB #define EXPORT_SYMTAB #endif #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include /* Project header */ #include #include #include #include #include #include "ifxmips_sflash.h" #include #include #include "ifxmips_ssc.h" #ifdef CONFIG_IFX_PMCU #include "ifxmips_ssc_pm.h" #endif /* CONFIG_IFX_PMCU */ #include #if defined(CONFIG_TFFS_DEV_MTDNOR) || defined(CONFIG_TFFS_DEV_LEGACY) #include #include #include #include "ifxmips_ssc_reg.h" static struct mtd_info *panic_reinit(struct mtd_info *mtd); #endif #define IFX_SFLASH_VER_MAJOR 2 #define IFX_SFLASH_VER_MID 0 #define IFX_SFLASH_VER_MINOR 2 #define IFX_SFLASH_NAME "ifx_sflash" #define IFX_SFLASH_ADDR_CYCLES 3 /* 24bit address */ /*--- #define IFX_SPI_FLASH_DBG ---*/ #define IFX_SPI_VERIFY #if defined(IFX_SPI_FLASH_DBG) #define IFX_SFLASH_PRINT(format, arg...) printk(KERN_ERR "%s: " format, __func__, ##arg) #define INLINE #else #define IFX_SFLASH_PRINT(format, arg...) pr_debug("%s: " format, __func__, ##arg) #define INLINE inline #endif DEFINE_SEMAPHORE(ifx_sflash_sem); /*--- spinlock_t ssc_irq_lock; ---*/ static ifx_spi_dev_t *spi_sflash = NULL; /* * NOTE: double check command sets and memory organization when you add * more flash chips. This current list focusses on newer chips, which * have been converging on command sets which including JEDEC ID. */ static struct ifx_sflash_manufacturer_info flash_manufacturers[] = { { /* Spansion -- single (large) sector size only, at least * for the chips listed here (without boot sectors). */ .name = "Spansion", .id = JED_MANU_SPANSION, .flashes = { { "S25Sl004", 0x0212, 64 * 1024, 8, 0, }, { "S25Sl008", 0x0213, 64 * 1024, 16, 0, }, { "S25LF016", 0x0214, 64 * 1024, 32, 0, }, { "S25LF032", 0x0215, 64 * 1024, 64, 0, }, { "S25LF064", 0x0216, 64 * 1024, 128, 0, }, { "", 0x0, 0, 0, 0,}, { "S25LF0128", 0x0218, 256 * 1024, 64, 0, }, { "", 0, 0, 0, 0, }, { "", 0, 0, 0, 0, }, { "", 0, 0, 0, 0, }, { "", 0, 0, 0, 0, }, { "", 0, 0, 0, 0, }, }, }, { /* ST Microelectronics -- newer production may have feature updates */ .name = "ST", .id = JED_MANU_ST, .flashes = { { "m25p05", 0x2010, 32 * 1024, 2, 0, }, { "m25p10", 0x2011, 32 * 1024, 4, 0, }, { "m25p20", 0x2012, 64 * 1024, 4, 0, }, { "m25p40", 0x2013, 64 * 1024, 8, 0, }, { "m25p16", 0x2015, 64 * 1024, 32, 0, }, { "m25p32", 0x2016, 64 * 1024, 64, 0, }, { "m25p64", 0x2017, 64 * 1024, 128, 0, }, { "m25p128", 0x2018, 256 * 1024, 64, 0, }, { "m45pe80", 0x4014, 64 * 1024, 16, 0, }, { "m45pe16", 0x4015, 64 * 1024, 32, 0, }, { "m25pe80", 0x8014, 64 * 1024, 16, 0, }, { "m25pe16", 0x8015, 64 * 1024, 32, SECT_4K, }, }, }, { /* SST -- large erase sizes are "overlays", "sectors" are 4K */ .name = "SST", .id = JED_MANU_SST, .flashes = { { "sst25vf040b", 0x258d, 64 * 1024, 8, SECT_4K, }, { "sst25vf080b", 0x258e, 64 * 1024, 16, SECT_4K, }, { "sst25vf016b", 0x2541, 64 * 1024, 32, SECT_4K, }, { "sst25vf032b", 0x254a, 64 * 1024, 64, SECT_4K, }, { "", 0, 0, 0, 0, }, { "", 0, 0, 0, 0, }, { "", 0, 0, 0, 0, }, { "", 0, 0, 0, 0, }, { "", 0, 0, 0, 0, }, { "", 0, 0, 0, 0, }, { "", 0, 0, 0, 0, }, { "", 0, 0, 0, 0, }, }, }, { .name = "Atmel", .id = JED_MANU_ATMEL, .flashes = { { "at25fs010", 0x6601, 32 * 1024, 4, SECT_4K, }, { "at25fs040", 0x6604, 64 * 1024, 8, SECT_4K, }, { "at25df041a", 0x4401, 64 * 1024, 8, SECT_4K, }, { "at25df641", 0x4800, 64 * 1024, 128, SECT_4K, }, { "at26f004", 0x0400, 64 * 1024, 8, SECT_4K, }, { "at26df081a", 0x4501, 64 * 1024, 16, SECT_4K, }, { "at26df161a", 0x4601, 64 * 1024, 32, SECT_4K, }, { "at26df321", 0x4701, 64 * 1024, 64, SECT_4K, }, { "", 0, 0, 0, 0, }, { "", 0, 0, 0, 0, }, { "", 0, 0, 0, 0, }, { "", 0, 0, 0, 0, }, }, }, { /* Winbond -- w25x "blocks" are 64K, "sectors" are 4KiB */ .name = "Winbond", .id = JED_MANU_WINBOND, .flashes = { { "w25x10", 0x4014, 8 * 1024 * 1024, 256, SECT_4K, }, { "w25x10", 0x3011, 64 * 1024, 2, SECT_4K, }, { "w25x20", 0x3012, 64 * 1024, 4, SECT_4K, }, { "w25x40", 0x3013, 64 * 1024, 8, SECT_4K, }, { "w25x80", 0x3014, 64 * 1024, 16, SECT_4K, }, { "w25x16", 0x3015, 64 * 1024, 32, SECT_4K, }, { "w25x32", 0x3016, 64 * 1024, 64, SECT_4K, }, { "w25x64", 0x3017, 64 * 1024, 128, SECT_4K, }, { "W25P80", 0x2014, 256 * 256, 16, 0, }, { "W25P16", 0x2015, 256 * 256, 32, 0, }, { "W25P32", 0x2016, 256 * 256, 64, 0, }, { "W25P64", 0x2017, 256 * 256, 128, 0, }, }, }, { .name = "MX", .id = JED_MANU_MX, .flashes = { { "MX25P2005", 0x2012, 16 * 256, 64, 0, }, { "MX25P4005", 0x2013, 16 * 256, 128, 0, }, { "MX25P8005", 0x2014, 16 * 256, 256, SECT_4K, }, { "MX25P1605", 0x2015, 256 * 256, 32, 0, }, { "MX25P3205", 0x2016, 256 * 256, 64, 0, }, { "MX25P6405", 0x2017, 256 * 256, 128, 0, }, { "MX25V8035", 0x2314, 16 * 256, 256, SECT_4K, }, { "", 0, 0, 0, 0, }, { "", 0, 0, 0, 0, }, { "", 0, 0, 0, 0, }, { "", 0, 0, 0, 0, }, { "", 0, 0, 0, 0, }, }, }, }; #ifdef CONFIG_MTD_CMDLINE_PARTS static const char *part_probes[] = { "cmdlinepart", NULL }; #endif #if 0 /*--- #ifdef IFX_SPI_FLASH_DBG ---*/ static void flash_dump(const char *title, const u8 *buf, size_t len) { unsigned int i, llen, lenlab = 0; const u8 *pos = buf; const unsigned int line_len = 16; printk("%s - hex_ascii(len=%lu):\n", title, (unsigned long) len); while (len) { llen = len > line_len ? line_len : len; printk("%08x: ", lenlab); for (i = 0; i < llen; i++) printk(" %02x", pos[i]); for (i = llen; i < line_len; i++) printk(" "); printk(" "); for (i = 0; i < llen; i++) { if (isprint(pos[i])) printk("%c", pos[i]); else printk("."); } for (i = llen; i < line_len; i++) printk(" "); printk("\n"); pos += llen; len -= llen; lenlab += line_len; } } #endif /* IFX_SPI_FLASH_DBG */ /*------------------------------------------------------------------------------------------*\ \*------------------------------------------------------------------------------------------*/ static void ifx_spi_setcs(enum spi_cs cs, enum spi_cs_enum set) { struct _spi_register *const SPI = (struct _spi_register *)&(*(volatile unsigned int *)(IFX_SSC_PHY_BASE | KSEG1)); udelay(1); switch(set){ case deselect: /*--- set CS high ---*/ SPI->FGPO = (1 << cs) << 8; break; case select: /*--- set CS low ---*/ SPI->FGPO = (1 << cs); break; } smp_wmb(); udelay(1); } /*--------------------------------------------------------------------------------*\ \*--------------------------------------------------------------------------------*/ static inline int ifx_spi_busy_wait(void) { struct _spi_register *const SPI = (struct _spi_register *)&(*(volatile unsigned int *)(IFX_SSC_PHY_BASE | KSEG1)); unsigned int usec = 1000; while(SPI->STAT.Bits.bsy && --usec){ udelay(1); } return SPI->STAT.Bits.bsy ? 1 : 0; } /*--------------------------------------------------------------------------------*\ \*--------------------------------------------------------------------------------*/ static inline unsigned int ifx_spi_read_wait(void) { struct _spi_register *const SPI = (struct _spi_register *)&(*(volatile unsigned int *)(IFX_SSC_PHY_BASE | KSEG1)); volatile unsigned int usec = 1000; while((SPI->FSTAT.Bits.rxffl < 1) && --usec){ udelay(1); } return SPI->FSTAT.Bits.rxffl ? 0 : 1; } /*------------------------------------------------------------------------------------------*\ * es muss RX & TX eingeschaltet sein, damit ifx_spi_cmd_simple funktioniert \*------------------------------------------------------------------------------------------*/ static uint8_t ifx_spi_cmd_simple(unsigned int cmd) { struct _spi_register *const SPI = (struct _spi_register *)&(*(volatile unsigned int *)(IFX_SSC_PHY_BASE | KSEG1)); uint8_t rxoff, txoff, tmp; tmp = 0xff; rxoff = SPI->CON.Bits.rxoff; txoff = SPI->CON.Bits.txoff; SPI->CON.Bits.rxoff = 0; SPI->CON.Bits.txoff = 0; ifx_spi_setcs(IFX_SSC_CSx, select); SPI->TB.Char.Byte = cmd; if(ifx_spi_read_wait()){ printk(KERN_ERR"%s error on ifx_spi_read_wait\n", __func__); goto err_out; } tmp = SPI->RB & 0xFF; err_out: ifx_spi_setcs(IFX_SSC_CSx, deselect); SPI->CON.Bits.rxoff = rxoff; SPI->CON.Bits.txoff = txoff; return tmp; } /*------------------------------------------------------------------------------------------*\ \*------------------------------------------------------------------------------------------*/ static unsigned char ifx_spi_read_status(enum spi_cs cs) { struct _spi_register *const SPI = (struct _spi_register *)&(*(volatile unsigned int *)(IFX_SSC_PHY_BASE | KSEG1)); uint8_t rxoff, txoff, tmp; tmp = 0xff; rxoff = SPI->CON.Bits.rxoff; txoff = SPI->CON.Bits.txoff; SPI->CON.Bits.rxoff = 0; SPI->CON.Bits.txoff = 0; ifx_spi_setcs(cs, select); SPI->TB.Short.Bytes = IFX_OPCODE_RDSR << 8; if(ifx_spi_read_wait()){ goto err_out; } tmp = SPI->RB & 0xFF; err_out: ifx_spi_setcs(cs, deselect); SPI->CON.Bits.rxoff = rxoff; SPI->CON.Bits.txoff = txoff; return tmp; } /*------------------------------------------------------------------------------------------*\ \*------------------------------------------------------------------------------------------*/ static INLINE int ifx_spi_sector_erase(ifx_spi_dev_t *spi_flash, uint32_t saddr) { struct _spi_register *const SPI = (struct _spi_register *)&(*(volatile unsigned int *)(IFX_SSC_PHY_BASE | KSEG1)); unsigned char status; int result = 0; ifx_spi_cmd_simple(IFX_OPCODE_WREN); /*--- WRITE_ENABLE muss nicht gelöscht werden ---*/ pr_debug("[%s] 0x%x\n", __func__, saddr); SPI->CON.Bits.rxoff = 1; ifx_spi_setcs(IFX_SSC_CSx, select); SPI->TB.Register = (spi_flash->erase_opcode << 24) + saddr; if(ifx_spi_busy_wait()) { pr_err("%s: error SPI busy %d\n", __func__, __LINE__); result = -EBUSY; goto err_out; } ifx_spi_setcs(IFX_SSC_CSx, deselect); SPI->CON.Bits.rxoff = 0; while (1) { status = ifx_spi_read_status(IFX_SSC_CSx); if ( ! (status & (WIP | WEL))) break; } return 0; err_out: ifx_spi_setcs(IFX_SSC_CSx, deselect); SPI->CON.Bits.rxoff = 0; return result; } /*------------------------------------------------------------------------------------------*\ \*------------------------------------------------------------------------------------------*/ static int ifx_spi_do_write(loff_t addr, size_t len, const uint8_t *buffer) { struct _spi_register *const SPI = (struct _spi_register *)&(*(volatile unsigned int *)(IFX_SSC_PHY_BASE | KSEG1)); uint8_t pagebuf[IFX_SFLASH_PAGESIZE + sizeof(uint32_t)], *pbuf; int result = 0; unsigned char status = 0; volatile unsigned int cnt; /*--- unsigned long flags; ---*/ pbuf = &(pagebuf[0]); pbuf = PTR_ALIGN(pbuf, sizeof(uint32_t)); memcpy(pbuf, buffer, len); /*--- spin_lock_irqsave(&ssc_irq_lock, flags); ---*/ SPI->CON.Bits.rxoff = 1; ifx_spi_cmd_simple(IFX_OPCODE_WREN); ifx_spi_setcs(IFX_SSC_CSx, select); SPI->TB.Register = (IFX_OPCODE_PP << 24) | addr; if(ifx_spi_busy_wait()){ pr_err("%s: error SPI busy %d\n", __func__, __LINE__); result = -ETIMEDOUT; goto err_out; } while(len >= sizeof(uint32_t)){ SPI->TB.Register = *(uint32_t *) pbuf; if(ifx_spi_busy_wait()){ pr_err("%s: error SPI busy %d\n", __func__, __LINE__); result = -EIO; goto err_out; } pbuf += sizeof(uint32_t); len -= sizeof(uint32_t); } if(len >= sizeof(uint16_t)){ SPI->TB.Short.Bytes = *(uint16_t *) pbuf; if(ifx_spi_busy_wait()){ pr_err("%s: error SPI busy %d\n", __func__, __LINE__); result = -EIO; goto err_out; } pbuf += sizeof(uint16_t); len -= sizeof(uint16_t); } if(len > 0){ SPI->TB.Char.Byte = *(uint8_t *) pbuf; if(ifx_spi_busy_wait()){ pr_err("%s: error SPI busy %d\n", __func__, __LINE__); result = -EIO; goto err_out; } } ifx_spi_setcs(IFX_SSC_CSx, deselect); SPI->CON.Bits.rxoff = 0; cnt = 10000; /*--- the timeout has to consider Tpp of the spi-Flash ---*/ do { if(--cnt == 0){ pr_err("[%s:%d] error on status %x chip_addr=%llx datalen=%u\n", __func__, __LINE__, status, addr, len); result = -EIO; goto err_out; } udelay(1); status = ifx_spi_read_status(IFX_SSC_CSx); } while( status & (WIP | WEL) ); err_out: ifx_spi_setcs(IFX_SSC_CSx, deselect); SPI->CON.Bits.rxoff = 0; SPI->CON.Bits.txoff = 0; /*--- spin_unlock_irqrestore(&ssc_irq_lock, flags); ---*/ return result; } static int ifx_spi_do_read(loff_t addr, size_t len, size_t *retlen, u_char *buf, int panic_mode); /*------------------------------------------------------------------------------------------*\ * \fn static int ifx_spi_flash_write (struct mtd_info *mtd, loff_t to, size_t len, size_t *retlen, const u_char *buf) * \brief Read from the serial flash device. * * \param[in] mtd Pointer to struct mtd_info * \param[in] to Start offset in flash device * \param[in] len Amount to write * \param[out] retlen Amount of data actually written * \param[out] buf Buffer containing the data * \return 0 No need to read actually or read successfully * \return -EINVAL invalid read length * \ingroup IFX_SFLASH_OS \*------------------------------------------------------------------------------------------*/ static int ifx_spi_flash_write (struct mtd_info *mtd, loff_t to, size_t len, size_t *retlen, const u_char *buf) { size_t total = 0, len_this_lp, bytes_this_page; unsigned int result; loff_t addr = 0; u8 *mem; IFX_SFLASH_PRINT("(to = 0x%.8x, len = %d)\n",(u32) to, len); if ( ! retlen) { return -EINVAL; } *retlen = 0; /* sanity check */ if (len == 0) { return 0; } if ((typeof(mtd->size))(to + len) > mtd->size) { return -EINVAL; } down(&ifx_sflash_sem); while(total < len) { mem = (u8 *)(buf + total); addr = to + total; bytes_this_page = IFX_SFLASH_PAGESIZE - (addr % IFX_SFLASH_PAGESIZE); len_this_lp = min((len - total), bytes_this_page); result = ifx_spi_do_write(addr, len_this_lp, mem); if (result) { *retlen = 0; pr_err("{%s} ERROR %d\n", __func__, result); up(&ifx_sflash_sem); return result; } total += len_this_lp; } *retlen = total; #if defined(IFX_SPI_VERIFY) { int read_result; size_t verifyread; unsigned char verify_buffer[IFX_SFLASH_PAGESIZE]; total = 0; while(total < len) { addr = to + total; bytes_this_page = IFX_SFLASH_PAGESIZE - (addr % IFX_SFLASH_PAGESIZE); len_this_lp = min((len - total), bytes_this_page); read_result = ifx_spi_do_read(addr, len_this_lp, &verifyread, verify_buffer, 0); if (read_result < 0) { pr_err("[%s] ERROR Verify read %d\n", __func__, read_result); } else { if (memcmp((u8 *)(buf + total), verify_buffer, verifyread)) { pr_err("[%s] ERROR Verify on 0x%llx\n", __func__, addr); up(&ifx_sflash_sem); return -EIO; } } total += verifyread; } } #endif up(&ifx_sflash_sem); return 0; } /*------------------------------------------------------------------------------------------*\ * im panic_mode darf kein printk etc. aufgerufen werden (deadlock-Gefahr wegen dvpe) \*------------------------------------------------------------------------------------------*/ static int ifx_spi_do_read(loff_t addr, size_t len, size_t *retlen, u_char *buf, int panic_mode) { struct _spi_register *const SPI = (struct _spi_register *)&(*(volatile unsigned int *)(IFX_SSC_PHY_BASE | KSEG1)); int result = 0; size_t chunk_len; uint32_t tmp_buf; /*--- unsigned long flags; ---*/ unsigned int count; /*--- pr_err("[%s] Called. addr: 0x%08llx len: 0x%08x\n", __func__, addr, len); ---*/ /*--- spin_lock_irqsave(&ssc_irq_lock, flags); ---*/ SPI->CON.Bits.rxoff = 1; SPI->CON.Bits.txoff = 0; ifx_spi_setcs(IFX_SSC_CSx, select); SPI->TB.Register = (IFX_OPCODE_NORM_READ << 24) | addr; if(ifx_spi_busy_wait()){ if(!panic_mode)pr_err("%s: error SPI busy %d rxcnt %d fifo 0x%x\n", __func__, __LINE__, SPI->RXCNT, SPI->FSTAT.Register); result = -EBUSY; goto err_out; } SPI->CON.Bits.txoff = 1; SPI->CON.Bits.rxoff = 0; *retlen = 0; while(len) { count = 10000; chunk_len = min(sizeof(uint32_t), len); while (SPI->RXCNT != 0); /*--- has to be 0 before next rx-request ! ---*/ SPI->RXREQ = chunk_len; smp_wmb(); while (SPI->FSTAT.Bits.rxffl < 1) { if ( ! count-- ) { if(!panic_mode)pr_err("%s: error SPI busy %d rxcnt %d fstat 0x%x\n", __func__, __LINE__, SPI->RXCNT, SPI->FSTAT.Register); result = -EBUSY; /*--- printk(KERN_ERR "%d %d %d %d %d\n", len, chunk_len, SPI->RXREQ, SPI->RXCNT, SPI->FSTAT.Bits.rxffl); ---*/ goto err_out; } } tmp_buf = SPI->RB; tmp_buf <<= (8 * (sizeof(uint32_t) - chunk_len)); memcpy(buf, &tmp_buf, chunk_len); len -= chunk_len; buf += chunk_len; *retlen += chunk_len; } err_out: ifx_spi_setcs(IFX_SSC_CSx, deselect); SPI->CON.Bits.rxoff = 0; SPI->CON.Bits.txoff = 0; /*--- spin_unlock_irqrestore(&ssc_irq_lock, flags); ---*/ return result; } /*------------------------------------------------------------------------------------------*\ * \fn static int ifx_spi_flash_read(struct mtd_info *mtd, loff_t from, size_t len, * size_t *retlen ,u_char *buf) * \brief Read from the serial flash device. * * \param[in] mtd Pointer to struct mtd_info * \param[in] from Start offset in flash device * \param[in] len Amount to read * \param[out] retlen About of data actually read * \param[out] buf Buffer containing the data * \return 0 No need to read actually or read successfully * \return -EINVAL invalid read length * \ingroup IFX_SFLASH_OS \*------------------------------------------------------------------------------------------*/ static int ifx_spi_flash_read(struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char *buf) { size_t total = 0; size_t len_this_lp, bytes_this_page, readlen; uint32_t addr; int result = 0; u8 *mem; /*--- IFX_SFLASH_PRINT("(from = 0x%.8x, len = %d)\n", (unsigned int)from, (unsigned int)len); ---*/ if (!len || from < 0) return 0; if ((typeof(mtd->size))(from + len) > mtd->size) return -EINVAL; down(&ifx_sflash_sem); /* Fragment support */ while (total < len) { mem = (u8 *)(buf + total); addr = from + total; bytes_this_page = IFX_SFLASH_PAGESIZE - (addr % IFX_SFLASH_PAGESIZE); len_this_lp = min((len - total), bytes_this_page); result = ifx_spi_do_read(addr, len_this_lp, &readlen, mem, 0); if (result) { *retlen = 0; up(&ifx_sflash_sem); return result; } total += readlen; } *retlen = total; up(&ifx_sflash_sem); return result; } /*------------------------------------------------------------------------------------------*\ * \fn static int ifx_spi_flash_erase(struct mtd_info *mtd,struct erase_info *instr) * \brief Erase pages of serial flash device. * * \param[in] mtd Pointer to struct mtd_info * \param[in] instr Pointer to struct erase_info * \return 0 OK * \return -EINVAL invalid erase size * \ingroup IFX_SFLASH_OS \*------------------------------------------------------------------------------------------*/ static int ifx_spi_flash_erase(struct mtd_info *mtd, struct erase_info *instr) { ifx_spi_dev_t *spi_flash = (ifx_spi_dev_t *)mtd->priv; uint32_t addr,len; uint32_t rem; IFX_SFLASH_PRINT("(addr = 0x%llx, len = %lld)\n", (long long)instr->addr, (long long)instr->len); if ((instr->addr + instr->len) > mtd->size) return (-EINVAL); div_u64_rem(instr->len, mtd->erasesize, &rem); if (rem) { return (-EINVAL); } addr = instr->addr; len = instr->len; down(&ifx_sflash_sem); /* REVISIT in some cases we could speed up erasing large regions * by using OPCODE_SE instead of OPCODE_BE_4K. We may have set up * to use "small sector erase", but that's not always optimal. */ while (len) { if (ifx_spi_sector_erase(spi_flash, addr) != 0) { instr->state = MTD_ERASE_FAILED; up(&ifx_sflash_sem); pr_err("[%s] ERROR Erase failed\n", __func__); return -EIO; } addr += mtd->erasesize; len -= mtd->erasesize; } up(&ifx_sflash_sem); /* Inform MTD subsystem that erase is complete */ instr->state = MTD_ERASE_DONE; mtd_erase_callback(instr); IFX_SFLASH_PRINT("return\n"); return 0; } /*------------------------------------------------------------------------------------------*\ \*------------------------------------------------------------------------------------------*/ static void ifx_spi_flash_sync(struct mtd_info *mtd __attribute__((unused))) { return; } /*------------------------------------------------------------------------------------------*\ \*------------------------------------------------------------------------------------------*/ static INLINE int ifx_spi_flash_size_to_index(u32 size) { int i; int index = IFX_FLASH_128KB; i = (size >> 17); /* 128 KB minimum */ if (i <= 1) { index = IFX_FLASH_128KB; } else if (i <= 2) { index = IFX_FLASH_256KB; } else if (i <= 4) { index = IFX_FLASH_512KB; } else if (i <= 8) { index = IFX_FLASH_1MB; } else if (i <= 16) { index = IFX_FLASH_2MB; } else if (i <= 32) { index = IFX_FLASH_4MB; } else if (i <= 64) { index = IFX_FLASH_8MB; } else if (i <= 128) { index = IFX_FLASH_16MB; } else { index = IFX_SPI_MAX_FLASH; } return index; } static INLINE void ifx_spi_flash_version(void) { char ver_str[128]; ifx_driver_version(ver_str, sizeof(ver_str), "SPI flash", IFX_SFLASH_VER_MAJOR, IFX_SFLASH_VER_MID, IFX_SFLASH_VER_MINOR); pr_info("%s", ver_str); } /*------------------------------------------------------------------------------------------*\ * \fn static int ifx_spi_flash_probe(ifx_spi_dev_t *pdev) * \brief Detect serial flash device * * \param[in] pdev Pointer to ifx_spi_dev_t * \return -l Failed to detect device * \return 0 OK * \ingroup IFX_SFLASH_INTERNAL \*------------------------------------------------------------------------------------------*/ static int ifx_spi_flash_probedevice(ifx_spi_dev_t *pdev) { struct _spi_register *const SPI = (struct _spi_register *)&(*(volatile unsigned int *)(IFX_SSC_PHY_BASE | KSEG1)); unsigned int i; unsigned int flash_data; u16 dev_id; SPI->CON.Bits.rxoff = 0; SPI->CON.Bits.txoff = 0; ifx_spi_setcs(IFX_SSC_CSx, select); SPI->TB.Register = IFX_OPCODE_RDID << 24; while(SPI->STAT.Bits.rxbv < 4) ; ifx_spi_setcs(IFX_SSC_CSx, deselect); flash_data = SPI->RB; SPI->CON.Bits.rxoff = 1; SPI->CON.Bits.txoff = 1; pdev->manufacturer_id = (flash_data >> 16) & 0xFF; pdev->device_id1 = (flash_data >> 8) & 0xFF; pdev->device_id2 = flash_data & 0xFF; dev_id = flash_data & 0xFFFF; IFX_SFLASH_PRINT("Vendor %02x Type %02x sig %02x\n", pdev->manufacturer_id, pdev->device_id1, pdev->device_id2); for (i = 0; i < ARRAY_SIZE(flash_manufacturers); ++i) { if (pdev->manufacturer_id == flash_manufacturers[i].id) { break; } } if (i == ARRAY_SIZE(flash_manufacturers)){ goto unknown; } pdev->manufacturer = &flash_manufacturers[i]; for (i = 0; pdev->manufacturer->flashes[i].id; ++i) { if (dev_id == pdev->manufacturer->flashes[i].id) { break; } } if (!pdev->manufacturer->flashes[i].id) { goto unknown; } pdev->flash = &pdev->manufacturer->flashes[i]; pdev->sector_size = pdev->flash->sector_size; pdev->num_sectors = pdev->flash->num_sectors; pdev->dummy_cycles = IFX_FAST_READ_DUMMY_BYTE; pdev->write_length = IFX_SFLASH_PAGESIZE; pdev->size = pdev->sector_size * pdev->num_sectors; printk("SPI Device: %s 0x%02X (%s) 0x%02X 0x%02X\n", pdev->flash->name, pdev->manufacturer_id, pdev->manufacturer->name, pdev->device_id1, pdev->device_id2); IFX_SFLASH_PRINT(" Parameters: num sectors = %lu, sector size = %lu, write size = %u\n", pdev->num_sectors, pdev->sector_size, pdev->write_length); return 0; unknown: printk("Unknown SPI device: 0x%02X 0x%02X 0x%02X\n", pdev->manufacturer_id, pdev->device_id1, pdev->device_id2); return -1; } /*------------------------------------------------------------------------------------------*\ * This function assumes that the CLC register is set with the appropriate value for RMC. \*------------------------------------------------------------------------------------------*/ static INLINE unsigned int ifx_ssc_get_kernel_clk(void) { struct _spi_register *const SPI = (struct _spi_register *)&(*(volatile unsigned int *)(IFX_SSC_PHY_BASE | KSEG1)); unsigned int rmc; rmc = SPI->CLC >> 8; if(rmc == 0){ rmc = 1; SPI->CLC = (rmc << 8); } return (ifx_get_fpi_hz() / rmc); } /*------------------------------------------------------------------------------------------*\ * called with SSC disabled to get access to the control bits \*------------------------------------------------------------------------------------------*/ #define IFX_SSC_SPLIT_BAUD_RATE 25000000 static int ifx_sflash_setbaudrate(unsigned int baudrate) { struct _spi_register *const SPI = (struct _spi_register *)&(*(volatile unsigned int *)(IFX_SSC_PHY_BASE | KSEG1)); /*--- #define DBG_SET_BAUDRATE ---*/ unsigned int ifx_ssc_clock; unsigned int br; /*--- unsigned long flags; ---*/ ifx_ssc_clock = ifx_ssc_get_kernel_clk(); if (ifx_ssc_clock == 0) { return -EINVAL; } #ifdef DBG_SET_BAUDRATE printk(KERN_ERR "[%x:%s/%d] baudrate=%u, ifx_ssc_clock=%u CPHY1=%x\n", smp_processor_id(), __FUNCTION__, __LINE__, baudrate, ifx_ssc_clock, IFX_REG_R32(IFX_GPHY1_CFG)); #endif /*--- #ifdef DBG_SET_BAUDRATE ---*/ #ifdef CONFIG_VR9 if (ifx_is_vr9_a21_chip()) { /* VR9 A21 high baudrate support */ u32 reg; printk("{%s} VR9 A21 high baudrate support\n", __func__); if (baudrate > IFX_SSC_SPLIT_BAUD_RATE) { reg = IFX_REG_R32(IFX_GPHY1_CFG); reg &= ~IFX_SSC_HIGH_BAUD_DELAY_MASK; reg |= IFX_SSC_HIGH_BAUD_DELAY_THREE_CLOCK; IFX_REG_W32(reg, IFX_GPHY1_CFG); } else { reg = IFX_REG_R32(IFX_GPHY1_CFG); reg &= ~IFX_SSC_HIGH_BAUD_DELAY_MASK; IFX_REG_W32(reg, IFX_GPHY1_CFG); } } #endif /* CONFIG_VR9 */ /* Compute divider */ br = (((ifx_ssc_clock >> 1) + baudrate / 2) / baudrate) - 1; #ifdef DBG_SET_BAUDRATE printk(KERN_ERR "[%d:%s/%d] br(divider)=%u\n", smp_processor_id(), __FUNCTION__, __LINE__, br); #endif /*--- #ifdef DBG_SET_BAUDRATE ---*/ if ((br > 0xffff) || ((br == 0) && SPI->STAT.Bits.ms)) { printk(KERN_ERR "%s: illegal baudrate %u br %d\n", __func__, baudrate, br); return -EINVAL; } /*--- spin_lock_irqsave(&ssc_irq_lock, flags); ---*/ SPI->BRT = br; smp_wmb(); #ifdef CONFIG_AR10 { u32 reg; reg = SPI->CON.Register; /*--- IFX_SSC_GET_CON(port); ---*/ if (baudrate > IFX_SSC_SPLIT_BAUD_RATE) { reg &= ~IFX_SSC_CON_CLK_DELAY; reg |= SM(IFX_SSC_CON_CLK_DELAY_DEFAULT, IFX_SSC_CON_CLK_DELAY); } else { reg &= ~IFX_SSC_CON_CLK_DELAY; } SPI->CON.Register = reg; /*--- IFX_SSC_SET_CON(reg, port); ---*/ smp_wmb(); } #endif /* CONFIG_AR10 */ /*--- spin_unlock_irqrestore(&ssc_irq_lock, flags); ---*/ return 0; } /*------------------------------------------------------------------------------------------*\ \*------------------------------------------------------------------------------------------*/ static INLINE int ifx_spi_flash_hw_init(void) { struct _spi_register *const SPI = (struct _spi_register *)&(*(volatile unsigned int *)(IFX_SSC_PHY_BASE | KSEG1)); SPI_PMU_SETUP(IFX_PMU_ENABLE); /* Clock Control Register *//* DISS OFF and RMC = 1 */ /* Disable SSC to get access to the control bits */ SPI->WHBSTATE = 1; /*--- Disable SSC ; ---*/ smp_wmb(); /*CSx */ SPI->GPOCON = (1 << (IFX_SSC_CSx + 8)); ifx_spi_setcs(IFX_SSC_CSx, deselect); /*--- disable IRQ ---*/ SPI->IRNEN = 0; /*--- *AMAZON_S_SSC1_IRNEN = 0x0; ---*/ if (ifx_sflash_setbaudrate(IFX_SFLASH_BAUDRATE)) { return -EINVAL; } /*--- enable and flush RX/TX FIFO ---*/ /*--- ACHTUNG: ifx_ssc_hwinit(struct ifx_ssc_port *port) ---*/ SPI->RXFCON.Register = (0xF << 8) | (1 << 1) | (1 << 0); SPI->TXFCON.Register = (0xF << 8) | (1 << 1) | (1 << 0); /*--- set CON, TX off , RX off, ENBV=0, BM=7(8 bit valid) HB=1(MSB first), PO=0,PH=1(SPI Mode 0) ---*/ SPI->CON.Register = CON_ENBV | CON_PH | CON_HB | CON_RXOFF | CON_TXOFF; /*--- 0x00070033 ---*/ smp_wmb(); /*--- Set Master mode and Enable SSC ---*/ SPI->WHBSTATE = (1 << 3) | (1 << 1); smp_wmb(); return 0; } /*------------------------------------------------------------------------------------------*\ \*------------------------------------------------------------------------------------------*/ static INLINE void ifx_spi_flash_gpio_init(void) { ifx_gpio_register(IFX_GPIO_MODULE_SSC); ifx_gpio_register(IFX_GPIO_MODULE_SPI_FLASH); } /*------------------------------------------------------------------------------------------*\ \*------------------------------------------------------------------------------------------*/ static INLINE void ifx_spi_flash_gpio_release(void) { ifx_gpio_deregister(IFX_GPIO_MODULE_SPI_FLASH); ifx_gpio_deregister(IFX_GPIO_MODULE_SSC); } /*------------------------------------------------------------------------------------------*\ \*------------------------------------------------------------------------------------------*/ static int ifx_spi_flash_probe (struct platform_device *pdev) { struct physmap_flash_data *pdata = pdev->dev.platform_data; struct mtd_info *mtd; #ifdef CONFIG_MTD_CMDLINE_PARTS int np; #endif /* CONFIG_MTD_CMDLINE_PARTS */ int ret = 0; int index; /*--- spin_lock_init(&ssc_irq_lock); ---*/ sema_init(&ifx_sflash_sem, 1); spi_sflash = kzalloc(sizeof(ifx_spi_dev_t), GFP_KERNEL); if (spi_sflash == NULL) { ret = -ENOMEM; goto done; } /*--- spi_sflash->addr_cycles = IFX_SFLASH_ADDR_CYCLES; ---*/ mtd = kzalloc(sizeof(struct mtd_info), GFP_KERNEL); if ( mtd == NULL ) { pr_err("[%s] Cant allocate mtd stuff\n", __func__); ret = -ENOMEM; goto err3; } ifx_spi_flash_gpio_init(); if (ifx_spi_flash_hw_init()) { pr_err("[%s] ERROR HW-Init\n", __func__); goto err5; } #ifdef CONFIG_IFX_PMCU ifx_ssc_pmcu_init( NULL ); #endif /* CONFIG_IFX_PMCU */ if (ifx_spi_flash_probedevice(spi_sflash) != 0) { pr_err("[%s] Found no serial flash device\n", __func__); ret = -ENXIO; goto err5; } mtd->priv = spi_sflash; mtd->name = IFX_SFLASH_NAME; mtd->type = MTD_NORFLASH; mtd->flags = (MTD_CAP_NORFLASH | MTD_WRITEABLE); mtd->size = spi_sflash->size; /* Prefer "small sector" erase if possible */ if (spi_sflash->flash->flags & SECT_4K) { spi_sflash->erase_opcode = IFX_OPCODE_BE_4K; mtd->erasesize = 4096; } else { spi_sflash->erase_opcode = IFX_OPCODE_SE; mtd->erasesize = spi_sflash->sector_size; } mtd->numeraseregions = 0; mtd->eraseregions = NULL; mtd->owner = THIS_MODULE; mtd->writesize = 1; /* like NOR flash, should be 1 */ mtd->_erase = ifx_spi_flash_erase; mtd->_read = ifx_spi_flash_read; mtd->_write = ifx_spi_flash_write; mtd->_sync = ifx_spi_flash_sync; index = ifx_spi_flash_size_to_index(spi_sflash->size); #if defined(CONFIG_TFFS_DEV_MTDNOR) || defined(CONFIG_TFFS_DEV_LEGACY) TFFS3_Register_Panic_CB(mtd, panic_reinit); #endif if (index > IFX_SPI_MAX_FLASH) { pr_err("[%s] flash size is too big to support\n", __func__); ret = -EINVAL; goto err5; } #ifdef IFX_SPI_FLASH_DBG printk (KERN_DEBUG "mtd->name = %s\n" "mtd->size = 0x%.8llx (%lluM)\n" "mtd->erasesize = 0x%.8x (%uK)\n" "mtd->numeraseregions = %d\n" "mtd index %d\n", mtd->name, mtd->size, mtd->size / (1024*1024), mtd->erasesize, mtd->erasesize / 1024, mtd->numeraseregions, index); if (mtd->numeraseregions) { int result; for (result = 0; result < mtd->numeraseregions; result++) { printk (KERN_DEBUG "\n\n" "mtd->eraseregions[%d].offset = 0x%.8llx\n" "mtd->eraseregions[%d].erasesize = 0x%.8x (%uK)\n" "mtd->eraseregions[%d].numblocks = %d\n", result, mtd->eraseregions[result].offset, result, mtd->eraseregions[result].erasesize, mtd->eraseregions[result].erasesize / 1024, result, mtd->eraseregions[result].numblocks); } } #endif /* IFX_SPI_FLASH_DBG */ mtd_device_register(mtd, pdata->parts, pdata->nr_parts); spi_sflash->mtd = mtd; ifx_spi_flash_version(); return ret; err5: kfree(mtd); err3: kfree(spi_sflash); done: ifx_spi_flash_gpio_release(); return ret; } /*------------------------------------------------------------------------------------------*\ \*------------------------------------------------------------------------------------------*/ static int ifx_spi_flash_remove(struct platform_device *pdev __maybe_unused) { if (spi_sflash != NULL) { if (spi_sflash->parsed_parts != NULL) { mtd_device_unregister(spi_sflash->mtd); } if (spi_sflash->mtd) kfree(spi_sflash->mtd); kfree(spi_sflash); } ifx_spi_flash_gpio_release(); return 0; } static struct platform_driver ifx_spi_flash_driver = { .probe = ifx_spi_flash_probe, .remove = ifx_spi_flash_remove, .driver = { .name = IFX_SFLASH_NAME, }, }; module_platform_driver(ifx_spi_flash_driver); #if defined(CONFIG_TFFS_DEV_MTDNOR) || defined(CONFIG_TFFS_DEV_LEGACY) #define FLASH_BUFFER_SIZE 256U struct _spi_register *const SPI = (struct _spi_register *)&(*(volatile unsigned int *)(IFX_SSC_PHY_BASE | KSEG1)); static DEFINE_SPINLOCK(panic_locking); /*--------------------------------------------------------------------------------*\ \*--------------------------------------------------------------------------------*/ static inline void panic_lock(unsigned long *flags, unsigned long *cpuflag __maybe_unused) { spin_lock_irqsave(&panic_locking, *flags); #if defined(CONFIG_SMP) *cpuflag = dvpe(); #endif/*--- #if defined(CONFIG_SMP) ---*/ } /*--------------------------------------------------------------------------------*\ \*--------------------------------------------------------------------------------*/ static inline void panic_unlock(unsigned long flags, unsigned long cpuflag __maybe_unused) { #if defined(CONFIG_SMP) evpe(cpuflag); #endif/*--- #if defined(CONFIG_SMP) ---*/ spin_unlock_irqrestore(&panic_locking, flags); } static int panic_read(struct mtd_info *mtd __maybe_unused, loff_t addr, size_t len, size_t *retlen, u_char *buf) { unsigned long flags, cpuflags; int result; panic_lock(&flags, &cpuflags); result = ifx_spi_do_read(addr, len, retlen, buf, 1); panic_unlock(flags, cpuflags); return result; } static int panic_do_write(struct mtd_info *mtd __maybe_unused, loff_t addr, size_t len, size_t *retlen, const uint8_t *buf) { uint8_t pagebuf[FLASH_BUFFER_SIZE + sizeof(uint32_t)], *pbuf; int result; size_t chunk_len, read_len, written; // TODO: clean up this check if(addr < 0x20000){ pr_emerg("[%s] Not writing to bootloader address 0x%08llx\n", __func__, addr); } result = 0; *retlen = 0; written = 0; pbuf = &(pagebuf[0]); pbuf = PTR_ALIGN(pbuf, sizeof(uint32_t)); while(written < len){ chunk_len = min((len - written), FLASH_BUFFER_SIZE); chunk_len = min(chunk_len, (size_t)(FLASH_BUFFER_SIZE - ((addr + written) & (FLASH_BUFFER_SIZE - 1)))); memcpy(pbuf, &(buf[written]), chunk_len); result = ifx_spi_do_write(addr + written, chunk_len, pbuf); if(result != 0){ goto err_out; } written += chunk_len; } *retlen = written; written = 0; while(written < len){ chunk_len = min((len - written), FLASH_BUFFER_SIZE); memset(pbuf, 0x0, FLASH_BUFFER_SIZE); /*--- result = panic_do_read(mtd, addr + written, chunk_len, &read_len, pbuf); ---*/ result = ifx_spi_do_read(addr + written, chunk_len, &read_len, pbuf, 0); if(result != 0 || read_len != chunk_len){ pr_err("[%s] verify read at 0x%08llx failed: chunk_len: 0x%08x read_len: 0x%08x\n", __func__, addr + written, chunk_len, read_len); result = -EIO; goto err_out; } if(memcmp(pbuf, &buf[written], chunk_len)){ pr_err("[%s] verify compare at 0x%08llx failed\n", __func__, addr + written); print_hex_dump(KERN_ERR, "orig: ", DUMP_PREFIX_OFFSET, 16, 4, &buf[written], chunk_len, 0); pr_err("\n"); print_hex_dump(KERN_ERR, "read: ", DUMP_PREFIX_OFFSET, 16, 4, pbuf, chunk_len, 0); result = -EIO; goto err_out; } written += chunk_len; } err_out: return result; } static int panic_write(struct mtd_info *mtd, loff_t addr, size_t len, size_t *retlen, const uint8_t *buf) { unsigned long flags, cpuflags; int result; panic_lock(&flags, &cpuflags); result = panic_do_write(mtd, addr, len, retlen, buf); panic_unlock(flags, cpuflags); return result; } static void panic_sync(struct mtd_info *mtd __maybe_unused) { } static int panic_erase(struct mtd_info *mtd __maybe_unused, struct erase_info *info __maybe_unused) { return -EROFS; } /*------------------------------------------------------------------------------------------*\ \*------------------------------------------------------------------------------------------*/ static struct mtd_info *panic_reinit(struct mtd_info *mtd) { unsigned long flags, cpuflags; unsigned int flash_data; unsigned int i, rmc, ssc_clock, br; struct ifx_sflash_manufacturer_info *pManufacturer; pr_err("[%s] Called for mtd %s\n", __func__, mtd->name); panic_lock(&flags, &cpuflags); if(down_trylock(&ifx_sflash_sem)){ return NULL; } /* SSC0 Ports */ /* P0.10/P0.15 as CS4/CS1 for flash or eeprom depends on jumper */ /* P0.10 ALT0= 0, ALT1=1, DIR=1 */ *(IFX_GPIO_P0_DIR) |= BIT(SPI_CSx); #if defined(CONFIG_VR9) *(IFX_GPIO_P0_ALTSEL0) &= ~BIT(SPI_CSx); *(IFX_GPIO_P0_ALTSEL1) |= BIT(SPI_CSx); #elif defined(CONFIG_AR10)/*--- #if defined(CONFIG_VR9) ---*/ *(IFX_GPIO_P0_ALTSEL0) |= BIT(SPI_CSx); *(IFX_GPIO_P0_ALTSEL1) &= ~BIT(SPI_CSx); #else #error unknown platform #endif *(IFX_GPIO_P0_OD) |= BIT(SPI_CSx); /* p1.0 SPI_DIN, p1.1 SPI_DOUT, p1.2 SPI_CLK */ *(IFX_GPIO_P1_DIR) |= (BIT(SPI_DOUT) | BIT(SPI_CLK)); *(IFX_GPIO_P1_DIR) &= ~(BIT(SPI_DIN)); *(IFX_GPIO_P1_ALTSEL0) |= (BIT(SPI_DOUT) | BIT(SPI_CLK) | BIT(SPI_DIN)); *(IFX_GPIO_P1_ALTSEL1) &= ~(BIT(SPI_DOUT) | BIT(SPI_CLK) | BIT(SPI_DIN)); *(IFX_GPIO_P1_OD) |= (BIT(SPI_DOUT) | BIT(SPI_CLK)); /* Clock Control Register *//* DISS OFF and RMC = 1 */ /* Disable SSC to get access to the control bits */ SPI->WHBSTATE = 1; /*--- Disable SSC ; ---*/ asm("SYNC"); rmc = SPI->CLC >> 8; if(rmc == 0){ rmc = 1; SPI->CLC = (rmc << 8); } /*CSx */ SPI->GPOCON = (1 << (IFX_SSC_CSx + 8)); ifx_spi_setcs(IFX_SSC_CSx, deselect); /* disable IRQ */ SPI->IRNEN = 0; /*--- *AMAZON_S_SSC1_IRNEN = 0x0; ---*/ /*------------------------------------------------------------------------------------------*\ * Set the Baudrate * BR = (FPI clk / (2 * Baudrate)) - 1 * Note: Must be set while SSC is disabled! \*------------------------------------------------------------------------------------------*/ ssc_clock = ifx_get_fpi_hz() / rmc; br = (((ssc_clock >> 1) + SFLASH_BAUDRATE / 2) / SFLASH_BAUDRATE) - 1; SPI->BRT = br; /*enable and flush RX/TX FIFO*/ SPI->RXFCON.Register = (0xF << 8) | (1 << 1) | (1 << 0); SPI->TXFCON.Register = (0xF << 8) | (1 << 1) | (1 << 0); /* set CON, TX off , RX off, ENBV=0, BM=7(8 bit valid) HB=1(MSB first), PO=0,PH=1(SPI Mode 0)*/ SPI->CON.Register = CON_ENBV | CON_PH | CON_HB | CON_RXOFF | CON_TXOFF; /*--- 0x00070033 ---*/ asm("SYNC"); /*Set Master mode and Enable SSC */ SPI->WHBSTATE = (1 << 3) | (1 << 1); asm("SYNC"); SPI->CON.Bits.rxoff = 0; SPI->CON.Bits.txoff = 0; ifx_spi_setcs(IFX_SSC_CSx, select); SPI->TB.Register = IFX_OPCODE_RDID << 24; while(SPI->STAT.Bits.rxbv < 4) ; ifx_spi_setcs(IFX_SSC_CSx, deselect); flash_data = SPI->RB; ifx_spi_read_status(IFX_SSC_CSx); /*--- zum Schluss ein Test, ob wir den Flash auch lesen können ---*/ for (i = 0; i < ARRAY_SIZE(flash_manufacturers); ++i) { if (((flash_data >> 16) & 0xFF) == flash_manufacturers[i].id) { break; } } if (i == ARRAY_SIZE(flash_manufacturers)){ return NULL; } pManufacturer = &flash_manufacturers[i]; for (i = 0; pManufacturer->flashes[i].id; ++i) { if ((flash_data & 0xFFFF) == pManufacturer->flashes[i].id) { break; } } if ( ! pManufacturer->flashes[i].id) { return NULL; } panic_unlock(flags, cpuflags); mtd->_read = panic_read; mtd->_write = panic_write; mtd->_sync = panic_sync; mtd->_erase = panic_erase; return mtd; } #endif // defined(CONFIG_TFFS_DEV_MTDNOR) || defined(CONFIG_TFFS_DEV_LEGACY) MODULE_LICENSE("GPL"); MODULE_AUTHOR("Chuanhua.Lei@infineon.com"); MODULE_SUPPORTED_DEVICE("Serial flash 25 types generic driver"); MODULE_DESCRIPTION("IFAP SPI flash device driver");