Add the rt linux 4.1.3-rt3 as base
[kvmfornfv.git] / kernel / drivers / memory / tegra20-mc.c
diff --git a/kernel/drivers/memory/tegra20-mc.c b/kernel/drivers/memory/tegra20-mc.c
new file mode 100644 (file)
index 0000000..cc309a0
--- /dev/null
@@ -0,0 +1,254 @@
+/*
+ * Tegra20 Memory Controller
+ *
+ * Copyright (c) 2012, NVIDIA CORPORATION.  All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include <linux/err.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/ratelimit.h>
+#include <linux/platform_device.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+
+#define DRV_NAME "tegra20-mc"
+
+#define MC_INTSTATUS                   0x0
+#define MC_INTMASK                     0x4
+
+#define MC_INT_ERR_SHIFT               6
+#define MC_INT_ERR_MASK                        (0x1f << MC_INT_ERR_SHIFT)
+#define MC_INT_DECERR_EMEM             BIT(MC_INT_ERR_SHIFT)
+#define MC_INT_INVALID_GART_PAGE       BIT(MC_INT_ERR_SHIFT + 1)
+#define MC_INT_SECURITY_VIOLATION      BIT(MC_INT_ERR_SHIFT + 2)
+#define MC_INT_ARBITRATION_EMEM                BIT(MC_INT_ERR_SHIFT + 3)
+
+#define MC_GART_ERROR_REQ              0x30
+#define MC_DECERR_EMEM_OTHERS_STATUS   0x58
+#define MC_SECURITY_VIOLATION_STATUS   0x74
+
+#define SECURITY_VIOLATION_TYPE                BIT(30) /* 0=TRUSTZONE, 1=CARVEOUT */
+
+#define MC_CLIENT_ID_MASK              0x3f
+
+#define NUM_MC_REG_BANKS               2
+
+struct tegra20_mc {
+       void __iomem *regs[NUM_MC_REG_BANKS];
+       struct device *dev;
+};
+
+static inline u32 mc_readl(struct tegra20_mc *mc, u32 offs)
+{
+       u32 val = 0;
+
+       if (offs < 0x24)
+               val = readl(mc->regs[0] + offs);
+       else if (offs < 0x400)
+               val = readl(mc->regs[1] + offs - 0x3c);
+
+       return val;
+}
+
+static inline void mc_writel(struct tegra20_mc *mc, u32 val, u32 offs)
+{
+       if (offs < 0x24)
+               writel(val, mc->regs[0] + offs);
+       else if (offs < 0x400)
+               writel(val, mc->regs[1] + offs - 0x3c);
+}
+
+static const char * const tegra20_mc_client[] = {
+       "cbr_display0a",
+       "cbr_display0ab",
+       "cbr_display0b",
+       "cbr_display0bb",
+       "cbr_display0c",
+       "cbr_display0cb",
+       "cbr_display1b",
+       "cbr_display1bb",
+       "cbr_eppup",
+       "cbr_g2pr",
+       "cbr_g2sr",
+       "cbr_mpeunifbr",
+       "cbr_viruv",
+       "csr_avpcarm7r",
+       "csr_displayhc",
+       "csr_displayhcb",
+       "csr_fdcdrd",
+       "csr_g2dr",
+       "csr_host1xdmar",
+       "csr_host1xr",
+       "csr_idxsrd",
+       "csr_mpcorer",
+       "csr_mpe_ipred",
+       "csr_mpeamemrd",
+       "csr_mpecsrd",
+       "csr_ppcsahbdmar",
+       "csr_ppcsahbslvr",
+       "csr_texsrd",
+       "csr_vdebsevr",
+       "csr_vdember",
+       "csr_vdemcer",
+       "csr_vdetper",
+       "cbw_eppu",
+       "cbw_eppv",
+       "cbw_eppy",
+       "cbw_mpeunifbw",
+       "cbw_viwsb",
+       "cbw_viwu",
+       "cbw_viwv",
+       "cbw_viwy",
+       "ccw_g2dw",
+       "csw_avpcarm7w",
+       "csw_fdcdwr",
+       "csw_host1xw",
+       "csw_ispw",
+       "csw_mpcorew",
+       "csw_mpecswr",
+       "csw_ppcsahbdmaw",
+       "csw_ppcsahbslvw",
+       "csw_vdebsevw",
+       "csw_vdembew",
+       "csw_vdetpmw",
+};
+
+static void tegra20_mc_decode(struct tegra20_mc *mc, int n)
+{
+       u32 addr, req;
+       const char *client = "Unknown";
+       int idx, cid;
+       const struct reg_info {
+               u32 offset;
+               u32 write_bit;  /* 0=READ, 1=WRITE */
+               int cid_shift;
+               char *message;
+       } reg[] = {
+               {
+                       .offset = MC_DECERR_EMEM_OTHERS_STATUS,
+                       .write_bit = 31,
+                       .message = "MC_DECERR",
+               },
+               {
+                       .offset = MC_GART_ERROR_REQ,
+                       .cid_shift = 1,
+                       .message = "MC_GART_ERR",
+
+               },
+               {
+                       .offset = MC_SECURITY_VIOLATION_STATUS,
+                       .write_bit = 31,
+                       .message = "MC_SECURITY_ERR",
+               },
+       };
+
+       idx = n - MC_INT_ERR_SHIFT;
+       if ((idx < 0) || (idx >= ARRAY_SIZE(reg))) {
+               dev_err_ratelimited(mc->dev, "Unknown interrupt status %08lx\n",
+                                   BIT(n));
+               return;
+       }
+
+       req = mc_readl(mc, reg[idx].offset);
+       cid = (req >> reg[idx].cid_shift) & MC_CLIENT_ID_MASK;
+       if (cid < ARRAY_SIZE(tegra20_mc_client))
+               client = tegra20_mc_client[cid];
+
+       addr = mc_readl(mc, reg[idx].offset + sizeof(u32));
+
+       dev_err_ratelimited(mc->dev, "%s (0x%08x): 0x%08x %s (%s %s)\n",
+                          reg[idx].message, req, addr, client,
+                          (req & BIT(reg[idx].write_bit)) ? "write" : "read",
+                          (reg[idx].offset == MC_SECURITY_VIOLATION_STATUS) ?
+                          ((req & SECURITY_VIOLATION_TYPE) ?
+                           "carveout" : "trustzone") : "");
+}
+
+static const struct of_device_id tegra20_mc_of_match[] = {
+       { .compatible = "nvidia,tegra20-mc", },
+       {},
+};
+
+static irqreturn_t tegra20_mc_isr(int irq, void *data)
+{
+       u32 stat, mask, bit;
+       struct tegra20_mc *mc = data;
+
+       stat = mc_readl(mc, MC_INTSTATUS);
+       mask = mc_readl(mc, MC_INTMASK);
+       mask &= stat;
+       if (!mask)
+               return IRQ_NONE;
+       while ((bit = ffs(mask)) != 0) {
+               tegra20_mc_decode(mc, bit - 1);
+               mask &= ~BIT(bit - 1);
+       }
+
+       mc_writel(mc, stat, MC_INTSTATUS);
+       return IRQ_HANDLED;
+}
+
+static int tegra20_mc_probe(struct platform_device *pdev)
+{
+       struct resource *irq;
+       struct tegra20_mc *mc;
+       int i, err;
+       u32 intmask;
+
+       mc = devm_kzalloc(&pdev->dev, sizeof(*mc), GFP_KERNEL);
+       if (!mc)
+               return -ENOMEM;
+       mc->dev = &pdev->dev;
+
+       for (i = 0; i < ARRAY_SIZE(mc->regs); i++) {
+               struct resource *res;
+
+               res = platform_get_resource(pdev, IORESOURCE_MEM, i);
+               mc->regs[i] = devm_ioremap_resource(&pdev->dev, res);
+               if (IS_ERR(mc->regs[i]))
+                       return PTR_ERR(mc->regs[i]);
+       }
+
+       irq = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
+       if (!irq)
+               return -ENODEV;
+       err = devm_request_irq(&pdev->dev, irq->start, tegra20_mc_isr,
+                              IRQF_SHARED, dev_name(&pdev->dev), mc);
+       if (err)
+               return -ENODEV;
+
+       platform_set_drvdata(pdev, mc);
+
+       intmask = MC_INT_INVALID_GART_PAGE |
+               MC_INT_DECERR_EMEM | MC_INT_SECURITY_VIOLATION;
+       mc_writel(mc, intmask, MC_INTMASK);
+       return 0;
+}
+
+static struct platform_driver tegra20_mc_driver = {
+       .probe = tegra20_mc_probe,
+       .driver = {
+               .name = DRV_NAME,
+               .of_match_table = tegra20_mc_of_match,
+       },
+};
+module_platform_driver(tegra20_mc_driver);
+
+MODULE_AUTHOR("Hiroshi DOYU <hdoyu@nvidia.com>");
+MODULE_DESCRIPTION("Tegra20 MC driver");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:" DRV_NAME);