/*------------------------------------------------------------------------------------------*\ * * Copyright (C) 2007 AVM GmbH * * 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. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA \*------------------------------------------------------------------------------------------*/ /*------------------------------------------------------------------------------------------*\ * Direkte Ansteuerung des SPI-Flash im Panic Mode (Urlader-Routinen) \*------------------------------------------------------------------------------------------*/ #include #include #include #include #include #include #include #include #include #include #include "tffs_direct_spi_puma5.h" #ifdef DEBUG_SPI #define DBG_SPI(...) printk(__VA_ARGS__) #define DebugPrintf(...) printk(KERN_ERR __VA_ARGS__) #else #define DBG_SPI(...) #define DebugPrintf(...) #endif #if defined(CONFIG_ARCH_PUMA5) #define WAIT() while (SPI->status.Bits.busy) #else #define WAIT() (while (SPI->status.Bits.busy || !SPI->status.Bits.wc)) #endif /*--- Chip-spezifische Defines aus Urlader ---*/ #define FLASH_BUFFER_SIZE 256 #define FLASH_WRITE_BUFFER_SIZE FLASH_BUFFER_SIZE #define FLASH_BLOCK_SIZE 0x10000 extern int tffs_mtd_offset[2]; extern void *tffs_spi_mmap[2]; struct _spi_register *const SPI = (struct _spi_register *)&(*(volatile unsigned int *)(AVALANCHE_SPI_BASE)); #define SPI_2_FLASH #define SPI_BIG_ENDIAN /*--- gibt an wie die Daten ins SPI-Flash geschrieben werden ---*/ /*------------------------------------------------------------------------------------------*\ * 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 Worten muss die Adresse entsprechend BIG_ENDIAN ungerechnet werden \*------------------------------------------------------------------------------------------*/ void spi_cmd_simple(unsigned int cmd, unsigned int cs) { DBG_SPI("[%s] cs 0x%x\n", __FUNCTION__, cs); SPI->data.Register = cmd; SPI->cmd.Register = cs + SPI_WORD_LEN_8 + SPI_CMD_WRITE + 0; /*--- framelength = 1 ---*/ WAIT(); } /*------------------------------------------------------------------------------------------*\ * beim PUMA-Design gibt es 2 SPI-Flashs an 2 CS * anhand der Adresse wird das CS ausgerechnet \*------------------------------------------------------------------------------------------*/ unsigned int calc_cs(unsigned int address) { DBG_SPI("[%s] address 0x%x\n", __FUNCTION__, address); if (address < MM_SPI_1_PHY) return SPI_USE_CS0; else return SPI_USE_CS1; } /*------------------------------------------------------------------------------------------*\ * aus Performacegründen kopieren wir zuerst 32bit weise, den Rest 8bit weise \*------------------------------------------------------------------------------------------*/ #if defined(CONFIG_ARCH_PUMA5) /*--- beim PUMA gibt es ein memory-mapped Interface zum SPI-Flash ---*/ int tffs_spi_read(unsigned int address, unsigned int mtd_id, unsigned char *pdata, unsigned int len) { memcpy(pdata, (unsigned char *)(address + tffs_spi_mmap[mtd_id]), len); return len; } #else int tffs_spi_read(unsigned int address, unsigned int mtd_id, unsigned char *pdata, unsigned int len) { unsigned int Bytes = 0; unsigned int read, read_len; DBG_SPI("[tffs_spi_read] 0x%x len %d 0x%p\n", address, len, pdata); address &= 0xFFFFFF; /*--- die oberen Adressen sind uninteressant ---*/ while (Bytes < len) { if ((address & 3) || ((unsigned int)pdata & 3) || ((len - Bytes) < 4)) { /*--- byteweise lesen ---*/ DBG_SPI("[tffs_spi_read] Bytes 0x%x len %d\n", address, len); read = spi_read_byte(address, pdata); } else { DBG_SPI("[tffs_spi_read] Int 0x%x len %d\n", address, len & ~3); read_len = (len - Bytes) > ((SPI_MAX_FRAME_LEN-1)<<2) ? ((SPI_MAX_FRAME_LEN-1)<<2) : (len - Bytes); read = spi_read_block(address, pdata, read_len); } address += read; pdata += read; Bytes += read; if (read == 0) { DebugPrintf("\n", (int)address); break; } } return Bytes; } #endif /*------------------------------------------------------------------------------------------*\ \*------------------------------------------------------------------------------------------*/ unsigned int spi_GetBlockSize(unsigned int address) { #if 0 /*--- aus Urlader ---*/ if(((address >= MM_SPI_0_PHY) && (address < (MM_SPI_0_PHY + Flash->Device.Size))) || ((address >= MM_SPI_1_PHY) && (address < (MM_SPI_1_PHY + Flash->Device.Size)))) { return Flash->Device.Geometry.erase_regions[0].blk_size; } return(0); #endif return FLASH_BLOCK_SIZE; } /*------------------------------------------------------------------------------------------*\ \*------------------------------------------------------------------------------------------*/ unsigned char spi_read_status(unsigned int cs) { SPI->data.Register = SPI_READ_STATUS << 8; SPI->cmd.Register = cs + SPI_WORD_LEN_16 + SPI_CMD_WRITE + (0 << 0); /*--- framelength = 1 ---*/ WAIT(); return (SPI->data.Register & 0xFF); } /*------------------------------------------------------------------------------------------*\ \*------------------------------------------------------------------------------------------*/ unsigned int spi_write_byte(unsigned int address, unsigned int mtd_id, unsigned char *pdata) { unsigned int cs; unsigned char status; unsigned char buffer; cs = calc_cs(address + tffs_mtd_offset[mtd_id]); SPI->sfi_switch.Bits.mmpt_s = 0; /*--- memory-mapped SPI-Interface aus ---*/ spi_cmd_simple(SPI_WRITE_ENABLE, cs); /*--- DBG_SPI("[spi_write_byte] status 0x%x\n", spi_read_status()); ---*/ SPI->data.Register = SPI_PAGE_PROGRAM + ((address + tffs_mtd_offset[mtd_id]) & 0xFFFFFF); SPI->cmd.Register = cs + SPI_WORD_LEN_32 + SPI_CMD_WRITE + (1 << 0); /*--- framelength = 2 ---*/ WAIT(); DBG_SPI("[spi_write_byte] 0x%x\n", *pdata); SPI->data.Register = *pdata; SPI->cmd.Register = cs + SPI_WORD_LEN_8 + SPI_CMD_WRITE + (1 << 0); /*--- framelength = 2 ---*/ WAIT(); while (1) { status = spi_read_status(cs); if (!(status & WIP)) break; } SPI->sfi_switch.Bits.mmpt_s = 1; /*--- memory-mapped SPI-Interface ein ---*/ tffs_spi_read(address, mtd_id, &buffer, 1); if (buffer != *pdata) { DebugPrintf("\n: B Error: Addr 0x%x should=0x%x read=0x%x\n", address + tffs_mtd_offset[mtd_id], *pdata, buffer); return 0; } return 1; } /*------------------------------------------------------------------------------------------*\ * wir schreiben hier die Daten immer 32 Bit weise \*------------------------------------------------------------------------------------------*/ unsigned int spi_write_block(unsigned int address, unsigned int mtd_id, unsigned char *data, unsigned int datalen) { unsigned char buffer[FLASH_BUFFER_SIZE]; unsigned int *pData, *pAddr = (unsigned int *)(address + tffs_mtd_offset[mtd_id]); unsigned int SectorSize = spi_GetBlockSize(address + tffs_mtd_offset[mtd_id]); unsigned int BufferSize; unsigned int cs = calc_cs(address + tffs_mtd_offset[mtd_id]); int i, status = 0; #if 0 /*--- aus Urlader ---*/ if (Flash->Device.Blockmode) BufferSize = Flash->Device.Geometry.writeBuffer_size; else BufferSize = sizeof(unsigned short); #endif BufferSize = FLASH_WRITE_BUFFER_SIZE; if (!SectorSize) { DebugPrintf("Error: \n", address + tffs_mtd_offset[mtd_id]); return 0; } if (datalen > BufferSize) { DebugPrintf("Error: BufferSize %d>\n", BufferSize); return 0; } if ((unsigned int)data & 3) { memset(buffer, 0xff, FLASH_BUFFER_SIZE); memcpy(buffer, data, datalen); /*--- die Daten aligned in den Buffer kopieren ---*/ pData = (unsigned int *)&buffer; } else { pData = (unsigned int *)data; } /*--- 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 % BufferSize) && (datalen > (BufferSize - ((unsigned int)pAddr % BufferSize)))) { datalen = (BufferSize - ((unsigned int)pAddr % BufferSize)); } SPI->sfi_switch.Bits.mmpt_s = 0; /*--- memory-mapped SPI-Interface aus ---*/ spi_cmd_simple(SPI_WRITE_ENABLE, cs); /*--- DBG_SPI("[spi_write_block] 0x%x\n", *(unsigned int *)pdata); ---*/ SPI->data.Register = SPI_PAGE_PROGRAM + ((address + tffs_mtd_offset[mtd_id]) & 0xFFFFFF); SPI->cmd.Register = cs + SPI_WORD_LEN_32 + SPI_CMD_WRITE + (datalen >> 2); WAIT(); for (i=0;i<(datalen>>2);i++) { /*--- DBG_SPI("[spi_write_block] 0x%x\n", *(unsigned int *)pdata); ---*/ SPI->data.Register = *(unsigned int *)pData; pData++; SPI->cmd.Register = cs + SPI_WORD_LEN_32 + SPI_CMD_WRITE + (datalen >> 2); WAIT(); } while (1) { status = spi_read_status(cs); if (!(status & WIP) && !(status & WEL)) break; } SPI->sfi_switch.Bits.mmpt_s = 1; /*--- memory-mapped SPI-Interface ein ---*/ /*--- DBG_SPI("[spi_write_buffer] %d Bytes 0x%x 0x%x\n", datalen, address + tffs_mtd_offset[mtd_id], ---*/ /*--- *(volatile unsigned int *)(address + tffs_mtd_offset[mtd_id])); ---*/ /*--- zum Schluss noch alles Vergleichen ---*/ tffs_spi_read(address, mtd_id, buffer, datalen); if (memcmp((char *)buffer, (char *)data, datalen)) { #if 0 for (i=0;i: %d Error: Addr 0x%x\n", datalen, address + tffs_mtd_offset[mtd_id]); return 0; } DBG_SPI("[spi_write_block] wrote %d\n", datalen); return datalen; } /*------------------------------------------------------------------------------------------*\ \*------------------------------------------------------------------------------------------*/ int tffs_spi_write(unsigned int address, unsigned int mtd_id, unsigned char *pdata, unsigned int len) { unsigned int Bytes = 0; unsigned int written, write; unsigned int buffer_size = FLASH_BUFFER_SIZE; DBG_SPI("[tffs_spi_write] address 0x%x len %d pdata 0x%p\n", address, len, pdata); while (Bytes < len) { if ((address & 3) || ((unsigned int)pdata & 3) || ((len - Bytes) < 4)) { /*--- byteweise lesen ---*/ DBG_SPI("[tffs_spi_write] Bytes addr 0x%x\n", address); written = spi_write_byte(address, mtd_id, pdata); } else { write = (len - Bytes) > buffer_size ? buffer_size : (len - Bytes); write &= ~3; DBG_SPI("[tffs_spi_write] Integer 0x%x len %d\n", address, write); written = spi_write_block(address, mtd_id, pdata, write); } address += written; pdata += written; Bytes += written; if (written == 0) { DebugPrintf("\n", (int)address); break; } } return Bytes; } /*------------------------------------------------------------------------------------------*\ \*------------------------------------------------------------------------------------------*/ unsigned int tffs_spi_init(void) { #if defined(DEBUG_SPI) && 0 SPI->clk_ctrl.Bits.dclk_div = 255; /*--- 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; SPI->sfi_switch.Bits.mmpt_s = 0; /*--- memory-mapped SPI-Interface aus ---*/ /*--- write REMS ---*/ SPI->data.Register = SPI_READ_ELECTRONIC_ID_MANUFACTURE; SPI->cmd.Register = SPI_USE_CS0 + SPI_WORD_LEN_8 + SPI_CMD_WRITE + (1 << 0); /*--- framelength = 1 ---*/ WAIT(); /*--- read ---*/ SPI->cmd.Register = SPI_USE_CS0 + SPI_WORD_LEN_24 + SPI_CMD_READ + (1 << 0); /*--- framelength = 1 ---*/ WAIT(); #if defined(SPI_2_FLASH) SPI->data.Register = SPI_READ_ELECTRONIC_ID_MANUFACTURE; SPI->cmd.Register = SPI_USE_CS1 + SPI_WORD_LEN_8 + SPI_CMD_WRITE + (1 << 0); /*--- framelength = 1 ---*/ WAIT(); /*--- read ---*/ SPI->cmd.Register = SPI_USE_CS1 + SPI_WORD_LEN_24 + SPI_CMD_READ + (1 << 0); /*--- framelength = 1 ---*/ WAIT(); #endif SPI->sfi_switch.Bits.mmpt_s = 1; /*--- memory-mapped SPI-Interface ein ---*/ return 0; }