18bc9630aSAlexandre Belloni // SPDX-License-Identifier: GPL-2.0
22985c29cSQiao Zhou /*
32985c29cSQiao Zhou * Real Time Clock driver for Marvell 88PM80x PMIC
42985c29cSQiao Zhou *
52985c29cSQiao Zhou * Copyright (c) 2012 Marvell International Ltd.
62985c29cSQiao Zhou * Wenzeng Chen<wzch@marvell.com>
72985c29cSQiao Zhou * Qiao Zhou <zhouqiao@marvell.com>
82985c29cSQiao Zhou */
92985c29cSQiao Zhou
102985c29cSQiao Zhou #include <linux/kernel.h>
112985c29cSQiao Zhou #include <linux/module.h>
122985c29cSQiao Zhou #include <linux/slab.h>
132985c29cSQiao Zhou #include <linux/regmap.h>
142985c29cSQiao Zhou #include <linux/mfd/core.h>
152985c29cSQiao Zhou #include <linux/mfd/88pm80x.h>
162985c29cSQiao Zhou #include <linux/rtc.h>
172985c29cSQiao Zhou
182985c29cSQiao Zhou #define PM800_RTC_COUNTER1 (0xD1)
192985c29cSQiao Zhou #define PM800_RTC_COUNTER2 (0xD2)
202985c29cSQiao Zhou #define PM800_RTC_COUNTER3 (0xD3)
212985c29cSQiao Zhou #define PM800_RTC_COUNTER4 (0xD4)
222985c29cSQiao Zhou #define PM800_RTC_EXPIRE1_1 (0xD5)
232985c29cSQiao Zhou #define PM800_RTC_EXPIRE1_2 (0xD6)
242985c29cSQiao Zhou #define PM800_RTC_EXPIRE1_3 (0xD7)
252985c29cSQiao Zhou #define PM800_RTC_EXPIRE1_4 (0xD8)
262985c29cSQiao Zhou #define PM800_RTC_TRIM1 (0xD9)
272985c29cSQiao Zhou #define PM800_RTC_TRIM2 (0xDA)
282985c29cSQiao Zhou #define PM800_RTC_TRIM3 (0xDB)
292985c29cSQiao Zhou #define PM800_RTC_TRIM4 (0xDC)
302985c29cSQiao Zhou #define PM800_RTC_EXPIRE2_1 (0xDD)
312985c29cSQiao Zhou #define PM800_RTC_EXPIRE2_2 (0xDE)
322985c29cSQiao Zhou #define PM800_RTC_EXPIRE2_3 (0xDF)
332985c29cSQiao Zhou #define PM800_RTC_EXPIRE2_4 (0xE0)
342985c29cSQiao Zhou
352985c29cSQiao Zhou #define PM800_POWER_DOWN_LOG1 (0xE5)
362985c29cSQiao Zhou #define PM800_POWER_DOWN_LOG2 (0xE6)
372985c29cSQiao Zhou
382985c29cSQiao Zhou struct pm80x_rtc_info {
392985c29cSQiao Zhou struct pm80x_chip *chip;
402985c29cSQiao Zhou struct regmap *map;
412985c29cSQiao Zhou struct rtc_device *rtc_dev;
422985c29cSQiao Zhou struct device *dev;
432985c29cSQiao Zhou
442985c29cSQiao Zhou int irq;
452985c29cSQiao Zhou };
462985c29cSQiao Zhou
rtc_update_handler(int irq,void * data)472985c29cSQiao Zhou static irqreturn_t rtc_update_handler(int irq, void *data)
482985c29cSQiao Zhou {
492985c29cSQiao Zhou struct pm80x_rtc_info *info = (struct pm80x_rtc_info *)data;
502985c29cSQiao Zhou int mask;
512985c29cSQiao Zhou
522985c29cSQiao Zhou mask = PM800_ALARM | PM800_ALARM_WAKEUP;
532985c29cSQiao Zhou regmap_update_bits(info->map, PM800_RTC_CONTROL, mask | PM800_ALARM1_EN,
542985c29cSQiao Zhou mask);
552985c29cSQiao Zhou rtc_update_irq(info->rtc_dev, 1, RTC_AF);
562985c29cSQiao Zhou return IRQ_HANDLED;
572985c29cSQiao Zhou }
582985c29cSQiao Zhou
pm80x_rtc_alarm_irq_enable(struct device * dev,unsigned int enabled)592985c29cSQiao Zhou static int pm80x_rtc_alarm_irq_enable(struct device *dev, unsigned int enabled)
602985c29cSQiao Zhou {
612985c29cSQiao Zhou struct pm80x_rtc_info *info = dev_get_drvdata(dev);
622985c29cSQiao Zhou
632985c29cSQiao Zhou if (enabled)
642985c29cSQiao Zhou regmap_update_bits(info->map, PM800_RTC_CONTROL,
652985c29cSQiao Zhou PM800_ALARM1_EN, PM800_ALARM1_EN);
662985c29cSQiao Zhou else
672985c29cSQiao Zhou regmap_update_bits(info->map, PM800_RTC_CONTROL,
682985c29cSQiao Zhou PM800_ALARM1_EN, 0);
692985c29cSQiao Zhou return 0;
702985c29cSQiao Zhou }
712985c29cSQiao Zhou
722985c29cSQiao Zhou /*
732985c29cSQiao Zhou * Calculate the next alarm time given the requested alarm time mask
742985c29cSQiao Zhou * and the current time.
752985c29cSQiao Zhou */
rtc_next_alarm_time(struct rtc_time * next,struct rtc_time * now,struct rtc_time * alrm)762985c29cSQiao Zhou static void rtc_next_alarm_time(struct rtc_time *next, struct rtc_time *now,
772985c29cSQiao Zhou struct rtc_time *alrm)
782985c29cSQiao Zhou {
792985c29cSQiao Zhou unsigned long next_time;
802985c29cSQiao Zhou unsigned long now_time;
812985c29cSQiao Zhou
822985c29cSQiao Zhou next->tm_year = now->tm_year;
832985c29cSQiao Zhou next->tm_mon = now->tm_mon;
842985c29cSQiao Zhou next->tm_mday = now->tm_mday;
852985c29cSQiao Zhou next->tm_hour = alrm->tm_hour;
862985c29cSQiao Zhou next->tm_min = alrm->tm_min;
872985c29cSQiao Zhou next->tm_sec = alrm->tm_sec;
882985c29cSQiao Zhou
8902f3712fSAlexandre Belloni now_time = rtc_tm_to_time64(now);
9002f3712fSAlexandre Belloni next_time = rtc_tm_to_time64(next);
912985c29cSQiao Zhou
922985c29cSQiao Zhou if (next_time < now_time) {
932985c29cSQiao Zhou /* Advance one day */
942985c29cSQiao Zhou next_time += 60 * 60 * 24;
9502f3712fSAlexandre Belloni rtc_time64_to_tm(next_time, next);
962985c29cSQiao Zhou }
972985c29cSQiao Zhou }
982985c29cSQiao Zhou
pm80x_rtc_read_time(struct device * dev,struct rtc_time * tm)992985c29cSQiao Zhou static int pm80x_rtc_read_time(struct device *dev, struct rtc_time *tm)
1002985c29cSQiao Zhou {
1012985c29cSQiao Zhou struct pm80x_rtc_info *info = dev_get_drvdata(dev);
1022985c29cSQiao Zhou unsigned char buf[4];
1032985c29cSQiao Zhou unsigned long ticks, base, data;
1042985c29cSQiao Zhou regmap_raw_read(info->map, PM800_RTC_EXPIRE2_1, buf, 4);
105fb0b3225SColin Ian King base = ((unsigned long)buf[3] << 24) | (buf[2] << 16) |
106fb0b3225SColin Ian King (buf[1] << 8) | buf[0];
1072985c29cSQiao Zhou dev_dbg(info->dev, "%x-%x-%x-%x\n", buf[0], buf[1], buf[2], buf[3]);
1082985c29cSQiao Zhou
1092985c29cSQiao Zhou /* load 32-bit read-only counter */
1102985c29cSQiao Zhou regmap_raw_read(info->map, PM800_RTC_COUNTER1, buf, 4);
111fb0b3225SColin Ian King data = ((unsigned long)buf[3] << 24) | (buf[2] << 16) |
112fb0b3225SColin Ian King (buf[1] << 8) | buf[0];
1132985c29cSQiao Zhou ticks = base + data;
1142985c29cSQiao Zhou dev_dbg(info->dev, "get base:0x%lx, RO count:0x%lx, ticks:0x%lx\n",
1152985c29cSQiao Zhou base, data, ticks);
11602f3712fSAlexandre Belloni rtc_time64_to_tm(ticks, tm);
1172985c29cSQiao Zhou return 0;
1182985c29cSQiao Zhou }
1192985c29cSQiao Zhou
pm80x_rtc_set_time(struct device * dev,struct rtc_time * tm)1202985c29cSQiao Zhou static int pm80x_rtc_set_time(struct device *dev, struct rtc_time *tm)
1212985c29cSQiao Zhou {
1222985c29cSQiao Zhou struct pm80x_rtc_info *info = dev_get_drvdata(dev);
1232985c29cSQiao Zhou unsigned char buf[4];
1242985c29cSQiao Zhou unsigned long ticks, base, data;
12539ba6942SAlexandre Belloni
12602f3712fSAlexandre Belloni ticks = rtc_tm_to_time64(tm);
1272985c29cSQiao Zhou
1282985c29cSQiao Zhou /* load 32-bit read-only counter */
1292985c29cSQiao Zhou regmap_raw_read(info->map, PM800_RTC_COUNTER1, buf, 4);
130fb0b3225SColin Ian King data = ((unsigned long)buf[3] << 24) | (buf[2] << 16) |
131fb0b3225SColin Ian King (buf[1] << 8) | buf[0];
1322985c29cSQiao Zhou base = ticks - data;
1332985c29cSQiao Zhou dev_dbg(info->dev, "set base:0x%lx, RO count:0x%lx, ticks:0x%lx\n",
1342985c29cSQiao Zhou base, data, ticks);
1352985c29cSQiao Zhou buf[0] = base & 0xFF;
1362985c29cSQiao Zhou buf[1] = (base >> 8) & 0xFF;
1372985c29cSQiao Zhou buf[2] = (base >> 16) & 0xFF;
1382985c29cSQiao Zhou buf[3] = (base >> 24) & 0xFF;
1392985c29cSQiao Zhou regmap_raw_write(info->map, PM800_RTC_EXPIRE2_1, buf, 4);
1402985c29cSQiao Zhou
1412985c29cSQiao Zhou return 0;
1422985c29cSQiao Zhou }
1432985c29cSQiao Zhou
pm80x_rtc_read_alarm(struct device * dev,struct rtc_wkalrm * alrm)1442985c29cSQiao Zhou static int pm80x_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alrm)
1452985c29cSQiao Zhou {
1462985c29cSQiao Zhou struct pm80x_rtc_info *info = dev_get_drvdata(dev);
1472985c29cSQiao Zhou unsigned char buf[4];
1482985c29cSQiao Zhou unsigned long ticks, base, data;
1492985c29cSQiao Zhou int ret;
1502985c29cSQiao Zhou
1512985c29cSQiao Zhou regmap_raw_read(info->map, PM800_RTC_EXPIRE2_1, buf, 4);
152fb0b3225SColin Ian King base = ((unsigned long)buf[3] << 24) | (buf[2] << 16) |
153fb0b3225SColin Ian King (buf[1] << 8) | buf[0];
1542985c29cSQiao Zhou dev_dbg(info->dev, "%x-%x-%x-%x\n", buf[0], buf[1], buf[2], buf[3]);
1552985c29cSQiao Zhou
1562985c29cSQiao Zhou regmap_raw_read(info->map, PM800_RTC_EXPIRE1_1, buf, 4);
157fb0b3225SColin Ian King data = ((unsigned long)buf[3] << 24) | (buf[2] << 16) |
158fb0b3225SColin Ian King (buf[1] << 8) | buf[0];
1592985c29cSQiao Zhou ticks = base + data;
1602985c29cSQiao Zhou dev_dbg(info->dev, "get base:0x%lx, RO count:0x%lx, ticks:0x%lx\n",
1612985c29cSQiao Zhou base, data, ticks);
1622985c29cSQiao Zhou
16302f3712fSAlexandre Belloni rtc_time64_to_tm(ticks, &alrm->time);
1642985c29cSQiao Zhou regmap_read(info->map, PM800_RTC_CONTROL, &ret);
1652985c29cSQiao Zhou alrm->enabled = (ret & PM800_ALARM1_EN) ? 1 : 0;
1662985c29cSQiao Zhou alrm->pending = (ret & (PM800_ALARM | PM800_ALARM_WAKEUP)) ? 1 : 0;
1672985c29cSQiao Zhou return 0;
1682985c29cSQiao Zhou }
1692985c29cSQiao Zhou
pm80x_rtc_set_alarm(struct device * dev,struct rtc_wkalrm * alrm)1702985c29cSQiao Zhou static int pm80x_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alrm)
1712985c29cSQiao Zhou {
1722985c29cSQiao Zhou struct pm80x_rtc_info *info = dev_get_drvdata(dev);
1732985c29cSQiao Zhou struct rtc_time now_tm, alarm_tm;
1742985c29cSQiao Zhou unsigned long ticks, base, data;
1752985c29cSQiao Zhou unsigned char buf[4];
1762985c29cSQiao Zhou int mask;
1772985c29cSQiao Zhou
1782985c29cSQiao Zhou regmap_update_bits(info->map, PM800_RTC_CONTROL, PM800_ALARM1_EN, 0);
1792985c29cSQiao Zhou
1802985c29cSQiao Zhou regmap_raw_read(info->map, PM800_RTC_EXPIRE2_1, buf, 4);
181fb0b3225SColin Ian King base = ((unsigned long)buf[3] << 24) | (buf[2] << 16) |
182fb0b3225SColin Ian King (buf[1] << 8) | buf[0];
1832985c29cSQiao Zhou dev_dbg(info->dev, "%x-%x-%x-%x\n", buf[0], buf[1], buf[2], buf[3]);
1842985c29cSQiao Zhou
1852985c29cSQiao Zhou /* load 32-bit read-only counter */
1862985c29cSQiao Zhou regmap_raw_read(info->map, PM800_RTC_COUNTER1, buf, 4);
187fb0b3225SColin Ian King data = ((unsigned long)buf[3] << 24) | (buf[2] << 16) |
188fb0b3225SColin Ian King (buf[1] << 8) | buf[0];
1892985c29cSQiao Zhou ticks = base + data;
1902985c29cSQiao Zhou dev_dbg(info->dev, "get base:0x%lx, RO count:0x%lx, ticks:0x%lx\n",
1912985c29cSQiao Zhou base, data, ticks);
1922985c29cSQiao Zhou
19302f3712fSAlexandre Belloni rtc_time64_to_tm(ticks, &now_tm);
1942985c29cSQiao Zhou dev_dbg(info->dev, "%s, now time : %lu\n", __func__, ticks);
1952985c29cSQiao Zhou rtc_next_alarm_time(&alarm_tm, &now_tm, &alrm->time);
1962985c29cSQiao Zhou /* get new ticks for alarm in 24 hours */
19702f3712fSAlexandre Belloni ticks = rtc_tm_to_time64(&alarm_tm);
1982985c29cSQiao Zhou dev_dbg(info->dev, "%s, alarm time: %lu\n", __func__, ticks);
1992985c29cSQiao Zhou data = ticks - base;
2002985c29cSQiao Zhou
2012985c29cSQiao Zhou buf[0] = data & 0xff;
2022985c29cSQiao Zhou buf[1] = (data >> 8) & 0xff;
2032985c29cSQiao Zhou buf[2] = (data >> 16) & 0xff;
2042985c29cSQiao Zhou buf[3] = (data >> 24) & 0xff;
2052985c29cSQiao Zhou regmap_raw_write(info->map, PM800_RTC_EXPIRE1_1, buf, 4);
2062985c29cSQiao Zhou if (alrm->enabled) {
2072985c29cSQiao Zhou mask = PM800_ALARM | PM800_ALARM_WAKEUP | PM800_ALARM1_EN;
2082985c29cSQiao Zhou regmap_update_bits(info->map, PM800_RTC_CONTROL, mask, mask);
2092985c29cSQiao Zhou } else {
2102985c29cSQiao Zhou mask = PM800_ALARM | PM800_ALARM_WAKEUP | PM800_ALARM1_EN;
2112985c29cSQiao Zhou regmap_update_bits(info->map, PM800_RTC_CONTROL, mask,
2122985c29cSQiao Zhou PM800_ALARM | PM800_ALARM_WAKEUP);
2132985c29cSQiao Zhou }
2142985c29cSQiao Zhou return 0;
2152985c29cSQiao Zhou }
2162985c29cSQiao Zhou
2172985c29cSQiao Zhou static const struct rtc_class_ops pm80x_rtc_ops = {
2182985c29cSQiao Zhou .read_time = pm80x_rtc_read_time,
2192985c29cSQiao Zhou .set_time = pm80x_rtc_set_time,
2202985c29cSQiao Zhou .read_alarm = pm80x_rtc_read_alarm,
2212985c29cSQiao Zhou .set_alarm = pm80x_rtc_set_alarm,
2222985c29cSQiao Zhou .alarm_irq_enable = pm80x_rtc_alarm_irq_enable,
2232985c29cSQiao Zhou };
2242985c29cSQiao Zhou
225569b05a3SJingoo Han #ifdef CONFIG_PM_SLEEP
pm80x_rtc_suspend(struct device * dev)2262985c29cSQiao Zhou static int pm80x_rtc_suspend(struct device *dev)
2272985c29cSQiao Zhou {
2282985c29cSQiao Zhou return pm80x_dev_suspend(dev);
2292985c29cSQiao Zhou }
2302985c29cSQiao Zhou
pm80x_rtc_resume(struct device * dev)2312985c29cSQiao Zhou static int pm80x_rtc_resume(struct device *dev)
2322985c29cSQiao Zhou {
2332985c29cSQiao Zhou return pm80x_dev_resume(dev);
2342985c29cSQiao Zhou }
2352985c29cSQiao Zhou #endif
2362985c29cSQiao Zhou
2372985c29cSQiao Zhou static SIMPLE_DEV_PM_OPS(pm80x_rtc_pm_ops, pm80x_rtc_suspend, pm80x_rtc_resume);
2382985c29cSQiao Zhou
pm80x_rtc_probe(struct platform_device * pdev)2395a167f45SGreg Kroah-Hartman static int pm80x_rtc_probe(struct platform_device *pdev)
2402985c29cSQiao Zhou {
2412985c29cSQiao Zhou struct pm80x_chip *chip = dev_get_drvdata(pdev->dev.parent);
2424ab82103SVaibhav Hiremath struct pm80x_rtc_pdata *pdata = dev_get_platdata(&pdev->dev);
2432985c29cSQiao Zhou struct pm80x_rtc_info *info;
2444ab82103SVaibhav Hiremath struct device_node *node = pdev->dev.of_node;
2452985c29cSQiao Zhou int ret;
2462985c29cSQiao Zhou
2474ab82103SVaibhav Hiremath if (!pdata && !node) {
2484ab82103SVaibhav Hiremath dev_err(&pdev->dev,
2494ab82103SVaibhav Hiremath "pm80x-rtc requires platform data or of_node\n");
2504ab82103SVaibhav Hiremath return -EINVAL;
2514ab82103SVaibhav Hiremath }
2524ab82103SVaibhav Hiremath
2534ab82103SVaibhav Hiremath if (!pdata) {
2544ab82103SVaibhav Hiremath pdata = devm_kzalloc(&pdev->dev, sizeof(*pdata), GFP_KERNEL);
2554ab82103SVaibhav Hiremath if (!pdata) {
2564ab82103SVaibhav Hiremath dev_err(&pdev->dev, "failed to allocate memory\n");
2574ab82103SVaibhav Hiremath return -ENOMEM;
2584ab82103SVaibhav Hiremath }
2594ab82103SVaibhav Hiremath }
2602985c29cSQiao Zhou
2612985c29cSQiao Zhou info =
2622985c29cSQiao Zhou devm_kzalloc(&pdev->dev, sizeof(struct pm80x_rtc_info), GFP_KERNEL);
2632985c29cSQiao Zhou if (!info)
2642985c29cSQiao Zhou return -ENOMEM;
2652985c29cSQiao Zhou info->irq = platform_get_irq(pdev, 0);
2662985c29cSQiao Zhou if (info->irq < 0) {
2672985c29cSQiao Zhou ret = -EINVAL;
2682985c29cSQiao Zhou goto out;
2692985c29cSQiao Zhou }
2702985c29cSQiao Zhou
2712985c29cSQiao Zhou info->chip = chip;
2722985c29cSQiao Zhou info->map = chip->regmap;
2732985c29cSQiao Zhou if (!info->map) {
2742985c29cSQiao Zhou dev_err(&pdev->dev, "no regmap!\n");
2752985c29cSQiao Zhou ret = -EINVAL;
2762985c29cSQiao Zhou goto out;
2772985c29cSQiao Zhou }
2782985c29cSQiao Zhou
2792985c29cSQiao Zhou info->dev = &pdev->dev;
2802985c29cSQiao Zhou dev_set_drvdata(&pdev->dev, info);
2812985c29cSQiao Zhou
282661eb89aSAlexandre Belloni info->rtc_dev = devm_rtc_allocate_device(&pdev->dev);
283661eb89aSAlexandre Belloni if (IS_ERR(info->rtc_dev))
284661eb89aSAlexandre Belloni return PTR_ERR(info->rtc_dev);
285661eb89aSAlexandre Belloni
2862985c29cSQiao Zhou ret = pm80x_request_irq(chip, info->irq, rtc_update_handler,
2872985c29cSQiao Zhou IRQF_ONESHOT, "rtc", info);
2882985c29cSQiao Zhou if (ret < 0) {
2892985c29cSQiao Zhou dev_err(chip->dev, "Failed to request IRQ: #%d: %d\n",
2902985c29cSQiao Zhou info->irq, ret);
2912985c29cSQiao Zhou goto out;
2922985c29cSQiao Zhou }
2932985c29cSQiao Zhou
294661eb89aSAlexandre Belloni info->rtc_dev->ops = &pm80x_rtc_ops;
29539ba6942SAlexandre Belloni info->rtc_dev->range_max = U32_MAX;
296661eb89aSAlexandre Belloni
297fdcfd854SBartosz Golaszewski ret = devm_rtc_register_device(info->rtc_dev);
29844c638ceSAlexandre Belloni if (ret)
2992985c29cSQiao Zhou goto out_rtc;
30044c638ceSAlexandre Belloni
3012985c29cSQiao Zhou /*
3022985c29cSQiao Zhou * enable internal XO instead of internal 3.25MHz clock since it can
3032985c29cSQiao Zhou * free running in PMIC power-down state.
3042985c29cSQiao Zhou */
3052985c29cSQiao Zhou regmap_update_bits(info->map, PM800_RTC_CONTROL, PM800_RTC1_USE_XO,
3062985c29cSQiao Zhou PM800_RTC1_USE_XO);
3072985c29cSQiao Zhou
3084ab82103SVaibhav Hiremath /* remember whether this power up is caused by PMIC RTC or not */
3092985c29cSQiao Zhou info->rtc_dev->dev.platform_data = &pdata->rtc_wakeup;
3102985c29cSQiao Zhou
3112985c29cSQiao Zhou device_init_wakeup(&pdev->dev, 1);
3122985c29cSQiao Zhou
3132985c29cSQiao Zhou return 0;
3142985c29cSQiao Zhou out_rtc:
3152985c29cSQiao Zhou pm80x_free_irq(chip, info->irq, info);
3162985c29cSQiao Zhou out:
3172985c29cSQiao Zhou return ret;
3182985c29cSQiao Zhou }
3192985c29cSQiao Zhou
pm80x_rtc_remove(struct platform_device * pdev)320*8ef70a5eSUwe Kleine-König static void pm80x_rtc_remove(struct platform_device *pdev)
3212985c29cSQiao Zhou {
3222985c29cSQiao Zhou struct pm80x_rtc_info *info = platform_get_drvdata(pdev);
3232985c29cSQiao Zhou pm80x_free_irq(info->chip, info->irq, info);
3242985c29cSQiao Zhou }
3252985c29cSQiao Zhou
3262985c29cSQiao Zhou static struct platform_driver pm80x_rtc_driver = {
3272985c29cSQiao Zhou .driver = {
3282985c29cSQiao Zhou .name = "88pm80x-rtc",
3292985c29cSQiao Zhou .pm = &pm80x_rtc_pm_ops,
3302985c29cSQiao Zhou },
3312985c29cSQiao Zhou .probe = pm80x_rtc_probe,
332*8ef70a5eSUwe Kleine-König .remove_new = pm80x_rtc_remove,
3332985c29cSQiao Zhou };
3342985c29cSQiao Zhou
3352985c29cSQiao Zhou module_platform_driver(pm80x_rtc_driver);
3362985c29cSQiao Zhou
3372985c29cSQiao Zhou MODULE_LICENSE("GPL");
3382985c29cSQiao Zhou MODULE_DESCRIPTION("Marvell 88PM80x RTC driver");
3392985c29cSQiao Zhou MODULE_AUTHOR("Qiao Zhou <zhouqiao@marvell.com>");
3402985c29cSQiao Zhou MODULE_ALIAS("platform:88pm80x-rtc");
341