diff options
author | Bartlomiej Zolnierkiewicz <b.zolnierkie@samsung.com> | 2014-03-25 14:51:39 -0400 |
---|---|---|
committer | Tejun Heo <tj@kernel.org> | 2014-03-25 15:17:13 -0400 |
commit | fd990556f0fa25446c6bfa9cf4c9e49d387d4472 (patch) | |
tree | 0020bd48b0312034f1d84d4560e323961d052669 /drivers/ata/libahci_platform.c | |
parent | 83b03fd67b9b3fa3795871169f3c08c35b3d6ea8 (diff) |
ata: move library code from ahci_platform.c to libahci_platform.c
Move AHCI platform library code from ahci_platform.c to
libahci_platform.c and fix dependencies for ahci_st,
ahci_imx and ahci_sunxi drivers.
Acked-by: Hans de Goede <hdegoede@redhat.com>
Signed-off-by: Bartlomiej Zolnierkiewicz <b.zolnierkie@samsung.com>
Signed-off-by: Tejun Heo <tj@kernel.org>
Diffstat (limited to 'drivers/ata/libahci_platform.c')
-rw-r--r-- | drivers/ata/libahci_platform.c | 541 |
1 files changed, 541 insertions, 0 deletions
diff --git a/drivers/ata/libahci_platform.c b/drivers/ata/libahci_platform.c new file mode 100644 index 000000000000..7cb3a85719c0 --- /dev/null +++ b/drivers/ata/libahci_platform.c | |||
@@ -0,0 +1,541 @@ | |||
1 | /* | ||
2 | * AHCI SATA platform library | ||
3 | * | ||
4 | * Copyright 2004-2005 Red Hat, Inc. | ||
5 | * Jeff Garzik <jgarzik@pobox.com> | ||
6 | * Copyright 2010 MontaVista Software, LLC. | ||
7 | * Anton Vorontsov <avorontsov@ru.mvista.com> | ||
8 | * | ||
9 | * This program is free software; you can redistribute it and/or modify | ||
10 | * it under the terms of the GNU General Public License as published by | ||
11 | * the Free Software Foundation; either version 2, or (at your option) | ||
12 | * any later version. | ||
13 | */ | ||
14 | |||
15 | #include <linux/clk.h> | ||
16 | #include <linux/kernel.h> | ||
17 | #include <linux/gfp.h> | ||
18 | #include <linux/module.h> | ||
19 | #include <linux/pm.h> | ||
20 | #include <linux/interrupt.h> | ||
21 | #include <linux/device.h> | ||
22 | #include <linux/platform_device.h> | ||
23 | #include <linux/libata.h> | ||
24 | #include <linux/ahci_platform.h> | ||
25 | #include <linux/phy/phy.h> | ||
26 | #include <linux/pm_runtime.h> | ||
27 | #include "ahci.h" | ||
28 | |||
29 | static void ahci_host_stop(struct ata_host *host); | ||
30 | |||
31 | struct ata_port_operations ahci_platform_ops = { | ||
32 | .inherits = &ahci_ops, | ||
33 | .host_stop = ahci_host_stop, | ||
34 | }; | ||
35 | EXPORT_SYMBOL_GPL(ahci_platform_ops); | ||
36 | |||
37 | static struct scsi_host_template ahci_platform_sht = { | ||
38 | AHCI_SHT("ahci_platform"), | ||
39 | }; | ||
40 | |||
41 | /** | ||
42 | * ahci_platform_enable_clks - Enable platform clocks | ||
43 | * @hpriv: host private area to store config values | ||
44 | * | ||
45 | * This function enables all the clks found in hpriv->clks, starting at | ||
46 | * index 0. If any clk fails to enable it disables all the clks already | ||
47 | * enabled in reverse order, and then returns an error. | ||
48 | * | ||
49 | * RETURNS: | ||
50 | * 0 on success otherwise a negative error code | ||
51 | */ | ||
52 | int ahci_platform_enable_clks(struct ahci_host_priv *hpriv) | ||
53 | { | ||
54 | int c, rc; | ||
55 | |||
56 | for (c = 0; c < AHCI_MAX_CLKS && hpriv->clks[c]; c++) { | ||
57 | rc = clk_prepare_enable(hpriv->clks[c]); | ||
58 | if (rc) | ||
59 | goto disable_unprepare_clk; | ||
60 | } | ||
61 | return 0; | ||
62 | |||
63 | disable_unprepare_clk: | ||
64 | while (--c >= 0) | ||
65 | clk_disable_unprepare(hpriv->clks[c]); | ||
66 | return rc; | ||
67 | } | ||
68 | EXPORT_SYMBOL_GPL(ahci_platform_enable_clks); | ||
69 | |||
70 | /** | ||
71 | * ahci_platform_disable_clks - Disable platform clocks | ||
72 | * @hpriv: host private area to store config values | ||
73 | * | ||
74 | * This function disables all the clks found in hpriv->clks, in reverse | ||
75 | * order of ahci_platform_enable_clks (starting at the end of the array). | ||
76 | */ | ||
77 | void ahci_platform_disable_clks(struct ahci_host_priv *hpriv) | ||
78 | { | ||
79 | int c; | ||
80 | |||
81 | for (c = AHCI_MAX_CLKS - 1; c >= 0; c--) | ||
82 | if (hpriv->clks[c]) | ||
83 | clk_disable_unprepare(hpriv->clks[c]); | ||
84 | } | ||
85 | EXPORT_SYMBOL_GPL(ahci_platform_disable_clks); | ||
86 | |||
87 | /** | ||
88 | * ahci_platform_enable_resources - Enable platform resources | ||
89 | * @hpriv: host private area to store config values | ||
90 | * | ||
91 | * This function enables all ahci_platform managed resources in the | ||
92 | * following order: | ||
93 | * 1) Regulator | ||
94 | * 2) Clocks (through ahci_platform_enable_clks) | ||
95 | * 3) Phy | ||
96 | * | ||
97 | * If resource enabling fails at any point the previous enabled resources | ||
98 | * are disabled in reverse order. | ||
99 | * | ||
100 | * RETURNS: | ||
101 | * 0 on success otherwise a negative error code | ||
102 | */ | ||
103 | int ahci_platform_enable_resources(struct ahci_host_priv *hpriv) | ||
104 | { | ||
105 | int rc; | ||
106 | |||
107 | if (hpriv->target_pwr) { | ||
108 | rc = regulator_enable(hpriv->target_pwr); | ||
109 | if (rc) | ||
110 | return rc; | ||
111 | } | ||
112 | |||
113 | rc = ahci_platform_enable_clks(hpriv); | ||
114 | if (rc) | ||
115 | goto disable_regulator; | ||
116 | |||
117 | if (hpriv->phy) { | ||
118 | rc = phy_init(hpriv->phy); | ||
119 | if (rc) | ||
120 | goto disable_clks; | ||
121 | |||
122 | rc = phy_power_on(hpriv->phy); | ||
123 | if (rc) { | ||
124 | phy_exit(hpriv->phy); | ||
125 | goto disable_clks; | ||
126 | } | ||
127 | } | ||
128 | |||
129 | return 0; | ||
130 | |||
131 | disable_clks: | ||
132 | ahci_platform_disable_clks(hpriv); | ||
133 | |||
134 | disable_regulator: | ||
135 | if (hpriv->target_pwr) | ||
136 | regulator_disable(hpriv->target_pwr); | ||
137 | return rc; | ||
138 | } | ||
139 | EXPORT_SYMBOL_GPL(ahci_platform_enable_resources); | ||
140 | |||
141 | /** | ||
142 | * ahci_platform_disable_resources - Disable platform resources | ||
143 | * @hpriv: host private area to store config values | ||
144 | * | ||
145 | * This function disables all ahci_platform managed resources in the | ||
146 | * following order: | ||
147 | * 1) Phy | ||
148 | * 2) Clocks (through ahci_platform_disable_clks) | ||
149 | * 3) Regulator | ||
150 | */ | ||
151 | void ahci_platform_disable_resources(struct ahci_host_priv *hpriv) | ||
152 | { | ||
153 | if (hpriv->phy) { | ||
154 | phy_power_off(hpriv->phy); | ||
155 | phy_exit(hpriv->phy); | ||
156 | } | ||
157 | |||
158 | ahci_platform_disable_clks(hpriv); | ||
159 | |||
160 | if (hpriv->target_pwr) | ||
161 | regulator_disable(hpriv->target_pwr); | ||
162 | } | ||
163 | EXPORT_SYMBOL_GPL(ahci_platform_disable_resources); | ||
164 | |||
165 | static void ahci_platform_put_resources(struct device *dev, void *res) | ||
166 | { | ||
167 | struct ahci_host_priv *hpriv = res; | ||
168 | int c; | ||
169 | |||
170 | if (hpriv->got_runtime_pm) { | ||
171 | pm_runtime_put_sync(dev); | ||
172 | pm_runtime_disable(dev); | ||
173 | } | ||
174 | |||
175 | for (c = 0; c < AHCI_MAX_CLKS && hpriv->clks[c]; c++) | ||
176 | clk_put(hpriv->clks[c]); | ||
177 | } | ||
178 | |||
179 | /** | ||
180 | * ahci_platform_get_resources - Get platform resources | ||
181 | * @pdev: platform device to get resources for | ||
182 | * | ||
183 | * This function allocates an ahci_host_priv struct, and gets the following | ||
184 | * resources, storing a reference to them inside the returned struct: | ||
185 | * | ||
186 | * 1) mmio registers (IORESOURCE_MEM 0, mandatory) | ||
187 | * 2) regulator for controlling the targets power (optional) | ||
188 | * 3) 0 - AHCI_MAX_CLKS clocks, as specified in the devs devicetree node, | ||
189 | * or for non devicetree enabled platforms a single clock | ||
190 | * 4) phy (optional) | ||
191 | * | ||
192 | * RETURNS: | ||
193 | * The allocated ahci_host_priv on success, otherwise an ERR_PTR value | ||
194 | */ | ||
195 | struct ahci_host_priv *ahci_platform_get_resources(struct platform_device *pdev) | ||
196 | { | ||
197 | struct device *dev = &pdev->dev; | ||
198 | struct ahci_host_priv *hpriv; | ||
199 | struct clk *clk; | ||
200 | int i, rc = -ENOMEM; | ||
201 | |||
202 | if (!devres_open_group(dev, NULL, GFP_KERNEL)) | ||
203 | return ERR_PTR(-ENOMEM); | ||
204 | |||
205 | hpriv = devres_alloc(ahci_platform_put_resources, sizeof(*hpriv), | ||
206 | GFP_KERNEL); | ||
207 | if (!hpriv) | ||
208 | goto err_out; | ||
209 | |||
210 | devres_add(dev, hpriv); | ||
211 | |||
212 | hpriv->mmio = devm_ioremap_resource(dev, | ||
213 | platform_get_resource(pdev, IORESOURCE_MEM, 0)); | ||
214 | if (IS_ERR(hpriv->mmio)) { | ||
215 | dev_err(dev, "no mmio space\n"); | ||
216 | rc = PTR_ERR(hpriv->mmio); | ||
217 | goto err_out; | ||
218 | } | ||
219 | |||
220 | hpriv->target_pwr = devm_regulator_get_optional(dev, "target"); | ||
221 | if (IS_ERR(hpriv->target_pwr)) { | ||
222 | rc = PTR_ERR(hpriv->target_pwr); | ||
223 | if (rc == -EPROBE_DEFER) | ||
224 | goto err_out; | ||
225 | hpriv->target_pwr = NULL; | ||
226 | } | ||
227 | |||
228 | for (i = 0; i < AHCI_MAX_CLKS; i++) { | ||
229 | /* | ||
230 | * For now we must use clk_get(dev, NULL) for the first clock, | ||
231 | * because some platforms (da850, spear13xx) are not yet | ||
232 | * converted to use devicetree for clocks. For new platforms | ||
233 | * this is equivalent to of_clk_get(dev->of_node, 0). | ||
234 | */ | ||
235 | if (i == 0) | ||
236 | clk = clk_get(dev, NULL); | ||
237 | else | ||
238 | clk = of_clk_get(dev->of_node, i); | ||
239 | |||
240 | if (IS_ERR(clk)) { | ||
241 | rc = PTR_ERR(clk); | ||
242 | if (rc == -EPROBE_DEFER) | ||
243 | goto err_out; | ||
244 | break; | ||
245 | } | ||
246 | hpriv->clks[i] = clk; | ||
247 | } | ||
248 | |||
249 | hpriv->phy = devm_phy_get(dev, "sata-phy"); | ||
250 | if (IS_ERR(hpriv->phy)) { | ||
251 | rc = PTR_ERR(hpriv->phy); | ||
252 | switch (rc) { | ||
253 | case -ENODEV: | ||
254 | case -ENOSYS: | ||
255 | /* continue normally */ | ||
256 | hpriv->phy = NULL; | ||
257 | break; | ||
258 | |||
259 | case -EPROBE_DEFER: | ||
260 | goto err_out; | ||
261 | |||
262 | default: | ||
263 | dev_err(dev, "couldn't get sata-phy\n"); | ||
264 | goto err_out; | ||
265 | } | ||
266 | } | ||
267 | |||
268 | pm_runtime_enable(dev); | ||
269 | pm_runtime_get_sync(dev); | ||
270 | hpriv->got_runtime_pm = true; | ||
271 | |||
272 | devres_remove_group(dev, NULL); | ||
273 | return hpriv; | ||
274 | |||
275 | err_out: | ||
276 | devres_release_group(dev, NULL); | ||
277 | return ERR_PTR(rc); | ||
278 | } | ||
279 | EXPORT_SYMBOL_GPL(ahci_platform_get_resources); | ||
280 | |||
281 | /** | ||
282 | * ahci_platform_init_host - Bring up an ahci-platform host | ||
283 | * @pdev: platform device pointer for the host | ||
284 | * @hpriv: ahci-host private data for the host | ||
285 | * @pi_template: template for the ata_port_info to use | ||
286 | * @force_port_map: param passed to ahci_save_initial_config | ||
287 | * @mask_port_map: param passed to ahci_save_initial_config | ||
288 | * | ||
289 | * This function does all the usual steps needed to bring up an | ||
290 | * ahci-platform host, note any necessary resources (ie clks, phy, etc.) | ||
291 | * must be initialized / enabled before calling this. | ||
292 | * | ||
293 | * RETURNS: | ||
294 | * 0 on success otherwise a negative error code | ||
295 | */ | ||
296 | int ahci_platform_init_host(struct platform_device *pdev, | ||
297 | struct ahci_host_priv *hpriv, | ||
298 | const struct ata_port_info *pi_template, | ||
299 | unsigned int force_port_map, | ||
300 | unsigned int mask_port_map) | ||
301 | { | ||
302 | struct device *dev = &pdev->dev; | ||
303 | struct ata_port_info pi = *pi_template; | ||
304 | const struct ata_port_info *ppi[] = { &pi, NULL }; | ||
305 | struct ata_host *host; | ||
306 | int i, irq, n_ports, rc; | ||
307 | |||
308 | irq = platform_get_irq(pdev, 0); | ||
309 | if (irq <= 0) { | ||
310 | dev_err(dev, "no irq\n"); | ||
311 | return -EINVAL; | ||
312 | } | ||
313 | |||
314 | /* prepare host */ | ||
315 | hpriv->flags |= (unsigned long)pi.private_data; | ||
316 | |||
317 | ahci_save_initial_config(dev, hpriv, force_port_map, mask_port_map); | ||
318 | |||
319 | if (hpriv->cap & HOST_CAP_NCQ) | ||
320 | pi.flags |= ATA_FLAG_NCQ; | ||
321 | |||
322 | if (hpriv->cap & HOST_CAP_PMP) | ||
323 | pi.flags |= ATA_FLAG_PMP; | ||
324 | |||
325 | ahci_set_em_messages(hpriv, &pi); | ||
326 | |||
327 | /* CAP.NP sometimes indicate the index of the last enabled | ||
328 | * port, at other times, that of the last possible port, so | ||
329 | * determining the maximum port number requires looking at | ||
330 | * both CAP.NP and port_map. | ||
331 | */ | ||
332 | n_ports = max(ahci_nr_ports(hpriv->cap), fls(hpriv->port_map)); | ||
333 | |||
334 | host = ata_host_alloc_pinfo(dev, ppi, n_ports); | ||
335 | if (!host) | ||
336 | return -ENOMEM; | ||
337 | |||
338 | host->private_data = hpriv; | ||
339 | |||
340 | if (!(hpriv->cap & HOST_CAP_SSS) || ahci_ignore_sss) | ||
341 | host->flags |= ATA_HOST_PARALLEL_SCAN; | ||
342 | else | ||
343 | dev_info(dev, "SSS flag set, parallel bus scan disabled\n"); | ||
344 | |||
345 | if (pi.flags & ATA_FLAG_EM) | ||
346 | ahci_reset_em(host); | ||
347 | |||
348 | for (i = 0; i < host->n_ports; i++) { | ||
349 | struct ata_port *ap = host->ports[i]; | ||
350 | |||
351 | ata_port_desc(ap, "mmio %pR", | ||
352 | platform_get_resource(pdev, IORESOURCE_MEM, 0)); | ||
353 | ata_port_desc(ap, "port 0x%x", 0x100 + ap->port_no * 0x80); | ||
354 | |||
355 | /* set enclosure management message type */ | ||
356 | if (ap->flags & ATA_FLAG_EM) | ||
357 | ap->em_message_type = hpriv->em_msg_type; | ||
358 | |||
359 | /* disabled/not-implemented port */ | ||
360 | if (!(hpriv->port_map & (1 << i))) | ||
361 | ap->ops = &ata_dummy_port_ops; | ||
362 | } | ||
363 | |||
364 | rc = ahci_reset_controller(host); | ||
365 | if (rc) | ||
366 | return rc; | ||
367 | |||
368 | ahci_init_controller(host); | ||
369 | ahci_print_info(host, "platform"); | ||
370 | |||
371 | return ata_host_activate(host, irq, ahci_interrupt, IRQF_SHARED, | ||
372 | &ahci_platform_sht); | ||
373 | } | ||
374 | EXPORT_SYMBOL_GPL(ahci_platform_init_host); | ||
375 | |||
376 | static void ahci_host_stop(struct ata_host *host) | ||
377 | { | ||
378 | struct device *dev = host->dev; | ||
379 | struct ahci_platform_data *pdata = dev_get_platdata(dev); | ||
380 | struct ahci_host_priv *hpriv = host->private_data; | ||
381 | |||
382 | if (pdata && pdata->exit) | ||
383 | pdata->exit(dev); | ||
384 | |||
385 | ahci_platform_disable_resources(hpriv); | ||
386 | } | ||
387 | |||
388 | #ifdef CONFIG_PM_SLEEP | ||
389 | /** | ||
390 | * ahci_platform_suspend_host - Suspend an ahci-platform host | ||
391 | * @dev: device pointer for the host | ||
392 | * | ||
393 | * This function does all the usual steps needed to suspend an | ||
394 | * ahci-platform host, note any necessary resources (ie clks, phy, etc.) | ||
395 | * must be disabled after calling this. | ||
396 | * | ||
397 | * RETURNS: | ||
398 | * 0 on success otherwise a negative error code | ||
399 | */ | ||
400 | int ahci_platform_suspend_host(struct device *dev) | ||
401 | { | ||
402 | struct ata_host *host = dev_get_drvdata(dev); | ||
403 | struct ahci_host_priv *hpriv = host->private_data; | ||
404 | void __iomem *mmio = hpriv->mmio; | ||
405 | u32 ctl; | ||
406 | |||
407 | if (hpriv->flags & AHCI_HFLAG_NO_SUSPEND) { | ||
408 | dev_err(dev, "firmware update required for suspend/resume\n"); | ||
409 | return -EIO; | ||
410 | } | ||
411 | |||
412 | /* | ||
413 | * AHCI spec rev1.1 section 8.3.3: | ||
414 | * Software must disable interrupts prior to requesting a | ||
415 | * transition of the HBA to D3 state. | ||
416 | */ | ||
417 | ctl = readl(mmio + HOST_CTL); | ||
418 | ctl &= ~HOST_IRQ_EN; | ||
419 | writel(ctl, mmio + HOST_CTL); | ||
420 | readl(mmio + HOST_CTL); /* flush */ | ||
421 | |||
422 | return ata_host_suspend(host, PMSG_SUSPEND); | ||
423 | } | ||
424 | EXPORT_SYMBOL_GPL(ahci_platform_suspend_host); | ||
425 | |||
426 | /** | ||
427 | * ahci_platform_resume_host - Resume an ahci-platform host | ||
428 | * @dev: device pointer for the host | ||
429 | * | ||
430 | * This function does all the usual steps needed to resume an ahci-platform | ||
431 | * host, note any necessary resources (ie clks, phy, etc.) must be | ||
432 | * initialized / enabled before calling this. | ||
433 | * | ||
434 | * RETURNS: | ||
435 | * 0 on success otherwise a negative error code | ||
436 | */ | ||
437 | int ahci_platform_resume_host(struct device *dev) | ||
438 | { | ||
439 | struct ata_host *host = dev_get_drvdata(dev); | ||
440 | int rc; | ||
441 | |||
442 | if (dev->power.power_state.event == PM_EVENT_SUSPEND) { | ||
443 | rc = ahci_reset_controller(host); | ||
444 | if (rc) | ||
445 | return rc; | ||
446 | |||
447 | ahci_init_controller(host); | ||
448 | } | ||
449 | |||
450 | ata_host_resume(host); | ||
451 | |||
452 | return 0; | ||
453 | } | ||
454 | EXPORT_SYMBOL_GPL(ahci_platform_resume_host); | ||
455 | |||
456 | /** | ||
457 | * ahci_platform_suspend - Suspend an ahci-platform device | ||
458 | * @dev: the platform device to suspend | ||
459 | * | ||
460 | * This function suspends the host associated with the device, followed by | ||
461 | * disabling all the resources of the device. | ||
462 | * | ||
463 | * RETURNS: | ||
464 | * 0 on success otherwise a negative error code | ||
465 | */ | ||
466 | int ahci_platform_suspend(struct device *dev) | ||
467 | { | ||
468 | struct ahci_platform_data *pdata = dev_get_platdata(dev); | ||
469 | struct ata_host *host = dev_get_drvdata(dev); | ||
470 | struct ahci_host_priv *hpriv = host->private_data; | ||
471 | int rc; | ||
472 | |||
473 | rc = ahci_platform_suspend_host(dev); | ||
474 | if (rc) | ||
475 | return rc; | ||
476 | |||
477 | if (pdata && pdata->suspend) { | ||
478 | rc = pdata->suspend(dev); | ||
479 | if (rc) | ||
480 | goto resume_host; | ||
481 | } | ||
482 | |||
483 | ahci_platform_disable_resources(hpriv); | ||
484 | |||
485 | return 0; | ||
486 | |||
487 | resume_host: | ||
488 | ahci_platform_resume_host(dev); | ||
489 | return rc; | ||
490 | } | ||
491 | EXPORT_SYMBOL_GPL(ahci_platform_suspend); | ||
492 | |||
493 | /** | ||
494 | * ahci_platform_resume - Resume an ahci-platform device | ||
495 | * @dev: the platform device to resume | ||
496 | * | ||
497 | * This function enables all the resources of the device followed by | ||
498 | * resuming the host associated with the device. | ||
499 | * | ||
500 | * RETURNS: | ||
501 | * 0 on success otherwise a negative error code | ||
502 | */ | ||
503 | int ahci_platform_resume(struct device *dev) | ||
504 | { | ||
505 | struct ahci_platform_data *pdata = dev_get_platdata(dev); | ||
506 | struct ata_host *host = dev_get_drvdata(dev); | ||
507 | struct ahci_host_priv *hpriv = host->private_data; | ||
508 | int rc; | ||
509 | |||
510 | rc = ahci_platform_enable_resources(hpriv); | ||
511 | if (rc) | ||
512 | return rc; | ||
513 | |||
514 | if (pdata && pdata->resume) { | ||
515 | rc = pdata->resume(dev); | ||
516 | if (rc) | ||
517 | goto disable_resources; | ||
518 | } | ||
519 | |||
520 | rc = ahci_platform_resume_host(dev); | ||
521 | if (rc) | ||
522 | goto disable_resources; | ||
523 | |||
524 | /* We resumed so update PM runtime state */ | ||
525 | pm_runtime_disable(dev); | ||
526 | pm_runtime_set_active(dev); | ||
527 | pm_runtime_enable(dev); | ||
528 | |||
529 | return 0; | ||
530 | |||
531 | disable_resources: | ||
532 | ahci_platform_disable_resources(hpriv); | ||
533 | |||
534 | return rc; | ||
535 | } | ||
536 | EXPORT_SYMBOL_GPL(ahci_platform_resume); | ||
537 | #endif | ||
538 | |||
539 | MODULE_DESCRIPTION("AHCI SATA platform library"); | ||
540 | MODULE_AUTHOR("Anton Vorontsov <avorontsov@ru.mvista.com>"); | ||
541 | MODULE_LICENSE("GPL"); | ||