rk3288:tsadc:support rockchip hwmon tsadc
author张晴 <zhangqing@rock-chips.com>
Mon, 24 Mar 2014 07:55:46 +0000 (15:55 +0800)
committer张晴 <zhangqing@rock-chips.com>
Mon, 24 Mar 2014 07:55:46 +0000 (15:55 +0800)
arch/arm/boot/dts/rk3288.dtsi
arch/arm/configs/rockchip_defconfig
drivers/hwmon/Kconfig
drivers/hwmon/Makefile
drivers/hwmon/hwmon-rockchip.h [new file with mode: 0755]
drivers/hwmon/rockchip-hwmon.c [new file with mode: 0755]
drivers/hwmon/rockchip_tsadc.c [new file with mode: 0755]

index 20551c5e2c90441e17f981e8acb77a8750afadce..150f6f318f761b432957a277f9f7aa30db9e462a 100755 (executable)
                        
                        status = "disabled";
        };
+       
+       tsadc: tsadc@ff280000{
+                       compatible = "rockchip,tsadc";
+                       reg = <0xff280000 0x100>;
+                       interrupts = <GIC_SPI 37 IRQ_TYPE_LEVEL_HIGH>;
+                       #io-channel-cells = <1>;
+                       io-channel-ranges;      
+                       clock-frequency = <50000>;
+                       clocks = <&clk_tsadc>, <&clk_gates7 2>;
+                       clock-names = "tsadc", "pclk_tsadc";
+                       status = "okay";
+       };
         
 
 };
index ccfd533fe97946c976b256c7da6c0dce4f5137f6..1b7cdf759ce081fbde7626a216cc3dbe8d5e4323 100755 (executable)
@@ -315,6 +315,7 @@ CONFIG_DEBUG_GPIO=y
 CONFIG_GPIO_SYSFS=y
 CONFIG_BATTERY_BQ24296=y
 CONFIG_BATTERY_BQ27320=y
+CONFIG_SENSORS_ROCKCHIP_TSADC=y
 CONFIG_THERMAL=y
 CONFIG_MFD_RK808=y
 CONFIG_REGULATOR=y
index df064e8cd9dc2f5b861175d18133a6b35b61daf3..a60cc816d4fcfb4888130f1a4013c2b9b5bece5c 100644 (file)
@@ -1020,6 +1020,15 @@ config SENSORS_SHT21
          This driver can also be built as a module.  If so, the module
          will be called sht21.
 
+config SENSORS_ROCKCHIP_TSADC
+       tristate "ROCKCHIP built-in TSADC"
+       help
+         If you say yes here you get support for the on-board TSADCs of
+         the ROCKCHIP RK3288 and other series of SoC
+
+         This driver can also be built as a module. If so, the module
+         will be called rockchip-hwmon.
+
 config SENSORS_S3C
        tristate "Samsung built-in ADC"
        depends on S3C_ADC
index d17d3e64f9f475d7b2661140d14995e6500aea2b..e809e1bc2927eababad84741cb114952d60bab0b 100644 (file)
@@ -113,6 +113,7 @@ obj-$(CONFIG_SENSORS_NTC_THERMISTOR)        += ntc_thermistor.o
 obj-$(CONFIG_SENSORS_PC87360)  += pc87360.o
 obj-$(CONFIG_SENSORS_PC87427)  += pc87427.o
 obj-$(CONFIG_SENSORS_PCF8591)  += pcf8591.o
+obj-$(CONFIG_SENSORS_ROCKCHIP_TSADC)   += rockchip-hwmon.o rockchip_tsadc.o
 obj-$(CONFIG_SENSORS_S3C)      += s3c-hwmon.o
 obj-$(CONFIG_SENSORS_SCH56XX_COMMON)+= sch56xx-common.o
 obj-$(CONFIG_SENSORS_SCH5627)  += sch5627.o
diff --git a/drivers/hwmon/hwmon-rockchip.h b/drivers/hwmon/hwmon-rockchip.h
new file mode 100755 (executable)
index 0000000..df2c91e
--- /dev/null
@@ -0,0 +1,77 @@
+/*
+ * Copyright (C) ST-Ericsson 2010 - 2013
+ * License terms: GNU General Public License v2
+ * Author: Martin Persson <martin.persson@stericsson.com>
+ *         Hongbo Zhang <hongbo.zhang@linaro.com>
+ */
+
+#ifndef _ROCKCHIP_H
+#define _ROCKCHIP_H
+
+#define NUM_SENSORS 5
+
+struct rockchip_temp;
+
+/*
+ * struct rockchip_temp_ops - rockchip chip specific ops
+ * @read_sensor: reads gpadc output
+ * @irq_handler: irq handler
+ * @show_name: hwmon device name
+ * @show_label: hwmon attribute label
+ * @is_visible: is attribute visible
+ */
+struct rockchip_temp_ops {
+       int (*read_sensor)(int);
+       int (*irq_handler)(int, struct rockchip_temp *);
+       ssize_t (*show_name)(struct device *,
+                       struct device_attribute *, char *);
+       ssize_t (*show_label) (struct device *,
+                       struct device_attribute *, char *);
+       int (*is_visible)(struct attribute *, int);
+};
+
+/*
+ * struct rockchip_temp - representation of temp mon device
+ * @pdev: platform device
+ * @hwmon_dev: hwmon device
+ * @ops: rockchip chip specific ops
+ * @gpadc_addr: gpadc channel address
+ * @min: sensor temperature min value
+ * @max: sensor temperature max value
+ * @max_hyst: sensor temperature hysteresis value for max limit
+ * @min_alarm: sensor temperature min alarm
+ * @max_alarm: sensor temperature max alarm
+ * @work: delayed work scheduled to monitor temperature periodically
+ * @work_active: True if work is active
+ * @lock: mutex
+ * @monitored_sensors: number of monitored sensors
+ * @plat_data: private usage, usually points to platform specific data
+ */
+struct rockchip_temp {
+       struct platform_device *pdev;
+       struct device *hwmon_dev;
+       struct rockchip_temp_ops ops;
+       u8 tsadc_addr[NUM_SENSORS];
+       unsigned long min[NUM_SENSORS];
+       unsigned long max[NUM_SENSORS];
+       unsigned long max_hyst[NUM_SENSORS];
+       bool min_alarm[NUM_SENSORS];
+       bool max_alarm[NUM_SENSORS];
+       struct delayed_work work;
+       bool work_active;
+       struct mutex lock;
+       int monitored_sensors;
+       void *plat_data;
+       void __iomem            *regs;
+       struct clk              *clk;
+       struct clk              *pclk;
+       struct resource         *ioarea;
+       struct tsadc_host               *tsadc;
+       struct work_struct      auto_ht_irq_work;
+       struct workqueue_struct  *workqueue;
+       struct workqueue_struct  *tsadc_workqueue;
+};
+
+int rockchip_hwmon_init(struct rockchip_temp *data);
+
+#endif /* _ROCKCHIP_H */
diff --git a/drivers/hwmon/rockchip-hwmon.c b/drivers/hwmon/rockchip-hwmon.c
new file mode 100755 (executable)
index 0000000..937f31e
--- /dev/null
@@ -0,0 +1,453 @@
+/*
+ * Copyright (C) rockchip 2014
+ * Author:zhangqing <zhangqing@rock-chips.com>
+ *        
+ * License Terms: GNU General Public License v2
+ *
+ * rockchip tsadc does not provide auto tsADC, so to monitor the required temperatures,
+ * a periodic work is used. It is more important to not wake up the CPU than
+ * to perform this job, hence the use of a deferred delay.
+ *
+ */
+
+#include <linux/err.h>
+#include <linux/hwmon.h>
+#include <linux/hwmon-sysfs.h>
+#include <linux/interrupt.h>
+#include <linux/jiffies.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/pm.h>
+#include <linux/slab.h>
+#include <linux/sysfs.h>
+#include <linux/workqueue.h>
+#include "hwmon-rockchip.h"
+
+
+#define DEFAULT_MONITOR_DELAY  HZ
+#define DEFAULT_MAX_TEMP       130
+
+static inline void schedule_monitor(struct rockchip_temp *data)
+{
+       data->work_active = true;
+       schedule_delayed_work(&data->work, DEFAULT_MONITOR_DELAY);
+}
+
+static void threshold_updated(struct rockchip_temp *data)
+{
+       int i;
+       for (i = 0; i < data->monitored_sensors; i++)
+               if (data->max[i] != 0 || data->min[i] != 0) {
+                       schedule_monitor(data);
+                       return;
+               }
+
+       dev_dbg(&data->pdev->dev, "No active thresholds.\n");
+       cancel_delayed_work_sync(&data->work);
+       data->work_active = false;
+}
+
+static void tsadc_monitor(struct work_struct *work)
+{
+       int temp,i, ret;
+       char alarm_node[30];
+       bool updated_min_alarm, updated_max_alarm;
+       struct rockchip_temp *data;
+
+       data = container_of(work, struct rockchip_temp, work.work);
+       mutex_lock(&data->lock);
+
+       for (i = 0; i < data->monitored_sensors; i++) {
+               /* Thresholds are considered inactive if set to 0 */
+               if (data->max[i] == 0 && data->min[i] == 0)
+                       continue;
+
+               if (data->max[i] < data->min[i])
+                       continue;
+
+               temp = data->ops.read_sensor(data->tsadc_addr[i]);
+               if (temp == 150) {
+                       dev_err(&data->pdev->dev, "TSADC read failed\n");
+                       continue;
+               }
+
+               updated_min_alarm = false;
+               updated_max_alarm = false;
+
+               if (data->min[i] != 0) {
+                       if (temp < data->min[i]) {
+                               if (data->min_alarm[i] == false) {
+                                       data->min_alarm[i] = true;
+                                       updated_min_alarm = true;
+                               }
+                       } else {
+                               if (data->min_alarm[i] == true) {
+                                       data->min_alarm[i] = false;
+                                       updated_min_alarm = true;
+                               }
+                       }
+               }
+               if (data->max[i] != 0) {
+                       if (temp > data->max[i]) {
+                               if (data->max_alarm[i] == false) {
+                                       data->max_alarm[i] = true;
+                                       updated_max_alarm = true;
+                               }
+                       } else if (temp < data->max[i] - data->max_hyst[i]) {
+                               if (data->max_alarm[i] == true) {
+                                       data->max_alarm[i] = false;
+                                       updated_max_alarm = true;
+                               }
+                       }
+               }
+
+               if (updated_min_alarm) {
+                       ret = sprintf(alarm_node, "temp%d_min_alarm", i + 1);
+                       sysfs_notify(&data->pdev->dev.kobj, NULL, alarm_node);
+               }
+               if (updated_max_alarm) {
+                       ret = sprintf(alarm_node, "temp%d_max_alarm", i + 1);
+                       sysfs_notify(&data->pdev->dev.kobj, NULL, alarm_node);
+               }
+       }
+
+       schedule_monitor(data);
+       mutex_unlock(&data->lock);
+}
+
+/* HWMON sysfs interfaces */
+static ssize_t show_name(struct device *dev, struct device_attribute *devattr,
+                        char *buf)
+{
+       struct rockchip_temp *data = dev_get_drvdata(dev);
+       /* Show chip name */
+       return data->ops.show_name(dev, devattr, buf);
+}
+
+static ssize_t show_label(struct device *dev,
+                         struct device_attribute *devattr, char *buf)
+{
+       struct rockchip_temp *data = dev_get_drvdata(dev);
+       /* Show each sensor label */
+       return data->ops.show_label(dev, devattr, buf);
+}
+
+static ssize_t show_input(struct device *dev,
+                         struct device_attribute *devattr, char *buf)
+{
+       int  temp;
+       struct rockchip_temp *data = dev_get_drvdata(dev);
+       struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
+       u8 tsadc_addr = data->tsadc_addr[attr->index];
+
+       temp = data->ops.read_sensor(tsadc_addr);
+
+       return sprintf(buf, "%d\n", temp);
+}
+
+/* Set functions (RW nodes) */
+static ssize_t set_min(struct device *dev, struct device_attribute *devattr,
+                      const char *buf, size_t count)
+{
+       unsigned long val;
+       struct rockchip_temp *data = dev_get_drvdata(dev);
+       struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
+       int res = kstrtol(buf, 10, &val);
+       if (res < 0)
+               return res;
+
+       val = clamp_val(val, 0, DEFAULT_MAX_TEMP);
+
+       mutex_lock(&data->lock);
+       data->min[attr->index] = val;
+       threshold_updated(data);
+       mutex_unlock(&data->lock);
+
+       return count;
+}
+
+static ssize_t set_max(struct device *dev, struct device_attribute *devattr,
+                      const char *buf, size_t count)
+{
+       unsigned long val;
+       struct rockchip_temp *data = dev_get_drvdata(dev);
+       struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
+       int res = kstrtol(buf, 10, &val);
+       if (res < 0)
+               return res;
+
+       val = clamp_val(val, 0, DEFAULT_MAX_TEMP);
+
+       mutex_lock(&data->lock);
+       data->max[attr->index] = val;
+       threshold_updated(data);
+       mutex_unlock(&data->lock);
+
+       return count;
+}
+
+static ssize_t set_max_hyst(struct device *dev,
+                           struct device_attribute *devattr,
+                           const char *buf, size_t count)
+{
+       unsigned long val;
+       struct rockchip_temp *data = dev_get_drvdata(dev);
+       struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
+       int res = kstrtoul(buf, 10, &val);
+       if (res < 0)
+               return res;
+
+       val = clamp_val(val, 0, DEFAULT_MAX_TEMP);
+
+       mutex_lock(&data->lock);
+       data->max_hyst[attr->index] = val;
+       threshold_updated(data);
+       mutex_unlock(&data->lock);
+
+       return count;
+}
+
+/* Show functions (RO nodes) */
+static ssize_t show_min(struct device *dev,
+                       struct device_attribute *devattr, char *buf)
+{
+       struct rockchip_temp *data = dev_get_drvdata(dev);
+       struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
+
+       return sprintf(buf, "%ld\n", data->min[attr->index]);
+}
+
+static ssize_t show_max(struct device *dev,
+                       struct device_attribute *devattr, char *buf)
+{
+       struct rockchip_temp *data = dev_get_drvdata(dev);
+       struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
+
+       return sprintf(buf, "%ld\n", data->max[attr->index]);
+}
+
+static ssize_t show_max_hyst(struct device *dev,
+                            struct device_attribute *devattr, char *buf)
+{
+       struct rockchip_temp *data = dev_get_drvdata(dev);
+       struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
+
+       return sprintf(buf, "%ld\n", data->max_hyst[attr->index]);
+}
+
+static ssize_t show_min_alarm(struct device *dev,
+                             struct device_attribute *devattr, char *buf)
+{
+       struct rockchip_temp *data = dev_get_drvdata(dev);
+       struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
+
+       return sprintf(buf, "%d\n", data->min_alarm[attr->index]);
+}
+
+static ssize_t show_max_alarm(struct device *dev,
+                             struct device_attribute *devattr, char *buf)
+{
+       struct rockchip_temp *data = dev_get_drvdata(dev);
+       struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
+
+       return sprintf(buf, "%d\n", data->max_alarm[attr->index]);
+}
+
+static umode_t rockchip_attrs_visible(struct kobject *kobj,
+                                  struct attribute *attr, int n)
+{
+       struct device *dev = container_of(kobj, struct device, kobj);
+       struct rockchip_temp *data = dev_get_drvdata(dev);
+
+       if (data->ops.is_visible)
+               return data->ops.is_visible(attr, n);
+
+       return attr->mode;
+}
+
+/* Chip name, required by hwmon */
+static SENSOR_DEVICE_ATTR(name, S_IRUGO, show_name, NULL, 0);
+
+/* GPADC - SENSOR0 */
+static SENSOR_DEVICE_ATTR(temp0_label, S_IRUGO, show_label, NULL, 0);
+static SENSOR_DEVICE_ATTR(temp0_input, S_IRUGO, show_input, NULL, 0);
+static SENSOR_DEVICE_ATTR(temp0_min, S_IWUSR | S_IRUGO, show_min, set_min, 0);
+static SENSOR_DEVICE_ATTR(temp0_max, S_IWUSR | S_IRUGO, show_max, set_max, 0);
+static SENSOR_DEVICE_ATTR(temp0_max_hyst, S_IWUSR | S_IRUGO,
+                         show_max_hyst, set_max_hyst, 0);
+static SENSOR_DEVICE_ATTR(temp0_min_alarm, S_IRUGO, show_min_alarm, NULL, 0);
+static SENSOR_DEVICE_ATTR(temp0_max_alarm, S_IRUGO, show_max_alarm, NULL, 0);
+
+/* GPADC - SENSOR1 */
+static SENSOR_DEVICE_ATTR(temp1_label, S_IRUGO, show_label, NULL, 1);
+static SENSOR_DEVICE_ATTR(temp1_input, S_IRUGO, show_input, NULL, 1);
+static SENSOR_DEVICE_ATTR(temp1_min, S_IWUSR | S_IRUGO, show_min, set_min, 1);
+static SENSOR_DEVICE_ATTR(temp1_max, S_IWUSR | S_IRUGO, show_max, set_max, 1);
+static SENSOR_DEVICE_ATTR(temp1_max_hyst, S_IWUSR | S_IRUGO,
+                         show_max_hyst, set_max_hyst, 1);
+static SENSOR_DEVICE_ATTR(temp1_min_alarm, S_IRUGO, show_min_alarm, NULL, 1);
+static SENSOR_DEVICE_ATTR(temp1_max_alarm, S_IRUGO, show_max_alarm, NULL, 1);
+
+/* GPADC - SENSOR2 */
+static SENSOR_DEVICE_ATTR(temp2_label, S_IRUGO, show_label, NULL, 2);
+static SENSOR_DEVICE_ATTR(temp2_input, S_IRUGO, show_input, NULL, 2);
+static SENSOR_DEVICE_ATTR(temp2_min, S_IWUSR | S_IRUGO, show_min, set_min, 2);
+static SENSOR_DEVICE_ATTR(temp2_max, S_IWUSR | S_IRUGO, show_max, set_max, 2);
+static SENSOR_DEVICE_ATTR(temp2_max_hyst, S_IWUSR | S_IRUGO,
+                         show_max_hyst, set_max_hyst, 2);
+static SENSOR_DEVICE_ATTR(temp2_min_alarm, S_IRUGO, show_min_alarm, NULL, 2);
+static SENSOR_DEVICE_ATTR(temp2_max_alarm, S_IRUGO, show_max_alarm, NULL, 2);
+
+/* GPADC - SENSOR3 */
+static SENSOR_DEVICE_ATTR(temp3_label, S_IRUGO, show_label, NULL, 3);
+static SENSOR_DEVICE_ATTR(temp3_input, S_IRUGO, show_input, NULL, 3);
+static SENSOR_DEVICE_ATTR(temp3_min, S_IWUSR | S_IRUGO, show_min, set_min, 3);
+static SENSOR_DEVICE_ATTR(temp3_max, S_IWUSR | S_IRUGO, show_max, set_max, 3);
+static SENSOR_DEVICE_ATTR(temp3_max_hyst, S_IWUSR | S_IRUGO,
+                         show_max_hyst, set_max_hyst, 3);
+static SENSOR_DEVICE_ATTR(temp3_min_alarm, S_IRUGO, show_min_alarm, NULL, 3);
+static SENSOR_DEVICE_ATTR(temp3_max_alarm, S_IRUGO, show_max_alarm, NULL, 3);
+
+
+struct attribute *rockchip_temp_attributes[] = {
+       &sensor_dev_attr_name.dev_attr.attr,
+
+       &sensor_dev_attr_temp0_label.dev_attr.attr,
+       &sensor_dev_attr_temp0_input.dev_attr.attr,
+       &sensor_dev_attr_temp0_min.dev_attr.attr,
+       &sensor_dev_attr_temp0_max.dev_attr.attr,
+       &sensor_dev_attr_temp0_max_hyst.dev_attr.attr,
+       &sensor_dev_attr_temp0_min_alarm.dev_attr.attr,
+       &sensor_dev_attr_temp0_max_alarm.dev_attr.attr,
+
+       &sensor_dev_attr_temp1_label.dev_attr.attr,
+       &sensor_dev_attr_temp1_input.dev_attr.attr,
+       &sensor_dev_attr_temp1_min.dev_attr.attr,
+       &sensor_dev_attr_temp1_max.dev_attr.attr,
+       &sensor_dev_attr_temp1_max_hyst.dev_attr.attr,
+       &sensor_dev_attr_temp1_min_alarm.dev_attr.attr,
+       &sensor_dev_attr_temp1_max_alarm.dev_attr.attr,
+
+       &sensor_dev_attr_temp2_label.dev_attr.attr,
+       &sensor_dev_attr_temp2_input.dev_attr.attr,
+       &sensor_dev_attr_temp2_min.dev_attr.attr,
+       &sensor_dev_attr_temp2_max.dev_attr.attr,
+       &sensor_dev_attr_temp2_max_hyst.dev_attr.attr,
+       &sensor_dev_attr_temp2_min_alarm.dev_attr.attr,
+       &sensor_dev_attr_temp2_max_alarm.dev_attr.attr,
+
+       &sensor_dev_attr_temp3_label.dev_attr.attr,
+       &sensor_dev_attr_temp3_input.dev_attr.attr,
+       &sensor_dev_attr_temp3_min.dev_attr.attr,
+       &sensor_dev_attr_temp3_max.dev_attr.attr,
+       &sensor_dev_attr_temp3_max_hyst.dev_attr.attr,
+       &sensor_dev_attr_temp3_min_alarm.dev_attr.attr,
+       &sensor_dev_attr_temp3_max_alarm.dev_attr.attr,
+
+       
+       NULL
+};
+
+static const struct attribute_group rockchip_temp_group = {
+       .attrs = rockchip_temp_attributes,
+       .is_visible = rockchip_attrs_visible,
+};
+
+static int rockchip_temp_probe(struct platform_device *pdev)
+{
+       struct rockchip_temp *data;
+       int err;
+       printk("%s,line=%d\n", __func__,__LINE__);
+
+       data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL);
+       if (!data)
+               return -ENOMEM;
+       data->pdev = pdev;
+       mutex_init(&data->lock);
+
+       /* Chip specific initialization */
+       err = rockchip_hwmon_init(data);
+       if (err < 0 || !data->ops.read_sensor || !data->ops.show_name ||
+                       !data->ops.show_label)
+               return err;
+
+       INIT_DEFERRABLE_WORK(&data->work, tsadc_monitor);
+       platform_set_drvdata(pdev, data);
+
+       err = sysfs_create_group(&pdev->dev.kobj, &rockchip_temp_group);
+       if (err < 0) {
+               dev_err(&pdev->dev, "Create sysfs group failed (%d)\n", err);
+               return err;
+       }
+       data->hwmon_dev = hwmon_device_register(&pdev->dev);
+       if (IS_ERR(data->hwmon_dev)) {
+               err = PTR_ERR(data->hwmon_dev);
+               dev_err(&pdev->dev, "Class registration failed (%d)\n", err);
+               goto exit_sysfs_group;
+       }
+       return 0;
+
+exit_sysfs_group:
+       sysfs_remove_group(&pdev->dev.kobj, &rockchip_temp_group);
+       return err;
+}
+
+static int rockchip_temp_remove(struct platform_device *pdev)
+{
+       struct rockchip_temp *data = platform_get_drvdata(pdev);
+
+       cancel_delayed_work_sync(&data->work);
+       hwmon_device_unregister(data->hwmon_dev);
+       sysfs_remove_group(&pdev->dev.kobj, &rockchip_temp_group);
+
+       return 0;
+}
+
+static int rockchip_temp_suspend(struct platform_device *pdev,
+                              pm_message_t state)
+{
+       struct rockchip_temp *data = platform_get_drvdata(pdev);
+
+       if (data->work_active)
+               cancel_delayed_work_sync(&data->work);
+
+       return 0;
+}
+
+static int rockchip_temp_resume(struct platform_device *pdev)
+{
+       struct rockchip_temp *data = platform_get_drvdata(pdev);
+
+       if (data->work_active)
+               schedule_monitor(data);
+
+       return 0;
+}
+
+#ifdef CONFIG_OF
+static const struct of_device_id rockchip_temp_match[] = {
+       { .compatible = "rockchip,tsadc" },
+       {},
+};
+#endif
+
+static struct platform_driver rockchip_temp_driver = {
+       .driver = {
+               .owner = THIS_MODULE,
+               .name = "tsadc",
+               .of_match_table = of_match_ptr(rockchip_temp_match),
+       },
+       .suspend = rockchip_temp_suspend,
+       .resume = rockchip_temp_resume,
+       .probe = rockchip_temp_probe,
+       .remove = rockchip_temp_remove,
+};
+
+module_platform_driver(rockchip_temp_driver);
+
+MODULE_AUTHOR("<rockchip>");
+MODULE_DESCRIPTION("rockchip temperature driver");
+MODULE_LICENSE("GPL");
+
diff --git a/drivers/hwmon/rockchip_tsadc.c b/drivers/hwmon/rockchip_tsadc.c
new file mode 100755 (executable)
index 0000000..df9e3d3
--- /dev/null
@@ -0,0 +1,469 @@
+/*
+ * Copyright (C) ST-Ericsson 2010 - 2013
+ * Author: Martin Persson <martin.persson@stericsson.com>
+ *         Hongbo Zhang <hongbo.zhang@linaro.org>
+ * License Terms: GNU General Public License v2
+ *
+ * When the AB8500 thermal warning temperature is reached (threshold cannot
+ * be changed by SW), an interrupt is set, and if no further action is taken
+ * within a certain time frame, pm_power off will be called.
+ *
+ * When AB8500 thermal shutdown temperature is reached a hardware shutdown of
+ * the AB8500 will occur.
+ */
+
+ #include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/device.h>
+#include <linux/platform_device.h>
+#include <linux/err.h>
+#include <linux/clk.h>
+#include <linux/interrupt.h>
+#include <linux/irq.h>
+#include <linux/io.h>
+#include <linux/delay.h>
+#include <linux/slab.h>
+#include <linux/workqueue.h>
+#include <linux/ioport.h>
+
+#include <linux/err.h>
+#include <linux/hwmon.h>
+#include <linux/hwmon-sysfs.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/sysfs.h>
+
+#include <linux/timer.h>
+#include <linux/completion.h>
+#include <linux/of_irq.h>
+#include <linux/regulator/consumer.h>
+#include <linux/of_platform.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+#include "hwmon-rockchip.h"
+
+
+#define DEFAULT_POWER_OFF_DELAY        (HZ * 10)
+/* Number of monitored sensors should not greater than NUM_SENSORS */
+#define NUM_MONITORED_SENSORS  4
+
+#define TSADC_USER_CON  0x00
+#define TSADC_AUTO_CON  0x04
+
+#define TSADC_CTRL_CH(ch)      ((ch) << 0)
+#define TSADC_CTRL_POWER_UP    (1 << 3)
+#define TSADC_CTRL_START       (1 << 4)
+
+#define TSADC_STAS_BUSY                (1 << 12)
+#define TSADC_STAS_BUSY_MASK   (1 << 12)
+#define TSADC_AUTO_STAS_BUSY           (1 << 16)
+#define TSADC_AUTO_STAS_BUSY_MASK      (1 << 16)
+#define TSADC_SAMPLE_DLY_SEL  (1 << 17)
+#define TSADC_SAMPLE_DLY_SEL_MASK  (1 << 17)
+
+#define TSADC_INT_EN  0x08
+#define TSADC_INT_PD  0x0c
+
+#define TSADC_DATA0  0x20
+#define TSADC_DATA1  0x24
+#define TSADC_DATA2  0x28
+#define TSADC_DATA3  0x2c
+#define TSADC_DATA_MASK                0xfff
+
+#define TSADC_COMP0_INT  0x30
+#define TSADC_COMP1_INT  0x34
+#define TSADC_COMP2_INT  0x38
+#define TSADC_COMP3_INT  0x3c
+
+#define TSADC_COMP0_SHUT  0x40
+#define TSADC_COMP1_SHUT  0x44
+#define TSADC_COMP2_SHUT  0x48
+#define TSADC_COMP3_SHUT  0x4c
+
+#define TSADC_HIGHT_INT_DEBOUNCE  0x60
+#define TSADC_HIGHT_TSHUT_DEBOUNCE  0x64
+#define TSADC_HIGHT_INT_DEBOUNCE_TIME 0x03
+#define TSADC_HIGHT_TSHUT_DEBOUNCE_TIME 0x03
+
+#define TSADC_AUTO_PERIOD  0x68
+#define TSADC_AUTO_PERIOD_HT  0x6c
+#define TSADC_AUTO_PERIOD_TIME  0x10
+#define TSADC_AUTO_PERIOD_HT_TIME  0x10
+
+#define TSADC_AUTO_EVENT_NAME          "tsadc"
+
+#define TSADC_COMP_INT_DATA            80
+#define TSADC_COMP_INT_DATA_MASK               0xfff
+#define TSADC_COMP_SHUT_DATA           100
+#define TSADC_COMP_SHUT_DATA_MASK              0xfff
+#define TSADC_HIGH_TEMP_TO_SHUTDOWN 0  // 1: set gpio0_a0 output 0 ; 0:reset cpu
+#define TSADC_TEMP_INT_EN 1
+#define TSADC_TEMP_SHUT_EN 1
+
+struct rockchip_tsadc_temp {
+       struct delayed_work power_off_work;
+       struct rockchip_temp *rockchip_data;
+       void __iomem            *regs;
+       struct clk              *clk;
+       struct clk              *pclk;
+       int irq;
+       struct resource         *ioarea;
+       struct tsadc_host               *tsadc;
+       struct work_struct      auto_ht_irq_work;
+       struct workqueue_struct  *workqueue;
+       struct workqueue_struct  *tsadc_workqueue;
+};
+struct tsadc_table
+{
+       int code;
+       int temp;
+};
+
+static const struct tsadc_table table[] =
+{
+       {TSADC_DATA_MASK, -40},
+
+       {3800, -40},
+       {3792, -35},
+       {3783, -30},
+       {3774, -25},
+       {3765, -20},
+       {3756, -15},
+       {3747, -10},
+       {3737, -5},
+       {3728, 0},
+       {3718, 5},
+
+       {3708, 10},
+       {3698, 15},
+       {3688, 20},
+       {3678, 25},
+       {3667, 30},
+       {3656, 35},
+       {3645, 40},
+       {3634, 45},
+       {3623, 50},
+       {3611, 55},
+
+       {3600, 60},
+       {3588, 65},
+       {3575, 70},
+       {3563, 75},
+       {3550, 80},
+       {3537, 85},
+       {3524, 90},
+       {3510, 95},
+       {3496, 100},
+       {3482, 105},
+
+       {3467, 110},
+       {3452, 115},
+       {3437, 120},
+       {3421, 125},
+
+       {0, 125},
+};
+
+static struct rockchip_tsadc_temp *g_dev;
+
+static DEFINE_MUTEX(tsadc_mutex);
+
+static u32 tsadc_readl(u32 offset)
+{
+       return readl_relaxed(g_dev->regs + offset);
+}
+
+static void tsadc_writel(u32 val, u32 offset)
+{
+       writel_relaxed(val, g_dev->regs + offset);
+}
+
+void rockchip_tsadc_auto_ht_work(struct work_struct *work)
+{
+        int ret,val;
+
+//     printk("%s,line=%d\n", __func__,__LINE__);
+
+       mutex_lock(&tsadc_mutex);
+
+       val = tsadc_readl(TSADC_INT_PD);
+       tsadc_writel(val &(~ (1 <<8) ), TSADC_INT_PD);
+       ret = tsadc_readl(TSADC_INT_PD);
+       tsadc_writel(ret | 0xff, TSADC_INT_PD);       //clr irq status
+       if ((val & 0x0f) != 0){
+               printk("rockchip tsadc is over temp . %s,line=%d\n", __func__,__LINE__);
+               pm_power_off();                                 //power_off
+       }
+       mutex_unlock(&tsadc_mutex);
+}
+
+static irqreturn_t rockchip_tsadc_auto_ht_interrupt(int irq, void *data)
+{
+       struct rockchip_tsadc_temp *dev = data;
+
+       printk("%s,line=%d\n", __func__,__LINE__);
+       
+       queue_work(dev->workqueue, &dev->auto_ht_irq_work);
+       
+       return IRQ_HANDLED;
+}
+
+static void rockchip_tsadc_set_cmpn_int_vale( int chn, int temp)
+{
+       u32 code = 0;
+       int i;
+
+       for (i = 0; i < ARRAY_SIZE(table) - 1; i++) {
+               if (temp <= table[i].temp && temp > table[i -1].temp) {
+                       code = table[i].code;
+               }
+       }
+       tsadc_writel((code & TSADC_COMP_INT_DATA_MASK), (TSADC_COMP0_INT + chn*4));
+
+}
+
+static void rockchip_tsadc_set_cmpn_shut_vale( int chn, int temp)
+{
+       u32 code=0;
+       int i;
+
+       for (i = 0; i < ARRAY_SIZE(table) - 1; i++) {
+               if (temp <= table[i].temp && temp > table[i -1].temp) {
+                       code = table[i].code;
+               }
+       }
+
+       tsadc_writel((code & TSADC_COMP_SHUT_DATA_MASK), (TSADC_COMP0_SHUT + chn*4));
+}
+
+static void rockchip_tsadc_set_auto_int_en( int chn, int ht_int_en,int tshut_en)
+{
+       u32 ret;
+       tsadc_writel(0, TSADC_INT_EN);
+       if (ht_int_en){
+               ret = tsadc_readl(TSADC_INT_EN);
+               tsadc_writel( ret | (1 << chn), TSADC_INT_EN);
+       }
+       if (tshut_en){
+               ret = tsadc_readl(TSADC_INT_EN);
+               #ifdef TSADC_HIGH_TEMP_TO_SHUTDOWN
+               tsadc_writel( ret | (1 << (chn + 4)), TSADC_INT_EN);
+               #else
+               tsadc_writel( ret | (1 << (chn + 8)), TSADC_INT_EN);
+               #endif
+       }       
+
+}
+static void rockchip_tsadc_auto_mode_set( int chn, int int_temp, int shut_temp,int int_en, int shut_en)
+{
+
+       u32 ret;
+       
+       if (!g_dev || chn > 1)
+               return;
+       
+       mutex_lock(&tsadc_mutex);
+       
+       clk_enable(g_dev->pclk);
+       clk_enable(g_dev->clk);
+       
+       msleep(10);
+       tsadc_writel(0, TSADC_AUTO_CON);
+       tsadc_writel( 1 << (4+chn), TSADC_AUTO_CON);
+       msleep(10);
+       if ((tsadc_readl(TSADC_AUTO_CON) & TSADC_AUTO_STAS_BUSY_MASK) != TSADC_AUTO_STAS_BUSY) {
+               rockchip_tsadc_set_cmpn_int_vale(chn,int_temp);
+               rockchip_tsadc_set_cmpn_shut_vale(chn,shut_temp),
+
+//             tsadc_writel(TSADC_AUTO_PERIOD_TIME, TSADC_AUTO_PERIOD);
+//             tsadc_writel(TSADC_AUTO_PERIOD_HT_TIME, TSADC_AUTO_PERIOD_HT);
+
+//             tsadc_writel(TSADC_HIGHT_INT_DEBOUNCE_TIME, TSADC_HIGHT_INT_DEBOUNCE);
+//             tsadc_writel(TSADC_HIGHT_TSHUT_DEBOUNCE_TIME, TSADC_HIGHT_TSHUT_DEBOUNCE);
+               
+               rockchip_tsadc_set_auto_int_en(chn,int_en,shut_en);     
+       }
+
+       msleep(10);
+
+       ret = tsadc_readl(TSADC_AUTO_CON);
+       tsadc_writel(ret | (1 <<0) , TSADC_AUTO_CON);
+       
+       mutex_unlock(&tsadc_mutex);
+               
+}
+
+int rockchip_tsadc_set_auto_temp(int chn)
+{
+       rockchip_tsadc_auto_mode_set(chn, TSADC_COMP_INT_DATA,TSADC_COMP_SHUT_DATA,TSADC_TEMP_INT_EN,TSADC_TEMP_SHUT_EN);
+       return 0;
+}
+EXPORT_SYMBOL(rockchip_tsadc_set_auto_temp);
+
+static void rockchip_tsadc_get(int chn, int *temp, int *code)
+{
+       *temp = 0;
+       *code = 0;
+
+       if (!g_dev || chn > 4){
+               *temp = 150;
+               return ;
+       }
+               
+       mutex_lock(&tsadc_mutex);
+
+       clk_enable(g_dev->pclk);
+       clk_enable(g_dev->clk);
+
+       msleep(10);
+       tsadc_writel(0, TSADC_USER_CON);
+       tsadc_writel(TSADC_CTRL_POWER_UP | TSADC_CTRL_CH(chn), TSADC_USER_CON);
+       msleep(20);
+       if ((tsadc_readl(TSADC_USER_CON) & TSADC_STAS_BUSY_MASK) != TSADC_STAS_BUSY) {
+               int i;
+               *code = tsadc_readl((TSADC_DATA0 + chn*4)) & TSADC_DATA_MASK;
+               for (i = 0; i < ARRAY_SIZE(table) - 1; i++) {
+                       if ((*code) <= table[i].code && (*code) > table[i + 1].code) {
+                               *temp = table[i].temp + (table[i + 1].temp - table[i].temp) * (table[i].code - (*code)) / (table[i].code - table[i + 1].code);
+                       }
+               }
+       }
+       
+       tsadc_writel(0, TSADC_USER_CON);
+
+       clk_disable(g_dev->clk);
+       clk_disable(g_dev->pclk);
+
+       mutex_unlock(&tsadc_mutex);
+}
+
+ int rockchip_tsadc_get_temp(int chn)
+{
+       int temp, code;
+       
+       rockchip_tsadc_get(chn, &temp, &code);
+       return temp;
+}
+EXPORT_SYMBOL(rockchip_tsadc_get_temp);
+
+static ssize_t rockchip_show_name(struct device *dev,
+               struct device_attribute *devattr, char *buf)
+{
+       return sprintf(buf, "rockchip-tsadc\n");
+}
+
+static ssize_t rockchip_show_label(struct device *dev,
+               struct device_attribute *devattr, char *buf)
+{
+       char *label;
+       struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
+       int index = attr->index;
+
+       switch (index) {
+       case 0:
+               label = "tsadc0";
+               break;
+       case 1:
+               label = "tsadc1";
+               break;
+       case 2:
+               label = "tsadc2";
+               break;
+       case 3:
+               label = "tsadc3";
+               break;
+       default:
+               return -EINVAL;
+       }
+
+       return sprintf(buf, "%s\n", label);
+}
+
+
+int rockchip_hwmon_init(struct rockchip_temp *data)
+{
+       struct rockchip_tsadc_temp *rockchip_tsadc_data;
+       struct resource *res;
+       struct device_node *np = data->pdev->dev.of_node;
+       int ret,irq;
+       u32 rate;
+       
+       rockchip_tsadc_data = devm_kzalloc(&data->pdev->dev, sizeof(*rockchip_tsadc_data),
+               GFP_KERNEL);
+       if (!rockchip_tsadc_data)
+               return -ENOMEM;
+
+       res = platform_get_resource(data->pdev, IORESOURCE_MEM, 0);
+       rockchip_tsadc_data->regs = devm_request_and_ioremap(&data->pdev->dev, res);
+       if (!rockchip_tsadc_data->regs) {
+               dev_err(&data->pdev->dev, "cannot map IO\n");
+               return -ENXIO;
+       } 
+
+       //irq request   
+       irq = platform_get_irq(data->pdev, 0);
+       if (irq < 0) {
+               dev_err(&data->pdev->dev, "no irq resource?\n");
+               return -EPERM;
+       }
+       rockchip_tsadc_data->irq = irq;
+       ret = request_threaded_irq(rockchip_tsadc_data->irq, NULL, rockchip_tsadc_auto_ht_interrupt, IRQF_ONESHOT, TSADC_AUTO_EVENT_NAME, rockchip_tsadc_data);
+       if (ret < 0) {
+               dev_err(&data->pdev->dev, "failed to attach tsadc irq\n");
+               return -EPERM;
+       }       
+
+       rockchip_tsadc_data->workqueue = create_singlethread_workqueue("rockchip_tsadc");
+       INIT_WORK(&rockchip_tsadc_data->auto_ht_irq_work, rockchip_tsadc_auto_ht_work);
+       
+       //clk enable
+       rockchip_tsadc_data->clk = devm_clk_get(&data->pdev->dev, "tsadc");
+       if (IS_ERR(rockchip_tsadc_data->clk)) {
+           dev_err(&data->pdev->dev, "failed to get tsadc clock\n");
+           ret = PTR_ERR(rockchip_tsadc_data->clk);
+           return -EPERM;
+       }
+
+       if(of_property_read_u32(np, "clock-frequency", &rate)) {
+          dev_err(&data->pdev->dev, "Missing clock-frequency property in the DT.\n");
+         return -EPERM;
+       }
+
+       ret = clk_set_rate(rockchip_tsadc_data->clk, rate);
+           if(ret < 0) {
+           dev_err(&data->pdev->dev, "failed to set adc clk\n");
+           return -EPERM;
+       }
+       clk_prepare_enable(rockchip_tsadc_data->clk);
+
+       rockchip_tsadc_data->pclk = devm_clk_get(&data->pdev->dev, "pclk_tsadc");
+       if (IS_ERR(rockchip_tsadc_data->pclk)) {
+           dev_err(&data->pdev->dev, "failed to get tsadc pclk\n");
+           ret = PTR_ERR(rockchip_tsadc_data->pclk);
+           return -EPERM;
+       }
+       clk_prepare_enable(rockchip_tsadc_data->pclk);
+
+       platform_set_drvdata(data->pdev, rockchip_tsadc_data);
+       g_dev = rockchip_tsadc_data;
+       data->plat_data = rockchip_tsadc_data;
+
+       data->monitored_sensors = NUM_MONITORED_SENSORS;
+       data->ops.read_sensor = rockchip_tsadc_get_temp;
+       data->ops.show_name = rockchip_show_name;
+       data->ops.show_label = rockchip_show_label;
+       data->ops.is_visible = NULL;
+
+//     rockchip_tsadc_set_auto_temp(0);
+
+       dev_info(&data->pdev->dev, "initialized\n");
+
+       return 0;
+}
+EXPORT_SYMBOL(rockchip_hwmon_init);
+
+MODULE_DESCRIPTION("Driver for TSADC");
+MODULE_AUTHOR("zhangqing, zhangqing@rock-chips.com");
+MODULE_LICENSE("GPL");
+