Linux Audio

Check our new training course

Loading...
Note: File does not exist in v3.1.
  1// SPDX-License-Identifier: GPL-2.0+
  2// Copyright IBM Corp 2019
  3
  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/sysfs.h>
 10
 11#include "common.h"
 12
 13/* OCC status register */
 14#define OCC_STAT_MASTER			BIT(7)
 15#define OCC_STAT_ACTIVE			BIT(0)
 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
 23static ssize_t occ_sysfs_show(struct device *dev,
 24			      struct device_attribute *attr, char *buf)
 25{
 26	int rc;
 27	int val = 0;
 28	struct occ *occ = dev_get_drvdata(dev);
 29	struct occ_poll_response_header *header;
 30	struct sensor_device_attribute *sattr = to_sensor_dev_attr(attr);
 31
 32	rc = occ_update_response(occ);
 33	if (rc)
 34		return rc;
 35
 36	header = (struct occ_poll_response_header *)occ->resp.data;
 37
 38	switch (sattr->index) {
 39	case 0:
 40		val = !!(header->status & OCC_STAT_MASTER);
 41		break;
 42	case 1:
 43		val = !!(header->status & OCC_STAT_ACTIVE);
 44		break;
 45	case 2:
 46		val = !!(header->ext_status & OCC_EXT_STAT_DVFS_OT);
 47		break;
 48	case 3:
 49		val = !!(header->ext_status & OCC_EXT_STAT_DVFS_POWER);
 50		break;
 51	case 4:
 52		val = !!(header->ext_status & OCC_EXT_STAT_MEM_THROTTLE);
 53		break;
 54	case 5:
 55		val = !!(header->ext_status & OCC_EXT_STAT_QUICK_DROP);
 56		break;
 57	case 6:
 58		val = header->occ_state;
 59		break;
 60	case 7:
 61		if (header->status & OCC_STAT_MASTER)
 62			val = hweight8(header->occs_present);
 63		else
 64			val = 1;
 65		break;
 66	default:
 67		return -EINVAL;
 68	}
 69
 70	return snprintf(buf, PAGE_SIZE - 1, "%d\n", val);
 71}
 72
 73static ssize_t occ_error_show(struct device *dev,
 74			      struct device_attribute *attr, char *buf)
 75{
 76	struct occ *occ = dev_get_drvdata(dev);
 77
 78	occ_update_response(occ);
 79
 80	return snprintf(buf, PAGE_SIZE - 1, "%d\n", occ->error);
 81}
 82
 83static SENSOR_DEVICE_ATTR(occ_master, 0444, occ_sysfs_show, NULL, 0);
 84static SENSOR_DEVICE_ATTR(occ_active, 0444, occ_sysfs_show, NULL, 1);
 85static SENSOR_DEVICE_ATTR(occ_dvfs_overtemp, 0444, occ_sysfs_show, NULL, 2);
 86static SENSOR_DEVICE_ATTR(occ_dvfs_power, 0444, occ_sysfs_show, NULL, 3);
 87static SENSOR_DEVICE_ATTR(occ_mem_throttle, 0444, occ_sysfs_show, NULL, 4);
 88static SENSOR_DEVICE_ATTR(occ_quick_pwr_drop, 0444, occ_sysfs_show, NULL, 5);
 89static SENSOR_DEVICE_ATTR(occ_state, 0444, occ_sysfs_show, NULL, 6);
 90static SENSOR_DEVICE_ATTR(occs_present, 0444, occ_sysfs_show, NULL, 7);
 91static DEVICE_ATTR_RO(occ_error);
 92
 93static struct attribute *occ_attributes[] = {
 94	&sensor_dev_attr_occ_master.dev_attr.attr,
 95	&sensor_dev_attr_occ_active.dev_attr.attr,
 96	&sensor_dev_attr_occ_dvfs_overtemp.dev_attr.attr,
 97	&sensor_dev_attr_occ_dvfs_power.dev_attr.attr,
 98	&sensor_dev_attr_occ_mem_throttle.dev_attr.attr,
 99	&sensor_dev_attr_occ_quick_pwr_drop.dev_attr.attr,
100	&sensor_dev_attr_occ_state.dev_attr.attr,
101	&sensor_dev_attr_occs_present.dev_attr.attr,
102	&dev_attr_occ_error.attr,
103	NULL
104};
105
106static const struct attribute_group occ_sysfs = {
107	.attrs = occ_attributes,
108};
109
110void occ_sysfs_poll_done(struct occ *occ)
111{
112	const char *name;
113	struct occ_poll_response_header *header =
114		(struct occ_poll_response_header *)occ->resp.data;
115
116	/*
117	 * On the first poll response, we haven't yet created the sysfs
118	 * attributes, so don't make any notify calls.
119	 */
120	if (!occ->hwmon)
121		goto done;
122
123	if ((header->status & OCC_STAT_MASTER) !=
124	    (occ->prev_stat & OCC_STAT_MASTER)) {
125		name = sensor_dev_attr_occ_master.dev_attr.attr.name;
126		sysfs_notify(&occ->bus_dev->kobj, NULL, name);
127	}
128
129	if ((header->status & OCC_STAT_ACTIVE) !=
130	    (occ->prev_stat & OCC_STAT_ACTIVE)) {
131		name = sensor_dev_attr_occ_active.dev_attr.attr.name;
132		sysfs_notify(&occ->bus_dev->kobj, NULL, name);
133	}
134
135	if ((header->ext_status & OCC_EXT_STAT_DVFS_OT) !=
136	    (occ->prev_ext_stat & OCC_EXT_STAT_DVFS_OT)) {
137		name = sensor_dev_attr_occ_dvfs_overtemp.dev_attr.attr.name;
138		sysfs_notify(&occ->bus_dev->kobj, NULL, name);
139	}
140
141	if ((header->ext_status & OCC_EXT_STAT_DVFS_POWER) !=
142	    (occ->prev_ext_stat & OCC_EXT_STAT_DVFS_POWER)) {
143		name = sensor_dev_attr_occ_dvfs_power.dev_attr.attr.name;
144		sysfs_notify(&occ->bus_dev->kobj, NULL, name);
145	}
146
147	if ((header->ext_status & OCC_EXT_STAT_MEM_THROTTLE) !=
148	    (occ->prev_ext_stat & OCC_EXT_STAT_MEM_THROTTLE)) {
149		name = sensor_dev_attr_occ_mem_throttle.dev_attr.attr.name;
150		sysfs_notify(&occ->bus_dev->kobj, NULL, name);
151	}
152
153	if ((header->ext_status & OCC_EXT_STAT_QUICK_DROP) !=
154	    (occ->prev_ext_stat & OCC_EXT_STAT_QUICK_DROP)) {
155		name = sensor_dev_attr_occ_quick_pwr_drop.dev_attr.attr.name;
156		sysfs_notify(&occ->bus_dev->kobj, NULL, name);
157	}
158
159	if ((header->status & OCC_STAT_MASTER) &&
160	    header->occs_present != occ->prev_occs_present) {
161		name = sensor_dev_attr_occs_present.dev_attr.attr.name;
162		sysfs_notify(&occ->bus_dev->kobj, NULL, name);
163	}
164
165	if (occ->error && occ->error != occ->prev_error) {
166		name = dev_attr_occ_error.attr.name;
167		sysfs_notify(&occ->bus_dev->kobj, NULL, name);
168	}
169
170	/* no notifications for OCC state; doesn't indicate error condition */
171
172done:
173	occ->prev_error = occ->error;
174	occ->prev_stat = header->status;
175	occ->prev_ext_stat = header->ext_status;
176	occ->prev_occs_present = header->occs_present;
177}
178
179int occ_setup_sysfs(struct occ *occ)
180{
181	return sysfs_create_group(&occ->bus_dev->kobj, &occ_sysfs);
182}
183
184void occ_shutdown(struct occ *occ)
185{
186	sysfs_remove_group(&occ->bus_dev->kobj, &occ_sysfs);
187}
188EXPORT_SYMBOL_GPL(occ_shutdown);