18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * 64-bit Periodic Interval Timer driver 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright (C) 2019 Microchip Technology Inc. and its subsidiaries 68c2ecf20Sopenharmony_ci * 78c2ecf20Sopenharmony_ci * Author: Claudiu Beznea <claudiu.beznea@microchip.com> 88c2ecf20Sopenharmony_ci */ 98c2ecf20Sopenharmony_ci 108c2ecf20Sopenharmony_ci#include <linux/clk.h> 118c2ecf20Sopenharmony_ci#include <linux/clockchips.h> 128c2ecf20Sopenharmony_ci#include <linux/interrupt.h> 138c2ecf20Sopenharmony_ci#include <linux/of_address.h> 148c2ecf20Sopenharmony_ci#include <linux/of_irq.h> 158c2ecf20Sopenharmony_ci#include <linux/sched_clock.h> 168c2ecf20Sopenharmony_ci#include <linux/slab.h> 178c2ecf20Sopenharmony_ci 188c2ecf20Sopenharmony_ci#define MCHP_PIT64B_CR 0x00 /* Control Register */ 198c2ecf20Sopenharmony_ci#define MCHP_PIT64B_CR_START BIT(0) 208c2ecf20Sopenharmony_ci#define MCHP_PIT64B_CR_SWRST BIT(8) 218c2ecf20Sopenharmony_ci 228c2ecf20Sopenharmony_ci#define MCHP_PIT64B_MR 0x04 /* Mode Register */ 238c2ecf20Sopenharmony_ci#define MCHP_PIT64B_MR_CONT BIT(0) 248c2ecf20Sopenharmony_ci#define MCHP_PIT64B_MR_ONE_SHOT (0) 258c2ecf20Sopenharmony_ci#define MCHP_PIT64B_MR_SGCLK BIT(3) 268c2ecf20Sopenharmony_ci#define MCHP_PIT64B_MR_PRES GENMASK(11, 8) 278c2ecf20Sopenharmony_ci 288c2ecf20Sopenharmony_ci#define MCHP_PIT64B_LSB_PR 0x08 /* LSB Period Register */ 298c2ecf20Sopenharmony_ci 308c2ecf20Sopenharmony_ci#define MCHP_PIT64B_MSB_PR 0x0C /* MSB Period Register */ 318c2ecf20Sopenharmony_ci 328c2ecf20Sopenharmony_ci#define MCHP_PIT64B_IER 0x10 /* Interrupt Enable Register */ 338c2ecf20Sopenharmony_ci#define MCHP_PIT64B_IER_PERIOD BIT(0) 348c2ecf20Sopenharmony_ci 358c2ecf20Sopenharmony_ci#define MCHP_PIT64B_ISR 0x1C /* Interrupt Status Register */ 368c2ecf20Sopenharmony_ci 378c2ecf20Sopenharmony_ci#define MCHP_PIT64B_TLSBR 0x20 /* Timer LSB Register */ 388c2ecf20Sopenharmony_ci 398c2ecf20Sopenharmony_ci#define MCHP_PIT64B_TMSBR 0x24 /* Timer MSB Register */ 408c2ecf20Sopenharmony_ci 418c2ecf20Sopenharmony_ci#define MCHP_PIT64B_PRES_MAX 0x10 428c2ecf20Sopenharmony_ci#define MCHP_PIT64B_LSBMASK GENMASK_ULL(31, 0) 438c2ecf20Sopenharmony_ci#define MCHP_PIT64B_PRES_TO_MODE(p) (MCHP_PIT64B_MR_PRES & ((p) << 8)) 448c2ecf20Sopenharmony_ci#define MCHP_PIT64B_MODE_TO_PRES(m) ((MCHP_PIT64B_MR_PRES & (m)) >> 8) 458c2ecf20Sopenharmony_ci#define MCHP_PIT64B_DEF_CS_FREQ 5000000UL /* 5 MHz */ 468c2ecf20Sopenharmony_ci#define MCHP_PIT64B_DEF_CE_FREQ 32768 /* 32 KHz */ 478c2ecf20Sopenharmony_ci 488c2ecf20Sopenharmony_ci#define MCHP_PIT64B_NAME "pit64b" 498c2ecf20Sopenharmony_ci 508c2ecf20Sopenharmony_ci/** 518c2ecf20Sopenharmony_ci * struct mchp_pit64b_timer - PIT64B timer data structure 528c2ecf20Sopenharmony_ci * @base: base address of PIT64B hardware block 538c2ecf20Sopenharmony_ci * @pclk: PIT64B's peripheral clock 548c2ecf20Sopenharmony_ci * @gclk: PIT64B's generic clock 558c2ecf20Sopenharmony_ci * @mode: precomputed value for mode register 568c2ecf20Sopenharmony_ci */ 578c2ecf20Sopenharmony_cistruct mchp_pit64b_timer { 588c2ecf20Sopenharmony_ci void __iomem *base; 598c2ecf20Sopenharmony_ci struct clk *pclk; 608c2ecf20Sopenharmony_ci struct clk *gclk; 618c2ecf20Sopenharmony_ci u32 mode; 628c2ecf20Sopenharmony_ci}; 638c2ecf20Sopenharmony_ci 648c2ecf20Sopenharmony_ci/** 658c2ecf20Sopenharmony_ci * mchp_pit64b_clkevt - PIT64B clockevent data structure 668c2ecf20Sopenharmony_ci * @timer: PIT64B timer 678c2ecf20Sopenharmony_ci * @clkevt: clockevent 688c2ecf20Sopenharmony_ci */ 698c2ecf20Sopenharmony_cistruct mchp_pit64b_clkevt { 708c2ecf20Sopenharmony_ci struct mchp_pit64b_timer timer; 718c2ecf20Sopenharmony_ci struct clock_event_device clkevt; 728c2ecf20Sopenharmony_ci}; 738c2ecf20Sopenharmony_ci 748c2ecf20Sopenharmony_ci#define to_mchp_pit64b_timer(x) \ 758c2ecf20Sopenharmony_ci ((struct mchp_pit64b_timer *)container_of(x,\ 768c2ecf20Sopenharmony_ci struct mchp_pit64b_clkevt, clkevt)) 778c2ecf20Sopenharmony_ci 788c2ecf20Sopenharmony_ci/* Base address for clocksource timer. */ 798c2ecf20Sopenharmony_cistatic void __iomem *mchp_pit64b_cs_base; 808c2ecf20Sopenharmony_ci/* Default cycles for clockevent timer. */ 818c2ecf20Sopenharmony_cistatic u64 mchp_pit64b_ce_cycles; 828c2ecf20Sopenharmony_ci 838c2ecf20Sopenharmony_cistatic inline u64 mchp_pit64b_cnt_read(void __iomem *base) 848c2ecf20Sopenharmony_ci{ 858c2ecf20Sopenharmony_ci unsigned long flags; 868c2ecf20Sopenharmony_ci u32 low, high; 878c2ecf20Sopenharmony_ci 888c2ecf20Sopenharmony_ci raw_local_irq_save(flags); 898c2ecf20Sopenharmony_ci 908c2ecf20Sopenharmony_ci /* 918c2ecf20Sopenharmony_ci * When using a 64 bit period TLSB must be read first, followed by the 928c2ecf20Sopenharmony_ci * read of TMSB. This sequence generates an atomic read of the 64 bit 938c2ecf20Sopenharmony_ci * timer value whatever the lapse of time between the accesses. 948c2ecf20Sopenharmony_ci */ 958c2ecf20Sopenharmony_ci low = readl_relaxed(base + MCHP_PIT64B_TLSBR); 968c2ecf20Sopenharmony_ci high = readl_relaxed(base + MCHP_PIT64B_TMSBR); 978c2ecf20Sopenharmony_ci 988c2ecf20Sopenharmony_ci raw_local_irq_restore(flags); 998c2ecf20Sopenharmony_ci 1008c2ecf20Sopenharmony_ci return (((u64)high << 32) | low); 1018c2ecf20Sopenharmony_ci} 1028c2ecf20Sopenharmony_ci 1038c2ecf20Sopenharmony_cistatic inline void mchp_pit64b_reset(struct mchp_pit64b_timer *timer, 1048c2ecf20Sopenharmony_ci u64 cycles, u32 mode, u32 irqs) 1058c2ecf20Sopenharmony_ci{ 1068c2ecf20Sopenharmony_ci u32 low, high; 1078c2ecf20Sopenharmony_ci 1088c2ecf20Sopenharmony_ci low = cycles & MCHP_PIT64B_LSBMASK; 1098c2ecf20Sopenharmony_ci high = cycles >> 32; 1108c2ecf20Sopenharmony_ci 1118c2ecf20Sopenharmony_ci writel_relaxed(MCHP_PIT64B_CR_SWRST, timer->base + MCHP_PIT64B_CR); 1128c2ecf20Sopenharmony_ci writel_relaxed(mode | timer->mode, timer->base + MCHP_PIT64B_MR); 1138c2ecf20Sopenharmony_ci writel_relaxed(high, timer->base + MCHP_PIT64B_MSB_PR); 1148c2ecf20Sopenharmony_ci writel_relaxed(low, timer->base + MCHP_PIT64B_LSB_PR); 1158c2ecf20Sopenharmony_ci writel_relaxed(irqs, timer->base + MCHP_PIT64B_IER); 1168c2ecf20Sopenharmony_ci writel_relaxed(MCHP_PIT64B_CR_START, timer->base + MCHP_PIT64B_CR); 1178c2ecf20Sopenharmony_ci} 1188c2ecf20Sopenharmony_ci 1198c2ecf20Sopenharmony_cistatic u64 mchp_pit64b_clksrc_read(struct clocksource *cs) 1208c2ecf20Sopenharmony_ci{ 1218c2ecf20Sopenharmony_ci return mchp_pit64b_cnt_read(mchp_pit64b_cs_base); 1228c2ecf20Sopenharmony_ci} 1238c2ecf20Sopenharmony_ci 1248c2ecf20Sopenharmony_cistatic u64 notrace mchp_pit64b_sched_read_clk(void) 1258c2ecf20Sopenharmony_ci{ 1268c2ecf20Sopenharmony_ci return mchp_pit64b_cnt_read(mchp_pit64b_cs_base); 1278c2ecf20Sopenharmony_ci} 1288c2ecf20Sopenharmony_ci 1298c2ecf20Sopenharmony_cistatic int mchp_pit64b_clkevt_shutdown(struct clock_event_device *cedev) 1308c2ecf20Sopenharmony_ci{ 1318c2ecf20Sopenharmony_ci struct mchp_pit64b_timer *timer = to_mchp_pit64b_timer(cedev); 1328c2ecf20Sopenharmony_ci 1338c2ecf20Sopenharmony_ci writel_relaxed(MCHP_PIT64B_CR_SWRST, timer->base + MCHP_PIT64B_CR); 1348c2ecf20Sopenharmony_ci 1358c2ecf20Sopenharmony_ci return 0; 1368c2ecf20Sopenharmony_ci} 1378c2ecf20Sopenharmony_ci 1388c2ecf20Sopenharmony_cistatic int mchp_pit64b_clkevt_set_periodic(struct clock_event_device *cedev) 1398c2ecf20Sopenharmony_ci{ 1408c2ecf20Sopenharmony_ci struct mchp_pit64b_timer *timer = to_mchp_pit64b_timer(cedev); 1418c2ecf20Sopenharmony_ci 1428c2ecf20Sopenharmony_ci mchp_pit64b_reset(timer, mchp_pit64b_ce_cycles, MCHP_PIT64B_MR_CONT, 1438c2ecf20Sopenharmony_ci MCHP_PIT64B_IER_PERIOD); 1448c2ecf20Sopenharmony_ci 1458c2ecf20Sopenharmony_ci return 0; 1468c2ecf20Sopenharmony_ci} 1478c2ecf20Sopenharmony_ci 1488c2ecf20Sopenharmony_cistatic int mchp_pit64b_clkevt_set_next_event(unsigned long evt, 1498c2ecf20Sopenharmony_ci struct clock_event_device *cedev) 1508c2ecf20Sopenharmony_ci{ 1518c2ecf20Sopenharmony_ci struct mchp_pit64b_timer *timer = to_mchp_pit64b_timer(cedev); 1528c2ecf20Sopenharmony_ci 1538c2ecf20Sopenharmony_ci mchp_pit64b_reset(timer, evt, MCHP_PIT64B_MR_ONE_SHOT, 1548c2ecf20Sopenharmony_ci MCHP_PIT64B_IER_PERIOD); 1558c2ecf20Sopenharmony_ci 1568c2ecf20Sopenharmony_ci return 0; 1578c2ecf20Sopenharmony_ci} 1588c2ecf20Sopenharmony_ci 1598c2ecf20Sopenharmony_cistatic void mchp_pit64b_clkevt_suspend(struct clock_event_device *cedev) 1608c2ecf20Sopenharmony_ci{ 1618c2ecf20Sopenharmony_ci struct mchp_pit64b_timer *timer = to_mchp_pit64b_timer(cedev); 1628c2ecf20Sopenharmony_ci 1638c2ecf20Sopenharmony_ci writel_relaxed(MCHP_PIT64B_CR_SWRST, timer->base + MCHP_PIT64B_CR); 1648c2ecf20Sopenharmony_ci if (timer->mode & MCHP_PIT64B_MR_SGCLK) 1658c2ecf20Sopenharmony_ci clk_disable_unprepare(timer->gclk); 1668c2ecf20Sopenharmony_ci clk_disable_unprepare(timer->pclk); 1678c2ecf20Sopenharmony_ci} 1688c2ecf20Sopenharmony_ci 1698c2ecf20Sopenharmony_cistatic void mchp_pit64b_clkevt_resume(struct clock_event_device *cedev) 1708c2ecf20Sopenharmony_ci{ 1718c2ecf20Sopenharmony_ci struct mchp_pit64b_timer *timer = to_mchp_pit64b_timer(cedev); 1728c2ecf20Sopenharmony_ci 1738c2ecf20Sopenharmony_ci clk_prepare_enable(timer->pclk); 1748c2ecf20Sopenharmony_ci if (timer->mode & MCHP_PIT64B_MR_SGCLK) 1758c2ecf20Sopenharmony_ci clk_prepare_enable(timer->gclk); 1768c2ecf20Sopenharmony_ci} 1778c2ecf20Sopenharmony_ci 1788c2ecf20Sopenharmony_cistatic irqreturn_t mchp_pit64b_interrupt(int irq, void *dev_id) 1798c2ecf20Sopenharmony_ci{ 1808c2ecf20Sopenharmony_ci struct mchp_pit64b_clkevt *irq_data = dev_id; 1818c2ecf20Sopenharmony_ci 1828c2ecf20Sopenharmony_ci /* Need to clear the interrupt. */ 1838c2ecf20Sopenharmony_ci readl_relaxed(irq_data->timer.base + MCHP_PIT64B_ISR); 1848c2ecf20Sopenharmony_ci 1858c2ecf20Sopenharmony_ci irq_data->clkevt.event_handler(&irq_data->clkevt); 1868c2ecf20Sopenharmony_ci 1878c2ecf20Sopenharmony_ci return IRQ_HANDLED; 1888c2ecf20Sopenharmony_ci} 1898c2ecf20Sopenharmony_ci 1908c2ecf20Sopenharmony_cistatic void __init mchp_pit64b_pres_compute(u32 *pres, u32 clk_rate, 1918c2ecf20Sopenharmony_ci u32 max_rate) 1928c2ecf20Sopenharmony_ci{ 1938c2ecf20Sopenharmony_ci u32 tmp; 1948c2ecf20Sopenharmony_ci 1958c2ecf20Sopenharmony_ci for (*pres = 0; *pres < MCHP_PIT64B_PRES_MAX; (*pres)++) { 1968c2ecf20Sopenharmony_ci tmp = clk_rate / (*pres + 1); 1978c2ecf20Sopenharmony_ci if (tmp <= max_rate) 1988c2ecf20Sopenharmony_ci break; 1998c2ecf20Sopenharmony_ci } 2008c2ecf20Sopenharmony_ci 2018c2ecf20Sopenharmony_ci /* Use the bigest prescaler if we didn't match one. */ 2028c2ecf20Sopenharmony_ci if (*pres == MCHP_PIT64B_PRES_MAX) 2038c2ecf20Sopenharmony_ci *pres = MCHP_PIT64B_PRES_MAX - 1; 2048c2ecf20Sopenharmony_ci} 2058c2ecf20Sopenharmony_ci 2068c2ecf20Sopenharmony_ci/** 2078c2ecf20Sopenharmony_ci * mchp_pit64b_init_mode - prepare PIT64B mode register value to be used at 2088c2ecf20Sopenharmony_ci * runtime; this includes prescaler and SGCLK bit 2098c2ecf20Sopenharmony_ci * 2108c2ecf20Sopenharmony_ci * PIT64B timer may be fed by gclk or pclk. When gclk is used its rate has to 2118c2ecf20Sopenharmony_ci * be at least 3 times lower that pclk's rate. pclk rate is fixed, gclk rate 2128c2ecf20Sopenharmony_ci * could be changed via clock APIs. The chosen clock (pclk or gclk) could be 2138c2ecf20Sopenharmony_ci * divided by the internal PIT64B's divider. 2148c2ecf20Sopenharmony_ci * 2158c2ecf20Sopenharmony_ci * This function, first tries to use GCLK by requesting the desired rate from 2168c2ecf20Sopenharmony_ci * PMC and then using the internal PIT64B prescaler, if any, to reach the 2178c2ecf20Sopenharmony_ci * requested rate. If PCLK/GCLK < 3 (condition requested by PIT64B hardware) 2188c2ecf20Sopenharmony_ci * then the function falls back on using PCLK as clock source for PIT64B timer 2198c2ecf20Sopenharmony_ci * choosing the highest prescaler in case it doesn't locate one to match the 2208c2ecf20Sopenharmony_ci * requested frequency. 2218c2ecf20Sopenharmony_ci * 2228c2ecf20Sopenharmony_ci * Below is presented the PIT64B block in relation with PMC: 2238c2ecf20Sopenharmony_ci * 2248c2ecf20Sopenharmony_ci * PIT64B 2258c2ecf20Sopenharmony_ci * PMC +------------------------------------+ 2268c2ecf20Sopenharmony_ci * +----+ | +-----+ | 2278c2ecf20Sopenharmony_ci * | |-->gclk -->|-->| | +---------+ +-----+ | 2288c2ecf20Sopenharmony_ci * | | | | MUX |--->| Divider |->|timer| | 2298c2ecf20Sopenharmony_ci * | |-->pclk -->|-->| | +---------+ +-----+ | 2308c2ecf20Sopenharmony_ci * +----+ | +-----+ | 2318c2ecf20Sopenharmony_ci * | ^ | 2328c2ecf20Sopenharmony_ci * | sel | 2338c2ecf20Sopenharmony_ci * +------------------------------------+ 2348c2ecf20Sopenharmony_ci * 2358c2ecf20Sopenharmony_ci * Where: 2368c2ecf20Sopenharmony_ci * - gclk rate <= pclk rate/3 2378c2ecf20Sopenharmony_ci * - gclk rate could be requested from PMC 2388c2ecf20Sopenharmony_ci * - pclk rate is fixed (cannot be requested from PMC) 2398c2ecf20Sopenharmony_ci */ 2408c2ecf20Sopenharmony_cistatic int __init mchp_pit64b_init_mode(struct mchp_pit64b_timer *timer, 2418c2ecf20Sopenharmony_ci unsigned long max_rate) 2428c2ecf20Sopenharmony_ci{ 2438c2ecf20Sopenharmony_ci unsigned long pclk_rate, diff = 0, best_diff = ULONG_MAX; 2448c2ecf20Sopenharmony_ci long gclk_round = 0; 2458c2ecf20Sopenharmony_ci u32 pres, best_pres = 0; 2468c2ecf20Sopenharmony_ci 2478c2ecf20Sopenharmony_ci pclk_rate = clk_get_rate(timer->pclk); 2488c2ecf20Sopenharmony_ci if (!pclk_rate) 2498c2ecf20Sopenharmony_ci return -EINVAL; 2508c2ecf20Sopenharmony_ci 2518c2ecf20Sopenharmony_ci timer->mode = 0; 2528c2ecf20Sopenharmony_ci 2538c2ecf20Sopenharmony_ci /* Try using GCLK. */ 2548c2ecf20Sopenharmony_ci gclk_round = clk_round_rate(timer->gclk, max_rate); 2558c2ecf20Sopenharmony_ci if (gclk_round < 0) 2568c2ecf20Sopenharmony_ci goto pclk; 2578c2ecf20Sopenharmony_ci 2588c2ecf20Sopenharmony_ci if (pclk_rate / gclk_round < 3) 2598c2ecf20Sopenharmony_ci goto pclk; 2608c2ecf20Sopenharmony_ci 2618c2ecf20Sopenharmony_ci mchp_pit64b_pres_compute(&pres, gclk_round, max_rate); 2628c2ecf20Sopenharmony_ci best_diff = abs(gclk_round / (pres + 1) - max_rate); 2638c2ecf20Sopenharmony_ci best_pres = pres; 2648c2ecf20Sopenharmony_ci 2658c2ecf20Sopenharmony_ci if (!best_diff) { 2668c2ecf20Sopenharmony_ci timer->mode |= MCHP_PIT64B_MR_SGCLK; 2678c2ecf20Sopenharmony_ci clk_set_rate(timer->gclk, gclk_round); 2688c2ecf20Sopenharmony_ci goto done; 2698c2ecf20Sopenharmony_ci } 2708c2ecf20Sopenharmony_ci 2718c2ecf20Sopenharmony_cipclk: 2728c2ecf20Sopenharmony_ci /* Check if requested rate could be obtained using PCLK. */ 2738c2ecf20Sopenharmony_ci mchp_pit64b_pres_compute(&pres, pclk_rate, max_rate); 2748c2ecf20Sopenharmony_ci diff = abs(pclk_rate / (pres + 1) - max_rate); 2758c2ecf20Sopenharmony_ci 2768c2ecf20Sopenharmony_ci if (best_diff > diff) { 2778c2ecf20Sopenharmony_ci /* Use PCLK. */ 2788c2ecf20Sopenharmony_ci best_pres = pres; 2798c2ecf20Sopenharmony_ci } else { 2808c2ecf20Sopenharmony_ci /* Use GCLK. */ 2818c2ecf20Sopenharmony_ci timer->mode |= MCHP_PIT64B_MR_SGCLK; 2828c2ecf20Sopenharmony_ci clk_set_rate(timer->gclk, gclk_round); 2838c2ecf20Sopenharmony_ci } 2848c2ecf20Sopenharmony_ci 2858c2ecf20Sopenharmony_cidone: 2868c2ecf20Sopenharmony_ci timer->mode |= MCHP_PIT64B_PRES_TO_MODE(best_pres); 2878c2ecf20Sopenharmony_ci 2888c2ecf20Sopenharmony_ci pr_info("PIT64B: using clk=%s with prescaler %u, freq=%lu [Hz]\n", 2898c2ecf20Sopenharmony_ci timer->mode & MCHP_PIT64B_MR_SGCLK ? "gclk" : "pclk", best_pres, 2908c2ecf20Sopenharmony_ci timer->mode & MCHP_PIT64B_MR_SGCLK ? 2918c2ecf20Sopenharmony_ci gclk_round / (best_pres + 1) : pclk_rate / (best_pres + 1)); 2928c2ecf20Sopenharmony_ci 2938c2ecf20Sopenharmony_ci return 0; 2948c2ecf20Sopenharmony_ci} 2958c2ecf20Sopenharmony_ci 2968c2ecf20Sopenharmony_cistatic int __init mchp_pit64b_init_clksrc(struct mchp_pit64b_timer *timer, 2978c2ecf20Sopenharmony_ci u32 clk_rate) 2988c2ecf20Sopenharmony_ci{ 2998c2ecf20Sopenharmony_ci int ret; 3008c2ecf20Sopenharmony_ci 3018c2ecf20Sopenharmony_ci mchp_pit64b_reset(timer, ULLONG_MAX, MCHP_PIT64B_MR_CONT, 0); 3028c2ecf20Sopenharmony_ci 3038c2ecf20Sopenharmony_ci mchp_pit64b_cs_base = timer->base; 3048c2ecf20Sopenharmony_ci 3058c2ecf20Sopenharmony_ci ret = clocksource_mmio_init(timer->base, MCHP_PIT64B_NAME, clk_rate, 3068c2ecf20Sopenharmony_ci 210, 64, mchp_pit64b_clksrc_read); 3078c2ecf20Sopenharmony_ci if (ret) { 3088c2ecf20Sopenharmony_ci pr_debug("clksrc: Failed to register PIT64B clocksource!\n"); 3098c2ecf20Sopenharmony_ci 3108c2ecf20Sopenharmony_ci /* Stop timer. */ 3118c2ecf20Sopenharmony_ci writel_relaxed(MCHP_PIT64B_CR_SWRST, 3128c2ecf20Sopenharmony_ci timer->base + MCHP_PIT64B_CR); 3138c2ecf20Sopenharmony_ci 3148c2ecf20Sopenharmony_ci return ret; 3158c2ecf20Sopenharmony_ci } 3168c2ecf20Sopenharmony_ci 3178c2ecf20Sopenharmony_ci sched_clock_register(mchp_pit64b_sched_read_clk, 64, clk_rate); 3188c2ecf20Sopenharmony_ci 3198c2ecf20Sopenharmony_ci return 0; 3208c2ecf20Sopenharmony_ci} 3218c2ecf20Sopenharmony_ci 3228c2ecf20Sopenharmony_cistatic int __init mchp_pit64b_init_clkevt(struct mchp_pit64b_timer *timer, 3238c2ecf20Sopenharmony_ci u32 clk_rate, u32 irq) 3248c2ecf20Sopenharmony_ci{ 3258c2ecf20Sopenharmony_ci struct mchp_pit64b_clkevt *ce; 3268c2ecf20Sopenharmony_ci int ret; 3278c2ecf20Sopenharmony_ci 3288c2ecf20Sopenharmony_ci ce = kzalloc(sizeof(*ce), GFP_KERNEL); 3298c2ecf20Sopenharmony_ci if (!ce) 3308c2ecf20Sopenharmony_ci return -ENOMEM; 3318c2ecf20Sopenharmony_ci 3328c2ecf20Sopenharmony_ci mchp_pit64b_ce_cycles = DIV_ROUND_CLOSEST(clk_rate, HZ); 3338c2ecf20Sopenharmony_ci 3348c2ecf20Sopenharmony_ci ce->timer.base = timer->base; 3358c2ecf20Sopenharmony_ci ce->timer.pclk = timer->pclk; 3368c2ecf20Sopenharmony_ci ce->timer.gclk = timer->gclk; 3378c2ecf20Sopenharmony_ci ce->timer.mode = timer->mode; 3388c2ecf20Sopenharmony_ci ce->clkevt.name = MCHP_PIT64B_NAME; 3398c2ecf20Sopenharmony_ci ce->clkevt.features = CLOCK_EVT_FEAT_ONESHOT | CLOCK_EVT_FEAT_PERIODIC; 3408c2ecf20Sopenharmony_ci ce->clkevt.rating = 150; 3418c2ecf20Sopenharmony_ci ce->clkevt.set_state_shutdown = mchp_pit64b_clkevt_shutdown; 3428c2ecf20Sopenharmony_ci ce->clkevt.set_state_periodic = mchp_pit64b_clkevt_set_periodic; 3438c2ecf20Sopenharmony_ci ce->clkevt.set_next_event = mchp_pit64b_clkevt_set_next_event; 3448c2ecf20Sopenharmony_ci ce->clkevt.suspend = mchp_pit64b_clkevt_suspend; 3458c2ecf20Sopenharmony_ci ce->clkevt.resume = mchp_pit64b_clkevt_resume; 3468c2ecf20Sopenharmony_ci ce->clkevt.cpumask = cpumask_of(0); 3478c2ecf20Sopenharmony_ci ce->clkevt.irq = irq; 3488c2ecf20Sopenharmony_ci 3498c2ecf20Sopenharmony_ci ret = request_irq(irq, mchp_pit64b_interrupt, IRQF_TIMER, 3508c2ecf20Sopenharmony_ci "pit64b_tick", ce); 3518c2ecf20Sopenharmony_ci if (ret) { 3528c2ecf20Sopenharmony_ci pr_debug("clkevt: Failed to setup PIT64B IRQ\n"); 3538c2ecf20Sopenharmony_ci kfree(ce); 3548c2ecf20Sopenharmony_ci return ret; 3558c2ecf20Sopenharmony_ci } 3568c2ecf20Sopenharmony_ci 3578c2ecf20Sopenharmony_ci clockevents_config_and_register(&ce->clkevt, clk_rate, 1, ULONG_MAX); 3588c2ecf20Sopenharmony_ci 3598c2ecf20Sopenharmony_ci return 0; 3608c2ecf20Sopenharmony_ci} 3618c2ecf20Sopenharmony_ci 3628c2ecf20Sopenharmony_cistatic int __init mchp_pit64b_dt_init_timer(struct device_node *node, 3638c2ecf20Sopenharmony_ci bool clkevt) 3648c2ecf20Sopenharmony_ci{ 3658c2ecf20Sopenharmony_ci u32 freq = clkevt ? MCHP_PIT64B_DEF_CE_FREQ : MCHP_PIT64B_DEF_CS_FREQ; 3668c2ecf20Sopenharmony_ci struct mchp_pit64b_timer timer; 3678c2ecf20Sopenharmony_ci unsigned long clk_rate; 3688c2ecf20Sopenharmony_ci u32 irq = 0; 3698c2ecf20Sopenharmony_ci int ret; 3708c2ecf20Sopenharmony_ci 3718c2ecf20Sopenharmony_ci /* Parse DT node. */ 3728c2ecf20Sopenharmony_ci timer.pclk = of_clk_get_by_name(node, "pclk"); 3738c2ecf20Sopenharmony_ci if (IS_ERR(timer.pclk)) 3748c2ecf20Sopenharmony_ci return PTR_ERR(timer.pclk); 3758c2ecf20Sopenharmony_ci 3768c2ecf20Sopenharmony_ci timer.gclk = of_clk_get_by_name(node, "gclk"); 3778c2ecf20Sopenharmony_ci if (IS_ERR(timer.gclk)) 3788c2ecf20Sopenharmony_ci return PTR_ERR(timer.gclk); 3798c2ecf20Sopenharmony_ci 3808c2ecf20Sopenharmony_ci timer.base = of_iomap(node, 0); 3818c2ecf20Sopenharmony_ci if (!timer.base) 3828c2ecf20Sopenharmony_ci return -ENXIO; 3838c2ecf20Sopenharmony_ci 3848c2ecf20Sopenharmony_ci if (clkevt) { 3858c2ecf20Sopenharmony_ci irq = irq_of_parse_and_map(node, 0); 3868c2ecf20Sopenharmony_ci if (!irq) { 3878c2ecf20Sopenharmony_ci ret = -ENODEV; 3888c2ecf20Sopenharmony_ci goto io_unmap; 3898c2ecf20Sopenharmony_ci } 3908c2ecf20Sopenharmony_ci } 3918c2ecf20Sopenharmony_ci 3928c2ecf20Sopenharmony_ci /* Initialize mode (prescaler + SGCK bit). To be used at runtime. */ 3938c2ecf20Sopenharmony_ci ret = mchp_pit64b_init_mode(&timer, freq); 3948c2ecf20Sopenharmony_ci if (ret) 3958c2ecf20Sopenharmony_ci goto irq_unmap; 3968c2ecf20Sopenharmony_ci 3978c2ecf20Sopenharmony_ci ret = clk_prepare_enable(timer.pclk); 3988c2ecf20Sopenharmony_ci if (ret) 3998c2ecf20Sopenharmony_ci goto irq_unmap; 4008c2ecf20Sopenharmony_ci 4018c2ecf20Sopenharmony_ci if (timer.mode & MCHP_PIT64B_MR_SGCLK) { 4028c2ecf20Sopenharmony_ci ret = clk_prepare_enable(timer.gclk); 4038c2ecf20Sopenharmony_ci if (ret) 4048c2ecf20Sopenharmony_ci goto pclk_unprepare; 4058c2ecf20Sopenharmony_ci 4068c2ecf20Sopenharmony_ci clk_rate = clk_get_rate(timer.gclk); 4078c2ecf20Sopenharmony_ci } else { 4088c2ecf20Sopenharmony_ci clk_rate = clk_get_rate(timer.pclk); 4098c2ecf20Sopenharmony_ci } 4108c2ecf20Sopenharmony_ci clk_rate = clk_rate / (MCHP_PIT64B_MODE_TO_PRES(timer.mode) + 1); 4118c2ecf20Sopenharmony_ci 4128c2ecf20Sopenharmony_ci if (clkevt) 4138c2ecf20Sopenharmony_ci ret = mchp_pit64b_init_clkevt(&timer, clk_rate, irq); 4148c2ecf20Sopenharmony_ci else 4158c2ecf20Sopenharmony_ci ret = mchp_pit64b_init_clksrc(&timer, clk_rate); 4168c2ecf20Sopenharmony_ci 4178c2ecf20Sopenharmony_ci if (ret) 4188c2ecf20Sopenharmony_ci goto gclk_unprepare; 4198c2ecf20Sopenharmony_ci 4208c2ecf20Sopenharmony_ci return 0; 4218c2ecf20Sopenharmony_ci 4228c2ecf20Sopenharmony_cigclk_unprepare: 4238c2ecf20Sopenharmony_ci if (timer.mode & MCHP_PIT64B_MR_SGCLK) 4248c2ecf20Sopenharmony_ci clk_disable_unprepare(timer.gclk); 4258c2ecf20Sopenharmony_cipclk_unprepare: 4268c2ecf20Sopenharmony_ci clk_disable_unprepare(timer.pclk); 4278c2ecf20Sopenharmony_ciirq_unmap: 4288c2ecf20Sopenharmony_ci irq_dispose_mapping(irq); 4298c2ecf20Sopenharmony_ciio_unmap: 4308c2ecf20Sopenharmony_ci iounmap(timer.base); 4318c2ecf20Sopenharmony_ci 4328c2ecf20Sopenharmony_ci return ret; 4338c2ecf20Sopenharmony_ci} 4348c2ecf20Sopenharmony_ci 4358c2ecf20Sopenharmony_cistatic int __init mchp_pit64b_dt_init(struct device_node *node) 4368c2ecf20Sopenharmony_ci{ 4378c2ecf20Sopenharmony_ci static int inits; 4388c2ecf20Sopenharmony_ci 4398c2ecf20Sopenharmony_ci switch (inits++) { 4408c2ecf20Sopenharmony_ci case 0: 4418c2ecf20Sopenharmony_ci /* 1st request, register clockevent. */ 4428c2ecf20Sopenharmony_ci return mchp_pit64b_dt_init_timer(node, true); 4438c2ecf20Sopenharmony_ci case 1: 4448c2ecf20Sopenharmony_ci /* 2nd request, register clocksource. */ 4458c2ecf20Sopenharmony_ci return mchp_pit64b_dt_init_timer(node, false); 4468c2ecf20Sopenharmony_ci } 4478c2ecf20Sopenharmony_ci 4488c2ecf20Sopenharmony_ci /* The rest, don't care. */ 4498c2ecf20Sopenharmony_ci return -EINVAL; 4508c2ecf20Sopenharmony_ci} 4518c2ecf20Sopenharmony_ci 4528c2ecf20Sopenharmony_ciTIMER_OF_DECLARE(mchp_pit64b, "microchip,sam9x60-pit64b", mchp_pit64b_dt_init); 453