Blame view

drivers/devfreq/tegra30-devfreq.c 19 KB
9952f6918   Thomas Gleixner   treewide: Replace...
1
  // SPDX-License-Identifier: GPL-2.0-only
6234f3801   Tomeu Vizoso   PM / devfreq: teg...
2
3
4
5
6
  /*
   * A devfreq driver for NVIDIA Tegra SoCs
   *
   * Copyright (c) 2014 NVIDIA CORPORATION. All rights reserved.
   * Copyright (C) 2014 Google, Inc
6234f3801   Tomeu Vizoso   PM / devfreq: teg...
7
8
9
10
11
12
13
14
   */
  
  #include <linux/clk.h>
  #include <linux/cpufreq.h>
  #include <linux/devfreq.h>
  #include <linux/interrupt.h>
  #include <linux/io.h>
  #include <linux/module.h>
ac3167257   Randy Dunlap   headers: separate...
15
  #include <linux/mod_devicetable.h>
6234f3801   Tomeu Vizoso   PM / devfreq: teg...
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
  #include <linux/platform_device.h>
  #include <linux/pm_opp.h>
  #include <linux/reset.h>
  
  #include "governor.h"
  
  #define ACTMON_GLB_STATUS					0x0
  #define ACTMON_GLB_PERIOD_CTRL					0x4
  
  #define ACTMON_DEV_CTRL						0x0
  #define ACTMON_DEV_CTRL_K_VAL_SHIFT				10
  #define ACTMON_DEV_CTRL_ENB_PERIODIC				BIT(18)
  #define ACTMON_DEV_CTRL_AVG_BELOW_WMARK_EN			BIT(20)
  #define ACTMON_DEV_CTRL_AVG_ABOVE_WMARK_EN			BIT(21)
  #define ACTMON_DEV_CTRL_CONSECUTIVE_BELOW_WMARK_NUM_SHIFT	23
  #define ACTMON_DEV_CTRL_CONSECUTIVE_ABOVE_WMARK_NUM_SHIFT	26
  #define ACTMON_DEV_CTRL_CONSECUTIVE_BELOW_WMARK_EN		BIT(29)
  #define ACTMON_DEV_CTRL_CONSECUTIVE_ABOVE_WMARK_EN		BIT(30)
  #define ACTMON_DEV_CTRL_ENB					BIT(31)
  
  #define ACTMON_DEV_UPPER_WMARK					0x4
  #define ACTMON_DEV_LOWER_WMARK					0x8
  #define ACTMON_DEV_INIT_AVG					0xc
  #define ACTMON_DEV_AVG_UPPER_WMARK				0x10
  #define ACTMON_DEV_AVG_LOWER_WMARK				0x14
  #define ACTMON_DEV_COUNT_WEIGHT					0x18
  #define ACTMON_DEV_AVG_COUNT					0x20
  #define ACTMON_DEV_INTR_STATUS					0x24
  
  #define ACTMON_INTR_STATUS_CLEAR				0xffffffff
  
  #define ACTMON_DEV_INTR_CONSECUTIVE_UPPER			BIT(31)
  #define ACTMON_DEV_INTR_CONSECUTIVE_LOWER			BIT(30)
  
  #define ACTMON_ABOVE_WMARK_WINDOW				1
  #define ACTMON_BELOW_WMARK_WINDOW				3
  #define ACTMON_BOOST_FREQ_STEP					16000
11573e913   Tomeu Vizoso   PM / devfreq: teg...
53
54
  /*
   * Activity counter is incremented every 256 memory transactions, and each
6234f3801   Tomeu Vizoso   PM / devfreq: teg...
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
   * transaction takes 4 EMC clocks for Tegra124; So the COUNT_WEIGHT is
   * 4 * 256 = 1024.
   */
  #define ACTMON_COUNT_WEIGHT					0x400
  
  /*
   * ACTMON_AVERAGE_WINDOW_LOG2: default value for @DEV_CTRL_K_VAL, which
   * translates to 2 ^ (K_VAL + 1). ex: 2 ^ (6 + 1) = 128
   */
  #define ACTMON_AVERAGE_WINDOW_LOG2			6
  #define ACTMON_SAMPLING_PERIOD				12 /* ms */
  #define ACTMON_DEFAULT_AVG_BAND				6  /* 1/10 of % */
  
  #define KHZ							1000
  
  /* Assume that the bus is saturated if the utilization is 25% */
  #define BUS_SATURATION_RATIO					25
  
  /**
   * struct tegra_devfreq_device_config - configuration specific to an ACTMON
   * device
   *
11573e913   Tomeu Vizoso   PM / devfreq: teg...
77
   * Coefficients and thresholds are percentages unless otherwise noted
6234f3801   Tomeu Vizoso   PM / devfreq: teg...
78
79
80
81
   */
  struct tegra_devfreq_device_config {
  	u32		offset;
  	u32		irq_mask;
11573e913   Tomeu Vizoso   PM / devfreq: teg...
82
  	/* Factors applied to boost_freq every consecutive watermark breach */
6234f3801   Tomeu Vizoso   PM / devfreq: teg...
83
84
  	unsigned int	boost_up_coeff;
  	unsigned int	boost_down_coeff;
11573e913   Tomeu Vizoso   PM / devfreq: teg...
85
86
  
  	/* Define the watermark bounds when applied to the current avg */
6234f3801   Tomeu Vizoso   PM / devfreq: teg...
87
88
  	unsigned int	boost_up_threshold;
  	unsigned int	boost_down_threshold;
11573e913   Tomeu Vizoso   PM / devfreq: teg...
89
90
91
92
93
94
  
  	/*
  	 * Threshold of activity (cycles) below which the CPU frequency isn't
  	 * to be taken into account. This is to avoid increasing the EMC
  	 * frequency when the CPU is very busy but not accessing the bus often.
  	 */
6234f3801   Tomeu Vizoso   PM / devfreq: teg...
95
96
97
98
99
100
101
102
103
104
  	u32		avg_dependency_threshold;
  };
  
  enum tegra_actmon_device {
  	MCALL = 0,
  	MCCPU,
  };
  
  static struct tegra_devfreq_device_config actmon_device_configs[] = {
  	{
11573e913   Tomeu Vizoso   PM / devfreq: teg...
105
  		/* MCALL: All memory accesses (including from the CPUs) */
6234f3801   Tomeu Vizoso   PM / devfreq: teg...
106
107
108
109
110
111
112
113
  		.offset = 0x1c0,
  		.irq_mask = 1 << 26,
  		.boost_up_coeff = 200,
  		.boost_down_coeff = 50,
  		.boost_up_threshold = 60,
  		.boost_down_threshold = 40,
  	},
  	{
11573e913   Tomeu Vizoso   PM / devfreq: teg...
114
  		/* MCCPU: memory accesses from the CPUs */
6234f3801   Tomeu Vizoso   PM / devfreq: teg...
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
  		.offset = 0x200,
  		.irq_mask = 1 << 25,
  		.boost_up_coeff = 800,
  		.boost_down_coeff = 90,
  		.boost_up_threshold = 27,
  		.boost_down_threshold = 10,
  		.avg_dependency_threshold = 50000,
  	},
  };
  
  /**
   * struct tegra_devfreq_device - state specific to an ACTMON device
   *
   * Frequencies are in kHz.
   */
  struct tegra_devfreq_device {
  	const struct tegra_devfreq_device_config *config;
11573e913   Tomeu Vizoso   PM / devfreq: teg...
132
  	void __iomem *regs;
6234f3801   Tomeu Vizoso   PM / devfreq: teg...
133

11573e913   Tomeu Vizoso   PM / devfreq: teg...
134
135
  	/* Average event count sampled in the last interrupt */
  	u32 avg_count;
6234f3801   Tomeu Vizoso   PM / devfreq: teg...
136

11573e913   Tomeu Vizoso   PM / devfreq: teg...
137
138
139
140
141
142
143
144
  	/*
  	 * Extra frequency to increase the target by due to consecutive
  	 * watermark breaches.
  	 */
  	unsigned long boost_freq;
  
  	/* Optimal frequency calculated from the stats for this device */
  	unsigned long target_freq;
6234f3801   Tomeu Vizoso   PM / devfreq: teg...
145
146
147
148
  };
  
  struct tegra_devfreq {
  	struct devfreq		*devfreq;
6234f3801   Tomeu Vizoso   PM / devfreq: teg...
149
150
151
  	struct reset_control	*reset;
  	struct clk		*clock;
  	void __iomem		*regs;
6234f3801   Tomeu Vizoso   PM / devfreq: teg...
152
153
154
155
156
157
  	struct clk		*emc_clock;
  	unsigned long		max_freq;
  	unsigned long		cur_freq;
  	struct notifier_block	rate_change_nb;
  
  	struct tegra_devfreq_device devices[ARRAY_SIZE(actmon_device_configs)];
7514dd05e   Dmitry Osipenko   PM / devfreq: teg...
158
159
  
  	int irq;
6234f3801   Tomeu Vizoso   PM / devfreq: teg...
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
  };
  
  struct tegra_actmon_emc_ratio {
  	unsigned long cpu_freq;
  	unsigned long emc_freq;
  };
  
  static struct tegra_actmon_emc_ratio actmon_emc_ratios[] = {
  	{ 1400000, ULONG_MAX },
  	{ 1200000,    750000 },
  	{ 1100000,    600000 },
  	{ 1000000,    500000 },
  	{  800000,    375000 },
  	{  500000,    200000 },
  	{  250000,    100000 },
  };
11573e913   Tomeu Vizoso   PM / devfreq: teg...
176
177
  static u32 actmon_readl(struct tegra_devfreq *tegra, u32 offset)
  {
efe9043db   Dmitry Osipenko   PM / devfreq: teg...
178
  	return readl_relaxed(tegra->regs + offset);
11573e913   Tomeu Vizoso   PM / devfreq: teg...
179
180
181
182
  }
  
  static void actmon_writel(struct tegra_devfreq *tegra, u32 val, u32 offset)
  {
efe9043db   Dmitry Osipenko   PM / devfreq: teg...
183
  	writel_relaxed(val, tegra->regs + offset);
11573e913   Tomeu Vizoso   PM / devfreq: teg...
184
185
186
187
  }
  
  static u32 device_readl(struct tegra_devfreq_device *dev, u32 offset)
  {
efe9043db   Dmitry Osipenko   PM / devfreq: teg...
188
  	return readl_relaxed(dev->regs + offset);
11573e913   Tomeu Vizoso   PM / devfreq: teg...
189
190
191
192
193
  }
  
  static void device_writel(struct tegra_devfreq_device *dev, u32 val,
  			  u32 offset)
  {
efe9043db   Dmitry Osipenko   PM / devfreq: teg...
194
  	writel_relaxed(val, dev->regs + offset);
11573e913   Tomeu Vizoso   PM / devfreq: teg...
195
  }
6234f3801   Tomeu Vizoso   PM / devfreq: teg...
196
197
198
199
  static unsigned long do_percent(unsigned long val, unsigned int pct)
  {
  	return val * pct / 100;
  }
11573e913   Tomeu Vizoso   PM / devfreq: teg...
200
201
  static void tegra_devfreq_update_avg_wmark(struct tegra_devfreq *tegra,
  					   struct tegra_devfreq_device *dev)
6234f3801   Tomeu Vizoso   PM / devfreq: teg...
202
203
  {
  	u32 avg = dev->avg_count;
11573e913   Tomeu Vizoso   PM / devfreq: teg...
204
205
206
207
  	u32 avg_band_freq = tegra->max_freq * ACTMON_DEFAULT_AVG_BAND / KHZ;
  	u32 band = avg_band_freq * ACTMON_SAMPLING_PERIOD;
  
  	device_writel(dev, avg + band, ACTMON_DEV_AVG_UPPER_WMARK);
6234f3801   Tomeu Vizoso   PM / devfreq: teg...
208

11573e913   Tomeu Vizoso   PM / devfreq: teg...
209
210
  	avg = max(dev->avg_count, band);
  	device_writel(dev, avg - band, ACTMON_DEV_AVG_LOWER_WMARK);
6234f3801   Tomeu Vizoso   PM / devfreq: teg...
211
212
213
214
215
216
  }
  
  static void tegra_devfreq_update_wmark(struct tegra_devfreq *tegra,
  				       struct tegra_devfreq_device *dev)
  {
  	u32 val = tegra->cur_freq * ACTMON_SAMPLING_PERIOD;
11573e913   Tomeu Vizoso   PM / devfreq: teg...
217
218
  	device_writel(dev, do_percent(val, dev->config->boost_up_threshold),
  		      ACTMON_DEV_UPPER_WMARK);
6234f3801   Tomeu Vizoso   PM / devfreq: teg...
219

11573e913   Tomeu Vizoso   PM / devfreq: teg...
220
221
  	device_writel(dev, do_percent(val, dev->config->boost_down_threshold),
  		      ACTMON_DEV_LOWER_WMARK);
6234f3801   Tomeu Vizoso   PM / devfreq: teg...
222
223
224
225
226
  }
  
  static void actmon_write_barrier(struct tegra_devfreq *tegra)
  {
  	/* ensure the update has reached the ACTMON */
ed2a8dd22   Dmitry Osipenko   PM / devfreq: teg...
227
  	readl(tegra->regs + ACTMON_GLB_STATUS);
6234f3801   Tomeu Vizoso   PM / devfreq: teg...
228
  }
11573e913   Tomeu Vizoso   PM / devfreq: teg...
229
230
  static void actmon_isr_device(struct tegra_devfreq *tegra,
  			      struct tegra_devfreq_device *dev)
6234f3801   Tomeu Vizoso   PM / devfreq: teg...
231
  {
11573e913   Tomeu Vizoso   PM / devfreq: teg...
232
  	u32 intr_status, dev_ctrl;
6234f3801   Tomeu Vizoso   PM / devfreq: teg...
233

11573e913   Tomeu Vizoso   PM / devfreq: teg...
234
235
  	dev->avg_count = device_readl(dev, ACTMON_DEV_AVG_COUNT);
  	tegra_devfreq_update_avg_wmark(tegra, dev);
6234f3801   Tomeu Vizoso   PM / devfreq: teg...
236

11573e913   Tomeu Vizoso   PM / devfreq: teg...
237
238
  	intr_status = device_readl(dev, ACTMON_DEV_INTR_STATUS);
  	dev_ctrl = device_readl(dev, ACTMON_DEV_CTRL);
6234f3801   Tomeu Vizoso   PM / devfreq: teg...
239

11573e913   Tomeu Vizoso   PM / devfreq: teg...
240
  	if (intr_status & ACTMON_DEV_INTR_CONSECUTIVE_UPPER) {
6234f3801   Tomeu Vizoso   PM / devfreq: teg...
241
242
243
244
245
246
  		/*
  		 * new_boost = min(old_boost * up_coef + step, max_freq)
  		 */
  		dev->boost_freq = do_percent(dev->boost_freq,
  					     dev->config->boost_up_coeff);
  		dev->boost_freq += ACTMON_BOOST_FREQ_STEP;
6234f3801   Tomeu Vizoso   PM / devfreq: teg...
247

11573e913   Tomeu Vizoso   PM / devfreq: teg...
248
249
250
251
252
253
254
  		dev_ctrl |= ACTMON_DEV_CTRL_CONSECUTIVE_BELOW_WMARK_EN;
  
  		if (dev->boost_freq >= tegra->max_freq)
  			dev->boost_freq = tegra->max_freq;
  		else
  			dev_ctrl |= ACTMON_DEV_CTRL_CONSECUTIVE_ABOVE_WMARK_EN;
  	} else if (intr_status & ACTMON_DEV_INTR_CONSECUTIVE_LOWER) {
6234f3801   Tomeu Vizoso   PM / devfreq: teg...
255
256
257
258
259
260
  		/*
  		 * new_boost = old_boost * down_coef
  		 * or 0 if (old_boost * down_coef < step / 2)
  		 */
  		dev->boost_freq = do_percent(dev->boost_freq,
  					     dev->config->boost_down_coeff);
11573e913   Tomeu Vizoso   PM / devfreq: teg...
261
262
263
264
  
  		dev_ctrl |= ACTMON_DEV_CTRL_CONSECUTIVE_ABOVE_WMARK_EN;
  
  		if (dev->boost_freq < (ACTMON_BOOST_FREQ_STEP >> 1))
6234f3801   Tomeu Vizoso   PM / devfreq: teg...
265
  			dev->boost_freq = 0;
11573e913   Tomeu Vizoso   PM / devfreq: teg...
266
267
  		else
  			dev_ctrl |= ACTMON_DEV_CTRL_CONSECUTIVE_BELOW_WMARK_EN;
6234f3801   Tomeu Vizoso   PM / devfreq: teg...
268
269
270
  	}
  
  	if (dev->config->avg_dependency_threshold) {
6234f3801   Tomeu Vizoso   PM / devfreq: teg...
271
  		if (dev->avg_count >= dev->config->avg_dependency_threshold)
11573e913   Tomeu Vizoso   PM / devfreq: teg...
272
  			dev_ctrl |= ACTMON_DEV_CTRL_CONSECUTIVE_BELOW_WMARK_EN;
6234f3801   Tomeu Vizoso   PM / devfreq: teg...
273
  		else if (dev->boost_freq == 0)
11573e913   Tomeu Vizoso   PM / devfreq: teg...
274
  			dev_ctrl &= ~ACTMON_DEV_CTRL_CONSECUTIVE_BELOW_WMARK_EN;
6234f3801   Tomeu Vizoso   PM / devfreq: teg...
275
  	}
11573e913   Tomeu Vizoso   PM / devfreq: teg...
276
277
278
  	device_writel(dev, dev_ctrl, ACTMON_DEV_CTRL);
  
  	device_writel(dev, ACTMON_INTR_STATUS_CLEAR, ACTMON_DEV_INTR_STATUS);
6234f3801   Tomeu Vizoso   PM / devfreq: teg...
279
280
  
  	actmon_write_barrier(tegra);
6234f3801   Tomeu Vizoso   PM / devfreq: teg...
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
  }
  
  static unsigned long actmon_cpu_to_emc_rate(struct tegra_devfreq *tegra,
  					    unsigned long cpu_freq)
  {
  	unsigned int i;
  	struct tegra_actmon_emc_ratio *ratio = actmon_emc_ratios;
  
  	for (i = 0; i < ARRAY_SIZE(actmon_emc_ratios); i++, ratio++) {
  		if (cpu_freq >= ratio->cpu_freq) {
  			if (ratio->emc_freq >= tegra->max_freq)
  				return tegra->max_freq;
  			else
  				return ratio->emc_freq;
  		}
  	}
  
  	return 0;
  }
  
  static void actmon_update_target(struct tegra_devfreq *tegra,
  				 struct tegra_devfreq_device *dev)
  {
  	unsigned long cpu_freq = 0;
  	unsigned long static_cpu_emc_freq = 0;
  	unsigned int avg_sustain_coef;
6234f3801   Tomeu Vizoso   PM / devfreq: teg...
307
308
309
310
311
  
  	if (dev->config->avg_dependency_threshold) {
  		cpu_freq = cpufreq_get(0);
  		static_cpu_emc_freq = actmon_cpu_to_emc_rate(tegra, cpu_freq);
  	}
6234f3801   Tomeu Vizoso   PM / devfreq: teg...
312
313
314
315
316
317
318
  	dev->target_freq = dev->avg_count / ACTMON_SAMPLING_PERIOD;
  	avg_sustain_coef = 100 * 100 / dev->config->boost_up_threshold;
  	dev->target_freq = do_percent(dev->target_freq, avg_sustain_coef);
  	dev->target_freq += dev->boost_freq;
  
  	if (dev->avg_count >= dev->config->avg_dependency_threshold)
  		dev->target_freq = max(dev->target_freq, static_cpu_emc_freq);
6234f3801   Tomeu Vizoso   PM / devfreq: teg...
319
320
321
322
323
  }
  
  static irqreturn_t actmon_thread_isr(int irq, void *data)
  {
  	struct tegra_devfreq *tegra = data;
dd3f2616b   Dmitry Osipenko   PM / devfreq: teg...
324
325
326
  	bool handled = false;
  	unsigned int i;
  	u32 val;
6234f3801   Tomeu Vizoso   PM / devfreq: teg...
327
328
  
  	mutex_lock(&tegra->devfreq->lock);
dd3f2616b   Dmitry Osipenko   PM / devfreq: teg...
329
330
331
332
333
334
335
336
337
338
339
  
  	val = actmon_readl(tegra, ACTMON_GLB_STATUS);
  	for (i = 0; i < ARRAY_SIZE(tegra->devices); i++) {
  		if (val & tegra->devices[i].config->irq_mask) {
  			actmon_isr_device(tegra, tegra->devices + i);
  			handled = true;
  		}
  	}
  
  	if (handled)
  		update_devfreq(tegra->devfreq);
6234f3801   Tomeu Vizoso   PM / devfreq: teg...
340
  	mutex_unlock(&tegra->devfreq->lock);
dd3f2616b   Dmitry Osipenko   PM / devfreq: teg...
341
  	return handled ? IRQ_HANDLED : IRQ_NONE;
6234f3801   Tomeu Vizoso   PM / devfreq: teg...
342
343
344
345
346
347
  }
  
  static int tegra_actmon_rate_notify_cb(struct notifier_block *nb,
  				       unsigned long action, void *ptr)
  {
  	struct clk_notifier_data *data = ptr;
11573e913   Tomeu Vizoso   PM / devfreq: teg...
348
349
  	struct tegra_devfreq *tegra;
  	struct tegra_devfreq_device *dev;
6234f3801   Tomeu Vizoso   PM / devfreq: teg...
350
  	unsigned int i;
6234f3801   Tomeu Vizoso   PM / devfreq: teg...
351

11573e913   Tomeu Vizoso   PM / devfreq: teg...
352
353
  	if (action != POST_RATE_CHANGE)
  		return NOTIFY_OK;
6234f3801   Tomeu Vizoso   PM / devfreq: teg...
354

11573e913   Tomeu Vizoso   PM / devfreq: teg...
355
  	tegra = container_of(nb, struct tegra_devfreq, rate_change_nb);
6234f3801   Tomeu Vizoso   PM / devfreq: teg...
356

11573e913   Tomeu Vizoso   PM / devfreq: teg...
357
  	tegra->cur_freq = data->new_rate / KHZ;
6234f3801   Tomeu Vizoso   PM / devfreq: teg...
358

11573e913   Tomeu Vizoso   PM / devfreq: teg...
359
360
  	for (i = 0; i < ARRAY_SIZE(tegra->devices); i++) {
  		dev = &tegra->devices[i];
11573e913   Tomeu Vizoso   PM / devfreq: teg...
361
  		tegra_devfreq_update_wmark(tegra, dev);
11573e913   Tomeu Vizoso   PM / devfreq: teg...
362
  	}
6234f3801   Tomeu Vizoso   PM / devfreq: teg...
363

11573e913   Tomeu Vizoso   PM / devfreq: teg...
364
  	actmon_write_barrier(tegra);
6234f3801   Tomeu Vizoso   PM / devfreq: teg...
365
366
367
  
  	return NOTIFY_OK;
  }
11573e913   Tomeu Vizoso   PM / devfreq: teg...
368
369
  static void tegra_actmon_configure_device(struct tegra_devfreq *tegra,
  					  struct tegra_devfreq_device *dev)
6234f3801   Tomeu Vizoso   PM / devfreq: teg...
370
  {
11573e913   Tomeu Vizoso   PM / devfreq: teg...
371
  	u32 val = 0;
6234f3801   Tomeu Vizoso   PM / devfreq: teg...
372

11573e913   Tomeu Vizoso   PM / devfreq: teg...
373
  	dev->target_freq = tegra->cur_freq;
6234f3801   Tomeu Vizoso   PM / devfreq: teg...
374

11573e913   Tomeu Vizoso   PM / devfreq: teg...
375
376
  	dev->avg_count = tegra->cur_freq * ACTMON_SAMPLING_PERIOD;
  	device_writel(dev, dev->avg_count, ACTMON_DEV_INIT_AVG);
6234f3801   Tomeu Vizoso   PM / devfreq: teg...
377

11573e913   Tomeu Vizoso   PM / devfreq: teg...
378
379
  	tegra_devfreq_update_avg_wmark(tegra, dev);
  	tegra_devfreq_update_wmark(tegra, dev);
6234f3801   Tomeu Vizoso   PM / devfreq: teg...
380

11573e913   Tomeu Vizoso   PM / devfreq: teg...
381
382
383
384
385
386
387
388
389
390
  	device_writel(dev, ACTMON_COUNT_WEIGHT, ACTMON_DEV_COUNT_WEIGHT);
  	device_writel(dev, ACTMON_INTR_STATUS_CLEAR, ACTMON_DEV_INTR_STATUS);
  
  	val |= ACTMON_DEV_CTRL_ENB_PERIODIC;
  	val |= (ACTMON_AVERAGE_WINDOW_LOG2 - 1)
  		<< ACTMON_DEV_CTRL_K_VAL_SHIFT;
  	val |= (ACTMON_BELOW_WMARK_WINDOW - 1)
  		<< ACTMON_DEV_CTRL_CONSECUTIVE_BELOW_WMARK_NUM_SHIFT;
  	val |= (ACTMON_ABOVE_WMARK_WINDOW - 1)
  		<< ACTMON_DEV_CTRL_CONSECUTIVE_ABOVE_WMARK_NUM_SHIFT;
546ff0938   Dmitry Osipenko   PM / devfreq: teg...
391
392
393
394
  	val |= ACTMON_DEV_CTRL_AVG_ABOVE_WMARK_EN;
  	val |= ACTMON_DEV_CTRL_AVG_BELOW_WMARK_EN;
  	val |= ACTMON_DEV_CTRL_CONSECUTIVE_BELOW_WMARK_EN;
  	val |= ACTMON_DEV_CTRL_CONSECUTIVE_ABOVE_WMARK_EN;
11573e913   Tomeu Vizoso   PM / devfreq: teg...
395
396
397
  	val |= ACTMON_DEV_CTRL_ENB;
  
  	device_writel(dev, val, ACTMON_DEV_CTRL);
546ff0938   Dmitry Osipenko   PM / devfreq: teg...
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
  }
  
  static void tegra_actmon_start(struct tegra_devfreq *tegra)
  {
  	unsigned int i;
  
  	disable_irq(tegra->irq);
  
  	actmon_writel(tegra, ACTMON_SAMPLING_PERIOD - 1,
  		      ACTMON_GLB_PERIOD_CTRL);
  
  	for (i = 0; i < ARRAY_SIZE(tegra->devices); i++)
  		tegra_actmon_configure_device(tegra, &tegra->devices[i]);
  
  	actmon_write_barrier(tegra);
  
  	enable_irq(tegra->irq);
  }
  
  static void tegra_actmon_stop(struct tegra_devfreq *tegra)
  {
  	unsigned int i;
  
  	disable_irq(tegra->irq);
  
  	for (i = 0; i < ARRAY_SIZE(tegra->devices); i++) {
  		device_writel(&tegra->devices[i], 0x00000000, ACTMON_DEV_CTRL);
  		device_writel(&tegra->devices[i], ACTMON_INTR_STATUS_CLEAR,
  			      ACTMON_DEV_INTR_STATUS);
  	}
11573e913   Tomeu Vizoso   PM / devfreq: teg...
428
429
  
  	actmon_write_barrier(tegra);
546ff0938   Dmitry Osipenko   PM / devfreq: teg...
430
431
  
  	enable_irq(tegra->irq);
6234f3801   Tomeu Vizoso   PM / devfreq: teg...
432
433
434
435
436
  }
  
  static int tegra_devfreq_target(struct device *dev, unsigned long *freq,
  				u32 flags)
  {
11573e913   Tomeu Vizoso   PM / devfreq: teg...
437
  	struct tegra_devfreq *tegra = dev_get_drvdata(dev);
30af44fae   Dmitry Osipenko   PM / devfreq: teg...
438
  	struct devfreq *devfreq = tegra->devfreq;
6234f3801   Tomeu Vizoso   PM / devfreq: teg...
439
  	struct dev_pm_opp *opp;
62bacb06b   Dmitry Osipenko   PM / devfreq: teg...
440
  	unsigned long rate;
30af44fae   Dmitry Osipenko   PM / devfreq: teg...
441
  	int err;
6234f3801   Tomeu Vizoso   PM / devfreq: teg...
442

62bacb06b   Dmitry Osipenko   PM / devfreq: teg...
443
  	opp = devfreq_recommended_opp(dev, freq, flags);
6234f3801   Tomeu Vizoso   PM / devfreq: teg...
444
  	if (IS_ERR(opp)) {
62bacb06b   Dmitry Osipenko   PM / devfreq: teg...
445
446
  		dev_err(dev, "Failed to find opp for %lu Hz
  ", *freq);
6234f3801   Tomeu Vizoso   PM / devfreq: teg...
447
448
449
  		return PTR_ERR(opp);
  	}
  	rate = dev_pm_opp_get_freq(opp);
8a31d9d94   Viresh Kumar   PM / OPP: Update ...
450
  	dev_pm_opp_put(opp);
6234f3801   Tomeu Vizoso   PM / devfreq: teg...
451

30af44fae   Dmitry Osipenko   PM / devfreq: teg...
452
453
454
455
456
457
458
  	err = clk_set_min_rate(tegra->emc_clock, rate);
  	if (err)
  		return err;
  
  	err = clk_set_rate(tegra->emc_clock, 0);
  	if (err)
  		goto restore_min_rate;
6234f3801   Tomeu Vizoso   PM / devfreq: teg...
459
460
  
  	return 0;
30af44fae   Dmitry Osipenko   PM / devfreq: teg...
461
462
463
464
465
  
  restore_min_rate:
  	clk_set_min_rate(tegra->emc_clock, devfreq->previous_freq);
  
  	return err;
6234f3801   Tomeu Vizoso   PM / devfreq: teg...
466
467
468
469
470
  }
  
  static int tegra_devfreq_get_dev_status(struct device *dev,
  					struct devfreq_dev_status *stat)
  {
11573e913   Tomeu Vizoso   PM / devfreq: teg...
471
  	struct tegra_devfreq *tegra = dev_get_drvdata(dev);
6234f3801   Tomeu Vizoso   PM / devfreq: teg...
472
  	struct tegra_devfreq_device *actmon_dev;
151531f79   Dmitry Osipenko   PM / devfreq: teg...
473
  	unsigned long cur_freq;
6234f3801   Tomeu Vizoso   PM / devfreq: teg...
474

151531f79   Dmitry Osipenko   PM / devfreq: teg...
475
  	cur_freq = READ_ONCE(tegra->cur_freq);
6234f3801   Tomeu Vizoso   PM / devfreq: teg...
476
477
478
479
480
  
  	/* To be used by the tegra governor */
  	stat->private_data = tegra;
  
  	/* The below are to be used by the other governors */
151531f79   Dmitry Osipenko   PM / devfreq: teg...
481
  	stat->current_frequency = cur_freq * KHZ;
6234f3801   Tomeu Vizoso   PM / devfreq: teg...
482
483
484
485
  
  	actmon_dev = &tegra->devices[MCALL];
  
  	/* Number of cycles spent on memory access */
11573e913   Tomeu Vizoso   PM / devfreq: teg...
486
  	stat->busy_time = device_readl(actmon_dev, ACTMON_DEV_AVG_COUNT);
6234f3801   Tomeu Vizoso   PM / devfreq: teg...
487
488
489
490
491
  
  	/* The bus can be considered to be saturated way before 100% */
  	stat->busy_time *= 100 / BUS_SATURATION_RATIO;
  
  	/* Number of cycles in a sampling period */
151531f79   Dmitry Osipenko   PM / devfreq: teg...
492
  	stat->total_time = ACTMON_SAMPLING_PERIOD * cur_freq;
6234f3801   Tomeu Vizoso   PM / devfreq: teg...
493

11573e913   Tomeu Vizoso   PM / devfreq: teg...
494
  	stat->busy_time = min(stat->busy_time, stat->total_time);
6234f3801   Tomeu Vizoso   PM / devfreq: teg...
495
496
  	return 0;
  }
11573e913   Tomeu Vizoso   PM / devfreq: teg...
497
498
499
500
501
502
503
504
  static struct devfreq_dev_profile tegra_devfreq_profile = {
  	.polling_ms	= 0,
  	.target		= tegra_devfreq_target,
  	.get_dev_status	= tegra_devfreq_get_dev_status,
  };
  
  static int tegra_governor_get_target(struct devfreq *devfreq,
  				     unsigned long *freq)
6234f3801   Tomeu Vizoso   PM / devfreq: teg...
505
  {
14de39031   MyungJoo Ham   PM / devfreq: teg...
506
  	struct devfreq_dev_status *stat;
6234f3801   Tomeu Vizoso   PM / devfreq: teg...
507
508
509
510
511
  	struct tegra_devfreq *tegra;
  	struct tegra_devfreq_device *dev;
  	unsigned long target_freq = 0;
  	unsigned int i;
  	int err;
14de39031   MyungJoo Ham   PM / devfreq: teg...
512
  	err = devfreq_update_stats(devfreq);
6234f3801   Tomeu Vizoso   PM / devfreq: teg...
513
514
  	if (err)
  		return err;
14de39031   MyungJoo Ham   PM / devfreq: teg...
515
516
517
  	stat = &devfreq->last_status;
  
  	tegra = stat->private_data;
6234f3801   Tomeu Vizoso   PM / devfreq: teg...
518
519
520
521
522
523
524
525
  
  	for (i = 0; i < ARRAY_SIZE(tegra->devices); i++) {
  		dev = &tegra->devices[i];
  
  		actmon_update_target(tegra, dev);
  
  		target_freq = max(target_freq, dev->target_freq);
  	}
62bacb06b   Dmitry Osipenko   PM / devfreq: teg...
526
  	*freq = target_freq * KHZ;
6234f3801   Tomeu Vizoso   PM / devfreq: teg...
527
528
529
  
  	return 0;
  }
11573e913   Tomeu Vizoso   PM / devfreq: teg...
530
531
  static int tegra_governor_event_handler(struct devfreq *devfreq,
  					unsigned int event, void *data)
6234f3801   Tomeu Vizoso   PM / devfreq: teg...
532
  {
1d1397c3e   Yangtao Li   PM / devfreq: teg...
533
  	struct tegra_devfreq *tegra = dev_get_drvdata(devfreq->dev.parent);
11573e913   Tomeu Vizoso   PM / devfreq: teg...
534
535
536
  
  	switch (event) {
  	case DEVFREQ_GOV_START:
11573e913   Tomeu Vizoso   PM / devfreq: teg...
537
  		devfreq_monitor_start(devfreq);
546ff0938   Dmitry Osipenko   PM / devfreq: teg...
538
  		tegra_actmon_start(tegra);
11573e913   Tomeu Vizoso   PM / devfreq: teg...
539
540
541
  		break;
  
  	case DEVFREQ_GOV_STOP:
546ff0938   Dmitry Osipenko   PM / devfreq: teg...
542
  		tegra_actmon_stop(tegra);
11573e913   Tomeu Vizoso   PM / devfreq: teg...
543
544
545
546
  		devfreq_monitor_stop(devfreq);
  		break;
  
  	case DEVFREQ_GOV_SUSPEND:
546ff0938   Dmitry Osipenko   PM / devfreq: teg...
547
  		tegra_actmon_stop(tegra);
11573e913   Tomeu Vizoso   PM / devfreq: teg...
548
549
550
551
  		devfreq_monitor_suspend(devfreq);
  		break;
  
  	case DEVFREQ_GOV_RESUME:
11573e913   Tomeu Vizoso   PM / devfreq: teg...
552
  		devfreq_monitor_resume(devfreq);
546ff0938   Dmitry Osipenko   PM / devfreq: teg...
553
  		tegra_actmon_start(tegra);
11573e913   Tomeu Vizoso   PM / devfreq: teg...
554
555
  		break;
  	}
1d1397c3e   Yangtao Li   PM / devfreq: teg...
556
  	return 0;
6234f3801   Tomeu Vizoso   PM / devfreq: teg...
557
558
559
  }
  
  static struct devfreq_governor tegra_devfreq_governor = {
11573e913   Tomeu Vizoso   PM / devfreq: teg...
560
561
562
  	.name = "tegra_actmon",
  	.get_target_freq = tegra_governor_get_target,
  	.event_handler = tegra_governor_event_handler,
386789ebb   Dmitry Osipenko   PM / devfreq: teg...
563
  	.immutable = true,
6234f3801   Tomeu Vizoso   PM / devfreq: teg...
564
  };
6234f3801   Tomeu Vizoso   PM / devfreq: teg...
565
566
567
568
  static int tegra_devfreq_probe(struct platform_device *pdev)
  {
  	struct tegra_devfreq *tegra;
  	struct tegra_devfreq_device *dev;
6234f3801   Tomeu Vizoso   PM / devfreq: teg...
569
  	unsigned int i;
5d498b463   Tomeu Vizoso   PM / devfreq: teg...
570
  	unsigned long rate;
6234f3801   Tomeu Vizoso   PM / devfreq: teg...
571
572
573
574
575
  	int err;
  
  	tegra = devm_kzalloc(&pdev->dev, sizeof(*tegra), GFP_KERNEL);
  	if (!tegra)
  		return -ENOMEM;
8fda5c1fa   Dmitry Osipenko   PM / devfreq: teg...
576
  	tegra->regs = devm_platform_ioremap_resource(pdev, 0);
11573e913   Tomeu Vizoso   PM / devfreq: teg...
577
  	if (IS_ERR(tegra->regs))
6234f3801   Tomeu Vizoso   PM / devfreq: teg...
578
  		return PTR_ERR(tegra->regs);
6234f3801   Tomeu Vizoso   PM / devfreq: teg...
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
  
  	tegra->reset = devm_reset_control_get(&pdev->dev, "actmon");
  	if (IS_ERR(tegra->reset)) {
  		dev_err(&pdev->dev, "Failed to get reset
  ");
  		return PTR_ERR(tegra->reset);
  	}
  
  	tegra->clock = devm_clk_get(&pdev->dev, "actmon");
  	if (IS_ERR(tegra->clock)) {
  		dev_err(&pdev->dev, "Failed to get actmon clock
  ");
  		return PTR_ERR(tegra->clock);
  	}
  
  	tegra->emc_clock = devm_clk_get(&pdev->dev, "emc");
  	if (IS_ERR(tegra->emc_clock)) {
  		dev_err(&pdev->dev, "Failed to get emc clock
  ");
  		return PTR_ERR(tegra->emc_clock);
  	}
8fda5c1fa   Dmitry Osipenko   PM / devfreq: teg...
600
601
602
603
604
  	tegra->irq = platform_get_irq(pdev, 0);
  	if (tegra->irq < 0) {
  		err = tegra->irq;
  		dev_err(&pdev->dev, "Failed to get IRQ: %d
  ", err);
6234f3801   Tomeu Vizoso   PM / devfreq: teg...
605
606
607
608
609
610
611
  		return err;
  	}
  
  	reset_control_assert(tegra->reset);
  
  	err = clk_prepare_enable(tegra->clock);
  	if (err) {
11573e913   Tomeu Vizoso   PM / devfreq: teg...
612
613
614
  		dev_err(&pdev->dev,
  			"Failed to prepare and enable ACTMON clock
  ");
6234f3801   Tomeu Vizoso   PM / devfreq: teg...
615
616
617
618
  		return err;
  	}
  
  	reset_control_deassert(tegra->reset);
c70eea739   Tomeu Vizoso   PM / devfreq: teg...
619
  	tegra->max_freq = clk_round_rate(tegra->emc_clock, ULONG_MAX) / KHZ;
6234f3801   Tomeu Vizoso   PM / devfreq: teg...
620
  	tegra->cur_freq = clk_get_rate(tegra->emc_clock) / KHZ;
6234f3801   Tomeu Vizoso   PM / devfreq: teg...
621
622
623
624
  	for (i = 0; i < ARRAY_SIZE(actmon_device_configs); i++) {
  		dev = tegra->devices + i;
  		dev->config = actmon_device_configs + i;
  		dev->regs = tegra->regs + dev->config->offset;
6234f3801   Tomeu Vizoso   PM / devfreq: teg...
625
  	}
5d498b463   Tomeu Vizoso   PM / devfreq: teg...
626
627
  	for (rate = 0; rate <= tegra->max_freq * KHZ; rate++) {
  		rate = clk_round_rate(tegra->emc_clock, rate);
5d498b463   Tomeu Vizoso   PM / devfreq: teg...
628

8fda5c1fa   Dmitry Osipenko   PM / devfreq: teg...
629
630
631
632
633
634
  		err = dev_pm_opp_add(&pdev->dev, rate, 0);
  		if (err) {
  			dev_err(&pdev->dev, "Failed to add OPP: %d
  ", err);
  			goto remove_opps;
  		}
6234f3801   Tomeu Vizoso   PM / devfreq: teg...
635
  	}
2da19b1a4   Tomeu Vizoso   PM / devfreq: teg...
636
  	platform_set_drvdata(pdev, tegra);
8fda5c1fa   Dmitry Osipenko   PM / devfreq: teg...
637
638
639
640
641
642
643
644
  	tegra->rate_change_nb.notifier_call = tegra_actmon_rate_notify_cb;
  	err = clk_notifier_register(tegra->emc_clock, &tegra->rate_change_nb);
  	if (err) {
  		dev_err(&pdev->dev,
  			"Failed to register rate change notifier
  ");
  		goto remove_opps;
  	}
5a7e10c89   Dmitry Osipenko   PM / devfreq: teg...
645
646
647
648
649
650
  	err = devfreq_add_governor(&tegra_devfreq_governor);
  	if (err) {
  		dev_err(&pdev->dev, "Failed to add governor: %d
  ", err);
  		goto unreg_notifier;
  	}
8fda5c1fa   Dmitry Osipenko   PM / devfreq: teg...
651
652
653
654
655
656
657
  	tegra_devfreq_profile.initial_freq = clk_get_rate(tegra->emc_clock);
  	tegra->devfreq = devfreq_add_device(&pdev->dev,
  					    &tegra_devfreq_profile,
  					    "tegra_actmon",
  					    NULL);
  	if (IS_ERR(tegra->devfreq)) {
  		err = PTR_ERR(tegra->devfreq);
5a7e10c89   Dmitry Osipenko   PM / devfreq: teg...
658
  		goto remove_governor;
8fda5c1fa   Dmitry Osipenko   PM / devfreq: teg...
659
  	}
7514dd05e   Dmitry Osipenko   PM / devfreq: teg...
660
  	err = devm_request_threaded_irq(&pdev->dev, tegra->irq, NULL,
dd3f2616b   Dmitry Osipenko   PM / devfreq: teg...
661
  					actmon_thread_isr, IRQF_ONESHOT,
6234f3801   Tomeu Vizoso   PM / devfreq: teg...
662
663
  					"tegra-devfreq", tegra);
  	if (err) {
8fda5c1fa   Dmitry Osipenko   PM / devfreq: teg...
664
665
666
  		dev_err(&pdev->dev, "Interrupt request failed: %d
  ", err);
  		goto remove_devfreq;
6234f3801   Tomeu Vizoso   PM / devfreq: teg...
667
  	}
6234f3801   Tomeu Vizoso   PM / devfreq: teg...
668
  	return 0;
8fda5c1fa   Dmitry Osipenko   PM / devfreq: teg...
669
670
671
  
  remove_devfreq:
  	devfreq_remove_device(tegra->devfreq);
5a7e10c89   Dmitry Osipenko   PM / devfreq: teg...
672
673
  remove_governor:
  	devfreq_remove_governor(&tegra_devfreq_governor);
8fda5c1fa   Dmitry Osipenko   PM / devfreq: teg...
674
675
676
677
678
679
680
681
682
683
  unreg_notifier:
  	clk_notifier_unregister(tegra->emc_clock, &tegra->rate_change_nb);
  
  remove_opps:
  	dev_pm_opp_remove_all_dynamic(&pdev->dev);
  
  	reset_control_reset(tegra->reset);
  	clk_disable_unprepare(tegra->clock);
  
  	return err;
6234f3801   Tomeu Vizoso   PM / devfreq: teg...
684
685
686
687
688
  }
  
  static int tegra_devfreq_remove(struct platform_device *pdev)
  {
  	struct tegra_devfreq *tegra = platform_get_drvdata(pdev);
11573e913   Tomeu Vizoso   PM / devfreq: teg...
689

8fda5c1fa   Dmitry Osipenko   PM / devfreq: teg...
690
  	devfreq_remove_device(tegra->devfreq);
5a7e10c89   Dmitry Osipenko   PM / devfreq: teg...
691
  	devfreq_remove_governor(&tegra_devfreq_governor);
6234f3801   Tomeu Vizoso   PM / devfreq: teg...
692
693
  
  	clk_notifier_unregister(tegra->emc_clock, &tegra->rate_change_nb);
8fda5c1fa   Dmitry Osipenko   PM / devfreq: teg...
694
  	dev_pm_opp_remove_all_dynamic(&pdev->dev);
6234f3801   Tomeu Vizoso   PM / devfreq: teg...
695

8fda5c1fa   Dmitry Osipenko   PM / devfreq: teg...
696
  	reset_control_reset(tegra->reset);
6234f3801   Tomeu Vizoso   PM / devfreq: teg...
697
698
699
700
  	clk_disable_unprepare(tegra->clock);
  
  	return 0;
  }
11573e913   Tomeu Vizoso   PM / devfreq: teg...
701
  static const struct of_device_id tegra_devfreq_of_match[] = {
1ac347488   Dmitry Osipenko   PM / devfreq: teg...
702
  	{ .compatible = "nvidia,tegra30-actmon" },
6234f3801   Tomeu Vizoso   PM / devfreq: teg...
703
704
705
  	{ .compatible = "nvidia,tegra124-actmon" },
  	{ },
  };
11573e913   Tomeu Vizoso   PM / devfreq: teg...
706
  MODULE_DEVICE_TABLE(of, tegra_devfreq_of_match);
6234f3801   Tomeu Vizoso   PM / devfreq: teg...
707
708
709
710
  static struct platform_driver tegra_devfreq_driver = {
  	.probe	= tegra_devfreq_probe,
  	.remove	= tegra_devfreq_remove,
  	.driver = {
11573e913   Tomeu Vizoso   PM / devfreq: teg...
711
  		.name = "tegra-devfreq",
6234f3801   Tomeu Vizoso   PM / devfreq: teg...
712
  		.of_match_table = tegra_devfreq_of_match,
6234f3801   Tomeu Vizoso   PM / devfreq: teg...
713
714
  	},
  };
5a7e10c89   Dmitry Osipenko   PM / devfreq: teg...
715
  module_platform_driver(tegra_devfreq_driver);
6234f3801   Tomeu Vizoso   PM / devfreq: teg...
716

11573e913   Tomeu Vizoso   PM / devfreq: teg...
717
  MODULE_LICENSE("GPL v2");
6234f3801   Tomeu Vizoso   PM / devfreq: teg...
718
719
  MODULE_DESCRIPTION("Tegra devfreq driver");
  MODULE_AUTHOR("Tomeu Vizoso <tomeu.vizoso@collabora.com>");