| From 4e87400732c77765afae2ea89ed43837457aa604 Mon Sep 17 00:00:00 2001 |
| From: Rajith Cherian <rajith@codeaurora.org> |
| Date: Wed, 1 Feb 2017 19:00:26 +0530 |
| Subject: [PATCH] ipq8064: tsens: Support for configurable interrupts |
| |
| Provide support for adding configurable high and |
| configurable low trip temperatures. An interrupts is |
| also triggerred when these trip points are hit. The |
| interrupts can be activated or deactivated from sysfs. |
| This functionality is made available only if |
| CONFIG_THERMAL_WRITABLE_TRIPS is defined. |
| |
| Change-Id: Ib73f3f9459de4fffce7bb985a0312a88291f4934 |
| Signed-off-by: Rajith Cherian <rajith@codeaurora.org> |
| --- |
| .../devicetree/bindings/thermal/qcom-tsens.txt | 4 ++ |
| drivers/thermal/of-thermal.c | 63 ++++++++++++++++++---- |
| drivers/thermal/qcom/tsens.c | 43 ++++++++++++--- |
| drivers/thermal/qcom/tsens.h | 11 ++++ |
| drivers/thermal/thermal_core.c | 44 ++++++++++++++- |
| include/linux/thermal.h | 14 +++++ |
| 6 files changed, 162 insertions(+), 17 deletions(-) |
| |
| --- a/drivers/thermal/of-thermal.c |
| +++ b/drivers/thermal/of-thermal.c |
| @@ -91,7 +91,7 @@ static int of_thermal_get_temp(struct th |
| { |
| struct __thermal_zone *data = tz->devdata; |
| |
| - if (!data->ops || !data->ops->get_temp) |
| + if (!data->ops || !data->ops->get_temp || (data->mode == THERMAL_DEVICE_DISABLED)) |
| return -EINVAL; |
| |
| return data->ops->get_temp(data->sensor_data, temp); |
| @@ -102,7 +102,8 @@ static int of_thermal_set_trips(struct t |
| { |
| struct __thermal_zone *data = tz->devdata; |
| |
| - if (!data->ops || !data->ops->set_trips) |
| + if (!data->ops || !data->ops->set_trips |
| + || (data->mode == THERMAL_DEVICE_DISABLED)) |
| return -EINVAL; |
| |
| return data->ops->set_trips(data->sensor_data, low, high); |
| @@ -191,6 +192,9 @@ static int of_thermal_set_emul_temp(stru |
| if (!data->ops || !data->ops->set_emul_temp) |
| return -EINVAL; |
| |
| + if (data->mode == THERMAL_DEVICE_DISABLED) |
| + return -EINVAL; |
| + |
| return data->ops->set_emul_temp(data->sensor_data, temp); |
| } |
| |
| @@ -199,7 +203,7 @@ static int of_thermal_get_trend(struct t |
| { |
| struct __thermal_zone *data = tz->devdata; |
| |
| - if (!data->ops || !data->ops->get_trend) |
| + if (!data->ops || !data->ops->get_trend || (data->mode == THERMAL_DEVICE_DISABLED)) |
| return -EINVAL; |
| |
| return data->ops->get_trend(data->sensor_data, trip, trend); |
| @@ -300,7 +304,9 @@ static int of_thermal_set_mode(struct th |
| mutex_unlock(&tz->lock); |
| |
| data->mode = mode; |
| - thermal_zone_device_update(tz, THERMAL_EVENT_UNSPECIFIED); |
| + |
| + if (mode == THERMAL_DEVICE_ENABLED) |
| + thermal_zone_device_update(tz, THERMAL_EVENT_UNSPECIFIED); |
| |
| return 0; |
| } |
| @@ -310,7 +316,8 @@ static int of_thermal_get_trip_type(stru |
| { |
| struct __thermal_zone *data = tz->devdata; |
| |
| - if (trip >= data->ntrips || trip < 0) |
| + if (trip >= data->ntrips || trip < 0 |
| + || (data->mode == THERMAL_DEVICE_DISABLED)) |
| return -EDOM; |
| |
| *type = data->trips[trip].type; |
| @@ -318,12 +325,39 @@ static int of_thermal_get_trip_type(stru |
| return 0; |
| } |
| |
| +static int of_thermal_activate_trip_type(struct thermal_zone_device *tz, |
| + int trip, enum thermal_trip_activation_mode mode) |
| +{ |
| + struct __thermal_zone *data = tz->devdata; |
| + |
| + if (trip >= data->ntrips || trip < 0 |
| + || (data->mode == THERMAL_DEVICE_DISABLED)) |
| + return -EDOM; |
| + |
| + /* |
| + * The configurable_hi and configurable_lo trip points can be |
| + * activated and deactivated. |
| + */ |
| + |
| + if (data->ops->set_trip_activate) { |
| + int ret; |
| + |
| + ret = data->ops->set_trip_activate(data->sensor_data, |
| + trip, mode); |
| + if (ret) |
| + return ret; |
| + } |
| + |
| + return 0; |
| +} |
| + |
| static int of_thermal_get_trip_temp(struct thermal_zone_device *tz, int trip, |
| int *temp) |
| { |
| struct __thermal_zone *data = tz->devdata; |
| |
| - if (trip >= data->ntrips || trip < 0) |
| + if (trip >= data->ntrips || trip < 0 |
| + || (data->mode == THERMAL_DEVICE_DISABLED)) |
| return -EDOM; |
| |
| *temp = data->trips[trip].temperature; |
| @@ -336,7 +370,8 @@ static int of_thermal_set_trip_temp(stru |
| { |
| struct __thermal_zone *data = tz->devdata; |
| |
| - if (trip >= data->ntrips || trip < 0) |
| + if (trip >= data->ntrips || trip < 0 |
| + || (data->mode == THERMAL_DEVICE_DISABLED)) |
| return -EDOM; |
| |
| if (data->ops && data->ops->set_trip_temp) { |
| @@ -358,7 +393,8 @@ static int of_thermal_get_trip_hyst(stru |
| { |
| struct __thermal_zone *data = tz->devdata; |
| |
| - if (trip >= data->ntrips || trip < 0) |
| + if (trip >= data->ntrips || trip < 0 |
| + || (data->mode == THERMAL_DEVICE_DISABLED)) |
| return -EDOM; |
| |
| *hyst = data->trips[trip].hysteresis; |
| @@ -371,7 +407,8 @@ static int of_thermal_set_trip_hyst(stru |
| { |
| struct __thermal_zone *data = tz->devdata; |
| |
| - if (trip >= data->ntrips || trip < 0) |
| + if (trip >= data->ntrips || trip < 0 |
| + || (data->mode == THERMAL_DEVICE_DISABLED)) |
| return -EDOM; |
| |
| /* thermal framework should take care of data->mask & (1 << trip) */ |
| @@ -446,6 +483,9 @@ thermal_zone_of_add_sensor(struct device |
| if (ops->set_emul_temp) |
| tzd->ops->set_emul_temp = of_thermal_set_emul_temp; |
| |
| + if (ops->set_trip_activate) |
| + tzd->ops->set_trip_activate = of_thermal_activate_trip_type; |
| + |
| mutex_unlock(&tzd->lock); |
| |
| return tzd; |
| @@ -768,7 +808,10 @@ static const char * const trip_types[] = |
| [THERMAL_TRIP_ACTIVE] = "active", |
| [THERMAL_TRIP_PASSIVE] = "passive", |
| [THERMAL_TRIP_HOT] = "hot", |
| - [THERMAL_TRIP_CRITICAL] = "critical", |
| + [THERMAL_TRIP_CRITICAL] = "critical_high", |
| + [THERMAL_TRIP_CONFIGURABLE_HI] = "configurable_hi", |
| + [THERMAL_TRIP_CONFIGURABLE_LOW] = "configurable_lo", |
| + [THERMAL_TRIP_CRITICAL_LOW] = "critical_low", |
| }; |
| |
| /** |
| --- a/drivers/thermal/qcom/tsens.c |
| +++ b/drivers/thermal/qcom/tsens.c |
| @@ -22,7 +22,7 @@ static int tsens_get_temp(void *data, in |
| |
| static int tsens_get_trend(void *data, int trip, enum thermal_trend *trend) |
| { |
| - const struct tsens_sensor *s = data; |
| + struct tsens_sensor *s = data; |
| struct tsens_priv *priv = s->priv; |
| |
| if (priv->ops->get_trend) |
| @@ -31,9 +31,10 @@ static int tsens_get_trend(void *data, i |
| return -ENOTSUPP; |
| } |
| |
| -static int __maybe_unused tsens_suspend(struct device *dev) |
| +static int __maybe_unused tsens_suspend(void *data) |
| { |
| - struct tsens_priv *priv = dev_get_drvdata(dev); |
| + struct tsens_sensor *s = data; |
| + struct tsens_priv *priv = s->priv; |
| |
| if (priv->ops && priv->ops->suspend) |
| return priv->ops->suspend(priv); |
| @@ -41,9 +42,10 @@ static int __maybe_unused tsens_suspend |
| return 0; |
| } |
| |
| -static int __maybe_unused tsens_resume(struct device *dev) |
| +static int __maybe_unused tsens_resume(void *data) |
| { |
| - struct tsens_priv *priv = dev_get_drvdata(dev); |
| + struct tsens_sensor *s = data; |
| + struct tsens_priv *priv = s->priv; |
| |
| if (priv->ops && priv->ops->resume) |
| return priv->ops->resume(priv); |
| @@ -51,6 +53,30 @@ static int __maybe_unused tsens_resume(s |
| return 0; |
| } |
| |
| +static int __maybe_unused tsens_set_trip_temp(void *data, int trip, int temp) |
| +{ |
| + struct tsens_sensor *s = data; |
| + struct tsens_priv *priv = s->priv; |
| + |
| + if (priv->ops && priv->ops->set_trip_temp) |
| + return priv->ops->set_trip_temp(s, trip, temp); |
| + |
| + return 0; |
| +} |
| + |
| +static int __maybe_unused tsens_activate_trip_type(void *data, int trip, |
| + enum thermal_trip_activation_mode mode) |
| +{ |
| + struct tsens_sensor *s = data; |
| + struct tsens_priv *priv = s->priv; |
| + |
| + if (priv->ops && priv->ops->set_trip_activate) |
| + return priv->ops->set_trip_activate(s, trip, mode); |
| + |
| + return 0; |
| +} |
| + |
| + |
| static SIMPLE_DEV_PM_OPS(tsens_pm_ops, tsens_suspend, tsens_resume); |
| |
| static const struct of_device_id tsens_table[] = { |
| @@ -80,6 +106,8 @@ MODULE_DEVICE_TABLE(of, tsens_table); |
| static const struct thermal_zone_of_device_ops tsens_of_ops = { |
| .get_temp = tsens_get_temp, |
| .get_trend = tsens_get_trend, |
| + .set_trip_temp = tsens_set_trip_temp, |
| + .set_trip_activate = tsens_activate_trip_type, |
| }; |
| |
| static int tsens_register(struct tsens_priv *priv) |
| @@ -123,7 +151,7 @@ static int tsens_probe(struct platform_d |
| if (id) |
| data = id->data; |
| else |
| - data = &data_8960; |
| + return -EINVAL; |
| |
| num_sensors = data->num_sensors; |
| |
| @@ -144,6 +172,9 @@ static int tsens_probe(struct platform_d |
| priv->dev = dev; |
| priv->num_sensors = num_sensors; |
| priv->ops = data->ops; |
| + |
| + priv->tsens_irq = platform_get_irq(pdev, 0); |
| + |
| for (i = 0; i < priv->num_sensors; i++) { |
| if (data->hw_ids) |
| priv->sensor[i].hw_id = data->hw_ids[i]; |
| --- a/drivers/thermal/qcom/tsens.h |
| +++ b/drivers/thermal/qcom/tsens.h |
| @@ -40,9 +40,12 @@ enum tsens_ver { |
| struct tsens_sensor { |
| struct tsens_priv *priv; |
| struct thermal_zone_device *tzd; |
| + struct work_struct notify_work; |
| int offset; |
| unsigned int id; |
| unsigned int hw_id; |
| + int calib_data; |
| + int calib_data_backup; |
| int slope; |
| u32 status; |
| }; |
| @@ -57,6 +60,9 @@ struct tsens_sensor { |
| * @suspend: Function to suspend the tsens device |
| * @resume: Function to resume the tsens device |
| * @get_trend: Function to get the thermal/temp trend |
| + * @set_trip_temp: Function to set trip temp |
| + * @get_trip_temp: Function to get trip temp |
| + * @set_trip_activate: Function to activate trip points |
| */ |
| struct tsens_ops { |
| /* mandatory callbacks */ |
| @@ -69,6 +75,9 @@ struct tsens_ops { |
| int (*suspend)(struct tsens_priv *priv); |
| int (*resume)(struct tsens_priv *priv); |
| int (*get_trend)(struct tsens_priv *priv, int i, enum thermal_trend *trend); |
| + int (*set_trip_temp)(void *data, int trip, int temp); |
| + int (*set_trip_activate)(void *data, int trip, |
| + enum thermal_trip_activation_mode mode); |
| }; |
| |
| #define REG_FIELD_FOR_EACH_SENSOR11(_name, _offset, _startbit, _stopbit) \ |
| @@ -300,6 +309,7 @@ struct tsens_context { |
| struct tsens_priv { |
| struct device *dev; |
| u32 num_sensors; |
| + u32 tsens_irq; |
| struct regmap *tm_map; |
| struct regmap *srot_map; |
| u32 tm_offset; |
| @@ -308,6 +318,7 @@ struct tsens_priv { |
| const struct tsens_features *feat; |
| const struct reg_field *fields; |
| const struct tsens_ops *ops; |
| + struct work_struct tsens_work; |
| struct tsens_sensor sensor[0]; |
| }; |
| |
| --- a/drivers/thermal/thermal_sysfs.c |
| +++ b/drivers/thermal/thermal_sysfs.c |
| @@ -113,12 +113,48 @@ trip_point_type_show(struct device *dev, |
| return sprintf(buf, "passive\n"); |
| case THERMAL_TRIP_ACTIVE: |
| return sprintf(buf, "active\n"); |
| + case THERMAL_TRIP_CONFIGURABLE_HI: |
| + return sprintf(buf, "configurable_hi\n"); |
| + case THERMAL_TRIP_CONFIGURABLE_LOW: |
| + return sprintf(buf, "configurable_low\n"); |
| + case THERMAL_TRIP_CRITICAL_LOW: |
| + return sprintf(buf, "critical_low\n"); |
| default: |
| return sprintf(buf, "unknown\n"); |
| } |
| } |
| |
| static ssize_t |
| +trip_point_type_activate(struct device *dev, struct device_attribute *attr, |
| + const char *buf, size_t count) |
| +{ |
| + struct thermal_zone_device *tz = to_thermal_zone(dev); |
| + int trip, ret; |
| + char *enabled = "enabled"; |
| + char *disabled = "disabled"; |
| + |
| + if (!tz->ops->set_trip_activate) |
| + return -EPERM; |
| + |
| + if (!sscanf(attr->attr.name, "trip_point_%d_type", &trip)) |
| + return -EINVAL; |
| + |
| + if (!strncmp(buf, enabled, strlen(enabled))) |
| + ret = tz->ops->set_trip_activate(tz, trip, |
| + THERMAL_TRIP_ACTIVATION_ENABLED); |
| + else if (!strncmp(buf, disabled, strlen(disabled))) |
| + ret = tz->ops->set_trip_activate(tz, trip, |
| + THERMAL_TRIP_ACTIVATION_DISABLED); |
| + else |
| + ret = -EINVAL; |
| + |
| + if (ret) |
| + return ret; |
| + |
| + return count; |
| +} |
| + |
| +static ssize_t |
| trip_point_temp_store(struct device *dev, struct device_attribute *attr, |
| const char *buf, size_t count) |
| { |
| @@ -559,6 +595,12 @@ static int create_trip_attrs(struct ther |
| tz->trip_type_attrs[indx].attr.show = trip_point_type_show; |
| attrs[indx] = &tz->trip_type_attrs[indx].attr.attr; |
| |
| + if (IS_ENABLED(CONFIG_THERMAL_WRITABLE_TRIPS)) { |
| + tz->trip_type_attrs[indx].attr.store |
| + = trip_point_type_activate; |
| + tz->trip_type_attrs[indx].attr.attr.mode |= S_IWUSR; |
| + } |
| + |
| /* create trip temp attribute */ |
| snprintf(tz->trip_temp_attrs[indx].name, THERMAL_NAME_LENGTH, |
| "trip_point_%d_temp", indx); |
| --- a/include/linux/thermal.h |
| +++ b/include/linux/thermal.h |
| @@ -63,11 +63,19 @@ enum thermal_device_mode { |
| THERMAL_DEVICE_ENABLED, |
| }; |
| |
| +enum thermal_trip_activation_mode { |
| + THERMAL_TRIP_ACTIVATION_DISABLED = 0, |
| + THERMAL_TRIP_ACTIVATION_ENABLED, |
| +}; |
| + |
| enum thermal_trip_type { |
| THERMAL_TRIP_ACTIVE = 0, |
| THERMAL_TRIP_PASSIVE, |
| THERMAL_TRIP_HOT, |
| THERMAL_TRIP_CRITICAL, |
| + THERMAL_TRIP_CONFIGURABLE_HI, |
| + THERMAL_TRIP_CONFIGURABLE_LOW, |
| + THERMAL_TRIP_CRITICAL_LOW, |
| }; |
| |
| enum thermal_trend { |
| @@ -105,6 +113,8 @@ struct thermal_zone_device_ops { |
| enum thermal_trip_type *); |
| int (*get_trip_temp) (struct thermal_zone_device *, int, int *); |
| int (*set_trip_temp) (struct thermal_zone_device *, int, int); |
| + int (*set_trip_activate) (struct thermal_zone_device *, int, |
| + enum thermal_trip_activation_mode); |
| int (*get_trip_hyst) (struct thermal_zone_device *, int, int *); |
| int (*set_trip_hyst) (struct thermal_zone_device *, int, int); |
| int (*get_crit_temp) (struct thermal_zone_device *, int *); |
| @@ -349,6 +359,8 @@ struct thermal_genl_event { |
| * temperature. |
| * @set_trip_temp: a pointer to a function that sets the trip temperature on |
| * hardware. |
| + * @activate_trip_type: a pointer to a function to enable/disable trip |
| + * temperature interrupts |
| */ |
| struct thermal_zone_of_device_ops { |
| int (*get_temp)(void *, int *); |
| @@ -356,6 +368,8 @@ struct thermal_zone_of_device_ops { |
| int (*set_trips)(void *, int, int); |
| int (*set_emul_temp)(void *, int); |
| int (*set_trip_temp)(void *, int, int); |
| + int (*set_trip_activate)(void *, int, |
| + enum thermal_trip_activation_mode); |
| }; |
| |
| /** |