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/platform_device.h> 9 #include <linux/slab.h> 10 #include <linux/cdx/cdx_bus.h> 11 #include <linux/irqdomain.h> 12 13 #include "cdx_controller.h" 14 #include "../cdx.h" 15 #include "mcdi_functions.h" 16 #include "mcdid.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 u16 msi_index; 64 int ret = 0; 65 u32 data; 66 u64 addr; 67 68 switch (dev_config->type) { 69 case CDX_DEV_MSI_CONF: 70 msi_index = dev_config->msi.msi_index; 71 data = dev_config->msi.data; 72 addr = dev_config->msi.addr; 73 74 ret = cdx_mcdi_write_msi(cdx->priv, bus_num, dev_num, msi_index, addr, data); 75 break; 76 case CDX_DEV_RESET_CONF: 77 ret = cdx_mcdi_reset_device(cdx->priv, bus_num, dev_num); 78 break; 79 case CDX_DEV_BUS_MASTER_CONF: 80 ret = cdx_mcdi_bus_master_enable(cdx->priv, bus_num, dev_num, 81 dev_config->bus_master_enable); 82 break; 83 case CDX_DEV_MSI_ENABLE: 84 ret = cdx_mcdi_msi_enable(cdx->priv, bus_num, dev_num, dev_config->msi_enable); 85 break; 86 default: 87 ret = -EINVAL; 88 } 89 90 return ret; 91 } 92 93 static int cdx_scan_devices(struct cdx_controller *cdx) 94 { 95 struct cdx_mcdi *cdx_mcdi = cdx->priv; 96 u8 bus_num, dev_num, num_cdx_bus; 97 int ret; 98 99 /* MCDI FW Read: Fetch the number of CDX buses on this controller */ 100 ret = cdx_mcdi_get_num_buses(cdx_mcdi); 101 if (ret < 0) { 102 dev_err(cdx->dev, 103 "Get number of CDX buses failed: %d\n", ret); 104 return ret; 105 } 106 num_cdx_bus = (u8)ret; 107 108 for (bus_num = 0; bus_num < num_cdx_bus; bus_num++) { 109 struct device *bus_dev; 110 u8 num_cdx_dev; 111 112 /* Add the bus on cdx subsystem */ 113 bus_dev = cdx_bus_add(cdx, bus_num); 114 if (!bus_dev) 115 continue; 116 117 /* MCDI FW Read: Fetch the number of devices present */ 118 ret = cdx_mcdi_get_num_devs(cdx_mcdi, bus_num); 119 if (ret < 0) { 120 dev_err(cdx->dev, 121 "Get devices on CDX bus %d failed: %d\n", bus_num, ret); 122 continue; 123 } 124 num_cdx_dev = (u8)ret; 125 126 for (dev_num = 0; dev_num < num_cdx_dev; dev_num++) { 127 struct cdx_dev_params dev_params; 128 129 /* MCDI FW: Get the device config */ 130 ret = cdx_mcdi_get_dev_config(cdx_mcdi, bus_num, 131 dev_num, &dev_params); 132 if (ret) { 133 dev_err(cdx->dev, 134 "CDX device config get failed for %d(bus):%d(dev), %d\n", 135 bus_num, dev_num, ret); 136 continue; 137 } 138 dev_params.cdx = cdx; 139 dev_params.parent = bus_dev; 140 141 /* Add the device to the cdx bus */ 142 ret = cdx_device_add(&dev_params); 143 if (ret) { 144 dev_err(cdx->dev, "registering cdx dev: %d failed: %d\n", 145 dev_num, ret); 146 continue; 147 } 148 149 dev_dbg(cdx->dev, "CDX dev: %d on cdx bus: %d created\n", 150 dev_num, bus_num); 151 } 152 } 153 154 return 0; 155 } 156 157 static struct cdx_ops cdx_ops = { 158 .bus_enable = cdx_bus_enable, 159 .bus_disable = cdx_bus_disable, 160 .scan = cdx_scan_devices, 161 .dev_configure = cdx_configure_device, 162 }; 163 164 static int xlnx_cdx_probe(struct platform_device *pdev) 165 { 166 struct cdx_controller *cdx; 167 struct cdx_mcdi *cdx_mcdi; 168 int ret; 169 170 cdx_mcdi = kzalloc_obj(*cdx_mcdi); 171 if (!cdx_mcdi) 172 return -ENOMEM; 173 174 /* Store the MCDI ops */ 175 cdx_mcdi->mcdi_ops = &mcdi_ops; 176 /* MCDI FW: Initialize the FW path */ 177 ret = cdx_mcdi_init(cdx_mcdi); 178 if (ret) { 179 dev_err_probe(&pdev->dev, ret, "MCDI Initialization failed\n"); 180 goto mcdi_init_fail; 181 } 182 183 cdx = kzalloc_obj(*cdx); 184 if (!cdx) { 185 ret = -ENOMEM; 186 goto cdx_alloc_fail; 187 } 188 platform_set_drvdata(pdev, cdx); 189 190 cdx->dev = &pdev->dev; 191 cdx->priv = cdx_mcdi; 192 cdx->ops = &cdx_ops; 193 194 /* Create MSI domain */ 195 if (IS_ENABLED(CONFIG_GENERIC_MSI_IRQ)) 196 cdx->msi_domain = cdx_msi_domain_init(&pdev->dev); 197 if (!cdx->msi_domain) { 198 ret = dev_err_probe(&pdev->dev, -ENODEV, "cdx_msi_domain_init() failed"); 199 goto cdx_msi_fail; 200 } 201 202 ret = cdx_setup_rpmsg(pdev); 203 if (ret) { 204 dev_err_probe(&pdev->dev, ret, "Failed to register CDX RPMsg transport\n"); 205 goto cdx_rpmsg_fail; 206 } 207 208 return 0; 209 210 cdx_rpmsg_fail: 211 irq_domain_remove(cdx->msi_domain); 212 cdx_msi_fail: 213 kfree(cdx); 214 cdx_alloc_fail: 215 cdx_mcdi_finish(cdx_mcdi); 216 mcdi_init_fail: 217 kfree(cdx_mcdi); 218 219 return ret; 220 } 221 222 static void xlnx_cdx_remove(struct platform_device *pdev) 223 { 224 struct cdx_controller *cdx = platform_get_drvdata(pdev); 225 struct cdx_mcdi *cdx_mcdi = cdx->priv; 226 227 cdx_destroy_rpmsg(pdev); 228 229 irq_domain_remove(cdx->msi_domain); 230 kfree(cdx); 231 232 cdx_mcdi_finish(cdx_mcdi); 233 kfree(cdx_mcdi); 234 } 235 236 static const struct of_device_id cdx_match_table[] = { 237 {.compatible = "xlnx,versal-net-cdx",}, 238 { }, 239 }; 240 241 MODULE_DEVICE_TABLE(of, cdx_match_table); 242 243 static struct platform_driver cdx_pdriver = { 244 .driver = { 245 .name = "cdx-controller", 246 .of_match_table = cdx_match_table, 247 }, 248 .probe = xlnx_cdx_probe, 249 .remove = xlnx_cdx_remove, 250 }; 251 252 module_platform_driver(cdx_pdriver); 253 254 MODULE_AUTHOR("AMD Inc."); 255 MODULE_DESCRIPTION("CDX controller for AMD devices"); 256 MODULE_LICENSE("GPL"); 257 MODULE_IMPORT_NS("CDX_BUS_CONTROLLER"); 258