#include <linux/io.h>
#include <linux/delay.h>
#include <linux/module.h>
#include <linux/thermal.h>
#include <linux/platform_device.h>

/*
 * According to a data sheet draft, "this temperature sensor uses a bandgap
 * type of circuit to compare a voltage which has a negative temperature
 * coefficient with a voltage that is proportional to absolute temperature.
 * A resistor bank allows 41 different temperature thresholds to be selected
 * and the logic output will then indicate whether the actual die temperature
 * lies above or below the selected threshold."
 */

#define TEMPSI_CMD	0
#define TEMPSI_RES	4
#define TEMPSI_CFG	8

#define CMD_OFF		0
#define CMD_ON		1
#define CMD_READ	2

#define IDX_MIN		15
#define IDX_MAX		40

struct tango_thermal_priv {
	void __iomem *base;
	int thresh_idx;
};

static bool temp_above_thresh(void __iomem *base, int thresh_idx)
{
	writel(CMD_READ | thresh_idx << 8, base + TEMPSI_CMD);
	usleep_range(10, 20);
	writel(CMD_READ | thresh_idx << 8, base + TEMPSI_CMD);

	return readl(base + TEMPSI_RES);
}

static int tango_get_temp(void *arg, int *res)
{
	struct tango_thermal_priv *priv = arg;
	int idx = priv->thresh_idx;

	if (temp_above_thresh(priv->base, idx)) {
		/* Search upward by incrementing thresh_idx */
		while (idx < IDX_MAX && temp_above_thresh(priv->base, ++idx))
			cpu_relax();
		idx = idx - 1; /* always return lower bound */
	} else {
		/* Search downward by decrementing thresh_idx */
		while (idx > IDX_MIN && !temp_above_thresh(priv->base, --idx))
			cpu_relax();
	}

	*res = (idx * 9 / 2 - 38) * 1000; /* millidegrees Celsius */
	priv->thresh_idx = idx;

	return 0;
}

static const struct thermal_zone_of_device_ops ops = {
	.get_temp	= tango_get_temp,
};

static void tango_thermal_init(struct tango_thermal_priv *priv)
{
	writel(0, priv->base + TEMPSI_CFG);
	writel(CMD_ON, priv->base + TEMPSI_CMD);
}

static int tango_thermal_probe(struct platform_device *pdev)
{
	struct resource *res;
	struct tango_thermal_priv *priv;
	struct thermal_zone_device *tzdev;

	priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
	if (!priv)
		return -ENOMEM;

	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
	priv->base = devm_ioremap_resource(&pdev->dev, res);
	if (IS_ERR(priv->base))
		return PTR_ERR(priv->base);

	platform_set_drvdata(pdev, priv);
	priv->thresh_idx = IDX_MIN;
	tango_thermal_init(priv);

	tzdev = devm_thermal_zone_of_sensor_register(&pdev->dev, 0, priv, &ops);
	return PTR_ERR_OR_ZERO(tzdev);
}

static int __maybe_unused tango_thermal_resume(struct device *dev)
{
	tango_thermal_init(dev_get_drvdata(dev));
	return 0;
}

static SIMPLE_DEV_PM_OPS(tango_thermal_pm, NULL, tango_thermal_resume);

static const struct of_device_id tango_sensor_ids[] = {
	{
		.compatible = "sigma,smp8758-thermal",
	},
	{ /* sentinel */ }
};
MODULE_DEVICE_TABLE(of, tango_sensor_ids);

static struct platform_driver tango_thermal_driver = {
	.probe	= tango_thermal_probe,
	.driver	= {
		.name		= "tango-thermal",
		.of_match_table	= tango_sensor_ids,
		.pm		= &tango_thermal_pm,
	},
};

module_platform_driver(tango_thermal_driver);

MODULE_LICENSE("GPL");
MODULE_AUTHOR("Sigma Designs");
MODULE_DESCRIPTION("Tango temperature sensor");
this.form.submit();'><option value='1'>1</option><option value='2'>2</option><option value='3' selected='selected'>3</option><option value='4'>4</option><option value='5'>5</option><option value='6'>6</option><option value='7'>7</option><option value='8'>8</option><option value='9'>9</option><option value='10'>10</option><option value='15'>15</option><option value='20'>20</option><option value='25'>25</option><option value='30'>30</option><option value='35'>35</option><option value='40'>40</option></select></td></tr><tr><td class='label'>space:</td><td class='ctrl'><select name='ignorews' onchange='this.form.submit();'><option value='0' selected='selected'>include</option><option value='1'>ignore</option></select></td></tr><tr><td class='label'>mode:</td><td class='ctrl'><select name='dt' onchange='this.form.submit();'><option value='0' selected='selected'>unified</option><option value='1'>ssdiff</option><option value='2'>stat only</option></select></td></tr><tr><td/><td class='ctrl'><noscript><input type='submit' value='reload'/></noscript></td></tr></table></form></div><table summary='commit info' class='commit-info'>
<tr><th>author</th><td>Steven Rostedt (VMware) &lt;rostedt@goodmis.org&gt;</td><td class='right'>2017-01-30 19:27:10 -0500</td></tr>
<tr><th>committer</th><td>Steven Rostedt (VMware) &lt;rostedt@goodmis.org&gt;</td><td class='right'>2017-01-31 09:13:49 -0500</td></tr>
<tr><th>commit</th><td colspan='2' class='oid'><a href='/cgit.cgi/linux/net-next.git/commit/fs/anon_inodes.c?id=79c6f448c8b79c321e4a1f31f98194e4f6b6cae7'>79c6f448c8b79c321e4a1f31f98194e4f6b6cae7</a> (<a href='/cgit.cgi/linux/net-next.git/patch/fs/anon_inodes.c?id=79c6f448c8b79c321e4a1f31f98194e4f6b6cae7'>patch</a>)</td></tr>
<tr><th>tree</th><td colspan='2' class='oid'><a href='/cgit.cgi/linux/net-next.git/tree/?id=79c6f448c8b79c321e4a1f31f98194e4f6b6cae7'>370efda701f03cccf21e02bb1fdd3b852547d75c</a> /<a href='/cgit.cgi/linux/net-next.git/tree/fs/anon_inodes.c?id=79c6f448c8b79c321e4a1f31f98194e4f6b6cae7'>fs/anon_inodes.c</a></td></tr>
<tr><th>parent</th><td colspan='2' class='oid'><a href='/cgit.cgi/linux/net-next.git/commit/fs/anon_inodes.c?id=0c744ea4f77d72b3dcebb7a8f2684633ec79be88'>0c744ea4f77d72b3dcebb7a8f2684633ec79be88</a> (<a href='/cgit.cgi/linux/net-next.git/diff/fs/anon_inodes.c?id=79c6f448c8b79c321e4a1f31f98194e4f6b6cae7&amp;id2=0c744ea4f77d72b3dcebb7a8f2684633ec79be88'>diff</a>)</td></tr></table>
<div class='commit-subject'>tracing: Fix hwlat kthread migration</div><div class='commit-msg'>The hwlat tracer creates a kernel thread at start of the tracer. It is
pinned to a single CPU and will move to the next CPU after each period of
running. If the user modifies the migration thread's affinity, it will not
change after that happens.

The original code created the thread at the first instance it was called,
but later was changed to destroy the thread after the tracer was finished,
and would not be created until the next instance of the tracer was
established. The code that initialized the affinity was only called on the
initial instantiation of the tracer. After that, it was not initialized, and
the previous affinity did not match the current newly created one, making
it appear that the user modified the thread's affinity when it did not, and
the thread failed to migrate again.

Cc: stable@vger.kernel.org
Fixes: 0330f7aa8ee6 ("tracing: Have hwlat trace migrate across tracing_cpumask CPUs")
Signed-off-by: Steven Rostedt (VMware) &lt;rostedt@goodmis.org&gt;
</div><div class='diffstat-header'><a href='/cgit.cgi/linux/net-next.git/diff/?id=79c6f448c8b79c321e4a1f31f98194e4f6b6cae7'>Diffstat</a> (limited to 'fs/anon_inodes.c')</div><table summary='diffstat' class='diffstat'>