[prev in list] [next in list] [prev in thread] [next in thread] 

List:       flashrom
Subject:    [flashrom] Acer Aspire 8730g (Nuvaton/Winstron WPCE755)
From:       celliwig--- via flashrom <flashrom () flashrom ! org>
Date:       2023-05-14 18:43:48
Message-ID: 20230514184348.A0930805ED6 () smtp ! hushmail ! com
[Download RAW message or body]

[Attachment #2 (multipart/alternative)]


Hi,
This may be of help to someone (or not ;)! Following the request in
the 'Laptops' section of the wiki to send in code, here's my attempt
to implement an opaque programmer for the WPCE775. Obviously
programmer_table.c/programmer.h need to be updated to reference the
programmer, and the Makefile updated to actually build it. i8042 code
is there to reset the KBC as the trackpad will go crazy after swapping
the EC to Flash Update mode. This is very much a work in progress.
It's only been used to write/erase a single sector so far. So use at
YOUR OWN RISK!!!
Some notes on extracting this from a phlash16 WPH file can be found
here:
https://github.com/Celliwig/Reverse-Engineering/tree/main/Acer/8730g
Specifically the found WPCE775 commands:
https://github.com/Celliwig/Reverse-Engineering/blob/main/Acer/8730g/wp-access-code/WPCE775_cmds.txt
Hope this helps somebody,
Celliwig

[Attachment #5 (unknown)]

<span style="font-family: Arial; font-size: 14px; line-height: \
150%;"><div>Hi,</div><div><br></div><div>This may be of help to someone (or not ;)! \
Following the request in the 'Laptops' section of the wiki to send in code, here's my \
attempt to implement an opaque programmer for the WPCE775. Obviously \
programmer_table.c/programmer.h need to be updated to reference the programmer, and \
the Makefile updated to actually build it. i8042 code is there to reset the KBC as \
the trackpad will go crazy after swapping the EC to Flash Update mode. This is very \
much a work in progress. It's only been used to write/erase a single sector so far. \
So use at YOUR OWN RISK!!!</div><div><br></div><div>Some notes on extracting this \
from a phlash16 WPH file can be found here: <a \
href="https://github.com/Celliwig/Reverse-Engineering/tree/main/Acer/8730g">https://gi \
thub.com/Celliwig/Reverse-Engineering/tree/main/Acer/8730g</a></div><div><br></div><div>Specifically \
the found WPCE775 commands: <a \
href="https://github.com/Celliwig/Reverse-Engineering/blob/main/Acer/8730g/wp-access-c \
ode/WPCE775_cmds.txt">https://github.com/Celliwig/Reverse-Engineering/blob/main/Acer/8730g/wp-access-code/WPCE775_cmds.txt</a></div><div><br></div><div>Hope \
this helps somebody,</div><div><br></div><div>Celliwig<br></div></span>


["i8042.c" (application/octet-stream)]

/*
 * This file is part of the flashrom project.
 *
 * Copyright (C) 2023 Celliwig
 *
 * 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; version 2 of the License.
 *
 * 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.
 */

/*
 * Routines to access (emulated) Intel 8042 keyboard controller
 * Needed because SuperIO's KBC can be become unstable when they are combined
 * with flash write functions.
 */

#include <strings.h>
#include <string.h>
#include <stdbool.h>
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <errno.h>

#if defined(__i386__) || defined(__x86_64__)

#include "hwaccess_x86_io.h"

#include "i8042.h"

// Unbind module from hardware
uint8_t i8042_kbc_driver_unbind()
{
	FILE *sysfs;

	sysfs = fopen(I8042_SYSFS_PATH "unbind", "w");
	if (sysfs == NULL) return -1;
	if (fprintf(sysfs, "i8042") < 0) return -2;
	fclose(sysfs);

	return 0;
}

// Rebind module to hardware
uint8_t i8042_kbc_driver_rebind()
{
	FILE *sysfs;

	sysfs = fopen(I8042_SYSFS_PATH "bind", "w");
	if (sysfs == NULL) return -1;
	if (fprintf(sysfs, "i8042") < 0) return -2;
	fclose(sysfs);

	return 0;
}

// Send keyboard controller a command (timeout=0 for no timeout)
uint8_t i8042_kbc_cmd_timeout(uint8_t cmd, uint32_t timeout)
{
	// Read KBC input buffer status (wait for empty)
	while (0x2 & INB(I8042_KBC_STATUS_PORT)) {
		OUTB(0, I8042_UNDEFINED_PORT);
		if (timeout > 0) {
			timeout--;
			if (timeout == 0) return -1;
		}
	}
	OUTB(cmd, I8042_KBC_COMMAND_PORT);
	return 0;
}

// Send keyboard controller a command (no timeout)
void i8042_kbc_cmd(uint8_t cmd)
{
	i8042_kbc_cmd_timeout(cmd, 0);
}

// Get keyboard controller data (timeout=0 for no timeout)
uint8_t i8042_kbc_data_read_timeout(uint8_t *buf, uint32_t timeout)
{
	// Read KBC output buffer status (wait for full)
	while (!(0x1 & INB(I8042_KBC_STATUS_PORT))) {
		OUTB(0, I8042_UNDEFINED_PORT);
		if (timeout > 0) {
			timeout--;
			if (timeout == 0) return -1;
		}
	}
	*buf = INB(I8042_KBC_DATA_PORT);
	return 0;
}

// Get keyboard controller data (no timeout)
uint8_t i8042_kbc_data_read()
{
	uint8_t val;
	i8042_kbc_data_read_timeout(&val, 0);
	return val;
}

// Set keyboard controller data (timeout=0 for no timeout)
uint8_t i8042_kbc_data_write_timeout(uint8_t val, uint32_t timeout)
{
	// Read KBC input buffer status (wait for empty)
	while (0x2 & INB(I8042_KBC_STATUS_PORT)) {
		OUTB(0, I8042_UNDEFINED_PORT);
		if (timeout > 0) {
			timeout--;
			if (timeout == 0) return -1;
		}
	}
	OUTB(val, I8042_KBC_DATA_PORT);
	return 0;
}

// Set keyboard controller data (no timeout)
void i8042_kbc_data_write(uint8_t val)
{
	i8042_kbc_data_write_timeout(val, 0);
}

// Get PS/2 controller configuration
uint8_t i8042_kbc_cfg_get()
{
	// Get controller configuration byte
	i8042_kbc_cmd(0x20);
	return i8042_kbc_data_read();
}

// Set PS/2 controller configuration
void i8042_kbc_cfg_set(uint8_t cfg)
{
	// Set controller configuration byte
	i8042_kbc_cmd(0x60);
	i8042_kbc_data_write(cfg);
}

// Send reset command to specified PS/2 device
uint8_t i8042_kbc_device_reset(uint8_t device)
{
	uint8_t val;
	bool read_extra = false;

	if (device == 2) {
		// Write following device command to port 2
		i8042_kbc_cmd(0xd4);
		// It's a mouse, so get device ID
		read_extra = true;
	}
	// Reset selected device
	i8042_kbc_data_write(0xff);

	if (i8042_kbc_data_read_timeout(&val, 0xffffff)) return -1;
	if (val != 0xfa) return -2;
	if (i8042_kbc_data_read_timeout(&val, 0xffffff)) return -3;
	if (val != 0xaa) return -4;
	if (read_extra) {
		if (i8042_kbc_data_read_timeout(&val, 0xffffff)) return -5;
	}

	return 0;
}

// Send enable/disable command to specified PS/2 device
uint8_t i8042_kbc_device_enable(uint8_t device, bool enable)
{
	uint8_t val;

	if (device == 2) {
		// Write following device command to port 2
		i8042_kbc_cmd(0xd4);
	}
	if (enable) {
		// Enable selected device
		i8042_kbc_data_write(0xf4);
	} else {
		// Disable selected device
		i8042_kbc_data_write(0xf5);
	}
	if (i8042_kbc_data_read_timeout(&val, 0xffffff)) return -1;
	if (val != 0xfa) return -2;

	return 0;
}

#endif

["i8042.h" (application/octet-stream)]
["wpce775.c" (application/octet-stream)]

/*
 * This file is part of the flashrom project.
 *
 * Copyright (C) 2023 Celliwig
 *
 * 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; version 2 of the License.
 *
 * 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.
 */

/*
 * Contains the routines for the Nuvaton/Winbond WPCE775, supports:
 * 	Acer Aspire 8730g
 */

#include <strings.h>
#include <string.h>
#include <stdbool.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include "flash.h"
#include "spi.h"
#include "programmer.h"
#include "platform/pci.h"

#if defined(__i386__) || defined(__x86_64__)

#include "hwaccess_physmap.h"
#include "hwaccess_x86_io.h"
#include "hwaccess_x86_msr.h"

#include "i8042.h"
#include "wpce775.h"

/*
 * I/O access
 */
/* Winbond/Nuvoton WPCE775 Super I/O helper functions */
/* Delays added to generic code to match existing code */
uint8_t wpce775_sio_read(uint16_t port, uint8_t reg)
{
	OUTB(reg, port);
	OUTB(0x0, WPCE775_UNDEFINED_PORT);	// Write to undefined port (creates 1us delay)
	uint8_t ret = INB(port + 1);
	OUTB(0x0, WPCE775_UNDEFINED_PORT);	// Write to undefined port (creates 1us delay)
	return ret;
}

void wpce775_sio_write(uint16_t port, uint8_t reg, uint8_t data)
{
	OUTB(reg, port);
	OUTB(0x0, WPCE775_UNDEFINED_PORT);	// Write to undefined port (creates 1us delay)
	OUTB(data, port + 1);
	OUTB(0x0, WPCE775_UNDEFINED_PORT);	// Write to undefined port (creates 1us delay)
}

void wpce775_sio_mask(uint16_t port, uint8_t reg, uint8_t data, uint8_t mask)
{
	uint8_t tmp;

	OUTB(reg, port);
	OUTB(0x0, WPCE775_UNDEFINED_PORT);	// Write to undefined port (creates 1us delay)
	tmp = INB(port + 1) & ~mask;
	OUTB(tmp | (data & mask), port + 1);
	OUTB(0x0, WPCE775_UNDEFINED_PORT);	// Write to undefined port (creates 1us delay)
}

/*
 * WPCE775 PS/2 KBC
 */
// Disable PS/2 controller
uint8_t wpce775_kbc_disable(struct wpce775_superio *wpce775)
{
	uint8_t val;

	msg_pdbg("	WPCE775 PS/2 Keyboard Controller Disable\n");

	// Unbind linux drivers
	if ((val = i8042_kbc_driver_unbind()) != 0) {
		msg_pdbg("              Failed to unbind driver. [0x%x]\n", val);
		return -1;
	}

	return 0;
}

// Enable PS/2 controller
void wpce775_kbc_enable(struct wpce775_superio *wpce775)
{
	uint8_t val;

	msg_pdbg("	WPCE775 PS/2 Keyboard Controller Enable\n");

	// Rebind linux drivers
	if ((val = i8042_kbc_driver_rebind()) != 0) {
		msg_pdbg("              Failed to rebind driver. [0x%x]\n", val);
	}

}

/*
 * ACPI
 */
// Update 'Power Button Enable (PWRBTN_EN)' state
bool wpce775_acpi_pwrbtnen(struct wpce775_superio *wpce775, bool btn_enable)
{
	uint16_t val;
	msg_pdbg("	ACPI - PM1_EN: %s\n", btn_enable ? "Enable" : "Disable" );
	val = INW(wpce775->apci_io_base + 0x02) & 0xfeff;
	if (btn_enable) val |= 0x100;
	OUTW(val, wpce775->apci_io_base + 0x02);
	return true;
}

// Clear SMI state
void wpce775_acpi_smistate_clear(struct wpce775_superio *wpce775)
{
	uint16_t val16;
	uint32_t val32;
	msg_pdbg("	ACPI - SMI state clear\n");

	// Copied from phlash16
	// ACPI ???
	val16 = INW(wpce775->apci_io_base);
	OUTW(val16, wpce775->apci_io_base);

	// Clear something ???
	val32 = INL(wpce775->apci_io_base + 0x20);
	OUTL(val32, wpce775->apci_io_base + 0x20);

	// Clear Microcontroller SMI# Status bit (before enabling SMI)
	val32 = INL(wpce775->apci_io_base + 0x34);
	OUTL(val32, wpce775->apci_io_base + 0x34);
}

// Update SMI state
void wpce775_acpi_smistate(struct wpce775_superio *wpce775, bool smi_restore)
{
	uint8_t val;
	msg_pdbg("	ACPI - SMI state: %s\n", smi_restore ? "Restore" : "Disable" );
	val = INB(wpce775->apci_io_base + 0x30);
	if (smi_restore) {
		val |= wpce775->acpi_smi_prev & 0x1;
	} else {
		wpce775->acpi_smi_prev = val;
		val &= 0xfe;
	}
	OUTB(val, wpce775->apci_io_base + 0x30);
}

/*
 * LPC/PCI
 */
// Probe for LPC ISA bridge on the PCI bus
bool wpce775_lpc_probe(struct wpce775_superio *wpce775)
{
	uint8_t acpi_ctrl;
	msg_pdbg("	Looking for LPC: ");
	// ISA bridge
	wpce775->pci_lpc = pcidev_find_vendorclass(PCI_VENDOR_ID_INTEL, \
PCI_HW_ID_INTEL_LPC);  if (!wpce775->pci_lpc) {
		msg_pdbg("ERROR: not found\n");
		return false;
	}
	msg_pdbg("Found\n");

	msg_pdbg("	Getting ACPI I/O port: ");
	// Get ACPI base I/O address
	wpce775->apci_io_base = pci_read_long(wpce775->pci_lpc, 0x40) & 0x0000ff80;
	// Get ACPI control
	acpi_ctrl = pci_read_byte(wpce775->pci_lpc, 0x44);
	if ((acpi_ctrl & 0x80) == 0) {
		msg_perr("WPCE775: ACPI disabled\n");
		return false;
	}
	msg_pdbg("0x%x\n", wpce775->apci_io_base);

	return true;
}

// Check the configuration of the LPC ISA bridge
bool wpce775_lpc_conf_chk(struct wpce775_superio *wpce775)
{
	uint32_t val;
	// Check LPC config
	msg_pdbg("	Checking LPC config.\n");
	msg_pdbg("		LPC - Firmware Hub Select 1 [0xd0]: ");
	val = pci_read_long(wpce775->pci_lpc, 0xd0);
	if ((val & 0xe) == 0xe) {
		msg_pdbg("Okay\n");
	} else {
		msg_pdbg("Invalid\n");
		return false;
	}
	msg_pdbg("		LPC - Firmware Hub Decode Enable 1 [0xd8]: ");
	val = pci_read_word(wpce775->pci_lpc, 0xd8);
	if ((val & 0x100) == 0x100) {
		msg_pdbg("Okay\n");
	} else {
		msg_pdbg("Invalid\n");
		return false;
	}
	return true;
}

// Control BIOS Write Enable
void wpce775_lpc_bios_write_enable(struct wpce775_superio *wpce775, bool \
write_enable) {
	uint8_t bios_ctrl;
	bios_ctrl = pci_read_byte(wpce775->pci_lpc, 0xdc);
	if (write_enable) {
		bios_ctrl |= 0x01;
	} else {
		bios_ctrl &= 0xfe;
	}
	msg_pdbg("	BIOS Control: BIOS Write Enable - 0x%x\n", bios_ctrl);
	pci_write_byte(wpce775->pci_lpc, 0xdc, bios_ctrl);
}

/*
 * MCU 1
 */
// MCU 1 init
bool wpce775_mcu1_io_init(struct wpce775_superio *wpce775)
{
	uint8_t val;
	uint32_t cnt;

	msg_pdbg("	WPCE775 MCU 1 init.\n");

	for (cnt = 0xffff; cnt > 0; cnt--) {
		val = INB(wpce775->mcu1_io_base);
		msg_pdbg("		I/O Port Read: 0x%x [0x%x]\n", wpce775->mcu1_io_base, val);
		OUTB(val, WPCE775_UNDEFINED_PORT);
		if ((val & 0x02) == 0) break;
	}
	if (cnt > 0x0) {
		val = 0x52;
		OUTB(val, wpce775->mcu1_io_base);
		msg_pdbg("		I/O Port Write: 0x%x [0x%x]\n", wpce775->mcu1_io_base, val);
		for (cnt = 0; cnt < 0x9c40; cnt++) {
			OUTB(val, WPCE775_UNDEFINED_PORT);
		}
	}
	for (cnt = 0; cnt < (0x32 * 0x9c40); cnt++) {
		OUTB(val, WPCE775_UNDEFINED_PORT);
	}

	return true;
}

/*
 * MCU 2
 */
// Initialise MCU 2 using I/O ports
bool wpce775_mcu2_mmio_init(struct wpce775_superio *wpce775)
{
	msg_pdbg("	Configuring WPCE775 MMIO BAR.\n");
	// LDN 0x0f (Shared memory (SHM))
	wpce775_sio_write(wpce775->mcu2_io_base, 0x07, 0x0f);
	wpce775_sio_mask(wpce775->mcu2_io_base, 0xf0, 0xe0, 0xf0);
	wpce775_sio_mask(wpce775->mcu2_io_base, 0xf1, 0x40, 0x40);
	// Write MMIO address
	wpce775_sio_write(wpce775->mcu2_io_base, 0xf8, (wpce775->mcu2_mmio_base) & 0xff);
	wpce775_sio_write(wpce775->mcu2_io_base, 0xf9, (wpce775->mcu2_mmio_base>>8) & 0xff);
	wpce775_sio_write(wpce775->mcu2_io_base, 0xfa, (wpce775->mcu2_mmio_base>>16) & \
0xff);  wpce775_sio_write(wpce775->mcu2_io_base, 0xfb, (wpce775->mcu2_mmio_base>>24) \
& 0xff);  // Activate
	wpce775_sio_write(wpce775->mcu2_io_base, 0x30, 0x01);
	return true;
}

// Open access to MCU 2 MMIO
bool wpce775_mcu2_mmio_open(struct wpce775_superio *wpce775)
{
	msg_pdbg("	Mapping 0x%x: ", wpce775->mcu2_mmio_base);
	wpce775->mcu2_mmio_dev = physmap("MPCE775 MMIO register", wpce775->mcu2_mmio_base, \
wpce775->mcu2_mmio_length);  if (wpce775->mcu2_mmio_dev == ERROR_PTR) {
		msg_pdbg("Failed\n");
		return false;
	}
	msg_pdbg("%p\n", wpce775->mcu2_mmio_dev);
	return true;
}

// Close access to MCU 2 MMIO
void wpce775_mcu2_mmio_close(struct wpce775_superio *wpce775)
{
	msg_pdbg("	Unmapping 0x%x\n", wpce775->mcu2_mmio_base);
	physunmap(wpce775->mcu2_mmio_dev, wpce775->mcu2_mmio_length);
}

// Check status
void wpce775_mcu2_mmio_status_check_loop(struct wpce775_superio *wpce775, uint8_t \
chk_mask, uint8_t chk_val, uint8_t dbg_code) {
	uint8_t val;
	msg_pdbg("		MCU 2: Status Check - 0x%x:0x%x [0x%x]\n", chk_mask, chk_val, dbg_code);
	bool loop = true;
	while (loop) {
		OUTW(dbg_code, 0x80);				// Writing to BIOS debug console
		val = mmio_readb(wpce775->mcu2_mmio_dev);
		if ((val & chk_mask) == chk_val) loop = false;
	}
}

// Run command / Clear status
void wpce775_mcu2_mmio_status_runclear(struct wpce775_superio *wpce775, uint8_t \
dbg_code) {
	mmio_writeb(0x01, wpce775->mcu2_mmio_dev);
	wpce775_mcu2_mmio_status_check_loop(wpce775, 0x80, 0x80, dbg_code);

	uint8_t val = mmio_readb(wpce775->mcu2_mmio_dev) & 0xfe;
	mmio_writeb(val, wpce775->mcu2_mmio_dev);
	wpce775_mcu2_mmio_status_check_loop(wpce775, 0x81, 0x00, dbg_code+1);
}

// Try to gain access permission
void wpce775_mcu2_mmio_mode_enter(struct wpce775_superio *wpce775,
				uint8_t arg1, uint8_t arg2, uint8_t arg3, uint8_t arg4,
				uint8_t arg5, uint8_t arg6, uint8_t arg7, uint8_t arg8)
{
	uint8_t val;
	msg_pdbg("	MCU 2: Mode - Enter\n");

	val = mmio_readb(wpce775->mcu2_mmio_dev) & 0xfe;
	mmio_writeb(val, wpce775->mcu2_mmio_dev);
	wpce775_mcu2_mmio_status_check_loop(wpce775, 0x01, 0x00, 0xb1);

	mmio_writeb(0x5a, wpce775->mcu2_mmio_dev + 0x03);
	mmio_writeb(arg1, wpce775->mcu2_mmio_dev + 0x06);
	mmio_writeb(arg2, wpce775->mcu2_mmio_dev + 0x05);
	mmio_writeb(arg3, wpce775->mcu2_mmio_dev + 0x09);
	mmio_writeb(arg4, wpce775->mcu2_mmio_dev + 0x0a);
	mmio_writeb(arg5, wpce775->mcu2_mmio_dev + 0x04);
	mmio_writeb(arg6, wpce775->mcu2_mmio_dev + 0x07);
	mmio_writeb(arg7, wpce775->mcu2_mmio_dev + 0x08);
	mmio_writeb(0x01, wpce775->mcu2_mmio_dev + 0x0b);
	mmio_writeb(0x00, wpce775->mcu2_mmio_dev + 0x0c);
	val = arg8;
	mmio_writeb( val, wpce775->mcu2_mmio_dev + 0x0d);
	if (val == 0x08) val = 0xff;
	mmio_writeb( val, wpce775->mcu2_mmio_dev + 0x0e);

	mmio_writeb(0x01, wpce775->mcu2_mmio_dev);
	wpce775_mcu2_mmio_status_check_loop(wpce775, 0x80, 0x80, 0xb2);

	val = mmio_readb(wpce775->mcu2_mmio_dev) & 0xfe;
	mmio_writeb(val, wpce775->mcu2_mmio_dev);
	wpce775_mcu2_mmio_status_check_loop(wpce775, 0x81, 0x00, 0xb3);
}

// Revoke access permission
void wpce775_mcu2_mmio_mode_exit(struct wpce775_superio *wpce775)
{
	uint8_t val;

	msg_pdbg("	MCU 2: Mode - Exit\n");

	// Check status
	wpce775_mcu2_mmio_status_check_loop(wpce775, 0x81, 0x00, 0xb1);

	mmio_writeb(0x22, wpce775->mcu2_mmio_dev + 0x03);

	mmio_writeb(0x01, wpce775->mcu2_mmio_dev);
	wpce775_mcu2_mmio_status_check_loop(wpce775, 0x80, 0x80, 0xb2);

	val = mmio_readb(wpce775->mcu2_mmio_dev) & 0xfe;
	mmio_writeb(val, wpce775->mcu2_mmio_dev);
	wpce775_mcu2_mmio_status_check_loop(wpce775, 0x81, 0x00, 0xb3);
}

// Return JEDEC ID
uint32_t wpce775_mcu2_mmio_mode_jedecid(struct wpce775_superio *wpce775)
{
	uint8_t val;
	uint32_t retval = 0;
	msg_pdbg("	MCU 2: Mode - JEDEC ID\n");

	// Check status
	wpce775_mcu2_mmio_status_check_loop(wpce775, 0x81, 0x00, 0xb4);

	mmio_writeb(0xc0, wpce775->mcu2_mmio_dev + 0x03);

	mmio_writeb(0x01, wpce775->mcu2_mmio_dev);
	wpce775_mcu2_mmio_status_check_loop(wpce775, 0x80, 0x80, 0xb5);

	val = mmio_readb(wpce775->mcu2_mmio_dev) & 0xfe;
	mmio_writeb(val, wpce775->mcu2_mmio_dev);
	wpce775_mcu2_mmio_status_check_loop(wpce775, 0x81, 0x00, 0xb6);

	retval |= (mmio_readb(wpce775->mcu2_mmio_dev + 0x04) << 0) & 0x000000ff;
	retval |= (mmio_readb(wpce775->mcu2_mmio_dev + 0x05) << 8) & 0x0000ff00;
	retval |= (mmio_readb(wpce775->mcu2_mmio_dev + 0x06) << 16) & 0x00ff0000;
	retval |= (mmio_readb(wpce775->mcu2_mmio_dev + 0x07) << 24) & 0xff000000;

	return retval;
}

// Loop through loading init data
bool wpce775_mcu2_mmio_mode_init(struct wpce775_superio *wpce775)
{
	bool retval = false;
	for (uint32_t i = 0; i < ARRAY_SIZE(wpce775_inittable); i++) {
		msg_pdbg("	Loading MCU 2 init. data: %u\n", i);
		const uint8_t *tmp_initdata = wpce775_inittable[i];
		wpce775_mcu2_mmio_mode_enter(wpce775,
					tmp_initdata[0x0], tmp_initdata[0x1], tmp_initdata[0x4], tmp_initdata[0x6],
					tmp_initdata[0x8], tmp_initdata[0x7], tmp_initdata[0xa], tmp_initdata[0xb]);
		uint16_t chkval_data = ((tmp_initdata[0xd]<<8) | tmp_initdata[0xc]);
		uint32_t chkval_calc = wpce775_mcu2_mmio_mode_jedecid(wpce775);
		if ((chkval_calc & 0xffff) == chkval_data) {
			msg_pdbg("	MCU 2: Init. data loaded\n");
			retval = true;
			break;
		} else {
			msg_pdbg("	MCU 2: Init. data rejected\n");
		}
	}
	return retval;
}

// Swap to flash ROM mode
void wpce775_mcu2_mmio_mode_flash(struct wpce775_superio *wpce775)
{
	uint8_t val;
	msg_pdbg("	MCU 2: Mode - Flash\n");

	// Check status
	wpce775_mcu2_mmio_status_check_loop(wpce775, 0x81, 0x00, 0xb4);

	mmio_writeb(0x10, wpce775->mcu2_mmio_dev + 0x03);
	mmio_writeb(0x55, wpce775->mcu2_mmio_dev + 0x04);
	mmio_writeb(0xaa, wpce775->mcu2_mmio_dev + 0x05);
	mmio_writeb(0xcd, wpce775->mcu2_mmio_dev + 0x06);
	mmio_writeb(0xbe, wpce775->mcu2_mmio_dev + 0x07);

	mmio_writeb(0x01, wpce775->mcu2_mmio_dev);
	wpce775_mcu2_mmio_status_check_loop(wpce775, 0x80, 0x80, 0xb5);

	val = mmio_readb(wpce775->mcu2_mmio_dev) & 0xfe;
	mmio_writeb(val, wpce775->mcu2_mmio_dev);
	wpce775_mcu2_mmio_status_check_loop(wpce775, 0x81, 0x00, 0xb6);
}

// Erase sector
void wpce775_mcu2_mmio_mode_erase(struct wpce775_superio *wpce775, uint32_t \
sector_addr) {
	uint8_t val;
	msg_pdbg("	MCU 2: Mode - Erase\n");

	// Check status
	wpce775_mcu2_mmio_status_check_loop(wpce775, 0x81, 0x00, 0xb4);

	mmio_writeb(0x80, wpce775->mcu2_mmio_dev + 0x03);
	mmio_writeb(((sector_addr>>0) & 0xff), wpce775->mcu2_mmio_dev + 0x04);
	mmio_writeb(((sector_addr>>8) & 0xff), wpce775->mcu2_mmio_dev + 0x05);
	mmio_writeb(((sector_addr>>16) & 0xff), wpce775->mcu2_mmio_dev + 0x06);
	mmio_writeb(((sector_addr>>24) & 0xff), wpce775->mcu2_mmio_dev + 0x07);

	mmio_writeb(0x01, wpce775->mcu2_mmio_dev);
	wpce775_mcu2_mmio_status_check_loop(wpce775, 0x80, 0x80, 0xb5);

	val = mmio_readb(wpce775->mcu2_mmio_dev) & 0xfe;
	mmio_writeb(val, wpce775->mcu2_mmio_dev);
	wpce775_mcu2_mmio_status_check_loop(wpce775, 0x81, 0x00, 0xb6);
}

// Program byte(s)
void wpce775_mcu2_mmio_mode_program(struct wpce775_superio *wpce775, const uint8_t \
*buf, uint32_t rom_addr, uint8_t len) {
	uint8_t val, offset;
	msg_pdbg("	MCU 2: Mode - Program\n");

	// Check data size
	if (len != 8) return;

	// Load address
	////////////////
	// Check status
	wpce775_mcu2_mmio_status_check_loop(wpce775, 0x81, 0x00, 0xb4);

	mmio_writeb(0xa0, wpce775->mcu2_mmio_dev + 0x03);
	mmio_writeb(((rom_addr>>0) & 0xff), wpce775->mcu2_mmio_dev + 0x04);
	mmio_writeb(((rom_addr>>8) & 0xff), wpce775->mcu2_mmio_dev + 0x05);
	mmio_writeb(((rom_addr>>16) & 0xff), wpce775->mcu2_mmio_dev + 0x06);
	mmio_writeb(((rom_addr>>24) & 0xff), wpce775->mcu2_mmio_dev + 0x07);

	mmio_writeb(0x01, wpce775->mcu2_mmio_dev);
	wpce775_mcu2_mmio_status_check_loop(wpce775, 0x80, 0x80, 0xb5);

	val = mmio_readb(wpce775->mcu2_mmio_dev) & 0xfe;
	mmio_writeb(val, wpce775->mcu2_mmio_dev);
	wpce775_mcu2_mmio_status_check_loop(wpce775, 0x81, 0x00, 0xb6);

	// Load data
	/////////////
	// Check status
	wpce775_mcu2_mmio_status_check_loop(wpce775, 0x81, 0x00, 0xb4);

	offset = 0;
	mmio_writeb(0xb8, wpce775->mcu2_mmio_dev + 0x03);
	while (len > 0) {
		mmio_writeb(buf[offset], wpce775->mcu2_mmio_dev + 0x04 + offset);
		offset++;
		len--;
	}

	mmio_writeb(0x01, wpce775->mcu2_mmio_dev);
	wpce775_mcu2_mmio_status_check_loop(wpce775, 0x80, 0x80, 0xb5);

	val = mmio_readb(wpce775->mcu2_mmio_dev) & 0xfe;
	mmio_writeb(val, wpce775->mcu2_mmio_dev);
	wpce775_mcu2_mmio_status_check_loop(wpce775, 0x81, 0x00, 0xb6);
}

// Manipulate memory block protection (this is a guess)
void wpce775_mcu2_mmio_lock(struct wpce775_superio *wpce775, bool lock)
{
	int32_t lock_offset;
	uint8_t val;
	if (lock)
		val = 0x1;
	else
		val = 0x0;
	for (lock_offset = 0x1f0002; lock_offset >= 0x000002; lock_offset -= 0x10000) {
		msg_pdbg("	MCU 2: Lock: %p[0x%x] = %u\n", wpce775->lock_mmio_dev + lock_offset, \
lock_offset,val);  mmio_writeb(val, wpce775->lock_mmio_dev + lock_offset);
	}
}

/*
 * Lock
 */
// Open access to MCU 2 lock MMIO
bool wpce775_lock_mmio_open(struct wpce775_superio *wpce775)
{
	msg_pdbg("	Mapping 0x%x: ", wpce775->lock_mmio_base);
	wpce775->lock_mmio_dev = physmap("MPCE775 lock MMIO register", \
wpce775->lock_mmio_base, wpce775->lock_mmio_length);  if (wpce775->lock_mmio_dev == \
ERROR_PTR) {  msg_pdbg("Failed\n");
		return false;
	}
	msg_pdbg("%p\n", wpce775->lock_mmio_dev);
	return true;
}

// Close access to MCU 2 lock MMIO
void wpce775_lock_mmio_close(struct wpce775_superio *wpce775)
{
	msg_pdbg("	Unmapping 0x%x\n", wpce775->lock_mmio_base);
	physunmap(wpce775->lock_mmio_dev, wpce775->lock_mmio_length);
}

/*
 * ROM
 */
// Open access to MCU 2 MMIO
bool wpce775_rom_mmio_open(struct wpce775_superio *wpce775)
{
	msg_pdbg("	Mapping 0x%x: ", wpce775->rom_mmio_base);
	wpce775->rom_mmio_dev = physmap("MPCE775 ROM MMIO register", wpce775->rom_mmio_base, \
wpce775->rom_mmio_length);  if (wpce775->rom_mmio_dev == ERROR_PTR) {
		msg_pdbg("Failed\n");
		return false;
	}
	msg_pdbg("%p\n", wpce775->rom_mmio_dev);
	return true;
}

// Close access to MCU 2 MMIO
void wpce775_rom_mmio_close(struct wpce775_superio *wpce775)
{
	msg_pdbg("	Unmapping 0x%x\n", wpce775->rom_mmio_base);
	physunmap(wpce775->rom_mmio_dev, wpce775->rom_mmio_length);
}

/*
 * Combined access controls
 */
// Lock access to flash ROM
void wpce775_access_lock(struct wpce775_superio *wpce775)
{
	msg_pdbg("	WPCE775: Lock flash ROM access.\n");

	// MCU 2 mode exit
	wpce775_mcu2_mmio_mode_exit(wpce775);

	// Enable SMI
	wpce775_acpi_smistate(wpce775, true);

	// Lock
	wpce775_mcu2_mmio_lock(wpce775, true);

	// Disable BIOS writes
	wpce775_lpc_bios_write_enable(wpce775, false);

	// Clear SMI state
	wpce775_acpi_smistate_clear(wpce775);

	// Enable PS/2 KBC
	wpce775_kbc_enable(wpce775);
}

// Unlock access to flash ROM
int wpce775_access_unlock(struct wpce775_superio *wpce775)
{
	msg_pdbg("	WPCE775: Unlock flash ROM access.\n");

	// Disable PS/2 KBC
	wpce775_kbc_disable(wpce775);

	if (!wpce775_mcu2_mmio_mode_init(wpce775)) {
		msg_pdbg("		Failed to init MCU 2\n");
		return -1;
	}

	// MCU 1 init
	wpce775_mcu1_io_init(wpce775);

	// MCU 2 ...
	wpce775_mcu2_mmio_mode_flash(wpce775);

	// Disable SMI
	wpce775_acpi_smistate(wpce775, false);

	// Enable BIOS writes
	wpce775_lpc_bios_write_enable(wpce775, true);

	// Unlock
	wpce775_mcu2_mmio_lock(wpce775, false);

	return 0;
}

/*
 * Debugging
 */
// Dump ICH9 registers
int wpce775_dumpICH9Reg(struct pci_dev *lpc)
{
	uint32_t tmp32, mmio_base, mmio_len;
	void *mmio_dev;

	msg_pinfo("	Dumping LPC: ");
	if (!lpc) {
		msg_perr("ERROR: No known Intel LPC bridge found.\n");
		return -1;
	} else {
		msg_pinfo("Found\n");
	}

	msg_pinfo("Vendor ID: 0x%x\n", pci_read_word(lpc, 0x00));
	msg_pinfo("Device ID: 0x%x\n", pci_read_word(lpc, 0x02));
	msg_pinfo("PCI command: 0x%x\n", pci_read_word(lpc, 0x04));
	msg_pinfo("PCI status: 0x%x\n", pci_read_word(lpc, 0x06));

	msg_pinfo("Revision: 0x%x\n", pci_read_byte(lpc, 0x08));
	msg_pinfo("Prog. interface: 0x%x\n", pci_read_byte(lpc, 0x09));
	msg_pinfo("Sub Class Code: 0x%x\n", pci_read_byte(lpc, 0x0A));
	msg_pinfo("Base Class Code: 0x%x\n", pci_read_byte(lpc, 0x0B));
	msg_pinfo("Primary Latency Timer: 0x%x\n", pci_read_byte(lpc, 0x0D));
	msg_pinfo("Header Type: 0x%x\n", pci_read_byte(lpc, 0x0E));

	msg_pinfo("Sub System Identifiers: 0x%x\n", pci_read_long(lpc, 0x2C));

	msg_pinfo("Capability List Pointer: 0x%x\n", pci_read_byte(lpc, 0x34));

	msg_pinfo("ACPI Base Address: 0x%x\n", pci_read_long(lpc, 0x40));
	msg_pinfo("ACPI Control: 0x%x\n", pci_read_byte(lpc, 0x44));

	msg_pinfo("GPIO Base Address: 0x%x\n", pci_read_long(lpc, 0x48));
	msg_pinfo("GPIO Control: 0x%x\n", pci_read_byte(lpc, 0x4C));

	msg_pinfo("PIRQ[A–D] Routing Control: 0x%x\n", pci_read_long(lpc, 0x60));
	msg_pinfo("Serial IRQ Control: 0x%x\n", pci_read_byte(lpc, 0x64));
	msg_pinfo("PIRQ[E–H] Routing Control: 0x%x\n", pci_read_long(lpc, 0x68));
	msg_pinfo("IOxAPIC Bus:Device:Function: 0x%x\n", pci_read_word(lpc, 0x6c));

	msg_pinfo("I/O Decode Ranges: 0x%x\n", pci_read_word(lpc, 0x80));
	msg_pinfo("LPC I/F Enables: 0x%x\n", pci_read_word(lpc, 0x82));
	msg_pinfo("LPC I/F Generic Decode Range 1: 0x%x\n", pci_read_long(lpc, 0x84));
	msg_pinfo("LPC I/F Generic Decode Range 2: 0x%x\n", pci_read_long(lpc, 0x88));
	msg_pinfo("LPC I/F Generic Decode Range 3: 0x%x\n", pci_read_long(lpc, 0x8c));
	msg_pinfo("LPC I/F Generic Decode Range 4: 0x%x\n", pci_read_long(lpc, 0x90));

	msg_pinfo("Firmware Hub Select 1: 0x%x\n", pci_read_long(lpc, 0xd0));
	msg_pinfo("Firmware Hub Select 2: 0x%x\n", pci_read_word(lpc, 0xd4));
	msg_pinfo("Firmware Hub Decode Enable 1: 0x%x\n", pci_read_word(lpc, 0xd8));
	msg_pinfo("BIOS Control: 0x%x\n", pci_read_byte(lpc, 0xdc));

	msg_pinfo("Feature Detection Capability ID: 0x%x\n", pci_read_word(lpc, 0xe0));
	msg_pinfo("Feature Detection Capability Length: 0x%x\n", pci_read_byte(lpc, 0xe2));
	msg_pinfo("Feature Detection Version: 0x%x\n", pci_read_byte(lpc, 0xe3));
	msg_pinfo("Feature Vector1: 0x%x\n", pci_read_long(lpc, 0xe4));
	msg_pinfo("Feature Vector2: 0x%x\n", pci_read_long(lpc, 0xe8));

	tmp32 = pci_read_long(lpc, 0xf0);
	msg_pinfo("Root Complex Base Address: 0x%x\n", tmp32);

	mmio_base = (tmp32 & 0xfffffffe);
	mmio_len = 0x3800 * 2;
	msg_pinfo("	Mapping 0x%x: ", mmio_base);
	mmio_dev = physmap("SPI MMIO register", mmio_base, mmio_len);
	if (mmio_dev == ERROR_PTR) {
		msg_pinfo("Failed\n");
		return -1;
	} else {
		msg_pinfo("Okay\n");
	}

	msg_pinfo("Chipset Initialization Register 1: 0x%x\n", mmio_readl(mmio_dev + 0x88));
	msg_pinfo("Chipset Initialization Register 2: 0x%x\n", mmio_readl(mmio_dev + \
0x1F4));  msg_pinfo("Chipset Initialization Register 3: 0x%x\n", mmio_readl(mmio_dev \
+ 0x1FC));  msg_pinfo("Chipset Initialization Register 13: 0x%x\n", \
mmio_readl(mmio_dev + 0xF20));  msg_pinfo("Chipset Initialization Register 5a: \
0x%x\n", mmio_readl(mmio_dev + 0x1D40));  msg_pinfo("Chipset Initialization Register \
5b: 0x%x\n", mmio_readl(mmio_dev + 0x1D44));  msg_pinfo("Chipset Initialization \
Register 6: 0x%x\n", mmio_readl(mmio_dev + 0x2024));  msg_pinfo("Chipset \
Initialization Register 7: 0x%x\n", mmio_readl(mmio_dev + 0x2034));  \
msg_pinfo("Chipset Initialization Register 11: 0x%x\n", mmio_readl(mmio_dev + \
0x20C4));  msg_pinfo("Chipset Initialization Register 12: 0x%x\n", \
mmio_readl(mmio_dev + 0x20E4));  msg_pinfo("Chipset Initialization Register 8: \
0x%x\n", mmio_readl(mmio_dev + 0x3430));  msg_pinfo("Chipset Initialization Register \
9: 0x%x\n", mmio_readl(mmio_dev + 0x350C));  msg_pinfo("Chipset Initialization \
Register 10: 0x%x\n", mmio_readl(mmio_dev + 0x352C));

	msg_pinfo("General Control and Status: 0x%x\n", mmio_readl(mmio_dev + 0x3410));

	msg_pinfo("BIOS Flash Primary Region: 0x%x\n", mmio_readl(mmio_dev + 0x3800 + \
0x00));  msg_pinfo("Hardware Sequencing Flash Status: 0x%x\n", mmio_readw(mmio_dev + \
0x3800 + 0x04));  msg_pinfo("Hardware Sequencing Flash Control: 0x%x\n", \
mmio_readw(mmio_dev + 0x3800 + 0x06));  msg_pinfo("Flash Address: 0x%x\n", \
mmio_readl(mmio_dev + 0x3800 + 0x08));

	msg_pinfo("Flash Data 0: 0x%x\n", mmio_readl(mmio_dev + 0x3800 + 0x10));
	msg_pinfo("Flash Data 1: 0x%x\n", mmio_readl(mmio_dev + 0x3800+ 0x14));
	msg_pinfo("Flash Data 2: 0x%x\n", mmio_readl(mmio_dev + 0x3800 + 0x18));
	msg_pinfo("Flash Data 3: 0x%x\n", mmio_readl(mmio_dev + 0x3800 + 0x1C));
	msg_pinfo("Flash Data 4: 0x%x\n", mmio_readl(mmio_dev + 0x3800 + 0x20));
	msg_pinfo("Flash Data 5: 0x%x\n", mmio_readl(mmio_dev + 0x3800 + 0x24));
	msg_pinfo("Flash Data 6: 0x%x\n", mmio_readl(mmio_dev + 0x3800 + 0x28));
	msg_pinfo("Flash Data 7: 0x%x\n", mmio_readl(mmio_dev + 0x3800 + 0x2C));
	msg_pinfo("Flash Data 8: 0x%x\n", mmio_readl(mmio_dev + 0x3800 + 0x30));
	msg_pinfo("Flash Data 9: 0x%x\n", mmio_readl(mmio_dev + 0x3800 + 0x34));
	msg_pinfo("Flash Data 10: 0x%x\n", mmio_readl(mmio_dev + 0x3800 + 0x38));
	msg_pinfo("Flash Data 11: 0x%x\n", mmio_readl(mmio_dev + 0x3800 + 0x3C));
	msg_pinfo("Flash Data 12: 0x%x\n", mmio_readl(mmio_dev + 0x3800 + 0x40));
	msg_pinfo("Flash Data 13: 0x%x\n", mmio_readl(mmio_dev + 0x3800 + 0x44));
	msg_pinfo("Flash Data 14: 0x%x\n", mmio_readl(mmio_dev + 0x3800 + 0x48));
	msg_pinfo("Flash Data 15: 0x%x\n", mmio_readl(mmio_dev + 0x3800 + 0x4C));

	msg_pinfo("Flash Region Access Permissions: 0x%x\n", mmio_readl(mmio_dev + 0x3800 + \
0x50));  msg_pinfo("Flash Region 0: 0x%x\n", mmio_readl(mmio_dev + 0x3800 + 0x54));
	msg_pinfo("Flash Region 1: 0x%x\n", mmio_readl(mmio_dev + 0x3800 + 0x58));
	msg_pinfo("Flash Region 2: 0x%x\n", mmio_readl(mmio_dev + 0x3800 + 0x5C));
	msg_pinfo("Flash Region 3: 0x%x\n", mmio_readl(mmio_dev + 0x3800 + 0x60));
	msg_pinfo("Flash Region 4: 0x%x\n", mmio_readl(mmio_dev + 0x3800 + 0x64));

	msg_pinfo("Flash Protected Range 0: 0x%x\n", mmio_readl(mmio_dev + 0x3800 + 0x74));
	msg_pinfo("Flash Protected Range 1: 0x%x\n", mmio_readl(mmio_dev + 0x3800 + 0x78));
	msg_pinfo("Flash Protected Range 2: 0x%x\n", mmio_readl(mmio_dev + 0x3800 + 0x7C));
	msg_pinfo("Flash Protected Range 3: 0x%x\n", mmio_readl(mmio_dev + 0x3800 + 0x80));
	msg_pinfo("Flash Protected Range 4: 0x%x\n", mmio_readl(mmio_dev + 0x3800 + 0x84));

	msg_pinfo("Software Sequencing Flash Status: 0x%x\n", mmio_readb(mmio_dev + 0x3800 + \
0x90));  msg_pinfo("Software Sequencing Flash Control: 0x%x\n", mmio_readw(mmio_dev + \
0x3800 + 0x91));

	msg_pinfo("Prefix Opcode Configuration: 0x%x\n", mmio_readw(mmio_dev + 0x3800 + \
0x94));  msg_pinfo("Opcode Type Configuration: 0x%x\n", mmio_readw(mmio_dev + 0x3800 \
+ 0x96));  msg_pinfo("Opcode Menu Configuration1: 0x%x\n", mmio_readl(mmio_dev + \
0x3800 + 0x98));  msg_pinfo("Opcode Menu Configuration2: 0x%x\n", mmio_readl(mmio_dev \
+ 0x3800 + 0x9C));

	msg_pinfo("BIOS Base Address Configuration: 0x%x\n", mmio_readl(mmio_dev + 0x3800 + \
0xA0));

	msg_pinfo("Flash Descriptor Observability Control: 0x%x\n", mmio_readl(mmio_dev + \
0x3800 + 0xB0));  msg_pinfo("Flash Descriptor Observability Data: 0x%x\n", \
mmio_readl(mmio_dev + 0x3800 + 0xB4));

	msg_pinfo("Additional Flash Control: 0x%x\n", mmio_readl(mmio_dev + 0x3800 + 0xC0));
	msg_pinfo("Host Lower Vendor Specific Component Capabilities: 0x%x\n", \
mmio_readl(mmio_dev + 0x3800 + 0xC4));  msg_pinfo("Host Upper Vendor Specific \
Component Capabilities: 0x%x\n", mmio_readl(mmio_dev + 0x3800 + 0xC8));

	msg_pinfo("Flash Partition Boundary: 0x%x\n", mmio_readl(mmio_dev + 0x3800 + 0xD0));

	physunmap(mmio_dev, mmio_len);

	return 0;
}

///////////////////////////////////////////////////////////////////////////////////////////////
 // Programmer
///////////////////////////////////////////////////////////////////////////////////////////////


static int wpce775_probe(struct flashctx *flash)
{
	struct wpce775_superio *wpce775 = flash->mst->opaque.data;
	const struct flashchip *entry;
	uint32_t jedec_id, mfg_id, model_id, i,j;
	bool flash_found = false;

	msg_pdbg("\nWPCE775 SuperIO: Probe.\n");

	// Get access to WPCE775
	wpce775_mcu2_mmio_mode_init(wpce775);

	jedec_id = wpce775_mcu2_mmio_mode_jedecid(wpce775);
	msg_pdbg("	Found: 0x%x\n", jedec_id);

	// Revoke access to WPCE775
	wpce775_mcu2_mmio_mode_exit(wpce775);

	mfg_id = jedec_id & 0xff;
	model_id = (jedec_id & 0xff00) >> 8;

	// Try to find flash info in table
	for (entry = &flashchips[0]; entry->vendor; entry++) {
		if ((entry->manufacture_id == mfg_id) &&
		    (entry->model_id == model_id) &&
		    //(entry->probe == PROBE_SPI_RDID) &&
		    ((entry->bustype & BUS_SPI) == BUS_SPI)) {
			flash_found = true;
			break;
		}
	}
	if (!flash_found) {
		msg_pwarn("	Unable to identify chip, mfg_id: 0x%02x, model_id: 0x%02x\n", mfg_id, \
model_id);  return -1;
	}
	msg_pdbg("	Chip identified: %s\n", entry->name);

	/* Update informational flash chip entries only */
	flash->chip->vendor = entry->vendor;
	flash->chip->name = entry->name;
	flash->chip->manufacture_id = entry->manufacture_id;
	flash->chip->model_id = entry->model_id;
	/* total_size read from flash descriptor */
	flash->chip->total_size = entry->total_size;
	flash->chip->page_size = entry->page_size;
	flash->chip->feature_bits = entry->feature_bits;
	flash->chip->tested = entry->tested;
	/* Support writeprotect */
	flash->chip->reg_bits = entry->reg_bits;
	flash->chip->decode_range = entry->decode_range;

	// Copy block erasers
	for (i = 0; i < NUM_ERASEFUNCTIONS; i++) {
		for (j = 0; j < NUM_ERASEREGIONS; j++) {
			flash->chip->block_erasers[i].eraseblocks[j] = \
entry->block_erasers[i].eraseblocks[j];  }
	}

	return 1;
}

static int wpce775_read(struct flashctx *flash, uint8_t *buf, unsigned int addr, \
unsigned int len) {
	struct wpce775_superio *wpce775 = flash->mst->opaque.data;
	uint32_t len_copy;

	msg_pdbg("\nWPCE775 SuperIO: Read.\n");
	msg_pdbg("	Reading data from flash: address=0x%x size=0x%x\n", addr, len);

	// Grant access
	if (wpce775_access_unlock(wpce775)) return -1;

	while (len > 0) {
		// Max 64k per access (from phlash16)
		if (len > 0x10000) {
			len_copy = 0x10000;
		} else {
			len_copy = len;
		}

		// Read data into buffer
		mmio_readn(wpce775->rom_mmio_dev+addr, buf, len_copy);

		// Update pointers & remaining size
		len -= len_copy;
		addr += len_copy;
		buf += len_copy;
	}

	// Revoke access
	wpce775_access_lock(wpce775);

	return 0;
}

static int wpce775_write(struct flashctx *flash, const uint8_t *buf,
				  unsigned int addr, unsigned int len)
{
	struct wpce775_superio *wpce775 = flash->mst->opaque.data;
	uint32_t len_copy;

	msg_pdbg("\nWPCE775 SuperIO: Write.\n");
	msg_pdbg("	Writing data to flash: address=0x%x size=0x%x\n", addr, len);

	// Check if this is within address space
	if ((addr + len) > (flash->chip->total_size * 1024)) return -1;

	// Check that the size of data to be written is divisable by 8 bytes
	if ((len % 8) != 0) return -2;

	// Update address to MMIO address space
	addr += wpce775->rom_mmio_base;

	// Grant access
	if (wpce775_access_unlock(wpce775)) return -3;

	while (len > 0) {
		// MCU2 program bytes routine has fixed length of 8 bytes
		len_copy = 0x8;

		// Write data from buffer
		wpce775_mcu2_mmio_mode_program(wpce775, buf, addr, len_copy);

		// Update pointers & remaining size
		len -= len_copy;
		addr += len_copy;
		buf += len_copy;
	}

	// Revoke access
	wpce775_access_lock(wpce775);

	return 0;
}

static int wpce775_erase(struct flashctx *flash, unsigned int addr, unsigned int len)
{
	struct wpce775_superio *wpce775 = flash->mst->opaque.data;
	uint32_t len_erase;

	msg_pdbg("\nWPCE775 SuperIO: Erase.\n");
	msg_pdbg("	Erasing data from flash: address=0x%x size=0x%x\n", addr, len);

	// Check if this is within address space
	if ((addr + len) > (flash->chip->total_size * 1024)) return -1;

	// Check that the address is on a sector boundary
	if ((addr % flash->chip->page_size) != 0) return -2;

	// Update address to MMIO address space
	addr += wpce775->rom_mmio_base;

	// Grant access
	if (wpce775_access_unlock(wpce775)) return -3;

	while (len > 0) {
		// Erase length set by page_size
		len_erase = flash->chip->page_size;

		// Erase sector
		wpce775_mcu2_mmio_mode_erase(wpce775, addr);

		// Update pointers & remaining size
		len -= len_erase;
		addr += len_erase;
	}

	// Revoke access
	wpce775_access_lock(wpce775);

	return 0;
}

static int wpce775_shutdown(void *data)
{
	struct wpce775_superio *wpce775 = data;

	msg_pdbg("\nWPCE775 SuperIO: Shutdown.\n");

	if (wpce775 != NULL) {
		// MCU2: Revoke access permission
		//wpce775_mcu2_mmio_mode_exit(wpce775);

		// Unmap ROM
		wpce775_rom_mmio_close(wpce775);

		// Unmap lock
		wpce775_lock_mmio_close(wpce775);

		// MCU2: Unmap MMIO
		wpce775_mcu2_mmio_close(wpce775);

		// Free resources
		free(wpce775);

		// Enable power button
		wpce775_acpi_pwrbtnen(wpce775, true);
	}

	return 0;
}

static const struct opaque_master opaque_master_wpce775 = {
	.probe		= wpce775_probe,
	.read		= wpce775_read,
	.write		= wpce775_write,
	.erase		= wpce775_erase,
	.shutdown	= wpce775_shutdown,
};

///////////////////////////////////////////////////////////////////////////////////////////////
 // FlashROM
///////////////////////////////////////////////////////////////////////////////////////////////


static int wpce775_init(const struct programmer_cfg *cfg)
{
	struct wpce775_superio *wpce775;
	int ret = 0;

	msg_pdbg("\nWPCE775 SuperIO: Initialise.\n");

	// Initialize PCI access for flash enables
	if (pci_init_common() != 0) return -1;
	// Get IO permissions
	if (rget_io_perms()) return-2;

	// Init struct
	msg_pdbg("	Allocating memory for structure: ");
	wpce775 = malloc(sizeof(struct wpce775_superio));
	if (wpce775 == NULL) {
		msg_perr("WPCE775: Out of Memory\n");
		return -3;
	}
	msg_pdbg("Okay\n");
	// Set base parameters
	wpce775->mcu1_io_base = WPCE775_SUPERIO_PORT3;
	wpce775->mcu2_io_base = WPCE775_SUPERIO_PORT2;
	wpce775->mcu2_mmio_base = WPCE775_MMIO_ADDR;
	wpce775->mcu2_mmio_length = WPCE775_MMIO_LEN;
	wpce775->lock_mmio_base = WPCE775_LOCK_ADDR;
	wpce775->lock_mmio_length = WPCE775_LOCK_LEN;
	wpce775->rom_mmio_base = WPCE775_ROM_ADDR;
	wpce775->rom_mmio_length = WPCE775_ROM_LEN;

	// Probe for the PCI LPC ISA bridge
	if (!wpce775_lpc_probe(wpce775)) {
		ret = -4;
		goto internal_init_exit;
	}

	// Disable power button (needs to be after LPC probe)
	wpce775_acpi_pwrbtnen(wpce775, false);

	// LPC
	if (!wpce775_lpc_conf_chk(wpce775)) {
		ret = -5;
		goto internal_init_exit;
	}

	// MCU 2 init
	wpce775_mcu2_mmio_init(wpce775);
	if (!wpce775_mcu2_mmio_open(wpce775)) {
		ret = -6;
		goto internal_init_exit;
	}
	// Map lock
	if (!wpce775_lock_mmio_open(wpce775)) {
		ret = -7;
		goto internal_init_exit;
	}
	// Map ROM
	if (!wpce775_rom_mmio_open(wpce775)) {
		ret = -8;
		goto internal_init_exit;
	}

	// Reset WPCE775 state (clear stuck command)
	wpce775_mcu2_mmio_status_runclear(wpce775, 0xff);

	return register_opaque_master(&opaque_master_wpce775, wpce775);

internal_init_exit:
	wpce775_shutdown(wpce775);
	return ret;
}

const struct programmer_entry programmer_wpce775 = {
	.name			= "wpce775",
	.type			= PCI,
	.devs.note		= "Programmer for SPI flash ROMs behind a WPCE775 SuperIO",
	.init			= wpce775_init,
};

#endif


["wpce775.h" (application/octet-stream)]

_______________________________________________
flashrom mailing list -- flashrom@flashrom.org
To unsubscribe send an email to flashrom-leave@flashrom.org


[prev in list] [next in list] [prev in thread] [next in thread] 

Configure | About | News | Add a list | Sponsored by KoreLogic