18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * Support for the FTS Systemmonitoring Chip "Teutates"
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci * Copyright (C) 2016 Fujitsu Technology Solutions GmbH,
68c2ecf20Sopenharmony_ci *		  Thilo Cestonaro <thilo.cestonaro@ts.fujitsu.com>
78c2ecf20Sopenharmony_ci */
88c2ecf20Sopenharmony_ci#include <linux/err.h>
98c2ecf20Sopenharmony_ci#include <linux/fs.h>
108c2ecf20Sopenharmony_ci#include <linux/hwmon.h>
118c2ecf20Sopenharmony_ci#include <linux/hwmon-sysfs.h>
128c2ecf20Sopenharmony_ci#include <linux/i2c.h>
138c2ecf20Sopenharmony_ci#include <linux/init.h>
148c2ecf20Sopenharmony_ci#include <linux/jiffies.h>
158c2ecf20Sopenharmony_ci#include <linux/module.h>
168c2ecf20Sopenharmony_ci#include <linux/mutex.h>
178c2ecf20Sopenharmony_ci#include <linux/slab.h>
188c2ecf20Sopenharmony_ci#include <linux/sysfs.h>
198c2ecf20Sopenharmony_ci#include <linux/uaccess.h>
208c2ecf20Sopenharmony_ci#include <linux/watchdog.h>
218c2ecf20Sopenharmony_ci
228c2ecf20Sopenharmony_ci#define FTS_DEVICE_ID_REG		0x0000
238c2ecf20Sopenharmony_ci#define FTS_DEVICE_REVISION_REG		0x0001
248c2ecf20Sopenharmony_ci#define FTS_DEVICE_STATUS_REG		0x0004
258c2ecf20Sopenharmony_ci#define FTS_SATELLITE_STATUS_REG	0x0005
268c2ecf20Sopenharmony_ci#define FTS_EVENT_STATUS_REG		0x0006
278c2ecf20Sopenharmony_ci#define FTS_GLOBAL_CONTROL_REG		0x0007
288c2ecf20Sopenharmony_ci
298c2ecf20Sopenharmony_ci#define FTS_DEVICE_DETECT_REG_1		0x0C
308c2ecf20Sopenharmony_ci#define FTS_DEVICE_DETECT_REG_2		0x0D
318c2ecf20Sopenharmony_ci#define FTS_DEVICE_DETECT_REG_3		0x0E
328c2ecf20Sopenharmony_ci
338c2ecf20Sopenharmony_ci#define FTS_SENSOR_EVENT_REG		0x0010
348c2ecf20Sopenharmony_ci
358c2ecf20Sopenharmony_ci#define FTS_FAN_EVENT_REG		0x0014
368c2ecf20Sopenharmony_ci#define FTS_FAN_PRESENT_REG		0x0015
378c2ecf20Sopenharmony_ci
388c2ecf20Sopenharmony_ci#define FTS_POWER_ON_TIME_COUNTER_A	0x007A
398c2ecf20Sopenharmony_ci#define FTS_POWER_ON_TIME_COUNTER_B	0x007B
408c2ecf20Sopenharmony_ci#define FTS_POWER_ON_TIME_COUNTER_C	0x007C
418c2ecf20Sopenharmony_ci
428c2ecf20Sopenharmony_ci#define FTS_PAGE_SELECT_REG		0x007F
438c2ecf20Sopenharmony_ci
448c2ecf20Sopenharmony_ci#define FTS_WATCHDOG_TIME_PRESET	0x000B
458c2ecf20Sopenharmony_ci#define FTS_WATCHDOG_CONTROL		0x5081
468c2ecf20Sopenharmony_ci
478c2ecf20Sopenharmony_ci#define FTS_NO_FAN_SENSORS		0x08
488c2ecf20Sopenharmony_ci#define FTS_NO_TEMP_SENSORS		0x10
498c2ecf20Sopenharmony_ci#define FTS_NO_VOLT_SENSORS		0x04
508c2ecf20Sopenharmony_ci
518c2ecf20Sopenharmony_cistatic const unsigned short normal_i2c[] = { 0x73, I2C_CLIENT_END };
528c2ecf20Sopenharmony_ci
538c2ecf20Sopenharmony_cistatic const struct i2c_device_id fts_id[] = {
548c2ecf20Sopenharmony_ci	{ "ftsteutates", 0 },
558c2ecf20Sopenharmony_ci	{ }
568c2ecf20Sopenharmony_ci};
578c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(i2c, fts_id);
588c2ecf20Sopenharmony_ci
598c2ecf20Sopenharmony_cienum WATCHDOG_RESOLUTION {
608c2ecf20Sopenharmony_ci	seconds = 1,
618c2ecf20Sopenharmony_ci	minutes = 60
628c2ecf20Sopenharmony_ci};
638c2ecf20Sopenharmony_ci
648c2ecf20Sopenharmony_cistruct fts_data {
658c2ecf20Sopenharmony_ci	struct i2c_client *client;
668c2ecf20Sopenharmony_ci	/* update sensor data lock */
678c2ecf20Sopenharmony_ci	struct mutex update_lock;
688c2ecf20Sopenharmony_ci	/* read/write register lock */
698c2ecf20Sopenharmony_ci	struct mutex access_lock;
708c2ecf20Sopenharmony_ci	unsigned long last_updated; /* in jiffies */
718c2ecf20Sopenharmony_ci	struct watchdog_device wdd;
728c2ecf20Sopenharmony_ci	enum WATCHDOG_RESOLUTION resolution;
738c2ecf20Sopenharmony_ci	bool valid; /* false until following fields are valid */
748c2ecf20Sopenharmony_ci
758c2ecf20Sopenharmony_ci	u8 volt[FTS_NO_VOLT_SENSORS];
768c2ecf20Sopenharmony_ci
778c2ecf20Sopenharmony_ci	u8 temp_input[FTS_NO_TEMP_SENSORS];
788c2ecf20Sopenharmony_ci	u8 temp_alarm;
798c2ecf20Sopenharmony_ci
808c2ecf20Sopenharmony_ci	u8 fan_present;
818c2ecf20Sopenharmony_ci	u8 fan_input[FTS_NO_FAN_SENSORS]; /* in rps */
828c2ecf20Sopenharmony_ci	u8 fan_source[FTS_NO_FAN_SENSORS];
838c2ecf20Sopenharmony_ci	u8 fan_alarm;
848c2ecf20Sopenharmony_ci};
858c2ecf20Sopenharmony_ci
868c2ecf20Sopenharmony_ci#define FTS_REG_FAN_INPUT(idx) ((idx) + 0x20)
878c2ecf20Sopenharmony_ci#define FTS_REG_FAN_SOURCE(idx) ((idx) + 0x30)
888c2ecf20Sopenharmony_ci#define FTS_REG_FAN_CONTROL(idx) (((idx) << 16) + 0x4881)
898c2ecf20Sopenharmony_ci
908c2ecf20Sopenharmony_ci#define FTS_REG_TEMP_INPUT(idx) ((idx) + 0x40)
918c2ecf20Sopenharmony_ci#define FTS_REG_TEMP_CONTROL(idx) (((idx) << 16) + 0x0681)
928c2ecf20Sopenharmony_ci
938c2ecf20Sopenharmony_ci#define FTS_REG_VOLT(idx) ((idx) + 0x18)
948c2ecf20Sopenharmony_ci
958c2ecf20Sopenharmony_ci/*****************************************************************************/
968c2ecf20Sopenharmony_ci/* I2C Helper functions							     */
978c2ecf20Sopenharmony_ci/*****************************************************************************/
988c2ecf20Sopenharmony_cistatic int fts_read_byte(struct i2c_client *client, unsigned short reg)
998c2ecf20Sopenharmony_ci{
1008c2ecf20Sopenharmony_ci	int ret;
1018c2ecf20Sopenharmony_ci	unsigned char page = reg >> 8;
1028c2ecf20Sopenharmony_ci	struct fts_data *data = dev_get_drvdata(&client->dev);
1038c2ecf20Sopenharmony_ci
1048c2ecf20Sopenharmony_ci	mutex_lock(&data->access_lock);
1058c2ecf20Sopenharmony_ci
1068c2ecf20Sopenharmony_ci	dev_dbg(&client->dev, "page select - page: 0x%.02x\n", page);
1078c2ecf20Sopenharmony_ci	ret = i2c_smbus_write_byte_data(client, FTS_PAGE_SELECT_REG, page);
1088c2ecf20Sopenharmony_ci	if (ret < 0)
1098c2ecf20Sopenharmony_ci		goto error;
1108c2ecf20Sopenharmony_ci
1118c2ecf20Sopenharmony_ci	reg &= 0xFF;
1128c2ecf20Sopenharmony_ci	ret = i2c_smbus_read_byte_data(client, reg);
1138c2ecf20Sopenharmony_ci	dev_dbg(&client->dev, "read - reg: 0x%.02x: val: 0x%.02x\n", reg, ret);
1148c2ecf20Sopenharmony_ci
1158c2ecf20Sopenharmony_cierror:
1168c2ecf20Sopenharmony_ci	mutex_unlock(&data->access_lock);
1178c2ecf20Sopenharmony_ci	return ret;
1188c2ecf20Sopenharmony_ci}
1198c2ecf20Sopenharmony_ci
1208c2ecf20Sopenharmony_cistatic int fts_write_byte(struct i2c_client *client, unsigned short reg,
1218c2ecf20Sopenharmony_ci			  unsigned char value)
1228c2ecf20Sopenharmony_ci{
1238c2ecf20Sopenharmony_ci	int ret;
1248c2ecf20Sopenharmony_ci	unsigned char page = reg >> 8;
1258c2ecf20Sopenharmony_ci	struct fts_data *data = dev_get_drvdata(&client->dev);
1268c2ecf20Sopenharmony_ci
1278c2ecf20Sopenharmony_ci	mutex_lock(&data->access_lock);
1288c2ecf20Sopenharmony_ci
1298c2ecf20Sopenharmony_ci	dev_dbg(&client->dev, "page select - page: 0x%.02x\n", page);
1308c2ecf20Sopenharmony_ci	ret = i2c_smbus_write_byte_data(client, FTS_PAGE_SELECT_REG, page);
1318c2ecf20Sopenharmony_ci	if (ret < 0)
1328c2ecf20Sopenharmony_ci		goto error;
1338c2ecf20Sopenharmony_ci
1348c2ecf20Sopenharmony_ci	reg &= 0xFF;
1358c2ecf20Sopenharmony_ci	dev_dbg(&client->dev,
1368c2ecf20Sopenharmony_ci		"write - reg: 0x%.02x: val: 0x%.02x\n", reg, value);
1378c2ecf20Sopenharmony_ci	ret = i2c_smbus_write_byte_data(client, reg, value);
1388c2ecf20Sopenharmony_ci
1398c2ecf20Sopenharmony_cierror:
1408c2ecf20Sopenharmony_ci	mutex_unlock(&data->access_lock);
1418c2ecf20Sopenharmony_ci	return ret;
1428c2ecf20Sopenharmony_ci}
1438c2ecf20Sopenharmony_ci
1448c2ecf20Sopenharmony_ci/*****************************************************************************/
1458c2ecf20Sopenharmony_ci/* Data Updater Helper function						     */
1468c2ecf20Sopenharmony_ci/*****************************************************************************/
1478c2ecf20Sopenharmony_cistatic int fts_update_device(struct fts_data *data)
1488c2ecf20Sopenharmony_ci{
1498c2ecf20Sopenharmony_ci	int i;
1508c2ecf20Sopenharmony_ci	int err = 0;
1518c2ecf20Sopenharmony_ci
1528c2ecf20Sopenharmony_ci	mutex_lock(&data->update_lock);
1538c2ecf20Sopenharmony_ci	if (!time_after(jiffies, data->last_updated + 2 * HZ) && data->valid)
1548c2ecf20Sopenharmony_ci		goto exit;
1558c2ecf20Sopenharmony_ci
1568c2ecf20Sopenharmony_ci	err = fts_read_byte(data->client, FTS_DEVICE_STATUS_REG);
1578c2ecf20Sopenharmony_ci	if (err < 0)
1588c2ecf20Sopenharmony_ci		goto exit;
1598c2ecf20Sopenharmony_ci
1608c2ecf20Sopenharmony_ci	data->valid = !!(err & 0x02); /* Data not ready yet */
1618c2ecf20Sopenharmony_ci	if (unlikely(!data->valid)) {
1628c2ecf20Sopenharmony_ci		err = -EAGAIN;
1638c2ecf20Sopenharmony_ci		goto exit;
1648c2ecf20Sopenharmony_ci	}
1658c2ecf20Sopenharmony_ci
1668c2ecf20Sopenharmony_ci	err = fts_read_byte(data->client, FTS_FAN_PRESENT_REG);
1678c2ecf20Sopenharmony_ci	if (err < 0)
1688c2ecf20Sopenharmony_ci		goto exit;
1698c2ecf20Sopenharmony_ci	data->fan_present = err;
1708c2ecf20Sopenharmony_ci
1718c2ecf20Sopenharmony_ci	err = fts_read_byte(data->client, FTS_FAN_EVENT_REG);
1728c2ecf20Sopenharmony_ci	if (err < 0)
1738c2ecf20Sopenharmony_ci		goto exit;
1748c2ecf20Sopenharmony_ci	data->fan_alarm = err;
1758c2ecf20Sopenharmony_ci
1768c2ecf20Sopenharmony_ci	for (i = 0; i < FTS_NO_FAN_SENSORS; i++) {
1778c2ecf20Sopenharmony_ci		if (data->fan_present & BIT(i)) {
1788c2ecf20Sopenharmony_ci			err = fts_read_byte(data->client, FTS_REG_FAN_INPUT(i));
1798c2ecf20Sopenharmony_ci			if (err < 0)
1808c2ecf20Sopenharmony_ci				goto exit;
1818c2ecf20Sopenharmony_ci			data->fan_input[i] = err;
1828c2ecf20Sopenharmony_ci
1838c2ecf20Sopenharmony_ci			err = fts_read_byte(data->client,
1848c2ecf20Sopenharmony_ci					    FTS_REG_FAN_SOURCE(i));
1858c2ecf20Sopenharmony_ci			if (err < 0)
1868c2ecf20Sopenharmony_ci				goto exit;
1878c2ecf20Sopenharmony_ci			data->fan_source[i] = err;
1888c2ecf20Sopenharmony_ci		} else {
1898c2ecf20Sopenharmony_ci			data->fan_input[i] = 0;
1908c2ecf20Sopenharmony_ci			data->fan_source[i] = 0;
1918c2ecf20Sopenharmony_ci		}
1928c2ecf20Sopenharmony_ci	}
1938c2ecf20Sopenharmony_ci
1948c2ecf20Sopenharmony_ci	err = fts_read_byte(data->client, FTS_SENSOR_EVENT_REG);
1958c2ecf20Sopenharmony_ci	if (err < 0)
1968c2ecf20Sopenharmony_ci		goto exit;
1978c2ecf20Sopenharmony_ci	data->temp_alarm = err;
1988c2ecf20Sopenharmony_ci
1998c2ecf20Sopenharmony_ci	for (i = 0; i < FTS_NO_TEMP_SENSORS; i++) {
2008c2ecf20Sopenharmony_ci		err = fts_read_byte(data->client, FTS_REG_TEMP_INPUT(i));
2018c2ecf20Sopenharmony_ci		if (err < 0)
2028c2ecf20Sopenharmony_ci			goto exit;
2038c2ecf20Sopenharmony_ci		data->temp_input[i] = err;
2048c2ecf20Sopenharmony_ci	}
2058c2ecf20Sopenharmony_ci
2068c2ecf20Sopenharmony_ci	for (i = 0; i < FTS_NO_VOLT_SENSORS; i++) {
2078c2ecf20Sopenharmony_ci		err = fts_read_byte(data->client, FTS_REG_VOLT(i));
2088c2ecf20Sopenharmony_ci		if (err < 0)
2098c2ecf20Sopenharmony_ci			goto exit;
2108c2ecf20Sopenharmony_ci		data->volt[i] = err;
2118c2ecf20Sopenharmony_ci	}
2128c2ecf20Sopenharmony_ci	data->last_updated = jiffies;
2138c2ecf20Sopenharmony_ci	err = 0;
2148c2ecf20Sopenharmony_ciexit:
2158c2ecf20Sopenharmony_ci	mutex_unlock(&data->update_lock);
2168c2ecf20Sopenharmony_ci	return err;
2178c2ecf20Sopenharmony_ci}
2188c2ecf20Sopenharmony_ci
2198c2ecf20Sopenharmony_ci/*****************************************************************************/
2208c2ecf20Sopenharmony_ci/* Watchdog functions							     */
2218c2ecf20Sopenharmony_ci/*****************************************************************************/
2228c2ecf20Sopenharmony_cistatic int fts_wd_set_resolution(struct fts_data *data,
2238c2ecf20Sopenharmony_ci				 enum WATCHDOG_RESOLUTION resolution)
2248c2ecf20Sopenharmony_ci{
2258c2ecf20Sopenharmony_ci	int ret;
2268c2ecf20Sopenharmony_ci
2278c2ecf20Sopenharmony_ci	if (data->resolution == resolution)
2288c2ecf20Sopenharmony_ci		return 0;
2298c2ecf20Sopenharmony_ci
2308c2ecf20Sopenharmony_ci	ret = fts_read_byte(data->client, FTS_WATCHDOG_CONTROL);
2318c2ecf20Sopenharmony_ci	if (ret < 0)
2328c2ecf20Sopenharmony_ci		return ret;
2338c2ecf20Sopenharmony_ci
2348c2ecf20Sopenharmony_ci	if ((resolution == seconds && ret & BIT(1)) ||
2358c2ecf20Sopenharmony_ci	    (resolution == minutes && (ret & BIT(1)) == 0)) {
2368c2ecf20Sopenharmony_ci		data->resolution = resolution;
2378c2ecf20Sopenharmony_ci		return 0;
2388c2ecf20Sopenharmony_ci	}
2398c2ecf20Sopenharmony_ci
2408c2ecf20Sopenharmony_ci	if (resolution == seconds)
2418c2ecf20Sopenharmony_ci		ret |= BIT(1);
2428c2ecf20Sopenharmony_ci	else
2438c2ecf20Sopenharmony_ci		ret &= ~BIT(1);
2448c2ecf20Sopenharmony_ci
2458c2ecf20Sopenharmony_ci	ret = fts_write_byte(data->client, FTS_WATCHDOG_CONTROL, ret);
2468c2ecf20Sopenharmony_ci	if (ret < 0)
2478c2ecf20Sopenharmony_ci		return ret;
2488c2ecf20Sopenharmony_ci
2498c2ecf20Sopenharmony_ci	data->resolution = resolution;
2508c2ecf20Sopenharmony_ci	return ret;
2518c2ecf20Sopenharmony_ci}
2528c2ecf20Sopenharmony_ci
2538c2ecf20Sopenharmony_cistatic int fts_wd_set_timeout(struct watchdog_device *wdd, unsigned int timeout)
2548c2ecf20Sopenharmony_ci{
2558c2ecf20Sopenharmony_ci	struct fts_data *data;
2568c2ecf20Sopenharmony_ci	enum WATCHDOG_RESOLUTION resolution = seconds;
2578c2ecf20Sopenharmony_ci	int ret;
2588c2ecf20Sopenharmony_ci
2598c2ecf20Sopenharmony_ci	data = watchdog_get_drvdata(wdd);
2608c2ecf20Sopenharmony_ci	/* switch watchdog resolution to minutes if timeout does not fit
2618c2ecf20Sopenharmony_ci	 * into a byte
2628c2ecf20Sopenharmony_ci	 */
2638c2ecf20Sopenharmony_ci	if (timeout > 0xFF) {
2648c2ecf20Sopenharmony_ci		timeout = DIV_ROUND_UP(timeout, 60) * 60;
2658c2ecf20Sopenharmony_ci		resolution = minutes;
2668c2ecf20Sopenharmony_ci	}
2678c2ecf20Sopenharmony_ci
2688c2ecf20Sopenharmony_ci	ret = fts_wd_set_resolution(data, resolution);
2698c2ecf20Sopenharmony_ci	if (ret < 0)
2708c2ecf20Sopenharmony_ci		return ret;
2718c2ecf20Sopenharmony_ci
2728c2ecf20Sopenharmony_ci	wdd->timeout = timeout;
2738c2ecf20Sopenharmony_ci	return 0;
2748c2ecf20Sopenharmony_ci}
2758c2ecf20Sopenharmony_ci
2768c2ecf20Sopenharmony_cistatic int fts_wd_start(struct watchdog_device *wdd)
2778c2ecf20Sopenharmony_ci{
2788c2ecf20Sopenharmony_ci	struct fts_data *data = watchdog_get_drvdata(wdd);
2798c2ecf20Sopenharmony_ci
2808c2ecf20Sopenharmony_ci	return fts_write_byte(data->client, FTS_WATCHDOG_TIME_PRESET,
2818c2ecf20Sopenharmony_ci			      wdd->timeout / (u8)data->resolution);
2828c2ecf20Sopenharmony_ci}
2838c2ecf20Sopenharmony_ci
2848c2ecf20Sopenharmony_cistatic int fts_wd_stop(struct watchdog_device *wdd)
2858c2ecf20Sopenharmony_ci{
2868c2ecf20Sopenharmony_ci	struct fts_data *data;
2878c2ecf20Sopenharmony_ci
2888c2ecf20Sopenharmony_ci	data = watchdog_get_drvdata(wdd);
2898c2ecf20Sopenharmony_ci	return fts_write_byte(data->client, FTS_WATCHDOG_TIME_PRESET, 0);
2908c2ecf20Sopenharmony_ci}
2918c2ecf20Sopenharmony_ci
2928c2ecf20Sopenharmony_cistatic const struct watchdog_info fts_wd_info = {
2938c2ecf20Sopenharmony_ci	.options = WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING | WDIOF_MAGICCLOSE,
2948c2ecf20Sopenharmony_ci	.identity = "FTS Teutates Hardware Watchdog",
2958c2ecf20Sopenharmony_ci};
2968c2ecf20Sopenharmony_ci
2978c2ecf20Sopenharmony_cistatic const struct watchdog_ops fts_wd_ops = {
2988c2ecf20Sopenharmony_ci	.owner = THIS_MODULE,
2998c2ecf20Sopenharmony_ci	.start = fts_wd_start,
3008c2ecf20Sopenharmony_ci	.stop = fts_wd_stop,
3018c2ecf20Sopenharmony_ci	.set_timeout = fts_wd_set_timeout,
3028c2ecf20Sopenharmony_ci};
3038c2ecf20Sopenharmony_ci
3048c2ecf20Sopenharmony_cistatic int fts_watchdog_init(struct fts_data *data)
3058c2ecf20Sopenharmony_ci{
3068c2ecf20Sopenharmony_ci	int timeout, ret;
3078c2ecf20Sopenharmony_ci
3088c2ecf20Sopenharmony_ci	watchdog_set_drvdata(&data->wdd, data);
3098c2ecf20Sopenharmony_ci
3108c2ecf20Sopenharmony_ci	timeout = fts_read_byte(data->client, FTS_WATCHDOG_TIME_PRESET);
3118c2ecf20Sopenharmony_ci	if (timeout < 0)
3128c2ecf20Sopenharmony_ci		return timeout;
3138c2ecf20Sopenharmony_ci
3148c2ecf20Sopenharmony_ci	/* watchdog not running, set timeout to a default of 60 sec. */
3158c2ecf20Sopenharmony_ci	if (timeout == 0) {
3168c2ecf20Sopenharmony_ci		ret = fts_wd_set_resolution(data, seconds);
3178c2ecf20Sopenharmony_ci		if (ret < 0)
3188c2ecf20Sopenharmony_ci			return ret;
3198c2ecf20Sopenharmony_ci		data->wdd.timeout = 60;
3208c2ecf20Sopenharmony_ci	} else {
3218c2ecf20Sopenharmony_ci		ret = fts_read_byte(data->client, FTS_WATCHDOG_CONTROL);
3228c2ecf20Sopenharmony_ci		if (ret < 0)
3238c2ecf20Sopenharmony_ci			return ret;
3248c2ecf20Sopenharmony_ci
3258c2ecf20Sopenharmony_ci		data->resolution = ret & BIT(1) ? seconds : minutes;
3268c2ecf20Sopenharmony_ci		data->wdd.timeout = timeout * (u8)data->resolution;
3278c2ecf20Sopenharmony_ci		set_bit(WDOG_HW_RUNNING, &data->wdd.status);
3288c2ecf20Sopenharmony_ci	}
3298c2ecf20Sopenharmony_ci
3308c2ecf20Sopenharmony_ci	/* Register our watchdog part */
3318c2ecf20Sopenharmony_ci	data->wdd.info = &fts_wd_info;
3328c2ecf20Sopenharmony_ci	data->wdd.ops = &fts_wd_ops;
3338c2ecf20Sopenharmony_ci	data->wdd.parent = &data->client->dev;
3348c2ecf20Sopenharmony_ci	data->wdd.min_timeout = 1;
3358c2ecf20Sopenharmony_ci
3368c2ecf20Sopenharmony_ci	/* max timeout 255 minutes. */
3378c2ecf20Sopenharmony_ci	data->wdd.max_hw_heartbeat_ms = 0xFF * 60 * MSEC_PER_SEC;
3388c2ecf20Sopenharmony_ci
3398c2ecf20Sopenharmony_ci	return watchdog_register_device(&data->wdd);
3408c2ecf20Sopenharmony_ci}
3418c2ecf20Sopenharmony_ci
3428c2ecf20Sopenharmony_ci/*****************************************************************************/
3438c2ecf20Sopenharmony_ci/* SysFS handler functions						     */
3448c2ecf20Sopenharmony_ci/*****************************************************************************/
3458c2ecf20Sopenharmony_cistatic ssize_t in_value_show(struct device *dev,
3468c2ecf20Sopenharmony_ci			     struct device_attribute *devattr, char *buf)
3478c2ecf20Sopenharmony_ci{
3488c2ecf20Sopenharmony_ci	struct fts_data *data = dev_get_drvdata(dev);
3498c2ecf20Sopenharmony_ci	int index = to_sensor_dev_attr(devattr)->index;
3508c2ecf20Sopenharmony_ci	int err;
3518c2ecf20Sopenharmony_ci
3528c2ecf20Sopenharmony_ci	err = fts_update_device(data);
3538c2ecf20Sopenharmony_ci	if (err < 0)
3548c2ecf20Sopenharmony_ci		return err;
3558c2ecf20Sopenharmony_ci
3568c2ecf20Sopenharmony_ci	return sprintf(buf, "%u\n", data->volt[index]);
3578c2ecf20Sopenharmony_ci}
3588c2ecf20Sopenharmony_ci
3598c2ecf20Sopenharmony_cistatic ssize_t temp_value_show(struct device *dev,
3608c2ecf20Sopenharmony_ci			       struct device_attribute *devattr, char *buf)
3618c2ecf20Sopenharmony_ci{
3628c2ecf20Sopenharmony_ci	struct fts_data *data = dev_get_drvdata(dev);
3638c2ecf20Sopenharmony_ci	int index = to_sensor_dev_attr(devattr)->index;
3648c2ecf20Sopenharmony_ci	int err;
3658c2ecf20Sopenharmony_ci
3668c2ecf20Sopenharmony_ci	err = fts_update_device(data);
3678c2ecf20Sopenharmony_ci	if (err < 0)
3688c2ecf20Sopenharmony_ci		return err;
3698c2ecf20Sopenharmony_ci
3708c2ecf20Sopenharmony_ci	return sprintf(buf, "%u\n", data->temp_input[index]);
3718c2ecf20Sopenharmony_ci}
3728c2ecf20Sopenharmony_ci
3738c2ecf20Sopenharmony_cistatic ssize_t temp_fault_show(struct device *dev,
3748c2ecf20Sopenharmony_ci			       struct device_attribute *devattr, char *buf)
3758c2ecf20Sopenharmony_ci{
3768c2ecf20Sopenharmony_ci	struct fts_data *data = dev_get_drvdata(dev);
3778c2ecf20Sopenharmony_ci	int index = to_sensor_dev_attr(devattr)->index;
3788c2ecf20Sopenharmony_ci	int err;
3798c2ecf20Sopenharmony_ci
3808c2ecf20Sopenharmony_ci	err = fts_update_device(data);
3818c2ecf20Sopenharmony_ci	if (err < 0)
3828c2ecf20Sopenharmony_ci		return err;
3838c2ecf20Sopenharmony_ci
3848c2ecf20Sopenharmony_ci	/* 00h Temperature = Sensor Error */
3858c2ecf20Sopenharmony_ci	return sprintf(buf, "%d\n", data->temp_input[index] == 0);
3868c2ecf20Sopenharmony_ci}
3878c2ecf20Sopenharmony_ci
3888c2ecf20Sopenharmony_cistatic ssize_t temp_alarm_show(struct device *dev,
3898c2ecf20Sopenharmony_ci			       struct device_attribute *devattr, char *buf)
3908c2ecf20Sopenharmony_ci{
3918c2ecf20Sopenharmony_ci	struct fts_data *data = dev_get_drvdata(dev);
3928c2ecf20Sopenharmony_ci	int index = to_sensor_dev_attr(devattr)->index;
3938c2ecf20Sopenharmony_ci	int err;
3948c2ecf20Sopenharmony_ci
3958c2ecf20Sopenharmony_ci	err = fts_update_device(data);
3968c2ecf20Sopenharmony_ci	if (err < 0)
3978c2ecf20Sopenharmony_ci		return err;
3988c2ecf20Sopenharmony_ci
3998c2ecf20Sopenharmony_ci	return sprintf(buf, "%u\n", !!(data->temp_alarm & BIT(index)));
4008c2ecf20Sopenharmony_ci}
4018c2ecf20Sopenharmony_ci
4028c2ecf20Sopenharmony_cistatic ssize_t
4038c2ecf20Sopenharmony_citemp_alarm_store(struct device *dev, struct device_attribute *devattr,
4048c2ecf20Sopenharmony_ci		 const char *buf, size_t count)
4058c2ecf20Sopenharmony_ci{
4068c2ecf20Sopenharmony_ci	struct fts_data *data = dev_get_drvdata(dev);
4078c2ecf20Sopenharmony_ci	int index = to_sensor_dev_attr(devattr)->index;
4088c2ecf20Sopenharmony_ci	long ret;
4098c2ecf20Sopenharmony_ci
4108c2ecf20Sopenharmony_ci	ret = fts_update_device(data);
4118c2ecf20Sopenharmony_ci	if (ret < 0)
4128c2ecf20Sopenharmony_ci		return ret;
4138c2ecf20Sopenharmony_ci
4148c2ecf20Sopenharmony_ci	if (kstrtoul(buf, 10, &ret) || ret != 0)
4158c2ecf20Sopenharmony_ci		return -EINVAL;
4168c2ecf20Sopenharmony_ci
4178c2ecf20Sopenharmony_ci	mutex_lock(&data->update_lock);
4188c2ecf20Sopenharmony_ci	ret = fts_read_byte(data->client, FTS_REG_TEMP_CONTROL(index));
4198c2ecf20Sopenharmony_ci	if (ret < 0)
4208c2ecf20Sopenharmony_ci		goto error;
4218c2ecf20Sopenharmony_ci
4228c2ecf20Sopenharmony_ci	ret = fts_write_byte(data->client, FTS_REG_TEMP_CONTROL(index),
4238c2ecf20Sopenharmony_ci			     ret | 0x1);
4248c2ecf20Sopenharmony_ci	if (ret < 0)
4258c2ecf20Sopenharmony_ci		goto error;
4268c2ecf20Sopenharmony_ci
4278c2ecf20Sopenharmony_ci	data->valid = false;
4288c2ecf20Sopenharmony_ci	ret = count;
4298c2ecf20Sopenharmony_cierror:
4308c2ecf20Sopenharmony_ci	mutex_unlock(&data->update_lock);
4318c2ecf20Sopenharmony_ci	return ret;
4328c2ecf20Sopenharmony_ci}
4338c2ecf20Sopenharmony_ci
4348c2ecf20Sopenharmony_cistatic ssize_t fan_value_show(struct device *dev,
4358c2ecf20Sopenharmony_ci			      struct device_attribute *devattr, char *buf)
4368c2ecf20Sopenharmony_ci{
4378c2ecf20Sopenharmony_ci	struct fts_data *data = dev_get_drvdata(dev);
4388c2ecf20Sopenharmony_ci	int index = to_sensor_dev_attr(devattr)->index;
4398c2ecf20Sopenharmony_ci	int err;
4408c2ecf20Sopenharmony_ci
4418c2ecf20Sopenharmony_ci	err = fts_update_device(data);
4428c2ecf20Sopenharmony_ci	if (err < 0)
4438c2ecf20Sopenharmony_ci		return err;
4448c2ecf20Sopenharmony_ci
4458c2ecf20Sopenharmony_ci	return sprintf(buf, "%u\n", data->fan_input[index]);
4468c2ecf20Sopenharmony_ci}
4478c2ecf20Sopenharmony_ci
4488c2ecf20Sopenharmony_cistatic ssize_t fan_source_show(struct device *dev,
4498c2ecf20Sopenharmony_ci			       struct device_attribute *devattr, char *buf)
4508c2ecf20Sopenharmony_ci{
4518c2ecf20Sopenharmony_ci	struct fts_data *data = dev_get_drvdata(dev);
4528c2ecf20Sopenharmony_ci	int index = to_sensor_dev_attr(devattr)->index;
4538c2ecf20Sopenharmony_ci	int err;
4548c2ecf20Sopenharmony_ci
4558c2ecf20Sopenharmony_ci	err = fts_update_device(data);
4568c2ecf20Sopenharmony_ci	if (err < 0)
4578c2ecf20Sopenharmony_ci		return err;
4588c2ecf20Sopenharmony_ci
4598c2ecf20Sopenharmony_ci	return sprintf(buf, "%u\n", data->fan_source[index]);
4608c2ecf20Sopenharmony_ci}
4618c2ecf20Sopenharmony_ci
4628c2ecf20Sopenharmony_cistatic ssize_t fan_alarm_show(struct device *dev,
4638c2ecf20Sopenharmony_ci			      struct device_attribute *devattr, char *buf)
4648c2ecf20Sopenharmony_ci{
4658c2ecf20Sopenharmony_ci	struct fts_data *data = dev_get_drvdata(dev);
4668c2ecf20Sopenharmony_ci	int index = to_sensor_dev_attr(devattr)->index;
4678c2ecf20Sopenharmony_ci	int err;
4688c2ecf20Sopenharmony_ci
4698c2ecf20Sopenharmony_ci	err = fts_update_device(data);
4708c2ecf20Sopenharmony_ci	if (err < 0)
4718c2ecf20Sopenharmony_ci		return err;
4728c2ecf20Sopenharmony_ci
4738c2ecf20Sopenharmony_ci	return sprintf(buf, "%d\n", !!(data->fan_alarm & BIT(index)));
4748c2ecf20Sopenharmony_ci}
4758c2ecf20Sopenharmony_ci
4768c2ecf20Sopenharmony_cistatic ssize_t
4778c2ecf20Sopenharmony_cifan_alarm_store(struct device *dev, struct device_attribute *devattr,
4788c2ecf20Sopenharmony_ci		const char *buf, size_t count)
4798c2ecf20Sopenharmony_ci{
4808c2ecf20Sopenharmony_ci	struct fts_data *data = dev_get_drvdata(dev);
4818c2ecf20Sopenharmony_ci	int index = to_sensor_dev_attr(devattr)->index;
4828c2ecf20Sopenharmony_ci	long ret;
4838c2ecf20Sopenharmony_ci
4848c2ecf20Sopenharmony_ci	ret = fts_update_device(data);
4858c2ecf20Sopenharmony_ci	if (ret < 0)
4868c2ecf20Sopenharmony_ci		return ret;
4878c2ecf20Sopenharmony_ci
4888c2ecf20Sopenharmony_ci	if (kstrtoul(buf, 10, &ret) || ret != 0)
4898c2ecf20Sopenharmony_ci		return -EINVAL;
4908c2ecf20Sopenharmony_ci
4918c2ecf20Sopenharmony_ci	mutex_lock(&data->update_lock);
4928c2ecf20Sopenharmony_ci	ret = fts_read_byte(data->client, FTS_REG_FAN_CONTROL(index));
4938c2ecf20Sopenharmony_ci	if (ret < 0)
4948c2ecf20Sopenharmony_ci		goto error;
4958c2ecf20Sopenharmony_ci
4968c2ecf20Sopenharmony_ci	ret = fts_write_byte(data->client, FTS_REG_FAN_CONTROL(index),
4978c2ecf20Sopenharmony_ci			     ret | 0x1);
4988c2ecf20Sopenharmony_ci	if (ret < 0)
4998c2ecf20Sopenharmony_ci		goto error;
5008c2ecf20Sopenharmony_ci
5018c2ecf20Sopenharmony_ci	data->valid = false;
5028c2ecf20Sopenharmony_ci	ret = count;
5038c2ecf20Sopenharmony_cierror:
5048c2ecf20Sopenharmony_ci	mutex_unlock(&data->update_lock);
5058c2ecf20Sopenharmony_ci	return ret;
5068c2ecf20Sopenharmony_ci}
5078c2ecf20Sopenharmony_ci
5088c2ecf20Sopenharmony_ci/*****************************************************************************/
5098c2ecf20Sopenharmony_ci/* SysFS structs							     */
5108c2ecf20Sopenharmony_ci/*****************************************************************************/
5118c2ecf20Sopenharmony_ci
5128c2ecf20Sopenharmony_ci/* Temprature sensors */
5138c2ecf20Sopenharmony_cistatic SENSOR_DEVICE_ATTR_RO(temp1_input, temp_value, 0);
5148c2ecf20Sopenharmony_cistatic SENSOR_DEVICE_ATTR_RO(temp2_input, temp_value, 1);
5158c2ecf20Sopenharmony_cistatic SENSOR_DEVICE_ATTR_RO(temp3_input, temp_value, 2);
5168c2ecf20Sopenharmony_cistatic SENSOR_DEVICE_ATTR_RO(temp4_input, temp_value, 3);
5178c2ecf20Sopenharmony_cistatic SENSOR_DEVICE_ATTR_RO(temp5_input, temp_value, 4);
5188c2ecf20Sopenharmony_cistatic SENSOR_DEVICE_ATTR_RO(temp6_input, temp_value, 5);
5198c2ecf20Sopenharmony_cistatic SENSOR_DEVICE_ATTR_RO(temp7_input, temp_value, 6);
5208c2ecf20Sopenharmony_cistatic SENSOR_DEVICE_ATTR_RO(temp8_input, temp_value, 7);
5218c2ecf20Sopenharmony_cistatic SENSOR_DEVICE_ATTR_RO(temp9_input, temp_value, 8);
5228c2ecf20Sopenharmony_cistatic SENSOR_DEVICE_ATTR_RO(temp10_input, temp_value, 9);
5238c2ecf20Sopenharmony_cistatic SENSOR_DEVICE_ATTR_RO(temp11_input, temp_value, 10);
5248c2ecf20Sopenharmony_cistatic SENSOR_DEVICE_ATTR_RO(temp12_input, temp_value, 11);
5258c2ecf20Sopenharmony_cistatic SENSOR_DEVICE_ATTR_RO(temp13_input, temp_value, 12);
5268c2ecf20Sopenharmony_cistatic SENSOR_DEVICE_ATTR_RO(temp14_input, temp_value, 13);
5278c2ecf20Sopenharmony_cistatic SENSOR_DEVICE_ATTR_RO(temp15_input, temp_value, 14);
5288c2ecf20Sopenharmony_cistatic SENSOR_DEVICE_ATTR_RO(temp16_input, temp_value, 15);
5298c2ecf20Sopenharmony_ci
5308c2ecf20Sopenharmony_cistatic SENSOR_DEVICE_ATTR_RO(temp1_fault, temp_fault, 0);
5318c2ecf20Sopenharmony_cistatic SENSOR_DEVICE_ATTR_RO(temp2_fault, temp_fault, 1);
5328c2ecf20Sopenharmony_cistatic SENSOR_DEVICE_ATTR_RO(temp3_fault, temp_fault, 2);
5338c2ecf20Sopenharmony_cistatic SENSOR_DEVICE_ATTR_RO(temp4_fault, temp_fault, 3);
5348c2ecf20Sopenharmony_cistatic SENSOR_DEVICE_ATTR_RO(temp5_fault, temp_fault, 4);
5358c2ecf20Sopenharmony_cistatic SENSOR_DEVICE_ATTR_RO(temp6_fault, temp_fault, 5);
5368c2ecf20Sopenharmony_cistatic SENSOR_DEVICE_ATTR_RO(temp7_fault, temp_fault, 6);
5378c2ecf20Sopenharmony_cistatic SENSOR_DEVICE_ATTR_RO(temp8_fault, temp_fault, 7);
5388c2ecf20Sopenharmony_cistatic SENSOR_DEVICE_ATTR_RO(temp9_fault, temp_fault, 8);
5398c2ecf20Sopenharmony_cistatic SENSOR_DEVICE_ATTR_RO(temp10_fault, temp_fault, 9);
5408c2ecf20Sopenharmony_cistatic SENSOR_DEVICE_ATTR_RO(temp11_fault, temp_fault, 10);
5418c2ecf20Sopenharmony_cistatic SENSOR_DEVICE_ATTR_RO(temp12_fault, temp_fault, 11);
5428c2ecf20Sopenharmony_cistatic SENSOR_DEVICE_ATTR_RO(temp13_fault, temp_fault, 12);
5438c2ecf20Sopenharmony_cistatic SENSOR_DEVICE_ATTR_RO(temp14_fault, temp_fault, 13);
5448c2ecf20Sopenharmony_cistatic SENSOR_DEVICE_ATTR_RO(temp15_fault, temp_fault, 14);
5458c2ecf20Sopenharmony_cistatic SENSOR_DEVICE_ATTR_RO(temp16_fault, temp_fault, 15);
5468c2ecf20Sopenharmony_ci
5478c2ecf20Sopenharmony_cistatic SENSOR_DEVICE_ATTR_RW(temp1_alarm, temp_alarm, 0);
5488c2ecf20Sopenharmony_cistatic SENSOR_DEVICE_ATTR_RW(temp2_alarm, temp_alarm, 1);
5498c2ecf20Sopenharmony_cistatic SENSOR_DEVICE_ATTR_RW(temp3_alarm, temp_alarm, 2);
5508c2ecf20Sopenharmony_cistatic SENSOR_DEVICE_ATTR_RW(temp4_alarm, temp_alarm, 3);
5518c2ecf20Sopenharmony_cistatic SENSOR_DEVICE_ATTR_RW(temp5_alarm, temp_alarm, 4);
5528c2ecf20Sopenharmony_cistatic SENSOR_DEVICE_ATTR_RW(temp6_alarm, temp_alarm, 5);
5538c2ecf20Sopenharmony_cistatic SENSOR_DEVICE_ATTR_RW(temp7_alarm, temp_alarm, 6);
5548c2ecf20Sopenharmony_cistatic SENSOR_DEVICE_ATTR_RW(temp8_alarm, temp_alarm, 7);
5558c2ecf20Sopenharmony_cistatic SENSOR_DEVICE_ATTR_RW(temp9_alarm, temp_alarm, 8);
5568c2ecf20Sopenharmony_cistatic SENSOR_DEVICE_ATTR_RW(temp10_alarm, temp_alarm, 9);
5578c2ecf20Sopenharmony_cistatic SENSOR_DEVICE_ATTR_RW(temp11_alarm, temp_alarm, 10);
5588c2ecf20Sopenharmony_cistatic SENSOR_DEVICE_ATTR_RW(temp12_alarm, temp_alarm, 11);
5598c2ecf20Sopenharmony_cistatic SENSOR_DEVICE_ATTR_RW(temp13_alarm, temp_alarm, 12);
5608c2ecf20Sopenharmony_cistatic SENSOR_DEVICE_ATTR_RW(temp14_alarm, temp_alarm, 13);
5618c2ecf20Sopenharmony_cistatic SENSOR_DEVICE_ATTR_RW(temp15_alarm, temp_alarm, 14);
5628c2ecf20Sopenharmony_cistatic SENSOR_DEVICE_ATTR_RW(temp16_alarm, temp_alarm, 15);
5638c2ecf20Sopenharmony_ci
5648c2ecf20Sopenharmony_cistatic struct attribute *fts_temp_attrs[] = {
5658c2ecf20Sopenharmony_ci	&sensor_dev_attr_temp1_input.dev_attr.attr,
5668c2ecf20Sopenharmony_ci	&sensor_dev_attr_temp2_input.dev_attr.attr,
5678c2ecf20Sopenharmony_ci	&sensor_dev_attr_temp3_input.dev_attr.attr,
5688c2ecf20Sopenharmony_ci	&sensor_dev_attr_temp4_input.dev_attr.attr,
5698c2ecf20Sopenharmony_ci	&sensor_dev_attr_temp5_input.dev_attr.attr,
5708c2ecf20Sopenharmony_ci	&sensor_dev_attr_temp6_input.dev_attr.attr,
5718c2ecf20Sopenharmony_ci	&sensor_dev_attr_temp7_input.dev_attr.attr,
5728c2ecf20Sopenharmony_ci	&sensor_dev_attr_temp8_input.dev_attr.attr,
5738c2ecf20Sopenharmony_ci	&sensor_dev_attr_temp9_input.dev_attr.attr,
5748c2ecf20Sopenharmony_ci	&sensor_dev_attr_temp10_input.dev_attr.attr,
5758c2ecf20Sopenharmony_ci	&sensor_dev_attr_temp11_input.dev_attr.attr,
5768c2ecf20Sopenharmony_ci	&sensor_dev_attr_temp12_input.dev_attr.attr,
5778c2ecf20Sopenharmony_ci	&sensor_dev_attr_temp13_input.dev_attr.attr,
5788c2ecf20Sopenharmony_ci	&sensor_dev_attr_temp14_input.dev_attr.attr,
5798c2ecf20Sopenharmony_ci	&sensor_dev_attr_temp15_input.dev_attr.attr,
5808c2ecf20Sopenharmony_ci	&sensor_dev_attr_temp16_input.dev_attr.attr,
5818c2ecf20Sopenharmony_ci
5828c2ecf20Sopenharmony_ci	&sensor_dev_attr_temp1_fault.dev_attr.attr,
5838c2ecf20Sopenharmony_ci	&sensor_dev_attr_temp2_fault.dev_attr.attr,
5848c2ecf20Sopenharmony_ci	&sensor_dev_attr_temp3_fault.dev_attr.attr,
5858c2ecf20Sopenharmony_ci	&sensor_dev_attr_temp4_fault.dev_attr.attr,
5868c2ecf20Sopenharmony_ci	&sensor_dev_attr_temp5_fault.dev_attr.attr,
5878c2ecf20Sopenharmony_ci	&sensor_dev_attr_temp6_fault.dev_attr.attr,
5888c2ecf20Sopenharmony_ci	&sensor_dev_attr_temp7_fault.dev_attr.attr,
5898c2ecf20Sopenharmony_ci	&sensor_dev_attr_temp8_fault.dev_attr.attr,
5908c2ecf20Sopenharmony_ci	&sensor_dev_attr_temp9_fault.dev_attr.attr,
5918c2ecf20Sopenharmony_ci	&sensor_dev_attr_temp10_fault.dev_attr.attr,
5928c2ecf20Sopenharmony_ci	&sensor_dev_attr_temp11_fault.dev_attr.attr,
5938c2ecf20Sopenharmony_ci	&sensor_dev_attr_temp12_fault.dev_attr.attr,
5948c2ecf20Sopenharmony_ci	&sensor_dev_attr_temp13_fault.dev_attr.attr,
5958c2ecf20Sopenharmony_ci	&sensor_dev_attr_temp14_fault.dev_attr.attr,
5968c2ecf20Sopenharmony_ci	&sensor_dev_attr_temp15_fault.dev_attr.attr,
5978c2ecf20Sopenharmony_ci	&sensor_dev_attr_temp16_fault.dev_attr.attr,
5988c2ecf20Sopenharmony_ci
5998c2ecf20Sopenharmony_ci	&sensor_dev_attr_temp1_alarm.dev_attr.attr,
6008c2ecf20Sopenharmony_ci	&sensor_dev_attr_temp2_alarm.dev_attr.attr,
6018c2ecf20Sopenharmony_ci	&sensor_dev_attr_temp3_alarm.dev_attr.attr,
6028c2ecf20Sopenharmony_ci	&sensor_dev_attr_temp4_alarm.dev_attr.attr,
6038c2ecf20Sopenharmony_ci	&sensor_dev_attr_temp5_alarm.dev_attr.attr,
6048c2ecf20Sopenharmony_ci	&sensor_dev_attr_temp6_alarm.dev_attr.attr,
6058c2ecf20Sopenharmony_ci	&sensor_dev_attr_temp7_alarm.dev_attr.attr,
6068c2ecf20Sopenharmony_ci	&sensor_dev_attr_temp8_alarm.dev_attr.attr,
6078c2ecf20Sopenharmony_ci	&sensor_dev_attr_temp9_alarm.dev_attr.attr,
6088c2ecf20Sopenharmony_ci	&sensor_dev_attr_temp10_alarm.dev_attr.attr,
6098c2ecf20Sopenharmony_ci	&sensor_dev_attr_temp11_alarm.dev_attr.attr,
6108c2ecf20Sopenharmony_ci	&sensor_dev_attr_temp12_alarm.dev_attr.attr,
6118c2ecf20Sopenharmony_ci	&sensor_dev_attr_temp13_alarm.dev_attr.attr,
6128c2ecf20Sopenharmony_ci	&sensor_dev_attr_temp14_alarm.dev_attr.attr,
6138c2ecf20Sopenharmony_ci	&sensor_dev_attr_temp15_alarm.dev_attr.attr,
6148c2ecf20Sopenharmony_ci	&sensor_dev_attr_temp16_alarm.dev_attr.attr,
6158c2ecf20Sopenharmony_ci	NULL
6168c2ecf20Sopenharmony_ci};
6178c2ecf20Sopenharmony_ci
6188c2ecf20Sopenharmony_ci/* Fans */
6198c2ecf20Sopenharmony_cistatic SENSOR_DEVICE_ATTR_RO(fan1_input, fan_value, 0);
6208c2ecf20Sopenharmony_cistatic SENSOR_DEVICE_ATTR_RO(fan2_input, fan_value, 1);
6218c2ecf20Sopenharmony_cistatic SENSOR_DEVICE_ATTR_RO(fan3_input, fan_value, 2);
6228c2ecf20Sopenharmony_cistatic SENSOR_DEVICE_ATTR_RO(fan4_input, fan_value, 3);
6238c2ecf20Sopenharmony_cistatic SENSOR_DEVICE_ATTR_RO(fan5_input, fan_value, 4);
6248c2ecf20Sopenharmony_cistatic SENSOR_DEVICE_ATTR_RO(fan6_input, fan_value, 5);
6258c2ecf20Sopenharmony_cistatic SENSOR_DEVICE_ATTR_RO(fan7_input, fan_value, 6);
6268c2ecf20Sopenharmony_cistatic SENSOR_DEVICE_ATTR_RO(fan8_input, fan_value, 7);
6278c2ecf20Sopenharmony_ci
6288c2ecf20Sopenharmony_cistatic SENSOR_DEVICE_ATTR_RO(fan1_source, fan_source, 0);
6298c2ecf20Sopenharmony_cistatic SENSOR_DEVICE_ATTR_RO(fan2_source, fan_source, 1);
6308c2ecf20Sopenharmony_cistatic SENSOR_DEVICE_ATTR_RO(fan3_source, fan_source, 2);
6318c2ecf20Sopenharmony_cistatic SENSOR_DEVICE_ATTR_RO(fan4_source, fan_source, 3);
6328c2ecf20Sopenharmony_cistatic SENSOR_DEVICE_ATTR_RO(fan5_source, fan_source, 4);
6338c2ecf20Sopenharmony_cistatic SENSOR_DEVICE_ATTR_RO(fan6_source, fan_source, 5);
6348c2ecf20Sopenharmony_cistatic SENSOR_DEVICE_ATTR_RO(fan7_source, fan_source, 6);
6358c2ecf20Sopenharmony_cistatic SENSOR_DEVICE_ATTR_RO(fan8_source, fan_source, 7);
6368c2ecf20Sopenharmony_ci
6378c2ecf20Sopenharmony_cistatic SENSOR_DEVICE_ATTR_RW(fan1_alarm, fan_alarm, 0);
6388c2ecf20Sopenharmony_cistatic SENSOR_DEVICE_ATTR_RW(fan2_alarm, fan_alarm, 1);
6398c2ecf20Sopenharmony_cistatic SENSOR_DEVICE_ATTR_RW(fan3_alarm, fan_alarm, 2);
6408c2ecf20Sopenharmony_cistatic SENSOR_DEVICE_ATTR_RW(fan4_alarm, fan_alarm, 3);
6418c2ecf20Sopenharmony_cistatic SENSOR_DEVICE_ATTR_RW(fan5_alarm, fan_alarm, 4);
6428c2ecf20Sopenharmony_cistatic SENSOR_DEVICE_ATTR_RW(fan6_alarm, fan_alarm, 5);
6438c2ecf20Sopenharmony_cistatic SENSOR_DEVICE_ATTR_RW(fan7_alarm, fan_alarm, 6);
6448c2ecf20Sopenharmony_cistatic SENSOR_DEVICE_ATTR_RW(fan8_alarm, fan_alarm, 7);
6458c2ecf20Sopenharmony_ci
6468c2ecf20Sopenharmony_cistatic struct attribute *fts_fan_attrs[] = {
6478c2ecf20Sopenharmony_ci	&sensor_dev_attr_fan1_input.dev_attr.attr,
6488c2ecf20Sopenharmony_ci	&sensor_dev_attr_fan2_input.dev_attr.attr,
6498c2ecf20Sopenharmony_ci	&sensor_dev_attr_fan3_input.dev_attr.attr,
6508c2ecf20Sopenharmony_ci	&sensor_dev_attr_fan4_input.dev_attr.attr,
6518c2ecf20Sopenharmony_ci	&sensor_dev_attr_fan5_input.dev_attr.attr,
6528c2ecf20Sopenharmony_ci	&sensor_dev_attr_fan6_input.dev_attr.attr,
6538c2ecf20Sopenharmony_ci	&sensor_dev_attr_fan7_input.dev_attr.attr,
6548c2ecf20Sopenharmony_ci	&sensor_dev_attr_fan8_input.dev_attr.attr,
6558c2ecf20Sopenharmony_ci
6568c2ecf20Sopenharmony_ci	&sensor_dev_attr_fan1_source.dev_attr.attr,
6578c2ecf20Sopenharmony_ci	&sensor_dev_attr_fan2_source.dev_attr.attr,
6588c2ecf20Sopenharmony_ci	&sensor_dev_attr_fan3_source.dev_attr.attr,
6598c2ecf20Sopenharmony_ci	&sensor_dev_attr_fan4_source.dev_attr.attr,
6608c2ecf20Sopenharmony_ci	&sensor_dev_attr_fan5_source.dev_attr.attr,
6618c2ecf20Sopenharmony_ci	&sensor_dev_attr_fan6_source.dev_attr.attr,
6628c2ecf20Sopenharmony_ci	&sensor_dev_attr_fan7_source.dev_attr.attr,
6638c2ecf20Sopenharmony_ci	&sensor_dev_attr_fan8_source.dev_attr.attr,
6648c2ecf20Sopenharmony_ci
6658c2ecf20Sopenharmony_ci	&sensor_dev_attr_fan1_alarm.dev_attr.attr,
6668c2ecf20Sopenharmony_ci	&sensor_dev_attr_fan2_alarm.dev_attr.attr,
6678c2ecf20Sopenharmony_ci	&sensor_dev_attr_fan3_alarm.dev_attr.attr,
6688c2ecf20Sopenharmony_ci	&sensor_dev_attr_fan4_alarm.dev_attr.attr,
6698c2ecf20Sopenharmony_ci	&sensor_dev_attr_fan5_alarm.dev_attr.attr,
6708c2ecf20Sopenharmony_ci	&sensor_dev_attr_fan6_alarm.dev_attr.attr,
6718c2ecf20Sopenharmony_ci	&sensor_dev_attr_fan7_alarm.dev_attr.attr,
6728c2ecf20Sopenharmony_ci	&sensor_dev_attr_fan8_alarm.dev_attr.attr,
6738c2ecf20Sopenharmony_ci	NULL
6748c2ecf20Sopenharmony_ci};
6758c2ecf20Sopenharmony_ci
6768c2ecf20Sopenharmony_ci/* Voltages */
6778c2ecf20Sopenharmony_cistatic SENSOR_DEVICE_ATTR_RO(in1_input, in_value, 0);
6788c2ecf20Sopenharmony_cistatic SENSOR_DEVICE_ATTR_RO(in2_input, in_value, 1);
6798c2ecf20Sopenharmony_cistatic SENSOR_DEVICE_ATTR_RO(in3_input, in_value, 2);
6808c2ecf20Sopenharmony_cistatic SENSOR_DEVICE_ATTR_RO(in4_input, in_value, 3);
6818c2ecf20Sopenharmony_cistatic struct attribute *fts_voltage_attrs[] = {
6828c2ecf20Sopenharmony_ci	&sensor_dev_attr_in1_input.dev_attr.attr,
6838c2ecf20Sopenharmony_ci	&sensor_dev_attr_in2_input.dev_attr.attr,
6848c2ecf20Sopenharmony_ci	&sensor_dev_attr_in3_input.dev_attr.attr,
6858c2ecf20Sopenharmony_ci	&sensor_dev_attr_in4_input.dev_attr.attr,
6868c2ecf20Sopenharmony_ci	NULL
6878c2ecf20Sopenharmony_ci};
6888c2ecf20Sopenharmony_ci
6898c2ecf20Sopenharmony_cistatic const struct attribute_group fts_voltage_attr_group = {
6908c2ecf20Sopenharmony_ci	.attrs = fts_voltage_attrs
6918c2ecf20Sopenharmony_ci};
6928c2ecf20Sopenharmony_ci
6938c2ecf20Sopenharmony_cistatic const struct attribute_group fts_temp_attr_group = {
6948c2ecf20Sopenharmony_ci	.attrs = fts_temp_attrs
6958c2ecf20Sopenharmony_ci};
6968c2ecf20Sopenharmony_ci
6978c2ecf20Sopenharmony_cistatic const struct attribute_group fts_fan_attr_group = {
6988c2ecf20Sopenharmony_ci	.attrs = fts_fan_attrs
6998c2ecf20Sopenharmony_ci};
7008c2ecf20Sopenharmony_ci
7018c2ecf20Sopenharmony_cistatic const struct attribute_group *fts_attr_groups[] = {
7028c2ecf20Sopenharmony_ci	&fts_voltage_attr_group,
7038c2ecf20Sopenharmony_ci	&fts_temp_attr_group,
7048c2ecf20Sopenharmony_ci	&fts_fan_attr_group,
7058c2ecf20Sopenharmony_ci	NULL
7068c2ecf20Sopenharmony_ci};
7078c2ecf20Sopenharmony_ci
7088c2ecf20Sopenharmony_ci/*****************************************************************************/
7098c2ecf20Sopenharmony_ci/* Module initialization / remove functions				     */
7108c2ecf20Sopenharmony_ci/*****************************************************************************/
7118c2ecf20Sopenharmony_cistatic int fts_detect(struct i2c_client *client,
7128c2ecf20Sopenharmony_ci		      struct i2c_board_info *info)
7138c2ecf20Sopenharmony_ci{
7148c2ecf20Sopenharmony_ci	int val;
7158c2ecf20Sopenharmony_ci
7168c2ecf20Sopenharmony_ci	/* detection works with revsion greater or equal to 0x2b */
7178c2ecf20Sopenharmony_ci	val = i2c_smbus_read_byte_data(client, FTS_DEVICE_REVISION_REG);
7188c2ecf20Sopenharmony_ci	if (val < 0x2b)
7198c2ecf20Sopenharmony_ci		return -ENODEV;
7208c2ecf20Sopenharmony_ci
7218c2ecf20Sopenharmony_ci	/* Device Detect Regs must have 0x17 0x34 and 0x54 */
7228c2ecf20Sopenharmony_ci	val = i2c_smbus_read_byte_data(client, FTS_DEVICE_DETECT_REG_1);
7238c2ecf20Sopenharmony_ci	if (val != 0x17)
7248c2ecf20Sopenharmony_ci		return -ENODEV;
7258c2ecf20Sopenharmony_ci
7268c2ecf20Sopenharmony_ci	val = i2c_smbus_read_byte_data(client, FTS_DEVICE_DETECT_REG_2);
7278c2ecf20Sopenharmony_ci	if (val != 0x34)
7288c2ecf20Sopenharmony_ci		return -ENODEV;
7298c2ecf20Sopenharmony_ci
7308c2ecf20Sopenharmony_ci	val = i2c_smbus_read_byte_data(client, FTS_DEVICE_DETECT_REG_3);
7318c2ecf20Sopenharmony_ci	if (val != 0x54)
7328c2ecf20Sopenharmony_ci		return -ENODEV;
7338c2ecf20Sopenharmony_ci
7348c2ecf20Sopenharmony_ci	/*
7358c2ecf20Sopenharmony_ci	 * 0x10 == Baseboard Management Controller, 0x01 == Teutates
7368c2ecf20Sopenharmony_ci	 * Device ID Reg needs to be 0x11
7378c2ecf20Sopenharmony_ci	 */
7388c2ecf20Sopenharmony_ci	val = i2c_smbus_read_byte_data(client, FTS_DEVICE_ID_REG);
7398c2ecf20Sopenharmony_ci	if (val != 0x11)
7408c2ecf20Sopenharmony_ci		return -ENODEV;
7418c2ecf20Sopenharmony_ci
7428c2ecf20Sopenharmony_ci	strlcpy(info->type, fts_id[0].name, I2C_NAME_SIZE);
7438c2ecf20Sopenharmony_ci	info->flags = 0;
7448c2ecf20Sopenharmony_ci	return 0;
7458c2ecf20Sopenharmony_ci}
7468c2ecf20Sopenharmony_ci
7478c2ecf20Sopenharmony_cistatic int fts_remove(struct i2c_client *client)
7488c2ecf20Sopenharmony_ci{
7498c2ecf20Sopenharmony_ci	struct fts_data *data = dev_get_drvdata(&client->dev);
7508c2ecf20Sopenharmony_ci
7518c2ecf20Sopenharmony_ci	watchdog_unregister_device(&data->wdd);
7528c2ecf20Sopenharmony_ci	return 0;
7538c2ecf20Sopenharmony_ci}
7548c2ecf20Sopenharmony_ci
7558c2ecf20Sopenharmony_cistatic int fts_probe(struct i2c_client *client)
7568c2ecf20Sopenharmony_ci{
7578c2ecf20Sopenharmony_ci	u8 revision;
7588c2ecf20Sopenharmony_ci	struct fts_data *data;
7598c2ecf20Sopenharmony_ci	int err;
7608c2ecf20Sopenharmony_ci	s8 deviceid;
7618c2ecf20Sopenharmony_ci	struct device *hwmon_dev;
7628c2ecf20Sopenharmony_ci
7638c2ecf20Sopenharmony_ci	if (client->addr != 0x73)
7648c2ecf20Sopenharmony_ci		return -ENODEV;
7658c2ecf20Sopenharmony_ci
7668c2ecf20Sopenharmony_ci	/* Baseboard Management Controller check */
7678c2ecf20Sopenharmony_ci	deviceid = i2c_smbus_read_byte_data(client, FTS_DEVICE_ID_REG);
7688c2ecf20Sopenharmony_ci	if (deviceid > 0 && (deviceid & 0xF0) == 0x10) {
7698c2ecf20Sopenharmony_ci		switch (deviceid & 0x0F) {
7708c2ecf20Sopenharmony_ci		case 0x01:
7718c2ecf20Sopenharmony_ci			break;
7728c2ecf20Sopenharmony_ci		default:
7738c2ecf20Sopenharmony_ci			dev_dbg(&client->dev,
7748c2ecf20Sopenharmony_ci				"No Baseboard Management Controller\n");
7758c2ecf20Sopenharmony_ci			return -ENODEV;
7768c2ecf20Sopenharmony_ci		}
7778c2ecf20Sopenharmony_ci	} else {
7788c2ecf20Sopenharmony_ci		dev_dbg(&client->dev, "No fujitsu board\n");
7798c2ecf20Sopenharmony_ci		return -ENODEV;
7808c2ecf20Sopenharmony_ci	}
7818c2ecf20Sopenharmony_ci
7828c2ecf20Sopenharmony_ci	data = devm_kzalloc(&client->dev, sizeof(struct fts_data),
7838c2ecf20Sopenharmony_ci			    GFP_KERNEL);
7848c2ecf20Sopenharmony_ci	if (!data)
7858c2ecf20Sopenharmony_ci		return -ENOMEM;
7868c2ecf20Sopenharmony_ci
7878c2ecf20Sopenharmony_ci	mutex_init(&data->update_lock);
7888c2ecf20Sopenharmony_ci	mutex_init(&data->access_lock);
7898c2ecf20Sopenharmony_ci	data->client = client;
7908c2ecf20Sopenharmony_ci	dev_set_drvdata(&client->dev, data);
7918c2ecf20Sopenharmony_ci
7928c2ecf20Sopenharmony_ci	err = i2c_smbus_read_byte_data(client, FTS_DEVICE_REVISION_REG);
7938c2ecf20Sopenharmony_ci	if (err < 0)
7948c2ecf20Sopenharmony_ci		return err;
7958c2ecf20Sopenharmony_ci	revision = err;
7968c2ecf20Sopenharmony_ci
7978c2ecf20Sopenharmony_ci	hwmon_dev = devm_hwmon_device_register_with_groups(&client->dev,
7988c2ecf20Sopenharmony_ci							   "ftsteutates",
7998c2ecf20Sopenharmony_ci							   data,
8008c2ecf20Sopenharmony_ci							   fts_attr_groups);
8018c2ecf20Sopenharmony_ci	if (IS_ERR(hwmon_dev))
8028c2ecf20Sopenharmony_ci		return PTR_ERR(hwmon_dev);
8038c2ecf20Sopenharmony_ci
8048c2ecf20Sopenharmony_ci	err = fts_watchdog_init(data);
8058c2ecf20Sopenharmony_ci	if (err)
8068c2ecf20Sopenharmony_ci		return err;
8078c2ecf20Sopenharmony_ci
8088c2ecf20Sopenharmony_ci	dev_info(&client->dev, "Detected FTS Teutates chip, revision: %d.%d\n",
8098c2ecf20Sopenharmony_ci		 (revision & 0xF0) >> 4, revision & 0x0F);
8108c2ecf20Sopenharmony_ci	return 0;
8118c2ecf20Sopenharmony_ci}
8128c2ecf20Sopenharmony_ci
8138c2ecf20Sopenharmony_ci/*****************************************************************************/
8148c2ecf20Sopenharmony_ci/* Module Details							     */
8158c2ecf20Sopenharmony_ci/*****************************************************************************/
8168c2ecf20Sopenharmony_cistatic struct i2c_driver fts_driver = {
8178c2ecf20Sopenharmony_ci	.class = I2C_CLASS_HWMON,
8188c2ecf20Sopenharmony_ci	.driver = {
8198c2ecf20Sopenharmony_ci		.name = "ftsteutates",
8208c2ecf20Sopenharmony_ci	},
8218c2ecf20Sopenharmony_ci	.id_table = fts_id,
8228c2ecf20Sopenharmony_ci	.probe_new = fts_probe,
8238c2ecf20Sopenharmony_ci	.remove = fts_remove,
8248c2ecf20Sopenharmony_ci	.detect = fts_detect,
8258c2ecf20Sopenharmony_ci	.address_list = normal_i2c,
8268c2ecf20Sopenharmony_ci};
8278c2ecf20Sopenharmony_ci
8288c2ecf20Sopenharmony_cimodule_i2c_driver(fts_driver);
8298c2ecf20Sopenharmony_ci
8308c2ecf20Sopenharmony_ciMODULE_AUTHOR("Thilo Cestonaro <thilo.cestonaro@ts.fujitsu.com>");
8318c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("FTS Teutates driver");
8328c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL");
833