Linux Audio

Check our new training course

Linux BSP development engineering services

Need help to port Linux and bootloaders to your hardware?
Loading...
Note: File does not exist in v6.8.
  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
 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 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		sdw_pdata[index].acp_rev = res->acp_rev;
125		pdevinfo[index].name = "amd_sdw_manager";
126		pdevinfo[index].id = index;
127		pdevinfo[index].parent = res->parent;
128		pdevinfo[index].num_res = 1;
129		pdevinfo[index].res = sdw_res;
130		pdevinfo[index].data = &sdw_pdata[index];
131		pdevinfo[index].size_data = sizeof(struct acp_sdw_pdata);
132		pdevinfo[index].fwnode = acpi_fwnode_handle(adev);
133		ctx->pdev[index] = platform_device_register_full(&pdevinfo[index]);
134		if (IS_ERR(ctx->pdev[index]))
135			goto err;
136	}
137	return ctx;
138err:
139	while (index--) {
140		if (!(link_mask & BIT(index)))
141			continue;
142
143		platform_device_unregister(ctx->pdev[index]);
144	}
145
146	kfree(ctx);
147	return NULL;
148}
149
150static 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
168int 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}
176EXPORT_SYMBOL_NS(sdw_amd_probe, "SOUNDWIRE_AMD_INIT");
177
178void sdw_amd_exit(struct sdw_amd_ctx *ctx)
179{
180	sdw_amd_cleanup(ctx);
181	kfree(ctx->peripherals);
182	kfree(ctx);
183}
184EXPORT_SYMBOL_NS(sdw_amd_exit, "SOUNDWIRE_AMD_INIT");
185
186int 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->peripherals = kmalloc(struct_size(ctx->peripherals, array, num_slaves),
209				   GFP_KERNEL);
210	if (!ctx->peripherals)
211		return -ENOMEM;
212	ctx->peripherals->num_peripherals = num_slaves;
213	for (index = 0; index < ctx->count; index++) {
214		if (!(ctx->link_mask & BIT(index)))
215			continue;
216		amd_manager = dev_get_drvdata(&ctx->pdev[index]->dev);
217		if (amd_manager) {
218			bus = &amd_manager->bus;
219			list_for_each_entry(slave, &bus->slaves, node) {
220				ctx->peripherals->array[i] = slave;
221				i++;
222			}
223		}
224	}
225	return 0;
226}
227EXPORT_SYMBOL_NS(sdw_amd_get_slave_info, "SOUNDWIRE_AMD_INIT");
228
229MODULE_AUTHOR("Vijendar.Mukunda@amd.com");
230MODULE_DESCRIPTION("AMD SoundWire Init Library");
231MODULE_LICENSE("Dual BSD/GPL");