| /* |
| * Copyright (c) 2013 Qualcomm Atheros, Inc. |
| * |
| * 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 <linux/module.h> |
| #include <linux/kernel.h> |
| #include <asm/byteorder.h> |
| #include <linux/init.h> |
| #include <linux/errno.h> |
| #include <linux/delay.h> |
| #include <linux/timer.h> |
| #include <linux/interrupt.h> |
| #include <linux/dma-mapping.h> |
| #include <linux/bitops.h> |
| #include <asm/irq.h> |
| #include <asm/io.h> |
| |
| #include <atheros.h> |
| |
| void UartInit(void); |
| void UartPut(u8 byte); |
| |
| void ath_dispatch_wlan_intr(void) |
| { |
| do_IRQ(ATH_CPU_IRQ_WLAN); |
| } |
| |
| static void ath_sys_frequency(void) |
| { |
| #ifdef CONFIG_ATH_EMULATION |
| #ifdef CONFIG_HORNET_EMULATION_WLAN_HARDI /* FPGA WLAN emulation */ |
| ath_cpu_freq = 48000000; |
| ath_ddr_freq = 48000000; |
| ath_ahb_freq = 24000000; |
| #else |
| ath_cpu_freq = 80000000; |
| ath_ddr_freq = 80000000; |
| ath_ahb_freq = 40000000; |
| #endif |
| #else |
| /* Hornet's PLL is completely different from Python's */ |
| u32 ref_clock_rate, pll_freq; |
| u32 pllreg, clockreg; |
| u32 nint, refdiv, outdiv; |
| u32 cpu_div, ahb_div, ddr_div; |
| |
| if (ath_reg_rd(HORNET_BOOTSTRAP_STATUS) & |
| HORNET_BOOTSTRAP_SEL_25M_40M_MASK) |
| ref_clock_rate = 40 * 1000000; |
| else |
| ref_clock_rate = 25 * 1000000; |
| |
| pllreg = ath_reg_rd(ATH_CPU_PLL_CONFIG); |
| clockreg = ath_reg_rd(ATH_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++; |
| } |
| |
| ath_cpu_freq = pll_freq / cpu_div; |
| ath_ddr_freq = pll_freq / ddr_div; |
| ath_ahb_freq = pll_freq / ahb_div; |
| #endif |
| } |
| |
| |
| void UartInit(void) |
| { |
| unsigned int rdata; |
| unsigned int baudRateDivisor, clock_step; |
| unsigned int fcEnable = 0; |
| |
| ath_sys_frequency(); |
| |
| MY_WRITE(0xb8040000, 0xcff); |
| MY_WRITE(0xb8040008, 0x3b); |
| /* Enable UART , SPI and Disable S26 UART */ |
| MY_WRITE(0xb8040028, (ath_reg_rd(0xb8040028) | 0x48002)); |
| |
| MY_WRITE(0xb8040008, 0x2f); |
| |
| #ifdef CONFIG_HORNET_EMULATION |
| baudRateDivisor = (ath_ahb_freq / (16 * ATH_CONSOLE_BAUD)) - 1; // 24 MHz clock is taken as UART clock |
| #else |
| |
| rdata = ath_reg_rd(HORNET_BOOTSTRAP_STATUS); |
| rdata &= HORNET_BOOTSTRAP_SEL_25M_40M_MASK; |
| |
| if (rdata) |
| baudRateDivisor = (40000000 / (16 * ATH_CONSOLE_BAUD)) - 1; // 40 MHz clock is taken as UART clock |
| else |
| baudRateDivisor = (25000000 / (16 * ATH_CONSOLE_BAUD)) - 1; // 25 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); |
| |
| serial_inited = 1; |
| } |
| |
| u8 UartGetPoll(void) |
| { |
| u8 ret_val; |
| unsigned int rdata; |
| |
| do { |
| rdata = uart_reg_read(UARTDATA_ADDRESS); |
| } while (!UARTDATA_UARTRXCSR_GET(rdata)); |
| |
| ret_val = (u8) UARTDATA_UARTTXRXDATA_GET(rdata); |
| rdata = UARTDATA_UARTRXCSR_SET(1); |
| uart_reg_write(UARTDATA_ADDRESS, rdata); |
| |
| return ret_val; |
| } |
| |
| void UartPut(u8 byte) |
| { |
| unsigned int rdata; |
| |
| if (!serial_inited) { |
| serial_inited = 1; |
| UartInit(); |
| } |
| |
| do { |
| rdata = uart_reg_read(UARTDATA_ADDRESS); |
| } while (UARTDATA_UARTTXCSR_GET(rdata) == 0); |
| |
| rdata = UARTDATA_UARTTXRXDATA_SET((unsigned int)byte); |
| rdata |= UARTDATA_UARTTXCSR_SET(1); |
| |
| uart_reg_write(UARTDATA_ADDRESS, rdata); |
| } |
| |
| /* |
| * EHCI (USB full speed host controller) |
| */ |
| static struct resource ath_usb_ehci_resources[] = { |
| [0] = { |
| .start = ATH_USB_EHCI_BASE, |
| .end = ATH_USB_EHCI_BASE + ATH_USB_WINDOW - 1, |
| .flags = IORESOURCE_MEM, |
| }, |
| [1] = { |
| .start = ATH_CPU_IRQ_USB, |
| .end = ATH_CPU_IRQ_USB, |
| .flags = IORESOURCE_IRQ, |
| }, |
| }; |
| |
| /* |
| * The dmamask must be set for EHCI to work |
| */ |
| static u64 ehci_dmamask = ~(u32) 0; |
| |
| static struct platform_device ath_usb_ehci_device = { |
| .name = "ath-ehci", |
| .id = 0, |
| .dev = { |
| .dma_mask = &ehci_dmamask, |
| .coherent_dma_mask = 0xffffffff, |
| }, |
| .num_resources = ARRAY_SIZE(ath_usb_ehci_resources), |
| .resource = ath_usb_ehci_resources, |
| }; |
| |
| static struct resource ath_uart_resources[] = { |
| { |
| .start = ATH_UART_BASE, |
| .end = ATH_UART_BASE + 0x0fff, |
| .flags = IORESOURCE_MEM, |
| }, |
| }; |
| |
| extern unsigned int ath_serial_in(int offset); |
| extern void ath_serial_out(int offset, int value); |
| unsigned int ath_plat_serial_in(struct uart_port *up, int offset) |
| { |
| return ath_serial_in(offset); |
| } |
| |
| void ath_plat_serial_out(struct uart_port *up, int offset, int value) |
| { |
| ath_serial_out(offset, value); |
| |
| } |
| |
| static struct plat_serial8250_port ath_uart_data[] = { |
| { |
| .mapbase = (u32) KSEG1ADDR(ATH_UART_BASE), |
| .membase = (void __iomem *)((u32) (KSEG1ADDR(ATH_UART_BASE))), |
| .irq = ATH_MISC_IRQ_UART, |
| .flags = (UPF_BOOT_AUTOCONF | UPF_SKIP_TEST), |
| .iotype = UPIO_MEM32, |
| .regshift = 2, |
| .uartclk = 0, /* ath_ahb_freq, */ |
| }, |
| {}, |
| }; |
| |
| static struct platform_device ath_uart = { |
| .name = "serial8250", |
| .id = 0, |
| .dev.platform_data = ath_uart_data, |
| .num_resources = 1, |
| .resource = ath_uart_resources |
| }; |
| |
| static struct platform_device *ar7241_platform_devices[] __initdata = { |
| &ath_uart, |
| &ath_usb_ehci_device |
| }; |
| |
| extern void ath_serial_setup(void); |
| |
| int ath_platform_init(void) |
| { |
| int ret; |
| |
| ath_uart_data[0].uartclk = ath_uart_freq; |
| |
| ret = platform_add_devices(ath_platform_devices, |
| ARRAY_SIZE(ath_platform_devices)); |
| |
| if (ret < 0) { |
| printk("%s: failed %d\n", __func__, ret); |
| return ret; |
| } |
| |
| return 0; |
| } |
| |
| arch_initcall(ath_platform_init); |