Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

fpga: dfl: add basic support for DFHv1

Version 1 of the Device Feature Header (DFH) definition adds
functionality to the Device Feature List (DFL) bus.

A DFHv1 header may have one or more parameter blocks that
further describes the HW to SW. Add support to the DFL bus
to parse the MSI-X parameter.

The location of a feature's register set is explicitly
described in DFHv1 and can be relative to the base of the DFHv1
or an absolute address. Parse the location and pass the information
to DFL driver.

Signed-off-by: Matthew Gerlach <matthew.gerlach@linux.intel.com>
Reviewed-by: Ilpo Järvinen <ilpo.jarvinen@linux.intel.com>
Link: https://lore.kernel.org/r/20230115151447.1353428-4-matthew.gerlach@linux.intel.com
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>

authored by

Matthew Gerlach and committed by
Greg Kroah-Hartman
4747ab89 0926d8d5

+213 -51
+194 -51
drivers/fpga/dfl.c
··· 13 13 #include <linux/dfl.h> 14 14 #include <linux/fpga-dfl.h> 15 15 #include <linux/module.h> 16 + #include <linux/overflow.h> 16 17 #include <linux/uaccess.h> 17 18 18 19 #include "dfl.h" ··· 343 342 if (ddev->mmio_res.parent) 344 343 release_resource(&ddev->mmio_res); 345 344 345 + kfree(ddev->params); 346 + 346 347 ida_free(&dfl_device_ida, ddev->id); 347 348 kfree(ddev->irqs); 348 349 kfree(ddev); ··· 383 380 ddev->type = feature_dev_id_type(pdev); 384 381 ddev->feature_id = feature->id; 385 382 ddev->revision = feature->revision; 383 + ddev->dfh_version = feature->dfh_version; 386 384 ddev->cdev = pdata->dfl_cdev; 385 + if (feature->param_size) { 386 + ddev->params = kmemdup(feature->params, feature->param_size, GFP_KERNEL); 387 + if (!ddev->params) { 388 + ret = -ENOMEM; 389 + goto put_dev; 390 + } 391 + ddev->param_size = feature->param_size; 392 + } 387 393 388 394 /* add mmio resource */ 389 395 parent_res = &pdev->resource[feature->resource_index]; ··· 720 708 * struct dfl_feature_info - sub feature info collected during feature dev build 721 709 * 722 710 * @fid: id of this sub feature. 711 + * @revision: revision of this sub feature 712 + * @dfh_version: version of Device Feature Header (DFH) 723 713 * @mmio_res: mmio resource of this sub feature. 724 714 * @ioaddr: mapped base address of mmio resource. 725 715 * @node: node in sub_features linked list. 726 716 * @irq_base: start of irq index in this sub feature. 727 717 * @nr_irqs: number of irqs of this sub feature. 718 + * @param_size: size DFH parameters. 719 + * @params: DFH parameter data. 728 720 */ 729 721 struct dfl_feature_info { 730 722 u16 fid; 731 723 u8 revision; 724 + u8 dfh_version; 732 725 struct resource mmio_res; 733 726 void __iomem *ioaddr; 734 727 struct list_head node; 735 728 unsigned int irq_base; 736 729 unsigned int nr_irqs; 730 + unsigned int param_size; 731 + u64 params[]; 737 732 }; 738 733 739 734 static void dfl_fpga_cdev_add_port_dev(struct dfl_fpga_cdev *cdev, ··· 816 797 feature->dev = fdev; 817 798 feature->id = finfo->fid; 818 799 feature->revision = finfo->revision; 800 + feature->dfh_version = finfo->dfh_version; 819 801 802 + if (finfo->param_size) { 803 + feature->params = devm_kmemdup(binfo->dev, 804 + finfo->params, finfo->param_size, 805 + GFP_KERNEL); 806 + if (!feature->params) 807 + return -ENOMEM; 808 + 809 + feature->param_size = finfo->param_size; 810 + } 820 811 /* 821 812 * the FIU header feature has some fundamental functions (sriov 822 813 * set, port enable/disable) needed for the dfl bus device and ··· 963 934 return 0; 964 935 } 965 936 937 + static u64 *find_param(u64 *params, resource_size_t max, int param_id) 938 + { 939 + u64 *end = params + max / sizeof(u64); 940 + u64 v, next; 941 + 942 + while (params < end) { 943 + v = *params; 944 + if (param_id == FIELD_GET(DFHv1_PARAM_HDR_ID, v)) 945 + return params; 946 + 947 + if (FIELD_GET(DFHv1_PARAM_HDR_NEXT_EOP, v)) 948 + break; 949 + 950 + next = FIELD_GET(DFHv1_PARAM_HDR_NEXT_OFFSET, v); 951 + params += next; 952 + } 953 + 954 + return NULL; 955 + } 956 + 957 + /** 958 + * dfh_find_param() - find parameter block for the given parameter id 959 + * @dfl_dev: dfl device 960 + * @param_id: id of dfl parameter 961 + * @psize: destination to store size of parameter data in bytes 962 + * 963 + * Return: pointer to start of parameter data, PTR_ERR otherwise. 964 + */ 965 + void *dfh_find_param(struct dfl_device *dfl_dev, int param_id, size_t *psize) 966 + { 967 + u64 *phdr = find_param(dfl_dev->params, dfl_dev->param_size, param_id); 968 + 969 + if (!phdr) 970 + return ERR_PTR(-ENOENT); 971 + 972 + if (psize) 973 + *psize = (FIELD_GET(DFHv1_PARAM_HDR_NEXT_OFFSET, *phdr) - 1) * sizeof(u64); 974 + 975 + return phdr + 1; 976 + } 977 + EXPORT_SYMBOL_GPL(dfh_find_param); 978 + 966 979 static int parse_feature_irqs(struct build_feature_devs_info *binfo, 967 - resource_size_t ofst, u16 fid, 968 - unsigned int *irq_base, unsigned int *nr_irqs) 980 + resource_size_t ofst, struct dfl_feature_info *finfo) 969 981 { 970 982 void __iomem *base = binfo->ioaddr + ofst; 971 983 unsigned int i, ibase, inr = 0; 984 + void *params = finfo->params; 972 985 enum dfl_id_type type; 986 + u16 fid = finfo->fid; 973 987 int virq; 988 + u64 *p; 974 989 u64 v; 975 990 976 - type = feature_dev_id_type(binfo->feature_dev); 991 + switch (finfo->dfh_version) { 992 + case 0: 993 + /* 994 + * DFHv0 only provides MMIO resource information for each feature 995 + * in the DFL header. There is no generic interrupt information. 996 + * Instead, features with interrupt functionality provide 997 + * the information in feature specific registers. 998 + */ 999 + type = feature_dev_id_type(binfo->feature_dev); 1000 + if (type == PORT_ID) { 1001 + switch (fid) { 1002 + case PORT_FEATURE_ID_UINT: 1003 + v = readq(base + PORT_UINT_CAP); 1004 + ibase = FIELD_GET(PORT_UINT_CAP_FST_VECT, v); 1005 + inr = FIELD_GET(PORT_UINT_CAP_INT_NUM, v); 1006 + break; 1007 + case PORT_FEATURE_ID_ERROR: 1008 + v = readq(base + PORT_ERROR_CAP); 1009 + ibase = FIELD_GET(PORT_ERROR_CAP_INT_VECT, v); 1010 + inr = FIELD_GET(PORT_ERROR_CAP_SUPP_INT, v); 1011 + break; 1012 + } 1013 + } else if (type == FME_ID) { 1014 + switch (fid) { 1015 + case FME_FEATURE_ID_GLOBAL_ERR: 1016 + v = readq(base + FME_ERROR_CAP); 1017 + ibase = FIELD_GET(FME_ERROR_CAP_INT_VECT, v); 1018 + inr = FIELD_GET(FME_ERROR_CAP_SUPP_INT, v); 1019 + break; 1020 + } 1021 + } 1022 + break; 977 1023 978 - /* 979 - * Ideally DFL framework should only read info from DFL header, but 980 - * current version DFL only provides mmio resources information for 981 - * each feature in DFL Header, no field for interrupt resources. 982 - * Interrupt resource information is provided by specific mmio 983 - * registers of each private feature which supports interrupt. So in 984 - * order to parse and assign irq resources, DFL framework has to look 985 - * into specific capability registers of these private features. 986 - * 987 - * Once future DFL version supports generic interrupt resource 988 - * information in common DFL headers, the generic interrupt parsing 989 - * code will be added. But in order to be compatible to old version 990 - * DFL, the driver may still fall back to these quirks. 991 - */ 992 - if (type == PORT_ID) { 993 - switch (fid) { 994 - case PORT_FEATURE_ID_UINT: 995 - v = readq(base + PORT_UINT_CAP); 996 - ibase = FIELD_GET(PORT_UINT_CAP_FST_VECT, v); 997 - inr = FIELD_GET(PORT_UINT_CAP_INT_NUM, v); 1024 + case 1: 1025 + /* 1026 + * DFHv1 provides interrupt resource information in DFHv1 1027 + * parameter blocks. 1028 + */ 1029 + p = find_param(params, finfo->param_size, DFHv1_PARAM_ID_MSI_X); 1030 + if (!p) 998 1031 break; 999 - case PORT_FEATURE_ID_ERROR: 1000 - v = readq(base + PORT_ERROR_CAP); 1001 - ibase = FIELD_GET(PORT_ERROR_CAP_INT_VECT, v); 1002 - inr = FIELD_GET(PORT_ERROR_CAP_SUPP_INT, v); 1003 - break; 1004 - } 1005 - } else if (type == FME_ID) { 1006 - if (fid == FME_FEATURE_ID_GLOBAL_ERR) { 1007 - v = readq(base + FME_ERROR_CAP); 1008 - ibase = FIELD_GET(FME_ERROR_CAP_INT_VECT, v); 1009 - inr = FIELD_GET(FME_ERROR_CAP_SUPP_INT, v); 1010 - } 1032 + 1033 + p++; 1034 + ibase = FIELD_GET(DFHv1_PARAM_MSI_X_STARTV, *p); 1035 + inr = FIELD_GET(DFHv1_PARAM_MSI_X_NUMV, *p); 1036 + break; 1037 + 1038 + default: 1039 + dev_warn(binfo->dev, "unexpected DFH version %d\n", finfo->dfh_version); 1040 + break; 1011 1041 } 1012 1042 1013 1043 if (!inr) { 1014 - *irq_base = 0; 1015 - *nr_irqs = 0; 1044 + finfo->irq_base = 0; 1045 + finfo->nr_irqs = 0; 1016 1046 return 0; 1017 1047 } 1018 1048 ··· 1094 1006 } 1095 1007 } 1096 1008 1097 - *irq_base = ibase; 1098 - *nr_irqs = inr; 1009 + finfo->irq_base = ibase; 1010 + finfo->nr_irqs = inr; 1099 1011 1100 1012 return 0; 1013 + } 1014 + 1015 + static int dfh_get_param_size(void __iomem *dfh_base, resource_size_t max) 1016 + { 1017 + int size = 0; 1018 + u64 v, next; 1019 + 1020 + if (!FIELD_GET(DFHv1_CSR_SIZE_GRP_HAS_PARAMS, 1021 + readq(dfh_base + DFHv1_CSR_SIZE_GRP))) 1022 + return 0; 1023 + 1024 + while (size + DFHv1_PARAM_HDR < max) { 1025 + v = readq(dfh_base + DFHv1_PARAM_HDR + size); 1026 + 1027 + next = FIELD_GET(DFHv1_PARAM_HDR_NEXT_OFFSET, v); 1028 + if (!next) 1029 + return -EINVAL; 1030 + 1031 + size += next * sizeof(u64); 1032 + 1033 + if (FIELD_GET(DFHv1_PARAM_HDR_NEXT_EOP, v)) 1034 + return size; 1035 + } 1036 + 1037 + return -ENOENT; 1101 1038 } 1102 1039 1103 1040 /* ··· 1136 1023 create_feature_instance(struct build_feature_devs_info *binfo, 1137 1024 resource_size_t ofst, resource_size_t size, u16 fid) 1138 1025 { 1139 - unsigned int irq_base, nr_irqs; 1140 1026 struct dfl_feature_info *finfo; 1027 + resource_size_t start, end; 1028 + int dfh_psize = 0; 1141 1029 u8 revision = 0; 1030 + u64 v, addr_off; 1031 + u8 dfh_ver = 0; 1142 1032 int ret; 1143 - u64 v; 1144 1033 1145 1034 if (fid != FEATURE_ID_AFU) { 1146 1035 v = readq(binfo->ioaddr + ofst); 1147 1036 revision = FIELD_GET(DFH_REVISION, v); 1148 - 1037 + dfh_ver = FIELD_GET(DFH_VERSION, v); 1149 1038 /* read feature size and id if inputs are invalid */ 1150 1039 size = size ? size : feature_size(v); 1151 1040 fid = fid ? fid : feature_id(v); 1041 + if (dfh_ver == 1) { 1042 + dfh_psize = dfh_get_param_size(binfo->ioaddr + ofst, size); 1043 + if (dfh_psize < 0) { 1044 + dev_err(binfo->dev, 1045 + "failed to read size of DFHv1 parameters %d\n", 1046 + dfh_psize); 1047 + return dfh_psize; 1048 + } 1049 + dev_dbg(binfo->dev, "dfhv1_psize %d\n", dfh_psize); 1050 + } 1152 1051 } 1153 1052 1154 1053 if (binfo->len - ofst < size) 1155 1054 return -EINVAL; 1156 1055 1157 - ret = parse_feature_irqs(binfo, ofst, fid, &irq_base, &nr_irqs); 1158 - if (ret) 1159 - return ret; 1160 - 1161 - finfo = kzalloc(sizeof(*finfo), GFP_KERNEL); 1056 + finfo = kzalloc(struct_size(finfo, params, dfh_psize / sizeof(u64)), GFP_KERNEL); 1162 1057 if (!finfo) 1163 1058 return -ENOMEM; 1164 1059 1060 + memcpy_fromio(finfo->params, binfo->ioaddr + ofst + DFHv1_PARAM_HDR, dfh_psize); 1061 + finfo->param_size = dfh_psize; 1062 + 1165 1063 finfo->fid = fid; 1166 1064 finfo->revision = revision; 1167 - finfo->mmio_res.start = binfo->start + ofst; 1168 - finfo->mmio_res.end = finfo->mmio_res.start + size - 1; 1065 + finfo->dfh_version = dfh_ver; 1066 + if (dfh_ver == 1) { 1067 + v = readq(binfo->ioaddr + ofst + DFHv1_CSR_ADDR); 1068 + addr_off = FIELD_GET(DFHv1_CSR_ADDR_MASK, v); 1069 + if (FIELD_GET(DFHv1_CSR_ADDR_REL, v)) 1070 + start = addr_off << 1; 1071 + else 1072 + start = binfo->start + ofst + addr_off; 1073 + 1074 + v = readq(binfo->ioaddr + ofst + DFHv1_CSR_SIZE_GRP); 1075 + end = start + FIELD_GET(DFHv1_CSR_SIZE_GRP_SIZE, v) - 1; 1076 + } else { 1077 + start = binfo->start + ofst; 1078 + end = start + size - 1; 1079 + } 1169 1080 finfo->mmio_res.flags = IORESOURCE_MEM; 1170 - finfo->irq_base = irq_base; 1171 - finfo->nr_irqs = nr_irqs; 1081 + finfo->mmio_res.start = start; 1082 + finfo->mmio_res.end = end; 1083 + 1084 + ret = parse_feature_irqs(binfo, ofst, finfo); 1085 + if (ret) { 1086 + kfree(finfo); 1087 + return ret; 1088 + } 1172 1089 1173 1090 list_add_tail(&finfo->node, &binfo->sub_features); 1174 1091 binfo->feature_num++;
+11
drivers/fpga/dfl.h
··· 111 111 #define DFHv1_PARAM_HDR_NEXT_EOP BIT_ULL(32) 112 112 #define DFHv1_PARAM_DATA 0x08 /* Offset of Param data from Param header */ 113 113 114 + #define DFHv1_PARAM_ID_MSI_X 0x1 115 + #define DFHv1_PARAM_MSI_X_NUMV GENMASK_ULL(63, 32) 116 + #define DFHv1_PARAM_MSI_X_STARTV GENMASK_ULL(31, 0) 117 + 114 118 /* Next AFU Register Bitfield */ 115 119 #define NEXT_AFU_NEXT_DFH_OFST GENMASK_ULL(23, 0) /* Offset to next AFU */ 116 120 ··· 267 263 * 268 264 * @dev: ptr to pdev of the feature device which has the sub feature. 269 265 * @id: sub feature id. 266 + * @revision: revisition of the instance of a feature. 270 267 * @resource_index: each sub feature has one mmio resource for its registers. 271 268 * this index is used to find its mmio resource from the 272 269 * feature dev (platform device)'s resources. ··· 277 272 * @ops: ops of this sub feature. 278 273 * @ddev: ptr to the dfl device of this sub feature. 279 274 * @priv: priv data of this feature. 275 + * @dfh_version: version of the DFH 276 + * @param_size: size of dfh parameters 277 + * @params: point to memory copy of dfh parameters 280 278 */ 281 279 struct dfl_feature { 282 280 struct platform_device *dev; ··· 292 284 const struct dfl_feature_ops *ops; 293 285 struct dfl_device *ddev; 294 286 void *priv; 287 + u8 dfh_version; 288 + unsigned int param_size; 289 + void *params; 295 290 }; 296 291 297 292 #define FEATURE_DEV_ID_UNUSED (-1)
+8
include/linux/dfl.h
··· 27 27 * @id: id of the dfl device. 28 28 * @type: type of DFL FIU of the device. See enum dfl_id_type. 29 29 * @feature_id: feature identifier local to its DFL FIU type. 30 + * @revision: revision of this dfl device feature. 30 31 * @mmio_res: mmio resource of this dfl device. 31 32 * @irqs: list of Linux IRQ numbers of this dfl device. 32 33 * @num_irqs: number of IRQs supported by this dfl device. 33 34 * @cdev: pointer to DFL FPGA container device this dfl device belongs to. 34 35 * @id_entry: matched id entry in dfl driver's id table. 36 + * @dfh_version: version of DFH for the device 37 + * @param_size: size of the block parameters in bytes 38 + * @params: pointer to block of parameters copied memory 35 39 */ 36 40 struct dfl_device { 37 41 struct device dev; ··· 48 44 unsigned int num_irqs; 49 45 struct dfl_fpga_cdev *cdev; 50 46 const struct dfl_device_id *id_entry; 47 + u8 dfh_version; 48 + unsigned int param_size; 49 + void *params; 51 50 }; 52 51 53 52 /** ··· 91 84 module_driver(__dfl_driver, dfl_driver_register, \ 92 85 dfl_driver_unregister) 93 86 87 + void *dfh_find_param(struct dfl_device *dfl_dev, int param_id, size_t *pcount); 94 88 #endif /* __LINUX_DFL_H */