Hi, On 04/07/2018 12:03:45+0530, Keerthy wrote: > Prepare rtc driver for rtc-only mode. This involes splitting the power-off > function so that an external driver can initiate the programming of > setting the power_off to be triggered in the next second. > I'm sorry, I don't see the point, can't you use of_device_is_system_power_controller and set the correct pm_power_off callback? > Signed-off-by: Keerthy <j-keerthy@xxxxxx> > --- > drivers/rtc/interface.c | 12 ++++ > drivers/rtc/rtc-omap.c | 164 ++++++++++++++++++++++++++++++++++-------------- > include/linux/rtc.h | 2 + > 3 files changed, 130 insertions(+), 48 deletions(-) > > diff --git a/drivers/rtc/interface.c b/drivers/rtc/interface.c > index 6d4012d..d8b70f0 100644 > --- a/drivers/rtc/interface.c > +++ b/drivers/rtc/interface.c > @@ -1139,3 +1139,15 @@ int rtc_set_offset(struct rtc_device *rtc, long offset) > trace_rtc_set_offset(offset, ret); > return ret; > } > + > +/** > + * rtc_power_off_program - Some of the rtc are hooked on to PMIC_EN > + * line and can be used to power off the SoC. > + * > + * Kernel interface to program rtc to power off > + */ > +void rtc_power_off_program(struct rtc_device *rtc) > +{ > + rtc->ops->power_off_program(rtc->dev.parent); > +} > +EXPORT_SYMBOL_GPL(rtc_power_off_program); > diff --git a/drivers/rtc/rtc-omap.c b/drivers/rtc/rtc-omap.c > index 3908639..4dcee1c 100644 > --- a/drivers/rtc/rtc-omap.c > +++ b/drivers/rtc/rtc-omap.c > @@ -29,6 +29,7 @@ > #include <linux/pinctrl/pinconf-generic.h> > #include <linux/platform_device.h> > #include <linux/pm_runtime.h> > +#include <linux/regulator/machine.h> > #include <linux/rtc.h> > > /* > @@ -131,6 +132,8 @@ > #define KICK0_VALUE 0x83e70b13 > #define KICK1_VALUE 0x95a4f1e0 > > +#define SHUTDOWN_TIME_SEC 1 > + > struct omap_rtc; > > struct omap_rtc_device_type { > @@ -415,6 +418,77 @@ static int omap_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alm) > > static struct omap_rtc *omap_rtc_power_off_rtc; > > +/** > + * omap_rtc_power_off_program: Set the pmic power off sequence. The RTC > + * generates pmic_pwr_enable control, which can be used to control an external > + * PMIC. > + */ > +void omap_rtc_power_off_program(struct device *dev) > +{ > + u32 val; > + struct rtc_time tm; > + unsigned long time; > + int seconds; > + > + omap_rtc_power_off_rtc->type->unlock(omap_rtc_power_off_rtc); > + > + /* Clear any existing ALARM2 event */ > + rtc_writel(omap_rtc_power_off_rtc, OMAP_RTC_STATUS_REG, > + OMAP_RTC_STATUS_ALARM2); > + > + pr_info("System will go to power_off state in approx. %d second\n", > + SHUTDOWN_TIME_SEC); > + > +again: > + /* Read rtc time */ > + tm.tm_sec = rtc_read(omap_rtc_power_off_rtc, OMAP_RTC_SECONDS_REG); > + seconds = tm.tm_sec; > + tm.tm_min = rtc_read(omap_rtc_power_off_rtc, OMAP_RTC_MINUTES_REG); > + tm.tm_hour = rtc_read(omap_rtc_power_off_rtc, OMAP_RTC_HOURS_REG); > + tm.tm_mday = rtc_read(omap_rtc_power_off_rtc, OMAP_RTC_DAYS_REG); > + tm.tm_mon = rtc_read(omap_rtc_power_off_rtc, OMAP_RTC_MONTHS_REG); > + tm.tm_year = rtc_read(omap_rtc_power_off_rtc, OMAP_RTC_YEARS_REG); > + bcd2tm(&tm); > + > + /* Convert Gregorian date to seconds since 01-01-1970 00:00:00 */ > + rtc_tm_to_time(&tm, &time); > + > + /* Convert seconds since 01-01-1970 00:00:00 to Gregorian date */ > + rtc_time_to_tm(time + SHUTDOWN_TIME_SEC, &tm); > + > + if (tm2bcd(&tm) < 0) > + return; > + > + /* After wait_not_busy, we have at least 15us until the next second. */ > + rtc_wait_not_busy(omap_rtc_power_off_rtc); > + > + /* Our calculations started right before the rollover, try again */ > + if (seconds != rtc_read(omap_rtc_power_off_rtc, OMAP_RTC_SECONDS_REG)) > + goto again; > + > + /* > + * pmic_pwr_enable is controlled by means of ALARM2 event. So here > + * programming alarm2 expiry time and enabling alarm2 interrupt > + */ > + rtc_write(omap_rtc_power_off_rtc, OMAP_RTC_ALARM2_SECONDS_REG, > + tm.tm_sec); > + rtc_write(omap_rtc_power_off_rtc, OMAP_RTC_ALARM2_MINUTES_REG, > + tm.tm_min); > + rtc_write(omap_rtc_power_off_rtc, OMAP_RTC_ALARM2_HOURS_REG, > + tm.tm_hour); > + rtc_write(omap_rtc_power_off_rtc, OMAP_RTC_ALARM2_DAYS_REG, > + tm.tm_mday); > + rtc_write(omap_rtc_power_off_rtc, OMAP_RTC_ALARM2_MONTHS_REG, > + tm.tm_mon); > + rtc_write(omap_rtc_power_off_rtc, OMAP_RTC_ALARM2_YEARS_REG, > + tm.tm_year); > + > + /* Enable alarm2 interrupt */ > + val = rtc_readl(omap_rtc_power_off_rtc, OMAP_RTC_INTERRUPTS_REG); > + rtc_writel(omap_rtc_power_off_rtc, OMAP_RTC_INTERRUPTS_REG, val | > + OMAP_RTC_INTERRUPTS_IT_ALARM2); > +} > + > /* > * omap_rtc_poweroff: RTC-controlled power off > * > @@ -431,45 +505,19 @@ static int omap_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alm) > */ > static void omap_rtc_power_off(void) > { > - struct omap_rtc *rtc = omap_rtc_power_off_rtc; > - struct rtc_time tm; > - unsigned long now; > + struct rtc_device *rtc = omap_rtc_power_off_rtc->rtc; > u32 val; > > - rtc->type->unlock(rtc); > - /* enable pmic_power_en control */ > - val = rtc_readl(rtc, OMAP_RTC_PMIC_REG); > - rtc_writel(rtc, OMAP_RTC_PMIC_REG, val | OMAP_RTC_PMIC_POWER_EN_EN); > - > - /* set alarm two seconds from now */ > - omap_rtc_read_time_raw(rtc, &tm); > - bcd2tm(&tm); > - rtc_tm_to_time(&tm, &now); > - rtc_time_to_tm(now + 2, &tm); > - > - if (tm2bcd(&tm) < 0) { > - dev_err(&rtc->rtc->dev, "power off failed\n"); > - return; > - } > - > - rtc_wait_not_busy(rtc); > + regulator_suspend_prepare(PM_SUSPEND_MAX); > + omap_rtc_power_off_rtc->type->unlock(omap_rtc_power_off_rtc); > + omap_rtc_power_off_program(rtc->dev.parent); > > - rtc_write(rtc, OMAP_RTC_ALARM2_SECONDS_REG, tm.tm_sec); > - rtc_write(rtc, OMAP_RTC_ALARM2_MINUTES_REG, tm.tm_min); > - rtc_write(rtc, OMAP_RTC_ALARM2_HOURS_REG, tm.tm_hour); > - rtc_write(rtc, OMAP_RTC_ALARM2_DAYS_REG, tm.tm_mday); > - rtc_write(rtc, OMAP_RTC_ALARM2_MONTHS_REG, tm.tm_mon); > - rtc_write(rtc, OMAP_RTC_ALARM2_YEARS_REG, tm.tm_year); > - > - /* > - * enable ALARM2 interrupt > - * > - * NOTE: this fails on AM3352 if rtc_write (writeb) is used > - */ > - val = rtc_read(rtc, OMAP_RTC_INTERRUPTS_REG); > - rtc_writel(rtc, OMAP_RTC_INTERRUPTS_REG, > - val | OMAP_RTC_INTERRUPTS_IT_ALARM2); > - rtc->type->lock(rtc); > + /* Set PMIC power enable and EXT_WAKEUP in case PB power on is used */ > + val = rtc_readl(omap_rtc_power_off_rtc, OMAP_RTC_PMIC_REG); > + val |= OMAP_RTC_PMIC_POWER_EN_EN | OMAP_RTC_PMIC_EXT_WKUP_POL(0) | > + OMAP_RTC_PMIC_EXT_WKUP_EN(0); > + rtc_writel(omap_rtc_power_off_rtc, OMAP_RTC_PMIC_REG, val); > + omap_rtc_power_off_rtc->type->lock(omap_rtc_power_off_rtc); > > /* > * Wait for alarm to trigger (within two seconds) and external PMIC to > @@ -477,6 +525,17 @@ static void omap_rtc_power_off(void) > * (e.g. debounce circuits). > */ > mdelay(2500); > + > + pr_err("rtc_power_off failed, bailing out.\n"); > +} > + > +static void omap_rtc_cleanup_pm_power_off(struct omap_rtc *rtc) > +{ > + if (pm_power_off == omap_rtc_power_off && > + omap_rtc_power_off_rtc == rtc) { > + pm_power_off = NULL; > + omap_rtc_power_off_rtc = NULL; > + } > } > > static const struct rtc_class_ops omap_rtc_ops = { > @@ -485,6 +544,7 @@ static void omap_rtc_power_off(void) > .read_alarm = omap_rtc_read_alarm, > .set_alarm = omap_rtc_set_alarm, > .alarm_irq_enable = omap_rtc_alarm_irq_enable, > + .power_off_program = omap_rtc_power_off_program, > }; > > static const struct omap_rtc_device_type omap_rtc_default_type = { > @@ -838,6 +898,11 @@ static int omap_rtc_probe(struct platform_device *pdev) > rtc->type->lock(rtc); > > device_init_wakeup(&pdev->dev, true); > + omap_rtc_power_off_rtc = rtc; > + > + if (rtc->is_pmic_controller) > + if (!pm_power_off) > + pm_power_off = omap_rtc_power_off; > > rtc->rtc = devm_rtc_allocate_device(&pdev->dev); > if (IS_ERR(rtc->rtc)) { > @@ -887,6 +952,7 @@ static int omap_rtc_probe(struct platform_device *pdev) > return 0; > > err: > + omap_rtc_cleanup_pm_power_off(rtc); > clk_disable_unprepare(rtc->clk); > device_init_wakeup(&pdev->dev, false); > rtc->type->lock(rtc); > @@ -901,11 +967,7 @@ static int omap_rtc_remove(struct platform_device *pdev) > struct omap_rtc *rtc = platform_get_drvdata(pdev); > u8 reg; > > - if (pm_power_off == omap_rtc_power_off && > - omap_rtc_power_off_rtc == rtc) { > - pm_power_off = NULL; > - omap_rtc_power_off_rtc = NULL; > - } > + omap_rtc_cleanup_pm_power_off(rtc); > > device_init_wakeup(&pdev->dev, 0); > > @@ -993,14 +1055,20 @@ static void omap_rtc_shutdown(struct platform_device *pdev) > struct omap_rtc *rtc = platform_get_drvdata(pdev); > u8 mask; > > - /* > - * Keep the ALARM interrupt enabled to allow the system to power up on > - * alarm events. > - */ > rtc->type->unlock(rtc); > - mask = rtc_read(rtc, OMAP_RTC_INTERRUPTS_REG); > - mask &= OMAP_RTC_INTERRUPTS_IT_ALARM; > - rtc_write(rtc, OMAP_RTC_INTERRUPTS_REG, mask); > + /* If rtc does not control PMIC then no need to enable ALARM */ > + if (!rtc->is_pmic_controller) { > + rtc_write(rtc, OMAP_RTC_INTERRUPTS_REG, 0); > + } else { > + /* > + * Keep the ALARM interrupt enabled to allow the system to > + * power up on alarm events. > + */ > + mask = rtc_read(rtc, OMAP_RTC_INTERRUPTS_REG); > + mask &= OMAP_RTC_INTERRUPTS_IT_ALARM; > + rtc_write(rtc, OMAP_RTC_INTERRUPTS_REG, mask); > + } > + > rtc->type->lock(rtc); > } > > diff --git a/include/linux/rtc.h b/include/linux/rtc.h > index 6268208..f17bc6a 100644 > --- a/include/linux/rtc.h > +++ b/include/linux/rtc.h > @@ -85,6 +85,7 @@ struct rtc_class_ops { > int (*alarm_irq_enable)(struct device *, unsigned int enabled); > int (*read_offset)(struct device *, long *offset); > int (*set_offset)(struct device *, long offset); > + void (*power_off_program)(struct device *dev); > }; > > typedef struct rtc_task { > @@ -229,6 +230,7 @@ int rtc_timer_start(struct rtc_device *rtc, struct rtc_timer *timer, > int rtc_read_offset(struct rtc_device *rtc, long *offset); > int rtc_set_offset(struct rtc_device *rtc, long offset); > void rtc_timer_do_work(struct work_struct *work); > +void rtc_power_off_program(struct rtc_device *rtc); > > static inline bool is_leap_year(unsigned int year) > { > -- > 1.9.1 > -- Alexandre Belloni, Bootlin (formerly Free Electrons) Embedded Linux and Kernel engineering https://bootlin.com -- To unsubscribe from this list: send the line "unsubscribe linux-omap" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html