exynos_tmu.c 36.7 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 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714
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);
		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);
		interrupt_en = 0; /* Disable all interrupts */
	}
	con |= 1 << EXYNOS7_PD_DET_EN_SHIFT;

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

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

	if (!data)
		return -EINVAL;
721 722 723

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

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

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

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

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

740
		if (data->soc != SOC_ARCH_EXYNOS5440) {
741 742
			val &= ~(EXYNOS_EMUL_TIME_MASK << EXYNOS_EMUL_TIME_SHIFT);
			val |= (EXYNOS_EMUL_TIME << EXYNOS_EMUL_TIME_SHIFT);
743
		}
744 745 746 747 748 749 750 751 752 753 754 755 756
		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;
		}
757
	} else {
758
		val &= ~EXYNOS_EMUL_ENABLE;
759 760
	}

761 762 763
	return val;
}

764 765 766 767 768 769 770 771
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;
772 773
	else if (data->soc == SOC_ARCH_EXYNOS7)
		emul_con = EXYNOS7_TMU_REG_EMUL_CON;
774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791
	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);
}

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

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

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

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

819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836
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);
}

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

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

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

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

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

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

873 874 875 876 877 878 879 880
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;
881 882 883
	} else if (data->soc == SOC_ARCH_EXYNOS7) {
		tmu_intstat = EXYNOS7_TMU_REG_INTPEND;
		tmu_intclear = EXYNOS7_TMU_REG_INTPEND;
884 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
	} 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);
}

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

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

	return IRQ_HANDLED;
}
919 920

static const struct of_device_id exynos_tmu_match[] = {
921 922 923
	{
		.compatible = "samsung,exynos3250-tmu",
	},
924 925 926
	{
		.compatible = "samsung,exynos4210-tmu",
	},
927 928 929
	{
		.compatible = "samsung,exynos4412-tmu",
	},
930 931 932
	{
		.compatible = "samsung,exynos5250-tmu",
	},
933 934 935
	{
		.compatible = "samsung,exynos5260-tmu",
	},
936 937 938 939 940 941
	{
		.compatible = "samsung,exynos5420-tmu",
	},
	{
		.compatible = "samsung,exynos5420-tmu-ext-triminfo",
	},
942 943 944
	{
		.compatible = "samsung,exynos5440-tmu",
	},
945 946 947
	{
		.compatible = "samsung,exynos7-tmu",
	},
948 949 950 951
	{},
};
MODULE_DEVICE_TABLE(of, exynos_tmu_match);

952
static int exynos_of_get_soc_type(struct device_node *np)
953
{
954 955 956 957 958 959 960 961 962 963 964 965 966 967 968 969 970
	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;
971 972
	else if (of_device_is_compatible(np, "samsung,exynos7-tmu"))
		return SOC_ARCH_EXYNOS7;
973 974 975 976 977 978 979 980 981 982 983 984 985 986 987 988 989 990 991 992 993 994 995 996 997 998 999 1000 1001 1002 1003 1004 1005 1006 1007 1008 1009 1010

	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;
1011
}
1012

1013
static int exynos_map_dt_data(struct platform_device *pdev)
D
Donggeun Kim 已提交
1014
{
1015 1016 1017
	struct exynos_tmu_data *data = platform_get_drvdata(pdev);
	struct exynos_tmu_platform_data *pdata;
	struct resource res;
1018
	int ret;
1019

1020
	if (!data || !pdev->dev.of_node)
1021
		return -ENODEV;
D
Donggeun Kim 已提交
1022

1023 1024 1025 1026 1027 1028 1029 1030 1031 1032 1033 1034 1035 1036 1037 1038
	/*
	 * 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");
	}

1039 1040 1041
	data->id = of_alias_get_id(pdev->dev.of_node, "tmuctrl");
	if (data->id < 0)
		data->id = 0;
1042

1043 1044 1045 1046 1047 1048 1049 1050 1051 1052 1053 1054 1055 1056 1057 1058 1059
	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;
	}

1060 1061 1062 1063 1064
	pdata = devm_kzalloc(&pdev->dev,
			     sizeof(struct exynos_tmu_platform_data),
			     GFP_KERNEL);
	if (!pdata)
		return -ENOMEM;
1065

1066
	exynos_of_sensor_conf(pdev->dev.of_node, pdata);
1067
	data->pdata = pdata;
1068
	data->soc = exynos_of_get_soc_type(pdev->dev.of_node);
1069 1070 1071 1072 1073 1074 1075 1076 1077 1078 1079 1080 1081 1082 1083 1084 1085 1086 1087 1088 1089 1090 1091 1092 1093 1094 1095

	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;
1096 1097 1098 1099 1100 1101 1102
	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;
1103 1104 1105 1106 1107
	default:
		dev_err(&pdev->dev, "Platform not supported\n");
		return -EINVAL;
	}

1108 1109 1110 1111
	/*
	 * Check if the TMU shares some registers and then try to map the
	 * memory of common registers.
	 */
1112 1113
	if (data->soc != SOC_ARCH_EXYNOS5420_TRIMINFO &&
	    data->soc != SOC_ARCH_EXYNOS5440)
1114 1115 1116 1117 1118 1119 1120
		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;
	}

1121
	data->base_second = devm_ioremap(&pdev->dev, res.start,
1122
					resource_size(&res));
1123
	if (!data->base_second) {
1124 1125 1126
		dev_err(&pdev->dev, "Failed to ioremap memory\n");
		return -ENOMEM;
	}
1127 1128 1129 1130

	return 0;
}

1131 1132 1133 1134 1135
static struct thermal_zone_of_device_ops exynos_sensor_ops = {
	.get_temp = exynos_get_temp,
	.set_emul_temp = exynos_tmu_set_emulation,
};

1136 1137 1138
static int exynos_tmu_probe(struct platform_device *pdev)
{
	struct exynos_tmu_platform_data *pdata;
1139 1140
	struct exynos_tmu_data *data;
	int ret;
1141

1142 1143
	data = devm_kzalloc(&pdev->dev, sizeof(struct exynos_tmu_data),
					GFP_KERNEL);
1144
	if (!data)
D
Donggeun Kim 已提交
1145 1146
		return -ENOMEM;

1147 1148
	platform_set_drvdata(pdev, data);
	mutex_init(&data->lock);
D
Donggeun Kim 已提交
1149

1150 1151 1152 1153 1154 1155
	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);
	}
1156 1157
	ret = exynos_map_dt_data(pdev);
	if (ret)
1158
		goto err_sensor;
D
Donggeun Kim 已提交
1159

1160
	pdata = data->pdata;
D
Donggeun Kim 已提交
1161

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

1164
	data->clk = devm_clk_get(&pdev->dev, "tmu_apbif");
D
Donggeun Kim 已提交
1165 1166
	if (IS_ERR(data->clk)) {
		dev_err(&pdev->dev, "Failed to get clock\n");
1167 1168
		ret = PTR_ERR(data->clk);
		goto err_sensor;
D
Donggeun Kim 已提交
1169 1170
	}

1171 1172 1173 1174
	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");
1175 1176
			ret = PTR_ERR(data->clk_sec);
			goto err_sensor;
1177 1178 1179 1180 1181
		}
	} else {
		ret = clk_prepare(data->clk_sec);
		if (ret) {
			dev_err(&pdev->dev, "Failed to get clock\n");
1182
			goto err_sensor;
1183 1184 1185
		}
	}

1186
	ret = clk_prepare(data->clk);
1187 1188 1189 1190
	if (ret) {
		dev_err(&pdev->dev, "Failed to get clock\n");
		goto err_clk_sec;
	}
1191

1192 1193 1194 1195 1196 1197 1198 1199 1200 1201 1202 1203 1204 1205
	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;
			}
		}
	}

1206
	ret = exynos_tmu_initialize(pdev);
D
Donggeun Kim 已提交
1207 1208
	if (ret) {
		dev_err(&pdev->dev, "Failed to initialize TMU\n");
1209
		goto err_sclk;
D
Donggeun Kim 已提交
1210 1211
	}

1212 1213 1214 1215
	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);
1216
		goto err_sclk;
1217
	}
1218

1219
	exynos_tmu_control(pdev, true);
D
Donggeun Kim 已提交
1220
	return 0;
1221 1222
err_sclk:
	clk_disable_unprepare(data->sclk);
D
Donggeun Kim 已提交
1223
err_clk:
1224
	clk_unprepare(data->clk);
1225 1226 1227
err_clk_sec:
	if (!IS_ERR(data->clk_sec))
		clk_unprepare(data->clk_sec);
1228 1229 1230
err_sensor:
	thermal_zone_of_sensor_unregister(&pdev->dev, data->tzd);

D
Donggeun Kim 已提交
1231 1232 1233
	return ret;
}

1234
static int exynos_tmu_remove(struct platform_device *pdev)
D
Donggeun Kim 已提交
1235
{
1236
	struct exynos_tmu_data *data = platform_get_drvdata(pdev);
1237
	struct thermal_zone_device *tzd = data->tzd;
D
Donggeun Kim 已提交
1238

1239
	thermal_zone_of_sensor_unregister(&pdev->dev, tzd);
1240 1241
	exynos_tmu_control(pdev, false);

1242
	clk_disable_unprepare(data->sclk);
1243
	clk_unprepare(data->clk);
1244 1245
	if (!IS_ERR(data->clk_sec))
		clk_unprepare(data->clk_sec);
D
Donggeun Kim 已提交
1246

1247 1248 1249
	if (!IS_ERR(data->regulator))
		regulator_disable(data->regulator);

D
Donggeun Kim 已提交
1250 1251 1252
	return 0;
}

1253
#ifdef CONFIG_PM_SLEEP
1254
static int exynos_tmu_suspend(struct device *dev)
D
Donggeun Kim 已提交
1255
{
1256
	exynos_tmu_control(to_platform_device(dev), false);
D
Donggeun Kim 已提交
1257 1258 1259 1260

	return 0;
}

1261
static int exynos_tmu_resume(struct device *dev)
D
Donggeun Kim 已提交
1262
{
1263 1264
	struct platform_device *pdev = to_platform_device(dev);

1265 1266
	exynos_tmu_initialize(pdev);
	exynos_tmu_control(pdev, true);
D
Donggeun Kim 已提交
1267 1268 1269

	return 0;
}
1270

1271 1272 1273
static SIMPLE_DEV_PM_OPS(exynos_tmu_pm,
			 exynos_tmu_suspend, exynos_tmu_resume);
#define EXYNOS_TMU_PM	(&exynos_tmu_pm)
D
Donggeun Kim 已提交
1274
#else
1275
#define EXYNOS_TMU_PM	NULL
D
Donggeun Kim 已提交
1276 1277
#endif

1278
static struct platform_driver exynos_tmu_driver = {
D
Donggeun Kim 已提交
1279
	.driver = {
1280 1281
		.name   = "exynos-tmu",
		.pm     = EXYNOS_TMU_PM,
1282
		.of_match_table = exynos_tmu_match,
D
Donggeun Kim 已提交
1283
	},
1284
	.probe = exynos_tmu_probe,
1285
	.remove	= exynos_tmu_remove,
D
Donggeun Kim 已提交
1286 1287
};

1288
module_platform_driver(exynos_tmu_driver);
D
Donggeun Kim 已提交
1289

1290
MODULE_DESCRIPTION("EXYNOS TMU Driver");
D
Donggeun Kim 已提交
1291 1292
MODULE_AUTHOR("Donggeun Kim <dg77.kim@samsung.com>");
MODULE_LICENSE("GPL");
1293
MODULE_ALIAS("platform:exynos-tmu");