517 lines · c
1// SPDX-License-Identifier: GPL-2.0-only2/*3 * Copyright (c) 2011 Samsung Electronics Co., Ltd.4 * http://www.samsung.com/5 *6 * samsung - Common hr-timer support (s3c and s5p)7 */8 9#include <linux/interrupt.h>10#include <linux/irq.h>11#include <linux/err.h>12#include <linux/clk.h>13#include <linux/clockchips.h>14#include <linux/list.h>15#include <linux/module.h>16#include <linux/of.h>17#include <linux/of_address.h>18#include <linux/of_irq.h>19#include <linux/platform_device.h>20#include <linux/slab.h>21#include <linux/sched_clock.h>22 23#include <clocksource/samsung_pwm.h>24 25/*26 * Clocksource driver27 */28 29#define REG_TCFG0 0x0030#define REG_TCFG1 0x0431#define REG_TCON 0x0832#define REG_TINT_CSTAT 0x4433 34#define REG_TCNTB(chan) (0x0c + 12 * (chan))35#define REG_TCMPB(chan) (0x10 + 12 * (chan))36 37#define TCFG0_PRESCALER_MASK 0xff38#define TCFG0_PRESCALER1_SHIFT 839 40#define TCFG1_SHIFT(x) ((x) * 4)41#define TCFG1_MUX_MASK 0xf42 43/*44 * Each channel occupies 4 bits in TCON register, but there is a gap of 445 * bits (one channel) after channel 0, so channels have different numbering46 * when accessing TCON register.47 *48 * In addition, the location of autoreload bit for channel 4 (TCON channel 5)49 * in its set of bits is 2 as opposed to 3 for other channels.50 */51#define TCON_START(chan) (1 << (4 * (chan) + 0))52#define TCON_MANUALUPDATE(chan) (1 << (4 * (chan) + 1))53#define TCON_INVERT(chan) (1 << (4 * (chan) + 2))54#define _TCON_AUTORELOAD(chan) (1 << (4 * (chan) + 3))55#define _TCON_AUTORELOAD4(chan) (1 << (4 * (chan) + 2))56#define TCON_AUTORELOAD(chan) \57 ((chan < 5) ? _TCON_AUTORELOAD(chan) : _TCON_AUTORELOAD4(chan))58 59DEFINE_SPINLOCK(samsung_pwm_lock);60EXPORT_SYMBOL(samsung_pwm_lock);61 62struct samsung_pwm_clocksource {63 void __iomem *base;64 const void __iomem *source_reg;65 unsigned int irq[SAMSUNG_PWM_NUM];66 struct samsung_pwm_variant variant;67 68 struct clk *timerclk;69 70 unsigned int event_id;71 unsigned int source_id;72 unsigned int tcnt_max;73 unsigned int tscaler_div;74 unsigned int tdiv;75 76 unsigned long clock_count_per_tick;77};78 79static struct samsung_pwm_clocksource pwm;80 81static void samsung_timer_set_prescale(unsigned int channel, u16 prescale)82{83 unsigned long flags;84 u8 shift = 0;85 u32 reg;86 87 if (channel >= 2)88 shift = TCFG0_PRESCALER1_SHIFT;89 90 spin_lock_irqsave(&samsung_pwm_lock, flags);91 92 reg = readl(pwm.base + REG_TCFG0);93 reg &= ~(TCFG0_PRESCALER_MASK << shift);94 reg |= (prescale - 1) << shift;95 writel(reg, pwm.base + REG_TCFG0);96 97 spin_unlock_irqrestore(&samsung_pwm_lock, flags);98}99 100static void samsung_timer_set_divisor(unsigned int channel, u8 divisor)101{102 u8 shift = TCFG1_SHIFT(channel);103 unsigned long flags;104 u32 reg;105 u8 bits;106 107 bits = (fls(divisor) - 1) - pwm.variant.div_base;108 109 spin_lock_irqsave(&samsung_pwm_lock, flags);110 111 reg = readl(pwm.base + REG_TCFG1);112 reg &= ~(TCFG1_MUX_MASK << shift);113 reg |= bits << shift;114 writel(reg, pwm.base + REG_TCFG1);115 116 spin_unlock_irqrestore(&samsung_pwm_lock, flags);117}118 119static void samsung_time_stop(unsigned int channel)120{121 unsigned long tcon;122 unsigned long flags;123 124 if (channel > 0)125 ++channel;126 127 spin_lock_irqsave(&samsung_pwm_lock, flags);128 129 tcon = readl_relaxed(pwm.base + REG_TCON);130 tcon &= ~TCON_START(channel);131 writel_relaxed(tcon, pwm.base + REG_TCON);132 133 spin_unlock_irqrestore(&samsung_pwm_lock, flags);134}135 136static void samsung_time_setup(unsigned int channel, unsigned long tcnt)137{138 unsigned long tcon;139 unsigned long flags;140 unsigned int tcon_chan = channel;141 142 if (tcon_chan > 0)143 ++tcon_chan;144 145 spin_lock_irqsave(&samsung_pwm_lock, flags);146 147 tcon = readl_relaxed(pwm.base + REG_TCON);148 149 tcon &= ~(TCON_START(tcon_chan) | TCON_AUTORELOAD(tcon_chan));150 tcon |= TCON_MANUALUPDATE(tcon_chan);151 152 writel_relaxed(tcnt, pwm.base + REG_TCNTB(channel));153 writel_relaxed(tcnt, pwm.base + REG_TCMPB(channel));154 writel_relaxed(tcon, pwm.base + REG_TCON);155 156 spin_unlock_irqrestore(&samsung_pwm_lock, flags);157}158 159static void samsung_time_start(unsigned int channel, bool periodic)160{161 unsigned long tcon;162 unsigned long flags;163 164 if (channel > 0)165 ++channel;166 167 spin_lock_irqsave(&samsung_pwm_lock, flags);168 169 tcon = readl_relaxed(pwm.base + REG_TCON);170 171 tcon &= ~TCON_MANUALUPDATE(channel);172 tcon |= TCON_START(channel);173 174 if (periodic)175 tcon |= TCON_AUTORELOAD(channel);176 else177 tcon &= ~TCON_AUTORELOAD(channel);178 179 writel_relaxed(tcon, pwm.base + REG_TCON);180 181 spin_unlock_irqrestore(&samsung_pwm_lock, flags);182}183 184static int samsung_set_next_event(unsigned long cycles,185 struct clock_event_device *evt)186{187 /*188 * This check is needed to account for internal rounding189 * errors inside clockevents core, which might result in190 * passing cycles = 0, which in turn would not generate any191 * timer interrupt and hang the system.192 *193 * Another solution would be to set up the clockevent device194 * with min_delta = 2, but this would unnecessarily increase195 * the minimum sleep period.196 */197 if (!cycles)198 cycles = 1;199 200 samsung_time_setup(pwm.event_id, cycles);201 samsung_time_start(pwm.event_id, false);202 203 return 0;204}205 206static int samsung_shutdown(struct clock_event_device *evt)207{208 samsung_time_stop(pwm.event_id);209 return 0;210}211 212static int samsung_set_periodic(struct clock_event_device *evt)213{214 samsung_time_stop(pwm.event_id);215 samsung_time_setup(pwm.event_id, pwm.clock_count_per_tick - 1);216 samsung_time_start(pwm.event_id, true);217 return 0;218}219 220static void samsung_clockevent_resume(struct clock_event_device *cev)221{222 samsung_timer_set_prescale(pwm.event_id, pwm.tscaler_div);223 samsung_timer_set_divisor(pwm.event_id, pwm.tdiv);224 225 if (pwm.variant.has_tint_cstat) {226 u32 mask = (1 << pwm.event_id);227 228 writel(mask | (mask << 5), pwm.base + REG_TINT_CSTAT);229 }230}231 232static struct clock_event_device time_event_device = {233 .name = "samsung_event_timer",234 .features = CLOCK_EVT_FEAT_PERIODIC |235 CLOCK_EVT_FEAT_ONESHOT,236 .rating = 200,237 .set_next_event = samsung_set_next_event,238 .set_state_shutdown = samsung_shutdown,239 .set_state_periodic = samsung_set_periodic,240 .set_state_oneshot = samsung_shutdown,241 .tick_resume = samsung_shutdown,242 .resume = samsung_clockevent_resume,243};244 245static irqreturn_t samsung_clock_event_isr(int irq, void *dev_id)246{247 struct clock_event_device *evt = dev_id;248 249 if (pwm.variant.has_tint_cstat) {250 u32 mask = (1 << pwm.event_id);251 252 writel(mask | (mask << 5), pwm.base + REG_TINT_CSTAT);253 }254 255 evt->event_handler(evt);256 257 return IRQ_HANDLED;258}259 260static void __init samsung_clockevent_init(void)261{262 unsigned long pclk;263 unsigned long clock_rate;264 unsigned int irq_number;265 266 pclk = clk_get_rate(pwm.timerclk);267 268 samsung_timer_set_prescale(pwm.event_id, pwm.tscaler_div);269 samsung_timer_set_divisor(pwm.event_id, pwm.tdiv);270 271 clock_rate = pclk / (pwm.tscaler_div * pwm.tdiv);272 pwm.clock_count_per_tick = clock_rate / HZ;273 274 time_event_device.cpumask = cpumask_of(0);275 clockevents_config_and_register(&time_event_device,276 clock_rate, 1, pwm.tcnt_max);277 278 irq_number = pwm.irq[pwm.event_id];279 if (request_irq(irq_number, samsung_clock_event_isr,280 IRQF_TIMER | IRQF_IRQPOLL, "samsung_time_irq",281 &time_event_device))282 pr_err("%s: request_irq() failed\n", "samsung_time_irq");283 284 if (pwm.variant.has_tint_cstat) {285 u32 mask = (1 << pwm.event_id);286 287 writel(mask | (mask << 5), pwm.base + REG_TINT_CSTAT);288 }289}290 291static void samsung_clocksource_suspend(struct clocksource *cs)292{293 samsung_time_stop(pwm.source_id);294}295 296static void samsung_clocksource_resume(struct clocksource *cs)297{298 samsung_timer_set_prescale(pwm.source_id, pwm.tscaler_div);299 samsung_timer_set_divisor(pwm.source_id, pwm.tdiv);300 301 samsung_time_setup(pwm.source_id, pwm.tcnt_max);302 samsung_time_start(pwm.source_id, true);303}304 305static u64 notrace samsung_clocksource_read(struct clocksource *c)306{307 return ~readl_relaxed(pwm.source_reg);308}309 310static struct clocksource samsung_clocksource = {311 .name = "samsung_clocksource_timer",312 .rating = 250,313 .read = samsung_clocksource_read,314 .suspend = samsung_clocksource_suspend,315 .resume = samsung_clocksource_resume,316 .flags = CLOCK_SOURCE_IS_CONTINUOUS,317};318 319/*320 * Override the global weak sched_clock symbol with this321 * local implementation which uses the clocksource to get some322 * better resolution when scheduling the kernel. We accept that323 * this wraps around for now, since it is just a relative time324 * stamp. (Inspired by U300 implementation.)325 */326static u64 notrace samsung_read_sched_clock(void)327{328 return samsung_clocksource_read(NULL);329}330 331static int __init samsung_clocksource_init(void)332{333 unsigned long pclk;334 unsigned long clock_rate;335 336 pclk = clk_get_rate(pwm.timerclk);337 338 samsung_timer_set_prescale(pwm.source_id, pwm.tscaler_div);339 samsung_timer_set_divisor(pwm.source_id, pwm.tdiv);340 341 clock_rate = pclk / (pwm.tscaler_div * pwm.tdiv);342 343 samsung_time_setup(pwm.source_id, pwm.tcnt_max);344 samsung_time_start(pwm.source_id, true);345 346 if (pwm.source_id == 4)347 pwm.source_reg = pwm.base + 0x40;348 else349 pwm.source_reg = pwm.base + pwm.source_id * 0x0c + 0x14;350 351 sched_clock_register(samsung_read_sched_clock,352 pwm.variant.bits, clock_rate);353 354 samsung_clocksource.mask = CLOCKSOURCE_MASK(pwm.variant.bits);355 return clocksource_register_hz(&samsung_clocksource, clock_rate);356}357 358static void __init samsung_timer_resources(void)359{360 clk_prepare_enable(pwm.timerclk);361 362 pwm.tcnt_max = (1UL << pwm.variant.bits) - 1;363 if (pwm.variant.bits == 16) {364 pwm.tscaler_div = 25;365 pwm.tdiv = 2;366 } else {367 pwm.tscaler_div = 2;368 pwm.tdiv = 1;369 }370}371 372/*373 * PWM master driver374 */375static int __init _samsung_pwm_clocksource_init(void)376{377 u8 mask;378 int channel;379 380 mask = ~pwm.variant.output_mask & ((1 << SAMSUNG_PWM_NUM) - 1);381 channel = fls(mask) - 1;382 if (channel < 0) {383 pr_crit("failed to find PWM channel for clocksource\n");384 return -EINVAL;385 }386 pwm.source_id = channel;387 388 mask &= ~(1 << channel);389 channel = fls(mask) - 1;390 if (channel < 0) {391 pr_crit("failed to find PWM channel for clock event\n");392 return -EINVAL;393 }394 pwm.event_id = channel;395 396 samsung_timer_resources();397 samsung_clockevent_init();398 399 return samsung_clocksource_init();400}401 402void __init samsung_pwm_clocksource_init(void __iomem *base,403 unsigned int *irqs,404 const struct samsung_pwm_variant *variant)405{406 pwm.base = base;407 memcpy(&pwm.variant, variant, sizeof(pwm.variant));408 memcpy(pwm.irq, irqs, SAMSUNG_PWM_NUM * sizeof(*irqs));409 410 pwm.timerclk = clk_get(NULL, "timers");411 if (IS_ERR(pwm.timerclk))412 panic("failed to get timers clock for timer");413 414 _samsung_pwm_clocksource_init();415}416 417#ifdef CONFIG_TIMER_OF418static int __init samsung_pwm_alloc(struct device_node *np,419 const struct samsung_pwm_variant *variant)420{421 u32 val;422 int i, ret;423 424 memcpy(&pwm.variant, variant, sizeof(pwm.variant));425 for (i = 0; i < SAMSUNG_PWM_NUM; ++i)426 pwm.irq[i] = irq_of_parse_and_map(np, i);427 428 of_property_for_each_u32(np, "samsung,pwm-outputs", val) {429 if (val >= SAMSUNG_PWM_NUM) {430 pr_warn("%s: invalid channel index in samsung,pwm-outputs property\n", __func__);431 continue;432 }433 pwm.variant.output_mask |= 1 << val;434 }435 436 pwm.base = of_iomap(np, 0);437 if (!pwm.base) {438 pr_err("%s: failed to map PWM registers\n", __func__);439 return -ENXIO;440 }441 442 pwm.timerclk = of_clk_get_by_name(np, "timers");443 if (IS_ERR(pwm.timerclk)) {444 pr_crit("failed to get timers clock for timer\n");445 ret = PTR_ERR(pwm.timerclk);446 goto err_clk;447 }448 449 ret = _samsung_pwm_clocksource_init();450 if (ret)451 goto err_clocksource;452 453 return 0;454 455err_clocksource:456 clk_put(pwm.timerclk);457 pwm.timerclk = NULL;458err_clk:459 iounmap(pwm.base);460 pwm.base = NULL;461 462 return ret;463}464 465static const struct samsung_pwm_variant s3c24xx_variant = {466 .bits = 16,467 .div_base = 1,468 .has_tint_cstat = false,469 .tclk_mask = (1 << 4),470};471 472static int __init s3c2410_pwm_clocksource_init(struct device_node *np)473{474 return samsung_pwm_alloc(np, &s3c24xx_variant);475}476TIMER_OF_DECLARE(s3c2410_pwm, "samsung,s3c2410-pwm", s3c2410_pwm_clocksource_init);477 478static const struct samsung_pwm_variant s3c64xx_variant = {479 .bits = 32,480 .div_base = 0,481 .has_tint_cstat = true,482 .tclk_mask = (1 << 7) | (1 << 6) | (1 << 5),483};484 485static int __init s3c64xx_pwm_clocksource_init(struct device_node *np)486{487 return samsung_pwm_alloc(np, &s3c64xx_variant);488}489TIMER_OF_DECLARE(s3c6400_pwm, "samsung,s3c6400-pwm", s3c64xx_pwm_clocksource_init);490 491static const struct samsung_pwm_variant s5p64x0_variant = {492 .bits = 32,493 .div_base = 0,494 .has_tint_cstat = true,495 .tclk_mask = 0,496};497 498static int __init s5p64x0_pwm_clocksource_init(struct device_node *np)499{500 return samsung_pwm_alloc(np, &s5p64x0_variant);501}502TIMER_OF_DECLARE(s5p6440_pwm, "samsung,s5p6440-pwm", s5p64x0_pwm_clocksource_init);503 504static const struct samsung_pwm_variant s5p_variant = {505 .bits = 32,506 .div_base = 0,507 .has_tint_cstat = true,508 .tclk_mask = (1 << 5),509};510 511static int __init s5p_pwm_clocksource_init(struct device_node *np)512{513 return samsung_pwm_alloc(np, &s5p_variant);514}515TIMER_OF_DECLARE(s5pc100_pwm, "samsung,s5pc100-pwm", s5p_pwm_clocksource_init);516#endif517