[PATCH] Forward porting ST40 time calcs from 2.4

Alex Bennee <[email protected]> Wed, 05 May 2004 17:37:33 +0100
Newsgroups gmane.linux.ports.sh.devel,gmane.linux.ports.sh.general
Organization Hackers Inc
Message-ID <[email protected]>
Hi,

With the 2.6 kernel running on an ST40 based board the kernel
miscalculated the CPU speed and completely failed to set Memory Clock to
something sane. The 2.4 kernel has a rather ugly hack to read the
various bits out which I've forward ported to 2.6. I've tested on my
ST40 board and it seems to work ok. In theory if run on a non ST40 board
the check should fall through and everything compute as before.

If no-one objects I'll commit into 2.6 in the next few days.

Index: time.c
===================================================================
RCS file: /cvsroot/linuxsh/linux/arch/sh/kernel/time.c,v
retrieving revision 1.21
diff -u -b -r1.21 time.c
--- time.c	21 Apr 2004 00:09:15 -0000	1.21
+++ time.c	5 May 2004 16:33:05 -0000
@@ -431,16 +431,126 @@
 _FREQ_TABLE(bfc);
 _FREQ_TABLE(pfc);
 
+#ifdef CONFIG_CPU_SUBTYPE_ST40STB1
+
+/* The ST40 divisors are totally different so we set the cpu data
+** clocks using a different algorithm
+**
+** I've just plugged this from the 2.4 code - Alex Bennee
<[email protected]>
+*/
+#define CCN_PVR_CHIP_SHIFT 24
+#define CCN_PVR_CHIP_MASK  0xff
+#define CCN_PVR_CHIP_ST40STB1 0x4
+
+
+struct frqcr_data {
+    unsigned short frqcr;
+    struct {
+	unsigned char multiplier;
+	unsigned char divisor;
+    } factor[3];
+};
+
+static struct frqcr_data st40_frqcr_table[] = {		
+    { 0x000, {{1,1}, {1,1}, {1,2}}},
+    { 0x002, {{1,1}, {1,1}, {1,4}}},
+    { 0x004, {{1,1}, {1,1}, {1,8}}},
+    { 0x008, {{1,1}, {1,2}, {1,2}}},
+    { 0x00A, {{1,1}, {1,2}, {1,4}}},
+    { 0x00C, {{1,1}, {1,2}, {1,8}}},
+    { 0x011, {{1,1}, {2,3}, {1,6}}},
+    { 0x013, {{1,1}, {2,3}, {1,3}}},
+    { 0x01A, {{1,1}, {1,2}, {1,4}}},
+    { 0x01C, {{1,1}, {1,2}, {1,8}}},
+    { 0x023, {{1,1}, {2,3}, {1,3}}},
+    { 0x02C, {{1,1}, {1,2}, {1,8}}},
+    { 0x048, {{1,2}, {1,2}, {1,4}}},
+    { 0x04A, {{1,2}, {1,2}, {1,6}}},
+    { 0x04C, {{1,2}, {1,2}, {1,8}}},
+    { 0x05A, {{1,2}, {1,3}, {1,6}}},
+    { 0x05C, {{1,2}, {1,3}, {1,6}}},
+    { 0x063, {{1,2}, {1,4}, {1,4}}},
+    { 0x06C, {{1,2}, {1,4}, {1,8}}},
+    { 0x091, {{1,3}, {1,3}, {1,6}}},
+    { 0x093, {{1,3}, {1,3}, {1,6}}},
+    { 0x0A3, {{1,3}, {1,6}, {1,6}}},
+    { 0x0DA, {{1,4}, {1,4}, {1,8}}},
+    { 0x0DC, {{1,4}, {1,4}, {1,8}}},
+    { 0x0EC, {{1,4}, {1,8}, {1,8}}},
+    { 0x123, {{1,4}, {1,4}, {1,8}}},
+    { 0x16C, {{1,4}, {1,8}, {1,8}}},
+};
+
+struct memclk_data {
+    unsigned char multiplier;
+    unsigned char divisor;
+};
+static struct memclk_data st40_memclk_table[8] = {
+    {1,1},	// 000
+    {1,2},	// 001
+    {1,3},	// 010
+    {2,3},	// 011
+    {1,4},	// 100
+    {1,6},	// 101
+    {1,8},	// 110
+    {1,8}	// 111
+};
+
+static void st40_specific_time_init(unsigned int module_clock, unsigned
short frqcr)
+{
+    unsigned int cpu_clock, master_clock, bus_clock, memory_clock;
+    struct frqcr_data *d;
+    int a;
+    unsigned long memclkcr;
+    struct memclk_data *e;
+
+    for (a=0; a<ARRAY_SIZE(st40_frqcr_table); a++) {
+	d = &st40_frqcr_table[a];
+	if (d->frqcr == (frqcr & 0x1ff))
+	    break;
+    }
+    if (a == ARRAY_SIZE(st40_frqcr_table)) {
+	d = st40_frqcr_table;
+	printk("ERROR: Unrecognised FRQCR value (0x%x), using default
multipliers\n",frqcr);
+    }
+
+    memclkcr = ctrl_inl(CLOCKGEN_MEMCLKCR);
+    e = &st40_memclk_table[memclkcr & MEMCLKCR_RATIO_MASK];
+
+    printk("Clock multipliers: CPU: %d/%d Bus: %d/%d Mem: %d/%d Periph:
%d/%d\n",
+	    d->factor[0].multiplier, d->factor[0].divisor,
+	    d->factor[1].multiplier, d->factor[1].divisor,
+	    e->multiplier,           e->divisor,
+	    d->factor[2].multiplier, d->factor[2].divisor);
+			
+    master_clock = module_clock * d->factor[2].divisor    /
d->factor[2].multiplier;
+    bus_clock    = master_clock * d->factor[1].multiplier /
d->factor[1].divisor;
+    memory_clock = master_clock * e->multiplier           / e->divisor;
+    cpu_clock    = master_clock * d->factor[0].multiplier /
d->factor[0].divisor;
+
+    current_cpu_data.cpu_clock    = cpu_clock;
+    current_cpu_data.master_clock = master_clock;
+    current_cpu_data.bus_clock    = bus_clock;
+    current_cpu_data.memory_clock = memory_clock;
+    current_cpu_data.module_clock = module_clock;
+
+}
+
+#endif
+
 void __init time_init(void)
 {
 	unsigned int timer_freq = 0;
 	unsigned int ifc, pfc, bfc;
 	unsigned long interval;
+#ifdef CONFIG_CPU_SUBTYPE_ST40STB1
+	unsigned long pvr;
+	unsigned short frqcr;
+#endif
 
 	if (board_time_init)
 		board_time_init();
 
-	get_current_frequency_divisors(&ifc, &bfc, &pfc);
 
 	/*
 	 * If we don't have an RTC (such as with the SH7300), don't attempt to
@@ -478,6 +588,16 @@
 	}
 #endif
 
+#ifdef CONFIG_CPU_SUBTYPE_ST40STB1
+	pvr = ctrl_inl(CCN_PVR);
+	frqcr = ctrl_inw(FRQCR);
+	printk("time.c ST40 Probe: PVR %08lx, FRQCR %04hx\n", pvr, frqcr);
+	if (((pvr >>CCN_PVR_CHIP_SHIFT) & CCN_PVR_CHIP_MASK) ==
CCN_PVR_CHIP_ST40STB1)
+	    st40_specific_time_init(current_cpu_data.module_clock, frqcr);
+	else
+#endif
+	    get_current_frequency_divisors(&ifc, &bfc, &pfc);
+
 	rtc_get_time(&xtime);
 
         set_normalized_timespec(&wall_to_monotonic,
@@ -489,6 +609,10 @@
 		setup_irq(TIMER_IRQ, &irq0);
 	}
 
+	/*
+	** for ST40 chips the current_cpu_data should already be set
+	** so not having valid pfc/bfc/ifc shouldn't be a problem
+	*/
 	if (!current_cpu_data.master_clock)
 		current_cpu_data.master_clock = current_cpu_data.module_clock * pfc;
 	if (!current_cpu_data.bus_clock)



-- 
Alex, homepage: http://www.bennee.com/~alex/
IN MY OPINION anyone interested in improving himself should not rule out
becoming pure energy.
		-- Jack Handley, The New Mexican, 1988.



-------------------------------------------------------
This SF.Net email is sponsored by Sleepycat Software
Learn developer strategies Cisco, Motorola, Ericsson & Lucent use to 
deliver higher performing products faster, at low TCO.
http://www.sleepycat.com/telcomwpreg.php?From=osdnemail3