linux/drivers/hwmon/lm87.c

// SPDX-License-Identifier: GPL-2.0-or-later
/*
 * lm87.c
 *
 * Copyright (C) 2000       Frodo Looijaard <[email protected]>
 *                          Philip Edelbrock <[email protected]>
 *                          Stephen Rousset <[email protected]>
 *                          Dan Eaton <[email protected]>
 * Copyright (C) 2004-2008  Jean Delvare <[email protected]>
 *
 * Original port to Linux 2.6 by Jeff Oliver.
 *
 * The LM87 is a sensor chip made by National Semiconductor. It monitors up
 * to 8 voltages (including its own power source), up to three temperatures
 * (its own plus up to two external ones) and up to two fans. The default
 * configuration is 6 voltages, two temperatures and two fans (see below).
 * Voltages are scaled internally with ratios such that the nominal value of
 * each voltage correspond to a register value of 192 (which means a
 * resolution of about 0.5% of the nominal value). Temperature values are
 * reported with a 1 deg resolution and a 3-4 deg accuracy. Complete
 * datasheet can be obtained from National's website at:
 *   http://www.national.com/pf/LM/LM87.html
 *
 * Some functions share pins, so not all functions are available at the same
 * time. Which are depends on the hardware setup. This driver normally
 * assumes that firmware configured the chip correctly. Where this is not
 * the case, platform code must set the I2C client's platform_data to point
 * to a u8 value to be written to the channel register.
 * For reference, here is the list of exclusive functions:
 *  - in0+in5 (default) or temp3
 *  - fan1 (default) or in6
 *  - fan2 (default) or in7
 *  - VID lines (default) or IRQ lines (not handled by this driver)
 *
 * The LM87 additionally features an analog output, supposedly usable to
 * control the speed of a fan. All new chips use pulse width modulation
 * instead. The LM87 is the only hardware monitoring chipset I know of
 * which uses amplitude modulation. Be careful when using this feature.
 *
 * This driver also supports the ADM1024, a sensor chip made by Analog
 * Devices. That chip is fully compatible with the LM87. Complete
 * datasheet can be obtained from Analog's website at:
 *   https://www.analog.com/en/prod/0,2877,ADM1024,00.html
 */

#include <linux/module.h>
#include <linux/init.h>
#include <linux/slab.h>
#include <linux/jiffies.h>
#include <linux/i2c.h>
#include <linux/hwmon.h>
#include <linux/hwmon-sysfs.h>
#include <linux/hwmon-vid.h>
#include <linux/err.h>
#include <linux/mutex.h>
#include <linux/regulator/consumer.h>

/*
 * Addresses to scan
 * LM87 has three possible addresses: 0x2c, 0x2d and 0x2e.
 */

static const unsigned short normal_i2c[] =;

/*
 * The LM87 registers
 */

/* nr in 0..5 */
#define LM87_REG_IN(nr)
#define LM87_REG_IN_MAX(nr)
#define LM87_REG_IN_MIN(nr)
/* nr in 0..1 */
#define LM87_REG_AIN(nr)
#define LM87_REG_AIN_MIN(nr)
#define LM87_REG_AIN_MAX(nr)

static u8 LM87_REG_TEMP[3] =;
static u8 LM87_REG_TEMP_HIGH[3] =;
static u8 LM87_REG_TEMP_LOW[3] =;

#define LM87_REG_TEMP_HW_INT_LOCK
#define LM87_REG_TEMP_HW_EXT_LOCK
#define LM87_REG_TEMP_HW_INT
#define LM87_REG_TEMP_HW_EXT

/* nr in 0..1 */
#define LM87_REG_FAN(nr)
#define LM87_REG_FAN_MIN(nr)
#define LM87_REG_AOUT

#define LM87_REG_CONFIG
#define LM87_REG_CHANNEL_MODE
#define LM87_REG_VID_FAN_DIV
#define LM87_REG_VID4

#define LM87_REG_ALARMS1
#define LM87_REG_ALARMS2

#define LM87_REG_COMPANY_ID
#define LM87_REG_REVISION

/*
 * Conversions and various macros
 * The LM87 uses signed 8-bit values for temperatures.
 */

#define IN_FROM_REG(reg, scale)
#define IN_TO_REG(val, scale)

#define TEMP_FROM_REG(reg)
#define TEMP_TO_REG(val)

#define FAN_FROM_REG(reg, div)
#define FAN_TO_REG(val, div)

#define FAN_DIV_FROM_REG(reg)

/* analog out is 9.80mV/LSB */
#define AOUT_FROM_REG(reg)
#define AOUT_TO_REG(val)

/* nr in 0..1 */
#define CHAN_NO_FAN(nr)
#define CHAN_TEMP3
#define CHAN_VCC_5V
#define CHAN_NO_VID

/*
 * Client data (each client gets its own)
 */

struct lm87_data {};

static inline int lm87_read_value(struct i2c_client *client, u8 reg)
{}

static inline int lm87_write_value(struct i2c_client *client, u8 reg, u8 value)
{}

static struct lm87_data *lm87_update_device(struct device *dev)
{}

/*
 * Sysfs stuff
 */

static ssize_t in_input_show(struct device *dev,
			     struct device_attribute *attr, char *buf)
{}

static ssize_t in_min_show(struct device *dev, struct device_attribute *attr,
			   char *buf)
{}

static ssize_t in_max_show(struct device *dev, struct device_attribute *attr,
			   char *buf)
{}

static ssize_t in_min_store(struct device *dev, struct device_attribute *attr,
			    const char *buf, size_t count)
{}

static ssize_t in_max_store(struct device *dev, struct device_attribute *attr,
			    const char *buf, size_t count)
{}

static SENSOR_DEVICE_ATTR_RO(in0_input, in_input, 0);
static SENSOR_DEVICE_ATTR_RW(in0_min, in_min, 0);
static SENSOR_DEVICE_ATTR_RW(in0_max, in_max, 0);
static SENSOR_DEVICE_ATTR_RO(in1_input, in_input, 1);
static SENSOR_DEVICE_ATTR_RW(in1_min, in_min, 1);
static SENSOR_DEVICE_ATTR_RW(in1_max, in_max, 1);
static SENSOR_DEVICE_ATTR_RO(in2_input, in_input, 2);
static SENSOR_DEVICE_ATTR_RW(in2_min, in_min, 2);
static SENSOR_DEVICE_ATTR_RW(in2_max, in_max, 2);
static SENSOR_DEVICE_ATTR_RO(in3_input, in_input, 3);
static SENSOR_DEVICE_ATTR_RW(in3_min, in_min, 3);
static SENSOR_DEVICE_ATTR_RW(in3_max, in_max, 3);
static SENSOR_DEVICE_ATTR_RO(in4_input, in_input, 4);
static SENSOR_DEVICE_ATTR_RW(in4_min, in_min, 4);
static SENSOR_DEVICE_ATTR_RW(in4_max, in_max, 4);
static SENSOR_DEVICE_ATTR_RO(in5_input, in_input, 5);
static SENSOR_DEVICE_ATTR_RW(in5_min, in_min, 5);
static SENSOR_DEVICE_ATTR_RW(in5_max, in_max, 5);
static SENSOR_DEVICE_ATTR_RO(in6_input, in_input, 6);
static SENSOR_DEVICE_ATTR_RW(in6_min, in_min, 6);
static SENSOR_DEVICE_ATTR_RW(in6_max, in_max, 6);
static SENSOR_DEVICE_ATTR_RO(in7_input, in_input, 7);
static SENSOR_DEVICE_ATTR_RW(in7_min, in_min, 7);
static SENSOR_DEVICE_ATTR_RW(in7_max, in_max, 7);

static ssize_t temp_input_show(struct device *dev,
			       struct device_attribute *attr, char *buf)
{}

static ssize_t temp_low_show(struct device *dev,
			     struct device_attribute *attr, char *buf)
{}

static ssize_t temp_high_show(struct device *dev,
			      struct device_attribute *attr, char *buf)
{}

static ssize_t temp_low_store(struct device *dev,
			      struct device_attribute *attr, const char *buf,
			      size_t count)
{}

static ssize_t temp_high_store(struct device *dev,
			       struct device_attribute *attr, const char *buf,
			       size_t count)
{}

static SENSOR_DEVICE_ATTR_RO(temp1_input, temp_input, 0);
static SENSOR_DEVICE_ATTR_RW(temp1_min, temp_low, 0);
static SENSOR_DEVICE_ATTR_RW(temp1_max, temp_high, 0);
static SENSOR_DEVICE_ATTR_RO(temp2_input, temp_input, 1);
static SENSOR_DEVICE_ATTR_RW(temp2_min, temp_low, 1);
static SENSOR_DEVICE_ATTR_RW(temp2_max, temp_high, 1);
static SENSOR_DEVICE_ATTR_RO(temp3_input, temp_input, 2);
static SENSOR_DEVICE_ATTR_RW(temp3_min, temp_low, 2);
static SENSOR_DEVICE_ATTR_RW(temp3_max, temp_high, 2);

static ssize_t temp1_crit_show(struct device *dev,
			       struct device_attribute *attr, char *buf)
{}

static ssize_t temp2_crit_show(struct device *dev,
			       struct device_attribute *attr, char *buf)
{}

static DEVICE_ATTR_RO(temp1_crit);
static DEVICE_ATTR_RO(temp2_crit);
static DEVICE_ATTR(temp3_crit, 0444, temp2_crit_show, NULL);

static ssize_t fan_input_show(struct device *dev,
			      struct device_attribute *attr, char *buf)
{}

static ssize_t fan_min_show(struct device *dev, struct device_attribute *attr,
			    char *buf)
{}

static ssize_t fan_div_show(struct device *dev, struct device_attribute *attr,
			    char *buf)
{}

static ssize_t fan_min_store(struct device *dev,
			     struct device_attribute *attr, const char *buf,
			     size_t count)
{}

/*
 * Note: we save and restore the fan minimum here, because its value is
 * determined in part by the fan clock divider.  This follows the principle
 * of least surprise; the user doesn't expect the fan minimum to change just
 * because the divider changed.
 */
static ssize_t fan_div_store(struct device *dev,
			     struct device_attribute *attr, const char *buf,
			     size_t count)
{}

static SENSOR_DEVICE_ATTR_RO(fan1_input, fan_input, 0);
static SENSOR_DEVICE_ATTR_RW(fan1_min, fan_min, 0);
static SENSOR_DEVICE_ATTR_RW(fan1_div, fan_div, 0);
static SENSOR_DEVICE_ATTR_RO(fan2_input, fan_input, 1);
static SENSOR_DEVICE_ATTR_RW(fan2_min, fan_min, 1);
static SENSOR_DEVICE_ATTR_RW(fan2_div, fan_div, 1);

static ssize_t alarms_show(struct device *dev, struct device_attribute *attr,
			   char *buf)
{}
static DEVICE_ATTR_RO(alarms);

static ssize_t cpu0_vid_show(struct device *dev,
			     struct device_attribute *attr, char *buf)
{}
static DEVICE_ATTR_RO(cpu0_vid);

static ssize_t vrm_show(struct device *dev, struct device_attribute *attr,
			char *buf)
{}
static ssize_t vrm_store(struct device *dev, struct device_attribute *attr,
			 const char *buf, size_t count)
{}
static DEVICE_ATTR_RW(vrm);

static ssize_t aout_output_show(struct device *dev,
				struct device_attribute *attr, char *buf)
{}
static ssize_t aout_output_store(struct device *dev,
				 struct device_attribute *attr,
				 const char *buf, size_t count)
{}
static DEVICE_ATTR_RW(aout_output);

static ssize_t alarm_show(struct device *dev, struct device_attribute *attr,
			  char *buf)
{}
static SENSOR_DEVICE_ATTR_RO(in0_alarm, alarm, 0);
static SENSOR_DEVICE_ATTR_RO(in1_alarm, alarm, 1);
static SENSOR_DEVICE_ATTR_RO(in2_alarm, alarm, 2);
static SENSOR_DEVICE_ATTR_RO(in3_alarm, alarm, 3);
static SENSOR_DEVICE_ATTR_RO(in4_alarm, alarm, 8);
static SENSOR_DEVICE_ATTR_RO(in5_alarm, alarm, 9);
static SENSOR_DEVICE_ATTR_RO(in6_alarm, alarm, 6);
static SENSOR_DEVICE_ATTR_RO(in7_alarm, alarm, 7);
static SENSOR_DEVICE_ATTR_RO(temp1_alarm, alarm, 4);
static SENSOR_DEVICE_ATTR_RO(temp2_alarm, alarm, 5);
static SENSOR_DEVICE_ATTR_RO(temp3_alarm, alarm, 5);
static SENSOR_DEVICE_ATTR_RO(fan1_alarm, alarm, 6);
static SENSOR_DEVICE_ATTR_RO(fan2_alarm, alarm, 7);
static SENSOR_DEVICE_ATTR_RO(temp2_fault, alarm, 14);
static SENSOR_DEVICE_ATTR_RO(temp3_fault, alarm, 15);

/*
 * Real code
 */

static struct attribute *lm87_attributes[] =;

static const struct attribute_group lm87_group =;

static struct attribute *lm87_attributes_in6[] =;

static const struct attribute_group lm87_group_in6 =;

static struct attribute *lm87_attributes_fan1[] =;

static const struct attribute_group lm87_group_fan1 =;

static struct attribute *lm87_attributes_in7[] =;

static const struct attribute_group lm87_group_in7 =;

static struct attribute *lm87_attributes_fan2[] =;

static const struct attribute_group lm87_group_fan2 =;

static struct attribute *lm87_attributes_temp3[] =;

static const struct attribute_group lm87_group_temp3 =;

static struct attribute *lm87_attributes_in0_5[] =;

static const struct attribute_group lm87_group_in0_5 =;

static struct attribute *lm87_attributes_vid[] =;

static const struct attribute_group lm87_group_vid =;

/* Return 0 if detection is successful, -ENODEV otherwise */
static int lm87_detect(struct i2c_client *client, struct i2c_board_info *info)
{}

static void lm87_restore_config(void *arg)
{}

static int lm87_init_client(struct i2c_client *client)
{}

static int lm87_probe(struct i2c_client *client)
{}

/*
 * Driver data (common to all clients)
 */

static const struct i2c_device_id lm87_id[] =;
MODULE_DEVICE_TABLE(i2c, lm87_id);

static const struct of_device_id lm87_of_match[] =;
MODULE_DEVICE_TABLE(of, lm87_of_match);

static struct i2c_driver lm87_driver =;

module_i2c_driver();

MODULE_AUTHOR();
MODULE_DESCRIPTION();
MODULE_LICENSE();