18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Copyright (c) 2011 Samsung Electronics Co., Ltd. 48c2ecf20Sopenharmony_ci * http://www.samsung.com/ 58c2ecf20Sopenharmony_ci * 68c2ecf20Sopenharmony_ci * samsung - Common hr-timer support (s3c and s5p) 78c2ecf20Sopenharmony_ci*/ 88c2ecf20Sopenharmony_ci 98c2ecf20Sopenharmony_ci#include <linux/interrupt.h> 108c2ecf20Sopenharmony_ci#include <linux/irq.h> 118c2ecf20Sopenharmony_ci#include <linux/err.h> 128c2ecf20Sopenharmony_ci#include <linux/clk.h> 138c2ecf20Sopenharmony_ci#include <linux/clockchips.h> 148c2ecf20Sopenharmony_ci#include <linux/list.h> 158c2ecf20Sopenharmony_ci#include <linux/module.h> 168c2ecf20Sopenharmony_ci#include <linux/of.h> 178c2ecf20Sopenharmony_ci#include <linux/of_address.h> 188c2ecf20Sopenharmony_ci#include <linux/of_irq.h> 198c2ecf20Sopenharmony_ci#include <linux/platform_device.h> 208c2ecf20Sopenharmony_ci#include <linux/slab.h> 218c2ecf20Sopenharmony_ci#include <linux/sched_clock.h> 228c2ecf20Sopenharmony_ci 238c2ecf20Sopenharmony_ci#include <clocksource/samsung_pwm.h> 248c2ecf20Sopenharmony_ci 258c2ecf20Sopenharmony_ci 268c2ecf20Sopenharmony_ci/* 278c2ecf20Sopenharmony_ci * Clocksource driver 288c2ecf20Sopenharmony_ci */ 298c2ecf20Sopenharmony_ci 308c2ecf20Sopenharmony_ci#define REG_TCFG0 0x00 318c2ecf20Sopenharmony_ci#define REG_TCFG1 0x04 328c2ecf20Sopenharmony_ci#define REG_TCON 0x08 338c2ecf20Sopenharmony_ci#define REG_TINT_CSTAT 0x44 348c2ecf20Sopenharmony_ci 358c2ecf20Sopenharmony_ci#define REG_TCNTB(chan) (0x0c + 12 * (chan)) 368c2ecf20Sopenharmony_ci#define REG_TCMPB(chan) (0x10 + 12 * (chan)) 378c2ecf20Sopenharmony_ci 388c2ecf20Sopenharmony_ci#define TCFG0_PRESCALER_MASK 0xff 398c2ecf20Sopenharmony_ci#define TCFG0_PRESCALER1_SHIFT 8 408c2ecf20Sopenharmony_ci 418c2ecf20Sopenharmony_ci#define TCFG1_SHIFT(x) ((x) * 4) 428c2ecf20Sopenharmony_ci#define TCFG1_MUX_MASK 0xf 438c2ecf20Sopenharmony_ci 448c2ecf20Sopenharmony_ci/* 458c2ecf20Sopenharmony_ci * Each channel occupies 4 bits in TCON register, but there is a gap of 4 468c2ecf20Sopenharmony_ci * bits (one channel) after channel 0, so channels have different numbering 478c2ecf20Sopenharmony_ci * when accessing TCON register. 488c2ecf20Sopenharmony_ci * 498c2ecf20Sopenharmony_ci * In addition, the location of autoreload bit for channel 4 (TCON channel 5) 508c2ecf20Sopenharmony_ci * in its set of bits is 2 as opposed to 3 for other channels. 518c2ecf20Sopenharmony_ci */ 528c2ecf20Sopenharmony_ci#define TCON_START(chan) (1 << (4 * (chan) + 0)) 538c2ecf20Sopenharmony_ci#define TCON_MANUALUPDATE(chan) (1 << (4 * (chan) + 1)) 548c2ecf20Sopenharmony_ci#define TCON_INVERT(chan) (1 << (4 * (chan) + 2)) 558c2ecf20Sopenharmony_ci#define _TCON_AUTORELOAD(chan) (1 << (4 * (chan) + 3)) 568c2ecf20Sopenharmony_ci#define _TCON_AUTORELOAD4(chan) (1 << (4 * (chan) + 2)) 578c2ecf20Sopenharmony_ci#define TCON_AUTORELOAD(chan) \ 588c2ecf20Sopenharmony_ci ((chan < 5) ? _TCON_AUTORELOAD(chan) : _TCON_AUTORELOAD4(chan)) 598c2ecf20Sopenharmony_ci 608c2ecf20Sopenharmony_ciDEFINE_SPINLOCK(samsung_pwm_lock); 618c2ecf20Sopenharmony_ciEXPORT_SYMBOL(samsung_pwm_lock); 628c2ecf20Sopenharmony_ci 638c2ecf20Sopenharmony_cistruct samsung_pwm_clocksource { 648c2ecf20Sopenharmony_ci void __iomem *base; 658c2ecf20Sopenharmony_ci void __iomem *source_reg; 668c2ecf20Sopenharmony_ci unsigned int irq[SAMSUNG_PWM_NUM]; 678c2ecf20Sopenharmony_ci struct samsung_pwm_variant variant; 688c2ecf20Sopenharmony_ci 698c2ecf20Sopenharmony_ci struct clk *timerclk; 708c2ecf20Sopenharmony_ci 718c2ecf20Sopenharmony_ci unsigned int event_id; 728c2ecf20Sopenharmony_ci unsigned int source_id; 738c2ecf20Sopenharmony_ci unsigned int tcnt_max; 748c2ecf20Sopenharmony_ci unsigned int tscaler_div; 758c2ecf20Sopenharmony_ci unsigned int tdiv; 768c2ecf20Sopenharmony_ci 778c2ecf20Sopenharmony_ci unsigned long clock_count_per_tick; 788c2ecf20Sopenharmony_ci}; 798c2ecf20Sopenharmony_ci 808c2ecf20Sopenharmony_cistatic struct samsung_pwm_clocksource pwm; 818c2ecf20Sopenharmony_ci 828c2ecf20Sopenharmony_cistatic void samsung_timer_set_prescale(unsigned int channel, u16 prescale) 838c2ecf20Sopenharmony_ci{ 848c2ecf20Sopenharmony_ci unsigned long flags; 858c2ecf20Sopenharmony_ci u8 shift = 0; 868c2ecf20Sopenharmony_ci u32 reg; 878c2ecf20Sopenharmony_ci 888c2ecf20Sopenharmony_ci if (channel >= 2) 898c2ecf20Sopenharmony_ci shift = TCFG0_PRESCALER1_SHIFT; 908c2ecf20Sopenharmony_ci 918c2ecf20Sopenharmony_ci spin_lock_irqsave(&samsung_pwm_lock, flags); 928c2ecf20Sopenharmony_ci 938c2ecf20Sopenharmony_ci reg = readl(pwm.base + REG_TCFG0); 948c2ecf20Sopenharmony_ci reg &= ~(TCFG0_PRESCALER_MASK << shift); 958c2ecf20Sopenharmony_ci reg |= (prescale - 1) << shift; 968c2ecf20Sopenharmony_ci writel(reg, pwm.base + REG_TCFG0); 978c2ecf20Sopenharmony_ci 988c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&samsung_pwm_lock, flags); 998c2ecf20Sopenharmony_ci} 1008c2ecf20Sopenharmony_ci 1018c2ecf20Sopenharmony_cistatic void samsung_timer_set_divisor(unsigned int channel, u8 divisor) 1028c2ecf20Sopenharmony_ci{ 1038c2ecf20Sopenharmony_ci u8 shift = TCFG1_SHIFT(channel); 1048c2ecf20Sopenharmony_ci unsigned long flags; 1058c2ecf20Sopenharmony_ci u32 reg; 1068c2ecf20Sopenharmony_ci u8 bits; 1078c2ecf20Sopenharmony_ci 1088c2ecf20Sopenharmony_ci bits = (fls(divisor) - 1) - pwm.variant.div_base; 1098c2ecf20Sopenharmony_ci 1108c2ecf20Sopenharmony_ci spin_lock_irqsave(&samsung_pwm_lock, flags); 1118c2ecf20Sopenharmony_ci 1128c2ecf20Sopenharmony_ci reg = readl(pwm.base + REG_TCFG1); 1138c2ecf20Sopenharmony_ci reg &= ~(TCFG1_MUX_MASK << shift); 1148c2ecf20Sopenharmony_ci reg |= bits << shift; 1158c2ecf20Sopenharmony_ci writel(reg, pwm.base + REG_TCFG1); 1168c2ecf20Sopenharmony_ci 1178c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&samsung_pwm_lock, flags); 1188c2ecf20Sopenharmony_ci} 1198c2ecf20Sopenharmony_ci 1208c2ecf20Sopenharmony_cistatic void samsung_time_stop(unsigned int channel) 1218c2ecf20Sopenharmony_ci{ 1228c2ecf20Sopenharmony_ci unsigned long tcon; 1238c2ecf20Sopenharmony_ci unsigned long flags; 1248c2ecf20Sopenharmony_ci 1258c2ecf20Sopenharmony_ci if (channel > 0) 1268c2ecf20Sopenharmony_ci ++channel; 1278c2ecf20Sopenharmony_ci 1288c2ecf20Sopenharmony_ci spin_lock_irqsave(&samsung_pwm_lock, flags); 1298c2ecf20Sopenharmony_ci 1308c2ecf20Sopenharmony_ci tcon = readl_relaxed(pwm.base + REG_TCON); 1318c2ecf20Sopenharmony_ci tcon &= ~TCON_START(channel); 1328c2ecf20Sopenharmony_ci writel_relaxed(tcon, pwm.base + REG_TCON); 1338c2ecf20Sopenharmony_ci 1348c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&samsung_pwm_lock, flags); 1358c2ecf20Sopenharmony_ci} 1368c2ecf20Sopenharmony_ci 1378c2ecf20Sopenharmony_cistatic void samsung_time_setup(unsigned int channel, unsigned long tcnt) 1388c2ecf20Sopenharmony_ci{ 1398c2ecf20Sopenharmony_ci unsigned long tcon; 1408c2ecf20Sopenharmony_ci unsigned long flags; 1418c2ecf20Sopenharmony_ci unsigned int tcon_chan = channel; 1428c2ecf20Sopenharmony_ci 1438c2ecf20Sopenharmony_ci if (tcon_chan > 0) 1448c2ecf20Sopenharmony_ci ++tcon_chan; 1458c2ecf20Sopenharmony_ci 1468c2ecf20Sopenharmony_ci spin_lock_irqsave(&samsung_pwm_lock, flags); 1478c2ecf20Sopenharmony_ci 1488c2ecf20Sopenharmony_ci tcon = readl_relaxed(pwm.base + REG_TCON); 1498c2ecf20Sopenharmony_ci 1508c2ecf20Sopenharmony_ci tcon &= ~(TCON_START(tcon_chan) | TCON_AUTORELOAD(tcon_chan)); 1518c2ecf20Sopenharmony_ci tcon |= TCON_MANUALUPDATE(tcon_chan); 1528c2ecf20Sopenharmony_ci 1538c2ecf20Sopenharmony_ci writel_relaxed(tcnt, pwm.base + REG_TCNTB(channel)); 1548c2ecf20Sopenharmony_ci writel_relaxed(tcnt, pwm.base + REG_TCMPB(channel)); 1558c2ecf20Sopenharmony_ci writel_relaxed(tcon, pwm.base + REG_TCON); 1568c2ecf20Sopenharmony_ci 1578c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&samsung_pwm_lock, flags); 1588c2ecf20Sopenharmony_ci} 1598c2ecf20Sopenharmony_ci 1608c2ecf20Sopenharmony_cistatic void samsung_time_start(unsigned int channel, bool periodic) 1618c2ecf20Sopenharmony_ci{ 1628c2ecf20Sopenharmony_ci unsigned long tcon; 1638c2ecf20Sopenharmony_ci unsigned long flags; 1648c2ecf20Sopenharmony_ci 1658c2ecf20Sopenharmony_ci if (channel > 0) 1668c2ecf20Sopenharmony_ci ++channel; 1678c2ecf20Sopenharmony_ci 1688c2ecf20Sopenharmony_ci spin_lock_irqsave(&samsung_pwm_lock, flags); 1698c2ecf20Sopenharmony_ci 1708c2ecf20Sopenharmony_ci tcon = readl_relaxed(pwm.base + REG_TCON); 1718c2ecf20Sopenharmony_ci 1728c2ecf20Sopenharmony_ci tcon &= ~TCON_MANUALUPDATE(channel); 1738c2ecf20Sopenharmony_ci tcon |= TCON_START(channel); 1748c2ecf20Sopenharmony_ci 1758c2ecf20Sopenharmony_ci if (periodic) 1768c2ecf20Sopenharmony_ci tcon |= TCON_AUTORELOAD(channel); 1778c2ecf20Sopenharmony_ci else 1788c2ecf20Sopenharmony_ci tcon &= ~TCON_AUTORELOAD(channel); 1798c2ecf20Sopenharmony_ci 1808c2ecf20Sopenharmony_ci writel_relaxed(tcon, pwm.base + REG_TCON); 1818c2ecf20Sopenharmony_ci 1828c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&samsung_pwm_lock, flags); 1838c2ecf20Sopenharmony_ci} 1848c2ecf20Sopenharmony_ci 1858c2ecf20Sopenharmony_cistatic int samsung_set_next_event(unsigned long cycles, 1868c2ecf20Sopenharmony_ci struct clock_event_device *evt) 1878c2ecf20Sopenharmony_ci{ 1888c2ecf20Sopenharmony_ci /* 1898c2ecf20Sopenharmony_ci * This check is needed to account for internal rounding 1908c2ecf20Sopenharmony_ci * errors inside clockevents core, which might result in 1918c2ecf20Sopenharmony_ci * passing cycles = 0, which in turn would not generate any 1928c2ecf20Sopenharmony_ci * timer interrupt and hang the system. 1938c2ecf20Sopenharmony_ci * 1948c2ecf20Sopenharmony_ci * Another solution would be to set up the clockevent device 1958c2ecf20Sopenharmony_ci * with min_delta = 2, but this would unnecessarily increase 1968c2ecf20Sopenharmony_ci * the minimum sleep period. 1978c2ecf20Sopenharmony_ci */ 1988c2ecf20Sopenharmony_ci if (!cycles) 1998c2ecf20Sopenharmony_ci cycles = 1; 2008c2ecf20Sopenharmony_ci 2018c2ecf20Sopenharmony_ci samsung_time_setup(pwm.event_id, cycles); 2028c2ecf20Sopenharmony_ci samsung_time_start(pwm.event_id, false); 2038c2ecf20Sopenharmony_ci 2048c2ecf20Sopenharmony_ci return 0; 2058c2ecf20Sopenharmony_ci} 2068c2ecf20Sopenharmony_ci 2078c2ecf20Sopenharmony_cistatic int samsung_shutdown(struct clock_event_device *evt) 2088c2ecf20Sopenharmony_ci{ 2098c2ecf20Sopenharmony_ci samsung_time_stop(pwm.event_id); 2108c2ecf20Sopenharmony_ci return 0; 2118c2ecf20Sopenharmony_ci} 2128c2ecf20Sopenharmony_ci 2138c2ecf20Sopenharmony_cistatic int samsung_set_periodic(struct clock_event_device *evt) 2148c2ecf20Sopenharmony_ci{ 2158c2ecf20Sopenharmony_ci samsung_time_stop(pwm.event_id); 2168c2ecf20Sopenharmony_ci samsung_time_setup(pwm.event_id, pwm.clock_count_per_tick - 1); 2178c2ecf20Sopenharmony_ci samsung_time_start(pwm.event_id, true); 2188c2ecf20Sopenharmony_ci return 0; 2198c2ecf20Sopenharmony_ci} 2208c2ecf20Sopenharmony_ci 2218c2ecf20Sopenharmony_cistatic void samsung_clockevent_resume(struct clock_event_device *cev) 2228c2ecf20Sopenharmony_ci{ 2238c2ecf20Sopenharmony_ci samsung_timer_set_prescale(pwm.event_id, pwm.tscaler_div); 2248c2ecf20Sopenharmony_ci samsung_timer_set_divisor(pwm.event_id, pwm.tdiv); 2258c2ecf20Sopenharmony_ci 2268c2ecf20Sopenharmony_ci if (pwm.variant.has_tint_cstat) { 2278c2ecf20Sopenharmony_ci u32 mask = (1 << pwm.event_id); 2288c2ecf20Sopenharmony_ci writel(mask | (mask << 5), pwm.base + REG_TINT_CSTAT); 2298c2ecf20Sopenharmony_ci } 2308c2ecf20Sopenharmony_ci} 2318c2ecf20Sopenharmony_ci 2328c2ecf20Sopenharmony_cistatic struct clock_event_device time_event_device = { 2338c2ecf20Sopenharmony_ci .name = "samsung_event_timer", 2348c2ecf20Sopenharmony_ci .features = CLOCK_EVT_FEAT_PERIODIC | 2358c2ecf20Sopenharmony_ci CLOCK_EVT_FEAT_ONESHOT, 2368c2ecf20Sopenharmony_ci .rating = 200, 2378c2ecf20Sopenharmony_ci .set_next_event = samsung_set_next_event, 2388c2ecf20Sopenharmony_ci .set_state_shutdown = samsung_shutdown, 2398c2ecf20Sopenharmony_ci .set_state_periodic = samsung_set_periodic, 2408c2ecf20Sopenharmony_ci .set_state_oneshot = samsung_shutdown, 2418c2ecf20Sopenharmony_ci .tick_resume = samsung_shutdown, 2428c2ecf20Sopenharmony_ci .resume = samsung_clockevent_resume, 2438c2ecf20Sopenharmony_ci}; 2448c2ecf20Sopenharmony_ci 2458c2ecf20Sopenharmony_cistatic irqreturn_t samsung_clock_event_isr(int irq, void *dev_id) 2468c2ecf20Sopenharmony_ci{ 2478c2ecf20Sopenharmony_ci struct clock_event_device *evt = dev_id; 2488c2ecf20Sopenharmony_ci 2498c2ecf20Sopenharmony_ci if (pwm.variant.has_tint_cstat) { 2508c2ecf20Sopenharmony_ci u32 mask = (1 << pwm.event_id); 2518c2ecf20Sopenharmony_ci writel(mask | (mask << 5), pwm.base + REG_TINT_CSTAT); 2528c2ecf20Sopenharmony_ci } 2538c2ecf20Sopenharmony_ci 2548c2ecf20Sopenharmony_ci evt->event_handler(evt); 2558c2ecf20Sopenharmony_ci 2568c2ecf20Sopenharmony_ci return IRQ_HANDLED; 2578c2ecf20Sopenharmony_ci} 2588c2ecf20Sopenharmony_ci 2598c2ecf20Sopenharmony_cistatic void __init samsung_clockevent_init(void) 2608c2ecf20Sopenharmony_ci{ 2618c2ecf20Sopenharmony_ci unsigned long pclk; 2628c2ecf20Sopenharmony_ci unsigned long clock_rate; 2638c2ecf20Sopenharmony_ci unsigned int irq_number; 2648c2ecf20Sopenharmony_ci 2658c2ecf20Sopenharmony_ci pclk = clk_get_rate(pwm.timerclk); 2668c2ecf20Sopenharmony_ci 2678c2ecf20Sopenharmony_ci samsung_timer_set_prescale(pwm.event_id, pwm.tscaler_div); 2688c2ecf20Sopenharmony_ci samsung_timer_set_divisor(pwm.event_id, pwm.tdiv); 2698c2ecf20Sopenharmony_ci 2708c2ecf20Sopenharmony_ci clock_rate = pclk / (pwm.tscaler_div * pwm.tdiv); 2718c2ecf20Sopenharmony_ci pwm.clock_count_per_tick = clock_rate / HZ; 2728c2ecf20Sopenharmony_ci 2738c2ecf20Sopenharmony_ci time_event_device.cpumask = cpumask_of(0); 2748c2ecf20Sopenharmony_ci clockevents_config_and_register(&time_event_device, 2758c2ecf20Sopenharmony_ci clock_rate, 1, pwm.tcnt_max); 2768c2ecf20Sopenharmony_ci 2778c2ecf20Sopenharmony_ci irq_number = pwm.irq[pwm.event_id]; 2788c2ecf20Sopenharmony_ci if (request_irq(irq_number, samsung_clock_event_isr, 2798c2ecf20Sopenharmony_ci IRQF_TIMER | IRQF_IRQPOLL, "samsung_time_irq", 2808c2ecf20Sopenharmony_ci &time_event_device)) 2818c2ecf20Sopenharmony_ci pr_err("%s: request_irq() failed\n", "samsung_time_irq"); 2828c2ecf20Sopenharmony_ci 2838c2ecf20Sopenharmony_ci if (pwm.variant.has_tint_cstat) { 2848c2ecf20Sopenharmony_ci u32 mask = (1 << pwm.event_id); 2858c2ecf20Sopenharmony_ci writel(mask | (mask << 5), pwm.base + REG_TINT_CSTAT); 2868c2ecf20Sopenharmony_ci } 2878c2ecf20Sopenharmony_ci} 2888c2ecf20Sopenharmony_ci 2898c2ecf20Sopenharmony_cistatic void samsung_clocksource_suspend(struct clocksource *cs) 2908c2ecf20Sopenharmony_ci{ 2918c2ecf20Sopenharmony_ci samsung_time_stop(pwm.source_id); 2928c2ecf20Sopenharmony_ci} 2938c2ecf20Sopenharmony_ci 2948c2ecf20Sopenharmony_cistatic void samsung_clocksource_resume(struct clocksource *cs) 2958c2ecf20Sopenharmony_ci{ 2968c2ecf20Sopenharmony_ci samsung_timer_set_prescale(pwm.source_id, pwm.tscaler_div); 2978c2ecf20Sopenharmony_ci samsung_timer_set_divisor(pwm.source_id, pwm.tdiv); 2988c2ecf20Sopenharmony_ci 2998c2ecf20Sopenharmony_ci samsung_time_setup(pwm.source_id, pwm.tcnt_max); 3008c2ecf20Sopenharmony_ci samsung_time_start(pwm.source_id, true); 3018c2ecf20Sopenharmony_ci} 3028c2ecf20Sopenharmony_ci 3038c2ecf20Sopenharmony_cistatic u64 notrace samsung_clocksource_read(struct clocksource *c) 3048c2ecf20Sopenharmony_ci{ 3058c2ecf20Sopenharmony_ci return ~readl_relaxed(pwm.source_reg); 3068c2ecf20Sopenharmony_ci} 3078c2ecf20Sopenharmony_ci 3088c2ecf20Sopenharmony_cistatic struct clocksource samsung_clocksource = { 3098c2ecf20Sopenharmony_ci .name = "samsung_clocksource_timer", 3108c2ecf20Sopenharmony_ci .rating = 250, 3118c2ecf20Sopenharmony_ci .read = samsung_clocksource_read, 3128c2ecf20Sopenharmony_ci .suspend = samsung_clocksource_suspend, 3138c2ecf20Sopenharmony_ci .resume = samsung_clocksource_resume, 3148c2ecf20Sopenharmony_ci .flags = CLOCK_SOURCE_IS_CONTINUOUS, 3158c2ecf20Sopenharmony_ci}; 3168c2ecf20Sopenharmony_ci 3178c2ecf20Sopenharmony_ci/* 3188c2ecf20Sopenharmony_ci * Override the global weak sched_clock symbol with this 3198c2ecf20Sopenharmony_ci * local implementation which uses the clocksource to get some 3208c2ecf20Sopenharmony_ci * better resolution when scheduling the kernel. We accept that 3218c2ecf20Sopenharmony_ci * this wraps around for now, since it is just a relative time 3228c2ecf20Sopenharmony_ci * stamp. (Inspired by U300 implementation.) 3238c2ecf20Sopenharmony_ci */ 3248c2ecf20Sopenharmony_cistatic u64 notrace samsung_read_sched_clock(void) 3258c2ecf20Sopenharmony_ci{ 3268c2ecf20Sopenharmony_ci return samsung_clocksource_read(NULL); 3278c2ecf20Sopenharmony_ci} 3288c2ecf20Sopenharmony_ci 3298c2ecf20Sopenharmony_cistatic int __init samsung_clocksource_init(void) 3308c2ecf20Sopenharmony_ci{ 3318c2ecf20Sopenharmony_ci unsigned long pclk; 3328c2ecf20Sopenharmony_ci unsigned long clock_rate; 3338c2ecf20Sopenharmony_ci 3348c2ecf20Sopenharmony_ci pclk = clk_get_rate(pwm.timerclk); 3358c2ecf20Sopenharmony_ci 3368c2ecf20Sopenharmony_ci samsung_timer_set_prescale(pwm.source_id, pwm.tscaler_div); 3378c2ecf20Sopenharmony_ci samsung_timer_set_divisor(pwm.source_id, pwm.tdiv); 3388c2ecf20Sopenharmony_ci 3398c2ecf20Sopenharmony_ci clock_rate = pclk / (pwm.tscaler_div * pwm.tdiv); 3408c2ecf20Sopenharmony_ci 3418c2ecf20Sopenharmony_ci samsung_time_setup(pwm.source_id, pwm.tcnt_max); 3428c2ecf20Sopenharmony_ci samsung_time_start(pwm.source_id, true); 3438c2ecf20Sopenharmony_ci 3448c2ecf20Sopenharmony_ci if (pwm.source_id == 4) 3458c2ecf20Sopenharmony_ci pwm.source_reg = pwm.base + 0x40; 3468c2ecf20Sopenharmony_ci else 3478c2ecf20Sopenharmony_ci pwm.source_reg = pwm.base + pwm.source_id * 0x0c + 0x14; 3488c2ecf20Sopenharmony_ci 3498c2ecf20Sopenharmony_ci sched_clock_register(samsung_read_sched_clock, 3508c2ecf20Sopenharmony_ci pwm.variant.bits, clock_rate); 3518c2ecf20Sopenharmony_ci 3528c2ecf20Sopenharmony_ci samsung_clocksource.mask = CLOCKSOURCE_MASK(pwm.variant.bits); 3538c2ecf20Sopenharmony_ci return clocksource_register_hz(&samsung_clocksource, clock_rate); 3548c2ecf20Sopenharmony_ci} 3558c2ecf20Sopenharmony_ci 3568c2ecf20Sopenharmony_cistatic void __init samsung_timer_resources(void) 3578c2ecf20Sopenharmony_ci{ 3588c2ecf20Sopenharmony_ci clk_prepare_enable(pwm.timerclk); 3598c2ecf20Sopenharmony_ci 3608c2ecf20Sopenharmony_ci pwm.tcnt_max = (1UL << pwm.variant.bits) - 1; 3618c2ecf20Sopenharmony_ci if (pwm.variant.bits == 16) { 3628c2ecf20Sopenharmony_ci pwm.tscaler_div = 25; 3638c2ecf20Sopenharmony_ci pwm.tdiv = 2; 3648c2ecf20Sopenharmony_ci } else { 3658c2ecf20Sopenharmony_ci pwm.tscaler_div = 2; 3668c2ecf20Sopenharmony_ci pwm.tdiv = 1; 3678c2ecf20Sopenharmony_ci } 3688c2ecf20Sopenharmony_ci} 3698c2ecf20Sopenharmony_ci 3708c2ecf20Sopenharmony_ci/* 3718c2ecf20Sopenharmony_ci * PWM master driver 3728c2ecf20Sopenharmony_ci */ 3738c2ecf20Sopenharmony_cistatic int __init _samsung_pwm_clocksource_init(void) 3748c2ecf20Sopenharmony_ci{ 3758c2ecf20Sopenharmony_ci u8 mask; 3768c2ecf20Sopenharmony_ci int channel; 3778c2ecf20Sopenharmony_ci 3788c2ecf20Sopenharmony_ci mask = ~pwm.variant.output_mask & ((1 << SAMSUNG_PWM_NUM) - 1); 3798c2ecf20Sopenharmony_ci channel = fls(mask) - 1; 3808c2ecf20Sopenharmony_ci if (channel < 0) { 3818c2ecf20Sopenharmony_ci pr_crit("failed to find PWM channel for clocksource\n"); 3828c2ecf20Sopenharmony_ci return -EINVAL; 3838c2ecf20Sopenharmony_ci } 3848c2ecf20Sopenharmony_ci pwm.source_id = channel; 3858c2ecf20Sopenharmony_ci 3868c2ecf20Sopenharmony_ci mask &= ~(1 << channel); 3878c2ecf20Sopenharmony_ci channel = fls(mask) - 1; 3888c2ecf20Sopenharmony_ci if (channel < 0) { 3898c2ecf20Sopenharmony_ci pr_crit("failed to find PWM channel for clock event\n"); 3908c2ecf20Sopenharmony_ci return -EINVAL; 3918c2ecf20Sopenharmony_ci } 3928c2ecf20Sopenharmony_ci pwm.event_id = channel; 3938c2ecf20Sopenharmony_ci 3948c2ecf20Sopenharmony_ci samsung_timer_resources(); 3958c2ecf20Sopenharmony_ci samsung_clockevent_init(); 3968c2ecf20Sopenharmony_ci 3978c2ecf20Sopenharmony_ci return samsung_clocksource_init(); 3988c2ecf20Sopenharmony_ci} 3998c2ecf20Sopenharmony_ci 4008c2ecf20Sopenharmony_civoid __init samsung_pwm_clocksource_init(void __iomem *base, 4018c2ecf20Sopenharmony_ci unsigned int *irqs, struct samsung_pwm_variant *variant) 4028c2ecf20Sopenharmony_ci{ 4038c2ecf20Sopenharmony_ci pwm.base = base; 4048c2ecf20Sopenharmony_ci memcpy(&pwm.variant, variant, sizeof(pwm.variant)); 4058c2ecf20Sopenharmony_ci memcpy(pwm.irq, irqs, SAMSUNG_PWM_NUM * sizeof(*irqs)); 4068c2ecf20Sopenharmony_ci 4078c2ecf20Sopenharmony_ci pwm.timerclk = clk_get(NULL, "timers"); 4088c2ecf20Sopenharmony_ci if (IS_ERR(pwm.timerclk)) 4098c2ecf20Sopenharmony_ci panic("failed to get timers clock for timer"); 4108c2ecf20Sopenharmony_ci 4118c2ecf20Sopenharmony_ci _samsung_pwm_clocksource_init(); 4128c2ecf20Sopenharmony_ci} 4138c2ecf20Sopenharmony_ci 4148c2ecf20Sopenharmony_ci#ifdef CONFIG_TIMER_OF 4158c2ecf20Sopenharmony_cistatic int __init samsung_pwm_alloc(struct device_node *np, 4168c2ecf20Sopenharmony_ci const struct samsung_pwm_variant *variant) 4178c2ecf20Sopenharmony_ci{ 4188c2ecf20Sopenharmony_ci struct property *prop; 4198c2ecf20Sopenharmony_ci const __be32 *cur; 4208c2ecf20Sopenharmony_ci u32 val; 4218c2ecf20Sopenharmony_ci int i; 4228c2ecf20Sopenharmony_ci 4238c2ecf20Sopenharmony_ci memcpy(&pwm.variant, variant, sizeof(pwm.variant)); 4248c2ecf20Sopenharmony_ci for (i = 0; i < SAMSUNG_PWM_NUM; ++i) 4258c2ecf20Sopenharmony_ci pwm.irq[i] = irq_of_parse_and_map(np, i); 4268c2ecf20Sopenharmony_ci 4278c2ecf20Sopenharmony_ci of_property_for_each_u32(np, "samsung,pwm-outputs", prop, cur, val) { 4288c2ecf20Sopenharmony_ci if (val >= SAMSUNG_PWM_NUM) { 4298c2ecf20Sopenharmony_ci pr_warn("%s: invalid channel index in samsung,pwm-outputs property\n", __func__); 4308c2ecf20Sopenharmony_ci continue; 4318c2ecf20Sopenharmony_ci } 4328c2ecf20Sopenharmony_ci pwm.variant.output_mask |= 1 << val; 4338c2ecf20Sopenharmony_ci } 4348c2ecf20Sopenharmony_ci 4358c2ecf20Sopenharmony_ci pwm.base = of_iomap(np, 0); 4368c2ecf20Sopenharmony_ci if (!pwm.base) { 4378c2ecf20Sopenharmony_ci pr_err("%s: failed to map PWM registers\n", __func__); 4388c2ecf20Sopenharmony_ci return -ENXIO; 4398c2ecf20Sopenharmony_ci } 4408c2ecf20Sopenharmony_ci 4418c2ecf20Sopenharmony_ci pwm.timerclk = of_clk_get_by_name(np, "timers"); 4428c2ecf20Sopenharmony_ci if (IS_ERR(pwm.timerclk)) { 4438c2ecf20Sopenharmony_ci pr_crit("failed to get timers clock for timer\n"); 4448c2ecf20Sopenharmony_ci return PTR_ERR(pwm.timerclk); 4458c2ecf20Sopenharmony_ci } 4468c2ecf20Sopenharmony_ci 4478c2ecf20Sopenharmony_ci return _samsung_pwm_clocksource_init(); 4488c2ecf20Sopenharmony_ci} 4498c2ecf20Sopenharmony_ci 4508c2ecf20Sopenharmony_cistatic const struct samsung_pwm_variant s3c24xx_variant = { 4518c2ecf20Sopenharmony_ci .bits = 16, 4528c2ecf20Sopenharmony_ci .div_base = 1, 4538c2ecf20Sopenharmony_ci .has_tint_cstat = false, 4548c2ecf20Sopenharmony_ci .tclk_mask = (1 << 4), 4558c2ecf20Sopenharmony_ci}; 4568c2ecf20Sopenharmony_ci 4578c2ecf20Sopenharmony_cistatic int __init s3c2410_pwm_clocksource_init(struct device_node *np) 4588c2ecf20Sopenharmony_ci{ 4598c2ecf20Sopenharmony_ci return samsung_pwm_alloc(np, &s3c24xx_variant); 4608c2ecf20Sopenharmony_ci} 4618c2ecf20Sopenharmony_ciTIMER_OF_DECLARE(s3c2410_pwm, "samsung,s3c2410-pwm", s3c2410_pwm_clocksource_init); 4628c2ecf20Sopenharmony_ci 4638c2ecf20Sopenharmony_cistatic const struct samsung_pwm_variant s3c64xx_variant = { 4648c2ecf20Sopenharmony_ci .bits = 32, 4658c2ecf20Sopenharmony_ci .div_base = 0, 4668c2ecf20Sopenharmony_ci .has_tint_cstat = true, 4678c2ecf20Sopenharmony_ci .tclk_mask = (1 << 7) | (1 << 6) | (1 << 5), 4688c2ecf20Sopenharmony_ci}; 4698c2ecf20Sopenharmony_ci 4708c2ecf20Sopenharmony_cistatic int __init s3c64xx_pwm_clocksource_init(struct device_node *np) 4718c2ecf20Sopenharmony_ci{ 4728c2ecf20Sopenharmony_ci return samsung_pwm_alloc(np, &s3c64xx_variant); 4738c2ecf20Sopenharmony_ci} 4748c2ecf20Sopenharmony_ciTIMER_OF_DECLARE(s3c6400_pwm, "samsung,s3c6400-pwm", s3c64xx_pwm_clocksource_init); 4758c2ecf20Sopenharmony_ci 4768c2ecf20Sopenharmony_cistatic const struct samsung_pwm_variant s5p64x0_variant = { 4778c2ecf20Sopenharmony_ci .bits = 32, 4788c2ecf20Sopenharmony_ci .div_base = 0, 4798c2ecf20Sopenharmony_ci .has_tint_cstat = true, 4808c2ecf20Sopenharmony_ci .tclk_mask = 0, 4818c2ecf20Sopenharmony_ci}; 4828c2ecf20Sopenharmony_ci 4838c2ecf20Sopenharmony_cistatic int __init s5p64x0_pwm_clocksource_init(struct device_node *np) 4848c2ecf20Sopenharmony_ci{ 4858c2ecf20Sopenharmony_ci return samsung_pwm_alloc(np, &s5p64x0_variant); 4868c2ecf20Sopenharmony_ci} 4878c2ecf20Sopenharmony_ciTIMER_OF_DECLARE(s5p6440_pwm, "samsung,s5p6440-pwm", s5p64x0_pwm_clocksource_init); 4888c2ecf20Sopenharmony_ci 4898c2ecf20Sopenharmony_cistatic const struct samsung_pwm_variant s5p_variant = { 4908c2ecf20Sopenharmony_ci .bits = 32, 4918c2ecf20Sopenharmony_ci .div_base = 0, 4928c2ecf20Sopenharmony_ci .has_tint_cstat = true, 4938c2ecf20Sopenharmony_ci .tclk_mask = (1 << 5), 4948c2ecf20Sopenharmony_ci}; 4958c2ecf20Sopenharmony_ci 4968c2ecf20Sopenharmony_cistatic int __init s5p_pwm_clocksource_init(struct device_node *np) 4978c2ecf20Sopenharmony_ci{ 4988c2ecf20Sopenharmony_ci return samsung_pwm_alloc(np, &s5p_variant); 4998c2ecf20Sopenharmony_ci} 5008c2ecf20Sopenharmony_ciTIMER_OF_DECLARE(s5pc100_pwm, "samsung,s5pc100-pwm", s5p_pwm_clocksource_init); 5018c2ecf20Sopenharmony_ci#endif 502