// 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 #include #include #include #include #include #include #include #include "cadence_master.h" #include "intel.h" 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 + res->shim_base; link->alh = res->mmio_base + res->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_dev *ldev; u32 link_mask; int i; link_mask = ctx->link_mask; for (i = 0; i < ctx->count; i++) { if (!(link_mask & BIT(i))) continue; ldev = ctx->ldev[i]; 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; } #define HDA_DSP_REG_ADSPIC2 (0x10) #define HDA_DSP_REG_ADSPIS2 (0x14) #define HDA_DSP_REG_ADSPIC2_SNDW BIT(5) /** * sdw_intel_enable_irq() - enable/disable Intel SoundWire IRQ * @mmio_base: The mmio base of the control register * @enable: true if enable */ void sdw_intel_enable_irq(void __iomem *mmio_base, bool enable) { u32 val; val = readl(mmio_base + HDA_DSP_REG_ADSPIC2); if (enable) val |= HDA_DSP_REG_ADSPIC2_SNDW; else val &= ~HDA_DSP_REG_ADSPIC2_SNDW; writel(val, mmio_base + HDA_DSP_REG_ADSPIC2); } EXPORT_SYMBOL_NS(sdw_intel_enable_irq, SOUNDWIRE_INTEL_INIT); irqreturn_t sdw_intel_thread(int irq, void *dev_id) { struct sdw_intel_ctx *ctx = dev_id; struct sdw_intel_link_res *link; list_for_each_entry(link, &ctx->link_list, list) sdw_cdns_irq(irq, link->cdns); sdw_intel_enable_irq(ctx->mmio_base, true); return IRQ_HANDLED; } EXPORT_SYMBOL_NS(sdw_intel_thread, SOUNDWIRE_INTEL_INIT); static struct sdw_intel_ctx *sdw_intel_probe_controller(struct sdw_intel_res *res) { 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; struct list_head *node; struct sdw_bus *bus; u32 link_mask; int num_slaves = 0; int count; int i; if (!res) return NULL; adev = acpi_fetch_acpi_dev(res->handle); if (!adev) return NULL; if (!res->count) return NULL; count = res->count; dev_dbg(&adev->dev, "Creating %d SDW Link devices\n", count); /* * 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; /* * 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->mmio_base = res->mmio_base; ctx->shim_base = res->shim_base; ctx->alh_base = res->alh_base; ctx->link_mask = res->link_mask; ctx->handle = res->handle; mutex_init(&ctx->shim_lock); link_mask = ctx->link_mask; INIT_LIST_HEAD(&ctx->link_list); for (i = 0; i < count; i++) { if (!(link_mask & BIT(i))) continue; /* * 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 = &ldev->link_res; link->cdns = auxiliary_get_drvdata(&ldev->auxdev); if (!link->cdns) { dev_err(&adev->dev, "failed to get link->cdns\n"); /* * 1 will be subtracted from i in the err label, but we need to call * intel_link_dev_unregister for this ldev, so plus 1 now */ i++; goto err; } list_add_tail(&link->list, &ctx->link_list); bus = &link->cdns->bus; /* Calculate number of slaves */ list_for_each(node, &bus->slaves) num_slaves++; } ctx->ids = kcalloc(num_slaves, sizeof(*ctx->ids), GFP_KERNEL); if (!ctx->ids) goto err; ctx->num_slaves = num_slaves; i = 0; list_for_each_entry(link, &ctx->link_list, list) { bus = &link->cdns->bus; list_for_each_entry(slave, &bus->slaves, node) { ctx->ids[i].id = slave->id; ctx->ids[i].link_id = bus->link_id; i++; } } return ctx; err: while (i--) { if (!(link_mask & BIT(i))) continue; ldev = ctx->ldev[i]; intel_link_dev_unregister(ldev); } kfree(ctx->ldev); kfree(ctx); return NULL; } static int sdw_intel_startup_controller(struct sdw_intel_ctx *ctx) { struct acpi_device *adev = acpi_fetch_acpi_dev(ctx->handle); struct sdw_intel_link_dev *ldev; u32 caps; u32 link_mask; int i; if (!adev) return -EINVAL; /* Check SNDWLCAP.LCOUNT */ caps = ioread32(ctx->mmio_base + ctx->shim_base + SDW_SHIM_LCAP); caps &= SDW_SHIM_LCAP_LCOUNT_MASK; /* Check HW supported vs property value */ if (caps < ctx->count) { dev_err(&adev->dev, "BIOS master count is larger than hardware capabilities\n"); return -EINVAL; } if (!ctx->ldev) return -EINVAL; link_mask = ctx->link_mask; /* Startup SDW Master devices */ for (i = 0; i < ctx->count; i++) { if (!(link_mask & BIT(i))) continue; ldev = ctx->ldev[i]; intel_link_startup(&ldev->auxdev); 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(ldev->link_res.dev); } } return 0; } /** * sdw_intel_probe() - SoundWire Intel probe routine * @res: resource data * * 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. */ struct sdw_intel_ctx *sdw_intel_probe(struct sdw_intel_res *res) { return sdw_intel_probe_controller(res); } EXPORT_SYMBOL_NS(sdw_intel_probe, SOUNDWIRE_INTEL_INIT); /** * sdw_intel_startup() - SoundWire Intel startup * @ctx: SoundWire context allocated in the probe * * Startup Intel SoundWire controller. This function will be called after * Intel Audio DSP is powered up. */ int sdw_intel_startup(struct sdw_intel_ctx *ctx) { return sdw_intel_startup_controller(ctx); } EXPORT_SYMBOL_NS(sdw_intel_startup, SOUNDWIRE_INTEL_INIT); /** * sdw_intel_exit() - SoundWire Intel exit * @ctx: SoundWire context allocated in the probe * * Delete the controller instances created and cleanup */ 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_dev *ldev; u32 link_mask; int i; if (!ctx->ldev) return; link_mask = ctx->link_mask; /* Startup SDW Master devices */ for (i = 0; i < ctx->count; i++) { if (!(link_mask & BIT(i))) continue; ldev = ctx->ldev[i]; intel_link_process_wakeen_event(&ldev->auxdev); } } EXPORT_SYMBOL_NS(sdw_intel_process_wakeen_event, SOUNDWIRE_INTEL_INIT); MODULE_LICENSE("Dual BSD/GPL"); MODULE_DESCRIPTION("Intel Soundwire Init Library");