diff options
Diffstat (limited to 'w39.c')
-rw-r--r-- | w39.c | 255 |
1 files changed, 255 insertions, 0 deletions
@@ -0,0 +1,255 @@ +/* + * This file is part of the flashrom project. + * + * Copyright (C) 2008 coresystems GmbH + * Copyright (C) 2010 Carl-Daniel Hailfinger + * + * 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "flash.h" +#include "chipdrivers.h" + +static int printlock_w39_fwh_block(struct flashchip *flash, int offset) +{ + chipaddr wrprotect = flash->virtual_registers + offset + 2; + uint8_t locking; + + locking = chip_readb(wrprotect); + msg_cdbg("Lock status of block at 0x%08x is ", offset); + switch (locking & 0x7) { + case 0: + msg_cdbg("Full Access.\n"); + break; + case 1: + msg_cdbg("Write Lock (Default State).\n"); + break; + case 2: + msg_cdbg("Locked Open (Full Access, Lock Down).\n"); + break; + case 3: + msg_cerr("Error: Write Lock, Locked Down.\n"); + break; + case 4: + msg_cdbg("Read Lock.\n"); + break; + case 5: + msg_cdbg("Read/Write Lock.\n"); + break; + case 6: + msg_cerr("Error: Read Lock, Locked Down.\n"); + break; + case 7: + msg_cerr("Error: Read/Write Lock, Locked Down.\n"); + break; + } + + /* Read or write lock present? */ + return (locking & ((1 << 2) | (1 << 0))) ? -1 : 0; +} + +static int unlock_w39_fwh_block(struct flashchip *flash, int offset) +{ + chipaddr wrprotect = flash->virtual_registers + offset + 2; + uint8_t locking; + + locking = chip_readb(wrprotect); + /* Read or write lock present? */ + if (locking & ((1 << 2) | (1 << 0))) { + /* Lockdown active? */ + if (locking & (1 << 1)) { + msg_cerr("Can't unlock block at 0x%x!\n", offset); + return -1; + } else { + msg_cdbg("Unlocking block at 0x%x\n", offset); + chip_writeb(0, wrprotect); + } + } + + return 0; +} + +static uint8_t w39_idmode_readb(struct flashchip *flash, int offset) +{ + chipaddr bios = flash->virtual_memory; + uint8_t val; + + /* Product Identification Entry */ + chip_writeb(0xAA, bios + 0x5555); + chip_writeb(0x55, bios + 0x2AAA); + chip_writeb(0x90, bios + 0x5555); + programmer_delay(10); + + /* Read something, maybe hardware lock bits */ + val = chip_readb(bios + offset); + + /* Product Identification Exit */ + chip_writeb(0xAA, bios + 0x5555); + chip_writeb(0x55, bios + 0x2AAA); + chip_writeb(0xF0, bios + 0x5555); + programmer_delay(10); + + return val; +} + +static int printlock_w39_tblwp(uint8_t lock) +{ + msg_cdbg("Hardware bootblock locking (#TBL) is %sactive.\n", + (lock & (1 << 2)) ? "" : "not "); + msg_cdbg("Hardware remaining chip locking (#WP) is %sactive..\n", + (lock & (1 << 3)) ? "" : "not "); + if (lock & ((1 << 2) | (1 << 3))) + return -1; + + return 0; +} + +static int printlock_w39_bootblock_64k16k(uint8_t lock) +{ + msg_cdbg("Software 64 kB bootblock locking is %sactive.\n", + (lock & (1 << 0)) ? "" : "not "); + msg_cdbg("Software 16 kB bootblock locking is %sactive.\n", + (lock & (1 << 1)) ? "" : "not "); + if (lock & ((1 << 1) | (1 << 0))) + return -1; + + return 0; +} + +static int printlock_w39_common(struct flashchip *flash, int offset) +{ + uint8_t lock; + + lock = w39_idmode_readb(flash, offset); + msg_cdbg("Lockout bits:\n"); + return printlock_w39_tblwp(lock); +} + +static int printlock_w39_fwh(struct flashchip *flash) +{ + int i, total_size = flash->total_size * 1024; + int ret = 0; + + /* Print lock status of the complete chip */ + for (i = 0; i < total_size; i += flash->page_size) + ret |= printlock_w39_fwh_block(flash, i); + + return ret; +} + +static int unlock_w39_fwh(struct flashchip *flash) +{ + int i, total_size = flash->total_size * 1024; + + /* Unlock the complete chip */ + for (i = 0; i < total_size; i += flash->page_size) + if (unlock_w39_fwh_block(flash, i)) + return -1; + + return 0; +} + +int printlock_w39v040a(struct flashchip *flash) +{ + uint8_t lock; + int ret = 0; + + /* The W39V040A datasheet contradicts itself on the lock register + * location: 0x00002 and 0x7fff2 are both mentioned. Pick the one + * which is similar to the other chips of the same family. + */ + lock = w39_idmode_readb(flash, 0x7fff2); + msg_cdbg("Lockout bits:\n"); + + ret = printlock_w39_tblwp(lock); + ret |= printlock_w39_bootblock_64k16k(lock); + + return ret; +} + +int printlock_w39v040b(struct flashchip *flash) +{ + return printlock_w39_common(flash, 0x7fff2); +} + +int printlock_w39v040c(struct flashchip *flash) +{ + /* Typo in the datasheet? The other chips use 0x7fff2. */ + return printlock_w39_common(flash, 0xfff2); +} + +int printlock_w39v040fa(struct flashchip *flash) +{ + int ret = 0; + + ret = printlock_w39v040a(flash); + ret |= printlock_w39_fwh(flash); + + return ret; +} + +int printlock_w39v040fb(struct flashchip *flash) +{ + int ret = 0; + + ret = printlock_w39v040b(flash); + ret |= printlock_w39_fwh(flash); + + return ret; +} + +int printlock_w39v040fc(struct flashchip *flash) +{ + int ret = 0; + + /* W39V040C and W39V040FC use different WP/TBL offsets. */ + ret = printlock_w39_common(flash, 0x7fff2); + ret |= printlock_w39_fwh(flash); + + return ret; +} + +int printlock_w39v080a(struct flashchip *flash) +{ + return printlock_w39_common(flash, 0xffff2); +} + +int printlock_w39v080fa(struct flashchip *flash) +{ + int ret = 0; + + ret = printlock_w39v080a(flash); + ret |= printlock_w39_fwh(flash); + + return ret; +} + +int printlock_w39v080fa_dual(struct flashchip *flash) +{ + msg_cinfo("Block locking for W39V080FA in dual mode is " + "undocumented.\n"); + /* Better safe than sorry. */ + return -1; +} + +int unlock_w39v080fa(struct flashchip *flash) +{ + if (unlock_w39_fwh(flash)) + return -1; + if (printlock_w39_common(flash, 0xffff2)) + return -1; + + return 0; +} |