blob: 5c41e80d9c8d2602744b3f12368615f25e487a7f [file] [log] [blame]
/*
* 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 <linux/platform_device.h>
#include <linux/serial_core.h>
#ifdef CONFIG_SERIAL_8250
#include <linux/serial_8250.h>
#endif
#include <atheros.h>
void ath_sys_frequency(void);
void UartInit(void);
void ath_dispatch_wlan_intr(void)
{
u_int32_t int_status = ath_reg_rd(ATH_GLOBAL_INT_STATUS);
#ifdef CONFIG_PCI
if (int_status & RST_GLOBAL_INTERRUPT_STATUS_PCIE_INT_MASK) {
do_IRQ(ATH_PCI_IRQ_BASE);
}
#endif
#ifdef CONFIG_ATH_HAS_PCI_EP
if (int_status & RST_GLOBAL_INTERRUPT_STATUS_PCIE_EP_INT_MASK) {
do_IRQ(ATH_CPU_IRQ_PCI_EP);
}
#endif
if (int_status & RST_GLOBAL_INTERRUPT_STATUS_WMAC_INT_MASK) {
do_IRQ(ATH_CPU_IRQ_WLAN);
}
}
void ath_demux_usb_pciep_rc2(void)
{
uint32_t intr = ath_reg_rd(ATH_GLOBAL_INT_STATUS);
#ifdef CONFIG_ATH_HAS_PCI_RC2
if (intr & RST_GLOBAL_INTERRUPT_STATUS_PCIE_RC2_INT_MASK) {
do_IRQ(ATH_PCI_RC2_IRQ);
}
#endif
if (intr & (RST_GLOBAL_INTERRUPT_STATUS_USB1_INT_MASK |
RST_GLOBAL_INTERRUPT_STATUS_USB2_INT_MASK)) {
do_IRQ(ATH_CPU_IRQ_USB);
}
}
unsigned int ath_slic_cntrl_rd(void)
{
return ath_reg_rd(ATH_SLIC_CTRL);
}
void ath_slic_cntrl_wr(unsigned int val)
{
ath_reg_wr(ATH_SLIC_CTRL, val);
}
void ath_spi_raw_output_u8(unsigned char val)
{
unsigned int i, j, reg = 0, dac_data = 0;
// Start Write transaction
reg = val ;
for (j = 0; j < 5; j++) { ; }
for (i = 0; i < 8; i++) { // TRANSMIT DATA
dac_data = 0x50000;
if ((reg >> (7-i) & 0x1) == 0x1) {
dac_data = dac_data | 0x1;
} else {
dac_data = dac_data & 0xfffffffe;
}
ath_reg_wr(ATH_SPI_WRITE, dac_data);
for (j = 0; j < 5; j++) { ; }
dac_data = dac_data | 0x50100; // RISING_CLK
ath_reg_wr(ATH_SPI_WRITE, dac_data);
for (j = 0; j < 15; j++) { ; }
}
}
unsigned int ath_spi_raw_input_u8(void)
{
unsigned int i, j;
for (i = 0; i < 8; i++) { // TRANSMIT DATA
ath_reg_wr(ATH_SPI_WRITE, 0x50100); //CS1 = 0 , CLK=1
for (j = 0; j < 15; j++) { ; }
ath_reg_wr(ATH_SPI_WRITE, 0x50000); //CS1 = 0 , CLK=0
for (j = 0; j < 15; j++) { ; }
}
ath_reg_wr(ATH_SPI_WRITE, 0x70000); //CS1 = 1 , CLK=0
for (j = 0; j < 15; j++) { ; }
return ath_reg_rd(ATH_SPI_RD_STATUS) & 0xff;
}
void UartInit(void)
{
int freq, div;
extern uint32_t serial_inited;
ath_sys_frequency();
freq = ath_uart_freq;
div = freq / (ATH_CONSOLE_BAUD * 16);
/* set DIAB bit */
UART_WRITE(OFS_LINE_CONTROL, 0x80);
/* set divisor */
UART_WRITE(OFS_DIVISOR_LSB, (div & 0xff));
UART_WRITE(OFS_DIVISOR_MSB, (div >> 8) & 0xff);
// UART16550_WRITE(OFS_DIVISOR_LSB, 0x61);
// UART16550_WRITE(OFS_DIVISOR_MSB, 0x03);
/* clear DIAB bit */
UART_WRITE(OFS_LINE_CONTROL, 0x00);
/* set data format */
UART_WRITE(OFS_DATA_FORMAT, 0x3);
UART_WRITE(OFS_INTR_ENABLE, 0);
serial_inited = 1;
}
void
ath_sys_frequency(void)
{
#if !defined(CONFIG_ATH_EMULATION)
unsigned int rdata, i;
unsigned int cpu_pll_low_int, cpu_pll_low_frac, cpu_pll_high_int, cpu_pll_high_frac;
unsigned int ddr_pll_low_int, ddr_pll_low_frac, ddr_pll_high_int, ddr_pll_high_frac;
unsigned int cpu_clk_low, cpu_clk_high;
unsigned int ddr_clk_low, ddr_clk_high;
unsigned int ahb_clk_low, ahb_clk_high;
/* CPU_DDR_CLOCK_CONTROL */
unsigned int ahbclk_from_ddrpll, ahb_post_div, ddr_post_div, cpu_post_div;
unsigned int cpu_ddr_clk_from_cpupll, cpu_ddr_clk_from_ddrpll;
unsigned int ahb_pll_bypass, ddr_pll_bypass, cpu_pll_bypass;
/* CPU_PLL_CONFIG, CPU_PLL_CONFIG1, CPU_PLL_DITHER1, CPU_PLL_DITHER2 */
unsigned int cpu_pllpwd, cpu_outdiv, cpu_Refdiv, cpu_Nint;
unsigned int cpu_dither_en, cpu_NFrac_Min, cpu_NFrac_Max;
unsigned int cpu_NFrac_Min_17_5, cpu_NFrac_Min_4_0;
unsigned int cpu_NFrac_Max_17_5, cpu_NFrac_Max_4_0;
/* DDR_PLL_CONFIG, DDR_PLL_CONFIG1, DDR_PLL_DITHER1, DDR_PLL_DITHER2 */
unsigned int ddr_pllpwd, ddr_outdiv, ddr_Refdiv, ddr_Nint;
unsigned int ddr_dither_en, ddr_NFrac_Min, ddr_NFrac_Max;
unsigned int ddr_NFrac_Min_17_5, ddr_NFrac_Min_4_0;
unsigned int ddr_NFrac_Max_17_5, ddr_NFrac_Max_4_0;
#endif
uint32_t ref_clk;
#if defined(CONFIG_ATH_EMULATION)
ref_clk = (40 * 1000000);
#else
if ((ath_reg_rd(RST_BOOTSTRAP_ADDRESS) & ATH_REF_CLK_40)) {
ref_clk = (40 * 1000000);
} else {
ref_clk = (25 * 1000000);
}
#endif
ath_uart_freq = ath_ref_freq = ref_clk;
#ifdef CONFIG_ATH_EMULATION
ath_cpu_freq = 80000000;
ath_ddr_freq = 80000000;
ath_ahb_freq = 40000000;
#else
rdata = ath_reg_rd(CPU_DDR_CLOCK_CONTROL_ADDRESS);
ahbclk_from_ddrpll = CPU_DDR_CLOCK_CONTROL_AHBCLK_FROM_DDRPLL_GET(rdata);
ahb_post_div = CPU_DDR_CLOCK_CONTROL_AHB_POST_DIV_GET(rdata);
ddr_post_div = CPU_DDR_CLOCK_CONTROL_DDR_POST_DIV_GET(rdata);
cpu_post_div = CPU_DDR_CLOCK_CONTROL_CPU_POST_DIV_GET(rdata);
cpu_ddr_clk_from_cpupll = CPU_DDR_CLOCK_CONTROL_CPU_DDR_CLK_FROM_CPUPLL_GET(rdata);
cpu_ddr_clk_from_ddrpll = CPU_DDR_CLOCK_CONTROL_CPU_DDR_CLK_FROM_DDRPLL_GET(rdata);
ahb_pll_bypass = CPU_DDR_CLOCK_CONTROL_AHB_PLL_BYPASS_GET(rdata);
ddr_pll_bypass = CPU_DDR_CLOCK_CONTROL_DDR_PLL_BYPASS_GET(rdata);
cpu_pll_bypass = CPU_DDR_CLOCK_CONTROL_CPU_PLL_BYPASS_GET(rdata);
if (ahb_pll_bypass) {
ath_ahb_freq = ref_clk / (ahb_post_div + 1);
//*ahb_clk_h = ref_clk / (ahb_post_div + 1);
}
if (ddr_pll_bypass) {
ath_ddr_freq = ref_clk;
//*ddr_clk_h = ref_clk;
}
if (cpu_pll_bypass) {
ath_cpu_freq = ref_clk;
//*cpu_clk_h = ref_clk;
}
if (ahb_pll_bypass && ddr_pll_bypass && cpu_pll_bypass) {
return;
}
rdata = ath_reg_rd(CPU_PLL_CONFIG_ADDRESS);
cpu_pllpwd = CPU_PLL_CONFIG_PLLPWD_GET(rdata);
cpu_outdiv = CPU_PLL_CONFIG_OUTDIV_GET(rdata);
cpu_Refdiv = CPU_PLL_CONFIG_REFDIV_GET(rdata);
rdata = ath_reg_rd(CPU_PLL_CONFIG1_ADDRESS);
cpu_Nint = CPU_PLL_CONFIG1_NINT_GET(rdata);
rdata = ath_reg_rd(CPU_PLL_DITHER1_ADDRESS);
cpu_dither_en = CPU_PLL_DITHER1_DITHER_EN_GET(rdata);
cpu_NFrac_Min = CPU_PLL_DITHER1_NFRAC_MIN_GET(rdata);
cpu_NFrac_Min_17_5 = (cpu_NFrac_Min >> 5) & 0x1fff;
cpu_NFrac_Min_4_0 = cpu_NFrac_Min & 0x1f;
rdata = ath_reg_rd(CPU_PLL_DITHER1_ADDRESS);
cpu_NFrac_Max = CPU_PLL_DITHER2_NFRAC_MAX_GET(rdata);
cpu_NFrac_Max_17_5 = (cpu_NFrac_Max >> 5) & 0x1fff;
cpu_NFrac_Max_4_0 = cpu_NFrac_Max & 0x1f;
rdata = ath_reg_rd(DDR_PLL_CONFIG_ADDRESS);
ddr_pllpwd = DDR_PLL_CONFIG_PLLPWD_GET(rdata);
ddr_outdiv = DDR_PLL_CONFIG_OUTDIV_GET(rdata);
ddr_Refdiv = DDR_PLL_CONFIG_REFDIV_GET(rdata);
rdata = ath_reg_rd(DDR_PLL_CONFIG1_ADDRESS);
ddr_Nint = DDR_PLL_CONFIG1_NINT_GET(rdata);
rdata = ath_reg_rd(DDR_PLL_DITHER1_ADDRESS);
ddr_dither_en = DDR_PLL_DITHER1_DITHER_EN_GET(rdata);
ddr_NFrac_Min = DDR_PLL_DITHER1_NFRAC_MIN_GET(rdata);
ddr_NFrac_Min_17_5 = (ddr_NFrac_Min >> 5) & 0x1fff;
ddr_NFrac_Min_4_0 = ddr_NFrac_Min & 0x1f;
rdata = ath_reg_rd(DDR_PLL_DITHER1_ADDRESS);
ddr_NFrac_Max = DDR_PLL_DITHER2_NFRAC_MAX_GET(rdata);
ddr_NFrac_Max_17_5 = (ddr_NFrac_Max >> 5) & 0x1fff;
ddr_NFrac_Max_4_0 = ddr_NFrac_Max & 0x1f;
/* CPU PLL */
i = (ref_clk/cpu_Refdiv);
cpu_pll_low_int = i*cpu_Nint;
cpu_pll_high_int = cpu_pll_low_int;
cpu_pll_low_frac = (i/(25*32))*((cpu_NFrac_Min_17_5*25 + cpu_NFrac_Min_4_0)/(8192/32));
cpu_pll_high_frac = (i/(25*32))*((cpu_NFrac_Max_17_5*25 + cpu_NFrac_Max_4_0)/(8192/32));
if (!cpu_dither_en || cpu_pll_high_frac <= cpu_pll_low_frac) {
cpu_pll_high_frac = cpu_pll_low_frac;
}
/* DDR PLL */
i = (ref_clk/ddr_Refdiv);
ddr_pll_low_int = i*ddr_Nint;
ddr_pll_high_int = ddr_pll_low_int;
ddr_pll_low_frac = (i/(25*32))*((ddr_NFrac_Min_17_5*25 + ddr_NFrac_Min_4_0)/(8192/32));
ddr_pll_high_frac = (i/(25*32))*((ddr_NFrac_Max_17_5*25 + ddr_NFrac_Max_4_0)/(8192/32));
if (!ddr_dither_en || ddr_pll_high_frac <= ddr_pll_low_frac) {
ddr_pll_high_frac = ddr_pll_low_frac;
}
/* CPU Clock, DDR Clock, AHB Clock (before post div) */
if (cpu_ddr_clk_from_cpupll) {
cpu_clk_low = cpu_pll_low_int + cpu_pll_low_frac;
cpu_clk_high = cpu_pll_high_int + cpu_pll_high_frac;
if (cpu_outdiv != 0) {
cpu_clk_low /= (2*cpu_outdiv);
cpu_clk_high /= (2*cpu_outdiv);
}
ddr_clk_low = cpu_clk_low;
ddr_clk_high = cpu_clk_high;
} else if (cpu_ddr_clk_from_ddrpll) {
ddr_clk_low = ddr_pll_low_int + ddr_pll_low_frac;
ddr_clk_high = ddr_pll_high_int + ddr_pll_high_frac;
if (ddr_outdiv != 0) {
ddr_clk_low /= (2*ddr_outdiv);
ddr_clk_high /= (2*ddr_outdiv);
}
cpu_clk_low = ddr_clk_low;
cpu_clk_high = ddr_clk_high;
} else {
cpu_clk_low = cpu_pll_low_int + cpu_pll_low_frac;
cpu_clk_high = cpu_pll_high_int + cpu_pll_high_frac;
ddr_clk_low = ddr_pll_low_int + ddr_pll_low_frac;
ddr_clk_high = ddr_pll_high_int + ddr_pll_high_frac;
if (cpu_outdiv != 0) {
cpu_clk_low /= (2*cpu_outdiv);
cpu_clk_high /= (2*cpu_outdiv);
}
if (ddr_outdiv != 0) {
ddr_clk_low /= (2*ddr_outdiv);
ddr_clk_high /= (2*ddr_outdiv);
}
}
if (ahbclk_from_ddrpll) {
ahb_clk_low = ddr_clk_low;
ahb_clk_high = ddr_clk_high;
} else {
ahb_clk_low = cpu_clk_low;
ahb_clk_high = cpu_clk_high;
}
/* CPU Clock, DDR Clock, AHB Clock */
cpu_clk_low /= (cpu_post_div + 1);
cpu_clk_high /= (cpu_post_div + 1);
ddr_clk_low /= (ddr_post_div + 1);
ddr_clk_high /= (ddr_post_div + 1);
ahb_clk_low /= (ahb_post_div + 1);
ahb_clk_high /= (ahb_post_div + 1);
ath_cpu_freq = cpu_clk_low;
ath_ddr_freq = ddr_clk_low;
ath_ahb_freq = ahb_clk_low;
//*cpu_clk_h = cpu_clk_high;
//*ddr_clk_h = ddr_clk_high;
//*ahb_clk_h = ahb_clk_high;
#endif
printk("%s: cpu %u ddr %u ahb %u\n", __func__,
ath_cpu_freq / 1000000,
ath_ddr_freq / 1000000,
ath_ahb_freq / 1000000);
return;
}
/*
* EHCI (USB full speed host controller)
*/
static struct resource ath_usb_ehci_resources_1[] = {
[0] = {
.start = ATH_USB_EHCI_BASE_1,
.end = ATH_USB_EHCI_BASE_1 + ATH_USB_WINDOW - 1,
.flags = IORESOURCE_MEM,
},
[1] = {
.start = ATH_CPU_IRQ_USB,
.end = ATH_CPU_IRQ_USB,
.flags = IORESOURCE_IRQ,
},
};
static struct resource ath_usb_ehci_resources_2[] = {
[0] = {
.start = ATH_USB_EHCI_BASE_2,
.end = ATH_USB_EHCI_BASE_2 + ATH_USB_WINDOW - 1,
.flags = IORESOURCE_MEM,
},
[1] = {
.start = ATH_CPU_IRQ_USB,
.end = ATH_CPU_IRQ_USB,
.flags = IORESOURCE_IRQ,
},
};
#ifdef CONFIG_ATH_HAS_PCI_EP
/*
* (PCI EP controller)
*/
static struct resource ath_pci_ep_resources[] = {
[0] = {
.start = ATH_PCI_EP_BASE_OFF,
.end = ATH_PCI_EP_BASE_OFF + 0xdff - 1,
.flags = IORESOURCE_MEM,
},
[1] = {
.start = ATH_CPU_IRQ_PCI_EP,
.end = ATH_CPU_IRQ_PCI_EP,
.flags = IORESOURCE_IRQ,
},
};
static u64 pci_ep_dmamask = ~(u32)0;
static struct platform_device ath_pci_ep_device = {
.name = "ath-pciep",
.id = 0,
.dev = {
.dma_mask = &pci_ep_dmamask,
.coherent_dma_mask = 0xffffffff,
},
.num_resources = ARRAY_SIZE(ath_pci_ep_resources),
.resource = ath_pci_ep_resources,
};
#endif
/*
* The dmamask must be set for EHCI to work
*/
static u64 ehci_dmamask = ~(u32) 0;
static struct platform_device ath_usb_ehci_device_1 = {
.name = "ath-ehci",
.id = 0,
.dev = {
.dma_mask = &ehci_dmamask,
.coherent_dma_mask = 0xffffffff,
},
.num_resources = ARRAY_SIZE(ath_usb_ehci_resources_1),
.resource = ath_usb_ehci_resources_1,
};
static struct platform_device ath_usb_ehci_device_2 = {
.name = "ath-ehci1",
.id = 1,
.dev = {
.dma_mask = &ehci_dmamask,
.coherent_dma_mask = 0xffffffff,
},
.num_resources = ARRAY_SIZE(ath_usb_ehci_resources_2),
.resource = ath_usb_ehci_resources_2,
};
#ifdef CONFIG_SERIAL_8250
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
};
#endif
static struct platform_device *ath_platform_devices[] __initdata = {
#ifdef CONFIG_SERIAL_8250
&ath_uart,
#endif
&ath_usb_ehci_device_1,
&ath_usb_ehci_device_2,
#ifdef CONFIG_ATH_HAS_PCI_EP
&ath_pci_ep_device
#endif
};
extern void ath_serial_setup(void);
extern void ath_set_wd_timer(uint32_t usec /* micro seconds */);
extern int ath_set_wd_timer_action(uint32_t val);
void
ath_aphang_timer_fn(struct timer_list *timer)
{
static int times;
if (times == 0) {
ath_set_wd_timer_action(ATH_WD_ACT_NONE);
ath_set_wd_timer(5 * USEC_PER_SEC);
ath_set_wd_timer_action(ATH_WD_ACT_RESET);
}
times = (times + 1) % HZ;
}
int ath_platform_init(void)
{
int ret;
#ifdef CONFIG_SERIAL_8250
ath_uart_data[0].uartclk = ath_uart_freq;
#endif
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);