Merge commit '4f3530f4a41d49c41015020cd9a5ed5c95b5d2db' into omap-for-v4.20/ti-sysc
[muen/linux.git] / drivers / bus / ti-sysc.c
index 4576a1268e0e4be8fc44ecdac1d40e6fd5353408..799d69f646fc767b496070cfba3299c8d4b1f8d3 100644 (file)
 #include <linux/of_address.h>
 #include <linux/of_platform.h>
 #include <linux/slab.h>
+#include <linux/iopoll.h>
 
 #include <linux/platform_data/ti-sysc.h>
 
 #include <dt-bindings/bus/ti-sysc.h>
 
+#define MAX_MODULE_SOFTRESET_WAIT              10000
+
 static const char * const reg_names[] = { "rev", "sysc", "syss", };
 
 enum sysc_clocks {
@@ -84,10 +87,16 @@ struct sysc {
        u32 revision;
        bool enabled;
        bool needs_resume;
+       unsigned int noirq_suspend:1;
        bool child_needs_resume;
        struct delayed_work idle_work;
 };
 
+void sysc_write(struct sysc *ddata, int offset, u32 value)
+{
+       writel_relaxed(value, ddata->module_va + offset);
+}
+
 static u32 sysc_read(struct sysc *ddata, int offset)
 {
        if (ddata->cfg.quirks & SYSC_QUIRK_16BIT) {
@@ -701,19 +710,25 @@ static int sysc_suspend(struct device *dev)
 
        ddata = dev_get_drvdata(dev);
 
-       if (ddata->cfg.quirks & (SYSC_QUIRK_RESOURCE_PROVIDER |
-                                SYSC_QUIRK_LEGACY_IDLE))
+       if (ddata->cfg.quirks & SYSC_QUIRK_LEGACY_IDLE)
                return 0;
 
-       if (!ddata->enabled)
+       if (!ddata->enabled || ddata->noirq_suspend)
                return 0;
 
        dev_dbg(ddata->dev, "%s %s\n", __func__,
                ddata->name ? ddata->name : "");
 
        error = pm_runtime_put_sync_suspend(dev);
-       if (error < 0) {
-               dev_warn(ddata->dev, "%s not idle %i %s\n",
+       if (error == -EBUSY) {
+               dev_dbg(ddata->dev, "%s busy, tagging for noirq suspend %s\n",
+                       __func__, ddata->name ? ddata->name : "");
+
+               ddata->noirq_suspend = true;
+
+               return 0;
+       } else if (error < 0) {
+               dev_warn(ddata->dev, "%s cannot suspend %i %s\n",
                         __func__, error,
                         ddata->name ? ddata->name : "");
 
@@ -732,73 +747,86 @@ static int sysc_resume(struct device *dev)
 
        ddata = dev_get_drvdata(dev);
 
-       if (ddata->cfg.quirks & (SYSC_QUIRK_RESOURCE_PROVIDER |
-                                SYSC_QUIRK_LEGACY_IDLE))
+       if (ddata->cfg.quirks & SYSC_QUIRK_LEGACY_IDLE)
                return 0;
 
-       if (ddata->needs_resume) {
-               dev_dbg(ddata->dev, "%s %s\n", __func__,
-                       ddata->name ? ddata->name : "");
+       if (!ddata->needs_resume || ddata->noirq_suspend)
+               return 0;
 
-               error = pm_runtime_get_sync(dev);
-               if (error < 0) {
-                       dev_err(ddata->dev, "%s  error %i %s\n",
-                               __func__, error,
-                                ddata->name ? ddata->name : "");
+       dev_dbg(ddata->dev, "%s %s\n", __func__,
+               ddata->name ? ddata->name : "");
 
-                       return error;
-               }
+       error = pm_runtime_get_sync(dev);
+       if (error < 0) {
+               dev_err(ddata->dev, "%s  error %i %s\n",
+                       __func__, error,
+                       ddata->name ? ddata->name : "");
 
-               ddata->needs_resume = false;
+               return error;
        }
 
+       ddata->needs_resume = false;
+
        return 0;
 }
 
 static int sysc_noirq_suspend(struct device *dev)
 {
        struct sysc *ddata;
+       int error;
 
        ddata = dev_get_drvdata(dev);
 
        if (ddata->cfg.quirks & SYSC_QUIRK_LEGACY_IDLE)
                return 0;
 
-       if (!(ddata->cfg.quirks & SYSC_QUIRK_RESOURCE_PROVIDER))
-               return 0;
-
-       if (!ddata->enabled)
+       if (!ddata->enabled || !ddata->noirq_suspend)
                return 0;
 
        dev_dbg(ddata->dev, "%s %s\n", __func__,
                ddata->name ? ddata->name : "");
 
+       error = sysc_runtime_suspend(dev);
+       if (error) {
+               dev_warn(ddata->dev, "%s busy %i %s\n",
+                        __func__, error, ddata->name ? ddata->name : "");
+
+               return 0;
+       }
+
        ddata->needs_resume = true;
 
-       return sysc_runtime_suspend(dev);
+       return 0;
 }
 
 static int sysc_noirq_resume(struct device *dev)
 {
        struct sysc *ddata;
+       int error;
 
        ddata = dev_get_drvdata(dev);
 
        if (ddata->cfg.quirks & SYSC_QUIRK_LEGACY_IDLE)
                return 0;
 
-       if (!(ddata->cfg.quirks & SYSC_QUIRK_RESOURCE_PROVIDER))
+       if (!ddata->needs_resume || !ddata->noirq_suspend)
                return 0;
 
-       if (ddata->needs_resume) {
-               dev_dbg(ddata->dev, "%s %s\n", __func__,
-                       ddata->name ? ddata->name : "");
+       dev_dbg(ddata->dev, "%s %s\n", __func__,
+               ddata->name ? ddata->name : "");
 
-               ddata->needs_resume = false;
+       error = sysc_runtime_resume(dev);
+       if (error) {
+               dev_warn(ddata->dev, "%s cannot resume %i %s\n",
+                        __func__, error,
+                        ddata->name ? ddata->name : "");
 
-               return sysc_runtime_resume(dev);
+               return error;
        }
 
+       /* Maybe also reconsider clearing noirq_suspend at some point */
+       ddata->needs_resume = false;
+
        return 0;
 }
 #endif
@@ -837,28 +865,8 @@ struct sysc_revision_quirk {
        }
 
 static const struct sysc_revision_quirk sysc_revision_quirks[] = {
-       /* These need to use noirq_suspend */
-       SYSC_QUIRK("control", 0, 0, 0x10, -1, 0x40000900, 0xffffffff,
-                  SYSC_QUIRK_RESOURCE_PROVIDER),
-       SYSC_QUIRK("i2c", 0, 0, 0x10, 0x90, 0x5040000a, 0xffffffff,
-                  SYSC_QUIRK_RESOURCE_PROVIDER),
-       SYSC_QUIRK("mcspi", 0, 0, 0x10, -1, 0x40300a0b, 0xffffffff,
-                  SYSC_QUIRK_RESOURCE_PROVIDER),
-       SYSC_QUIRK("prcm", 0, 0, -1, -1, 0x40000100, 0xffffffff,
-                  SYSC_QUIRK_RESOURCE_PROVIDER),
-       SYSC_QUIRK("ocp2scp", 0, 0, 0x10, 0x14, 0x50060005, 0xffffffff,
-                  SYSC_QUIRK_RESOURCE_PROVIDER),
-       SYSC_QUIRK("padconf", 0, 0, 0x10, -1, 0x4fff0800, 0xffffffff,
-                  SYSC_QUIRK_RESOURCE_PROVIDER),
-       SYSC_QUIRK("scm", 0, 0, 0x10, -1, 0x40000900, 0xffffffff,
-                  SYSC_QUIRK_RESOURCE_PROVIDER),
-       SYSC_QUIRK("scrm", 0, 0, -1, -1, 0x00000010, 0xffffffff,
-                  SYSC_QUIRK_RESOURCE_PROVIDER),
-       SYSC_QUIRK("sdma", 0, 0, 0x2c, 0x28, 0x00010900, 0xffffffff,
-                  SYSC_QUIRK_RESOURCE_PROVIDER),
-
        /* These drivers need to be fixed to not use pm_runtime_irq_safe() */
-       SYSC_QUIRK("gpio", 0, 0, 0x10, 0x114, 0x50600801, 0xffffffff,
+       SYSC_QUIRK("gpio", 0, 0, 0x10, 0x114, 0x50600801, 0xffff0fff,
                   SYSC_QUIRK_LEGACY_IDLE | SYSC_QUIRK_OPT_CLKS_IN_RESET),
        SYSC_QUIRK("mmu", 0, 0, 0x10, 0x14, 0x00000020, 0xffffffff,
                   SYSC_QUIRK_LEGACY_IDLE),
@@ -878,33 +886,60 @@ static const struct sysc_revision_quirk sysc_revision_quirks[] = {
        SYSC_QUIRK("uart", 0, 0x50, 0x54, 0x58, 0x00000052, 0xffffffff,
                   SYSC_QUIRK_LEGACY_IDLE),
        /* Uarts on omap4 and later */
-       SYSC_QUIRK("uart", 0, 0x50, 0x54, 0x58, 0x50411e03, 0xffffffff,
+       SYSC_QUIRK("uart", 0, 0x50, 0x54, 0x58, 0x50411e03, 0xffff00ff,
                   SYSC_QUIRK_LEGACY_IDLE),
-
-       /* These devices don't yet suspend properly without legacy setting */
-       SYSC_QUIRK("sdio", 0, 0, 0x10, -1, 0x40202301, 0xffffffff,
-                  SYSC_QUIRK_LEGACY_IDLE),
-       SYSC_QUIRK("wdt", 0, 0, 0x10, 0x14, 0x502a0500, 0xffffffff,
-                  SYSC_QUIRK_LEGACY_IDLE),
-       SYSC_QUIRK("wdt", 0, 0, 0x10, 0x14, 0x502a0d00, 0xffffffff,
+       SYSC_QUIRK("uart", 0, 0x50, 0x54, 0x58, 0x47422e03, 0xffffffff,
                   SYSC_QUIRK_LEGACY_IDLE),
 
 #ifdef DEBUG
+       SYSC_QUIRK("adc", 0, 0, 0x10, -1, 0x47300001, 0xffffffff, 0),
        SYSC_QUIRK("aess", 0, 0, 0x10, -1, 0x40000000, 0xffffffff, 0),
+       SYSC_QUIRK("control", 0, 0, 0x10, -1, 0x40000900, 0xffffffff, 0),
+       SYSC_QUIRK("cpgmac", 0, 0x1200, 0x1208, 0x1204, 0x4edb1902,
+                  0xffff00f0, 0),
+       SYSC_QUIRK("dcan", 0, 0, -1, -1, 0xffffffff, 0xffffffff, 0),
+       SYSC_QUIRK("dcan", 0, 0, -1, -1, 0x00001401, 0xffffffff, 0),
+       SYSC_QUIRK("dwc3", 0, 0, 0x10, -1, 0x500a0200, 0xffffffff, 0),
+       SYSC_QUIRK("epwmss", 0, 0, 0x4, -1, 0x47400001, 0xffffffff, 0),
        SYSC_QUIRK("gpu", 0, 0x1fc00, 0x1fc10, -1, 0, 0, 0),
        SYSC_QUIRK("hdq1w", 0, 0, 0x14, 0x18, 0x00000006, 0xffffffff, 0),
+       SYSC_QUIRK("hdq1w", 0, 0, 0x14, 0x18, 0x0000000a, 0xffffffff, 0),
        SYSC_QUIRK("hsi", 0, 0, 0x10, 0x14, 0x50043101, 0xffffffff, 0),
        SYSC_QUIRK("iss", 0, 0, 0x10, -1, 0x40000101, 0xffffffff, 0),
+       SYSC_QUIRK("i2c", 0, 0, 0x10, 0x90, 0x5040000a, 0xfffffff0, 0),
+       SYSC_QUIRK("lcdc", 0, 0, 0x54, -1, 0x4f201000, 0xffffffff, 0),
        SYSC_QUIRK("mcasp", 0, 0, 0x4, -1, 0x44306302, 0xffffffff, 0),
+       SYSC_QUIRK("mcasp", 0, 0, 0x4, -1, 0x44307b02, 0xffffffff, 0),
        SYSC_QUIRK("mcbsp", 0, -1, 0x8c, -1, 0, 0, 0),
+       SYSC_QUIRK("mcspi", 0, 0, 0x10, -1, 0x40300a0b, 0xffffffff, 0),
+       SYSC_QUIRK("mcspi", 0, 0, 0x110, 0x114, 0x40300a0b, 0xffffffff, 0),
        SYSC_QUIRK("mailbox", 0, 0, 0x10, -1, 0x00000400, 0xffffffff, 0),
+       SYSC_QUIRK("m3", 0, 0, -1, -1, 0x5f580105, 0x0fff0f00, 0),
+       SYSC_QUIRK("ocp2scp", 0, 0, 0x10, 0x14, 0x50060005, 0xffffffff, 0),
+       SYSC_QUIRK("ocp2scp", 0, 0, -1, -1, 0x50060007, 0xffffffff, 0),
+       SYSC_QUIRK("padconf", 0, 0, 0x10, -1, 0x4fff0800, 0xffffffff, 0),
+       SYSC_QUIRK("prcm", 0, 0, -1, -1, 0x40000100, 0xffffffff, 0),
+       SYSC_QUIRK("prcm", 0, 0, -1, -1, 0x00004102, 0xffffffff, 0),
+       SYSC_QUIRK("prcm", 0, 0, -1, -1, 0x40000400, 0xffffffff, 0),
+       SYSC_QUIRK("scm", 0, 0, 0x10, -1, 0x40000900, 0xffffffff, 0),
+       SYSC_QUIRK("scm", 0, 0, -1, -1, 0x4e8b0100, 0xffffffff, 0),
+       SYSC_QUIRK("scm", 0, 0, -1, -1, 0x4f000100, 0xffffffff, 0),
+       SYSC_QUIRK("scrm", 0, 0, -1, -1, 0x00000010, 0xffffffff, 0),
+       SYSC_QUIRK("sdio", 0, 0, 0x10, -1, 0x40202301, 0xffffffff, 0),
+       SYSC_QUIRK("sdio", 0, 0x2fc, 0x110, 0x114, 0x31010000, 0xffffffff, 0),
+       SYSC_QUIRK("sdma", 0, 0, 0x2c, 0x28, 0x00010900, 0xffffffff, 0),
        SYSC_QUIRK("slimbus", 0, 0, 0x10, -1, 0x40000902, 0xffffffff, 0),
        SYSC_QUIRK("slimbus", 0, 0, 0x10, -1, 0x40002903, 0xffffffff, 0),
        SYSC_QUIRK("spinlock", 0, 0, 0x10, -1, 0x50020000, 0xffffffff, 0),
+       SYSC_QUIRK("rng", 0, 0x1fe0, 0x1fe4, -1, 0x00000020, 0xffffffff, 0),
+       SYSC_QUIRK("rtc", 0, 0x74, 0x78, -1, 0x4eb01908, 0xfffff0f0, 0),
+       SYSC_QUIRK("timer32k", 0, 0, 0x4, -1, 0x00000060, 0xffffffff, 0),
        SYSC_QUIRK("usbhstll", 0, 0, 0x10, 0x14, 0x00000004, 0xffffffff, 0),
        SYSC_QUIRK("usb_host_hs", 0, 0, 0x10, 0x14, 0x50700100, 0xffffffff, 0),
        SYSC_QUIRK("usb_otg_hs", 0, 0x400, 0x404, 0x408, 0x00000050,
                   0xffffffff, 0),
+       SYSC_QUIRK("wdt", 0, 0, 0x10, 0x14, 0x502a0500, 0xfffff0f0, 0),
+       SYSC_QUIRK("vfpe", 0, 0, 0x104, -1, 0x4d001200, 0xffffffff, 0),
 #endif
 };
 
@@ -940,6 +975,36 @@ static void sysc_init_revision_quirks(struct sysc *ddata)
        }
 }
 
+static int sysc_reset(struct sysc *ddata)
+{
+       int offset = ddata->offsets[SYSC_SYSCONFIG];
+       int val;
+
+       if (ddata->legacy_mode || offset < 0 ||
+           ddata->cfg.quirks & SYSC_QUIRK_NO_RESET_ON_INIT)
+               return 0;
+
+       /*
+        * Currently only support reset status in sysstatus.
+        * Warn and return error in all other cases
+        */
+       if (!ddata->cfg.syss_mask) {
+               dev_err(ddata->dev, "No ti,syss-mask. Reset failed\n");
+               return -EINVAL;
+       }
+
+       val = sysc_read(ddata, offset);
+       val |= (0x1 << ddata->cap->regbits->srst_shift);
+       sysc_write(ddata, offset, val);
+
+       /* Poll on reset status */
+       offset = ddata->offsets[SYSC_SYSSTATUS];
+
+       return readl_poll_timeout(ddata->module_va + offset, val,
+                                 (val & ddata->cfg.syss_mask) == 0x0,
+                                 100, MAX_MODULE_SOFTRESET_WAIT);
+}
+
 /* At this point the module is configured enough to read the revision */
 static int sysc_init_module(struct sysc *ddata)
 {
@@ -957,6 +1022,14 @@ static int sysc_init_module(struct sysc *ddata)
                return 0;
        }
 
+       error = sysc_reset(ddata);
+       if (error) {
+               dev_err(ddata->dev, "Reset failed with %d\n", error);
+               pm_runtime_put_sync(ddata->dev);
+
+               return error;
+       }
+
        ddata->revision = sysc_read_revision(ddata);
        pm_runtime_put_sync(ddata->dev);
 
@@ -1549,6 +1622,23 @@ static const struct sysc_capabilities sysc_omap4_usb_host_fs = {
        .regbits = &sysc_regbits_omap4_usb_host_fs,
 };
 
+static const struct sysc_regbits sysc_regbits_dra7_mcan = {
+       .dmadisable_shift = -ENODEV,
+       .midle_shift = -ENODEV,
+       .sidle_shift = -ENODEV,
+       .clkact_shift = -ENODEV,
+       .enwkup_shift = 4,
+       .srst_shift = 0,
+       .emufree_shift = -ENODEV,
+       .autoidle_shift = -ENODEV,
+};
+
+static const struct sysc_capabilities sysc_dra7_mcan = {
+       .type = TI_SYSC_DRA7_MCAN,
+       .sysc_mask = SYSC_DRA7_MCAN_ENAWAKEUP | SYSC_OMAP4_SOFTRESET,
+       .regbits = &sysc_regbits_dra7_mcan,
+};
+
 static int sysc_init_pdata(struct sysc *ddata)
 {
        struct ti_sysc_platform_data *pdata = dev_get_platdata(ddata->dev);
@@ -1740,6 +1830,7 @@ static const struct of_device_id sysc_match[] = {
        { .compatible = "ti,sysc-mcasp", .data = &sysc_omap4_mcasp, },
        { .compatible = "ti,sysc-usb-host-fs",
          .data = &sysc_omap4_usb_host_fs, },
+       { .compatible = "ti,sysc-dra7-mcan", .data = &sysc_dra7_mcan, },
        {  },
 };
 MODULE_DEVICE_TABLE(of, sysc_match);