1 /* 2 * This program is free software; you can redistribute it and/or modify it 3 * under the terms of the GNU General Public License version 2 as published 4 * by the Free Software Foundation. 5 * 6 * Copyright (C) 2010 Thomas Langer <thomas.langer@lantiq.com> 7 * Copyright (C) 2010 John Crispin <blogic@openwrt.org> 8 */ 9 #include <linux/io.h> 10 #include <linux/module.h> 11 #include <linux/init.h> 12 #include <linux/kernel.h> 13 #include <linux/types.h> 14 #include <linux/clk.h> 15 #include <linux/err.h> 16 #include <linux/list.h> 17 18 #include <asm/time.h> 19 #include <asm/irq.h> 20 #include <asm/div64.h> 21 22 #include <lantiq_soc.h> 23 24 #include "clk.h" 25 26 struct clk { 27 const char *name; 28 unsigned long rate; 29 unsigned long (*get_rate) (void); 30 }; 31 32 static struct clk *cpu_clk; 33 static int cpu_clk_cnt; 34 35 /* lantiq socs have 3 static clocks */ 36 static struct clk cpu_clk_generic[] = { 37 { 38 .name = "cpu", 39 .get_rate = ltq_get_cpu_hz, 40 }, { 41 .name = "fpi", 42 .get_rate = ltq_get_fpi_hz, 43 }, { 44 .name = "io", 45 .get_rate = ltq_get_io_region_clock, 46 }, 47 }; 48 49 static struct resource ltq_cgu_resource = { 50 .name = "cgu", 51 .start = LTQ_CGU_BASE_ADDR, 52 .end = LTQ_CGU_BASE_ADDR + LTQ_CGU_SIZE - 1, 53 .flags = IORESOURCE_MEM, 54 }; 55 56 /* remapped clock register range */ 57 void __iomem *ltq_cgu_membase; 58 59 void clk_init(void) 60 { 61 cpu_clk = cpu_clk_generic; 62 cpu_clk_cnt = ARRAY_SIZE(cpu_clk_generic); 63 } 64 65 static inline int clk_good(struct clk *clk) 66 { 67 return clk && !IS_ERR(clk); 68 } 69 70 unsigned long clk_get_rate(struct clk *clk) 71 { 72 if (unlikely(!clk_good(clk))) 73 return 0; 74 75 if (clk->rate != 0) 76 return clk->rate; 77 78 if (clk->get_rate != NULL) 79 return clk->get_rate(); 80 81 return 0; 82 } 83 EXPORT_SYMBOL(clk_get_rate); 84 85 struct clk *clk_get(struct device *dev, const char *id) 86 { 87 int i; 88 89 for (i = 0; i < cpu_clk_cnt; i++) 90 if (!strcmp(id, cpu_clk[i].name)) 91 return &cpu_clk[i]; 92 BUG(); 93 return ERR_PTR(-ENOENT); 94 } 95 EXPORT_SYMBOL(clk_get); 96 97 void clk_put(struct clk *clk) 98 { 99 /* not used */ 100 } 101 EXPORT_SYMBOL(clk_put); 102 103 static inline u32 ltq_get_counter_resolution(void) 104 { 105 u32 res; 106 107 __asm__ __volatile__( 108 ".set push\n" 109 ".set mips32r2\n" 110 "rdhwr %0, $3\n" 111 ".set pop\n" 112 : "=&r" (res) 113 : /* no input */ 114 : "memory"); 115 116 return res; 117 } 118 119 void __init plat_time_init(void) 120 { 121 struct clk *clk; 122 123 if (insert_resource(&iomem_resource, <q_cgu_resource) < 0) 124 panic("Failed to insert cgu memory\n"); 125 126 if (request_mem_region(ltq_cgu_resource.start, 127 resource_size(<q_cgu_resource), "cgu") < 0) 128 panic("Failed to request cgu memory\n"); 129 130 ltq_cgu_membase = ioremap_nocache(ltq_cgu_resource.start, 131 resource_size(<q_cgu_resource)); 132 if (!ltq_cgu_membase) { 133 pr_err("Failed to remap cgu memory\n"); 134 unreachable(); 135 } 136 clk = clk_get(0, "cpu"); 137 mips_hpt_frequency = clk_get_rate(clk) / ltq_get_counter_resolution(); 138 write_c0_compare(read_c0_count()); 139 clk_put(clk); 140 } 141