summaryrefslogtreecommitdiff
path: root/board/nvidia/common/uart-spi-switch.c
blob: 23aa0b9d19d8f93f711bf029cd4053a2fc7f5e9c (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
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);
}