diff options
author | Oleksij Rempel <linux@rempel-privat.de> | 2013-05-30 20:18:40 +0200 |
---|---|---|
committer | Sascha Hauer <s.hauer@pengutronix.de> | 2013-05-31 08:56:33 +0200 |
commit | 59fe549733fb21f1e669bea148dc8c7899890844 (patch) | |
tree | 73987d701aeac5d34f7a14ffbc3112e847d69ade /arch/mips/mach-ar231x/ar231x.c | |
parent | df308bd4bfadf8a5dd29f6a4f9a29cc459e00ad3 (diff) | |
download | barebox-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.c | 195 |
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); |