blob: 9b019fcab08b1627b5543fcb2be6a140a5bd98bb [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/config.h>
#include <linux/init.h>
#include <linux/tty.h>
#include <linux/ioport.h>
#include <linux/delay.h>
#include <linux/irq.h>
#include <linux/interrupt.h>
#include <linux/serial.h>
#include <linux/serial_core.h>
#include <linux/serial_reg.h>
#include <linux/serial_8250.h>
#include <linux/console.h>
#include <linux/types.h>
#include <linux/string.h>
#include <linux/pci.h>
#include <linux/mtd/mtd.h>
#include <asm/reboot.h>
#include <asm/io.h>
#include <asm/time.h>
#include <asm/pgtable.h>
#include <asm/processor.h>
#include <asm/reboot.h>
#include <asm/system.h>
#include <asm/serial.h>
#include <asm/traps.h>
#include <atheros.h>
#if 0 /* For WLAN debug */
u_int32_t wasp_loop = 0;
EXPORT_SYMBOL(wasp_loop);
u_int32_t g_rxbuf_cnt = 0;
u_int32_t g_rxadded = 0;
u_int32_t g_rxdepth = 0;
u_int32_t g_rxtail = 0;
u_int32_t g_rxhead = 0;
u_int32_t g_rxbuf[512];
u_int32_t g_rxdp[512];
u_int32_t g_rxfifodepth[512];
EXPORT_SYMBOL(g_rxbuf_cnt);
EXPORT_SYMBOL(g_rxadded);
EXPORT_SYMBOL(g_rxdepth);
EXPORT_SYMBOL(g_rxtail);
EXPORT_SYMBOL(g_rxhead);
EXPORT_SYMBOL(g_rxbuf);
EXPORT_SYMBOL(g_rxdp);
EXPORT_SYMBOL(g_rxfifodepth);
#endif
uint32_t ath_cpu_freq, ath_ahb_freq, ath_ddr_freq,
ath_ref_freq, ath_uart_freq;
uint32_t serial_inited;
static int __init ath_init_ioc(void);
void serial_print(const char *fmt, ...);
void writeserial(char *str, int count);
void ath_sys_frequency(void);
void UartInit(void);
/*
* Export AHB freq value to be used by Ethernet MDIO.
*/
EXPORT_SYMBOL(ath_ahb_freq);
/*
* Export Ref freq value to be used by I2S module.
*/
EXPORT_SYMBOL(ath_ref_freq);
void ath_restart(char *command)
{
for (;;) {
if (is_ar934x_10()) {
ath_reg_wr(ATH_GPIO_OE, ath_reg_rd(ATH_GPIO_OE) & (~(1 << 17)));
} else {
ath_reg_wr(ATH_RESET, RST_RESET_FULL_CHIP_RESET_MASK);
}
}
}
void ath_halt(void)
{
printk(KERN_NOTICE "\n** You can safely turn off the power\n");
while (1) ;
}
void ath_power_off(void)
{
ath_halt();
}
const char
*get_system_type(void)
{
extern uint32_t ath_otp_read(uint32_t addr);
#ifdef CONFIG_ATH_EMULATION
# define ath_sys_type(x) x " emu"
#else
# define ath_sys_type(x) x
# define ath_sys_type_otp(x) x " wmac not present"
#endif
/*
* Make sure WMAC is enabled.
* Read Memory Address 0 of OTP and Check if WMAC is Disabled
* If Disabled, do not initialize WMAC
*/
#ifdef ATH_OTP_MEM_0FFSET_ZERO
if (ath_otp_read(ATH_OTP_MEM_0FFSET_ZERO) & ATH_OTP_WMAC_DISABLED) {
return ath_sys_type_otp(CONFIG_ATH_SYS_TYPE);
} else
#endif
{
return ath_sys_type(CONFIG_ATH_SYS_TYPE);
}
}
EXPORT_SYMBOL(get_system_type);
int
valid_wmac_num(u_int16_t wmac_num)
{
return (wmac_num == 0);
}
/*
* HOWL has only one wmac device, hence the following routines
* ignore the wmac_num parameter
*/
int
get_wmac_irq(u_int16_t wmac_num)
{
return ATH_CPU_IRQ_WLAN;
}
unsigned long
get_wmac_base(u_int16_t wmac_num)
{
return KSEG1ADDR(ATH_WMAC_BASE);
}
unsigned long
get_wmac_mem_len(u_int16_t wmac_num)
{
return ATH_WMAC_LEN;
}
EXPORT_SYMBOL(valid_wmac_num);
EXPORT_SYMBOL(get_wmac_irq);
EXPORT_SYMBOL(get_wmac_base);
EXPORT_SYMBOL(get_wmac_mem_len);
#ifdef CONFIG_SERIAL_8250
void __init ath_serial_setup(void)
{
struct uart_port p;
memset(&p, 0, sizeof(p));
p.flags = (UPF_BOOT_AUTOCONF | UPF_SKIP_TEST);
p.iotype = UPIO_MEM32;
p.uartclk = ath_ahb_freq;
p.irq = ATH_MISC_IRQ_UART;
p.regshift = 2;
p.mapbase = (u32) KSEG1ADDR(ATH_UART_BASE);
p.membase = (void __iomem *)p.mapbase;
if (early_serial_setup(&p) != 0)
printk(KERN_ERR "early_serial_setup failed\n");
serial_print("%s: early_serial_setup done..\n", __func__);
}
#endif
unsigned int __cpuinit get_c0_compare_int(void)
{
//printk("%s: returning timer irq : %d\n",__func__, ATH_CPU_IRQ_TIMER);
return ATH_CPU_IRQ_TIMER;
}
#ifdef CONFIG_MACH_QCA956x
static void __init fix_ev124229(void)
{
ath_reg_rmw_set(RST_MISC_INTERRUPT_MASK_ADDRESS, RST_MISC_INTERRUPT_MASK_MIPS_SI_TimerInt_MASK_MASK);
}
#endif
void __init plat_time_init(void)
{
mips_hpt_frequency = ath_cpu_freq / 2;
#ifdef CONFIG_MACH_QCA956x
late_time_init = fix_ev124229;
#endif
printk("%s: plat time init done\n", __func__);
}
#ifdef CONFIG_MACH_QCA955x
void ath_pci_reg_status(int id, u_int32_t int_status, u_int32_t g_reset_addr, u_int32_t app_addr,
u_int32_t uer_reg, u_int32_t cer_reg)
{
u_int32_t val;
val = ath_reg_rd(int_status);
printk(" PCI Intr register of RC%d : %08x\n", id, val);
printk(" RC%d: PCI link down is %d and link req reset %d\n", id,
PCIE_INT_MASK_LINK_DOWN_GET(val),
PCIE_INT_MASK_LINK_REQ_RST_GET(val));
printk(" PCI link status from global reg : %d\n",
(ath_reg_rd(g_reset_addr) & 0x1 == 0x1));
printk(" PCI Err counter registers are RC%d(%08x) : %08x\n", id, app_addr,
ath_reg_rd(app_addr));
printk(" PCI Err RC%d(%08x) : %08x, (%08x) : %08x\n", id, uer_reg, ath_reg_rd(uer_reg),
cer_reg, ath_reg_rd(cer_reg));
}
#endif
int ath_be_handler(struct pt_regs *regs, int is_fixup)
{
#ifdef CONFIG_MACH_AR934x
printk("ath data bus error: cause 0x%x epc 0x%x\nrebooting...", read_c0_cause(), read_c0_epc());
ath_restart(NULL);
#else
printk("ath data bus error: cause %#x\n", read_c0_cause());
#endif
#ifdef CONFIG_MACH_QCA955x
printk(" ####PCI link status on data bus error ####\n");
ath_pci_reg_status(0, PCIE_INT_STATUS_ADDRESS, PCIE_RESET_ADDRESS,
PCIE_AER_ADDRESS, PCIE_UER_ADDRESS, PCIE_CER_ADDRESS);
ath_pci_reg_status(1, PCIE_INT_STATUS_ADDRESS_2, PCIE_RESET_ADDRESS_2,
PCIE_AER_ADDRESS_2, PCIE_UER_ADDRESS_2, PCIE_CER_ADDRESS_2);
printk(" ###################\n");
#endif
return (is_fixup ? MIPS_BE_FIXUP : MIPS_BE_FATAL);
}
void __init plat_mem_setup(void)
{
#if 1
board_be_handler = ath_be_handler;
#endif
_machine_restart = ath_restart;
_machine_halt = ath_halt;
pm_power_off = ath_power_off;
/*
** early_serial_setup seems to conflict with serial8250_register_port()
** In order for console to work, we need to call register_console().
** We can call serial8250_register_port() directly or use
** platform_add_devices() function which eventually calls the
** register_console(). AP71 takes this approach too. Only drawback
** is if system screws up before we register console, we won't see
** any msgs on the console. System being stable now this should be
** a special case anyways. Just initialize Uart here.
*/
#ifdef CONFIG_SERIAL_8250
UartInit();
#endif
#ifdef CONFIG_MACH_AR933x
/* clear wmac reset */
ath_reg_wr(ATH_RESET,
(ath_reg_rd(ATH_RESET) & (~ATH_RESET_WMAC)));
#endif
serial_print("Booting %s\n", get_system_type());
}
/*
* -------------------------------------------------
* Early printk hack
*/
u8 UartGetPoll(void) __attribute__ ((weak));
void UartPut(u8 byte) __attribute__ ((weak));
u8 UartGetPoll()
{
while ((UART_READ(OFS_LINE_STATUS) & 0x1) == 0) ;
return UART_READ(OFS_RCV_BUFFER);
}
void UartPut(u8 byte)
{
if (!serial_inited) {
serial_inited = 1;
UartInit();
}
while (((UART_READ(OFS_LINE_STATUS)) & 0x20) == 0x0) ;
UART_WRITE(OFS_SEND_BUFFER, byte);
}
#ifdef CONFIG_EARLY_PRINTK
void prom_putchar(u8 byte)
{
UartPut(byte);
}
#endif
extern int vsprintf(char *buf, const char *fmt, va_list args);
static char sprint_buf[1024];
void serial_print(const char *fmt, ...)
{
va_list args;
int n;
va_start(args, fmt);
n = vsprintf(sprint_buf, fmt, args);
va_end(args);
writeserial(sprint_buf, n);
}
void writeserial(char *str, int count)
{
int i;
for (i = 0; i <= count; i++)
UartPut(str[i]);
UartPut('\r');
memset(str, '\0', 1024);
return;
}
unsigned int ath_serial_in(int offset)
{
return UART_READ(offset);
}
void ath_serial_out(int offset, int value)
{
UART_WRITE(offset, (u8) value);
}
#include <asm/uaccess.h>
#define M_PERFCTL_EVENT(event) ((event) << 5)
unsigned int clocks_at_start;
void start_cntrs(unsigned int event0, unsigned int event1)
{
write_c0_perfcntr0(0x00000000);
write_c0_perfcntr1(0x00000000);
/*
* go...
*/
write_c0_perfctrl0(0x80000000 | M_PERFCTL_EVENT(event0) | 0xf);
write_c0_perfctrl1(0x00000000 | M_PERFCTL_EVENT(event1) | 0xf);
}
void stop_cntrs(void)
{
write_c0_perfctrl0(0);
write_c0_perfctrl1(0);
}
void read_cntrs(unsigned int *c0, unsigned int *c1)
{
*c0 = read_c0_perfcntr0();
*c1 = read_c0_perfcntr1();
}
static int ath_ioc_open(struct inode *inode, struct file *file)
{
return 0;
}
static ssize_t
ath_ioc_read(struct file *file, char *buf, size_t count, loff_t * ppos)
{
unsigned int c0, c1, ticks = (read_c0_count() - clocks_at_start);
char str[256];
unsigned int secs = ticks / mips_hpt_frequency;
read_cntrs(&c0, &c1);
stop_cntrs();
sprintf(str, "%d secs (%#x) event0:%#x event1:%#x", secs, ticks, c0,
c1);
copy_to_user(buf, str, strlen(str));
return (strlen(str));
}
#if 0
static void ath_dcache_test(void)
{
int i, j;
unsigned char p;
for (i = 0; i < 4; i++) {
for (j = 0; j < (10 * 1024); j++) {
p = *((unsigned char *)0x81000000 + j);
}
}
}
#endif
static ssize_t
ath_ioc_write(struct file *file, const char *buf, size_t count,
loff_t * ppos)
{
int event0, event1;
sscanf(buf, "%d:%d", &event0, &event1);
printk("\nevent0 %d event1 %d\n", event0, event1);
clocks_at_start = read_c0_count();
start_cntrs(event0, event1);
return (count);
}
struct file_operations ath_ioc_fops = {
open:ath_ioc_open,
read:ath_ioc_read,
write:ath_ioc_write,
};
/*
* General purpose ioctl i/f
*/
static int __init ath_init_ioc()
{
static int _mymajor;
_mymajor = register_chrdev(77, "ATH_GPIOC", &ath_ioc_fops);
if (_mymajor < 0) {
printk("Failed to register GPIOC\n");
return _mymajor;
}
printk("ATH GPIOC major %d\n", _mymajor);
return 0;
}
device_initcall(ath_init_ioc);
#ifdef CONFIG_MTD
/*
* NAND Read API for Calibration data
*
*/
int
ath_nand_local_read(u_char *cal_part,loff_t from, size_t len,
size_t *retlen, u_char *buf)
{
int ret;
struct mtd_info *mtd;
if (!len || !retlen) return (0);
printk("Reading Flash for Calibraton data from 0x%llx and partition name is %s\n",from,cal_part);
mtd = get_mtd_device_nm(cal_part);
if (mtd == ERR_PTR(-ENODEV)) {
printk("MTD partition doesn't exist \n");
ret = -EIO;
return ret;
}
ret = mtd->read(mtd, from, len, retlen, buf);
return ret;
}
EXPORT_SYMBOL(ath_nand_local_read);
#endif