1 // SPDX-License-Identifier: GPL-2.0
3 * CDX host controller driver for AMD versal-net platform.
5 * Copyright (C) 2022-2023, Advanced Micro Devices, Inc.
8 #include <linux/mod_devicetable.h>
9 #include <linux/platform_device.h>
10 #include <linux/slab.h>
11 #include <linux/cdx/cdx_bus.h>
12 #include <linux/irqdomain.h>
14 #include "cdx_controller.h"
16 #include "mcdi_functions.h"
19 static unsigned int cdx_mcdi_rpc_timeout(struct cdx_mcdi
*cdx
, unsigned int cmd
)
21 return MCDI_RPC_TIMEOUT
;
24 static void cdx_mcdi_request(struct cdx_mcdi
*cdx
,
25 const struct cdx_dword
*hdr
, size_t hdr_len
,
26 const struct cdx_dword
*sdu
, size_t sdu_len
)
28 if (cdx_rpmsg_send(cdx
, hdr
, hdr_len
, sdu
, sdu_len
))
29 dev_err(&cdx
->rpdev
->dev
, "Failed to send rpmsg data\n");
32 static const struct cdx_mcdi_ops mcdi_ops
= {
33 .mcdi_rpc_timeout
= cdx_mcdi_rpc_timeout
,
34 .mcdi_request
= cdx_mcdi_request
,
37 static int cdx_bus_enable(struct cdx_controller
*cdx
, u8 bus_num
)
39 return cdx_mcdi_bus_enable(cdx
->priv
, bus_num
);
42 static int cdx_bus_disable(struct cdx_controller
*cdx
, u8 bus_num
)
44 return cdx_mcdi_bus_disable(cdx
->priv
, bus_num
);
47 void cdx_rpmsg_post_probe(struct cdx_controller
*cdx
)
49 /* Register CDX controller with CDX bus driver */
50 if (cdx_register_controller(cdx
))
51 dev_err(cdx
->dev
, "Failed to register CDX controller\n");
54 void cdx_rpmsg_pre_remove(struct cdx_controller
*cdx
)
56 cdx_unregister_controller(cdx
);
57 cdx_mcdi_wait_for_quiescence(cdx
->priv
, MCDI_RPC_TIMEOUT
);
60 static int cdx_configure_device(struct cdx_controller
*cdx
,
61 u8 bus_num
, u8 dev_num
,
62 struct cdx_device_config
*dev_config
)
69 switch (dev_config
->type
) {
70 case CDX_DEV_MSI_CONF
:
71 msi_index
= dev_config
->msi
.msi_index
;
72 data
= dev_config
->msi
.data
;
73 addr
= dev_config
->msi
.addr
;
75 ret
= cdx_mcdi_write_msi(cdx
->priv
, bus_num
, dev_num
, msi_index
, addr
, data
);
77 case CDX_DEV_RESET_CONF
:
78 ret
= cdx_mcdi_reset_device(cdx
->priv
, bus_num
, dev_num
);
80 case CDX_DEV_BUS_MASTER_CONF
:
81 ret
= cdx_mcdi_bus_master_enable(cdx
->priv
, bus_num
, dev_num
,
82 dev_config
->bus_master_enable
);
84 case CDX_DEV_MSI_ENABLE
:
85 ret
= cdx_mcdi_msi_enable(cdx
->priv
, bus_num
, dev_num
, dev_config
->msi_enable
);
94 static int cdx_scan_devices(struct cdx_controller
*cdx
)
96 struct cdx_mcdi
*cdx_mcdi
= cdx
->priv
;
97 u8 bus_num
, dev_num
, num_cdx_bus
;
100 /* MCDI FW Read: Fetch the number of CDX buses on this controller */
101 ret
= cdx_mcdi_get_num_buses(cdx_mcdi
);
104 "Get number of CDX buses failed: %d\n", ret
);
107 num_cdx_bus
= (u8
)ret
;
109 for (bus_num
= 0; bus_num
< num_cdx_bus
; bus_num
++) {
110 struct device
*bus_dev
;
113 /* Add the bus on cdx subsystem */
114 bus_dev
= cdx_bus_add(cdx
, bus_num
);
118 /* MCDI FW Read: Fetch the number of devices present */
119 ret
= cdx_mcdi_get_num_devs(cdx_mcdi
, bus_num
);
122 "Get devices on CDX bus %d failed: %d\n", bus_num
, ret
);
125 num_cdx_dev
= (u8
)ret
;
127 for (dev_num
= 0; dev_num
< num_cdx_dev
; dev_num
++) {
128 struct cdx_dev_params dev_params
;
130 /* MCDI FW: Get the device config */
131 ret
= cdx_mcdi_get_dev_config(cdx_mcdi
, bus_num
,
132 dev_num
, &dev_params
);
135 "CDX device config get failed for %d(bus):%d(dev), %d\n",
136 bus_num
, dev_num
, ret
);
139 dev_params
.cdx
= cdx
;
140 dev_params
.parent
= bus_dev
;
142 /* Add the device to the cdx bus */
143 ret
= cdx_device_add(&dev_params
);
145 dev_err(cdx
->dev
, "registering cdx dev: %d failed: %d\n",
150 dev_dbg(cdx
->dev
, "CDX dev: %d on cdx bus: %d created\n",
158 static struct cdx_ops cdx_ops
= {
159 .bus_enable
= cdx_bus_enable
,
160 .bus_disable
= cdx_bus_disable
,
161 .scan
= cdx_scan_devices
,
162 .dev_configure
= cdx_configure_device
,
165 static int xlnx_cdx_probe(struct platform_device
*pdev
)
167 struct cdx_controller
*cdx
;
168 struct cdx_mcdi
*cdx_mcdi
;
171 cdx_mcdi
= kzalloc(sizeof(*cdx_mcdi
), GFP_KERNEL
);
175 /* Store the MCDI ops */
176 cdx_mcdi
->mcdi_ops
= &mcdi_ops
;
177 /* MCDI FW: Initialize the FW path */
178 ret
= cdx_mcdi_init(cdx_mcdi
);
180 dev_err_probe(&pdev
->dev
, ret
, "MCDI Initialization failed\n");
184 cdx
= kzalloc(sizeof(*cdx
), GFP_KERNEL
);
189 platform_set_drvdata(pdev
, cdx
);
191 cdx
->dev
= &pdev
->dev
;
192 cdx
->priv
= cdx_mcdi
;
195 /* Create MSI domain */
196 cdx
->msi_domain
= cdx_msi_domain_init(&pdev
->dev
);
197 if (!cdx
->msi_domain
) {
198 dev_err(&pdev
->dev
, "cdx_msi_domain_init() failed");
203 ret
= cdx_setup_rpmsg(pdev
);
205 if (ret
!= -EPROBE_DEFER
)
206 dev_err(&pdev
->dev
, "Failed to register CDX RPMsg transport\n");
210 dev_info(&pdev
->dev
, "Successfully registered CDX controller with RPMsg as transport\n");
214 irq_domain_remove(cdx
->msi_domain
);
218 cdx_mcdi_finish(cdx_mcdi
);
225 static void xlnx_cdx_remove(struct platform_device
*pdev
)
227 struct cdx_controller
*cdx
= platform_get_drvdata(pdev
);
228 struct cdx_mcdi
*cdx_mcdi
= cdx
->priv
;
230 cdx_destroy_rpmsg(pdev
);
232 irq_domain_remove(cdx
->msi_domain
);
235 cdx_mcdi_finish(cdx_mcdi
);
239 static const struct of_device_id cdx_match_table
[] = {
240 {.compatible
= "xlnx,versal-net-cdx",},
244 MODULE_DEVICE_TABLE(of
, cdx_match_table
);
246 static struct platform_driver cdx_pdriver
= {
248 .name
= "cdx-controller",
250 .of_match_table
= cdx_match_table
,
252 .probe
= xlnx_cdx_probe
,
253 .remove
= xlnx_cdx_remove
,
256 static int __init
cdx_controller_init(void)
260 ret
= platform_driver_register(&cdx_pdriver
);
262 pr_err("platform_driver_register() failed: %d\n", ret
);
267 static void __exit
cdx_controller_exit(void)
269 platform_driver_unregister(&cdx_pdriver
);
272 module_init(cdx_controller_init
);
273 module_exit(cdx_controller_exit
);
275 MODULE_AUTHOR("AMD Inc.");
276 MODULE_DESCRIPTION("CDX controller for AMD devices");
277 MODULE_LICENSE("GPL");
278 MODULE_IMPORT_NS("CDX_BUS_CONTROLLER");