Quellcodebibliothek Statistik Leitseite products/Sources/formale Sprachen/C/Linux/drivers/hwmon/occ/   (Open Source Betriebssystem Version 6.17.9©)  Datei vom 24.10.2025 mit Größe 26 kB image not shown  

Quelle  common.c   Sprache: C

 
// SPDX-License-Identifier: GPL-2.0+
// Copyright IBM Corp 2019

#include <linux/device.h>
#include <linux/export.h>
#include <linux/hwmon.h>
#include <linux/hwmon-sysfs.h>
#include <linux/jiffies.h>
#include <linux/kernel.h>
#include <linux/math64.h>
#include <linux/module.h>
#include <linux/mutex.h>
#include <linux/property.h>
#include <linux/sysfs.h>
#include <linux/unaligned.h>

#include "common.h"

#define EXTN_FLAG_SENSOR_ID  BIT(7)

#define OCC_ERROR_COUNT_THRESHOLD 2 /* required by OCC spec */

#define OCC_STATE_SAFE   4
#define OCC_SAFE_TIMEOUT  msecs_to_jiffies(60000) /* 1 min */

#define OCC_UPDATE_FREQUENCY  msecs_to_jiffies(1000)

#define OCC_TEMP_SENSOR_FAULT  0xFF

#define OCC_FRU_TYPE_VRM  3

/* OCC sensor type and version definitions */

struct temp_sensor_1 {
 u16 sensor_id;
 u16 value;
} __packed;

struct temp_sensor_2 {
 u32 sensor_id;
 u8 fru_type;
 u8 value;
} __packed;

struct temp_sensor_10 {
 u32 sensor_id;
 u8 fru_type;
 u8 value;
 u8 throttle;
 u8 reserved;
} __packed;

struct freq_sensor_1 {
 u16 sensor_id;
 u16 value;
} __packed;

struct freq_sensor_2 {
 u32 sensor_id;
 u16 value;
} __packed;

struct power_sensor_1 {
 u16 sensor_id;
 u32 update_tag;
 u32 accumulator;
 u16 value;
} __packed;

struct power_sensor_2 {
 u32 sensor_id;
 u8 function_id;
 u8 apss_channel;
 u16 reserved;
 u32 update_tag;
 u64 accumulator;
 u16 value;
} __packed;

struct power_sensor_data {
 u16 value;
 u32 update_tag;
 u64 accumulator;
} __packed;

struct power_sensor_data_and_time {
 u16 update_time;
 u16 value;
 u32 update_tag;
 u64 accumulator;
} __packed;

struct power_sensor_a0 {
 u32 sensor_id;
 struct power_sensor_data_and_time system;
 u32 reserved;
 struct power_sensor_data_and_time proc;
 struct power_sensor_data vdd;
 struct power_sensor_data vdn;
} __packed;

struct caps_sensor_2 {
 u16 cap;
 u16 system_power;
 u16 n_cap;
 u16 max;
 u16 min;
 u16 user;
 u8 user_source;
} __packed;

struct caps_sensor_3 {
 u16 cap;
 u16 system_power;
 u16 n_cap;
 u16 max;
 u16 hard_min;
 u16 soft_min;
 u16 user;
 u8 user_source;
} __packed;

struct extended_sensor {
 union {
  u8 name[4];
  u32 sensor_id;
 };
 u8 flags;
 u8 reserved;
 u8 data[6];
} __packed;

static int occ_poll(struct occ *occ)
{
 int rc;
 u8 cmd[7];
 struct occ_poll_response_header *header;

 /* big endian */
 cmd[0] = 0;   /* sequence number */
 cmd[1] = 0;   /* cmd type */
 cmd[2] = 0;   /* data length msb */
 cmd[3] = 1;   /* data length lsb */
 cmd[4] = occ->poll_cmd_data; /* data */
 cmd[5] = 0;   /* checksum msb */
 cmd[6] = 0;   /* checksum lsb */

 /* mutex should already be locked if necessary */
 rc = occ->send_cmd(occ, cmd, sizeof(cmd), &occ->resp, sizeof(occ->resp));
 if (rc) {
  occ->last_error = rc;
  if (occ->error_count++ > OCC_ERROR_COUNT_THRESHOLD)
   occ->error = rc;

  goto done;
 }

 /* clear error since communication was successful */
 occ->error_count = 0;
 occ->last_error = 0;
 occ->error = 0;

 /* check for safe state */
 header = (struct occ_poll_response_header *)occ->resp.data;
 if (header->occ_state == OCC_STATE_SAFE) {
  if (occ->last_safe) {
   if (time_after(jiffies,
           occ->last_safe + OCC_SAFE_TIMEOUT))
    occ->error = -EHOSTDOWN;
  } else {
   occ->last_safe = jiffies;
  }
 } else {
  occ->last_safe = 0;
 }

done:
 occ_sysfs_poll_done(occ);
 return rc;
}

static int occ_set_user_power_cap(struct occ *occ, u16 user_power_cap)
{
 int rc;
 u8 cmd[8];
 u8 resp[8];
 __be16 user_power_cap_be = cpu_to_be16(user_power_cap);

 cmd[0] = 0; /* sequence number */
 cmd[1] = 0x22; /* cmd type */
 cmd[2] = 0; /* data length msb */
 cmd[3] = 2; /* data length lsb */

 memcpy(&cmd[4], &user_power_cap_be, 2);

 cmd[6] = 0; /* checksum msb */
 cmd[7] = 0; /* checksum lsb */

 rc = mutex_lock_interruptible(&occ->lock);
 if (rc)
  return rc;

 rc = occ->send_cmd(occ, cmd, sizeof(cmd), resp, sizeof(resp));

 mutex_unlock(&occ->lock);

 return rc;
}

int occ_update_response(struct occ *occ)
{
 int rc = mutex_lock_interruptible(&occ->lock);

 if (rc)
  return rc;

 /* limit the maximum rate of polling the OCC */
 if (time_after(jiffies, occ->next_update)) {
  rc = occ_poll(occ);
  occ->next_update = jiffies + OCC_UPDATE_FREQUENCY;
 } else {
  rc = occ->last_error;
 }

 mutex_unlock(&occ->lock);
 return rc;
}

static ssize_t occ_show_temp_1(struct device *dev,
          struct device_attribute *attr, char *buf)
{
 int rc;
 u32 val = 0;
 struct temp_sensor_1 *temp;
 struct occ *occ = dev_get_drvdata(dev);
 struct occ_sensors *sensors = &occ->sensors;
 struct sensor_device_attribute_2 *sattr = to_sensor_dev_attr_2(attr);

 rc = occ_update_response(occ);
 if (rc)
  return rc;

 temp = ((struct temp_sensor_1 *)sensors->temp.data) + sattr->index;

 switch (sattr->nr) {
 case 0:
  val = get_unaligned_be16(&temp->sensor_id);
  break;
 case 1:
  /*
 * If a sensor reading has expired and couldn't be refreshed,
 * OCC returns 0xFFFF for that sensor.
 */

  if (temp->value == 0xFFFF)
   return -EREMOTEIO;
  val = get_unaligned_be16(&temp->value) * 1000;
  break;
 default:
  return -EINVAL;
 }

 return sysfs_emit(buf, "%u\n", val);
}

static ssize_t occ_show_temp_2(struct device *dev,
          struct device_attribute *attr, char *buf)
{
 int rc;
 u32 val = 0;
 struct temp_sensor_2 *temp;
 struct occ *occ = dev_get_drvdata(dev);
 struct occ_sensors *sensors = &occ->sensors;
 struct sensor_device_attribute_2 *sattr = to_sensor_dev_attr_2(attr);

 rc = occ_update_response(occ);
 if (rc)
  return rc;

 temp = ((struct temp_sensor_2 *)sensors->temp.data) + sattr->index;

 switch (sattr->nr) {
 case 0:
  val = get_unaligned_be32(&temp->sensor_id);
  break;
 case 1:
  val = temp->value;
  if (val == OCC_TEMP_SENSOR_FAULT)
   return -EREMOTEIO;

  /*
 * VRM doesn't return temperature, only alarm bit. This
 * attribute maps to tempX_alarm instead of tempX_input for
 * VRM
 */

  if (temp->fru_type != OCC_FRU_TYPE_VRM) {
   /* sensor not ready */
   if (val == 0)
    return -EAGAIN;

   val *= 1000;
  }
  break;
 case 2:
  val = temp->fru_type;
  break;
 case 3:
  val = temp->value == OCC_TEMP_SENSOR_FAULT;
  break;
 default:
  return -EINVAL;
 }

 return sysfs_emit(buf, "%u\n", val);
}

static ssize_t occ_show_temp_10(struct device *dev,
    struct device_attribute *attr, char *buf)
{
 int rc;
 u32 val = 0;
 struct temp_sensor_10 *temp;
 struct occ *occ = dev_get_drvdata(dev);
 struct occ_sensors *sensors = &occ->sensors;
 struct sensor_device_attribute_2 *sattr = to_sensor_dev_attr_2(attr);

 rc = occ_update_response(occ);
 if (rc)
  return rc;

 temp = ((struct temp_sensor_10 *)sensors->temp.data) + sattr->index;

 switch (sattr->nr) {
 case 0:
  val = get_unaligned_be32(&temp->sensor_id);
  break;
 case 1:
  val = temp->value;
  if (val == OCC_TEMP_SENSOR_FAULT)
   return -EREMOTEIO;

  /* sensor not ready */
  if (val == 0)
   return -EAGAIN;

  val *= 1000;
  break;
 case 2:
  val = temp->fru_type;
  break;
 case 3:
  val = temp->value == OCC_TEMP_SENSOR_FAULT;
  break;
 case 4:
  val = temp->throttle * 1000;
  break;
 default:
  return -EINVAL;
 }

 return sysfs_emit(buf, "%u\n", val);
}

static ssize_t occ_show_freq_1(struct device *dev,
          struct device_attribute *attr, char *buf)
{
 int rc;
 u16 val = 0;
 struct freq_sensor_1 *freq;
 struct occ *occ = dev_get_drvdata(dev);
 struct occ_sensors *sensors = &occ->sensors;
 struct sensor_device_attribute_2 *sattr = to_sensor_dev_attr_2(attr);

 rc = occ_update_response(occ);
 if (rc)
  return rc;

 freq = ((struct freq_sensor_1 *)sensors->freq.data) + sattr->index;

 switch (sattr->nr) {
 case 0:
  val = get_unaligned_be16(&freq->sensor_id);
  break;
 case 1:
  val = get_unaligned_be16(&freq->value);
  break;
 default:
  return -EINVAL;
 }

 return sysfs_emit(buf, "%u\n", val);
}

static ssize_t occ_show_freq_2(struct device *dev,
          struct device_attribute *attr, char *buf)
{
 int rc;
 u32 val = 0;
 struct freq_sensor_2 *freq;
 struct occ *occ = dev_get_drvdata(dev);
 struct occ_sensors *sensors = &occ->sensors;
 struct sensor_device_attribute_2 *sattr = to_sensor_dev_attr_2(attr);

 rc = occ_update_response(occ);
 if (rc)
  return rc;

 freq = ((struct freq_sensor_2 *)sensors->freq.data) + sattr->index;

 switch (sattr->nr) {
 case 0:
  val = get_unaligned_be32(&freq->sensor_id);
  break;
 case 1:
  val = get_unaligned_be16(&freq->value);
  break;
 default:
  return -EINVAL;
 }

 return sysfs_emit(buf, "%u\n", val);
}

static ssize_t occ_show_power_1(struct device *dev,
    struct device_attribute *attr, char *buf)
{
 int rc;
 u64 val = 0;
 struct power_sensor_1 *power;
 struct occ *occ = dev_get_drvdata(dev);
 struct occ_sensors *sensors = &occ->sensors;
 struct sensor_device_attribute_2 *sattr = to_sensor_dev_attr_2(attr);

 rc = occ_update_response(occ);
 if (rc)
  return rc;

 power = ((struct power_sensor_1 *)sensors->power.data) + sattr->index;

 switch (sattr->nr) {
 case 0:
  val = get_unaligned_be16(&power->sensor_id);
  break;
 case 1:
  val = get_unaligned_be32(&power->accumulator) /
   get_unaligned_be32(&power->update_tag);
  val *= 1000000ULL;
  break;
 case 2:
  val = (u64)get_unaligned_be32(&power->update_tag) *
      occ->powr_sample_time_us;
  break;
 case 3:
  val = get_unaligned_be16(&power->value) * 1000000ULL;
  break;
 default:
  return -EINVAL;
 }

 return sysfs_emit(buf, "%llu\n", val);
}

static u64 occ_get_powr_avg(u64 accum, u32 samples)
{
 return (samples == 0) ? 0 :
  mul_u64_u32_div(accum, 1000000UL, samples);
}

static ssize_t occ_show_power_2(struct device *dev,
    struct device_attribute *attr, char *buf)
{
 int rc;
 u64 val = 0;
 struct power_sensor_2 *power;
 struct occ *occ = dev_get_drvdata(dev);
 struct occ_sensors *sensors = &occ->sensors;
 struct sensor_device_attribute_2 *sattr = to_sensor_dev_attr_2(attr);

 rc = occ_update_response(occ);
 if (rc)
  return rc;

 power = ((struct power_sensor_2 *)sensors->power.data) + sattr->index;

 switch (sattr->nr) {
 case 0:
  return sysfs_emit(buf, "%u_%u_%u\n",
      get_unaligned_be32(&power->sensor_id),
      power->function_id, power->apss_channel);
 case 1:
  val = occ_get_powr_avg(get_unaligned_be64(&power->accumulator),
           get_unaligned_be32(&power->update_tag));
  break;
 case 2:
  val = (u64)get_unaligned_be32(&power->update_tag) *
      occ->powr_sample_time_us;
  break;
 case 3:
  val = get_unaligned_be16(&power->value) * 1000000ULL;
  break;
 default:
  return -EINVAL;
 }

 return sysfs_emit(buf, "%llu\n", val);
}

static ssize_t occ_show_power_a0(struct device *dev,
     struct device_attribute *attr, char *buf)
{
 int rc;
 u64 val = 0;
 struct power_sensor_a0 *power;
 struct occ *occ = dev_get_drvdata(dev);
 struct occ_sensors *sensors = &occ->sensors;
 struct sensor_device_attribute_2 *sattr = to_sensor_dev_attr_2(attr);

 rc = occ_update_response(occ);
 if (rc)
  return rc;

 power = ((struct power_sensor_a0 *)sensors->power.data) + sattr->index;

 switch (sattr->nr) {
 case 0:
  return sysfs_emit(buf, "%u_system\n",
      get_unaligned_be32(&power->sensor_id));
 case 1:
  val = occ_get_powr_avg(get_unaligned_be64(&power->system.accumulator),
           get_unaligned_be32(&power->system.update_tag));
  break;
 case 2:
  val = (u64)get_unaligned_be32(&power->system.update_tag) *
      occ->powr_sample_time_us;
  break;
 case 3:
  val = get_unaligned_be16(&power->system.value) * 1000000ULL;
  break;
 case 4:
  return sysfs_emit(buf, "%u_proc\n",
      get_unaligned_be32(&power->sensor_id));
 case 5:
  val = occ_get_powr_avg(get_unaligned_be64(&power->proc.accumulator),
           get_unaligned_be32(&power->proc.update_tag));
  break;
 case 6:
  val = (u64)get_unaligned_be32(&power->proc.update_tag) *
      occ->powr_sample_time_us;
  break;
 case 7:
  val = get_unaligned_be16(&power->proc.value) * 1000000ULL;
  break;
 case 8:
  return sysfs_emit(buf, "%u_vdd\n",
      get_unaligned_be32(&power->sensor_id));
 case 9:
  val = occ_get_powr_avg(get_unaligned_be64(&power->vdd.accumulator),
           get_unaligned_be32(&power->vdd.update_tag));
  break;
 case 10:
  val = (u64)get_unaligned_be32(&power->vdd.update_tag) *
      occ->powr_sample_time_us;
  break;
 case 11:
  val = get_unaligned_be16(&power->vdd.value) * 1000000ULL;
  break;
 case 12:
  return sysfs_emit(buf, "%u_vdn\n",
      get_unaligned_be32(&power->sensor_id));
 case 13:
  val = occ_get_powr_avg(get_unaligned_be64(&power->vdn.accumulator),
           get_unaligned_be32(&power->vdn.update_tag));
  break;
 case 14:
  val = (u64)get_unaligned_be32(&power->vdn.update_tag) *
      occ->powr_sample_time_us;
  break;
 case 15:
  val = get_unaligned_be16(&power->vdn.value) * 1000000ULL;
  break;
 default:
  return -EINVAL;
 }

 return sysfs_emit(buf, "%llu\n", val);
}

static ssize_t occ_show_caps_1_2(struct device *dev,
     struct device_attribute *attr, char *buf)
{
 int rc;
 u64 val = 0;
 struct caps_sensor_2 *caps;
 struct occ *occ = dev_get_drvdata(dev);
 struct occ_sensors *sensors = &occ->sensors;
 struct sensor_device_attribute_2 *sattr = to_sensor_dev_attr_2(attr);

 rc = occ_update_response(occ);
 if (rc)
  return rc;

 caps = ((struct caps_sensor_2 *)sensors->caps.data) + sattr->index;

 switch (sattr->nr) {
 case 0:
  return sysfs_emit(buf, "system\n");
 case 1:
  val = get_unaligned_be16(&caps->cap) * 1000000ULL;
  break;
 case 2:
  val = get_unaligned_be16(&caps->system_power) * 1000000ULL;
  break;
 case 3:
  val = get_unaligned_be16(&caps->n_cap) * 1000000ULL;
  break;
 case 4:
  val = get_unaligned_be16(&caps->max) * 1000000ULL;
  break;
 case 5:
  val = get_unaligned_be16(&caps->min) * 1000000ULL;
  break;
 case 6:
  val = get_unaligned_be16(&caps->user) * 1000000ULL;
  break;
 case 7:
  if (occ->sensors.caps.version == 1)
   return -EINVAL;

  val = caps->user_source;
  break;
 default:
  return -EINVAL;
 }

 return sysfs_emit(buf, "%llu\n", val);
}

static ssize_t occ_show_caps_3(struct device *dev,
          struct device_attribute *attr, char *buf)
{
 int rc;
 u64 val = 0;
 struct caps_sensor_3 *caps;
 struct occ *occ = dev_get_drvdata(dev);
 struct occ_sensors *sensors = &occ->sensors;
 struct sensor_device_attribute_2 *sattr = to_sensor_dev_attr_2(attr);

 rc = occ_update_response(occ);
 if (rc)
  return rc;

 caps = ((struct caps_sensor_3 *)sensors->caps.data) + sattr->index;

 switch (sattr->nr) {
 case 0:
  return sysfs_emit(buf, "system\n");
 case 1:
  val = get_unaligned_be16(&caps->cap) * 1000000ULL;
  break;
 case 2:
  val = get_unaligned_be16(&caps->system_power) * 1000000ULL;
  break;
 case 3:
  val = get_unaligned_be16(&caps->n_cap) * 1000000ULL;
  break;
 case 4:
  val = get_unaligned_be16(&caps->max) * 1000000ULL;
  break;
 case 5:
  val = get_unaligned_be16(&caps->hard_min) * 1000000ULL;
  break;
 case 6:
  val = get_unaligned_be16(&caps->user) * 1000000ULL;
  break;
 case 7:
  val = caps->user_source;
  break;
 case 8:
  val = get_unaligned_be16(&caps->soft_min) * 1000000ULL;
  break;
 default:
  return -EINVAL;
 }

 return sysfs_emit(buf, "%llu\n", val);
}

static ssize_t occ_store_caps_user(struct device *dev,
       struct device_attribute *attr,
       const char *buf, size_t count)
{
 int rc;
 u16 user_power_cap;
 unsigned long long value;
 struct occ *occ = dev_get_drvdata(dev);

 rc = kstrtoull(buf, 0, &value);
 if (rc)
  return rc;

 user_power_cap = div64_u64(value, 1000000ULL); /* microwatt to watt */

 rc = occ_set_user_power_cap(occ, user_power_cap);
 if (rc)
  return rc;

 return count;
}

static ssize_t occ_show_extended(struct device *dev,
     struct device_attribute *attr, char *buf)
{
 int rc;
 struct extended_sensor *extn;
 struct occ *occ = dev_get_drvdata(dev);
 struct occ_sensors *sensors = &occ->sensors;
 struct sensor_device_attribute_2 *sattr = to_sensor_dev_attr_2(attr);

 rc = occ_update_response(occ);
 if (rc)
  return rc;

 extn = ((struct extended_sensor *)sensors->extended.data) +
  sattr->index;

 switch (sattr->nr) {
 case 0:
  if (extn->flags & EXTN_FLAG_SENSOR_ID) {
   rc = sysfs_emit(buf, "%u",
     get_unaligned_be32(&extn->sensor_id));
  } else {
   rc = sysfs_emit(buf, "%4phN\n", extn->name);
  }
  break;
 case 1:
  rc = sysfs_emit(buf, "%02x\n", extn->flags);
  break;
 case 2:
  rc = sysfs_emit(buf, "%6phN\n", extn->data);
  break;
 default:
  return -EINVAL;
 }

 return rc;
}

/*
 * A helper to make it easier to define an occ_attribute. Since these
 * are dynamically allocated, we cannot use the existing kernel macros which
 * stringify the name argument.
 */

static void occ_init_attribute(struct occ_attribute *attr, int mode,
 ssize_t (*show)(struct device *dev, struct device_attribute *attr, char *buf),
 ssize_t (*store)(struct device *dev, struct device_attribute *attr,
       const char *buf, size_t count),
 int nr, int index, const char *fmt, ...)
{
 va_list args;

 va_start(args, fmt);
 vsnprintf(attr->name, sizeof(attr->name), fmt, args);
 va_end(args);

 attr->sensor.dev_attr.attr.name = attr->name;
 attr->sensor.dev_attr.attr.mode = mode;
 attr->sensor.dev_attr.show = show;
 attr->sensor.dev_attr.store = store;
 attr->sensor.index = index;
 attr->sensor.nr = nr;
}

/*
 * Allocate and instatiate sensor_device_attribute_2s. It's most efficient to
 * use our own instead of the built-in hwmon attribute types.
 */

static int occ_setup_sensor_attrs(struct occ *occ)
{
 unsigned int i, s, num_attrs = 0;
 struct device *dev = occ->bus_dev;
 struct occ_sensors *sensors = &occ->sensors;
 struct occ_attribute *attr;
 struct temp_sensor_2 *temp;
 ssize_t (*show_temp)(struct device *, struct device_attribute *,
        char *) = occ_show_temp_1;
 ssize_t (*show_freq)(struct device *, struct device_attribute *,
        char *) = occ_show_freq_1;
 ssize_t (*show_power)(struct device *, struct device_attribute *,
         char *) = occ_show_power_1;
 ssize_t (*show_caps)(struct device *, struct device_attribute *,
        char *) = occ_show_caps_1_2;

 switch (sensors->temp.version) {
 case 1:
  num_attrs += (sensors->temp.num_sensors * 2);
  break;
 case 2:
  num_attrs += (sensors->temp.num_sensors * 4);
  show_temp = occ_show_temp_2;
  break;
 case 0x10:
  num_attrs += (sensors->temp.num_sensors * 5);
  show_temp = occ_show_temp_10;
  break;
 default:
  sensors->temp.num_sensors = 0;
 }

 switch (sensors->freq.version) {
 case 2:
  show_freq = occ_show_freq_2;
  fallthrough;
 case 1:
  num_attrs += (sensors->freq.num_sensors * 2);
  break;
 default:
  sensors->freq.num_sensors = 0;
 }

 switch (sensors->power.version) {
 case 2:
  show_power = occ_show_power_2;
  fallthrough;
 case 1:
  num_attrs += (sensors->power.num_sensors * 4);
  break;
 case 0xA0:
  num_attrs += (sensors->power.num_sensors * 16);
  show_power = occ_show_power_a0;
  break;
 default:
  sensors->power.num_sensors = 0;
 }

 switch (sensors->caps.version) {
 case 1:
  num_attrs += (sensors->caps.num_sensors * 7);
  break;
 case 2:
  num_attrs += (sensors->caps.num_sensors * 8);
  break;
 case 3:
  show_caps = occ_show_caps_3;
  num_attrs += (sensors->caps.num_sensors * 9);
  break;
 default:
  sensors->caps.num_sensors = 0;
 }

 switch (sensors->extended.version) {
 case 1:
  num_attrs += (sensors->extended.num_sensors * 3);
  break;
 default:
  sensors->extended.num_sensors = 0;
 }

 occ->attrs = devm_kcalloc(dev, num_attrs, sizeof(*occ->attrs),
      GFP_KERNEL);
 if (!occ->attrs)
  return -ENOMEM;

 /* null-terminated list */
 occ->group.attrs = devm_kcalloc(dev, num_attrs + 1,
     sizeof(*occ->group.attrs),
     GFP_KERNEL);
 if (!occ->group.attrs)
  return -ENOMEM;

 attr = occ->attrs;

 for (i = 0; i < sensors->temp.num_sensors; ++i) {
  s = i + 1;
  temp = ((struct temp_sensor_2 *)sensors->temp.data) + i;

  occ_init_attribute(attr, 0444, show_temp, NULL,
       0, i, "temp%d_label", s);
  attr++;

  if (sensors->temp.version == 2 &&
      temp->fru_type == OCC_FRU_TYPE_VRM) {
   occ_init_attribute(attr, 0444, show_temp, NULL,
        1, i, "temp%d_alarm", s);
  } else {
   occ_init_attribute(attr, 0444, show_temp, NULL,
        1, i, "temp%d_input", s);
  }

  attr++;

  if (sensors->temp.version > 1) {
   occ_init_attribute(attr, 0444, show_temp, NULL,
        2, i, "temp%d_fru_type", s);
   attr++;

   occ_init_attribute(attr, 0444, show_temp, NULL,
        3, i, "temp%d_fault", s);
   attr++;

   if (sensors->temp.version == 0x10) {
    occ_init_attribute(attr, 0444, show_temp, NULL,
         4, i, "temp%d_max", s);
    attr++;
   }
  }
 }

 for (i = 0; i < sensors->freq.num_sensors; ++i) {
  s = i + 1;

  occ_init_attribute(attr, 0444, show_freq, NULL,
       0, i, "freq%d_label", s);
  attr++;

  occ_init_attribute(attr, 0444, show_freq, NULL,
       1, i, "freq%d_input", s);
  attr++;
 }

 if (sensors->power.version == 0xA0) {
  /*
 * Special case for many-attribute power sensor. Split it into
 * a sensor number per power type, emulating several sensors.
 */

  for (i = 0; i < sensors->power.num_sensors; ++i) {
   unsigned int j;
   unsigned int nr = 0;

   s = (i * 4) + 1;

   for (j = 0; j < 4; ++j) {
    occ_init_attribute(attr, 0444, show_power,
         NULL, nr++, i,
         "power%d_label", s);
    attr++;

    occ_init_attribute(attr, 0444, show_power,
         NULL, nr++, i,
         "power%d_average", s);
    attr++;

    occ_init_attribute(attr, 0444, show_power,
         NULL, nr++, i,
         "power%d_average_interval", s);
    attr++;

    occ_init_attribute(attr, 0444, show_power,
         NULL, nr++, i,
         "power%d_input", s);
    attr++;

    s++;
   }
  }

  s = (sensors->power.num_sensors * 4) + 1;
 } else {
  for (i = 0; i < sensors->power.num_sensors; ++i) {
   s = i + 1;

   occ_init_attribute(attr, 0444, show_power, NULL,
        0, i, "power%d_label", s);
   attr++;

   occ_init_attribute(attr, 0444, show_power, NULL,
        1, i, "power%d_average", s);
   attr++;

   occ_init_attribute(attr, 0444, show_power, NULL,
        2, i, "power%d_average_interval", s);
   attr++;

   occ_init_attribute(attr, 0444, show_power, NULL,
        3, i, "power%d_input", s);
   attr++;
  }

  s = sensors->power.num_sensors + 1;
 }

 if (sensors->caps.num_sensors >= 1) {
  occ_init_attribute(attr, 0444, show_caps, NULL,
       0, 0, "power%d_label", s);
  attr++;

  occ_init_attribute(attr, 0444, show_caps, NULL,
       1, 0, "power%d_cap", s);
  attr++;

  occ_init_attribute(attr, 0444, show_caps, NULL,
       2, 0, "power%d_input", s);
  attr++;

  occ_init_attribute(attr, 0444, show_caps, NULL,
       3, 0, "power%d_cap_not_redundant", s);
  attr++;

  occ_init_attribute(attr, 0444, show_caps, NULL,
       4, 0, "power%d_cap_max", s);
  attr++;

  occ_init_attribute(attr, 0444, show_caps, NULL,
       5, 0, "power%d_cap_min", s);
  attr++;

  occ_init_attribute(attr, 0644, show_caps, occ_store_caps_user,
       6, 0, "power%d_cap_user", s);
  attr++;

  if (sensors->caps.version > 1) {
   occ_init_attribute(attr, 0444, show_caps, NULL,
        7, 0, "power%d_cap_user_source", s);
   attr++;

   if (sensors->caps.version > 2) {
    occ_init_attribute(attr, 0444, show_caps, NULL,
         8, 0,
         "power%d_cap_min_soft", s);
    attr++;
   }
  }
 }

 for (i = 0; i < sensors->extended.num_sensors; ++i) {
  s = i + 1;

  occ_init_attribute(attr, 0444, occ_show_extended, NULL,
       0, i, "extn%d_label", s);
  attr++;

  occ_init_attribute(attr, 0444, occ_show_extended, NULL,
       1, i, "extn%d_flags", s);
  attr++;

  occ_init_attribute(attr, 0444, occ_show_extended, NULL,
       2, i, "extn%d_input", s);
  attr++;
 }

 /* put the sensors in the group */
 for (i = 0; i < num_attrs; ++i) {
  sysfs_attr_init(&occ->attrs[i].sensor.dev_attr.attr);
  occ->group.attrs[i] = &occ->attrs[i].sensor.dev_attr.attr;
 }

 return 0;
}

/* only need to do this once at startup, as OCC won't change sensors on us */
static void occ_parse_poll_response(struct occ *occ)
{
 unsigned int i, old_offset, offset = 0, size = 0;
 struct occ_sensor *sensor;
 struct occ_sensors *sensors = &occ->sensors;
 struct occ_response *resp = &occ->resp;
 struct occ_poll_response *poll =
  (struct occ_poll_response *)&resp->data[0];
 struct occ_poll_response_header *header = &poll->header;
 struct occ_sensor_data_block *block = &poll->block;

 dev_info(occ->bus_dev, "OCC found, code level: %.16s\n",
   header->occ_code_level);

 for (i = 0; i < header->num_sensor_data_blocks; ++i) {
  block = (struct occ_sensor_data_block *)((u8 *)block + offset);
  old_offset = offset;
  offset = (block->header.num_sensors *
     block->header.sensor_length) + sizeof(block->header);
  size += offset;

  /* validate all the length/size fields */
  if ((size + sizeof(*header)) >= OCC_RESP_DATA_BYTES) {
   dev_warn(occ->bus_dev, "exceeded response buffer\n");
   return;
  }

  dev_dbg(occ->bus_dev, " %04x..%04x: %.4s (%d sensors)\n",
   old_offset, offset - 1, block->header.eye_catcher,
   block->header.num_sensors);

  /* match sensor block type */
  if (strncmp(block->header.eye_catcher, "TEMP", 4) == 0)
   sensor = &sensors->temp;
  else if (strncmp(block->header.eye_catcher, "FREQ", 4) == 0)
   sensor = &sensors->freq;
  else if (strncmp(block->header.eye_catcher, "POWR", 4) == 0)
   sensor = &sensors->power;
  else if (strncmp(block->header.eye_catcher, "CAPS", 4) == 0)
   sensor = &sensors->caps;
  else if (strncmp(block->header.eye_catcher, "EXTN", 4) == 0)
   sensor = &sensors->extended;
  else {
   dev_warn(occ->bus_dev, "sensor not supported %.4s\n",
     block->header.eye_catcher);
   continue;
  }

  sensor->num_sensors = block->header.num_sensors;
  sensor->version = block->header.sensor_format;
  sensor->data = &block->data;
 }

 dev_dbg(occ->bus_dev, "Max resp size: %u+%zd=%zd\n", size,
  sizeof(*header), size + sizeof(*header));
}

int occ_active(struct occ *occ, bool active)
{
 int rc = mutex_lock_interruptible(&occ->lock);

 if (rc)
  return rc;

 if (active) {
  if (occ->active) {
   rc = -EALREADY;
   goto unlock;
  }

  occ->error_count = 0;
  occ->last_safe = 0;

  rc = occ_poll(occ);
  if (rc < 0) {
   dev_err(occ->bus_dev,
    "failed to get OCC poll response=%02x: %d\n",
    occ->resp.return_status, rc);
   goto unlock;
  }

  occ->active = true;
  occ->next_update = jiffies + OCC_UPDATE_FREQUENCY;
  occ_parse_poll_response(occ);

  rc = occ_setup_sensor_attrs(occ);
  if (rc) {
   dev_err(occ->bus_dev,
    "failed to setup sensor attrs: %d\n", rc);
   goto unlock;
  }

  occ->hwmon = hwmon_device_register_with_groups(occ->bus_dev,
              "occ", occ,
              occ->groups);
  if (IS_ERR(occ->hwmon)) {
   rc = PTR_ERR(occ->hwmon);
   occ->hwmon = NULL;
   dev_err(occ->bus_dev,
    "failed to register hwmon device: %d\n", rc);
   goto unlock;
  }
 } else {
  if (!occ->active) {
   rc = -EALREADY;
   goto unlock;
  }

  if (occ->hwmon)
   hwmon_device_unregister(occ->hwmon);
  occ->active = false;
  occ->hwmon = NULL;
 }

unlock:
 mutex_unlock(&occ->lock);
 return rc;
}

int occ_setup(struct occ *occ)
{
 int rc;

 mutex_init(&occ->lock);
 occ->groups[0] = &occ->group;

 rc = occ_setup_sysfs(occ);
 if (rc) {
  dev_err(occ->bus_dev, "failed to setup sysfs: %d\n", rc);
  return rc;
 }

 if (!device_property_read_bool(occ->bus_dev, "ibm,no-poll-on-init")) {
  rc = occ_active(occ, true);
  if (rc)
   occ_shutdown_sysfs(occ);
 }

 return rc;
}
EXPORT_SYMBOL_GPL(occ_setup);

void occ_shutdown(struct occ *occ)
{
 mutex_lock(&occ->lock);

 occ_shutdown_sysfs(occ);

 if (occ->hwmon)
  hwmon_device_unregister(occ->hwmon);
 occ->hwmon = NULL;

 mutex_unlock(&occ->lock);
}
EXPORT_SYMBOL_GPL(occ_shutdown);

MODULE_AUTHOR("Eddie James ");
MODULE_DESCRIPTION("Common OCC hwmon code");
MODULE_LICENSE("GPL");

Messung V0.5
C=98 H=95 G=96

¤ Dauer der Verarbeitung: 0.17 Sekunden  (vorverarbeitet)  ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

Beweissystem der NASA

Beweissystem Isabelle

NIST Cobol Testsuite

Cephes Mathematical Library

Wiener Entwicklungsmethode

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.