rk3288:tsadc:support rockchip hwmon tsadc

This commit is contained in:
张晴
2014-03-24 15:55:46 +08:00
parent 7e266f3121
commit d3f6f7f47c
7 changed files with 1022 additions and 0 deletions

View File

@@ -1044,6 +1044,18 @@
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";
};
};

View File

@@ -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

View 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

View 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

77
drivers/hwmon/hwmon-rockchip.h Executable file
View File

@@ -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 */

453
drivers/hwmon/rockchip-hwmon.c Executable file
View File

@@ -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");

469
drivers/hwmon/rockchip_tsadc.c Executable file
View File

@@ -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");