| /* |
| * Copyright (c) 2014 Qualcomm Atheros, Inc. |
| * |
| * See file CREDITS for list of people who contributed to this |
| * project. |
| * |
| * This program is free software; you can redistribute it and/or |
| * modify it under the terms of the GNU General Public License as |
| * published by the Free Software Foundation; either version 2 of |
| * the License, or (at your option) any later version. |
| * |
| * This program is distributed in the hope that it will be useful, |
| * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| * GNU General Public License for more details. |
| * |
| * You should have received a copy of the GNU General Public License |
| * along with this program; if not, write to the Free Software |
| * Foundation, Inc., 59 Temple Place, Suite 330, Boston, |
| * MA 02111-1307 USA |
| */ |
| |
| #include <asm/addrspace.h> |
| #include <asm/types.h> |
| #include <config.h> |
| #include <hornet_soc.h> |
| |
| #define uart_reg_read(x) ar7240_reg_rd( (AR7240_UART_BASE+x) ) |
| #define uart_reg_write(x, y) ar7240_reg_wr( (AR7240_UART_BASE+x), y) |
| |
| static int |
| AthrUartGet(char *__ch_data) |
| { |
| u32 rdata; |
| |
| rdata = uart_reg_read(UARTDATA_ADDRESS); |
| |
| if (UARTDATA_UARTRXCSR_GET(rdata)) { |
| *__ch_data = (char)UARTDATA_UARTTXRXDATA_GET(rdata); |
| rdata = UARTDATA_UARTRXCSR_SET(1); |
| uart_reg_write(UARTDATA_ADDRESS, rdata); |
| return 1; |
| } |
| else { |
| return 0; |
| } |
| } |
| |
| static void |
| AthrUartPut(char __ch_data) |
| { |
| u32 rdata; |
| |
| do { |
| rdata = uart_reg_read(UARTDATA_ADDRESS); |
| } while (UARTDATA_UARTTXCSR_GET(rdata) == 0); |
| |
| rdata = UARTDATA_UARTTXRXDATA_SET((u32)__ch_data); |
| rdata |= UARTDATA_UARTTXCSR_SET(1); |
| |
| uart_reg_write(UARTDATA_ADDRESS, rdata); |
| } |
| |
| void |
| ar7240_sys_frequency(u32 *cpu_freq, u32 *ddr_freq, u32 *ahb_freq) |
| { |
| #ifdef CONFIG_HORNET_EMU |
| #ifdef CONFIG_HORNET_EMU_HARDI_WLAN |
| *cpu_freq = 48 * 1000000; |
| *ddr_freq = 48 * 1000000; |
| *ahb_freq = 24 * 1000000; |
| #else |
| *cpu_freq = 80 * 1000000; |
| *ddr_freq = 80 * 1000000; |
| *ahb_freq = 40 * 1000000; |
| #endif |
| #else |
| u32 ref_clock_rate, pll_freq; |
| u32 pllreg, clockreg; |
| u32 nint, refdiv, outdiv; |
| u32 cpu_div, ahb_div, ddr_div; |
| |
| if ( ar7240_reg_rd(HORNET_BOOTSTRAP_STATUS) & HORNET_BOOTSTRAP_SEL_25M_40M_MASK ) |
| ref_clock_rate = 40 * 1000000; |
| else |
| ref_clock_rate = 25 * 1000000; |
| |
| pllreg = ar7240_reg_rd(AR7240_CPU_PLL_CONFIG); |
| clockreg = ar7240_reg_rd(AR7240_CPU_CLOCK_CONTROL); |
| |
| if (clockreg & HORNET_CLOCK_CONTROL_BYPASS_MASK) { |
| /* Bypass PLL */ |
| pll_freq = ref_clock_rate; |
| cpu_div = ahb_div = ddr_div = 1; |
| } |
| else { |
| nint = (pllreg & HORNET_PLL_CONFIG_NINT_MASK) >> HORNET_PLL_CONFIG_NINT_SHIFT; |
| refdiv = (pllreg & HORNET_PLL_CONFIG_REFDIV_MASK) >> HORNET_PLL_CONFIG_REFDIV_SHIFT; |
| outdiv = (pllreg & HORNET_PLL_CONFIG_OUTDIV_MASK) >> HORNET_PLL_CONFIG_OUTDIV_SHIFT; |
| |
| pll_freq = (ref_clock_rate / refdiv) * nint; |
| |
| if (outdiv == 1) |
| pll_freq /= 2; |
| else if (outdiv == 2) |
| pll_freq /= 4; |
| else if (outdiv == 3) |
| pll_freq /= 8; |
| else if (outdiv == 4) |
| pll_freq /= 16; |
| else if (outdiv == 5) |
| pll_freq /= 32; |
| else if (outdiv == 6) |
| pll_freq /= 64; |
| else if (outdiv == 7) |
| pll_freq /= 128; |
| else /* outdiv == 0 --> illegal value */ |
| pll_freq /= 2; |
| |
| cpu_div = (clockreg & HORNET_CLOCK_CONTROL_CPU_POST_DIV_MASK) >> HORNET_CLOCK_CONTROL_CPU_POST_DIV_SHIFT; |
| ddr_div = (clockreg & HORNET_CLOCK_CONTROL_DDR_POST_DIV_MASK) >> HORNET_CLOCK_CONTROL_DDR_POST_DIV_SFIFT; |
| ahb_div = (clockreg & HORNET_CLOCK_CONTROL_AHB_POST_DIV_MASK) >> HORNET_CLOCK_CONTROL_AHB_POST_DIV_SFIFT; |
| |
| /* |
| * b00 : div by 1, b01 : div by 2, b10 : div by 3, b11 : div by 4 |
| */ |
| cpu_div++; |
| ddr_div++; |
| ahb_div++; |
| } |
| |
| *cpu_freq = pll_freq / cpu_div; |
| *ddr_freq = pll_freq / ddr_div; |
| *ahb_freq = pll_freq / ahb_div; |
| #endif |
| } |
| |
| int serial_init(void) |
| { |
| u32 rdata; |
| u32 baudRateDivisor, clock_step; |
| u32 fcEnable = 0; |
| u32 ahb_freq, ddr_freq, cpu_freq; |
| |
| ar7240_sys_frequency(&cpu_freq, &ddr_freq, &ahb_freq); |
| |
| /* GPIO Configuration */ |
| ar7240_reg_wr(AR7240_GPIO_OE, 0xcff); |
| rdata = ar7240_reg_rd(AR7240_GPIO_OUT); |
| rdata |= 0x400; // GPIO 10 (UART_SOUT) must output 1 |
| ar7240_reg_wr(AR7240_GPIO_OUT, rdata); |
| |
| rdata = ar7240_reg_rd(AR7240_GPIO_FUNC); |
| /* GPIO_FUN, bit1/UART_EN, bit2/UART_RTS_CTS_EN, bit15(disable_s26_uart) */ |
| rdata |= (0x3<<1)|(0x1<<15); |
| ar7240_reg_wr(AR7240_GPIO_FUNC, rdata); |
| |
| /* Get reference clock rate, then set baud rate to 115200 */ |
| #ifndef CONFIG_HORNET_EMU |
| |
| rdata = ar7240_reg_rd(HORNET_BOOTSTRAP_STATUS); |
| rdata &= HORNET_BOOTSTRAP_SEL_25M_40M_MASK; |
| |
| if (rdata) |
| baudRateDivisor = ( 40000000 / (16*115200) ) - 1; // 40 MHz clock is taken as UART clock |
| else |
| baudRateDivisor = ( 25000000 / (16*115200) ) - 1; // 25 MHz clock is taken as UART clock |
| #else |
| baudRateDivisor = ( ahb_freq / (16*115200) ) - 1; // 40 MHz clock is taken as UART clock |
| #endif |
| |
| clock_step = 8192; |
| |
| rdata = UARTCLOCK_UARTCLOCKSCALE_SET(baudRateDivisor) | UARTCLOCK_UARTCLOCKSTEP_SET(clock_step); |
| uart_reg_write(UARTCLOCK_ADDRESS, rdata); |
| |
| /* Config Uart Controller */ |
| #if 1 /* No interrupt */ |
| rdata = UARTCS_UARTDMAEN_SET(0) | UARTCS_UARTHOSTINTEN_SET(0) | UARTCS_UARTHOSTINT_SET(0) |
| | UARTCS_UARTSERIATXREADY_SET(0) | UARTCS_UARTTXREADYORIDE_SET(~fcEnable) |
| | UARTCS_UARTRXREADYORIDE_SET(~fcEnable) | UARTCS_UARTHOSTINTEN_SET(0); |
| #else |
| rdata = UARTCS_UARTDMAEN_SET(0) | UARTCS_UARTHOSTINTEN_SET(0) | UARTCS_UARTHOSTINT_SET(0) |
| | UARTCS_UARTSERIATXREADY_SET(0) | UARTCS_UARTTXREADYORIDE_SET(~fcEnable) |
| | UARTCS_UARTRXREADYORIDE_SET(~fcEnable) | UARTCS_UARTHOSTINTEN_SET(1); |
| #endif |
| |
| /* is_dte == 1 */ |
| rdata = rdata | UARTCS_UARTINTERFACEMODE_SET(2); |
| |
| if (fcEnable) { |
| rdata = rdata | UARTCS_UARTFLOWCONTROLMODE_SET(2); |
| } |
| |
| /* invert_fc ==0 (Inverted Flow Control) */ |
| //rdata = rdata | UARTCS_UARTFLOWCONTROLMODE_SET(3); |
| |
| /* parityEnable == 0 */ |
| //rdata = rdata | UARTCS_UARTPARITYMODE_SET(2); -->Parity Odd |
| //rdata = rdata | UARTCS_UARTPARITYMODE_SET(3); -->Parity Even |
| uart_reg_write(UARTCS_ADDRESS, rdata); |
| |
| return 0; |
| } |
| |
| int serial_tstc (void) |
| { |
| return (UARTDATA_UARTRXCSR_GET(uart_reg_read(UARTDATA_ADDRESS))); |
| } |
| |
| u8 serial_getc(void) |
| { |
| char ch_data; |
| |
| while (!AthrUartGet(&ch_data)) ; |
| |
| return (u8)ch_data; |
| } |
| |
| |
| void serial_putc(u8 byte) |
| { |
| if (byte == '\n') AthrUartPut('\r'); |
| |
| AthrUartPut((char)byte); |
| } |
| |
| void serial_setbrg (void) |
| { |
| } |
| |
| void serial_puts (const char *s) |
| { |
| while (*s) |
| { |
| serial_putc (*s++); |
| } |
| } |