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, ®);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, ®);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, ®);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, ®);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, ®);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, ®);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, ®);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, ®);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, ®);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, ®);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, ®);292 293 /* Enable DFX Temperature IRQ */294 regmap_read(priv->syscon, data->dfx_irq_mask_off, ®);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, ®);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, ®);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, ®);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, ®);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