nt9856x/BSP/linux-kernel/drivers/rtc/rtc-na51055.c
2023-03-28 15:07:53 +08:00

1074 lines
26 KiB
C
Executable File

/*
* driver/rtc/rtc-na51055.c
*
* Author: howard_chang@novatek.com.tw
* Created: Feb 26, 2019
* Copyright: Novatek Inc.
*
*/
#include <linux/module.h>
#include <linux/rtc.h>
#include <linux/platform_device.h>
#include <linux/interrupt.h>
#include <linux/irq.h>
#include <linux/io.h>
#include <linux/slab.h>
#include <mach/nvt-io.h>
#include <plat/rtc_reg.h>
#include <plat/rtc_int.h>
#include <plat/hardware.h>
#include <linux/clk.h>
#include <linux/workqueue.h>
#include <linux/delay.h>
#include <linux/semaphore.h>
#include <linux/proc_fs.h>
#include <linux/of.h>
#define DRV_VERSION "1.11.030"
#ifdef CONFIG_RTC_NVT_PWBC_SDT_USER
#define RTC_PWBC_SDT_ISR_DISABLE 1
#else
#define RTC_PWBC_SDT_ISR_DISABLE 0
#endif
static struct completion cset_completion;
static struct semaphore rtc_sem;
static unsigned int _REGIOBASE;
static int is_power_lost;
#define loc_cpu() down(&rtc_sem);
#define unl_cpu() up(&rtc_sem);
void rtc_wait_pwr_alarm_done(void);
void rtc_trigger_cset(void);
static const unsigned short rtc_ydays[2][13] = {
/* Normal years */
{ 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365 },
/* Leap years */
{ 0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335, 366 }
};
struct nvt_rtc_priv {
struct rtc_device *rtc;
struct proc_dir_entry *pproc_pwbc;
struct proc_dir_entry *pproc_data;
struct delayed_work cset_work;
};
static void rtc_setreg(uint32_t offset, REGVALUE value)
{
nvt_writel(value, _REGIOBASE + offset);
}
static REGVALUE rtc_getreg(uint32_t offset)
{
return nvt_readl(_REGIOBASE + offset);
}
static void nvt_rtc_add_one_sec(u32* sec, u32* min, u32* hour, u32* days)
{
if (*sec == 59) {
if (*min == 59) {
*hour += 1;
*min = 0;
*sec = 0;
} else {
*min += 1;
*sec = 0;
}
} else {
*sec += 1;
}
if (*hour > 23) {
*hour = 0;
*days += 1;
}
}
void nvt_rtc_power_control(int reboot_sec)
{
union RTC_PWBC_REG pwbc_reg;
union RTC_CTRL_REG ctrl_reg;
if (reboot_sec) {
union RTC_TIMER_REG timer_reg;
union RTC_DAYKEY_REG daykey_reg;
union RTC_PWRALM_REG pwralm_reg;
union RTC_OSCAN_REG oscan_reg;
int i;
u32 sec, min, hour, days;
timer_reg.reg = rtc_getreg(RTC_TIMER_REG_OFS);
daykey_reg.reg = rtc_getreg(RTC_DAYKEY_REG_OFS);
sec = timer_reg.bit.sec;
min = timer_reg.bit.min;
hour = timer_reg.bit.hour;
days = daykey_reg.bit.day;
for (i = 0;i < reboot_sec; i++)
nvt_rtc_add_one_sec(&sec, &min, &hour, &days);
if (!wait_for_completion_timeout(&cset_completion, msecs_to_jiffies(1000))) {
printk("RTC completion timeout, plz check RTC 32K osc or PWR_SW or VCC_VBAT\n");
return;
}
ctrl_reg.reg = rtc_getreg(RTC_CTRL_REG_OFS);
ctrl_reg.bit.cset = 0;
ctrl_reg.bit.pwralarmtime_sel = 1;
ctrl_reg.bit.pwralarmday_sel = 1;
ctrl_reg.bit.alarm_inten = 1;
rtc_setreg(RTC_CTRL_REG_OFS, ctrl_reg.reg);
pwralm_reg.bit.sec = sec;
pwralm_reg.bit.min = min;
pwralm_reg.bit.hour = hour;
pwralm_reg.bit.day = days & 0x1F;
rtc_setreg(RTC_PWRALM_REG_OFS, pwralm_reg.reg);
/*Set OSC analog parameter*/
oscan_reg.reg = rtc_getreg(RTC_OSCAN_REG_OFS);
oscan_reg.bit.osc_analogcfg = RTC_INT_OSC_ANALOG_CFG;
rtc_setreg(RTC_OSCAN_REG_OFS, oscan_reg.reg);
rtc_trigger_cset();
rtc_wait_pwr_alarm_done();
pwbc_reg.reg = rtc_getreg(RTC_PWBC_REG_OFS);
pwbc_reg.bit.pwralarm_en = 1;
rtc_setreg(RTC_PWBC_REG_OFS, pwbc_reg.reg);
}
loc_cpu();
if (!wait_for_completion_timeout(&cset_completion, msecs_to_jiffies(1000))) {
printk("RTC completion timeout, plz check RTC 32K osc or PWR_SW or VCC_VBAT\n");
unl_cpu();
return;
}
rtc_wait_pwr_alarm_done();
pwbc_reg.reg = 0x0;
pwbc_reg.bit.reset_sdt_timer = 1;
rtc_setreg(RTC_PWBC_REG_OFS, pwbc_reg.reg);
pwbc_reg.reg = 0x0;
pwbc_reg.bit.pwr_off = 1;
rtc_setreg(RTC_PWBC_REG_OFS, pwbc_reg.reg);
printk("%s: If not power off, plz check 32K crystal or pad PWR_EN\r\n", __func__);
}
/*
//add by Y_S WU 2014.5.22 CTS default time
static struct rtc_time default_tm = {
.tm_year = (2014 - 1900), // year 2014
.tm_mon = 1, // month 2
.tm_mday = 5, // day 5
.tm_hour = 12,
.tm_min = 0,
.tm_sec = 0
};
*/
static void rtc_cset_work(struct work_struct *work)
{
complete(&cset_completion);
}
static irqreturn_t rtc_update_handler(int irq, void *data)
{
union RTC_STATUS_REG status_reg;
union RTC_PWBC_REG pwbc_reg;
union RTC_PWBCSTS_REG pwbcsts_reg;
struct nvt_rtc_priv *priv = (struct nvt_rtc_priv*)data;
status_reg.reg = rtc_getreg(RTC_STATUS_REG_OFS);
if (status_reg.bit.alarm_sts || status_reg.bit.cset_sts) {
rtc_setreg(RTC_STATUS_REG_OFS, status_reg.reg);
/*CSET done*/
if (status_reg.bit.cset_sts) {
schedule_delayed_work(&priv->cset_work, msecs_to_jiffies(8));
}
}
pwbcsts_reg.reg = rtc_getreg(RTC_PWBCSTS_REG_OFS);
pwbc_reg.reg = rtc_getreg(RTC_PWBC_REG_OFS);
pwbcsts_reg.reg &= pwbc_reg.reg;
if (pwbcsts_reg.reg) {
rtc_setreg(RTC_PWBCSTS_REG_OFS, pwbcsts_reg.reg);
if (pwbcsts_reg.bit.pwr_sw1_sts) {
// reset shutdown timer to prevent SW1 shutdown system
if (pwbc_reg.bit.reset_sdt_timer == 0) {
pwbc_reg.bit.reset_sdt_timer = 1;
rtc_setreg(RTC_PWBC_REG_OFS, pwbc_reg.reg);
}
}
}
return IRQ_HANDLED;
}
void rtc_trigger_cset(void)
{
ulong timeout;
union RTC_STATUS_REG status_reg;
union RTC_CTRL_REG ctrl_reg;
/*Wait for RTC SRST done*/
timeout = jiffies + msecs_to_jiffies(1000);
do {
status_reg.reg = rtc_getreg(RTC_STATUS_REG_OFS);
if (time_after_eq(jiffies, timeout)) {
printk("RTC s/w reset timeout, plz check RTC 32K osc or PWR_SW or VCC_VBAT\n");
return;
}
} while (status_reg.bit.srst_sts == 1);
/*Wait for RTC is ready for next CSET*/
timeout = jiffies + msecs_to_jiffies(1000);
do {
ctrl_reg.reg = rtc_getreg(RTC_CTRL_REG_OFS);
if (time_after_eq(jiffies, timeout)) {
printk("RTC cset timeout, plz check RTC 32K osc or PWR_SW or VCC_VBAT\n");
return;
}
} while (ctrl_reg.bit.cset == 1);
/*Trigger CSET*/
ctrl_reg.reg = rtc_getreg(RTC_CTRL_REG_OFS);
ctrl_reg.bit.cset = 1;
ctrl_reg.bit.cset_inten = 1;
rtc_setreg(RTC_CTRL_REG_OFS, ctrl_reg.reg);
}
void rtc_wait_pwr_alarm_done(void)
{
ulong timeout;
union RTC_PWBC_REG pwbc_reg;
timeout = jiffies + msecs_to_jiffies(1000);
do
{
pwbc_reg.reg = rtc_getreg(RTC_PWBC_REG_OFS);
if (time_after_eq(jiffies, timeout)) {
printk("RTC cset timeout, plz check RTC 32K osc or PWR_SW or VCC_VBAT\n");
return;
}
} while ((pwbc_reg.bit.pwralarm_en == 1) || \
(pwbc_reg.bit.pwralarm_dis == 1));
}
#ifdef CONFIG_RTC_INTF_DEV
static int nvt_rtc_ioctl(struct device *dev, unsigned int cmd, \
unsigned long arg)
{
/*
switch(cmd) {
case RTC_AIE_ON:
pr_info("RTC_AIE_ON\n");
break;
case RTC_AIE_OFF:
pr_info("RTC_AIE_OFF\n");
break;
case RTC_UIE_ON:
pr_info("RTC_UIE_ON\n");
break;
case RTC_UIE_OFF:
pr_info("RTC_UIE_OFF\n");
break;
case RTC_WIE_ON:
pr_info("RTC_WIE_ON\n");
break;
case RTC_WIE_OFF:
pr_info("RTC_WIE_OFF\n");
break;
case RTC_ALM_SET:
pr_info("RTC_ALM_SET\n");
break;
case RTC_ALM_READ:
pr_info("RTC_ALM_READ\n");
break;
case RTC_RD_TIME:
pr_info("RTC_RD_TIME\n");
break;
case RTC_SET_TIME:
pr_info("RTC_SET_TIME\n");
break;
case RTC_IRQP_READ:
pr_info("RTC_IRQP_READ\n");
break;
case RTC_IRQP_SET:
pr_info("RTC_IRQP_SET\n");
break;
case RTC_EPOCH_READ:
pr_info("RTC_EPOCH_READ\n");
break;
case RTC_EPOCH_SET:
pr_info("RTC_EPOCH_SET\n");
break;
case RTC_WKALM_SET:
pr_info("RTC_WKALM_SET\n");
break;
case RTC_WKALM_RD:
pr_info("RTC_WKALM_RD\n");
break;
case RTC_PLL_SET:
pr_info("RTC_PLL_SET\n");
break;
case RTC_PLL_GET:
pr_info("RTC_PLL_GET\n");
break;
default:
pr_info("unknown rtc ioctl :0X%X\n",cmd);
}
*/
return 0;
}
#else
#define nvt_rtc_ioctl NULL
#endif
#ifdef CONFIG_PROC_FS
static int nvt_rtc_proc(struct device *dev, struct seq_file *seq)
{
return 0;
}
#else
#define nvt_rtc_proc NULL
#endif
static int nvt_rtc_reset(struct device *dev)
{
ulong timeout;
union RTC_STATUS_REG status_reg;
union RTC_CTRL_REG ctrl_reg;
// Wait for previous SRST done
do {
status_reg.reg = rtc_getreg(RTC_STATUS_REG_OFS);
} while (status_reg.bit.srst_sts);
// Wait for RTC is ready for next CSET
do {
ctrl_reg.reg = rtc_getreg(RTC_CTRL_REG_OFS);
} while (ctrl_reg.bit.cset);
// Do software reset
ctrl_reg.reg = 0;
ctrl_reg.bit.srst = 1;
rtc_setreg(RTC_CTRL_REG_OFS, ctrl_reg.reg);
// Wait for RTC SRST finished, RTC can do other operations except SRST
timeout = jiffies + msecs_to_jiffies(1000);
do {
ctrl_reg.reg = rtc_getreg(RTC_CTRL_REG_OFS);
if (time_after_eq(jiffies, timeout)) {
dev_err(dev, "RTC s/w reset timeout, plz check RTC 32K osc or PWR_SW\n");
return -ETXTBSY;
}
} while(ctrl_reg.bit.srst);
return 0;
}
static int nvt_rtc_chk_power(struct device *dev)
{
int ret = 0;
int year_looper;
uint32_t days = 0;
union RTC_DAYKEY_REG daykey_reg;
union RTC_CTRL_REG ctrl_reg;
union RTC_OSCAN_REG oscan_reg;
union RTC_PWBC_REG pwbc_reg;
const int RESET_YEAR = 1970 - 1900;
const int RESET_MON = 0;
// set power alarm default disabled
pwbc_reg.reg = rtc_getreg(RTC_PWBC_REG_OFS);
pwbc_reg.bit.pwralarm_dis = 1;
rtc_setreg(RTC_PWBC_REG_OFS, pwbc_reg.reg);
for (year_looper = 0; year_looper < RESET_YEAR; year_looper++)
days += rtc_ydays[is_leap_year(year_looper + 1900)][12];
days += rtc_ydays[is_leap_year(year_looper + 1900)][RESET_MON];
daykey_reg.reg = rtc_getreg(RTC_DAYKEY_REG_OFS);
if (daykey_reg.bit.key & RTC_INT_KEY_POR) {
is_power_lost = 1;
printk("%s: RTC power lost detected\r\n", __func__);
ret = nvt_rtc_reset(dev);
if (ret) return ret;
} else {
oscan_reg.reg = rtc_getreg(RTC_OSCAN_REG_OFS);
if (oscan_reg.bit.osc_analogcfg == RTC_INT_OSC_ANALOG_CFG) {
return 0;
}
is_power_lost = 1;
printk("%s: RTC ANA not expected 0x%x\r\n", __func__, oscan_reg.bit.osc_analogcfg);
ret = nvt_rtc_reset(dev);
if (ret) return ret;
}
loc_cpu();
/*wait for cset done*/
wait_for_completion(&cset_completion);
ctrl_reg.reg = rtc_getreg(RTC_CTRL_REG_OFS);
ctrl_reg.bit.cset = 0;
ctrl_reg.bit.key_sel = 1;
ctrl_reg.bit.pwralarmday_sel = 1;
ctrl_reg.bit.day_sel = 1;
rtc_setreg(RTC_CTRL_REG_OFS, ctrl_reg.reg);
daykey_reg.reg = rtc_getreg(RTC_DAYKEY_REG_OFS);
daykey_reg.bit.key = RTC_INT_KEY;
rtc_setreg(RTC_DAYKEY_REG_OFS, daykey_reg.reg);
/*Set OSC analog parameter*/
oscan_reg.reg = rtc_getreg(RTC_OSCAN_REG_OFS);
oscan_reg.bit.osc_analogcfg = RTC_INT_OSC_ANALOG_CFG;
rtc_setreg(RTC_OSCAN_REG_OFS, oscan_reg.reg);
/*Set default year*/
daykey_reg.reg = rtc_getreg(RTC_DAYKEY_REG_OFS);
daykey_reg.bit.day = days;
rtc_setreg(RTC_DAYKEY_REG_OFS, daykey_reg.reg);
rtc_trigger_cset();
unl_cpu();
return ret;
}
static int nvt_rtc_read_time(struct device *dev, struct rtc_time *tm)
{
uint32_t days, months, years, month_days;
union RTC_TIMER_REG timer_reg;
union RTC_DAYKEY_REG daykey_reg;
loc_cpu();
/*wait for cset done*/
if (!wait_for_completion_timeout(&cset_completion, msecs_to_jiffies(1000))) {
dev_err(dev, "RTC completion timeout, plz check RTC 32K osc or PWR_SW or VCC_VBAT\n");
unl_cpu();
return -ETXTBSY;
}
complete(&cset_completion);
unl_cpu();
timer_reg.reg = rtc_getreg(RTC_TIMER_REG_OFS);
daykey_reg.reg = rtc_getreg(RTC_DAYKEY_REG_OFS);
days = daykey_reg.bit.day;
for (years = 0; days >= rtc_ydays[is_leap_year(years + 1900)][12]; \
years++) {
days -= rtc_ydays[is_leap_year(years + 1900)][12];
}
for (months = 1; months < 13; months++) {
if (days <= rtc_ydays[is_leap_year(years + 1900)][months]) {
days -= rtc_ydays[is_leap_year(years + 1900)][months-1];
months--;
break;
}
}
month_days = rtc_ydays[is_leap_year(years + 1900)][months+1] - \
rtc_ydays[is_leap_year(years + 1900)][months];
if (days == month_days) {
months++;
days = 1;
} else
days++; /*Align linux time format*/
tm->tm_sec = timer_reg.bit.sec;
tm->tm_min = timer_reg.bit.min;
tm->tm_hour = timer_reg.bit.hour;
tm->tm_mday = days;
tm->tm_mon = months;
tm->tm_year = years;
pr_debug("after read time: sec = %d, min = %d, hour = %d, mday = %d," \
"mon = %d, year = %d, wday = %d, yday = %d," \
"\n", tm->tm_sec, tm->tm_min, tm->tm_hour, tm->tm_mday, \
tm->tm_mon, tm->tm_year, tm->tm_wday, tm->tm_yday);
return rtc_valid_tm(tm);
}
static int nvt_rtc_set_time(struct device *dev, struct rtc_time *tm)
{
int year_looper, ret;
uint32_t days = 0;
union RTC_TIMER_REG timer_reg;
union RTC_DAYKEY_REG daykey_reg;
union RTC_CTRL_REG ctrl_reg;
pr_debug("kernel set time: sec = %d, min = %d, hour = %d, mday = %d," \
"mon = %d, year = %d, wday = %d, yday = %d," \
"\n", tm->tm_sec, tm->tm_min, tm->tm_hour, tm->tm_mday, \
tm->tm_mon, tm->tm_year, tm->tm_wday, tm->tm_yday);
ret = rtc_valid_tm(tm);
if (ret < 0)
return ret;
for (year_looper = 0; year_looper < tm->tm_year; year_looper++)
days += rtc_ydays[is_leap_year(year_looper + 1900)][12];
days += rtc_ydays[is_leap_year(year_looper + 1900)][tm->tm_mon];
tm->tm_mday--; /*subtract the day which is not ended*/
days += tm->tm_mday;
loc_cpu();
/*wait for cset done*/
if (!wait_for_completion_timeout(&cset_completion, msecs_to_jiffies(1000))) {
dev_err(dev, "RTC completion timeout, plz check RTC 32K osc or PWR_SW or VCC_VBAT\n");
unl_cpu();
return -ETXTBSY;
}
ctrl_reg.reg = rtc_getreg(RTC_CTRL_REG_OFS);
ctrl_reg.bit.cset = 0;
ctrl_reg.bit.day_sel = 1;
ctrl_reg.bit.time_sel = 1;
rtc_setreg(RTC_CTRL_REG_OFS, ctrl_reg.reg);
timer_reg.reg = 0;
timer_reg.bit.sec = tm->tm_sec;
timer_reg.bit.min = tm->tm_min;
timer_reg.bit.hour = tm->tm_hour;
rtc_setreg(RTC_TIMER_REG_OFS, timer_reg.reg);
daykey_reg.reg = rtc_getreg(RTC_DAYKEY_REG_OFS);
daykey_reg.bit.day = days;
rtc_setreg(RTC_DAYKEY_REG_OFS, daykey_reg.reg);
rtc_trigger_cset();
unl_cpu();
return ret;
}
static int nvt_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alrm)
{
struct rtc_time *tm = &alrm->time;
uint32_t days, alarm_days, months, years, month_days;
union RTC_PWRALM_REG pwralarm_reg;
union RTC_DAYKEY_REG daykey_reg;
loc_cpu();
/*wait for cset done*/
if (!wait_for_completion_timeout(&cset_completion, msecs_to_jiffies(1000))) {
dev_err(dev, "RTC completion timeout, plz check RTC 32K osc or PWR_SW or VCC_VBAT\n");
unl_cpu();
return -ETXTBSY;
}
complete(&cset_completion);
unl_cpu();
pwralarm_reg.reg = rtc_getreg(RTC_PWRALM_REG_OFS);
daykey_reg.reg = rtc_getreg(RTC_DAYKEY_REG_OFS);
alarm_days = pwralarm_reg.bit.day;
days = daykey_reg.bit.day;
if ((days & RTC_INT_MAX_PWRALARM_DAY) > alarm_days) {
days = ((days & ~RTC_INT_MAX_PWRALARM_DAY) | alarm_days) \
+ (1 << RTC_INT_PRWALARM_DAY_SHIFT);
} else
days = ((days & ~RTC_INT_MAX_PWRALARM_DAY) | alarm_days);
for (years = 0; days >= rtc_ydays[is_leap_year(years + 1900)][12]; \
years++) {
days -= rtc_ydays[is_leap_year(years + 1900)][12];
}
for (months = 1; months < 13; months++) {
if (days <= rtc_ydays[is_leap_year(years + 1900)][months]) {
days -= rtc_ydays[is_leap_year(years + 1900)][months-1];
months--;
break;
}
}
month_days = rtc_ydays[is_leap_year(years + 1900)][months+1] - \
rtc_ydays[is_leap_year(years + 1900)][months];
if (days == month_days) {
months++;
days = 1;
} else
days++; /*Align linux time format*/
tm->tm_sec = pwralarm_reg.bit.sec;
tm->tm_min = pwralarm_reg.bit.min;
tm->tm_hour = pwralarm_reg.bit.hour;
tm->tm_mday = days;
tm->tm_mon = months;
tm->tm_year = years;
pr_debug("read alarm time: sec = %d, min = %d, hour = %d, mday = %d," \
"mon = %d, year = %d, wday = %d, yday = %d," \
"\n", tm->tm_sec, tm->tm_min, tm->tm_hour, tm->tm_mday, \
tm->tm_mon, tm->tm_year, tm->tm_wday, tm->tm_yday);
return rtc_valid_tm(tm);
}
static int nvt_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alrm)
{
struct rtc_time *tm = &alrm->time;
int year_looper, ret;
uint32_t current_days = 0, alarm_days = 0;
union RTC_DAYKEY_REG daykey_reg;
union RTC_CTRL_REG ctrl_reg;
union RTC_PWRALM_REG pwralarm_reg;
union RTC_PWBC_REG pwbc_reg;
union RTC_OSCAN_REG oscan_reg;
daykey_reg.reg = rtc_getreg(RTC_DAYKEY_REG_OFS);
current_days = daykey_reg.bit.day;
pr_debug("set alarm time: sec = %d, min = %d, hour = %d, mday = %d," \
"mon = %d, year = %d, wday = %d, yday = %d," \
"\n", tm->tm_sec, tm->tm_min, tm->tm_hour, tm->tm_mday, \
tm->tm_mon, tm->tm_year, tm->tm_wday, tm->tm_yday);
ret = rtc_valid_tm(tm);
if (ret < 0)
return ret;
for (year_looper = 0; year_looper < tm->tm_year; year_looper++)
alarm_days += rtc_ydays[is_leap_year(year_looper + 1900)][12];
alarm_days += rtc_ydays[is_leap_year(year_looper + 1900)][tm->tm_mon];
tm->tm_mday--; /*subtract the day which is not ended*/
alarm_days += tm->tm_mday;
/*Check date parameter for maximum register setting*/
if ((alarm_days < current_days) || ((alarm_days - current_days) > \
RTC_INT_MAX_PWRALARM_DAY)) {
pr_err("Invalid parameter!\n");
return E_PAR;
}
if (!wait_for_completion_timeout(&cset_completion, msecs_to_jiffies(1000))) {
dev_err(dev, "RTC completion timeout, plz check RTC 32K osc or PWR_SW or VCC_VBAT\n");
return -ETXTBSY;
}
ctrl_reg.reg = rtc_getreg(RTC_CTRL_REG_OFS);
ctrl_reg.bit.cset = 0;
ctrl_reg.bit.pwralarmtime_sel = 1;
ctrl_reg.bit.pwralarmday_sel = 1;
ctrl_reg.bit.alarm_inten = 1;
rtc_setreg(RTC_CTRL_REG_OFS, ctrl_reg.reg);
pwralarm_reg.reg = 0;
pwralarm_reg.bit.sec = tm->tm_sec;
pwralarm_reg.bit.min = tm->tm_min;
pwralarm_reg.bit.hour = tm->tm_hour;
pwralarm_reg.bit.day = alarm_days;
rtc_setreg(RTC_PWRALM_REG_OFS, pwralarm_reg.reg);
/*Set OSC analog parameter*/
oscan_reg.reg = rtc_getreg(RTC_OSCAN_REG_OFS);
oscan_reg.bit.osc_analogcfg = RTC_INT_OSC_ANALOG_CFG;
rtc_setreg(RTC_OSCAN_REG_OFS, oscan_reg.reg);
rtc_trigger_cset();
rtc_wait_pwr_alarm_done();
pwbc_reg.reg = rtc_getreg(RTC_PWBC_REG_OFS);
pwbc_reg.bit.pwralarm_en = 1;
rtc_setreg(RTC_PWBC_REG_OFS, pwbc_reg.reg);
return ret;
}
static int pwbc_init(void)
{
#if (RTC_PWBC_SDT_ISR_DISABLE == 0)
union RTC_PWBC_REG pwbc_ctrl_reg;
pwbc_ctrl_reg.reg = rtc_getreg(RTC_PWBC_REG_OFS);
pwbc_ctrl_reg.bit.pwr_sw1_inten = 1;
rtc_setreg(RTC_PWBC_REG_OFS, pwbc_ctrl_reg.reg);
#endif
return 0;
}
/*
PWBC related
*/
static int pwbc_proc_show(struct seq_file *seq, void *v)
{
union RTC_PWBCSTS_REG pwbcsts_reg;
pwbcsts_reg.reg = rtc_getreg(RTC_PWBCSTS_REG_OFS);
seq_printf(seq, "power switch 1 value %d\n", pwbcsts_reg.bit.pwr_sw1_pin);
seq_printf(seq, "power switch 2 value %d\n", pwbcsts_reg.bit.pwr_sw2_pin);
seq_printf(seq, "power switch 3 value %d\n", pwbcsts_reg.bit.pwr_sw3_pin);
seq_printf(seq, "power switch 4 value %d\n", pwbcsts_reg.bit.pwr_sw4_pin);
seq_printf(seq, "power on source ");
if (pwbcsts_reg.bit.pwronsrc_sw1) {
seq_printf(seq, "SW1\r\n");
} else if (pwbcsts_reg.bit.pwronsrc_sw2) {
seq_printf(seq, "SW2\r\n");
} else if (pwbcsts_reg.bit.pwronsrc_sw3) {
seq_printf(seq, "SW3\r\n");
} else if (pwbcsts_reg.bit.pwronsrc_sw4) {
seq_printf(seq, "SW4\r\n");
} else {
seq_printf(seq, "None\r\n");
}
seq_printf(seq, "Power lost: %s\r\n", (is_power_lost?"Yes":"No"));
return 0;
}
static int pwbc_proc_open(struct inode *inode, struct file *file)
{
return single_open(file, pwbc_proc_show, NULL);
}
#define PWBC_MAX_CMD_LENGTH 30
static ssize_t pwbc_proc_cmd_write(struct file *file, const char __user *buf,
size_t size, loff_t *off)
{
int val;
int len = size;
char cmd_line[PWBC_MAX_CMD_LENGTH];
char *cmdstr = cmd_line;
const char delimiters[] = {' ', 0x0A, 0x0D, '\0'};
char *p_arg;
union RTC_PWBC_REG pwbc_reg;
// check command length
if (len > (PWBC_MAX_CMD_LENGTH - 1)) {
printk("%s: Command length %d is too long\n", __func__, len);
return -EINVAL;
}
// copy command string from user space
if (copy_from_user(cmd_line, buf, len)) {
return -EINVAL;
}
if (len == 0)
cmd_line[0] = '\0';
else
cmd_line[len - 1] = '\0';
p_arg = strsep(&cmdstr, delimiters);
sscanf(p_arg, "%x", &val);
loc_cpu();
pwbc_reg.reg = rtc_getreg(RTC_PWBC_REG_OFS);
// reset shutdown timer to prevent SW1 shutdown system
if (pwbc_reg.bit.reset_sdt_timer == 0) {
pwbc_reg.bit.reset_sdt_timer = 1;
rtc_setreg(RTC_PWBC_REG_OFS, pwbc_reg.reg);
}
unl_cpu();
return size;
}
static struct file_operations proc_pwbc_fops = {
.owner = THIS_MODULE,
.open = pwbc_proc_open,
.read = seq_read,
.llseek = seq_lseek,
.release = single_release,
.write = pwbc_proc_cmd_write,
};
static const struct rtc_class_ops nvt_rtc_ops = {
.ioctl = nvt_rtc_ioctl,
.proc = nvt_rtc_proc,
.read_time = nvt_rtc_read_time,
.set_time = nvt_rtc_set_time,
.read_alarm = nvt_rtc_read_alarm,
.set_alarm = nvt_rtc_set_alarm,
};
/*
User data related
*/
static int data_proc_show(struct seq_file *seq, void *v)
{
union RTC_DATA_REG data_reg;
loc_cpu();
/*wait for cset done*/
if (!wait_for_completion_timeout(&cset_completion, msecs_to_jiffies(1000))) {
printk("RTC completion timeout, plz check RTC 32K osc or PWR_SW or VCC_VBAT\n");
unl_cpu();
return -ETXTBSY;
}
complete(&cset_completion);
unl_cpu();
data_reg.reg = rtc_getreg(RTC_DATA_REG_OFS);
seq_printf(seq, "0x%x\n", data_reg.bit.data0);
return 0;
}
static int data_proc_open(struct inode *inode, struct file *file)
{
return single_open(file, data_proc_show, NULL);
}
#define MAX_CMD_LENGTH 30
static ssize_t data_proc_cmd_write(struct file *file, const char __user *buf,
size_t size, loff_t *off)
{
int val;
int len = size;
char cmd_line[MAX_CMD_LENGTH];
char *cmdstr = cmd_line;
const char delimiters[] = {' ', 0x0A, 0x0D, '\0'};
char *p_arg;
union RTC_CTRL_REG ctrl_reg;
union RTC_DATA_REG data_reg;
// check command length
if (len > (MAX_CMD_LENGTH - 1)) {
printk("%s: Command length %d is too long\n", __func__, len);
return -EINVAL;
}
// copy command string from user space
if (copy_from_user(cmd_line, buf, len)) {
return -EINVAL;
}
if (len == 0)
cmd_line[0] = '\0';
else
cmd_line[len - 1] = '\0';
p_arg = strsep(&cmdstr, delimiters);
sscanf(p_arg, "%x", &val);
loc_cpu();
/*wait for cset done*/
if (!wait_for_completion_timeout(&cset_completion, msecs_to_jiffies(1000))) {
printk("RTC completion timeout, plz check RTC 32K osc or PWR_SW or VCC_VBAT\n");
unl_cpu();
return -ETXTBSY;
}
ctrl_reg.reg = rtc_getreg(RTC_CTRL_REG_OFS);
ctrl_reg.bit.cset = 0;
ctrl_reg.bit.data0_sel = 1;
rtc_setreg(RTC_CTRL_REG_OFS, ctrl_reg.reg);
data_reg.reg = rtc_getreg(RTC_DATA_REG_OFS);
data_reg.bit.data0 = val;
rtc_setreg(RTC_DATA_REG_OFS, data_reg.reg);
rtc_trigger_cset();
unl_cpu();
return size;
}
static struct file_operations proc_data_fops = {
.owner = THIS_MODULE,
.open = data_proc_open,
.read = seq_read,
.llseek = seq_lseek,
.release = single_release,
.write = data_proc_cmd_write,
};
static int nvt_rtc_probe(struct platform_device *pdev)
{
struct rtc_device *rtc;
struct nvt_rtc_priv *priv;
struct resource *memres = NULL;
struct proc_dir_entry *pentry = NULL;
int ret = 0, irq = 0;
/* setup resource */
memres = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (unlikely(!memres)) {
dev_err(&pdev->dev, "failed to get resource\n");
return -ENXIO;
}
_REGIOBASE = (u32) devm_ioremap_resource(&pdev->dev, memres);;
if (unlikely(_REGIOBASE == 0)) {
dev_err(&pdev->dev, "failed to get io memory\n");
goto out;
}
priv = kzalloc(sizeof(struct nvt_rtc_priv), GFP_KERNEL);
if (!priv)
return -ENOMEM;
INIT_DELAYED_WORK(&priv->cset_work, rtc_cset_work);
is_power_lost = 0;
platform_set_drvdata(pdev, priv);
device_init_wakeup(&pdev->dev, 1);
pwbc_init();
/*Set default HW configuration*/
/*Define what data type will be used, RCW_DEF or manual define*/
irq = platform_get_irq(pdev, 0);
if (irq < 0) {
dev_err(&pdev->dev, "No IRQ resource!\n");
goto out;
}
ret = devm_request_irq(&pdev->dev, irq, rtc_update_handler, 0, pdev->name,
priv);
if (ret < 0) {
dev_err(&pdev->dev, "Failed to request IRQ: #%d: %d\n", irq, ret);
return ret;
}
init_completion(&cset_completion);
sema_init(&rtc_sem, 1);
complete(&cset_completion);
nvt_rtc_chk_power(&pdev->dev);
pentry = proc_create("pwbc", S_IRUGO | S_IXUGO, NULL, &proc_pwbc_fops);
if (pentry == NULL) {
dev_err(&pdev->dev, "failed to create pwbc proc!\n");
ret = -EINVAL;
goto out;
}
priv->pproc_pwbc = pentry;
pentry = proc_create("rtc_data", S_IRUGO | S_IXUGO, NULL, &proc_data_fops);
if (pentry == NULL) {
dev_err(&pdev->dev, "failed to create data proc!\n");
ret = -EINVAL;
goto out_proc_pwbc;
}
priv->pproc_data = pentry;
rtc = rtc_device_register("nvt_rtc", &pdev->dev,
&nvt_rtc_ops, THIS_MODULE);
if (IS_ERR(rtc)) {
ret = PTR_ERR(rtc);
goto out_proc_data;
}
priv->rtc = rtc;
return 0;
out_proc_data:
if (priv->pproc_data) {
proc_remove(priv->pproc_data);
}
out_proc_pwbc:
if (priv->pproc_pwbc) {
proc_remove(priv->pproc_pwbc);
}
out:
if (priv->rtc)
rtc_device_unregister(priv->rtc);
kfree(priv);
return ret;
}
static int nvt_rtc_remove(struct platform_device *pdev)
{
struct nvt_rtc_priv *priv = platform_get_drvdata(pdev);
if (priv->pproc_data) {
proc_remove(priv->pproc_data);
}
if (priv->pproc_pwbc) {
proc_remove(priv->pproc_pwbc);
}
rtc_device_unregister(priv->rtc);
kfree(priv);
return 0;
}
#ifdef CONFIG_OF
static const struct of_device_id nvt_rtc_of_dt_ids[] = {
{ .compatible = "nvt,nvt_rtc", },
{},
};
#endif
static struct platform_driver nvt_rtc_platform_driver = {
.driver = {
.name = "nvt_rtc",
.owner = THIS_MODULE,
#ifdef CONFIG_OF
.of_match_table = nvt_rtc_of_dt_ids,
#endif
},
.probe = nvt_rtc_probe,
.remove = nvt_rtc_remove,
};
static int __init nvt_rtc_init(void)
{
int ret;
ret = platform_driver_register(&nvt_rtc_platform_driver);
return ret;
}
static void __exit nvt_rtc_exit(void)
{
platform_driver_unregister(&nvt_rtc_platform_driver);
}
MODULE_AUTHOR("Novatek");
MODULE_DESCRIPTION("nvt RTC driver");
MODULE_LICENSE("GPL");
MODULE_VERSION(DRV_VERSION);
MODULE_ALIAS("platform:rtc-nvt");
module_init(nvt_rtc_init);
module_exit(nvt_rtc_exit);