// SPDX-License-Identifier: GPL-2.0-only
// Copyright (c) 2023-2024, NVIDIA CORPORATION & AFFILIATES. All rights reserved.

#include <nvidia/conftest.h>

#include <linux/hwmon-sysfs.h>
#include <linux/hwmon.h>
#include <linux/i2c.h>
#include <linux/module.h>
#include <linux/mutex.h>
#include <linux/of.h>
#include <linux/version.h>

#define DEVICE_NAME "f75308"
#define DEVICE_VID_ADDR 0xC0
#define DEVICE_PID_ADDR 0xC2

#define DEVICE_VID 0x1934
#define DEVICE_PID_64PIN 0x1012
#define DEVICE_PID_48PIN 0x1022
#define DEVICE_PID_28PIN 0x1032

#define F75308_MAX_FAN_IN 14
#define F75308_MAX_FAN_CTRL_CNT 11
#define F75308_MAX_FAN_SEG_CNT 5

#define F75308_REG_BANK 0x00
#define F75308_REG_VOLT(index) (0x30 + (index))
#define F75308_REG_TEMP_READ(index) (0x40 + (index)*2)
#define F75308_REG_FAN_READ(index) (0x80 + (index)*2)
#define F75308_REG_FAN_DUTY_READ(index) (0xA0 + (index))
#define F75308_REG_FAN_DUTY_WRITE(index) (0x11 + (index)*0x10)
#define F75308_REG_FAN_TYPE(index) (0x70 + (index) / 4)
#define F75308_REG_FAN_MODE(index) (0x74 + (index) / 4)
#define F75308_REG_FAN_SEG(index) (0x18 + (index)*0x10)
#define F75308_REG_FAN_MAP(index) (0x50 + (index))

#define F75308_BIT_PAIR_SHIFT(index) ((index) % 4 * 2)

enum chip {
	f75308a_28,
	f75308b_48,
	f75308c_64,
};

struct f75308_priv {
	struct mutex locker;
	struct i2c_client *client;
	struct device *hwmon_dev;
	enum chip chip_id;
};

static inline int temp_from_s16(s16 val)
{
	/*
	 * For F75308 series, the format of the temperature reading is
	 * signed 11-bit with LSB = 0.125 degree Celsius, left-justified
	 * in 16-bit register. To align with the unit used in the thermal
	 * framework, convert it to millidegree Celsius.
	 */
	return val / 32 * 125;
}

static int f75308_read8(struct i2c_client *client, u8 reg)
{
	return i2c_smbus_read_byte_data(client, reg);
}

static int f75308_write8(struct i2c_client *client, u8 reg, u8 value)
{
	return i2c_smbus_write_byte_data(client, reg, value);
}

/*
 * It is assumed that client->locker is held (unless we are in detection or
 * initialization steps).
 */
static int f75308_read16(struct i2c_client *client, u8 reg)
{
	int hi, lo;

	hi = f75308_read8(client, reg);
	if (hi < 0)
		return hi;

	lo = f75308_read8(client, reg + 1);
	if (lo < 0)
		return lo;

	return hi << 8 | lo;
}

/*
 * It is assumed that client->locker is held (unless we are in detection or
 * initialization steps).
 */
static int f75308_write_mask8(struct i2c_client *client, u8 reg, u8 mask,
			      u8 value)
{
	int status;

	status = f75308_read8(client, reg);
	if (status < 0)
		return status;

	status = (status & ~mask) | (value & mask);
	return f75308_write8(client, reg, status);
}

/*
 * It is assumed that client->locker is held (unless we are in detection or
 * initialization steps).
 */
static bool f75308_manual_duty_enabled(struct i2c_client *client, int index)
{
	int status, data;

	status = f75308_write8(client, F75308_REG_BANK, 4);
	if (status)
		return status;

	data = f75308_read8(client, F75308_REG_FAN_MODE(index));
	if (data < 0)
		return data;

	data >>= F75308_BIT_PAIR_SHIFT(index);
	data &= GENMASK(1, 0);

	return data == 0x03;
}

static ssize_t f75308_in_input_show(struct device *dev,
				    struct device_attribute *attr, char *buf)
{
	struct f75308_priv *priv = dev_get_drvdata(dev);
	struct i2c_client *client = priv->client;
	int index = to_sensor_dev_attr(attr)->index;
	int status, data;

	mutex_lock(&priv->locker);

	status = f75308_write8(client, F75308_REG_BANK, 0);
	if (status)
		goto release_lock;

	data = f75308_read8(client, F75308_REG_VOLT(index));
	if (data < 0) {
		status = data;
		goto release_lock;
	}

	mutex_unlock(&priv->locker);

	/* Scale of the voltage is 8mV */
	data *= 8;
	status = sprintf(buf, "%d\n", data);
	return status;

release_lock:
	mutex_unlock(&priv->locker);
	return status;
}

static ssize_t f75308_temp_input_show(struct device *dev,
				      struct device_attribute *attr, char *buf)
{
	struct f75308_priv *priv = dev_get_drvdata(dev);
	struct i2c_client *client = priv->client;
	int index = to_sensor_dev_attr(attr)->index;
	int status, data;

	mutex_lock(&priv->locker);

	status = f75308_write8(client, F75308_REG_BANK, 0);
	if (status)
		goto release_lock;

	data = f75308_read16(client, F75308_REG_TEMP_READ(index));
	if (data < 0) {
		status = data;
		goto release_lock;
	}

	mutex_unlock(&priv->locker);

	data = temp_from_s16(data);
	status = sprintf(buf, "%d\n", data);
	return status;

release_lock:
	mutex_unlock(&priv->locker);
	return status;
}

static ssize_t f75308_fan_input_show(struct device *dev,
				     struct device_attribute *attr, char *buf)
{
	struct f75308_priv *priv = dev_get_drvdata(dev);
	struct i2c_client *client = priv->client;
	int index = to_sensor_dev_attr(attr)->index;
	int status, lsb, msb, data;

	mutex_lock(&priv->locker);

	status = f75308_write8(client, F75308_REG_BANK, 0);
	if (status)
		goto release_lock;

	msb = f75308_read8(client, F75308_REG_FAN_READ(index) + 0);
	if (msb < 0) {
		status = msb;
		goto release_lock;
	}

	lsb = f75308_read8(client, F75308_REG_FAN_READ(index) + 1);
	if (lsb < 0) {
		status = lsb;
		goto release_lock;
	}

	mutex_unlock(&priv->locker);

	dev_dbg(dev, "%s: index: %d, msb: %x, lsb: %x\n", __func__, index, msb,
		lsb);
	if (msb == 0x1F && lsb == 0xFF)
		data = 0;
	else if (msb || lsb)
		data = 1500000 / (msb * 256 + lsb);
	else
		data = 0;

	status = sprintf(buf, "%d\n", data);
	return status;

release_lock:
	mutex_unlock(&priv->locker);
	return status;
}

static ssize_t f75308_pwm_show(struct device *dev,
			       struct device_attribute *attr, char *buf)
{
	struct f75308_priv *priv = dev_get_drvdata(dev);
	struct i2c_client *client = priv->client;
	int index = to_sensor_dev_attr(attr)->index;
	int status, data;

	mutex_lock(&priv->locker);

	status = f75308_write8(client, F75308_REG_BANK, 0);
	if (status)
		goto release_lock;

	data = f75308_read8(client, F75308_REG_FAN_DUTY_READ(index));
	if (data < 0) {
		status = data;
		goto release_lock;
	}

	mutex_unlock(&priv->locker);

	status = sprintf(buf, "%d\n", data);
	return status;

release_lock:
	mutex_unlock(&priv->locker);
	return status;
}

static ssize_t f75308_pwm_store(struct device *dev,
				struct device_attribute *attr, const char *buf,
				size_t count)
{
	struct f75308_priv *priv = dev_get_drvdata(dev);
	struct i2c_client *client = priv->client;
	int index = to_sensor_dev_attr(attr)->index;
	int status, pwm;

	status = kstrtoint(buf, 0, &pwm);
	if (status)
		return status;

	pwm = clamp_val(pwm, 0, 255);

	mutex_lock(&priv->locker);

	if (!f75308_manual_duty_enabled(client, index)) {
		dev_err(dev, "%s: Only manual_duty mode supports PWM write!\n",
			__func__);
		status = -EOPNOTSUPP;
		goto release_lock;
	}

	status = f75308_write8(client, F75308_REG_BANK, 5);
	if (status)
		goto release_lock;

	status = f75308_write8(client, F75308_REG_FAN_DUTY_WRITE(index), pwm);

release_lock:
	mutex_unlock(&priv->locker);
	return status ? status : count;
}

static ssize_t f75308_fan_type_show(struct device *dev,
				    struct device_attribute *attr, char *buf)
{
	struct f75308_priv *priv = dev_get_drvdata(dev);
	struct i2c_client *client = priv->client;
	int index = to_sensor_dev_attr(attr)->index;
	int status, data;

	mutex_lock(&priv->locker);

	status = f75308_write8(client, F75308_REG_BANK, 4);
	if (status)
		goto release_lock;

	data = f75308_read8(client, F75308_REG_FAN_TYPE(index));
	if (data < 0) {
		status = data;
		goto release_lock;
	}

	mutex_unlock(&priv->locker);

	data >>= F75308_BIT_PAIR_SHIFT(index);
	data &= GENMASK(1, 0);

	switch (data) {
	case 0:
		status = sprintf(buf, "pwm\n");
		break;
	case 1:
		status = sprintf(buf, "linear\n");
		break;
	case 2:
		status = sprintf(buf, "pwm_opendrain\n");
		break;
	default:
		status = sprintf(buf,
				 "%s: invalid data: index: %d, data: %xh\n",
				 __func__, index, data);
		break;
	}

	return status;

release_lock:
	mutex_unlock(&priv->locker);
	return status;
}

static int __f75308_fan_type_store(struct device *dev, int index,
				   const char *buf)
{
	struct f75308_priv *priv = dev_get_drvdata(dev);
	struct i2c_client *client = priv->client;
	int status, data, shift;

	shift = F75308_BIT_PAIR_SHIFT(index);
	if (!strncmp(buf, "pwm_opendrain", strlen("pwm_opendrain"))) {
		data = 0x02;
	} else if (!strncmp(buf, "linear", strlen("linear"))) {
		data = 0x01;
	} else if (!strncmp(buf, "pwm", strlen("pwm"))) {
		data = 0x00;
	} else {
		dev_err(dev, "%s: support only pwm/linear/pwm_opendrain\n",
			__func__);
		return -EINVAL;
	}

	mutex_lock(&priv->locker);

	status = f75308_write8(client, F75308_REG_BANK, 4);
	if (status)
		goto release_lock;

	status = f75308_write_mask8(client, F75308_REG_FAN_TYPE(index),
				    GENMASK(1, 0) << shift, data << shift);

release_lock:
	mutex_unlock(&priv->locker);
	return status;
}

static ssize_t f75308_fan_type_store(struct device *dev,
				     struct device_attribute *attr,
				     const char *buf, size_t count)
{
	int index = to_sensor_dev_attr(attr)->index;
	int status;

	status = __f75308_fan_type_store(dev, index, buf);
	return status ? status : count;
}

static ssize_t f75308_fan_mode_show(struct device *dev,
				    struct device_attribute *attr, char *buf)
{
	struct f75308_priv *priv = dev_get_drvdata(dev);
	struct i2c_client *client = priv->client;
	int index = to_sensor_dev_attr(attr)->index;
	int status, data;

	mutex_lock(&priv->locker);

	status = f75308_write8(client, F75308_REG_BANK, 4);
	if (status)
		goto release_lock;

	data = f75308_read8(client, F75308_REG_FAN_MODE(index));
	if (data < 0) {
		status = data;
		goto release_lock;
	}

	mutex_unlock(&priv->locker);

	data >>= F75308_BIT_PAIR_SHIFT(index);
	data &= GENMASK(1, 0);

	switch (data) {
	case 0:
		status = sprintf(buf, "auto_rpm\n");
		break;
	case 1:
		status = sprintf(buf, "auto_duty\n");
		break;
	case 2:
		status = sprintf(buf, "manual_rpm\n");
		break;
	case 3:
		status = sprintf(buf, "manual_duty\n");
		break;
	default:
		status = sprintf(buf,
				 "%s: invalid data: index: %d, data: %xh\n",
				 __func__, index, data);
		break;
	}

	return status;

release_lock:
	mutex_unlock(&priv->locker);
	return status;
}

static int __f75308_fan_mode_store(struct device *dev, int index,
				   const char *buf)
{
	struct f75308_priv *priv = dev_get_drvdata(dev);
	struct i2c_client *client = priv->client;
	int status, data, shift;

	shift = F75308_BIT_PAIR_SHIFT(index);
	if (!strncmp(buf, "manual_rpm", strlen("manual_rpm"))) {
		data = 0x02;
	} else if (!strncmp(buf, "manual_duty", strlen("manual_duty"))) {
		data = 0x03;
	} else if (!strncmp(buf, "auto_rpm", strlen("auto_rpm"))) {
		data = 0x00;
	} else if (!strncmp(buf, "auto_duty", strlen("auto_duty"))) {
		data = 0x01;
	} else {
		dev_err(dev,
			"%s: support only manual_rpm/manual_duty/auto_rpm/auto_duty\n",
			__func__);

		return -EINVAL;
	}

	mutex_lock(&priv->locker);

	status = f75308_write8(client, F75308_REG_BANK, 4);
	if (status)
		goto release_lock;

	status = f75308_write_mask8(client, F75308_REG_FAN_MODE(index),
				    GENMASK(1, 0) << shift, data << shift);

release_lock:
	mutex_unlock(&priv->locker);
	return status;
}

static ssize_t f75308_fan_mode_store(struct device *dev,
				     struct device_attribute *attr,
				     const char *buf, size_t count)
{
	int index = to_sensor_dev_attr(attr)->index;
	int status;

	status = __f75308_fan_mode_store(dev, index, buf);
	return status ? status : count;
}

static ssize_t f75308_fan_5_seg_show(struct device *dev,
				     struct device_attribute *attr, char *buf)
{
	struct f75308_priv *priv = dev_get_drvdata(dev);
	struct i2c_client *client = priv->client;
	int index = to_sensor_dev_attr(attr)->index;
	int status, data[F75308_MAX_FAN_SEG_CNT], i;

	mutex_lock(&priv->locker);

	status = f75308_write8(client, F75308_REG_BANK, 5);
	if (status)
		goto release_lock;

	for (i = 0; i < F75308_MAX_FAN_SEG_CNT; ++i) {
		data[i] = f75308_read8(client, F75308_REG_FAN_SEG(index) + i);
		if (data[i] < 0) {
			status = data[i];
			goto release_lock;
		}

		data[i] = data[i] * 100 / 255;
		dev_dbg(dev, "%s: reg: %x, data: %d%%\n", __func__,
			F75308_REG_FAN_SEG(index) + i, data[i]);
	}

	mutex_unlock(&priv->locker);

	status = sprintf(buf, "%d%% %d%% %d%% %d%% %d%%\n", data[0], data[1],
			 data[2], data[3], data[4]);
	return status;

release_lock:
	mutex_unlock(&priv->locker);
	return status;
}

static int __f75308_fan_5_seg_store(struct device *dev, int index,
				    u8 data[F75308_MAX_FAN_SEG_CNT])
{
	struct f75308_priv *priv = dev_get_drvdata(dev);
	struct i2c_client *client = priv->client;
	int status, i;

	for (i = 0; i < F75308_MAX_FAN_SEG_CNT; ++i)
		if (data[i] > 100)
			return -EINVAL;

	mutex_lock(&priv->locker);

	status = f75308_write8(client, F75308_REG_BANK, 5);
	if (status)
		goto release_lock;

	for (i = 0; i < F75308_MAX_FAN_SEG_CNT; ++i) {
		data[i] = 255 * data[i] / 100;

		status = f75308_write8(client, F75308_REG_FAN_SEG(index) + i,
				       data[i]);
		if (status)
			goto release_lock;

		dev_dbg(dev, "%s: reg: %x, data: %d\n", __func__,
			F75308_REG_FAN_SEG(index) + i, data[i]);
	}

release_lock:
	mutex_unlock(&priv->locker);
	return status;
}

static ssize_t f75308_fan_5_seg_store(struct device *dev,
				      struct device_attribute *attr,
				      const char *buf, size_t count)
{
	int index = to_sensor_dev_attr(attr)->index;
	u8 data[F75308_MAX_FAN_SEG_CNT];
	char *p;
	int status, i;

	for (i = 0; i < F75308_MAX_FAN_SEG_CNT; ++i) {
		p = strsep((char **)&buf, " ");
		if (!p)
			return -EINVAL;

		status = kstrtou8(p, 0, &data[i]);
		if (status)
			return status;
	}

	status = __f75308_fan_5_seg_store(dev, index, data);
	return status ? status : count;
}

static ssize_t f75308_fan_map_show(struct device *dev,
				   struct device_attribute *attr, char *buf)
{
	struct f75308_priv *priv = dev_get_drvdata(dev);
	struct i2c_client *client = priv->client;
	int index = to_sensor_dev_attr(attr)->index;
	int status, data;

	mutex_lock(&priv->locker);

	status = f75308_write8(client, F75308_REG_BANK, 4);
	if (status)
		goto release_lock;

	data = f75308_read8(client, F75308_REG_FAN_MAP(index));
	if (data < 0) {
		status = data;
		goto release_lock;
	}

	mutex_unlock(&priv->locker);
	dev_dbg(dev, "%s: idx: %d, data: %x\n", __func__, index, data);
	status = sprintf(buf, "%d\n", data);
	return status;

release_lock:
	mutex_unlock(&priv->locker);
	return status;
}

static ssize_t f75308_fan_map_store(struct device *dev,
				    struct device_attribute *attr,
				    const char *buf, size_t count)
{
	struct f75308_priv *priv = dev_get_drvdata(dev);
	struct i2c_client *client = priv->client;
	int index = to_sensor_dev_attr(attr)->index;
	int status, data;

	status = kstrtoint(buf, 0, &data);
	if (status)
		return status;

	mutex_lock(&priv->locker);

	status = f75308_write8(client, F75308_REG_BANK, 4);
	if (status)
		goto release_lock;

	status = f75308_write8(client, F75308_REG_FAN_MAP(index), data);
	if (status)
		goto release_lock;

	dev_dbg(dev, "%s: idx: %d, data: %x\n", __func__, index, data);

release_lock:
	mutex_unlock(&priv->locker);
	return status ? status : count;
}

static SENSOR_DEVICE_ATTR_RO(in0_input, f75308_in_input, 0);
static SENSOR_DEVICE_ATTR_RO(in1_input, f75308_in_input, 1);
static SENSOR_DEVICE_ATTR_RO(in2_input, f75308_in_input, 2);
static SENSOR_DEVICE_ATTR_RO(in3_input, f75308_in_input, 3);
static SENSOR_DEVICE_ATTR_RO(in4_input, f75308_in_input, 4);
static SENSOR_DEVICE_ATTR_RO(in5_input, f75308_in_input, 5);
static SENSOR_DEVICE_ATTR_RO(in6_input, f75308_in_input, 6);
static SENSOR_DEVICE_ATTR_RO(in7_input, f75308_in_input, 7);
static SENSOR_DEVICE_ATTR_RO(in8_input, f75308_in_input, 8);
static SENSOR_DEVICE_ATTR_RO(in9_input, f75308_in_input, 9);
static SENSOR_DEVICE_ATTR_RO(in10_input, f75308_in_input, 10);
static SENSOR_DEVICE_ATTR_RO(in11_input, f75308_in_input, 11);
static SENSOR_DEVICE_ATTR_RO(in12_input, f75308_in_input, 12);
static SENSOR_DEVICE_ATTR_RO(in13_input, f75308_in_input, 13);
static SENSOR_DEVICE_ATTR_RO(in14_input, f75308_in_input, 14);

static SENSOR_DEVICE_ATTR_RO(temp0_input, f75308_temp_input, 0);
static SENSOR_DEVICE_ATTR_RO(temp1_input, f75308_temp_input, 1);
static SENSOR_DEVICE_ATTR_RO(temp2_input, f75308_temp_input, 2);
static SENSOR_DEVICE_ATTR_RO(temp3_input, f75308_temp_input, 3);
static SENSOR_DEVICE_ATTR_RO(temp4_input, f75308_temp_input, 4);
static SENSOR_DEVICE_ATTR_RO(temp5_input, f75308_temp_input, 5);
static SENSOR_DEVICE_ATTR_RO(temp6_input, f75308_temp_input, 6);

static SENSOR_DEVICE_ATTR_RO(fan1_input, f75308_fan_input, 0);
static SENSOR_DEVICE_ATTR_RO(fan2_input, f75308_fan_input, 1);
static SENSOR_DEVICE_ATTR_RO(fan3_input, f75308_fan_input, 2);
static SENSOR_DEVICE_ATTR_RO(fan4_input, f75308_fan_input, 3);
static SENSOR_DEVICE_ATTR_RO(fan5_input, f75308_fan_input, 4);
static SENSOR_DEVICE_ATTR_RO(fan6_input, f75308_fan_input, 5);
static SENSOR_DEVICE_ATTR_RO(fan7_input, f75308_fan_input, 6);
static SENSOR_DEVICE_ATTR_RO(fan8_input, f75308_fan_input, 7);
static SENSOR_DEVICE_ATTR_RO(fan9_input, f75308_fan_input, 8);
static SENSOR_DEVICE_ATTR_RO(fan10_input, f75308_fan_input, 9);
static SENSOR_DEVICE_ATTR_RO(fan11_input, f75308_fan_input, 10);
static SENSOR_DEVICE_ATTR_RO(fan12_input, f75308_fan_input, 11);
static SENSOR_DEVICE_ATTR_RO(fan13_input, f75308_fan_input, 12);
static SENSOR_DEVICE_ATTR_RO(fan14_input, f75308_fan_input, 13);

static SENSOR_DEVICE_ATTR_RW(pwm1, f75308_pwm, 0);
static SENSOR_DEVICE_ATTR_RW(pwm2, f75308_pwm, 1);
static SENSOR_DEVICE_ATTR_RW(pwm3, f75308_pwm, 2);
static SENSOR_DEVICE_ATTR_RW(pwm4, f75308_pwm, 3);
static SENSOR_DEVICE_ATTR_RW(pwm5, f75308_pwm, 4);
static SENSOR_DEVICE_ATTR_RW(pwm6, f75308_pwm, 5);
static SENSOR_DEVICE_ATTR_RW(pwm7, f75308_pwm, 6);
static SENSOR_DEVICE_ATTR_RW(pwm8, f75308_pwm, 7);
static SENSOR_DEVICE_ATTR_RW(pwm9, f75308_pwm, 8);
static SENSOR_DEVICE_ATTR_RW(pwm10, f75308_pwm, 9);
static SENSOR_DEVICE_ATTR_RW(pwm11, f75308_pwm, 10);

static SENSOR_DEVICE_ATTR_RW(fan1_type, f75308_fan_type, 0);
static SENSOR_DEVICE_ATTR_RW(fan2_type, f75308_fan_type, 1);
static SENSOR_DEVICE_ATTR_RW(fan3_type, f75308_fan_type, 2);
static SENSOR_DEVICE_ATTR_RW(fan4_type, f75308_fan_type, 3);
static SENSOR_DEVICE_ATTR_RW(fan5_type, f75308_fan_type, 4);
static SENSOR_DEVICE_ATTR_RW(fan6_type, f75308_fan_type, 5);
static SENSOR_DEVICE_ATTR_RW(fan7_type, f75308_fan_type, 6);
static SENSOR_DEVICE_ATTR_RW(fan8_type, f75308_fan_type, 7);
static SENSOR_DEVICE_ATTR_RW(fan9_type, f75308_fan_type, 8);
static SENSOR_DEVICE_ATTR_RW(fan10_type, f75308_fan_type, 9);
static SENSOR_DEVICE_ATTR_RW(fan11_type, f75308_fan_type, 10);

static SENSOR_DEVICE_ATTR_RW(fan1_mode, f75308_fan_mode, 0);
static SENSOR_DEVICE_ATTR_RW(fan2_mode, f75308_fan_mode, 1);
static SENSOR_DEVICE_ATTR_RW(fan3_mode, f75308_fan_mode, 2);
static SENSOR_DEVICE_ATTR_RW(fan4_mode, f75308_fan_mode, 3);
static SENSOR_DEVICE_ATTR_RW(fan5_mode, f75308_fan_mode, 4);
static SENSOR_DEVICE_ATTR_RW(fan6_mode, f75308_fan_mode, 5);
static SENSOR_DEVICE_ATTR_RW(fan7_mode, f75308_fan_mode, 6);
static SENSOR_DEVICE_ATTR_RW(fan8_mode, f75308_fan_mode, 7);
static SENSOR_DEVICE_ATTR_RW(fan9_mode, f75308_fan_mode, 8);
static SENSOR_DEVICE_ATTR_RW(fan10_mode, f75308_fan_mode, 9);
static SENSOR_DEVICE_ATTR_RW(fan11_mode, f75308_fan_mode, 10);

static SENSOR_DEVICE_ATTR_RW(fan1_5_seg, f75308_fan_5_seg, 0);
static SENSOR_DEVICE_ATTR_RW(fan2_5_seg, f75308_fan_5_seg, 1);
static SENSOR_DEVICE_ATTR_RW(fan3_5_seg, f75308_fan_5_seg, 2);
static SENSOR_DEVICE_ATTR_RW(fan4_5_seg, f75308_fan_5_seg, 3);
static SENSOR_DEVICE_ATTR_RW(fan5_5_seg, f75308_fan_5_seg, 4);
static SENSOR_DEVICE_ATTR_RW(fan6_5_seg, f75308_fan_5_seg, 5);
static SENSOR_DEVICE_ATTR_RW(fan7_5_seg, f75308_fan_5_seg, 6);
static SENSOR_DEVICE_ATTR_RW(fan8_5_seg, f75308_fan_5_seg, 7);
static SENSOR_DEVICE_ATTR_RW(fan9_5_seg, f75308_fan_5_seg, 8);
static SENSOR_DEVICE_ATTR_RW(fan10_5_seg, f75308_fan_5_seg, 9);
static SENSOR_DEVICE_ATTR_RW(fan11_5_seg, f75308_fan_5_seg, 10);

static SENSOR_DEVICE_ATTR_RW(fan1_map, f75308_fan_map, 0);
static SENSOR_DEVICE_ATTR_RW(fan2_map, f75308_fan_map, 1);
static SENSOR_DEVICE_ATTR_RW(fan3_map, f75308_fan_map, 2);
static SENSOR_DEVICE_ATTR_RW(fan4_map, f75308_fan_map, 3);
static SENSOR_DEVICE_ATTR_RW(fan5_map, f75308_fan_map, 4);
static SENSOR_DEVICE_ATTR_RW(fan6_map, f75308_fan_map, 5);
static SENSOR_DEVICE_ATTR_RW(fan7_map, f75308_fan_map, 6);
static SENSOR_DEVICE_ATTR_RW(fan8_map, f75308_fan_map, 7);
static SENSOR_DEVICE_ATTR_RW(fan9_map, f75308_fan_map, 8);
static SENSOR_DEVICE_ATTR_RW(fan10_map, f75308_fan_map, 9);
static SENSOR_DEVICE_ATTR_RW(fan11_map, f75308_fan_map, 10);

static struct attribute *f75308a_28_attributes[] = {
	&sensor_dev_attr_temp0_input.dev_attr.attr,
	&sensor_dev_attr_temp1_input.dev_attr.attr,
	&sensor_dev_attr_temp2_input.dev_attr.attr,
	&sensor_dev_attr_temp3_input.dev_attr.attr,

	&sensor_dev_attr_fan1_input.dev_attr.attr,
	&sensor_dev_attr_fan2_input.dev_attr.attr,
	&sensor_dev_attr_fan3_input.dev_attr.attr,
	&sensor_dev_attr_fan4_input.dev_attr.attr,

	&sensor_dev_attr_in0_input.dev_attr.attr,
	&sensor_dev_attr_in1_input.dev_attr.attr,
	&sensor_dev_attr_in2_input.dev_attr.attr,
	&sensor_dev_attr_in3_input.dev_attr.attr,
	&sensor_dev_attr_in4_input.dev_attr.attr,
	&sensor_dev_attr_in5_input.dev_attr.attr,

	&sensor_dev_attr_pwm1.dev_attr.attr,
	&sensor_dev_attr_pwm2.dev_attr.attr,
	&sensor_dev_attr_pwm3.dev_attr.attr,
	&sensor_dev_attr_pwm4.dev_attr.attr,

	&sensor_dev_attr_fan1_type.dev_attr.attr,
	&sensor_dev_attr_fan2_type.dev_attr.attr,
	&sensor_dev_attr_fan3_type.dev_attr.attr,
	&sensor_dev_attr_fan4_type.dev_attr.attr,

	&sensor_dev_attr_fan1_mode.dev_attr.attr,
	&sensor_dev_attr_fan2_mode.dev_attr.attr,
	&sensor_dev_attr_fan3_mode.dev_attr.attr,
	&sensor_dev_attr_fan4_mode.dev_attr.attr,

	&sensor_dev_attr_fan1_map.dev_attr.attr,
	&sensor_dev_attr_fan2_map.dev_attr.attr,
	&sensor_dev_attr_fan3_map.dev_attr.attr,
	&sensor_dev_attr_fan4_map.dev_attr.attr,

	&sensor_dev_attr_fan1_5_seg.dev_attr.attr,
	&sensor_dev_attr_fan2_5_seg.dev_attr.attr,
	&sensor_dev_attr_fan3_5_seg.dev_attr.attr,
	&sensor_dev_attr_fan4_5_seg.dev_attr.attr,

	NULL
};

static struct attribute *f75308b_48_attributes[] = {
	&sensor_dev_attr_temp0_input.dev_attr.attr,
	&sensor_dev_attr_temp1_input.dev_attr.attr,
	&sensor_dev_attr_temp2_input.dev_attr.attr,
	&sensor_dev_attr_temp3_input.dev_attr.attr,
	&sensor_dev_attr_temp4_input.dev_attr.attr,
	&sensor_dev_attr_temp5_input.dev_attr.attr,

	&sensor_dev_attr_fan1_input.dev_attr.attr,
	&sensor_dev_attr_fan2_input.dev_attr.attr,
	&sensor_dev_attr_fan3_input.dev_attr.attr,
	&sensor_dev_attr_fan4_input.dev_attr.attr,
	&sensor_dev_attr_fan5_input.dev_attr.attr,
	&sensor_dev_attr_fan6_input.dev_attr.attr,
	&sensor_dev_attr_fan7_input.dev_attr.attr,
	&sensor_dev_attr_fan8_input.dev_attr.attr,
	&sensor_dev_attr_fan9_input.dev_attr.attr,

	&sensor_dev_attr_in0_input.dev_attr.attr,
	&sensor_dev_attr_in1_input.dev_attr.attr,
	&sensor_dev_attr_in2_input.dev_attr.attr,
	&sensor_dev_attr_in3_input.dev_attr.attr,
	&sensor_dev_attr_in4_input.dev_attr.attr,
	&sensor_dev_attr_in5_input.dev_attr.attr,
	&sensor_dev_attr_in6_input.dev_attr.attr,
	&sensor_dev_attr_in7_input.dev_attr.attr,
	&sensor_dev_attr_in8_input.dev_attr.attr,
	&sensor_dev_attr_in9_input.dev_attr.attr,
	&sensor_dev_attr_in10_input.dev_attr.attr,

	&sensor_dev_attr_pwm1.dev_attr.attr,
	&sensor_dev_attr_pwm2.dev_attr.attr,
	&sensor_dev_attr_pwm3.dev_attr.attr,
	&sensor_dev_attr_pwm4.dev_attr.attr,
	&sensor_dev_attr_pwm5.dev_attr.attr,
	&sensor_dev_attr_pwm6.dev_attr.attr,
	&sensor_dev_attr_pwm7.dev_attr.attr,

	&sensor_dev_attr_fan1_type.dev_attr.attr,
	&sensor_dev_attr_fan2_type.dev_attr.attr,
	&sensor_dev_attr_fan3_type.dev_attr.attr,
	&sensor_dev_attr_fan4_type.dev_attr.attr,
	&sensor_dev_attr_fan5_type.dev_attr.attr,
	&sensor_dev_attr_fan6_type.dev_attr.attr,
	&sensor_dev_attr_fan7_type.dev_attr.attr,

	&sensor_dev_attr_fan1_mode.dev_attr.attr,
	&sensor_dev_attr_fan2_mode.dev_attr.attr,
	&sensor_dev_attr_fan3_mode.dev_attr.attr,
	&sensor_dev_attr_fan4_mode.dev_attr.attr,
	&sensor_dev_attr_fan5_mode.dev_attr.attr,
	&sensor_dev_attr_fan6_mode.dev_attr.attr,
	&sensor_dev_attr_fan7_mode.dev_attr.attr,

	&sensor_dev_attr_fan1_map.dev_attr.attr,
	&sensor_dev_attr_fan2_map.dev_attr.attr,
	&sensor_dev_attr_fan3_map.dev_attr.attr,
	&sensor_dev_attr_fan4_map.dev_attr.attr,
	&sensor_dev_attr_fan5_map.dev_attr.attr,
	&sensor_dev_attr_fan6_map.dev_attr.attr,
	&sensor_dev_attr_fan7_map.dev_attr.attr,

	&sensor_dev_attr_fan1_5_seg.dev_attr.attr,
	&sensor_dev_attr_fan2_5_seg.dev_attr.attr,
	&sensor_dev_attr_fan3_5_seg.dev_attr.attr,
	&sensor_dev_attr_fan4_5_seg.dev_attr.attr,
	&sensor_dev_attr_fan5_5_seg.dev_attr.attr,
	&sensor_dev_attr_fan6_5_seg.dev_attr.attr,
	&sensor_dev_attr_fan7_5_seg.dev_attr.attr,

	NULL
};

static struct attribute *f75308c_64_attributes[] = {
	&sensor_dev_attr_temp0_input.dev_attr.attr,
	&sensor_dev_attr_temp1_input.dev_attr.attr,
	&sensor_dev_attr_temp2_input.dev_attr.attr,
	&sensor_dev_attr_temp3_input.dev_attr.attr,
	&sensor_dev_attr_temp4_input.dev_attr.attr,
	&sensor_dev_attr_temp5_input.dev_attr.attr,
	&sensor_dev_attr_temp6_input.dev_attr.attr,

	&sensor_dev_attr_fan1_input.dev_attr.attr,
	&sensor_dev_attr_fan2_input.dev_attr.attr,
	&sensor_dev_attr_fan3_input.dev_attr.attr,
	&sensor_dev_attr_fan4_input.dev_attr.attr,
	&sensor_dev_attr_fan5_input.dev_attr.attr,
	&sensor_dev_attr_fan6_input.dev_attr.attr,
	&sensor_dev_attr_fan7_input.dev_attr.attr,
	&sensor_dev_attr_fan8_input.dev_attr.attr,
	&sensor_dev_attr_fan9_input.dev_attr.attr,
	&sensor_dev_attr_fan10_input.dev_attr.attr,
	&sensor_dev_attr_fan11_input.dev_attr.attr,
	&sensor_dev_attr_fan12_input.dev_attr.attr,
	&sensor_dev_attr_fan13_input.dev_attr.attr,
	&sensor_dev_attr_fan14_input.dev_attr.attr,

	&sensor_dev_attr_in0_input.dev_attr.attr,
	&sensor_dev_attr_in1_input.dev_attr.attr,
	&sensor_dev_attr_in2_input.dev_attr.attr,
	&sensor_dev_attr_in3_input.dev_attr.attr,
	&sensor_dev_attr_in4_input.dev_attr.attr,
	&sensor_dev_attr_in5_input.dev_attr.attr,
	&sensor_dev_attr_in6_input.dev_attr.attr,
	&sensor_dev_attr_in7_input.dev_attr.attr,
	&sensor_dev_attr_in8_input.dev_attr.attr,
	&sensor_dev_attr_in9_input.dev_attr.attr,
	&sensor_dev_attr_in10_input.dev_attr.attr,
	&sensor_dev_attr_in11_input.dev_attr.attr,
	&sensor_dev_attr_in12_input.dev_attr.attr,
	&sensor_dev_attr_in13_input.dev_attr.attr,
	&sensor_dev_attr_in14_input.dev_attr.attr,

	&sensor_dev_attr_pwm1.dev_attr.attr,
	&sensor_dev_attr_pwm2.dev_attr.attr,
	&sensor_dev_attr_pwm3.dev_attr.attr,
	&sensor_dev_attr_pwm4.dev_attr.attr,
	&sensor_dev_attr_pwm5.dev_attr.attr,
	&sensor_dev_attr_pwm6.dev_attr.attr,
	&sensor_dev_attr_pwm7.dev_attr.attr,
	&sensor_dev_attr_pwm8.dev_attr.attr,
	&sensor_dev_attr_pwm9.dev_attr.attr,
	&sensor_dev_attr_pwm10.dev_attr.attr,
	&sensor_dev_attr_pwm11.dev_attr.attr,

	&sensor_dev_attr_fan1_type.dev_attr.attr,
	&sensor_dev_attr_fan2_type.dev_attr.attr,
	&sensor_dev_attr_fan3_type.dev_attr.attr,
	&sensor_dev_attr_fan4_type.dev_attr.attr,
	&sensor_dev_attr_fan5_type.dev_attr.attr,
	&sensor_dev_attr_fan6_type.dev_attr.attr,
	&sensor_dev_attr_fan7_type.dev_attr.attr,
	&sensor_dev_attr_fan8_type.dev_attr.attr,
	&sensor_dev_attr_fan9_type.dev_attr.attr,
	&sensor_dev_attr_fan10_type.dev_attr.attr,
	&sensor_dev_attr_fan11_type.dev_attr.attr,

	&sensor_dev_attr_fan1_mode.dev_attr.attr,
	&sensor_dev_attr_fan2_mode.dev_attr.attr,
	&sensor_dev_attr_fan3_mode.dev_attr.attr,
	&sensor_dev_attr_fan4_mode.dev_attr.attr,
	&sensor_dev_attr_fan5_mode.dev_attr.attr,
	&sensor_dev_attr_fan6_mode.dev_attr.attr,
	&sensor_dev_attr_fan7_mode.dev_attr.attr,
	&sensor_dev_attr_fan8_mode.dev_attr.attr,
	&sensor_dev_attr_fan9_mode.dev_attr.attr,
	&sensor_dev_attr_fan10_mode.dev_attr.attr,
	&sensor_dev_attr_fan11_mode.dev_attr.attr,

	&sensor_dev_attr_fan1_map.dev_attr.attr,
	&sensor_dev_attr_fan2_map.dev_attr.attr,
	&sensor_dev_attr_fan3_map.dev_attr.attr,
	&sensor_dev_attr_fan4_map.dev_attr.attr,
	&sensor_dev_attr_fan5_map.dev_attr.attr,
	&sensor_dev_attr_fan6_map.dev_attr.attr,
	&sensor_dev_attr_fan7_map.dev_attr.attr,
	&sensor_dev_attr_fan8_map.dev_attr.attr,
	&sensor_dev_attr_fan9_map.dev_attr.attr,
	&sensor_dev_attr_fan10_map.dev_attr.attr,
	&sensor_dev_attr_fan11_map.dev_attr.attr,

	&sensor_dev_attr_fan1_5_seg.dev_attr.attr,
	&sensor_dev_attr_fan2_5_seg.dev_attr.attr,
	&sensor_dev_attr_fan3_5_seg.dev_attr.attr,
	&sensor_dev_attr_fan4_5_seg.dev_attr.attr,
	&sensor_dev_attr_fan5_5_seg.dev_attr.attr,
	&sensor_dev_attr_fan6_5_seg.dev_attr.attr,
	&sensor_dev_attr_fan7_5_seg.dev_attr.attr,
	&sensor_dev_attr_fan8_5_seg.dev_attr.attr,
	&sensor_dev_attr_fan9_5_seg.dev_attr.attr,
	&sensor_dev_attr_fan10_5_seg.dev_attr.attr,
	&sensor_dev_attr_fan11_5_seg.dev_attr.attr,

	NULL
};

static const struct attribute_group f75308a_28_group = {
	.attrs = f75308a_28_attributes,
};

static const struct attribute_group f75308b_48_group = {
	.attrs = f75308b_48_attributes,
};

static const struct attribute_group f75308c_64_group = {
	.attrs = f75308c_64_attributes,
};

static const struct attribute_group *f75308a_28_groups[] = {
	&f75308a_28_group,
	NULL,
};

static const struct attribute_group *f75308b_48_groups[] = {
	&f75308b_48_group,
	NULL,
};

static const struct attribute_group *f75308c_64_groups[] = {
	&f75308c_64_group,
	NULL,
};

static const struct attribute_group **f75308_groups[] = {
	f75308a_28_groups,
	f75308b_48_groups,
	f75308c_64_groups,
};

static int f75308_get_devid(struct i2c_client *client, enum chip *chipid)
{
	u16 vendid, pid;
	int status;

	status = f75308_write8(client, F75308_REG_BANK, 0);
	if (status)
		return status;

	vendid = f75308_read16(client, DEVICE_VID_ADDR);
	pid = f75308_read16(client, DEVICE_PID_ADDR);
	if (vendid != DEVICE_VID)
		return -ENODEV;

	if (pid == DEVICE_PID_64PIN)
		*chipid = f75308c_64;
	else if (pid == DEVICE_PID_48PIN)
		*chipid = f75308b_48;
	else if (pid == DEVICE_PID_28PIN)
		*chipid = f75308a_28;
	else
		return -ENODEV;

	return 0;
}

static int f75308_detect(struct i2c_client *client, struct i2c_board_info *info)
{
	enum chip chipid;
	const char *name;
	int status;

	status = f75308_get_devid(client, &chipid);
	if (status)
		return status;

	if (chipid == f75308a_28)
		name = "F75308AR";
	else if (chipid == f75308b_48)
		name = "F75308BD";
	else if (chipid == f75308c_64)
		name = "F75308CU";
	else
		return -ENODEV;

	strscpy(info->type, name, I2C_NAME_SIZE);

	return 0;
}

static int f75308_init(struct i2c_client *client, enum chip chip_id)
{
	int status, data;

	/*
	 * The default temperature control source of FAN4 is T4. However, T4
	 * is only present in F75308BD and F75308CU, not in F75308AR. Hence,
	 * remap the temperature control source of FAN4 of F75308AR to T0 if
	 * T4 is selected.
	 */
	if (chip_id == f75308a_28) {
		status = f75308_write8(client, F75308_REG_BANK, 4);
		if (status)
			return status;

		data = f75308_read8(client, F75308_REG_FAN_MAP(3));
		if (data < 0)
			return data;

		if (data == 0x04) {
			status =
				f75308_write8(client, F75308_REG_FAN_MAP(3), 0);
			if (status)
				return status;
		}
	}

	return 0;
}

static int f75308_probe_child_from_dt(struct device *dev,
				      struct device_node *child,
				      struct i2c_client *client)
{
	int status;
	u32 reg_idx, seg_idx = 0, seg_val;
	u8 seg5[F75308_MAX_FAN_SEG_CNT];
	const char *val_str;
	struct property *prop;
	const __be32 *p;

	status = of_property_read_u32(child, "reg", &reg_idx);
	if (status) {
		dev_err(dev, "missing reg property of %pOFn\n", child);
		return status;
	} else if (reg_idx >= F75308_MAX_FAN_CTRL_CNT) {
		dev_err(dev, "invalid reg %u of %pOFn\n", reg_idx, child);
		return -EINVAL;
	}

	status = of_property_read_string(child, "type", &val_str);
	if (status) {
		dev_err(dev, "invalid type property of %pOFn\n", child);
		return status;
	}

	status = __f75308_fan_type_store(dev, reg_idx, val_str);
	if (status)
		return status;

	status = of_property_read_string(child, "duty", &val_str);
	if (status) {
		dev_err(dev, "invalid duty property of %pOFn\n", child);
		return status;
	}

	status = __f75308_fan_mode_store(dev, reg_idx, val_str);
	if (status)
		return status;

	of_property_for_each_u32(child, "5seg", prop, p, seg_val) {
		dev_dbg(dev, "%s: seg5[%u]: %u\n", __func__, seg_idx, seg_val);
		if (seg_idx < F75308_MAX_FAN_SEG_CNT)
			seg5[seg_idx++] = seg_val;
		else
			break;
	}

	if (seg_idx != F75308_MAX_FAN_SEG_CNT) {
		dev_err(dev, "invalid 5seg property of %pOFn\n", child);
		return -EINVAL;
	}

	status = __f75308_fan_5_seg_store(dev, reg_idx, seg5);
	if (status)
		return status;

	return status;
}

static int f75308_probe_from_dt(struct device *dev, struct i2c_client *client)
{
	const struct device_node *np = dev->of_node;
	struct device_node *child;
	int status;

	/* Compatible with non-DT platforms */
	if (!np)
		return 0;

	for_each_child_of_node(np, child) {
		status = f75308_probe_child_from_dt(dev, child, client);
		if (status) {
			of_node_put(child);
			return status;
		}
	}

	return 0;
}

static int f75308_probe(struct i2c_client *client)
{
	struct device *dev = &client->dev;
	struct f75308_priv *priv;
	int status;

	if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA))
		return -EIO;

	priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
	if (!priv)
		return -ENOMEM;

	mutex_init(&priv->locker);
	priv->client = client;
	dev_set_drvdata(dev, priv);

	status = f75308_get_devid(client, &priv->chip_id);
	if (status) {
		dev_err(dev, "Unable to get f75308 id: %d\n", status);
		goto destroy_lock;
	}

	status = f75308_init(client, priv->chip_id);
	if (status) {
		dev_err(dev, "Unable to initialize f75308: %d\n", status);
		goto destroy_lock;
	}

	status = f75308_probe_from_dt(dev, priv->client);
	if (status) {
		dev_err(dev, "Unable to parse all dt entries: %d\n", status);
		goto destroy_lock;
	}

	priv->hwmon_dev = devm_hwmon_device_register_with_groups(
		dev, DEVICE_NAME, priv, f75308_groups[priv->chip_id]);
	if (IS_ERR(priv->hwmon_dev)) {
		status = PTR_ERR(priv->hwmon_dev);
		if (status)
			goto destroy_lock;
	}

	return 0;

destroy_lock:
	mutex_destroy(&priv->locker);
	return status;
}

#if defined(NV_I2C_DRIVER_STRUCT_REMOVE_RETURN_TYPE_INT) /* Linux 6.1 */
static int f75308_remove(struct i2c_client *client)
#else
static void f75308_remove(struct i2c_client *client)
#endif
{
	struct f75308_priv *priv = dev_get_drvdata(&client->dev);

	mutex_destroy(&priv->locker);
#if defined(NV_I2C_DRIVER_STRUCT_REMOVE_RETURN_TYPE_INT) /* Linux 6.1 */
	return 0;
#endif
}

static const unsigned short f75308_addr[] = {
	0x2C, 0x2D, 0x2E, 0x2F, 0x4C, 0x4D, 0x4E, 0x4F, I2C_CLIENT_END,
};

static const struct i2c_device_id f75308_id[] = { { "F75308CU", f75308c_64 },
						  { "F75308BD", f75308b_48 },
						  { "F75308AR", f75308a_28 },
						  {} };
MODULE_DEVICE_TABLE(i2c, f75308_id);

#ifdef CONFIG_OF
static const struct of_device_id f75308_match_table[] = {
	{ .compatible = "fintek,f75308" },
	{},
};
MODULE_DEVICE_TABLE(of, f75308_match_table);
#else
#define f75308_match_table NULL
#endif

static struct i2c_driver f75308_driver = {
	.class = I2C_CLASS_HWMON,
	.driver = {
		.name = DEVICE_NAME,
		.owner = THIS_MODULE,
		.of_match_table = of_match_ptr(f75308_match_table),
	},
	.detect = f75308_detect,
#if defined(NV_I2C_DRIVER_STRUCT_HAS_PROBE_NEW) /* Dropped on Linux 6.6 */
	.probe_new = f75308_probe,
#else
	.probe     = f75308_probe,
#endif
	.remove = f75308_remove,
	.address_list = f75308_addr,
	.id_table = f75308_id,
};

module_i2c_driver(f75308_driver);

MODULE_AUTHOR("Ji-Ze Hong (Peter Hong) <hpeter+linux_kernel@gmail.com>");
MODULE_AUTHOR("Yi-Wei Wang <yiweiw@nvidia.com>");
MODULE_LICENSE("GPL v2");
MODULE_DESCRIPTION("F75308 hardware monitoring driver");
