blob: d1bdfa3ca1e75b8ce36d25ba46275305361ba4fb [file] [log] [blame]
xjb04a4022021-11-25 15:01:52 +08001// SPDX-License-Identifier: GPL-2.0
2/*
3 * Copyright (c) 2019 MediaTek Inc.
4 * Author: Pierre Lee <pierre.lee@mediatek.com>
5 */
6
7
8#include <linux/clk.h>
9#include <linux/clk-provider.h>
10#include <linux/delay.h>
11#include <linux/device.h>
12#include <linux/kernel.h>
13#include <linux/list.h>
14#include <linux/miscdevice.h>
15#include <linux/module.h>
16#include <linux/of.h>
17#include <linux/of_address.h>
18#include <linux/of_device.h>
19#include <linux/platform_device.h>
20#include <linux/slab.h>
21#include <linux/spinlock.h>
22#include <linux/string.h>
23#include "clk-fhctl.h"
24#include "clk-fhctl-debug.h"
25#include "clk-mtk.h"
26
27
28/************************************************
29 ********** register base addr **********
30 ************************************************/
31#define REG_ADDR(base, x) (void __iomem *)((unsigned long)base + (x))
32
33/************************************************
34 ********** Variable **********
35 ************************************************/
36
37/* spinlock for fhctl */
38static DEFINE_SPINLOCK(fhctl_lock);
39static LIST_HEAD(clk_mt_fhctl_list);
40
41static struct mtk_fhctl *g_p_fhctl;
42
43/*****************************************************************
44 * Global variable operation
45 ****************************************************************/
46static void __set_fhctl(struct mtk_fhctl *pfhctl)
47{
48 g_p_fhctl = pfhctl;
49}
50
51static struct mtk_fhctl *__get_fhctl(void)
52{
53 return g_p_fhctl;
54}
55
56
57/*****************************************************************
58 * OF Info init
59 ****************************************************************/
60static int mtk_fhctl_parse_dt(struct mtk_fhctl *fhctl)
61{
62
63 unsigned int pll_num;
64 struct device *dev;
65 struct device_node *child;
66 struct device_node *node;
67
68 pll_num = fhctl->pll_num;
69 dev = fhctl->dev;
70 node = dev->of_node;
71
72 for_each_child_of_node(node, child) {
73 struct clk_mt_fhctl_pll_data *pll_data;
74 unsigned int id, pll_id, ssc;
75 int err, tbl_size;
76 bool ret;
77
78 /* search for fhctl id */
79 err = of_property_read_u32(child, "mediatek,fh-id", &id);
80 if (err) {
81 dev_info(dev, "miss fh-id property: %s", child->name);
82 return err;
83 }
84
85 if (id >= pll_num) {
86 dev_info(dev, "invalid %s fh-id:%d", child->name, id);
87 return -EINVAL;
88 }
89
90 pll_data = fhctl->fh_tbl[id]->pll_data;
91
92 /* Search for pll type */
93 pll_data->pll_type = FH_PLL_TYPE_FORCE;
94
95 /* Search for freqhopping table */
96 tbl_size = of_property_count_u32_elems(child,
97 "mediatek,fh-tbl");
98 if (tbl_size > 0) {
99 pll_data->hp_tbl_size = tbl_size;
100 pll_data->hp_tbl = devm_kzalloc(dev,
101 sizeof(u32)*tbl_size,
102 GFP_KERNEL);
103
104 if (!pll_data->hp_tbl)
105 return -ENOMEM;
106
107 err = of_property_read_u32_array(child,
108 "mediatek,fh-tbl",
109 pll_data->hp_tbl,
110 tbl_size);
111 if (err) {
112 dev_info(dev, "invalid fh-tbl property of %s",
113 child->name);
114 return err;
115 }
116
117 /* Parse successfully. Set pll type */
118 pll_data->pll_type = FH_PLL_TYPE_GENERAL;
119 }
120
121 /* Search for cpu pll type property */
122 ret = of_property_read_bool(child, "mediatek,fh-cpu-pll");
123 if (ret)
124 pll_data->pll_type = FH_PLL_TYPE_CPU;
125
126 /* Search for fh-pll-id */
127 err = of_property_read_u32(child, "mediatek,fh-pll-id",
128 &pll_id);
129 if (!err)
130 fhctl->idmap[id] = pll_id;
131
132 /* Search for default ssc rate */
133 err = of_property_read_u32(child, "mediatek,fh-ssc-rate",
134 &ssc);
135 if (!err)
136 pll_data->pll_default_ssc_rate = ssc;
137 }
138
139 return 0;
140}
141
142
143static int __add_fh_obj_tbl(struct mtk_fhctl *pfhctl, int posi,
144 struct clk_mt_fhctl *pfh)
145{
146 if (pfhctl == NULL) {
147 pr_info("Error: null pointer pfhctl");
148 return -EFAULT;
149 }
150
151 if (posi >= pfhctl->pll_num)
152 return -EINVAL;
153
154 pfhctl->fh_tbl[posi] = pfh;
155 return 0;
156}
157
158struct clk_mt_fhctl *mtk_fh_get_fh_obj_tbl(struct mtk_fhctl *pfhctl, int posi)
159{
160 int size;
161 struct clk_mt_fhctl *pfh;
162
163 if (pfhctl == NULL) {
164 pr_info("Error: null pointer pfhctl");
165 return ERR_PTR(-EFAULT);
166 }
167
168 size = pfhctl->pll_num;
169
170 if (posi >= size) {
171 dev_info(pfhctl->dev, "Error: size:%d posi:%d", size, posi);
172 return ERR_PTR(-EINVAL);
173 }
174
175 pfh = pfhctl->fh_tbl[posi];
176
177 dev_dbg(pfhctl->dev, "get fh:0x%p pll_id:%d", pfh, posi);
178
179 return pfh;
180}
181EXPORT_SYMBOL(mtk_fh_get_fh_obj_tbl);
182
183/*********************************************************
184 * For clock driver control
185 ********************************************************/
186bool _mtk_fh_set_rate(int pll_id, unsigned long dds, int postdiv)
187{
188 struct mtk_fhctl *fhctl;
189 struct clk_mt_fhctl *fh;
190 int fhctl_pll_id;
191
192 int i, tbl_size, ret;
193
194 pr_debug("check pll_id:0x%x dds:0x%lx", pll_id, dds);
195
196 fhctl = __get_fhctl();
197 if (fhctl == NULL) {
198 pr_info("ERROR: fhctl is not initialized");
199 return false;
200 }
201
202 /* Lookup table */
203 if (unlikely(pll_id < 0))
204 return false;
205
206 fhctl_pll_id = -1;
207 for (i = 0; i < fhctl->pll_num; i++)
208 if (fhctl->idmap[i] == pll_id) {
209 fhctl_pll_id = i;
210 break;
211 }
212
213 if (fhctl_pll_id == -1) {
214 pr_debug("pll not supportted by fhctl");
215 return false;
216 }
217
218 pr_debug("found fhctl_pll_id:%d", fhctl_pll_id);
219
220 fh = mtk_fh_get_fh_obj_tbl(fhctl, fhctl_pll_id);
221
222 if (IS_ERR_OR_NULL(fh))
223 return false;
224
225 if (fh->pll_data->pll_type == FH_PLL_TYPE_NOT_SUPPORT) {
226 pr_info("ERROR: pll not support");
227 return false;
228 }
229
230 if (fh->pll_data->pll_type == FH_PLL_TYPE_CPU) {
231 pr_info("ERROR: CPU hopping not support in AP side");
232 return false;
233 }
234
235 if (fh->pll_data->pll_type == FH_PLL_TYPE_FORCE) {
236 /* Force hopping by FHCTL. */
237 ret = fh->hal_ops->pll_hopping(fh, dds, postdiv);
238 return (ret == 0);
239 }
240
241 /* Look up hopping support table */
242 if (fh->pll_data->hp_tbl == NULL) {
243 pr_info("ERROR: fh->pll_data->hp_tbl NULL!");
244 return false;
245 }
246
247 tbl_size = fh->pll_data->hp_tbl_size;
248 for (i = 0; i < tbl_size; i++) {
249 if (fh->pll_data->hp_tbl[i] == dds) {
250 pr_debug("%s dds:0x%lx by fhctl hopping",
251 fh->pll_data->pll_name, dds);
252 ret = fh->hal_ops->pll_hopping(fh, dds, postdiv);
253 return (ret == 0);
254 }
255 }
256
257 return false;
258}
259
260/****************************************************
261 * CLK FHCTL reg init
262 ***************************************************/
263
264static struct clk_mt_fhctl_regs *__mt_fhctl_fh_regs_init(
265 struct mtk_fhctl *fhctl, unsigned int pll_id)
266{
267 struct clk_mt_fhctl_regs *fh_regs;
268 void *fhctl_base = fhctl->fhctl_base;
269 void *apmixed_base = fhctl->apmixed_base;
270 unsigned int reg_cfg_offs = fhctl->dev_comp->pll_regs[pll_id];
271 unsigned int reg_con0_offs = fhctl->dev_comp->pll_con0_regs[pll_id];
272 unsigned int reg_pcw_offs = fhctl->dev_comp->pll_apmix_pcw_offs;
273
274 fh_regs = devm_kmalloc(fhctl->dev, sizeof(struct clk_mt_fhctl_regs),
275 GFP_KERNEL);
276 if (!fh_regs)
277 return ERR_PTR(-ENOMEM);
278
279 /* fhctl common regs */
280 fh_regs->reg_unitslope_en = REG_ADDR(fhctl_base,
281 fhctl->dev_comp->common_regs[OFFSET_UNITSLOPE_EN]);
282 fh_regs->reg_hp_en = REG_ADDR(fhctl_base,
283 fhctl->dev_comp->common_regs[OFFSET_HP_EN]);
284 fh_regs->reg_clk_con = REG_ADDR(fhctl_base,
285 fhctl->dev_comp->common_regs[OFFSET_CLK_CON]);
286 fh_regs->reg_rst_con = REG_ADDR(fhctl_base,
287 fhctl->dev_comp->common_regs[OFFSET_RST_CON]);
288 fh_regs->reg_slope0 = REG_ADDR(fhctl_base,
289 fhctl->dev_comp->common_regs[OFFSET_SLOPE0]);
290 fh_regs->reg_slope1 = REG_ADDR(fhctl_base,
291 fhctl->dev_comp->common_regs[OFFSET_SLOPE1]);
292
293 /* fhctl PLL specific regs */
294 fh_regs->reg_cfg = REG_ADDR(fhctl_base, reg_cfg_offs);
295 fh_regs->reg_updnlmt = REG_ADDR(fhctl_base, reg_cfg_offs + 0x04);
296 fh_regs->reg_dds = REG_ADDR(fhctl_base, reg_cfg_offs + 0x08);
297 fh_regs->reg_dvfs = REG_ADDR(fhctl_base, reg_cfg_offs + 0xC);
298 fh_regs->reg_mon = REG_ADDR(fhctl_base, reg_cfg_offs + 0x10);
299
300 fh_regs->reg_con0 = REG_ADDR(apmixed_base, reg_con0_offs);
301 fh_regs->reg_con_pcw = REG_ADDR(apmixed_base, reg_con0_offs + reg_pcw_offs);
302
303 return fh_regs;
304}
305
306
307static struct clk_mt_fhctl *clk_register_fhctl_pll(
308 struct device *dev,
309 const struct clk_mt_fhctl_hal_ops *hal_ops,
310 struct clk_mt_fhctl_pll_data *pll_data,
311 struct clk_mt_fhctl_regs *fh_regs)
312{
313 struct clk_mt_fhctl *fh;
314
315 fh = devm_kmalloc(dev, sizeof(struct clk_mt_fhctl), GFP_KERNEL);
316 if (!fh)
317 return ERR_PTR(-ENOMEM);
318
319 fh->pll_data = pll_data;
320 fh->fh_regs = fh_regs;
321 fh->hal_ops = hal_ops;
322 fh->lock = &fhctl_lock;
323
324 return fh;
325}
326
327
328static int mt_fh_plt_drv_probe(struct platform_device *pdev)
329{
330 int i, err, pll_num;
331 int dds_mask_size;
332 struct mtk_fhctl *fhctl;
333 struct resource *res;
334 struct device_node *apmixed_node;
335
336 dev_info(&pdev->dev, "FHCTL driver probe start");
337
338 fhctl = devm_kmalloc(&pdev->dev, sizeof(*fhctl), GFP_KERNEL);
339 if (!fhctl)
340 return -ENOMEM;
341
342 res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
343 fhctl->fhctl_base = devm_ioremap_resource(&pdev->dev, res);
344 if (IS_ERR(fhctl->fhctl_base))
345 return PTR_ERR(fhctl->fhctl_base);
346
347 /* Init APMIXED base address */
348 apmixed_node = of_parse_phandle(pdev->dev.of_node,
349 "mediatek,apmixed", 0);
350 if (!apmixed_node) {
351 dev_info(&pdev->dev, "fhctl: missing mediatek,apmixed node");
352 return -ENODEV;
353 }
354 fhctl->apmixed_base = of_iomap(apmixed_node, 0);
355
356
357 fhctl->dev = &pdev->dev;
358 fhctl->dev_comp = of_device_get_match_data(&pdev->dev);
359 fhctl->pll_num = fhctl->dev_comp->pll_num;
360 dds_mask_size = fhctl->dev_comp->pll_dds_reg_field_size;
361
362 pll_num = fhctl->pll_num;
363
364 fhctl->idmap = devm_kmalloc(&pdev->dev,
365 sizeof(int)*pll_num, GFP_KERNEL);
366 if (!fhctl->idmap)
367 return -ENOMEM;
368
369 platform_set_drvdata(pdev, fhctl);
370
371 fhctl->fh_tbl = devm_kmalloc(&pdev->dev,
372 sizeof(struct clk_mt_fhctl *) * pll_num, GFP_KERNEL);
373 if (!fhctl->fh_tbl)
374 return -ENOMEM;
375
376 /* register all fhctl pll */
377 for (i = 0; i < pll_num; i++) {
378 struct clk_mt_fhctl *fh;
379 struct clk_mt_fhctl_pll_data *pll_data;
380 struct clk_mt_fhctl_regs *fh_regs;
381
382 pll_data = devm_kmalloc(&pdev->dev,
383 sizeof(struct clk_mt_fhctl_pll_data), GFP_KERNEL);
384 if (!pll_data)
385 return -ENOMEM;
386
387 fhctl->idmap[i] = -1;
388
389 /* Set pll data */
390 pll_data->pll_id = i;
391 pll_data->pll_name = fhctl->dev_comp->pll_names[i];
392 pll_data->pll_type = FH_PLL_TYPE_NOT_SUPPORT;
393 pll_data->dds_mask = GENMASK(dds_mask_size-1, 0);
394 pll_data->pll_default_ssc_rate = 0;
395 pll_data->slope0_value =
396 fhctl->dev_comp->pll_slope0_reg_setting;
397 pll_data->slope1_value =
398 fhctl->dev_comp->pll_slope1_reg_setting;
399 pll_data->hp_tbl = NULL;
400 pll_data->hp_tbl_size = 0;
401
402 /* Init fhctl PLL regs */
403 fh_regs = __mt_fhctl_fh_regs_init(fhctl, i);
404 if (IS_ERR_OR_NULL(fh_regs)) {
405 dev_info(&pdev->dev, "ERROR: init fh_regs fail.");
406 return PTR_ERR(fh_regs);
407 }
408
409 fh = clk_register_fhctl_pll(&pdev->dev, &mt_fhctl_hal_ops,
410 pll_data, fh_regs);
411 if (IS_ERR(fh)) {
412 dev_info(&pdev->dev,
413 "register clk fhctl failed: %s",
414 pll_data->pll_name);
415 return PTR_ERR(fh);
416 }
417
418 list_add(&fh->node, &clk_mt_fhctl_list);
419
420 /* Add fh object to table */
421 err = __add_fh_obj_tbl(fhctl, i, fh);
422 if (err)
423 dev_info(&pdev->dev,
424 "add fh object %d to table failed", i);
425
426 }
427
428 fhctl->ipi_ops_p = &ipi_ops;
429 err = fhctl->ipi_ops_p->ipi_init();
430 if (err){
431 dev_info(&pdev->dev, "ERROR fhctl->mt_fh_hal_init() fail");
432 return err;
433 }
434
435 /* Read fhctl setting by device tree */
436 err = mtk_fhctl_parse_dt(fhctl);
437 if (err) {
438 dev_info(&pdev->dev, "ERROR mtk_fhctl_parse_dt fail");
439 return err;
440 }
441
442 for (i = 0; i < pll_num ; i++)
443 fhctl->fh_tbl[i]->hal_ops->pll_init(fhctl->fh_tbl[i]);
444
445 __set_fhctl(fhctl);
446
447 mt_fhctl_init_debugfs(fhctl);
448
449 mtk_fh_set_rate = _mtk_fh_set_rate;
450
451 dev_info(&pdev->dev, "FHCTL Init Done");
452
453 for (i = 0; i < fhctl->pll_num; i++)
454 dev_info(&pdev->dev, "pllid_map[%d]=%d", i, fhctl->idmap[i]);
455
456 fhctl->reg_tr = fhctl->fhctl_base + (uintptr_t)FH_REG_TR;
457 /* show setting value */
458 dev_dbg(&pdev->dev, "pll_num:%d", fhctl->pll_num);
459 dev_dbg(&pdev->dev, "apmixed_base:0x%lx",
460 (unsigned long)fhctl->apmixed_base);
461 dev_dbg(&pdev->dev, "fhctl_base:0x%lx",
462 (unsigned long)fhctl->fhctl_base);
463 dev_dbg(&pdev->dev, "reg_tr:0x%lx",
464 (unsigned long)fhctl->reg_tr);
465 dev_dbg(&pdev->dev, "pll_dds_reg_field_size:%d",
466 fhctl->dev_comp->pll_dds_reg_field_size);
467 dev_dbg(&pdev->dev, "pll_reg_offs:0x%x",
468 fhctl->dev_comp->pll_regs[0]);
469 dev_dbg(&pdev->dev, "pll-type[0]:%d",
470 fhctl->fh_tbl[0]->pll_data->pll_type);
471 dev_dbg(&pdev->dev, "pll_default_enable_ssc[0]:%d",
472 fhctl->fh_tbl[0]->pll_data->pll_default_ssc_rate);
473 dev_dbg(&pdev->dev, "pll_con0_regs[0]:0x%x",
474 fhctl->dev_comp->pll_con0_regs[0]);
475 dev_dbg(&pdev->dev, "pll_slope0_reg_settings[0]:0x%x",
476 fhctl->dev_comp->pll_slope0_reg_setting);
477 dev_dbg(&pdev->dev, "pll_slope1_reg_settings[0]:0x%x",
478 fhctl->dev_comp->pll_slope1_reg_setting);
479 dev_dbg(&pdev->dev, "pll_names[0]:%s",
480 fhctl->fh_tbl[0]->pll_data->pll_name);
481
482 return 0;
483}
484
485static int mt_fh_plt_drv_remove(struct platform_device *pdev)
486{
487 struct mtk_fhctl *fhctl = platform_get_drvdata(pdev);
488
489 mtk_fh_set_rate = NULL;
490 mt_fhctl_exit_debugfs(fhctl);
491 return 0;
492}
493
494static void mt_fh_plt_drv_shutdown(struct platform_device *pdev)
495{
496 struct clk_mt_fhctl *fh;
497
498 dev_dbg(&pdev->dev, "%s!", __func__);
499
500 list_for_each_entry(fh, &clk_mt_fhctl_list, node) {
501 if (fh->pll_data->pll_default_ssc_rate > 0) {
502 dev_info(&pdev->dev, "Shutdown to Disable SSC => PLL:%s ",
503 fh->pll_data->pll_name);
504 fh->hal_ops->pll_ssc_disable(fh);
505 }
506 }
507 dev_dbg(&pdev->dev, "%s Done!", __func__);
508}
509
510
511static const u16 mt_fhctl_regs_v1[] = {
512 [OFFSET_HP_EN] = 0x0,
513 [OFFSET_CLK_CON] = 0x4,
514 [OFFSET_RST_CON] = 0x8,
515 [OFFSET_SLOPE0] = 0xc,
516 [OFFSET_SLOPE1] = 0x10,
517 [OFFSET_FHCTL_DSSC_CFG] = 0x14,
518};
519
520static const u16 mt_fhctl_regs_v2[] = {
521 [OFFSET_UNITSLOPE_EN] = 0x0,
522 [OFFSET_HP_EN] = 0x4,
523 [OFFSET_CLK_CON] = 0x8,
524 [OFFSET_RST_CON] = 0xc,
525 [OFFSET_SLOPE0] = 0x10,
526 [OFFSET_SLOPE1] = 0x14,
527 [OFFSET_FHCTL_DSSC_CFG] = 0x18,
528};
529
530static const u16 mt_fhctl_regs_v3[] = {
531 [OFFSET_UNITSLOPE_EN] = 0x4,
532 [OFFSET_HP_EN] = 0x0,
533 [OFFSET_CLK_CON] = 0x8,
534 [OFFSET_RST_CON] = 0xc,
535 [OFFSET_SLOPE0] = 0x10,
536 [OFFSET_SLOPE1] = 0x14,
537 [OFFSET_FHCTL_DSSC_CFG] = 0x18,
538};
539
540
541
542static const char * const mt6779_pll_names[] = {
543 "armpll_ll", "armpll_bl", "armpll_bb", "ccipll",
544 "mfgpll", "mpll", "mempll", "mainpll",
545 "msdcpll", "mmpll", "adsppll", "tvdpll"};
546
547
548static const u16 mt6779_pll_regs[] = {
549 0x0038, 0x004C, 0xdead, 0x0074,
550 0x088, 0x009C, 0x00B0, 0x00C4,
551 0x00D8, 0x00EC, 0x0100, 0x0114};
552
553static const u16 mt6779_pll_con0_regs[] = {
554 0x200, 0x210, 0x0220, 0x02A0,
555 0x0250, 0x0290, 0xdead, 0x0230,
556 0x0260, 0x0280, 0x02b0, 0x0270};
557
558
559static const struct mtk_fhctl_compatible mt6779_fhctl_compat = {
560 .common_regs = mt_fhctl_regs_v1,
561 .pll_num = 12,
562 .pll_names = mt6779_pll_names,
563 .pll_dds_reg_field_size = 22,
564 .pll_apmix_pcw_offs = CON1_OFFS,
565 .pll_regs = mt6779_pll_regs,
566 .pll_con0_regs = mt6779_pll_con0_regs,
567 .pll_slope0_reg_setting = 0x6003c97,
568 .pll_slope1_reg_setting = 0x6003c97,
569};
570
571
572static const char * const mt6880_pll_names[] = {
573 "armpll_ll", "mainpll", "mpll", "ccipll",
574 "msdcpll", "mfgpll", "mmpll", "net1pll",
575 "net2pll", "wedmcupll", "mempll"};
576
577
578static const u16 mt6880_pll_regs[] = {
579 0x003C, 0x0050, 0x0064, 0x0078,
580 0x008C, 0x00A0, 0x00B4, 0x00C8,
581 0x00DC, 0x00F0, 0x0104};
582
583static const u16 mt6880_pll_con0_regs[] = {
584 0x0204, 0x0404, 0x0604, 0x0218,
585 0x022C, 0x0618, 0x042C, 0x0804,
586 0x0818, 0x082C, 0xdead};
587
588
589static const struct mtk_fhctl_compatible mt6880_fhctl_compat = {
590 .common_regs = mt_fhctl_regs_v3,
591 .pll_num = 11,
592 .pll_names = mt6880_pll_names,
593 .pll_dds_reg_field_size = 22,
594 .pll_apmix_pcw_offs = CON2_OFFS,
595 .pll_regs = mt6880_pll_regs,
596 .pll_con0_regs = mt6880_pll_con0_regs,
597 .pll_slope0_reg_setting = 0x6003c97,
598 .pll_slope1_reg_setting = 0x6003c97,
599};
600
601
602static const struct of_device_id mtk_fhctl_of_match[] = {
603 { .compatible = "mediatek,mt6779-fhctl", .data = &mt6779_fhctl_compat },
604 { .compatible = "mediatek,mt6880-fhctl", .data = &mt6880_fhctl_compat },
605 {}
606};
607MODULE_DEVICE_TABLE(of, mtk_fhctl_of_match);
608
609
610static struct platform_driver fhctl_driver = {
611 .probe = mt_fh_plt_drv_probe,
612 .remove = mt_fh_plt_drv_remove,
613 .shutdown = mt_fh_plt_drv_shutdown,
614 .driver = {
615 .name = "mt-freqhopping",
616 .owner = THIS_MODULE,
617 .of_match_table = of_match_ptr(mtk_fhctl_of_match),
618 },
619};
620
621module_platform_driver(fhctl_driver);
622
623
624
625MODULE_LICENSE("GPL v2");
626MODULE_DESCRIPTION("MediaTek FHCTL Driver");
627MODULE_AUTHOR("Pierre Lee <pierre.lee@mediatek.com>");
628