exynos_tmu.c 30.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
#define MCELSIUS	1000
123 124 125 126 127 128
/**
 * 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.
129
 * @base_second: base address of the common registers of the TMU controller.
130 131 132 133 134
 * @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.
135
 * @clk_sec: pointer to the clock structure for accessing the base_second.
136 137
 * @temp_error1: fused value of the first point trim.
 * @temp_error2: fused value of the second point trim.
138
 * @regulator: pointer to the TMU regulator structure.
139
 * @reg_conf: pointer to structure to register with core thermal.
140
 * @tmu_initialize: SoC specific TMU initialization method
141
 * @tmu_control: SoC specific TMU control method
142
 * @tmu_read: SoC specific TMU temperature read method
143
 * @tmu_set_emulation: SoC specific TMU emulation setting method
144
 * @tmu_clear_irqs: SoC specific TMU interrupts clearing method
145
 */
146
struct exynos_tmu_data {
147
	int id;
148
	struct exynos_tmu_platform_data *pdata;
D
Donggeun Kim 已提交
149
	void __iomem *base;
150
	void __iomem *base_second;
D
Donggeun Kim 已提交
151
	int irq;
152
	enum soc_type soc;
D
Donggeun Kim 已提交
153 154
	struct work_struct irq_work;
	struct mutex lock;
155
	struct clk *clk, *clk_sec;
D
Donggeun Kim 已提交
156
	u8 temp_error1, temp_error2;
157
	struct regulator *regulator;
158 159
	struct thermal_zone_device *tzd;

160
	int (*tmu_initialize)(struct platform_device *pdev);
161
	void (*tmu_control)(struct platform_device *pdev, bool on);
162
	int (*tmu_read)(struct exynos_tmu_data *data);
163 164
	void (*tmu_set_emulation)(struct exynos_tmu_data *data,
				  unsigned long temp);
165
	void (*tmu_clear_irqs)(struct exynos_tmu_data *data);
D
Donggeun Kim 已提交
166 167
};

168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194
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;

	if (!p) {
		pr_err("Wrong temperature configuration data\n");
		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 已提交
195 196 197 198
/*
 * TMU treats temperature as a mapped temperature code.
 * The temperature is converted differently depending on the calibration type.
 */
199
static int temp_to_code(struct exynos_tmu_data *data, u8 temp)
D
Donggeun Kim 已提交
200
{
201
	struct exynos_tmu_platform_data *pdata = data->pdata;
D
Donggeun Kim 已提交
202 203 204 205
	int temp_code;

	switch (pdata->cal_type) {
	case TYPE_TWO_POINT_TRIMMING:
206 207 208 209
		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 已提交
210 211
		break;
	case TYPE_ONE_POINT_TRIMMING:
212
		temp_code = temp + data->temp_error1 - pdata->first_point_trim;
D
Donggeun Kim 已提交
213 214
		break;
	default:
215
		temp_code = temp + pdata->default_temp_offset;
D
Donggeun Kim 已提交
216 217
		break;
	}
218

D
Donggeun Kim 已提交
219 220 221 222 223 224 225
	return temp_code;
}

/*
 * Calculate a temperature value from a temperature code.
 * The unit of the temperature is degree Celsius.
 */
226
static int code_to_temp(struct exynos_tmu_data *data, u8 temp_code)
D
Donggeun Kim 已提交
227
{
228
	struct exynos_tmu_platform_data *pdata = data->pdata;
D
Donggeun Kim 已提交
229 230 231 232
	int temp;

	switch (pdata->cal_type) {
	case TYPE_TWO_POINT_TRIMMING:
233 234 235 236
		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 已提交
237 238
		break;
	case TYPE_ONE_POINT_TRIMMING:
239
		temp = temp_code - data->temp_error1 + pdata->first_point_trim;
D
Donggeun Kim 已提交
240 241
		break;
	default:
242
		temp = temp_code - pdata->default_temp_offset;
D
Donggeun Kim 已提交
243 244
		break;
	}
245

D
Donggeun Kim 已提交
246 247 248
	return temp;
}

249
static void sanitize_temp_error(struct exynos_tmu_data *data, u32 trim_info)
D
Donggeun Kim 已提交
250
{
251
	struct exynos_tmu_platform_data *pdata = data->pdata;
D
Donggeun Kim 已提交
252

253
	data->temp_error1 = trim_info & EXYNOS_TMU_TEMP_MASK;
254
	data->temp_error2 = ((trim_info >> EXYNOS_TRIMINFO_85_SHIFT) &
255
				EXYNOS_TMU_TEMP_MASK);
256

257 258 259 260 261 262 263
	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 =
264
			(pdata->efuse_value >> EXYNOS_TRIMINFO_85_SHIFT) &
265
			EXYNOS_TMU_TEMP_MASK;
266
}
267

268 269
static u32 get_th_reg(struct exynos_tmu_data *data, u32 threshold, bool falling)
{
270 271 272 273
	struct thermal_zone_device *tz = data->tzd;
	const struct thermal_trip * const trips =
		of_thermal_get_trip_points(tz);
	unsigned long temp;
274
	int i;
275

276 277 278 279 280
	if (!trips) {
		pr_err("%s: Cannot get trip points from of-thermal.c!\n",
		       __func__);
		return 0;
	}
281

282 283 284 285 286
	for (i = 0; i < of_thermal_get_ntrips(tz); i++) {
		if (trips[i].type == THERMAL_TRIP_CRITICAL)
			continue;

		temp = trips[i].temperature / MCELSIUS;
287
		if (falling)
288
			temp -= (trips[i].hysteresis / MCELSIUS);
289 290
		else
			threshold &= ~(0xff << 8 * i);
291

292
		threshold |= temp_to_code(data, temp) << 8 * i;
D
Donggeun Kim 已提交
293
	}
294 295 296 297

	return threshold;
}

298
static int exynos_tmu_initialize(struct platform_device *pdev)
D
Donggeun Kim 已提交
299
{
300
	struct exynos_tmu_data *data = platform_get_drvdata(pdev);
301
	int ret;
D
Donggeun Kim 已提交
302 303 304

	mutex_lock(&data->lock);
	clk_enable(data->clk);
305 306
	if (!IS_ERR(data->clk_sec))
		clk_enable(data->clk_sec);
307
	ret = data->tmu_initialize(pdev);
D
Donggeun Kim 已提交
308 309
	clk_disable(data->clk);
	mutex_unlock(&data->lock);
310 311
	if (!IS_ERR(data->clk_sec))
		clk_disable(data->clk_sec);
D
Donggeun Kim 已提交
312 313 314 315

	return ret;
}

316
static u32 get_con_reg(struct exynos_tmu_data *data, u32 con)
D
Donggeun Kim 已提交
317
{
318
	struct exynos_tmu_platform_data *pdata = data->pdata;
D
Donggeun Kim 已提交
319

320 321 322
	if (data->soc == SOC_ARCH_EXYNOS4412 ||
	    data->soc == SOC_ARCH_EXYNOS3250)
		con |= (EXYNOS4412_MUX_ADDR_VALUE << EXYNOS4412_MUX_ADDR_SHIFT);
323

324 325
	con &= ~(EXYNOS_TMU_REF_VOLTAGE_MASK << EXYNOS_TMU_REF_VOLTAGE_SHIFT);
	con |= pdata->reference_voltage << EXYNOS_TMU_REF_VOLTAGE_SHIFT;
326

327 328
	con &= ~(EXYNOS_TMU_BUF_SLOPE_SEL_MASK << EXYNOS_TMU_BUF_SLOPE_SEL_SHIFT);
	con |= (pdata->gain << EXYNOS_TMU_BUF_SLOPE_SEL_SHIFT);
329 330

	if (pdata->noise_cancel_mode) {
331 332
		con &= ~(EXYNOS_TMU_TRIP_MODE_MASK << EXYNOS_TMU_TRIP_MODE_SHIFT);
		con |= (pdata->noise_cancel_mode << EXYNOS_TMU_TRIP_MODE_SHIFT);
333 334
	}

335 336 337 338 339 340
	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 已提交
341

342 343
	mutex_lock(&data->lock);
	clk_enable(data->clk);
344
	data->tmu_control(pdev, on);
D
Donggeun Kim 已提交
345 346 347 348
	clk_disable(data->clk);
	mutex_unlock(&data->lock);
}

349
static int exynos4210_tmu_initialize(struct platform_device *pdev)
D
Donggeun Kim 已提交
350
{
351
	struct exynos_tmu_data *data = platform_get_drvdata(pdev);
352 353 354
	struct thermal_zone_device *tz = data->tzd;
	const struct thermal_trip * const trips =
		of_thermal_get_trip_points(tz);
355
	int ret = 0, threshold_code, i;
356 357 358 359 360 361 362 363 364
	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 已提交
365

366 367 368 369 370
	status = readb(data->base + EXYNOS_TMU_REG_STATUS);
	if (!status) {
		ret = -EBUSY;
		goto out;
	}
D
Donggeun Kim 已提交
371

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

374
	/* Write temperature code for threshold */
375 376 377 378 379 380
	reference = trips[0].temperature / MCELSIUS;
	threshold_code = temp_to_code(data, reference);
	if (threshold_code < 0) {
		ret = threshold_code;
		goto out;
	}
381 382
	writeb(threshold_code, data->base + EXYNOS4210_TMU_REG_THRESHOLD_TEMP);

383 384 385
	for (i = 0; i < of_thermal_get_ntrips(tz); i++) {
		temp = trips[i].temperature / MCELSIUS;
		writeb(temp - reference, data->base +
386
		       EXYNOS4210_TMU_REG_TRIG_LEVEL0 + i * 4);
387
	}
388

389
	data->tmu_clear_irqs(data);
390 391 392 393 394 395 396
out:
	return ret;
}

static int exynos4412_tmu_initialize(struct platform_device *pdev)
{
	struct exynos_tmu_data *data = platform_get_drvdata(pdev);
397 398
	const struct thermal_trip * const trips =
		of_thermal_get_trip_points(data->tzd);
399 400
	unsigned int status, trim_info, con, ctrl, rising_threshold;
	int ret = 0, threshold_code, i;
401
	unsigned long crit_temp = 0;
402 403 404 405 406 407 408 409 410 411 412 413 414 415

	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);
416
		}
417 418 419 420
		ctrl = readl(data->base + EXYNOS_TMU_TRIMINFO_CON2);
		ctrl |= EXYNOS_TRIMINFO_RELOAD_ENABLE;
		writel(ctrl, data->base + EXYNOS_TMU_TRIMINFO_CON2);
	}
421

422 423 424 425 426 427 428 429 430 431 432 433 434 435
	/* 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);

436
	data->tmu_clear_irqs(data);
437 438

	/* if last threshold limit is also present */
439 440 441 442 443
	for (i = 0; i < of_thermal_get_ntrips(data->tzd); i++) {
		if (trips[i].type == THERMAL_TRIP_CRITICAL) {
			crit_temp = trips[i].temperature;
			break;
		}
444
	}
445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461

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

462
out:
463 464
	return ret;
}
D
Donggeun Kim 已提交
465

466 467 468 469
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;
470 471
	int ret = 0, threshold_code;
	unsigned long crit_temp = 0;
472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496

	/*
	 * 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);

497
	data->tmu_clear_irqs(data);
498 499

	/* if last threshold limit is also present */
500 501
	if (!data->tzd->ops->get_crit_temp(data->tzd, &crit_temp)) {
		threshold_code = temp_to_code(data, crit_temp / MCELSIUS);
502 503 504 505 506 507 508 509 510 511 512 513
		/* 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 已提交
514 515
}

516
static void exynos4210_tmu_control(struct platform_device *pdev, bool on)
517
{
518
	struct exynos_tmu_data *data = platform_get_drvdata(pdev);
519
	struct thermal_zone_device *tz = data->tzd;
520
	unsigned int con, interrupt_en;
521

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

524 525 526
	if (on) {
		con |= (1 << EXYNOS_TMU_CORE_EN_SHIFT);
		interrupt_en =
527 528 529 530 531 532 533 534 535
			(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);

536
		if (data->soc != SOC_ARCH_EXYNOS4210)
537 538 539 540 541 542 543 544 545 546 547 548 549
			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);
550
	struct thermal_zone_device *tz = data->tzd;
551 552 553 554 555 556 557
	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 =
558 559 560 561 562 563 564 565 566 567
			(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;
568 569 570 571 572 573 574 575
	} 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);
}

576
static int exynos_get_temp(void *p, long *temp)
D
Donggeun Kim 已提交
577
{
578 579 580 581
	struct exynos_tmu_data *data = p;

	if (!data)
		return -EINVAL;
582 583 584

	mutex_lock(&data->lock);
	clk_enable(data->clk);
585 586 587

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

D
Donggeun Kim 已提交
588 589
	clk_disable(data->clk);
	mutex_unlock(&data->lock);
590

591
	return 0;
D
Donggeun Kim 已提交
592
}
593 594

#ifdef CONFIG_THERMAL_EMULATION
595 596 597
static u32 get_emul_con_reg(struct exynos_tmu_data *data, unsigned int val,
			    unsigned long temp)
{
598 599 600
	if (temp) {
		temp /= MCELSIUS;

601
		if (data->soc != SOC_ARCH_EXYNOS5440) {
602 603
			val &= ~(EXYNOS_EMUL_TIME_MASK << EXYNOS_EMUL_TIME_SHIFT);
			val |= (EXYNOS_EMUL_TIME << EXYNOS_EMUL_TIME_SHIFT);
604
		}
605 606
		val &= ~(EXYNOS_EMUL_DATA_MASK << EXYNOS_EMUL_DATA_SHIFT);
		val |= (temp_to_code(data, temp) << EXYNOS_EMUL_DATA_SHIFT) |
607
			EXYNOS_EMUL_ENABLE;
608
	} else {
609
		val &= ~EXYNOS_EMUL_ENABLE;
610 611
	}

612 613 614
	return val;
}

615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640
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;
	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);
}

641 642 643 644 645
static int exynos_tmu_set_emulation(void *drv_data, unsigned long temp)
{
	struct exynos_tmu_data *data = drv_data;
	int ret = -EINVAL;

646
	if (data->soc == SOC_ARCH_EXYNOS4210)
647 648 649 650 651 652 653
		goto out;

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

	mutex_lock(&data->lock);
	clk_enable(data->clk);
654
	data->tmu_set_emulation(data, temp);
655 656 657 658 659 660 661
	clk_disable(data->clk);
	mutex_unlock(&data->lock);
	return 0;
out:
	return ret;
}
#else
662 663
#define exynos4412_tmu_set_emulation NULL
#define exynos5440_tmu_set_emulation NULL
664 665
static int exynos_tmu_set_emulation(void *drv_data,	unsigned long temp)
	{ return -EINVAL; }
666
#endif /* CONFIG_THERMAL_EMULATION */
667

668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685
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);
}

686
static void exynos_tmu_work(struct work_struct *work)
D
Donggeun Kim 已提交
687
{
688 689
	struct exynos_tmu_data *data = container_of(work,
			struct exynos_tmu_data, irq_work);
690
	unsigned int val_type;
691

692 693
	if (!IS_ERR(data->clk_sec))
		clk_enable(data->clk_sec);
694
	/* Find which sensor generated this interrupt */
695 696
	if (data->soc == SOC_ARCH_EXYNOS5440) {
		val_type = readl(data->base_second + EXYNOS5440_TMU_IRQ_STATUS);
697 698 699
		if (!((val_type >> data->id) & 0x1))
			goto out;
	}
700 701
	if (!IS_ERR(data->clk_sec))
		clk_disable(data->clk_sec);
D
Donggeun Kim 已提交
702

703
	exynos_report_trigger(data);
D
Donggeun Kim 已提交
704 705
	mutex_lock(&data->lock);
	clk_enable(data->clk);
706

707
	/* TODO: take action based on particular interrupt */
708
	data->tmu_clear_irqs(data);
709

D
Donggeun Kim 已提交
710 711
	clk_disable(data->clk);
	mutex_unlock(&data->lock);
712
out:
713
	enable_irq(data->irq);
D
Donggeun Kim 已提交
714 715
}

716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749
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;
	} 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);
}

750
static irqreturn_t exynos_tmu_irq(int irq, void *id)
D
Donggeun Kim 已提交
751
{
752
	struct exynos_tmu_data *data = id;
D
Donggeun Kim 已提交
753 754 755 756 757 758

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

	return IRQ_HANDLED;
}
759 760

static const struct of_device_id exynos_tmu_match[] = {
761 762 763
	{
		.compatible = "samsung,exynos3250-tmu",
	},
764 765 766
	{
		.compatible = "samsung,exynos4210-tmu",
	},
767 768 769
	{
		.compatible = "samsung,exynos4412-tmu",
	},
770 771 772
	{
		.compatible = "samsung,exynos5250-tmu",
	},
773 774 775
	{
		.compatible = "samsung,exynos5260-tmu",
	},
776 777 778 779 780 781
	{
		.compatible = "samsung,exynos5420-tmu",
	},
	{
		.compatible = "samsung,exynos5420-tmu-ext-triminfo",
	},
782 783 784
	{
		.compatible = "samsung,exynos5440-tmu",
	},
785 786 787 788
	{},
};
MODULE_DEVICE_TABLE(of, exynos_tmu_match);

789
static int exynos_of_get_soc_type(struct device_node *np)
790
{
791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845
	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;

	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;
846
}
847

848
static int exynos_map_dt_data(struct platform_device *pdev)
D
Donggeun Kim 已提交
849
{
850 851 852
	struct exynos_tmu_data *data = platform_get_drvdata(pdev);
	struct exynos_tmu_platform_data *pdata;
	struct resource res;
853
	int ret;
854

855
	if (!data || !pdev->dev.of_node)
856
		return -ENODEV;
D
Donggeun Kim 已提交
857

858 859 860 861 862 863 864 865 866 867 868 869 870 871 872 873
	/*
	 * 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");
	}

874 875 876
	data->id = of_alias_get_id(pdev->dev.of_node, "tmuctrl");
	if (data->id < 0)
		data->id = 0;
877

878 879 880 881 882 883 884 885 886 887 888 889 890 891 892 893 894
	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;
	}

895 896 897 898 899
	pdata = devm_kzalloc(&pdev->dev,
			     sizeof(struct exynos_tmu_platform_data),
			     GFP_KERNEL);
	if (!pdata)
		return -ENOMEM;
900

901
	exynos_of_sensor_conf(pdev->dev.of_node, pdata);
902
	data->pdata = pdata;
903
	data->soc = exynos_of_get_soc_type(pdev->dev.of_node);
904 905 906 907 908 909 910 911 912 913 914 915 916 917 918 919 920 921 922 923 924 925 926 927 928 929 930 931 932 933 934 935

	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;
	default:
		dev_err(&pdev->dev, "Platform not supported\n");
		return -EINVAL;
	}

936 937 938 939
	/*
	 * Check if the TMU shares some registers and then try to map the
	 * memory of common registers.
	 */
940 941
	if (data->soc != SOC_ARCH_EXYNOS5420_TRIMINFO &&
	    data->soc != SOC_ARCH_EXYNOS5440)
942 943 944 945 946 947 948
		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;
	}

949
	data->base_second = devm_ioremap(&pdev->dev, res.start,
950
					resource_size(&res));
951
	if (!data->base_second) {
952 953 954
		dev_err(&pdev->dev, "Failed to ioremap memory\n");
		return -ENOMEM;
	}
955 956 957 958

	return 0;
}

959 960 961 962 963
static struct thermal_zone_of_device_ops exynos_sensor_ops = {
	.get_temp = exynos_get_temp,
	.set_emul_temp = exynos_tmu_set_emulation,
};

964 965 966
static int exynos_tmu_probe(struct platform_device *pdev)
{
	struct exynos_tmu_platform_data *pdata;
967 968
	struct exynos_tmu_data *data;
	int ret;
969

970 971
	data = devm_kzalloc(&pdev->dev, sizeof(struct exynos_tmu_data),
					GFP_KERNEL);
972
	if (!data)
D
Donggeun Kim 已提交
973 974
		return -ENOMEM;

975 976
	platform_set_drvdata(pdev, data);
	mutex_init(&data->lock);
D
Donggeun Kim 已提交
977

978 979 980 981 982 983
	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);
	}
984 985
	ret = exynos_map_dt_data(pdev);
	if (ret)
986
		goto err_sensor;
D
Donggeun Kim 已提交
987

988
	pdata = data->pdata;
D
Donggeun Kim 已提交
989

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

992
	data->clk = devm_clk_get(&pdev->dev, "tmu_apbif");
D
Donggeun Kim 已提交
993 994
	if (IS_ERR(data->clk)) {
		dev_err(&pdev->dev, "Failed to get clock\n");
995 996
		ret = PTR_ERR(data->clk);
		goto err_sensor;
D
Donggeun Kim 已提交
997 998
	}

999 1000 1001 1002
	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");
1003 1004
			ret = PTR_ERR(data->clk_sec);
			goto err_sensor;
1005 1006 1007 1008 1009
		}
	} else {
		ret = clk_prepare(data->clk_sec);
		if (ret) {
			dev_err(&pdev->dev, "Failed to get clock\n");
1010
			goto err_sensor;
1011 1012 1013
		}
	}

1014
	ret = clk_prepare(data->clk);
1015 1016 1017 1018
	if (ret) {
		dev_err(&pdev->dev, "Failed to get clock\n");
		goto err_clk_sec;
	}
1019

1020
	ret = exynos_tmu_initialize(pdev);
D
Donggeun Kim 已提交
1021 1022 1023 1024 1025
	if (ret) {
		dev_err(&pdev->dev, "Failed to initialize TMU\n");
		goto err_clk;
	}

1026 1027 1028 1029 1030 1031
	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);
		goto err_clk;
	}
1032

1033
	exynos_tmu_control(pdev, true);
D
Donggeun Kim 已提交
1034 1035
	return 0;
err_clk:
1036
	clk_unprepare(data->clk);
1037 1038 1039
err_clk_sec:
	if (!IS_ERR(data->clk_sec))
		clk_unprepare(data->clk_sec);
1040 1041 1042
err_sensor:
	thermal_zone_of_sensor_unregister(&pdev->dev, data->tzd);

D
Donggeun Kim 已提交
1043 1044 1045
	return ret;
}

1046
static int exynos_tmu_remove(struct platform_device *pdev)
D
Donggeun Kim 已提交
1047
{
1048
	struct exynos_tmu_data *data = platform_get_drvdata(pdev);
1049
	struct thermal_zone_device *tzd = data->tzd;
D
Donggeun Kim 已提交
1050

1051
	thermal_zone_of_sensor_unregister(&pdev->dev, tzd);
1052 1053
	exynos_tmu_control(pdev, false);

1054
	clk_unprepare(data->clk);
1055 1056
	if (!IS_ERR(data->clk_sec))
		clk_unprepare(data->clk_sec);
D
Donggeun Kim 已提交
1057

1058 1059 1060
	if (!IS_ERR(data->regulator))
		regulator_disable(data->regulator);

D
Donggeun Kim 已提交
1061 1062 1063
	return 0;
}

1064
#ifdef CONFIG_PM_SLEEP
1065
static int exynos_tmu_suspend(struct device *dev)
D
Donggeun Kim 已提交
1066
{
1067
	exynos_tmu_control(to_platform_device(dev), false);
D
Donggeun Kim 已提交
1068 1069 1070 1071

	return 0;
}

1072
static int exynos_tmu_resume(struct device *dev)
D
Donggeun Kim 已提交
1073
{
1074 1075
	struct platform_device *pdev = to_platform_device(dev);

1076 1077
	exynos_tmu_initialize(pdev);
	exynos_tmu_control(pdev, true);
D
Donggeun Kim 已提交
1078 1079 1080

	return 0;
}
1081

1082 1083 1084
static SIMPLE_DEV_PM_OPS(exynos_tmu_pm,
			 exynos_tmu_suspend, exynos_tmu_resume);
#define EXYNOS_TMU_PM	(&exynos_tmu_pm)
D
Donggeun Kim 已提交
1085
#else
1086
#define EXYNOS_TMU_PM	NULL
D
Donggeun Kim 已提交
1087 1088
#endif

1089
static struct platform_driver exynos_tmu_driver = {
D
Donggeun Kim 已提交
1090
	.driver = {
1091 1092
		.name   = "exynos-tmu",
		.pm     = EXYNOS_TMU_PM,
1093
		.of_match_table = exynos_tmu_match,
D
Donggeun Kim 已提交
1094
	},
1095
	.probe = exynos_tmu_probe,
1096
	.remove	= exynos_tmu_remove,
D
Donggeun Kim 已提交
1097 1098
};

1099
module_platform_driver(exynos_tmu_driver);
D
Donggeun Kim 已提交
1100

1101
MODULE_DESCRIPTION("EXYNOS TMU Driver");
D
Donggeun Kim 已提交
1102 1103
MODULE_AUTHOR("Donggeun Kim <dg77.kim@samsung.com>");
MODULE_LICENSE("GPL");
1104
MODULE_ALIAS("platform:exynos-tmu");