sb600spi.c 4.84 KB
Newer Older
1 2 3
/*
 * This file is part of the flashrom project.
 *
4 5
 * Copyright (C) 2008 Wang Qingpei <Qingpei.Wang@amd.com>
 * Copyright (C) 2008 Joe Bao <Zheng.Bao@amd.com>
Uwe Hermann's avatar
Uwe Hermann committed
6
 * Copyright (C) 2008 Advanced Micro Devices, Inc.
7
 * Copyright (C) 2009, 2010 Carl-Daniel Hailfinger
8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
 *
 * 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
 */

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

26
#include "flash.h"
27
#include "chipdrivers.h"
28 29
#include "spi.h"

30 31 32 33 34 35 36 37 38 39 40 41 42
/* This struct is unused, but helps visualize the SB600 SPI BAR layout.
 *struct sb600_spi_controller {
 *	unsigned int spi_cntrl0;	/ * 00h * /
 *	unsigned int restrictedcmd1;	/ * 04h * /
 *	unsigned int restrictedcmd2;	/ * 08h * /
 *	unsigned int spi_cntrl1;	/ * 0ch * /
 *	unsigned int spi_cmdvalue0;	/ * 10h * /
 *	unsigned int spi_cmdvalue1;	/ * 14h * /
 *	unsigned int spi_cmdvalue2;	/ * 18h * /
 *	unsigned int spi_fakeid;	/ * 1Ch * /
 *};
 */

43
uint8_t *sb600_spibar = NULL;
44

45
int sb600_spi_read(struct flashchip *flash, uint8_t *buf, int start, int len)
46
{
47
	/* Maximum read length is 8 bytes. */
48
	return spi_read_chunked(flash, buf, start, len, 8);
49 50
}

51
int sb600_spi_write_256(struct flashchip *flash, uint8_t *buf, int start, int len)
52
{
53
	spi_disable_blockprotect();
54
	return spi_write_chunked(flash, buf, start, len, 5);
55 56
}

57
static void reset_internal_fifo_pointer(void)
58
{
59
	mmio_writeb(mmio_readb(sb600_spibar + 2) | 0x10, sb600_spibar + 2);
60

61
	while (mmio_readb(sb600_spibar + 0xD) & 0x7)
62
		msg_pspew("reset\n");
63 64
}

65
static void execute_command(void)
66
{
67
	mmio_writeb(mmio_readb(sb600_spibar + 2) | 1, sb600_spibar + 2);
68

69
	while (mmio_readb(sb600_spibar + 2) & 1)
70 71 72
		;
}

73
int sb600_spi_send_command(unsigned int writecnt, unsigned int readcnt,
74 75 76 77 78
		      const unsigned char *writearr, unsigned char *readarr)
{
	int count;
	/* First byte is cmd which can not being sent through FIFO. */
	unsigned char cmd = *writearr++;
79
	unsigned int readoffby1;
80 81 82

	writecnt--;

83 84
	msg_pspew("%s, cmd=%x, writecnt=%x, readcnt=%x\n",
		  __func__, cmd, writecnt, readcnt);
85 86

	if (readcnt > 8) {
87
		msg_pinfo("%s, SB600 SPI controller can not receive %d bytes, "
88 89
		       "it is limited to 8 bytes\n", __func__, readcnt);
		return SPI_INVALID_LENGTH;
90 91 92
	}

	if (writecnt > 8) {
93
		msg_pinfo("%s, SB600 SPI controller can not send %d bytes, "
94 95
		       "it is limited to 8 bytes\n", __func__, writecnt);
		return SPI_INVALID_LENGTH;
96 97
	}

98 99 100 101 102 103 104 105
	/* This is a workaround for a bug in SB600 and SB700. If we only send
	 * an opcode and no additional data/address, the SPI controller will
	 * read one byte too few from the chip. Basically, the last byte of
	 * the chip response is discarded and will not end up in the FIFO.
	 * It is unclear if the CS# line is set high too early as well.
	 */
	readoffby1 = (writecnt) ? 0 : 1;
	mmio_writeb((readcnt + readoffby1) << 4 | (writecnt), sb600_spibar + 1);
106
	mmio_writeb(cmd, sb600_spibar + 0);
107 108 109 110 111 112

	/* Before we use the FIFO, reset it first. */
	reset_internal_fifo_pointer();

	/* Send the write byte to FIFO. */
	for (count = 0; count < writecnt; count++, writearr++) {
113
		msg_pspew(" [%x]", *writearr);
114
		mmio_writeb(*writearr, sb600_spibar + 0xC);
115
	}
116
	msg_pspew("\n");
117 118 119 120 121 122 123 124 125 126 127

	/*
	 * We should send the data by sequence, which means we need to reset
	 * the FIFO pointer to the first byte we want to send.
	 */
	reset_internal_fifo_pointer();

	execute_command();

	/*
	 * After the command executed, we should find out the index of the
128 129 130 131 132 133 134 135
	 * received byte. Here we just reset the FIFO pointer and skip the
	 * writecnt.
	 * It would be possible to increase the FIFO pointer by one instead
	 * of reading and discarding one byte from the FIFO.
	 * The FIFO is implemented on top of an 8 byte ring buffer and the
	 * buffer is never cleared. For every byte that is shifted out after
	 * the opcode, the FIFO already stores the response from the chip.
	 * Usually, the chip will respond with 0x00 or 0xff.
136 137 138
	 */
	reset_internal_fifo_pointer();

139
	/* Skip the bytes we sent. */
140
	for (count = 0; count < writecnt; count++) {
141
		cmd = mmio_readb(sb600_spibar + 0xC);
142
		msg_pspew("[ %2x]", cmd);
143 144
	}

145 146
	msg_pspew("The FIFO pointer after skipping is %d.\n",
		  mmio_readb(sb600_spibar + 0xd) & 0x07);
147
	for (count = 0; count < readcnt; count++, readarr++) {
148
		*readarr = mmio_readb(sb600_spibar + 0xC);
149
		msg_pspew("[%02x]", *readarr);
150
	}
151
	msg_pspew("\n");
152 153 154

	return 0;
}
155 156

#endif