18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0+ 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * TI OMAP Real Time Clock interface for Linux 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright (C) 2003 MontaVista Software, Inc. 68c2ecf20Sopenharmony_ci * Author: George G. Davis <gdavis@mvista.com> or <source@mvista.com> 78c2ecf20Sopenharmony_ci * 88c2ecf20Sopenharmony_ci * Copyright (C) 2006 David Brownell (new RTC framework) 98c2ecf20Sopenharmony_ci * Copyright (C) 2014 Johan Hovold <johan@kernel.org> 108c2ecf20Sopenharmony_ci */ 118c2ecf20Sopenharmony_ci 128c2ecf20Sopenharmony_ci#include <linux/bcd.h> 138c2ecf20Sopenharmony_ci#include <linux/clk.h> 148c2ecf20Sopenharmony_ci#include <linux/delay.h> 158c2ecf20Sopenharmony_ci#include <linux/init.h> 168c2ecf20Sopenharmony_ci#include <linux/io.h> 178c2ecf20Sopenharmony_ci#include <linux/ioport.h> 188c2ecf20Sopenharmony_ci#include <linux/kernel.h> 198c2ecf20Sopenharmony_ci#include <linux/module.h> 208c2ecf20Sopenharmony_ci#include <linux/of.h> 218c2ecf20Sopenharmony_ci#include <linux/of_device.h> 228c2ecf20Sopenharmony_ci#include <linux/pinctrl/pinctrl.h> 238c2ecf20Sopenharmony_ci#include <linux/pinctrl/pinconf.h> 248c2ecf20Sopenharmony_ci#include <linux/pinctrl/pinconf-generic.h> 258c2ecf20Sopenharmony_ci#include <linux/platform_device.h> 268c2ecf20Sopenharmony_ci#include <linux/pm_runtime.h> 278c2ecf20Sopenharmony_ci#include <linux/rtc.h> 288c2ecf20Sopenharmony_ci#include <linux/rtc/rtc-omap.h> 298c2ecf20Sopenharmony_ci 308c2ecf20Sopenharmony_ci/* 318c2ecf20Sopenharmony_ci * The OMAP RTC is a year/month/day/hours/minutes/seconds BCD clock 328c2ecf20Sopenharmony_ci * with century-range alarm matching, driven by the 32kHz clock. 338c2ecf20Sopenharmony_ci * 348c2ecf20Sopenharmony_ci * The main user-visible ways it differs from PC RTCs are by omitting 358c2ecf20Sopenharmony_ci * "don't care" alarm fields and sub-second periodic IRQs, and having 368c2ecf20Sopenharmony_ci * an autoadjust mechanism to calibrate to the true oscillator rate. 378c2ecf20Sopenharmony_ci * 388c2ecf20Sopenharmony_ci * Board-specific wiring options include using split power mode with 398c2ecf20Sopenharmony_ci * RTC_OFF_NOFF used as the reset signal (so the RTC won't be reset), 408c2ecf20Sopenharmony_ci * and wiring RTC_WAKE_INT (so the RTC alarm can wake the system from 418c2ecf20Sopenharmony_ci * low power modes) for OMAP1 boards (OMAP-L138 has this built into 428c2ecf20Sopenharmony_ci * the SoC). See the BOARD-SPECIFIC CUSTOMIZATION comment. 438c2ecf20Sopenharmony_ci */ 448c2ecf20Sopenharmony_ci 458c2ecf20Sopenharmony_ci/* RTC registers */ 468c2ecf20Sopenharmony_ci#define OMAP_RTC_SECONDS_REG 0x00 478c2ecf20Sopenharmony_ci#define OMAP_RTC_MINUTES_REG 0x04 488c2ecf20Sopenharmony_ci#define OMAP_RTC_HOURS_REG 0x08 498c2ecf20Sopenharmony_ci#define OMAP_RTC_DAYS_REG 0x0C 508c2ecf20Sopenharmony_ci#define OMAP_RTC_MONTHS_REG 0x10 518c2ecf20Sopenharmony_ci#define OMAP_RTC_YEARS_REG 0x14 528c2ecf20Sopenharmony_ci#define OMAP_RTC_WEEKS_REG 0x18 538c2ecf20Sopenharmony_ci 548c2ecf20Sopenharmony_ci#define OMAP_RTC_ALARM_SECONDS_REG 0x20 558c2ecf20Sopenharmony_ci#define OMAP_RTC_ALARM_MINUTES_REG 0x24 568c2ecf20Sopenharmony_ci#define OMAP_RTC_ALARM_HOURS_REG 0x28 578c2ecf20Sopenharmony_ci#define OMAP_RTC_ALARM_DAYS_REG 0x2c 588c2ecf20Sopenharmony_ci#define OMAP_RTC_ALARM_MONTHS_REG 0x30 598c2ecf20Sopenharmony_ci#define OMAP_RTC_ALARM_YEARS_REG 0x34 608c2ecf20Sopenharmony_ci 618c2ecf20Sopenharmony_ci#define OMAP_RTC_CTRL_REG 0x40 628c2ecf20Sopenharmony_ci#define OMAP_RTC_STATUS_REG 0x44 638c2ecf20Sopenharmony_ci#define OMAP_RTC_INTERRUPTS_REG 0x48 648c2ecf20Sopenharmony_ci 658c2ecf20Sopenharmony_ci#define OMAP_RTC_COMP_LSB_REG 0x4c 668c2ecf20Sopenharmony_ci#define OMAP_RTC_COMP_MSB_REG 0x50 678c2ecf20Sopenharmony_ci#define OMAP_RTC_OSC_REG 0x54 688c2ecf20Sopenharmony_ci 698c2ecf20Sopenharmony_ci#define OMAP_RTC_SCRATCH0_REG 0x60 708c2ecf20Sopenharmony_ci#define OMAP_RTC_SCRATCH1_REG 0x64 718c2ecf20Sopenharmony_ci#define OMAP_RTC_SCRATCH2_REG 0x68 728c2ecf20Sopenharmony_ci 738c2ecf20Sopenharmony_ci#define OMAP_RTC_KICK0_REG 0x6c 748c2ecf20Sopenharmony_ci#define OMAP_RTC_KICK1_REG 0x70 758c2ecf20Sopenharmony_ci 768c2ecf20Sopenharmony_ci#define OMAP_RTC_IRQWAKEEN 0x7c 778c2ecf20Sopenharmony_ci 788c2ecf20Sopenharmony_ci#define OMAP_RTC_ALARM2_SECONDS_REG 0x80 798c2ecf20Sopenharmony_ci#define OMAP_RTC_ALARM2_MINUTES_REG 0x84 808c2ecf20Sopenharmony_ci#define OMAP_RTC_ALARM2_HOURS_REG 0x88 818c2ecf20Sopenharmony_ci#define OMAP_RTC_ALARM2_DAYS_REG 0x8c 828c2ecf20Sopenharmony_ci#define OMAP_RTC_ALARM2_MONTHS_REG 0x90 838c2ecf20Sopenharmony_ci#define OMAP_RTC_ALARM2_YEARS_REG 0x94 848c2ecf20Sopenharmony_ci 858c2ecf20Sopenharmony_ci#define OMAP_RTC_PMIC_REG 0x98 868c2ecf20Sopenharmony_ci 878c2ecf20Sopenharmony_ci/* OMAP_RTC_CTRL_REG bit fields: */ 888c2ecf20Sopenharmony_ci#define OMAP_RTC_CTRL_SPLIT BIT(7) 898c2ecf20Sopenharmony_ci#define OMAP_RTC_CTRL_DISABLE BIT(6) 908c2ecf20Sopenharmony_ci#define OMAP_RTC_CTRL_SET_32_COUNTER BIT(5) 918c2ecf20Sopenharmony_ci#define OMAP_RTC_CTRL_TEST BIT(4) 928c2ecf20Sopenharmony_ci#define OMAP_RTC_CTRL_MODE_12_24 BIT(3) 938c2ecf20Sopenharmony_ci#define OMAP_RTC_CTRL_AUTO_COMP BIT(2) 948c2ecf20Sopenharmony_ci#define OMAP_RTC_CTRL_ROUND_30S BIT(1) 958c2ecf20Sopenharmony_ci#define OMAP_RTC_CTRL_STOP BIT(0) 968c2ecf20Sopenharmony_ci 978c2ecf20Sopenharmony_ci/* OMAP_RTC_STATUS_REG bit fields: */ 988c2ecf20Sopenharmony_ci#define OMAP_RTC_STATUS_POWER_UP BIT(7) 998c2ecf20Sopenharmony_ci#define OMAP_RTC_STATUS_ALARM2 BIT(7) 1008c2ecf20Sopenharmony_ci#define OMAP_RTC_STATUS_ALARM BIT(6) 1018c2ecf20Sopenharmony_ci#define OMAP_RTC_STATUS_1D_EVENT BIT(5) 1028c2ecf20Sopenharmony_ci#define OMAP_RTC_STATUS_1H_EVENT BIT(4) 1038c2ecf20Sopenharmony_ci#define OMAP_RTC_STATUS_1M_EVENT BIT(3) 1048c2ecf20Sopenharmony_ci#define OMAP_RTC_STATUS_1S_EVENT BIT(2) 1058c2ecf20Sopenharmony_ci#define OMAP_RTC_STATUS_RUN BIT(1) 1068c2ecf20Sopenharmony_ci#define OMAP_RTC_STATUS_BUSY BIT(0) 1078c2ecf20Sopenharmony_ci 1088c2ecf20Sopenharmony_ci/* OMAP_RTC_INTERRUPTS_REG bit fields: */ 1098c2ecf20Sopenharmony_ci#define OMAP_RTC_INTERRUPTS_IT_ALARM2 BIT(4) 1108c2ecf20Sopenharmony_ci#define OMAP_RTC_INTERRUPTS_IT_ALARM BIT(3) 1118c2ecf20Sopenharmony_ci#define OMAP_RTC_INTERRUPTS_IT_TIMER BIT(2) 1128c2ecf20Sopenharmony_ci 1138c2ecf20Sopenharmony_ci/* OMAP_RTC_OSC_REG bit fields: */ 1148c2ecf20Sopenharmony_ci#define OMAP_RTC_OSC_32KCLK_EN BIT(6) 1158c2ecf20Sopenharmony_ci#define OMAP_RTC_OSC_SEL_32KCLK_SRC BIT(3) 1168c2ecf20Sopenharmony_ci#define OMAP_RTC_OSC_OSC32K_GZ_DISABLE BIT(4) 1178c2ecf20Sopenharmony_ci 1188c2ecf20Sopenharmony_ci/* OMAP_RTC_IRQWAKEEN bit fields: */ 1198c2ecf20Sopenharmony_ci#define OMAP_RTC_IRQWAKEEN_ALARM_WAKEEN BIT(1) 1208c2ecf20Sopenharmony_ci 1218c2ecf20Sopenharmony_ci/* OMAP_RTC_PMIC bit fields: */ 1228c2ecf20Sopenharmony_ci#define OMAP_RTC_PMIC_POWER_EN_EN BIT(16) 1238c2ecf20Sopenharmony_ci#define OMAP_RTC_PMIC_EXT_WKUP_EN(x) BIT(x) 1248c2ecf20Sopenharmony_ci#define OMAP_RTC_PMIC_EXT_WKUP_POL(x) BIT(4 + x) 1258c2ecf20Sopenharmony_ci 1268c2ecf20Sopenharmony_ci/* OMAP_RTC_KICKER values */ 1278c2ecf20Sopenharmony_ci#define KICK0_VALUE 0x83e70b13 1288c2ecf20Sopenharmony_ci#define KICK1_VALUE 0x95a4f1e0 1298c2ecf20Sopenharmony_ci 1308c2ecf20Sopenharmony_cistruct omap_rtc; 1318c2ecf20Sopenharmony_ci 1328c2ecf20Sopenharmony_cistruct omap_rtc_device_type { 1338c2ecf20Sopenharmony_ci bool has_32kclk_en; 1348c2ecf20Sopenharmony_ci bool has_irqwakeen; 1358c2ecf20Sopenharmony_ci bool has_pmic_mode; 1368c2ecf20Sopenharmony_ci bool has_power_up_reset; 1378c2ecf20Sopenharmony_ci void (*lock)(struct omap_rtc *rtc); 1388c2ecf20Sopenharmony_ci void (*unlock)(struct omap_rtc *rtc); 1398c2ecf20Sopenharmony_ci}; 1408c2ecf20Sopenharmony_ci 1418c2ecf20Sopenharmony_cistruct omap_rtc { 1428c2ecf20Sopenharmony_ci struct rtc_device *rtc; 1438c2ecf20Sopenharmony_ci void __iomem *base; 1448c2ecf20Sopenharmony_ci struct clk *clk; 1458c2ecf20Sopenharmony_ci int irq_alarm; 1468c2ecf20Sopenharmony_ci int irq_timer; 1478c2ecf20Sopenharmony_ci u8 interrupts_reg; 1488c2ecf20Sopenharmony_ci bool is_pmic_controller; 1498c2ecf20Sopenharmony_ci bool has_ext_clk; 1508c2ecf20Sopenharmony_ci bool is_suspending; 1518c2ecf20Sopenharmony_ci const struct omap_rtc_device_type *type; 1528c2ecf20Sopenharmony_ci struct pinctrl_dev *pctldev; 1538c2ecf20Sopenharmony_ci}; 1548c2ecf20Sopenharmony_ci 1558c2ecf20Sopenharmony_cistatic inline u8 rtc_read(struct omap_rtc *rtc, unsigned int reg) 1568c2ecf20Sopenharmony_ci{ 1578c2ecf20Sopenharmony_ci return readb(rtc->base + reg); 1588c2ecf20Sopenharmony_ci} 1598c2ecf20Sopenharmony_ci 1608c2ecf20Sopenharmony_cistatic inline u32 rtc_readl(struct omap_rtc *rtc, unsigned int reg) 1618c2ecf20Sopenharmony_ci{ 1628c2ecf20Sopenharmony_ci return readl(rtc->base + reg); 1638c2ecf20Sopenharmony_ci} 1648c2ecf20Sopenharmony_ci 1658c2ecf20Sopenharmony_cistatic inline void rtc_write(struct omap_rtc *rtc, unsigned int reg, u8 val) 1668c2ecf20Sopenharmony_ci{ 1678c2ecf20Sopenharmony_ci writeb(val, rtc->base + reg); 1688c2ecf20Sopenharmony_ci} 1698c2ecf20Sopenharmony_ci 1708c2ecf20Sopenharmony_cistatic inline void rtc_writel(struct omap_rtc *rtc, unsigned int reg, u32 val) 1718c2ecf20Sopenharmony_ci{ 1728c2ecf20Sopenharmony_ci writel(val, rtc->base + reg); 1738c2ecf20Sopenharmony_ci} 1748c2ecf20Sopenharmony_ci 1758c2ecf20Sopenharmony_cistatic void am3352_rtc_unlock(struct omap_rtc *rtc) 1768c2ecf20Sopenharmony_ci{ 1778c2ecf20Sopenharmony_ci rtc_writel(rtc, OMAP_RTC_KICK0_REG, KICK0_VALUE); 1788c2ecf20Sopenharmony_ci rtc_writel(rtc, OMAP_RTC_KICK1_REG, KICK1_VALUE); 1798c2ecf20Sopenharmony_ci} 1808c2ecf20Sopenharmony_ci 1818c2ecf20Sopenharmony_cistatic void am3352_rtc_lock(struct omap_rtc *rtc) 1828c2ecf20Sopenharmony_ci{ 1838c2ecf20Sopenharmony_ci rtc_writel(rtc, OMAP_RTC_KICK0_REG, 0); 1848c2ecf20Sopenharmony_ci rtc_writel(rtc, OMAP_RTC_KICK1_REG, 0); 1858c2ecf20Sopenharmony_ci} 1868c2ecf20Sopenharmony_ci 1878c2ecf20Sopenharmony_cistatic void default_rtc_unlock(struct omap_rtc *rtc) 1888c2ecf20Sopenharmony_ci{ 1898c2ecf20Sopenharmony_ci} 1908c2ecf20Sopenharmony_ci 1918c2ecf20Sopenharmony_cistatic void default_rtc_lock(struct omap_rtc *rtc) 1928c2ecf20Sopenharmony_ci{ 1938c2ecf20Sopenharmony_ci} 1948c2ecf20Sopenharmony_ci 1958c2ecf20Sopenharmony_ci/* 1968c2ecf20Sopenharmony_ci * We rely on the rtc framework to handle locking (rtc->ops_lock), 1978c2ecf20Sopenharmony_ci * so the only other requirement is that register accesses which 1988c2ecf20Sopenharmony_ci * require BUSY to be clear are made with IRQs locally disabled 1998c2ecf20Sopenharmony_ci */ 2008c2ecf20Sopenharmony_cistatic void rtc_wait_not_busy(struct omap_rtc *rtc) 2018c2ecf20Sopenharmony_ci{ 2028c2ecf20Sopenharmony_ci int count; 2038c2ecf20Sopenharmony_ci u8 status; 2048c2ecf20Sopenharmony_ci 2058c2ecf20Sopenharmony_ci /* BUSY may stay active for 1/32768 second (~30 usec) */ 2068c2ecf20Sopenharmony_ci for (count = 0; count < 50; count++) { 2078c2ecf20Sopenharmony_ci status = rtc_read(rtc, OMAP_RTC_STATUS_REG); 2088c2ecf20Sopenharmony_ci if (!(status & OMAP_RTC_STATUS_BUSY)) 2098c2ecf20Sopenharmony_ci break; 2108c2ecf20Sopenharmony_ci udelay(1); 2118c2ecf20Sopenharmony_ci } 2128c2ecf20Sopenharmony_ci /* now we have ~15 usec to read/write various registers */ 2138c2ecf20Sopenharmony_ci} 2148c2ecf20Sopenharmony_ci 2158c2ecf20Sopenharmony_cistatic irqreturn_t rtc_irq(int irq, void *dev_id) 2168c2ecf20Sopenharmony_ci{ 2178c2ecf20Sopenharmony_ci struct omap_rtc *rtc = dev_id; 2188c2ecf20Sopenharmony_ci unsigned long events = 0; 2198c2ecf20Sopenharmony_ci u8 irq_data; 2208c2ecf20Sopenharmony_ci 2218c2ecf20Sopenharmony_ci irq_data = rtc_read(rtc, OMAP_RTC_STATUS_REG); 2228c2ecf20Sopenharmony_ci 2238c2ecf20Sopenharmony_ci /* alarm irq? */ 2248c2ecf20Sopenharmony_ci if (irq_data & OMAP_RTC_STATUS_ALARM) { 2258c2ecf20Sopenharmony_ci rtc->type->unlock(rtc); 2268c2ecf20Sopenharmony_ci rtc_write(rtc, OMAP_RTC_STATUS_REG, OMAP_RTC_STATUS_ALARM); 2278c2ecf20Sopenharmony_ci rtc->type->lock(rtc); 2288c2ecf20Sopenharmony_ci events |= RTC_IRQF | RTC_AF; 2298c2ecf20Sopenharmony_ci } 2308c2ecf20Sopenharmony_ci 2318c2ecf20Sopenharmony_ci /* 1/sec periodic/update irq? */ 2328c2ecf20Sopenharmony_ci if (irq_data & OMAP_RTC_STATUS_1S_EVENT) 2338c2ecf20Sopenharmony_ci events |= RTC_IRQF | RTC_UF; 2348c2ecf20Sopenharmony_ci 2358c2ecf20Sopenharmony_ci rtc_update_irq(rtc->rtc, 1, events); 2368c2ecf20Sopenharmony_ci 2378c2ecf20Sopenharmony_ci return IRQ_HANDLED; 2388c2ecf20Sopenharmony_ci} 2398c2ecf20Sopenharmony_ci 2408c2ecf20Sopenharmony_cistatic int omap_rtc_alarm_irq_enable(struct device *dev, unsigned int enabled) 2418c2ecf20Sopenharmony_ci{ 2428c2ecf20Sopenharmony_ci struct omap_rtc *rtc = dev_get_drvdata(dev); 2438c2ecf20Sopenharmony_ci u8 reg, irqwake_reg = 0; 2448c2ecf20Sopenharmony_ci 2458c2ecf20Sopenharmony_ci local_irq_disable(); 2468c2ecf20Sopenharmony_ci rtc_wait_not_busy(rtc); 2478c2ecf20Sopenharmony_ci reg = rtc_read(rtc, OMAP_RTC_INTERRUPTS_REG); 2488c2ecf20Sopenharmony_ci if (rtc->type->has_irqwakeen) 2498c2ecf20Sopenharmony_ci irqwake_reg = rtc_read(rtc, OMAP_RTC_IRQWAKEEN); 2508c2ecf20Sopenharmony_ci 2518c2ecf20Sopenharmony_ci if (enabled) { 2528c2ecf20Sopenharmony_ci reg |= OMAP_RTC_INTERRUPTS_IT_ALARM; 2538c2ecf20Sopenharmony_ci irqwake_reg |= OMAP_RTC_IRQWAKEEN_ALARM_WAKEEN; 2548c2ecf20Sopenharmony_ci } else { 2558c2ecf20Sopenharmony_ci reg &= ~OMAP_RTC_INTERRUPTS_IT_ALARM; 2568c2ecf20Sopenharmony_ci irqwake_reg &= ~OMAP_RTC_IRQWAKEEN_ALARM_WAKEEN; 2578c2ecf20Sopenharmony_ci } 2588c2ecf20Sopenharmony_ci rtc_wait_not_busy(rtc); 2598c2ecf20Sopenharmony_ci rtc->type->unlock(rtc); 2608c2ecf20Sopenharmony_ci rtc_write(rtc, OMAP_RTC_INTERRUPTS_REG, reg); 2618c2ecf20Sopenharmony_ci if (rtc->type->has_irqwakeen) 2628c2ecf20Sopenharmony_ci rtc_write(rtc, OMAP_RTC_IRQWAKEEN, irqwake_reg); 2638c2ecf20Sopenharmony_ci rtc->type->lock(rtc); 2648c2ecf20Sopenharmony_ci local_irq_enable(); 2658c2ecf20Sopenharmony_ci 2668c2ecf20Sopenharmony_ci return 0; 2678c2ecf20Sopenharmony_ci} 2688c2ecf20Sopenharmony_ci 2698c2ecf20Sopenharmony_ci/* this hardware doesn't support "don't care" alarm fields */ 2708c2ecf20Sopenharmony_cistatic void tm2bcd(struct rtc_time *tm) 2718c2ecf20Sopenharmony_ci{ 2728c2ecf20Sopenharmony_ci tm->tm_sec = bin2bcd(tm->tm_sec); 2738c2ecf20Sopenharmony_ci tm->tm_min = bin2bcd(tm->tm_min); 2748c2ecf20Sopenharmony_ci tm->tm_hour = bin2bcd(tm->tm_hour); 2758c2ecf20Sopenharmony_ci tm->tm_mday = bin2bcd(tm->tm_mday); 2768c2ecf20Sopenharmony_ci 2778c2ecf20Sopenharmony_ci tm->tm_mon = bin2bcd(tm->tm_mon + 1); 2788c2ecf20Sopenharmony_ci tm->tm_year = bin2bcd(tm->tm_year - 100); 2798c2ecf20Sopenharmony_ci} 2808c2ecf20Sopenharmony_ci 2818c2ecf20Sopenharmony_cistatic void bcd2tm(struct rtc_time *tm) 2828c2ecf20Sopenharmony_ci{ 2838c2ecf20Sopenharmony_ci tm->tm_sec = bcd2bin(tm->tm_sec); 2848c2ecf20Sopenharmony_ci tm->tm_min = bcd2bin(tm->tm_min); 2858c2ecf20Sopenharmony_ci tm->tm_hour = bcd2bin(tm->tm_hour); 2868c2ecf20Sopenharmony_ci tm->tm_mday = bcd2bin(tm->tm_mday); 2878c2ecf20Sopenharmony_ci tm->tm_mon = bcd2bin(tm->tm_mon) - 1; 2888c2ecf20Sopenharmony_ci /* epoch == 1900 */ 2898c2ecf20Sopenharmony_ci tm->tm_year = bcd2bin(tm->tm_year) + 100; 2908c2ecf20Sopenharmony_ci} 2918c2ecf20Sopenharmony_ci 2928c2ecf20Sopenharmony_cistatic void omap_rtc_read_time_raw(struct omap_rtc *rtc, struct rtc_time *tm) 2938c2ecf20Sopenharmony_ci{ 2948c2ecf20Sopenharmony_ci tm->tm_sec = rtc_read(rtc, OMAP_RTC_SECONDS_REG); 2958c2ecf20Sopenharmony_ci tm->tm_min = rtc_read(rtc, OMAP_RTC_MINUTES_REG); 2968c2ecf20Sopenharmony_ci tm->tm_hour = rtc_read(rtc, OMAP_RTC_HOURS_REG); 2978c2ecf20Sopenharmony_ci tm->tm_mday = rtc_read(rtc, OMAP_RTC_DAYS_REG); 2988c2ecf20Sopenharmony_ci tm->tm_mon = rtc_read(rtc, OMAP_RTC_MONTHS_REG); 2998c2ecf20Sopenharmony_ci tm->tm_year = rtc_read(rtc, OMAP_RTC_YEARS_REG); 3008c2ecf20Sopenharmony_ci} 3018c2ecf20Sopenharmony_ci 3028c2ecf20Sopenharmony_cistatic int omap_rtc_read_time(struct device *dev, struct rtc_time *tm) 3038c2ecf20Sopenharmony_ci{ 3048c2ecf20Sopenharmony_ci struct omap_rtc *rtc = dev_get_drvdata(dev); 3058c2ecf20Sopenharmony_ci 3068c2ecf20Sopenharmony_ci /* we don't report wday/yday/isdst ... */ 3078c2ecf20Sopenharmony_ci local_irq_disable(); 3088c2ecf20Sopenharmony_ci rtc_wait_not_busy(rtc); 3098c2ecf20Sopenharmony_ci omap_rtc_read_time_raw(rtc, tm); 3108c2ecf20Sopenharmony_ci local_irq_enable(); 3118c2ecf20Sopenharmony_ci 3128c2ecf20Sopenharmony_ci bcd2tm(tm); 3138c2ecf20Sopenharmony_ci 3148c2ecf20Sopenharmony_ci return 0; 3158c2ecf20Sopenharmony_ci} 3168c2ecf20Sopenharmony_ci 3178c2ecf20Sopenharmony_cistatic int omap_rtc_set_time(struct device *dev, struct rtc_time *tm) 3188c2ecf20Sopenharmony_ci{ 3198c2ecf20Sopenharmony_ci struct omap_rtc *rtc = dev_get_drvdata(dev); 3208c2ecf20Sopenharmony_ci 3218c2ecf20Sopenharmony_ci tm2bcd(tm); 3228c2ecf20Sopenharmony_ci 3238c2ecf20Sopenharmony_ci local_irq_disable(); 3248c2ecf20Sopenharmony_ci rtc_wait_not_busy(rtc); 3258c2ecf20Sopenharmony_ci 3268c2ecf20Sopenharmony_ci rtc->type->unlock(rtc); 3278c2ecf20Sopenharmony_ci rtc_write(rtc, OMAP_RTC_YEARS_REG, tm->tm_year); 3288c2ecf20Sopenharmony_ci rtc_write(rtc, OMAP_RTC_MONTHS_REG, tm->tm_mon); 3298c2ecf20Sopenharmony_ci rtc_write(rtc, OMAP_RTC_DAYS_REG, tm->tm_mday); 3308c2ecf20Sopenharmony_ci rtc_write(rtc, OMAP_RTC_HOURS_REG, tm->tm_hour); 3318c2ecf20Sopenharmony_ci rtc_write(rtc, OMAP_RTC_MINUTES_REG, tm->tm_min); 3328c2ecf20Sopenharmony_ci rtc_write(rtc, OMAP_RTC_SECONDS_REG, tm->tm_sec); 3338c2ecf20Sopenharmony_ci rtc->type->lock(rtc); 3348c2ecf20Sopenharmony_ci 3358c2ecf20Sopenharmony_ci local_irq_enable(); 3368c2ecf20Sopenharmony_ci 3378c2ecf20Sopenharmony_ci return 0; 3388c2ecf20Sopenharmony_ci} 3398c2ecf20Sopenharmony_ci 3408c2ecf20Sopenharmony_cistatic int omap_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alm) 3418c2ecf20Sopenharmony_ci{ 3428c2ecf20Sopenharmony_ci struct omap_rtc *rtc = dev_get_drvdata(dev); 3438c2ecf20Sopenharmony_ci u8 interrupts; 3448c2ecf20Sopenharmony_ci 3458c2ecf20Sopenharmony_ci local_irq_disable(); 3468c2ecf20Sopenharmony_ci rtc_wait_not_busy(rtc); 3478c2ecf20Sopenharmony_ci 3488c2ecf20Sopenharmony_ci alm->time.tm_sec = rtc_read(rtc, OMAP_RTC_ALARM_SECONDS_REG); 3498c2ecf20Sopenharmony_ci alm->time.tm_min = rtc_read(rtc, OMAP_RTC_ALARM_MINUTES_REG); 3508c2ecf20Sopenharmony_ci alm->time.tm_hour = rtc_read(rtc, OMAP_RTC_ALARM_HOURS_REG); 3518c2ecf20Sopenharmony_ci alm->time.tm_mday = rtc_read(rtc, OMAP_RTC_ALARM_DAYS_REG); 3528c2ecf20Sopenharmony_ci alm->time.tm_mon = rtc_read(rtc, OMAP_RTC_ALARM_MONTHS_REG); 3538c2ecf20Sopenharmony_ci alm->time.tm_year = rtc_read(rtc, OMAP_RTC_ALARM_YEARS_REG); 3548c2ecf20Sopenharmony_ci 3558c2ecf20Sopenharmony_ci local_irq_enable(); 3568c2ecf20Sopenharmony_ci 3578c2ecf20Sopenharmony_ci bcd2tm(&alm->time); 3588c2ecf20Sopenharmony_ci 3598c2ecf20Sopenharmony_ci interrupts = rtc_read(rtc, OMAP_RTC_INTERRUPTS_REG); 3608c2ecf20Sopenharmony_ci alm->enabled = !!(interrupts & OMAP_RTC_INTERRUPTS_IT_ALARM); 3618c2ecf20Sopenharmony_ci 3628c2ecf20Sopenharmony_ci return 0; 3638c2ecf20Sopenharmony_ci} 3648c2ecf20Sopenharmony_ci 3658c2ecf20Sopenharmony_cistatic int omap_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alm) 3668c2ecf20Sopenharmony_ci{ 3678c2ecf20Sopenharmony_ci struct omap_rtc *rtc = dev_get_drvdata(dev); 3688c2ecf20Sopenharmony_ci u8 reg, irqwake_reg = 0; 3698c2ecf20Sopenharmony_ci 3708c2ecf20Sopenharmony_ci tm2bcd(&alm->time); 3718c2ecf20Sopenharmony_ci 3728c2ecf20Sopenharmony_ci local_irq_disable(); 3738c2ecf20Sopenharmony_ci rtc_wait_not_busy(rtc); 3748c2ecf20Sopenharmony_ci 3758c2ecf20Sopenharmony_ci rtc->type->unlock(rtc); 3768c2ecf20Sopenharmony_ci rtc_write(rtc, OMAP_RTC_ALARM_YEARS_REG, alm->time.tm_year); 3778c2ecf20Sopenharmony_ci rtc_write(rtc, OMAP_RTC_ALARM_MONTHS_REG, alm->time.tm_mon); 3788c2ecf20Sopenharmony_ci rtc_write(rtc, OMAP_RTC_ALARM_DAYS_REG, alm->time.tm_mday); 3798c2ecf20Sopenharmony_ci rtc_write(rtc, OMAP_RTC_ALARM_HOURS_REG, alm->time.tm_hour); 3808c2ecf20Sopenharmony_ci rtc_write(rtc, OMAP_RTC_ALARM_MINUTES_REG, alm->time.tm_min); 3818c2ecf20Sopenharmony_ci rtc_write(rtc, OMAP_RTC_ALARM_SECONDS_REG, alm->time.tm_sec); 3828c2ecf20Sopenharmony_ci 3838c2ecf20Sopenharmony_ci reg = rtc_read(rtc, OMAP_RTC_INTERRUPTS_REG); 3848c2ecf20Sopenharmony_ci if (rtc->type->has_irqwakeen) 3858c2ecf20Sopenharmony_ci irqwake_reg = rtc_read(rtc, OMAP_RTC_IRQWAKEEN); 3868c2ecf20Sopenharmony_ci 3878c2ecf20Sopenharmony_ci if (alm->enabled) { 3888c2ecf20Sopenharmony_ci reg |= OMAP_RTC_INTERRUPTS_IT_ALARM; 3898c2ecf20Sopenharmony_ci irqwake_reg |= OMAP_RTC_IRQWAKEEN_ALARM_WAKEEN; 3908c2ecf20Sopenharmony_ci } else { 3918c2ecf20Sopenharmony_ci reg &= ~OMAP_RTC_INTERRUPTS_IT_ALARM; 3928c2ecf20Sopenharmony_ci irqwake_reg &= ~OMAP_RTC_IRQWAKEEN_ALARM_WAKEEN; 3938c2ecf20Sopenharmony_ci } 3948c2ecf20Sopenharmony_ci rtc_write(rtc, OMAP_RTC_INTERRUPTS_REG, reg); 3958c2ecf20Sopenharmony_ci if (rtc->type->has_irqwakeen) 3968c2ecf20Sopenharmony_ci rtc_write(rtc, OMAP_RTC_IRQWAKEEN, irqwake_reg); 3978c2ecf20Sopenharmony_ci rtc->type->lock(rtc); 3988c2ecf20Sopenharmony_ci 3998c2ecf20Sopenharmony_ci local_irq_enable(); 4008c2ecf20Sopenharmony_ci 4018c2ecf20Sopenharmony_ci return 0; 4028c2ecf20Sopenharmony_ci} 4038c2ecf20Sopenharmony_ci 4048c2ecf20Sopenharmony_cistatic struct omap_rtc *omap_rtc_power_off_rtc; 4058c2ecf20Sopenharmony_ci 4068c2ecf20Sopenharmony_ci/** 4078c2ecf20Sopenharmony_ci * omap_rtc_power_off_program: Set the pmic power off sequence. The RTC 4088c2ecf20Sopenharmony_ci * generates pmic_pwr_enable control, which can be used to control an external 4098c2ecf20Sopenharmony_ci * PMIC. 4108c2ecf20Sopenharmony_ci */ 4118c2ecf20Sopenharmony_ciint omap_rtc_power_off_program(struct device *dev) 4128c2ecf20Sopenharmony_ci{ 4138c2ecf20Sopenharmony_ci struct omap_rtc *rtc = omap_rtc_power_off_rtc; 4148c2ecf20Sopenharmony_ci struct rtc_time tm; 4158c2ecf20Sopenharmony_ci unsigned long now; 4168c2ecf20Sopenharmony_ci int seconds; 4178c2ecf20Sopenharmony_ci u32 val; 4188c2ecf20Sopenharmony_ci 4198c2ecf20Sopenharmony_ci rtc->type->unlock(rtc); 4208c2ecf20Sopenharmony_ci /* enable pmic_power_en control */ 4218c2ecf20Sopenharmony_ci val = rtc_readl(rtc, OMAP_RTC_PMIC_REG); 4228c2ecf20Sopenharmony_ci rtc_writel(rtc, OMAP_RTC_PMIC_REG, val | OMAP_RTC_PMIC_POWER_EN_EN); 4238c2ecf20Sopenharmony_ci 4248c2ecf20Sopenharmony_ciagain: 4258c2ecf20Sopenharmony_ci /* Clear any existing ALARM2 event */ 4268c2ecf20Sopenharmony_ci rtc_writel(rtc, OMAP_RTC_STATUS_REG, OMAP_RTC_STATUS_ALARM2); 4278c2ecf20Sopenharmony_ci 4288c2ecf20Sopenharmony_ci /* set alarm one second from now */ 4298c2ecf20Sopenharmony_ci omap_rtc_read_time_raw(rtc, &tm); 4308c2ecf20Sopenharmony_ci seconds = tm.tm_sec; 4318c2ecf20Sopenharmony_ci bcd2tm(&tm); 4328c2ecf20Sopenharmony_ci now = rtc_tm_to_time64(&tm); 4338c2ecf20Sopenharmony_ci rtc_time64_to_tm(now + 1, &tm); 4348c2ecf20Sopenharmony_ci 4358c2ecf20Sopenharmony_ci tm2bcd(&tm); 4368c2ecf20Sopenharmony_ci 4378c2ecf20Sopenharmony_ci rtc_wait_not_busy(rtc); 4388c2ecf20Sopenharmony_ci 4398c2ecf20Sopenharmony_ci rtc_write(rtc, OMAP_RTC_ALARM2_SECONDS_REG, tm.tm_sec); 4408c2ecf20Sopenharmony_ci rtc_write(rtc, OMAP_RTC_ALARM2_MINUTES_REG, tm.tm_min); 4418c2ecf20Sopenharmony_ci rtc_write(rtc, OMAP_RTC_ALARM2_HOURS_REG, tm.tm_hour); 4428c2ecf20Sopenharmony_ci rtc_write(rtc, OMAP_RTC_ALARM2_DAYS_REG, tm.tm_mday); 4438c2ecf20Sopenharmony_ci rtc_write(rtc, OMAP_RTC_ALARM2_MONTHS_REG, tm.tm_mon); 4448c2ecf20Sopenharmony_ci rtc_write(rtc, OMAP_RTC_ALARM2_YEARS_REG, tm.tm_year); 4458c2ecf20Sopenharmony_ci 4468c2ecf20Sopenharmony_ci /* 4478c2ecf20Sopenharmony_ci * enable ALARM2 interrupt 4488c2ecf20Sopenharmony_ci * 4498c2ecf20Sopenharmony_ci * NOTE: this fails on AM3352 if rtc_write (writeb) is used 4508c2ecf20Sopenharmony_ci */ 4518c2ecf20Sopenharmony_ci val = rtc_read(rtc, OMAP_RTC_INTERRUPTS_REG); 4528c2ecf20Sopenharmony_ci rtc_writel(rtc, OMAP_RTC_INTERRUPTS_REG, 4538c2ecf20Sopenharmony_ci val | OMAP_RTC_INTERRUPTS_IT_ALARM2); 4548c2ecf20Sopenharmony_ci 4558c2ecf20Sopenharmony_ci /* Retry in case roll over happened before alarm was armed. */ 4568c2ecf20Sopenharmony_ci if (rtc_read(rtc, OMAP_RTC_SECONDS_REG) != seconds) { 4578c2ecf20Sopenharmony_ci val = rtc_read(rtc, OMAP_RTC_STATUS_REG); 4588c2ecf20Sopenharmony_ci if (!(val & OMAP_RTC_STATUS_ALARM2)) 4598c2ecf20Sopenharmony_ci goto again; 4608c2ecf20Sopenharmony_ci } 4618c2ecf20Sopenharmony_ci 4628c2ecf20Sopenharmony_ci rtc->type->lock(rtc); 4638c2ecf20Sopenharmony_ci 4648c2ecf20Sopenharmony_ci return 0; 4658c2ecf20Sopenharmony_ci} 4668c2ecf20Sopenharmony_ciEXPORT_SYMBOL(omap_rtc_power_off_program); 4678c2ecf20Sopenharmony_ci 4688c2ecf20Sopenharmony_ci/* 4698c2ecf20Sopenharmony_ci * omap_rtc_poweroff: RTC-controlled power off 4708c2ecf20Sopenharmony_ci * 4718c2ecf20Sopenharmony_ci * The RTC can be used to control an external PMIC via the pmic_power_en pin, 4728c2ecf20Sopenharmony_ci * which can be configured to transition to OFF on ALARM2 events. 4738c2ecf20Sopenharmony_ci * 4748c2ecf20Sopenharmony_ci * Notes: 4758c2ecf20Sopenharmony_ci * The one-second alarm offset is the shortest offset possible as the alarm 4768c2ecf20Sopenharmony_ci * registers must be set before the next timer update and the offset 4778c2ecf20Sopenharmony_ci * calculation is too heavy for everything to be done within a single access 4788c2ecf20Sopenharmony_ci * period (~15 us). 4798c2ecf20Sopenharmony_ci * 4808c2ecf20Sopenharmony_ci * Called with local interrupts disabled. 4818c2ecf20Sopenharmony_ci */ 4828c2ecf20Sopenharmony_cistatic void omap_rtc_power_off(void) 4838c2ecf20Sopenharmony_ci{ 4848c2ecf20Sopenharmony_ci struct rtc_device *rtc = omap_rtc_power_off_rtc->rtc; 4858c2ecf20Sopenharmony_ci u32 val; 4868c2ecf20Sopenharmony_ci 4878c2ecf20Sopenharmony_ci omap_rtc_power_off_program(rtc->dev.parent); 4888c2ecf20Sopenharmony_ci 4898c2ecf20Sopenharmony_ci /* Set PMIC power enable and EXT_WAKEUP in case PB power on is used */ 4908c2ecf20Sopenharmony_ci omap_rtc_power_off_rtc->type->unlock(omap_rtc_power_off_rtc); 4918c2ecf20Sopenharmony_ci val = rtc_readl(omap_rtc_power_off_rtc, OMAP_RTC_PMIC_REG); 4928c2ecf20Sopenharmony_ci val |= OMAP_RTC_PMIC_POWER_EN_EN | OMAP_RTC_PMIC_EXT_WKUP_POL(0) | 4938c2ecf20Sopenharmony_ci OMAP_RTC_PMIC_EXT_WKUP_EN(0); 4948c2ecf20Sopenharmony_ci rtc_writel(omap_rtc_power_off_rtc, OMAP_RTC_PMIC_REG, val); 4958c2ecf20Sopenharmony_ci omap_rtc_power_off_rtc->type->lock(omap_rtc_power_off_rtc); 4968c2ecf20Sopenharmony_ci 4978c2ecf20Sopenharmony_ci /* 4988c2ecf20Sopenharmony_ci * Wait for alarm to trigger (within one second) and external PMIC to 4998c2ecf20Sopenharmony_ci * power off the system. Add a 500 ms margin for external latencies 5008c2ecf20Sopenharmony_ci * (e.g. debounce circuits). 5018c2ecf20Sopenharmony_ci */ 5028c2ecf20Sopenharmony_ci mdelay(1500); 5038c2ecf20Sopenharmony_ci} 5048c2ecf20Sopenharmony_ci 5058c2ecf20Sopenharmony_cistatic const struct rtc_class_ops omap_rtc_ops = { 5068c2ecf20Sopenharmony_ci .read_time = omap_rtc_read_time, 5078c2ecf20Sopenharmony_ci .set_time = omap_rtc_set_time, 5088c2ecf20Sopenharmony_ci .read_alarm = omap_rtc_read_alarm, 5098c2ecf20Sopenharmony_ci .set_alarm = omap_rtc_set_alarm, 5108c2ecf20Sopenharmony_ci .alarm_irq_enable = omap_rtc_alarm_irq_enable, 5118c2ecf20Sopenharmony_ci}; 5128c2ecf20Sopenharmony_ci 5138c2ecf20Sopenharmony_cistatic const struct omap_rtc_device_type omap_rtc_default_type = { 5148c2ecf20Sopenharmony_ci .has_power_up_reset = true, 5158c2ecf20Sopenharmony_ci .lock = default_rtc_lock, 5168c2ecf20Sopenharmony_ci .unlock = default_rtc_unlock, 5178c2ecf20Sopenharmony_ci}; 5188c2ecf20Sopenharmony_ci 5198c2ecf20Sopenharmony_cistatic const struct omap_rtc_device_type omap_rtc_am3352_type = { 5208c2ecf20Sopenharmony_ci .has_32kclk_en = true, 5218c2ecf20Sopenharmony_ci .has_irqwakeen = true, 5228c2ecf20Sopenharmony_ci .has_pmic_mode = true, 5238c2ecf20Sopenharmony_ci .lock = am3352_rtc_lock, 5248c2ecf20Sopenharmony_ci .unlock = am3352_rtc_unlock, 5258c2ecf20Sopenharmony_ci}; 5268c2ecf20Sopenharmony_ci 5278c2ecf20Sopenharmony_cistatic const struct omap_rtc_device_type omap_rtc_da830_type = { 5288c2ecf20Sopenharmony_ci .lock = am3352_rtc_lock, 5298c2ecf20Sopenharmony_ci .unlock = am3352_rtc_unlock, 5308c2ecf20Sopenharmony_ci}; 5318c2ecf20Sopenharmony_ci 5328c2ecf20Sopenharmony_cistatic const struct platform_device_id omap_rtc_id_table[] = { 5338c2ecf20Sopenharmony_ci { 5348c2ecf20Sopenharmony_ci .name = "omap_rtc", 5358c2ecf20Sopenharmony_ci .driver_data = (kernel_ulong_t)&omap_rtc_default_type, 5368c2ecf20Sopenharmony_ci }, { 5378c2ecf20Sopenharmony_ci .name = "am3352-rtc", 5388c2ecf20Sopenharmony_ci .driver_data = (kernel_ulong_t)&omap_rtc_am3352_type, 5398c2ecf20Sopenharmony_ci }, { 5408c2ecf20Sopenharmony_ci .name = "da830-rtc", 5418c2ecf20Sopenharmony_ci .driver_data = (kernel_ulong_t)&omap_rtc_da830_type, 5428c2ecf20Sopenharmony_ci }, { 5438c2ecf20Sopenharmony_ci /* sentinel */ 5448c2ecf20Sopenharmony_ci } 5458c2ecf20Sopenharmony_ci}; 5468c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(platform, omap_rtc_id_table); 5478c2ecf20Sopenharmony_ci 5488c2ecf20Sopenharmony_cistatic const struct of_device_id omap_rtc_of_match[] = { 5498c2ecf20Sopenharmony_ci { 5508c2ecf20Sopenharmony_ci .compatible = "ti,am3352-rtc", 5518c2ecf20Sopenharmony_ci .data = &omap_rtc_am3352_type, 5528c2ecf20Sopenharmony_ci }, { 5538c2ecf20Sopenharmony_ci .compatible = "ti,da830-rtc", 5548c2ecf20Sopenharmony_ci .data = &omap_rtc_da830_type, 5558c2ecf20Sopenharmony_ci }, { 5568c2ecf20Sopenharmony_ci /* sentinel */ 5578c2ecf20Sopenharmony_ci } 5588c2ecf20Sopenharmony_ci}; 5598c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(of, omap_rtc_of_match); 5608c2ecf20Sopenharmony_ci 5618c2ecf20Sopenharmony_cistatic const struct pinctrl_pin_desc rtc_pins_desc[] = { 5628c2ecf20Sopenharmony_ci PINCTRL_PIN(0, "ext_wakeup0"), 5638c2ecf20Sopenharmony_ci PINCTRL_PIN(1, "ext_wakeup1"), 5648c2ecf20Sopenharmony_ci PINCTRL_PIN(2, "ext_wakeup2"), 5658c2ecf20Sopenharmony_ci PINCTRL_PIN(3, "ext_wakeup3"), 5668c2ecf20Sopenharmony_ci}; 5678c2ecf20Sopenharmony_ci 5688c2ecf20Sopenharmony_cistatic int rtc_pinctrl_get_groups_count(struct pinctrl_dev *pctldev) 5698c2ecf20Sopenharmony_ci{ 5708c2ecf20Sopenharmony_ci return 0; 5718c2ecf20Sopenharmony_ci} 5728c2ecf20Sopenharmony_ci 5738c2ecf20Sopenharmony_cistatic const char *rtc_pinctrl_get_group_name(struct pinctrl_dev *pctldev, 5748c2ecf20Sopenharmony_ci unsigned int group) 5758c2ecf20Sopenharmony_ci{ 5768c2ecf20Sopenharmony_ci return NULL; 5778c2ecf20Sopenharmony_ci} 5788c2ecf20Sopenharmony_ci 5798c2ecf20Sopenharmony_cistatic const struct pinctrl_ops rtc_pinctrl_ops = { 5808c2ecf20Sopenharmony_ci .get_groups_count = rtc_pinctrl_get_groups_count, 5818c2ecf20Sopenharmony_ci .get_group_name = rtc_pinctrl_get_group_name, 5828c2ecf20Sopenharmony_ci .dt_node_to_map = pinconf_generic_dt_node_to_map_pin, 5838c2ecf20Sopenharmony_ci .dt_free_map = pinconf_generic_dt_free_map, 5848c2ecf20Sopenharmony_ci}; 5858c2ecf20Sopenharmony_ci 5868c2ecf20Sopenharmony_ci#define PIN_CONFIG_ACTIVE_HIGH (PIN_CONFIG_END + 1) 5878c2ecf20Sopenharmony_ci 5888c2ecf20Sopenharmony_cistatic const struct pinconf_generic_params rtc_params[] = { 5898c2ecf20Sopenharmony_ci {"ti,active-high", PIN_CONFIG_ACTIVE_HIGH, 0}, 5908c2ecf20Sopenharmony_ci}; 5918c2ecf20Sopenharmony_ci 5928c2ecf20Sopenharmony_ci#ifdef CONFIG_DEBUG_FS 5938c2ecf20Sopenharmony_cistatic const struct pin_config_item rtc_conf_items[ARRAY_SIZE(rtc_params)] = { 5948c2ecf20Sopenharmony_ci PCONFDUMP(PIN_CONFIG_ACTIVE_HIGH, "input active high", NULL, false), 5958c2ecf20Sopenharmony_ci}; 5968c2ecf20Sopenharmony_ci#endif 5978c2ecf20Sopenharmony_ci 5988c2ecf20Sopenharmony_cistatic int rtc_pinconf_get(struct pinctrl_dev *pctldev, 5998c2ecf20Sopenharmony_ci unsigned int pin, unsigned long *config) 6008c2ecf20Sopenharmony_ci{ 6018c2ecf20Sopenharmony_ci struct omap_rtc *rtc = pinctrl_dev_get_drvdata(pctldev); 6028c2ecf20Sopenharmony_ci unsigned int param = pinconf_to_config_param(*config); 6038c2ecf20Sopenharmony_ci u32 val; 6048c2ecf20Sopenharmony_ci u16 arg = 0; 6058c2ecf20Sopenharmony_ci 6068c2ecf20Sopenharmony_ci val = rtc_readl(rtc, OMAP_RTC_PMIC_REG); 6078c2ecf20Sopenharmony_ci 6088c2ecf20Sopenharmony_ci switch (param) { 6098c2ecf20Sopenharmony_ci case PIN_CONFIG_INPUT_ENABLE: 6108c2ecf20Sopenharmony_ci if (!(val & OMAP_RTC_PMIC_EXT_WKUP_EN(pin))) 6118c2ecf20Sopenharmony_ci return -EINVAL; 6128c2ecf20Sopenharmony_ci break; 6138c2ecf20Sopenharmony_ci case PIN_CONFIG_ACTIVE_HIGH: 6148c2ecf20Sopenharmony_ci if (val & OMAP_RTC_PMIC_EXT_WKUP_POL(pin)) 6158c2ecf20Sopenharmony_ci return -EINVAL; 6168c2ecf20Sopenharmony_ci break; 6178c2ecf20Sopenharmony_ci default: 6188c2ecf20Sopenharmony_ci return -ENOTSUPP; 6198c2ecf20Sopenharmony_ci } 6208c2ecf20Sopenharmony_ci 6218c2ecf20Sopenharmony_ci *config = pinconf_to_config_packed(param, arg); 6228c2ecf20Sopenharmony_ci 6238c2ecf20Sopenharmony_ci return 0; 6248c2ecf20Sopenharmony_ci} 6258c2ecf20Sopenharmony_ci 6268c2ecf20Sopenharmony_cistatic int rtc_pinconf_set(struct pinctrl_dev *pctldev, 6278c2ecf20Sopenharmony_ci unsigned int pin, unsigned long *configs, 6288c2ecf20Sopenharmony_ci unsigned int num_configs) 6298c2ecf20Sopenharmony_ci{ 6308c2ecf20Sopenharmony_ci struct omap_rtc *rtc = pinctrl_dev_get_drvdata(pctldev); 6318c2ecf20Sopenharmony_ci u32 val; 6328c2ecf20Sopenharmony_ci unsigned int param; 6338c2ecf20Sopenharmony_ci u32 param_val; 6348c2ecf20Sopenharmony_ci int i; 6358c2ecf20Sopenharmony_ci 6368c2ecf20Sopenharmony_ci val = rtc_readl(rtc, OMAP_RTC_PMIC_REG); 6378c2ecf20Sopenharmony_ci 6388c2ecf20Sopenharmony_ci /* active low by default */ 6398c2ecf20Sopenharmony_ci val |= OMAP_RTC_PMIC_EXT_WKUP_POL(pin); 6408c2ecf20Sopenharmony_ci 6418c2ecf20Sopenharmony_ci for (i = 0; i < num_configs; i++) { 6428c2ecf20Sopenharmony_ci param = pinconf_to_config_param(configs[i]); 6438c2ecf20Sopenharmony_ci param_val = pinconf_to_config_argument(configs[i]); 6448c2ecf20Sopenharmony_ci 6458c2ecf20Sopenharmony_ci switch (param) { 6468c2ecf20Sopenharmony_ci case PIN_CONFIG_INPUT_ENABLE: 6478c2ecf20Sopenharmony_ci if (param_val) 6488c2ecf20Sopenharmony_ci val |= OMAP_RTC_PMIC_EXT_WKUP_EN(pin); 6498c2ecf20Sopenharmony_ci else 6508c2ecf20Sopenharmony_ci val &= ~OMAP_RTC_PMIC_EXT_WKUP_EN(pin); 6518c2ecf20Sopenharmony_ci break; 6528c2ecf20Sopenharmony_ci case PIN_CONFIG_ACTIVE_HIGH: 6538c2ecf20Sopenharmony_ci val &= ~OMAP_RTC_PMIC_EXT_WKUP_POL(pin); 6548c2ecf20Sopenharmony_ci break; 6558c2ecf20Sopenharmony_ci default: 6568c2ecf20Sopenharmony_ci dev_err(&rtc->rtc->dev, "Property %u not supported\n", 6578c2ecf20Sopenharmony_ci param); 6588c2ecf20Sopenharmony_ci return -ENOTSUPP; 6598c2ecf20Sopenharmony_ci } 6608c2ecf20Sopenharmony_ci } 6618c2ecf20Sopenharmony_ci 6628c2ecf20Sopenharmony_ci rtc->type->unlock(rtc); 6638c2ecf20Sopenharmony_ci rtc_writel(rtc, OMAP_RTC_PMIC_REG, val); 6648c2ecf20Sopenharmony_ci rtc->type->lock(rtc); 6658c2ecf20Sopenharmony_ci 6668c2ecf20Sopenharmony_ci return 0; 6678c2ecf20Sopenharmony_ci} 6688c2ecf20Sopenharmony_ci 6698c2ecf20Sopenharmony_cistatic const struct pinconf_ops rtc_pinconf_ops = { 6708c2ecf20Sopenharmony_ci .is_generic = true, 6718c2ecf20Sopenharmony_ci .pin_config_get = rtc_pinconf_get, 6728c2ecf20Sopenharmony_ci .pin_config_set = rtc_pinconf_set, 6738c2ecf20Sopenharmony_ci}; 6748c2ecf20Sopenharmony_ci 6758c2ecf20Sopenharmony_cistatic struct pinctrl_desc rtc_pinctrl_desc = { 6768c2ecf20Sopenharmony_ci .pins = rtc_pins_desc, 6778c2ecf20Sopenharmony_ci .npins = ARRAY_SIZE(rtc_pins_desc), 6788c2ecf20Sopenharmony_ci .pctlops = &rtc_pinctrl_ops, 6798c2ecf20Sopenharmony_ci .confops = &rtc_pinconf_ops, 6808c2ecf20Sopenharmony_ci .custom_params = rtc_params, 6818c2ecf20Sopenharmony_ci .num_custom_params = ARRAY_SIZE(rtc_params), 6828c2ecf20Sopenharmony_ci#ifdef CONFIG_DEBUG_FS 6838c2ecf20Sopenharmony_ci .custom_conf_items = rtc_conf_items, 6848c2ecf20Sopenharmony_ci#endif 6858c2ecf20Sopenharmony_ci .owner = THIS_MODULE, 6868c2ecf20Sopenharmony_ci}; 6878c2ecf20Sopenharmony_ci 6888c2ecf20Sopenharmony_cistatic int omap_rtc_scratch_read(void *priv, unsigned int offset, void *_val, 6898c2ecf20Sopenharmony_ci size_t bytes) 6908c2ecf20Sopenharmony_ci{ 6918c2ecf20Sopenharmony_ci struct omap_rtc *rtc = priv; 6928c2ecf20Sopenharmony_ci u32 *val = _val; 6938c2ecf20Sopenharmony_ci int i; 6948c2ecf20Sopenharmony_ci 6958c2ecf20Sopenharmony_ci for (i = 0; i < bytes / 4; i++) 6968c2ecf20Sopenharmony_ci val[i] = rtc_readl(rtc, 6978c2ecf20Sopenharmony_ci OMAP_RTC_SCRATCH0_REG + offset + (i * 4)); 6988c2ecf20Sopenharmony_ci 6998c2ecf20Sopenharmony_ci return 0; 7008c2ecf20Sopenharmony_ci} 7018c2ecf20Sopenharmony_ci 7028c2ecf20Sopenharmony_cistatic int omap_rtc_scratch_write(void *priv, unsigned int offset, void *_val, 7038c2ecf20Sopenharmony_ci size_t bytes) 7048c2ecf20Sopenharmony_ci{ 7058c2ecf20Sopenharmony_ci struct omap_rtc *rtc = priv; 7068c2ecf20Sopenharmony_ci u32 *val = _val; 7078c2ecf20Sopenharmony_ci int i; 7088c2ecf20Sopenharmony_ci 7098c2ecf20Sopenharmony_ci rtc->type->unlock(rtc); 7108c2ecf20Sopenharmony_ci for (i = 0; i < bytes / 4; i++) 7118c2ecf20Sopenharmony_ci rtc_writel(rtc, 7128c2ecf20Sopenharmony_ci OMAP_RTC_SCRATCH0_REG + offset + (i * 4), val[i]); 7138c2ecf20Sopenharmony_ci rtc->type->lock(rtc); 7148c2ecf20Sopenharmony_ci 7158c2ecf20Sopenharmony_ci return 0; 7168c2ecf20Sopenharmony_ci} 7178c2ecf20Sopenharmony_ci 7188c2ecf20Sopenharmony_cistatic struct nvmem_config omap_rtc_nvmem_config = { 7198c2ecf20Sopenharmony_ci .name = "omap_rtc_scratch", 7208c2ecf20Sopenharmony_ci .word_size = 4, 7218c2ecf20Sopenharmony_ci .stride = 4, 7228c2ecf20Sopenharmony_ci .size = OMAP_RTC_KICK0_REG - OMAP_RTC_SCRATCH0_REG, 7238c2ecf20Sopenharmony_ci .reg_read = omap_rtc_scratch_read, 7248c2ecf20Sopenharmony_ci .reg_write = omap_rtc_scratch_write, 7258c2ecf20Sopenharmony_ci}; 7268c2ecf20Sopenharmony_ci 7278c2ecf20Sopenharmony_cistatic int omap_rtc_probe(struct platform_device *pdev) 7288c2ecf20Sopenharmony_ci{ 7298c2ecf20Sopenharmony_ci struct omap_rtc *rtc; 7308c2ecf20Sopenharmony_ci u8 reg, mask, new_ctrl; 7318c2ecf20Sopenharmony_ci const struct platform_device_id *id_entry; 7328c2ecf20Sopenharmony_ci const struct of_device_id *of_id; 7338c2ecf20Sopenharmony_ci int ret; 7348c2ecf20Sopenharmony_ci 7358c2ecf20Sopenharmony_ci rtc = devm_kzalloc(&pdev->dev, sizeof(*rtc), GFP_KERNEL); 7368c2ecf20Sopenharmony_ci if (!rtc) 7378c2ecf20Sopenharmony_ci return -ENOMEM; 7388c2ecf20Sopenharmony_ci 7398c2ecf20Sopenharmony_ci of_id = of_match_device(omap_rtc_of_match, &pdev->dev); 7408c2ecf20Sopenharmony_ci if (of_id) { 7418c2ecf20Sopenharmony_ci rtc->type = of_id->data; 7428c2ecf20Sopenharmony_ci rtc->is_pmic_controller = rtc->type->has_pmic_mode && 7438c2ecf20Sopenharmony_ci of_device_is_system_power_controller(pdev->dev.of_node); 7448c2ecf20Sopenharmony_ci } else { 7458c2ecf20Sopenharmony_ci id_entry = platform_get_device_id(pdev); 7468c2ecf20Sopenharmony_ci rtc->type = (void *)id_entry->driver_data; 7478c2ecf20Sopenharmony_ci } 7488c2ecf20Sopenharmony_ci 7498c2ecf20Sopenharmony_ci rtc->irq_timer = platform_get_irq(pdev, 0); 7508c2ecf20Sopenharmony_ci if (rtc->irq_timer <= 0) 7518c2ecf20Sopenharmony_ci return -ENOENT; 7528c2ecf20Sopenharmony_ci 7538c2ecf20Sopenharmony_ci rtc->irq_alarm = platform_get_irq(pdev, 1); 7548c2ecf20Sopenharmony_ci if (rtc->irq_alarm <= 0) 7558c2ecf20Sopenharmony_ci return -ENOENT; 7568c2ecf20Sopenharmony_ci 7578c2ecf20Sopenharmony_ci rtc->clk = devm_clk_get(&pdev->dev, "ext-clk"); 7588c2ecf20Sopenharmony_ci if (!IS_ERR(rtc->clk)) 7598c2ecf20Sopenharmony_ci rtc->has_ext_clk = true; 7608c2ecf20Sopenharmony_ci else 7618c2ecf20Sopenharmony_ci rtc->clk = devm_clk_get(&pdev->dev, "int-clk"); 7628c2ecf20Sopenharmony_ci 7638c2ecf20Sopenharmony_ci if (!IS_ERR(rtc->clk)) 7648c2ecf20Sopenharmony_ci clk_prepare_enable(rtc->clk); 7658c2ecf20Sopenharmony_ci 7668c2ecf20Sopenharmony_ci rtc->base = devm_platform_ioremap_resource(pdev, 0); 7678c2ecf20Sopenharmony_ci if (IS_ERR(rtc->base)) { 7688c2ecf20Sopenharmony_ci clk_disable_unprepare(rtc->clk); 7698c2ecf20Sopenharmony_ci return PTR_ERR(rtc->base); 7708c2ecf20Sopenharmony_ci } 7718c2ecf20Sopenharmony_ci 7728c2ecf20Sopenharmony_ci platform_set_drvdata(pdev, rtc); 7738c2ecf20Sopenharmony_ci 7748c2ecf20Sopenharmony_ci /* Enable the clock/module so that we can access the registers */ 7758c2ecf20Sopenharmony_ci pm_runtime_enable(&pdev->dev); 7768c2ecf20Sopenharmony_ci pm_runtime_get_sync(&pdev->dev); 7778c2ecf20Sopenharmony_ci 7788c2ecf20Sopenharmony_ci rtc->type->unlock(rtc); 7798c2ecf20Sopenharmony_ci 7808c2ecf20Sopenharmony_ci /* 7818c2ecf20Sopenharmony_ci * disable interrupts 7828c2ecf20Sopenharmony_ci * 7838c2ecf20Sopenharmony_ci * NOTE: ALARM2 is not cleared on AM3352 if rtc_write (writeb) is used 7848c2ecf20Sopenharmony_ci */ 7858c2ecf20Sopenharmony_ci rtc_writel(rtc, OMAP_RTC_INTERRUPTS_REG, 0); 7868c2ecf20Sopenharmony_ci 7878c2ecf20Sopenharmony_ci /* enable RTC functional clock */ 7888c2ecf20Sopenharmony_ci if (rtc->type->has_32kclk_en) { 7898c2ecf20Sopenharmony_ci reg = rtc_read(rtc, OMAP_RTC_OSC_REG); 7908c2ecf20Sopenharmony_ci rtc_writel(rtc, OMAP_RTC_OSC_REG, 7918c2ecf20Sopenharmony_ci reg | OMAP_RTC_OSC_32KCLK_EN); 7928c2ecf20Sopenharmony_ci } 7938c2ecf20Sopenharmony_ci 7948c2ecf20Sopenharmony_ci /* clear old status */ 7958c2ecf20Sopenharmony_ci reg = rtc_read(rtc, OMAP_RTC_STATUS_REG); 7968c2ecf20Sopenharmony_ci 7978c2ecf20Sopenharmony_ci mask = OMAP_RTC_STATUS_ALARM; 7988c2ecf20Sopenharmony_ci 7998c2ecf20Sopenharmony_ci if (rtc->type->has_pmic_mode) 8008c2ecf20Sopenharmony_ci mask |= OMAP_RTC_STATUS_ALARM2; 8018c2ecf20Sopenharmony_ci 8028c2ecf20Sopenharmony_ci if (rtc->type->has_power_up_reset) { 8038c2ecf20Sopenharmony_ci mask |= OMAP_RTC_STATUS_POWER_UP; 8048c2ecf20Sopenharmony_ci if (reg & OMAP_RTC_STATUS_POWER_UP) 8058c2ecf20Sopenharmony_ci dev_info(&pdev->dev, "RTC power up reset detected\n"); 8068c2ecf20Sopenharmony_ci } 8078c2ecf20Sopenharmony_ci 8088c2ecf20Sopenharmony_ci if (reg & mask) 8098c2ecf20Sopenharmony_ci rtc_write(rtc, OMAP_RTC_STATUS_REG, reg & mask); 8108c2ecf20Sopenharmony_ci 8118c2ecf20Sopenharmony_ci /* On boards with split power, RTC_ON_NOFF won't reset the RTC */ 8128c2ecf20Sopenharmony_ci reg = rtc_read(rtc, OMAP_RTC_CTRL_REG); 8138c2ecf20Sopenharmony_ci if (reg & OMAP_RTC_CTRL_STOP) 8148c2ecf20Sopenharmony_ci dev_info(&pdev->dev, "already running\n"); 8158c2ecf20Sopenharmony_ci 8168c2ecf20Sopenharmony_ci /* force to 24 hour mode */ 8178c2ecf20Sopenharmony_ci new_ctrl = reg & (OMAP_RTC_CTRL_SPLIT | OMAP_RTC_CTRL_AUTO_COMP); 8188c2ecf20Sopenharmony_ci new_ctrl |= OMAP_RTC_CTRL_STOP; 8198c2ecf20Sopenharmony_ci 8208c2ecf20Sopenharmony_ci /* 8218c2ecf20Sopenharmony_ci * BOARD-SPECIFIC CUSTOMIZATION CAN GO HERE: 8228c2ecf20Sopenharmony_ci * 8238c2ecf20Sopenharmony_ci * - Device wake-up capability setting should come through chip 8248c2ecf20Sopenharmony_ci * init logic. OMAP1 boards should initialize the "wakeup capable" 8258c2ecf20Sopenharmony_ci * flag in the platform device if the board is wired right for 8268c2ecf20Sopenharmony_ci * being woken up by RTC alarm. For OMAP-L138, this capability 8278c2ecf20Sopenharmony_ci * is built into the SoC by the "Deep Sleep" capability. 8288c2ecf20Sopenharmony_ci * 8298c2ecf20Sopenharmony_ci * - Boards wired so RTC_ON_nOFF is used as the reset signal, 8308c2ecf20Sopenharmony_ci * rather than nPWRON_RESET, should forcibly enable split 8318c2ecf20Sopenharmony_ci * power mode. (Some chip errata report that RTC_CTRL_SPLIT 8328c2ecf20Sopenharmony_ci * is write-only, and always reads as zero...) 8338c2ecf20Sopenharmony_ci */ 8348c2ecf20Sopenharmony_ci 8358c2ecf20Sopenharmony_ci if (new_ctrl & OMAP_RTC_CTRL_SPLIT) 8368c2ecf20Sopenharmony_ci dev_info(&pdev->dev, "split power mode\n"); 8378c2ecf20Sopenharmony_ci 8388c2ecf20Sopenharmony_ci if (reg != new_ctrl) 8398c2ecf20Sopenharmony_ci rtc_write(rtc, OMAP_RTC_CTRL_REG, new_ctrl); 8408c2ecf20Sopenharmony_ci 8418c2ecf20Sopenharmony_ci /* 8428c2ecf20Sopenharmony_ci * If we have the external clock then switch to it so we can keep 8438c2ecf20Sopenharmony_ci * ticking across suspend. 8448c2ecf20Sopenharmony_ci */ 8458c2ecf20Sopenharmony_ci if (rtc->has_ext_clk) { 8468c2ecf20Sopenharmony_ci reg = rtc_read(rtc, OMAP_RTC_OSC_REG); 8478c2ecf20Sopenharmony_ci reg &= ~OMAP_RTC_OSC_OSC32K_GZ_DISABLE; 8488c2ecf20Sopenharmony_ci reg |= OMAP_RTC_OSC_32KCLK_EN | OMAP_RTC_OSC_SEL_32KCLK_SRC; 8498c2ecf20Sopenharmony_ci rtc_writel(rtc, OMAP_RTC_OSC_REG, reg); 8508c2ecf20Sopenharmony_ci } 8518c2ecf20Sopenharmony_ci 8528c2ecf20Sopenharmony_ci rtc->type->lock(rtc); 8538c2ecf20Sopenharmony_ci 8548c2ecf20Sopenharmony_ci device_init_wakeup(&pdev->dev, true); 8558c2ecf20Sopenharmony_ci 8568c2ecf20Sopenharmony_ci rtc->rtc = devm_rtc_allocate_device(&pdev->dev); 8578c2ecf20Sopenharmony_ci if (IS_ERR(rtc->rtc)) { 8588c2ecf20Sopenharmony_ci ret = PTR_ERR(rtc->rtc); 8598c2ecf20Sopenharmony_ci goto err; 8608c2ecf20Sopenharmony_ci } 8618c2ecf20Sopenharmony_ci 8628c2ecf20Sopenharmony_ci rtc->rtc->ops = &omap_rtc_ops; 8638c2ecf20Sopenharmony_ci rtc->rtc->range_min = RTC_TIMESTAMP_BEGIN_2000; 8648c2ecf20Sopenharmony_ci rtc->rtc->range_max = RTC_TIMESTAMP_END_2099; 8658c2ecf20Sopenharmony_ci omap_rtc_nvmem_config.priv = rtc; 8668c2ecf20Sopenharmony_ci 8678c2ecf20Sopenharmony_ci /* handle periodic and alarm irqs */ 8688c2ecf20Sopenharmony_ci ret = devm_request_irq(&pdev->dev, rtc->irq_timer, rtc_irq, 0, 8698c2ecf20Sopenharmony_ci dev_name(&rtc->rtc->dev), rtc); 8708c2ecf20Sopenharmony_ci if (ret) 8718c2ecf20Sopenharmony_ci goto err; 8728c2ecf20Sopenharmony_ci 8738c2ecf20Sopenharmony_ci if (rtc->irq_timer != rtc->irq_alarm) { 8748c2ecf20Sopenharmony_ci ret = devm_request_irq(&pdev->dev, rtc->irq_alarm, rtc_irq, 0, 8758c2ecf20Sopenharmony_ci dev_name(&rtc->rtc->dev), rtc); 8768c2ecf20Sopenharmony_ci if (ret) 8778c2ecf20Sopenharmony_ci goto err; 8788c2ecf20Sopenharmony_ci } 8798c2ecf20Sopenharmony_ci 8808c2ecf20Sopenharmony_ci /* Support ext_wakeup pinconf */ 8818c2ecf20Sopenharmony_ci rtc_pinctrl_desc.name = dev_name(&pdev->dev); 8828c2ecf20Sopenharmony_ci 8838c2ecf20Sopenharmony_ci rtc->pctldev = pinctrl_register(&rtc_pinctrl_desc, &pdev->dev, rtc); 8848c2ecf20Sopenharmony_ci if (IS_ERR(rtc->pctldev)) { 8858c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "Couldn't register pinctrl driver\n"); 8868c2ecf20Sopenharmony_ci ret = PTR_ERR(rtc->pctldev); 8878c2ecf20Sopenharmony_ci goto err; 8888c2ecf20Sopenharmony_ci } 8898c2ecf20Sopenharmony_ci 8908c2ecf20Sopenharmony_ci ret = rtc_register_device(rtc->rtc); 8918c2ecf20Sopenharmony_ci if (ret) 8928c2ecf20Sopenharmony_ci goto err_deregister_pinctrl; 8938c2ecf20Sopenharmony_ci 8948c2ecf20Sopenharmony_ci rtc_nvmem_register(rtc->rtc, &omap_rtc_nvmem_config); 8958c2ecf20Sopenharmony_ci 8968c2ecf20Sopenharmony_ci if (rtc->is_pmic_controller) { 8978c2ecf20Sopenharmony_ci if (!pm_power_off) { 8988c2ecf20Sopenharmony_ci omap_rtc_power_off_rtc = rtc; 8998c2ecf20Sopenharmony_ci pm_power_off = omap_rtc_power_off; 9008c2ecf20Sopenharmony_ci } 9018c2ecf20Sopenharmony_ci } 9028c2ecf20Sopenharmony_ci 9038c2ecf20Sopenharmony_ci return 0; 9048c2ecf20Sopenharmony_ci 9058c2ecf20Sopenharmony_cierr_deregister_pinctrl: 9068c2ecf20Sopenharmony_ci pinctrl_unregister(rtc->pctldev); 9078c2ecf20Sopenharmony_cierr: 9088c2ecf20Sopenharmony_ci clk_disable_unprepare(rtc->clk); 9098c2ecf20Sopenharmony_ci device_init_wakeup(&pdev->dev, false); 9108c2ecf20Sopenharmony_ci rtc->type->lock(rtc); 9118c2ecf20Sopenharmony_ci pm_runtime_put_sync(&pdev->dev); 9128c2ecf20Sopenharmony_ci pm_runtime_disable(&pdev->dev); 9138c2ecf20Sopenharmony_ci 9148c2ecf20Sopenharmony_ci return ret; 9158c2ecf20Sopenharmony_ci} 9168c2ecf20Sopenharmony_ci 9178c2ecf20Sopenharmony_cistatic int omap_rtc_remove(struct platform_device *pdev) 9188c2ecf20Sopenharmony_ci{ 9198c2ecf20Sopenharmony_ci struct omap_rtc *rtc = platform_get_drvdata(pdev); 9208c2ecf20Sopenharmony_ci u8 reg; 9218c2ecf20Sopenharmony_ci 9228c2ecf20Sopenharmony_ci if (pm_power_off == omap_rtc_power_off && 9238c2ecf20Sopenharmony_ci omap_rtc_power_off_rtc == rtc) { 9248c2ecf20Sopenharmony_ci pm_power_off = NULL; 9258c2ecf20Sopenharmony_ci omap_rtc_power_off_rtc = NULL; 9268c2ecf20Sopenharmony_ci } 9278c2ecf20Sopenharmony_ci 9288c2ecf20Sopenharmony_ci device_init_wakeup(&pdev->dev, 0); 9298c2ecf20Sopenharmony_ci 9308c2ecf20Sopenharmony_ci if (!IS_ERR(rtc->clk)) 9318c2ecf20Sopenharmony_ci clk_disable_unprepare(rtc->clk); 9328c2ecf20Sopenharmony_ci 9338c2ecf20Sopenharmony_ci rtc->type->unlock(rtc); 9348c2ecf20Sopenharmony_ci /* leave rtc running, but disable irqs */ 9358c2ecf20Sopenharmony_ci rtc_write(rtc, OMAP_RTC_INTERRUPTS_REG, 0); 9368c2ecf20Sopenharmony_ci 9378c2ecf20Sopenharmony_ci if (rtc->has_ext_clk) { 9388c2ecf20Sopenharmony_ci reg = rtc_read(rtc, OMAP_RTC_OSC_REG); 9398c2ecf20Sopenharmony_ci reg &= ~OMAP_RTC_OSC_SEL_32KCLK_SRC; 9408c2ecf20Sopenharmony_ci rtc_write(rtc, OMAP_RTC_OSC_REG, reg); 9418c2ecf20Sopenharmony_ci } 9428c2ecf20Sopenharmony_ci 9438c2ecf20Sopenharmony_ci rtc->type->lock(rtc); 9448c2ecf20Sopenharmony_ci 9458c2ecf20Sopenharmony_ci /* Disable the clock/module */ 9468c2ecf20Sopenharmony_ci pm_runtime_put_sync(&pdev->dev); 9478c2ecf20Sopenharmony_ci pm_runtime_disable(&pdev->dev); 9488c2ecf20Sopenharmony_ci 9498c2ecf20Sopenharmony_ci /* Remove ext_wakeup pinconf */ 9508c2ecf20Sopenharmony_ci pinctrl_unregister(rtc->pctldev); 9518c2ecf20Sopenharmony_ci 9528c2ecf20Sopenharmony_ci return 0; 9538c2ecf20Sopenharmony_ci} 9548c2ecf20Sopenharmony_ci 9558c2ecf20Sopenharmony_cistatic int __maybe_unused omap_rtc_suspend(struct device *dev) 9568c2ecf20Sopenharmony_ci{ 9578c2ecf20Sopenharmony_ci struct omap_rtc *rtc = dev_get_drvdata(dev); 9588c2ecf20Sopenharmony_ci 9598c2ecf20Sopenharmony_ci rtc->interrupts_reg = rtc_read(rtc, OMAP_RTC_INTERRUPTS_REG); 9608c2ecf20Sopenharmony_ci 9618c2ecf20Sopenharmony_ci rtc->type->unlock(rtc); 9628c2ecf20Sopenharmony_ci /* 9638c2ecf20Sopenharmony_ci * FIXME: the RTC alarm is not currently acting as a wakeup event 9648c2ecf20Sopenharmony_ci * source on some platforms, and in fact this enable() call is just 9658c2ecf20Sopenharmony_ci * saving a flag that's never used... 9668c2ecf20Sopenharmony_ci */ 9678c2ecf20Sopenharmony_ci if (device_may_wakeup(dev)) 9688c2ecf20Sopenharmony_ci enable_irq_wake(rtc->irq_alarm); 9698c2ecf20Sopenharmony_ci else 9708c2ecf20Sopenharmony_ci rtc_write(rtc, OMAP_RTC_INTERRUPTS_REG, 0); 9718c2ecf20Sopenharmony_ci rtc->type->lock(rtc); 9728c2ecf20Sopenharmony_ci 9738c2ecf20Sopenharmony_ci rtc->is_suspending = true; 9748c2ecf20Sopenharmony_ci 9758c2ecf20Sopenharmony_ci return 0; 9768c2ecf20Sopenharmony_ci} 9778c2ecf20Sopenharmony_ci 9788c2ecf20Sopenharmony_cistatic int __maybe_unused omap_rtc_resume(struct device *dev) 9798c2ecf20Sopenharmony_ci{ 9808c2ecf20Sopenharmony_ci struct omap_rtc *rtc = dev_get_drvdata(dev); 9818c2ecf20Sopenharmony_ci 9828c2ecf20Sopenharmony_ci rtc->type->unlock(rtc); 9838c2ecf20Sopenharmony_ci if (device_may_wakeup(dev)) 9848c2ecf20Sopenharmony_ci disable_irq_wake(rtc->irq_alarm); 9858c2ecf20Sopenharmony_ci else 9868c2ecf20Sopenharmony_ci rtc_write(rtc, OMAP_RTC_INTERRUPTS_REG, rtc->interrupts_reg); 9878c2ecf20Sopenharmony_ci rtc->type->lock(rtc); 9888c2ecf20Sopenharmony_ci 9898c2ecf20Sopenharmony_ci rtc->is_suspending = false; 9908c2ecf20Sopenharmony_ci 9918c2ecf20Sopenharmony_ci return 0; 9928c2ecf20Sopenharmony_ci} 9938c2ecf20Sopenharmony_ci 9948c2ecf20Sopenharmony_cistatic int __maybe_unused omap_rtc_runtime_suspend(struct device *dev) 9958c2ecf20Sopenharmony_ci{ 9968c2ecf20Sopenharmony_ci struct omap_rtc *rtc = dev_get_drvdata(dev); 9978c2ecf20Sopenharmony_ci 9988c2ecf20Sopenharmony_ci if (rtc->is_suspending && !rtc->has_ext_clk) 9998c2ecf20Sopenharmony_ci return -EBUSY; 10008c2ecf20Sopenharmony_ci 10018c2ecf20Sopenharmony_ci return 0; 10028c2ecf20Sopenharmony_ci} 10038c2ecf20Sopenharmony_ci 10048c2ecf20Sopenharmony_cistatic const struct dev_pm_ops omap_rtc_pm_ops = { 10058c2ecf20Sopenharmony_ci SET_SYSTEM_SLEEP_PM_OPS(omap_rtc_suspend, omap_rtc_resume) 10068c2ecf20Sopenharmony_ci SET_RUNTIME_PM_OPS(omap_rtc_runtime_suspend, NULL, NULL) 10078c2ecf20Sopenharmony_ci}; 10088c2ecf20Sopenharmony_ci 10098c2ecf20Sopenharmony_cistatic void omap_rtc_shutdown(struct platform_device *pdev) 10108c2ecf20Sopenharmony_ci{ 10118c2ecf20Sopenharmony_ci struct omap_rtc *rtc = platform_get_drvdata(pdev); 10128c2ecf20Sopenharmony_ci u8 mask; 10138c2ecf20Sopenharmony_ci 10148c2ecf20Sopenharmony_ci /* 10158c2ecf20Sopenharmony_ci * Keep the ALARM interrupt enabled to allow the system to power up on 10168c2ecf20Sopenharmony_ci * alarm events. 10178c2ecf20Sopenharmony_ci */ 10188c2ecf20Sopenharmony_ci rtc->type->unlock(rtc); 10198c2ecf20Sopenharmony_ci mask = rtc_read(rtc, OMAP_RTC_INTERRUPTS_REG); 10208c2ecf20Sopenharmony_ci mask &= OMAP_RTC_INTERRUPTS_IT_ALARM; 10218c2ecf20Sopenharmony_ci rtc_write(rtc, OMAP_RTC_INTERRUPTS_REG, mask); 10228c2ecf20Sopenharmony_ci rtc->type->lock(rtc); 10238c2ecf20Sopenharmony_ci} 10248c2ecf20Sopenharmony_ci 10258c2ecf20Sopenharmony_cistatic struct platform_driver omap_rtc_driver = { 10268c2ecf20Sopenharmony_ci .probe = omap_rtc_probe, 10278c2ecf20Sopenharmony_ci .remove = omap_rtc_remove, 10288c2ecf20Sopenharmony_ci .shutdown = omap_rtc_shutdown, 10298c2ecf20Sopenharmony_ci .driver = { 10308c2ecf20Sopenharmony_ci .name = "omap_rtc", 10318c2ecf20Sopenharmony_ci .pm = &omap_rtc_pm_ops, 10328c2ecf20Sopenharmony_ci .of_match_table = omap_rtc_of_match, 10338c2ecf20Sopenharmony_ci }, 10348c2ecf20Sopenharmony_ci .id_table = omap_rtc_id_table, 10358c2ecf20Sopenharmony_ci}; 10368c2ecf20Sopenharmony_ci 10378c2ecf20Sopenharmony_cimodule_platform_driver(omap_rtc_driver); 10388c2ecf20Sopenharmony_ci 10398c2ecf20Sopenharmony_ciMODULE_ALIAS("platform:omap_rtc"); 10408c2ecf20Sopenharmony_ciMODULE_AUTHOR("George G. Davis (and others)"); 10418c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 1042