blob: 3acc6b7c0727cc5bc185187152e1c11c5d5783b3 [file] [log] [blame]
Linus Torvalds1da177e2005-04-16 15:20:36 -07001/*
2 * arch/ppc/boot/simple/mpc52xx_tty.c
3 *
4 * Minimal serial functions needed to send messages out a MPC52xx
5 * Programmable Serial Controller (PSC).
6 *
7 * Author: Dale Farnsworth <dfarnsworth@mvista.com>
8 *
9 * 2003-2004 (c) MontaVista, Software, Inc. This file is licensed under the
10 * terms of the GNU General Public License version 2. This program is licensed
11 * "as is" without any warranty of any kind, whether express or implied.
12 */
13
14#include <linux/config.h>
15#include <linux/types.h>
16#include <asm/uaccess.h>
17#include <asm/mpc52xx.h>
18#include <asm/mpc52xx_psc.h>
19#include <asm/serial.h>
20#include <asm/io.h>
21#include <asm/time.h>
22
23
24#ifdef MPC52xx_PF_CONSOLE_PORT
25#define MPC52xx_CONSOLE MPC52xx_PSCx_OFFSET(MPC52xx_PF_CONSOLE_PORT)
26#define MPC52xx_PSC_CONFIG_SHIFT ((MPC52xx_PF_CONSOLE_PORT-1)<<2)
27#else
28#error "MPC52xx_PF_CONSOLE_PORT not defined"
29#endif
30
31static struct mpc52xx_psc __iomem *psc =
32 (struct mpc52xx_psc __iomem *) MPC52xx_PA(MPC52xx_CONSOLE);
33
34/* The decrementer counts at the system bus clock frequency
35 * divided by four. The most accurate time base is connected to the
36 * rtc. We read the decrementer change during one rtc tick
37 * and multiply by 4 to get the system bus clock frequency. Since a
38 * rtc tick is one seconds, and that's pretty long, we change the rtc
39 * dividers temporarly to set them 64x faster ;)
40 */
41static int
42mpc52xx_ipbfreq(void)
43{
44 struct mpc52xx_rtc __iomem *rtc =
45 (struct mpc52xx_rtc __iomem *) MPC52xx_PA(MPC52xx_RTC_OFFSET);
46 struct mpc52xx_cdm __iomem *cdm =
47 (struct mpc52xx_cdm __iomem *) MPC52xx_PA(MPC52xx_CDM_OFFSET);
48 int current_time, previous_time;
49 int tbl_start, tbl_end;
50 int xlbfreq, ipbfreq;
51
52 out_be32(&rtc->dividers, 0x8f1f0000); /* Set RTC 64x faster */
53 previous_time = in_be32(&rtc->time);
54 while ((current_time = in_be32(&rtc->time)) == previous_time) ;
55 tbl_start = get_tbl();
56 previous_time = current_time;
57 while ((current_time = in_be32(&rtc->time)) == previous_time) ;
58 tbl_end = get_tbl();
59 out_be32(&rtc->dividers, 0xffff0000); /* Restore RTC */
60
61 xlbfreq = (tbl_end - tbl_start) << 8;
62 ipbfreq = (in_8(&cdm->ipb_clk_sel) & 1) ? xlbfreq / 2 : xlbfreq;
63
64 return ipbfreq;
65}
66
67unsigned long
68serial_init(int ignored, void *ignored2)
69{
70 struct mpc52xx_gpio __iomem *gpio =
71 (struct mpc52xx_gpio __iomem *) MPC52xx_PA(MPC52xx_GPIO_OFFSET);
72 int divisor;
73 int mode1;
74 int mode2;
75 u32 val32;
76
77 static int been_here = 0;
78
79 if (been_here)
80 return 0;
81
82 been_here = 1;
83
84 val32 = in_be32(&gpio->port_config);
85 val32 &= ~(0x7 << MPC52xx_PSC_CONFIG_SHIFT);
86 val32 |= MPC52xx_GPIO_PSC_CONFIG_UART_WITHOUT_CD
87 << MPC52xx_PSC_CONFIG_SHIFT;
88 out_be32(&gpio->port_config, val32);
89
90 out_8(&psc->command, MPC52xx_PSC_RST_TX
91 | MPC52xx_PSC_RX_DISABLE | MPC52xx_PSC_TX_ENABLE);
92 out_8(&psc->command, MPC52xx_PSC_RST_RX);
93
94 out_be32(&psc->sicr, 0x0);
95 out_be16(&psc->mpc52xx_psc_clock_select, 0xdd00);
96 out_be16(&psc->tfalarm, 0xf8);
97
98 out_8(&psc->command, MPC52xx_PSC_SEL_MODE_REG_1
99 | MPC52xx_PSC_RX_ENABLE
100 | MPC52xx_PSC_TX_ENABLE);
101
102 divisor = ((mpc52xx_ipbfreq()
103 / (CONFIG_SERIAL_MPC52xx_CONSOLE_BAUD * 16)) + 1) >> 1;
104
105 mode1 = MPC52xx_PSC_MODE_8_BITS | MPC52xx_PSC_MODE_PARNONE
106 | MPC52xx_PSC_MODE_ERR;
107 mode2 = MPC52xx_PSC_MODE_ONE_STOP;
108
109 out_8(&psc->ctur, divisor>>8);
110 out_8(&psc->ctlr, divisor);
111 out_8(&psc->command, MPC52xx_PSC_SEL_MODE_REG_1);
112 out_8(&psc->mode, mode1);
113 out_8(&psc->mode, mode2);
114
115 return 0; /* ignored */
116}
117
118void
119serial_putc(void *ignored, const char c)
120{
121 serial_init(0, NULL);
122
123 while (!(in_be16(&psc->mpc52xx_psc_status) & MPC52xx_PSC_SR_TXEMP)) ;
124 out_8(&psc->mpc52xx_psc_buffer_8, c);
125 while (!(in_be16(&psc->mpc52xx_psc_status) & MPC52xx_PSC_SR_TXEMP)) ;
126}
127
128char
129serial_getc(void *ignored)
130{
131 while (!(in_be16(&psc->mpc52xx_psc_status) & MPC52xx_PSC_SR_RXRDY)) ;
132
133 return in_8(&psc->mpc52xx_psc_buffer_8);
134}
135
136int
137serial_tstc(void *ignored)
138{
139 return (in_be16(&psc->mpc52xx_psc_status) & MPC52xx_PSC_SR_RXRDY) != 0;
140}