summaryrefslogtreecommitdiff
path: root/drivers/hwmon
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/hwmon')
-rw-r--r--drivers/hwmon/gpio-fan.c165
-rwxr-xr-xdrivers/hwmon/hwmon-rockchip.h77
-rwxr-xr-xdrivers/hwmon/rockchip-hwmon.c453
-rwxr-xr-xdrivers/hwmon/rockchip_tsadc.c520
4 files changed, 1204 insertions, 11 deletions
diff --git a/drivers/hwmon/gpio-fan.c b/drivers/hwmon/gpio-fan.c
index 685568b1236d..4622cbc99b58 100644
--- a/drivers/hwmon/gpio-fan.c
+++ b/drivers/hwmon/gpio-fan.c
@@ -26,6 +26,8 @@
#include <linux/interrupt.h>
#include <linux/irq.h>
#include <linux/platform_device.h>
+#include <linux/devfreq.h>
+#include <linux/devfreq_cooling.h>
#include <linux/err.h>
#include <linux/mutex.h>
#include <linux/hwmon.h>
@@ -53,6 +55,8 @@ struct gpio_fan_data {
bool pwm_enable;
struct gpio_fan_alarm *alarm;
struct work_struct alarm_work;
+ struct devfreq *devfreq;
+ struct thermal_cooling_device *devfreq_cooling;
};
/*
@@ -537,11 +541,128 @@ static const struct of_device_id of_gpio_fan_match[] = {
MODULE_DEVICE_TABLE(of, of_gpio_fan_match);
#endif /* CONFIG_OF_GPIO */
+static inline void reset_last_status(struct devfreq *devfreq)
+{
+ devfreq->last_status.total_time = 1;
+ devfreq->last_status.busy_time = 1;
+}
+
+static int rockchip_fanfreq_target(struct device *dev, unsigned long *freq,
+ u32 flags)
+{
+ struct gpio_fan_data *fan_data = dev_get_drvdata(dev);
+ struct dev_pm_opp *opp;
+ struct devfreq_dev_profile *devp;
+ int i = 0;
+ int speed_index = 0;
+
+ if (!fan_data || IS_ERR_OR_NULL(fan_data->devfreq))
+ return 0;
+
+ devp = fan_data->devfreq->profile;
+
+ rcu_read_lock();
+ opp = devfreq_recommended_opp(dev, freq, flags);
+ if (IS_ERR(opp)) {
+ rcu_read_unlock();
+ return PTR_ERR(opp);
+ }
+ *freq = dev_pm_opp_get_freq(opp);
+ rcu_read_unlock();
+
+ for (i = 0; i < devp->max_state; i++) {
+ if (*freq < devp->freq_table[i])
+ break;
+ }
+
+ speed_index = devp->max_state - i;
+
+ set_fan_speed(fan_data, speed_index);
+
+ fan_data->devfreq->last_status.current_frequency = *freq;
+
+ return 0;
+}
+
+static int rockchip_fanfreq_get_dev_status(struct device *dev,
+ struct devfreq_dev_status *stat)
+{
+ stat->busy_time = 1;
+ stat->total_time = 1;
+ return 0;
+}
+
+static int rockchip_fanfreq_get_cur_freq(struct device *dev,
+ unsigned long *freq)
+{
+ struct gpio_fan_data *fan_data = dev_get_drvdata(dev);
+
+ if (!fan_data || IS_ERR_OR_NULL(fan_data->devfreq))
+ return 0;
+
+ *freq = fan_data->devfreq->last_status.current_frequency;
+
+ return 0;
+}
+
+static struct devfreq_dev_profile rockchip_devfreq_fan_profile = {
+ .polling_ms = 2000,
+ .target = rockchip_fanfreq_target,
+ .get_dev_status = rockchip_fanfreq_get_dev_status,
+ .get_cur_freq = rockchip_fanfreq_get_cur_freq,
+};
+
+static struct devfreq_cooling_power fan_cooling_power_data = {
+ .dyn_power_coeff = 120,
+};
+
+static int rockchip_fanfreq_init_freq_table(struct device *dev,
+ struct devfreq_dev_profile *devp)
+{
+ int count;
+ int i = 0;
+ unsigned long freq = 0;
+ struct dev_pm_opp *opp;
+
+ rcu_read_lock();
+ count = dev_pm_opp_get_opp_count(dev);
+ if (count < 0) {
+ rcu_read_unlock();
+ return count;
+ }
+ rcu_read_unlock();
+
+ devp->freq_table =
+ devm_kmalloc_array(dev, count, sizeof(devp->freq_table[0]),
+ GFP_KERNEL);
+ if (!devp->freq_table)
+ return -ENOMEM;
+
+ rcu_read_lock();
+ for (i = 0; i < count; i++, freq++) {
+ opp = dev_pm_opp_find_freq_ceil(dev, &freq);
+ if (IS_ERR(opp))
+ break;
+
+ devp->freq_table[i] = freq;
+ }
+ rcu_read_unlock();
+
+ if (count != i)
+ dev_warn(dev, "Unable to enumerate all OPPs (%d!=%d)\n",
+ count, i);
+
+ devp->max_state = i;
+ return 0;
+}
+
static int gpio_fan_probe(struct platform_device *pdev)
{
int err;
struct gpio_fan_data *fan_data;
struct gpio_fan_platform_data *pdata = dev_get_platdata(&pdev->dev);
+ struct devfreq_dev_profile *devp = &rockchip_devfreq_fan_profile;
+ struct device *dev = &pdev->dev;
fan_data = devm_kzalloc(&pdev->dev, sizeof(struct gpio_fan_data),
GFP_KERNEL);
@@ -592,17 +713,39 @@ static int gpio_fan_probe(struct platform_device *pdev)
gpio_fan_groups);
if (IS_ERR(fan_data->hwmon_dev))
return PTR_ERR(fan_data->hwmon_dev);
-#ifdef CONFIG_OF_GPIO
- /* Optional cooling device register for Device tree platforms */
- fan_data->cdev = thermal_of_cooling_device_register(pdev->dev.of_node,
- "gpio-fan",
- fan_data,
- &gpio_fan_cool_ops);
-#else /* CONFIG_OF_GPIO */
- /* Optional cooling device register for non Device tree platforms */
- fan_data->cdev = thermal_cooling_device_register("gpio-fan", fan_data,
- &gpio_fan_cool_ops);
-#endif /* CONFIG_OF_GPIO */
+
+ if (dev_pm_opp_of_add_table(dev)) {
+ dev_err(dev, "Invalid operating-points\n");
+ return -EINVAL;
+ }
+
+ if (rockchip_fanfreq_init_freq_table(dev, devp))
+ return -EFAULT;
+
+ fan_data->devfreq = devm_devfreq_add_device(dev, devp,
+ "performance", NULL);
+ if (IS_ERR(fan_data->devfreq))
+ return PTR_ERR(fan_data->devfreq);
+
+ devm_devfreq_register_opp_notifier(dev, fan_data->devfreq);
+
+ fan_data->devfreq->min_freq = devp->freq_table[0];
+ fan_data->devfreq->max_freq =
+ devp->freq_table[devp->max_state ? devp->max_state - 1 : 0];
+ fan_data->devfreq->last_status.current_frequency =
+ fan_data->devfreq->max_freq;
+ devp->initial_freq = fan_data->devfreq->max_freq;
+
+ reset_last_status(fan_data->devfreq);
+
+ fan_data->devfreq_cooling =
+ of_devfreq_cooling_register_power(dev->of_node,
+ fan_data->devfreq,
+ &fan_cooling_power_data);
+ if (IS_ERR_OR_NULL(fan_data->devfreq_cooling)) {
+ err = PTR_ERR(fan_data->devfreq_cooling);
+ dev_err(dev, "Failed to register cooling device (%d)\n", err);
+ }
dev_info(&pdev->dev, "GPIO fan initialized\n");
diff --git a/drivers/hwmon/hwmon-rockchip.h b/drivers/hwmon/hwmon-rockchip.h
new file mode 100755
index 000000000000..df2c91ed8d25
--- /dev/null
+++ b/drivers/hwmon/hwmon-rockchip.h
@@ -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
index 000000000000..bed728ed4e49
--- /dev/null
+++ b/drivers/hwmon/rockchip-hwmon.c
@@ -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 <linux/rockchip/common.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(i);
+ if (temp == INVALID_TEMP) {
+ 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);
+
+ temp = data->ops.read_sensor(attr->index);
+
+ 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
index 000000000000..22691294087c
--- /dev/null
+++ b/drivers/hwmon/rockchip_tsadc.c
@@ -0,0 +1,520 @@
+/*
+ * 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 <linux/rockchip/common.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 0x0a
+#define TSADC_HIGHT_TSHUT_DEBOUNCE_TIME 0x0a
+
+#define TSADC_AUTO_PERIOD 0x68
+#define TSADC_AUTO_PERIOD_HT 0x6c
+#define TSADC_AUTO_PERIOD_TIME 0x03e8
+#define TSADC_AUTO_PERIOD_HT_TIME 0x64
+
+#define TSADC_AUTO_EVENT_NAME "tsadc"
+
+#define TSADC_COMP_INT_DATA 80
+#define TSADC_COMP_INT_DATA_MASK 0xfff
+#define TSADC_COMP_SHUT_DATA_MASK 0xfff
+#define TSADC_TEMP_INT_EN 0
+#define TSADC_TEMP_SHUT_EN 1
+static int tsadc_ht_temp;
+static int tsadc_ht_reset_cru;
+static int tsadc_ht_pull_gpio;
+
+struct tsadc_port {
+ struct pinctrl *pctl;
+ struct pinctrl_state *pins_default;
+ struct pinctrl_state *pins_tsadc_int;
+};
+
+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);
+ if (tsadc_ht_pull_gpio)
+ tsadc_writel(ret | (0xf << (chn + 4)), TSADC_INT_EN);
+ else if (tsadc_ht_reset_cru)
+ tsadc_writel(ret | (0xf << (chn + 8)), TSADC_INT_EN);
+ }
+
+}
+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 > 4)
+ 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(3 << (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_ht_temp, 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)
+{
+ int i;
+ *temp = 0;
+ *code = 0;
+
+ if (!g_dev || chn > 4){
+ *temp = INVALID_TEMP;
+ return ;
+ }
+#if 0
+ 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) {
+ *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);
+#else
+ *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);
+ }
+#endif
+}
+
+ 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;
+ struct tsadc_port *uap;
+
+ 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;
+
+ if (of_property_read_u32(np, "tsadc-ht-temp",
+ &tsadc_ht_temp)) {
+ dev_err(&data->pdev->dev, "Missing tsadc_ht_temp in the DT.\n");
+ return -EPERM;
+ }
+ if (of_property_read_u32(np, "tsadc-ht-reset-cru",
+ &tsadc_ht_reset_cru)) {
+ dev_err(&data->pdev->dev, "Missing tsadc_ht_reset_cru in the DT.\n");
+ return -EPERM;
+ }
+ if (of_property_read_u32(np, "tsadc-ht-pull-gpio",
+ &tsadc_ht_pull_gpio)) {
+ dev_err(&data->pdev->dev, "Missing tsadc_ht_pull_gpio in the DT.\n");
+ return -EPERM;
+ }
+
+ if (tsadc_ht_pull_gpio){
+ /*bit8=1 gpio0_b2 = 1 shutdown else gpio0_b2 =1 shutdown*/
+ /*
+ ret = tsadc_readl(TSADC_AUTO_CON);
+ tsadc_writel(ret | (1 << 8) , TSADC_AUTO_CON);
+ */
+ uap = devm_kzalloc(&data->pdev->dev, sizeof(struct tsadc_port),
+ GFP_KERNEL);
+ if (uap == NULL)
+ dev_err(&data->pdev->dev,
+ "uap is not set %s,line=%d\n", __func__, __LINE__);
+ uap->pctl = devm_pinctrl_get(&data->pdev->dev);
+ uap->pins_default = pinctrl_lookup_state(uap->pctl, "default");
+ uap->pins_tsadc_int = pinctrl_lookup_state(uap->pctl, "tsadc_int");
+ pinctrl_select_state(uap->pctl, uap->pins_tsadc_int);
+ }
+
+ rockchip_tsadc_set_auto_temp(1);
+
+ 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;
+
+ dev_info(&data->pdev->dev, "initialized\n");
+ return 0;
+}
+EXPORT_SYMBOL(rockchip_hwmon_init);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("zhangqing <zhangqing@rock-chips.com>");
+MODULE_DESCRIPTION("Driver for TSADC");