+
+ kfree(output.pointer);
+
+ if (entry->key == NULL)
+ return -ENOENT;
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(acpi_amba_dsm_lookup);
+
+static int acpi_amba_add_resource(struct acpi_resource *ares, void
+*data) {
+ struct amba_device *dev = data;
+ struct resource r;
+ int irq_idx;
+
+ switch (ares->type) {
+ case ACPI_RESOURCE_TYPE_FIXED_MEMORY32:
+ if (!acpi_dev_resource_memory(ares, &dev->res))
+ pr_err("failed to map memory resource\n");
+ break;
+ case ACPI_RESOURCE_TYPE_EXTENDED_IRQ:
+ for (irq_idx = 0; irq_idx < AMBA_NR_IRQS; irq_idx++) {
+ if (acpi_dev_resource_interrupt(ares, irq_idx, &r))
+ dev->irq[irq_idx] = r.start;
+ else
+ break;
+ }
+
+ break;
+ case ACPI_RESOURCE_TYPE_END_TAG:
+ /* ignore the end tag */
+ break;
+ default:
+ /* log an error, but proceed with driver probe */
+ pr_err("unhandled acpi resource type= %d\n",
+ ares->type);
+ break;
+ }
+
+ return 1; /* Tell ACPI core that this resource has been handled */ }
+
+static struct clk *acpi_amba_get_clk(acpi_handle handle, int index,
+ char **clk_name)
+{
+ struct acpi_amba_dsm_entry entry;
+ acpi_handle clk_handle;
+ struct acpi_device *adev, *clk_adev;
+ char *clk_path;
+ struct clk *clk = NULL;
+ int len;
+
+ if (acpi_bus_get_device(handle, &adev)) {
+ pr_err("cannot get device from handle\n");
+ return NULL;
+ }
+
+ /* key=value format for clocks is:
+ * "clock-name"="apb_pclk \\_SB.CLK0"
+ */
+ if (acpi_amba_dsm_lookup(handle, "clock-name", index, &entry) != 0)
+ return NULL;
+
+ /* look under the clock device for the clock that matches the entry */
+ *clk_name = NULL;
+ len = strcspn(entry.value, " ");
+ if (len > 0 && (len + 1) < strlen(entry.value)) {
+ clk_path = entry.value + len + 1;
+ *clk_name = kmalloc(len + 1, GFP_KERNEL);
+ strncpy(*clk_name, entry.value, len);
+ (*clk_name)[len] = '\0';
+ if (ACPI_FAILURE(
+ acpi_get_handle(NULL, clk_path, &clk_handle)) == 0 &&
+ acpi_bus_get_device(clk_handle, &clk_adev) == 0)
+ clk = clk_get_sys(dev_name(&clk_adev->dev), *clk_name);
+ } else
+ pr_err("Invalid clock-name value format '%s' for %s\n",
+ entry.value, dev_name(&adev->dev));
+
+ kfree(entry.key);
+ kfree(entry.value);
+
+ return clk;
+}
+
+static void acpi_amba_register_clocks(struct acpi_device *adev,
+ struct clk *default_clk, const char *default_clk_name) {
+ struct clk *clk;
+ int i;
+ char *clk_name;
+
+ if (default_clk) {
+ /* for amba_get_enable_pclk() ... */
+ clk_register_clkdev(default_clk, default_clk_name,
+ dev_name(&adev->dev));
+ /* for devm_clk_get() ... */
+ clk_register_clkdev(default_clk, NULL, dev_name(&adev->dev));
+ }
+
+ for (i = 0;; i++) {
+ clk_name = NULL;
+ clk = acpi_amba_get_clk(ACPI_HANDLE(&adev->dev), i, &clk_name);
+ if (!clk)
+ break;
+
+ clk_register_clkdev(clk, clk_name, dev_name(&adev->dev));
+
+ kfree(clk_name);
+ }
+}
+
+/* acpi_amba_add_device()
+ *
+ * ACPI equivalent to of_amba_device_create() */ static acpi_status
+acpi_amba_add_device(acpi_handle handle,
+ u32 lvl_not_used, void *data, void **not_used) {
+ struct list_head resource_list;
+ struct acpi_device *adev;
+ struct amba_device *dev;
+ int ret;
+ struct acpi_amba_bus_info *bus_info = data;
+ struct platform_device *bus_pdev = bus_info->pdev;
+
+ if (acpi_bus_get_device(handle, &adev)) {
+ pr_err("%s: acpi_bus_get_device failed\n", __func__);
+ return AE_OK;
+ }
+
+ pr_debug("Creating amba device %s\n", dev_name(&adev->dev));
+
+ dev = amba_device_alloc(NULL, 0, 0);
+ if (!dev) {
+ pr_err("%s(): amba_device_alloc() failed for %s\n",
+ __func__, dev_name(&adev->dev));
+ return AE_CTRL_TERMINATE;
+ }
+
+ /* setup generic device info */
+ dev->dev.coherent_dma_mask = ~0;
+ dev->dev.parent = &bus_pdev->dev;
+ dev_set_name(&dev->dev, "%s", dev_name(&adev->dev));
+
+ /* setup amba-specific device info */
+ dev->dma_mask = ~0;
+
+ ACPI_HANDLE_SET(&dev->dev, handle);
+ ACPI_HANDLE_SET(&adev->dev, handle);
+
+ INIT_LIST_HEAD(&resource_list);
+ acpi_dev_get_resources(adev, &resource_list,
+ acpi_amba_add_resource, dev);
+ acpi_dev_free_resource_list(&resource_list);
+
+ /* Add clocks */
+ acpi_amba_register_clocks(adev, bus_info->clk, bus_info->clk_name);
+
+ /* Read AMBA hardware ID and add device to system. If a driver matching
+ * hardware ID has already been registered, bind this device to it.
+ * Otherwise, the platform subsystem will match up the hardware ID
+ * when the matching driver is registered.
+ */
+ ret = amba_device_add(dev, &iomem_resource);
+ if (ret) {
+ pr_err("%s(): amba_device_add() failed (%d) for %s\n",
+ __func__, ret, dev_name(&adev->dev));
+ goto err_free;
+ }
+
+ return AE_OK;
+
+err_free:
+ amba_device_put(dev);
+
+ return AE_OK; /* don't prevent other devices from being probed */ }
+
+static int acpi_amba_bus_probe(struct platform_device *pdev) {
+ struct acpi_amba_bus_info bus_info;
+ bus_info.clk_name = NULL;
+
+ /* see if there's a top-level clock to use as default for sub-devices */
+ bus_info.clk = acpi_amba_get_clk(ACPI_HANDLE(&pdev->dev), 0,
+ &bus_info.clk_name);
+
+ /* probe each device */
+ bus_info.pdev = pdev;
+ acpi_walk_namespace(ACPI_TYPE_DEVICE, ACPI_HANDLE(&pdev->dev), 1,
+ acpi_amba_add_device, NULL, &bus_info, NULL);
+
+ kfree(bus_info.clk_name);
+
+ return 0;
+}
+
+static const struct acpi_device_id amba_bus_acpi_match[] = {
+ { "AMBA0000", 0 },
+ { },
+};
+
+static struct platform_driver amba_bus_acpi_driver = {
+ .driver = {
+ .name = "amba-acpi",
+ .owner = THIS_MODULE,
+ .acpi_match_table = ACPI_PTR(amba_bus_acpi_match),
+ },
+ .probe = acpi_amba_bus_probe,
+};
+
+static int __init acpi_amba_bus_init(void) {
+ return platform_driver_register(&amba_bus_acpi_driver);
+}
+
+postcore_initcall(acpi_amba_bus_init);
+
+MODULE_AUTHOR("Brandon Anderson <brandon.anderson@xxxxxxx>");
+MODULE_DESCRIPTION("ACPI Connector Resource for AMBA bus");
+MODULE_LICENSE("GPLv2"); MODULE_ALIAS("platform:amba-acpi");
+
+#endif /* CONFIG_ACPI */
diff --git a/include/linux/amba/acpi.h b/include/linux/amba/acpi.h new
file mode 100644 index 0000000..40a72b2
--- /dev/null
+++ b/include/linux/amba/acpi.h
@@ -0,0 +1,29 @@
+/*
+ * AMBA bus ACPI helpers
+ *
+ * Copyright (C) 2013 Advanced Micro Devices, Inc.
+ *
+ * Author: Brandon Anderson <brandon.anderson@xxxxxxx>
+ *
+ * This program is free software; you can redistribute it and/or
+modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+#ifndef __ARM_AMBA_ACPI_H__
+#define __ARM_AMBA_ACPI_H__
+
+#ifdef CONFIG_ACPI
+#include <linux/acpi.h>
+
+struct acpi_amba_dsm_entry {
+ char *key;
+ char *value;
+};
+
+int acpi_amba_dsm_lookup(acpi_handle handle,
+ const char *tag, int index,
+ struct acpi_amba_dsm_entry *entry);
+
+#endif
+
+#endif