Commit d4a67d9d authored by Gabor Juhos's avatar Gabor Juhos Committed by Ralf Baechle

MIPS: Add initial support for the Atheros AR71XX/AR724X/AR931X SoCs

This patch adds initial support for various Atheros SoCs based on the
MIPS 24Kc core. The following models are supported at the moment:

  - AR7130
  - AR7141
  - AR7161
  - AR9130
  - AR9132
  - AR7240
  - AR7241
  - AR7242

The current patch contains minimal support only, but the resulting
kernel can boot into user-space with using of an initramfs image on
various boards which are using these SoCs. Support for more built-in
devices and individual boards will be implemented in further patches.
Signed-off-by: default avatarGabor Juhos <juhosg@openwrt.org>
Signed-off-by: default avatarImre Kaloz <kaloz@openwrt.org>
Cc: linux-mips@linux-mips.org
Cc: Luis R. Rodriguez <lrodriguez@atheros.com>
Cc: Cliff Holden <Cliff.Holden@Atheros.com>
Cc: Kathy Giori <Kathy.Giori@Atheros.com>
Patchwork: https://patchwork.linux-mips.org/patch/1947/Signed-off-by: default avatarRalf Baechle <ralf@linux-mips.org>
parent 94bb0c1a
......@@ -2,6 +2,7 @@
platforms += alchemy
platforms += ar7
platforms += ath79
platforms += bcm47xx
platforms += bcm63xx
platforms += cavium-octeon
......
......@@ -66,6 +66,20 @@ config AR7
Support for the Texas Instruments AR7 System-on-a-Chip
family: TNETD7100, 7200 and 7300.
config ATH79
bool "Atheros AR71XX/AR724X/AR913X based boards"
select BOOT_RAW
select CEVT_R4K
select CSRC_R4K
select DMA_NONCOHERENT
select IRQ_CPU
select SYS_HAS_CPU_MIPS32_R2
select SYS_HAS_EARLY_PRINTK
select SYS_SUPPORTS_32BIT_KERNEL
select SYS_SUPPORTS_BIG_ENDIAN
help
Support for the Atheros AR71XX/AR724X/AR913X SoCs.
config BCM47XX
bool "Broadcom BCM47XX based boards"
select CEVT_R4K
......@@ -718,6 +732,7 @@ config CAVIUM_OCTEON_REFERENCE_BOARD
endchoice
source "arch/mips/alchemy/Kconfig"
source "arch/mips/ath79/Kconfig"
source "arch/mips/bcm63xx/Kconfig"
source "arch/mips/jazz/Kconfig"
source "arch/mips/jz4740/Kconfig"
......
if ATH79
config SOC_AR71XX
def_bool n
config SOC_AR724X
def_bool n
config SOC_AR913X
def_bool n
endif
#
# Makefile for the Atheros AR71XX/AR724X/AR913X specific parts of the kernel
#
# Copyright (C) 2008-2011 Gabor Juhos <juhosg@openwrt.org>
# Copyright (C) 2008 Imre Kaloz <kaloz@openwrt.org>
#
# This program is free software; you can redistribute it and/or modify it
# under the terms of the GNU General Public License version 2 as published
# by the Free Software Foundation.
obj-y := prom.o setup.o irq.o common.o clock.o
obj-$(CONFIG_EARLY_PRINTK) += early_printk.o
#
# Devices
#
obj-y += dev-common.o
#
# Atheros AR71xx/AR724x/AR913x
#
platform-$(CONFIG_ATH79) += ath79/
cflags-$(CONFIG_ATH79) += -I$(srctree)/arch/mips/include/asm/mach-ath79
load-$(CONFIG_ATH79) = 0xffffffff80060000
/*
* Atheros AR71XX/AR724X/AR913X common routines
*
* Copyright (C) 2011 Gabor Juhos <juhosg@openwrt.org>
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 as published
* by the Free Software Foundation.
*/
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/err.h>
#include <linux/clk.h>
#include <asm/mach-ath79/ath79.h>
#include <asm/mach-ath79/ar71xx_regs.h>
#include "common.h"
#define AR71XX_BASE_FREQ 40000000
#define AR724X_BASE_FREQ 5000000
#define AR913X_BASE_FREQ 5000000
struct clk {
unsigned long rate;
};
static struct clk ath79_ref_clk;
static struct clk ath79_cpu_clk;
static struct clk ath79_ddr_clk;
static struct clk ath79_ahb_clk;
static struct clk ath79_wdt_clk;
static struct clk ath79_uart_clk;
static void __init ar71xx_clocks_init(void)
{
u32 pll;
u32 freq;
u32 div;
ath79_ref_clk.rate = AR71XX_BASE_FREQ;
pll = ath79_pll_rr(AR71XX_PLL_REG_CPU_CONFIG);
div = ((pll >> AR71XX_PLL_DIV_SHIFT) & AR71XX_PLL_DIV_MASK) + 1;
freq = div * ath79_ref_clk.rate;
div = ((pll >> AR71XX_CPU_DIV_SHIFT) & AR71XX_CPU_DIV_MASK) + 1;
ath79_cpu_clk.rate = freq / div;
div = ((pll >> AR71XX_DDR_DIV_SHIFT) & AR71XX_DDR_DIV_MASK) + 1;
ath79_ddr_clk.rate = freq / div;
div = (((pll >> AR71XX_AHB_DIV_SHIFT) & AR71XX_AHB_DIV_MASK) + 1) * 2;
ath79_ahb_clk.rate = ath79_cpu_clk.rate / div;
ath79_wdt_clk.rate = ath79_ahb_clk.rate;
ath79_uart_clk.rate = ath79_ahb_clk.rate;
}
static void __init ar724x_clocks_init(void)
{
u32 pll;
u32 freq;
u32 div;
ath79_ref_clk.rate = AR724X_BASE_FREQ;
pll = ath79_pll_rr(AR724X_PLL_REG_CPU_CONFIG);
div = ((pll >> AR724X_PLL_DIV_SHIFT) & AR724X_PLL_DIV_MASK);
freq = div * ath79_ref_clk.rate;
div = ((pll >> AR724X_PLL_REF_DIV_SHIFT) & AR724X_PLL_REF_DIV_MASK);
freq *= div;
ath79_cpu_clk.rate = freq;
div = ((pll >> AR724X_DDR_DIV_SHIFT) & AR724X_DDR_DIV_MASK) + 1;
ath79_ddr_clk.rate = freq / div;
div = (((pll >> AR724X_AHB_DIV_SHIFT) & AR724X_AHB_DIV_MASK) + 1) * 2;
ath79_ahb_clk.rate = ath79_cpu_clk.rate / div;
ath79_wdt_clk.rate = ath79_ahb_clk.rate;
ath79_uart_clk.rate = ath79_ahb_clk.rate;
}
static void __init ar913x_clocks_init(void)
{
u32 pll;
u32 freq;
u32 div;
ath79_ref_clk.rate = AR913X_BASE_FREQ;
pll = ath79_pll_rr(AR913X_PLL_REG_CPU_CONFIG);
div = ((pll >> AR913X_PLL_DIV_SHIFT) & AR913X_PLL_DIV_MASK);
freq = div * ath79_ref_clk.rate;
ath79_cpu_clk.rate = freq;
div = ((pll >> AR913X_DDR_DIV_SHIFT) & AR913X_DDR_DIV_MASK) + 1;
ath79_ddr_clk.rate = freq / div;
div = (((pll >> AR913X_AHB_DIV_SHIFT) & AR913X_AHB_DIV_MASK) + 1) * 2;
ath79_ahb_clk.rate = ath79_cpu_clk.rate / div;
ath79_wdt_clk.rate = ath79_ahb_clk.rate;
ath79_uart_clk.rate = ath79_ahb_clk.rate;
}
void __init ath79_clocks_init(void)
{
if (soc_is_ar71xx())
ar71xx_clocks_init();
else if (soc_is_ar724x())
ar724x_clocks_init();
else if (soc_is_ar913x())
ar913x_clocks_init();
else
BUG();
pr_info("Clocks: CPU:%lu.%03luMHz, DDR:%lu.%03luMHz, AHB:%lu.%03luMHz, "
"Ref:%lu.%03luMHz",
ath79_cpu_clk.rate / 1000000,
(ath79_cpu_clk.rate / 1000) % 1000,
ath79_ddr_clk.rate / 1000000,
(ath79_ddr_clk.rate / 1000) % 1000,
ath79_ahb_clk.rate / 1000000,
(ath79_ahb_clk.rate / 1000) % 1000,
ath79_ref_clk.rate / 1000000,
(ath79_ref_clk.rate / 1000) % 1000);
}
/*
* Linux clock API
*/
struct clk *clk_get(struct device *dev, const char *id)
{
if (!strcmp(id, "ref"))
return &ath79_ref_clk;
if (!strcmp(id, "cpu"))
return &ath79_cpu_clk;
if (!strcmp(id, "ddr"))
return &ath79_ddr_clk;
if (!strcmp(id, "ahb"))
return &ath79_ahb_clk;
if (!strcmp(id, "wdt"))
return &ath79_wdt_clk;
if (!strcmp(id, "uart"))
return &ath79_uart_clk;
return ERR_PTR(-ENOENT);
}
EXPORT_SYMBOL(clk_get);
int clk_enable(struct clk *clk)
{
return 0;
}
EXPORT_SYMBOL(clk_enable);
void clk_disable(struct clk *clk)
{
}
EXPORT_SYMBOL(clk_disable);
unsigned long clk_get_rate(struct clk *clk)
{
return clk->rate;
}
EXPORT_SYMBOL(clk_get_rate);
void clk_put(struct clk *clk)
{
}
EXPORT_SYMBOL(clk_put);
/*
* Atheros AR71XX/AR724X/AR913X common routines
*
* Copyright (C) 2008-2010 Gabor Juhos <juhosg@openwrt.org>
* Copyright (C) 2008 Imre Kaloz <kaloz@openwrt.org>
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 as published
* by the Free Software Foundation.
*/
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/types.h>
#include <linux/spinlock.h>
#include <asm/mach-ath79/ath79.h>
#include <asm/mach-ath79/ar71xx_regs.h>
#include "common.h"
static DEFINE_SPINLOCK(ath79_device_reset_lock);
u32 ath79_cpu_freq;
EXPORT_SYMBOL_GPL(ath79_cpu_freq);
u32 ath79_ahb_freq;
EXPORT_SYMBOL_GPL(ath79_ahb_freq);
u32 ath79_ddr_freq;
EXPORT_SYMBOL_GPL(ath79_ddr_freq);
enum ath79_soc_type ath79_soc;
void __iomem *ath79_pll_base;
void __iomem *ath79_reset_base;
EXPORT_SYMBOL_GPL(ath79_reset_base);
void __iomem *ath79_ddr_base;
void ath79_ddr_wb_flush(u32 reg)
{
void __iomem *flush_reg = ath79_ddr_base + reg;
/* Flush the DDR write buffer. */
__raw_writel(0x1, flush_reg);
while (__raw_readl(flush_reg) & 0x1)
;
/* It must be run twice. */
__raw_writel(0x1, flush_reg);
while (__raw_readl(flush_reg) & 0x1)
;
}
EXPORT_SYMBOL_GPL(ath79_ddr_wb_flush);
void ath79_device_reset_set(u32 mask)
{
unsigned long flags;
u32 reg;
u32 t;
if (soc_is_ar71xx())
reg = AR71XX_RESET_REG_RESET_MODULE;
else if (soc_is_ar724x())
reg = AR724X_RESET_REG_RESET_MODULE;
else if (soc_is_ar913x())
reg = AR913X_RESET_REG_RESET_MODULE;
else
BUG();
spin_lock_irqsave(&ath79_device_reset_lock, flags);
t = ath79_reset_rr(reg);
ath79_reset_wr(reg, t | mask);
spin_unlock_irqrestore(&ath79_device_reset_lock, flags);
}
EXPORT_SYMBOL_GPL(ath79_device_reset_set);
void ath79_device_reset_clear(u32 mask)
{
unsigned long flags;
u32 reg;
u32 t;
if (soc_is_ar71xx())
reg = AR71XX_RESET_REG_RESET_MODULE;
else if (soc_is_ar724x())
reg = AR724X_RESET_REG_RESET_MODULE;
else if (soc_is_ar913x())
reg = AR913X_RESET_REG_RESET_MODULE;
else
BUG();
spin_lock_irqsave(&ath79_device_reset_lock, flags);
t = ath79_reset_rr(reg);
ath79_reset_wr(reg, t & ~mask);
spin_unlock_irqrestore(&ath79_device_reset_lock, flags);
}
EXPORT_SYMBOL_GPL(ath79_device_reset_clear);
/*
* Atheros AR71XX/AR724X/AR913X common definitions
*
* Copyright (C) 2008-2011 Gabor Juhos <juhosg@openwrt.org>
* Copyright (C) 2008 Imre Kaloz <kaloz@openwrt.org>
*
* Parts of this file are based on Atheros' 2.6.15 BSP
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 as published
* by the Free Software Foundation.
*/
#ifndef __ATH79_COMMON_H
#define __ATH79_COMMON_H
#include <linux/types.h>
#include <linux/init.h>
#define ATH79_MEM_SIZE_MIN (2 * 1024 * 1024)
#define ATH79_MEM_SIZE_MAX (128 * 1024 * 1024)
void ath79_clocks_init(void);
void ath79_ddr_wb_flush(unsigned int reg);
#endif /* __ATH79_COMMON_H */
/*
* Atheros AR71XX/AR724X/AR913X common devices
*
* Copyright (C) 2008-2011 Gabor Juhos <juhosg@openwrt.org>
* Copyright (C) 2008 Imre Kaloz <kaloz@openwrt.org>
*
* Parts of this file are based on Atheros' 2.6.15 BSP
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 as published
* by the Free Software Foundation.
*/
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/platform_device.h>
#include <linux/serial_8250.h>
#include <linux/clk.h>
#include <linux/err.h>
#include <asm/mach-ath79/ath79.h>
#include <asm/mach-ath79/ar71xx_regs.h>
#include "common.h"
#include "dev-common.h"
static struct resource ath79_uart_resources[] = {
{
.start = AR71XX_UART_BASE,
.end = AR71XX_UART_BASE + AR71XX_UART_SIZE - 1,
.flags = IORESOURCE_MEM,
},
};
#define AR71XX_UART_FLAGS (UPF_BOOT_AUTOCONF | UPF_SKIP_TEST | UPF_IOREMAP)
static struct plat_serial8250_port ath79_uart_data[] = {
{
.mapbase = AR71XX_UART_BASE,
.irq = ATH79_MISC_IRQ_UART,
.flags = AR71XX_UART_FLAGS,
.iotype = UPIO_MEM32,
.regshift = 2,
}, {
/* terminating entry */
}
};
static struct platform_device ath79_uart_device = {
.name = "serial8250",
.id = PLAT8250_DEV_PLATFORM,
.resource = ath79_uart_resources,
.num_resources = ARRAY_SIZE(ath79_uart_resources),
.dev = {
.platform_data = ath79_uart_data
},
};
void __init ath79_register_uart(void)
{
struct clk *clk;
clk = clk_get(NULL, "uart");
if (IS_ERR(clk))
panic("unable to get UART clock, err=%ld", PTR_ERR(clk));
ath79_uart_data[0].uartclk = clk_get_rate(clk);
platform_device_register(&ath79_uart_device);
}
/*
* Atheros AR71XX/AR724X/AR913X common devices
*
* Copyright (C) 2008-2010 Gabor Juhos <juhosg@openwrt.org>
* Copyright (C) 2008 Imre Kaloz <kaloz@openwrt.org>
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 as published
* by the Free Software Foundation.
*/
#ifndef _ATH79_DEV_COMMON_H
#define _ATH79_DEV_COMMON_H
void ath79_register_uart(void);
#endif /* _ATH79_DEV_COMMON_H */
/*
* Atheros AR71XX/AR724X/AR913X SoC early printk support
*
* Copyright (C) 2008-2010 Gabor Juhos <juhosg@openwrt.org>
* Copyright (C) 2008 Imre Kaloz <kaloz@openwrt.org>
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 as published
* by the Free Software Foundation.
*/
#include <linux/io.h>
#include <linux/serial_reg.h>
#include <asm/addrspace.h>
#include <asm/mach-ath79/ar71xx_regs.h>
static inline void prom_wait_thre(void __iomem *base)
{
u32 lsr;
do {
lsr = __raw_readl(base + UART_LSR * 4);
if (lsr & UART_LSR_THRE)
break;
} while (1);
}
void prom_putchar(unsigned char ch)
{
void __iomem *base = (void __iomem *)(KSEG1ADDR(AR71XX_UART_BASE));
prom_wait_thre(base);
__raw_writel(ch, base + UART_TX * 4);
prom_wait_thre(base);
}
/*
* Atheros AR71xx/AR724x/AR913x specific interrupt handling
*
* Copyright (C) 2008-2010 Gabor Juhos <juhosg@openwrt.org>
* Copyright (C) 2008 Imre Kaloz <kaloz@openwrt.org>
*
* Parts of this file are based on Atheros' 2.6.15 BSP
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 as published
* by the Free Software Foundation.
*/
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/interrupt.h>
#include <linux/irq.h>
#include <asm/irq_cpu.h>
#include <asm/mipsregs.h>
#include <asm/mach-ath79/ath79.h>
#include <asm/mach-ath79/ar71xx_regs.h>
#include "common.h"
static unsigned int ath79_ip2_flush_reg;
static unsigned int ath79_ip3_flush_reg;
static void ath79_misc_irq_handler(unsigned int irq, struct irq_desc *desc)
{
void __iomem *base = ath79_reset_base;
u32 pending;
pending = __raw_readl(base + AR71XX_RESET_REG_MISC_INT_STATUS) &
__raw_readl(base + AR71XX_RESET_REG_MISC_INT_ENABLE);
if (pending & MISC_INT_UART)
generic_handle_irq(ATH79_MISC_IRQ_UART);
else if (pending & MISC_INT_DMA)
generic_handle_irq(ATH79_MISC_IRQ_DMA);
else if (pending & MISC_INT_PERFC)
generic_handle_irq(ATH79_MISC_IRQ_PERFC);
else if (pending & MISC_INT_TIMER)
generic_handle_irq(ATH79_MISC_IRQ_TIMER);
else if (pending & MISC_INT_OHCI)
generic_handle_irq(ATH79_MISC_IRQ_OHCI);
else if (pending & MISC_INT_ERROR)
generic_handle_irq(ATH79_MISC_IRQ_ERROR);
else if (pending & MISC_INT_GPIO)
generic_handle_irq(ATH79_MISC_IRQ_GPIO);
else if (pending & MISC_INT_WDOG)
generic_handle_irq(ATH79_MISC_IRQ_WDOG);
else
spurious_interrupt();
}
static void ar71xx_misc_irq_unmask(unsigned int irq)
{
void __iomem *base = ath79_reset_base;
u32 t;
irq -= ATH79_MISC_IRQ_BASE;
t = __raw_readl(base + AR71XX_RESET_REG_MISC_INT_ENABLE);
__raw_writel(t | (1 << irq), base + AR71XX_RESET_REG_MISC_INT_ENABLE);
/* flush write */
__raw_readl(base + AR71XX_RESET_REG_MISC_INT_ENABLE);
}
static void ar71xx_misc_irq_mask(unsigned int irq)
{
void __iomem *base = ath79_reset_base;
u32 t;
irq -= ATH79_MISC_IRQ_BASE;
t = __raw_readl(base + AR71XX_RESET_REG_MISC_INT_ENABLE);
__raw_writel(t & ~(1 << irq), base + AR71XX_RESET_REG_MISC_INT_ENABLE);
/* flush write */
__raw_readl(base + AR71XX_RESET_REG_MISC_INT_ENABLE);
}
static void ar724x_misc_irq_ack(unsigned int irq)
{
void __iomem *base = ath79_reset_base;
u32 t;
irq -= ATH79_MISC_IRQ_BASE;
t = __raw_readl(base + AR71XX_RESET_REG_MISC_INT_STATUS);
__raw_writel(t & ~(1 << irq), base + AR71XX_RESET_REG_MISC_INT_STATUS);
/* flush write */
__raw_readl(base + AR71XX_RESET_REG_MISC_INT_STATUS);
}
static struct irq_chip ath79_misc_irq_chip = {
.name = "MISC",
.unmask = ar71xx_misc_irq_unmask,
.mask = ar71xx_misc_irq_mask,
};
static void __init ath79_misc_irq_init(void)