aboutsummaryrefslogtreecommitdiffstats
path: root/tools/perf/scripts/python/bin
AgeCommit message (Expand)AuthorFilesLines
2018-01-12perf script python: Add script to profile and resolve physical mem typeKan Liang2-0/+22
2017-08-15perf script python: Add support for exporting to sqlite3Adrian Hunter2-0/+37
2017-06-30perf intel-pt: Add example script for power events and PTWRITEAdrian Hunter2-0/+16
2016-06-21perf script: Add stackcollapse.py scriptPaolo Bonzini2-0/+11
2015-08-28perf scripts python: Add new compaction-times scriptTony Jones2-0/+6
2014-11-03perf tools: Add call information to Python exportAdrian Hunter1-5/+10
2014-10-29perf script: Add Python script to export to postgresqlAdrian Hunter2-0/+32
2014-06-25perf scripts: Fallback to syscalls:* when raw_syscalls:* is not availableDaniel Bristot de Oliveira4-4/+8
2012-09-17perf scripts: Add event_analyzing_sample-record/reportFeng Tang2-0/+11
2011-09-29perf script: Add drop monitor scriptNeil Horman2-0/+6
2010-12-25perf script: Finish the rename from trace to scriptArnaldo Carvalho de Melo7-7/+7
2010-11-10perf trace scripting: remove system-wide param from shell scriptsTom Zanussi7-7/+7
2010-10-26perf python scripting: Add futex-contention scriptArnaldo Carvalho de Melo2-0/+6
2010-10-23perf trace: Use $PERF_EXEC_PATH in canned report scriptsBen Hutchings6-6/+6
2010-09-07perf: Add a script to show packets processingKoki Sanagi2-0/+13
2010-08-02perf: New migration tool overviewFrederic Weisbecker2-0/+5
2010-05-10perf/trace/scripting: syscall-counts script cleanupTom Zanussi1-1/+1
2010-05-10perf/trace/scripting: syscall-counts-by-pid script cleanupTom Zanussi1-1/+1
2010-05-10perf/trace/scripting: failed-syscalls-by-pid script cleanupTom Zanussi1-1/+1
2010-04-30perf: Remove leftover useless options to record trace events from scriptsFrederic Weisbecker4-4/+4
2010-04-14perf trace/scripting: Enable scripting shell scripts for live modeTom Zanussi6-6/+24
2010-04-14perf trace/scripting: Add rwtop and sctop scriptsTom Zanussi2-0/+26
2010-02-25perf/scripts: Add syscall tracing scriptsTom Zanussi6-0/+18
d='n455' href='#n455'>455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596
// SPDX-License-Identifier: GPL-2.0-only

#include <linux/bitfield.h>
#include <linux/bits.h>
#include <linux/err.h>
#include <linux/hwmon.h>
#include <linux/hwmon-sysfs.h>
#include <linux/i2c.h>
#include <linux/regmap.h>
#include <linux/util_macros.h>

#define REG_CR1		0x00
#define CR1_HYST	BIT(5)
#define CR1_DRV		GENMASK(4, 3)
#define CR1_TEMP_SRC	GENMASK(1, 0)
#define REG_CR2		0x01
#define CR2_STBY	BIT(7)
#define CR2_ALERTS	BIT(6)
#define CR2_DFC		BIT(0)
#define REG_CR3		0x02
#define REG_PWMR	0x50
#define REG_PWMV	0x51
#define REG_STATUS	0x5A
#define STATUS_ALARM_CRIT(ch)	BIT(2 + 2 * (ch))
#define STATUS_ALARM_MAX(ch)	BIT(3 + 2 * (ch))
#define STATUS_RDFA		BIT(6)

#define REG_TACH(ch)		(0x52 + (ch) * 2)
#define REG_TEMP_INPUT(ch)	(0x56 + (ch) * 2)
#define REG_TEMP_MAX(ch)	(0x06 + (ch) * 2)
#define REG_TEMP_CRIT(ch)	(0x0A + (ch) * 2)

#define TEMP11_FROM_REG(reg)	((reg) / 32 * 125)
#define TEMP11_TO_REG(val)	(DIV_ROUND_CLOSEST(clamp_val((val), -128000, \
							     127875), 125) * 32)

#define LUT_SIZE	48

#define REG_LUT(index)		(0x20 + (index))

struct max31760_state {
	struct regmap *regmap;

	struct lut_attribute {
		char name[24];
		struct sensor_device_attribute sda;
	} lut[LUT_SIZE];

	struct attribute *attrs[LUT_SIZE + 2];
	struct attribute_group group;
	const struct attribute_group *groups[2];
};

static bool max31760_volatile_reg(struct device *dev, unsigned int reg)
{
	return reg > 0x50;
}

static const struct regmap_config regmap_config = {
	.reg_bits = 8,
	.val_bits = 8,
	.max_register = 0x5B,
	.cache_type = REGCACHE_RBTREE,
	.volatile_reg = max31760_volatile_reg,
};

static const int max31760_pwm_freq[] = {33, 150, 1500, 25000};

static int tach_to_rpm(u16 tach)
{
	if (tach == 0)
		tach = 1;

	return 60 * 100000 / tach / 2;
}

static int max31760_read(struct device *dev, enum hwmon_sensor_types type,
			 u32 attr, int channel, long *val)
{
	struct max31760_state *state = dev_get_drvdata(dev);
	unsigned int regval;
	unsigned int reg_temp;
	s16 temp;
	u8 reg[2];
	int ret;

	switch (type) {
	case hwmon_temp:
		switch (attr) {
		case hwmon_temp_fault:
			ret = regmap_read(state->regmap, REG_STATUS, &regval);
			if (ret)
				return ret;

			*val = FIELD_GET(STATUS_RDFA, regval);

			return 0;
		case hwmon_temp_max_alarm:
			ret = regmap_read(state->regmap, REG_STATUS, &regval);
			if (ret)
				return ret;

			if (channel)
				*val = FIELD_GET(STATUS_ALARM_MAX(1), regval);
			else
				*val = FIELD_GET(STATUS_ALARM_MAX(0), regval);

			return 0;
		case hwmon_temp_crit_alarm:
			ret = regmap_read(state->regmap, REG_STATUS, &regval);
			if (ret)
				return ret;

			if (channel)
				*val = FIELD_GET(STATUS_ALARM_CRIT(1), regval);
			else
				*val = FIELD_GET(STATUS_ALARM_CRIT(0), regval);

			return 0;
		case hwmon_temp_input:
			reg_temp = REG_TEMP_INPUT(channel);
			break;
		case hwmon_temp_max:
			reg_temp = REG_TEMP_MAX(channel);
			break;
		case hwmon_temp_crit:
			reg_temp = REG_TEMP_CRIT(channel);
			break;
		default:
			return -EOPNOTSUPP;
		}

		ret = regmap_bulk_read(state->regmap, reg_temp, reg, 2);
		if (ret)
			return ret;

		temp = (reg[0] << 8) | reg[1];

		*val = TEMP11_FROM_REG(temp);

		return 0;
	case hwmon_fan:
		switch (attr) {
		case hwmon_fan_input:
			ret = regmap_bulk_read(state->regmap, REG_TACH(channel), reg, 2);
			if (ret)
				return ret;

			*val = tach_to_rpm(reg[0] * 256 + reg[1]);

			return 0;
		case hwmon_fan_fault:
			ret = regmap_read(state->regmap, REG_STATUS, &regval);
			if (ret)
				return ret;

			if (channel)
				*val = FIELD_GET(BIT(1), regval);
			else
				*val = FIELD_GET(BIT(0), regval);

			return 0;
		case hwmon_fan_enable:
			ret = regmap_read(state->regmap, REG_CR3, &regval);
			if (ret)
				return ret;

			if (channel)
				*val = FIELD_GET(BIT(1), regval);
			else
				*val = FIELD_GET(BIT(0), regval);

			return 0;
		default:
			return -EOPNOTSUPP;
		}
	case hwmon_pwm:
		switch (attr) {
		case hwmon_pwm_input:
			ret = regmap_read(state->regmap, REG_PWMV, &regval);
			if (ret)
				return ret;

			*val = regval;

			return 0;
		case hwmon_pwm_freq:
			ret = regmap_read(state->regmap, REG_CR1, &regval);
			if (ret)
				return ret;

			regval = FIELD_GET(CR1_DRV, regval);
			if (regval >= ARRAY_SIZE(max31760_pwm_freq))
				return -EINVAL;

			*val = max31760_pwm_freq[regval];

			return 0;
		case hwmon_pwm_enable:
			ret = regmap_read(state->regmap, REG_CR2, &regval);
			if (ret)
				return ret;

			*val = 2 - FIELD_GET(CR2_DFC, regval);

			return 0;
		case hwmon_pwm_auto_channels_temp:
			ret = regmap_read(state->regmap, REG_CR1, &regval);
			if (ret)
				return ret;

			switch (FIELD_GET(CR1_TEMP_SRC, regval)) {
			case 0:
				*val = 2;
				break;
			case 1:
				*val = 1;
				break;
			case 2:
			case 3:
				*val = 3;
				break;
			default:
				return -EINVAL;
			}

			return 0;
		default:
			return -EOPNOTSUPP;
		}
	default:
		return -EOPNOTSUPP;
	}
}

static int max31760_write(struct device *dev, enum hwmon_sensor_types type,
			  u32 attr, int channel, long val)
{
	struct max31760_state *state = dev_get_drvdata(dev);
	unsigned int pwm_index;
	unsigned int reg_temp;
	int temp;
	u8 reg_val[2];

	switch (type) {
	case hwmon_temp:
		switch (attr) {
		case hwmon_temp_max:
			reg_temp = REG_TEMP_MAX(channel);
			break;
		case hwmon_temp_crit:
			reg_temp = REG_TEMP_CRIT(channel);
			break;
		default:
			return -EOPNOTSUPP;
		}

		temp = TEMP11_TO_REG(val);
		reg_val[0] = temp >> 8;
		reg_val[1] = temp & 0xFF;

		return regmap_bulk_write(state->regmap, reg_temp, reg_val, 2);
	case hwmon_fan:
		switch (attr) {
		case hwmon_fan_enable:
			if (val == 0)
				return regmap_clear_bits(state->regmap, REG_CR3, BIT(channel));

			if (val == 1)
				return regmap_set_bits(state->regmap, REG_CR3, BIT(channel));

			return -EINVAL;
		default:
			return -EOPNOTSUPP;
		}
	case hwmon_pwm:
		switch (attr) {
		case hwmon_pwm_input:
			if (val < 0 || val > 255)
				return -EINVAL;

			return regmap_write(state->regmap, REG_PWMR, val);
		case hwmon_pwm_enable:
			if (val == 1)
				return regmap_set_bits(state->regmap, REG_CR2, CR2_DFC);

			if (val == 2)
				return regmap_clear_bits(state->regmap, REG_CR2, CR2_DFC);

			return -EINVAL;
		case hwmon_pwm_freq:
			pwm_index = find_closest(val, max31760_pwm_freq,
						 ARRAY_SIZE(max31760_pwm_freq));

			return regmap_update_bits(state->regmap,
						  REG_CR1, CR1_DRV,
						  FIELD_PREP(CR1_DRV, pwm_index));
		case hwmon_pwm_auto_channels_temp:
			switch (val) {
			case 1:
				break;
			case 2:
				val = 0;
				break;
			case 3:
				val = 2;
				break;
			default:
				return -EINVAL;
			}

			return regmap_update_bits(state->regmap, REG_CR1, CR1_TEMP_SRC, val);
		default:
			return -EOPNOTSUPP;
		}
	default:
		return -EOPNOTSUPP;
	}
}

static const struct hwmon_channel_info * const max31760_info[] = {
	HWMON_CHANNEL_INFO(chip,
			   HWMON_C_REGISTER_TZ),
	HWMON_CHANNEL_INFO(fan,
			   HWMON_F_INPUT | HWMON_F_FAULT | HWMON_F_ENABLE,
			   HWMON_F_INPUT | HWMON_F_FAULT | HWMON_F_ENABLE),
	HWMON_CHANNEL_INFO(temp,
			   HWMON_T_INPUT | HWMON_T_MAX | HWMON_T_CRIT | HWMON_T_FAULT |
			   HWMON_T_MAX_ALARM | HWMON_T_CRIT_ALARM | HWMON_T_LABEL,
			   HWMON_T_INPUT | HWMON_T_MAX | HWMON_T_CRIT |
			   HWMON_T_MAX_ALARM | HWMON_T_CRIT_ALARM | HWMON_T_LABEL),
	HWMON_CHANNEL_INFO(pwm,
			   HWMON_PWM_ENABLE | HWMON_PWM_FREQ | HWMON_PWM_INPUT |
			   HWMON_PWM_AUTO_CHANNELS_TEMP),
	NULL
};

static umode_t max31760_is_visible(const void *data,
				   enum hwmon_sensor_types type,
				   u32 attr, int channel)
{
	switch (type) {
	case hwmon_temp:
		switch (attr) {
		case hwmon_temp_input:
		case hwmon_temp_max_alarm:
		case hwmon_temp_crit_alarm:
		case hwmon_temp_fault:
		case hwmon_temp_label:
			return 0444;
		case hwmon_temp_max:
		case hwmon_temp_crit:
			return 0644;
		default:
			return 0;
		}
	case hwmon_fan:
		switch (attr) {
		case hwmon_fan_input:
		case hwmon_fan_fault:
			return 0444;
		case hwmon_fan_enable:
			return 0644;
		default:
			return 0;
		}
	case hwmon_pwm:
		switch (attr) {
		case hwmon_pwm_enable:
		case hwmon_pwm_input:
		case hwmon_pwm_freq:
		case hwmon_pwm_auto_channels_temp:
			return 0644;
		default:
			return 0;
		}
	default:
		return 0;
	}
}

static int max31760_read_string(struct device *dev,
				enum hwmon_sensor_types type,
				u32 attr, int channel, const char **str)
{
	switch (type) {
	case hwmon_temp:
		if (attr != hwmon_temp_label)
			return -EOPNOTSUPP;

		*str = channel ? "local" : "remote";

		return 0;
	default:
		return -EOPNOTSUPP;
	}
}

static const struct hwmon_ops max31760_hwmon_ops = {
	.is_visible = max31760_is_visible,
	.read = max31760_read,
	.write = max31760_write,
	.read_string = max31760_read_string
};

static const struct hwmon_chip_info max31760_chip_info = {
	.ops = &max31760_hwmon_ops,
	.info = max31760_info,
};

static ssize_t lut_show(struct device *dev,
			struct device_attribute *devattr, char *buf)
{
	struct sensor_device_attribute *sda = to_sensor_dev_attr(devattr);
	struct max31760_state *state = dev_get_drvdata(dev);
	int ret;
	unsigned int regval;

	ret = regmap_read(state->regmap, REG_LUT(sda->index), &regval);
	if (ret)
		return ret;

	return sysfs_emit(buf, "%d\n", regval);
}

static ssize_t lut_store(struct device *dev,
			 struct device_attribute *devattr,
			 const char *buf, size_t count)
{
	struct sensor_device_attribute *sda = to_sensor_dev_attr(devattr);
	struct max31760_state *state = dev_get_drvdata(dev);
	int ret;
	u8 pwm;

	ret = kstrtou8(buf, 10, &pwm);
	if (ret)
		return ret;

	ret = regmap_write(state->regmap, REG_LUT(sda->index), pwm);
	if (ret)
		return ret;

	return count;
}

static ssize_t pwm1_auto_point_temp_hyst_show(struct device *dev,
					      struct device_attribute *attr,
					      char *buf)
{
	struct max31760_state *state = dev_get_drvdata(dev);
	unsigned int regval;
	int ret;

	ret = regmap_read(state->regmap, REG_CR1, &regval);
	if (ret)
		return ret;

	return sysfs_emit(buf, "%d\n", (1 + (int)FIELD_GET(CR1_HYST, regval)) * 2000);
}

static ssize_t pwm1_auto_point_temp_hyst_store(struct device *dev,
					       struct device_attribute *attr,
					       const char *buf,
					       size_t count)
{
	struct max31760_state *state = dev_get_drvdata(dev);
	unsigned int hyst;
	int ret;

	ret = kstrtou32(buf, 10, &hyst);
	if (ret)
		return ret;

	if (hyst < 3000)
		ret = regmap_clear_bits(state->regmap, REG_CR1, CR1_HYST);
	else
		ret = regmap_set_bits(state->regmap, REG_CR1, CR1_HYST);

	if (ret)
		return ret;

	return count;
}

static DEVICE_ATTR_RW(pwm1_auto_point_temp_hyst);

static void max31760_create_lut_nodes(struct max31760_state *state)
{
	int i;
	struct sensor_device_attribute *sda;
	struct lut_attribute *lut;

	for (i = 0; i < LUT_SIZE; ++i) {
		lut = &state->lut[i];
		sda = &lut->sda;

		snprintf(lut->name, sizeof(lut->name),
			 "pwm1_auto_point%d_pwm", i + 1);

		sda->dev_attr.attr.mode = 0644;
		sda->index = i;
		sda->dev_attr.show = lut_show;
		sda->dev_attr.store = lut_store;
		sda->dev_attr.attr.name = lut->name;

		sysfs_attr_init(&sda->dev_attr.attr);

		state->attrs[i] = &sda->dev_attr.attr;
	}

	state->attrs[i] = &dev_attr_pwm1_auto_point_temp_hyst.attr;

	state->group.attrs = state->attrs;
	state->groups[0] = &state->group;
}

static int max31760_probe(struct i2c_client *client)
{
	struct device *dev = &client->dev;
	struct max31760_state *state;
	struct device *hwmon_dev;
	int ret;

	state = devm_kzalloc(dev, sizeof(*state), GFP_KERNEL);
	if (!state)
		return -ENOMEM;

	state->regmap = devm_regmap_init_i2c(client, &regmap_config);
	if (IS_ERR(state->regmap))
		return dev_err_probe(dev,
				     PTR_ERR(state->regmap),
				     "regmap initialization failed\n");

	dev_set_drvdata(dev, state);

	/* Set alert output to comparator mode */
	ret = regmap_set_bits(state->regmap, REG_CR2, CR2_ALERTS);
	if (ret)
		return dev_err_probe(dev, ret, "cannot write register\n");

	max31760_create_lut_nodes(state);

	hwmon_dev = devm_hwmon_device_register_with_info(dev, client->name,
							 state,
							 &max31760_chip_info,
							 state->groups);

	return PTR_ERR_OR_ZERO(hwmon_dev);
}

static const struct of_device_id max31760_of_match[] = {
	{.compatible = "adi,max31760"},
	{ }
};
MODULE_DEVICE_TABLE(of, max31760_of_match);

static const struct i2c_device_id max31760_id[] = {
	{"max31760"},
	{ }
};
MODULE_DEVICE_TABLE(i2c, max31760_id);

static int max31760_suspend(struct device *dev)
{
	struct max31760_state *state = dev_get_drvdata(dev);

	return regmap_set_bits(state->regmap, REG_CR2, CR2_STBY);
}

static int max31760_resume(struct device *dev)
{
	struct max31760_state *state = dev_get_drvdata(dev);

	return regmap_clear_bits(state->regmap, REG_CR2, CR2_STBY);
}

static DEFINE_SIMPLE_DEV_PM_OPS(max31760_pm_ops, max31760_suspend,
				max31760_resume);

static struct i2c_driver max31760_driver = {
	.class		= I2C_CLASS_HWMON,
	.driver = {
		.name	= "max31760",
		.of_match_table = max31760_of_match,
		.pm = pm_ptr(&max31760_pm_ops)
	},
	.probe		= max31760_probe,
	.id_table	= max31760_id
};
module_i2c_driver(max31760_driver);

MODULE_AUTHOR("Ibrahim Tilki <Ibrahim.Tilki@analog.com>");
MODULE_DESCRIPTION("Analog Devices MAX31760 Fan Speed Controller");
MODULE_SOFTDEP("pre: regmap_i2c");
MODULE_VERSION("1.0");
MODULE_LICENSE("GPL");