diff options
Diffstat (limited to 'drivers/fpga/dfl.c')
-rw-r--r-- | drivers/fpga/dfl.c | 226 |
1 files changed, 207 insertions, 19 deletions
diff --git a/drivers/fpga/dfl.c b/drivers/fpga/dfl.c index 4b66aaa32b5a..96a2b8274a33 100644 --- a/drivers/fpga/dfl.c +++ b/drivers/fpga/dfl.c | |||
@@ -231,16 +231,20 @@ EXPORT_SYMBOL_GPL(dfl_fpga_port_ops_del); | |||
231 | */ | 231 | */ |
232 | int dfl_fpga_check_port_id(struct platform_device *pdev, void *pport_id) | 232 | int dfl_fpga_check_port_id(struct platform_device *pdev, void *pport_id) |
233 | { | 233 | { |
234 | struct dfl_fpga_port_ops *port_ops = dfl_fpga_port_ops_get(pdev); | 234 | struct dfl_feature_platform_data *pdata = dev_get_platdata(&pdev->dev); |
235 | int port_id; | 235 | struct dfl_fpga_port_ops *port_ops; |
236 | |||
237 | if (pdata->id != FEATURE_DEV_ID_UNUSED) | ||
238 | return pdata->id == *(int *)pport_id; | ||
236 | 239 | ||
240 | port_ops = dfl_fpga_port_ops_get(pdev); | ||
237 | if (!port_ops || !port_ops->get_id) | 241 | if (!port_ops || !port_ops->get_id) |
238 | return 0; | 242 | return 0; |
239 | 243 | ||
240 | port_id = port_ops->get_id(pdev); | 244 | pdata->id = port_ops->get_id(pdev); |
241 | dfl_fpga_port_ops_put(port_ops); | 245 | dfl_fpga_port_ops_put(port_ops); |
242 | 246 | ||
243 | return port_id == *(int *)pport_id; | 247 | return pdata->id == *(int *)pport_id; |
244 | } | 248 | } |
245 | EXPORT_SYMBOL_GPL(dfl_fpga_check_port_id); | 249 | EXPORT_SYMBOL_GPL(dfl_fpga_check_port_id); |
246 | 250 | ||
@@ -255,7 +259,8 @@ void dfl_fpga_dev_feature_uinit(struct platform_device *pdev) | |||
255 | 259 | ||
256 | dfl_fpga_dev_for_each_feature(pdata, feature) | 260 | dfl_fpga_dev_for_each_feature(pdata, feature) |
257 | if (feature->ops) { | 261 | if (feature->ops) { |
258 | feature->ops->uinit(pdev, feature); | 262 | if (feature->ops->uinit) |
263 | feature->ops->uinit(pdev, feature); | ||
259 | feature->ops = NULL; | 264 | feature->ops = NULL; |
260 | } | 265 | } |
261 | } | 266 | } |
@@ -266,17 +271,34 @@ static int dfl_feature_instance_init(struct platform_device *pdev, | |||
266 | struct dfl_feature *feature, | 271 | struct dfl_feature *feature, |
267 | struct dfl_feature_driver *drv) | 272 | struct dfl_feature_driver *drv) |
268 | { | 273 | { |
269 | int ret; | 274 | int ret = 0; |
270 | 275 | ||
271 | ret = drv->ops->init(pdev, feature); | 276 | if (drv->ops->init) { |
272 | if (ret) | 277 | ret = drv->ops->init(pdev, feature); |
273 | return ret; | 278 | if (ret) |
279 | return ret; | ||
280 | } | ||
274 | 281 | ||
275 | feature->ops = drv->ops; | 282 | feature->ops = drv->ops; |
276 | 283 | ||
277 | return ret; | 284 | return ret; |
278 | } | 285 | } |
279 | 286 | ||
287 | static bool dfl_feature_drv_match(struct dfl_feature *feature, | ||
288 | struct dfl_feature_driver *driver) | ||
289 | { | ||
290 | const struct dfl_feature_id *ids = driver->id_table; | ||
291 | |||
292 | if (ids) { | ||
293 | while (ids->id) { | ||
294 | if (ids->id == feature->id) | ||
295 | return true; | ||
296 | ids++; | ||
297 | } | ||
298 | } | ||
299 | return false; | ||
300 | } | ||
301 | |||
280 | /** | 302 | /** |
281 | * dfl_fpga_dev_feature_init - init for sub features of dfl feature device | 303 | * dfl_fpga_dev_feature_init - init for sub features of dfl feature device |
282 | * @pdev: feature device. | 304 | * @pdev: feature device. |
@@ -297,8 +319,7 @@ int dfl_fpga_dev_feature_init(struct platform_device *pdev, | |||
297 | 319 | ||
298 | while (drv->ops) { | 320 | while (drv->ops) { |
299 | dfl_fpga_dev_for_each_feature(pdata, feature) { | 321 | dfl_fpga_dev_for_each_feature(pdata, feature) { |
300 | /* match feature and drv using id */ | 322 | if (dfl_feature_drv_match(feature, drv)) { |
301 | if (feature->id == drv->id) { | ||
302 | ret = dfl_feature_instance_init(pdev, pdata, | 323 | ret = dfl_feature_instance_init(pdev, pdata, |
303 | feature, drv); | 324 | feature, drv); |
304 | if (ret) | 325 | if (ret) |
@@ -474,6 +495,7 @@ static int build_info_commit_dev(struct build_feature_devs_info *binfo) | |||
474 | pdata->dev = fdev; | 495 | pdata->dev = fdev; |
475 | pdata->num = binfo->feature_num; | 496 | pdata->num = binfo->feature_num; |
476 | pdata->dfl_cdev = binfo->cdev; | 497 | pdata->dfl_cdev = binfo->cdev; |
498 | pdata->id = FEATURE_DEV_ID_UNUSED; | ||
477 | mutex_init(&pdata->lock); | 499 | mutex_init(&pdata->lock); |
478 | lockdep_set_class_and_name(&pdata->lock, &dfl_pdata_keys[type], | 500 | lockdep_set_class_and_name(&pdata->lock, &dfl_pdata_keys[type], |
479 | dfl_pdata_key_strings[type]); | 501 | dfl_pdata_key_strings[type]); |
@@ -973,25 +995,27 @@ void dfl_fpga_feature_devs_remove(struct dfl_fpga_cdev *cdev) | |||
973 | { | 995 | { |
974 | struct dfl_feature_platform_data *pdata, *ptmp; | 996 | struct dfl_feature_platform_data *pdata, *ptmp; |
975 | 997 | ||
976 | remove_feature_devs(cdev); | ||
977 | |||
978 | mutex_lock(&cdev->lock); | 998 | mutex_lock(&cdev->lock); |
979 | if (cdev->fme_dev) { | 999 | if (cdev->fme_dev) |
980 | /* the fme should be unregistered. */ | ||
981 | WARN_ON(device_is_registered(cdev->fme_dev)); | ||
982 | put_device(cdev->fme_dev); | 1000 | put_device(cdev->fme_dev); |
983 | } | ||
984 | 1001 | ||
985 | list_for_each_entry_safe(pdata, ptmp, &cdev->port_dev_list, node) { | 1002 | list_for_each_entry_safe(pdata, ptmp, &cdev->port_dev_list, node) { |
986 | struct platform_device *port_dev = pdata->dev; | 1003 | struct platform_device *port_dev = pdata->dev; |
987 | 1004 | ||
988 | /* the port should be unregistered. */ | 1005 | /* remove released ports */ |
989 | WARN_ON(device_is_registered(&port_dev->dev)); | 1006 | if (!device_is_registered(&port_dev->dev)) { |
1007 | dfl_id_free(feature_dev_id_type(port_dev), | ||
1008 | port_dev->id); | ||
1009 | platform_device_put(port_dev); | ||
1010 | } | ||
1011 | |||
990 | list_del(&pdata->node); | 1012 | list_del(&pdata->node); |
991 | put_device(&port_dev->dev); | 1013 | put_device(&port_dev->dev); |
992 | } | 1014 | } |
993 | mutex_unlock(&cdev->lock); | 1015 | mutex_unlock(&cdev->lock); |
994 | 1016 | ||
1017 | remove_feature_devs(cdev); | ||
1018 | |||
995 | fpga_region_unregister(cdev->region); | 1019 | fpga_region_unregister(cdev->region); |
996 | devm_kfree(cdev->parent, cdev); | 1020 | devm_kfree(cdev->parent, cdev); |
997 | } | 1021 | } |
@@ -1042,6 +1066,170 @@ static int __init dfl_fpga_init(void) | |||
1042 | return ret; | 1066 | return ret; |
1043 | } | 1067 | } |
1044 | 1068 | ||
1069 | /** | ||
1070 | * dfl_fpga_cdev_release_port - release a port platform device | ||
1071 | * | ||
1072 | * @cdev: parent container device. | ||
1073 | * @port_id: id of the port platform device. | ||
1074 | * | ||
1075 | * This function allows user to release a port platform device. This is a | ||
1076 | * mandatory step before turn a port from PF into VF for SRIOV support. | ||
1077 | * | ||
1078 | * Return: 0 on success, negative error code otherwise. | ||
1079 | */ | ||
1080 | int dfl_fpga_cdev_release_port(struct dfl_fpga_cdev *cdev, int port_id) | ||
1081 | { | ||
1082 | struct platform_device *port_pdev; | ||
1083 | int ret = -ENODEV; | ||
1084 | |||
1085 | mutex_lock(&cdev->lock); | ||
1086 | port_pdev = __dfl_fpga_cdev_find_port(cdev, &port_id, | ||
1087 | dfl_fpga_check_port_id); | ||
1088 | if (!port_pdev) | ||
1089 | goto unlock_exit; | ||
1090 | |||
1091 | if (!device_is_registered(&port_pdev->dev)) { | ||
1092 | ret = -EBUSY; | ||
1093 | goto put_dev_exit; | ||
1094 | } | ||
1095 | |||
1096 | ret = dfl_feature_dev_use_begin(dev_get_platdata(&port_pdev->dev)); | ||
1097 | if (ret) | ||
1098 | goto put_dev_exit; | ||
1099 | |||
1100 | platform_device_del(port_pdev); | ||
1101 | cdev->released_port_num++; | ||
1102 | put_dev_exit: | ||
1103 | put_device(&port_pdev->dev); | ||
1104 | unlock_exit: | ||
1105 | mutex_unlock(&cdev->lock); | ||
1106 | return ret; | ||
1107 | } | ||
1108 | EXPORT_SYMBOL_GPL(dfl_fpga_cdev_release_port); | ||
1109 | |||
1110 | /** | ||
1111 | * dfl_fpga_cdev_assign_port - assign a port platform device back | ||
1112 | * | ||
1113 | * @cdev: parent container device. | ||
1114 | * @port_id: id of the port platform device. | ||
1115 | * | ||
1116 | * This function allows user to assign a port platform device back. This is | ||
1117 | * a mandatory step after disable SRIOV support. | ||
1118 | * | ||
1119 | * Return: 0 on success, negative error code otherwise. | ||
1120 | */ | ||
1121 | int dfl_fpga_cdev_assign_port(struct dfl_fpga_cdev *cdev, int port_id) | ||
1122 | { | ||
1123 | struct platform_device *port_pdev; | ||
1124 | int ret = -ENODEV; | ||
1125 | |||
1126 | mutex_lock(&cdev->lock); | ||
1127 | port_pdev = __dfl_fpga_cdev_find_port(cdev, &port_id, | ||
1128 | dfl_fpga_check_port_id); | ||
1129 | if (!port_pdev) | ||
1130 | goto unlock_exit; | ||
1131 | |||
1132 | if (device_is_registered(&port_pdev->dev)) { | ||
1133 | ret = -EBUSY; | ||
1134 | goto put_dev_exit; | ||
1135 | } | ||
1136 | |||
1137 | ret = platform_device_add(port_pdev); | ||
1138 | if (ret) | ||
1139 | goto put_dev_exit; | ||
1140 | |||
1141 | dfl_feature_dev_use_end(dev_get_platdata(&port_pdev->dev)); | ||
1142 | cdev->released_port_num--; | ||
1143 | put_dev_exit: | ||
1144 | put_device(&port_pdev->dev); | ||
1145 | unlock_exit: | ||
1146 | mutex_unlock(&cdev->lock); | ||
1147 | return ret; | ||
1148 | } | ||
1149 | EXPORT_SYMBOL_GPL(dfl_fpga_cdev_assign_port); | ||
1150 | |||
1151 | static void config_port_access_mode(struct device *fme_dev, int port_id, | ||
1152 | bool is_vf) | ||
1153 | { | ||
1154 | void __iomem *base; | ||
1155 | u64 v; | ||
1156 | |||
1157 | base = dfl_get_feature_ioaddr_by_id(fme_dev, FME_FEATURE_ID_HEADER); | ||
1158 | |||
1159 | v = readq(base + FME_HDR_PORT_OFST(port_id)); | ||
1160 | |||
1161 | v &= ~FME_PORT_OFST_ACC_CTRL; | ||
1162 | v |= FIELD_PREP(FME_PORT_OFST_ACC_CTRL, | ||
1163 | is_vf ? FME_PORT_OFST_ACC_VF : FME_PORT_OFST_ACC_PF); | ||
1164 | |||
1165 | writeq(v, base + FME_HDR_PORT_OFST(port_id)); | ||
1166 | } | ||
1167 | |||
1168 | #define config_port_vf_mode(dev, id) config_port_access_mode(dev, id, true) | ||
1169 | #define config_port_pf_mode(dev, id) config_port_access_mode(dev, id, false) | ||
1170 | |||
1171 | /** | ||
1172 | * dfl_fpga_cdev_config_ports_pf - configure ports to PF access mode | ||
1173 | * | ||
1174 | * @cdev: parent container device. | ||
1175 | * | ||
1176 | * This function is needed in sriov configuration routine. It could be used to | ||
1177 | * configure the all released ports from VF access mode to PF. | ||
1178 | */ | ||
1179 | void dfl_fpga_cdev_config_ports_pf(struct dfl_fpga_cdev *cdev) | ||
1180 | { | ||
1181 | struct dfl_feature_platform_data *pdata; | ||
1182 | |||
1183 | mutex_lock(&cdev->lock); | ||
1184 | list_for_each_entry(pdata, &cdev->port_dev_list, node) { | ||
1185 | if (device_is_registered(&pdata->dev->dev)) | ||
1186 | continue; | ||
1187 | |||
1188 | config_port_pf_mode(cdev->fme_dev, pdata->id); | ||
1189 | } | ||
1190 | mutex_unlock(&cdev->lock); | ||
1191 | } | ||
1192 | EXPORT_SYMBOL_GPL(dfl_fpga_cdev_config_ports_pf); | ||
1193 | |||
1194 | /** | ||
1195 | * dfl_fpga_cdev_config_ports_vf - configure ports to VF access mode | ||
1196 | * | ||
1197 | * @cdev: parent container device. | ||
1198 | * @num_vfs: VF device number. | ||
1199 | * | ||
1200 | * This function is needed in sriov configuration routine. It could be used to | ||
1201 | * configure the released ports from PF access mode to VF. | ||
1202 | * | ||
1203 | * Return: 0 on success, negative error code otherwise. | ||
1204 | */ | ||
1205 | int dfl_fpga_cdev_config_ports_vf(struct dfl_fpga_cdev *cdev, int num_vfs) | ||
1206 | { | ||
1207 | struct dfl_feature_platform_data *pdata; | ||
1208 | int ret = 0; | ||
1209 | |||
1210 | mutex_lock(&cdev->lock); | ||
1211 | /* | ||
1212 | * can't turn multiple ports into 1 VF device, only 1 port for 1 VF | ||
1213 | * device, so if released port number doesn't match VF device number, | ||
1214 | * then reject the request with -EINVAL error code. | ||
1215 | */ | ||
1216 | if (cdev->released_port_num != num_vfs) { | ||
1217 | ret = -EINVAL; | ||
1218 | goto done; | ||
1219 | } | ||
1220 | |||
1221 | list_for_each_entry(pdata, &cdev->port_dev_list, node) { | ||
1222 | if (device_is_registered(&pdata->dev->dev)) | ||
1223 | continue; | ||
1224 | |||
1225 | config_port_vf_mode(cdev->fme_dev, pdata->id); | ||
1226 | } | ||
1227 | done: | ||
1228 | mutex_unlock(&cdev->lock); | ||
1229 | return ret; | ||
1230 | } | ||
1231 | EXPORT_SYMBOL_GPL(dfl_fpga_cdev_config_ports_vf); | ||
1232 | |||
1045 | static void __exit dfl_fpga_exit(void) | 1233 | static void __exit dfl_fpga_exit(void) |
1046 | { | 1234 | { |
1047 | dfl_chardev_uinit(); | 1235 | dfl_chardev_uinit(); |