sec-irq.c 11.1 KB
Newer Older
S
Sangbeom Kim 已提交
1
/*
2
 * sec-irq.c
S
Sangbeom Kim 已提交
3 4 5 6 7 8 9 10 11 12 13 14 15 16
 *
 * Copyright (c) 2011 Samsung Electronics Co., Ltd
 *              http://www.samsung.com
 *
 *  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.
 *
 */

#include <linux/device.h>
#include <linux/interrupt.h>
#include <linux/irq.h>
S
Sangbeom Kim 已提交
17 18 19 20
#include <linux/mfd/samsung/core.h>
#include <linux/mfd/samsung/irq.h>
#include <linux/mfd/samsung/s5m8763.h>
#include <linux/mfd/samsung/s5m8767.h>
S
Sangbeom Kim 已提交
21

22
struct sec_irq_data {
S
Sangbeom Kim 已提交
23 24 25 26
	int reg;
	int mask;
};

27
static struct sec_irq_data s5m8767_irqs[] = {
S
Sangbeom Kim 已提交
28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97
	[S5M8767_IRQ_PWRR] = {
		.reg = 1,
		.mask = S5M8767_IRQ_PWRR_MASK,
	},
	[S5M8767_IRQ_PWRF] = {
		.reg = 1,
		.mask = S5M8767_IRQ_PWRF_MASK,
	},
	[S5M8767_IRQ_PWR1S] = {
		.reg = 1,
		.mask = S5M8767_IRQ_PWR1S_MASK,
	},
	[S5M8767_IRQ_JIGR] = {
		.reg = 1,
		.mask = S5M8767_IRQ_JIGR_MASK,
	},
	[S5M8767_IRQ_JIGF] = {
		.reg = 1,
		.mask = S5M8767_IRQ_JIGF_MASK,
	},
	[S5M8767_IRQ_LOWBAT2] = {
		.reg = 1,
		.mask = S5M8767_IRQ_LOWBAT2_MASK,
	},
	[S5M8767_IRQ_LOWBAT1] = {
		.reg = 1,
		.mask = S5M8767_IRQ_LOWBAT1_MASK,
	},
	[S5M8767_IRQ_MRB] = {
		.reg = 2,
		.mask = S5M8767_IRQ_MRB_MASK,
	},
	[S5M8767_IRQ_DVSOK2] = {
		.reg = 2,
		.mask = S5M8767_IRQ_DVSOK2_MASK,
	},
	[S5M8767_IRQ_DVSOK3] = {
		.reg = 2,
		.mask = S5M8767_IRQ_DVSOK3_MASK,
	},
	[S5M8767_IRQ_DVSOK4] = {
		.reg = 2,
		.mask = S5M8767_IRQ_DVSOK4_MASK,
	},
	[S5M8767_IRQ_RTC60S] = {
		.reg = 3,
		.mask = S5M8767_IRQ_RTC60S_MASK,
	},
	[S5M8767_IRQ_RTCA1] = {
		.reg = 3,
		.mask = S5M8767_IRQ_RTCA1_MASK,
	},
	[S5M8767_IRQ_RTCA2] = {
		.reg = 3,
		.mask = S5M8767_IRQ_RTCA2_MASK,
	},
	[S5M8767_IRQ_SMPL] = {
		.reg = 3,
		.mask = S5M8767_IRQ_SMPL_MASK,
	},
	[S5M8767_IRQ_RTC1S] = {
		.reg = 3,
		.mask = S5M8767_IRQ_RTC1S_MASK,
	},
	[S5M8767_IRQ_WTSR] = {
		.reg = 3,
		.mask = S5M8767_IRQ_WTSR_MASK,
	},
};

98
static struct sec_irq_data s5m8763_irqs[] = {
S
Sangbeom Kim 已提交
99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172
	[S5M8763_IRQ_DCINF] = {
		.reg = 1,
		.mask = S5M8763_IRQ_DCINF_MASK,
	},
	[S5M8763_IRQ_DCINR] = {
		.reg = 1,
		.mask = S5M8763_IRQ_DCINR_MASK,
	},
	[S5M8763_IRQ_JIGF] = {
		.reg = 1,
		.mask = S5M8763_IRQ_JIGF_MASK,
	},
	[S5M8763_IRQ_JIGR] = {
		.reg = 1,
		.mask = S5M8763_IRQ_JIGR_MASK,
	},
	[S5M8763_IRQ_PWRONF] = {
		.reg = 1,
		.mask = S5M8763_IRQ_PWRONF_MASK,
	},
	[S5M8763_IRQ_PWRONR] = {
		.reg = 1,
		.mask = S5M8763_IRQ_PWRONR_MASK,
	},
	[S5M8763_IRQ_WTSREVNT] = {
		.reg = 2,
		.mask = S5M8763_IRQ_WTSREVNT_MASK,
	},
	[S5M8763_IRQ_SMPLEVNT] = {
		.reg = 2,
		.mask = S5M8763_IRQ_SMPLEVNT_MASK,
	},
	[S5M8763_IRQ_ALARM1] = {
		.reg = 2,
		.mask = S5M8763_IRQ_ALARM1_MASK,
	},
	[S5M8763_IRQ_ALARM0] = {
		.reg = 2,
		.mask = S5M8763_IRQ_ALARM0_MASK,
	},
	[S5M8763_IRQ_ONKEY1S] = {
		.reg = 3,
		.mask = S5M8763_IRQ_ONKEY1S_MASK,
	},
	[S5M8763_IRQ_TOPOFFR] = {
		.reg = 3,
		.mask = S5M8763_IRQ_TOPOFFR_MASK,
	},
	[S5M8763_IRQ_DCINOVPR] = {
		.reg = 3,
		.mask = S5M8763_IRQ_DCINOVPR_MASK,
	},
	[S5M8763_IRQ_CHGRSTF] = {
		.reg = 3,
		.mask = S5M8763_IRQ_CHGRSTF_MASK,
	},
	[S5M8763_IRQ_DONER] = {
		.reg = 3,
		.mask = S5M8763_IRQ_DONER_MASK,
	},
	[S5M8763_IRQ_CHGFAULT] = {
		.reg = 3,
		.mask = S5M8763_IRQ_CHGFAULT_MASK,
	},
	[S5M8763_IRQ_LOBAT1] = {
		.reg = 4,
		.mask = S5M8763_IRQ_LOBAT1_MASK,
	},
	[S5M8763_IRQ_LOBAT2] = {
		.reg = 4,
		.mask = S5M8763_IRQ_LOBAT2_MASK,
	},
};

173 174
static inline struct sec_irq_data *
irq_to_s5m8767_irq(struct sec_pmic_dev *sec_pmic, int irq)
S
Sangbeom Kim 已提交
175
{
176
	return &s5m8767_irqs[irq - sec_pmic->irq_base];
S
Sangbeom Kim 已提交
177 178 179 180
}

static void s5m8767_irq_lock(struct irq_data *data)
{
181
	struct sec_pmic_dev *sec_pmic = irq_data_get_irq_chip_data(data);
S
Sangbeom Kim 已提交
182

183
	mutex_lock(&sec_pmic->irqlock);
S
Sangbeom Kim 已提交
184 185 186 187
}

static void s5m8767_irq_sync_unlock(struct irq_data *data)
{
188
	struct sec_pmic_dev *sec_pmic = irq_data_get_irq_chip_data(data);
S
Sangbeom Kim 已提交
189 190
	int i;

191 192 193 194 195
	for (i = 0; i < ARRAY_SIZE(sec_pmic->irq_masks_cur); i++) {
		if (sec_pmic->irq_masks_cur[i] != sec_pmic->irq_masks_cache[i]) {
			sec_pmic->irq_masks_cache[i] = sec_pmic->irq_masks_cur[i];
			sec_reg_write(sec_pmic, S5M8767_REG_INT1M + i,
					sec_pmic->irq_masks_cur[i]);
S
Sangbeom Kim 已提交
196 197 198
		}
	}

199
	mutex_unlock(&sec_pmic->irqlock);
S
Sangbeom Kim 已提交
200 201 202 203
}

static void s5m8767_irq_unmask(struct irq_data *data)
{
204 205
	struct sec_pmic_dev *sec_pmic = irq_data_get_irq_chip_data(data);
	struct sec_irq_data *irq_data = irq_to_s5m8767_irq(sec_pmic,
S
Sangbeom Kim 已提交
206 207
							       data->irq);

208
	sec_pmic->irq_masks_cur[irq_data->reg - 1] &= ~irq_data->mask;
S
Sangbeom Kim 已提交
209 210 211 212
}

static void s5m8767_irq_mask(struct irq_data *data)
{
213 214
	struct sec_pmic_dev *sec_pmic = irq_data_get_irq_chip_data(data);
	struct sec_irq_data *irq_data = irq_to_s5m8767_irq(sec_pmic,
S
Sangbeom Kim 已提交
215 216
							       data->irq);

217
	sec_pmic->irq_masks_cur[irq_data->reg - 1] |= irq_data->mask;
S
Sangbeom Kim 已提交
218 219 220 221 222 223 224 225 226 227
}

static struct irq_chip s5m8767_irq_chip = {
	.name = "s5m8767",
	.irq_bus_lock = s5m8767_irq_lock,
	.irq_bus_sync_unlock = s5m8767_irq_sync_unlock,
	.irq_mask = s5m8767_irq_mask,
	.irq_unmask = s5m8767_irq_unmask,
};

228 229
static inline struct sec_irq_data *
irq_to_s5m8763_irq(struct sec_pmic_dev *sec_pmic, int irq)
S
Sangbeom Kim 已提交
230
{
231
	return &s5m8763_irqs[irq - sec_pmic->irq_base];
S
Sangbeom Kim 已提交
232 233 234 235
}

static void s5m8763_irq_lock(struct irq_data *data)
{
236
	struct sec_pmic_dev *sec_pmic = irq_data_get_irq_chip_data(data);
S
Sangbeom Kim 已提交
237

238
	mutex_lock(&sec_pmic->irqlock);
S
Sangbeom Kim 已提交
239 240 241 242
}

static void s5m8763_irq_sync_unlock(struct irq_data *data)
{
243
	struct sec_pmic_dev *sec_pmic = irq_data_get_irq_chip_data(data);
S
Sangbeom Kim 已提交
244 245
	int i;

246 247 248 249 250
	for (i = 0; i < ARRAY_SIZE(sec_pmic->irq_masks_cur); i++) {
		if (sec_pmic->irq_masks_cur[i] != sec_pmic->irq_masks_cache[i]) {
			sec_pmic->irq_masks_cache[i] = sec_pmic->irq_masks_cur[i];
			sec_reg_write(sec_pmic, S5M8763_REG_IRQM1 + i,
					sec_pmic->irq_masks_cur[i]);
S
Sangbeom Kim 已提交
251 252 253
		}
	}

254
	mutex_unlock(&sec_pmic->irqlock);
S
Sangbeom Kim 已提交
255 256 257 258
}

static void s5m8763_irq_unmask(struct irq_data *data)
{
259 260
	struct sec_pmic_dev *sec_pmic = irq_data_get_irq_chip_data(data);
	struct sec_irq_data *irq_data = irq_to_s5m8763_irq(sec_pmic,
S
Sangbeom Kim 已提交
261 262
							       data->irq);

263
	sec_pmic->irq_masks_cur[irq_data->reg - 1] &= ~irq_data->mask;
S
Sangbeom Kim 已提交
264 265 266 267
}

static void s5m8763_irq_mask(struct irq_data *data)
{
268 269
	struct sec_pmic_dev *sec_pmic = irq_data_get_irq_chip_data(data);
	struct sec_irq_data *irq_data = irq_to_s5m8763_irq(sec_pmic,
S
Sangbeom Kim 已提交
270 271
							       data->irq);

272
	sec_pmic->irq_masks_cur[irq_data->reg - 1] |= irq_data->mask;
S
Sangbeom Kim 已提交
273 274 275 276 277 278 279 280 281 282 283 284 285
}

static struct irq_chip s5m8763_irq_chip = {
	.name = "s5m8763",
	.irq_bus_lock = s5m8763_irq_lock,
	.irq_bus_sync_unlock = s5m8763_irq_sync_unlock,
	.irq_mask = s5m8763_irq_mask,
	.irq_unmask = s5m8763_irq_unmask,
};


static irqreturn_t s5m8767_irq_thread(int irq, void *data)
{
286
	struct sec_pmic_dev *sec_pmic = data;
S
Sangbeom Kim 已提交
287 288 289 290 291
	u8 irq_reg[NUM_IRQ_REGS-1];
	int ret;
	int i;


292
	ret = sec_bulk_read(sec_pmic, S5M8767_REG_INT1,
S
Sangbeom Kim 已提交
293 294
				NUM_IRQ_REGS - 1, irq_reg);
	if (ret < 0) {
295
		dev_err(sec_pmic->dev, "Failed to read interrupt register: %d\n",
S
Sangbeom Kim 已提交
296 297 298 299 300
				ret);
		return IRQ_NONE;
	}

	for (i = 0; i < NUM_IRQ_REGS - 1; i++)
301
		irq_reg[i] &= ~sec_pmic->irq_masks_cur[i];
S
Sangbeom Kim 已提交
302 303 304

	for (i = 0; i < S5M8767_IRQ_NR; i++) {
		if (irq_reg[s5m8767_irqs[i].reg - 1] & s5m8767_irqs[i].mask)
305
			handle_nested_irq(sec_pmic->irq_base + i);
S
Sangbeom Kim 已提交
306 307 308 309 310 311 312
	}

	return IRQ_HANDLED;
}

static irqreturn_t s5m8763_irq_thread(int irq, void *data)
{
313
	struct sec_pmic_dev *sec_pmic = data;
S
Sangbeom Kim 已提交
314 315 316 317
	u8 irq_reg[NUM_IRQ_REGS];
	int ret;
	int i;

318
	ret = sec_bulk_read(sec_pmic, S5M8763_REG_IRQ1,
S
Sangbeom Kim 已提交
319 320
				NUM_IRQ_REGS, irq_reg);
	if (ret < 0) {
321
		dev_err(sec_pmic->dev, "Failed to read interrupt register: %d\n",
S
Sangbeom Kim 已提交
322 323 324 325 326
				ret);
		return IRQ_NONE;
	}

	for (i = 0; i < NUM_IRQ_REGS; i++)
327
		irq_reg[i] &= ~sec_pmic->irq_masks_cur[i];
S
Sangbeom Kim 已提交
328 329 330

	for (i = 0; i < S5M8763_IRQ_NR; i++) {
		if (irq_reg[s5m8763_irqs[i].reg - 1] & s5m8763_irqs[i].mask)
331
			handle_nested_irq(sec_pmic->irq_base + i);
S
Sangbeom Kim 已提交
332 333 334 335 336
	}

	return IRQ_HANDLED;
}

337
int sec_irq_resume(struct sec_pmic_dev *sec_pmic)
S
Sangbeom Kim 已提交
338
{
339 340
	if (sec_pmic->irq && sec_pmic->irq_base) {
		switch (sec_pmic->device_type) {
S
Sangbeom Kim 已提交
341
		case S5M8763X:
342
			s5m8763_irq_thread(sec_pmic->irq_base, sec_pmic);
S
Sangbeom Kim 已提交
343 344
			break;
		case S5M8767X:
345
			s5m8767_irq_thread(sec_pmic->irq_base, sec_pmic);
S
Sangbeom Kim 已提交
346 347
			break;
		default:
348
			dev_err(sec_pmic->dev,
349
				"Unknown device type %d\n",
350
				sec_pmic->device_type);
351
			return -EINVAL;
S
Sangbeom Kim 已提交
352 353 354 355 356 357

		}
	}
	return 0;
}

358
int sec_irq_init(struct sec_pmic_dev *sec_pmic)
S
Sangbeom Kim 已提交
359 360 361 362
{
	int i;
	int cur_irq;
	int ret = 0;
363
	int type = sec_pmic->device_type;
S
Sangbeom Kim 已提交
364

365 366
	if (!sec_pmic->irq) {
		dev_warn(sec_pmic->dev,
S
Sangbeom Kim 已提交
367
			 "No interrupt specified, no interrupts\n");
368
		sec_pmic->irq_base = 0;
S
Sangbeom Kim 已提交
369 370 371
		return 0;
	}

372 373
	if (!sec_pmic->irq_base) {
		dev_err(sec_pmic->dev,
S
Sangbeom Kim 已提交
374 375 376 377
			"No interrupt base specified, no interrupts\n");
		return 0;
	}

378
	mutex_init(&sec_pmic->irqlock);
S
Sangbeom Kim 已提交
379 380 381 382

	switch (type) {
	case S5M8763X:
		for (i = 0; i < NUM_IRQ_REGS; i++) {
383 384 385
			sec_pmic->irq_masks_cur[i] = 0xff;
			sec_pmic->irq_masks_cache[i] = 0xff;
			sec_reg_write(sec_pmic, S5M8763_REG_IRQM1 + i,
S
Sangbeom Kim 已提交
386 387 388
						0xff);
		}

389 390
		sec_reg_write(sec_pmic, S5M8763_REG_STATUSM1, 0xff);
		sec_reg_write(sec_pmic, S5M8763_REG_STATUSM2, 0xff);
S
Sangbeom Kim 已提交
391 392

		for (i = 0; i < S5M8763_IRQ_NR; i++) {
393 394
			cur_irq = i + sec_pmic->irq_base;
			irq_set_chip_data(cur_irq, sec_pmic);
S
Sangbeom Kim 已提交
395 396 397 398 399 400 401 402 403 404
			irq_set_chip_and_handler(cur_irq, &s5m8763_irq_chip,
						 handle_edge_irq);
			irq_set_nested_thread(cur_irq, 1);
#ifdef CONFIG_ARM
			set_irq_flags(cur_irq, IRQF_VALID);
#else
			irq_set_noprobe(cur_irq);
#endif
		}

405
		ret = request_threaded_irq(sec_pmic->irq, NULL,
S
Sangbeom Kim 已提交
406 407
					s5m8763_irq_thread,
					IRQF_TRIGGER_FALLING | IRQF_ONESHOT,
408
					"sec-pmic-irq", sec_pmic);
S
Sangbeom Kim 已提交
409
		if (ret) {
410 411
			dev_err(sec_pmic->dev, "Failed to request IRQ %d: %d\n",
				sec_pmic->irq, ret);
S
Sangbeom Kim 已提交
412 413 414 415 416
			return ret;
		}
		break;
	case S5M8767X:
		for (i = 0; i < NUM_IRQ_REGS - 1; i++) {
417 418 419
			sec_pmic->irq_masks_cur[i] = 0xff;
			sec_pmic->irq_masks_cache[i] = 0xff;
			sec_reg_write(sec_pmic, S5M8767_REG_INT1M + i,
S
Sangbeom Kim 已提交
420 421 422
						0xff);
		}
		for (i = 0; i < S5M8767_IRQ_NR; i++) {
423 424
			cur_irq = i + sec_pmic->irq_base;
			irq_set_chip_data(cur_irq, sec_pmic);
S
Sangbeom Kim 已提交
425
			if (ret) {
426
				dev_err(sec_pmic->dev,
S
Sangbeom Kim 已提交
427
					"Failed to irq_set_chip_data %d: %d\n",
428
					sec_pmic->irq, ret);
S
Sangbeom Kim 已提交
429 430 431 432 433 434 435 436 437 438 439 440 441
				return ret;
			}

			irq_set_chip_and_handler(cur_irq, &s5m8767_irq_chip,
						 handle_edge_irq);
			irq_set_nested_thread(cur_irq, 1);
#ifdef CONFIG_ARM
			set_irq_flags(cur_irq, IRQF_VALID);
#else
			irq_set_noprobe(cur_irq);
#endif
		}

442
		ret = request_threaded_irq(sec_pmic->irq, NULL,
S
Sangbeom Kim 已提交
443 444
					   s5m8767_irq_thread,
					   IRQF_TRIGGER_FALLING | IRQF_ONESHOT,
445
					   "sec-pmic-irq", sec_pmic);
S
Sangbeom Kim 已提交
446
		if (ret) {
447 448
			dev_err(sec_pmic->dev, "Failed to request IRQ %d: %d\n",
				sec_pmic->irq, ret);
S
Sangbeom Kim 已提交
449 450 451 452
			return ret;
		}
		break;
	default:
453 454
		dev_err(sec_pmic->dev,
			"Unknown device type %d\n", sec_pmic->device_type);
455
		return -EINVAL;
S
Sangbeom Kim 已提交
456 457
	}

458
	if (!sec_pmic->ono)
S
Sangbeom Kim 已提交
459 460 461 462
		return 0;

	switch (type) {
	case S5M8763X:
463
		ret = request_threaded_irq(sec_pmic->ono, NULL,
S
Sangbeom Kim 已提交
464 465 466
						s5m8763_irq_thread,
						IRQF_TRIGGER_FALLING |
						IRQF_TRIGGER_RISING |
467 468
						IRQF_ONESHOT, "sec_pmic-ono",
						sec_pmic);
S
Sangbeom Kim 已提交
469 470
		break;
	case S5M8767X:
471
		ret = request_threaded_irq(sec_pmic->ono, NULL,
S
Sangbeom Kim 已提交
472 473 474
					s5m8767_irq_thread,
					IRQF_TRIGGER_FALLING |
					IRQF_TRIGGER_RISING |
475
					IRQF_ONESHOT, "sec_pmic-ono", sec_pmic);
S
Sangbeom Kim 已提交
476 477
		break;
	default:
478
		ret = -EINVAL;
S
Sangbeom Kim 已提交
479 480 481
		break;
	}

482
	if (ret) {
483 484
		dev_err(sec_pmic->dev, "Failed to request IRQ %d: %d\n",
			sec_pmic->ono, ret);
485 486
		return ret;
	}
S
Sangbeom Kim 已提交
487 488 489 490

	return 0;
}

491
void sec_irq_exit(struct sec_pmic_dev *sec_pmic)
S
Sangbeom Kim 已提交
492
{
493 494
	if (sec_pmic->ono)
		free_irq(sec_pmic->ono, sec_pmic);
S
Sangbeom Kim 已提交
495

496 497
	if (sec_pmic->irq)
		free_irq(sec_pmic->irq, sec_pmic);
S
Sangbeom Kim 已提交
498
}