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"
@@ -342,6 +343,8 @@ static void release_dfl_dev(struct device *dev)
342
343
if (ddev -> mmio_res .parent )
343
344
release_resource (& ddev -> mmio_res );
344
345
346
+ kfree (ddev -> params );
347
+
345
348
ida_free (& dfl_device_ida , ddev -> id );
346
349
kfree (ddev -> irqs );
347
350
kfree (ddev );
@@ -380,7 +383,16 @@ dfl_dev_add(struct dfl_feature_platform_data *pdata,
380
383
ddev -> type = feature_dev_id_type (pdev );
381
384
ddev -> feature_id = feature -> id ;
382
385
ddev -> revision = feature -> revision ;
386
+ ddev -> dfh_version = feature -> dfh_version ;
383
387
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
+ }
384
396
385
397
/* add mmio resource */
386
398
parent_res = & pdev -> resource [feature -> resource_index ];
@@ -708,20 +720,27 @@ struct build_feature_devs_info {
708
720
* struct dfl_feature_info - sub feature info collected during feature dev build
709
721
*
710
722
* @fid: id of this sub feature.
723
+ * @revision: revision of this sub feature
724
+ * @dfh_version: version of Device Feature Header (DFH)
711
725
* @mmio_res: mmio resource of this sub feature.
712
726
* @ioaddr: mapped base address of mmio resource.
713
727
* @node: node in sub_features linked list.
714
728
* @irq_base: start of irq index in this sub feature.
715
729
* @nr_irqs: number of irqs of this sub feature.
730
+ * @param_size: size DFH parameters.
731
+ * @params: DFH parameter data.
716
732
*/
717
733
struct dfl_feature_info {
718
734
u16 fid ;
719
735
u8 revision ;
736
+ u8 dfh_version ;
720
737
struct resource mmio_res ;
721
738
void __iomem * ioaddr ;
722
739
struct list_head node ;
723
740
unsigned int irq_base ;
724
741
unsigned int nr_irqs ;
742
+ unsigned int param_size ;
743
+ u64 params [];
725
744
};
726
745
727
746
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)
797
816
feature -> dev = fdev ;
798
817
feature -> id = finfo -> fid ;
799
818
feature -> revision = finfo -> revision ;
819
+ feature -> dfh_version = finfo -> dfh_version ;
800
820
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
+ }
801
830
/*
802
831
* the FIU header feature has some fundamental functions (sriov
803
832
* set, port enable/disable) needed for the dfl bus device and
@@ -934,56 +963,115 @@ static u16 feature_id(u64 value)
934
963
return 0 ;
935
964
}
936
965
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
+
937
1008
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 )
940
1010
{
941
1011
void __iomem * base = binfo -> ioaddr + ofst ;
942
1012
unsigned int i , ibase , inr = 0 ;
1013
+ void * params = finfo -> params ;
943
1014
enum dfl_id_type type ;
1015
+ u16 fid = finfo -> fid ;
944
1016
int virq ;
1017
+ u64 * p ;
945
1018
u64 v ;
946
1019
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 ;
948
1052
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 )
974
1060
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 ;
982
1070
}
983
1071
984
1072
if (!inr ) {
985
- * irq_base = 0 ;
986
- * nr_irqs = 0 ;
1073
+ finfo -> irq_base = 0 ;
1074
+ finfo -> nr_irqs = 0 ;
987
1075
return 0 ;
988
1076
}
989
1077
@@ -1006,12 +1094,37 @@ static int parse_feature_irqs(struct build_feature_devs_info *binfo,
1006
1094
}
1007
1095
}
1008
1096
1009
- * irq_base = ibase ;
1010
- * nr_irqs = inr ;
1097
+ finfo -> irq_base = ibase ;
1098
+ finfo -> nr_irqs = inr ;
1011
1099
1012
1100
return 0 ;
1013
1101
}
1014
1102
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
+
1015
1128
/*
1016
1129
* when create sub feature instances, for private features, it doesn't need
1017
1130
* to provide resource size and feature id as they could be read from DFH
@@ -1023,39 +1136,69 @@ static int
1023
1136
create_feature_instance (struct build_feature_devs_info * binfo ,
1024
1137
resource_size_t ofst , resource_size_t size , u16 fid )
1025
1138
{
1026
- unsigned int irq_base , nr_irqs ;
1027
1139
struct dfl_feature_info * finfo ;
1140
+ resource_size_t start , end ;
1141
+ int dfh_psize = 0 ;
1028
1142
u8 revision = 0 ;
1143
+ u64 v , addr_off ;
1144
+ u8 dfh_ver = 0 ;
1029
1145
int ret ;
1030
- u64 v ;
1031
1146
1032
1147
if (fid != FEATURE_ID_AFU ) {
1033
1148
v = readq (binfo -> ioaddr + ofst );
1034
1149
revision = FIELD_GET (DFH_REVISION , v );
1035
-
1150
+ dfh_ver = FIELD_GET ( DFH_VERSION , v );
1036
1151
/* read feature size and id if inputs are invalid */
1037
1152
size = size ? size : feature_size (v );
1038
1153
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
+ }
1039
1164
}
1040
1165
1041
1166
if (binfo -> len - ofst < size )
1042
1167
return - EINVAL ;
1043
1168
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 );
1049
1170
if (!finfo )
1050
1171
return - ENOMEM ;
1051
1172
1173
+ memcpy_fromio (finfo -> params , binfo -> ioaddr + ofst + DFHv1_PARAM_HDR , dfh_psize );
1174
+ finfo -> param_size = dfh_psize ;
1175
+
1052
1176
finfo -> fid = fid ;
1053
1177
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
+ }
1056
1193
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
+ }
1059
1202
1060
1203
list_add_tail (& finfo -> node , & binfo -> sub_features );
1061
1204
binfo -> feature_num ++ ;
0 commit comments