162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci#include <linux/init.h> 362306a36Sopenharmony_ci#include <linux/clocksource.h> 462306a36Sopenharmony_ci#include <linux/clockchips.h> 562306a36Sopenharmony_ci#include <linux/interrupt.h> 662306a36Sopenharmony_ci#include <linux/irq.h> 762306a36Sopenharmony_ci 862306a36Sopenharmony_ci#include <linux/clk.h> 962306a36Sopenharmony_ci#include <linux/delay.h> 1062306a36Sopenharmony_ci#include <linux/err.h> 1162306a36Sopenharmony_ci#include <linux/ioport.h> 1262306a36Sopenharmony_ci#include <linux/io.h> 1362306a36Sopenharmony_ci#include <linux/of_address.h> 1462306a36Sopenharmony_ci#include <linux/of_irq.h> 1562306a36Sopenharmony_ci#include <linux/sched_clock.h> 1662306a36Sopenharmony_ci#include <linux/syscore_ops.h> 1762306a36Sopenharmony_ci#include <soc/at91/atmel_tcb.h> 1862306a36Sopenharmony_ci 1962306a36Sopenharmony_ci 2062306a36Sopenharmony_ci/* 2162306a36Sopenharmony_ci * We're configured to use a specific TC block, one that's not hooked 2262306a36Sopenharmony_ci * up to external hardware, to provide a time solution: 2362306a36Sopenharmony_ci * 2462306a36Sopenharmony_ci * - Two channels combine to create a free-running 32 bit counter 2562306a36Sopenharmony_ci * with a base rate of 5+ MHz, packaged as a clocksource (with 2662306a36Sopenharmony_ci * resolution better than 200 nsec). 2762306a36Sopenharmony_ci * - Some chips support 32 bit counter. A single channel is used for 2862306a36Sopenharmony_ci * this 32 bit free-running counter. the second channel is not used. 2962306a36Sopenharmony_ci * 3062306a36Sopenharmony_ci * - The third channel may be used to provide a clockevent source, used in 3162306a36Sopenharmony_ci * either periodic or oneshot mode. For 16-bit counter its runs at 32 KiHZ, 3262306a36Sopenharmony_ci * and can handle delays of up to two seconds. For 32-bit counters, it runs at 3362306a36Sopenharmony_ci * the same rate as the clocksource 3462306a36Sopenharmony_ci * 3562306a36Sopenharmony_ci * REVISIT behavior during system suspend states... we should disable 3662306a36Sopenharmony_ci * all clocks and save the power. Easily done for clockevent devices, 3762306a36Sopenharmony_ci * but clocksources won't necessarily get the needed notifications. 3862306a36Sopenharmony_ci * For deeper system sleep states, this will be mandatory... 3962306a36Sopenharmony_ci */ 4062306a36Sopenharmony_ci 4162306a36Sopenharmony_cistatic void __iomem *tcaddr; 4262306a36Sopenharmony_cistatic struct 4362306a36Sopenharmony_ci{ 4462306a36Sopenharmony_ci u32 cmr; 4562306a36Sopenharmony_ci u32 imr; 4662306a36Sopenharmony_ci u32 rc; 4762306a36Sopenharmony_ci bool clken; 4862306a36Sopenharmony_ci} tcb_cache[3]; 4962306a36Sopenharmony_cistatic u32 bmr_cache; 5062306a36Sopenharmony_ci 5162306a36Sopenharmony_cistatic const u8 atmel_tcb_divisors[] = { 2, 8, 32, 128 }; 5262306a36Sopenharmony_ci 5362306a36Sopenharmony_cistatic u64 tc_get_cycles(struct clocksource *cs) 5462306a36Sopenharmony_ci{ 5562306a36Sopenharmony_ci unsigned long flags; 5662306a36Sopenharmony_ci u32 lower, upper; 5762306a36Sopenharmony_ci 5862306a36Sopenharmony_ci raw_local_irq_save(flags); 5962306a36Sopenharmony_ci do { 6062306a36Sopenharmony_ci upper = readl_relaxed(tcaddr + ATMEL_TC_REG(1, CV)); 6162306a36Sopenharmony_ci lower = readl_relaxed(tcaddr + ATMEL_TC_REG(0, CV)); 6262306a36Sopenharmony_ci } while (upper != readl_relaxed(tcaddr + ATMEL_TC_REG(1, CV))); 6362306a36Sopenharmony_ci 6462306a36Sopenharmony_ci raw_local_irq_restore(flags); 6562306a36Sopenharmony_ci return (upper << 16) | lower; 6662306a36Sopenharmony_ci} 6762306a36Sopenharmony_ci 6862306a36Sopenharmony_cistatic u64 tc_get_cycles32(struct clocksource *cs) 6962306a36Sopenharmony_ci{ 7062306a36Sopenharmony_ci return readl_relaxed(tcaddr + ATMEL_TC_REG(0, CV)); 7162306a36Sopenharmony_ci} 7262306a36Sopenharmony_ci 7362306a36Sopenharmony_cistatic void tc_clksrc_suspend(struct clocksource *cs) 7462306a36Sopenharmony_ci{ 7562306a36Sopenharmony_ci int i; 7662306a36Sopenharmony_ci 7762306a36Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(tcb_cache); i++) { 7862306a36Sopenharmony_ci tcb_cache[i].cmr = readl(tcaddr + ATMEL_TC_REG(i, CMR)); 7962306a36Sopenharmony_ci tcb_cache[i].imr = readl(tcaddr + ATMEL_TC_REG(i, IMR)); 8062306a36Sopenharmony_ci tcb_cache[i].rc = readl(tcaddr + ATMEL_TC_REG(i, RC)); 8162306a36Sopenharmony_ci tcb_cache[i].clken = !!(readl(tcaddr + ATMEL_TC_REG(i, SR)) & 8262306a36Sopenharmony_ci ATMEL_TC_CLKSTA); 8362306a36Sopenharmony_ci } 8462306a36Sopenharmony_ci 8562306a36Sopenharmony_ci bmr_cache = readl(tcaddr + ATMEL_TC_BMR); 8662306a36Sopenharmony_ci} 8762306a36Sopenharmony_ci 8862306a36Sopenharmony_cistatic void tc_clksrc_resume(struct clocksource *cs) 8962306a36Sopenharmony_ci{ 9062306a36Sopenharmony_ci int i; 9162306a36Sopenharmony_ci 9262306a36Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(tcb_cache); i++) { 9362306a36Sopenharmony_ci /* Restore registers for the channel, RA and RB are not used */ 9462306a36Sopenharmony_ci writel(tcb_cache[i].cmr, tcaddr + ATMEL_TC_REG(i, CMR)); 9562306a36Sopenharmony_ci writel(tcb_cache[i].rc, tcaddr + ATMEL_TC_REG(i, RC)); 9662306a36Sopenharmony_ci writel(0, tcaddr + ATMEL_TC_REG(i, RA)); 9762306a36Sopenharmony_ci writel(0, tcaddr + ATMEL_TC_REG(i, RB)); 9862306a36Sopenharmony_ci /* Disable all the interrupts */ 9962306a36Sopenharmony_ci writel(0xff, tcaddr + ATMEL_TC_REG(i, IDR)); 10062306a36Sopenharmony_ci /* Reenable interrupts that were enabled before suspending */ 10162306a36Sopenharmony_ci writel(tcb_cache[i].imr, tcaddr + ATMEL_TC_REG(i, IER)); 10262306a36Sopenharmony_ci /* Start the clock if it was used */ 10362306a36Sopenharmony_ci if (tcb_cache[i].clken) 10462306a36Sopenharmony_ci writel(ATMEL_TC_CLKEN, tcaddr + ATMEL_TC_REG(i, CCR)); 10562306a36Sopenharmony_ci } 10662306a36Sopenharmony_ci 10762306a36Sopenharmony_ci /* Dual channel, chain channels */ 10862306a36Sopenharmony_ci writel(bmr_cache, tcaddr + ATMEL_TC_BMR); 10962306a36Sopenharmony_ci /* Finally, trigger all the channels*/ 11062306a36Sopenharmony_ci writel(ATMEL_TC_SYNC, tcaddr + ATMEL_TC_BCR); 11162306a36Sopenharmony_ci} 11262306a36Sopenharmony_ci 11362306a36Sopenharmony_cistatic struct clocksource clksrc = { 11462306a36Sopenharmony_ci .rating = 200, 11562306a36Sopenharmony_ci .read = tc_get_cycles, 11662306a36Sopenharmony_ci .mask = CLOCKSOURCE_MASK(32), 11762306a36Sopenharmony_ci .flags = CLOCK_SOURCE_IS_CONTINUOUS, 11862306a36Sopenharmony_ci .suspend = tc_clksrc_suspend, 11962306a36Sopenharmony_ci .resume = tc_clksrc_resume, 12062306a36Sopenharmony_ci}; 12162306a36Sopenharmony_ci 12262306a36Sopenharmony_cistatic u64 notrace tc_sched_clock_read(void) 12362306a36Sopenharmony_ci{ 12462306a36Sopenharmony_ci return tc_get_cycles(&clksrc); 12562306a36Sopenharmony_ci} 12662306a36Sopenharmony_ci 12762306a36Sopenharmony_cistatic u64 notrace tc_sched_clock_read32(void) 12862306a36Sopenharmony_ci{ 12962306a36Sopenharmony_ci return tc_get_cycles32(&clksrc); 13062306a36Sopenharmony_ci} 13162306a36Sopenharmony_ci 13262306a36Sopenharmony_cistatic struct delay_timer tc_delay_timer; 13362306a36Sopenharmony_ci 13462306a36Sopenharmony_cistatic unsigned long tc_delay_timer_read(void) 13562306a36Sopenharmony_ci{ 13662306a36Sopenharmony_ci return tc_get_cycles(&clksrc); 13762306a36Sopenharmony_ci} 13862306a36Sopenharmony_ci 13962306a36Sopenharmony_cistatic unsigned long notrace tc_delay_timer_read32(void) 14062306a36Sopenharmony_ci{ 14162306a36Sopenharmony_ci return tc_get_cycles32(&clksrc); 14262306a36Sopenharmony_ci} 14362306a36Sopenharmony_ci 14462306a36Sopenharmony_ci#ifdef CONFIG_GENERIC_CLOCKEVENTS 14562306a36Sopenharmony_ci 14662306a36Sopenharmony_cistruct tc_clkevt_device { 14762306a36Sopenharmony_ci struct clock_event_device clkevt; 14862306a36Sopenharmony_ci struct clk *clk; 14962306a36Sopenharmony_ci u32 rate; 15062306a36Sopenharmony_ci void __iomem *regs; 15162306a36Sopenharmony_ci}; 15262306a36Sopenharmony_ci 15362306a36Sopenharmony_cistatic struct tc_clkevt_device *to_tc_clkevt(struct clock_event_device *clkevt) 15462306a36Sopenharmony_ci{ 15562306a36Sopenharmony_ci return container_of(clkevt, struct tc_clkevt_device, clkevt); 15662306a36Sopenharmony_ci} 15762306a36Sopenharmony_ci 15862306a36Sopenharmony_cistatic u32 timer_clock; 15962306a36Sopenharmony_ci 16062306a36Sopenharmony_cistatic int tc_shutdown(struct clock_event_device *d) 16162306a36Sopenharmony_ci{ 16262306a36Sopenharmony_ci struct tc_clkevt_device *tcd = to_tc_clkevt(d); 16362306a36Sopenharmony_ci void __iomem *regs = tcd->regs; 16462306a36Sopenharmony_ci 16562306a36Sopenharmony_ci writel(0xff, regs + ATMEL_TC_REG(2, IDR)); 16662306a36Sopenharmony_ci writel(ATMEL_TC_CLKDIS, regs + ATMEL_TC_REG(2, CCR)); 16762306a36Sopenharmony_ci if (!clockevent_state_detached(d)) 16862306a36Sopenharmony_ci clk_disable(tcd->clk); 16962306a36Sopenharmony_ci 17062306a36Sopenharmony_ci return 0; 17162306a36Sopenharmony_ci} 17262306a36Sopenharmony_ci 17362306a36Sopenharmony_cistatic int tc_set_oneshot(struct clock_event_device *d) 17462306a36Sopenharmony_ci{ 17562306a36Sopenharmony_ci struct tc_clkevt_device *tcd = to_tc_clkevt(d); 17662306a36Sopenharmony_ci void __iomem *regs = tcd->regs; 17762306a36Sopenharmony_ci 17862306a36Sopenharmony_ci if (clockevent_state_oneshot(d) || clockevent_state_periodic(d)) 17962306a36Sopenharmony_ci tc_shutdown(d); 18062306a36Sopenharmony_ci 18162306a36Sopenharmony_ci clk_enable(tcd->clk); 18262306a36Sopenharmony_ci 18362306a36Sopenharmony_ci /* count up to RC, then irq and stop */ 18462306a36Sopenharmony_ci writel(timer_clock | ATMEL_TC_CPCSTOP | ATMEL_TC_WAVE | 18562306a36Sopenharmony_ci ATMEL_TC_WAVESEL_UP_AUTO, regs + ATMEL_TC_REG(2, CMR)); 18662306a36Sopenharmony_ci writel(ATMEL_TC_CPCS, regs + ATMEL_TC_REG(2, IER)); 18762306a36Sopenharmony_ci 18862306a36Sopenharmony_ci /* set_next_event() configures and starts the timer */ 18962306a36Sopenharmony_ci return 0; 19062306a36Sopenharmony_ci} 19162306a36Sopenharmony_ci 19262306a36Sopenharmony_cistatic int tc_set_periodic(struct clock_event_device *d) 19362306a36Sopenharmony_ci{ 19462306a36Sopenharmony_ci struct tc_clkevt_device *tcd = to_tc_clkevt(d); 19562306a36Sopenharmony_ci void __iomem *regs = tcd->regs; 19662306a36Sopenharmony_ci 19762306a36Sopenharmony_ci if (clockevent_state_oneshot(d) || clockevent_state_periodic(d)) 19862306a36Sopenharmony_ci tc_shutdown(d); 19962306a36Sopenharmony_ci 20062306a36Sopenharmony_ci /* By not making the gentime core emulate periodic mode on top 20162306a36Sopenharmony_ci * of oneshot, we get lower overhead and improved accuracy. 20262306a36Sopenharmony_ci */ 20362306a36Sopenharmony_ci clk_enable(tcd->clk); 20462306a36Sopenharmony_ci 20562306a36Sopenharmony_ci /* count up to RC, then irq and restart */ 20662306a36Sopenharmony_ci writel(timer_clock | ATMEL_TC_WAVE | ATMEL_TC_WAVESEL_UP_AUTO, 20762306a36Sopenharmony_ci regs + ATMEL_TC_REG(2, CMR)); 20862306a36Sopenharmony_ci writel((tcd->rate + HZ / 2) / HZ, tcaddr + ATMEL_TC_REG(2, RC)); 20962306a36Sopenharmony_ci 21062306a36Sopenharmony_ci /* Enable clock and interrupts on RC compare */ 21162306a36Sopenharmony_ci writel(ATMEL_TC_CPCS, regs + ATMEL_TC_REG(2, IER)); 21262306a36Sopenharmony_ci 21362306a36Sopenharmony_ci /* go go gadget! */ 21462306a36Sopenharmony_ci writel(ATMEL_TC_CLKEN | ATMEL_TC_SWTRG, regs + 21562306a36Sopenharmony_ci ATMEL_TC_REG(2, CCR)); 21662306a36Sopenharmony_ci return 0; 21762306a36Sopenharmony_ci} 21862306a36Sopenharmony_ci 21962306a36Sopenharmony_cistatic int tc_next_event(unsigned long delta, struct clock_event_device *d) 22062306a36Sopenharmony_ci{ 22162306a36Sopenharmony_ci writel_relaxed(delta, tcaddr + ATMEL_TC_REG(2, RC)); 22262306a36Sopenharmony_ci 22362306a36Sopenharmony_ci /* go go gadget! */ 22462306a36Sopenharmony_ci writel_relaxed(ATMEL_TC_CLKEN | ATMEL_TC_SWTRG, 22562306a36Sopenharmony_ci tcaddr + ATMEL_TC_REG(2, CCR)); 22662306a36Sopenharmony_ci return 0; 22762306a36Sopenharmony_ci} 22862306a36Sopenharmony_ci 22962306a36Sopenharmony_cistatic struct tc_clkevt_device clkevt = { 23062306a36Sopenharmony_ci .clkevt = { 23162306a36Sopenharmony_ci .features = CLOCK_EVT_FEAT_PERIODIC | 23262306a36Sopenharmony_ci CLOCK_EVT_FEAT_ONESHOT, 23362306a36Sopenharmony_ci /* Should be lower than at91rm9200's system timer */ 23462306a36Sopenharmony_ci .rating = 125, 23562306a36Sopenharmony_ci .set_next_event = tc_next_event, 23662306a36Sopenharmony_ci .set_state_shutdown = tc_shutdown, 23762306a36Sopenharmony_ci .set_state_periodic = tc_set_periodic, 23862306a36Sopenharmony_ci .set_state_oneshot = tc_set_oneshot, 23962306a36Sopenharmony_ci }, 24062306a36Sopenharmony_ci}; 24162306a36Sopenharmony_ci 24262306a36Sopenharmony_cistatic irqreturn_t ch2_irq(int irq, void *handle) 24362306a36Sopenharmony_ci{ 24462306a36Sopenharmony_ci struct tc_clkevt_device *dev = handle; 24562306a36Sopenharmony_ci unsigned int sr; 24662306a36Sopenharmony_ci 24762306a36Sopenharmony_ci sr = readl_relaxed(dev->regs + ATMEL_TC_REG(2, SR)); 24862306a36Sopenharmony_ci if (sr & ATMEL_TC_CPCS) { 24962306a36Sopenharmony_ci dev->clkevt.event_handler(&dev->clkevt); 25062306a36Sopenharmony_ci return IRQ_HANDLED; 25162306a36Sopenharmony_ci } 25262306a36Sopenharmony_ci 25362306a36Sopenharmony_ci return IRQ_NONE; 25462306a36Sopenharmony_ci} 25562306a36Sopenharmony_ci 25662306a36Sopenharmony_cistatic int __init setup_clkevents(struct atmel_tc *tc, int divisor_idx) 25762306a36Sopenharmony_ci{ 25862306a36Sopenharmony_ci int ret; 25962306a36Sopenharmony_ci struct clk *t2_clk = tc->clk[2]; 26062306a36Sopenharmony_ci int irq = tc->irq[2]; 26162306a36Sopenharmony_ci int bits = tc->tcb_config->counter_width; 26262306a36Sopenharmony_ci 26362306a36Sopenharmony_ci /* try to enable t2 clk to avoid future errors in mode change */ 26462306a36Sopenharmony_ci ret = clk_prepare_enable(t2_clk); 26562306a36Sopenharmony_ci if (ret) 26662306a36Sopenharmony_ci return ret; 26762306a36Sopenharmony_ci 26862306a36Sopenharmony_ci clkevt.regs = tc->regs; 26962306a36Sopenharmony_ci clkevt.clk = t2_clk; 27062306a36Sopenharmony_ci 27162306a36Sopenharmony_ci if (bits == 32) { 27262306a36Sopenharmony_ci timer_clock = divisor_idx; 27362306a36Sopenharmony_ci clkevt.rate = clk_get_rate(t2_clk) / atmel_tcb_divisors[divisor_idx]; 27462306a36Sopenharmony_ci } else { 27562306a36Sopenharmony_ci ret = clk_prepare_enable(tc->slow_clk); 27662306a36Sopenharmony_ci if (ret) { 27762306a36Sopenharmony_ci clk_disable_unprepare(t2_clk); 27862306a36Sopenharmony_ci return ret; 27962306a36Sopenharmony_ci } 28062306a36Sopenharmony_ci 28162306a36Sopenharmony_ci clkevt.rate = clk_get_rate(tc->slow_clk); 28262306a36Sopenharmony_ci timer_clock = ATMEL_TC_TIMER_CLOCK5; 28362306a36Sopenharmony_ci } 28462306a36Sopenharmony_ci 28562306a36Sopenharmony_ci clk_disable(t2_clk); 28662306a36Sopenharmony_ci 28762306a36Sopenharmony_ci clkevt.clkevt.cpumask = cpumask_of(0); 28862306a36Sopenharmony_ci 28962306a36Sopenharmony_ci ret = request_irq(irq, ch2_irq, IRQF_TIMER, "tc_clkevt", &clkevt); 29062306a36Sopenharmony_ci if (ret) { 29162306a36Sopenharmony_ci clk_unprepare(t2_clk); 29262306a36Sopenharmony_ci if (bits != 32) 29362306a36Sopenharmony_ci clk_disable_unprepare(tc->slow_clk); 29462306a36Sopenharmony_ci return ret; 29562306a36Sopenharmony_ci } 29662306a36Sopenharmony_ci 29762306a36Sopenharmony_ci clockevents_config_and_register(&clkevt.clkevt, clkevt.rate, 1, BIT(bits) - 1); 29862306a36Sopenharmony_ci 29962306a36Sopenharmony_ci return ret; 30062306a36Sopenharmony_ci} 30162306a36Sopenharmony_ci 30262306a36Sopenharmony_ci#else /* !CONFIG_GENERIC_CLOCKEVENTS */ 30362306a36Sopenharmony_ci 30462306a36Sopenharmony_cistatic int __init setup_clkevents(struct atmel_tc *tc, int divisor_idx) 30562306a36Sopenharmony_ci{ 30662306a36Sopenharmony_ci /* NOTHING */ 30762306a36Sopenharmony_ci return 0; 30862306a36Sopenharmony_ci} 30962306a36Sopenharmony_ci 31062306a36Sopenharmony_ci#endif 31162306a36Sopenharmony_ci 31262306a36Sopenharmony_cistatic void __init tcb_setup_dual_chan(struct atmel_tc *tc, int mck_divisor_idx) 31362306a36Sopenharmony_ci{ 31462306a36Sopenharmony_ci /* channel 0: waveform mode, input mclk/8, clock TIOA0 on overflow */ 31562306a36Sopenharmony_ci writel(mck_divisor_idx /* likely divide-by-8 */ 31662306a36Sopenharmony_ci | ATMEL_TC_WAVE 31762306a36Sopenharmony_ci | ATMEL_TC_WAVESEL_UP /* free-run */ 31862306a36Sopenharmony_ci | ATMEL_TC_ASWTRG_SET /* TIOA0 rises at software trigger */ 31962306a36Sopenharmony_ci | ATMEL_TC_ACPA_SET /* TIOA0 rises at 0 */ 32062306a36Sopenharmony_ci | ATMEL_TC_ACPC_CLEAR, /* (duty cycle 50%) */ 32162306a36Sopenharmony_ci tcaddr + ATMEL_TC_REG(0, CMR)); 32262306a36Sopenharmony_ci writel(0x0000, tcaddr + ATMEL_TC_REG(0, RA)); 32362306a36Sopenharmony_ci writel(0x8000, tcaddr + ATMEL_TC_REG(0, RC)); 32462306a36Sopenharmony_ci writel(0xff, tcaddr + ATMEL_TC_REG(0, IDR)); /* no irqs */ 32562306a36Sopenharmony_ci writel(ATMEL_TC_CLKEN, tcaddr + ATMEL_TC_REG(0, CCR)); 32662306a36Sopenharmony_ci 32762306a36Sopenharmony_ci /* channel 1: waveform mode, input TIOA0 */ 32862306a36Sopenharmony_ci writel(ATMEL_TC_XC1 /* input: TIOA0 */ 32962306a36Sopenharmony_ci | ATMEL_TC_WAVE 33062306a36Sopenharmony_ci | ATMEL_TC_WAVESEL_UP, /* free-run */ 33162306a36Sopenharmony_ci tcaddr + ATMEL_TC_REG(1, CMR)); 33262306a36Sopenharmony_ci writel(0xff, tcaddr + ATMEL_TC_REG(1, IDR)); /* no irqs */ 33362306a36Sopenharmony_ci writel(ATMEL_TC_CLKEN, tcaddr + ATMEL_TC_REG(1, CCR)); 33462306a36Sopenharmony_ci 33562306a36Sopenharmony_ci /* chain channel 0 to channel 1*/ 33662306a36Sopenharmony_ci writel(ATMEL_TC_TC1XC1S_TIOA0, tcaddr + ATMEL_TC_BMR); 33762306a36Sopenharmony_ci /* then reset all the timers */ 33862306a36Sopenharmony_ci writel(ATMEL_TC_SYNC, tcaddr + ATMEL_TC_BCR); 33962306a36Sopenharmony_ci} 34062306a36Sopenharmony_ci 34162306a36Sopenharmony_cistatic void __init tcb_setup_single_chan(struct atmel_tc *tc, int mck_divisor_idx) 34262306a36Sopenharmony_ci{ 34362306a36Sopenharmony_ci /* channel 0: waveform mode, input mclk/8 */ 34462306a36Sopenharmony_ci writel(mck_divisor_idx /* likely divide-by-8 */ 34562306a36Sopenharmony_ci | ATMEL_TC_WAVE 34662306a36Sopenharmony_ci | ATMEL_TC_WAVESEL_UP, /* free-run */ 34762306a36Sopenharmony_ci tcaddr + ATMEL_TC_REG(0, CMR)); 34862306a36Sopenharmony_ci writel(0xff, tcaddr + ATMEL_TC_REG(0, IDR)); /* no irqs */ 34962306a36Sopenharmony_ci writel(ATMEL_TC_CLKEN, tcaddr + ATMEL_TC_REG(0, CCR)); 35062306a36Sopenharmony_ci 35162306a36Sopenharmony_ci /* then reset all the timers */ 35262306a36Sopenharmony_ci writel(ATMEL_TC_SYNC, tcaddr + ATMEL_TC_BCR); 35362306a36Sopenharmony_ci} 35462306a36Sopenharmony_ci 35562306a36Sopenharmony_cistatic struct atmel_tcb_config tcb_rm9200_config = { 35662306a36Sopenharmony_ci .counter_width = 16, 35762306a36Sopenharmony_ci}; 35862306a36Sopenharmony_ci 35962306a36Sopenharmony_cistatic struct atmel_tcb_config tcb_sam9x5_config = { 36062306a36Sopenharmony_ci .counter_width = 32, 36162306a36Sopenharmony_ci}; 36262306a36Sopenharmony_ci 36362306a36Sopenharmony_cistatic struct atmel_tcb_config tcb_sama5d2_config = { 36462306a36Sopenharmony_ci .counter_width = 32, 36562306a36Sopenharmony_ci .has_gclk = 1, 36662306a36Sopenharmony_ci}; 36762306a36Sopenharmony_ci 36862306a36Sopenharmony_cistatic const struct of_device_id atmel_tcb_of_match[] = { 36962306a36Sopenharmony_ci { .compatible = "atmel,at91rm9200-tcb", .data = &tcb_rm9200_config, }, 37062306a36Sopenharmony_ci { .compatible = "atmel,at91sam9x5-tcb", .data = &tcb_sam9x5_config, }, 37162306a36Sopenharmony_ci { .compatible = "atmel,sama5d2-tcb", .data = &tcb_sama5d2_config, }, 37262306a36Sopenharmony_ci { /* sentinel */ } 37362306a36Sopenharmony_ci}; 37462306a36Sopenharmony_ci 37562306a36Sopenharmony_cistatic int __init tcb_clksrc_init(struct device_node *node) 37662306a36Sopenharmony_ci{ 37762306a36Sopenharmony_ci struct atmel_tc tc; 37862306a36Sopenharmony_ci struct clk *t0_clk; 37962306a36Sopenharmony_ci const struct of_device_id *match; 38062306a36Sopenharmony_ci u64 (*tc_sched_clock)(void); 38162306a36Sopenharmony_ci u32 rate, divided_rate = 0; 38262306a36Sopenharmony_ci int best_divisor_idx = -1; 38362306a36Sopenharmony_ci int bits; 38462306a36Sopenharmony_ci int i; 38562306a36Sopenharmony_ci int ret; 38662306a36Sopenharmony_ci 38762306a36Sopenharmony_ci /* Protect against multiple calls */ 38862306a36Sopenharmony_ci if (tcaddr) 38962306a36Sopenharmony_ci return 0; 39062306a36Sopenharmony_ci 39162306a36Sopenharmony_ci tc.regs = of_iomap(node->parent, 0); 39262306a36Sopenharmony_ci if (!tc.regs) 39362306a36Sopenharmony_ci return -ENXIO; 39462306a36Sopenharmony_ci 39562306a36Sopenharmony_ci t0_clk = of_clk_get_by_name(node->parent, "t0_clk"); 39662306a36Sopenharmony_ci if (IS_ERR(t0_clk)) 39762306a36Sopenharmony_ci return PTR_ERR(t0_clk); 39862306a36Sopenharmony_ci 39962306a36Sopenharmony_ci tc.slow_clk = of_clk_get_by_name(node->parent, "slow_clk"); 40062306a36Sopenharmony_ci if (IS_ERR(tc.slow_clk)) 40162306a36Sopenharmony_ci return PTR_ERR(tc.slow_clk); 40262306a36Sopenharmony_ci 40362306a36Sopenharmony_ci tc.clk[0] = t0_clk; 40462306a36Sopenharmony_ci tc.clk[1] = of_clk_get_by_name(node->parent, "t1_clk"); 40562306a36Sopenharmony_ci if (IS_ERR(tc.clk[1])) 40662306a36Sopenharmony_ci tc.clk[1] = t0_clk; 40762306a36Sopenharmony_ci tc.clk[2] = of_clk_get_by_name(node->parent, "t2_clk"); 40862306a36Sopenharmony_ci if (IS_ERR(tc.clk[2])) 40962306a36Sopenharmony_ci tc.clk[2] = t0_clk; 41062306a36Sopenharmony_ci 41162306a36Sopenharmony_ci tc.irq[2] = of_irq_get(node->parent, 2); 41262306a36Sopenharmony_ci if (tc.irq[2] <= 0) { 41362306a36Sopenharmony_ci tc.irq[2] = of_irq_get(node->parent, 0); 41462306a36Sopenharmony_ci if (tc.irq[2] <= 0) 41562306a36Sopenharmony_ci return -EINVAL; 41662306a36Sopenharmony_ci } 41762306a36Sopenharmony_ci 41862306a36Sopenharmony_ci match = of_match_node(atmel_tcb_of_match, node->parent); 41962306a36Sopenharmony_ci if (!match) 42062306a36Sopenharmony_ci return -ENODEV; 42162306a36Sopenharmony_ci 42262306a36Sopenharmony_ci tc.tcb_config = match->data; 42362306a36Sopenharmony_ci bits = tc.tcb_config->counter_width; 42462306a36Sopenharmony_ci 42562306a36Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(tc.irq); i++) 42662306a36Sopenharmony_ci writel(ATMEL_TC_ALL_IRQ, tc.regs + ATMEL_TC_REG(i, IDR)); 42762306a36Sopenharmony_ci 42862306a36Sopenharmony_ci ret = clk_prepare_enable(t0_clk); 42962306a36Sopenharmony_ci if (ret) { 43062306a36Sopenharmony_ci pr_debug("can't enable T0 clk\n"); 43162306a36Sopenharmony_ci return ret; 43262306a36Sopenharmony_ci } 43362306a36Sopenharmony_ci 43462306a36Sopenharmony_ci /* How fast will we be counting? Pick something over 5 MHz. */ 43562306a36Sopenharmony_ci rate = (u32) clk_get_rate(t0_clk); 43662306a36Sopenharmony_ci i = 0; 43762306a36Sopenharmony_ci if (tc.tcb_config->has_gclk) 43862306a36Sopenharmony_ci i = 1; 43962306a36Sopenharmony_ci for (; i < ARRAY_SIZE(atmel_tcb_divisors); i++) { 44062306a36Sopenharmony_ci unsigned divisor = atmel_tcb_divisors[i]; 44162306a36Sopenharmony_ci unsigned tmp; 44262306a36Sopenharmony_ci 44362306a36Sopenharmony_ci tmp = rate / divisor; 44462306a36Sopenharmony_ci pr_debug("TC: %u / %-3u [%d] --> %u\n", rate, divisor, i, tmp); 44562306a36Sopenharmony_ci if ((best_divisor_idx >= 0) && (tmp < 5 * 1000 * 1000)) 44662306a36Sopenharmony_ci break; 44762306a36Sopenharmony_ci divided_rate = tmp; 44862306a36Sopenharmony_ci best_divisor_idx = i; 44962306a36Sopenharmony_ci } 45062306a36Sopenharmony_ci 45162306a36Sopenharmony_ci clksrc.name = kbasename(node->parent->full_name); 45262306a36Sopenharmony_ci clkevt.clkevt.name = kbasename(node->parent->full_name); 45362306a36Sopenharmony_ci pr_debug("%s at %d.%03d MHz\n", clksrc.name, divided_rate / 1000000, 45462306a36Sopenharmony_ci ((divided_rate % 1000000) + 500) / 1000); 45562306a36Sopenharmony_ci 45662306a36Sopenharmony_ci tcaddr = tc.regs; 45762306a36Sopenharmony_ci 45862306a36Sopenharmony_ci if (bits == 32) { 45962306a36Sopenharmony_ci /* use appropriate function to read 32 bit counter */ 46062306a36Sopenharmony_ci clksrc.read = tc_get_cycles32; 46162306a36Sopenharmony_ci /* setup only channel 0 */ 46262306a36Sopenharmony_ci tcb_setup_single_chan(&tc, best_divisor_idx); 46362306a36Sopenharmony_ci tc_sched_clock = tc_sched_clock_read32; 46462306a36Sopenharmony_ci tc_delay_timer.read_current_timer = tc_delay_timer_read32; 46562306a36Sopenharmony_ci } else { 46662306a36Sopenharmony_ci /* we have three clocks no matter what the 46762306a36Sopenharmony_ci * underlying platform supports. 46862306a36Sopenharmony_ci */ 46962306a36Sopenharmony_ci ret = clk_prepare_enable(tc.clk[1]); 47062306a36Sopenharmony_ci if (ret) { 47162306a36Sopenharmony_ci pr_debug("can't enable T1 clk\n"); 47262306a36Sopenharmony_ci goto err_disable_t0; 47362306a36Sopenharmony_ci } 47462306a36Sopenharmony_ci /* setup both channel 0 & 1 */ 47562306a36Sopenharmony_ci tcb_setup_dual_chan(&tc, best_divisor_idx); 47662306a36Sopenharmony_ci tc_sched_clock = tc_sched_clock_read; 47762306a36Sopenharmony_ci tc_delay_timer.read_current_timer = tc_delay_timer_read; 47862306a36Sopenharmony_ci } 47962306a36Sopenharmony_ci 48062306a36Sopenharmony_ci /* and away we go! */ 48162306a36Sopenharmony_ci ret = clocksource_register_hz(&clksrc, divided_rate); 48262306a36Sopenharmony_ci if (ret) 48362306a36Sopenharmony_ci goto err_disable_t1; 48462306a36Sopenharmony_ci 48562306a36Sopenharmony_ci /* channel 2: periodic and oneshot timer support */ 48662306a36Sopenharmony_ci ret = setup_clkevents(&tc, best_divisor_idx); 48762306a36Sopenharmony_ci if (ret) 48862306a36Sopenharmony_ci goto err_unregister_clksrc; 48962306a36Sopenharmony_ci 49062306a36Sopenharmony_ci sched_clock_register(tc_sched_clock, 32, divided_rate); 49162306a36Sopenharmony_ci 49262306a36Sopenharmony_ci tc_delay_timer.freq = divided_rate; 49362306a36Sopenharmony_ci register_current_timer_delay(&tc_delay_timer); 49462306a36Sopenharmony_ci 49562306a36Sopenharmony_ci return 0; 49662306a36Sopenharmony_ci 49762306a36Sopenharmony_cierr_unregister_clksrc: 49862306a36Sopenharmony_ci clocksource_unregister(&clksrc); 49962306a36Sopenharmony_ci 50062306a36Sopenharmony_cierr_disable_t1: 50162306a36Sopenharmony_ci if (bits != 32) 50262306a36Sopenharmony_ci clk_disable_unprepare(tc.clk[1]); 50362306a36Sopenharmony_ci 50462306a36Sopenharmony_cierr_disable_t0: 50562306a36Sopenharmony_ci clk_disable_unprepare(t0_clk); 50662306a36Sopenharmony_ci 50762306a36Sopenharmony_ci tcaddr = NULL; 50862306a36Sopenharmony_ci 50962306a36Sopenharmony_ci return ret; 51062306a36Sopenharmony_ci} 51162306a36Sopenharmony_ciTIMER_OF_DECLARE(atmel_tcb_clksrc, "atmel,tcb-timer", tcb_clksrc_init); 512