| /* |
| * arch/ppc/boot/simple/mpc52xx_tty.c |
| * |
| * 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/config.h> |
| #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; |
| } |