]> Git Repo - J-linux.git/blob - drivers/soundwire/amd_init.c
Merge tag 'phy-for-6.13' of git://git.kernel.org/pub/scm/linux/kernel/git/phy/linux-phy
[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                 pdevinfo[index].name = "amd_sdw_manager";
125                 pdevinfo[index].id = index;
126                 pdevinfo[index].parent = res->parent;
127                 pdevinfo[index].num_res = 1;
128                 pdevinfo[index].res = sdw_res;
129                 pdevinfo[index].data = &sdw_pdata[index];
130                 pdevinfo[index].size_data = sizeof(struct acp_sdw_pdata);
131                 pdevinfo[index].fwnode = acpi_fwnode_handle(adev);
132                 ctx->pdev[index] = platform_device_register_full(&pdevinfo[index]);
133                 if (IS_ERR(ctx->pdev[index]))
134                         goto err;
135         }
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(ctx);
146         return NULL;
147 }
148
149 static int sdw_amd_startup(struct sdw_amd_ctx *ctx)
150 {
151         struct amd_sdw_manager *amd_manager;
152         int i, ret;
153
154         /* Startup SDW Manager devices */
155         for (i = 0; i < ctx->count; i++) {
156                 if (!(ctx->link_mask & BIT(i)))
157                         continue;
158                 amd_manager = dev_get_drvdata(&ctx->pdev[i]->dev);
159                 ret = amd_sdw_manager_start(amd_manager);
160                 if (ret)
161                         return ret;
162         }
163
164         return 0;
165 }
166
167 int sdw_amd_probe(struct sdw_amd_res *res, struct sdw_amd_ctx **sdw_ctx)
168 {
169         *sdw_ctx = sdw_amd_probe_controller(res);
170         if (!*sdw_ctx)
171                 return -ENODEV;
172
173         return sdw_amd_startup(*sdw_ctx);
174 }
175 EXPORT_SYMBOL_NS(sdw_amd_probe, SOUNDWIRE_AMD_INIT);
176
177 void sdw_amd_exit(struct sdw_amd_ctx *ctx)
178 {
179         sdw_amd_cleanup(ctx);
180         kfree(ctx->peripherals);
181         kfree(ctx);
182 }
183 EXPORT_SYMBOL_NS(sdw_amd_exit, SOUNDWIRE_AMD_INIT);
184
185 int sdw_amd_get_slave_info(struct sdw_amd_ctx *ctx)
186 {
187         struct amd_sdw_manager *amd_manager;
188         struct sdw_bus *bus;
189         struct sdw_slave *slave;
190         struct list_head *node;
191         int index;
192         int i = 0;
193         int num_slaves = 0;
194
195         for (index = 0; index < ctx->count; index++) {
196                 if (!(ctx->link_mask & BIT(index)))
197                         continue;
198                 amd_manager = dev_get_drvdata(&ctx->pdev[index]->dev);
199                 if (!amd_manager)
200                         return -ENODEV;
201                 bus = &amd_manager->bus;
202                 /* Calculate number of slaves */
203                 list_for_each(node, &bus->slaves)
204                         num_slaves++;
205         }
206
207         ctx->peripherals = kmalloc(struct_size(ctx->peripherals, array, num_slaves),
208                                    GFP_KERNEL);
209         if (!ctx->peripherals)
210                 return -ENOMEM;
211         ctx->peripherals->num_peripherals = 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->peripherals->array[i] = slave;
220                                 i++;
221                         }
222                 }
223         }
224         return 0;
225 }
226 EXPORT_SYMBOL_NS(sdw_amd_get_slave_info, SOUNDWIRE_AMD_INIT);
227
228 MODULE_AUTHOR("[email protected]");
229 MODULE_DESCRIPTION("AMD SoundWire Init Library");
230 MODULE_LICENSE("Dual BSD/GPL");
This page took 0.04115 seconds and 4 git commands to generate.