brintos

brintos / linux-shallow public Read only

0
0
Text · 5.6 KiB · db040f4 Raw
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