blob: 8c226f5602f1b271aab5401a4b1cdb06a7cfc008 [file] [log] [blame]
/*
* Copyright (C) 2016-2017 Dell, Inc.
* Copyright (C) 2016 Hewlett Packard Enterprise Development LP
* Copyright (c) 2016, Intel Corporation.
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU Lesser General Public License,
* version 2.1, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT ANY
* WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
* FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for
* more details.
*/
#include <stdlib.h>
#include <limits.h>
#include <util/log.h>
#include <ndctl/libndctl.h>
#include "private.h"
#include "msft.h"
#define CMD_MSFT(_c) ((_c)->msft)
#define CMD_MSFT_SMART(_c) (CMD_MSFT(_c)->u.smart.data)
static struct ndctl_cmd *msft_dimm_cmd_new_smart(struct ndctl_dimm *dimm)
{
struct ndctl_bus *bus = ndctl_dimm_get_bus(dimm);
struct ndctl_ctx *ctx = ndctl_bus_get_ctx(bus);
struct ndctl_cmd *cmd;
size_t size;
struct ndn_pkg_msft *msft;
if (!ndctl_dimm_is_cmd_supported(dimm, ND_CMD_CALL)) {
dbg(ctx, "unsupported cmd\n");
return NULL;
}
size = sizeof(*cmd) + sizeof(struct ndn_pkg_msft);
cmd = calloc(1, size);
if (!cmd)
return NULL;
cmd->dimm = dimm;
ndctl_cmd_ref(cmd);
cmd->type = ND_CMD_CALL;
cmd->size = size;
cmd->status = 1;
msft = CMD_MSFT(cmd);
msft->gen.nd_family = NVDIMM_FAMILY_MSFT;
msft->gen.nd_command = NDN_MSFT_CMD_SMART;
msft->gen.nd_fw_size = 0;
msft->gen.nd_size_in = offsetof(struct ndn_msft_smart, status);
msft->gen.nd_size_out = sizeof(msft->u.smart);
msft->u.smart.status = 0;
cmd->firmware_status = &msft->u.smart.status;
return cmd;
}
static int msft_smart_valid(struct ndctl_cmd *cmd)
{
if (cmd->type != ND_CMD_CALL ||
cmd->size != sizeof(*cmd) + sizeof(struct ndn_pkg_msft) ||
CMD_MSFT(cmd)->gen.nd_family != NVDIMM_FAMILY_MSFT ||
CMD_MSFT(cmd)->gen.nd_command != NDN_MSFT_CMD_SMART ||
cmd->status != 0)
return cmd->status < 0 ? cmd->status : -EINVAL;
return 0;
}
static unsigned int msft_cmd_smart_get_flags(struct ndctl_cmd *cmd)
{
if (msft_smart_valid(cmd) < 0)
return UINT_MAX;
/* below health data can be retrieved via MSFT _DSM function 11 */
return NDN_MSFT_SMART_HEALTH_VALID |
NDN_MSFT_SMART_TEMP_VALID |
NDN_MSFT_SMART_USED_VALID;
}
static unsigned int num_set_bit_health(__u16 num)
{
int i;
__u16 n = num & 0x7FFF;
unsigned int count = 0;
for (i = 0; i < 15; i++)
if (!!(n & (1 << i)))
count++;
return count;
}
static unsigned int msft_cmd_smart_get_health(struct ndctl_cmd *cmd)
{
unsigned int health;
unsigned int num;
if (msft_smart_valid(cmd) < 0)
return UINT_MAX;
num = num_set_bit_health(CMD_MSFT_SMART(cmd)->health);
if (num == 0)
health = 0;
else if (num < 2)
health = ND_SMART_NON_CRITICAL_HEALTH;
else if (num < 3)
health = ND_SMART_CRITICAL_HEALTH;
else
health = ND_SMART_FATAL_HEALTH;
return health;
}
static unsigned int msft_cmd_smart_get_temperature(struct ndctl_cmd *cmd)
{
if (msft_smart_valid(cmd) < 0)
return UINT_MAX;
return CMD_MSFT_SMART(cmd)->temp * 16;
}
static unsigned int msft_cmd_smart_get_life_used(struct ndctl_cmd *cmd)
{
if (msft_smart_valid(cmd) < 0)
return UINT_MAX;
return 100 - CMD_MSFT_SMART(cmd)->nvm_lifetime;
}
struct ndctl_smart_ops * const msft_smart_ops = &(struct ndctl_smart_ops) {
.new_smart = msft_dimm_cmd_new_smart,
.smart_get_flags = msft_cmd_smart_get_flags,
.smart_get_health = msft_cmd_smart_get_health,
.smart_get_temperature = msft_cmd_smart_get_temperature,
.smart_get_life_used = msft_cmd_smart_get_life_used,
};