xref: /openbmc/linux/drivers/hwmon/ftsteutates.c (revision dea60ff028befe061a7b4e1effd09bd7b14df9e0)
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/fs.h>
1008426edaSThilo Cestonaro #include <linux/hwmon.h>
1108426edaSThilo Cestonaro #include <linux/hwmon-sysfs.h>
1208426edaSThilo Cestonaro #include <linux/i2c.h>
1308426edaSThilo Cestonaro #include <linux/init.h>
1408426edaSThilo Cestonaro #include <linux/jiffies.h>
15ca8fd8c1SArmin Wolf #include <linux/math.h>
1608426edaSThilo Cestonaro #include <linux/module.h>
1708426edaSThilo Cestonaro #include <linux/mutex.h>
1808426edaSThilo Cestonaro #include <linux/slab.h>
1908426edaSThilo Cestonaro #include <linux/sysfs.h>
2008426edaSThilo Cestonaro #include <linux/watchdog.h>
2108426edaSThilo Cestonaro 
2208426edaSThilo Cestonaro #define FTS_DEVICE_ID_REG		0x0000
2308426edaSThilo Cestonaro #define FTS_DEVICE_REVISION_REG		0x0001
2408426edaSThilo Cestonaro #define FTS_DEVICE_STATUS_REG		0x0004
2508426edaSThilo Cestonaro #define FTS_SATELLITE_STATUS_REG	0x0005
2608426edaSThilo Cestonaro #define FTS_EVENT_STATUS_REG		0x0006
2708426edaSThilo Cestonaro #define FTS_GLOBAL_CONTROL_REG		0x0007
2808426edaSThilo Cestonaro 
292d5aee43SThilo Cestonaro #define FTS_DEVICE_DETECT_REG_1		0x0C
302d5aee43SThilo Cestonaro #define FTS_DEVICE_DETECT_REG_2		0x0D
312d5aee43SThilo Cestonaro #define FTS_DEVICE_DETECT_REG_3		0x0E
322d5aee43SThilo Cestonaro 
3308426edaSThilo Cestonaro #define FTS_SENSOR_EVENT_REG		0x0010
3408426edaSThilo Cestonaro 
3508426edaSThilo Cestonaro #define FTS_FAN_EVENT_REG		0x0014
3608426edaSThilo Cestonaro #define FTS_FAN_PRESENT_REG		0x0015
3708426edaSThilo Cestonaro 
3808426edaSThilo Cestonaro #define FTS_POWER_ON_TIME_COUNTER_A	0x007A
3908426edaSThilo Cestonaro #define FTS_POWER_ON_TIME_COUNTER_B	0x007B
4008426edaSThilo Cestonaro #define FTS_POWER_ON_TIME_COUNTER_C	0x007C
4108426edaSThilo Cestonaro 
4208426edaSThilo Cestonaro #define FTS_PAGE_SELECT_REG		0x007F
4308426edaSThilo Cestonaro 
4408426edaSThilo Cestonaro #define FTS_WATCHDOG_TIME_PRESET	0x000B
4508426edaSThilo Cestonaro #define FTS_WATCHDOG_CONTROL		0x5081
4608426edaSThilo Cestonaro 
4708426edaSThilo Cestonaro #define FTS_NO_FAN_SENSORS		0x08
4808426edaSThilo Cestonaro #define FTS_NO_TEMP_SENSORS		0x10
4908426edaSThilo Cestonaro #define FTS_NO_VOLT_SENSORS		0x04
5008426edaSThilo Cestonaro 
512d5aee43SThilo Cestonaro static const unsigned short normal_i2c[] = { 0x73, I2C_CLIENT_END };
522d5aee43SThilo Cestonaro 
531dad2e95SArvind Yadav static const struct i2c_device_id fts_id[] = {
5408426edaSThilo Cestonaro 	{ "ftsteutates", 0 },
5508426edaSThilo Cestonaro 	{ }
5608426edaSThilo Cestonaro };
5708426edaSThilo Cestonaro MODULE_DEVICE_TABLE(i2c, fts_id);
5808426edaSThilo Cestonaro 
5908426edaSThilo Cestonaro enum WATCHDOG_RESOLUTION {
6008426edaSThilo Cestonaro 	seconds = 1,
6108426edaSThilo Cestonaro 	minutes = 60
6208426edaSThilo Cestonaro };
6308426edaSThilo Cestonaro 
6408426edaSThilo Cestonaro struct fts_data {
6508426edaSThilo Cestonaro 	struct i2c_client *client;
6608426edaSThilo Cestonaro 	/* update sensor data lock */
6708426edaSThilo Cestonaro 	struct mutex update_lock;
6808426edaSThilo Cestonaro 	/* read/write register lock */
6908426edaSThilo Cestonaro 	struct mutex access_lock;
7008426edaSThilo Cestonaro 	unsigned long last_updated; /* in jiffies */
7108426edaSThilo Cestonaro 	struct watchdog_device wdd;
7208426edaSThilo Cestonaro 	enum WATCHDOG_RESOLUTION resolution;
7308426edaSThilo Cestonaro 	bool valid; /* false until following fields are valid */
7408426edaSThilo Cestonaro 
7508426edaSThilo Cestonaro 	u8 volt[FTS_NO_VOLT_SENSORS];
7608426edaSThilo Cestonaro 
7708426edaSThilo Cestonaro 	u8 temp_input[FTS_NO_TEMP_SENSORS];
7808426edaSThilo Cestonaro 	u8 temp_alarm;
7908426edaSThilo Cestonaro 
8008426edaSThilo Cestonaro 	u8 fan_present;
8108426edaSThilo Cestonaro 	u8 fan_input[FTS_NO_FAN_SENSORS]; /* in rps */
8208426edaSThilo Cestonaro 	u8 fan_source[FTS_NO_FAN_SENSORS];
8308426edaSThilo Cestonaro 	u8 fan_alarm;
8408426edaSThilo Cestonaro };
8508426edaSThilo Cestonaro 
8608426edaSThilo Cestonaro #define FTS_REG_FAN_INPUT(idx) ((idx) + 0x20)
8708426edaSThilo Cestonaro #define FTS_REG_FAN_SOURCE(idx) ((idx) + 0x30)
8808426edaSThilo Cestonaro #define FTS_REG_FAN_CONTROL(idx) (((idx) << 16) + 0x4881)
8908426edaSThilo Cestonaro 
9008426edaSThilo Cestonaro #define FTS_REG_TEMP_INPUT(idx) ((idx) + 0x40)
9108426edaSThilo Cestonaro #define FTS_REG_TEMP_CONTROL(idx) (((idx) << 16) + 0x0681)
9208426edaSThilo Cestonaro 
9308426edaSThilo Cestonaro #define FTS_REG_VOLT(idx) ((idx) + 0x18)
9408426edaSThilo Cestonaro 
9508426edaSThilo Cestonaro /*****************************************************************************/
9608426edaSThilo Cestonaro /* I2C Helper functions							     */
9708426edaSThilo Cestonaro /*****************************************************************************/
9808426edaSThilo Cestonaro static int fts_read_byte(struct i2c_client *client, unsigned short reg)
9908426edaSThilo Cestonaro {
10008426edaSThilo Cestonaro 	int ret;
10108426edaSThilo Cestonaro 	unsigned char page = reg >> 8;
10208426edaSThilo Cestonaro 	struct fts_data *data = dev_get_drvdata(&client->dev);
10308426edaSThilo Cestonaro 
10408426edaSThilo Cestonaro 	mutex_lock(&data->access_lock);
10508426edaSThilo Cestonaro 
10608426edaSThilo Cestonaro 	dev_dbg(&client->dev, "page select - page: 0x%.02x\n", page);
10708426edaSThilo Cestonaro 	ret = i2c_smbus_write_byte_data(client, FTS_PAGE_SELECT_REG, page);
10808426edaSThilo Cestonaro 	if (ret < 0)
10908426edaSThilo Cestonaro 		goto error;
11008426edaSThilo Cestonaro 
11108426edaSThilo Cestonaro 	reg &= 0xFF;
11208426edaSThilo Cestonaro 	ret = i2c_smbus_read_byte_data(client, reg);
11308426edaSThilo Cestonaro 	dev_dbg(&client->dev, "read - reg: 0x%.02x: val: 0x%.02x\n", reg, ret);
11408426edaSThilo Cestonaro 
11508426edaSThilo Cestonaro error:
11608426edaSThilo Cestonaro 	mutex_unlock(&data->access_lock);
11708426edaSThilo Cestonaro 	return ret;
11808426edaSThilo Cestonaro }
11908426edaSThilo Cestonaro 
12008426edaSThilo Cestonaro static int fts_write_byte(struct i2c_client *client, unsigned short reg,
12108426edaSThilo Cestonaro 			  unsigned char value)
12208426edaSThilo Cestonaro {
12308426edaSThilo Cestonaro 	int ret;
12408426edaSThilo Cestonaro 	unsigned char page = reg >> 8;
12508426edaSThilo Cestonaro 	struct fts_data *data = dev_get_drvdata(&client->dev);
12608426edaSThilo Cestonaro 
12708426edaSThilo Cestonaro 	mutex_lock(&data->access_lock);
12808426edaSThilo Cestonaro 
12908426edaSThilo Cestonaro 	dev_dbg(&client->dev, "page select - page: 0x%.02x\n", page);
13008426edaSThilo Cestonaro 	ret = i2c_smbus_write_byte_data(client, FTS_PAGE_SELECT_REG, page);
13108426edaSThilo Cestonaro 	if (ret < 0)
13208426edaSThilo Cestonaro 		goto error;
13308426edaSThilo Cestonaro 
13408426edaSThilo Cestonaro 	reg &= 0xFF;
13508426edaSThilo Cestonaro 	dev_dbg(&client->dev,
13608426edaSThilo Cestonaro 		"write - reg: 0x%.02x: val: 0x%.02x\n", reg, value);
13708426edaSThilo Cestonaro 	ret = i2c_smbus_write_byte_data(client, reg, value);
13808426edaSThilo Cestonaro 
13908426edaSThilo Cestonaro error:
14008426edaSThilo Cestonaro 	mutex_unlock(&data->access_lock);
14108426edaSThilo Cestonaro 	return ret;
14208426edaSThilo Cestonaro }
14308426edaSThilo Cestonaro 
14408426edaSThilo Cestonaro /*****************************************************************************/
14508426edaSThilo Cestonaro /* Data Updater Helper function						     */
14608426edaSThilo Cestonaro /*****************************************************************************/
14708426edaSThilo Cestonaro static int fts_update_device(struct fts_data *data)
14808426edaSThilo Cestonaro {
14908426edaSThilo Cestonaro 	int i;
15008426edaSThilo Cestonaro 	int err = 0;
15108426edaSThilo Cestonaro 
15208426edaSThilo Cestonaro 	mutex_lock(&data->update_lock);
15308426edaSThilo Cestonaro 	if (!time_after(jiffies, data->last_updated + 2 * HZ) && data->valid)
15408426edaSThilo Cestonaro 		goto exit;
15508426edaSThilo Cestonaro 
15608426edaSThilo Cestonaro 	err = fts_read_byte(data->client, FTS_DEVICE_STATUS_REG);
15708426edaSThilo Cestonaro 	if (err < 0)
15808426edaSThilo Cestonaro 		goto exit;
15908426edaSThilo Cestonaro 
16008426edaSThilo Cestonaro 	data->valid = !!(err & 0x02); /* Data not ready yet */
16108426edaSThilo Cestonaro 	if (unlikely(!data->valid)) {
16208426edaSThilo Cestonaro 		err = -EAGAIN;
16308426edaSThilo Cestonaro 		goto exit;
16408426edaSThilo Cestonaro 	}
16508426edaSThilo Cestonaro 
16608426edaSThilo Cestonaro 	err = fts_read_byte(data->client, FTS_FAN_PRESENT_REG);
16708426edaSThilo Cestonaro 	if (err < 0)
16808426edaSThilo Cestonaro 		goto exit;
16908426edaSThilo Cestonaro 	data->fan_present = err;
17008426edaSThilo Cestonaro 
17108426edaSThilo Cestonaro 	err = fts_read_byte(data->client, FTS_FAN_EVENT_REG);
17208426edaSThilo Cestonaro 	if (err < 0)
17308426edaSThilo Cestonaro 		goto exit;
17408426edaSThilo Cestonaro 	data->fan_alarm = err;
17508426edaSThilo Cestonaro 
17608426edaSThilo Cestonaro 	for (i = 0; i < FTS_NO_FAN_SENSORS; i++) {
17708426edaSThilo Cestonaro 		if (data->fan_present & BIT(i)) {
17808426edaSThilo Cestonaro 			err = fts_read_byte(data->client, FTS_REG_FAN_INPUT(i));
17908426edaSThilo Cestonaro 			if (err < 0)
18008426edaSThilo Cestonaro 				goto exit;
18108426edaSThilo Cestonaro 			data->fan_input[i] = err;
18208426edaSThilo Cestonaro 
18308426edaSThilo Cestonaro 			err = fts_read_byte(data->client,
18408426edaSThilo Cestonaro 					    FTS_REG_FAN_SOURCE(i));
18508426edaSThilo Cestonaro 			if (err < 0)
18608426edaSThilo Cestonaro 				goto exit;
18708426edaSThilo Cestonaro 			data->fan_source[i] = err;
18808426edaSThilo Cestonaro 		} else {
18908426edaSThilo Cestonaro 			data->fan_input[i] = 0;
19008426edaSThilo Cestonaro 			data->fan_source[i] = 0;
19108426edaSThilo Cestonaro 		}
19208426edaSThilo Cestonaro 	}
19308426edaSThilo Cestonaro 
19408426edaSThilo Cestonaro 	err = fts_read_byte(data->client, FTS_SENSOR_EVENT_REG);
19508426edaSThilo Cestonaro 	if (err < 0)
19608426edaSThilo Cestonaro 		goto exit;
19708426edaSThilo Cestonaro 	data->temp_alarm = err;
19808426edaSThilo Cestonaro 
19908426edaSThilo Cestonaro 	for (i = 0; i < FTS_NO_TEMP_SENSORS; i++) {
20008426edaSThilo Cestonaro 		err = fts_read_byte(data->client, FTS_REG_TEMP_INPUT(i));
20108426edaSThilo Cestonaro 		if (err < 0)
20208426edaSThilo Cestonaro 			goto exit;
20308426edaSThilo Cestonaro 		data->temp_input[i] = err;
20408426edaSThilo Cestonaro 	}
20508426edaSThilo Cestonaro 
20608426edaSThilo Cestonaro 	for (i = 0; i < FTS_NO_VOLT_SENSORS; i++) {
20708426edaSThilo Cestonaro 		err = fts_read_byte(data->client, FTS_REG_VOLT(i));
20808426edaSThilo Cestonaro 		if (err < 0)
20908426edaSThilo Cestonaro 			goto exit;
21008426edaSThilo Cestonaro 		data->volt[i] = err;
21108426edaSThilo Cestonaro 	}
21208426edaSThilo Cestonaro 	data->last_updated = jiffies;
21308426edaSThilo Cestonaro 	err = 0;
21408426edaSThilo Cestonaro exit:
21508426edaSThilo Cestonaro 	mutex_unlock(&data->update_lock);
21608426edaSThilo Cestonaro 	return err;
21708426edaSThilo Cestonaro }
21808426edaSThilo Cestonaro 
21908426edaSThilo Cestonaro /*****************************************************************************/
22008426edaSThilo Cestonaro /* Watchdog functions							     */
22108426edaSThilo Cestonaro /*****************************************************************************/
22208426edaSThilo Cestonaro static int fts_wd_set_resolution(struct fts_data *data,
22308426edaSThilo Cestonaro 				 enum WATCHDOG_RESOLUTION resolution)
22408426edaSThilo Cestonaro {
22508426edaSThilo Cestonaro 	int ret;
22608426edaSThilo Cestonaro 
22708426edaSThilo Cestonaro 	if (data->resolution == resolution)
22808426edaSThilo Cestonaro 		return 0;
22908426edaSThilo Cestonaro 
23008426edaSThilo Cestonaro 	ret = fts_read_byte(data->client, FTS_WATCHDOG_CONTROL);
23108426edaSThilo Cestonaro 	if (ret < 0)
23208426edaSThilo Cestonaro 		return ret;
23308426edaSThilo Cestonaro 
23408426edaSThilo Cestonaro 	if ((resolution == seconds && ret & BIT(1)) ||
23508426edaSThilo Cestonaro 	    (resolution == minutes && (ret & BIT(1)) == 0)) {
23608426edaSThilo Cestonaro 		data->resolution = resolution;
23708426edaSThilo Cestonaro 		return 0;
23808426edaSThilo Cestonaro 	}
23908426edaSThilo Cestonaro 
24008426edaSThilo Cestonaro 	if (resolution == seconds)
2414c8702b3SGuenter Roeck 		ret |= BIT(1);
24208426edaSThilo Cestonaro 	else
24308426edaSThilo Cestonaro 		ret &= ~BIT(1);
24408426edaSThilo Cestonaro 
24508426edaSThilo Cestonaro 	ret = fts_write_byte(data->client, FTS_WATCHDOG_CONTROL, ret);
24608426edaSThilo Cestonaro 	if (ret < 0)
24708426edaSThilo Cestonaro 		return ret;
24808426edaSThilo Cestonaro 
24908426edaSThilo Cestonaro 	data->resolution = resolution;
25008426edaSThilo Cestonaro 	return ret;
25108426edaSThilo Cestonaro }
25208426edaSThilo Cestonaro 
25308426edaSThilo Cestonaro static int fts_wd_set_timeout(struct watchdog_device *wdd, unsigned int timeout)
25408426edaSThilo Cestonaro {
25508426edaSThilo Cestonaro 	struct fts_data *data;
25608426edaSThilo Cestonaro 	enum WATCHDOG_RESOLUTION resolution = seconds;
25708426edaSThilo Cestonaro 	int ret;
25808426edaSThilo Cestonaro 
25908426edaSThilo Cestonaro 	data = watchdog_get_drvdata(wdd);
26008426edaSThilo Cestonaro 	/* switch watchdog resolution to minutes if timeout does not fit
26108426edaSThilo Cestonaro 	 * into a byte
26208426edaSThilo Cestonaro 	 */
26308426edaSThilo Cestonaro 	if (timeout > 0xFF) {
26408426edaSThilo Cestonaro 		timeout = DIV_ROUND_UP(timeout, 60) * 60;
26508426edaSThilo Cestonaro 		resolution = minutes;
26608426edaSThilo Cestonaro 	}
26708426edaSThilo Cestonaro 
26808426edaSThilo Cestonaro 	ret = fts_wd_set_resolution(data, resolution);
26908426edaSThilo Cestonaro 	if (ret < 0)
27008426edaSThilo Cestonaro 		return ret;
27108426edaSThilo Cestonaro 
27208426edaSThilo Cestonaro 	wdd->timeout = timeout;
27308426edaSThilo Cestonaro 	return 0;
27408426edaSThilo Cestonaro }
27508426edaSThilo Cestonaro 
27608426edaSThilo Cestonaro static int fts_wd_start(struct watchdog_device *wdd)
27708426edaSThilo Cestonaro {
27808426edaSThilo Cestonaro 	struct fts_data *data = watchdog_get_drvdata(wdd);
27908426edaSThilo Cestonaro 
28008426edaSThilo Cestonaro 	return fts_write_byte(data->client, FTS_WATCHDOG_TIME_PRESET,
28108426edaSThilo Cestonaro 			      wdd->timeout / (u8)data->resolution);
28208426edaSThilo Cestonaro }
28308426edaSThilo Cestonaro 
28408426edaSThilo Cestonaro static int fts_wd_stop(struct watchdog_device *wdd)
28508426edaSThilo Cestonaro {
28608426edaSThilo Cestonaro 	struct fts_data *data;
28708426edaSThilo Cestonaro 
28808426edaSThilo Cestonaro 	data = watchdog_get_drvdata(wdd);
28908426edaSThilo Cestonaro 	return fts_write_byte(data->client, FTS_WATCHDOG_TIME_PRESET, 0);
29008426edaSThilo Cestonaro }
29108426edaSThilo Cestonaro 
29208426edaSThilo Cestonaro static const struct watchdog_info fts_wd_info = {
29308426edaSThilo Cestonaro 	.options = WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING | WDIOF_MAGICCLOSE,
29408426edaSThilo Cestonaro 	.identity = "FTS Teutates Hardware Watchdog",
29508426edaSThilo Cestonaro };
29608426edaSThilo Cestonaro 
29708426edaSThilo Cestonaro static const struct watchdog_ops fts_wd_ops = {
29808426edaSThilo Cestonaro 	.owner = THIS_MODULE,
29908426edaSThilo Cestonaro 	.start = fts_wd_start,
30008426edaSThilo Cestonaro 	.stop = fts_wd_stop,
30108426edaSThilo Cestonaro 	.set_timeout = fts_wd_set_timeout,
30208426edaSThilo Cestonaro };
30308426edaSThilo Cestonaro 
30408426edaSThilo Cestonaro static int fts_watchdog_init(struct fts_data *data)
30508426edaSThilo Cestonaro {
30608426edaSThilo Cestonaro 	int timeout, ret;
30708426edaSThilo Cestonaro 
30808426edaSThilo Cestonaro 	watchdog_set_drvdata(&data->wdd, data);
30908426edaSThilo Cestonaro 
31008426edaSThilo Cestonaro 	timeout = fts_read_byte(data->client, FTS_WATCHDOG_TIME_PRESET);
31108426edaSThilo Cestonaro 	if (timeout < 0)
31208426edaSThilo Cestonaro 		return timeout;
31308426edaSThilo Cestonaro 
31408426edaSThilo Cestonaro 	/* watchdog not running, set timeout to a default of 60 sec. */
31508426edaSThilo Cestonaro 	if (timeout == 0) {
31608426edaSThilo Cestonaro 		ret = fts_wd_set_resolution(data, seconds);
31708426edaSThilo Cestonaro 		if (ret < 0)
31808426edaSThilo Cestonaro 			return ret;
31908426edaSThilo Cestonaro 		data->wdd.timeout = 60;
32008426edaSThilo Cestonaro 	} else {
32108426edaSThilo Cestonaro 		ret = fts_read_byte(data->client, FTS_WATCHDOG_CONTROL);
32208426edaSThilo Cestonaro 		if (ret < 0)
32308426edaSThilo Cestonaro 			return ret;
32408426edaSThilo Cestonaro 
32508426edaSThilo Cestonaro 		data->resolution = ret & BIT(1) ? seconds : minutes;
32608426edaSThilo Cestonaro 		data->wdd.timeout = timeout * (u8)data->resolution;
32708426edaSThilo Cestonaro 		set_bit(WDOG_HW_RUNNING, &data->wdd.status);
32808426edaSThilo Cestonaro 	}
32908426edaSThilo Cestonaro 
33008426edaSThilo Cestonaro 	/* Register our watchdog part */
33108426edaSThilo Cestonaro 	data->wdd.info = &fts_wd_info;
33208426edaSThilo Cestonaro 	data->wdd.ops = &fts_wd_ops;
33308426edaSThilo Cestonaro 	data->wdd.parent = &data->client->dev;
33408426edaSThilo Cestonaro 	data->wdd.min_timeout = 1;
33508426edaSThilo Cestonaro 
33608426edaSThilo Cestonaro 	/* max timeout 255 minutes. */
33708426edaSThilo Cestonaro 	data->wdd.max_hw_heartbeat_ms = 0xFF * 60 * MSEC_PER_SEC;
33808426edaSThilo Cestonaro 
339e1515a74SArmin Wolf 	return devm_watchdog_register_device(&data->client->dev, &data->wdd);
34008426edaSThilo Cestonaro }
34108426edaSThilo Cestonaro 
342626f5eaeSGuenter Roeck static ssize_t fan_source_show(struct device *dev,
34308426edaSThilo Cestonaro 			       struct device_attribute *devattr, char *buf)
34408426edaSThilo Cestonaro {
34508426edaSThilo Cestonaro 	struct fts_data *data = dev_get_drvdata(dev);
34608426edaSThilo Cestonaro 	int index = to_sensor_dev_attr(devattr)->index;
34708426edaSThilo Cestonaro 	int err;
34808426edaSThilo Cestonaro 
34908426edaSThilo Cestonaro 	err = fts_update_device(data);
35008426edaSThilo Cestonaro 	if (err < 0)
35108426edaSThilo Cestonaro 		return err;
35208426edaSThilo Cestonaro 
35308426edaSThilo Cestonaro 	return sprintf(buf, "%u\n", data->fan_source[index]);
35408426edaSThilo Cestonaro }
35508426edaSThilo Cestonaro 
356626f5eaeSGuenter Roeck static SENSOR_DEVICE_ATTR_RO(fan1_source, fan_source, 0);
357626f5eaeSGuenter Roeck static SENSOR_DEVICE_ATTR_RO(fan2_source, fan_source, 1);
358626f5eaeSGuenter Roeck static SENSOR_DEVICE_ATTR_RO(fan3_source, fan_source, 2);
359626f5eaeSGuenter Roeck static SENSOR_DEVICE_ATTR_RO(fan4_source, fan_source, 3);
360626f5eaeSGuenter Roeck static SENSOR_DEVICE_ATTR_RO(fan5_source, fan_source, 4);
361626f5eaeSGuenter Roeck static SENSOR_DEVICE_ATTR_RO(fan6_source, fan_source, 5);
362626f5eaeSGuenter Roeck static SENSOR_DEVICE_ATTR_RO(fan7_source, fan_source, 6);
363626f5eaeSGuenter Roeck static SENSOR_DEVICE_ATTR_RO(fan8_source, fan_source, 7);
36408426edaSThilo Cestonaro 
36508426edaSThilo Cestonaro static struct attribute *fts_fan_attrs[] = {
36608426edaSThilo Cestonaro 	&sensor_dev_attr_fan1_source.dev_attr.attr,
36708426edaSThilo Cestonaro 	&sensor_dev_attr_fan2_source.dev_attr.attr,
36808426edaSThilo Cestonaro 	&sensor_dev_attr_fan3_source.dev_attr.attr,
36908426edaSThilo Cestonaro 	&sensor_dev_attr_fan4_source.dev_attr.attr,
37008426edaSThilo Cestonaro 	&sensor_dev_attr_fan5_source.dev_attr.attr,
37108426edaSThilo Cestonaro 	&sensor_dev_attr_fan6_source.dev_attr.attr,
37208426edaSThilo Cestonaro 	&sensor_dev_attr_fan7_source.dev_attr.attr,
37308426edaSThilo Cestonaro 	&sensor_dev_attr_fan8_source.dev_attr.attr,
37408426edaSThilo Cestonaro 	NULL
37508426edaSThilo Cestonaro };
37608426edaSThilo Cestonaro 
377*dea60ff0SArmin Wolf static const struct attribute_group fts_attr_group = {
37808426edaSThilo Cestonaro 	.attrs = fts_fan_attrs
37908426edaSThilo Cestonaro };
38008426edaSThilo Cestonaro 
38108426edaSThilo Cestonaro static const struct attribute_group *fts_attr_groups[] = {
382*dea60ff0SArmin Wolf 	&fts_attr_group,
38308426edaSThilo Cestonaro 	NULL
38408426edaSThilo Cestonaro };
38508426edaSThilo Cestonaro 
386*dea60ff0SArmin Wolf static umode_t fts_is_visible(const void *devdata, enum hwmon_sensor_types type, u32 attr,
387*dea60ff0SArmin Wolf 			      int channel)
388*dea60ff0SArmin Wolf {
389*dea60ff0SArmin Wolf 	switch (type) {
390*dea60ff0SArmin Wolf 	case hwmon_temp:
391*dea60ff0SArmin Wolf 		switch (attr) {
392*dea60ff0SArmin Wolf 		case hwmon_temp_input:
393*dea60ff0SArmin Wolf 		case hwmon_temp_fault:
394*dea60ff0SArmin Wolf 			return 0444;
395*dea60ff0SArmin Wolf 		case hwmon_temp_alarm:
396*dea60ff0SArmin Wolf 			return 0644;
397*dea60ff0SArmin Wolf 		default:
398*dea60ff0SArmin Wolf 			break;
399*dea60ff0SArmin Wolf 		}
400*dea60ff0SArmin Wolf 		break;
401*dea60ff0SArmin Wolf 	case hwmon_fan:
402*dea60ff0SArmin Wolf 		switch (attr) {
403*dea60ff0SArmin Wolf 		case hwmon_fan_input:
404*dea60ff0SArmin Wolf 			return 0444;
405*dea60ff0SArmin Wolf 		case hwmon_fan_alarm:
406*dea60ff0SArmin Wolf 			return 0644;
407*dea60ff0SArmin Wolf 		default:
408*dea60ff0SArmin Wolf 			break;
409*dea60ff0SArmin Wolf 		}
410*dea60ff0SArmin Wolf 		break;
411*dea60ff0SArmin Wolf 	case hwmon_in:
412*dea60ff0SArmin Wolf 		return 0444;
413*dea60ff0SArmin Wolf 	default:
414*dea60ff0SArmin Wolf 		break;
415*dea60ff0SArmin Wolf 	}
416*dea60ff0SArmin Wolf 
417*dea60ff0SArmin Wolf 	return 0;
418*dea60ff0SArmin Wolf }
419*dea60ff0SArmin Wolf 
420*dea60ff0SArmin Wolf static int fts_read(struct device *dev, enum hwmon_sensor_types type, u32 attr, int channel,
421*dea60ff0SArmin Wolf 		    long *val)
422*dea60ff0SArmin Wolf {
423*dea60ff0SArmin Wolf 	struct fts_data *data = dev_get_drvdata(dev);
424*dea60ff0SArmin Wolf 	int ret = fts_update_device(data);
425*dea60ff0SArmin Wolf 
426*dea60ff0SArmin Wolf 	if (ret < 0)
427*dea60ff0SArmin Wolf 		return ret;
428*dea60ff0SArmin Wolf 
429*dea60ff0SArmin Wolf 	switch (type) {
430*dea60ff0SArmin Wolf 	case hwmon_temp:
431*dea60ff0SArmin Wolf 		switch (attr) {
432*dea60ff0SArmin Wolf 		case hwmon_temp_input:
433*dea60ff0SArmin Wolf 			*val = (data->temp_input[channel] - 64) * 1000;
434*dea60ff0SArmin Wolf 
435*dea60ff0SArmin Wolf 			return 0;
436*dea60ff0SArmin Wolf 		case hwmon_temp_alarm:
437*dea60ff0SArmin Wolf 			*val = !!(data->temp_alarm & BIT(channel));
438*dea60ff0SArmin Wolf 
439*dea60ff0SArmin Wolf 			return 0;
440*dea60ff0SArmin Wolf 		case hwmon_temp_fault:
441*dea60ff0SArmin Wolf 			/* 00h Temperature = Sensor Error */;
442*dea60ff0SArmin Wolf 			*val = (data->temp_input[channel] == 0);
443*dea60ff0SArmin Wolf 
444*dea60ff0SArmin Wolf 			return 0;
445*dea60ff0SArmin Wolf 		default:
446*dea60ff0SArmin Wolf 			break;
447*dea60ff0SArmin Wolf 		}
448*dea60ff0SArmin Wolf 		break;
449*dea60ff0SArmin Wolf 	case hwmon_fan:
450*dea60ff0SArmin Wolf 		switch (attr) {
451*dea60ff0SArmin Wolf 		case hwmon_fan_input:
452*dea60ff0SArmin Wolf 			*val = data->fan_input[channel] * 60;
453*dea60ff0SArmin Wolf 
454*dea60ff0SArmin Wolf 			return 0;
455*dea60ff0SArmin Wolf 		case hwmon_fan_alarm:
456*dea60ff0SArmin Wolf 			*val = !!(data->fan_alarm & BIT(channel));
457*dea60ff0SArmin Wolf 
458*dea60ff0SArmin Wolf 			return 0;
459*dea60ff0SArmin Wolf 		default:
460*dea60ff0SArmin Wolf 			break;
461*dea60ff0SArmin Wolf 		}
462*dea60ff0SArmin Wolf 		break;
463*dea60ff0SArmin Wolf 	case hwmon_in:
464*dea60ff0SArmin Wolf 		switch (attr) {
465*dea60ff0SArmin Wolf 		case hwmon_in_input:
466*dea60ff0SArmin Wolf 			*val = DIV_ROUND_CLOSEST(data->volt[channel] * 3300, 255);
467*dea60ff0SArmin Wolf 
468*dea60ff0SArmin Wolf 			return 0;
469*dea60ff0SArmin Wolf 		default:
470*dea60ff0SArmin Wolf 			break;
471*dea60ff0SArmin Wolf 		}
472*dea60ff0SArmin Wolf 		break;
473*dea60ff0SArmin Wolf 	default:
474*dea60ff0SArmin Wolf 		break;
475*dea60ff0SArmin Wolf 	}
476*dea60ff0SArmin Wolf 
477*dea60ff0SArmin Wolf 	return -EOPNOTSUPP;
478*dea60ff0SArmin Wolf }
479*dea60ff0SArmin Wolf 
480*dea60ff0SArmin Wolf static int fts_write(struct device *dev, enum hwmon_sensor_types type, u32 attr, int channel,
481*dea60ff0SArmin Wolf 		     long val)
482*dea60ff0SArmin Wolf {
483*dea60ff0SArmin Wolf 	struct fts_data *data = dev_get_drvdata(dev);
484*dea60ff0SArmin Wolf 	int ret = fts_update_device(data);
485*dea60ff0SArmin Wolf 
486*dea60ff0SArmin Wolf 	if (ret < 0)
487*dea60ff0SArmin Wolf 		return ret;
488*dea60ff0SArmin Wolf 
489*dea60ff0SArmin Wolf 	switch (type) {
490*dea60ff0SArmin Wolf 	case hwmon_temp:
491*dea60ff0SArmin Wolf 		switch (attr) {
492*dea60ff0SArmin Wolf 		case hwmon_temp_alarm:
493*dea60ff0SArmin Wolf 			if (val)
494*dea60ff0SArmin Wolf 				return -EINVAL;
495*dea60ff0SArmin Wolf 
496*dea60ff0SArmin Wolf 			mutex_lock(&data->update_lock);
497*dea60ff0SArmin Wolf 			ret = fts_read_byte(data->client, FTS_REG_TEMP_CONTROL(channel));
498*dea60ff0SArmin Wolf 			if (ret >= 0)
499*dea60ff0SArmin Wolf 				ret = fts_write_byte(data->client, FTS_REG_TEMP_CONTROL(channel),
500*dea60ff0SArmin Wolf 						     ret | 0x1);
501*dea60ff0SArmin Wolf 			if (ret >= 0)
502*dea60ff0SArmin Wolf 				data->valid = false;
503*dea60ff0SArmin Wolf 
504*dea60ff0SArmin Wolf 			mutex_unlock(&data->update_lock);
505*dea60ff0SArmin Wolf 			if (ret < 0)
506*dea60ff0SArmin Wolf 				return ret;
507*dea60ff0SArmin Wolf 
508*dea60ff0SArmin Wolf 			return 0;
509*dea60ff0SArmin Wolf 		default:
510*dea60ff0SArmin Wolf 			break;
511*dea60ff0SArmin Wolf 		}
512*dea60ff0SArmin Wolf 		break;
513*dea60ff0SArmin Wolf 	case hwmon_fan:
514*dea60ff0SArmin Wolf 		switch (attr) {
515*dea60ff0SArmin Wolf 		case hwmon_fan_alarm:
516*dea60ff0SArmin Wolf 			if (val)
517*dea60ff0SArmin Wolf 				return -EINVAL;
518*dea60ff0SArmin Wolf 
519*dea60ff0SArmin Wolf 			mutex_lock(&data->update_lock);
520*dea60ff0SArmin Wolf 			ret = fts_read_byte(data->client, FTS_REG_FAN_CONTROL(channel));
521*dea60ff0SArmin Wolf 			if (ret >= 0)
522*dea60ff0SArmin Wolf 				ret = fts_write_byte(data->client, FTS_REG_FAN_CONTROL(channel),
523*dea60ff0SArmin Wolf 						     ret | 0x1);
524*dea60ff0SArmin Wolf 			if (ret >= 0)
525*dea60ff0SArmin Wolf 				data->valid = false;
526*dea60ff0SArmin Wolf 
527*dea60ff0SArmin Wolf 			mutex_unlock(&data->update_lock);
528*dea60ff0SArmin Wolf 			if (ret < 0)
529*dea60ff0SArmin Wolf 				return ret;
530*dea60ff0SArmin Wolf 
531*dea60ff0SArmin Wolf 			return 0;
532*dea60ff0SArmin Wolf 		default:
533*dea60ff0SArmin Wolf 			break;
534*dea60ff0SArmin Wolf 		}
535*dea60ff0SArmin Wolf 		break;
536*dea60ff0SArmin Wolf 	default:
537*dea60ff0SArmin Wolf 		break;
538*dea60ff0SArmin Wolf 	}
539*dea60ff0SArmin Wolf 
540*dea60ff0SArmin Wolf 	return -EOPNOTSUPP;
541*dea60ff0SArmin Wolf }
542*dea60ff0SArmin Wolf 
543*dea60ff0SArmin Wolf static const struct hwmon_ops fts_ops = {
544*dea60ff0SArmin Wolf 	.is_visible = fts_is_visible,
545*dea60ff0SArmin Wolf 	.read = fts_read,
546*dea60ff0SArmin Wolf 	.write = fts_write,
547*dea60ff0SArmin Wolf };
548*dea60ff0SArmin Wolf 
549*dea60ff0SArmin Wolf static const struct hwmon_channel_info *fts_info[] = {
550*dea60ff0SArmin Wolf 	HWMON_CHANNEL_INFO(chip, HWMON_C_REGISTER_TZ),
551*dea60ff0SArmin Wolf 	HWMON_CHANNEL_INFO(temp,
552*dea60ff0SArmin Wolf 			   HWMON_T_INPUT | HWMON_T_ALARM | HWMON_T_FAULT,
553*dea60ff0SArmin Wolf 			   HWMON_T_INPUT | HWMON_T_ALARM | HWMON_T_FAULT,
554*dea60ff0SArmin Wolf 			   HWMON_T_INPUT | HWMON_T_ALARM | HWMON_T_FAULT,
555*dea60ff0SArmin Wolf 			   HWMON_T_INPUT | HWMON_T_ALARM | HWMON_T_FAULT,
556*dea60ff0SArmin Wolf 			   HWMON_T_INPUT | HWMON_T_ALARM | HWMON_T_FAULT,
557*dea60ff0SArmin Wolf 			   HWMON_T_INPUT | HWMON_T_ALARM | HWMON_T_FAULT,
558*dea60ff0SArmin Wolf 			   HWMON_T_INPUT | HWMON_T_ALARM | HWMON_T_FAULT,
559*dea60ff0SArmin Wolf 			   HWMON_T_INPUT | HWMON_T_ALARM | HWMON_T_FAULT,
560*dea60ff0SArmin Wolf 			   HWMON_T_INPUT | HWMON_T_ALARM | HWMON_T_FAULT,
561*dea60ff0SArmin Wolf 			   HWMON_T_INPUT | HWMON_T_ALARM | HWMON_T_FAULT,
562*dea60ff0SArmin Wolf 			   HWMON_T_INPUT | HWMON_T_ALARM | HWMON_T_FAULT,
563*dea60ff0SArmin Wolf 			   HWMON_T_INPUT | HWMON_T_ALARM | HWMON_T_FAULT,
564*dea60ff0SArmin Wolf 			   HWMON_T_INPUT | HWMON_T_ALARM | HWMON_T_FAULT,
565*dea60ff0SArmin Wolf 			   HWMON_T_INPUT | HWMON_T_ALARM | HWMON_T_FAULT,
566*dea60ff0SArmin Wolf 			   HWMON_T_INPUT | HWMON_T_ALARM | HWMON_T_FAULT,
567*dea60ff0SArmin Wolf 			   HWMON_T_INPUT | HWMON_T_ALARM | HWMON_T_FAULT
568*dea60ff0SArmin Wolf 			   ),
569*dea60ff0SArmin Wolf 	HWMON_CHANNEL_INFO(fan,
570*dea60ff0SArmin Wolf 			   HWMON_F_INPUT | HWMON_F_ALARM,
571*dea60ff0SArmin Wolf 			   HWMON_F_INPUT | HWMON_F_ALARM,
572*dea60ff0SArmin Wolf 			   HWMON_F_INPUT | HWMON_F_ALARM,
573*dea60ff0SArmin Wolf 			   HWMON_F_INPUT | HWMON_F_ALARM,
574*dea60ff0SArmin Wolf 			   HWMON_F_INPUT | HWMON_F_ALARM,
575*dea60ff0SArmin Wolf 			   HWMON_F_INPUT | HWMON_F_ALARM,
576*dea60ff0SArmin Wolf 			   HWMON_F_INPUT | HWMON_F_ALARM,
577*dea60ff0SArmin Wolf 			   HWMON_F_INPUT | HWMON_F_ALARM
578*dea60ff0SArmin Wolf 			   ),
579*dea60ff0SArmin Wolf 	HWMON_CHANNEL_INFO(in,
580*dea60ff0SArmin Wolf 			   HWMON_I_INPUT,
581*dea60ff0SArmin Wolf 			   HWMON_I_INPUT,
582*dea60ff0SArmin Wolf 			   HWMON_I_INPUT,
583*dea60ff0SArmin Wolf 			   HWMON_I_INPUT
584*dea60ff0SArmin Wolf 			   ),
585*dea60ff0SArmin Wolf 	NULL
586*dea60ff0SArmin Wolf };
587*dea60ff0SArmin Wolf 
588*dea60ff0SArmin Wolf static const struct hwmon_chip_info fts_chip_info = {
589*dea60ff0SArmin Wolf 	.ops = &fts_ops,
590*dea60ff0SArmin Wolf 	.info = fts_info,
591*dea60ff0SArmin Wolf };
592*dea60ff0SArmin Wolf 
59308426edaSThilo Cestonaro /*****************************************************************************/
59408426edaSThilo Cestonaro /* Module initialization / remove functions				     */
59508426edaSThilo Cestonaro /*****************************************************************************/
5962d5aee43SThilo Cestonaro static int fts_detect(struct i2c_client *client,
5972d5aee43SThilo Cestonaro 		      struct i2c_board_info *info)
5982d5aee43SThilo Cestonaro {
5992d5aee43SThilo Cestonaro 	int val;
6002d5aee43SThilo Cestonaro 
60123bc3cafSzuoqilin 	/* detection works with revision greater or equal to 0x2b */
6022d5aee43SThilo Cestonaro 	val = i2c_smbus_read_byte_data(client, FTS_DEVICE_REVISION_REG);
6032d5aee43SThilo Cestonaro 	if (val < 0x2b)
6042d5aee43SThilo Cestonaro 		return -ENODEV;
6052d5aee43SThilo Cestonaro 
6062d5aee43SThilo Cestonaro 	/* Device Detect Regs must have 0x17 0x34 and 0x54 */
6072d5aee43SThilo Cestonaro 	val = i2c_smbus_read_byte_data(client, FTS_DEVICE_DETECT_REG_1);
6082d5aee43SThilo Cestonaro 	if (val != 0x17)
6092d5aee43SThilo Cestonaro 		return -ENODEV;
6102d5aee43SThilo Cestonaro 
6112d5aee43SThilo Cestonaro 	val = i2c_smbus_read_byte_data(client, FTS_DEVICE_DETECT_REG_2);
6122d5aee43SThilo Cestonaro 	if (val != 0x34)
6132d5aee43SThilo Cestonaro 		return -ENODEV;
6142d5aee43SThilo Cestonaro 
6152d5aee43SThilo Cestonaro 	val = i2c_smbus_read_byte_data(client, FTS_DEVICE_DETECT_REG_3);
6162d5aee43SThilo Cestonaro 	if (val != 0x54)
6172d5aee43SThilo Cestonaro 		return -ENODEV;
6182d5aee43SThilo Cestonaro 
6192d5aee43SThilo Cestonaro 	/*
6202d5aee43SThilo Cestonaro 	 * 0x10 == Baseboard Management Controller, 0x01 == Teutates
6212d5aee43SThilo Cestonaro 	 * Device ID Reg needs to be 0x11
6222d5aee43SThilo Cestonaro 	 */
6232d5aee43SThilo Cestonaro 	val = i2c_smbus_read_byte_data(client, FTS_DEVICE_ID_REG);
6242d5aee43SThilo Cestonaro 	if (val != 0x11)
6252d5aee43SThilo Cestonaro 		return -ENODEV;
6262d5aee43SThilo Cestonaro 
627f2f394dbSWolfram Sang 	strscpy(info->type, fts_id[0].name, I2C_NAME_SIZE);
6282d5aee43SThilo Cestonaro 	info->flags = 0;
6292d5aee43SThilo Cestonaro 	return 0;
6302d5aee43SThilo Cestonaro }
6312d5aee43SThilo Cestonaro 
63267487038SStephen Kitt static int fts_probe(struct i2c_client *client)
63308426edaSThilo Cestonaro {
63408426edaSThilo Cestonaro 	u8 revision;
63508426edaSThilo Cestonaro 	struct fts_data *data;
63608426edaSThilo Cestonaro 	int err;
63708426edaSThilo Cestonaro 	s8 deviceid;
63808426edaSThilo Cestonaro 	struct device *hwmon_dev;
63908426edaSThilo Cestonaro 
64008426edaSThilo Cestonaro 	if (client->addr != 0x73)
64108426edaSThilo Cestonaro 		return -ENODEV;
64208426edaSThilo Cestonaro 
64308426edaSThilo Cestonaro 	/* Baseboard Management Controller check */
64408426edaSThilo Cestonaro 	deviceid = i2c_smbus_read_byte_data(client, FTS_DEVICE_ID_REG);
64508426edaSThilo Cestonaro 	if (deviceid > 0 && (deviceid & 0xF0) == 0x10) {
64608426edaSThilo Cestonaro 		switch (deviceid & 0x0F) {
64708426edaSThilo Cestonaro 		case 0x01:
64808426edaSThilo Cestonaro 			break;
64908426edaSThilo Cestonaro 		default:
65008426edaSThilo Cestonaro 			dev_dbg(&client->dev,
65108426edaSThilo Cestonaro 				"No Baseboard Management Controller\n");
65208426edaSThilo Cestonaro 			return -ENODEV;
65308426edaSThilo Cestonaro 		}
65408426edaSThilo Cestonaro 	} else {
65508426edaSThilo Cestonaro 		dev_dbg(&client->dev, "No fujitsu board\n");
65608426edaSThilo Cestonaro 		return -ENODEV;
65708426edaSThilo Cestonaro 	}
65808426edaSThilo Cestonaro 
65908426edaSThilo Cestonaro 	data = devm_kzalloc(&client->dev, sizeof(struct fts_data),
66008426edaSThilo Cestonaro 			    GFP_KERNEL);
66108426edaSThilo Cestonaro 	if (!data)
66208426edaSThilo Cestonaro 		return -ENOMEM;
66308426edaSThilo Cestonaro 
66408426edaSThilo Cestonaro 	mutex_init(&data->update_lock);
66508426edaSThilo Cestonaro 	mutex_init(&data->access_lock);
66608426edaSThilo Cestonaro 	data->client = client;
66708426edaSThilo Cestonaro 	dev_set_drvdata(&client->dev, data);
66808426edaSThilo Cestonaro 
66908426edaSThilo Cestonaro 	err = i2c_smbus_read_byte_data(client, FTS_DEVICE_REVISION_REG);
67008426edaSThilo Cestonaro 	if (err < 0)
67108426edaSThilo Cestonaro 		return err;
67208426edaSThilo Cestonaro 	revision = err;
67308426edaSThilo Cestonaro 
674*dea60ff0SArmin Wolf 	hwmon_dev = devm_hwmon_device_register_with_info(&client->dev, "ftsteutates", data,
675*dea60ff0SArmin Wolf 							 &fts_chip_info, fts_attr_groups);
67608426edaSThilo Cestonaro 	if (IS_ERR(hwmon_dev))
67708426edaSThilo Cestonaro 		return PTR_ERR(hwmon_dev);
67808426edaSThilo Cestonaro 
67908426edaSThilo Cestonaro 	err = fts_watchdog_init(data);
68008426edaSThilo Cestonaro 	if (err)
68108426edaSThilo Cestonaro 		return err;
68208426edaSThilo Cestonaro 
68308426edaSThilo Cestonaro 	dev_info(&client->dev, "Detected FTS Teutates chip, revision: %d.%d\n",
68408426edaSThilo Cestonaro 		 (revision & 0xF0) >> 4, revision & 0x0F);
68508426edaSThilo Cestonaro 	return 0;
68608426edaSThilo Cestonaro }
68708426edaSThilo Cestonaro 
68808426edaSThilo Cestonaro /*****************************************************************************/
68908426edaSThilo Cestonaro /* Module Details							     */
69008426edaSThilo Cestonaro /*****************************************************************************/
69108426edaSThilo Cestonaro static struct i2c_driver fts_driver = {
6922d5aee43SThilo Cestonaro 	.class = I2C_CLASS_HWMON,
69308426edaSThilo Cestonaro 	.driver = {
69408426edaSThilo Cestonaro 		.name = "ftsteutates",
69508426edaSThilo Cestonaro 	},
69608426edaSThilo Cestonaro 	.id_table = fts_id,
69767487038SStephen Kitt 	.probe_new = fts_probe,
6982d5aee43SThilo Cestonaro 	.detect = fts_detect,
6992d5aee43SThilo Cestonaro 	.address_list = normal_i2c,
70008426edaSThilo Cestonaro };
70108426edaSThilo Cestonaro 
70208426edaSThilo Cestonaro module_i2c_driver(fts_driver);
70308426edaSThilo Cestonaro 
70408426edaSThilo Cestonaro MODULE_AUTHOR("Thilo Cestonaro <thilo.cestonaro@ts.fujitsu.com>");
70508426edaSThilo Cestonaro MODULE_DESCRIPTION("FTS Teutates driver");
70608426edaSThilo Cestonaro MODULE_LICENSE("GPL");
707