18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 28c2ecf20Sopenharmony_ci#include <linux/init.h> 38c2ecf20Sopenharmony_ci#include <linux/clocksource.h> 48c2ecf20Sopenharmony_ci#include <linux/clockchips.h> 58c2ecf20Sopenharmony_ci#include <linux/interrupt.h> 68c2ecf20Sopenharmony_ci#include <linux/irq.h> 78c2ecf20Sopenharmony_ci 88c2ecf20Sopenharmony_ci#include <linux/clk.h> 98c2ecf20Sopenharmony_ci#include <linux/delay.h> 108c2ecf20Sopenharmony_ci#include <linux/err.h> 118c2ecf20Sopenharmony_ci#include <linux/ioport.h> 128c2ecf20Sopenharmony_ci#include <linux/io.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/syscore_ops.h> 178c2ecf20Sopenharmony_ci#include <soc/at91/atmel_tcb.h> 188c2ecf20Sopenharmony_ci 198c2ecf20Sopenharmony_ci 208c2ecf20Sopenharmony_ci/* 218c2ecf20Sopenharmony_ci * We're configured to use a specific TC block, one that's not hooked 228c2ecf20Sopenharmony_ci * up to external hardware, to provide a time solution: 238c2ecf20Sopenharmony_ci * 248c2ecf20Sopenharmony_ci * - Two channels combine to create a free-running 32 bit counter 258c2ecf20Sopenharmony_ci * with a base rate of 5+ MHz, packaged as a clocksource (with 268c2ecf20Sopenharmony_ci * resolution better than 200 nsec). 278c2ecf20Sopenharmony_ci * - Some chips support 32 bit counter. A single channel is used for 288c2ecf20Sopenharmony_ci * this 32 bit free-running counter. the second channel is not used. 298c2ecf20Sopenharmony_ci * 308c2ecf20Sopenharmony_ci * - The third channel may be used to provide a clockevent source, used in 318c2ecf20Sopenharmony_ci * either periodic or oneshot mode. For 16-bit counter its runs at 32 KiHZ, 328c2ecf20Sopenharmony_ci * and can handle delays of up to two seconds. For 32-bit counters, it runs at 338c2ecf20Sopenharmony_ci * the same rate as the clocksource 348c2ecf20Sopenharmony_ci * 358c2ecf20Sopenharmony_ci * REVISIT behavior during system suspend states... we should disable 368c2ecf20Sopenharmony_ci * all clocks and save the power. Easily done for clockevent devices, 378c2ecf20Sopenharmony_ci * but clocksources won't necessarily get the needed notifications. 388c2ecf20Sopenharmony_ci * For deeper system sleep states, this will be mandatory... 398c2ecf20Sopenharmony_ci */ 408c2ecf20Sopenharmony_ci 418c2ecf20Sopenharmony_cistatic void __iomem *tcaddr; 428c2ecf20Sopenharmony_cistatic struct 438c2ecf20Sopenharmony_ci{ 448c2ecf20Sopenharmony_ci u32 cmr; 458c2ecf20Sopenharmony_ci u32 imr; 468c2ecf20Sopenharmony_ci u32 rc; 478c2ecf20Sopenharmony_ci bool clken; 488c2ecf20Sopenharmony_ci} tcb_cache[3]; 498c2ecf20Sopenharmony_cistatic u32 bmr_cache; 508c2ecf20Sopenharmony_ci 518c2ecf20Sopenharmony_cistatic const u8 atmel_tcb_divisors[] = { 2, 8, 32, 128 }; 528c2ecf20Sopenharmony_ci 538c2ecf20Sopenharmony_cistatic u64 tc_get_cycles(struct clocksource *cs) 548c2ecf20Sopenharmony_ci{ 558c2ecf20Sopenharmony_ci unsigned long flags; 568c2ecf20Sopenharmony_ci u32 lower, upper; 578c2ecf20Sopenharmony_ci 588c2ecf20Sopenharmony_ci raw_local_irq_save(flags); 598c2ecf20Sopenharmony_ci do { 608c2ecf20Sopenharmony_ci upper = readl_relaxed(tcaddr + ATMEL_TC_REG(1, CV)); 618c2ecf20Sopenharmony_ci lower = readl_relaxed(tcaddr + ATMEL_TC_REG(0, CV)); 628c2ecf20Sopenharmony_ci } while (upper != readl_relaxed(tcaddr + ATMEL_TC_REG(1, CV))); 638c2ecf20Sopenharmony_ci 648c2ecf20Sopenharmony_ci raw_local_irq_restore(flags); 658c2ecf20Sopenharmony_ci return (upper << 16) | lower; 668c2ecf20Sopenharmony_ci} 678c2ecf20Sopenharmony_ci 688c2ecf20Sopenharmony_cistatic u64 tc_get_cycles32(struct clocksource *cs) 698c2ecf20Sopenharmony_ci{ 708c2ecf20Sopenharmony_ci return readl_relaxed(tcaddr + ATMEL_TC_REG(0, CV)); 718c2ecf20Sopenharmony_ci} 728c2ecf20Sopenharmony_ci 738c2ecf20Sopenharmony_cistatic void tc_clksrc_suspend(struct clocksource *cs) 748c2ecf20Sopenharmony_ci{ 758c2ecf20Sopenharmony_ci int i; 768c2ecf20Sopenharmony_ci 778c2ecf20Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(tcb_cache); i++) { 788c2ecf20Sopenharmony_ci tcb_cache[i].cmr = readl(tcaddr + ATMEL_TC_REG(i, CMR)); 798c2ecf20Sopenharmony_ci tcb_cache[i].imr = readl(tcaddr + ATMEL_TC_REG(i, IMR)); 808c2ecf20Sopenharmony_ci tcb_cache[i].rc = readl(tcaddr + ATMEL_TC_REG(i, RC)); 818c2ecf20Sopenharmony_ci tcb_cache[i].clken = !!(readl(tcaddr + ATMEL_TC_REG(i, SR)) & 828c2ecf20Sopenharmony_ci ATMEL_TC_CLKSTA); 838c2ecf20Sopenharmony_ci } 848c2ecf20Sopenharmony_ci 858c2ecf20Sopenharmony_ci bmr_cache = readl(tcaddr + ATMEL_TC_BMR); 868c2ecf20Sopenharmony_ci} 878c2ecf20Sopenharmony_ci 888c2ecf20Sopenharmony_cistatic void tc_clksrc_resume(struct clocksource *cs) 898c2ecf20Sopenharmony_ci{ 908c2ecf20Sopenharmony_ci int i; 918c2ecf20Sopenharmony_ci 928c2ecf20Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(tcb_cache); i++) { 938c2ecf20Sopenharmony_ci /* Restore registers for the channel, RA and RB are not used */ 948c2ecf20Sopenharmony_ci writel(tcb_cache[i].cmr, tcaddr + ATMEL_TC_REG(i, CMR)); 958c2ecf20Sopenharmony_ci writel(tcb_cache[i].rc, tcaddr + ATMEL_TC_REG(i, RC)); 968c2ecf20Sopenharmony_ci writel(0, tcaddr + ATMEL_TC_REG(i, RA)); 978c2ecf20Sopenharmony_ci writel(0, tcaddr + ATMEL_TC_REG(i, RB)); 988c2ecf20Sopenharmony_ci /* Disable all the interrupts */ 998c2ecf20Sopenharmony_ci writel(0xff, tcaddr + ATMEL_TC_REG(i, IDR)); 1008c2ecf20Sopenharmony_ci /* Reenable interrupts that were enabled before suspending */ 1018c2ecf20Sopenharmony_ci writel(tcb_cache[i].imr, tcaddr + ATMEL_TC_REG(i, IER)); 1028c2ecf20Sopenharmony_ci /* Start the clock if it was used */ 1038c2ecf20Sopenharmony_ci if (tcb_cache[i].clken) 1048c2ecf20Sopenharmony_ci writel(ATMEL_TC_CLKEN, tcaddr + ATMEL_TC_REG(i, CCR)); 1058c2ecf20Sopenharmony_ci } 1068c2ecf20Sopenharmony_ci 1078c2ecf20Sopenharmony_ci /* Dual channel, chain channels */ 1088c2ecf20Sopenharmony_ci writel(bmr_cache, tcaddr + ATMEL_TC_BMR); 1098c2ecf20Sopenharmony_ci /* Finally, trigger all the channels*/ 1108c2ecf20Sopenharmony_ci writel(ATMEL_TC_SYNC, tcaddr + ATMEL_TC_BCR); 1118c2ecf20Sopenharmony_ci} 1128c2ecf20Sopenharmony_ci 1138c2ecf20Sopenharmony_cistatic struct clocksource clksrc = { 1148c2ecf20Sopenharmony_ci .rating = 200, 1158c2ecf20Sopenharmony_ci .read = tc_get_cycles, 1168c2ecf20Sopenharmony_ci .mask = CLOCKSOURCE_MASK(32), 1178c2ecf20Sopenharmony_ci .flags = CLOCK_SOURCE_IS_CONTINUOUS, 1188c2ecf20Sopenharmony_ci .suspend = tc_clksrc_suspend, 1198c2ecf20Sopenharmony_ci .resume = tc_clksrc_resume, 1208c2ecf20Sopenharmony_ci}; 1218c2ecf20Sopenharmony_ci 1228c2ecf20Sopenharmony_cistatic u64 notrace tc_sched_clock_read(void) 1238c2ecf20Sopenharmony_ci{ 1248c2ecf20Sopenharmony_ci return tc_get_cycles(&clksrc); 1258c2ecf20Sopenharmony_ci} 1268c2ecf20Sopenharmony_ci 1278c2ecf20Sopenharmony_cistatic u64 notrace tc_sched_clock_read32(void) 1288c2ecf20Sopenharmony_ci{ 1298c2ecf20Sopenharmony_ci return tc_get_cycles32(&clksrc); 1308c2ecf20Sopenharmony_ci} 1318c2ecf20Sopenharmony_ci 1328c2ecf20Sopenharmony_cistatic struct delay_timer tc_delay_timer; 1338c2ecf20Sopenharmony_ci 1348c2ecf20Sopenharmony_cistatic unsigned long tc_delay_timer_read(void) 1358c2ecf20Sopenharmony_ci{ 1368c2ecf20Sopenharmony_ci return tc_get_cycles(&clksrc); 1378c2ecf20Sopenharmony_ci} 1388c2ecf20Sopenharmony_ci 1398c2ecf20Sopenharmony_cistatic unsigned long notrace tc_delay_timer_read32(void) 1408c2ecf20Sopenharmony_ci{ 1418c2ecf20Sopenharmony_ci return tc_get_cycles32(&clksrc); 1428c2ecf20Sopenharmony_ci} 1438c2ecf20Sopenharmony_ci 1448c2ecf20Sopenharmony_ci#ifdef CONFIG_GENERIC_CLOCKEVENTS 1458c2ecf20Sopenharmony_ci 1468c2ecf20Sopenharmony_cistruct tc_clkevt_device { 1478c2ecf20Sopenharmony_ci struct clock_event_device clkevt; 1488c2ecf20Sopenharmony_ci struct clk *clk; 1498c2ecf20Sopenharmony_ci u32 rate; 1508c2ecf20Sopenharmony_ci void __iomem *regs; 1518c2ecf20Sopenharmony_ci}; 1528c2ecf20Sopenharmony_ci 1538c2ecf20Sopenharmony_cistatic struct tc_clkevt_device *to_tc_clkevt(struct clock_event_device *clkevt) 1548c2ecf20Sopenharmony_ci{ 1558c2ecf20Sopenharmony_ci return container_of(clkevt, struct tc_clkevt_device, clkevt); 1568c2ecf20Sopenharmony_ci} 1578c2ecf20Sopenharmony_ci 1588c2ecf20Sopenharmony_cistatic u32 timer_clock; 1598c2ecf20Sopenharmony_ci 1608c2ecf20Sopenharmony_cistatic int tc_shutdown(struct clock_event_device *d) 1618c2ecf20Sopenharmony_ci{ 1628c2ecf20Sopenharmony_ci struct tc_clkevt_device *tcd = to_tc_clkevt(d); 1638c2ecf20Sopenharmony_ci void __iomem *regs = tcd->regs; 1648c2ecf20Sopenharmony_ci 1658c2ecf20Sopenharmony_ci writel(0xff, regs + ATMEL_TC_REG(2, IDR)); 1668c2ecf20Sopenharmony_ci writel(ATMEL_TC_CLKDIS, regs + ATMEL_TC_REG(2, CCR)); 1678c2ecf20Sopenharmony_ci if (!clockevent_state_detached(d)) 1688c2ecf20Sopenharmony_ci clk_disable(tcd->clk); 1698c2ecf20Sopenharmony_ci 1708c2ecf20Sopenharmony_ci return 0; 1718c2ecf20Sopenharmony_ci} 1728c2ecf20Sopenharmony_ci 1738c2ecf20Sopenharmony_cistatic int tc_set_oneshot(struct clock_event_device *d) 1748c2ecf20Sopenharmony_ci{ 1758c2ecf20Sopenharmony_ci struct tc_clkevt_device *tcd = to_tc_clkevt(d); 1768c2ecf20Sopenharmony_ci void __iomem *regs = tcd->regs; 1778c2ecf20Sopenharmony_ci 1788c2ecf20Sopenharmony_ci if (clockevent_state_oneshot(d) || clockevent_state_periodic(d)) 1798c2ecf20Sopenharmony_ci tc_shutdown(d); 1808c2ecf20Sopenharmony_ci 1818c2ecf20Sopenharmony_ci clk_enable(tcd->clk); 1828c2ecf20Sopenharmony_ci 1838c2ecf20Sopenharmony_ci /* count up to RC, then irq and stop */ 1848c2ecf20Sopenharmony_ci writel(timer_clock | ATMEL_TC_CPCSTOP | ATMEL_TC_WAVE | 1858c2ecf20Sopenharmony_ci ATMEL_TC_WAVESEL_UP_AUTO, regs + ATMEL_TC_REG(2, CMR)); 1868c2ecf20Sopenharmony_ci writel(ATMEL_TC_CPCS, regs + ATMEL_TC_REG(2, IER)); 1878c2ecf20Sopenharmony_ci 1888c2ecf20Sopenharmony_ci /* set_next_event() configures and starts the timer */ 1898c2ecf20Sopenharmony_ci return 0; 1908c2ecf20Sopenharmony_ci} 1918c2ecf20Sopenharmony_ci 1928c2ecf20Sopenharmony_cistatic int tc_set_periodic(struct clock_event_device *d) 1938c2ecf20Sopenharmony_ci{ 1948c2ecf20Sopenharmony_ci struct tc_clkevt_device *tcd = to_tc_clkevt(d); 1958c2ecf20Sopenharmony_ci void __iomem *regs = tcd->regs; 1968c2ecf20Sopenharmony_ci 1978c2ecf20Sopenharmony_ci if (clockevent_state_oneshot(d) || clockevent_state_periodic(d)) 1988c2ecf20Sopenharmony_ci tc_shutdown(d); 1998c2ecf20Sopenharmony_ci 2008c2ecf20Sopenharmony_ci /* By not making the gentime core emulate periodic mode on top 2018c2ecf20Sopenharmony_ci * of oneshot, we get lower overhead and improved accuracy. 2028c2ecf20Sopenharmony_ci */ 2038c2ecf20Sopenharmony_ci clk_enable(tcd->clk); 2048c2ecf20Sopenharmony_ci 2058c2ecf20Sopenharmony_ci /* count up to RC, then irq and restart */ 2068c2ecf20Sopenharmony_ci writel(timer_clock | ATMEL_TC_WAVE | ATMEL_TC_WAVESEL_UP_AUTO, 2078c2ecf20Sopenharmony_ci regs + ATMEL_TC_REG(2, CMR)); 2088c2ecf20Sopenharmony_ci writel((tcd->rate + HZ / 2) / HZ, tcaddr + ATMEL_TC_REG(2, RC)); 2098c2ecf20Sopenharmony_ci 2108c2ecf20Sopenharmony_ci /* Enable clock and interrupts on RC compare */ 2118c2ecf20Sopenharmony_ci writel(ATMEL_TC_CPCS, regs + ATMEL_TC_REG(2, IER)); 2128c2ecf20Sopenharmony_ci 2138c2ecf20Sopenharmony_ci /* go go gadget! */ 2148c2ecf20Sopenharmony_ci writel(ATMEL_TC_CLKEN | ATMEL_TC_SWTRG, regs + 2158c2ecf20Sopenharmony_ci ATMEL_TC_REG(2, CCR)); 2168c2ecf20Sopenharmony_ci return 0; 2178c2ecf20Sopenharmony_ci} 2188c2ecf20Sopenharmony_ci 2198c2ecf20Sopenharmony_cistatic int tc_next_event(unsigned long delta, struct clock_event_device *d) 2208c2ecf20Sopenharmony_ci{ 2218c2ecf20Sopenharmony_ci writel_relaxed(delta, tcaddr + ATMEL_TC_REG(2, RC)); 2228c2ecf20Sopenharmony_ci 2238c2ecf20Sopenharmony_ci /* go go gadget! */ 2248c2ecf20Sopenharmony_ci writel_relaxed(ATMEL_TC_CLKEN | ATMEL_TC_SWTRG, 2258c2ecf20Sopenharmony_ci tcaddr + ATMEL_TC_REG(2, CCR)); 2268c2ecf20Sopenharmony_ci return 0; 2278c2ecf20Sopenharmony_ci} 2288c2ecf20Sopenharmony_ci 2298c2ecf20Sopenharmony_cistatic struct tc_clkevt_device clkevt = { 2308c2ecf20Sopenharmony_ci .clkevt = { 2318c2ecf20Sopenharmony_ci .features = CLOCK_EVT_FEAT_PERIODIC | 2328c2ecf20Sopenharmony_ci CLOCK_EVT_FEAT_ONESHOT, 2338c2ecf20Sopenharmony_ci /* Should be lower than at91rm9200's system timer */ 2348c2ecf20Sopenharmony_ci .rating = 125, 2358c2ecf20Sopenharmony_ci .set_next_event = tc_next_event, 2368c2ecf20Sopenharmony_ci .set_state_shutdown = tc_shutdown, 2378c2ecf20Sopenharmony_ci .set_state_periodic = tc_set_periodic, 2388c2ecf20Sopenharmony_ci .set_state_oneshot = tc_set_oneshot, 2398c2ecf20Sopenharmony_ci }, 2408c2ecf20Sopenharmony_ci}; 2418c2ecf20Sopenharmony_ci 2428c2ecf20Sopenharmony_cistatic irqreturn_t ch2_irq(int irq, void *handle) 2438c2ecf20Sopenharmony_ci{ 2448c2ecf20Sopenharmony_ci struct tc_clkevt_device *dev = handle; 2458c2ecf20Sopenharmony_ci unsigned int sr; 2468c2ecf20Sopenharmony_ci 2478c2ecf20Sopenharmony_ci sr = readl_relaxed(dev->regs + ATMEL_TC_REG(2, SR)); 2488c2ecf20Sopenharmony_ci if (sr & ATMEL_TC_CPCS) { 2498c2ecf20Sopenharmony_ci dev->clkevt.event_handler(&dev->clkevt); 2508c2ecf20Sopenharmony_ci return IRQ_HANDLED; 2518c2ecf20Sopenharmony_ci } 2528c2ecf20Sopenharmony_ci 2538c2ecf20Sopenharmony_ci return IRQ_NONE; 2548c2ecf20Sopenharmony_ci} 2558c2ecf20Sopenharmony_ci 2568c2ecf20Sopenharmony_cistatic int __init setup_clkevents(struct atmel_tc *tc, int divisor_idx) 2578c2ecf20Sopenharmony_ci{ 2588c2ecf20Sopenharmony_ci int ret; 2598c2ecf20Sopenharmony_ci struct clk *t2_clk = tc->clk[2]; 2608c2ecf20Sopenharmony_ci int irq = tc->irq[2]; 2618c2ecf20Sopenharmony_ci int bits = tc->tcb_config->counter_width; 2628c2ecf20Sopenharmony_ci 2638c2ecf20Sopenharmony_ci /* try to enable t2 clk to avoid future errors in mode change */ 2648c2ecf20Sopenharmony_ci ret = clk_prepare_enable(t2_clk); 2658c2ecf20Sopenharmony_ci if (ret) 2668c2ecf20Sopenharmony_ci return ret; 2678c2ecf20Sopenharmony_ci 2688c2ecf20Sopenharmony_ci clkevt.regs = tc->regs; 2698c2ecf20Sopenharmony_ci clkevt.clk = t2_clk; 2708c2ecf20Sopenharmony_ci 2718c2ecf20Sopenharmony_ci if (bits == 32) { 2728c2ecf20Sopenharmony_ci timer_clock = divisor_idx; 2738c2ecf20Sopenharmony_ci clkevt.rate = clk_get_rate(t2_clk) / atmel_tcb_divisors[divisor_idx]; 2748c2ecf20Sopenharmony_ci } else { 2758c2ecf20Sopenharmony_ci ret = clk_prepare_enable(tc->slow_clk); 2768c2ecf20Sopenharmony_ci if (ret) { 2778c2ecf20Sopenharmony_ci clk_disable_unprepare(t2_clk); 2788c2ecf20Sopenharmony_ci return ret; 2798c2ecf20Sopenharmony_ci } 2808c2ecf20Sopenharmony_ci 2818c2ecf20Sopenharmony_ci clkevt.rate = clk_get_rate(tc->slow_clk); 2828c2ecf20Sopenharmony_ci timer_clock = ATMEL_TC_TIMER_CLOCK5; 2838c2ecf20Sopenharmony_ci } 2848c2ecf20Sopenharmony_ci 2858c2ecf20Sopenharmony_ci clk_disable(t2_clk); 2868c2ecf20Sopenharmony_ci 2878c2ecf20Sopenharmony_ci clkevt.clkevt.cpumask = cpumask_of(0); 2888c2ecf20Sopenharmony_ci 2898c2ecf20Sopenharmony_ci ret = request_irq(irq, ch2_irq, IRQF_TIMER, "tc_clkevt", &clkevt); 2908c2ecf20Sopenharmony_ci if (ret) { 2918c2ecf20Sopenharmony_ci clk_unprepare(t2_clk); 2928c2ecf20Sopenharmony_ci if (bits != 32) 2938c2ecf20Sopenharmony_ci clk_disable_unprepare(tc->slow_clk); 2948c2ecf20Sopenharmony_ci return ret; 2958c2ecf20Sopenharmony_ci } 2968c2ecf20Sopenharmony_ci 2978c2ecf20Sopenharmony_ci clockevents_config_and_register(&clkevt.clkevt, clkevt.rate, 1, BIT(bits) - 1); 2988c2ecf20Sopenharmony_ci 2998c2ecf20Sopenharmony_ci return ret; 3008c2ecf20Sopenharmony_ci} 3018c2ecf20Sopenharmony_ci 3028c2ecf20Sopenharmony_ci#else /* !CONFIG_GENERIC_CLOCKEVENTS */ 3038c2ecf20Sopenharmony_ci 3048c2ecf20Sopenharmony_cistatic int __init setup_clkevents(struct atmel_tc *tc, int divisor_idx) 3058c2ecf20Sopenharmony_ci{ 3068c2ecf20Sopenharmony_ci /* NOTHING */ 3078c2ecf20Sopenharmony_ci return 0; 3088c2ecf20Sopenharmony_ci} 3098c2ecf20Sopenharmony_ci 3108c2ecf20Sopenharmony_ci#endif 3118c2ecf20Sopenharmony_ci 3128c2ecf20Sopenharmony_cistatic void __init tcb_setup_dual_chan(struct atmel_tc *tc, int mck_divisor_idx) 3138c2ecf20Sopenharmony_ci{ 3148c2ecf20Sopenharmony_ci /* channel 0: waveform mode, input mclk/8, clock TIOA0 on overflow */ 3158c2ecf20Sopenharmony_ci writel(mck_divisor_idx /* likely divide-by-8 */ 3168c2ecf20Sopenharmony_ci | ATMEL_TC_WAVE 3178c2ecf20Sopenharmony_ci | ATMEL_TC_WAVESEL_UP /* free-run */ 3188c2ecf20Sopenharmony_ci | ATMEL_TC_ASWTRG_SET /* TIOA0 rises at software trigger */ 3198c2ecf20Sopenharmony_ci | ATMEL_TC_ACPA_SET /* TIOA0 rises at 0 */ 3208c2ecf20Sopenharmony_ci | ATMEL_TC_ACPC_CLEAR, /* (duty cycle 50%) */ 3218c2ecf20Sopenharmony_ci tcaddr + ATMEL_TC_REG(0, CMR)); 3228c2ecf20Sopenharmony_ci writel(0x0000, tcaddr + ATMEL_TC_REG(0, RA)); 3238c2ecf20Sopenharmony_ci writel(0x8000, tcaddr + ATMEL_TC_REG(0, RC)); 3248c2ecf20Sopenharmony_ci writel(0xff, tcaddr + ATMEL_TC_REG(0, IDR)); /* no irqs */ 3258c2ecf20Sopenharmony_ci writel(ATMEL_TC_CLKEN, tcaddr + ATMEL_TC_REG(0, CCR)); 3268c2ecf20Sopenharmony_ci 3278c2ecf20Sopenharmony_ci /* channel 1: waveform mode, input TIOA0 */ 3288c2ecf20Sopenharmony_ci writel(ATMEL_TC_XC1 /* input: TIOA0 */ 3298c2ecf20Sopenharmony_ci | ATMEL_TC_WAVE 3308c2ecf20Sopenharmony_ci | ATMEL_TC_WAVESEL_UP, /* free-run */ 3318c2ecf20Sopenharmony_ci tcaddr + ATMEL_TC_REG(1, CMR)); 3328c2ecf20Sopenharmony_ci writel(0xff, tcaddr + ATMEL_TC_REG(1, IDR)); /* no irqs */ 3338c2ecf20Sopenharmony_ci writel(ATMEL_TC_CLKEN, tcaddr + ATMEL_TC_REG(1, CCR)); 3348c2ecf20Sopenharmony_ci 3358c2ecf20Sopenharmony_ci /* chain channel 0 to channel 1*/ 3368c2ecf20Sopenharmony_ci writel(ATMEL_TC_TC1XC1S_TIOA0, tcaddr + ATMEL_TC_BMR); 3378c2ecf20Sopenharmony_ci /* then reset all the timers */ 3388c2ecf20Sopenharmony_ci writel(ATMEL_TC_SYNC, tcaddr + ATMEL_TC_BCR); 3398c2ecf20Sopenharmony_ci} 3408c2ecf20Sopenharmony_ci 3418c2ecf20Sopenharmony_cistatic void __init tcb_setup_single_chan(struct atmel_tc *tc, int mck_divisor_idx) 3428c2ecf20Sopenharmony_ci{ 3438c2ecf20Sopenharmony_ci /* channel 0: waveform mode, input mclk/8 */ 3448c2ecf20Sopenharmony_ci writel(mck_divisor_idx /* likely divide-by-8 */ 3458c2ecf20Sopenharmony_ci | ATMEL_TC_WAVE 3468c2ecf20Sopenharmony_ci | ATMEL_TC_WAVESEL_UP, /* free-run */ 3478c2ecf20Sopenharmony_ci tcaddr + ATMEL_TC_REG(0, CMR)); 3488c2ecf20Sopenharmony_ci writel(0xff, tcaddr + ATMEL_TC_REG(0, IDR)); /* no irqs */ 3498c2ecf20Sopenharmony_ci writel(ATMEL_TC_CLKEN, tcaddr + ATMEL_TC_REG(0, CCR)); 3508c2ecf20Sopenharmony_ci 3518c2ecf20Sopenharmony_ci /* then reset all the timers */ 3528c2ecf20Sopenharmony_ci writel(ATMEL_TC_SYNC, tcaddr + ATMEL_TC_BCR); 3538c2ecf20Sopenharmony_ci} 3548c2ecf20Sopenharmony_ci 3558c2ecf20Sopenharmony_cistatic struct atmel_tcb_config tcb_rm9200_config = { 3568c2ecf20Sopenharmony_ci .counter_width = 16, 3578c2ecf20Sopenharmony_ci}; 3588c2ecf20Sopenharmony_ci 3598c2ecf20Sopenharmony_cistatic struct atmel_tcb_config tcb_sam9x5_config = { 3608c2ecf20Sopenharmony_ci .counter_width = 32, 3618c2ecf20Sopenharmony_ci}; 3628c2ecf20Sopenharmony_ci 3638c2ecf20Sopenharmony_cistatic struct atmel_tcb_config tcb_sama5d2_config = { 3648c2ecf20Sopenharmony_ci .counter_width = 32, 3658c2ecf20Sopenharmony_ci .has_gclk = 1, 3668c2ecf20Sopenharmony_ci}; 3678c2ecf20Sopenharmony_ci 3688c2ecf20Sopenharmony_cistatic const struct of_device_id atmel_tcb_of_match[] = { 3698c2ecf20Sopenharmony_ci { .compatible = "atmel,at91rm9200-tcb", .data = &tcb_rm9200_config, }, 3708c2ecf20Sopenharmony_ci { .compatible = "atmel,at91sam9x5-tcb", .data = &tcb_sam9x5_config, }, 3718c2ecf20Sopenharmony_ci { .compatible = "atmel,sama5d2-tcb", .data = &tcb_sama5d2_config, }, 3728c2ecf20Sopenharmony_ci { /* sentinel */ } 3738c2ecf20Sopenharmony_ci}; 3748c2ecf20Sopenharmony_ci 3758c2ecf20Sopenharmony_cistatic int __init tcb_clksrc_init(struct device_node *node) 3768c2ecf20Sopenharmony_ci{ 3778c2ecf20Sopenharmony_ci struct atmel_tc tc; 3788c2ecf20Sopenharmony_ci struct clk *t0_clk; 3798c2ecf20Sopenharmony_ci const struct of_device_id *match; 3808c2ecf20Sopenharmony_ci u64 (*tc_sched_clock)(void); 3818c2ecf20Sopenharmony_ci u32 rate, divided_rate = 0; 3828c2ecf20Sopenharmony_ci int best_divisor_idx = -1; 3838c2ecf20Sopenharmony_ci int bits; 3848c2ecf20Sopenharmony_ci int i; 3858c2ecf20Sopenharmony_ci int ret; 3868c2ecf20Sopenharmony_ci 3878c2ecf20Sopenharmony_ci /* Protect against multiple calls */ 3888c2ecf20Sopenharmony_ci if (tcaddr) 3898c2ecf20Sopenharmony_ci return 0; 3908c2ecf20Sopenharmony_ci 3918c2ecf20Sopenharmony_ci tc.regs = of_iomap(node->parent, 0); 3928c2ecf20Sopenharmony_ci if (!tc.regs) 3938c2ecf20Sopenharmony_ci return -ENXIO; 3948c2ecf20Sopenharmony_ci 3958c2ecf20Sopenharmony_ci t0_clk = of_clk_get_by_name(node->parent, "t0_clk"); 3968c2ecf20Sopenharmony_ci if (IS_ERR(t0_clk)) 3978c2ecf20Sopenharmony_ci return PTR_ERR(t0_clk); 3988c2ecf20Sopenharmony_ci 3998c2ecf20Sopenharmony_ci tc.slow_clk = of_clk_get_by_name(node->parent, "slow_clk"); 4008c2ecf20Sopenharmony_ci if (IS_ERR(tc.slow_clk)) 4018c2ecf20Sopenharmony_ci return PTR_ERR(tc.slow_clk); 4028c2ecf20Sopenharmony_ci 4038c2ecf20Sopenharmony_ci tc.clk[0] = t0_clk; 4048c2ecf20Sopenharmony_ci tc.clk[1] = of_clk_get_by_name(node->parent, "t1_clk"); 4058c2ecf20Sopenharmony_ci if (IS_ERR(tc.clk[1])) 4068c2ecf20Sopenharmony_ci tc.clk[1] = t0_clk; 4078c2ecf20Sopenharmony_ci tc.clk[2] = of_clk_get_by_name(node->parent, "t2_clk"); 4088c2ecf20Sopenharmony_ci if (IS_ERR(tc.clk[2])) 4098c2ecf20Sopenharmony_ci tc.clk[2] = t0_clk; 4108c2ecf20Sopenharmony_ci 4118c2ecf20Sopenharmony_ci tc.irq[2] = of_irq_get(node->parent, 2); 4128c2ecf20Sopenharmony_ci if (tc.irq[2] <= 0) { 4138c2ecf20Sopenharmony_ci tc.irq[2] = of_irq_get(node->parent, 0); 4148c2ecf20Sopenharmony_ci if (tc.irq[2] <= 0) 4158c2ecf20Sopenharmony_ci return -EINVAL; 4168c2ecf20Sopenharmony_ci } 4178c2ecf20Sopenharmony_ci 4188c2ecf20Sopenharmony_ci match = of_match_node(atmel_tcb_of_match, node->parent); 4198c2ecf20Sopenharmony_ci if (!match) 4208c2ecf20Sopenharmony_ci return -ENODEV; 4218c2ecf20Sopenharmony_ci 4228c2ecf20Sopenharmony_ci tc.tcb_config = match->data; 4238c2ecf20Sopenharmony_ci bits = tc.tcb_config->counter_width; 4248c2ecf20Sopenharmony_ci 4258c2ecf20Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(tc.irq); i++) 4268c2ecf20Sopenharmony_ci writel(ATMEL_TC_ALL_IRQ, tc.regs + ATMEL_TC_REG(i, IDR)); 4278c2ecf20Sopenharmony_ci 4288c2ecf20Sopenharmony_ci ret = clk_prepare_enable(t0_clk); 4298c2ecf20Sopenharmony_ci if (ret) { 4308c2ecf20Sopenharmony_ci pr_debug("can't enable T0 clk\n"); 4318c2ecf20Sopenharmony_ci return ret; 4328c2ecf20Sopenharmony_ci } 4338c2ecf20Sopenharmony_ci 4348c2ecf20Sopenharmony_ci /* How fast will we be counting? Pick something over 5 MHz. */ 4358c2ecf20Sopenharmony_ci rate = (u32) clk_get_rate(t0_clk); 4368c2ecf20Sopenharmony_ci i = 0; 4378c2ecf20Sopenharmony_ci if (tc.tcb_config->has_gclk) 4388c2ecf20Sopenharmony_ci i = 1; 4398c2ecf20Sopenharmony_ci for (; i < ARRAY_SIZE(atmel_tcb_divisors); i++) { 4408c2ecf20Sopenharmony_ci unsigned divisor = atmel_tcb_divisors[i]; 4418c2ecf20Sopenharmony_ci unsigned tmp; 4428c2ecf20Sopenharmony_ci 4438c2ecf20Sopenharmony_ci tmp = rate / divisor; 4448c2ecf20Sopenharmony_ci pr_debug("TC: %u / %-3u [%d] --> %u\n", rate, divisor, i, tmp); 4458c2ecf20Sopenharmony_ci if ((best_divisor_idx >= 0) && (tmp < 5 * 1000 * 1000)) 4468c2ecf20Sopenharmony_ci break; 4478c2ecf20Sopenharmony_ci divided_rate = tmp; 4488c2ecf20Sopenharmony_ci best_divisor_idx = i; 4498c2ecf20Sopenharmony_ci } 4508c2ecf20Sopenharmony_ci 4518c2ecf20Sopenharmony_ci clksrc.name = kbasename(node->parent->full_name); 4528c2ecf20Sopenharmony_ci clkevt.clkevt.name = kbasename(node->parent->full_name); 4538c2ecf20Sopenharmony_ci pr_debug("%s at %d.%03d MHz\n", clksrc.name, divided_rate / 1000000, 4548c2ecf20Sopenharmony_ci ((divided_rate % 1000000) + 500) / 1000); 4558c2ecf20Sopenharmony_ci 4568c2ecf20Sopenharmony_ci tcaddr = tc.regs; 4578c2ecf20Sopenharmony_ci 4588c2ecf20Sopenharmony_ci if (bits == 32) { 4598c2ecf20Sopenharmony_ci /* use apropriate function to read 32 bit counter */ 4608c2ecf20Sopenharmony_ci clksrc.read = tc_get_cycles32; 4618c2ecf20Sopenharmony_ci /* setup ony channel 0 */ 4628c2ecf20Sopenharmony_ci tcb_setup_single_chan(&tc, best_divisor_idx); 4638c2ecf20Sopenharmony_ci tc_sched_clock = tc_sched_clock_read32; 4648c2ecf20Sopenharmony_ci tc_delay_timer.read_current_timer = tc_delay_timer_read32; 4658c2ecf20Sopenharmony_ci } else { 4668c2ecf20Sopenharmony_ci /* we have three clocks no matter what the 4678c2ecf20Sopenharmony_ci * underlying platform supports. 4688c2ecf20Sopenharmony_ci */ 4698c2ecf20Sopenharmony_ci ret = clk_prepare_enable(tc.clk[1]); 4708c2ecf20Sopenharmony_ci if (ret) { 4718c2ecf20Sopenharmony_ci pr_debug("can't enable T1 clk\n"); 4728c2ecf20Sopenharmony_ci goto err_disable_t0; 4738c2ecf20Sopenharmony_ci } 4748c2ecf20Sopenharmony_ci /* setup both channel 0 & 1 */ 4758c2ecf20Sopenharmony_ci tcb_setup_dual_chan(&tc, best_divisor_idx); 4768c2ecf20Sopenharmony_ci tc_sched_clock = tc_sched_clock_read; 4778c2ecf20Sopenharmony_ci tc_delay_timer.read_current_timer = tc_delay_timer_read; 4788c2ecf20Sopenharmony_ci } 4798c2ecf20Sopenharmony_ci 4808c2ecf20Sopenharmony_ci /* and away we go! */ 4818c2ecf20Sopenharmony_ci ret = clocksource_register_hz(&clksrc, divided_rate); 4828c2ecf20Sopenharmony_ci if (ret) 4838c2ecf20Sopenharmony_ci goto err_disable_t1; 4848c2ecf20Sopenharmony_ci 4858c2ecf20Sopenharmony_ci /* channel 2: periodic and oneshot timer support */ 4868c2ecf20Sopenharmony_ci ret = setup_clkevents(&tc, best_divisor_idx); 4878c2ecf20Sopenharmony_ci if (ret) 4888c2ecf20Sopenharmony_ci goto err_unregister_clksrc; 4898c2ecf20Sopenharmony_ci 4908c2ecf20Sopenharmony_ci sched_clock_register(tc_sched_clock, 32, divided_rate); 4918c2ecf20Sopenharmony_ci 4928c2ecf20Sopenharmony_ci tc_delay_timer.freq = divided_rate; 4938c2ecf20Sopenharmony_ci register_current_timer_delay(&tc_delay_timer); 4948c2ecf20Sopenharmony_ci 4958c2ecf20Sopenharmony_ci return 0; 4968c2ecf20Sopenharmony_ci 4978c2ecf20Sopenharmony_cierr_unregister_clksrc: 4988c2ecf20Sopenharmony_ci clocksource_unregister(&clksrc); 4998c2ecf20Sopenharmony_ci 5008c2ecf20Sopenharmony_cierr_disable_t1: 5018c2ecf20Sopenharmony_ci if (bits != 32) 5028c2ecf20Sopenharmony_ci clk_disable_unprepare(tc.clk[1]); 5038c2ecf20Sopenharmony_ci 5048c2ecf20Sopenharmony_cierr_disable_t0: 5058c2ecf20Sopenharmony_ci clk_disable_unprepare(t0_clk); 5068c2ecf20Sopenharmony_ci 5078c2ecf20Sopenharmony_ci tcaddr = NULL; 5088c2ecf20Sopenharmony_ci 5098c2ecf20Sopenharmony_ci return ret; 5108c2ecf20Sopenharmony_ci} 5118c2ecf20Sopenharmony_ciTIMER_OF_DECLARE(atmel_tcb_clksrc, "atmel,tcb-timer", tcb_clksrc_init); 512