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/export.h> 12 #include <linux/io.h> 13 #include <linux/module.h> 14 #include <linux/platform_device.h> 15 16 #include "amd_init.h" 17 18 #define ACP_PAD_PULLDOWN_CTRL 0x0001448 19 #define ACP_SW_PAD_KEEPER_EN 0x0001454 20 #define AMD_SDW0_PAD_CTRL_MASK 0x60 21 #define AMD_SDW1_PAD_CTRL_MASK 5 22 #define AMD_SDW_PAD_CTRL_MASK (AMD_SDW0_PAD_CTRL_MASK | AMD_SDW1_PAD_CTRL_MASK) 23 #define AMD_SDW0_PAD_EN 1 24 #define AMD_SDW1_PAD_EN 0x10 25 #define AMD_SDW_PAD_EN (AMD_SDW0_PAD_EN | AMD_SDW1_PAD_EN) 26 27 static int amd_enable_sdw_pads(void __iomem *mmio, u32 link_mask, struct device *dev) 28 { 29 u32 pad_keeper_en, pad_pulldown_ctrl_mask; 30 31 switch (link_mask) { 32 case 1: 33 pad_keeper_en = AMD_SDW0_PAD_EN; 34 pad_pulldown_ctrl_mask = AMD_SDW0_PAD_CTRL_MASK; 35 break; 36 case 2: 37 pad_keeper_en = AMD_SDW1_PAD_EN; 38 pad_pulldown_ctrl_mask = AMD_SDW1_PAD_CTRL_MASK; 39 break; 40 case 3: 41 pad_keeper_en = AMD_SDW_PAD_EN; 42 pad_pulldown_ctrl_mask = AMD_SDW_PAD_CTRL_MASK; 43 break; 44 default: 45 dev_err(dev, "No SDW Links are enabled\n"); 46 return -ENODEV; 47 } 48 49 amd_updatel(mmio, ACP_SW_PAD_KEEPER_EN, pad_keeper_en, pad_keeper_en); 50 amd_updatel(mmio, ACP_PAD_PULLDOWN_CTRL, pad_pulldown_ctrl_mask, 0); 51 52 return 0; 53 } 54 55 static int sdw_amd_cleanup(struct sdw_amd_ctx *ctx) 56 { 57 int i; 58 59 for (i = 0; i < ctx->count; i++) { 60 if (!(ctx->link_mask & BIT(i))) 61 continue; 62 platform_device_unregister(ctx->pdev[i]); 63 } 64 65 return 0; 66 } 67 68 static struct sdw_amd_ctx *sdw_amd_probe_controller(struct sdw_amd_res *res) 69 { 70 struct sdw_amd_ctx *ctx; 71 struct acpi_device *adev; 72 struct resource *sdw_res; 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 sdw_res = kzalloc(sizeof(*sdw_res), GFP_KERNEL); 108 if (!sdw_res) { 109 kfree(ctx); 110 return NULL; 111 } 112 sdw_res->flags = IORESOURCE_MEM; 113 sdw_res->start = res->addr; 114 sdw_res->end = res->addr + res->reg_range; 115 memset(&pdevinfo, 0, sizeof(pdevinfo)); 116 link_mask = ctx->link_mask; 117 for (index = 0; index < count; index++) { 118 if (!(link_mask & BIT(index))) 119 continue; 120 121 sdw_pdata[index].instance = index; 122 sdw_pdata[index].acp_sdw_lock = res->acp_lock; 123 pdevinfo[index].name = "amd_sdw_manager"; 124 pdevinfo[index].id = index; 125 pdevinfo[index].parent = res->parent; 126 pdevinfo[index].num_res = 1; 127 pdevinfo[index].res = sdw_res; 128 pdevinfo[index].data = &sdw_pdata[index]; 129 pdevinfo[index].size_data = sizeof(struct acp_sdw_pdata); 130 pdevinfo[index].fwnode = acpi_fwnode_handle(adev); 131 ctx->pdev[index] = platform_device_register_full(&pdevinfo[index]); 132 if (IS_ERR(ctx->pdev[index])) 133 goto err; 134 } 135 kfree(sdw_res); 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(sdw_res); 146 kfree(ctx); 147 return NULL; 148 } 149 150 static int sdw_amd_startup(struct sdw_amd_ctx *ctx) 151 { 152 struct amd_sdw_manager *amd_manager; 153 int i, ret; 154 155 /* Startup SDW Manager devices */ 156 for (i = 0; i < ctx->count; i++) { 157 if (!(ctx->link_mask & BIT(i))) 158 continue; 159 amd_manager = dev_get_drvdata(&ctx->pdev[i]->dev); 160 ret = amd_sdw_manager_start(amd_manager); 161 if (ret) 162 return ret; 163 } 164 165 return 0; 166 } 167 168 int sdw_amd_probe(struct sdw_amd_res *res, struct sdw_amd_ctx **sdw_ctx) 169 { 170 *sdw_ctx = sdw_amd_probe_controller(res); 171 if (!*sdw_ctx) 172 return -ENODEV; 173 174 return sdw_amd_startup(*sdw_ctx); 175 } 176 EXPORT_SYMBOL_NS(sdw_amd_probe, SOUNDWIRE_AMD_INIT); 177 178 void sdw_amd_exit(struct sdw_amd_ctx *ctx) 179 { 180 sdw_amd_cleanup(ctx); 181 kfree(ctx->ids); 182 kfree(ctx); 183 } 184 EXPORT_SYMBOL_NS(sdw_amd_exit, SOUNDWIRE_AMD_INIT); 185 186 int sdw_amd_get_slave_info(struct sdw_amd_ctx *ctx) 187 { 188 struct amd_sdw_manager *amd_manager; 189 struct sdw_bus *bus; 190 struct sdw_slave *slave; 191 struct list_head *node; 192 int index; 193 int i = 0; 194 int num_slaves = 0; 195 196 for (index = 0; index < ctx->count; index++) { 197 if (!(ctx->link_mask & BIT(index))) 198 continue; 199 amd_manager = dev_get_drvdata(&ctx->pdev[index]->dev); 200 if (!amd_manager) 201 return -ENODEV; 202 bus = &amd_manager->bus; 203 /* Calculate number of slaves */ 204 list_for_each(node, &bus->slaves) 205 num_slaves++; 206 } 207 208 ctx->ids = kcalloc(num_slaves, sizeof(*ctx->ids), GFP_KERNEL); 209 if (!ctx->ids) 210 return -ENOMEM; 211 ctx->num_slaves = num_slaves; 212 for (index = 0; index < ctx->count; index++) { 213 if (!(ctx->link_mask & BIT(index))) 214 continue; 215 amd_manager = dev_get_drvdata(&ctx->pdev[index]->dev); 216 if (amd_manager) { 217 bus = &amd_manager->bus; 218 list_for_each_entry(slave, &bus->slaves, node) { 219 ctx->ids[i].id = slave->id; 220 ctx->ids[i].link_id = bus->link_id; 221 i++; 222 } 223 } 224 } 225 return 0; 226 } 227 EXPORT_SYMBOL_NS(sdw_amd_get_slave_info, SOUNDWIRE_AMD_INIT); 228 229 MODULE_AUTHOR("Vijendar.Mukunda@amd.com"); 230 MODULE_DESCRIPTION("AMD SoundWire Init Library"); 231 MODULE_LICENSE("Dual BSD/GPL"); 232