/* $Id: * * Portions of this program which I authored may be used for any purpose * so long as this notice is left intact. * * 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. */ #include "flashcfm.h" #include "flash_filter.h" #include #if HOST_FLASHING # include # include # include #endif #define MCF_CFM_CFMCLKD_DIVLD (0x80) #define MCF_CFM_CFMUSTAT_BLANK (0x04) #define MCF_CFM_CFMUSTAT_CCIF (0x40) #define MCF_CFM_CFMUSTAT_CBEIF (0x80) #define MCF_CFM_CFMCMD_BLANK_CHECK (0x5) #define MCF_CFM_CFMCMD_PAGE_ERASE_VERIFY (0x6) #define MCF_CFM_CFMCMD_WORD_PROGRAM (0x20) #define MCF_CFM_CFMCMD_PAGE_ERASE (0x40) #define MCF_CFM_CFMCMD_MASS_ERASE (0x41) /* * Warning! do not change this struct without changing the download_struct * function. */ typedef struct { uint32_t backdoor_address_start; uint32_t backdoor_address_end; uint32_t flash_address; uint32_t flash_size; uint32_t ipsbar; uint32_t cfmclkd; uint32_t cfmustat; uint32_t cfmcmd; } chiptype_t; #if HOST_FLASHING static inline uint8_t read_byte(uint32_t a_addr) { unsigned char result; if (bdmReadByte(a_addr, &result) < 0) fprintf(stderr, "read_byte(0x%08x): %s\n", a_addr, bdmErrorString()); return (uint8_t) result; } static inline uint16_t read_word(uint32_t a_addr) { unsigned short result; if (bdmReadWord(a_addr, &result) < 0) fprintf(stderr, "read_word(0x%08x): %s\n", a_addr, bdmErrorString()); return (uint16_t) result; } static inline uint32_t read_longword(uint32_t a_addr) { unsigned long result; if (bdmReadLongWord(a_addr, &result) < 0) fprintf(stderr, "read_longword(0x%08x): %s\n", a_addr, bdmErrorString()); return (uint32_t) result; } static inline void write_byte(uint32_t a_addr, uint8_t a_x) { if (bdmWriteByte(a_addr, a_x) < 0) fprintf(stderr, "write_byte(0x%08x): %s\n", a_addr, bdmErrorString()); } static inline void write_word(uint32_t a_addr, uint16_t a_x) { if (bdmWriteWord(a_addr, a_x) < 0) fprintf(stderr, "write_word(0x%08x): %s\n", a_addr, bdmErrorString()); } static inline void write_longword(uint32_t a_addr, uint32_t a_x) { if (bdmWriteLongWord(a_addr, a_x) < 0) fprintf(stderr, "write_longword(0x%08x): %s\n", a_addr, bdmErrorString()); } #else # define read_byte(A) (*((volatile uint8_t *) (A))) # define read_word(A) (*((volatile uint16_t *) (A))) # define read_longword(A) (*((volatile uint32_t *) (A))) # define write_byte(A, B) *((volatile uint8_t *)(A)) = (B) # define write_word(A, B) *((volatile uint16_t *)(A)) = (B) # define write_longword(A, B) *((volatile uint32_t *)(A)) = (B) #endif static inline uint32_t swap32(uint32_t a_v) { return (uint32_t) (((a_v >> 24) & 0x000000ff) | ((a_v << 24) & 0xff000000) | ((a_v >> 8) & 0x0000ff00) | ((a_v << 8) & 0x00ff0000)); } static inline int is_little_endian(void) { uint32_t i = 1; return (int) *((uint8_t *) &i); } /* We need a unique symbol to associate a (target-based) plugin with its (host-based) driver. */ static char driver_magic[] = "flashcfm"; #if HOST_FLASHING static char * prog_entry(void) { return "flashcfm_prog"; } #endif /* The actual programming function. * NOTE: pos and cnt need to be 4 byte aligned! */ static uint32_t flashcfm_prog(void *chip_descr, uint32_t pos, unsigned char *data, uint32_t cnt) { chiptype_t *ct = (chiptype_t *) chip_descr; uint32_t offset = ct->backdoor_address_start + (pos - ct->flash_address); uint32_t n = 0; cnt /= 4; if (read_byte(ct->cfmclkd) & MCF_CFM_CFMCLKD_DIVLD) { while (!(read_byte(ct->cfmustat) & MCF_CFM_CFMUSTAT_CBEIF)) { } for (n = 0; n < cnt; ++n) { uint32_t d = ((uint32_t *) data)[n]; /* write_longword on host assumes host endianess so will perform a * byte order swap if host is little endian. we need to swap if little * endian such that the swap will just swap back to the correct endianess */ if(is_little_endian()) d = swap32(d); write_longword(offset + n * 4, d); write_byte(ct->cfmcmd, MCF_CFM_CFMCMD_WORD_PROGRAM); write_byte(ct->cfmustat, MCF_CFM_CFMUSTAT_CBEIF); if (read_byte(ct->cfmustat) & MCF_CFM_CFMUSTAT_CBEIF) { continue; } while (!(read_byte(ct->cfmustat) & MCF_CFM_CFMUSTAT_CCIF)) { } } } return n * 4; } /* Initiate erase operation. Sector address is relative to chip-base. With sector address==-1, the whole chip is erased. The erase operation can be called several times before flash_29_erase_wait() is called for simultanous erasure of multiple sectors. */ static void flashcfm_erase(void *chip_descr, int32_t sector_address) { chiptype_t *ct = (chiptype_t *) chip_descr; if (read_byte(ct->cfmclkd) & MCF_CFM_CFMCLKD_DIVLD) { while (!(read_byte(ct->cfmustat) & MCF_CFM_CFMUSTAT_CBEIF)) { } if (sector_address == -1) { write_longword(ct->backdoor_address_start, 0); write_byte(ct->cfmcmd, MCF_CFM_CFMCMD_MASS_ERASE); } else { write_longword(ct->backdoor_address_start + (sector_address - ct->flash_address), 0); write_byte(ct->cfmcmd, MCF_CFM_CFMCMD_PAGE_ERASE); } write_byte(ct->cfmustat, MCF_CFM_CFMUSTAT_CBEIF); } } /* Blank check operation. Sector address is relative to chip-base. With sector address==-1, the whole chip is checked. */ static int flashcfm_blank_chk(void *chip_descr, int32_t sector_address) { chiptype_t *ct = (chiptype_t *) chip_descr; int result = 0; if (read_byte(ct->cfmclkd) & MCF_CFM_CFMCLKD_DIVLD) { while (!(read_byte(ct->cfmustat) & MCF_CFM_CFMUSTAT_CBEIF)) { } if (sector_address == -1) { write_longword(ct->backdoor_address_start, 0); write_byte(ct->cfmcmd, MCF_CFM_CFMCMD_BLANK_CHECK); } else { write_longword(ct->backdoor_address_start + (sector_address - ct->flash_address), 0); write_byte(ct->cfmcmd, MCF_CFM_CFMCMD_PAGE_ERASE_VERIFY); } write_byte(ct->cfmustat, MCF_CFM_CFMUSTAT_CBEIF); while (!(read_byte(ct->cfmustat) & MCF_CFM_CFMUSTAT_CCIF)) { } if (read_byte(ct->cfmustat) & MCF_CFM_CFMUSTAT_BLANK) { result = 1; write_byte(ct->cfmustat, MCF_CFM_CFMUSTAT_BLANK); } } return result; } /* wait for queued erasing operations to finish */ static int flashcfm_erase_wait(void *chip_descr) { chiptype_t *ct = (chiptype_t *) chip_descr; #if HOST_FLASHING int spin = 0; #endif if (read_byte(ct->cfmclkd) & MCF_CFM_CFMCLKD_DIVLD) { while (!(read_byte(ct->cfmustat) & MCF_CFM_CFMUSTAT_CCIF)) { #if HOST_FLASHING spin = flash_spin(spin); #endif } return 1; } return 0; } #if HOST_FLASHING /* autodetect */ static uint32_t flashcfm_search_chip(void *chip_descr, char *description, uint32_t pos) { chiptype_t *ct = (chiptype_t *) chip_descr; unsigned long flashbar = 0; uint32_t fbar_reg; flash_get_var("%flashbar", &fbar_reg, 0x0c04); if (bdmReadControlRegister(fbar_reg, &flashbar) >= 0) { if ((flashbar & 1) && ((flashbar & 0xfff80000) == pos)) { flash_get_var("IPSBAR", &ct->ipsbar, 0x40000000); flash_get_var("FLASH_BACKDOOR", &ct->backdoor_address_start, 0x04000000); flash_get_var("FLASH_SIZE", &ct->flash_size, 256 * 1024); ct->backdoor_address_start += ct->ipsbar; ct->backdoor_address_end = ct->backdoor_address_start + ct->flash_size; flash_get_var("MCF_CFM_CFMCLKD", &ct->cfmclkd, 0x1D0002); flash_get_var("MCF_CFM_CFMUSTAT", &ct->cfmustat, 0x1D0020); flash_get_var("ct->cfmcmd", &ct->cfmcmd, 0x1D0024); ct->cfmclkd += ct->ipsbar; ct->cfmustat += ct->ipsbar; ct->cfmcmd += ct->ipsbar; ct->flash_address = pos; if (description) { sprintf(description, "Coldfire flash module @ 0x%08lx..0x%08lx " "size:0x%08lx", pos, pos + ct->flash_size, ct->flash_size); } return ct->flash_size; } } return 0; } static int download_struct(void *chip_descr, uint32_t adr) { chiptype_t *ct = (chiptype_t *) chip_descr; bdmWriteLongWord(adr, ct->backdoor_address_start); adr += 4; bdmWriteLongWord(adr, ct->backdoor_address_end); adr += 4; bdmWriteLongWord(adr, ct->flash_address); adr += 4; bdmWriteLongWord(adr, ct->flash_size); adr += 4; bdmWriteLongWord(adr, ct->ipsbar); adr += 4; bdmWriteLongWord(adr, ct->cfmclkd); adr += 4; bdmWriteLongWord(adr, ct->cfmustat); adr += 4; bdmWriteLongWord(adr, ct->cfmcmd); adr += 4; return adr; } void init_flashcfm(int num) { register_algorithm(num, driver_magic, sizeof(chiptype_t), download_struct, flashcfm_search_chip, flashcfm_erase, flashcfm_blank_chk, flashcfm_erase_wait, flashcfm_prog, prog_entry); } #else void init_flashcfm(int num) { register_algorithm(num, driver_magic, 0, 0, 0, flashcfm_erase, flashcfm_blank_chk, flashcfm_erase_wait, flashcfm_prog, 0); } #endif