rtc: nvvrs: add NVIDIA VRS RTC device driver
Add support for NVIDIA VRS (Voltage Regulator Specification) RTC device driver. NVIDIA VRS is a Power Management IC (PMIC) that implements a power sequencing solution with I2C interface. The device includes RTC which provides functionality to get/set system time, retain system time across boot, wake system from suspend and shutdown state. Supported platforms: - NVIDIA Jetson AGX Orin Developer Kit - NVIDIA IGX Orin Development Kit - NVIDIA Jetson Orin NX Developer Kit - NVIDIA Jetson Orin Nano Developer Kit Signed-off-by: Shubhi Garg <shgarg@nvidia.com> Reviewed-by: Jon Hunter <jonathanh@nvidia.com> Tested-by: Jon Hunter <jonathanh@nvidia.com> Link: https://patch.msgid.link/20251007135738.487694-4-shgarg@nvidia.com Signed-off-by: Alexandre Belloni <alexandre.belloni@bootlin.com>pull/1354/merge
parent
4c03653f19
commit
9d6d6b0693
|
|
@ -18428,6 +18428,14 @@ S: Maintained
|
||||||
F: drivers/video/fbdev/nvidia/
|
F: drivers/video/fbdev/nvidia/
|
||||||
F: drivers/video/fbdev/riva/
|
F: drivers/video/fbdev/riva/
|
||||||
|
|
||||||
|
NVIDIA VRS RTC DRIVER
|
||||||
|
M: Shubhi Garg <shgarg@nvidia.com>
|
||||||
|
L: linux-tegra@vger.kernel.org
|
||||||
|
S: Maintained
|
||||||
|
F: Documentation/devicetree/bindings/rtc/nvidia,vrs-10.yaml
|
||||||
|
F: drivers/rtc/rtc-nvidia-vrs10.c
|
||||||
|
F: include/linux/rtc/rtc-nvidia-vrs10.h
|
||||||
|
|
||||||
NVIDIA WMI EC BACKLIGHT DRIVER
|
NVIDIA WMI EC BACKLIGHT DRIVER
|
||||||
M: Daniel Dadap <ddadap@nvidia.com>
|
M: Daniel Dadap <ddadap@nvidia.com>
|
||||||
L: platform-driver-x86@vger.kernel.org
|
L: platform-driver-x86@vger.kernel.org
|
||||||
|
|
|
||||||
|
|
@ -416,6 +416,15 @@ config RTC_DRV_SPACEMIT_P1
|
||||||
This driver can also be built as a module, which will be called
|
This driver can also be built as a module, which will be called
|
||||||
"spacemit-p1-rtc".
|
"spacemit-p1-rtc".
|
||||||
|
|
||||||
|
config RTC_DRV_NVIDIA_VRS10
|
||||||
|
tristate "NVIDIA VRS10 RTC device"
|
||||||
|
help
|
||||||
|
If you say yes here you will get support for the battery backed RTC device
|
||||||
|
of NVIDIA VRS (Voltage Regulator Specification). The RTC is connected via
|
||||||
|
I2C interface and supports alarm functionality.
|
||||||
|
This driver can also be built as a module. If so, the module will be called
|
||||||
|
rtc-nvidia-vrs10.
|
||||||
|
|
||||||
config RTC_DRV_NCT3018Y
|
config RTC_DRV_NCT3018Y
|
||||||
tristate "Nuvoton NCT3018Y"
|
tristate "Nuvoton NCT3018Y"
|
||||||
depends on OF
|
depends on OF
|
||||||
|
|
|
||||||
|
|
@ -122,6 +122,7 @@ obj-$(CONFIG_RTC_DRV_GAMECUBE) += rtc-gamecube.o
|
||||||
obj-$(CONFIG_RTC_DRV_NCT3018Y) += rtc-nct3018y.o
|
obj-$(CONFIG_RTC_DRV_NCT3018Y) += rtc-nct3018y.o
|
||||||
obj-$(CONFIG_RTC_DRV_NCT6694) += rtc-nct6694.o
|
obj-$(CONFIG_RTC_DRV_NCT6694) += rtc-nct6694.o
|
||||||
obj-$(CONFIG_RTC_DRV_NTXEC) += rtc-ntxec.o
|
obj-$(CONFIG_RTC_DRV_NTXEC) += rtc-ntxec.o
|
||||||
|
obj-$(CONFIG_RTC_DRV_NVIDIA_VRS10)+= rtc-nvidia-vrs10.o
|
||||||
obj-$(CONFIG_RTC_DRV_OMAP) += rtc-omap.o
|
obj-$(CONFIG_RTC_DRV_OMAP) += rtc-omap.o
|
||||||
obj-$(CONFIG_RTC_DRV_OPAL) += rtc-opal.o
|
obj-$(CONFIG_RTC_DRV_OPAL) += rtc-opal.o
|
||||||
obj-$(CONFIG_RTC_DRV_OPTEE) += rtc-optee.o
|
obj-$(CONFIG_RTC_DRV_OPTEE) += rtc-optee.o
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,542 @@
|
||||||
|
// 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");
|
||||||
Loading…
Reference in New Issue