Skip to content

Commit 4747ab8

Browse files
matthew-gerlachgregkh
authored andcommitted
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 <[email protected]> Reviewed-by: Ilpo Järvinen <[email protected]> Link: https://lore.kernel.org/r/[email protected] Signed-off-by: Greg Kroah-Hartman <[email protected]>
1 parent 0926d8d commit 4747ab8

File tree

3 files changed

+213
-51
lines changed

3 files changed

+213
-51
lines changed

drivers/fpga/dfl.c

Lines changed: 194 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
#include <linux/dfl.h>
1414
#include <linux/fpga-dfl.h>
1515
#include <linux/module.h>
16+
#include <linux/overflow.h>
1617
#include <linux/uaccess.h>
1718

1819
#include "dfl.h"
@@ -342,6 +343,8 @@ static void release_dfl_dev(struct device *dev)
342343
if (ddev->mmio_res.parent)
343344
release_resource(&ddev->mmio_res);
344345

346+
kfree(ddev->params);
347+
345348
ida_free(&dfl_device_ida, ddev->id);
346349
kfree(ddev->irqs);
347350
kfree(ddev);
@@ -380,7 +383,16 @@ dfl_dev_add(struct dfl_feature_platform_data *pdata,
380383
ddev->type = feature_dev_id_type(pdev);
381384
ddev->feature_id = feature->id;
382385
ddev->revision = feature->revision;
386+
ddev->dfh_version = feature->dfh_version;
383387
ddev->cdev = pdata->dfl_cdev;
388+
if (feature->param_size) {
389+
ddev->params = kmemdup(feature->params, feature->param_size, GFP_KERNEL);
390+
if (!ddev->params) {
391+
ret = -ENOMEM;
392+
goto put_dev;
393+
}
394+
ddev->param_size = feature->param_size;
395+
}
384396

385397
/* add mmio resource */
386398
parent_res = &pdev->resource[feature->resource_index];
@@ -708,20 +720,27 @@ struct build_feature_devs_info {
708720
* struct dfl_feature_info - sub feature info collected during feature dev build
709721
*
710722
* @fid: id of this sub feature.
723+
* @revision: revision of this sub feature
724+
* @dfh_version: version of Device Feature Header (DFH)
711725
* @mmio_res: mmio resource of this sub feature.
712726
* @ioaddr: mapped base address of mmio resource.
713727
* @node: node in sub_features linked list.
714728
* @irq_base: start of irq index in this sub feature.
715729
* @nr_irqs: number of irqs of this sub feature.
730+
* @param_size: size DFH parameters.
731+
* @params: DFH parameter data.
716732
*/
717733
struct dfl_feature_info {
718734
u16 fid;
719735
u8 revision;
736+
u8 dfh_version;
720737
struct resource mmio_res;
721738
void __iomem *ioaddr;
722739
struct list_head node;
723740
unsigned int irq_base;
724741
unsigned int nr_irqs;
742+
unsigned int param_size;
743+
u64 params[];
725744
};
726745

727746
static void dfl_fpga_cdev_add_port_dev(struct dfl_fpga_cdev *cdev,
@@ -797,7 +816,17 @@ static int build_info_commit_dev(struct build_feature_devs_info *binfo)
797816
feature->dev = fdev;
798817
feature->id = finfo->fid;
799818
feature->revision = finfo->revision;
819+
feature->dfh_version = finfo->dfh_version;
800820

821+
if (finfo->param_size) {
822+
feature->params = devm_kmemdup(binfo->dev,
823+
finfo->params, finfo->param_size,
824+
GFP_KERNEL);
825+
if (!feature->params)
826+
return -ENOMEM;
827+
828+
feature->param_size = finfo->param_size;
829+
}
801830
/*
802831
* the FIU header feature has some fundamental functions (sriov
803832
* set, port enable/disable) needed for the dfl bus device and
@@ -934,56 +963,115 @@ static u16 feature_id(u64 value)
934963
return 0;
935964
}
936965

966+
static u64 *find_param(u64 *params, resource_size_t max, int param_id)
967+
{
968+
u64 *end = params + max / sizeof(u64);
969+
u64 v, next;
970+
971+
while (params < end) {
972+
v = *params;
973+
if (param_id == FIELD_GET(DFHv1_PARAM_HDR_ID, v))
974+
return params;
975+
976+
if (FIELD_GET(DFHv1_PARAM_HDR_NEXT_EOP, v))
977+
break;
978+
979+
next = FIELD_GET(DFHv1_PARAM_HDR_NEXT_OFFSET, v);
980+
params += next;
981+
}
982+
983+
return NULL;
984+
}
985+
986+
/**
987+
* dfh_find_param() - find parameter block for the given parameter id
988+
* @dfl_dev: dfl device
989+
* @param_id: id of dfl parameter
990+
* @psize: destination to store size of parameter data in bytes
991+
*
992+
* Return: pointer to start of parameter data, PTR_ERR otherwise.
993+
*/
994+
void *dfh_find_param(struct dfl_device *dfl_dev, int param_id, size_t *psize)
995+
{
996+
u64 *phdr = find_param(dfl_dev->params, dfl_dev->param_size, param_id);
997+
998+
if (!phdr)
999+
return ERR_PTR(-ENOENT);
1000+
1001+
if (psize)
1002+
*psize = (FIELD_GET(DFHv1_PARAM_HDR_NEXT_OFFSET, *phdr) - 1) * sizeof(u64);
1003+
1004+
return phdr + 1;
1005+
}
1006+
EXPORT_SYMBOL_GPL(dfh_find_param);
1007+
9371008
static int parse_feature_irqs(struct build_feature_devs_info *binfo,
938-
resource_size_t ofst, u16 fid,
939-
unsigned int *irq_base, unsigned int *nr_irqs)
1009+
resource_size_t ofst, struct dfl_feature_info *finfo)
9401010
{
9411011
void __iomem *base = binfo->ioaddr + ofst;
9421012
unsigned int i, ibase, inr = 0;
1013+
void *params = finfo->params;
9431014
enum dfl_id_type type;
1015+
u16 fid = finfo->fid;
9441016
int virq;
1017+
u64 *p;
9451018
u64 v;
9461019

947-
type = feature_dev_id_type(binfo->feature_dev);
1020+
switch (finfo->dfh_version) {
1021+
case 0:
1022+
/*
1023+
* DFHv0 only provides MMIO resource information for each feature
1024+
* in the DFL header. There is no generic interrupt information.
1025+
* Instead, features with interrupt functionality provide
1026+
* the information in feature specific registers.
1027+
*/
1028+
type = feature_dev_id_type(binfo->feature_dev);
1029+
if (type == PORT_ID) {
1030+
switch (fid) {
1031+
case PORT_FEATURE_ID_UINT:
1032+
v = readq(base + PORT_UINT_CAP);
1033+
ibase = FIELD_GET(PORT_UINT_CAP_FST_VECT, v);
1034+
inr = FIELD_GET(PORT_UINT_CAP_INT_NUM, v);
1035+
break;
1036+
case PORT_FEATURE_ID_ERROR:
1037+
v = readq(base + PORT_ERROR_CAP);
1038+
ibase = FIELD_GET(PORT_ERROR_CAP_INT_VECT, v);
1039+
inr = FIELD_GET(PORT_ERROR_CAP_SUPP_INT, v);
1040+
break;
1041+
}
1042+
} else if (type == FME_ID) {
1043+
switch (fid) {
1044+
case FME_FEATURE_ID_GLOBAL_ERR:
1045+
v = readq(base + FME_ERROR_CAP);
1046+
ibase = FIELD_GET(FME_ERROR_CAP_INT_VECT, v);
1047+
inr = FIELD_GET(FME_ERROR_CAP_SUPP_INT, v);
1048+
break;
1049+
}
1050+
}
1051+
break;
9481052

949-
/*
950-
* Ideally DFL framework should only read info from DFL header, but
951-
* current version DFL only provides mmio resources information for
952-
* each feature in DFL Header, no field for interrupt resources.
953-
* Interrupt resource information is provided by specific mmio
954-
* registers of each private feature which supports interrupt. So in
955-
* order to parse and assign irq resources, DFL framework has to look
956-
* into specific capability registers of these private features.
957-
*
958-
* Once future DFL version supports generic interrupt resource
959-
* information in common DFL headers, the generic interrupt parsing
960-
* code will be added. But in order to be compatible to old version
961-
* DFL, the driver may still fall back to these quirks.
962-
*/
963-
if (type == PORT_ID) {
964-
switch (fid) {
965-
case PORT_FEATURE_ID_UINT:
966-
v = readq(base + PORT_UINT_CAP);
967-
ibase = FIELD_GET(PORT_UINT_CAP_FST_VECT, v);
968-
inr = FIELD_GET(PORT_UINT_CAP_INT_NUM, v);
969-
break;
970-
case PORT_FEATURE_ID_ERROR:
971-
v = readq(base + PORT_ERROR_CAP);
972-
ibase = FIELD_GET(PORT_ERROR_CAP_INT_VECT, v);
973-
inr = FIELD_GET(PORT_ERROR_CAP_SUPP_INT, v);
1053+
case 1:
1054+
/*
1055+
* DFHv1 provides interrupt resource information in DFHv1
1056+
* parameter blocks.
1057+
*/
1058+
p = find_param(params, finfo->param_size, DFHv1_PARAM_ID_MSI_X);
1059+
if (!p)
9741060
break;
975-
}
976-
} else if (type == FME_ID) {
977-
if (fid == FME_FEATURE_ID_GLOBAL_ERR) {
978-
v = readq(base + FME_ERROR_CAP);
979-
ibase = FIELD_GET(FME_ERROR_CAP_INT_VECT, v);
980-
inr = FIELD_GET(FME_ERROR_CAP_SUPP_INT, v);
981-
}
1061+
1062+
p++;
1063+
ibase = FIELD_GET(DFHv1_PARAM_MSI_X_STARTV, *p);
1064+
inr = FIELD_GET(DFHv1_PARAM_MSI_X_NUMV, *p);
1065+
break;
1066+
1067+
default:
1068+
dev_warn(binfo->dev, "unexpected DFH version %d\n", finfo->dfh_version);
1069+
break;
9821070
}
9831071

9841072
if (!inr) {
985-
*irq_base = 0;
986-
*nr_irqs = 0;
1073+
finfo->irq_base = 0;
1074+
finfo->nr_irqs = 0;
9871075
return 0;
9881076
}
9891077

@@ -1006,12 +1094,37 @@ static int parse_feature_irqs(struct build_feature_devs_info *binfo,
10061094
}
10071095
}
10081096

1009-
*irq_base = ibase;
1010-
*nr_irqs = inr;
1097+
finfo->irq_base = ibase;
1098+
finfo->nr_irqs = inr;
10111099

10121100
return 0;
10131101
}
10141102

1103+
static int dfh_get_param_size(void __iomem *dfh_base, resource_size_t max)
1104+
{
1105+
int size = 0;
1106+
u64 v, next;
1107+
1108+
if (!FIELD_GET(DFHv1_CSR_SIZE_GRP_HAS_PARAMS,
1109+
readq(dfh_base + DFHv1_CSR_SIZE_GRP)))
1110+
return 0;
1111+
1112+
while (size + DFHv1_PARAM_HDR < max) {
1113+
v = readq(dfh_base + DFHv1_PARAM_HDR + size);
1114+
1115+
next = FIELD_GET(DFHv1_PARAM_HDR_NEXT_OFFSET, v);
1116+
if (!next)
1117+
return -EINVAL;
1118+
1119+
size += next * sizeof(u64);
1120+
1121+
if (FIELD_GET(DFHv1_PARAM_HDR_NEXT_EOP, v))
1122+
return size;
1123+
}
1124+
1125+
return -ENOENT;
1126+
}
1127+
10151128
/*
10161129
* when create sub feature instances, for private features, it doesn't need
10171130
* to provide resource size and feature id as they could be read from DFH
@@ -1023,39 +1136,69 @@ static int
10231136
create_feature_instance(struct build_feature_devs_info *binfo,
10241137
resource_size_t ofst, resource_size_t size, u16 fid)
10251138
{
1026-
unsigned int irq_base, nr_irqs;
10271139
struct dfl_feature_info *finfo;
1140+
resource_size_t start, end;
1141+
int dfh_psize = 0;
10281142
u8 revision = 0;
1143+
u64 v, addr_off;
1144+
u8 dfh_ver = 0;
10291145
int ret;
1030-
u64 v;
10311146

10321147
if (fid != FEATURE_ID_AFU) {
10331148
v = readq(binfo->ioaddr + ofst);
10341149
revision = FIELD_GET(DFH_REVISION, v);
1035-
1150+
dfh_ver = FIELD_GET(DFH_VERSION, v);
10361151
/* read feature size and id if inputs are invalid */
10371152
size = size ? size : feature_size(v);
10381153
fid = fid ? fid : feature_id(v);
1154+
if (dfh_ver == 1) {
1155+
dfh_psize = dfh_get_param_size(binfo->ioaddr + ofst, size);
1156+
if (dfh_psize < 0) {
1157+
dev_err(binfo->dev,
1158+
"failed to read size of DFHv1 parameters %d\n",
1159+
dfh_psize);
1160+
return dfh_psize;
1161+
}
1162+
dev_dbg(binfo->dev, "dfhv1_psize %d\n", dfh_psize);
1163+
}
10391164
}
10401165

10411166
if (binfo->len - ofst < size)
10421167
return -EINVAL;
10431168

1044-
ret = parse_feature_irqs(binfo, ofst, fid, &irq_base, &nr_irqs);
1045-
if (ret)
1046-
return ret;
1047-
1048-
finfo = kzalloc(sizeof(*finfo), GFP_KERNEL);
1169+
finfo = kzalloc(struct_size(finfo, params, dfh_psize / sizeof(u64)), GFP_KERNEL);
10491170
if (!finfo)
10501171
return -ENOMEM;
10511172

1173+
memcpy_fromio(finfo->params, binfo->ioaddr + ofst + DFHv1_PARAM_HDR, dfh_psize);
1174+
finfo->param_size = dfh_psize;
1175+
10521176
finfo->fid = fid;
10531177
finfo->revision = revision;
1054-
finfo->mmio_res.start = binfo->start + ofst;
1055-
finfo->mmio_res.end = finfo->mmio_res.start + size - 1;
1178+
finfo->dfh_version = dfh_ver;
1179+
if (dfh_ver == 1) {
1180+
v = readq(binfo->ioaddr + ofst + DFHv1_CSR_ADDR);
1181+
addr_off = FIELD_GET(DFHv1_CSR_ADDR_MASK, v);
1182+
if (FIELD_GET(DFHv1_CSR_ADDR_REL, v))
1183+
start = addr_off << 1;
1184+
else
1185+
start = binfo->start + ofst + addr_off;
1186+
1187+
v = readq(binfo->ioaddr + ofst + DFHv1_CSR_SIZE_GRP);
1188+
end = start + FIELD_GET(DFHv1_CSR_SIZE_GRP_SIZE, v) - 1;
1189+
} else {
1190+
start = binfo->start + ofst;
1191+
end = start + size - 1;
1192+
}
10561193
finfo->mmio_res.flags = IORESOURCE_MEM;
1057-
finfo->irq_base = irq_base;
1058-
finfo->nr_irqs = nr_irqs;
1194+
finfo->mmio_res.start = start;
1195+
finfo->mmio_res.end = end;
1196+
1197+
ret = parse_feature_irqs(binfo, ofst, finfo);
1198+
if (ret) {
1199+
kfree(finfo);
1200+
return ret;
1201+
}
10591202

10601203
list_add_tail(&finfo->node, &binfo->sub_features);
10611204
binfo->feature_num++;

drivers/fpga/dfl.h

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -111,6 +111,10 @@
111111
#define DFHv1_PARAM_HDR_NEXT_EOP BIT_ULL(32)
112112
#define DFHv1_PARAM_DATA 0x08 /* Offset of Param data from Param header */
113113

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+
114118
/* Next AFU Register Bitfield */
115119
#define NEXT_AFU_NEXT_DFH_OFST GENMASK_ULL(23, 0) /* Offset to next AFU */
116120

@@ -263,6 +267,7 @@ struct dfl_feature_irq_ctx {
263267
*
264268
* @dev: ptr to pdev of the feature device which has the sub feature.
265269
* @id: sub feature id.
270+
* @revision: revisition of the instance of a feature.
266271
* @resource_index: each sub feature has one mmio resource for its registers.
267272
* this index is used to find its mmio resource from the
268273
* feature dev (platform device)'s resources.
@@ -272,6 +277,9 @@ struct dfl_feature_irq_ctx {
272277
* @ops: ops of this sub feature.
273278
* @ddev: ptr to the dfl device of this sub feature.
274279
* @priv: priv data of this feature.
280+
* @dfh_version: version of the DFH
281+
* @param_size: size of dfh parameters
282+
* @params: point to memory copy of dfh parameters
275283
*/
276284
struct dfl_feature {
277285
struct platform_device *dev;
@@ -284,6 +292,9 @@ struct dfl_feature {
284292
const struct dfl_feature_ops *ops;
285293
struct dfl_device *ddev;
286294
void *priv;
295+
u8 dfh_version;
296+
unsigned int param_size;
297+
void *params;
287298
};
288299

289300
#define FEATURE_DEV_ID_UNUSED (-1)

0 commit comments

Comments
 (0)