1 // SPDX-License-Identifier: GPL-2.0 2 /* Copyright(c) Advanced Micro Devices, Inc */ 3 4 #include <linux/module.h> 5 #include <linux/auxiliary_bus.h> 6 #include <linux/pci.h> 7 #include <linux/vmalloc.h> 8 9 #include <uapi/fwctl/fwctl.h> 10 #include <uapi/fwctl/pds.h> 11 #include <linux/fwctl.h> 12 13 #include <linux/pds/pds_common.h> 14 #include <linux/pds/pds_core_if.h> 15 #include <linux/pds/pds_adminq.h> 16 #include <linux/pds/pds_auxbus.h> 17 18 struct pdsfc_uctx { 19 struct fwctl_uctx uctx; 20 u32 uctx_caps; 21 }; 22 23 struct pdsfc_dev { 24 struct fwctl_device fwctl; 25 struct pds_auxiliary_dev *padev; 26 u32 caps; 27 struct pds_fwctl_ident ident; 28 }; 29 30 static int pdsfc_open_uctx(struct fwctl_uctx *uctx) 31 { 32 struct pdsfc_dev *pdsfc = container_of(uctx->fwctl, struct pdsfc_dev, fwctl); 33 struct pdsfc_uctx *pdsfc_uctx = container_of(uctx, struct pdsfc_uctx, uctx); 34 35 pdsfc_uctx->uctx_caps = pdsfc->caps; 36 37 return 0; 38 } 39 40 static void pdsfc_close_uctx(struct fwctl_uctx *uctx) 41 { 42 } 43 44 static void *pdsfc_info(struct fwctl_uctx *uctx, size_t *length) 45 { 46 struct pdsfc_uctx *pdsfc_uctx = container_of(uctx, struct pdsfc_uctx, uctx); 47 struct fwctl_info_pds *info; 48 49 info = kzalloc(sizeof(*info), GFP_KERNEL); 50 if (!info) 51 return ERR_PTR(-ENOMEM); 52 53 info->uctx_caps = pdsfc_uctx->uctx_caps; 54 55 return info; 56 } 57 58 static int pdsfc_identify(struct pdsfc_dev *pdsfc) 59 { 60 struct device *dev = &pdsfc->fwctl.dev; 61 union pds_core_adminq_comp comp = {0}; 62 union pds_core_adminq_cmd cmd; 63 struct pds_fwctl_ident *ident; 64 dma_addr_t ident_pa; 65 int err; 66 67 ident = dma_alloc_coherent(dev->parent, sizeof(*ident), &ident_pa, GFP_KERNEL); 68 if (!ident) { 69 dev_err(dev, "Failed to map ident buffer\n"); 70 return -ENOMEM; 71 } 72 73 cmd = (union pds_core_adminq_cmd) { 74 .fwctl_ident = { 75 .opcode = PDS_FWCTL_CMD_IDENT, 76 .version = 0, 77 .len = cpu_to_le32(sizeof(*ident)), 78 .ident_pa = cpu_to_le64(ident_pa), 79 } 80 }; 81 82 err = pds_client_adminq_cmd(pdsfc->padev, &cmd, sizeof(cmd), &comp, 0); 83 if (err) 84 dev_err(dev, "Failed to send adminq cmd opcode: %u err: %d\n", 85 cmd.fwctl_ident.opcode, err); 86 else 87 pdsfc->ident = *ident; 88 89 dma_free_coherent(dev->parent, sizeof(*ident), ident, ident_pa); 90 91 return err; 92 } 93 94 static void *pdsfc_fw_rpc(struct fwctl_uctx *uctx, enum fwctl_rpc_scope scope, 95 void *in, size_t in_len, size_t *out_len) 96 { 97 return NULL; 98 } 99 100 static const struct fwctl_ops pdsfc_ops = { 101 .device_type = FWCTL_DEVICE_TYPE_PDS, 102 .uctx_size = sizeof(struct pdsfc_uctx), 103 .open_uctx = pdsfc_open_uctx, 104 .close_uctx = pdsfc_close_uctx, 105 .info = pdsfc_info, 106 .fw_rpc = pdsfc_fw_rpc, 107 }; 108 109 static int pdsfc_probe(struct auxiliary_device *adev, 110 const struct auxiliary_device_id *id) 111 { 112 struct pds_auxiliary_dev *padev = 113 container_of(adev, struct pds_auxiliary_dev, aux_dev); 114 struct device *dev = &adev->dev; 115 struct pdsfc_dev *pdsfc; 116 int err; 117 118 pdsfc = fwctl_alloc_device(&padev->vf_pdev->dev, &pdsfc_ops, 119 struct pdsfc_dev, fwctl); 120 if (!pdsfc) 121 return dev_err_probe(dev, -ENOMEM, "Failed to allocate fwctl device struct\n"); 122 pdsfc->padev = padev; 123 124 err = pdsfc_identify(pdsfc); 125 if (err) { 126 fwctl_put(&pdsfc->fwctl); 127 return dev_err_probe(dev, err, "Failed to identify device\n"); 128 } 129 130 err = fwctl_register(&pdsfc->fwctl); 131 if (err) { 132 fwctl_put(&pdsfc->fwctl); 133 return dev_err_probe(dev, err, "Failed to register device\n"); 134 } 135 136 auxiliary_set_drvdata(adev, pdsfc); 137 138 return 0; 139 } 140 141 static void pdsfc_remove(struct auxiliary_device *adev) 142 { 143 struct pdsfc_dev *pdsfc = auxiliary_get_drvdata(adev); 144 145 fwctl_unregister(&pdsfc->fwctl); 146 fwctl_put(&pdsfc->fwctl); 147 } 148 149 static const struct auxiliary_device_id pdsfc_id_table[] = { 150 {.name = PDS_CORE_DRV_NAME "." PDS_DEV_TYPE_FWCTL_STR }, 151 {} 152 }; 153 MODULE_DEVICE_TABLE(auxiliary, pdsfc_id_table); 154 155 static struct auxiliary_driver pdsfc_driver = { 156 .name = "pds_fwctl", 157 .probe = pdsfc_probe, 158 .remove = pdsfc_remove, 159 .id_table = pdsfc_id_table, 160 }; 161 162 module_auxiliary_driver(pdsfc_driver); 163 164 MODULE_IMPORT_NS("FWCTL"); 165 MODULE_DESCRIPTION("pds fwctl driver"); 166 MODULE_AUTHOR("Shannon Nelson <shannon.nelson@amd.com>"); 167 MODULE_AUTHOR("Brett Creeley <brett.creeley@amd.com>"); 168 MODULE_LICENSE("GPL"); 169