18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Hwmon client for disk and solid state drives with temperature sensors 48c2ecf20Sopenharmony_ci * Copyright (C) 2019 Zodiac Inflight Innovations 58c2ecf20Sopenharmony_ci * 68c2ecf20Sopenharmony_ci * With input from: 78c2ecf20Sopenharmony_ci * Hwmon client for S.M.A.R.T. hard disk drives with temperature sensors. 88c2ecf20Sopenharmony_ci * (C) 2018 Linus Walleij 98c2ecf20Sopenharmony_ci * 108c2ecf20Sopenharmony_ci * hwmon: Driver for SCSI/ATA temperature sensors 118c2ecf20Sopenharmony_ci * by Constantin Baranov <const@mimas.ru>, submitted September 2009 128c2ecf20Sopenharmony_ci * 138c2ecf20Sopenharmony_ci * This drive supports reporting the temperatire of SATA drives. It can be 148c2ecf20Sopenharmony_ci * easily extended to report the temperature of SCSI drives. 158c2ecf20Sopenharmony_ci * 168c2ecf20Sopenharmony_ci * The primary means to read drive temperatures and temperature limits 178c2ecf20Sopenharmony_ci * for ATA drives is the SCT Command Transport feature set as specified in 188c2ecf20Sopenharmony_ci * ATA8-ACS. 198c2ecf20Sopenharmony_ci * It can be used to read the current drive temperature, temperature limits, 208c2ecf20Sopenharmony_ci * and historic minimum and maximum temperatures. The SCT Command Transport 218c2ecf20Sopenharmony_ci * feature set is documented in "AT Attachment 8 - ATA/ATAPI Command Set 228c2ecf20Sopenharmony_ci * (ATA8-ACS)". 238c2ecf20Sopenharmony_ci * 248c2ecf20Sopenharmony_ci * If the SCT Command Transport feature set is not available, drive temperatures 258c2ecf20Sopenharmony_ci * may be readable through SMART attributes. Since SMART attributes are not well 268c2ecf20Sopenharmony_ci * defined, this method is only used as fallback mechanism. 278c2ecf20Sopenharmony_ci * 288c2ecf20Sopenharmony_ci * There are three SMART attributes which may report drive temperatures. 298c2ecf20Sopenharmony_ci * Those are defined as follows (from 308c2ecf20Sopenharmony_ci * http://www.cropel.com/library/smart-attribute-list.aspx). 318c2ecf20Sopenharmony_ci * 328c2ecf20Sopenharmony_ci * 190 Temperature Temperature, monitored by a sensor somewhere inside 338c2ecf20Sopenharmony_ci * the drive. Raw value typicaly holds the actual 348c2ecf20Sopenharmony_ci * temperature (hexadecimal) in its rightmost two digits. 358c2ecf20Sopenharmony_ci * 368c2ecf20Sopenharmony_ci * 194 Temperature Temperature, monitored by a sensor somewhere inside 378c2ecf20Sopenharmony_ci * the drive. Raw value typicaly holds the actual 388c2ecf20Sopenharmony_ci * temperature (hexadecimal) in its rightmost two digits. 398c2ecf20Sopenharmony_ci * 408c2ecf20Sopenharmony_ci * 231 Temperature Temperature, monitored by a sensor somewhere inside 418c2ecf20Sopenharmony_ci * the drive. Raw value typicaly holds the actual 428c2ecf20Sopenharmony_ci * temperature (hexadecimal) in its rightmost two digits. 438c2ecf20Sopenharmony_ci * 448c2ecf20Sopenharmony_ci * Wikipedia defines attributes a bit differently. 458c2ecf20Sopenharmony_ci * 468c2ecf20Sopenharmony_ci * 190 Temperature Value is equal to (100-temp. °C), allowing manufacturer 478c2ecf20Sopenharmony_ci * Difference or to set a minimum threshold which corresponds to a 488c2ecf20Sopenharmony_ci * Airflow maximum temperature. This also follows the convention of 498c2ecf20Sopenharmony_ci * Temperature 100 being a best-case value and lower values being 508c2ecf20Sopenharmony_ci * undesirable. However, some older drives may instead 518c2ecf20Sopenharmony_ci * report raw Temperature (identical to 0xC2) or 528c2ecf20Sopenharmony_ci * Temperature minus 50 here. 538c2ecf20Sopenharmony_ci * 194 Temperature or Indicates the device temperature, if the appropriate 548c2ecf20Sopenharmony_ci * Temperature sensor is fitted. Lowest byte of the raw value contains 558c2ecf20Sopenharmony_ci * Celsius the exact temperature value (Celsius degrees). 568c2ecf20Sopenharmony_ci * 231 Life Left Indicates the approximate SSD life left, in terms of 578c2ecf20Sopenharmony_ci * (SSDs) or program/erase cycles or available reserved blocks. 588c2ecf20Sopenharmony_ci * Temperature A normalized value of 100 represents a new drive, with 598c2ecf20Sopenharmony_ci * a threshold value at 10 indicating a need for 608c2ecf20Sopenharmony_ci * replacement. A value of 0 may mean that the drive is 618c2ecf20Sopenharmony_ci * operating in read-only mode to allow data recovery. 628c2ecf20Sopenharmony_ci * Previously (pre-2010) occasionally used for Drive 638c2ecf20Sopenharmony_ci * Temperature (more typically reported at 0xC2). 648c2ecf20Sopenharmony_ci * 658c2ecf20Sopenharmony_ci * Common denominator is that the first raw byte reports the temperature 668c2ecf20Sopenharmony_ci * in degrees C on almost all drives. Some drives may report a fractional 678c2ecf20Sopenharmony_ci * temperature in the second raw byte. 688c2ecf20Sopenharmony_ci * 698c2ecf20Sopenharmony_ci * Known exceptions (from libatasmart): 708c2ecf20Sopenharmony_ci * - SAMSUNG SV0412H and SAMSUNG SV1204H) report the temperature in 10th 718c2ecf20Sopenharmony_ci * degrees C in the first two raw bytes. 728c2ecf20Sopenharmony_ci * - A few Maxtor drives report an unknown or bad value in attribute 194. 738c2ecf20Sopenharmony_ci * - Certain Apple SSD drives report an unknown value in attribute 190. 748c2ecf20Sopenharmony_ci * Only certain firmware versions are affected. 758c2ecf20Sopenharmony_ci * 768c2ecf20Sopenharmony_ci * Those exceptions affect older ATA drives and are currently ignored. 778c2ecf20Sopenharmony_ci * Also, the second raw byte (possibly reporting the fractional temperature) 788c2ecf20Sopenharmony_ci * is currently ignored. 798c2ecf20Sopenharmony_ci * 808c2ecf20Sopenharmony_ci * Many drives also report temperature limits in additional SMART data raw 818c2ecf20Sopenharmony_ci * bytes. The format of those is not well defined and varies widely. 828c2ecf20Sopenharmony_ci * The driver does not currently attempt to report those limits. 838c2ecf20Sopenharmony_ci * 848c2ecf20Sopenharmony_ci * According to data in smartmontools, attribute 231 is rarely used to report 858c2ecf20Sopenharmony_ci * drive temperatures. At the same time, several drives report SSD life left 868c2ecf20Sopenharmony_ci * in attribute 231, but do not support temperature sensors. For this reason, 878c2ecf20Sopenharmony_ci * attribute 231 is currently ignored. 888c2ecf20Sopenharmony_ci * 898c2ecf20Sopenharmony_ci * Following above definitions, temperatures are reported as follows. 908c2ecf20Sopenharmony_ci * If SCT Command Transport is supported, it is used to read the 918c2ecf20Sopenharmony_ci * temperature and, if available, temperature limits. 928c2ecf20Sopenharmony_ci * - Otherwise, if SMART attribute 194 is supported, it is used to read 938c2ecf20Sopenharmony_ci * the temperature. 948c2ecf20Sopenharmony_ci * - Otherwise, if SMART attribute 190 is supported, it is used to read 958c2ecf20Sopenharmony_ci * the temperature. 968c2ecf20Sopenharmony_ci */ 978c2ecf20Sopenharmony_ci 988c2ecf20Sopenharmony_ci#include <linux/ata.h> 998c2ecf20Sopenharmony_ci#include <linux/bits.h> 1008c2ecf20Sopenharmony_ci#include <linux/device.h> 1018c2ecf20Sopenharmony_ci#include <linux/hwmon.h> 1028c2ecf20Sopenharmony_ci#include <linux/kernel.h> 1038c2ecf20Sopenharmony_ci#include <linux/list.h> 1048c2ecf20Sopenharmony_ci#include <linux/module.h> 1058c2ecf20Sopenharmony_ci#include <linux/mutex.h> 1068c2ecf20Sopenharmony_ci#include <scsi/scsi_cmnd.h> 1078c2ecf20Sopenharmony_ci#include <scsi/scsi_device.h> 1088c2ecf20Sopenharmony_ci#include <scsi/scsi_driver.h> 1098c2ecf20Sopenharmony_ci#include <scsi/scsi_proto.h> 1108c2ecf20Sopenharmony_ci 1118c2ecf20Sopenharmony_cistruct drivetemp_data { 1128c2ecf20Sopenharmony_ci struct list_head list; /* list of instantiated devices */ 1138c2ecf20Sopenharmony_ci struct mutex lock; /* protect data buffer accesses */ 1148c2ecf20Sopenharmony_ci struct scsi_device *sdev; /* SCSI device */ 1158c2ecf20Sopenharmony_ci struct device *dev; /* instantiating device */ 1168c2ecf20Sopenharmony_ci struct device *hwdev; /* hardware monitoring device */ 1178c2ecf20Sopenharmony_ci u8 smartdata[ATA_SECT_SIZE]; /* local buffer */ 1188c2ecf20Sopenharmony_ci int (*get_temp)(struct drivetemp_data *st, u32 attr, long *val); 1198c2ecf20Sopenharmony_ci bool have_temp_lowest; /* lowest temp in SCT status */ 1208c2ecf20Sopenharmony_ci bool have_temp_highest; /* highest temp in SCT status */ 1218c2ecf20Sopenharmony_ci bool have_temp_min; /* have min temp */ 1228c2ecf20Sopenharmony_ci bool have_temp_max; /* have max temp */ 1238c2ecf20Sopenharmony_ci bool have_temp_lcrit; /* have lower critical limit */ 1248c2ecf20Sopenharmony_ci bool have_temp_crit; /* have critical limit */ 1258c2ecf20Sopenharmony_ci int temp_min; /* min temp */ 1268c2ecf20Sopenharmony_ci int temp_max; /* max temp */ 1278c2ecf20Sopenharmony_ci int temp_lcrit; /* lower critical limit */ 1288c2ecf20Sopenharmony_ci int temp_crit; /* critical limit */ 1298c2ecf20Sopenharmony_ci}; 1308c2ecf20Sopenharmony_ci 1318c2ecf20Sopenharmony_cistatic LIST_HEAD(drivetemp_devlist); 1328c2ecf20Sopenharmony_ci 1338c2ecf20Sopenharmony_ci#define ATA_MAX_SMART_ATTRS 30 1348c2ecf20Sopenharmony_ci#define SMART_TEMP_PROP_190 190 1358c2ecf20Sopenharmony_ci#define SMART_TEMP_PROP_194 194 1368c2ecf20Sopenharmony_ci 1378c2ecf20Sopenharmony_ci#define SCT_STATUS_REQ_ADDR 0xe0 1388c2ecf20Sopenharmony_ci#define SCT_STATUS_VERSION_LOW 0 /* log byte offsets */ 1398c2ecf20Sopenharmony_ci#define SCT_STATUS_VERSION_HIGH 1 1408c2ecf20Sopenharmony_ci#define SCT_STATUS_TEMP 200 1418c2ecf20Sopenharmony_ci#define SCT_STATUS_TEMP_LOWEST 201 1428c2ecf20Sopenharmony_ci#define SCT_STATUS_TEMP_HIGHEST 202 1438c2ecf20Sopenharmony_ci#define SCT_READ_LOG_ADDR 0xe1 1448c2ecf20Sopenharmony_ci#define SMART_READ_LOG 0xd5 1458c2ecf20Sopenharmony_ci#define SMART_WRITE_LOG 0xd6 1468c2ecf20Sopenharmony_ci 1478c2ecf20Sopenharmony_ci#define INVALID_TEMP 0x80 1488c2ecf20Sopenharmony_ci 1498c2ecf20Sopenharmony_ci#define temp_is_valid(temp) ((temp) != INVALID_TEMP) 1508c2ecf20Sopenharmony_ci#define temp_from_sct(temp) (((s8)(temp)) * 1000) 1518c2ecf20Sopenharmony_ci 1528c2ecf20Sopenharmony_cistatic inline bool ata_id_smart_supported(u16 *id) 1538c2ecf20Sopenharmony_ci{ 1548c2ecf20Sopenharmony_ci return id[ATA_ID_COMMAND_SET_1] & BIT(0); 1558c2ecf20Sopenharmony_ci} 1568c2ecf20Sopenharmony_ci 1578c2ecf20Sopenharmony_cistatic inline bool ata_id_smart_enabled(u16 *id) 1588c2ecf20Sopenharmony_ci{ 1598c2ecf20Sopenharmony_ci return id[ATA_ID_CFS_ENABLE_1] & BIT(0); 1608c2ecf20Sopenharmony_ci} 1618c2ecf20Sopenharmony_ci 1628c2ecf20Sopenharmony_cistatic int drivetemp_scsi_command(struct drivetemp_data *st, 1638c2ecf20Sopenharmony_ci u8 ata_command, u8 feature, 1648c2ecf20Sopenharmony_ci u8 lba_low, u8 lba_mid, u8 lba_high) 1658c2ecf20Sopenharmony_ci{ 1668c2ecf20Sopenharmony_ci u8 scsi_cmd[MAX_COMMAND_SIZE]; 1678c2ecf20Sopenharmony_ci int data_dir; 1688c2ecf20Sopenharmony_ci 1698c2ecf20Sopenharmony_ci memset(scsi_cmd, 0, sizeof(scsi_cmd)); 1708c2ecf20Sopenharmony_ci scsi_cmd[0] = ATA_16; 1718c2ecf20Sopenharmony_ci if (ata_command == ATA_CMD_SMART && feature == SMART_WRITE_LOG) { 1728c2ecf20Sopenharmony_ci scsi_cmd[1] = (5 << 1); /* PIO Data-out */ 1738c2ecf20Sopenharmony_ci /* 1748c2ecf20Sopenharmony_ci * No off.line or cc, write to dev, block count in sector count 1758c2ecf20Sopenharmony_ci * field. 1768c2ecf20Sopenharmony_ci */ 1778c2ecf20Sopenharmony_ci scsi_cmd[2] = 0x06; 1788c2ecf20Sopenharmony_ci data_dir = DMA_TO_DEVICE; 1798c2ecf20Sopenharmony_ci } else { 1808c2ecf20Sopenharmony_ci scsi_cmd[1] = (4 << 1); /* PIO Data-in */ 1818c2ecf20Sopenharmony_ci /* 1828c2ecf20Sopenharmony_ci * No off.line or cc, read from dev, block count in sector count 1838c2ecf20Sopenharmony_ci * field. 1848c2ecf20Sopenharmony_ci */ 1858c2ecf20Sopenharmony_ci scsi_cmd[2] = 0x0e; 1868c2ecf20Sopenharmony_ci data_dir = DMA_FROM_DEVICE; 1878c2ecf20Sopenharmony_ci } 1888c2ecf20Sopenharmony_ci scsi_cmd[4] = feature; 1898c2ecf20Sopenharmony_ci scsi_cmd[6] = 1; /* 1 sector */ 1908c2ecf20Sopenharmony_ci scsi_cmd[8] = lba_low; 1918c2ecf20Sopenharmony_ci scsi_cmd[10] = lba_mid; 1928c2ecf20Sopenharmony_ci scsi_cmd[12] = lba_high; 1938c2ecf20Sopenharmony_ci scsi_cmd[14] = ata_command; 1948c2ecf20Sopenharmony_ci 1958c2ecf20Sopenharmony_ci return scsi_execute_req(st->sdev, scsi_cmd, data_dir, 1968c2ecf20Sopenharmony_ci st->smartdata, ATA_SECT_SIZE, NULL, HZ, 5, 1978c2ecf20Sopenharmony_ci NULL); 1988c2ecf20Sopenharmony_ci} 1998c2ecf20Sopenharmony_ci 2008c2ecf20Sopenharmony_cistatic int drivetemp_ata_command(struct drivetemp_data *st, u8 feature, 2018c2ecf20Sopenharmony_ci u8 select) 2028c2ecf20Sopenharmony_ci{ 2038c2ecf20Sopenharmony_ci return drivetemp_scsi_command(st, ATA_CMD_SMART, feature, select, 2048c2ecf20Sopenharmony_ci ATA_SMART_LBAM_PASS, ATA_SMART_LBAH_PASS); 2058c2ecf20Sopenharmony_ci} 2068c2ecf20Sopenharmony_ci 2078c2ecf20Sopenharmony_cistatic int drivetemp_get_smarttemp(struct drivetemp_data *st, u32 attr, 2088c2ecf20Sopenharmony_ci long *temp) 2098c2ecf20Sopenharmony_ci{ 2108c2ecf20Sopenharmony_ci u8 *buf = st->smartdata; 2118c2ecf20Sopenharmony_ci bool have_temp = false; 2128c2ecf20Sopenharmony_ci u8 temp_raw; 2138c2ecf20Sopenharmony_ci u8 csum; 2148c2ecf20Sopenharmony_ci int err; 2158c2ecf20Sopenharmony_ci int i; 2168c2ecf20Sopenharmony_ci 2178c2ecf20Sopenharmony_ci err = drivetemp_ata_command(st, ATA_SMART_READ_VALUES, 0); 2188c2ecf20Sopenharmony_ci if (err) 2198c2ecf20Sopenharmony_ci return err; 2208c2ecf20Sopenharmony_ci 2218c2ecf20Sopenharmony_ci /* Checksum the read value table */ 2228c2ecf20Sopenharmony_ci csum = 0; 2238c2ecf20Sopenharmony_ci for (i = 0; i < ATA_SECT_SIZE; i++) 2248c2ecf20Sopenharmony_ci csum += buf[i]; 2258c2ecf20Sopenharmony_ci if (csum) { 2268c2ecf20Sopenharmony_ci dev_dbg(&st->sdev->sdev_gendev, 2278c2ecf20Sopenharmony_ci "checksum error reading SMART values\n"); 2288c2ecf20Sopenharmony_ci return -EIO; 2298c2ecf20Sopenharmony_ci } 2308c2ecf20Sopenharmony_ci 2318c2ecf20Sopenharmony_ci for (i = 0; i < ATA_MAX_SMART_ATTRS; i++) { 2328c2ecf20Sopenharmony_ci u8 *attr = buf + i * 12; 2338c2ecf20Sopenharmony_ci int id = attr[2]; 2348c2ecf20Sopenharmony_ci 2358c2ecf20Sopenharmony_ci if (!id) 2368c2ecf20Sopenharmony_ci continue; 2378c2ecf20Sopenharmony_ci 2388c2ecf20Sopenharmony_ci if (id == SMART_TEMP_PROP_190) { 2398c2ecf20Sopenharmony_ci temp_raw = attr[7]; 2408c2ecf20Sopenharmony_ci have_temp = true; 2418c2ecf20Sopenharmony_ci } 2428c2ecf20Sopenharmony_ci if (id == SMART_TEMP_PROP_194) { 2438c2ecf20Sopenharmony_ci temp_raw = attr[7]; 2448c2ecf20Sopenharmony_ci have_temp = true; 2458c2ecf20Sopenharmony_ci break; 2468c2ecf20Sopenharmony_ci } 2478c2ecf20Sopenharmony_ci } 2488c2ecf20Sopenharmony_ci 2498c2ecf20Sopenharmony_ci if (have_temp) { 2508c2ecf20Sopenharmony_ci *temp = temp_raw * 1000; 2518c2ecf20Sopenharmony_ci return 0; 2528c2ecf20Sopenharmony_ci } 2538c2ecf20Sopenharmony_ci 2548c2ecf20Sopenharmony_ci return -ENXIO; 2558c2ecf20Sopenharmony_ci} 2568c2ecf20Sopenharmony_ci 2578c2ecf20Sopenharmony_cistatic int drivetemp_get_scttemp(struct drivetemp_data *st, u32 attr, long *val) 2588c2ecf20Sopenharmony_ci{ 2598c2ecf20Sopenharmony_ci u8 *buf = st->smartdata; 2608c2ecf20Sopenharmony_ci int err; 2618c2ecf20Sopenharmony_ci 2628c2ecf20Sopenharmony_ci err = drivetemp_ata_command(st, SMART_READ_LOG, SCT_STATUS_REQ_ADDR); 2638c2ecf20Sopenharmony_ci if (err) 2648c2ecf20Sopenharmony_ci return err; 2658c2ecf20Sopenharmony_ci switch (attr) { 2668c2ecf20Sopenharmony_ci case hwmon_temp_input: 2678c2ecf20Sopenharmony_ci if (!temp_is_valid(buf[SCT_STATUS_TEMP])) 2688c2ecf20Sopenharmony_ci return -ENODATA; 2698c2ecf20Sopenharmony_ci *val = temp_from_sct(buf[SCT_STATUS_TEMP]); 2708c2ecf20Sopenharmony_ci break; 2718c2ecf20Sopenharmony_ci case hwmon_temp_lowest: 2728c2ecf20Sopenharmony_ci if (!temp_is_valid(buf[SCT_STATUS_TEMP_LOWEST])) 2738c2ecf20Sopenharmony_ci return -ENODATA; 2748c2ecf20Sopenharmony_ci *val = temp_from_sct(buf[SCT_STATUS_TEMP_LOWEST]); 2758c2ecf20Sopenharmony_ci break; 2768c2ecf20Sopenharmony_ci case hwmon_temp_highest: 2778c2ecf20Sopenharmony_ci if (!temp_is_valid(buf[SCT_STATUS_TEMP_HIGHEST])) 2788c2ecf20Sopenharmony_ci return -ENODATA; 2798c2ecf20Sopenharmony_ci *val = temp_from_sct(buf[SCT_STATUS_TEMP_HIGHEST]); 2808c2ecf20Sopenharmony_ci break; 2818c2ecf20Sopenharmony_ci default: 2828c2ecf20Sopenharmony_ci err = -EINVAL; 2838c2ecf20Sopenharmony_ci break; 2848c2ecf20Sopenharmony_ci } 2858c2ecf20Sopenharmony_ci return err; 2868c2ecf20Sopenharmony_ci} 2878c2ecf20Sopenharmony_ci 2888c2ecf20Sopenharmony_cistatic const char * const sct_avoid_models[] = { 2898c2ecf20Sopenharmony_ci/* 2908c2ecf20Sopenharmony_ci * These drives will have WRITE FPDMA QUEUED command timeouts and sometimes just 2918c2ecf20Sopenharmony_ci * freeze until power-cycled under heavy write loads when their temperature is 2928c2ecf20Sopenharmony_ci * getting polled in SCT mode. The SMART mode seems to be fine, though. 2938c2ecf20Sopenharmony_ci * 2948c2ecf20Sopenharmony_ci * While only the 3 TB model (DT01ACA3) was actually caught exhibiting the 2958c2ecf20Sopenharmony_ci * problem let's play safe here to avoid data corruption and ban the whole 2968c2ecf20Sopenharmony_ci * DT01ACAx family. 2978c2ecf20Sopenharmony_ci 2988c2ecf20Sopenharmony_ci * The models from this array are prefix-matched. 2998c2ecf20Sopenharmony_ci */ 3008c2ecf20Sopenharmony_ci "TOSHIBA DT01ACA", 3018c2ecf20Sopenharmony_ci}; 3028c2ecf20Sopenharmony_ci 3038c2ecf20Sopenharmony_cistatic bool drivetemp_sct_avoid(struct drivetemp_data *st) 3048c2ecf20Sopenharmony_ci{ 3058c2ecf20Sopenharmony_ci struct scsi_device *sdev = st->sdev; 3068c2ecf20Sopenharmony_ci unsigned int ctr; 3078c2ecf20Sopenharmony_ci 3088c2ecf20Sopenharmony_ci if (!sdev->model) 3098c2ecf20Sopenharmony_ci return false; 3108c2ecf20Sopenharmony_ci 3118c2ecf20Sopenharmony_ci /* 3128c2ecf20Sopenharmony_ci * The "model" field contains just the raw SCSI INQUIRY response 3138c2ecf20Sopenharmony_ci * "product identification" field, which has a width of 16 bytes. 3148c2ecf20Sopenharmony_ci * This field is space-filled, but is NOT NULL-terminated. 3158c2ecf20Sopenharmony_ci */ 3168c2ecf20Sopenharmony_ci for (ctr = 0; ctr < ARRAY_SIZE(sct_avoid_models); ctr++) 3178c2ecf20Sopenharmony_ci if (!strncmp(sdev->model, sct_avoid_models[ctr], 3188c2ecf20Sopenharmony_ci strlen(sct_avoid_models[ctr]))) 3198c2ecf20Sopenharmony_ci return true; 3208c2ecf20Sopenharmony_ci 3218c2ecf20Sopenharmony_ci return false; 3228c2ecf20Sopenharmony_ci} 3238c2ecf20Sopenharmony_ci 3248c2ecf20Sopenharmony_cistatic int drivetemp_identify_sata(struct drivetemp_data *st) 3258c2ecf20Sopenharmony_ci{ 3268c2ecf20Sopenharmony_ci struct scsi_device *sdev = st->sdev; 3278c2ecf20Sopenharmony_ci u8 *buf = st->smartdata; 3288c2ecf20Sopenharmony_ci struct scsi_vpd *vpd; 3298c2ecf20Sopenharmony_ci bool is_ata, is_sata; 3308c2ecf20Sopenharmony_ci bool have_sct_data_table; 3318c2ecf20Sopenharmony_ci bool have_sct_temp; 3328c2ecf20Sopenharmony_ci bool have_smart; 3338c2ecf20Sopenharmony_ci bool have_sct; 3348c2ecf20Sopenharmony_ci u16 *ata_id; 3358c2ecf20Sopenharmony_ci u16 version; 3368c2ecf20Sopenharmony_ci long temp; 3378c2ecf20Sopenharmony_ci int err; 3388c2ecf20Sopenharmony_ci 3398c2ecf20Sopenharmony_ci /* SCSI-ATA Translation present? */ 3408c2ecf20Sopenharmony_ci rcu_read_lock(); 3418c2ecf20Sopenharmony_ci vpd = rcu_dereference(sdev->vpd_pg89); 3428c2ecf20Sopenharmony_ci 3438c2ecf20Sopenharmony_ci /* 3448c2ecf20Sopenharmony_ci * Verify that ATA IDENTIFY DEVICE data is included in ATA Information 3458c2ecf20Sopenharmony_ci * VPD and that the drive implements the SATA protocol. 3468c2ecf20Sopenharmony_ci */ 3478c2ecf20Sopenharmony_ci if (!vpd || vpd->len < 572 || vpd->data[56] != ATA_CMD_ID_ATA || 3488c2ecf20Sopenharmony_ci vpd->data[36] != 0x34) { 3498c2ecf20Sopenharmony_ci rcu_read_unlock(); 3508c2ecf20Sopenharmony_ci return -ENODEV; 3518c2ecf20Sopenharmony_ci } 3528c2ecf20Sopenharmony_ci ata_id = (u16 *)&vpd->data[60]; 3538c2ecf20Sopenharmony_ci is_ata = ata_id_is_ata(ata_id); 3548c2ecf20Sopenharmony_ci is_sata = ata_id_is_sata(ata_id); 3558c2ecf20Sopenharmony_ci have_sct = ata_id_sct_supported(ata_id); 3568c2ecf20Sopenharmony_ci have_sct_data_table = ata_id_sct_data_tables(ata_id); 3578c2ecf20Sopenharmony_ci have_smart = ata_id_smart_supported(ata_id) && 3588c2ecf20Sopenharmony_ci ata_id_smart_enabled(ata_id); 3598c2ecf20Sopenharmony_ci 3608c2ecf20Sopenharmony_ci rcu_read_unlock(); 3618c2ecf20Sopenharmony_ci 3628c2ecf20Sopenharmony_ci /* bail out if this is not a SATA device */ 3638c2ecf20Sopenharmony_ci if (!is_ata || !is_sata) 3648c2ecf20Sopenharmony_ci return -ENODEV; 3658c2ecf20Sopenharmony_ci 3668c2ecf20Sopenharmony_ci if (have_sct && drivetemp_sct_avoid(st)) { 3678c2ecf20Sopenharmony_ci dev_notice(&sdev->sdev_gendev, 3688c2ecf20Sopenharmony_ci "will avoid using SCT for temperature monitoring\n"); 3698c2ecf20Sopenharmony_ci have_sct = false; 3708c2ecf20Sopenharmony_ci } 3718c2ecf20Sopenharmony_ci 3728c2ecf20Sopenharmony_ci if (!have_sct) 3738c2ecf20Sopenharmony_ci goto skip_sct; 3748c2ecf20Sopenharmony_ci 3758c2ecf20Sopenharmony_ci err = drivetemp_ata_command(st, SMART_READ_LOG, SCT_STATUS_REQ_ADDR); 3768c2ecf20Sopenharmony_ci if (err) 3778c2ecf20Sopenharmony_ci goto skip_sct; 3788c2ecf20Sopenharmony_ci 3798c2ecf20Sopenharmony_ci version = (buf[SCT_STATUS_VERSION_HIGH] << 8) | 3808c2ecf20Sopenharmony_ci buf[SCT_STATUS_VERSION_LOW]; 3818c2ecf20Sopenharmony_ci if (version != 2 && version != 3) 3828c2ecf20Sopenharmony_ci goto skip_sct; 3838c2ecf20Sopenharmony_ci 3848c2ecf20Sopenharmony_ci have_sct_temp = temp_is_valid(buf[SCT_STATUS_TEMP]); 3858c2ecf20Sopenharmony_ci if (!have_sct_temp) 3868c2ecf20Sopenharmony_ci goto skip_sct; 3878c2ecf20Sopenharmony_ci 3888c2ecf20Sopenharmony_ci st->have_temp_lowest = temp_is_valid(buf[SCT_STATUS_TEMP_LOWEST]); 3898c2ecf20Sopenharmony_ci st->have_temp_highest = temp_is_valid(buf[SCT_STATUS_TEMP_HIGHEST]); 3908c2ecf20Sopenharmony_ci 3918c2ecf20Sopenharmony_ci if (!have_sct_data_table) 3928c2ecf20Sopenharmony_ci goto skip_sct_data; 3938c2ecf20Sopenharmony_ci 3948c2ecf20Sopenharmony_ci /* Request and read temperature history table */ 3958c2ecf20Sopenharmony_ci memset(buf, '\0', sizeof(st->smartdata)); 3968c2ecf20Sopenharmony_ci buf[0] = 5; /* data table command */ 3978c2ecf20Sopenharmony_ci buf[2] = 1; /* read table */ 3988c2ecf20Sopenharmony_ci buf[4] = 2; /* temperature history table */ 3998c2ecf20Sopenharmony_ci 4008c2ecf20Sopenharmony_ci err = drivetemp_ata_command(st, SMART_WRITE_LOG, SCT_STATUS_REQ_ADDR); 4018c2ecf20Sopenharmony_ci if (err) 4028c2ecf20Sopenharmony_ci goto skip_sct_data; 4038c2ecf20Sopenharmony_ci 4048c2ecf20Sopenharmony_ci err = drivetemp_ata_command(st, SMART_READ_LOG, SCT_READ_LOG_ADDR); 4058c2ecf20Sopenharmony_ci if (err) 4068c2ecf20Sopenharmony_ci goto skip_sct_data; 4078c2ecf20Sopenharmony_ci 4088c2ecf20Sopenharmony_ci /* 4098c2ecf20Sopenharmony_ci * Temperature limits per AT Attachment 8 - 4108c2ecf20Sopenharmony_ci * ATA/ATAPI Command Set (ATA8-ACS) 4118c2ecf20Sopenharmony_ci */ 4128c2ecf20Sopenharmony_ci st->have_temp_max = temp_is_valid(buf[6]); 4138c2ecf20Sopenharmony_ci st->have_temp_crit = temp_is_valid(buf[7]); 4148c2ecf20Sopenharmony_ci st->have_temp_min = temp_is_valid(buf[8]); 4158c2ecf20Sopenharmony_ci st->have_temp_lcrit = temp_is_valid(buf[9]); 4168c2ecf20Sopenharmony_ci 4178c2ecf20Sopenharmony_ci st->temp_max = temp_from_sct(buf[6]); 4188c2ecf20Sopenharmony_ci st->temp_crit = temp_from_sct(buf[7]); 4198c2ecf20Sopenharmony_ci st->temp_min = temp_from_sct(buf[8]); 4208c2ecf20Sopenharmony_ci st->temp_lcrit = temp_from_sct(buf[9]); 4218c2ecf20Sopenharmony_ci 4228c2ecf20Sopenharmony_ciskip_sct_data: 4238c2ecf20Sopenharmony_ci if (have_sct_temp) { 4248c2ecf20Sopenharmony_ci st->get_temp = drivetemp_get_scttemp; 4258c2ecf20Sopenharmony_ci return 0; 4268c2ecf20Sopenharmony_ci } 4278c2ecf20Sopenharmony_ciskip_sct: 4288c2ecf20Sopenharmony_ci if (!have_smart) 4298c2ecf20Sopenharmony_ci return -ENODEV; 4308c2ecf20Sopenharmony_ci st->get_temp = drivetemp_get_smarttemp; 4318c2ecf20Sopenharmony_ci return drivetemp_get_smarttemp(st, hwmon_temp_input, &temp); 4328c2ecf20Sopenharmony_ci} 4338c2ecf20Sopenharmony_ci 4348c2ecf20Sopenharmony_cistatic int drivetemp_identify(struct drivetemp_data *st) 4358c2ecf20Sopenharmony_ci{ 4368c2ecf20Sopenharmony_ci struct scsi_device *sdev = st->sdev; 4378c2ecf20Sopenharmony_ci 4388c2ecf20Sopenharmony_ci /* Bail out immediately if there is no inquiry data */ 4398c2ecf20Sopenharmony_ci if (!sdev->inquiry || sdev->inquiry_len < 16) 4408c2ecf20Sopenharmony_ci return -ENODEV; 4418c2ecf20Sopenharmony_ci 4428c2ecf20Sopenharmony_ci /* Disk device? */ 4438c2ecf20Sopenharmony_ci if (sdev->type != TYPE_DISK && sdev->type != TYPE_ZBC) 4448c2ecf20Sopenharmony_ci return -ENODEV; 4458c2ecf20Sopenharmony_ci 4468c2ecf20Sopenharmony_ci return drivetemp_identify_sata(st); 4478c2ecf20Sopenharmony_ci} 4488c2ecf20Sopenharmony_ci 4498c2ecf20Sopenharmony_cistatic int drivetemp_read(struct device *dev, enum hwmon_sensor_types type, 4508c2ecf20Sopenharmony_ci u32 attr, int channel, long *val) 4518c2ecf20Sopenharmony_ci{ 4528c2ecf20Sopenharmony_ci struct drivetemp_data *st = dev_get_drvdata(dev); 4538c2ecf20Sopenharmony_ci int err = 0; 4548c2ecf20Sopenharmony_ci 4558c2ecf20Sopenharmony_ci if (type != hwmon_temp) 4568c2ecf20Sopenharmony_ci return -EINVAL; 4578c2ecf20Sopenharmony_ci 4588c2ecf20Sopenharmony_ci switch (attr) { 4598c2ecf20Sopenharmony_ci case hwmon_temp_input: 4608c2ecf20Sopenharmony_ci case hwmon_temp_lowest: 4618c2ecf20Sopenharmony_ci case hwmon_temp_highest: 4628c2ecf20Sopenharmony_ci mutex_lock(&st->lock); 4638c2ecf20Sopenharmony_ci err = st->get_temp(st, attr, val); 4648c2ecf20Sopenharmony_ci mutex_unlock(&st->lock); 4658c2ecf20Sopenharmony_ci break; 4668c2ecf20Sopenharmony_ci case hwmon_temp_lcrit: 4678c2ecf20Sopenharmony_ci *val = st->temp_lcrit; 4688c2ecf20Sopenharmony_ci break; 4698c2ecf20Sopenharmony_ci case hwmon_temp_min: 4708c2ecf20Sopenharmony_ci *val = st->temp_min; 4718c2ecf20Sopenharmony_ci break; 4728c2ecf20Sopenharmony_ci case hwmon_temp_max: 4738c2ecf20Sopenharmony_ci *val = st->temp_max; 4748c2ecf20Sopenharmony_ci break; 4758c2ecf20Sopenharmony_ci case hwmon_temp_crit: 4768c2ecf20Sopenharmony_ci *val = st->temp_crit; 4778c2ecf20Sopenharmony_ci break; 4788c2ecf20Sopenharmony_ci default: 4798c2ecf20Sopenharmony_ci err = -EINVAL; 4808c2ecf20Sopenharmony_ci break; 4818c2ecf20Sopenharmony_ci } 4828c2ecf20Sopenharmony_ci return err; 4838c2ecf20Sopenharmony_ci} 4848c2ecf20Sopenharmony_ci 4858c2ecf20Sopenharmony_cistatic umode_t drivetemp_is_visible(const void *data, 4868c2ecf20Sopenharmony_ci enum hwmon_sensor_types type, 4878c2ecf20Sopenharmony_ci u32 attr, int channel) 4888c2ecf20Sopenharmony_ci{ 4898c2ecf20Sopenharmony_ci const struct drivetemp_data *st = data; 4908c2ecf20Sopenharmony_ci 4918c2ecf20Sopenharmony_ci switch (type) { 4928c2ecf20Sopenharmony_ci case hwmon_temp: 4938c2ecf20Sopenharmony_ci switch (attr) { 4948c2ecf20Sopenharmony_ci case hwmon_temp_input: 4958c2ecf20Sopenharmony_ci return 0444; 4968c2ecf20Sopenharmony_ci case hwmon_temp_lowest: 4978c2ecf20Sopenharmony_ci if (st->have_temp_lowest) 4988c2ecf20Sopenharmony_ci return 0444; 4998c2ecf20Sopenharmony_ci break; 5008c2ecf20Sopenharmony_ci case hwmon_temp_highest: 5018c2ecf20Sopenharmony_ci if (st->have_temp_highest) 5028c2ecf20Sopenharmony_ci return 0444; 5038c2ecf20Sopenharmony_ci break; 5048c2ecf20Sopenharmony_ci case hwmon_temp_min: 5058c2ecf20Sopenharmony_ci if (st->have_temp_min) 5068c2ecf20Sopenharmony_ci return 0444; 5078c2ecf20Sopenharmony_ci break; 5088c2ecf20Sopenharmony_ci case hwmon_temp_max: 5098c2ecf20Sopenharmony_ci if (st->have_temp_max) 5108c2ecf20Sopenharmony_ci return 0444; 5118c2ecf20Sopenharmony_ci break; 5128c2ecf20Sopenharmony_ci case hwmon_temp_lcrit: 5138c2ecf20Sopenharmony_ci if (st->have_temp_lcrit) 5148c2ecf20Sopenharmony_ci return 0444; 5158c2ecf20Sopenharmony_ci break; 5168c2ecf20Sopenharmony_ci case hwmon_temp_crit: 5178c2ecf20Sopenharmony_ci if (st->have_temp_crit) 5188c2ecf20Sopenharmony_ci return 0444; 5198c2ecf20Sopenharmony_ci break; 5208c2ecf20Sopenharmony_ci default: 5218c2ecf20Sopenharmony_ci break; 5228c2ecf20Sopenharmony_ci } 5238c2ecf20Sopenharmony_ci break; 5248c2ecf20Sopenharmony_ci default: 5258c2ecf20Sopenharmony_ci break; 5268c2ecf20Sopenharmony_ci } 5278c2ecf20Sopenharmony_ci return 0; 5288c2ecf20Sopenharmony_ci} 5298c2ecf20Sopenharmony_ci 5308c2ecf20Sopenharmony_cistatic const struct hwmon_channel_info *drivetemp_info[] = { 5318c2ecf20Sopenharmony_ci HWMON_CHANNEL_INFO(chip, 5328c2ecf20Sopenharmony_ci HWMON_C_REGISTER_TZ), 5338c2ecf20Sopenharmony_ci HWMON_CHANNEL_INFO(temp, HWMON_T_INPUT | 5348c2ecf20Sopenharmony_ci HWMON_T_LOWEST | HWMON_T_HIGHEST | 5358c2ecf20Sopenharmony_ci HWMON_T_MIN | HWMON_T_MAX | 5368c2ecf20Sopenharmony_ci HWMON_T_LCRIT | HWMON_T_CRIT), 5378c2ecf20Sopenharmony_ci NULL 5388c2ecf20Sopenharmony_ci}; 5398c2ecf20Sopenharmony_ci 5408c2ecf20Sopenharmony_cistatic const struct hwmon_ops drivetemp_ops = { 5418c2ecf20Sopenharmony_ci .is_visible = drivetemp_is_visible, 5428c2ecf20Sopenharmony_ci .read = drivetemp_read, 5438c2ecf20Sopenharmony_ci}; 5448c2ecf20Sopenharmony_ci 5458c2ecf20Sopenharmony_cistatic const struct hwmon_chip_info drivetemp_chip_info = { 5468c2ecf20Sopenharmony_ci .ops = &drivetemp_ops, 5478c2ecf20Sopenharmony_ci .info = drivetemp_info, 5488c2ecf20Sopenharmony_ci}; 5498c2ecf20Sopenharmony_ci 5508c2ecf20Sopenharmony_ci/* 5518c2ecf20Sopenharmony_ci * The device argument points to sdev->sdev_dev. Its parent is 5528c2ecf20Sopenharmony_ci * sdev->sdev_gendev, which we can use to get the scsi_device pointer. 5538c2ecf20Sopenharmony_ci */ 5548c2ecf20Sopenharmony_cistatic int drivetemp_add(struct device *dev, struct class_interface *intf) 5558c2ecf20Sopenharmony_ci{ 5568c2ecf20Sopenharmony_ci struct scsi_device *sdev = to_scsi_device(dev->parent); 5578c2ecf20Sopenharmony_ci struct drivetemp_data *st; 5588c2ecf20Sopenharmony_ci int err; 5598c2ecf20Sopenharmony_ci 5608c2ecf20Sopenharmony_ci st = kzalloc(sizeof(*st), GFP_KERNEL); 5618c2ecf20Sopenharmony_ci if (!st) 5628c2ecf20Sopenharmony_ci return -ENOMEM; 5638c2ecf20Sopenharmony_ci 5648c2ecf20Sopenharmony_ci st->sdev = sdev; 5658c2ecf20Sopenharmony_ci st->dev = dev; 5668c2ecf20Sopenharmony_ci mutex_init(&st->lock); 5678c2ecf20Sopenharmony_ci 5688c2ecf20Sopenharmony_ci if (drivetemp_identify(st)) { 5698c2ecf20Sopenharmony_ci err = -ENODEV; 5708c2ecf20Sopenharmony_ci goto abort; 5718c2ecf20Sopenharmony_ci } 5728c2ecf20Sopenharmony_ci 5738c2ecf20Sopenharmony_ci st->hwdev = hwmon_device_register_with_info(dev->parent, "drivetemp", 5748c2ecf20Sopenharmony_ci st, &drivetemp_chip_info, 5758c2ecf20Sopenharmony_ci NULL); 5768c2ecf20Sopenharmony_ci if (IS_ERR(st->hwdev)) { 5778c2ecf20Sopenharmony_ci err = PTR_ERR(st->hwdev); 5788c2ecf20Sopenharmony_ci goto abort; 5798c2ecf20Sopenharmony_ci } 5808c2ecf20Sopenharmony_ci 5818c2ecf20Sopenharmony_ci list_add(&st->list, &drivetemp_devlist); 5828c2ecf20Sopenharmony_ci return 0; 5838c2ecf20Sopenharmony_ci 5848c2ecf20Sopenharmony_ciabort: 5858c2ecf20Sopenharmony_ci kfree(st); 5868c2ecf20Sopenharmony_ci return err; 5878c2ecf20Sopenharmony_ci} 5888c2ecf20Sopenharmony_ci 5898c2ecf20Sopenharmony_cistatic void drivetemp_remove(struct device *dev, struct class_interface *intf) 5908c2ecf20Sopenharmony_ci{ 5918c2ecf20Sopenharmony_ci struct drivetemp_data *st, *tmp; 5928c2ecf20Sopenharmony_ci 5938c2ecf20Sopenharmony_ci list_for_each_entry_safe(st, tmp, &drivetemp_devlist, list) { 5948c2ecf20Sopenharmony_ci if (st->dev == dev) { 5958c2ecf20Sopenharmony_ci list_del(&st->list); 5968c2ecf20Sopenharmony_ci hwmon_device_unregister(st->hwdev); 5978c2ecf20Sopenharmony_ci kfree(st); 5988c2ecf20Sopenharmony_ci break; 5998c2ecf20Sopenharmony_ci } 6008c2ecf20Sopenharmony_ci } 6018c2ecf20Sopenharmony_ci} 6028c2ecf20Sopenharmony_ci 6038c2ecf20Sopenharmony_cistatic struct class_interface drivetemp_interface = { 6048c2ecf20Sopenharmony_ci .add_dev = drivetemp_add, 6058c2ecf20Sopenharmony_ci .remove_dev = drivetemp_remove, 6068c2ecf20Sopenharmony_ci}; 6078c2ecf20Sopenharmony_ci 6088c2ecf20Sopenharmony_cistatic int __init drivetemp_init(void) 6098c2ecf20Sopenharmony_ci{ 6108c2ecf20Sopenharmony_ci return scsi_register_interface(&drivetemp_interface); 6118c2ecf20Sopenharmony_ci} 6128c2ecf20Sopenharmony_ci 6138c2ecf20Sopenharmony_cistatic void __exit drivetemp_exit(void) 6148c2ecf20Sopenharmony_ci{ 6158c2ecf20Sopenharmony_ci scsi_unregister_interface(&drivetemp_interface); 6168c2ecf20Sopenharmony_ci} 6178c2ecf20Sopenharmony_ci 6188c2ecf20Sopenharmony_cimodule_init(drivetemp_init); 6198c2ecf20Sopenharmony_cimodule_exit(drivetemp_exit); 6208c2ecf20Sopenharmony_ci 6218c2ecf20Sopenharmony_ciMODULE_AUTHOR("Guenter Roeck <linus@roeck-us.net>"); 6228c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Hard drive temperature monitor"); 6238c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 6248c2ecf20Sopenharmony_ciMODULE_ALIAS("platform:drivetemp"); 625