|
| 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"); |
0 commit comments