summaryrefslogtreecommitdiffstats
path: root/arch/sparc/kernel/tadpole.c
blob: 4bbfe4e2676de094dc5513123fdbafce9e0eea7c (plain) (blame)
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
/* tadpole.c: Probing for the tadpole clock stopping h/w at boot time.
 *
 * Copyright (C) 1996 David Redman (djhr@tadpole.co.uk)
 */

#include <linux/string.h>
#include <linux/kernel.h>
#include <linux/sched.h>
#include <linux/init.h>

#include <asm/asi.h>
#include <asm/oplib.h>
#include <asm/io.h>

#include "kernel.h"

#define MACIO_SCSI_CSR_ADDR	0x78400000
#define MACIO_EN_DMA		0x00000200
#define CLOCK_INIT_DONE		1

static int clk_state;
static volatile unsigned char *clk_ctrl;

/* TODO - cpu_pwr_save is only assigned - cleanup potential. */
static void (*cpu_pwr_save)(void);

static inline unsigned int ldphys(unsigned int addr)
{
	unsigned long data;
    
	__asm__ __volatile__("\n\tlda [%1] %2, %0\n\t" : 
			     "=r" (data) :
			     "r" (addr), "i" (ASI_M_BYPASS));
	return data;
}

static void clk_init(void)
{
	__asm__ __volatile__("mov 0x6c, %%g1\n\t"
			     "mov 0x4c, %%g2\n\t"
			     "mov 0xdf, %%g3\n\t"
			     "stb %%g1, [%0+3]\n\t"
			     "stb %%g2, [%0+3]\n\t"
			     "stb %%g3, [%0+3]\n\t" : :
			     "r" (clk_ctrl) :
			     "g1", "g2", "g3");
}

static void clk_slow(void)
{
	__asm__ __volatile__("mov 0xcc, %%g2\n\t"
			     "mov 0x4c, %%g3\n\t"
			     "mov 0xcf, %%g4\n\t"
			     "mov 0xdf, %%g5\n\t"
			     "stb %%g2, [%0+3]\n\t"
			     "stb %%g3, [%0+3]\n\t"
			     "stb %%g4, [%0+3]\n\t"
			     "stb %%g5, [%0+3]\n\t" : :
			     "r" (clk_ctrl) :
			     "g2", "g3", "g4", "g5");
}

/*
 * Tadpole is guaranteed to be UP, using local_irq_save.
 */
static void tsu_clockstop(void)
{
	unsigned int mcsr;
	unsigned long flags;

	if (!clk_ctrl)
		return;
	if (!(clk_state & CLOCK_INIT_DONE)) {
		local_irq_save(flags);
		clk_init();
		clk_state |= CLOCK_INIT_DONE;       /* all done */
		local_irq_restore(flags);
		return;
	}
	if (!(clk_ctrl[2] & 1))
		return;               /* no speed up yet */

	local_irq_save(flags);

	/* if SCSI DMA in progress, don't slow clock */
	mcsr = ldphys(MACIO_SCSI_CSR_ADDR);
	if ((mcsr&MACIO_EN_DMA) != 0) {
		local_irq_restore(flags);
		return;
	}
	/* TODO... the minimum clock setting ought to increase the
	 * memory refresh interval..
	 */
	clk_slow();
	local_irq_restore(flags);
}

static void swift_clockstop(void)
{
	if (!clk_ctrl)
		return;
	clk_ctrl[0] = 0;
}

void __init clock_stop_probe(void)
{
	phandle node, clk_nd;
	char name[20];
    
	prom_getstring(prom_root_node, "name", name, sizeof(name));
	if (strncmp(name, "Tadpole", 7))
		return;
	node = prom_getchild(prom_root_node);
	node = prom_searchsiblings(node, "obio");
	node = prom_getchild(node);
	clk_nd = prom_searchsiblings(node, "clk-ctrl");
	if (!clk_nd)
		return;
	printk("Clock Stopping h/w detected... ");
	clk_ctrl = (char *) prom_getint(clk_nd, "address");
	clk_state = 0;
	if (name[10] == '\0') {
		cpu_pwr_save = tsu_clockstop;
		printk("enabled (S3)\n");
	} else if ((name[10] == 'X') || (name[10] == 'G')) {
		cpu_pwr_save = swift_clockstop;
		printk("enabled (%s)\n",name+7);
	} else
		printk("disabled %s\n",name+7);
}