summaryrefslogtreecommitdiff
path: root/arch/arm/mach-tegra/tegra3_tsensor.c
blob: 7a1c122099bc11d499af3e2808c9f2ff9147f9a0 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
/*
 * arch/arm/mach-tegra/tegra3_tsensor.c
 *
 * Copyright (C) 2011-2012 NVIDIA Corporation.
 *
 * This software is licensed under the terms of the GNU General Public
 * License version 2, as published by the Free Software Foundation, and
 * may be copied, distributed, and modified under those terms.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 */

#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/io.h>
#include <linux/ioport.h>
#include <linux/slab.h>

#include <mach/tsensor.h>
#include <mach/tegra_fuse.h>
#include <mach/iomap.h>
#include <mach/thermal.h>
#include <mach/tsensor.h>

#include "devices.h"
#include "tegra3_tsensor.h"

/* fuse revision constants used for tsensor */
#define TSENSOR_FUSE_REVISION_DECIMAL 8
#define TSENSOR_FUSE_REVISION_INTEGER 0

/* scratch register offsets needed for powering off PMU */
#define SCRATCH54_OFFSET			0x258
#define SCRATCH55_OFFSET			0x25C

/* scratch 54 register bit field offsets */
#define PMU_OFF_DATA_OFFSET			8

/* scratch 55 register bit field offsets */
#define RESET_TEGRA_OFFSET			31
#define CONTROLLER_TYPE_OFFSET			30
#define I2C_CONTROLLER_ID_OFFSET		27
#define PINMUX_OFFSET				24
#define CHECKSUM_OFFSET				16
#define PMU_16BIT_SUPPORT_OFFSET		15
/* scratch 55 register bit field masks */
#define RESET_TEGRA_MASK			0x1
#define CONTROLLER_TYPE_MASK			0x1
#define I2C_CONTROLLER_ID_MASK			0x7
#define PINMUX_MASK				0x7
#define CHECKSUM_MASK				0xff
#define PMU_16BIT_SUPPORT_MASK			0x1

#define TSENSOR_OFFSET	(4000 + 5000)

static int tsensor_get_temp(void *vdata, long *milli_temp)
{
	struct tegra_tsensor_data *data = vdata;
	return tsensor_thermal_get_temp(data, milli_temp);
}

static int tsensor_get_temp_low(void *vdata, long *milli_temp)
{
	struct tegra_tsensor_data *data = vdata;
	return tsensor_thermal_get_temp_low(data, milli_temp);
}

static int tsensor_set_limits(void *vdata,
			long lo_limit_milli,
			long hi_limit_milli)
{
	struct tegra_tsensor_data *data = vdata;
	return tsensor_thermal_set_limits(data,
					lo_limit_milli,
					hi_limit_milli);
}

static int tsensor_set_alert(void *vdata,
			void (*alert_func)(void *),
			void *alert_data)
{
	struct tegra_tsensor_data *data = vdata;
	return tsensor_thermal_set_alert(data, alert_func, alert_data);
}

static int tsensor_set_shutdown_temp(void *vdata, long shutdown_temp_milli)
{
	struct tegra_tsensor_data *data = vdata;
	return tsensor_thermal_set_shutdown_temp(data, shutdown_temp_milli);
}

static void tegra3_tsensor_probe_callback(struct tegra_tsensor_data *data)
{
	struct tegra_thermal_device *thermal_device;

	thermal_device = kzalloc(sizeof(struct tegra_thermal_device),
					GFP_KERNEL);

	if (!thermal_device) {
		pr_err("unable to allocate thermal device\n");
		return;
	}

	thermal_device->name = "tsensor";
	thermal_device->data = data;
	thermal_device->id = THERMAL_DEVICE_ID_TSENSOR;
	thermal_device->offset = TSENSOR_OFFSET;
	thermal_device->get_temp = tsensor_get_temp;
	thermal_device->get_temp_low = tsensor_get_temp_low;
	thermal_device->set_limits = tsensor_set_limits;
	thermal_device->set_alert = tsensor_set_alert;
	thermal_device->set_shutdown_temp = tsensor_set_shutdown_temp;

	/* This should not fail */
	if (tegra_thermal_device_register(thermal_device))
		BUG();
}

static struct tegra_tsensor_platform_data tsensor_data = {
	.probe_callback = tegra3_tsensor_probe_callback,
};

void __init tegra3_tsensor_init(struct tegra_tsensor_pmu_data *data)
{
	unsigned int reg;
	int err;
	u32 val, checksum;
	void __iomem *pMem = NULL;
	/* tsensor driver is instantiated based on fuse revision */
	err = tegra_fuse_get_revision(&reg);
	if (err)
		goto labelEnd;
	pr_info("\nTegra3 fuse revision %d ", reg);
	if (reg < TSENSOR_FUSE_REVISION_DECIMAL)
		goto labelEnd;

	if (!data)
		goto labelSkipPowerOff;

	if (!request_mem_region(TEGRA_PMC_BASE +
		SCRATCH54_OFFSET, 8, "tegra-tsensor"))
		pr_err(" [%s, line=%d]: Error mem busy\n",
			__func__, __LINE__);

	pMem = ioremap(TEGRA_PMC_BASE + SCRATCH54_OFFSET, 8);
	if (!pMem) {
		pr_err(" [%s, line=%d]: can't ioremap "
			"pmc iomem\n", __FILE__, __LINE__);
		goto labelEnd;
	}

	/*
	 * Fill scratch registers to power off the device
	 * in case if temperature crosses threshold TH3
	 */
	val = (data->poweroff_reg_data << PMU_OFF_DATA_OFFSET) |
		data->poweroff_reg_addr;
	writel(val, pMem);

	val = ((data->reset_tegra & RESET_TEGRA_MASK) << RESET_TEGRA_OFFSET) |
		((data->controller_type & CONTROLLER_TYPE_MASK) <<
		CONTROLLER_TYPE_OFFSET) |
		((data->i2c_controller_id & I2C_CONTROLLER_ID_MASK) <<
		I2C_CONTROLLER_ID_OFFSET) |
		((data->pinmux & PINMUX_MASK) << PINMUX_OFFSET) |
		((data->pmu_16bit_ops & PMU_16BIT_SUPPORT_MASK) <<
		PMU_16BIT_SUPPORT_OFFSET) | data->pmu_i2c_addr;

	checksum = data->poweroff_reg_addr +
		data->poweroff_reg_data + (val & 0xFF) +
		((val >> 8) & 0xFF) + ((val >> 24) & 0xFF);
	checksum &= 0xFF;
	checksum = 0x100 - checksum;

	val |= (checksum << CHECKSUM_OFFSET);
	writel(val, pMem + 4);

labelSkipPowerOff:
	/* set platform data for device before register */
	tegra_tsensor_device.dev.platform_data = &tsensor_data;
	platform_device_register(&tegra_tsensor_device);

labelEnd:
	return;
}