Re: [PATCH 2/5 v2] thermal: rcar_gen3_thermal: Add R-Car Gen3 thermal driver

From: Zhang Rui
Date: Mon Sep 05 2016 - 20:51:33 EST


On å, 2016-09-03 at 05:25 +0000, Khiem Nguyen wrote:
> Signed-off-by: Hien Dang <hien.dang.eb@xxxxxxxxxxx>
> Signed-off-by: Thao Nguyen <thao.nguyen.yb@xxxxxxxxxxxxxxx>
> Signed-off-by: Khiem Nguyen <khiem.nguyen.xt@xxxxxxxxxxx>

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 <linux/delay.h>
> +#include <linux/err.h>
> +#include <linux/irq.h>
> +#include <linux/interrupt.h>
> +#include <linux/io.h>
> +#include <linux/module.h>
> +#include <linux/of_device.h>
> +#include <linux/platform_device.h>
> +#include <linux/pm_runtime.h>
> +#include <linux/spinlock.h>
> +#include <linux/thermal.h>
> +
> +/* 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 <khiem.nguyen.xt@xxxxxxxxxxx>");
> --Â
> 1.9.1
>