xref: /kernel/linux/linux-6.6/drivers/hwmon/occ/sysfs.c (revision 62306a36)
162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0+
262306a36Sopenharmony_ci// Copyright IBM Corp 2019
362306a36Sopenharmony_ci
462306a36Sopenharmony_ci#include <linux/bitops.h>
562306a36Sopenharmony_ci#include <linux/device.h>
662306a36Sopenharmony_ci#include <linux/export.h>
762306a36Sopenharmony_ci#include <linux/hwmon-sysfs.h>
862306a36Sopenharmony_ci#include <linux/kernel.h>
962306a36Sopenharmony_ci#include <linux/kstrtox.h>
1062306a36Sopenharmony_ci#include <linux/sysfs.h>
1162306a36Sopenharmony_ci
1262306a36Sopenharmony_ci#include "common.h"
1362306a36Sopenharmony_ci
1462306a36Sopenharmony_ci/* OCC status register */
1562306a36Sopenharmony_ci#define OCC_STAT_MASTER			BIT(7)
1662306a36Sopenharmony_ci
1762306a36Sopenharmony_ci/* OCC extended status register */
1862306a36Sopenharmony_ci#define OCC_EXT_STAT_DVFS_OT		BIT(7)
1962306a36Sopenharmony_ci#define OCC_EXT_STAT_DVFS_POWER		BIT(6)
2062306a36Sopenharmony_ci#define OCC_EXT_STAT_MEM_THROTTLE	BIT(5)
2162306a36Sopenharmony_ci#define OCC_EXT_STAT_QUICK_DROP		BIT(4)
2262306a36Sopenharmony_ci#define OCC_EXT_STAT_DVFS_VDD		BIT(3)
2362306a36Sopenharmony_ci#define OCC_EXT_STAT_GPU_THROTTLE	GENMASK(2, 0)
2462306a36Sopenharmony_ci
2562306a36Sopenharmony_cistatic ssize_t occ_active_store(struct device *dev,
2662306a36Sopenharmony_ci				struct device_attribute *attr,
2762306a36Sopenharmony_ci				const char *buf, size_t count)
2862306a36Sopenharmony_ci{
2962306a36Sopenharmony_ci	int rc;
3062306a36Sopenharmony_ci	bool active;
3162306a36Sopenharmony_ci	struct occ *occ = dev_get_drvdata(dev);
3262306a36Sopenharmony_ci
3362306a36Sopenharmony_ci	rc = kstrtobool(buf, &active);
3462306a36Sopenharmony_ci	if (rc)
3562306a36Sopenharmony_ci		return rc;
3662306a36Sopenharmony_ci
3762306a36Sopenharmony_ci	rc = occ_active(occ, active);
3862306a36Sopenharmony_ci	if (rc)
3962306a36Sopenharmony_ci		return rc;
4062306a36Sopenharmony_ci
4162306a36Sopenharmony_ci	return count;
4262306a36Sopenharmony_ci}
4362306a36Sopenharmony_ci
4462306a36Sopenharmony_cistatic ssize_t occ_sysfs_show(struct device *dev,
4562306a36Sopenharmony_ci			      struct device_attribute *attr, char *buf)
4662306a36Sopenharmony_ci{
4762306a36Sopenharmony_ci	int rc;
4862306a36Sopenharmony_ci	int val = 0;
4962306a36Sopenharmony_ci	struct occ *occ = dev_get_drvdata(dev);
5062306a36Sopenharmony_ci	struct occ_poll_response_header *header;
5162306a36Sopenharmony_ci	struct sensor_device_attribute *sattr = to_sensor_dev_attr(attr);
5262306a36Sopenharmony_ci
5362306a36Sopenharmony_ci	if (occ->active) {
5462306a36Sopenharmony_ci		rc = occ_update_response(occ);
5562306a36Sopenharmony_ci		if (rc)
5662306a36Sopenharmony_ci			return rc;
5762306a36Sopenharmony_ci
5862306a36Sopenharmony_ci		header = (struct occ_poll_response_header *)occ->resp.data;
5962306a36Sopenharmony_ci
6062306a36Sopenharmony_ci		switch (sattr->index) {
6162306a36Sopenharmony_ci		case 0:
6262306a36Sopenharmony_ci			val = !!(header->status & OCC_STAT_MASTER);
6362306a36Sopenharmony_ci			break;
6462306a36Sopenharmony_ci		case 1:
6562306a36Sopenharmony_ci			val = 1;
6662306a36Sopenharmony_ci			break;
6762306a36Sopenharmony_ci		case 2:
6862306a36Sopenharmony_ci			val = !!(header->ext_status & OCC_EXT_STAT_DVFS_OT);
6962306a36Sopenharmony_ci			break;
7062306a36Sopenharmony_ci		case 3:
7162306a36Sopenharmony_ci			val = !!(header->ext_status & OCC_EXT_STAT_DVFS_POWER);
7262306a36Sopenharmony_ci			break;
7362306a36Sopenharmony_ci		case 4:
7462306a36Sopenharmony_ci			val = !!(header->ext_status &
7562306a36Sopenharmony_ci				 OCC_EXT_STAT_MEM_THROTTLE);
7662306a36Sopenharmony_ci			break;
7762306a36Sopenharmony_ci		case 5:
7862306a36Sopenharmony_ci			val = !!(header->ext_status & OCC_EXT_STAT_QUICK_DROP);
7962306a36Sopenharmony_ci			break;
8062306a36Sopenharmony_ci		case 6:
8162306a36Sopenharmony_ci			val = header->occ_state;
8262306a36Sopenharmony_ci			break;
8362306a36Sopenharmony_ci		case 7:
8462306a36Sopenharmony_ci			if (header->status & OCC_STAT_MASTER)
8562306a36Sopenharmony_ci				val = hweight8(header->occs_present);
8662306a36Sopenharmony_ci			else
8762306a36Sopenharmony_ci				val = 1;
8862306a36Sopenharmony_ci			break;
8962306a36Sopenharmony_ci		case 8:
9062306a36Sopenharmony_ci			val = header->ips_status;
9162306a36Sopenharmony_ci			break;
9262306a36Sopenharmony_ci		case 9:
9362306a36Sopenharmony_ci			val = header->mode;
9462306a36Sopenharmony_ci			break;
9562306a36Sopenharmony_ci		case 10:
9662306a36Sopenharmony_ci			val = !!(header->ext_status & OCC_EXT_STAT_DVFS_VDD);
9762306a36Sopenharmony_ci			break;
9862306a36Sopenharmony_ci		case 11:
9962306a36Sopenharmony_ci			val = header->ext_status & OCC_EXT_STAT_GPU_THROTTLE;
10062306a36Sopenharmony_ci			break;
10162306a36Sopenharmony_ci		default:
10262306a36Sopenharmony_ci			return -EINVAL;
10362306a36Sopenharmony_ci		}
10462306a36Sopenharmony_ci	} else {
10562306a36Sopenharmony_ci		if (sattr->index == 1)
10662306a36Sopenharmony_ci			val = 0;
10762306a36Sopenharmony_ci		else if (sattr->index <= 11)
10862306a36Sopenharmony_ci			val = -ENODATA;
10962306a36Sopenharmony_ci		else
11062306a36Sopenharmony_ci			return -EINVAL;
11162306a36Sopenharmony_ci	}
11262306a36Sopenharmony_ci
11362306a36Sopenharmony_ci	return sysfs_emit(buf, "%d\n", val);
11462306a36Sopenharmony_ci}
11562306a36Sopenharmony_ci
11662306a36Sopenharmony_cistatic ssize_t occ_error_show(struct device *dev,
11762306a36Sopenharmony_ci			      struct device_attribute *attr, char *buf)
11862306a36Sopenharmony_ci{
11962306a36Sopenharmony_ci	struct occ *occ = dev_get_drvdata(dev);
12062306a36Sopenharmony_ci
12162306a36Sopenharmony_ci	occ_update_response(occ);
12262306a36Sopenharmony_ci
12362306a36Sopenharmony_ci	return sysfs_emit(buf, "%d\n", occ->error);
12462306a36Sopenharmony_ci}
12562306a36Sopenharmony_ci
12662306a36Sopenharmony_cistatic SENSOR_DEVICE_ATTR(occ_master, 0444, occ_sysfs_show, NULL, 0);
12762306a36Sopenharmony_cistatic SENSOR_DEVICE_ATTR(occ_active, 0644, occ_sysfs_show, occ_active_store,
12862306a36Sopenharmony_ci			  1);
12962306a36Sopenharmony_cistatic SENSOR_DEVICE_ATTR(occ_dvfs_overtemp, 0444, occ_sysfs_show, NULL, 2);
13062306a36Sopenharmony_cistatic SENSOR_DEVICE_ATTR(occ_dvfs_power, 0444, occ_sysfs_show, NULL, 3);
13162306a36Sopenharmony_cistatic SENSOR_DEVICE_ATTR(occ_mem_throttle, 0444, occ_sysfs_show, NULL, 4);
13262306a36Sopenharmony_cistatic SENSOR_DEVICE_ATTR(occ_quick_pwr_drop, 0444, occ_sysfs_show, NULL, 5);
13362306a36Sopenharmony_cistatic SENSOR_DEVICE_ATTR(occ_state, 0444, occ_sysfs_show, NULL, 6);
13462306a36Sopenharmony_cistatic SENSOR_DEVICE_ATTR(occs_present, 0444, occ_sysfs_show, NULL, 7);
13562306a36Sopenharmony_cistatic SENSOR_DEVICE_ATTR(occ_ips_status, 0444, occ_sysfs_show, NULL, 8);
13662306a36Sopenharmony_cistatic SENSOR_DEVICE_ATTR(occ_mode, 0444, occ_sysfs_show, NULL, 9);
13762306a36Sopenharmony_cistatic SENSOR_DEVICE_ATTR(occ_dvfs_vdd, 0444, occ_sysfs_show, NULL, 10);
13862306a36Sopenharmony_cistatic SENSOR_DEVICE_ATTR(occ_gpu_throttle, 0444, occ_sysfs_show, NULL, 11);
13962306a36Sopenharmony_cistatic DEVICE_ATTR_RO(occ_error);
14062306a36Sopenharmony_ci
14162306a36Sopenharmony_cistatic struct attribute *occ_attributes[] = {
14262306a36Sopenharmony_ci	&sensor_dev_attr_occ_master.dev_attr.attr,
14362306a36Sopenharmony_ci	&sensor_dev_attr_occ_active.dev_attr.attr,
14462306a36Sopenharmony_ci	&sensor_dev_attr_occ_dvfs_overtemp.dev_attr.attr,
14562306a36Sopenharmony_ci	&sensor_dev_attr_occ_dvfs_power.dev_attr.attr,
14662306a36Sopenharmony_ci	&sensor_dev_attr_occ_mem_throttle.dev_attr.attr,
14762306a36Sopenharmony_ci	&sensor_dev_attr_occ_quick_pwr_drop.dev_attr.attr,
14862306a36Sopenharmony_ci	&sensor_dev_attr_occ_state.dev_attr.attr,
14962306a36Sopenharmony_ci	&sensor_dev_attr_occs_present.dev_attr.attr,
15062306a36Sopenharmony_ci	&sensor_dev_attr_occ_ips_status.dev_attr.attr,
15162306a36Sopenharmony_ci	&sensor_dev_attr_occ_mode.dev_attr.attr,
15262306a36Sopenharmony_ci	&sensor_dev_attr_occ_dvfs_vdd.dev_attr.attr,
15362306a36Sopenharmony_ci	&sensor_dev_attr_occ_gpu_throttle.dev_attr.attr,
15462306a36Sopenharmony_ci	&dev_attr_occ_error.attr,
15562306a36Sopenharmony_ci	NULL
15662306a36Sopenharmony_ci};
15762306a36Sopenharmony_ci
15862306a36Sopenharmony_cistatic const struct attribute_group occ_sysfs = {
15962306a36Sopenharmony_ci	.attrs = occ_attributes,
16062306a36Sopenharmony_ci};
16162306a36Sopenharmony_ci
16262306a36Sopenharmony_civoid occ_sysfs_poll_done(struct occ *occ)
16362306a36Sopenharmony_ci{
16462306a36Sopenharmony_ci	const char *name;
16562306a36Sopenharmony_ci	struct occ_poll_response_header *header =
16662306a36Sopenharmony_ci		(struct occ_poll_response_header *)occ->resp.data;
16762306a36Sopenharmony_ci
16862306a36Sopenharmony_ci	/*
16962306a36Sopenharmony_ci	 * On the first poll response, we haven't yet created the sysfs
17062306a36Sopenharmony_ci	 * attributes, so don't make any notify calls.
17162306a36Sopenharmony_ci	 */
17262306a36Sopenharmony_ci	if (!occ->active)
17362306a36Sopenharmony_ci		goto done;
17462306a36Sopenharmony_ci
17562306a36Sopenharmony_ci	if ((header->status & OCC_STAT_MASTER) !=
17662306a36Sopenharmony_ci	    (occ->prev_stat & OCC_STAT_MASTER)) {
17762306a36Sopenharmony_ci		name = sensor_dev_attr_occ_master.dev_attr.attr.name;
17862306a36Sopenharmony_ci		sysfs_notify(&occ->bus_dev->kobj, NULL, name);
17962306a36Sopenharmony_ci	}
18062306a36Sopenharmony_ci
18162306a36Sopenharmony_ci	if ((header->ext_status & OCC_EXT_STAT_DVFS_OT) !=
18262306a36Sopenharmony_ci	    (occ->prev_ext_stat & OCC_EXT_STAT_DVFS_OT)) {
18362306a36Sopenharmony_ci		name = sensor_dev_attr_occ_dvfs_overtemp.dev_attr.attr.name;
18462306a36Sopenharmony_ci		sysfs_notify(&occ->bus_dev->kobj, NULL, name);
18562306a36Sopenharmony_ci	}
18662306a36Sopenharmony_ci
18762306a36Sopenharmony_ci	if ((header->ext_status & OCC_EXT_STAT_DVFS_POWER) !=
18862306a36Sopenharmony_ci	    (occ->prev_ext_stat & OCC_EXT_STAT_DVFS_POWER)) {
18962306a36Sopenharmony_ci		name = sensor_dev_attr_occ_dvfs_power.dev_attr.attr.name;
19062306a36Sopenharmony_ci		sysfs_notify(&occ->bus_dev->kobj, NULL, name);
19162306a36Sopenharmony_ci	}
19262306a36Sopenharmony_ci
19362306a36Sopenharmony_ci	if ((header->ext_status & OCC_EXT_STAT_MEM_THROTTLE) !=
19462306a36Sopenharmony_ci	    (occ->prev_ext_stat & OCC_EXT_STAT_MEM_THROTTLE)) {
19562306a36Sopenharmony_ci		name = sensor_dev_attr_occ_mem_throttle.dev_attr.attr.name;
19662306a36Sopenharmony_ci		sysfs_notify(&occ->bus_dev->kobj, NULL, name);
19762306a36Sopenharmony_ci	}
19862306a36Sopenharmony_ci
19962306a36Sopenharmony_ci	if ((header->ext_status & OCC_EXT_STAT_QUICK_DROP) !=
20062306a36Sopenharmony_ci	    (occ->prev_ext_stat & OCC_EXT_STAT_QUICK_DROP)) {
20162306a36Sopenharmony_ci		name = sensor_dev_attr_occ_quick_pwr_drop.dev_attr.attr.name;
20262306a36Sopenharmony_ci		sysfs_notify(&occ->bus_dev->kobj, NULL, name);
20362306a36Sopenharmony_ci	}
20462306a36Sopenharmony_ci
20562306a36Sopenharmony_ci	if ((header->ext_status & OCC_EXT_STAT_DVFS_VDD) !=
20662306a36Sopenharmony_ci	    (occ->prev_ext_stat & OCC_EXT_STAT_DVFS_VDD)) {
20762306a36Sopenharmony_ci		name = sensor_dev_attr_occ_dvfs_vdd.dev_attr.attr.name;
20862306a36Sopenharmony_ci		sysfs_notify(&occ->bus_dev->kobj, NULL, name);
20962306a36Sopenharmony_ci	}
21062306a36Sopenharmony_ci
21162306a36Sopenharmony_ci	if ((header->ext_status & OCC_EXT_STAT_GPU_THROTTLE) !=
21262306a36Sopenharmony_ci	    (occ->prev_ext_stat & OCC_EXT_STAT_GPU_THROTTLE)) {
21362306a36Sopenharmony_ci		name = sensor_dev_attr_occ_gpu_throttle.dev_attr.attr.name;
21462306a36Sopenharmony_ci		sysfs_notify(&occ->bus_dev->kobj, NULL, name);
21562306a36Sopenharmony_ci	}
21662306a36Sopenharmony_ci
21762306a36Sopenharmony_ci	if ((header->status & OCC_STAT_MASTER) &&
21862306a36Sopenharmony_ci	    header->occs_present != occ->prev_occs_present) {
21962306a36Sopenharmony_ci		name = sensor_dev_attr_occs_present.dev_attr.attr.name;
22062306a36Sopenharmony_ci		sysfs_notify(&occ->bus_dev->kobj, NULL, name);
22162306a36Sopenharmony_ci	}
22262306a36Sopenharmony_ci
22362306a36Sopenharmony_ci	if (header->ips_status != occ->prev_ips_status) {
22462306a36Sopenharmony_ci		name = sensor_dev_attr_occ_ips_status.dev_attr.attr.name;
22562306a36Sopenharmony_ci		sysfs_notify(&occ->bus_dev->kobj, NULL, name);
22662306a36Sopenharmony_ci	}
22762306a36Sopenharmony_ci
22862306a36Sopenharmony_ci	if (header->mode != occ->prev_mode) {
22962306a36Sopenharmony_ci		name = sensor_dev_attr_occ_mode.dev_attr.attr.name;
23062306a36Sopenharmony_ci		sysfs_notify(&occ->bus_dev->kobj, NULL, name);
23162306a36Sopenharmony_ci	}
23262306a36Sopenharmony_ci
23362306a36Sopenharmony_ci	if (occ->error && occ->error != occ->prev_error) {
23462306a36Sopenharmony_ci		name = dev_attr_occ_error.attr.name;
23562306a36Sopenharmony_ci		sysfs_notify(&occ->bus_dev->kobj, NULL, name);
23662306a36Sopenharmony_ci	}
23762306a36Sopenharmony_ci
23862306a36Sopenharmony_ci	/* no notifications for OCC state; doesn't indicate error condition */
23962306a36Sopenharmony_ci
24062306a36Sopenharmony_cidone:
24162306a36Sopenharmony_ci	occ->prev_error = occ->error;
24262306a36Sopenharmony_ci	occ->prev_stat = header->status;
24362306a36Sopenharmony_ci	occ->prev_ext_stat = header->ext_status;
24462306a36Sopenharmony_ci	occ->prev_occs_present = header->occs_present;
24562306a36Sopenharmony_ci	occ->prev_ips_status = header->ips_status;
24662306a36Sopenharmony_ci	occ->prev_mode = header->mode;
24762306a36Sopenharmony_ci}
24862306a36Sopenharmony_ci
24962306a36Sopenharmony_ciint occ_setup_sysfs(struct occ *occ)
25062306a36Sopenharmony_ci{
25162306a36Sopenharmony_ci	return sysfs_create_group(&occ->bus_dev->kobj, &occ_sysfs);
25262306a36Sopenharmony_ci}
25362306a36Sopenharmony_ci
25462306a36Sopenharmony_civoid occ_shutdown_sysfs(struct occ *occ)
25562306a36Sopenharmony_ci{
25662306a36Sopenharmony_ci	sysfs_remove_group(&occ->bus_dev->kobj, &occ_sysfs);
25762306a36Sopenharmony_ci}
258