162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * R-Car THS/TSC thermal sensor driver 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright (C) 2012 Renesas Solutions Corp. 662306a36Sopenharmony_ci * Kuninori Morimoto <kuninori.morimoto.gx@renesas.com> 762306a36Sopenharmony_ci */ 862306a36Sopenharmony_ci#include <linux/delay.h> 962306a36Sopenharmony_ci#include <linux/err.h> 1062306a36Sopenharmony_ci#include <linux/irq.h> 1162306a36Sopenharmony_ci#include <linux/interrupt.h> 1262306a36Sopenharmony_ci#include <linux/io.h> 1362306a36Sopenharmony_ci#include <linux/module.h> 1462306a36Sopenharmony_ci#include <linux/of.h> 1562306a36Sopenharmony_ci#include <linux/platform_device.h> 1662306a36Sopenharmony_ci#include <linux/pm_runtime.h> 1762306a36Sopenharmony_ci#include <linux/reboot.h> 1862306a36Sopenharmony_ci#include <linux/slab.h> 1962306a36Sopenharmony_ci#include <linux/spinlock.h> 2062306a36Sopenharmony_ci#include <linux/thermal.h> 2162306a36Sopenharmony_ci 2262306a36Sopenharmony_ci#include "thermal_hwmon.h" 2362306a36Sopenharmony_ci 2462306a36Sopenharmony_ci#define IDLE_INTERVAL 5000 2562306a36Sopenharmony_ci 2662306a36Sopenharmony_ci#define COMMON_STR 0x00 2762306a36Sopenharmony_ci#define COMMON_ENR 0x04 2862306a36Sopenharmony_ci#define COMMON_INTMSK 0x0c 2962306a36Sopenharmony_ci 3062306a36Sopenharmony_ci#define REG_POSNEG 0x20 3162306a36Sopenharmony_ci#define REG_FILONOFF 0x28 3262306a36Sopenharmony_ci#define REG_THSCR 0x2c 3362306a36Sopenharmony_ci#define REG_THSSR 0x30 3462306a36Sopenharmony_ci#define REG_INTCTRL 0x34 3562306a36Sopenharmony_ci 3662306a36Sopenharmony_ci/* THSCR */ 3762306a36Sopenharmony_ci#define CPCTL (1 << 12) 3862306a36Sopenharmony_ci 3962306a36Sopenharmony_ci/* THSSR */ 4062306a36Sopenharmony_ci#define CTEMP 0x3f 4162306a36Sopenharmony_ci 4262306a36Sopenharmony_cistruct rcar_thermal_common { 4362306a36Sopenharmony_ci void __iomem *base; 4462306a36Sopenharmony_ci struct device *dev; 4562306a36Sopenharmony_ci struct list_head head; 4662306a36Sopenharmony_ci spinlock_t lock; 4762306a36Sopenharmony_ci}; 4862306a36Sopenharmony_ci 4962306a36Sopenharmony_cistruct rcar_thermal_chip { 5062306a36Sopenharmony_ci unsigned int use_of_thermal : 1; 5162306a36Sopenharmony_ci unsigned int has_filonoff : 1; 5262306a36Sopenharmony_ci unsigned int irq_per_ch : 1; 5362306a36Sopenharmony_ci unsigned int needs_suspend_resume : 1; 5462306a36Sopenharmony_ci unsigned int nirqs; 5562306a36Sopenharmony_ci unsigned int ctemp_bands; 5662306a36Sopenharmony_ci}; 5762306a36Sopenharmony_ci 5862306a36Sopenharmony_cistatic const struct rcar_thermal_chip rcar_thermal = { 5962306a36Sopenharmony_ci .use_of_thermal = 0, 6062306a36Sopenharmony_ci .has_filonoff = 1, 6162306a36Sopenharmony_ci .irq_per_ch = 0, 6262306a36Sopenharmony_ci .needs_suspend_resume = 0, 6362306a36Sopenharmony_ci .nirqs = 1, 6462306a36Sopenharmony_ci .ctemp_bands = 1, 6562306a36Sopenharmony_ci}; 6662306a36Sopenharmony_ci 6762306a36Sopenharmony_cistatic const struct rcar_thermal_chip rcar_gen2_thermal = { 6862306a36Sopenharmony_ci .use_of_thermal = 1, 6962306a36Sopenharmony_ci .has_filonoff = 1, 7062306a36Sopenharmony_ci .irq_per_ch = 0, 7162306a36Sopenharmony_ci .needs_suspend_resume = 0, 7262306a36Sopenharmony_ci .nirqs = 1, 7362306a36Sopenharmony_ci .ctemp_bands = 1, 7462306a36Sopenharmony_ci}; 7562306a36Sopenharmony_ci 7662306a36Sopenharmony_cistatic const struct rcar_thermal_chip rcar_gen3_thermal = { 7762306a36Sopenharmony_ci .use_of_thermal = 1, 7862306a36Sopenharmony_ci .has_filonoff = 0, 7962306a36Sopenharmony_ci .irq_per_ch = 1, 8062306a36Sopenharmony_ci .needs_suspend_resume = 1, 8162306a36Sopenharmony_ci /* 8262306a36Sopenharmony_ci * The Gen3 chip has 3 interrupts, but this driver uses only 2 8362306a36Sopenharmony_ci * interrupts to detect a temperature change, rise or fall. 8462306a36Sopenharmony_ci */ 8562306a36Sopenharmony_ci .nirqs = 2, 8662306a36Sopenharmony_ci .ctemp_bands = 2, 8762306a36Sopenharmony_ci}; 8862306a36Sopenharmony_ci 8962306a36Sopenharmony_cistruct rcar_thermal_priv { 9062306a36Sopenharmony_ci void __iomem *base; 9162306a36Sopenharmony_ci struct rcar_thermal_common *common; 9262306a36Sopenharmony_ci struct thermal_zone_device *zone; 9362306a36Sopenharmony_ci const struct rcar_thermal_chip *chip; 9462306a36Sopenharmony_ci struct delayed_work work; 9562306a36Sopenharmony_ci struct mutex lock; 9662306a36Sopenharmony_ci struct list_head list; 9762306a36Sopenharmony_ci int id; 9862306a36Sopenharmony_ci}; 9962306a36Sopenharmony_ci 10062306a36Sopenharmony_ci#define rcar_thermal_for_each_priv(pos, common) \ 10162306a36Sopenharmony_ci list_for_each_entry(pos, &common->head, list) 10262306a36Sopenharmony_ci 10362306a36Sopenharmony_ci#define MCELSIUS(temp) ((temp) * 1000) 10462306a36Sopenharmony_ci#define rcar_priv_to_dev(priv) ((priv)->common->dev) 10562306a36Sopenharmony_ci#define rcar_has_irq_support(priv) ((priv)->common->base) 10662306a36Sopenharmony_ci#define rcar_id_to_shift(priv) ((priv)->id * 8) 10762306a36Sopenharmony_ci 10862306a36Sopenharmony_cistatic const struct of_device_id rcar_thermal_dt_ids[] = { 10962306a36Sopenharmony_ci { 11062306a36Sopenharmony_ci .compatible = "renesas,rcar-thermal", 11162306a36Sopenharmony_ci .data = &rcar_thermal, 11262306a36Sopenharmony_ci }, 11362306a36Sopenharmony_ci { 11462306a36Sopenharmony_ci .compatible = "renesas,rcar-gen2-thermal", 11562306a36Sopenharmony_ci .data = &rcar_gen2_thermal, 11662306a36Sopenharmony_ci }, 11762306a36Sopenharmony_ci { 11862306a36Sopenharmony_ci .compatible = "renesas,thermal-r8a774c0", 11962306a36Sopenharmony_ci .data = &rcar_gen3_thermal, 12062306a36Sopenharmony_ci }, 12162306a36Sopenharmony_ci { 12262306a36Sopenharmony_ci .compatible = "renesas,thermal-r8a77970", 12362306a36Sopenharmony_ci .data = &rcar_gen3_thermal, 12462306a36Sopenharmony_ci }, 12562306a36Sopenharmony_ci { 12662306a36Sopenharmony_ci .compatible = "renesas,thermal-r8a77990", 12762306a36Sopenharmony_ci .data = &rcar_gen3_thermal, 12862306a36Sopenharmony_ci }, 12962306a36Sopenharmony_ci { 13062306a36Sopenharmony_ci .compatible = "renesas,thermal-r8a77995", 13162306a36Sopenharmony_ci .data = &rcar_gen3_thermal, 13262306a36Sopenharmony_ci }, 13362306a36Sopenharmony_ci {}, 13462306a36Sopenharmony_ci}; 13562306a36Sopenharmony_ciMODULE_DEVICE_TABLE(of, rcar_thermal_dt_ids); 13662306a36Sopenharmony_ci 13762306a36Sopenharmony_ci/* 13862306a36Sopenharmony_ci * basic functions 13962306a36Sopenharmony_ci */ 14062306a36Sopenharmony_ci#define rcar_thermal_common_read(c, r) \ 14162306a36Sopenharmony_ci _rcar_thermal_common_read(c, COMMON_ ##r) 14262306a36Sopenharmony_cistatic u32 _rcar_thermal_common_read(struct rcar_thermal_common *common, 14362306a36Sopenharmony_ci u32 reg) 14462306a36Sopenharmony_ci{ 14562306a36Sopenharmony_ci return ioread32(common->base + reg); 14662306a36Sopenharmony_ci} 14762306a36Sopenharmony_ci 14862306a36Sopenharmony_ci#define rcar_thermal_common_write(c, r, d) \ 14962306a36Sopenharmony_ci _rcar_thermal_common_write(c, COMMON_ ##r, d) 15062306a36Sopenharmony_cistatic void _rcar_thermal_common_write(struct rcar_thermal_common *common, 15162306a36Sopenharmony_ci u32 reg, u32 data) 15262306a36Sopenharmony_ci{ 15362306a36Sopenharmony_ci iowrite32(data, common->base + reg); 15462306a36Sopenharmony_ci} 15562306a36Sopenharmony_ci 15662306a36Sopenharmony_ci#define rcar_thermal_common_bset(c, r, m, d) \ 15762306a36Sopenharmony_ci _rcar_thermal_common_bset(c, COMMON_ ##r, m, d) 15862306a36Sopenharmony_cistatic void _rcar_thermal_common_bset(struct rcar_thermal_common *common, 15962306a36Sopenharmony_ci u32 reg, u32 mask, u32 data) 16062306a36Sopenharmony_ci{ 16162306a36Sopenharmony_ci u32 val; 16262306a36Sopenharmony_ci 16362306a36Sopenharmony_ci val = ioread32(common->base + reg); 16462306a36Sopenharmony_ci val &= ~mask; 16562306a36Sopenharmony_ci val |= (data & mask); 16662306a36Sopenharmony_ci iowrite32(val, common->base + reg); 16762306a36Sopenharmony_ci} 16862306a36Sopenharmony_ci 16962306a36Sopenharmony_ci#define rcar_thermal_read(p, r) _rcar_thermal_read(p, REG_ ##r) 17062306a36Sopenharmony_cistatic u32 _rcar_thermal_read(struct rcar_thermal_priv *priv, u32 reg) 17162306a36Sopenharmony_ci{ 17262306a36Sopenharmony_ci return ioread32(priv->base + reg); 17362306a36Sopenharmony_ci} 17462306a36Sopenharmony_ci 17562306a36Sopenharmony_ci#define rcar_thermal_write(p, r, d) _rcar_thermal_write(p, REG_ ##r, d) 17662306a36Sopenharmony_cistatic void _rcar_thermal_write(struct rcar_thermal_priv *priv, 17762306a36Sopenharmony_ci u32 reg, u32 data) 17862306a36Sopenharmony_ci{ 17962306a36Sopenharmony_ci iowrite32(data, priv->base + reg); 18062306a36Sopenharmony_ci} 18162306a36Sopenharmony_ci 18262306a36Sopenharmony_ci#define rcar_thermal_bset(p, r, m, d) _rcar_thermal_bset(p, REG_ ##r, m, d) 18362306a36Sopenharmony_cistatic void _rcar_thermal_bset(struct rcar_thermal_priv *priv, u32 reg, 18462306a36Sopenharmony_ci u32 mask, u32 data) 18562306a36Sopenharmony_ci{ 18662306a36Sopenharmony_ci u32 val; 18762306a36Sopenharmony_ci 18862306a36Sopenharmony_ci val = ioread32(priv->base + reg); 18962306a36Sopenharmony_ci val &= ~mask; 19062306a36Sopenharmony_ci val |= (data & mask); 19162306a36Sopenharmony_ci iowrite32(val, priv->base + reg); 19262306a36Sopenharmony_ci} 19362306a36Sopenharmony_ci 19462306a36Sopenharmony_ci/* 19562306a36Sopenharmony_ci * zone device functions 19662306a36Sopenharmony_ci */ 19762306a36Sopenharmony_cistatic int rcar_thermal_update_temp(struct rcar_thermal_priv *priv) 19862306a36Sopenharmony_ci{ 19962306a36Sopenharmony_ci struct device *dev = rcar_priv_to_dev(priv); 20062306a36Sopenharmony_ci int old, new, ctemp = -EINVAL; 20162306a36Sopenharmony_ci unsigned int i; 20262306a36Sopenharmony_ci 20362306a36Sopenharmony_ci mutex_lock(&priv->lock); 20462306a36Sopenharmony_ci 20562306a36Sopenharmony_ci /* 20662306a36Sopenharmony_ci * TSC decides a value of CPTAP automatically, 20762306a36Sopenharmony_ci * and this is the conditions which validate interrupt. 20862306a36Sopenharmony_ci */ 20962306a36Sopenharmony_ci rcar_thermal_bset(priv, THSCR, CPCTL, CPCTL); 21062306a36Sopenharmony_ci 21162306a36Sopenharmony_ci old = ~0; 21262306a36Sopenharmony_ci for (i = 0; i < 128; i++) { 21362306a36Sopenharmony_ci /* 21462306a36Sopenharmony_ci * we need to wait 300us after changing comparator offset 21562306a36Sopenharmony_ci * to get stable temperature. 21662306a36Sopenharmony_ci * see "Usage Notes" on datasheet 21762306a36Sopenharmony_ci */ 21862306a36Sopenharmony_ci usleep_range(300, 400); 21962306a36Sopenharmony_ci 22062306a36Sopenharmony_ci new = rcar_thermal_read(priv, THSSR) & CTEMP; 22162306a36Sopenharmony_ci if (new == old) { 22262306a36Sopenharmony_ci ctemp = new; 22362306a36Sopenharmony_ci break; 22462306a36Sopenharmony_ci } 22562306a36Sopenharmony_ci old = new; 22662306a36Sopenharmony_ci } 22762306a36Sopenharmony_ci 22862306a36Sopenharmony_ci if (ctemp < 0) { 22962306a36Sopenharmony_ci dev_err(dev, "thermal sensor was broken\n"); 23062306a36Sopenharmony_ci goto err_out_unlock; 23162306a36Sopenharmony_ci } 23262306a36Sopenharmony_ci 23362306a36Sopenharmony_ci /* 23462306a36Sopenharmony_ci * enable IRQ 23562306a36Sopenharmony_ci */ 23662306a36Sopenharmony_ci if (rcar_has_irq_support(priv)) { 23762306a36Sopenharmony_ci if (priv->chip->has_filonoff) 23862306a36Sopenharmony_ci rcar_thermal_write(priv, FILONOFF, 0); 23962306a36Sopenharmony_ci 24062306a36Sopenharmony_ci /* enable Rising/Falling edge interrupt */ 24162306a36Sopenharmony_ci rcar_thermal_write(priv, POSNEG, 0x1); 24262306a36Sopenharmony_ci rcar_thermal_write(priv, INTCTRL, (((ctemp - 0) << 8) | 24362306a36Sopenharmony_ci ((ctemp - 1) << 0))); 24462306a36Sopenharmony_ci } 24562306a36Sopenharmony_ci 24662306a36Sopenharmony_cierr_out_unlock: 24762306a36Sopenharmony_ci mutex_unlock(&priv->lock); 24862306a36Sopenharmony_ci 24962306a36Sopenharmony_ci return ctemp; 25062306a36Sopenharmony_ci} 25162306a36Sopenharmony_ci 25262306a36Sopenharmony_cistatic int rcar_thermal_get_current_temp(struct rcar_thermal_priv *priv, 25362306a36Sopenharmony_ci int *temp) 25462306a36Sopenharmony_ci{ 25562306a36Sopenharmony_ci int ctemp; 25662306a36Sopenharmony_ci 25762306a36Sopenharmony_ci ctemp = rcar_thermal_update_temp(priv); 25862306a36Sopenharmony_ci if (ctemp < 0) 25962306a36Sopenharmony_ci return ctemp; 26062306a36Sopenharmony_ci 26162306a36Sopenharmony_ci /* Guaranteed operating range is -45C to 125C. */ 26262306a36Sopenharmony_ci 26362306a36Sopenharmony_ci if (priv->chip->ctemp_bands == 1) 26462306a36Sopenharmony_ci *temp = MCELSIUS((ctemp * 5) - 65); 26562306a36Sopenharmony_ci else if (ctemp < 24) 26662306a36Sopenharmony_ci *temp = MCELSIUS(((ctemp * 55) - 720) / 10); 26762306a36Sopenharmony_ci else 26862306a36Sopenharmony_ci *temp = MCELSIUS((ctemp * 5) - 60); 26962306a36Sopenharmony_ci 27062306a36Sopenharmony_ci return 0; 27162306a36Sopenharmony_ci} 27262306a36Sopenharmony_ci 27362306a36Sopenharmony_cistatic int rcar_thermal_get_temp(struct thermal_zone_device *zone, int *temp) 27462306a36Sopenharmony_ci{ 27562306a36Sopenharmony_ci struct rcar_thermal_priv *priv = thermal_zone_device_priv(zone); 27662306a36Sopenharmony_ci 27762306a36Sopenharmony_ci return rcar_thermal_get_current_temp(priv, temp); 27862306a36Sopenharmony_ci} 27962306a36Sopenharmony_ci 28062306a36Sopenharmony_cistatic struct thermal_zone_device_ops rcar_thermal_zone_ops = { 28162306a36Sopenharmony_ci .get_temp = rcar_thermal_get_temp, 28262306a36Sopenharmony_ci}; 28362306a36Sopenharmony_ci 28462306a36Sopenharmony_cistatic struct thermal_trip trips[] = { 28562306a36Sopenharmony_ci { .type = THERMAL_TRIP_CRITICAL, .temperature = 90000 } 28662306a36Sopenharmony_ci}; 28762306a36Sopenharmony_ci 28862306a36Sopenharmony_ci/* 28962306a36Sopenharmony_ci * interrupt 29062306a36Sopenharmony_ci */ 29162306a36Sopenharmony_ci#define rcar_thermal_irq_enable(p) _rcar_thermal_irq_ctrl(p, 1) 29262306a36Sopenharmony_ci#define rcar_thermal_irq_disable(p) _rcar_thermal_irq_ctrl(p, 0) 29362306a36Sopenharmony_cistatic void _rcar_thermal_irq_ctrl(struct rcar_thermal_priv *priv, int enable) 29462306a36Sopenharmony_ci{ 29562306a36Sopenharmony_ci struct rcar_thermal_common *common = priv->common; 29662306a36Sopenharmony_ci unsigned long flags; 29762306a36Sopenharmony_ci u32 mask = 0x3 << rcar_id_to_shift(priv); /* enable Rising/Falling */ 29862306a36Sopenharmony_ci 29962306a36Sopenharmony_ci if (!rcar_has_irq_support(priv)) 30062306a36Sopenharmony_ci return; 30162306a36Sopenharmony_ci 30262306a36Sopenharmony_ci spin_lock_irqsave(&common->lock, flags); 30362306a36Sopenharmony_ci 30462306a36Sopenharmony_ci rcar_thermal_common_bset(common, INTMSK, mask, enable ? 0 : mask); 30562306a36Sopenharmony_ci 30662306a36Sopenharmony_ci spin_unlock_irqrestore(&common->lock, flags); 30762306a36Sopenharmony_ci} 30862306a36Sopenharmony_ci 30962306a36Sopenharmony_cistatic void rcar_thermal_work(struct work_struct *work) 31062306a36Sopenharmony_ci{ 31162306a36Sopenharmony_ci struct rcar_thermal_priv *priv; 31262306a36Sopenharmony_ci int ret; 31362306a36Sopenharmony_ci 31462306a36Sopenharmony_ci priv = container_of(work, struct rcar_thermal_priv, work.work); 31562306a36Sopenharmony_ci 31662306a36Sopenharmony_ci ret = rcar_thermal_update_temp(priv); 31762306a36Sopenharmony_ci if (ret < 0) 31862306a36Sopenharmony_ci return; 31962306a36Sopenharmony_ci 32062306a36Sopenharmony_ci rcar_thermal_irq_enable(priv); 32162306a36Sopenharmony_ci 32262306a36Sopenharmony_ci thermal_zone_device_update(priv->zone, THERMAL_EVENT_UNSPECIFIED); 32362306a36Sopenharmony_ci} 32462306a36Sopenharmony_ci 32562306a36Sopenharmony_cistatic u32 rcar_thermal_had_changed(struct rcar_thermal_priv *priv, u32 status) 32662306a36Sopenharmony_ci{ 32762306a36Sopenharmony_ci struct device *dev = rcar_priv_to_dev(priv); 32862306a36Sopenharmony_ci 32962306a36Sopenharmony_ci status = (status >> rcar_id_to_shift(priv)) & 0x3; 33062306a36Sopenharmony_ci 33162306a36Sopenharmony_ci if (status) { 33262306a36Sopenharmony_ci dev_dbg(dev, "thermal%d %s%s\n", 33362306a36Sopenharmony_ci priv->id, 33462306a36Sopenharmony_ci (status & 0x2) ? "Rising " : "", 33562306a36Sopenharmony_ci (status & 0x1) ? "Falling" : ""); 33662306a36Sopenharmony_ci } 33762306a36Sopenharmony_ci 33862306a36Sopenharmony_ci return status; 33962306a36Sopenharmony_ci} 34062306a36Sopenharmony_ci 34162306a36Sopenharmony_cistatic irqreturn_t rcar_thermal_irq(int irq, void *data) 34262306a36Sopenharmony_ci{ 34362306a36Sopenharmony_ci struct rcar_thermal_common *common = data; 34462306a36Sopenharmony_ci struct rcar_thermal_priv *priv; 34562306a36Sopenharmony_ci u32 status, mask; 34662306a36Sopenharmony_ci 34762306a36Sopenharmony_ci spin_lock(&common->lock); 34862306a36Sopenharmony_ci 34962306a36Sopenharmony_ci mask = rcar_thermal_common_read(common, INTMSK); 35062306a36Sopenharmony_ci status = rcar_thermal_common_read(common, STR); 35162306a36Sopenharmony_ci rcar_thermal_common_write(common, STR, 0x000F0F0F & mask); 35262306a36Sopenharmony_ci 35362306a36Sopenharmony_ci spin_unlock(&common->lock); 35462306a36Sopenharmony_ci 35562306a36Sopenharmony_ci status = status & ~mask; 35662306a36Sopenharmony_ci 35762306a36Sopenharmony_ci /* 35862306a36Sopenharmony_ci * check the status 35962306a36Sopenharmony_ci */ 36062306a36Sopenharmony_ci rcar_thermal_for_each_priv(priv, common) { 36162306a36Sopenharmony_ci if (rcar_thermal_had_changed(priv, status)) { 36262306a36Sopenharmony_ci rcar_thermal_irq_disable(priv); 36362306a36Sopenharmony_ci queue_delayed_work(system_freezable_wq, &priv->work, 36462306a36Sopenharmony_ci msecs_to_jiffies(300)); 36562306a36Sopenharmony_ci } 36662306a36Sopenharmony_ci } 36762306a36Sopenharmony_ci 36862306a36Sopenharmony_ci return IRQ_HANDLED; 36962306a36Sopenharmony_ci} 37062306a36Sopenharmony_ci 37162306a36Sopenharmony_ci/* 37262306a36Sopenharmony_ci * platform functions 37362306a36Sopenharmony_ci */ 37462306a36Sopenharmony_cistatic int rcar_thermal_remove(struct platform_device *pdev) 37562306a36Sopenharmony_ci{ 37662306a36Sopenharmony_ci struct rcar_thermal_common *common = platform_get_drvdata(pdev); 37762306a36Sopenharmony_ci struct device *dev = &pdev->dev; 37862306a36Sopenharmony_ci struct rcar_thermal_priv *priv; 37962306a36Sopenharmony_ci 38062306a36Sopenharmony_ci rcar_thermal_for_each_priv(priv, common) { 38162306a36Sopenharmony_ci rcar_thermal_irq_disable(priv); 38262306a36Sopenharmony_ci cancel_delayed_work_sync(&priv->work); 38362306a36Sopenharmony_ci if (priv->chip->use_of_thermal) 38462306a36Sopenharmony_ci thermal_remove_hwmon_sysfs(priv->zone); 38562306a36Sopenharmony_ci else 38662306a36Sopenharmony_ci thermal_zone_device_unregister(priv->zone); 38762306a36Sopenharmony_ci } 38862306a36Sopenharmony_ci 38962306a36Sopenharmony_ci pm_runtime_put(dev); 39062306a36Sopenharmony_ci pm_runtime_disable(dev); 39162306a36Sopenharmony_ci 39262306a36Sopenharmony_ci return 0; 39362306a36Sopenharmony_ci} 39462306a36Sopenharmony_ci 39562306a36Sopenharmony_cistatic int rcar_thermal_probe(struct platform_device *pdev) 39662306a36Sopenharmony_ci{ 39762306a36Sopenharmony_ci struct rcar_thermal_common *common; 39862306a36Sopenharmony_ci struct rcar_thermal_priv *priv; 39962306a36Sopenharmony_ci struct device *dev = &pdev->dev; 40062306a36Sopenharmony_ci struct resource *res; 40162306a36Sopenharmony_ci const struct rcar_thermal_chip *chip = of_device_get_match_data(dev); 40262306a36Sopenharmony_ci int mres = 0; 40362306a36Sopenharmony_ci int i; 40462306a36Sopenharmony_ci int ret = -ENODEV; 40562306a36Sopenharmony_ci int idle = IDLE_INTERVAL; 40662306a36Sopenharmony_ci u32 enr_bits = 0; 40762306a36Sopenharmony_ci 40862306a36Sopenharmony_ci common = devm_kzalloc(dev, sizeof(*common), GFP_KERNEL); 40962306a36Sopenharmony_ci if (!common) 41062306a36Sopenharmony_ci return -ENOMEM; 41162306a36Sopenharmony_ci 41262306a36Sopenharmony_ci platform_set_drvdata(pdev, common); 41362306a36Sopenharmony_ci 41462306a36Sopenharmony_ci INIT_LIST_HEAD(&common->head); 41562306a36Sopenharmony_ci spin_lock_init(&common->lock); 41662306a36Sopenharmony_ci common->dev = dev; 41762306a36Sopenharmony_ci 41862306a36Sopenharmony_ci pm_runtime_enable(dev); 41962306a36Sopenharmony_ci pm_runtime_get_sync(dev); 42062306a36Sopenharmony_ci 42162306a36Sopenharmony_ci for (i = 0; i < chip->nirqs; i++) { 42262306a36Sopenharmony_ci int irq; 42362306a36Sopenharmony_ci 42462306a36Sopenharmony_ci ret = platform_get_irq_optional(pdev, i); 42562306a36Sopenharmony_ci if (ret < 0 && ret != -ENXIO) 42662306a36Sopenharmony_ci goto error_unregister; 42762306a36Sopenharmony_ci if (ret > 0) 42862306a36Sopenharmony_ci irq = ret; 42962306a36Sopenharmony_ci else 43062306a36Sopenharmony_ci break; 43162306a36Sopenharmony_ci 43262306a36Sopenharmony_ci if (!common->base) { 43362306a36Sopenharmony_ci /* 43462306a36Sopenharmony_ci * platform has IRQ support. 43562306a36Sopenharmony_ci * Then, driver uses common registers 43662306a36Sopenharmony_ci * rcar_has_irq_support() will be enabled 43762306a36Sopenharmony_ci */ 43862306a36Sopenharmony_ci res = platform_get_resource(pdev, IORESOURCE_MEM, 43962306a36Sopenharmony_ci mres++); 44062306a36Sopenharmony_ci common->base = devm_ioremap_resource(dev, res); 44162306a36Sopenharmony_ci if (IS_ERR(common->base)) { 44262306a36Sopenharmony_ci ret = PTR_ERR(common->base); 44362306a36Sopenharmony_ci goto error_unregister; 44462306a36Sopenharmony_ci } 44562306a36Sopenharmony_ci 44662306a36Sopenharmony_ci idle = 0; /* polling delay is not needed */ 44762306a36Sopenharmony_ci } 44862306a36Sopenharmony_ci 44962306a36Sopenharmony_ci ret = devm_request_irq(dev, irq, rcar_thermal_irq, 45062306a36Sopenharmony_ci IRQF_SHARED, dev_name(dev), common); 45162306a36Sopenharmony_ci if (ret) { 45262306a36Sopenharmony_ci dev_err(dev, "irq request failed\n "); 45362306a36Sopenharmony_ci goto error_unregister; 45462306a36Sopenharmony_ci } 45562306a36Sopenharmony_ci 45662306a36Sopenharmony_ci /* update ENR bits */ 45762306a36Sopenharmony_ci if (chip->irq_per_ch) 45862306a36Sopenharmony_ci enr_bits |= 1 << i; 45962306a36Sopenharmony_ci } 46062306a36Sopenharmony_ci 46162306a36Sopenharmony_ci for (i = 0;; i++) { 46262306a36Sopenharmony_ci res = platform_get_resource(pdev, IORESOURCE_MEM, mres++); 46362306a36Sopenharmony_ci if (!res) 46462306a36Sopenharmony_ci break; 46562306a36Sopenharmony_ci 46662306a36Sopenharmony_ci priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); 46762306a36Sopenharmony_ci if (!priv) { 46862306a36Sopenharmony_ci ret = -ENOMEM; 46962306a36Sopenharmony_ci goto error_unregister; 47062306a36Sopenharmony_ci } 47162306a36Sopenharmony_ci 47262306a36Sopenharmony_ci priv->base = devm_ioremap_resource(dev, res); 47362306a36Sopenharmony_ci if (IS_ERR(priv->base)) { 47462306a36Sopenharmony_ci ret = PTR_ERR(priv->base); 47562306a36Sopenharmony_ci goto error_unregister; 47662306a36Sopenharmony_ci } 47762306a36Sopenharmony_ci 47862306a36Sopenharmony_ci priv->common = common; 47962306a36Sopenharmony_ci priv->id = i; 48062306a36Sopenharmony_ci priv->chip = chip; 48162306a36Sopenharmony_ci mutex_init(&priv->lock); 48262306a36Sopenharmony_ci INIT_LIST_HEAD(&priv->list); 48362306a36Sopenharmony_ci INIT_DELAYED_WORK(&priv->work, rcar_thermal_work); 48462306a36Sopenharmony_ci ret = rcar_thermal_update_temp(priv); 48562306a36Sopenharmony_ci if (ret < 0) 48662306a36Sopenharmony_ci goto error_unregister; 48762306a36Sopenharmony_ci 48862306a36Sopenharmony_ci if (chip->use_of_thermal) { 48962306a36Sopenharmony_ci priv->zone = devm_thermal_of_zone_register( 49062306a36Sopenharmony_ci dev, i, priv, 49162306a36Sopenharmony_ci &rcar_thermal_zone_ops); 49262306a36Sopenharmony_ci } else { 49362306a36Sopenharmony_ci priv->zone = thermal_zone_device_register_with_trips( 49462306a36Sopenharmony_ci "rcar_thermal", trips, ARRAY_SIZE(trips), 0, priv, 49562306a36Sopenharmony_ci &rcar_thermal_zone_ops, NULL, 0, 49662306a36Sopenharmony_ci idle); 49762306a36Sopenharmony_ci 49862306a36Sopenharmony_ci ret = thermal_zone_device_enable(priv->zone); 49962306a36Sopenharmony_ci if (ret) { 50062306a36Sopenharmony_ci thermal_zone_device_unregister(priv->zone); 50162306a36Sopenharmony_ci priv->zone = ERR_PTR(ret); 50262306a36Sopenharmony_ci } 50362306a36Sopenharmony_ci } 50462306a36Sopenharmony_ci if (IS_ERR(priv->zone)) { 50562306a36Sopenharmony_ci dev_err(dev, "can't register thermal zone\n"); 50662306a36Sopenharmony_ci ret = PTR_ERR(priv->zone); 50762306a36Sopenharmony_ci priv->zone = NULL; 50862306a36Sopenharmony_ci goto error_unregister; 50962306a36Sopenharmony_ci } 51062306a36Sopenharmony_ci 51162306a36Sopenharmony_ci if (chip->use_of_thermal) { 51262306a36Sopenharmony_ci ret = thermal_add_hwmon_sysfs(priv->zone); 51362306a36Sopenharmony_ci if (ret) 51462306a36Sopenharmony_ci goto error_unregister; 51562306a36Sopenharmony_ci } 51662306a36Sopenharmony_ci 51762306a36Sopenharmony_ci rcar_thermal_irq_enable(priv); 51862306a36Sopenharmony_ci 51962306a36Sopenharmony_ci list_move_tail(&priv->list, &common->head); 52062306a36Sopenharmony_ci 52162306a36Sopenharmony_ci /* update ENR bits */ 52262306a36Sopenharmony_ci if (!chip->irq_per_ch) 52362306a36Sopenharmony_ci enr_bits |= 3 << (i * 8); 52462306a36Sopenharmony_ci } 52562306a36Sopenharmony_ci 52662306a36Sopenharmony_ci if (common->base && enr_bits) 52762306a36Sopenharmony_ci rcar_thermal_common_write(common, ENR, enr_bits); 52862306a36Sopenharmony_ci 52962306a36Sopenharmony_ci dev_info(dev, "%d sensor probed\n", i); 53062306a36Sopenharmony_ci 53162306a36Sopenharmony_ci return 0; 53262306a36Sopenharmony_ci 53362306a36Sopenharmony_cierror_unregister: 53462306a36Sopenharmony_ci rcar_thermal_remove(pdev); 53562306a36Sopenharmony_ci 53662306a36Sopenharmony_ci return ret; 53762306a36Sopenharmony_ci} 53862306a36Sopenharmony_ci 53962306a36Sopenharmony_ci#ifdef CONFIG_PM_SLEEP 54062306a36Sopenharmony_cistatic int rcar_thermal_suspend(struct device *dev) 54162306a36Sopenharmony_ci{ 54262306a36Sopenharmony_ci struct rcar_thermal_common *common = dev_get_drvdata(dev); 54362306a36Sopenharmony_ci struct rcar_thermal_priv *priv = list_first_entry(&common->head, 54462306a36Sopenharmony_ci typeof(*priv), list); 54562306a36Sopenharmony_ci 54662306a36Sopenharmony_ci if (priv->chip->needs_suspend_resume) { 54762306a36Sopenharmony_ci rcar_thermal_common_write(common, ENR, 0); 54862306a36Sopenharmony_ci rcar_thermal_irq_disable(priv); 54962306a36Sopenharmony_ci rcar_thermal_bset(priv, THSCR, CPCTL, 0); 55062306a36Sopenharmony_ci } 55162306a36Sopenharmony_ci 55262306a36Sopenharmony_ci return 0; 55362306a36Sopenharmony_ci} 55462306a36Sopenharmony_ci 55562306a36Sopenharmony_cistatic int rcar_thermal_resume(struct device *dev) 55662306a36Sopenharmony_ci{ 55762306a36Sopenharmony_ci struct rcar_thermal_common *common = dev_get_drvdata(dev); 55862306a36Sopenharmony_ci struct rcar_thermal_priv *priv = list_first_entry(&common->head, 55962306a36Sopenharmony_ci typeof(*priv), list); 56062306a36Sopenharmony_ci int ret; 56162306a36Sopenharmony_ci 56262306a36Sopenharmony_ci if (priv->chip->needs_suspend_resume) { 56362306a36Sopenharmony_ci ret = rcar_thermal_update_temp(priv); 56462306a36Sopenharmony_ci if (ret < 0) 56562306a36Sopenharmony_ci return ret; 56662306a36Sopenharmony_ci rcar_thermal_irq_enable(priv); 56762306a36Sopenharmony_ci rcar_thermal_common_write(common, ENR, 0x03); 56862306a36Sopenharmony_ci } 56962306a36Sopenharmony_ci 57062306a36Sopenharmony_ci return 0; 57162306a36Sopenharmony_ci} 57262306a36Sopenharmony_ci#endif 57362306a36Sopenharmony_ci 57462306a36Sopenharmony_cistatic SIMPLE_DEV_PM_OPS(rcar_thermal_pm_ops, rcar_thermal_suspend, 57562306a36Sopenharmony_ci rcar_thermal_resume); 57662306a36Sopenharmony_ci 57762306a36Sopenharmony_cistatic struct platform_driver rcar_thermal_driver = { 57862306a36Sopenharmony_ci .driver = { 57962306a36Sopenharmony_ci .name = "rcar_thermal", 58062306a36Sopenharmony_ci .pm = &rcar_thermal_pm_ops, 58162306a36Sopenharmony_ci .of_match_table = rcar_thermal_dt_ids, 58262306a36Sopenharmony_ci }, 58362306a36Sopenharmony_ci .probe = rcar_thermal_probe, 58462306a36Sopenharmony_ci .remove = rcar_thermal_remove, 58562306a36Sopenharmony_ci}; 58662306a36Sopenharmony_cimodule_platform_driver(rcar_thermal_driver); 58762306a36Sopenharmony_ci 58862306a36Sopenharmony_ciMODULE_LICENSE("GPL v2"); 58962306a36Sopenharmony_ciMODULE_DESCRIPTION("R-Car THS/TSC thermal sensor driver"); 59062306a36Sopenharmony_ciMODULE_AUTHOR("Kuninori Morimoto <kuninori.morimoto.gx@renesas.com>"); 591