/****************************************************************************** ** ** FILE NAME : amazon_s_sflash.c ** PROJECT : amazon_s ** MODULES : SPI Flash ** ** DATE : 20 Nov 2007 ** AUTHOR : Lei Chuanhua ** DESCRIPTION : SPI Flash MTD Driver ** COPYRIGHT : Copyright (c) 2007 ** 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 ** 20 Nov 2007 Lei Chuanhua Initial Version ** 28 Nov 2007 Lei Chuanhua SPI DMA support ** 11 Dec 2007 Lei Chuanhua Modify for new SPI spec ** 15 Dec 2007 Lei Chuanhua Supports kernel 2.6.17 and later ** 03 Jan 2008 Lei Chuanhua Supports AT25FXXX and Winbond W25X/Qxxx sflash ** 25 Nov 2008 Lei Chuanhua Remove static buffer and kmalloc max dma burst ** len alignment buffer once. Cleanup init code. ** 24 Dec 2008 Lei Chuanhua Fragment support for read operation ** 20 May 2009 Lei Chuanhua Move SPI CS to SPI client driver. *******************************************************************************/ #ifndef EXPORT_SYMTAB #define EXPORT_SYMTAB #endif #ifndef AUTOCONF_INCLUDED #include #endif /* AUTOCONF_INCLUDED */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef CONFIG_MTD_PARTITIONS #define __AMAZON_S_SFLASH__ #include #endif /* CONFIG_MTD_PARTITIONS */ //#define SPI_FLASH_DBG #if defined(SPI_FLASH_DBG) #define DBG_SPI(format, arg...) \ printk("%s: " format, __func__, ##arg) #define INLINE #else #define DBG_SPI(format, arg...) \ do {} while(0) #define INLINE inline #endif /* SPI flash device driver parts */ #define MAX_ADDRESS_NUM 5 #define MAX_DUMMY_CYCLES 10 #define SFLASH_DETECT_COUNTER 100000 /* XXX, check if flash mounted */ /* * 32 should be maximum requirement for DMA alignment at the low level, * keep it here for future change */ #define SFLASH_DMA_MAX_BURST_LEN 32 #define SFLASH_MAX_WRITE_SIZE (SPI_FALSH_PAGE_SIZE * 2) #define SFLASH_MAX_READ_SIZE (SPI_FALSH_PAGE_SIZE * 8) /* Tunable */ #define ITEMS(X) (sizeof(X)/sizeof (X[0])) typedef struct ifx_flash_config { u32 size; u32 nsectors; u32 sector_size; u32 pgsize; }ifx_flash_config_t; /* Mapping of generic opcodes to MX serial flash opcodes */ struct opcodes { u16 code; }; /* Driver private data */ typedef struct { struct mtd_info *mtd; struct mtd_partition *parsed_parts; /* parsed partitions */ char *flash_tx_org_buf; /* Original write buffer */ char *flash_tx_buf; /* Aligned write buffer */ char *flash_rx_org_buf; /* Orignal read buffer */ char *flash_rx_buf; /* Aligned read buffer */ u8 addr_cycles; IFX_SSC_HANDLE sflash_handler; ifx_flash_config_t *flash_cfg; struct opcodes *op; } spi_dev_t; static spi_dev_t *spi_sflash = NULL; #define SPI_CMD(X) spi_sflash->op[(X)].code struct ifx_flash_vendor_config_t { u8 vid; struct opcodes op[SPI_MAX_OPCODES]; ifx_flash_config_t ifx_flash_cfg[SPI_MAX_FLASH]; }; #define IFX_FLASH_MAX_VENDOR 8 static struct ifx_flash_vendor_config_t ifx_flash_vendor_cfgtbl[] = { { MX_VENDOR_ID, { {MX_SFLASH_WREN}, {MX_SFLASH_WRDI}, {MX_SFLASH_RDSR}, {MX_SFLASH_WRSR}, {MX_SFLASH_READ}, {MX_SFLASH_FAST_READ}, {MX_SFLASH_PP}, {MX_SFLASH_SE}, {MX_SFLASH_CE}, {MX_SFLASH_DP}, {MX_SFLASH_RDID}, }, { { 0, 0, 0, 0}, { MX_256KB_SIZE_COUNT, MX_256KB_SECTOR_COUNT, MX_256KB_SECTOR_SIZE, SPI_FALSH_PAGE_SIZE}, { MX_512KB_SIZE_COUNT, MX_512KB_SECTOR_COUNT, MX_512KB_SECTOR_SIZE, SPI_FALSH_PAGE_SIZE}, { MX_1MB_SIZE_COUNT, MX_1MB_SECTOR_COUNT, MX_1MB_SECTOR_SIZE, SPI_FALSH_PAGE_SIZE}, { MX_2MB_SIZE_COUNT, MX_2MB_SECTOR_COUNT, MX_2MB_SECTOR_SIZE, SPI_FALSH_PAGE_SIZE}, { MX_4MB_SIZE_COUNT, MX_4MB_SECTOR_COUNT, MX_4MB_SECTOR_SIZE, SPI_FALSH_PAGE_SIZE}, { MX_8MB_SIZE_COUNT, MX_8MB_SECTOR_COUNT, MX_8MB_SECTOR_SIZE, SPI_FALSH_PAGE_SIZE}, }, }, { AT_VENDOR_ID, { {AT_SFLASH_WREN}, {AT_SFLASH_WRDI}, {AT_SFLASH_RDSR}, {AT_SFLASH_WRSR}, {AT_SFLASH_READ}, {AT_SFLASH_FAST_READ}, {AT_SFLASH_PP}, {AT_SFLASH_SE}, {AT_SFLASH_CE}, {AT_SFLASH_DP}, {AT_SFLASH_RDID}, }, { { AT_128KB_SIZE_COUNT, AT_128KB_SECTOR_COUNT, AT_128KB_SECTOR_SIZE, SPI_FALSH_PAGE_SIZE}, { AT_256KB_SIZE_COUNT, AT_256KB_SECTOR_COUNT, AT_256KB_SECTOR_SIZE, SPI_FALSH_PAGE_SIZE}, { 0, 0, 0, 0}, { 0, 0, 0, 0}, { 0, 0, 0, 0}, { 0, 0, 0, 0}, { 0, 0, 0, 0}, }, }, { WB_VENDOR_ID, { {WB_SFLASH_WREN}, {WB_SFLASH_WRDI}, {WB_SFLASH_RDSR}, {WB_SFLASH_WRSR}, {WB_SFLASH_READ}, {WB_SFLASH_FAST_READ}, {WB_SFLASH_PP}, {WB_SFLASH_SE}, {WB_SFLASH_CE}, {WB_SFLASH_DP}, {WB_SFLASH_RDID}, }, { { 0, 0, 0, 0}, { 0, 0, 0, 0}, { 0, 0, 0, 0}, { WB_1MB_SIZE_COUNT, WB_1MB_SECTOR_COUNT, WB_1MB_SECTOR_SIZE, SPI_FALSH_PAGE_SIZE}, { WB_2MB_SIZE_COUNT, WB_2MB_SECTOR_COUNT, WB_2MB_SECTOR_SIZE, SPI_FALSH_PAGE_SIZE}, { WB_4MB_SIZE_COUNT, WB_4MB_SECTOR_COUNT, WB_4MB_SECTOR_SIZE, SPI_FALSH_PAGE_SIZE}, { WB_8MB_SIZE_COUNT, WB_8MB_SECTOR_COUNT, WB_8MB_SECTOR_SIZE, SPI_FALSH_PAGE_SIZE}, }, } }; #ifdef SPI_FLASH_DBG static void flash_dump(const char *title, const u8 *buf, size_t len) { int i, llen, lenlab = 0; const u8 *pos = buf; const 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 /* SPI_FLASH_DBG */ /* Brief: convert address from u32 to u8 array * Parameter: * address: the address, maximum 32 bit * addr: array that holds the results, maximum 4 elemets */ static INLINE void u32_to_u8_addr(u32 address, u8* addr) { addr[0] = (u8) ((address>>16) & 0xff); addr[1] = (u8) ((address>>8) & 0xff); addr[2] = (u8) (address & 0xff); } /* Serial flash interface */ static int sflash_rdsr(spi_dev_t *dev, u8 *status) { int ret; u8 cmd = MX_SFLASH_RDSR; /* XXX, seems that all 25xxx supports the same read status command */ if ((ret = amazon_s_sscTxRx(dev->sflash_handler ,(char *)&cmd, sizeof(u8), status, sizeof(u8))) != 2){ DBG_SPI("line %d amazon_s_sscTxRx fails %d\n", __LINE__, ret); return -1; } return 0; } static int sflash_sync(spi_dev_t *dev) { int ret = 0; u8 status; int count = 0; while (1) { ret = sflash_rdsr(dev, &status); if (ret) { DBG_SPI("Read back status fails %d\n", ret); break; } else if ((status & SFLASH_SR_WIP) == 0) { break; } if (++count > SFLASH_DETECT_COUNTER) { DBG_SPI("Detct counter out of range!!!\n"); ret = -1; break; } } return ret; } static int sflash_session(spi_dev_t *dev, u8 cmd, u8 *addr, u8 dummy_cycles, u8 * wbuf, u32 wcnt, u8 * rbuf, u32 rcnt) { int i; int ret = 0; int start = 0; int total = 0; char *buf = dev->flash_tx_buf; /* Always use the same buffer for efficiecy */ char *tbuf; /* Sanity check */ if (unlikely(rcnt >= SFLASH_MAX_READ_SIZE)) { printk("%s: please increase read buffer size\n", __func__); return -1; } /* CMD */ buf[0] = cmd; start = 1; /* Address */ if (addr != NULL) { for (i = 0; i < dev->addr_cycles; i++) { buf[start + i] = addr[i]; } start += dev->addr_cycles; } /* Dummy cycles */ if (dummy_cycles > 0 && dummy_cycles < MAX_DUMMY_CYCLES) { for (i = 0; i < dummy_cycles; i ++) { buf[start + i] = 0; } start += dummy_cycles; } /* Possibly, there is no flash mounted */ if (sflash_sync(dev) == -1) return -1; if ((wcnt == 0) && (rcnt == 0)) { /* Cmd + Addr + Dummy cycles */ if ((ret = amazon_s_sscTx(dev->sflash_handler, buf, start)) != start) { DBG_SPI("line %d amazon_s_sscTx fails %d\n", __LINE__, ret); goto sflash_session_out; } } else if (wcnt > 0) { /* Cmd + Addr + Dummy cycles + Write data */ total = start + wcnt; memcpy(buf + start, wbuf, wcnt); if ((ret = amazon_s_sscTx(dev->sflash_handler, buf, total)) != total) { DBG_SPI("line %d amazon_s_sscTx fails %d\n", __LINE__, ret); goto sflash_session_out; } } else if (rcnt > 0) { /* Cmd + Addr + Dummy cycles + Read data */ int rx_aligned = 0; total = start + rcnt; rx_aligned = (((u32)rbuf)& (SFLASH_DMA_MAX_BURST_LEN - 1)) == 0 ? 1 : 0; if (rx_aligned == 0){ tbuf = dev->flash_rx_buf; } else { tbuf = rbuf; } if ((ret = amazon_s_sscTxRx(dev->sflash_handler, buf, start, tbuf, rcnt)) != total) { DBG_SPI("line %d amazon_s_sscTxRx fails %d\n", __LINE__, ret); goto sflash_session_out; } if (rx_aligned == 0) { memcpy(rbuf, tbuf, rcnt); } else { /* Do nothing */ } } else { printk("%s should never happen\n", __func__); } sflash_session_out: return ret; } static INLINE int sflash_wren(void) { return sflash_session(spi_sflash, SPI_CMD(SPI_WRITE_ENABLE), NULL, 0, NULL, 0, NULL, 0); } static INLINE int sflash_rdid(u32 *data) { /* XXX, different vendor has different read id command, check datasheet */ u8 cmd; #ifdef CONFIG_USE_EMULATOR cmd = AT_SFLASH_RDID; #else cmd = MX_SFLASH_RDID; #endif return sflash_session(spi_sflash, cmd, NULL, 0, NULL, 0, (char *)data, 4); } static INLINE int sflash_se(u8 *addr) { return sflash_session(spi_sflash, SPI_CMD(SPI_SECTOR_ERASE), addr, 0, NULL, 0, NULL, 0); } static INLINE int sflash_ce(void) { return sflash_session(spi_sflash, SPI_CMD(SPI_BULK_ERASE), NULL, 0, NULL, 0, NULL, 0); } static INLINE int sflash_pp(u8 *addr, u8 *buf, u32 len) { return sflash_session(spi_sflash, SPI_CMD(SPI_PAGE_PROGRAM), addr, 0, buf, len, NULL, 0); } static INLINE int sflash_rd(u8 *addr, u8 *buf, u32 len) { return sflash_session(spi_sflash, SPI_CMD(SPI_RD_DATA), addr, 0, NULL, 0, buf, len); } static INLINE int amazon_s_spi_read(u32 saddr, u8 *buf, u32 len) { int ret; u8 addr[MAX_ADDRESS_NUM] = {0}; u32_to_u8_addr(saddr, addr); ret = sflash_rd(addr, buf, len); return ret; } static INLINE int amazon_s_spi_write(u32 saddr, u8 *buf,u32 len) { int ret; u8 addr[MAX_ADDRESS_NUM] = {0}; u32_to_u8_addr(saddr, addr); sflash_wren(); ret = sflash_pp(addr, buf, len); return ret; } static INLINE void amazon_s_spi_chip_erase(void) { sflash_wren(); sflash_ce(); } static INLINE void amazon_s_spi_sector_erase(u32 saddr) { u8 addr[MAX_ADDRESS_NUM] = {0}; u32_to_u8_addr(saddr, addr); sflash_wren(); sflash_se(addr); } static INLINE void amazon_s_spi_rdid(u32 *status) { sflash_rdid(status); } /* OS glue interface according to different platforms */ #ifdef CONFIG_AMAZON_S #define IFX_SFLASH_MODE IFX_SSC_MODE_0 #define IFX_SFLASH_PRIORITY IFX_SSC_PRIO_LOW #define IFX_SFLASH_FRAGSIZE 268 /* XXX, must be more than 260, page size + cmd + addr */ #define IFX_SFLASH_MAXFIFOSIZE 32 #define IFX_SFLASH_DRV_VERSION "1.0.4" #define IFX_SFLASH_CS IFX_SSC_WHBGPOSTAT_OUT3_POS #define IFX_SFLASH_NAME "amazon_s-sflash" #ifdef CONFIG_USE_EMULATOR #define IFX_SFLASH_BAUDRATE 10000 /* 10K Hz */ #else #define IFX_SFLASH_BAUDRATE 20000000 /* 2 MHz */ #endif #define IFX_SFLASH_ADDR_CYCLES 3 /* 24 bit address */ #define ifx_spi_rdid(status) amazon_s_spi_rdid((status)) #define ifx_spi_sector_erase(addr) amazon_s_spi_sector_erase((addr)) #define ifx_spi_read(addr, buf, len) amazon_s_spi_read((addr), (buf), (len)) #define ifx_spi_write(addr, buf, len) amazon_s_spi_write((addr), (buf), (len)) #define ifx_spi_chip_erase(dummy) amazon_s_spi_chip_erase((dummy)) #else #error "SPI flash not supported yet for this platform" #endif static INLINE int ifx_spi_flash_cs_handler(u32 csq, IFX_CS_DATA cs_data) { if (csq == IFX_SSC_CS_ON) { /* Low active */ return amazon_s_ssc_cs_low(cs_data); } else { return amazon_s_ssc_cs_high(cs_data); } } static INLINE void ifx_spi_flash_version(void) { printk("Infineon Technologies Synchronous SPI flash driver version %s \n", IFX_SFLASH_DRV_VERSION); } static int ifx_spi_flash_probe(u8 *vendorid) { u32 tmp = 0; u8 vid; u8 mid; u8 sig; u8 idx = 0; ifx_spi_rdid(&tmp); vid = (tmp >> 24) & 0xff; mid = (tmp >> 16) & 0xff; sig = (tmp >> 8) & 0xff; DBG_SPI("The whole %08x Vendor %02x Type %02x sig %02x\n", tmp, vid, mid, sig); *vendorid = vid; switch (vid) { case MX_VENDOR_ID: if (mid == MX_MEM_TYPE) { switch (sig) { case MX_2MBIT_SIGNATURE: idx = FLASH_256KB; break; case MX_4MBIT_SIGNATURE: idx = FLASH_512KB; break; case MX_8MBIT_SIGNATURE: idx = FLASH_1MB; break; case MX_16MBIT_SIGNATURE: idx = FLASH_2MB; break; case MX_32MBIT_SIGNATURE: idx = FLASH_4MB; break; case MX_64MBIT_SIGNATURE: idx = FLASH_8MB; break; default: idx = FLASH_INVALID_SIZE; break; } } break; case AT_VENDOR_ID: if (mid == AT_MEM_TYPE) { /* XXX, ATMEL flash has no information about size */ switch (sig) { default: idx = FLASH_256KB; break; } } break; case WB_VENDOR_ID: if (mid == WB_MEM_TYPE){ switch (sig) { case WB_8MBIT_SIGNATURE: idx = FLASH_1MB; break; case WB_16MBIT_SIGNATURE: idx = FLASH_2MB; break; case WB_32MBIT_SIGNATURE: idx = FLASH_4MB; break; case WB_64MBIT_SIGNATURE: idx = FLASH_8MB; break; default: idx = FLASH_INVALID_SIZE; break; } } break; case STM_VENDOR_ID: printk("%s STM SPI flash Not tested yet\n", __func__); break; case SP_VENDOR_ID: printk("%s Spansion SPI flash Not tested yet\n", __func__); break; default: printk("%s vendor %d SPI flash Not tested yet\n",__func__, vid); break; } return idx; } static int ifx_spi_flash_read(struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen ,u_char *buf) { int total = 0; int len_this_lp; u32 addr; u8 *mem; DBG_SPI ("(from = 0x%.8x, len = %d)\n",(u32) from, (int)len); if (!len) return 0; if ((from + len) > mtd->size) return -EINVAL; /* Fragment support */ while (total < len) { mem = (u8 *)(buf + total); addr = from + total; len_this_lp = min((len - total), (size_t)IFX_SFLASH_FRAGSIZE); ifx_spi_read(addr, mem, len_this_lp); total += len_this_lp; } *retlen = len; return 0; } static int ifx_spi_flash_write (struct mtd_info *mtd, loff_t to, size_t len, size_t *retlen, const u_char *buf) { int total = 0, len_this_lp, bytes_this_page; u32 addr = 0; u8 *mem; DBG_SPI ("(to = 0x%.8x, len = %d)\n",(u32) to, len); *retlen = 0; /* sanity check */ if (len == 0) return 0; if ((to + len) > mtd->size) return (-1); while(total < len) { mem = (u8 *)(buf + total); addr = to + total; bytes_this_page = SPI_FALSH_PAGE_SIZE - (addr % SPI_FALSH_PAGE_SIZE); len_this_lp = min((len - total), (size_t)bytes_this_page); ifx_spi_write(addr, mem, len_this_lp); total += len_this_lp; } *retlen = len; return 0; } static int ifx_spi_flash_erase(struct mtd_info *mtd,struct erase_info *instr) { int nsect, s_curr, s_last; DBG_SPI("(addr = 0x%.8x, len = %d)\n", instr->addr, instr->len); if ((instr->addr + instr->len) > mtd->size) return (-EINVAL); nsect = instr->len/mtd->erasesize; if ((instr->len % mtd->erasesize) > 0) nsect ++; s_curr = instr->addr/mtd->erasesize; s_last = s_curr + nsect; do { ifx_spi_sector_erase(s_curr * mtd->erasesize); } while (++s_curr < s_last); instr->state = MTD_ERASE_DONE; if (instr->callback) { instr->callback (instr); } DBG_SPI ("return\n"); return 0; } #define IFX_SSC_SPI_CS4 10 static INLINE void ifx_spi_flash_gpio_init(void) { IFX_SSC_PIN_RESERVE(IFX_SSC_SPI_CS4); IFX_SSC_DIR_OUT(IFX_SSC_SPI_CS4); IFX_SSC_OD_SET(IFX_SSC_SPI_CS4); IFX_SSC_ALTSEL0_CLR(IFX_SSC_SPI_CS4); IFX_SSC_ALTSEL1_SET(IFX_SSC_SPI_CS4); } static INLINE void ifx_spi_flash_gpio_release(void) { IFX_SSC_PIN_FREE(IFX_SSC_SPI_CS4); } static INLINE IFX_SSC_HANDLE ifx_spi_flash_register(char *dev_name) { IFX_SSC_CONFIGURE_t ssc_cfg = {0}; ssc_cfg.baudrate = IFX_SFLASH_BAUDRATE; ssc_cfg.csset_cb = ifx_spi_flash_cs_handler; ssc_cfg.cs_data = IFX_SFLASH_CS; ssc_cfg.fragSize = IFX_SFLASH_FRAGSIZE; ssc_cfg.maxFIFOSize = IFX_SFLASH_MAXFIFOSIZE; ssc_cfg.ssc_mode = IFX_SFLASH_MODE; ssc_cfg.ssc_prio = IFX_SFLASH_PRIORITY; return amazon_s_sscAllocConnection(dev_name, &ssc_cfg); } static int __init ifx_spi_flash_init (void) { ifx_flash_config_t *flash_cfg; IFX_SSC_HANDLE *sflash_handler; struct mtd_info *mtd; int index; u8 vid = 0; int i; #ifdef CONFIG_MTD_CMDLINE_PARTS int np; #endif /* CONFIG_MTD_CMDLINE_PARTS */ int ret = 0; ifx_spi_flash_gpio_init(); spi_sflash = (spi_dev_t*)kmalloc(sizeof(spi_dev_t), GFP_KERNEL); if (spi_sflash == NULL) { ret = -ENOMEM; goto done; } memset(spi_sflash, 0, sizeof (spi_dev_t)); /* * Make sure tx buffer address is DMA burst length aligned and 2 page size< 512> should be enouhg for * serial flash. In this way, host cpu can make good use of DMA operation. */ spi_sflash->flash_tx_org_buf = kmalloc(SFLASH_MAX_WRITE_SIZE + SFLASH_DMA_MAX_BURST_LEN - 1, GFP_KERNEL); if (spi_sflash->flash_tx_org_buf == NULL) { ret = -ENOMEM; goto err1; } spi_sflash->flash_tx_buf = (char *)(((u32)(spi_sflash->flash_tx_org_buf + SFLASH_DMA_MAX_BURST_LEN - 1)) & ~(SFLASH_DMA_MAX_BURST_LEN - 1)); /* * Make sure rx buffer address is DMA burst length aligned and 8 page size< 2KB> should be enouhg for * serial flash. In this way, host cpu can make good use of DMA operation. */ spi_sflash->flash_rx_org_buf = kmalloc(SFLASH_MAX_READ_SIZE + SFLASH_DMA_MAX_BURST_LEN - 1, GFP_KERNEL); if (spi_sflash->flash_rx_org_buf == NULL) { ret = -ENOMEM; goto err2; } spi_sflash->flash_rx_buf = (char *)(((u32)(spi_sflash->flash_rx_org_buf + SFLASH_DMA_MAX_BURST_LEN - 1)) & ~(SFLASH_DMA_MAX_BURST_LEN - 1)); spi_sflash->addr_cycles = IFX_SFLASH_ADDR_CYCLES; mtd = kmalloc(sizeof(struct mtd_info), GFP_KERNEL); if ( mtd == NULL ) { printk(KERN_WARNING "%s Cant allocate mtd stuff\n", __func__); ret = -ENOMEM; goto err3; } memset(mtd, 0, sizeof(struct mtd_info)); sflash_handler = ifx_spi_flash_register(IFX_SFLASH_NAME); if (sflash_handler == NULL) { printk("%s: failed to register sflash\n", __func__); ret = -ENOMEM; goto err4; } printk("MTD driver for SPI flash.\n"); printk("Probing for Serial flash ...\n"); spi_sflash->sflash_handler = sflash_handler; index = ifx_spi_flash_probe(&vid); if (index == FLASH_INVALID_SIZE) { printk (KERN_WARNING "%s: Found no serial flash device\n", __func__); ret = -ENXIO; goto err5; } for (i = 0; i < ITEMS(ifx_flash_vendor_cfgtbl); i++) { if (ifx_flash_vendor_cfgtbl[i].vid == vid) { spi_sflash->flash_cfg = ifx_flash_vendor_cfgtbl[i].ifx_flash_cfg; spi_sflash->op = ifx_flash_vendor_cfgtbl[i].op; break; } } if (i >= ITEMS(ifx_flash_vendor_cfgtbl)) { printk (KERN_WARNING "%s: Found no flash configration table \n", __func__); ret = -EINVAL; goto err5; } flash_cfg = &spi_sflash->flash_cfg[index]; mtd->name = IFX_SFLASH_NAME; mtd->type = MTD_NORFLASH; mtd->flags = (MTD_CAP_NORFLASH|MTD_WRITEABLE); mtd->size = flash_cfg->size; mtd->erasesize = flash_cfg->sector_size; mtd->numeraseregions = 0; mtd->eraseregions = NULL; #if LINUX_VERSION_CODE <= KERNEL_VERSION(2,5,41) mtd->module = THIS_MODULE; #else mtd->owner = THIS_MODULE; #endif #if LINUX_VERSION_CODE > KERNEL_VERSION(2,6,16) mtd->writesize = 1; /* like NOR flash, should be 1 */ #endif mtd->erase = ifx_spi_flash_erase; mtd->read = ifx_spi_flash_read; mtd->write = ifx_spi_flash_write; #ifdef SPI_FLASH_DBG printk (KERN_DEBUG "mtd->name = %s\n" "mtd->size = 0x%.8x (%uM)\n" "mtd->erasesize = 0x%.8x (%uK)\n" "mtd->numeraseregions = %d\n", mtd->name, mtd->size, mtd->size / (1024*1024), mtd->erasesize, mtd->erasesize / 1024, mtd->numeraseregions); if (mtd->numeraseregions) { int result; for (result = 0; result < mtd->numeraseregions; result++) { printk (KERN_DEBUG "\n\n" "mtd->eraseregions[%d].offset = 0x%.8x\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 /* SPI_FLASH_DBG */ #ifdef CONFIG_MTD_PARTITIONS #ifdef CONFIG_MTD_CMDLINE_PARTS np = parse_cmdline_partitions(mtd, &spi_sflash->parsed_parts, "ifx spi controller"); if ( np > 0 ) add_mtd_partitions(mtd, spi_sflash->parsed_parts, np); else add_mtd_device(mtd); #else add_mtd_partitions(mtd, ifx_spi_partitions[index], ITEMS(ifx_spi_partitions[index])); #endif /* CONFIG_MTD_CMDLINE_PARTS */ #endif /* CONFIG_MTD_PARTITIONS */ spi_sflash->mtd = mtd; ifx_spi_flash_version(); return ret; err5: amazon_s_sscFreeConnection(spi_sflash->sflash_handler); err4: kfree(mtd); err3: kfree(spi_sflash->flash_rx_org_buf); err2: kfree(spi_sflash->flash_tx_org_buf); err1: kfree(spi_sflash); done: ifx_spi_flash_gpio_release(); return ret; } static void __exit ifx_spi_flash_exit(void) { if (spi_sflash != NULL) { if (spi_sflash->parsed_parts != NULL) { del_mtd_partitions (spi_sflash->mtd); } kfree(spi_sflash->mtd); amazon_s_sscFreeConnection(spi_sflash->sflash_handler); kfree(spi_sflash->flash_rx_org_buf); kfree(spi_sflash->flash_tx_org_buf); kfree(spi_sflash); } ifx_spi_flash_gpio_release(); } module_init(ifx_spi_flash_init); module_exit(ifx_spi_flash_exit); MODULE_LICENSE ("GPL"); MODULE_AUTHOR ("Chuanhua.Lei@infineon.com"); MODULE_SUPPORTED_DEVICE ("MX25PXXX AT25FXXX W25P/Qxxx"); MODULE_DESCRIPTION ("IFAP SPI flash device driver");