Submitter | David Hendricks |
---|---|
Date | 2011-07-07 00:32:11 |
Message ID | <CAKOoyUc6SqScdAsd2Hf=K0rLAmPKUO2-emVtKhUANMUL5628VA@mail.gmail.com> |
Download | mbox | patch |
Permalink | /patch/3258/ |
State | Changes Requested |
Headers | show |
Comments
Am 07.07.2011 02:32 schrieb David Hendricks: > On Tue, Jun 28, 2011 at 5:54 PM, Carl-Daniel Hailfinger > <c-d.hailfinger.devel.2006@gmx.net> wrote: > >> 2-3-add_arm_isms.patch >> Have you checked with an architecture guru that sync_primitive() can >> really be empty, not only for that arch in general, but also for >> accesses to a mmapped region the way flashrom does it? >> > Nope. Scattered resources/forum posts about ARMv7 suggest that peripheral > memory is strongly ordered. Also, the U-Boot code doesn't have special > handling for sync primitives AFAICT. > > I'm pretty new to ARM... Any advice from experts on this list would be > appreciated. > I can't help you directly, but if you tell a Linux kernel developer with ARM experience how we access such regions, he/she might have some useful info for you: open("/dev/mem", O_RDWR | O_SYNC), then mmap, then reading/writing those regions with "normal" memory accessors. Opening /dev/mem with O_SYNC sometimes already has the right propertiess and no additional ordering helper is required. > I've updated this patch set on top of the other patches you've been working > on (autodetect target processor <http://patchwork.coreboot.org/patch/3256/> > and the libpci hack <http://patchwork.coreboot.org/patch/3255/>). > Thanks. > The difference between this patchset and the previous one is pretty minor. > We can now discard the first patch which #ifdef'd out PCI code for ARM > targets. The remaining differences are mostly centered around the Makefile > and arch.h > > New patches are: > Signed-off-by: David Hendricks <dhendrix@google.com> > > > 2-2-add_tegra2_spi_controller.patch: > Index: flashrom-tegra2/processor_enable.c > =================================================================== > --- flashrom-tegra2.orig/processor_enable.c > +++ flashrom-tegra2/processor_enable.c > @@ -81,6 +81,48 @@ static int is_loongson(void) > } > #endif > > +#if defined(__arm__) > +#include <stdio.h> > +#include <string.h> > +#include <ctype.h> > + > +/* Returns true if the /proc/cpuinfo contains a line: "CPU part *: *0xc09". > + * TODO: need to extend in future for same SPI controller in chip family. > + */ > +static int is_tegra2(void) > +{ > + FILE *cpuinfo; > + uint32_t impl = 0, architecture = 0, variant = 0, part = 0; > + const char *name = "CPU part"; > + const char *value = "0xc09"; > + int ret = 0; > + > + cpuinfo = fopen("/proc/cpuinfo", "rb"); > Hm. Would it be possible to share some code with the Loongson cpuinfo parser? > + if (!cpuinfo) > + return 0; > + while (!feof(cpuinfo)) { > + char line[512], *ptr; > + if (fgets(line, sizeof(line), cpuinfo) == NULL) > + break; > + ptr = line; > + while (*ptr && isspace((unsigned char)*ptr)) > + ptr++; > + if (strncmp(ptr, name, strlen(name)) == 0) > + ptr += strlen(name); > + while (*ptr && isspace((unsigned char)*ptr)) > + ptr++; > + if (*ptr != ':') > + continue; > + ptr++; > + while (*ptr && isspace((unsigned char)*ptr)) > + ptr++; > + ret = (strncmp(ptr, value, strlen(value)) == 0); > + } > I'm going to trust you on this code. It seems to work for you. > + fclose(cpuinfo); > + return ret; > +} > +#endif > + > int processor_flash_enable(void) > { > /* FIXME: detect loongson on FreeBSD and OpenBSD as well. */ > @@ -90,6 +132,13 @@ int processor_flash_enable(void) > return 0; > } > #endif > + > +#if defined (__arm__) > + if (is_tegra2()) { > + msg_pdbg("Detected NVIDIA Tegra 2.\n"); > + return tegra2_spi_init(); > + } > +#endif > /* Not implemented yet. Oh well. */ > return 1; > } > Index: flashrom-tegra2/programmer.h > =================================================================== > --- flashrom-tegra2.orig/programmer.h > +++ flashrom-tegra2/programmer.h > @@ -524,6 +524,9 @@ enum spi_controller { > SPI_CONTROLLER_VIA, > SPI_CONTROLLER_WBSIO, > #endif > +#if defined(__arm__) > + SPI_CONTROLLER_TEGRA2, > +#endif > #endif > #if CONFIG_FT2232_SPI == 1 > SPI_CONTROLLER_FT2232, > @@ -589,6 +592,13 @@ int mcp6x_spi_init(int want_spi); > /* sb600spi.c */ > int sb600_probe_spi(struct pci_dev *dev); > > +/* tegra2_spi.c */ > +int tegra2_spi_init(void); > +int tegra2_spi_send_command(unsigned int writecnt, unsigned int readcnt, > + const unsigned char *writearr, unsigned char *readarr); > +int tegra2_spi_read(struct flashchip *flash, uint8_t *buf, int start, int len); > +int tegra2_spi_write(struct flashchip *flash, uint8_t *buf, int start, int len); > Can you kill the two lines above? > + > /* wbsio_spi.c */ > int wbsio_check_for_spi(void); > #endif > Index: flashrom-tegra2/tegra2_spi.c > =================================================================== > --- /dev/null > +++ flashrom-tegra2/tegra2_spi.c > @@ -0,0 +1,405 @@ > +/* > + * This file is part of the flashrom project. > + * > + * Copyright (C) 2010 NVIDIA Corporation > + * Copyright (C) 2011 Google Inc > + * > + * 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 > + */ > + > +#if defined(__arm__) > +#include <assert.h> > +#include <stdio.h> > +#include <stdlib.h> > +#include <string.h> > + > +#include "flash.h" > +#include "programmer.h" > +#include "tegra2_spi.h" > + > +static void *gpio_base, *clkrst_base, *apbmisc_base, *spi_base; > + > +#define SPI_TIMEOUT 50000 /* 100ms = 50000 * 2us */ > + > +/* On Seaboard: GPIO_PI3 = Port I = 8, bit = 3 */ > +#define UART_DISABLE_PORT 8 > +#define UART_DISABLE_BIT 3 > + > +/* Config port:bit as GPIO, not SFPIO (default) */ > +static void __set_config(unsigned port, unsigned bit, int type) > Rename type to val? Names starting with __ do not really make me happy. How about tegra2_spi_set_config? Or are there compelling cosmetic reasons? > +{ > + u32 u; > + > + msg_pdbg("%s: port = %d, bit = %d, %s\n", __func__, > + port, bit, type ? "GPIO" : "SFPIO"); > + > + u = mmio_readl(GPIO_CNF(port)); > + if (type) /* GPIO */ > + u |= 1 << bit; > + else > + u &= ~(1 << bit); > + rmmio_writel(u, GPIO_CNF(port)); > +} > + > +/* Config GPIO port:bit as input or output (OE) */ > +static void __set_direction(unsigned port, unsigned bit, int output) > Function rename, please (see above). > +{ > + u32 u; > + > + msg_pdbg("%s: port = %d, bit = %d, %s\n", __func__, > + port, bit, output ? "OUT" : "IN"); > + > + u = mmio_readl(GPIO_OE(port)); > + if (output) > + u |= 1 << bit; > + else > + u &= ~(1 << bit); > + rmmio_writel(u, GPIO_OE(port)); > +} > + > +/* set GPIO OUT port:bit as 0 or 1 */ > +static void __set_level(unsigned port, unsigned bit, int high) > Rename high to val? > +{ > + u32 u; > + > + msg_pdbg("%s: port = %d, bit %d == %d\n", __func__, > + port, bit, high); > + > + u = mmio_readl(GPIO_OUT(port)); > + if (high) > + u |= 1 << bit; > + else > + u &= ~(1 << bit); > + rmmio_writel(u, GPIO_OUT(port)); > +} > + > +/* set GPIO port:bit as an output, with polarity 'value' */ > +static int tg2_gpio_direction_output(unsigned port, unsigned bit, int value) > Rename value to val? Or rename val to value everywhere else in this patch? > +{ > + msg_pdbg("%s: port = %d, bit = %d, value = %d\n", > + __func__, port, bit, value); > + > + /* Configure as a GPIO */ > + __set_config(port, bit, 1); > + > + /* Configure GPIO output value. */ > + __set_level(port, bit, value); > + > + /* Configure GPIO direction as output. */ > + __set_direction(port, bit, 1); > + > + return 0; > +} > + > +static void spi_cs_activate(void) > +{ > + uint32_t *spi_cmd = (uint32_t *)spi_base; > + > + /* > + * CS is negated on Tegra, so drive a 1 to get a 0 > + */ > + mmio_writel(mmio_readl(spi_cmd) | SPI_CMD_CS_VAL, spi_cmd); > + msg_pspew("%s: CS driven %s\n", __func__, > + (mmio_readl(spi_cmd) & SPI_CMD_CS_VAL) ? "LOW" : "HIGH"); > +} > + > +static void spi_cs_deactivate(void) > +{ > + uint32_t *spi_cmd = (uint32_t *)spi_base; > + > + > + /* > + * CS is negated on Tegra, so drive a 0 to get a 1 > + */ > + mmio_writel(mmio_readl(spi_cmd) & ~SPI_CMD_CS_VAL, spi_cmd); > + msg_pspew("%s: CS driven %s\n", __func__, > + (mmio_readl(spi_cmd) & SPI_CMD_CS_VAL) ? "LOW" : "HIGH"); > +} > + > +/* Helper function to calculate the clock cycle in this round. > + * Also updates the byte count remaining to be used this round. > + * > + * For example, we want to write 6 bytes to SPI and then read 5 bytes back. > + * > + * +---+---+---+---+---+---+ > + * | W | W | W | W | W | W | > + * +---+---+---+---+---+---+---+---+---+---+---+ > + * | R | R | R | R | R | > + * +---+---+---+---+---+ > + * |<-- round 0 -->| > + * |<-- round 1 -->| > + * |<-- round 2 -->| > + * > + * So that the continuous calling this function would get: > + * > + * round| RET| writecnt readcnt bits to_write to_read > + * -----+----+--------------------------------------------- > + * INIT | | 6 5 > + * 0 | 1 | 2 5 32 4 0 > + * 1 | 1 | 0 3 32 2 2 > + * 2 | 1 | 0 0 24 0 3 > + * 3 | 0 | - - - - - > + * > + */ > +int next4Bytes(uint32_t *writecnt, uint32_t *readcnt, int *num_bits, > + uint32_t *to_write, uint32_t *to_read) { > + assert(writecnt); > Won't this explode in round 1+2? > + assert(readcnt); > Won't this explode in round 2? > + assert(num_bits); > + assert(to_write); > Explosions in round 2? > + assert(to_read); > Explosions in round 0? > + > + *to_write = min(*writecnt, 4); > + *to_read = min(*readcnt, 4 - *to_write); > + > + *writecnt -= *to_write; > + *readcnt -= *to_read; > + > + *num_bits = (*to_write + *to_read) * 8; > + > + if (*num_bits) > + return 1; /* need to be called again. */ > + else > + return 0; /* handled write and read requests. */ > +} > + > +/* > + * Tegra2 FIFO design is ... interesting. For example, you want to Tx 2 bytes: > + * > + * +---+---+ > + * writearr[]: | 0 | 1 | > + * +---+---+ > + * \ \ > + * \ \ > + * \ \ > + * \ \ > + * \ \ > + * 31 +---+---+---+---+ 0 > + * tmp(32-bits): | X | X | 0 | 1 | > + * +---+---+---+---+ LSB > + * > + * It is neither little or big endian. The first bit for SPI controller to > + * transfer is the bit 15 in FIFO, neither bit 31 or bit 0, because the transfer > + * length is 16 bits (2 bytes). > + * > + * Rx follows the similar rule. First bit comes at bit 0, and the whole FIFO > + * left-shifts 1 bit for every bit comes in. Hence, after reading 3 bytes, > + * the first coming bit will reside in bit 23. > + */ > +int tegra2_spi_send_command(unsigned int writecnt, > + unsigned int readcnt, > + const unsigned char *writearr, > + unsigned char *readarr) > +{ > + int retval = 0; > + uint8_t *delayed_msg = NULL; /* for UART is disabled. */ > + uint32_t *spi_cmd = (uint32_t *)spi_base; > + uint32_t *spi_sts = (uint32_t *)(spi_base + 0x04); > + uint32_t *tx_fifo = (uint32_t *)(spi_base + 0x10); > + uint32_t *rx_fifo = (uint32_t *)(spi_base + 0x20); > + uint32_t status; > + uint32_t to_write, to_read; /* byte counts to fill FIFO. */ > + uint32_t bits; /* bit count to tell SPI controller. */ > + > + mmio_writel(mmio_readl(spi_sts), spi_sts); > + mmio_writel(mmio_readl(spi_cmd) | SPI_CMD_TXEN | SPI_CMD_RXEN, spi_cmd); > + spi_cs_activate(); > + > + while (next4Bytes(&writecnt, &readcnt, &bits, &to_write, &to_read)) { > + int i; > + uint32_t tmp; > + uint32_t tm; /* timeout counter */ > + > + /* prepare Tx FIFO */ > + for (tmp = 0, i = 0; i < to_write; ++i) { > + tmp |= (*writearr++) << ((bits / 8 - 1 - i) * 8); > + } > + mmio_writel(tmp, tx_fifo); > + > + /* Kick the SCLK running: Shift out TX FIFO, and receive RX. */ > + mmio_writel(mmio_readl(spi_cmd) & ~SPI_CMD_BIT_LENGTH_MASK, > + spi_cmd); > + mmio_writel(mmio_readl(spi_cmd) | (bits - 1), > + spi_cmd); > + mmio_writel(mmio_readl(spi_cmd) | SPI_CMD_GO, spi_cmd); > + > + /* Wait for controller completes the task. */ > + for (tm = 0; tm < SPI_TIMEOUT; ++tm) { > + if (((status = mmio_readl(spi_sts)) & > + (SPI_STAT_BSY | SPI_STAT_RDY)) == SPI_STAT_RDY) > + break; > + /* We setup clock to 6MHz, so that we shall come back > + * after: 1 / (6MHz) * 8 bits = 1.333us > + */ > + programmer_delay(2); > + } > + mmio_writel(mmio_readl(spi_sts) | SPI_STAT_RDY, spi_sts); > + > + /* Since the UART is disabled here, we delay printing the > + * message until spi_cs_deactivate() is called. > + */ > + if (tm >= SPI_TIMEOUT) { > + static uint8_t err[256]; > + retval = -1; > + snprintf(err, sizeof(err), > + "%s():%d BSY&RDY timeout, status = 0x%08x\n", > + __func__, __LINE__, status); > + delayed_msg = err; > + break; > + } > + > + /* read RX FIFO */ > + tmp = mmio_readl(rx_fifo); > + for (i = 0; i < to_read; ++i) { > + *readarr++ = (tmp >> ((to_read - 1 - i) * 8)) & 0xFF; > + } > + } > + > + mmio_writel(status = mmio_readl(spi_sts), spi_sts); > + > + spi_cs_deactivate(); > + if (delayed_msg) { > + msg_perr("%s\n", delayed_msg); > + } > + > + return retval; > +} > + > +int tegra2_spi_read(struct flashchip *flash, uint8_t *buf, int start, int len) > +{ > + return spi_read_chunked(flash, buf, start, len, > + spi_programmer->max_data_read); > +} > The function above is not needed anymore. > + > +int tegra2_spi_write(struct flashchip *flash, uint8_t *buf, int start, int len) > +{ > + return spi_write_chunked(flash, buf, start, len, > + spi_programmer->max_data_write); > +} > The function above is not needed anymore. > + > +/* Unmap register spaces */ > +int tegra2_spi_shutdown(void *data) > +{ > + physunmap(gpio_base, 4096); > + physunmap(clkrst_base, 4096); > + physunmap(apbmisc_base, 4096); > + physunmap(spi_base - 0x380, 4096); > + return 0; > +} > + > +static const struct spi_programmer spi_programmer_tegra2 = { > + .type = SPI_CONTROLLER_TEGRA2, > + /* FIFO depth is 32, packets can be up to 32-bits in length. */ > + .max_data_read = 128, > + .max_data_write = 128, > + .command = tegra2_spi_send_command, > + .multicommand = default_spi_send_multicommand, > + .read = tegra2_spi_read, > + .write_256 = tegra2_spi_write, > Please change .read and .write_256 to use the generic functions if possible. > +}; > + > +/* Map register spaces */ > +int tegra2_spi_init(void) > +{ > + u32 val; > + uint32_t *spi_cmd; > + uint32_t *spi_sts; > + > + buses_supported = CHIP_BUSTYPE_SPI; > AFAICS setting buses_supported is handled by register_spi_programmer. That said, you may want to set it to CHIP_BUSTYPE_NONE here (the handling of buses_supported for internal is a bit weird for historic reasons). > + register_spi_programmer(&spi_programmer_tegra2); > Way too early, needs to be at the end of the init function. > + > + gpio_base = physmap("GPIO", TEGRA2_GPIO_BASE, 4096); > + clkrst_base = physmap("CLK/RST", NV_ADDRESS_MAP_PPSB_CLK_RST_BASE, > + 4096); > + apbmisc_base = physmap("APB MISC", NV_ADDRESS_MAP_APB_MISC_BASE, 4096); > + /* non-page offset */ > + spi_base = physmap("SPI", TEGRA2_SPI_BASE - 0x380, 4096) + 0x380; > + > + register_shutdown(tegra2_spi_shutdown, NULL); > + > + flashbase = 0; /* FIXME: to make sanity check happy. */ > + > + /* Init variables */ > + spi_cmd = (uint32_t *)spi_base; > + spi_sts = (uint32_t *)(spi_base + 0x04); > + > + /* > + * SPI reset/clocks init - reset SPI, set clocks, release from reset > + */ > + > + /* SWE_SPI1_RST: Hold SPI controller 1 in reset */ > + val = mmio_readl(clkrst_base + 0x08) | 0x800; > + rmmio_writel(val, (clkrst_base + 0x08)); > + msg_pdbg("%s: ClkRst = %08x\n", __func__, val); > + > + /* CLK_ENB_SPI1: Enable clock to SPI 1 Controller */ > + val = mmio_readl(clkrst_base + 0x14) | 0x800; > + rmmio_writel(val, (clkrst_base + 0x14)); > + msg_pdbg("%s: ClkEnable = %08x\n", __func__, val); > + > + /* Change default SPI clock from 12MHz to 6MHz, same as BootROM */ > + val = mmio_readl(clkrst_base + 0x114) | 0x2; > + rmmio_writel(val, (clkrst_base + 0x114)); > + msg_pdbg("%s: ClkSrc = %08x\n", __func__, val); > + > + /* SWE_SPI1_RST: Clear SPI1 reset bit (take it out of reset), > + * use regular mmio_writel (restore callback already registered) */ > + val = mmio_readl(clkrst_base + 0x08) & 0xFFFFF7FF; > + mmio_writel(val, (clkrst_base + 0x08)); > + msg_pdbg("%s: ClkRst final = %08x\n", __func__, val); > + > + /* Clear stale status here */ > + mmio_writel(SPI_STAT_RDY | SPI_STAT_RXF_FLUSH | SPI_STAT_TXF_FLUSH | > + SPI_STAT_RXF_UNR | SPI_STAT_TXF_OVF, spi_sts); > + msg_pdbg("%s: STATUS = %08x\n", __func__, mmio_readl(spi_sts)); > + > + /* > + * Use sw-controlled CS, so we can clock in data after ReadID, etc. > + */ > + mmio_writel(mmio_readl(spi_cmd) | SPI_CMD_CS_SOFT, spi_cmd); > + msg_pdbg("%s: COMMAND = %08x\n", __func__, mmio_readl(spi_cmd)); > + > + /* > + * SPI pins on Tegra2 are muxed - change pinmux last due to UART issue > + */ > + val = mmio_readl(apbmisc_base + 0x88) | 0xC0000000; > + rmmio_writel(val, (apbmisc_base + 0x88)); > + msg_pdbg("%s: PinMuxRegC = %08x\n", __func__, val); > + > + /* Set Z_LSPI to non-tristate mode */ > + val = mmio_readl(apbmisc_base + 0x20) & 0xFFFFFFFE; > + rmmio_writel(val, (apbmisc_base + 0x20)); > + msg_pdbg("%s: TriStateReg = %08x\n", __func__, val); > + > + /* delay 100ms so that all chars in buffer (1KB) can be flushed. */ > + programmer_delay(100000); > + > + /* > + * We need to dynamically change the pinmux, shared w/UART RXD/CTS! > + */ > + val = mmio_readl(apbmisc_base + 0x84) | 0x0000000C; /* 3 = SFLASH */ > + rmmio_writel(val, (apbmisc_base + 0x84)); > + msg_pdbg("%s: PinMuxRegB = %08x\n", __func__, val); > + > + /* On Seaboard, MOSI/MISO are shared w/UART. > + * Use GPIO I3 (UART_DISABLE) to tristate UART during SPI activity. > + * Enable UART later (cs_deactivate) so we can use it for U-Boot comms. > + */ > + msg_pdbg("%s: DISABLING UART!\n", __func__); > + tg2_gpio_direction_output(UART_DISABLE_PORT, UART_DISABLE_BIT, 1); > Here is the right place for register_spi_programmer. > + > + return 0; > +} > +#endif > Index: flashrom-tegra2/tegra2_spi.h > =================================================================== > --- /dev/null > +++ flashrom-tegra2/tegra2_spi.h > @@ -0,0 +1,81 @@ > +/* > + * This file is part of the flashrom project. > + * > + * Copyright (C) 2010 NVIDIA Corporation > + * Copyright (C) 2011 Google Inc > + * > + * 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 > + */ > + > +#ifndef __TEGRA2_SPI_H__ > +#define __TEGRA2_SPI_H__ > + > +// *************************************************************************** > +// Hardware BARs > + > +#define TEGRA2_GPIO_BASE 0x6000D000 > +#define TEGRA2_SPI_BASE 0x7000C380 > +#define NV_ADDRESS_MAP_PPSB_CLK_RST_BASE 0x60006000 > +#define NV_ADDRESS_MAP_APB_MISC_BASE 0x70000000 > + > +// *************************************************************************** > +// Clock/reset controller > +#define CLK_RST_ENB_H_0_OFFSET 0x14 > +#define CLK_RST_ENB_H_0_SPI1 (1 << 11) > + > +// *************************************************************************** > +// GPIO controller > + > +#define GPIO_OFF(port) (((port / 4) * 128) + ((port % 4) * 4)) > +#define GPIO_CNF(port) (gpio_base + GPIO_OFF(port) + 0x00) > +#define GPIO_OE(port) (gpio_base + GPIO_OFF(port) + 0x10) > +#define GPIO_OUT(port) (gpio_base + GPIO_OFF(port) + 0x20) > +#define GPIO_IN(port) (gpio_base + GPIO_OFF(port) + 0x30) > +#define GPIO_INT_STA(port) (gpio_base + GPIO_OFF(port) + 0x40) > +#define GPIO_INT_ENB(port) (gpio_base + GPIO_OFF(port) + 0x50) > +#define GPIO_INT_LVL(port) (gpio_base + GPIO_OFF(port) + 0x60) > +#define GPIO_INT_CLR(port) (gpio_base + GPIO_OFF(port) + 0x70) > + > +#define SPI_CMD_GO (1 << 30) > +#define SPI_CMD_ACTIVE_SCLK (1 << 26) > +#define SPI_CMD_CK_SDA (1 << 21) > +#define SPI_CMD_ACTIVE_SDA (1 << 18) > +#define SPI_CMD_CS_POL (1 << 16) > +#define SPI_CMD_TXEN (1 << 15) > +#define SPI_CMD_RXEN (1 << 14) > +#define SPI_CMD_CS_VAL (1 << 13) > +#define SPI_CMD_CS_SOFT (1 << 12) > +#define SPI_CMD_CS_DELAY (1 << 9) > +#define SPI_CMD_CS3_EN (1 << 8) > +#define SPI_CMD_CS2_EN (1 << 7) > +#define SPI_CMD_CS1_EN (1 << 6) > +#define SPI_CMD_CS0_EN (1 << 5) > +#define SPI_CMD_BIT_LENGTH (1 << 4) > +#define SPI_CMD_BIT_LENGTH_MASK 0x0000001F > + > +#define SPI_STAT_BSY (1 << 31) > +#define SPI_STAT_RDY (1 << 30) > +#define SPI_STAT_RXF_FLUSH (1 << 29) > +#define SPI_STAT_TXF_FLUSH (1 << 28) > +#define SPI_STAT_RXF_UNR (1 << 27) > +#define SPI_STAT_TXF_OVF (1 << 26) > +#define SPI_STAT_RXF_EMPTY (1 << 25) > +#define SPI_STAT_RXF_FULL (1 << 24) > +#define SPI_STAT_TXF_EMPTY (1 << 23) > +#define SPI_STAT_TXF_FULL (1 << 22) > +#define SPI_STAT_SEL_TXRX_N (1 << 16) > +#define SPI_STAT_CUR_BLKCNT (1 << 15) > + > +#endif /* __TEGRA2_SPI_H__ */ > Index: flashrom-tegra2/Makefile > =================================================================== > --- flashrom-tegra2.orig/Makefile > +++ flashrom-tegra2/Makefile > @@ -310,6 +310,10 @@ PROGRAMMER_OBJS += processor_enable.o ch > ifeq ($(ARCH),"x86") > PROGRAMMER_OBJS += it87spi.o it85spi.o ichspi.o sb600spi.o wbsio_spi.o mcp6x_spi.o > else > +ifeq ($(ARCH),"arm") > +PROGRAMMER_OBJS += tegra2_spi.o > +else > +endif > endif > NEED_PCI := yes > endif > My overall impression of the Tegra2 SPI driver is that the hardware is some weird bitbanging/bytewise combination and nobody is sure how to deal with it. Please note that this is still not a complete review, but it will allow to get the patch closer to a mergeable state. Regards, Carl-Daniel
Patch
Index: flashrom-tegra2/processor_enable.c =================================================================== --- flashrom-tegra2.orig/processor_enable.c +++ flashrom-tegra2/processor_enable.c @@ -81,6 +81,48 @@ static int is_loongson(void) } #endif +#if defined(__arm__) +#include <stdio.h> +#include <string.h> +#include <ctype.h> + +/* Returns true if the /proc/cpuinfo contains a line: "CPU part *: *0xc09". + * TODO: need to extend in future for same SPI controller in chip family. + */ +static int is_tegra2(void) +{ + FILE *cpuinfo; + uint32_t impl = 0, architecture = 0, variant = 0, part = 0; + const char *name = "CPU part"; + const char *value = "0xc09"; + int ret = 0; + + cpuinfo = fopen("/proc/cpuinfo", "rb"); + if (!cpuinfo) + return 0; + while (!feof(cpuinfo)) { + char line[512], *ptr; + if (fgets(line, sizeof(line), cpuinfo) == NULL) + break; + ptr = line; + while (*ptr && isspace((unsigned char)*ptr)) + ptr++; + if (strncmp(ptr, name, strlen(name)) == 0) + ptr += strlen(name); + while (*ptr && isspace((unsigned char)*ptr)) + ptr++; + if (*ptr != ':') + continue; + ptr++; + while (*ptr && isspace((unsigned char)*ptr)) + ptr++; + ret = (strncmp(ptr, value, strlen(value)) == 0); + } + fclose(cpuinfo); + return ret; +} +#endif + int processor_flash_enable(void) { /* FIXME: detect loongson on FreeBSD and OpenBSD as well. */ @@ -90,6 +132,13 @@ int processor_flash_enable(void) return 0; } #endif + +#if defined (__arm__) + if (is_tegra2()) { + msg_pdbg("Detected NVIDIA Tegra 2.\n"); + return tegra2_spi_init(); + } +#endif /* Not implemented yet. Oh well. */ return 1; } Index: flashrom-tegra2/programmer.h =================================================================== --- flashrom-tegra2.orig/programmer.h +++ flashrom-tegra2/programmer.h @@ -524,6 +524,9 @@ enum spi_controller { SPI_CONTROLLER_VIA, SPI_CONTROLLER_WBSIO, #endif +#if defined(__arm__) + SPI_CONTROLLER_TEGRA2, +#endif #endif #if CONFIG_FT2232_SPI == 1 SPI_CONTROLLER_FT2232, @@ -589,6 +592,13 @@ int mcp6x_spi_init(int want_spi); /* sb600spi.c */ int sb600_probe_spi(struct pci_dev *dev); +/* tegra2_spi.c */ +int tegra2_spi_init(void); +int tegra2_spi_send_command(unsigned int writecnt, unsigned int readcnt, + const unsigned char *writearr, unsigned char *readarr); +int tegra2_spi_read(struct flashchip *flash, uint8_t *buf, int start, int len); +int tegra2_spi_write(struct flashchip *flash, uint8_t *buf, int start, int len); + /* wbsio_spi.c */ int wbsio_check_for_spi(void); #endif Index: flashrom-tegra2/tegra2_spi.c =================================================================== --- /dev/null +++ flashrom-tegra2/tegra2_spi.c @@ -0,0 +1,405 @@ +/* + * This file is part of the flashrom project. + * + * Copyright (C) 2010 NVIDIA Corporation + * Copyright (C) 2011 Google Inc + * + * 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 + */ + +#if defined(__arm__) +#include <assert.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#include "flash.h" +#include "programmer.h" +#include "tegra2_spi.h" + +static void *gpio_base, *clkrst_base, *apbmisc_base, *spi_base; + +#define SPI_TIMEOUT 50000 /* 100ms = 50000 * 2us */ + +/* On Seaboard: GPIO_PI3 = Port I = 8, bit = 3 */ +#define UART_DISABLE_PORT 8 +#define UART_DISABLE_BIT 3 + +/* Config port:bit as GPIO, not SFPIO (default) */ +static void __set_config(unsigned port, unsigned bit, int type) +{ + u32 u; + + msg_pdbg("%s: port = %d, bit = %d, %s\n", __func__, + port, bit, type ? "GPIO" : "SFPIO"); + + u = mmio_readl(GPIO_CNF(port)); + if (type) /* GPIO */ + u |= 1 << bit; + else + u &= ~(1 << bit); + rmmio_writel(u, GPIO_CNF(port)); +} + +/* Config GPIO port:bit as input or output (OE) */ +static void __set_direction(unsigned port, unsigned bit, int output) +{ + u32 u; + + msg_pdbg("%s: port = %d, bit = %d, %s\n", __func__, + port, bit, output ? "OUT" : "IN"); + + u = mmio_readl(GPIO_OE(port)); + if (output) + u |= 1 << bit; + else + u &= ~(1 << bit); + rmmio_writel(u, GPIO_OE(port)); +} + +/* set GPIO OUT port:bit as 0 or 1 */ +static void __set_level(unsigned port, unsigned bit, int high) +{ + u32 u; + + msg_pdbg("%s: port = %d, bit %d == %d\n", __func__, + port, bit, high); + + u = mmio_readl(GPIO_OUT(port)); + if (high) + u |= 1 << bit; + else + u &= ~(1 << bit); + rmmio_writel(u, GPIO_OUT(port)); +} + +/* set GPIO port:bit as an output, with polarity 'value' */ +static int tg2_gpio_direction_output(unsigned port, unsigned bit, int value) +{ + msg_pdbg("%s: port = %d, bit = %d, value = %d\n", + __func__, port, bit, value); + + /* Configure as a GPIO */ + __set_config(port, bit, 1); + + /* Configure GPIO output value. */ + __set_level(port, bit, value); + + /* Configure GPIO direction as output. */ + __set_direction(port, bit, 1); + + return 0; +} + +static void spi_cs_activate(void) +{ + uint32_t *spi_cmd = (uint32_t *)spi_base; + + /* + * CS is negated on Tegra, so drive a 1 to get a 0 + */ + mmio_writel(mmio_readl(spi_cmd) | SPI_CMD_CS_VAL, spi_cmd); + msg_pspew("%s: CS driven %s\n", __func__, + (mmio_readl(spi_cmd) & SPI_CMD_CS_VAL) ? "LOW" : "HIGH"); +} + +static void spi_cs_deactivate(void) +{ + uint32_t *spi_cmd = (uint32_t *)spi_base; + + + /* + * CS is negated on Tegra, so drive a 0 to get a 1 + */ + mmio_writel(mmio_readl(spi_cmd) & ~SPI_CMD_CS_VAL, spi_cmd); + msg_pspew("%s: CS driven %s\n", __func__, + (mmio_readl(spi_cmd) & SPI_CMD_CS_VAL) ? "LOW" : "HIGH"); +} + +/* Helper function to calculate the clock cycle in this round. + * Also updates the byte count remaining to be used this round. + * + * For example, we want to write 6 bytes to SPI and then read 5 bytes back. + * + * +---+---+---+---+---+---+ + * | W | W | W | W | W | W | + * +---+---+---+---+---+---+---+---+---+---+---+ + * | R | R | R | R | R | + * +---+---+---+---+---+ + * |<-- round 0 -->| + * |<-- round 1 -->| + * |<-- round 2 -->| + * + * So that the continuous calling this function would get: + * + * round| RET| writecnt readcnt bits to_write to_read + * -----+----+--------------------------------------------- + * INIT | | 6 5 + * 0 | 1 | 2 5 32 4 0 + * 1 | 1 | 0 3 32 2 2 + * 2 | 1 | 0 0 24 0 3 + * 3 | 0 | - - - - - + * + */ +int next4Bytes(uint32_t *writecnt, uint32_t *readcnt, int *num_bits, + uint32_t *to_write, uint32_t *to_read) { + assert(writecnt); + assert(readcnt); + assert(num_bits); + assert(to_write); + assert(to_read); + + *to_write = min(*writecnt, 4); + *to_read = min(*readcnt, 4 - *to_write); + + *writecnt -= *to_write; + *readcnt -= *to_read; + + *num_bits = (*to_write + *to_read) * 8; + + if (*num_bits) + return 1; /* need to be called again. */ + else + return 0; /* handled write and read requests. */ +} + +/* + * Tegra2 FIFO design is ... interesting. For example, you want to Tx 2 bytes: + * + * +---+---+ + * writearr[]: | 0 | 1 | + * +---+---+ + * \ \ + * \ \ + * \ \ + * \ \ + * \ \ + * 31 +---+---+---+---+ 0 + * tmp(32-bits): | X | X | 0 | 1 | + * +---+---+---+---+ LSB + * + * It is neither little or big endian. The first bit for SPI controller to + * transfer is the bit 15 in FIFO, neither bit 31 or bit 0, because the transfer + * length is 16 bits (2 bytes). + * + * Rx follows the similar rule. First bit comes at bit 0, and the whole FIFO + * left-shifts 1 bit for every bit comes in. Hence, after reading 3 bytes, + * the first coming bit will reside in bit 23. + */ +int tegra2_spi_send_command(unsigned int writecnt, + unsigned int readcnt, + const unsigned char *writearr, + unsigned char *readarr) +{ + int retval = 0; + uint8_t *delayed_msg = NULL; /* for UART is disabled. */ + uint32_t *spi_cmd = (uint32_t *)spi_base; + uint32_t *spi_sts = (uint32_t *)(spi_base + 0x04); + uint32_t *tx_fifo = (uint32_t *)(spi_base + 0x10); + uint32_t *rx_fifo = (uint32_t *)(spi_base + 0x20); + uint32_t status; + uint32_t to_write, to_read; /* byte counts to fill FIFO. */ + uint32_t bits; /* bit count to tell SPI controller. */ + + mmio_writel(mmio_readl(spi_sts), spi_sts); + mmio_writel(mmio_readl(spi_cmd) | SPI_CMD_TXEN | SPI_CMD_RXEN, spi_cmd); + spi_cs_activate(); + + while (next4Bytes(&writecnt, &readcnt, &bits, &to_write, &to_read)) { + int i; + uint32_t tmp; + uint32_t tm; /* timeout counter */ + + /* prepare Tx FIFO */ + for (tmp = 0, i = 0; i < to_write; ++i) { + tmp |= (*writearr++) << ((bits / 8 - 1 - i) * 8); + } + mmio_writel(tmp, tx_fifo); + + /* Kick the SCLK running: Shift out TX FIFO, and receive RX. */ + mmio_writel(mmio_readl(spi_cmd) & ~SPI_CMD_BIT_LENGTH_MASK, + spi_cmd); + mmio_writel(mmio_readl(spi_cmd) | (bits - 1), + spi_cmd); + mmio_writel(mmio_readl(spi_cmd) | SPI_CMD_GO, spi_cmd); + + /* Wait for controller completes the task. */ + for (tm = 0; tm < SPI_TIMEOUT; ++tm) { + if (((status = mmio_readl(spi_sts)) & + (SPI_STAT_BSY | SPI_STAT_RDY)) == SPI_STAT_RDY) + break; + /* We setup clock to 6MHz, so that we shall come back + * after: 1 / (6MHz) * 8 bits = 1.333us + */ + programmer_delay(2); + } + mmio_writel(mmio_readl(spi_sts) | SPI_STAT_RDY, spi_sts); + + /* Since the UART is disabled here, we delay printing the + * message until spi_cs_deactivate() is called. + */ + if (tm >= SPI_TIMEOUT) { + static uint8_t err[256]; + retval = -1; + snprintf(err, sizeof(err), + "%s():%d BSY&RDY timeout, status = 0x%08x\n", + __func__, __LINE__, status); + delayed_msg = err; + break; + } + + /* read RX FIFO */ + tmp = mmio_readl(rx_fifo); + for (i = 0; i < to_read; ++i) { + *readarr++ = (tmp >> ((to_read - 1 - i) * 8)) & 0xFF; + } + } + + mmio_writel(status = mmio_readl(spi_sts), spi_sts); + + spi_cs_deactivate(); + if (delayed_msg) { + msg_perr("%s\n", delayed_msg); + } + + return retval; +} + +int tegra2_spi_read(struct flashchip *flash, uint8_t *buf, int start, int len) +{ + return spi_read_chunked(flash, buf, start, len, + spi_programmer->max_data_read); +} + +int tegra2_spi_write(struct flashchip *flash, uint8_t *buf, int start, int len) +{ + return spi_write_chunked(flash, buf, start, len, + spi_programmer->max_data_write); +} + +/* Unmap register spaces */ +int tegra2_spi_shutdown(void *data) +{ + physunmap(gpio_base, 4096); + physunmap(clkrst_base, 4096); + physunmap(apbmisc_base, 4096); + physunmap(spi_base - 0x380, 4096); + return 0; +} + +static const struct spi_programmer spi_programmer_tegra2 = { + .type = SPI_CONTROLLER_TEGRA2, + /* FIFO depth is 32, packets can be up to 32-bits in length. */ + .max_data_read = 128, + .max_data_write = 128, + .command = tegra2_spi_send_command, + .multicommand = default_spi_send_multicommand, + .read = tegra2_spi_read, + .write_256 = tegra2_spi_write, +}; + +/* Map register spaces */ +int tegra2_spi_init(void) +{ + u32 val; + uint32_t *spi_cmd; + uint32_t *spi_sts; + + buses_supported = CHIP_BUSTYPE_SPI; + register_spi_programmer(&spi_programmer_tegra2); + + gpio_base = physmap("GPIO", TEGRA2_GPIO_BASE, 4096); + clkrst_base = physmap("CLK/RST", NV_ADDRESS_MAP_PPSB_CLK_RST_BASE, + 4096); + apbmisc_base = physmap("APB MISC", NV_ADDRESS_MAP_APB_MISC_BASE, 4096); + /* non-page offset */ + spi_base = physmap("SPI", TEGRA2_SPI_BASE - 0x380, 4096) + 0x380; + + register_shutdown(tegra2_spi_shutdown, NULL); + + flashbase = 0; /* FIXME: to make sanity check happy. */ + + /* Init variables */ + spi_cmd = (uint32_t *)spi_base; + spi_sts = (uint32_t *)(spi_base + 0x04); + + /* + * SPI reset/clocks init - reset SPI, set clocks, release from reset + */ + + /* SWE_SPI1_RST: Hold SPI controller 1 in reset */ + val = mmio_readl(clkrst_base + 0x08) | 0x800; + rmmio_writel(val, (clkrst_base + 0x08)); + msg_pdbg("%s: ClkRst = %08x\n", __func__, val); + + /* CLK_ENB_SPI1: Enable clock to SPI 1 Controller */ + val = mmio_readl(clkrst_base + 0x14) | 0x800; + rmmio_writel(val, (clkrst_base + 0x14)); + msg_pdbg("%s: ClkEnable = %08x\n", __func__, val); + + /* Change default SPI clock from 12MHz to 6MHz, same as BootROM */ + val = mmio_readl(clkrst_base + 0x114) | 0x2; + rmmio_writel(val, (clkrst_base + 0x114)); + msg_pdbg("%s: ClkSrc = %08x\n", __func__, val); + + /* SWE_SPI1_RST: Clear SPI1 reset bit (take it out of reset), + * use regular mmio_writel (restore callback already registered) */ + val = mmio_readl(clkrst_base + 0x08) & 0xFFFFF7FF; + mmio_writel(val, (clkrst_base + 0x08)); + msg_pdbg("%s: ClkRst final = %08x\n", __func__, val); + + /* Clear stale status here */ + mmio_writel(SPI_STAT_RDY | SPI_STAT_RXF_FLUSH | SPI_STAT_TXF_FLUSH | + SPI_STAT_RXF_UNR | SPI_STAT_TXF_OVF, spi_sts); + msg_pdbg("%s: STATUS = %08x\n", __func__, mmio_readl(spi_sts)); + + /* + * Use sw-controlled CS, so we can clock in data after ReadID, etc. + */ + mmio_writel(mmio_readl(spi_cmd) | SPI_CMD_CS_SOFT, spi_cmd); + msg_pdbg("%s: COMMAND = %08x\n", __func__, mmio_readl(spi_cmd)); + + /* + * SPI pins on Tegra2 are muxed - change pinmux last due to UART issue + */ + val = mmio_readl(apbmisc_base + 0x88) | 0xC0000000; + rmmio_writel(val, (apbmisc_base + 0x88)); + msg_pdbg("%s: PinMuxRegC = %08x\n", __func__, val); + + /* Set Z_LSPI to non-tristate mode */ + val = mmio_readl(apbmisc_base + 0x20) & 0xFFFFFFFE; + rmmio_writel(val, (apbmisc_base + 0x20)); + msg_pdbg("%s: TriStateReg = %08x\n", __func__, val); + + /* delay 100ms so that all chars in buffer (1KB) can be flushed. */ + programmer_delay(100000); + + /* + * We need to dynamically change the pinmux, shared w/UART RXD/CTS! + */ + val = mmio_readl(apbmisc_base + 0x84) | 0x0000000C; /* 3 = SFLASH */ + rmmio_writel(val, (apbmisc_base + 0x84)); + msg_pdbg("%s: PinMuxRegB = %08x\n", __func__, val); + + /* On Seaboard, MOSI/MISO are shared w/UART. + * Use GPIO I3 (UART_DISABLE) to tristate UART during SPI activity. + * Enable UART later (cs_deactivate) so we can use it for U-Boot comms. + */ + msg_pdbg("%s: DISABLING UART!\n", __func__); + tg2_gpio_direction_output(UART_DISABLE_PORT, UART_DISABLE_BIT, 1); + + return 0; +} +#endif Index: flashrom-tegra2/tegra2_spi.h =================================================================== --- /dev/null +++ flashrom-tegra2/tegra2_spi.h @@ -0,0 +1,81 @@ +/* + * This file is part of the flashrom project. + * + * Copyright (C) 2010 NVIDIA Corporation + * Copyright (C) 2011 Google Inc + * + * 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 + */ + +#ifndef __TEGRA2_SPI_H__ +#define __TEGRA2_SPI_H__ + +// *************************************************************************** +// Hardware BARs + +#define TEGRA2_GPIO_BASE 0x6000D000 +#define TEGRA2_SPI_BASE 0x7000C380 +#define NV_ADDRESS_MAP_PPSB_CLK_RST_BASE 0x60006000 +#define NV_ADDRESS_MAP_APB_MISC_BASE 0x70000000 + +// *************************************************************************** +// Clock/reset controller +#define CLK_RST_ENB_H_0_OFFSET 0x14 +#define CLK_RST_ENB_H_0_SPI1 (1 << 11) + +// *************************************************************************** +// GPIO controller + +#define GPIO_OFF(port) (((port / 4) * 128) + ((port % 4) * 4)) +#define GPIO_CNF(port) (gpio_base + GPIO_OFF(port) + 0x00) +#define GPIO_OE(port) (gpio_base + GPIO_OFF(port) + 0x10) +#define GPIO_OUT(port) (gpio_base + GPIO_OFF(port) + 0x20) +#define GPIO_IN(port) (gpio_base + GPIO_OFF(port) + 0x30) +#define GPIO_INT_STA(port) (gpio_base + GPIO_OFF(port) + 0x40) +#define GPIO_INT_ENB(port) (gpio_base + GPIO_OFF(port) + 0x50) +#define GPIO_INT_LVL(port) (gpio_base + GPIO_OFF(port) + 0x60) +#define GPIO_INT_CLR(port) (gpio_base + GPIO_OFF(port) + 0x70) + +#define SPI_CMD_GO (1 << 30) +#define SPI_CMD_ACTIVE_SCLK (1 << 26) +#define SPI_CMD_CK_SDA (1 << 21) +#define SPI_CMD_ACTIVE_SDA (1 << 18) +#define SPI_CMD_CS_POL (1 << 16) +#define SPI_CMD_TXEN (1 << 15) +#define SPI_CMD_RXEN (1 << 14) +#define SPI_CMD_CS_VAL (1 << 13) +#define SPI_CMD_CS_SOFT (1 << 12) +#define SPI_CMD_CS_DELAY (1 << 9) +#define SPI_CMD_CS3_EN (1 << 8) +#define SPI_CMD_CS2_EN (1 << 7) +#define SPI_CMD_CS1_EN (1 << 6) +#define SPI_CMD_CS0_EN (1 << 5) +#define SPI_CMD_BIT_LENGTH (1 << 4) +#define SPI_CMD_BIT_LENGTH_MASK 0x0000001F + +#define SPI_STAT_BSY (1 << 31) +#define SPI_STAT_RDY (1 << 30) +#define SPI_STAT_RXF_FLUSH (1 << 29) +#define SPI_STAT_TXF_FLUSH (1 << 28) +#define SPI_STAT_RXF_UNR (1 << 27) +#define SPI_STAT_TXF_OVF (1 << 26) +#define SPI_STAT_RXF_EMPTY (1 << 25) +#define SPI_STAT_RXF_FULL (1 << 24) +#define SPI_STAT_TXF_EMPTY (1 << 23) +#define SPI_STAT_TXF_FULL (1 << 22) +#define SPI_STAT_SEL_TXRX_N (1 << 16) +#define SPI_STAT_CUR_BLKCNT (1 << 15) + +#endif /* __TEGRA2_SPI_H__ */ Index: flashrom-tegra2/Makefile =================================================================== --- flashrom-tegra2.orig/Makefile +++ flashrom-tegra2/Makefile @@ -310,6 +310,10 @@ PROGRAMMER_OBJS += processor_enable.o ch ifeq ($(ARCH),"x86") PROGRAMMER_OBJS += it87spi.o it85spi.o ichspi.o sb600spi.o wbsio_spi.o mcp6x_spi.o else +ifeq ($(ARCH),"arm") +PROGRAMMER_OBJS += tegra2_spi.o +else +endif endif NEED_PCI := yes endif