Add samsung specific changes

This commit is contained in:
2025-08-11 14:29:00 +02:00
parent c66122e619
commit 4d134a1294
2688 changed files with 1127995 additions and 11475 deletions

View File

@@ -1985,4 +1985,9 @@ config RTC_DRV_POLARFIRE_SOC
This driver can also be built as a module, if so, the module
will be called "rtc-mpfs".
config RTC_AUTO_PWRON
tristate "RTC Auto Power on PMICs"
help
Support for the auto power on alarm on the PMIC.
endif # RTC_CLASS

View File

@@ -184,3 +184,4 @@ obj-$(CONFIG_RTC_DRV_WM8350) += rtc-wm8350.o
obj-$(CONFIG_RTC_DRV_X1205) += rtc-x1205.o
obj-$(CONFIG_RTC_DRV_XGENE) += rtc-xgene.o
obj-$(CONFIG_RTC_DRV_ZYNQMP) += rtc-zynqmp.o
obj-$(CONFIG_RTC_AUTO_PWRON) += sec_pon_alarm.o

View File

@@ -4,6 +4,7 @@
*
* Copyright (c) 2010-2011, Code Aurora Forum. All rights reserved.
* Copyright (c) 2023, Linaro Limited
* Copyright (c) 2021-2023 Qualcomm Innovation Center, Inc. All rights reserved.
*/
#include <linux/of.h>
#include <linux/module.h>
@@ -72,13 +73,13 @@ static int pm8xxx_rtc_read_nvmem_offset(struct pm8xxx_rtc *rtc_dd)
{
size_t len;
void *buf;
int rc;
int rtc_alarm_status;
buf = nvmem_cell_read(rtc_dd->nvmem_cell, &len);
if (IS_ERR(buf)) {
rc = PTR_ERR(buf);
dev_dbg(rtc_dd->dev, "failed to read nvmem offset: %d\n", rc);
return rc;
rtc_alarm_status = PTR_ERR(buf);
dev_dbg(rtc_dd->dev, "failed to read nvmem offset: %d\n", rtc_alarm_status);
return rtc_alarm_status;
}
if (len != sizeof(u32)) {
@@ -97,14 +98,14 @@ static int pm8xxx_rtc_read_nvmem_offset(struct pm8xxx_rtc *rtc_dd)
static int pm8xxx_rtc_write_nvmem_offset(struct pm8xxx_rtc *rtc_dd, u32 offset)
{
u8 buf[sizeof(u32)];
int rc;
int rtc_alarm_status;
put_unaligned_le32(offset, buf);
rc = nvmem_cell_write(rtc_dd->nvmem_cell, buf, sizeof(buf));
if (rc < 0) {
dev_dbg(rtc_dd->dev, "failed to write nvmem offset: %d\n", rc);
return rc;
rtc_alarm_status = nvmem_cell_write(rtc_dd->nvmem_cell, buf, sizeof(buf));
if (rtc_alarm_status < 0) {
dev_dbg(rtc_dd->dev, "failed to write nvmem offset: %d\n", rtc_alarm_status);
return rtc_alarm_status;
}
return 0;
@@ -123,25 +124,25 @@ static int pm8xxx_rtc_read_raw(struct pm8xxx_rtc *rtc_dd, u32 *secs)
const struct pm8xxx_rtc_regs *regs = rtc_dd->regs;
u8 value[NUM_8_BIT_RTC_REGS];
unsigned int reg;
int rc;
int rtc_alarm_status;
rc = regmap_bulk_read(rtc_dd->regmap, regs->read, value, sizeof(value));
if (rc)
return rc;
rtc_alarm_status = regmap_bulk_read(rtc_dd->regmap, regs->read, value, sizeof(value));
if (rtc_alarm_status)
return rtc_alarm_status;
/*
* Read the LSB again and check if there has been a carry over.
* If there has, redo the read operation.
*/
rc = regmap_read(rtc_dd->regmap, regs->read, &reg);
if (rc < 0)
return rc;
rtc_alarm_status = regmap_read(rtc_dd->regmap, regs->read, &reg);
if (rtc_alarm_status < 0)
return rtc_alarm_status;
if (reg < value[0]) {
rc = regmap_bulk_read(rtc_dd->regmap, regs->read, value,
rtc_alarm_status = regmap_bulk_read(rtc_dd->regmap, regs->read, value,
sizeof(value));
if (rc)
return rc;
if (rtc_alarm_status)
return rtc_alarm_status;
}
*secs = get_unaligned_le32(value);
@@ -153,23 +154,23 @@ static int pm8xxx_rtc_update_offset(struct pm8xxx_rtc *rtc_dd, u32 secs)
{
u32 raw_secs;
u32 offset;
int rc;
int rtc_alarm_status;
if (!rtc_dd->nvmem_cell)
return -ENODEV;
rc = pm8xxx_rtc_read_raw(rtc_dd, &raw_secs);
if (rc)
return rc;
rtc_alarm_status = pm8xxx_rtc_read_raw(rtc_dd, &raw_secs);
if (rtc_alarm_status)
return rtc_alarm_status;
offset = secs - raw_secs;
if (offset == rtc_dd->offset)
return 0;
rc = pm8xxx_rtc_write_nvmem_offset(rtc_dd, offset);
if (rc)
return rc;
rtc_alarm_status = pm8xxx_rtc_write_nvmem_offset(rtc_dd, offset);
if (rtc_alarm_status)
return rtc_alarm_status;
rtc_dd->offset = offset;
@@ -190,47 +191,47 @@ static int __pm8xxx_rtc_set_time(struct pm8xxx_rtc *rtc_dd, u32 secs)
const struct pm8xxx_rtc_regs *regs = rtc_dd->regs;
u8 value[NUM_8_BIT_RTC_REGS];
bool alarm_enabled;
int rc;
int rtc_alarm_status;
put_unaligned_le32(secs, value);
rc = regmap_update_bits_check(rtc_dd->regmap, regs->alarm_ctrl,
rtc_alarm_status = regmap_update_bits_check(rtc_dd->regmap, regs->alarm_ctrl,
regs->alarm_en, 0, &alarm_enabled);
if (rc)
return rc;
if (rtc_alarm_status)
return rtc_alarm_status;
/* Disable RTC */
rc = regmap_update_bits(rtc_dd->regmap, regs->ctrl, PM8xxx_RTC_ENABLE, 0);
if (rc)
return rc;
rtc_alarm_status = regmap_update_bits(rtc_dd->regmap, regs->ctrl, PM8xxx_RTC_ENABLE, 0);
if (rtc_alarm_status)
return rtc_alarm_status;
/* Write 0 to Byte[0] */
rc = regmap_write(rtc_dd->regmap, regs->write, 0);
if (rc)
return rc;
rtc_alarm_status = regmap_write(rtc_dd->regmap, regs->write, 0);
if (rtc_alarm_status)
return rtc_alarm_status;
/* Write Byte[1], Byte[2], Byte[3] */
rc = regmap_bulk_write(rtc_dd->regmap, regs->write + 1,
rtc_alarm_status = regmap_bulk_write(rtc_dd->regmap, regs->write + 1,
&value[1], sizeof(value) - 1);
if (rc)
return rc;
if (rtc_alarm_status)
return rtc_alarm_status;
/* Write Byte[0] */
rc = regmap_write(rtc_dd->regmap, regs->write, value[0]);
if (rc)
return rc;
rtc_alarm_status = regmap_write(rtc_dd->regmap, regs->write, value[0]);
if (rtc_alarm_status)
return rtc_alarm_status;
/* Enable RTC */
rc = regmap_update_bits(rtc_dd->regmap, regs->ctrl, PM8xxx_RTC_ENABLE,
rtc_alarm_status = regmap_update_bits(rtc_dd->regmap, regs->ctrl, PM8xxx_RTC_ENABLE,
PM8xxx_RTC_ENABLE);
if (rc)
return rc;
if (rtc_alarm_status)
return rtc_alarm_status;
if (alarm_enabled) {
rc = regmap_update_bits(rtc_dd->regmap, regs->alarm_ctrl,
rtc_alarm_status = regmap_update_bits(rtc_dd->regmap, regs->alarm_ctrl,
regs->alarm_en, regs->alarm_en);
if (rc)
return rc;
if (rtc_alarm_status)
return rtc_alarm_status;
}
return 0;
@@ -240,17 +241,17 @@ static int pm8xxx_rtc_set_time(struct device *dev, struct rtc_time *tm)
{
struct pm8xxx_rtc *rtc_dd = dev_get_drvdata(dev);
u32 secs;
int rc;
int rtc_alarm_status;
secs = rtc_tm_to_time64(tm);
if (rtc_dd->allow_set_time)
rc = __pm8xxx_rtc_set_time(rtc_dd, secs);
rtc_alarm_status = __pm8xxx_rtc_set_time(rtc_dd, secs);
else
rc = pm8xxx_rtc_update_offset(rtc_dd, secs);
rtc_alarm_status = pm8xxx_rtc_update_offset(rtc_dd, secs);
if (rc)
return rc;
if (rtc_alarm_status)
return rtc_alarm_status;
dev_dbg(dev, "set time: %ptRd %ptRt (%u + %u)\n", tm, tm,
secs - rtc_dd->offset, rtc_dd->offset);
@@ -261,11 +262,11 @@ static int pm8xxx_rtc_read_time(struct device *dev, struct rtc_time *tm)
{
struct pm8xxx_rtc *rtc_dd = dev_get_drvdata(dev);
u32 secs;
int rc;
int rtc_alarm_status;
rc = pm8xxx_rtc_read_raw(rtc_dd, &secs);
if (rc)
return rc;
rtc_alarm_status = pm8xxx_rtc_read_raw(rtc_dd, &secs);
if (rtc_alarm_status)
return rtc_alarm_status;
secs += rtc_dd->offset;
rtc_time64_to_tm(secs, tm);
@@ -281,27 +282,27 @@ static int pm8xxx_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alarm)
const struct pm8xxx_rtc_regs *regs = rtc_dd->regs;
u8 value[NUM_8_BIT_RTC_REGS];
u32 secs;
int rc;
int rtc_alarm_status;
secs = rtc_tm_to_time64(&alarm->time);
secs -= rtc_dd->offset;
put_unaligned_le32(secs, value);
rc = regmap_update_bits(rtc_dd->regmap, regs->alarm_ctrl,
rtc_alarm_status = regmap_update_bits(rtc_dd->regmap, regs->alarm_ctrl,
regs->alarm_en, 0);
if (rc)
return rc;
if (rtc_alarm_status)
return rtc_alarm_status;
rc = regmap_bulk_write(rtc_dd->regmap, regs->alarm_rw, value,
rtc_alarm_status = regmap_bulk_write(rtc_dd->regmap, regs->alarm_rw, value,
sizeof(value));
if (rc)
return rc;
if (rtc_alarm_status)
return rtc_alarm_status;
if (alarm->enabled) {
rc = regmap_update_bits(rtc_dd->regmap, regs->alarm_ctrl,
rtc_alarm_status = regmap_update_bits(rtc_dd->regmap, regs->alarm_ctrl,
regs->alarm_en, regs->alarm_en);
if (rc)
return rc;
if (rtc_alarm_status)
return rtc_alarm_status;
}
dev_dbg(dev, "set alarm: %ptRd %ptRt\n", &alarm->time, &alarm->time);
@@ -316,20 +317,20 @@ static int pm8xxx_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alarm)
u8 value[NUM_8_BIT_RTC_REGS];
unsigned int ctrl_reg;
u32 secs;
int rc;
int rtc_alarm_status;
rc = regmap_bulk_read(rtc_dd->regmap, regs->alarm_rw, value,
rtc_alarm_status = regmap_bulk_read(rtc_dd->regmap, regs->alarm_rw, value,
sizeof(value));
if (rc)
return rc;
if (rtc_alarm_status)
return rtc_alarm_status;
secs = get_unaligned_le32(value);
secs += rtc_dd->offset;
rtc_time64_to_tm(secs, &alarm->time);
rc = regmap_read(rtc_dd->regmap, regs->alarm_ctrl, &ctrl_reg);
if (rc)
return rc;
rtc_alarm_status = regmap_read(rtc_dd->regmap, regs->alarm_ctrl, &ctrl_reg);
if (rtc_alarm_status)
return rtc_alarm_status;
alarm->enabled = !!(ctrl_reg & PM8xxx_RTC_ALARM_ENABLE);
@@ -344,24 +345,24 @@ static int pm8xxx_rtc_alarm_irq_enable(struct device *dev, unsigned int enable)
const struct pm8xxx_rtc_regs *regs = rtc_dd->regs;
u8 value[NUM_8_BIT_RTC_REGS] = {0};
unsigned int val;
int rc;
int rtc_alarm_status;
if (enable)
val = regs->alarm_en;
else
val = 0;
rc = regmap_update_bits(rtc_dd->regmap, regs->alarm_ctrl,
rtc_alarm_status = regmap_update_bits(rtc_dd->regmap, regs->alarm_ctrl,
regs->alarm_en, val);
if (rc)
return rc;
if (rtc_alarm_status)
return rtc_alarm_status;
/* Clear alarm register */
if (!enable) {
rc = regmap_bulk_write(rtc_dd->regmap, regs->alarm_rw, value,
rtc_alarm_status = regmap_bulk_write(rtc_dd->regmap, regs->alarm_rw, value,
sizeof(value));
if (rc)
return rc;
if (rtc_alarm_status)
return rtc_alarm_status;
}
return 0;
@@ -379,20 +380,20 @@ static irqreturn_t pm8xxx_alarm_trigger(int irq, void *dev_id)
{
struct pm8xxx_rtc *rtc_dd = dev_id;
const struct pm8xxx_rtc_regs *regs = rtc_dd->regs;
int rc;
int rtc_alarm_status;
rtc_update_irq(rtc_dd->rtc, 1, RTC_IRQF | RTC_AF);
/* Disable alarm */
rc = regmap_update_bits(rtc_dd->regmap, regs->alarm_ctrl,
rtc_alarm_status = regmap_update_bits(rtc_dd->regmap, regs->alarm_ctrl,
regs->alarm_en, 0);
if (rc)
if (rtc_alarm_status)
return IRQ_NONE;
/* Clear alarm status */
rc = regmap_update_bits(rtc_dd->regmap, regs->alarm_ctrl2,
rtc_alarm_status = regmap_update_bits(rtc_dd->regmap, regs->alarm_ctrl2,
PM8xxx_RTC_ALARM_CLEAR, 0);
if (rc)
if (rtc_alarm_status)
return IRQ_NONE;
return IRQ_HANDLED;
@@ -446,11 +447,26 @@ static const struct pm8xxx_rtc_regs pmk8350_regs = {
.alarm_en = BIT(7),
};
static const struct pm8xxx_rtc_regs pm5100_regs = {
.ctrl = 0x6446,
.write = 0x6440,
.read = 0x6448,
.alarm_rw = 0x6540,
.alarm_ctrl = 0x6546,
.alarm_ctrl2 = 0x6548,
.alarm_en = BIT(7),
};
/*
* Hardcoded RTC bases until IORESOURCE_REG mapping is figured out
*/
static const struct of_device_id pm8xxx_id_table[] = {
{ .compatible = "qcom,pm8921-rtc", .data = &pm8921_regs },
{ .compatible = "qcom,pm8058-rtc", .data = &pm8058_regs },
{ .compatible = "qcom,pm8941-rtc", .data = &pm8941_regs },
{ .compatible = "qcom,pmk8350-rtc", .data = &pmk8350_regs },
{ .compatible = "qcom,pm5100-rtc", .data = &pm5100_regs },
{ },
};
MODULE_DEVICE_TABLE(of, pm8xxx_id_table);
@@ -459,7 +475,7 @@ static int pm8xxx_rtc_probe(struct platform_device *pdev)
{
const struct of_device_id *match;
struct pm8xxx_rtc *rtc_dd;
int rc;
int rtc_alarm_status;
match = of_match_node(pm8xxx_id_table, pdev->dev.of_node);
if (!match)
@@ -482,9 +498,9 @@ static int pm8xxx_rtc_probe(struct platform_device *pdev)
rtc_dd->nvmem_cell = devm_nvmem_cell_get(&pdev->dev, "offset");
if (IS_ERR(rtc_dd->nvmem_cell)) {
rc = PTR_ERR(rtc_dd->nvmem_cell);
if (rc != -ENOENT)
return rc;
rtc_alarm_status = PTR_ERR(rtc_dd->nvmem_cell);
if (rtc_alarm_status != -ENOENT)
return rtc_alarm_status;
rtc_dd->nvmem_cell = NULL;
}
@@ -492,14 +508,14 @@ static int pm8xxx_rtc_probe(struct platform_device *pdev)
rtc_dd->dev = &pdev->dev;
if (!rtc_dd->allow_set_time) {
rc = pm8xxx_rtc_read_offset(rtc_dd);
if (rc)
return rc;
rtc_alarm_status = pm8xxx_rtc_read_offset(rtc_dd);
if (rtc_alarm_status)
return rtc_alarm_status;
}
rc = pm8xxx_rtc_enable(rtc_dd);
if (rc)
return rc;
rtc_alarm_status = pm8xxx_rtc_enable(rtc_dd);
if (rtc_alarm_status)
return rtc_alarm_status;
platform_set_drvdata(pdev, rtc_dd);
@@ -512,29 +528,62 @@ static int pm8xxx_rtc_probe(struct platform_device *pdev)
rtc_dd->rtc->ops = &pm8xxx_rtc_ops;
rtc_dd->rtc->range_max = U32_MAX;
rc = devm_request_any_context_irq(&pdev->dev, rtc_dd->alarm_irq,
rtc_alarm_status = devm_request_any_context_irq(&pdev->dev, rtc_dd->alarm_irq,
pm8xxx_alarm_trigger,
IRQF_TRIGGER_RISING,
"pm8xxx_rtc_alarm", rtc_dd);
if (rc < 0)
return rc;
if (rtc_alarm_status < 0)
return rtc_alarm_status;
rc = devm_rtc_register_device(rtc_dd->rtc);
if (rc)
return rc;
rtc_alarm_status = devm_rtc_register_device(rtc_dd->rtc);
if (rtc_alarm_status)
return rtc_alarm_status;
rc = dev_pm_set_wake_irq(&pdev->dev, rtc_dd->alarm_irq);
if (rc)
return rc;
rtc_alarm_status = dev_pm_set_wake_irq(&pdev->dev, rtc_dd->alarm_irq);
if (rtc_alarm_status)
return rtc_alarm_status;
return 0;
}
#if IS_ENABLED(CONFIG_RTC_AUTO_PWRON)
static struct rtc_wkalrm pwron_alarm;
void pmic_rtc_setalarm(struct rtc_wkalrm *alm)
{
memcpy(&pwron_alarm, alm, sizeof(struct rtc_wkalrm));
}
EXPORT_SYMBOL(pmic_rtc_setalarm);
#endif
static void pm8xxx_remove(struct platform_device *pdev)
{
dev_pm_clear_wake_irq(&pdev->dev);
}
static void pm8xxx_rtc_shutdown(struct platform_device *pdev)
{
struct pm8xxx_rtc *rtc_dd = platform_get_drvdata(pdev);
#if IS_ENABLED(CONFIG_RTC_AUTO_PWRON)
struct rtc_wkalrm alarm;
int rtc_alarm_status = 0;
if (pdev) {
pm8xxx_rtc_set_alarm(&pdev->dev, &pwron_alarm);
rtc_alarm_status = pm8xxx_rtc_read_alarm(&pdev->dev, &alarm);
if (!rtc_alarm_status) {
pr_info("%s: %d-%02d-%02d %02d:%02d:%02d\n", __func__,
alarm.time.tm_year + 1900, alarm.time.tm_mon + 1, alarm.time.tm_mday,
alarm.time.tm_hour, alarm.time.tm_min, alarm.time.tm_sec);
}
} else
pr_err("%s: spmi device not found\n", __func__);
#endif
devm_free_irq(rtc_dd->dev, rtc_dd->alarm_irq, rtc_dd);
}
static struct platform_driver pm8xxx_rtc_driver = {
.probe = pm8xxx_rtc_probe,
.remove_new = pm8xxx_remove,
@@ -542,6 +591,7 @@ static struct platform_driver pm8xxx_rtc_driver = {
.name = "rtc-pm8xxx",
.of_match_table = pm8xxx_id_table,
},
.shutdown = pm8xxx_rtc_shutdown,
};
module_platform_driver(pm8xxx_rtc_driver);

437
drivers/rtc/sec_pon_alarm.c Normal file
View File

@@ -0,0 +1,437 @@
/*
* /drivers/rtc/power-on-alarm.c
*
* Copyright (C) 2021 Samsung Electronics
*
* This software is licensed under the terms of the GNU General Public
* License version 2, as published by the Free Software Foundation, and
* may be copied, distributed, and modified under those terms.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
*/
#include <linux/time.h>
#include <linux/module.h>
#include <linux/device.h>
#include <linux/miscdevice.h>
#include <linux/fs.h>
#include <linux/platform_device.h>
#include <linux/uaccess.h>
#include <linux/alarmtimer.h>
#include <linux/ioctl.h>
#include <linux/sec_param.h>
#include <linux/rtc.h>
#include <linux/of_device.h>
#define PON_ALARM_KPARAM_MAGIC 0x41504153
#define PON_ALARM_START_POLL_TIME (10LL * NSEC_PER_SEC) /* 10 sec */
#define PON_ALARM_BOOTING_TIME (5 * 60)
#define PON_ALARM_POLL_TIME (15 * 60)
#define BOOTALM_BIT_EN 0
#define BOOTALM_BIT_YEAR 1
#define BOOTALM_BIT_MONTH 5
#define BOOTALM_BIT_DAY 7
#define BOOTALM_BIT_HOUR 9
#define BOOTALM_BIT_MIN 11
#define BOOTALM_BIT_TOTAL 13
#define RTC_ALM_SEC_MASK 0x00000003
#define RTC_RESET_NUM 70
#define RTC_RESET_YEAR 50
#define ALARM_ENABLE_MULTIPLIER 1
#define PRIME_EPOCH_YEAR 1900
enum pon_alarm_state {
PON_ALARM_DISTANT = 0,
PON_ALARM_NEAR,
PON_ALARM_EXPIRED,
PON_ALARM_OVER,
PON_ALARM_ERROR
};
struct pon_alarm_info {
struct device *dev;
struct rtc_device *rtcdev;
struct rtc_wkalrm val;
struct alarm check_poll;
struct work_struct check_func;
struct wakeup_source *ws;
uint lpm_mode;
unsigned char triggered;
};
struct alarm_timespec {
char alarm[14];
};
#define ANDROID_ALARM_BASE_CMD(cmd) (cmd & ~(_IOC(0, 0, 0xf0, 0)))
#define ANDROID_ALARM_SET_ALARM_BOOT _IOW('a', 7, struct alarm_timespec)
extern void pmic_rtc_setalarm(struct rtc_wkalrm *alm);
static unsigned int __read_mostly rtcalarm;
static unsigned int __read_mostly lpcharge;
module_param(rtcalarm, uint, 0444);
module_param(lpcharge, uint, 0444);
struct pon_alarm_info pon_alarm;
static void pon_alarm_normalize(struct rtc_wkalrm *alarm)
{
if (!alarm->enabled) {
/* RTC reset + 50 years = 1580518864 = 0x5e34cdd0 */
alarm->time.tm_year = RTC_RESET_NUM + RTC_RESET_YEAR;
alarm->time.tm_mon = 1;
alarm->time.tm_mday = 1;
alarm->time.tm_hour = 1;
alarm->time.tm_min = 1;
alarm->time.tm_sec = 4;
}
}
static void pon_alarm_set(struct rtc_wkalrm *alarm)
{
time64_t secs_pwron;
unsigned int rtc_alarm_params[3];
int ret;
memcpy(&pon_alarm.val, alarm, sizeof(struct rtc_wkalrm));
pon_alarm_normalize(&pon_alarm.val);
pr_info("%s: reserve pmic alarm\n", __func__);
pmic_rtc_setalarm(&pon_alarm.val);
secs_pwron = rtc_tm_to_time64(&pon_alarm.val.time);
rtc_alarm_params[0] = PON_ALARM_KPARAM_MAGIC;
rtc_alarm_params[1] = (unsigned int)pon_alarm.val.enabled;
rtc_alarm_params[2] = (unsigned int)secs_pwron;
ret = sec_set_param(param_index_sapa, rtc_alarm_params);
pr_info("%s: ret=%d, enabled=%d, alarm=%u\n",
__func__, ret, rtc_alarm_params[1], rtc_alarm_params[2]);
}
static char *pon_alarm_get_timeformat(struct rtc_time tm)
{
static char time_str[20];
snprintf(time_str, sizeof(time_str), "%4d-%02d-%02d %02d:%02d:%02d",
tm.tm_year, tm.tm_mon + 1, tm.tm_mday,
tm.tm_hour, tm.tm_min, tm.tm_sec);
return time_str;
}
static void pon_get_alarm_time(struct rtc_wkalrm *alm, u8 *buf_ptr)
{
/*
* 0|1234|56|78|90|12
* 1|2010|01|01|00|00
* en|yyyy|mm|dd|hh|mm
*/
alm->time.tm_sec = 0;
alm->time.tm_min = (buf_ptr[BOOTALM_BIT_MIN] - '0') * 10
+ (buf_ptr[BOOTALM_BIT_MIN+1] - '0');
alm->time.tm_hour = (buf_ptr[BOOTALM_BIT_HOUR] - '0') * 10
+ (buf_ptr[BOOTALM_BIT_HOUR+1] - '0');
alm->time.tm_mday = (buf_ptr[BOOTALM_BIT_DAY] - '0') * 10
+ (buf_ptr[BOOTALM_BIT_DAY+1] - '0');
alm->time.tm_mon = (buf_ptr[BOOTALM_BIT_MONTH] - '0') * 10
+ (buf_ptr[BOOTALM_BIT_MONTH+1] - '0');
alm->time.tm_year = (buf_ptr[BOOTALM_BIT_YEAR] - '0') * 1000
+ (buf_ptr[BOOTALM_BIT_YEAR+1] - '0') * 100
+ (buf_ptr[BOOTALM_BIT_YEAR+2] - '0') * 10
+ (buf_ptr[BOOTALM_BIT_YEAR+3] - '0');
switch (*buf_ptr) {
case '1':
alm->enabled = 1;
break;
case '2':
alm->enabled = 2;
break;
default:
pr_warn("Invalid value for alarm enable flag: %c, setting to 0\n", *buf_ptr);
alm->enabled = 0;
}
pr_info("%s: %s => tm(%d %s)\n", __func__, buf_ptr, alm->enabled, pon_alarm_get_timeformat(alm->time));
}
static void pon_read_kernel_time(struct timespec64 *ktm_ts, struct rtc_wkalrm *alm)
{
struct rtc_time ktm_tm;
/* read kernel time */
ktime_get_real_ts64(ktm_ts);
ktm_tm = rtc_ktime_to_tm(timespec64_to_ktime(*ktm_ts));
pr_info("%s: <KTM > %s\n", __func__, pon_alarm_get_timeformat(ktm_tm));
alm->time.tm_mon -= 1;
alm->time.tm_year -= PRIME_EPOCH_YEAR;
pr_info("%s: <ALRM> %s\n", __func__, pon_alarm_get_timeformat(alm->time));
}
static int pon_read_current_time(time64_t *rtc_sec, struct rtc_time *rtc_tm)
{
int ret = 0;
ret = rtc_read_time(pon_alarm.rtcdev, rtc_tm);
if (ret) {
pr_err("%s: rtc read failed, ret=%d\n", __func__, ret);
return ret;
}
*rtc_sec = rtc_tm_to_time64(rtc_tm);
pr_info("%s: <rtc > %s -> %lld\n", __func__, pon_alarm_get_timeformat(*rtc_tm), *rtc_sec);
return ret;
}
static void pon_calculate_offset(struct timespec64 *delta, struct timespec64 ktm_ts, time64_t rtc_sec)
{
set_normalized_timespec64(delta,
(time64_t)ktm_ts.tv_sec - rtc_sec,
(s64)ktm_ts.tv_nsec);
}
static time64_t set_rtc_alm_sec(struct timespec64 delta, struct rtc_wkalrm *alm)
{
time64_t rtc_alm_sec = rtc_tm_to_time64(&alm->time) - delta.tv_sec;
rtc_alm_sec = (rtc_alm_sec & ~RTC_ALM_SEC_MASK) | ((alm->enabled - 1) << (ALARM_ENABLE_MULTIPLIER));
return rtc_alm_sec;
}
static void pon_convert_utc_to_rtc(struct timespec64 delta, struct rtc_wkalrm *alm)
{
time64_t rtc_alm_sec = set_rtc_alm_sec(delta, alm);
alm->enabled = 1;
rtc_time64_to_tm(rtc_alm_sec, &alm->time);
pr_info("%s: <alrm> %s -> %lld\n", __func__, pon_alarm_get_timeformat(alm->time), rtc_alm_sec);
}
static void pon_alarm_parse_data(char *alarm_data, struct rtc_wkalrm *alm)
{
char buf_ptr[BOOTALM_BIT_TOTAL + 1] = {0,};
struct rtc_time rtc_tm;
struct timespec64 delta, ktm_ts;
time64_t rtc_sec;
int ret;
alarm_data[BOOTALM_BIT_TOTAL] = '\0';
strlcpy(buf_ptr, alarm_data, BOOTALM_BIT_TOTAL+1);
pon_get_alarm_time(alm, buf_ptr);
if (!alm->enabled) {
pr_info("%s: pon alarm is not enabled!\n", __func__);
return;
}
pon_read_kernel_time(&ktm_ts, alm);
ret = pon_read_current_time(&rtc_sec, &rtc_tm);
if (ret < 0) {
pr_info("%s: pon read current time fail!\n", __func__);
return;
}
pon_calculate_offset(&delta, ktm_ts, rtc_sec);
pon_convert_utc_to_rtc(delta, alm);
}
static enum pon_alarm_state check_pon_alarm_state(time64_t rtc_secs, time64_t secs_pwron, time64_t *data)
{
if (rtc_secs < secs_pwron) {
if (secs_pwron - rtc_secs > PON_ALARM_POLL_TIME)
return PON_ALARM_DISTANT;
if (data)
*data = secs_pwron - rtc_secs;
return PON_ALARM_NEAR;
} else if (rtc_secs <= secs_pwron + PON_ALARM_BOOTING_TIME) {
if (data)
*data = rtc_secs + 10;
return PON_ALARM_EXPIRED;
} else {
return PON_ALARM_OVER;
}
}
static int pon_alarm_check_state(time64_t *data)
{
struct rtc_time tm;
time64_t rtc_secs;
time64_t secs_pwron;
enum pon_alarm_state state = PON_ALARM_NEAR;
int ret;
ret = rtc_read_time(pon_alarm.rtcdev, &tm);
if (ret) {
pr_err("%s: rtc read failed.\n", __func__);
return PON_ALARM_ERROR;
}
rtc_secs = rtc_tm_to_time64(&tm);
secs_pwron = rtc_tm_to_time64(&pon_alarm.val.time);
state = check_pon_alarm_state(rtc_secs, secs_pwron, data);
pr_info("%s: rtc:%lld, alrm:%lld[%d]\n", __func__, rtc_secs, secs_pwron, state);
return state;
}
static void pon_alarm_check_func(struct work_struct *work)
{
struct pon_alarm_info *pon_info = container_of(work, struct pon_alarm_info, check_func);
int res;
time64_t remain;
res = pon_alarm_check_state(&remain);
if (res <= PON_ALARM_NEAR) {
ktime_t ktime;
if (res == PON_ALARM_DISTANT)
remain = PON_ALARM_POLL_TIME;
ktime = ns_to_ktime((u64)remain * NSEC_PER_SEC);
alarm_start_relative(&pon_info->check_poll, ktime);
pr_info("%s: next %lld s\n", __func__, remain);
} else if (res == PON_ALARM_EXPIRED) {
__pm_stay_awake(pon_info->ws);
pon_info->triggered = 1;
}
}
static enum alarmtimer_restart pon_alarm_check_callback(struct alarm *alarm, ktime_t now)
{
struct pon_alarm_info *pon_info = container_of(alarm, struct pon_alarm_info, check_poll);
schedule_work(&pon_info->check_func);
return ALARMTIMER_NORESTART;
}
static int pon_alarm_open(struct inode *inode, struct file *file)
{
file->private_data = NULL;
return 0;
}
static ssize_t pon_alarm_read(struct file *file,
char __user *buff, size_t count, loff_t *ppos)
{
char trigger = (pon_alarm.triggered) ? '1' : '0';
if (copy_to_user((void __user *)buff, &trigger, 1)) {
pr_err("%s: read failed.\n", __func__);
return -EFAULT;
}
pr_info("%s: trigger=%c\n", __func__, trigger);
return 1;
}
static long pon_alarm_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
{
int ret = 0;
struct alarm_timespec data;
struct rtc_wkalrm alm;
pr_info("%s: cmd=%08x\n", __func__, cmd);
switch (ANDROID_ALARM_BASE_CMD(cmd)) {
case ANDROID_ALARM_SET_ALARM_BOOT:
if (copy_from_user(data.alarm, (void __user *)arg, 14)) {
ret = -EFAULT;
pr_err("%s: SET ret=%d\n", __func__, ret);
return ret;
}
pon_alarm_parse_data(data.alarm, &alm);
pon_alarm_set(&alm);
break;
}
return ret;
}
static int pon_alarm_release(struct inode *inode, struct file *file)
{
return 0;
}
static const struct file_operations pon_alarm_fops = {
.owner = THIS_MODULE,
.open = pon_alarm_open,
.read = pon_alarm_read,
.unlocked_ioctl = pon_alarm_ioctl,
.release = pon_alarm_release,
};
static struct miscdevice pon_alarm_device = {
.minor = MISC_DYNAMIC_MINOR,
.name = "power_on_alarm",
.fops = &pon_alarm_fops,
};
static int __init pon_alarm_init(void)
{
struct rtc_device *rtcdev;
int ret;
rtcdev = alarmtimer_get_rtcdev();
if (!rtcdev) {
pr_err("%s: no rtcdev (defer)\n", __func__);
return -ENODEV;
}
ret = misc_register(&pon_alarm_device);
if (ret) {
pr_err("%s: ret=%d, fail to register misc device\n", __func__, ret);
return ret;
}
pr_info("%s: rtcalarm=%u, lpcharge=%u\n", __func__, rtcalarm, lpcharge);
pon_alarm.rtcdev = rtcdev;
pon_alarm.lpm_mode = lpcharge;
pon_alarm.triggered = 0;
pon_alarm.val.enabled = (rtcalarm) ? 1 : 0;
rtc_time64_to_tm((time64_t)rtcalarm, &pon_alarm.val.time);
if (pon_alarm.lpm_mode && pon_alarm.val.enabled) {
pr_info("%s: reserve pmic alarm\n", __func__);
pmic_rtc_setalarm(&pon_alarm.val);
pon_alarm.ws = wakeup_source_register(pon_alarm.dev, "PON_ALARM");
alarm_init(&pon_alarm.check_poll, ALARM_REALTIME, pon_alarm_check_callback);
INIT_WORK(&pon_alarm.check_func, pon_alarm_check_func);
alarm_start_relative(&pon_alarm.check_poll, ns_to_ktime(PON_ALARM_START_POLL_TIME));
}
pr_info("%s: done\n", __func__);
return 0;
}
static void __exit pon_alarm_exit(void)
{
misc_deregister(&pon_alarm_device);
if (pon_alarm.lpm_mode && pon_alarm.val.enabled) {
pr_info("%s: clear for lpm_mode\n", __func__);
cancel_work_sync(&pon_alarm.check_func);
alarm_cancel(&pon_alarm.check_poll);
wakeup_source_unregister(pon_alarm.ws);
}
}
module_init(pon_alarm_init);
module_exit(pon_alarm_exit);
MODULE_ALIAS("platform:sec_pon_alarm");
MODULE_DESCRIPTION("SEC_PON_ALARM driver");
MODULE_LICENSE("GPL v2");