Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

hwmon: (occ) Delay hwmon registration until user request

Instead of registering the hwmon device at probe time, use the
existing "occ_active" sysfs file to control when the driver polls
the OCC for sensor data and registers with hwmon. The reason for
this change is that the SBE, which is the device by which the
driver communicates with the OCC, cannot handle communications
during certain system state transitions, resulting in
unrecoverable system errors.

Signed-off-by: Eddie James <eajames@linux.ibm.com>
Link: https://lore.kernel.org/r/20220427140443.11428-1-eajames@linux.ibm.com
Signed-off-by: Guenter Roeck <linux@roeck-us.net>

authored by

Eddie James and committed by
Guenter Roeck
849b0156 c3963bc0

+157 -91
+72 -32
drivers/hwmon/occ/common.c
··· 1149 1149 sizeof(*header), size + sizeof(*header)); 1150 1150 } 1151 1151 1152 - int occ_setup(struct occ *occ, const char *name) 1152 + int occ_active(struct occ *occ, bool active) 1153 + { 1154 + int rc = mutex_lock_interruptible(&occ->lock); 1155 + 1156 + if (rc) 1157 + return rc; 1158 + 1159 + if (active) { 1160 + if (occ->active) { 1161 + rc = -EALREADY; 1162 + goto unlock; 1163 + } 1164 + 1165 + occ->error_count = 0; 1166 + occ->last_safe = 0; 1167 + 1168 + rc = occ_poll(occ); 1169 + if (rc < 0) { 1170 + dev_err(occ->bus_dev, 1171 + "failed to get OCC poll response=%02x: %d\n", 1172 + occ->resp.return_status, rc); 1173 + goto unlock; 1174 + } 1175 + 1176 + occ->active = true; 1177 + occ->next_update = jiffies + OCC_UPDATE_FREQUENCY; 1178 + occ_parse_poll_response(occ); 1179 + 1180 + rc = occ_setup_sensor_attrs(occ); 1181 + if (rc) { 1182 + dev_err(occ->bus_dev, 1183 + "failed to setup sensor attrs: %d\n", rc); 1184 + goto unlock; 1185 + } 1186 + 1187 + occ->hwmon = hwmon_device_register_with_groups(occ->bus_dev, 1188 + "occ", occ, 1189 + occ->groups); 1190 + if (IS_ERR(occ->hwmon)) { 1191 + rc = PTR_ERR(occ->hwmon); 1192 + occ->hwmon = NULL; 1193 + dev_err(occ->bus_dev, 1194 + "failed to register hwmon device: %d\n", rc); 1195 + goto unlock; 1196 + } 1197 + } else { 1198 + if (!occ->active) { 1199 + rc = -EALREADY; 1200 + goto unlock; 1201 + } 1202 + 1203 + if (occ->hwmon) 1204 + hwmon_device_unregister(occ->hwmon); 1205 + occ->active = false; 1206 + occ->hwmon = NULL; 1207 + } 1208 + 1209 + unlock: 1210 + mutex_unlock(&occ->lock); 1211 + return rc; 1212 + } 1213 + 1214 + int occ_setup(struct occ *occ) 1153 1215 { 1154 1216 int rc; 1155 1217 1156 1218 mutex_init(&occ->lock); 1157 1219 occ->groups[0] = &occ->group; 1158 - 1159 - /* no need to lock */ 1160 - rc = occ_poll(occ); 1161 - if (rc == -ESHUTDOWN) { 1162 - dev_info(occ->bus_dev, "host is not ready\n"); 1163 - return rc; 1164 - } else if (rc < 0) { 1165 - dev_err(occ->bus_dev, 1166 - "failed to get OCC poll response=%02x: %d\n", 1167 - occ->resp.return_status, rc); 1168 - return rc; 1169 - } 1170 - 1171 - occ->next_update = jiffies + OCC_UPDATE_FREQUENCY; 1172 - occ_parse_poll_response(occ); 1173 - 1174 - rc = occ_setup_sensor_attrs(occ); 1175 - if (rc) { 1176 - dev_err(occ->bus_dev, "failed to setup sensor attrs: %d\n", 1177 - rc); 1178 - return rc; 1179 - } 1180 - 1181 - occ->hwmon = devm_hwmon_device_register_with_groups(occ->bus_dev, name, 1182 - occ, occ->groups); 1183 - if (IS_ERR(occ->hwmon)) { 1184 - rc = PTR_ERR(occ->hwmon); 1185 - dev_err(occ->bus_dev, "failed to register hwmon device: %d\n", 1186 - rc); 1187 - return rc; 1188 - } 1189 1220 1190 1221 rc = occ_setup_sysfs(occ); 1191 1222 if (rc) ··· 1225 1194 return rc; 1226 1195 } 1227 1196 EXPORT_SYMBOL_GPL(occ_setup); 1197 + 1198 + void occ_shutdown(struct occ *occ) 1199 + { 1200 + occ_shutdown_sysfs(occ); 1201 + 1202 + if (occ->hwmon) 1203 + hwmon_device_unregister(occ->hwmon); 1204 + } 1205 + EXPORT_SYMBOL_GPL(occ_shutdown); 1228 1206 1229 1207 MODULE_AUTHOR("Eddie James <eajames@linux.ibm.com>"); 1230 1208 MODULE_DESCRIPTION("Common OCC hwmon code");
+4 -1
drivers/hwmon/occ/common.h
··· 106 106 struct attribute_group group; 107 107 const struct attribute_group *groups[2]; 108 108 109 + bool active; 109 110 int error; /* final transfer error after retry */ 110 111 int last_error; /* latest transfer error */ 111 112 unsigned int error_count; /* number of xfr errors observed */ ··· 124 123 u8 prev_mode; 125 124 }; 126 125 127 - int occ_setup(struct occ *occ, const char *name); 126 + int occ_active(struct occ *occ, bool active); 127 + int occ_setup(struct occ *occ); 128 128 int occ_setup_sysfs(struct occ *occ); 129 129 void occ_shutdown(struct occ *occ); 130 + void occ_shutdown_sysfs(struct occ *occ); 130 131 void occ_sysfs_poll_done(struct occ *occ); 131 132 int occ_update_response(struct occ *occ); 132 133
+1 -1
drivers/hwmon/occ/p8_i2c.c
··· 223 223 occ->poll_cmd_data = 0x10; /* P8 OCC poll data */ 224 224 occ->send_cmd = p8_i2c_occ_send_cmd; 225 225 226 - return occ_setup(occ, "p8_occ"); 226 + return occ_setup(occ); 227 227 } 228 228 229 229 static int p8_i2c_occ_remove(struct i2c_client *client)
+1 -1
drivers/hwmon/occ/p9_sbe.c
··· 145 145 occ->poll_cmd_data = 0x20; /* P9 OCC poll data */ 146 146 occ->send_cmd = p9_sbe_occ_send_cmd; 147 147 148 - rc = occ_setup(occ, "p9_occ"); 148 + rc = occ_setup(occ); 149 149 if (rc == -ESHUTDOWN) 150 150 rc = -ENODEV; /* Host is shutdown, don't spew errors */ 151 151
+79 -56
drivers/hwmon/occ/sysfs.c
··· 6 6 #include <linux/export.h> 7 7 #include <linux/hwmon-sysfs.h> 8 8 #include <linux/kernel.h> 9 + #include <linux/kstrtox.h> 9 10 #include <linux/sysfs.h> 10 11 11 12 #include "common.h" 12 13 13 14 /* OCC status register */ 14 15 #define OCC_STAT_MASTER BIT(7) 15 - #define OCC_STAT_ACTIVE BIT(0) 16 16 17 17 /* OCC extended status register */ 18 18 #define OCC_EXT_STAT_DVFS_OT BIT(7) ··· 21 21 #define OCC_EXT_STAT_QUICK_DROP BIT(4) 22 22 #define OCC_EXT_STAT_DVFS_VDD BIT(3) 23 23 #define OCC_EXT_STAT_GPU_THROTTLE GENMASK(2, 0) 24 + 25 + static ssize_t occ_active_store(struct device *dev, 26 + struct device_attribute *attr, 27 + const char *buf, size_t count) 28 + { 29 + int rc; 30 + bool active; 31 + struct occ *occ = dev_get_drvdata(dev); 32 + 33 + rc = kstrtobool(buf, &active); 34 + if (rc) 35 + return rc; 36 + 37 + rc = occ_active(occ, active); 38 + if (rc) 39 + return rc; 40 + 41 + return count; 42 + } 24 43 25 44 static ssize_t occ_sysfs_show(struct device *dev, 26 45 struct device_attribute *attr, char *buf) ··· 50 31 struct occ_poll_response_header *header; 51 32 struct sensor_device_attribute *sattr = to_sensor_dev_attr(attr); 52 33 53 - rc = occ_update_response(occ); 54 - if (rc) 55 - return rc; 34 + if (occ->active) { 35 + rc = occ_update_response(occ); 36 + if (rc) 37 + return rc; 56 38 57 - header = (struct occ_poll_response_header *)occ->resp.data; 39 + header = (struct occ_poll_response_header *)occ->resp.data; 58 40 59 - switch (sattr->index) { 60 - case 0: 61 - val = !!(header->status & OCC_STAT_MASTER); 62 - break; 63 - case 1: 64 - val = !!(header->status & OCC_STAT_ACTIVE); 65 - break; 66 - case 2: 67 - val = !!(header->ext_status & OCC_EXT_STAT_DVFS_OT); 68 - break; 69 - case 3: 70 - val = !!(header->ext_status & OCC_EXT_STAT_DVFS_POWER); 71 - break; 72 - case 4: 73 - val = !!(header->ext_status & OCC_EXT_STAT_MEM_THROTTLE); 74 - break; 75 - case 5: 76 - val = !!(header->ext_status & OCC_EXT_STAT_QUICK_DROP); 77 - break; 78 - case 6: 79 - val = header->occ_state; 80 - break; 81 - case 7: 82 - if (header->status & OCC_STAT_MASTER) 83 - val = hweight8(header->occs_present); 84 - else 41 + switch (sattr->index) { 42 + case 0: 43 + val = !!(header->status & OCC_STAT_MASTER); 44 + break; 45 + case 1: 85 46 val = 1; 86 - break; 87 - case 8: 88 - val = header->ips_status; 89 - break; 90 - case 9: 91 - val = header->mode; 92 - break; 93 - case 10: 94 - val = !!(header->ext_status & OCC_EXT_STAT_DVFS_VDD); 95 - break; 96 - case 11: 97 - val = header->ext_status & OCC_EXT_STAT_GPU_THROTTLE; 98 - break; 99 - default: 100 - return -EINVAL; 47 + break; 48 + case 2: 49 + val = !!(header->ext_status & OCC_EXT_STAT_DVFS_OT); 50 + break; 51 + case 3: 52 + val = !!(header->ext_status & OCC_EXT_STAT_DVFS_POWER); 53 + break; 54 + case 4: 55 + val = !!(header->ext_status & 56 + OCC_EXT_STAT_MEM_THROTTLE); 57 + break; 58 + case 5: 59 + val = !!(header->ext_status & OCC_EXT_STAT_QUICK_DROP); 60 + break; 61 + case 6: 62 + val = header->occ_state; 63 + break; 64 + case 7: 65 + if (header->status & OCC_STAT_MASTER) 66 + val = hweight8(header->occs_present); 67 + else 68 + val = 1; 69 + break; 70 + case 8: 71 + val = header->ips_status; 72 + break; 73 + case 9: 74 + val = header->mode; 75 + break; 76 + case 10: 77 + val = !!(header->ext_status & OCC_EXT_STAT_DVFS_VDD); 78 + break; 79 + case 11: 80 + val = header->ext_status & OCC_EXT_STAT_GPU_THROTTLE; 81 + break; 82 + default: 83 + return -EINVAL; 84 + } 85 + } else { 86 + if (sattr->index == 1) 87 + val = 0; 88 + else if (sattr->index <= 11) 89 + val = -ENODATA; 90 + else 91 + return -EINVAL; 101 92 } 102 93 103 94 return sysfs_emit(buf, "%d\n", val); ··· 124 95 } 125 96 126 97 static SENSOR_DEVICE_ATTR(occ_master, 0444, occ_sysfs_show, NULL, 0); 127 - static SENSOR_DEVICE_ATTR(occ_active, 0444, occ_sysfs_show, NULL, 1); 98 + static SENSOR_DEVICE_ATTR(occ_active, 0644, occ_sysfs_show, occ_active_store, 99 + 1); 128 100 static SENSOR_DEVICE_ATTR(occ_dvfs_overtemp, 0444, occ_sysfs_show, NULL, 2); 129 101 static SENSOR_DEVICE_ATTR(occ_dvfs_power, 0444, occ_sysfs_show, NULL, 3); 130 102 static SENSOR_DEVICE_ATTR(occ_mem_throttle, 0444, occ_sysfs_show, NULL, 4); ··· 169 139 * On the first poll response, we haven't yet created the sysfs 170 140 * attributes, so don't make any notify calls. 171 141 */ 172 - if (!occ->hwmon) 142 + if (!occ->active) 173 143 goto done; 174 144 175 145 if ((header->status & OCC_STAT_MASTER) != 176 146 (occ->prev_stat & OCC_STAT_MASTER)) { 177 147 name = sensor_dev_attr_occ_master.dev_attr.attr.name; 178 - sysfs_notify(&occ->bus_dev->kobj, NULL, name); 179 - } 180 - 181 - if ((header->status & OCC_STAT_ACTIVE) != 182 - (occ->prev_stat & OCC_STAT_ACTIVE)) { 183 - name = sensor_dev_attr_occ_active.dev_attr.attr.name; 184 148 sysfs_notify(&occ->bus_dev->kobj, NULL, name); 185 149 } 186 150 ··· 251 227 return sysfs_create_group(&occ->bus_dev->kobj, &occ_sysfs); 252 228 } 253 229 254 - void occ_shutdown(struct occ *occ) 230 + void occ_shutdown_sysfs(struct occ *occ) 255 231 { 256 232 sysfs_remove_group(&occ->bus_dev->kobj, &occ_sysfs); 257 233 } 258 - EXPORT_SYMBOL_GPL(occ_shutdown);