diff options
author | Alex Waterman <alexw@nvidia.com> | 2013-11-01 16:31:35 -0700 |
---|---|---|
committer | Krishna Reddy <vdumpa@nvidia.com> | 2014-04-11 11:14:33 -0700 |
commit | c2a9dbc2494b51ca7b50cb4fae6101c489a00873 (patch) | |
tree | 04b3843ed360aae549de283e7e5ff8b011f7a26d /drivers/platform | |
parent | 78c99f5bf9e53ff75e04ff19b30c12dfcc60c1fa (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.c | 153 | ||||
-rw-r--r-- | drivers/platform/tegra/mc/mcerr-t11.c | 4 | ||||
-rw-r--r-- | drivers/platform/tegra/mc/mcerr-t12.c | 4 | ||||
-rw-r--r-- | drivers/platform/tegra/mc/mcerr-t14.c | 4 | ||||
-rw-r--r-- | drivers/platform/tegra/mc/mcerr.c | 63 |
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", |