xref: /linux/drivers/hwmon/ftsteutates.c (revision c184f377a4cc9078d42563ef0fe3de6ea574b0cf)
1c942fddfSThomas Gleixner // SPDX-License-Identifier: GPL-2.0-or-later
208426edaSThilo Cestonaro /*
308426edaSThilo Cestonaro  * Support for the FTS Systemmonitoring Chip "Teutates"
408426edaSThilo Cestonaro  *
508426edaSThilo Cestonaro  * Copyright (C) 2016 Fujitsu Technology Solutions GmbH,
608426edaSThilo Cestonaro  *		  Thilo Cestonaro <thilo.cestonaro@ts.fujitsu.com>
708426edaSThilo Cestonaro  */
808426edaSThilo Cestonaro #include <linux/err.h>
908426edaSThilo Cestonaro #include <linux/hwmon.h>
1008426edaSThilo Cestonaro #include <linux/i2c.h>
1108426edaSThilo Cestonaro #include <linux/init.h>
1208426edaSThilo Cestonaro #include <linux/jiffies.h>
13ca8fd8c1SArmin Wolf #include <linux/math.h>
1408426edaSThilo Cestonaro #include <linux/module.h>
1508426edaSThilo Cestonaro #include <linux/mutex.h>
1608426edaSThilo Cestonaro #include <linux/slab.h>
1708426edaSThilo Cestonaro #include <linux/watchdog.h>
1808426edaSThilo Cestonaro 
1908426edaSThilo Cestonaro #define FTS_DEVICE_ID_REG		0x0000
2008426edaSThilo Cestonaro #define FTS_DEVICE_REVISION_REG		0x0001
2108426edaSThilo Cestonaro #define FTS_DEVICE_STATUS_REG		0x0004
2208426edaSThilo Cestonaro #define FTS_SATELLITE_STATUS_REG	0x0005
2308426edaSThilo Cestonaro #define FTS_EVENT_STATUS_REG		0x0006
2408426edaSThilo Cestonaro #define FTS_GLOBAL_CONTROL_REG		0x0007
2508426edaSThilo Cestonaro 
262d5aee43SThilo Cestonaro #define FTS_DEVICE_DETECT_REG_1		0x0C
272d5aee43SThilo Cestonaro #define FTS_DEVICE_DETECT_REG_2		0x0D
282d5aee43SThilo Cestonaro #define FTS_DEVICE_DETECT_REG_3		0x0E
292d5aee43SThilo Cestonaro 
3008426edaSThilo Cestonaro #define FTS_SENSOR_EVENT_REG		0x0010
3108426edaSThilo Cestonaro 
3208426edaSThilo Cestonaro #define FTS_FAN_EVENT_REG		0x0014
3308426edaSThilo Cestonaro #define FTS_FAN_PRESENT_REG		0x0015
3408426edaSThilo Cestonaro 
3508426edaSThilo Cestonaro #define FTS_POWER_ON_TIME_COUNTER_A	0x007A
3608426edaSThilo Cestonaro #define FTS_POWER_ON_TIME_COUNTER_B	0x007B
3708426edaSThilo Cestonaro #define FTS_POWER_ON_TIME_COUNTER_C	0x007C
3808426edaSThilo Cestonaro 
3908426edaSThilo Cestonaro #define FTS_PAGE_SELECT_REG		0x007F
4008426edaSThilo Cestonaro 
4108426edaSThilo Cestonaro #define FTS_WATCHDOG_TIME_PRESET	0x000B
4208426edaSThilo Cestonaro #define FTS_WATCHDOG_CONTROL		0x5081
4308426edaSThilo Cestonaro 
4408426edaSThilo Cestonaro #define FTS_NO_FAN_SENSORS		0x08
4508426edaSThilo Cestonaro #define FTS_NO_TEMP_SENSORS		0x10
4608426edaSThilo Cestonaro #define FTS_NO_VOLT_SENSORS		0x04
4708426edaSThilo Cestonaro 
481c5759d8SArmin Wolf #define FTS_FAN_SOURCE_INVALID		0xff
491c5759d8SArmin Wolf 
502d5aee43SThilo Cestonaro static const unsigned short normal_i2c[] = { 0x73, I2C_CLIENT_END };
512d5aee43SThilo Cestonaro 
521dad2e95SArvind Yadav static const struct i2c_device_id fts_id[] = {
5308426edaSThilo Cestonaro 	{ "ftsteutates", 0 },
5408426edaSThilo Cestonaro 	{ }
5508426edaSThilo Cestonaro };
5608426edaSThilo Cestonaro MODULE_DEVICE_TABLE(i2c, fts_id);
5708426edaSThilo Cestonaro 
5808426edaSThilo Cestonaro enum WATCHDOG_RESOLUTION {
5908426edaSThilo Cestonaro 	seconds = 1,
6008426edaSThilo Cestonaro 	minutes = 60
6108426edaSThilo Cestonaro };
6208426edaSThilo Cestonaro 
6308426edaSThilo Cestonaro struct fts_data {
6408426edaSThilo Cestonaro 	struct i2c_client *client;
6508426edaSThilo Cestonaro 	/* update sensor data lock */
6608426edaSThilo Cestonaro 	struct mutex update_lock;
6708426edaSThilo Cestonaro 	/* read/write register lock */
6808426edaSThilo Cestonaro 	struct mutex access_lock;
6908426edaSThilo Cestonaro 	unsigned long last_updated; /* in jiffies */
7008426edaSThilo Cestonaro 	struct watchdog_device wdd;
7108426edaSThilo Cestonaro 	enum WATCHDOG_RESOLUTION resolution;
7208426edaSThilo Cestonaro 	bool valid; /* false until following fields are valid */
7308426edaSThilo Cestonaro 
7408426edaSThilo Cestonaro 	u8 volt[FTS_NO_VOLT_SENSORS];
7508426edaSThilo Cestonaro 
7608426edaSThilo Cestonaro 	u8 temp_input[FTS_NO_TEMP_SENSORS];
7708426edaSThilo Cestonaro 	u8 temp_alarm;
7808426edaSThilo Cestonaro 
7908426edaSThilo Cestonaro 	u8 fan_present;
8008426edaSThilo Cestonaro 	u8 fan_input[FTS_NO_FAN_SENSORS]; /* in rps */
8108426edaSThilo Cestonaro 	u8 fan_source[FTS_NO_FAN_SENSORS];
8208426edaSThilo Cestonaro 	u8 fan_alarm;
8308426edaSThilo Cestonaro };
8408426edaSThilo Cestonaro 
8508426edaSThilo Cestonaro #define FTS_REG_FAN_INPUT(idx) ((idx) + 0x20)
8608426edaSThilo Cestonaro #define FTS_REG_FAN_SOURCE(idx) ((idx) + 0x30)
8708426edaSThilo Cestonaro #define FTS_REG_FAN_CONTROL(idx) (((idx) << 16) + 0x4881)
8808426edaSThilo Cestonaro 
8908426edaSThilo Cestonaro #define FTS_REG_TEMP_INPUT(idx) ((idx) + 0x40)
9008426edaSThilo Cestonaro #define FTS_REG_TEMP_CONTROL(idx) (((idx) << 16) + 0x0681)
9108426edaSThilo Cestonaro 
9208426edaSThilo Cestonaro #define FTS_REG_VOLT(idx) ((idx) + 0x18)
9308426edaSThilo Cestonaro 
9408426edaSThilo Cestonaro /*****************************************************************************/
9508426edaSThilo Cestonaro /* I2C Helper functions							     */
9608426edaSThilo Cestonaro /*****************************************************************************/
9708426edaSThilo Cestonaro static int fts_read_byte(struct i2c_client *client, unsigned short reg)
9808426edaSThilo Cestonaro {
9908426edaSThilo Cestonaro 	int ret;
10008426edaSThilo Cestonaro 	unsigned char page = reg >> 8;
10108426edaSThilo Cestonaro 	struct fts_data *data = dev_get_drvdata(&client->dev);
10208426edaSThilo Cestonaro 
10308426edaSThilo Cestonaro 	mutex_lock(&data->access_lock);
10408426edaSThilo Cestonaro 
10508426edaSThilo Cestonaro 	dev_dbg(&client->dev, "page select - page: 0x%.02x\n", page);
10608426edaSThilo Cestonaro 	ret = i2c_smbus_write_byte_data(client, FTS_PAGE_SELECT_REG, page);
10708426edaSThilo Cestonaro 	if (ret < 0)
10808426edaSThilo Cestonaro 		goto error;
10908426edaSThilo Cestonaro 
11008426edaSThilo Cestonaro 	reg &= 0xFF;
11108426edaSThilo Cestonaro 	ret = i2c_smbus_read_byte_data(client, reg);
11208426edaSThilo Cestonaro 	dev_dbg(&client->dev, "read - reg: 0x%.02x: val: 0x%.02x\n", reg, ret);
11308426edaSThilo Cestonaro 
11408426edaSThilo Cestonaro error:
11508426edaSThilo Cestonaro 	mutex_unlock(&data->access_lock);
11608426edaSThilo Cestonaro 	return ret;
11708426edaSThilo Cestonaro }
11808426edaSThilo Cestonaro 
11908426edaSThilo Cestonaro static int fts_write_byte(struct i2c_client *client, unsigned short reg,
12008426edaSThilo Cestonaro 			  unsigned char value)
12108426edaSThilo Cestonaro {
12208426edaSThilo Cestonaro 	int ret;
12308426edaSThilo Cestonaro 	unsigned char page = reg >> 8;
12408426edaSThilo Cestonaro 	struct fts_data *data = dev_get_drvdata(&client->dev);
12508426edaSThilo Cestonaro 
12608426edaSThilo Cestonaro 	mutex_lock(&data->access_lock);
12708426edaSThilo Cestonaro 
12808426edaSThilo Cestonaro 	dev_dbg(&client->dev, "page select - page: 0x%.02x\n", page);
12908426edaSThilo Cestonaro 	ret = i2c_smbus_write_byte_data(client, FTS_PAGE_SELECT_REG, page);
13008426edaSThilo Cestonaro 	if (ret < 0)
13108426edaSThilo Cestonaro 		goto error;
13208426edaSThilo Cestonaro 
13308426edaSThilo Cestonaro 	reg &= 0xFF;
13408426edaSThilo Cestonaro 	dev_dbg(&client->dev,
13508426edaSThilo Cestonaro 		"write - reg: 0x%.02x: val: 0x%.02x\n", reg, value);
13608426edaSThilo Cestonaro 	ret = i2c_smbus_write_byte_data(client, reg, value);
13708426edaSThilo Cestonaro 
13808426edaSThilo Cestonaro error:
13908426edaSThilo Cestonaro 	mutex_unlock(&data->access_lock);
14008426edaSThilo Cestonaro 	return ret;
14108426edaSThilo Cestonaro }
14208426edaSThilo Cestonaro 
14308426edaSThilo Cestonaro /*****************************************************************************/
14408426edaSThilo Cestonaro /* Data Updater Helper function						     */
14508426edaSThilo Cestonaro /*****************************************************************************/
14608426edaSThilo Cestonaro static int fts_update_device(struct fts_data *data)
14708426edaSThilo Cestonaro {
14808426edaSThilo Cestonaro 	int i;
14908426edaSThilo Cestonaro 	int err = 0;
15008426edaSThilo Cestonaro 
15108426edaSThilo Cestonaro 	mutex_lock(&data->update_lock);
15208426edaSThilo Cestonaro 	if (!time_after(jiffies, data->last_updated + 2 * HZ) && data->valid)
15308426edaSThilo Cestonaro 		goto exit;
15408426edaSThilo Cestonaro 
15508426edaSThilo Cestonaro 	err = fts_read_byte(data->client, FTS_DEVICE_STATUS_REG);
15608426edaSThilo Cestonaro 	if (err < 0)
15708426edaSThilo Cestonaro 		goto exit;
15808426edaSThilo Cestonaro 
15908426edaSThilo Cestonaro 	data->valid = !!(err & 0x02); /* Data not ready yet */
16008426edaSThilo Cestonaro 	if (unlikely(!data->valid)) {
16108426edaSThilo Cestonaro 		err = -EAGAIN;
16208426edaSThilo Cestonaro 		goto exit;
16308426edaSThilo Cestonaro 	}
16408426edaSThilo Cestonaro 
16508426edaSThilo Cestonaro 	err = fts_read_byte(data->client, FTS_FAN_PRESENT_REG);
16608426edaSThilo Cestonaro 	if (err < 0)
16708426edaSThilo Cestonaro 		goto exit;
16808426edaSThilo Cestonaro 	data->fan_present = err;
16908426edaSThilo Cestonaro 
17008426edaSThilo Cestonaro 	err = fts_read_byte(data->client, FTS_FAN_EVENT_REG);
17108426edaSThilo Cestonaro 	if (err < 0)
17208426edaSThilo Cestonaro 		goto exit;
17308426edaSThilo Cestonaro 	data->fan_alarm = err;
17408426edaSThilo Cestonaro 
17508426edaSThilo Cestonaro 	for (i = 0; i < FTS_NO_FAN_SENSORS; i++) {
17608426edaSThilo Cestonaro 		if (data->fan_present & BIT(i)) {
17708426edaSThilo Cestonaro 			err = fts_read_byte(data->client, FTS_REG_FAN_INPUT(i));
17808426edaSThilo Cestonaro 			if (err < 0)
17908426edaSThilo Cestonaro 				goto exit;
18008426edaSThilo Cestonaro 			data->fan_input[i] = err;
18108426edaSThilo Cestonaro 
18208426edaSThilo Cestonaro 			err = fts_read_byte(data->client,
18308426edaSThilo Cestonaro 					    FTS_REG_FAN_SOURCE(i));
18408426edaSThilo Cestonaro 			if (err < 0)
18508426edaSThilo Cestonaro 				goto exit;
18608426edaSThilo Cestonaro 			data->fan_source[i] = err;
18708426edaSThilo Cestonaro 		} else {
18808426edaSThilo Cestonaro 			data->fan_input[i] = 0;
1891c5759d8SArmin Wolf 			data->fan_source[i] = FTS_FAN_SOURCE_INVALID;
19008426edaSThilo Cestonaro 		}
19108426edaSThilo Cestonaro 	}
19208426edaSThilo Cestonaro 
19308426edaSThilo Cestonaro 	err = fts_read_byte(data->client, FTS_SENSOR_EVENT_REG);
19408426edaSThilo Cestonaro 	if (err < 0)
19508426edaSThilo Cestonaro 		goto exit;
19608426edaSThilo Cestonaro 	data->temp_alarm = err;
19708426edaSThilo Cestonaro 
19808426edaSThilo Cestonaro 	for (i = 0; i < FTS_NO_TEMP_SENSORS; i++) {
19908426edaSThilo Cestonaro 		err = fts_read_byte(data->client, FTS_REG_TEMP_INPUT(i));
20008426edaSThilo Cestonaro 		if (err < 0)
20108426edaSThilo Cestonaro 			goto exit;
20208426edaSThilo Cestonaro 		data->temp_input[i] = err;
20308426edaSThilo Cestonaro 	}
20408426edaSThilo Cestonaro 
20508426edaSThilo Cestonaro 	for (i = 0; i < FTS_NO_VOLT_SENSORS; i++) {
20608426edaSThilo Cestonaro 		err = fts_read_byte(data->client, FTS_REG_VOLT(i));
20708426edaSThilo Cestonaro 		if (err < 0)
20808426edaSThilo Cestonaro 			goto exit;
20908426edaSThilo Cestonaro 		data->volt[i] = err;
21008426edaSThilo Cestonaro 	}
21108426edaSThilo Cestonaro 	data->last_updated = jiffies;
21208426edaSThilo Cestonaro 	err = 0;
21308426edaSThilo Cestonaro exit:
21408426edaSThilo Cestonaro 	mutex_unlock(&data->update_lock);
21508426edaSThilo Cestonaro 	return err;
21608426edaSThilo Cestonaro }
21708426edaSThilo Cestonaro 
21808426edaSThilo Cestonaro /*****************************************************************************/
21908426edaSThilo Cestonaro /* Watchdog functions							     */
22008426edaSThilo Cestonaro /*****************************************************************************/
22108426edaSThilo Cestonaro static int fts_wd_set_resolution(struct fts_data *data,
22208426edaSThilo Cestonaro 				 enum WATCHDOG_RESOLUTION resolution)
22308426edaSThilo Cestonaro {
22408426edaSThilo Cestonaro 	int ret;
22508426edaSThilo Cestonaro 
22608426edaSThilo Cestonaro 	if (data->resolution == resolution)
22708426edaSThilo Cestonaro 		return 0;
22808426edaSThilo Cestonaro 
22908426edaSThilo Cestonaro 	ret = fts_read_byte(data->client, FTS_WATCHDOG_CONTROL);
23008426edaSThilo Cestonaro 	if (ret < 0)
23108426edaSThilo Cestonaro 		return ret;
23208426edaSThilo Cestonaro 
23308426edaSThilo Cestonaro 	if ((resolution == seconds && ret & BIT(1)) ||
23408426edaSThilo Cestonaro 	    (resolution == minutes && (ret & BIT(1)) == 0)) {
23508426edaSThilo Cestonaro 		data->resolution = resolution;
23608426edaSThilo Cestonaro 		return 0;
23708426edaSThilo Cestonaro 	}
23808426edaSThilo Cestonaro 
23908426edaSThilo Cestonaro 	if (resolution == seconds)
2404c8702b3SGuenter Roeck 		ret |= BIT(1);
24108426edaSThilo Cestonaro 	else
24208426edaSThilo Cestonaro 		ret &= ~BIT(1);
24308426edaSThilo Cestonaro 
24408426edaSThilo Cestonaro 	ret = fts_write_byte(data->client, FTS_WATCHDOG_CONTROL, ret);
24508426edaSThilo Cestonaro 	if (ret < 0)
24608426edaSThilo Cestonaro 		return ret;
24708426edaSThilo Cestonaro 
24808426edaSThilo Cestonaro 	data->resolution = resolution;
24908426edaSThilo Cestonaro 	return ret;
25008426edaSThilo Cestonaro }
25108426edaSThilo Cestonaro 
25208426edaSThilo Cestonaro static int fts_wd_set_timeout(struct watchdog_device *wdd, unsigned int timeout)
25308426edaSThilo Cestonaro {
25408426edaSThilo Cestonaro 	struct fts_data *data;
25508426edaSThilo Cestonaro 	enum WATCHDOG_RESOLUTION resolution = seconds;
25608426edaSThilo Cestonaro 	int ret;
25708426edaSThilo Cestonaro 
25808426edaSThilo Cestonaro 	data = watchdog_get_drvdata(wdd);
25908426edaSThilo Cestonaro 	/* switch watchdog resolution to minutes if timeout does not fit
26008426edaSThilo Cestonaro 	 * into a byte
26108426edaSThilo Cestonaro 	 */
26208426edaSThilo Cestonaro 	if (timeout > 0xFF) {
26308426edaSThilo Cestonaro 		timeout = DIV_ROUND_UP(timeout, 60) * 60;
26408426edaSThilo Cestonaro 		resolution = minutes;
26508426edaSThilo Cestonaro 	}
26608426edaSThilo Cestonaro 
26708426edaSThilo Cestonaro 	ret = fts_wd_set_resolution(data, resolution);
26808426edaSThilo Cestonaro 	if (ret < 0)
26908426edaSThilo Cestonaro 		return ret;
27008426edaSThilo Cestonaro 
27108426edaSThilo Cestonaro 	wdd->timeout = timeout;
27208426edaSThilo Cestonaro 	return 0;
27308426edaSThilo Cestonaro }
27408426edaSThilo Cestonaro 
27508426edaSThilo Cestonaro static int fts_wd_start(struct watchdog_device *wdd)
27608426edaSThilo Cestonaro {
27708426edaSThilo Cestonaro 	struct fts_data *data = watchdog_get_drvdata(wdd);
27808426edaSThilo Cestonaro 
27908426edaSThilo Cestonaro 	return fts_write_byte(data->client, FTS_WATCHDOG_TIME_PRESET,
28008426edaSThilo Cestonaro 			      wdd->timeout / (u8)data->resolution);
28108426edaSThilo Cestonaro }
28208426edaSThilo Cestonaro 
28308426edaSThilo Cestonaro static int fts_wd_stop(struct watchdog_device *wdd)
28408426edaSThilo Cestonaro {
28508426edaSThilo Cestonaro 	struct fts_data *data;
28608426edaSThilo Cestonaro 
28708426edaSThilo Cestonaro 	data = watchdog_get_drvdata(wdd);
28808426edaSThilo Cestonaro 	return fts_write_byte(data->client, FTS_WATCHDOG_TIME_PRESET, 0);
28908426edaSThilo Cestonaro }
29008426edaSThilo Cestonaro 
29108426edaSThilo Cestonaro static const struct watchdog_info fts_wd_info = {
29208426edaSThilo Cestonaro 	.options = WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING | WDIOF_MAGICCLOSE,
29308426edaSThilo Cestonaro 	.identity = "FTS Teutates Hardware Watchdog",
29408426edaSThilo Cestonaro };
29508426edaSThilo Cestonaro 
29608426edaSThilo Cestonaro static const struct watchdog_ops fts_wd_ops = {
29708426edaSThilo Cestonaro 	.owner = THIS_MODULE,
29808426edaSThilo Cestonaro 	.start = fts_wd_start,
29908426edaSThilo Cestonaro 	.stop = fts_wd_stop,
30008426edaSThilo Cestonaro 	.set_timeout = fts_wd_set_timeout,
30108426edaSThilo Cestonaro };
30208426edaSThilo Cestonaro 
30308426edaSThilo Cestonaro static int fts_watchdog_init(struct fts_data *data)
30408426edaSThilo Cestonaro {
30508426edaSThilo Cestonaro 	int timeout, ret;
30608426edaSThilo Cestonaro 
30708426edaSThilo Cestonaro 	watchdog_set_drvdata(&data->wdd, data);
30808426edaSThilo Cestonaro 
30908426edaSThilo Cestonaro 	timeout = fts_read_byte(data->client, FTS_WATCHDOG_TIME_PRESET);
31008426edaSThilo Cestonaro 	if (timeout < 0)
31108426edaSThilo Cestonaro 		return timeout;
31208426edaSThilo Cestonaro 
31308426edaSThilo Cestonaro 	/* watchdog not running, set timeout to a default of 60 sec. */
31408426edaSThilo Cestonaro 	if (timeout == 0) {
31508426edaSThilo Cestonaro 		ret = fts_wd_set_resolution(data, seconds);
31608426edaSThilo Cestonaro 		if (ret < 0)
31708426edaSThilo Cestonaro 			return ret;
31808426edaSThilo Cestonaro 		data->wdd.timeout = 60;
31908426edaSThilo Cestonaro 	} else {
32008426edaSThilo Cestonaro 		ret = fts_read_byte(data->client, FTS_WATCHDOG_CONTROL);
32108426edaSThilo Cestonaro 		if (ret < 0)
32208426edaSThilo Cestonaro 			return ret;
32308426edaSThilo Cestonaro 
32408426edaSThilo Cestonaro 		data->resolution = ret & BIT(1) ? seconds : minutes;
32508426edaSThilo Cestonaro 		data->wdd.timeout = timeout * (u8)data->resolution;
32608426edaSThilo Cestonaro 		set_bit(WDOG_HW_RUNNING, &data->wdd.status);
32708426edaSThilo Cestonaro 	}
32808426edaSThilo Cestonaro 
32908426edaSThilo Cestonaro 	/* Register our watchdog part */
33008426edaSThilo Cestonaro 	data->wdd.info = &fts_wd_info;
33108426edaSThilo Cestonaro 	data->wdd.ops = &fts_wd_ops;
33208426edaSThilo Cestonaro 	data->wdd.parent = &data->client->dev;
33308426edaSThilo Cestonaro 	data->wdd.min_timeout = 1;
33408426edaSThilo Cestonaro 
33508426edaSThilo Cestonaro 	/* max timeout 255 minutes. */
33608426edaSThilo Cestonaro 	data->wdd.max_hw_heartbeat_ms = 0xFF * 60 * MSEC_PER_SEC;
33708426edaSThilo Cestonaro 
338e1515a74SArmin Wolf 	return devm_watchdog_register_device(&data->client->dev, &data->wdd);
33908426edaSThilo Cestonaro }
34008426edaSThilo Cestonaro 
341dea60ff0SArmin Wolf static umode_t fts_is_visible(const void *devdata, enum hwmon_sensor_types type, u32 attr,
342dea60ff0SArmin Wolf 			      int channel)
343dea60ff0SArmin Wolf {
344dea60ff0SArmin Wolf 	switch (type) {
345dea60ff0SArmin Wolf 	case hwmon_temp:
346dea60ff0SArmin Wolf 		switch (attr) {
347dea60ff0SArmin Wolf 		case hwmon_temp_input:
348dea60ff0SArmin Wolf 		case hwmon_temp_fault:
349dea60ff0SArmin Wolf 			return 0444;
350dea60ff0SArmin Wolf 		case hwmon_temp_alarm:
351dea60ff0SArmin Wolf 			return 0644;
352dea60ff0SArmin Wolf 		default:
353dea60ff0SArmin Wolf 			break;
354dea60ff0SArmin Wolf 		}
355dea60ff0SArmin Wolf 		break;
356dea60ff0SArmin Wolf 	case hwmon_fan:
357dea60ff0SArmin Wolf 		switch (attr) {
358dea60ff0SArmin Wolf 		case hwmon_fan_input:
359*c184f377SArmin Wolf 		case hwmon_fan_fault:
360dea60ff0SArmin Wolf 			return 0444;
361dea60ff0SArmin Wolf 		case hwmon_fan_alarm:
362dea60ff0SArmin Wolf 			return 0644;
363dea60ff0SArmin Wolf 		default:
364dea60ff0SArmin Wolf 			break;
365dea60ff0SArmin Wolf 		}
366dea60ff0SArmin Wolf 		break;
3671c5759d8SArmin Wolf 	case hwmon_pwm:
368dea60ff0SArmin Wolf 	case hwmon_in:
369dea60ff0SArmin Wolf 		return 0444;
370dea60ff0SArmin Wolf 	default:
371dea60ff0SArmin Wolf 		break;
372dea60ff0SArmin Wolf 	}
373dea60ff0SArmin Wolf 
374dea60ff0SArmin Wolf 	return 0;
375dea60ff0SArmin Wolf }
376dea60ff0SArmin Wolf 
377dea60ff0SArmin Wolf static int fts_read(struct device *dev, enum hwmon_sensor_types type, u32 attr, int channel,
378dea60ff0SArmin Wolf 		    long *val)
379dea60ff0SArmin Wolf {
380dea60ff0SArmin Wolf 	struct fts_data *data = dev_get_drvdata(dev);
381dea60ff0SArmin Wolf 	int ret = fts_update_device(data);
382dea60ff0SArmin Wolf 
383dea60ff0SArmin Wolf 	if (ret < 0)
384dea60ff0SArmin Wolf 		return ret;
385dea60ff0SArmin Wolf 
386dea60ff0SArmin Wolf 	switch (type) {
387dea60ff0SArmin Wolf 	case hwmon_temp:
388dea60ff0SArmin Wolf 		switch (attr) {
389dea60ff0SArmin Wolf 		case hwmon_temp_input:
390dea60ff0SArmin Wolf 			*val = (data->temp_input[channel] - 64) * 1000;
391dea60ff0SArmin Wolf 
392dea60ff0SArmin Wolf 			return 0;
393dea60ff0SArmin Wolf 		case hwmon_temp_alarm:
394dea60ff0SArmin Wolf 			*val = !!(data->temp_alarm & BIT(channel));
395dea60ff0SArmin Wolf 
396dea60ff0SArmin Wolf 			return 0;
397dea60ff0SArmin Wolf 		case hwmon_temp_fault:
398dea60ff0SArmin Wolf 			/* 00h Temperature = Sensor Error */;
399dea60ff0SArmin Wolf 			*val = (data->temp_input[channel] == 0);
400dea60ff0SArmin Wolf 
401dea60ff0SArmin Wolf 			return 0;
402dea60ff0SArmin Wolf 		default:
403dea60ff0SArmin Wolf 			break;
404dea60ff0SArmin Wolf 		}
405dea60ff0SArmin Wolf 		break;
406dea60ff0SArmin Wolf 	case hwmon_fan:
407dea60ff0SArmin Wolf 		switch (attr) {
408dea60ff0SArmin Wolf 		case hwmon_fan_input:
409dea60ff0SArmin Wolf 			*val = data->fan_input[channel] * 60;
410dea60ff0SArmin Wolf 
411dea60ff0SArmin Wolf 			return 0;
412dea60ff0SArmin Wolf 		case hwmon_fan_alarm:
413dea60ff0SArmin Wolf 			*val = !!(data->fan_alarm & BIT(channel));
414dea60ff0SArmin Wolf 
415dea60ff0SArmin Wolf 			return 0;
416*c184f377SArmin Wolf 		case hwmon_fan_fault:
417*c184f377SArmin Wolf 			*val = !(data->fan_present & BIT(channel));
418*c184f377SArmin Wolf 
419*c184f377SArmin Wolf 			return 0;
420dea60ff0SArmin Wolf 		default:
421dea60ff0SArmin Wolf 			break;
422dea60ff0SArmin Wolf 		}
423dea60ff0SArmin Wolf 		break;
4241c5759d8SArmin Wolf 	case hwmon_pwm:
4251c5759d8SArmin Wolf 		switch (attr) {
4261c5759d8SArmin Wolf 		case hwmon_pwm_auto_channels_temp:
4271c5759d8SArmin Wolf 			if (data->fan_source[channel] == FTS_FAN_SOURCE_INVALID)
4281c5759d8SArmin Wolf 				*val = 0;
4291c5759d8SArmin Wolf 			else
4301c5759d8SArmin Wolf 				*val = BIT(data->fan_source[channel]);
4311c5759d8SArmin Wolf 
4321c5759d8SArmin Wolf 			return 0;
4331c5759d8SArmin Wolf 		default:
4341c5759d8SArmin Wolf 			break;
4351c5759d8SArmin Wolf 		}
4361c5759d8SArmin Wolf 		break;
437dea60ff0SArmin Wolf 	case hwmon_in:
438dea60ff0SArmin Wolf 		switch (attr) {
439dea60ff0SArmin Wolf 		case hwmon_in_input:
440dea60ff0SArmin Wolf 			*val = DIV_ROUND_CLOSEST(data->volt[channel] * 3300, 255);
441dea60ff0SArmin Wolf 
442dea60ff0SArmin Wolf 			return 0;
443dea60ff0SArmin Wolf 		default:
444dea60ff0SArmin Wolf 			break;
445dea60ff0SArmin Wolf 		}
446dea60ff0SArmin Wolf 		break;
447dea60ff0SArmin Wolf 	default:
448dea60ff0SArmin Wolf 		break;
449dea60ff0SArmin Wolf 	}
450dea60ff0SArmin Wolf 
451dea60ff0SArmin Wolf 	return -EOPNOTSUPP;
452dea60ff0SArmin Wolf }
453dea60ff0SArmin Wolf 
454dea60ff0SArmin Wolf static int fts_write(struct device *dev, enum hwmon_sensor_types type, u32 attr, int channel,
455dea60ff0SArmin Wolf 		     long val)
456dea60ff0SArmin Wolf {
457dea60ff0SArmin Wolf 	struct fts_data *data = dev_get_drvdata(dev);
458dea60ff0SArmin Wolf 	int ret = fts_update_device(data);
459dea60ff0SArmin Wolf 
460dea60ff0SArmin Wolf 	if (ret < 0)
461dea60ff0SArmin Wolf 		return ret;
462dea60ff0SArmin Wolf 
463dea60ff0SArmin Wolf 	switch (type) {
464dea60ff0SArmin Wolf 	case hwmon_temp:
465dea60ff0SArmin Wolf 		switch (attr) {
466dea60ff0SArmin Wolf 		case hwmon_temp_alarm:
467dea60ff0SArmin Wolf 			if (val)
468dea60ff0SArmin Wolf 				return -EINVAL;
469dea60ff0SArmin Wolf 
470dea60ff0SArmin Wolf 			mutex_lock(&data->update_lock);
471dea60ff0SArmin Wolf 			ret = fts_read_byte(data->client, FTS_REG_TEMP_CONTROL(channel));
472dea60ff0SArmin Wolf 			if (ret >= 0)
473dea60ff0SArmin Wolf 				ret = fts_write_byte(data->client, FTS_REG_TEMP_CONTROL(channel),
474dea60ff0SArmin Wolf 						     ret | 0x1);
475dea60ff0SArmin Wolf 			if (ret >= 0)
476dea60ff0SArmin Wolf 				data->valid = false;
477dea60ff0SArmin Wolf 
478dea60ff0SArmin Wolf 			mutex_unlock(&data->update_lock);
479dea60ff0SArmin Wolf 			if (ret < 0)
480dea60ff0SArmin Wolf 				return ret;
481dea60ff0SArmin Wolf 
482dea60ff0SArmin Wolf 			return 0;
483dea60ff0SArmin Wolf 		default:
484dea60ff0SArmin Wolf 			break;
485dea60ff0SArmin Wolf 		}
486dea60ff0SArmin Wolf 		break;
487dea60ff0SArmin Wolf 	case hwmon_fan:
488dea60ff0SArmin Wolf 		switch (attr) {
489dea60ff0SArmin Wolf 		case hwmon_fan_alarm:
490dea60ff0SArmin Wolf 			if (val)
491dea60ff0SArmin Wolf 				return -EINVAL;
492dea60ff0SArmin Wolf 
493dea60ff0SArmin Wolf 			mutex_lock(&data->update_lock);
494dea60ff0SArmin Wolf 			ret = fts_read_byte(data->client, FTS_REG_FAN_CONTROL(channel));
495dea60ff0SArmin Wolf 			if (ret >= 0)
496dea60ff0SArmin Wolf 				ret = fts_write_byte(data->client, FTS_REG_FAN_CONTROL(channel),
497dea60ff0SArmin Wolf 						     ret | 0x1);
498dea60ff0SArmin Wolf 			if (ret >= 0)
499dea60ff0SArmin Wolf 				data->valid = false;
500dea60ff0SArmin Wolf 
501dea60ff0SArmin Wolf 			mutex_unlock(&data->update_lock);
502dea60ff0SArmin Wolf 			if (ret < 0)
503dea60ff0SArmin Wolf 				return ret;
504dea60ff0SArmin Wolf 
505dea60ff0SArmin Wolf 			return 0;
506dea60ff0SArmin Wolf 		default:
507dea60ff0SArmin Wolf 			break;
508dea60ff0SArmin Wolf 		}
509dea60ff0SArmin Wolf 		break;
510dea60ff0SArmin Wolf 	default:
511dea60ff0SArmin Wolf 		break;
512dea60ff0SArmin Wolf 	}
513dea60ff0SArmin Wolf 
514dea60ff0SArmin Wolf 	return -EOPNOTSUPP;
515dea60ff0SArmin Wolf }
516dea60ff0SArmin Wolf 
517dea60ff0SArmin Wolf static const struct hwmon_ops fts_ops = {
518dea60ff0SArmin Wolf 	.is_visible = fts_is_visible,
519dea60ff0SArmin Wolf 	.read = fts_read,
520dea60ff0SArmin Wolf 	.write = fts_write,
521dea60ff0SArmin Wolf };
522dea60ff0SArmin Wolf 
523dea60ff0SArmin Wolf static const struct hwmon_channel_info *fts_info[] = {
524dea60ff0SArmin Wolf 	HWMON_CHANNEL_INFO(chip, HWMON_C_REGISTER_TZ),
525dea60ff0SArmin Wolf 	HWMON_CHANNEL_INFO(temp,
526dea60ff0SArmin Wolf 			   HWMON_T_INPUT | HWMON_T_ALARM | HWMON_T_FAULT,
527dea60ff0SArmin Wolf 			   HWMON_T_INPUT | HWMON_T_ALARM | HWMON_T_FAULT,
528dea60ff0SArmin Wolf 			   HWMON_T_INPUT | HWMON_T_ALARM | HWMON_T_FAULT,
529dea60ff0SArmin Wolf 			   HWMON_T_INPUT | HWMON_T_ALARM | HWMON_T_FAULT,
530dea60ff0SArmin Wolf 			   HWMON_T_INPUT | HWMON_T_ALARM | HWMON_T_FAULT,
531dea60ff0SArmin Wolf 			   HWMON_T_INPUT | HWMON_T_ALARM | HWMON_T_FAULT,
532dea60ff0SArmin Wolf 			   HWMON_T_INPUT | HWMON_T_ALARM | HWMON_T_FAULT,
533dea60ff0SArmin Wolf 			   HWMON_T_INPUT | HWMON_T_ALARM | HWMON_T_FAULT,
534dea60ff0SArmin Wolf 			   HWMON_T_INPUT | HWMON_T_ALARM | HWMON_T_FAULT,
535dea60ff0SArmin Wolf 			   HWMON_T_INPUT | HWMON_T_ALARM | HWMON_T_FAULT,
536dea60ff0SArmin Wolf 			   HWMON_T_INPUT | HWMON_T_ALARM | HWMON_T_FAULT,
537dea60ff0SArmin Wolf 			   HWMON_T_INPUT | HWMON_T_ALARM | HWMON_T_FAULT,
538dea60ff0SArmin Wolf 			   HWMON_T_INPUT | HWMON_T_ALARM | HWMON_T_FAULT,
539dea60ff0SArmin Wolf 			   HWMON_T_INPUT | HWMON_T_ALARM | HWMON_T_FAULT,
540dea60ff0SArmin Wolf 			   HWMON_T_INPUT | HWMON_T_ALARM | HWMON_T_FAULT,
541dea60ff0SArmin Wolf 			   HWMON_T_INPUT | HWMON_T_ALARM | HWMON_T_FAULT
542dea60ff0SArmin Wolf 			   ),
543dea60ff0SArmin Wolf 	HWMON_CHANNEL_INFO(fan,
544*c184f377SArmin Wolf 			   HWMON_F_INPUT | HWMON_F_ALARM | HWMON_F_FAULT,
545*c184f377SArmin Wolf 			   HWMON_F_INPUT | HWMON_F_ALARM | HWMON_F_FAULT,
546*c184f377SArmin Wolf 			   HWMON_F_INPUT | HWMON_F_ALARM | HWMON_F_FAULT,
547*c184f377SArmin Wolf 			   HWMON_F_INPUT | HWMON_F_ALARM | HWMON_F_FAULT,
548*c184f377SArmin Wolf 			   HWMON_F_INPUT | HWMON_F_ALARM | HWMON_F_FAULT,
549*c184f377SArmin Wolf 			   HWMON_F_INPUT | HWMON_F_ALARM | HWMON_F_FAULT,
550*c184f377SArmin Wolf 			   HWMON_F_INPUT | HWMON_F_ALARM | HWMON_F_FAULT,
551*c184f377SArmin Wolf 			   HWMON_F_INPUT | HWMON_F_ALARM | HWMON_F_FAULT
552dea60ff0SArmin Wolf 			   ),
5531c5759d8SArmin Wolf 	HWMON_CHANNEL_INFO(pwm,
5541c5759d8SArmin Wolf 			   HWMON_PWM_AUTO_CHANNELS_TEMP,
5551c5759d8SArmin Wolf 			   HWMON_PWM_AUTO_CHANNELS_TEMP,
5561c5759d8SArmin Wolf 			   HWMON_PWM_AUTO_CHANNELS_TEMP,
5571c5759d8SArmin Wolf 			   HWMON_PWM_AUTO_CHANNELS_TEMP,
5581c5759d8SArmin Wolf 			   HWMON_PWM_AUTO_CHANNELS_TEMP,
5591c5759d8SArmin Wolf 			   HWMON_PWM_AUTO_CHANNELS_TEMP,
5601c5759d8SArmin Wolf 			   HWMON_PWM_AUTO_CHANNELS_TEMP,
5611c5759d8SArmin Wolf 			   HWMON_PWM_AUTO_CHANNELS_TEMP
5621c5759d8SArmin Wolf 			   ),
563dea60ff0SArmin Wolf 	HWMON_CHANNEL_INFO(in,
564dea60ff0SArmin Wolf 			   HWMON_I_INPUT,
565dea60ff0SArmin Wolf 			   HWMON_I_INPUT,
566dea60ff0SArmin Wolf 			   HWMON_I_INPUT,
567dea60ff0SArmin Wolf 			   HWMON_I_INPUT
568dea60ff0SArmin Wolf 			   ),
569dea60ff0SArmin Wolf 	NULL
570dea60ff0SArmin Wolf };
571dea60ff0SArmin Wolf 
572dea60ff0SArmin Wolf static const struct hwmon_chip_info fts_chip_info = {
573dea60ff0SArmin Wolf 	.ops = &fts_ops,
574dea60ff0SArmin Wolf 	.info = fts_info,
575dea60ff0SArmin Wolf };
576dea60ff0SArmin Wolf 
57708426edaSThilo Cestonaro /*****************************************************************************/
57808426edaSThilo Cestonaro /* Module initialization / remove functions				     */
57908426edaSThilo Cestonaro /*****************************************************************************/
5802d5aee43SThilo Cestonaro static int fts_detect(struct i2c_client *client,
5812d5aee43SThilo Cestonaro 		      struct i2c_board_info *info)
5822d5aee43SThilo Cestonaro {
5832d5aee43SThilo Cestonaro 	int val;
5842d5aee43SThilo Cestonaro 
58523bc3cafSzuoqilin 	/* detection works with revision greater or equal to 0x2b */
5862d5aee43SThilo Cestonaro 	val = i2c_smbus_read_byte_data(client, FTS_DEVICE_REVISION_REG);
5872d5aee43SThilo Cestonaro 	if (val < 0x2b)
5882d5aee43SThilo Cestonaro 		return -ENODEV;
5892d5aee43SThilo Cestonaro 
5902d5aee43SThilo Cestonaro 	/* Device Detect Regs must have 0x17 0x34 and 0x54 */
5912d5aee43SThilo Cestonaro 	val = i2c_smbus_read_byte_data(client, FTS_DEVICE_DETECT_REG_1);
5922d5aee43SThilo Cestonaro 	if (val != 0x17)
5932d5aee43SThilo Cestonaro 		return -ENODEV;
5942d5aee43SThilo Cestonaro 
5952d5aee43SThilo Cestonaro 	val = i2c_smbus_read_byte_data(client, FTS_DEVICE_DETECT_REG_2);
5962d5aee43SThilo Cestonaro 	if (val != 0x34)
5972d5aee43SThilo Cestonaro 		return -ENODEV;
5982d5aee43SThilo Cestonaro 
5992d5aee43SThilo Cestonaro 	val = i2c_smbus_read_byte_data(client, FTS_DEVICE_DETECT_REG_3);
6002d5aee43SThilo Cestonaro 	if (val != 0x54)
6012d5aee43SThilo Cestonaro 		return -ENODEV;
6022d5aee43SThilo Cestonaro 
6032d5aee43SThilo Cestonaro 	/*
6042d5aee43SThilo Cestonaro 	 * 0x10 == Baseboard Management Controller, 0x01 == Teutates
6052d5aee43SThilo Cestonaro 	 * Device ID Reg needs to be 0x11
6062d5aee43SThilo Cestonaro 	 */
6072d5aee43SThilo Cestonaro 	val = i2c_smbus_read_byte_data(client, FTS_DEVICE_ID_REG);
6082d5aee43SThilo Cestonaro 	if (val != 0x11)
6092d5aee43SThilo Cestonaro 		return -ENODEV;
6102d5aee43SThilo Cestonaro 
611f2f394dbSWolfram Sang 	strscpy(info->type, fts_id[0].name, I2C_NAME_SIZE);
6122d5aee43SThilo Cestonaro 	info->flags = 0;
6132d5aee43SThilo Cestonaro 	return 0;
6142d5aee43SThilo Cestonaro }
6152d5aee43SThilo Cestonaro 
61667487038SStephen Kitt static int fts_probe(struct i2c_client *client)
61708426edaSThilo Cestonaro {
61808426edaSThilo Cestonaro 	u8 revision;
61908426edaSThilo Cestonaro 	struct fts_data *data;
62008426edaSThilo Cestonaro 	int err;
62108426edaSThilo Cestonaro 	s8 deviceid;
62208426edaSThilo Cestonaro 	struct device *hwmon_dev;
62308426edaSThilo Cestonaro 
62408426edaSThilo Cestonaro 	if (client->addr != 0x73)
62508426edaSThilo Cestonaro 		return -ENODEV;
62608426edaSThilo Cestonaro 
62708426edaSThilo Cestonaro 	/* Baseboard Management Controller check */
62808426edaSThilo Cestonaro 	deviceid = i2c_smbus_read_byte_data(client, FTS_DEVICE_ID_REG);
62908426edaSThilo Cestonaro 	if (deviceid > 0 && (deviceid & 0xF0) == 0x10) {
63008426edaSThilo Cestonaro 		switch (deviceid & 0x0F) {
63108426edaSThilo Cestonaro 		case 0x01:
63208426edaSThilo Cestonaro 			break;
63308426edaSThilo Cestonaro 		default:
63408426edaSThilo Cestonaro 			dev_dbg(&client->dev,
63508426edaSThilo Cestonaro 				"No Baseboard Management Controller\n");
63608426edaSThilo Cestonaro 			return -ENODEV;
63708426edaSThilo Cestonaro 		}
63808426edaSThilo Cestonaro 	} else {
63908426edaSThilo Cestonaro 		dev_dbg(&client->dev, "No fujitsu board\n");
64008426edaSThilo Cestonaro 		return -ENODEV;
64108426edaSThilo Cestonaro 	}
64208426edaSThilo Cestonaro 
64308426edaSThilo Cestonaro 	data = devm_kzalloc(&client->dev, sizeof(struct fts_data),
64408426edaSThilo Cestonaro 			    GFP_KERNEL);
64508426edaSThilo Cestonaro 	if (!data)
64608426edaSThilo Cestonaro 		return -ENOMEM;
64708426edaSThilo Cestonaro 
64808426edaSThilo Cestonaro 	mutex_init(&data->update_lock);
64908426edaSThilo Cestonaro 	mutex_init(&data->access_lock);
65008426edaSThilo Cestonaro 	data->client = client;
65108426edaSThilo Cestonaro 	dev_set_drvdata(&client->dev, data);
65208426edaSThilo Cestonaro 
65308426edaSThilo Cestonaro 	err = i2c_smbus_read_byte_data(client, FTS_DEVICE_REVISION_REG);
65408426edaSThilo Cestonaro 	if (err < 0)
65508426edaSThilo Cestonaro 		return err;
65608426edaSThilo Cestonaro 	revision = err;
65708426edaSThilo Cestonaro 
658dea60ff0SArmin Wolf 	hwmon_dev = devm_hwmon_device_register_with_info(&client->dev, "ftsteutates", data,
6591c5759d8SArmin Wolf 							 &fts_chip_info, NULL);
66008426edaSThilo Cestonaro 	if (IS_ERR(hwmon_dev))
66108426edaSThilo Cestonaro 		return PTR_ERR(hwmon_dev);
66208426edaSThilo Cestonaro 
66308426edaSThilo Cestonaro 	err = fts_watchdog_init(data);
66408426edaSThilo Cestonaro 	if (err)
66508426edaSThilo Cestonaro 		return err;
66608426edaSThilo Cestonaro 
66708426edaSThilo Cestonaro 	dev_info(&client->dev, "Detected FTS Teutates chip, revision: %d.%d\n",
66808426edaSThilo Cestonaro 		 (revision & 0xF0) >> 4, revision & 0x0F);
66908426edaSThilo Cestonaro 	return 0;
67008426edaSThilo Cestonaro }
67108426edaSThilo Cestonaro 
67208426edaSThilo Cestonaro /*****************************************************************************/
67308426edaSThilo Cestonaro /* Module Details							     */
67408426edaSThilo Cestonaro /*****************************************************************************/
67508426edaSThilo Cestonaro static struct i2c_driver fts_driver = {
6762d5aee43SThilo Cestonaro 	.class = I2C_CLASS_HWMON,
67708426edaSThilo Cestonaro 	.driver = {
67808426edaSThilo Cestonaro 		.name = "ftsteutates",
67908426edaSThilo Cestonaro 	},
68008426edaSThilo Cestonaro 	.id_table = fts_id,
68167487038SStephen Kitt 	.probe_new = fts_probe,
6822d5aee43SThilo Cestonaro 	.detect = fts_detect,
6832d5aee43SThilo Cestonaro 	.address_list = normal_i2c,
68408426edaSThilo Cestonaro };
68508426edaSThilo Cestonaro 
68608426edaSThilo Cestonaro module_i2c_driver(fts_driver);
68708426edaSThilo Cestonaro 
68808426edaSThilo Cestonaro MODULE_AUTHOR("Thilo Cestonaro <thilo.cestonaro@ts.fujitsu.com>");
68908426edaSThilo Cestonaro MODULE_DESCRIPTION("FTS Teutates driver");
69008426edaSThilo Cestonaro MODULE_LICENSE("GPL");
691