1 // SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause) 2 /* 3 * SoundWire AMD Manager Initialize routines 4 * 5 * Initializes and creates SDW devices based on ACPI and Hardware values 6 * 7 * Copyright 2024 Advanced Micro Devices, Inc. 8 */ 9 10 #include <linux/acpi.h> 11 #include <linux/cleanup.h> 12 #include <linux/export.h> 13 #include <linux/io.h> 14 #include <linux/module.h> 15 #include <linux/platform_device.h> 16 17 #include "amd_init.h" 18 19 #define ACP_PAD_PULLDOWN_CTRL 0x0001448 20 #define ACP_SW_PAD_KEEPER_EN 0x0001454 21 #define AMD_SDW0_PAD_CTRL_MASK 0x60 22 #define AMD_SDW1_PAD_CTRL_MASK 5 23 #define AMD_SDW_PAD_CTRL_MASK (AMD_SDW0_PAD_CTRL_MASK | AMD_SDW1_PAD_CTRL_MASK) 24 #define AMD_SDW0_PAD_EN 1 25 #define AMD_SDW1_PAD_EN 0x10 26 #define AMD_SDW_PAD_EN (AMD_SDW0_PAD_EN | AMD_SDW1_PAD_EN) 27 28 static int amd_enable_sdw_pads(void __iomem *mmio, u32 link_mask, struct device *dev) 29 { 30 u32 pad_keeper_en, pad_pulldown_ctrl_mask; 31 32 switch (link_mask) { 33 case 1: 34 pad_keeper_en = AMD_SDW0_PAD_EN; 35 pad_pulldown_ctrl_mask = AMD_SDW0_PAD_CTRL_MASK; 36 break; 37 case 2: 38 pad_keeper_en = AMD_SDW1_PAD_EN; 39 pad_pulldown_ctrl_mask = AMD_SDW1_PAD_CTRL_MASK; 40 break; 41 case 3: 42 pad_keeper_en = AMD_SDW_PAD_EN; 43 pad_pulldown_ctrl_mask = AMD_SDW_PAD_CTRL_MASK; 44 break; 45 default: 46 dev_err(dev, "No SDW Links are enabled\n"); 47 return -ENODEV; 48 } 49 50 amd_updatel(mmio, ACP_SW_PAD_KEEPER_EN, pad_keeper_en, pad_keeper_en); 51 amd_updatel(mmio, ACP_PAD_PULLDOWN_CTRL, pad_pulldown_ctrl_mask, 0); 52 53 return 0; 54 } 55 56 static int sdw_amd_cleanup(struct sdw_amd_ctx *ctx) 57 { 58 int i; 59 60 for (i = 0; i < ctx->count; i++) { 61 if (!(ctx->link_mask & BIT(i))) 62 continue; 63 platform_device_unregister(ctx->pdev[i]); 64 } 65 66 return 0; 67 } 68 69 static struct sdw_amd_ctx *sdw_amd_probe_controller(struct sdw_amd_res *res) 70 { 71 struct sdw_amd_ctx *ctx; 72 struct acpi_device *adev; 73 struct acp_sdw_pdata sdw_pdata[2]; 74 struct platform_device_info pdevinfo[2]; 75 u32 link_mask; 76 int count, index; 77 int ret; 78 79 if (!res) 80 return NULL; 81 82 adev = acpi_fetch_acpi_dev(res->handle); 83 if (!adev) 84 return NULL; 85 86 if (!res->count) 87 return NULL; 88 89 count = res->count; 90 dev_dbg(&adev->dev, "Creating %d SDW Link devices\n", count); 91 ret = amd_enable_sdw_pads(res->mmio_base, res->link_mask, res->parent); 92 if (ret) 93 return NULL; 94 95 /* 96 * we need to alloc/free memory manually and can't use devm: 97 * this routine may be called from a workqueue, and not from 98 * the parent .probe. 99 * If devm_ was used, the memory might never be freed on errors. 100 */ 101 ctx = kzalloc(sizeof(*ctx), GFP_KERNEL); 102 if (!ctx) 103 return NULL; 104 105 ctx->count = count; 106 ctx->link_mask = res->link_mask; 107 struct resource *sdw_res __free(kfree) = kzalloc(sizeof(*sdw_res), 108 GFP_KERNEL); 109 if (!sdw_res) { 110 kfree(ctx); 111 return NULL; 112 } 113 sdw_res->flags = IORESOURCE_MEM; 114 sdw_res->start = res->addr; 115 sdw_res->end = res->addr + res->reg_range; 116 memset(&pdevinfo, 0, sizeof(pdevinfo)); 117 link_mask = ctx->link_mask; 118 for (index = 0; index < count; index++) { 119 if (!(link_mask & BIT(index))) 120 continue; 121 122 sdw_pdata[index].instance = index; 123 sdw_pdata[index].acp_sdw_lock = res->acp_lock; 124 pdevinfo[index].name = "amd_sdw_manager"; 125 pdevinfo[index].id = index; 126 pdevinfo[index].parent = res->parent; 127 pdevinfo[index].num_res = 1; 128 pdevinfo[index].res = sdw_res; 129 pdevinfo[index].data = &sdw_pdata[index]; 130 pdevinfo[index].size_data = sizeof(struct acp_sdw_pdata); 131 pdevinfo[index].fwnode = acpi_fwnode_handle(adev); 132 ctx->pdev[index] = platform_device_register_full(&pdevinfo[index]); 133 if (IS_ERR(ctx->pdev[index])) 134 goto err; 135 } 136 return ctx; 137 err: 138 while (index--) { 139 if (!(link_mask & BIT(index))) 140 continue; 141 142 platform_device_unregister(ctx->pdev[index]); 143 } 144 145 kfree(ctx); 146 return NULL; 147 } 148 149 static int sdw_amd_startup(struct sdw_amd_ctx *ctx) 150 { 151 struct amd_sdw_manager *amd_manager; 152 int i, ret; 153 154 /* Startup SDW Manager devices */ 155 for (i = 0; i < ctx->count; i++) { 156 if (!(ctx->link_mask & BIT(i))) 157 continue; 158 amd_manager = dev_get_drvdata(&ctx->pdev[i]->dev); 159 ret = amd_sdw_manager_start(amd_manager); 160 if (ret) 161 return ret; 162 } 163 164 return 0; 165 } 166 167 int sdw_amd_probe(struct sdw_amd_res *res, struct sdw_amd_ctx **sdw_ctx) 168 { 169 *sdw_ctx = sdw_amd_probe_controller(res); 170 if (!*sdw_ctx) 171 return -ENODEV; 172 173 return sdw_amd_startup(*sdw_ctx); 174 } 175 EXPORT_SYMBOL_NS(sdw_amd_probe, SOUNDWIRE_AMD_INIT); 176 177 void sdw_amd_exit(struct sdw_amd_ctx *ctx) 178 { 179 sdw_amd_cleanup(ctx); 180 kfree(ctx->ids); 181 kfree(ctx); 182 } 183 EXPORT_SYMBOL_NS(sdw_amd_exit, SOUNDWIRE_AMD_INIT); 184 185 int sdw_amd_get_slave_info(struct sdw_amd_ctx *ctx) 186 { 187 struct amd_sdw_manager *amd_manager; 188 struct sdw_bus *bus; 189 struct sdw_slave *slave; 190 struct list_head *node; 191 int index; 192 int i = 0; 193 int num_slaves = 0; 194 195 for (index = 0; index < ctx->count; index++) { 196 if (!(ctx->link_mask & BIT(index))) 197 continue; 198 amd_manager = dev_get_drvdata(&ctx->pdev[index]->dev); 199 if (!amd_manager) 200 return -ENODEV; 201 bus = &amd_manager->bus; 202 /* Calculate number of slaves */ 203 list_for_each(node, &bus->slaves) 204 num_slaves++; 205 } 206 207 ctx->ids = kcalloc(num_slaves, sizeof(*ctx->ids), GFP_KERNEL); 208 if (!ctx->ids) 209 return -ENOMEM; 210 ctx->num_slaves = num_slaves; 211 for (index = 0; index < ctx->count; index++) { 212 if (!(ctx->link_mask & BIT(index))) 213 continue; 214 amd_manager = dev_get_drvdata(&ctx->pdev[index]->dev); 215 if (amd_manager) { 216 bus = &amd_manager->bus; 217 list_for_each_entry(slave, &bus->slaves, node) { 218 ctx->ids[i].id = slave->id; 219 ctx->ids[i].link_id = bus->link_id; 220 i++; 221 } 222 } 223 } 224 return 0; 225 } 226 EXPORT_SYMBOL_NS(sdw_amd_get_slave_info, SOUNDWIRE_AMD_INIT); 227 228 MODULE_AUTHOR("Vijendar.Mukunda@amd.com"); 229 MODULE_DESCRIPTION("AMD SoundWire Init Library"); 230 MODULE_LICENSE("Dual BSD/GPL"); 231