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 > -- To unsubscribe from this list: send the line "unsubscribe devicetree" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html