258 lines · c
1// SPDX-License-Identifier: GPL-2.0+2// Copyright IBM Corp 20193 4#include <linux/bitops.h>5#include <linux/device.h>6#include <linux/export.h>7#include <linux/hwmon-sysfs.h>8#include <linux/kernel.h>9#include <linux/kstrtox.h>10#include <linux/sysfs.h>11 12#include "common.h"13 14/* OCC status register */15#define OCC_STAT_MASTER BIT(7)16 17/* OCC extended status register */18#define OCC_EXT_STAT_DVFS_OT BIT(7)19#define OCC_EXT_STAT_DVFS_POWER BIT(6)20#define OCC_EXT_STAT_MEM_THROTTLE BIT(5)21#define OCC_EXT_STAT_QUICK_DROP BIT(4)22#define OCC_EXT_STAT_DVFS_VDD BIT(3)23#define OCC_EXT_STAT_GPU_THROTTLE GENMASK(2, 0)24 25static ssize_t occ_active_store(struct device *dev,26 struct device_attribute *attr,27 const char *buf, size_t count)28{29 int rc;30 bool active;31 struct occ *occ = dev_get_drvdata(dev);32 33 rc = kstrtobool(buf, &active);34 if (rc)35 return rc;36 37 rc = occ_active(occ, active);38 if (rc)39 return rc;40 41 return count;42}43 44static ssize_t occ_sysfs_show(struct device *dev,45 struct device_attribute *attr, char *buf)46{47 int rc;48 int val = 0;49 struct occ *occ = dev_get_drvdata(dev);50 struct occ_poll_response_header *header;51 struct sensor_device_attribute *sattr = to_sensor_dev_attr(attr);52 53 if (occ->active) {54 rc = occ_update_response(occ);55 if (rc)56 return rc;57 58 header = (struct occ_poll_response_header *)occ->resp.data;59 60 switch (sattr->index) {61 case 0:62 val = !!(header->status & OCC_STAT_MASTER);63 break;64 case 1:65 val = 1;66 break;67 case 2:68 val = !!(header->ext_status & OCC_EXT_STAT_DVFS_OT);69 break;70 case 3:71 val = !!(header->ext_status & OCC_EXT_STAT_DVFS_POWER);72 break;73 case 4:74 val = !!(header->ext_status &75 OCC_EXT_STAT_MEM_THROTTLE);76 break;77 case 5:78 val = !!(header->ext_status & OCC_EXT_STAT_QUICK_DROP);79 break;80 case 6:81 val = header->occ_state;82 break;83 case 7:84 if (header->status & OCC_STAT_MASTER)85 val = hweight8(header->occs_present);86 else87 val = 1;88 break;89 case 8:90 val = header->ips_status;91 break;92 case 9:93 val = header->mode;94 break;95 case 10:96 val = !!(header->ext_status & OCC_EXT_STAT_DVFS_VDD);97 break;98 case 11:99 val = header->ext_status & OCC_EXT_STAT_GPU_THROTTLE;100 break;101 default:102 return -EINVAL;103 }104 } else {105 if (sattr->index == 1)106 val = 0;107 else if (sattr->index <= 11)108 val = -ENODATA;109 else110 return -EINVAL;111 }112 113 return sysfs_emit(buf, "%d\n", val);114}115 116static ssize_t occ_error_show(struct device *dev,117 struct device_attribute *attr, char *buf)118{119 struct occ *occ = dev_get_drvdata(dev);120 121 occ_update_response(occ);122 123 return sysfs_emit(buf, "%d\n", occ->error);124}125 126static SENSOR_DEVICE_ATTR(occ_master, 0444, occ_sysfs_show, NULL, 0);127static SENSOR_DEVICE_ATTR(occ_active, 0644, occ_sysfs_show, occ_active_store,128 1);129static SENSOR_DEVICE_ATTR(occ_dvfs_overtemp, 0444, occ_sysfs_show, NULL, 2);130static SENSOR_DEVICE_ATTR(occ_dvfs_power, 0444, occ_sysfs_show, NULL, 3);131static SENSOR_DEVICE_ATTR(occ_mem_throttle, 0444, occ_sysfs_show, NULL, 4);132static SENSOR_DEVICE_ATTR(occ_quick_pwr_drop, 0444, occ_sysfs_show, NULL, 5);133static SENSOR_DEVICE_ATTR(occ_state, 0444, occ_sysfs_show, NULL, 6);134static SENSOR_DEVICE_ATTR(occs_present, 0444, occ_sysfs_show, NULL, 7);135static SENSOR_DEVICE_ATTR(occ_ips_status, 0444, occ_sysfs_show, NULL, 8);136static SENSOR_DEVICE_ATTR(occ_mode, 0444, occ_sysfs_show, NULL, 9);137static SENSOR_DEVICE_ATTR(occ_dvfs_vdd, 0444, occ_sysfs_show, NULL, 10);138static SENSOR_DEVICE_ATTR(occ_gpu_throttle, 0444, occ_sysfs_show, NULL, 11);139static DEVICE_ATTR_RO(occ_error);140 141static struct attribute *occ_attributes[] = {142 &sensor_dev_attr_occ_master.dev_attr.attr,143 &sensor_dev_attr_occ_active.dev_attr.attr,144 &sensor_dev_attr_occ_dvfs_overtemp.dev_attr.attr,145 &sensor_dev_attr_occ_dvfs_power.dev_attr.attr,146 &sensor_dev_attr_occ_mem_throttle.dev_attr.attr,147 &sensor_dev_attr_occ_quick_pwr_drop.dev_attr.attr,148 &sensor_dev_attr_occ_state.dev_attr.attr,149 &sensor_dev_attr_occs_present.dev_attr.attr,150 &sensor_dev_attr_occ_ips_status.dev_attr.attr,151 &sensor_dev_attr_occ_mode.dev_attr.attr,152 &sensor_dev_attr_occ_dvfs_vdd.dev_attr.attr,153 &sensor_dev_attr_occ_gpu_throttle.dev_attr.attr,154 &dev_attr_occ_error.attr,155 NULL156};157 158static const struct attribute_group occ_sysfs = {159 .attrs = occ_attributes,160};161 162void occ_sysfs_poll_done(struct occ *occ)163{164 const char *name;165 struct occ_poll_response_header *header =166 (struct occ_poll_response_header *)occ->resp.data;167 168 /*169 * On the first poll response, we haven't yet created the sysfs170 * attributes, so don't make any notify calls.171 */172 if (!occ->active)173 goto done;174 175 if ((header->status & OCC_STAT_MASTER) !=176 (occ->prev_stat & OCC_STAT_MASTER)) {177 name = sensor_dev_attr_occ_master.dev_attr.attr.name;178 sysfs_notify(&occ->bus_dev->kobj, NULL, name);179 }180 181 if ((header->ext_status & OCC_EXT_STAT_DVFS_OT) !=182 (occ->prev_ext_stat & OCC_EXT_STAT_DVFS_OT)) {183 name = sensor_dev_attr_occ_dvfs_overtemp.dev_attr.attr.name;184 sysfs_notify(&occ->bus_dev->kobj, NULL, name);185 }186 187 if ((header->ext_status & OCC_EXT_STAT_DVFS_POWER) !=188 (occ->prev_ext_stat & OCC_EXT_STAT_DVFS_POWER)) {189 name = sensor_dev_attr_occ_dvfs_power.dev_attr.attr.name;190 sysfs_notify(&occ->bus_dev->kobj, NULL, name);191 }192 193 if ((header->ext_status & OCC_EXT_STAT_MEM_THROTTLE) !=194 (occ->prev_ext_stat & OCC_EXT_STAT_MEM_THROTTLE)) {195 name = sensor_dev_attr_occ_mem_throttle.dev_attr.attr.name;196 sysfs_notify(&occ->bus_dev->kobj, NULL, name);197 }198 199 if ((header->ext_status & OCC_EXT_STAT_QUICK_DROP) !=200 (occ->prev_ext_stat & OCC_EXT_STAT_QUICK_DROP)) {201 name = sensor_dev_attr_occ_quick_pwr_drop.dev_attr.attr.name;202 sysfs_notify(&occ->bus_dev->kobj, NULL, name);203 }204 205 if ((header->ext_status & OCC_EXT_STAT_DVFS_VDD) !=206 (occ->prev_ext_stat & OCC_EXT_STAT_DVFS_VDD)) {207 name = sensor_dev_attr_occ_dvfs_vdd.dev_attr.attr.name;208 sysfs_notify(&occ->bus_dev->kobj, NULL, name);209 }210 211 if ((header->ext_status & OCC_EXT_STAT_GPU_THROTTLE) !=212 (occ->prev_ext_stat & OCC_EXT_STAT_GPU_THROTTLE)) {213 name = sensor_dev_attr_occ_gpu_throttle.dev_attr.attr.name;214 sysfs_notify(&occ->bus_dev->kobj, NULL, name);215 }216 217 if ((header->status & OCC_STAT_MASTER) &&218 header->occs_present != occ->prev_occs_present) {219 name = sensor_dev_attr_occs_present.dev_attr.attr.name;220 sysfs_notify(&occ->bus_dev->kobj, NULL, name);221 }222 223 if (header->ips_status != occ->prev_ips_status) {224 name = sensor_dev_attr_occ_ips_status.dev_attr.attr.name;225 sysfs_notify(&occ->bus_dev->kobj, NULL, name);226 }227 228 if (header->mode != occ->prev_mode) {229 name = sensor_dev_attr_occ_mode.dev_attr.attr.name;230 sysfs_notify(&occ->bus_dev->kobj, NULL, name);231 }232 233 if (occ->error && occ->error != occ->prev_error) {234 name = dev_attr_occ_error.attr.name;235 sysfs_notify(&occ->bus_dev->kobj, NULL, name);236 }237 238 /* no notifications for OCC state; doesn't indicate error condition */239 240done:241 occ->prev_error = occ->error;242 occ->prev_stat = header->status;243 occ->prev_ext_stat = header->ext_status;244 occ->prev_occs_present = header->occs_present;245 occ->prev_ips_status = header->ips_status;246 occ->prev_mode = header->mode;247}248 249int occ_setup_sysfs(struct occ *occ)250{251 return sysfs_create_group(&occ->bus_dev->kobj, &occ_sysfs);252}253 254void occ_shutdown_sysfs(struct occ *occ)255{256 sysfs_remove_group(&occ->bus_dev->kobj, &occ_sysfs);257}258