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