]> asedeno.scripts.mit.edu Git - linux.git/blobdiff - drivers/rtc/rtc-sun6i.c
Merge tag 'ceph-for-5.4-rc1' of git://github.com/ceph/ceph-client
[linux.git] / drivers / rtc / rtc-sun6i.c
index c0e75c373605909b99cff0f3a87198a05c913aa9..5e2bd9f1d01ead21f413dd38f254265aef5a71d9 100644 (file)
 /* 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;
@@ -279,7 +297,7 @@ static void __init sun6i_rtc_clk_init(struct device_node *node,
 
        of_property_read_string_index(node, "clock-output-names", 1,
                                      &clkout_name);
-       rtc->ext_losc = clk_register_gate(NULL, clkout_name, rtc->hw.init->name,
+       rtc->ext_losc = clk_register_gate(NULL, clkout_name, init.name,
                                          0, rtc->base + SUN6I_LOSC_OUT_GATING,
                                          SUN6I_LOSC_OUT_GATING_EN_OFFSET, 0,
                                          &rtc->lock);
@@ -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);