GNU Linux-libre 6.7.9-gnu
[releases.git] / drivers / cdx / controller / cdx_controller.c
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);