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