1d62a7d41SVinod Koul // SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause) 2d62a7d41SVinod Koul // Copyright(c) 2015-17 Intel Corporation. 3d62a7d41SVinod Koul 4d62a7d41SVinod Koul /* 5d62a7d41SVinod Koul * SDW Intel Init Routines 6d62a7d41SVinod Koul * 7d62a7d41SVinod Koul * Initializes and creates SDW devices based on ACPI and Hardware values 8d62a7d41SVinod Koul */ 9d62a7d41SVinod Koul 10d62a7d41SVinod Koul #include <linux/acpi.h> 114abbd783SPaul Gortmaker #include <linux/export.h> 123fc40449SVinod Koul #include <linux/io.h> 134abbd783SPaul Gortmaker #include <linux/module.h> 14d62a7d41SVinod Koul #include <linux/platform_device.h> 15d62a7d41SVinod Koul #include <linux/soundwire/sdw_intel.h> 16d62a7d41SVinod Koul #include "intel.h" 17d62a7d41SVinod Koul 186f11586fSPierre-Louis Bossart #define SDW_LINK_TYPE 4 /* from Intel ACPI documentation */ 19d62a7d41SVinod Koul #define SDW_MAX_LINKS 4 20d62a7d41SVinod Koul #define SDW_SHIM_LCAP 0x0 21d62a7d41SVinod Koul #define SDW_SHIM_BASE 0x2C000 22d62a7d41SVinod Koul #define SDW_ALH_BASE 0x2C800 23d62a7d41SVinod Koul #define SDW_LINK_BASE 0x30000 24d62a7d41SVinod Koul #define SDW_LINK_SIZE 0x10000 25d62a7d41SVinod Koul 2650302fc7SPierre-Louis Bossart static int link_mask; 2750302fc7SPierre-Louis Bossart module_param_named(sdw_link_mask, link_mask, int, 0444); 2850302fc7SPierre-Louis Bossart MODULE_PARM_DESC(sdw_link_mask, "Intel link mask (one bit per link)"); 2950302fc7SPierre-Louis Bossart 30d62a7d41SVinod Koul static int sdw_intel_cleanup_pdev(struct sdw_intel_ctx *ctx) 31d62a7d41SVinod Koul { 32f98f690fSPierre-Louis Bossart struct sdw_intel_link_res *link = ctx->links; 33d62a7d41SVinod Koul int i; 34d62a7d41SVinod Koul 35d62a7d41SVinod Koul if (!link) 36d62a7d41SVinod Koul return 0; 37d62a7d41SVinod Koul 38d62a7d41SVinod Koul for (i = 0; i < ctx->count; i++) { 39d62a7d41SVinod Koul if (link->pdev) 40d62a7d41SVinod Koul platform_device_unregister(link->pdev); 41d62a7d41SVinod Koul link++; 42d62a7d41SVinod Koul } 43d62a7d41SVinod Koul 44d62a7d41SVinod Koul kfree(ctx->links); 45d62a7d41SVinod Koul ctx->links = NULL; 46d62a7d41SVinod Koul 47d62a7d41SVinod Koul return 0; 48d62a7d41SVinod Koul } 49d62a7d41SVinod Koul 50d62a7d41SVinod Koul static struct sdw_intel_ctx 51d62a7d41SVinod Koul *sdw_intel_add_controller(struct sdw_intel_res *res) 52d62a7d41SVinod Koul { 53d62a7d41SVinod Koul struct platform_device_info pdevinfo; 54d62a7d41SVinod Koul struct platform_device *pdev; 55f98f690fSPierre-Louis Bossart struct sdw_intel_link_res *link; 56d62a7d41SVinod Koul struct sdw_intel_ctx *ctx; 57d62a7d41SVinod Koul struct acpi_device *adev; 58d62a7d41SVinod Koul int ret, i; 59d62a7d41SVinod Koul u8 count; 60d62a7d41SVinod Koul u32 caps; 61d62a7d41SVinod Koul 62d62a7d41SVinod Koul if (acpi_bus_get_device(res->handle, &adev)) 63d62a7d41SVinod Koul return NULL; 64d62a7d41SVinod Koul 65d62a7d41SVinod Koul /* Found controller, find links supported */ 66d62a7d41SVinod Koul count = 0; 67d62a7d41SVinod Koul ret = fwnode_property_read_u8_array(acpi_fwnode_handle(adev), 68d62a7d41SVinod Koul "mipi-sdw-master-count", &count, 1); 69d62a7d41SVinod Koul 70d62a7d41SVinod Koul /* Don't fail on error, continue and use hw value */ 71d62a7d41SVinod Koul if (ret) { 72d62a7d41SVinod Koul dev_err(&adev->dev, 73d62a7d41SVinod Koul "Failed to read mipi-sdw-master-count: %d\n", ret); 74d62a7d41SVinod Koul count = SDW_MAX_LINKS; 75d62a7d41SVinod Koul } 76d62a7d41SVinod Koul 77d62a7d41SVinod Koul /* Check SNDWLCAP.LCOUNT */ 78d62a7d41SVinod Koul caps = ioread32(res->mmio_base + SDW_SHIM_BASE + SDW_SHIM_LCAP); 79432732b8SPierre-Louis Bossart caps &= GENMASK(2, 0); 80d62a7d41SVinod Koul 81d62a7d41SVinod Koul /* Check HW supported vs property value and use min of two */ 82d62a7d41SVinod Koul count = min_t(u8, caps, count); 83d62a7d41SVinod Koul 84d62a7d41SVinod Koul /* Check count is within bounds */ 85d62a7d41SVinod Koul if (count > SDW_MAX_LINKS) { 86d62a7d41SVinod Koul dev_err(&adev->dev, "Link count %d exceeds max %d\n", 87d62a7d41SVinod Koul count, SDW_MAX_LINKS); 88d62a7d41SVinod Koul return NULL; 896f7219feSGuennadi Liakhovetski } 906f7219feSGuennadi Liakhovetski 916f7219feSGuennadi Liakhovetski if (!count) { 92432732b8SPierre-Louis Bossart dev_warn(&adev->dev, "No SoundWire links detected\n"); 93432732b8SPierre-Louis Bossart return NULL; 94d62a7d41SVinod Koul } 95d62a7d41SVinod Koul 96d62a7d41SVinod Koul dev_dbg(&adev->dev, "Creating %d SDW Link devices\n", count); 97d62a7d41SVinod Koul 98d62a7d41SVinod Koul ctx = kzalloc(sizeof(*ctx), GFP_KERNEL); 99d62a7d41SVinod Koul if (!ctx) 100d62a7d41SVinod Koul return NULL; 101d62a7d41SVinod Koul 102d62a7d41SVinod Koul ctx->count = count; 103d62a7d41SVinod Koul ctx->links = kcalloc(ctx->count, sizeof(*ctx->links), GFP_KERNEL); 104d62a7d41SVinod Koul if (!ctx->links) 105d62a7d41SVinod Koul goto link_err; 106d62a7d41SVinod Koul 107d62a7d41SVinod Koul link = ctx->links; 108d62a7d41SVinod Koul 109d62a7d41SVinod Koul /* Create SDW Master devices */ 110d62a7d41SVinod Koul for (i = 0; i < count; i++) { 11150302fc7SPierre-Louis Bossart if (link_mask && !(link_mask & BIT(i))) { 11250302fc7SPierre-Louis Bossart dev_dbg(&adev->dev, 11350302fc7SPierre-Louis Bossart "Link %d masked, will not be enabled\n", i); 11450302fc7SPierre-Louis Bossart link++; 11550302fc7SPierre-Louis Bossart continue; 11650302fc7SPierre-Louis Bossart } 11750302fc7SPierre-Louis Bossart 118f98f690fSPierre-Louis Bossart link->registers = res->mmio_base + SDW_LINK_BASE 119d62a7d41SVinod Koul + (SDW_LINK_SIZE * i); 120f98f690fSPierre-Louis Bossart link->shim = res->mmio_base + SDW_SHIM_BASE; 121f98f690fSPierre-Louis Bossart link->alh = res->mmio_base + SDW_ALH_BASE; 122d62a7d41SVinod Koul 123f98f690fSPierre-Louis Bossart link->ops = res->ops; 1244b206d34SRander Wang link->dev = res->dev; 125c46302ecSVinod Koul 126d62a7d41SVinod Koul memset(&pdevinfo, 0, sizeof(pdevinfo)); 127d62a7d41SVinod Koul 128d62a7d41SVinod Koul pdevinfo.parent = res->parent; 129d62a7d41SVinod Koul pdevinfo.name = "int-sdw"; 130d62a7d41SVinod Koul pdevinfo.id = i; 131d62a7d41SVinod Koul pdevinfo.fwnode = acpi_fwnode_handle(adev); 132d62a7d41SVinod Koul 133d62a7d41SVinod Koul pdev = platform_device_register_full(&pdevinfo); 134d62a7d41SVinod Koul if (IS_ERR(pdev)) { 135d62a7d41SVinod Koul dev_err(&adev->dev, 136d62a7d41SVinod Koul "platform device creation failed: %ld\n", 137d62a7d41SVinod Koul PTR_ERR(pdev)); 138d62a7d41SVinod Koul goto pdev_err; 139d62a7d41SVinod Koul } 140d62a7d41SVinod Koul 141d62a7d41SVinod Koul link->pdev = pdev; 142d62a7d41SVinod Koul link++; 143d62a7d41SVinod Koul } 144d62a7d41SVinod Koul 145d62a7d41SVinod Koul return ctx; 146d62a7d41SVinod Koul 147d62a7d41SVinod Koul pdev_err: 148d62a7d41SVinod Koul sdw_intel_cleanup_pdev(ctx); 149d62a7d41SVinod Koul link_err: 150d62a7d41SVinod Koul kfree(ctx); 151d62a7d41SVinod Koul return NULL; 152d62a7d41SVinod Koul } 153d62a7d41SVinod Koul 154d62a7d41SVinod Koul static acpi_status sdw_intel_acpi_cb(acpi_handle handle, u32 level, 155d62a7d41SVinod Koul void *cdata, void **return_value) 156d62a7d41SVinod Koul { 157d62a7d41SVinod Koul struct sdw_intel_res *res = cdata; 158d62a7d41SVinod Koul struct acpi_device *adev; 1596f11586fSPierre-Louis Bossart acpi_status status; 1606f11586fSPierre-Louis Bossart u64 adr; 1616f11586fSPierre-Louis Bossart 1626f11586fSPierre-Louis Bossart status = acpi_evaluate_integer(handle, METHOD_NAME__ADR, NULL, &adr); 1636f11586fSPierre-Louis Bossart if (ACPI_FAILURE(status)) 1646f11586fSPierre-Louis Bossart return AE_OK; /* keep going */ 165d62a7d41SVinod Koul 166d62a7d41SVinod Koul if (acpi_bus_get_device(handle, &adev)) { 167e1c815f4SVinod Koul pr_err("%s: Couldn't find ACPI handle\n", __func__); 168d62a7d41SVinod Koul return AE_NOT_FOUND; 169d62a7d41SVinod Koul } 170d62a7d41SVinod Koul 171d62a7d41SVinod Koul res->handle = handle; 1726f11586fSPierre-Louis Bossart 1736f11586fSPierre-Louis Bossart /* 1746f11586fSPierre-Louis Bossart * On some Intel platforms, multiple children of the HDAS 1756f11586fSPierre-Louis Bossart * device can be found, but only one of them is the SoundWire 1766f11586fSPierre-Louis Bossart * controller. The SNDW device is always exposed with 1776f11586fSPierre-Louis Bossart * Name(_ADR, 0x40000000), with bits 31..28 representing the 1786f11586fSPierre-Louis Bossart * SoundWire link so filter accordingly 1796f11586fSPierre-Louis Bossart */ 1806f11586fSPierre-Louis Bossart if ((adr & GENMASK(31, 28)) >> 28 != SDW_LINK_TYPE) 1816f11586fSPierre-Louis Bossart return AE_OK; /* keep going */ 1826f11586fSPierre-Louis Bossart 1836f11586fSPierre-Louis Bossart /* device found, stop namespace walk */ 1846f11586fSPierre-Louis Bossart return AE_CTRL_TERMINATE; 185d62a7d41SVinod Koul } 186d62a7d41SVinod Koul 187d62a7d41SVinod Koul /** 188d62a7d41SVinod Koul * sdw_intel_init() - SoundWire Intel init routine 189d62a7d41SVinod Koul * @parent_handle: ACPI parent handle 190d62a7d41SVinod Koul * @res: resource data 191d62a7d41SVinod Koul * 192d62a7d41SVinod Koul * This scans the namespace and creates SoundWire link controller devices 193d62a7d41SVinod Koul * based on the info queried. 194d62a7d41SVinod Koul */ 195d62a7d41SVinod Koul void *sdw_intel_init(acpi_handle *parent_handle, struct sdw_intel_res *res) 196d62a7d41SVinod Koul { 197d62a7d41SVinod Koul acpi_status status; 198d62a7d41SVinod Koul 199d62a7d41SVinod Koul status = acpi_walk_namespace(ACPI_TYPE_DEVICE, 200d62a7d41SVinod Koul parent_handle, 1, 201d62a7d41SVinod Koul sdw_intel_acpi_cb, 202d62a7d41SVinod Koul NULL, res, NULL); 203d62a7d41SVinod Koul if (ACPI_FAILURE(status)) 204d62a7d41SVinod Koul return NULL; 205d62a7d41SVinod Koul 206d62a7d41SVinod Koul return sdw_intel_add_controller(res); 207d62a7d41SVinod Koul } 208d62a7d41SVinod Koul 209d62a7d41SVinod Koul /** 210d62a7d41SVinod Koul * sdw_intel_exit() - SoundWire Intel exit 211d62a7d41SVinod Koul * @arg: callback context 212d62a7d41SVinod Koul * 213d62a7d41SVinod Koul * Delete the controller instances created and cleanup 214d62a7d41SVinod Koul */ 215f98f690fSPierre-Louis Bossart void sdw_intel_exit(struct sdw_intel_ctx *ctx) 216d62a7d41SVinod Koul { 217d62a7d41SVinod Koul sdw_intel_cleanup_pdev(ctx); 218d62a7d41SVinod Koul kfree(ctx); 219d62a7d41SVinod Koul } 220d62a7d41SVinod Koul EXPORT_SYMBOL(sdw_intel_exit); 221d62a7d41SVinod Koul 222d62a7d41SVinod Koul MODULE_LICENSE("Dual BSD/GPL"); 223d62a7d41SVinod Koul MODULE_DESCRIPTION("Intel Soundwire Init Library"); 224