RDMA/bnxt_re: Fix system crash during RDMA resource initialization
[linux-2.6-block.git] / drivers / bus / ti-sysc.c
index 80d60f43db56123076fed5ea2047da35af88af8f..e4fe954e63a9be53b74397c825bf4f57b06dcca5 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 {
@@ -88,6 +91,11 @@ struct sysc {
        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) {
@@ -490,32 +498,29 @@ static int sysc_check_registers(struct sysc *ddata)
 
 /**
  * syc_ioremap - ioremap register space for the interconnect target module
- * @ddata: deviec driver data
+ * @ddata: device driver data
  *
  * Note that the interconnect target module registers can be anywhere
- * within the first child device address space. For example, SGX has
- * them at offset 0x1fc00 in the 32MB module address space. We just
- * what we need around the interconnect target module registers.
+ * within the interconnect target module range. For example, SGX has
+ * them at offset 0x1fc00 in the 32MB module address space. And cpsw
+ * has them at offset 0x1200 in the CPSW_WR child. Usually the
+ * the interconnect target module registers are at the beginning of
+ * the module range though.
  */
 static int sysc_ioremap(struct sysc *ddata)
 {
-       u32 size = 0;
-
-       if (ddata->offsets[SYSC_SYSSTATUS] >= 0)
-               size = ddata->offsets[SYSC_SYSSTATUS];
-       else if (ddata->offsets[SYSC_SYSCONFIG] >= 0)
-               size = ddata->offsets[SYSC_SYSCONFIG];
-       else if (ddata->offsets[SYSC_REVISION] >= 0)
-               size = ddata->offsets[SYSC_REVISION];
-       else
-               return -EINVAL;
+       int size;
+
+       size = max3(ddata->offsets[SYSC_REVISION],
+                   ddata->offsets[SYSC_SYSCONFIG],
+                   ddata->offsets[SYSC_SYSSTATUS]);
 
-       size &= 0xfff00;
-       size += SZ_256;
+       if (size < 0 || (size + sizeof(u32)) > ddata->module_size)
+               return -EINVAL;
 
        ddata->module_va = devm_ioremap(ddata->dev,
                                        ddata->module_pa,
-                                       size);
+                                       size + sizeof(u32));
        if (!ddata->module_va)
                return -EIO;
 
@@ -943,6 +948,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)
 {
@@ -960,6 +995,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);
 
@@ -1178,10 +1221,10 @@ static int sysc_child_suspend_noirq(struct device *dev)
        if (!pm_runtime_status_suspended(dev)) {
                error = pm_generic_runtime_suspend(dev);
                if (error) {
-                       dev_err(dev, "%s error at %i: %i\n",
-                               __func__, __LINE__, error);
+                       dev_warn(dev, "%s busy at %i: %i\n",
+                                __func__, __LINE__, error);
 
-                       return error;
+                       return 0;
                }
 
                error = sysc_runtime_suspend(ddata->dev);
@@ -1552,6 +1595,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);
@@ -1743,6 +1803,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);