| xj | b04a402 | 2021-11-25 15:01:52 +0800 | [diff] [blame] | 1 | /* | 
|  | 2 | * Driver for MediaTek SoC based RTC | 
|  | 3 | * | 
|  | 4 | * Copyright (C) 2017 Sean Wang <sean.wang@mediatek.com> | 
|  | 5 | * | 
|  | 6 | * This program is free software; you can redistribute it and/or | 
|  | 7 | * modify it under the terms of the GNU General Public License as | 
|  | 8 | * published by the Free Software Foundation; either version 2 of | 
|  | 9 | * the License, or (at your option) any later version. | 
|  | 10 | * | 
|  | 11 | * This program is distributed in the hope that it will be useful, | 
|  | 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | 
|  | 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | 
|  | 14 | * GNU General Public License for more details. | 
|  | 15 | */ | 
|  | 16 |  | 
|  | 17 | #include <linux/clk.h> | 
|  | 18 | #include <linux/interrupt.h> | 
|  | 19 | #include <linux/module.h> | 
|  | 20 | #include <linux/of_address.h> | 
|  | 21 | #include <linux/of_device.h> | 
|  | 22 | #include <linux/platform_device.h> | 
|  | 23 | #include <linux/rtc.h> | 
|  | 24 |  | 
|  | 25 | #define MTK_RTC_DEV KBUILD_MODNAME | 
|  | 26 |  | 
|  | 27 | #define MTK_RTC_PWRCHK1		0x4 | 
|  | 28 | #define	RTC_PWRCHK1_MAGIC	0xc6 | 
|  | 29 |  | 
|  | 30 | #define MTK_RTC_PWRCHK2		0x8 | 
|  | 31 | #define	RTC_PWRCHK2_MAGIC	0x9a | 
|  | 32 |  | 
|  | 33 | #define MTK_RTC_KEY		0xc | 
|  | 34 | #define	RTC_KEY_MAGIC		0x59 | 
|  | 35 |  | 
|  | 36 | #define MTK_RTC_PROT1		0x10 | 
|  | 37 | #define	RTC_PROT1_MAGIC		0xa3 | 
|  | 38 |  | 
|  | 39 | #define MTK_RTC_PROT2		0x14 | 
|  | 40 | #define	RTC_PROT2_MAGIC		0x57 | 
|  | 41 |  | 
|  | 42 | #define MTK_RTC_PROT3		0x18 | 
|  | 43 | #define	RTC_PROT3_MAGIC		0x67 | 
|  | 44 |  | 
|  | 45 | #define MTK_RTC_PROT4		0x1c | 
|  | 46 | #define	RTC_PROT4_MAGIC		0xd2 | 
|  | 47 |  | 
|  | 48 | #define MTK_RTC_CTL		0x20 | 
|  | 49 | #define	RTC_RC_STOP		BIT(0) | 
|  | 50 |  | 
|  | 51 | #define MTK_RTC_DEBNCE		0x2c | 
|  | 52 | #define	RTC_DEBNCE_MASK		GENMASK(2, 0) | 
|  | 53 |  | 
|  | 54 | #define MTK_RTC_INT		0x30 | 
|  | 55 | #define RTC_INT_AL_STA		BIT(4) | 
|  | 56 |  | 
|  | 57 | /* | 
|  | 58 | * Ranges from 0x40 to 0x78 provide RTC time setup for year, month, | 
|  | 59 | * day of month, day of week, hour, minute and second. | 
|  | 60 | */ | 
|  | 61 | #define MTK_RTC_TREG(_t, _f)	(0x40 + (0x4 * (_f)) + ((_t) * 0x20)) | 
|  | 62 |  | 
|  | 63 | #define MTK_RTC_AL_CTL		0x7c | 
|  | 64 | #define	RTC_AL_EN		BIT(0) | 
|  | 65 | #define	RTC_AL_ALL		GENMASK(7, 0) | 
|  | 66 |  | 
|  | 67 | /* | 
|  | 68 | * The offset is used in the translation for the year between in struct | 
|  | 69 | * rtc_time and in hardware register MTK_RTC_TREG(x,MTK_YEA) | 
|  | 70 | */ | 
|  | 71 | #define MTK_RTC_TM_YR_OFFSET	100 | 
|  | 72 |  | 
|  | 73 | /* | 
|  | 74 | * The lowest value for the valid tm_year. RTC hardware would take incorrectly | 
|  | 75 | * tm_year 100 as not a leap year and thus it is also required being excluded | 
|  | 76 | * from the valid options. | 
|  | 77 | */ | 
|  | 78 | #define MTK_RTC_TM_YR_L		(MTK_RTC_TM_YR_OFFSET + 1) | 
|  | 79 |  | 
|  | 80 | /* | 
|  | 81 | * The most year the RTC can hold is 99 and the next to 99 in year register | 
|  | 82 | * would be wraparound to 0, for MT7622. | 
|  | 83 | */ | 
|  | 84 | #define MTK_RTC_HW_YR_LIMIT	99 | 
|  | 85 |  | 
|  | 86 | /* The highest value for the valid tm_year */ | 
|  | 87 | #define MTK_RTC_TM_YR_H		(MTK_RTC_TM_YR_OFFSET + MTK_RTC_HW_YR_LIMIT) | 
|  | 88 |  | 
|  | 89 | /* Simple macro helps to check whether the hardware supports the tm_year */ | 
|  | 90 | #define MTK_RTC_TM_YR_VALID(_y)	((_y) >= MTK_RTC_TM_YR_L && \ | 
|  | 91 | (_y) <= MTK_RTC_TM_YR_H) | 
|  | 92 |  | 
|  | 93 | /* Types of the function the RTC provides are time counter and alarm. */ | 
|  | 94 | enum { | 
|  | 95 | MTK_TC, | 
|  | 96 | MTK_AL, | 
|  | 97 | }; | 
|  | 98 |  | 
|  | 99 | /* Indexes are used for the pointer to relevant registers in MTK_RTC_TREG */ | 
|  | 100 | enum { | 
|  | 101 | MTK_YEA, | 
|  | 102 | MTK_MON, | 
|  | 103 | MTK_DOM, | 
|  | 104 | MTK_DOW, | 
|  | 105 | MTK_HOU, | 
|  | 106 | MTK_MIN, | 
|  | 107 | MTK_SEC | 
|  | 108 | }; | 
|  | 109 |  | 
|  | 110 | struct mtk_rtc { | 
|  | 111 | struct rtc_device *rtc; | 
|  | 112 | void __iomem *base; | 
|  | 113 | int irq; | 
|  | 114 | struct clk *clk; | 
|  | 115 | }; | 
|  | 116 |  | 
|  | 117 | static void mtk_w32(struct mtk_rtc *rtc, u32 reg, u32 val) | 
|  | 118 | { | 
|  | 119 | writel_relaxed(val, rtc->base + reg); | 
|  | 120 | } | 
|  | 121 |  | 
|  | 122 | static u32 mtk_r32(struct mtk_rtc *rtc, u32 reg) | 
|  | 123 | { | 
|  | 124 | return readl_relaxed(rtc->base + reg); | 
|  | 125 | } | 
|  | 126 |  | 
|  | 127 | static void mtk_rmw(struct mtk_rtc *rtc, u32 reg, u32 mask, u32 set) | 
|  | 128 | { | 
|  | 129 | u32 val; | 
|  | 130 |  | 
|  | 131 | val = mtk_r32(rtc, reg); | 
|  | 132 | val &= ~mask; | 
|  | 133 | val |= set; | 
|  | 134 | mtk_w32(rtc, reg, val); | 
|  | 135 | } | 
|  | 136 |  | 
|  | 137 | static void mtk_set(struct mtk_rtc *rtc, u32 reg, u32 val) | 
|  | 138 | { | 
|  | 139 | mtk_rmw(rtc, reg, 0, val); | 
|  | 140 | } | 
|  | 141 |  | 
|  | 142 | static void mtk_clr(struct mtk_rtc *rtc, u32 reg, u32 val) | 
|  | 143 | { | 
|  | 144 | mtk_rmw(rtc, reg, val, 0); | 
|  | 145 | } | 
|  | 146 |  | 
|  | 147 | static void mtk_rtc_hw_init(struct mtk_rtc *hw) | 
|  | 148 | { | 
|  | 149 | /* The setup of the init sequence is for allowing RTC got to work */ | 
|  | 150 | mtk_w32(hw, MTK_RTC_PWRCHK1, RTC_PWRCHK1_MAGIC); | 
|  | 151 | mtk_w32(hw, MTK_RTC_PWRCHK2, RTC_PWRCHK2_MAGIC); | 
|  | 152 | mtk_w32(hw, MTK_RTC_KEY, RTC_KEY_MAGIC); | 
|  | 153 | mtk_w32(hw, MTK_RTC_PROT1, RTC_PROT1_MAGIC); | 
|  | 154 | mtk_w32(hw, MTK_RTC_PROT2, RTC_PROT2_MAGIC); | 
|  | 155 | mtk_w32(hw, MTK_RTC_PROT3, RTC_PROT3_MAGIC); | 
|  | 156 | mtk_w32(hw, MTK_RTC_PROT4, RTC_PROT4_MAGIC); | 
|  | 157 | mtk_rmw(hw, MTK_RTC_DEBNCE, RTC_DEBNCE_MASK, 0); | 
|  | 158 | mtk_clr(hw, MTK_RTC_CTL, RTC_RC_STOP); | 
|  | 159 | } | 
|  | 160 |  | 
|  | 161 | static void mtk_rtc_get_alarm_or_time(struct mtk_rtc *hw, struct rtc_time *tm, | 
|  | 162 | int time_alarm) | 
|  | 163 | { | 
|  | 164 | u32 year, mon, mday, wday, hour, min, sec; | 
|  | 165 |  | 
|  | 166 | /* | 
|  | 167 | * Read again until the field of the second is not changed which | 
|  | 168 | * ensures all fields in the consistent state. Note that MTK_SEC must | 
|  | 169 | * be read first. In this way, it guarantees the others remain not | 
|  | 170 | * changed when the results for two MTK_SEC consecutive reads are same. | 
|  | 171 | */ | 
|  | 172 | do { | 
|  | 173 | sec = mtk_r32(hw, MTK_RTC_TREG(time_alarm, MTK_SEC)); | 
|  | 174 | min = mtk_r32(hw, MTK_RTC_TREG(time_alarm, MTK_MIN)); | 
|  | 175 | hour = mtk_r32(hw, MTK_RTC_TREG(time_alarm, MTK_HOU)); | 
|  | 176 | wday = mtk_r32(hw, MTK_RTC_TREG(time_alarm, MTK_DOW)); | 
|  | 177 | mday = mtk_r32(hw, MTK_RTC_TREG(time_alarm, MTK_DOM)); | 
|  | 178 | mon = mtk_r32(hw, MTK_RTC_TREG(time_alarm, MTK_MON)); | 
|  | 179 | year = mtk_r32(hw, MTK_RTC_TREG(time_alarm, MTK_YEA)); | 
|  | 180 | } while (sec != mtk_r32(hw, MTK_RTC_TREG(time_alarm, MTK_SEC))); | 
|  | 181 |  | 
|  | 182 | tm->tm_sec  = sec; | 
|  | 183 | tm->tm_min  = min; | 
|  | 184 | tm->tm_hour = hour; | 
|  | 185 | tm->tm_wday = wday; | 
|  | 186 | tm->tm_mday = mday; | 
|  | 187 | tm->tm_mon  = mon - 1; | 
|  | 188 |  | 
|  | 189 | /* Rebase to the absolute year which userspace queries */ | 
|  | 190 | tm->tm_year = year + MTK_RTC_TM_YR_OFFSET; | 
|  | 191 | } | 
|  | 192 |  | 
|  | 193 | static void mtk_rtc_set_alarm_or_time(struct mtk_rtc *hw, struct rtc_time *tm, | 
|  | 194 | int time_alarm) | 
|  | 195 | { | 
|  | 196 | u32 year; | 
|  | 197 |  | 
|  | 198 | /* Rebase to the relative year which RTC hardware requires */ | 
|  | 199 | year = tm->tm_year - MTK_RTC_TM_YR_OFFSET; | 
|  | 200 |  | 
|  | 201 | mtk_w32(hw, MTK_RTC_TREG(time_alarm, MTK_YEA), year); | 
|  | 202 | mtk_w32(hw, MTK_RTC_TREG(time_alarm, MTK_MON), tm->tm_mon + 1); | 
|  | 203 | mtk_w32(hw, MTK_RTC_TREG(time_alarm, MTK_DOW), tm->tm_wday); | 
|  | 204 | mtk_w32(hw, MTK_RTC_TREG(time_alarm, MTK_DOM), tm->tm_mday); | 
|  | 205 | mtk_w32(hw, MTK_RTC_TREG(time_alarm, MTK_HOU), tm->tm_hour); | 
|  | 206 | mtk_w32(hw, MTK_RTC_TREG(time_alarm, MTK_MIN), tm->tm_min); | 
|  | 207 | mtk_w32(hw, MTK_RTC_TREG(time_alarm, MTK_SEC), tm->tm_sec); | 
|  | 208 | } | 
|  | 209 |  | 
|  | 210 | static irqreturn_t mtk_rtc_alarmirq(int irq, void *id) | 
|  | 211 | { | 
|  | 212 | struct mtk_rtc *hw = (struct mtk_rtc *)id; | 
|  | 213 | u32 irq_sta; | 
|  | 214 |  | 
|  | 215 | irq_sta = mtk_r32(hw, MTK_RTC_INT); | 
|  | 216 | if (irq_sta & RTC_INT_AL_STA) { | 
|  | 217 | /* Stop alarm also implicitly disables the alarm interrupt */ | 
|  | 218 | mtk_w32(hw, MTK_RTC_AL_CTL, 0); | 
|  | 219 | rtc_update_irq(hw->rtc, 1, RTC_IRQF | RTC_AF); | 
|  | 220 |  | 
|  | 221 | /* Ack alarm interrupt status */ | 
|  | 222 | mtk_w32(hw, MTK_RTC_INT, RTC_INT_AL_STA); | 
|  | 223 | return IRQ_HANDLED; | 
|  | 224 | } | 
|  | 225 |  | 
|  | 226 | return IRQ_NONE; | 
|  | 227 | } | 
|  | 228 |  | 
|  | 229 | static int mtk_rtc_gettime(struct device *dev, struct rtc_time *tm) | 
|  | 230 | { | 
|  | 231 | struct mtk_rtc *hw = dev_get_drvdata(dev); | 
|  | 232 |  | 
|  | 233 | mtk_rtc_get_alarm_or_time(hw, tm, MTK_TC); | 
|  | 234 |  | 
|  | 235 | return 0; | 
|  | 236 | } | 
|  | 237 |  | 
|  | 238 | static int mtk_rtc_settime(struct device *dev, struct rtc_time *tm) | 
|  | 239 | { | 
|  | 240 | struct mtk_rtc *hw = dev_get_drvdata(dev); | 
|  | 241 |  | 
|  | 242 | if (!MTK_RTC_TM_YR_VALID(tm->tm_year)) | 
|  | 243 | return -EINVAL; | 
|  | 244 |  | 
|  | 245 | /* Stop time counter before setting a new one*/ | 
|  | 246 | mtk_set(hw, MTK_RTC_CTL, RTC_RC_STOP); | 
|  | 247 |  | 
|  | 248 | mtk_rtc_set_alarm_or_time(hw, tm, MTK_TC); | 
|  | 249 |  | 
|  | 250 | /* Restart the time counter */ | 
|  | 251 | mtk_clr(hw, MTK_RTC_CTL, RTC_RC_STOP); | 
|  | 252 |  | 
|  | 253 | return 0; | 
|  | 254 | } | 
|  | 255 |  | 
|  | 256 | static int mtk_rtc_getalarm(struct device *dev, struct rtc_wkalrm *wkalrm) | 
|  | 257 | { | 
|  | 258 | struct mtk_rtc *hw = dev_get_drvdata(dev); | 
|  | 259 | struct rtc_time *alrm_tm = &wkalrm->time; | 
|  | 260 |  | 
|  | 261 | mtk_rtc_get_alarm_or_time(hw, alrm_tm, MTK_AL); | 
|  | 262 |  | 
|  | 263 | wkalrm->enabled = !!(mtk_r32(hw, MTK_RTC_AL_CTL) & RTC_AL_EN); | 
|  | 264 | wkalrm->pending = !!(mtk_r32(hw, MTK_RTC_INT) & RTC_INT_AL_STA); | 
|  | 265 |  | 
|  | 266 | return 0; | 
|  | 267 | } | 
|  | 268 |  | 
|  | 269 | static int mtk_rtc_setalarm(struct device *dev, struct rtc_wkalrm *wkalrm) | 
|  | 270 | { | 
|  | 271 | struct mtk_rtc *hw = dev_get_drvdata(dev); | 
|  | 272 | struct rtc_time *alrm_tm = &wkalrm->time; | 
|  | 273 |  | 
|  | 274 | if (!MTK_RTC_TM_YR_VALID(alrm_tm->tm_year)) | 
|  | 275 | return -EINVAL; | 
|  | 276 |  | 
|  | 277 | /* | 
|  | 278 | * Stop the alarm also implicitly including disables interrupt before | 
|  | 279 | * setting a new one. | 
|  | 280 | */ | 
|  | 281 | mtk_clr(hw, MTK_RTC_AL_CTL, RTC_AL_EN); | 
|  | 282 |  | 
|  | 283 | /* | 
|  | 284 | * Avoid contention between mtk_rtc_setalarm and IRQ handler so that | 
|  | 285 | * disabling the interrupt and awaiting for pending IRQ handler to | 
|  | 286 | * complete. | 
|  | 287 | */ | 
|  | 288 | synchronize_irq(hw->irq); | 
|  | 289 |  | 
|  | 290 | mtk_rtc_set_alarm_or_time(hw, alrm_tm, MTK_AL); | 
|  | 291 |  | 
|  | 292 | /* Restart the alarm with the new setup */ | 
|  | 293 | mtk_w32(hw, MTK_RTC_AL_CTL, RTC_AL_ALL); | 
|  | 294 |  | 
|  | 295 | return 0; | 
|  | 296 | } | 
|  | 297 |  | 
|  | 298 | static const struct rtc_class_ops mtk_rtc_ops = { | 
|  | 299 | .read_time		= mtk_rtc_gettime, | 
|  | 300 | .set_time		= mtk_rtc_settime, | 
|  | 301 | .read_alarm		= mtk_rtc_getalarm, | 
|  | 302 | .set_alarm		= mtk_rtc_setalarm, | 
|  | 303 | }; | 
|  | 304 |  | 
|  | 305 | static const struct of_device_id mtk_rtc_match[] = { | 
|  | 306 | { .compatible = "mediatek,mt7622-rtc" }, | 
|  | 307 | { .compatible = "mediatek,soc-rtc" }, | 
|  | 308 | {}, | 
|  | 309 | }; | 
|  | 310 | MODULE_DEVICE_TABLE(of, mtk_rtc_match); | 
|  | 311 |  | 
|  | 312 | static int mtk_rtc_probe(struct platform_device *pdev) | 
|  | 313 | { | 
|  | 314 | struct mtk_rtc *hw; | 
|  | 315 | struct resource *res; | 
|  | 316 | int ret; | 
|  | 317 |  | 
|  | 318 | hw = devm_kzalloc(&pdev->dev, sizeof(*hw), GFP_KERNEL); | 
|  | 319 | if (!hw) | 
|  | 320 | return -ENOMEM; | 
|  | 321 |  | 
|  | 322 | platform_set_drvdata(pdev, hw); | 
|  | 323 |  | 
|  | 324 | res = platform_get_resource(pdev, IORESOURCE_MEM, 0); | 
|  | 325 | hw->base = devm_ioremap_resource(&pdev->dev, res); | 
|  | 326 | if (IS_ERR(hw->base)) | 
|  | 327 | return PTR_ERR(hw->base); | 
|  | 328 |  | 
|  | 329 | hw->clk = devm_clk_get(&pdev->dev, "rtc"); | 
|  | 330 | if (IS_ERR(hw->clk)) { | 
|  | 331 | dev_err(&pdev->dev, "No clock\n"); | 
|  | 332 | return PTR_ERR(hw->clk); | 
|  | 333 | } | 
|  | 334 |  | 
|  | 335 | ret = clk_prepare_enable(hw->clk); | 
|  | 336 | if (ret) | 
|  | 337 | return ret; | 
|  | 338 |  | 
|  | 339 | hw->irq = platform_get_irq(pdev, 0); | 
|  | 340 | if (hw->irq < 0) { | 
|  | 341 | dev_err(&pdev->dev, "No IRQ resource\n"); | 
|  | 342 | ret = hw->irq; | 
|  | 343 | goto err; | 
|  | 344 | } | 
|  | 345 |  | 
|  | 346 | ret = devm_request_irq(&pdev->dev, hw->irq, mtk_rtc_alarmirq, | 
|  | 347 | 0, dev_name(&pdev->dev), hw); | 
|  | 348 | if (ret) { | 
|  | 349 | dev_err(&pdev->dev, "Can't request IRQ\n"); | 
|  | 350 | goto err; | 
|  | 351 | } | 
|  | 352 |  | 
|  | 353 | mtk_rtc_hw_init(hw); | 
|  | 354 |  | 
|  | 355 | device_init_wakeup(&pdev->dev, true); | 
|  | 356 |  | 
|  | 357 | hw->rtc = devm_rtc_device_register(&pdev->dev, pdev->name, | 
|  | 358 | &mtk_rtc_ops, THIS_MODULE); | 
|  | 359 | if (IS_ERR(hw->rtc)) { | 
|  | 360 | ret = PTR_ERR(hw->rtc); | 
|  | 361 | dev_err(&pdev->dev, "Unable to register device\n"); | 
|  | 362 | goto err; | 
|  | 363 | } | 
|  | 364 |  | 
|  | 365 | return 0; | 
|  | 366 | err: | 
|  | 367 | clk_disable_unprepare(hw->clk); | 
|  | 368 |  | 
|  | 369 | return ret; | 
|  | 370 | } | 
|  | 371 |  | 
|  | 372 | static int mtk_rtc_remove(struct platform_device *pdev) | 
|  | 373 | { | 
|  | 374 | struct mtk_rtc *hw = platform_get_drvdata(pdev); | 
|  | 375 |  | 
|  | 376 | clk_disable_unprepare(hw->clk); | 
|  | 377 |  | 
|  | 378 | return 0; | 
|  | 379 | } | 
|  | 380 |  | 
|  | 381 | #ifdef CONFIG_PM_SLEEP | 
|  | 382 | static int mtk_rtc_suspend(struct device *dev) | 
|  | 383 | { | 
|  | 384 | struct mtk_rtc *hw = dev_get_drvdata(dev); | 
|  | 385 |  | 
|  | 386 | if (device_may_wakeup(dev)) | 
|  | 387 | enable_irq_wake(hw->irq); | 
|  | 388 |  | 
|  | 389 | return 0; | 
|  | 390 | } | 
|  | 391 |  | 
|  | 392 | static int mtk_rtc_resume(struct device *dev) | 
|  | 393 | { | 
|  | 394 | struct mtk_rtc *hw = dev_get_drvdata(dev); | 
|  | 395 |  | 
|  | 396 | if (device_may_wakeup(dev)) | 
|  | 397 | disable_irq_wake(hw->irq); | 
|  | 398 |  | 
|  | 399 | return 0; | 
|  | 400 | } | 
|  | 401 |  | 
|  | 402 | static SIMPLE_DEV_PM_OPS(mtk_rtc_pm_ops, mtk_rtc_suspend, mtk_rtc_resume); | 
|  | 403 |  | 
|  | 404 | #define MTK_RTC_PM_OPS (&mtk_rtc_pm_ops) | 
|  | 405 | #else	/* CONFIG_PM */ | 
|  | 406 | #define MTK_RTC_PM_OPS NULL | 
|  | 407 | #endif	/* CONFIG_PM */ | 
|  | 408 |  | 
|  | 409 | static struct platform_driver mtk_rtc_driver = { | 
|  | 410 | .probe	= mtk_rtc_probe, | 
|  | 411 | .remove	= mtk_rtc_remove, | 
|  | 412 | .driver = { | 
|  | 413 | .name = MTK_RTC_DEV, | 
|  | 414 | .of_match_table = mtk_rtc_match, | 
|  | 415 | .pm = MTK_RTC_PM_OPS, | 
|  | 416 | }, | 
|  | 417 | }; | 
|  | 418 |  | 
|  | 419 | module_platform_driver(mtk_rtc_driver); | 
|  | 420 |  | 
|  | 421 | MODULE_DESCRIPTION("MediaTek SoC based RTC Driver"); | 
|  | 422 | MODULE_AUTHOR("Sean Wang <sean.wang@mediatek.com>"); | 
|  | 423 | MODULE_LICENSE("GPL"); |