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> 11d62a7d41SVinod Koul #include <linux/platform_device.h> 12d62a7d41SVinod Koul #include <linux/soundwire/sdw_intel.h> 13d62a7d41SVinod Koul #include "intel.h" 14d62a7d41SVinod Koul 15d62a7d41SVinod Koul #define SDW_MAX_LINKS 4 16d62a7d41SVinod Koul #define SDW_SHIM_LCAP 0x0 17d62a7d41SVinod Koul #define SDW_SHIM_BASE 0x2C000 18d62a7d41SVinod Koul #define SDW_ALH_BASE 0x2C800 19d62a7d41SVinod Koul #define SDW_LINK_BASE 0x30000 20d62a7d41SVinod Koul #define SDW_LINK_SIZE 0x10000 21d62a7d41SVinod Koul 22d62a7d41SVinod Koul struct sdw_link_data { 23d62a7d41SVinod Koul struct sdw_intel_link_res res; 24d62a7d41SVinod Koul struct platform_device *pdev; 25d62a7d41SVinod Koul }; 26d62a7d41SVinod Koul 27d62a7d41SVinod Koul struct sdw_intel_ctx { 28d62a7d41SVinod Koul int count; 29d62a7d41SVinod Koul struct sdw_link_data *links; 30d62a7d41SVinod Koul }; 31d62a7d41SVinod Koul 32d62a7d41SVinod Koul static int sdw_intel_cleanup_pdev(struct sdw_intel_ctx *ctx) 33d62a7d41SVinod Koul { 34d62a7d41SVinod Koul struct sdw_link_data *link = ctx->links; 35d62a7d41SVinod Koul int i; 36d62a7d41SVinod Koul 37d62a7d41SVinod Koul if (!link) 38d62a7d41SVinod Koul return 0; 39d62a7d41SVinod Koul 40d62a7d41SVinod Koul for (i = 0; i < ctx->count; i++) { 41d62a7d41SVinod Koul if (link->pdev) 42d62a7d41SVinod Koul platform_device_unregister(link->pdev); 43d62a7d41SVinod Koul link++; 44d62a7d41SVinod Koul } 45d62a7d41SVinod Koul 46d62a7d41SVinod Koul kfree(ctx->links); 47d62a7d41SVinod Koul ctx->links = NULL; 48d62a7d41SVinod Koul 49d62a7d41SVinod Koul return 0; 50d62a7d41SVinod Koul } 51d62a7d41SVinod Koul 52d62a7d41SVinod Koul static struct sdw_intel_ctx 53d62a7d41SVinod Koul *sdw_intel_add_controller(struct sdw_intel_res *res) 54d62a7d41SVinod Koul { 55d62a7d41SVinod Koul struct platform_device_info pdevinfo; 56d62a7d41SVinod Koul struct platform_device *pdev; 57d62a7d41SVinod Koul struct sdw_link_data *link; 58d62a7d41SVinod Koul struct sdw_intel_ctx *ctx; 59d62a7d41SVinod Koul struct acpi_device *adev; 60d62a7d41SVinod Koul int ret, i; 61d62a7d41SVinod Koul u8 count; 62d62a7d41SVinod Koul u32 caps; 63d62a7d41SVinod Koul 64d62a7d41SVinod Koul if (acpi_bus_get_device(res->handle, &adev)) 65d62a7d41SVinod Koul return NULL; 66d62a7d41SVinod Koul 67d62a7d41SVinod Koul /* Found controller, find links supported */ 68d62a7d41SVinod Koul count = 0; 69d62a7d41SVinod Koul ret = fwnode_property_read_u8_array(acpi_fwnode_handle(adev), 70d62a7d41SVinod Koul "mipi-sdw-master-count", &count, 1); 71d62a7d41SVinod Koul 72d62a7d41SVinod Koul /* Don't fail on error, continue and use hw value */ 73d62a7d41SVinod Koul if (ret) { 74d62a7d41SVinod Koul dev_err(&adev->dev, 75d62a7d41SVinod Koul "Failed to read mipi-sdw-master-count: %d\n", ret); 76d62a7d41SVinod Koul count = SDW_MAX_LINKS; 77d62a7d41SVinod Koul } 78d62a7d41SVinod Koul 79d62a7d41SVinod Koul /* Check SNDWLCAP.LCOUNT */ 80d62a7d41SVinod Koul caps = ioread32(res->mmio_base + SDW_SHIM_BASE + SDW_SHIM_LCAP); 81d62a7d41SVinod Koul 82d62a7d41SVinod Koul /* Check HW supported vs property value and use min of two */ 83d62a7d41SVinod Koul count = min_t(u8, caps, count); 84d62a7d41SVinod Koul 85d62a7d41SVinod Koul /* Check count is within bounds */ 86d62a7d41SVinod Koul if (count > SDW_MAX_LINKS) { 87d62a7d41SVinod Koul dev_err(&adev->dev, "Link count %d exceeds max %d\n", 88d62a7d41SVinod Koul count, SDW_MAX_LINKS); 89d62a7d41SVinod Koul return NULL; 90d62a7d41SVinod Koul } 91d62a7d41SVinod Koul 92d62a7d41SVinod Koul dev_dbg(&adev->dev, "Creating %d SDW Link devices\n", count); 93d62a7d41SVinod Koul 94d62a7d41SVinod Koul ctx = kzalloc(sizeof(*ctx), GFP_KERNEL); 95d62a7d41SVinod Koul if (!ctx) 96d62a7d41SVinod Koul return NULL; 97d62a7d41SVinod Koul 98d62a7d41SVinod Koul ctx->count = count; 99d62a7d41SVinod Koul ctx->links = kcalloc(ctx->count, sizeof(*ctx->links), GFP_KERNEL); 100d62a7d41SVinod Koul if (!ctx->links) 101d62a7d41SVinod Koul goto link_err; 102d62a7d41SVinod Koul 103d62a7d41SVinod Koul link = ctx->links; 104d62a7d41SVinod Koul 105d62a7d41SVinod Koul /* Create SDW Master devices */ 106d62a7d41SVinod Koul for (i = 0; i < count; i++) { 107d62a7d41SVinod Koul 108d62a7d41SVinod Koul link->res.irq = res->irq; 109d62a7d41SVinod Koul link->res.registers = res->mmio_base + SDW_LINK_BASE 110d62a7d41SVinod Koul + (SDW_LINK_SIZE * i); 111d62a7d41SVinod Koul link->res.shim = res->mmio_base + SDW_SHIM_BASE; 112d62a7d41SVinod Koul link->res.alh = res->mmio_base + SDW_ALH_BASE; 113d62a7d41SVinod Koul 114c46302ecSVinod Koul link->res.ops = res->ops; 115c46302ecSVinod Koul link->res.arg = res->arg; 116c46302ecSVinod Koul 117d62a7d41SVinod Koul memset(&pdevinfo, 0, sizeof(pdevinfo)); 118d62a7d41SVinod Koul 119d62a7d41SVinod Koul pdevinfo.parent = res->parent; 120d62a7d41SVinod Koul pdevinfo.name = "int-sdw"; 121d62a7d41SVinod Koul pdevinfo.id = i; 122d62a7d41SVinod Koul pdevinfo.fwnode = acpi_fwnode_handle(adev); 123d62a7d41SVinod Koul pdevinfo.data = &link->res; 124d62a7d41SVinod Koul pdevinfo.size_data = sizeof(link->res); 125d62a7d41SVinod Koul 126d62a7d41SVinod Koul pdev = platform_device_register_full(&pdevinfo); 127d62a7d41SVinod Koul if (IS_ERR(pdev)) { 128d62a7d41SVinod Koul dev_err(&adev->dev, 129d62a7d41SVinod Koul "platform device creation failed: %ld\n", 130d62a7d41SVinod Koul PTR_ERR(pdev)); 131d62a7d41SVinod Koul goto pdev_err; 132d62a7d41SVinod Koul } 133d62a7d41SVinod Koul 134d62a7d41SVinod Koul link->pdev = pdev; 135d62a7d41SVinod Koul link++; 136d62a7d41SVinod Koul } 137d62a7d41SVinod Koul 138d62a7d41SVinod Koul return ctx; 139d62a7d41SVinod Koul 140d62a7d41SVinod Koul pdev_err: 141d62a7d41SVinod Koul sdw_intel_cleanup_pdev(ctx); 142d62a7d41SVinod Koul link_err: 143d62a7d41SVinod Koul kfree(ctx); 144d62a7d41SVinod Koul return NULL; 145d62a7d41SVinod Koul } 146d62a7d41SVinod Koul 147d62a7d41SVinod Koul static acpi_status sdw_intel_acpi_cb(acpi_handle handle, u32 level, 148d62a7d41SVinod Koul void *cdata, void **return_value) 149d62a7d41SVinod Koul { 150d62a7d41SVinod Koul struct sdw_intel_res *res = cdata; 151d62a7d41SVinod Koul struct acpi_device *adev; 152d62a7d41SVinod Koul 153d62a7d41SVinod Koul if (acpi_bus_get_device(handle, &adev)) { 154d62a7d41SVinod Koul dev_err(&adev->dev, "Couldn't find ACPI handle\n"); 155d62a7d41SVinod Koul return AE_NOT_FOUND; 156d62a7d41SVinod Koul } 157d62a7d41SVinod Koul 158d62a7d41SVinod Koul res->handle = handle; 159d62a7d41SVinod Koul return AE_OK; 160d62a7d41SVinod Koul } 161d62a7d41SVinod Koul 162d62a7d41SVinod Koul /** 163d62a7d41SVinod Koul * sdw_intel_init() - SoundWire Intel init routine 164d62a7d41SVinod Koul * @parent_handle: ACPI parent handle 165d62a7d41SVinod Koul * @res: resource data 166d62a7d41SVinod Koul * 167d62a7d41SVinod Koul * This scans the namespace and creates SoundWire link controller devices 168d62a7d41SVinod Koul * based on the info queried. 169d62a7d41SVinod Koul */ 170d62a7d41SVinod Koul void *sdw_intel_init(acpi_handle *parent_handle, struct sdw_intel_res *res) 171d62a7d41SVinod Koul { 172d62a7d41SVinod Koul acpi_status status; 173d62a7d41SVinod Koul 174d62a7d41SVinod Koul status = acpi_walk_namespace(ACPI_TYPE_DEVICE, 175d62a7d41SVinod Koul parent_handle, 1, 176d62a7d41SVinod Koul sdw_intel_acpi_cb, 177d62a7d41SVinod Koul NULL, res, NULL); 178d62a7d41SVinod Koul if (ACPI_FAILURE(status)) 179d62a7d41SVinod Koul return NULL; 180d62a7d41SVinod Koul 181d62a7d41SVinod Koul return sdw_intel_add_controller(res); 182d62a7d41SVinod Koul } 183d62a7d41SVinod Koul EXPORT_SYMBOL(sdw_intel_init); 184d62a7d41SVinod Koul 185d62a7d41SVinod Koul /** 186d62a7d41SVinod Koul * sdw_intel_exit() - SoundWire Intel exit 187d62a7d41SVinod Koul * @arg: callback context 188d62a7d41SVinod Koul * 189d62a7d41SVinod Koul * Delete the controller instances created and cleanup 190d62a7d41SVinod Koul */ 191d62a7d41SVinod Koul void sdw_intel_exit(void *arg) 192d62a7d41SVinod Koul { 193d62a7d41SVinod Koul struct sdw_intel_ctx *ctx = arg; 194d62a7d41SVinod Koul 195d62a7d41SVinod Koul sdw_intel_cleanup_pdev(ctx); 196d62a7d41SVinod Koul kfree(ctx); 197d62a7d41SVinod Koul } 198d62a7d41SVinod Koul EXPORT_SYMBOL(sdw_intel_exit); 199d62a7d41SVinod Koul 200d62a7d41SVinod Koul MODULE_LICENSE("Dual BSD/GPL"); 201d62a7d41SVinod Koul MODULE_DESCRIPTION("Intel Soundwire Init Library"); 202