brintos

brintos / linux-shallow public Read only

0
0
Text · 7.3 KiB · 7d61493 Raw
282 lines · c
1// SPDX-License-Identifier: GPL-2.0+2/*3 * Driver for Broadcom BCM2835 SoC temperature sensor4 *5 * Copyright (C) 2016 Martin Sperl6 */7 8#include <linux/clk.h>9#include <linux/debugfs.h>10#include <linux/device.h>11#include <linux/err.h>12#include <linux/io.h>13#include <linux/kernel.h>14#include <linux/module.h>15#include <linux/of.h>16#include <linux/of_address.h>17#include <linux/of_device.h>18#include <linux/platform_device.h>19#include <linux/thermal.h>20 21#include "../thermal_hwmon.h"22 23#define BCM2835_TS_TSENSCTL			0x0024#define BCM2835_TS_TSENSSTAT			0x0425 26#define BCM2835_TS_TSENSCTL_PRWDW		BIT(0)27#define BCM2835_TS_TSENSCTL_RSTB		BIT(1)28 29/*30 * bandgap reference voltage in 6 mV increments31 * 000b = 1178 mV, 001b = 1184 mV, ... 111b = 1220 mV32 */33#define BCM2835_TS_TSENSCTL_CTRL_BITS		334#define BCM2835_TS_TSENSCTL_CTRL_SHIFT		235#define BCM2835_TS_TSENSCTL_CTRL_MASK		    \36	GENMASK(BCM2835_TS_TSENSCTL_CTRL_BITS +     \37		BCM2835_TS_TSENSCTL_CTRL_SHIFT - 1, \38		BCM2835_TS_TSENSCTL_CTRL_SHIFT)39#define BCM2835_TS_TSENSCTL_CTRL_DEFAULT	140#define BCM2835_TS_TSENSCTL_EN_INT		BIT(5)41#define BCM2835_TS_TSENSCTL_DIRECT		BIT(6)42#define BCM2835_TS_TSENSCTL_CLR_INT		BIT(7)43#define BCM2835_TS_TSENSCTL_THOLD_SHIFT		844#define BCM2835_TS_TSENSCTL_THOLD_BITS		1045#define BCM2835_TS_TSENSCTL_THOLD_MASK		     \46	GENMASK(BCM2835_TS_TSENSCTL_THOLD_BITS +     \47		BCM2835_TS_TSENSCTL_THOLD_SHIFT - 1, \48		BCM2835_TS_TSENSCTL_THOLD_SHIFT)49/*50 * time how long the block to be asserted in reset51 * which based on a clock counter (TSENS clock assumed)52 */53#define BCM2835_TS_TSENSCTL_RSTDELAY_SHIFT	1854#define BCM2835_TS_TSENSCTL_RSTDELAY_BITS	855#define BCM2835_TS_TSENSCTL_REGULEN		BIT(26)56 57#define BCM2835_TS_TSENSSTAT_DATA_BITS		1058#define BCM2835_TS_TSENSSTAT_DATA_SHIFT		059#define BCM2835_TS_TSENSSTAT_DATA_MASK		     \60	GENMASK(BCM2835_TS_TSENSSTAT_DATA_BITS +     \61		BCM2835_TS_TSENSSTAT_DATA_SHIFT - 1, \62		BCM2835_TS_TSENSSTAT_DATA_SHIFT)63#define BCM2835_TS_TSENSSTAT_VALID		BIT(10)64#define BCM2835_TS_TSENSSTAT_INTERRUPT		BIT(11)65 66struct bcm2835_thermal_data {67	struct thermal_zone_device *tz;68	void __iomem *regs;69	struct clk *clk;70	struct dentry *debugfsdir;71};72 73static int bcm2835_thermal_adc2temp(u32 adc, int offset, int slope)74{75	return offset + slope * adc;76}77 78static int bcm2835_thermal_temp2adc(int temp, int offset, int slope)79{80	temp -= offset;81	temp /= slope;82 83	if (temp < 0)84		temp = 0;85	if (temp >= BIT(BCM2835_TS_TSENSSTAT_DATA_BITS))86		temp = BIT(BCM2835_TS_TSENSSTAT_DATA_BITS) - 1;87 88	return temp;89}90 91static int bcm2835_thermal_get_temp(struct thermal_zone_device *tz, int *temp)92{93	struct bcm2835_thermal_data *data = thermal_zone_device_priv(tz);94	u32 val = readl(data->regs + BCM2835_TS_TSENSSTAT);95 96	if (!(val & BCM2835_TS_TSENSSTAT_VALID))97		return -EIO;98 99	val &= BCM2835_TS_TSENSSTAT_DATA_MASK;100 101	*temp = bcm2835_thermal_adc2temp(102		val,103		thermal_zone_get_offset(data->tz),104		thermal_zone_get_slope(data->tz));105 106	return 0;107}108 109static const struct debugfs_reg32 bcm2835_thermal_regs[] = {110	{111		.name = "ctl",112		.offset = 0113	},114	{115		.name = "stat",116		.offset = 4117	}118};119 120static void bcm2835_thermal_debugfs(struct platform_device *pdev)121{122	struct bcm2835_thermal_data *data = platform_get_drvdata(pdev);123	struct debugfs_regset32 *regset;124 125	data->debugfsdir = debugfs_create_dir("bcm2835_thermal", NULL);126 127	regset = devm_kzalloc(&pdev->dev, sizeof(*regset), GFP_KERNEL);128	if (!regset)129		return;130 131	regset->regs = bcm2835_thermal_regs;132	regset->nregs = ARRAY_SIZE(bcm2835_thermal_regs);133	regset->base = data->regs;134 135	debugfs_create_regset32("regset", 0444, data->debugfsdir, regset);136}137 138static const struct thermal_zone_device_ops bcm2835_thermal_ops = {139	.get_temp = bcm2835_thermal_get_temp,140};141 142/*143 * Note: as per Raspberry Foundation FAQ144 * (https://www.raspberrypi.org/help/faqs/#performanceOperatingTemperature)145 * the recommended temperature range for the SoC -40C to +85C146 * so the trip limit is set to 80C.147 * this applies to all the BCM283X SoC148 */149 150static const struct of_device_id bcm2835_thermal_of_match_table[] = {151	{152		.compatible = "brcm,bcm2835-thermal",153	},154	{155		.compatible = "brcm,bcm2836-thermal",156	},157	{158		.compatible = "brcm,bcm2837-thermal",159	},160	{},161};162MODULE_DEVICE_TABLE(of, bcm2835_thermal_of_match_table);163 164static int bcm2835_thermal_probe(struct platform_device *pdev)165{166	struct device *dev = &pdev->dev;167	const struct of_device_id *match;168	struct thermal_zone_device *tz;169	struct bcm2835_thermal_data *data;170	int err = 0;171	u32 val;172	unsigned long rate;173 174	data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL);175	if (!data)176		return -ENOMEM;177 178	match = of_match_device(bcm2835_thermal_of_match_table, dev);179	if (!match)180		return -EINVAL;181 182	data->regs = devm_platform_get_and_ioremap_resource(pdev, 0, NULL);183	if (IS_ERR(data->regs)) {184		err = PTR_ERR(data->regs);185		return err;186	}187 188	data->clk = devm_clk_get_enabled(dev, NULL);189	if (IS_ERR(data->clk))190		return dev_err_probe(dev, PTR_ERR(data->clk), "Could not get clk\n");191 192	rate = clk_get_rate(data->clk);193	if ((rate < 1920000) || (rate > 5000000))194		dev_warn(dev,195			 "Clock %pCn running at %lu Hz is outside of the recommended range: 1.92 to 5MHz\n",196			 data->clk, rate);197 198	/* register of thermal sensor and get info from DT */199	tz = devm_thermal_of_zone_register(dev, 0, data, &bcm2835_thermal_ops);200	if (IS_ERR(tz))201		return dev_err_probe(dev, PTR_ERR(tz), "Failed to register the thermal device\n");202 203	/*204	 * right now the FW does set up the HW-block, so we are not205	 * touching the configuration registers.206	 * But if the HW is not enabled, then set it up207	 * using "sane" values used by the firmware right now.208	 */209	val = readl(data->regs + BCM2835_TS_TSENSCTL);210	if (!(val & BCM2835_TS_TSENSCTL_RSTB)) {211		int offset, slope, crit_temp;212 213		slope = thermal_zone_get_slope(tz);214		offset = thermal_zone_get_offset(tz);215		/*216		 * For now we deal only with critical, otherwise217		 * would need to iterate218		 */219		err = thermal_zone_get_crit_temp(tz, &crit_temp);220		if (err < 0) {221			dev_err(dev, "Not able to read trip_temp: %d\n", err);222			return err;223		}224 225		/* set bandgap reference voltage and enable voltage regulator */226		val = (BCM2835_TS_TSENSCTL_CTRL_DEFAULT <<227		       BCM2835_TS_TSENSCTL_CTRL_SHIFT) |228		      BCM2835_TS_TSENSCTL_REGULEN;229 230		/* use the recommended reset duration */231		val |= (0xFE << BCM2835_TS_TSENSCTL_RSTDELAY_SHIFT);232 233		/*  trip_adc value from info */234		val |= bcm2835_thermal_temp2adc(crit_temp,235						offset,236						slope)237			<< BCM2835_TS_TSENSCTL_THOLD_SHIFT;238 239		/* write the value back to the register as 2 steps */240		writel(val, data->regs + BCM2835_TS_TSENSCTL);241		val |= BCM2835_TS_TSENSCTL_RSTB;242		writel(val, data->regs + BCM2835_TS_TSENSCTL);243	}244 245	data->tz = tz;246 247	platform_set_drvdata(pdev, data);248 249	/*250	 * Thermal_zone doesn't enable hwmon as default,251	 * enable it here252	 */253	err = thermal_add_hwmon_sysfs(tz);254	if (err)255		return err;256 257	bcm2835_thermal_debugfs(pdev);258 259	return 0;260}261 262static void bcm2835_thermal_remove(struct platform_device *pdev)263{264	struct bcm2835_thermal_data *data = platform_get_drvdata(pdev);265 266	debugfs_remove_recursive(data->debugfsdir);267}268 269static struct platform_driver bcm2835_thermal_driver = {270	.probe = bcm2835_thermal_probe,271	.remove_new = bcm2835_thermal_remove,272	.driver = {273		.name = "bcm2835_thermal",274		.of_match_table = bcm2835_thermal_of_match_table,275	},276};277module_platform_driver(bcm2835_thermal_driver);278 279MODULE_AUTHOR("Martin Sperl");280MODULE_DESCRIPTION("Thermal driver for bcm2835 chip");281MODULE_LICENSE("GPL");282