Blame view

drivers/devfreq/devfreq.c 31.2 KB
a3c98b8b2   MyungJoo Ham   PM: Introduce dev...
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
  /*
   * devfreq: Generic Dynamic Voltage and Frequency Scaling (DVFS) Framework
   *	    for Non-CPU Devices.
   *
   * Copyright (C) 2011 Samsung Electronics
   *	MyungJoo Ham <myungjoo.ham@samsung.com>
   *
   * 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.
   */
  
  #include <linux/kernel.h>
  #include <linux/sched.h>
  #include <linux/errno.h>
  #include <linux/err.h>
  #include <linux/init.h>
952f6d138   MyungJoo Ham   PM / devfreq: Rem...
18
  #include <linux/module.h>
a3c98b8b2   MyungJoo Ham   PM: Introduce dev...
19
  #include <linux/slab.h>
952f6d138   MyungJoo Ham   PM / devfreq: Rem...
20
  #include <linux/stat.h>
e4db1c743   Nishanth Menon   PM / OPP: rename ...
21
  #include <linux/pm_opp.h>
a3c98b8b2   MyungJoo Ham   PM: Introduce dev...
22
23
24
25
26
27
28
  #include <linux/devfreq.h>
  #include <linux/workqueue.h>
  #include <linux/platform_device.h>
  #include <linux/list.h>
  #include <linux/printk.h>
  #include <linux/hrtimer.h>
  #include "governor.h"
1a1357ea1   Nishanth Menon   PM / devfreq: mak...
29
  static struct class *devfreq_class;
a3c98b8b2   MyungJoo Ham   PM: Introduce dev...
30
31
  
  /*
7e6fdd4ba   Rajagopal Venkat   PM / devfreq: Cor...
32
33
34
   * devfreq core provides delayed work based load monitoring helper
   * functions. Governors can use these or can implement their own
   * monitoring mechanism.
a3c98b8b2   MyungJoo Ham   PM: Introduce dev...
35
   */
a3c98b8b2   MyungJoo Ham   PM: Introduce dev...
36
  static struct workqueue_struct *devfreq_wq;
a3c98b8b2   MyungJoo Ham   PM: Introduce dev...
37

3aa173b8d   Nishanth Menon   PM / devfreq: pro...
38
39
  /* The list of all device-devfreq governors */
  static LIST_HEAD(devfreq_governor_list);
a3c98b8b2   MyungJoo Ham   PM: Introduce dev...
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
  /* The list of all device-devfreq */
  static LIST_HEAD(devfreq_list);
  static DEFINE_MUTEX(devfreq_list_lock);
  
  /**
   * find_device_devfreq() - find devfreq struct using device pointer
   * @dev:	device pointer used to lookup device devfreq.
   *
   * Search the list of device devfreqs and return the matched device's
   * devfreq info. devfreq_list_lock should be held by the caller.
   */
  static struct devfreq *find_device_devfreq(struct device *dev)
  {
  	struct devfreq *tmp_devfreq;
  
  	if (unlikely(IS_ERR_OR_NULL(dev))) {
  		pr_err("DEVFREQ: %s: Invalid parameters
  ", __func__);
  		return ERR_PTR(-EINVAL);
  	}
  	WARN(!mutex_is_locked(&devfreq_list_lock),
  	     "devfreq_list_lock must be locked.");
  
  	list_for_each_entry(tmp_devfreq, &devfreq_list, node) {
  		if (tmp_devfreq->dev.parent == dev)
  			return tmp_devfreq;
  	}
  
  	return ERR_PTR(-ENODEV);
  }
  
  /**
e552bbaf5   Jonghwa Lee   PM / devfreq: Add...
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
   * devfreq_get_freq_level() - Lookup freq_table for the frequency
   * @devfreq:	the devfreq instance
   * @freq:	the target frequency
   */
  static int devfreq_get_freq_level(struct devfreq *devfreq, unsigned long freq)
  {
  	int lev;
  
  	for (lev = 0; lev < devfreq->profile->max_state; lev++)
  		if (freq == devfreq->profile->freq_table[lev])
  			return lev;
  
  	return -EINVAL;
  }
  
  /**
   * devfreq_update_status() - Update statistics of devfreq behavior
   * @devfreq:	the devfreq instance
   * @freq:	the update target frequency
   */
  static int devfreq_update_status(struct devfreq *devfreq, unsigned long freq)
  {
e35d35a1c   Saravana Kannan   PM / devfreq: Rew...
94
  	int lev, prev_lev, ret = 0;
e552bbaf5   Jonghwa Lee   PM / devfreq: Add...
95
  	unsigned long cur_time;
e552bbaf5   Jonghwa Lee   PM / devfreq: Add...
96
  	cur_time = jiffies;
e35d35a1c   Saravana Kannan   PM / devfreq: Rew...
97
98
99
100
101
102
103
104
  
  	prev_lev = devfreq_get_freq_level(devfreq, devfreq->previous_freq);
  	if (prev_lev < 0) {
  		ret = prev_lev;
  		goto out;
  	}
  
  	devfreq->time_in_state[prev_lev] +=
e552bbaf5   Jonghwa Lee   PM / devfreq: Add...
105
  			 cur_time - devfreq->last_stat_updated;
e35d35a1c   Saravana Kannan   PM / devfreq: Rew...
106
107
108
109
110
111
112
113
  
  	lev = devfreq_get_freq_level(devfreq, freq);
  	if (lev < 0) {
  		ret = lev;
  		goto out;
  	}
  
  	if (lev != prev_lev) {
e552bbaf5   Jonghwa Lee   PM / devfreq: Add...
114
115
116
117
  		devfreq->trans_table[(prev_lev *
  				devfreq->profile->max_state) + lev]++;
  		devfreq->total_trans++;
  	}
e552bbaf5   Jonghwa Lee   PM / devfreq: Add...
118

e35d35a1c   Saravana Kannan   PM / devfreq: Rew...
119
120
121
  out:
  	devfreq->last_stat_updated = cur_time;
  	return ret;
e552bbaf5   Jonghwa Lee   PM / devfreq: Add...
122
  }
3aa173b8d   Nishanth Menon   PM / devfreq: pro...
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
  /**
   * find_devfreq_governor() - find devfreq governor from name
   * @name:	name of the governor
   *
   * Search the list of devfreq governors and return the matched
   * governor's pointer. devfreq_list_lock should be held by the caller.
   */
  static struct devfreq_governor *find_devfreq_governor(const char *name)
  {
  	struct devfreq_governor *tmp_governor;
  
  	if (unlikely(IS_ERR_OR_NULL(name))) {
  		pr_err("DEVFREQ: %s: Invalid parameters
  ", __func__);
  		return ERR_PTR(-EINVAL);
  	}
  	WARN(!mutex_is_locked(&devfreq_list_lock),
  	     "devfreq_list_lock must be locked.");
  
  	list_for_each_entry(tmp_governor, &devfreq_governor_list, node) {
  		if (!strncmp(tmp_governor->name, name, DEVFREQ_NAME_LEN))
  			return tmp_governor;
  	}
  
  	return ERR_PTR(-ENODEV);
  }
7e6fdd4ba   Rajagopal Venkat   PM / devfreq: Cor...
149
  /* Load monitoring helper functions for governors use */
a3c98b8b2   MyungJoo Ham   PM: Introduce dev...
150
151
152
153
154
155
156
157
158
159
160
  /**
   * update_devfreq() - Reevaluate the device and configure frequency.
   * @devfreq:	the devfreq instance.
   *
   * Note: Lock devfreq->lock before calling update_devfreq
   *	 This function is exported for governors.
   */
  int update_devfreq(struct devfreq *devfreq)
  {
  	unsigned long freq;
  	int err = 0;
ab5f299f5   MyungJoo Ham   PM / devfreq: add...
161
  	u32 flags = 0;
a3c98b8b2   MyungJoo Ham   PM: Introduce dev...
162
163
164
165
166
167
  
  	if (!mutex_is_locked(&devfreq->lock)) {
  		WARN(true, "devfreq->lock must be locked by the caller.
  ");
  		return -EINVAL;
  	}
1b5c1be2c   Nishanth Menon   PM / devfreq: map...
168
169
  	if (!devfreq->governor)
  		return -EINVAL;
a3c98b8b2   MyungJoo Ham   PM: Introduce dev...
170
171
172
173
  	/* Reevaluate the proper frequency */
  	err = devfreq->governor->get_target_freq(devfreq, &freq);
  	if (err)
  		return err;
ab5f299f5   MyungJoo Ham   PM / devfreq: add...
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
  	/*
  	 * Adjust the freuqency with user freq and QoS.
  	 *
  	 * List from the highest proiority
  	 * max_freq (probably called by thermal when it's too hot)
  	 * min_freq
  	 */
  
  	if (devfreq->min_freq && freq < devfreq->min_freq) {
  		freq = devfreq->min_freq;
  		flags &= ~DEVFREQ_FLAG_LEAST_UPPER_BOUND; /* Use GLB */
  	}
  	if (devfreq->max_freq && freq > devfreq->max_freq) {
  		freq = devfreq->max_freq;
  		flags |= DEVFREQ_FLAG_LEAST_UPPER_BOUND; /* Use LUB */
  	}
  
  	err = devfreq->profile->target(devfreq->dev.parent, &freq, flags);
a3c98b8b2   MyungJoo Ham   PM: Introduce dev...
192
193
  	if (err)
  		return err;
e552bbaf5   Jonghwa Lee   PM / devfreq: Add...
194
195
196
197
198
  	if (devfreq->profile->freq_table)
  		if (devfreq_update_status(devfreq, freq))
  			dev_err(&devfreq->dev,
  				"Couldn't update frequency transition information.
  ");
a3c98b8b2   MyungJoo Ham   PM: Introduce dev...
199
200
201
  	devfreq->previous_freq = freq;
  	return err;
  }
2df5021fa   Nishanth Menon   PM / devfreq: exp...
202
  EXPORT_SYMBOL(update_devfreq);
a3c98b8b2   MyungJoo Ham   PM: Introduce dev...
203
204
  
  /**
7e6fdd4ba   Rajagopal Venkat   PM / devfreq: Cor...
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
   * devfreq_monitor() - Periodically poll devfreq objects.
   * @work:	the work struct used to run devfreq_monitor periodically.
   *
   */
  static void devfreq_monitor(struct work_struct *work)
  {
  	int err;
  	struct devfreq *devfreq = container_of(work,
  					struct devfreq, work.work);
  
  	mutex_lock(&devfreq->lock);
  	err = update_devfreq(devfreq);
  	if (err)
  		dev_err(&devfreq->dev, "dvfs failed with (%d) error
  ", err);
  
  	queue_delayed_work(devfreq_wq, &devfreq->work,
  				msecs_to_jiffies(devfreq->profile->polling_ms));
  	mutex_unlock(&devfreq->lock);
  }
  
  /**
   * devfreq_monitor_start() - Start load monitoring of devfreq instance
   * @devfreq:	the devfreq instance.
   *
   * Helper function for starting devfreq device load monitoing. By
   * default delayed work based monitoring is supported. Function
   * to be called from governor in response to DEVFREQ_GOV_START
   * event when device is added to devfreq framework.
   */
  void devfreq_monitor_start(struct devfreq *devfreq)
  {
  	INIT_DEFERRABLE_WORK(&devfreq->work, devfreq_monitor);
  	if (devfreq->profile->polling_ms)
  		queue_delayed_work(devfreq_wq, &devfreq->work,
  			msecs_to_jiffies(devfreq->profile->polling_ms));
  }
6dcdd8e3c   MyungJoo Ham   PM / devfreq: rem...
242
  EXPORT_SYMBOL(devfreq_monitor_start);
7e6fdd4ba   Rajagopal Venkat   PM / devfreq: Cor...
243
244
245
246
247
248
249
250
251
252
253
254
255
  
  /**
   * devfreq_monitor_stop() - Stop load monitoring of a devfreq instance
   * @devfreq:	the devfreq instance.
   *
   * Helper function to stop devfreq device load monitoing. Function
   * to be called from governor in response to DEVFREQ_GOV_STOP
   * event when device is removed from devfreq framework.
   */
  void devfreq_monitor_stop(struct devfreq *devfreq)
  {
  	cancel_delayed_work_sync(&devfreq->work);
  }
6dcdd8e3c   MyungJoo Ham   PM / devfreq: rem...
256
  EXPORT_SYMBOL(devfreq_monitor_stop);
7e6fdd4ba   Rajagopal Venkat   PM / devfreq: Cor...
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
  
  /**
   * devfreq_monitor_suspend() - Suspend load monitoring of a devfreq instance
   * @devfreq:	the devfreq instance.
   *
   * Helper function to suspend devfreq device load monitoing. Function
   * to be called from governor in response to DEVFREQ_GOV_SUSPEND
   * event or when polling interval is set to zero.
   *
   * Note: Though this function is same as devfreq_monitor_stop(),
   * intentionally kept separate to provide hooks for collecting
   * transition statistics.
   */
  void devfreq_monitor_suspend(struct devfreq *devfreq)
  {
  	mutex_lock(&devfreq->lock);
  	if (devfreq->stop_polling) {
  		mutex_unlock(&devfreq->lock);
  		return;
  	}
39688ce6f   Rajagopal Venkat   PM / devfreq: acc...
277
  	devfreq_update_status(devfreq, devfreq->previous_freq);
7e6fdd4ba   Rajagopal Venkat   PM / devfreq: Cor...
278
279
280
281
  	devfreq->stop_polling = true;
  	mutex_unlock(&devfreq->lock);
  	cancel_delayed_work_sync(&devfreq->work);
  }
6dcdd8e3c   MyungJoo Ham   PM / devfreq: rem...
282
  EXPORT_SYMBOL(devfreq_monitor_suspend);
7e6fdd4ba   Rajagopal Venkat   PM / devfreq: Cor...
283
284
285
286
287
288
289
290
291
292
293
  
  /**
   * devfreq_monitor_resume() - Resume load monitoring of a devfreq instance
   * @devfreq:    the devfreq instance.
   *
   * Helper function to resume devfreq device load monitoing. Function
   * to be called from governor in response to DEVFREQ_GOV_RESUME
   * event or when polling interval is set to non-zero.
   */
  void devfreq_monitor_resume(struct devfreq *devfreq)
  {
39688ce6f   Rajagopal Venkat   PM / devfreq: acc...
294
  	unsigned long freq;
7e6fdd4ba   Rajagopal Venkat   PM / devfreq: Cor...
295
296
297
298
299
300
301
302
  	mutex_lock(&devfreq->lock);
  	if (!devfreq->stop_polling)
  		goto out;
  
  	if (!delayed_work_pending(&devfreq->work) &&
  			devfreq->profile->polling_ms)
  		queue_delayed_work(devfreq_wq, &devfreq->work,
  			msecs_to_jiffies(devfreq->profile->polling_ms));
39688ce6f   Rajagopal Venkat   PM / devfreq: acc...
303
304
  
  	devfreq->last_stat_updated = jiffies;
7e6fdd4ba   Rajagopal Venkat   PM / devfreq: Cor...
305
  	devfreq->stop_polling = false;
39688ce6f   Rajagopal Venkat   PM / devfreq: acc...
306
307
308
  	if (devfreq->profile->get_cur_freq &&
  		!devfreq->profile->get_cur_freq(devfreq->dev.parent, &freq))
  		devfreq->previous_freq = freq;
7e6fdd4ba   Rajagopal Venkat   PM / devfreq: Cor...
309
310
311
  out:
  	mutex_unlock(&devfreq->lock);
  }
6dcdd8e3c   MyungJoo Ham   PM / devfreq: rem...
312
  EXPORT_SYMBOL(devfreq_monitor_resume);
7e6fdd4ba   Rajagopal Venkat   PM / devfreq: Cor...
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
  
  /**
   * devfreq_interval_update() - Update device devfreq monitoring interval
   * @devfreq:    the devfreq instance.
   * @delay:      new polling interval to be set.
   *
   * Helper function to set new load monitoring polling interval. Function
   * to be called from governor in response to DEVFREQ_GOV_INTERVAL event.
   */
  void devfreq_interval_update(struct devfreq *devfreq, unsigned int *delay)
  {
  	unsigned int cur_delay = devfreq->profile->polling_ms;
  	unsigned int new_delay = *delay;
  
  	mutex_lock(&devfreq->lock);
  	devfreq->profile->polling_ms = new_delay;
  
  	if (devfreq->stop_polling)
  		goto out;
  
  	/* if new delay is zero, stop polling */
  	if (!new_delay) {
  		mutex_unlock(&devfreq->lock);
  		cancel_delayed_work_sync(&devfreq->work);
  		return;
  	}
  
  	/* if current delay is zero, start polling with new delay */
  	if (!cur_delay) {
  		queue_delayed_work(devfreq_wq, &devfreq->work,
  			msecs_to_jiffies(devfreq->profile->polling_ms));
  		goto out;
  	}
  
  	/* if current delay is greater than new delay, restart polling */
  	if (cur_delay > new_delay) {
  		mutex_unlock(&devfreq->lock);
  		cancel_delayed_work_sync(&devfreq->work);
  		mutex_lock(&devfreq->lock);
  		if (!devfreq->stop_polling)
  			queue_delayed_work(devfreq_wq, &devfreq->work,
  			      msecs_to_jiffies(devfreq->profile->polling_ms));
  	}
  out:
  	mutex_unlock(&devfreq->lock);
  }
6dcdd8e3c   MyungJoo Ham   PM / devfreq: rem...
359
  EXPORT_SYMBOL(devfreq_interval_update);
a3c98b8b2   MyungJoo Ham   PM: Introduce dev...
360
361
362
363
  
  /**
   * devfreq_notifier_call() - Notify that the device frequency requirements
   *			   has been changed out of devfreq framework.
c5b4a1c15   Nishanth Menon   PM / devfreq: ker...
364
365
366
   * @nb:		the notifier_block (supposed to be devfreq->nb)
   * @type:	not used
   * @devp:	not used
a3c98b8b2   MyungJoo Ham   PM: Introduce dev...
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
   *
   * Called by a notifier that uses devfreq->nb.
   */
  static int devfreq_notifier_call(struct notifier_block *nb, unsigned long type,
  				 void *devp)
  {
  	struct devfreq *devfreq = container_of(nb, struct devfreq, nb);
  	int ret;
  
  	mutex_lock(&devfreq->lock);
  	ret = update_devfreq(devfreq);
  	mutex_unlock(&devfreq->lock);
  
  	return ret;
  }
  
  /**
7e6fdd4ba   Rajagopal Venkat   PM / devfreq: Cor...
384
   * _remove_devfreq() - Remove devfreq from the list and release its resources.
a3c98b8b2   MyungJoo Ham   PM: Introduce dev...
385
386
   * @devfreq:	the devfreq struct
   * @skip:	skip calling device_unregister().
a3c98b8b2   MyungJoo Ham   PM: Introduce dev...
387
   */
585fc83ec   Chanwoo Choi   PM / devfreq: Fix...
388
  static void _remove_devfreq(struct devfreq *devfreq)
a3c98b8b2   MyungJoo Ham   PM: Introduce dev...
389
  {
7e6fdd4ba   Rajagopal Venkat   PM / devfreq: Cor...
390
391
392
393
394
  	mutex_lock(&devfreq_list_lock);
  	if (IS_ERR(find_device_devfreq(devfreq->dev.parent))) {
  		mutex_unlock(&devfreq_list_lock);
  		dev_warn(&devfreq->dev, "releasing devfreq which doesn't exist
  ");
a3c98b8b2   MyungJoo Ham   PM: Introduce dev...
395
396
  		return;
  	}
7e6fdd4ba   Rajagopal Venkat   PM / devfreq: Cor...
397
398
  	list_del(&devfreq->node);
  	mutex_unlock(&devfreq_list_lock);
a3c98b8b2   MyungJoo Ham   PM: Introduce dev...
399

1b5c1be2c   Nishanth Menon   PM / devfreq: map...
400
401
402
  	if (devfreq->governor)
  		devfreq->governor->event_handler(devfreq,
  						 DEVFREQ_GOV_STOP, NULL);
a3c98b8b2   MyungJoo Ham   PM: Introduce dev...
403
404
405
  
  	if (devfreq->profile->exit)
  		devfreq->profile->exit(devfreq->dev.parent);
a3c98b8b2   MyungJoo Ham   PM: Introduce dev...
406
  	mutex_destroy(&devfreq->lock);
a3c98b8b2   MyungJoo Ham   PM: Introduce dev...
407
408
409
410
411
412
413
414
  	kfree(devfreq);
  }
  
  /**
   * devfreq_dev_release() - Callback for struct device to release the device.
   * @dev:	the devfreq device
   *
   * This calls _remove_devfreq() if _remove_devfreq() is not called.
a3c98b8b2   MyungJoo Ham   PM: Introduce dev...
415
416
417
418
   */
  static void devfreq_dev_release(struct device *dev)
  {
  	struct devfreq *devfreq = to_devfreq(dev);
a3c98b8b2   MyungJoo Ham   PM: Introduce dev...
419

585fc83ec   Chanwoo Choi   PM / devfreq: Fix...
420
  	_remove_devfreq(devfreq);
a3c98b8b2   MyungJoo Ham   PM: Introduce dev...
421
422
423
424
425
426
  }
  
  /**
   * devfreq_add_device() - Add devfreq feature to the device
   * @dev:	the device to add devfreq feature.
   * @profile:	device-specific profile to run devfreq.
1b5c1be2c   Nishanth Menon   PM / devfreq: map...
427
   * @governor_name:	name of the policy to choose frequency.
a3c98b8b2   MyungJoo Ham   PM: Introduce dev...
428
429
430
431
432
   * @data:	private data for the governor. The devfreq framework does not
   *		touch this value.
   */
  struct devfreq *devfreq_add_device(struct device *dev,
  				   struct devfreq_dev_profile *profile,
1b5c1be2c   Nishanth Menon   PM / devfreq: map...
433
  				   const char *governor_name,
a3c98b8b2   MyungJoo Ham   PM: Introduce dev...
434
435
436
  				   void *data)
  {
  	struct devfreq *devfreq;
1b5c1be2c   Nishanth Menon   PM / devfreq: map...
437
  	struct devfreq_governor *governor;
a3c98b8b2   MyungJoo Ham   PM: Introduce dev...
438
  	int err = 0;
1b5c1be2c   Nishanth Menon   PM / devfreq: map...
439
  	if (!dev || !profile || !governor_name) {
a3c98b8b2   MyungJoo Ham   PM: Introduce dev...
440
441
442
443
  		dev_err(dev, "%s: Invalid parameters.
  ", __func__);
  		return ERR_PTR(-EINVAL);
  	}
7e6fdd4ba   Rajagopal Venkat   PM / devfreq: Cor...
444
445
446
447
448
449
450
451
  	mutex_lock(&devfreq_list_lock);
  	devfreq = find_device_devfreq(dev);
  	mutex_unlock(&devfreq_list_lock);
  	if (!IS_ERR(devfreq)) {
  		dev_err(dev, "%s: Unable to create devfreq for the device. It already has one.
  ", __func__);
  		err = -EINVAL;
  		goto err_out;
a3c98b8b2   MyungJoo Ham   PM: Introduce dev...
452
453
454
455
456
457
458
459
  	}
  
  	devfreq = kzalloc(sizeof(struct devfreq), GFP_KERNEL);
  	if (!devfreq) {
  		dev_err(dev, "%s: Unable to create devfreq for the device
  ",
  			__func__);
  		err = -ENOMEM;
3f19f08a7   Axel Lin   PM / devfreq: sep...
460
  		goto err_out;
a3c98b8b2   MyungJoo Ham   PM: Introduce dev...
461
462
463
464
465
466
467
468
  	}
  
  	mutex_init(&devfreq->lock);
  	mutex_lock(&devfreq->lock);
  	devfreq->dev.parent = dev;
  	devfreq->dev.class = devfreq_class;
  	devfreq->dev.release = devfreq_dev_release;
  	devfreq->profile = profile;
1b5c1be2c   Nishanth Menon   PM / devfreq: map...
469
  	strncpy(devfreq->governor_name, governor_name, DEVFREQ_NAME_LEN);
a3c98b8b2   MyungJoo Ham   PM: Introduce dev...
470
471
  	devfreq->previous_freq = profile->initial_freq;
  	devfreq->data = data;
a3c98b8b2   MyungJoo Ham   PM: Introduce dev...
472
  	devfreq->nb.notifier_call = devfreq_notifier_call;
e552bbaf5   Jonghwa Lee   PM / devfreq: Add...
473
474
475
476
477
478
479
480
  	devfreq->trans_table =	devm_kzalloc(dev, sizeof(unsigned int) *
  						devfreq->profile->max_state *
  						devfreq->profile->max_state,
  						GFP_KERNEL);
  	devfreq->time_in_state = devm_kzalloc(dev, sizeof(unsigned int) *
  						devfreq->profile->max_state,
  						GFP_KERNEL);
  	devfreq->last_stat_updated = jiffies;
02aa2a376   Kees Cook   drivers: avoid fo...
481
  	dev_set_name(&devfreq->dev, "%s", dev_name(dev));
a3c98b8b2   MyungJoo Ham   PM: Introduce dev...
482
483
484
  	err = device_register(&devfreq->dev);
  	if (err) {
  		put_device(&devfreq->dev);
7e6fdd4ba   Rajagopal Venkat   PM / devfreq: Cor...
485
  		mutex_unlock(&devfreq->lock);
a3c98b8b2   MyungJoo Ham   PM: Introduce dev...
486
487
  		goto err_dev;
  	}
a3c98b8b2   MyungJoo Ham   PM: Introduce dev...
488
  	mutex_unlock(&devfreq->lock);
a3c98b8b2   MyungJoo Ham   PM: Introduce dev...
489
  	mutex_lock(&devfreq_list_lock);
a3c98b8b2   MyungJoo Ham   PM: Introduce dev...
490
  	list_add(&devfreq->node, &devfreq_list);
1b5c1be2c   Nishanth Menon   PM / devfreq: map...
491
492
493
494
495
496
  	governor = find_devfreq_governor(devfreq->governor_name);
  	if (!IS_ERR(governor))
  		devfreq->governor = governor;
  	if (devfreq->governor)
  		err = devfreq->governor->event_handler(devfreq,
  					DEVFREQ_GOV_START, NULL);
a3c98b8b2   MyungJoo Ham   PM: Introduce dev...
497
  	mutex_unlock(&devfreq_list_lock);
7e6fdd4ba   Rajagopal Venkat   PM / devfreq: Cor...
498
499
500
501
502
  	if (err) {
  		dev_err(dev, "%s: Unable to start governor for the device
  ",
  			__func__);
  		goto err_init;
a3c98b8b2   MyungJoo Ham   PM: Introduce dev...
503
  	}
7e6fdd4ba   Rajagopal Venkat   PM / devfreq: Cor...
504

3f19f08a7   Axel Lin   PM / devfreq: sep...
505
  	return devfreq;
a3c98b8b2   MyungJoo Ham   PM: Introduce dev...
506
  err_init:
7e6fdd4ba   Rajagopal Venkat   PM / devfreq: Cor...
507
  	list_del(&devfreq->node);
a3c98b8b2   MyungJoo Ham   PM: Introduce dev...
508
509
  	device_unregister(&devfreq->dev);
  err_dev:
a3c98b8b2   MyungJoo Ham   PM: Introduce dev...
510
  	kfree(devfreq);
3f19f08a7   Axel Lin   PM / devfreq: sep...
511
512
  err_out:
  	return ERR_PTR(err);
a3c98b8b2   MyungJoo Ham   PM: Introduce dev...
513
  }
7e6fdd4ba   Rajagopal Venkat   PM / devfreq: Cor...
514
  EXPORT_SYMBOL(devfreq_add_device);
a3c98b8b2   MyungJoo Ham   PM: Introduce dev...
515
516
517
  
  /**
   * devfreq_remove_device() - Remove devfreq feature from a device.
c5b4a1c15   Nishanth Menon   PM / devfreq: ker...
518
   * @devfreq:	the devfreq instance to be removed
de9c73943   MyungJoo Ham   PM / devfreq: add...
519
520
   *
   * The opposite of devfreq_add_device().
a3c98b8b2   MyungJoo Ham   PM: Introduce dev...
521
522
523
524
525
   */
  int devfreq_remove_device(struct devfreq *devfreq)
  {
  	if (!devfreq)
  		return -EINVAL;
585fc83ec   Chanwoo Choi   PM / devfreq: Fix...
526
527
  	device_unregister(&devfreq->dev);
  	put_device(&devfreq->dev);
9f3bdd4f9   Axel Lin   PM / devfreq: fix...
528

a3c98b8b2   MyungJoo Ham   PM: Introduce dev...
529
530
  	return 0;
  }
7e6fdd4ba   Rajagopal Venkat   PM / devfreq: Cor...
531
  EXPORT_SYMBOL(devfreq_remove_device);
a3c98b8b2   MyungJoo Ham   PM: Introduce dev...
532

8cd84092d   Chanwoo Choi   PM / devfreq: Add...
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
  static int devm_devfreq_dev_match(struct device *dev, void *res, void *data)
  {
  	struct devfreq **r = res;
  
  	if (WARN_ON(!r || !*r))
  		return 0;
  
  	return *r == data;
  }
  
  static void devm_devfreq_dev_release(struct device *dev, void *res)
  {
  	devfreq_remove_device(*(struct devfreq **)res);
  }
  
  /**
   * devm_devfreq_add_device() - Resource-managed devfreq_add_device()
   * @dev:	the device to add devfreq feature.
   * @profile:	device-specific profile to run devfreq.
   * @governor_name:	name of the policy to choose frequency.
   * @data:	private data for the governor. The devfreq framework does not
   *		touch this value.
   *
   * This function manages automatically the memory of devfreq device using device
   * resource management and simplify the free operation for memory of devfreq
   * device.
   */
  struct devfreq *devm_devfreq_add_device(struct device *dev,
  					struct devfreq_dev_profile *profile,
  					const char *governor_name,
  					void *data)
  {
  	struct devfreq **ptr, *devfreq;
  
  	ptr = devres_alloc(devm_devfreq_dev_release, sizeof(*ptr), GFP_KERNEL);
  	if (!ptr)
  		return ERR_PTR(-ENOMEM);
  
  	devfreq = devfreq_add_device(dev, profile, governor_name, data);
  	if (IS_ERR(devfreq)) {
  		devres_free(ptr);
  		return ERR_PTR(-ENOMEM);
  	}
  
  	*ptr = devfreq;
  	devres_add(dev, ptr);
  
  	return devfreq;
  }
  EXPORT_SYMBOL(devm_devfreq_add_device);
  
  /**
   * devm_devfreq_remove_device() - Resource-managed devfreq_remove_device()
   * @dev:	the device to add devfreq feature.
   * @devfreq:	the devfreq instance to be removed
   */
  void devm_devfreq_remove_device(struct device *dev, struct devfreq *devfreq)
  {
  	WARN_ON(devres_release(dev, devm_devfreq_dev_release,
  			       devm_devfreq_dev_match, devfreq));
  }
  EXPORT_SYMBOL(devm_devfreq_remove_device);
206c30cfe   Rajagopal Venkat   PM / devfreq: Add...
595
596
597
  /**
   * devfreq_suspend_device() - Suspend devfreq of a device.
   * @devfreq: the devfreq instance to be suspended
de9c73943   MyungJoo Ham   PM / devfreq: add...
598
599
600
601
   *
   * This function is intended to be called by the pm callbacks
   * (e.g., runtime_suspend, suspend) of the device driver that
   * holds the devfreq.
206c30cfe   Rajagopal Venkat   PM / devfreq: Add...
602
603
604
   */
  int devfreq_suspend_device(struct devfreq *devfreq)
  {
a3c98b8b2   MyungJoo Ham   PM: Introduce dev...
605
606
  	if (!devfreq)
  		return -EINVAL;
1b5c1be2c   Nishanth Menon   PM / devfreq: map...
607
608
  	if (!devfreq->governor)
  		return 0;
206c30cfe   Rajagopal Venkat   PM / devfreq: Add...
609
610
611
612
613
614
615
616
  	return devfreq->governor->event_handler(devfreq,
  				DEVFREQ_GOV_SUSPEND, NULL);
  }
  EXPORT_SYMBOL(devfreq_suspend_device);
  
  /**
   * devfreq_resume_device() - Resume devfreq of a device.
   * @devfreq: the devfreq instance to be resumed
de9c73943   MyungJoo Ham   PM / devfreq: add...
617
618
619
620
   *
   * This function is intended to be called by the pm callbacks
   * (e.g., runtime_resume, resume) of the device driver that
   * holds the devfreq.
206c30cfe   Rajagopal Venkat   PM / devfreq: Add...
621
622
623
624
625
   */
  int devfreq_resume_device(struct devfreq *devfreq)
  {
  	if (!devfreq)
  		return -EINVAL;
1b5c1be2c   Nishanth Menon   PM / devfreq: map...
626
627
  	if (!devfreq->governor)
  		return 0;
206c30cfe   Rajagopal Venkat   PM / devfreq: Add...
628
629
630
631
  	return devfreq->governor->event_handler(devfreq,
  				DEVFREQ_GOV_RESUME, NULL);
  }
  EXPORT_SYMBOL(devfreq_resume_device);
3aa173b8d   Nishanth Menon   PM / devfreq: pro...
632
633
634
635
636
637
638
  /**
   * devfreq_add_governor() - Add devfreq governor
   * @governor:	the devfreq governor to be added
   */
  int devfreq_add_governor(struct devfreq_governor *governor)
  {
  	struct devfreq_governor *g;
1b5c1be2c   Nishanth Menon   PM / devfreq: map...
639
  	struct devfreq *devfreq;
3aa173b8d   Nishanth Menon   PM / devfreq: pro...
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
  	int err = 0;
  
  	if (!governor) {
  		pr_err("%s: Invalid parameters.
  ", __func__);
  		return -EINVAL;
  	}
  
  	mutex_lock(&devfreq_list_lock);
  	g = find_devfreq_governor(governor->name);
  	if (!IS_ERR(g)) {
  		pr_err("%s: governor %s already registered
  ", __func__,
  		       g->name);
  		err = -EINVAL;
  		goto err_out;
  	}
9f3bdd4f9   Axel Lin   PM / devfreq: fix...
657

3aa173b8d   Nishanth Menon   PM / devfreq: pro...
658
  	list_add(&governor->node, &devfreq_governor_list);
1b5c1be2c   Nishanth Menon   PM / devfreq: map...
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
  	list_for_each_entry(devfreq, &devfreq_list, node) {
  		int ret = 0;
  		struct device *dev = devfreq->dev.parent;
  
  		if (!strncmp(devfreq->governor_name, governor->name,
  			     DEVFREQ_NAME_LEN)) {
  			/* The following should never occur */
  			if (devfreq->governor) {
  				dev_warn(dev,
  					 "%s: Governor %s already present
  ",
  					 __func__, devfreq->governor->name);
  				ret = devfreq->governor->event_handler(devfreq,
  							DEVFREQ_GOV_STOP, NULL);
  				if (ret) {
  					dev_warn(dev,
  						 "%s: Governor %s stop = %d
  ",
  						 __func__,
  						 devfreq->governor->name, ret);
  				}
  				/* Fall through */
  			}
  			devfreq->governor = governor;
  			ret = devfreq->governor->event_handler(devfreq,
  						DEVFREQ_GOV_START, NULL);
  			if (ret) {
  				dev_warn(dev, "%s: Governor %s start=%d
  ",
  					 __func__, devfreq->governor->name,
  					 ret);
  			}
a3c98b8b2   MyungJoo Ham   PM: Introduce dev...
691
692
  		}
  	}
3aa173b8d   Nishanth Menon   PM / devfreq: pro...
693
694
  err_out:
  	mutex_unlock(&devfreq_list_lock);
a3c98b8b2   MyungJoo Ham   PM: Introduce dev...
695

3aa173b8d   Nishanth Menon   PM / devfreq: pro...
696
697
698
  	return err;
  }
  EXPORT_SYMBOL(devfreq_add_governor);
a3c98b8b2   MyungJoo Ham   PM: Introduce dev...
699

3aa173b8d   Nishanth Menon   PM / devfreq: pro...
700
701
702
703
704
705
706
  /**
   * devfreq_remove_device() - Remove devfreq feature from a device.
   * @governor:	the devfreq governor to be removed
   */
  int devfreq_remove_governor(struct devfreq_governor *governor)
  {
  	struct devfreq_governor *g;
1b5c1be2c   Nishanth Menon   PM / devfreq: map...
707
  	struct devfreq *devfreq;
3aa173b8d   Nishanth Menon   PM / devfreq: pro...
708
709
710
711
712
713
714
715
716
717
718
719
720
  	int err = 0;
  
  	if (!governor) {
  		pr_err("%s: Invalid parameters.
  ", __func__);
  		return -EINVAL;
  	}
  
  	mutex_lock(&devfreq_list_lock);
  	g = find_devfreq_governor(governor->name);
  	if (IS_ERR(g)) {
  		pr_err("%s: governor %s not registered
  ", __func__,
b9e1c8e82   Sachin Kamat   PM / devfreq: Fix...
721
  		       governor->name);
f9c08e2ac   Sachin Kamat   PM / devfreq: Fix...
722
  		err = PTR_ERR(g);
3aa173b8d   Nishanth Menon   PM / devfreq: pro...
723
724
  		goto err_out;
  	}
1b5c1be2c   Nishanth Menon   PM / devfreq: map...
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
  	list_for_each_entry(devfreq, &devfreq_list, node) {
  		int ret;
  		struct device *dev = devfreq->dev.parent;
  
  		if (!strncmp(devfreq->governor_name, governor->name,
  			     DEVFREQ_NAME_LEN)) {
  			/* we should have a devfreq governor! */
  			if (!devfreq->governor) {
  				dev_warn(dev, "%s: Governor %s NOT present
  ",
  					 __func__, governor->name);
  				continue;
  				/* Fall through */
  			}
  			ret = devfreq->governor->event_handler(devfreq,
  						DEVFREQ_GOV_STOP, NULL);
  			if (ret) {
  				dev_warn(dev, "%s: Governor %s stop=%d
  ",
  					 __func__, devfreq->governor->name,
  					 ret);
  			}
  			devfreq->governor = NULL;
  		}
  	}
3aa173b8d   Nishanth Menon   PM / devfreq: pro...
750
751
752
753
754
755
  
  	list_del(&governor->node);
  err_out:
  	mutex_unlock(&devfreq_list_lock);
  
  	return err;
a3c98b8b2   MyungJoo Ham   PM: Introduce dev...
756
  }
3aa173b8d   Nishanth Menon   PM / devfreq: pro...
757
  EXPORT_SYMBOL(devfreq_remove_governor);
a3c98b8b2   MyungJoo Ham   PM: Introduce dev...
758

a93d6b0a8   Greg Kroah-Hartman   devfreq: convert ...
759
  static ssize_t governor_show(struct device *dev,
9005b6509   MyungJoo Ham   PM / devfreq: Add...
760
761
  			     struct device_attribute *attr, char *buf)
  {
1b5c1be2c   Nishanth Menon   PM / devfreq: map...
762
763
  	if (!to_devfreq(dev)->governor)
  		return -EINVAL;
9005b6509   MyungJoo Ham   PM / devfreq: Add...
764
765
766
  	return sprintf(buf, "%s
  ", to_devfreq(dev)->governor->name);
  }
a93d6b0a8   Greg Kroah-Hartman   devfreq: convert ...
767
  static ssize_t governor_store(struct device *dev, struct device_attribute *attr,
0359d1afe   Nishanth Menon   PM / devfreq: all...
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
  			      const char *buf, size_t count)
  {
  	struct devfreq *df = to_devfreq(dev);
  	int ret;
  	char str_governor[DEVFREQ_NAME_LEN + 1];
  	struct devfreq_governor *governor;
  
  	ret = sscanf(buf, "%" __stringify(DEVFREQ_NAME_LEN) "s", str_governor);
  	if (ret != 1)
  		return -EINVAL;
  
  	mutex_lock(&devfreq_list_lock);
  	governor = find_devfreq_governor(str_governor);
  	if (IS_ERR(governor)) {
  		ret = PTR_ERR(governor);
  		goto out;
  	}
  	if (df->governor == governor)
  		goto out;
  
  	if (df->governor) {
  		ret = df->governor->event_handler(df, DEVFREQ_GOV_STOP, NULL);
  		if (ret) {
  			dev_warn(dev, "%s: Governor %s not stopped(%d)
  ",
  				 __func__, df->governor->name, ret);
  			goto out;
  		}
  	}
  	df->governor = governor;
  	strncpy(df->governor_name, governor->name, DEVFREQ_NAME_LEN);
  	ret = df->governor->event_handler(df, DEVFREQ_GOV_START, NULL);
  	if (ret)
  		dev_warn(dev, "%s: Governor %s not started(%d)
  ",
  			 __func__, df->governor->name, ret);
  out:
  	mutex_unlock(&devfreq_list_lock);
  
  	if (!ret)
  		ret = count;
  	return ret;
  }
a93d6b0a8   Greg Kroah-Hartman   devfreq: convert ...
811
812
813
814
815
  static DEVICE_ATTR_RW(governor);
  
  static ssize_t available_governors_show(struct device *d,
  					struct device_attribute *attr,
  					char *buf)
50a5b33e0   Nishanth Menon   PM / devfreq: Add...
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
  {
  	struct devfreq_governor *tmp_governor;
  	ssize_t count = 0;
  
  	mutex_lock(&devfreq_list_lock);
  	list_for_each_entry(tmp_governor, &devfreq_governor_list, node)
  		count += scnprintf(&buf[count], (PAGE_SIZE - count - 2),
  				   "%s ", tmp_governor->name);
  	mutex_unlock(&devfreq_list_lock);
  
  	/* Truncate the trailing space */
  	if (count)
  		count--;
  
  	count += sprintf(&buf[count], "
  ");
  
  	return count;
  }
a93d6b0a8   Greg Kroah-Hartman   devfreq: convert ...
835
  static DEVICE_ATTR_RO(available_governors);
0359d1afe   Nishanth Menon   PM / devfreq: all...
836

a93d6b0a8   Greg Kroah-Hartman   devfreq: convert ...
837
838
  static ssize_t cur_freq_show(struct device *dev, struct device_attribute *attr,
  			     char *buf)
9005b6509   MyungJoo Ham   PM / devfreq: Add...
839
  {
7f98a905d   Rajagopal Venkat   PM / devfreq: Add...
840
841
842
843
844
845
846
847
848
849
850
  	unsigned long freq;
  	struct devfreq *devfreq = to_devfreq(dev);
  
  	if (devfreq->profile->get_cur_freq &&
  		!devfreq->profile->get_cur_freq(devfreq->dev.parent, &freq))
  			return sprintf(buf, "%lu
  ", freq);
  
  	return sprintf(buf, "%lu
  ", devfreq->previous_freq);
  }
a93d6b0a8   Greg Kroah-Hartman   devfreq: convert ...
851
  static DEVICE_ATTR_RO(cur_freq);
7f98a905d   Rajagopal Venkat   PM / devfreq: Add...
852

a93d6b0a8   Greg Kroah-Hartman   devfreq: convert ...
853
854
  static ssize_t target_freq_show(struct device *dev,
  				struct device_attribute *attr, char *buf)
7f98a905d   Rajagopal Venkat   PM / devfreq: Add...
855
  {
9005b6509   MyungJoo Ham   PM / devfreq: Add...
856
857
858
  	return sprintf(buf, "%lu
  ", to_devfreq(dev)->previous_freq);
  }
a93d6b0a8   Greg Kroah-Hartman   devfreq: convert ...
859
  static DEVICE_ATTR_RO(target_freq);
9005b6509   MyungJoo Ham   PM / devfreq: Add...
860

a93d6b0a8   Greg Kroah-Hartman   devfreq: convert ...
861
  static ssize_t polling_interval_show(struct device *dev,
9005b6509   MyungJoo Ham   PM / devfreq: Add...
862
863
864
865
866
  				     struct device_attribute *attr, char *buf)
  {
  	return sprintf(buf, "%d
  ", to_devfreq(dev)->profile->polling_ms);
  }
a93d6b0a8   Greg Kroah-Hartman   devfreq: convert ...
867
  static ssize_t polling_interval_store(struct device *dev,
9005b6509   MyungJoo Ham   PM / devfreq: Add...
868
869
870
871
872
873
  				      struct device_attribute *attr,
  				      const char *buf, size_t count)
  {
  	struct devfreq *df = to_devfreq(dev);
  	unsigned int value;
  	int ret;
1b5c1be2c   Nishanth Menon   PM / devfreq: map...
874
875
  	if (!df->governor)
  		return -EINVAL;
9005b6509   MyungJoo Ham   PM / devfreq: Add...
876
877
  	ret = sscanf(buf, "%u", &value);
  	if (ret != 1)
12e26265e   Nishanth Menon   PM / devfreq: fix...
878
  		return -EINVAL;
9005b6509   MyungJoo Ham   PM / devfreq: Add...
879

7e6fdd4ba   Rajagopal Venkat   PM / devfreq: Cor...
880
  	df->governor->event_handler(df, DEVFREQ_GOV_INTERVAL, &value);
9005b6509   MyungJoo Ham   PM / devfreq: Add...
881
  	ret = count;
9005b6509   MyungJoo Ham   PM / devfreq: Add...
882
883
  	return ret;
  }
a93d6b0a8   Greg Kroah-Hartman   devfreq: convert ...
884
  static DEVICE_ATTR_RW(polling_interval);
9005b6509   MyungJoo Ham   PM / devfreq: Add...
885

a93d6b0a8   Greg Kroah-Hartman   devfreq: convert ...
886
  static ssize_t min_freq_store(struct device *dev, struct device_attribute *attr,
6530b9dea   MyungJoo Ham   PM / devfreq: add...
887
888
889
890
891
892
893
894
895
  			      const char *buf, size_t count)
  {
  	struct devfreq *df = to_devfreq(dev);
  	unsigned long value;
  	int ret;
  	unsigned long max;
  
  	ret = sscanf(buf, "%lu", &value);
  	if (ret != 1)
12e26265e   Nishanth Menon   PM / devfreq: fix...
896
  		return -EINVAL;
6530b9dea   MyungJoo Ham   PM / devfreq: add...
897
898
899
900
901
902
903
904
905
906
907
908
909
  
  	mutex_lock(&df->lock);
  	max = df->max_freq;
  	if (value && max && value > max) {
  		ret = -EINVAL;
  		goto unlock;
  	}
  
  	df->min_freq = value;
  	update_devfreq(df);
  	ret = count;
  unlock:
  	mutex_unlock(&df->lock);
6530b9dea   MyungJoo Ham   PM / devfreq: add...
910
911
  	return ret;
  }
a93d6b0a8   Greg Kroah-Hartman   devfreq: convert ...
912
  static ssize_t min_freq_show(struct device *dev, struct device_attribute *attr,
6530b9dea   MyungJoo Ham   PM / devfreq: add...
913
914
915
916
917
  			     char *buf)
  {
  	return sprintf(buf, "%lu
  ", to_devfreq(dev)->min_freq);
  }
a93d6b0a8   Greg Kroah-Hartman   devfreq: convert ...
918
  static ssize_t max_freq_store(struct device *dev, struct device_attribute *attr,
6530b9dea   MyungJoo Ham   PM / devfreq: add...
919
920
921
922
923
924
925
926
927
  			      const char *buf, size_t count)
  {
  	struct devfreq *df = to_devfreq(dev);
  	unsigned long value;
  	int ret;
  	unsigned long min;
  
  	ret = sscanf(buf, "%lu", &value);
  	if (ret != 1)
12e26265e   Nishanth Menon   PM / devfreq: fix...
928
  		return -EINVAL;
6530b9dea   MyungJoo Ham   PM / devfreq: add...
929
930
931
932
933
934
935
936
937
938
939
940
941
  
  	mutex_lock(&df->lock);
  	min = df->min_freq;
  	if (value && min && value < min) {
  		ret = -EINVAL;
  		goto unlock;
  	}
  
  	df->max_freq = value;
  	update_devfreq(df);
  	ret = count;
  unlock:
  	mutex_unlock(&df->lock);
6530b9dea   MyungJoo Ham   PM / devfreq: add...
942
943
  	return ret;
  }
a93d6b0a8   Greg Kroah-Hartman   devfreq: convert ...
944
  static DEVICE_ATTR_RW(min_freq);
6530b9dea   MyungJoo Ham   PM / devfreq: add...
945

a93d6b0a8   Greg Kroah-Hartman   devfreq: convert ...
946
  static ssize_t max_freq_show(struct device *dev, struct device_attribute *attr,
6530b9dea   MyungJoo Ham   PM / devfreq: add...
947
948
949
950
951
  			     char *buf)
  {
  	return sprintf(buf, "%lu
  ", to_devfreq(dev)->max_freq);
  }
a93d6b0a8   Greg Kroah-Hartman   devfreq: convert ...
952
  static DEVICE_ATTR_RW(max_freq);
6530b9dea   MyungJoo Ham   PM / devfreq: add...
953

a93d6b0a8   Greg Kroah-Hartman   devfreq: convert ...
954
955
956
  static ssize_t available_frequencies_show(struct device *d,
  					  struct device_attribute *attr,
  					  char *buf)
d287de855   Nishanth Menon   PM / devfreq: Add...
957
958
959
  {
  	struct devfreq *df = to_devfreq(d);
  	struct device *dev = df->dev.parent;
47d43ba73   Nishanth Menon   PM / OPP: rename ...
960
  	struct dev_pm_opp *opp;
d287de855   Nishanth Menon   PM / devfreq: Add...
961
962
963
964
965
  	ssize_t count = 0;
  	unsigned long freq = 0;
  
  	rcu_read_lock();
  	do {
5d4879cda   Nishanth Menon   PM / OPP: rename ...
966
  		opp = dev_pm_opp_find_freq_ceil(dev, &freq);
d287de855   Nishanth Menon   PM / devfreq: Add...
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
  		if (IS_ERR(opp))
  			break;
  
  		count += scnprintf(&buf[count], (PAGE_SIZE - count - 2),
  				   "%lu ", freq);
  		freq++;
  	} while (1);
  	rcu_read_unlock();
  
  	/* Truncate the trailing space */
  	if (count)
  		count--;
  
  	count += sprintf(&buf[count], "
  ");
  
  	return count;
  }
a93d6b0a8   Greg Kroah-Hartman   devfreq: convert ...
985
  static DEVICE_ATTR_RO(available_frequencies);
d287de855   Nishanth Menon   PM / devfreq: Add...
986

a93d6b0a8   Greg Kroah-Hartman   devfreq: convert ...
987
988
  static ssize_t trans_stat_show(struct device *dev,
  			       struct device_attribute *attr, char *buf)
e552bbaf5   Jonghwa Lee   PM / devfreq: Add...
989
990
991
  {
  	struct devfreq *devfreq = to_devfreq(dev);
  	ssize_t len;
39688ce6f   Rajagopal Venkat   PM / devfreq: acc...
992
  	int i, j;
e552bbaf5   Jonghwa Lee   PM / devfreq: Add...
993
  	unsigned int max_state = devfreq->profile->max_state;
39688ce6f   Rajagopal Venkat   PM / devfreq: acc...
994
995
  	if (!devfreq->stop_polling &&
  			devfreq_update_status(devfreq, devfreq->previous_freq))
e552bbaf5   Jonghwa Lee   PM / devfreq: Add...
996
997
998
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
  		return 0;
  
  	len = sprintf(buf, "   From  :   To
  ");
  	len += sprintf(buf + len, "         :");
  	for (i = 0; i < max_state; i++)
  		len += sprintf(buf + len, "%8u",
  				devfreq->profile->freq_table[i]);
  
  	len += sprintf(buf + len, "   time(ms)
  ");
  
  	for (i = 0; i < max_state; i++) {
  		if (devfreq->profile->freq_table[i]
  					== devfreq->previous_freq) {
  			len += sprintf(buf + len, "*");
  		} else {
  			len += sprintf(buf + len, " ");
  		}
  		len += sprintf(buf + len, "%8u:",
  				devfreq->profile->freq_table[i]);
  		for (j = 0; j < max_state; j++)
  			len += sprintf(buf + len, "%8u",
  				devfreq->trans_table[(i * max_state) + j]);
  		len += sprintf(buf + len, "%10u
  ",
  			jiffies_to_msecs(devfreq->time_in_state[i]));
  	}
  
  	len += sprintf(buf + len, "Total transition : %u
  ",
  					devfreq->total_trans);
  	return len;
  }
a93d6b0a8   Greg Kroah-Hartman   devfreq: convert ...
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
  static DEVICE_ATTR_RO(trans_stat);
  
  static struct attribute *devfreq_attrs[] = {
  	&dev_attr_governor.attr,
  	&dev_attr_available_governors.attr,
  	&dev_attr_cur_freq.attr,
  	&dev_attr_available_frequencies.attr,
  	&dev_attr_target_freq.attr,
  	&dev_attr_polling_interval.attr,
  	&dev_attr_min_freq.attr,
  	&dev_attr_max_freq.attr,
  	&dev_attr_trans_stat.attr,
  	NULL,
9005b6509   MyungJoo Ham   PM / devfreq: Add...
1043
  };
a93d6b0a8   Greg Kroah-Hartman   devfreq: convert ...
1044
  ATTRIBUTE_GROUPS(devfreq);
9005b6509   MyungJoo Ham   PM / devfreq: Add...
1045

a3c98b8b2   MyungJoo Ham   PM: Introduce dev...
1046
1047
1048
1049
1050
1051
1052
1053
  static int __init devfreq_init(void)
  {
  	devfreq_class = class_create(THIS_MODULE, "devfreq");
  	if (IS_ERR(devfreq_class)) {
  		pr_err("%s: couldn't create class
  ", __FILE__);
  		return PTR_ERR(devfreq_class);
  	}
7e6fdd4ba   Rajagopal Venkat   PM / devfreq: Cor...
1054
1055
  
  	devfreq_wq = create_freezable_workqueue("devfreq_wq");
ea7f4548c   Dan Carpenter   PM / devfreq: cre...
1056
  	if (!devfreq_wq) {
7e6fdd4ba   Rajagopal Venkat   PM / devfreq: Cor...
1057
1058
1059
  		class_destroy(devfreq_class);
  		pr_err("%s: couldn't create workqueue
  ", __FILE__);
ea7f4548c   Dan Carpenter   PM / devfreq: cre...
1060
  		return -ENOMEM;
7e6fdd4ba   Rajagopal Venkat   PM / devfreq: Cor...
1061
  	}
a93d6b0a8   Greg Kroah-Hartman   devfreq: convert ...
1062
  	devfreq_class->dev_groups = devfreq_groups;
7e6fdd4ba   Rajagopal Venkat   PM / devfreq: Cor...
1063

a3c98b8b2   MyungJoo Ham   PM: Introduce dev...
1064
1065
1066
1067
1068
1069
1070
  	return 0;
  }
  subsys_initcall(devfreq_init);
  
  static void __exit devfreq_exit(void)
  {
  	class_destroy(devfreq_class);
7e6fdd4ba   Rajagopal Venkat   PM / devfreq: Cor...
1071
  	destroy_workqueue(devfreq_wq);
a3c98b8b2   MyungJoo Ham   PM: Introduce dev...
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
  }
  module_exit(devfreq_exit);
  
  /*
   * The followings are helper functions for devfreq user device drivers with
   * OPP framework.
   */
  
  /**
   * devfreq_recommended_opp() - Helper function to get proper OPP for the
   *			     freq value given to target callback.
c5b4a1c15   Nishanth Menon   PM / devfreq: ker...
1083
1084
1085
   * @dev:	The devfreq user device. (parent of devfreq)
   * @freq:	The frequency given to target function
   * @flags:	Flags handed from devfreq framework.
a3c98b8b2   MyungJoo Ham   PM: Introduce dev...
1086
   *
bcb27549f   Nishanth Menon   PM / devfreq: add...
1087
1088
1089
1090
1091
   * Locking: This function must be called under rcu_read_lock(). opp is a rcu
   * protected pointer. The reason for the same is that the opp pointer which is
   * returned will remain valid for use with opp_get_{voltage, freq} only while
   * under the locked area. The pointer returned must be used prior to unlocking
   * with rcu_read_unlock() to maintain the integrity of the pointer.
a3c98b8b2   MyungJoo Ham   PM: Introduce dev...
1092
   */
47d43ba73   Nishanth Menon   PM / OPP: rename ...
1093
1094
1095
  struct dev_pm_opp *devfreq_recommended_opp(struct device *dev,
  					   unsigned long *freq,
  					   u32 flags)
a3c98b8b2   MyungJoo Ham   PM: Introduce dev...
1096
  {
47d43ba73   Nishanth Menon   PM / OPP: rename ...
1097
  	struct dev_pm_opp *opp;
a3c98b8b2   MyungJoo Ham   PM: Introduce dev...
1098

ab5f299f5   MyungJoo Ham   PM / devfreq: add...
1099
1100
  	if (flags & DEVFREQ_FLAG_LEAST_UPPER_BOUND) {
  		/* The freq is an upper bound. opp should be lower */
5d4879cda   Nishanth Menon   PM / OPP: rename ...
1101
  		opp = dev_pm_opp_find_freq_floor(dev, freq);
ab5f299f5   MyungJoo Ham   PM / devfreq: add...
1102
1103
  
  		/* If not available, use the closest opp */
0779726cc   Nishanth Menon   PM / OPP: predict...
1104
  		if (opp == ERR_PTR(-ERANGE))
5d4879cda   Nishanth Menon   PM / OPP: rename ...
1105
  			opp = dev_pm_opp_find_freq_ceil(dev, freq);
ab5f299f5   MyungJoo Ham   PM / devfreq: add...
1106
1107
  	} else {
  		/* The freq is an lower bound. opp should be higher */
5d4879cda   Nishanth Menon   PM / OPP: rename ...
1108
  		opp = dev_pm_opp_find_freq_ceil(dev, freq);
ab5f299f5   MyungJoo Ham   PM / devfreq: add...
1109
1110
  
  		/* If not available, use the closest opp */
0779726cc   Nishanth Menon   PM / OPP: predict...
1111
  		if (opp == ERR_PTR(-ERANGE))
5d4879cda   Nishanth Menon   PM / OPP: rename ...
1112
  			opp = dev_pm_opp_find_freq_floor(dev, freq);
ab5f299f5   MyungJoo Ham   PM / devfreq: add...
1113
  	}
a3c98b8b2   MyungJoo Ham   PM: Introduce dev...
1114
1115
  	return opp;
  }
bd7e92770   Ãrjan Eide   PM / devfreq: Exp...
1116
  EXPORT_SYMBOL(devfreq_recommended_opp);
a3c98b8b2   MyungJoo Ham   PM: Introduce dev...
1117
1118
1119
1120
1121
  
  /**
   * devfreq_register_opp_notifier() - Helper function to get devfreq notified
   *				   for any changes in the OPP availability
   *				   changes
c5b4a1c15   Nishanth Menon   PM / devfreq: ker...
1122
1123
   * @dev:	The devfreq user device. (parent of devfreq)
   * @devfreq:	The devfreq object.
a3c98b8b2   MyungJoo Ham   PM: Introduce dev...
1124
1125
1126
   */
  int devfreq_register_opp_notifier(struct device *dev, struct devfreq *devfreq)
  {
389baed01   MyungJoo Ham   PM / devfreq: mis...
1127
1128
  	struct srcu_notifier_head *nh;
  	int ret = 0;
a3c98b8b2   MyungJoo Ham   PM: Introduce dev...
1129

389baed01   MyungJoo Ham   PM / devfreq: mis...
1130
  	rcu_read_lock();
5d4879cda   Nishanth Menon   PM / OPP: rename ...
1131
  	nh = dev_pm_opp_get_notifier(dev);
a3c98b8b2   MyungJoo Ham   PM: Introduce dev...
1132
  	if (IS_ERR(nh))
389baed01   MyungJoo Ham   PM / devfreq: mis...
1133
1134
1135
1136
1137
1138
  		ret = PTR_ERR(nh);
  	rcu_read_unlock();
  	if (!ret)
  		ret = srcu_notifier_chain_register(nh, &devfreq->nb);
  
  	return ret;
a3c98b8b2   MyungJoo Ham   PM: Introduce dev...
1139
  }
bd7e92770   Ãrjan Eide   PM / devfreq: Exp...
1140
  EXPORT_SYMBOL(devfreq_register_opp_notifier);
a3c98b8b2   MyungJoo Ham   PM: Introduce dev...
1141
1142
1143
1144
1145
  
  /**
   * devfreq_unregister_opp_notifier() - Helper function to stop getting devfreq
   *				     notified for any changes in the OPP
   *				     availability changes anymore.
c5b4a1c15   Nishanth Menon   PM / devfreq: ker...
1146
1147
   * @dev:	The devfreq user device. (parent of devfreq)
   * @devfreq:	The devfreq object.
a3c98b8b2   MyungJoo Ham   PM: Introduce dev...
1148
1149
1150
1151
1152
1153
   *
   * At exit() callback of devfreq_dev_profile, this must be included if
   * devfreq_recommended_opp is used.
   */
  int devfreq_unregister_opp_notifier(struct device *dev, struct devfreq *devfreq)
  {
389baed01   MyungJoo Ham   PM / devfreq: mis...
1154
1155
  	struct srcu_notifier_head *nh;
  	int ret = 0;
a3c98b8b2   MyungJoo Ham   PM: Introduce dev...
1156

389baed01   MyungJoo Ham   PM / devfreq: mis...
1157
  	rcu_read_lock();
5d4879cda   Nishanth Menon   PM / OPP: rename ...
1158
  	nh = dev_pm_opp_get_notifier(dev);
a3c98b8b2   MyungJoo Ham   PM: Introduce dev...
1159
  	if (IS_ERR(nh))
389baed01   MyungJoo Ham   PM / devfreq: mis...
1160
1161
1162
1163
1164
1165
  		ret = PTR_ERR(nh);
  	rcu_read_unlock();
  	if (!ret)
  		ret = srcu_notifier_chain_unregister(nh, &devfreq->nb);
  
  	return ret;
a3c98b8b2   MyungJoo Ham   PM: Introduce dev...
1166
  }
bd7e92770   Ãrjan Eide   PM / devfreq: Exp...
1167
  EXPORT_SYMBOL(devfreq_unregister_opp_notifier);
a3c98b8b2   MyungJoo Ham   PM: Introduce dev...
1168

d5b040d0c   Chanwoo Choi   PM / devfreq: Add...
1169
1170
1171
1172
1173
1174
1175
1176
1177
1178
1179
1180
1181
1182
1183
1184
1185
1186
1187
1188
1189
1190
1191
1192
1193
1194
1195
1196
1197
1198
1199
1200
1201
1202
1203
1204
1205
1206
1207
1208
1209
1210
1211
1212
1213
1214
1215
  static void devm_devfreq_opp_release(struct device *dev, void *res)
  {
  	devfreq_unregister_opp_notifier(dev, *(struct devfreq **)res);
  }
  
  /**
   * devm_ devfreq_register_opp_notifier()
   *		- Resource-managed devfreq_register_opp_notifier()
   * @dev:	The devfreq user device. (parent of devfreq)
   * @devfreq:	The devfreq object.
   */
  int devm_devfreq_register_opp_notifier(struct device *dev,
  				       struct devfreq *devfreq)
  {
  	struct devfreq **ptr;
  	int ret;
  
  	ptr = devres_alloc(devm_devfreq_opp_release, sizeof(*ptr), GFP_KERNEL);
  	if (!ptr)
  		return -ENOMEM;
  
  	ret = devfreq_register_opp_notifier(dev, devfreq);
  	if (ret) {
  		devres_free(ptr);
  		return ret;
  	}
  
  	*ptr = devfreq;
  	devres_add(dev, ptr);
  
  	return 0;
  }
  EXPORT_SYMBOL(devm_devfreq_register_opp_notifier);
  
  /**
   * devm_devfreq_unregister_opp_notifier()
   *		- Resource-managed devfreq_unregister_opp_notifier()
   * @dev:	The devfreq user device. (parent of devfreq)
   * @devfreq:	The devfreq object.
   */
  void devm_devfreq_unregister_opp_notifier(struct device *dev,
  					 struct devfreq *devfreq)
  {
  	WARN_ON(devres_release(dev, devm_devfreq_opp_release,
  			       devm_devfreq_dev_match, devfreq));
  }
  EXPORT_SYMBOL(devm_devfreq_unregister_opp_notifier);
a3c98b8b2   MyungJoo Ham   PM: Introduce dev...
1216
1217
1218
  MODULE_AUTHOR("MyungJoo Ham <myungjoo.ham@samsung.com>");
  MODULE_DESCRIPTION("devfreq class support");
  MODULE_LICENSE("GPL");