[U-Boot] [PATCH 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/patch/119325/
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 is under review.
http://patchwork.ozlabs.org/patch/120013/
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: Create SPI/UART switch code
Tom Warren (1): tegra2: spi: Add SPI driver for SPIFLASH on Seaboard
arch/arm/include/asm/arch-tegra2/tegra2_spi.h | 75 +++++++ board/nvidia/common/Makefile | 53 +++++ board/nvidia/common/board.c | 79 ++++++--- board/nvidia/common/board.h | 1 + board/nvidia/common/uart-spi-fix.c | 140 ++++++++++++++ board/nvidia/seaboard/Makefile | 1 - board/nvidia/seaboard/seaboard.c | 3 + drivers/spi/Makefile | 1 + drivers/spi/tegra2_spi.c | 255 +++++++++++++++++++++++++ include/configs/harmony.h | 3 + include/configs/seaboard.h | 17 ++ include/configs/tegra2-common.h | 3 +- include/uart-spi-fix.h | 45 +++++ 13 files changed, 650 insertions(+), 26 deletions(-) create mode 100644 arch/arm/include/asm/arch-tegra2/tegra2_spi.h create mode 100644 board/nvidia/common/Makefile create mode 100644 board/nvidia/common/uart-spi-fix.c create mode 100644 drivers/spi/tegra2_spi.c create mode 100644 include/uart-spi-fix.h

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 --- arch/arm/include/asm/arch-tegra2/tegra2_spi.h | 75 ++++++++ board/nvidia/common/board.c | 4 + drivers/spi/Makefile | 1 + drivers/spi/tegra2_spi.c | 245 +++++++++++++++++++++++++ 4 files changed, 325 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_spi.h b/arch/arm/include/asm/arch-tegra2/tegra2_spi.h new file mode 100644 index 0000000..4344334 --- /dev/null +++ b/arch/arm/include/asm/arch-tegra2/tegra2_spi.h @@ -0,0 +1,75 @@ +/* + * 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> + +#define TEGRA2_SPI_BASE 0x7000C380 + +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 (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) + +#define SPI_TIMEOUT 1000 + +#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 96c9642..02e7c40 100644 --- a/drivers/spi/Makefile +++ b/drivers/spi/Makefile @@ -41,6 +41,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..c8c20aa --- /dev/null +++ b/drivers/spi/tegra2_spi.c @@ -0,0 +1,245 @@ +/* + * Copyright (c) 2010-2011 NVIDIA Corporation + * With help from the mpc8xxx 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> + +int spi_cs_is_valid(unsigned int bus, unsigned int cs) +{ + /* Tegra2 SPI-Flash - only 1 device ('bus/cs') */ + if (bus > 0 && cs != 0) + return 0; + else + return 1; +} + + +struct spi_slave *spi_setup_slave(unsigned int bus, unsigned int cs, + unsigned int max_hz, unsigned int mode) +{ + struct spi_slave *slave; + + if (!spi_cs_is_valid(bus, cs)) + return NULL; + + slave = malloc(sizeof(struct spi_slave)); + if (!slave) + return NULL; + + slave->bus = bus; + slave->cs = cs; + + /* + * Currently, Tegra2 SFLASH uses mode 0 & a 24MHz clock. + * Use 'mode' and 'maz_hz' to change that here, if needed. + */ + + return slave; +} + +void spi_free_slave(struct spi_slave *slave) +{ + free(slave); +} + +void spi_init(void) +{ + struct spi_tegra *spi = (struct spi_tegra *)TEGRA2_SPI_BASE; + u32 reg; + + /* Change SPI clock to 48MHz, PLLP_OUT0 source */ + clock_start_periph_pll(PERIPH_ID_SPI1, CLOCK_ID_PERIPH, 48000000); + + /* 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, &spi->status); + debug("spi_init: STATUS = %08x\n", readl(&spi->status)); + + /* + * Use sw-controlled CS, so we can clock in data after ReadID, etc. + */ + reg = readl(&spi->command); + writel(reg | SPI_CMD_CS_SOFT, &spi->command); + debug("spi_init: COMMAND = %08x\n", readl(&spi->command)); + + /* + * SPI pins on Tegra2 are muxed - change pinmux later due to UART + * issue. + */ +} + +int spi_claim_bus(struct spi_slave *slave) +{ + return 0; +} + +void spi_release_bus(struct spi_slave *slave) +{ +} + +void spi_cs_activate(struct spi_slave *slave) +{ + struct spi_tegra *spi = (struct spi_tegra *)TEGRA2_SPI_BASE; + u32 val; + + /* CS is negated on Tegra, so drive a 1 to get a 0 */ + val = readl(&spi->command); + writel(val | SPI_CMD_CS_VAL, &spi->command); +} + +void spi_cs_deactivate(struct spi_slave *slave) +{ + struct spi_tegra *spi = (struct spi_tegra *)TEGRA2_SPI_BASE; + u32 val; + + /* CS is negated on Tegra, so drive a 0 to get a 1 */ + val = readl(&spi->command); + writel(val & ~SPI_CMD_CS_VAL, &spi->command); +} + +int spi_xfer(struct spi_slave *slave, unsigned int bitlen, const void *dout, + void *din, unsigned long flags) +{ + struct spi_tegra *spi = (struct spi_tegra *)TEGRA2_SPI_BASE; + unsigned int status; + int num_bytes = (bitlen + 7) / 8; + int i, ret, tm, bytes, bits, isRead = 0; + u32 reg, tmpdout, tmpdin = 0; + + debug("spi_xfer: slave %u:%u dout %08X din %08X bitlen %u\n", + slave->bus, slave->cs, *(u8 *)dout, *(u8 *)din, bitlen); + + ret = tm = 0; + + status = readl(&spi->status); + writel(status, &spi->status); /* Clear all SPI events via R/W */ + debug("spi_xfer entry: STATUS = %08x\n", status); + + reg = readl(&spi->command); + writel((reg |= (SPI_CMD_TXEN | SPI_CMD_RXEN)), &spi->command); + debug("spi_xfer: COMMAND = %08x\n", readl(&spi->command)); + + if (flags & SPI_XFER_BEGIN) + spi_cs_activate(slave); + + /* handle data in 32-bit chunks */ + while (num_bytes > 0) { + tmpdout = 0; + bytes = (num_bytes > 4) ? 4 : num_bytes; + bits = (bitlen > 32) ? 32 : bitlen; + + if (dout != NULL) { + for (i = 0; i < bytes; ++i) + tmpdout = (tmpdout << 8) | ((u8 *) dout)[i]; + } + + num_bytes -= bytes; + if (dout) + dout += bytes; + bitlen -= bits; + + reg = readl(&spi->command); + reg &= ~SPI_CMD_BIT_LENGTH_MASK; + reg |= (bits - 1); + writel(reg, &spi->command); + + /* Write data to FIFO and initiate transfer */ + writel(tmpdout, &spi->tx_fifo); + writel((reg |= SPI_CMD_GO), &spi->command); + + /* + * Wait for SPI transmit FIFO to empty, or to time out. + * The RX FIFO status will be read and cleared last + */ + for (tm = 0, isRead = 0; tm < SPI_TIMEOUT; ++tm) { + status = readl(&spi->status); + + while (status & SPI_STAT_BSY) { + status = readl(&spi->status); + + tm++; + if (tm > SPI_TIMEOUT) { + tm = 0; + break; + } + } + + while (!(status & SPI_STAT_RDY)) { + status = readl(&spi->status); + + tm++; + if (tm > SPI_TIMEOUT) { + tm = 0; + break; + } + } + + if (!(status & SPI_STAT_RXF_EMPTY)) { + tmpdin = readl(&spi->rx_fifo); + isRead = 1; + status = readl(&spi->status); + + /* swap bytes read in */ + if (din != NULL) { + for (i = bytes - 1; i >= 0; --i) { + ((u8 *)din)[i] = + (tmpdin & 0xff); + tmpdin >>= 8; + } + din += bytes; + } + } + + /* We can exit when we've had both RX and TX activity */ + status = readl(&spi->status); + if (isRead && (status & SPI_STAT_TXF_EMPTY)) + break; + } + + if (tm >= SPI_TIMEOUT) + ret = -1; + + status = readl(&spi->status); + writel(status, &spi->status); /* ACK RDY, etc. bits */ + } + + if (flags & SPI_XFER_END) + spi_cs_deactivate(slave); + + debug("spi_xfer: transfer ended. Value=%08x, status = %08x\n", + tmpdin, status); + + if (ret == -1) + printf("spi_xfer: timeout during SPI transfer, tm = %d\n", tm); + + return ret; +}

On Thursday 20 October 2011 15:03:24 Simon Glass wrote:
This driver supports SPI on Tegra2, running at 48MHz.
the summary says "SPIFLASH" and "Seaboard". sounds like this is a tegra2 SoC issue, and so driver/board specific info shouldn't be in the summary.
adding notes to the changelog as to what boards/setups have been tested is OK though ...
--- /dev/null +++ b/arch/arm/include/asm/arch-tegra2/tegra2_spi.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 */
+};
don't need that newline before the closing brace
--- /dev/null +++ b/drivers/spi/tegra2_spi.c
+int spi_cs_is_valid(unsigned int bus, unsigned int cs) +{
- /* Tegra2 SPI-Flash - only 1 device ('bus/cs') */
- if (bus > 0 && cs != 0)
return 0;
- else
return 1;
+}
only need one new line here
+struct spi_slave *spi_setup_slave(unsigned int bus, unsigned int cs,
unsigned int max_hz, unsigned int mode)
+{
- struct spi_slave *slave;
- if (!spi_cs_is_valid(bus, cs))
return NULL;
- slave = malloc(sizeof(struct spi_slave));
- if (!slave)
return NULL;
- slave->bus = bus;
- slave->cs = cs;
- /*
* Currently, Tegra2 SFLASH uses mode 0 & a 24MHz clock.
* Use 'mode' and 'maz_hz' to change that here, if needed.
*/
- return slave;
+}
this should be respecting hz/mode ...
+void spi_init(void) +{
- struct spi_tegra *spi = (struct spi_tegra *)TEGRA2_SPI_BASE;
- u32 reg;
- /* Change SPI clock to 48MHz, PLLP_OUT0 source */
- clock_start_periph_pll(PERIPH_ID_SPI1, CLOCK_ID_PERIPH, 48000000);
- /* 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, &spi->status);
- debug("spi_init: STATUS = %08x\n", readl(&spi->status));
- /*
* Use sw-controlled CS, so we can clock in data after ReadID, etc.
*/
- reg = readl(&spi->command);
- writel(reg | SPI_CMD_CS_SOFT, &spi->command);
- debug("spi_init: COMMAND = %08x\n", readl(&spi->command));
- /*
* SPI pins on Tegra2 are muxed - change pinmux later due to UART
* issue.
*/
+}
shouldn't this be in spi_claim_bus() ? and configured using the values requested at spi_setup_slave() ?
+int spi_xfer(struct spi_slave *slave, unsigned int bitlen, const void
*dout, void *din, unsigned long flags)
+{
- int num_bytes = (bitlen + 7) / 8;
if you can only handle multiples of 8 bits, then return an error. many (most) of the spi buses do just that.
- reg = readl(&spi->command);
- writel((reg |= (SPI_CMD_TXEN | SPI_CMD_RXEN)), &spi->command);
ugh! pull that "|=" operation out and move it up to the readl() line above.
- debug("spi_xfer: COMMAND = %08x\n", readl(&spi->command));
the debug() should use "reg" rather than readl().
- debug("spi_xfer: slave %u:%u dout %08X din %08X bitlen %u\n",
slave->bus, slave->cs, *(u8 *)dout, *(u8 *)din, bitlen);
...
tmpdout = (tmpdout << 8) | ((u8 *) dout)[i];
...
((u8 *)din)[i] =
(tmpdin & 0xff);
please setup local vars with u8* type and set din/dout to that to avoid all these inline casts
writel((reg |= SPI_CMD_GO), &spi->command);
hoist that |= operation up and out -mike

Hi Mike,
On Thu, Oct 20, 2011 at 1:03 PM, Mike Frysinger vapier@gentoo.org wrote:
On Thursday 20 October 2011 15:03:24 Simon Glass wrote:
This driver supports SPI on Tegra2, running at 48MHz.
the summary says "SPIFLASH" and "Seaboard". sounds like this is a tegra2 SoC issue, and so driver/board specific info shouldn't be in the summary.
adding notes to the changelog as to what boards/setups have been tested is OK though ...
--- /dev/null +++ b/arch/arm/include/asm/arch-tegra2/tegra2_spi.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 */
+};
don't need that newline before the closing brace
--- /dev/null +++ b/drivers/spi/tegra2_spi.c
+int spi_cs_is_valid(unsigned int bus, unsigned int cs) +{
- /* Tegra2 SPI-Flash - only 1 device ('bus/cs') */
- if (bus > 0 && cs != 0)
- return 0;
- else
- return 1;
+}
only need one new line here
+struct spi_slave *spi_setup_slave(unsigned int bus, unsigned int cs,
- unsigned int max_hz, unsigned int mode)
+{
- struct spi_slave *slave;
- if (!spi_cs_is_valid(bus, cs))
- return NULL;
- slave = malloc(sizeof(struct spi_slave));
- if (!slave)
- return NULL;
- slave->bus = bus;
- slave->cs = cs;
- /*
- * Currently, Tegra2 SFLASH uses mode 0 & a 24MHz clock.
- * Use 'mode' and 'maz_hz' to change that here, if needed.
- */
- return slave;
+}
this should be respecting hz/mode ...
Thanks for the comments.
OK I will need to implement this. There will be a short delay....
Regards, Simon
+void spi_init(void) +{
- struct spi_tegra *spi = (struct spi_tegra *)TEGRA2_SPI_BASE;
- u32 reg;
- /* Change SPI clock to 48MHz, PLLP_OUT0 source */
- clock_start_periph_pll(PERIPH_ID_SPI1, CLOCK_ID_PERIPH, 48000000);
- /* 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, &spi->status);
- debug("spi_init: STATUS = %08x\n", readl(&spi->status));
- /*
- * Use sw-controlled CS, so we can clock in data after ReadID, etc.
- */
- reg = readl(&spi->command);
- writel(reg | SPI_CMD_CS_SOFT, &spi->command);
- debug("spi_init: COMMAND = %08x\n", readl(&spi->command));
- /*
- * SPI pins on Tegra2 are muxed - change pinmux later due to UART
- * issue.
- */
+}
shouldn't this be in spi_claim_bus() ? and configured using the values requested at spi_setup_slave() ?
+int spi_xfer(struct spi_slave *slave, unsigned int bitlen, const void
- *dout, void *din, unsigned long flags)
+{
- int num_bytes = (bitlen + 7) / 8;
if you can only handle multiples of 8 bits, then return an error. many (most) of the spi buses do just that.
- reg = readl(&spi->command);
- writel((reg |= (SPI_CMD_TXEN | SPI_CMD_RXEN)), &spi->command);
ugh! pull that "|=" operation out and move it up to the readl() line above.
- debug("spi_xfer: COMMAND = %08x\n", readl(&spi->command));
the debug() should use "reg" rather than readl().
- debug("spi_xfer: slave %u:%u dout %08X din %08X bitlen %u\n",
- slave->bus, slave->cs, *(u8 *)dout, *(u8 *)din, bitlen);
...
- tmpdout = (tmpdout << 8) | ((u8 *) dout)[i];
...
- ((u8 *)din)[i] =
- (tmpdin & 0xff);
please setup local vars with u8* type and set din/dout to that to avoid all these inline casts
- writel((reg |= SPI_CMD_GO), &spi->command);
hoist that |= operation up and out -mike

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 --- include/configs/harmony.h | 3 +++ include/configs/seaboard.h | 6 ++++++ include/configs/tegra2-common.h | 3 +-- 3 files changed, 10 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..bd83a84 100644 --- a/include/configs/seaboard.h +++ b/include/configs/seaboard.h @@ -63,4 +63,10 @@ #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_SECT_SIZE CONFIG_ENV_SIZE +#define CONFIG_ENV_OFFSET ((4 << 20) - CONFIG_ENV_SECT_SIZE) #endif /* __CONFIG_H */ diff --git a/include/configs/tegra2-common.h b/include/configs/tegra2-common.h index 73e0f05..2dd6fa4 100644 --- a/include/configs/tegra2-common.h +++ b/include/configs/tegra2-common.h @@ -50,8 +50,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

Simon,
-----Original Message----- From: Simon Glass [mailto:sjg@chromium.org] Sent: Thursday, October 20, 2011 12:03 PM To: U-Boot Mailing List Cc: Albert ARIBAUD; Tom Warren; Stephen Warren; Simon Glass Subject: [PATCH 5/8] tegra2: Enable SPI environment on Seaboard
This uses the SPI flash on Seaboard to store an 8KB environment.
Signed-off-by: Simon Glass sjg@chromium.org
Great to see these patches going up!
include/configs/harmony.h | 3 +++ include/configs/seaboard.h | 6 ++++++ include/configs/tegra2-common.h | 3 +-- 3 files changed, 10 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..bd83a84 100644 --- a/include/configs/seaboard.h +++ b/include/configs/seaboard.h @@ -63,4 +63,10 @@ #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_SECT_SIZE CONFIG_ENV_SIZE +#define CONFIG_ENV_OFFSET ((4 << 20) - CONFIG_ENV_SECT_SIZE)
First, why not use a SZ_4M equate here? 4 << 20 is a bit hard to decode on a quick read. Second, you are assuming here that all SPI chips will be 4MB/32Mbit. While that's true on All extant Seaboards, it may not always be true. Maybe we should provide the expected size in a #define in the SPI section in seaboard.h?
#endif /* __CONFIG_H */ diff --git a/include/configs/tegra2-common.h b/include/configs/tegra2- common.h index 73e0f05..2dd6fa4 100644 --- a/include/configs/tegra2-common.h +++ b/include/configs/tegra2-common.h @@ -50,8 +50,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 */
Same deal here - using SZ_8K just reads better to me.
/*
- Size of malloc() pool
-- 1.7.3.1
Thanks,
Tom
----------------------------------------------------------------------------------- This email message is for the sole use of the intended recipient(s) and may contain confidential information. Any unauthorized review, use, disclosure or distribution is prohibited. If you are not the intended recipient, please contact the sender by reply email and destroy all copies of the original message. -----------------------------------------------------------------------------------

Hi Tom,
On Thu, Oct 20, 2011 at 12:31 PM, Tom Warren TWarren@nvidia.com wrote:
Simon,
-----Original Message----- From: Simon Glass [mailto:sjg@chromium.org] Sent: Thursday, October 20, 2011 12:03 PM To: U-Boot Mailing List Cc: Albert ARIBAUD; Tom Warren; Stephen Warren; Simon Glass Subject: [PATCH 5/8] tegra2: Enable SPI environment on Seaboard
This uses the SPI flash on Seaboard to store an 8KB environment.
Signed-off-by: Simon Glass sjg@chromium.org
Great to see these patches going up!
Yes - testing is a bit tricky at present since we are so many patches behind. I will send a summary email to the list with what I think are the outstanding Tegra patches.
include/configs/harmony.h | 3 +++ include/configs/seaboard.h | 6 ++++++ include/configs/tegra2-common.h | 3 +-- 3 files changed, 10 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..bd83a84 100644 --- a/include/configs/seaboard.h +++ b/include/configs/seaboard.h @@ -63,4 +63,10 @@ #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_SECT_SIZE CONFIG_ENV_SIZE +#define CONFIG_ENV_OFFSET ((4 << 20) - CONFIG_ENV_SECT_SIZE)
First, why not use a SZ_4M equate here? 4 << 20 is a bit hard to decode on a quick read.
While I may (or may not) agree with you, I didn't want to annoy Wolfgang who doesn't like the SZ macros.
Second, you are assuming here that all SPI chips will be 4MB/32Mbit. While that's true on All extant Seaboards, it may not always be true. Maybe we should provide the expected size in a #define in the SPI section in seaboard.h?
Yes I will add a SPI flash size into seaboard.h, thanks.
Regards, Simon
#endif /* __CONFIG_H */ diff --git a/include/configs/tegra2-common.h b/include/configs/tegra2- common.h index 73e0f05..2dd6fa4 100644 --- a/include/configs/tegra2-common.h +++ b/include/configs/tegra2-common.h @@ -50,8 +50,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 */
Same deal here - using SZ_8K just reads better to me.
/* * Size of malloc() pool -- 1.7.3.1
Thanks,
Tom
This email message is for the sole use of the intended recipient(s) and may contain confidential information. Any unauthorized review, use, disclosure or distribution is prohibited. If you are not the intended recipient, please contact the sender by reply email and destroy all copies of the original message.

Simon,
-----Original Message----- From: sjg@google.com [mailto:sjg@google.com] On Behalf Of Simon Glass Sent: Thursday, October 20, 2011 12:58 PM To: Tom Warren Cc: U-Boot Mailing List; Albert ARIBAUD; Stephen Warren Subject: Re: [PATCH 5/8] tegra2: Enable SPI environment on Seaboard
Hi Tom,
On Thu, Oct 20, 2011 at 12:31 PM, Tom Warren TWarren@nvidia.com wrote:
Simon,
-----Original Message----- From: Simon Glass [mailto:sjg@chromium.org] Sent: Thursday, October 20, 2011 12:03 PM To: U-Boot Mailing List Cc: Albert ARIBAUD; Tom Warren; Stephen Warren; Simon Glass Subject: [PATCH 5/8] tegra2: Enable SPI environment on Seaboard
This uses the SPI flash on Seaboard to store an 8KB environment.
Signed-off-by: Simon Glass sjg@chromium.org
Great to see these patches going up!
Yes - testing is a bit tricky at present since we are so many patches behind. I will send a summary email to the list with what I think are the outstanding Tegra patches.
include/configs/harmony.h | 3 +++ include/configs/seaboard.h | 6 ++++++ include/configs/tegra2-common.h | 3 +-- 3 files changed, 10 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..bd83a84 100644 --- a/include/configs/seaboard.h +++ b/include/configs/seaboard.h @@ -63,4 +63,10 @@ #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_SECT_SIZE CONFIG_ENV_SIZE +#define CONFIG_ENV_OFFSET ((4 << 20) - CONFIG_ENV_SECT_SIZE)
First, why not use a SZ_4M equate here? 4 << 20 is a bit hard to decode on
a quick read.
While I may (or may not) agree with you, I didn't want to annoy Wolfgang who doesn't like the SZ macros.
OK, sorry. Didn't know Wolfgang wasn't a fan of the SZ_ equates. Note that they are not macros, but defines/equates. No magic, no side-effects (see size.h). I still like them a whole lot better than x << y or 0x002000, etc. Too easy to use one-too-many (or too-few) zeroes and get the wrong value. You'll see 'em used in my T30 config files, etc.
Second, you are assuming here that all SPI chips will be 4MB/32Mbit. While
that's true on
All extant Seaboards, it may not always be true. Maybe we should provide
the expected size
in a #define in the SPI section in seaboard.h?
Yes I will add a SPI flash size into seaboard.h, thanks.
Cool. Thanks.
Tom
Regards, Simon
#endif /* __CONFIG_H */ diff --git a/include/configs/tegra2-common.h b/include/configs/tegra2- common.h index 73e0f05..2dd6fa4 100644 --- a/include/configs/tegra2-common.h +++ b/include/configs/tegra2-common.h @@ -50,8 +50,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 */
Same deal here - using SZ_8K just reads better to me.
/* * Size of malloc() pool -- 1.7.3.1
Thanks,
Tom
This email message is for the sole use of the intended recipient(s) and
may contain
confidential information. Any unauthorized review, use, disclosure or
distribution
is prohibited. If you are not the intended recipient, please contact the
sender by
reply email and destroy all copies of the original message.

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
Signed-off-by: Simon Glass sjg@chromium.org --- board/nvidia/common/Makefile | 53 ++++++++++++++ board/nvidia/common/uart-spi-fix.c | 140 ++++++++++++++++++++++++++++++++++++ board/nvidia/seaboard/Makefile | 1 - include/uart-spi-fix.h | 45 ++++++++++++ 4 files changed, 238 insertions(+), 1 deletions(-) create mode 100644 board/nvidia/common/Makefile create mode 100644 board/nvidia/common/uart-spi-fix.c create mode 100644 include/uart-spi-fix.h
diff --git a/board/nvidia/common/Makefile b/board/nvidia/common/Makefile new file mode 100644 index 0000000..bf6d11a --- /dev/null +++ b/board/nvidia/common/Makefile @@ -0,0 +1,53 @@ +# 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-fix.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)) + +clean: + rm -f $(SOBJS) $(OBJS) + +distclean: clean + rm -f $(LIB) core *.bak $(obj).depend + +######################################################################### +# This is for $(obj).depend target +include $(SRCTREE)/rules.mk + +sinclude $(obj).depend + +######################################################################### diff --git a/board/nvidia/common/uart-spi-fix.c b/board/nvidia/common/uart-spi-fix.c new file mode 100644 index 0000000..e03857e --- /dev/null +++ b/board/nvidia/common/uart-spi-fix.c @@ -0,0 +1,140 @@ +/* + * 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 <asm/io.h> +#include <ns16550.h> +#include "uart-spi-fix.h" +#include <libfdt.h> +#include <fdt_support.h> +#include <asm/gpio.h> +#include <asm/arch/pinmux.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 = 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) + gpio_direction_output(config.gpio, 0); + switch_pos = SWITCH_BOTH; +} + +/* + * Configure the UART / SPI switch. + */ +void gpio_config_uart(void) +{ + get_config(&local); + switch_pos = SWITCH_BOTH; + 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 uart_enable(NS16550_t regs) +{ + /* Also prevents calling spi_uart_switch() before relocation */ + if (regs == local.regs) + spi_uart_switch(&local, SWITCH_UART); +} + +void spi_enable(void) +{ + spi_uart_switch(&local, SWITCH_SPI); +} diff --git a/board/nvidia/seaboard/Makefile b/board/nvidia/seaboard/Makefile index aab185e..7b67a26 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)) diff --git a/include/uart-spi-fix.h b/include/uart-spi-fix.h new file mode 100644 index 0000000..68a7eb1 --- /dev/null +++ b/include/uart-spi-fix.h @@ -0,0 +1,45 @@ +/* + * 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 + */ + + +#if defined(CONFIG_SPI_UART_SWITCH) + +#ifndef __ASSEMBLY__ +/* + * 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 uart_enable(NS16550_t regs); + +/* + * Signal that we are about the use the SPI bus. + */ +void spi_enable(void); +#endif + +#else /* not CONFIG_SPI_UART_SWITCH */ + +static inline void uart_enable(NS16550_t regs) {} +static inline void spi_enable(void) {} + +#endif

On Thursday 20 October 2011 15:03:27 Simon Glass wrote:
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.
so how does printf()/debug() work in the spi driver ? or it doesn't ?
owns the pins. It also flips over the controlling GPIO as needed
missing period at the end here
--- /dev/null +++ b/board/nvidia/common/Makefile
+clean:
- rm -f $(SOBJS) $(OBJS)
+distclean: clean
- rm -f $(LIB) core *.bak $(obj).depend
dead code
--- /dev/null +++ b/board/nvidia/common/uart-spi-fix.c
maybe call it "uart-spi-switch" ?
--- 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))
unrelated change sneak in ?
--- /dev/null +++ b/include/uart-spi-fix.h
i can't see this being a general thing, so probably best to keep in the tegra- specific subdirs -mike

On Thursday 20 October 2011 15:03:27 Simon Glass wrote:
+void uart_enable(NS16550_t regs); +void spi_enable(void);
also, you're not really enabling these devices, you're switching the pinmux. so perhaps a better name would be "pinmux_select_uart()" ? or something similar ? -mike

Add the SPI / UART switch logic into the Tegra2 SPI driver so that it can co-exist with the NS16550 UART.
Signed-off-by: Simon Glass sjg@chromium.org --- drivers/spi/tegra2_spi.c | 10 ++++++++++ 1 files changed, 10 insertions(+), 0 deletions(-)
diff --git a/drivers/spi/tegra2_spi.c b/drivers/spi/tegra2_spi.c index c8c20aa..83b36b3 100644 --- a/drivers/spi/tegra2_spi.c +++ b/drivers/spi/tegra2_spi.c @@ -24,6 +24,7 @@ #include <common.h>
#include <malloc.h> +#include <ns16550.h> /* for NS16550_drain and NS16550_clear */ #include <spi.h> #include <asm/io.h> #include <asm/gpio.h> @@ -31,6 +32,7 @@ #include <asm/arch/clock.h> #include <asm/arch/pinmux.h> #include <asm/arch/tegra2_spi.h> +#include "uart-spi-fix.h"
int spi_cs_is_valid(unsigned int bus, unsigned int cs) { @@ -104,6 +106,12 @@ int spi_claim_bus(struct spi_slave *slave)
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) @@ -111,6 +119,8 @@ void spi_cs_activate(struct spi_slave *slave) struct spi_tegra *spi = (struct spi_tegra *)TEGRA2_SPI_BASE; u32 val;
+ spi_enable(); + /* CS is negated on Tegra, so drive a 1 to get a 0 */ val = readl(&spi->command); writel(val | SPI_CMD_CS_VAL, &spi->command);

On Thursday 20 October 2011 15:03:28 Simon Glass wrote:
--- a/drivers/spi/tegra2_spi.c +++ b/drivers/spi/tegra2_spi.c
+#include <ns16550.h> /* for NS16550_drain and NS16550_clear */
except you don't use those funcs in this file ... so this include shouldn't be here ?
+#include "uart-spi-fix.h"
this file isn't in drivers/spi/, so you shouldn't be using "". -mike

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 2e8f4cc..2074e50 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 /*

On Thursday 20 October 2011 15:03:29 Simon Glass wrote:
--- a/board/nvidia/seaboard/seaboard.c +++ b/board/nvidia/seaboard/seaboard.c
+/* TODO: Remove this code when the SPI switch is working */ +#ifndef CONFIG_SPI_UART_SWITCH
eh ? isn't it working after these patches ? -mike

Hi Mike,
On Thu, Oct 20, 2011 at 1:09 PM, Mike Frysinger vapier@gentoo.org wrote:
On Thursday 20 October 2011 15:03:29 Simon Glass wrote:
--- a/board/nvidia/seaboard/seaboard.c +++ b/board/nvidia/seaboard/seaboard.c
+/* TODO: Remove this code when the SPI switch is working */ +#ifndef CONFIG_SPI_UART_SWITCH
eh ? isn't it working after these patches ? -mike
No, we need to resolve the NS16550 buffering issue, and also work out how to get that driver to indicate when it needs to 'claim' the pins from SPI.
I have a working implementation, and patches for it. Basically I call uart_enable() from the NS16550 driver before writing any serial data out or when wanting to read serial data. But it is ugly and I am torn between simple ugliness and complex pervasiveness.
Advice welcome.
Regards, Simon

On Thursday 20 October 2011 16:23:33 Simon Glass wrote:
On Thu, Oct 20, 2011 at 1:09 PM, Mike Frysinger wrote:
On Thursday 20 October 2011 15:03:29 Simon Glass wrote:
--- a/board/nvidia/seaboard/seaboard.c +++ b/board/nvidia/seaboard/seaboard.c
+/* TODO: Remove this code when the SPI switch is working */ +#ifndef CONFIG_SPI_UART_SWITCH
eh ? isn't it working after these patches ?
No, we need to resolve the NS16550 buffering issue, and also work out how to get that driver to indicate when it needs to 'claim' the pins from SPI.
I have a working implementation, and patches for it. Basically I call uart_enable() from the NS16550 driver before writing any serial data out or when wanting to read serial data. But it is ugly and I am torn between simple ugliness and complex pervasiveness.
Advice welcome.
hmmmmmmmmmm. this is probably crazy, but ... if you enable multi serial, enable the ns16550 driver, and then write another "serial" driver that merely makes the relevant pinmux calls before tailing into the ns16550, and then set your stdout/stdin/stderr to that glue driver ... -mike

Hi Mike,
On Thu, Oct 20, 2011 at 1:34 PM, Mike Frysinger vapier@gentoo.org wrote:
On Thursday 20 October 2011 16:23:33 Simon Glass wrote:
On Thu, Oct 20, 2011 at 1:09 PM, Mike Frysinger wrote:
On Thursday 20 October 2011 15:03:29 Simon Glass wrote:
--- a/board/nvidia/seaboard/seaboard.c +++ b/board/nvidia/seaboard/seaboard.c
+/* TODO: Remove this code when the SPI switch is working */ +#ifndef CONFIG_SPI_UART_SWITCH
eh ? isn't it working after these patches ?
No, we need to resolve the NS16550 buffering issue, and also work out how to get that driver to indicate when it needs to 'claim' the pins from SPI.
I have a working implementation, and patches for it. Basically I call uart_enable() from the NS16550 driver before writing any serial data out or when wanting to read serial data. But it is ugly and I am torn between simple ugliness and complex pervasiveness.
Advice welcome.
hmmmmmmmmmm. this is probably crazy, but ... if you enable multi serial, enable the ns16550 driver, and then write another "serial" driver that merely makes the relevant pinmux calls before tailing into the ns16550, and then set your stdout/stdin/stderr to that glue driver ... -mike
Gosh, that is baroque. Still it is an unusual hardware situation.
Given the current mess around the eserial structure definition macros I would need to copy that again and create a set of _putc(), _puts() functions for each UART I think. It could be done once serial is cleaned up, but really don't want to undertake it now. In any case we need buffering for all this to work so I might as well wait until this is resolved:
http://patchwork.ozlabs.org/patch/120013/
Regards, Simon

On Thursday 20 October 2011 16:44:26 Simon Glass wrote:
Given the current mess around the eserial structure definition macros I would need to copy that again and create a set of _putc(), _puts() functions for each UART I think. It could be done once serial is cleaned up, but really don't want to undertake it now.
well, your pseudo serial driver need not have one reflection per ns16550 instance. you could require the user set an env var to the name of the serial device that you should redirect to. -mike
participants (3)
-
Mike Frysinger
-
Simon Glass
-
Tom Warren