linux/drivers/hwmon/emc2305.c

// SPDX-License-Identifier: GPL-2.0+
/*
 * Hardware monitoring driver for EMC2305 fan controller
 *
 * Copyright (C) 2022 Nvidia Technologies Ltd.
 */

#include <linux/err.h>
#include <linux/hwmon.h>
#include <linux/i2c.h>
#include <linux/module.h>
#include <linux/platform_data/emc2305.h>
#include <linux/thermal.h>

#define EMC2305_REG_DRIVE_FAIL_STATUS
#define EMC2305_REG_VENDOR
#define EMC2305_FAN_MAX
#define EMC2305_FAN_MIN
#define EMC2305_FAN_MAX_STATE
#define EMC2305_DEVICE
#define EMC2305_VENDOR
#define EMC2305_REG_PRODUCT_ID
#define EMC2305_TACH_REGS_UNUSE_BITS
#define EMC2305_TACH_CNT_MULTIPLIER
#define EMC2305_TACH_RANGE_MIN

#define EMC2305_PWM_DUTY2STATE(duty, max_state, pwm_max)
#define EMC2305_PWM_STATE2DUTY(state, max_state, pwm_max)

/*
 * Factor by equations [2] and [3] from data sheet; valid for fans where the number of edges
 * equal (poles * 2 + 1).
 */
#define EMC2305_RPM_FACTOR

#define EMC2305_REG_FAN_DRIVE(n)
#define EMC2305_REG_FAN_MIN_DRIVE(n)
#define EMC2305_REG_FAN_TACH(n)

enum emc230x_product_id {};

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

/**
 * struct emc2305_cdev_data - device-specific cooling device state
 * @cdev: cooling device
 * @cur_state: cooling current state
 * @last_hwmon_state: last cooling state updated by hwmon subsystem
 * @last_thermal_state: last cooling state updated by thermal subsystem
 *
 * The 'last_hwmon_state' and 'last_thermal_state' fields are provided to support fan low limit
 * speed feature. The purpose of this feature is to provides ability to limit fan speed
 * according to some system wise considerations, like absence of some replaceable units (PSU or
 * line cards), high system ambient temperature, unreliable transceivers temperature sensing or
 * some other factors which indirectly impacts system's airflow
 * Fan low limit feature is supported through 'hwmon' interface: 'hwmon' 'pwm' attribute is
 * used for setting low limit for fan speed in case 'thermal' subsystem is configured in
 * kernel. In this case setting fan speed through 'hwmon' will never let the 'thermal'
 * subsystem to select a lower duty cycle than the duty cycle selected with the 'pwm'
 * attribute.
 * From other side, fan speed is to be updated in hardware through 'pwm' only in case the
 * requested fan speed is above last speed set by 'thermal' subsystem, otherwise requested fan
 * speed will be just stored with no PWM update.
 */
struct emc2305_cdev_data {};

/**
 * struct emc2305_data - device-specific data
 * @client: i2c client
 * @hwmon_dev: hwmon device
 * @max_state: maximum cooling state of the cooling device
 * @pwm_num: number of PWM channels
 * @pwm_separate: separate PWM settings for every channel
 * @pwm_min: array of minimum PWM per channel
 * @cdev_data: array of cooling devices data
 */
struct emc2305_data {};

static char *emc2305_fan_name[] =;

static void emc2305_unset_tz(struct device *dev);

static int emc2305_get_max_channel(const struct emc2305_data *data)
{}

static int emc2305_get_cdev_idx(struct thermal_cooling_device *cdev)
{}

static int emc2305_get_cur_state(struct thermal_cooling_device *cdev, unsigned long *state)
{}

static int emc2305_get_max_state(struct thermal_cooling_device *cdev, unsigned long *state)
{}

static int __emc2305_set_cur_state(struct emc2305_data *data, int cdev_idx, unsigned long state)
{}

static int emc2305_set_cur_state(struct thermal_cooling_device *cdev, unsigned long state)
{}

static const struct thermal_cooling_device_ops emc2305_cooling_ops =;

static int emc2305_show_fault(struct device *dev, int channel)
{}

static int emc2305_show_fan(struct device *dev, int channel)
{}

static int emc2305_show_pwm(struct device *dev, int channel)
{}

static int emc2305_set_pwm(struct device *dev, long val, int channel)
{}

static int emc2305_set_single_tz(struct device *dev, int idx)
{}

static int emc2305_set_tz(struct device *dev)
{}

static void emc2305_unset_tz(struct device *dev)
{}

static umode_t
emc2305_is_visible(const void *data, enum hwmon_sensor_types type, u32 attr, int channel)
{
	int max_channel = emc2305_get_max_channel(data);

	/* Don't show channels which are not physically connected. */
	if (channel >= max_channel)
		return 0;
	switch (type) {
	case hwmon_fan:
		switch (attr) {
		case hwmon_fan_input:
			return 0444;
		case hwmon_fan_fault:
			return 0444;
		default:
			break;
		}
		break;
	case hwmon_pwm:
		switch (attr) {
		case hwmon_pwm_input:
			return 0644;
		default:
			break;
		}
		break;
	default:
		break;
	}

	return 0;
};

static int
emc2305_write(struct device *dev, enum hwmon_sensor_types type, u32 attr, int channel, long val)
{
	struct emc2305_data *data = dev_get_drvdata(dev);
	int cdev_idx;

	switch (type) {
	case hwmon_pwm:
		switch (attr) {
		case hwmon_pwm_input:
			/* If thermal is configured - handle PWM limit setting. */
			if (IS_REACHABLE(CONFIG_THERMAL)) {
				if (data->pwm_separate)
					cdev_idx = channel;
				else
					cdev_idx = 0;
				data->cdev_data[cdev_idx].last_hwmon_state =
					EMC2305_PWM_DUTY2STATE(val, data->max_state,
							       EMC2305_FAN_MAX);
				/*
				 * Update PWM only in case requested state is not less than the
				 * last thermal state.
				 */
				if (data->cdev_data[cdev_idx].last_hwmon_state >=
				    data->cdev_data[cdev_idx].last_thermal_state)
					return __emc2305_set_cur_state(data, cdev_idx,
							data->cdev_data[cdev_idx].last_hwmon_state);
				return 0;
			}
			return emc2305_set_pwm(dev, val, channel);
		default:
			break;
		}
		break;
	default:
		break;
	}

	return -EOPNOTSUPP;
};

static int
emc2305_read(struct device *dev, enum hwmon_sensor_types type, u32 attr, int channel, long *val)
{
	int ret;

	switch (type) {
	case hwmon_fan:
		switch (attr) {
		case hwmon_fan_input:
			ret = emc2305_show_fan(dev, channel);
			if (ret < 0)
				return ret;
			*val = ret;
			return 0;
		case hwmon_fan_fault:
			ret = emc2305_show_fault(dev, channel);
			if (ret < 0)
				return ret;
			*val = ret;
			return 0;
		default:
			break;
		}
		break;
	case hwmon_pwm:
		switch (attr) {
		case hwmon_pwm_input:
			ret = emc2305_show_pwm(dev, channel);
			if (ret < 0)
				return ret;
			*val = ret;
			return 0;
		default:
			break;
		}
		break;
	default:
		break;
	}

	return -EOPNOTSUPP;
};

static const struct hwmon_ops emc2305_ops =;

static const struct hwmon_channel_info * const emc2305_info[] =;

static const struct hwmon_chip_info emc2305_chip_info =;

static int emc2305_identify(struct device *dev)
{}

static int emc2305_probe(struct i2c_client *client)
{}

static void emc2305_remove(struct i2c_client *client)
{}

static struct i2c_driver emc2305_driver =;

module_i2c_driver();

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