From d2a73e225d113fdccd80373ad9aeb2b58b32a30b Mon Sep 17 00:00:00 2001 From: "kuninori.morimoto.gx@renesas.com" Date: Sun, 2 Dec 2012 18:48:41 -0800 Subject: thermal: rcar: add .get_trip_type/temp and .notify support This patch adds .get_trip_type(), .get_trip_temp(), and .notify() on rcar_thermal_zone_ops. Driver will try platform power OFF if it reached to critical temperature. Signed-off-by: Kuninori Morimoto Signed-off-by: Zhang Rui --- drivers/thermal/rcar_thermal.c | 68 ++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 65 insertions(+), 3 deletions(-) (limited to 'drivers/thermal/rcar_thermal.c') diff --git a/drivers/thermal/rcar_thermal.c b/drivers/thermal/rcar_thermal.c index 90db951725da..89979ff10e27 100644 --- a/drivers/thermal/rcar_thermal.c +++ b/drivers/thermal/rcar_thermal.c @@ -22,10 +22,13 @@ #include #include #include +#include #include #include #include +#define IDLE_INTERVAL 5000 + #define THSCR 0x2c #define THSSR 0x30 @@ -176,8 +179,66 @@ static int rcar_thermal_get_temp(struct thermal_zone_device *zone, return 0; } +static int rcar_thermal_get_trip_type(struct thermal_zone_device *zone, + int trip, enum thermal_trip_type *type) +{ + struct rcar_thermal_priv *priv = rcar_zone_to_priv(zone); + + /* see rcar_thermal_get_temp() */ + switch (trip) { + case 0: /* +90 <= temp */ + *type = THERMAL_TRIP_CRITICAL; + break; + default: + dev_err(priv->dev, "rcar driver trip error\n"); + return -EINVAL; + } + + return 0; +} + +static int rcar_thermal_get_trip_temp(struct thermal_zone_device *zone, + int trip, unsigned long *temp) +{ + struct rcar_thermal_priv *priv = rcar_zone_to_priv(zone); + + /* see rcar_thermal_get_temp() */ + switch (trip) { + case 0: /* +90 <= temp */ + *temp = MCELSIUS(90); + break; + default: + dev_err(priv->dev, "rcar driver trip error\n"); + return -EINVAL; + } + + return 0; +} + +static int rcar_thermal_notify(struct thermal_zone_device *zone, + int trip, enum thermal_trip_type type) +{ + struct rcar_thermal_priv *priv = rcar_zone_to_priv(zone); + + switch (type) { + case THERMAL_TRIP_CRITICAL: + /* FIXME */ + dev_warn(priv->dev, + "Thermal reached to critical temperature\n"); + machine_power_off(); + break; + default: + break; + } + + return 0; +} + static struct thermal_zone_device_ops rcar_thermal_zone_ops = { - .get_temp = rcar_thermal_get_temp, + .get_temp = rcar_thermal_get_temp, + .get_trip_type = rcar_thermal_get_trip_type, + .get_trip_temp = rcar_thermal_get_trip_temp, + .notify = rcar_thermal_notify, }; /* @@ -211,8 +272,9 @@ static int rcar_thermal_probe(struct platform_device *pdev) return -ENOMEM; } - zone = thermal_zone_device_register("rcar_thermal", 0, 0, priv, - &rcar_thermal_zone_ops, NULL, 0, 0); + zone = thermal_zone_device_register("rcar_thermal", 1, 0, priv, + &rcar_thermal_zone_ops, NULL, 0, + IDLE_INTERVAL); if (IS_ERR(zone)) { dev_err(&pdev->dev, "thermal zone device is NULL\n"); return PTR_ERR(zone); -- cgit v1.2.3 From 9dde8f86085d283042718f88eed017eccad73ab9 Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Thu, 31 Jan 2013 09:02:51 +0000 Subject: thermal: rcar: use parenthesis on macro Signed-off-by: Kuninori Morimoto Signed-off-by: Zhang Rui --- drivers/thermal/rcar_thermal.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/thermal/rcar_thermal.c') diff --git a/drivers/thermal/rcar_thermal.c b/drivers/thermal/rcar_thermal.c index 89979ff10e27..47b2b227c91e 100644 --- a/drivers/thermal/rcar_thermal.c +++ b/drivers/thermal/rcar_thermal.c @@ -47,7 +47,7 @@ struct rcar_thermal_priv { }; #define MCELSIUS(temp) ((temp) * 1000) -#define rcar_zone_to_priv(zone) (zone->devdata) +#define rcar_zone_to_priv(zone) ((zone)->devdata) /* * basic functions -- cgit v1.2.3 From f8f53e1874c2dfddf4c6dc69008ba85d6de4d944 Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Thu, 31 Jan 2013 09:03:11 +0000 Subject: thermal: rcar: enable CPCTL to use hardware TSC deciding If CPCTL was 1 on R-Car thermal, the thermal comparator offset is automatically decided by hardware. And this CPCTL is the conditions which validate interrupt. This patch enabled CPCTL. Signed-off-by: Kuninori Morimoto Signed-off-by: Zhang Rui --- drivers/thermal/rcar_thermal.c | 91 ++++++++++++------------------------------ 1 file changed, 26 insertions(+), 65 deletions(-) (limited to 'drivers/thermal/rcar_thermal.c') diff --git a/drivers/thermal/rcar_thermal.c b/drivers/thermal/rcar_thermal.c index 47b2b227c91e..068b2a1c5c15 100644 --- a/drivers/thermal/rcar_thermal.c +++ b/drivers/thermal/rcar_thermal.c @@ -33,7 +33,7 @@ #define THSSR 0x30 /* THSCR */ -#define CPTAP 0xf +#define CPCTL (1 << 12) /* THSSR */ #define CTEMP 0x3f @@ -43,11 +43,11 @@ struct rcar_thermal_priv { void __iomem *base; struct device *dev; spinlock_t lock; - u32 comp; }; #define MCELSIUS(temp) ((temp) * 1000) #define rcar_zone_to_priv(zone) ((zone)->devdata) +#define rcar_priv_to_dev(priv) ((priv)->dev) /* * basic functions @@ -103,79 +103,41 @@ static int rcar_thermal_get_temp(struct thermal_zone_device *zone, unsigned long *temp) { struct rcar_thermal_priv *priv = rcar_zone_to_priv(zone); - int val, min, max, tmp; - - tmp = -200; /* default */ - while (1) { - if (priv->comp < 1 || priv->comp > 12) { - dev_err(priv->dev, - "THSSR invalid data (%d)\n", priv->comp); - priv->comp = 4; /* for next thermal */ - return -EINVAL; - } - - /* - * THS comparator offset and the reference temperature - * - * Comparator | reference | Temperature field - * offset | temperature | measurement - * | (degrees C) | (degrees C) - * -------------+---------------+------------------- - * 1 | -45 | -45 to -30 - * 2 | -30 | -30 to -15 - * 3 | -15 | -15 to 0 - * 4 | 0 | 0 to +15 - * 5 | +15 | +15 to +30 - * 6 | +30 | +30 to +45 - * 7 | +45 | +45 to +60 - * 8 | +60 | +60 to +75 - * 9 | +75 | +75 to +90 - * 10 | +90 | +90 to +105 - * 11 | +105 | +105 to +120 - * 12 | +120 | +120 to +135 - */ - - /* calculate thermal limitation */ - min = (priv->comp * 15) - 60; - max = min + 15; - + struct device *dev = rcar_priv_to_dev(priv); + int i; + int ctemp, old, new; + + /* + * TSC decides a value of CPTAP automatically, + * and this is the conditions which validate interrupt. + */ + rcar_thermal_bset(priv, THSCR, CPCTL, CPCTL); + + ctemp = 0; + old = ~0; + for (i = 0; i < 128; i++) { /* * we need to wait 300us after changing comparator offset * to get stable temperature. * see "Usage Notes" on datasheet */ - rcar_thermal_bset(priv, THSCR, CPTAP, priv->comp); udelay(300); - /* calculate current temperature */ - val = rcar_thermal_read(priv, THSSR) & CTEMP; - val = (val * 5) - 65; - - dev_dbg(priv->dev, "comp/min/max/val = %d/%d/%d/%d\n", - priv->comp, min, max, val); - - /* - * If val is same as min/max, then, - * it should try again on next comparator. - * But the val might be correct temperature. - * Keep it on "tmp" and compare with next val. - */ - if (tmp == val) - break; - - if (val <= min) { - tmp = min; - priv->comp--; /* try again */ - } else if (val >= max) { - tmp = max; - priv->comp++; /* try again */ - } else { - tmp = val; + new = rcar_thermal_read(priv, THSSR) & CTEMP; + if (new == old) { + ctemp = new; break; } + old = new; } - *temp = MCELSIUS(tmp); + if (!ctemp) { + dev_err(dev, "thermal sensor was broken\n"); + return -EINVAL; + } + + *temp = MCELSIUS((ctemp * 5) - 65); + return 0; } @@ -262,7 +224,6 @@ static int rcar_thermal_probe(struct platform_device *pdev) return -ENOMEM; } - priv->comp = 4; /* basic setup */ priv->dev = &pdev->dev; spin_lock_init(&priv->lock); priv->base = devm_ioremap_nocache(&pdev->dev, -- cgit v1.2.3 From b2bbc6a2ace78eaca2f6482b58b984519aa783ac Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Thu, 31 Jan 2013 09:03:22 +0000 Subject: thermal: rcar: use mutex lock instead of spin lock Current R-Car thermal driver is using spin lock for each registers read/write, but it is pointless lock. This lock is required while reading temperature, but it needs long wait (= 300ms). So, this patch used mutex lock while reading temperature, instead of spin lock for each registers. Signed-off-by: Kuninori Morimoto Signed-off-by: Zhang Rui --- drivers/thermal/rcar_thermal.c | 30 +++++++----------------------- 1 file changed, 7 insertions(+), 23 deletions(-) (limited to 'drivers/thermal/rcar_thermal.c') diff --git a/drivers/thermal/rcar_thermal.c b/drivers/thermal/rcar_thermal.c index 068b2a1c5c15..e19b267f76d6 100644 --- a/drivers/thermal/rcar_thermal.c +++ b/drivers/thermal/rcar_thermal.c @@ -42,7 +42,7 @@ struct rcar_thermal_priv { void __iomem *base; struct device *dev; - spinlock_t lock; + struct mutex lock; }; #define MCELSIUS(temp) ((temp) * 1000) @@ -54,46 +54,26 @@ struct rcar_thermal_priv { */ static u32 rcar_thermal_read(struct rcar_thermal_priv *priv, u32 reg) { - unsigned long flags; - u32 ret; - - spin_lock_irqsave(&priv->lock, flags); - - ret = ioread32(priv->base + reg); - - spin_unlock_irqrestore(&priv->lock, flags); - - return ret; + return ioread32(priv->base + reg); } #if 0 /* no user at this point */ static void rcar_thermal_write(struct rcar_thermal_priv *priv, u32 reg, u32 data) { - unsigned long flags; - - spin_lock_irqsave(&priv->lock, flags); - iowrite32(data, priv->base + reg); - - spin_unlock_irqrestore(&priv->lock, flags); } #endif static void rcar_thermal_bset(struct rcar_thermal_priv *priv, u32 reg, u32 mask, u32 data) { - unsigned long flags; u32 val; - spin_lock_irqsave(&priv->lock, flags); - val = ioread32(priv->base + reg); val &= ~mask; val |= (data & mask); iowrite32(val, priv->base + reg); - - spin_unlock_irqrestore(&priv->lock, flags); } /* @@ -107,6 +87,8 @@ static int rcar_thermal_get_temp(struct thermal_zone_device *zone, int i; int ctemp, old, new; + mutex_lock(&priv->lock); + /* * TSC decides a value of CPTAP automatically, * and this is the conditions which validate interrupt. @@ -138,6 +120,8 @@ static int rcar_thermal_get_temp(struct thermal_zone_device *zone, *temp = MCELSIUS((ctemp * 5) - 65); + mutex_unlock(&priv->lock); + return 0; } @@ -225,7 +209,7 @@ static int rcar_thermal_probe(struct platform_device *pdev) } priv->dev = &pdev->dev; - spin_lock_init(&priv->lock); + mutex_init(&priv->lock); priv->base = devm_ioremap_nocache(&pdev->dev, res->start, resource_size(res)); if (!priv->base) { -- cgit v1.2.3 From 3676d1dd3d3069ca70b8075c0e86482cbaa01c2f Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Thu, 31 Jan 2013 09:03:33 +0000 Subject: thermal: rcar: multi channel support R-Car thermal sensor will be multi channel sensor in next generation. But "IRQ controlling method" and "register mapping" are different between old/new chip. This patch adds multi sensor support. Then, this driver assumes there is common register if platform has IRQ resource. The IRQ will be supported soon. Signed-off-by: Kuninori Morimoto Signed-off-by: Zhang Rui --- drivers/thermal/rcar_thermal.c | 128 ++++++++++++++++++++++++++++++----------- 1 file changed, 94 insertions(+), 34 deletions(-) (limited to 'drivers/thermal/rcar_thermal.c') diff --git a/drivers/thermal/rcar_thermal.c b/drivers/thermal/rcar_thermal.c index e19b267f76d6..1ba02770153a 100644 --- a/drivers/thermal/rcar_thermal.c +++ b/drivers/thermal/rcar_thermal.c @@ -38,16 +38,27 @@ /* THSSR */ #define CTEMP 0x3f +struct rcar_thermal_common { + void __iomem *base; + struct device *dev; + struct list_head head; +}; struct rcar_thermal_priv { void __iomem *base; - struct device *dev; + struct rcar_thermal_common *common; + struct thermal_zone_device *zone; struct mutex lock; + struct list_head list; }; +#define rcar_thermal_for_each_priv(pos, common) \ + list_for_each_entry(pos, &common->head, list) + #define MCELSIUS(temp) ((temp) * 1000) #define rcar_zone_to_priv(zone) ((zone)->devdata) -#define rcar_priv_to_dev(priv) ((priv)->dev) +#define rcar_priv_to_dev(priv) ((priv)->common->dev) +#define rcar_has_irq_support(priv) ((priv)->common->base) /* * basic functions @@ -129,6 +140,7 @@ static int rcar_thermal_get_trip_type(struct thermal_zone_device *zone, int trip, enum thermal_trip_type *type) { struct rcar_thermal_priv *priv = rcar_zone_to_priv(zone); + struct device *dev = rcar_priv_to_dev(priv); /* see rcar_thermal_get_temp() */ switch (trip) { @@ -136,7 +148,7 @@ static int rcar_thermal_get_trip_type(struct thermal_zone_device *zone, *type = THERMAL_TRIP_CRITICAL; break; default: - dev_err(priv->dev, "rcar driver trip error\n"); + dev_err(dev, "rcar driver trip error\n"); return -EINVAL; } @@ -147,6 +159,7 @@ static int rcar_thermal_get_trip_temp(struct thermal_zone_device *zone, int trip, unsigned long *temp) { struct rcar_thermal_priv *priv = rcar_zone_to_priv(zone); + struct device *dev = rcar_priv_to_dev(priv); /* see rcar_thermal_get_temp() */ switch (trip) { @@ -154,7 +167,7 @@ static int rcar_thermal_get_trip_temp(struct thermal_zone_device *zone, *temp = MCELSIUS(90); break; default: - dev_err(priv->dev, "rcar driver trip error\n"); + dev_err(dev, "rcar driver trip error\n"); return -EINVAL; } @@ -165,12 +178,12 @@ static int rcar_thermal_notify(struct thermal_zone_device *zone, int trip, enum thermal_trip_type type) { struct rcar_thermal_priv *priv = rcar_zone_to_priv(zone); + struct device *dev = rcar_priv_to_dev(priv); switch (type) { case THERMAL_TRIP_CRITICAL: /* FIXME */ - dev_warn(priv->dev, - "Thermal reached to critical temperature\n"); + dev_warn(dev, "Thermal reached to critical temperature\n"); machine_power_off(); break; default: @@ -192,51 +205,98 @@ static struct thermal_zone_device_ops rcar_thermal_zone_ops = { */ static int rcar_thermal_probe(struct platform_device *pdev) { - struct thermal_zone_device *zone; + struct rcar_thermal_common *common; struct rcar_thermal_priv *priv; - struct resource *res; - - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - if (!res) { - dev_err(&pdev->dev, "Could not get platform resource\n"); - return -ENODEV; - } + struct device *dev = &pdev->dev; + struct resource *res, *irq; + int mres = 0; + int i; - priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL); - if (!priv) { - dev_err(&pdev->dev, "Could not allocate priv\n"); + common = devm_kzalloc(dev, sizeof(*common), GFP_KERNEL); + if (!common) { + dev_err(dev, "Could not allocate common\n"); return -ENOMEM; } - priv->dev = &pdev->dev; - mutex_init(&priv->lock); - priv->base = devm_ioremap_nocache(&pdev->dev, - res->start, resource_size(res)); - if (!priv->base) { - dev_err(&pdev->dev, "Unable to ioremap thermal register\n"); - return -ENOMEM; + INIT_LIST_HEAD(&common->head); + common->dev = dev; + + irq = platform_get_resource(pdev, IORESOURCE_IRQ, 0); + if (irq) { + /* + * platform has IRQ support. + * Then, drier use common register + */ + res = platform_get_resource(pdev, IORESOURCE_MEM, mres++); + if (!res) { + dev_err(dev, "Could not get platform resource\n"); + return -ENODEV; + } + + /* + * rcar_has_irq_support() will be enabled + */ + common->base = devm_request_and_ioremap(dev, res); + if (!common->base) { + dev_err(dev, "Unable to ioremap thermal register\n"); + return -ENOMEM; + } } - zone = thermal_zone_device_register("rcar_thermal", 1, 0, priv, - &rcar_thermal_zone_ops, NULL, 0, - IDLE_INTERVAL); - if (IS_ERR(zone)) { - dev_err(&pdev->dev, "thermal zone device is NULL\n"); - return PTR_ERR(zone); + for (i = 0;; i++) { + res = platform_get_resource(pdev, IORESOURCE_MEM, mres++); + if (!res) + break; + + priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); + if (!priv) { + dev_err(dev, "Could not allocate priv\n"); + return -ENOMEM; + } + + priv->base = devm_request_and_ioremap(dev, res); + if (!priv->base) { + dev_err(dev, "Unable to ioremap priv register\n"); + return -ENOMEM; + } + + priv->common = common; + mutex_init(&priv->lock); + INIT_LIST_HEAD(&priv->list); + + priv->zone = thermal_zone_device_register("rcar_thermal", + 1, 0, priv, + &rcar_thermal_zone_ops, NULL, 0, + IDLE_INTERVAL); + if (IS_ERR(priv->zone)) { + dev_err(dev, "can't register thermal zone\n"); + goto error_unregister; + } + + list_move_tail(&priv->list, &common->head); } - platform_set_drvdata(pdev, zone); + platform_set_drvdata(pdev, common); - dev_info(&pdev->dev, "proved\n"); + dev_info(dev, "%d sensor proved\n", i); return 0; + +error_unregister: + rcar_thermal_for_each_priv(priv, common) + thermal_zone_device_unregister(priv->zone); + + return -ENODEV; } static int rcar_thermal_remove(struct platform_device *pdev) { - struct thermal_zone_device *zone = platform_get_drvdata(pdev); + struct rcar_thermal_common *common = platform_get_drvdata(pdev); + struct rcar_thermal_priv *priv; + + rcar_thermal_for_each_priv(priv, common) + thermal_zone_device_unregister(priv->zone); - thermal_zone_device_unregister(zone); platform_set_drvdata(pdev, NULL); return 0; -- cgit v1.2.3 From e9137a582fcbe36d9dedb8d7f6902a059154b14e Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Thu, 31 Jan 2013 09:03:46 +0000 Subject: thermal: rcar: add read/write functions for common/priv data R-Car thermal driver will use struct common in next feature (interrupt support). But the register address is different between struct priv and common. This patch adds read/write functions for struct common, and use macro technique to avoid wrong register access. This is preparation patch for next feature (interrupt support), therefore, there is no user to use this common read/write function at this point. Signed-off-by: Kuninori Morimoto Signed-off-by: Zhang Rui --- drivers/thermal/rcar_thermal.c | 48 ++++++++++++++++++++++++++++++++++++------ 1 file changed, 41 insertions(+), 7 deletions(-) (limited to 'drivers/thermal/rcar_thermal.c') diff --git a/drivers/thermal/rcar_thermal.c b/drivers/thermal/rcar_thermal.c index 1ba02770153a..cf6aa98956b9 100644 --- a/drivers/thermal/rcar_thermal.c +++ b/drivers/thermal/rcar_thermal.c @@ -29,8 +29,8 @@ #define IDLE_INTERVAL 5000 -#define THSCR 0x2c -#define THSSR 0x30 +#define REG_THSCR 0x2c +#define REG_THSSR 0x30 /* THSCR */ #define CPCTL (1 << 12) @@ -63,21 +63,55 @@ struct rcar_thermal_priv { /* * basic functions */ -static u32 rcar_thermal_read(struct rcar_thermal_priv *priv, u32 reg) +#if 0 +#define rcar_thermal_common_read(c, r) \ + _rcar_thermal_common_read(c, COMMON_ ##r) +static u32 _rcar_thermal_common_read(struct rcar_thermal_common *common, + u32 reg) +{ + return ioread32(common->base + reg); +} + +#define rcar_thermal_common_write(c, r, d) \ + _rcar_thermal_common_write(c, COMMON_ ##r, d) +static void _rcar_thermal_common_write(struct rcar_thermal_common *common, + u32 reg, u32 data) +{ + iowrite32(data, common->base + reg); +} + +#define rcar_thermal_common_bset(c, r, m, d) \ + _rcar_thermal_common_bset(c, COMMON_ ##r, m, d) +static void _rcar_thermal_common_bset(struct rcar_thermal_common *common, + u32 reg, u32 mask, u32 data) +{ + u32 val; + + val = ioread32(common->base + reg); + val &= ~mask; + val |= (data & mask); + iowrite32(val, common->base + reg); +} +#endif + +#define rcar_thermal_read(p, r) _rcar_thermal_read(p, REG_ ##r) +static u32 _rcar_thermal_read(struct rcar_thermal_priv *priv, u32 reg) { return ioread32(priv->base + reg); } #if 0 /* no user at this point */ -static void rcar_thermal_write(struct rcar_thermal_priv *priv, - u32 reg, u32 data) +#define rcar_thermal_write(p, r, d) _rcar_thermal_write(p, REG_ ##r, d) +static void _rcar_thermal_write(struct rcar_thermal_priv *priv, + u32 reg, u32 data) { iowrite32(data, priv->base + reg); } #endif -static void rcar_thermal_bset(struct rcar_thermal_priv *priv, u32 reg, - u32 mask, u32 data) +#define rcar_thermal_bset(p, r, m, d) _rcar_thermal_bset(p, REG_ ##r, m, d) +static void _rcar_thermal_bset(struct rcar_thermal_priv *priv, u32 reg, + u32 mask, u32 data) { u32 val; -- cgit v1.2.3 From e0a5172e9eec7f0d3c476e013c51dab62f3fc666 Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Thu, 31 Jan 2013 09:04:48 +0000 Subject: thermal: rcar: add interrupt support This patch adds interrupt support for R-Car thermal driver. New generation R-Car thermal sensor interrupt controller was different from old generation. This patch supports new generation sensor only, since the old generation interrupt controller had never been used before, and will never be used in the future. Signed-off-by: Kuninori Morimoto Signed-off-by: Zhang Rui --- drivers/thermal/rcar_thermal.c | 159 ++++++++++++++++++++++++++++++++++++++--- 1 file changed, 150 insertions(+), 9 deletions(-) (limited to 'drivers/thermal/rcar_thermal.c') diff --git a/drivers/thermal/rcar_thermal.c b/drivers/thermal/rcar_thermal.c index cf6aa98956b9..80aae3cf65eb 100644 --- a/drivers/thermal/rcar_thermal.c +++ b/drivers/thermal/rcar_thermal.c @@ -19,6 +19,8 @@ */ #include #include +#include +#include #include #include #include @@ -29,8 +31,15 @@ #define IDLE_INTERVAL 5000 +#define COMMON_STR 0x00 +#define COMMON_ENR 0x04 +#define COMMON_INTMSK 0x0c + +#define REG_POSNEG 0x20 +#define REG_FILONOFF 0x28 #define REG_THSCR 0x2c #define REG_THSSR 0x30 +#define REG_INTCTRL 0x34 /* THSCR */ #define CPCTL (1 << 12) @@ -42,14 +51,18 @@ struct rcar_thermal_common { void __iomem *base; struct device *dev; struct list_head head; + spinlock_t lock; }; struct rcar_thermal_priv { void __iomem *base; struct rcar_thermal_common *common; struct thermal_zone_device *zone; + struct delayed_work work; struct mutex lock; struct list_head list; + int id; + int ctemp; }; #define rcar_thermal_for_each_priv(pos, common) \ @@ -59,11 +72,17 @@ struct rcar_thermal_priv { #define rcar_zone_to_priv(zone) ((zone)->devdata) #define rcar_priv_to_dev(priv) ((priv)->common->dev) #define rcar_has_irq_support(priv) ((priv)->common->base) +#define rcar_id_to_shift(priv) ((priv)->id * 8) + +#ifdef DEBUG +# define rcar_force_update_temp(priv) 1 +#else +# define rcar_force_update_temp(priv) 0 +#endif /* * basic functions */ -#if 0 #define rcar_thermal_common_read(c, r) \ _rcar_thermal_common_read(c, COMMON_ ##r) static u32 _rcar_thermal_common_read(struct rcar_thermal_common *common, @@ -92,7 +111,6 @@ static void _rcar_thermal_common_bset(struct rcar_thermal_common *common, val |= (data & mask); iowrite32(val, common->base + reg); } -#endif #define rcar_thermal_read(p, r) _rcar_thermal_read(p, REG_ ##r) static u32 _rcar_thermal_read(struct rcar_thermal_priv *priv, u32 reg) @@ -100,14 +118,12 @@ static u32 _rcar_thermal_read(struct rcar_thermal_priv *priv, u32 reg) return ioread32(priv->base + reg); } -#if 0 /* no user at this point */ #define rcar_thermal_write(p, r, d) _rcar_thermal_write(p, REG_ ##r, d) static void _rcar_thermal_write(struct rcar_thermal_priv *priv, u32 reg, u32 data) { iowrite32(data, priv->base + reg); } -#endif #define rcar_thermal_bset(p, r, m, d) _rcar_thermal_bset(p, REG_ ##r, m, d) static void _rcar_thermal_bset(struct rcar_thermal_priv *priv, u32 reg, @@ -124,10 +140,8 @@ static void _rcar_thermal_bset(struct rcar_thermal_priv *priv, u32 reg, /* * zone device functions */ -static int rcar_thermal_get_temp(struct thermal_zone_device *zone, - unsigned long *temp) +static int rcar_thermal_update_temp(struct rcar_thermal_priv *priv) { - struct rcar_thermal_priv *priv = rcar_zone_to_priv(zone); struct device *dev = rcar_priv_to_dev(priv); int i; int ctemp, old, new; @@ -163,13 +177,42 @@ static int rcar_thermal_get_temp(struct thermal_zone_device *zone, return -EINVAL; } - *temp = MCELSIUS((ctemp * 5) - 65); + /* + * enable IRQ + */ + if (rcar_has_irq_support(priv)) { + rcar_thermal_write(priv, FILONOFF, 0); + + /* enable Rising/Falling edge interrupt */ + rcar_thermal_write(priv, POSNEG, 0x1); + rcar_thermal_write(priv, INTCTRL, (((ctemp - 0) << 8) | + ((ctemp - 1) << 0))); + } + + dev_dbg(dev, "thermal%d %d -> %d\n", priv->id, priv->ctemp, ctemp); + + priv->ctemp = ctemp; mutex_unlock(&priv->lock); return 0; } +static int rcar_thermal_get_temp(struct thermal_zone_device *zone, + unsigned long *temp) +{ + struct rcar_thermal_priv *priv = rcar_zone_to_priv(zone); + + if (!rcar_has_irq_support(priv) || rcar_force_update_temp(priv)) + rcar_thermal_update_temp(priv); + + mutex_lock(&priv->lock); + *temp = MCELSIUS((priv->ctemp * 5) - 65); + mutex_unlock(&priv->lock); + + return 0; +} + static int rcar_thermal_get_trip_type(struct thermal_zone_device *zone, int trip, enum thermal_trip_type *type) { @@ -234,6 +277,82 @@ static struct thermal_zone_device_ops rcar_thermal_zone_ops = { .notify = rcar_thermal_notify, }; +/* + * interrupt + */ +#define rcar_thermal_irq_enable(p) _rcar_thermal_irq_ctrl(p, 1) +#define rcar_thermal_irq_disable(p) _rcar_thermal_irq_ctrl(p, 0) +static void _rcar_thermal_irq_ctrl(struct rcar_thermal_priv *priv, int enable) +{ + struct rcar_thermal_common *common = priv->common; + unsigned long flags; + u32 mask = 0x3 << rcar_id_to_shift(priv); /* enable Rising/Falling */ + + spin_lock_irqsave(&common->lock, flags); + + rcar_thermal_common_bset(common, INTMSK, mask, enable ? 0 : mask); + + spin_unlock_irqrestore(&common->lock, flags); +} + +static void rcar_thermal_work(struct work_struct *work) +{ + struct rcar_thermal_priv *priv; + + priv = container_of(work, struct rcar_thermal_priv, work.work); + + rcar_thermal_update_temp(priv); + rcar_thermal_irq_enable(priv); + thermal_zone_device_update(priv->zone); +} + +static u32 rcar_thermal_had_changed(struct rcar_thermal_priv *priv, u32 status) +{ + struct device *dev = rcar_priv_to_dev(priv); + + status = (status >> rcar_id_to_shift(priv)) & 0x3; + + if (status & 0x3) { + dev_dbg(dev, "thermal%d %s%s\n", + priv->id, + (status & 0x2) ? "Rising " : "", + (status & 0x1) ? "Falling" : ""); + } + + return status; +} + +static irqreturn_t rcar_thermal_irq(int irq, void *data) +{ + struct rcar_thermal_common *common = data; + struct rcar_thermal_priv *priv; + unsigned long flags; + u32 status, mask; + + spin_lock_irqsave(&common->lock, flags); + + mask = rcar_thermal_common_read(common, INTMSK); + status = rcar_thermal_common_read(common, STR); + rcar_thermal_common_write(common, STR, 0x000F0F0F & mask); + + spin_unlock_irqrestore(&common->lock, flags); + + status = status & ~mask; + + /* + * check the status + */ + rcar_thermal_for_each_priv(priv, common) { + if (rcar_thermal_had_changed(priv, status)) { + rcar_thermal_irq_disable(priv); + schedule_delayed_work(&priv->work, + msecs_to_jiffies(300)); + } + } + + return IRQ_HANDLED; +} + /* * platform functions */ @@ -245,6 +364,7 @@ static int rcar_thermal_probe(struct platform_device *pdev) struct resource *res, *irq; int mres = 0; int i; + int idle = IDLE_INTERVAL; common = devm_kzalloc(dev, sizeof(*common), GFP_KERNEL); if (!common) { @@ -253,10 +373,13 @@ static int rcar_thermal_probe(struct platform_device *pdev) } INIT_LIST_HEAD(&common->head); + spin_lock_init(&common->lock); common->dev = dev; irq = platform_get_resource(pdev, IORESOURCE_IRQ, 0); if (irq) { + int ret; + /* * platform has IRQ support. * Then, drier use common register @@ -267,6 +390,13 @@ static int rcar_thermal_probe(struct platform_device *pdev) return -ENODEV; } + ret = devm_request_irq(dev, irq->start, rcar_thermal_irq, 0, + dev_name(dev), common); + if (ret) { + dev_err(dev, "irq request failed\n "); + return ret; + } + /* * rcar_has_irq_support() will be enabled */ @@ -275,6 +405,11 @@ static int rcar_thermal_probe(struct platform_device *pdev) dev_err(dev, "Unable to ioremap thermal register\n"); return -ENOMEM; } + + /* enable temperature comparation */ + rcar_thermal_common_write(common, ENR, 0x00030303); + + idle = 0; /* polling delaye is not needed */ } for (i = 0;; i++) { @@ -295,19 +430,25 @@ static int rcar_thermal_probe(struct platform_device *pdev) } priv->common = common; + priv->id = i; mutex_init(&priv->lock); INIT_LIST_HEAD(&priv->list); + INIT_DELAYED_WORK(&priv->work, rcar_thermal_work); + rcar_thermal_update_temp(priv); priv->zone = thermal_zone_device_register("rcar_thermal", 1, 0, priv, &rcar_thermal_zone_ops, NULL, 0, - IDLE_INTERVAL); + idle); if (IS_ERR(priv->zone)) { dev_err(dev, "can't register thermal zone\n"); goto error_unregister; } list_move_tail(&priv->list, &common->head); + + if (rcar_has_irq_support(priv)) + rcar_thermal_irq_enable(priv); } platform_set_drvdata(pdev, common); -- cgit v1.2.3 From e6e053f4e47634c07993cb31893556d24e18b65e Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Thu, 31 Jan 2013 09:26:13 +0000 Subject: thermal: rcar: remove machine_power_off() from rcar_thermal_notify() Machine/System power-off is run in thermal frame work if it become critical temperature. This patch removed pointless machine_power_off() from thermal_zone_device_ops :: .notify Signed-off-by: Kuninori Morimoto Signed-off-by: Zhang Rui --- drivers/thermal/rcar_thermal.c | 1 - 1 file changed, 1 deletion(-) (limited to 'drivers/thermal/rcar_thermal.c') diff --git a/drivers/thermal/rcar_thermal.c b/drivers/thermal/rcar_thermal.c index 80aae3cf65eb..2eebcadb4c99 100644 --- a/drivers/thermal/rcar_thermal.c +++ b/drivers/thermal/rcar_thermal.c @@ -261,7 +261,6 @@ static int rcar_thermal_notify(struct thermal_zone_device *zone, case THERMAL_TRIP_CRITICAL: /* FIXME */ dev_warn(dev, "Thermal reached to critical temperature\n"); - machine_power_off(); break; default: break; -- cgit v1.2.3 From 76cc1887496fe80138c6b07c37d7f81e4cf27cde Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Thu, 31 Jan 2013 09:05:26 +0000 Subject: thermal: rcar: add Device Tree support Support for loading the Renesas R-Car thermal module via devicetree. Signed-off-by: Kuninori Morimoto Signed-off-by: Zhang Rui --- .../devicetree/bindings/thermal/rcar-thermal.txt | 29 ++++++++++++++++++++++ drivers/thermal/rcar_thermal.c | 7 ++++++ 2 files changed, 36 insertions(+) create mode 100644 Documentation/devicetree/bindings/thermal/rcar-thermal.txt (limited to 'drivers/thermal/rcar_thermal.c') diff --git a/Documentation/devicetree/bindings/thermal/rcar-thermal.txt b/Documentation/devicetree/bindings/thermal/rcar-thermal.txt new file mode 100644 index 000000000000..28ef498a66e5 --- /dev/null +++ b/Documentation/devicetree/bindings/thermal/rcar-thermal.txt @@ -0,0 +1,29 @@ +* Renesas R-Car Thermal + +Required properties: +- compatible : "renesas,rcar-thermal" +- reg : Address range of the thermal registers. + The 1st reg will be recognized as common register + if it has "interrupts". + +Option properties: + +- interrupts : use interrupt + +Example (non interrupt support): + +thermal@e61f0100 { + compatible = "renesas,rcar-thermal"; + reg = <0xe61f0100 0x38>; +}; + +Example (interrupt support): + +thermal@e61f0000 { + compatible = "renesas,rcar-thermal"; + reg = <0xe61f0000 0x14 + 0xe61f0100 0x38 + 0xe61f0200 0x38 + 0xe61f0300 0x38>; + interrupts = <0 69 4>; +}; diff --git a/drivers/thermal/rcar_thermal.c b/drivers/thermal/rcar_thermal.c index 2eebcadb4c99..909bb4bb837f 100644 --- a/drivers/thermal/rcar_thermal.c +++ b/drivers/thermal/rcar_thermal.c @@ -476,9 +476,16 @@ static int rcar_thermal_remove(struct platform_device *pdev) return 0; } +static const struct of_device_id rcar_thermal_dt_ids[] __devinitconst = { + { .compatible = "renesas,rcar-thermal", }, + {}, +}; +MODULE_DEVICE_TABLE(of, rcar_thermal_dt_ids); + static struct platform_driver rcar_thermal_driver = { .driver = { .name = "rcar_thermal", + .of_match_table = rcar_thermal_dt_ids, }, .probe = rcar_thermal_probe, .remove = rcar_thermal_remove, -- cgit v1.2.3 From f5b6d45f8cf688f51140fd21f1da3b90562762a9 Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Thu, 14 Feb 2013 23:26:58 +0100 Subject: thermal: rcar: remove __devinitconst commit 76cc18874 "thermal: rcar: add Device Tree support" added device tree support for this driver, but also added an instance of __devinitconst, which is no longer defined Signed-off-by: Arnd Bergmann Cc: Kuninori Morimoto Cc: Zhang Rui --- drivers/thermal/rcar_thermal.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/thermal/rcar_thermal.c') diff --git a/drivers/thermal/rcar_thermal.c b/drivers/thermal/rcar_thermal.c index 909bb4bb837f..28f091994013 100644 --- a/drivers/thermal/rcar_thermal.c +++ b/drivers/thermal/rcar_thermal.c @@ -476,7 +476,7 @@ static int rcar_thermal_remove(struct platform_device *pdev) return 0; } -static const struct of_device_id rcar_thermal_dt_ids[] __devinitconst = { +static const struct of_device_id rcar_thermal_dt_ids[] = { { .compatible = "renesas,rcar-thermal", }, {}, }; -- cgit v1.2.3