Commit | Line | Data |
---|---|---|
0722249a CC |
1 | /* |
2 | * Generic Exynos Bus frequency driver with DEVFREQ Framework | |
3 | * | |
403e0689 | 4 | * Copyright (c) 2016 Samsung Electronics Co., Ltd. |
0722249a CC |
5 | * Author : Chanwoo Choi <cw00.choi@samsung.com> |
6 | * | |
7 | * This driver support Exynos Bus frequency feature by using | |
8 | * DEVFREQ framework and is based on drivers/devfreq/exynos/exynos4_bus.c. | |
9 | * | |
10 | * This program is free software; you can redistribute it and/or modify | |
11 | * it under the terms of the GNU General Public License version 2 as | |
12 | * published by the Free Software Foundation. | |
13 | */ | |
14 | ||
15 | #include <linux/clk.h> | |
16 | #include <linux/devfreq.h> | |
17 | #include <linux/devfreq-event.h> | |
18 | #include <linux/device.h> | |
19 | #include <linux/export.h> | |
20 | #include <linux/module.h> | |
21 | #include <linux/of_device.h> | |
22 | #include <linux/pm_opp.h> | |
23 | #include <linux/platform_device.h> | |
24 | #include <linux/regulator/consumer.h> | |
25 | #include <linux/slab.h> | |
26 | ||
27 | #define DEFAULT_SATURATION_RATIO 40 | |
28 | #define DEFAULT_VOLTAGE_TOLERANCE 2 | |
29 | ||
30 | struct exynos_bus { | |
31 | struct device *dev; | |
32 | ||
33 | struct devfreq *devfreq; | |
34 | struct devfreq_event_dev **edev; | |
35 | unsigned int edev_count; | |
36 | struct mutex lock; | |
37 | ||
c8ce82b9 | 38 | unsigned long curr_freq; |
0722249a CC |
39 | |
40 | struct regulator *regulator; | |
41 | struct clk *clk; | |
42 | unsigned int voltage_tolerance; | |
43 | unsigned int ratio; | |
44 | }; | |
45 | ||
46 | /* | |
47 | * Control the devfreq-event device to get the current state of bus | |
48 | */ | |
49 | #define exynos_bus_ops_edev(ops) \ | |
50 | static int exynos_bus_##ops(struct exynos_bus *bus) \ | |
51 | { \ | |
52 | int i, ret; \ | |
53 | \ | |
54 | for (i = 0; i < bus->edev_count; i++) { \ | |
55 | if (!bus->edev[i]) \ | |
56 | continue; \ | |
57 | ret = devfreq_event_##ops(bus->edev[i]); \ | |
58 | if (ret < 0) \ | |
59 | return ret; \ | |
60 | } \ | |
61 | \ | |
62 | return 0; \ | |
63 | } | |
64 | exynos_bus_ops_edev(enable_edev); | |
65 | exynos_bus_ops_edev(disable_edev); | |
66 | exynos_bus_ops_edev(set_event); | |
67 | ||
68 | static int exynos_bus_get_event(struct exynos_bus *bus, | |
69 | struct devfreq_event_data *edata) | |
70 | { | |
71 | struct devfreq_event_data event_data; | |
72 | unsigned long load_count = 0, total_count = 0; | |
73 | int i, ret = 0; | |
74 | ||
75 | for (i = 0; i < bus->edev_count; i++) { | |
76 | if (!bus->edev[i]) | |
77 | continue; | |
78 | ||
79 | ret = devfreq_event_get_event(bus->edev[i], &event_data); | |
80 | if (ret < 0) | |
81 | return ret; | |
82 | ||
83 | if (i == 0 || event_data.load_count > load_count) { | |
84 | load_count = event_data.load_count; | |
85 | total_count = event_data.total_count; | |
86 | } | |
87 | } | |
88 | ||
89 | edata->load_count = load_count; | |
90 | edata->total_count = total_count; | |
91 | ||
92 | return ret; | |
93 | } | |
94 | ||
95 | /* | |
403e0689 | 96 | * Must necessary function for devfreq simple-ondemand governor |
0722249a CC |
97 | */ |
98 | static int exynos_bus_target(struct device *dev, unsigned long *freq, u32 flags) | |
99 | { | |
100 | struct exynos_bus *bus = dev_get_drvdata(dev); | |
101 | struct dev_pm_opp *new_opp; | |
c8ce82b9 | 102 | unsigned long old_freq, new_freq, new_volt, tol; |
0722249a CC |
103 | int ret = 0; |
104 | ||
105 | /* Get new opp-bus instance according to new bus clock */ | |
0722249a CC |
106 | new_opp = devfreq_recommended_opp(dev, freq, flags); |
107 | if (IS_ERR(new_opp)) { | |
108 | dev_err(dev, "failed to get recommended opp instance\n"); | |
0722249a CC |
109 | return PTR_ERR(new_opp); |
110 | } | |
111 | ||
112 | new_freq = dev_pm_opp_get_freq(new_opp); | |
113 | new_volt = dev_pm_opp_get_voltage(new_opp); | |
8a31d9d9 VK |
114 | dev_pm_opp_put(new_opp); |
115 | ||
c8ce82b9 | 116 | old_freq = bus->curr_freq; |
0722249a CC |
117 | |
118 | if (old_freq == new_freq) | |
119 | return 0; | |
120 | tol = new_volt * bus->voltage_tolerance / 100; | |
121 | ||
122 | /* Change voltage and frequency according to new OPP level */ | |
123 | mutex_lock(&bus->lock); | |
124 | ||
125 | if (old_freq < new_freq) { | |
126 | ret = regulator_set_voltage_tol(bus->regulator, new_volt, tol); | |
127 | if (ret < 0) { | |
128 | dev_err(bus->dev, "failed to set voltage\n"); | |
129 | goto out; | |
130 | } | |
131 | } | |
132 | ||
133 | ret = clk_set_rate(bus->clk, new_freq); | |
134 | if (ret < 0) { | |
135 | dev_err(dev, "failed to change clock of bus\n"); | |
136 | clk_set_rate(bus->clk, old_freq); | |
137 | goto out; | |
138 | } | |
139 | ||
140 | if (old_freq > new_freq) { | |
141 | ret = regulator_set_voltage_tol(bus->regulator, new_volt, tol); | |
142 | if (ret < 0) { | |
143 | dev_err(bus->dev, "failed to set voltage\n"); | |
144 | goto out; | |
145 | } | |
146 | } | |
c8ce82b9 | 147 | bus->curr_freq = new_freq; |
0722249a | 148 | |
7b70246c CC |
149 | dev_dbg(dev, "Set the frequency of bus (%luHz -> %luHz, %luHz)\n", |
150 | old_freq, new_freq, clk_get_rate(bus->clk)); | |
0722249a CC |
151 | out: |
152 | mutex_unlock(&bus->lock); | |
153 | ||
154 | return ret; | |
155 | } | |
156 | ||
157 | static int exynos_bus_get_dev_status(struct device *dev, | |
158 | struct devfreq_dev_status *stat) | |
159 | { | |
160 | struct exynos_bus *bus = dev_get_drvdata(dev); | |
161 | struct devfreq_event_data edata; | |
162 | int ret; | |
163 | ||
c8ce82b9 | 164 | stat->current_frequency = bus->curr_freq; |
0722249a CC |
165 | |
166 | ret = exynos_bus_get_event(bus, &edata); | |
167 | if (ret < 0) { | |
168 | stat->total_time = stat->busy_time = 0; | |
169 | goto err; | |
170 | } | |
171 | ||
172 | stat->busy_time = (edata.load_count * 100) / bus->ratio; | |
173 | stat->total_time = edata.total_count; | |
174 | ||
175 | dev_dbg(dev, "Usage of devfreq-event : %lu/%lu\n", stat->busy_time, | |
176 | stat->total_time); | |
177 | ||
178 | err: | |
179 | ret = exynos_bus_set_event(bus); | |
180 | if (ret < 0) { | |
181 | dev_err(dev, "failed to set event to devfreq-event devices\n"); | |
182 | return ret; | |
183 | } | |
184 | ||
185 | return ret; | |
186 | } | |
187 | ||
188 | static void exynos_bus_exit(struct device *dev) | |
189 | { | |
190 | struct exynos_bus *bus = dev_get_drvdata(dev); | |
191 | int ret; | |
192 | ||
193 | ret = exynos_bus_disable_edev(bus); | |
194 | if (ret < 0) | |
195 | dev_warn(dev, "failed to disable the devfreq-event devices\n"); | |
196 | ||
197 | if (bus->regulator) | |
198 | regulator_disable(bus->regulator); | |
199 | ||
200 | dev_pm_opp_of_remove_table(dev); | |
403e0689 | 201 | clk_disable_unprepare(bus->clk); |
0722249a CC |
202 | } |
203 | ||
403e0689 CC |
204 | /* |
205 | * Must necessary function for devfreq passive governor | |
206 | */ | |
207 | static int exynos_bus_passive_target(struct device *dev, unsigned long *freq, | |
208 | u32 flags) | |
0722249a | 209 | { |
403e0689 CC |
210 | struct exynos_bus *bus = dev_get_drvdata(dev); |
211 | struct dev_pm_opp *new_opp; | |
212 | unsigned long old_freq, new_freq; | |
213 | int ret = 0; | |
0722249a | 214 | |
403e0689 | 215 | /* Get new opp-bus instance according to new bus clock */ |
403e0689 CC |
216 | new_opp = devfreq_recommended_opp(dev, freq, flags); |
217 | if (IS_ERR(new_opp)) { | |
218 | dev_err(dev, "failed to get recommended opp instance\n"); | |
403e0689 | 219 | return PTR_ERR(new_opp); |
0722249a CC |
220 | } |
221 | ||
403e0689 | 222 | new_freq = dev_pm_opp_get_freq(new_opp); |
8a31d9d9 VK |
223 | dev_pm_opp_put(new_opp); |
224 | ||
c8ce82b9 | 225 | old_freq = bus->curr_freq; |
0722249a | 226 | |
403e0689 CC |
227 | if (old_freq == new_freq) |
228 | return 0; | |
229 | ||
230 | /* Change the frequency according to new OPP level */ | |
231 | mutex_lock(&bus->lock); | |
232 | ||
233 | ret = clk_set_rate(bus->clk, new_freq); | |
0722249a | 234 | if (ret < 0) { |
403e0689 CC |
235 | dev_err(dev, "failed to set the clock of bus\n"); |
236 | goto out; | |
0722249a CC |
237 | } |
238 | ||
403e0689 | 239 | *freq = new_freq; |
c8ce82b9 | 240 | bus->curr_freq = new_freq; |
403e0689 | 241 | |
7b70246c CC |
242 | dev_dbg(dev, "Set the frequency of bus (%luHz -> %luHz, %luHz)\n", |
243 | old_freq, new_freq, clk_get_rate(bus->clk)); | |
403e0689 CC |
244 | out: |
245 | mutex_unlock(&bus->lock); | |
246 | ||
247 | return ret; | |
248 | } | |
249 | ||
250 | static void exynos_bus_passive_exit(struct device *dev) | |
251 | { | |
252 | struct exynos_bus *bus = dev_get_drvdata(dev); | |
253 | ||
254 | dev_pm_opp_of_remove_table(dev); | |
255 | clk_disable_unprepare(bus->clk); | |
256 | } | |
257 | ||
258 | static int exynos_bus_parent_parse_of(struct device_node *np, | |
259 | struct exynos_bus *bus) | |
260 | { | |
261 | struct device *dev = bus->dev; | |
262 | int i, ret, count, size; | |
0722249a CC |
263 | |
264 | /* Get the regulator to provide each bus with the power */ | |
265 | bus->regulator = devm_regulator_get(dev, "vdd"); | |
266 | if (IS_ERR(bus->regulator)) { | |
267 | dev_err(dev, "failed to get VDD regulator\n"); | |
403e0689 | 268 | return PTR_ERR(bus->regulator); |
0722249a CC |
269 | } |
270 | ||
271 | ret = regulator_enable(bus->regulator); | |
272 | if (ret < 0) { | |
273 | dev_err(dev, "failed to enable VDD regulator\n"); | |
403e0689 | 274 | return ret; |
0722249a CC |
275 | } |
276 | ||
277 | /* | |
278 | * Get the devfreq-event devices to get the current utilization of | |
279 | * buses. This raw data will be used in devfreq ondemand governor. | |
280 | */ | |
281 | count = devfreq_event_get_edev_count(dev); | |
282 | if (count < 0) { | |
283 | dev_err(dev, "failed to get the count of devfreq-event dev\n"); | |
284 | ret = count; | |
285 | goto err_regulator; | |
286 | } | |
287 | bus->edev_count = count; | |
288 | ||
289 | size = sizeof(*bus->edev) * count; | |
290 | bus->edev = devm_kzalloc(dev, size, GFP_KERNEL); | |
291 | if (!bus->edev) { | |
292 | ret = -ENOMEM; | |
293 | goto err_regulator; | |
294 | } | |
295 | ||
296 | for (i = 0; i < count; i++) { | |
297 | bus->edev[i] = devfreq_event_get_edev_by_phandle(dev, i); | |
298 | if (IS_ERR(bus->edev[i])) { | |
299 | ret = -EPROBE_DEFER; | |
300 | goto err_regulator; | |
301 | } | |
302 | } | |
303 | ||
304 | /* | |
305 | * Optionally, Get the saturation ratio according to Exynos SoC | |
306 | * When measuring the utilization of each AXI bus with devfreq-event | |
307 | * devices, the measured real cycle might be much lower than the | |
308 | * total cycle of bus during sampling rate. In result, the devfreq | |
309 | * simple-ondemand governor might not decide to change the current | |
310 | * frequency due to too utilization (= real cycle/total cycle). | |
311 | * So, this property is used to adjust the utilization when calculating | |
312 | * the busy_time in exynos_bus_get_dev_status(). | |
313 | */ | |
314 | if (of_property_read_u32(np, "exynos,saturation-ratio", &bus->ratio)) | |
315 | bus->ratio = DEFAULT_SATURATION_RATIO; | |
316 | ||
317 | if (of_property_read_u32(np, "exynos,voltage-tolerance", | |
318 | &bus->voltage_tolerance)) | |
319 | bus->voltage_tolerance = DEFAULT_VOLTAGE_TOLERANCE; | |
320 | ||
321 | return 0; | |
322 | ||
323 | err_regulator: | |
324 | regulator_disable(bus->regulator); | |
403e0689 CC |
325 | |
326 | return ret; | |
327 | } | |
328 | ||
329 | static int exynos_bus_parse_of(struct device_node *np, | |
330 | struct exynos_bus *bus) | |
331 | { | |
332 | struct device *dev = bus->dev; | |
c8ce82b9 | 333 | struct dev_pm_opp *opp; |
403e0689 CC |
334 | unsigned long rate; |
335 | int ret; | |
336 | ||
337 | /* Get the clock to provide each bus with source clock */ | |
338 | bus->clk = devm_clk_get(dev, "bus"); | |
339 | if (IS_ERR(bus->clk)) { | |
340 | dev_err(dev, "failed to get bus clock\n"); | |
341 | return PTR_ERR(bus->clk); | |
342 | } | |
343 | ||
344 | ret = clk_prepare_enable(bus->clk); | |
345 | if (ret < 0) { | |
346 | dev_err(dev, "failed to get enable clock\n"); | |
347 | return ret; | |
348 | } | |
349 | ||
350 | /* Get the freq and voltage from OPP table to scale the bus freq */ | |
403e0689 CC |
351 | ret = dev_pm_opp_of_add_table(dev); |
352 | if (ret < 0) { | |
353 | dev_err(dev, "failed to get OPP table\n"); | |
403e0689 CC |
354 | goto err_clk; |
355 | } | |
356 | ||
357 | rate = clk_get_rate(bus->clk); | |
c8ce82b9 | 358 | |
c8ce82b9 VK |
359 | opp = devfreq_recommended_opp(dev, &rate, 0); |
360 | if (IS_ERR(opp)) { | |
403e0689 | 361 | dev_err(dev, "failed to find dev_pm_opp\n"); |
c8ce82b9 | 362 | ret = PTR_ERR(opp); |
403e0689 CC |
363 | goto err_opp; |
364 | } | |
c8ce82b9 | 365 | bus->curr_freq = dev_pm_opp_get_freq(opp); |
8a31d9d9 | 366 | dev_pm_opp_put(opp); |
403e0689 CC |
367 | |
368 | return 0; | |
369 | ||
0722249a CC |
370 | err_opp: |
371 | dev_pm_opp_of_remove_table(dev); | |
372 | err_clk: | |
373 | clk_disable_unprepare(bus->clk); | |
374 | ||
375 | return ret; | |
376 | } | |
377 | ||
378 | static int exynos_bus_probe(struct platform_device *pdev) | |
379 | { | |
380 | struct device *dev = &pdev->dev; | |
d8150d14 | 381 | struct device_node *np = dev->of_node, *node; |
0722249a CC |
382 | struct devfreq_dev_profile *profile; |
383 | struct devfreq_simple_ondemand_data *ondemand_data; | |
403e0689 CC |
384 | struct devfreq_passive_data *passive_data; |
385 | struct devfreq *parent_devfreq; | |
0722249a | 386 | struct exynos_bus *bus; |
403e0689 CC |
387 | int ret, max_state; |
388 | unsigned long min_freq, max_freq; | |
0722249a CC |
389 | |
390 | if (!np) { | |
391 | dev_err(dev, "failed to find devicetree node\n"); | |
392 | return -EINVAL; | |
393 | } | |
394 | ||
395 | bus = devm_kzalloc(&pdev->dev, sizeof(*bus), GFP_KERNEL); | |
396 | if (!bus) | |
397 | return -ENOMEM; | |
398 | mutex_init(&bus->lock); | |
399 | bus->dev = &pdev->dev; | |
400 | platform_set_drvdata(pdev, bus); | |
401 | ||
402 | /* Parse the device-tree to get the resource information */ | |
403 | ret = exynos_bus_parse_of(np, bus); | |
404 | if (ret < 0) | |
c07e074b | 405 | return ret; |
0722249a | 406 | |
0722249a | 407 | profile = devm_kzalloc(dev, sizeof(*profile), GFP_KERNEL); |
403e0689 CC |
408 | if (!profile) { |
409 | ret = -ENOMEM; | |
410 | goto err; | |
411 | } | |
412 | ||
d8150d14 PC |
413 | node = of_parse_phandle(dev->of_node, "devfreq", 0); |
414 | if (node) { | |
415 | of_node_put(node); | |
403e0689 | 416 | goto passive; |
d8150d14 | 417 | } else { |
403e0689 | 418 | ret = exynos_bus_parent_parse_of(np, bus); |
d8150d14 | 419 | } |
403e0689 CC |
420 | |
421 | if (ret < 0) | |
422 | goto err; | |
423 | ||
83cb0e4d | 424 | /* Initialize the struct profile and governor data for parent device */ |
0722249a CC |
425 | profile->polling_ms = 50; |
426 | profile->target = exynos_bus_target; | |
427 | profile->get_dev_status = exynos_bus_get_dev_status; | |
428 | profile->exit = exynos_bus_exit; | |
429 | ||
430 | ondemand_data = devm_kzalloc(dev, sizeof(*ondemand_data), GFP_KERNEL); | |
403e0689 CC |
431 | if (!ondemand_data) { |
432 | ret = -ENOMEM; | |
433 | goto err; | |
434 | } | |
0722249a CC |
435 | ondemand_data->upthreshold = 40; |
436 | ondemand_data->downdifferential = 5; | |
437 | ||
438 | /* Add devfreq device to monitor and handle the exynos bus */ | |
aa7c352f CC |
439 | bus->devfreq = devm_devfreq_add_device(dev, profile, |
440 | DEVFREQ_GOV_SIMPLE_ONDEMAND, | |
0722249a CC |
441 | ondemand_data); |
442 | if (IS_ERR(bus->devfreq)) { | |
443 | dev_err(dev, "failed to add devfreq device\n"); | |
403e0689 CC |
444 | ret = PTR_ERR(bus->devfreq); |
445 | goto err; | |
0722249a CC |
446 | } |
447 | ||
448 | /* Register opp_notifier to catch the change of OPP */ | |
449 | ret = devm_devfreq_register_opp_notifier(dev, bus->devfreq); | |
450 | if (ret < 0) { | |
451 | dev_err(dev, "failed to register opp notifier\n"); | |
403e0689 | 452 | goto err; |
0722249a CC |
453 | } |
454 | ||
455 | /* | |
456 | * Enable devfreq-event to get raw data which is used to determine | |
457 | * current bus load. | |
458 | */ | |
459 | ret = exynos_bus_enable_edev(bus); | |
460 | if (ret < 0) { | |
461 | dev_err(dev, "failed to enable devfreq-event devices\n"); | |
403e0689 | 462 | goto err; |
0722249a CC |
463 | } |
464 | ||
465 | ret = exynos_bus_set_event(bus); | |
466 | if (ret < 0) { | |
467 | dev_err(dev, "failed to set event to devfreq-event devices\n"); | |
403e0689 | 468 | goto err; |
0722249a CC |
469 | } |
470 | ||
403e0689 CC |
471 | goto out; |
472 | passive: | |
83cb0e4d | 473 | /* Initialize the struct profile and governor data for passive device */ |
403e0689 CC |
474 | profile->target = exynos_bus_passive_target; |
475 | profile->exit = exynos_bus_passive_exit; | |
476 | ||
477 | /* Get the instance of parent devfreq device */ | |
478 | parent_devfreq = devfreq_get_devfreq_by_phandle(dev, 0); | |
479 | if (IS_ERR(parent_devfreq)) { | |
480 | ret = -EPROBE_DEFER; | |
481 | goto err; | |
482 | } | |
483 | ||
484 | passive_data = devm_kzalloc(dev, sizeof(*passive_data), GFP_KERNEL); | |
485 | if (!passive_data) { | |
486 | ret = -ENOMEM; | |
487 | goto err; | |
488 | } | |
489 | passive_data->parent = parent_devfreq; | |
490 | ||
491 | /* Add devfreq device for exynos bus with passive governor */ | |
aa7c352f | 492 | bus->devfreq = devm_devfreq_add_device(dev, profile, DEVFREQ_GOV_PASSIVE, |
403e0689 CC |
493 | passive_data); |
494 | if (IS_ERR(bus->devfreq)) { | |
495 | dev_err(dev, | |
496 | "failed to add devfreq dev with passive governor\n"); | |
32dd7731 | 497 | ret = PTR_ERR(bus->devfreq); |
403e0689 CC |
498 | goto err; |
499 | } | |
500 | ||
501 | out: | |
502 | max_state = bus->devfreq->profile->max_state; | |
503 | min_freq = (bus->devfreq->profile->freq_table[0] / 1000); | |
504 | max_freq = (bus->devfreq->profile->freq_table[max_state - 1] / 1000); | |
505 | pr_info("exynos-bus: new bus device registered: %s (%6ld KHz ~ %6ld KHz)\n", | |
506 | dev_name(dev), min_freq, max_freq); | |
507 | ||
0722249a | 508 | return 0; |
403e0689 CC |
509 | |
510 | err: | |
511 | dev_pm_opp_of_remove_table(dev); | |
512 | clk_disable_unprepare(bus->clk); | |
513 | ||
514 | return ret; | |
0722249a CC |
515 | } |
516 | ||
fbb9c3c9 MS |
517 | static void exynos_bus_shutdown(struct platform_device *pdev) |
518 | { | |
519 | struct exynos_bus *bus = dev_get_drvdata(&pdev->dev); | |
520 | ||
521 | devfreq_suspend_device(bus->devfreq); | |
522 | } | |
523 | ||
0722249a CC |
524 | #ifdef CONFIG_PM_SLEEP |
525 | static int exynos_bus_resume(struct device *dev) | |
526 | { | |
527 | struct exynos_bus *bus = dev_get_drvdata(dev); | |
528 | int ret; | |
529 | ||
530 | ret = exynos_bus_enable_edev(bus); | |
531 | if (ret < 0) { | |
532 | dev_err(dev, "failed to enable the devfreq-event devices\n"); | |
533 | return ret; | |
534 | } | |
535 | ||
536 | return 0; | |
537 | } | |
538 | ||
539 | static int exynos_bus_suspend(struct device *dev) | |
540 | { | |
541 | struct exynos_bus *bus = dev_get_drvdata(dev); | |
542 | int ret; | |
543 | ||
544 | ret = exynos_bus_disable_edev(bus); | |
545 | if (ret < 0) { | |
546 | dev_err(dev, "failed to disable the devfreq-event devices\n"); | |
547 | return ret; | |
548 | } | |
549 | ||
550 | return 0; | |
551 | } | |
552 | #endif | |
553 | ||
554 | static const struct dev_pm_ops exynos_bus_pm = { | |
555 | SET_SYSTEM_SLEEP_PM_OPS(exynos_bus_suspend, exynos_bus_resume) | |
556 | }; | |
557 | ||
558 | static const struct of_device_id exynos_bus_of_match[] = { | |
559 | { .compatible = "samsung,exynos-bus", }, | |
560 | { /* sentinel */ }, | |
561 | }; | |
562 | MODULE_DEVICE_TABLE(of, exynos_bus_of_match); | |
563 | ||
564 | static struct platform_driver exynos_bus_platdrv = { | |
565 | .probe = exynos_bus_probe, | |
fbb9c3c9 | 566 | .shutdown = exynos_bus_shutdown, |
0722249a CC |
567 | .driver = { |
568 | .name = "exynos-bus", | |
569 | .pm = &exynos_bus_pm, | |
570 | .of_match_table = of_match_ptr(exynos_bus_of_match), | |
571 | }, | |
572 | }; | |
573 | module_platform_driver(exynos_bus_platdrv); | |
574 | ||
575 | MODULE_DESCRIPTION("Generic Exynos Bus frequency driver"); | |
576 | MODULE_AUTHOR("Chanwoo Choi <cw00.choi@samsung.com>"); | |
577 | MODULE_LICENSE("GPL v2"); |