summaryrefslogtreecommitdiffstats
path: root/arch/mips/mach-ar231x/ar231x.c
diff options
context:
space:
mode:
authorOleksij Rempel <linux@rempel-privat.de>2013-05-30 20:18:40 +0200
committerSascha Hauer <s.hauer@pengutronix.de>2013-05-31 08:56:33 +0200
commit59fe549733fb21f1e669bea148dc8c7899890844 (patch)
tree73987d701aeac5d34f7a14ffbc3112e847d69ade /arch/mips/mach-ar231x/ar231x.c
parentdf308bd4bfadf8a5dd29f6a4f9a29cc459e00ad3 (diff)
downloadbarebox-59fe549733fb21f1e669bea148dc8c7899890844.tar.gz
barebox-59fe549733fb21f1e669bea148dc8c7899890844.tar.xz
MIPS: add Atheros ar531x family support
Signed-off-by: Oleksij Rempel <linux@rempel-privat.de> Signed-off-by: Antony Pavlov <antonynpavlov@gmail.com> Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de>
Diffstat (limited to 'arch/mips/mach-ar231x/ar231x.c')
-rw-r--r--arch/mips/mach-ar231x/ar231x.c195
1 files changed, 195 insertions, 0 deletions
diff --git a/arch/mips/mach-ar231x/ar231x.c b/arch/mips/mach-ar231x/ar231x.c
new file mode 100644
index 0000000000..ca912bf0ef
--- /dev/null
+++ b/arch/mips/mach-ar231x/ar231x.c
@@ -0,0 +1,195 @@
+/*
+ * Based on Linux driver:
+ * Copyright (C) 2003 Atheros Communications, Inc., All Rights Reserved.
+ * Copyright (C) 2006 FON Technology, SL.
+ * Copyright (C) 2006 Imre Kaloz <kaloz@openwrt.org>
+ * Copyright (C) 2006-2009 Felix Fietkau <nbd@openwrt.org>
+ * Ported to Barebox:
+ * Copyright (C) 2013 Oleksij Rempel <linux@rempel-privat.de>
+ *
+ * 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.
+ */
+
+#include <common.h>
+#include <init.h>
+#include <io.h>
+#include <ns16550.h>
+#include <mach/ar231x_platform.h>
+#include <mach/ar2312_regs.h>
+
+struct ar231x_board_data ar231x_board;
+
+/*
+ * This table is indexed by bits 5..4 of the CLOCKCTL1 register
+ * to determine the predevisor value.
+ */
+static int CLOCKCTL1_PREDIVIDE_TABLE[4] = { 1, 2, 4, 5 };
+
+static unsigned int
+ar2312_cpu_frequency(void)
+{
+ unsigned int predivide_mask, predivide_shift;
+ unsigned int multiplier_mask, multiplier_shift;
+ unsigned int clock_ctl1, pre_divide_select, pre_divisor, multiplier;
+ unsigned int doubler_mask;
+ u32 devid;
+
+ devid = __raw_readl((char *)KSEG1ADDR(AR2312_REV));
+ devid &= AR2312_REV_MAJ;
+ devid >>= AR2312_REV_MAJ_S;
+ if (devid == AR2312_REV_MAJ_AR2313) {
+ predivide_mask = AR2313_CLOCKCTL1_PREDIVIDE_MASK;
+ predivide_shift = AR2313_CLOCKCTL1_PREDIVIDE_SHIFT;
+ multiplier_mask = AR2313_CLOCKCTL1_MULTIPLIER_MASK;
+ multiplier_shift = AR2313_CLOCKCTL1_MULTIPLIER_SHIFT;
+ doubler_mask = AR2313_CLOCKCTL1_DOUBLER_MASK;
+ } else { /* AR5312 and AR2312 */
+ predivide_mask = AR2312_CLOCKCTL1_PREDIVIDE_MASK;
+ predivide_shift = AR2312_CLOCKCTL1_PREDIVIDE_SHIFT;
+ multiplier_mask = AR2312_CLOCKCTL1_MULTIPLIER_MASK;
+ multiplier_shift = AR2312_CLOCKCTL1_MULTIPLIER_SHIFT;
+ doubler_mask = AR2312_CLOCKCTL1_DOUBLER_MASK;
+ }
+
+ /*
+ * Clocking is derived from a fixed 40MHz input clock.
+ *
+ * cpuFreq = InputClock * MULT (where MULT is PLL multiplier)
+ * sysFreq = cpuFreq / 4 (used for APB clock, serial,
+ * flash, Timer, Watchdog Timer)
+ *
+ * cntFreq = cpuFreq / 2 (use for CPU count/compare)
+ *
+ * So, for example, with a PLL multiplier of 5, we have
+ *
+ * cpuFreq = 200MHz
+ * sysFreq = 50MHz
+ * cntFreq = 100MHz
+ *
+ * We compute the CPU frequency, based on PLL settings.
+ */
+
+ clock_ctl1 = __raw_readl((char *)KSEG1ADDR(AR2312_CLOCKCTL1));
+ pre_divide_select = (clock_ctl1 & predivide_mask) >> predivide_shift;
+ pre_divisor = CLOCKCTL1_PREDIVIDE_TABLE[pre_divide_select];
+ multiplier = (clock_ctl1 & multiplier_mask) >> multiplier_shift;
+
+ if (clock_ctl1 & doubler_mask)
+ multiplier = multiplier << 1;
+
+ return (40000000 / pre_divisor) * multiplier;
+}
+
+static unsigned int
+ar2312_sys_frequency(void)
+{
+ return ar2312_cpu_frequency() / 4;
+}
+
+/*
+ * shutdown watchdog
+ */
+static int watchdog_init(void)
+{
+ pr_debug("Disable watchdog.\n");
+ __raw_writeb(AR2312_WD_CTRL_IGNORE_EXPIRATION,
+ (char *)KSEG1ADDR(AR2312_WD_CTRL));
+ return 0;
+}
+
+static void flash_init(void)
+{
+ u32 ctl, old_ctl;
+
+ /* Configure flash bank 0.
+ * Assume 8M maximum window size on this SoC.
+ * Flash will be aliased if it's smaller
+ */
+ old_ctl = __raw_readl((char *)KSEG1ADDR(AR2312_FLASHCTL0));
+ ctl = FLASHCTL_E | FLASHCTL_AC_8M | FLASHCTL_RBLE |
+ (0x01 << FLASHCTL_IDCY_S) |
+ (0x07 << FLASHCTL_WST1_S) |
+ (0x07 << FLASHCTL_WST2_S) | (old_ctl & FLASHCTL_MW);
+
+ __raw_writel(ctl, (char *)KSEG1ADDR(AR2312_FLASHCTL0));
+ /* Disable other flash banks */
+ old_ctl = __raw_readl((char *)KSEG1ADDR(AR2312_FLASHCTL1));
+ __raw_writel(old_ctl & ~(FLASHCTL_E | FLASHCTL_AC),
+ (char *)KSEG1ADDR(AR2312_FLASHCTL1));
+
+ old_ctl = __raw_readl((char *)KSEG1ADDR(AR2312_FLASHCTL2));
+ __raw_writel(old_ctl & ~(FLASHCTL_E | FLASHCTL_AC),
+ (char *)KSEG1ADDR(AR2312_FLASHCTL2));
+
+ /* We need to find atheros config. MAC address is there. */
+ ar231x_find_config((char *)KSEG1ADDR(AR2312_FLASH +
+ AR2312_MAX_FLASH_SIZE));
+}
+
+static int ether_init(void)
+{
+ static struct resource res[2];
+ struct ar231x_eth_platform_data *eth = &ar231x_board.eth_pdata;
+
+ /* Base ETH registers */
+ res[0].start = KSEG1ADDR(AR2312_ENET1);
+ res[0].end = res[0].start + 0x100000 - 1;
+ res[0].flags = IORESOURCE_MEM;
+ /* Base PHY registers */
+ res[1].start = KSEG1ADDR(AR2312_ENET0);
+ res[1].end = res[1].start + 0x100000 - 1;
+ res[1].flags = IORESOURCE_MEM;
+
+ /* MAC address located in atheros config on flash. */
+ eth->mac = ar231x_board.config->enet0_mac;
+
+ eth->reset_mac = AR2312_RESET_ENET0 | AR2312_RESET_ENET1;
+ eth->reset_phy = AR2312_RESET_EPHY0 | AR2312_RESET_EPHY1;
+
+ eth->reset_bit = ar231x_reset_bit;
+
+ /* FIXME: base_reset should be replaced with reset driver */
+ eth->base_reset = KSEG1ADDR(AR2312_RESET);
+
+ add_generic_device_res("ar231x_eth", DEVICE_ID_DYNAMIC, res, 2, eth);
+ return 0;
+}
+
+static int platform_init(void)
+{
+ add_generic_device("ar231x_reset", DEVICE_ID_SINGLE, NULL,
+ KSEG1ADDR(AR2312_RESET), 0x4,
+ IORESOURCE_MEM, NULL);
+ watchdog_init();
+ flash_init();
+ ether_init();
+ return 0;
+}
+late_initcall(platform_init);
+
+static struct NS16550_plat serial_plat = {
+ .shift = AR2312_UART_SHIFT,
+};
+
+static int ar2312_console_init(void)
+{
+ u32 reset;
+
+ /* reset UART0 */
+ reset = __raw_readl((char *)KSEG1ADDR(AR2312_RESET));
+ reset = ((reset & ~AR2312_RESET_APB) | AR2312_RESET_UART0);
+ __raw_writel(reset, (char *)KSEG1ADDR(AR2312_RESET));
+
+ reset &= ~AR2312_RESET_UART0;
+ __raw_writel(reset, (char *)KSEG1ADDR(AR2312_RESET));
+
+ /* Register the serial port */
+ serial_plat.clock = ar2312_sys_frequency();
+ add_ns16550_device(DEVICE_ID_DYNAMIC, KSEG1ADDR(AR2312_UART0),
+ 8 << AR2312_UART_SHIFT, IORESOURCE_MEM_8BIT, &serial_plat);
+ return 0;
+}
+console_initcall(ar2312_console_init);