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

Quelle  sg2042-mcu.c   Sprache: C

 
// SPDX-License-Identifier: GPL-2.0-only
/*
 * Copyright (c) 2024 Inochi Amaoto <inochiama@outlook.com>
 *
 * Sophgo power control mcu for SG2042
 */


#include <linux/cleanup.h>
#include <linux/debugfs.h>
#include <linux/err.h>
#include <linux/hwmon.h>
#include <linux/i2c.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/mutex.h>

/* fixed MCU registers */
#define REG_BOARD_TYPE    0x00
#define REG_MCU_FIRMWARE_VERSION  0x01
#define REG_PCB_VERSION    0x02
#define REG_PWR_CTRL    0x03
#define REG_SOC_TEMP    0x04
#define REG_BOARD_TEMP    0x05
#define REG_RST_COUNT    0x0a
#define REG_UPTIME    0x0b
#define REG_RESET_REASON   0x0d
#define REG_MCU_TYPE    0x18
#define REG_REPOWER_POLICY   0x65
#define REG_CRITICAL_TEMP   0x66
#define REG_REPOWER_TEMP   0x67

#define REPOWER_POLICY_REBOOT   1
#define REPOWER_POLICY_KEEP_OFF   2

#define MCU_POWER_MAX    0xff

#define DEFINE_MCU_DEBUG_ATTR(_name, _reg, _format)   \
 static int _name##_show(struct seq_file *seqf,   \
        void *unused)   \
 {        \
  struct sg2042_mcu_data *mcu = seqf->private;  \
  int ret;      \
  ret = i2c_smbus_read_byte_data(mcu->client, (_reg)); \
  if (ret < 0)      \
   return ret;     \
  seq_printf(seqf, _format "\n", ret);   \
  return 0;      \
 }        \
 DEFINE_SHOW_ATTRIBUTE(_name)     \

struct sg2042_mcu_data {
 struct i2c_client *client;
 struct mutex  mutex;
};

static ssize_t reset_count_show(struct device *dev,
    struct device_attribute *attr,
    char *buf)
{
 struct sg2042_mcu_data *mcu = dev_get_drvdata(dev);
 int ret;

 ret = i2c_smbus_read_byte_data(mcu->client, REG_RST_COUNT);
 if (ret < 0)
  return ret;

 return sprintf(buf, "%d\n", ret);
}

static ssize_t uptime_show(struct device *dev,
      struct device_attribute *attr,
      char *buf)
{
 struct sg2042_mcu_data *mcu = dev_get_drvdata(dev);
 u8 time_val[2];
 int ret;

 ret = i2c_smbus_read_i2c_block_data(mcu->client, REG_UPTIME,
         sizeof(time_val), time_val);
 if (ret < 0)
  return ret;

 return sprintf(buf, "%d\n",
         (time_val[0]) | (time_val[1] << 8));
}

static ssize_t reset_reason_show(struct device *dev,
     struct device_attribute *attr,
     char *buf)
{
 struct sg2042_mcu_data *mcu = dev_get_drvdata(dev);
 int ret;

 ret = i2c_smbus_read_byte_data(mcu->client, REG_RESET_REASON);
 if (ret < 0)
  return ret;

 return sprintf(buf, "0x%02x\n", ret);
}

static ssize_t repower_policy_show(struct device *dev,
       struct device_attribute *attr,
       char *buf)
{
 struct sg2042_mcu_data *mcu = dev_get_drvdata(dev);
 int ret;
 const char *action;

 ret = i2c_smbus_read_byte_data(mcu->client, REG_REPOWER_POLICY);
 if (ret < 0)
  return ret;

 if (ret == REPOWER_POLICY_REBOOT)
  action = "repower";
 else if (ret == REPOWER_POLICY_KEEP_OFF)
  action = "keep";
 else
  action = "unknown";

 return sprintf(buf, "%s\n", action);
}

static ssize_t repower_policy_store(struct device *dev,
        struct device_attribute *attr,
        const char *buf, size_t count)
{
 struct sg2042_mcu_data *mcu = dev_get_drvdata(dev);
 u8 value;
 int ret;

 if (sysfs_streq("repower", buf))
  value = REPOWER_POLICY_REBOOT;
 else if (sysfs_streq("keep", buf))
  value = REPOWER_POLICY_KEEP_OFF;
 else
  return -EINVAL;

 ret = i2c_smbus_write_byte_data(mcu->client,
     REG_REPOWER_POLICY, value);
 if (ret < 0)
  return ret;

 return count;
}

static DEVICE_ATTR_RO(reset_count);
static DEVICE_ATTR_RO(uptime);
static DEVICE_ATTR_RO(reset_reason);
static DEVICE_ATTR_RW(repower_policy);

DEFINE_MCU_DEBUG_ATTR(firmware_version, REG_MCU_FIRMWARE_VERSION, "0x%02x");
DEFINE_MCU_DEBUG_ATTR(pcb_version, REG_PCB_VERSION, "0x%02x");
DEFINE_MCU_DEBUG_ATTR(board_type, REG_BOARD_TYPE, "0x%02x");
DEFINE_MCU_DEBUG_ATTR(mcu_type, REG_MCU_TYPE, "%d");

static struct attribute *sg2042_mcu_attrs[] = {
 &dev_attr_reset_count.attr,
 &dev_attr_uptime.attr,
 &dev_attr_reset_reason.attr,
 &dev_attr_repower_policy.attr,
 NULL
};

static const struct attribute_group sg2042_mcu_attr_group = {
 .attrs = sg2042_mcu_attrs,
};

static const struct attribute_group *sg2042_mcu_groups[] = {
 &sg2042_mcu_attr_group,
 NULL
};

static const struct hwmon_channel_info * const sg2042_mcu_info[] = {
 HWMON_CHANNEL_INFO(chip, HWMON_C_REGISTER_TZ),
 HWMON_CHANNEL_INFO(temp, HWMON_T_INPUT | HWMON_T_CRIT |
     HWMON_T_CRIT_HYST,
     HWMON_T_INPUT),
 NULL
};

static int sg2042_mcu_read(struct device *dev,
      enum hwmon_sensor_types type,
      u32 attr, int channel, long *val)
{
 struct sg2042_mcu_data *mcu = dev_get_drvdata(dev);
 int tmp;
 u8 reg;

 switch (attr) {
 case hwmon_temp_input:
  reg = channel ? REG_BOARD_TEMP : REG_SOC_TEMP;
  break;
 case hwmon_temp_crit:
  reg = REG_CRITICAL_TEMP;
  break;
 case hwmon_temp_crit_hyst:
  reg = REG_REPOWER_TEMP;
  break;
 default:
  return -EOPNOTSUPP;
 }

 tmp = i2c_smbus_read_byte_data(mcu->client, reg);
 if (tmp < 0)
  return tmp;
 *val = tmp * 1000;

 return 0;
}

static int sg2042_mcu_write(struct device *dev,
       enum hwmon_sensor_types type,
       u32 attr, int channel, long val)
{
 struct sg2042_mcu_data *mcu = dev_get_drvdata(dev);
 int temp = val / 1000;
 int hyst_temp, crit_temp;
 u8 reg;

 temp = clamp_val(temp, 0, MCU_POWER_MAX);

 guard(mutex)(&mcu->mutex);

 switch (attr) {
 case hwmon_temp_crit:
  hyst_temp = i2c_smbus_read_byte_data(mcu->client,
           REG_REPOWER_TEMP);
  if (hyst_temp < 0)
   return hyst_temp;

  crit_temp = temp;
  reg = REG_CRITICAL_TEMP;
  break;
 case hwmon_temp_crit_hyst:
  crit_temp = i2c_smbus_read_byte_data(mcu->client,
           REG_CRITICAL_TEMP);
  if (crit_temp < 0)
   return crit_temp;

  hyst_temp = temp;
  reg = REG_REPOWER_TEMP;
  break;
 default:
  return -EOPNOTSUPP;
 }

 /*
 * ensure hyst_temp is smaller to avoid MCU from
 * keeping triggering repower event.
 */

 if (crit_temp < hyst_temp)
  return -EINVAL;

 return i2c_smbus_write_byte_data(mcu->client, reg, temp);
}

static umode_t sg2042_mcu_is_visible(const void *_data,
         enum hwmon_sensor_types type,
         u32 attr, int channel)
{
 switch (type) {
 case hwmon_temp:
  switch (attr) {
  case hwmon_temp_input:
   return 0444;
  case hwmon_temp_crit:
  case hwmon_temp_crit_hyst:
   if (channel == 0)
    return 0644;
   break;
  default:
   break;
  }
  break;
 default:
   break;
 }
 return 0;
}

static const struct hwmon_ops sg2042_mcu_ops = {
 .is_visible = sg2042_mcu_is_visible,
 .read = sg2042_mcu_read,
 .write = sg2042_mcu_write,
};

static const struct hwmon_chip_info sg2042_mcu_chip_info = {
 .ops = &sg2042_mcu_ops,
 .info = sg2042_mcu_info,
};

static void sg2042_mcu_debugfs_init(struct sg2042_mcu_data *mcu)
{
 debugfs_create_file("firmware_version", 0444, mcu->client->debugfs,
       mcu, &firmware_version_fops);
 debugfs_create_file("pcb_version", 0444, mcu->client->debugfs, mcu,
       &pcb_version_fops);
 debugfs_create_file("mcu_type", 0444, mcu->client->debugfs, mcu,
       &mcu_type_fops);
 debugfs_create_file("board_type", 0444, mcu->client->debugfs, mcu,
       &board_type_fops);
}

static int sg2042_mcu_i2c_probe(struct i2c_client *client)
{
 struct device *dev = &client->dev;
 struct sg2042_mcu_data *mcu;
 struct device *hwmon_dev;

 if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA |
      I2C_FUNC_SMBUS_BLOCK_DATA))
  return -ENODEV;

 mcu = devm_kmalloc(dev, sizeof(*mcu), GFP_KERNEL);
 if (!mcu)
  return -ENOMEM;

 mutex_init(&mcu->mutex);
 mcu->client = client;

 i2c_set_clientdata(client, mcu);

 hwmon_dev = devm_hwmon_device_register_with_info(dev, "sg2042_mcu",
        mcu,
        &sg2042_mcu_chip_info,
        NULL);
 if (IS_ERR(hwmon_dev))
  return PTR_ERR(hwmon_dev);

 sg2042_mcu_debugfs_init(mcu);

 return 0;
}

static const struct i2c_device_id sg2042_mcu_id[] = {
 { "sg2042-hwmon-mcu" },
 { }
};
MODULE_DEVICE_TABLE(i2c, sg2042_mcu_id);

static const struct of_device_id sg2042_mcu_of_id[] = {
 { .compatible = "sophgo,sg2042-hwmon-mcu" },
 {},
};
MODULE_DEVICE_TABLE(of, sg2042_mcu_of_id);

static struct i2c_driver sg2042_mcu_driver = {
 .driver = {
  .name = "sg2042-mcu",
  .of_match_table = sg2042_mcu_of_id,
  .dev_groups = sg2042_mcu_groups,
 },
 .probe = sg2042_mcu_i2c_probe,
 .id_table = sg2042_mcu_id,
};
module_i2c_driver(sg2042_mcu_driver);

MODULE_AUTHOR("Inochi Amaoto ");
MODULE_DESCRIPTION("MCU I2C driver for SG2042 soc platform");
MODULE_LICENSE("GPL");

Messung V0.5
C=93 H=94 G=93

¤ Dauer der Verarbeitung: 0.0 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.