aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/rtc/rtc-sun6i.c
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@linux-foundation.org>2019-09-22 11:05:43 -0700
committerLinus Torvalds <torvalds@linux-foundation.org>2019-09-22 11:05:43 -0700
commit9dbd83f665298c9dcf647f20d6d6488e9019114b (patch)
tree538712c8f87137989743b22fb4a4ad4345e80572 /drivers/rtc/rtc-sun6i.c
parentMerge tag 'rpmsg-v5.4' of git://git.kernel.org/pub/scm/linux/kernel/git/andersson/remoteproc (diff)
parentrtc: meson: mark PM functions as __maybe_unused (diff)
downloadlinux-dev-9dbd83f665298c9dcf647f20d6d6488e9019114b.tar.xz
linux-dev-9dbd83f665298c9dcf647f20d6d6488e9019114b.zip
Merge tag 'rtc-5.4' of git://git.kernel.org/pub/scm/linux/kernel/git/abelloni/linux
Pull RTC updates from Alexandre Belloni: "Two new drivers and the new pcf2127 feature make the bulk of the additions. The rest are the usual fixes and new features. Subsystem: - add debug message when registration fails New drivers: - Amlogic Virtual Wake - Freescale FlexTimer Module alarm Drivers: - remove superfluous error messages - convert to i2c_new_dummy_device and devm_i2c_new_dummy_device - Remove dev_err() usage after platform_get_irq() - Set RTC range for: pcf2123, pcf8563, snvs. - pcf2127: tamper detection and watchdog support - pcf85363: fix regmap issue - sun6i: H6 support - remove w90x900/nuc900 driver" * tag 'rtc-5.4' of git://git.kernel.org/pub/scm/linux/kernel/git/abelloni/linux: (51 commits) rtc: meson: mark PM functions as __maybe_unused rtc: sc27xx: Remove clearing SPRD_RTC_POWEROFF_ALM_FLAG flag dt-bindings: rtc: ds1307: add rx8130 compatible rtc: sun6i: Allow using as wakeup source from suspend rtc: pcf8563: let the core handle range offsetting rtc: pcf8563: remove useless indirection rtc: pcf8563: convert to devm_rtc_allocate_device rtc: pcf8563: add Microcrystal RV8564 compatible rtc: pcf8563: add Epson RTC8564 compatible rtc: s35390a: convert to devm_i2c_new_dummy_device() rtc: max77686: convert to devm_i2c_new_dummy_device() rtc: pcf85363/pcf85263: fix regmap error in set_time rtc: snvs: switch to rtc_time64_to_tm/rtc_tm_to_time64 rtc: snvs: set range rtc: snvs: fix possible race condition rtc: pcf2127: bugfix: watchdog build dependency rtc: pcf2127: add tamper detection support rtc: pcf2127: add watchdog feature support rtc: pcf2127: bugfix: read rtc disables watchdog rtc: pcf2127: cleanup register and bit defines ...
Diffstat (limited to 'drivers/rtc/rtc-sun6i.c')
-rw-r--r--drivers/rtc/rtc-sun6i.c74
1 files changed, 69 insertions, 5 deletions
diff --git a/drivers/rtc/rtc-sun6i.c b/drivers/rtc/rtc-sun6i.c
index d50ee023b559..5e2bd9f1d01e 100644
--- a/drivers/rtc/rtc-sun6i.c
+++ b/drivers/rtc/rtc-sun6i.c
@@ -32,9 +32,11 @@
/* Control register */
#define SUN6I_LOSC_CTRL 0x0000
#define SUN6I_LOSC_CTRL_KEY (0x16aa << 16)
+#define SUN6I_LOSC_CTRL_AUTO_SWT_BYPASS BIT(15)
#define SUN6I_LOSC_CTRL_ALM_DHMS_ACC BIT(9)
#define SUN6I_LOSC_CTRL_RTC_HMS_ACC BIT(8)
#define SUN6I_LOSC_CTRL_RTC_YMD_ACC BIT(7)
+#define SUN6I_LOSC_CTRL_EXT_LOSC_EN BIT(4)
#define SUN6I_LOSC_CTRL_EXT_OSC BIT(0)
#define SUN6I_LOSC_CTRL_ACC_MASK GENMASK(9, 7)
@@ -128,6 +130,8 @@ struct sun6i_rtc_clk_data {
unsigned int has_prescaler : 1;
unsigned int has_out_clk : 1;
unsigned int export_iosc : 1;
+ unsigned int has_losc_en : 1;
+ unsigned int has_auto_swt : 1;
};
struct sun6i_rtc_dev {
@@ -190,6 +194,10 @@ static int sun6i_rtc_osc_set_parent(struct clk_hw *hw, u8 index)
val &= ~SUN6I_LOSC_CTRL_EXT_OSC;
val |= SUN6I_LOSC_CTRL_KEY;
val |= index ? SUN6I_LOSC_CTRL_EXT_OSC : 0;
+ if (rtc->data->has_losc_en) {
+ val &= ~SUN6I_LOSC_CTRL_EXT_LOSC_EN;
+ val |= index ? SUN6I_LOSC_CTRL_EXT_LOSC_EN : 0;
+ }
writel(val, rtc->base + SUN6I_LOSC_CTRL);
spin_unlock_irqrestore(&rtc->lock, flags);
@@ -215,6 +223,7 @@ static void __init sun6i_rtc_clk_init(struct device_node *node,
const char *iosc_name = "rtc-int-osc";
const char *clkout_name = "osc32k-out";
const char *parents[2];
+ u32 reg;
rtc = kzalloc(sizeof(*rtc), GFP_KERNEL);
if (!rtc)
@@ -235,9 +244,18 @@ static void __init sun6i_rtc_clk_init(struct device_node *node,
goto err;
}
+ reg = SUN6I_LOSC_CTRL_KEY;
+ if (rtc->data->has_auto_swt) {
+ /* Bypass auto-switch to int osc, on ext losc failure */
+ reg |= SUN6I_LOSC_CTRL_AUTO_SWT_BYPASS;
+ writel(reg, rtc->base + SUN6I_LOSC_CTRL);
+ }
+
/* Switch to the external, more precise, oscillator */
- writel(SUN6I_LOSC_CTRL_KEY | SUN6I_LOSC_CTRL_EXT_OSC,
- rtc->base + SUN6I_LOSC_CTRL);
+ reg |= SUN6I_LOSC_CTRL_EXT_OSC;
+ if (rtc->data->has_losc_en)
+ reg |= SUN6I_LOSC_CTRL_EXT_LOSC_EN;
+ writel(reg, rtc->base + SUN6I_LOSC_CTRL);
/* Yes, I know, this is ugly. */
sun6i_rtc = rtc;
@@ -345,6 +363,23 @@ CLK_OF_DECLARE_DRIVER(sun8i_h3_rtc_clk, "allwinner,sun8i-h3-rtc",
CLK_OF_DECLARE_DRIVER(sun50i_h5_rtc_clk, "allwinner,sun50i-h5-rtc",
sun8i_h3_rtc_clk_init);
+static const struct sun6i_rtc_clk_data sun50i_h6_rtc_data = {
+ .rc_osc_rate = 16000000,
+ .fixed_prescaler = 32,
+ .has_prescaler = 1,
+ .has_out_clk = 1,
+ .export_iosc = 1,
+ .has_losc_en = 1,
+ .has_auto_swt = 1,
+};
+
+static void __init sun50i_h6_rtc_clk_init(struct device_node *node)
+{
+ sun6i_rtc_clk_init(node, &sun50i_h6_rtc_data);
+}
+CLK_OF_DECLARE_DRIVER(sun50i_h6_rtc_clk, "allwinner,sun50i-h6-rtc",
+ sun50i_h6_rtc_clk_init);
+
static const struct sun6i_rtc_clk_data sun8i_v3_rtc_data = {
.rc_osc_rate = 32000,
.has_out_clk = 1,
@@ -598,6 +633,33 @@ static const struct rtc_class_ops sun6i_rtc_ops = {
.alarm_irq_enable = sun6i_rtc_alarm_irq_enable
};
+#ifdef CONFIG_PM_SLEEP
+/* Enable IRQ wake on suspend, to wake up from RTC. */
+static int sun6i_rtc_suspend(struct device *dev)
+{
+ struct sun6i_rtc_dev *chip = dev_get_drvdata(dev);
+
+ if (device_may_wakeup(dev))
+ enable_irq_wake(chip->irq);
+
+ return 0;
+}
+
+/* Disable IRQ wake on resume. */
+static int sun6i_rtc_resume(struct device *dev)
+{
+ struct sun6i_rtc_dev *chip = dev_get_drvdata(dev);
+
+ if (device_may_wakeup(dev))
+ disable_irq_wake(chip->irq);
+
+ return 0;
+}
+#endif
+
+static SIMPLE_DEV_PM_OPS(sun6i_rtc_pm_ops,
+ sun6i_rtc_suspend, sun6i_rtc_resume);
+
static int sun6i_rtc_probe(struct platform_device *pdev)
{
struct sun6i_rtc_dev *chip = sun6i_rtc;
@@ -610,10 +672,8 @@ static int sun6i_rtc_probe(struct platform_device *pdev)
chip->dev = &pdev->dev;
chip->irq = platform_get_irq(pdev, 0);
- if (chip->irq < 0) {
- dev_err(&pdev->dev, "No IRQ resource\n");
+ if (chip->irq < 0)
return chip->irq;
- }
ret = devm_request_irq(&pdev->dev, chip->irq, sun6i_rtc_alarmirq,
0, dev_name(&pdev->dev), chip);
@@ -650,6 +710,8 @@ static int sun6i_rtc_probe(struct platform_device *pdev)
clk_prepare_enable(chip->losc);
+ device_init_wakeup(&pdev->dev, 1);
+
chip->rtc = devm_rtc_device_register(&pdev->dev, "rtc-sun6i",
&sun6i_rtc_ops, THIS_MODULE);
if (IS_ERR(chip->rtc)) {
@@ -675,6 +737,7 @@ static const struct of_device_id sun6i_rtc_dt_ids[] = {
{ .compatible = "allwinner,sun8i-r40-rtc" },
{ .compatible = "allwinner,sun8i-v3-rtc" },
{ .compatible = "allwinner,sun50i-h5-rtc" },
+ { .compatible = "allwinner,sun50i-h6-rtc" },
{ /* sentinel */ },
};
MODULE_DEVICE_TABLE(of, sun6i_rtc_dt_ids);
@@ -684,6 +747,7 @@ static struct platform_driver sun6i_rtc_driver = {
.driver = {
.name = "sun6i-rtc",
.of_match_table = sun6i_rtc_dt_ids,
+ .pm = &sun6i_rtc_pm_ops,
},
};
builtin_platform_driver(sun6i_rtc_driver);