1 // SPDX-License-Identifier: GPL-2.0-or-later 2 /* 3 * Faraday Technology FTRTC010 driver 4 * 5 * Copyright (C) 2009 Janos Laube <janos.dev@gmail.com> 6 * 7 * Original code for older kernel 2.6.15 are from Stormlinksemi 8 * first update from Janos Laube for > 2.6.29 kernels 9 * 10 * checkpatch fixes and usage of rtc-lib code 11 * Hans Ulli Kroll <ulli.kroll@googlemail.com> 12 */ 13 14 #include <linux/rtc.h> 15 #include <linux/io.h> 16 #include <linux/slab.h> 17 #include <linux/platform_device.h> 18 #include <linux/kernel.h> 19 #include <linux/module.h> 20 #include <linux/clk.h> 21 22 #define DRV_NAME "rtc-ftrtc010" 23 24 MODULE_AUTHOR("Hans Ulli Kroll <ulli.kroll@googlemail.com>"); 25 MODULE_DESCRIPTION("RTC driver for Gemini SoC"); 26 MODULE_LICENSE("GPL"); 27 MODULE_ALIAS("platform:" DRV_NAME); 28 29 struct ftrtc010_rtc { 30 void __iomem *rtc_base; 31 int rtc_irq; 32 struct clk *pclk; 33 struct clk *extclk; 34 }; 35 36 enum ftrtc010_rtc_offsets { 37 FTRTC010_RTC_SECOND = 0x00, 38 FTRTC010_RTC_MINUTE = 0x04, 39 FTRTC010_RTC_HOUR = 0x08, 40 FTRTC010_RTC_DAYS = 0x0C, 41 FTRTC010_RTC_ALARM_SECOND = 0x10, 42 FTRTC010_RTC_ALARM_MINUTE = 0x14, 43 FTRTC010_RTC_ALARM_HOUR = 0x18, 44 FTRTC010_RTC_RECORD = 0x1C, 45 FTRTC010_RTC_CR = 0x20, 46 }; 47 48 static irqreturn_t ftrtc010_rtc_interrupt(int irq, void *dev) 49 { 50 return IRQ_HANDLED; 51 } 52 53 /* 54 * Looks like the RTC in the Gemini SoC is (totaly) broken 55 * We can't read/write directly the time from RTC registers. 56 * We must do some "offset" calculation to get the real time 57 * 58 * This FIX works pretty fine and Stormlinksemi aka Cortina-Networks does 59 * the same thing, without the rtc-lib.c calls. 60 */ 61 62 static int ftrtc010_rtc_read_time(struct device *dev, struct rtc_time *tm) 63 { 64 struct ftrtc010_rtc *rtc = dev_get_drvdata(dev); 65 66 u32 days, hour, min, sec, offset; 67 timeu64_t time; 68 69 sec = readl(rtc->rtc_base + FTRTC010_RTC_SECOND); 70 min = readl(rtc->rtc_base + FTRTC010_RTC_MINUTE); 71 hour = readl(rtc->rtc_base + FTRTC010_RTC_HOUR); 72 days = readl(rtc->rtc_base + FTRTC010_RTC_DAYS); 73 offset = readl(rtc->rtc_base + FTRTC010_RTC_RECORD); 74 75 time = offset + days * 86400 + hour * 3600 + min * 60 + sec; 76 77 rtc_time64_to_tm(time, tm); 78 79 return 0; 80 } 81 82 static int ftrtc010_rtc_set_time(struct device *dev, struct rtc_time *tm) 83 { 84 struct ftrtc010_rtc *rtc = dev_get_drvdata(dev); 85 u32 sec, min, hour, day, offset; 86 timeu64_t time; 87 88 time = rtc_tm_to_time64(tm); 89 90 sec = readl(rtc->rtc_base + FTRTC010_RTC_SECOND); 91 min = readl(rtc->rtc_base + FTRTC010_RTC_MINUTE); 92 hour = readl(rtc->rtc_base + FTRTC010_RTC_HOUR); 93 day = readl(rtc->rtc_base + FTRTC010_RTC_DAYS); 94 95 offset = time - (day * 86400 + hour * 3600 + min * 60 + sec); 96 97 writel(offset, rtc->rtc_base + FTRTC010_RTC_RECORD); 98 writel(0x01, rtc->rtc_base + FTRTC010_RTC_CR); 99 100 return 0; 101 } 102 103 static const struct rtc_class_ops ftrtc010_rtc_ops = { 104 .read_time = ftrtc010_rtc_read_time, 105 .set_time = ftrtc010_rtc_set_time, 106 }; 107 108 static int ftrtc010_rtc_probe(struct platform_device *pdev) 109 { 110 u32 days, hour, min, sec; 111 struct ftrtc010_rtc *rtc; 112 struct device *dev = &pdev->dev; 113 struct resource *res; 114 struct rtc_device *rtc_dev; 115 int ret; 116 117 rtc = devm_kzalloc(&pdev->dev, sizeof(*rtc), GFP_KERNEL); 118 if (unlikely(!rtc)) 119 return -ENOMEM; 120 platform_set_drvdata(pdev, rtc); 121 122 rtc->pclk = devm_clk_get(dev, "PCLK"); 123 if (IS_ERR(rtc->pclk)) { 124 dev_err(dev, "could not get PCLK\n"); 125 } else { 126 ret = clk_prepare_enable(rtc->pclk); 127 if (ret) { 128 dev_err(dev, "failed to enable PCLK\n"); 129 return ret; 130 } 131 } 132 rtc->extclk = devm_clk_get(dev, "EXTCLK"); 133 if (IS_ERR(rtc->extclk)) { 134 dev_err(dev, "could not get EXTCLK\n"); 135 } else { 136 ret = clk_prepare_enable(rtc->extclk); 137 if (ret) { 138 dev_err(dev, "failed to enable EXTCLK\n"); 139 goto err_disable_pclk; 140 } 141 } 142 143 rtc->rtc_irq = platform_get_irq(pdev, 0); 144 if (rtc->rtc_irq < 0) { 145 ret = rtc->rtc_irq; 146 goto err_disable_extclk; 147 } 148 149 res = platform_get_resource(pdev, IORESOURCE_MEM, 0); 150 if (!res) { 151 ret = -ENODEV; 152 goto err_disable_extclk; 153 } 154 155 rtc->rtc_base = devm_ioremap(dev, res->start, 156 resource_size(res)); 157 if (!rtc->rtc_base) { 158 ret = -ENOMEM; 159 goto err_disable_extclk; 160 } 161 162 rtc_dev = devm_rtc_allocate_device(dev); 163 if (IS_ERR(rtc_dev)) { 164 ret = PTR_ERR(rtc_dev); 165 goto err_disable_extclk; 166 } 167 168 rtc_dev->ops = &ftrtc010_rtc_ops; 169 170 sec = readl(rtc->rtc_base + FTRTC010_RTC_SECOND); 171 min = readl(rtc->rtc_base + FTRTC010_RTC_MINUTE); 172 hour = readl(rtc->rtc_base + FTRTC010_RTC_HOUR); 173 days = readl(rtc->rtc_base + FTRTC010_RTC_DAYS); 174 175 rtc_dev->range_min = (u64)days * 86400 + hour * 3600 + min * 60 + sec; 176 rtc_dev->range_max = U32_MAX + rtc_dev->range_min; 177 178 ret = devm_request_irq(dev, rtc->rtc_irq, ftrtc010_rtc_interrupt, 179 IRQF_SHARED, pdev->name, dev); 180 if (unlikely(ret)) 181 goto err_disable_extclk; 182 183 return devm_rtc_register_device(rtc_dev); 184 185 err_disable_extclk: 186 clk_disable_unprepare(rtc->extclk); 187 err_disable_pclk: 188 clk_disable_unprepare(rtc->pclk); 189 return ret; 190 } 191 192 static void ftrtc010_rtc_remove(struct platform_device *pdev) 193 { 194 struct ftrtc010_rtc *rtc = platform_get_drvdata(pdev); 195 196 if (!IS_ERR(rtc->extclk)) 197 clk_disable_unprepare(rtc->extclk); 198 if (!IS_ERR(rtc->pclk)) 199 clk_disable_unprepare(rtc->pclk); 200 } 201 202 static const struct of_device_id ftrtc010_rtc_dt_match[] = { 203 { .compatible = "cortina,gemini-rtc" }, 204 { .compatible = "faraday,ftrtc010" }, 205 { } 206 }; 207 MODULE_DEVICE_TABLE(of, ftrtc010_rtc_dt_match); 208 209 static struct platform_driver ftrtc010_rtc_driver = { 210 .driver = { 211 .name = DRV_NAME, 212 .of_match_table = ftrtc010_rtc_dt_match, 213 }, 214 .probe = ftrtc010_rtc_probe, 215 .remove = ftrtc010_rtc_remove, 216 }; 217 218 module_platform_driver_probe(ftrtc010_rtc_driver, ftrtc010_rtc_probe); 219