brintos

brintos / linux-shallow public Read only

0
0
Text · 25.9 KiB · fdcb077 Raw
985 lines · c
1// SPDX-License-Identifier: GPL-2.0-only2/*3 * Marvell EBU Armada SoCs thermal sensor driver4 *5 * Copyright (C) 2013 Marvell6 */7#include <linux/device.h>8#include <linux/err.h>9#include <linux/io.h>10#include <linux/kernel.h>11#include <linux/of.h>12#include <linux/module.h>13#include <linux/delay.h>14#include <linux/platform_device.h>15#include <linux/of_device.h>16#include <linux/thermal.h>17#include <linux/iopoll.h>18#include <linux/mfd/syscon.h>19#include <linux/regmap.h>20#include <linux/interrupt.h>21 22/* Thermal Manager Control and Status Register */23#define PMU_TDC0_SW_RST_MASK		(0x1 << 1)24#define PMU_TM_DISABLE_OFFS		025#define PMU_TM_DISABLE_MASK		(0x1 << PMU_TM_DISABLE_OFFS)26#define PMU_TDC0_REF_CAL_CNT_OFFS	1127#define PMU_TDC0_REF_CAL_CNT_MASK	(0x1ff << PMU_TDC0_REF_CAL_CNT_OFFS)28#define PMU_TDC0_OTF_CAL_MASK		(0x1 << 30)29#define PMU_TDC0_START_CAL_MASK		(0x1 << 25)30 31#define A375_UNIT_CONTROL_SHIFT		2732#define A375_UNIT_CONTROL_MASK		0x733#define A375_READOUT_INVERT		BIT(15)34#define A375_HW_RESETn			BIT(8)35 36/* Errata fields */37#define CONTROL0_TSEN_TC_TRIM_MASK	0x738#define CONTROL0_TSEN_TC_TRIM_VAL	0x339 40#define CONTROL0_TSEN_START		BIT(0)41#define CONTROL0_TSEN_RESET		BIT(1)42#define CONTROL0_TSEN_ENABLE		BIT(2)43#define CONTROL0_TSEN_AVG_BYPASS	BIT(6)44#define CONTROL0_TSEN_CHAN_SHIFT	1345#define CONTROL0_TSEN_CHAN_MASK		0xF46#define CONTROL0_TSEN_OSR_SHIFT		2447#define CONTROL0_TSEN_OSR_MAX		0x348#define CONTROL0_TSEN_MODE_SHIFT	3049#define CONTROL0_TSEN_MODE_EXTERNAL	0x250#define CONTROL0_TSEN_MODE_MASK		0x351 52#define CONTROL1_TSEN_AVG_MASK		0x753#define CONTROL1_EXT_TSEN_SW_RESET	BIT(7)54#define CONTROL1_EXT_TSEN_HW_RESETn	BIT(8)55#define CONTROL1_TSEN_INT_EN		BIT(25)56#define CONTROL1_TSEN_SELECT_OFF	2157#define CONTROL1_TSEN_SELECT_MASK	0x358 59#define STATUS_POLL_PERIOD_US		100060#define STATUS_POLL_TIMEOUT_US		10000061#define OVERHEAT_INT_POLL_DELAY_MS	100062 63struct armada_thermal_data;64 65/* Marvell EBU Thermal Sensor Dev Structure */66struct armada_thermal_priv {67	struct device *dev;68	struct regmap *syscon;69	char zone_name[THERMAL_NAME_LENGTH];70	/* serialize temperature reads/updates */71	struct mutex update_lock;72	struct armada_thermal_data *data;73	struct thermal_zone_device *overheat_sensor;74	int interrupt_source;75	int current_channel;76	long current_threshold;77	long current_hysteresis;78};79 80struct armada_thermal_data {81	/* Initialize the thermal IC */82	void (*init)(struct platform_device *pdev,83		     struct armada_thermal_priv *priv);84 85	/* Formula coeficients: temp = (b - m * reg) / div */86	s64 coef_b;87	s64 coef_m;88	u32 coef_div;89	bool inverted;90	bool signed_sample;91 92	/* Register shift and mask to access the sensor temperature */93	unsigned int temp_shift;94	unsigned int temp_mask;95	unsigned int thresh_shift;96	unsigned int hyst_shift;97	unsigned int hyst_mask;98	u32 is_valid_bit;99 100	/* Syscon access */101	unsigned int syscon_control0_off;102	unsigned int syscon_control1_off;103	unsigned int syscon_status_off;104	unsigned int dfx_irq_cause_off;105	unsigned int dfx_irq_mask_off;106	unsigned int dfx_overheat_irq;107	unsigned int dfx_server_irq_mask_off;108	unsigned int dfx_server_irq_en;109 110	/* One sensor is in the thermal IC, the others are in the CPUs if any */111	unsigned int cpu_nr;112};113 114struct armada_drvdata {115	enum drvtype {116		LEGACY,117		SYSCON118	} type;119	union {120		struct armada_thermal_priv *priv;121		struct thermal_zone_device *tz;122	} data;123};124 125/*126 * struct armada_thermal_sensor - hold the information of one thermal sensor127 * @thermal: pointer to the local private structure128 * @tzd: pointer to the thermal zone device129 * @id: identifier of the thermal sensor130 */131struct armada_thermal_sensor {132	struct armada_thermal_priv *priv;133	int id;134};135 136static void armadaxp_init(struct platform_device *pdev,137			  struct armada_thermal_priv *priv)138{139	struct armada_thermal_data *data = priv->data;140	u32 reg;141 142	regmap_read(priv->syscon, data->syscon_control1_off, &reg);143	reg |= PMU_TDC0_OTF_CAL_MASK;144 145	/* Reference calibration value */146	reg &= ~PMU_TDC0_REF_CAL_CNT_MASK;147	reg |= (0xf1 << PMU_TDC0_REF_CAL_CNT_OFFS);148 149	/* Reset the sensor */150	reg |= PMU_TDC0_SW_RST_MASK;151 152	regmap_write(priv->syscon, data->syscon_control1_off, reg);153 154	reg &= ~PMU_TDC0_SW_RST_MASK;155	regmap_write(priv->syscon, data->syscon_control1_off, reg);156 157	/* Enable the sensor */158	regmap_read(priv->syscon, data->syscon_status_off, &reg);159	reg &= ~PMU_TM_DISABLE_MASK;160	regmap_write(priv->syscon, data->syscon_status_off, reg);161}162 163static void armada370_init(struct platform_device *pdev,164			   struct armada_thermal_priv *priv)165{166	struct armada_thermal_data *data = priv->data;167	u32 reg;168 169	regmap_read(priv->syscon, data->syscon_control1_off, &reg);170	reg |= PMU_TDC0_OTF_CAL_MASK;171 172	/* Reference calibration value */173	reg &= ~PMU_TDC0_REF_CAL_CNT_MASK;174	reg |= (0xf1 << PMU_TDC0_REF_CAL_CNT_OFFS);175 176	/* Reset the sensor */177	reg &= ~PMU_TDC0_START_CAL_MASK;178 179	regmap_write(priv->syscon, data->syscon_control1_off, reg);180 181	msleep(10);182}183 184static void armada375_init(struct platform_device *pdev,185			   struct armada_thermal_priv *priv)186{187	struct armada_thermal_data *data = priv->data;188	u32 reg;189 190	regmap_read(priv->syscon, data->syscon_control1_off, &reg);191	reg &= ~(A375_UNIT_CONTROL_MASK << A375_UNIT_CONTROL_SHIFT);192	reg &= ~A375_READOUT_INVERT;193	reg &= ~A375_HW_RESETn;194	regmap_write(priv->syscon, data->syscon_control1_off, reg);195 196	msleep(20);197 198	reg |= A375_HW_RESETn;199	regmap_write(priv->syscon, data->syscon_control1_off, reg);200 201	msleep(50);202}203 204static int armada_wait_sensor_validity(struct armada_thermal_priv *priv)205{206	u32 reg;207 208	return regmap_read_poll_timeout(priv->syscon,209					priv->data->syscon_status_off, reg,210					reg & priv->data->is_valid_bit,211					STATUS_POLL_PERIOD_US,212					STATUS_POLL_TIMEOUT_US);213}214 215static void armada380_init(struct platform_device *pdev,216			   struct armada_thermal_priv *priv)217{218	struct armada_thermal_data *data = priv->data;219	u32 reg;220 221	/* Disable the HW/SW reset */222	regmap_read(priv->syscon, data->syscon_control1_off, &reg);223	reg |= CONTROL1_EXT_TSEN_HW_RESETn;224	reg &= ~CONTROL1_EXT_TSEN_SW_RESET;225	regmap_write(priv->syscon, data->syscon_control1_off, reg);226 227	/* Set Tsen Tc Trim to correct default value (errata #132698) */228	regmap_read(priv->syscon, data->syscon_control0_off, &reg);229	reg &= ~CONTROL0_TSEN_TC_TRIM_MASK;230	reg |= CONTROL0_TSEN_TC_TRIM_VAL;231	regmap_write(priv->syscon, data->syscon_control0_off, reg);232}233 234static void armada_ap80x_init(struct platform_device *pdev,235			      struct armada_thermal_priv *priv)236{237	struct armada_thermal_data *data = priv->data;238	u32 reg;239 240	regmap_read(priv->syscon, data->syscon_control0_off, &reg);241	reg &= ~CONTROL0_TSEN_RESET;242	reg |= CONTROL0_TSEN_START | CONTROL0_TSEN_ENABLE;243 244	/* Sample every ~2ms */245	reg |= CONTROL0_TSEN_OSR_MAX << CONTROL0_TSEN_OSR_SHIFT;246 247	/* Enable average (2 samples by default) */248	reg &= ~CONTROL0_TSEN_AVG_BYPASS;249 250	regmap_write(priv->syscon, data->syscon_control0_off, reg);251}252 253static void armada_cp110_init(struct platform_device *pdev,254			      struct armada_thermal_priv *priv)255{256	struct armada_thermal_data *data = priv->data;257	u32 reg;258 259	armada380_init(pdev, priv);260 261	/* Sample every ~2ms */262	regmap_read(priv->syscon, data->syscon_control0_off, &reg);263	reg |= CONTROL0_TSEN_OSR_MAX << CONTROL0_TSEN_OSR_SHIFT;264	regmap_write(priv->syscon, data->syscon_control0_off, reg);265 266	/* Average the output value over 2^1 = 2 samples */267	regmap_read(priv->syscon, data->syscon_control1_off, &reg);268	reg &= ~CONTROL1_TSEN_AVG_MASK;269	reg |= 1;270	regmap_write(priv->syscon, data->syscon_control1_off, reg);271}272 273static bool armada_is_valid(struct armada_thermal_priv *priv)274{275	u32 reg;276 277	if (!priv->data->is_valid_bit)278		return true;279 280	regmap_read(priv->syscon, priv->data->syscon_status_off, &reg);281 282	return reg & priv->data->is_valid_bit;283}284 285static void armada_enable_overheat_interrupt(struct armada_thermal_priv *priv)286{287	struct armada_thermal_data *data = priv->data;288	u32 reg;289 290	/* Clear DFX temperature IRQ cause */291	regmap_read(priv->syscon, data->dfx_irq_cause_off, &reg);292 293	/* Enable DFX Temperature IRQ */294	regmap_read(priv->syscon, data->dfx_irq_mask_off, &reg);295	reg |= data->dfx_overheat_irq;296	regmap_write(priv->syscon, data->dfx_irq_mask_off, reg);297 298	/* Enable DFX server IRQ */299	regmap_read(priv->syscon, data->dfx_server_irq_mask_off, &reg);300	reg |= data->dfx_server_irq_en;301	regmap_write(priv->syscon, data->dfx_server_irq_mask_off, reg);302 303	/* Enable overheat interrupt */304	regmap_read(priv->syscon, data->syscon_control1_off, &reg);305	reg |= CONTROL1_TSEN_INT_EN;306	regmap_write(priv->syscon, data->syscon_control1_off, reg);307}308 309static void __maybe_unused310armada_disable_overheat_interrupt(struct armada_thermal_priv *priv)311{312	struct armada_thermal_data *data = priv->data;313	u32 reg;314 315	regmap_read(priv->syscon, data->syscon_control1_off, &reg);316	reg &= ~CONTROL1_TSEN_INT_EN;317	regmap_write(priv->syscon, data->syscon_control1_off, reg);318}319 320/* There is currently no board with more than one sensor per channel */321static int armada_select_channel(struct armada_thermal_priv *priv, int channel)322{323	struct armada_thermal_data *data = priv->data;324	u32 ctrl0;325 326	if (channel < 0 || channel > priv->data->cpu_nr)327		return -EINVAL;328 329	if (priv->current_channel == channel)330		return 0;331 332	/* Stop the measurements */333	regmap_read(priv->syscon, data->syscon_control0_off, &ctrl0);334	ctrl0 &= ~CONTROL0_TSEN_START;335	regmap_write(priv->syscon, data->syscon_control0_off, ctrl0);336 337	/* Reset the mode, internal sensor will be automatically selected */338	ctrl0 &= ~(CONTROL0_TSEN_MODE_MASK << CONTROL0_TSEN_MODE_SHIFT);339 340	/* Other channels are external and should be selected accordingly */341	if (channel) {342		/* Change the mode to external */343		ctrl0 |= CONTROL0_TSEN_MODE_EXTERNAL <<344			 CONTROL0_TSEN_MODE_SHIFT;345		/* Select the sensor */346		ctrl0 &= ~(CONTROL0_TSEN_CHAN_MASK << CONTROL0_TSEN_CHAN_SHIFT);347		ctrl0 |= (channel - 1) << CONTROL0_TSEN_CHAN_SHIFT;348	}349 350	/* Actually set the mode/channel */351	regmap_write(priv->syscon, data->syscon_control0_off, ctrl0);352	priv->current_channel = channel;353 354	/* Re-start the measurements */355	ctrl0 |= CONTROL0_TSEN_START;356	regmap_write(priv->syscon, data->syscon_control0_off, ctrl0);357 358	/*359	 * The IP has a latency of ~15ms, so after updating the selected source,360	 * we must absolutely wait for the sensor validity bit to ensure we read361	 * actual data.362	 */363	if (armada_wait_sensor_validity(priv))364		return -EIO;365 366	return 0;367}368 369static int armada_read_sensor(struct armada_thermal_priv *priv, int *temp)370{371	u32 reg, div;372	s64 sample, b, m;373 374	regmap_read(priv->syscon, priv->data->syscon_status_off, &reg);375	reg = (reg >> priv->data->temp_shift) & priv->data->temp_mask;376	if (priv->data->signed_sample)377		/* The most significant bit is the sign bit */378		sample = sign_extend32(reg, fls(priv->data->temp_mask) - 1);379	else380		sample = reg;381 382	/* Get formula coeficients */383	b = priv->data->coef_b;384	m = priv->data->coef_m;385	div = priv->data->coef_div;386 387	if (priv->data->inverted)388		*temp = div_s64((m * sample) - b, div);389	else390		*temp = div_s64(b - (m * sample), div);391 392	return 0;393}394 395static int armada_get_temp_legacy(struct thermal_zone_device *thermal,396				  int *temp)397{398	struct armada_thermal_priv *priv = thermal_zone_device_priv(thermal);399	int ret;400 401	/* Valid check */402	if (!armada_is_valid(priv))403		return -EIO;404 405	/* Do the actual reading */406	ret = armada_read_sensor(priv, temp);407 408	return ret;409}410 411static struct thermal_zone_device_ops legacy_ops = {412	.get_temp = armada_get_temp_legacy,413};414 415static int armada_get_temp(struct thermal_zone_device *tz, int *temp)416{417	struct armada_thermal_sensor *sensor = thermal_zone_device_priv(tz);418	struct armada_thermal_priv *priv = sensor->priv;419	int ret;420 421	mutex_lock(&priv->update_lock);422 423	/* Select the desired channel */424	ret = armada_select_channel(priv, sensor->id);425	if (ret)426		goto unlock_mutex;427 428	/* Do the actual reading */429	ret = armada_read_sensor(priv, temp);430	if (ret)431		goto unlock_mutex;432 433	/*434	 * Select back the interrupt source channel from which a potential435	 * critical trip point has been set.436	 */437	ret = armada_select_channel(priv, priv->interrupt_source);438 439unlock_mutex:440	mutex_unlock(&priv->update_lock);441 442	return ret;443}444 445static const struct thermal_zone_device_ops of_ops = {446	.get_temp = armada_get_temp,447};448 449static unsigned int armada_mc_to_reg_temp(struct armada_thermal_data *data,450					  unsigned int temp_mc)451{452	s64 b = data->coef_b;453	s64 m = data->coef_m;454	s64 div = data->coef_div;455	unsigned int sample;456 457	if (data->inverted)458		sample = div_s64(((temp_mc * div) + b), m);459	else460		sample = div_s64((b - (temp_mc * div)), m);461 462	return sample & data->temp_mask;463}464 465/*466 * The documentation states:467 * high/low watermark = threshold +/- 0.4761 * 2^(hysteresis + 2)468 * which is the mathematical derivation for:469 * 0x0 <=> 1.9°C, 0x1 <=> 3.8°C, 0x2 <=> 7.6°C, 0x3 <=> 15.2°C470 */471static unsigned int hyst_levels_mc[] = {1900, 3800, 7600, 15200};472 473static unsigned int armada_mc_to_reg_hyst(struct armada_thermal_data *data,474					  unsigned int hyst_mc)475{476	int i;477 478	/*479	 * We will always take the smallest possible hysteresis to avoid risking480	 * the hardware integrity by enlarging the threshold by +8°C in the481	 * worst case.482	 */483	for (i = ARRAY_SIZE(hyst_levels_mc) - 1; i > 0; i--)484		if (hyst_mc >= hyst_levels_mc[i])485			break;486 487	return i & data->hyst_mask;488}489 490static void armada_set_overheat_thresholds(struct armada_thermal_priv *priv,491					   int thresh_mc, int hyst_mc)492{493	struct armada_thermal_data *data = priv->data;494	unsigned int threshold = armada_mc_to_reg_temp(data, thresh_mc);495	unsigned int hysteresis = armada_mc_to_reg_hyst(data, hyst_mc);496	u32 ctrl1;497 498	regmap_read(priv->syscon, data->syscon_control1_off, &ctrl1);499 500	/* Set Threshold */501	if (thresh_mc >= 0) {502		ctrl1 &= ~(data->temp_mask << data->thresh_shift);503		ctrl1 |= threshold << data->thresh_shift;504		priv->current_threshold = thresh_mc;505	}506 507	/* Set Hysteresis */508	if (hyst_mc >= 0) {509		ctrl1 &= ~(data->hyst_mask << data->hyst_shift);510		ctrl1 |= hysteresis << data->hyst_shift;511		priv->current_hysteresis = hyst_mc;512	}513 514	regmap_write(priv->syscon, data->syscon_control1_off, ctrl1);515}516 517static irqreturn_t armada_overheat_isr(int irq, void *blob)518{519	/*520	 * Disable the IRQ and continue in thread context (thermal core521	 * notification and temperature monitoring).522	 */523	disable_irq_nosync(irq);524 525	return IRQ_WAKE_THREAD;526}527 528static irqreturn_t armada_overheat_isr_thread(int irq, void *blob)529{530	struct armada_thermal_priv *priv = blob;531	int low_threshold = priv->current_threshold - priv->current_hysteresis;532	int temperature;533	u32 dummy;534	int ret;535 536	/* Notify the core in thread context */537	thermal_zone_device_update(priv->overheat_sensor,538				   THERMAL_EVENT_UNSPECIFIED);539 540	/*541	 * The overheat interrupt must be cleared by reading the DFX interrupt542	 * cause _after_ the temperature has fallen down to the low threshold.543	 * Otherwise future interrupts might not be served.544	 */545	do {546		msleep(OVERHEAT_INT_POLL_DELAY_MS);547		mutex_lock(&priv->update_lock);548		ret = armada_read_sensor(priv, &temperature);549		mutex_unlock(&priv->update_lock);550		if (ret)551			goto enable_irq;552	} while (temperature >= low_threshold);553 554	regmap_read(priv->syscon, priv->data->dfx_irq_cause_off, &dummy);555 556	/* Notify the thermal core that the temperature is acceptable again */557	thermal_zone_device_update(priv->overheat_sensor,558				   THERMAL_EVENT_UNSPECIFIED);559 560enable_irq:561	enable_irq(irq);562 563	return IRQ_HANDLED;564}565 566static const struct armada_thermal_data armadaxp_data = {567	.init = armadaxp_init,568	.temp_shift = 10,569	.temp_mask = 0x1ff,570	.coef_b = 3153000000ULL,571	.coef_m = 10000000ULL,572	.coef_div = 13825,573	.syscon_status_off = 0xb0,574	.syscon_control1_off = 0x2d0,575};576 577static const struct armada_thermal_data armada370_data = {578	.init = armada370_init,579	.is_valid_bit = BIT(9),580	.temp_shift = 10,581	.temp_mask = 0x1ff,582	.coef_b = 3153000000ULL,583	.coef_m = 10000000ULL,584	.coef_div = 13825,585	.syscon_status_off = 0x0,586	.syscon_control1_off = 0x4,587};588 589static const struct armada_thermal_data armada375_data = {590	.init = armada375_init,591	.is_valid_bit = BIT(10),592	.temp_shift = 0,593	.temp_mask = 0x1ff,594	.coef_b = 3171900000ULL,595	.coef_m = 10000000ULL,596	.coef_div = 13616,597	.syscon_status_off = 0x78,598	.syscon_control0_off = 0x7c,599	.syscon_control1_off = 0x80,600};601 602static const struct armada_thermal_data armada380_data = {603	.init = armada380_init,604	.is_valid_bit = BIT(10),605	.temp_shift = 0,606	.temp_mask = 0x3ff,607	.coef_b = 1172499100ULL,608	.coef_m = 2000096ULL,609	.coef_div = 4201,610	.inverted = true,611	.syscon_control0_off = 0x70,612	.syscon_control1_off = 0x74,613	.syscon_status_off = 0x78,614};615 616static const struct armada_thermal_data armada_ap806_data = {617	.init = armada_ap80x_init,618	.is_valid_bit = BIT(16),619	.temp_shift = 0,620	.temp_mask = 0x3ff,621	.thresh_shift = 3,622	.hyst_shift = 19,623	.hyst_mask = 0x3,624	.coef_b = -150000LL,625	.coef_m = 423ULL,626	.coef_div = 1,627	.inverted = true,628	.signed_sample = true,629	.syscon_control0_off = 0x84,630	.syscon_control1_off = 0x88,631	.syscon_status_off = 0x8C,632	.dfx_irq_cause_off = 0x108,633	.dfx_irq_mask_off = 0x10C,634	.dfx_overheat_irq = BIT(22),635	.dfx_server_irq_mask_off = 0x104,636	.dfx_server_irq_en = BIT(1),637	.cpu_nr = 4,638};639 640static const struct armada_thermal_data armada_ap807_data = {641	.init = armada_ap80x_init,642	.is_valid_bit = BIT(16),643	.temp_shift = 0,644	.temp_mask = 0x3ff,645	.thresh_shift = 3,646	.hyst_shift = 19,647	.hyst_mask = 0x3,648	.coef_b = -128900LL,649	.coef_m = 394ULL,650	.coef_div = 1,651	.inverted = true,652	.signed_sample = true,653	.syscon_control0_off = 0x84,654	.syscon_control1_off = 0x88,655	.syscon_status_off = 0x8C,656	.dfx_irq_cause_off = 0x108,657	.dfx_irq_mask_off = 0x10C,658	.dfx_overheat_irq = BIT(22),659	.dfx_server_irq_mask_off = 0x104,660	.dfx_server_irq_en = BIT(1),661	.cpu_nr = 4,662};663 664static const struct armada_thermal_data armada_cp110_data = {665	.init = armada_cp110_init,666	.is_valid_bit = BIT(10),667	.temp_shift = 0,668	.temp_mask = 0x3ff,669	.thresh_shift = 16,670	.hyst_shift = 26,671	.hyst_mask = 0x3,672	.coef_b = 1172499100ULL,673	.coef_m = 2000096ULL,674	.coef_div = 4201,675	.inverted = true,676	.syscon_control0_off = 0x70,677	.syscon_control1_off = 0x74,678	.syscon_status_off = 0x78,679	.dfx_irq_cause_off = 0x108,680	.dfx_irq_mask_off = 0x10C,681	.dfx_overheat_irq = BIT(20),682	.dfx_server_irq_mask_off = 0x104,683	.dfx_server_irq_en = BIT(1),684};685 686static const struct of_device_id armada_thermal_id_table[] = {687	{688		.compatible = "marvell,armadaxp-thermal",689		.data       = &armadaxp_data,690	},691	{692		.compatible = "marvell,armada370-thermal",693		.data       = &armada370_data,694	},695	{696		.compatible = "marvell,armada375-thermal",697		.data       = &armada375_data,698	},699	{700		.compatible = "marvell,armada380-thermal",701		.data       = &armada380_data,702	},703	{704		.compatible = "marvell,armada-ap806-thermal",705		.data       = &armada_ap806_data,706	},707	{708		.compatible = "marvell,armada-ap807-thermal",709		.data       = &armada_ap807_data,710	},711	{712		.compatible = "marvell,armada-cp110-thermal",713		.data       = &armada_cp110_data,714	},715	{716		/* sentinel */717	},718};719MODULE_DEVICE_TABLE(of, armada_thermal_id_table);720 721static const struct regmap_config armada_thermal_regmap_config = {722	.reg_bits = 32,723	.reg_stride = 4,724	.val_bits = 32,725	.fast_io = true,726};727 728static int armada_thermal_probe_legacy(struct platform_device *pdev,729				       struct armada_thermal_priv *priv)730{731	struct armada_thermal_data *data = priv->data;732	void __iomem *base;733 734	/* First memory region points towards the status register */735	base = devm_platform_get_and_ioremap_resource(pdev, 0, NULL);736	if (IS_ERR(base))737		return PTR_ERR(base);738 739	/*740	 * Fix up from the old individual DT register specification to741	 * cover all the registers.  We do this by adjusting the ioremap()742	 * result, which should be fine as ioremap() deals with pages.743	 * However, validate that we do not cross a page boundary while744	 * making this adjustment.745	 */746	if (((unsigned long)base & ~PAGE_MASK) < data->syscon_status_off)747		return -EINVAL;748	base -= data->syscon_status_off;749 750	priv->syscon = devm_regmap_init_mmio(&pdev->dev, base,751					     &armada_thermal_regmap_config);752	return PTR_ERR_OR_ZERO(priv->syscon);753}754 755static int armada_thermal_probe_syscon(struct platform_device *pdev,756				       struct armada_thermal_priv *priv)757{758	priv->syscon = syscon_node_to_regmap(pdev->dev.parent->of_node);759	return PTR_ERR_OR_ZERO(priv->syscon);760}761 762static void armada_set_sane_name(struct platform_device *pdev,763				 struct armada_thermal_priv *priv)764{765	const char *name = dev_name(&pdev->dev);766 767	if (strlen(name) > THERMAL_NAME_LENGTH) {768		/*769		 * When inside a system controller, the device name has the770		 * form: f06f8000.system-controller:ap-thermal so stripping771		 * after the ':' should give us a shorter but meaningful name.772		 */773		name = strrchr(name, ':');774		if (!name)775			name = "armada_thermal";776		else777			name++;778	}779 780	/* Save the name locally */781	strscpy(priv->zone_name, name, THERMAL_NAME_LENGTH);782 783	/* Then ensure there are no '-' or hwmon core will complain */784	strreplace(priv->zone_name, '-', '_');785}786 787/*788 * The IP can manage to trigger interrupts on overheat situation from all the789 * sensors. However, the interrupt source changes along with the last selected790 * source (ie. the last read sensor), which is an inconsistent behavior. Avoid791 * possible glitches by always selecting back only one channel (arbitrarily: the792 * first in the DT which has a critical trip point). We also disable sensor793 * switch during overheat situations.794 */795static int armada_configure_overheat_int(struct armada_thermal_priv *priv,796					 struct thermal_zone_device *tz,797					 int sensor_id)798{799	/* Retrieve the critical trip point to enable the overheat interrupt */800	int temperature;801	int ret;802 803	ret = thermal_zone_get_crit_temp(tz, &temperature);804	if (ret)805		return ret;806 807	ret = armada_select_channel(priv, sensor_id);808	if (ret)809		return ret;810 811	/*812	 * A critical temperature does not have a hysteresis813	 */814	armada_set_overheat_thresholds(priv, temperature, 0);815	priv->overheat_sensor = tz;816	priv->interrupt_source = sensor_id;817	armada_enable_overheat_interrupt(priv);818 819	return 0;820}821 822static int armada_thermal_probe(struct platform_device *pdev)823{824	struct thermal_zone_device *tz;825	struct armada_thermal_sensor *sensor;826	struct armada_drvdata *drvdata;827	const struct of_device_id *match;828	struct armada_thermal_priv *priv;829	int sensor_id, irq;830	int ret;831 832	match = of_match_device(armada_thermal_id_table, &pdev->dev);833	if (!match)834		return -ENODEV;835 836	priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);837	if (!priv)838		return -ENOMEM;839 840	drvdata = devm_kzalloc(&pdev->dev, sizeof(*drvdata), GFP_KERNEL);841	if (!drvdata)842		return -ENOMEM;843 844	priv->dev = &pdev->dev;845	priv->data = (struct armada_thermal_data *)match->data;846 847	mutex_init(&priv->update_lock);848 849	/*850	 * Legacy DT bindings only described "control1" register (also referred851	 * as "control MSB" on old documentation). Then, bindings moved to cover852	 * "control0/control LSB" and "control1/control MSB" registers within853	 * the same resource, which was then of size 8 instead of 4.854	 *855	 * The logic of defining sporadic registers is broken. For instance, it856	 * blocked the addition of the overheat interrupt feature that needed857	 * another resource somewhere else in the same memory area. One solution858	 * is to define an overall system controller and put the thermal node859	 * into it, which requires the use of regmaps across all the driver.860	 */861	if (IS_ERR(syscon_node_to_regmap(pdev->dev.parent->of_node))) {862		/* Ensure device name is correct for the thermal core */863		armada_set_sane_name(pdev, priv);864 865		ret = armada_thermal_probe_legacy(pdev, priv);866		if (ret)867			return ret;868 869		priv->data->init(pdev, priv);870 871		/* Wait the sensors to be valid */872		armada_wait_sensor_validity(priv);873 874		tz = thermal_tripless_zone_device_register(priv->zone_name,875							   priv, &legacy_ops,876							   NULL);877		if (IS_ERR(tz)) {878			dev_err(&pdev->dev,879				"Failed to register thermal zone device\n");880			return PTR_ERR(tz);881		}882 883		ret = thermal_zone_device_enable(tz);884		if (ret) {885			thermal_zone_device_unregister(tz);886			return ret;887		}888 889		drvdata->type = LEGACY;890		drvdata->data.tz = tz;891		platform_set_drvdata(pdev, drvdata);892 893		return 0;894	}895 896	ret = armada_thermal_probe_syscon(pdev, priv);897	if (ret)898		return ret;899 900	priv->current_channel = -1;901	priv->data->init(pdev, priv);902	drvdata->type = SYSCON;903	drvdata->data.priv = priv;904	platform_set_drvdata(pdev, drvdata);905 906	irq = platform_get_irq(pdev, 0);907	if (irq == -EPROBE_DEFER)908		return irq;909 910	/* The overheat interrupt feature is not mandatory */911	if (irq > 0) {912		ret = devm_request_threaded_irq(&pdev->dev, irq,913						armada_overheat_isr,914						armada_overheat_isr_thread,915						0, NULL, priv);916		if (ret) {917			dev_err(&pdev->dev, "Cannot request threaded IRQ %d\n",918				irq);919			return ret;920		}921	}922 923	/*924	 * There is one channel for the IC and one per CPU (if any), each925	 * channel has one sensor.926	 */927	for (sensor_id = 0; sensor_id <= priv->data->cpu_nr; sensor_id++) {928		sensor = devm_kzalloc(&pdev->dev,929				      sizeof(struct armada_thermal_sensor),930				      GFP_KERNEL);931		if (!sensor)932			return -ENOMEM;933 934		/* Register the sensor */935		sensor->priv = priv;936		sensor->id = sensor_id;937		tz = devm_thermal_of_zone_register(&pdev->dev,938						   sensor->id, sensor,939						   &of_ops);940		if (IS_ERR(tz)) {941			dev_info(&pdev->dev, "Thermal sensor %d unavailable\n",942				 sensor_id);943			devm_kfree(&pdev->dev, sensor);944			continue;945		}946 947		/*948		 * The first channel that has a critical trip point registered949		 * in the DT will serve as interrupt source. Others possible950		 * critical trip points will simply be ignored by the driver.951		 */952		if (irq > 0 && !priv->overheat_sensor)953			armada_configure_overheat_int(priv, tz, sensor->id);954	}955 956	/* Just complain if no overheat interrupt was set up */957	if (!priv->overheat_sensor)958		dev_warn(&pdev->dev, "Overheat interrupt not available\n");959 960	return 0;961}962 963static void armada_thermal_exit(struct platform_device *pdev)964{965	struct armada_drvdata *drvdata = platform_get_drvdata(pdev);966 967	if (drvdata->type == LEGACY)968		thermal_zone_device_unregister(drvdata->data.tz);969}970 971static struct platform_driver armada_thermal_driver = {972	.probe = armada_thermal_probe,973	.remove_new = armada_thermal_exit,974	.driver = {975		.name = "armada_thermal",976		.of_match_table = armada_thermal_id_table,977	},978};979 980module_platform_driver(armada_thermal_driver);981 982MODULE_AUTHOR("Ezequiel Garcia <ezequiel.garcia@free-electrons.com>");983MODULE_DESCRIPTION("Marvell EBU Armada SoCs thermal driver");984MODULE_LICENSE("GPL v2");985