summaryrefslogtreecommitdiff
path: root/drivers/platform
diff options
context:
space:
mode:
authorAlex Waterman <alexw@nvidia.com>2013-11-01 16:31:35 -0700
committerKrishna Reddy <vdumpa@nvidia.com>2014-04-11 11:14:33 -0700
commitc2a9dbc2494b51ca7b50cb4fae6101c489a00873 (patch)
tree04b3843ed360aae549de283e7e5ff8b011f7a26d /drivers/platform
parent78c99f5bf9e53ff75e04ff19b30c12dfcc60c1fa (diff)
ARM: tegra: MC: Add DT support
Adds DT support to the Tegra MC driver. This also removes the ability to boot without a DT with a proper MC node. This may need to change. Change-Id: I6d7fd7b333ae4a87e0d90b4c5f0d842837e4c638 Signed-off-by: Alex Waterman <alexw@nvidia.com> Reviewed-on: http://git-master/r/325823 Reviewed-by: Automatic_Commit_Validation_User GVS: Gerrit_Virtual_Submit Reviewed-by: Krishna Reddy <vdumpa@nvidia.com>
Diffstat (limited to 'drivers/platform')
-rw-r--r--drivers/platform/tegra/mc/mc.c153
-rw-r--r--drivers/platform/tegra/mc/mcerr-t11.c4
-rw-r--r--drivers/platform/tegra/mc/mcerr-t12.c4
-rw-r--r--drivers/platform/tegra/mc/mcerr-t14.c4
-rw-r--r--drivers/platform/tegra/mc/mcerr.c63
5 files changed, 191 insertions, 37 deletions
diff --git a/drivers/platform/tegra/mc/mc.c b/drivers/platform/tegra/mc/mc.c
index e1cdd7637d3e..3d14dcdfc530 100644
--- a/drivers/platform/tegra/mc/mc.c
+++ b/drivers/platform/tegra/mc/mc.c
@@ -25,6 +25,9 @@
#include <linux/delay.h>
#include <linux/debugfs.h>
#include <linux/tegra-soc.h>
+#include <linux/of_device.h>
+#include <linux/of_address.h>
+#include <linux/platform_device.h>
#include <mach/mc.h>
#include <mach/mcerr.h>
@@ -48,10 +51,14 @@
#endif
static DEFINE_SPINLOCK(tegra_mc_lock);
-void __iomem *mc = (void __iomem *)IO_ADDRESS(TEGRA_MC_BASE);
-#ifdef MC_DUAL_CHANNEL
-void __iomem *mc1 = (void __iomem *)IO_ADDRESS(TEGRA_MC1_BASE);
-#endif
+int mc_channels;
+void __iomem *mc;
+
+/* Only populated if there are 2 channels. */
+void __iomem *mc0;
+void __iomem *mc1;
+
+int mc_intr_count;
#ifdef CONFIG_PM_SLEEP
static u32 mc_boot_timing[MC_TIMING_REG_NUM1 + MC_TIMING_REG_NUM2
@@ -198,6 +205,9 @@ int tegra_mc_flush(int id)
unsigned long flags;
bool ret;
+ if (!mc)
+ return 0;
+
if (id < 32) {
rst_ctrl_reg = MC_CLIENT_HOTRESET_CTRL;
rst_stat_reg = MC_CLIENT_HOTRESET_STAT;
@@ -233,6 +243,9 @@ int tegra_mc_flush_done(int id)
u32 rst_ctrl_reg, rst_stat_reg;
unsigned long flags;
+ if (!mc)
+ return 0;
+
if (id < 32) {
rst_ctrl_reg = MC_CLIENT_HOTRESET_CTRL;
rst_stat_reg = MC_CLIENT_HOTRESET_STAT;
@@ -249,22 +262,116 @@ int tegra_mc_flush_done(int id)
mc_writel(rst_ctrl, rst_ctrl_reg);
spin_unlock_irqrestore(&tegra_mc_lock, flags);
-
return 0;
}
EXPORT_SYMBOL(tegra_mc_flush_done);
/*
+ * Map an MC register space. Each MC has a set of register ranges which must
+ * be parsed. The first starting address in the set of ranges is returned as
+ * it is expected that the DT file has the register ranges in ascending
+ * order.
+ *
+ * device 0 = global channel.
+ * device n = specific channel device-1, e.g device = 1 ==> channel 0.
+ */
+static void __iomem *tegra_mc_map_regs(struct platform_device *pdev, int device)
+{
+ const void *prop;
+ void __iomem *regs;
+ void __iomem *regs_start = NULL;
+ u32 reg_ranges;
+ int i, start;
+
+ prop = of_get_property(pdev->dev.of_node, "reg-ranges", NULL);
+ if (!prop) {
+ pr_err("Failed to get MC MMIO region\n");
+ pr_err(" device = %d: missing reg-ranges\n", device);
+ return NULL;
+ }
+
+ reg_ranges = be32_to_cpup(prop);
+ start = device * reg_ranges;
+
+ for (i = 0; i < reg_ranges; i++) {
+ regs = of_iomap(pdev->dev.of_node, start + i);
+ if (!regs) {
+ pr_err("Failed to get MC MMIO region\n");
+ pr_err(" device = %d, range = %u\n", device, i);
+ return NULL;
+ }
+
+ if (i == 0)
+ regs_start = regs;
+ }
+
+ return regs_start;
+}
+
+static const struct of_device_id mc_of_ids[] = {
+ { .compatible = "nvidia,tegra-mc" },
+ { }
+};
+
+/*
* MC driver init.
*/
-static int __init tegra_mc_init(void)
+static int tegra_mc_probe(struct platform_device *pdev)
{
#if defined(CONFIG_ARCH_TEGRA_3x_SOC) || \
defined(CONFIG_TEGRA_MC_EARLY_ACK)
u32 reg;
#endif
+ const void *prop;
struct dentry *mc_debugfs_dir;
+ const struct of_device_id *match;
+
+ if (!pdev->dev.of_node)
+ return -EINVAL;
+
+ match = of_match_device(mc_of_ids, &pdev->dev);
+ if (!match) {
+ pr_err("Missing DT entry!\n");
+ return -EINVAL;
+ }
+
+ /*
+ * Channel count.
+ */
+ prop = of_get_property(pdev->dev.of_node, "channels", NULL);
+ if (!prop)
+ mc_channels = 1;
+ else
+ mc_channels = be32_to_cpup(prop);
+
+ if (mc_channels != 1 && mc_channels != 2) {
+ pr_err("Invalid number of memory channels: %d\n", mc_channels);
+ return -EINVAL;
+ }
+
+ /*
+ * IO mem.
+ */
+ mc = tegra_mc_map_regs(pdev, 0);
+ if (!mc)
+ return -ENOMEM;
+ pr_info("MC mapped MMIO address: 0x%p\n", mc);
+
+ if (mc_dual_channel()) {
+ mc0 = tegra_mc_map_regs(pdev, 1);
+ if (!mc0) {
+ pr_err("Failed to make channel 0\n");
+ return -ENOMEM;
+ }
+ pr_info("MC0 mapped MMIO address: 0x%p\n", mc0);
+ mc1 = tegra_mc_map_regs(pdev, 2);
+ if (!mc1) {
+ pr_err("Failed to make channel 0\n");
+ return -ENOMEM;
+ }
+ pr_info("MC1 mapped MMIO address: 0x%p\n", mc1);
+ }
tegra_mc_timing_save();
@@ -290,8 +397,40 @@ static int __init tegra_mc_init(void)
return PTR_ERR(mc_debugfs_dir);
}
- tegra_mcerr_init(mc_debugfs_dir);
+ tegra_mcerr_init(mc_debugfs_dir, pdev);
+
+ return 0;
+}
+
+static int tegra_mc_remove(struct platform_device *pdev)
+{
+ return 0;
+}
+
+static struct platform_driver mc_driver = {
+ .driver = {
+ .name = "tegra-mc",
+ .of_match_table = mc_of_ids,
+ .owner = THIS_MODULE,
+ },
+
+ .probe = tegra_mc_probe,
+ .remove = tegra_mc_remove,
+};
+
+static int __init tegra_mc_init(void)
+{
+ int ret;
+
+ ret = platform_driver_register(&mc_driver);
+ if (ret)
+ return ret;
return 0;
}
arch_initcall(tegra_mc_init);
+
+static void __exit tegra_mc_fini(void)
+{
+}
+module_exit(tegra_mc_fini);
diff --git a/drivers/platform/tegra/mc/mcerr-t11.c b/drivers/platform/tegra/mc/mcerr-t11.c
index 5cfc1b9b8e22..368e70a4ee4b 100644
--- a/drivers/platform/tegra/mc/mcerr-t11.c
+++ b/drivers/platform/tegra/mc/mcerr-t11.c
@@ -128,7 +128,7 @@ static void mcerr_t11x_info_update(struct mc_client *c, u32 stat)
if (stat & MC_INT_SECERR_SEC)
c->intr_counts[4]++;
- if (stat & ~MC_INT_EN_MASK)
+ if (stat & ~mc_int_mask)
c->intr_counts[5]++;
}
@@ -164,7 +164,7 @@ static int mcerr_t11x_debugfs_show(struct seq_file *s, void *v)
if (strcmp(mc_clients[i].name, "dummy") == 0)
continue;
/* Only print clients who actually have errors. */
- for (j = 0; j < INTR_COUNT; j++) {
+ for (j = 0; j < mc_intr_count; j++) {
if (mc_clients[i].intr_counts[j]) {
do_print = 1;
break;
diff --git a/drivers/platform/tegra/mc/mcerr-t12.c b/drivers/platform/tegra/mc/mcerr-t12.c
index 0a2dde60e8aa..802bf6c9ef55 100644
--- a/drivers/platform/tegra/mc/mcerr-t12.c
+++ b/drivers/platform/tegra/mc/mcerr-t12.c
@@ -162,7 +162,7 @@ static void mcerr_t12x_info_update(struct mc_client *c, u32 stat)
if (stat & MC_INT_DECERR_MTS)
c->intr_counts[6]++;
- if (stat & ~MC_INT_EN_MASK)
+ if (stat & ~mc_int_mask)
c->intr_counts[7]++;
}
@@ -182,7 +182,7 @@ static int mcerr_t12x_debugfs_show(struct seq_file *s, void *v)
if (strcmp(mc_clients[i].name, "dummy") == 0)
continue;
/* Only print clients who actually have errors. */
- for (j = 0; j < INTR_COUNT; j++) {
+ for (j = 0; j < mc_intr_count; j++) {
if (mc_clients[i].intr_counts[j]) {
do_print = 1;
break;
diff --git a/drivers/platform/tegra/mc/mcerr-t14.c b/drivers/platform/tegra/mc/mcerr-t14.c
index 4400f8a4e7af..6f07a5419dd6 100644
--- a/drivers/platform/tegra/mc/mcerr-t14.c
+++ b/drivers/platform/tegra/mc/mcerr-t14.c
@@ -139,7 +139,7 @@ static void mcerr_t14x_info_update(struct mc_client *c, u32 stat)
if (stat & MC_INT_DECERR_BBC)
c->intr_counts[6]++;
- if (stat & ~MC_INT_EN_MASK)
+ if (stat & ~mc_int_mask)
c->intr_counts[7]++;
}
@@ -159,7 +159,7 @@ static int mcerr_t14x_debugfs_show(struct seq_file *s, void *v)
if (strcmp(mc_clients[i].name, "dummy") == 0)
continue;
/* Only print clients who actually have errors. */
- for (j = 0; j < INTR_COUNT; j++) {
+ for (j = 0; j < mc_intr_count; j++) {
if (mc_clients[i].intr_counts[j]) {
do_print = 1;
break;
diff --git a/drivers/platform/tegra/mc/mcerr.c b/drivers/platform/tegra/mc/mcerr.c
index 5aa8c4a5da5a..00372ae1cefe 100644
--- a/drivers/platform/tegra/mc/mcerr.c
+++ b/drivers/platform/tegra/mc/mcerr.c
@@ -31,7 +31,8 @@
#include <linux/debugfs.h>
#include <linux/seq_file.h>
#include <linux/moduleparam.h>
-#include <linux/spinlock_types.h>
+#include <linux/platform_device.h>
+#include <linux/of_irq.h>
#include <mach/mc.h>
#include <mach/irqs.h>
@@ -132,6 +133,8 @@ static DECLARE_DELAYED_WORK(unthrottle_prints_work, unthrottle_prints);
static struct dentry *mcerr_debugfs_dir;
+u32 mc_int_mask;
+
/*
* Chip specific functions.
*/
@@ -198,16 +201,16 @@ static irqreturn_t tegra_mc_error_isr(int irq, void *data)
cancel_delayed_work(&unthrottle_prints_work);
-#ifdef MC_DUAL_CHANNEL
- /*
- * Interrupts can come from either MC; handle the case in which the
- * interrupt is generated by the second MC.
- */
- if (intr & MC_INT_EXT_INTR_IN) {
- err_mc = 1;
- intr = __mc_readl(err_mc, MC_INT_STATUS);
+ if (mc_dual_channel()) {
+ /*
+ * Interrupts can come from either MC; handle the case in which
+ * the interrupt is generated by the second MC.
+ */
+ if (intr & MC_INT_EXT_INTR_IN) {
+ err_mc = 1;
+ intr = __mc_readl(err_mc, MC_INT_STATUS);
+ }
}
-#endif
/*
* Sometimes the MC seems to generate spurious interrupts - that
@@ -219,7 +222,7 @@ static irqreturn_t tegra_mc_error_isr(int irq, void *data)
goto out;
}
- intr &= MC_INT_EN_MASK;
+ intr &= mc_int_mask;
if (intr & MC_INT_ARBITRATION_EMEM) {
arb_intr();
if (intr == MC_INT_ARBITRATION_EMEM)
@@ -231,9 +234,9 @@ static irqreturn_t tegra_mc_error_isr(int irq, void *data)
count = ++error_count;
spin_unlock(&mc_lock);
- fault = chip_specific.mcerr_info(intr & MC_INT_EN_MASK);
+ fault = chip_specific.mcerr_info(intr & mc_int_mask);
if (WARN(!fault, "[mcerr] Unknown error! intr sig: 0x%08x\n",
- intr & MC_INT_EN_MASK))
+ intr & mc_int_mask))
goto out;
if (fault->flags & E_NO_STATUS) {
@@ -262,7 +265,7 @@ static irqreturn_t tegra_mc_error_isr(int irq, void *data)
else
smmu_info = NULL;
- chip_specific.mcerr_info_update(client, intr & MC_INT_EN_MASK);
+ chip_specific.mcerr_info_update(client, intr & mc_int_mask);
if (mcerr_throttle_enabled && count >= MAX_PRINTS) {
schedule_delayed_work(&unthrottle_prints_work, HZ/2);
@@ -305,7 +308,7 @@ static void mcerr_default_info_update(struct mc_client *c, u32 stat)
c->intr_counts[2]++;
/* Unknown interrupts. */
- if (stat & ~MC_INT_EN_MASK)
+ if (stat & ~mc_int_mask)
c->intr_counts[3]++;
}
@@ -341,7 +344,7 @@ static int mcerr_default_debugfs_show(struct seq_file *s, void *v)
do_print = 0;
/* Only print clients who actually have errors. */
- for (j = 0; j < INTR_COUNT; j++) {
+ for (j = 0; j < mc_intr_count; j++) {
if (mc_clients[i].intr_counts[j]) {
do_print = 1;
break;
@@ -398,9 +401,10 @@ DEFINE_SIMPLE_ATTRIBUTE(mcerr_throttle_debugfs_fops, __get_throttle,
* non-essential piece of the kernel no reason to fail the entire MC init
* if this fails.
*/
-int __init tegra_mcerr_init(struct dentry *mc_parent)
+int tegra_mcerr_init(struct dentry *mc_parent, struct platform_device *pdev)
{
- u32 reg;
+ int irq;
+ const void *prop;
chip_specific.mcerr_info = mcerr_default_info;
chip_specific.mcerr_info_update = mcerr_default_info_update;
@@ -414,15 +418,26 @@ int __init tegra_mcerr_init(struct dentry *mc_parent)
*/
mcerr_chip_specific_setup(&chip_specific);
- if (request_irq(INT_MC_GENERAL, tegra_mc_error_isr, 0,
- "mc_status", NULL)) {
- pr_err("%s: unable to register MC error interrupt\n", __func__);
+ irq = irq_of_parse_and_map(pdev->dev.of_node, 0);
+ if (irq < 0) {
+ pr_err("Unable to parse/map MC error interrupt\n");
goto done;
- } else {
- reg = MC_INT_EN_MASK;
- mc_writel(reg, MC_INT_MASK);
}
+ if (request_irq(irq, tegra_mc_error_isr, 0, "mc_status", NULL)) {
+ pr_err("Unable to register MC error interrupt\n");
+ goto done;
+ }
+
+ prop = of_get_property(pdev->dev.of_node, "int_mask", NULL);
+ if (!prop) {
+ pr_err("No int_mask prop for mcerr!\n");
+ return -EINVAL;
+ }
+
+ mc_int_mask = be32_to_cpup(prop);
+ mc_writel(mc_int_mask, MC_INT_MASK);
+
mcerr_debugfs_dir = debugfs_create_dir("err", mc_parent);
if (mcerr_debugfs_dir == NULL) {
pr_err("Failed to make debugfs node: %ld\n",