xref: /linux/drivers/soundwire/amd_init.c (revision 4f05e82003d1c20da29fa593420b8d92e2c8d4e6)
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