Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

at v4.13-rc3 125 lines 3.2 kB view raw
1#include <linux/io.h> 2#include <linux/delay.h> 3#include <linux/module.h> 4#include <linux/thermal.h> 5#include <linux/platform_device.h> 6 7/* 8 * According to a data sheet draft, "this temperature sensor uses a bandgap 9 * type of circuit to compare a voltage which has a negative temperature 10 * coefficient with a voltage that is proportional to absolute temperature. 11 * A resistor bank allows 41 different temperature thresholds to be selected 12 * and the logic output will then indicate whether the actual die temperature 13 * lies above or below the selected threshold." 14 */ 15 16#define TEMPSI_CMD 0 17#define TEMPSI_RES 4 18#define TEMPSI_CFG 8 19 20#define CMD_OFF 0 21#define CMD_ON 1 22#define CMD_READ 2 23 24#define IDX_MIN 15 25#define IDX_MAX 40 26 27struct tango_thermal_priv { 28 void __iomem *base; 29 int thresh_idx; 30}; 31 32static bool temp_above_thresh(void __iomem *base, int thresh_idx) 33{ 34 writel(CMD_READ | thresh_idx << 8, base + TEMPSI_CMD); 35 usleep_range(10, 20); 36 writel(CMD_READ | thresh_idx << 8, base + TEMPSI_CMD); 37 38 return readl(base + TEMPSI_RES); 39} 40 41static int tango_get_temp(void *arg, int *res) 42{ 43 struct tango_thermal_priv *priv = arg; 44 int idx = priv->thresh_idx; 45 46 if (temp_above_thresh(priv->base, idx)) { 47 /* Search upward by incrementing thresh_idx */ 48 while (idx < IDX_MAX && temp_above_thresh(priv->base, ++idx)) 49 cpu_relax(); 50 idx = idx - 1; /* always return lower bound */ 51 } else { 52 /* Search downward by decrementing thresh_idx */ 53 while (idx > IDX_MIN && !temp_above_thresh(priv->base, --idx)) 54 cpu_relax(); 55 } 56 57 *res = (idx * 9 / 2 - 38) * 1000; /* millidegrees Celsius */ 58 priv->thresh_idx = idx; 59 60 return 0; 61} 62 63static const struct thermal_zone_of_device_ops ops = { 64 .get_temp = tango_get_temp, 65}; 66 67static void tango_thermal_init(struct tango_thermal_priv *priv) 68{ 69 writel(0, priv->base + TEMPSI_CFG); 70 writel(CMD_ON, priv->base + TEMPSI_CMD); 71} 72 73static int tango_thermal_probe(struct platform_device *pdev) 74{ 75 struct resource *res; 76 struct tango_thermal_priv *priv; 77 struct thermal_zone_device *tzdev; 78 79 priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL); 80 if (!priv) 81 return -ENOMEM; 82 83 res = platform_get_resource(pdev, IORESOURCE_MEM, 0); 84 priv->base = devm_ioremap_resource(&pdev->dev, res); 85 if (IS_ERR(priv->base)) 86 return PTR_ERR(priv->base); 87 88 platform_set_drvdata(pdev, priv); 89 priv->thresh_idx = IDX_MIN; 90 tango_thermal_init(priv); 91 92 tzdev = devm_thermal_zone_of_sensor_register(&pdev->dev, 0, priv, &ops); 93 return PTR_ERR_OR_ZERO(tzdev); 94} 95 96static int __maybe_unused tango_thermal_resume(struct device *dev) 97{ 98 tango_thermal_init(dev_get_drvdata(dev)); 99 return 0; 100} 101 102static SIMPLE_DEV_PM_OPS(tango_thermal_pm, NULL, tango_thermal_resume); 103 104static const struct of_device_id tango_sensor_ids[] = { 105 { 106 .compatible = "sigma,smp8758-thermal", 107 }, 108 { /* sentinel */ } 109}; 110MODULE_DEVICE_TABLE(of, tango_sensor_ids); 111 112static struct platform_driver tango_thermal_driver = { 113 .probe = tango_thermal_probe, 114 .driver = { 115 .name = "tango-thermal", 116 .of_match_table = tango_sensor_ids, 117 .pm = &tango_thermal_pm, 118 }, 119}; 120 121module_platform_driver(tango_thermal_driver); 122 123MODULE_LICENSE("GPL"); 124MODULE_AUTHOR("Sigma Designs"); 125MODULE_DESCRIPTION("Tango temperature sensor");