Hello Ivan

On Wed, Oct 01, 2014 at 06:53:37PM +0300, Ivan T. Ivanov wrote:
> Add support for the temperature alarm peripheral found inside
> Qualcomm plug-and-play (QPNP) PMIC chips.  The temperature alarm
> peripheral outputs a pulse on an interrupt line whenever the
> thermal over temperature stage value changes.  Implement an ISR
> to manage this interrupt.
> 
> Register a thermal zone device in sysfs with multiple trip points
> corresponding to the physical threshold temperatures between over
> temperature stages.  The temperature reported by this thermal
> zone device should reflect the actual PMIC die temperature if an
> ADC is present on the given PMIC.  If no ADC is present, then the
> reported temperature should be estimated from the over
> temperature stage value.
> 
> Send a notification to userspace via sysfs_notify() whenever the
> over temperature stage value changes.
> 

Thanks for sending your driver.

> Cc: David Collins <colli...@codeaurora.org>
> Signed-off-by: Ivan T. Ivanov <iiva...@mm-sol.com>
> ---
> 
> Changes since v2:
> 
> - Fixed review comments from Kiran Padwal
> - Files renamed from qpnp-* to qcom-spmi-* to be inline with
>   other PMIC sub-function drivers like qcom-spmi-iadc and
>   qcom-spmi-vadc.
> 
> v2: http://www.gossamer-threads.com/lists/linux/kernel/2017366
> 
>  .../bindings/thermal/qcom-spmi-temp-alarm.txt      |  26 ++
>  drivers/thermal/Kconfig                            |  13 +
>  drivers/thermal/Makefile                           |   1 +
>  drivers/thermal/qcom-spmi-temp-alarm.c             | 517 
> +++++++++++++++++++++
>  4 files changed, 557 insertions(+)
>  create mode 100644 
> Documentation/devicetree/bindings/thermal/qcom-spmi-temp-alarm.txt
>  create mode 100644 drivers/thermal/qcom-spmi-temp-alarm.c
> 
> diff --git 
> a/Documentation/devicetree/bindings/thermal/qcom-spmi-temp-alarm.txt 
> b/Documentation/devicetree/bindings/thermal/qcom-spmi-temp-alarm.txt
> new file mode 100644
> index 0000000..afa908c
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/thermal/qcom-spmi-temp-alarm.txt
> @@ -0,0 +1,26 @@
> +Qualcomm QPNP PMIC Temperature Alarm
> +
> +QPNP temperature alarm peripherals are found inside of Qualcomm PMIC chips
> +that utilize the Qualcomm SPMI implementation. These peripherals provide an
> +interrupt signal and status register to identify high PMIC die temperature.
> +
> +Required properties:
> +- compatible:      Should contain "qcom,spmi-temp-alarm".
> +- reg:             Specifies the SPMI address and length of the controller's
> +                   registers.
> +- interrupts:      PMIC temperature alarm interrupt
> +
> +Optional properties:
> +- io-channels:     Should contain IIO channel specifier for the ADC channel,
> +                   which report chip die temperature.
> +- io-channel-names: Should contain "thermal".
> +
> +Example:
> +
> +     thermal-alarm@2400 {
> +             compatible = "qcom,spmi-temp-alarm";
> +             reg = <0x2400 0x100>;
> +             interrupts = <0 0x24 0 IRQ_TYPE_EDGE_RISING>;
> +             io-channels = <&pm8941_vadc VADC_DIE_TEMP>;
> +             io-channel-names = "thermal";
> +     };
> diff --git a/drivers/thermal/Kconfig b/drivers/thermal/Kconfig
> index 693208e..6adb661 100644
> --- a/drivers/thermal/Kconfig
> +++ b/drivers/thermal/Kconfig
> @@ -248,4 +248,17 @@ depends on ARCH_STI && OF
>  source "drivers/thermal/st/Kconfig"
>  endmenu
>  
> +config QCOM_SPMI_TEMP_ALARM
> +     tristate "Qualcomm SPMI PMIC Temperature Alarm"
> +     depends on OF && SPMI && IIO
> +     select REGMAP_SPMI
> +     help
> +       This enables a thermal sysfs driver for Qualcomm plug-and-play (QPNP)
> +       PMIC devices. It shows up in sysfs as a thermal zone with multiple
> +       trip points. The temperature reported by the thermal zone reflects the
> +       real time die temperature if an ADC is present or an estimate of the
> +       temperature based upon the over temperature stage value. Enabling the
> +       thermal zone device via the mode file results in shifting PMIC over
> +       temperature shutdown control from hardware to software.
> +
>  endif
> diff --git a/drivers/thermal/Makefile b/drivers/thermal/Makefile
> index 31e232f..55f6c23 100644
> --- a/drivers/thermal/Makefile
> +++ b/drivers/thermal/Makefile
> @@ -33,3 +33,4 @@ obj-$(CONFIG_INTEL_SOC_DTS_THERMAL) += 
> intel_soc_dts_thermal.o
>  obj-$(CONFIG_TI_SOC_THERMAL) += ti-soc-thermal/
>  obj-$(CONFIG_ACPI_INT3403_THERMAL)   += int3403_thermal.o
>  obj-$(CONFIG_ST_THERMAL)     += st/
> +obj-$(CONFIG_QCOM_SPMI_TEMP_ALARM)   += qcom-spmi-temp-alarm.o
> diff --git a/drivers/thermal/qcom-spmi-temp-alarm.c 
> b/drivers/thermal/qcom-spmi-temp-alarm.c
> new file mode 100644
> index 0000000..5ce0fef
> --- /dev/null
> +++ b/drivers/thermal/qcom-spmi-temp-alarm.c
> @@ -0,0 +1,517 @@
> +/*
> + * Copyright (c) 2011-2014, The Linux Foundation. All rights reserved.
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License version 2 and
> + * only version 2 as published by the Free Software Foundation.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> + * GNU General Public License for more details.
> + */
> +
> +#include <linux/delay.h>
> +#include <linux/err.h>
> +#include <linux/iio/consumer.h>
> +#include <linux/interrupt.h>
> +#include <linux/module.h>
> +#include <linux/of.h>
> +#include <linux/of_device.h>
> +#include <linux/platform_device.h>
> +#include <linux/regmap.h>
> +#include <linux/thermal.h>
> +
> +#define QPNP_TM_REG_TYPE             0x04
> +#define QPNP_TM_REG_SUBTYPE          0x05
> +#define QPNP_TM_REG_STATUS           0x08
> +#define QPNP_TM_REG_SHUTDOWN_CTRL1   0x40
> +#define QPNP_TM_REG_SHUTDOWN_CTRL2   0x42
> +#define QPNP_TM_REG_ALARM_CTRL               0x46
> +
> +#define QPNP_TM_TYPE                 0x09
> +#define QPNP_TM_SUBTYPE                      0x08
> +
> +#define STATUS_STAGE_MASK            0x03
> +
> +#define SHUTDOWN_CTRL1_OVERRIDE_STAGE3       0x80
> +#define SHUTDOWN_CTRL1_OVERRIDE_STAGE2       0x40
> +#define SHUTDOWN_CTRL1_THRESHOLD_MASK        0x03
> +
> +#define SHUTDOWN_CTRL2_CLEAR_STAGE3  0x80
> +#define SHUTDOWN_CTRL2_CLEAR_STAGE2  0x40
> +
> +#define ALARM_CTRL_FORCE_ENABLE              0x80
> +#define ALARM_CTRL_FOLLOW_HW_ENABLE  0x01
> +
> +/*
> + * Trip point values based on threshold control
> + * 0 = {105 C, 125 C, 145 C}
> + * 1 = {110 C, 130 C, 150 C}
> + * 2 = {115 C, 135 C, 155 C}
> + * 3 = {120 C, 140 C, 160 C}
> +*/
> +#define TEMP_STAGE_STEP                      20000   /* Stage step: 20.000 C 
> */
> +#define TEMP_STAGE_HYSTERESIS                2000
> +
> +#define TEMP_THRESH_MIN                      105000  /* Threshold Min: 105 C 
> */
> +#define TEMP_THRESH_STEP             5000    /* Threshold step: 5 C */
> +
> +#define THRESH_MIN                   0
> +#define THRESH_MAX                   3
> +
> +/* Trip points from most critical to least critical */
> +#define TRIP_STAGE3                  0
> +#define TRIP_STAGE2                  1
> +#define TRIP_STAGE1                  2
> +#define TRIP_NUM                     3
> +
> +/* Delay between TEMP_STAT IRQ going high and status value changing in ms */
> +#define READ_DELAY_MS                        40
> +
> +/* Temperature in Milli Celsius reported during stage 0 if no ADC is present 
> */
> +#define DEFAULT_TEMP                 37000
> +
> +struct qpnp_tm_chip {
> +     struct delayed_work             work;
> +     struct regmap                   *map;
> +     struct device                   *dev;
> +     struct thermal_zone_device      *tz_dev;
> +     enum thermal_device_mode        mode;
> +     long                            temp;
> +     unsigned int                    thresh;
> +     unsigned int                    stage;
> +     unsigned int                    prev_stage;
> +     unsigned int                    base;
> +     struct iio_channel              *adc;
> +};
> +
> +static int qpnp_tm_read(struct qpnp_tm_chip *chip, u16 addr, u8 *data)
> +{
> +     unsigned int val;
> +     int ret;
> +
> +     ret = regmap_read(chip->map, chip->base + addr, &val);
> +     if (ret < 0)
> +             return ret;
> +
> +     *data = val;
> +     return 0;
> +}
> +
> +static int qpnp_tm_write(struct qpnp_tm_chip *chip, u16 addr, u8 data)
> +{
> +     return regmap_write(chip->map, chip->base + addr, data);
> +}
> +
> +static int qpnp_tm_shutdown_override(struct qpnp_tm_chip *chip, bool enable)
> +{
> +     u8 reg;
> +
> +     reg = chip->thresh & SHUTDOWN_CTRL1_THRESHOLD_MASK;
> +
> +     if (enable) {
> +             reg |= SHUTDOWN_CTRL1_OVERRIDE_STAGE2;
> +             reg |= SHUTDOWN_CTRL1_OVERRIDE_STAGE3;
> +     }
> +
> +     return qpnp_tm_write(chip, QPNP_TM_REG_SHUTDOWN_CTRL1, reg);
> +}
> +
> +/*
> + * This function updates the internal temp value based on the
> + * current thermal stage and threshold as well as the previous stage
> + */
> +static int qpnp_tm_update_temp_no_adc(struct qpnp_tm_chip *chip)
> +{
> +     unsigned int stage;
> +     int ret;
> +     u8 reg = 0;
> +
> +     ret = qpnp_tm_read(chip, QPNP_TM_REG_STATUS, &reg);
> +     if (ret < 0)
> +             return ret;
> +
> +     stage = reg & STATUS_STAGE_MASK;
> +
> +     if (stage > chip->stage) {
> +             /* increasing stage, use lower bound */
> +             chip->temp = (stage - 1) * TEMP_STAGE_STEP +
> +                          chip->thresh * TEMP_THRESH_STEP +
> +                          TEMP_STAGE_HYSTERESIS + TEMP_THRESH_MIN;
> +     } else if (stage < chip->stage) {
> +             /* decreasing stage, use upper bound */
> +             chip->temp = stage * TEMP_STAGE_STEP +
> +                          chip->thresh * TEMP_THRESH_STEP -
> +                          TEMP_STAGE_HYSTERESIS + TEMP_THRESH_MIN;
> +     }
> +
> +     chip->stage = stage;
> +
> +     return 0;
> +}
> +
> +static int qpnp_tz_get_temp(struct thermal_zone_device *thermal,
> +                        unsigned long *temp)
> +{
> +     struct qpnp_tm_chip *chip = thermal->devdata;
> +     int ret, mili_celsius;
> +
> +     if (!temp)
> +             return -EINVAL;
> +
> +     if (IS_ERR(chip->adc)) {
> +             ret = qpnp_tm_update_temp_no_adc(chip);
> +             if (ret < 0)
> +                     return ret;
> +     } else {
> +             ret = iio_read_channel_processed(chip->adc, &mili_celsius);
> +             if (ret < 0)
> +                     return ret;
> +
> +             chip->temp = mili_celsius;
> +     }
> +
> +     *temp = chip->temp < 0 ? 0 : chip->temp;
> +
> +     return 0;
> +}
> +
> +static int qpnp_tz_get_mode(struct thermal_zone_device *thermal,
> +                         enum thermal_device_mode *mode)
> +{
> +     struct qpnp_tm_chip *chip = thermal->devdata;
> +
> +     if (!mode)
> +             return -EINVAL;
> +
> +     *mode = chip->mode;
> +
> +     return 0;
> +}
> +
> +static int qpnp_tz_set_mode(struct thermal_zone_device *thermal,
> +                         enum thermal_device_mode mode)
> +{
> +     struct qpnp_tm_chip *chip = thermal->devdata;
> +     int ret;
> +
> +     if (mode == chip->mode)
> +             return 0;
> +
> +     if (mode == THERMAL_DEVICE_ENABLED)
> +             ret = qpnp_tm_shutdown_override(chip, true);
> +     else
> +             ret = qpnp_tm_shutdown_override(chip, false);


Can you please elaborate a little more why you need to switch these when
mode changes?

> +
> +     chip->mode = mode;
> +
> +     return ret;
> +}
> +
> +static int qpnp_tz_get_trip_type(struct thermal_zone_device *thermal, int 
> trip,
> +                            enum thermal_trip_type *type)
> +{
> +     if (trip < 0 || !type)
> +             return -EINVAL;
> +
> +     switch (trip) {
> +     case TRIP_STAGE3:
> +             *type = THERMAL_TRIP_CRITICAL;
> +             break;
> +     case TRIP_STAGE2:
> +             *type = THERMAL_TRIP_HOT;
> +             break;
> +     case TRIP_STAGE1:
> +             *type = THERMAL_TRIP_HOT;
> +             break;
> +     default:
> +             return -EINVAL;
> +     }
> +
> +     return 0;
> +}
> +
> +static int qpnp_tz_get_trip_temp(struct thermal_zone_device *thermal, int 
> trip,
> +                             unsigned long *temp)
> +{
> +     struct qpnp_tm_chip *chip = thermal->devdata;
> +     int thresh_temp;
> +
> +     if (trip < 0 || !temp)
> +             return -EINVAL;
> +
> +     thresh_temp = chip->thresh * TEMP_THRESH_STEP + TEMP_THRESH_MIN;
> +
> +     switch (trip) {
> +     case TRIP_STAGE3:
> +             thresh_temp += 2 * TEMP_STAGE_STEP;
> +             break;
> +     case TRIP_STAGE2:
> +             thresh_temp += TEMP_STAGE_STEP;
> +             break;
> +     case TRIP_STAGE1:
> +             break;
> +     default:
> +             return -EINVAL;
> +     }
> +
> +     *temp = thresh_temp;
> +
> +     return 0;
> +}
> +
> +static int qpnp_tz_get_crit_temp(struct thermal_zone_device *thermal,
> +                              unsigned long *temp)
> +{
> +     struct qpnp_tm_chip *chip = thermal->devdata;
> +
> +     if (!temp)
> +             return -EINVAL;
> +
> +     *temp = chip->thresh * TEMP_THRESH_STEP + TEMP_THRESH_MIN +
> +             2 * TEMP_STAGE_STEP;
> +
> +     return 0;
> +}
> +
> +static irqreturn_t qpnp_tm_isr(int irq, void *data)
> +{
> +     struct qpnp_tm_chip *chip = data;
> +
> +     schedule_delayed_work(&chip->work, msecs_to_jiffies(READ_DELAY_MS) + 1);
> +
> +     return IRQ_HANDLED;
> +}
> +
> +static void qpnp_tm_work(struct work_struct *work)
> +{
> +     struct delayed_work *dwork;
> +     struct qpnp_tm_chip *chip;
> +     int ret, mili_celsius;
> +     u8 reg;
> +
> +     dwork = container_of(work, struct delayed_work, work);
> +     chip = container_of(dwork, struct qpnp_tm_chip, work);
> +
> +     if (IS_ERR(chip->adc)) {
> +             ret = qpnp_tm_update_temp_no_adc(chip);
> +             if (ret < 0)
> +                     return;
> +     } else {
> +             ret = qpnp_tm_read(chip, QPNP_TM_REG_STATUS, &reg);
> +             if (ret < 0)
> +                     return;
> +
> +             chip->stage = reg & STATUS_STAGE_MASK;
> +
> +             ret = iio_read_channel_processed(chip->adc, &mili_celsius);
> +             if (ret < 0)
> +                     return;
> +
> +             chip->temp = mili_celsius;
> +     }
> +
> +     if (chip->stage != chip->prev_stage) {
> +             chip->prev_stage = chip->stage;
> +
> +             dev_warn(chip->dev, "Thermal alarm stage %u, threshold %u, temp 
> %ld mC\n",
> +                      chip->stage, chip->thresh, chip->temp);
> +
> +             thermal_zone_device_update(chip->tz_dev);
> +
> +             /* Notify user space */
> +             sysfs_notify(&chip->tz_dev->device.kobj, NULL, "type");
> +     }
> +}
> +
> +/*
> + * This function initializes the internal temp value based on only the
> + * current thermal stage and threshold. Setup threshold control and
> + * disable shutdown override.
> + */
> +static int qpnp_tm_init(struct qpnp_tm_chip *chip)
> +{
> +     int ret;
> +     u8 reg;
> +
> +     chip->thresh = THRESH_MIN;
> +     chip->temp = DEFAULT_TEMP;
> +
> +     ret = qpnp_tm_read(chip, QPNP_TM_REG_STATUS, &reg);
> +     if (ret < 0)
> +             return ret;
> +
> +     chip->stage = reg & STATUS_STAGE_MASK;
> +
> +     if (chip->stage)
> +             chip->temp = chip->thresh * TEMP_THRESH_STEP +
> +                          (chip->stage - 1) * TEMP_STAGE_STEP +
> +                          TEMP_THRESH_MIN;
> +
> +     /*
> +      * Set threshold and disable software override of stage 2 and 3
> +      * shutdowns.
> +      */
> +     reg = chip->thresh & SHUTDOWN_CTRL1_THRESHOLD_MASK;
> +     ret = qpnp_tm_write(chip, QPNP_TM_REG_SHUTDOWN_CTRL1, reg);
> +     if (ret < 0)
> +             return ret;
> +
> +     /* Enable the thermal alarm PMIC module in always-on mode. */
> +     reg = ALARM_CTRL_FORCE_ENABLE;
> +     ret = qpnp_tm_write(chip, QPNP_TM_REG_ALARM_CTRL, reg);
> +
> +     return ret;
> +}
> +
> +static struct thermal_zone_device_ops qpnp_tz_ops = {
> +     .get_temp       = qpnp_tz_get_temp,
> +     .get_mode       = qpnp_tz_get_mode,
> +     .set_mode       = qpnp_tz_set_mode,
> +     .get_trip_type  = qpnp_tz_get_trip_type,
> +     .get_trip_temp  = qpnp_tz_get_trip_temp,
> +     .get_crit_temp  = qpnp_tz_get_crit_temp,
> +};
> +
> +static int qpnp_tm_probe(struct platform_device *pdev)
> +{
> +     struct qpnp_tm_chip *chip;
> +     struct device_node *node;
> +     u8 type, subtype;
> +     int ret, irq, res[2];
> +
> +     node = pdev->dev.of_node;
> +
> +     chip = devm_kzalloc(&pdev->dev, sizeof(*chip), GFP_KERNEL);
> +     if (!chip)
> +             return -ENOMEM;
> +
> +     dev_set_drvdata(&pdev->dev, chip);
> +
> +     chip->dev = &pdev->dev;
> +     INIT_DELAYED_WORK(&chip->work, qpnp_tm_work);
> +
> +     chip->map = dev_get_regmap(chip->dev->parent, NULL);
> +     if (!chip->map)
> +             return -ENXIO;
> +
> +     ret = of_property_read_u32_array(node, "reg", res, 2);
> +     if (ret < 0)
> +             return ret;
> +
> +     chip->base = res[0];
> +
> +     ret = qpnp_tm_read(chip, QPNP_TM_REG_TYPE, &type);
> +     if (ret < 0) {
> +             dev_err(&pdev->dev, "could not read type\n");
> +             return ret;
> +     }
> +
> +     ret = qpnp_tm_read(chip, QPNP_TM_REG_SUBTYPE, &subtype);
> +     if (ret < 0) {
> +             dev_err(&pdev->dev, "could not read subtype\n");
> +             return ret;
> +     }
> +
> +     if (type != QPNP_TM_TYPE || subtype != QPNP_TM_SUBTYPE) {
> +             dev_err(&pdev->dev, "invalid type 0x%02x or subtype 0x%02x\n",
> +                     type, subtype);
> +             return -ENODEV;
> +     }
> +
> +     irq = platform_get_irq(pdev, 0);
> +     if (irq < 0)
> +             return irq;
> +
> +     chip->adc = iio_channel_get(chip->dev, "thermal");
> +     if (PTR_ERR(chip->adc) == -EPROBE_DEFER)
> +             return PTR_ERR(chip->adc);
> +
> +     ret = qpnp_tm_init(chip);
> +     if (ret < 0) {
> +             dev_err(&pdev->dev, "init failed\n");
> +             return ret;
> +     }
> +
> +     /* Start in HW control. Switch to SW control when user changes mode. */
> +     chip->mode = THERMAL_DEVICE_DISABLED;
> +
> +     chip->tz_dev = thermal_zone_device_register(node->name, TRIP_NUM, 0,
> +                                                 chip, &qpnp_tz_ops, NULL,

Have you considered using of-thermal instead of doing your own specific thermal
zone registration? Having a glance look in this driver, most of the
operation are covered by of-thermal. Ahy concerns using of-thermal in
your case?

> +                                                 0, 0);
> +     if (IS_ERR(chip->tz_dev)) {
> +             dev_err(&pdev->dev, "TZ registration failed\n");
> +             return PTR_ERR(chip->tz_dev);
> +     }
> +
> +     ret = devm_request_irq(chip->dev, irq, qpnp_tm_isr, 0, node->name,
> +                           chip);

The IRQ handling would probably be kept in your driver.

> +     if (ret < 0)
> +             thermal_zone_device_unregister(chip->tz_dev);
> +
> +     return ret;
> +}
> +
> +static int qpnp_tm_remove(struct platform_device *pdev)
> +{
> +     struct qpnp_tm_chip *chip = dev_get_drvdata(&pdev->dev);
> +
> +     cancel_delayed_work_sync(&chip->work);
> +     thermal_zone_device_unregister(chip->tz_dev);
> +     qpnp_tm_shutdown_override(chip, false);
> +
> +     return 0;
> +}
> +
> +#ifdef CONFIG_PM_SLEEP
> +static int qpnp_tm_suspend(struct device *dev)
> +{
> +     struct qpnp_tm_chip *chip = dev_get_drvdata(dev);
> +
> +     /* Clear override bits in suspend to allow hardware control */
> +     qpnp_tm_shutdown_override(chip, false);
> +
> +     return 0;
> +}
> +
> +static int qpnp_tm_resume(struct device *dev)
> +{
> +     struct qpnp_tm_chip *chip = dev_get_drvdata(dev);
> +
> +     /* Override hardware actions so software can control */
> +     if (chip->mode == THERMAL_DEVICE_ENABLED)
> +             qpnp_tm_shutdown_override(chip, true);
> +
> +     return 0;
> +}
> +
> +static const struct dev_pm_ops qpnp_tm_pm_ops = {
> +     SET_SYSTEM_SLEEP_PM_OPS(qpnp_tm_suspend, qpnp_tm_resume)
> +};
> +
> +#define QPNP_TM_PM_OPS       (&qpnp_tm_pm_ops)
> +#else
> +#define QPNP_TM_PM_OPS       NULL
> +#endif
> +
> +static const struct of_device_id qpnp_tm_match_table[] = {
> +     { .compatible = "qcom,spmi-temp-alarm" },
> +     { }
> +};
> +MODULE_DEVICE_TABLE(of, qpnp_tm_match_table);
> +
> +static struct platform_driver qpnp_tm_driver = {
> +     .driver = {
> +             .name = "spmi-temp-alarm",
> +             .of_match_table = qpnp_tm_match_table,
> +             .pm = QPNP_TM_PM_OPS,
> +     },
> +     .probe  = qpnp_tm_probe,
> +     .remove = qpnp_tm_remove,
> +};
> +module_platform_driver(qpnp_tm_driver);
> +
> +MODULE_ALIAS("platform:spmi-temp-alarm");
> +MODULE_DESCRIPTION("QPNP PMIC Temperature Alarm driver");
> +MODULE_LICENSE("GPL v2");
> -- 
> 1.9.1
> 

Attachment: signature.asc
Description: Digital signature

Reply via email to