Blame view

drivers/cpufreq/pmac64-cpufreq.c 17.5 KB
d2912cb15   Thomas Gleixner   treewide: Replace...
1
  // SPDX-License-Identifier: GPL-2.0-only
4350147a8   Benjamin Herrenschmidt   [PATCH] ppc64: SM...
2
3
4
5
  /*
   *  Copyright (C) 2002 - 2005 Benjamin Herrenschmidt <benh@kernel.crashing.org>
   *  and                       Markus Demleitner <msdemlei@cl.uni-heidelberg.de>
   *
4350147a8   Benjamin Herrenschmidt   [PATCH] ppc64: SM...
6
7
8
   * 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...
9
  #undef DEBUG
1c5864e26   Joe Perches   cpufreq: Use cons...
10
  #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
4350147a8   Benjamin Herrenschmidt   [PATCH] ppc64: SM...
11
12
13
14
15
16
  #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...
17
18
19
  #include <linux/cpufreq.h>
  #include <linux/init.h>
  #include <linux/completion.h>
14cc3e2b6   Ingo Molnar   [PATCH] sem2mutex...
20
  #include <linux/mutex.h>
760287ab9   Sudeep KarkadaNagesha   cpufreq: pmac64-c...
21
  #include <linux/of_device.h>
4350147a8   Benjamin Herrenschmidt   [PATCH] ppc64: SM...
22
23
24
25
26
27
28
  #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...
29
  #include <asm/pmac_pfunc.h>
4350147a8   Benjamin Herrenschmidt   [PATCH] ppc64: SM...
30

7ed14c217   Benjamin Herrenschmidt   [POWERPC] Add cpu...
31
  #define DBG(fmt...) pr_debug(fmt)
4350147a8   Benjamin Herrenschmidt   [PATCH] ppc64: SM...
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
  
  /* 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...
64
65
66
  	{0, CPUFREQ_HIGH,	0},
  	{0, CPUFREQ_LOW,	0},
  	{0, 0,			CPUFREQ_TABLE_END},
4350147a8   Benjamin Herrenschmidt   [PATCH] ppc64: SM...
67
  };
4350147a8   Benjamin Herrenschmidt   [PATCH] ppc64: SM...
68
  /* Power mode data is an array of the 32 bits PCR values to use for
943ffb587   Adrian Bunk   spelling: s/retre...
69
   * the various frequencies, retrieved from the device-tree
4350147a8   Benjamin Herrenschmidt   [PATCH] ppc64: SM...
70
   */
4350147a8   Benjamin Herrenschmidt   [PATCH] ppc64: SM...
71
  static int g5_pmode_cur;
9a699aefa   Benjamin Herrenschmidt   [PATCH] 4/5 power...
72
73
74
  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...
75
  static unsigned long transition_latency;
4350147a8   Benjamin Herrenschmidt   [PATCH] ppc64: SM...
76

e272a2853   Benjamin Herrenschmidt   [POWERPC] Add cpu...
77
  #ifdef CONFIG_PMAC_SMU
7ed14c217   Benjamin Herrenschmidt   [POWERPC] Add cpu...
78

9ca91e0fb   Stephen Rothwell   [POWERPC] silence...
79
  static const u32 *g5_pmode_data;
7ed14c217   Benjamin Herrenschmidt   [POWERPC] Add cpu...
80
  static int g5_pmode_max;
4350147a8   Benjamin Herrenschmidt   [PATCH] ppc64: SM...
81
82
83
  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...
84
85
86
  /*
   * SMU based voltage switching for Neo2 platforms
   */
4350147a8   Benjamin Herrenschmidt   [PATCH] ppc64: SM...
87

9a699aefa   Benjamin Herrenschmidt   [PATCH] 4/5 power...
88
  static void g5_smu_switch_volt(int speed_mode)
4350147a8   Benjamin Herrenschmidt   [PATCH] ppc64: SM...
89
90
  {
  	struct smu_simple_cmd	cmd;
6e9a4738c   Peter Zijlstra   [PATCH] completio...
91
  	DECLARE_COMPLETION_ONSTACK(comp);
4350147a8   Benjamin Herrenschmidt   [PATCH] ppc64: SM...
92
93
94
95
96
  	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...
97
98
99
100
101
102
103
104
  /*
   * 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...
105
  {
9a699aefa   Benjamin Herrenschmidt   [PATCH] 4/5 power...
106
107
108
  	struct pmf_args args;
  	u32 slew, done = 0;
  	unsigned long timeout;
4350147a8   Benjamin Herrenschmidt   [PATCH] ppc64: SM...
109

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

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

9a699aefa   Benjamin Herrenschmidt   [PATCH] 4/5 power...
116
117
118
119
120
121
122
123
124
125
126
  	/* 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: ...
127
  		usleep_range(1000, 1000);
9a699aefa   Benjamin Herrenschmidt   [PATCH] 4/5 power...
128
129
  	}
  	if (done == 0)
1c5864e26   Joe Perches   cpufreq: Use cons...
130
131
  		pr_warn("Timeout in clock slewing !
  ");
9a699aefa   Benjamin Herrenschmidt   [PATCH] 4/5 power...
132
  }
4350147a8   Benjamin Herrenschmidt   [PATCH] ppc64: SM...
133

9a699aefa   Benjamin Herrenschmidt   [PATCH] 4/5 power...
134
135
136
137
138
139
140
141
  
  /*
   * 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...
142
143
144
145
  
  	/* 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...
146
  	local_irq_save(flags);
4350147a8   Benjamin Herrenschmidt   [PATCH] ppc64: SM...
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
  	/* 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...
168
  	local_irq_restore(flags);
4350147a8   Benjamin Herrenschmidt   [PATCH] ppc64: SM...
169
170
171
172
173
174
  	/* 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...
175
176
  	return 0;
  }
9a699aefa   Benjamin Herrenschmidt   [PATCH] 4/5 power...
177
  static int g5_scom_query_freq(void)
4350147a8   Benjamin Herrenschmidt   [PATCH] ppc64: SM...
178
179
180
181
182
183
184
185
186
187
  {
  	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...
188
  /*
7ed14c217   Benjamin Herrenschmidt   [POWERPC] Add cpu...
189
190
191
192
193
194
   * Fake voltage switching for platforms with missing support
   */
  
  static void g5_dummy_switch_volt(int speed_mode)
  {
  }
e272a2853   Benjamin Herrenschmidt   [POWERPC] Add cpu...
195
  #endif /* CONFIG_PMAC_SMU */
7ed14c217   Benjamin Herrenschmidt   [POWERPC] Add cpu...
196
197
  
  /*
9a699aefa   Benjamin Herrenschmidt   [PATCH] 4/5 power...
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
   * 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: ...
219
  	usleep_range(10000, 10000); /* should be faster , to fix */
9a699aefa   Benjamin Herrenschmidt   [PATCH] 4/5 power...
220
221
222
223
224
225
226
227
228
  }
  
  /*
   * 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...
229
  static struct pmf_function *pfunc_slewing_done;
9a699aefa   Benjamin Herrenschmidt   [PATCH] 4/5 power...
230
231
232
233
234
235
  
  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...
236
237
238
239
  	int rc;
  
  	DBG("g5_pfunc_switch_freq(%d)
  ", speed_mode);
9a699aefa   Benjamin Herrenschmidt   [PATCH] 4/5 power...
240
241
242
243
244
245
246
  
  	/* 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...
247
  		rc = pmf_call_one(pfunc_cpu_setfreq_high, NULL);
9a699aefa   Benjamin Herrenschmidt   [PATCH] 4/5 power...
248
  	else
7ed14c217   Benjamin Herrenschmidt   [POWERPC] Add cpu...
249
250
251
  		rc = pmf_call_one(pfunc_cpu_setfreq_low, NULL);
  
  	if (rc)
1c5864e26   Joe Perches   cpufreq: Use cons...
252
253
  		pr_warn("pfunc switch error %d
  ", rc);
9a699aefa   Benjamin Herrenschmidt   [PATCH] 4/5 power...
254
255
256
257
258
259
260
261
262
263
264
265
  
  	/* 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: ...
266
  		usleep_range(500, 500);
9a699aefa   Benjamin Herrenschmidt   [PATCH] 4/5 power...
267
268
  	}
  	if (done == 0)
1c5864e26   Joe Perches   cpufreq: Use cons...
269
270
  		pr_warn("Timeout in clock slewing !
  ");
9a699aefa   Benjamin Herrenschmidt   [PATCH] 4/5 power...
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
  
  	/* 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...
292
293
294
295
  
  /*
   * Common interface to the cpufreq core
   */
4350147a8   Benjamin Herrenschmidt   [PATCH] ppc64: SM...
296

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

760287ab9   Sudeep KarkadaNagesha   cpufreq: pmac64-c...
323
  static int __init g5_neo2_cpufreq_init(struct device_node *cpunode)
4350147a8   Benjamin Herrenschmidt   [PATCH] ppc64: SM...
324
  {
4350147a8   Benjamin Herrenschmidt   [PATCH] ppc64: SM...
325
  	unsigned int psize, ssize;
4350147a8   Benjamin Herrenschmidt   [PATCH] ppc64: SM...
326
  	unsigned long max_freq;
9a699aefa   Benjamin Herrenschmidt   [PATCH] 4/5 power...
327
  	char *freq_method, *volt_method;
018a3d1db   Jeremy Kerr   [POWERPC] powerma...
328
329
  	const u32 *valp;
  	u32 pvr_hi;
9a699aefa   Benjamin Herrenschmidt   [PATCH] 4/5 power...
330
331
  	int use_volts_vdnap = 0;
  	int use_volts_smu = 0;
4350147a8   Benjamin Herrenschmidt   [PATCH] ppc64: SM...
332
  	int rc = -ENODEV;
9a699aefa   Benjamin Herrenschmidt   [PATCH] 4/5 power...
333
  	/* Check supported platforms */
71a157e8e   Grant Likely   of: add 'of_' pre...
334
335
  	if (of_machine_is_compatible("PowerMac8,1") ||
  	    of_machine_is_compatible("PowerMac8,2") ||
891083622   Aaro Koskinen   cpufreq: pmac64: ...
336
337
  	    of_machine_is_compatible("PowerMac9,1") ||
  	    of_machine_is_compatible("PowerMac12,1"))
9a699aefa   Benjamin Herrenschmidt   [PATCH] 4/5 power...
338
  		use_volts_smu = 1;
71a157e8e   Grant Likely   of: add 'of_' pre...
339
  	else if (of_machine_is_compatible("PowerMac11,2"))
9a699aefa   Benjamin Herrenschmidt   [PATCH] 4/5 power...
340
341
342
  		use_volts_vdnap = 1;
  	else
  		return -ENODEV;
4350147a8   Benjamin Herrenschmidt   [PATCH] ppc64: SM...
343
  	/* Check 970FX for now */
e2eb63927   Stephen Rothwell   [POWERPC] Rename ...
344
  	valp = of_get_property(cpunode, "cpu-version", NULL);
4350147a8   Benjamin Herrenschmidt   [PATCH] ppc64: SM...
345
346
347
348
349
  	if (!valp) {
  		DBG("No cpu-version property !
  ");
  		goto bail_noprops;
  	}
9a699aefa   Benjamin Herrenschmidt   [PATCH] 4/5 power...
350
351
  	pvr_hi = (*valp) >> 16;
  	if (pvr_hi != 0x3c && pvr_hi != 0x44) {
1c5864e26   Joe Perches   cpufreq: Use cons...
352
353
  		pr_err("Unsupported CPU version
  ");
4350147a8   Benjamin Herrenschmidt   [PATCH] ppc64: SM...
354
355
356
357
  		goto bail_noprops;
  	}
  
  	/* Look for the powertune data in the device-tree */
e2eb63927   Stephen Rothwell   [POWERPC] Rename ...
358
  	g5_pmode_data = of_get_property(cpunode, "power-mode-data",&psize);
4350147a8   Benjamin Herrenschmidt   [PATCH] ppc64: SM...
359
360
361
362
363
364
  	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...
365
  	if (use_volts_smu) {
018a3d1db   Jeremy Kerr   [POWERPC] powerma...
366
  		const struct smu_sdbp_header *shdr;
9a699aefa   Benjamin Herrenschmidt   [PATCH] 4/5 power...
367
368
369
370
371
372
  
  		/* 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...
373
374
  		ssize = (shdr->len * sizeof(u32)) - sizeof(*shdr);
  		g5_fvt_count = ssize / sizeof(*g5_fvt_table);
9a699aefa   Benjamin Herrenschmidt   [PATCH] 4/5 power...
375
376
377
378
379
380
381
382
383
384
385
386
387
  		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...
388
389
  			pr_err("Can't find root of device tree
  ");
9a699aefa   Benjamin Herrenschmidt   [PATCH] 4/5 power...
390
391
392
393
394
  			goto bail_noprops;
  		}
  		pfunc_set_vdnap0 = pmf_find_function(root, "set-vdnap0");
  		pfunc_vdnap0_complete =
  			pmf_find_function(root, "slewing-done");
0dc0eb781   Yangtao Li   cpufreq: pmac64: ...
395
  		of_node_put(root);
9a699aefa   Benjamin Herrenschmidt   [PATCH] 4/5 power...
396
397
  		if (pfunc_set_vdnap0 == NULL ||
  		    pfunc_vdnap0_complete == NULL) {
1c5864e26   Joe Perches   cpufreq: Use cons...
398
399
  			pr_err("Can't find required platform function
  ");
9a699aefa   Benjamin Herrenschmidt   [PATCH] 4/5 power...
400
401
402
403
404
405
406
407
408
  			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...
409
410
411
412
413
414
415
416
  
  	/*
  	 * 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 ...
417
  	valp = of_get_property(cpunode, "clock-frequency", NULL);
4350147a8   Benjamin Herrenschmidt   [PATCH] ppc64: SM...
418
419
420
421
422
  	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...
423
  	/* Set callbacks */
16962e7ce   Nick Piggin   powerpc: Estimate...
424
  	transition_latency = 12000;
9a699aefa   Benjamin Herrenschmidt   [PATCH] 4/5 power...
425
426
427
  	g5_switch_freq = g5_scom_switch_freq;
  	g5_query_freq = g5_scom_query_freq;
  	freq_method = "SCOM";
4350147a8   Benjamin Herrenschmidt   [PATCH] ppc64: SM...
428
429
430
431
432
  
  	/* 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...
433
434
435
436
  	g5_switch_volt(CPUFREQ_HIGH);
  	msleep(10);
  	g5_pmode_cur = -1;
  	g5_switch_freq(g5_query_freq());
4350147a8   Benjamin Herrenschmidt   [PATCH] ppc64: SM...
437

b49c22a6c   Joe Perches   cpufreq: Convert ...
438
439
440
441
442
443
444
  	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...
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
  		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...
461
  #endif /* CONFIG_PMAC_SMU */
7ed14c217   Benjamin Herrenschmidt   [POWERPC] Add cpu...
462

760287ab9   Sudeep KarkadaNagesha   cpufreq: pmac64-c...
463
  static int __init g5_pm72_cpufreq_init(struct device_node *cpunode)
9a699aefa   Benjamin Herrenschmidt   [PATCH] 4/5 power...
464
  {
760287ab9   Sudeep KarkadaNagesha   cpufreq: pmac64-c...
465
  	struct device_node *cpuid = NULL, *hwclock = NULL;
018a3d1db   Jeremy Kerr   [POWERPC] powerma...
466
467
  	const u8 *eeprom = NULL;
  	const u32 *valp;
9a699aefa   Benjamin Herrenschmidt   [PATCH] 4/5 power...
468
469
  	u64 max_freq, min_freq, ih, il;
  	int has_volt = 1, rc = 0;
7ed14c217   Benjamin Herrenschmidt   [POWERPC] Add cpu...
470
471
472
  	DBG("cpufreq: Initializing for PowerMac7,2, PowerMac7,3 and"
  	    " RackMac3,1...
  ");
9a699aefa   Benjamin Herrenschmidt   [PATCH] 4/5 power...
473
474
475
  	/* 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 ...
476
  		eeprom = of_get_property(cpuid, "cpuid", NULL);
9a699aefa   Benjamin Herrenschmidt   [PATCH] 4/5 power...
477
  	if (eeprom == NULL) {
1c5864e26   Joe Perches   cpufreq: Use cons...
478
479
  		pr_err("Can't find cpuid EEPROM !
  ");
9a699aefa   Benjamin Herrenschmidt   [PATCH] 4/5 power...
480
481
482
483
484
  		rc = -ENODEV;
  		goto bail;
  	}
  
  	/* Lookup the i2c hwclock */
ccdb8ed3b   Grant Likely   of: Migrate of_fi...
485
  	for_each_node_by_name(hwclock, "i2c-hwclock") {
e2eb63927   Stephen Rothwell   [POWERPC] Rename ...
486
  		const char *loc = of_get_property(hwclock,
018a3d1db   Jeremy Kerr   [POWERPC] powerma...
487
  				"hwctrl-location", NULL);
9a699aefa   Benjamin Herrenschmidt   [PATCH] 4/5 power...
488
489
490
491
  		if (loc == NULL)
  			continue;
  		if (strcmp(loc, "CPU CLOCK"))
  			continue;
e2eb63927   Stephen Rothwell   [POWERPC] Rename ...
492
  		if (!of_get_property(hwclock, "platform-get-frequency", NULL))
9a699aefa   Benjamin Herrenschmidt   [PATCH] 4/5 power...
493
494
495
496
  			continue;
  		break;
  	}
  	if (hwclock == NULL) {
1c5864e26   Joe Perches   cpufreq: Use cons...
497
498
  		pr_err("Can't find i2c clock chip !
  ");
9a699aefa   Benjamin Herrenschmidt   [PATCH] 4/5 power...
499
500
501
  		rc = -ENODEV;
  		goto bail;
  	}
cc5a7a749   Rob Herring   cpufreq: Convert ...
502
503
  	DBG("cpufreq: i2c clock chip found: %pOF
  ", hwclock);
9a699aefa   Benjamin Herrenschmidt   [PATCH] 4/5 power...
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
  
  	/* 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...
526
527
  		pr_err("Can't find platform functions !
  ");
9a699aefa   Benjamin Herrenschmidt   [PATCH] 4/5 power...
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
  		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 ...
553
  	valp = of_get_property(cpunode, "clock-frequency", NULL);
9a699aefa   Benjamin Herrenschmidt   [PATCH] 4/5 power...
554
  	if (!valp) {
1c5864e26   Joe Perches   cpufreq: Use cons...
555
556
  		pr_err("Can't find CPU frequency !
  ");
9a699aefa   Benjamin Herrenschmidt   [PATCH] 4/5 power...
557
558
559
560
561
562
563
564
565
566
567
568
  		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...
569
570
571
  
  	/* Check for machines with no useful settings */
  	if (il == ih) {
1c5864e26   Joe Perches   cpufreq: Use cons...
572
573
  		pr_warn("No low frequency mode available on this model !
  ");
7ed14c217   Benjamin Herrenschmidt   [POWERPC] Add cpu...
574
575
576
  		rc = -ENODEV;
  		goto bail;
  	}
9a699aefa   Benjamin Herrenschmidt   [PATCH] 4/5 power...
577
578
579
580
581
582
  	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...
583
584
  		pr_err("Can't calculate low frequency !
  ");
7ed14c217   Benjamin Herrenschmidt   [POWERPC] Add cpu...
585
  		rc = -ENXIO;
9a699aefa   Benjamin Herrenschmidt   [PATCH] 4/5 power...
586
587
588
589
  		goto bail;
  	}
  	g5_cpu_freqs[0].frequency = max_freq;
  	g5_cpu_freqs[1].frequency = min_freq;
af671d8b2   Aaro Koskinen   cpufreq: pmac64: ...
590
591
  	/* Based on a measurement on Xserve G5, rounded up. */
  	transition_latency = 10 * NSEC_PER_MSEC;
9a699aefa   Benjamin Herrenschmidt   [PATCH] 4/5 power...
592
593
594
595
596
597
598
599
600
601
602
603
604
  	/* 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 ...
605
606
607
608
609
610
611
  	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...
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
  		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...
634
635
  static int __init g5_cpufreq_init(void)
  {
760287ab9   Sudeep KarkadaNagesha   cpufreq: pmac64-c...
636
  	struct device_node *cpunode;
7ed14c217   Benjamin Herrenschmidt   [POWERPC] Add cpu...
637
  	int rc = 0;
9a699aefa   Benjamin Herrenschmidt   [PATCH] 4/5 power...
638

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

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