linux/drivers/hwmon/lm63.c

// SPDX-License-Identifier: GPL-2.0-or-later
/*
 * lm63.c - driver for the National Semiconductor LM63 temperature sensor
 *          with integrated fan control
 * Copyright (C) 2004-2008  Jean Delvare <[email protected]>
 * Based on the lm90 driver.
 *
 * The LM63 is a sensor chip made by National Semiconductor. It measures
 * two temperatures (its own and one external one) and the speed of one
 * fan, those speed it can additionally control. Complete datasheet can be
 * obtained from National's website at:
 *   http://www.national.com/pf/LM/LM63.html
 *
 * The LM63 is basically an LM86 with fan speed monitoring and control
 * capabilities added. It misses some of the LM86 features though:
 *  - No low limit for local temperature.
 *  - No critical limit for local temperature.
 *  - Critical limit for remote temperature can be changed only once. We
 *    will consider that the critical limit is read-only.
 *
 * The datasheet isn't very clear about what the tachometer reading is.
 * I had a explanation from National Semiconductor though. The two lower
 * bits of the read value have to be masked out. The value is still 16 bit
 * in width.
 */

#include <linux/module.h>
#include <linux/init.h>
#include <linux/slab.h>
#include <linux/jiffies.h>
#include <linux/i2c.h>
#include <linux/hwmon-sysfs.h>
#include <linux/hwmon.h>
#include <linux/err.h>
#include <linux/mutex.h>
#include <linux/of.h>
#include <linux/sysfs.h>
#include <linux/types.h>

/*
 * Addresses to scan
 * Address is fully defined internally and cannot be changed except for
 * LM64 which has one pin dedicated to address selection.
 * LM63 and LM96163 have address 0x4c.
 * LM64 can have address 0x18 or 0x4e.
 */

static const unsigned short normal_i2c[] =;

/*
 * The LM63 registers
 */

#define LM63_REG_CONFIG1
#define LM63_REG_CONVRATE
#define LM63_REG_CONFIG2
#define LM63_REG_CONFIG_FAN

#define LM63_REG_TACH_COUNT_MSB
#define LM63_REG_TACH_COUNT_LSB
#define LM63_REG_TACH_LIMIT_MSB
#define LM63_REG_TACH_LIMIT_LSB

#define LM63_REG_PWM_VALUE
#define LM63_REG_PWM_FREQ
#define LM63_REG_LUT_TEMP_HYST
#define LM63_REG_LUT_TEMP(nr)
#define LM63_REG_LUT_PWM(nr)

#define LM63_REG_LOCAL_TEMP
#define LM63_REG_LOCAL_HIGH

#define LM63_REG_REMOTE_TEMP_MSB
#define LM63_REG_REMOTE_TEMP_LSB
#define LM63_REG_REMOTE_OFFSET_MSB
#define LM63_REG_REMOTE_OFFSET_LSB
#define LM63_REG_REMOTE_HIGH_MSB
#define LM63_REG_REMOTE_HIGH_LSB
#define LM63_REG_REMOTE_LOW_MSB
#define LM63_REG_REMOTE_LOW_LSB
#define LM63_REG_REMOTE_TCRIT
#define LM63_REG_REMOTE_TCRIT_HYST

#define LM63_REG_ALERT_STATUS
#define LM63_REG_ALERT_MASK

#define LM63_REG_MAN_ID
#define LM63_REG_CHIP_ID

#define LM96163_REG_TRUTHERM
#define LM96163_REG_REMOTE_TEMP_U_MSB
#define LM96163_REG_REMOTE_TEMP_U_LSB
#define LM96163_REG_CONFIG_ENHANCED

#define LM63_MAX_CONVRATE

#define LM63_MAX_CONVRATE_HZ
#define LM96163_MAX_CONVRATE_HZ

/*
 * Conversions and various macros
 * For tachometer counts, the LM63 uses 16-bit values.
 * For local temperature and high limit, remote critical limit and hysteresis
 * value, it uses signed 8-bit values with LSB = 1 degree Celsius.
 * For remote temperature, low and high limits, it uses signed 11-bit values
 * with LSB = 0.125 degree Celsius, left-justified in 16-bit registers.
 * For LM64 the actual remote diode temperature is 16 degree Celsius higher
 * than the register reading. Remote temperature setpoints have to be
 * adapted accordingly.
 */

#define FAN_FROM_REG(reg)
#define FAN_TO_REG(val)
#define TEMP8_FROM_REG(reg)
#define TEMP8_TO_REG(val)
#define TEMP8U_TO_REG(val)
#define TEMP11_FROM_REG(reg)
#define TEMP11_TO_REG(val)
#define TEMP11U_TO_REG(val)
#define HYST_TO_REG(val)

#define UPDATE_INTERVAL(max, rate)

enum chips {};

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

struct lm63_data {};

static inline int temp8_from_reg(struct lm63_data *data, int nr)
{}

static inline int lut_temp_from_reg(struct lm63_data *data, int nr)
{}

static inline int lut_temp_to_reg(struct lm63_data *data, long val)
{}

/*
 * Update the lookup table register cache.
 * client->update_lock must be held when calling this function.
 */
static void lm63_update_lut(struct lm63_data *data)
{}

static struct lm63_data *lm63_update_device(struct device *dev)
{}

/*
 * Trip points in the lookup table should be in ascending order for both
 * temperatures and PWM output values.
 */
static int lm63_lut_looks_bad(struct device *dev, struct lm63_data *data)
{}

/*
 * Sysfs callback functions and files
 */

static ssize_t show_fan(struct device *dev, struct device_attribute *devattr,
			char *buf)
{}

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

static ssize_t show_pwm1(struct device *dev, struct device_attribute *devattr,
			 char *buf)
{}

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

static ssize_t pwm1_enable_show(struct device *dev,
				struct device_attribute *dummy, char *buf)
{}

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

/*
 * There are 8bit registers for both local(temp1) and remote(temp2) sensor.
 * For remote sensor registers temp2_offset has to be considered,
 * for local sensor it must not.
 * So we need separate 8bit accessors for local and remote sensor.
 */
static ssize_t show_local_temp8(struct device *dev,
				struct device_attribute *devattr,
				char *buf)
{}

static ssize_t show_remote_temp8(struct device *dev,
				 struct device_attribute *devattr,
				 char *buf)
{}

static ssize_t show_lut_temp(struct device *dev,
			      struct device_attribute *devattr,
			      char *buf)
{}

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

static ssize_t show_temp11(struct device *dev, struct device_attribute *devattr,
			   char *buf)
{}

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

/*
 * Hysteresis register holds a relative value, while we want to present
 * an absolute to user-space
 */
static ssize_t temp2_crit_hyst_show(struct device *dev,
				    struct device_attribute *dummy, char *buf)
{}

static ssize_t show_lut_temp_hyst(struct device *dev,
				  struct device_attribute *devattr, char *buf)
{}

/*
 * And now the other way around, user-space provides an absolute
 * hysteresis value and we have to store a relative one
 */
static ssize_t temp2_crit_hyst_store(struct device *dev,
				     struct device_attribute *dummy,
				     const char *buf, size_t count)
{}

/*
 * Set conversion rate.
 * client->update_lock must be held when calling this function.
 */
static void lm63_set_convrate(struct lm63_data *data, unsigned int interval)
{}

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

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

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

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

static ssize_t alarms_show(struct device *dev, struct device_attribute *dummy,
			   char *buf)
{}

static ssize_t show_alarm(struct device *dev, struct device_attribute *devattr,
			  char *buf)
{}

static SENSOR_DEVICE_ATTR(fan1_input, S_IRUGO, show_fan, NULL, 0);
static SENSOR_DEVICE_ATTR(fan1_min, S_IWUSR | S_IRUGO, show_fan,
	set_fan, 1);

static SENSOR_DEVICE_ATTR(pwm1, S_IWUSR | S_IRUGO, show_pwm1, set_pwm1, 0);
static DEVICE_ATTR_RW(pwm1_enable);
static SENSOR_DEVICE_ATTR(pwm1_auto_point1_pwm, S_IWUSR | S_IRUGO,
	show_pwm1, set_pwm1, 1);
static SENSOR_DEVICE_ATTR(pwm1_auto_point1_temp, S_IWUSR | S_IRUGO,
	show_lut_temp, set_temp8, 3);
static SENSOR_DEVICE_ATTR(pwm1_auto_point1_temp_hyst, S_IRUGO,
	show_lut_temp_hyst, NULL, 3);
static SENSOR_DEVICE_ATTR(pwm1_auto_point2_pwm, S_IWUSR | S_IRUGO,
	show_pwm1, set_pwm1, 2);
static SENSOR_DEVICE_ATTR(pwm1_auto_point2_temp, S_IWUSR | S_IRUGO,
	show_lut_temp, set_temp8, 4);
static SENSOR_DEVICE_ATTR(pwm1_auto_point2_temp_hyst, S_IRUGO,
	show_lut_temp_hyst, NULL, 4);
static SENSOR_DEVICE_ATTR(pwm1_auto_point3_pwm, S_IWUSR | S_IRUGO,
	show_pwm1, set_pwm1, 3);
static SENSOR_DEVICE_ATTR(pwm1_auto_point3_temp, S_IWUSR | S_IRUGO,
	show_lut_temp, set_temp8, 5);
static SENSOR_DEVICE_ATTR(pwm1_auto_point3_temp_hyst, S_IRUGO,
	show_lut_temp_hyst, NULL, 5);
static SENSOR_DEVICE_ATTR(pwm1_auto_point4_pwm, S_IWUSR | S_IRUGO,
	show_pwm1, set_pwm1, 4);
static SENSOR_DEVICE_ATTR(pwm1_auto_point4_temp, S_IWUSR | S_IRUGO,
	show_lut_temp, set_temp8, 6);
static SENSOR_DEVICE_ATTR(pwm1_auto_point4_temp_hyst, S_IRUGO,
	show_lut_temp_hyst, NULL, 6);
static SENSOR_DEVICE_ATTR(pwm1_auto_point5_pwm, S_IWUSR | S_IRUGO,
	show_pwm1, set_pwm1, 5);
static SENSOR_DEVICE_ATTR(pwm1_auto_point5_temp, S_IWUSR | S_IRUGO,
	show_lut_temp, set_temp8, 7);
static SENSOR_DEVICE_ATTR(pwm1_auto_point5_temp_hyst, S_IRUGO,
	show_lut_temp_hyst, NULL, 7);
static SENSOR_DEVICE_ATTR(pwm1_auto_point6_pwm, S_IWUSR | S_IRUGO,
	show_pwm1, set_pwm1, 6);
static SENSOR_DEVICE_ATTR(pwm1_auto_point6_temp, S_IWUSR | S_IRUGO,
	show_lut_temp, set_temp8, 8);
static SENSOR_DEVICE_ATTR(pwm1_auto_point6_temp_hyst, S_IRUGO,
	show_lut_temp_hyst, NULL, 8);
static SENSOR_DEVICE_ATTR(pwm1_auto_point7_pwm, S_IWUSR | S_IRUGO,
	show_pwm1, set_pwm1, 7);
static SENSOR_DEVICE_ATTR(pwm1_auto_point7_temp, S_IWUSR | S_IRUGO,
	show_lut_temp, set_temp8, 9);
static SENSOR_DEVICE_ATTR(pwm1_auto_point7_temp_hyst, S_IRUGO,
	show_lut_temp_hyst, NULL, 9);
static SENSOR_DEVICE_ATTR(pwm1_auto_point8_pwm, S_IWUSR | S_IRUGO,
	show_pwm1, set_pwm1, 8);
static SENSOR_DEVICE_ATTR(pwm1_auto_point8_temp, S_IWUSR | S_IRUGO,
	show_lut_temp, set_temp8, 10);
static SENSOR_DEVICE_ATTR(pwm1_auto_point8_temp_hyst, S_IRUGO,
	show_lut_temp_hyst, NULL, 10);
static SENSOR_DEVICE_ATTR(pwm1_auto_point9_pwm, S_IWUSR | S_IRUGO,
	show_pwm1, set_pwm1, 9);
static SENSOR_DEVICE_ATTR(pwm1_auto_point9_temp, S_IWUSR | S_IRUGO,
	show_lut_temp, set_temp8, 11);
static SENSOR_DEVICE_ATTR(pwm1_auto_point9_temp_hyst, S_IRUGO,
	show_lut_temp_hyst, NULL, 11);
static SENSOR_DEVICE_ATTR(pwm1_auto_point10_pwm, S_IWUSR | S_IRUGO,
	show_pwm1, set_pwm1, 10);
static SENSOR_DEVICE_ATTR(pwm1_auto_point10_temp, S_IWUSR | S_IRUGO,
	show_lut_temp, set_temp8, 12);
static SENSOR_DEVICE_ATTR(pwm1_auto_point10_temp_hyst, S_IRUGO,
	show_lut_temp_hyst, NULL, 12);
static SENSOR_DEVICE_ATTR(pwm1_auto_point11_pwm, S_IWUSR | S_IRUGO,
	show_pwm1, set_pwm1, 11);
static SENSOR_DEVICE_ATTR(pwm1_auto_point11_temp, S_IWUSR | S_IRUGO,
	show_lut_temp, set_temp8, 13);
static SENSOR_DEVICE_ATTR(pwm1_auto_point11_temp_hyst, S_IRUGO,
	show_lut_temp_hyst, NULL, 13);
static SENSOR_DEVICE_ATTR(pwm1_auto_point12_pwm, S_IWUSR | S_IRUGO,
	show_pwm1, set_pwm1, 12);
static SENSOR_DEVICE_ATTR(pwm1_auto_point12_temp, S_IWUSR | S_IRUGO,
	show_lut_temp, set_temp8, 14);
static SENSOR_DEVICE_ATTR(pwm1_auto_point12_temp_hyst, S_IRUGO,
	show_lut_temp_hyst, NULL, 14);

static SENSOR_DEVICE_ATTR(temp1_input, S_IRUGO, show_local_temp8, NULL, 0);
static SENSOR_DEVICE_ATTR(temp1_max, S_IWUSR | S_IRUGO, show_local_temp8,
	set_temp8, 1);

static SENSOR_DEVICE_ATTR(temp2_input, S_IRUGO, show_temp11, NULL, 0);
static SENSOR_DEVICE_ATTR(temp2_min, S_IWUSR | S_IRUGO, show_temp11,
	set_temp11, 1);
static SENSOR_DEVICE_ATTR(temp2_max, S_IWUSR | S_IRUGO, show_temp11,
	set_temp11, 2);
static SENSOR_DEVICE_ATTR(temp2_offset, S_IWUSR | S_IRUGO, show_temp11,
	set_temp11, 3);
static SENSOR_DEVICE_ATTR(temp2_crit, S_IRUGO, show_remote_temp8,
	set_temp8, 2);
static DEVICE_ATTR_RW(temp2_crit_hyst);

static DEVICE_ATTR_RW(temp2_type);

/* Individual alarm files */
static SENSOR_DEVICE_ATTR(fan1_min_alarm, S_IRUGO, show_alarm, NULL, 0);
static SENSOR_DEVICE_ATTR(temp2_crit_alarm, S_IRUGO, show_alarm, NULL, 1);
static SENSOR_DEVICE_ATTR(temp2_fault, S_IRUGO, show_alarm, NULL, 2);
static SENSOR_DEVICE_ATTR(temp2_min_alarm, S_IRUGO, show_alarm, NULL, 3);
static SENSOR_DEVICE_ATTR(temp2_max_alarm, S_IRUGO, show_alarm, NULL, 4);
static SENSOR_DEVICE_ATTR(temp1_max_alarm, S_IRUGO, show_alarm, NULL, 6);
/* Raw alarm file for compatibility */
static DEVICE_ATTR_RO(alarms);

static DEVICE_ATTR_RW(update_interval);

static struct attribute *lm63_attributes[] =;

static struct attribute *lm63_attributes_temp2_type[] =;

static const struct attribute_group lm63_group_temp2_type =;

static struct attribute *lm63_attributes_extra_lut[] =;

static const struct attribute_group lm63_group_extra_lut =;

/*
 * On LM63, temp2_crit can be set only once, which should be job
 * of the bootloader.
 * On LM64, temp2_crit can always be set.
 * On LM96163, temp2_crit can be set if bit 1 of the configuration
 * register is true.
 */
static umode_t lm63_attribute_mode(struct kobject *kobj,
				   struct attribute *attr, int index)
{}

static const struct attribute_group lm63_group =;

static struct attribute *lm63_attributes_fan1[] =;

static const struct attribute_group lm63_group_fan1 =;

/*
 * Real code
 */

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

/*
 * Ideally we shouldn't have to initialize anything, since the BIOS
 * should have taken care of everything
 */
static void lm63_init_client(struct lm63_data *data)
{}

static const struct i2c_device_id lm63_id[];

static int lm63_probe(struct i2c_client *client)
{}

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

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

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

static struct i2c_driver lm63_driver =;

module_i2c_driver();

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