brintos

brintos / linux-shallow public Read only

0
0
Text · 7.2 KiB · 2317301 Raw
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