* [RFC PATCH v2 0/4] thermal: iio bindings @ 2015-09-17 22:30 Srinivas Pandruvada 2015-09-17 22:30 ` [RFC PATCH v2 1/4] thermal: core: enhance thermal_zone_device_update Srinivas Pandruvada [not found] ` <1442529054-25803-1-git-send-email-srinivas.pandruvada-VuQAYsv1563Yd54FQh9/CA@public.gmane.org> 0 siblings, 2 replies; 8+ messages in thread From: Srinivas Pandruvada @ 2015-09-17 22:30 UTC (permalink / raw) To: jic23, rui.zhang, edubezval; +Cc: linux-pm, linux-iio, Srinivas Pandruvada v2 - Rebase to 4.3.rc1 - Additional parameter to thermal_zone_device_update for event type - Moved the dummy IIO callbacks to local include file, in this way "not used" warnings can be avoided - Remove changes from user space governor, rather notify from core - Validate the trigger, so that when thermal device supports async notifications, this will be exclusive trigger - Use new thermal core event type to differentiate events for iio threshold notification v1 - Change commit message to be more clear - Split in three patches (First for thermal_iio.c, second to integrate to thermal_core and third for user space governor). - Removed IIO defines in thermal.h - Removed direct push to buffers - Other comments from Jonathan except introduce validate_trigger v0 Base version for first review Srinivas Pandruvada (4): thermal: core: enhance thermal_zone_device_update thermal: iio device for thermal sensor thermal: use iio binding calls thermal: x86_pkg_temp: Register threshold callbacks drivers/acpi/thermal.c | 2 +- drivers/platform/x86/acerhdf.c | 2 +- drivers/thermal/Kconfig | 12 + drivers/thermal/Makefile | 1 + drivers/thermal/db8500_thermal.c | 2 +- drivers/thermal/hisi_thermal.c | 3 +- drivers/thermal/imx_thermal.c | 4 +- .../thermal/int340x_thermal/int340x_thermal_zone.h | 2 +- drivers/thermal/intel_soc_dts_iosf.c | 3 +- drivers/thermal/of-thermal.c | 2 +- drivers/thermal/qcom-spmi-temp-alarm.c | 2 +- drivers/thermal/rcar_thermal.c | 3 +- drivers/thermal/rockchip_thermal.c | 3 +- drivers/thermal/samsung/exynos_tmu.c | 2 +- drivers/thermal/st/st_thermal_memmap.c | 3 +- drivers/thermal/thermal_core.c | 21 +- drivers/thermal/thermal_iio.c | 375 +++++++++++++++++++++ drivers/thermal/thermal_iio.h | 45 +++ drivers/thermal/ti-soc-thermal/ti-thermal-common.c | 4 +- drivers/thermal/x86_pkg_temp_thermal.c | 11 +- include/linux/thermal.h | 26 +- 21 files changed, 503 insertions(+), 25 deletions(-) create mode 100644 drivers/thermal/thermal_iio.c create mode 100644 drivers/thermal/thermal_iio.h -- 1.9.3 ^ permalink raw reply [flat|nested] 8+ messages in thread
* [RFC PATCH v2 1/4] thermal: core: enhance thermal_zone_device_update 2015-09-17 22:30 [RFC PATCH v2 0/4] thermal: iio bindings Srinivas Pandruvada @ 2015-09-17 22:30 ` Srinivas Pandruvada [not found] ` <1442529054-25803-2-git-send-email-srinivas.pandruvada-VuQAYsv1563Yd54FQh9/CA@public.gmane.org> [not found] ` <1442529054-25803-1-git-send-email-srinivas.pandruvada-VuQAYsv1563Yd54FQh9/CA@public.gmane.org> 1 sibling, 1 reply; 8+ messages in thread From: Srinivas Pandruvada @ 2015-09-17 22:30 UTC (permalink / raw) To: jic23, rui.zhang, edubezval; +Cc: linux-pm, linux-iio, Srinivas Pandruvada thermal_zone_device_update is called by thermal core and clients to trigger reading of temperature, evaluation of trips and take governor specified actions. There can be number of reasons this may be triggered, for example when HW calculates new temperature sample or violation of a threshold. Since this is the only interface available for client drivers to trigger thermal core actions, it should also should have a way to specify the trigger event type. This change adds additional parameter to specify event. This is optional for the driver to specify event type, in that case this can be just THERMAL_DEVICE_EVENT_NONE. Alternately they can select THERMAL_DEVICE_EVENT_THRESHOLD for temperature threshold event, THERMAL_DEVICE_EVENT_TEMP_SAMPLE for new temperature sample notification, THERMAL_DEVICE_TRIP_TEMP_CHANGE for a temperature trip change. Signed-off-by: Srinivas Pandruvada <srinivas.pandruvada@linux.intel.com> --- drivers/acpi/thermal.c | 2 +- drivers/platform/x86/acerhdf.c | 2 +- drivers/thermal/db8500_thermal.c | 2 +- drivers/thermal/hisi_thermal.c | 3 ++- drivers/thermal/imx_thermal.c | 4 ++-- drivers/thermal/int340x_thermal/int340x_thermal_zone.h | 2 +- drivers/thermal/intel_soc_dts_iosf.c | 3 ++- drivers/thermal/of-thermal.c | 2 +- drivers/thermal/qcom-spmi-temp-alarm.c | 2 +- drivers/thermal/rcar_thermal.c | 3 ++- drivers/thermal/rockchip_thermal.c | 3 ++- drivers/thermal/samsung/exynos_tmu.c | 2 +- drivers/thermal/st/st_thermal_memmap.c | 3 ++- drivers/thermal/thermal_core.c | 11 ++++++----- drivers/thermal/ti-soc-thermal/ti-thermal-common.c | 4 ++-- drivers/thermal/x86_pkg_temp_thermal.c | 3 ++- include/linux/thermal.h | 13 +++++++++++-- 17 files changed, 40 insertions(+), 24 deletions(-) diff --git a/drivers/acpi/thermal.c b/drivers/acpi/thermal.c index 30d8518..e4f547b 100644 --- a/drivers/acpi/thermal.c +++ b/drivers/acpi/thermal.c @@ -520,7 +520,7 @@ static void acpi_thermal_check(void *data) if (!tz->tz_enabled) return; - thermal_zone_device_update(tz->thermal_zone); + thermal_zone_device_update(tz->thermal_zone, THERMAL_DEVICE_EVENT_NONE); } /* sys I/F for generic thermal sysfs support */ diff --git a/drivers/platform/x86/acerhdf.c b/drivers/platform/x86/acerhdf.c index 460fa67..6382d4e 100644 --- a/drivers/platform/x86/acerhdf.c +++ b/drivers/platform/x86/acerhdf.c @@ -405,7 +405,7 @@ static inline void acerhdf_enable_kernelmode(void) kernelmode = 1; thz_dev->polling_delay = interval*1000; - thermal_zone_device_update(thz_dev); + thermal_zone_device_update(thz_dev, THERMAL_DEVICE_EVENT_NONE); pr_notice("kernel mode fan control ON\n"); } diff --git a/drivers/thermal/db8500_thermal.c b/drivers/thermal/db8500_thermal.c index 652acd8..e4cbbe6 100644 --- a/drivers/thermal/db8500_thermal.c +++ b/drivers/thermal/db8500_thermal.c @@ -306,7 +306,7 @@ static void db8500_thermal_work(struct work_struct *work) if (cur_mode == THERMAL_DEVICE_DISABLED) return; - thermal_zone_device_update(pzone->therm_dev); + thermal_zone_device_update(pzone->therm_dev, THERMAL_DEVICE_EVENT_NONE); dev_dbg(&pzone->therm_dev->device, "thermal work finished.\n"); } diff --git a/drivers/thermal/hisi_thermal.c b/drivers/thermal/hisi_thermal.c index 36d0729..65f3432 100644 --- a/drivers/thermal/hisi_thermal.c +++ b/drivers/thermal/hisi_thermal.c @@ -227,7 +227,8 @@ static irqreturn_t hisi_thermal_alarm_irq_thread(int irq, void *dev) mutex_unlock(&data->thermal_lock); for (i = 0; i < HISI_MAX_SENSORS; i++) - thermal_zone_device_update(data->sensors[i].tzd); + thermal_zone_device_update(data->sensors[i].tzd, + THERMAL_DEVICE_EVENT_NONE); return IRQ_HANDLED; } diff --git a/drivers/thermal/imx_thermal.c b/drivers/thermal/imx_thermal.c index 4bec1d3..24b3aa6 100644 --- a/drivers/thermal/imx_thermal.c +++ b/drivers/thermal/imx_thermal.c @@ -249,7 +249,7 @@ static int imx_set_mode(struct thermal_zone_device *tz, } data->mode = mode; - thermal_zone_device_update(tz); + thermal_zone_device_update(tz, THERMAL_DEVICE_EVENT_NONE); return 0; } @@ -436,7 +436,7 @@ static irqreturn_t imx_thermal_alarm_irq_thread(int irq, void *dev) dev_dbg(&data->tz->device, "THERMAL ALARM: T > %d\n", data->alarm_temp / 1000); - thermal_zone_device_update(data->tz); + thermal_zone_device_update(data->tz, THERMAL_DEVICE_EVENT_NONE); return IRQ_HANDLED; } diff --git a/drivers/thermal/int340x_thermal/int340x_thermal_zone.h b/drivers/thermal/int340x_thermal/int340x_thermal_zone.h index aaadf72..2f93b85 100644 --- a/drivers/thermal/int340x_thermal/int340x_thermal_zone.h +++ b/drivers/thermal/int340x_thermal/int340x_thermal_zone.h @@ -62,7 +62,7 @@ static inline void *int340x_thermal_zone_get_priv_data( static inline void int340x_thermal_zone_device_update( struct int34x_thermal_zone *tzone) { - thermal_zone_device_update(tzone->zone); + thermal_zone_device_update(tzone->zone, THERMAL_DEVICE_EVENT_THRESHOLD); } #endif diff --git a/drivers/thermal/intel_soc_dts_iosf.c b/drivers/thermal/intel_soc_dts_iosf.c index 5841d1d..4402ab0 100644 --- a/drivers/thermal/intel_soc_dts_iosf.c +++ b/drivers/thermal/intel_soc_dts_iosf.c @@ -392,7 +392,8 @@ void intel_soc_dts_iosf_interrupt_handler(struct intel_soc_dts_sensors *sensors) for (i = 0; i < SOC_MAX_DTS_SENSORS; ++i) { pr_debug("TZD update for zone %d\n", i); - thermal_zone_device_update(sensors->soc_dts[i].tzone); + thermal_zone_device_update(sensors->soc_dts[i].tzone, + THERMAL_DEVICE_EVENT_THRESHOLD); } } else spin_unlock_irqrestore(&sensors->intr_notify_lock, flags); diff --git a/drivers/thermal/of-thermal.c b/drivers/thermal/of-thermal.c index 42b7d42..4168bf9 100644 --- a/drivers/thermal/of-thermal.c +++ b/drivers/thermal/of-thermal.c @@ -292,7 +292,7 @@ static int of_thermal_set_mode(struct thermal_zone_device *tz, mutex_unlock(&tz->lock); data->mode = mode; - thermal_zone_device_update(tz); + thermal_zone_device_update(tz, THERMAL_DEVICE_EVENT_NONE); return 0; } diff --git a/drivers/thermal/qcom-spmi-temp-alarm.c b/drivers/thermal/qcom-spmi-temp-alarm.c index b677aad..bf3da3c 100644 --- a/drivers/thermal/qcom-spmi-temp-alarm.c +++ b/drivers/thermal/qcom-spmi-temp-alarm.c @@ -150,7 +150,7 @@ static irqreturn_t qpnp_tm_isr(int irq, void *data) { struct qpnp_tm_chip *chip = data; - thermal_zone_device_update(chip->tz_dev); + thermal_zone_device_update(chip->tz_dev, THERMAL_DEVICE_EVENT_NONE); return IRQ_HANDLED; } diff --git a/drivers/thermal/rcar_thermal.c b/drivers/thermal/rcar_thermal.c index 5d4ae7d..2eac44a 100644 --- a/drivers/thermal/rcar_thermal.c +++ b/drivers/thermal/rcar_thermal.c @@ -308,7 +308,8 @@ static void rcar_thermal_work(struct work_struct *work) rcar_thermal_get_temp(priv->zone, &nctemp); if (nctemp != cctemp) - thermal_zone_device_update(priv->zone); + thermal_zone_device_update(priv->zone, + THERMAL_DEVICE_EVENT_NONE); } static u32 rcar_thermal_had_changed(struct rcar_thermal_priv *priv, u32 status) diff --git a/drivers/thermal/rockchip_thermal.c b/drivers/thermal/rockchip_thermal.c index c89ffb2..b3d7214 100644 --- a/drivers/thermal/rockchip_thermal.c +++ b/drivers/thermal/rockchip_thermal.c @@ -361,7 +361,8 @@ static irqreturn_t rockchip_thermal_alarm_irq_thread(int irq, void *dev) thermal->chip->irq_ack(thermal->regs); for (i = 0; i < ARRAY_SIZE(thermal->sensors); i++) - thermal_zone_device_update(thermal->sensors[i].tzd); + thermal_zone_device_update(thermal->sensors[i].tzd, + THERMAL_DEVICE_EVENT_NONE); return IRQ_HANDLED; } diff --git a/drivers/thermal/samsung/exynos_tmu.c b/drivers/thermal/samsung/exynos_tmu.c index 0bae8cc..80ccc92 100644 --- a/drivers/thermal/samsung/exynos_tmu.c +++ b/drivers/thermal/samsung/exynos_tmu.c @@ -223,7 +223,7 @@ static void exynos_report_trigger(struct exynos_tmu_data *p) return; } - thermal_zone_device_update(tz); + thermal_zone_device_update(tz, THERMAL_DEVICE_EVENT_NONE); mutex_lock(&tz->lock); /* Find the level for which trip happened */ diff --git a/drivers/thermal/st/st_thermal_memmap.c b/drivers/thermal/st/st_thermal_memmap.c index fc0c9e1..45b6b9f 100644 --- a/drivers/thermal/st/st_thermal_memmap.c +++ b/drivers/thermal/st/st_thermal_memmap.c @@ -42,7 +42,8 @@ static irqreturn_t st_mmap_thermal_trip_handler(int irq, void *sdata) { struct st_thermal_sensor *sensor = sdata; - thermal_zone_device_update(sensor->thermal_dev); + thermal_zone_device_update(sensor->thermal_dev, + THERMAL_DEVICE_EVENT_NONE); return IRQ_HANDLED; } diff --git a/drivers/thermal/thermal_core.c b/drivers/thermal/thermal_core.c index 5e5fc70..18e7802 100644 --- a/drivers/thermal/thermal_core.c +++ b/drivers/thermal/thermal_core.c @@ -536,7 +536,8 @@ static void update_temperature(struct thermal_zone_device *tz) tz->last_temperature, tz->temperature); } -void thermal_zone_device_update(struct thermal_zone_device *tz) +void thermal_zone_device_update(struct thermal_zone_device *tz, + enum thermal_device_event_type event) { int count; @@ -555,7 +556,7 @@ static void thermal_zone_device_check(struct work_struct *work) struct thermal_zone_device *tz = container_of(work, struct thermal_zone_device, poll_queue.work); - thermal_zone_device_update(tz); + thermal_zone_device_update(tz, THERMAL_DEVICE_EVENT_NONE); } /* sys I/F for thermal zone */ @@ -794,7 +795,7 @@ passive_store(struct device *dev, struct device_attribute *attr, tz->forced_passive = state; - thermal_zone_device_update(tz); + thermal_zone_device_update(tz, THERMAL_DEVICE_EVENT_NONE); return count; } @@ -885,7 +886,7 @@ emul_temp_store(struct device *dev, struct device_attribute *attr, } if (!ret) - thermal_zone_device_update(tz); + thermal_zone_device_update(tz, THERMAL_DEVICE_EVENT_NONE); return ret ? ret : count; } @@ -1872,7 +1873,7 @@ struct thermal_zone_device *thermal_zone_device_register(const char *type, INIT_DELAYED_WORK(&(tz->poll_queue), thermal_zone_device_check); - thermal_zone_device_update(tz); + thermal_zone_device_update(tz, THERMAL_DEVICE_EVENT_NONE); return tz; diff --git a/drivers/thermal/ti-soc-thermal/ti-thermal-common.c b/drivers/thermal/ti-soc-thermal/ti-thermal-common.c index b213a12..ab2a011 100644 --- a/drivers/thermal/ti-soc-thermal/ti-thermal-common.c +++ b/drivers/thermal/ti-soc-thermal/ti-thermal-common.c @@ -52,7 +52,7 @@ static void ti_thermal_work(struct work_struct *work) struct ti_thermal_data *data = container_of(work, struct ti_thermal_data, thermal_wq); - thermal_zone_device_update(data->ti_thermal); + thermal_zone_device_update(data->ti_thermal, THERMAL_DEVICE_EVENT_NONE); dev_dbg(&data->ti_thermal->device, "updated thermal zone %s\n", data->ti_thermal->type); @@ -205,7 +205,7 @@ static int ti_thermal_set_mode(struct thermal_zone_device *thermal, data->mode = mode; ti_bandgap_write_update_interval(bgp, data->sensor_id, data->ti_thermal->polling_delay); - thermal_zone_device_update(data->ti_thermal); + thermal_zone_device_update(data->ti_thermal, THERMAL_DEVICE_EVENT_NONE); dev_dbg(&thermal->device, "thermal polling set for duration=%d msec\n", data->ti_thermal->polling_delay); diff --git a/drivers/thermal/x86_pkg_temp_thermal.c b/drivers/thermal/x86_pkg_temp_thermal.c index 7fc919f..849f682 100644 --- a/drivers/thermal/x86_pkg_temp_thermal.c +++ b/drivers/thermal/x86_pkg_temp_thermal.c @@ -348,7 +348,8 @@ static void pkg_temp_thermal_threshold_work_fn(struct work_struct *work) } if (notify) { pr_debug("thermal_zone_device_update\n"); - thermal_zone_device_update(phdev->tzone); + thermal_zone_device_update(phdev->tzone, + THERMAL_DEVICE_EVENT_THRESHOLD); } } diff --git a/include/linux/thermal.h b/include/linux/thermal.h index 17292fe..c074f6a 100644 --- a/include/linux/thermal.h +++ b/include/linux/thermal.h @@ -87,6 +87,13 @@ enum thermal_trend { THERMAL_TREND_DROP_FULL, /* apply lowest cooling action */ }; +enum thermal_device_event_type { + THERMAL_DEVICE_EVENT_NONE, /* No specific reason */ + THERMAL_DEVICE_EVENT_THRESHOLD, /* temp thereshold event */ + THERMAL_DEVICE_EVENT_TEMP_SAMPLE, /* New temp sample notify */ + THERMAL_DEVICE_TRIP_TEMP_CHANGE, /* trip temp change */ +}; + struct thermal_zone_device_ops { int (*bind) (struct thermal_zone_device *, struct thermal_cooling_device *); @@ -393,7 +400,8 @@ int thermal_zone_bind_cooling_device(struct thermal_zone_device *, int, unsigned int); int thermal_zone_unbind_cooling_device(struct thermal_zone_device *, int, struct thermal_cooling_device *); -void thermal_zone_device_update(struct thermal_zone_device *); +void thermal_zone_device_update(struct thermal_zone_device *, + enum thermal_device_event_type); struct thermal_cooling_device *thermal_cooling_device_register(char *, void *, const struct thermal_cooling_device_ops *); @@ -436,7 +444,8 @@ static inline int thermal_zone_unbind_cooling_device( struct thermal_zone_device *tz, int trip, struct thermal_cooling_device *cdev) { return -ENODEV; } -static inline void thermal_zone_device_update(struct thermal_zone_device *tz) +static inline void thermal_zone_device_update(struct thermal_zone_device *tz, + enum thermal_device_event_type) { } static inline struct thermal_cooling_device * thermal_cooling_device_register(char *type, void *devdata, -- 1.9.3 ^ permalink raw reply related [flat|nested] 8+ messages in thread
[parent not found: <1442529054-25803-2-git-send-email-srinivas.pandruvada-VuQAYsv1563Yd54FQh9/CA@public.gmane.org>]
* Re: [RFC PATCH v2 1/4] thermal: core: enhance thermal_zone_device_update [not found] ` <1442529054-25803-2-git-send-email-srinivas.pandruvada-VuQAYsv1563Yd54FQh9/CA@public.gmane.org> @ 2015-09-20 10:31 ` Jonathan Cameron 0 siblings, 0 replies; 8+ messages in thread From: Jonathan Cameron @ 2015-09-20 10:31 UTC (permalink / raw) To: Srinivas Pandruvada, rui.zhang-ral2JQCrhuEAvxtiuMwx3w, edubezval-Re5JQEeQqe8AvxtiuMwx3w Cc: linux-pm-u79uwXL29TY76Z2rM5mHXA, linux-iio-u79uwXL29TY76Z2rM5mHXA On 17/09/15 23:30, Srinivas Pandruvada wrote: > thermal_zone_device_update is called by thermal core and clients to > trigger reading of temperature, evaluation of trips and take governor > specified actions. There can be number of reasons this may be triggered, > for example when HW calculates new temperature sample or violation of a > threshold. Since this is the only interface available for client drivers > to trigger thermal core actions, it should also should have a way to > specify the trigger event type. > This change adds additional parameter to specify event. This is optional > for the driver to specify event type, in that case this can be just > THERMAL_DEVICE_EVENT_NONE. Alternately they can select > THERMAL_DEVICE_EVENT_THRESHOLD for temperature threshold event, > THERMAL_DEVICE_EVENT_TEMP_SAMPLE for new temperature sample notification, > THERMAL_DEVICE_TRIP_TEMP_CHANGE for a temperature trip change. This seems sensible to me. In the next patch I raise the question if you want to be able to filter your trigger based on which of these occurs.. I guess that can be added later without breaking ABI so maybe leave it for now. > > Signed-off-by: Srinivas Pandruvada <srinivas.pandruvada-VuQAYsv1563Yd54FQh9/CA@public.gmane.org> > --- > drivers/acpi/thermal.c | 2 +- > drivers/platform/x86/acerhdf.c | 2 +- > drivers/thermal/db8500_thermal.c | 2 +- > drivers/thermal/hisi_thermal.c | 3 ++- > drivers/thermal/imx_thermal.c | 4 ++-- > drivers/thermal/int340x_thermal/int340x_thermal_zone.h | 2 +- > drivers/thermal/intel_soc_dts_iosf.c | 3 ++- > drivers/thermal/of-thermal.c | 2 +- > drivers/thermal/qcom-spmi-temp-alarm.c | 2 +- > drivers/thermal/rcar_thermal.c | 3 ++- > drivers/thermal/rockchip_thermal.c | 3 ++- > drivers/thermal/samsung/exynos_tmu.c | 2 +- > drivers/thermal/st/st_thermal_memmap.c | 3 ++- > drivers/thermal/thermal_core.c | 11 ++++++----- > drivers/thermal/ti-soc-thermal/ti-thermal-common.c | 4 ++-- > drivers/thermal/x86_pkg_temp_thermal.c | 3 ++- > include/linux/thermal.h | 13 +++++++++++-- > 17 files changed, 40 insertions(+), 24 deletions(-) > > diff --git a/drivers/acpi/thermal.c b/drivers/acpi/thermal.c > index 30d8518..e4f547b 100644 > --- a/drivers/acpi/thermal.c > +++ b/drivers/acpi/thermal.c > @@ -520,7 +520,7 @@ static void acpi_thermal_check(void *data) > if (!tz->tz_enabled) > return; > > - thermal_zone_device_update(tz->thermal_zone); > + thermal_zone_device_update(tz->thermal_zone, THERMAL_DEVICE_EVENT_NONE); > } > > /* sys I/F for generic thermal sysfs support */ > diff --git a/drivers/platform/x86/acerhdf.c b/drivers/platform/x86/acerhdf.c > index 460fa67..6382d4e 100644 > --- a/drivers/platform/x86/acerhdf.c > +++ b/drivers/platform/x86/acerhdf.c > @@ -405,7 +405,7 @@ static inline void acerhdf_enable_kernelmode(void) > kernelmode = 1; > > thz_dev->polling_delay = interval*1000; > - thermal_zone_device_update(thz_dev); > + thermal_zone_device_update(thz_dev, THERMAL_DEVICE_EVENT_NONE); > pr_notice("kernel mode fan control ON\n"); > } > > diff --git a/drivers/thermal/db8500_thermal.c b/drivers/thermal/db8500_thermal.c > index 652acd8..e4cbbe6 100644 > --- a/drivers/thermal/db8500_thermal.c > +++ b/drivers/thermal/db8500_thermal.c > @@ -306,7 +306,7 @@ static void db8500_thermal_work(struct work_struct *work) > if (cur_mode == THERMAL_DEVICE_DISABLED) > return; > > - thermal_zone_device_update(pzone->therm_dev); > + thermal_zone_device_update(pzone->therm_dev, THERMAL_DEVICE_EVENT_NONE); > dev_dbg(&pzone->therm_dev->device, "thermal work finished.\n"); > } > > diff --git a/drivers/thermal/hisi_thermal.c b/drivers/thermal/hisi_thermal.c > index 36d0729..65f3432 100644 > --- a/drivers/thermal/hisi_thermal.c > +++ b/drivers/thermal/hisi_thermal.c > @@ -227,7 +227,8 @@ static irqreturn_t hisi_thermal_alarm_irq_thread(int irq, void *dev) > mutex_unlock(&data->thermal_lock); > > for (i = 0; i < HISI_MAX_SENSORS; i++) > - thermal_zone_device_update(data->sensors[i].tzd); > + thermal_zone_device_update(data->sensors[i].tzd, > + THERMAL_DEVICE_EVENT_NONE); > > return IRQ_HANDLED; > } > diff --git a/drivers/thermal/imx_thermal.c b/drivers/thermal/imx_thermal.c > index 4bec1d3..24b3aa6 100644 > --- a/drivers/thermal/imx_thermal.c > +++ b/drivers/thermal/imx_thermal.c > @@ -249,7 +249,7 @@ static int imx_set_mode(struct thermal_zone_device *tz, > } > > data->mode = mode; > - thermal_zone_device_update(tz); > + thermal_zone_device_update(tz, THERMAL_DEVICE_EVENT_NONE); > > return 0; > } > @@ -436,7 +436,7 @@ static irqreturn_t imx_thermal_alarm_irq_thread(int irq, void *dev) > dev_dbg(&data->tz->device, "THERMAL ALARM: T > %d\n", > data->alarm_temp / 1000); > > - thermal_zone_device_update(data->tz); > + thermal_zone_device_update(data->tz, THERMAL_DEVICE_EVENT_NONE); > > return IRQ_HANDLED; > } > diff --git a/drivers/thermal/int340x_thermal/int340x_thermal_zone.h b/drivers/thermal/int340x_thermal/int340x_thermal_zone.h > index aaadf72..2f93b85 100644 > --- a/drivers/thermal/int340x_thermal/int340x_thermal_zone.h > +++ b/drivers/thermal/int340x_thermal/int340x_thermal_zone.h > @@ -62,7 +62,7 @@ static inline void *int340x_thermal_zone_get_priv_data( > static inline void int340x_thermal_zone_device_update( > struct int34x_thermal_zone *tzone) > { > - thermal_zone_device_update(tzone->zone); > + thermal_zone_device_update(tzone->zone, THERMAL_DEVICE_EVENT_THRESHOLD); > } > > #endif > diff --git a/drivers/thermal/intel_soc_dts_iosf.c b/drivers/thermal/intel_soc_dts_iosf.c > index 5841d1d..4402ab0 100644 > --- a/drivers/thermal/intel_soc_dts_iosf.c > +++ b/drivers/thermal/intel_soc_dts_iosf.c > @@ -392,7 +392,8 @@ void intel_soc_dts_iosf_interrupt_handler(struct intel_soc_dts_sensors *sensors) > > for (i = 0; i < SOC_MAX_DTS_SENSORS; ++i) { > pr_debug("TZD update for zone %d\n", i); > - thermal_zone_device_update(sensors->soc_dts[i].tzone); > + thermal_zone_device_update(sensors->soc_dts[i].tzone, > + THERMAL_DEVICE_EVENT_THRESHOLD); > } > } else > spin_unlock_irqrestore(&sensors->intr_notify_lock, flags); > diff --git a/drivers/thermal/of-thermal.c b/drivers/thermal/of-thermal.c > index 42b7d42..4168bf9 100644 > --- a/drivers/thermal/of-thermal.c > +++ b/drivers/thermal/of-thermal.c > @@ -292,7 +292,7 @@ static int of_thermal_set_mode(struct thermal_zone_device *tz, > mutex_unlock(&tz->lock); > > data->mode = mode; > - thermal_zone_device_update(tz); > + thermal_zone_device_update(tz, THERMAL_DEVICE_EVENT_NONE); > > return 0; > } > diff --git a/drivers/thermal/qcom-spmi-temp-alarm.c b/drivers/thermal/qcom-spmi-temp-alarm.c > index b677aad..bf3da3c 100644 > --- a/drivers/thermal/qcom-spmi-temp-alarm.c > +++ b/drivers/thermal/qcom-spmi-temp-alarm.c > @@ -150,7 +150,7 @@ static irqreturn_t qpnp_tm_isr(int irq, void *data) > { > struct qpnp_tm_chip *chip = data; > > - thermal_zone_device_update(chip->tz_dev); > + thermal_zone_device_update(chip->tz_dev, THERMAL_DEVICE_EVENT_NONE); > > return IRQ_HANDLED; > } > diff --git a/drivers/thermal/rcar_thermal.c b/drivers/thermal/rcar_thermal.c > index 5d4ae7d..2eac44a 100644 > --- a/drivers/thermal/rcar_thermal.c > +++ b/drivers/thermal/rcar_thermal.c > @@ -308,7 +308,8 @@ static void rcar_thermal_work(struct work_struct *work) > > rcar_thermal_get_temp(priv->zone, &nctemp); > if (nctemp != cctemp) > - thermal_zone_device_update(priv->zone); > + thermal_zone_device_update(priv->zone, > + THERMAL_DEVICE_EVENT_NONE); > } > > static u32 rcar_thermal_had_changed(struct rcar_thermal_priv *priv, u32 status) > diff --git a/drivers/thermal/rockchip_thermal.c b/drivers/thermal/rockchip_thermal.c > index c89ffb2..b3d7214 100644 > --- a/drivers/thermal/rockchip_thermal.c > +++ b/drivers/thermal/rockchip_thermal.c > @@ -361,7 +361,8 @@ static irqreturn_t rockchip_thermal_alarm_irq_thread(int irq, void *dev) > thermal->chip->irq_ack(thermal->regs); > > for (i = 0; i < ARRAY_SIZE(thermal->sensors); i++) > - thermal_zone_device_update(thermal->sensors[i].tzd); > + thermal_zone_device_update(thermal->sensors[i].tzd, > + THERMAL_DEVICE_EVENT_NONE); > > return IRQ_HANDLED; > } > diff --git a/drivers/thermal/samsung/exynos_tmu.c b/drivers/thermal/samsung/exynos_tmu.c > index 0bae8cc..80ccc92 100644 > --- a/drivers/thermal/samsung/exynos_tmu.c > +++ b/drivers/thermal/samsung/exynos_tmu.c > @@ -223,7 +223,7 @@ static void exynos_report_trigger(struct exynos_tmu_data *p) > return; > } > > - thermal_zone_device_update(tz); > + thermal_zone_device_update(tz, THERMAL_DEVICE_EVENT_NONE); > > mutex_lock(&tz->lock); > /* Find the level for which trip happened */ > diff --git a/drivers/thermal/st/st_thermal_memmap.c b/drivers/thermal/st/st_thermal_memmap.c > index fc0c9e1..45b6b9f 100644 > --- a/drivers/thermal/st/st_thermal_memmap.c > +++ b/drivers/thermal/st/st_thermal_memmap.c > @@ -42,7 +42,8 @@ static irqreturn_t st_mmap_thermal_trip_handler(int irq, void *sdata) > { > struct st_thermal_sensor *sensor = sdata; > > - thermal_zone_device_update(sensor->thermal_dev); > + thermal_zone_device_update(sensor->thermal_dev, > + THERMAL_DEVICE_EVENT_NONE); > > return IRQ_HANDLED; > } > diff --git a/drivers/thermal/thermal_core.c b/drivers/thermal/thermal_core.c > index 5e5fc70..18e7802 100644 > --- a/drivers/thermal/thermal_core.c > +++ b/drivers/thermal/thermal_core.c > @@ -536,7 +536,8 @@ static void update_temperature(struct thermal_zone_device *tz) > tz->last_temperature, tz->temperature); > } > > -void thermal_zone_device_update(struct thermal_zone_device *tz) > +void thermal_zone_device_update(struct thermal_zone_device *tz, > + enum thermal_device_event_type event) > { > int count; > > @@ -555,7 +556,7 @@ static void thermal_zone_device_check(struct work_struct *work) > struct thermal_zone_device *tz = container_of(work, struct > thermal_zone_device, > poll_queue.work); > - thermal_zone_device_update(tz); > + thermal_zone_device_update(tz, THERMAL_DEVICE_EVENT_NONE); > } > > /* sys I/F for thermal zone */ > @@ -794,7 +795,7 @@ passive_store(struct device *dev, struct device_attribute *attr, > > tz->forced_passive = state; > > - thermal_zone_device_update(tz); > + thermal_zone_device_update(tz, THERMAL_DEVICE_EVENT_NONE); > > return count; > } > @@ -885,7 +886,7 @@ emul_temp_store(struct device *dev, struct device_attribute *attr, > } > > if (!ret) > - thermal_zone_device_update(tz); > + thermal_zone_device_update(tz, THERMAL_DEVICE_EVENT_NONE); > > return ret ? ret : count; > } > @@ -1872,7 +1873,7 @@ struct thermal_zone_device *thermal_zone_device_register(const char *type, > > INIT_DELAYED_WORK(&(tz->poll_queue), thermal_zone_device_check); > > - thermal_zone_device_update(tz); > + thermal_zone_device_update(tz, THERMAL_DEVICE_EVENT_NONE); > > return tz; > > diff --git a/drivers/thermal/ti-soc-thermal/ti-thermal-common.c b/drivers/thermal/ti-soc-thermal/ti-thermal-common.c > index b213a12..ab2a011 100644 > --- a/drivers/thermal/ti-soc-thermal/ti-thermal-common.c > +++ b/drivers/thermal/ti-soc-thermal/ti-thermal-common.c > @@ -52,7 +52,7 @@ static void ti_thermal_work(struct work_struct *work) > struct ti_thermal_data *data = container_of(work, > struct ti_thermal_data, thermal_wq); > > - thermal_zone_device_update(data->ti_thermal); > + thermal_zone_device_update(data->ti_thermal, THERMAL_DEVICE_EVENT_NONE); > > dev_dbg(&data->ti_thermal->device, "updated thermal zone %s\n", > data->ti_thermal->type); > @@ -205,7 +205,7 @@ static int ti_thermal_set_mode(struct thermal_zone_device *thermal, > data->mode = mode; > ti_bandgap_write_update_interval(bgp, data->sensor_id, > data->ti_thermal->polling_delay); > - thermal_zone_device_update(data->ti_thermal); > + thermal_zone_device_update(data->ti_thermal, THERMAL_DEVICE_EVENT_NONE); > dev_dbg(&thermal->device, "thermal polling set for duration=%d msec\n", > data->ti_thermal->polling_delay); > > diff --git a/drivers/thermal/x86_pkg_temp_thermal.c b/drivers/thermal/x86_pkg_temp_thermal.c > index 7fc919f..849f682 100644 > --- a/drivers/thermal/x86_pkg_temp_thermal.c > +++ b/drivers/thermal/x86_pkg_temp_thermal.c > @@ -348,7 +348,8 @@ static void pkg_temp_thermal_threshold_work_fn(struct work_struct *work) > } > if (notify) { > pr_debug("thermal_zone_device_update\n"); > - thermal_zone_device_update(phdev->tzone); > + thermal_zone_device_update(phdev->tzone, > + THERMAL_DEVICE_EVENT_THRESHOLD); > } > } > > diff --git a/include/linux/thermal.h b/include/linux/thermal.h > index 17292fe..c074f6a 100644 > --- a/include/linux/thermal.h > +++ b/include/linux/thermal.h > @@ -87,6 +87,13 @@ enum thermal_trend { > THERMAL_TREND_DROP_FULL, /* apply lowest cooling action */ > }; > > +enum thermal_device_event_type { > + THERMAL_DEVICE_EVENT_NONE, /* No specific reason */ > + THERMAL_DEVICE_EVENT_THRESHOLD, /* temp thereshold event */ > + THERMAL_DEVICE_EVENT_TEMP_SAMPLE, /* New temp sample notify */ > + THERMAL_DEVICE_TRIP_TEMP_CHANGE, /* trip temp change */ > +}; > + > struct thermal_zone_device_ops { > int (*bind) (struct thermal_zone_device *, > struct thermal_cooling_device *); > @@ -393,7 +400,8 @@ int thermal_zone_bind_cooling_device(struct thermal_zone_device *, int, > unsigned int); > int thermal_zone_unbind_cooling_device(struct thermal_zone_device *, int, > struct thermal_cooling_device *); > -void thermal_zone_device_update(struct thermal_zone_device *); > +void thermal_zone_device_update(struct thermal_zone_device *, > + enum thermal_device_event_type); > > struct thermal_cooling_device *thermal_cooling_device_register(char *, void *, > const struct thermal_cooling_device_ops *); > @@ -436,7 +444,8 @@ static inline int thermal_zone_unbind_cooling_device( > struct thermal_zone_device *tz, int trip, > struct thermal_cooling_device *cdev) > { return -ENODEV; } > -static inline void thermal_zone_device_update(struct thermal_zone_device *tz) > +static inline void thermal_zone_device_update(struct thermal_zone_device *tz, > + enum thermal_device_event_type) > { } > static inline struct thermal_cooling_device * > thermal_cooling_device_register(char *type, void *devdata, > ^ permalink raw reply [flat|nested] 8+ messages in thread
[parent not found: <1442529054-25803-1-git-send-email-srinivas.pandruvada-VuQAYsv1563Yd54FQh9/CA@public.gmane.org>]
* [RFC PATCH v2 2/4] thermal: iio device for thermal sensor [not found] ` <1442529054-25803-1-git-send-email-srinivas.pandruvada-VuQAYsv1563Yd54FQh9/CA@public.gmane.org> @ 2015-09-17 22:30 ` Srinivas Pandruvada 2015-09-20 10:31 ` Jonathan Cameron 2015-09-17 22:30 ` [RFC PATCH v2 3/4] thermal: use iio binding calls Srinivas Pandruvada 2015-09-17 22:30 ` [RFC PATCH v2 4/4] thermal: x86_pkg_temp: Register threshold callbacks Srinivas Pandruvada 2 siblings, 1 reply; 8+ messages in thread From: Srinivas Pandruvada @ 2015-09-17 22:30 UTC (permalink / raw) To: jic23-DgEjT+Ai2ygdnm+yROfE0A, rui.zhang-ral2JQCrhuEAvxtiuMwx3w, edubezval-Re5JQEeQqe8AvxtiuMwx3w Cc: linux-pm-u79uwXL29TY76Z2rM5mHXA, linux-iio-u79uwXL29TY76Z2rM5mHXA, Srinivas Pandruvada This change registers temperature sensor in a thermal zone as an IIO temperature device. This allows user space thermal application to fully utilize IIO capability to read temperature events and samples. The primary motivation for this change to improve performance for user space thermal controllers like Linux thermal daemon or Intel® Dynamic Platform and Thermal Framework (DPTF) for Chromium OS. The current sysfs has several limitations, which forces the current user space to use polling as the safe way. This polling causes performance bottlenecks as some devices requires extremely aggressive response to changing temperature conditions. These are some limitations, which this change tries to address: - Temperature reading via sysfs attribute, which needs conversion from string to int - No way to set threshold settings to avoid polling for temperature change without using a RW passive trip point. - Event notifications via slow kobject uevents, which needs parsing to id the zone and read temperature - Only pull interface from user space, kernel drivers can't send samples - One sample at a time read from user space These limitations can be addressed by registering temperature sensor as an IIO device. IIO provides - buffered access via /dev/iio:deviceX node with select/poll capability - temperature thresholds setting via iio event thresholds - Wait and receive events - Able to use different external trigger (data ready indications) and push samples to buffers Next set of patches uses the IIO binding introduced in this patchset. The iio device created during call to thermal_zone_device_register. Samples are pushed to iio buffers when thermal_zone_device_update is called from client drivers. New thermal zone device callbacks: It introduces three new callbacks for thermal client drivers: get_threshold_temp: Get the current threshold temperature set_threshold_temp: Set the current threshold temperature check_notification_support: Inform that the client driver has asynchronous notification mechanism. If it is then polling can be avoided for the temperature sensor. Signed-off-by: Srinivas Pandruvada <srinivas.pandruvada-VuQAYsv1563Yd54FQh9/CA@public.gmane.org> --- drivers/thermal/Kconfig | 12 ++ drivers/thermal/Makefile | 1 + drivers/thermal/thermal_iio.c | 375 ++++++++++++++++++++++++++++++++++++++++++ drivers/thermal/thermal_iio.h | 45 +++++ include/linux/thermal.h | 13 ++ 5 files changed, 446 insertions(+) create mode 100644 drivers/thermal/thermal_iio.c create mode 100644 drivers/thermal/thermal_iio.h diff --git a/drivers/thermal/Kconfig b/drivers/thermal/Kconfig index 0390044..bc7836e 100644 --- a/drivers/thermal/Kconfig +++ b/drivers/thermal/Kconfig @@ -29,6 +29,18 @@ config THERMAL_HWMON Say 'Y' here if you want all thermal sensors to have hwmon sysfs interface too. +config THERMAL_IIO + bool + prompt "Thermal sensor from a zone as IIO sensor device" + select IIO + select IIO_BUFFER + select IIO_TRIGGERED_BUFFER + help + This will register thermal sensor in a zone as an IIO temperature + sensor device. + This will help user space thermal controllers to use IIO ABI to + get events and buffered acces to temperature samples. + config THERMAL_OF bool prompt "APIs to parse thermal data out of device tree" diff --git a/drivers/thermal/Makefile b/drivers/thermal/Makefile index 26f1608..b72a28d 100644 --- a/drivers/thermal/Makefile +++ b/drivers/thermal/Makefile @@ -7,6 +7,7 @@ thermal_sys-y += thermal_core.o # interface to/from other layers providing sensors thermal_sys-$(CONFIG_THERMAL_HWMON) += thermal_hwmon.o +thermal_sys-$(CONFIG_THERMAL_IIO) += thermal_iio.o thermal_sys-$(CONFIG_THERMAL_OF) += of-thermal.o # governors diff --git a/drivers/thermal/thermal_iio.c b/drivers/thermal/thermal_iio.c new file mode 100644 index 0000000..9c351e8 --- /dev/null +++ b/drivers/thermal/thermal_iio.c @@ -0,0 +1,375 @@ +/* + * thermal iio interface driver + * Copyright (c) 2015, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope 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/thermal.h> +#include <linux/iio/iio.h> +#include <linux/iio/sysfs.h> +#include <linux/iio/buffer.h> +#include <linux/iio/trigger.h> +#include <linux/iio/events.h> +#include <linux/iio/trigger_consumer.h> +#include <linux/iio/triggered_buffer.h> + +struct thermal_iio_data { + struct thermal_zone_device *tz; + struct iio_trigger *interrupt_trig; + struct iio_chan_spec *channels; + bool enable_trigger; + int temp_thres; + bool ev_enable_state; + struct mutex mutex; +}; + +static const struct iio_event_spec thermal_event = { + .type = IIO_EV_TYPE_THRESH, + .dir = IIO_EV_DIR_EITHER, + .mask_separate = BIT(IIO_EV_INFO_VALUE) | + BIT(IIO_EV_INFO_ENABLE) +}; + +#define THERMAL_TEMP_CHANNELS(event_cnt) { \ + { \ + .type = IIO_TEMP, \ + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \ + .scan_index = 0, \ + .scan_type = { \ + .sign = 'u', \ + .realbits = 32, \ + .storagebits = 32, \ + .endianness = IIO_CPU, \ + }, \ + .event_spec = &thermal_event, \ + .num_event_specs = event_cnt, \ + }, \ + IIO_CHAN_SOFT_TIMESTAMP(1), \ +} + +static const struct iio_chan_spec thermal_iio_channels[] = + THERMAL_TEMP_CHANNELS(0); +static const struct iio_chan_spec thermal_iio_channels_with_events[] = + THERMAL_TEMP_CHANNELS(1); + +static int thermal_iio_read_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int *val, int *val2, long mask) +{ + struct thermal_iio_data *iio_data = iio_priv(indio_dev); + int temp; + int ret; + + switch (mask) { + case IIO_CHAN_INFO_RAW: + ret = thermal_zone_get_temp(iio_data->tz, &temp); + if (!ret) { + *val = temp; + ret = IIO_VAL_INT; + } + break; + default: + ret = -EINVAL; + } + + return ret; +} + +static irqreturn_t thermal_trigger_handler(int irq, void *p) +{ + struct iio_poll_func *pf = p; + struct iio_dev *indio_dev = pf->indio_dev; + struct thermal_iio_data *iio_data = iio_priv(indio_dev); + u32 buffer[4]; + int temp; + int ret; + + ret = thermal_zone_get_temp(iio_data->tz, &temp); + if (ret) + goto err_read; + + *(u32 *)buffer = temp; + iio_push_to_buffers_with_timestamp(indio_dev, buffer, + iio_get_time_ns()); +err_read: + iio_trigger_notify_done(indio_dev->trig); + + return IRQ_HANDLED; +} + +static int thermal_data_rdy_trigger_set_state(struct iio_trigger *trig, + bool state) +{ + struct iio_dev *indio_dev = iio_trigger_get_drvdata(trig); + struct thermal_iio_data *iio_data = iio_priv(indio_dev); + int ret = 0; + + mutex_lock(&iio_data->mutex); + if (iio_data->tz->ops->set_notification_status) { + ret = iio_data->tz->ops->set_notification_status(iio_data->tz, + state); + if (!ret) + iio_data->enable_trigger = state; + } else + iio_data->enable_trigger = state; + mutex_unlock(&iio_data->mutex); + + return ret; +} + +static int thermal_iio_validate_trigger(struct iio_dev *indio_dev, + struct iio_trigger *trig) +{ + struct thermal_iio_data *iio_data = iio_priv(indio_dev); + + if (iio_data->interrupt_trig && iio_data->interrupt_trig != trig) + return -EINVAL; + + return 0; +} + +static const struct iio_trigger_ops thermal_trigger_ops = { + .set_trigger_state = thermal_data_rdy_trigger_set_state, + .owner = THIS_MODULE, +}; + +static int thermal_iio_read_event(struct iio_dev *indio_dev, + const struct iio_chan_spec *chan, + enum iio_event_type type, + enum iio_event_direction dir, + enum iio_event_info info, + int *val, int *val2) +{ + struct thermal_iio_data *iio_data = iio_priv(indio_dev); + int ret; + + mutex_lock(&iio_data->mutex); + *val2 = 0; + switch (info) { + case IIO_EV_INFO_VALUE: + *val = iio_data->temp_thres; + ret = IIO_VAL_INT; + break; + default: + ret = -EINVAL; + } + mutex_unlock(&iio_data->mutex); + + return ret; +} + +static int thermal_iio_write_event(struct iio_dev *indio_dev, + const struct iio_chan_spec *chan, + enum iio_event_type type, + enum iio_event_direction dir, + enum iio_event_info info, + int val, int val2) +{ + struct thermal_iio_data *iio_data = iio_priv(indio_dev); + int ret = 0; + + mutex_lock(&iio_data->mutex); + switch (info) { + case IIO_EV_INFO_VALUE: + iio_data->temp_thres = val; + break; + default: + ret = -EINVAL; + } + mutex_unlock(&iio_data->mutex); + + return ret; +} + +static int thermal_iio_read_event_config(struct iio_dev *indio_dev, + const struct iio_chan_spec *chan, + enum iio_event_type type, + enum iio_event_direction dir) +{ + struct thermal_iio_data *iio_data = iio_priv(indio_dev); + bool state; + + mutex_lock(&iio_data->mutex); + state = iio_data->ev_enable_state; + mutex_unlock(&iio_data->mutex); + + return state; +} + +static int thermal_iio_write_event_config(struct iio_dev *indio_dev, + const struct iio_chan_spec *chan, + enum iio_event_type type, + enum iio_event_direction dir, + int state) +{ + struct thermal_iio_data *iio_data = iio_priv(indio_dev); + int temp_thres; + int ret = 0; + + mutex_lock(&iio_data->mutex); + if ((state && iio_data->ev_enable_state) || + (!state && !iio_data->ev_enable_state)) + goto done_write_event; + + if (state && !iio_data->temp_thres) { + ret = -EINVAL; + goto done_write_event; + } + + if (state) + temp_thres = iio_data->temp_thres; + else + temp_thres = 0; + + ret = iio_data->tz->ops->set_threshold_temp(iio_data->tz, 0, + temp_thres); + if (ret) + goto done_write_event; + + if (iio_data->tz->ops->set_notification_status) { + ret = iio_data->tz->ops->set_notification_status(iio_data->tz, + state > 0); + if (!ret) + iio_data->ev_enable_state = state; + } else + iio_data->ev_enable_state = state; + +done_write_event: + mutex_unlock(&iio_data->mutex); + + return ret; +} + +static int thermal_iio_setup_trig(struct iio_trigger **iio_trig, + struct thermal_zone_device *tz, + const char *format) +{ + struct iio_trigger *trig; + int ret; + + trig = devm_iio_trigger_alloc(&tz->device, format, tz->type, + tz->indio_dev->id); + if (!trig) { + dev_err(&tz->device, "Trigger Allocate Failed\n"); + return -ENOMEM; + } + trig->dev.parent = &tz->device; + trig->ops = &thermal_trigger_ops; + iio_trigger_set_drvdata(trig, tz->indio_dev); + ret = iio_trigger_register(trig); + if (ret) { + dev_err(&tz->device, "Trigger Allocate Failed\n"); + return ret; + } + *iio_trig = trig; + + return 0; +} + +static const struct iio_info thermal_iio_info = { + .read_raw = thermal_iio_read_raw, + .read_event_value = thermal_iio_read_event, + .write_event_value = thermal_iio_write_event, + .write_event_config = thermal_iio_write_event_config, + .read_event_config = thermal_iio_read_event_config, + .validate_trigger = thermal_iio_validate_trigger, + .driver_module = THIS_MODULE, +}; + +int thermal_iio_sensor_register(struct thermal_zone_device *tz) +{ + struct thermal_iio_data *iio_data; + int ret; + + tz->indio_dev = devm_iio_device_alloc(&tz->device, sizeof(*iio_data)); + if (!tz->indio_dev) + return -ENOMEM; + + iio_data = iio_priv(tz->indio_dev); + iio_data->tz = tz; + mutex_init(&iio_data->mutex); + + tz->indio_dev->dev.parent = &tz->device; + if (tz->ops->set_threshold_temp) + tz->indio_dev->channels = thermal_iio_channels_with_events; + else + tz->indio_dev->channels = thermal_iio_channels; + tz->indio_dev->num_channels = ARRAY_SIZE(thermal_iio_channels); + tz->indio_dev->name = tz->type; + tz->indio_dev->info = &thermal_iio_info; + tz->indio_dev->modes = INDIO_DIRECT_MODE; + + if (tz->ops->check_notification_support && + tz->ops->check_notification_support(tz)) { + ret = thermal_iio_setup_trig(&iio_data->interrupt_trig, tz, + "%s-dev%d"); + if (ret) + return ret; + + tz->indio_dev->trig = iio_trigger_get(iio_data->interrupt_trig); + } + ret = iio_triggered_buffer_setup(tz->indio_dev, + &iio_pollfunc_store_time, + thermal_trigger_handler, NULL); + if (ret) { + dev_err(&tz->device, "failed to init trigger buffer\n"); + goto err_unreg_int_trig; + } + ret = iio_device_register(tz->indio_dev); + if (ret < 0) { + dev_err(&tz->device, "unable to register iio device\n"); + goto err_cleanup_trig; + } + + return 0; + +err_cleanup_trig: + iio_triggered_buffer_cleanup(tz->indio_dev); +err_unreg_int_trig: + if (iio_data->interrupt_trig) + iio_trigger_unregister(iio_data->interrupt_trig); + + return ret; +} + +int thermal_iio_sensor_unregister(struct thermal_zone_device *tz) +{ + struct thermal_iio_data *iio_data = iio_priv(tz->indio_dev); + + iio_device_unregister(tz->indio_dev); + iio_triggered_buffer_cleanup(tz->indio_dev); + if (iio_data->interrupt_trig) + iio_trigger_unregister(iio_data->interrupt_trig); + + return 0; +} + +#define IIO_EVENT_CODE_THERMAL_THRES IIO_UNMOD_EVENT_CODE(IIO_TEMP, 0,\ + IIO_EV_TYPE_THRESH,\ + IIO_EV_DIR_EITHER) + +int thermal_iio_sensor_notify(struct thermal_zone_device *tz, + enum thermal_device_event_type event) +{ + struct thermal_iio_data *iio_data = iio_priv(tz->indio_dev); + + mutex_lock(&iio_data->mutex); + if (iio_data->ev_enable_state && + event == THERMAL_DEVICE_EVENT_THRESHOLD) + iio_push_event(tz->indio_dev, + IIO_EVENT_CODE_THERMAL_THRES, + iio_get_time_ns()); + if (iio_data->enable_trigger) + iio_trigger_poll(tz->indio_dev->trig); + mutex_unlock(&iio_data->mutex); + + return 0; +} diff --git a/drivers/thermal/thermal_iio.h b/drivers/thermal/thermal_iio.h new file mode 100644 index 0000000..c7d7168 --- /dev/null +++ b/drivers/thermal/thermal_iio.h @@ -0,0 +1,45 @@ +/* + * thermal iio interface driver interface file + * Copyright (c) 2015, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope 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. + */ + +#ifndef __THERMAL_IIO_H__ +#define __THERMAL_IIO_H__ + +#if defined(CONFIG_THERMAL_IIO) + +int thermal_iio_sensor_register(struct thermal_zone_device *tz); +int thermal_iio_sensor_unregister(struct thermal_zone_device *tz); +int thermal_iio_sensor_notify(struct thermal_zone_device *tz, + enum thermal_device_event_type event); + +#else + +static int thermal_iio_sensor_register(struct thermal_zone_device *tz) +{ + return 0; +} + +static int thermal_iio_sensor_unregister(struct thermal_zone_device *tz) +{ + return 0; +} + +static int thermal_iio_sensor_threshold_notify(struct thermal_zone_device *tz, + enum thermal_device_event_type event); + +{ + return 0; +} + +#endif +#endif diff --git a/include/linux/thermal.h b/include/linux/thermal.h index c074f6a..925d1e5 100644 --- a/include/linux/thermal.h +++ b/include/linux/thermal.h @@ -114,6 +114,13 @@ struct thermal_zone_device_ops { int (*set_emul_temp) (struct thermal_zone_device *, int); int (*get_trend) (struct thermal_zone_device *, int, enum thermal_trend *); + int (*get_threshold_temp)(struct thermal_zone_device *, int, + int *); + int (*set_threshold_temp)(struct thermal_zone_device *, int, + int); + int (*set_notification_status)(struct thermal_zone_device *, + bool status); + bool (*check_notification_support)(struct thermal_zone_device *); int (*notify) (struct thermal_zone_device *, int, enum thermal_trip_type); }; @@ -148,6 +155,8 @@ struct thermal_attr { char name[THERMAL_NAME_LENGTH]; }; +struct iio_dev; + /** * struct thermal_zone_device - structure for a thermal zone * @id: unique id number for each thermal zone @@ -182,6 +191,7 @@ struct thermal_attr { * @lock: lock to protect thermal_instances list * @node: node in thermal_tz_list (in thermal_core.c) * @poll_queue: delayed work for polling + * @indio_dev: pointer to instance of an IIO dev for this zone */ struct thermal_zone_device { int id; @@ -208,6 +218,9 @@ struct thermal_zone_device { struct mutex lock; struct list_head node; struct delayed_work poll_queue; +#if defined(CONFIG_THERMAL_IIO) + struct iio_dev *indio_dev; +#endif }; /** -- 1.9.3 ^ permalink raw reply related [flat|nested] 8+ messages in thread
* Re: [RFC PATCH v2 2/4] thermal: iio device for thermal sensor 2015-09-17 22:30 ` [RFC PATCH v2 2/4] thermal: iio device for thermal sensor Srinivas Pandruvada @ 2015-09-20 10:31 ` Jonathan Cameron 2015-09-21 18:38 ` Srinivas Pandruvada 0 siblings, 1 reply; 8+ messages in thread From: Jonathan Cameron @ 2015-09-20 10:31 UTC (permalink / raw) To: Srinivas Pandruvada, rui.zhang, edubezval; +Cc: linux-pm, linux-iio On 17/09/15 23:30, Srinivas Pandruvada wrote: > This change registers temperature sensor in a thermal zone as an IIO > temperature device. This allows user space thermal application to fully > utilize IIO capability to read temperature events and samples. > The primary motivation for this change to improve performance for user > space thermal controllers like Linux thermal daemon or Intel® Dynamic > Platform and Thermal Framework (DPTF) for Chromium OS. > The current sysfs has several limitations, which forces the current > user space to use polling as the safe way. This polling causes performance > bottlenecks as some devices requires extremely aggressive response to > changing temperature conditions. > These are some limitations, which this change tries to address: > - Temperature reading via sysfs attribute, which needs conversion from > string to int > - No way to set threshold settings to avoid polling for temperature change > without using a RW passive trip point. > - Event notifications via slow kobject uevents, which needs parsing to id > the zone and read temperature > - Only pull interface from user space, kernel drivers can't send samples > - One sample at a time read from user space > These limitations can be addressed by registering temperature sensor > as an IIO device. IIO provides > - buffered access via /dev/iio:deviceX node with select/poll capability > - temperature thresholds setting via iio event thresholds > - Wait and receive events > - Able to use different external trigger (data ready indications) and push > samples to buffers > > Next set of patches uses the IIO binding introduced in this patchset. > The iio device created during call to thermal_zone_device_register. Samples > are pushed to iio buffers when thermal_zone_device_update is called from > client drivers. > > New thermal zone device callbacks: > It introduces three new callbacks for thermal client drivers: > get_threshold_temp: Get the current threshold temperature > set_threshold_temp: Set the current threshold temperature > check_notification_support: Inform that the client driver has asynchronous > notification mechanism. If it is then polling can be avoided for the > temperature sensor. > > Signed-off-by: Srinivas Pandruvada <srinivas.pandruvada@linux.intel.com> > --- > drivers/thermal/Kconfig | 12 ++ > drivers/thermal/Makefile | 1 + > drivers/thermal/thermal_iio.c | 375 ++++++++++++++++++++++++++++++++++++++++++ > drivers/thermal/thermal_iio.h | 45 +++++ > include/linux/thermal.h | 13 ++ > 5 files changed, 446 insertions(+) > create mode 100644 drivers/thermal/thermal_iio.c > create mode 100644 drivers/thermal/thermal_iio.h > > diff --git a/drivers/thermal/Kconfig b/drivers/thermal/Kconfig > index 0390044..bc7836e 100644 > --- a/drivers/thermal/Kconfig > +++ b/drivers/thermal/Kconfig > @@ -29,6 +29,18 @@ config THERMAL_HWMON > Say 'Y' here if you want all thermal sensors to > have hwmon sysfs interface too. > > +config THERMAL_IIO > + bool > + prompt "Thermal sensor from a zone as IIO sensor device" > + select IIO > + select IIO_BUFFER > + select IIO_TRIGGERED_BUFFER > + help > + This will register thermal sensor in a zone as an IIO temperature > + sensor device. > + This will help user space thermal controllers to use IIO ABI to > + get events and buffered acces to temperature samples. > + > config THERMAL_OF > bool > prompt "APIs to parse thermal data out of device tree" > diff --git a/drivers/thermal/Makefile b/drivers/thermal/Makefile > index 26f1608..b72a28d 100644 > --- a/drivers/thermal/Makefile > +++ b/drivers/thermal/Makefile > @@ -7,6 +7,7 @@ thermal_sys-y += thermal_core.o > > # interface to/from other layers providing sensors > thermal_sys-$(CONFIG_THERMAL_HWMON) += thermal_hwmon.o > +thermal_sys-$(CONFIG_THERMAL_IIO) += thermal_iio.o > thermal_sys-$(CONFIG_THERMAL_OF) += of-thermal.o > > # governors > diff --git a/drivers/thermal/thermal_iio.c b/drivers/thermal/thermal_iio.c > new file mode 100644 > index 0000000..9c351e8 > --- /dev/null > +++ b/drivers/thermal/thermal_iio.c > @@ -0,0 +1,375 @@ > +/* > + * thermal iio interface driver > + * Copyright (c) 2015, Intel Corporation. > + * > + * This program is free software; you can redistribute it and/or modify it > + * under the terms and conditions of the GNU General Public License, > + * version 2, as published by the Free Software Foundation. > + * > + * This program is distributed in the hope 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/thermal.h> > +#include <linux/iio/iio.h> > +#include <linux/iio/sysfs.h> > +#include <linux/iio/buffer.h> > +#include <linux/iio/trigger.h> > +#include <linux/iio/events.h> > +#include <linux/iio/trigger_consumer.h> > +#include <linux/iio/triggered_buffer.h> > + > +struct thermal_iio_data { > + struct thermal_zone_device *tz; > + struct iio_trigger *interrupt_trig; > + struct iio_chan_spec *channels; > + bool enable_trigger; > + int temp_thres; > + bool ev_enable_state; > + struct mutex mutex; > +}; > + > +static const struct iio_event_spec thermal_event = { > + .type = IIO_EV_TYPE_THRESH, > + .dir = IIO_EV_DIR_EITHER, > + .mask_separate = BIT(IIO_EV_INFO_VALUE) | > + BIT(IIO_EV_INFO_ENABLE) > +}; > + > +#define THERMAL_TEMP_CHANNELS(event_cnt) { \ > + { \ > + .type = IIO_TEMP, \ > + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \ > + .scan_index = 0, \ > + .scan_type = { \ > + .sign = 'u', \ > + .realbits = 32, \ > + .storagebits = 32, \ > + .endianness = IIO_CPU, \ > + }, \ > + .event_spec = &thermal_event, \ > + .num_event_specs = event_cnt, \ > + }, \ > + IIO_CHAN_SOFT_TIMESTAMP(1), \ > +} > + > +static const struct iio_chan_spec thermal_iio_channels[] = > + THERMAL_TEMP_CHANNELS(0); > +static const struct iio_chan_spec thermal_iio_channels_with_events[] = > + THERMAL_TEMP_CHANNELS(1); Hmm. I'd be tempted to put this out long hand to avoid that case of assigning num_event_specs to 0. That's a little messy for the saving of a couple of lines. > + > +static int thermal_iio_read_raw(struct iio_dev *indio_dev, > + struct iio_chan_spec const *chan, > + int *val, int *val2, long mask) > +{ > + struct thermal_iio_data *iio_data = iio_priv(indio_dev); > + int temp; > + int ret; > + > + switch (mask) { > + case IIO_CHAN_INFO_RAW: > + ret = thermal_zone_get_temp(iio_data->tz, &temp); > + if (!ret) { > + *val = temp; > + ret = IIO_VAL_INT; > + } > + break; > + default: > + ret = -EINVAL; > + } > + > + return ret; > +} > + > +static irqreturn_t thermal_trigger_handler(int irq, void *p) > +{ > + struct iio_poll_func *pf = p; > + struct iio_dev *indio_dev = pf->indio_dev; > + struct thermal_iio_data *iio_data = iio_priv(indio_dev); > + u32 buffer[4]; > + int temp; > + int ret; > + > + ret = thermal_zone_get_temp(iio_data->tz, &temp); > + if (ret) > + goto err_read; > + > + *(u32 *)buffer = temp; > + iio_push_to_buffers_with_timestamp(indio_dev, buffer, > + iio_get_time_ns()); > +err_read: > + iio_trigger_notify_done(indio_dev->trig); > + > + return IRQ_HANDLED; > +} > + > +static int thermal_data_rdy_trigger_set_state(struct iio_trigger *trig, > + bool state) > +{ > + struct iio_dev *indio_dev = iio_trigger_get_drvdata(trig); > + struct thermal_iio_data *iio_data = iio_priv(indio_dev); > + int ret = 0; > + > + mutex_lock(&iio_data->mutex); > + if (iio_data->tz->ops->set_notification_status) { > + ret = iio_data->tz->ops->set_notification_status(iio_data->tz, > + state); > + if (!ret) > + iio_data->enable_trigger = state; > + } else > + iio_data->enable_trigger = state; > + mutex_unlock(&iio_data->mutex); > + > + return ret; > +} > + > +static int thermal_iio_validate_trigger(struct iio_dev *indio_dev, > + struct iio_trigger *trig) > +{ > + struct thermal_iio_data *iio_data = iio_priv(indio_dev); > + > + if (iio_data->interrupt_trig && iio_data->interrupt_trig != trig) > + return -EINVAL; > + > + return 0; > +} > + > +static const struct iio_trigger_ops thermal_trigger_ops = { > + .set_trigger_state = thermal_data_rdy_trigger_set_state, > + .owner = THIS_MODULE, > +}; > + > +static int thermal_iio_read_event(struct iio_dev *indio_dev, > + const struct iio_chan_spec *chan, > + enum iio_event_type type, > + enum iio_event_direction dir, > + enum iio_event_info info, > + int *val, int *val2) > +{ > + struct thermal_iio_data *iio_data = iio_priv(indio_dev); > + int ret; > + > + mutex_lock(&iio_data->mutex); You shouldn't need to assign a value to val2 if you are returning IIO_VAL_INT. > + *val2 = 0; > + switch (info) { > + case IIO_EV_INFO_VALUE: > + *val = iio_data->temp_thres; > + ret = IIO_VAL_INT; > + break; > + default: > + ret = -EINVAL; > + } > + mutex_unlock(&iio_data->mutex); > + > + return ret; > +} > + > +static int thermal_iio_write_event(struct iio_dev *indio_dev, > + const struct iio_chan_spec *chan, > + enum iio_event_type type, > + enum iio_event_direction dir, > + enum iio_event_info info, > + int val, int val2) > +{ > + struct thermal_iio_data *iio_data = iio_priv(indio_dev); > + int ret = 0; > + > + mutex_lock(&iio_data->mutex); > + switch (info) { > + case IIO_EV_INFO_VALUE: > + iio_data->temp_thres = val; > + break; > + default: > + ret = -EINVAL; > + } > + mutex_unlock(&iio_data->mutex); > + > + return ret; > +} > + > +static int thermal_iio_read_event_config(struct iio_dev *indio_dev, > + const struct iio_chan_spec *chan, > + enum iio_event_type type, > + enum iio_event_direction dir) > +{ > + struct thermal_iio_data *iio_data = iio_priv(indio_dev); > + bool state; > + > + mutex_lock(&iio_data->mutex); > + state = iio_data->ev_enable_state; > + mutex_unlock(&iio_data->mutex); > + > + return state; > +} > + > +static int thermal_iio_write_event_config(struct iio_dev *indio_dev, > + const struct iio_chan_spec *chan, > + enum iio_event_type type, > + enum iio_event_direction dir, > + int state) > +{ > + struct thermal_iio_data *iio_data = iio_priv(indio_dev); > + int temp_thres; > + int ret = 0; > + > + mutex_lock(&iio_data->mutex); > + if ((state && iio_data->ev_enable_state) || > + (!state && !iio_data->ev_enable_state)) > + goto done_write_event; > + > + if (state && !iio_data->temp_thres) { > + ret = -EINVAL; > + goto done_write_event; > + } > + > + if (state) > + temp_thres = iio_data->temp_thres; > + else > + temp_thres = 0; That's fun. Guess the freezing point is never of relevance here. (in actively cooled systems it could be...) > + > + ret = iio_data->tz->ops->set_threshold_temp(iio_data->tz, 0, > + temp_thres); > + if (ret) > + goto done_write_event; > + > + if (iio_data->tz->ops->set_notification_status) { > + ret = iio_data->tz->ops->set_notification_status(iio_data->tz, > + state > 0); > + if (!ret) > + iio_data->ev_enable_state = state; > + } else > + iio_data->ev_enable_state = state; > + > +done_write_event: > + mutex_unlock(&iio_data->mutex); > + > + return ret; > +} > + > +static int thermal_iio_setup_trig(struct iio_trigger **iio_trig, > + struct thermal_zone_device *tz, > + const char *format) > +{ > + struct iio_trigger *trig; > + int ret; > + > + trig = devm_iio_trigger_alloc(&tz->device, format, tz->type, > + tz->indio_dev->id); > + if (!trig) { > + dev_err(&tz->device, "Trigger Allocate Failed\n"); > + return -ENOMEM; > + } > + trig->dev.parent = &tz->device; > + trig->ops = &thermal_trigger_ops; > + iio_trigger_set_drvdata(trig, tz->indio_dev); > + ret = iio_trigger_register(trig); > + if (ret) { > + dev_err(&tz->device, "Trigger Allocate Failed\n"); > + return ret; > + } > + *iio_trig = trig; I'm cynical. Any failure in this function should result in the *iio_trig not being used anyway so I'd be tempted to write directly into it in the first place rathe rthan having the local variable. Still their are obvious maintainability and semantic reasons for not assigning it until success so feel free to keep it as it is! You write nice consistent code (more so than me I suspect!) > + > + return 0; > +} > + > +static const struct iio_info thermal_iio_info = { > + .read_raw = thermal_iio_read_raw, > + .read_event_value = thermal_iio_read_event, > + .write_event_value = thermal_iio_write_event, > + .write_event_config = thermal_iio_write_event_config, > + .read_event_config = thermal_iio_read_event_config, > + .validate_trigger = thermal_iio_validate_trigger, > + .driver_module = THIS_MODULE, > +}; > + > +int thermal_iio_sensor_register(struct thermal_zone_device *tz) > +{ > + struct thermal_iio_data *iio_data; > + int ret; > + > + tz->indio_dev = devm_iio_device_alloc(&tz->device, sizeof(*iio_data)); > + if (!tz->indio_dev) > + return -ENOMEM; > + > + iio_data = iio_priv(tz->indio_dev); > + iio_data->tz = tz; > + mutex_init(&iio_data->mutex); > + > + tz->indio_dev->dev.parent = &tz->device; > + if (tz->ops->set_threshold_temp) > + tz->indio_dev->channels = thermal_iio_channels_with_events; > + else > + tz->indio_dev->channels = thermal_iio_channels; > + tz->indio_dev->num_channels = ARRAY_SIZE(thermal_iio_channels); > + tz->indio_dev->name = tz->type; > + tz->indio_dev->info = &thermal_iio_info; > + tz->indio_dev->modes = INDIO_DIRECT_MODE; > + > + if (tz->ops->check_notification_support && > + tz->ops->check_notification_support(tz)) { > + ret = thermal_iio_setup_trig(&iio_data->interrupt_trig, tz, > + "%s-dev%d"); Just a thought, I wonder if we want the trigger naming to make it obvious that this is tripping on an event rather than simply being a new value available? As this whole driver is a little 'special' in terms of what it is for, perhaps an explicit documentation file would be a useful addition? Maybe even with a basic illustration of how this might be used with a governer? (maybe this is obvious to people from the thermal side of things ;) Quite a bit of what we'd want is in your patch descriptions! > + if (ret) > + return ret; > + > + tz->indio_dev->trig = iio_trigger_get(iio_data->interrupt_trig); > + } > + ret = iio_triggered_buffer_setup(tz->indio_dev, > + &iio_pollfunc_store_time, > + thermal_trigger_handler, NULL); > + if (ret) { > + dev_err(&tz->device, "failed to init trigger buffer\n"); > + goto err_unreg_int_trig; > + } > + ret = iio_device_register(tz->indio_dev); > + if (ret < 0) { > + dev_err(&tz->device, "unable to register iio device\n"); > + goto err_cleanup_trig; > + } > + > + return 0; > + > +err_cleanup_trig: > + iio_triggered_buffer_cleanup(tz->indio_dev); > +err_unreg_int_trig: > + if (iio_data->interrupt_trig) > + iio_trigger_unregister(iio_data->interrupt_trig); > + > + return ret; > +} > + > +int thermal_iio_sensor_unregister(struct thermal_zone_device *tz) > +{ > + struct thermal_iio_data *iio_data = iio_priv(tz->indio_dev); > + > + iio_device_unregister(tz->indio_dev); > + iio_triggered_buffer_cleanup(tz->indio_dev); > + if (iio_data->interrupt_trig) > + iio_trigger_unregister(iio_data->interrupt_trig); > + > + return 0; > +} > + > +#define IIO_EVENT_CODE_THERMAL_THRES IIO_UNMOD_EVENT_CODE(IIO_TEMP, 0,\ > + IIO_EV_TYPE_THRESH,\ > + IIO_EV_DIR_EITHER) > + > +int thermal_iio_sensor_notify(struct thermal_zone_device *tz, > + enum thermal_device_event_type event) > +{ > + struct thermal_iio_data *iio_data = iio_priv(tz->indio_dev); > + > + mutex_lock(&iio_data->mutex); > + if (iio_data->ev_enable_state && > + event == THERMAL_DEVICE_EVENT_THRESHOLD) > + iio_push_event(tz->indio_dev, As this macro is only used in one place, I would slightly prefer that you just had it expanded here. Not a major point though as I don't have to look far for the definition after all! Is there really no way of knowing if it was a rising or a falling trip? I would have thought that info was vital for a userspace governer! I guess it just has to follow up with an explicit temperature read to find out what happened. > + IIO_EVENT_CODE_THERMAL_THRES, > + iio_get_time_ns()); > + if (iio_data->enable_trigger) > + iio_trigger_poll(tz->indio_dev->trig); I wonder if you want to allow specification of which of your event types you want to trigger on. Hmm. I'd suggest separate triggers but right now we don't support multiple triggers of one buffer in IIO. Perhaps we need to consider if there is a major enough usecase to support that in future. > + mutex_unlock(&iio_data->mutex); > + > + return 0; > +} > diff --git a/drivers/thermal/thermal_iio.h b/drivers/thermal/thermal_iio.h > new file mode 100644 > index 0000000..c7d7168 > --- /dev/null > +++ b/drivers/thermal/thermal_iio.h > @@ -0,0 +1,45 @@ > +/* > + * thermal iio interface driver interface file > + * Copyright (c) 2015, Intel Corporation. > + * > + * This program is free software; you can redistribute it and/or modify it > + * under the terms and conditions of the GNU General Public License, > + * version 2, as published by the Free Software Foundation. > + * > + * This program is distributed in the hope 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. > + */ > + > +#ifndef __THERMAL_IIO_H__ > +#define __THERMAL_IIO_H__ > + > +#if defined(CONFIG_THERMAL_IIO) > + > +int thermal_iio_sensor_register(struct thermal_zone_device *tz); > +int thermal_iio_sensor_unregister(struct thermal_zone_device *tz); > +int thermal_iio_sensor_notify(struct thermal_zone_device *tz, > + enum thermal_device_event_type event); > + > +#else > + > +static int thermal_iio_sensor_register(struct thermal_zone_device *tz) > +{ > + return 0; > +} > + > +static int thermal_iio_sensor_unregister(struct thermal_zone_device *tz) > +{ > + return 0; > +} > + > +static int thermal_iio_sensor_threshold_notify(struct thermal_zone_device *tz, > + enum thermal_device_event_type event); > + > +{ > + return 0; > +} > + > +#endif > +#endif > diff --git a/include/linux/thermal.h b/include/linux/thermal.h > index c074f6a..925d1e5 100644 > --- a/include/linux/thermal.h > +++ b/include/linux/thermal.h > @@ -114,6 +114,13 @@ struct thermal_zone_device_ops { > int (*set_emul_temp) (struct thermal_zone_device *, int); > int (*get_trend) (struct thermal_zone_device *, int, > enum thermal_trend *); > + int (*get_threshold_temp)(struct thermal_zone_device *, int, > + int *); > + int (*set_threshold_temp)(struct thermal_zone_device *, int, > + int); > + int (*set_notification_status)(struct thermal_zone_device *, > + bool status); > + bool (*check_notification_support)(struct thermal_zone_device *); > int (*notify) (struct thermal_zone_device *, int, > enum thermal_trip_type); > }; > @@ -148,6 +155,8 @@ struct thermal_attr { > char name[THERMAL_NAME_LENGTH]; > }; > > +struct iio_dev; > + > /** > * struct thermal_zone_device - structure for a thermal zone > * @id: unique id number for each thermal zone > @@ -182,6 +191,7 @@ struct thermal_attr { > * @lock: lock to protect thermal_instances list > * @node: node in thermal_tz_list (in thermal_core.c) > * @poll_queue: delayed work for polling > + * @indio_dev: pointer to instance of an IIO dev for this zone > */ > struct thermal_zone_device { > int id; > @@ -208,6 +218,9 @@ struct thermal_zone_device { > struct mutex lock; > struct list_head node; > struct delayed_work poll_queue; > +#if defined(CONFIG_THERMAL_IIO) > + struct iio_dev *indio_dev; > +#endif > }; > > /** > ^ permalink raw reply [flat|nested] 8+ messages in thread
* Re: [RFC PATCH v2 2/4] thermal: iio device for thermal sensor 2015-09-20 10:31 ` Jonathan Cameron @ 2015-09-21 18:38 ` Srinivas Pandruvada 0 siblings, 0 replies; 8+ messages in thread From: Srinivas Pandruvada @ 2015-09-21 18:38 UTC (permalink / raw) To: Jonathan Cameron; +Cc: rui.zhang, edubezval, linux-pm, linux-iio On Sun, 2015-09-20 at 11:31 +0100, Jonathan Cameron wrote: > On 17/09/15 23:30, Srinivas Pandruvada wrote: > > This change registers temperature sensor in a thermal zone as an IIO > > temperature device. This allows user space thermal application to fully > > utilize IIO capability to read temperature events and samples. > > The primary motivation for this change to improve performance for user > > space thermal controllers like Linux thermal daemon or Intel® Dynamic > > Platform and Thermal Framework (DPTF) for Chromium OS. > > The current sysfs has several limitations, which forces the current > > user space to use polling as the safe way. This polling causes performance > > bottlenecks as some devices requires extremely aggressive response to > > changing temperature conditions. > > These are some limitations, which this change tries to address: > > - Temperature reading via sysfs attribute, which needs conversion from > > string to int > > - No way to set threshold settings to avoid polling for temperature change > > without using a RW passive trip point. > > - Event notifications via slow kobject uevents, which needs parsing to id > > the zone and read temperature > > - Only pull interface from user space, kernel drivers can't send samples > > - One sample at a time read from user space > > These limitations can be addressed by registering temperature sensor > > as an IIO device. IIO provides > > - buffered access via /dev/iio:deviceX node with select/poll capability > > - temperature thresholds setting via iio event thresholds > > - Wait and receive events > > - Able to use different external trigger (data ready indications) and push > > samples to buffers > > > > Next set of patches uses the IIO binding introduced in this patchset. > > The iio device created during call to thermal_zone_device_register. Samples > > are pushed to iio buffers when thermal_zone_device_update is called from > > client drivers. > > > > New thermal zone device callbacks: > > It introduces three new callbacks for thermal client drivers: > > get_threshold_temp: Get the current threshold temperature > > set_threshold_temp: Set the current threshold temperature > > check_notification_support: Inform that the client driver has asynchronous > > notification mechanism. If it is then polling can be avoided for the > > temperature sensor. > > > > Signed-off-by: Srinivas Pandruvada <srinivas.pandruvada@linux.intel.com> > > --- > > drivers/thermal/Kconfig | 12 ++ > > drivers/thermal/Makefile | 1 + > > drivers/thermal/thermal_iio.c | 375 ++++++++++++++++++++++++++++++++++++++++++ > > drivers/thermal/thermal_iio.h | 45 +++++ > > include/linux/thermal.h | 13 ++ > > 5 files changed, 446 insertions(+) > > create mode 100644 drivers/thermal/thermal_iio.c > > create mode 100644 drivers/thermal/thermal_iio.h > > > > diff --git a/drivers/thermal/Kconfig b/drivers/thermal/Kconfig > > index 0390044..bc7836e 100644 > > --- a/drivers/thermal/Kconfig > > +++ b/drivers/thermal/Kconfig > > @@ -29,6 +29,18 @@ config THERMAL_HWMON > > Say 'Y' here if you want all thermal sensors to > > have hwmon sysfs interface too. > > > > +config THERMAL_IIO > > + bool > > + prompt "Thermal sensor from a zone as IIO sensor device" > > + select IIO > > + select IIO_BUFFER > > + select IIO_TRIGGERED_BUFFER > > + help > > + This will register thermal sensor in a zone as an IIO temperature > > + sensor device. > > + This will help user space thermal controllers to use IIO ABI to > > + get events and buffered acces to temperature samples. > > + > > config THERMAL_OF > > bool > > prompt "APIs to parse thermal data out of device tree" > > diff --git a/drivers/thermal/Makefile b/drivers/thermal/Makefile > > index 26f1608..b72a28d 100644 > > --- a/drivers/thermal/Makefile > > +++ b/drivers/thermal/Makefile > > @@ -7,6 +7,7 @@ thermal_sys-y += thermal_core.o > > > > # interface to/from other layers providing sensors > > thermal_sys-$(CONFIG_THERMAL_HWMON) += thermal_hwmon.o > > +thermal_sys-$(CONFIG_THERMAL_IIO) += thermal_iio.o > > thermal_sys-$(CONFIG_THERMAL_OF) += of-thermal.o > > > > # governors > > diff --git a/drivers/thermal/thermal_iio.c b/drivers/thermal/thermal_iio.c > > new file mode 100644 > > index 0000000..9c351e8 > > --- /dev/null > > +++ b/drivers/thermal/thermal_iio.c > > @@ -0,0 +1,375 @@ > > +/* > > + * thermal iio interface driver > > + * Copyright (c) 2015, Intel Corporation. > > + * > > + * This program is free software; you can redistribute it and/or modify it > > + * under the terms and conditions of the GNU General Public License, > > + * version 2, as published by the Free Software Foundation. > > + * > > + * This program is distributed in the hope 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/thermal.h> > > +#include <linux/iio/iio.h> > > +#include <linux/iio/sysfs.h> > > +#include <linux/iio/buffer.h> > > +#include <linux/iio/trigger.h> > > +#include <linux/iio/events.h> > > +#include <linux/iio/trigger_consumer.h> > > +#include <linux/iio/triggered_buffer.h> > > + > > +struct thermal_iio_data { > > + struct thermal_zone_device *tz; > > + struct iio_trigger *interrupt_trig; > > + struct iio_chan_spec *channels; > > + bool enable_trigger; > > + int temp_thres; > > + bool ev_enable_state; > > + struct mutex mutex; > > +}; > > + > > +static const struct iio_event_spec thermal_event = { > > + .type = IIO_EV_TYPE_THRESH, > > + .dir = IIO_EV_DIR_EITHER, > > + .mask_separate = BIT(IIO_EV_INFO_VALUE) | > > + BIT(IIO_EV_INFO_ENABLE) > > +}; > > + > > +#define THERMAL_TEMP_CHANNELS(event_cnt) { \ > > + { \ > > + .type = IIO_TEMP, \ > > + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \ > > + .scan_index = 0, \ > > + .scan_type = { \ > > + .sign = 'u', \ > > + .realbits = 32, \ > > + .storagebits = 32, \ > > + .endianness = IIO_CPU, \ > > + }, \ > > + .event_spec = &thermal_event, \ > > + .num_event_specs = event_cnt, \ > > + }, \ > > + IIO_CHAN_SOFT_TIMESTAMP(1), \ > > +} > > + > > +static const struct iio_chan_spec thermal_iio_channels[] = > > + THERMAL_TEMP_CHANNELS(0); > > +static const struct iio_chan_spec thermal_iio_channels_with_events[] = > > + THERMAL_TEMP_CHANNELS(1); > Hmm. I'd be tempted to put this out long hand to avoid that case of assigning > num_event_specs to 0. That's a little messy for the saving of a couple > of lines. > I will do in the next revision. > > + > > +static int thermal_iio_read_raw(struct iio_dev *indio_dev, > > + struct iio_chan_spec const *chan, > > + int *val, int *val2, long mask) > > +{ > > + struct thermal_iio_data *iio_data = iio_priv(indio_dev); > > + int temp; > > + int ret; > > + > > + switch (mask) { > > + case IIO_CHAN_INFO_RAW: > > + ret = thermal_zone_get_temp(iio_data->tz, &temp); > > + if (!ret) { > > + *val = temp; > > + ret = IIO_VAL_INT; > > + } > > + break; > > + default: > > + ret = -EINVAL; > > + } > > + > > + return ret; > > +} > > + > > +static irqreturn_t thermal_trigger_handler(int irq, void *p) > > +{ > > + struct iio_poll_func *pf = p; > > + struct iio_dev *indio_dev = pf->indio_dev; > > + struct thermal_iio_data *iio_data = iio_priv(indio_dev); > > + u32 buffer[4]; > > + int temp; > > + int ret; > > + > > + ret = thermal_zone_get_temp(iio_data->tz, &temp); > > + if (ret) > > + goto err_read; > > + > > + *(u32 *)buffer = temp; > > + iio_push_to_buffers_with_timestamp(indio_dev, buffer, > > + iio_get_time_ns()); > > +err_read: > > + iio_trigger_notify_done(indio_dev->trig); > > + > > + return IRQ_HANDLED; > > +} > > + > > +static int thermal_data_rdy_trigger_set_state(struct iio_trigger *trig, > > + bool state) > > +{ > > + struct iio_dev *indio_dev = iio_trigger_get_drvdata(trig); > > + struct thermal_iio_data *iio_data = iio_priv(indio_dev); > > + int ret = 0; > > + > > + mutex_lock(&iio_data->mutex); > > + if (iio_data->tz->ops->set_notification_status) { > > + ret = iio_data->tz->ops->set_notification_status(iio_data->tz, > > + state); > > + if (!ret) > > + iio_data->enable_trigger = state; > > + } else > > + iio_data->enable_trigger = state; > > + mutex_unlock(&iio_data->mutex); > > + > > + return ret; > > +} > > + > > +static int thermal_iio_validate_trigger(struct iio_dev *indio_dev, > > + struct iio_trigger *trig) > > +{ > > + struct thermal_iio_data *iio_data = iio_priv(indio_dev); > > + > > + if (iio_data->interrupt_trig && iio_data->interrupt_trig != trig) > > + return -EINVAL; > > + > > + return 0; > > +} > > + > > +static const struct iio_trigger_ops thermal_trigger_ops = { > > + .set_trigger_state = thermal_data_rdy_trigger_set_state, > > + .owner = THIS_MODULE, > > +}; > > + > > +static int thermal_iio_read_event(struct iio_dev *indio_dev, > > + const struct iio_chan_spec *chan, > > + enum iio_event_type type, > > + enum iio_event_direction dir, > > + enum iio_event_info info, > > + int *val, int *val2) > > +{ > > + struct thermal_iio_data *iio_data = iio_priv(indio_dev); > > + int ret; > > + > > + mutex_lock(&iio_data->mutex); > You shouldn't need to assign a value to val2 if you are returning IIO_VAL_INT. OK > > + *val2 = 0; > > + switch (info) { > > + case IIO_EV_INFO_VALUE: > > + *val = iio_data->temp_thres; > > + ret = IIO_VAL_INT; > > + break; > > + default: > > + ret = -EINVAL; > > + } > > + mutex_unlock(&iio_data->mutex); > > + > > + return ret; > > +} > > + > > +static int thermal_iio_write_event(struct iio_dev *indio_dev, > > + const struct iio_chan_spec *chan, > > + enum iio_event_type type, > > + enum iio_event_direction dir, > > + enum iio_event_info info, > > + int val, int val2) > > +{ > > + struct thermal_iio_data *iio_data = iio_priv(indio_dev); > > + int ret = 0; > > + > > + mutex_lock(&iio_data->mutex); > > + switch (info) { > > + case IIO_EV_INFO_VALUE: > > + iio_data->temp_thres = val; > > + break; > > + default: > > + ret = -EINVAL; > > + } > > + mutex_unlock(&iio_data->mutex); > > + > > + return ret; > > +} > > + > > +static int thermal_iio_read_event_config(struct iio_dev *indio_dev, > > + const struct iio_chan_spec *chan, > > + enum iio_event_type type, > > + enum iio_event_direction dir) > > +{ > > + struct thermal_iio_data *iio_data = iio_priv(indio_dev); > > + bool state; > > + > > + mutex_lock(&iio_data->mutex); > > + state = iio_data->ev_enable_state; > > + mutex_unlock(&iio_data->mutex); > > + > > + return state; > > +} > > + > > +static int thermal_iio_write_event_config(struct iio_dev *indio_dev, > > + const struct iio_chan_spec *chan, > > + enum iio_event_type type, > > + enum iio_event_direction dir, > > + int state) > > +{ > > + struct thermal_iio_data *iio_data = iio_priv(indio_dev); > > + int temp_thres; > > + int ret = 0; > > + > > + mutex_lock(&iio_data->mutex); > > + if ((state && iio_data->ev_enable_state) || > > + (!state && !iio_data->ev_enable_state)) > > + goto done_write_event; > > + > > + if (state && !iio_data->temp_thres) { > > + ret = -EINVAL; > > + goto done_write_event; > > + } > > + > > + if (state) > > + temp_thres = iio_data->temp_thres; > > + else > > + temp_thres = 0; > That's fun. Guess the freezing point is never of relevance here. > (in actively cooled systems it could be...) Setting 0 means stop notification as we will not reach for a thermal sensor, I guess. > > + > > + ret = iio_data->tz->ops->set_threshold_temp(iio_data->tz, 0, > > + temp_thres); > > + if (ret) > > + goto done_write_event; > > + > > + if (iio_data->tz->ops->set_notification_status) { > > + ret = iio_data->tz->ops->set_notification_status(iio_data->tz, > > + state > 0); > > + if (!ret) > > + iio_data->ev_enable_state = state; > > + } else > > + iio_data->ev_enable_state = state; > > + > > +done_write_event: > > + mutex_unlock(&iio_data->mutex); > > + > > + return ret; > > +} > > + > > +static int thermal_iio_setup_trig(struct iio_trigger **iio_trig, > > + struct thermal_zone_device *tz, > > + const char *format) > > +{ > > + struct iio_trigger *trig; > > + int ret; > > + > > + trig = devm_iio_trigger_alloc(&tz->device, format, tz->type, > > + tz->indio_dev->id); > > + if (!trig) { > > + dev_err(&tz->device, "Trigger Allocate Failed\n"); > > + return -ENOMEM; > > + } > > + trig->dev.parent = &tz->device; > > + trig->ops = &thermal_trigger_ops; > > + iio_trigger_set_drvdata(trig, tz->indio_dev); > > + ret = iio_trigger_register(trig); > > + if (ret) { > > + dev_err(&tz->device, "Trigger Allocate Failed\n"); > > + return ret; > > + } > > + *iio_trig = trig; > > I'm cynical. Any failure in this function should result in the > *iio_trig not being used anyway so I'd be tempted to write > directly into it in the first place rathe rthan having the local > variable. Still their are obvious maintainability and semantic reasons > for not assigning it until success so feel free to keep it as it is! > You write nice consistent code (more so than me I suspect!) > I usually prefer to not mess up the original memory till we have some valid value. But in this case since the probe will fail on error, we can use the *iio_trig instead of local variable. > > + > > + return 0; > > +} > > + > > +static const struct iio_info thermal_iio_info = { > > + .read_raw = thermal_iio_read_raw, > > + .read_event_value = thermal_iio_read_event, > > + .write_event_value = thermal_iio_write_event, > > + .write_event_config = thermal_iio_write_event_config, > > + .read_event_config = thermal_iio_read_event_config, > > + .validate_trigger = thermal_iio_validate_trigger, > > + .driver_module = THIS_MODULE, > > +}; > > + > > +int thermal_iio_sensor_register(struct thermal_zone_device *tz) > > +{ > > + struct thermal_iio_data *iio_data; > > + int ret; > > + > > + tz->indio_dev = devm_iio_device_alloc(&tz->device, sizeof(*iio_data)); > > + if (!tz->indio_dev) > > + return -ENOMEM; > > + > > + iio_data = iio_priv(tz->indio_dev); > > + iio_data->tz = tz; > > + mutex_init(&iio_data->mutex); > > + > > + tz->indio_dev->dev.parent = &tz->device; > > + if (tz->ops->set_threshold_temp) > > + tz->indio_dev->channels = thermal_iio_channels_with_events; > > + else > > + tz->indio_dev->channels = thermal_iio_channels; > > + tz->indio_dev->num_channels = ARRAY_SIZE(thermal_iio_channels); > > + tz->indio_dev->name = tz->type; > > + tz->indio_dev->info = &thermal_iio_info; > > + tz->indio_dev->modes = INDIO_DIRECT_MODE; > > + > > + if (tz->ops->check_notification_support && > > + tz->ops->check_notification_support(tz)) { > > + ret = thermal_iio_setup_trig(&iio_data->interrupt_trig, tz, > > + "%s-dev%d"); > Just a thought, I wonder if we want the trigger naming to make it obvious > that this is tripping on an event rather than simply being a new value > available? > > As this whole driver is a little 'special' in terms of what it is for, > perhaps an explicit documentation file would be a useful addition? > Maybe even with a basic illustration of how this might be used with a > governer? > (maybe this is obvious to people from the thermal side of things ;) > Quite a bit of what we'd want is in your patch descriptions! Good idea. I will add a documentation file for this. > > + if (ret) > > + return ret; > > + > > + tz->indio_dev->trig = iio_trigger_get(iio_data->interrupt_trig); > > + } > > + ret = iio_triggered_buffer_setup(tz->indio_dev, > > + &iio_pollfunc_store_time, > > + thermal_trigger_handler, NULL); > > + if (ret) { > > + dev_err(&tz->device, "failed to init trigger buffer\n"); > > + goto err_unreg_int_trig; > > + } > > + ret = iio_device_register(tz->indio_dev); > > + if (ret < 0) { > > + dev_err(&tz->device, "unable to register iio device\n"); > > + goto err_cleanup_trig; > > + } > > + > > + return 0; > > + > > +err_cleanup_trig: > > + iio_triggered_buffer_cleanup(tz->indio_dev); > > +err_unreg_int_trig: > > + if (iio_data->interrupt_trig) > > + iio_trigger_unregister(iio_data->interrupt_trig); > > + > > + return ret; > > +} > > + > > +int thermal_iio_sensor_unregister(struct thermal_zone_device *tz) > > +{ > > + struct thermal_iio_data *iio_data = iio_priv(tz->indio_dev); > > + > > + iio_device_unregister(tz->indio_dev); > > + iio_triggered_buffer_cleanup(tz->indio_dev); > > + if (iio_data->interrupt_trig) > > + iio_trigger_unregister(iio_data->interrupt_trig); > > + > > + return 0; > > +} > > + > > +#define IIO_EVENT_CODE_THERMAL_THRES IIO_UNMOD_EVENT_CODE(IIO_TEMP, 0,\ > > + IIO_EV_TYPE_THRESH,\ > > + IIO_EV_DIR_EITHER) > > + > > +int thermal_iio_sensor_notify(struct thermal_zone_device *tz, > > + enum thermal_device_event_type event) > > +{ > > + struct thermal_iio_data *iio_data = iio_priv(tz->indio_dev); > > + > > + mutex_lock(&iio_data->mutex); > > + if (iio_data->ev_enable_state && > > + event == THERMAL_DEVICE_EVENT_THRESHOLD) > > + iio_push_event(tz->indio_dev, > > As this macro is only used in one place, I would slightly prefer > that you just had it expanded here. Not a major point though as I don't > have to look far for the definition after all! > OK. > Is there really no way of knowing if it was a rising or a falling trip? > I would have thought that info was vital for a userspace governer! > I guess it just has to follow up with an explicit temperature read to find > out what happened. > Good idea. The currently x86 supported thermal drivers can't do but some other can do, so better to enhance. > > + IIO_EVENT_CODE_THERMAL_THRES, > > + iio_get_time_ns()); > > + if (iio_data->enable_trigger) > > + iio_trigger_poll(tz->indio_dev->trig); > I wonder if you want to allow specification of which of your event types > you want to trigger on. Hmm. I'd suggest separate triggers but right now we > don't support multiple triggers of one buffer in IIO. Perhaps we need to > consider if there is a major enough usecase to support that in future. > Let me think on this. Thanks, Srinivas > > + mutex_unlock(&iio_data->mutex); > > + > > + return 0; > > +} > > diff --git a/drivers/thermal/thermal_iio.h b/drivers/thermal/thermal_iio.h > > new file mode 100644 > > index 0000000..c7d7168 > > --- /dev/null > > +++ b/drivers/thermal/thermal_iio.h > > @@ -0,0 +1,45 @@ > > +/* > > + * thermal iio interface driver interface file > > + * Copyright (c) 2015, Intel Corporation. > > + * > > + * This program is free software; you can redistribute it and/or modify it > > + * under the terms and conditions of the GNU General Public License, > > + * version 2, as published by the Free Software Foundation. > > + * > > + * This program is distributed in the hope 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. > > + */ > > + > > +#ifndef __THERMAL_IIO_H__ > > +#define __THERMAL_IIO_H__ > > + > > +#if defined(CONFIG_THERMAL_IIO) > > + > > +int thermal_iio_sensor_register(struct thermal_zone_device *tz); > > +int thermal_iio_sensor_unregister(struct thermal_zone_device *tz); > > +int thermal_iio_sensor_notify(struct thermal_zone_device *tz, > > + enum thermal_device_event_type event); > > + > > +#else > > + > > +static int thermal_iio_sensor_register(struct thermal_zone_device *tz) > > +{ > > + return 0; > > +} > > + > > +static int thermal_iio_sensor_unregister(struct thermal_zone_device *tz) > > +{ > > + return 0; > > +} > > + > > +static int thermal_iio_sensor_threshold_notify(struct thermal_zone_device *tz, > > + enum thermal_device_event_type event); > > + > > +{ > > + return 0; > > +} > > + > > +#endif > > +#endif > > diff --git a/include/linux/thermal.h b/include/linux/thermal.h > > index c074f6a..925d1e5 100644 > > --- a/include/linux/thermal.h > > +++ b/include/linux/thermal.h > > @@ -114,6 +114,13 @@ struct thermal_zone_device_ops { > > int (*set_emul_temp) (struct thermal_zone_device *, int); > > int (*get_trend) (struct thermal_zone_device *, int, > > enum thermal_trend *); > > + int (*get_threshold_temp)(struct thermal_zone_device *, int, > > + int *); > > + int (*set_threshold_temp)(struct thermal_zone_device *, int, > > + int); > > + int (*set_notification_status)(struct thermal_zone_device *, > > + bool status); > > + bool (*check_notification_support)(struct thermal_zone_device *); > > int (*notify) (struct thermal_zone_device *, int, > > enum thermal_trip_type); > > }; > > @@ -148,6 +155,8 @@ struct thermal_attr { > > char name[THERMAL_NAME_LENGTH]; > > }; > > > > +struct iio_dev; > > + > > /** > > * struct thermal_zone_device - structure for a thermal zone > > * @id: unique id number for each thermal zone > > @@ -182,6 +191,7 @@ struct thermal_attr { > > * @lock: lock to protect thermal_instances list > > * @node: node in thermal_tz_list (in thermal_core.c) > > * @poll_queue: delayed work for polling > > + * @indio_dev: pointer to instance of an IIO dev for this zone > > */ > > struct thermal_zone_device { > > int id; > > @@ -208,6 +218,9 @@ struct thermal_zone_device { > > struct mutex lock; > > struct list_head node; > > struct delayed_work poll_queue; > > +#if defined(CONFIG_THERMAL_IIO) > > + struct iio_dev *indio_dev; > > +#endif > > }; > > > > /** > > > > -- > To unsubscribe from this list: send the line "unsubscribe linux-pm" in > the body of a message to majordomo@vger.kernel.org > More majordomo info at http://vger.kernel.org/majordomo-info.html ^ permalink raw reply [flat|nested] 8+ messages in thread
* [RFC PATCH v2 3/4] thermal: use iio binding calls [not found] ` <1442529054-25803-1-git-send-email-srinivas.pandruvada-VuQAYsv1563Yd54FQh9/CA@public.gmane.org> 2015-09-17 22:30 ` [RFC PATCH v2 2/4] thermal: iio device for thermal sensor Srinivas Pandruvada @ 2015-09-17 22:30 ` Srinivas Pandruvada 2015-09-17 22:30 ` [RFC PATCH v2 4/4] thermal: x86_pkg_temp: Register threshold callbacks Srinivas Pandruvada 2 siblings, 0 replies; 8+ messages in thread From: Srinivas Pandruvada @ 2015-09-17 22:30 UTC (permalink / raw) To: jic23-DgEjT+Ai2ygdnm+yROfE0A, rui.zhang-ral2JQCrhuEAvxtiuMwx3w, edubezval-Re5JQEeQqe8AvxtiuMwx3w Cc: linux-pm-u79uwXL29TY76Z2rM5mHXA, linux-iio-u79uwXL29TY76Z2rM5mHXA, Srinivas Pandruvada This change: Register zone temperature sensor as IIO device during zone registration, Remove iio device during zone removal, Notify iio binding during thermal_device_update call. Signed-off-by: Srinivas Pandruvada <srinivas.pandruvada-VuQAYsv1563Yd54FQh9/CA@public.gmane.org> --- drivers/thermal/thermal_core.c | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/drivers/thermal/thermal_core.c b/drivers/thermal/thermal_core.c index 18e7802..dbde29f 100644 --- a/drivers/thermal/thermal_core.c +++ b/drivers/thermal/thermal_core.c @@ -43,6 +43,7 @@ #include "thermal_core.h" #include "thermal_hwmon.h" +#include "thermal_iio.h" MODULE_AUTHOR("Zhang Rui"); MODULE_DESCRIPTION("Generic thermal management sysfs support"); @@ -544,6 +545,7 @@ void thermal_zone_device_update(struct thermal_zone_device *tz, if (!tz->ops->get_temp) return; + thermal_iio_sensor_notify(tz, event); update_temperature(tz); for (count = 0; count < tz->trips; count++) @@ -1858,10 +1860,15 @@ struct thermal_zone_device *thermal_zone_device_register(const char *type, mutex_unlock(&thermal_governor_lock); + if (thermal_iio_sensor_register(tz)) + goto unregister; + if (!tz->tzp || !tz->tzp->no_hwmon) { result = thermal_add_hwmon_sysfs(tz); - if (result) + if (result) { + thermal_iio_sensor_unregister(tz); goto unregister; + } } mutex_lock(&thermal_list_lock); @@ -1943,6 +1950,7 @@ void thermal_zone_device_unregister(struct thermal_zone_device *tz) remove_trip_attrs(tz); thermal_set_governor(tz, NULL); + thermal_iio_sensor_unregister(tz); thermal_remove_hwmon_sysfs(tz); release_idr(&thermal_tz_idr, &thermal_idr_lock, tz->id); idr_destroy(&tz->idr); -- 1.9.3 ^ permalink raw reply related [flat|nested] 8+ messages in thread
* [RFC PATCH v2 4/4] thermal: x86_pkg_temp: Register threshold callbacks [not found] ` <1442529054-25803-1-git-send-email-srinivas.pandruvada-VuQAYsv1563Yd54FQh9/CA@public.gmane.org> 2015-09-17 22:30 ` [RFC PATCH v2 2/4] thermal: iio device for thermal sensor Srinivas Pandruvada 2015-09-17 22:30 ` [RFC PATCH v2 3/4] thermal: use iio binding calls Srinivas Pandruvada @ 2015-09-17 22:30 ` Srinivas Pandruvada 2 siblings, 0 replies; 8+ messages in thread From: Srinivas Pandruvada @ 2015-09-17 22:30 UTC (permalink / raw) To: jic23-DgEjT+Ai2ygdnm+yROfE0A, rui.zhang-ral2JQCrhuEAvxtiuMwx3w, edubezval-Re5JQEeQqe8AvxtiuMwx3w Cc: linux-pm-u79uwXL29TY76Z2rM5mHXA, linux-iio-u79uwXL29TY76Z2rM5mHXA, Srinivas Pandruvada Register callbacks to get/set temperature thresholds. Signed-off-by: Srinivas Pandruvada <srinivas.pandruvada-VuQAYsv1563Yd54FQh9/CA@public.gmane.org> --- drivers/thermal/x86_pkg_temp_thermal.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/drivers/thermal/x86_pkg_temp_thermal.c b/drivers/thermal/x86_pkg_temp_thermal.c index 849f682..58d0770 100644 --- a/drivers/thermal/x86_pkg_temp_thermal.c +++ b/drivers/thermal/x86_pkg_temp_thermal.c @@ -273,12 +273,20 @@ static int sys_get_trip_type(struct thermal_zone_device *thermal, return 0; } +static bool sys_check_notify_support(struct thermal_zone_device *tz) +{ + return true; +} + /* Thermal zone callback registry */ static struct thermal_zone_device_ops tzone_ops = { .get_temp = sys_get_curr_temp, .get_trip_temp = sys_get_trip_temp, .get_trip_type = sys_get_trip_type, .set_trip_temp = sys_set_trip_temp, + .get_threshold_temp = sys_get_trip_temp, + .set_threshold_temp = sys_set_trip_temp, + .check_notification_support = sys_check_notify_support, }; static bool pkg_temp_thermal_platform_thermal_rate_control(void) -- 1.9.3 ^ permalink raw reply related [flat|nested] 8+ messages in thread
end of thread, other threads:[~2015-09-21 18:57 UTC | newest] Thread overview: 8+ messages (download: mbox.gz follow: Atom feed -- links below jump to the message on this page -- 2015-09-17 22:30 [RFC PATCH v2 0/4] thermal: iio bindings Srinivas Pandruvada 2015-09-17 22:30 ` [RFC PATCH v2 1/4] thermal: core: enhance thermal_zone_device_update Srinivas Pandruvada [not found] ` <1442529054-25803-2-git-send-email-srinivas.pandruvada-VuQAYsv1563Yd54FQh9/CA@public.gmane.org> 2015-09-20 10:31 ` Jonathan Cameron [not found] ` <1442529054-25803-1-git-send-email-srinivas.pandruvada-VuQAYsv1563Yd54FQh9/CA@public.gmane.org> 2015-09-17 22:30 ` [RFC PATCH v2 2/4] thermal: iio device for thermal sensor Srinivas Pandruvada 2015-09-20 10:31 ` Jonathan Cameron 2015-09-21 18:38 ` Srinivas Pandruvada 2015-09-17 22:30 ` [RFC PATCH v2 3/4] thermal: use iio binding calls Srinivas Pandruvada 2015-09-17 22:30 ` [RFC PATCH v2 4/4] thermal: x86_pkg_temp: Register threshold callbacks Srinivas Pandruvada
This is a public inbox, see mirroring instructions for how to clone and mirror all data and code used for this inbox; as well as URLs for NNTP newsgroup(s).