xref: /linux/drivers/cdx/controller/cdx_controller.c (revision eb01fe7abbe2d0b38824d2a93fdb4cc3eaf2ccc1)
1 // SPDX-License-Identifier: GPL-2.0
2 /*
3  * CDX host controller driver for AMD versal-net platform.
4  *
5  * Copyright (C) 2022-2023, Advanced Micro Devices, Inc.
6  */
7 
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 
13 #include "cdx_controller.h"
14 #include "../cdx.h"
15 #include "mcdi_functions.h"
16 #include "mcdi.h"
17 
18 static unsigned int cdx_mcdi_rpc_timeout(struct cdx_mcdi *cdx, unsigned int cmd)
19 {
20 	return MCDI_RPC_TIMEOUT;
21 }
22 
23 static void cdx_mcdi_request(struct cdx_mcdi *cdx,
24 			     const struct cdx_dword *hdr, size_t hdr_len,
25 			     const struct cdx_dword *sdu, size_t sdu_len)
26 {
27 	if (cdx_rpmsg_send(cdx, hdr, hdr_len, sdu, sdu_len))
28 		dev_err(&cdx->rpdev->dev, "Failed to send rpmsg data\n");
29 }
30 
31 static const struct cdx_mcdi_ops mcdi_ops = {
32 	.mcdi_rpc_timeout = cdx_mcdi_rpc_timeout,
33 	.mcdi_request = cdx_mcdi_request,
34 };
35 
36 static int cdx_bus_enable(struct cdx_controller *cdx, u8 bus_num)
37 {
38 	return cdx_mcdi_bus_enable(cdx->priv, bus_num);
39 }
40 
41 static int cdx_bus_disable(struct cdx_controller *cdx, u8 bus_num)
42 {
43 	return cdx_mcdi_bus_disable(cdx->priv, bus_num);
44 }
45 
46 void cdx_rpmsg_post_probe(struct cdx_controller *cdx)
47 {
48 	/* Register CDX controller with CDX bus driver */
49 	if (cdx_register_controller(cdx))
50 		dev_err(cdx->dev, "Failed to register CDX controller\n");
51 }
52 
53 void cdx_rpmsg_pre_remove(struct cdx_controller *cdx)
54 {
55 	cdx_unregister_controller(cdx);
56 	cdx_mcdi_wait_for_quiescence(cdx->priv, MCDI_RPC_TIMEOUT);
57 }
58 
59 static int cdx_configure_device(struct cdx_controller *cdx,
60 				u8 bus_num, u8 dev_num,
61 				struct cdx_device_config *dev_config)
62 {
63 	int ret = 0;
64 
65 	switch (dev_config->type) {
66 	case CDX_DEV_RESET_CONF:
67 		ret = cdx_mcdi_reset_device(cdx->priv, bus_num, dev_num);
68 		break;
69 	case CDX_DEV_BUS_MASTER_CONF:
70 		ret = cdx_mcdi_bus_master_enable(cdx->priv, bus_num, dev_num,
71 						 dev_config->bus_master_enable);
72 		break;
73 	default:
74 		ret = -EINVAL;
75 	}
76 
77 	return ret;
78 }
79 
80 static int cdx_scan_devices(struct cdx_controller *cdx)
81 {
82 	struct cdx_mcdi *cdx_mcdi = cdx->priv;
83 	u8 bus_num, dev_num, num_cdx_bus;
84 	int ret;
85 
86 	/* MCDI FW Read: Fetch the number of CDX buses on this controller */
87 	ret = cdx_mcdi_get_num_buses(cdx_mcdi);
88 	if (ret < 0) {
89 		dev_err(cdx->dev,
90 			"Get number of CDX buses failed: %d\n", ret);
91 		return ret;
92 	}
93 	num_cdx_bus = (u8)ret;
94 
95 	for (bus_num = 0; bus_num < num_cdx_bus; bus_num++) {
96 		struct device *bus_dev;
97 		u8 num_cdx_dev;
98 
99 		/* Add the bus on cdx subsystem */
100 		bus_dev = cdx_bus_add(cdx, bus_num);
101 		if (!bus_dev)
102 			continue;
103 
104 		/* MCDI FW Read: Fetch the number of devices present */
105 		ret = cdx_mcdi_get_num_devs(cdx_mcdi, bus_num);
106 		if (ret < 0) {
107 			dev_err(cdx->dev,
108 				"Get devices on CDX bus %d failed: %d\n", bus_num, ret);
109 			continue;
110 		}
111 		num_cdx_dev = (u8)ret;
112 
113 		for (dev_num = 0; dev_num < num_cdx_dev; dev_num++) {
114 			struct cdx_dev_params dev_params;
115 
116 			/* MCDI FW: Get the device config */
117 			ret = cdx_mcdi_get_dev_config(cdx_mcdi, bus_num,
118 						      dev_num, &dev_params);
119 			if (ret) {
120 				dev_err(cdx->dev,
121 					"CDX device config get failed for %d(bus):%d(dev), %d\n",
122 					bus_num, dev_num, ret);
123 				continue;
124 			}
125 			dev_params.cdx = cdx;
126 			dev_params.parent = bus_dev;
127 
128 			/* Add the device to the cdx bus */
129 			ret = cdx_device_add(&dev_params);
130 			if (ret) {
131 				dev_err(cdx->dev, "registering cdx dev: %d failed: %d\n",
132 					dev_num, ret);
133 				continue;
134 			}
135 
136 			dev_dbg(cdx->dev, "CDX dev: %d on cdx bus: %d created\n",
137 				dev_num, bus_num);
138 		}
139 	}
140 
141 	return 0;
142 }
143 
144 static struct cdx_ops cdx_ops = {
145 	.bus_enable		= cdx_bus_enable,
146 	.bus_disable	= cdx_bus_disable,
147 	.scan		= cdx_scan_devices,
148 	.dev_configure	= cdx_configure_device,
149 };
150 
151 static int xlnx_cdx_probe(struct platform_device *pdev)
152 {
153 	struct cdx_controller *cdx;
154 	struct cdx_mcdi *cdx_mcdi;
155 	int ret;
156 
157 	cdx_mcdi = kzalloc(sizeof(*cdx_mcdi), GFP_KERNEL);
158 	if (!cdx_mcdi)
159 		return -ENOMEM;
160 
161 	/* Store the MCDI ops */
162 	cdx_mcdi->mcdi_ops = &mcdi_ops;
163 	/* MCDI FW: Initialize the FW path */
164 	ret = cdx_mcdi_init(cdx_mcdi);
165 	if (ret) {
166 		dev_err_probe(&pdev->dev, ret, "MCDI Initialization failed\n");
167 		goto mcdi_init_fail;
168 	}
169 
170 	cdx = kzalloc(sizeof(*cdx), GFP_KERNEL);
171 	if (!cdx) {
172 		ret = -ENOMEM;
173 		goto cdx_alloc_fail;
174 	}
175 	platform_set_drvdata(pdev, cdx);
176 
177 	cdx->dev = &pdev->dev;
178 	cdx->priv = cdx_mcdi;
179 	cdx->ops = &cdx_ops;
180 
181 	ret = cdx_setup_rpmsg(pdev);
182 	if (ret) {
183 		if (ret != -EPROBE_DEFER)
184 			dev_err(&pdev->dev, "Failed to register CDX RPMsg transport\n");
185 		goto cdx_rpmsg_fail;
186 	}
187 
188 	dev_info(&pdev->dev, "Successfully registered CDX controller with RPMsg as transport\n");
189 	return 0;
190 
191 cdx_rpmsg_fail:
192 	kfree(cdx);
193 cdx_alloc_fail:
194 	cdx_mcdi_finish(cdx_mcdi);
195 mcdi_init_fail:
196 	kfree(cdx_mcdi);
197 
198 	return ret;
199 }
200 
201 static int xlnx_cdx_remove(struct platform_device *pdev)
202 {
203 	struct cdx_controller *cdx = platform_get_drvdata(pdev);
204 	struct cdx_mcdi *cdx_mcdi = cdx->priv;
205 
206 	cdx_destroy_rpmsg(pdev);
207 
208 	kfree(cdx);
209 
210 	cdx_mcdi_finish(cdx_mcdi);
211 	kfree(cdx_mcdi);
212 
213 	return 0;
214 }
215 
216 static const struct of_device_id cdx_match_table[] = {
217 	{.compatible = "xlnx,versal-net-cdx",},
218 	{ },
219 };
220 
221 MODULE_DEVICE_TABLE(of, cdx_match_table);
222 
223 static struct platform_driver cdx_pdriver = {
224 	.driver = {
225 		   .name = "cdx-controller",
226 		   .pm = NULL,
227 		   .of_match_table = cdx_match_table,
228 		   },
229 	.probe = xlnx_cdx_probe,
230 	.remove = xlnx_cdx_remove,
231 };
232 
233 static int __init cdx_controller_init(void)
234 {
235 	int ret;
236 
237 	ret = platform_driver_register(&cdx_pdriver);
238 	if (ret)
239 		pr_err("platform_driver_register() failed: %d\n", ret);
240 
241 	return ret;
242 }
243 
244 static void __exit cdx_controller_exit(void)
245 {
246 	platform_driver_unregister(&cdx_pdriver);
247 }
248 
249 module_init(cdx_controller_init);
250 module_exit(cdx_controller_exit);
251 
252 MODULE_AUTHOR("AMD Inc.");
253 MODULE_DESCRIPTION("CDX controller for AMD devices");
254 MODULE_LICENSE("GPL");
255 MODULE_IMPORT_NS(CDX_BUS_CONTROLLER);
256