| // SPDX-License-Identifier: GPL-2.0-only | |
| /* | |
| * NVIDIA Voltage Regulator Specification RTC | |
| * | |
| * SPDX-FileCopyrightText: Copyright (c) 2025 NVIDIA CORPORATION & AFFILIATES. | |
| * All rights reserved. | |
| */ | |
| #include <linux/bits.h> | |
| #include <linux/err.h> | |
| #include <linux/i2c.h> | |
| #include <linux/interrupt.h> | |
| #include <linux/module.h> | |
| #include <linux/rtc.h> | |
| #define NVVRS_REG_VENDOR_ID 0x00 | |
| #define NVVRS_REG_MODEL_REV 0x01 | |
| /* Interrupts registers */ | |
| #define NVVRS_REG_INT_SRC1 0x10 | |
| #define NVVRS_REG_INT_SRC2 0x11 | |
| #define NVVRS_REG_INT_VENDOR 0x12 | |
| /* Control Registers */ | |
| #define NVVRS_REG_CTL_1 0x28 | |
| #define NVVRS_REG_CTL_2 0x29 | |
| /* RTC Registers */ | |
| #define NVVRS_REG_RTC_T3 0x70 | |
| #define NVVRS_REG_RTC_T2 0x71 | |
| #define NVVRS_REG_RTC_T1 0x72 | |
| #define NVVRS_REG_RTC_T0 0x73 | |
| #define NVVRS_REG_RTC_A3 0x74 | |
| #define NVVRS_REG_RTC_A2 0x75 | |
| #define NVVRS_REG_RTC_A1 0x76 | |
| #define NVVRS_REG_RTC_A0 0x77 | |
| /* Interrupt Mask */ | |
| #define NVVRS_INT_SRC1_RSTIRQ_MASK BIT(0) | |
| #define NVVRS_INT_SRC1_OSC_MASK BIT(1) | |
| #define NVVRS_INT_SRC1_EN_MASK BIT(2) | |
| #define NVVRS_INT_SRC1_RTC_MASK BIT(3) | |
| #define NVVRS_INT_SRC1_PEC_MASK BIT(4) | |
| #define NVVRS_INT_SRC1_WDT_MASK BIT(5) | |
| #define NVVRS_INT_SRC1_EM_PD_MASK BIT(6) | |
| #define NVVRS_INT_SRC1_INTERNAL_MASK BIT(7) | |
| #define NVVRS_INT_SRC2_PBSP_MASK BIT(0) | |
| #define NVVRS_INT_SRC2_ECC_DED_MASK BIT(1) | |
| #define NVVRS_INT_SRC2_TSD_MASK BIT(2) | |
| #define NVVRS_INT_SRC2_LDO_MASK BIT(3) | |
| #define NVVRS_INT_SRC2_BIST_MASK BIT(4) | |
| #define NVVRS_INT_SRC2_RT_CRC_MASK BIT(5) | |
| #define NVVRS_INT_SRC2_VENDOR_MASK BIT(7) | |
| #define NVVRS_INT_VENDOR0_MASK BIT(0) | |
| #define NVVRS_INT_VENDOR1_MASK BIT(1) | |
| #define NVVRS_INT_VENDOR2_MASK BIT(2) | |
| #define NVVRS_INT_VENDOR3_MASK BIT(3) | |
| #define NVVRS_INT_VENDOR4_MASK BIT(4) | |
| #define NVVRS_INT_VENDOR5_MASK BIT(5) | |
| #define NVVRS_INT_VENDOR6_MASK BIT(6) | |
| #define NVVRS_INT_VENDOR7_MASK BIT(7) | |
| /* Controller Register Mask */ | |
| #define NVVRS_REG_CTL_1_FORCE_SHDN (BIT(0) | BIT(1)) | |
| #define NVVRS_REG_CTL_1_FORCE_ACT BIT(2) | |
| #define NVVRS_REG_CTL_1_FORCE_INT BIT(3) | |
| #define NVVRS_REG_CTL_2_EN_PEC BIT(0) | |
| #define NVVRS_REG_CTL_2_REQ_PEC BIT(1) | |
| #define NVVRS_REG_CTL_2_RTC_PU BIT(2) | |
| #define NVVRS_REG_CTL_2_RTC_WAKE BIT(3) | |
| #define NVVRS_REG_CTL_2_RST_DLY 0xF0 | |
| #define ALARM_RESET_VAL 0xffffffff | |
| #define NVVRS_MIN_MODEL_REV 0x40 | |
| enum nvvrs_irq_regs { | |
| NVVRS_IRQ_REG_INT_SRC1 = 0, | |
| NVVRS_IRQ_REG_INT_SRC2 = 1, | |
| NVVRS_IRQ_REG_INT_VENDOR = 2, | |
| NVVRS_IRQ_REG_COUNT = 3, | |
| }; | |
| struct nvvrs_rtc_info { | |
| struct device *dev; | |
| struct i2c_client *client; | |
| struct rtc_device *rtc; | |
| unsigned int irq; | |
| }; | |
| static int nvvrs_update_bits(struct nvvrs_rtc_info *info, u8 reg, | |
| u8 mask, u8 value) | |
| { | |
| int ret; | |
| u8 val; | |
| ret = i2c_smbus_read_byte_data(info->client, reg); | |
| if (ret < 0) | |
| return ret; | |
| val = (u8)ret; | |
| val &= ~mask; | |
| val |= (value & mask); | |
| return i2c_smbus_write_byte_data(info->client, reg, val); | |
| } | |
| static int nvvrs_rtc_write_alarm(struct i2c_client *client, u8 *time) | |
| { | |
| int ret; | |
| ret = i2c_smbus_write_byte_data(client, NVVRS_REG_RTC_A3, time[3]); | |
| if (ret < 0) | |
| return ret; | |
| ret = i2c_smbus_write_byte_data(client, NVVRS_REG_RTC_A2, time[2]); | |
| if (ret < 0) | |
| return ret; | |
| ret = i2c_smbus_write_byte_data(client, NVVRS_REG_RTC_A1, time[1]); | |
| if (ret < 0) | |
| return ret; | |
| return i2c_smbus_write_byte_data(client, NVVRS_REG_RTC_A0, time[0]); | |
| } | |
| static int nvvrs_rtc_enable_alarm(struct nvvrs_rtc_info *info) | |
| { | |
| int ret; | |
| /* Set RTC_WAKE bit for autonomous wake from sleep */ | |
| ret = nvvrs_update_bits(info, NVVRS_REG_CTL_2, NVVRS_REG_CTL_2_RTC_WAKE, | |
| NVVRS_REG_CTL_2_RTC_WAKE); | |
| if (ret < 0) | |
| return ret; | |
| /* Set RTC_PU bit for autonomous wake from shutdown */ | |
| ret = nvvrs_update_bits(info, NVVRS_REG_CTL_2, NVVRS_REG_CTL_2_RTC_PU, | |
| NVVRS_REG_CTL_2_RTC_PU); | |
| if (ret < 0) | |
| return ret; | |
| return 0; | |
| } | |
| static int nvvrs_rtc_disable_alarm(struct nvvrs_rtc_info *info) | |
| { | |
| struct i2c_client *client = info->client; | |
| u8 val[4]; | |
| int ret; | |
| /* Clear RTC_WAKE bit */ | |
| ret = nvvrs_update_bits(info, NVVRS_REG_CTL_2, NVVRS_REG_CTL_2_RTC_WAKE, | |
| 0); | |
| if (ret < 0) | |
| return ret; | |
| /* Clear RTC_PU bit */ | |
| ret = nvvrs_update_bits(info, NVVRS_REG_CTL_2, NVVRS_REG_CTL_2_RTC_PU, | |
| 0); | |
| if (ret < 0) | |
| return ret; | |
| /* Write ALARM_RESET_VAL in RTC Alarm register to disable alarm */ | |
| val[0] = 0xff; | |
| val[1] = 0xff; | |
| val[2] = 0xff; | |
| val[3] = 0xff; | |
| ret = nvvrs_rtc_write_alarm(client, val); | |
| if (ret < 0) | |
| return ret; | |
| return 0; | |
| } | |
| static int nvvrs_rtc_read_time(struct device *dev, struct rtc_time *tm) | |
| { | |
| struct nvvrs_rtc_info *info = dev_get_drvdata(dev); | |
| time64_t secs = 0; | |
| int ret; | |
| u8 val; | |
| /* | |
| * Multi-byte transfers are not supported with PEC enabled | |
| * Read MSB first to avoid coherency issues | |
| */ | |
| ret = i2c_smbus_read_byte_data(info->client, NVVRS_REG_RTC_T3); | |
| if (ret < 0) | |
| return ret; | |
| val = (u8)ret; | |
| secs |= (time64_t)val << 24; | |
| ret = i2c_smbus_read_byte_data(info->client, NVVRS_REG_RTC_T2); | |
| if (ret < 0) | |
| return ret; | |
| val = (u8)ret; | |
| secs |= (time64_t)val << 16; | |
| ret = i2c_smbus_read_byte_data(info->client, NVVRS_REG_RTC_T1); | |
| if (ret < 0) | |
| return ret; | |
| val = (u8)ret; | |
| secs |= (time64_t)val << 8; | |
| ret = i2c_smbus_read_byte_data(info->client, NVVRS_REG_RTC_T0); | |
| if (ret < 0) | |
| return ret; | |
| val = (u8)ret; | |
| secs |= val; | |
| rtc_time64_to_tm(secs, tm); | |
| return 0; | |
| } | |
| static int nvvrs_rtc_set_time(struct device *dev, struct rtc_time *tm) | |
| { | |
| struct nvvrs_rtc_info *info = dev_get_drvdata(dev); | |
| time64_t secs; | |
| u8 time[4]; | |
| int ret; | |
| secs = rtc_tm_to_time64(tm); | |
| time[0] = secs & 0xff; | |
| time[1] = (secs >> 8) & 0xff; | |
| time[2] = (secs >> 16) & 0xff; | |
| time[3] = (secs >> 24) & 0xff; | |
| ret = i2c_smbus_write_byte_data(info->client, NVVRS_REG_RTC_T3, time[3]); | |
| if (ret < 0) | |
| return ret; | |
| ret = i2c_smbus_write_byte_data(info->client, NVVRS_REG_RTC_T2, time[2]); | |
| if (ret < 0) | |
| return ret; | |
| ret = i2c_smbus_write_byte_data(info->client, NVVRS_REG_RTC_T1, time[1]); | |
| if (ret < 0) | |
| return ret; | |
| ret = i2c_smbus_write_byte_data(info->client, NVVRS_REG_RTC_T0, time[0]); | |
| return ret; | |
| } | |
| static int nvvrs_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alrm) | |
| { | |
| struct nvvrs_rtc_info *info = dev_get_drvdata(dev); | |
| time64_t alarm_val = 0; | |
| int ret; | |
| u8 val; | |
| /* Multi-byte transfers are not supported with PEC enabled */ | |
| ret = i2c_smbus_read_byte_data(info->client, NVVRS_REG_RTC_A3); | |
| if (ret < 0) | |
| return ret; | |
| val = (u8)ret; | |
| alarm_val |= (time64_t)val << 24; | |
| ret = i2c_smbus_read_byte_data(info->client, NVVRS_REG_RTC_A2); | |
| if (ret < 0) | |
| return ret; | |
| val = (u8)ret; | |
| alarm_val |= (time64_t)val << 16; | |
| ret = i2c_smbus_read_byte_data(info->client, NVVRS_REG_RTC_A1); | |
| if (ret < 0) | |
| return ret; | |
| val = (u8)ret; | |
| alarm_val |= (time64_t)val << 8; | |
| ret = i2c_smbus_read_byte_data(info->client, NVVRS_REG_RTC_A0); | |
| if (ret < 0) | |
| return ret; | |
| val = (u8)ret; | |
| alarm_val |= val; | |
| if (alarm_val == ALARM_RESET_VAL) | |
| alrm->enabled = 0; | |
| else | |
| alrm->enabled = 1; | |
| rtc_time64_to_tm(alarm_val, &alrm->time); | |
| return 0; | |
| } | |
| static int nvvrs_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alrm) | |
| { | |
| struct nvvrs_rtc_info *info = dev_get_drvdata(dev); | |
| time64_t secs; | |
| u8 time[4]; | |
| int ret; | |
| if (!alrm->enabled) { | |
| ret = nvvrs_rtc_disable_alarm(info); | |
| if (ret < 0) | |
| return ret; | |
| } | |
| ret = nvvrs_rtc_enable_alarm(info); | |
| if (ret < 0) | |
| return ret; | |
| secs = rtc_tm_to_time64(&alrm->time); | |
| time[0] = secs & 0xff; | |
| time[1] = (secs >> 8) & 0xff; | |
| time[2] = (secs >> 16) & 0xff; | |
| time[3] = (secs >> 24) & 0xff; | |
| ret = nvvrs_rtc_write_alarm(info->client, time); | |
| return ret; | |
| } | |
| static int nvvrs_pseq_irq_clear(struct nvvrs_rtc_info *info) | |
| { | |
| unsigned int i; | |
| int ret; | |
| for (i = 0; i < NVVRS_IRQ_REG_COUNT; i++) { | |
| ret = i2c_smbus_read_byte_data(info->client, | |
| NVVRS_REG_INT_SRC1 + i); | |
| if (ret < 0) { | |
| dev_err(info->dev, "Failed to read INT_SRC%d : %d\n", | |
| i + 1, ret); | |
| return ret; | |
| } | |
| ret = i2c_smbus_write_byte_data(info->client, | |
| NVVRS_REG_INT_SRC1 + i, | |
| (u8)ret); | |
| if (ret < 0) { | |
| dev_err(info->dev, "Failed to clear INT_SRC%d : %d\n", | |
| i + 1, ret); | |
| return ret; | |
| } | |
| } | |
| return 0; | |
| } | |
| static irqreturn_t nvvrs_rtc_irq_handler(int irq, void *data) | |
| { | |
| struct nvvrs_rtc_info *info = data; | |
| int ret; | |
| /* Check for RTC alarm interrupt */ | |
| ret = i2c_smbus_read_byte_data(info->client, NVVRS_REG_INT_SRC1); | |
| if (ret < 0) | |
| return IRQ_NONE; | |
| if (ret & NVVRS_INT_SRC1_RTC_MASK) { | |
| rtc_lock(info->rtc); | |
| rtc_update_irq(info->rtc, 1, RTC_IRQF | RTC_AF); | |
| rtc_unlock(info->rtc); | |
| } | |
| /* Clear all interrupts */ | |
| if (nvvrs_pseq_irq_clear(info) < 0) | |
| return IRQ_NONE; | |
| return IRQ_HANDLED; | |
| } | |
| static int nvvrs_rtc_alarm_irq_enable(struct device *dev, unsigned int enabled) | |
| { | |
| /* | |
| * This hardware does not support enabling/disabling the alarm IRQ | |
| * independently. The alarm is disabled by clearing the alarm time | |
| * via set_alarm(). | |
| */ | |
| return 0; | |
| } | |
| static const struct rtc_class_ops nvvrs_rtc_ops = { | |
| .read_time = nvvrs_rtc_read_time, | |
| .set_time = nvvrs_rtc_set_time, | |
| .read_alarm = nvvrs_rtc_read_alarm, | |
| .set_alarm = nvvrs_rtc_set_alarm, | |
| .alarm_irq_enable = nvvrs_rtc_alarm_irq_enable, | |
| }; | |
| static int nvvrs_pseq_vendor_info(struct nvvrs_rtc_info *info) | |
| { | |
| struct i2c_client *client = info->client; | |
| u8 vendor_id, model_rev; | |
| int ret; | |
| ret = i2c_smbus_read_byte_data(client, NVVRS_REG_VENDOR_ID); | |
| if (ret < 0) | |
| return dev_err_probe(&client->dev, ret, | |
| "Failed to read Vendor ID\n"); | |
| vendor_id = (u8)ret; | |
| ret = i2c_smbus_read_byte_data(client, NVVRS_REG_MODEL_REV); | |
| if (ret < 0) | |
| return dev_err_probe(&client->dev, ret, | |
| "Failed to read Model Revision\n"); | |
| model_rev = (u8)ret; | |
| if (model_rev < NVVRS_MIN_MODEL_REV) { | |
| return dev_err_probe(&client->dev, -ENODEV, | |
| "Chip revision 0x%02x is not supported!\n", | |
| model_rev); | |
| } | |
| dev_dbg(&client->dev, "NVVRS Vendor ID: 0x%02x, Model Rev: 0x%02x\n", | |
| vendor_id, model_rev); | |
| return 0; | |
| } | |
| static int nvvrs_rtc_probe(struct i2c_client *client) | |
| { | |
| struct nvvrs_rtc_info *info; | |
| int ret; | |
| info = devm_kzalloc(&client->dev, sizeof(*info), GFP_KERNEL); | |
| if (!info) | |
| return -ENOMEM; | |
| if (client->irq <= 0) | |
| return dev_err_probe(&client->dev, -EINVAL, "No IRQ specified\n"); | |
| info->irq = client->irq; | |
| info->dev = &client->dev; | |
| client->flags |= I2C_CLIENT_PEC; | |
| i2c_set_clientdata(client, info); | |
| info->client = client; | |
| /* Check vendor info */ | |
| if (nvvrs_pseq_vendor_info(info) < 0) | |
| return dev_err_probe(&client->dev, -EINVAL, | |
| "Failed to get vendor info\n"); | |
| /* Clear any pending IRQs before requesting IRQ handler */ | |
| if (nvvrs_pseq_irq_clear(info) < 0) | |
| return dev_err_probe(&client->dev, -EINVAL, | |
| "Failed to clear interrupts\n"); | |
| /* Allocate RTC device */ | |
| info->rtc = devm_rtc_allocate_device(info->dev); | |
| if (IS_ERR(info->rtc)) | |
| return PTR_ERR(info->rtc); | |
| info->rtc->ops = &nvvrs_rtc_ops; | |
| info->rtc->range_min = RTC_TIMESTAMP_BEGIN_2000; | |
| info->rtc->range_max = RTC_TIMESTAMP_END_2099; | |
| /* Request RTC IRQ */ | |
| ret = devm_request_threaded_irq(info->dev, info->irq, NULL, | |
| nvvrs_rtc_irq_handler, IRQF_ONESHOT, | |
| "nvvrs-rtc", info); | |
| if (ret < 0) { | |
| dev_err_probe(info->dev, ret, "Failed to request RTC IRQ\n"); | |
| return ret; | |
| } | |
| /* RTC as a wakeup source */ | |
| devm_device_init_wakeup(info->dev); | |
| return devm_rtc_register_device(info->rtc); | |
| } | |
| #ifdef CONFIG_PM_SLEEP | |
| static int nvvrs_rtc_suspend(struct device *dev) | |
| { | |
| struct nvvrs_rtc_info *info = dev_get_drvdata(dev); | |
| int ret; | |
| if (device_may_wakeup(dev)) { | |
| /* Set RTC_WAKE bit for auto wake system from suspend state */ | |
| ret = nvvrs_update_bits(info, NVVRS_REG_CTL_2, | |
| NVVRS_REG_CTL_2_RTC_WAKE, | |
| NVVRS_REG_CTL_2_RTC_WAKE); | |
| if (ret < 0) { | |
| dev_err(info->dev, "Failed to set RTC_WAKE bit (%d)\n", | |
| ret); | |
| return ret; | |
| } | |
| return enable_irq_wake(info->irq); | |
| } | |
| return 0; | |
| } | |
| static int nvvrs_rtc_resume(struct device *dev) | |
| { | |
| struct nvvrs_rtc_info *info = dev_get_drvdata(dev); | |
| int ret; | |
| if (device_may_wakeup(dev)) { | |
| /* Clear FORCE_ACT bit */ | |
| ret = nvvrs_update_bits(info, NVVRS_REG_CTL_1, | |
| NVVRS_REG_CTL_1_FORCE_ACT, 0); | |
| if (ret < 0) { | |
| dev_err(info->dev, "Failed to clear FORCE_ACT bit (%d)\n", | |
| ret); | |
| return ret; | |
| } | |
| return disable_irq_wake(info->irq); | |
| } | |
| return 0; | |
| } | |
| #endif | |
| static SIMPLE_DEV_PM_OPS(nvvrs_rtc_pm_ops, nvvrs_rtc_suspend, nvvrs_rtc_resume); | |
| static const struct of_device_id nvvrs_rtc_of_match[] = { | |
| { .compatible = "nvidia,vrs-10" }, | |
| { }, | |
| }; | |
| MODULE_DEVICE_TABLE(of, nvvrs_rtc_of_match); | |
| static struct i2c_driver nvvrs_rtc_driver = { | |
| .driver = { | |
| .name = "rtc-nvidia-vrs10", | |
| .pm = &nvvrs_rtc_pm_ops, | |
| .of_match_table = nvvrs_rtc_of_match, | |
| }, | |
| .probe = nvvrs_rtc_probe, | |
| }; | |
| module_i2c_driver(nvvrs_rtc_driver); | |
| MODULE_AUTHOR("Shubhi Garg <shgarg@nvidia.com>"); | |
| MODULE_DESCRIPTION("NVIDIA Voltage Regulator Specification RTC driver"); | |
| MODULE_LICENSE("GPL"); |