9
0
Fork 0
barebox/arch/mips/mach-ar231x/ar231x.c

198 lines
5.7 KiB
C

/*
* 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 | IORESOURCE_MEM_8BIT,
&serial_plat);
return 0;
}
console_initcall(ar2312_console_init);