@@ -107,3 +107,14 @@ ndctl_cmd_fw_xlat_firmware_status(struct ndctl_cmd *cmd)
else
return FW_EUNKNOWN;
}
+
+NDCTL_EXPORT int
+ndctl_dimm_fw_update_supported(struct ndctl_dimm *dimm)
+{
+ struct ndctl_dimm_ops *ops = dimm->ops;
+
+ if (ops && ops->fw_update_supported)
+ return ops->fw_update_supported(dimm);
+ else
+ return -ENOTTY;
+}
@@ -650,6 +650,37 @@ intel_dimm_cmd_new_lss(struct ndctl_dimm *dimm)
return cmd;
}
+static int intel_dimm_fw_update_supported(struct ndctl_dimm *dimm)
+{
+ struct ndctl_ctx *ctx = ndctl_dimm_get_ctx(dimm);
+
+ if (!ndctl_dimm_is_cmd_supported(dimm, ND_CMD_CALL)) {
+ dbg(ctx, "unsupported cmd: %d\n", ND_CMD_CALL);
+ return -EOPNOTSUPP;
+ }
+
+ /*
+ * We only need to check FW_GET_INFO. If that isn't supported then
+ * the others aren't either.
+ */
+ if (test_dimm_dsm(dimm, ND_INTEL_FW_GET_INFO) ==
+ DIMM_DSM_UNSUPPORTED ||
+ test_dimm_dsm(dimm, ND_INTEL_FW_START_UPDATE) ==
+ DIMM_DSM_UNSUPPORTED ||
+ test_dimm_dsm(dimm, ND_INTEL_FW_SEND_DATA) ==
+ DIMM_DSM_UNSUPPORTED ||
+ test_dimm_dsm(dimm, ND_INTEL_FW_FINISH_UPDATE) ==
+ DIMM_DSM_UNSUPPORTED ||
+ test_dimm_dsm(dimm, ND_INTEL_FW_FINISH_STATUS_QUERY) ==
+ DIMM_DSM_UNSUPPORTED) {
+ dbg(ctx, "unsupported function: %d\n",
+ ND_INTEL_FW_GET_INFO);
+ return -EIO;
+ }
+
+ return 0;
+}
+
struct ndctl_dimm_ops * const intel_dimm_ops = &(struct ndctl_dimm_ops) {
.cmd_desc = intel_cmd_desc,
.new_smart = intel_dimm_cmd_new_smart,
@@ -703,4 +734,5 @@ struct ndctl_dimm_ops * const intel_dimm_ops = &(struct ndctl_dimm_ops) {
.fw_fquery_get_fw_rev = intel_cmd_fw_fquery_get_fw_rev,
.fw_xlat_firmware_status = intel_cmd_fw_xlat_firmware_status,
.new_ack_shutdown_count = intel_dimm_cmd_new_lss,
+ .fw_update_supported = intel_dimm_fw_update_supported,
};
@@ -344,4 +344,5 @@ global:
ndctl_cmd_fw_fquery_get_fw_rev;
ndctl_cmd_fw_xlat_firmware_status;
ndctl_dimm_cmd_new_ack_shutdown_count;
+ ndctl_dimm_fw_update_supported;
} LIBNDCTL_13;
@@ -325,6 +325,7 @@ struct ndctl_dimm_ops {
unsigned long long (*fw_fquery_get_fw_rev)(struct ndctl_cmd *);
enum ND_FW_STATUS (*fw_xlat_firmware_status)(struct ndctl_cmd *);
struct ndctl_cmd *(*new_ack_shutdown_count)(struct ndctl_dimm *);
+ int (*fw_update_supported)(struct ndctl_dimm *);
};
struct ndctl_dimm_ops * const intel_dimm_ops;
@@ -625,6 +625,7 @@ unsigned int ndctl_cmd_fw_start_get_context(struct ndctl_cmd *cmd);
unsigned long long ndctl_cmd_fw_fquery_get_fw_rev(struct ndctl_cmd *cmd);
enum ND_FW_STATUS ndctl_cmd_fw_xlat_firmware_status(struct ndctl_cmd *cmd);
struct ndctl_cmd *ndctl_dimm_cmd_new_ack_shutdown_count(struct ndctl_dimm *dimm);
+int ndctl_dimm_fw_update_supported(struct ndctl_dimm *dimm);
#ifdef __cplusplus
} /* extern "C" */
@@ -449,11 +449,24 @@ static int get_ndctl_dimm(struct update_context *uctx, void *ctx)
{
struct ndctl_dimm *dimm;
struct ndctl_bus *bus;
+ int rc;
ndctl_bus_foreach(ctx, bus)
ndctl_dimm_foreach(bus, dimm) {
if (!util_dimm_filter(dimm, uctx->dimm_id))
continue;
+ rc = ndctl_dimm_fw_update_supported(dimm);
+ switch (rc) {
+ case -ENOTTY:
+ error("DIMM firmware update not supported by ndctl.");
+ return rc;
+ case -EOPNOTSUPP:
+ error("DIMM firmware update not supported by the kernel");
+ return rc;
+ case -EIO:
+ error("DIMM firmware update not supported by platform firmware.");
+ return rc;
+ }
uctx->dimm = dimm;
return 0;
}