| .. | .. |
|---|
| 1 | +// SPDX-License-Identifier: GPL-2.0-only |
|---|
| 1 | 2 | /* linux/arch/arm/mach-exynos4/mct.c |
|---|
| 2 | 3 | * |
|---|
| 3 | 4 | * Copyright (c) 2011 Samsung Electronics Co., Ltd. |
|---|
| 4 | 5 | * http://www.samsung.com |
|---|
| 5 | 6 | * |
|---|
| 6 | | - * EXYNOS4 MCT(Multi-Core Timer) support |
|---|
| 7 | | - * |
|---|
| 8 | | - * This program is free software; you can redistribute it and/or modify |
|---|
| 9 | | - * it under the terms of the GNU General Public License version 2 as |
|---|
| 10 | | - * published by the Free Software Foundation. |
|---|
| 7 | + * Exynos4 MCT(Multi-Core Timer) support |
|---|
| 11 | 8 | */ |
|---|
| 12 | 9 | |
|---|
| 13 | | -#include <linux/sched.h> |
|---|
| 14 | 10 | #include <linux/interrupt.h> |
|---|
| 15 | 11 | #include <linux/irq.h> |
|---|
| 16 | 12 | #include <linux/err.h> |
|---|
| 17 | 13 | #include <linux/clk.h> |
|---|
| 18 | 14 | #include <linux/clockchips.h> |
|---|
| 19 | 15 | #include <linux/cpu.h> |
|---|
| 20 | | -#include <linux/platform_device.h> |
|---|
| 21 | 16 | #include <linux/delay.h> |
|---|
| 22 | 17 | #include <linux/percpu.h> |
|---|
| 23 | 18 | #include <linux/of.h> |
|---|
| .. | .. |
|---|
| 334 | 329 | return IRQ_HANDLED; |
|---|
| 335 | 330 | } |
|---|
| 336 | 331 | |
|---|
| 337 | | -static struct irqaction mct_comp_event_irq = { |
|---|
| 338 | | - .name = "mct_comp_irq", |
|---|
| 339 | | - .flags = IRQF_TIMER | IRQF_IRQPOLL, |
|---|
| 340 | | - .handler = exynos4_mct_comp_isr, |
|---|
| 341 | | - .dev_id = &mct_comp_device, |
|---|
| 342 | | -}; |
|---|
| 343 | | - |
|---|
| 344 | 332 | static int exynos4_clockevent_init(void) |
|---|
| 345 | 333 | { |
|---|
| 346 | 334 | mct_comp_device.cpumask = cpumask_of(0); |
|---|
| 347 | 335 | clockevents_config_and_register(&mct_comp_device, clk_rate, |
|---|
| 348 | 336 | 0xf, 0xffffffff); |
|---|
| 349 | | - setup_irq(mct_irqs[MCT_G0_IRQ], &mct_comp_event_irq); |
|---|
| 337 | + if (request_irq(mct_irqs[MCT_G0_IRQ], exynos4_mct_comp_isr, |
|---|
| 338 | + IRQF_TIMER | IRQF_IRQPOLL, "mct_comp_irq", |
|---|
| 339 | + &mct_comp_device)) |
|---|
| 340 | + pr_err("%s: request_irq() failed\n", "mct_comp_irq"); |
|---|
| 350 | 341 | |
|---|
| 351 | 342 | return 0; |
|---|
| 352 | 343 | } |
|---|
| .. | .. |
|---|
| 503 | 494 | return 0; |
|---|
| 504 | 495 | } |
|---|
| 505 | 496 | |
|---|
| 506 | | -static int __init exynos4_timer_resources(struct device_node *np, void __iomem *base) |
|---|
| 497 | +static int __init exynos4_timer_resources(struct device_node *np) |
|---|
| 507 | 498 | { |
|---|
| 508 | | - int err, cpu; |
|---|
| 509 | 499 | struct clk *mct_clk, *tick_clk; |
|---|
| 510 | 500 | |
|---|
| 511 | | - tick_clk = np ? of_clk_get_by_name(np, "fin_pll") : |
|---|
| 512 | | - clk_get(NULL, "fin_pll"); |
|---|
| 501 | + reg_base = of_iomap(np, 0); |
|---|
| 502 | + if (!reg_base) |
|---|
| 503 | + panic("%s: unable to ioremap mct address space\n", __func__); |
|---|
| 504 | + |
|---|
| 505 | + tick_clk = of_clk_get_by_name(np, "fin_pll"); |
|---|
| 513 | 506 | if (IS_ERR(tick_clk)) |
|---|
| 514 | 507 | panic("%s: unable to determine tick clock rate\n", __func__); |
|---|
| 515 | 508 | clk_rate = clk_get_rate(tick_clk); |
|---|
| 516 | 509 | |
|---|
| 517 | | - mct_clk = np ? of_clk_get_by_name(np, "mct") : clk_get(NULL, "mct"); |
|---|
| 510 | + mct_clk = of_clk_get_by_name(np, "mct"); |
|---|
| 518 | 511 | if (IS_ERR(mct_clk)) |
|---|
| 519 | 512 | panic("%s: unable to retrieve mct clock instance\n", __func__); |
|---|
| 520 | 513 | clk_prepare_enable(mct_clk); |
|---|
| 521 | 514 | |
|---|
| 522 | | - reg_base = base; |
|---|
| 523 | | - if (!reg_base) |
|---|
| 524 | | - panic("%s: unable to ioremap mct address space\n", __func__); |
|---|
| 515 | + return 0; |
|---|
| 516 | +} |
|---|
| 517 | + |
|---|
| 518 | +static int __init exynos4_timer_interrupts(struct device_node *np, |
|---|
| 519 | + unsigned int int_type) |
|---|
| 520 | +{ |
|---|
| 521 | + int nr_irqs, i, err, cpu; |
|---|
| 522 | + |
|---|
| 523 | + mct_int_type = int_type; |
|---|
| 524 | + |
|---|
| 525 | + /* This driver uses only one global timer interrupt */ |
|---|
| 526 | + mct_irqs[MCT_G0_IRQ] = irq_of_parse_and_map(np, MCT_G0_IRQ); |
|---|
| 527 | + |
|---|
| 528 | + /* |
|---|
| 529 | + * Find out the number of local irqs specified. The local |
|---|
| 530 | + * timer irqs are specified after the four global timer |
|---|
| 531 | + * irqs are specified. |
|---|
| 532 | + */ |
|---|
| 533 | + nr_irqs = of_irq_count(np); |
|---|
| 534 | + if (nr_irqs > ARRAY_SIZE(mct_irqs)) { |
|---|
| 535 | + pr_err("exynos-mct: too many (%d) interrupts configured in DT\n", |
|---|
| 536 | + nr_irqs); |
|---|
| 537 | + nr_irqs = ARRAY_SIZE(mct_irqs); |
|---|
| 538 | + } |
|---|
| 539 | + for (i = MCT_L0_IRQ; i < nr_irqs; i++) |
|---|
| 540 | + mct_irqs[i] = irq_of_parse_and_map(np, i); |
|---|
| 525 | 541 | |
|---|
| 526 | 542 | if (mct_int_type == MCT_INT_PPI) { |
|---|
| 527 | 543 | |
|---|
| .. | .. |
|---|
| 532 | 548 | mct_irqs[MCT_L0_IRQ], err); |
|---|
| 533 | 549 | } else { |
|---|
| 534 | 550 | for_each_possible_cpu(cpu) { |
|---|
| 535 | | - int mct_irq = mct_irqs[MCT_L0_IRQ + cpu]; |
|---|
| 551 | + int mct_irq; |
|---|
| 536 | 552 | struct mct_clock_event_device *pcpu_mevt = |
|---|
| 537 | 553 | per_cpu_ptr(&percpu_mct_tick, cpu); |
|---|
| 538 | 554 | |
|---|
| 539 | 555 | pcpu_mevt->evt.irq = -1; |
|---|
| 556 | + if (MCT_L0_IRQ + cpu >= ARRAY_SIZE(mct_irqs)) |
|---|
| 557 | + break; |
|---|
| 558 | + mct_irq = mct_irqs[MCT_L0_IRQ + cpu]; |
|---|
| 540 | 559 | |
|---|
| 541 | 560 | irq_set_status_flags(mct_irq, IRQ_NOAUTOEN); |
|---|
| 542 | 561 | if (request_irq(mct_irq, |
|---|
| .. | .. |
|---|
| 581 | 600 | |
|---|
| 582 | 601 | static int __init mct_init_dt(struct device_node *np, unsigned int int_type) |
|---|
| 583 | 602 | { |
|---|
| 584 | | - u32 nr_irqs, i; |
|---|
| 585 | 603 | int ret; |
|---|
| 586 | 604 | |
|---|
| 587 | | - mct_int_type = int_type; |
|---|
| 605 | + ret = exynos4_timer_resources(np); |
|---|
| 606 | + if (ret) |
|---|
| 607 | + return ret; |
|---|
| 588 | 608 | |
|---|
| 589 | | - /* This driver uses only one global timer interrupt */ |
|---|
| 590 | | - mct_irqs[MCT_G0_IRQ] = irq_of_parse_and_map(np, MCT_G0_IRQ); |
|---|
| 591 | | - |
|---|
| 592 | | - /* |
|---|
| 593 | | - * Find out the number of local irqs specified. The local |
|---|
| 594 | | - * timer irqs are specified after the four global timer |
|---|
| 595 | | - * irqs are specified. |
|---|
| 596 | | - */ |
|---|
| 597 | | -#ifdef CONFIG_OF |
|---|
| 598 | | - nr_irqs = of_irq_count(np); |
|---|
| 599 | | -#else |
|---|
| 600 | | - nr_irqs = 0; |
|---|
| 601 | | -#endif |
|---|
| 602 | | - for (i = MCT_L0_IRQ; i < nr_irqs; i++) |
|---|
| 603 | | - mct_irqs[i] = irq_of_parse_and_map(np, i); |
|---|
| 604 | | - |
|---|
| 605 | | - ret = exynos4_timer_resources(np, of_iomap(np, 0)); |
|---|
| 609 | + ret = exynos4_timer_interrupts(np, int_type); |
|---|
| 606 | 610 | if (ret) |
|---|
| 607 | 611 | return ret; |
|---|
| 608 | 612 | |
|---|