]> Git Repo - J-linux.git/blob - drivers/soundwire/amd_init.c
Merge tag 'vfs-6.13-rc7.fixes' of git://git.kernel.org/pub/scm/linux/kernel/git/vfs/vfs
[J-linux.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/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
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
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
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
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->peripherals);
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->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("[email protected]");
230 MODULE_DESCRIPTION("AMD SoundWire Init Library");
231 MODULE_LICENSE("Dual BSD/GPL");
This page took 0.038961 seconds and 4 git commands to generate.