1 // SPDX-License-Identifier: GPL-2.0 2 /* 3 * Hardware monitoring (hwmon) support for the LSI / Broadcom mpt3sas 4 * SAS HBA driver. Exposes the IOC and board temperature sensors by 5 * reading MPI IO Unit Page 7. 6 */ 7 8 #include <linux/err.h> 9 #include <linux/hwmon.h> 10 #include <linux/slab.h> 11 12 #include "mpt3sas_base.h" 13 14 struct mpt3sas_hwmon { 15 struct MPT3SAS_ADAPTER *ioc; 16 struct device *hwmon_dev; 17 bool ioc_present; 18 bool board_present; 19 }; 20 21 /* 22 * Convert a (raw, units) reading to millidegrees Celsius. 23 * Returns -ENODATA when the sensor reports "not present" or 24 * unknown units. Temperature values are interpreted as signed 25 * two's-complement integers. 26 * 27 * The MPI2_IOUNITPAGE7_IOC_TEMP_* and MPI2_IOUNITPAGE7_BOARD_TEMP_* 28 * defines in mpi2_cnfg.h share the same values; the IOC ones are 29 * used for both channels. 30 */ 31 static int _hwmon_to_mdegc(s16 raw, u8 units, long *out) 32 { 33 switch (units) { 34 case MPI2_IOUNITPAGE7_IOC_TEMP_CELSIUS: 35 *out = (long)raw * 1000; 36 return 0; 37 case MPI2_IOUNITPAGE7_IOC_TEMP_FAHRENHEIT: 38 /* (F - 32) * 5 / 9, expressed in milli-units */ 39 *out = ((long)raw - 32) * 5000 / 9; 40 return 0; 41 default: 42 return -ENODATA; 43 } 44 } 45 46 static umode_t _hwmon_is_visible(const void *drvdata, 47 enum hwmon_sensor_types type, 48 u32 attr, int channel) 49 { 50 const struct mpt3sas_hwmon *h = drvdata; 51 52 if (type != hwmon_temp) 53 return 0; 54 if (attr != hwmon_temp_input && attr != hwmon_temp_label) 55 return 0; 56 if (channel == 0 && h->ioc_present) 57 return 0444; 58 if (channel == 1 && h->board_present) 59 return 0444; 60 return 0; 61 } 62 63 static int _hwmon_read(struct device *dev, enum hwmon_sensor_types type, 64 u32 attr, int channel, long *val) 65 { 66 struct mpt3sas_hwmon *h = dev_get_drvdata(dev); 67 Mpi2ConfigReply_t mpi_reply; 68 Mpi2IOUnitPage7_t page; 69 int r; 70 71 if (type != hwmon_temp || attr != hwmon_temp_input) 72 return -EOPNOTSUPP; 73 74 r = mpt3sas_config_get_iounit_pg7(h->ioc, &mpi_reply, &page); 75 if (r) 76 return r; 77 78 if (channel == 0) 79 return _hwmon_to_mdegc((s16)le16_to_cpu(page.IOCTemperature), 80 page.IOCTemperatureUnits, val); 81 if (channel == 1) 82 return _hwmon_to_mdegc((s16)le16_to_cpu(page.BoardTemperature), 83 page.BoardTemperatureUnits, val); 84 return -EOPNOTSUPP; 85 } 86 87 static const char * const mpt3sas_hwmon_temp_labels[] = { 88 "IOC", 89 "Board", 90 }; 91 92 static int _hwmon_read_string(struct device *dev, 93 enum hwmon_sensor_types type, 94 u32 attr, int channel, const char **str) 95 { 96 if (type != hwmon_temp || attr != hwmon_temp_label) 97 return -EOPNOTSUPP; 98 *str = mpt3sas_hwmon_temp_labels[channel]; 99 return 0; 100 } 101 102 static const struct hwmon_channel_info * const mpt3sas_hwmon_info[] = { 103 HWMON_CHANNEL_INFO(temp, 104 HWMON_T_INPUT | HWMON_T_LABEL, 105 HWMON_T_INPUT | HWMON_T_LABEL), 106 NULL, 107 }; 108 109 static const struct hwmon_ops mpt3sas_hwmon_ops = { 110 .is_visible = _hwmon_is_visible, 111 .read = _hwmon_read, 112 .read_string = _hwmon_read_string, 113 }; 114 115 static const struct hwmon_chip_info mpt3sas_hwmon_chip_info = { 116 .ops = &mpt3sas_hwmon_ops, 117 .info = mpt3sas_hwmon_info, 118 }; 119 120 /** 121 * mpt3sas_hwmon_register - register an hwmon device for the IOC 122 * @ioc: per adapter object 123 * Context: sleep. 124 * 125 * Succeeds without registering when no temperature sensors are present, 126 * so cards without thermal monitoring do not expose an empty hwmon node. 127 * Paired with mpt3sas_hwmon_unregister() from the driver's remove path. 128 * 129 * Return: 0 for success, non-zero for failure. 130 */ 131 int mpt3sas_hwmon_register(struct MPT3SAS_ADAPTER *ioc) 132 { 133 struct device *parent = &ioc->pdev->dev; 134 struct mpt3sas_hwmon *h; 135 struct device *hwdev; 136 Mpi2ConfigReply_t mpi_reply; 137 Mpi2IOUnitPage7_t page; 138 int r; 139 140 r = mpt3sas_config_get_iounit_pg7(ioc, &mpi_reply, &page); 141 if (r) 142 return r; 143 144 /* 145 * A page where both *TemperatureUnits are NOT_PRESENT covers 146 * two cases: cards that genuinely lack sensors, and firmware 147 * errors that left the page zero-filled (the accessor mirrors 148 * _config_request() behaviour). Either way: skip registration. 149 */ 150 if (page.IOCTemperatureUnits == MPI2_IOUNITPAGE7_IOC_TEMP_NOT_PRESENT && 151 page.BoardTemperatureUnits == MPI2_IOUNITPAGE7_BOARD_TEMP_NOT_PRESENT) 152 return 0; 153 154 h = kzalloc_obj(*h); 155 if (!h) 156 return -ENOMEM; 157 158 h->ioc = ioc; 159 h->ioc_present = page.IOCTemperatureUnits != MPI2_IOUNITPAGE7_IOC_TEMP_NOT_PRESENT; 160 h->board_present = page.BoardTemperatureUnits != MPI2_IOUNITPAGE7_BOARD_TEMP_NOT_PRESENT; 161 162 hwdev = hwmon_device_register_with_info(parent, "mpt3sas", h, 163 &mpt3sas_hwmon_chip_info, 164 NULL); 165 if (IS_ERR(hwdev)) { 166 kfree(h); 167 return PTR_ERR(hwdev); 168 } 169 170 h->hwmon_dev = hwdev; 171 ioc->hwmon = h; 172 return 0; 173 } 174 175 /** 176 * mpt3sas_hwmon_unregister - tear down the hwmon device, if any 177 * @ioc: per adapter object 178 * 179 * Safe to call when registration was skipped (no sensors) or 180 * failed; in those cases ioc->hwmon is NULL and this is a no-op. 181 */ 182 void mpt3sas_hwmon_unregister(struct MPT3SAS_ADAPTER *ioc) 183 { 184 struct mpt3sas_hwmon *h = ioc->hwmon; 185 186 if (!h) 187 return; 188 hwmon_device_unregister(h->hwmon_dev); 189 kfree(h); 190 ioc->hwmon = NULL; 191 } 192