Blame view

drivers/cpufreq/pmac64-cpufreq.c 17.6 KB
4350147a8   Benjamin Herrenschmidt   [PATCH] ppc64: SM...
1
2
3
4
5
6
7
8
9
10
11
  /*
   *  Copyright (C) 2002 - 2005 Benjamin Herrenschmidt <benh@kernel.crashing.org>
   *  and                       Markus Demleitner <msdemlei@cl.uni-heidelberg.de>
   *
   * 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.
   *
   * This driver adds basic cpufreq support for SMU & 970FX based G5 Macs,
   * that is iMac G5 and latest single CPU desktop.
   */
7ed14c217   Benjamin Herrenschmidt   [POWERPC] Add cpu...
12
  #undef DEBUG
1c5864e26   Joe Perches   cpufreq: Use cons...
13
  #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
4350147a8   Benjamin Herrenschmidt   [PATCH] ppc64: SM...
14
15
16
17
18
19
  #include <linux/module.h>
  #include <linux/types.h>
  #include <linux/errno.h>
  #include <linux/kernel.h>
  #include <linux/delay.h>
  #include <linux/sched.h>
4350147a8   Benjamin Herrenschmidt   [PATCH] ppc64: SM...
20
21
22
  #include <linux/cpufreq.h>
  #include <linux/init.h>
  #include <linux/completion.h>
14cc3e2b6   Ingo Molnar   [PATCH] sem2mutex...
23
  #include <linux/mutex.h>
760287ab9   Sudeep KarkadaNagesha   cpufreq: pmac64-c...
24
  #include <linux/of_device.h>
4350147a8   Benjamin Herrenschmidt   [PATCH] ppc64: SM...
25
26
27
28
29
30
31
  #include <asm/prom.h>
  #include <asm/machdep.h>
  #include <asm/irq.h>
  #include <asm/sections.h>
  #include <asm/cputable.h>
  #include <asm/time.h>
  #include <asm/smu.h>
9a699aefa   Benjamin Herrenschmidt   [PATCH] 4/5 power...
32
  #include <asm/pmac_pfunc.h>
4350147a8   Benjamin Herrenschmidt   [PATCH] ppc64: SM...
33

7ed14c217   Benjamin Herrenschmidt   [POWERPC] Add cpu...
34
  #define DBG(fmt...) pr_debug(fmt)
4350147a8   Benjamin Herrenschmidt   [PATCH] ppc64: SM...
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
  
  /* see 970FX user manual */
  
  #define SCOM_PCR 0x0aa001			/* PCR scom addr */
  
  #define PCR_HILO_SELECT		0x80000000U	/* 1 = PCR, 0 = PCRH */
  #define PCR_SPEED_FULL		0x00000000U	/* 1:1 speed value */
  #define PCR_SPEED_HALF		0x00020000U	/* 1:2 speed value */
  #define PCR_SPEED_QUARTER	0x00040000U	/* 1:4 speed value */
  #define PCR_SPEED_MASK		0x000e0000U	/* speed mask */
  #define PCR_SPEED_SHIFT		17
  #define PCR_FREQ_REQ_VALID	0x00010000U	/* freq request valid */
  #define PCR_VOLT_REQ_VALID	0x00008000U	/* volt request valid */
  #define PCR_TARGET_TIME_MASK	0x00006000U	/* target time */
  #define PCR_STATLAT_MASK	0x00001f00U	/* STATLAT value */
  #define PCR_SNOOPLAT_MASK	0x000000f0U	/* SNOOPLAT value */
  #define PCR_SNOOPACC_MASK	0x0000000fU	/* SNOOPACC value */
  
  #define SCOM_PSR 0x408001			/* PSR scom addr */
  /* warning: PSR is a 64 bits register */
  #define PSR_CMD_RECEIVED	0x2000000000000000U   /* command received */
  #define PSR_CMD_COMPLETED	0x1000000000000000U   /* command completed */
  #define PSR_CUR_SPEED_MASK	0x0300000000000000U   /* current speed */
  #define PSR_CUR_SPEED_SHIFT	(56)
  
  /*
   * The G5 only supports two frequencies (Quarter speed is not supported)
   */
  #define CPUFREQ_HIGH                  0
  #define CPUFREQ_LOW                   1
  
  static struct cpufreq_frequency_table g5_cpu_freqs[] = {
7f4b04614   Viresh Kumar   cpufreq: create a...
67
68
69
  	{0, CPUFREQ_HIGH,	0},
  	{0, CPUFREQ_LOW,	0},
  	{0, 0,			CPUFREQ_TABLE_END},
4350147a8   Benjamin Herrenschmidt   [PATCH] ppc64: SM...
70
  };
4350147a8   Benjamin Herrenschmidt   [PATCH] ppc64: SM...
71
  /* Power mode data is an array of the 32 bits PCR values to use for
943ffb587   Adrian Bunk   spelling: s/retre...
72
   * the various frequencies, retrieved from the device-tree
4350147a8   Benjamin Herrenschmidt   [PATCH] ppc64: SM...
73
   */
4350147a8   Benjamin Herrenschmidt   [PATCH] ppc64: SM...
74
  static int g5_pmode_cur;
9a699aefa   Benjamin Herrenschmidt   [PATCH] 4/5 power...
75
76
77
  static void (*g5_switch_volt)(int speed_mode);
  static int (*g5_switch_freq)(int speed_mode);
  static int (*g5_query_freq)(void);
16962e7ce   Nick Piggin   powerpc: Estimate...
78
  static unsigned long transition_latency;
4350147a8   Benjamin Herrenschmidt   [PATCH] ppc64: SM...
79

e272a2853   Benjamin Herrenschmidt   [POWERPC] Add cpu...
80
  #ifdef CONFIG_PMAC_SMU
7ed14c217   Benjamin Herrenschmidt   [POWERPC] Add cpu...
81

9ca91e0fb   Stephen Rothwell   [POWERPC] silence...
82
  static const u32 *g5_pmode_data;
7ed14c217   Benjamin Herrenschmidt   [POWERPC] Add cpu...
83
  static int g5_pmode_max;
4350147a8   Benjamin Herrenschmidt   [PATCH] ppc64: SM...
84
85
86
  static struct smu_sdbp_fvt *g5_fvt_table;	/* table of op. points */
  static int g5_fvt_count;			/* number of op. points */
  static int g5_fvt_cur;				/* current op. point */
9a699aefa   Benjamin Herrenschmidt   [PATCH] 4/5 power...
87
88
89
  /*
   * SMU based voltage switching for Neo2 platforms
   */
4350147a8   Benjamin Herrenschmidt   [PATCH] ppc64: SM...
90

9a699aefa   Benjamin Herrenschmidt   [PATCH] 4/5 power...
91
  static void g5_smu_switch_volt(int speed_mode)
4350147a8   Benjamin Herrenschmidt   [PATCH] ppc64: SM...
92
93
  {
  	struct smu_simple_cmd	cmd;
6e9a4738c   Peter Zijlstra   [PATCH] completio...
94
  	DECLARE_COMPLETION_ONSTACK(comp);
4350147a8   Benjamin Herrenschmidt   [PATCH] ppc64: SM...
95
96
97
98
99
  	smu_queue_simple(&cmd, SMU_CMD_POWER_COMMAND, 8, smu_done_complete,
  			 &comp, 'V', 'S', 'L', 'E', 'W',
  			 0xff, g5_fvt_cur+1, speed_mode);
  	wait_for_completion(&comp);
  }
9a699aefa   Benjamin Herrenschmidt   [PATCH] 4/5 power...
100
101
102
103
104
105
106
107
  /*
   * Platform function based voltage/vdnap switching for Neo2
   */
  
  static struct pmf_function *pfunc_set_vdnap0;
  static struct pmf_function *pfunc_vdnap0_complete;
  
  static void g5_vdnap_switch_volt(int speed_mode)
4350147a8   Benjamin Herrenschmidt   [PATCH] ppc64: SM...
108
  {
9a699aefa   Benjamin Herrenschmidt   [PATCH] 4/5 power...
109
110
111
  	struct pmf_args args;
  	u32 slew, done = 0;
  	unsigned long timeout;
4350147a8   Benjamin Herrenschmidt   [PATCH] ppc64: SM...
112

9a699aefa   Benjamin Herrenschmidt   [PATCH] 4/5 power...
113
114
115
  	slew = (speed_mode == CPUFREQ_LOW) ? 1 : 0;
  	args.count = 1;
  	args.u[0].p = &slew;
4350147a8   Benjamin Herrenschmidt   [PATCH] ppc64: SM...
116

9a699aefa   Benjamin Herrenschmidt   [PATCH] 4/5 power...
117
  	pmf_call_one(pfunc_set_vdnap0, &args);
4350147a8   Benjamin Herrenschmidt   [PATCH] ppc64: SM...
118

9a699aefa   Benjamin Herrenschmidt   [PATCH] 4/5 power...
119
120
121
122
123
124
125
126
127
128
129
  	/* It's an irq GPIO so we should be able to just block here,
  	 * I'll do that later after I've properly tested the IRQ code for
  	 * platform functions
  	 */
  	timeout = jiffies + HZ/10;
  	while(!time_after(jiffies, timeout)) {
  		args.count = 1;
  		args.u[0].p = &done;
  		pmf_call_one(pfunc_vdnap0_complete, &args);
  		if (done)
  			break;
45a428ebb   Aaro Koskinen   cpufreq: pmac64: ...
130
  		usleep_range(1000, 1000);
9a699aefa   Benjamin Herrenschmidt   [PATCH] 4/5 power...
131
132
  	}
  	if (done == 0)
1c5864e26   Joe Perches   cpufreq: Use cons...
133
134
  		pr_warn("Timeout in clock slewing !
  ");
9a699aefa   Benjamin Herrenschmidt   [PATCH] 4/5 power...
135
  }
4350147a8   Benjamin Herrenschmidt   [PATCH] ppc64: SM...
136

9a699aefa   Benjamin Herrenschmidt   [PATCH] 4/5 power...
137
138
139
140
141
142
143
144
  
  /*
   * SCOM based frequency switching for 970FX rev3
   */
  static int g5_scom_switch_freq(int speed_mode)
  {
  	unsigned long flags;
  	int to;
4350147a8   Benjamin Herrenschmidt   [PATCH] ppc64: SM...
145
146
147
148
  
  	/* If frequency is going up, first ramp up the voltage */
  	if (speed_mode < g5_pmode_cur)
  		g5_switch_volt(speed_mode);
9a699aefa   Benjamin Herrenschmidt   [PATCH] 4/5 power...
149
  	local_irq_save(flags);
4350147a8   Benjamin Herrenschmidt   [PATCH] ppc64: SM...
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
  	/* Clear PCR high */
  	scom970_write(SCOM_PCR, 0);
  	/* Clear PCR low */
         	scom970_write(SCOM_PCR, PCR_HILO_SELECT | 0);
  	/* Set PCR low */
  	scom970_write(SCOM_PCR, PCR_HILO_SELECT |
  		      g5_pmode_data[speed_mode]);
  
  	/* Wait for completion */
  	for (to = 0; to < 10; to++) {
  		unsigned long psr = scom970_read(SCOM_PSR);
  
  		if ((psr & PSR_CMD_RECEIVED) == 0 &&
  		    (((psr >> PSR_CUR_SPEED_SHIFT) ^
  		      (g5_pmode_data[speed_mode] >> PCR_SPEED_SHIFT)) & 0x3)
  		    == 0)
  			break;
  		if (psr & PSR_CMD_COMPLETED)
  			break;
  		udelay(100);
  	}
9a699aefa   Benjamin Herrenschmidt   [PATCH] 4/5 power...
171
  	local_irq_restore(flags);
4350147a8   Benjamin Herrenschmidt   [PATCH] ppc64: SM...
172
173
174
175
176
177
  	/* If frequency is going down, last ramp the voltage */
  	if (speed_mode > g5_pmode_cur)
  		g5_switch_volt(speed_mode);
  
  	g5_pmode_cur = speed_mode;
  	ppc_proc_freq = g5_cpu_freqs[speed_mode].frequency * 1000ul;
4350147a8   Benjamin Herrenschmidt   [PATCH] ppc64: SM...
178
179
  	return 0;
  }
9a699aefa   Benjamin Herrenschmidt   [PATCH] 4/5 power...
180
  static int g5_scom_query_freq(void)
4350147a8   Benjamin Herrenschmidt   [PATCH] ppc64: SM...
181
182
183
184
185
186
187
188
189
190
  {
  	unsigned long psr = scom970_read(SCOM_PSR);
  	int i;
  
  	for (i = 0; i <= g5_pmode_max; i++)
  		if ((((psr >> PSR_CUR_SPEED_SHIFT) ^
  		      (g5_pmode_data[i] >> PCR_SPEED_SHIFT)) & 0x3) == 0)
  			break;
  	return i;
  }
9a699aefa   Benjamin Herrenschmidt   [PATCH] 4/5 power...
191
  /*
7ed14c217   Benjamin Herrenschmidt   [POWERPC] Add cpu...
192
193
194
195
196
197
   * Fake voltage switching for platforms with missing support
   */
  
  static void g5_dummy_switch_volt(int speed_mode)
  {
  }
e272a2853   Benjamin Herrenschmidt   [POWERPC] Add cpu...
198
  #endif /* CONFIG_PMAC_SMU */
7ed14c217   Benjamin Herrenschmidt   [POWERPC] Add cpu...
199
200
  
  /*
9a699aefa   Benjamin Herrenschmidt   [PATCH] 4/5 power...
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
   * Platform function based voltage switching for PowerMac7,2 & 7,3
   */
  
  static struct pmf_function *pfunc_cpu0_volt_high;
  static struct pmf_function *pfunc_cpu0_volt_low;
  static struct pmf_function *pfunc_cpu1_volt_high;
  static struct pmf_function *pfunc_cpu1_volt_low;
  
  static void g5_pfunc_switch_volt(int speed_mode)
  {
  	if (speed_mode == CPUFREQ_HIGH) {
  		if (pfunc_cpu0_volt_high)
  			pmf_call_one(pfunc_cpu0_volt_high, NULL);
  		if (pfunc_cpu1_volt_high)
  			pmf_call_one(pfunc_cpu1_volt_high, NULL);
  	} else {
  		if (pfunc_cpu0_volt_low)
  			pmf_call_one(pfunc_cpu0_volt_low, NULL);
  		if (pfunc_cpu1_volt_low)
  			pmf_call_one(pfunc_cpu1_volt_low, NULL);
  	}
45a428ebb   Aaro Koskinen   cpufreq: pmac64: ...
222
  	usleep_range(10000, 10000); /* should be faster , to fix */
9a699aefa   Benjamin Herrenschmidt   [PATCH] 4/5 power...
223
224
225
226
227
228
229
230
231
  }
  
  /*
   * Platform function based frequency switching for PowerMac7,2 & 7,3
   */
  
  static struct pmf_function *pfunc_cpu_setfreq_high;
  static struct pmf_function *pfunc_cpu_setfreq_low;
  static struct pmf_function *pfunc_cpu_getfreq;
d258e64ef   Joe Perches   powerpc: Remove u...
232
  static struct pmf_function *pfunc_slewing_done;
9a699aefa   Benjamin Herrenschmidt   [PATCH] 4/5 power...
233
234
235
236
237
238
  
  static int g5_pfunc_switch_freq(int speed_mode)
  {
  	struct pmf_args args;
  	u32 done = 0;
  	unsigned long timeout;
7ed14c217   Benjamin Herrenschmidt   [POWERPC] Add cpu...
239
240
241
242
  	int rc;
  
  	DBG("g5_pfunc_switch_freq(%d)
  ", speed_mode);
9a699aefa   Benjamin Herrenschmidt   [PATCH] 4/5 power...
243
244
245
246
247
248
249
  
  	/* If frequency is going up, first ramp up the voltage */
  	if (speed_mode < g5_pmode_cur)
  		g5_switch_volt(speed_mode);
  
  	/* Do it */
  	if (speed_mode == CPUFREQ_HIGH)
7ed14c217   Benjamin Herrenschmidt   [POWERPC] Add cpu...
250
  		rc = pmf_call_one(pfunc_cpu_setfreq_high, NULL);
9a699aefa   Benjamin Herrenschmidt   [PATCH] 4/5 power...
251
  	else
7ed14c217   Benjamin Herrenschmidt   [POWERPC] Add cpu...
252
253
254
  		rc = pmf_call_one(pfunc_cpu_setfreq_low, NULL);
  
  	if (rc)
1c5864e26   Joe Perches   cpufreq: Use cons...
255
256
  		pr_warn("pfunc switch error %d
  ", rc);
9a699aefa   Benjamin Herrenschmidt   [PATCH] 4/5 power...
257
258
259
260
261
262
263
264
265
266
267
268
  
  	/* It's an irq GPIO so we should be able to just block here,
  	 * I'll do that later after I've properly tested the IRQ code for
  	 * platform functions
  	 */
  	timeout = jiffies + HZ/10;
  	while(!time_after(jiffies, timeout)) {
  		args.count = 1;
  		args.u[0].p = &done;
  		pmf_call_one(pfunc_slewing_done, &args);
  		if (done)
  			break;
45a428ebb   Aaro Koskinen   cpufreq: pmac64: ...
269
  		usleep_range(500, 500);
9a699aefa   Benjamin Herrenschmidt   [PATCH] 4/5 power...
270
271
  	}
  	if (done == 0)
1c5864e26   Joe Perches   cpufreq: Use cons...
272
273
  		pr_warn("Timeout in clock slewing !
  ");
9a699aefa   Benjamin Herrenschmidt   [PATCH] 4/5 power...
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
  
  	/* If frequency is going down, last ramp the voltage */
  	if (speed_mode > g5_pmode_cur)
  		g5_switch_volt(speed_mode);
  
  	g5_pmode_cur = speed_mode;
  	ppc_proc_freq = g5_cpu_freqs[speed_mode].frequency * 1000ul;
  
  	return 0;
  }
  
  static int g5_pfunc_query_freq(void)
  {
  	struct pmf_args args;
  	u32 val = 0;
  
  	args.count = 1;
  	args.u[0].p = &val;
  	pmf_call_one(pfunc_cpu_getfreq, &args);
  	return val ? CPUFREQ_HIGH : CPUFREQ_LOW;
  }
9a699aefa   Benjamin Herrenschmidt   [PATCH] 4/5 power...
295
296
297
298
  
  /*
   * Common interface to the cpufreq core
   */
4350147a8   Benjamin Herrenschmidt   [PATCH] ppc64: SM...
299

9c0ebcf78   Viresh Kumar   cpufreq: Implemen...
300
  static int g5_cpufreq_target(struct cpufreq_policy *policy, unsigned int index)
4350147a8   Benjamin Herrenschmidt   [PATCH] ppc64: SM...
301
  {
d4019f0a9   Viresh Kumar   cpufreq: move fre...
302
  	return g5_switch_freq(index);
4350147a8   Benjamin Herrenschmidt   [PATCH] ppc64: SM...
303
304
305
306
307
308
309
310
311
  }
  
  static unsigned int g5_cpufreq_get_speed(unsigned int cpu)
  {
  	return g5_cpu_freqs[g5_pmode_cur].frequency;
  }
  
  static int g5_cpufreq_cpu_init(struct cpufreq_policy *policy)
  {
8ce6f9de9   Viresh Kumar   cpufreq: pmac64: ...
312
  	return cpufreq_generic_init(policy, g5_cpu_freqs, transition_latency);
4350147a8   Benjamin Herrenschmidt   [PATCH] ppc64: SM...
313
  }
4350147a8   Benjamin Herrenschmidt   [PATCH] ppc64: SM...
314
315
  static struct cpufreq_driver g5_cpufreq_driver = {
  	.name		= "powermac",
4350147a8   Benjamin Herrenschmidt   [PATCH] ppc64: SM...
316
317
  	.flags		= CPUFREQ_CONST_LOOPS,
  	.init		= g5_cpufreq_cpu_init,
2633a46c5   Viresh Kumar   cpufreq: pmac: Us...
318
  	.verify		= cpufreq_generic_frequency_table_verify,
9c0ebcf78   Viresh Kumar   cpufreq: Implemen...
319
  	.target_index	= g5_cpufreq_target,
4350147a8   Benjamin Herrenschmidt   [PATCH] ppc64: SM...
320
  	.get		= g5_cpufreq_get_speed,
2633a46c5   Viresh Kumar   cpufreq: pmac: Us...
321
  	.attr 		= cpufreq_generic_attr,
4350147a8   Benjamin Herrenschmidt   [PATCH] ppc64: SM...
322
  };
e272a2853   Benjamin Herrenschmidt   [POWERPC] Add cpu...
323
  #ifdef CONFIG_PMAC_SMU
7ed14c217   Benjamin Herrenschmidt   [POWERPC] Add cpu...
324

760287ab9   Sudeep KarkadaNagesha   cpufreq: pmac64-c...
325
  static int __init g5_neo2_cpufreq_init(struct device_node *cpunode)
4350147a8   Benjamin Herrenschmidt   [PATCH] ppc64: SM...
326
  {
4350147a8   Benjamin Herrenschmidt   [PATCH] ppc64: SM...
327
  	unsigned int psize, ssize;
4350147a8   Benjamin Herrenschmidt   [PATCH] ppc64: SM...
328
  	unsigned long max_freq;
9a699aefa   Benjamin Herrenschmidt   [PATCH] 4/5 power...
329
  	char *freq_method, *volt_method;
018a3d1db   Jeremy Kerr   [POWERPC] powerma...
330
331
  	const u32 *valp;
  	u32 pvr_hi;
9a699aefa   Benjamin Herrenschmidt   [PATCH] 4/5 power...
332
333
  	int use_volts_vdnap = 0;
  	int use_volts_smu = 0;
4350147a8   Benjamin Herrenschmidt   [PATCH] ppc64: SM...
334
  	int rc = -ENODEV;
9a699aefa   Benjamin Herrenschmidt   [PATCH] 4/5 power...
335
  	/* Check supported platforms */
71a157e8e   Grant Likely   of: add 'of_' pre...
336
337
  	if (of_machine_is_compatible("PowerMac8,1") ||
  	    of_machine_is_compatible("PowerMac8,2") ||
891083622   Aaro Koskinen   cpufreq: pmac64: ...
338
339
  	    of_machine_is_compatible("PowerMac9,1") ||
  	    of_machine_is_compatible("PowerMac12,1"))
9a699aefa   Benjamin Herrenschmidt   [PATCH] 4/5 power...
340
  		use_volts_smu = 1;
71a157e8e   Grant Likely   of: add 'of_' pre...
341
  	else if (of_machine_is_compatible("PowerMac11,2"))
9a699aefa   Benjamin Herrenschmidt   [PATCH] 4/5 power...
342
343
344
  		use_volts_vdnap = 1;
  	else
  		return -ENODEV;
4350147a8   Benjamin Herrenschmidt   [PATCH] ppc64: SM...
345
  	/* Check 970FX for now */
e2eb63927   Stephen Rothwell   [POWERPC] Rename ...
346
  	valp = of_get_property(cpunode, "cpu-version", NULL);
4350147a8   Benjamin Herrenschmidt   [PATCH] ppc64: SM...
347
348
349
350
351
  	if (!valp) {
  		DBG("No cpu-version property !
  ");
  		goto bail_noprops;
  	}
9a699aefa   Benjamin Herrenschmidt   [PATCH] 4/5 power...
352
353
  	pvr_hi = (*valp) >> 16;
  	if (pvr_hi != 0x3c && pvr_hi != 0x44) {
1c5864e26   Joe Perches   cpufreq: Use cons...
354
355
  		pr_err("Unsupported CPU version
  ");
4350147a8   Benjamin Herrenschmidt   [PATCH] ppc64: SM...
356
357
358
359
  		goto bail_noprops;
  	}
  
  	/* Look for the powertune data in the device-tree */
e2eb63927   Stephen Rothwell   [POWERPC] Rename ...
360
  	g5_pmode_data = of_get_property(cpunode, "power-mode-data",&psize);
4350147a8   Benjamin Herrenschmidt   [PATCH] ppc64: SM...
361
362
363
364
365
366
  	if (!g5_pmode_data) {
  		DBG("No power-mode-data !
  ");
  		goto bail_noprops;
  	}
  	g5_pmode_max = psize / sizeof(u32) - 1;
9a699aefa   Benjamin Herrenschmidt   [PATCH] 4/5 power...
367
  	if (use_volts_smu) {
018a3d1db   Jeremy Kerr   [POWERPC] powerma...
368
  		const struct smu_sdbp_header *shdr;
9a699aefa   Benjamin Herrenschmidt   [PATCH] 4/5 power...
369
370
371
372
373
374
  
  		/* Look for the FVT table */
  		shdr = smu_get_sdb_partition(SMU_SDB_FVT_ID, NULL);
  		if (!shdr)
  			goto bail_noprops;
  		g5_fvt_table = (struct smu_sdbp_fvt *)&shdr[1];
d5b73cd87   Viresh Kumar   cpufreq: Use size...
375
376
  		ssize = (shdr->len * sizeof(u32)) - sizeof(*shdr);
  		g5_fvt_count = ssize / sizeof(*g5_fvt_table);
9a699aefa   Benjamin Herrenschmidt   [PATCH] 4/5 power...
377
378
379
380
381
382
383
384
385
386
387
388
389
  		g5_fvt_cur = 0;
  
  		/* Sanity checking */
  		if (g5_fvt_count < 1 || g5_pmode_max < 1)
  			goto bail_noprops;
  
  		g5_switch_volt = g5_smu_switch_volt;
  		volt_method = "SMU";
  	} else if (use_volts_vdnap) {
  		struct device_node *root;
  
  		root = of_find_node_by_path("/");
  		if (root == NULL) {
1c5864e26   Joe Perches   cpufreq: Use cons...
390
391
  			pr_err("Can't find root of device tree
  ");
9a699aefa   Benjamin Herrenschmidt   [PATCH] 4/5 power...
392
393
394
395
396
397
398
  			goto bail_noprops;
  		}
  		pfunc_set_vdnap0 = pmf_find_function(root, "set-vdnap0");
  		pfunc_vdnap0_complete =
  			pmf_find_function(root, "slewing-done");
  		if (pfunc_set_vdnap0 == NULL ||
  		    pfunc_vdnap0_complete == NULL) {
1c5864e26   Joe Perches   cpufreq: Use cons...
399
400
  			pr_err("Can't find required platform function
  ");
9a699aefa   Benjamin Herrenschmidt   [PATCH] 4/5 power...
401
402
403
404
405
406
407
408
409
  			goto bail_noprops;
  		}
  
  		g5_switch_volt = g5_vdnap_switch_volt;
  		volt_method = "GPIO";
  	} else {
  		g5_switch_volt = g5_dummy_switch_volt;
  		volt_method = "none";
  	}
4350147a8   Benjamin Herrenschmidt   [PATCH] ppc64: SM...
410
411
412
413
414
415
416
417
  
  	/*
  	 * From what I see, clock-frequency is always the maximal frequency.
  	 * The current driver can not slew sysclk yet, so we really only deal
  	 * with powertune steps for now. We also only implement full freq and
  	 * half freq in this version. So far, I haven't yet seen a machine
  	 * supporting anything else.
  	 */
e2eb63927   Stephen Rothwell   [POWERPC] Rename ...
418
  	valp = of_get_property(cpunode, "clock-frequency", NULL);
4350147a8   Benjamin Herrenschmidt   [PATCH] ppc64: SM...
419
420
421
422
423
  	if (!valp)
  		return -ENODEV;
  	max_freq = (*valp)/1000;
  	g5_cpu_freqs[0].frequency = max_freq;
  	g5_cpu_freqs[1].frequency = max_freq/2;
9a699aefa   Benjamin Herrenschmidt   [PATCH] 4/5 power...
424
  	/* Set callbacks */
16962e7ce   Nick Piggin   powerpc: Estimate...
425
  	transition_latency = 12000;
9a699aefa   Benjamin Herrenschmidt   [PATCH] 4/5 power...
426
427
428
  	g5_switch_freq = g5_scom_switch_freq;
  	g5_query_freq = g5_scom_query_freq;
  	freq_method = "SCOM";
4350147a8   Benjamin Herrenschmidt   [PATCH] ppc64: SM...
429
430
431
432
433
  
  	/* Force apply current frequency to make sure everything is in
  	 * sync (voltage is right for example). Firmware may leave us with
  	 * a strange setting ...
  	 */
9a699aefa   Benjamin Herrenschmidt   [PATCH] 4/5 power...
434
435
436
437
  	g5_switch_volt(CPUFREQ_HIGH);
  	msleep(10);
  	g5_pmode_cur = -1;
  	g5_switch_freq(g5_query_freq());
4350147a8   Benjamin Herrenschmidt   [PATCH] ppc64: SM...
438

b49c22a6c   Joe Perches   cpufreq: Convert ...
439
440
441
442
443
444
445
  	pr_info("Registering G5 CPU frequency driver
  ");
  	pr_info("Frequency method: %s, Voltage method: %s
  ",
  		freq_method, volt_method);
  	pr_info("Low: %d Mhz, High: %d Mhz, Cur: %d MHz
  ",
4350147a8   Benjamin Herrenschmidt   [PATCH] ppc64: SM...
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
  		g5_cpu_freqs[1].frequency/1000,
  		g5_cpu_freqs[0].frequency/1000,
  		g5_cpu_freqs[g5_pmode_cur].frequency/1000);
  
  	rc = cpufreq_register_driver(&g5_cpufreq_driver);
  
  	/* We keep the CPU node on hold... hopefully, Apple G5 don't have
  	 * hotplug CPU with a dynamic device-tree ...
  	 */
  	return rc;
  
   bail_noprops:
  	of_node_put(cpunode);
  
  	return rc;
  }
e272a2853   Benjamin Herrenschmidt   [POWERPC] Add cpu...
462
  #endif /* CONFIG_PMAC_SMU */
7ed14c217   Benjamin Herrenschmidt   [POWERPC] Add cpu...
463

760287ab9   Sudeep KarkadaNagesha   cpufreq: pmac64-c...
464
  static int __init g5_pm72_cpufreq_init(struct device_node *cpunode)
9a699aefa   Benjamin Herrenschmidt   [PATCH] 4/5 power...
465
  {
760287ab9   Sudeep KarkadaNagesha   cpufreq: pmac64-c...
466
  	struct device_node *cpuid = NULL, *hwclock = NULL;
018a3d1db   Jeremy Kerr   [POWERPC] powerma...
467
468
  	const u8 *eeprom = NULL;
  	const u32 *valp;
9a699aefa   Benjamin Herrenschmidt   [PATCH] 4/5 power...
469
470
  	u64 max_freq, min_freq, ih, il;
  	int has_volt = 1, rc = 0;
7ed14c217   Benjamin Herrenschmidt   [POWERPC] Add cpu...
471
472
473
  	DBG("cpufreq: Initializing for PowerMac7,2, PowerMac7,3 and"
  	    " RackMac3,1...
  ");
9a699aefa   Benjamin Herrenschmidt   [PATCH] 4/5 power...
474
475
476
  	/* Lookup the cpuid eeprom node */
          cpuid = of_find_node_by_path("/u3@0,f8000000/i2c@f8001000/cpuid@a0");
  	if (cpuid != NULL)
e2eb63927   Stephen Rothwell   [POWERPC] Rename ...
477
  		eeprom = of_get_property(cpuid, "cpuid", NULL);
9a699aefa   Benjamin Herrenschmidt   [PATCH] 4/5 power...
478
  	if (eeprom == NULL) {
1c5864e26   Joe Perches   cpufreq: Use cons...
479
480
  		pr_err("Can't find cpuid EEPROM !
  ");
9a699aefa   Benjamin Herrenschmidt   [PATCH] 4/5 power...
481
482
483
484
485
  		rc = -ENODEV;
  		goto bail;
  	}
  
  	/* Lookup the i2c hwclock */
ccdb8ed3b   Grant Likely   of: Migrate of_fi...
486
  	for_each_node_by_name(hwclock, "i2c-hwclock") {
e2eb63927   Stephen Rothwell   [POWERPC] Rename ...
487
  		const char *loc = of_get_property(hwclock,
018a3d1db   Jeremy Kerr   [POWERPC] powerma...
488
  				"hwctrl-location", NULL);
9a699aefa   Benjamin Herrenschmidt   [PATCH] 4/5 power...
489
490
491
492
  		if (loc == NULL)
  			continue;
  		if (strcmp(loc, "CPU CLOCK"))
  			continue;
e2eb63927   Stephen Rothwell   [POWERPC] Rename ...
493
  		if (!of_get_property(hwclock, "platform-get-frequency", NULL))
9a699aefa   Benjamin Herrenschmidt   [PATCH] 4/5 power...
494
495
496
497
  			continue;
  		break;
  	}
  	if (hwclock == NULL) {
1c5864e26   Joe Perches   cpufreq: Use cons...
498
499
  		pr_err("Can't find i2c clock chip !
  ");
9a699aefa   Benjamin Herrenschmidt   [PATCH] 4/5 power...
500
501
502
  		rc = -ENODEV;
  		goto bail;
  	}
cc5a7a749   Rob Herring   cpufreq: Convert ...
503
504
  	DBG("cpufreq: i2c clock chip found: %pOF
  ", hwclock);
9a699aefa   Benjamin Herrenschmidt   [PATCH] 4/5 power...
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
  
  	/* Now get all the platform functions */
  	pfunc_cpu_getfreq =
  		pmf_find_function(hwclock, "get-frequency");
  	pfunc_cpu_setfreq_high =
  		pmf_find_function(hwclock, "set-frequency-high");
  	pfunc_cpu_setfreq_low =
  		pmf_find_function(hwclock, "set-frequency-low");
  	pfunc_slewing_done =
  		pmf_find_function(hwclock, "slewing-done");
  	pfunc_cpu0_volt_high =
  		pmf_find_function(hwclock, "set-voltage-high-0");
  	pfunc_cpu0_volt_low =
  		pmf_find_function(hwclock, "set-voltage-low-0");
  	pfunc_cpu1_volt_high =
  		pmf_find_function(hwclock, "set-voltage-high-1");
  	pfunc_cpu1_volt_low =
  		pmf_find_function(hwclock, "set-voltage-low-1");
  
  	/* Check we have minimum requirements */
  	if (pfunc_cpu_getfreq == NULL || pfunc_cpu_setfreq_high == NULL ||
  	    pfunc_cpu_setfreq_low == NULL || pfunc_slewing_done == NULL) {
1c5864e26   Joe Perches   cpufreq: Use cons...
527
528
  		pr_err("Can't find platform functions !
  ");
9a699aefa   Benjamin Herrenschmidt   [PATCH] 4/5 power...
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
  		rc = -ENODEV;
  		goto bail;
  	}
  
  	/* Check that we have complete sets */
  	if (pfunc_cpu0_volt_high == NULL || pfunc_cpu0_volt_low == NULL) {
  		pmf_put_function(pfunc_cpu0_volt_high);
  		pmf_put_function(pfunc_cpu0_volt_low);
  		pfunc_cpu0_volt_high = pfunc_cpu0_volt_low = NULL;
  		has_volt = 0;
  	}
  	if (!has_volt ||
  	    pfunc_cpu1_volt_high == NULL || pfunc_cpu1_volt_low == NULL) {
  		pmf_put_function(pfunc_cpu1_volt_high);
  		pmf_put_function(pfunc_cpu1_volt_low);
  		pfunc_cpu1_volt_high = pfunc_cpu1_volt_low = NULL;
  	}
  
  	/* Note: The device tree also contains a "platform-set-values"
  	 * function for which I haven't quite figured out the usage. It
  	 * might have to be called on init and/or wakeup, I'm not too sure
  	 * but things seem to work fine without it so far ...
  	 */
  
  	/* Get max frequency from device-tree */
e2eb63927   Stephen Rothwell   [POWERPC] Rename ...
554
  	valp = of_get_property(cpunode, "clock-frequency", NULL);
9a699aefa   Benjamin Herrenschmidt   [PATCH] 4/5 power...
555
  	if (!valp) {
1c5864e26   Joe Perches   cpufreq: Use cons...
556
557
  		pr_err("Can't find CPU frequency !
  ");
9a699aefa   Benjamin Herrenschmidt   [PATCH] 4/5 power...
558
559
560
561
562
563
564
565
566
567
568
569
  		rc = -ENODEV;
  		goto bail;
  	}
  
  	max_freq = (*valp)/1000;
  
  	/* Now calculate reduced frequency by using the cpuid input freq
  	 * ratio. This requires 64 bits math unless we are willing to lose
  	 * some precision
  	 */
  	ih = *((u32 *)(eeprom + 0x10));
  	il = *((u32 *)(eeprom + 0x20));
7ed14c217   Benjamin Herrenschmidt   [POWERPC] Add cpu...
570
571
572
  
  	/* Check for machines with no useful settings */
  	if (il == ih) {
1c5864e26   Joe Perches   cpufreq: Use cons...
573
574
  		pr_warn("No low frequency mode available on this model !
  ");
7ed14c217   Benjamin Herrenschmidt   [POWERPC] Add cpu...
575
576
577
  		rc = -ENODEV;
  		goto bail;
  	}
9a699aefa   Benjamin Herrenschmidt   [PATCH] 4/5 power...
578
579
580
581
582
583
  	min_freq = 0;
  	if (ih != 0 && il != 0)
  		min_freq = (max_freq * il) / ih;
  
  	/* Sanity check */
  	if (min_freq >= max_freq || min_freq < 1000) {
1c5864e26   Joe Perches   cpufreq: Use cons...
584
585
  		pr_err("Can't calculate low frequency !
  ");
7ed14c217   Benjamin Herrenschmidt   [POWERPC] Add cpu...
586
  		rc = -ENXIO;
9a699aefa   Benjamin Herrenschmidt   [PATCH] 4/5 power...
587
588
589
590
  		goto bail;
  	}
  	g5_cpu_freqs[0].frequency = max_freq;
  	g5_cpu_freqs[1].frequency = min_freq;
af671d8b2   Aaro Koskinen   cpufreq: pmac64: ...
591
592
  	/* Based on a measurement on Xserve G5, rounded up. */
  	transition_latency = 10 * NSEC_PER_MSEC;
9a699aefa   Benjamin Herrenschmidt   [PATCH] 4/5 power...
593
594
595
596
597
598
599
600
601
602
603
604
605
  	/* Set callbacks */
  	g5_switch_volt = g5_pfunc_switch_volt;
  	g5_switch_freq = g5_pfunc_switch_freq;
  	g5_query_freq = g5_pfunc_query_freq;
  
  	/* Force apply current frequency to make sure everything is in
  	 * sync (voltage is right for example). Firmware may leave us with
  	 * a strange setting ...
  	 */
  	g5_switch_volt(CPUFREQ_HIGH);
  	msleep(10);
  	g5_pmode_cur = -1;
  	g5_switch_freq(g5_query_freq());
b49c22a6c   Joe Perches   cpufreq: Convert ...
606
607
608
609
610
611
612
  	pr_info("Registering G5 CPU frequency driver
  ");
  	pr_info("Frequency method: i2c/pfunc, Voltage method: %s
  ",
  		has_volt ? "i2c/pfunc" : "none");
  	pr_info("Low: %d Mhz, High: %d Mhz, Cur: %d MHz
  ",
9a699aefa   Benjamin Herrenschmidt   [PATCH] 4/5 power...
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
  		g5_cpu_freqs[1].frequency/1000,
  		g5_cpu_freqs[0].frequency/1000,
  		g5_cpu_freqs[g5_pmode_cur].frequency/1000);
  
  	rc = cpufreq_register_driver(&g5_cpufreq_driver);
   bail:
  	if (rc != 0) {
  		pmf_put_function(pfunc_cpu_getfreq);
  		pmf_put_function(pfunc_cpu_setfreq_high);
  		pmf_put_function(pfunc_cpu_setfreq_low);
  		pmf_put_function(pfunc_slewing_done);
  		pmf_put_function(pfunc_cpu0_volt_high);
  		pmf_put_function(pfunc_cpu0_volt_low);
  		pmf_put_function(pfunc_cpu1_volt_high);
  		pmf_put_function(pfunc_cpu1_volt_low);
  	}
  	of_node_put(hwclock);
  	of_node_put(cpuid);
  	of_node_put(cpunode);
  
  	return rc;
  }
9a699aefa   Benjamin Herrenschmidt   [PATCH] 4/5 power...
635
636
  static int __init g5_cpufreq_init(void)
  {
760287ab9   Sudeep KarkadaNagesha   cpufreq: pmac64-c...
637
  	struct device_node *cpunode;
7ed14c217   Benjamin Herrenschmidt   [POWERPC] Add cpu...
638
  	int rc = 0;
9a699aefa   Benjamin Herrenschmidt   [PATCH] 4/5 power...
639

760287ab9   Sudeep KarkadaNagesha   cpufreq: pmac64-c...
640
641
642
  	/* Get first CPU node */
  	cpunode = of_cpu_device_node_get(0);
  	if (cpunode == NULL) {
1c5864e26   Joe Perches   cpufreq: Use cons...
643
644
  		pr_err("Can't find any CPU node
  ");
9a699aefa   Benjamin Herrenschmidt   [PATCH] 4/5 power...
645
646
  		return -ENODEV;
  	}
71a157e8e   Grant Likely   of: add 'of_' pre...
647
648
649
  	if (of_machine_is_compatible("PowerMac7,2") ||
  	    of_machine_is_compatible("PowerMac7,3") ||
  	    of_machine_is_compatible("RackMac3,1"))
760287ab9   Sudeep KarkadaNagesha   cpufreq: pmac64-c...
650
  		rc = g5_pm72_cpufreq_init(cpunode);
e272a2853   Benjamin Herrenschmidt   [POWERPC] Add cpu...
651
  #ifdef CONFIG_PMAC_SMU
9a699aefa   Benjamin Herrenschmidt   [PATCH] 4/5 power...
652
  	else
760287ab9   Sudeep KarkadaNagesha   cpufreq: pmac64-c...
653
  		rc = g5_neo2_cpufreq_init(cpunode);
e272a2853   Benjamin Herrenschmidt   [POWERPC] Add cpu...
654
  #endif /* CONFIG_PMAC_SMU */
9a699aefa   Benjamin Herrenschmidt   [PATCH] 4/5 power...
655

9a699aefa   Benjamin Herrenschmidt   [PATCH] 4/5 power...
656
657
  	return rc;
  }
4350147a8   Benjamin Herrenschmidt   [PATCH] ppc64: SM...
658
659
660
661
  module_init(g5_cpufreq_init);
  
  
  MODULE_LICENSE("GPL");