162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * Hwmon client for disk and solid state drives with temperature sensors
462306a36Sopenharmony_ci * Copyright (C) 2019 Zodiac Inflight Innovations
562306a36Sopenharmony_ci *
662306a36Sopenharmony_ci * With input from:
762306a36Sopenharmony_ci *    Hwmon client for S.M.A.R.T. hard disk drives with temperature sensors.
862306a36Sopenharmony_ci *    (C) 2018 Linus Walleij
962306a36Sopenharmony_ci *
1062306a36Sopenharmony_ci *    hwmon: Driver for SCSI/ATA temperature sensors
1162306a36Sopenharmony_ci *    by Constantin Baranov <const@mimas.ru>, submitted September 2009
1262306a36Sopenharmony_ci *
1362306a36Sopenharmony_ci * This drive supports reporting the temperature of SATA drives. It can be
1462306a36Sopenharmony_ci * easily extended to report the temperature of SCSI drives.
1562306a36Sopenharmony_ci *
1662306a36Sopenharmony_ci * The primary means to read drive temperatures and temperature limits
1762306a36Sopenharmony_ci * for ATA drives is the SCT Command Transport feature set as specified in
1862306a36Sopenharmony_ci * ATA8-ACS.
1962306a36Sopenharmony_ci * It can be used to read the current drive temperature, temperature limits,
2062306a36Sopenharmony_ci * and historic minimum and maximum temperatures. The SCT Command Transport
2162306a36Sopenharmony_ci * feature set is documented in "AT Attachment 8 - ATA/ATAPI Command Set
2262306a36Sopenharmony_ci * (ATA8-ACS)".
2362306a36Sopenharmony_ci *
2462306a36Sopenharmony_ci * If the SCT Command Transport feature set is not available, drive temperatures
2562306a36Sopenharmony_ci * may be readable through SMART attributes. Since SMART attributes are not well
2662306a36Sopenharmony_ci * defined, this method is only used as fallback mechanism.
2762306a36Sopenharmony_ci *
2862306a36Sopenharmony_ci * There are three SMART attributes which may report drive temperatures.
2962306a36Sopenharmony_ci * Those are defined as follows (from
3062306a36Sopenharmony_ci * http://www.cropel.com/library/smart-attribute-list.aspx).
3162306a36Sopenharmony_ci *
3262306a36Sopenharmony_ci * 190	Temperature	Temperature, monitored by a sensor somewhere inside
3362306a36Sopenharmony_ci *			the drive. Raw value typicaly holds the actual
3462306a36Sopenharmony_ci *			temperature (hexadecimal) in its rightmost two digits.
3562306a36Sopenharmony_ci *
3662306a36Sopenharmony_ci * 194	Temperature	Temperature, monitored by a sensor somewhere inside
3762306a36Sopenharmony_ci *			the drive. Raw value typicaly holds the actual
3862306a36Sopenharmony_ci *			temperature (hexadecimal) in its rightmost two digits.
3962306a36Sopenharmony_ci *
4062306a36Sopenharmony_ci * 231	Temperature	Temperature, monitored by a sensor somewhere inside
4162306a36Sopenharmony_ci *			the drive. Raw value typicaly holds the actual
4262306a36Sopenharmony_ci *			temperature (hexadecimal) in its rightmost two digits.
4362306a36Sopenharmony_ci *
4462306a36Sopenharmony_ci * Wikipedia defines attributes a bit differently.
4562306a36Sopenharmony_ci *
4662306a36Sopenharmony_ci * 190	Temperature	Value is equal to (100-temp. °C), allowing manufacturer
4762306a36Sopenharmony_ci *	Difference or	to set a minimum threshold which corresponds to a
4862306a36Sopenharmony_ci *	Airflow		maximum temperature. This also follows the convention of
4962306a36Sopenharmony_ci *	Temperature	100 being a best-case value and lower values being
5062306a36Sopenharmony_ci *			undesirable. However, some older drives may instead
5162306a36Sopenharmony_ci *			report raw Temperature (identical to 0xC2) or
5262306a36Sopenharmony_ci *			Temperature minus 50 here.
5362306a36Sopenharmony_ci * 194	Temperature or	Indicates the device temperature, if the appropriate
5462306a36Sopenharmony_ci *	Temperature	sensor is fitted. Lowest byte of the raw value contains
5562306a36Sopenharmony_ci *	Celsius		the exact temperature value (Celsius degrees).
5662306a36Sopenharmony_ci * 231	Life Left	Indicates the approximate SSD life left, in terms of
5762306a36Sopenharmony_ci *	(SSDs) or	program/erase cycles or available reserved blocks.
5862306a36Sopenharmony_ci *	Temperature	A normalized value of 100 represents a new drive, with
5962306a36Sopenharmony_ci *			a threshold value at 10 indicating a need for
6062306a36Sopenharmony_ci *			replacement. A value of 0 may mean that the drive is
6162306a36Sopenharmony_ci *			operating in read-only mode to allow data recovery.
6262306a36Sopenharmony_ci *			Previously (pre-2010) occasionally used for Drive
6362306a36Sopenharmony_ci *			Temperature (more typically reported at 0xC2).
6462306a36Sopenharmony_ci *
6562306a36Sopenharmony_ci * Common denominator is that the first raw byte reports the temperature
6662306a36Sopenharmony_ci * in degrees C on almost all drives. Some drives may report a fractional
6762306a36Sopenharmony_ci * temperature in the second raw byte.
6862306a36Sopenharmony_ci *
6962306a36Sopenharmony_ci * Known exceptions (from libatasmart):
7062306a36Sopenharmony_ci * - SAMSUNG SV0412H and SAMSUNG SV1204H) report the temperature in 10th
7162306a36Sopenharmony_ci *   degrees C in the first two raw bytes.
7262306a36Sopenharmony_ci * - A few Maxtor drives report an unknown or bad value in attribute 194.
7362306a36Sopenharmony_ci * - Certain Apple SSD drives report an unknown value in attribute 190.
7462306a36Sopenharmony_ci *   Only certain firmware versions are affected.
7562306a36Sopenharmony_ci *
7662306a36Sopenharmony_ci * Those exceptions affect older ATA drives and are currently ignored.
7762306a36Sopenharmony_ci * Also, the second raw byte (possibly reporting the fractional temperature)
7862306a36Sopenharmony_ci * is currently ignored.
7962306a36Sopenharmony_ci *
8062306a36Sopenharmony_ci * Many drives also report temperature limits in additional SMART data raw
8162306a36Sopenharmony_ci * bytes. The format of those is not well defined and varies widely.
8262306a36Sopenharmony_ci * The driver does not currently attempt to report those limits.
8362306a36Sopenharmony_ci *
8462306a36Sopenharmony_ci * According to data in smartmontools, attribute 231 is rarely used to report
8562306a36Sopenharmony_ci * drive temperatures. At the same time, several drives report SSD life left
8662306a36Sopenharmony_ci * in attribute 231, but do not support temperature sensors. For this reason,
8762306a36Sopenharmony_ci * attribute 231 is currently ignored.
8862306a36Sopenharmony_ci *
8962306a36Sopenharmony_ci * Following above definitions, temperatures are reported as follows.
9062306a36Sopenharmony_ci *   If SCT Command Transport is supported, it is used to read the
9162306a36Sopenharmony_ci *   temperature and, if available, temperature limits.
9262306a36Sopenharmony_ci * - Otherwise, if SMART attribute 194 is supported, it is used to read
9362306a36Sopenharmony_ci *   the temperature.
9462306a36Sopenharmony_ci * - Otherwise, if SMART attribute 190 is supported, it is used to read
9562306a36Sopenharmony_ci *   the temperature.
9662306a36Sopenharmony_ci */
9762306a36Sopenharmony_ci
9862306a36Sopenharmony_ci#include <linux/ata.h>
9962306a36Sopenharmony_ci#include <linux/bits.h>
10062306a36Sopenharmony_ci#include <linux/device.h>
10162306a36Sopenharmony_ci#include <linux/hwmon.h>
10262306a36Sopenharmony_ci#include <linux/kernel.h>
10362306a36Sopenharmony_ci#include <linux/list.h>
10462306a36Sopenharmony_ci#include <linux/module.h>
10562306a36Sopenharmony_ci#include <linux/mutex.h>
10662306a36Sopenharmony_ci#include <scsi/scsi_cmnd.h>
10762306a36Sopenharmony_ci#include <scsi/scsi_device.h>
10862306a36Sopenharmony_ci#include <scsi/scsi_driver.h>
10962306a36Sopenharmony_ci#include <scsi/scsi_proto.h>
11062306a36Sopenharmony_ci
11162306a36Sopenharmony_cistruct drivetemp_data {
11262306a36Sopenharmony_ci	struct list_head list;		/* list of instantiated devices */
11362306a36Sopenharmony_ci	struct mutex lock;		/* protect data buffer accesses */
11462306a36Sopenharmony_ci	struct scsi_device *sdev;	/* SCSI device */
11562306a36Sopenharmony_ci	struct device *dev;		/* instantiating device */
11662306a36Sopenharmony_ci	struct device *hwdev;		/* hardware monitoring device */
11762306a36Sopenharmony_ci	u8 smartdata[ATA_SECT_SIZE];	/* local buffer */
11862306a36Sopenharmony_ci	int (*get_temp)(struct drivetemp_data *st, u32 attr, long *val);
11962306a36Sopenharmony_ci	bool have_temp_lowest;		/* lowest temp in SCT status */
12062306a36Sopenharmony_ci	bool have_temp_highest;		/* highest temp in SCT status */
12162306a36Sopenharmony_ci	bool have_temp_min;		/* have min temp */
12262306a36Sopenharmony_ci	bool have_temp_max;		/* have max temp */
12362306a36Sopenharmony_ci	bool have_temp_lcrit;		/* have lower critical limit */
12462306a36Sopenharmony_ci	bool have_temp_crit;		/* have critical limit */
12562306a36Sopenharmony_ci	int temp_min;			/* min temp */
12662306a36Sopenharmony_ci	int temp_max;			/* max temp */
12762306a36Sopenharmony_ci	int temp_lcrit;			/* lower critical limit */
12862306a36Sopenharmony_ci	int temp_crit;			/* critical limit */
12962306a36Sopenharmony_ci};
13062306a36Sopenharmony_ci
13162306a36Sopenharmony_cistatic LIST_HEAD(drivetemp_devlist);
13262306a36Sopenharmony_ci
13362306a36Sopenharmony_ci#define ATA_MAX_SMART_ATTRS	30
13462306a36Sopenharmony_ci#define SMART_TEMP_PROP_190	190
13562306a36Sopenharmony_ci#define SMART_TEMP_PROP_194	194
13662306a36Sopenharmony_ci
13762306a36Sopenharmony_ci#define SCT_STATUS_REQ_ADDR	0xe0
13862306a36Sopenharmony_ci#define  SCT_STATUS_VERSION_LOW		0	/* log byte offsets */
13962306a36Sopenharmony_ci#define  SCT_STATUS_VERSION_HIGH	1
14062306a36Sopenharmony_ci#define  SCT_STATUS_TEMP		200
14162306a36Sopenharmony_ci#define  SCT_STATUS_TEMP_LOWEST		201
14262306a36Sopenharmony_ci#define  SCT_STATUS_TEMP_HIGHEST	202
14362306a36Sopenharmony_ci#define SCT_READ_LOG_ADDR	0xe1
14462306a36Sopenharmony_ci#define  SMART_READ_LOG			0xd5
14562306a36Sopenharmony_ci#define  SMART_WRITE_LOG		0xd6
14662306a36Sopenharmony_ci
14762306a36Sopenharmony_ci#define INVALID_TEMP		0x80
14862306a36Sopenharmony_ci
14962306a36Sopenharmony_ci#define temp_is_valid(temp)	((temp) != INVALID_TEMP)
15062306a36Sopenharmony_ci#define temp_from_sct(temp)	(((s8)(temp)) * 1000)
15162306a36Sopenharmony_ci
15262306a36Sopenharmony_cistatic inline bool ata_id_smart_supported(u16 *id)
15362306a36Sopenharmony_ci{
15462306a36Sopenharmony_ci	return id[ATA_ID_COMMAND_SET_1] & BIT(0);
15562306a36Sopenharmony_ci}
15662306a36Sopenharmony_ci
15762306a36Sopenharmony_cistatic inline bool ata_id_smart_enabled(u16 *id)
15862306a36Sopenharmony_ci{
15962306a36Sopenharmony_ci	return id[ATA_ID_CFS_ENABLE_1] & BIT(0);
16062306a36Sopenharmony_ci}
16162306a36Sopenharmony_ci
16262306a36Sopenharmony_cistatic int drivetemp_scsi_command(struct drivetemp_data *st,
16362306a36Sopenharmony_ci				 u8 ata_command, u8 feature,
16462306a36Sopenharmony_ci				 u8 lba_low, u8 lba_mid, u8 lba_high)
16562306a36Sopenharmony_ci{
16662306a36Sopenharmony_ci	u8 scsi_cmd[MAX_COMMAND_SIZE];
16762306a36Sopenharmony_ci	enum req_op op;
16862306a36Sopenharmony_ci
16962306a36Sopenharmony_ci	memset(scsi_cmd, 0, sizeof(scsi_cmd));
17062306a36Sopenharmony_ci	scsi_cmd[0] = ATA_16;
17162306a36Sopenharmony_ci	if (ata_command == ATA_CMD_SMART && feature == SMART_WRITE_LOG) {
17262306a36Sopenharmony_ci		scsi_cmd[1] = (5 << 1);	/* PIO Data-out */
17362306a36Sopenharmony_ci		/*
17462306a36Sopenharmony_ci		 * No off.line or cc, write to dev, block count in sector count
17562306a36Sopenharmony_ci		 * field.
17662306a36Sopenharmony_ci		 */
17762306a36Sopenharmony_ci		scsi_cmd[2] = 0x06;
17862306a36Sopenharmony_ci		op = REQ_OP_DRV_OUT;
17962306a36Sopenharmony_ci	} else {
18062306a36Sopenharmony_ci		scsi_cmd[1] = (4 << 1);	/* PIO Data-in */
18162306a36Sopenharmony_ci		/*
18262306a36Sopenharmony_ci		 * No off.line or cc, read from dev, block count in sector count
18362306a36Sopenharmony_ci		 * field.
18462306a36Sopenharmony_ci		 */
18562306a36Sopenharmony_ci		scsi_cmd[2] = 0x0e;
18662306a36Sopenharmony_ci		op = REQ_OP_DRV_IN;
18762306a36Sopenharmony_ci	}
18862306a36Sopenharmony_ci	scsi_cmd[4] = feature;
18962306a36Sopenharmony_ci	scsi_cmd[6] = 1;	/* 1 sector */
19062306a36Sopenharmony_ci	scsi_cmd[8] = lba_low;
19162306a36Sopenharmony_ci	scsi_cmd[10] = lba_mid;
19262306a36Sopenharmony_ci	scsi_cmd[12] = lba_high;
19362306a36Sopenharmony_ci	scsi_cmd[14] = ata_command;
19462306a36Sopenharmony_ci
19562306a36Sopenharmony_ci	return scsi_execute_cmd(st->sdev, scsi_cmd, op, st->smartdata,
19662306a36Sopenharmony_ci				ATA_SECT_SIZE, HZ, 5, NULL);
19762306a36Sopenharmony_ci}
19862306a36Sopenharmony_ci
19962306a36Sopenharmony_cistatic int drivetemp_ata_command(struct drivetemp_data *st, u8 feature,
20062306a36Sopenharmony_ci				 u8 select)
20162306a36Sopenharmony_ci{
20262306a36Sopenharmony_ci	return drivetemp_scsi_command(st, ATA_CMD_SMART, feature, select,
20362306a36Sopenharmony_ci				     ATA_SMART_LBAM_PASS, ATA_SMART_LBAH_PASS);
20462306a36Sopenharmony_ci}
20562306a36Sopenharmony_ci
20662306a36Sopenharmony_cistatic int drivetemp_get_smarttemp(struct drivetemp_data *st, u32 attr,
20762306a36Sopenharmony_ci				  long *temp)
20862306a36Sopenharmony_ci{
20962306a36Sopenharmony_ci	u8 *buf = st->smartdata;
21062306a36Sopenharmony_ci	bool have_temp = false;
21162306a36Sopenharmony_ci	u8 temp_raw;
21262306a36Sopenharmony_ci	u8 csum;
21362306a36Sopenharmony_ci	int err;
21462306a36Sopenharmony_ci	int i;
21562306a36Sopenharmony_ci
21662306a36Sopenharmony_ci	err = drivetemp_ata_command(st, ATA_SMART_READ_VALUES, 0);
21762306a36Sopenharmony_ci	if (err)
21862306a36Sopenharmony_ci		return err;
21962306a36Sopenharmony_ci
22062306a36Sopenharmony_ci	/* Checksum the read value table */
22162306a36Sopenharmony_ci	csum = 0;
22262306a36Sopenharmony_ci	for (i = 0; i < ATA_SECT_SIZE; i++)
22362306a36Sopenharmony_ci		csum += buf[i];
22462306a36Sopenharmony_ci	if (csum) {
22562306a36Sopenharmony_ci		dev_dbg(&st->sdev->sdev_gendev,
22662306a36Sopenharmony_ci			"checksum error reading SMART values\n");
22762306a36Sopenharmony_ci		return -EIO;
22862306a36Sopenharmony_ci	}
22962306a36Sopenharmony_ci
23062306a36Sopenharmony_ci	for (i = 0; i < ATA_MAX_SMART_ATTRS; i++) {
23162306a36Sopenharmony_ci		u8 *attr = buf + i * 12;
23262306a36Sopenharmony_ci		int id = attr[2];
23362306a36Sopenharmony_ci
23462306a36Sopenharmony_ci		if (!id)
23562306a36Sopenharmony_ci			continue;
23662306a36Sopenharmony_ci
23762306a36Sopenharmony_ci		if (id == SMART_TEMP_PROP_190) {
23862306a36Sopenharmony_ci			temp_raw = attr[7];
23962306a36Sopenharmony_ci			have_temp = true;
24062306a36Sopenharmony_ci		}
24162306a36Sopenharmony_ci		if (id == SMART_TEMP_PROP_194) {
24262306a36Sopenharmony_ci			temp_raw = attr[7];
24362306a36Sopenharmony_ci			have_temp = true;
24462306a36Sopenharmony_ci			break;
24562306a36Sopenharmony_ci		}
24662306a36Sopenharmony_ci	}
24762306a36Sopenharmony_ci
24862306a36Sopenharmony_ci	if (have_temp) {
24962306a36Sopenharmony_ci		*temp = temp_raw * 1000;
25062306a36Sopenharmony_ci		return 0;
25162306a36Sopenharmony_ci	}
25262306a36Sopenharmony_ci
25362306a36Sopenharmony_ci	return -ENXIO;
25462306a36Sopenharmony_ci}
25562306a36Sopenharmony_ci
25662306a36Sopenharmony_cistatic int drivetemp_get_scttemp(struct drivetemp_data *st, u32 attr, long *val)
25762306a36Sopenharmony_ci{
25862306a36Sopenharmony_ci	u8 *buf = st->smartdata;
25962306a36Sopenharmony_ci	int err;
26062306a36Sopenharmony_ci
26162306a36Sopenharmony_ci	err = drivetemp_ata_command(st, SMART_READ_LOG, SCT_STATUS_REQ_ADDR);
26262306a36Sopenharmony_ci	if (err)
26362306a36Sopenharmony_ci		return err;
26462306a36Sopenharmony_ci	switch (attr) {
26562306a36Sopenharmony_ci	case hwmon_temp_input:
26662306a36Sopenharmony_ci		if (!temp_is_valid(buf[SCT_STATUS_TEMP]))
26762306a36Sopenharmony_ci			return -ENODATA;
26862306a36Sopenharmony_ci		*val = temp_from_sct(buf[SCT_STATUS_TEMP]);
26962306a36Sopenharmony_ci		break;
27062306a36Sopenharmony_ci	case hwmon_temp_lowest:
27162306a36Sopenharmony_ci		if (!temp_is_valid(buf[SCT_STATUS_TEMP_LOWEST]))
27262306a36Sopenharmony_ci			return -ENODATA;
27362306a36Sopenharmony_ci		*val = temp_from_sct(buf[SCT_STATUS_TEMP_LOWEST]);
27462306a36Sopenharmony_ci		break;
27562306a36Sopenharmony_ci	case hwmon_temp_highest:
27662306a36Sopenharmony_ci		if (!temp_is_valid(buf[SCT_STATUS_TEMP_HIGHEST]))
27762306a36Sopenharmony_ci			return -ENODATA;
27862306a36Sopenharmony_ci		*val = temp_from_sct(buf[SCT_STATUS_TEMP_HIGHEST]);
27962306a36Sopenharmony_ci		break;
28062306a36Sopenharmony_ci	default:
28162306a36Sopenharmony_ci		err = -EINVAL;
28262306a36Sopenharmony_ci		break;
28362306a36Sopenharmony_ci	}
28462306a36Sopenharmony_ci	return err;
28562306a36Sopenharmony_ci}
28662306a36Sopenharmony_ci
28762306a36Sopenharmony_cistatic const char * const sct_avoid_models[] = {
28862306a36Sopenharmony_ci/*
28962306a36Sopenharmony_ci * These drives will have WRITE FPDMA QUEUED command timeouts and sometimes just
29062306a36Sopenharmony_ci * freeze until power-cycled under heavy write loads when their temperature is
29162306a36Sopenharmony_ci * getting polled in SCT mode. The SMART mode seems to be fine, though.
29262306a36Sopenharmony_ci *
29362306a36Sopenharmony_ci * While only the 3 TB model (DT01ACA3) was actually caught exhibiting the
29462306a36Sopenharmony_ci * problem let's play safe here to avoid data corruption and ban the whole
29562306a36Sopenharmony_ci * DT01ACAx family.
29662306a36Sopenharmony_ci
29762306a36Sopenharmony_ci * The models from this array are prefix-matched.
29862306a36Sopenharmony_ci */
29962306a36Sopenharmony_ci	"TOSHIBA DT01ACA",
30062306a36Sopenharmony_ci};
30162306a36Sopenharmony_ci
30262306a36Sopenharmony_cistatic bool drivetemp_sct_avoid(struct drivetemp_data *st)
30362306a36Sopenharmony_ci{
30462306a36Sopenharmony_ci	struct scsi_device *sdev = st->sdev;
30562306a36Sopenharmony_ci	unsigned int ctr;
30662306a36Sopenharmony_ci
30762306a36Sopenharmony_ci	if (!sdev->model)
30862306a36Sopenharmony_ci		return false;
30962306a36Sopenharmony_ci
31062306a36Sopenharmony_ci	/*
31162306a36Sopenharmony_ci	 * The "model" field contains just the raw SCSI INQUIRY response
31262306a36Sopenharmony_ci	 * "product identification" field, which has a width of 16 bytes.
31362306a36Sopenharmony_ci	 * This field is space-filled, but is NOT NULL-terminated.
31462306a36Sopenharmony_ci	 */
31562306a36Sopenharmony_ci	for (ctr = 0; ctr < ARRAY_SIZE(sct_avoid_models); ctr++)
31662306a36Sopenharmony_ci		if (!strncmp(sdev->model, sct_avoid_models[ctr],
31762306a36Sopenharmony_ci			     strlen(sct_avoid_models[ctr])))
31862306a36Sopenharmony_ci			return true;
31962306a36Sopenharmony_ci
32062306a36Sopenharmony_ci	return false;
32162306a36Sopenharmony_ci}
32262306a36Sopenharmony_ci
32362306a36Sopenharmony_cistatic int drivetemp_identify_sata(struct drivetemp_data *st)
32462306a36Sopenharmony_ci{
32562306a36Sopenharmony_ci	struct scsi_device *sdev = st->sdev;
32662306a36Sopenharmony_ci	u8 *buf = st->smartdata;
32762306a36Sopenharmony_ci	struct scsi_vpd *vpd;
32862306a36Sopenharmony_ci	bool is_ata, is_sata;
32962306a36Sopenharmony_ci	bool have_sct_data_table;
33062306a36Sopenharmony_ci	bool have_sct_temp;
33162306a36Sopenharmony_ci	bool have_smart;
33262306a36Sopenharmony_ci	bool have_sct;
33362306a36Sopenharmony_ci	u16 *ata_id;
33462306a36Sopenharmony_ci	u16 version;
33562306a36Sopenharmony_ci	long temp;
33662306a36Sopenharmony_ci	int err;
33762306a36Sopenharmony_ci
33862306a36Sopenharmony_ci	/* SCSI-ATA Translation present? */
33962306a36Sopenharmony_ci	rcu_read_lock();
34062306a36Sopenharmony_ci	vpd = rcu_dereference(sdev->vpd_pg89);
34162306a36Sopenharmony_ci
34262306a36Sopenharmony_ci	/*
34362306a36Sopenharmony_ci	 * Verify that ATA IDENTIFY DEVICE data is included in ATA Information
34462306a36Sopenharmony_ci	 * VPD and that the drive implements the SATA protocol.
34562306a36Sopenharmony_ci	 */
34662306a36Sopenharmony_ci	if (!vpd || vpd->len < 572 || vpd->data[56] != ATA_CMD_ID_ATA ||
34762306a36Sopenharmony_ci	    vpd->data[36] != 0x34) {
34862306a36Sopenharmony_ci		rcu_read_unlock();
34962306a36Sopenharmony_ci		return -ENODEV;
35062306a36Sopenharmony_ci	}
35162306a36Sopenharmony_ci	ata_id = (u16 *)&vpd->data[60];
35262306a36Sopenharmony_ci	is_ata = ata_id_is_ata(ata_id);
35362306a36Sopenharmony_ci	is_sata = ata_id_is_sata(ata_id);
35462306a36Sopenharmony_ci	have_sct = ata_id_sct_supported(ata_id);
35562306a36Sopenharmony_ci	have_sct_data_table = ata_id_sct_data_tables(ata_id);
35662306a36Sopenharmony_ci	have_smart = ata_id_smart_supported(ata_id) &&
35762306a36Sopenharmony_ci				ata_id_smart_enabled(ata_id);
35862306a36Sopenharmony_ci
35962306a36Sopenharmony_ci	rcu_read_unlock();
36062306a36Sopenharmony_ci
36162306a36Sopenharmony_ci	/* bail out if this is not a SATA device */
36262306a36Sopenharmony_ci	if (!is_ata || !is_sata)
36362306a36Sopenharmony_ci		return -ENODEV;
36462306a36Sopenharmony_ci
36562306a36Sopenharmony_ci	if (have_sct && drivetemp_sct_avoid(st)) {
36662306a36Sopenharmony_ci		dev_notice(&sdev->sdev_gendev,
36762306a36Sopenharmony_ci			   "will avoid using SCT for temperature monitoring\n");
36862306a36Sopenharmony_ci		have_sct = false;
36962306a36Sopenharmony_ci	}
37062306a36Sopenharmony_ci
37162306a36Sopenharmony_ci	if (!have_sct)
37262306a36Sopenharmony_ci		goto skip_sct;
37362306a36Sopenharmony_ci
37462306a36Sopenharmony_ci	err = drivetemp_ata_command(st, SMART_READ_LOG, SCT_STATUS_REQ_ADDR);
37562306a36Sopenharmony_ci	if (err)
37662306a36Sopenharmony_ci		goto skip_sct;
37762306a36Sopenharmony_ci
37862306a36Sopenharmony_ci	version = (buf[SCT_STATUS_VERSION_HIGH] << 8) |
37962306a36Sopenharmony_ci		  buf[SCT_STATUS_VERSION_LOW];
38062306a36Sopenharmony_ci	if (version != 2 && version != 3)
38162306a36Sopenharmony_ci		goto skip_sct;
38262306a36Sopenharmony_ci
38362306a36Sopenharmony_ci	have_sct_temp = temp_is_valid(buf[SCT_STATUS_TEMP]);
38462306a36Sopenharmony_ci	if (!have_sct_temp)
38562306a36Sopenharmony_ci		goto skip_sct;
38662306a36Sopenharmony_ci
38762306a36Sopenharmony_ci	st->have_temp_lowest = temp_is_valid(buf[SCT_STATUS_TEMP_LOWEST]);
38862306a36Sopenharmony_ci	st->have_temp_highest = temp_is_valid(buf[SCT_STATUS_TEMP_HIGHEST]);
38962306a36Sopenharmony_ci
39062306a36Sopenharmony_ci	if (!have_sct_data_table)
39162306a36Sopenharmony_ci		goto skip_sct_data;
39262306a36Sopenharmony_ci
39362306a36Sopenharmony_ci	/* Request and read temperature history table */
39462306a36Sopenharmony_ci	memset(buf, '\0', sizeof(st->smartdata));
39562306a36Sopenharmony_ci	buf[0] = 5;	/* data table command */
39662306a36Sopenharmony_ci	buf[2] = 1;	/* read table */
39762306a36Sopenharmony_ci	buf[4] = 2;	/* temperature history table */
39862306a36Sopenharmony_ci
39962306a36Sopenharmony_ci	err = drivetemp_ata_command(st, SMART_WRITE_LOG, SCT_STATUS_REQ_ADDR);
40062306a36Sopenharmony_ci	if (err)
40162306a36Sopenharmony_ci		goto skip_sct_data;
40262306a36Sopenharmony_ci
40362306a36Sopenharmony_ci	err = drivetemp_ata_command(st, SMART_READ_LOG, SCT_READ_LOG_ADDR);
40462306a36Sopenharmony_ci	if (err)
40562306a36Sopenharmony_ci		goto skip_sct_data;
40662306a36Sopenharmony_ci
40762306a36Sopenharmony_ci	/*
40862306a36Sopenharmony_ci	 * Temperature limits per AT Attachment 8 -
40962306a36Sopenharmony_ci	 * ATA/ATAPI Command Set (ATA8-ACS)
41062306a36Sopenharmony_ci	 */
41162306a36Sopenharmony_ci	st->have_temp_max = temp_is_valid(buf[6]);
41262306a36Sopenharmony_ci	st->have_temp_crit = temp_is_valid(buf[7]);
41362306a36Sopenharmony_ci	st->have_temp_min = temp_is_valid(buf[8]);
41462306a36Sopenharmony_ci	st->have_temp_lcrit = temp_is_valid(buf[9]);
41562306a36Sopenharmony_ci
41662306a36Sopenharmony_ci	st->temp_max = temp_from_sct(buf[6]);
41762306a36Sopenharmony_ci	st->temp_crit = temp_from_sct(buf[7]);
41862306a36Sopenharmony_ci	st->temp_min = temp_from_sct(buf[8]);
41962306a36Sopenharmony_ci	st->temp_lcrit = temp_from_sct(buf[9]);
42062306a36Sopenharmony_ci
42162306a36Sopenharmony_ciskip_sct_data:
42262306a36Sopenharmony_ci	if (have_sct_temp) {
42362306a36Sopenharmony_ci		st->get_temp = drivetemp_get_scttemp;
42462306a36Sopenharmony_ci		return 0;
42562306a36Sopenharmony_ci	}
42662306a36Sopenharmony_ciskip_sct:
42762306a36Sopenharmony_ci	if (!have_smart)
42862306a36Sopenharmony_ci		return -ENODEV;
42962306a36Sopenharmony_ci	st->get_temp = drivetemp_get_smarttemp;
43062306a36Sopenharmony_ci	return drivetemp_get_smarttemp(st, hwmon_temp_input, &temp);
43162306a36Sopenharmony_ci}
43262306a36Sopenharmony_ci
43362306a36Sopenharmony_cistatic int drivetemp_identify(struct drivetemp_data *st)
43462306a36Sopenharmony_ci{
43562306a36Sopenharmony_ci	struct scsi_device *sdev = st->sdev;
43662306a36Sopenharmony_ci
43762306a36Sopenharmony_ci	/* Bail out immediately if there is no inquiry data */
43862306a36Sopenharmony_ci	if (!sdev->inquiry || sdev->inquiry_len < 16)
43962306a36Sopenharmony_ci		return -ENODEV;
44062306a36Sopenharmony_ci
44162306a36Sopenharmony_ci	/* Disk device? */
44262306a36Sopenharmony_ci	if (sdev->type != TYPE_DISK && sdev->type != TYPE_ZBC)
44362306a36Sopenharmony_ci		return -ENODEV;
44462306a36Sopenharmony_ci
44562306a36Sopenharmony_ci	return drivetemp_identify_sata(st);
44662306a36Sopenharmony_ci}
44762306a36Sopenharmony_ci
44862306a36Sopenharmony_cistatic int drivetemp_read(struct device *dev, enum hwmon_sensor_types type,
44962306a36Sopenharmony_ci			 u32 attr, int channel, long *val)
45062306a36Sopenharmony_ci{
45162306a36Sopenharmony_ci	struct drivetemp_data *st = dev_get_drvdata(dev);
45262306a36Sopenharmony_ci	int err = 0;
45362306a36Sopenharmony_ci
45462306a36Sopenharmony_ci	if (type != hwmon_temp)
45562306a36Sopenharmony_ci		return -EINVAL;
45662306a36Sopenharmony_ci
45762306a36Sopenharmony_ci	switch (attr) {
45862306a36Sopenharmony_ci	case hwmon_temp_input:
45962306a36Sopenharmony_ci	case hwmon_temp_lowest:
46062306a36Sopenharmony_ci	case hwmon_temp_highest:
46162306a36Sopenharmony_ci		mutex_lock(&st->lock);
46262306a36Sopenharmony_ci		err = st->get_temp(st, attr, val);
46362306a36Sopenharmony_ci		mutex_unlock(&st->lock);
46462306a36Sopenharmony_ci		break;
46562306a36Sopenharmony_ci	case hwmon_temp_lcrit:
46662306a36Sopenharmony_ci		*val = st->temp_lcrit;
46762306a36Sopenharmony_ci		break;
46862306a36Sopenharmony_ci	case hwmon_temp_min:
46962306a36Sopenharmony_ci		*val = st->temp_min;
47062306a36Sopenharmony_ci		break;
47162306a36Sopenharmony_ci	case hwmon_temp_max:
47262306a36Sopenharmony_ci		*val = st->temp_max;
47362306a36Sopenharmony_ci		break;
47462306a36Sopenharmony_ci	case hwmon_temp_crit:
47562306a36Sopenharmony_ci		*val = st->temp_crit;
47662306a36Sopenharmony_ci		break;
47762306a36Sopenharmony_ci	default:
47862306a36Sopenharmony_ci		err = -EINVAL;
47962306a36Sopenharmony_ci		break;
48062306a36Sopenharmony_ci	}
48162306a36Sopenharmony_ci	return err;
48262306a36Sopenharmony_ci}
48362306a36Sopenharmony_ci
48462306a36Sopenharmony_cistatic umode_t drivetemp_is_visible(const void *data,
48562306a36Sopenharmony_ci				   enum hwmon_sensor_types type,
48662306a36Sopenharmony_ci				   u32 attr, int channel)
48762306a36Sopenharmony_ci{
48862306a36Sopenharmony_ci	const struct drivetemp_data *st = data;
48962306a36Sopenharmony_ci
49062306a36Sopenharmony_ci	switch (type) {
49162306a36Sopenharmony_ci	case hwmon_temp:
49262306a36Sopenharmony_ci		switch (attr) {
49362306a36Sopenharmony_ci		case hwmon_temp_input:
49462306a36Sopenharmony_ci			return 0444;
49562306a36Sopenharmony_ci		case hwmon_temp_lowest:
49662306a36Sopenharmony_ci			if (st->have_temp_lowest)
49762306a36Sopenharmony_ci				return 0444;
49862306a36Sopenharmony_ci			break;
49962306a36Sopenharmony_ci		case hwmon_temp_highest:
50062306a36Sopenharmony_ci			if (st->have_temp_highest)
50162306a36Sopenharmony_ci				return 0444;
50262306a36Sopenharmony_ci			break;
50362306a36Sopenharmony_ci		case hwmon_temp_min:
50462306a36Sopenharmony_ci			if (st->have_temp_min)
50562306a36Sopenharmony_ci				return 0444;
50662306a36Sopenharmony_ci			break;
50762306a36Sopenharmony_ci		case hwmon_temp_max:
50862306a36Sopenharmony_ci			if (st->have_temp_max)
50962306a36Sopenharmony_ci				return 0444;
51062306a36Sopenharmony_ci			break;
51162306a36Sopenharmony_ci		case hwmon_temp_lcrit:
51262306a36Sopenharmony_ci			if (st->have_temp_lcrit)
51362306a36Sopenharmony_ci				return 0444;
51462306a36Sopenharmony_ci			break;
51562306a36Sopenharmony_ci		case hwmon_temp_crit:
51662306a36Sopenharmony_ci			if (st->have_temp_crit)
51762306a36Sopenharmony_ci				return 0444;
51862306a36Sopenharmony_ci			break;
51962306a36Sopenharmony_ci		default:
52062306a36Sopenharmony_ci			break;
52162306a36Sopenharmony_ci		}
52262306a36Sopenharmony_ci		break;
52362306a36Sopenharmony_ci	default:
52462306a36Sopenharmony_ci		break;
52562306a36Sopenharmony_ci	}
52662306a36Sopenharmony_ci	return 0;
52762306a36Sopenharmony_ci}
52862306a36Sopenharmony_ci
52962306a36Sopenharmony_cistatic const struct hwmon_channel_info * const drivetemp_info[] = {
53062306a36Sopenharmony_ci	HWMON_CHANNEL_INFO(chip,
53162306a36Sopenharmony_ci			   HWMON_C_REGISTER_TZ),
53262306a36Sopenharmony_ci	HWMON_CHANNEL_INFO(temp, HWMON_T_INPUT |
53362306a36Sopenharmony_ci			   HWMON_T_LOWEST | HWMON_T_HIGHEST |
53462306a36Sopenharmony_ci			   HWMON_T_MIN | HWMON_T_MAX |
53562306a36Sopenharmony_ci			   HWMON_T_LCRIT | HWMON_T_CRIT),
53662306a36Sopenharmony_ci	NULL
53762306a36Sopenharmony_ci};
53862306a36Sopenharmony_ci
53962306a36Sopenharmony_cistatic const struct hwmon_ops drivetemp_ops = {
54062306a36Sopenharmony_ci	.is_visible = drivetemp_is_visible,
54162306a36Sopenharmony_ci	.read = drivetemp_read,
54262306a36Sopenharmony_ci};
54362306a36Sopenharmony_ci
54462306a36Sopenharmony_cistatic const struct hwmon_chip_info drivetemp_chip_info = {
54562306a36Sopenharmony_ci	.ops = &drivetemp_ops,
54662306a36Sopenharmony_ci	.info = drivetemp_info,
54762306a36Sopenharmony_ci};
54862306a36Sopenharmony_ci
54962306a36Sopenharmony_ci/*
55062306a36Sopenharmony_ci * The device argument points to sdev->sdev_dev. Its parent is
55162306a36Sopenharmony_ci * sdev->sdev_gendev, which we can use to get the scsi_device pointer.
55262306a36Sopenharmony_ci */
55362306a36Sopenharmony_cistatic int drivetemp_add(struct device *dev)
55462306a36Sopenharmony_ci{
55562306a36Sopenharmony_ci	struct scsi_device *sdev = to_scsi_device(dev->parent);
55662306a36Sopenharmony_ci	struct drivetemp_data *st;
55762306a36Sopenharmony_ci	int err;
55862306a36Sopenharmony_ci
55962306a36Sopenharmony_ci	st = kzalloc(sizeof(*st), GFP_KERNEL);
56062306a36Sopenharmony_ci	if (!st)
56162306a36Sopenharmony_ci		return -ENOMEM;
56262306a36Sopenharmony_ci
56362306a36Sopenharmony_ci	st->sdev = sdev;
56462306a36Sopenharmony_ci	st->dev = dev;
56562306a36Sopenharmony_ci	mutex_init(&st->lock);
56662306a36Sopenharmony_ci
56762306a36Sopenharmony_ci	if (drivetemp_identify(st)) {
56862306a36Sopenharmony_ci		err = -ENODEV;
56962306a36Sopenharmony_ci		goto abort;
57062306a36Sopenharmony_ci	}
57162306a36Sopenharmony_ci
57262306a36Sopenharmony_ci	st->hwdev = hwmon_device_register_with_info(dev->parent, "drivetemp",
57362306a36Sopenharmony_ci						    st, &drivetemp_chip_info,
57462306a36Sopenharmony_ci						    NULL);
57562306a36Sopenharmony_ci	if (IS_ERR(st->hwdev)) {
57662306a36Sopenharmony_ci		err = PTR_ERR(st->hwdev);
57762306a36Sopenharmony_ci		goto abort;
57862306a36Sopenharmony_ci	}
57962306a36Sopenharmony_ci
58062306a36Sopenharmony_ci	list_add(&st->list, &drivetemp_devlist);
58162306a36Sopenharmony_ci	return 0;
58262306a36Sopenharmony_ci
58362306a36Sopenharmony_ciabort:
58462306a36Sopenharmony_ci	kfree(st);
58562306a36Sopenharmony_ci	return err;
58662306a36Sopenharmony_ci}
58762306a36Sopenharmony_ci
58862306a36Sopenharmony_cistatic void drivetemp_remove(struct device *dev)
58962306a36Sopenharmony_ci{
59062306a36Sopenharmony_ci	struct drivetemp_data *st, *tmp;
59162306a36Sopenharmony_ci
59262306a36Sopenharmony_ci	list_for_each_entry_safe(st, tmp, &drivetemp_devlist, list) {
59362306a36Sopenharmony_ci		if (st->dev == dev) {
59462306a36Sopenharmony_ci			list_del(&st->list);
59562306a36Sopenharmony_ci			hwmon_device_unregister(st->hwdev);
59662306a36Sopenharmony_ci			kfree(st);
59762306a36Sopenharmony_ci			break;
59862306a36Sopenharmony_ci		}
59962306a36Sopenharmony_ci	}
60062306a36Sopenharmony_ci}
60162306a36Sopenharmony_ci
60262306a36Sopenharmony_cistatic struct class_interface drivetemp_interface = {
60362306a36Sopenharmony_ci	.add_dev = drivetemp_add,
60462306a36Sopenharmony_ci	.remove_dev = drivetemp_remove,
60562306a36Sopenharmony_ci};
60662306a36Sopenharmony_ci
60762306a36Sopenharmony_cistatic int __init drivetemp_init(void)
60862306a36Sopenharmony_ci{
60962306a36Sopenharmony_ci	return scsi_register_interface(&drivetemp_interface);
61062306a36Sopenharmony_ci}
61162306a36Sopenharmony_ci
61262306a36Sopenharmony_cistatic void __exit drivetemp_exit(void)
61362306a36Sopenharmony_ci{
61462306a36Sopenharmony_ci	scsi_unregister_interface(&drivetemp_interface);
61562306a36Sopenharmony_ci}
61662306a36Sopenharmony_ci
61762306a36Sopenharmony_cimodule_init(drivetemp_init);
61862306a36Sopenharmony_cimodule_exit(drivetemp_exit);
61962306a36Sopenharmony_ci
62062306a36Sopenharmony_ciMODULE_AUTHOR("Guenter Roeck <linus@roeck-us.net>");
62162306a36Sopenharmony_ciMODULE_DESCRIPTION("Hard drive temperature monitor");
62262306a36Sopenharmony_ciMODULE_LICENSE("GPL");
62362306a36Sopenharmony_ciMODULE_ALIAS("platform:drivetemp");
624