brintos

brintos / linux-shallow public Read only

0
0
Text · 9.8 KiB · 52e26be Raw
400 lines · c
1// SPDX-License-Identifier: GPL-2.02//3// Copyright 2016 Freescale Semiconductor, Inc.4 5#include <linux/clk.h>6#include <linux/err.h>7#include <linux/io.h>8#include <linux/module.h>9#include <linux/of.h>10#include <linux/platform_device.h>11#include <linux/regmap.h>12#include <linux/sizes.h>13#include <linux/thermal.h>14#include <linux/units.h>15 16#include "thermal_hwmon.h"17 18#define SITES_MAX		1619#define TMR_DISABLE		0x020#define TMR_ME			0x8000000021#define TMR_ALPF		0x0c00000022#define TMR_ALPF_V2		0x0300000023#define TMTMIR_DEFAULT	0x0000000f24#define TIER_DISABLE	0x025#define TEUMR0_V2		0x51009c0026#define TMSARA_V2		0xe27#define TMU_VER1		0x128#define TMU_VER2		0x229 30#define REGS_TMR	0x000	/* Mode Register */31#define TMR_DISABLE	0x032#define TMR_ME		0x8000000033#define TMR_ALPF	0x0c00000034 35#define REGS_TMTMIR	0x008	/* Temperature measurement interval Register */36#define TMTMIR_DEFAULT	0x0000000f37 38#define REGS_V2_TMSR	0x008	/* monitor site register */39 40#define REGS_V2_TMTMIR	0x00c	/* Temperature measurement interval Register */41 42#define REGS_TIER	0x020	/* Interrupt Enable Register */43#define TIER_DISABLE	0x044 45 46#define REGS_TTCFGR	0x080	/* Temperature Configuration Register */47#define REGS_TSCFGR	0x084	/* Sensor Configuration Register */48 49#define REGS_TRITSR(n)	(0x100 + 16 * (n)) /* Immediate Temperature50					    * Site Register51					    */52#define TRITSR_V	BIT(31)53#define TRITSR_TP5	BIT(9)54#define REGS_V2_TMSAR(n)	(0x304 + 16 * (n))	/* TMU monitoring55						* site adjustment register56						*/57#define REGS_TTRnCR(n)	(0xf10 + 4 * (n)) /* Temperature Range n58					   * Control Register59					   */60#define NUM_TTRCR_V1	461#define NUM_TTRCR_MAX	1662 63#define REGS_IPBRR(n)		(0xbf8 + 4 * (n)) /* IP Block Revision64						   * Register n65						   */66#define REGS_V2_TEUMR(n)	(0xf00 + 4 * (n))67 68/*69 * Thermal zone data70 */71struct qoriq_sensor {72	int				id;73};74 75struct qoriq_tmu_data {76	int ver;77	u32 ttrcr[NUM_TTRCR_MAX];78	struct regmap *regmap;79	struct clk *clk;80	struct qoriq_sensor	sensor[SITES_MAX];81};82 83static struct qoriq_tmu_data *qoriq_sensor_to_data(struct qoriq_sensor *s)84{85	return container_of(s, struct qoriq_tmu_data, sensor[s->id]);86}87 88static int tmu_get_temp(struct thermal_zone_device *tz, int *temp)89{90	struct qoriq_sensor *qsensor = thermal_zone_device_priv(tz);91	struct qoriq_tmu_data *qdata = qoriq_sensor_to_data(qsensor);92	u32 val;93	/*94	 * REGS_TRITSR(id) has the following layout:95	 *96	 * For TMU Rev1:97	 * 31  ... 7 6 5 4 3 2 1 098	 *  V          TEMP99	 *100	 * Where V bit signifies if the measurement is ready and is101	 * within sensor range. TEMP is an 8 bit value representing102	 * temperature in Celsius.103 104	 * For TMU Rev2:105	 * 31  ... 8 7 6 5 4 3 2 1 0106	 *  V          TEMP107	 *108	 * Where V bit signifies if the measurement is ready and is109	 * within sensor range. TEMP is an 9 bit value representing110	 * temperature in KelVin.111	 */112 113	regmap_read(qdata->regmap, REGS_TMR, &val);114	if (!(val & TMR_ME))115		return -EAGAIN;116 117	if (regmap_read_poll_timeout(qdata->regmap,118				     REGS_TRITSR(qsensor->id),119				     val,120				     val & TRITSR_V,121				     USEC_PER_MSEC,122				     10 * USEC_PER_MSEC))123		return -ENODATA;124 125	if (qdata->ver == TMU_VER1) {126		*temp = (val & GENMASK(7, 0)) * MILLIDEGREE_PER_DEGREE;127	} else {128		if (val & TRITSR_TP5)129			*temp = milli_kelvin_to_millicelsius((val & GENMASK(8, 0)) *130							     MILLIDEGREE_PER_DEGREE + 500);131		else132			*temp = kelvin_to_millicelsius(val & GENMASK(8, 0));133	}134 135	return 0;136}137 138static const struct thermal_zone_device_ops tmu_tz_ops = {139	.get_temp = tmu_get_temp,140};141 142static int qoriq_tmu_register_tmu_zone(struct device *dev,143				       struct qoriq_tmu_data *qdata)144{145	int id, sites = 0;146 147	for (id = 0; id < SITES_MAX; id++) {148		struct thermal_zone_device *tzd;149		struct qoriq_sensor *sensor = &qdata->sensor[id];150		int ret;151 152		sensor->id = id;153 154		tzd = devm_thermal_of_zone_register(dev, id,155						    sensor,156						    &tmu_tz_ops);157		ret = PTR_ERR_OR_ZERO(tzd);158		if (ret) {159			if (ret == -ENODEV)160				continue;161 162			return ret;163		}164 165		if (qdata->ver == TMU_VER1)166			sites |= 0x1 << (15 - id);167		else168			sites |= 0x1 << id;169 170		devm_thermal_add_hwmon_sysfs(dev, tzd);171	}172 173	if (sites) {174		if (qdata->ver == TMU_VER1) {175			regmap_write(qdata->regmap, REGS_TMR, TMR_ME | TMR_ALPF | sites);176		} else {177			regmap_write(qdata->regmap, REGS_V2_TMSR, sites);178			regmap_write(qdata->regmap, REGS_TMR, TMR_ME | TMR_ALPF_V2);179		}180	}181 182	return 0;183}184 185static int qoriq_tmu_calibration(struct device *dev,186				 struct qoriq_tmu_data *data)187{188	int i, val, len;189	const u32 *calibration;190	struct device_node *np = dev->of_node;191 192	len = of_property_count_u32_elems(np, "fsl,tmu-range");193	if (len < 0 || (data->ver == TMU_VER1 && len > NUM_TTRCR_V1) ||194	    (data->ver > TMU_VER1 && len > NUM_TTRCR_MAX)) {195		dev_err(dev, "invalid range data.\n");196		return len;197	}198 199	val = of_property_read_u32_array(np, "fsl,tmu-range", data->ttrcr, len);200	if (val != 0) {201		dev_err(dev, "failed to read range data.\n");202		return val;203	}204 205	/* Init temperature range registers */206	for (i = 0; i < len; i++)207		regmap_write(data->regmap, REGS_TTRnCR(i), data->ttrcr[i]);208 209	calibration = of_get_property(np, "fsl,tmu-calibration", &len);210	if (calibration == NULL || len % 8) {211		dev_err(dev, "invalid calibration data.\n");212		return -ENODEV;213	}214 215	for (i = 0; i < len; i += 8, calibration += 2) {216		val = of_read_number(calibration, 1);217		regmap_write(data->regmap, REGS_TTCFGR, val);218		val = of_read_number(calibration + 1, 1);219		regmap_write(data->regmap, REGS_TSCFGR, val);220	}221 222	return 0;223}224 225static void qoriq_tmu_init_device(struct qoriq_tmu_data *data)226{227	/* Disable interrupt, using polling instead */228	regmap_write(data->regmap, REGS_TIER, TIER_DISABLE);229 230	/* Set update_interval */231 232	if (data->ver == TMU_VER1) {233		regmap_write(data->regmap, REGS_TMTMIR, TMTMIR_DEFAULT);234	} else {235		regmap_write(data->regmap, REGS_V2_TMTMIR, TMTMIR_DEFAULT);236		regmap_write(data->regmap, REGS_V2_TEUMR(0), TEUMR0_V2);237	}238 239	/* Disable monitoring */240	regmap_write(data->regmap, REGS_TMR, TMR_DISABLE);241}242 243static const struct regmap_range qoriq_yes_ranges[] = {244	regmap_reg_range(REGS_TMR, REGS_TSCFGR),245	regmap_reg_range(REGS_TTRnCR(0), REGS_TTRnCR(15)),246	regmap_reg_range(REGS_V2_TEUMR(0), REGS_V2_TEUMR(2)),247	regmap_reg_range(REGS_V2_TMSAR(0), REGS_V2_TMSAR(15)),248	regmap_reg_range(REGS_IPBRR(0), REGS_IPBRR(1)),249	/* Read only registers below */250	regmap_reg_range(REGS_TRITSR(0), REGS_TRITSR(15)),251};252 253static const struct regmap_access_table qoriq_wr_table = {254	.yes_ranges	= qoriq_yes_ranges,255	.n_yes_ranges	= ARRAY_SIZE(qoriq_yes_ranges) - 1,256};257 258static const struct regmap_access_table qoriq_rd_table = {259	.yes_ranges	= qoriq_yes_ranges,260	.n_yes_ranges	= ARRAY_SIZE(qoriq_yes_ranges),261};262 263static void qoriq_tmu_action(void *p)264{265	struct qoriq_tmu_data *data = p;266 267	regmap_write(data->regmap, REGS_TMR, TMR_DISABLE);268	clk_disable_unprepare(data->clk);269}270 271static int qoriq_tmu_probe(struct platform_device *pdev)272{273	int ret;274	u32 ver;275	struct qoriq_tmu_data *data;276	struct device_node *np = pdev->dev.of_node;277	struct device *dev = &pdev->dev;278	const bool little_endian = of_property_read_bool(np, "little-endian");279	const enum regmap_endian format_endian =280		little_endian ? REGMAP_ENDIAN_LITTLE : REGMAP_ENDIAN_BIG;281	const struct regmap_config regmap_config = {282		.reg_bits		= 32,283		.val_bits		= 32,284		.reg_stride		= 4,285		.rd_table		= &qoriq_rd_table,286		.wr_table		= &qoriq_wr_table,287		.val_format_endian	= format_endian,288		.max_register		= SZ_4K,289	};290	void __iomem *base;291 292	data = devm_kzalloc(dev, sizeof(struct qoriq_tmu_data),293			    GFP_KERNEL);294	if (!data)295		return -ENOMEM;296 297	base = devm_platform_ioremap_resource(pdev, 0);298	ret = PTR_ERR_OR_ZERO(base);299	if (ret) {300		dev_err(dev, "Failed to get memory region\n");301		return ret;302	}303 304	data->regmap = devm_regmap_init_mmio(dev, base, &regmap_config);305	ret = PTR_ERR_OR_ZERO(data->regmap);306	if (ret) {307		dev_err(dev, "Failed to init regmap (%d)\n", ret);308		return ret;309	}310 311	data->clk = devm_clk_get_optional(dev, NULL);312	if (IS_ERR(data->clk))313		return PTR_ERR(data->clk);314 315	ret = clk_prepare_enable(data->clk);316	if (ret) {317		dev_err(dev, "Failed to enable clock\n");318		return ret;319	}320 321	ret = devm_add_action_or_reset(dev, qoriq_tmu_action, data);322	if (ret)323		return ret;324 325	/* version register offset at: 0xbf8 on both v1 and v2 */326	ret = regmap_read(data->regmap, REGS_IPBRR(0), &ver);327	if (ret) {328		dev_err(&pdev->dev, "Failed to read IP block version\n");329		return ret;330	}331	data->ver = (ver >> 8) & 0xff;332 333	qoriq_tmu_init_device(data);	/* TMU initialization */334 335	ret = qoriq_tmu_calibration(dev, data);	/* TMU calibration */336	if (ret < 0)337		return ret;338 339	ret = qoriq_tmu_register_tmu_zone(dev, data);340	if (ret < 0) {341		dev_err(dev, "Failed to register sensors\n");342		return ret;343	}344 345	platform_set_drvdata(pdev, data);346 347	return 0;348}349 350static int qoriq_tmu_suspend(struct device *dev)351{352	struct qoriq_tmu_data *data = dev_get_drvdata(dev);353	int ret;354 355	ret = regmap_update_bits(data->regmap, REGS_TMR, TMR_ME, 0);356	if (ret)357		return ret;358 359	clk_disable_unprepare(data->clk);360 361	return 0;362}363 364static int qoriq_tmu_resume(struct device *dev)365{366	int ret;367	struct qoriq_tmu_data *data = dev_get_drvdata(dev);368 369	ret = clk_prepare_enable(data->clk);370	if (ret)371		return ret;372 373	/* Enable monitoring */374	return regmap_update_bits(data->regmap, REGS_TMR, TMR_ME, TMR_ME);375}376 377static DEFINE_SIMPLE_DEV_PM_OPS(qoriq_tmu_pm_ops,378				qoriq_tmu_suspend, qoriq_tmu_resume);379 380static const struct of_device_id qoriq_tmu_match[] = {381	{ .compatible = "fsl,qoriq-tmu", },382	{ .compatible = "fsl,imx8mq-tmu", },383	{},384};385MODULE_DEVICE_TABLE(of, qoriq_tmu_match);386 387static struct platform_driver qoriq_tmu = {388	.driver	= {389		.name		= "qoriq_thermal",390		.pm		= pm_sleep_ptr(&qoriq_tmu_pm_ops),391		.of_match_table	= qoriq_tmu_match,392	},393	.probe	= qoriq_tmu_probe,394};395module_platform_driver(qoriq_tmu);396 397MODULE_AUTHOR("Jia Hongtao <hongtao.jia@nxp.com>");398MODULE_DESCRIPTION("QorIQ Thermal Monitoring Unit driver");399MODULE_LICENSE("GPL v2");400