Message ID | 20170306200401.29923-6-niklas.soderlund+renesas@ragnatech.se (mailing list archive) |
---|---|
State | Superseded, archived |
Delegated to: | Eduardo Valentin |
Headers | show |
On Mon, Mar 6, 2017 at 9:03 PM, Niklas Söderlund <niklas.soderlund+renesas@ragnatech.se> wrote: > Enable hardware trip points by implementing the set_trips callback. The > thermal core will take care of setting the initial trip point window and > to update it once the driver reports a TSC have moved outside it. has moved > The interrupt structure for this device is a bit odd. There is not a > dedicated IRQ for each TSC, instead the interrupts are shared between > all TSC. IRQn is fired if the temp monitored in IRQTEMPn is reached in all TSCs > any of the TSC, example IRQ3 is fired if temperature in IRQTEMP3 is TSCs > reached in either TSC0, TSC1 or TSC2. > > For this reason the usage of interrupts in this driver is a all on or an all-on or all-off > all off design. When an interrupt happens all TSC are checked and all TSCs > thermal zones are updated. This could be refined to be more fine grained > but the thermal core takes care of only updating the thermal zones that > have left there trip point window. their > Signed-off-by: Niklas Söderlund <niklas.soderlund+renesas@ragnatech.se> Reviewed-by: Geert Uytterhoeven <geert+renesas@glider.be> > --- > drivers/thermal/rcar_gen3_thermal.c | 137 ++++++++++++++++++++++++++++++++++++ > 1 file changed, 137 insertions(+) > > diff --git a/drivers/thermal/rcar_gen3_thermal.c b/drivers/thermal/rcar_gen3_thermal.c > index 65f7204936a18278..e52181015e589a7f 100644 > --- a/drivers/thermal/rcar_gen3_thermal.c > +++ b/drivers/thermal/rcar_gen3_thermal.c > @@ -76,6 +87,7 @@ struct rcar_gen3_thermal_tsc { > }; > > struct rcar_gen3_thermal_priv { > + spinlock_t lock; When declaring a spinlock, please add a comment what exactly is protected by the lock. > +static void rcar_thermal_irq_disable(struct rcar_gen3_thermal_priv *priv) > +{ > + int i; unsigned int (for all positive loop counters) > + > + for (i = 0; i < TSC_MAX_NUM; i++) > + rcar_gen3_thermal_write(priv->tscs[i], REG_GEN3_IRQMSK, 0); > +} Gr{oetje,eeting}s, Geert -- Geert Uytterhoeven -- There's lots of Linux beyond ia32 -- geert@linux-m68k.org In personal conversations with technical people, I call myself a hacker. But when I'm talking to journalists I just say "programmer" or something like that. -- Linus Torvalds
Hi Niklas, thanks for your work! On Mon, Mar 06, 2017 at 09:03:59PM +0100, Niklas Söderlund wrote: > Enable hardware trip points by implementing the set_trips callback. The > thermal core will take care of setting the initial trip point window and > to update it once the driver reports a TSC have moved outside it. > > The interrupt structure for this device is a bit odd. There is not a > dedicated IRQ for each TSC, instead the interrupts are shared between > all TSC. IRQn is fired if the temp monitored in IRQTEMPn is reached in > any of the TSC, example IRQ3 is fired if temperature in IRQTEMP3 is > reached in either TSC0, TSC1 or TSC2. > > For this reason the usage of interrupts in this driver is a all on or s/a all/an all/ > all off design. When an interrupt happens all TSC are checked and all > thermal zones are updated. This could be refined to be more fine grained > but the thermal core takes care of only updating the thermal zones that > have left there trip point window. s/there/the/ > > Signed-off-by: Niklas Söderlund <niklas.soderlund+renesas@ragnatech.se> > --- > drivers/thermal/rcar_gen3_thermal.c | 137 ++++++++++++++++++++++++++++++++++++ > 1 file changed, 137 insertions(+) > > +static int rcar_gen3_thermal_set_trips(void *devdata, int low, int high) > +{ > + struct rcar_gen3_thermal_tsc *tsc = devdata; > + > + if (low < -40000) > + low = -40000; > + if (high > 125000) > + high = 125000; Use clamp_val()? > + > + rcar_gen3_thermal_write(tsc, REG_GEN3_IRQTEMP1, > + rcar_gen3_thermal_mcelsius_to_temp(tsc, low)); > + > + rcar_gen3_thermal_write(tsc, REG_GEN3_IRQTEMP2, > + rcar_gen3_thermal_mcelsius_to_temp(tsc, high)); > + > + dev_dbg(tsc->dev, "TSC%d: low: %d high: %d\n", tsc->num, low, high); Is there no sysfs/debugfs way to get this from thermal core? I'd bet this is of generic interest. Bonus point: if we drop this dbg, we can also drop the 'dev' and 'num' members from the struct again. > + > + return 0; > +} > + > static struct thermal_zone_of_device_ops rcar_gen3_tz_of_ops = { > .get_temp = rcar_gen3_thermal_get_temp, > + .set_trips = rcar_gen3_thermal_set_trips, > }; > > +static void rcar_thermal_irq_disable(struct rcar_gen3_thermal_priv *priv) > +{ > + int i; > + > + for (i = 0; i < TSC_MAX_NUM; i++) > + rcar_gen3_thermal_write(priv->tscs[i], REG_GEN3_IRQMSK, 0); > +} > + > +static void rcar_thermal_irq_enable(struct rcar_gen3_thermal_priv *priv) > +{ > + int i; > + > + for (i = 0; i < TSC_MAX_NUM; i++) > + rcar_gen3_thermal_write(priv->tscs[i], REG_GEN3_IRQMSK, > + IRQ_TEMPD1 | IRQ_TEMP2); > +} Merge the two into one with a second parameter which is bool? rcar_gen3_irq_enable(priv, true/false)? > pm_runtime_enable(dev); > @@ -277,6 +388,8 @@ static int rcar_gen3_thermal_probe(struct platform_device *pdev) > > for (i = 0; i < TSC_MAX_NUM; i++) { > struct rcar_gen3_thermal_tsc *tsc; > + char *irqname; > + int irq; > > tsc = devm_kzalloc(dev, sizeof(*tsc), GFP_KERNEL); > if (!tsc) { > @@ -299,6 +412,25 @@ static int rcar_gen3_thermal_probe(struct platform_device *pdev) > goto error_unregister; > } > > + irq = platform_get_irq(pdev, i); Hmmm, with reusing 'i' for getting the interrupt resource, you assume that the number of TSC will always equal the number of interrupts. It's somewhat reasonable, yet we have seen enough surprises in HW to better not assume it IMO. Another advantage of decoupling it into a seperate loop is that you only need to register as much interrupts as the driver actually uses. It uses 2 interrupts, so no need for installing 3 handlers, no? > + if (irq < 0) { > + ret = irq; > + goto error_unregister; > + } ... > + > + dev_info(tsc->dev, "TSC%d: Loaded %d trip points\n", tsc->num, > + of_thermal_get_ntrips(tsc->zone)); I'd think the of_thermal_get_ntrips is a little bit hidden in the dev_info. Would like it more explicitly. Also, it cannot fail? What happens if DT provides broken trip points... > } > > + rcar_thermal_irq_enable(priv); ... because we enable interrupts unconditionally here? Regards, Wolfram
Hi Wolfram, Thanks for your feedback. On 2017-03-07 20:55:33 +0100, Wolfram Sang wrote: > Hi Niklas, > > thanks for your work! > > On Mon, Mar 06, 2017 at 09:03:59PM +0100, Niklas Söderlund wrote: > > Enable hardware trip points by implementing the set_trips callback. The > > thermal core will take care of setting the initial trip point window and > > to update it once the driver reports a TSC have moved outside it. > > > > The interrupt structure for this device is a bit odd. There is not a > > dedicated IRQ for each TSC, instead the interrupts are shared between > > all TSC. IRQn is fired if the temp monitored in IRQTEMPn is reached in > > any of the TSC, example IRQ3 is fired if temperature in IRQTEMP3 is > > reached in either TSC0, TSC1 or TSC2. > > > > For this reason the usage of interrupts in this driver is a all on or > > s/a all/an all/ Will fix > > > all off design. When an interrupt happens all TSC are checked and all > > thermal zones are updated. This could be refined to be more fine grained > > but the thermal core takes care of only updating the thermal zones that > > have left there trip point window. > > s/there/the/ Will fix > > > > > Signed-off-by: Niklas Söderlund <niklas.soderlund+renesas@ragnatech.se> > > --- > > drivers/thermal/rcar_gen3_thermal.c | 137 ++++++++++++++++++++++++++++++++++++ > > 1 file changed, 137 insertions(+) > > > > +static int rcar_gen3_thermal_set_trips(void *devdata, int low, int high) > > +{ > > + struct rcar_gen3_thermal_tsc *tsc = devdata; > > + > > + if (low < -40000) > > + low = -40000; > > + if (high > 125000) > > + high = 125000; > > Use clamp_val()? Good idea, will use. > > > + > > + rcar_gen3_thermal_write(tsc, REG_GEN3_IRQTEMP1, > > + rcar_gen3_thermal_mcelsius_to_temp(tsc, low)); > > + > > + rcar_gen3_thermal_write(tsc, REG_GEN3_IRQTEMP2, > > + rcar_gen3_thermal_mcelsius_to_temp(tsc, high)); > > + > > + dev_dbg(tsc->dev, "TSC%d: low: %d high: %d\n", tsc->num, low, high); > > Is there no sysfs/debugfs way to get this from thermal core? I'd bet > this is of generic interest. Bonus point: if we drop this dbg, we can > also drop the 'dev' and 'num' members from the struct again. I can't find any sysfs or debugfs knobs to show this information, what do exist is a dev_dbg in thermal_zone_set_trips() which can print similar information. The difference is that the trip values have not been clamped to the device specification in that printout since it happens before the call to the rcar_gen3_thermal_set_trips() call (where they are still clamped before anything is written to hardware): thermal_zone_set_trips(): [ 2.014173] thermal thermal_zone0: new temperature boundaries: -2147483647 < x < 120000 rcar_gen3_thermal_set_trips(): [ 2.014180] rcar_gen3_thermal e6198000.thermal: TSC0: low: -40000 high: 120000 But this is for debugging so I see no issue whit dropping this debug printout from the driver and depend on the one from thermal core. Will remove this and the patch which stores tsc->dev and tsc->num form the next version. > > > + > > + return 0; > > +} > > + > > static struct thermal_zone_of_device_ops rcar_gen3_tz_of_ops = { > > .get_temp = rcar_gen3_thermal_get_temp, > > + .set_trips = rcar_gen3_thermal_set_trips, > > }; > > > > +static void rcar_thermal_irq_disable(struct rcar_gen3_thermal_priv *priv) > > +{ > > + int i; > > + > > + for (i = 0; i < TSC_MAX_NUM; i++) > > + rcar_gen3_thermal_write(priv->tscs[i], REG_GEN3_IRQMSK, 0); > > +} > > + > > +static void rcar_thermal_irq_enable(struct rcar_gen3_thermal_priv *priv) > > +{ > > + int i; > > + > > + for (i = 0; i < TSC_MAX_NUM; i++) > > + rcar_gen3_thermal_write(priv->tscs[i], REG_GEN3_IRQMSK, > > + IRQ_TEMPD1 | IRQ_TEMP2); > > +} > > Merge the two into one with a second parameter which is bool? > rcar_gen3_irq_enable(priv, true/false)? OK > > > pm_runtime_enable(dev); > > @@ -277,6 +388,8 @@ static int rcar_gen3_thermal_probe(struct platform_device *pdev) > > > > for (i = 0; i < TSC_MAX_NUM; i++) { > > struct rcar_gen3_thermal_tsc *tsc; > > + char *irqname; > > + int irq; > > > > tsc = devm_kzalloc(dev, sizeof(*tsc), GFP_KERNEL); > > if (!tsc) { > > @@ -299,6 +412,25 @@ static int rcar_gen3_thermal_probe(struct platform_device *pdev) > > goto error_unregister; > > } > > > > + irq = platform_get_irq(pdev, i); > > Hmmm, with reusing 'i' for getting the interrupt resource, you assume > that the number of TSC will always equal the number of interrupts. It's > somewhat reasonable, yet we have seen enough surprises in HW to better > not assume it IMO. > > Another advantage of decoupling it into a seperate loop is that you only > need to register as much interrupts as the driver actually uses. It uses > 2 interrupts, so no need for installing 3 handlers, no? That is correct, will update for next version. > > > + if (irq < 0) { > > + ret = irq; > > + goto error_unregister; > > + } > > ... > > > + > > + dev_info(tsc->dev, "TSC%d: Loaded %d trip points\n", tsc->num, > > + of_thermal_get_ntrips(tsc->zone)); > > I'd think the of_thermal_get_ntrips is a little bit hidden in the > dev_info. Would like it more explicitly. Also, it cannot fail? What > happens if DT provides broken trip points... All thermal zones are parsed by the thermal core by of_parse_thermal_zones() called from thermal_init() which is called using fs_initcall(thermal_init). Then the call chain devm_thermal_zone_of_sensor_register() thermal_zone_of_sensor_register() thermal_zone_of_add_sensor() thermal_zone_get_zone_by_name() Finds the zone parsed at init time, so if the zone is broken in some way it would not be available and devm_thermal_zone_of_sensor_register() would fail. But you are correct of_thermal_get_ntrips() could return -ENODEV I will add check for this and error out if that is the case. > > > } > > > > + rcar_thermal_irq_enable(priv); > > ... because we enable interrupts unconditionally here? > > Regards, > > Wolfram >
Hi Geert, Thanks for your feedback. On 2017-03-07 16:36:00 +0100, Geert Uytterhoeven wrote: > On Mon, Mar 6, 2017 at 9:03 PM, Niklas Söderlund > <niklas.soderlund+renesas@ragnatech.se> wrote: > > Enable hardware trip points by implementing the set_trips callback. The > > thermal core will take care of setting the initial trip point window and > > to update it once the driver reports a TSC have moved outside it. > > has moved > > > The interrupt structure for this device is a bit odd. There is not a > > dedicated IRQ for each TSC, instead the interrupts are shared between > > all TSC. IRQn is fired if the temp monitored in IRQTEMPn is reached in > > all TSCs > > > any of the TSC, example IRQ3 is fired if temperature in IRQTEMP3 is > > TSCs > > > reached in either TSC0, TSC1 or TSC2. > > > > For this reason the usage of interrupts in this driver is a all on or > > an all-on or all-off > > > all off design. When an interrupt happens all TSC are checked and all > > TSCs > > > thermal zones are updated. This could be refined to be more fine grained > > but the thermal core takes care of only updating the thermal zones that > > have left there trip point window. > > their > > > Signed-off-by: Niklas Söderlund <niklas.soderlund+renesas@ragnatech.se> > > Reviewed-by: Geert Uytterhoeven <geert+renesas@glider.be> I will fix all the spelling suggestions above, thanks! But I will not add your Reviewed-by since Wolframs comments suggest I need to update some parts of this patch. > > > --- > > drivers/thermal/rcar_gen3_thermal.c | 137 ++++++++++++++++++++++++++++++++++++ > > 1 file changed, 137 insertions(+) > > > > diff --git a/drivers/thermal/rcar_gen3_thermal.c b/drivers/thermal/rcar_gen3_thermal.c > > index 65f7204936a18278..e52181015e589a7f 100644 > > --- a/drivers/thermal/rcar_gen3_thermal.c > > +++ b/drivers/thermal/rcar_gen3_thermal.c > > > @@ -76,6 +87,7 @@ struct rcar_gen3_thermal_tsc { > > }; > > > > struct rcar_gen3_thermal_priv { > > + spinlock_t lock; > > When declaring a spinlock, please add a comment what exactly is > protected by the lock. Will do thanks. > > > +static void rcar_thermal_irq_disable(struct rcar_gen3_thermal_priv *priv) > > +{ > > + int i; > > unsigned int (for all positive loop counters) Thanks, will fix. > > > + > > + for (i = 0; i < TSC_MAX_NUM; i++) > > + rcar_gen3_thermal_write(priv->tscs[i], REG_GEN3_IRQMSK, 0); > > +} > > Gr{oetje,eeting}s, > > Geert > > -- > Geert Uytterhoeven -- There's lots of Linux beyond ia32 -- geert@linux-m68k.org > > In personal conversations with technical people, I call myself a hacker. But > when I'm talking to journalists I just say "programmer" or something like that. > -- Linus Torvalds
Hi Niklas, On Fri, Mar 17, 2017 at 11:06 AM, Niklas Söderlund <niklas.soderlund@ragnatech.se> wrote: > On 2017-03-07 20:55:33 +0100, Wolfram Sang wrote: >> On Mon, Mar 06, 2017 at 09:03:59PM +0100, Niklas Söderlund wrote: >> > Signed-off-by: Niklas Söderlund <niklas.soderlund+renesas@ragnatech.se> >> > --- >> > drivers/thermal/rcar_gen3_thermal.c | 137 ++++++++++++++++++++++++++++++++++++ >> > 1 file changed, 137 insertions(+) >> > >> > +static int rcar_gen3_thermal_set_trips(void *devdata, int low, int high) >> > +{ >> > + struct rcar_gen3_thermal_tsc *tsc = devdata; >> > + >> > + if (low < -40000) >> > + low = -40000; >> > + if (high > 125000) >> > + high = 125000; >> >> Use clamp_val()? > > Good idea, will use. Why not clamp()? Does the type checking stand in the way? Gr{oetje,eeting}s, Geert -- Geert Uytterhoeven -- There's lots of Linux beyond ia32 -- geert@linux-m68k.org In personal conversations with technical people, I call myself a hacker. But when I'm talking to journalists I just say "programmer" or something like that. -- Linus Torvalds
diff --git a/drivers/thermal/rcar_gen3_thermal.c b/drivers/thermal/rcar_gen3_thermal.c index 65f7204936a18278..e52181015e589a7f 100644 --- a/drivers/thermal/rcar_gen3_thermal.c +++ b/drivers/thermal/rcar_gen3_thermal.c @@ -23,8 +23,11 @@ #include <linux/of_device.h> #include <linux/platform_device.h> #include <linux/pm_runtime.h> +#include <linux/spinlock.h> #include <linux/thermal.h> +#include "thermal_core.h" + /* Register offsets */ #define REG_GEN3_IRQSTR 0x04 #define REG_GEN3_IRQMSK 0x08 @@ -40,6 +43,14 @@ #define REG_GEN3_THCODE2 0x54 #define REG_GEN3_THCODE3 0x58 +/* IRQ{STR,MSK,EN} bits */ +#define IRQ_TEMP1 BIT(0) +#define IRQ_TEMP2 BIT(1) +#define IRQ_TEMP3 BIT(2) +#define IRQ_TEMPD1 BIT(3) +#define IRQ_TEMPD2 BIT(4) +#define IRQ_TEMPD3 BIT(5) + /* CTSR bits */ #define CTSR_PONM BIT(8) #define CTSR_AOUT BIT(7) @@ -76,6 +87,7 @@ struct rcar_gen3_thermal_tsc { }; struct rcar_gen3_thermal_priv { + spinlock_t lock; struct rcar_gen3_thermal_tsc *tscs[TSC_MAX_NUM]; }; @@ -114,6 +126,7 @@ static inline void rcar_gen3_thermal_write(struct rcar_gen3_thermal_tsc *tsc, #define FIXPT_SHIFT 7 #define FIXPT_INT(_x) ((_x) << FIXPT_SHIFT) +#define INT_FIXPT(_x) ((_x) >> FIXPT_SHIFT) #define FIXPT_DIV(_a, _b) DIV_ROUND_CLOSEST(((_a) << FIXPT_SHIFT), (_b)) #define FIXPT_TO_MCELSIUS(_x) ((_x) * 1000 >> FIXPT_SHIFT) @@ -179,10 +192,99 @@ static int rcar_gen3_thermal_get_temp(void *devdata, int *temp) return 0; } +static int rcar_gen3_thermal_mcelsius_to_temp(struct rcar_gen3_thermal_tsc *tsc, + int mcelsius) +{ + int celsius, val1, val2; + + celsius = DIV_ROUND_CLOSEST(mcelsius, 1000); + val1 = celsius * tsc->coef.a1 + tsc->coef.b1; + val2 = celsius * tsc->coef.a2 + tsc->coef.b2; + + return INT_FIXPT((val1 + val2) / 2); +} + +static int rcar_gen3_thermal_set_trips(void *devdata, int low, int high) +{ + struct rcar_gen3_thermal_tsc *tsc = devdata; + + if (low < -40000) + low = -40000; + if (high > 125000) + high = 125000; + + rcar_gen3_thermal_write(tsc, REG_GEN3_IRQTEMP1, + rcar_gen3_thermal_mcelsius_to_temp(tsc, low)); + + rcar_gen3_thermal_write(tsc, REG_GEN3_IRQTEMP2, + rcar_gen3_thermal_mcelsius_to_temp(tsc, high)); + + dev_dbg(tsc->dev, "TSC%d: low: %d high: %d\n", tsc->num, low, high); + + return 0; +} + static struct thermal_zone_of_device_ops rcar_gen3_tz_of_ops = { .get_temp = rcar_gen3_thermal_get_temp, + .set_trips = rcar_gen3_thermal_set_trips, }; +static void rcar_thermal_irq_disable(struct rcar_gen3_thermal_priv *priv) +{ + int i; + + for (i = 0; i < TSC_MAX_NUM; i++) + rcar_gen3_thermal_write(priv->tscs[i], REG_GEN3_IRQMSK, 0); +} + +static void rcar_thermal_irq_enable(struct rcar_gen3_thermal_priv *priv) +{ + int i; + + for (i = 0; i < TSC_MAX_NUM; i++) + rcar_gen3_thermal_write(priv->tscs[i], REG_GEN3_IRQMSK, + IRQ_TEMPD1 | IRQ_TEMP2); +} + +static irqreturn_t rcar_gen3_thermal_irq(int irq, void *data) +{ + struct rcar_gen3_thermal_priv *priv = data; + unsigned long flags; + u32 status; + int i, ret = IRQ_HANDLED; + + spin_lock_irqsave(&priv->lock, flags); + for (i = 0; i < TSC_MAX_NUM; i++) { + status = rcar_gen3_thermal_read(priv->tscs[i], REG_GEN3_IRQSTR); + rcar_gen3_thermal_write(priv->tscs[i], REG_GEN3_IRQSTR, 0); + if (status) + ret = IRQ_WAKE_THREAD; + } + + if (ret == IRQ_WAKE_THREAD) + rcar_thermal_irq_disable(priv); + spin_unlock_irqrestore(&priv->lock, flags); + + return ret; +} + +static irqreturn_t rcar_gen3_thermal_irq_thread(int irq, void *data) +{ + struct rcar_gen3_thermal_priv *priv = data; + unsigned long flags; + int i; + + for (i = 0; i < TSC_MAX_NUM; i++) + thermal_zone_device_update(priv->tscs[i]->zone, + THERMAL_EVENT_UNSPECIFIED); + + spin_lock_irqsave(&priv->lock, flags); + rcar_thermal_irq_enable(priv); + spin_unlock_irqrestore(&priv->lock, flags); + + return IRQ_HANDLED; +} + static void r8a7795_thermal_init(struct rcar_gen3_thermal_tsc *tsc) { rcar_gen3_thermal_write(tsc, REG_GEN3_CTSR, CTSR_THBGR); @@ -191,7 +293,11 @@ static void r8a7795_thermal_init(struct rcar_gen3_thermal_tsc *tsc) usleep_range(1000, 2000); rcar_gen3_thermal_write(tsc, REG_GEN3_CTSR, CTSR_PONM); + rcar_gen3_thermal_write(tsc, REG_GEN3_IRQCTL, 0x3F); + rcar_gen3_thermal_write(tsc, REG_GEN3_IRQMSK, 0); + rcar_gen3_thermal_write(tsc, REG_GEN3_IRQEN, IRQ_TEMPD1 | IRQ_TEMP2); + rcar_gen3_thermal_write(tsc, REG_GEN3_CTSR, CTSR_PONM | CTSR_AOUT | CTSR_THBGR | CTSR_VMEN); @@ -215,6 +321,9 @@ static void r8a7796_thermal_init(struct rcar_gen3_thermal_tsc *tsc) usleep_range(1000, 2000); rcar_gen3_thermal_write(tsc, REG_GEN3_IRQCTL, 0x3F); + rcar_gen3_thermal_write(tsc, REG_GEN3_IRQMSK, 0); + rcar_gen3_thermal_write(tsc, REG_GEN3_IRQEN, IRQ_TEMPD1 | IRQ_TEMP2); + reg_val = rcar_gen3_thermal_read(tsc, REG_GEN3_THCTR); reg_val |= THCTR_THSST; rcar_gen3_thermal_write(tsc, REG_GEN3_THCTR, reg_val); @@ -270,6 +379,8 @@ static int rcar_gen3_thermal_probe(struct platform_device *pdev) if (!priv) return -ENOMEM; + spin_lock_init(&priv->lock); + platform_set_drvdata(pdev, priv); pm_runtime_enable(dev); @@ -277,6 +388,8 @@ static int rcar_gen3_thermal_probe(struct platform_device *pdev) for (i = 0; i < TSC_MAX_NUM; i++) { struct rcar_gen3_thermal_tsc *tsc; + char *irqname; + int irq; tsc = devm_kzalloc(dev, sizeof(*tsc), GFP_KERNEL); if (!tsc) { @@ -299,6 +412,25 @@ static int rcar_gen3_thermal_probe(struct platform_device *pdev) goto error_unregister; } + irq = platform_get_irq(pdev, i); + if (irq < 0) { + ret = irq; + goto error_unregister; + } + + irqname = devm_kasprintf(dev, GFP_KERNEL, "%s:ch%d", + dev_name(dev), i); + if (!irqname) { + ret = -ENOMEM; + goto error_unregister; + } + + ret = devm_request_threaded_irq(dev, irq, rcar_gen3_thermal_irq, + rcar_gen3_thermal_irq_thread, + IRQF_SHARED, irqname, priv); + if (ret) + goto error_unregister; + priv->tscs[i] = tsc; match_data->thermal_init(tsc); @@ -312,8 +444,13 @@ static int rcar_gen3_thermal_probe(struct platform_device *pdev) goto error_unregister; } tsc->zone = zone; + + dev_info(tsc->dev, "TSC%d: Loaded %d trip points\n", tsc->num, + of_thermal_get_ntrips(tsc->zone)); } + rcar_thermal_irq_enable(priv); + return 0; error_unregister:
Enable hardware trip points by implementing the set_trips callback. The thermal core will take care of setting the initial trip point window and to update it once the driver reports a TSC have moved outside it. The interrupt structure for this device is a bit odd. There is not a dedicated IRQ for each TSC, instead the interrupts are shared between all TSC. IRQn is fired if the temp monitored in IRQTEMPn is reached in any of the TSC, example IRQ3 is fired if temperature in IRQTEMP3 is reached in either TSC0, TSC1 or TSC2. For this reason the usage of interrupts in this driver is a all on or all off design. When an interrupt happens all TSC are checked and all thermal zones are updated. This could be refined to be more fine grained but the thermal core takes care of only updating the thermal zones that have left there trip point window. Signed-off-by: Niklas Söderlund <niklas.soderlund+renesas@ragnatech.se> --- drivers/thermal/rcar_gen3_thermal.c | 137 ++++++++++++++++++++++++++++++++++++ 1 file changed, 137 insertions(+)