/*------------------------------------------------------------------------------------------*\ * * 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_vr9.h" /*--- #define DEBUG_SPI ---*/ #ifdef DEBUG_SPI #define DBG_SPI(...) printk(__VA_ARGS__) #define DebugPrintf(...) printk(KERN_ERR __VA_ARGS__) #else #define DBG_SPI(...) #define DebugPrintf(...) #endif #define BIT(x) (1 << (x)) #define WAIT() while (SPI->STAT.Bits.bsy) #define IFX_SSC_PHY_BASE (KSEG1 | 0x1E100800) #define SPI_CS4 (10 & 0xF) /* P0.10 */ #define SPI_DIN (0 & 0xF) /* P1.0 */ #define SPI_DOUT (1 & 0xF) /* P1.1 */ #define SPI_CLK (2 & 0xF) /* P1.2 */ #define AMAZON_S_SSC_BR_BR_VALUE(value) (((( 1 << 16) - 1) & (value)) << 0) #define FLASH_BASE 0 #define FLASH_BUFFER_SIZE 256 struct _spi_register *const SPI = (struct _spi_register *)&(*(volatile unsigned int *)(IFX_SSC_PHY_BASE)); struct tffs_spi_flash_device tffs_spi_device; extern int tffs_mtd_offset[2]; #define SPI_BIG_ENDIAN /*------------------------------------------------------------------------------------------*\ \*------------------------------------------------------------------------------------------*/ static void delay(unsigned int units) { volatile unsigned int ii; for (ii = 0; ii < units; ii++); } /*------------------------------------------------------------------------------------------*\ \*------------------------------------------------------------------------------------------*/ void spi_cs(enum spi_cs cs, enum spi_cs_enum set) { switch (set) { case deselect: /*--- set CS high ---*/ SPI->FGPO = (1 << cs) << 8; break; case select: /*--- set CS low ---*/ SPI->FGPO = (1 << cs); break; } } /*------------------------------------------------------------------------------------------*\ * es muss RX & TX eingeschaltet sein, damit spi_cmd_simple funktioniert \*------------------------------------------------------------------------------------------*/ void spi_cmd_simple(unsigned int cmd) { volatile unsigned int tmp; spi_cs(IFX_SSC_CS4, select); SPI->TB.Char.Byte = cmd; while (SPI->FSTAT.Bits.rxffl < 1); tmp = SPI->RB; /*--- das gelesene Byte aus der Rx-Fifo löschen ---*/ spi_cs(IFX_SSC_CS4, deselect); delay(5); } /*-----------------------------------------------------------------------------------------*\ \*------------------------------------------------------------------------------------------*/ unsigned int spi_read_byte(unsigned int address, unsigned int mtd_id, unsigned char *pdata) { address = address - FLASH_BASE + tffs_mtd_offset[mtd_id]; SPI->CON.Bits.rxoff = 1; SPI->CON.Bits.txoff = 0; spi_cs(IFX_SSC_CS4, select); SPI->TB.Register = SPI_READ | address; WAIT(); SPI->CON.Bits.txoff = 1; SPI->CON.Bits.rxoff = 0; SPI->RXREQ = 1; /*--- 1 Byte lesen ---*/ while (SPI->FSTAT.Bits.rxffl < 1); spi_cs(IFX_SSC_CS4, deselect); SPI->CON.Bits.txoff = 0; *pdata = SPI->RB; return 1; } /*------------------------------------------------------------------------------------------*\ * 4 Byte aligned, wir kopieren immer 4 Bytes \*------------------------------------------------------------------------------------------*/ unsigned int spi_read_block(unsigned int address, unsigned int mtd_id, unsigned char *pdata, unsigned int datalen) { unsigned int len; len = datalen = datalen >> 2; address = address - FLASH_BASE + tffs_mtd_offset[mtd_id]; SPI->CON.Bits.rxoff = 1; SPI->CON.Bits.txoff = 0; spi_cs(IFX_SSC_CS4, select); SPI->TB.Register = SPI_READ | address; WAIT(); SPI->CON.Bits.txoff = 1; SPI->CON.Bits.rxoff = 0; while (len) { SPI->RXREQ = 4; /*--- 4 Byte lesen ---*/ while (SPI->FSTAT.Bits.rxffl < 1); delay(5); #if defined(SPI_BIG_ENDIAN) *(unsigned int *)pdata = SPI->RB; /*--- DBG_SPI("[spi_read_block] 0x%x\n", *(unsigned int *)pdata); ---*/ 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--; } spi_cs(IFX_SSC_CS4, deselect); SPI->CON.Bits.txoff = 0; return datalen << 2; } /*------------------------------------------------------------------------------------------*\ * aus Performacegründen kopieren wir zuerst 32bit weise, den Rest 8bit weise \*------------------------------------------------------------------------------------------*/ 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 mtd %d len %d 0x%p\n", address, mtd_id, len, pdata); while (Bytes < len) { if ((address & 3) || ((unsigned int)pdata & 3) || ((len - Bytes) < 4)) { /*--- byteweise lesen ---*/ read = spi_read_byte(address, mtd_id, pdata); DBG_SPI("[tffs_spi_read] Bytes 0x%x len %d data 0x%x\n", address, len, *pdata); } else { read_len = (len - Bytes) > ((SPI_MAX_FRAME_LEN-1)<<2) ? ((SPI_MAX_FRAME_LEN-1)<<2) : (len - Bytes); read = spi_read_block(address, mtd_id, pdata, read_len); DBG_SPI("[tffs_spi_read] Int 0x%x len %d data 0x%x\n", address, len & ~3, *(unsigned int *)pdata); } address += read; pdata += read; Bytes += read; if (read == 0) { DebugPrintf("\n", (int)address); break; } } return Bytes; } /*------------------------------------------------------------------------------------------*\ \*------------------------------------------------------------------------------------------*/ unsigned int spi_GetBlockSize(unsigned int address) { int i; unsigned int regions = 0; if((address >= FLASH_BASE) && (address < (FLASH_BASE + tffs_spi_device.Size))) { for (i=0;iTB.Short.Bytes = SPI_READ_STATUS << 8; while (SPI->FSTAT.Bits.rxffl < 1); spi_cs(IFX_SSC_CS4, deselect); return (SPI->RB & 0xFF); } /*------------------------------------------------------------------------------------------*\ \*------------------------------------------------------------------------------------------*/ unsigned int spi_write_byte(unsigned int address, unsigned int mtd_id, unsigned char *pdata) { unsigned char status; unsigned char buffer; unsigned int chip_address = address - FLASH_BASE + tffs_mtd_offset[mtd_id]; spi_cmd_simple(SPI_WRITE_ENABLE); DBG_SPI("[spi_write_byte] status 0x%x\n", spi_read_status(IFX_SSC_CS4)); SPI->CON.Bits.rxoff = 1; spi_cs(IFX_SSC_CS4, select); SPI->TB.Register = SPI_PAGE_PROGRAM | chip_address; WAIT(); SPI->TB.Char.Byte = *pdata; WAIT(); spi_cs(IFX_SSC_CS4, deselect); SPI->CON.Bits.rxoff = 0; while (1) { status = spi_read_status(IFX_SSC_CS4); if (!(status & WIP)) break; } 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, *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; unsigned int SectorSize = spi_GetBlockSize(address + tffs_mtd_offset[mtd_id]); unsigned int BufferSize; enum spi_cs cs = IFX_SSC_CS4; int i, status = 0; unsigned int chip_address = address - FLASH_BASE + tffs_mtd_offset[mtd_id]; if (tffs_spi_device.Blockmode) BufferSize = tffs_spi_device.Geometry.writeBuffer_size; else BufferSize = sizeof(unsigned short); if (!SectorSize) { DebugPrintf("[%s] Error: \n", __FUNCTION__, chip_address); 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 ((chip_address % SectorSize) && (datalen > (chip_address % SectorSize))) { datalen -= chip_address % SectorSize; } /*--- nicht über die Grenze des Writebuffers schreiben ---*/ if ((chip_address % BufferSize) && (datalen > (BufferSize - (chip_address % BufferSize)))) { datalen = (BufferSize - (chip_address % BufferSize)); } spi_cmd_simple(SPI_WRITE_ENABLE); SPI->CON.Bits.rxoff = 1; spi_cs(IFX_SSC_CS4, select); SPI->TB.Register = SPI_PAGE_PROGRAM | chip_address; WAIT(); for (i=0;i<(datalen>>2);i++) { /*--- DBG_SPI("[spi_write_block] 0x%x\n", *(unsigned int *)pdata); ---*/ SPI->TB.Register = *(unsigned int *)pData; pData++; WAIT(); } spi_cs(IFX_SSC_CS4, deselect); SPI->CON.Bits.rxoff = 0; while (1) { status = spi_read_status(cs); if (!(status & WIP) && !(status & WEL)) break; } /*--- 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 should [0] = 0x%x read [0] = 0x%x\n", datalen, address, data[0], buffer[0]); 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 buffersize = tffs_spi_device.Geometry.writeBuffer_size; DBG_SPI("[tffs_spi_write] address 0x%x mtd %d len %d pdata 0x%p\n", address, mtd_id, len, pdata); while (Bytes < len) { if ((address & 3) || ((len - Bytes) < 4)) { /*--- byteweise lesen ---*/ DBG_SPI("[tffs_spi_write] Bytes addr 0x%x pdata 0x%p\n", address, pdata); written = spi_write_byte(address, mtd_id, pdata); } else { write = (len - Bytes) > buffersize ? buffersize : (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("[%s] \n", __FUNCTION__, (int)address); break; } } return Bytes; } /*------------------------------------------------------------------------------------------*\ * initialize SSC1 module * Parameter: baudrate FPI/clock ~ FPI/clock/2^16 \*------------------------------------------------------------------------------------------*/ void spi_init(unsigned int baudrate) { /* enable SSC1 */ //*AMAZON_S_PMU_PM_GEN |= AMAZON_S_PMU_PM_GEN_EN11; /* SSC0 Ports */ /* P0.10 as CS4 for flash or eeprom depends on jumper */ /* P0.10 ALT0= 0, ALT1=1, DIR=1 */ *(IFX_GPIO_P0_DIR) |= BIT(SPI_CS4); *(IFX_GPIO_P0_ALTSEL0) &= ~ BIT(SPI_CS4); *(IFX_GPIO_P0_ALTSEL1) |= BIT(SPI_CS4); *(IFX_GPIO_P0_OD) |= BIT(SPI_CS4); /* 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 */ SPI->CLC = (1<<8); /*--- *AMAZON_S_SSC1_CLC = (1<<8); ---*/ /* Disable SSC to get access to the control bits */ SPI->WHBSTATE = 1; /*--- *AMAZON_S_SSC1_WHBSTATE = 0x1; ---*/ asm("SYNC"); /*CS4 */ SPI->GPOCON = (1<<11); /*--- *AMAZON_S_SSC1_GPOCON = (1<<11); ---*/ spi_cs(IFX_SSC_CS4, 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! \*------------------------------------------------------------------------------------------*/ DebugPrintf("{%s} FPI Clk %d CGU_CLK=0x%x\n", __FUNCTION__, cgu_get_fpi_bus_clock (2), *IFX_CGU_IF_CLK); SPI->BRT = AMAZON_S_SSC_BR_BR_VALUE(((cgu_get_fpi_bus_clock (2/*Master*/)>>1) + baudrate/2)/(baudrate) - 1); /*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"); } /*------------------------------------------------------------------------------------------*\ * auf dem Demoboard nutzen wir SERIAL_CS1 \*------------------------------------------------------------------------------------------*/ unsigned int tffs_spi_init(void) { unsigned int flash_data; spi_init(SFLASH_BAUDRATE); SPI->CON.Bits.rxoff = 0; SPI->CON.Bits.txoff = 0; spi_cs(IFX_SSC_CS4, select); SPI->TB.Register = SPI_READ_ELECTRONIC_ID_MANUFACTURE << 24; while (SPI->STAT.Bits.rxbv < 4); spi_cs(IFX_SSC_CS4, deselect); flash_data = SPI->RB; DebugPrintf("[%s] FlashID 0x%x\n", __FUNCTION__, flash_data & 0xFFFFFF); spi_read_status(IFX_SSC_CS4); switch (flash_data & 0x00FFFF00) { case ((MX_MANUFACTURE_ID << 16) + (MX_MEMORY_TYPE_SPI << 8)): DebugPrintf("[%s] Manufacturer MX\n", __FUNCTION__); tffs_spi_device.Size = 1<<(flash_data & 0xFF); tffs_spi_device.Geometry.writeBuffer_size = FLASH_BUFFER_SIZE; tffs_spi_device.Geometry.num_erase_regions = 1; tffs_spi_device.Geometry.erase_regions[0].blk_size = 0x10000; tffs_spi_device.Geometry.erase_regions[0].size = tffs_spi_device.Size; tffs_spi_device.Geometry.erase_regions[0].blk_num = tffs_spi_device.Size / 0x10000; tffs_spi_device.Blockmode = 1; break; case ((SPANSION_MANUFACTURE_ID << 16) + ( SPANSION_MEMORY_TYPE_SPI << 8)): DebugPrintf("[%s] Manufacturer SPANSION\n", __FUNCTION__); tffs_spi_device.Size = 1<<(flash_data & 0xFF); tffs_spi_device.Geometry.writeBuffer_size = FLASH_BUFFER_SIZE; tffs_spi_device.Geometry.num_erase_regions = 1; tffs_spi_device.Geometry.erase_regions[0].blk_size = 0x10000; tffs_spi_device.Geometry.erase_regions[0].size = tffs_spi_device.Size; tffs_spi_device.Geometry.erase_regions[0].blk_num = tffs_spi_device.Size / 0x10000; tffs_spi_device.Blockmode = 1; break; default: DebugPrintf("[init_spi] \n"); return FLASH_ID_FAILED; } return FLASH_SUCCESS; }