/* * $Id: amd.c,v 1.8 2003/09/23 17:48:40 telka Exp $ * * Flash driver for AMD Am29LV640D, Am29LV641D, Am29LV642D * Copyright (C) 2003 AH * * 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. * * Written by August Hörandl * Modified by Marcel Telka , 2003. * * Documentation: * [1] Advanced Micro Devices, "Am29LV640D/Am29LV641D", * September 20, 2002 Rev B, 22366b8.pdf * [2] Advanced Micro Devices, "Am29LV642D", * August 14, 2001 Rev A, 25022.pdf * */ #include "sysdep.h" #include #include #include #include #include #include #include #include #include #include static int dbg = 0; static int amd_flash_erase_block( cfi_array_t *cfi_array, uint32_t adr ); static int amd_flash_unlock_block( cfi_array_t *cfi_array, uint32_t adr ); static int amd_flash_program( cfi_array_t *cfi_array, uint32_t adr, uint32_t data ); static void amd_flash_read_array( cfi_array_t *cfi_array ); static int o; static int fastwrite=1; static int unlockbypass=0; /* autodetect, we can handle this chip */ static int amd_flash_autodetect32( cfi_array_t *cfi_array ) { if(cfi_array->bus_width != 4) return 0; o = 2; /* Heuristic */ return (cfi_array->cfi_chips[0]->cfi.identification_string.pri_id_code == CFI_VENDOR_AMD_SCS); } static int amd_flash_autodetect16( cfi_array_t *cfi_array ) { if(cfi_array->bus_width != 2) return 0; o = 1; /* Heuristic */ return (cfi_array->cfi_chips[0]->cfi.identification_string.pri_id_code == CFI_VENDOR_AMD_SCS); } static int amd_flash_autodetect8( cfi_array_t *cfi_array ) { if(cfi_array->bus_width != 1) return 0; o = 1; /* Heuristic */ return (cfi_array->cfi_chips[0]->cfi.identification_string.pri_id_code == CFI_VENDOR_AMD_SCS); } /* * check device status * 1/true PASS * 0/false FAIL */ /* * first implementation: see [1], page 29 */ #if 0 static int amdstatus29( parts *ps, uint32_t adr, uint32_t data ) { int timeout; uint32_t dq7mask = ((1 << 7) << 16) + (1 << 7); uint32_t dq5mask = ((1 << 5) << 16) + (1 << 5); uint32_t bit7 = (data & (1 << 7)) != 0; uint32_t data1; for (timeout = 0; timeout < 100; timeout++) { data1 = bus_read( ps, adr << o ); data1 = bus_read( ps, adr << o ); if (dbg) printf( "amdstatus %d: %04X (%04X) = %04X\n", timeout, data1, (data1 & dq7mask), bit7 ); if (((data1 & dq7mask) == dq7mask) == bit7) /* FIXME: This looks non-portable */ return 1; if ((data1 & dq5mask) == dq5mask) break; usleep( 100 ); } data1 = bus_read( ps, adr << o ); if (((data1 & dq7mask) == dq7mask) == bit7) /* FIXME: This looks non-portable */ return 1; return 0; } #endif /* 0 */ /* * second implementation: see [1], page 30 */ static int amdstatus( bus_t *bus, uint32_t adr, int data ) { int timeout; uint32_t togglemask = ((1 << 6) << 16) + (1 << 6); /* DQ 6 */ /* int dq5mask = ((1 << 5) << 16) + (1 << 5); DQ5 */ for (timeout = 0; timeout < 100; timeout++) { uint32_t data1 = bus_read( bus, adr ); uint32_t data2 = bus_read( bus, adr ); /*printf("amdstatus %d: %04X/%04X %04X/%04X \n", */ /* timeout, data1, data2, (data1 & togglemask), (data2 & togglemask)); */ if ( (data1 & togglemask) == (data2 & togglemask)) /* no toggle */ return 1; /* if ( (data1 & dq5mask) != 0 ) TODO */ /* return 0; */ if (dbg) printf( "amdstatus %d: %04X/%04X\n", timeout, data1, data2 ); else printf( "." ); usleep( 100 ); } return 0; } #if 0 static int amdisprotected( parts *ps, uint32_t adr ) { uint32_t data; bus_write( ps, 0x0555 << o, 0x00aa00aa ); /* autoselect p29, sector erase */ bus_write( ps, 0x02aa << o, 0x00550055 ); bus_write( ps, 0x0555 << o, 0x00900090 ); data = bus_read( ps, adr + (0x0002 << 2) ); /* Read Array */ amd_flash_read_array( ps ); /* AMD reset */ return ((data & 0x00ff00ff) != 0); } #endif /* 0 */ static void amd_flash_print_info( cfi_array_t *cfi_array ) { int mid, cid, prot; bus_t *bus = cfi_array->bus; bus_write( bus, 0x0555 << o, 0x00aa00aa ); /* autoselect p29 */ bus_write( bus, 0x02aa << o, 0x00550055 ); bus_write( bus, 0x0555 << o, 0x00900090 ); mid = bus_read( bus, 0x00 << o ) & 0xFFFF; cid = bus_read( bus, 0x01 << o ) & 0xFFFF; prot = bus_read( bus, 0x02 << o ) & 0xFF; amd_flash_read_array( cfi_array ); /* AMD reset */ printf( _("Chip: AMD Flash\n\tManufacturer: ") ); switch (mid) { case 0x0001: printf( _("AMD") ); break; default: printf( _("Unknown manufacturer (ID 0x%04x)"), mid ); break; } printf( _("\n\tChip: ") ); switch (cid) { case 0x22D7: printf( _("Am29LV640D/Am29LV641D/Am29LV642D") ); break; case 0x225B: printf( _("Am29LV800B") ); break; default: printf ( _("Unknown (ID 0x%04x)"), cid ); break; } printf( _("\n\tProtected: %04x\n"), prot ); } static int amd_flash_erase_block( cfi_array_t *cfi_array, uint32_t adr ) { bus_t *bus = cfi_array->bus; printf("flash_erase_block 0x%08X\n", adr); /* printf("protected: %d\n", amdisprotected(ps, adr)); */ bus_write( bus, 0x0555 << o, 0x00aa00aa ); /* autoselect p29, sector erase */ bus_write( bus, 0x02aa << o, 0x00550055 ); bus_write( bus, 0x0555 << o, 0x00800080 ); bus_write( bus, 0x0555 << o, 0x00aa00aa ); bus_write( bus, 0x02aa << o, 0x00550055 ); bus_write( bus, adr, 0x00300030 ); if (amdstatus( bus, adr, 0xffff )) { printf( "flash_erase_block 0x%08X DONE\n", adr ); amd_flash_read_array( cfi_array ); /* AMD reset */ return 0; } printf( "flash_erase_block 0x%08X FAILED\n", adr ); /* Read Array */ amd_flash_read_array( cfi_array ); /* AMD reset */ return FLASH_ERROR_UNKNOWN; } static int amd_flash_unlock_block( cfi_array_t *cfi_array, uint32_t adr ) { printf( "flash_unlock_block 0x%08X IGNORE\n", adr ); return 0; } static int amd_flash_program( cfi_array_t *cfi_array, uint32_t adr, uint32_t data ) { int status; bus_t *bus = cfi_array->bus; if (dbg) printf("\nflash_program 0x%08X = 0x%08X\n", adr, data); if(fastwrite) { if(!unlockbypass) { bus_write( bus, 0x0555 << o, 0x00aa00aa ); /* autoselect p29, program */ bus_write( bus, 0x02aa << o, 0x00550055 ); bus_write( bus, 0x0555 << o, 0x00200020 ); unlockbypass=1; } bus_write( bus, adr, 0x00a000a0 ); } else { bus_write( bus, 0x0555 << o, 0x00aa00aa ); /* autoselect p29, program */ bus_write( bus, 0x02aa << o, 0x00550055 ); bus_write( bus, 0x0555 << o, 0x00A000A0 ); } bus_write( bus, adr, data ); status = amdstatus( bus, adr, data ); /* amd_flash_read_array(ps); */ return !status; } static void amd_flash_read_array( cfi_array_t *cfi_array ) { /* Read Array */ bus_write( cfi_array->bus, 0x0, 0x00F000F0 ); /* AMD reset */ if(unlockbypass) { bus_write( cfi_array->bus, 0x0, 0x00900090 ); bus_write( cfi_array->bus, 0x0, 0x00000000 ); } bus_write( cfi_array->bus, 0x0, 0x00F000F0 ); /* AMD reset */ unlockbypass=0; } flash_driver_t amd_32_flash_driver = { 4, /* buswidth */ N_("AMD/Fujitsu Standard Command Set"), N_("supported: AMD 29LV640D, 29LV641D, 29LV642D; 2x16 Bit"), amd_flash_autodetect32, amd_flash_print_info, amd_flash_erase_block, amd_flash_unlock_block, amd_flash_program, amd_flash_read_array, }; flash_driver_t amd_16_flash_driver = { 2, /* buswidth */ N_("AMD/Fujitsu Standard Command Set"), N_("supported: AMD 29LV800B, 29LV400T; 1x16 Bit"), amd_flash_autodetect16, amd_flash_print_info, amd_flash_erase_block, amd_flash_unlock_block, amd_flash_program, amd_flash_read_array, }; flash_driver_t amd_8_flash_driver = { 1, /* buswidth */ N_("AMD/Fujitsu Standard Command Set"), N_("supported: AMD 29LV160; 1x8 Bit"), amd_flash_autodetect8, amd_flash_print_info, amd_flash_erase_block, amd_flash_unlock_block, amd_flash_program, amd_flash_read_array, };