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