1e2f05d60SEddie James // SPDX-License-Identifier: GPL-2.0+ 2e2f05d60SEddie James // Copyright IBM Corp 2019 35b5513b8SEddie James 45b5513b8SEddie James #include <linux/device.h> 55679ed99SJean Delvare #include <linux/export.h> 654076cb3SEddie James #include <linux/hwmon.h> 7c10e753dSEddie James #include <linux/hwmon-sysfs.h> 8c10e753dSEddie James #include <linux/jiffies.h> 9aa195fe4SEddie James #include <linux/kernel.h> 10c10e753dSEddie James #include <linux/math64.h> 115679ed99SJean Delvare #include <linux/module.h> 12c10e753dSEddie James #include <linux/mutex.h> 1354076cb3SEddie James #include <linux/sysfs.h> 14c10e753dSEddie James #include <asm/unaligned.h> 155b5513b8SEddie James 165b5513b8SEddie James #include "common.h" 175b5513b8SEddie James 18c10e753dSEddie James #define EXTN_FLAG_SENSOR_ID BIT(7) 19c10e753dSEddie James 20df04ced6SEddie James #define OCC_ERROR_COUNT_THRESHOLD 2 /* required by OCC spec */ 21df04ced6SEddie James 22df04ced6SEddie James #define OCC_STATE_SAFE 4 23df04ced6SEddie James #define OCC_SAFE_TIMEOUT msecs_to_jiffies(60000) /* 1 min */ 24df04ced6SEddie James 25c10e753dSEddie James #define OCC_UPDATE_FREQUENCY msecs_to_jiffies(1000) 26c10e753dSEddie James 27c10e753dSEddie James #define OCC_TEMP_SENSOR_FAULT 0xFF 28c10e753dSEddie James 29c10e753dSEddie James #define OCC_FRU_TYPE_VRM 3 30c10e753dSEddie James 31c10e753dSEddie James /* OCC sensor type and version definitions */ 32c10e753dSEddie James 33c10e753dSEddie James struct temp_sensor_1 { 34c10e753dSEddie James u16 sensor_id; 35c10e753dSEddie James u16 value; 36c10e753dSEddie James } __packed; 37c10e753dSEddie James 38c10e753dSEddie James struct temp_sensor_2 { 39c10e753dSEddie James u32 sensor_id; 40c10e753dSEddie James u8 fru_type; 41c10e753dSEddie James u8 value; 42c10e753dSEddie James } __packed; 43c10e753dSEddie James 44db4919ecSEddie James struct temp_sensor_10 { 45db4919ecSEddie James u32 sensor_id; 46db4919ecSEddie James u8 fru_type; 47db4919ecSEddie James u8 value; 48db4919ecSEddie James u8 throttle; 49db4919ecSEddie James u8 reserved; 50db4919ecSEddie James } __packed; 51db4919ecSEddie James 52c10e753dSEddie James struct freq_sensor_1 { 53c10e753dSEddie James u16 sensor_id; 54c10e753dSEddie James u16 value; 55c10e753dSEddie James } __packed; 56c10e753dSEddie James 57c10e753dSEddie James struct freq_sensor_2 { 58c10e753dSEddie James u32 sensor_id; 59c10e753dSEddie James u16 value; 60c10e753dSEddie James } __packed; 61c10e753dSEddie James 62c10e753dSEddie James struct power_sensor_1 { 63c10e753dSEddie James u16 sensor_id; 64c10e753dSEddie James u32 update_tag; 65c10e753dSEddie James u32 accumulator; 66c10e753dSEddie James u16 value; 67c10e753dSEddie James } __packed; 68c10e753dSEddie James 69c10e753dSEddie James struct power_sensor_2 { 70c10e753dSEddie James u32 sensor_id; 71c10e753dSEddie James u8 function_id; 72c10e753dSEddie James u8 apss_channel; 73c10e753dSEddie James u16 reserved; 74c10e753dSEddie James u32 update_tag; 75c10e753dSEddie James u64 accumulator; 76c10e753dSEddie James u16 value; 77c10e753dSEddie James } __packed; 78c10e753dSEddie James 79c10e753dSEddie James struct power_sensor_data { 80c10e753dSEddie James u16 value; 81c10e753dSEddie James u32 update_tag; 82c10e753dSEddie James u64 accumulator; 83c10e753dSEddie James } __packed; 84c10e753dSEddie James 85c10e753dSEddie James struct power_sensor_data_and_time { 86c10e753dSEddie James u16 update_time; 87c10e753dSEddie James u16 value; 88c10e753dSEddie James u32 update_tag; 89c10e753dSEddie James u64 accumulator; 90c10e753dSEddie James } __packed; 91c10e753dSEddie James 92c10e753dSEddie James struct power_sensor_a0 { 93c10e753dSEddie James u32 sensor_id; 94c10e753dSEddie James struct power_sensor_data_and_time system; 95c10e753dSEddie James u32 reserved; 96c10e753dSEddie James struct power_sensor_data_and_time proc; 97c10e753dSEddie James struct power_sensor_data vdd; 98c10e753dSEddie James struct power_sensor_data vdn; 99c10e753dSEddie James } __packed; 100c10e753dSEddie James 101c10e753dSEddie James struct caps_sensor_2 { 102c10e753dSEddie James u16 cap; 103c10e753dSEddie James u16 system_power; 104c10e753dSEddie James u16 n_cap; 105c10e753dSEddie James u16 max; 106c10e753dSEddie James u16 min; 107c10e753dSEddie James u16 user; 108c10e753dSEddie James u8 user_source; 109c10e753dSEddie James } __packed; 110c10e753dSEddie James 111c10e753dSEddie James struct caps_sensor_3 { 112c10e753dSEddie James u16 cap; 113c10e753dSEddie James u16 system_power; 114c10e753dSEddie James u16 n_cap; 115c10e753dSEddie James u16 max; 116c10e753dSEddie James u16 hard_min; 117c10e753dSEddie James u16 soft_min; 118c10e753dSEddie James u16 user; 119c10e753dSEddie James u8 user_source; 120c10e753dSEddie James } __packed; 121c10e753dSEddie James 122c10e753dSEddie James struct extended_sensor { 123c10e753dSEddie James union { 124c10e753dSEddie James u8 name[4]; 125c10e753dSEddie James u32 sensor_id; 126c10e753dSEddie James }; 127c10e753dSEddie James u8 flags; 128c10e753dSEddie James u8 reserved; 129c10e753dSEddie James u8 data[6]; 130c10e753dSEddie James } __packed; 131c10e753dSEddie James 1325b5513b8SEddie James static int occ_poll(struct occ *occ) 1335b5513b8SEddie James { 134df04ced6SEddie James int rc; 135908dbf02SEddie James u8 cmd[7]; 136df04ced6SEddie James struct occ_poll_response_header *header; 1375b5513b8SEddie James 1385b5513b8SEddie James /* big endian */ 139908dbf02SEddie James cmd[0] = 0; /* sequence number */ 1405b5513b8SEddie James cmd[1] = 0; /* cmd type */ 1415b5513b8SEddie James cmd[2] = 0; /* data length msb */ 1425b5513b8SEddie James cmd[3] = 1; /* data length lsb */ 1435b5513b8SEddie James cmd[4] = occ->poll_cmd_data; /* data */ 144908dbf02SEddie James cmd[5] = 0; /* checksum msb */ 145908dbf02SEddie James cmd[6] = 0; /* checksum lsb */ 1465b5513b8SEddie James 147c10e753dSEddie James /* mutex should already be locked if necessary */ 148d0fef244SEddie James rc = occ->send_cmd(occ, cmd, sizeof(cmd), &occ->resp, sizeof(occ->resp)); 149df04ced6SEddie James if (rc) { 150b5c46a53SEddie James occ->last_error = rc; 151df04ced6SEddie James if (occ->error_count++ > OCC_ERROR_COUNT_THRESHOLD) 152df04ced6SEddie James occ->error = rc; 153df04ced6SEddie James 154df04ced6SEddie James goto done; 155df04ced6SEddie James } 156df04ced6SEddie James 157df04ced6SEddie James /* clear error since communication was successful */ 158df04ced6SEddie James occ->error_count = 0; 159b5c46a53SEddie James occ->last_error = 0; 160df04ced6SEddie James occ->error = 0; 161df04ced6SEddie James 162df04ced6SEddie James /* check for safe state */ 163df04ced6SEddie James header = (struct occ_poll_response_header *)occ->resp.data; 164df04ced6SEddie James if (header->occ_state == OCC_STATE_SAFE) { 165df04ced6SEddie James if (occ->last_safe) { 166df04ced6SEddie James if (time_after(jiffies, 167df04ced6SEddie James occ->last_safe + OCC_SAFE_TIMEOUT)) 168df04ced6SEddie James occ->error = -EHOSTDOWN; 169df04ced6SEddie James } else { 170df04ced6SEddie James occ->last_safe = jiffies; 171df04ced6SEddie James } 172df04ced6SEddie James } else { 173df04ced6SEddie James occ->last_safe = 0; 174df04ced6SEddie James } 175df04ced6SEddie James 176df04ced6SEddie James done: 177df04ced6SEddie James occ_sysfs_poll_done(occ); 178df04ced6SEddie James return rc; 1795b5513b8SEddie James } 1805b5513b8SEddie James 181c10e753dSEddie James static int occ_set_user_power_cap(struct occ *occ, u16 user_power_cap) 182c10e753dSEddie James { 183c10e753dSEddie James int rc; 184c10e753dSEddie James u8 cmd[8]; 185d0fef244SEddie James u8 resp[8]; 186c10e753dSEddie James __be16 user_power_cap_be = cpu_to_be16(user_power_cap); 187c10e753dSEddie James 188908dbf02SEddie James cmd[0] = 0; /* sequence number */ 189908dbf02SEddie James cmd[1] = 0x22; /* cmd type */ 190908dbf02SEddie James cmd[2] = 0; /* data length msb */ 191908dbf02SEddie James cmd[3] = 2; /* data length lsb */ 192c10e753dSEddie James 193c10e753dSEddie James memcpy(&cmd[4], &user_power_cap_be, 2); 194c10e753dSEddie James 195908dbf02SEddie James cmd[6] = 0; /* checksum msb */ 196908dbf02SEddie James cmd[7] = 0; /* checksum lsb */ 197c10e753dSEddie James 198c10e753dSEddie James rc = mutex_lock_interruptible(&occ->lock); 199c10e753dSEddie James if (rc) 200c10e753dSEddie James return rc; 201c10e753dSEddie James 202d0fef244SEddie James rc = occ->send_cmd(occ, cmd, sizeof(cmd), resp, sizeof(resp)); 203c10e753dSEddie James 204c10e753dSEddie James mutex_unlock(&occ->lock); 205c10e753dSEddie James 206c10e753dSEddie James return rc; 207c10e753dSEddie James } 208c10e753dSEddie James 209df04ced6SEddie James int occ_update_response(struct occ *occ) 210c10e753dSEddie James { 211c10e753dSEddie James int rc = mutex_lock_interruptible(&occ->lock); 212c10e753dSEddie James 213c10e753dSEddie James if (rc) 214c10e753dSEddie James return rc; 215c10e753dSEddie James 216c10e753dSEddie James /* limit the maximum rate of polling the OCC */ 2175216dff2SEddie James if (time_after(jiffies, occ->next_update)) { 218c10e753dSEddie James rc = occ_poll(occ); 2195216dff2SEddie James occ->next_update = jiffies + OCC_UPDATE_FREQUENCY; 220b5c46a53SEddie James } else { 221b5c46a53SEddie James rc = occ->last_error; 222c10e753dSEddie James } 223c10e753dSEddie James 224c10e753dSEddie James mutex_unlock(&occ->lock); 225c10e753dSEddie James return rc; 226c10e753dSEddie James } 227c10e753dSEddie James 228c10e753dSEddie James static ssize_t occ_show_temp_1(struct device *dev, 229c10e753dSEddie James struct device_attribute *attr, char *buf) 230c10e753dSEddie James { 231c10e753dSEddie James int rc; 232c10e753dSEddie James u32 val = 0; 233c10e753dSEddie James struct temp_sensor_1 *temp; 234c10e753dSEddie James struct occ *occ = dev_get_drvdata(dev); 235c10e753dSEddie James struct occ_sensors *sensors = &occ->sensors; 236c10e753dSEddie James struct sensor_device_attribute_2 *sattr = to_sensor_dev_attr_2(attr); 237c10e753dSEddie James 238c10e753dSEddie James rc = occ_update_response(occ); 239c10e753dSEddie James if (rc) 240c10e753dSEddie James return rc; 241c10e753dSEddie James 242c10e753dSEddie James temp = ((struct temp_sensor_1 *)sensors->temp.data) + sattr->index; 243c10e753dSEddie James 244c10e753dSEddie James switch (sattr->nr) { 245c10e753dSEddie James case 0: 246c10e753dSEddie James val = get_unaligned_be16(&temp->sensor_id); 247c10e753dSEddie James break; 248c10e753dSEddie James case 1: 24980830342SAlexander Soldatov /* 25080830342SAlexander Soldatov * If a sensor reading has expired and couldn't be refreshed, 25180830342SAlexander Soldatov * OCC returns 0xFFFF for that sensor. 25280830342SAlexander Soldatov */ 25380830342SAlexander Soldatov if (temp->value == 0xFFFF) 25480830342SAlexander Soldatov return -EREMOTEIO; 255c10e753dSEddie James val = get_unaligned_be16(&temp->value) * 1000; 256c10e753dSEddie James break; 257c10e753dSEddie James default: 258c10e753dSEddie James return -EINVAL; 259c10e753dSEddie James } 260c10e753dSEddie James 2611f4d4af4SGuenter Roeck return sysfs_emit(buf, "%u\n", val); 262c10e753dSEddie James } 263c10e753dSEddie James 264c10e753dSEddie James static ssize_t occ_show_temp_2(struct device *dev, 265c10e753dSEddie James struct device_attribute *attr, char *buf) 266c10e753dSEddie James { 267c10e753dSEddie James int rc; 268c10e753dSEddie James u32 val = 0; 269c10e753dSEddie James struct temp_sensor_2 *temp; 270c10e753dSEddie James struct occ *occ = dev_get_drvdata(dev); 271c10e753dSEddie James struct occ_sensors *sensors = &occ->sensors; 272c10e753dSEddie James struct sensor_device_attribute_2 *sattr = to_sensor_dev_attr_2(attr); 273c10e753dSEddie James 274c10e753dSEddie James rc = occ_update_response(occ); 275c10e753dSEddie James if (rc) 276c10e753dSEddie James return rc; 277c10e753dSEddie James 278c10e753dSEddie James temp = ((struct temp_sensor_2 *)sensors->temp.data) + sattr->index; 279c10e753dSEddie James 280c10e753dSEddie James switch (sattr->nr) { 281c10e753dSEddie James case 0: 282c10e753dSEddie James val = get_unaligned_be32(&temp->sensor_id); 283c10e753dSEddie James break; 284c10e753dSEddie James case 1: 285c10e753dSEddie James val = temp->value; 286c10e753dSEddie James if (val == OCC_TEMP_SENSOR_FAULT) 287c10e753dSEddie James return -EREMOTEIO; 288c10e753dSEddie James 289c10e753dSEddie James /* 290c10e753dSEddie James * VRM doesn't return temperature, only alarm bit. This 291c10e753dSEddie James * attribute maps to tempX_alarm instead of tempX_input for 292c10e753dSEddie James * VRM 293c10e753dSEddie James */ 294c10e753dSEddie James if (temp->fru_type != OCC_FRU_TYPE_VRM) { 295c10e753dSEddie James /* sensor not ready */ 296c10e753dSEddie James if (val == 0) 297c10e753dSEddie James return -EAGAIN; 298c10e753dSEddie James 299c10e753dSEddie James val *= 1000; 300c10e753dSEddie James } 301c10e753dSEddie James break; 302c10e753dSEddie James case 2: 303c10e753dSEddie James val = temp->fru_type; 304c10e753dSEddie James break; 305c10e753dSEddie James case 3: 306c10e753dSEddie James val = temp->value == OCC_TEMP_SENSOR_FAULT; 307c10e753dSEddie James break; 308c10e753dSEddie James default: 309c10e753dSEddie James return -EINVAL; 310c10e753dSEddie James } 311c10e753dSEddie James 3121f4d4af4SGuenter Roeck return sysfs_emit(buf, "%u\n", val); 313c10e753dSEddie James } 314c10e753dSEddie James 315db4919ecSEddie James static ssize_t occ_show_temp_10(struct device *dev, 316db4919ecSEddie James struct device_attribute *attr, char *buf) 317db4919ecSEddie James { 318db4919ecSEddie James int rc; 319db4919ecSEddie James u32 val = 0; 320db4919ecSEddie James struct temp_sensor_10 *temp; 321db4919ecSEddie James struct occ *occ = dev_get_drvdata(dev); 322db4919ecSEddie James struct occ_sensors *sensors = &occ->sensors; 323db4919ecSEddie James struct sensor_device_attribute_2 *sattr = to_sensor_dev_attr_2(attr); 324db4919ecSEddie James 325db4919ecSEddie James rc = occ_update_response(occ); 326db4919ecSEddie James if (rc) 327db4919ecSEddie James return rc; 328db4919ecSEddie James 329db4919ecSEddie James temp = ((struct temp_sensor_10 *)sensors->temp.data) + sattr->index; 330db4919ecSEddie James 331db4919ecSEddie James switch (sattr->nr) { 332db4919ecSEddie James case 0: 333db4919ecSEddie James val = get_unaligned_be32(&temp->sensor_id); 334db4919ecSEddie James break; 335db4919ecSEddie James case 1: 336db4919ecSEddie James val = temp->value; 337db4919ecSEddie James if (val == OCC_TEMP_SENSOR_FAULT) 338db4919ecSEddie James return -EREMOTEIO; 339db4919ecSEddie James 340db4919ecSEddie James /* sensor not ready */ 341db4919ecSEddie James if (val == 0) 342db4919ecSEddie James return -EAGAIN; 343db4919ecSEddie James 344db4919ecSEddie James val *= 1000; 345db4919ecSEddie James break; 346db4919ecSEddie James case 2: 347db4919ecSEddie James val = temp->fru_type; 348db4919ecSEddie James break; 349db4919ecSEddie James case 3: 350db4919ecSEddie James val = temp->value == OCC_TEMP_SENSOR_FAULT; 351db4919ecSEddie James break; 352db4919ecSEddie James case 4: 353db4919ecSEddie James val = temp->throttle * 1000; 354db4919ecSEddie James break; 355db4919ecSEddie James default: 356db4919ecSEddie James return -EINVAL; 357db4919ecSEddie James } 358db4919ecSEddie James 3591f4d4af4SGuenter Roeck return sysfs_emit(buf, "%u\n", val); 360db4919ecSEddie James } 361db4919ecSEddie James 362c10e753dSEddie James static ssize_t occ_show_freq_1(struct device *dev, 363c10e753dSEddie James struct device_attribute *attr, char *buf) 364c10e753dSEddie James { 365c10e753dSEddie James int rc; 366c10e753dSEddie James u16 val = 0; 367c10e753dSEddie James struct freq_sensor_1 *freq; 368c10e753dSEddie James struct occ *occ = dev_get_drvdata(dev); 369c10e753dSEddie James struct occ_sensors *sensors = &occ->sensors; 370c10e753dSEddie James struct sensor_device_attribute_2 *sattr = to_sensor_dev_attr_2(attr); 371c10e753dSEddie James 372c10e753dSEddie James rc = occ_update_response(occ); 373c10e753dSEddie James if (rc) 374c10e753dSEddie James return rc; 375c10e753dSEddie James 376c10e753dSEddie James freq = ((struct freq_sensor_1 *)sensors->freq.data) + sattr->index; 377c10e753dSEddie James 378c10e753dSEddie James switch (sattr->nr) { 379c10e753dSEddie James case 0: 380c10e753dSEddie James val = get_unaligned_be16(&freq->sensor_id); 381c10e753dSEddie James break; 382c10e753dSEddie James case 1: 383c10e753dSEddie James val = get_unaligned_be16(&freq->value); 384c10e753dSEddie James break; 385c10e753dSEddie James default: 386c10e753dSEddie James return -EINVAL; 387c10e753dSEddie James } 388c10e753dSEddie James 3891f4d4af4SGuenter Roeck return sysfs_emit(buf, "%u\n", val); 390c10e753dSEddie James } 391c10e753dSEddie James 392c10e753dSEddie James static ssize_t occ_show_freq_2(struct device *dev, 393c10e753dSEddie James struct device_attribute *attr, char *buf) 394c10e753dSEddie James { 395c10e753dSEddie James int rc; 396c10e753dSEddie James u32 val = 0; 397c10e753dSEddie James struct freq_sensor_2 *freq; 398c10e753dSEddie James struct occ *occ = dev_get_drvdata(dev); 399c10e753dSEddie James struct occ_sensors *sensors = &occ->sensors; 400c10e753dSEddie James struct sensor_device_attribute_2 *sattr = to_sensor_dev_attr_2(attr); 401c10e753dSEddie James 402c10e753dSEddie James rc = occ_update_response(occ); 403c10e753dSEddie James if (rc) 404c10e753dSEddie James return rc; 405c10e753dSEddie James 406c10e753dSEddie James freq = ((struct freq_sensor_2 *)sensors->freq.data) + sattr->index; 407c10e753dSEddie James 408c10e753dSEddie James switch (sattr->nr) { 409c10e753dSEddie James case 0: 410c10e753dSEddie James val = get_unaligned_be32(&freq->sensor_id); 411c10e753dSEddie James break; 412c10e753dSEddie James case 1: 413c10e753dSEddie James val = get_unaligned_be16(&freq->value); 414c10e753dSEddie James break; 415c10e753dSEddie James default: 416c10e753dSEddie James return -EINVAL; 417c10e753dSEddie James } 418c10e753dSEddie James 4191f4d4af4SGuenter Roeck return sysfs_emit(buf, "%u\n", val); 420c10e753dSEddie James } 421c10e753dSEddie James 422c10e753dSEddie James static ssize_t occ_show_power_1(struct device *dev, 423c10e753dSEddie James struct device_attribute *attr, char *buf) 424c10e753dSEddie James { 425c10e753dSEddie James int rc; 426c10e753dSEddie James u64 val = 0; 427c10e753dSEddie James struct power_sensor_1 *power; 428c10e753dSEddie James struct occ *occ = dev_get_drvdata(dev); 429c10e753dSEddie James struct occ_sensors *sensors = &occ->sensors; 430c10e753dSEddie James struct sensor_device_attribute_2 *sattr = to_sensor_dev_attr_2(attr); 431c10e753dSEddie James 432c10e753dSEddie James rc = occ_update_response(occ); 433c10e753dSEddie James if (rc) 434c10e753dSEddie James return rc; 435c10e753dSEddie James 436c10e753dSEddie James power = ((struct power_sensor_1 *)sensors->power.data) + sattr->index; 437c10e753dSEddie James 438c10e753dSEddie James switch (sattr->nr) { 439c10e753dSEddie James case 0: 440c10e753dSEddie James val = get_unaligned_be16(&power->sensor_id); 441c10e753dSEddie James break; 442c10e753dSEddie James case 1: 443c10e753dSEddie James val = get_unaligned_be32(&power->accumulator) / 444c10e753dSEddie James get_unaligned_be32(&power->update_tag); 445c10e753dSEddie James val *= 1000000ULL; 446c10e753dSEddie James break; 447c10e753dSEddie James case 2: 448b0407d82SGustavo A. R. Silva val = (u64)get_unaligned_be32(&power->update_tag) * 449c10e753dSEddie James occ->powr_sample_time_us; 450c10e753dSEddie James break; 451c10e753dSEddie James case 3: 452c10e753dSEddie James val = get_unaligned_be16(&power->value) * 1000000ULL; 453c10e753dSEddie James break; 454c10e753dSEddie James default: 455c10e753dSEddie James return -EINVAL; 456c10e753dSEddie James } 457c10e753dSEddie James 4581f4d4af4SGuenter Roeck return sysfs_emit(buf, "%llu\n", val); 459c10e753dSEddie James } 460c10e753dSEddie James 461c10e753dSEddie James static u64 occ_get_powr_avg(u64 *accum, u32 *samples) 462c10e753dSEddie James { 463211186caSLei YU u64 divisor = get_unaligned_be32(samples); 464211186caSLei YU 465211186caSLei YU return (divisor == 0) ? 0 : 466211186caSLei YU div64_u64(get_unaligned_be64(accum) * 1000000ULL, divisor); 467c10e753dSEddie James } 468c10e753dSEddie James 469c10e753dSEddie James static ssize_t occ_show_power_2(struct device *dev, 470c10e753dSEddie James struct device_attribute *attr, char *buf) 471c10e753dSEddie James { 472c10e753dSEddie James int rc; 473c10e753dSEddie James u64 val = 0; 474c10e753dSEddie James struct power_sensor_2 *power; 475c10e753dSEddie James struct occ *occ = dev_get_drvdata(dev); 476c10e753dSEddie James struct occ_sensors *sensors = &occ->sensors; 477c10e753dSEddie James struct sensor_device_attribute_2 *sattr = to_sensor_dev_attr_2(attr); 478c10e753dSEddie James 479c10e753dSEddie James rc = occ_update_response(occ); 480c10e753dSEddie James if (rc) 481c10e753dSEddie James return rc; 482c10e753dSEddie James 483c10e753dSEddie James power = ((struct power_sensor_2 *)sensors->power.data) + sattr->index; 484c10e753dSEddie James 485c10e753dSEddie James switch (sattr->nr) { 486c10e753dSEddie James case 0: 4871f4d4af4SGuenter Roeck return sysfs_emit(buf, "%u_%u_%u\n", 488c10e753dSEddie James get_unaligned_be32(&power->sensor_id), 489c10e753dSEddie James power->function_id, power->apss_channel); 490c10e753dSEddie James case 1: 491c10e753dSEddie James val = occ_get_powr_avg(&power->accumulator, 492c10e753dSEddie James &power->update_tag); 493c10e753dSEddie James break; 494c10e753dSEddie James case 2: 495b0407d82SGustavo A. R. Silva val = (u64)get_unaligned_be32(&power->update_tag) * 496c10e753dSEddie James occ->powr_sample_time_us; 497c10e753dSEddie James break; 498c10e753dSEddie James case 3: 499c10e753dSEddie James val = get_unaligned_be16(&power->value) * 1000000ULL; 500c10e753dSEddie James break; 501c10e753dSEddie James default: 502c10e753dSEddie James return -EINVAL; 503c10e753dSEddie James } 504c10e753dSEddie James 5051f4d4af4SGuenter Roeck return sysfs_emit(buf, "%llu\n", val); 506c10e753dSEddie James } 507c10e753dSEddie James 508c10e753dSEddie James static ssize_t occ_show_power_a0(struct device *dev, 509c10e753dSEddie James struct device_attribute *attr, char *buf) 510c10e753dSEddie James { 511c10e753dSEddie James int rc; 512c10e753dSEddie James u64 val = 0; 513c10e753dSEddie James struct power_sensor_a0 *power; 514c10e753dSEddie James struct occ *occ = dev_get_drvdata(dev); 515c10e753dSEddie James struct occ_sensors *sensors = &occ->sensors; 516c10e753dSEddie James struct sensor_device_attribute_2 *sattr = to_sensor_dev_attr_2(attr); 517c10e753dSEddie James 518c10e753dSEddie James rc = occ_update_response(occ); 519c10e753dSEddie James if (rc) 520c10e753dSEddie James return rc; 521c10e753dSEddie James 522c10e753dSEddie James power = ((struct power_sensor_a0 *)sensors->power.data) + sattr->index; 523c10e753dSEddie James 524c10e753dSEddie James switch (sattr->nr) { 525c10e753dSEddie James case 0: 5261f4d4af4SGuenter Roeck return sysfs_emit(buf, "%u_system\n", 527c10e753dSEddie James get_unaligned_be32(&power->sensor_id)); 528c10e753dSEddie James case 1: 529c10e753dSEddie James val = occ_get_powr_avg(&power->system.accumulator, 530c10e753dSEddie James &power->system.update_tag); 531c10e753dSEddie James break; 532c10e753dSEddie James case 2: 533b0407d82SGustavo A. R. Silva val = (u64)get_unaligned_be32(&power->system.update_tag) * 534c10e753dSEddie James occ->powr_sample_time_us; 535c10e753dSEddie James break; 536c10e753dSEddie James case 3: 537c10e753dSEddie James val = get_unaligned_be16(&power->system.value) * 1000000ULL; 538c10e753dSEddie James break; 539c10e753dSEddie James case 4: 5401f4d4af4SGuenter Roeck return sysfs_emit(buf, "%u_proc\n", 541c10e753dSEddie James get_unaligned_be32(&power->sensor_id)); 542c10e753dSEddie James case 5: 543c10e753dSEddie James val = occ_get_powr_avg(&power->proc.accumulator, 544c10e753dSEddie James &power->proc.update_tag); 545c10e753dSEddie James break; 546c10e753dSEddie James case 6: 547b0407d82SGustavo A. R. Silva val = (u64)get_unaligned_be32(&power->proc.update_tag) * 548c10e753dSEddie James occ->powr_sample_time_us; 549c10e753dSEddie James break; 550c10e753dSEddie James case 7: 551c10e753dSEddie James val = get_unaligned_be16(&power->proc.value) * 1000000ULL; 552c10e753dSEddie James break; 553c10e753dSEddie James case 8: 5541f4d4af4SGuenter Roeck return sysfs_emit(buf, "%u_vdd\n", 555c10e753dSEddie James get_unaligned_be32(&power->sensor_id)); 556c10e753dSEddie James case 9: 557c10e753dSEddie James val = occ_get_powr_avg(&power->vdd.accumulator, 558c10e753dSEddie James &power->vdd.update_tag); 559c10e753dSEddie James break; 560c10e753dSEddie James case 10: 561b0407d82SGustavo A. R. Silva val = (u64)get_unaligned_be32(&power->vdd.update_tag) * 562c10e753dSEddie James occ->powr_sample_time_us; 563c10e753dSEddie James break; 564c10e753dSEddie James case 11: 565c10e753dSEddie James val = get_unaligned_be16(&power->vdd.value) * 1000000ULL; 566c10e753dSEddie James break; 567c10e753dSEddie James case 12: 5681f4d4af4SGuenter Roeck return sysfs_emit(buf, "%u_vdn\n", 569c10e753dSEddie James get_unaligned_be32(&power->sensor_id)); 570c10e753dSEddie James case 13: 571c10e753dSEddie James val = occ_get_powr_avg(&power->vdn.accumulator, 572c10e753dSEddie James &power->vdn.update_tag); 573c10e753dSEddie James break; 574c10e753dSEddie James case 14: 575b0407d82SGustavo A. R. Silva val = (u64)get_unaligned_be32(&power->vdn.update_tag) * 576c10e753dSEddie James occ->powr_sample_time_us; 577c10e753dSEddie James break; 578c10e753dSEddie James case 15: 579c10e753dSEddie James val = get_unaligned_be16(&power->vdn.value) * 1000000ULL; 580c10e753dSEddie James break; 581c10e753dSEddie James default: 582c10e753dSEddie James return -EINVAL; 583c10e753dSEddie James } 584c10e753dSEddie James 5851f4d4af4SGuenter Roeck return sysfs_emit(buf, "%llu\n", val); 586c10e753dSEddie James } 587c10e753dSEddie James 588c10e753dSEddie James static ssize_t occ_show_caps_1_2(struct device *dev, 589c10e753dSEddie James struct device_attribute *attr, char *buf) 590c10e753dSEddie James { 591c10e753dSEddie James int rc; 592c10e753dSEddie James u64 val = 0; 593c10e753dSEddie James struct caps_sensor_2 *caps; 594c10e753dSEddie James struct occ *occ = dev_get_drvdata(dev); 595c10e753dSEddie James struct occ_sensors *sensors = &occ->sensors; 596c10e753dSEddie James struct sensor_device_attribute_2 *sattr = to_sensor_dev_attr_2(attr); 597c10e753dSEddie James 598c10e753dSEddie James rc = occ_update_response(occ); 599c10e753dSEddie James if (rc) 600c10e753dSEddie James return rc; 601c10e753dSEddie James 602c10e753dSEddie James caps = ((struct caps_sensor_2 *)sensors->caps.data) + sattr->index; 603c10e753dSEddie James 604c10e753dSEddie James switch (sattr->nr) { 605c10e753dSEddie James case 0: 6061f4d4af4SGuenter Roeck return sysfs_emit(buf, "system\n"); 607c10e753dSEddie James case 1: 608c10e753dSEddie James val = get_unaligned_be16(&caps->cap) * 1000000ULL; 609c10e753dSEddie James break; 610c10e753dSEddie James case 2: 611c10e753dSEddie James val = get_unaligned_be16(&caps->system_power) * 1000000ULL; 612c10e753dSEddie James break; 613c10e753dSEddie James case 3: 614c10e753dSEddie James val = get_unaligned_be16(&caps->n_cap) * 1000000ULL; 615c10e753dSEddie James break; 616c10e753dSEddie James case 4: 617c10e753dSEddie James val = get_unaligned_be16(&caps->max) * 1000000ULL; 618c10e753dSEddie James break; 619c10e753dSEddie James case 5: 620c10e753dSEddie James val = get_unaligned_be16(&caps->min) * 1000000ULL; 621c10e753dSEddie James break; 622c10e753dSEddie James case 6: 623c10e753dSEddie James val = get_unaligned_be16(&caps->user) * 1000000ULL; 624c10e753dSEddie James break; 625c10e753dSEddie James case 7: 626c10e753dSEddie James if (occ->sensors.caps.version == 1) 627c10e753dSEddie James return -EINVAL; 628c10e753dSEddie James 629c10e753dSEddie James val = caps->user_source; 630c10e753dSEddie James break; 631c10e753dSEddie James default: 632c10e753dSEddie James return -EINVAL; 633c10e753dSEddie James } 634c10e753dSEddie James 6351f4d4af4SGuenter Roeck return sysfs_emit(buf, "%llu\n", val); 636c10e753dSEddie James } 637c10e753dSEddie James 638c10e753dSEddie James static ssize_t occ_show_caps_3(struct device *dev, 639c10e753dSEddie James struct device_attribute *attr, char *buf) 640c10e753dSEddie James { 641c10e753dSEddie James int rc; 642c10e753dSEddie James u64 val = 0; 643c10e753dSEddie James struct caps_sensor_3 *caps; 644c10e753dSEddie James struct occ *occ = dev_get_drvdata(dev); 645c10e753dSEddie James struct occ_sensors *sensors = &occ->sensors; 646c10e753dSEddie James struct sensor_device_attribute_2 *sattr = to_sensor_dev_attr_2(attr); 647c10e753dSEddie James 648c10e753dSEddie James rc = occ_update_response(occ); 649c10e753dSEddie James if (rc) 650c10e753dSEddie James return rc; 651c10e753dSEddie James 652c10e753dSEddie James caps = ((struct caps_sensor_3 *)sensors->caps.data) + sattr->index; 653c10e753dSEddie James 654c10e753dSEddie James switch (sattr->nr) { 655c10e753dSEddie James case 0: 6561f4d4af4SGuenter Roeck return sysfs_emit(buf, "system\n"); 657c10e753dSEddie James case 1: 658c10e753dSEddie James val = get_unaligned_be16(&caps->cap) * 1000000ULL; 659c10e753dSEddie James break; 660c10e753dSEddie James case 2: 661c10e753dSEddie James val = get_unaligned_be16(&caps->system_power) * 1000000ULL; 662c10e753dSEddie James break; 663c10e753dSEddie James case 3: 664c10e753dSEddie James val = get_unaligned_be16(&caps->n_cap) * 1000000ULL; 665c10e753dSEddie James break; 666c10e753dSEddie James case 4: 667c10e753dSEddie James val = get_unaligned_be16(&caps->max) * 1000000ULL; 668c10e753dSEddie James break; 669c10e753dSEddie James case 5: 670c10e753dSEddie James val = get_unaligned_be16(&caps->hard_min) * 1000000ULL; 671c10e753dSEddie James break; 672c10e753dSEddie James case 6: 673c10e753dSEddie James val = get_unaligned_be16(&caps->user) * 1000000ULL; 674c10e753dSEddie James break; 675c10e753dSEddie James case 7: 676c10e753dSEddie James val = caps->user_source; 677c10e753dSEddie James break; 6782b8d17ddSEddie James case 8: 6792b8d17ddSEddie James val = get_unaligned_be16(&caps->soft_min) * 1000000ULL; 6802b8d17ddSEddie James break; 681c10e753dSEddie James default: 682c10e753dSEddie James return -EINVAL; 683c10e753dSEddie James } 684c10e753dSEddie James 6851f4d4af4SGuenter Roeck return sysfs_emit(buf, "%llu\n", val); 686c10e753dSEddie James } 687c10e753dSEddie James 688c10e753dSEddie James static ssize_t occ_store_caps_user(struct device *dev, 689c10e753dSEddie James struct device_attribute *attr, 690c10e753dSEddie James const char *buf, size_t count) 691c10e753dSEddie James { 692c10e753dSEddie James int rc; 693c10e753dSEddie James u16 user_power_cap; 694c10e753dSEddie James unsigned long long value; 695c10e753dSEddie James struct occ *occ = dev_get_drvdata(dev); 696c10e753dSEddie James 697c10e753dSEddie James rc = kstrtoull(buf, 0, &value); 698c10e753dSEddie James if (rc) 699c10e753dSEddie James return rc; 700c10e753dSEddie James 701c10e753dSEddie James user_power_cap = div64_u64(value, 1000000ULL); /* microwatt to watt */ 702c10e753dSEddie James 703c10e753dSEddie James rc = occ_set_user_power_cap(occ, user_power_cap); 704c10e753dSEddie James if (rc) 705c10e753dSEddie James return rc; 706c10e753dSEddie James 707c10e753dSEddie James return count; 708c10e753dSEddie James } 709c10e753dSEddie James 710c10e753dSEddie James static ssize_t occ_show_extended(struct device *dev, 711c10e753dSEddie James struct device_attribute *attr, char *buf) 712c10e753dSEddie James { 713c10e753dSEddie James int rc; 714c10e753dSEddie James struct extended_sensor *extn; 715c10e753dSEddie James struct occ *occ = dev_get_drvdata(dev); 716c10e753dSEddie James struct occ_sensors *sensors = &occ->sensors; 717c10e753dSEddie James struct sensor_device_attribute_2 *sattr = to_sensor_dev_attr_2(attr); 718c10e753dSEddie James 719c10e753dSEddie James rc = occ_update_response(occ); 720c10e753dSEddie James if (rc) 721c10e753dSEddie James return rc; 722c10e753dSEddie James 723c10e753dSEddie James extn = ((struct extended_sensor *)sensors->extended.data) + 724c10e753dSEddie James sattr->index; 725c10e753dSEddie James 726c10e753dSEddie James switch (sattr->nr) { 727c10e753dSEddie James case 0: 7281f4d4af4SGuenter Roeck if (extn->flags & EXTN_FLAG_SENSOR_ID) { 7291f4d4af4SGuenter Roeck rc = sysfs_emit(buf, "%u", 730c10e753dSEddie James get_unaligned_be32(&extn->sensor_id)); 7311f4d4af4SGuenter Roeck } else { 7321f4d4af4SGuenter Roeck rc = sysfs_emit(buf, "%02x%02x%02x%02x\n", 733c10e753dSEddie James extn->name[0], extn->name[1], 734c10e753dSEddie James extn->name[2], extn->name[3]); 7351f4d4af4SGuenter Roeck } 736c10e753dSEddie James break; 737c10e753dSEddie James case 1: 7381f4d4af4SGuenter Roeck rc = sysfs_emit(buf, "%02x\n", extn->flags); 739c10e753dSEddie James break; 740c10e753dSEddie James case 2: 7411f4d4af4SGuenter Roeck rc = sysfs_emit(buf, "%02x%02x%02x%02x%02x%02x\n", 742c10e753dSEddie James extn->data[0], extn->data[1], extn->data[2], 743c10e753dSEddie James extn->data[3], extn->data[4], extn->data[5]); 744c10e753dSEddie James break; 745c10e753dSEddie James default: 746c10e753dSEddie James return -EINVAL; 747c10e753dSEddie James } 748c10e753dSEddie James 749c10e753dSEddie James return rc; 750c10e753dSEddie James } 751c10e753dSEddie James 75254076cb3SEddie James /* 75354076cb3SEddie James * Some helper macros to make it easier to define an occ_attribute. Since these 75454076cb3SEddie James * are dynamically allocated, we shouldn't use the existing kernel macros which 75554076cb3SEddie James * stringify the name argument. 75654076cb3SEddie James */ 75754076cb3SEddie James #define ATTR_OCC(_name, _mode, _show, _store) { \ 75854076cb3SEddie James .attr = { \ 75954076cb3SEddie James .name = _name, \ 76054076cb3SEddie James .mode = VERIFY_OCTAL_PERMISSIONS(_mode), \ 76154076cb3SEddie James }, \ 76254076cb3SEddie James .show = _show, \ 76354076cb3SEddie James .store = _store, \ 76454076cb3SEddie James } 76554076cb3SEddie James 76654076cb3SEddie James #define SENSOR_ATTR_OCC(_name, _mode, _show, _store, _nr, _index) { \ 76754076cb3SEddie James .dev_attr = ATTR_OCC(_name, _mode, _show, _store), \ 76854076cb3SEddie James .index = _index, \ 76954076cb3SEddie James .nr = _nr, \ 77054076cb3SEddie James } 77154076cb3SEddie James 77254076cb3SEddie James #define OCC_INIT_ATTR(_name, _mode, _show, _store, _nr, _index) \ 77354076cb3SEddie James ((struct sensor_device_attribute_2) \ 77454076cb3SEddie James SENSOR_ATTR_OCC(_name, _mode, _show, _store, _nr, _index)) 77554076cb3SEddie James 77654076cb3SEddie James /* 77754076cb3SEddie James * Allocate and instatiate sensor_device_attribute_2s. It's most efficient to 77854076cb3SEddie James * use our own instead of the built-in hwmon attribute types. 77954076cb3SEddie James */ 78054076cb3SEddie James static int occ_setup_sensor_attrs(struct occ *occ) 78154076cb3SEddie James { 78254076cb3SEddie James unsigned int i, s, num_attrs = 0; 78354076cb3SEddie James struct device *dev = occ->bus_dev; 78454076cb3SEddie James struct occ_sensors *sensors = &occ->sensors; 78554076cb3SEddie James struct occ_attribute *attr; 78654076cb3SEddie James struct temp_sensor_2 *temp; 78754076cb3SEddie James ssize_t (*show_temp)(struct device *, struct device_attribute *, 78854076cb3SEddie James char *) = occ_show_temp_1; 78954076cb3SEddie James ssize_t (*show_freq)(struct device *, struct device_attribute *, 79054076cb3SEddie James char *) = occ_show_freq_1; 79154076cb3SEddie James ssize_t (*show_power)(struct device *, struct device_attribute *, 79254076cb3SEddie James char *) = occ_show_power_1; 79354076cb3SEddie James ssize_t (*show_caps)(struct device *, struct device_attribute *, 79454076cb3SEddie James char *) = occ_show_caps_1_2; 79554076cb3SEddie James 79654076cb3SEddie James switch (sensors->temp.version) { 79754076cb3SEddie James case 1: 79854076cb3SEddie James num_attrs += (sensors->temp.num_sensors * 2); 79954076cb3SEddie James break; 80054076cb3SEddie James case 2: 80154076cb3SEddie James num_attrs += (sensors->temp.num_sensors * 4); 80254076cb3SEddie James show_temp = occ_show_temp_2; 80354076cb3SEddie James break; 804db4919ecSEddie James case 0x10: 805db4919ecSEddie James num_attrs += (sensors->temp.num_sensors * 5); 806db4919ecSEddie James show_temp = occ_show_temp_10; 807db4919ecSEddie James break; 80854076cb3SEddie James default: 80954076cb3SEddie James sensors->temp.num_sensors = 0; 81054076cb3SEddie James } 81154076cb3SEddie James 81254076cb3SEddie James switch (sensors->freq.version) { 81354076cb3SEddie James case 2: 81454076cb3SEddie James show_freq = occ_show_freq_2; 815df561f66SGustavo A. R. Silva fallthrough; 81654076cb3SEddie James case 1: 81754076cb3SEddie James num_attrs += (sensors->freq.num_sensors * 2); 81854076cb3SEddie James break; 81954076cb3SEddie James default: 82054076cb3SEddie James sensors->freq.num_sensors = 0; 82154076cb3SEddie James } 82254076cb3SEddie James 82354076cb3SEddie James switch (sensors->power.version) { 82454076cb3SEddie James case 2: 82554076cb3SEddie James show_power = occ_show_power_2; 826df561f66SGustavo A. R. Silva fallthrough; 82754076cb3SEddie James case 1: 82854076cb3SEddie James num_attrs += (sensors->power.num_sensors * 4); 82954076cb3SEddie James break; 83054076cb3SEddie James case 0xA0: 83154076cb3SEddie James num_attrs += (sensors->power.num_sensors * 16); 83254076cb3SEddie James show_power = occ_show_power_a0; 83354076cb3SEddie James break; 83454076cb3SEddie James default: 83554076cb3SEddie James sensors->power.num_sensors = 0; 83654076cb3SEddie James } 83754076cb3SEddie James 83854076cb3SEddie James switch (sensors->caps.version) { 83954076cb3SEddie James case 1: 84054076cb3SEddie James num_attrs += (sensors->caps.num_sensors * 7); 84154076cb3SEddie James break; 84254076cb3SEddie James case 2: 84354076cb3SEddie James num_attrs += (sensors->caps.num_sensors * 8); 84454076cb3SEddie James break; 8452b8d17ddSEddie James case 3: 8462b8d17ddSEddie James show_caps = occ_show_caps_3; 8472b8d17ddSEddie James num_attrs += (sensors->caps.num_sensors * 9); 8482b8d17ddSEddie James break; 84954076cb3SEddie James default: 85054076cb3SEddie James sensors->caps.num_sensors = 0; 85154076cb3SEddie James } 85254076cb3SEddie James 85354076cb3SEddie James switch (sensors->extended.version) { 85454076cb3SEddie James case 1: 85554076cb3SEddie James num_attrs += (sensors->extended.num_sensors * 3); 85654076cb3SEddie James break; 85754076cb3SEddie James default: 85854076cb3SEddie James sensors->extended.num_sensors = 0; 85954076cb3SEddie James } 86054076cb3SEddie James 86154076cb3SEddie James occ->attrs = devm_kzalloc(dev, sizeof(*occ->attrs) * num_attrs, 86254076cb3SEddie James GFP_KERNEL); 86354076cb3SEddie James if (!occ->attrs) 86454076cb3SEddie James return -ENOMEM; 86554076cb3SEddie James 86654076cb3SEddie James /* null-terminated list */ 86754076cb3SEddie James occ->group.attrs = devm_kzalloc(dev, sizeof(*occ->group.attrs) * 86854076cb3SEddie James num_attrs + 1, GFP_KERNEL); 86954076cb3SEddie James if (!occ->group.attrs) 87054076cb3SEddie James return -ENOMEM; 87154076cb3SEddie James 87254076cb3SEddie James attr = occ->attrs; 87354076cb3SEddie James 87454076cb3SEddie James for (i = 0; i < sensors->temp.num_sensors; ++i) { 87554076cb3SEddie James s = i + 1; 87654076cb3SEddie James temp = ((struct temp_sensor_2 *)sensors->temp.data) + i; 87754076cb3SEddie James 87854076cb3SEddie James snprintf(attr->name, sizeof(attr->name), "temp%d_label", s); 87954076cb3SEddie James attr->sensor = OCC_INIT_ATTR(attr->name, 0444, show_temp, NULL, 88054076cb3SEddie James 0, i); 88154076cb3SEddie James attr++; 88254076cb3SEddie James 883ffa26000SEddie James if (sensors->temp.version == 2 && 88454076cb3SEddie James temp->fru_type == OCC_FRU_TYPE_VRM) { 88554076cb3SEddie James snprintf(attr->name, sizeof(attr->name), 88654076cb3SEddie James "temp%d_alarm", s); 88754076cb3SEddie James } else { 88854076cb3SEddie James snprintf(attr->name, sizeof(attr->name), 88954076cb3SEddie James "temp%d_input", s); 89054076cb3SEddie James } 89154076cb3SEddie James 89254076cb3SEddie James attr->sensor = OCC_INIT_ATTR(attr->name, 0444, show_temp, NULL, 89354076cb3SEddie James 1, i); 89454076cb3SEddie James attr++; 89554076cb3SEddie James 89654076cb3SEddie James if (sensors->temp.version > 1) { 89754076cb3SEddie James snprintf(attr->name, sizeof(attr->name), 89854076cb3SEddie James "temp%d_fru_type", s); 89954076cb3SEddie James attr->sensor = OCC_INIT_ATTR(attr->name, 0444, 90054076cb3SEddie James show_temp, NULL, 2, i); 90154076cb3SEddie James attr++; 90254076cb3SEddie James 90354076cb3SEddie James snprintf(attr->name, sizeof(attr->name), 90454076cb3SEddie James "temp%d_fault", s); 90554076cb3SEddie James attr->sensor = OCC_INIT_ATTR(attr->name, 0444, 90654076cb3SEddie James show_temp, NULL, 3, i); 90754076cb3SEddie James attr++; 908db4919ecSEddie James 909db4919ecSEddie James if (sensors->temp.version == 0x10) { 910db4919ecSEddie James snprintf(attr->name, sizeof(attr->name), 911db4919ecSEddie James "temp%d_max", s); 912db4919ecSEddie James attr->sensor = OCC_INIT_ATTR(attr->name, 0444, 913db4919ecSEddie James show_temp, NULL, 914db4919ecSEddie James 4, i); 915db4919ecSEddie James attr++; 916db4919ecSEddie James } 91754076cb3SEddie James } 91854076cb3SEddie James } 91954076cb3SEddie James 92054076cb3SEddie James for (i = 0; i < sensors->freq.num_sensors; ++i) { 92154076cb3SEddie James s = i + 1; 92254076cb3SEddie James 92354076cb3SEddie James snprintf(attr->name, sizeof(attr->name), "freq%d_label", s); 92454076cb3SEddie James attr->sensor = OCC_INIT_ATTR(attr->name, 0444, show_freq, NULL, 92554076cb3SEddie James 0, i); 92654076cb3SEddie James attr++; 92754076cb3SEddie James 92854076cb3SEddie James snprintf(attr->name, sizeof(attr->name), "freq%d_input", s); 92954076cb3SEddie James attr->sensor = OCC_INIT_ATTR(attr->name, 0444, show_freq, NULL, 93054076cb3SEddie James 1, i); 93154076cb3SEddie James attr++; 93254076cb3SEddie James } 93354076cb3SEddie James 93454076cb3SEddie James if (sensors->power.version == 0xA0) { 93554076cb3SEddie James /* 93654076cb3SEddie James * Special case for many-attribute power sensor. Split it into 93754076cb3SEddie James * a sensor number per power type, emulating several sensors. 93854076cb3SEddie James */ 93954076cb3SEddie James for (i = 0; i < sensors->power.num_sensors; ++i) { 94054076cb3SEddie James unsigned int j; 94154076cb3SEddie James unsigned int nr = 0; 94254076cb3SEddie James 94354076cb3SEddie James s = (i * 4) + 1; 94454076cb3SEddie James 94554076cb3SEddie James for (j = 0; j < 4; ++j) { 94654076cb3SEddie James snprintf(attr->name, sizeof(attr->name), 94754076cb3SEddie James "power%d_label", s); 94854076cb3SEddie James attr->sensor = OCC_INIT_ATTR(attr->name, 0444, 94954076cb3SEddie James show_power, NULL, 95054076cb3SEddie James nr++, i); 95154076cb3SEddie James attr++; 95254076cb3SEddie James 95354076cb3SEddie James snprintf(attr->name, sizeof(attr->name), 95454076cb3SEddie James "power%d_average", s); 95554076cb3SEddie James attr->sensor = OCC_INIT_ATTR(attr->name, 0444, 95654076cb3SEddie James show_power, NULL, 95754076cb3SEddie James nr++, i); 95854076cb3SEddie James attr++; 95954076cb3SEddie James 96054076cb3SEddie James snprintf(attr->name, sizeof(attr->name), 96154076cb3SEddie James "power%d_average_interval", s); 96254076cb3SEddie James attr->sensor = OCC_INIT_ATTR(attr->name, 0444, 96354076cb3SEddie James show_power, NULL, 96454076cb3SEddie James nr++, i); 96554076cb3SEddie James attr++; 96654076cb3SEddie James 96754076cb3SEddie James snprintf(attr->name, sizeof(attr->name), 96854076cb3SEddie James "power%d_input", s); 96954076cb3SEddie James attr->sensor = OCC_INIT_ATTR(attr->name, 0444, 97054076cb3SEddie James show_power, NULL, 97154076cb3SEddie James nr++, i); 97254076cb3SEddie James attr++; 97354076cb3SEddie James 97454076cb3SEddie James s++; 97554076cb3SEddie James } 97654076cb3SEddie James } 9778e6af454SEddie James 9788e6af454SEddie James s = (sensors->power.num_sensors * 4) + 1; 97954076cb3SEddie James } else { 98054076cb3SEddie James for (i = 0; i < sensors->power.num_sensors; ++i) { 98154076cb3SEddie James s = i + 1; 98254076cb3SEddie James 98354076cb3SEddie James snprintf(attr->name, sizeof(attr->name), 98454076cb3SEddie James "power%d_label", s); 98554076cb3SEddie James attr->sensor = OCC_INIT_ATTR(attr->name, 0444, 98654076cb3SEddie James show_power, NULL, 0, i); 98754076cb3SEddie James attr++; 98854076cb3SEddie James 98954076cb3SEddie James snprintf(attr->name, sizeof(attr->name), 99054076cb3SEddie James "power%d_average", s); 99154076cb3SEddie James attr->sensor = OCC_INIT_ATTR(attr->name, 0444, 99254076cb3SEddie James show_power, NULL, 1, i); 99354076cb3SEddie James attr++; 99454076cb3SEddie James 99554076cb3SEddie James snprintf(attr->name, sizeof(attr->name), 99654076cb3SEddie James "power%d_average_interval", s); 99754076cb3SEddie James attr->sensor = OCC_INIT_ATTR(attr->name, 0444, 99854076cb3SEddie James show_power, NULL, 2, i); 99954076cb3SEddie James attr++; 100054076cb3SEddie James 100154076cb3SEddie James snprintf(attr->name, sizeof(attr->name), 100254076cb3SEddie James "power%d_input", s); 100354076cb3SEddie James attr->sensor = OCC_INIT_ATTR(attr->name, 0444, 100454076cb3SEddie James show_power, NULL, 3, i); 100554076cb3SEddie James attr++; 100654076cb3SEddie James } 10078e6af454SEddie James 10088e6af454SEddie James s = sensors->power.num_sensors + 1; 100954076cb3SEddie James } 101054076cb3SEddie James 101154076cb3SEddie James if (sensors->caps.num_sensors >= 1) { 101254076cb3SEddie James snprintf(attr->name, sizeof(attr->name), "power%d_label", s); 101354076cb3SEddie James attr->sensor = OCC_INIT_ATTR(attr->name, 0444, show_caps, NULL, 101454076cb3SEddie James 0, 0); 101554076cb3SEddie James attr++; 101654076cb3SEddie James 101754076cb3SEddie James snprintf(attr->name, sizeof(attr->name), "power%d_cap", s); 101854076cb3SEddie James attr->sensor = OCC_INIT_ATTR(attr->name, 0444, show_caps, NULL, 101954076cb3SEddie James 1, 0); 102054076cb3SEddie James attr++; 102154076cb3SEddie James 102254076cb3SEddie James snprintf(attr->name, sizeof(attr->name), "power%d_input", s); 102354076cb3SEddie James attr->sensor = OCC_INIT_ATTR(attr->name, 0444, show_caps, NULL, 102454076cb3SEddie James 2, 0); 102554076cb3SEddie James attr++; 102654076cb3SEddie James 102754076cb3SEddie James snprintf(attr->name, sizeof(attr->name), 102854076cb3SEddie James "power%d_cap_not_redundant", s); 102954076cb3SEddie James attr->sensor = OCC_INIT_ATTR(attr->name, 0444, show_caps, NULL, 103054076cb3SEddie James 3, 0); 103154076cb3SEddie James attr++; 103254076cb3SEddie James 103354076cb3SEddie James snprintf(attr->name, sizeof(attr->name), "power%d_cap_max", s); 103454076cb3SEddie James attr->sensor = OCC_INIT_ATTR(attr->name, 0444, show_caps, NULL, 103554076cb3SEddie James 4, 0); 103654076cb3SEddie James attr++; 103754076cb3SEddie James 103854076cb3SEddie James snprintf(attr->name, sizeof(attr->name), "power%d_cap_min", s); 103954076cb3SEddie James attr->sensor = OCC_INIT_ATTR(attr->name, 0444, show_caps, NULL, 104054076cb3SEddie James 5, 0); 104154076cb3SEddie James attr++; 104254076cb3SEddie James 104354076cb3SEddie James snprintf(attr->name, sizeof(attr->name), "power%d_cap_user", 104454076cb3SEddie James s); 104554076cb3SEddie James attr->sensor = OCC_INIT_ATTR(attr->name, 0644, show_caps, 104654076cb3SEddie James occ_store_caps_user, 6, 0); 104754076cb3SEddie James attr++; 104854076cb3SEddie James 104954076cb3SEddie James if (sensors->caps.version > 1) { 105054076cb3SEddie James snprintf(attr->name, sizeof(attr->name), 105154076cb3SEddie James "power%d_cap_user_source", s); 105254076cb3SEddie James attr->sensor = OCC_INIT_ATTR(attr->name, 0444, 105354076cb3SEddie James show_caps, NULL, 7, 0); 105454076cb3SEddie James attr++; 10552b8d17ddSEddie James 10562b8d17ddSEddie James if (sensors->caps.version > 2) { 10572b8d17ddSEddie James snprintf(attr->name, sizeof(attr->name), 10582b8d17ddSEddie James "power%d_cap_min_soft", s); 10592b8d17ddSEddie James attr->sensor = OCC_INIT_ATTR(attr->name, 0444, 10602b8d17ddSEddie James show_caps, NULL, 10612b8d17ddSEddie James 8, 0); 10622b8d17ddSEddie James attr++; 10632b8d17ddSEddie James } 106454076cb3SEddie James } 106554076cb3SEddie James } 106654076cb3SEddie James 106754076cb3SEddie James for (i = 0; i < sensors->extended.num_sensors; ++i) { 106854076cb3SEddie James s = i + 1; 106954076cb3SEddie James 107054076cb3SEddie James snprintf(attr->name, sizeof(attr->name), "extn%d_label", s); 107154076cb3SEddie James attr->sensor = OCC_INIT_ATTR(attr->name, 0444, 107254076cb3SEddie James occ_show_extended, NULL, 0, i); 107354076cb3SEddie James attr++; 107454076cb3SEddie James 107554076cb3SEddie James snprintf(attr->name, sizeof(attr->name), "extn%d_flags", s); 107654076cb3SEddie James attr->sensor = OCC_INIT_ATTR(attr->name, 0444, 107754076cb3SEddie James occ_show_extended, NULL, 1, i); 107854076cb3SEddie James attr++; 107954076cb3SEddie James 108054076cb3SEddie James snprintf(attr->name, sizeof(attr->name), "extn%d_input", s); 108154076cb3SEddie James attr->sensor = OCC_INIT_ATTR(attr->name, 0444, 108254076cb3SEddie James occ_show_extended, NULL, 2, i); 108354076cb3SEddie James attr++; 108454076cb3SEddie James } 108554076cb3SEddie James 108654076cb3SEddie James /* put the sensors in the group */ 108754076cb3SEddie James for (i = 0; i < num_attrs; ++i) { 108854076cb3SEddie James sysfs_attr_init(&occ->attrs[i].sensor.dev_attr.attr); 108954076cb3SEddie James occ->group.attrs[i] = &occ->attrs[i].sensor.dev_attr.attr; 109054076cb3SEddie James } 109154076cb3SEddie James 109254076cb3SEddie James return 0; 109354076cb3SEddie James } 109454076cb3SEddie James 1095aa195fe4SEddie James /* only need to do this once at startup, as OCC won't change sensors on us */ 1096aa195fe4SEddie James static void occ_parse_poll_response(struct occ *occ) 1097aa195fe4SEddie James { 1098aa195fe4SEddie James unsigned int i, old_offset, offset = 0, size = 0; 1099aa195fe4SEddie James struct occ_sensor *sensor; 1100aa195fe4SEddie James struct occ_sensors *sensors = &occ->sensors; 1101aa195fe4SEddie James struct occ_response *resp = &occ->resp; 1102aa195fe4SEddie James struct occ_poll_response *poll = 1103aa195fe4SEddie James (struct occ_poll_response *)&resp->data[0]; 1104aa195fe4SEddie James struct occ_poll_response_header *header = &poll->header; 1105aa195fe4SEddie James struct occ_sensor_data_block *block = &poll->block; 1106aa195fe4SEddie James 1107aa195fe4SEddie James dev_info(occ->bus_dev, "OCC found, code level: %.16s\n", 1108aa195fe4SEddie James header->occ_code_level); 1109aa195fe4SEddie James 1110aa195fe4SEddie James for (i = 0; i < header->num_sensor_data_blocks; ++i) { 1111aa195fe4SEddie James block = (struct occ_sensor_data_block *)((u8 *)block + offset); 1112aa195fe4SEddie James old_offset = offset; 1113aa195fe4SEddie James offset = (block->header.num_sensors * 1114aa195fe4SEddie James block->header.sensor_length) + sizeof(block->header); 1115aa195fe4SEddie James size += offset; 1116aa195fe4SEddie James 1117aa195fe4SEddie James /* validate all the length/size fields */ 1118aa195fe4SEddie James if ((size + sizeof(*header)) >= OCC_RESP_DATA_BYTES) { 1119aa195fe4SEddie James dev_warn(occ->bus_dev, "exceeded response buffer\n"); 1120aa195fe4SEddie James return; 1121aa195fe4SEddie James } 1122aa195fe4SEddie James 1123aa195fe4SEddie James dev_dbg(occ->bus_dev, " %04x..%04x: %.4s (%d sensors)\n", 1124aa195fe4SEddie James old_offset, offset - 1, block->header.eye_catcher, 1125aa195fe4SEddie James block->header.num_sensors); 1126aa195fe4SEddie James 1127aa195fe4SEddie James /* match sensor block type */ 1128aa195fe4SEddie James if (strncmp(block->header.eye_catcher, "TEMP", 4) == 0) 1129aa195fe4SEddie James sensor = &sensors->temp; 1130aa195fe4SEddie James else if (strncmp(block->header.eye_catcher, "FREQ", 4) == 0) 1131aa195fe4SEddie James sensor = &sensors->freq; 1132aa195fe4SEddie James else if (strncmp(block->header.eye_catcher, "POWR", 4) == 0) 1133aa195fe4SEddie James sensor = &sensors->power; 1134aa195fe4SEddie James else if (strncmp(block->header.eye_catcher, "CAPS", 4) == 0) 1135aa195fe4SEddie James sensor = &sensors->caps; 1136aa195fe4SEddie James else if (strncmp(block->header.eye_catcher, "EXTN", 4) == 0) 1137aa195fe4SEddie James sensor = &sensors->extended; 1138aa195fe4SEddie James else { 1139aa195fe4SEddie James dev_warn(occ->bus_dev, "sensor not supported %.4s\n", 1140aa195fe4SEddie James block->header.eye_catcher); 1141aa195fe4SEddie James continue; 1142aa195fe4SEddie James } 1143aa195fe4SEddie James 1144aa195fe4SEddie James sensor->num_sensors = block->header.num_sensors; 1145aa195fe4SEddie James sensor->version = block->header.sensor_format; 1146aa195fe4SEddie James sensor->data = &block->data; 1147aa195fe4SEddie James } 1148aa195fe4SEddie James 1149aa195fe4SEddie James dev_dbg(occ->bus_dev, "Max resp size: %u+%zd=%zd\n", size, 1150aa195fe4SEddie James sizeof(*header), size + sizeof(*header)); 1151aa195fe4SEddie James } 1152aa195fe4SEddie James 1153cf8e9f21SEddie James int occ_active(struct occ *occ, bool active) 11545b5513b8SEddie James { 1155cf8e9f21SEddie James int rc = mutex_lock_interruptible(&occ->lock); 11565b5513b8SEddie James 1157cf8e9f21SEddie James if (rc) 11585b5513b8SEddie James return rc; 1159cf8e9f21SEddie James 1160cf8e9f21SEddie James if (active) { 1161cf8e9f21SEddie James if (occ->active) { 1162cf8e9f21SEddie James rc = -EALREADY; 1163cf8e9f21SEddie James goto unlock; 1164cf8e9f21SEddie James } 1165cf8e9f21SEddie James 1166cf8e9f21SEddie James occ->error_count = 0; 1167cf8e9f21SEddie James occ->last_safe = 0; 1168cf8e9f21SEddie James 1169cf8e9f21SEddie James rc = occ_poll(occ); 1170cf8e9f21SEddie James if (rc < 0) { 117138483e8fSEddie James dev_err(occ->bus_dev, 117238483e8fSEddie James "failed to get OCC poll response=%02x: %d\n", 117338483e8fSEddie James occ->resp.return_status, rc); 1174cf8e9f21SEddie James goto unlock; 11755b5513b8SEddie James } 11765b5513b8SEddie James 1177cf8e9f21SEddie James occ->active = true; 11785216dff2SEddie James occ->next_update = jiffies + OCC_UPDATE_FREQUENCY; 1179aa195fe4SEddie James occ_parse_poll_response(occ); 1180aa195fe4SEddie James 118154076cb3SEddie James rc = occ_setup_sensor_attrs(occ); 118254076cb3SEddie James if (rc) { 1183cf8e9f21SEddie James dev_err(occ->bus_dev, 1184cf8e9f21SEddie James "failed to setup sensor attrs: %d\n", rc); 1185cf8e9f21SEddie James goto unlock; 1186cf8e9f21SEddie James } 1187cf8e9f21SEddie James 1188cf8e9f21SEddie James occ->hwmon = hwmon_device_register_with_groups(occ->bus_dev, 1189cf8e9f21SEddie James "occ", occ, 1190cf8e9f21SEddie James occ->groups); 1191cf8e9f21SEddie James if (IS_ERR(occ->hwmon)) { 1192cf8e9f21SEddie James rc = PTR_ERR(occ->hwmon); 1193cf8e9f21SEddie James occ->hwmon = NULL; 1194cf8e9f21SEddie James dev_err(occ->bus_dev, 1195cf8e9f21SEddie James "failed to register hwmon device: %d\n", rc); 1196cf8e9f21SEddie James goto unlock; 1197cf8e9f21SEddie James } 1198cf8e9f21SEddie James } else { 1199cf8e9f21SEddie James if (!occ->active) { 1200cf8e9f21SEddie James rc = -EALREADY; 1201cf8e9f21SEddie James goto unlock; 1202cf8e9f21SEddie James } 1203cf8e9f21SEddie James 1204cf8e9f21SEddie James if (occ->hwmon) 1205cf8e9f21SEddie James hwmon_device_unregister(occ->hwmon); 1206cf8e9f21SEddie James occ->active = false; 1207cf8e9f21SEddie James occ->hwmon = NULL; 1208cf8e9f21SEddie James } 1209cf8e9f21SEddie James 1210cf8e9f21SEddie James unlock: 1211cf8e9f21SEddie James mutex_unlock(&occ->lock); 121254076cb3SEddie James return rc; 121354076cb3SEddie James } 121454076cb3SEddie James 1215cf8e9f21SEddie James int occ_setup(struct occ *occ) 1216cf8e9f21SEddie James { 1217cf8e9f21SEddie James int rc; 1218cf8e9f21SEddie James 1219cf8e9f21SEddie James mutex_init(&occ->lock); 1220cf8e9f21SEddie James occ->groups[0] = &occ->group; 122154076cb3SEddie James 1222df04ced6SEddie James rc = occ_setup_sysfs(occ); 1223df04ced6SEddie James if (rc) 1224df04ced6SEddie James dev_err(occ->bus_dev, "failed to setup sysfs: %d\n", rc); 1225df04ced6SEddie James 1226df04ced6SEddie James return rc; 12275b5513b8SEddie James } 12285679ed99SJean Delvare EXPORT_SYMBOL_GPL(occ_setup); 12295679ed99SJean Delvare 1230cf8e9f21SEddie James void occ_shutdown(struct occ *occ) 1231cf8e9f21SEddie James { 1232cf8e9f21SEddie James occ_shutdown_sysfs(occ); 1233cf8e9f21SEddie James 1234cf8e9f21SEddie James if (occ->hwmon) 1235cf8e9f21SEddie James hwmon_device_unregister(occ->hwmon); 1236cf8e9f21SEddie James } 1237cf8e9f21SEddie James EXPORT_SYMBOL_GPL(occ_shutdown); 1238cf8e9f21SEddie James 12395679ed99SJean Delvare MODULE_AUTHOR("Eddie James <eajames@linux.ibm.com>"); 12405679ed99SJean Delvare MODULE_DESCRIPTION("Common OCC hwmon code"); 12415679ed99SJean Delvare MODULE_LICENSE("GPL"); 1242