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 /*****************************************************************************/
fts_read_byte(struct i2c_client * client,unsigned short reg)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
fts_write_byte(struct i2c_client * client,unsigned short reg,unsigned char value)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 /*****************************************************************************/
fts_update_device(struct fts_data * data)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 /*****************************************************************************/
fts_wd_set_resolution(struct fts_data * data,enum WATCHDOG_RESOLUTION resolution)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
fts_wd_set_timeout(struct watchdog_device * wdd,unsigned int timeout)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
fts_wd_start(struct watchdog_device * wdd)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
fts_wd_stop(struct watchdog_device * wdd)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
fts_watchdog_init(struct fts_data * data)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
fts_is_visible(const void * devdata,enum hwmon_sensor_types type,u32 attr,int channel)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:
359c184f377SArmin 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
fts_read(struct device * dev,enum hwmon_sensor_types type,u32 attr,int channel,long * val)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;
416c184f377SArmin Wolf case hwmon_fan_fault:
417c184f377SArmin Wolf *val = !(data->fan_present & BIT(channel));
418c184f377SArmin Wolf
419c184f377SArmin 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
fts_write(struct device * dev,enum hwmon_sensor_types type,u32 attr,int channel,long val)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
523*06c37796SKrzysztof Kozlowski static const struct hwmon_channel_info * const 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,
544c184f377SArmin Wolf HWMON_F_INPUT | HWMON_F_ALARM | HWMON_F_FAULT,
545c184f377SArmin Wolf HWMON_F_INPUT | HWMON_F_ALARM | HWMON_F_FAULT,
546c184f377SArmin Wolf HWMON_F_INPUT | HWMON_F_ALARM | HWMON_F_FAULT,
547c184f377SArmin Wolf HWMON_F_INPUT | HWMON_F_ALARM | HWMON_F_FAULT,
548c184f377SArmin Wolf HWMON_F_INPUT | HWMON_F_ALARM | HWMON_F_FAULT,
549c184f377SArmin Wolf HWMON_F_INPUT | HWMON_F_ALARM | HWMON_F_FAULT,
550c184f377SArmin Wolf HWMON_F_INPUT | HWMON_F_ALARM | HWMON_F_FAULT,
551c184f377SArmin 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 /*****************************************************************************/
fts_detect(struct i2c_client * client,struct i2c_board_info * info)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
fts_probe(struct i2c_client * client)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 = 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