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 void cdx_rpmsg_post_probe(struct cdx_controller *cdx) 37 { 38 /* Register CDX controller with CDX bus driver */ 39 if (cdx_register_controller(cdx)) 40 dev_err(cdx->dev, "Failed to register CDX controller\n"); 41 } 42 43 void cdx_rpmsg_pre_remove(struct cdx_controller *cdx) 44 { 45 cdx_unregister_controller(cdx); 46 cdx_mcdi_wait_for_quiescence(cdx->priv, MCDI_RPC_TIMEOUT); 47 } 48 49 static int cdx_configure_device(struct cdx_controller *cdx, 50 u8 bus_num, u8 dev_num, 51 struct cdx_device_config *dev_config) 52 { 53 int ret = 0; 54 55 switch (dev_config->type) { 56 case CDX_DEV_RESET_CONF: 57 ret = cdx_mcdi_reset_device(cdx->priv, bus_num, dev_num); 58 break; 59 case CDX_DEV_BUS_MASTER_CONF: 60 ret = cdx_mcdi_bus_master_enable(cdx->priv, bus_num, dev_num, 61 dev_config->bus_master_enable); 62 break; 63 default: 64 ret = -EINVAL; 65 } 66 67 return ret; 68 } 69 70 static int cdx_scan_devices(struct cdx_controller *cdx) 71 { 72 struct cdx_mcdi *cdx_mcdi = cdx->priv; 73 u8 bus_num, dev_num, num_cdx_bus; 74 int ret; 75 76 /* MCDI FW Read: Fetch the number of CDX buses on this controller */ 77 ret = cdx_mcdi_get_num_buses(cdx_mcdi); 78 if (ret < 0) { 79 dev_err(cdx->dev, 80 "Get number of CDX buses failed: %d\n", ret); 81 return ret; 82 } 83 num_cdx_bus = (u8)ret; 84 85 for (bus_num = 0; bus_num < num_cdx_bus; bus_num++) { 86 u8 num_cdx_dev; 87 88 /* MCDI FW Read: Fetch the number of devices present */ 89 ret = cdx_mcdi_get_num_devs(cdx_mcdi, bus_num); 90 if (ret < 0) { 91 dev_err(cdx->dev, 92 "Get devices on CDX bus %d failed: %d\n", bus_num, ret); 93 continue; 94 } 95 num_cdx_dev = (u8)ret; 96 97 for (dev_num = 0; dev_num < num_cdx_dev; dev_num++) { 98 struct cdx_dev_params dev_params; 99 100 /* MCDI FW: Get the device config */ 101 ret = cdx_mcdi_get_dev_config(cdx_mcdi, bus_num, 102 dev_num, &dev_params); 103 if (ret) { 104 dev_err(cdx->dev, 105 "CDX device config get failed for %d(bus):%d(dev), %d\n", 106 bus_num, dev_num, ret); 107 continue; 108 } 109 dev_params.cdx = cdx; 110 111 /* Add the device to the cdx bus */ 112 ret = cdx_device_add(&dev_params); 113 if (ret) { 114 dev_err(cdx->dev, "registering cdx dev: %d failed: %d\n", 115 dev_num, ret); 116 continue; 117 } 118 119 dev_dbg(cdx->dev, "CDX dev: %d on cdx bus: %d created\n", 120 dev_num, bus_num); 121 } 122 } 123 124 return 0; 125 } 126 127 static struct cdx_ops cdx_ops = { 128 .scan = cdx_scan_devices, 129 .dev_configure = cdx_configure_device, 130 }; 131 132 static int xlnx_cdx_probe(struct platform_device *pdev) 133 { 134 struct cdx_controller *cdx; 135 struct cdx_mcdi *cdx_mcdi; 136 int ret; 137 138 cdx_mcdi = kzalloc(sizeof(*cdx_mcdi), GFP_KERNEL); 139 if (!cdx_mcdi) 140 return -ENOMEM; 141 142 /* Store the MCDI ops */ 143 cdx_mcdi->mcdi_ops = &mcdi_ops; 144 /* MCDI FW: Initialize the FW path */ 145 ret = cdx_mcdi_init(cdx_mcdi); 146 if (ret) { 147 dev_err_probe(&pdev->dev, ret, "MCDI Initialization failed\n"); 148 goto mcdi_init_fail; 149 } 150 151 cdx = kzalloc(sizeof(*cdx), GFP_KERNEL); 152 if (!cdx) { 153 ret = -ENOMEM; 154 goto cdx_alloc_fail; 155 } 156 platform_set_drvdata(pdev, cdx); 157 158 cdx->dev = &pdev->dev; 159 cdx->priv = cdx_mcdi; 160 cdx->ops = &cdx_ops; 161 162 ret = cdx_setup_rpmsg(pdev); 163 if (ret) { 164 if (ret != -EPROBE_DEFER) 165 dev_err(&pdev->dev, "Failed to register CDX RPMsg transport\n"); 166 goto cdx_rpmsg_fail; 167 } 168 169 dev_info(&pdev->dev, "Successfully registered CDX controller with RPMsg as transport\n"); 170 return 0; 171 172 cdx_rpmsg_fail: 173 kfree(cdx); 174 cdx_alloc_fail: 175 cdx_mcdi_finish(cdx_mcdi); 176 mcdi_init_fail: 177 kfree(cdx_mcdi); 178 179 return ret; 180 } 181 182 static int xlnx_cdx_remove(struct platform_device *pdev) 183 { 184 struct cdx_controller *cdx = platform_get_drvdata(pdev); 185 struct cdx_mcdi *cdx_mcdi = cdx->priv; 186 187 cdx_destroy_rpmsg(pdev); 188 189 kfree(cdx); 190 191 cdx_mcdi_finish(cdx_mcdi); 192 kfree(cdx_mcdi); 193 194 return 0; 195 } 196 197 static const struct of_device_id cdx_match_table[] = { 198 {.compatible = "xlnx,versal-net-cdx",}, 199 { }, 200 }; 201 202 MODULE_DEVICE_TABLE(of, cdx_match_table); 203 204 static struct platform_driver cdx_pdriver = { 205 .driver = { 206 .name = "cdx-controller", 207 .pm = NULL, 208 .of_match_table = cdx_match_table, 209 }, 210 .probe = xlnx_cdx_probe, 211 .remove = xlnx_cdx_remove, 212 }; 213 214 static int __init cdx_controller_init(void) 215 { 216 int ret; 217 218 ret = platform_driver_register(&cdx_pdriver); 219 if (ret) 220 pr_err("platform_driver_register() failed: %d\n", ret); 221 222 return ret; 223 } 224 225 static void __exit cdx_controller_exit(void) 226 { 227 platform_driver_unregister(&cdx_pdriver); 228 } 229 230 module_init(cdx_controller_init); 231 module_exit(cdx_controller_exit); 232 233 MODULE_AUTHOR("AMD Inc."); 234 MODULE_DESCRIPTION("CDX controller for AMD devices"); 235 MODULE_LICENSE("GPL"); 236