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 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 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 */ 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 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 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 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 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 2262985c29cSQiao Zhou static int pm80x_rtc_suspend(struct device *dev) 2272985c29cSQiao Zhou { 2282985c29cSQiao Zhou return pm80x_dev_suspend(dev); 2292985c29cSQiao Zhou } 2302985c29cSQiao Zhou 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 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 297*fdcfd854SBartosz 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 3205a167f45SGreg Kroah-Hartman static int 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 return 0; 3252985c29cSQiao Zhou } 3262985c29cSQiao Zhou 3272985c29cSQiao Zhou static struct platform_driver pm80x_rtc_driver = { 3282985c29cSQiao Zhou .driver = { 3292985c29cSQiao Zhou .name = "88pm80x-rtc", 3302985c29cSQiao Zhou .pm = &pm80x_rtc_pm_ops, 3312985c29cSQiao Zhou }, 3322985c29cSQiao Zhou .probe = pm80x_rtc_probe, 3335a167f45SGreg Kroah-Hartman .remove = pm80x_rtc_remove, 3342985c29cSQiao Zhou }; 3352985c29cSQiao Zhou 3362985c29cSQiao Zhou module_platform_driver(pm80x_rtc_driver); 3372985c29cSQiao Zhou 3382985c29cSQiao Zhou MODULE_LICENSE("GPL"); 3392985c29cSQiao Zhou MODULE_DESCRIPTION("Marvell 88PM80x RTC driver"); 3402985c29cSQiao Zhou MODULE_AUTHOR("Qiao Zhou <zhouqiao@marvell.com>"); 3412985c29cSQiao Zhou MODULE_ALIAS("platform:88pm80x-rtc"); 342