aboutsummaryrefslogtreecommitdiffstats
path: root/arch/ppc/boot/simple/mpc52xx_tty.c
blob: 7b5924cc3c026eaed2f4d8d91289d58e2c44d29c (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
/*
 * Minimal serial functions needed to send messages out a MPC52xx
 * Programmable Serial Controller (PSC).
 *
 * Author: Dale Farnsworth <dfarnsworth@mvista.com>
 *
 * 2003-2004 (c) MontaVista, Software, Inc.  This file is licensed under the
 * terms of the GNU General Public License version 2.  This program is licensed
 * "as is" without any warranty of any kind, whether express or implied.
 */

#include <linux/types.h>
#include <asm/uaccess.h>
#include <asm/mpc52xx.h>
#include <asm/mpc52xx_psc.h>
#include <asm/serial.h>
#include <asm/io.h>
#include <asm/time.h>


#ifdef MPC52xx_PF_CONSOLE_PORT
#define MPC52xx_CONSOLE MPC52xx_PSCx_OFFSET(MPC52xx_PF_CONSOLE_PORT)
#define MPC52xx_PSC_CONFIG_SHIFT ((MPC52xx_PF_CONSOLE_PORT-1)<<2)
#else
#error "MPC52xx_PF_CONSOLE_PORT not defined"
#endif

static struct mpc52xx_psc __iomem *psc =
	(struct mpc52xx_psc __iomem *) MPC52xx_PA(MPC52xx_CONSOLE);

/* The decrementer counts at the system bus clock frequency
 * divided by four.  The most accurate time base is connected to the
 * rtc.  We read the decrementer change during one rtc tick
 * and multiply by 4 to get the system bus clock frequency. Since a
 * rtc tick is one seconds, and that's pretty long, we change the rtc
 * dividers temporarly to set them 64x faster ;)
 */
static int
mpc52xx_ipbfreq(void)
{
	struct mpc52xx_rtc __iomem *rtc =
		(struct mpc52xx_rtc __iomem *) MPC52xx_PA(MPC52xx_RTC_OFFSET);
	struct mpc52xx_cdm __iomem *cdm =
		(struct mpc52xx_cdm __iomem *) MPC52xx_PA(MPC52xx_CDM_OFFSET);
	int current_time, previous_time;
	int tbl_start, tbl_end;
	int xlbfreq, ipbfreq;

	out_be32(&rtc->dividers, 0x8f1f0000);	/* Set RTC 64x faster */
	previous_time = in_be32(&rtc->time);
	while ((current_time = in_be32(&rtc->time)) == previous_time) ;
	tbl_start = get_tbl();
	previous_time = current_time;
	while ((current_time = in_be32(&rtc->time)) == previous_time) ;
	tbl_end = get_tbl();
	out_be32(&rtc->dividers, 0xffff0000);   /* Restore RTC */

	xlbfreq = (tbl_end - tbl_start) << 8;
	ipbfreq = (in_8(&cdm->ipb_clk_sel) & 1) ? xlbfreq / 2 : xlbfreq;

	return ipbfreq;
}

unsigned long
serial_init(int ignored, void *ignored2)
{
	struct mpc52xx_gpio __iomem *gpio =
		(struct mpc52xx_gpio __iomem *) MPC52xx_PA(MPC52xx_GPIO_OFFSET);
	int divisor;
	int mode1;
	int mode2;
	u32 val32;

	static int been_here = 0;

	if (been_here)
		return 0;

	been_here = 1;

	val32 = in_be32(&gpio->port_config);
	val32 &= ~(0x7 << MPC52xx_PSC_CONFIG_SHIFT);
	val32 |= MPC52xx_GPIO_PSC_CONFIG_UART_WITHOUT_CD
				<< MPC52xx_PSC_CONFIG_SHIFT;
	out_be32(&gpio->port_config, val32);

	out_8(&psc->command, MPC52xx_PSC_RST_TX
			| MPC52xx_PSC_RX_DISABLE | MPC52xx_PSC_TX_ENABLE);
	out_8(&psc->command, MPC52xx_PSC_RST_RX);

	out_be32(&psc->sicr, 0x0);
	out_be16(&psc->mpc52xx_psc_clock_select, 0xdd00);
	out_be16(&psc->tfalarm, 0xf8);

	out_8(&psc->command, MPC52xx_PSC_SEL_MODE_REG_1
			| MPC52xx_PSC_RX_ENABLE
			| MPC52xx_PSC_TX_ENABLE);

	divisor = ((mpc52xx_ipbfreq()
			/ (CONFIG_SERIAL_MPC52xx_CONSOLE_BAUD * 16)) + 1) >> 1;

	mode1 = MPC52xx_PSC_MODE_8_BITS | MPC52xx_PSC_MODE_PARNONE
			| MPC52xx_PSC_MODE_ERR;
	mode2 = MPC52xx_PSC_MODE_ONE_STOP;

	out_8(&psc->ctur, divisor>>8);
	out_8(&psc->ctlr, divisor);
	out_8(&psc->command, MPC52xx_PSC_SEL_MODE_REG_1);
	out_8(&psc->mode, mode1);
	out_8(&psc->mode, mode2);

	return 0;	/* ignored */
}

void
serial_putc(void *ignored, const char c)
{
	serial_init(0, NULL);

	while (!(in_be16(&psc->mpc52xx_psc_status) & MPC52xx_PSC_SR_TXEMP)) ;
	out_8(&psc->mpc52xx_psc_buffer_8, c);
	while (!(in_be16(&psc->mpc52xx_psc_status) & MPC52xx_PSC_SR_TXEMP)) ;
}

char
serial_getc(void *ignored)
{
	while (!(in_be16(&psc->mpc52xx_psc_status) & MPC52xx_PSC_SR_RXRDY)) ;

	return in_8(&psc->mpc52xx_psc_buffer_8);
}

int
serial_tstc(void *ignored)
{
	return (in_be16(&psc->mpc52xx_psc_status) & MPC52xx_PSC_SR_RXRDY) != 0;
}