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
amd_enable_sdw_pads(void __iomem * mmio,u32 link_mask,struct device * dev)28 static 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
sdw_amd_cleanup(struct sdw_amd_ctx * ctx)56 static 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
sdw_amd_probe_controller(struct sdw_amd_res * res)69 static 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;
138 err:
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
sdw_amd_startup(struct sdw_amd_ctx * ctx)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
sdw_amd_probe(struct sdw_amd_res * res,struct sdw_amd_ctx ** sdw_ctx)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
sdw_amd_exit(struct sdw_amd_ctx * ctx)178 void sdw_amd_exit(struct sdw_amd_ctx *ctx)
179 {
180 sdw_amd_cleanup(ctx);
181 kfree(ctx->peripherals);
182 kfree(ctx);
183 }
184 EXPORT_SYMBOL_NS(sdw_amd_exit, "SOUNDWIRE_AMD_INIT");
185
sdw_amd_get_slave_info(struct sdw_amd_ctx * ctx)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->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 }
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