diff options
author | Neil Gabriel <ngabriel@nvidia.com> | 2013-12-05 00:00:46 -0600 |
---|---|---|
committer | Mitch Luban <mluban@nvidia.com> | 2014-02-21 11:34:11 -0800 |
commit | ea3f309322f575e288ffe99ab47013e299303f01 (patch) | |
tree | 32c598f2b20bc07b8d6ef7ba1b2438f083786e12 /drivers/platform | |
parent | 72e1d7775b4aa080d6c2187200395203605225a4 (diff) |
Hierarchical Interrupt Controller - Initial Draft
First draft or a new driver for the Tegra Hierarchical Interrupt
Controller. Enables and configures basic Mselect errors and interrupts
and hooks an interrupt handler.
Change-Id: I11c1987ad4fc6431b5d7f6948388d39c02279542
Signed-off-by: Neil Gabriel <ngabriel@nvidia.com>
Reviewed-on: http://git-master/r/366669
Reviewed-by: Mitch Luban <mluban@nvidia.com>
Tested-by: Mitch Luban <mluban@nvidia.com>
Diffstat (limited to 'drivers/platform')
-rw-r--r-- | drivers/platform/tegra/Makefile | 2 | ||||
-rw-r--r-- | drivers/platform/tegra/hier_ictlr/Makefile | 8 | ||||
-rw-r--r-- | drivers/platform/tegra/hier_ictlr/hier_ictlr.c | 192 |
3 files changed, 202 insertions, 0 deletions
diff --git a/drivers/platform/tegra/Makefile b/drivers/platform/tegra/Makefile index 5f5888a2bbbd..ee9f7e05aafb 100644 --- a/drivers/platform/tegra/Makefile +++ b/drivers/platform/tegra/Makefile @@ -105,3 +105,5 @@ else # --- CONFIG_ARM64 --- # obj-y += dummy.o # generate a built-in.o endif + +obj-y += hier_ictlr/ diff --git a/drivers/platform/tegra/hier_ictlr/Makefile b/drivers/platform/tegra/hier_ictlr/Makefile new file mode 100644 index 000000000000..bfcbe36455c6 --- /dev/null +++ b/drivers/platform/tegra/hier_ictlr/Makefile @@ -0,0 +1,8 @@ +# +# Makefile for tegra ... +# + +subdir-ccflags-y = -Werror + +obj-y += hier_ictlr.o + diff --git a/drivers/platform/tegra/hier_ictlr/hier_ictlr.c b/drivers/platform/tegra/hier_ictlr/hier_ictlr.c new file mode 100644 index 000000000000..5e565d0aa090 --- /dev/null +++ b/drivers/platform/tegra/hier_ictlr/hier_ictlr.c @@ -0,0 +1,192 @@ +/* + * Copyright (c) 2013-2014, NVIDIA CORPORATION. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that 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 Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include <linux/module.h> +#include <linux/interrupt.h> +#include <linux/platform_device.h> +#include <linux/err.h> +#include <linux/io.h> + +#define HIER_GROUP_CPU_ENABLE 0x00000000 +#define HIER_GROUP_CPU_STATUS 0x00000004 +#define HIER_GROUP1_MSELECT_ERROR 15 + +#define MSELECT_CONFIG_0 0x0 +#define MSELECT_CONFIG_0_READ_TIMEOUT_EN_SLAVE0_SHIFT 16 +#define MSELECT_CONFIG_0_WRITE_TIMEOUT_EN_SLAVE0_SHIFT 17 +#define MSELECT_CONFIG_0_READ_TIMEOUT_EN_SLAVE1_SHIFT 18 +#define MSELECT_CONFIG_0_WRITE_TIMEOUT_EN_SLAVE1_SHIFT 19 +#define MSELECT_CONFIG_0_READ_TIMEOUT_EN_SLAVE2_SHIFT 20 +#define MSELECT_CONFIG_0_WRITE_TIMEOUT_EN_SLAVE2_SHIFT 21 +#define MSELECT_CONFIG_0_ERR_RESP_EN_SLAVE1_SHIFT 24 +#define MSELECT_CONFIG_0_ERR_RESP_EN_SLAVE2_SHIFT 25 + +#define MSELECT_TIMEOUT_TIMER_0 0x5c +#define MSELECT_ERROR_STATUS_0 0x60 + +struct tegra_hier_ictlr { + u32 irq; + void __iomem *hier_ictlr_base; + void __iomem *mselect_base; +}; + +static irqreturn_t tegra_hier_ictlr_irq_handler(int irq, void *data) +{ + struct device *dev = data; + struct tegra_hier_ictlr *ictlr = dev_get_drvdata(dev); + unsigned long status; + + status = readl(ictlr->mselect_base + MSELECT_ERROR_STATUS_0); + if (status != 0) { + pr_err("MSELECT error detected! status=0x%x\n", + (unsigned int)status); + BUG(); + } + + return IRQ_HANDLED; +} + +static int tegra_hier_ictlr_map_memory(struct platform_device *pdev, + struct tegra_hier_ictlr *ictlr) +{ + struct resource *res; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res) { + dev_err(&pdev->dev, + "Unable to allocate resources (hier-ictlr).\n"); + return -EBUSY; + } + + ictlr->hier_ictlr_base = devm_request_and_ioremap(&pdev->dev, res); + if (!ictlr->hier_ictlr_base) { + dev_err(&pdev->dev, "Unable to map memory (hier-ictlr).\n"); + return -EBUSY; + } + + res = platform_get_resource(pdev, IORESOURCE_MEM, 1); + if (!res) { + dev_err(&pdev->dev, + "Unable to allocate resources (mselect).\n"); + return -EBUSY; + } + + ictlr->mselect_base = devm_request_and_ioremap(&pdev->dev, res); + if (!ictlr->mselect_base) { + dev_err(&pdev->dev, "Unable to map memory (mselect).\n"); + return -EBUSY; + } + + return 0; +} + +static int tegra_hier_ictlr_irq_init(struct platform_device *pdev, + struct tegra_hier_ictlr *ictlr) +{ + unsigned long reg; + int ret; + + ictlr->irq = platform_get_irq(pdev, 0); + if (ictlr->irq <= 0) + return -EBUSY; + + ret = devm_request_irq(&pdev->dev, ictlr->irq, + tegra_hier_ictlr_irq_handler, IRQF_TRIGGER_HIGH, + "hier_ictlr_irq", &pdev->dev); + + if (ret) { + dev_err(&pdev->dev, + "Unable to request interrupt for device (err=%d).\n", + ret); + return ret; + } + + reg = readl(ictlr->hier_ictlr_base + HIER_GROUP_CPU_ENABLE); + writel(reg | (1 << HIER_GROUP1_MSELECT_ERROR), + ictlr->hier_ictlr_base + HIER_GROUP_CPU_ENABLE); + + return 0; +} + +static int tegra_hier_ictlr_mselect_init(struct platform_device *pdev, + struct tegra_hier_ictlr *ictlr) +{ + unsigned long reg; + + writel(0xFFFFFF, ictlr->mselect_base + MSELECT_TIMEOUT_TIMER_0); + + reg = readl(ictlr->mselect_base + MSELECT_CONFIG_0); + writel(reg | + ((1 << MSELECT_CONFIG_0_READ_TIMEOUT_EN_SLAVE0_SHIFT) | + (1 << MSELECT_CONFIG_0_WRITE_TIMEOUT_EN_SLAVE0_SHIFT) | + (1 << MSELECT_CONFIG_0_READ_TIMEOUT_EN_SLAVE1_SHIFT) | + (1 << MSELECT_CONFIG_0_WRITE_TIMEOUT_EN_SLAVE1_SHIFT) | + (1 << MSELECT_CONFIG_0_READ_TIMEOUT_EN_SLAVE2_SHIFT) | + (1 << MSELECT_CONFIG_0_WRITE_TIMEOUT_EN_SLAVE2_SHIFT) | + (1 << MSELECT_CONFIG_0_ERR_RESP_EN_SLAVE1_SHIFT) | + (1 << MSELECT_CONFIG_0_ERR_RESP_EN_SLAVE2_SHIFT)), + ictlr->hier_ictlr_base + MSELECT_CONFIG_0); + + return 0; +} + +static int tegra_hier_ictlr_probe(struct platform_device *pdev) +{ + struct tegra_hier_ictlr *ictlr; + int ret; + + ictlr = devm_kzalloc(&pdev->dev, sizeof(struct tegra_hier_ictlr), + GFP_KERNEL); + if (!ictlr) + return -ENOMEM; + + ret = tegra_hier_ictlr_map_memory(pdev, ictlr); + if (ret) + return ret; + + ret = tegra_hier_ictlr_mselect_init(pdev, ictlr); + if (ret) + return ret; + + ret = tegra_hier_ictlr_irq_init(pdev, ictlr); + if (ret) + return ret; + + dev_notice(&pdev->dev, "probed\n"); + + dev_set_drvdata(&pdev->dev, ictlr); + return 0; +} + +static struct platform_driver tegra_hier_ictlr_driver = { + .driver = { + .name = "tegra-hier-ictlr", + .owner = THIS_MODULE, + }, + .probe = tegra_hier_ictlr_probe, +}; + +static int __init tegra_hier_ictlr_init(void) +{ + return platform_driver_register(&tegra_hier_ictlr_driver); +} + +module_init(tegra_hier_ictlr_init); + +MODULE_DESCRIPTION("Tegra Hierarchical Interrupt Controller Driver"); +MODULE_LICENSE("GPL v2"); |