#include /* clocksource_register_khz */ #include /* sched_clock_register */ #include /* clk_register_fixed_rate */ #include /* clk_register_clkdev */ #include /* register_current_timer_delay */ #include /* twd_local_timer_register */ #include /* scu_a9_get_base */ #define FAST_RAMP 1 #define XTAL_FREQ 27000000 /* in Hz */ #define CLKGEN_BASE 0x10000 #define SYS_clkgen0_pll (clkgen_base + 0x00) #define SYS_cpuclk_div_ctrl (clkgen_base + 0x24) #define SYS_xtal_in_cnt (clkgen_base + 0x48) static void __iomem *clkgen_base; static unsigned long read_xtal_counter(void) { return readl_relaxed(SYS_xtal_in_cnt); } static u64 read_sched_clock(void) { return read_xtal_counter(); } static cycle_t read_clocksource(struct clocksource *cs) { return read_xtal_counter(); } static struct clocksource tangox_xtal = { .name = "tangox_xtal", .rating = 300, .read = read_clocksource, .mask = CLOCKSOURCE_MASK(32), .flags = CLOCK_SOURCE_IS_CONTINUOUS, }; static struct delay_timer delay_timer = { read_xtal_counter, XTAL_FREQ }; #define pi_alert(format, ...) do { \ static char fmt[] __initdata = KERN_ALERT format; \ printk(fmt, ## __VA_ARGS__); \ } while (0) static int __init wrap_local_timer_register(void) { unsigned long twd_base = scu_a9_get_base() + 0x600; struct twd_local_timer tangox_twd = {{ DEFINE_RES_MEM(twd_base, 16), DEFINE_RES_IRQ(29) }}; return twd_local_timer_register(&tangox_twd); } #define REG(name, ...) union name { struct { u32 __VA_ARGS__; }; u32 val; } REG(SYS_clkgen_pll, N:7, :6, K:3, M:3, :5, Isel:3, :3, T:1, B:1); /* * CG0, CG1, CG2, CG3 PLL Control: * ------------------------------- * * | Byte 3 | Byte 2 | Byte 1 | Byte 0 | * |3 3 2 2 2 2 2 2|2 2 2 2 1 1 1 1|1 1 1 1 1 1 | | * |1 0 9 8 7 6 5 4|3 2 1 0 9 8 7 6|5 4 3 2 1 0 9 8|7 6 5 4 3 2 1 0| * |-|-|-----|-----|---------|-----|-----|---------|-|-------------| * |B|T|xxxxx|Isel |xxxxxxxxx| M | K |xxxxxxxxx|x| N | * |-|-|-----|-----|---------|-----|-----|---------|-|-------------| * * These registers are used to configure the PLL parameters: * * Bits 6 to 0: N[6:0]. Default = 29 * Bits 15 to 13: K[2:0]. Default = 1 * Bit 18 to 16: M[2:0]. Default = 0 * Bits 26 to 24: Isel[2:0] (PLL Input Select). Default = 1 * Bits 30 : T (PLL Test). Default = 0 * Bits 31 : B (PLL Bypass). Default = 0 * * PLL0 : Out = In * (N+1) / (M+1) / 2^K * PLL1 : Same as PLL0 * PLL2 : Same as PLL0 * Default values : All PLLs configured to output 405MHz. */ static void __init tangox_clock_tree_register(void) { struct clk *clk; unsigned int mul, div; union SYS_clkgen_pll pll; pll.val = readl_relaxed(SYS_clkgen0_pll); mul = pll.N + 1; div = (pll.M + 1) << pll.K; if (pll.Isel != 1) pi_alert("PLL0 source is not XTAL_IN!\n"); clk = clk_register_fixed_rate(0, "XTAL", 0, CLK_IS_ROOT, XTAL_FREQ); if (!clk) pi_alert("Failed to register %s clk!\n", "XTAL"); clk = clk_register_fixed_factor(0, "PLL0", "XTAL", 0, mul, div); if (!clk) pi_alert("Failed to register %s clk!\n", "PLL0"); clk = clk_register_divider(0, "CPU_CLK", "PLL0", 0, SYS_cpuclk_div_ctrl, 8, 8, CLK_DIVIDER_ONE_BASED, 0); if (!clk) pi_alert("Failed to register %s clk!\n", "CPU_CLK"); clk_register_clkdev(clk, NULL, "cpu_clk"); clk = clk_register_fixed_factor(0, "PERIPHCLK", "CPU_CLK", 0, 1, 2); if (!clk) pi_alert("Failed to register %s clk!\n", "PERIPHCLK"); clk_register_clkdev(clk, NULL, "smp_twd"); writel_relaxed(FAST_RAMP << 21 | 1 << 8, SYS_cpuclk_div_ctrl); } void __init tangox_timer_init(void) { int err; clkgen_base = ioremap(CLKGEN_BASE, 0x100); if (clkgen_base == NULL) return; register_current_timer_delay(&delay_timer); sched_clock_register(read_sched_clock, 32, XTAL_FREQ); err = clocksource_register_hz(&tangox_xtal, XTAL_FREQ); if (err) pi_alert("Failed to register tangox_xtal clocksource!\n"); tangox_clock_tree_register(); err = wrap_local_timer_register(); if (err) pi_alert("Failed to register local timer!\n"); }