On 六, 2016-09-03 at 05:25 +0000, Khiem Nguyen wrote:
> Signed-off-by: Hien Dang <[email protected]>
> Signed-off-by: Thao Nguyen <[email protected]>
> Signed-off-by: Khiem Nguyen <[email protected]>

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 <[email protected]>");
> -- 
> 1.9.1
> 

Reply via email to