aboutsummaryrefslogtreecommitdiffstats
path: root/sst_fwhub.c
blob: d688a350d948841afcb2d910fd1810a97e1f155b (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
/*
 * This file is part of the flashrom project.
 *
 * Copyright (C) 2000 Silicon Integrated System Corporation
 * Copyright (C) 2009 Kontron Modular Computers
 * Copyright (C) 2009 Sean Nelson <audiohacked@gmail.com>
 *
 * 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.
 */

/* Adapted from the Intel FW hub stuff for 82802ax parts. */

#include "flash.h"
#include "chipdrivers.h"

static int check_sst_fwhub_block_lock(struct flashctx *flash, unsigned int offset)
{
	chipaddr registers = flash->virtual_registers;
	uint8_t blockstatus;

	blockstatus = chip_readb(flash, registers + offset + 2);
	msg_cdbg("Lock status for 0x%06x (size 0x%06x) is %02x, ",
		     offset, flash->chip->page_size, blockstatus);
	switch (blockstatus & 0x3) {
	case 0x0:
		msg_cdbg("full access\n");
		break;
	case 0x1:
		msg_cdbg("write locked\n");
		break;
	case 0x2:
		msg_cdbg("locked open\n");
		break;
	case 0x3:
		msg_cdbg("write locked down\n");
		break;
	}
	/* Return content of the write_locked bit */
	return blockstatus & 0x1;
}

static int clear_sst_fwhub_block_lock(struct flashctx *flash, unsigned int offset)
{
	chipaddr registers = flash->virtual_registers;
	uint8_t blockstatus;

	blockstatus = check_sst_fwhub_block_lock(flash, offset);

	if (blockstatus) {
		msg_cdbg("Trying to clear lock for 0x%06x... ", offset);
		chip_writeb(flash, 0, registers + offset + 2);

		blockstatus = check_sst_fwhub_block_lock(flash, offset);
		msg_cdbg("%s\n", (blockstatus) ? "failed" : "OK");
	}

	return blockstatus;
}

int printlock_sst_fwhub(struct flashctx *flash)
{
	unsigned int i;

	for (i = 0; i < flash->chip->total_size * 1024; i += flash->chip->page_size)
		check_sst_fwhub_block_lock(flash, i);

	return 0;
}

int unlock_sst_fwhub(struct flashctx *flash)
{
	unsigned int i;
	int ret = 0;

	for (i = 0; i < flash->chip->total_size * 1024; i += flash->chip->page_size)
	{
		if (clear_sst_fwhub_block_lock(flash, i))
		{
			msg_cwarn("Warning: Unlock Failed for block 0x%06x\n", i);
			ret++;
		}
	}
	return ret;
}
o">- 5); } static int linux_spi_shutdown(void *data) { if (fd != -1) { close(fd); fd = -1; } return 0; } static int linux_spi_send_command(const struct flashctx *flash, unsigned int writecnt, unsigned int readcnt, const unsigned char *txbuf, unsigned char *rxbuf) { int iocontrol_code; struct spi_ioc_transfer msg[2] = { { .tx_buf = (uint64_t)(uintptr_t)txbuf, .len = writecnt, }, { .rx_buf = (uint64_t)(uintptr_t)rxbuf, .len = readcnt, }, }; if (fd == -1) return -1; /* The implementation currently does not support requests that don't start with sending a command. */ if (writecnt == 0) return SPI_INVALID_LENGTH; /* Just submit the first (write) request in case there is nothing to read. Otherwise submit both requests. */ if (readcnt == 0) iocontrol_code = SPI_IOC_MESSAGE(1); else iocontrol_code = SPI_IOC_MESSAGE(2); if (ioctl(fd, iocontrol_code, msg) == -1) { msg_cerr("%s: ioctl: %s\n", __func__, strerror(errno)); return -1; } return 0; } static const struct spi_master spi_master_linux = { .features = SPI_MASTER_4BA, .max_data_read = MAX_DATA_UNSPECIFIED, /* TODO? */ .max_data_write = MAX_DATA_UNSPECIFIED, /* TODO? */ .command = linux_spi_send_command, .multicommand = default_spi_send_multicommand, .read = linux_spi_read, .write_256 = linux_spi_write_256, .write_aai = default_spi_write_aai, }; int linux_spi_init(void) { char *p, *endp, *dev; uint32_t speed_hz = 2 * 1000 * 1000; /* FIXME: make the following configurable by CLI options. */ /* SPI mode 0 (beware this also includes: MSB first, CS active low and others */ const uint8_t mode = SPI_MODE_0; const uint8_t bits = 8; p = extract_programmer_param("spispeed"); if (p && strlen(p)) { speed_hz = (uint32_t)strtoul(p, &endp, 10) * 1000; if (p == endp || speed_hz == 0) { msg_perr("%s: invalid clock: %s kHz\n", __func__, p); free(p); return 1; } } else { msg_pinfo("Using default %"PRIu32 "kHz clock. Use 'spispeed' parameter to override.\n", speed_hz / 1000); } free(p); dev = extract_programmer_param("dev"); if (!dev || !strlen(dev)) { msg_perr("No SPI device given. Use flashrom -p " "linux_spi:dev=/dev/spidevX.Y\n"); free(dev); return 1; } msg_pdbg("Using device %s\n", dev); if ((fd = open(dev, O_RDWR)) == -1) { msg_perr("%s: failed to open %s: %s\n", __func__, dev, strerror(errno)); free(dev); return 1; } free(dev); if (register_shutdown(linux_spi_shutdown, NULL)) return 1; /* We rely on the shutdown function for cleanup from here on. */ if (ioctl(fd, SPI_IOC_WR_MAX_SPEED_HZ, &speed_hz) == -1) { msg_perr("%s: failed to set speed to %"PRIu32"Hz: %s\n", __func__, speed_hz, strerror(errno)); return 1; } msg_pdbg("Using %"PRIu32"kHz clock\n", speed_hz / 1000); if (ioctl(fd, SPI_IOC_WR_MODE, &mode) == -1) { msg_perr("%s: failed to set SPI mode to 0x%02x: %s\n", __func__, mode, strerror(errno)); return 1; } if (ioctl(fd, SPI_IOC_WR_BITS_PER_WORD, &bits) == -1) { msg_perr("%s: failed to set the number of bits per SPI word to %u: %s\n", __func__, bits == 0 ? 8 : bits, strerror(errno)); return 1; } /* Read max buffer size from sysfs, or use page size as fallback. */ FILE *fp; fp = fopen(BUF_SIZE_FROM_SYSFS, "r"); if (!fp) { msg_pwarn("Cannot open %s: %s.\n", BUF_SIZE_FROM_SYSFS, strerror(errno)); goto out; } char buf[10]; if (!fgets(buf, sizeof(buf), fp)) { if (feof(fp)) msg_pwarn("Cannot read %s: file is empty.\n", BUF_SIZE_FROM_SYSFS); else msg_pwarn("Cannot read %s: %s.\n", BUF_SIZE_FROM_SYSFS, strerror(errno)); goto out; } long int tmp; errno = 0; tmp = strtol(buf, NULL, 0); if ((tmp < 0) || errno) { msg_pwarn("Buffer size %ld from %s seems wrong.\n", tmp, BUF_SIZE_FROM_SYSFS); } else { msg_pdbg("%s: Using value from %s as max buffer size.\n", __func__, BUF_SIZE_FROM_SYSFS); max_kernel_buf_size = (size_t)tmp; } out: if (fp) fclose(fp); if (!max_kernel_buf_size) { msg_pdbg("%s: Using page size as max buffer size.\n", __func__); max_kernel_buf_size = (size_t)getpagesize(); } msg_pdbg("%s: max_kernel_buf_size: %zu\n", __func__, max_kernel_buf_size); register_spi_master(&spi_master_linux); return 0; } #endif // CONFIG_LINUX_SPI == 1