| /* | |
| * 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; | |
| } |