/* * arch/ppc/boot/simple/mpc52xx_tty.c * * Minimal serial functions needed to send messages out a MPC52xx * Programmable Serial Controller (PSC). * * Author: Dale Farnsworth * * 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 #include #include #include #include #include #include #include #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; }