aboutsummaryrefslogtreecommitdiffstats
path: root/arch/sh/kernel/cpufreq.c
blob: e0590ffebd73f546af41b9a47453ab7eb09543ae (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
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
/*
 * arch/sh/kernel/cpufreq.c
 *
 * cpufreq driver for the SuperH processors.
 *
 * Copyright (C) 2002 - 2007 Paul Mundt
 * Copyright (C) 2002 M. R. Brown
 *
 * Clock framework bits from arch/avr32/mach-at32ap/cpufreq.c
 *
 *   Copyright (C) 2004-2007 Atmel Corporation
 *
 * This file is subject to the terms and conditions of the GNU General Public
 * License.  See the file "COPYING" in the main directory of this archive
 * for more details.
 */
#include <linux/types.h>
#include <linux/cpufreq.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/err.h>
#include <linux/cpumask.h>
#include <linux/smp.h>
#include <linux/sched.h>	/* set_cpus_allowed() */
#include <linux/clk.h>

static struct clk *cpuclk;

static unsigned int sh_cpufreq_get(unsigned int cpu)
{
	return (clk_get_rate(cpuclk) + 500) / 1000;
}

/*
 * Here we notify other drivers of the proposed change and the final change.
 */
static int sh_cpufreq_target(struct cpufreq_policy *policy,
			     unsigned int target_freq,
			     unsigned int relation)
{
	unsigned int cpu = policy->cpu;
	cpumask_t cpus_allowed;
	struct cpufreq_freqs freqs;
	long freq;

	if (!cpu_online(cpu))
		return -ENODEV;

	cpus_allowed = current->cpus_allowed;
	set_cpus_allowed(current, cpumask_of_cpu(cpu));

	BUG_ON(smp_processor_id() != cpu);

	/* Convert target_freq from kHz to Hz */
	freq = clk_round_rate(cpuclk, target_freq * 1000);

	if (freq < (policy->min * 1000) || freq > (policy->max * 1000))
		return -EINVAL;

	pr_debug("cpufreq: requested frequency %u Hz\n", target_freq * 1000);

	freqs.cpu	= cpu;
	freqs.old	= sh_cpufreq_get(cpu);
	freqs.new	= (freq + 500) / 1000;
	freqs.flags	= 0;

	cpufreq_notify_transition(&freqs, CPUFREQ_PRECHANGE);
	set_cpus_allowed(current, cpus_allowed);
	clk_set_rate(cpuclk, freq);
	cpufreq_notify_transition(&freqs, CPUFREQ_POSTCHANGE);

	pr_debug("cpufreq: set frequency %lu Hz\n", freq);

	return 0;
}

static int sh_cpufreq_cpu_init(struct cpufreq_policy *policy)
{
	if (!cpu_online(policy->cpu))
		return -ENODEV;

	cpuclk = clk_get(NULL, "cpu_clk");
	if (IS_ERR(cpuclk)) {
		printk(KERN_ERR "cpufreq: couldn't get CPU clk\n");
		return PTR_ERR(cpuclk);
	}

	/* cpuinfo and default policy values */
	policy->cpuinfo.min_freq = (clk_round_rate(cpuclk, 1) + 500) / 1000;
	policy->cpuinfo.max_freq = (clk_round_rate(cpuclk, ~0UL) + 500) / 1000;
	policy->cpuinfo.transition_latency = CPUFREQ_ETERNAL;

	policy->cur		= sh_cpufreq_get(policy->cpu);
	policy->min		= policy->cpuinfo.min_freq;
	policy->max		= policy->cpuinfo.max_freq;


	/*
	 * Catch the cases where the clock framework hasn't been wired up
	 * properly to support scaling.
	 */
	if (unlikely(policy->min == policy->max)) {
		printk(KERN_ERR "cpufreq: clock framework rate rounding "
		       "not supported on this CPU.\n");

		clk_put(cpuclk);
		return -EINVAL;
	}

	printk(KERN_INFO "cpufreq: Frequencies - Minimum %u.%03u MHz, "
	       "Maximum %u.%03u MHz.\n",
	       policy->min / 1000, policy->min % 1000,
	       policy->max / 1000, policy->max % 1000);

	return 0;
}

static int sh_cpufreq_verify(struct cpufreq_policy *policy)
{
	cpufreq_verify_within_limits(policy, policy->cpuinfo.min_freq,
				     policy->cpuinfo.max_freq);
	return 0;
}

static int sh_cpufreq_exit(struct cpufreq_policy *policy)
{
	clk_put(cpuclk);
	return 0;
}

static struct cpufreq_driver sh_cpufreq_driver = {
	.owner		= THIS_MODULE,
	.name		= "sh",
	.init		= sh_cpufreq_cpu_init,
	.verify		= sh_cpufreq_verify,
	.target		= sh_cpufreq_target,
	.get		= sh_cpufreq_get,
	.exit		= sh_cpufreq_exit,
};

static int __init sh_cpufreq_module_init(void)
{
	printk(KERN_INFO "cpufreq: SuperH CPU frequency driver.\n");
	return cpufreq_register_driver(&sh_cpufreq_driver);
}

static void __exit sh_cpufreq_module_exit(void)
{
	cpufreq_unregister_driver(&sh_cpufreq_driver);
}

module_init(sh_cpufreq_module_init);
module_exit(sh_cpufreq_module_exit);

MODULE_AUTHOR("Paul Mundt <lethal@linux-sh.org>");
MODULE_DESCRIPTION("cpufreq driver for SuperH");
MODULE_LICENSE("GPL");
CL_CMD_LBUS_TO_PCI (0x9<<24) /* aux commands */ #define PCL_CMD_NOP (0x0<<24) #define PCL_CMD_LOAD (0x3<<24) #define PCL_CMD_STOREQ (0x4<<24) #define PCL_CMD_STORED (0xb<<24) #define PCL_CMD_STORE0 (0x5<<24) #define PCL_CMD_STORE1 (0x6<<24) #define PCL_CMD_COMPARE (0xe<<24) #define PCL_CMD_SWAP_COMPARE (0xf<<24) #define PCL_CMD_ADD (0xd<<24) #define PCL_CMD_BRANCH (0x7<<24) /* BRANCH condition codes */ #define PCL_COND_DMARDY_SET (0x1<<20) #define PCL_COND_DMARDY_CLEAR (0x2<<20) #define PCL_GEN_INTR (1<<19) #define PCL_LAST_BUFF (1<<18) #define PCL_LAST_CMD (PCL_LAST_BUFF) #define PCL_WAITSTAT (1<<17) #define PCL_BIGENDIAN (1<<16) #define PCL_ISOMODE (1<<12) #define DMA0_PREV_PCL 0x100 #define DMA1_PREV_PCL 0x120 #define DMA2_PREV_PCL 0x140 #define DMA3_PREV_PCL 0x160 #define DMA4_PREV_PCL 0x180 #define DMA_PREV_PCL(chan) (DMA_BREG(DMA0_PREV_PCL, chan)) #define DMA0_CURRENT_PCL 0x104 #define DMA1_CURRENT_PCL 0x124 #define DMA2_CURRENT_PCL 0x144 #define DMA3_CURRENT_PCL 0x164 #define DMA4_CURRENT_PCL 0x184 #define DMA_CURRENT_PCL(chan) (DMA_BREG(DMA0_CURRENT_PCL, chan)) #define DMA0_CHAN_STAT 0x10c #define DMA1_CHAN_STAT 0x12c #define DMA2_CHAN_STAT 0x14c #define DMA3_CHAN_STAT 0x16c #define DMA4_CHAN_STAT 0x18c #define DMA_CHAN_STAT(chan) (DMA_BREG(DMA0_CHAN_STAT, chan)) /* CHAN_STATUS registers share bits */ #define DMA_CHAN_STAT_SELFID (1<<31) #define DMA_CHAN_STAT_ISOPKT (1<<30) #define DMA_CHAN_STAT_PCIERR (1<<29) #define DMA_CHAN_STAT_PKTERR (1<<28) #define DMA_CHAN_STAT_PKTCMPL (1<<27) #define DMA_CHAN_STAT_SPECIALACK (1<<14) #define DMA0_CHAN_CTRL 0x110 #define DMA1_CHAN_CTRL 0x130 #define DMA2_CHAN_CTRL 0x150 #define DMA3_CHAN_CTRL 0x170 #define DMA4_CHAN_CTRL 0x190 #define DMA_CHAN_CTRL(chan) (DMA_BREG(DMA0_CHAN_CTRL, chan)) /* CHAN_CTRL registers share bits */ #define DMA_CHAN_CTRL_ENABLE (1<<31) #define DMA_CHAN_CTRL_BUSY (1<<30) #define DMA_CHAN_CTRL_LINK (1<<29) #define DMA0_READY 0x114 #define DMA1_READY 0x134 #define DMA2_READY 0x154 #define DMA3_READY 0x174 #define DMA4_READY 0x194 #define DMA_READY(chan) (DMA_BREG(DMA0_READY, chan)) #define DMA_GLOBAL_REGISTER 0x908 #define FIFO_SIZES 0xa00 #define FIFO_CONTROL 0xa10 #define FIFO_CONTROL_GRF_FLUSH (1<<4) #define FIFO_CONTROL_ITF_FLUSH (1<<3) #define FIFO_CONTROL_ATF_FLUSH (1<<2) #define FIFO_XMIT_THRESHOLD 0xa14 #define DMA0_WORD0_CMP_VALUE 0xb00 #define DMA1_WORD0_CMP_VALUE 0xb10 #define DMA2_WORD0_CMP_VALUE 0xb20 #define DMA3_WORD0_CMP_VALUE 0xb30 #define DMA4_WORD0_CMP_VALUE 0xb40 #define DMA_WORD0_CMP_VALUE(chan) (DMA_SREG(DMA0_WORD0_CMP_VALUE, chan)) #define DMA0_WORD0_CMP_ENABLE 0xb04 #define DMA1_WORD0_CMP_ENABLE 0xb14 #define DMA2_WORD0_CMP_ENABLE 0xb24 #define DMA3_WORD0_CMP_ENABLE 0xb34 #define DMA4_WORD0_CMP_ENABLE 0xb44 #define DMA_WORD0_CMP_ENABLE(chan) (DMA_SREG(DMA0_WORD0_CMP_ENABLE, chan)) #define DMA0_WORD1_CMP_VALUE 0xb08 #define DMA1_WORD1_CMP_VALUE 0xb18 #define DMA2_WORD1_CMP_VALUE 0xb28 #define DMA3_WORD1_CMP_VALUE 0xb38 #define DMA4_WORD1_CMP_VALUE 0xb48 #define DMA_WORD1_CMP_VALUE(chan) (DMA_SREG(DMA0_WORD1_CMP_VALUE, chan)) #define DMA0_WORD1_CMP_ENABLE 0xb0c #define DMA1_WORD1_CMP_ENABLE 0xb1c #define DMA2_WORD1_CMP_ENABLE 0xb2c #define DMA3_WORD1_CMP_ENABLE 0xb3c #define DMA4_WORD1_CMP_ENABLE 0xb4c #define DMA_WORD1_CMP_ENABLE(chan) (DMA_SREG(DMA0_WORD1_CMP_ENABLE, chan)) /* word 1 compare enable flags */ #define DMA_WORD1_CMP_MATCH_OTHERBUS (1<<15) #define DMA_WORD1_CMP_MATCH_BROADCAST (1<<14) #define DMA_WORD1_CMP_MATCH_BUS_BCAST (1<<13) #define DMA_WORD1_CMP_MATCH_LOCAL_NODE (1<<12) #define DMA_WORD1_CMP_MATCH_EXACT (1<<11) #define DMA_WORD1_CMP_ENABLE_SELF_ID (1<<10) #define DMA_WORD1_CMP_ENABLE_MASTER (1<<8) #define LINK_ID 0xf00 #define LINK_ID_BUS(id) (id<<22) #define LINK_ID_NODE(id) (id<<16) #define LINK_CONTROL 0xf04 #define LINK_CONTROL_BUSY (1<<29) #define LINK_CONTROL_TX_ISO_EN (1<<26) #define LINK_CONTROL_RX_ISO_EN (1<<25) #define LINK_CONTROL_TX_ASYNC_EN (1<<24) #define LINK_CONTROL_RX_ASYNC_EN (1<<23) #define LINK_CONTROL_RESET_TX (1<<21) #define LINK_CONTROL_RESET_RX (1<<20) #define LINK_CONTROL_CYCMASTER (1<<11) #define LINK_CONTROL_CYCSOURCE (1<<10) #define LINK_CONTROL_CYCTIMEREN (1<<9) #define LINK_CONTROL_RCV_CMP_VALID (1<<7) #define LINK_CONTROL_SNOOP_ENABLE (1<<6) #define CYCLE_TIMER 0xf08 #define LINK_PHY 0xf0c #define LINK_PHY_READ (1<<31) #define LINK_PHY_WRITE (1<<30) #define LINK_PHY_ADDR(addr) (addr<<24) #define LINK_PHY_WDATA(data) (data<<16) #define LINK_PHY_RADDR(addr) (addr<<8) #define LINK_INT_STATUS 0xf14 #define LINK_INT_ENABLE 0xf18 /* status and enable have identical bit numbers */ #define LINK_INT_LINK_INT (1<<31) #define LINK_INT_PHY_TIME_OUT (1<<30) #define LINK_INT_PHY_REG_RCVD (1<<29) #define LINK_INT_PHY_BUSRESET (1<<28) #define LINK_INT_TX_RDY (1<<26) #define LINK_INT_RX_DATA_RDY (1<<25) #define LINK_INT_IT_STUCK (1<<20) #define LINK_INT_AT_STUCK (1<<19) #define LINK_INT_SNTRJ (1<<17) #define LINK_INT_HDR_ERR (1<<16) #define LINK_INT_TC_ERR (1<<15) #define LINK_INT_CYC_SEC (1<<11) #define LINK_INT_CYC_STRT (1<<10) #define LINK_INT_CYC_DONE (1<<9) #define LINK_INT_CYC_PEND (1<<8) #define LINK_INT_CYC_LOST (1<<7) #define LINK_INT_CYC_ARB_FAILED (1<<6) #define LINK_INT_GRF_OVER_FLOW (1<<5) #define LINK_INT_ITF_UNDER_FLOW (1<<4) #define LINK_INT_ATF_UNDER_FLOW (1<<3) #define LINK_INT_IARB_FAILED (1<<0)