1 // SPDX-License-Identifier: GPL-2.0+ 2 /* 3 * Realtek RTD129x RTC 4 * 5 * Copyright (c) 2017 Andreas Färber 6 */ 7 8 #include <linux/clk.h> 9 #include <linux/io.h> 10 #include <linux/module.h> 11 #include <linux/of.h> 12 #include <linux/of_address.h> 13 #include <linux/platform_device.h> 14 #include <linux/rtc.h> 15 #include <linux/spinlock.h> 16 17 #define RTD_RTCSEC 0x00 18 #define RTD_RTCMIN 0x04 19 #define RTD_RTCHR 0x08 20 #define RTD_RTCDATE1 0x0c 21 #define RTD_RTCDATE2 0x10 22 #define RTD_RTCACR 0x28 23 #define RTD_RTCEN 0x2c 24 #define RTD_RTCCR 0x30 25 26 #define RTD_RTCSEC_RTCSEC_MASK 0x7f 27 28 #define RTD_RTCMIN_RTCMIN_MASK 0x3f 29 30 #define RTD_RTCHR_RTCHR_MASK 0x1f 31 32 #define RTD_RTCDATE1_RTCDATE1_MASK 0xff 33 34 #define RTD_RTCDATE2_RTCDATE2_MASK 0x7f 35 36 #define RTD_RTCACR_RTCPWR BIT(7) 37 38 #define RTD_RTCEN_RTCEN_MASK 0xff 39 40 #define RTD_RTCCR_RTCRST BIT(6) 41 42 struct rtd119x_rtc { 43 void __iomem *base; 44 struct clk *clk; 45 struct rtc_device *rtcdev; 46 unsigned int base_year; 47 }; 48 49 static inline int rtd119x_rtc_days_in_year(int year) 50 { 51 return 365 + (is_leap_year(year) ? 1 : 0); 52 } 53 54 static void rtd119x_rtc_reset(struct device *dev) 55 { 56 struct rtd119x_rtc *data = dev_get_drvdata(dev); 57 u32 val; 58 59 val = readl_relaxed(data->base + RTD_RTCCR); 60 val |= RTD_RTCCR_RTCRST; 61 writel_relaxed(val, data->base + RTD_RTCCR); 62 63 val &= ~RTD_RTCCR_RTCRST; 64 writel(val, data->base + RTD_RTCCR); 65 } 66 67 static void rtd119x_rtc_set_enabled(struct device *dev, bool enable) 68 { 69 struct rtd119x_rtc *data = dev_get_drvdata(dev); 70 u32 val; 71 72 val = readl_relaxed(data->base + RTD_RTCEN); 73 if (enable) { 74 if ((val & RTD_RTCEN_RTCEN_MASK) == 0x5a) 75 return; 76 writel_relaxed(0x5a, data->base + RTD_RTCEN); 77 } else { 78 writel_relaxed(0, data->base + RTD_RTCEN); 79 } 80 } 81 82 static int rtd119x_rtc_read_time(struct device *dev, struct rtc_time *tm) 83 { 84 struct rtd119x_rtc *data = dev_get_drvdata(dev); 85 s32 day; 86 u32 sec; 87 unsigned int year; 88 int tries = 0; 89 90 while (true) { 91 tm->tm_sec = (readl_relaxed(data->base + RTD_RTCSEC) & RTD_RTCSEC_RTCSEC_MASK) >> 1; 92 tm->tm_min = readl_relaxed(data->base + RTD_RTCMIN) & RTD_RTCMIN_RTCMIN_MASK; 93 tm->tm_hour = readl_relaxed(data->base + RTD_RTCHR) & RTD_RTCHR_RTCHR_MASK; 94 day = readl_relaxed(data->base + RTD_RTCDATE1) & RTD_RTCDATE1_RTCDATE1_MASK; 95 day |= (readl_relaxed(data->base + RTD_RTCDATE2) & RTD_RTCDATE2_RTCDATE2_MASK) << 8; 96 sec = (readl_relaxed(data->base + RTD_RTCSEC) & RTD_RTCSEC_RTCSEC_MASK) >> 1; 97 tries++; 98 99 if (sec == tm->tm_sec) 100 break; 101 102 if (tries >= 3) 103 return -EINVAL; 104 } 105 if (tries > 1) 106 dev_dbg(dev, "%s: needed %i tries\n", __func__, tries); 107 108 year = data->base_year; 109 while (day >= rtd119x_rtc_days_in_year(year)) { 110 day -= rtd119x_rtc_days_in_year(year); 111 year++; 112 } 113 tm->tm_year = year - 1900; 114 tm->tm_yday = day; 115 116 tm->tm_mon = 0; 117 while (day >= rtc_month_days(tm->tm_mon, year)) { 118 day -= rtc_month_days(tm->tm_mon, year); 119 tm->tm_mon++; 120 } 121 tm->tm_mday = day + 1; 122 123 return 0; 124 } 125 126 static int rtd119x_rtc_set_time(struct device *dev, struct rtc_time *tm) 127 { 128 struct rtd119x_rtc *data = dev_get_drvdata(dev); 129 unsigned int day; 130 int i; 131 132 if (1900 + tm->tm_year < data->base_year) 133 return -EINVAL; 134 135 day = 0; 136 for (i = data->base_year; i < 1900 + tm->tm_year; i++) 137 day += rtd119x_rtc_days_in_year(i); 138 139 day += tm->tm_yday; 140 if (day > 0x7fff) 141 return -EINVAL; 142 143 rtd119x_rtc_set_enabled(dev, false); 144 145 writel_relaxed((tm->tm_sec << 1) & RTD_RTCSEC_RTCSEC_MASK, data->base + RTD_RTCSEC); 146 writel_relaxed(tm->tm_min & RTD_RTCMIN_RTCMIN_MASK, data->base + RTD_RTCMIN); 147 writel_relaxed(tm->tm_hour & RTD_RTCHR_RTCHR_MASK, data->base + RTD_RTCHR); 148 writel_relaxed(day & RTD_RTCDATE1_RTCDATE1_MASK, data->base + RTD_RTCDATE1); 149 writel_relaxed((day >> 8) & RTD_RTCDATE2_RTCDATE2_MASK, data->base + RTD_RTCDATE2); 150 151 rtd119x_rtc_set_enabled(dev, true); 152 153 return 0; 154 } 155 156 static const struct rtc_class_ops rtd119x_rtc_ops = { 157 .read_time = rtd119x_rtc_read_time, 158 .set_time = rtd119x_rtc_set_time, 159 }; 160 161 static const struct of_device_id rtd119x_rtc_dt_ids[] = { 162 { .compatible = "realtek,rtd1295-rtc" }, 163 { } 164 }; 165 166 static int rtd119x_rtc_probe(struct platform_device *pdev) 167 { 168 struct rtd119x_rtc *data; 169 u32 val; 170 int ret; 171 172 data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL); 173 if (!data) 174 return -ENOMEM; 175 176 platform_set_drvdata(pdev, data); 177 data->base_year = 2014; 178 179 data->base = devm_platform_ioremap_resource(pdev, 0); 180 if (IS_ERR(data->base)) 181 return PTR_ERR(data->base); 182 183 data->clk = of_clk_get(pdev->dev.of_node, 0); 184 if (IS_ERR(data->clk)) 185 return PTR_ERR(data->clk); 186 187 ret = clk_prepare_enable(data->clk); 188 if (ret) { 189 clk_put(data->clk); 190 return ret; 191 } 192 193 val = readl_relaxed(data->base + RTD_RTCACR); 194 if (!(val & RTD_RTCACR_RTCPWR)) { 195 writel_relaxed(RTD_RTCACR_RTCPWR, data->base + RTD_RTCACR); 196 197 rtd119x_rtc_reset(&pdev->dev); 198 199 writel_relaxed(0, data->base + RTD_RTCMIN); 200 writel_relaxed(0, data->base + RTD_RTCHR); 201 writel_relaxed(0, data->base + RTD_RTCDATE1); 202 writel_relaxed(0, data->base + RTD_RTCDATE2); 203 } 204 205 rtd119x_rtc_set_enabled(&pdev->dev, true); 206 207 data->rtcdev = devm_rtc_device_register(&pdev->dev, "rtc", 208 &rtd119x_rtc_ops, THIS_MODULE); 209 if (IS_ERR(data->rtcdev)) { 210 dev_err(&pdev->dev, "failed to register rtc device"); 211 clk_disable_unprepare(data->clk); 212 clk_put(data->clk); 213 return PTR_ERR(data->rtcdev); 214 } 215 216 return 0; 217 } 218 219 static void rtd119x_rtc_remove(struct platform_device *pdev) 220 { 221 struct rtd119x_rtc *data = platform_get_drvdata(pdev); 222 223 rtd119x_rtc_set_enabled(&pdev->dev, false); 224 225 clk_disable_unprepare(data->clk); 226 clk_put(data->clk); 227 } 228 229 static struct platform_driver rtd119x_rtc_driver = { 230 .probe = rtd119x_rtc_probe, 231 .remove = rtd119x_rtc_remove, 232 .driver = { 233 .name = "rtd1295-rtc", 234 .of_match_table = rtd119x_rtc_dt_ids, 235 }, 236 }; 237 builtin_platform_driver(rtd119x_rtc_driver); 238