1// SPDX-License-Identifier: GPL-2.0-or-later 2/* 3 * Loongson RTC driver 4 * 5 * Maintained out-of-tree by Huacai Chen <chenhuacai@kernel.org>. 6 * Rewritten for mainline by WANG Xuerui <git@xen0n.name>. 7 * Binbin Zhou <zhoubinbin@loongson.cn> 8 */ 9 10#include <linux/bitfield.h> 11#include <linux/kernel.h> 12#include <linux/module.h> 13#include <linux/platform_device.h> 14#include <linux/regmap.h> 15#include <linux/rtc.h> 16#include <linux/acpi.h> 17 18/* Time Of Year(TOY) counters registers */ 19#define TOY_TRIM_REG 0x20 /* Must be initialized to 0 */ 20#define TOY_WRITE0_REG 0x24 /* TOY low 32-bits value (write-only) */ 21#define TOY_WRITE1_REG 0x28 /* TOY high 32-bits value (write-only) */ 22#define TOY_READ0_REG 0x2c /* TOY low 32-bits value (read-only) */ 23#define TOY_READ1_REG 0x30 /* TOY high 32-bits value (read-only) */ 24#define TOY_MATCH0_REG 0x34 /* TOY timing interrupt 0 */ 25#define TOY_MATCH1_REG 0x38 /* TOY timing interrupt 1 */ 26#define TOY_MATCH2_REG 0x3c /* TOY timing interrupt 2 */ 27 28/* RTC counters registers */ 29#define RTC_CTRL_REG 0x40 /* TOY and RTC control register */ 30#define RTC_TRIM_REG 0x60 /* Must be initialized to 0 */ 31#define RTC_WRITE0_REG 0x64 /* RTC counters value (write-only) */ 32#define RTC_READ0_REG 0x68 /* RTC counters value (read-only) */ 33#define RTC_MATCH0_REG 0x6c /* RTC timing interrupt 0 */ 34#define RTC_MATCH1_REG 0x70 /* RTC timing interrupt 1 */ 35#define RTC_MATCH2_REG 0x74 /* RTC timing interrupt 2 */ 36 37/* bitmask of TOY_WRITE0_REG */ 38#define TOY_MON GENMASK(31, 26) 39#define TOY_DAY GENMASK(25, 21) 40#define TOY_HOUR GENMASK(20, 16) 41#define TOY_MIN GENMASK(15, 10) 42#define TOY_SEC GENMASK(9, 4) 43#define TOY_MSEC GENMASK(3, 0) 44 45/* bitmask of TOY_MATCH0/1/2_REG */ 46#define TOY_MATCH_YEAR GENMASK(31, 26) 47#define TOY_MATCH_MON GENMASK(25, 22) 48#define TOY_MATCH_DAY GENMASK(21, 17) 49#define TOY_MATCH_HOUR GENMASK(16, 12) 50#define TOY_MATCH_MIN GENMASK(11, 6) 51#define TOY_MATCH_SEC GENMASK(5, 0) 52 53/* bitmask of RTC_CTRL_REG */ 54#define RTC_ENABLE BIT(13) /* 1: RTC counters enable */ 55#define TOY_ENABLE BIT(11) /* 1: TOY counters enable */ 56#define OSC_ENABLE BIT(8) /* 1: 32.768k crystal enable */ 57#define TOY_ENABLE_MASK (TOY_ENABLE | OSC_ENABLE) 58 59/* PM domain registers */ 60#define PM1_STS_REG 0x0c /* Power management 1 status register */ 61#define RTC_STS BIT(10) /* RTC status */ 62#define PM1_EN_REG 0x10 /* Power management 1 enable register */ 63#define RTC_EN BIT(10) /* RTC event enable */ 64 65/* 66 * According to the LS1C manual, RTC_CTRL and alarm-related registers are not defined. 67 * Accessing the relevant registers will cause the system to hang. 68 */ 69#define LS1C_RTC_CTRL_WORKAROUND BIT(0) 70 71struct loongson_rtc_config { 72 u32 pm_offset; /* Offset of PM domain, for RTC alarm wakeup */ 73 u32 flags; /* Workaround bits */ 74}; 75 76struct loongson_rtc_priv { 77 spinlock_t lock; /* protects PM registers access */ 78 u32 fix_year; /* RTC alarm year compensation value */ 79 struct rtc_device *rtcdev; 80 struct regmap *regmap; 81 void __iomem *pm_base; /* PM domain base, for RTC alarm wakeup */ 82 const struct loongson_rtc_config *config; 83}; 84 85static const struct loongson_rtc_config ls1b_rtc_config = { 86 .pm_offset = 0, 87 .flags = 0, 88}; 89 90static const struct loongson_rtc_config ls1c_rtc_config = { 91 .pm_offset = 0, 92 .flags = LS1C_RTC_CTRL_WORKAROUND, 93}; 94 95static const struct loongson_rtc_config generic_rtc_config = { 96 .pm_offset = 0x100, 97 .flags = 0, 98}; 99 100static const struct loongson_rtc_config ls2k1000_rtc_config = { 101 .pm_offset = 0x800, 102 .flags = 0, 103}; 104 105static const struct regmap_config loongson_rtc_regmap_config = { 106 .reg_bits = 32, 107 .val_bits = 32, 108 .reg_stride = 4, 109}; 110 111/* RTC alarm irq handler */ 112static irqreturn_t loongson_rtc_isr(int irq, void *id) 113{ 114 struct loongson_rtc_priv *priv = (struct loongson_rtc_priv *)id; 115 116 rtc_update_irq(priv->rtcdev, 1, RTC_AF | RTC_IRQF); 117 return IRQ_HANDLED; 118} 119 120/* For ACPI fixed event handler */ 121static u32 loongson_rtc_handler(void *id) 122{ 123 struct loongson_rtc_priv *priv = (struct loongson_rtc_priv *)id; 124 125 spin_lock(&priv->lock); 126 /* Disable RTC alarm wakeup and interrupt */ 127 writel(readl(priv->pm_base + PM1_EN_REG) & ~RTC_EN, 128 priv->pm_base + PM1_EN_REG); 129 130 /* Clear RTC interrupt status */ 131 writel(RTC_STS, priv->pm_base + PM1_STS_REG); 132 spin_unlock(&priv->lock); 133 134 /* 135 * The TOY_MATCH0_REG should be cleared 0 here, 136 * otherwise the interrupt cannot be cleared. 137 */ 138 return regmap_write(priv->regmap, TOY_MATCH0_REG, 0); 139} 140 141static int loongson_rtc_set_enabled(struct device *dev) 142{ 143 struct loongson_rtc_priv *priv = dev_get_drvdata(dev); 144 145 if (priv->config->flags & LS1C_RTC_CTRL_WORKAROUND) 146 return 0; 147 148 /* Enable RTC TOY counters and crystal */ 149 return regmap_update_bits(priv->regmap, RTC_CTRL_REG, TOY_ENABLE_MASK, 150 TOY_ENABLE_MASK); 151} 152 153static bool loongson_rtc_get_enabled(struct device *dev) 154{ 155 int ret; 156 u32 ctrl_data; 157 struct loongson_rtc_priv *priv = dev_get_drvdata(dev); 158 159 if (priv->config->flags & LS1C_RTC_CTRL_WORKAROUND) 160 return true; 161 162 ret = regmap_read(priv->regmap, RTC_CTRL_REG, &ctrl_data); 163 if (ret < 0) 164 return false; 165 166 return ctrl_data & TOY_ENABLE_MASK; 167} 168 169static int loongson_rtc_read_time(struct device *dev, struct rtc_time *tm) 170{ 171 int ret; 172 u32 rtc_data[2]; 173 struct loongson_rtc_priv *priv = dev_get_drvdata(dev); 174 175 if (!loongson_rtc_get_enabled(dev)) 176 return -EINVAL; 177 178 ret = regmap_bulk_read(priv->regmap, TOY_READ0_REG, rtc_data, 179 ARRAY_SIZE(rtc_data)); 180 if (ret < 0) 181 return ret; 182 183 tm->tm_sec = FIELD_GET(TOY_SEC, rtc_data[0]); 184 tm->tm_min = FIELD_GET(TOY_MIN, rtc_data[0]); 185 tm->tm_hour = FIELD_GET(TOY_HOUR, rtc_data[0]); 186 tm->tm_mday = FIELD_GET(TOY_DAY, rtc_data[0]); 187 tm->tm_mon = FIELD_GET(TOY_MON, rtc_data[0]) - 1; 188 tm->tm_year = rtc_data[1]; 189 190 /* Prepare for RTC alarm year compensation value. */ 191 priv->fix_year = tm->tm_year / 64 * 64; 192 return 0; 193} 194 195static int loongson_rtc_set_time(struct device *dev, struct rtc_time *tm) 196{ 197 int ret; 198 u32 rtc_data[2]; 199 struct loongson_rtc_priv *priv = dev_get_drvdata(dev); 200 201 rtc_data[0] = FIELD_PREP(TOY_SEC, tm->tm_sec) 202 | FIELD_PREP(TOY_MIN, tm->tm_min) 203 | FIELD_PREP(TOY_HOUR, tm->tm_hour) 204 | FIELD_PREP(TOY_DAY, tm->tm_mday) 205 | FIELD_PREP(TOY_MON, tm->tm_mon + 1); 206 rtc_data[1] = tm->tm_year; 207 208 ret = regmap_bulk_write(priv->regmap, TOY_WRITE0_REG, rtc_data, 209 ARRAY_SIZE(rtc_data)); 210 if (ret < 0) 211 return ret; 212 213 return loongson_rtc_set_enabled(dev); 214} 215 216static int loongson_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alrm) 217{ 218 int ret; 219 u32 alarm_data; 220 struct loongson_rtc_priv *priv = dev_get_drvdata(dev); 221 222 ret = regmap_read(priv->regmap, TOY_MATCH0_REG, &alarm_data); 223 if (ret < 0) 224 return ret; 225 226 alrm->time.tm_sec = FIELD_GET(TOY_MATCH_SEC, alarm_data); 227 alrm->time.tm_min = FIELD_GET(TOY_MATCH_MIN, alarm_data); 228 alrm->time.tm_hour = FIELD_GET(TOY_MATCH_HOUR, alarm_data); 229 alrm->time.tm_mday = FIELD_GET(TOY_MATCH_DAY, alarm_data); 230 alrm->time.tm_mon = FIELD_GET(TOY_MATCH_MON, alarm_data) - 1; 231 /* 232 * This is a hardware bug: the year field of SYS_TOYMATCH is only 6 bits, 233 * making it impossible to save year values larger than 64. 234 * 235 * SYS_TOYMATCH is used to match the alarm time value and determine if 236 * an alarm is triggered, so we must keep the lower 6 bits of the year 237 * value constant during the value conversion. 238 * 239 * In summary, we need to manually add 64(or a multiple of 64) to the 240 * year value to avoid the invalid alarm prompt at startup. 241 */ 242 alrm->time.tm_year = FIELD_GET(TOY_MATCH_YEAR, alarm_data) + priv->fix_year; 243 244 alrm->enabled = !!(readl(priv->pm_base + PM1_EN_REG) & RTC_EN); 245 return 0; 246} 247 248static int loongson_rtc_alarm_irq_enable(struct device *dev, unsigned int enabled) 249{ 250 u32 val; 251 struct loongson_rtc_priv *priv = dev_get_drvdata(dev); 252 253 spin_lock(&priv->lock); 254 val = readl(priv->pm_base + PM1_EN_REG); 255 /* Enable RTC alarm wakeup */ 256 writel(enabled ? val | RTC_EN : val & ~RTC_EN, 257 priv->pm_base + PM1_EN_REG); 258 spin_unlock(&priv->lock); 259 260 return 0; 261} 262 263static int loongson_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alrm) 264{ 265 int ret; 266 u32 alarm_data; 267 struct loongson_rtc_priv *priv = dev_get_drvdata(dev); 268 269 alarm_data = FIELD_PREP(TOY_MATCH_SEC, alrm->time.tm_sec) 270 | FIELD_PREP(TOY_MATCH_MIN, alrm->time.tm_min) 271 | FIELD_PREP(TOY_MATCH_HOUR, alrm->time.tm_hour) 272 | FIELD_PREP(TOY_MATCH_DAY, alrm->time.tm_mday) 273 | FIELD_PREP(TOY_MATCH_MON, alrm->time.tm_mon + 1) 274 | FIELD_PREP(TOY_MATCH_YEAR, alrm->time.tm_year - priv->fix_year); 275 276 ret = regmap_write(priv->regmap, TOY_MATCH0_REG, alarm_data); 277 if (ret < 0) 278 return ret; 279 280 return loongson_rtc_alarm_irq_enable(dev, alrm->enabled); 281} 282 283static const struct rtc_class_ops loongson_rtc_ops = { 284 .read_time = loongson_rtc_read_time, 285 .set_time = loongson_rtc_set_time, 286 .read_alarm = loongson_rtc_read_alarm, 287 .set_alarm = loongson_rtc_set_alarm, 288 .alarm_irq_enable = loongson_rtc_alarm_irq_enable, 289}; 290 291static int loongson_rtc_probe(struct platform_device *pdev) 292{ 293 int ret, alarm_irq; 294 void __iomem *regs; 295 struct loongson_rtc_priv *priv; 296 struct device *dev = &pdev->dev; 297 298 priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); 299 if (!priv) 300 return -ENOMEM; 301 302 regs = devm_platform_ioremap_resource(pdev, 0); 303 if (IS_ERR(regs)) 304 return dev_err_probe(dev, PTR_ERR(regs), 305 "devm_platform_ioremap_resource failed\n"); 306 307 priv->regmap = devm_regmap_init_mmio(dev, regs, 308 &loongson_rtc_regmap_config); 309 if (IS_ERR(priv->regmap)) 310 return dev_err_probe(dev, PTR_ERR(priv->regmap), 311 "devm_regmap_init_mmio failed\n"); 312 313 priv->config = device_get_match_data(dev); 314 spin_lock_init(&priv->lock); 315 platform_set_drvdata(pdev, priv); 316 317 priv->rtcdev = devm_rtc_allocate_device(dev); 318 if (IS_ERR(priv->rtcdev)) 319 return dev_err_probe(dev, PTR_ERR(priv->rtcdev), 320 "devm_rtc_allocate_device failed\n"); 321 322 /* Get RTC alarm irq */ 323 alarm_irq = platform_get_irq(pdev, 0); 324 if (alarm_irq > 0) { 325 ret = devm_request_irq(dev, alarm_irq, loongson_rtc_isr, 326 0, "loongson-alarm", priv); 327 if (ret < 0) 328 return dev_err_probe(dev, ret, "Unable to request irq %d\n", 329 alarm_irq); 330 331 priv->pm_base = regs - priv->config->pm_offset; 332 device_init_wakeup(dev, 1); 333 334 if (has_acpi_companion(dev)) 335 acpi_install_fixed_event_handler(ACPI_EVENT_RTC, 336 loongson_rtc_handler, priv); 337 } else { 338 /* Loongson-1C RTC does not support alarm */ 339 clear_bit(RTC_FEATURE_ALARM, priv->rtcdev->features); 340 } 341 342 /* Loongson RTC does not support UIE */ 343 clear_bit(RTC_FEATURE_UPDATE_INTERRUPT, priv->rtcdev->features); 344 priv->rtcdev->ops = &loongson_rtc_ops; 345 priv->rtcdev->range_min = RTC_TIMESTAMP_BEGIN_2000; 346 priv->rtcdev->range_max = RTC_TIMESTAMP_END_2099; 347 348 return devm_rtc_register_device(priv->rtcdev); 349} 350 351static void loongson_rtc_remove(struct platform_device *pdev) 352{ 353 struct device *dev = &pdev->dev; 354 struct loongson_rtc_priv *priv = dev_get_drvdata(dev); 355 356 if (!test_bit(RTC_FEATURE_ALARM, priv->rtcdev->features)) 357 return; 358 359 if (has_acpi_companion(dev)) 360 acpi_remove_fixed_event_handler(ACPI_EVENT_RTC, 361 loongson_rtc_handler); 362 363 device_init_wakeup(dev, 0); 364 loongson_rtc_alarm_irq_enable(dev, 0); 365} 366 367static const struct of_device_id loongson_rtc_of_match[] = { 368 { .compatible = "loongson,ls1b-rtc", .data = &ls1b_rtc_config }, 369 { .compatible = "loongson,ls1c-rtc", .data = &ls1c_rtc_config }, 370 { .compatible = "loongson,ls7a-rtc", .data = &generic_rtc_config }, 371 { .compatible = "loongson,ls2k1000-rtc", .data = &ls2k1000_rtc_config }, 372 { /* sentinel */ } 373}; 374MODULE_DEVICE_TABLE(of, loongson_rtc_of_match); 375 376static const struct acpi_device_id loongson_rtc_acpi_match[] = { 377 { "LOON0001", .driver_data = (kernel_ulong_t)&generic_rtc_config }, 378 { } 379}; 380MODULE_DEVICE_TABLE(acpi, loongson_rtc_acpi_match); 381 382static struct platform_driver loongson_rtc_driver = { 383 .probe = loongson_rtc_probe, 384 .remove_new = loongson_rtc_remove, 385 .driver = { 386 .name = "loongson-rtc", 387 .of_match_table = loongson_rtc_of_match, 388 .acpi_match_table = loongson_rtc_acpi_match, 389 }, 390}; 391module_platform_driver(loongson_rtc_driver); 392 393MODULE_DESCRIPTION("Loongson RTC driver"); 394MODULE_AUTHOR("Binbin Zhou <zhoubinbin@loongson.cn>"); 395MODULE_AUTHOR("WANG Xuerui <git@xen0n.name>"); 396MODULE_AUTHOR("Huacai Chen <chenhuacai@kernel.org>"); 397MODULE_LICENSE("GPL"); 398