Blame view

drivers/cpufreq/exynos5440-cpufreq.c 12.1 KB
49d7b5bfb   Amit Daniel Kachhap   cpufreq: exynos: ...
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
  /*
   * Copyright (c) 2013 Samsung Electronics Co., Ltd.
   *		http://www.samsung.com
   *
   * Amit Daniel Kachhap <amit.daniel@samsung.com>
   *
   * EXYNOS5440 - CPU frequency scaling support
   *
   * 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/clk.h>
  #include <linux/cpu.h>
  #include <linux/cpufreq.h>
  #include <linux/err.h>
  #include <linux/interrupt.h>
  #include <linux/io.h>
  #include <linux/module.h>
  #include <linux/of_address.h>
  #include <linux/of_irq.h>
e4db1c743   Nishanth Menon   PM / OPP: rename ...
23
  #include <linux/pm_opp.h>
49d7b5bfb   Amit Daniel Kachhap   cpufreq: exynos: ...
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
  #include <linux/platform_device.h>
  #include <linux/slab.h>
  
  /* Register definitions */
  #define XMU_DVFS_CTRL		0x0060
  #define XMU_PMU_P0_7		0x0064
  #define XMU_C0_3_PSTATE		0x0090
  #define XMU_P_LIMIT		0x00a0
  #define XMU_P_STATUS		0x00a4
  #define XMU_PMUEVTEN		0x00d0
  #define XMU_PMUIRQEN		0x00d4
  #define XMU_PMUIRQ		0x00d8
  
  /* PMU mask and shift definations */
  #define P_VALUE_MASK		0x7
  
  #define XMU_DVFS_CTRL_EN_SHIFT	0
  
  #define P0_7_CPUCLKDEV_SHIFT	21
  #define P0_7_CPUCLKDEV_MASK	0x7
  #define P0_7_ATBCLKDEV_SHIFT	18
  #define P0_7_ATBCLKDEV_MASK	0x7
  #define P0_7_CSCLKDEV_SHIFT	15
  #define P0_7_CSCLKDEV_MASK	0x7
  #define P0_7_CPUEMA_SHIFT	28
  #define P0_7_CPUEMA_MASK	0xf
  #define P0_7_L2EMA_SHIFT	24
  #define P0_7_L2EMA_MASK		0xf
  #define P0_7_VDD_SHIFT		8
  #define P0_7_VDD_MASK		0x7f
  #define P0_7_FREQ_SHIFT		0
  #define P0_7_FREQ_MASK		0xff
  
  #define C0_3_PSTATE_VALID_SHIFT	8
  #define C0_3_PSTATE_CURR_SHIFT	4
  #define C0_3_PSTATE_NEW_SHIFT	0
  
  #define PSTATE_CHANGED_EVTEN_SHIFT	0
  
  #define PSTATE_CHANGED_IRQEN_SHIFT	0
  
  #define PSTATE_CHANGED_SHIFT		0
  
  /* some constant values for clock divider calculation */
  #define CPU_DIV_FREQ_MAX	500
  #define CPU_DBG_FREQ_MAX	375
  #define CPU_ATB_FREQ_MAX	500
  
  #define PMIC_LOW_VOLT		0x30
  #define PMIC_HIGH_VOLT		0x28
  
  #define CPUEMA_HIGH		0x2
  #define CPUEMA_MID		0x4
  #define CPUEMA_LOW		0x7
  
  #define L2EMA_HIGH		0x1
  #define L2EMA_MID		0x3
  #define L2EMA_LOW		0x4
  
  #define DIV_TAB_MAX	2
  /* frequency unit is 20MHZ */
  #define FREQ_UNIT	20
  #define MAX_VOLTAGE	1550000 /* In microvolt */
  #define VOLTAGE_STEP	12500	/* In microvolt */
  
  #define CPUFREQ_NAME		"exynos5440_dvfs"
  #define DEF_TRANS_LATENCY	100000
  
  enum cpufreq_level_index {
  	L0, L1, L2, L3, L4,
  	L5, L6, L7, L8, L9,
  };
  #define CPUFREQ_LEVEL_END	(L7 + 1)
  
  struct exynos_dvfs_data {
  	void __iomem *base;
  	struct resource *mem;
  	int irq;
  	struct clk *cpu_clk;
49d7b5bfb   Amit Daniel Kachhap   cpufreq: exynos: ...
103
104
105
106
107
108
109
110
111
112
113
114
115
116
  	unsigned int latency;
  	struct cpufreq_frequency_table *freq_table;
  	unsigned int freq_count;
  	struct device *dev;
  	bool dvfs_enabled;
  	struct work_struct irq_work;
  };
  
  static struct exynos_dvfs_data *dvfs_info;
  static DEFINE_MUTEX(cpufreq_lock);
  static struct cpufreq_freqs freqs;
  
  static int init_div_table(void)
  {
041526f91   Stratos Karafotis   cpufreq: Use cpuf...
117
  	struct cpufreq_frequency_table *pos, *freq_tbl = dvfs_info->freq_table;
49d7b5bfb   Amit Daniel Kachhap   cpufreq: exynos: ...
118
  	unsigned int tmp, clk_div, ema_div, freq, volt_id;
47d43ba73   Nishanth Menon   PM / OPP: rename ...
119
  	struct dev_pm_opp *opp;
49d7b5bfb   Amit Daniel Kachhap   cpufreq: exynos: ...
120

70eb0855b   Amit Daniel Kachhap   cpufreq: exynos54...
121
  	rcu_read_lock();
041526f91   Stratos Karafotis   cpufreq: Use cpuf...
122
  	cpufreq_for_each_entry(pos, freq_tbl) {
5d4879cda   Nishanth Menon   PM / OPP: rename ...
123
  		opp = dev_pm_opp_find_freq_exact(dvfs_info->dev,
041526f91   Stratos Karafotis   cpufreq: Use cpuf...
124
  					pos->frequency * 1000, true);
49d7b5bfb   Amit Daniel Kachhap   cpufreq: exynos: ...
125
  		if (IS_ERR(opp)) {
70eb0855b   Amit Daniel Kachhap   cpufreq: exynos54...
126
  			rcu_read_unlock();
49d7b5bfb   Amit Daniel Kachhap   cpufreq: exynos: ...
127
128
129
  			dev_err(dvfs_info->dev,
  				"failed to find valid OPP for %u KHZ
  ",
041526f91   Stratos Karafotis   cpufreq: Use cpuf...
130
  				pos->frequency);
49d7b5bfb   Amit Daniel Kachhap   cpufreq: exynos: ...
131
132
  			return PTR_ERR(opp);
  		}
041526f91   Stratos Karafotis   cpufreq: Use cpuf...
133
  		freq = pos->frequency / 1000; /* In MHZ */
49d7b5bfb   Amit Daniel Kachhap   cpufreq: exynos: ...
134
135
136
137
138
139
140
141
  		clk_div = ((freq / CPU_DIV_FREQ_MAX) & P0_7_CPUCLKDEV_MASK)
  					<< P0_7_CPUCLKDEV_SHIFT;
  		clk_div |= ((freq / CPU_ATB_FREQ_MAX) & P0_7_ATBCLKDEV_MASK)
  					<< P0_7_ATBCLKDEV_SHIFT;
  		clk_div |= ((freq / CPU_DBG_FREQ_MAX) & P0_7_CSCLKDEV_MASK)
  					<< P0_7_CSCLKDEV_SHIFT;
  
  		/* Calculate EMA */
5d4879cda   Nishanth Menon   PM / OPP: rename ...
142
  		volt_id = dev_pm_opp_get_voltage(opp);
49d7b5bfb   Amit Daniel Kachhap   cpufreq: exynos: ...
143
144
145
146
147
148
149
150
151
152
153
154
155
156
  		volt_id = (MAX_VOLTAGE - volt_id) / VOLTAGE_STEP;
  		if (volt_id < PMIC_HIGH_VOLT) {
  			ema_div = (CPUEMA_HIGH << P0_7_CPUEMA_SHIFT) |
  				(L2EMA_HIGH << P0_7_L2EMA_SHIFT);
  		} else if (volt_id > PMIC_LOW_VOLT) {
  			ema_div = (CPUEMA_LOW << P0_7_CPUEMA_SHIFT) |
  				(L2EMA_LOW << P0_7_L2EMA_SHIFT);
  		} else {
  			ema_div = (CPUEMA_MID << P0_7_CPUEMA_SHIFT) |
  				(L2EMA_MID << P0_7_L2EMA_SHIFT);
  		}
  
  		tmp = (clk_div | ema_div | (volt_id << P0_7_VDD_SHIFT)
  			| ((freq / FREQ_UNIT) << P0_7_FREQ_SHIFT));
041526f91   Stratos Karafotis   cpufreq: Use cpuf...
157
158
  		__raw_writel(tmp, dvfs_info->base + XMU_PMU_P0_7 + 4 *
  						(pos - freq_tbl));
49d7b5bfb   Amit Daniel Kachhap   cpufreq: exynos: ...
159
  	}
70eb0855b   Amit Daniel Kachhap   cpufreq: exynos54...
160
  	rcu_read_unlock();
49d7b5bfb   Amit Daniel Kachhap   cpufreq: exynos: ...
161
162
  	return 0;
  }
652ed95d5   Viresh Kumar   cpufreq: introduc...
163
  static void exynos_enable_dvfs(unsigned int cur_frequency)
49d7b5bfb   Amit Daniel Kachhap   cpufreq: exynos: ...
164
  {
041526f91   Stratos Karafotis   cpufreq: Use cpuf...
165
  	unsigned int tmp, cpu;
49d7b5bfb   Amit Daniel Kachhap   cpufreq: exynos: ...
166
  	struct cpufreq_frequency_table *freq_table = dvfs_info->freq_table;
041526f91   Stratos Karafotis   cpufreq: Use cpuf...
167
  	struct cpufreq_frequency_table *pos;
49d7b5bfb   Amit Daniel Kachhap   cpufreq: exynos: ...
168
169
170
171
172
173
174
175
176
177
178
179
180
181
  	/* Disable DVFS */
  	__raw_writel(0,	dvfs_info->base + XMU_DVFS_CTRL);
  
  	/* Enable PSTATE Change Event */
  	tmp = __raw_readl(dvfs_info->base + XMU_PMUEVTEN);
  	tmp |= (1 << PSTATE_CHANGED_EVTEN_SHIFT);
  	 __raw_writel(tmp, dvfs_info->base + XMU_PMUEVTEN);
  
  	/* Enable PSTATE Change IRQ */
  	tmp = __raw_readl(dvfs_info->base + XMU_PMUIRQEN);
  	tmp |= (1 << PSTATE_CHANGED_IRQEN_SHIFT);
  	 __raw_writel(tmp, dvfs_info->base + XMU_PMUIRQEN);
  
  	/* Set initial performance index */
041526f91   Stratos Karafotis   cpufreq: Use cpuf...
182
183
  	cpufreq_for_each_entry(pos, freq_table)
  		if (pos->frequency == cur_frequency)
49d7b5bfb   Amit Daniel Kachhap   cpufreq: exynos: ...
184
  			break;
041526f91   Stratos Karafotis   cpufreq: Use cpuf...
185
  	if (pos->frequency == CPUFREQ_TABLE_END) {
49d7b5bfb   Amit Daniel Kachhap   cpufreq: exynos: ...
186
187
188
  		dev_crit(dvfs_info->dev, "Boot up frequency not supported
  ");
  		/* Assign the highest frequency */
041526f91   Stratos Karafotis   cpufreq: Use cpuf...
189
190
  		pos = freq_table;
  		cur_frequency = pos->frequency;
49d7b5bfb   Amit Daniel Kachhap   cpufreq: exynos: ...
191
192
193
  	}
  
  	dev_info(dvfs_info->dev, "Setting dvfs initial frequency = %uKHZ",
652ed95d5   Viresh Kumar   cpufreq: introduc...
194
  						cur_frequency);
49d7b5bfb   Amit Daniel Kachhap   cpufreq: exynos: ...
195
196
197
198
  
  	for (cpu = 0; cpu < CONFIG_NR_CPUS; cpu++) {
  		tmp = __raw_readl(dvfs_info->base + XMU_C0_3_PSTATE + cpu * 4);
  		tmp &= ~(P_VALUE_MASK << C0_3_PSTATE_NEW_SHIFT);
041526f91   Stratos Karafotis   cpufreq: Use cpuf...
199
  		tmp |= ((pos - freq_table) << C0_3_PSTATE_NEW_SHIFT);
49d7b5bfb   Amit Daniel Kachhap   cpufreq: exynos: ...
200
201
202
203
204
205
206
  		__raw_writel(tmp, dvfs_info->base + XMU_C0_3_PSTATE + cpu * 4);
  	}
  
  	/* Enable DVFS */
  	__raw_writel(1 << XMU_DVFS_CTRL_EN_SHIFT,
  				dvfs_info->base + XMU_DVFS_CTRL);
  }
9c0ebcf78   Viresh Kumar   cpufreq: Implemen...
207
  static int exynos_target(struct cpufreq_policy *policy, unsigned int index)
49d7b5bfb   Amit Daniel Kachhap   cpufreq: exynos: ...
208
  {
9c0ebcf78   Viresh Kumar   cpufreq: Implemen...
209
210
  	unsigned int tmp;
  	int i;
49d7b5bfb   Amit Daniel Kachhap   cpufreq: exynos: ...
211
212
213
  	struct cpufreq_frequency_table *freq_table = dvfs_info->freq_table;
  
  	mutex_lock(&cpufreq_lock);
652ed95d5   Viresh Kumar   cpufreq: introduc...
214
  	freqs.old = policy->cur;
49d7b5bfb   Amit Daniel Kachhap   cpufreq: exynos: ...
215
  	freqs.new = freq_table[index].frequency;
8fec051ee   Viresh Kumar   cpufreq: Convert ...
216
  	cpufreq_freq_transition_begin(policy, &freqs);
49d7b5bfb   Amit Daniel Kachhap   cpufreq: exynos: ...
217
218
219
220
221
222
223
224
225
  
  	/* Set the target frequency in all C0_3_PSTATE register */
  	for_each_cpu(i, policy->cpus) {
  		tmp = __raw_readl(dvfs_info->base + XMU_C0_3_PSTATE + i * 4);
  		tmp &= ~(P_VALUE_MASK << C0_3_PSTATE_NEW_SHIFT);
  		tmp |= (index << C0_3_PSTATE_NEW_SHIFT);
  
  		__raw_writel(tmp, dvfs_info->base + XMU_C0_3_PSTATE + i * 4);
  	}
49d7b5bfb   Amit Daniel Kachhap   cpufreq: exynos: ...
226
  	mutex_unlock(&cpufreq_lock);
9c0ebcf78   Viresh Kumar   cpufreq: Implemen...
227
  	return 0;
49d7b5bfb   Amit Daniel Kachhap   cpufreq: exynos: ...
228
229
230
231
232
233
234
235
236
237
238
239
240
  }
  
  static void exynos_cpufreq_work(struct work_struct *work)
  {
  	unsigned int cur_pstate, index;
  	struct cpufreq_policy *policy = cpufreq_cpu_get(0); /* boot CPU */
  	struct cpufreq_frequency_table *freq_table = dvfs_info->freq_table;
  
  	/* Ensure we can access cpufreq structures */
  	if (unlikely(dvfs_info->dvfs_enabled == false))
  		goto skip_work;
  
  	mutex_lock(&cpufreq_lock);
652ed95d5   Viresh Kumar   cpufreq: introduc...
241
  	freqs.old = policy->cur;
49d7b5bfb   Amit Daniel Kachhap   cpufreq: exynos: ...
242
243
244
245
246
247
248
249
250
  
  	cur_pstate = __raw_readl(dvfs_info->base + XMU_P_STATUS);
  	if (cur_pstate >> C0_3_PSTATE_VALID_SHIFT & 0x1)
  		index = (cur_pstate >> C0_3_PSTATE_CURR_SHIFT) & P_VALUE_MASK;
  	else
  		index = (cur_pstate >> C0_3_PSTATE_NEW_SHIFT) & P_VALUE_MASK;
  
  	if (likely(index < dvfs_info->freq_count)) {
  		freqs.new = freq_table[index].frequency;
49d7b5bfb   Amit Daniel Kachhap   cpufreq: exynos: ...
251
252
253
  	} else {
  		dev_crit(dvfs_info->dev, "New frequency out of range
  ");
652ed95d5   Viresh Kumar   cpufreq: introduc...
254
  		freqs.new = freqs.old;
49d7b5bfb   Amit Daniel Kachhap   cpufreq: exynos: ...
255
  	}
8fec051ee   Viresh Kumar   cpufreq: Convert ...
256
  	cpufreq_freq_transition_end(policy, &freqs, 0);
49d7b5bfb   Amit Daniel Kachhap   cpufreq: exynos: ...
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
  
  	cpufreq_cpu_put(policy);
  	mutex_unlock(&cpufreq_lock);
  skip_work:
  	enable_irq(dvfs_info->irq);
  }
  
  static irqreturn_t exynos_cpufreq_irq(int irq, void *id)
  {
  	unsigned int tmp;
  
  	tmp = __raw_readl(dvfs_info->base + XMU_PMUIRQ);
  	if (tmp >> PSTATE_CHANGED_SHIFT & 0x1) {
  		__raw_writel(tmp, dvfs_info->base + XMU_PMUIRQ);
  		disable_irq_nosync(irq);
  		schedule_work(&dvfs_info->irq_work);
  	}
  	return IRQ_HANDLED;
  }
  
  static void exynos_sort_descend_freq_table(void)
  {
  	struct cpufreq_frequency_table *freq_tbl = dvfs_info->freq_table;
  	int i = 0, index;
  	unsigned int tmp_freq;
  	/*
  	 * Exynos5440 clock controller state logic expects the cpufreq table to
  	 * be in descending order. But the OPP library constructs the table in
  	 * ascending order. So to make the table descending we just need to
  	 * swap the i element with the N - i element.
  	 */
  	for (i = 0; i < dvfs_info->freq_count / 2; i++) {
  		index = dvfs_info->freq_count - i - 1;
  		tmp_freq = freq_tbl[i].frequency;
  		freq_tbl[i].frequency = freq_tbl[index].frequency;
  		freq_tbl[index].frequency = tmp_freq;
  	}
  }
  
  static int exynos_cpufreq_cpu_init(struct cpufreq_policy *policy)
  {
652ed95d5   Viresh Kumar   cpufreq: introduc...
298
  	policy->clk = dvfs_info->cpu_clk;
b249abaeb   Viresh Kumar   cpufreq: exynos: ...
299
300
  	return cpufreq_generic_init(policy, dvfs_info->freq_table,
  			dvfs_info->latency);
49d7b5bfb   Amit Daniel Kachhap   cpufreq: exynos: ...
301
302
303
  }
  
  static struct cpufreq_driver exynos_driver = {
ae6b42713   Viresh Kumar   cpufreq: Mark ARM...
304
305
  	.flags		= CPUFREQ_STICKY | CPUFREQ_ASYNC_NOTIFICATION |
  				CPUFREQ_NEED_INITIAL_FREQ_CHECK,
eea6181ed   Viresh Kumar   cpufreq: exynos: ...
306
  	.verify		= cpufreq_generic_frequency_table_verify,
9c0ebcf78   Viresh Kumar   cpufreq: Implemen...
307
  	.target_index	= exynos_target,
652ed95d5   Viresh Kumar   cpufreq: introduc...
308
  	.get		= cpufreq_generic_get,
49d7b5bfb   Amit Daniel Kachhap   cpufreq: exynos: ...
309
310
  	.init		= exynos_cpufreq_cpu_init,
  	.name		= CPUFREQ_NAME,
5d7e690d7   Jungseok Lee   cpufreq: exynos: ...
311
  	.attr		= cpufreq_generic_attr,
49d7b5bfb   Amit Daniel Kachhap   cpufreq: exynos: ...
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
  };
  
  static const struct of_device_id exynos_cpufreq_match[] = {
  	{
  		.compatible = "samsung,exynos5440-cpufreq",
  	},
  	{},
  };
  MODULE_DEVICE_TABLE(of, exynos_cpufreq_match);
  
  static int exynos_cpufreq_probe(struct platform_device *pdev)
  {
  	int ret = -EINVAL;
  	struct device_node *np;
  	struct resource res;
652ed95d5   Viresh Kumar   cpufreq: introduc...
327
  	unsigned int cur_frequency;
49d7b5bfb   Amit Daniel Kachhap   cpufreq: exynos: ...
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
359
360
361
362
363
364
  
  	np =  pdev->dev.of_node;
  	if (!np)
  		return -ENODEV;
  
  	dvfs_info = devm_kzalloc(&pdev->dev, sizeof(*dvfs_info), GFP_KERNEL);
  	if (!dvfs_info) {
  		ret = -ENOMEM;
  		goto err_put_node;
  	}
  
  	dvfs_info->dev = &pdev->dev;
  
  	ret = of_address_to_resource(np, 0, &res);
  	if (ret)
  		goto err_put_node;
  
  	dvfs_info->base = devm_ioremap_resource(dvfs_info->dev, &res);
  	if (IS_ERR(dvfs_info->base)) {
  		ret = PTR_ERR(dvfs_info->base);
  		goto err_put_node;
  	}
  
  	dvfs_info->irq = irq_of_parse_and_map(np, 0);
  	if (!dvfs_info->irq) {
  		dev_err(dvfs_info->dev, "No cpufreq irq found
  ");
  		ret = -ENODEV;
  		goto err_put_node;
  	}
  
  	ret = of_init_opp_table(dvfs_info->dev);
  	if (ret) {
  		dev_err(dvfs_info->dev, "failed to init OPP table: %d
  ", ret);
  		goto err_put_node;
  	}
5d4879cda   Nishanth Menon   PM / OPP: rename ...
365
366
  	ret = dev_pm_opp_init_cpufreq_table(dvfs_info->dev,
  					    &dvfs_info->freq_table);
49d7b5bfb   Amit Daniel Kachhap   cpufreq: exynos: ...
367
368
369
370
  	if (ret) {
  		dev_err(dvfs_info->dev,
  			"failed to init cpufreq table: %d
  ", ret);
c6104fdbb   Viresh Kumar   exynos5440: free ...
371
  		goto err_free_opp;
49d7b5bfb   Amit Daniel Kachhap   cpufreq: exynos: ...
372
  	}
5d4879cda   Nishanth Menon   PM / OPP: rename ...
373
  	dvfs_info->freq_count = dev_pm_opp_get_opp_count(dvfs_info->dev);
49d7b5bfb   Amit Daniel Kachhap   cpufreq: exynos: ...
374
375
376
377
378
379
380
381
382
383
384
385
  	exynos_sort_descend_freq_table();
  
  	if (of_property_read_u32(np, "clock-latency", &dvfs_info->latency))
  		dvfs_info->latency = DEF_TRANS_LATENCY;
  
  	dvfs_info->cpu_clk = devm_clk_get(dvfs_info->dev, "armclk");
  	if (IS_ERR(dvfs_info->cpu_clk)) {
  		dev_err(dvfs_info->dev, "Failed to get cpu clock
  ");
  		ret = PTR_ERR(dvfs_info->cpu_clk);
  		goto err_free_table;
  	}
652ed95d5   Viresh Kumar   cpufreq: introduc...
386
387
  	cur_frequency = clk_get_rate(dvfs_info->cpu_clk);
  	if (!cur_frequency) {
49d7b5bfb   Amit Daniel Kachhap   cpufreq: exynos: ...
388
389
390
391
392
  		dev_err(dvfs_info->dev, "Failed to get clock rate
  ");
  		ret = -EINVAL;
  		goto err_free_table;
  	}
652ed95d5   Viresh Kumar   cpufreq: introduc...
393
  	cur_frequency /= 1000;
49d7b5bfb   Amit Daniel Kachhap   cpufreq: exynos: ...
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
  
  	INIT_WORK(&dvfs_info->irq_work, exynos_cpufreq_work);
  	ret = devm_request_irq(dvfs_info->dev, dvfs_info->irq,
  				exynos_cpufreq_irq, IRQF_TRIGGER_NONE,
  				CPUFREQ_NAME, dvfs_info);
  	if (ret) {
  		dev_err(dvfs_info->dev, "Failed to register IRQ
  ");
  		goto err_free_table;
  	}
  
  	ret = init_div_table();
  	if (ret) {
  		dev_err(dvfs_info->dev, "Failed to initialise div table
  ");
  		goto err_free_table;
  	}
652ed95d5   Viresh Kumar   cpufreq: introduc...
411
  	exynos_enable_dvfs(cur_frequency);
49d7b5bfb   Amit Daniel Kachhap   cpufreq: exynos: ...
412
413
414
415
416
417
418
419
420
421
422
423
424
  	ret = cpufreq_register_driver(&exynos_driver);
  	if (ret) {
  		dev_err(dvfs_info->dev,
  			"%s: failed to register cpufreq driver
  ", __func__);
  		goto err_free_table;
  	}
  
  	of_node_put(np);
  	dvfs_info->dvfs_enabled = true;
  	return 0;
  
  err_free_table:
5d4879cda   Nishanth Menon   PM / OPP: rename ...
425
  	dev_pm_opp_free_cpufreq_table(dvfs_info->dev, &dvfs_info->freq_table);
c6104fdbb   Viresh Kumar   exynos5440: free ...
426
427
  err_free_opp:
  	of_free_opp_table(dvfs_info->dev);
49d7b5bfb   Amit Daniel Kachhap   cpufreq: exynos: ...
428
429
  err_put_node:
  	of_node_put(np);
116decb7e   Sachin Kamat   cpufreq: exynos54...
430
431
  	dev_err(&pdev->dev, "%s: failed initialization
  ", __func__);
49d7b5bfb   Amit Daniel Kachhap   cpufreq: exynos: ...
432
433
434
435
436
437
  	return ret;
  }
  
  static int exynos_cpufreq_remove(struct platform_device *pdev)
  {
  	cpufreq_unregister_driver(&exynos_driver);
5d4879cda   Nishanth Menon   PM / OPP: rename ...
438
  	dev_pm_opp_free_cpufreq_table(dvfs_info->dev, &dvfs_info->freq_table);
c6104fdbb   Viresh Kumar   exynos5440: free ...
439
  	of_free_opp_table(dvfs_info->dev);
49d7b5bfb   Amit Daniel Kachhap   cpufreq: exynos: ...
440
441
442
443
444
445
  	return 0;
  }
  
  static struct platform_driver exynos_cpufreq_platdrv = {
  	.driver = {
  		.name	= "exynos5440-cpufreq",
49d7b5bfb   Amit Daniel Kachhap   cpufreq: exynos: ...
446
447
448
449
450
451
452
453
454
455
  		.of_match_table = exynos_cpufreq_match,
  	},
  	.probe		= exynos_cpufreq_probe,
  	.remove		= exynos_cpufreq_remove,
  };
  module_platform_driver(exynos_cpufreq_platdrv);
  
  MODULE_AUTHOR("Amit Daniel Kachhap <amit.daniel@samsung.com>");
  MODULE_DESCRIPTION("Exynos5440 cpufreq driver");
  MODULE_LICENSE("GPL");