18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * emc2103.c - Support for SMSC EMC2103
48c2ecf20Sopenharmony_ci * Copyright (c) 2010 SMSC
58c2ecf20Sopenharmony_ci */
68c2ecf20Sopenharmony_ci
78c2ecf20Sopenharmony_ci#include <linux/module.h>
88c2ecf20Sopenharmony_ci#include <linux/init.h>
98c2ecf20Sopenharmony_ci#include <linux/slab.h>
108c2ecf20Sopenharmony_ci#include <linux/jiffies.h>
118c2ecf20Sopenharmony_ci#include <linux/i2c.h>
128c2ecf20Sopenharmony_ci#include <linux/hwmon.h>
138c2ecf20Sopenharmony_ci#include <linux/hwmon-sysfs.h>
148c2ecf20Sopenharmony_ci#include <linux/err.h>
158c2ecf20Sopenharmony_ci#include <linux/mutex.h>
168c2ecf20Sopenharmony_ci
178c2ecf20Sopenharmony_ci/* Addresses scanned */
188c2ecf20Sopenharmony_cistatic const unsigned short normal_i2c[] = { 0x2E, I2C_CLIENT_END };
198c2ecf20Sopenharmony_ci
208c2ecf20Sopenharmony_cistatic const u8 REG_TEMP[4] = { 0x00, 0x02, 0x04, 0x06 };
218c2ecf20Sopenharmony_cistatic const u8 REG_TEMP_MIN[4] = { 0x3c, 0x38, 0x39, 0x3a };
228c2ecf20Sopenharmony_cistatic const u8 REG_TEMP_MAX[4] = { 0x34, 0x30, 0x31, 0x32 };
238c2ecf20Sopenharmony_ci
248c2ecf20Sopenharmony_ci#define REG_CONF1		0x20
258c2ecf20Sopenharmony_ci#define REG_TEMP_MAX_ALARM	0x24
268c2ecf20Sopenharmony_ci#define REG_TEMP_MIN_ALARM	0x25
278c2ecf20Sopenharmony_ci#define REG_FAN_CONF1		0x42
288c2ecf20Sopenharmony_ci#define REG_FAN_TARGET_LO	0x4c
298c2ecf20Sopenharmony_ci#define REG_FAN_TARGET_HI	0x4d
308c2ecf20Sopenharmony_ci#define REG_FAN_TACH_HI		0x4e
318c2ecf20Sopenharmony_ci#define REG_FAN_TACH_LO		0x4f
328c2ecf20Sopenharmony_ci#define REG_PRODUCT_ID		0xfd
338c2ecf20Sopenharmony_ci#define REG_MFG_ID		0xfe
348c2ecf20Sopenharmony_ci
358c2ecf20Sopenharmony_ci/* equation 4 from datasheet: rpm = (3932160 * multipler) / count */
368c2ecf20Sopenharmony_ci#define FAN_RPM_FACTOR		3932160
378c2ecf20Sopenharmony_ci
388c2ecf20Sopenharmony_ci/*
398c2ecf20Sopenharmony_ci * 2103-2 and 2103-4's 3rd temperature sensor can be connected to two diodes
408c2ecf20Sopenharmony_ci * in anti-parallel mode, and in this configuration both can be read
418c2ecf20Sopenharmony_ci * independently (so we have 4 temperature inputs).  The device can't
428c2ecf20Sopenharmony_ci * detect if it's connected in this mode, so we have to manually enable
438c2ecf20Sopenharmony_ci * it.  Default is to leave the device in the state it's already in (-1).
448c2ecf20Sopenharmony_ci * This parameter allows APD mode to be optionally forced on or off
458c2ecf20Sopenharmony_ci */
468c2ecf20Sopenharmony_cistatic int apd = -1;
478c2ecf20Sopenharmony_cimodule_param(apd, bint, 0);
488c2ecf20Sopenharmony_ciMODULE_PARM_DESC(apd, "Set to zero to disable anti-parallel diode mode");
498c2ecf20Sopenharmony_ci
508c2ecf20Sopenharmony_cistruct temperature {
518c2ecf20Sopenharmony_ci	s8	degrees;
528c2ecf20Sopenharmony_ci	u8	fraction;	/* 0-7 multiples of 0.125 */
538c2ecf20Sopenharmony_ci};
548c2ecf20Sopenharmony_ci
558c2ecf20Sopenharmony_cistruct emc2103_data {
568c2ecf20Sopenharmony_ci	struct i2c_client	*client;
578c2ecf20Sopenharmony_ci	const struct		attribute_group *groups[4];
588c2ecf20Sopenharmony_ci	struct mutex		update_lock;
598c2ecf20Sopenharmony_ci	bool			valid;		/* registers are valid */
608c2ecf20Sopenharmony_ci	bool			fan_rpm_control;
618c2ecf20Sopenharmony_ci	int			temp_count;	/* num of temp sensors */
628c2ecf20Sopenharmony_ci	unsigned long		last_updated;	/* in jiffies */
638c2ecf20Sopenharmony_ci	struct temperature	temp[4];	/* internal + 3 external */
648c2ecf20Sopenharmony_ci	s8			temp_min[4];	/* no fractional part */
658c2ecf20Sopenharmony_ci	s8			temp_max[4];    /* no fractional part */
668c2ecf20Sopenharmony_ci	u8			temp_min_alarm;
678c2ecf20Sopenharmony_ci	u8			temp_max_alarm;
688c2ecf20Sopenharmony_ci	u8			fan_multiplier;
698c2ecf20Sopenharmony_ci	u16			fan_tach;
708c2ecf20Sopenharmony_ci	u16			fan_target;
718c2ecf20Sopenharmony_ci};
728c2ecf20Sopenharmony_ci
738c2ecf20Sopenharmony_cistatic int read_u8_from_i2c(struct i2c_client *client, u8 i2c_reg, u8 *output)
748c2ecf20Sopenharmony_ci{
758c2ecf20Sopenharmony_ci	int status = i2c_smbus_read_byte_data(client, i2c_reg);
768c2ecf20Sopenharmony_ci	if (status < 0) {
778c2ecf20Sopenharmony_ci		dev_warn(&client->dev, "reg 0x%02x, err %d\n",
788c2ecf20Sopenharmony_ci			i2c_reg, status);
798c2ecf20Sopenharmony_ci	} else {
808c2ecf20Sopenharmony_ci		*output = status;
818c2ecf20Sopenharmony_ci	}
828c2ecf20Sopenharmony_ci	return status;
838c2ecf20Sopenharmony_ci}
848c2ecf20Sopenharmony_ci
858c2ecf20Sopenharmony_cistatic void read_temp_from_i2c(struct i2c_client *client, u8 i2c_reg,
868c2ecf20Sopenharmony_ci			       struct temperature *temp)
878c2ecf20Sopenharmony_ci{
888c2ecf20Sopenharmony_ci	u8 degrees, fractional;
898c2ecf20Sopenharmony_ci
908c2ecf20Sopenharmony_ci	if (read_u8_from_i2c(client, i2c_reg, &degrees) < 0)
918c2ecf20Sopenharmony_ci		return;
928c2ecf20Sopenharmony_ci
938c2ecf20Sopenharmony_ci	if (read_u8_from_i2c(client, i2c_reg + 1, &fractional) < 0)
948c2ecf20Sopenharmony_ci		return;
958c2ecf20Sopenharmony_ci
968c2ecf20Sopenharmony_ci	temp->degrees = degrees;
978c2ecf20Sopenharmony_ci	temp->fraction = (fractional & 0xe0) >> 5;
988c2ecf20Sopenharmony_ci}
998c2ecf20Sopenharmony_ci
1008c2ecf20Sopenharmony_cistatic void read_fan_from_i2c(struct i2c_client *client, u16 *output,
1018c2ecf20Sopenharmony_ci			      u8 hi_addr, u8 lo_addr)
1028c2ecf20Sopenharmony_ci{
1038c2ecf20Sopenharmony_ci	u8 high_byte, lo_byte;
1048c2ecf20Sopenharmony_ci
1058c2ecf20Sopenharmony_ci	if (read_u8_from_i2c(client, hi_addr, &high_byte) < 0)
1068c2ecf20Sopenharmony_ci		return;
1078c2ecf20Sopenharmony_ci
1088c2ecf20Sopenharmony_ci	if (read_u8_from_i2c(client, lo_addr, &lo_byte) < 0)
1098c2ecf20Sopenharmony_ci		return;
1108c2ecf20Sopenharmony_ci
1118c2ecf20Sopenharmony_ci	*output = ((u16)high_byte << 5) | (lo_byte >> 3);
1128c2ecf20Sopenharmony_ci}
1138c2ecf20Sopenharmony_ci
1148c2ecf20Sopenharmony_cistatic void write_fan_target_to_i2c(struct i2c_client *client, u16 new_target)
1158c2ecf20Sopenharmony_ci{
1168c2ecf20Sopenharmony_ci	u8 high_byte = (new_target & 0x1fe0) >> 5;
1178c2ecf20Sopenharmony_ci	u8 low_byte = (new_target & 0x001f) << 3;
1188c2ecf20Sopenharmony_ci	i2c_smbus_write_byte_data(client, REG_FAN_TARGET_LO, low_byte);
1198c2ecf20Sopenharmony_ci	i2c_smbus_write_byte_data(client, REG_FAN_TARGET_HI, high_byte);
1208c2ecf20Sopenharmony_ci}
1218c2ecf20Sopenharmony_ci
1228c2ecf20Sopenharmony_cistatic void read_fan_config_from_i2c(struct i2c_client *client)
1238c2ecf20Sopenharmony_ci
1248c2ecf20Sopenharmony_ci{
1258c2ecf20Sopenharmony_ci	struct emc2103_data *data = i2c_get_clientdata(client);
1268c2ecf20Sopenharmony_ci	u8 conf1;
1278c2ecf20Sopenharmony_ci
1288c2ecf20Sopenharmony_ci	if (read_u8_from_i2c(client, REG_FAN_CONF1, &conf1) < 0)
1298c2ecf20Sopenharmony_ci		return;
1308c2ecf20Sopenharmony_ci
1318c2ecf20Sopenharmony_ci	data->fan_multiplier = 1 << ((conf1 & 0x60) >> 5);
1328c2ecf20Sopenharmony_ci	data->fan_rpm_control = (conf1 & 0x80) != 0;
1338c2ecf20Sopenharmony_ci}
1348c2ecf20Sopenharmony_ci
1358c2ecf20Sopenharmony_cistatic struct emc2103_data *emc2103_update_device(struct device *dev)
1368c2ecf20Sopenharmony_ci{
1378c2ecf20Sopenharmony_ci	struct emc2103_data *data = dev_get_drvdata(dev);
1388c2ecf20Sopenharmony_ci	struct i2c_client *client = data->client;
1398c2ecf20Sopenharmony_ci
1408c2ecf20Sopenharmony_ci	mutex_lock(&data->update_lock);
1418c2ecf20Sopenharmony_ci
1428c2ecf20Sopenharmony_ci	if (time_after(jiffies, data->last_updated + HZ + HZ / 2)
1438c2ecf20Sopenharmony_ci	    || !data->valid) {
1448c2ecf20Sopenharmony_ci		int i;
1458c2ecf20Sopenharmony_ci
1468c2ecf20Sopenharmony_ci		for (i = 0; i < data->temp_count; i++) {
1478c2ecf20Sopenharmony_ci			read_temp_from_i2c(client, REG_TEMP[i], &data->temp[i]);
1488c2ecf20Sopenharmony_ci			read_u8_from_i2c(client, REG_TEMP_MIN[i],
1498c2ecf20Sopenharmony_ci				&data->temp_min[i]);
1508c2ecf20Sopenharmony_ci			read_u8_from_i2c(client, REG_TEMP_MAX[i],
1518c2ecf20Sopenharmony_ci				&data->temp_max[i]);
1528c2ecf20Sopenharmony_ci		}
1538c2ecf20Sopenharmony_ci
1548c2ecf20Sopenharmony_ci		read_u8_from_i2c(client, REG_TEMP_MIN_ALARM,
1558c2ecf20Sopenharmony_ci			&data->temp_min_alarm);
1568c2ecf20Sopenharmony_ci		read_u8_from_i2c(client, REG_TEMP_MAX_ALARM,
1578c2ecf20Sopenharmony_ci			&data->temp_max_alarm);
1588c2ecf20Sopenharmony_ci
1598c2ecf20Sopenharmony_ci		read_fan_from_i2c(client, &data->fan_tach,
1608c2ecf20Sopenharmony_ci			REG_FAN_TACH_HI, REG_FAN_TACH_LO);
1618c2ecf20Sopenharmony_ci		read_fan_from_i2c(client, &data->fan_target,
1628c2ecf20Sopenharmony_ci			REG_FAN_TARGET_HI, REG_FAN_TARGET_LO);
1638c2ecf20Sopenharmony_ci		read_fan_config_from_i2c(client);
1648c2ecf20Sopenharmony_ci
1658c2ecf20Sopenharmony_ci		data->last_updated = jiffies;
1668c2ecf20Sopenharmony_ci		data->valid = true;
1678c2ecf20Sopenharmony_ci	}
1688c2ecf20Sopenharmony_ci
1698c2ecf20Sopenharmony_ci	mutex_unlock(&data->update_lock);
1708c2ecf20Sopenharmony_ci
1718c2ecf20Sopenharmony_ci	return data;
1728c2ecf20Sopenharmony_ci}
1738c2ecf20Sopenharmony_ci
1748c2ecf20Sopenharmony_cistatic ssize_t
1758c2ecf20Sopenharmony_citemp_show(struct device *dev, struct device_attribute *da, char *buf)
1768c2ecf20Sopenharmony_ci{
1778c2ecf20Sopenharmony_ci	int nr = to_sensor_dev_attr(da)->index;
1788c2ecf20Sopenharmony_ci	struct emc2103_data *data = emc2103_update_device(dev);
1798c2ecf20Sopenharmony_ci	int millidegrees = data->temp[nr].degrees * 1000
1808c2ecf20Sopenharmony_ci		+ data->temp[nr].fraction * 125;
1818c2ecf20Sopenharmony_ci	return sprintf(buf, "%d\n", millidegrees);
1828c2ecf20Sopenharmony_ci}
1838c2ecf20Sopenharmony_ci
1848c2ecf20Sopenharmony_cistatic ssize_t
1858c2ecf20Sopenharmony_citemp_min_show(struct device *dev, struct device_attribute *da, char *buf)
1868c2ecf20Sopenharmony_ci{
1878c2ecf20Sopenharmony_ci	int nr = to_sensor_dev_attr(da)->index;
1888c2ecf20Sopenharmony_ci	struct emc2103_data *data = emc2103_update_device(dev);
1898c2ecf20Sopenharmony_ci	int millidegrees = data->temp_min[nr] * 1000;
1908c2ecf20Sopenharmony_ci	return sprintf(buf, "%d\n", millidegrees);
1918c2ecf20Sopenharmony_ci}
1928c2ecf20Sopenharmony_ci
1938c2ecf20Sopenharmony_cistatic ssize_t
1948c2ecf20Sopenharmony_citemp_max_show(struct device *dev, struct device_attribute *da, char *buf)
1958c2ecf20Sopenharmony_ci{
1968c2ecf20Sopenharmony_ci	int nr = to_sensor_dev_attr(da)->index;
1978c2ecf20Sopenharmony_ci	struct emc2103_data *data = emc2103_update_device(dev);
1988c2ecf20Sopenharmony_ci	int millidegrees = data->temp_max[nr] * 1000;
1998c2ecf20Sopenharmony_ci	return sprintf(buf, "%d\n", millidegrees);
2008c2ecf20Sopenharmony_ci}
2018c2ecf20Sopenharmony_ci
2028c2ecf20Sopenharmony_cistatic ssize_t
2038c2ecf20Sopenharmony_citemp_fault_show(struct device *dev, struct device_attribute *da, char *buf)
2048c2ecf20Sopenharmony_ci{
2058c2ecf20Sopenharmony_ci	int nr = to_sensor_dev_attr(da)->index;
2068c2ecf20Sopenharmony_ci	struct emc2103_data *data = emc2103_update_device(dev);
2078c2ecf20Sopenharmony_ci	bool fault = (data->temp[nr].degrees == -128);
2088c2ecf20Sopenharmony_ci	return sprintf(buf, "%d\n", fault ? 1 : 0);
2098c2ecf20Sopenharmony_ci}
2108c2ecf20Sopenharmony_ci
2118c2ecf20Sopenharmony_cistatic ssize_t
2128c2ecf20Sopenharmony_citemp_min_alarm_show(struct device *dev, struct device_attribute *da,
2138c2ecf20Sopenharmony_ci		    char *buf)
2148c2ecf20Sopenharmony_ci{
2158c2ecf20Sopenharmony_ci	int nr = to_sensor_dev_attr(da)->index;
2168c2ecf20Sopenharmony_ci	struct emc2103_data *data = emc2103_update_device(dev);
2178c2ecf20Sopenharmony_ci	bool alarm = data->temp_min_alarm & (1 << nr);
2188c2ecf20Sopenharmony_ci	return sprintf(buf, "%d\n", alarm ? 1 : 0);
2198c2ecf20Sopenharmony_ci}
2208c2ecf20Sopenharmony_ci
2218c2ecf20Sopenharmony_cistatic ssize_t
2228c2ecf20Sopenharmony_citemp_max_alarm_show(struct device *dev, struct device_attribute *da,
2238c2ecf20Sopenharmony_ci		    char *buf)
2248c2ecf20Sopenharmony_ci{
2258c2ecf20Sopenharmony_ci	int nr = to_sensor_dev_attr(da)->index;
2268c2ecf20Sopenharmony_ci	struct emc2103_data *data = emc2103_update_device(dev);
2278c2ecf20Sopenharmony_ci	bool alarm = data->temp_max_alarm & (1 << nr);
2288c2ecf20Sopenharmony_ci	return sprintf(buf, "%d\n", alarm ? 1 : 0);
2298c2ecf20Sopenharmony_ci}
2308c2ecf20Sopenharmony_ci
2318c2ecf20Sopenharmony_cistatic ssize_t temp_min_store(struct device *dev, struct device_attribute *da,
2328c2ecf20Sopenharmony_ci			      const char *buf, size_t count)
2338c2ecf20Sopenharmony_ci{
2348c2ecf20Sopenharmony_ci	int nr = to_sensor_dev_attr(da)->index;
2358c2ecf20Sopenharmony_ci	struct emc2103_data *data = dev_get_drvdata(dev);
2368c2ecf20Sopenharmony_ci	struct i2c_client *client = data->client;
2378c2ecf20Sopenharmony_ci	long val;
2388c2ecf20Sopenharmony_ci
2398c2ecf20Sopenharmony_ci	int result = kstrtol(buf, 10, &val);
2408c2ecf20Sopenharmony_ci	if (result < 0)
2418c2ecf20Sopenharmony_ci		return result;
2428c2ecf20Sopenharmony_ci
2438c2ecf20Sopenharmony_ci	val = DIV_ROUND_CLOSEST(clamp_val(val, -63000, 127000), 1000);
2448c2ecf20Sopenharmony_ci
2458c2ecf20Sopenharmony_ci	mutex_lock(&data->update_lock);
2468c2ecf20Sopenharmony_ci	data->temp_min[nr] = val;
2478c2ecf20Sopenharmony_ci	i2c_smbus_write_byte_data(client, REG_TEMP_MIN[nr], val);
2488c2ecf20Sopenharmony_ci	mutex_unlock(&data->update_lock);
2498c2ecf20Sopenharmony_ci
2508c2ecf20Sopenharmony_ci	return count;
2518c2ecf20Sopenharmony_ci}
2528c2ecf20Sopenharmony_ci
2538c2ecf20Sopenharmony_cistatic ssize_t temp_max_store(struct device *dev, struct device_attribute *da,
2548c2ecf20Sopenharmony_ci			      const char *buf, size_t count)
2558c2ecf20Sopenharmony_ci{
2568c2ecf20Sopenharmony_ci	int nr = to_sensor_dev_attr(da)->index;
2578c2ecf20Sopenharmony_ci	struct emc2103_data *data = dev_get_drvdata(dev);
2588c2ecf20Sopenharmony_ci	struct i2c_client *client = data->client;
2598c2ecf20Sopenharmony_ci	long val;
2608c2ecf20Sopenharmony_ci
2618c2ecf20Sopenharmony_ci	int result = kstrtol(buf, 10, &val);
2628c2ecf20Sopenharmony_ci	if (result < 0)
2638c2ecf20Sopenharmony_ci		return result;
2648c2ecf20Sopenharmony_ci
2658c2ecf20Sopenharmony_ci	val = DIV_ROUND_CLOSEST(clamp_val(val, -63000, 127000), 1000);
2668c2ecf20Sopenharmony_ci
2678c2ecf20Sopenharmony_ci	mutex_lock(&data->update_lock);
2688c2ecf20Sopenharmony_ci	data->temp_max[nr] = val;
2698c2ecf20Sopenharmony_ci	i2c_smbus_write_byte_data(client, REG_TEMP_MAX[nr], val);
2708c2ecf20Sopenharmony_ci	mutex_unlock(&data->update_lock);
2718c2ecf20Sopenharmony_ci
2728c2ecf20Sopenharmony_ci	return count;
2738c2ecf20Sopenharmony_ci}
2748c2ecf20Sopenharmony_ci
2758c2ecf20Sopenharmony_cistatic ssize_t
2768c2ecf20Sopenharmony_cifan1_input_show(struct device *dev, struct device_attribute *da, char *buf)
2778c2ecf20Sopenharmony_ci{
2788c2ecf20Sopenharmony_ci	struct emc2103_data *data = emc2103_update_device(dev);
2798c2ecf20Sopenharmony_ci	int rpm = 0;
2808c2ecf20Sopenharmony_ci	if (data->fan_tach != 0)
2818c2ecf20Sopenharmony_ci		rpm = (FAN_RPM_FACTOR * data->fan_multiplier) / data->fan_tach;
2828c2ecf20Sopenharmony_ci	return sprintf(buf, "%d\n", rpm);
2838c2ecf20Sopenharmony_ci}
2848c2ecf20Sopenharmony_ci
2858c2ecf20Sopenharmony_cistatic ssize_t
2868c2ecf20Sopenharmony_cifan1_div_show(struct device *dev, struct device_attribute *da, char *buf)
2878c2ecf20Sopenharmony_ci{
2888c2ecf20Sopenharmony_ci	struct emc2103_data *data = emc2103_update_device(dev);
2898c2ecf20Sopenharmony_ci	int fan_div = 8 / data->fan_multiplier;
2908c2ecf20Sopenharmony_ci	return sprintf(buf, "%d\n", fan_div);
2918c2ecf20Sopenharmony_ci}
2928c2ecf20Sopenharmony_ci
2938c2ecf20Sopenharmony_ci/*
2948c2ecf20Sopenharmony_ci * Note: we also update the fan target here, because its value is
2958c2ecf20Sopenharmony_ci * determined in part by the fan clock divider.  This follows the principle
2968c2ecf20Sopenharmony_ci * of least surprise; the user doesn't expect the fan target to change just
2978c2ecf20Sopenharmony_ci * because the divider changed.
2988c2ecf20Sopenharmony_ci */
2998c2ecf20Sopenharmony_cistatic ssize_t fan1_div_store(struct device *dev, struct device_attribute *da,
3008c2ecf20Sopenharmony_ci			      const char *buf, size_t count)
3018c2ecf20Sopenharmony_ci{
3028c2ecf20Sopenharmony_ci	struct emc2103_data *data = emc2103_update_device(dev);
3038c2ecf20Sopenharmony_ci	struct i2c_client *client = data->client;
3048c2ecf20Sopenharmony_ci	int new_range_bits, old_div = 8 / data->fan_multiplier;
3058c2ecf20Sopenharmony_ci	long new_div;
3068c2ecf20Sopenharmony_ci
3078c2ecf20Sopenharmony_ci	int status = kstrtol(buf, 10, &new_div);
3088c2ecf20Sopenharmony_ci	if (status < 0)
3098c2ecf20Sopenharmony_ci		return status;
3108c2ecf20Sopenharmony_ci
3118c2ecf20Sopenharmony_ci	if (new_div == old_div) /* No change */
3128c2ecf20Sopenharmony_ci		return count;
3138c2ecf20Sopenharmony_ci
3148c2ecf20Sopenharmony_ci	switch (new_div) {
3158c2ecf20Sopenharmony_ci	case 1:
3168c2ecf20Sopenharmony_ci		new_range_bits = 3;
3178c2ecf20Sopenharmony_ci		break;
3188c2ecf20Sopenharmony_ci	case 2:
3198c2ecf20Sopenharmony_ci		new_range_bits = 2;
3208c2ecf20Sopenharmony_ci		break;
3218c2ecf20Sopenharmony_ci	case 4:
3228c2ecf20Sopenharmony_ci		new_range_bits = 1;
3238c2ecf20Sopenharmony_ci		break;
3248c2ecf20Sopenharmony_ci	case 8:
3258c2ecf20Sopenharmony_ci		new_range_bits = 0;
3268c2ecf20Sopenharmony_ci		break;
3278c2ecf20Sopenharmony_ci	default:
3288c2ecf20Sopenharmony_ci		return -EINVAL;
3298c2ecf20Sopenharmony_ci	}
3308c2ecf20Sopenharmony_ci
3318c2ecf20Sopenharmony_ci	mutex_lock(&data->update_lock);
3328c2ecf20Sopenharmony_ci
3338c2ecf20Sopenharmony_ci	status = i2c_smbus_read_byte_data(client, REG_FAN_CONF1);
3348c2ecf20Sopenharmony_ci	if (status < 0) {
3358c2ecf20Sopenharmony_ci		dev_dbg(&client->dev, "reg 0x%02x, err %d\n",
3368c2ecf20Sopenharmony_ci			REG_FAN_CONF1, status);
3378c2ecf20Sopenharmony_ci		mutex_unlock(&data->update_lock);
3388c2ecf20Sopenharmony_ci		return status;
3398c2ecf20Sopenharmony_ci	}
3408c2ecf20Sopenharmony_ci	status &= 0x9F;
3418c2ecf20Sopenharmony_ci	status |= (new_range_bits << 5);
3428c2ecf20Sopenharmony_ci	i2c_smbus_write_byte_data(client, REG_FAN_CONF1, status);
3438c2ecf20Sopenharmony_ci
3448c2ecf20Sopenharmony_ci	data->fan_multiplier = 8 / new_div;
3458c2ecf20Sopenharmony_ci
3468c2ecf20Sopenharmony_ci	/* update fan target if high byte is not disabled */
3478c2ecf20Sopenharmony_ci	if ((data->fan_target & 0x1fe0) != 0x1fe0) {
3488c2ecf20Sopenharmony_ci		u16 new_target = (data->fan_target * old_div) / new_div;
3498c2ecf20Sopenharmony_ci		data->fan_target = min(new_target, (u16)0x1fff);
3508c2ecf20Sopenharmony_ci		write_fan_target_to_i2c(client, data->fan_target);
3518c2ecf20Sopenharmony_ci	}
3528c2ecf20Sopenharmony_ci
3538c2ecf20Sopenharmony_ci	/* invalidate data to force re-read from hardware */
3548c2ecf20Sopenharmony_ci	data->valid = false;
3558c2ecf20Sopenharmony_ci
3568c2ecf20Sopenharmony_ci	mutex_unlock(&data->update_lock);
3578c2ecf20Sopenharmony_ci	return count;
3588c2ecf20Sopenharmony_ci}
3598c2ecf20Sopenharmony_ci
3608c2ecf20Sopenharmony_cistatic ssize_t
3618c2ecf20Sopenharmony_cifan1_target_show(struct device *dev, struct device_attribute *da, char *buf)
3628c2ecf20Sopenharmony_ci{
3638c2ecf20Sopenharmony_ci	struct emc2103_data *data = emc2103_update_device(dev);
3648c2ecf20Sopenharmony_ci	int rpm = 0;
3658c2ecf20Sopenharmony_ci
3668c2ecf20Sopenharmony_ci	/* high byte of 0xff indicates disabled so return 0 */
3678c2ecf20Sopenharmony_ci	if ((data->fan_target != 0) && ((data->fan_target & 0x1fe0) != 0x1fe0))
3688c2ecf20Sopenharmony_ci		rpm = (FAN_RPM_FACTOR * data->fan_multiplier)
3698c2ecf20Sopenharmony_ci			/ data->fan_target;
3708c2ecf20Sopenharmony_ci
3718c2ecf20Sopenharmony_ci	return sprintf(buf, "%d\n", rpm);
3728c2ecf20Sopenharmony_ci}
3738c2ecf20Sopenharmony_ci
3748c2ecf20Sopenharmony_cistatic ssize_t fan1_target_store(struct device *dev,
3758c2ecf20Sopenharmony_ci				 struct device_attribute *da, const char *buf,
3768c2ecf20Sopenharmony_ci				 size_t count)
3778c2ecf20Sopenharmony_ci{
3788c2ecf20Sopenharmony_ci	struct emc2103_data *data = emc2103_update_device(dev);
3798c2ecf20Sopenharmony_ci	struct i2c_client *client = data->client;
3808c2ecf20Sopenharmony_ci	unsigned long rpm_target;
3818c2ecf20Sopenharmony_ci
3828c2ecf20Sopenharmony_ci	int result = kstrtoul(buf, 10, &rpm_target);
3838c2ecf20Sopenharmony_ci	if (result < 0)
3848c2ecf20Sopenharmony_ci		return result;
3858c2ecf20Sopenharmony_ci
3868c2ecf20Sopenharmony_ci	/* Datasheet states 16384 as maximum RPM target (table 3.2) */
3878c2ecf20Sopenharmony_ci	rpm_target = clamp_val(rpm_target, 0, 16384);
3888c2ecf20Sopenharmony_ci
3898c2ecf20Sopenharmony_ci	mutex_lock(&data->update_lock);
3908c2ecf20Sopenharmony_ci
3918c2ecf20Sopenharmony_ci	if (rpm_target == 0)
3928c2ecf20Sopenharmony_ci		data->fan_target = 0x1fff;
3938c2ecf20Sopenharmony_ci	else
3948c2ecf20Sopenharmony_ci		data->fan_target = clamp_val(
3958c2ecf20Sopenharmony_ci			(FAN_RPM_FACTOR * data->fan_multiplier) / rpm_target,
3968c2ecf20Sopenharmony_ci			0, 0x1fff);
3978c2ecf20Sopenharmony_ci
3988c2ecf20Sopenharmony_ci	write_fan_target_to_i2c(client, data->fan_target);
3998c2ecf20Sopenharmony_ci
4008c2ecf20Sopenharmony_ci	mutex_unlock(&data->update_lock);
4018c2ecf20Sopenharmony_ci	return count;
4028c2ecf20Sopenharmony_ci}
4038c2ecf20Sopenharmony_ci
4048c2ecf20Sopenharmony_cistatic ssize_t
4058c2ecf20Sopenharmony_cifan1_fault_show(struct device *dev, struct device_attribute *da, char *buf)
4068c2ecf20Sopenharmony_ci{
4078c2ecf20Sopenharmony_ci	struct emc2103_data *data = emc2103_update_device(dev);
4088c2ecf20Sopenharmony_ci	bool fault = ((data->fan_tach & 0x1fe0) == 0x1fe0);
4098c2ecf20Sopenharmony_ci	return sprintf(buf, "%d\n", fault ? 1 : 0);
4108c2ecf20Sopenharmony_ci}
4118c2ecf20Sopenharmony_ci
4128c2ecf20Sopenharmony_cistatic ssize_t
4138c2ecf20Sopenharmony_cipwm1_enable_show(struct device *dev, struct device_attribute *da, char *buf)
4148c2ecf20Sopenharmony_ci{
4158c2ecf20Sopenharmony_ci	struct emc2103_data *data = emc2103_update_device(dev);
4168c2ecf20Sopenharmony_ci	return sprintf(buf, "%d\n", data->fan_rpm_control ? 3 : 0);
4178c2ecf20Sopenharmony_ci}
4188c2ecf20Sopenharmony_ci
4198c2ecf20Sopenharmony_cistatic ssize_t pwm1_enable_store(struct device *dev,
4208c2ecf20Sopenharmony_ci				 struct device_attribute *da, const char *buf,
4218c2ecf20Sopenharmony_ci				 size_t count)
4228c2ecf20Sopenharmony_ci{
4238c2ecf20Sopenharmony_ci	struct emc2103_data *data = dev_get_drvdata(dev);
4248c2ecf20Sopenharmony_ci	struct i2c_client *client = data->client;
4258c2ecf20Sopenharmony_ci	long new_value;
4268c2ecf20Sopenharmony_ci	u8 conf_reg;
4278c2ecf20Sopenharmony_ci
4288c2ecf20Sopenharmony_ci	int result = kstrtol(buf, 10, &new_value);
4298c2ecf20Sopenharmony_ci	if (result < 0)
4308c2ecf20Sopenharmony_ci		return result;
4318c2ecf20Sopenharmony_ci
4328c2ecf20Sopenharmony_ci	mutex_lock(&data->update_lock);
4338c2ecf20Sopenharmony_ci	switch (new_value) {
4348c2ecf20Sopenharmony_ci	case 0:
4358c2ecf20Sopenharmony_ci		data->fan_rpm_control = false;
4368c2ecf20Sopenharmony_ci		break;
4378c2ecf20Sopenharmony_ci	case 3:
4388c2ecf20Sopenharmony_ci		data->fan_rpm_control = true;
4398c2ecf20Sopenharmony_ci		break;
4408c2ecf20Sopenharmony_ci	default:
4418c2ecf20Sopenharmony_ci		count = -EINVAL;
4428c2ecf20Sopenharmony_ci		goto err;
4438c2ecf20Sopenharmony_ci	}
4448c2ecf20Sopenharmony_ci
4458c2ecf20Sopenharmony_ci	result = read_u8_from_i2c(client, REG_FAN_CONF1, &conf_reg);
4468c2ecf20Sopenharmony_ci	if (result < 0) {
4478c2ecf20Sopenharmony_ci		count = result;
4488c2ecf20Sopenharmony_ci		goto err;
4498c2ecf20Sopenharmony_ci	}
4508c2ecf20Sopenharmony_ci
4518c2ecf20Sopenharmony_ci	if (data->fan_rpm_control)
4528c2ecf20Sopenharmony_ci		conf_reg |= 0x80;
4538c2ecf20Sopenharmony_ci	else
4548c2ecf20Sopenharmony_ci		conf_reg &= ~0x80;
4558c2ecf20Sopenharmony_ci
4568c2ecf20Sopenharmony_ci	i2c_smbus_write_byte_data(client, REG_FAN_CONF1, conf_reg);
4578c2ecf20Sopenharmony_cierr:
4588c2ecf20Sopenharmony_ci	mutex_unlock(&data->update_lock);
4598c2ecf20Sopenharmony_ci	return count;
4608c2ecf20Sopenharmony_ci}
4618c2ecf20Sopenharmony_ci
4628c2ecf20Sopenharmony_cistatic SENSOR_DEVICE_ATTR_RO(temp1_input, temp, 0);
4638c2ecf20Sopenharmony_cistatic SENSOR_DEVICE_ATTR_RW(temp1_min, temp_min, 0);
4648c2ecf20Sopenharmony_cistatic SENSOR_DEVICE_ATTR_RW(temp1_max, temp_max, 0);
4658c2ecf20Sopenharmony_cistatic SENSOR_DEVICE_ATTR_RO(temp1_fault, temp_fault, 0);
4668c2ecf20Sopenharmony_cistatic SENSOR_DEVICE_ATTR_RO(temp1_min_alarm, temp_min_alarm, 0);
4678c2ecf20Sopenharmony_cistatic SENSOR_DEVICE_ATTR_RO(temp1_max_alarm, temp_max_alarm, 0);
4688c2ecf20Sopenharmony_ci
4698c2ecf20Sopenharmony_cistatic SENSOR_DEVICE_ATTR_RO(temp2_input, temp, 1);
4708c2ecf20Sopenharmony_cistatic SENSOR_DEVICE_ATTR_RW(temp2_min, temp_min, 1);
4718c2ecf20Sopenharmony_cistatic SENSOR_DEVICE_ATTR_RW(temp2_max, temp_max, 1);
4728c2ecf20Sopenharmony_cistatic SENSOR_DEVICE_ATTR_RO(temp2_fault, temp_fault, 1);
4738c2ecf20Sopenharmony_cistatic SENSOR_DEVICE_ATTR_RO(temp2_min_alarm, temp_min_alarm, 1);
4748c2ecf20Sopenharmony_cistatic SENSOR_DEVICE_ATTR_RO(temp2_max_alarm, temp_max_alarm, 1);
4758c2ecf20Sopenharmony_ci
4768c2ecf20Sopenharmony_cistatic SENSOR_DEVICE_ATTR_RO(temp3_input, temp, 2);
4778c2ecf20Sopenharmony_cistatic SENSOR_DEVICE_ATTR_RW(temp3_min, temp_min, 2);
4788c2ecf20Sopenharmony_cistatic SENSOR_DEVICE_ATTR_RW(temp3_max, temp_max, 2);
4798c2ecf20Sopenharmony_cistatic SENSOR_DEVICE_ATTR_RO(temp3_fault, temp_fault, 2);
4808c2ecf20Sopenharmony_cistatic SENSOR_DEVICE_ATTR_RO(temp3_min_alarm, temp_min_alarm, 2);
4818c2ecf20Sopenharmony_cistatic SENSOR_DEVICE_ATTR_RO(temp3_max_alarm, temp_max_alarm, 2);
4828c2ecf20Sopenharmony_ci
4838c2ecf20Sopenharmony_cistatic SENSOR_DEVICE_ATTR_RO(temp4_input, temp, 3);
4848c2ecf20Sopenharmony_cistatic SENSOR_DEVICE_ATTR_RW(temp4_min, temp_min, 3);
4858c2ecf20Sopenharmony_cistatic SENSOR_DEVICE_ATTR_RW(temp4_max, temp_max, 3);
4868c2ecf20Sopenharmony_cistatic SENSOR_DEVICE_ATTR_RO(temp4_fault, temp_fault, 3);
4878c2ecf20Sopenharmony_cistatic SENSOR_DEVICE_ATTR_RO(temp4_min_alarm, temp_min_alarm, 3);
4888c2ecf20Sopenharmony_cistatic SENSOR_DEVICE_ATTR_RO(temp4_max_alarm, temp_max_alarm, 3);
4898c2ecf20Sopenharmony_ci
4908c2ecf20Sopenharmony_cistatic DEVICE_ATTR_RO(fan1_input);
4918c2ecf20Sopenharmony_cistatic DEVICE_ATTR_RW(fan1_div);
4928c2ecf20Sopenharmony_cistatic DEVICE_ATTR_RW(fan1_target);
4938c2ecf20Sopenharmony_cistatic DEVICE_ATTR_RO(fan1_fault);
4948c2ecf20Sopenharmony_ci
4958c2ecf20Sopenharmony_cistatic DEVICE_ATTR_RW(pwm1_enable);
4968c2ecf20Sopenharmony_ci
4978c2ecf20Sopenharmony_ci/* sensors present on all models */
4988c2ecf20Sopenharmony_cistatic struct attribute *emc2103_attributes[] = {
4998c2ecf20Sopenharmony_ci	&sensor_dev_attr_temp1_input.dev_attr.attr,
5008c2ecf20Sopenharmony_ci	&sensor_dev_attr_temp1_min.dev_attr.attr,
5018c2ecf20Sopenharmony_ci	&sensor_dev_attr_temp1_max.dev_attr.attr,
5028c2ecf20Sopenharmony_ci	&sensor_dev_attr_temp1_fault.dev_attr.attr,
5038c2ecf20Sopenharmony_ci	&sensor_dev_attr_temp1_min_alarm.dev_attr.attr,
5048c2ecf20Sopenharmony_ci	&sensor_dev_attr_temp1_max_alarm.dev_attr.attr,
5058c2ecf20Sopenharmony_ci	&sensor_dev_attr_temp2_input.dev_attr.attr,
5068c2ecf20Sopenharmony_ci	&sensor_dev_attr_temp2_min.dev_attr.attr,
5078c2ecf20Sopenharmony_ci	&sensor_dev_attr_temp2_max.dev_attr.attr,
5088c2ecf20Sopenharmony_ci	&sensor_dev_attr_temp2_fault.dev_attr.attr,
5098c2ecf20Sopenharmony_ci	&sensor_dev_attr_temp2_min_alarm.dev_attr.attr,
5108c2ecf20Sopenharmony_ci	&sensor_dev_attr_temp2_max_alarm.dev_attr.attr,
5118c2ecf20Sopenharmony_ci	&dev_attr_fan1_input.attr,
5128c2ecf20Sopenharmony_ci	&dev_attr_fan1_div.attr,
5138c2ecf20Sopenharmony_ci	&dev_attr_fan1_target.attr,
5148c2ecf20Sopenharmony_ci	&dev_attr_fan1_fault.attr,
5158c2ecf20Sopenharmony_ci	&dev_attr_pwm1_enable.attr,
5168c2ecf20Sopenharmony_ci	NULL
5178c2ecf20Sopenharmony_ci};
5188c2ecf20Sopenharmony_ci
5198c2ecf20Sopenharmony_ci/* extra temperature sensors only present on 2103-2 and 2103-4 */
5208c2ecf20Sopenharmony_cistatic struct attribute *emc2103_attributes_temp3[] = {
5218c2ecf20Sopenharmony_ci	&sensor_dev_attr_temp3_input.dev_attr.attr,
5228c2ecf20Sopenharmony_ci	&sensor_dev_attr_temp3_min.dev_attr.attr,
5238c2ecf20Sopenharmony_ci	&sensor_dev_attr_temp3_max.dev_attr.attr,
5248c2ecf20Sopenharmony_ci	&sensor_dev_attr_temp3_fault.dev_attr.attr,
5258c2ecf20Sopenharmony_ci	&sensor_dev_attr_temp3_min_alarm.dev_attr.attr,
5268c2ecf20Sopenharmony_ci	&sensor_dev_attr_temp3_max_alarm.dev_attr.attr,
5278c2ecf20Sopenharmony_ci	NULL
5288c2ecf20Sopenharmony_ci};
5298c2ecf20Sopenharmony_ci
5308c2ecf20Sopenharmony_ci/* extra temperature sensors only present on 2103-2 and 2103-4 in APD mode */
5318c2ecf20Sopenharmony_cistatic struct attribute *emc2103_attributes_temp4[] = {
5328c2ecf20Sopenharmony_ci	&sensor_dev_attr_temp4_input.dev_attr.attr,
5338c2ecf20Sopenharmony_ci	&sensor_dev_attr_temp4_min.dev_attr.attr,
5348c2ecf20Sopenharmony_ci	&sensor_dev_attr_temp4_max.dev_attr.attr,
5358c2ecf20Sopenharmony_ci	&sensor_dev_attr_temp4_fault.dev_attr.attr,
5368c2ecf20Sopenharmony_ci	&sensor_dev_attr_temp4_min_alarm.dev_attr.attr,
5378c2ecf20Sopenharmony_ci	&sensor_dev_attr_temp4_max_alarm.dev_attr.attr,
5388c2ecf20Sopenharmony_ci	NULL
5398c2ecf20Sopenharmony_ci};
5408c2ecf20Sopenharmony_ci
5418c2ecf20Sopenharmony_cistatic const struct attribute_group emc2103_group = {
5428c2ecf20Sopenharmony_ci	.attrs = emc2103_attributes,
5438c2ecf20Sopenharmony_ci};
5448c2ecf20Sopenharmony_ci
5458c2ecf20Sopenharmony_cistatic const struct attribute_group emc2103_temp3_group = {
5468c2ecf20Sopenharmony_ci	.attrs = emc2103_attributes_temp3,
5478c2ecf20Sopenharmony_ci};
5488c2ecf20Sopenharmony_ci
5498c2ecf20Sopenharmony_cistatic const struct attribute_group emc2103_temp4_group = {
5508c2ecf20Sopenharmony_ci	.attrs = emc2103_attributes_temp4,
5518c2ecf20Sopenharmony_ci};
5528c2ecf20Sopenharmony_ci
5538c2ecf20Sopenharmony_cistatic int
5548c2ecf20Sopenharmony_ciemc2103_probe(struct i2c_client *client)
5558c2ecf20Sopenharmony_ci{
5568c2ecf20Sopenharmony_ci	struct emc2103_data *data;
5578c2ecf20Sopenharmony_ci	struct device *hwmon_dev;
5588c2ecf20Sopenharmony_ci	int status, idx = 0;
5598c2ecf20Sopenharmony_ci
5608c2ecf20Sopenharmony_ci	if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA))
5618c2ecf20Sopenharmony_ci		return -EIO;
5628c2ecf20Sopenharmony_ci
5638c2ecf20Sopenharmony_ci	data = devm_kzalloc(&client->dev, sizeof(struct emc2103_data),
5648c2ecf20Sopenharmony_ci			    GFP_KERNEL);
5658c2ecf20Sopenharmony_ci	if (!data)
5668c2ecf20Sopenharmony_ci		return -ENOMEM;
5678c2ecf20Sopenharmony_ci
5688c2ecf20Sopenharmony_ci	i2c_set_clientdata(client, data);
5698c2ecf20Sopenharmony_ci	data->client = client;
5708c2ecf20Sopenharmony_ci	mutex_init(&data->update_lock);
5718c2ecf20Sopenharmony_ci
5728c2ecf20Sopenharmony_ci	/* 2103-2 and 2103-4 have 3 external diodes, 2103-1 has 1 */
5738c2ecf20Sopenharmony_ci	status = i2c_smbus_read_byte_data(client, REG_PRODUCT_ID);
5748c2ecf20Sopenharmony_ci	if (status == 0x24) {
5758c2ecf20Sopenharmony_ci		/* 2103-1 only has 1 external diode */
5768c2ecf20Sopenharmony_ci		data->temp_count = 2;
5778c2ecf20Sopenharmony_ci	} else {
5788c2ecf20Sopenharmony_ci		/* 2103-2 and 2103-4 have 3 or 4 external diodes */
5798c2ecf20Sopenharmony_ci		status = i2c_smbus_read_byte_data(client, REG_CONF1);
5808c2ecf20Sopenharmony_ci		if (status < 0) {
5818c2ecf20Sopenharmony_ci			dev_dbg(&client->dev, "reg 0x%02x, err %d\n", REG_CONF1,
5828c2ecf20Sopenharmony_ci				status);
5838c2ecf20Sopenharmony_ci			return status;
5848c2ecf20Sopenharmony_ci		}
5858c2ecf20Sopenharmony_ci
5868c2ecf20Sopenharmony_ci		/* detect current state of hardware */
5878c2ecf20Sopenharmony_ci		data->temp_count = (status & 0x01) ? 4 : 3;
5888c2ecf20Sopenharmony_ci
5898c2ecf20Sopenharmony_ci		/* force APD state if module parameter is set */
5908c2ecf20Sopenharmony_ci		if (apd == 0) {
5918c2ecf20Sopenharmony_ci			/* force APD mode off */
5928c2ecf20Sopenharmony_ci			data->temp_count = 3;
5938c2ecf20Sopenharmony_ci			status &= ~(0x01);
5948c2ecf20Sopenharmony_ci			i2c_smbus_write_byte_data(client, REG_CONF1, status);
5958c2ecf20Sopenharmony_ci		} else if (apd == 1) {
5968c2ecf20Sopenharmony_ci			/* force APD mode on */
5978c2ecf20Sopenharmony_ci			data->temp_count = 4;
5988c2ecf20Sopenharmony_ci			status |= 0x01;
5998c2ecf20Sopenharmony_ci			i2c_smbus_write_byte_data(client, REG_CONF1, status);
6008c2ecf20Sopenharmony_ci		}
6018c2ecf20Sopenharmony_ci	}
6028c2ecf20Sopenharmony_ci
6038c2ecf20Sopenharmony_ci	/* sysfs hooks */
6048c2ecf20Sopenharmony_ci	data->groups[idx++] = &emc2103_group;
6058c2ecf20Sopenharmony_ci	if (data->temp_count >= 3)
6068c2ecf20Sopenharmony_ci		data->groups[idx++] = &emc2103_temp3_group;
6078c2ecf20Sopenharmony_ci	if (data->temp_count == 4)
6088c2ecf20Sopenharmony_ci		data->groups[idx++] = &emc2103_temp4_group;
6098c2ecf20Sopenharmony_ci
6108c2ecf20Sopenharmony_ci	hwmon_dev = devm_hwmon_device_register_with_groups(&client->dev,
6118c2ecf20Sopenharmony_ci							   client->name, data,
6128c2ecf20Sopenharmony_ci							   data->groups);
6138c2ecf20Sopenharmony_ci	if (IS_ERR(hwmon_dev))
6148c2ecf20Sopenharmony_ci		return PTR_ERR(hwmon_dev);
6158c2ecf20Sopenharmony_ci
6168c2ecf20Sopenharmony_ci	dev_info(&client->dev, "%s: sensor '%s'\n",
6178c2ecf20Sopenharmony_ci		 dev_name(hwmon_dev), client->name);
6188c2ecf20Sopenharmony_ci
6198c2ecf20Sopenharmony_ci	return 0;
6208c2ecf20Sopenharmony_ci}
6218c2ecf20Sopenharmony_ci
6228c2ecf20Sopenharmony_cistatic const struct i2c_device_id emc2103_ids[] = {
6238c2ecf20Sopenharmony_ci	{ "emc2103", 0, },
6248c2ecf20Sopenharmony_ci	{ /* LIST END */ }
6258c2ecf20Sopenharmony_ci};
6268c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(i2c, emc2103_ids);
6278c2ecf20Sopenharmony_ci
6288c2ecf20Sopenharmony_ci/* Return 0 if detection is successful, -ENODEV otherwise */
6298c2ecf20Sopenharmony_cistatic int
6308c2ecf20Sopenharmony_ciemc2103_detect(struct i2c_client *new_client, struct i2c_board_info *info)
6318c2ecf20Sopenharmony_ci{
6328c2ecf20Sopenharmony_ci	struct i2c_adapter *adapter = new_client->adapter;
6338c2ecf20Sopenharmony_ci	int manufacturer, product;
6348c2ecf20Sopenharmony_ci
6358c2ecf20Sopenharmony_ci	if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA))
6368c2ecf20Sopenharmony_ci		return -ENODEV;
6378c2ecf20Sopenharmony_ci
6388c2ecf20Sopenharmony_ci	manufacturer = i2c_smbus_read_byte_data(new_client, REG_MFG_ID);
6398c2ecf20Sopenharmony_ci	if (manufacturer != 0x5D)
6408c2ecf20Sopenharmony_ci		return -ENODEV;
6418c2ecf20Sopenharmony_ci
6428c2ecf20Sopenharmony_ci	product = i2c_smbus_read_byte_data(new_client, REG_PRODUCT_ID);
6438c2ecf20Sopenharmony_ci	if ((product != 0x24) && (product != 0x26))
6448c2ecf20Sopenharmony_ci		return -ENODEV;
6458c2ecf20Sopenharmony_ci
6468c2ecf20Sopenharmony_ci	strlcpy(info->type, "emc2103", I2C_NAME_SIZE);
6478c2ecf20Sopenharmony_ci
6488c2ecf20Sopenharmony_ci	return 0;
6498c2ecf20Sopenharmony_ci}
6508c2ecf20Sopenharmony_ci
6518c2ecf20Sopenharmony_cistatic struct i2c_driver emc2103_driver = {
6528c2ecf20Sopenharmony_ci	.class		= I2C_CLASS_HWMON,
6538c2ecf20Sopenharmony_ci	.driver = {
6548c2ecf20Sopenharmony_ci		.name	= "emc2103",
6558c2ecf20Sopenharmony_ci	},
6568c2ecf20Sopenharmony_ci	.probe_new	= emc2103_probe,
6578c2ecf20Sopenharmony_ci	.id_table	= emc2103_ids,
6588c2ecf20Sopenharmony_ci	.detect		= emc2103_detect,
6598c2ecf20Sopenharmony_ci	.address_list	= normal_i2c,
6608c2ecf20Sopenharmony_ci};
6618c2ecf20Sopenharmony_ci
6628c2ecf20Sopenharmony_cimodule_i2c_driver(emc2103_driver);
6638c2ecf20Sopenharmony_ci
6648c2ecf20Sopenharmony_ciMODULE_AUTHOR("Steve Glendinning <steve.glendinning@shawell.net>");
6658c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("SMSC EMC2103 hwmon driver");
6668c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL");
667