diff mbox

[U-Boot] spi: Tegra2: Add SPI driver for SPIFLASH on Seaboard

Message ID 1304002513-25701-1-git-send-email-twarren@nvidia.com
State Changes Requested
Headers show

Commit Message

Tom Warren April 28, 2011, 2:55 p.m. UTC
Signed-off-by: Tom Warren <twarren@nvidia.com>
---
This patch adds support for the SPIFLASH peripheral on Tegra2 (SPI 1).
Probe, erase, read and write are all supported, as well as low-level
SPI commands via 'sspi'.

Note that, at this time, only Seaboard has a SPI flash part (Winbond).
With this code, I've written U-Boot to SPI from within U-Boot, and
booted the system from that SPI image (boot sel jumpers must be set
to SPI [1000], and the UART ENABLE jumper must be set to GPIO CTRL).

 arch/arm/include/asm/arch-tegra2/tegra2_spi.h |   79 ++++++
 arch/arm/lib/board.c                          |   10 +-
 drivers/spi/Makefile                          |    1 +
 drivers/spi/tegra2_spi.c                      |  335 +++++++++++++++++++++++++
 include/configs/seaboard.h                    |   10 +
 5 files changed, 432 insertions(+), 3 deletions(-)
 create mode 100644 arch/arm/include/asm/arch-tegra2/tegra2_spi.h
 create mode 100644 drivers/spi/tegra2_spi.c

Comments

Mike Frysinger April 29, 2011, 11:21 p.m. UTC | #1
On Thursday, April 28, 2011 10:55:13 Tom Warren wrote:
> Signed-off-by: Tom Warren <twarren@nvidia.com>
> ---
> This patch adds support for the SPIFLASH peripheral on Tegra2 (SPI 1).
> Probe, erase, read and write are all supported, as well as low-level
> SPI commands via 'sspi'.
> 
> Note that, at this time, only Seaboard has a SPI flash part (Winbond).
> With this code, I've written U-Boot to SPI from within U-Boot, and
> booted the system from that SPI image (boot sel jumpers must be set
> to SPI [1000], and the UART ENABLE jumper must be set to GPIO CTRL).

so this is a driver for a SPI bus, and the board you're working on just 
happens to have some SPI flashes connected to it ?  then the "SPIFLASH" part 
is irrelevant, as is the board in question.  please remove references to both 
and just refer to this as "a SPI driver for Tegra2 processors".

> --- /dev/null
> +++ b/arch/arm/include/asm/arch-tegra2/tegra2_spi.h

if the only thing that uses this is the spi driver, then please put this in 
drivers/spi/tegra2_spi.h

> +#define	SPI_CMD_GO		(1 << 30)

dont put a tab after the #define

> --- a/arch/arm/lib/board.c
> +++ b/arch/arm/lib/board.c

this change needs to be split out into a dedicated one

> -

this new line should not be deleted

> +#if defined(CONFIG_CMD_SPI)
> +#include <spi.h>
> +#endif

no need to protect the inclusion.  just always include it.

>  #endif
> -
>  #if defined(CONFIG_CMD_NAND)

nor should this newline be deleted

>  	puts ("NAND:  ");
>  	nand_init();		/* go init the NAND */
>  #endif
> -
> +#if defined(CONFIG_CMD_SPI)

nor this newline

> +int spi_claim_bus(struct spi_slave *slave)
> +{
> +	/* Move bulk of spi_init code here? */

yes, so do that

> +void spi_release_bus(struct spi_slave *slave)
> +{

this func should be disabling the spi bus

> +void spi_cs_activate(struct spi_slave *slave)
> +{
> +	struct pmux_tri_ctlr *pmt = (struct pmux_tri_ctlr *)NV_PA_APB_MISC_BASE;
> +	struct spi_tegra *spi = (struct spi_tegra *)TEGRA2_SPI_BASE;
> +	u32 val;
> +
> +	/*
> +	 * Delay here to clean up comms - spurious chars seen around SPI xfers.
> +	 * Fine-tune later.
> +	 */
> +	udelay(1000);

fine tune now ?

> +	/*
> +	 * 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(UART_DISABLE_GPIO, 1);

board specific issues shouldnt be in a processor driver

> +	int numBytes = (bitlen + 7) / 8;

no camel case.  use "num_bytes".

> +					for (i = 0; i < bytes; ++i) {
> +						((u8 *)din)[i] = (tmpdin >> 24);

create a dedicated pointer and deference that.  casts on the lhs are poor 
style.

> --- a/include/configs/seaboard.h
> +++ b/include/configs/seaboard.h

this should be split out into a dedicated patch
-mike
Tom Warren May 2, 2011, 3:33 p.m. UTC | #2
Mike,

On Fri, Apr 29, 2011 at 4:21 PM, Mike Frysinger <vapier@gentoo.org> wrote:
> On Thursday, April 28, 2011 10:55:13 Tom Warren wrote:
>> Signed-off-by: Tom Warren <twarren@nvidia.com>
>> ---
>> This patch adds support for the SPIFLASH peripheral on Tegra2 (SPI 1).
>> Probe, erase, read and write are all supported, as well as low-level
>> SPI commands via 'sspi'.
>>
>> Note that, at this time, only Seaboard has a SPI flash part (Winbond).
>> With this code, I've written U-Boot to SPI from within U-Boot, and
>> booted the system from that SPI image (boot sel jumpers must be set
>> to SPI [1000], and the UART ENABLE jumper must be set to GPIO CTRL).
>
> so this is a driver for a SPI bus, and the board you're working on just
> happens to have some SPI flashes connected to it ?  then the "SPIFLASH" part
> is irrelevant, as is the board in question.  please remove references to both
> and just refer to this as "a SPI driver for Tegra2 processors".
No, there are 4 general purpose SPI interfaces in Tegra2 (SLINK), and
1 dedicated SPI FLASH controller. From the Tegra2 TRM:

"The Serial Flash interface (SFLASH) Controller interfaces Tegra 2 to
SPI-capable devices such as Flash
memories and ADC/DAC devices. Tegra 2 supports only master mode of SPI
operation on this interface.
This interface is specifically intended for serial flash and similar
devices. For a general purpose SPI
interface, use the SLINK serial interface."

The register sets are similar, but not identical.

As to the board reference, it's in my comments so people wishing to
use this will know that only Seaboard is available w/a SPI chip -
there's no provision for that on the Harmony board.

>
>> --- /dev/null
>> +++ b/arch/arm/include/asm/arch-tegra2/tegra2_spi.h
>
> if the only thing that uses this is the spi driver, then please put this in
> drivers/spi/tegra2_spi.h
Will do.
>
>> +#define      SPI_CMD_GO              (1 << 30)
>
> dont put a tab after the #define
>
OK. I'll fix 'em up.

>> --- a/arch/arm/lib/board.c
>> +++ b/arch/arm/lib/board.c
>
> this change needs to be split out into a dedicated one
>
>> -
>
> this new line should not be deleted
>
>> +#if defined(CONFIG_CMD_SPI)
>> +#include <spi.h>
>> +#endif
>
> no need to protect the inclusion.  just always include it.
OK.
>
>>  #endif
>> -
>>  #if defined(CONFIG_CMD_NAND)
>
> nor should this newline be deleted
>
>>       puts ("NAND:  ");
>>       nand_init();            /* go init the NAND */
>>  #endif
>> -
>> +#if defined(CONFIG_CMD_SPI)
>
> nor this newline
I'm going to remove these changes from arm/lib/board.c and move
spi_init() to our common board file (nvidia/common/board.c.

>
>> +int spi_claim_bus(struct spi_slave *slave)
>> +{
>> +     /* Move bulk of spi_init code here? */
>
> yes, so do that
>
OK, I'll look into that.

>> +void spi_release_bus(struct spi_slave *slave)
>> +{
>
> this func should be disabling the spi bus
OK, I'll look into that, too.

>
>> +void spi_cs_activate(struct spi_slave *slave)
>> +{
>> +     struct pmux_tri_ctlr *pmt = (struct pmux_tri_ctlr *)NV_PA_APB_MISC_BASE;
>> +     struct spi_tegra *spi = (struct spi_tegra *)TEGRA2_SPI_BASE;
>> +     u32 val;
>> +
>> +     /*
>> +      * Delay here to clean up comms - spurious chars seen around SPI xfers.
>> +      * Fine-tune later.
>> +      */
>> +     udelay(1000);
>
> fine tune now ?
The comment went in during development, and the 1000 values is the
final fine-tuned value (1ms). So I'll change the comment.

>
>> +     /*
>> +      * 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(UART_DISABLE_GPIO, 1);
>
> board specific issues shouldnt be in a processor driver.
Please explain what you mean by a processor driver.  This does need an
ifdef, so any future board that doesn't have Seaboard's defiency of a
muxed SPIFLASH/UART won't have to mess with a GPIO, but for Seaboard,
we have to dynamically change GPIO_PI3 every SPI transaction to ensure
UART spew can continue (in DEBUG builds, etc.).
>
>> +     int numBytes = (bitlen + 7) / 8;
>
> no camel case.  use "num_bytes".
Sure, will do. This came over from the driver I ported from.

>
>> +                                     for (i = 0; i < bytes; ++i) {
>> +                                             ((u8 *)din)[i] = (tmpdin >> 24);
>
> create a dedicated pointer and deference that.  casts on the lhs are poor
> style.
Again, this was in the driver source I used as a porting reference.
I'll change it.

>
>> --- a/include/configs/seaboard.h
>> +++ b/include/configs/seaboard.h
>
> this should be split out into a dedicated patch
OK.

Thanks,

Tom
> -mike
>
Mike Frysinger May 2, 2011, 9:26 p.m. UTC | #3
On Monday, May 02, 2011 11:33:24 Tom Warren wrote:
> On Fri, Apr 29, 2011 at 4:21 PM, Mike Frysinger wrote:
> > On Thursday, April 28, 2011 10:55:13 Tom Warren wrote:
> >> This patch adds support for the SPIFLASH peripheral on Tegra2 (SPI 1).
> >> Probe, erase, read and write are all supported, as well as low-level
> >> SPI commands via 'sspi'.
> >> 
> >> Note that, at this time, only Seaboard has a SPI flash part (Winbond).
> >> With this code, I've written U-Boot to SPI from within U-Boot, and
> >> booted the system from that SPI image (boot sel jumpers must be set
> >> to SPI [1000], and the UART ENABLE jumper must be set to GPIO CTRL).
> > 
> > so this is a driver for a SPI bus, and the board you're working on just
> > happens to have some SPI flashes connected to it ?  then the "SPIFLASH"
> > part is irrelevant, as is the board in question.  please remove
> > references to both and just refer to this as "a SPI driver for Tegra2
> > processors".
> 
> No, there are 4 general purpose SPI interfaces in Tegra2 (SLINK), and
> 1 dedicated SPI FLASH controller. From the Tegra2 TRM:
> 
> "The Serial Flash interface (SFLASH) Controller interfaces Tegra 2 to
> SPI-capable devices such as Flash
> memories and ADC/DAC devices. Tegra 2 supports only master mode of SPI
> operation on this interface.
> This interface is specifically intended for serial flash and similar
> devices. For a general purpose SPI
> interface, use the SLINK serial interface."
> 
> The register sets are similar, but not identical.

do you plan on writing a driver for the SLINK logic ?  would it be a new file, 
or would you keep the two integrated ?  if diff files, i'd name this one 
"tegra2_sflash_spi.c".  if you keep them merged, then "tegra2_spi.c" is fine.

> As to the board reference, it's in my comments so people wishing to
> use this will know that only Seaboard is available w/a SPI chip -
> there's no provision for that on the Harmony board.

generally, processor drivers are not concerned with board issues.  if you want 
to document board-specific issues somewhere, add a doc/README.<boardname>.

> >> --- a/arch/arm/lib/board.c
> >> +++ b/arch/arm/lib/board.c
> >> +#if defined(CONFIG_CMD_SPI)
> 
> I'm going to remove these changes from arm/lib/board.c and move
> spi_init() to our common board file (nvidia/common/board.c.

i dont think you want to do this.  other than the style changes, and needing a 
sep changeset, having the arm board.c file call spi_init() is desirable.

> >> +      * 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(UART_DISABLE_GPIO, 1);
> > 
> > board specific issues shouldnt be in a processor driver.
> 
> Please explain what you mean by a processor driver.

you're writing a driver for the Tegra2 SoC.  that is the only thing this 
driver should be doing.  that means no board-specific logic is allowed.

> This does need an
> ifdef, so any future board that doesn't have Seaboard's defiency of a
> muxed SPIFLASH/UART won't have to mess with a GPIO, but for Seaboard,
> we have to dynamically change GPIO_PI3 every SPI transaction to ensure
> UART spew can continue (in DEBUG builds, etc.).

right, this is a board-specific issue.  thus it doesnt belong in a driver that 
is only concerned with the Tegra2 SoC.

what you could do is:
void spi_init_common(void) { ... only tegra2 code ... }
void spi_init(void) __attribute__((weak, alias("spi_init_common")));

and then in the seaboard directory, add:
void spi_init(void)
{
	... do random gpio/portmux stuff ...
	spi_init_common();
}

or another style would be to:
extern void spi_board_init(void) __weak;
void spi_init(void)
{
	if (spi_board_init())
		spi_board_init();
	... only tegra2 code ...
}

and then in the seaboard directory, add:
void spi_board_init(void)
{
	... do random gpio/portmux stuff ...
}
-mike
Tom Warren May 2, 2011, 11:06 p.m. UTC | #4
Mike,

On Mon, May 2, 2011 at 2:26 PM, Mike Frysinger <vapier@gentoo.org> wrote:
> On Monday, May 02, 2011 11:33:24 Tom Warren wrote:
>> On Fri, Apr 29, 2011 at 4:21 PM, Mike Frysinger wrote:
>> > On Thursday, April 28, 2011 10:55:13 Tom Warren wrote:
>> >> This patch adds support for the SPIFLASH peripheral on Tegra2 (SPI 1).
>> >> Probe, erase, read and write are all supported, as well as low-level
>> >> SPI commands via 'sspi'.
>> >>
>> >> Note that, at this time, only Seaboard has a SPI flash part (Winbond).
>> >> With this code, I've written U-Boot to SPI from within U-Boot, and
>> >> booted the system from that SPI image (boot sel jumpers must be set
>> >> to SPI [1000], and the UART ENABLE jumper must be set to GPIO CTRL).
>> >
>> > so this is a driver for a SPI bus, and the board you're working on just
>> > happens to have some SPI flashes connected to it ?  then the "SPIFLASH"
>> > part is irrelevant, as is the board in question.  please remove
>> > references to both and just refer to this as "a SPI driver for Tegra2
>> > processors".
>>
>> No, there are 4 general purpose SPI interfaces in Tegra2 (SLINK), and
>> 1 dedicated SPI FLASH controller. From the Tegra2 TRM:
>>
>> "The Serial Flash interface (SFLASH) Controller interfaces Tegra 2 to
>> SPI-capable devices such as Flash
>> memories and ADC/DAC devices. Tegra 2 supports only master mode of SPI
>> operation on this interface.
>> This interface is specifically intended for serial flash and similar
>> devices. For a general purpose SPI
>> interface, use the SLINK serial interface."
>>
>> The register sets are similar, but not identical.
>
> do you plan on writing a driver for the SLINK logic ?  would it be a new file,
> or would you keep the two integrated ?  if diff files, i'd name this one
> "tegra2_sflash_spi.c".  if you keep them merged, then "tegra2_spi.c" is fine.
No plan to write SLINK drivers for U-Boot.  Seaboard has a touchscreen
on SPI1, but U-Boot doesn't care about that.
So I'll keep it tegra2_spi.c for now.

>
>> As to the board reference, it's in my comments so people wishing to
>> use this will know that only Seaboard is available w/a SPI chip -
>> there's no provision for that on the Harmony board.
>
> generally, processor drivers are not concerned with board issues.  if you want
> to document board-specific issues somewhere, add a doc/README.<boardname>.
>
Good idea. I'll gen one up for Seaboard.

>> >> --- a/arch/arm/lib/board.c
>> >> +++ b/arch/arm/lib/board.c
>> >> +#if defined(CONFIG_CMD_SPI)
>>
>> I'm going to remove these changes from arm/lib/board.c and move
>> spi_init() to our common board file (nvidia/common/board.c.
>
> i dont think you want to do this.  other than the style changes, and needing a
> sep changeset, having the arm board.c file call spi_init() is desirable.
OK. I'll fix up arm board.c with your comments in mind and put it in a
separate patch.

>
>> >> +      * 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(UART_DISABLE_GPIO, 1);
>> >
>> > board specific issues shouldnt be in a processor driver.
>>
>> Please explain what you mean by a processor driver.
>
> you're writing a driver for the Tegra2 SoC.  that is the only thing this
> driver should be doing.  that means no board-specific logic is allowed.
>
>> This does need an
>> ifdef, so any future board that doesn't have Seaboard's defiency of a
>> muxed SPIFLASH/UART won't have to mess with a GPIO, but for Seaboard,
>> we have to dynamically change GPIO_PI3 every SPI transaction to ensure
>> UART spew can continue (in DEBUG builds, etc.).
>
> right, this is a board-specific issue.  thus it doesnt belong in a driver that
> is only concerned with the Tegra2 SoC.
>
> what you could do is:
> void spi_init_common(void) { ... only tegra2 code ... }
> void spi_init(void) __attribute__((weak, alias("spi_init_common")));
>
> and then in the seaboard directory, add:
> void spi_init(void)
> {
>        ... do random gpio/portmux stuff ...
>        spi_init_common();
> }
>
> or another style would be to:
> extern void spi_board_init(void) __weak;
> void spi_init(void)
> {
>        if (spi_board_init())
>                spi_board_init();
>        ... only tegra2 code ...
> }
>
> and then in the seaboard directory, add:
> void spi_board_init(void)
> {
>        ... do random gpio/portmux stuff ...
> }

I'll try out these ideas. Thanks.

Tom
> -mike
>
Mike Frysinger May 2, 2011, 11:16 p.m. UTC | #5
On Monday, May 02, 2011 19:06:51 Tom Warren wrote:
> Seaboard has a touchscreen on SPI1, but U-Boot doesn't care about that.

fwiw, u-boot has splash screen support
-mike
Tom Warren May 3, 2011, 3:13 p.m. UTC | #6
On Mon, May 2, 2011 at 4:16 PM, Mike Frysinger <vapier@gentoo.org> wrote:
> On Monday, May 02, 2011 19:06:51 Tom Warren wrote:
>> Seaboard has a touchscreen on SPI1, but U-Boot doesn't care about that.
>
> fwiw, u-boot has splash screen support
> -mike
>
Thanks, Mike. I'm talking touchscreen support. We'll be submitting LCD
support for Tegra2 at a later date, so splash screens can be shown by
ODMs, etc.

Tom
Mike Frysinger May 3, 2011, 7:31 p.m. UTC | #7
On Tue, May 3, 2011 at 11:13, Tom Warren wrote:
> On Mon, May 2, 2011 at 4:16 PM, Mike Frysinger wrote:
>> On Monday, May 02, 2011 19:06:51 Tom Warren wrote:
>>> Seaboard has a touchscreen on SPI1, but U-Boot doesn't care about that.
>>
>> fwiw, u-boot has splash screen support
>
> Thanks, Mike. I'm talking touchscreen support. We'll be submitting LCD
> support for Tegra2 at a later date, so splash screens can be shown by
> ODMs, etc.

you could implement a simple console input driver for the touchscreen
(translate the gestures into up/down/enter/etc...), but you're
probably treading way into new territory for u-boot if you do :)
-mike
diff mbox

Patch

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..5ab2a2a
--- /dev/null
+++ b/arch/arm/include/asm/arch-tegra2/tegra2_spi.h
@@ -0,0 +1,79 @@ 
+/*
+ * 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 SWR_SPI1_RST		(1 << 11)
+#define CLK_ENB_SPI1		(1 << 11)
+#define GMD_SEL_SFLASH		(3 << 30)
+#define GMC_SEL_SFLASH		(3 << 2)
+#define Z_LSPI			(1 << 0)
+
+#endif	/* _TEGRA2_SPI_H_ */
diff --git a/arch/arm/lib/board.c b/arch/arm/lib/board.c
index dc46e21..feb2e6b 100644
--- a/arch/arm/lib/board.c
+++ b/arch/arm/lib/board.c
@@ -60,7 +60,9 @@ 
 #ifdef CONFIG_DRIVER_LAN91C96
 #include "../drivers/net/lan91c96.h"
 #endif
-
+#if defined(CONFIG_CMD_SPI)
+#include <spi.h>
+#endif
 DECLARE_GLOBAL_DATA_PTR;
 
 ulong monitor_flash_len;
@@ -506,12 +508,14 @@  void board_init_r (gd_t *id, ulong dest_addr)
 		hang ();
 	}
 #endif
-
 #if defined(CONFIG_CMD_NAND)
 	puts ("NAND:  ");
 	nand_init();		/* go init the NAND */
 #endif
-
+#if defined(CONFIG_CMD_SPI)
+	puts("SPI: ");
+	spi_init();
+#endif
 #if defined(CONFIG_CMD_ONENAND)
 	onenand_init();
 #endif
diff --git a/drivers/spi/Makefile b/drivers/spi/Makefile
index d582fbb..c85a131 100644
--- a/drivers/spi/Makefile
+++ b/drivers/spi/Makefile
@@ -38,6 +38,7 @@  COBJS-$(CONFIG_MXC_SPI) += mxc_spi.o
 COBJS-$(CONFIG_OMAP3_SPI) += omap3_spi.o
 COBJS-$(CONFIG_SOFT_SPI) += soft_spi.o
 COBJS-$(CONFIG_SH_SPI) += sh_spi.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..6101f1f
--- /dev/null
+++ b/drivers/spi/tegra2_spi.c
@@ -0,0 +1,335 @@ 
+/*
+ * 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/arch/clk_rst.h>
+#include <asm/arch/gpio.h>
+#include <asm/arch/pinmux.h>
+#include <asm/arch/tegra2_spi.h>
+
+#define SPI_TIMEOUT	1000
+
+/* On Seaboard: GPIO_PI3 = Port I = 8, bit = 3 */
+#define UART_DISABLE_GPIO	GPIO_PI3
+
+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 clk_rst_ctlr *clkrst = (struct clk_rst_ctlr *)NV_PA_CLK_RST_BASE;
+	struct pmux_tri_ctlr *pmt = (struct pmux_tri_ctlr *)NV_PA_APB_MISC_BASE;
+	struct spi_tegra *spi = (struct spi_tegra *)TEGRA2_SPI_BASE;
+	u32 reg;
+
+	/* Enable UART via GPIO_PI3 so serial console works */
+	gpio_direction_output(UART_DISABLE_GPIO, 0);
+
+	/*
+	 * SPI reset/clocks init - reset SPI, set clocks, release from reset
+	 */
+	reg = readl(&clkrst->crc_rst_dev_h);
+	reg |= SWR_SPI1_RST;		/* Reset SPI1 ctlr */
+	writel(reg, &clkrst->crc_rst_dev_h);
+	debug("spi_init: ClkRst = %08x\n", reg);
+
+	reg = readl(&clkrst->crc_clk_out_enb_h);
+	reg |= CLK_ENB_SPI1;		/* Enable clock to SPI1 ctlr */
+	writel(reg, &clkrst->crc_clk_out_enb_h);
+	debug("spi_init: ClkEnable = %08x\n", reg);
+
+	/* Change SPI clock to 24MHz, PLLP_OUT0 source */
+	reg = readl(&clkrst->crc_clk_src_spi1);
+	reg &= 0x3FFFFF00;		/* src = PLLP_OUT0 */
+	reg |= ((9-1) << 1);		/* div = 9 in 7.1 format */
+	writel(reg, &clkrst->crc_clk_src_spi1);
+	debug("spi_init: ClkSrc = %08x\n", reg);
+
+	/* wait for 2us */
+	udelay(2);
+
+	reg = readl(&clkrst->crc_rst_dev_h);
+	reg &= ~SWR_SPI1_RST;		/* Release SPI1 ctlr  reset */
+	writel(reg, &clkrst->crc_rst_dev_h);
+	debug("spi_init: Final ClkRst = %08x\n", reg);
+
+	/* 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 last due to UART issue
+	 */
+
+	reg = readl(&pmt->pmt_ctl_c);
+	reg |= GMD_SEL_SFLASH;		/* GMD_SEL [31:30] = (3) SFLASH */
+	writel(reg, &pmt->pmt_ctl_c);
+	debug("spi_init: PinMuxRegC = %08x\n", reg);
+
+	reg = readl(&pmt->pmt_tri_d);
+	reg &= ~Z_LSPI;		/* Z_LSPI = normal (0) */
+	writel(reg, &pmt->pmt_tri_d);
+	debug("spi_init: TriStateReg = %08x\n", reg);
+
+	/*
+	 * NOTE:
+	 * Don't set PinMux bits 3:2 to SPI here or subsequent UART data
+	 * won't go out! It'll be correctly set in the actual SPI driver
+	 * before/after any transactions (cs_activate/_deactivate).
+	 */
+}
+
+int spi_claim_bus(struct spi_slave *slave)
+{
+	/* Move bulk of spi_init code here? */
+	return 0;
+}
+
+void spi_release_bus(struct spi_slave *slave)
+{
+	/* Release UART_DISABLE and set pinmux to UART4 here? */
+}
+
+void spi_cs_activate(struct spi_slave *slave)
+{
+	struct pmux_tri_ctlr *pmt = (struct pmux_tri_ctlr *)NV_PA_APB_MISC_BASE;
+	struct spi_tegra *spi = (struct spi_tegra *)TEGRA2_SPI_BASE;
+	u32 val;
+
+	/*
+	 * Delay here to clean up comms - spurious chars seen around SPI xfers.
+	 * Fine-tune later.
+	 */
+	udelay(1000);
+
+	/* We need to dynamically change the pinmux, shared w/UART RXD/CTS */
+	val = readl(&pmt->pmt_ctl_b);
+	val |= GMC_SEL_SFLASH;		/* GMC_SEL [3:2] = (3) SFLASH */
+	writel(val, &pmt->pmt_ctl_b);
+
+	/*
+	 * 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(UART_DISABLE_GPIO, 1);
+
+	/* 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 pmux_tri_ctlr *pmt = (struct pmux_tri_ctlr *)NV_PA_APB_MISC_BASE;
+	struct spi_tegra *spi = (struct spi_tegra *)TEGRA2_SPI_BASE;
+	u32 val;
+
+	/*
+	 * Delay here to clean up comms - spurious chars seen around SPI xfers.
+	 * Fine-tune later.
+	 */
+	udelay(1000);
+
+	/* We need to dynamically change the pinmux, shared w/UART RXD/CTS */
+	val = readl(&pmt->pmt_ctl_b);
+	val &= ~GMC_SEL_SFLASH;		/* GMC_SEL [3:2] = (0) UARTD */
+	writel(val, &pmt->pmt_ctl_b);
+
+	/*
+	 * On Seaboard, MOSI/MISO are shared w/UART.
+	 * GPIO I3 (UART_DISABLE) is used to tristate UART in cs_activate.
+	 * Enable UART here by setting that GPIO to 0 so we can do U-Boot comms.
+	 */
+	gpio_direction_output(UART_DISABLE_GPIO, 0);
+
+	/* 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 numBytes = (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 (numBytes > 0) {
+		tmpdout = 0;
+		bytes = (numBytes >  4) ?  4 : numBytes;
+		bits  = (bitlen   > 32) ? 32 : bitlen;
+
+		if (dout != NULL) {
+			for (i = 0; i < bytes; ++i)
+				tmpdout = (tmpdout << 8) | ((u8 *) dout)[i];
+		}
+
+		numBytes -= bytes;
+		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 = 0; i < bytes; ++i) {
+						((u8 *)din)[i] = (tmpdin >> 24);
+						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;
+}
diff --git a/include/configs/seaboard.h b/include/configs/seaboard.h
index 4e8ef07..1b3a022 100644
--- a/include/configs/seaboard.h
+++ b/include/configs/seaboard.h
@@ -42,6 +42,16 @@ 
 
 #define CONFIG_BOARD_EARLY_INIT_F
 
+/* GPIO */
 #define CONFIG_TEGRA2_GPIO
 #define CONFIG_CMD_TEGRA2_GPIO_INFO
+
+/* 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
+
 #endif /* __CONFIG_H */