1 // SPDX-License-Identifier: GPL-2.0-only OR MIT 2 /* 3 * Apple SMC RTC driver 4 * Copyright The Asahi Linux Contributors 5 */ 6 7 #include <linux/bitops.h> 8 #include <linux/mfd/macsmc.h> 9 #include <linux/module.h> 10 #include <linux/nvmem-consumer.h> 11 #include <linux/of.h> 12 #include <linux/platform_device.h> 13 #include <linux/rtc.h> 14 #include <linux/slab.h> 15 16 /* 48-bit RTC */ 17 #define RTC_BYTES 6 18 #define RTC_BITS (8 * RTC_BYTES) 19 20 /* 32768 Hz clock */ 21 #define RTC_SEC_SHIFT 15 22 23 struct macsmc_rtc { 24 struct device *dev; 25 struct apple_smc *smc; 26 struct rtc_device *rtc_dev; 27 struct nvmem_cell *rtc_offset; 28 }; 29 30 static int macsmc_rtc_get_time(struct device *dev, struct rtc_time *tm) 31 { 32 struct macsmc_rtc *rtc = dev_get_drvdata(dev); 33 u64 ctr = 0, off = 0; 34 time64_t now; 35 void *p_off; 36 size_t len; 37 int ret; 38 39 ret = apple_smc_read(rtc->smc, SMC_KEY(CLKM), &ctr, RTC_BYTES); 40 if (ret < 0) 41 return ret; 42 if (ret != RTC_BYTES) 43 return -EIO; 44 45 p_off = nvmem_cell_read(rtc->rtc_offset, &len); 46 if (IS_ERR(p_off)) 47 return PTR_ERR(p_off); 48 if (len < RTC_BYTES) { 49 kfree(p_off); 50 return -EIO; 51 } 52 53 memcpy(&off, p_off, RTC_BYTES); 54 kfree(p_off); 55 56 /* Sign extend from 48 to 64 bits, then arithmetic shift right 15 bits to get seconds */ 57 now = sign_extend64(ctr + off, RTC_BITS - 1) >> RTC_SEC_SHIFT; 58 rtc_time64_to_tm(now, tm); 59 60 return ret; 61 } 62 63 static int macsmc_rtc_set_time(struct device *dev, struct rtc_time *tm) 64 { 65 struct macsmc_rtc *rtc = dev_get_drvdata(dev); 66 u64 ctr = 0, off = 0; 67 int ret; 68 69 ret = apple_smc_read(rtc->smc, SMC_KEY(CLKM), &ctr, RTC_BYTES); 70 if (ret < 0) 71 return ret; 72 if (ret != RTC_BYTES) 73 return -EIO; 74 75 /* This sets the offset such that the set second begins now */ 76 off = (rtc_tm_to_time64(tm) << RTC_SEC_SHIFT) - ctr; 77 return nvmem_cell_write(rtc->rtc_offset, &off, RTC_BYTES); 78 } 79 80 static const struct rtc_class_ops macsmc_rtc_ops = { 81 .read_time = macsmc_rtc_get_time, 82 .set_time = macsmc_rtc_set_time, 83 }; 84 85 static int macsmc_rtc_probe(struct platform_device *pdev) 86 { 87 struct apple_smc *smc = dev_get_drvdata(pdev->dev.parent); 88 struct macsmc_rtc *rtc; 89 90 /* 91 * MFD will probe this device even without a node in the device tree, 92 * thus bail out early if the SMC on the current machines does not 93 * support RTC and has no node in the device tree. 94 */ 95 if (!pdev->dev.of_node) 96 return -ENODEV; 97 98 rtc = devm_kzalloc(&pdev->dev, sizeof(*rtc), GFP_KERNEL); 99 if (!rtc) 100 return -ENOMEM; 101 102 rtc->dev = &pdev->dev; 103 rtc->smc = smc; 104 105 rtc->rtc_offset = devm_nvmem_cell_get(&pdev->dev, "rtc_offset"); 106 if (IS_ERR(rtc->rtc_offset)) 107 return dev_err_probe(&pdev->dev, PTR_ERR(rtc->rtc_offset), 108 "Failed to get rtc_offset NVMEM cell\n"); 109 110 rtc->rtc_dev = devm_rtc_allocate_device(&pdev->dev); 111 if (IS_ERR(rtc->rtc_dev)) 112 return PTR_ERR(rtc->rtc_dev); 113 114 rtc->rtc_dev->ops = &macsmc_rtc_ops; 115 rtc->rtc_dev->range_min = S64_MIN >> (RTC_SEC_SHIFT + (64 - RTC_BITS)); 116 rtc->rtc_dev->range_max = S64_MAX >> (RTC_SEC_SHIFT + (64 - RTC_BITS)); 117 118 platform_set_drvdata(pdev, rtc); 119 120 return devm_rtc_register_device(rtc->rtc_dev); 121 } 122 123 static const struct of_device_id macsmc_rtc_of_table[] = { 124 { .compatible = "apple,smc-rtc", }, 125 {} 126 }; 127 MODULE_DEVICE_TABLE(of, macsmc_rtc_of_table); 128 129 static struct platform_driver macsmc_rtc_driver = { 130 .driver = { 131 .name = "macsmc-rtc", 132 .of_match_table = macsmc_rtc_of_table, 133 }, 134 .probe = macsmc_rtc_probe, 135 }; 136 module_platform_driver(macsmc_rtc_driver); 137 138 MODULE_LICENSE("Dual MIT/GPL"); 139 MODULE_DESCRIPTION("Apple SMC RTC driver"); 140 MODULE_AUTHOR("Hector Martin <marcan@marcan.st>"); 141