162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0+ 262306a36Sopenharmony_ci// Copyright IBM Corp 2019 362306a36Sopenharmony_ci 462306a36Sopenharmony_ci#include <linux/device.h> 562306a36Sopenharmony_ci#include <linux/export.h> 662306a36Sopenharmony_ci#include <linux/hwmon.h> 762306a36Sopenharmony_ci#include <linux/hwmon-sysfs.h> 862306a36Sopenharmony_ci#include <linux/jiffies.h> 962306a36Sopenharmony_ci#include <linux/kernel.h> 1062306a36Sopenharmony_ci#include <linux/math64.h> 1162306a36Sopenharmony_ci#include <linux/module.h> 1262306a36Sopenharmony_ci#include <linux/mutex.h> 1362306a36Sopenharmony_ci#include <linux/property.h> 1462306a36Sopenharmony_ci#include <linux/sysfs.h> 1562306a36Sopenharmony_ci#include <asm/unaligned.h> 1662306a36Sopenharmony_ci 1762306a36Sopenharmony_ci#include "common.h" 1862306a36Sopenharmony_ci 1962306a36Sopenharmony_ci#define EXTN_FLAG_SENSOR_ID BIT(7) 2062306a36Sopenharmony_ci 2162306a36Sopenharmony_ci#define OCC_ERROR_COUNT_THRESHOLD 2 /* required by OCC spec */ 2262306a36Sopenharmony_ci 2362306a36Sopenharmony_ci#define OCC_STATE_SAFE 4 2462306a36Sopenharmony_ci#define OCC_SAFE_TIMEOUT msecs_to_jiffies(60000) /* 1 min */ 2562306a36Sopenharmony_ci 2662306a36Sopenharmony_ci#define OCC_UPDATE_FREQUENCY msecs_to_jiffies(1000) 2762306a36Sopenharmony_ci 2862306a36Sopenharmony_ci#define OCC_TEMP_SENSOR_FAULT 0xFF 2962306a36Sopenharmony_ci 3062306a36Sopenharmony_ci#define OCC_FRU_TYPE_VRM 3 3162306a36Sopenharmony_ci 3262306a36Sopenharmony_ci/* OCC sensor type and version definitions */ 3362306a36Sopenharmony_ci 3462306a36Sopenharmony_cistruct temp_sensor_1 { 3562306a36Sopenharmony_ci u16 sensor_id; 3662306a36Sopenharmony_ci u16 value; 3762306a36Sopenharmony_ci} __packed; 3862306a36Sopenharmony_ci 3962306a36Sopenharmony_cistruct temp_sensor_2 { 4062306a36Sopenharmony_ci u32 sensor_id; 4162306a36Sopenharmony_ci u8 fru_type; 4262306a36Sopenharmony_ci u8 value; 4362306a36Sopenharmony_ci} __packed; 4462306a36Sopenharmony_ci 4562306a36Sopenharmony_cistruct temp_sensor_10 { 4662306a36Sopenharmony_ci u32 sensor_id; 4762306a36Sopenharmony_ci u8 fru_type; 4862306a36Sopenharmony_ci u8 value; 4962306a36Sopenharmony_ci u8 throttle; 5062306a36Sopenharmony_ci u8 reserved; 5162306a36Sopenharmony_ci} __packed; 5262306a36Sopenharmony_ci 5362306a36Sopenharmony_cistruct freq_sensor_1 { 5462306a36Sopenharmony_ci u16 sensor_id; 5562306a36Sopenharmony_ci u16 value; 5662306a36Sopenharmony_ci} __packed; 5762306a36Sopenharmony_ci 5862306a36Sopenharmony_cistruct freq_sensor_2 { 5962306a36Sopenharmony_ci u32 sensor_id; 6062306a36Sopenharmony_ci u16 value; 6162306a36Sopenharmony_ci} __packed; 6262306a36Sopenharmony_ci 6362306a36Sopenharmony_cistruct power_sensor_1 { 6462306a36Sopenharmony_ci u16 sensor_id; 6562306a36Sopenharmony_ci u32 update_tag; 6662306a36Sopenharmony_ci u32 accumulator; 6762306a36Sopenharmony_ci u16 value; 6862306a36Sopenharmony_ci} __packed; 6962306a36Sopenharmony_ci 7062306a36Sopenharmony_cistruct power_sensor_2 { 7162306a36Sopenharmony_ci u32 sensor_id; 7262306a36Sopenharmony_ci u8 function_id; 7362306a36Sopenharmony_ci u8 apss_channel; 7462306a36Sopenharmony_ci u16 reserved; 7562306a36Sopenharmony_ci u32 update_tag; 7662306a36Sopenharmony_ci u64 accumulator; 7762306a36Sopenharmony_ci u16 value; 7862306a36Sopenharmony_ci} __packed; 7962306a36Sopenharmony_ci 8062306a36Sopenharmony_cistruct power_sensor_data { 8162306a36Sopenharmony_ci u16 value; 8262306a36Sopenharmony_ci u32 update_tag; 8362306a36Sopenharmony_ci u64 accumulator; 8462306a36Sopenharmony_ci} __packed; 8562306a36Sopenharmony_ci 8662306a36Sopenharmony_cistruct power_sensor_data_and_time { 8762306a36Sopenharmony_ci u16 update_time; 8862306a36Sopenharmony_ci u16 value; 8962306a36Sopenharmony_ci u32 update_tag; 9062306a36Sopenharmony_ci u64 accumulator; 9162306a36Sopenharmony_ci} __packed; 9262306a36Sopenharmony_ci 9362306a36Sopenharmony_cistruct power_sensor_a0 { 9462306a36Sopenharmony_ci u32 sensor_id; 9562306a36Sopenharmony_ci struct power_sensor_data_and_time system; 9662306a36Sopenharmony_ci u32 reserved; 9762306a36Sopenharmony_ci struct power_sensor_data_and_time proc; 9862306a36Sopenharmony_ci struct power_sensor_data vdd; 9962306a36Sopenharmony_ci struct power_sensor_data vdn; 10062306a36Sopenharmony_ci} __packed; 10162306a36Sopenharmony_ci 10262306a36Sopenharmony_cistruct caps_sensor_2 { 10362306a36Sopenharmony_ci u16 cap; 10462306a36Sopenharmony_ci u16 system_power; 10562306a36Sopenharmony_ci u16 n_cap; 10662306a36Sopenharmony_ci u16 max; 10762306a36Sopenharmony_ci u16 min; 10862306a36Sopenharmony_ci u16 user; 10962306a36Sopenharmony_ci u8 user_source; 11062306a36Sopenharmony_ci} __packed; 11162306a36Sopenharmony_ci 11262306a36Sopenharmony_cistruct caps_sensor_3 { 11362306a36Sopenharmony_ci u16 cap; 11462306a36Sopenharmony_ci u16 system_power; 11562306a36Sopenharmony_ci u16 n_cap; 11662306a36Sopenharmony_ci u16 max; 11762306a36Sopenharmony_ci u16 hard_min; 11862306a36Sopenharmony_ci u16 soft_min; 11962306a36Sopenharmony_ci u16 user; 12062306a36Sopenharmony_ci u8 user_source; 12162306a36Sopenharmony_ci} __packed; 12262306a36Sopenharmony_ci 12362306a36Sopenharmony_cistruct extended_sensor { 12462306a36Sopenharmony_ci union { 12562306a36Sopenharmony_ci u8 name[4]; 12662306a36Sopenharmony_ci u32 sensor_id; 12762306a36Sopenharmony_ci }; 12862306a36Sopenharmony_ci u8 flags; 12962306a36Sopenharmony_ci u8 reserved; 13062306a36Sopenharmony_ci u8 data[6]; 13162306a36Sopenharmony_ci} __packed; 13262306a36Sopenharmony_ci 13362306a36Sopenharmony_cistatic int occ_poll(struct occ *occ) 13462306a36Sopenharmony_ci{ 13562306a36Sopenharmony_ci int rc; 13662306a36Sopenharmony_ci u8 cmd[7]; 13762306a36Sopenharmony_ci struct occ_poll_response_header *header; 13862306a36Sopenharmony_ci 13962306a36Sopenharmony_ci /* big endian */ 14062306a36Sopenharmony_ci cmd[0] = 0; /* sequence number */ 14162306a36Sopenharmony_ci cmd[1] = 0; /* cmd type */ 14262306a36Sopenharmony_ci cmd[2] = 0; /* data length msb */ 14362306a36Sopenharmony_ci cmd[3] = 1; /* data length lsb */ 14462306a36Sopenharmony_ci cmd[4] = occ->poll_cmd_data; /* data */ 14562306a36Sopenharmony_ci cmd[5] = 0; /* checksum msb */ 14662306a36Sopenharmony_ci cmd[6] = 0; /* checksum lsb */ 14762306a36Sopenharmony_ci 14862306a36Sopenharmony_ci /* mutex should already be locked if necessary */ 14962306a36Sopenharmony_ci rc = occ->send_cmd(occ, cmd, sizeof(cmd), &occ->resp, sizeof(occ->resp)); 15062306a36Sopenharmony_ci if (rc) { 15162306a36Sopenharmony_ci occ->last_error = rc; 15262306a36Sopenharmony_ci if (occ->error_count++ > OCC_ERROR_COUNT_THRESHOLD) 15362306a36Sopenharmony_ci occ->error = rc; 15462306a36Sopenharmony_ci 15562306a36Sopenharmony_ci goto done; 15662306a36Sopenharmony_ci } 15762306a36Sopenharmony_ci 15862306a36Sopenharmony_ci /* clear error since communication was successful */ 15962306a36Sopenharmony_ci occ->error_count = 0; 16062306a36Sopenharmony_ci occ->last_error = 0; 16162306a36Sopenharmony_ci occ->error = 0; 16262306a36Sopenharmony_ci 16362306a36Sopenharmony_ci /* check for safe state */ 16462306a36Sopenharmony_ci header = (struct occ_poll_response_header *)occ->resp.data; 16562306a36Sopenharmony_ci if (header->occ_state == OCC_STATE_SAFE) { 16662306a36Sopenharmony_ci if (occ->last_safe) { 16762306a36Sopenharmony_ci if (time_after(jiffies, 16862306a36Sopenharmony_ci occ->last_safe + OCC_SAFE_TIMEOUT)) 16962306a36Sopenharmony_ci occ->error = -EHOSTDOWN; 17062306a36Sopenharmony_ci } else { 17162306a36Sopenharmony_ci occ->last_safe = jiffies; 17262306a36Sopenharmony_ci } 17362306a36Sopenharmony_ci } else { 17462306a36Sopenharmony_ci occ->last_safe = 0; 17562306a36Sopenharmony_ci } 17662306a36Sopenharmony_ci 17762306a36Sopenharmony_cidone: 17862306a36Sopenharmony_ci occ_sysfs_poll_done(occ); 17962306a36Sopenharmony_ci return rc; 18062306a36Sopenharmony_ci} 18162306a36Sopenharmony_ci 18262306a36Sopenharmony_cistatic int occ_set_user_power_cap(struct occ *occ, u16 user_power_cap) 18362306a36Sopenharmony_ci{ 18462306a36Sopenharmony_ci int rc; 18562306a36Sopenharmony_ci u8 cmd[8]; 18662306a36Sopenharmony_ci u8 resp[8]; 18762306a36Sopenharmony_ci __be16 user_power_cap_be = cpu_to_be16(user_power_cap); 18862306a36Sopenharmony_ci 18962306a36Sopenharmony_ci cmd[0] = 0; /* sequence number */ 19062306a36Sopenharmony_ci cmd[1] = 0x22; /* cmd type */ 19162306a36Sopenharmony_ci cmd[2] = 0; /* data length msb */ 19262306a36Sopenharmony_ci cmd[3] = 2; /* data length lsb */ 19362306a36Sopenharmony_ci 19462306a36Sopenharmony_ci memcpy(&cmd[4], &user_power_cap_be, 2); 19562306a36Sopenharmony_ci 19662306a36Sopenharmony_ci cmd[6] = 0; /* checksum msb */ 19762306a36Sopenharmony_ci cmd[7] = 0; /* checksum lsb */ 19862306a36Sopenharmony_ci 19962306a36Sopenharmony_ci rc = mutex_lock_interruptible(&occ->lock); 20062306a36Sopenharmony_ci if (rc) 20162306a36Sopenharmony_ci return rc; 20262306a36Sopenharmony_ci 20362306a36Sopenharmony_ci rc = occ->send_cmd(occ, cmd, sizeof(cmd), resp, sizeof(resp)); 20462306a36Sopenharmony_ci 20562306a36Sopenharmony_ci mutex_unlock(&occ->lock); 20662306a36Sopenharmony_ci 20762306a36Sopenharmony_ci return rc; 20862306a36Sopenharmony_ci} 20962306a36Sopenharmony_ci 21062306a36Sopenharmony_ciint occ_update_response(struct occ *occ) 21162306a36Sopenharmony_ci{ 21262306a36Sopenharmony_ci int rc = mutex_lock_interruptible(&occ->lock); 21362306a36Sopenharmony_ci 21462306a36Sopenharmony_ci if (rc) 21562306a36Sopenharmony_ci return rc; 21662306a36Sopenharmony_ci 21762306a36Sopenharmony_ci /* limit the maximum rate of polling the OCC */ 21862306a36Sopenharmony_ci if (time_after(jiffies, occ->next_update)) { 21962306a36Sopenharmony_ci rc = occ_poll(occ); 22062306a36Sopenharmony_ci occ->next_update = jiffies + OCC_UPDATE_FREQUENCY; 22162306a36Sopenharmony_ci } else { 22262306a36Sopenharmony_ci rc = occ->last_error; 22362306a36Sopenharmony_ci } 22462306a36Sopenharmony_ci 22562306a36Sopenharmony_ci mutex_unlock(&occ->lock); 22662306a36Sopenharmony_ci return rc; 22762306a36Sopenharmony_ci} 22862306a36Sopenharmony_ci 22962306a36Sopenharmony_cistatic ssize_t occ_show_temp_1(struct device *dev, 23062306a36Sopenharmony_ci struct device_attribute *attr, char *buf) 23162306a36Sopenharmony_ci{ 23262306a36Sopenharmony_ci int rc; 23362306a36Sopenharmony_ci u32 val = 0; 23462306a36Sopenharmony_ci struct temp_sensor_1 *temp; 23562306a36Sopenharmony_ci struct occ *occ = dev_get_drvdata(dev); 23662306a36Sopenharmony_ci struct occ_sensors *sensors = &occ->sensors; 23762306a36Sopenharmony_ci struct sensor_device_attribute_2 *sattr = to_sensor_dev_attr_2(attr); 23862306a36Sopenharmony_ci 23962306a36Sopenharmony_ci rc = occ_update_response(occ); 24062306a36Sopenharmony_ci if (rc) 24162306a36Sopenharmony_ci return rc; 24262306a36Sopenharmony_ci 24362306a36Sopenharmony_ci temp = ((struct temp_sensor_1 *)sensors->temp.data) + sattr->index; 24462306a36Sopenharmony_ci 24562306a36Sopenharmony_ci switch (sattr->nr) { 24662306a36Sopenharmony_ci case 0: 24762306a36Sopenharmony_ci val = get_unaligned_be16(&temp->sensor_id); 24862306a36Sopenharmony_ci break; 24962306a36Sopenharmony_ci case 1: 25062306a36Sopenharmony_ci /* 25162306a36Sopenharmony_ci * If a sensor reading has expired and couldn't be refreshed, 25262306a36Sopenharmony_ci * OCC returns 0xFFFF for that sensor. 25362306a36Sopenharmony_ci */ 25462306a36Sopenharmony_ci if (temp->value == 0xFFFF) 25562306a36Sopenharmony_ci return -EREMOTEIO; 25662306a36Sopenharmony_ci val = get_unaligned_be16(&temp->value) * 1000; 25762306a36Sopenharmony_ci break; 25862306a36Sopenharmony_ci default: 25962306a36Sopenharmony_ci return -EINVAL; 26062306a36Sopenharmony_ci } 26162306a36Sopenharmony_ci 26262306a36Sopenharmony_ci return sysfs_emit(buf, "%u\n", val); 26362306a36Sopenharmony_ci} 26462306a36Sopenharmony_ci 26562306a36Sopenharmony_cistatic ssize_t occ_show_temp_2(struct device *dev, 26662306a36Sopenharmony_ci struct device_attribute *attr, char *buf) 26762306a36Sopenharmony_ci{ 26862306a36Sopenharmony_ci int rc; 26962306a36Sopenharmony_ci u32 val = 0; 27062306a36Sopenharmony_ci struct temp_sensor_2 *temp; 27162306a36Sopenharmony_ci struct occ *occ = dev_get_drvdata(dev); 27262306a36Sopenharmony_ci struct occ_sensors *sensors = &occ->sensors; 27362306a36Sopenharmony_ci struct sensor_device_attribute_2 *sattr = to_sensor_dev_attr_2(attr); 27462306a36Sopenharmony_ci 27562306a36Sopenharmony_ci rc = occ_update_response(occ); 27662306a36Sopenharmony_ci if (rc) 27762306a36Sopenharmony_ci return rc; 27862306a36Sopenharmony_ci 27962306a36Sopenharmony_ci temp = ((struct temp_sensor_2 *)sensors->temp.data) + sattr->index; 28062306a36Sopenharmony_ci 28162306a36Sopenharmony_ci switch (sattr->nr) { 28262306a36Sopenharmony_ci case 0: 28362306a36Sopenharmony_ci val = get_unaligned_be32(&temp->sensor_id); 28462306a36Sopenharmony_ci break; 28562306a36Sopenharmony_ci case 1: 28662306a36Sopenharmony_ci val = temp->value; 28762306a36Sopenharmony_ci if (val == OCC_TEMP_SENSOR_FAULT) 28862306a36Sopenharmony_ci return -EREMOTEIO; 28962306a36Sopenharmony_ci 29062306a36Sopenharmony_ci /* 29162306a36Sopenharmony_ci * VRM doesn't return temperature, only alarm bit. This 29262306a36Sopenharmony_ci * attribute maps to tempX_alarm instead of tempX_input for 29362306a36Sopenharmony_ci * VRM 29462306a36Sopenharmony_ci */ 29562306a36Sopenharmony_ci if (temp->fru_type != OCC_FRU_TYPE_VRM) { 29662306a36Sopenharmony_ci /* sensor not ready */ 29762306a36Sopenharmony_ci if (val == 0) 29862306a36Sopenharmony_ci return -EAGAIN; 29962306a36Sopenharmony_ci 30062306a36Sopenharmony_ci val *= 1000; 30162306a36Sopenharmony_ci } 30262306a36Sopenharmony_ci break; 30362306a36Sopenharmony_ci case 2: 30462306a36Sopenharmony_ci val = temp->fru_type; 30562306a36Sopenharmony_ci break; 30662306a36Sopenharmony_ci case 3: 30762306a36Sopenharmony_ci val = temp->value == OCC_TEMP_SENSOR_FAULT; 30862306a36Sopenharmony_ci break; 30962306a36Sopenharmony_ci default: 31062306a36Sopenharmony_ci return -EINVAL; 31162306a36Sopenharmony_ci } 31262306a36Sopenharmony_ci 31362306a36Sopenharmony_ci return sysfs_emit(buf, "%u\n", val); 31462306a36Sopenharmony_ci} 31562306a36Sopenharmony_ci 31662306a36Sopenharmony_cistatic ssize_t occ_show_temp_10(struct device *dev, 31762306a36Sopenharmony_ci struct device_attribute *attr, char *buf) 31862306a36Sopenharmony_ci{ 31962306a36Sopenharmony_ci int rc; 32062306a36Sopenharmony_ci u32 val = 0; 32162306a36Sopenharmony_ci struct temp_sensor_10 *temp; 32262306a36Sopenharmony_ci struct occ *occ = dev_get_drvdata(dev); 32362306a36Sopenharmony_ci struct occ_sensors *sensors = &occ->sensors; 32462306a36Sopenharmony_ci struct sensor_device_attribute_2 *sattr = to_sensor_dev_attr_2(attr); 32562306a36Sopenharmony_ci 32662306a36Sopenharmony_ci rc = occ_update_response(occ); 32762306a36Sopenharmony_ci if (rc) 32862306a36Sopenharmony_ci return rc; 32962306a36Sopenharmony_ci 33062306a36Sopenharmony_ci temp = ((struct temp_sensor_10 *)sensors->temp.data) + sattr->index; 33162306a36Sopenharmony_ci 33262306a36Sopenharmony_ci switch (sattr->nr) { 33362306a36Sopenharmony_ci case 0: 33462306a36Sopenharmony_ci val = get_unaligned_be32(&temp->sensor_id); 33562306a36Sopenharmony_ci break; 33662306a36Sopenharmony_ci case 1: 33762306a36Sopenharmony_ci val = temp->value; 33862306a36Sopenharmony_ci if (val == OCC_TEMP_SENSOR_FAULT) 33962306a36Sopenharmony_ci return -EREMOTEIO; 34062306a36Sopenharmony_ci 34162306a36Sopenharmony_ci /* sensor not ready */ 34262306a36Sopenharmony_ci if (val == 0) 34362306a36Sopenharmony_ci return -EAGAIN; 34462306a36Sopenharmony_ci 34562306a36Sopenharmony_ci val *= 1000; 34662306a36Sopenharmony_ci break; 34762306a36Sopenharmony_ci case 2: 34862306a36Sopenharmony_ci val = temp->fru_type; 34962306a36Sopenharmony_ci break; 35062306a36Sopenharmony_ci case 3: 35162306a36Sopenharmony_ci val = temp->value == OCC_TEMP_SENSOR_FAULT; 35262306a36Sopenharmony_ci break; 35362306a36Sopenharmony_ci case 4: 35462306a36Sopenharmony_ci val = temp->throttle * 1000; 35562306a36Sopenharmony_ci break; 35662306a36Sopenharmony_ci default: 35762306a36Sopenharmony_ci return -EINVAL; 35862306a36Sopenharmony_ci } 35962306a36Sopenharmony_ci 36062306a36Sopenharmony_ci return sysfs_emit(buf, "%u\n", val); 36162306a36Sopenharmony_ci} 36262306a36Sopenharmony_ci 36362306a36Sopenharmony_cistatic ssize_t occ_show_freq_1(struct device *dev, 36462306a36Sopenharmony_ci struct device_attribute *attr, char *buf) 36562306a36Sopenharmony_ci{ 36662306a36Sopenharmony_ci int rc; 36762306a36Sopenharmony_ci u16 val = 0; 36862306a36Sopenharmony_ci struct freq_sensor_1 *freq; 36962306a36Sopenharmony_ci struct occ *occ = dev_get_drvdata(dev); 37062306a36Sopenharmony_ci struct occ_sensors *sensors = &occ->sensors; 37162306a36Sopenharmony_ci struct sensor_device_attribute_2 *sattr = to_sensor_dev_attr_2(attr); 37262306a36Sopenharmony_ci 37362306a36Sopenharmony_ci rc = occ_update_response(occ); 37462306a36Sopenharmony_ci if (rc) 37562306a36Sopenharmony_ci return rc; 37662306a36Sopenharmony_ci 37762306a36Sopenharmony_ci freq = ((struct freq_sensor_1 *)sensors->freq.data) + sattr->index; 37862306a36Sopenharmony_ci 37962306a36Sopenharmony_ci switch (sattr->nr) { 38062306a36Sopenharmony_ci case 0: 38162306a36Sopenharmony_ci val = get_unaligned_be16(&freq->sensor_id); 38262306a36Sopenharmony_ci break; 38362306a36Sopenharmony_ci case 1: 38462306a36Sopenharmony_ci val = get_unaligned_be16(&freq->value); 38562306a36Sopenharmony_ci break; 38662306a36Sopenharmony_ci default: 38762306a36Sopenharmony_ci return -EINVAL; 38862306a36Sopenharmony_ci } 38962306a36Sopenharmony_ci 39062306a36Sopenharmony_ci return sysfs_emit(buf, "%u\n", val); 39162306a36Sopenharmony_ci} 39262306a36Sopenharmony_ci 39362306a36Sopenharmony_cistatic ssize_t occ_show_freq_2(struct device *dev, 39462306a36Sopenharmony_ci struct device_attribute *attr, char *buf) 39562306a36Sopenharmony_ci{ 39662306a36Sopenharmony_ci int rc; 39762306a36Sopenharmony_ci u32 val = 0; 39862306a36Sopenharmony_ci struct freq_sensor_2 *freq; 39962306a36Sopenharmony_ci struct occ *occ = dev_get_drvdata(dev); 40062306a36Sopenharmony_ci struct occ_sensors *sensors = &occ->sensors; 40162306a36Sopenharmony_ci struct sensor_device_attribute_2 *sattr = to_sensor_dev_attr_2(attr); 40262306a36Sopenharmony_ci 40362306a36Sopenharmony_ci rc = occ_update_response(occ); 40462306a36Sopenharmony_ci if (rc) 40562306a36Sopenharmony_ci return rc; 40662306a36Sopenharmony_ci 40762306a36Sopenharmony_ci freq = ((struct freq_sensor_2 *)sensors->freq.data) + sattr->index; 40862306a36Sopenharmony_ci 40962306a36Sopenharmony_ci switch (sattr->nr) { 41062306a36Sopenharmony_ci case 0: 41162306a36Sopenharmony_ci val = get_unaligned_be32(&freq->sensor_id); 41262306a36Sopenharmony_ci break; 41362306a36Sopenharmony_ci case 1: 41462306a36Sopenharmony_ci val = get_unaligned_be16(&freq->value); 41562306a36Sopenharmony_ci break; 41662306a36Sopenharmony_ci default: 41762306a36Sopenharmony_ci return -EINVAL; 41862306a36Sopenharmony_ci } 41962306a36Sopenharmony_ci 42062306a36Sopenharmony_ci return sysfs_emit(buf, "%u\n", val); 42162306a36Sopenharmony_ci} 42262306a36Sopenharmony_ci 42362306a36Sopenharmony_cistatic ssize_t occ_show_power_1(struct device *dev, 42462306a36Sopenharmony_ci struct device_attribute *attr, char *buf) 42562306a36Sopenharmony_ci{ 42662306a36Sopenharmony_ci int rc; 42762306a36Sopenharmony_ci u64 val = 0; 42862306a36Sopenharmony_ci struct power_sensor_1 *power; 42962306a36Sopenharmony_ci struct occ *occ = dev_get_drvdata(dev); 43062306a36Sopenharmony_ci struct occ_sensors *sensors = &occ->sensors; 43162306a36Sopenharmony_ci struct sensor_device_attribute_2 *sattr = to_sensor_dev_attr_2(attr); 43262306a36Sopenharmony_ci 43362306a36Sopenharmony_ci rc = occ_update_response(occ); 43462306a36Sopenharmony_ci if (rc) 43562306a36Sopenharmony_ci return rc; 43662306a36Sopenharmony_ci 43762306a36Sopenharmony_ci power = ((struct power_sensor_1 *)sensors->power.data) + sattr->index; 43862306a36Sopenharmony_ci 43962306a36Sopenharmony_ci switch (sattr->nr) { 44062306a36Sopenharmony_ci case 0: 44162306a36Sopenharmony_ci val = get_unaligned_be16(&power->sensor_id); 44262306a36Sopenharmony_ci break; 44362306a36Sopenharmony_ci case 1: 44462306a36Sopenharmony_ci val = get_unaligned_be32(&power->accumulator) / 44562306a36Sopenharmony_ci get_unaligned_be32(&power->update_tag); 44662306a36Sopenharmony_ci val *= 1000000ULL; 44762306a36Sopenharmony_ci break; 44862306a36Sopenharmony_ci case 2: 44962306a36Sopenharmony_ci val = (u64)get_unaligned_be32(&power->update_tag) * 45062306a36Sopenharmony_ci occ->powr_sample_time_us; 45162306a36Sopenharmony_ci break; 45262306a36Sopenharmony_ci case 3: 45362306a36Sopenharmony_ci val = get_unaligned_be16(&power->value) * 1000000ULL; 45462306a36Sopenharmony_ci break; 45562306a36Sopenharmony_ci default: 45662306a36Sopenharmony_ci return -EINVAL; 45762306a36Sopenharmony_ci } 45862306a36Sopenharmony_ci 45962306a36Sopenharmony_ci return sysfs_emit(buf, "%llu\n", val); 46062306a36Sopenharmony_ci} 46162306a36Sopenharmony_ci 46262306a36Sopenharmony_cistatic u64 occ_get_powr_avg(u64 *accum, u32 *samples) 46362306a36Sopenharmony_ci{ 46462306a36Sopenharmony_ci u64 divisor = get_unaligned_be32(samples); 46562306a36Sopenharmony_ci 46662306a36Sopenharmony_ci return (divisor == 0) ? 0 : 46762306a36Sopenharmony_ci div64_u64(get_unaligned_be64(accum) * 1000000ULL, divisor); 46862306a36Sopenharmony_ci} 46962306a36Sopenharmony_ci 47062306a36Sopenharmony_cistatic ssize_t occ_show_power_2(struct device *dev, 47162306a36Sopenharmony_ci struct device_attribute *attr, char *buf) 47262306a36Sopenharmony_ci{ 47362306a36Sopenharmony_ci int rc; 47462306a36Sopenharmony_ci u64 val = 0; 47562306a36Sopenharmony_ci struct power_sensor_2 *power; 47662306a36Sopenharmony_ci struct occ *occ = dev_get_drvdata(dev); 47762306a36Sopenharmony_ci struct occ_sensors *sensors = &occ->sensors; 47862306a36Sopenharmony_ci struct sensor_device_attribute_2 *sattr = to_sensor_dev_attr_2(attr); 47962306a36Sopenharmony_ci 48062306a36Sopenharmony_ci rc = occ_update_response(occ); 48162306a36Sopenharmony_ci if (rc) 48262306a36Sopenharmony_ci return rc; 48362306a36Sopenharmony_ci 48462306a36Sopenharmony_ci power = ((struct power_sensor_2 *)sensors->power.data) + sattr->index; 48562306a36Sopenharmony_ci 48662306a36Sopenharmony_ci switch (sattr->nr) { 48762306a36Sopenharmony_ci case 0: 48862306a36Sopenharmony_ci return sysfs_emit(buf, "%u_%u_%u\n", 48962306a36Sopenharmony_ci get_unaligned_be32(&power->sensor_id), 49062306a36Sopenharmony_ci power->function_id, power->apss_channel); 49162306a36Sopenharmony_ci case 1: 49262306a36Sopenharmony_ci val = occ_get_powr_avg(&power->accumulator, 49362306a36Sopenharmony_ci &power->update_tag); 49462306a36Sopenharmony_ci break; 49562306a36Sopenharmony_ci case 2: 49662306a36Sopenharmony_ci val = (u64)get_unaligned_be32(&power->update_tag) * 49762306a36Sopenharmony_ci occ->powr_sample_time_us; 49862306a36Sopenharmony_ci break; 49962306a36Sopenharmony_ci case 3: 50062306a36Sopenharmony_ci val = get_unaligned_be16(&power->value) * 1000000ULL; 50162306a36Sopenharmony_ci break; 50262306a36Sopenharmony_ci default: 50362306a36Sopenharmony_ci return -EINVAL; 50462306a36Sopenharmony_ci } 50562306a36Sopenharmony_ci 50662306a36Sopenharmony_ci return sysfs_emit(buf, "%llu\n", val); 50762306a36Sopenharmony_ci} 50862306a36Sopenharmony_ci 50962306a36Sopenharmony_cistatic ssize_t occ_show_power_a0(struct device *dev, 51062306a36Sopenharmony_ci struct device_attribute *attr, char *buf) 51162306a36Sopenharmony_ci{ 51262306a36Sopenharmony_ci int rc; 51362306a36Sopenharmony_ci u64 val = 0; 51462306a36Sopenharmony_ci struct power_sensor_a0 *power; 51562306a36Sopenharmony_ci struct occ *occ = dev_get_drvdata(dev); 51662306a36Sopenharmony_ci struct occ_sensors *sensors = &occ->sensors; 51762306a36Sopenharmony_ci struct sensor_device_attribute_2 *sattr = to_sensor_dev_attr_2(attr); 51862306a36Sopenharmony_ci 51962306a36Sopenharmony_ci rc = occ_update_response(occ); 52062306a36Sopenharmony_ci if (rc) 52162306a36Sopenharmony_ci return rc; 52262306a36Sopenharmony_ci 52362306a36Sopenharmony_ci power = ((struct power_sensor_a0 *)sensors->power.data) + sattr->index; 52462306a36Sopenharmony_ci 52562306a36Sopenharmony_ci switch (sattr->nr) { 52662306a36Sopenharmony_ci case 0: 52762306a36Sopenharmony_ci return sysfs_emit(buf, "%u_system\n", 52862306a36Sopenharmony_ci get_unaligned_be32(&power->sensor_id)); 52962306a36Sopenharmony_ci case 1: 53062306a36Sopenharmony_ci val = occ_get_powr_avg(&power->system.accumulator, 53162306a36Sopenharmony_ci &power->system.update_tag); 53262306a36Sopenharmony_ci break; 53362306a36Sopenharmony_ci case 2: 53462306a36Sopenharmony_ci val = (u64)get_unaligned_be32(&power->system.update_tag) * 53562306a36Sopenharmony_ci occ->powr_sample_time_us; 53662306a36Sopenharmony_ci break; 53762306a36Sopenharmony_ci case 3: 53862306a36Sopenharmony_ci val = get_unaligned_be16(&power->system.value) * 1000000ULL; 53962306a36Sopenharmony_ci break; 54062306a36Sopenharmony_ci case 4: 54162306a36Sopenharmony_ci return sysfs_emit(buf, "%u_proc\n", 54262306a36Sopenharmony_ci get_unaligned_be32(&power->sensor_id)); 54362306a36Sopenharmony_ci case 5: 54462306a36Sopenharmony_ci val = occ_get_powr_avg(&power->proc.accumulator, 54562306a36Sopenharmony_ci &power->proc.update_tag); 54662306a36Sopenharmony_ci break; 54762306a36Sopenharmony_ci case 6: 54862306a36Sopenharmony_ci val = (u64)get_unaligned_be32(&power->proc.update_tag) * 54962306a36Sopenharmony_ci occ->powr_sample_time_us; 55062306a36Sopenharmony_ci break; 55162306a36Sopenharmony_ci case 7: 55262306a36Sopenharmony_ci val = get_unaligned_be16(&power->proc.value) * 1000000ULL; 55362306a36Sopenharmony_ci break; 55462306a36Sopenharmony_ci case 8: 55562306a36Sopenharmony_ci return sysfs_emit(buf, "%u_vdd\n", 55662306a36Sopenharmony_ci get_unaligned_be32(&power->sensor_id)); 55762306a36Sopenharmony_ci case 9: 55862306a36Sopenharmony_ci val = occ_get_powr_avg(&power->vdd.accumulator, 55962306a36Sopenharmony_ci &power->vdd.update_tag); 56062306a36Sopenharmony_ci break; 56162306a36Sopenharmony_ci case 10: 56262306a36Sopenharmony_ci val = (u64)get_unaligned_be32(&power->vdd.update_tag) * 56362306a36Sopenharmony_ci occ->powr_sample_time_us; 56462306a36Sopenharmony_ci break; 56562306a36Sopenharmony_ci case 11: 56662306a36Sopenharmony_ci val = get_unaligned_be16(&power->vdd.value) * 1000000ULL; 56762306a36Sopenharmony_ci break; 56862306a36Sopenharmony_ci case 12: 56962306a36Sopenharmony_ci return sysfs_emit(buf, "%u_vdn\n", 57062306a36Sopenharmony_ci get_unaligned_be32(&power->sensor_id)); 57162306a36Sopenharmony_ci case 13: 57262306a36Sopenharmony_ci val = occ_get_powr_avg(&power->vdn.accumulator, 57362306a36Sopenharmony_ci &power->vdn.update_tag); 57462306a36Sopenharmony_ci break; 57562306a36Sopenharmony_ci case 14: 57662306a36Sopenharmony_ci val = (u64)get_unaligned_be32(&power->vdn.update_tag) * 57762306a36Sopenharmony_ci occ->powr_sample_time_us; 57862306a36Sopenharmony_ci break; 57962306a36Sopenharmony_ci case 15: 58062306a36Sopenharmony_ci val = get_unaligned_be16(&power->vdn.value) * 1000000ULL; 58162306a36Sopenharmony_ci break; 58262306a36Sopenharmony_ci default: 58362306a36Sopenharmony_ci return -EINVAL; 58462306a36Sopenharmony_ci } 58562306a36Sopenharmony_ci 58662306a36Sopenharmony_ci return sysfs_emit(buf, "%llu\n", val); 58762306a36Sopenharmony_ci} 58862306a36Sopenharmony_ci 58962306a36Sopenharmony_cistatic ssize_t occ_show_caps_1_2(struct device *dev, 59062306a36Sopenharmony_ci struct device_attribute *attr, char *buf) 59162306a36Sopenharmony_ci{ 59262306a36Sopenharmony_ci int rc; 59362306a36Sopenharmony_ci u64 val = 0; 59462306a36Sopenharmony_ci struct caps_sensor_2 *caps; 59562306a36Sopenharmony_ci struct occ *occ = dev_get_drvdata(dev); 59662306a36Sopenharmony_ci struct occ_sensors *sensors = &occ->sensors; 59762306a36Sopenharmony_ci struct sensor_device_attribute_2 *sattr = to_sensor_dev_attr_2(attr); 59862306a36Sopenharmony_ci 59962306a36Sopenharmony_ci rc = occ_update_response(occ); 60062306a36Sopenharmony_ci if (rc) 60162306a36Sopenharmony_ci return rc; 60262306a36Sopenharmony_ci 60362306a36Sopenharmony_ci caps = ((struct caps_sensor_2 *)sensors->caps.data) + sattr->index; 60462306a36Sopenharmony_ci 60562306a36Sopenharmony_ci switch (sattr->nr) { 60662306a36Sopenharmony_ci case 0: 60762306a36Sopenharmony_ci return sysfs_emit(buf, "system\n"); 60862306a36Sopenharmony_ci case 1: 60962306a36Sopenharmony_ci val = get_unaligned_be16(&caps->cap) * 1000000ULL; 61062306a36Sopenharmony_ci break; 61162306a36Sopenharmony_ci case 2: 61262306a36Sopenharmony_ci val = get_unaligned_be16(&caps->system_power) * 1000000ULL; 61362306a36Sopenharmony_ci break; 61462306a36Sopenharmony_ci case 3: 61562306a36Sopenharmony_ci val = get_unaligned_be16(&caps->n_cap) * 1000000ULL; 61662306a36Sopenharmony_ci break; 61762306a36Sopenharmony_ci case 4: 61862306a36Sopenharmony_ci val = get_unaligned_be16(&caps->max) * 1000000ULL; 61962306a36Sopenharmony_ci break; 62062306a36Sopenharmony_ci case 5: 62162306a36Sopenharmony_ci val = get_unaligned_be16(&caps->min) * 1000000ULL; 62262306a36Sopenharmony_ci break; 62362306a36Sopenharmony_ci case 6: 62462306a36Sopenharmony_ci val = get_unaligned_be16(&caps->user) * 1000000ULL; 62562306a36Sopenharmony_ci break; 62662306a36Sopenharmony_ci case 7: 62762306a36Sopenharmony_ci if (occ->sensors.caps.version == 1) 62862306a36Sopenharmony_ci return -EINVAL; 62962306a36Sopenharmony_ci 63062306a36Sopenharmony_ci val = caps->user_source; 63162306a36Sopenharmony_ci break; 63262306a36Sopenharmony_ci default: 63362306a36Sopenharmony_ci return -EINVAL; 63462306a36Sopenharmony_ci } 63562306a36Sopenharmony_ci 63662306a36Sopenharmony_ci return sysfs_emit(buf, "%llu\n", val); 63762306a36Sopenharmony_ci} 63862306a36Sopenharmony_ci 63962306a36Sopenharmony_cistatic ssize_t occ_show_caps_3(struct device *dev, 64062306a36Sopenharmony_ci struct device_attribute *attr, char *buf) 64162306a36Sopenharmony_ci{ 64262306a36Sopenharmony_ci int rc; 64362306a36Sopenharmony_ci u64 val = 0; 64462306a36Sopenharmony_ci struct caps_sensor_3 *caps; 64562306a36Sopenharmony_ci struct occ *occ = dev_get_drvdata(dev); 64662306a36Sopenharmony_ci struct occ_sensors *sensors = &occ->sensors; 64762306a36Sopenharmony_ci struct sensor_device_attribute_2 *sattr = to_sensor_dev_attr_2(attr); 64862306a36Sopenharmony_ci 64962306a36Sopenharmony_ci rc = occ_update_response(occ); 65062306a36Sopenharmony_ci if (rc) 65162306a36Sopenharmony_ci return rc; 65262306a36Sopenharmony_ci 65362306a36Sopenharmony_ci caps = ((struct caps_sensor_3 *)sensors->caps.data) + sattr->index; 65462306a36Sopenharmony_ci 65562306a36Sopenharmony_ci switch (sattr->nr) { 65662306a36Sopenharmony_ci case 0: 65762306a36Sopenharmony_ci return sysfs_emit(buf, "system\n"); 65862306a36Sopenharmony_ci case 1: 65962306a36Sopenharmony_ci val = get_unaligned_be16(&caps->cap) * 1000000ULL; 66062306a36Sopenharmony_ci break; 66162306a36Sopenharmony_ci case 2: 66262306a36Sopenharmony_ci val = get_unaligned_be16(&caps->system_power) * 1000000ULL; 66362306a36Sopenharmony_ci break; 66462306a36Sopenharmony_ci case 3: 66562306a36Sopenharmony_ci val = get_unaligned_be16(&caps->n_cap) * 1000000ULL; 66662306a36Sopenharmony_ci break; 66762306a36Sopenharmony_ci case 4: 66862306a36Sopenharmony_ci val = get_unaligned_be16(&caps->max) * 1000000ULL; 66962306a36Sopenharmony_ci break; 67062306a36Sopenharmony_ci case 5: 67162306a36Sopenharmony_ci val = get_unaligned_be16(&caps->hard_min) * 1000000ULL; 67262306a36Sopenharmony_ci break; 67362306a36Sopenharmony_ci case 6: 67462306a36Sopenharmony_ci val = get_unaligned_be16(&caps->user) * 1000000ULL; 67562306a36Sopenharmony_ci break; 67662306a36Sopenharmony_ci case 7: 67762306a36Sopenharmony_ci val = caps->user_source; 67862306a36Sopenharmony_ci break; 67962306a36Sopenharmony_ci case 8: 68062306a36Sopenharmony_ci val = get_unaligned_be16(&caps->soft_min) * 1000000ULL; 68162306a36Sopenharmony_ci break; 68262306a36Sopenharmony_ci default: 68362306a36Sopenharmony_ci return -EINVAL; 68462306a36Sopenharmony_ci } 68562306a36Sopenharmony_ci 68662306a36Sopenharmony_ci return sysfs_emit(buf, "%llu\n", val); 68762306a36Sopenharmony_ci} 68862306a36Sopenharmony_ci 68962306a36Sopenharmony_cistatic ssize_t occ_store_caps_user(struct device *dev, 69062306a36Sopenharmony_ci struct device_attribute *attr, 69162306a36Sopenharmony_ci const char *buf, size_t count) 69262306a36Sopenharmony_ci{ 69362306a36Sopenharmony_ci int rc; 69462306a36Sopenharmony_ci u16 user_power_cap; 69562306a36Sopenharmony_ci unsigned long long value; 69662306a36Sopenharmony_ci struct occ *occ = dev_get_drvdata(dev); 69762306a36Sopenharmony_ci 69862306a36Sopenharmony_ci rc = kstrtoull(buf, 0, &value); 69962306a36Sopenharmony_ci if (rc) 70062306a36Sopenharmony_ci return rc; 70162306a36Sopenharmony_ci 70262306a36Sopenharmony_ci user_power_cap = div64_u64(value, 1000000ULL); /* microwatt to watt */ 70362306a36Sopenharmony_ci 70462306a36Sopenharmony_ci rc = occ_set_user_power_cap(occ, user_power_cap); 70562306a36Sopenharmony_ci if (rc) 70662306a36Sopenharmony_ci return rc; 70762306a36Sopenharmony_ci 70862306a36Sopenharmony_ci return count; 70962306a36Sopenharmony_ci} 71062306a36Sopenharmony_ci 71162306a36Sopenharmony_cistatic ssize_t occ_show_extended(struct device *dev, 71262306a36Sopenharmony_ci struct device_attribute *attr, char *buf) 71362306a36Sopenharmony_ci{ 71462306a36Sopenharmony_ci int rc; 71562306a36Sopenharmony_ci struct extended_sensor *extn; 71662306a36Sopenharmony_ci struct occ *occ = dev_get_drvdata(dev); 71762306a36Sopenharmony_ci struct occ_sensors *sensors = &occ->sensors; 71862306a36Sopenharmony_ci struct sensor_device_attribute_2 *sattr = to_sensor_dev_attr_2(attr); 71962306a36Sopenharmony_ci 72062306a36Sopenharmony_ci rc = occ_update_response(occ); 72162306a36Sopenharmony_ci if (rc) 72262306a36Sopenharmony_ci return rc; 72362306a36Sopenharmony_ci 72462306a36Sopenharmony_ci extn = ((struct extended_sensor *)sensors->extended.data) + 72562306a36Sopenharmony_ci sattr->index; 72662306a36Sopenharmony_ci 72762306a36Sopenharmony_ci switch (sattr->nr) { 72862306a36Sopenharmony_ci case 0: 72962306a36Sopenharmony_ci if (extn->flags & EXTN_FLAG_SENSOR_ID) { 73062306a36Sopenharmony_ci rc = sysfs_emit(buf, "%u", 73162306a36Sopenharmony_ci get_unaligned_be32(&extn->sensor_id)); 73262306a36Sopenharmony_ci } else { 73362306a36Sopenharmony_ci rc = sysfs_emit(buf, "%4phN\n", extn->name); 73462306a36Sopenharmony_ci } 73562306a36Sopenharmony_ci break; 73662306a36Sopenharmony_ci case 1: 73762306a36Sopenharmony_ci rc = sysfs_emit(buf, "%02x\n", extn->flags); 73862306a36Sopenharmony_ci break; 73962306a36Sopenharmony_ci case 2: 74062306a36Sopenharmony_ci rc = sysfs_emit(buf, "%6phN\n", extn->data); 74162306a36Sopenharmony_ci break; 74262306a36Sopenharmony_ci default: 74362306a36Sopenharmony_ci return -EINVAL; 74462306a36Sopenharmony_ci } 74562306a36Sopenharmony_ci 74662306a36Sopenharmony_ci return rc; 74762306a36Sopenharmony_ci} 74862306a36Sopenharmony_ci 74962306a36Sopenharmony_ci/* 75062306a36Sopenharmony_ci * Some helper macros to make it easier to define an occ_attribute. Since these 75162306a36Sopenharmony_ci * are dynamically allocated, we shouldn't use the existing kernel macros which 75262306a36Sopenharmony_ci * stringify the name argument. 75362306a36Sopenharmony_ci */ 75462306a36Sopenharmony_ci#define ATTR_OCC(_name, _mode, _show, _store) { \ 75562306a36Sopenharmony_ci .attr = { \ 75662306a36Sopenharmony_ci .name = _name, \ 75762306a36Sopenharmony_ci .mode = VERIFY_OCTAL_PERMISSIONS(_mode), \ 75862306a36Sopenharmony_ci }, \ 75962306a36Sopenharmony_ci .show = _show, \ 76062306a36Sopenharmony_ci .store = _store, \ 76162306a36Sopenharmony_ci} 76262306a36Sopenharmony_ci 76362306a36Sopenharmony_ci#define SENSOR_ATTR_OCC(_name, _mode, _show, _store, _nr, _index) { \ 76462306a36Sopenharmony_ci .dev_attr = ATTR_OCC(_name, _mode, _show, _store), \ 76562306a36Sopenharmony_ci .index = _index, \ 76662306a36Sopenharmony_ci .nr = _nr, \ 76762306a36Sopenharmony_ci} 76862306a36Sopenharmony_ci 76962306a36Sopenharmony_ci#define OCC_INIT_ATTR(_name, _mode, _show, _store, _nr, _index) \ 77062306a36Sopenharmony_ci ((struct sensor_device_attribute_2) \ 77162306a36Sopenharmony_ci SENSOR_ATTR_OCC(_name, _mode, _show, _store, _nr, _index)) 77262306a36Sopenharmony_ci 77362306a36Sopenharmony_ci/* 77462306a36Sopenharmony_ci * Allocate and instatiate sensor_device_attribute_2s. It's most efficient to 77562306a36Sopenharmony_ci * use our own instead of the built-in hwmon attribute types. 77662306a36Sopenharmony_ci */ 77762306a36Sopenharmony_cistatic int occ_setup_sensor_attrs(struct occ *occ) 77862306a36Sopenharmony_ci{ 77962306a36Sopenharmony_ci unsigned int i, s, num_attrs = 0; 78062306a36Sopenharmony_ci struct device *dev = occ->bus_dev; 78162306a36Sopenharmony_ci struct occ_sensors *sensors = &occ->sensors; 78262306a36Sopenharmony_ci struct occ_attribute *attr; 78362306a36Sopenharmony_ci struct temp_sensor_2 *temp; 78462306a36Sopenharmony_ci ssize_t (*show_temp)(struct device *, struct device_attribute *, 78562306a36Sopenharmony_ci char *) = occ_show_temp_1; 78662306a36Sopenharmony_ci ssize_t (*show_freq)(struct device *, struct device_attribute *, 78762306a36Sopenharmony_ci char *) = occ_show_freq_1; 78862306a36Sopenharmony_ci ssize_t (*show_power)(struct device *, struct device_attribute *, 78962306a36Sopenharmony_ci char *) = occ_show_power_1; 79062306a36Sopenharmony_ci ssize_t (*show_caps)(struct device *, struct device_attribute *, 79162306a36Sopenharmony_ci char *) = occ_show_caps_1_2; 79262306a36Sopenharmony_ci 79362306a36Sopenharmony_ci switch (sensors->temp.version) { 79462306a36Sopenharmony_ci case 1: 79562306a36Sopenharmony_ci num_attrs += (sensors->temp.num_sensors * 2); 79662306a36Sopenharmony_ci break; 79762306a36Sopenharmony_ci case 2: 79862306a36Sopenharmony_ci num_attrs += (sensors->temp.num_sensors * 4); 79962306a36Sopenharmony_ci show_temp = occ_show_temp_2; 80062306a36Sopenharmony_ci break; 80162306a36Sopenharmony_ci case 0x10: 80262306a36Sopenharmony_ci num_attrs += (sensors->temp.num_sensors * 5); 80362306a36Sopenharmony_ci show_temp = occ_show_temp_10; 80462306a36Sopenharmony_ci break; 80562306a36Sopenharmony_ci default: 80662306a36Sopenharmony_ci sensors->temp.num_sensors = 0; 80762306a36Sopenharmony_ci } 80862306a36Sopenharmony_ci 80962306a36Sopenharmony_ci switch (sensors->freq.version) { 81062306a36Sopenharmony_ci case 2: 81162306a36Sopenharmony_ci show_freq = occ_show_freq_2; 81262306a36Sopenharmony_ci fallthrough; 81362306a36Sopenharmony_ci case 1: 81462306a36Sopenharmony_ci num_attrs += (sensors->freq.num_sensors * 2); 81562306a36Sopenharmony_ci break; 81662306a36Sopenharmony_ci default: 81762306a36Sopenharmony_ci sensors->freq.num_sensors = 0; 81862306a36Sopenharmony_ci } 81962306a36Sopenharmony_ci 82062306a36Sopenharmony_ci switch (sensors->power.version) { 82162306a36Sopenharmony_ci case 2: 82262306a36Sopenharmony_ci show_power = occ_show_power_2; 82362306a36Sopenharmony_ci fallthrough; 82462306a36Sopenharmony_ci case 1: 82562306a36Sopenharmony_ci num_attrs += (sensors->power.num_sensors * 4); 82662306a36Sopenharmony_ci break; 82762306a36Sopenharmony_ci case 0xA0: 82862306a36Sopenharmony_ci num_attrs += (sensors->power.num_sensors * 16); 82962306a36Sopenharmony_ci show_power = occ_show_power_a0; 83062306a36Sopenharmony_ci break; 83162306a36Sopenharmony_ci default: 83262306a36Sopenharmony_ci sensors->power.num_sensors = 0; 83362306a36Sopenharmony_ci } 83462306a36Sopenharmony_ci 83562306a36Sopenharmony_ci switch (sensors->caps.version) { 83662306a36Sopenharmony_ci case 1: 83762306a36Sopenharmony_ci num_attrs += (sensors->caps.num_sensors * 7); 83862306a36Sopenharmony_ci break; 83962306a36Sopenharmony_ci case 2: 84062306a36Sopenharmony_ci num_attrs += (sensors->caps.num_sensors * 8); 84162306a36Sopenharmony_ci break; 84262306a36Sopenharmony_ci case 3: 84362306a36Sopenharmony_ci show_caps = occ_show_caps_3; 84462306a36Sopenharmony_ci num_attrs += (sensors->caps.num_sensors * 9); 84562306a36Sopenharmony_ci break; 84662306a36Sopenharmony_ci default: 84762306a36Sopenharmony_ci sensors->caps.num_sensors = 0; 84862306a36Sopenharmony_ci } 84962306a36Sopenharmony_ci 85062306a36Sopenharmony_ci switch (sensors->extended.version) { 85162306a36Sopenharmony_ci case 1: 85262306a36Sopenharmony_ci num_attrs += (sensors->extended.num_sensors * 3); 85362306a36Sopenharmony_ci break; 85462306a36Sopenharmony_ci default: 85562306a36Sopenharmony_ci sensors->extended.num_sensors = 0; 85662306a36Sopenharmony_ci } 85762306a36Sopenharmony_ci 85862306a36Sopenharmony_ci occ->attrs = devm_kzalloc(dev, sizeof(*occ->attrs) * num_attrs, 85962306a36Sopenharmony_ci GFP_KERNEL); 86062306a36Sopenharmony_ci if (!occ->attrs) 86162306a36Sopenharmony_ci return -ENOMEM; 86262306a36Sopenharmony_ci 86362306a36Sopenharmony_ci /* null-terminated list */ 86462306a36Sopenharmony_ci occ->group.attrs = devm_kzalloc(dev, sizeof(*occ->group.attrs) * 86562306a36Sopenharmony_ci num_attrs + 1, GFP_KERNEL); 86662306a36Sopenharmony_ci if (!occ->group.attrs) 86762306a36Sopenharmony_ci return -ENOMEM; 86862306a36Sopenharmony_ci 86962306a36Sopenharmony_ci attr = occ->attrs; 87062306a36Sopenharmony_ci 87162306a36Sopenharmony_ci for (i = 0; i < sensors->temp.num_sensors; ++i) { 87262306a36Sopenharmony_ci s = i + 1; 87362306a36Sopenharmony_ci temp = ((struct temp_sensor_2 *)sensors->temp.data) + i; 87462306a36Sopenharmony_ci 87562306a36Sopenharmony_ci snprintf(attr->name, sizeof(attr->name), "temp%d_label", s); 87662306a36Sopenharmony_ci attr->sensor = OCC_INIT_ATTR(attr->name, 0444, show_temp, NULL, 87762306a36Sopenharmony_ci 0, i); 87862306a36Sopenharmony_ci attr++; 87962306a36Sopenharmony_ci 88062306a36Sopenharmony_ci if (sensors->temp.version == 2 && 88162306a36Sopenharmony_ci temp->fru_type == OCC_FRU_TYPE_VRM) { 88262306a36Sopenharmony_ci snprintf(attr->name, sizeof(attr->name), 88362306a36Sopenharmony_ci "temp%d_alarm", s); 88462306a36Sopenharmony_ci } else { 88562306a36Sopenharmony_ci snprintf(attr->name, sizeof(attr->name), 88662306a36Sopenharmony_ci "temp%d_input", s); 88762306a36Sopenharmony_ci } 88862306a36Sopenharmony_ci 88962306a36Sopenharmony_ci attr->sensor = OCC_INIT_ATTR(attr->name, 0444, show_temp, NULL, 89062306a36Sopenharmony_ci 1, i); 89162306a36Sopenharmony_ci attr++; 89262306a36Sopenharmony_ci 89362306a36Sopenharmony_ci if (sensors->temp.version > 1) { 89462306a36Sopenharmony_ci snprintf(attr->name, sizeof(attr->name), 89562306a36Sopenharmony_ci "temp%d_fru_type", s); 89662306a36Sopenharmony_ci attr->sensor = OCC_INIT_ATTR(attr->name, 0444, 89762306a36Sopenharmony_ci show_temp, NULL, 2, i); 89862306a36Sopenharmony_ci attr++; 89962306a36Sopenharmony_ci 90062306a36Sopenharmony_ci snprintf(attr->name, sizeof(attr->name), 90162306a36Sopenharmony_ci "temp%d_fault", s); 90262306a36Sopenharmony_ci attr->sensor = OCC_INIT_ATTR(attr->name, 0444, 90362306a36Sopenharmony_ci show_temp, NULL, 3, i); 90462306a36Sopenharmony_ci attr++; 90562306a36Sopenharmony_ci 90662306a36Sopenharmony_ci if (sensors->temp.version == 0x10) { 90762306a36Sopenharmony_ci snprintf(attr->name, sizeof(attr->name), 90862306a36Sopenharmony_ci "temp%d_max", s); 90962306a36Sopenharmony_ci attr->sensor = OCC_INIT_ATTR(attr->name, 0444, 91062306a36Sopenharmony_ci show_temp, NULL, 91162306a36Sopenharmony_ci 4, i); 91262306a36Sopenharmony_ci attr++; 91362306a36Sopenharmony_ci } 91462306a36Sopenharmony_ci } 91562306a36Sopenharmony_ci } 91662306a36Sopenharmony_ci 91762306a36Sopenharmony_ci for (i = 0; i < sensors->freq.num_sensors; ++i) { 91862306a36Sopenharmony_ci s = i + 1; 91962306a36Sopenharmony_ci 92062306a36Sopenharmony_ci snprintf(attr->name, sizeof(attr->name), "freq%d_label", s); 92162306a36Sopenharmony_ci attr->sensor = OCC_INIT_ATTR(attr->name, 0444, show_freq, NULL, 92262306a36Sopenharmony_ci 0, i); 92362306a36Sopenharmony_ci attr++; 92462306a36Sopenharmony_ci 92562306a36Sopenharmony_ci snprintf(attr->name, sizeof(attr->name), "freq%d_input", s); 92662306a36Sopenharmony_ci attr->sensor = OCC_INIT_ATTR(attr->name, 0444, show_freq, NULL, 92762306a36Sopenharmony_ci 1, i); 92862306a36Sopenharmony_ci attr++; 92962306a36Sopenharmony_ci } 93062306a36Sopenharmony_ci 93162306a36Sopenharmony_ci if (sensors->power.version == 0xA0) { 93262306a36Sopenharmony_ci /* 93362306a36Sopenharmony_ci * Special case for many-attribute power sensor. Split it into 93462306a36Sopenharmony_ci * a sensor number per power type, emulating several sensors. 93562306a36Sopenharmony_ci */ 93662306a36Sopenharmony_ci for (i = 0; i < sensors->power.num_sensors; ++i) { 93762306a36Sopenharmony_ci unsigned int j; 93862306a36Sopenharmony_ci unsigned int nr = 0; 93962306a36Sopenharmony_ci 94062306a36Sopenharmony_ci s = (i * 4) + 1; 94162306a36Sopenharmony_ci 94262306a36Sopenharmony_ci for (j = 0; j < 4; ++j) { 94362306a36Sopenharmony_ci snprintf(attr->name, sizeof(attr->name), 94462306a36Sopenharmony_ci "power%d_label", s); 94562306a36Sopenharmony_ci attr->sensor = OCC_INIT_ATTR(attr->name, 0444, 94662306a36Sopenharmony_ci show_power, NULL, 94762306a36Sopenharmony_ci nr++, i); 94862306a36Sopenharmony_ci attr++; 94962306a36Sopenharmony_ci 95062306a36Sopenharmony_ci snprintf(attr->name, sizeof(attr->name), 95162306a36Sopenharmony_ci "power%d_average", s); 95262306a36Sopenharmony_ci attr->sensor = OCC_INIT_ATTR(attr->name, 0444, 95362306a36Sopenharmony_ci show_power, NULL, 95462306a36Sopenharmony_ci nr++, i); 95562306a36Sopenharmony_ci attr++; 95662306a36Sopenharmony_ci 95762306a36Sopenharmony_ci snprintf(attr->name, sizeof(attr->name), 95862306a36Sopenharmony_ci "power%d_average_interval", s); 95962306a36Sopenharmony_ci attr->sensor = OCC_INIT_ATTR(attr->name, 0444, 96062306a36Sopenharmony_ci show_power, NULL, 96162306a36Sopenharmony_ci nr++, i); 96262306a36Sopenharmony_ci attr++; 96362306a36Sopenharmony_ci 96462306a36Sopenharmony_ci snprintf(attr->name, sizeof(attr->name), 96562306a36Sopenharmony_ci "power%d_input", s); 96662306a36Sopenharmony_ci attr->sensor = OCC_INIT_ATTR(attr->name, 0444, 96762306a36Sopenharmony_ci show_power, NULL, 96862306a36Sopenharmony_ci nr++, i); 96962306a36Sopenharmony_ci attr++; 97062306a36Sopenharmony_ci 97162306a36Sopenharmony_ci s++; 97262306a36Sopenharmony_ci } 97362306a36Sopenharmony_ci } 97462306a36Sopenharmony_ci 97562306a36Sopenharmony_ci s = (sensors->power.num_sensors * 4) + 1; 97662306a36Sopenharmony_ci } else { 97762306a36Sopenharmony_ci for (i = 0; i < sensors->power.num_sensors; ++i) { 97862306a36Sopenharmony_ci s = i + 1; 97962306a36Sopenharmony_ci 98062306a36Sopenharmony_ci snprintf(attr->name, sizeof(attr->name), 98162306a36Sopenharmony_ci "power%d_label", s); 98262306a36Sopenharmony_ci attr->sensor = OCC_INIT_ATTR(attr->name, 0444, 98362306a36Sopenharmony_ci show_power, NULL, 0, i); 98462306a36Sopenharmony_ci attr++; 98562306a36Sopenharmony_ci 98662306a36Sopenharmony_ci snprintf(attr->name, sizeof(attr->name), 98762306a36Sopenharmony_ci "power%d_average", s); 98862306a36Sopenharmony_ci attr->sensor = OCC_INIT_ATTR(attr->name, 0444, 98962306a36Sopenharmony_ci show_power, NULL, 1, i); 99062306a36Sopenharmony_ci attr++; 99162306a36Sopenharmony_ci 99262306a36Sopenharmony_ci snprintf(attr->name, sizeof(attr->name), 99362306a36Sopenharmony_ci "power%d_average_interval", s); 99462306a36Sopenharmony_ci attr->sensor = OCC_INIT_ATTR(attr->name, 0444, 99562306a36Sopenharmony_ci show_power, NULL, 2, i); 99662306a36Sopenharmony_ci attr++; 99762306a36Sopenharmony_ci 99862306a36Sopenharmony_ci snprintf(attr->name, sizeof(attr->name), 99962306a36Sopenharmony_ci "power%d_input", s); 100062306a36Sopenharmony_ci attr->sensor = OCC_INIT_ATTR(attr->name, 0444, 100162306a36Sopenharmony_ci show_power, NULL, 3, i); 100262306a36Sopenharmony_ci attr++; 100362306a36Sopenharmony_ci } 100462306a36Sopenharmony_ci 100562306a36Sopenharmony_ci s = sensors->power.num_sensors + 1; 100662306a36Sopenharmony_ci } 100762306a36Sopenharmony_ci 100862306a36Sopenharmony_ci if (sensors->caps.num_sensors >= 1) { 100962306a36Sopenharmony_ci snprintf(attr->name, sizeof(attr->name), "power%d_label", s); 101062306a36Sopenharmony_ci attr->sensor = OCC_INIT_ATTR(attr->name, 0444, show_caps, NULL, 101162306a36Sopenharmony_ci 0, 0); 101262306a36Sopenharmony_ci attr++; 101362306a36Sopenharmony_ci 101462306a36Sopenharmony_ci snprintf(attr->name, sizeof(attr->name), "power%d_cap", s); 101562306a36Sopenharmony_ci attr->sensor = OCC_INIT_ATTR(attr->name, 0444, show_caps, NULL, 101662306a36Sopenharmony_ci 1, 0); 101762306a36Sopenharmony_ci attr++; 101862306a36Sopenharmony_ci 101962306a36Sopenharmony_ci snprintf(attr->name, sizeof(attr->name), "power%d_input", s); 102062306a36Sopenharmony_ci attr->sensor = OCC_INIT_ATTR(attr->name, 0444, show_caps, NULL, 102162306a36Sopenharmony_ci 2, 0); 102262306a36Sopenharmony_ci attr++; 102362306a36Sopenharmony_ci 102462306a36Sopenharmony_ci snprintf(attr->name, sizeof(attr->name), 102562306a36Sopenharmony_ci "power%d_cap_not_redundant", s); 102662306a36Sopenharmony_ci attr->sensor = OCC_INIT_ATTR(attr->name, 0444, show_caps, NULL, 102762306a36Sopenharmony_ci 3, 0); 102862306a36Sopenharmony_ci attr++; 102962306a36Sopenharmony_ci 103062306a36Sopenharmony_ci snprintf(attr->name, sizeof(attr->name), "power%d_cap_max", s); 103162306a36Sopenharmony_ci attr->sensor = OCC_INIT_ATTR(attr->name, 0444, show_caps, NULL, 103262306a36Sopenharmony_ci 4, 0); 103362306a36Sopenharmony_ci attr++; 103462306a36Sopenharmony_ci 103562306a36Sopenharmony_ci snprintf(attr->name, sizeof(attr->name), "power%d_cap_min", s); 103662306a36Sopenharmony_ci attr->sensor = OCC_INIT_ATTR(attr->name, 0444, show_caps, NULL, 103762306a36Sopenharmony_ci 5, 0); 103862306a36Sopenharmony_ci attr++; 103962306a36Sopenharmony_ci 104062306a36Sopenharmony_ci snprintf(attr->name, sizeof(attr->name), "power%d_cap_user", 104162306a36Sopenharmony_ci s); 104262306a36Sopenharmony_ci attr->sensor = OCC_INIT_ATTR(attr->name, 0644, show_caps, 104362306a36Sopenharmony_ci occ_store_caps_user, 6, 0); 104462306a36Sopenharmony_ci attr++; 104562306a36Sopenharmony_ci 104662306a36Sopenharmony_ci if (sensors->caps.version > 1) { 104762306a36Sopenharmony_ci snprintf(attr->name, sizeof(attr->name), 104862306a36Sopenharmony_ci "power%d_cap_user_source", s); 104962306a36Sopenharmony_ci attr->sensor = OCC_INIT_ATTR(attr->name, 0444, 105062306a36Sopenharmony_ci show_caps, NULL, 7, 0); 105162306a36Sopenharmony_ci attr++; 105262306a36Sopenharmony_ci 105362306a36Sopenharmony_ci if (sensors->caps.version > 2) { 105462306a36Sopenharmony_ci snprintf(attr->name, sizeof(attr->name), 105562306a36Sopenharmony_ci "power%d_cap_min_soft", s); 105662306a36Sopenharmony_ci attr->sensor = OCC_INIT_ATTR(attr->name, 0444, 105762306a36Sopenharmony_ci show_caps, NULL, 105862306a36Sopenharmony_ci 8, 0); 105962306a36Sopenharmony_ci attr++; 106062306a36Sopenharmony_ci } 106162306a36Sopenharmony_ci } 106262306a36Sopenharmony_ci } 106362306a36Sopenharmony_ci 106462306a36Sopenharmony_ci for (i = 0; i < sensors->extended.num_sensors; ++i) { 106562306a36Sopenharmony_ci s = i + 1; 106662306a36Sopenharmony_ci 106762306a36Sopenharmony_ci snprintf(attr->name, sizeof(attr->name), "extn%d_label", s); 106862306a36Sopenharmony_ci attr->sensor = OCC_INIT_ATTR(attr->name, 0444, 106962306a36Sopenharmony_ci occ_show_extended, NULL, 0, i); 107062306a36Sopenharmony_ci attr++; 107162306a36Sopenharmony_ci 107262306a36Sopenharmony_ci snprintf(attr->name, sizeof(attr->name), "extn%d_flags", s); 107362306a36Sopenharmony_ci attr->sensor = OCC_INIT_ATTR(attr->name, 0444, 107462306a36Sopenharmony_ci occ_show_extended, NULL, 1, i); 107562306a36Sopenharmony_ci attr++; 107662306a36Sopenharmony_ci 107762306a36Sopenharmony_ci snprintf(attr->name, sizeof(attr->name), "extn%d_input", s); 107862306a36Sopenharmony_ci attr->sensor = OCC_INIT_ATTR(attr->name, 0444, 107962306a36Sopenharmony_ci occ_show_extended, NULL, 2, i); 108062306a36Sopenharmony_ci attr++; 108162306a36Sopenharmony_ci } 108262306a36Sopenharmony_ci 108362306a36Sopenharmony_ci /* put the sensors in the group */ 108462306a36Sopenharmony_ci for (i = 0; i < num_attrs; ++i) { 108562306a36Sopenharmony_ci sysfs_attr_init(&occ->attrs[i].sensor.dev_attr.attr); 108662306a36Sopenharmony_ci occ->group.attrs[i] = &occ->attrs[i].sensor.dev_attr.attr; 108762306a36Sopenharmony_ci } 108862306a36Sopenharmony_ci 108962306a36Sopenharmony_ci return 0; 109062306a36Sopenharmony_ci} 109162306a36Sopenharmony_ci 109262306a36Sopenharmony_ci/* only need to do this once at startup, as OCC won't change sensors on us */ 109362306a36Sopenharmony_cistatic void occ_parse_poll_response(struct occ *occ) 109462306a36Sopenharmony_ci{ 109562306a36Sopenharmony_ci unsigned int i, old_offset, offset = 0, size = 0; 109662306a36Sopenharmony_ci struct occ_sensor *sensor; 109762306a36Sopenharmony_ci struct occ_sensors *sensors = &occ->sensors; 109862306a36Sopenharmony_ci struct occ_response *resp = &occ->resp; 109962306a36Sopenharmony_ci struct occ_poll_response *poll = 110062306a36Sopenharmony_ci (struct occ_poll_response *)&resp->data[0]; 110162306a36Sopenharmony_ci struct occ_poll_response_header *header = &poll->header; 110262306a36Sopenharmony_ci struct occ_sensor_data_block *block = &poll->block; 110362306a36Sopenharmony_ci 110462306a36Sopenharmony_ci dev_info(occ->bus_dev, "OCC found, code level: %.16s\n", 110562306a36Sopenharmony_ci header->occ_code_level); 110662306a36Sopenharmony_ci 110762306a36Sopenharmony_ci for (i = 0; i < header->num_sensor_data_blocks; ++i) { 110862306a36Sopenharmony_ci block = (struct occ_sensor_data_block *)((u8 *)block + offset); 110962306a36Sopenharmony_ci old_offset = offset; 111062306a36Sopenharmony_ci offset = (block->header.num_sensors * 111162306a36Sopenharmony_ci block->header.sensor_length) + sizeof(block->header); 111262306a36Sopenharmony_ci size += offset; 111362306a36Sopenharmony_ci 111462306a36Sopenharmony_ci /* validate all the length/size fields */ 111562306a36Sopenharmony_ci if ((size + sizeof(*header)) >= OCC_RESP_DATA_BYTES) { 111662306a36Sopenharmony_ci dev_warn(occ->bus_dev, "exceeded response buffer\n"); 111762306a36Sopenharmony_ci return; 111862306a36Sopenharmony_ci } 111962306a36Sopenharmony_ci 112062306a36Sopenharmony_ci dev_dbg(occ->bus_dev, " %04x..%04x: %.4s (%d sensors)\n", 112162306a36Sopenharmony_ci old_offset, offset - 1, block->header.eye_catcher, 112262306a36Sopenharmony_ci block->header.num_sensors); 112362306a36Sopenharmony_ci 112462306a36Sopenharmony_ci /* match sensor block type */ 112562306a36Sopenharmony_ci if (strncmp(block->header.eye_catcher, "TEMP", 4) == 0) 112662306a36Sopenharmony_ci sensor = &sensors->temp; 112762306a36Sopenharmony_ci else if (strncmp(block->header.eye_catcher, "FREQ", 4) == 0) 112862306a36Sopenharmony_ci sensor = &sensors->freq; 112962306a36Sopenharmony_ci else if (strncmp(block->header.eye_catcher, "POWR", 4) == 0) 113062306a36Sopenharmony_ci sensor = &sensors->power; 113162306a36Sopenharmony_ci else if (strncmp(block->header.eye_catcher, "CAPS", 4) == 0) 113262306a36Sopenharmony_ci sensor = &sensors->caps; 113362306a36Sopenharmony_ci else if (strncmp(block->header.eye_catcher, "EXTN", 4) == 0) 113462306a36Sopenharmony_ci sensor = &sensors->extended; 113562306a36Sopenharmony_ci else { 113662306a36Sopenharmony_ci dev_warn(occ->bus_dev, "sensor not supported %.4s\n", 113762306a36Sopenharmony_ci block->header.eye_catcher); 113862306a36Sopenharmony_ci continue; 113962306a36Sopenharmony_ci } 114062306a36Sopenharmony_ci 114162306a36Sopenharmony_ci sensor->num_sensors = block->header.num_sensors; 114262306a36Sopenharmony_ci sensor->version = block->header.sensor_format; 114362306a36Sopenharmony_ci sensor->data = &block->data; 114462306a36Sopenharmony_ci } 114562306a36Sopenharmony_ci 114662306a36Sopenharmony_ci dev_dbg(occ->bus_dev, "Max resp size: %u+%zd=%zd\n", size, 114762306a36Sopenharmony_ci sizeof(*header), size + sizeof(*header)); 114862306a36Sopenharmony_ci} 114962306a36Sopenharmony_ci 115062306a36Sopenharmony_ciint occ_active(struct occ *occ, bool active) 115162306a36Sopenharmony_ci{ 115262306a36Sopenharmony_ci int rc = mutex_lock_interruptible(&occ->lock); 115362306a36Sopenharmony_ci 115462306a36Sopenharmony_ci if (rc) 115562306a36Sopenharmony_ci return rc; 115662306a36Sopenharmony_ci 115762306a36Sopenharmony_ci if (active) { 115862306a36Sopenharmony_ci if (occ->active) { 115962306a36Sopenharmony_ci rc = -EALREADY; 116062306a36Sopenharmony_ci goto unlock; 116162306a36Sopenharmony_ci } 116262306a36Sopenharmony_ci 116362306a36Sopenharmony_ci occ->error_count = 0; 116462306a36Sopenharmony_ci occ->last_safe = 0; 116562306a36Sopenharmony_ci 116662306a36Sopenharmony_ci rc = occ_poll(occ); 116762306a36Sopenharmony_ci if (rc < 0) { 116862306a36Sopenharmony_ci dev_err(occ->bus_dev, 116962306a36Sopenharmony_ci "failed to get OCC poll response=%02x: %d\n", 117062306a36Sopenharmony_ci occ->resp.return_status, rc); 117162306a36Sopenharmony_ci goto unlock; 117262306a36Sopenharmony_ci } 117362306a36Sopenharmony_ci 117462306a36Sopenharmony_ci occ->active = true; 117562306a36Sopenharmony_ci occ->next_update = jiffies + OCC_UPDATE_FREQUENCY; 117662306a36Sopenharmony_ci occ_parse_poll_response(occ); 117762306a36Sopenharmony_ci 117862306a36Sopenharmony_ci rc = occ_setup_sensor_attrs(occ); 117962306a36Sopenharmony_ci if (rc) { 118062306a36Sopenharmony_ci dev_err(occ->bus_dev, 118162306a36Sopenharmony_ci "failed to setup sensor attrs: %d\n", rc); 118262306a36Sopenharmony_ci goto unlock; 118362306a36Sopenharmony_ci } 118462306a36Sopenharmony_ci 118562306a36Sopenharmony_ci occ->hwmon = hwmon_device_register_with_groups(occ->bus_dev, 118662306a36Sopenharmony_ci "occ", occ, 118762306a36Sopenharmony_ci occ->groups); 118862306a36Sopenharmony_ci if (IS_ERR(occ->hwmon)) { 118962306a36Sopenharmony_ci rc = PTR_ERR(occ->hwmon); 119062306a36Sopenharmony_ci occ->hwmon = NULL; 119162306a36Sopenharmony_ci dev_err(occ->bus_dev, 119262306a36Sopenharmony_ci "failed to register hwmon device: %d\n", rc); 119362306a36Sopenharmony_ci goto unlock; 119462306a36Sopenharmony_ci } 119562306a36Sopenharmony_ci } else { 119662306a36Sopenharmony_ci if (!occ->active) { 119762306a36Sopenharmony_ci rc = -EALREADY; 119862306a36Sopenharmony_ci goto unlock; 119962306a36Sopenharmony_ci } 120062306a36Sopenharmony_ci 120162306a36Sopenharmony_ci if (occ->hwmon) 120262306a36Sopenharmony_ci hwmon_device_unregister(occ->hwmon); 120362306a36Sopenharmony_ci occ->active = false; 120462306a36Sopenharmony_ci occ->hwmon = NULL; 120562306a36Sopenharmony_ci } 120662306a36Sopenharmony_ci 120762306a36Sopenharmony_ciunlock: 120862306a36Sopenharmony_ci mutex_unlock(&occ->lock); 120962306a36Sopenharmony_ci return rc; 121062306a36Sopenharmony_ci} 121162306a36Sopenharmony_ci 121262306a36Sopenharmony_ciint occ_setup(struct occ *occ) 121362306a36Sopenharmony_ci{ 121462306a36Sopenharmony_ci int rc; 121562306a36Sopenharmony_ci 121662306a36Sopenharmony_ci mutex_init(&occ->lock); 121762306a36Sopenharmony_ci occ->groups[0] = &occ->group; 121862306a36Sopenharmony_ci 121962306a36Sopenharmony_ci rc = occ_setup_sysfs(occ); 122062306a36Sopenharmony_ci if (rc) { 122162306a36Sopenharmony_ci dev_err(occ->bus_dev, "failed to setup sysfs: %d\n", rc); 122262306a36Sopenharmony_ci return rc; 122362306a36Sopenharmony_ci } 122462306a36Sopenharmony_ci 122562306a36Sopenharmony_ci if (!device_property_read_bool(occ->bus_dev, "ibm,no-poll-on-init")) { 122662306a36Sopenharmony_ci rc = occ_active(occ, true); 122762306a36Sopenharmony_ci if (rc) 122862306a36Sopenharmony_ci occ_shutdown_sysfs(occ); 122962306a36Sopenharmony_ci } 123062306a36Sopenharmony_ci 123162306a36Sopenharmony_ci return rc; 123262306a36Sopenharmony_ci} 123362306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(occ_setup); 123462306a36Sopenharmony_ci 123562306a36Sopenharmony_civoid occ_shutdown(struct occ *occ) 123662306a36Sopenharmony_ci{ 123762306a36Sopenharmony_ci mutex_lock(&occ->lock); 123862306a36Sopenharmony_ci 123962306a36Sopenharmony_ci occ_shutdown_sysfs(occ); 124062306a36Sopenharmony_ci 124162306a36Sopenharmony_ci if (occ->hwmon) 124262306a36Sopenharmony_ci hwmon_device_unregister(occ->hwmon); 124362306a36Sopenharmony_ci occ->hwmon = NULL; 124462306a36Sopenharmony_ci 124562306a36Sopenharmony_ci mutex_unlock(&occ->lock); 124662306a36Sopenharmony_ci} 124762306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(occ_shutdown); 124862306a36Sopenharmony_ci 124962306a36Sopenharmony_ciMODULE_AUTHOR("Eddie James <eajames@linux.ibm.com>"); 125062306a36Sopenharmony_ciMODULE_DESCRIPTION("Common OCC hwmon code"); 125162306a36Sopenharmony_ciMODULE_LICENSE("GPL"); 1252