mirror of
https://review.coreboot.org/flashrom.git
synced 2025-04-27 07:02:34 +02:00

write_aai is required to register a spi master, if it is not set in spi_master struct then register_spi_master returns ERROR_FLASHROM_BUG. Masters in this patch did not have it set in the struct, and register_spi_master always returned an error for them. However return value of register_spi_master was ignored, so this was hard to notice. Next patch in the chain checks return value of register_spi_master. BUG=b:185191942 TEST=builds and ninja test Change-Id: I712e74e11244e1f0ab8d8e245fcd5207ce211219 Signed-off-by: Anastasia Klimchuk <aklm@chromium.org> Reviewed-on: https://review.coreboot.org/c/flashrom/+/56102 Tested-by: build bot (Jenkins) <no-reply@coreboot.org> Reviewed-by: Edward O'Callaghan <quasisec@chromium.org> Reviewed-by: Nico Huber <nico.h@gmx.de>
537 lines
16 KiB
C
537 lines
16 KiB
C
/*
|
|
* This file is part of the flashrom project.
|
|
*
|
|
* Copyright (C) 2010-2020, Google Inc.
|
|
* All rights reserved.
|
|
*
|
|
* Redistribution and use in source and binary forms, with or without
|
|
* modification, are permitted provided that the following conditions are
|
|
* met:
|
|
*
|
|
* * Redistributions of source code must retain the above copyright
|
|
* notice, this list of conditions and the following disclaimer.
|
|
* * Redistributions in binary form must reproduce the above
|
|
* copyright notice, this list of conditions and the following disclaimer
|
|
* in the documentation and/or other materials provided with the
|
|
* distribution.
|
|
* * Neither the name of Google Inc. nor the names of its
|
|
* contributors may be used to endorse or promote products derived from
|
|
* this software without specific prior written permission.
|
|
*
|
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
|
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
|
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
|
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
|
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
|
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
|
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
|
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
|
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
|
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
*
|
|
* Alternatively, this software may be distributed under the terms of the
|
|
* GNU General Public License ("GPL") version 2 as published by the Free
|
|
* Software Foundation.
|
|
*/
|
|
|
|
#if defined(__i386__) || defined(__x86_64__)
|
|
#include <inttypes.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <unistd.h>
|
|
|
|
#include "flash.h"
|
|
#include "hwaccess.h"
|
|
#include "chipdrivers.h"
|
|
#include "programmer.h"
|
|
#include "spi.h"
|
|
|
|
#define MEC1308_SIO_PORT1 0x2e
|
|
#define MEC1308_SIO_PORT2 0x4e
|
|
#define MEC1308_SIO_ENTRY_KEY 0x55
|
|
#define MEC1308_SIO_EXIT_KEY 0xaa
|
|
|
|
#define MEC1308_SIOCFG_LDN 0x07 /* LDN Bank Selector */
|
|
#define MEC1308_DEVICE_ID_REG 0x20 /* Device ID Register */
|
|
#define MEC1308_DEVICE_ID_VAL 0x4d /* Device ID Value for MEC1308 */
|
|
#define MEC1310_DEVICE_ID_VAL 0x04 /* Device ID Value for MEC1310 */
|
|
#define MEC1308_DEVICE_REV 0x21 /* Device Revision ID Register */
|
|
|
|
#define MEC1308_MBX_CMD 0x82 /* mailbox command register offset */
|
|
#define MEC1308_MBX_EXT_CMD 0x83 /* mailbox ext. command reg offset */
|
|
#define MEC1308_MBX_DATA_START 0x84 /* first mailbox data register offset */
|
|
#define MEC1308_MBX_DATA_END 0x91 /* last mailbox data register offset */
|
|
|
|
static unsigned int mbx_data; /* Mailbox register interface data address*/
|
|
|
|
/*
|
|
* These command codes depend on EC firmware. The ones listed below are input
|
|
* using the mailbox interface, though others may be input using the ACPI
|
|
* interface. Some commands also have an output value (ie pass/failure code)
|
|
* which EC writes to the mailbox command register after completion.
|
|
*/
|
|
#define MEC1308_CMD_SMI_ENABLE 0x84
|
|
#define MEC1308_CMD_SMI_DISABLE 0x85
|
|
#define MEC1308_CMD_ACPI_ENABLE 0x86
|
|
#define MEC1308_CMD_ACPI_DISABLE 0x87
|
|
|
|
/*
|
|
* Passthru commands are also input using the mailbox interface. Passthru mode
|
|
* enter/start/end commands are special since they require a command word to
|
|
* be written to the data registers. Other passthru commands are performed
|
|
* after passthru mode has been started.
|
|
*
|
|
* Multiple passthru mode commands may be issued before ending passthru mode.
|
|
* You do not need to enter, start, and end passthru mode for each SPI
|
|
* command. However, other mailbox commands might not work when passthru mode
|
|
* is enabled. For example, you may read all SPI chip content while in passthru
|
|
* mode, but you should exit passthru mode before performing other EC commands
|
|
* such as reading fan speed.
|
|
*/
|
|
#define MEC1308_CMD_PASSTHRU 0x55 /* force EC to process word */
|
|
#define MEC1308_CMD_PASSTHRU_SUCCESS 0xaa /* success code for passthru */
|
|
#define MEC1308_CMD_PASSTHRU_FAIL 0xfe /* failure code for passthru */
|
|
#define MEC1308_CMD_PASSTHRU_ENTER "PathThruMode" /* not a typo... */
|
|
#define MEC1308_CMD_PASSTHRU_START "Start"
|
|
#define MEC1308_CMD_PASSTHRU_EXIT "End_Mode"
|
|
#define MEC1308_CMD_PASSTHRU_CS_EN 0xf0 /* chip-select enable */
|
|
#define MEC1308_CMD_PASSTHRU_CS_DIS 0xf1 /* chip-select disable */
|
|
#define MEC1308_CMD_PASSTHRU_SEND 0xf2 /* send byte from data0 */
|
|
#define MEC1308_CMD_PASSTHRU_READ 0xf3 /* read byte, place in data0 */
|
|
|
|
typedef struct
|
|
{
|
|
unsigned int in_sio_cfgmode;
|
|
unsigned int mbx_idx; /* Mailbox register interface index address */
|
|
unsigned int mbx_data; /* Mailbox register interface data address*/
|
|
} mec1308_data_t;
|
|
|
|
static void mec1308_sio_enter(mec1308_data_t *ctx_data, uint16_t port)
|
|
{
|
|
if (ctx_data->in_sio_cfgmode)
|
|
return;
|
|
|
|
OUTB(MEC1308_SIO_ENTRY_KEY, port);
|
|
ctx_data->in_sio_cfgmode = 1;
|
|
}
|
|
|
|
static void mec1308_sio_exit(mec1308_data_t *ctx_data, uint16_t port)
|
|
{
|
|
if (!ctx_data->in_sio_cfgmode)
|
|
return;
|
|
|
|
OUTB(MEC1308_SIO_EXIT_KEY, port);
|
|
ctx_data->in_sio_cfgmode = 0;
|
|
}
|
|
|
|
/** probe for super i/o index
|
|
* @port: allocated buffer to store port
|
|
*
|
|
* returns 0 to indicate success, <0 to indicate error
|
|
*/
|
|
static int mec1308_get_sio_index(mec1308_data_t *ctx_data, uint16_t *port)
|
|
{
|
|
uint16_t ports[] = { MEC1308_SIO_PORT1,
|
|
MEC1308_SIO_PORT2,
|
|
};
|
|
size_t i;
|
|
static uint16_t port_internal, port_found = 0;
|
|
|
|
if (port_found) {
|
|
*port = port_internal;
|
|
return 0;
|
|
}
|
|
|
|
if (rget_io_perms())
|
|
return -1;
|
|
|
|
for (i = 0; i < ARRAY_SIZE(ports); i++) {
|
|
uint8_t tmp8;
|
|
|
|
/*
|
|
* Only after config mode has been successfully entered will the
|
|
* index port will read back the last value written to it.
|
|
* So we will attempt to enter config mode, set the index
|
|
* register, and see if the index register retains the value.
|
|
*
|
|
* Note: It seems to work "best" when using a device ID register
|
|
* as the index and reading from the data port before reading
|
|
* the index port.
|
|
*/
|
|
mec1308_sio_enter(ctx_data, ports[i]);
|
|
OUTB(MEC1308_DEVICE_ID_REG, ports[i]);
|
|
tmp8 = INB(ports[i] + 1);
|
|
tmp8 = INB(ports[i]);
|
|
if ((tmp8 != MEC1308_DEVICE_ID_REG)) {
|
|
ctx_data->in_sio_cfgmode = 0;
|
|
continue;
|
|
}
|
|
|
|
port_internal = ports[i];
|
|
port_found = 1;
|
|
break;
|
|
}
|
|
|
|
if (!port_found) {
|
|
msg_cdbg("\nfailed to obtain super i/o index\n");
|
|
return -1;
|
|
}
|
|
|
|
msg_cdbg("\nsuper i/o index = 0x%04x\n", port_internal);
|
|
*port = port_internal;
|
|
return 0;
|
|
}
|
|
|
|
static uint8_t mbx_read(mec1308_data_t *ctx_data, uint8_t idx)
|
|
{
|
|
OUTB(idx, ctx_data->mbx_idx);
|
|
return INB(mbx_data);
|
|
}
|
|
|
|
static int mbx_wait(mec1308_data_t *ctx_data)
|
|
{
|
|
int i;
|
|
int max_attempts = 10000;
|
|
int rc = 0;
|
|
|
|
for (i = 0; mbx_read(ctx_data, MEC1308_MBX_CMD); i++) {
|
|
if (i == max_attempts) {
|
|
rc = 1;
|
|
break;
|
|
}
|
|
/* FIXME: This delay adds determinism to the delay period. It
|
|
was chosen arbitrarily thru some experiments. */
|
|
programmer_delay(2);
|
|
}
|
|
|
|
return rc;
|
|
}
|
|
|
|
static int mbx_write(mec1308_data_t *ctx_data, uint8_t idx, uint8_t data)
|
|
{
|
|
int rc = 0;
|
|
|
|
if (idx == MEC1308_MBX_CMD && mbx_wait(ctx_data)) {
|
|
msg_perr("%s: command register not clear\n", __func__);
|
|
return 1;
|
|
}
|
|
|
|
OUTB(idx, ctx_data->mbx_idx);
|
|
OUTB(data, mbx_data);
|
|
|
|
if (idx == MEC1308_MBX_CMD)
|
|
rc = mbx_wait(ctx_data);
|
|
|
|
return rc;
|
|
}
|
|
|
|
static void mbx_clear(mec1308_data_t *ctx_data)
|
|
{
|
|
int reg;
|
|
|
|
for (reg = MEC1308_MBX_DATA_START; reg < MEC1308_MBX_DATA_END; reg++)
|
|
mbx_write(ctx_data, reg, 0x00);
|
|
mbx_write(ctx_data, MEC1308_MBX_CMD, 0x00);
|
|
}
|
|
|
|
static int mec1308_exit_passthru_mode(mec1308_data_t *ctx_data)
|
|
{
|
|
uint8_t tmp8;
|
|
size_t i;
|
|
|
|
/* exit passthru mode */
|
|
for (i = 0; i < strlen(MEC1308_CMD_PASSTHRU_EXIT); i++) {
|
|
mbx_write(ctx_data, MEC1308_MBX_DATA_START + i,
|
|
MEC1308_CMD_PASSTHRU_EXIT[i]);
|
|
}
|
|
|
|
if (mbx_write(ctx_data, MEC1308_MBX_CMD, MEC1308_CMD_PASSTHRU)) {
|
|
msg_pdbg("%s(): exit passthru command timed out\n", __func__);
|
|
return 1;
|
|
}
|
|
|
|
tmp8 = mbx_read(ctx_data, MEC1308_MBX_DATA_START);
|
|
msg_pdbg("%s: result: 0x%02x ", __func__, tmp8);
|
|
if (tmp8 == MEC1308_CMD_PASSTHRU_SUCCESS) {
|
|
msg_pdbg("(exited passthru mode)\n");
|
|
} else if (tmp8 == MEC1308_CMD_PASSTHRU_FAIL) {
|
|
msg_pdbg("(failed to exit passthru mode)\n");
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int enter_passthru_mode(mec1308_data_t *ctx_data)
|
|
{
|
|
uint8_t tmp8;
|
|
size_t i;
|
|
|
|
/*
|
|
* Enter passthru mode. If the EC does not successfully enter passthru
|
|
* mode the first time, we'll clear the mailbox and issue the "exit
|
|
* passthru mode" command sequence up to 3 times or until it arrives in
|
|
* a known state.
|
|
*
|
|
* Note: This workaround was developed experimentally.
|
|
*/
|
|
for (i = 0; i < 3; i++) {
|
|
size_t j;
|
|
|
|
msg_pdbg("%s(): entering passthru mode, attempt %d out of 3\n",
|
|
__func__, (int)(i + 1));
|
|
for (j = 0; j < strlen(MEC1308_CMD_PASSTHRU_ENTER); j++) {
|
|
mbx_write(ctx_data, MEC1308_MBX_DATA_START + j,
|
|
MEC1308_CMD_PASSTHRU_ENTER[j]);
|
|
}
|
|
|
|
if (mbx_write(ctx_data, MEC1308_MBX_CMD, MEC1308_CMD_PASSTHRU))
|
|
msg_pdbg("%s(): enter passthru command timed out\n",
|
|
__func__);
|
|
|
|
tmp8 = mbx_read(ctx_data, MEC1308_MBX_DATA_START);
|
|
if (tmp8 == MEC1308_CMD_PASSTHRU_SUCCESS)
|
|
break;
|
|
|
|
msg_pdbg("%s(): command failed, clearing data registers and "
|
|
"issuing full exit passthru command...\n", __func__);
|
|
mbx_clear(ctx_data);
|
|
mec1308_exit_passthru_mode(ctx_data);
|
|
}
|
|
|
|
if (tmp8 != MEC1308_CMD_PASSTHRU_SUCCESS) {
|
|
msg_perr("%s(): failed to enter passthru mode, result=0x%02x\n",
|
|
__func__, tmp8);
|
|
return 1;
|
|
}
|
|
|
|
msg_pdbg("%s(): enter passthru mode return code: 0x%02x\n",
|
|
__func__, tmp8);
|
|
|
|
/* start passthru mode */
|
|
for (i = 0; i < strlen(MEC1308_CMD_PASSTHRU_START); i++)
|
|
mbx_write(ctx_data, MEC1308_MBX_DATA_START + i,
|
|
MEC1308_CMD_PASSTHRU_START[i]);
|
|
if (mbx_write(ctx_data, MEC1308_MBX_CMD, MEC1308_CMD_PASSTHRU)) {
|
|
msg_pdbg("%s(): start passthru command timed out\n", __func__);
|
|
return 1;
|
|
}
|
|
tmp8 = mbx_read(ctx_data, MEC1308_MBX_DATA_START);
|
|
if (tmp8 != MEC1308_CMD_PASSTHRU_SUCCESS) {
|
|
msg_perr("%s(): failed to enter passthru mode, result=%02x\n",
|
|
__func__, tmp8);
|
|
return 1;
|
|
}
|
|
msg_pdbg("%s(): start passthru mode return code: 0x%02x\n",
|
|
__func__, tmp8);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int mec1308_shutdown(void *data)
|
|
{
|
|
mec1308_data_t *ctx_data = (mec1308_data_t *)data;
|
|
|
|
/* Exit passthru mode before performing commands which do not affect
|
|
the SPI ROM */
|
|
mec1308_exit_passthru_mode(ctx_data);
|
|
|
|
/* Re-enable SMI and ACPI.
|
|
FIXME: is there an ordering dependency? */
|
|
if (mbx_write(ctx_data, MEC1308_MBX_CMD, MEC1308_CMD_SMI_ENABLE))
|
|
msg_pdbg("%s: unable to re-enable SMI\n", __func__);
|
|
if (mbx_write(ctx_data, MEC1308_MBX_CMD, MEC1308_CMD_ACPI_ENABLE))
|
|
msg_pdbg("%s: unable to re-enable ACPI\n", __func__);
|
|
|
|
free(data);
|
|
return 0;
|
|
}
|
|
|
|
static int mec1308_chip_select(mec1308_data_t *ctx_data)
|
|
{
|
|
return mbx_write(ctx_data, MEC1308_MBX_CMD, MEC1308_CMD_PASSTHRU_CS_EN);
|
|
}
|
|
|
|
static int mec1308_chip_deselect(mec1308_data_t *ctx_data)
|
|
{
|
|
return mbx_write(ctx_data, MEC1308_MBX_CMD, MEC1308_CMD_PASSTHRU_CS_DIS);
|
|
}
|
|
|
|
/*
|
|
* MEC1308 will not allow direct access to SPI chip from host if EC is
|
|
* connected to LPC bus. This function will forward commands issued thru
|
|
* mailbox interface to the SPI flash chip.
|
|
*/
|
|
static int mec1308_spi_send_command(const struct flashctx *flash, unsigned int writecnt,
|
|
unsigned int readcnt,
|
|
const unsigned char *writearr,
|
|
unsigned char *readarr)
|
|
{
|
|
int rc = 0;
|
|
size_t i;
|
|
mec1308_data_t *ctx_data = (mec1308_data_t *)flash->mst->spi.data;
|
|
|
|
if (mec1308_chip_select(ctx_data))
|
|
return 1;
|
|
|
|
for (i = 0; i < writecnt; i++) {
|
|
if (mbx_write(ctx_data, MEC1308_MBX_DATA_START, writearr[i]) ||
|
|
mbx_write(ctx_data, MEC1308_MBX_CMD, MEC1308_CMD_PASSTHRU_SEND)) {
|
|
msg_pdbg("%s: failed to issue send command\n",__func__);
|
|
rc = 1;
|
|
goto mec1308_spi_send_command_exit;
|
|
}
|
|
}
|
|
|
|
for (i = 0; i < readcnt; i++) {
|
|
if (mbx_write(ctx_data, MEC1308_MBX_CMD, MEC1308_CMD_PASSTHRU_READ)) {
|
|
msg_pdbg("%s: failed to issue read command\n",__func__);
|
|
rc = 1;
|
|
goto mec1308_spi_send_command_exit;
|
|
}
|
|
readarr[i] = mbx_read(ctx_data, MEC1308_MBX_DATA_START);
|
|
}
|
|
|
|
mec1308_spi_send_command_exit:
|
|
rc |= mec1308_chip_deselect(ctx_data);
|
|
return rc;
|
|
}
|
|
|
|
static const struct spi_master spi_master_mec1308 = {
|
|
.max_data_read = 256, /* FIXME: should be MAX_DATA_READ_UNLIMITED? */
|
|
.max_data_write = 256, /* FIXME: should be MAX_DATA_WRITE_UNLIMITED? */
|
|
.command = mec1308_spi_send_command,
|
|
.multicommand = default_spi_send_multicommand,
|
|
.read = default_spi_read,
|
|
.write_256 = default_spi_write_256,
|
|
.write_aai = default_spi_write_aai,
|
|
};
|
|
|
|
static int check_params(void)
|
|
{
|
|
int ret = 0;
|
|
char *const p = extract_programmer_param("type");
|
|
if (p && strcmp(p, "ec")) {
|
|
msg_pdbg("mec1308 only supports \"ec\" type devices\n");
|
|
ret = 1;
|
|
}
|
|
|
|
free(p);
|
|
return ret;
|
|
}
|
|
|
|
static int mec1308_init(void)
|
|
{
|
|
uint16_t sio_port;
|
|
uint8_t device_id;
|
|
uint8_t tmp8;
|
|
mec1308_data_t *ctx_data = NULL;
|
|
|
|
msg_pdbg("%s(): entered\n", __func__);
|
|
|
|
ctx_data = calloc(1, sizeof(mec1308_data_t));
|
|
if (!ctx_data) {
|
|
msg_perr("Unable to allocate space for extra context data.\n");
|
|
return 1;
|
|
}
|
|
|
|
if (check_params())
|
|
goto init_err_exit;
|
|
|
|
if (mec1308_get_sio_index(ctx_data, &sio_port) < 0) {
|
|
msg_pdbg("MEC1308 not found (probe failed).\n");
|
|
goto init_err_exit;
|
|
}
|
|
device_id = sio_read(sio_port, MEC1308_DEVICE_ID_REG);
|
|
switch(device_id) {
|
|
case MEC1308_DEVICE_ID_VAL:
|
|
msg_pdbg("Found EC: MEC1308 (ID:0x%02x,Rev:0x%02x) on "
|
|
"sio_port:0x%x.\n", device_id,
|
|
sio_read(sio_port, MEC1308_DEVICE_REV), sio_port);
|
|
break;
|
|
case MEC1310_DEVICE_ID_VAL:
|
|
msg_pdbg("Found EC: MEC1310 (ID:0x%02x,Rev:0x%02x) on "
|
|
"sio_port:0x%x.\n", device_id,
|
|
sio_read(sio_port, MEC1308_DEVICE_REV), sio_port);
|
|
break;
|
|
default:
|
|
msg_pdbg("MEC1308 not found\n");
|
|
goto init_err_exit;
|
|
}
|
|
|
|
/*
|
|
* setup mailbox interface at LDN 9
|
|
*/
|
|
sio_write(sio_port, MEC1308_SIOCFG_LDN, 0x09);
|
|
tmp8 = sio_read(sio_port, 0x30);
|
|
tmp8 |= 1;
|
|
sio_write(sio_port, 0x30, tmp8); /* activate logical device */
|
|
|
|
ctx_data->mbx_idx = (unsigned int)sio_read(sio_port, 0x60) << 8 |
|
|
sio_read(sio_port, 0x61);
|
|
mbx_data = ctx_data->mbx_idx + 1;
|
|
msg_pdbg("%s: mbx_idx: 0x%04x, mbx_data: 0x%04x\n",
|
|
__func__, ctx_data->mbx_idx, mbx_data);
|
|
|
|
/* Exit Super I/O config mode */
|
|
mec1308_sio_exit(ctx_data, sio_port);
|
|
|
|
/* Now that we can read the mailbox, we will wait for any remaining
|
|
* command to finish.*/
|
|
if (mbx_wait(ctx_data) != 0) {
|
|
msg_perr("%s: mailbox is not available\n", __func__);
|
|
goto init_err_exit;
|
|
}
|
|
|
|
/* Further setup -- disable SMI and ACPI.
|
|
FIXME: is there an ordering dependency? */
|
|
if (mbx_write(ctx_data, MEC1308_MBX_CMD, MEC1308_CMD_ACPI_DISABLE)) {
|
|
msg_pdbg("%s: unable to disable ACPI\n", __func__);
|
|
goto init_err_exit;
|
|
}
|
|
|
|
if (mbx_write(ctx_data, MEC1308_MBX_CMD, MEC1308_CMD_SMI_DISABLE)) {
|
|
msg_pdbg("%s: unable to disable SMI\n", __func__);
|
|
goto init_err_exit;
|
|
}
|
|
|
|
/*
|
|
* Enter SPI Pass-Thru Mode after commands which do not require access
|
|
* to SPI ROM are complete. We'll start by doing the exit_passthru_mode
|
|
* sequence, which is benign if the EC is already in passthru mode.
|
|
*/
|
|
mec1308_exit_passthru_mode(ctx_data);
|
|
|
|
if (enter_passthru_mode(ctx_data))
|
|
goto init_err_cleanup_exit;
|
|
|
|
internal_buses_supported |= BUS_LPC; /* for LPC <--> SPI bridging */
|
|
|
|
if (register_shutdown(mec1308_shutdown, ctx_data))
|
|
goto init_err_cleanup_exit;
|
|
register_spi_master(&spi_master_mec1308, ctx_data);
|
|
msg_pdbg("%s(): successfully initialized mec1308\n", __func__);
|
|
|
|
return 0;
|
|
|
|
init_err_cleanup_exit:
|
|
mec1308_shutdown(ctx_data);
|
|
return 1;
|
|
|
|
init_err_exit:
|
|
free(ctx_data);
|
|
return 1;
|
|
}
|
|
|
|
const struct programmer_entry programmer_mec1308 = {
|
|
.name = "mec1308",
|
|
.type = OTHER,
|
|
.devs.note = "Microchip MEC1308 Embedded Controller.\n",
|
|
.init = mec1308_init,
|
|
.map_flash_region = fallback_map,
|
|
.unmap_flash_region = fallback_unmap,
|
|
.delay = internal_delay,
|
|
};
|
|
|
|
#endif
|