[U-Boot] [PATCH v3 0/8] tegra2: Implement SPI flash and saved environment

Enable SPI flash on the Tegra2 Seaboard along with a saved 8KB environment. This involves a few pieces:
- Tegra2 SPI driver - Seaboard config changes - a SPI / UART switch to handle switching between SPI and console UART
This series applies cleanly on top of Stephen Warren's latest patch series
http://patchwork.ozlabs.org/project/uboot/list/?submitter=Stephen+Warren&...
(the first patch of which is already applied)
and also requires Stephen's GPIO fix.
http://patchwork.ozlabs.org/patch/118184/
Still to come are the NS16550 driver changes to cope with the pins disappearing from under it. This patch series has been NAKed, the XON/XOFF series won't really help and it isn't clear exactly what to do here. I will return to this another day.
http://patchwork.ozlabs.org/patch/120013/
This patch series has been tested on Seaboard and other Tegra2 hardware.
Changes in v2: - Update to support SPI mode and frequency - Tidy driver according to comments, and tidy a little more - Add CONFIG_SPI_FLASH_SIZE to define flash size - Add config for SPI environment mode/speed - Fix up GPIO selection to use gpio_request() first - Rename uart-spi-fix to uart-spi-switch - Rename uart/spi_enable() to pinmux_select_uart/spi() - Add pinmux logic to SPI driver
Changes in v3: - Remove the replaced bus/cs check function - Change (bitlen & 7) to (bitlen % 8) - Remove unneeded #includes and header file guard
Simon Glass (7): tegra2: Tidy UART selection tegra2: Add UARTB support tegra2: config: Enable SPI flash on Seaboard tegra2: Enable SPI environment on Seaboard tegra2: Implement SPI / UART GPIO switch tegra2: spi: Support SPI / UART switch tegra2: Plumb in SPI/UART switch code
Tom Warren (1): tegra2: spi: Add SPI driver for Tegra2 SOC
arch/arm/include/asm/arch-tegra2/tegra2.h | 1 + arch/arm/include/asm/arch-tegra2/tegra2_spi.h | 76 ++++++ arch/arm/include/asm/arch-tegra2/uart-spi-switch.h | 46 ++++ board/nvidia/common/Makefile | 47 ++++ board/nvidia/common/board.c | 79 ++++-- board/nvidia/common/board.h | 1 + board/nvidia/common/uart-spi-switch.c | 138 ++++++++++ board/nvidia/seaboard/Makefile | 1 - board/nvidia/seaboard/seaboard.c | 3 + drivers/spi/Makefile | 1 + drivers/spi/tegra2_spi.c | 274 ++++++++++++++++++++ include/configs/harmony.h | 3 + include/configs/seaboard.h | 20 ++ include/configs/tegra2-common.h | 3 +- 14 files changed, 667 insertions(+), 26 deletions(-) create mode 100644 arch/arm/include/asm/arch-tegra2/tegra2_spi.h create mode 100644 arch/arm/include/asm/arch-tegra2/uart-spi-switch.h create mode 100644 board/nvidia/common/Makefile create mode 100644 board/nvidia/common/uart-spi-switch.c create mode 100644 drivers/spi/tegra2_spi.c

UART selection is done with a lot of #ifdefs. This cleans things up a little.
Signed-off-by: Simon Glass sjg@chromium.org ---
board/nvidia/common/board.c | 57 +++++++++++++++++++++++++----------------- 1 files changed, 34 insertions(+), 23 deletions(-)
diff --git a/board/nvidia/common/board.c b/board/nvidia/common/board.c index 0f12de2..a5da310 100644 --- a/board/nvidia/common/board.c +++ b/board/nvidia/common/board.c @@ -35,6 +35,12 @@
DECLARE_GLOBAL_DATA_PTR;
+enum { + /* UARTs which we can enable */ + UARTA = 1 << 0, + UARTD = 1 << 3, +}; + const struct tegra2_sysinfo sysinfo = { CONFIG_TEGRA2_BOARD_STRING }; @@ -64,36 +70,32 @@ static void enable_uart(enum periph_id pid)
/* * Routine: clock_init_uart - * Description: init the PLL and clock for the UART(s) + * Description: init clock for the UART(s) */ -static void clock_init_uart(void) +static void clock_init_uart(int uart_ids) { -#if defined(CONFIG_TEGRA2_ENABLE_UARTA) - enable_uart(PERIPH_ID_UART1); -#endif /* CONFIG_TEGRA2_ENABLE_UARTA */ -#if defined(CONFIG_TEGRA2_ENABLE_UARTD) - enable_uart(PERIPH_ID_UART4); -#endif /* CONFIG_TEGRA2_ENABLE_UARTD */ + if (uart_ids & UARTA) + enable_uart(PERIPH_ID_UART1); + if (uart_ids & UARTD) + enable_uart(PERIPH_ID_UART4); }
/* * Routine: pin_mux_uart * Description: setup the pin muxes/tristate values for the UART(s) */ -static void pin_mux_uart(void) +static void pin_mux_uart(int uart_ids) { -#if defined(CONFIG_TEGRA2_ENABLE_UARTA) - pinmux_set_func(PINGRP_IRRX, PMUX_FUNC_UARTA); - pinmux_set_func(PINGRP_IRTX, PMUX_FUNC_UARTA); - - pinmux_tristate_disable(PINGRP_IRRX); - pinmux_tristate_disable(PINGRP_IRTX); -#endif /* CONFIG_TEGRA2_ENABLE_UARTA */ -#if defined(CONFIG_TEGRA2_ENABLE_UARTD) - pinmux_set_func(PINGRP_GMC, PMUX_FUNC_UARTD); - - pinmux_tristate_disable(PINGRP_GMC); -#endif /* CONFIG_TEGRA2_ENABLE_UARTD */ + if (uart_ids & UARTA) { + pinmux_set_func(PINGRP_IRRX, PMUX_FUNC_UARTA); + pinmux_set_func(PINGRP_IRTX, PMUX_FUNC_UARTA); + pinmux_tristate_disable(PINGRP_IRRX); + pinmux_tristate_disable(PINGRP_IRTX); + } + if (uart_ids & UARTD) { + pinmux_set_func(PINGRP_GMC, PMUX_FUNC_UARTD); + pinmux_tristate_disable(PINGRP_GMC); + } }
/* @@ -114,14 +116,23 @@ int board_init(void) #ifdef CONFIG_BOARD_EARLY_INIT_F int board_early_init_f(void) { + int uart_ids = 0; /* bit mask of which UART ids to enable */ + +#ifdef CONFIG_TEGRA2_ENABLE_UARTA + uart_ids |= UARTA; +#endif +#ifdef CONFIG_TEGRA2_ENABLE_UARTD + uart_ids |= UARTD; +#endif + /* Initialize essential common plls */ clock_early_init();
/* Initialize UART clocks */ - clock_init_uart(); + clock_init_uart(uart_ids);
/* Initialize periph pinmuxes */ - pin_mux_uart(); + pin_mux_uart(uart_ids);
/* Initialize periph GPIOs */ gpio_config_uart();

UARTB is used on some boards, so support it here.
Signed-off-by: Simon Glass sjg@chromium.org ---
board/nvidia/common/board.c | 10 ++++++++++ 1 files changed, 10 insertions(+), 0 deletions(-)
diff --git a/board/nvidia/common/board.c b/board/nvidia/common/board.c index a5da310..a2d45c1 100644 --- a/board/nvidia/common/board.c +++ b/board/nvidia/common/board.c @@ -38,6 +38,7 @@ DECLARE_GLOBAL_DATA_PTR; enum { /* UARTs which we can enable */ UARTA = 1 << 0, + UARTB = 1 << 1, UARTD = 1 << 3, };
@@ -76,6 +77,8 @@ static void clock_init_uart(int uart_ids) { if (uart_ids & UARTA) enable_uart(PERIPH_ID_UART1); + if (uart_ids & UARTB) + enable_uart(PERIPH_ID_UART2); if (uart_ids & UARTD) enable_uart(PERIPH_ID_UART4); } @@ -92,6 +95,10 @@ static void pin_mux_uart(int uart_ids) pinmux_tristate_disable(PINGRP_IRRX); pinmux_tristate_disable(PINGRP_IRTX); } + if (uart_ids & UARTB) { + pinmux_set_func(PINGRP_UAD, PMUX_FUNC_IRDA); + pinmux_tristate_disable(PINGRP_UAD); + } if (uart_ids & UARTD) { pinmux_set_func(PINGRP_GMC, PMUX_FUNC_UARTD); pinmux_tristate_disable(PINGRP_GMC); @@ -121,6 +128,9 @@ int board_early_init_f(void) #ifdef CONFIG_TEGRA2_ENABLE_UARTA uart_ids |= UARTA; #endif +#ifdef CONFIG_TEGRA2_ENABLE_UARTB + uart_ids |= UARTB; +#endif #ifdef CONFIG_TEGRA2_ENABLE_UARTD uart_ids |= UARTD; #endif

From: Tom Warren twarren.nvidia@gmail.com
This driver supports SPI on Tegra2, running at 48MHz.
Signed-off-by: Tom Warren twarren@nvidia.com --- Changes in v2: - Update to support SPI mode and frequency - Tidy driver according to comments, and tidy a little more
Changes in v3: - Remove the replaced bus/cs check function - Change (bitlen & 7) to (bitlen % 8)
arch/arm/include/asm/arch-tegra2/tegra2.h | 1 + arch/arm/include/asm/arch-tegra2/tegra2_spi.h | 76 +++++++ board/nvidia/common/board.c | 4 + drivers/spi/Makefile | 1 + drivers/spi/tegra2_spi.c | 260 +++++++++++++++++++++++++ 5 files changed, 342 insertions(+), 0 deletions(-) create mode 100644 arch/arm/include/asm/arch-tegra2/tegra2_spi.h create mode 100644 drivers/spi/tegra2_spi.c
diff --git a/arch/arm/include/asm/arch-tegra2/tegra2.h b/arch/arm/include/asm/arch-tegra2/tegra2.h index 742a75a..8941443 100644 --- a/arch/arm/include/asm/arch-tegra2/tegra2.h +++ b/arch/arm/include/asm/arch-tegra2/tegra2.h @@ -38,6 +38,7 @@ #define NV_PA_APB_UARTC_BASE (NV_PA_APB_MISC_BASE + 0x6200) #define NV_PA_APB_UARTD_BASE (NV_PA_APB_MISC_BASE + 0x6300) #define NV_PA_APB_UARTE_BASE (NV_PA_APB_MISC_BASE + 0x6400) +#define TEGRA2_SPI_BASE (NV_PA_APB_MISC_BASE + 0xC380) #define NV_PA_PMC_BASE 0x7000E400 #define NV_PA_CSITE_BASE 0x70040000
diff --git a/arch/arm/include/asm/arch-tegra2/tegra2_spi.h b/arch/arm/include/asm/arch-tegra2/tegra2_spi.h new file mode 100644 index 0000000..ceec428 --- /dev/null +++ b/arch/arm/include/asm/arch-tegra2/tegra2_spi.h @@ -0,0 +1,76 @@ +/* + * NVIDIA Tegra2 SPI-FLASH controller + * + * Copyright 2010-2011 NVIDIA Corporation + * + * This software may be used and distributed according to the + * terms of the GNU Public License, Version 2, incorporated + * herein by reference. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * Version 2 as published by the Free Software Foundation. + * + * 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., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + */ + +#ifndef _TEGRA2_SPI_H_ +#define _TEGRA2_SPI_H_ + +#include <asm/types.h> + +struct spi_tegra { + u32 command; /* SPI_COMMAND_0 register */ + u32 status; /* SPI_STATUS_0 register */ + u32 rx_cmp; /* SPI_RX_CMP_0 register */ + u32 dma_ctl; /* SPI_DMA_CTL_0 register */ + u32 tx_fifo; /* SPI_TX_FIFO_0 register */ + u32 rsvd[3]; /* offsets 0x14 to 0x1F reserved */ + u32 rx_fifo; /* SPI_RX_FIFO_0 register */ +}; + +#define SPI_CMD_GO (1 << 30) +#define SPI_CMD_ACTIVE_SCLK_SHIFT 26 +#define SPI_CMD_ACTIVE_SCLK_MASK (3 << SPI_CMD_ACTIVE_SCLK_SHIFT) +#define SPI_CMD_CK_SDA (1 << 21) +#define SPI_CMD_ACTIVE_SDA_SHIFT 18 +#define SPI_CMD_ACTIVE_SDA_MASK (3 << SPI_CMD_ACTIVE_SDA_SHIFT) +#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) + +#define SPI_TIMEOUT 1000 +#define TEGRA2_SPI_MAX_FREQ 52000000 + + +#endif /* _TEGRA2_SPI_H_ */ diff --git a/board/nvidia/common/board.c b/board/nvidia/common/board.c index a2d45c1..2591ebc 100644 --- a/board/nvidia/common/board.c +++ b/board/nvidia/common/board.c @@ -31,6 +31,7 @@ #include <asm/arch/clock.h> #include <asm/arch/pinmux.h> #include <asm/arch/uart.h> +#include <spi.h> #include "board.h"
DECLARE_GLOBAL_DATA_PTR; @@ -114,6 +115,9 @@ int board_init(void) clock_init(); clock_verify();
+#ifdef CONFIG_TEGRA2_SPI + spi_init(); +#endif /* boot param addr */ gd->bd->bi_boot_params = (NV_PA_SDRAM_BASE + 0x100);
diff --git a/drivers/spi/Makefile b/drivers/spi/Makefile index 84ad6fa..0a06822 100644 --- a/drivers/spi/Makefile +++ b/drivers/spi/Makefile @@ -42,6 +42,7 @@ COBJS-$(CONFIG_OMAP3_SPI) += omap3_spi.o COBJS-$(CONFIG_SOFT_SPI) += soft_spi.o COBJS-$(CONFIG_SH_SPI) += sh_spi.o COBJS-$(CONFIG_FSL_ESPI) += fsl_espi.o +COBJS-$(CONFIG_TEGRA2_SPI) += tegra2_spi.o
COBJS := $(COBJS-y) SRCS := $(COBJS:.o=.c) diff --git a/drivers/spi/tegra2_spi.c b/drivers/spi/tegra2_spi.c new file mode 100644 index 0000000..2db5a84 --- /dev/null +++ b/drivers/spi/tegra2_spi.c @@ -0,0 +1,260 @@ +/* + * Copyright (c) 2010-2011 NVIDIA Corporation + * With help from the mpc8xxx SPI driver + * With more help from omap3_spi SPI driver + * + * See file CREDITS for list of people who contributed to this + * project. + * + * 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., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + */ + +#include <common.h> + +#include <malloc.h> +#include <spi.h> +#include <asm/io.h> +#include <asm/gpio.h> +#include <asm/arch/clk_rst.h> +#include <asm/arch/clock.h> +#include <asm/arch/pinmux.h> +#include <asm/arch/tegra2_spi.h> + +struct tegra_spi_slave { + struct spi_slave slave; + struct spi_tegra *regs; + unsigned int freq; + unsigned int mode; +}; + +static inline struct tegra_spi_slave *to_tegra_spi(struct spi_slave *slave) +{ + return container_of(slave, struct tegra_spi_slave, slave); +} + +struct spi_slave *spi_setup_slave(unsigned int bus, unsigned int cs, + unsigned int max_hz, unsigned int mode) +{ + struct tegra_spi_slave *spi; + + if (bus != 0) { + printf("SPI error: unsupported bus %d\n", bus); + return NULL; + } + if (cs != 0) { + printf("SPI error: unsupported chip select %d on bus %d\n", + cs, bus); + return NULL; + } + + if (max_hz > TEGRA2_SPI_MAX_FREQ) { + printf("SPI error: unsupported frequency %d Hz. Max frequency" + " is %d Hz\n", max_hz, TEGRA2_SPI_MAX_FREQ); + return NULL; + } + + spi = malloc(sizeof(struct tegra_spi_slave)); + if (!spi) { + printf("SPI error: malloc of SPI structure failed\n"); + return NULL; + } + spi->slave.bus = bus; + spi->slave.cs = cs; + spi->freq = max_hz; + spi->regs = (struct spi_tegra *)TEGRA2_SPI_BASE; + spi->mode = mode; + + return &spi->slave; +} + +void spi_free_slave(struct spi_slave *slave) +{ + struct tegra_spi_slave *spi = to_tegra_spi(slave); + + free(spi); +} + +void spi_init(void) +{ + /* do nothing */ +} + +int spi_claim_bus(struct spi_slave *slave) +{ + struct tegra_spi_slave *spi = to_tegra_spi(slave); + struct spi_tegra *regs = spi->regs; + u32 reg; + + /* Change SPI clock to correct frequency, PLLP_OUT0 source */ + clock_start_periph_pll(PERIPH_ID_SPI1, CLOCK_ID_PERIPH, spi->freq); + + /* Clear stale status here */ + reg = SPI_STAT_RDY | SPI_STAT_RXF_FLUSH | SPI_STAT_TXF_FLUSH | \ + SPI_STAT_RXF_UNR | SPI_STAT_TXF_OVF; + writel(reg, ®s->status); + debug("spi_init: STATUS = %08x\n", readl(®s->status)); + + /* + * Use sw-controlled CS, so we can clock in data after ReadID, etc. + */ + reg = (spi->mode & 1) << SPI_CMD_ACTIVE_SDA_SHIFT; + if (spi->mode & 2) + reg |= 1 << SPI_CMD_ACTIVE_SCLK_SHIFT; + clrsetbits_le32(®s->command, SPI_CMD_ACTIVE_SCLK_MASK | + SPI_CMD_ACTIVE_SDA_MASK, SPI_CMD_CS_SOFT | reg); + debug("spi_init: COMMAND = %08x\n", readl(®s->command)); + + /* + * SPI pins on Tegra2 are muxed - change pinmux later due to UART + * issue. + */ + pinmux_set_func(PINGRP_GMD, PMUX_FUNC_SFLASH); + pinmux_tristate_disable(PINGRP_LSPI); + return 0; +} + +void spi_release_bus(struct spi_slave *slave) +{ + /* + * We can't release UART_DISABLE and set pinmux to UART4 here since + * some code (e,g, spi_flash_probe) uses printf() while the SPI + * bus is held. That is arguably bad, but it has the advantage of + * already being in the source tree. + */ +} + +void spi_cs_activate(struct spi_slave *slave) +{ + struct tegra_spi_slave *spi = to_tegra_spi(slave); + + /* CS is negated on Tegra, so drive a 1 to get a 0 */ + setbits_le32(&spi->regs->command, SPI_CMD_CS_VAL); +} + +void spi_cs_deactivate(struct spi_slave *slave) +{ + struct tegra_spi_slave *spi = to_tegra_spi(slave); + + /* CS is negated on Tegra, so drive a 0 to get a 1 */ + clrbits_le32(&spi->regs->command, SPI_CMD_CS_VAL); +} + +int spi_xfer(struct spi_slave *slave, unsigned int bitlen, + const void *data_out, void *data_in, unsigned long flags) +{ + struct tegra_spi_slave *spi = to_tegra_spi(slave); + struct spi_tegra *regs = spi->regs; + u32 reg, tmpdout, tmpdin = 0; + const u8 *dout = data_out; + u8 *din = data_in; + int num_bytes; + int ret; + + debug("spi_xfer: slave %u:%u dout %08X din %08X bitlen %u\n", + slave->bus, slave->cs, *(u8 *)dout, *(u8 *)din, bitlen); + if (bitlen % 8) + return -1; + num_bytes = bitlen / 8; + + ret = 0; + + reg = readl(®s->status); + writel(reg, ®s->status); /* Clear all SPI events via R/W */ + debug("spi_xfer entry: STATUS = %08x\n", reg); + + reg = readl(®s->command); + reg |= SPI_CMD_TXEN | SPI_CMD_RXEN; + writel(reg, ®s->command); + debug("spi_xfer: COMMAND = %08x\n", readl(®s->command)); + + if (flags & SPI_XFER_BEGIN) + spi_cs_activate(slave); + + /* handle data in 32-bit chunks */ + while (num_bytes > 0) { + int bytes; + int is_read = 0; + int tm, i; + + tmpdout = 0; + bytes = (num_bytes > 4) ? 4 : num_bytes; + + if (dout != NULL) { + for (i = 0; i < bytes; ++i) + tmpdout = (tmpdout << 8) | dout[i]; + } + + num_bytes -= bytes; + if (dout) + dout += bytes; + + clrsetbits_le32(®s->command, SPI_CMD_BIT_LENGTH_MASK, + bytes * 8 - 1); + writel(tmpdout, ®s->tx_fifo); + setbits_le32(®s->command, SPI_CMD_GO); + + /* + * Wait for SPI transmit FIFO to empty, or to time out. + * The RX FIFO status will be read and cleared last + */ + for (tm = 0, is_read = 0; tm < SPI_TIMEOUT; ++tm) { + u32 status; + + status = readl(®s->status); + + /* We can exit when we've had both RX and TX activity */ + if (is_read && (status & SPI_STAT_TXF_EMPTY)) + break; + + if ((status & (SPI_STAT_BSY | SPI_STAT_RDY)) != + SPI_STAT_RDY) + tm++; + + else if (!(status & SPI_STAT_RXF_EMPTY)) { + tmpdin = readl(®s->rx_fifo); + is_read = 1; + + /* swap bytes read in */ + if (din != NULL) { + for (i = bytes - 1; i >= 0; --i) { + din[i] = tmpdin & 0xff; + tmpdin >>= 8; + } + din += bytes; + } + } + } + + if (tm >= SPI_TIMEOUT) + ret = tm; + + /* clear ACK RDY, etc. bits */ + writel(readl(®s->status), ®s->status); + } + + if (flags & SPI_XFER_END) + spi_cs_deactivate(slave); + + debug("spi_xfer: transfer ended. Value=%08x, status = %08x\n", + tmpdin, readl(®s->status)); + + if (ret) { + printf("spi_xfer: timeout during SPI transfer, tm %d\n", ret); + return -1; + } + + return 0; +}

The Seaboard includes a Winbond 4MB flash part.
Signed-off-by: Simon Glass sjg@chromium.org ---
include/configs/seaboard.h | 11 +++++++++++ 1 files changed, 11 insertions(+), 0 deletions(-)
diff --git a/include/configs/seaboard.h b/include/configs/seaboard.h index 7d29144..7e8c8cc 100644 --- a/include/configs/seaboard.h +++ b/include/configs/seaboard.h @@ -37,11 +37,22 @@ #define CONFIG_TEGRA2_ENABLE_UARTD #define CONFIG_SYS_NS16550_COM1 NV_PA_APB_UARTD_BASE
+/* On Seaboard: GPIO_PI3 = Port I = 8, bit = 3 */ +#define CONFIG_UART_DISABLE_GPIO GPIO_PI3 + #define CONFIG_MACH_TYPE MACH_TYPE_SEABOARD #define CONFIG_SYS_BOARD_ODMDATA 0x300d8011 /* lp1, 1GB */
#define CONFIG_BOARD_EARLY_INIT_F
+/* SPI */ +#define CONFIG_TEGRA2_SPI +#define CONFIG_SPI_FLASH +#define CONFIG_SPI_FLASH_WINBOND +#define CONFIG_SF_DEFAULT_MODE SPI_MODE_0 +#define CONFIG_CMD_SPI +#define CONFIG_CMD_SF + /* SD/MMC */ #define CONFIG_MMC #define CONFIG_GENERIC_MMC

This uses the SPI flash on Seaboard to store an 8KB environment.
Signed-off-by: Simon Glass sjg@chromium.org --- Changes in v2: - Add CONFIG_SPI_FLASH_SIZE to define flash size - Add config for SPI environment mode/speed
include/configs/harmony.h | 3 +++ include/configs/seaboard.h | 9 +++++++++ include/configs/tegra2-common.h | 3 +-- 3 files changed, 13 insertions(+), 2 deletions(-)
diff --git a/include/configs/harmony.h b/include/configs/harmony.h index 89e4911..ce0ae9f 100644 --- a/include/configs/harmony.h +++ b/include/configs/harmony.h @@ -58,4 +58,7 @@ #define CONFIG_EFI_PARTITION #define CONFIG_CMD_EXT2 #define CONFIG_CMD_FAT + +/* Environment not stored */ +#define CONFIG_ENV_IS_NOWHERE #endif /* __CONFIG_H */ diff --git a/include/configs/seaboard.h b/include/configs/seaboard.h index 7e8c8cc..261f952 100644 --- a/include/configs/seaboard.h +++ b/include/configs/seaboard.h @@ -52,6 +52,7 @@ #define CONFIG_SF_DEFAULT_MODE SPI_MODE_0 #define CONFIG_CMD_SPI #define CONFIG_CMD_SF +#define CONFIG_SPI_FLASH_SIZE (4 << 20)
/* SD/MMC */ #define CONFIG_MMC @@ -63,4 +64,12 @@ #define CONFIG_EFI_PARTITION #define CONFIG_CMD_EXT2 #define CONFIG_CMD_FAT + +/* Environment in SPI */ +#define CONFIG_ENV_IS_IN_SPI_FLASH +#define CONFIG_ENV_SPI_MAX_HZ 48000000 +#define CONFIG_ENV_SPI_MODE SPI_MODE_0 + +#define CONFIG_ENV_SECT_SIZE CONFIG_ENV_SIZE +#define CONFIG_ENV_OFFSET (CONFIG_SPI_FLASH_SIZE - CONFIG_ENV_SECT_SIZE) #endif /* __CONFIG_H */ diff --git a/include/configs/tegra2-common.h b/include/configs/tegra2-common.h index 9c3b9fa..4595ae4 100644 --- a/include/configs/tegra2-common.h +++ b/include/configs/tegra2-common.h @@ -52,8 +52,7 @@ #define CONFIG_OF_LIBFDT /* enable passing of devicetree */
/* Environment */ -#define CONFIG_ENV_IS_NOWHERE -#define CONFIG_ENV_SIZE 0x20000 /* Total Size Environment */ +#define CONFIG_ENV_SIZE 0x2000 /* Total Size Environment */
/* * Size of malloc() pool

The Tegra2 Seaboard has the unfortunate feature that SPI and the console UART are multiplexed on the same pins. We need to switch between one and the other during SPI and console activity.
This new file implements a switch and keeps track of which peripheral owns the pins. It also flips over the controlling GPIO as needed
Since we are adding a second file to board/nvidia/common, we create a proper Makefile there and remove the direct board.o include from board/nvidia/seaboard/Makefile
Signed-off-by: Simon Glass sjg@chromium.org --- Changes in v2: - Fix up GPIO selection to use gpio_request() first - Rename uart-spi-fix to uart-spi-switch - Rename uart/spi_enable() to pinmux_select_uart/spi()
Changes in v3: - Remove unneeded #includes and header file guard
arch/arm/include/asm/arch-tegra2/uart-spi-switch.h | 46 +++++++ board/nvidia/common/Makefile | 47 +++++++ board/nvidia/common/uart-spi-switch.c | 138 ++++++++++++++++++++ board/nvidia/seaboard/Makefile | 1 - 4 files changed, 231 insertions(+), 1 deletions(-) create mode 100644 arch/arm/include/asm/arch-tegra2/uart-spi-switch.h create mode 100644 board/nvidia/common/Makefile create mode 100644 board/nvidia/common/uart-spi-switch.c
diff --git a/arch/arm/include/asm/arch-tegra2/uart-spi-switch.h b/arch/arm/include/asm/arch-tegra2/uart-spi-switch.h new file mode 100644 index 0000000..e4503b1 --- /dev/null +++ b/arch/arm/include/asm/arch-tegra2/uart-spi-switch.h @@ -0,0 +1,46 @@ +/* + * Copyright (c) 2011 The Chromium OS Authors. + * See file CREDITS for list of people who contributed to this + * project. + * + * 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., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + */ + +#ifndef _UART_SPI_SWITCH_H +#define _UART_SPI_SWITCH_H + +#if defined(CONFIG_SPI_UART_SWITCH) +/* + * Signal that we are about to use the UART. This unfortunate hack is + * required by Seaboard, which cannot use its console and SPI at the same + * time! If the board file provides this, the board config will declare it. + * Let this be a lesson for others. + */ +void pinmux_select_uart(NS16550_t regs); + +/* + * Signal that we are about the use the SPI bus. + */ +void pinmux_select_spi(void); + +#else /* not CONFIG_SPI_UART_SWITCH */ + +static inline void pinmux_select_uart(NS16550_t regs) {} +static inline void pinmux_select_spi(void) {} + +#endif + +#endif diff --git a/board/nvidia/common/Makefile b/board/nvidia/common/Makefile new file mode 100644 index 0000000..3e748fd --- /dev/null +++ b/board/nvidia/common/Makefile @@ -0,0 +1,47 @@ +# Copyright (c) 2011 The Chromium OS Authors. +# See file CREDITS for list of people who contributed to this +# project. +# +# 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., 59 Temple Place, Suite 330, Boston, +# MA 02111-1307 USA + +include $(TOPDIR)/config.mk + +ifneq ($(OBJTREE),$(SRCTREE)) +$(shell mkdir -p $(obj)board/$(VENDOR)/common) +endif + +LIB = $(obj)lib$(VENDOR).o + +COBJS-y += board.o +COBJS-$(CONFIG_SPI_UART_SWITCH) += uart-spi-switch.o + +COBJS := $(COBJS-y) +SRCS := $(SOBJS:.o=.S) $(COBJS:.o=.c) +OBJS := $(addprefix $(obj),$(COBJS)) +SOBJS := $(addprefix $(obj),$(SOBJS)) + +all: $(LIB) + +$(LIB): $(obj).depend $(OBJS) $(SOBJS) + $(call cmd_link_o_target, $(OBJS) $(SOBJS)) + +######################################################################### +# This is for $(obj).depend target +include $(SRCTREE)/rules.mk + +sinclude $(obj).depend + +######################################################################### diff --git a/board/nvidia/common/uart-spi-switch.c b/board/nvidia/common/uart-spi-switch.c new file mode 100644 index 0000000..23aa0b9 --- /dev/null +++ b/board/nvidia/common/uart-spi-switch.c @@ -0,0 +1,138 @@ +/* + * Copyright (c) 2011 The Chromium OS Authors. + * + * See file CREDITS for list of people who contributed to this + * project. + * + * 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., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + */ + +#include <common.h> +#include <ns16550.h> +#include <asm/gpio.h> +#include <asm/arch/pinmux.h> +#include <asm/arch/uart-spi-switch.h> +#include <asm/arch/tegra2.h> +#include <asm/arch/tegra2_spi.h> + + +/* position of the UART/SPI select switch */ +enum spi_uart_switch { + SWITCH_UNKNOWN, + SWITCH_SPI, + SWITCH_UART, + SWITCH_BOTH +}; + +/* Information about the spi/uart switch */ +struct spi_uart { + int gpio; /* GPIO to control switch */ + NS16550_t regs; /* Address of UART affected */ + u32 port; /* Port number of UART affected */ +}; + +static struct spi_uart local; +static enum spi_uart_switch switch_pos; /* Current switch position */ + + +static void get_config(struct spi_uart *config) +{ +#if defined CONFIG_SPI_CORRUPTS_UART + config->gpio = CONFIG_UART_DISABLE_GPIO; + config->regs = (NS16550_t)CONFIG_SPI_CORRUPTS_UART; + config->port = CONFIG_SPI_CORRUPTS_UART_NR; +#else + config->gpio = -1; +#endif +} + +/* + * Init the UART / SPI switch. This can be called before relocation so we must + * not access BSS. + */ +void gpio_early_init_uart(void) +{ + struct spi_uart config; + + get_config(&config); + if (config.gpio != -1) { + /* Cannot provide a label prior to relocation */ + gpio_request(config.gpio, NULL); + gpio_direction_output(config.gpio, 0); + } +} + +/* + * Configure the UART / SPI switch. + */ +void gpio_config_uart(void) +{ + get_config(&local); + if (local.gpio != -1) { + gpio_direction_output(local.gpio, 0); + switch_pos = SWITCH_UART; + } else { + /* + * If we're here we don't have a SPI switch; go ahead and + * enable the SPI now. We didn't in spi_init() so we wouldn't + * kill the UART. + */ + pinmux_set_func(PINGRP_GMC, PMUX_FUNC_SFLASH); + switch_pos = SWITCH_BOTH; + } +} + +static void spi_uart_switch(struct spi_uart *config, + enum spi_uart_switch new_pos) +{ + if (switch_pos == SWITCH_BOTH || new_pos == switch_pos) + return; + + /* if the UART was selected, allow it to drain */ + if (switch_pos == SWITCH_UART) + NS16550_drain(config->regs, config->port); + + /* We need to dynamically change the pinmux, shared w/UART RXD/CTS */ + pinmux_set_func(PINGRP_GMC, new_pos == SWITCH_SPI ? + PMUX_FUNC_SFLASH : PMUX_FUNC_UARTD); + + /* + * 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. + */ + gpio_direction_output(config->gpio, new_pos == SWITCH_SPI); + switch_pos = new_pos; + + /* if the SPI was selected, clear any junk bytes in the UART */ + if (switch_pos == SWITCH_UART) { + /* TODO: What if it is part-way through clocking in junk? */ + udelay(100); + NS16550_clear(config->regs, config->port); + } +} + +void pinmux_select_uart(NS16550_t regs) +{ + /* Also prevents calling spi_uart_switch() before relocation */ + if (regs == local.regs) + spi_uart_switch(&local, SWITCH_UART); +} + +void pinmux_select_spi(void) +{ + spi_uart_switch(&local, SWITCH_SPI); +} diff --git a/board/nvidia/seaboard/Makefile b/board/nvidia/seaboard/Makefile index f6599de..b6efa1c 100644 --- a/board/nvidia/seaboard/Makefile +++ b/board/nvidia/seaboard/Makefile @@ -31,7 +31,6 @@ endif LIB = $(obj)lib$(BOARD).o
COBJS := $(BOARD).o -COBJS += ../common/board.o
SRCS := $(COBJS:.o=.c) OBJS := $(addprefix $(obj),$(COBJS))

Add the SPI / UART switch logic into the Tegra2 SPI driver so that it can co-exist with the NS16550 UART.
We need the ns16550.h header for NS16550_t for now.
Signed-off-by: Simon Glass sjg@chromium.org --- Changes in v2: - Add pinmux logic to SPI driver - Rename uart/spi_enable() to pinmux_select_uart/spi()
drivers/spi/tegra2_spi.c | 14 ++++++++++++++ 1 files changed, 14 insertions(+), 0 deletions(-)
diff --git a/drivers/spi/tegra2_spi.c b/drivers/spi/tegra2_spi.c index 2db5a84..fe37218 100644 --- a/drivers/spi/tegra2_spi.c +++ b/drivers/spi/tegra2_spi.c @@ -28,9 +28,11 @@ #include <spi.h> #include <asm/io.h> #include <asm/gpio.h> +#include <ns16550.h> #include <asm/arch/clk_rst.h> #include <asm/arch/clock.h> #include <asm/arch/pinmux.h> +#include <asm/arch/uart-spi-switch.h> #include <asm/arch/tegra2_spi.h>
struct tegra_spi_slave { @@ -123,6 +125,16 @@ int spi_claim_bus(struct spi_slave *slave) */ pinmux_set_func(PINGRP_GMD, PMUX_FUNC_SFLASH); pinmux_tristate_disable(PINGRP_LSPI); + +#ifndef CONFIG_SPI_UART_SWITCH + /* + * NOTE: + * Only set PinMux bits 3:2 to SPI here on boards that don't have the + * SPI UART switch or subsequent UART data won't go out! See + * spi_uart_switch(). + */ + /* TODO: pinmux_set_func(PINGRP_GMC, PMUX_FUNC_SFLASH); */ +#endif return 0; }
@@ -140,6 +152,8 @@ void spi_cs_activate(struct spi_slave *slave) { struct tegra_spi_slave *spi = to_tegra_spi(slave);
+ pinmux_select_spi(); + /* CS is negated on Tegra, so drive a 1 to get a 0 */ setbits_le32(&spi->regs->command, SPI_CMD_CS_VAL); }

On Seaboard the UART and SPI interfere with each other. This causes the UART to receive spurious zero bytes after SPI transactions and also means that SPI can corrupt a few output characters when it starts up if they are still in the UART buffer.
This updates the board to use the SPI/UART switch to avoid the problem.
For now this feature is turned off since it needs changes to the NS16550 UART to operate.
Signed-off-by: Simon Glass sjg@chromium.org ---
board/nvidia/common/board.c | 8 ++++++++ board/nvidia/common/board.h | 1 + board/nvidia/seaboard/seaboard.c | 3 +++ 3 files changed, 12 insertions(+), 0 deletions(-)
diff --git a/board/nvidia/common/board.c b/board/nvidia/common/board.c index 2591ebc..0403645 100644 --- a/board/nvidia/common/board.c +++ b/board/nvidia/common/board.c @@ -112,9 +112,13 @@ static void pin_mux_uart(int uart_ids) */ int board_init(void) { + /* Do clocks and UART first so that printf() works */ clock_init(); clock_verify();
+#ifdef CONFIG_SPI_UART_SWITCH + gpio_config_uart(); +#endif #ifdef CONFIG_TEGRA2_SPI spi_init(); #endif @@ -149,7 +153,11 @@ int board_early_init_f(void) pin_mux_uart(uart_ids);
/* Initialize periph GPIOs */ +#ifdef CONFIG_SPI_UART_SWITCH + gpio_early_init_uart(); +#else gpio_config_uart(); +#endif
/* Init UART, scratch regs, and start CPU */ tegra2_start(); diff --git a/board/nvidia/common/board.h b/board/nvidia/common/board.h index 35acbca..2c89ff4 100644 --- a/board/nvidia/common/board.h +++ b/board/nvidia/common/board.h @@ -27,5 +27,6 @@ void tegra2_start(void); void gpio_config_uart(void); int tegra2_mmc_init(int dev_index, int bus_width, int pwr_gpio, int cd_gpio); +void gpio_early_init_uart(void);
#endif /* BOARD_H */ diff --git a/board/nvidia/seaboard/seaboard.c b/board/nvidia/seaboard/seaboard.c index 7f2827b..0b779f6 100644 --- a/board/nvidia/seaboard/seaboard.c +++ b/board/nvidia/seaboard/seaboard.c @@ -31,6 +31,8 @@ #endif #include "../common/board.h"
+/* TODO: Remove this code when the SPI switch is working */ +#ifndef CONFIG_SPI_UART_SWITCH /* * Routine: gpio_config_uart_seaboard * Description: Force GPIO_PI3 low on Seaboard so UART4 works. @@ -48,6 +50,7 @@ void gpio_config_uart(void) return; gpio_config_uart_seaboard(); } +#endif
#ifdef CONFIG_TEGRA2_MMC /*
participants (1)
-
Simon Glass