From mboxrd@z Thu Jan 1 00:00:00 1970 From: Zhang Rui Subject: Re: [PATCH 2/5 v2] thermal: rcar_gen3_thermal: Add R-Car Gen3 thermal driver Date: Tue, 06 Sep 2016 08:51:16 +0800 Message-ID: <1473123076.2201.37.camel@intel.com> References: Mime-Version: 1.0 Content-Type: text/plain; charset="UTF-8" Content-Transfer-Encoding: 8bit Return-path: In-Reply-To: Sender: devicetree-owner-u79uwXL29TY76Z2rM5mHXA@public.gmane.org To: Khiem Nguyen , Kuninori Morimoto Cc: Wolfram Sang , Geert Uytterhoeven , Magnus Damm , Eduardo Valentin , Rob Herring , Mark Rutland , "linux-pm-u79uwXL29TY76Z2rM5mHXA@public.gmane.org" , "devicetree-u79uwXL29TY76Z2rM5mHXA@public.gmane.org" , "linux-kernel-u79uwXL29TY76Z2rM5mHXA@public.gmane.org" , "Thao Phuong Le. Nguyen" , Hien Dang , Toru Oishi List-Id: devicetree@vger.kernel.org On 六, 2016-09-03 at 05:25 +0000, Khiem Nguyen wrote: > Signed-off-by: Hien Dang > Signed-off-by: Thao Nguyen > Signed-off-by: Khiem Nguyen Well, I can only see patch 4/5, 5/5 in patchwork but I can not see this one.... thanks, rui > --- > > v2: >  * Set static function for _linear_temp_converter(). >  * Update the compatible string following new format. >  * Add newline to improve readability. >  * Change thermal_init callbacks to void functions. >  * Improve the processing to register thermal_zones. >   >  drivers/thermal/Kconfig             |   9 + >  drivers/thermal/Makefile            |   1 + >  drivers/thermal/rcar_gen3_thermal.c | 539 > ++++++++++++++++++++++++++++++++++++ >  3 files changed, 549 insertions(+) >  create mode 100644 drivers/thermal/rcar_gen3_thermal.c > > diff --git a/drivers/thermal/Kconfig b/drivers/thermal/Kconfig > index 900d505..8500a0a 100644 > --- a/drivers/thermal/Kconfig > +++ b/drivers/thermal/Kconfig > @@ -233,6 +233,15 @@ config RCAR_THERMAL >     Enable this to plug the R-Car thermal sensor driver into > the Linux >     thermal framework. >   > +config RCAR_GEN3_THERMAL > + tristate "Renesas R-Car Gen3 thermal driver" > + depends on ARCH_RENESAS || COMPILE_TEST > + depends on HAS_IOMEM > + depends on OF > + help > +   Enable this to plug the R-Car Gen3 thermal sensor driver > into the Linux > +   thermal framework. > + >  config KIRKWOOD_THERMAL >   tristate "Temperature sensor on Marvell Kirkwood SoCs" >   depends on MACH_KIRKWOOD || COMPILE_TEST > diff --git a/drivers/thermal/Makefile b/drivers/thermal/Makefile > index d091134..b7e7082 100644 > --- a/drivers/thermal/Makefile > +++ b/drivers/thermal/Makefile > @@ -31,6 +31,7 @@ obj-$(CONFIG_QCOM_SPMI_TEMP_ALARM) += qcom- > spmi-temp-alarm.o >  obj-$(CONFIG_SPEAR_THERMAL) += spear_thermal.o >  obj-$(CONFIG_ROCKCHIP_THERMAL) += rockchip_thermal.o >  obj-$(CONFIG_RCAR_THERMAL) += rcar_thermal.o > +obj-$(CONFIG_RCAR_GEN3_THERMAL) += rcar_gen3_thermal.o >  obj-$(CONFIG_KIRKWOOD_THERMAL)  += kirkwood_thermal.o >  obj-y += samsung/ >  obj-$(CONFIG_DOVE_THERMAL)   += dove_thermal.o > diff --git a/drivers/thermal/rcar_gen3_thermal.c > b/drivers/thermal/rcar_gen3_thermal.c > new file mode 100644 > index 0000000..cdaaa75 > --- /dev/null > +++ b/drivers/thermal/rcar_gen3_thermal.c > @@ -0,0 +1,539 @@ > +/* > + *  R-Car Gen3 THS thermal sensor driver > + *  Based on drivers/thermal/rcar_thermal.c > + * > + * Copyright (C) 2016 Renesas Electronics Corporation. > + * > + *  This program is free software; you can redistribute it and/or > modify > + *  it under the terms of the GNU General Public License as > published by > + *  the Free Software Foundation; version 2 of the License. > + * > + *  This program is distributed in the hope that it will be useful, > but > + *  WITHOUT ANY WARRANTY; without even the implied warranty of > + *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the > GNU > + *  General Public License for more details. > + * > + */ > +#include > +#include > +#include > +#include > +#include > +#include > +#include > +#include > +#include > +#include > +#include > + > +/* Register offset */ > +#define REG_GEN3_CTSR 0x20 > +#define REG_GEN3_THCTR 0x20 > +#define REG_GEN3_IRQSTR 0x04 > +#define REG_GEN3_IRQMSK 0x08 > +#define REG_GEN3_IRQCTL 0x0C > +#define REG_GEN3_IRQEN 0x10 > +#define REG_GEN3_IRQTEMP1 0x14 > +#define REG_GEN3_IRQTEMP2 0x18 > +#define REG_GEN3_IRQTEMP3 0x1C > +#define REG_GEN3_TEMP 0x28 > +#define REG_GEN3_THCODE1 0x50 > +#define REG_GEN3_THCODE2 0x54 > +#define REG_GEN3_THCODE3 0x58 > + > +#define PTAT_BASE 0xE6198000 > +#define REG_GEN3_PTAT1 0x5C > +#define REG_GEN3_PTAT2 0x60 > +#define REG_GEN3_PTAT3 0x64 > +#define PTAT_SIZE REG_GEN3_PTAT3 > + > +/* CTSR bit */ > +#define PONM            (0x1 << 8) > +#define AOUT            (0x1 << 7) > +#define THBGR           (0x1 << 5) > +#define VMEN            (0x1 << 4) > +#define VMST            (0x1 << 1) > +#define THSST           (0x1 << 0) > + > +/* THCTR bit */ > +#define CTCTL (0x1 << 24) > +#define THCNTSEN(x) (x << 16) > + > +#define BIT_LEN_12 0x1 > + > +#define CTEMP_MASK 0xFFF > + > +#define MCELSIUS(temp) ((temp) * 1000) > +#define TEMP_IRQ_SHIFT(tsc_id) (0x1 << tsc_id) > +#define TEMPD_IRQ_SHIFT(tsc_id) (0x1 << (tsc_id + 3)) > +#define GEN3_FUSE_MASK 0xFFF > + > +/* Structure for thermal temperature calculation */ > +struct equation_coefs { > + long a1; > + long b1; > + long a2; > + long b2; > +}; > + > +struct fuse_factors { > + int thcode_1; > + int thcode_2; > + int thcode_3; > + int ptat_1; > + int ptat_2; > + int ptat_3; > +}; > + > +struct rcar_gen3_thermal_priv { > + void __iomem *base; > + struct device *dev; > + struct thermal_zone_device *zone; > + struct delayed_work work; > + struct fuse_factors factor; > + struct equation_coefs coef; > + spinlock_t lock; > + int id; > + int irq; > + u32 ctemp; > + const struct rcar_gen3_thermal_data *data; > +}; > + > +struct rcar_gen3_thermal_data { > + void (*thermal_init)(struct rcar_gen3_thermal_priv *priv); > +}; > + > +#define rcar_priv_to_dev(priv) ((priv)->dev) > +#define rcar_has_irq_support(priv) ((priv)->irq) > + > +/* Temperature calculation  */ > +#define CODETSD(x) ((x) * 1000) > +#define TJ_1 96000L > +#define TJ_3 (-41000L) > +#define PW2(x) ((x)*(x)) > + > +static u32 thermal_reg_read(struct rcar_gen3_thermal_priv *priv, u32 > reg) > +{ > + return ioread32(priv->base + reg); > +} > + > +static void thermal_reg_write(struct rcar_gen3_thermal_priv *priv, > + u32 reg, u32 data) > +{ > + iowrite32(data, priv->base + reg); > +} > + > +static int _round_temp(int temp) > +{ > + int tmp1, tmp2; > + int result = 0; > + > + tmp1 = abs(temp) % 1000; > + tmp2 = abs(temp) / 1000; > + > + if (tmp1 < 250) > + result = CODETSD(tmp2); > + else if (tmp1 < 750 && tmp1 >= 250) > + result = CODETSD(tmp2) + 500; > + else > + result = CODETSD(tmp2) + 1000; > + > + return ((temp < 0) ? (result * (-1)) : result); > +} > + > +static int _read_fuse_factor(struct rcar_gen3_thermal_priv *priv) > +{ > + /* > +  * FIXME: The value should be read from some FUSE registers. > +  * For available SoC, these registers have not been > supported yet. > +  * The pre-defined value will be applied for now. > +  */ > + priv->factor.ptat_1 = 2351; > + priv->factor.ptat_2 = 1509; > + priv->factor.ptat_3 = 435; > + switch (priv->id) { > + case 0: > + priv->factor.thcode_1 = 3248; > + priv->factor.thcode_2 = 2800; > + priv->factor.thcode_3 = 2221; > + break; > + case 1: > + priv->factor.thcode_1 = 3245; > + priv->factor.thcode_2 = 2795; > + priv->factor.thcode_3 = 2216; > + break; > + case 2: > + priv->factor.thcode_1 = 3250; > + priv->factor.thcode_2 = 2805; > + priv->factor.thcode_3 = 2237; > + break; > + } > + > + return 0; > +} > + > +static void _linear_coefficient_calculation(struct > rcar_gen3_thermal_priv *priv) > +{ > + int tj_2 = 0; > + long a1, b1; > + long a2, b2; > + long a1_num, a1_den; > + long a2_num, a2_den; > + > + tj_2 = (CODETSD((priv->factor.ptat_2 - priv->factor.ptat_3) > * 137) > + / (priv->factor.ptat_1 - priv->factor.ptat_3)) - > CODETSD(41); > + > + /* > +  * The following code is to calculate coefficients for > linear equation. > +  */ > + /* Coefficient a1 and b1 */ > + a1_num = CODETSD(priv->factor.thcode_2 - priv- > >factor.thcode_3); > + a1_den = tj_2 - TJ_3; > + a1 = (10000 * a1_num) / a1_den; > + b1 = (10000 * priv->factor.thcode_3) - ((a1 * TJ_3) / 1000); > + > + /* Coefficient a2 and b2 */ > + a2_num = CODETSD(priv->factor.thcode_2 - priv- > >factor.thcode_1); > + a2_den = tj_2 - TJ_1; > + a2 = (10000 * a2_num) / a2_den; > + b2 = (10000 * priv->factor.thcode_1) - ((a2 * TJ_1) / 1000); > + > + priv->coef.a1 = DIV_ROUND_CLOSEST(a1, 10); > + priv->coef.b1 = DIV_ROUND_CLOSEST(b1, 10); > + priv->coef.a2 = DIV_ROUND_CLOSEST(a2, 10); > + priv->coef.b2 = DIV_ROUND_CLOSEST(b2, 10); > +} > + > +static int _linear_temp_converter(struct equation_coefs coef, > + int temp_code) > +{ > + int temp, temp1, temp2; > + > + temp1 = MCELSIUS((CODETSD(temp_code) - coef.b1)) / coef.a1; > + temp2 = MCELSIUS((CODETSD(temp_code) - coef.b2)) / coef.a2; > + temp = (temp1 + temp2) / 2; > + > + return _round_temp(temp); > +} > + > +/* > + * Zone device functions > + */ > +static int rcar_gen3_thermal_update_temp(struct > rcar_gen3_thermal_priv *priv) > +{ > + u32 ctemp; > + int i; > + unsigned long flags; > + u32 reg = REG_GEN3_IRQTEMP1 + (priv->id * 4); > + > + spin_lock_irqsave(&priv->lock, flags); > + > + for (i = 0; i < 256; i++) { > + ctemp = thermal_reg_read(priv, REG_GEN3_TEMP) & > CTEMP_MASK; > + > + if (rcar_has_irq_support(priv)) { > + thermal_reg_write(priv, reg, ctemp); > + > + if (thermal_reg_read(priv, REG_GEN3_IRQSTR) > != 0) > + break; > + } else > + break; > + > + udelay(150); > + } > + > + priv->ctemp = ctemp; > + spin_unlock_irqrestore(&priv->lock, flags); > + > + return 0; > +} > + > +static int rcar_gen3_thermal_get_temp(void *devdata, int *temp) > +{ > + struct rcar_gen3_thermal_priv *priv = devdata; > + int ctemp; > + unsigned long flags; > + > + rcar_gen3_thermal_update_temp(priv); > + > + spin_lock_irqsave(&priv->lock, flags); > + ctemp = _linear_temp_converter(priv->coef, priv->ctemp); > + spin_unlock_irqrestore(&priv->lock, flags); > + > + if ((ctemp < MCELSIUS(-40)) || (ctemp > MCELSIUS(125))) { > + struct device *dev = rcar_priv_to_dev(priv); > + > + dev_dbg(dev, "Temperature is not measured > correctly!\n"); > + return -EIO; > + } > + > + *temp = ctemp; > + > + return 0; > +} > + > +static void r8a7795_thermal_init(struct rcar_gen3_thermal_priv > *priv) > +{ > + unsigned long flags; > + > + spin_lock_irqsave(&priv->lock, flags); > + > + thermal_reg_write(priv, REG_GEN3_CTSR,  THBGR); > + thermal_reg_write(priv, REG_GEN3_CTSR,  0x0); > + > + udelay(1000); > + > + thermal_reg_write(priv, REG_GEN3_CTSR, PONM); > + thermal_reg_write(priv, REG_GEN3_IRQCTL, 0x3F); > + thermal_reg_write(priv, REG_GEN3_IRQEN, TEMP_IRQ_SHIFT(priv- > >id) | > + TEMPD_IRQ_SHIFT(priv > ->id)); > + thermal_reg_write(priv, REG_GEN3_CTSR, > + PONM | AOUT | THBGR | VMEN); > + > + udelay(100); > + > + thermal_reg_write(priv, REG_GEN3_CTSR, > + PONM | AOUT | THBGR | VMEN | VMST | THSST); > + > + spin_unlock_irqrestore(&priv->lock, flags); > +} > + > +static void r8a7796_thermal_init(struct rcar_gen3_thermal_priv > *priv) > +{ > + unsigned long flags; > + unsigned long reg_val; > + > + spin_lock_irqsave(&priv->lock, flags); > + > + thermal_reg_write(priv, REG_GEN3_THCTR,  0x0); > + > + udelay(1000); > + > + thermal_reg_write(priv, REG_GEN3_IRQCTL, 0x3F); > + thermal_reg_write(priv, REG_GEN3_IRQEN, TEMP_IRQ_SHIFT(priv- > >id) | > + TEMPD_IRQ_SHIFT(priv > ->id)); > + thermal_reg_write(priv, REG_GEN3_THCTR, > + CTCTL | > THCNTSEN(BIT_LEN_12)); > + reg_val = thermal_reg_read(priv, REG_GEN3_THCTR); > + reg_val &= ~CTCTL; > + reg_val |= THSST; > + thermal_reg_write(priv, REG_GEN3_THCTR, reg_val); > + > + spin_unlock_irqrestore(&priv->lock, flags); > +} > + > +/* > + * Interrupt > + */ > +#define rcar_gen3_thermal_irq_enable(p) _thermal_irq_ > ctrl(p, 1) > +#define rcar_gen3_thermal_irq_disable(p) _thermal_irq_ctrl(p, > 0) > +static void _thermal_irq_ctrl(struct rcar_gen3_thermal_priv *priv, > int enable) > +{ > + unsigned long flags; > + > + if (!rcar_has_irq_support(priv)) > + return; > + > + spin_lock_irqsave(&priv->lock, flags); > + > + thermal_reg_write(priv, REG_GEN3_IRQMSK, > + enable ? (TEMP_IRQ_SHIFT(priv->id) | > + TEMPD_IRQ_SHIFT(priv->id)) : 0); > + > + spin_unlock_irqrestore(&priv->lock, flags); > +} > + > +static void rcar_gen3_thermal_work(struct work_struct *work) > +{ > + struct rcar_gen3_thermal_priv *priv; > + > + priv = container_of(work, struct rcar_gen3_thermal_priv, > work.work); > + > + thermal_zone_device_update(priv->zone); > + > + rcar_gen3_thermal_irq_enable(priv); > +} > + > +static irqreturn_t rcar_gen3_thermal_irq(int irq, void *data) > +{ > + struct rcar_gen3_thermal_priv *priv = data; > + unsigned long flags; > + int status; > + > + spin_lock_irqsave(&priv->lock, flags); > + > + status = thermal_reg_read(priv, REG_GEN3_IRQSTR); > + thermal_reg_write(priv, REG_GEN3_IRQSTR, 0); > + > + spin_unlock_irqrestore(&priv->lock, flags); > + > + if ((status & TEMP_IRQ_SHIFT(priv->id)) || > + (status & TEMPD_IRQ_SHIFT(priv->id))) { > + rcar_gen3_thermal_irq_disable(priv); > + schedule_delayed_work(&priv->work, > +       msecs_to_jiffies(300)); > + } > + > + return IRQ_HANDLED; > +} > + > +static struct thermal_zone_of_device_ops rcar_gen3_tz_of_ops = { > + .get_temp = rcar_gen3_thermal_get_temp, > +}; > + > +/* > + * Platform functions > + */ > +static int rcar_gen3_thermal_remove(struct platform_device *pdev) > +{ > + struct rcar_gen3_thermal_priv *priv = > platform_get_drvdata(pdev); > + struct device *dev = &pdev->dev; > + > + rcar_gen3_thermal_irq_disable(priv); > + thermal_zone_of_sensor_unregister(dev, priv->zone); > + > + pm_runtime_put(dev); > + pm_runtime_disable(dev); > + > + return 0; > +} > + > +static const struct rcar_gen3_thermal_data r8a7795_data = { > + .thermal_init = r8a7795_thermal_init, > +}; > + > +static const struct rcar_gen3_thermal_data r8a7796_data = { > + .thermal_init = r8a7796_thermal_init, > +}; > + > +static const struct of_device_id rcar_gen3_thermal_dt_ids[] = { > + { .compatible = "renesas,r8a7795-thermal", .data = > &r8a7795_data}, > + { .compatible = "renesas,r8a7796-thermal", .data = > &r8a7796_data}, > + {}, > +}; > +MODULE_DEVICE_TABLE(of, rcar_gen3_thermal_dt_ids); > + > +static int rcar_gen3_thermal_probe(struct platform_device *pdev) > +{ > + struct rcar_gen3_thermal_priv *priv; > + struct device *dev = &pdev->dev; > + struct resource *res, *irq; > + int ret = -ENODEV; > + int idle; > + struct device_node *tz_nd, *tmp_nd; > + struct thermal_zone_device *zone; > + > + priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); > + if (!priv) > + return -ENOMEM; > + > + platform_set_drvdata(pdev, priv); > + > + priv->dev = dev; > + > + pm_runtime_enable(dev); > + pm_runtime_get_sync(dev); > + > + priv->data = of_device_get_match_data(dev); > + if (!priv->data) > + goto error_unregister; > + > + priv->irq = 0; > + > + irq = platform_get_resource(pdev, IORESOURCE_IRQ, 0); > + if (irq) { > + priv->irq = 1; > + for_each_node_with_property(tz_nd, "polling-delay") > { > + tmp_nd = of_parse_phandle(tz_nd, > + "thermal-sensors", 0); > + if (tmp_nd && !strcmp(tmp_nd->full_name, > + dev->of_node->full_name)) { > + of_property_read_u32(tz_nd, > "polling-delay", > + &idle); > + if (idle > 0) > + priv->irq = 0; > + break; > + } > + } > + } > + > + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); > + if (!res) > + goto error_unregister; > + > + priv->base = devm_ioremap_resource(dev, res); > + if (IS_ERR(priv->base)) { > + ret = PTR_ERR(priv->base); > + goto error_unregister; > + } > + > + spin_lock_init(&priv->lock); > + INIT_DELAYED_WORK(&priv->work, rcar_gen3_thermal_work); > + > + priv->id = of_alias_get_id(dev->of_node, "tsc"); > + if (priv->id < 0) { > + dev_err(dev, "Failed to get alias id (%d)\n", priv- > >id); > + ret = priv->id; > + goto error_unregister; > + } > + > + zone = devm_thermal_zone_of_sensor_register(dev, 0, priv, > + &rcar_gen3_tz_of_ops); > + > + if (IS_ERR(zone)) { > + dev_err(dev, "Can't register thermal zone\n"); > + ret = PTR_ERR(zone); > + goto error_unregister; > + } > + priv->zone = zone; > + > + priv->data->thermal_init(priv); > + > + ret = _read_fuse_factor(priv); > + if (ret) > + goto error_unregister; > + > + _linear_coefficient_calculation(priv); > + > + ret = rcar_gen3_thermal_update_temp(priv); > + > + if (ret < 0) > + goto error_unregister; > + > + > + rcar_gen3_thermal_irq_enable(priv); > + > + /* Interrupt */ > + if (irq) { > + ret = devm_request_irq(dev, irq->start, > + rcar_gen3_thermal_irq, 0, > +        dev_name(dev), priv); > + if (ret) { > + dev_err(dev, "IRQ request failed\n "); > + goto error_unregister; > + } > + } > + > + dev_info(dev, "probed\n"); > + > + return 0; > + > +error_unregister: > + rcar_gen3_thermal_remove(pdev); > + > + return ret; > +} > + > +static struct platform_driver rcar_gen3_thermal_driver = { > + .driver = { > + .name = "rcar_gen3_thermal", > + .of_match_table = rcar_gen3_thermal_dt_ids, > + }, > + .probe = rcar_gen3_thermal_probe, > + .remove = rcar_gen3_thermal_remove, > +}; > +module_platform_driver(rcar_gen3_thermal_driver); > + > +MODULE_LICENSE("GPL v2"); > +MODULE_DESCRIPTION("R-Car Gen3 THS thermal sensor driver"); > +MODULE_AUTHOR("Khiem Nguyen "); > --  > 1.9.1 > -- To unsubscribe from this list: send the line "unsubscribe devicetree" in the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org More majordomo info at http://vger.kernel.org/majordomo-info.html