sec-irq.c 11.0 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>
17
#include <linux/mfd/samsung/s5m-core.h>
S
Sangbeom Kim 已提交
18

19
struct sec_irq_data {
S
Sangbeom Kim 已提交
20 21 22 23
	int reg;
	int mask;
};

24
static struct sec_irq_data s5m8767_irqs[] = {
S
Sangbeom Kim 已提交
25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94
	[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,
	},
};

95
static struct sec_irq_data s5m8763_irqs[] = {
S
Sangbeom Kim 已提交
96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169
	[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,
	},
};

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

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

180
	mutex_lock(&sec_pmic->irqlock);
S
Sangbeom Kim 已提交
181 182 183 184
}

static void s5m8767_irq_sync_unlock(struct irq_data *data)
{
185
	struct sec_pmic_dev *sec_pmic = irq_data_get_irq_chip_data(data);
S
Sangbeom Kim 已提交
186 187
	int i;

188 189 190 191 192
	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 已提交
193 194 195
		}
	}

196
	mutex_unlock(&sec_pmic->irqlock);
S
Sangbeom Kim 已提交
197 198 199 200
}

static void s5m8767_irq_unmask(struct irq_data *data)
{
201 202
	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 已提交
203 204
							       data->irq);

205
	sec_pmic->irq_masks_cur[irq_data->reg - 1] &= ~irq_data->mask;
S
Sangbeom Kim 已提交
206 207 208 209
}

static void s5m8767_irq_mask(struct irq_data *data)
{
210 211
	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 已提交
212 213
							       data->irq);

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

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,
};

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

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

235
	mutex_lock(&sec_pmic->irqlock);
S
Sangbeom Kim 已提交
236 237 238 239
}

static void s5m8763_irq_sync_unlock(struct irq_data *data)
{
240
	struct sec_pmic_dev *sec_pmic = irq_data_get_irq_chip_data(data);
S
Sangbeom Kim 已提交
241 242
	int i;

243 244 245 246 247
	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 已提交
248 249 250
		}
	}

251
	mutex_unlock(&sec_pmic->irqlock);
S
Sangbeom Kim 已提交
252 253 254 255
}

static void s5m8763_irq_unmask(struct irq_data *data)
{
256 257
	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 已提交
258 259
							       data->irq);

260
	sec_pmic->irq_masks_cur[irq_data->reg - 1] &= ~irq_data->mask;
S
Sangbeom Kim 已提交
261 262 263 264
}

static void s5m8763_irq_mask(struct irq_data *data)
{
265 266
	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 已提交
267 268
							       data->irq);

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

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)
{
283
	struct sec_pmic_dev *sec_pmic = data;
S
Sangbeom Kim 已提交
284 285 286 287 288
	u8 irq_reg[NUM_IRQ_REGS-1];
	int ret;
	int i;


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

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

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

	return IRQ_HANDLED;
}

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

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

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

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

	return IRQ_HANDLED;
}

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

		}
	}
	return 0;
}

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

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

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

375
	mutex_init(&sec_pmic->irqlock);
S
Sangbeom Kim 已提交
376 377 378 379

	switch (type) {
	case S5M8763X:
		for (i = 0; i < NUM_IRQ_REGS; i++) {
380 381 382
			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 已提交
383 384 385
						0xff);
		}

386 387
		sec_reg_write(sec_pmic, S5M8763_REG_STATUSM1, 0xff);
		sec_reg_write(sec_pmic, S5M8763_REG_STATUSM2, 0xff);
S
Sangbeom Kim 已提交
388 389

		for (i = 0; i < S5M8763_IRQ_NR; i++) {
390 391
			cur_irq = i + sec_pmic->irq_base;
			irq_set_chip_data(cur_irq, sec_pmic);
S
Sangbeom Kim 已提交
392 393 394 395 396 397 398 399 400 401
			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
		}

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

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

455
	if (!sec_pmic->ono)
S
Sangbeom Kim 已提交
456 457 458 459
		return 0;

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

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

	return 0;
}

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

493 494
	if (sec_pmic->irq)
		free_irq(sec_pmic->irq, sec_pmic);
S
Sangbeom Kim 已提交
495
}