/* Common Flash Interface probe code. (C) 2000 Red Hat. GPL'd. $Id: cfi_probe.c,v 1.83 2004/11/16 18:19:02 nico Exp $ */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include /*------------------------------------------------------------------------------------------*\ * Definitionen SPI-Flash * nach einigen Komandos muss CE wieder high sein -> deshalb gleich (x << 24) * gesetzt werden diese mit spi_cmd_simple \*------------------------------------------------------------------------------------------*/ #define WRITE_ENABLE (0x06 << 24) /*--- write enable ---*/ #define WRITE_DISABLE (0x04 << 24) /*--- write disable ---*/ #define READ_ID (0x9F << 24) /*--- read identification ---*/ #define READ_STATUS (0x05 << 24) /*--- read status register ---*/ #define WRITE_STATUS (0x01 << 24) /*--- write status register ---*/ #define READ (0x03 << 24) /*--- read data ---*/ #define FASTREAD (0x0B << 24) /*--- fast read data ---*/ #define PARALLEL_MODE (0x55 << 24) /*--- parallel mode ---*/ #define SECTOR_ERASE (0x20 << 24) /*--- sector erase ---*/ #define BLOCK_ERASE (0xD8 << 24) /*--- block erase ---*/ #define CHIP_ERASE (0x60 << 24) /*--- chip erase ---*/ #define PAGE_PROGRAM (0x02 << 24) /*--- page program ---*/ #define DEEP_POWER_DOWN (0xB9 << 24) /*--- deep power down ---*/ #define ENTER_4K (0xA5 << 24) /*--- enter 4kb ---*/ #define EXIT_4K (0xB5 << 24) /*--- exit 4kb ---*/ #define RELEASE_POWER_DOWN (0xAB << 24) /*--- release from deep power-down ---*/ #define READ_ELECTRONIK_ID REALEASE_POWER_DOWN /*--- read electronic id ---*/ #define READ_ELECTRONIC_ID_MANUFACTURE 0x90 /*--- read electronic manufacturer id & device id ---*/ /*--- status bits ---*/ #define WIP (1 << 0) #define WEL (1 << 1) #define BP0 (1 << 2) #define BP1 (1 << 3) #define BP2 (1 << 4) #define BP3 (1 << 5) #define PROG_ERROR (1 << 6) #define REG_WRITE_PROTECT (1 << 7) /*--- #define DEBUG_SPI ---*/ #if defined(DEBUG_SPI) #define DBG_SPI(...) printk(KERN_NOTICE __VA_ARGS__) #else #define DBG_SPI(...) #endif #define SPI_BIG_ENDIAN #define MX_DEVICE_ID_64MB_SPI 0x16 /*--- MX25L6405 ---*/ #define MX_MANUFACTURE_ID 0xC2 #define SPI_FLASH_BUFFER_SIZE 256 /*------------------------------------------------------------------------------------------*\ \*------------------------------------------------------------------------------------------*/ volatile struct _spi_register *const SPI = (struct _spi_register *)&(*(volatile unsigned int *)(UR8_SPI_BASE)); struct mx25l6405 { struct semaphore lock; struct mtd_info mtd; }; #define SPI_BIG_ENDIAN /*------------------------------------------------------------------------------------------*\ * Der UR8 Bootcode liest 32Bit Worte in den internen RAM und führt diese aus. * 32Bit Worte kommen in BIG_ENDIAN im Dataregister an - wir schreiben auch BIG_ENDIAN * bei 8Bit muss die Adresse entsprechend BIG_ENDIAN berechnet werden \*------------------------------------------------------------------------------------------*/ void spi_cmd_simple(unsigned int cmd) { SPI->data.Register = cmd; SPI->cmd.Register = SPI_USE_CS0 + SPI_WORD_LEN_8 + SPI_CMD_WRITE + 0; /*--- framelength = 1 ---*/ while (SPI->status.Bits.busy || !SPI->status.Bits.wc); } /*------------------------------------------------------------------------------------------*\ \*------------------------------------------------------------------------------------------*/ unsigned int spi_read_byte(unsigned int address, unsigned char *pdata) { #if defined(SPI_BIG_ENDIAN) SPI->data.Register = READ + (address ^ 3); #else SPI->data.Register = READ + address; #endif SPI->cmd.Register = SPI_USE_CS0 + SPI_WORD_LEN_32 + SPI_CMD_WRITE + 1; /*--- framelength = 2 ---*/ while (SPI->status.Bits.busy || !SPI->status.Bits.wc); /*--- Kommando absetzen ---*/ SPI->cmd.Register = SPI_USE_CS0 + SPI_WORD_LEN_8 + SPI_CMD_READ + 1; /*--- framelength = 2 ---*/ while (SPI->status.Bits.busy || !SPI->status.Bits.wc); /*--- Byte lesen ---*/ *pdata = SPI->data.Byte[0]; return 1; } /*------------------------------------------------------------------------------------------*\ * 4 Byte aligned, wir kopieren immer 4 Bytes \*------------------------------------------------------------------------------------------*/ unsigned int spi_read_block(unsigned int address, unsigned char *pdata, unsigned int datalen) { unsigned int len; len = datalen = datalen >> 2; SPI->data.Register = READ + address; SPI->cmd.Register = SPI_USE_CS0 + SPI_WORD_LEN_32 + SPI_CMD_WRITE + len; /*--- (len - 1) + 1 ist Framelen ---*/ while (SPI->status.Bits.busy || !SPI->status.Bits.wc); while (len) { SPI->cmd.Register = SPI_USE_CS0 + SPI_WORD_LEN_32 + SPI_CMD_READ + len; while (SPI->status.Bits.busy || !SPI->status.Bits.wc); /*--- DBG_SPI("[spi_read_block] 0x%x\n", SPI->data); ---*/ #if defined(SPI_BIG_ENDIAN) *(unsigned int *)pdata = SPI->data.Register; pdata += 4; #else *pdata++ = SPI->data.Byte[3]; /*--- da wir 4 Byte lesen, ist LSB im MSB ---*/ *pdata++ = SPI->data.Byte[2]; *pdata++ = SPI->data.Byte[1]; *pdata++ = SPI->data.Byte[0]; #endif len--; } return datalen << 2; } /*------------------------------------------------------------------------------------------*\ * aus Performacegründen kopieren wir zuerst 32bit weise, den Rest 8bit weise * int spi_read(unsigned int address, unsigned char *pdata, unsigned int len) { \*------------------------------------------------------------------------------------------*/ static int spi_read(struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char *buf) { struct map_info *map = (struct map_info *)mtd->priv; struct mx25l6405 *flash = (struct mx25l6405 *)map->fldrv_priv; unsigned int read, read_len; unsigned char *buffer = buf; down(&flash->lock); DBG_SPI("[spi_read] 0x%x len %d 0x%p ", (unsigned int)from, len, buffer); *retlen = 0; from &= 0xFFFFFF; /*--- die oberen Adressen sind uninteressant ---*/ while (*retlen < len) { if ((from & 3) || ((unsigned int)buffer & 3) || ((len - *retlen) < 4)) { /*--- byteweise lesen ---*/ DBG_SPI("[spi_read] *retlen 0x%x len %d\n", (unsigned int)from, len); read = spi_read_byte(from, buffer); } else { DBG_SPI("[spi_read] Int 0x%x len %d\n", (unsigned int)from, len & ~3); read_len = (len - *retlen) > ((SPI_MAX_FRAME_LEN-1)<<2) ? ((SPI_MAX_FRAME_LEN-1)<<2) : (len - *retlen); read = spi_read_block(from, buffer, read_len); } from += read; buffer += read; *retlen += read; if (read == 0) { printk(KERN_NOTICE "\n", (unsigned int)from); break; } } DBG_SPI("retlen %d\n", *retlen); up(&flash->lock); return 0; } /*------------------------------------------------------------------------------------------*\ \*------------------------------------------------------------------------------------------*/ unsigned char spi_read_status(void) { SPI->data.Register = READ_STATUS; SPI->cmd.Register = SPI_USE_CS0 + SPI_WORD_LEN_16 + SPI_CMD_WRITE + (0 << 0); /*--- framelength = 1 ---*/ while (SPI->status.Bits.busy || !SPI->status.Bits.wc); return (SPI->data.Register & 0xFF); } /*------------------------------------------------------------------------------------------*\ \*------------------------------------------------------------------------------------------*/ unsigned int spi_write_byte(unsigned int address, const unsigned char *pdata) { unsigned char status; spi_cmd_simple(WRITE_ENABLE); DBG_SPI("[spi_write_byte] status 0x%x\n", spi_read_status()); #if defined(SPI_BIG_ENDIAN) SPI->data.Register = PAGE_PROGRAM + (address ^ 3); #else SPI->data.Register = PAGE_PROGRAM + address; #endif SPI->cmd.Register = SPI_USE_CS0 + SPI_WORD_LEN_32 + SPI_CMD_WRITE + (1 << 0); /*--- framelength = 2 ---*/ while (SPI->status.Bits.busy || !SPI->status.Bits.wc); DBG_SPI("[spi_write_byte] 0x%x\n", *pdata); SPI->data.Register = *pdata << 24; SPI->cmd.Register = SPI_USE_CS0 + SPI_WORD_LEN_8 + SPI_CMD_WRITE + (1 << 0); /*--- framelength = 2 ---*/ while (SPI->status.Bits.busy || !SPI->status.Bits.wc); while (1) { status = spi_read_status(); if (status & PROG_ERROR) { return 0; } if (!(status & WIP)) break; } return 1; } /*------------------------------------------------------------------------------------------*\ * wir schreiben hier die Daten immer 32 Bit weise * SPI_FLASH_BUFFER_SIZE == 256 (bei unserem Flash) \*------------------------------------------------------------------------------------------*/ unsigned int spi_write_block(unsigned int address, const unsigned char *data, unsigned int datalen) { const unsigned char *pdata = data; unsigned int *pAddr = (unsigned int *)address; unsigned int SectorSize = 0x10000; int i, status = 0; if (!SectorSize) { printk(KERN_NOTICE "Error: \n", address); return 0; } if (datalen > SPI_FLASH_BUFFER_SIZE) { printk(KERN_NOTICE "Error: SPI_FLASH_BUFFER_SIZE>\n"); return 0; } /*--- wir dürfen nicht über eine Sectorgrenze schreiben ---*/ if (((unsigned int)pAddr % SectorSize) && (datalen > ((unsigned int)pAddr % SectorSize))) { datalen -= (unsigned int)pAddr % SectorSize; } /*--- nicht über die Grenze des Writebuffers schreiben ---*/ if (((unsigned int)pAddr % SPI_FLASH_BUFFER_SIZE) && (datalen > (SPI_FLASH_BUFFER_SIZE - ((unsigned int)pAddr % SPI_FLASH_BUFFER_SIZE)))) { datalen = (SPI_FLASH_BUFFER_SIZE - ((unsigned int)pAddr % SPI_FLASH_BUFFER_SIZE)); } spi_cmd_simple(WRITE_ENABLE); SPI->data.Register = PAGE_PROGRAM + address; SPI->cmd.Register = SPI_USE_CS0 + SPI_WORD_LEN_32 + SPI_CMD_WRITE + (datalen >> 2); while (SPI->status.Bits.busy || !SPI->status.Bits.wc); for (i=0;i<(datalen>>2);i++) { /*--- DBG_SPI("[spi_write_block] 0x%x\n", *(unsigned int *)pdata); ---*/ #if defined(SPI_BIG_ENDIAN) SPI->data.Register = *(unsigned int *)pdata; pdata += 4; #else SPI->data.Byte[3] = *pdata++; SPI->data.Byte[2] = *pdata++; SPI->data.Byte[1] = *pdata++; SPI->data.Byte[0] = *pdata++; #endif SPI->cmd.Register = SPI_USE_CS0 + SPI_WORD_LEN_32 + SPI_CMD_WRITE + (datalen >> 2); while (SPI->status.Bits.busy || !SPI->status.Bits.wc); } while (1) { status = spi_read_status(); if (status & PROG_ERROR) { spi_cmd_simple(WRITE_DISABLE); return 0; } if (!(status & WIP)) break; } DBG_SPI("[spi_write_block] wrote %d\n", datalen); return datalen; } /*------------------------------------------------------------------------------------------*\ * int spi_write(unsigned int address, unsigned char *pdata, unsigned int len) { \*------------------------------------------------------------------------------------------*/ static int spi_write(struct mtd_info *mtd, loff_t to, size_t len, size_t *retlen, const u_char *buf) { struct map_info *map = (struct map_info *)mtd->priv; struct mx25l6405 *flash = (struct mx25l6405 *)map->fldrv_priv; unsigned int written, write; down(&flash->lock); *retlen = 0; to &= 0xFFFFFF; DBG_SPI("[spi_write] to 0x%x len %d buf 0x%p\n", (unsigned int)to, len, buf); while (*retlen < len) { if ((to & 3) || ((unsigned int)buf & 3) || ((len - *retlen) < 4)) { /*--- byteweise lesen ---*/ DBG_SPI("[spi_write] *retlen addr 0x%x\n", (unsigned int)to); written = spi_write_byte((unsigned int)to, buf); } else { write = (len - *retlen) > SPI_FLASH_BUFFER_SIZE ? SPI_FLASH_BUFFER_SIZE : (len - *retlen); write &= ~3; DBG_SPI("[spi_write] Int 0x%x len %d\n", (unsigned int)to, write); written = spi_write_block((unsigned int)to, buf, write); } to += written; buf += written; *retlen += written; if (written == 0) { printk(KERN_NOTICE "\n", (unsigned int)to); break; } } up(&flash->lock); return 0; } /*------------------------------------------------------------------------------------------*\ * unsigned int spi_erase_cmd(volatile unsigned int address) \*------------------------------------------------------------------------------------------*/ int spi_erase(struct mtd_info *mtd, struct erase_info *instr) { struct map_info *map = (struct map_info *)mtd->priv; struct mx25l6405 *flash = (struct mx25l6405 *)map->fldrv_priv; unsigned char status; unsigned int ret = 0; unsigned int address = instr->addr; unsigned int len = instr->len; unsigned int erased = 0; down(&flash->lock); while (len > erased) { spi_cmd_simple(WRITE_ENABLE); /*--- WRITE_ENABLE muss nicht gelöscht werden ---*/ DBG_SPI("[spi_erase_cmd] 0x%x len %d\n", address, len); address &= 0xFFFFFF; SPI->data.Register = BLOCK_ERASE + address; SPI->cmd.Register = SPI_USE_CS0 + SPI_WORD_LEN_32 + SPI_CMD_WRITE + (0 << 0); /*--- framelength = 1 ---*/ while (SPI->status.Bits.busy || !SPI->status.Bits.wc); while (1) { status = spi_read_status(); if (status & PROG_ERROR) { ret = 1; spi_cmd_simple(WRITE_DISABLE); break; } if (!(status & WIP)) break; } address += mtd->erasesize; erased += mtd->erasesize; } up(&flash->lock); return ret; } /*------------------------------------------------------------------------------------------*\ * auf dem Demoboard nutzen wir SERIAL_CS1 \*------------------------------------------------------------------------------------------*/ static struct resource spi_resource_pins = { .name = "spi-flash (func)", .flags = IORESOURCE_IO, .start = 12, .end = 14 }; static struct resource spi_resource_cs0 = { .name = "spi-flash-CS0 (func)", .flags = IORESOURCE_IO, .start = 10, .end = 10 }; struct mtd_info *init_spi_flash(struct map_info *map) { volatile struct _hw_clock *Clock = (struct _hw_clock *)UR8_CLOCK_BASE; volatile union _hw_non_reset *Reset = (union _hw_non_reset *)UR8_RESET_BASE; struct mx25l6405 *flash; struct mtd_info *mtd = NULL; unsigned int ret; printk("[init_spi_flash]\n"); ret = request_resource(&gpio_resource, &spi_resource_pins); if (ret) { printk("[init_spi] \n", ret); return 0; } ret = request_resource(&gpio_resource, &spi_resource_cs0); if (ret) { printk("[init_spi] \n", ret); return 0; } ur8_gpio_ctrl(10, FUNCTION_PIN, 0); ur8_gpio_ctrl(12, FUNCTION_PIN, 0); ur8_gpio_ctrl(13, FUNCTION_PIN, 0); ur8_gpio_ctrl(14, FUNCTION_PIN, 0); Clock->PDCR.Bits.spip = 0; /*--- disable Powerdown SPI ---*/ Reset->Bits.spi_unreset = 1; /*--- unreset SPI ---*/ #if defined(DEBUG_SPI) SPI->clk_ctrl.Bits.dclk_div = 300; /*--- zum debuggen etwas langsamer, der Ossi ist nicht so schnell ---*/ #else SPI->clk_ctrl.Bits.dclk_div = 2; /*--- spi -clock max. 3x modulefrequency 60/3 = 20MHz ---*/ #endif SPI->clk_ctrl.Bits.enable = 1; /*--- write REMS ---*/ SPI->data.Register = READ_ELECTRONIC_ID_MANUFACTURE << 24; SPI->cmd.Register = SPI_USE_CS0 + SPI_WORD_LEN_32 + SPI_CMD_READ + (1 << 0); /*--- framelength = 1 ---*/ while (SPI->status.Bits.busy || !SPI->status.Bits.wc); /*--- read ---*/ SPI->cmd.Register = SPI_USE_CS0 + SPI_WORD_LEN_16 + SPI_CMD_READ + (1 << 0); /*--- framelength = 1 ---*/ while (SPI->status.Bits.busy || !SPI->status.Bits.wc); DBG_SPI("SPI->data.Register 0x%x\n", SPI->data.Register); if (SPI->data.Register == ((MX_MANUFACTURE_ID << 8) + MX_DEVICE_ID_64MB_SPI)) { flash = kmalloc(sizeof(*flash), GFP_KERNEL); if (!flash) { printk(KERN_WARNING "Failed to allocate memory for spi-Flash device\n"); return NULL; } memset(flash, 0, sizeof(*flash)); init_MUTEX(&flash->lock); map->fldrv_priv = flash; mtd = kmalloc(sizeof(*mtd), GFP_KERNEL); if (!mtd) { printk(KERN_WARNING "Failed to allocate memory for MTD device\n"); return NULL; } memset(mtd, 0, sizeof(*mtd)); mtd->priv = map; mtd->type = MTD_SPIFLASH; mtd->size = 0x800000; mtd->erasesize = 0x10000; mtd->numeraseregions = 1; mtd->eraseregions = kmalloc(sizeof(struct mtd_erase_region_info) * mtd->numeraseregions, GFP_KERNEL); if (!mtd->eraseregions) { printk(KERN_WARNING "Failed to allocate memory for MTD erase region info\n"); goto setup_err; } mtd->eraseregions[0].offset = 0; mtd->eraseregions[0].erasesize = 0x10000; mtd->eraseregions[0].numblocks = 128; mtd->writesize = 1; /* Fill in the default mtd operations */ mtd->erase = spi_erase; mtd->write = spi_write; mtd->read = spi_read; /*--- mtd->sync = spi_sync; ---*/ /*--- mtd->suspend = spi_suspend; ---*/ /*--- mtd->resume = spi_resume; ---*/ mtd->flags = MTD_CAP_NORFLASH; mtd->name = map->name; printk(KERN_NOTICE "[spi-flash] done\n"); __module_get(THIS_MODULE); return mtd; } setup_err: if (mtd) { if (mtd->eraseregions) kfree(mtd->eraseregions); kfree(mtd); } printk("[init_spi] \n"); return NULL; } /*------------------------------------------------------------------------------------------*\ * init spi, search spi-flash, fill mtd_info \*------------------------------------------------------------------------------------------*/ struct mtd_info *init_spi_flash(struct map_info *map); struct mtd_info *spi_probe(struct map_info *map) { return init_spi_flash(map); } static struct mtd_chip_driver spi_chipdrv = { .probe = spi_probe, .name = "spi_probe", .module = THIS_MODULE }; int __init spi_probe_init(void) { register_mtd_chip_driver(&spi_chipdrv); return 0; } static void __exit spi_probe_exit(void) { unregister_mtd_chip_driver(&spi_chipdrv); } module_init(spi_probe_init); module_exit(spi_probe_exit); MODULE_LICENSE("GPL"); MODULE_AUTHOR("Haiko Schillert "); MODULE_DESCRIPTION("spi chip driver for mtd flashs");