summaryrefslogtreecommitdiff
path: root/drivers/soundwire/intel_init.c
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@linux-foundation.org>2021-07-05 23:42:16 +0300
committerLinus Torvalds <torvalds@linux-foundation.org>2021-07-05 23:42:16 +0300
commiteed0218e8cae9fcd186c30e9fcf5fe46a87e056e (patch)
tree799a1360b947a56d05a60433fdf60a96bf3b3348 /drivers/soundwire/intel_init.c
parent3f8b8e7dbd79086ad48fcff33de9399f3da66a69 (diff)
parent6f746d485fb9188dc67dce7de63d21f0c28a1f2e (diff)
downloadlinux-eed0218e8cae9fcd186c30e9fcf5fe46a87e056e.tar.xz
Merge tag 'char-misc-5.14-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 set of char / misc and other driver subsystem updates for 5.14-rc1. Included in here are: - habanalabs driver updates - fsl-mc driver updates - comedi driver updates - fpga driver updates - extcon driver updates - interconnect driver updates - mei driver updates - nvmem driver updates - phy driver updates - pnp driver updates - soundwire driver updates - lots of other tiny driver updates for char and misc drivers This is looking more and more like the "various driver subsystems mushed together" tree... All of these have been in linux-next for a while with no reported issues" * tag 'char-misc-5.14-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/char-misc: (292 commits) mcb: Use DEFINE_RES_MEM() helper macro and fix the end address PNP: moved EXPORT_SYMBOL so that it immediately followed its function/variable bus: mhi: pci-generic: Add missing 'pci_disable_pcie_error_reporting()' calls bus: mhi: Wait for M2 state during system resume bus: mhi: core: Fix power down latency intel_th: Wait until port is in reset before programming it intel_th: msu: Make contiguous buffers uncached intel_th: Remove an unused exit point from intel_th_remove() stm class: Spelling fix nitro_enclaves: Set Bus Master for the NE PCI device misc: ibmasm: Modify matricies to matrices misc: vmw_vmci: return the correct errno code siox: Simplify error handling via dev_err_probe() fpga: machxo2-spi: Address warning about unused variable lkdtm/heap: Add init_on_alloc tests selftests/lkdtm: Enable various testable CONFIGs lkdtm: Add CONFIG hints in errors where possible lkdtm: Enable DOUBLE_FAULT on all architectures lkdtm/heap: Add vmalloc linear overflow test lkdtm/bugs: XFAIL UNALIGNED_LOAD_STORE_WRITE ...
Diffstat (limited to 'drivers/soundwire/intel_init.c')
-rw-r--r--drivers/soundwire/intel_init.c232
1 files changed, 157 insertions, 75 deletions
diff --git a/drivers/soundwire/intel_init.c b/drivers/soundwire/intel_init.c
index 30ce95ec2d70..9e283bef53d2 100644
--- a/drivers/soundwire/intel_init.c
+++ b/drivers/soundwire/intel_init.c
@@ -12,7 +12,7 @@
#include <linux/interrupt.h>
#include <linux/io.h>
#include <linux/module.h>
-#include <linux/platform_device.h>
+#include <linux/auxiliary_bus.h>
#include <linux/pm_runtime.h>
#include <linux/soundwire/sdw_intel.h>
#include "cadence_master.h"
@@ -24,28 +24,108 @@
#define SDW_LINK_BASE 0x30000
#define SDW_LINK_SIZE 0x10000
+static void intel_link_dev_release(struct device *dev)
+{
+ struct auxiliary_device *auxdev = to_auxiliary_dev(dev);
+ struct sdw_intel_link_dev *ldev = auxiliary_dev_to_sdw_intel_link_dev(auxdev);
+
+ kfree(ldev);
+}
+
+/* alloc, init and add link devices */
+static struct sdw_intel_link_dev *intel_link_dev_register(struct sdw_intel_res *res,
+ struct sdw_intel_ctx *ctx,
+ struct fwnode_handle *fwnode,
+ const char *name,
+ int link_id)
+{
+ struct sdw_intel_link_dev *ldev;
+ struct sdw_intel_link_res *link;
+ struct auxiliary_device *auxdev;
+ int ret;
+
+ ldev = kzalloc(sizeof(*ldev), GFP_KERNEL);
+ if (!ldev)
+ return ERR_PTR(-ENOMEM);
+
+ auxdev = &ldev->auxdev;
+ auxdev->name = name;
+ auxdev->dev.parent = res->parent;
+ auxdev->dev.fwnode = fwnode;
+ auxdev->dev.release = intel_link_dev_release;
+
+ /* we don't use an IDA since we already have a link ID */
+ auxdev->id = link_id;
+
+ /*
+ * keep a handle on the allocated memory, to be used in all other functions.
+ * Since the same pattern is used to skip links that are not enabled, there is
+ * no need to check if ctx->ldev[i] is NULL later on.
+ */
+ ctx->ldev[link_id] = ldev;
+
+ /* Add link information used in the driver probe */
+ link = &ldev->link_res;
+ link->mmio_base = res->mmio_base;
+ link->registers = res->mmio_base + SDW_LINK_BASE
+ + (SDW_LINK_SIZE * link_id);
+ link->shim = res->mmio_base + SDW_SHIM_BASE;
+ link->alh = res->mmio_base + SDW_ALH_BASE;
+
+ link->ops = res->ops;
+ link->dev = res->dev;
+
+ link->clock_stop_quirks = res->clock_stop_quirks;
+ link->shim_lock = &ctx->shim_lock;
+ link->shim_mask = &ctx->shim_mask;
+ link->link_mask = ctx->link_mask;
+
+ /* now follow the two-step init/add sequence */
+ ret = auxiliary_device_init(auxdev);
+ if (ret < 0) {
+ dev_err(res->parent, "failed to initialize link dev %s link_id %d\n",
+ name, link_id);
+ kfree(ldev);
+ return ERR_PTR(ret);
+ }
+
+ ret = auxiliary_device_add(&ldev->auxdev);
+ if (ret < 0) {
+ dev_err(res->parent, "failed to add link dev %s link_id %d\n",
+ ldev->auxdev.name, link_id);
+ /* ldev will be freed with the put_device() and .release sequence */
+ auxiliary_device_uninit(&ldev->auxdev);
+ return ERR_PTR(ret);
+ }
+
+ return ldev;
+}
+
+static void intel_link_dev_unregister(struct sdw_intel_link_dev *ldev)
+{
+ auxiliary_device_delete(&ldev->auxdev);
+ auxiliary_device_uninit(&ldev->auxdev);
+}
+
static int sdw_intel_cleanup(struct sdw_intel_ctx *ctx)
{
- struct sdw_intel_link_res *link = ctx->links;
+ struct sdw_intel_link_dev *ldev;
u32 link_mask;
int i;
- if (!link)
- return 0;
-
link_mask = ctx->link_mask;
- for (i = 0; i < ctx->count; i++, link++) {
+ for (i = 0; i < ctx->count; i++) {
if (!(link_mask & BIT(i)))
continue;
- if (link->pdev) {
- pm_runtime_disable(&link->pdev->dev);
- platform_device_unregister(link->pdev);
- }
+ ldev = ctx->ldev[i];
- if (!link->clock_stop_quirks)
- pm_runtime_put_noidle(link->dev);
+ pm_runtime_disable(&ldev->auxdev.dev);
+ if (!ldev->link_res.clock_stop_quirks)
+ pm_runtime_put_noidle(ldev->link_res.dev);
+
+ intel_link_dev_unregister(ldev);
}
return 0;
@@ -91,9 +171,8 @@ EXPORT_SYMBOL_NS(sdw_intel_thread, SOUNDWIRE_INTEL_INIT);
static struct sdw_intel_ctx
*sdw_intel_probe_controller(struct sdw_intel_res *res)
{
- struct platform_device_info pdevinfo;
- struct platform_device *pdev;
struct sdw_intel_link_res *link;
+ struct sdw_intel_link_dev *ldev;
struct sdw_intel_ctx *ctx;
struct acpi_device *adev;
struct sdw_slave *slave;
@@ -116,67 +195,60 @@ static struct sdw_intel_ctx
count = res->count;
dev_dbg(&adev->dev, "Creating %d SDW Link devices\n", count);
- ctx = devm_kzalloc(&adev->dev, sizeof(*ctx), GFP_KERNEL);
+ /*
+ * we need to alloc/free memory manually and can't use devm:
+ * this routine may be called from a workqueue, and not from
+ * the parent .probe.
+ * If devm_ was used, the memory might never be freed on errors.
+ */
+ ctx = kzalloc(sizeof(*ctx), GFP_KERNEL);
if (!ctx)
return NULL;
ctx->count = count;
- ctx->links = devm_kcalloc(&adev->dev, ctx->count,
- sizeof(*ctx->links), GFP_KERNEL);
- if (!ctx->links)
+
+ /*
+ * allocate the array of pointers. The link-specific data is allocated
+ * as part of the first loop below and released with the auxiliary_device_uninit().
+ * If some links are disabled, the link pointer will remain NULL. Given that the
+ * number of links is small, this is simpler than using a list to keep track of links.
+ */
+ ctx->ldev = kcalloc(ctx->count, sizeof(*ctx->ldev), GFP_KERNEL);
+ if (!ctx->ldev) {
+ kfree(ctx);
return NULL;
+ }
- ctx->count = count;
ctx->mmio_base = res->mmio_base;
ctx->link_mask = res->link_mask;
ctx->handle = res->handle;
mutex_init(&ctx->shim_lock);
- link = ctx->links;
link_mask = ctx->link_mask;
INIT_LIST_HEAD(&ctx->link_list);
- /* Create SDW Master devices */
- for (i = 0; i < count; i++, link++) {
- if (!(link_mask & BIT(i))) {
- dev_dbg(&adev->dev,
- "Link %d masked, will not be enabled\n", i);
+ for (i = 0; i < count; i++) {
+ if (!(link_mask & BIT(i)))
continue;
- }
- link->mmio_base = res->mmio_base;
- link->registers = res->mmio_base + SDW_LINK_BASE
- + (SDW_LINK_SIZE * i);
- link->shim = res->mmio_base + SDW_SHIM_BASE;
- link->alh = res->mmio_base + SDW_ALH_BASE;
-
- link->ops = res->ops;
- link->dev = res->dev;
-
- link->clock_stop_quirks = res->clock_stop_quirks;
- link->shim_lock = &ctx->shim_lock;
- link->shim_mask = &ctx->shim_mask;
- link->link_mask = link_mask;
-
- memset(&pdevinfo, 0, sizeof(pdevinfo));
-
- pdevinfo.parent = res->parent;
- pdevinfo.name = "intel-sdw";
- pdevinfo.id = i;
- pdevinfo.fwnode = acpi_fwnode_handle(adev);
- pdevinfo.data = link;
- pdevinfo.size_data = sizeof(*link);
-
- pdev = platform_device_register_full(&pdevinfo);
- if (IS_ERR(pdev)) {
- dev_err(&adev->dev,
- "platform device creation failed: %ld\n",
- PTR_ERR(pdev));
+ /*
+ * init and add a device for each link
+ *
+ * The name of the device will be soundwire_intel.link.[i],
+ * with the "soundwire_intel" module prefix automatically added
+ * by the auxiliary bus core.
+ */
+ ldev = intel_link_dev_register(res,
+ ctx,
+ acpi_fwnode_handle(adev),
+ "link",
+ i);
+ if (IS_ERR(ldev))
goto err;
- }
- link->pdev = pdev;
- link->cdns = platform_get_drvdata(pdev);
+
+ link = &ldev->link_res;
+ link->cdns = dev_get_drvdata(&ldev->auxdev.dev);
if (!link->cdns) {
dev_err(&adev->dev, "failed to get link->cdns\n");
@@ -194,8 +266,7 @@ static struct sdw_intel_ctx
num_slaves++;
}
- ctx->ids = devm_kcalloc(&adev->dev, num_slaves,
- sizeof(*ctx->ids), GFP_KERNEL);
+ ctx->ids = kcalloc(num_slaves, sizeof(*ctx->ids), GFP_KERNEL);
if (!ctx->ids)
goto err;
@@ -213,8 +284,14 @@ static struct sdw_intel_ctx
return ctx;
err:
- ctx->count = i;
- sdw_intel_cleanup(ctx);
+ while (i--) {
+ if (!(link_mask & BIT(i)))
+ continue;
+ ldev = ctx->ldev[i];
+ intel_link_dev_unregister(ldev);
+ }
+ kfree(ctx->ldev);
+ kfree(ctx);
return NULL;
}
@@ -222,7 +299,7 @@ static int
sdw_intel_startup_controller(struct sdw_intel_ctx *ctx)
{
struct acpi_device *adev;
- struct sdw_intel_link_res *link;
+ struct sdw_intel_link_dev *ldev;
u32 caps;
u32 link_mask;
int i;
@@ -241,27 +318,28 @@ sdw_intel_startup_controller(struct sdw_intel_ctx *ctx)
return -EINVAL;
}
- if (!ctx->links)
+ if (!ctx->ldev)
return -EINVAL;
- link = ctx->links;
link_mask = ctx->link_mask;
/* Startup SDW Master devices */
- for (i = 0; i < ctx->count; i++, link++) {
+ for (i = 0; i < ctx->count; i++) {
if (!(link_mask & BIT(i)))
continue;
- intel_master_startup(link->pdev);
+ ldev = ctx->ldev[i];
+
+ intel_link_startup(&ldev->auxdev);
- if (!link->clock_stop_quirks) {
+ if (!ldev->link_res.clock_stop_quirks) {
/*
* we need to prevent the parent PCI device
* from entering pm_runtime suspend, so that
* power rails to the SoundWire IP are not
* turned off.
*/
- pm_runtime_get_noresume(link->dev);
+ pm_runtime_get_noresume(ldev->link_res.dev);
}
}
@@ -272,8 +350,8 @@ sdw_intel_startup_controller(struct sdw_intel_ctx *ctx)
* sdw_intel_probe() - SoundWire Intel probe routine
* @res: resource data
*
- * This registers a platform device for each Master handled by the controller,
- * and SoundWire Master and Slave devices will be created by the platform
+ * This registers an auxiliary device for each Master handled by the controller,
+ * and SoundWire Master and Slave devices will be created by the auxiliary
* device probe. All the information necessary is stored in the context, and
* the res argument pointer can be freed after this step.
* This function will be called after sdw_intel_acpi_scan() by SOF probe.
@@ -306,27 +384,31 @@ EXPORT_SYMBOL_NS(sdw_intel_startup, SOUNDWIRE_INTEL_INIT);
void sdw_intel_exit(struct sdw_intel_ctx *ctx)
{
sdw_intel_cleanup(ctx);
+ kfree(ctx->ids);
+ kfree(ctx->ldev);
+ kfree(ctx);
}
EXPORT_SYMBOL_NS(sdw_intel_exit, SOUNDWIRE_INTEL_INIT);
void sdw_intel_process_wakeen_event(struct sdw_intel_ctx *ctx)
{
- struct sdw_intel_link_res *link;
+ struct sdw_intel_link_dev *ldev;
u32 link_mask;
int i;
- if (!ctx->links)
+ if (!ctx->ldev)
return;
- link = ctx->links;
link_mask = ctx->link_mask;
/* Startup SDW Master devices */
- for (i = 0; i < ctx->count; i++, link++) {
+ for (i = 0; i < ctx->count; i++) {
if (!(link_mask & BIT(i)))
continue;
- intel_master_process_wakeen_event(link->pdev);
+ ldev = ctx->ldev[i];
+
+ intel_link_process_wakeen_event(&ldev->auxdev);
}
}
EXPORT_SYMBOL_NS(sdw_intel_process_wakeen_event, SOUNDWIRE_INTEL_INIT);