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