18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
28c2ecf20Sopenharmony_ci/***************************************************************************
38c2ecf20Sopenharmony_ci *   Copyright (C) 2011-2012 Hans de Goede <hdegoede@redhat.com>           *
48c2ecf20Sopenharmony_ci *                                                                         *
58c2ecf20Sopenharmony_ci ***************************************************************************/
68c2ecf20Sopenharmony_ci
78c2ecf20Sopenharmony_ci#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
88c2ecf20Sopenharmony_ci
98c2ecf20Sopenharmony_ci#include <linux/module.h>
108c2ecf20Sopenharmony_ci#include <linux/init.h>
118c2ecf20Sopenharmony_ci#include <linux/slab.h>
128c2ecf20Sopenharmony_ci#include <linux/jiffies.h>
138c2ecf20Sopenharmony_ci#include <linux/platform_device.h>
148c2ecf20Sopenharmony_ci#include <linux/hwmon.h>
158c2ecf20Sopenharmony_ci#include <linux/hwmon-sysfs.h>
168c2ecf20Sopenharmony_ci#include <linux/err.h>
178c2ecf20Sopenharmony_ci#include <linux/mutex.h>
188c2ecf20Sopenharmony_ci#include "sch56xx-common.h"
198c2ecf20Sopenharmony_ci
208c2ecf20Sopenharmony_ci#define DRVNAME "sch5636"
218c2ecf20Sopenharmony_ci#define DEVNAME "theseus" /* We only support one model for now */
228c2ecf20Sopenharmony_ci
238c2ecf20Sopenharmony_ci#define SCH5636_REG_FUJITSU_ID		0x780
248c2ecf20Sopenharmony_ci#define SCH5636_REG_FUJITSU_REV		0x783
258c2ecf20Sopenharmony_ci
268c2ecf20Sopenharmony_ci#define SCH5636_NO_INS			5
278c2ecf20Sopenharmony_ci#define SCH5636_NO_TEMPS		16
288c2ecf20Sopenharmony_ci#define SCH5636_NO_FANS			8
298c2ecf20Sopenharmony_ci
308c2ecf20Sopenharmony_cistatic const u16 SCH5636_REG_IN_VAL[SCH5636_NO_INS] = {
318c2ecf20Sopenharmony_ci	0x22, 0x23, 0x24, 0x25, 0x189 };
328c2ecf20Sopenharmony_cistatic const u16 SCH5636_REG_IN_FACTORS[SCH5636_NO_INS] = {
338c2ecf20Sopenharmony_ci	4400, 1500, 4000, 4400, 16000 };
348c2ecf20Sopenharmony_cistatic const char * const SCH5636_IN_LABELS[SCH5636_NO_INS] = {
358c2ecf20Sopenharmony_ci	"3.3V", "VREF", "VBAT", "3.3AUX", "12V" };
368c2ecf20Sopenharmony_ci
378c2ecf20Sopenharmony_cistatic const u16 SCH5636_REG_TEMP_VAL[SCH5636_NO_TEMPS] = {
388c2ecf20Sopenharmony_ci	0x2B, 0x26, 0x27, 0x28, 0x29, 0x2A, 0x180, 0x181,
398c2ecf20Sopenharmony_ci	0x85, 0x86, 0x87, 0x88, 0x89, 0x8A, 0x8B, 0x8C };
408c2ecf20Sopenharmony_ci#define SCH5636_REG_TEMP_CTRL(i)	(0x790 + (i))
418c2ecf20Sopenharmony_ci#define SCH5636_TEMP_WORKING		0x01
428c2ecf20Sopenharmony_ci#define SCH5636_TEMP_ALARM		0x02
438c2ecf20Sopenharmony_ci#define SCH5636_TEMP_DEACTIVATED	0x80
448c2ecf20Sopenharmony_ci
458c2ecf20Sopenharmony_cistatic const u16 SCH5636_REG_FAN_VAL[SCH5636_NO_FANS] = {
468c2ecf20Sopenharmony_ci	0x2C, 0x2E, 0x30, 0x32, 0x62, 0x64, 0x66, 0x68 };
478c2ecf20Sopenharmony_ci#define SCH5636_REG_FAN_CTRL(i)		(0x880 + (i))
488c2ecf20Sopenharmony_ci/* FAULT in datasheet, but acts as an alarm */
498c2ecf20Sopenharmony_ci#define SCH5636_FAN_ALARM		0x04
508c2ecf20Sopenharmony_ci#define SCH5636_FAN_NOT_PRESENT		0x08
518c2ecf20Sopenharmony_ci#define SCH5636_FAN_DEACTIVATED		0x80
528c2ecf20Sopenharmony_ci
538c2ecf20Sopenharmony_ci
548c2ecf20Sopenharmony_cistruct sch5636_data {
558c2ecf20Sopenharmony_ci	unsigned short addr;
568c2ecf20Sopenharmony_ci	struct device *hwmon_dev;
578c2ecf20Sopenharmony_ci	struct sch56xx_watchdog_data *watchdog;
588c2ecf20Sopenharmony_ci
598c2ecf20Sopenharmony_ci	struct mutex update_lock;
608c2ecf20Sopenharmony_ci	char valid;			/* !=0 if following fields are valid */
618c2ecf20Sopenharmony_ci	unsigned long last_updated;	/* In jiffies */
628c2ecf20Sopenharmony_ci	u8 in[SCH5636_NO_INS];
638c2ecf20Sopenharmony_ci	u8 temp_val[SCH5636_NO_TEMPS];
648c2ecf20Sopenharmony_ci	u8 temp_ctrl[SCH5636_NO_TEMPS];
658c2ecf20Sopenharmony_ci	u16 fan_val[SCH5636_NO_FANS];
668c2ecf20Sopenharmony_ci	u8 fan_ctrl[SCH5636_NO_FANS];
678c2ecf20Sopenharmony_ci};
688c2ecf20Sopenharmony_ci
698c2ecf20Sopenharmony_cistatic struct sch5636_data *sch5636_update_device(struct device *dev)
708c2ecf20Sopenharmony_ci{
718c2ecf20Sopenharmony_ci	struct sch5636_data *data = dev_get_drvdata(dev);
728c2ecf20Sopenharmony_ci	struct sch5636_data *ret = data;
738c2ecf20Sopenharmony_ci	int i, val;
748c2ecf20Sopenharmony_ci
758c2ecf20Sopenharmony_ci	mutex_lock(&data->update_lock);
768c2ecf20Sopenharmony_ci
778c2ecf20Sopenharmony_ci	/* Cache the values for 1 second */
788c2ecf20Sopenharmony_ci	if (data->valid && !time_after(jiffies, data->last_updated + HZ))
798c2ecf20Sopenharmony_ci		goto abort;
808c2ecf20Sopenharmony_ci
818c2ecf20Sopenharmony_ci	for (i = 0; i < SCH5636_NO_INS; i++) {
828c2ecf20Sopenharmony_ci		val = sch56xx_read_virtual_reg(data->addr,
838c2ecf20Sopenharmony_ci					       SCH5636_REG_IN_VAL[i]);
848c2ecf20Sopenharmony_ci		if (unlikely(val < 0)) {
858c2ecf20Sopenharmony_ci			ret = ERR_PTR(val);
868c2ecf20Sopenharmony_ci			goto abort;
878c2ecf20Sopenharmony_ci		}
888c2ecf20Sopenharmony_ci		data->in[i] = val;
898c2ecf20Sopenharmony_ci	}
908c2ecf20Sopenharmony_ci
918c2ecf20Sopenharmony_ci	for (i = 0; i < SCH5636_NO_TEMPS; i++) {
928c2ecf20Sopenharmony_ci		if (data->temp_ctrl[i] & SCH5636_TEMP_DEACTIVATED)
938c2ecf20Sopenharmony_ci			continue;
948c2ecf20Sopenharmony_ci
958c2ecf20Sopenharmony_ci		val = sch56xx_read_virtual_reg(data->addr,
968c2ecf20Sopenharmony_ci					       SCH5636_REG_TEMP_VAL[i]);
978c2ecf20Sopenharmony_ci		if (unlikely(val < 0)) {
988c2ecf20Sopenharmony_ci			ret = ERR_PTR(val);
998c2ecf20Sopenharmony_ci			goto abort;
1008c2ecf20Sopenharmony_ci		}
1018c2ecf20Sopenharmony_ci		data->temp_val[i] = val;
1028c2ecf20Sopenharmony_ci
1038c2ecf20Sopenharmony_ci		val = sch56xx_read_virtual_reg(data->addr,
1048c2ecf20Sopenharmony_ci					       SCH5636_REG_TEMP_CTRL(i));
1058c2ecf20Sopenharmony_ci		if (unlikely(val < 0)) {
1068c2ecf20Sopenharmony_ci			ret = ERR_PTR(val);
1078c2ecf20Sopenharmony_ci			goto abort;
1088c2ecf20Sopenharmony_ci		}
1098c2ecf20Sopenharmony_ci		data->temp_ctrl[i] = val;
1108c2ecf20Sopenharmony_ci		/* Alarms need to be explicitly write-cleared */
1118c2ecf20Sopenharmony_ci		if (val & SCH5636_TEMP_ALARM) {
1128c2ecf20Sopenharmony_ci			sch56xx_write_virtual_reg(data->addr,
1138c2ecf20Sopenharmony_ci						SCH5636_REG_TEMP_CTRL(i), val);
1148c2ecf20Sopenharmony_ci		}
1158c2ecf20Sopenharmony_ci	}
1168c2ecf20Sopenharmony_ci
1178c2ecf20Sopenharmony_ci	for (i = 0; i < SCH5636_NO_FANS; i++) {
1188c2ecf20Sopenharmony_ci		if (data->fan_ctrl[i] & SCH5636_FAN_DEACTIVATED)
1198c2ecf20Sopenharmony_ci			continue;
1208c2ecf20Sopenharmony_ci
1218c2ecf20Sopenharmony_ci		val = sch56xx_read_virtual_reg16(data->addr,
1228c2ecf20Sopenharmony_ci						 SCH5636_REG_FAN_VAL[i]);
1238c2ecf20Sopenharmony_ci		if (unlikely(val < 0)) {
1248c2ecf20Sopenharmony_ci			ret = ERR_PTR(val);
1258c2ecf20Sopenharmony_ci			goto abort;
1268c2ecf20Sopenharmony_ci		}
1278c2ecf20Sopenharmony_ci		data->fan_val[i] = val;
1288c2ecf20Sopenharmony_ci
1298c2ecf20Sopenharmony_ci		val = sch56xx_read_virtual_reg(data->addr,
1308c2ecf20Sopenharmony_ci					       SCH5636_REG_FAN_CTRL(i));
1318c2ecf20Sopenharmony_ci		if (unlikely(val < 0)) {
1328c2ecf20Sopenharmony_ci			ret = ERR_PTR(val);
1338c2ecf20Sopenharmony_ci			goto abort;
1348c2ecf20Sopenharmony_ci		}
1358c2ecf20Sopenharmony_ci		data->fan_ctrl[i] = val;
1368c2ecf20Sopenharmony_ci		/* Alarms need to be explicitly write-cleared */
1378c2ecf20Sopenharmony_ci		if (val & SCH5636_FAN_ALARM) {
1388c2ecf20Sopenharmony_ci			sch56xx_write_virtual_reg(data->addr,
1398c2ecf20Sopenharmony_ci						SCH5636_REG_FAN_CTRL(i), val);
1408c2ecf20Sopenharmony_ci		}
1418c2ecf20Sopenharmony_ci	}
1428c2ecf20Sopenharmony_ci
1438c2ecf20Sopenharmony_ci	data->last_updated = jiffies;
1448c2ecf20Sopenharmony_ci	data->valid = 1;
1458c2ecf20Sopenharmony_ciabort:
1468c2ecf20Sopenharmony_ci	mutex_unlock(&data->update_lock);
1478c2ecf20Sopenharmony_ci	return ret;
1488c2ecf20Sopenharmony_ci}
1498c2ecf20Sopenharmony_ci
1508c2ecf20Sopenharmony_cistatic int reg_to_rpm(u16 reg)
1518c2ecf20Sopenharmony_ci{
1528c2ecf20Sopenharmony_ci	if (reg == 0)
1538c2ecf20Sopenharmony_ci		return -EIO;
1548c2ecf20Sopenharmony_ci	if (reg == 0xffff)
1558c2ecf20Sopenharmony_ci		return 0;
1568c2ecf20Sopenharmony_ci
1578c2ecf20Sopenharmony_ci	return 5400540 / reg;
1588c2ecf20Sopenharmony_ci}
1598c2ecf20Sopenharmony_ci
1608c2ecf20Sopenharmony_cistatic ssize_t name_show(struct device *dev, struct device_attribute *devattr,
1618c2ecf20Sopenharmony_ci			 char *buf)
1628c2ecf20Sopenharmony_ci{
1638c2ecf20Sopenharmony_ci	return snprintf(buf, PAGE_SIZE, "%s\n", DEVNAME);
1648c2ecf20Sopenharmony_ci}
1658c2ecf20Sopenharmony_ci
1668c2ecf20Sopenharmony_cistatic ssize_t in_value_show(struct device *dev,
1678c2ecf20Sopenharmony_ci			     struct device_attribute *devattr, char *buf)
1688c2ecf20Sopenharmony_ci{
1698c2ecf20Sopenharmony_ci	struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
1708c2ecf20Sopenharmony_ci	struct sch5636_data *data = sch5636_update_device(dev);
1718c2ecf20Sopenharmony_ci	int val;
1728c2ecf20Sopenharmony_ci
1738c2ecf20Sopenharmony_ci	if (IS_ERR(data))
1748c2ecf20Sopenharmony_ci		return PTR_ERR(data);
1758c2ecf20Sopenharmony_ci
1768c2ecf20Sopenharmony_ci	val = DIV_ROUND_CLOSEST(
1778c2ecf20Sopenharmony_ci		data->in[attr->index] * SCH5636_REG_IN_FACTORS[attr->index],
1788c2ecf20Sopenharmony_ci		255);
1798c2ecf20Sopenharmony_ci	return snprintf(buf, PAGE_SIZE, "%d\n", val);
1808c2ecf20Sopenharmony_ci}
1818c2ecf20Sopenharmony_ci
1828c2ecf20Sopenharmony_cistatic ssize_t in_label_show(struct device *dev,
1838c2ecf20Sopenharmony_ci			     struct device_attribute *devattr, char *buf)
1848c2ecf20Sopenharmony_ci{
1858c2ecf20Sopenharmony_ci	struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
1868c2ecf20Sopenharmony_ci
1878c2ecf20Sopenharmony_ci	return snprintf(buf, PAGE_SIZE, "%s\n",
1888c2ecf20Sopenharmony_ci			SCH5636_IN_LABELS[attr->index]);
1898c2ecf20Sopenharmony_ci}
1908c2ecf20Sopenharmony_ci
1918c2ecf20Sopenharmony_cistatic ssize_t temp_value_show(struct device *dev,
1928c2ecf20Sopenharmony_ci			       struct device_attribute *devattr, char *buf)
1938c2ecf20Sopenharmony_ci{
1948c2ecf20Sopenharmony_ci	struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
1958c2ecf20Sopenharmony_ci	struct sch5636_data *data = sch5636_update_device(dev);
1968c2ecf20Sopenharmony_ci	int val;
1978c2ecf20Sopenharmony_ci
1988c2ecf20Sopenharmony_ci	if (IS_ERR(data))
1998c2ecf20Sopenharmony_ci		return PTR_ERR(data);
2008c2ecf20Sopenharmony_ci
2018c2ecf20Sopenharmony_ci	val = (data->temp_val[attr->index] - 64) * 1000;
2028c2ecf20Sopenharmony_ci	return snprintf(buf, PAGE_SIZE, "%d\n", val);
2038c2ecf20Sopenharmony_ci}
2048c2ecf20Sopenharmony_ci
2058c2ecf20Sopenharmony_cistatic ssize_t temp_fault_show(struct device *dev,
2068c2ecf20Sopenharmony_ci			       struct device_attribute *devattr, char *buf)
2078c2ecf20Sopenharmony_ci{
2088c2ecf20Sopenharmony_ci	struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
2098c2ecf20Sopenharmony_ci	struct sch5636_data *data = sch5636_update_device(dev);
2108c2ecf20Sopenharmony_ci	int val;
2118c2ecf20Sopenharmony_ci
2128c2ecf20Sopenharmony_ci	if (IS_ERR(data))
2138c2ecf20Sopenharmony_ci		return PTR_ERR(data);
2148c2ecf20Sopenharmony_ci
2158c2ecf20Sopenharmony_ci	val = (data->temp_ctrl[attr->index] & SCH5636_TEMP_WORKING) ? 0 : 1;
2168c2ecf20Sopenharmony_ci	return snprintf(buf, PAGE_SIZE, "%d\n", val);
2178c2ecf20Sopenharmony_ci}
2188c2ecf20Sopenharmony_ci
2198c2ecf20Sopenharmony_cistatic ssize_t temp_alarm_show(struct device *dev,
2208c2ecf20Sopenharmony_ci			       struct device_attribute *devattr, char *buf)
2218c2ecf20Sopenharmony_ci{
2228c2ecf20Sopenharmony_ci	struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
2238c2ecf20Sopenharmony_ci	struct sch5636_data *data = sch5636_update_device(dev);
2248c2ecf20Sopenharmony_ci	int val;
2258c2ecf20Sopenharmony_ci
2268c2ecf20Sopenharmony_ci	if (IS_ERR(data))
2278c2ecf20Sopenharmony_ci		return PTR_ERR(data);
2288c2ecf20Sopenharmony_ci
2298c2ecf20Sopenharmony_ci	val = (data->temp_ctrl[attr->index] & SCH5636_TEMP_ALARM) ? 1 : 0;
2308c2ecf20Sopenharmony_ci	return snprintf(buf, PAGE_SIZE, "%d\n", val);
2318c2ecf20Sopenharmony_ci}
2328c2ecf20Sopenharmony_ci
2338c2ecf20Sopenharmony_cistatic ssize_t fan_value_show(struct device *dev,
2348c2ecf20Sopenharmony_ci			      struct device_attribute *devattr, char *buf)
2358c2ecf20Sopenharmony_ci{
2368c2ecf20Sopenharmony_ci	struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
2378c2ecf20Sopenharmony_ci	struct sch5636_data *data = sch5636_update_device(dev);
2388c2ecf20Sopenharmony_ci	int val;
2398c2ecf20Sopenharmony_ci
2408c2ecf20Sopenharmony_ci	if (IS_ERR(data))
2418c2ecf20Sopenharmony_ci		return PTR_ERR(data);
2428c2ecf20Sopenharmony_ci
2438c2ecf20Sopenharmony_ci	val = reg_to_rpm(data->fan_val[attr->index]);
2448c2ecf20Sopenharmony_ci	if (val < 0)
2458c2ecf20Sopenharmony_ci		return val;
2468c2ecf20Sopenharmony_ci
2478c2ecf20Sopenharmony_ci	return snprintf(buf, PAGE_SIZE, "%d\n", val);
2488c2ecf20Sopenharmony_ci}
2498c2ecf20Sopenharmony_ci
2508c2ecf20Sopenharmony_cistatic ssize_t fan_fault_show(struct device *dev,
2518c2ecf20Sopenharmony_ci			      struct device_attribute *devattr, char *buf)
2528c2ecf20Sopenharmony_ci{
2538c2ecf20Sopenharmony_ci	struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
2548c2ecf20Sopenharmony_ci	struct sch5636_data *data = sch5636_update_device(dev);
2558c2ecf20Sopenharmony_ci	int val;
2568c2ecf20Sopenharmony_ci
2578c2ecf20Sopenharmony_ci	if (IS_ERR(data))
2588c2ecf20Sopenharmony_ci		return PTR_ERR(data);
2598c2ecf20Sopenharmony_ci
2608c2ecf20Sopenharmony_ci	val = (data->fan_ctrl[attr->index] & SCH5636_FAN_NOT_PRESENT) ? 1 : 0;
2618c2ecf20Sopenharmony_ci	return snprintf(buf, PAGE_SIZE, "%d\n", val);
2628c2ecf20Sopenharmony_ci}
2638c2ecf20Sopenharmony_ci
2648c2ecf20Sopenharmony_cistatic ssize_t fan_alarm_show(struct device *dev,
2658c2ecf20Sopenharmony_ci			      struct device_attribute *devattr, char *buf)
2668c2ecf20Sopenharmony_ci{
2678c2ecf20Sopenharmony_ci	struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
2688c2ecf20Sopenharmony_ci	struct sch5636_data *data = sch5636_update_device(dev);
2698c2ecf20Sopenharmony_ci	int val;
2708c2ecf20Sopenharmony_ci
2718c2ecf20Sopenharmony_ci	if (IS_ERR(data))
2728c2ecf20Sopenharmony_ci		return PTR_ERR(data);
2738c2ecf20Sopenharmony_ci
2748c2ecf20Sopenharmony_ci	val = (data->fan_ctrl[attr->index] & SCH5636_FAN_ALARM) ? 1 : 0;
2758c2ecf20Sopenharmony_ci	return snprintf(buf, PAGE_SIZE, "%d\n", val);
2768c2ecf20Sopenharmony_ci}
2778c2ecf20Sopenharmony_ci
2788c2ecf20Sopenharmony_cistatic struct sensor_device_attribute sch5636_attr[] = {
2798c2ecf20Sopenharmony_ci	SENSOR_ATTR_RO(name, name, 0),
2808c2ecf20Sopenharmony_ci	SENSOR_ATTR_RO(in0_input, in_value, 0),
2818c2ecf20Sopenharmony_ci	SENSOR_ATTR_RO(in0_label, in_label, 0),
2828c2ecf20Sopenharmony_ci	SENSOR_ATTR_RO(in1_input, in_value, 1),
2838c2ecf20Sopenharmony_ci	SENSOR_ATTR_RO(in1_label, in_label, 1),
2848c2ecf20Sopenharmony_ci	SENSOR_ATTR_RO(in2_input, in_value, 2),
2858c2ecf20Sopenharmony_ci	SENSOR_ATTR_RO(in2_label, in_label, 2),
2868c2ecf20Sopenharmony_ci	SENSOR_ATTR_RO(in3_input, in_value, 3),
2878c2ecf20Sopenharmony_ci	SENSOR_ATTR_RO(in3_label, in_label, 3),
2888c2ecf20Sopenharmony_ci	SENSOR_ATTR_RO(in4_input, in_value, 4),
2898c2ecf20Sopenharmony_ci	SENSOR_ATTR_RO(in4_label, in_label, 4),
2908c2ecf20Sopenharmony_ci};
2918c2ecf20Sopenharmony_ci
2928c2ecf20Sopenharmony_cistatic struct sensor_device_attribute sch5636_temp_attr[] = {
2938c2ecf20Sopenharmony_ci	SENSOR_ATTR_RO(temp1_input, temp_value, 0),
2948c2ecf20Sopenharmony_ci	SENSOR_ATTR_RO(temp1_fault, temp_fault, 0),
2958c2ecf20Sopenharmony_ci	SENSOR_ATTR_RO(temp1_alarm, temp_alarm, 0),
2968c2ecf20Sopenharmony_ci	SENSOR_ATTR_RO(temp2_input, temp_value, 1),
2978c2ecf20Sopenharmony_ci	SENSOR_ATTR_RO(temp2_fault, temp_fault, 1),
2988c2ecf20Sopenharmony_ci	SENSOR_ATTR_RO(temp2_alarm, temp_alarm, 1),
2998c2ecf20Sopenharmony_ci	SENSOR_ATTR_RO(temp3_input, temp_value, 2),
3008c2ecf20Sopenharmony_ci	SENSOR_ATTR_RO(temp3_fault, temp_fault, 2),
3018c2ecf20Sopenharmony_ci	SENSOR_ATTR_RO(temp3_alarm, temp_alarm, 2),
3028c2ecf20Sopenharmony_ci	SENSOR_ATTR_RO(temp4_input, temp_value, 3),
3038c2ecf20Sopenharmony_ci	SENSOR_ATTR_RO(temp4_fault, temp_fault, 3),
3048c2ecf20Sopenharmony_ci	SENSOR_ATTR_RO(temp4_alarm, temp_alarm, 3),
3058c2ecf20Sopenharmony_ci	SENSOR_ATTR_RO(temp5_input, temp_value, 4),
3068c2ecf20Sopenharmony_ci	SENSOR_ATTR_RO(temp5_fault, temp_fault, 4),
3078c2ecf20Sopenharmony_ci	SENSOR_ATTR_RO(temp5_alarm, temp_alarm, 4),
3088c2ecf20Sopenharmony_ci	SENSOR_ATTR_RO(temp6_input, temp_value, 5),
3098c2ecf20Sopenharmony_ci	SENSOR_ATTR_RO(temp6_fault, temp_fault, 5),
3108c2ecf20Sopenharmony_ci	SENSOR_ATTR_RO(temp6_alarm, temp_alarm, 5),
3118c2ecf20Sopenharmony_ci	SENSOR_ATTR_RO(temp7_input, temp_value, 6),
3128c2ecf20Sopenharmony_ci	SENSOR_ATTR_RO(temp7_fault, temp_fault, 6),
3138c2ecf20Sopenharmony_ci	SENSOR_ATTR_RO(temp7_alarm, temp_alarm, 6),
3148c2ecf20Sopenharmony_ci	SENSOR_ATTR_RO(temp8_input, temp_value, 7),
3158c2ecf20Sopenharmony_ci	SENSOR_ATTR_RO(temp8_fault, temp_fault, 7),
3168c2ecf20Sopenharmony_ci	SENSOR_ATTR_RO(temp8_alarm, temp_alarm, 7),
3178c2ecf20Sopenharmony_ci	SENSOR_ATTR_RO(temp9_input, temp_value, 8),
3188c2ecf20Sopenharmony_ci	SENSOR_ATTR_RO(temp9_fault, temp_fault, 8),
3198c2ecf20Sopenharmony_ci	SENSOR_ATTR_RO(temp9_alarm, temp_alarm, 8),
3208c2ecf20Sopenharmony_ci	SENSOR_ATTR_RO(temp10_input, temp_value, 9),
3218c2ecf20Sopenharmony_ci	SENSOR_ATTR_RO(temp10_fault, temp_fault, 9),
3228c2ecf20Sopenharmony_ci	SENSOR_ATTR_RO(temp10_alarm, temp_alarm, 9),
3238c2ecf20Sopenharmony_ci	SENSOR_ATTR_RO(temp11_input, temp_value, 10),
3248c2ecf20Sopenharmony_ci	SENSOR_ATTR_RO(temp11_fault, temp_fault, 10),
3258c2ecf20Sopenharmony_ci	SENSOR_ATTR_RO(temp11_alarm, temp_alarm, 10),
3268c2ecf20Sopenharmony_ci	SENSOR_ATTR_RO(temp12_input, temp_value, 11),
3278c2ecf20Sopenharmony_ci	SENSOR_ATTR_RO(temp12_fault, temp_fault, 11),
3288c2ecf20Sopenharmony_ci	SENSOR_ATTR_RO(temp12_alarm, temp_alarm, 11),
3298c2ecf20Sopenharmony_ci	SENSOR_ATTR_RO(temp13_input, temp_value, 12),
3308c2ecf20Sopenharmony_ci	SENSOR_ATTR_RO(temp13_fault, temp_fault, 12),
3318c2ecf20Sopenharmony_ci	SENSOR_ATTR_RO(temp13_alarm, temp_alarm, 12),
3328c2ecf20Sopenharmony_ci	SENSOR_ATTR_RO(temp14_input, temp_value, 13),
3338c2ecf20Sopenharmony_ci	SENSOR_ATTR_RO(temp14_fault, temp_fault, 13),
3348c2ecf20Sopenharmony_ci	SENSOR_ATTR_RO(temp14_alarm, temp_alarm, 13),
3358c2ecf20Sopenharmony_ci	SENSOR_ATTR_RO(temp15_input, temp_value, 14),
3368c2ecf20Sopenharmony_ci	SENSOR_ATTR_RO(temp15_fault, temp_fault, 14),
3378c2ecf20Sopenharmony_ci	SENSOR_ATTR_RO(temp15_alarm, temp_alarm, 14),
3388c2ecf20Sopenharmony_ci	SENSOR_ATTR_RO(temp16_input, temp_value, 15),
3398c2ecf20Sopenharmony_ci	SENSOR_ATTR_RO(temp16_fault, temp_fault, 15),
3408c2ecf20Sopenharmony_ci	SENSOR_ATTR_RO(temp16_alarm, temp_alarm, 15),
3418c2ecf20Sopenharmony_ci};
3428c2ecf20Sopenharmony_ci
3438c2ecf20Sopenharmony_cistatic struct sensor_device_attribute sch5636_fan_attr[] = {
3448c2ecf20Sopenharmony_ci	SENSOR_ATTR_RO(fan1_input, fan_value, 0),
3458c2ecf20Sopenharmony_ci	SENSOR_ATTR_RO(fan1_fault, fan_fault, 0),
3468c2ecf20Sopenharmony_ci	SENSOR_ATTR_RO(fan1_alarm, fan_alarm, 0),
3478c2ecf20Sopenharmony_ci	SENSOR_ATTR_RO(fan2_input, fan_value, 1),
3488c2ecf20Sopenharmony_ci	SENSOR_ATTR_RO(fan2_fault, fan_fault, 1),
3498c2ecf20Sopenharmony_ci	SENSOR_ATTR_RO(fan2_alarm, fan_alarm, 1),
3508c2ecf20Sopenharmony_ci	SENSOR_ATTR_RO(fan3_input, fan_value, 2),
3518c2ecf20Sopenharmony_ci	SENSOR_ATTR_RO(fan3_fault, fan_fault, 2),
3528c2ecf20Sopenharmony_ci	SENSOR_ATTR_RO(fan3_alarm, fan_alarm, 2),
3538c2ecf20Sopenharmony_ci	SENSOR_ATTR_RO(fan4_input, fan_value, 3),
3548c2ecf20Sopenharmony_ci	SENSOR_ATTR_RO(fan4_fault, fan_fault, 3),
3558c2ecf20Sopenharmony_ci	SENSOR_ATTR_RO(fan4_alarm, fan_alarm, 3),
3568c2ecf20Sopenharmony_ci	SENSOR_ATTR_RO(fan5_input, fan_value, 4),
3578c2ecf20Sopenharmony_ci	SENSOR_ATTR_RO(fan5_fault, fan_fault, 4),
3588c2ecf20Sopenharmony_ci	SENSOR_ATTR_RO(fan5_alarm, fan_alarm, 4),
3598c2ecf20Sopenharmony_ci	SENSOR_ATTR_RO(fan6_input, fan_value, 5),
3608c2ecf20Sopenharmony_ci	SENSOR_ATTR_RO(fan6_fault, fan_fault, 5),
3618c2ecf20Sopenharmony_ci	SENSOR_ATTR_RO(fan6_alarm, fan_alarm, 5),
3628c2ecf20Sopenharmony_ci	SENSOR_ATTR_RO(fan7_input, fan_value, 6),
3638c2ecf20Sopenharmony_ci	SENSOR_ATTR_RO(fan7_fault, fan_fault, 6),
3648c2ecf20Sopenharmony_ci	SENSOR_ATTR_RO(fan7_alarm, fan_alarm, 6),
3658c2ecf20Sopenharmony_ci	SENSOR_ATTR_RO(fan8_input, fan_value, 7),
3668c2ecf20Sopenharmony_ci	SENSOR_ATTR_RO(fan8_fault, fan_fault, 7),
3678c2ecf20Sopenharmony_ci	SENSOR_ATTR_RO(fan8_alarm, fan_alarm, 7),
3688c2ecf20Sopenharmony_ci};
3698c2ecf20Sopenharmony_ci
3708c2ecf20Sopenharmony_cistatic int sch5636_remove(struct platform_device *pdev)
3718c2ecf20Sopenharmony_ci{
3728c2ecf20Sopenharmony_ci	struct sch5636_data *data = platform_get_drvdata(pdev);
3738c2ecf20Sopenharmony_ci	int i;
3748c2ecf20Sopenharmony_ci
3758c2ecf20Sopenharmony_ci	if (data->watchdog)
3768c2ecf20Sopenharmony_ci		sch56xx_watchdog_unregister(data->watchdog);
3778c2ecf20Sopenharmony_ci
3788c2ecf20Sopenharmony_ci	if (data->hwmon_dev)
3798c2ecf20Sopenharmony_ci		hwmon_device_unregister(data->hwmon_dev);
3808c2ecf20Sopenharmony_ci
3818c2ecf20Sopenharmony_ci	for (i = 0; i < ARRAY_SIZE(sch5636_attr); i++)
3828c2ecf20Sopenharmony_ci		device_remove_file(&pdev->dev, &sch5636_attr[i].dev_attr);
3838c2ecf20Sopenharmony_ci
3848c2ecf20Sopenharmony_ci	for (i = 0; i < SCH5636_NO_TEMPS * 3; i++)
3858c2ecf20Sopenharmony_ci		device_remove_file(&pdev->dev,
3868c2ecf20Sopenharmony_ci				   &sch5636_temp_attr[i].dev_attr);
3878c2ecf20Sopenharmony_ci
3888c2ecf20Sopenharmony_ci	for (i = 0; i < SCH5636_NO_FANS * 3; i++)
3898c2ecf20Sopenharmony_ci		device_remove_file(&pdev->dev,
3908c2ecf20Sopenharmony_ci				   &sch5636_fan_attr[i].dev_attr);
3918c2ecf20Sopenharmony_ci
3928c2ecf20Sopenharmony_ci	return 0;
3938c2ecf20Sopenharmony_ci}
3948c2ecf20Sopenharmony_ci
3958c2ecf20Sopenharmony_cistatic int sch5636_probe(struct platform_device *pdev)
3968c2ecf20Sopenharmony_ci{
3978c2ecf20Sopenharmony_ci	struct sch5636_data *data;
3988c2ecf20Sopenharmony_ci	int i, err, val, revision[2];
3998c2ecf20Sopenharmony_ci	char id[4];
4008c2ecf20Sopenharmony_ci
4018c2ecf20Sopenharmony_ci	data = devm_kzalloc(&pdev->dev, sizeof(struct sch5636_data),
4028c2ecf20Sopenharmony_ci			    GFP_KERNEL);
4038c2ecf20Sopenharmony_ci	if (!data)
4048c2ecf20Sopenharmony_ci		return -ENOMEM;
4058c2ecf20Sopenharmony_ci
4068c2ecf20Sopenharmony_ci	data->addr = platform_get_resource(pdev, IORESOURCE_IO, 0)->start;
4078c2ecf20Sopenharmony_ci	mutex_init(&data->update_lock);
4088c2ecf20Sopenharmony_ci	platform_set_drvdata(pdev, data);
4098c2ecf20Sopenharmony_ci
4108c2ecf20Sopenharmony_ci	for (i = 0; i < 3; i++) {
4118c2ecf20Sopenharmony_ci		val = sch56xx_read_virtual_reg(data->addr,
4128c2ecf20Sopenharmony_ci					       SCH5636_REG_FUJITSU_ID + i);
4138c2ecf20Sopenharmony_ci		if (val < 0) {
4148c2ecf20Sopenharmony_ci			pr_err("Could not read Fujitsu id byte at %#x\n",
4158c2ecf20Sopenharmony_ci				SCH5636_REG_FUJITSU_ID + i);
4168c2ecf20Sopenharmony_ci			err = val;
4178c2ecf20Sopenharmony_ci			goto error;
4188c2ecf20Sopenharmony_ci		}
4198c2ecf20Sopenharmony_ci		id[i] = val;
4208c2ecf20Sopenharmony_ci	}
4218c2ecf20Sopenharmony_ci	id[i] = '\0';
4228c2ecf20Sopenharmony_ci
4238c2ecf20Sopenharmony_ci	if (strcmp(id, "THS")) {
4248c2ecf20Sopenharmony_ci		pr_err("Unknown Fujitsu id: %02x%02x%02x\n",
4258c2ecf20Sopenharmony_ci		       id[0], id[1], id[2]);
4268c2ecf20Sopenharmony_ci		err = -ENODEV;
4278c2ecf20Sopenharmony_ci		goto error;
4288c2ecf20Sopenharmony_ci	}
4298c2ecf20Sopenharmony_ci
4308c2ecf20Sopenharmony_ci	for (i = 0; i < 2; i++) {
4318c2ecf20Sopenharmony_ci		val = sch56xx_read_virtual_reg(data->addr,
4328c2ecf20Sopenharmony_ci					       SCH5636_REG_FUJITSU_REV + i);
4338c2ecf20Sopenharmony_ci		if (val < 0) {
4348c2ecf20Sopenharmony_ci			err = val;
4358c2ecf20Sopenharmony_ci			goto error;
4368c2ecf20Sopenharmony_ci		}
4378c2ecf20Sopenharmony_ci		revision[i] = val;
4388c2ecf20Sopenharmony_ci	}
4398c2ecf20Sopenharmony_ci	pr_info("Found %s chip at %#hx, revision: %d.%02d\n", DEVNAME,
4408c2ecf20Sopenharmony_ci		data->addr, revision[0], revision[1]);
4418c2ecf20Sopenharmony_ci
4428c2ecf20Sopenharmony_ci	/* Read all temp + fan ctrl registers to determine which are active */
4438c2ecf20Sopenharmony_ci	for (i = 0; i < SCH5636_NO_TEMPS; i++) {
4448c2ecf20Sopenharmony_ci		val = sch56xx_read_virtual_reg(data->addr,
4458c2ecf20Sopenharmony_ci					       SCH5636_REG_TEMP_CTRL(i));
4468c2ecf20Sopenharmony_ci		if (unlikely(val < 0)) {
4478c2ecf20Sopenharmony_ci			err = val;
4488c2ecf20Sopenharmony_ci			goto error;
4498c2ecf20Sopenharmony_ci		}
4508c2ecf20Sopenharmony_ci		data->temp_ctrl[i] = val;
4518c2ecf20Sopenharmony_ci	}
4528c2ecf20Sopenharmony_ci
4538c2ecf20Sopenharmony_ci	for (i = 0; i < SCH5636_NO_FANS; i++) {
4548c2ecf20Sopenharmony_ci		val = sch56xx_read_virtual_reg(data->addr,
4558c2ecf20Sopenharmony_ci					       SCH5636_REG_FAN_CTRL(i));
4568c2ecf20Sopenharmony_ci		if (unlikely(val < 0)) {
4578c2ecf20Sopenharmony_ci			err = val;
4588c2ecf20Sopenharmony_ci			goto error;
4598c2ecf20Sopenharmony_ci		}
4608c2ecf20Sopenharmony_ci		data->fan_ctrl[i] = val;
4618c2ecf20Sopenharmony_ci	}
4628c2ecf20Sopenharmony_ci
4638c2ecf20Sopenharmony_ci	for (i = 0; i < ARRAY_SIZE(sch5636_attr); i++) {
4648c2ecf20Sopenharmony_ci		err = device_create_file(&pdev->dev,
4658c2ecf20Sopenharmony_ci					 &sch5636_attr[i].dev_attr);
4668c2ecf20Sopenharmony_ci		if (err)
4678c2ecf20Sopenharmony_ci			goto error;
4688c2ecf20Sopenharmony_ci	}
4698c2ecf20Sopenharmony_ci
4708c2ecf20Sopenharmony_ci	for (i = 0; i < (SCH5636_NO_TEMPS * 3); i++) {
4718c2ecf20Sopenharmony_ci		if (data->temp_ctrl[i/3] & SCH5636_TEMP_DEACTIVATED)
4728c2ecf20Sopenharmony_ci			continue;
4738c2ecf20Sopenharmony_ci
4748c2ecf20Sopenharmony_ci		err = device_create_file(&pdev->dev,
4758c2ecf20Sopenharmony_ci					&sch5636_temp_attr[i].dev_attr);
4768c2ecf20Sopenharmony_ci		if (err)
4778c2ecf20Sopenharmony_ci			goto error;
4788c2ecf20Sopenharmony_ci	}
4798c2ecf20Sopenharmony_ci
4808c2ecf20Sopenharmony_ci	for (i = 0; i < (SCH5636_NO_FANS * 3); i++) {
4818c2ecf20Sopenharmony_ci		if (data->fan_ctrl[i/3] & SCH5636_FAN_DEACTIVATED)
4828c2ecf20Sopenharmony_ci			continue;
4838c2ecf20Sopenharmony_ci
4848c2ecf20Sopenharmony_ci		err = device_create_file(&pdev->dev,
4858c2ecf20Sopenharmony_ci					&sch5636_fan_attr[i].dev_attr);
4868c2ecf20Sopenharmony_ci		if (err)
4878c2ecf20Sopenharmony_ci			goto error;
4888c2ecf20Sopenharmony_ci	}
4898c2ecf20Sopenharmony_ci
4908c2ecf20Sopenharmony_ci	data->hwmon_dev = hwmon_device_register(&pdev->dev);
4918c2ecf20Sopenharmony_ci	if (IS_ERR(data->hwmon_dev)) {
4928c2ecf20Sopenharmony_ci		err = PTR_ERR(data->hwmon_dev);
4938c2ecf20Sopenharmony_ci		data->hwmon_dev = NULL;
4948c2ecf20Sopenharmony_ci		goto error;
4958c2ecf20Sopenharmony_ci	}
4968c2ecf20Sopenharmony_ci
4978c2ecf20Sopenharmony_ci	/* Note failing to register the watchdog is not a fatal error */
4988c2ecf20Sopenharmony_ci	data->watchdog = sch56xx_watchdog_register(&pdev->dev, data->addr,
4998c2ecf20Sopenharmony_ci					(revision[0] << 8) | revision[1],
5008c2ecf20Sopenharmony_ci					&data->update_lock, 0);
5018c2ecf20Sopenharmony_ci
5028c2ecf20Sopenharmony_ci	return 0;
5038c2ecf20Sopenharmony_ci
5048c2ecf20Sopenharmony_cierror:
5058c2ecf20Sopenharmony_ci	sch5636_remove(pdev);
5068c2ecf20Sopenharmony_ci	return err;
5078c2ecf20Sopenharmony_ci}
5088c2ecf20Sopenharmony_ci
5098c2ecf20Sopenharmony_cistatic struct platform_driver sch5636_driver = {
5108c2ecf20Sopenharmony_ci	.driver = {
5118c2ecf20Sopenharmony_ci		.name	= DRVNAME,
5128c2ecf20Sopenharmony_ci	},
5138c2ecf20Sopenharmony_ci	.probe		= sch5636_probe,
5148c2ecf20Sopenharmony_ci	.remove		= sch5636_remove,
5158c2ecf20Sopenharmony_ci};
5168c2ecf20Sopenharmony_ci
5178c2ecf20Sopenharmony_cimodule_platform_driver(sch5636_driver);
5188c2ecf20Sopenharmony_ci
5198c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("SMSC SCH5636 Hardware Monitoring Driver");
5208c2ecf20Sopenharmony_ciMODULE_AUTHOR("Hans de Goede <hdegoede@redhat.com>");
5218c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL");
522