231 lines · c
1// SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause)2/*3 * SoundWire AMD Manager Initialize routines4 *5 * Initializes and creates SDW devices based on ACPI and Hardware values6 *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 0x000144820#define ACP_SW_PAD_KEEPER_EN 0x000145421#define AMD_SDW0_PAD_CTRL_MASK 0x6022#define AMD_SDW1_PAD_CTRL_MASK 523#define AMD_SDW_PAD_CTRL_MASK (AMD_SDW0_PAD_CTRL_MASK | AMD_SDW1_PAD_CTRL_MASK)24#define AMD_SDW0_PAD_EN 125#define AMD_SDW1_PAD_EN 0x1026#define AMD_SDW_PAD_EN (AMD_SDW0_PAD_EN | AMD_SDW1_PAD_EN)27 28static 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 56static 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 69static 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 from98 * 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;137err: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 149static 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 167int 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}175EXPORT_SYMBOL_NS(sdw_amd_probe, SOUNDWIRE_AMD_INIT);176 177void sdw_amd_exit(struct sdw_amd_ctx *ctx)178{179 sdw_amd_cleanup(ctx);180 kfree(ctx->ids);181 kfree(ctx);182}183EXPORT_SYMBOL_NS(sdw_amd_exit, SOUNDWIRE_AMD_INIT);184 185int 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}226EXPORT_SYMBOL_NS(sdw_amd_get_slave_info, SOUNDWIRE_AMD_INIT);227 228MODULE_AUTHOR("Vijendar.Mukunda@amd.com");229MODULE_DESCRIPTION("AMD SoundWire Init Library");230MODULE_LICENSE("Dual BSD/GPL");231