/* * This file is part of the coreboot project. * * Copyright (C) 2000 AG Electronics Ltd. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. * * 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #include #include #include "../flash.h" struct data_amd800 { unsigned base; unsigned spacing; unsigned cs; const char *tag; }; static const char *identify_amd (struct flash_device *flash_device); static int erase_flash_amd800 (void *data, unsigned offset, unsigned length); static int program_flash_amd800 (void *data, unsigned offset, const void *source, unsigned length); static uint8_t read_byte_amd800(void *data, unsigned offset); static flash_fn fn_amd800 = { identify_amd, 0, 0, erase_flash_amd800, program_flash_amd800, read_byte_amd800 }; const char *identify_amd (struct flash_device *flash_device) { struct data_amd800 *d800 = flash_device->data; if (!d800->tag) { volatile unsigned char *flash = (volatile unsigned char *) d800->base; unsigned char type, id; *(flash + 0xaaa * d800->spacing) = 0xaa; *(flash + 0x555 * d800->spacing) = 0x55; *(flash + 0xaaa * d800->spacing) = 0x90; type = *(flash + 2 * d800->spacing); id = *flash; *flash = 0xf0; if ((id == 1 || id == 0x20) && type == 0x5b) { d800->cs = 45; d800->tag = "Am29LV800BB"; flash_device->base = d800->base; flash_device->size = 1024*1024; flash_device->erase_size = 64*1024; flash_device->store_size = 1; } else { printk_info("Unknown flash ID: 0x%02x 0x%02x\n", id, type); } } return d800->tag; } int erase_flash_amd800 (void *data, unsigned offset, unsigned length) { struct data_amd800 *d800 = data; volatile unsigned char *flash = (volatile unsigned char *) d800->base; volatile unsigned char *flash_aaa = flash + 0xaaa * d800->spacing; volatile unsigned char *flash_555 = flash + 0x555 * d800->spacing; int id; int cs = 9999; printk_info("Erase from 0x%08x to 0x%08x\n", offset, offset + length); *flash_aaa = 0xAA; // Chip Erase *flash_555 = 0x55; *flash_aaa = 0x80; *flash_aaa = 0xAA; *flash_555 = 0x55; *flash_aaa = 0x10; for (; cs > 0; cs--) { id = *(flash + 16); if (id & 0xA0) // DQ7 or DQ5 set: done or error break; printk_info("%4d\b\b\b\b", cs); } *flash_aaa = 0xF0; // In case of error printk_info("\b\b\b\b \b\b\b\b"); if (cs == 0) { printk_info("Could not erase flash, timeout.\n"); return -1; } else if ((id & 0x80) == 0) { printk_info("Could not erase flash, status=%02x.\n", id); return -1; } printk_info("Flash erased\n"); return 0; } int init_flash_amd800 (char *tag, unsigned base, unsigned spacing) { struct data_amd800 *data = malloc (sizeof (struct data_amd800)); if (data) { data->base = base; data->spacing = spacing; data->tag = 0; if (register_flash_device (&fn_amd800, tag, data) < 0) { free (data); return -1; } } else return -1; return 0; } int program_flash_amd800 (void *data, unsigned offset, const void *source, unsigned length) { struct data_amd800 *d800 = data; volatile unsigned char *flash = (volatile unsigned char *) d800->base; volatile unsigned char *flash_aaa = flash + 0xaaa * d800->spacing; volatile unsigned char *flash_555 = flash + 0x555 * d800->spacing; int id = 0; int cs; int errs = 0; volatile char *s; volatile char *d; printk_info("Program from 0x%08x to 0x%08x\n", offset, offset + length); printk_info("Data at %p\n", source); *flash_aaa = 0xAA; // Unlock Bypass *flash_555 = 0x55; *flash_aaa = 0x20; s = (unsigned char *) source; d = flash + offset * d800->spacing; cs = length; while (cs > 0 && !errs) { *flash = 0xA0; // Unlock Bypass Program *d = *s; // Program data while (1) { id = *d; if ((id & 0x80) == (*s & 0x80)) // DQ7 right? => program done break; else if (id & 0x20) { // DQ5 set? => maybe errors id = *d; if ((id & 0x80) != (*s & 0x80)) { errs++; break; } } } // PRINT("Set %08lx = %02x\n", d, *d); s += 1; d += d800->spacing; cs--; } *flash = 0x90; // Unlock Bypass Program Reset *flash = 0x00; *flash = 0xF0; if (errs != 0) { printk_info("FAIL: Status=%02x Address=%p.\n", id, d - d800->spacing); return -1; } printk_info("OK.\n"); // Step 4: Verify the flash. printk_info(" Verifying flash : ..."); errs = 0; s = (unsigned char *) source; d = flash + offset * d800->spacing; for (cs = 0; cs < length; cs++) { if (*s != *d) { if (errs == 0) printk_info("ERROR: Addr: %08p, PCI: %02x Lcl: %02x.\n", s, *s, *d); errs++; } s += 1; d += d800->spacing; } if (errs == 0) printk_info("OK.\n"); else { printk_info(" FAIL: %d errors.\n", errs); return -1; } return 0; } uint8_t read_byte_amd800 (void *data, unsigned offset) { struct data_amd800 *d800 = data; volatile unsigned char *flash = (volatile unsigned char *) d800->base; return *(flash + offset * d800->spacing); }