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