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