1 // SPDX-License-Identifier: GPL-2.0+ 2 /* 3 * Copyright 2024 NXP. 4 */ 5 6 #include <linux/jiffies.h> 7 #include <linux/module.h> 8 #include <linux/platform_device.h> 9 #include <linux/rtc.h> 10 #include <linux/scmi_protocol.h> 11 #include <linux/scmi_imx_protocol.h> 12 13 struct scmi_imx_bbm { 14 const struct scmi_imx_bbm_proto_ops *ops; 15 struct rtc_device *rtc_dev; 16 struct scmi_protocol_handle *ph; 17 struct notifier_block nb; 18 }; 19 20 static int scmi_imx_bbm_read_time(struct device *dev, struct rtc_time *tm) 21 { 22 struct scmi_imx_bbm *bbnsm = dev_get_drvdata(dev); 23 struct scmi_protocol_handle *ph = bbnsm->ph; 24 u64 val; 25 int ret; 26 27 ret = bbnsm->ops->rtc_time_get(ph, 0, &val); 28 if (ret) 29 return ret; 30 31 rtc_time64_to_tm(val, tm); 32 33 return 0; 34 } 35 36 static int scmi_imx_bbm_set_time(struct device *dev, struct rtc_time *tm) 37 { 38 struct scmi_imx_bbm *bbnsm = dev_get_drvdata(dev); 39 struct scmi_protocol_handle *ph = bbnsm->ph; 40 u64 val; 41 42 val = rtc_tm_to_time64(tm); 43 44 return bbnsm->ops->rtc_time_set(ph, 0, val); 45 } 46 47 static int scmi_imx_bbm_alarm_irq_enable(struct device *dev, unsigned int enable) 48 { 49 struct scmi_imx_bbm *bbnsm = dev_get_drvdata(dev); 50 struct scmi_protocol_handle *ph = bbnsm->ph; 51 52 /* scmi_imx_bbm_set_alarm enables the irq, just handle disable here */ 53 if (!enable) 54 return bbnsm->ops->rtc_alarm_set(ph, 0, false, 0); 55 56 return 0; 57 } 58 59 static int scmi_imx_bbm_set_alarm(struct device *dev, struct rtc_wkalrm *alrm) 60 { 61 struct scmi_imx_bbm *bbnsm = dev_get_drvdata(dev); 62 struct scmi_protocol_handle *ph = bbnsm->ph; 63 struct rtc_time *alrm_tm = &alrm->time; 64 u64 val; 65 66 val = rtc_tm_to_time64(alrm_tm); 67 68 return bbnsm->ops->rtc_alarm_set(ph, 0, true, val); 69 } 70 71 static const struct rtc_class_ops smci_imx_bbm_rtc_ops = { 72 .read_time = scmi_imx_bbm_read_time, 73 .set_time = scmi_imx_bbm_set_time, 74 .set_alarm = scmi_imx_bbm_set_alarm, 75 .alarm_irq_enable = scmi_imx_bbm_alarm_irq_enable, 76 }; 77 78 static int scmi_imx_bbm_rtc_notifier(struct notifier_block *nb, unsigned long event, void *data) 79 { 80 struct scmi_imx_bbm *bbnsm = container_of(nb, struct scmi_imx_bbm, nb); 81 struct scmi_imx_bbm_notif_report *r = data; 82 83 if (r->is_rtc) 84 rtc_update_irq(bbnsm->rtc_dev, 1, RTC_AF | RTC_IRQF); 85 else 86 pr_err("Unexpected bbm event: %s\n", __func__); 87 88 return 0; 89 } 90 91 static int scmi_imx_bbm_rtc_init(struct scmi_device *sdev) 92 { 93 const struct scmi_handle *handle = sdev->handle; 94 struct device *dev = &sdev->dev; 95 struct scmi_imx_bbm *bbnsm = dev_get_drvdata(dev); 96 int ret; 97 98 bbnsm->rtc_dev = devm_rtc_allocate_device(dev); 99 if (IS_ERR(bbnsm->rtc_dev)) 100 return PTR_ERR(bbnsm->rtc_dev); 101 102 bbnsm->rtc_dev->ops = &smci_imx_bbm_rtc_ops; 103 bbnsm->rtc_dev->range_max = U32_MAX; 104 105 bbnsm->nb.notifier_call = &scmi_imx_bbm_rtc_notifier; 106 ret = handle->notify_ops->devm_event_notifier_register(sdev, SCMI_PROTOCOL_IMX_BBM, 107 SCMI_EVENT_IMX_BBM_RTC, 108 NULL, &bbnsm->nb); 109 if (ret) 110 return ret; 111 112 return devm_rtc_register_device(bbnsm->rtc_dev); 113 } 114 115 static int scmi_imx_bbm_rtc_probe(struct scmi_device *sdev) 116 { 117 const struct scmi_handle *handle = sdev->handle; 118 struct device *dev = &sdev->dev; 119 struct scmi_protocol_handle *ph; 120 struct scmi_imx_bbm *bbnsm; 121 int ret; 122 123 if (!handle) 124 return -ENODEV; 125 126 bbnsm = devm_kzalloc(dev, sizeof(*bbnsm), GFP_KERNEL); 127 if (!bbnsm) 128 return -ENOMEM; 129 130 bbnsm->ops = handle->devm_protocol_get(sdev, SCMI_PROTOCOL_IMX_BBM, &ph); 131 if (IS_ERR(bbnsm->ops)) 132 return PTR_ERR(bbnsm->ops); 133 134 bbnsm->ph = ph; 135 136 device_init_wakeup(dev, true); 137 138 dev_set_drvdata(dev, bbnsm); 139 140 ret = scmi_imx_bbm_rtc_init(sdev); 141 if (ret) 142 device_init_wakeup(dev, false); 143 144 return ret; 145 } 146 147 static const struct scmi_device_id scmi_id_table[] = { 148 { SCMI_PROTOCOL_IMX_BBM, "imx-bbm-rtc" }, 149 { }, 150 }; 151 MODULE_DEVICE_TABLE(scmi, scmi_id_table); 152 153 static struct scmi_driver scmi_imx_bbm_rtc_driver = { 154 .name = "scmi-imx-bbm-rtc", 155 .probe = scmi_imx_bbm_rtc_probe, 156 .id_table = scmi_id_table, 157 }; 158 module_scmi_driver(scmi_imx_bbm_rtc_driver); 159 160 MODULE_AUTHOR("Peng Fan <peng.fan@nxp.com>"); 161 MODULE_DESCRIPTION("IMX SM BBM RTC driver"); 162 MODULE_LICENSE("GPL"); 163