+
+ if (priv->runtime_src_idx < 0) {
+ ret = priv->runtime_src_idx;
+ dev_err(dev, "RTC runtime clock source is not specified\n");
+ goto disable_ipg_clk;
+ }
+
+ ret = rtc_clk_src_switch(&priv->clk, priv->runtime_src_idx);
+ if (ret) {
+ dev_err(dev, "Failed clk source switch, err: %d\n", ret);
+ goto disable_ipg_clk;
+ }
+
+ platform_set_drvdata(pdev, priv);
+ priv->rdev->ops = &rtc_ops;
+
+ ret = devm_rtc_register_device(priv->rdev);
+ if (ret) {
+ dev_err(dev, "Failed to register RTC device\n");
+ goto disable_rtc;
+ }
+
+ ret = devm_request_irq(dev, priv->dt_irq_id,
+ rtc_handler, 0, dev_name(dev), pdev);
+ if (ret) {
+ dev_err(dev, "Request interrupt %d failed, error: %d\n",
+ priv->dt_irq_id, ret);
+ goto disable_rtc;
+ }
+
+ return 0;
+
+disable_ipg_clk:
+ clk_disable_unprepare(priv->ipg);
+disable_rtc:
+ s32g_rtc_disable(priv);
+ return ret;
+}
+
+static void rtc_remove(struct platform_device *pdev)
+{
+ struct rtc_priv *priv = platform_get_drvdata(pdev);
+
+ s32g_rtc_disable(priv);
+}
+
+static void __maybe_unused enable_api_irq(struct device *dev, unsigned int enabled)
+{
+ struct rtc_priv *priv = dev_get_drvdata(dev);
+ u32 api_irq = RTCC_APIEN | RTCC_APIIE;
+ u32 rtcc;
+
+ rtcc = ioread32(priv->rtc_base + RTCC_OFFSET);
+ if (enabled)
+ rtcc |= api_irq;
+ else
+ rtcc &= ~api_irq;
+ iowrite32(rtcc, priv->rtc_base + RTCC_OFFSET);
+}
+
+static int __maybe_unused rtc_suspend(struct device *dev)
+{
+ struct rtc_priv *init_priv = dev_get_drvdata(dev);
+ struct rtc_priv priv;
+ long long base_sec;
+ int ret = 0;
+ u32 rtcval;
+ u32 sec;
+
+ if (!device_may_wakeup(dev))
+ return 0;
+
+ if (init_priv->suspend_src_idx < 0)
+ return 0;
+
+ if (rtc_clk_get_parent(&init_priv->clk) == init_priv->suspend_src_idx)
+ return 0;
+
+ /* Save last known timestamp before we switch clocks and reinit RTC */
+ ret = s32g_rtc_read_time(dev, &init_priv->base.tm);
+ if (ret)
+ return ret;
+
+ /*
+ * Use a local copy of the RTC control block to
+ * avoid restoring it on resume path.
+ */
+ memcpy(&priv, init_priv, sizeof(priv));
+
+ ret = get_time_left(dev, init_priv, &sec);
+ if (ret)
+ return ret;
+
+ /* Adjust for the number of seconds we'll be asleep */
+ base_sec = rtc_tm_to_time64(&init_priv->base.tm);
+ base_sec += sec;
+ rtc_time64_to_tm(base_sec, &init_priv->base.tm);
+
+ ret = rtc_clk_src_switch(&priv.clk, priv.suspend_src_idx);
+ if (ret) {
+ dev_err(dev, "Failed clk source switch, err: %d\n", ret);
+ return ret;
+ }
+
+ ret = sec_to_rtcval(&priv, sec, &rtcval);
+ if (ret) {
+ dev_warn(dev, "Alarm is too far in the future\n");
+ return ret;
+ }
+
+ s32g_rtc_alarm_irq_enable(dev, 0);
+ enable_api_irq(dev, 1);
+ iowrite32(rtcval, priv.rtc_base + APIVAL_OFFSET);
+ iowrite32(0, priv.rtc_base + RTCVAL_OFFSET);
+
+ return ret;
+}
+
+static int __maybe_unused rtc_resume(struct device *dev)
+{
+ struct rtc_priv *priv = dev_get_drvdata(dev);
+ int ret;
+
+ if (!device_may_wakeup(dev))
+ return 0;
+
+ if (rtc_clk_get_parent(&priv->clk) == priv->runtime_src_idx)
+ return 0;
+
+ /* Disable wake-up interrupts */
+ enable_api_irq(dev, 0);
+
+ ret = rtc_clk_src_switch(&priv->clk, priv->runtime_src_idx);
+ if (ret) {
+ dev_err(dev, "Failed clk source switch, err: %d\n", ret);
+ return ret;
+ }
+
+ /*
+ * Now RTCCNT has just been reset, and is out of sync with priv->base;
+ * reapply the saved time settings
+ */
+ return s32g_rtc_set_time(dev, &priv->base.tm);
+}
+
+static const struct of_device_id rtc_dt_ids[] = {
+ { .compatible = "nxp,s32g2-rtc", .data = &rtc_s32g2_data},
+ { /* sentinel */ },
+};
+
+static SIMPLE_DEV_PM_OPS(rtc_pm_ops,
+ rtc_suspend, rtc_resume);
+
+static struct platform_driver rtc_driver = {
+ .driver = {
+ .name = "s32g-rtc",
+ .pm = &rtc_pm_ops,
+ .of_match_table = rtc_dt_ids,
+ },
+ .probe = rtc_probe,
+ .remove = rtc_remove,
+};
+module_platform_driver(rtc_driver);
+
+MODULE_AUTHOR("NXP");
+MODULE_DESCRIPTION("NXP RTC driver for S32G2/S32G3");
+MODULE_LICENSE("GPL");
--
2.45.2