These changes are the raw update to linux-4.4.6-rt14. Kernel sources
[kvmfornfv.git] / kernel / drivers / staging / board / board.c
index d5a6abc..965afc7 100644 (file)
@@ -1,10 +1,30 @@
+/*
+ * Copyright (C) 2014 Magnus Damm
+ * Copyright (C) 2015 Glider bvba
+ *
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License.  See the file "COPYING" in the main directory of this archive
+ * for more details.
+ */
+
+#define pr_fmt(fmt)    "board_staging: "  fmt
+
+#include <linux/clkdev.h>
 #include <linux/init.h>
+#include <linux/irq.h>
 #include <linux/device.h>
 #include <linux/kernel.h>
 #include <linux/of.h>
 #include <linux/of_address.h>
+#include <linux/of_irq.h>
+#include <linux/platform_device.h>
+#include <linux/pm_domain.h>
+
 #include "board.h"
 
+static struct device_node *irqc_node __initdata;
+static unsigned int irqc_base __initdata;
+
 static bool find_by_address(u64 base_address)
 {
        struct device_node *dn = of_find_all_nodes(NULL);
@@ -38,3 +58,153 @@ bool __init board_staging_dt_node_available(const struct resource *resource,
 
        return false; /* Nothing found */
 }
+
+int __init board_staging_gic_setup_xlate(const char *gic_match,
+                                        unsigned int base)
+{
+       WARN_ON(irqc_node);
+
+       irqc_node = of_find_compatible_node(NULL, NULL, gic_match);
+
+       WARN_ON(!irqc_node);
+       if (!irqc_node)
+               return -ENOENT;
+
+       irqc_base = base;
+       return 0;
+}
+
+static void __init gic_fixup_resource(struct resource *res)
+{
+       struct of_phandle_args irq_data;
+       unsigned int hwirq = res->start;
+       unsigned int virq;
+
+       if (resource_type(res) != IORESOURCE_IRQ || !irqc_node)
+               return;
+
+       irq_data.np = irqc_node;
+       irq_data.args_count = 3;
+       irq_data.args[0] = 0;
+       irq_data.args[1] = hwirq - irqc_base;
+       switch (res->flags &
+               (IORESOURCE_IRQ_LOWEDGE | IORESOURCE_IRQ_HIGHEDGE |
+                IORESOURCE_IRQ_LOWLEVEL | IORESOURCE_IRQ_HIGHLEVEL)) {
+       case IORESOURCE_IRQ_LOWEDGE:
+               irq_data.args[2] = IRQ_TYPE_EDGE_FALLING;
+               break;
+       case IORESOURCE_IRQ_HIGHEDGE:
+               irq_data.args[2] = IRQ_TYPE_EDGE_RISING;
+               break;
+       case IORESOURCE_IRQ_LOWLEVEL:
+               irq_data.args[2] = IRQ_TYPE_LEVEL_LOW;
+               break;
+       case IORESOURCE_IRQ_HIGHLEVEL:
+       default:
+               irq_data.args[2] = IRQ_TYPE_LEVEL_HIGH;
+               break;
+       }
+
+       virq = irq_create_of_mapping(&irq_data);
+       if (WARN_ON(!virq))
+               return;
+
+       pr_debug("hwirq %u -> virq %u\n", hwirq, virq);
+       res->start = virq;
+}
+
+void __init board_staging_gic_fixup_resources(struct resource *res,
+                                             unsigned int nres)
+{
+       unsigned int i;
+
+       for (i = 0; i < nres; i++)
+               gic_fixup_resource(&res[i]);
+}
+
+int __init board_staging_register_clock(const struct board_staging_clk *bsc)
+{
+       int error;
+
+       pr_debug("Aliasing clock %s for con_id %s dev_id %s\n", bsc->clk,
+                bsc->con_id, bsc->dev_id);
+       error = clk_add_alias(bsc->con_id, bsc->dev_id, bsc->clk, NULL);
+       if (error)
+               pr_err("Failed to alias clock %s (%d)\n", bsc->clk, error);
+
+       return error;
+}
+
+#ifdef CONFIG_PM_GENERIC_DOMAINS_OF
+static int board_staging_add_dev_domain(struct platform_device *pdev,
+                                       const char *domain)
+{
+       struct of_phandle_args pd_args;
+       struct generic_pm_domain *pd;
+       struct device_node *np;
+
+       np = of_find_node_by_path(domain);
+       if (!np) {
+               pr_err("Cannot find domain node %s\n", domain);
+               return -ENOENT;
+       }
+
+       pd_args.np = np;
+       pd_args.args_count = 0;
+       pd = of_genpd_get_from_provider(&pd_args);
+       if (IS_ERR(pd)) {
+               pr_err("Cannot find genpd %s (%ld)\n", domain, PTR_ERR(pd));
+               return PTR_ERR(pd);
+
+       }
+       pr_debug("Found genpd %s for device %s\n", pd->name, pdev->name);
+
+       return pm_genpd_add_device(pd, &pdev->dev);
+}
+#else
+static inline int board_staging_add_dev_domain(struct platform_device *pdev,
+                                              const char *domain)
+{
+       return 0;
+}
+#endif
+
+int __init board_staging_register_device(const struct board_staging_dev *dev)
+{
+       struct platform_device *pdev = dev->pdev;
+       unsigned int i;
+       int error;
+
+       pr_debug("Trying to register device %s\n", pdev->name);
+       if (board_staging_dt_node_available(pdev->resource,
+                                           pdev->num_resources)) {
+               pr_warn("Skipping %s, already in DT\n", pdev->name);
+               return -EEXIST;
+       }
+
+       board_staging_gic_fixup_resources(pdev->resource, pdev->num_resources);
+
+       for (i = 0; i < dev->nclocks; i++)
+               board_staging_register_clock(&dev->clocks[i]);
+
+       if (dev->domain)
+               board_staging_add_dev_domain(pdev, dev->domain);
+
+       error = platform_device_register(pdev);
+       if (error) {
+               pr_err("Failed to register device %s (%d)\n", pdev->name,
+                      error);
+               return error;
+       }
+
+       return error;
+}
+
+void __init board_staging_register_devices(const struct board_staging_dev *devs,
+                                          unsigned int ndevs)
+{
+       unsigned int i;
+
+       for (i = 0; i < ndevs; i++)
+               board_staging_register_device(&devs[i]);
+}