aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/soundwire/intel_init.c
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@linux-foundation.org>2018-02-01 10:31:17 -0800
committerLinus Torvalds <torvalds@linux-foundation.org>2018-02-01 10:31:17 -0800
commitf6cff79f1d122f78a4b35bf4b2f0112afcd89ea4 (patch)
treecf3a38576f9adbb3860982c25f72aebed2bb541a /drivers/soundwire/intel_init.c
parent47fcc0360cfb3fe82e4daddacad3c1cd80b0b75d (diff)
parent9ff6576e124b1227c27c1da43fe5f8ee908263e0 (diff)
downloadlinux-f6cff79f1d122f78a4b35bf4b2f0112afcd89ea4.tar.gz
Merge tag 'char-misc-4.16-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/char-misc
Pull char/misc driver updates from Greg KH: "Here is the big pull request for char/misc drivers for 4.16-rc1. There's a lot of stuff in here. Three new driver subsystems were added for various types of hardware busses: - siox - slimbus - soundwire as well as a new vboxguest subsystem for the VirtualBox hypervisor drivers. There's also big updates from the FPGA subsystem, lots of Android binder fixes, the usual handful of hyper-v updates, and lots of other smaller driver updates. All of these have been in linux-next for a long time, with no reported issues" * tag 'char-misc-4.16-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/char-misc: (155 commits) char: lp: use true or false for boolean values android: binder: use VM_ALLOC to get vm area android: binder: Use true and false for boolean values lkdtm: fix handle_irq_event symbol for INT_HW_IRQ_EN EISA: Delete error message for a failed memory allocation in eisa_probe() EISA: Whitespace cleanup misc: remove AVR32 dependencies virt: vbox: Add error mapping for VERR_INVALID_NAME and VERR_NO_MORE_FILES soundwire: Fix a signedness bug uio_hv_generic: fix new type mismatch warnings uio_hv_generic: fix type mismatch warnings auxdisplay: img-ascii-lcd: add missing MODULE_DESCRIPTION/AUTHOR/LICENSE uio_hv_generic: add rescind support uio_hv_generic: check that host supports monitor page uio_hv_generic: create send and receive buffers uio: document uio_hv_generic regions doc: fix documentation about uio_hv_generic vmbus: add monitor_id and subchannel_id to sysfs per channel vmbus: fix ABI documentation uio_hv_generic: use ISR callback method ...
Diffstat (limited to 'drivers/soundwire/intel_init.c')
-rw-r--r--drivers/soundwire/intel_init.c198
1 files changed, 198 insertions, 0 deletions
diff --git a/drivers/soundwire/intel_init.c b/drivers/soundwire/intel_init.c
new file mode 100644
index 000000000000..6f2bb99526f2
--- /dev/null
+++ b/drivers/soundwire/intel_init.c
@@ -0,0 +1,198 @@
+// SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause)
+// Copyright(c) 2015-17 Intel Corporation.
+
+/*
+ * SDW Intel Init Routines
+ *
+ * Initializes and creates SDW devices based on ACPI and Hardware values
+ */
+
+#include <linux/acpi.h>
+#include <linux/platform_device.h>
+#include <linux/soundwire/sdw_intel.h>
+#include "intel.h"
+
+#define SDW_MAX_LINKS 4
+#define SDW_SHIM_LCAP 0x0
+#define SDW_SHIM_BASE 0x2C000
+#define SDW_ALH_BASE 0x2C800
+#define SDW_LINK_BASE 0x30000
+#define SDW_LINK_SIZE 0x10000
+
+struct sdw_link_data {
+ struct sdw_intel_link_res res;
+ struct platform_device *pdev;
+};
+
+struct sdw_intel_ctx {
+ int count;
+ struct sdw_link_data *links;
+};
+
+static int sdw_intel_cleanup_pdev(struct sdw_intel_ctx *ctx)
+{
+ struct sdw_link_data *link = ctx->links;
+ int i;
+
+ if (!link)
+ return 0;
+
+ for (i = 0; i < ctx->count; i++) {
+ if (link->pdev)
+ platform_device_unregister(link->pdev);
+ link++;
+ }
+
+ kfree(ctx->links);
+ ctx->links = NULL;
+
+ return 0;
+}
+
+static struct sdw_intel_ctx
+*sdw_intel_add_controller(struct sdw_intel_res *res)
+{
+ struct platform_device_info pdevinfo;
+ struct platform_device *pdev;
+ struct sdw_link_data *link;
+ struct sdw_intel_ctx *ctx;
+ struct acpi_device *adev;
+ int ret, i;
+ u8 count;
+ u32 caps;
+
+ if (acpi_bus_get_device(res->handle, &adev))
+ return NULL;
+
+ /* Found controller, find links supported */
+ count = 0;
+ ret = fwnode_property_read_u8_array(acpi_fwnode_handle(adev),
+ "mipi-sdw-master-count", &count, 1);
+
+ /* Don't fail on error, continue and use hw value */
+ if (ret) {
+ dev_err(&adev->dev,
+ "Failed to read mipi-sdw-master-count: %d\n", ret);
+ count = SDW_MAX_LINKS;
+ }
+
+ /* Check SNDWLCAP.LCOUNT */
+ caps = ioread32(res->mmio_base + SDW_SHIM_BASE + SDW_SHIM_LCAP);
+
+ /* Check HW supported vs property value and use min of two */
+ count = min_t(u8, caps, count);
+
+ /* Check count is within bounds */
+ if (count > SDW_MAX_LINKS) {
+ dev_err(&adev->dev, "Link count %d exceeds max %d\n",
+ count, SDW_MAX_LINKS);
+ return NULL;
+ }
+
+ dev_dbg(&adev->dev, "Creating %d SDW Link devices\n", count);
+
+ ctx = kzalloc(sizeof(*ctx), GFP_KERNEL);
+ if (!ctx)
+ return NULL;
+
+ ctx->count = count;
+ ctx->links = kcalloc(ctx->count, sizeof(*ctx->links), GFP_KERNEL);
+ if (!ctx->links)
+ goto link_err;
+
+ link = ctx->links;
+
+ /* Create SDW Master devices */
+ for (i = 0; i < count; i++) {
+
+ link->res.irq = res->irq;
+ link->res.registers = res->mmio_base + SDW_LINK_BASE
+ + (SDW_LINK_SIZE * i);
+ link->res.shim = res->mmio_base + SDW_SHIM_BASE;
+ link->res.alh = res->mmio_base + SDW_ALH_BASE;
+
+ memset(&pdevinfo, 0, sizeof(pdevinfo));
+
+ pdevinfo.parent = res->parent;
+ pdevinfo.name = "int-sdw";
+ pdevinfo.id = i;
+ pdevinfo.fwnode = acpi_fwnode_handle(adev);
+ pdevinfo.data = &link->res;
+ pdevinfo.size_data = sizeof(link->res);
+
+ pdev = platform_device_register_full(&pdevinfo);
+ if (IS_ERR(pdev)) {
+ dev_err(&adev->dev,
+ "platform device creation failed: %ld\n",
+ PTR_ERR(pdev));
+ goto pdev_err;
+ }
+
+ link->pdev = pdev;
+ link++;
+ }
+
+ return ctx;
+
+pdev_err:
+ sdw_intel_cleanup_pdev(ctx);
+link_err:
+ kfree(ctx);
+ return NULL;
+}
+
+static acpi_status sdw_intel_acpi_cb(acpi_handle handle, u32 level,
+ void *cdata, void **return_value)
+{
+ struct sdw_intel_res *res = cdata;
+ struct acpi_device *adev;
+
+ if (acpi_bus_get_device(handle, &adev)) {
+ dev_err(&adev->dev, "Couldn't find ACPI handle\n");
+ return AE_NOT_FOUND;
+ }
+
+ res->handle = handle;
+ return AE_OK;
+}
+
+/**
+ * sdw_intel_init() - SoundWire Intel init routine
+ * @parent_handle: ACPI parent handle
+ * @res: resource data
+ *
+ * This scans the namespace and creates SoundWire link controller devices
+ * based on the info queried.
+ */
+void *sdw_intel_init(acpi_handle *parent_handle, struct sdw_intel_res *res)
+{
+ acpi_status status;
+
+ status = acpi_walk_namespace(ACPI_TYPE_DEVICE,
+ parent_handle, 1,
+ sdw_intel_acpi_cb,
+ NULL, res, NULL);
+ if (ACPI_FAILURE(status))
+ return NULL;
+
+ return sdw_intel_add_controller(res);
+}
+EXPORT_SYMBOL(sdw_intel_init);
+
+/**
+ * sdw_intel_exit() - SoundWire Intel exit
+ * @arg: callback context
+ *
+ * Delete the controller instances created and cleanup
+ */
+void sdw_intel_exit(void *arg)
+{
+ struct sdw_intel_ctx *ctx = arg;
+
+ sdw_intel_cleanup_pdev(ctx);
+ kfree(ctx);
+}
+EXPORT_SYMBOL(sdw_intel_exit);
+
+MODULE_LICENSE("Dual BSD/GPL");
+MODULE_DESCRIPTION("Intel Soundwire Init Library");