aboutsummaryrefslogtreecommitdiffstats
path: root/arch/mips/kernel/csrc-powertv.c
blob: 2e7c5232da8d72c09c399bacaf3ad45e2eaa726e (plain)
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
/*
 * Copyright (C) 2008 Scientific-Atlanta, Inc.
 *
 * 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.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
 */
/*
 * The file comes from kernel/csrc-r4k.c
 */
#include <linux/clocksource.h>
#include <linux/init.h>

#include <asm/time.h>			/* Not included in linux/time.h */

#include <asm/mach-powertv/asic_regs.h>
#include "powertv-clock.h"

/* MIPS PLL Register Definitions */
#define PLL_GET_M(x)		(((x) >> 8) & 0x000000FF)
#define PLL_GET_N(x)		(((x) >> 16) & 0x000000FF)
#define PLL_GET_P(x)		(((x) >> 24) & 0x00000007)

/*
 * returns:  Clock frequency in kHz
 */
unsigned int __init mips_get_pll_freq(void)
{
	unsigned int pll_reg, m, n, p;
	unsigned int fin = 54000; /* Base frequency in kHz */
	unsigned int fout;

	/* Read PLL register setting */
	pll_reg = asic_read(mips_pll_setup);
	m = PLL_GET_M(pll_reg);
	n = PLL_GET_N(pll_reg);
	p = PLL_GET_P(pll_reg);
	pr_info("MIPS PLL Register:0x%x  M=%d  N=%d  P=%d\n", pll_reg, m, n, p);

	/* Calculate clock frequency = (2 * N * 54MHz) / (M * (2**P)) */
	fout = ((2 * n * fin) / (m * (0x01 << p)));

	pr_info("MIPS Clock Freq=%d kHz\n", fout);

	return fout;
}

static cycle_t c0_hpt_read(struct clocksource *cs)
{
	return read_c0_count();
}

static struct clocksource clocksource_mips = {
	.name		= "powertv-counter",
	.read		= c0_hpt_read,
	.mask		= CLOCKSOURCE_MASK(32),
	.flags		= CLOCK_SOURCE_IS_CONTINUOUS,
};

static void __init powertv_c0_hpt_clocksource_init(void)
{
	unsigned int pll_freq = mips_get_pll_freq();

	pr_info("CPU frequency %d.%02d MHz\n", pll_freq / 1000,
		(pll_freq % 1000) * 100 / 1000);

	mips_hpt_frequency = pll_freq / 2 * 1000;

	clocksource_mips.rating = 200 + mips_hpt_frequency / 10000000;

	clocksource_register_hz(&clocksource_mips, mips_hpt_frequency);
}

/**
 * struct tim_c - free running counter
 * @hi:	High 16 bits of the counter
 * @lo:	Low 32 bits of the counter
 *
 * Lays out the structure of the free running counter in memory. This counter
 * increments at a rate of 27 MHz/8 on all platforms.
 */
struct tim_c {
	unsigned int hi;
	unsigned int lo;
};

static struct tim_c *tim_c;

static cycle_t tim_c_read(struct clocksource *cs)
{
	unsigned int hi;
	unsigned int next_hi;
	unsigned int lo;

	hi = readl(&tim_c->hi);

	for (;;) {
		lo = readl(&tim_c->lo);
		next_hi = readl(&tim_c->hi);
		if (next_hi == hi)
			break;
		hi = next_hi;
	}

pr_crit("%s: read %llx\n", __func__, ((u64) hi << 32) | lo);
	return ((u64) hi << 32) | lo;
}

#define TIM_C_SIZE		48		/* # bits in the timer */

static struct clocksource clocksource_tim_c = {
	.name		= "powertv-tim_c",
	.read		= tim_c_read,
	.mask		= CLOCKSOURCE_MASK(TIM_C_SIZE),
	.flags		= CLOCK_SOURCE_IS_CONTINUOUS,
};

/**
 * powertv_tim_c_clocksource_init - set up a clock source for the TIM_C clock
 *
 * We know that TIM_C counts at 27 MHz/8, so each cycle corresponds to
 * 1 / (27,000,000/8) seconds.
 */
static void __init powertv_tim_c_clocksource_init(void)
{
	const unsigned long	counts_per_second = 27000000 / 8;

	clocksource_tim_c.rating = 200;

	clocksource_register_hz(&clocksource_tim_c, counts_per_second);
	tim_c = (struct tim_c *) asic_reg_addr(tim_ch);
}

/**
 powertv_clocksource_init - initialize all clocksources
 */
void __init powertv_clocksource_init(void)
{
	powertv_c0_hpt_clocksource_init();
	powertv_tim_c_clocksource_init();
}