1 // SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause)
3 * SoundWire AMD Manager Initialize routines
5 * Initializes and creates SDW devices based on ACPI and Hardware values
7 * Copyright 2024 Advanced Micro Devices, Inc.
10 #include <linux/acpi.h>
11 #include <linux/cleanup.h>
12 #include <linux/export.h>
14 #include <linux/module.h>
15 #include <linux/platform_device.h>
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)
28 static int amd_enable_sdw_pads(void __iomem
*mmio
, u32 link_mask
, struct device
*dev
)
30 u32 pad_keeper_en
, pad_pulldown_ctrl_mask
;
34 pad_keeper_en
= AMD_SDW0_PAD_EN
;
35 pad_pulldown_ctrl_mask
= AMD_SDW0_PAD_CTRL_MASK
;
38 pad_keeper_en
= AMD_SDW1_PAD_EN
;
39 pad_pulldown_ctrl_mask
= AMD_SDW1_PAD_CTRL_MASK
;
42 pad_keeper_en
= AMD_SDW_PAD_EN
;
43 pad_pulldown_ctrl_mask
= AMD_SDW_PAD_CTRL_MASK
;
46 dev_err(dev
, "No SDW Links are enabled\n");
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);
56 static int sdw_amd_cleanup(struct sdw_amd_ctx
*ctx
)
60 for (i
= 0; i
< ctx
->count
; i
++) {
61 if (!(ctx
->link_mask
& BIT(i
)))
63 platform_device_unregister(ctx
->pdev
[i
]);
69 static struct sdw_amd_ctx
*sdw_amd_probe_controller(struct sdw_amd_res
*res
)
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];
82 adev
= acpi_fetch_acpi_dev(res
->handle
);
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
);
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
99 * If devm_ was used, the memory might never be freed on errors.
101 ctx
= kzalloc(sizeof(*ctx
), GFP_KERNEL
);
106 ctx
->link_mask
= res
->link_mask
;
107 struct resource
*sdw_res
__free(kfree
) = kzalloc(sizeof(*sdw_res
),
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
)))
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
]))
140 if (!(link_mask
& BIT(index
)))
143 platform_device_unregister(ctx
->pdev
[index
]);
150 static int sdw_amd_startup(struct sdw_amd_ctx
*ctx
)
152 struct amd_sdw_manager
*amd_manager
;
155 /* Startup SDW Manager devices */
156 for (i
= 0; i
< ctx
->count
; i
++) {
157 if (!(ctx
->link_mask
& BIT(i
)))
159 amd_manager
= dev_get_drvdata(&ctx
->pdev
[i
]->dev
);
160 ret
= amd_sdw_manager_start(amd_manager
);
168 int sdw_amd_probe(struct sdw_amd_res
*res
, struct sdw_amd_ctx
**sdw_ctx
)
170 *sdw_ctx
= sdw_amd_probe_controller(res
);
174 return sdw_amd_startup(*sdw_ctx
);
176 EXPORT_SYMBOL_NS(sdw_amd_probe
, "SOUNDWIRE_AMD_INIT");
178 void sdw_amd_exit(struct sdw_amd_ctx
*ctx
)
180 sdw_amd_cleanup(ctx
);
181 kfree(ctx
->peripherals
);
184 EXPORT_SYMBOL_NS(sdw_amd_exit
, "SOUNDWIRE_AMD_INIT");
186 int sdw_amd_get_slave_info(struct sdw_amd_ctx
*ctx
)
188 struct amd_sdw_manager
*amd_manager
;
190 struct sdw_slave
*slave
;
191 struct list_head
*node
;
196 for (index
= 0; index
< ctx
->count
; index
++) {
197 if (!(ctx
->link_mask
& BIT(index
)))
199 amd_manager
= dev_get_drvdata(&ctx
->pdev
[index
]->dev
);
202 bus
= &amd_manager
->bus
;
203 /* Calculate number of slaves */
204 list_for_each(node
, &bus
->slaves
)
208 ctx
->peripherals
= kmalloc(struct_size(ctx
->peripherals
, array
, num_slaves
),
210 if (!ctx
->peripherals
)
212 ctx
->peripherals
->num_peripherals
= num_slaves
;
213 for (index
= 0; index
< ctx
->count
; index
++) {
214 if (!(ctx
->link_mask
& BIT(index
)))
216 amd_manager
= dev_get_drvdata(&ctx
->pdev
[index
]->dev
);
218 bus
= &amd_manager
->bus
;
219 list_for_each_entry(slave
, &bus
->slaves
, node
) {
220 ctx
->peripherals
->array
[i
] = slave
;
227 EXPORT_SYMBOL_NS(sdw_amd_get_slave_info
, "SOUNDWIRE_AMD_INIT");
229 MODULE_AUTHOR("Vijendar.Mukunda@amd.com");
230 MODULE_DESCRIPTION("AMD SoundWire Init Library");
231 MODULE_LICENSE("Dual BSD/GPL");