xref: /openbmc/linux/drivers/rtc/rtc-88pm80x.c (revision 8ef70a5e)
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