exynos_tmu.c 36.8 KB
Newer Older
D
Donggeun Kim 已提交
1
/*
2
 * exynos_tmu.c - Samsung EXYNOS TMU (Thermal Management Unit)
D
Donggeun Kim 已提交
3
 *
4 5 6 7
 *  Copyright (C) 2014 Samsung Electronics
 *  Bartlomiej Zolnierkiewicz <b.zolnierkie@samsung.com>
 *  Lukasz Majewski <l.majewski@samsung.com>
 *
D
Donggeun Kim 已提交
8 9
 *  Copyright (C) 2011 Samsung Electronics
 *  Donggeun Kim <dg77.kim@samsung.com>
10
 *  Amit Daniel Kachhap <amit.kachhap@linaro.org>
D
Donggeun Kim 已提交
11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * 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.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 *
 */

#include <linux/clk.h>
#include <linux/io.h>
30 31
#include <linux/interrupt.h>
#include <linux/module.h>
32
#include <linux/of.h>
33 34
#include <linux/of_address.h>
#include <linux/of_irq.h>
35
#include <linux/platform_device.h>
36
#include <linux/regulator/consumer.h>
37

38
#include "exynos_tmu.h"
39
#include "../thermal_core.h"
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

/* Exynos generic registers */
#define EXYNOS_TMU_REG_TRIMINFO		0x0
#define EXYNOS_TMU_REG_CONTROL		0x20
#define EXYNOS_TMU_REG_STATUS		0x28
#define EXYNOS_TMU_REG_CURRENT_TEMP	0x40
#define EXYNOS_TMU_REG_INTEN		0x70
#define EXYNOS_TMU_REG_INTSTAT		0x74
#define EXYNOS_TMU_REG_INTCLEAR		0x78

#define EXYNOS_TMU_TEMP_MASK		0xff
#define EXYNOS_TMU_REF_VOLTAGE_SHIFT	24
#define EXYNOS_TMU_REF_VOLTAGE_MASK	0x1f
#define EXYNOS_TMU_BUF_SLOPE_SEL_MASK	0xf
#define EXYNOS_TMU_BUF_SLOPE_SEL_SHIFT	8
#define EXYNOS_TMU_CORE_EN_SHIFT	0

/* Exynos3250 specific registers */
#define EXYNOS_TMU_TRIMINFO_CON1	0x10

/* Exynos4210 specific registers */
#define EXYNOS4210_TMU_REG_THRESHOLD_TEMP	0x44
#define EXYNOS4210_TMU_REG_TRIG_LEVEL0	0x50

/* Exynos5250, Exynos4412, Exynos3250 specific registers */
#define EXYNOS_TMU_TRIMINFO_CON2	0x14
#define EXYNOS_THD_TEMP_RISE		0x50
#define EXYNOS_THD_TEMP_FALL		0x54
#define EXYNOS_EMUL_CON		0x80

#define EXYNOS_TRIMINFO_RELOAD_ENABLE	1
#define EXYNOS_TRIMINFO_25_SHIFT	0
#define EXYNOS_TRIMINFO_85_SHIFT	8
#define EXYNOS_TMU_TRIP_MODE_SHIFT	13
#define EXYNOS_TMU_TRIP_MODE_MASK	0x7
#define EXYNOS_TMU_THERM_TRIP_EN_SHIFT	12

#define EXYNOS_TMU_INTEN_RISE0_SHIFT	0
#define EXYNOS_TMU_INTEN_RISE1_SHIFT	4
#define EXYNOS_TMU_INTEN_RISE2_SHIFT	8
#define EXYNOS_TMU_INTEN_RISE3_SHIFT	12
#define EXYNOS_TMU_INTEN_FALL0_SHIFT	16

#define EXYNOS_EMUL_TIME	0x57F0
#define EXYNOS_EMUL_TIME_MASK	0xffff
#define EXYNOS_EMUL_TIME_SHIFT	16
#define EXYNOS_EMUL_DATA_SHIFT	8
#define EXYNOS_EMUL_DATA_MASK	0xFF
#define EXYNOS_EMUL_ENABLE	0x1

/* Exynos5260 specific */
#define EXYNOS5260_TMU_REG_INTEN		0xC0
#define EXYNOS5260_TMU_REG_INTSTAT		0xC4
#define EXYNOS5260_TMU_REG_INTCLEAR		0xC8
#define EXYNOS5260_EMUL_CON			0x100

/* Exynos4412 specific */
#define EXYNOS4412_MUX_ADDR_VALUE          6
#define EXYNOS4412_MUX_ADDR_SHIFT          20

/*exynos5440 specific registers*/
#define EXYNOS5440_TMU_S0_7_TRIM		0x000
#define EXYNOS5440_TMU_S0_7_CTRL		0x020
#define EXYNOS5440_TMU_S0_7_DEBUG		0x040
#define EXYNOS5440_TMU_S0_7_TEMP		0x0f0
#define EXYNOS5440_TMU_S0_7_TH0			0x110
#define EXYNOS5440_TMU_S0_7_TH1			0x130
#define EXYNOS5440_TMU_S0_7_TH2			0x150
#define EXYNOS5440_TMU_S0_7_IRQEN		0x210
#define EXYNOS5440_TMU_S0_7_IRQ			0x230
/* exynos5440 common registers */
#define EXYNOS5440_TMU_IRQ_STATUS		0x000
#define EXYNOS5440_TMU_PMIN			0x004

#define EXYNOS5440_TMU_INTEN_RISE0_SHIFT	0
#define EXYNOS5440_TMU_INTEN_RISE1_SHIFT	1
#define EXYNOS5440_TMU_INTEN_RISE2_SHIFT	2
#define EXYNOS5440_TMU_INTEN_RISE3_SHIFT	3
#define EXYNOS5440_TMU_INTEN_FALL0_SHIFT	4
#define EXYNOS5440_TMU_TH_RISE4_SHIFT		24
#define EXYNOS5440_EFUSE_SWAP_OFFSET		8
121

122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141
/* Exynos7 specific registers */
#define EXYNOS7_THD_TEMP_RISE7_6		0x50
#define EXYNOS7_THD_TEMP_FALL7_6		0x60
#define EXYNOS7_TMU_REG_INTEN			0x110
#define EXYNOS7_TMU_REG_INTPEND			0x118
#define EXYNOS7_TMU_REG_EMUL_CON		0x160

#define EXYNOS7_TMU_TEMP_MASK			0x1ff
#define EXYNOS7_PD_DET_EN_SHIFT			23
#define EXYNOS7_TMU_INTEN_RISE0_SHIFT		0
#define EXYNOS7_TMU_INTEN_RISE1_SHIFT		1
#define EXYNOS7_TMU_INTEN_RISE2_SHIFT		2
#define EXYNOS7_TMU_INTEN_RISE3_SHIFT		3
#define EXYNOS7_TMU_INTEN_RISE4_SHIFT		4
#define EXYNOS7_TMU_INTEN_RISE5_SHIFT		5
#define EXYNOS7_TMU_INTEN_RISE6_SHIFT		6
#define EXYNOS7_TMU_INTEN_RISE7_SHIFT		7
#define EXYNOS7_EMUL_DATA_SHIFT			7
#define EXYNOS7_EMUL_DATA_MASK			0x1ff

142
#define MCELSIUS	1000
143 144 145 146 147 148
/**
 * struct exynos_tmu_data : A structure to hold the private data of the TMU
	driver
 * @id: identifier of the one instance of the TMU controller.
 * @pdata: pointer to the tmu platform/configuration data
 * @base: base address of the single instance of the TMU controller.
149
 * @base_second: base address of the common registers of the TMU controller.
150 151 152 153 154
 * @irq: irq number of the TMU controller.
 * @soc: id of the SOC type.
 * @irq_work: pointer to the irq work structure.
 * @lock: lock to implement synchronization.
 * @clk: pointer to the clock structure.
155
 * @clk_sec: pointer to the clock structure for accessing the base_second.
156
 * @sclk: pointer to the clock structure for accessing the tmu special clk.
157 158
 * @temp_error1: fused value of the first point trim.
 * @temp_error2: fused value of the second point trim.
159
 * @regulator: pointer to the TMU regulator structure.
160
 * @reg_conf: pointer to structure to register with core thermal.
161
 * @tmu_initialize: SoC specific TMU initialization method
162
 * @tmu_control: SoC specific TMU control method
163
 * @tmu_read: SoC specific TMU temperature read method
164
 * @tmu_set_emulation: SoC specific TMU emulation setting method
165
 * @tmu_clear_irqs: SoC specific TMU interrupts clearing method
166
 */
167
struct exynos_tmu_data {
168
	int id;
169
	struct exynos_tmu_platform_data *pdata;
D
Donggeun Kim 已提交
170
	void __iomem *base;
171
	void __iomem *base_second;
D
Donggeun Kim 已提交
172
	int irq;
173
	enum soc_type soc;
D
Donggeun Kim 已提交
174 175
	struct work_struct irq_work;
	struct mutex lock;
176 177
	struct clk *clk, *clk_sec, *sclk;
	u16 temp_error1, temp_error2;
178
	struct regulator *regulator;
179 180
	struct thermal_zone_device *tzd;

181
	int (*tmu_initialize)(struct platform_device *pdev);
182
	void (*tmu_control)(struct platform_device *pdev, bool on);
183
	int (*tmu_read)(struct exynos_tmu_data *data);
184 185
	void (*tmu_set_emulation)(struct exynos_tmu_data *data,
				  unsigned long temp);
186
	void (*tmu_clear_irqs)(struct exynos_tmu_data *data);
D
Donggeun Kim 已提交
187 188
};

189 190 191 192 193 194 195
static void exynos_report_trigger(struct exynos_tmu_data *p)
{
	char data[10], *envp[] = { data, NULL };
	struct thermal_zone_device *tz = p->tzd;
	unsigned long temp;
	unsigned int i;

196 197
	if (!tz) {
		pr_err("No thermal zone device defined\n");
198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215
		return;
	}

	thermal_zone_device_update(tz);

	mutex_lock(&tz->lock);
	/* Find the level for which trip happened */
	for (i = 0; i < of_thermal_get_ntrips(tz); i++) {
		tz->ops->get_trip_temp(tz, i, &temp);
		if (tz->last_temperature < temp)
			break;
	}

	snprintf(data, sizeof(data), "%u", i);
	kobject_uevent_env(&tz->device.kobj, KOBJ_CHANGE, envp);
	mutex_unlock(&tz->lock);
}

D
Donggeun Kim 已提交
216 217 218 219
/*
 * TMU treats temperature as a mapped temperature code.
 * The temperature is converted differently depending on the calibration type.
 */
220
static int temp_to_code(struct exynos_tmu_data *data, u8 temp)
D
Donggeun Kim 已提交
221
{
222
	struct exynos_tmu_platform_data *pdata = data->pdata;
D
Donggeun Kim 已提交
223 224 225 226
	int temp_code;

	switch (pdata->cal_type) {
	case TYPE_TWO_POINT_TRIMMING:
227 228 229 230
		temp_code = (temp - pdata->first_point_trim) *
			(data->temp_error2 - data->temp_error1) /
			(pdata->second_point_trim - pdata->first_point_trim) +
			data->temp_error1;
D
Donggeun Kim 已提交
231 232
		break;
	case TYPE_ONE_POINT_TRIMMING:
233
		temp_code = temp + data->temp_error1 - pdata->first_point_trim;
D
Donggeun Kim 已提交
234 235
		break;
	default:
236
		temp_code = temp + pdata->default_temp_offset;
D
Donggeun Kim 已提交
237 238
		break;
	}
239

D
Donggeun Kim 已提交
240 241 242 243 244 245 246
	return temp_code;
}

/*
 * Calculate a temperature value from a temperature code.
 * The unit of the temperature is degree Celsius.
 */
247
static int code_to_temp(struct exynos_tmu_data *data, u16 temp_code)
D
Donggeun Kim 已提交
248
{
249
	struct exynos_tmu_platform_data *pdata = data->pdata;
D
Donggeun Kim 已提交
250 251 252 253
	int temp;

	switch (pdata->cal_type) {
	case TYPE_TWO_POINT_TRIMMING:
254 255 256 257
		temp = (temp_code - data->temp_error1) *
			(pdata->second_point_trim - pdata->first_point_trim) /
			(data->temp_error2 - data->temp_error1) +
			pdata->first_point_trim;
D
Donggeun Kim 已提交
258 259
		break;
	case TYPE_ONE_POINT_TRIMMING:
260
		temp = temp_code - data->temp_error1 + pdata->first_point_trim;
D
Donggeun Kim 已提交
261 262
		break;
	default:
263
		temp = temp_code - pdata->default_temp_offset;
D
Donggeun Kim 已提交
264 265
		break;
	}
266

D
Donggeun Kim 已提交
267 268 269
	return temp;
}

270
static void sanitize_temp_error(struct exynos_tmu_data *data, u32 trim_info)
D
Donggeun Kim 已提交
271
{
272
	struct exynos_tmu_platform_data *pdata = data->pdata;
D
Donggeun Kim 已提交
273

274
	data->temp_error1 = trim_info & EXYNOS_TMU_TEMP_MASK;
275
	data->temp_error2 = ((trim_info >> EXYNOS_TRIMINFO_85_SHIFT) &
276
				EXYNOS_TMU_TEMP_MASK);
277

278 279 280 281 282 283 284
	if (!data->temp_error1 ||
		(pdata->min_efuse_value > data->temp_error1) ||
		(data->temp_error1 > pdata->max_efuse_value))
		data->temp_error1 = pdata->efuse_value & EXYNOS_TMU_TEMP_MASK;

	if (!data->temp_error2)
		data->temp_error2 =
285
			(pdata->efuse_value >> EXYNOS_TRIMINFO_85_SHIFT) &
286
			EXYNOS_TMU_TEMP_MASK;
287
}
288

289 290
static u32 get_th_reg(struct exynos_tmu_data *data, u32 threshold, bool falling)
{
291 292 293 294
	struct thermal_zone_device *tz = data->tzd;
	const struct thermal_trip * const trips =
		of_thermal_get_trip_points(tz);
	unsigned long temp;
295
	int i;
296

297 298 299 300 301
	if (!trips) {
		pr_err("%s: Cannot get trip points from of-thermal.c!\n",
		       __func__);
		return 0;
	}
302

303 304 305 306 307
	for (i = 0; i < of_thermal_get_ntrips(tz); i++) {
		if (trips[i].type == THERMAL_TRIP_CRITICAL)
			continue;

		temp = trips[i].temperature / MCELSIUS;
308
		if (falling)
309
			temp -= (trips[i].hysteresis / MCELSIUS);
310 311
		else
			threshold &= ~(0xff << 8 * i);
312

313
		threshold |= temp_to_code(data, temp) << 8 * i;
D
Donggeun Kim 已提交
314
	}
315 316 317 318

	return threshold;
}

319
static int exynos_tmu_initialize(struct platform_device *pdev)
D
Donggeun Kim 已提交
320
{
321
	struct exynos_tmu_data *data = platform_get_drvdata(pdev);
322
	int ret;
D
Donggeun Kim 已提交
323 324 325

	mutex_lock(&data->lock);
	clk_enable(data->clk);
326 327
	if (!IS_ERR(data->clk_sec))
		clk_enable(data->clk_sec);
328
	ret = data->tmu_initialize(pdev);
D
Donggeun Kim 已提交
329 330
	clk_disable(data->clk);
	mutex_unlock(&data->lock);
331 332
	if (!IS_ERR(data->clk_sec))
		clk_disable(data->clk_sec);
D
Donggeun Kim 已提交
333 334 335 336

	return ret;
}

337
static u32 get_con_reg(struct exynos_tmu_data *data, u32 con)
D
Donggeun Kim 已提交
338
{
339
	struct exynos_tmu_platform_data *pdata = data->pdata;
D
Donggeun Kim 已提交
340

341 342 343
	if (data->soc == SOC_ARCH_EXYNOS4412 ||
	    data->soc == SOC_ARCH_EXYNOS3250)
		con |= (EXYNOS4412_MUX_ADDR_VALUE << EXYNOS4412_MUX_ADDR_SHIFT);
344

345 346
	con &= ~(EXYNOS_TMU_REF_VOLTAGE_MASK << EXYNOS_TMU_REF_VOLTAGE_SHIFT);
	con |= pdata->reference_voltage << EXYNOS_TMU_REF_VOLTAGE_SHIFT;
347

348 349
	con &= ~(EXYNOS_TMU_BUF_SLOPE_SEL_MASK << EXYNOS_TMU_BUF_SLOPE_SEL_SHIFT);
	con |= (pdata->gain << EXYNOS_TMU_BUF_SLOPE_SEL_SHIFT);
350 351

	if (pdata->noise_cancel_mode) {
352 353
		con &= ~(EXYNOS_TMU_TRIP_MODE_MASK << EXYNOS_TMU_TRIP_MODE_SHIFT);
		con |= (pdata->noise_cancel_mode << EXYNOS_TMU_TRIP_MODE_SHIFT);
354 355
	}

356 357 358 359 360 361
	return con;
}

static void exynos_tmu_control(struct platform_device *pdev, bool on)
{
	struct exynos_tmu_data *data = platform_get_drvdata(pdev);
D
Donggeun Kim 已提交
362

363 364
	mutex_lock(&data->lock);
	clk_enable(data->clk);
365
	data->tmu_control(pdev, on);
D
Donggeun Kim 已提交
366 367 368 369
	clk_disable(data->clk);
	mutex_unlock(&data->lock);
}

370
static int exynos4210_tmu_initialize(struct platform_device *pdev)
D
Donggeun Kim 已提交
371
{
372
	struct exynos_tmu_data *data = platform_get_drvdata(pdev);
373 374 375
	struct thermal_zone_device *tz = data->tzd;
	const struct thermal_trip * const trips =
		of_thermal_get_trip_points(tz);
376
	int ret = 0, threshold_code, i;
377 378 379 380 381 382 383 384 385
	unsigned long reference, temp;
	unsigned int status;

	if (!trips) {
		pr_err("%s: Cannot get trip points from of-thermal.c!\n",
		       __func__);
		ret = -ENODEV;
		goto out;
	}
D
Donggeun Kim 已提交
386

387 388 389 390 391
	status = readb(data->base + EXYNOS_TMU_REG_STATUS);
	if (!status) {
		ret = -EBUSY;
		goto out;
	}
D
Donggeun Kim 已提交
392

393
	sanitize_temp_error(data, readl(data->base + EXYNOS_TMU_REG_TRIMINFO));
D
Donggeun Kim 已提交
394

395
	/* Write temperature code for threshold */
396 397 398 399 400 401
	reference = trips[0].temperature / MCELSIUS;
	threshold_code = temp_to_code(data, reference);
	if (threshold_code < 0) {
		ret = threshold_code;
		goto out;
	}
402 403
	writeb(threshold_code, data->base + EXYNOS4210_TMU_REG_THRESHOLD_TEMP);

404 405 406
	for (i = 0; i < of_thermal_get_ntrips(tz); i++) {
		temp = trips[i].temperature / MCELSIUS;
		writeb(temp - reference, data->base +
407
		       EXYNOS4210_TMU_REG_TRIG_LEVEL0 + i * 4);
408
	}
409

410
	data->tmu_clear_irqs(data);
411 412 413 414 415 416 417
out:
	return ret;
}

static int exynos4412_tmu_initialize(struct platform_device *pdev)
{
	struct exynos_tmu_data *data = platform_get_drvdata(pdev);
418 419
	const struct thermal_trip * const trips =
		of_thermal_get_trip_points(data->tzd);
420 421
	unsigned int status, trim_info, con, ctrl, rising_threshold;
	int ret = 0, threshold_code, i;
422
	unsigned long crit_temp = 0;
423 424 425 426 427 428 429 430 431 432 433 434 435 436

	status = readb(data->base + EXYNOS_TMU_REG_STATUS);
	if (!status) {
		ret = -EBUSY;
		goto out;
	}

	if (data->soc == SOC_ARCH_EXYNOS3250 ||
	    data->soc == SOC_ARCH_EXYNOS4412 ||
	    data->soc == SOC_ARCH_EXYNOS5250) {
		if (data->soc == SOC_ARCH_EXYNOS3250) {
			ctrl = readl(data->base + EXYNOS_TMU_TRIMINFO_CON1);
			ctrl |= EXYNOS_TRIMINFO_RELOAD_ENABLE;
			writel(ctrl, data->base + EXYNOS_TMU_TRIMINFO_CON1);
437
		}
438 439 440 441
		ctrl = readl(data->base + EXYNOS_TMU_TRIMINFO_CON2);
		ctrl |= EXYNOS_TRIMINFO_RELOAD_ENABLE;
		writel(ctrl, data->base + EXYNOS_TMU_TRIMINFO_CON2);
	}
442

443 444 445 446 447 448 449 450 451 452 453 454 455 456
	/* On exynos5420 the triminfo register is in the shared space */
	if (data->soc == SOC_ARCH_EXYNOS5420_TRIMINFO)
		trim_info = readl(data->base_second + EXYNOS_TMU_REG_TRIMINFO);
	else
		trim_info = readl(data->base + EXYNOS_TMU_REG_TRIMINFO);

	sanitize_temp_error(data, trim_info);

	/* Write temperature code for rising and falling threshold */
	rising_threshold = readl(data->base + EXYNOS_THD_TEMP_RISE);
	rising_threshold = get_th_reg(data, rising_threshold, false);
	writel(rising_threshold, data->base + EXYNOS_THD_TEMP_RISE);
	writel(get_th_reg(data, 0, true), data->base + EXYNOS_THD_TEMP_FALL);

457
	data->tmu_clear_irqs(data);
458 459

	/* if last threshold limit is also present */
460 461 462 463 464
	for (i = 0; i < of_thermal_get_ntrips(data->tzd); i++) {
		if (trips[i].type == THERMAL_TRIP_CRITICAL) {
			crit_temp = trips[i].temperature;
			break;
		}
465
	}
466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482

	if (i == of_thermal_get_ntrips(data->tzd)) {
		pr_err("%s: No CRITICAL trip point defined at of-thermal.c!\n",
		       __func__);
		ret = -EINVAL;
		goto out;
	}

	threshold_code = temp_to_code(data, crit_temp / MCELSIUS);
	/* 1-4 level to be assigned in th0 reg */
	rising_threshold &= ~(0xff << 8 * i);
	rising_threshold |= threshold_code << 8 * i;
	writel(rising_threshold, data->base + EXYNOS_THD_TEMP_RISE);
	con = readl(data->base + EXYNOS_TMU_REG_CONTROL);
	con |= (1 << EXYNOS_TMU_THERM_TRIP_EN_SHIFT);
	writel(con, data->base + EXYNOS_TMU_REG_CONTROL);

483
out:
484 485
	return ret;
}
D
Donggeun Kim 已提交
486

487 488 489 490
static int exynos5440_tmu_initialize(struct platform_device *pdev)
{
	struct exynos_tmu_data *data = platform_get_drvdata(pdev);
	unsigned int trim_info = 0, con, rising_threshold;
491 492
	int ret = 0, threshold_code;
	unsigned long crit_temp = 0;
493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517

	/*
	 * For exynos5440 soc triminfo value is swapped between TMU0 and
	 * TMU2, so the below logic is needed.
	 */
	switch (data->id) {
	case 0:
		trim_info = readl(data->base + EXYNOS5440_EFUSE_SWAP_OFFSET +
				 EXYNOS5440_TMU_S0_7_TRIM);
		break;
	case 1:
		trim_info = readl(data->base + EXYNOS5440_TMU_S0_7_TRIM);
		break;
	case 2:
		trim_info = readl(data->base - EXYNOS5440_EFUSE_SWAP_OFFSET +
				  EXYNOS5440_TMU_S0_7_TRIM);
	}
	sanitize_temp_error(data, trim_info);

	/* Write temperature code for rising and falling threshold */
	rising_threshold = readl(data->base + EXYNOS5440_TMU_S0_7_TH0);
	rising_threshold = get_th_reg(data, rising_threshold, false);
	writel(rising_threshold, data->base + EXYNOS5440_TMU_S0_7_TH0);
	writel(0, data->base + EXYNOS5440_TMU_S0_7_TH1);

518
	data->tmu_clear_irqs(data);
519 520

	/* if last threshold limit is also present */
521 522
	if (!data->tzd->ops->get_crit_temp(data->tzd, &crit_temp)) {
		threshold_code = temp_to_code(data, crit_temp / MCELSIUS);
523 524 525 526 527 528 529 530 531 532 533 534
		/* 5th level to be assigned in th2 reg */
		rising_threshold =
			threshold_code << EXYNOS5440_TMU_TH_RISE4_SHIFT;
		writel(rising_threshold, data->base + EXYNOS5440_TMU_S0_7_TH2);
		con = readl(data->base + EXYNOS5440_TMU_S0_7_CTRL);
		con |= (1 << EXYNOS_TMU_THERM_TRIP_EN_SHIFT);
		writel(con, data->base + EXYNOS5440_TMU_S0_7_CTRL);
	}
	/* Clear the PMIN in the common TMU register */
	if (!data->id)
		writel(0, data->base_second + EXYNOS5440_TMU_PMIN);
	return ret;
D
Donggeun Kim 已提交
535 536
}

537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614
static int exynos7_tmu_initialize(struct platform_device *pdev)
{
	struct exynos_tmu_data *data = platform_get_drvdata(pdev);
	struct thermal_zone_device *tz = data->tzd;
	struct exynos_tmu_platform_data *pdata = data->pdata;
	unsigned int status, trim_info;
	unsigned int rising_threshold = 0, falling_threshold = 0;
	int ret = 0, threshold_code, i;
	unsigned long temp, temp_hist;
	unsigned int reg_off, bit_off;

	status = readb(data->base + EXYNOS_TMU_REG_STATUS);
	if (!status) {
		ret = -EBUSY;
		goto out;
	}

	trim_info = readl(data->base + EXYNOS_TMU_REG_TRIMINFO);

	data->temp_error1 = trim_info & EXYNOS7_TMU_TEMP_MASK;
	if (!data->temp_error1 ||
	    (pdata->min_efuse_value > data->temp_error1) ||
	    (data->temp_error1 > pdata->max_efuse_value))
		data->temp_error1 = pdata->efuse_value & EXYNOS_TMU_TEMP_MASK;

	/* Write temperature code for rising and falling threshold */
	for (i = (of_thermal_get_ntrips(tz) - 1); i >= 0; i--) {
		/*
		 * On exynos7 there are 4 rising and 4 falling threshold
		 * registers (0x50-0x5c and 0x60-0x6c respectively). Each
		 * register holds the value of two threshold levels (at bit
		 * offsets 0 and 16). Based on the fact that there are atmost
		 * eight possible trigger levels, calculate the register and
		 * bit offsets where the threshold levels are to be written.
		 *
		 * e.g. EXYNOS7_THD_TEMP_RISE7_6 (0x50)
		 * [24:16] - Threshold level 7
		 * [8:0] - Threshold level 6
		 * e.g. EXYNOS7_THD_TEMP_RISE5_4 (0x54)
		 * [24:16] - Threshold level 5
		 * [8:0] - Threshold level 4
		 *
		 * and similarly for falling thresholds.
		 *
		 * Based on the above, calculate the register and bit offsets
		 * for rising/falling threshold levels and populate them.
		 */
		reg_off = ((7 - i) / 2) * 4;
		bit_off = ((8 - i) % 2);

		tz->ops->get_trip_temp(tz, i, &temp);
		temp /= MCELSIUS;

		tz->ops->get_trip_hyst(tz, i, &temp_hist);
		temp_hist = temp - (temp_hist / MCELSIUS);

		/* Set 9-bit temperature code for rising threshold levels */
		threshold_code = temp_to_code(data, temp);
		rising_threshold = readl(data->base +
			EXYNOS7_THD_TEMP_RISE7_6 + reg_off);
		rising_threshold &= ~(EXYNOS7_TMU_TEMP_MASK << (16 * bit_off));
		rising_threshold |= threshold_code << (16 * bit_off);
		writel(rising_threshold,
		       data->base + EXYNOS7_THD_TEMP_RISE7_6 + reg_off);

		/* Set 9-bit temperature code for falling threshold levels */
		threshold_code = temp_to_code(data, temp_hist);
		falling_threshold &= ~(EXYNOS7_TMU_TEMP_MASK << (16 * bit_off));
		falling_threshold |= threshold_code << (16 * bit_off);
		writel(falling_threshold,
		       data->base + EXYNOS7_THD_TEMP_FALL7_6 + reg_off);
	}

	data->tmu_clear_irqs(data);
out:
	return ret;
}

615
static void exynos4210_tmu_control(struct platform_device *pdev, bool on)
616
{
617
	struct exynos_tmu_data *data = platform_get_drvdata(pdev);
618
	struct thermal_zone_device *tz = data->tzd;
619
	unsigned int con, interrupt_en;
620

621
	con = get_con_reg(data, readl(data->base + EXYNOS_TMU_REG_CONTROL));
622

623 624 625
	if (on) {
		con |= (1 << EXYNOS_TMU_CORE_EN_SHIFT);
		interrupt_en =
626 627 628 629 630 631 632 633 634
			(of_thermal_is_trip_valid(tz, 3)
			 << EXYNOS_TMU_INTEN_RISE3_SHIFT) |
			(of_thermal_is_trip_valid(tz, 2)
			 << EXYNOS_TMU_INTEN_RISE2_SHIFT) |
			(of_thermal_is_trip_valid(tz, 1)
			 << EXYNOS_TMU_INTEN_RISE1_SHIFT) |
			(of_thermal_is_trip_valid(tz, 0)
			 << EXYNOS_TMU_INTEN_RISE0_SHIFT);

635
		if (data->soc != SOC_ARCH_EXYNOS4210)
636 637 638 639 640 641 642 643 644 645 646 647 648
			interrupt_en |=
				interrupt_en << EXYNOS_TMU_INTEN_FALL0_SHIFT;
	} else {
		con &= ~(1 << EXYNOS_TMU_CORE_EN_SHIFT);
		interrupt_en = 0; /* Disable all interrupts */
	}
	writel(interrupt_en, data->base + EXYNOS_TMU_REG_INTEN);
	writel(con, data->base + EXYNOS_TMU_REG_CONTROL);
}

static void exynos5440_tmu_control(struct platform_device *pdev, bool on)
{
	struct exynos_tmu_data *data = platform_get_drvdata(pdev);
649
	struct thermal_zone_device *tz = data->tzd;
650 651 652 653 654 655 656
	unsigned int con, interrupt_en;

	con = get_con_reg(data, readl(data->base + EXYNOS5440_TMU_S0_7_CTRL));

	if (on) {
		con |= (1 << EXYNOS_TMU_CORE_EN_SHIFT);
		interrupt_en =
657 658 659 660 661 662 663 664 665 666
			(of_thermal_is_trip_valid(tz, 3)
			 << EXYNOS5440_TMU_INTEN_RISE3_SHIFT) |
			(of_thermal_is_trip_valid(tz, 2)
			 << EXYNOS5440_TMU_INTEN_RISE2_SHIFT) |
			(of_thermal_is_trip_valid(tz, 1)
			 << EXYNOS5440_TMU_INTEN_RISE1_SHIFT) |
			(of_thermal_is_trip_valid(tz, 0)
			 << EXYNOS5440_TMU_INTEN_RISE0_SHIFT);
		interrupt_en |=
			interrupt_en << EXYNOS5440_TMU_INTEN_FALL0_SHIFT;
667 668 669 670 671 672 673 674
	} else {
		con &= ~(1 << EXYNOS_TMU_CORE_EN_SHIFT);
		interrupt_en = 0; /* Disable all interrupts */
	}
	writel(interrupt_en, data->base + EXYNOS5440_TMU_S0_7_IRQEN);
	writel(con, data->base + EXYNOS5440_TMU_S0_7_CTRL);
}

675 676 677 678 679 680 681 682 683 684
static void exynos7_tmu_control(struct platform_device *pdev, bool on)
{
	struct exynos_tmu_data *data = platform_get_drvdata(pdev);
	struct thermal_zone_device *tz = data->tzd;
	unsigned int con, interrupt_en;

	con = get_con_reg(data, readl(data->base + EXYNOS_TMU_REG_CONTROL));

	if (on) {
		con |= (1 << EXYNOS_TMU_CORE_EN_SHIFT);
685
		con |= (1 << EXYNOS7_PD_DET_EN_SHIFT);
686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707
		interrupt_en =
			(of_thermal_is_trip_valid(tz, 7)
			<< EXYNOS7_TMU_INTEN_RISE7_SHIFT) |
			(of_thermal_is_trip_valid(tz, 6)
			<< EXYNOS7_TMU_INTEN_RISE6_SHIFT) |
			(of_thermal_is_trip_valid(tz, 5)
			<< EXYNOS7_TMU_INTEN_RISE5_SHIFT) |
			(of_thermal_is_trip_valid(tz, 4)
			<< EXYNOS7_TMU_INTEN_RISE4_SHIFT) |
			(of_thermal_is_trip_valid(tz, 3)
			<< EXYNOS7_TMU_INTEN_RISE3_SHIFT) |
			(of_thermal_is_trip_valid(tz, 2)
			<< EXYNOS7_TMU_INTEN_RISE2_SHIFT) |
			(of_thermal_is_trip_valid(tz, 1)
			<< EXYNOS7_TMU_INTEN_RISE1_SHIFT) |
			(of_thermal_is_trip_valid(tz, 0)
			<< EXYNOS7_TMU_INTEN_RISE0_SHIFT);

		interrupt_en |=
			interrupt_en << EXYNOS_TMU_INTEN_FALL0_SHIFT;
	} else {
		con &= ~(1 << EXYNOS_TMU_CORE_EN_SHIFT);
708
		con &= ~(1 << EXYNOS7_PD_DET_EN_SHIFT);
709 710 711 712 713 714 715
		interrupt_en = 0; /* Disable all interrupts */
	}

	writel(interrupt_en, data->base + EXYNOS7_TMU_REG_INTEN);
	writel(con, data->base + EXYNOS_TMU_REG_CONTROL);
}

716
static int exynos_get_temp(void *p, long *temp)
D
Donggeun Kim 已提交
717
{
718 719
	struct exynos_tmu_data *data = p;

720
	if (!data || !data->tmu_read)
721
		return -EINVAL;
722 723 724

	mutex_lock(&data->lock);
	clk_enable(data->clk);
725 726 727

	*temp = code_to_temp(data, data->tmu_read(data)) * MCELSIUS;

D
Donggeun Kim 已提交
728 729
	clk_disable(data->clk);
	mutex_unlock(&data->lock);
730

731
	return 0;
D
Donggeun Kim 已提交
732
}
733 734

#ifdef CONFIG_THERMAL_EMULATION
735 736 737
static u32 get_emul_con_reg(struct exynos_tmu_data *data, unsigned int val,
			    unsigned long temp)
{
738 739 740
	if (temp) {
		temp /= MCELSIUS;

741
		if (data->soc != SOC_ARCH_EXYNOS5440) {
742 743
			val &= ~(EXYNOS_EMUL_TIME_MASK << EXYNOS_EMUL_TIME_SHIFT);
			val |= (EXYNOS_EMUL_TIME << EXYNOS_EMUL_TIME_SHIFT);
744
		}
745 746 747 748 749 750 751 752 753 754 755 756 757
		if (data->soc == SOC_ARCH_EXYNOS7) {
			val &= ~(EXYNOS7_EMUL_DATA_MASK <<
				EXYNOS7_EMUL_DATA_SHIFT);
			val |= (temp_to_code(data, temp) <<
				EXYNOS7_EMUL_DATA_SHIFT) |
				EXYNOS_EMUL_ENABLE;
		} else {
			val &= ~(EXYNOS_EMUL_DATA_MASK <<
				EXYNOS_EMUL_DATA_SHIFT);
			val |= (temp_to_code(data, temp) <<
				EXYNOS_EMUL_DATA_SHIFT) |
				EXYNOS_EMUL_ENABLE;
		}
758
	} else {
759
		val &= ~EXYNOS_EMUL_ENABLE;
760 761
	}

762 763 764
	return val;
}

765 766 767 768 769 770 771 772
static void exynos4412_tmu_set_emulation(struct exynos_tmu_data *data,
					 unsigned long temp)
{
	unsigned int val;
	u32 emul_con;

	if (data->soc == SOC_ARCH_EXYNOS5260)
		emul_con = EXYNOS5260_EMUL_CON;
773 774
	else if (data->soc == SOC_ARCH_EXYNOS7)
		emul_con = EXYNOS7_TMU_REG_EMUL_CON;
775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792
	else
		emul_con = EXYNOS_EMUL_CON;

	val = readl(data->base + emul_con);
	val = get_emul_con_reg(data, val, temp);
	writel(val, data->base + emul_con);
}

static void exynos5440_tmu_set_emulation(struct exynos_tmu_data *data,
					 unsigned long temp)
{
	unsigned int val;

	val = readl(data->base + EXYNOS5440_TMU_S0_7_DEBUG);
	val = get_emul_con_reg(data, val, temp);
	writel(val, data->base + EXYNOS5440_TMU_S0_7_DEBUG);
}

793 794 795 796 797
static int exynos_tmu_set_emulation(void *drv_data, unsigned long temp)
{
	struct exynos_tmu_data *data = drv_data;
	int ret = -EINVAL;

798
	if (data->soc == SOC_ARCH_EXYNOS4210)
799 800 801 802 803 804 805
		goto out;

	if (temp && temp < MCELSIUS)
		goto out;

	mutex_lock(&data->lock);
	clk_enable(data->clk);
806
	data->tmu_set_emulation(data, temp);
807 808 809 810 811 812 813
	clk_disable(data->clk);
	mutex_unlock(&data->lock);
	return 0;
out:
	return ret;
}
#else
814 815
#define exynos4412_tmu_set_emulation NULL
#define exynos5440_tmu_set_emulation NULL
816 817
static int exynos_tmu_set_emulation(void *drv_data,	unsigned long temp)
	{ return -EINVAL; }
818
#endif /* CONFIG_THERMAL_EMULATION */
819

820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837
static int exynos4210_tmu_read(struct exynos_tmu_data *data)
{
	int ret = readb(data->base + EXYNOS_TMU_REG_CURRENT_TEMP);

	/* "temp_code" should range between 75 and 175 */
	return (ret < 75 || ret > 175) ? -ENODATA : ret;
}

static int exynos4412_tmu_read(struct exynos_tmu_data *data)
{
	return readb(data->base + EXYNOS_TMU_REG_CURRENT_TEMP);
}

static int exynos5440_tmu_read(struct exynos_tmu_data *data)
{
	return readb(data->base + EXYNOS5440_TMU_S0_7_TEMP);
}

838 839 840 841 842 843
static int exynos7_tmu_read(struct exynos_tmu_data *data)
{
	return readw(data->base + EXYNOS_TMU_REG_CURRENT_TEMP) &
		EXYNOS7_TMU_TEMP_MASK;
}

844
static void exynos_tmu_work(struct work_struct *work)
D
Donggeun Kim 已提交
845
{
846 847
	struct exynos_tmu_data *data = container_of(work,
			struct exynos_tmu_data, irq_work);
848
	unsigned int val_type;
849

850 851
	if (!IS_ERR(data->clk_sec))
		clk_enable(data->clk_sec);
852
	/* Find which sensor generated this interrupt */
853 854
	if (data->soc == SOC_ARCH_EXYNOS5440) {
		val_type = readl(data->base_second + EXYNOS5440_TMU_IRQ_STATUS);
855 856 857
		if (!((val_type >> data->id) & 0x1))
			goto out;
	}
858 859
	if (!IS_ERR(data->clk_sec))
		clk_disable(data->clk_sec);
D
Donggeun Kim 已提交
860

861
	exynos_report_trigger(data);
D
Donggeun Kim 已提交
862 863
	mutex_lock(&data->lock);
	clk_enable(data->clk);
864

865
	/* TODO: take action based on particular interrupt */
866
	data->tmu_clear_irqs(data);
867

D
Donggeun Kim 已提交
868 869
	clk_disable(data->clk);
	mutex_unlock(&data->lock);
870
out:
871
	enable_irq(data->irq);
D
Donggeun Kim 已提交
872 873
}

874 875 876 877 878 879 880 881
static void exynos4210_tmu_clear_irqs(struct exynos_tmu_data *data)
{
	unsigned int val_irq;
	u32 tmu_intstat, tmu_intclear;

	if (data->soc == SOC_ARCH_EXYNOS5260) {
		tmu_intstat = EXYNOS5260_TMU_REG_INTSTAT;
		tmu_intclear = EXYNOS5260_TMU_REG_INTCLEAR;
882 883 884
	} else if (data->soc == SOC_ARCH_EXYNOS7) {
		tmu_intstat = EXYNOS7_TMU_REG_INTPEND;
		tmu_intclear = EXYNOS7_TMU_REG_INTPEND;
885 886 887 888 889 890 891 892 893 894 895 896 897 898 899 900 901 902 903 904 905 906 907 908 909 910
	} else {
		tmu_intstat = EXYNOS_TMU_REG_INTSTAT;
		tmu_intclear = EXYNOS_TMU_REG_INTCLEAR;
	}

	val_irq = readl(data->base + tmu_intstat);
	/*
	 * Clear the interrupts.  Please note that the documentation for
	 * Exynos3250, Exynos4412, Exynos5250 and Exynos5260 incorrectly
	 * states that INTCLEAR register has a different placing of bits
	 * responsible for FALL IRQs than INTSTAT register.  Exynos5420
	 * and Exynos5440 documentation is correct (Exynos4210 doesn't
	 * support FALL IRQs at all).
	 */
	writel(val_irq, data->base + tmu_intclear);
}

static void exynos5440_tmu_clear_irqs(struct exynos_tmu_data *data)
{
	unsigned int val_irq;

	val_irq = readl(data->base + EXYNOS5440_TMU_S0_7_IRQ);
	/* clear the interrupts */
	writel(val_irq, data->base + EXYNOS5440_TMU_S0_7_IRQ);
}

911
static irqreturn_t exynos_tmu_irq(int irq, void *id)
D
Donggeun Kim 已提交
912
{
913
	struct exynos_tmu_data *data = id;
D
Donggeun Kim 已提交
914 915 916 917 918 919

	disable_irq_nosync(irq);
	schedule_work(&data->irq_work);

	return IRQ_HANDLED;
}
920 921

static const struct of_device_id exynos_tmu_match[] = {
922 923 924 925 926 927 928 929 930 931
	{ .compatible = "samsung,exynos3250-tmu", },
	{ .compatible = "samsung,exynos4210-tmu", },
	{ .compatible = "samsung,exynos4412-tmu", },
	{ .compatible = "samsung,exynos5250-tmu", },
	{ .compatible = "samsung,exynos5260-tmu", },
	{ .compatible = "samsung,exynos5420-tmu", },
	{ .compatible = "samsung,exynos5420-tmu-ext-triminfo", },
	{ .compatible = "samsung,exynos5440-tmu", },
	{ .compatible = "samsung,exynos7-tmu", },
	{ /* sentinel */ },
932 933 934
};
MODULE_DEVICE_TABLE(of, exynos_tmu_match);

935
static int exynos_of_get_soc_type(struct device_node *np)
936
{
937 938 939 940 941 942 943 944 945 946 947 948 949 950 951 952 953
	if (of_device_is_compatible(np, "samsung,exynos3250-tmu"))
		return SOC_ARCH_EXYNOS3250;
	else if (of_device_is_compatible(np, "samsung,exynos4210-tmu"))
		return SOC_ARCH_EXYNOS4210;
	else if (of_device_is_compatible(np, "samsung,exynos4412-tmu"))
		return SOC_ARCH_EXYNOS4412;
	else if (of_device_is_compatible(np, "samsung,exynos5250-tmu"))
		return SOC_ARCH_EXYNOS5250;
	else if (of_device_is_compatible(np, "samsung,exynos5260-tmu"))
		return SOC_ARCH_EXYNOS5260;
	else if (of_device_is_compatible(np, "samsung,exynos5420-tmu"))
		return SOC_ARCH_EXYNOS5420;
	else if (of_device_is_compatible(np,
					 "samsung,exynos5420-tmu-ext-triminfo"))
		return SOC_ARCH_EXYNOS5420_TRIMINFO;
	else if (of_device_is_compatible(np, "samsung,exynos5440-tmu"))
		return SOC_ARCH_EXYNOS5440;
954 955
	else if (of_device_is_compatible(np, "samsung,exynos7-tmu"))
		return SOC_ARCH_EXYNOS7;
956 957 958 959 960 961 962 963 964 965 966 967 968 969 970 971 972 973 974 975 976 977 978 979 980 981 982 983 984 985 986 987 988 989 990 991 992 993

	return -EINVAL;
}

static int exynos_of_sensor_conf(struct device_node *np,
				 struct exynos_tmu_platform_data *pdata)
{
	u32 value;
	int ret;

	of_node_get(np);

	ret = of_property_read_u32(np, "samsung,tmu_gain", &value);
	pdata->gain = (u8)value;
	of_property_read_u32(np, "samsung,tmu_reference_voltage", &value);
	pdata->reference_voltage = (u8)value;
	of_property_read_u32(np, "samsung,tmu_noise_cancel_mode", &value);
	pdata->noise_cancel_mode = (u8)value;

	of_property_read_u32(np, "samsung,tmu_efuse_value",
			     &pdata->efuse_value);
	of_property_read_u32(np, "samsung,tmu_min_efuse_value",
			     &pdata->min_efuse_value);
	of_property_read_u32(np, "samsung,tmu_max_efuse_value",
			     &pdata->max_efuse_value);

	of_property_read_u32(np, "samsung,tmu_first_point_trim", &value);
	pdata->first_point_trim = (u8)value;
	of_property_read_u32(np, "samsung,tmu_second_point_trim", &value);
	pdata->second_point_trim = (u8)value;
	of_property_read_u32(np, "samsung,tmu_default_temp_offset", &value);
	pdata->default_temp_offset = (u8)value;

	of_property_read_u32(np, "samsung,tmu_cal_type", &pdata->cal_type);
	of_property_read_u32(np, "samsung,tmu_cal_mode", &pdata->cal_mode);

	of_node_put(np);
	return 0;
994
}
995

996
static int exynos_map_dt_data(struct platform_device *pdev)
D
Donggeun Kim 已提交
997
{
998 999 1000
	struct exynos_tmu_data *data = platform_get_drvdata(pdev);
	struct exynos_tmu_platform_data *pdata;
	struct resource res;
1001
	int ret;
1002

1003
	if (!data || !pdev->dev.of_node)
1004
		return -ENODEV;
D
Donggeun Kim 已提交
1005

1006 1007 1008 1009 1010 1011 1012 1013 1014 1015 1016 1017 1018 1019 1020 1021
	/*
	 * Try enabling the regulator if found
	 * TODO: Add regulator as an SOC feature, so that regulator enable
	 * is a compulsory call.
	 */
	data->regulator = devm_regulator_get(&pdev->dev, "vtmu");
	if (!IS_ERR(data->regulator)) {
		ret = regulator_enable(data->regulator);
		if (ret) {
			dev_err(&pdev->dev, "failed to enable vtmu\n");
			return ret;
		}
	} else {
		dev_info(&pdev->dev, "Regulator node (vtmu) not found\n");
	}

1022 1023 1024
	data->id = of_alias_get_id(pdev->dev.of_node, "tmuctrl");
	if (data->id < 0)
		data->id = 0;
1025

1026 1027 1028 1029 1030 1031 1032 1033 1034 1035 1036 1037 1038 1039 1040 1041 1042
	data->irq = irq_of_parse_and_map(pdev->dev.of_node, 0);
	if (data->irq <= 0) {
		dev_err(&pdev->dev, "failed to get IRQ\n");
		return -ENODEV;
	}

	if (of_address_to_resource(pdev->dev.of_node, 0, &res)) {
		dev_err(&pdev->dev, "failed to get Resource 0\n");
		return -ENODEV;
	}

	data->base = devm_ioremap(&pdev->dev, res.start, resource_size(&res));
	if (!data->base) {
		dev_err(&pdev->dev, "Failed to ioremap memory\n");
		return -EADDRNOTAVAIL;
	}

1043 1044 1045 1046 1047
	pdata = devm_kzalloc(&pdev->dev,
			     sizeof(struct exynos_tmu_platform_data),
			     GFP_KERNEL);
	if (!pdata)
		return -ENOMEM;
1048

1049
	exynos_of_sensor_conf(pdev->dev.of_node, pdata);
1050
	data->pdata = pdata;
1051
	data->soc = exynos_of_get_soc_type(pdev->dev.of_node);
1052 1053 1054 1055 1056 1057 1058 1059 1060 1061 1062 1063 1064 1065 1066 1067 1068 1069 1070 1071 1072 1073 1074 1075 1076 1077 1078

	switch (data->soc) {
	case SOC_ARCH_EXYNOS4210:
		data->tmu_initialize = exynos4210_tmu_initialize;
		data->tmu_control = exynos4210_tmu_control;
		data->tmu_read = exynos4210_tmu_read;
		data->tmu_clear_irqs = exynos4210_tmu_clear_irqs;
		break;
	case SOC_ARCH_EXYNOS3250:
	case SOC_ARCH_EXYNOS4412:
	case SOC_ARCH_EXYNOS5250:
	case SOC_ARCH_EXYNOS5260:
	case SOC_ARCH_EXYNOS5420:
	case SOC_ARCH_EXYNOS5420_TRIMINFO:
		data->tmu_initialize = exynos4412_tmu_initialize;
		data->tmu_control = exynos4210_tmu_control;
		data->tmu_read = exynos4412_tmu_read;
		data->tmu_set_emulation = exynos4412_tmu_set_emulation;
		data->tmu_clear_irqs = exynos4210_tmu_clear_irqs;
		break;
	case SOC_ARCH_EXYNOS5440:
		data->tmu_initialize = exynos5440_tmu_initialize;
		data->tmu_control = exynos5440_tmu_control;
		data->tmu_read = exynos5440_tmu_read;
		data->tmu_set_emulation = exynos5440_tmu_set_emulation;
		data->tmu_clear_irqs = exynos5440_tmu_clear_irqs;
		break;
1079 1080 1081 1082 1083 1084 1085
	case SOC_ARCH_EXYNOS7:
		data->tmu_initialize = exynos7_tmu_initialize;
		data->tmu_control = exynos7_tmu_control;
		data->tmu_read = exynos7_tmu_read;
		data->tmu_set_emulation = exynos4412_tmu_set_emulation;
		data->tmu_clear_irqs = exynos4210_tmu_clear_irqs;
		break;
1086 1087 1088 1089 1090
	default:
		dev_err(&pdev->dev, "Platform not supported\n");
		return -EINVAL;
	}

1091 1092 1093 1094
	/*
	 * Check if the TMU shares some registers and then try to map the
	 * memory of common registers.
	 */
1095 1096
	if (data->soc != SOC_ARCH_EXYNOS5420_TRIMINFO &&
	    data->soc != SOC_ARCH_EXYNOS5440)
1097 1098 1099 1100 1101 1102 1103
		return 0;

	if (of_address_to_resource(pdev->dev.of_node, 1, &res)) {
		dev_err(&pdev->dev, "failed to get Resource 1\n");
		return -ENODEV;
	}

1104
	data->base_second = devm_ioremap(&pdev->dev, res.start,
1105
					resource_size(&res));
1106
	if (!data->base_second) {
1107 1108 1109
		dev_err(&pdev->dev, "Failed to ioremap memory\n");
		return -ENOMEM;
	}
1110 1111 1112 1113

	return 0;
}

1114 1115 1116 1117 1118
static struct thermal_zone_of_device_ops exynos_sensor_ops = {
	.get_temp = exynos_get_temp,
	.set_emul_temp = exynos_tmu_set_emulation,
};

1119 1120 1121
static int exynos_tmu_probe(struct platform_device *pdev)
{
	struct exynos_tmu_platform_data *pdata;
1122 1123
	struct exynos_tmu_data *data;
	int ret;
1124

1125 1126
	data = devm_kzalloc(&pdev->dev, sizeof(struct exynos_tmu_data),
					GFP_KERNEL);
1127
	if (!data)
D
Donggeun Kim 已提交
1128 1129
		return -ENOMEM;

1130 1131
	platform_set_drvdata(pdev, data);
	mutex_init(&data->lock);
D
Donggeun Kim 已提交
1132

1133 1134 1135 1136 1137 1138
	data->tzd = thermal_zone_of_sensor_register(&pdev->dev, 0, data,
						    &exynos_sensor_ops);
	if (IS_ERR(data->tzd)) {
		pr_err("thermal: tz: %p ERROR\n", data->tzd);
		return PTR_ERR(data->tzd);
	}
1139 1140
	ret = exynos_map_dt_data(pdev);
	if (ret)
1141
		goto err_sensor;
D
Donggeun Kim 已提交
1142

1143
	pdata = data->pdata;
D
Donggeun Kim 已提交
1144

1145
	INIT_WORK(&data->irq_work, exynos_tmu_work);
D
Donggeun Kim 已提交
1146

1147
	data->clk = devm_clk_get(&pdev->dev, "tmu_apbif");
D
Donggeun Kim 已提交
1148 1149
	if (IS_ERR(data->clk)) {
		dev_err(&pdev->dev, "Failed to get clock\n");
1150 1151
		ret = PTR_ERR(data->clk);
		goto err_sensor;
D
Donggeun Kim 已提交
1152 1153
	}

1154 1155 1156 1157
	data->clk_sec = devm_clk_get(&pdev->dev, "tmu_triminfo_apbif");
	if (IS_ERR(data->clk_sec)) {
		if (data->soc == SOC_ARCH_EXYNOS5420_TRIMINFO) {
			dev_err(&pdev->dev, "Failed to get triminfo clock\n");
1158 1159
			ret = PTR_ERR(data->clk_sec);
			goto err_sensor;
1160 1161 1162 1163 1164
		}
	} else {
		ret = clk_prepare(data->clk_sec);
		if (ret) {
			dev_err(&pdev->dev, "Failed to get clock\n");
1165
			goto err_sensor;
1166 1167 1168
		}
	}

1169
	ret = clk_prepare(data->clk);
1170 1171 1172 1173
	if (ret) {
		dev_err(&pdev->dev, "Failed to get clock\n");
		goto err_clk_sec;
	}
1174

1175 1176 1177 1178 1179 1180 1181 1182 1183 1184 1185 1186 1187 1188
	if (data->soc == SOC_ARCH_EXYNOS7) {
		data->sclk = devm_clk_get(&pdev->dev, "tmu_sclk");
		if (IS_ERR(data->sclk)) {
			dev_err(&pdev->dev, "Failed to get sclk\n");
			goto err_clk;
		} else {
			ret = clk_prepare_enable(data->sclk);
			if (ret) {
				dev_err(&pdev->dev, "Failed to enable sclk\n");
				goto err_clk;
			}
		}
	}

1189
	ret = exynos_tmu_initialize(pdev);
D
Donggeun Kim 已提交
1190 1191
	if (ret) {
		dev_err(&pdev->dev, "Failed to initialize TMU\n");
1192
		goto err_sclk;
D
Donggeun Kim 已提交
1193 1194
	}

1195 1196 1197 1198
	ret = devm_request_irq(&pdev->dev, data->irq, exynos_tmu_irq,
		IRQF_TRIGGER_RISING | IRQF_SHARED, dev_name(&pdev->dev), data);
	if (ret) {
		dev_err(&pdev->dev, "Failed to request irq: %d\n", data->irq);
1199
		goto err_sclk;
1200
	}
1201

1202
	exynos_tmu_control(pdev, true);
D
Donggeun Kim 已提交
1203
	return 0;
1204 1205
err_sclk:
	clk_disable_unprepare(data->sclk);
D
Donggeun Kim 已提交
1206
err_clk:
1207
	clk_unprepare(data->clk);
1208 1209 1210
err_clk_sec:
	if (!IS_ERR(data->clk_sec))
		clk_unprepare(data->clk_sec);
1211 1212 1213
err_sensor:
	thermal_zone_of_sensor_unregister(&pdev->dev, data->tzd);

D
Donggeun Kim 已提交
1214 1215 1216
	return ret;
}

1217
static int exynos_tmu_remove(struct platform_device *pdev)
D
Donggeun Kim 已提交
1218
{
1219
	struct exynos_tmu_data *data = platform_get_drvdata(pdev);
1220
	struct thermal_zone_device *tzd = data->tzd;
D
Donggeun Kim 已提交
1221

1222
	thermal_zone_of_sensor_unregister(&pdev->dev, tzd);
1223 1224
	exynos_tmu_control(pdev, false);

1225
	clk_disable_unprepare(data->sclk);
1226
	clk_unprepare(data->clk);
1227 1228
	if (!IS_ERR(data->clk_sec))
		clk_unprepare(data->clk_sec);
D
Donggeun Kim 已提交
1229

1230 1231 1232
	if (!IS_ERR(data->regulator))
		regulator_disable(data->regulator);

D
Donggeun Kim 已提交
1233 1234 1235
	return 0;
}

1236
#ifdef CONFIG_PM_SLEEP
1237
static int exynos_tmu_suspend(struct device *dev)
D
Donggeun Kim 已提交
1238
{
1239
	exynos_tmu_control(to_platform_device(dev), false);
D
Donggeun Kim 已提交
1240 1241 1242 1243

	return 0;
}

1244
static int exynos_tmu_resume(struct device *dev)
D
Donggeun Kim 已提交
1245
{
1246 1247
	struct platform_device *pdev = to_platform_device(dev);

1248 1249
	exynos_tmu_initialize(pdev);
	exynos_tmu_control(pdev, true);
D
Donggeun Kim 已提交
1250 1251 1252

	return 0;
}
1253

1254 1255 1256
static SIMPLE_DEV_PM_OPS(exynos_tmu_pm,
			 exynos_tmu_suspend, exynos_tmu_resume);
#define EXYNOS_TMU_PM	(&exynos_tmu_pm)
D
Donggeun Kim 已提交
1257
#else
1258
#define EXYNOS_TMU_PM	NULL
D
Donggeun Kim 已提交
1259 1260
#endif

1261
static struct platform_driver exynos_tmu_driver = {
D
Donggeun Kim 已提交
1262
	.driver = {
1263 1264
		.name   = "exynos-tmu",
		.pm     = EXYNOS_TMU_PM,
1265
		.of_match_table = exynos_tmu_match,
D
Donggeun Kim 已提交
1266
	},
1267
	.probe = exynos_tmu_probe,
1268
	.remove	= exynos_tmu_remove,
D
Donggeun Kim 已提交
1269 1270
};

1271
module_platform_driver(exynos_tmu_driver);
D
Donggeun Kim 已提交
1272

1273
MODULE_DESCRIPTION("EXYNOS TMU Driver");
D
Donggeun Kim 已提交
1274 1275
MODULE_AUTHOR("Donggeun Kim <dg77.kim@samsung.com>");
MODULE_LICENSE("GPL");
1276
MODULE_ALIAS("platform:exynos-tmu");