/* * Copyright (C) 2002 by egnite Software GmbH. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holders nor the names of * contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * For additional information see http://www.ethernut.de/ * */ /*! * \file arch/avr/dev/spiflash.c * \brief AVR SPI flash support. * * \verbatim * $Id: spiflash.c 4706 2012-10-06 17:42:01Z haraldkipp $ * \endverbatim */ #include #include #include #include #include #include #include /*! * \addtogroup xgSpiFlash */ /*@{*/ /*! * \brief Exchange SPI byte. */ #ifdef __GNUC__ static __inline uint8_t SpiByte(uint8_t c) #else static uint8_t SpiByte(uint8_t c) #endif { outb(SPDR, c); loop_until_bit_is_set(SPSR, SPIF); return inb(SPDR); } /*! * \brief Enable SPI device flash programming. * * \return 0 if device could be located, -1 otherwise. */ int SpiFlashEnable(void) { uint8_t i; uint8_t rc; /* * PB0(O): SS * PB1(O): SCK * PB2(O): MOSI * PB3(I): MISO * * PB4(O): Reset target * PB5(-): Unused * PB6(-): Unused * PB7(-): Unused */ /* * SCK and MOSI outputs need configuration, * even if SPI mode is enabled. */ cbi(PORTB, 1); sbi(DDRB, 1); cbi(PORTB, 2); sbi(DDRB, 2); /* * Enable pull-up on MISO. */ sbi(PORTB, 3); for (i = 0; i < 32; i++) { /* * Set reset low. */ cbi(PORTB, 4); sbi(DDRB, 4); /* * Set slave select pin to output. Otherwise a low signal * on this pin might force us to SPI slave mode. */ sbi(DDRB, 0); outb(SPCR, BV(MSTR) | BV(SPE) | BV(SPR0)); /* * Try to enable programming. */ SpiByte(0xAC); SpiByte(0x53); rc = SpiByte(0xFF); SpiByte(0xff); if (rc == 0x53) return 0; /* * Programming enable failed. This may be because the * target is not synchronized. A positive pulse on the * clock line should help. */ outb(SPCR, 0); sbi(PORTB, 1); cbi(PORTB, 1); } return -1; } /*! * Read SPI device ID. * * \param id Three byte character array, which receives * the CPU ID. */ void SpiFlashId(uint8_t * id) { uint8_t i; for (i = 0; i < 3; i++) { SpiByte(0x30); SpiByte(0x00); SpiByte(i); id[i] = SpiByte(0x00); } } /*! * \brief Write byte to the target's flash memory. * * The target must have been erased by a previous call * to SpiFlashErase(). * * \param high Must be 0 to write the low byte or 8 to * write the high byte. * \param addr Word address to write to. * \param data Byte value to write. * * \return 0 on success, -1 otherwise. */ int SpiFlashWriteByte(uint8_t high, uint16_t addr, uint8_t data) { uint8_t d; if (data != 0xff) { SpiByte(0x40 | high); SpiByte(addr >> 8); SpiByte(addr & 0xFF); SpiByte(data); /* * During programming a value of 0x7F appears at the memory location. * If we are programming this value, we delay execution by 10 ms. * Otherwise we poll the memory location until we read back the * programmed value. */ if (data == 0x7f) NutDelay(10); else { for (d = 0; d < 255; d++) { /* * Read program flash byte. */ SpiByte(0x20 | high); SpiByte(addr >> 8); SpiByte(addr & 0xFF); if (SpiByte(0xFF) == data) break; } if (d == 255) return -1; } } return 0; } /*! * \brief Write word to the target's flash memory. * * \param addr Word address to write to. * \param data Word value to write. * * \return 0 on success, -1 otherwise. */ int SpiFlashWriteWord(uint16_t addr, uint16_t data) { if (SpiFlashWriteByte(0, addr, data & 0xFF)) return -1; if (SpiFlashWriteByte(8, addr, data >> 8)) return -1; return 0; } /*! * \brief Erase target's flash memory. * * Sets all bytes on the target's flash memory to 0xFF. * In addtion all lock bits are set to 1 (unprogrammed). */ void SpiFlashErase(void) { /* * Send chip erase command. */ SpiByte(0xAC); SpiByte(0x80); SpiByte(0x00); SpiByte(0x00); NutDelay(25); } /*@}*/