GNU Linux-libre 6.9.1-gnu
[releases.git] / drivers / soundwire / amd_init.c
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");