1 /* 2 * Copyright (c) 2011 Samsung Electronics Co., Ltd. 3 * http://www.samsung.com/ 4 * 5 * samsung - Common hr-timer support (s3c and s5p) 6 * 7 * This program is free software; you can redistribute it and/or modify 8 * it under the terms of the GNU General Public License version 2 as 9 * published by the Free Software Foundation. 10 */ 11 12 #include <linux/interrupt.h> 13 #include <linux/irq.h> 14 #include <linux/err.h> 15 #include <linux/clk.h> 16 #include <linux/clockchips.h> 17 #include <linux/list.h> 18 #include <linux/module.h> 19 #include <linux/of.h> 20 #include <linux/of_address.h> 21 #include <linux/of_irq.h> 22 #include <linux/platform_device.h> 23 #include <linux/slab.h> 24 #include <linux/sched_clock.h> 25 26 #include <clocksource/samsung_pwm.h> 27 28 29 /* 30 * Clocksource driver 31 */ 32 33 #define REG_TCFG0 0x00 34 #define REG_TCFG1 0x04 35 #define REG_TCON 0x08 36 #define REG_TINT_CSTAT 0x44 37 38 #define REG_TCNTB(chan) (0x0c + 12 * (chan)) 39 #define REG_TCMPB(chan) (0x10 + 12 * (chan)) 40 41 #define TCFG0_PRESCALER_MASK 0xff 42 #define TCFG0_PRESCALER1_SHIFT 8 43 44 #define TCFG1_SHIFT(x) ((x) * 4) 45 #define TCFG1_MUX_MASK 0xf 46 47 #define TCON_START(chan) (1 << (4 * (chan) + 0)) 48 #define TCON_MANUALUPDATE(chan) (1 << (4 * (chan) + 1)) 49 #define TCON_INVERT(chan) (1 << (4 * (chan) + 2)) 50 #define TCON_AUTORELOAD(chan) (1 << (4 * (chan) + 3)) 51 52 DEFINE_SPINLOCK(samsung_pwm_lock); 53 EXPORT_SYMBOL(samsung_pwm_lock); 54 55 struct samsung_pwm_clocksource { 56 void __iomem *base; 57 unsigned int irq[SAMSUNG_PWM_NUM]; 58 struct samsung_pwm_variant variant; 59 60 struct clk *timerclk; 61 62 unsigned int event_id; 63 unsigned int source_id; 64 unsigned int tcnt_max; 65 unsigned int tscaler_div; 66 unsigned int tdiv; 67 68 unsigned long clock_count_per_tick; 69 }; 70 71 static struct samsung_pwm_clocksource pwm; 72 73 static void samsung_timer_set_prescale(unsigned int channel, u16 prescale) 74 { 75 unsigned long flags; 76 u8 shift = 0; 77 u32 reg; 78 79 if (channel >= 2) 80 shift = TCFG0_PRESCALER1_SHIFT; 81 82 spin_lock_irqsave(&samsung_pwm_lock, flags); 83 84 reg = readl(pwm.base + REG_TCFG0); 85 reg &= ~(TCFG0_PRESCALER_MASK << shift); 86 reg |= (prescale - 1) << shift; 87 writel(reg, pwm.base + REG_TCFG0); 88 89 spin_unlock_irqrestore(&samsung_pwm_lock, flags); 90 } 91 92 static void samsung_timer_set_divisor(unsigned int channel, u8 divisor) 93 { 94 u8 shift = TCFG1_SHIFT(channel); 95 unsigned long flags; 96 u32 reg; 97 u8 bits; 98 99 bits = (fls(divisor) - 1) - pwm.variant.div_base; 100 101 spin_lock_irqsave(&samsung_pwm_lock, flags); 102 103 reg = readl(pwm.base + REG_TCFG1); 104 reg &= ~(TCFG1_MUX_MASK << shift); 105 reg |= bits << shift; 106 writel(reg, pwm.base + REG_TCFG1); 107 108 spin_unlock_irqrestore(&samsung_pwm_lock, flags); 109 } 110 111 static void samsung_time_stop(unsigned int channel) 112 { 113 unsigned long tcon; 114 unsigned long flags; 115 116 if (channel > 0) 117 ++channel; 118 119 spin_lock_irqsave(&samsung_pwm_lock, flags); 120 121 tcon = __raw_readl(pwm.base + REG_TCON); 122 tcon &= ~TCON_START(channel); 123 __raw_writel(tcon, pwm.base + REG_TCON); 124 125 spin_unlock_irqrestore(&samsung_pwm_lock, flags); 126 } 127 128 static void samsung_time_setup(unsigned int channel, unsigned long tcnt) 129 { 130 unsigned long tcon; 131 unsigned long flags; 132 unsigned int tcon_chan = channel; 133 134 if (tcon_chan > 0) 135 ++tcon_chan; 136 137 spin_lock_irqsave(&samsung_pwm_lock, flags); 138 139 tcon = __raw_readl(pwm.base + REG_TCON); 140 141 tcon &= ~(TCON_START(tcon_chan) | TCON_AUTORELOAD(tcon_chan)); 142 tcon |= TCON_MANUALUPDATE(tcon_chan); 143 144 __raw_writel(tcnt, pwm.base + REG_TCNTB(channel)); 145 __raw_writel(tcnt, pwm.base + REG_TCMPB(channel)); 146 __raw_writel(tcon, pwm.base + REG_TCON); 147 148 spin_unlock_irqrestore(&samsung_pwm_lock, flags); 149 } 150 151 static void samsung_time_start(unsigned int channel, bool periodic) 152 { 153 unsigned long tcon; 154 unsigned long flags; 155 156 if (channel > 0) 157 ++channel; 158 159 spin_lock_irqsave(&samsung_pwm_lock, flags); 160 161 tcon = __raw_readl(pwm.base + REG_TCON); 162 163 tcon &= ~TCON_MANUALUPDATE(channel); 164 tcon |= TCON_START(channel); 165 166 if (periodic) 167 tcon |= TCON_AUTORELOAD(channel); 168 else 169 tcon &= ~TCON_AUTORELOAD(channel); 170 171 __raw_writel(tcon, pwm.base + REG_TCON); 172 173 spin_unlock_irqrestore(&samsung_pwm_lock, flags); 174 } 175 176 static int samsung_set_next_event(unsigned long cycles, 177 struct clock_event_device *evt) 178 { 179 /* 180 * This check is needed to account for internal rounding 181 * errors inside clockevents core, which might result in 182 * passing cycles = 0, which in turn would not generate any 183 * timer interrupt and hang the system. 184 * 185 * Another solution would be to set up the clockevent device 186 * with min_delta = 2, but this would unnecessarily increase 187 * the minimum sleep period. 188 */ 189 if (!cycles) 190 cycles = 1; 191 192 samsung_time_setup(pwm.event_id, cycles); 193 samsung_time_start(pwm.event_id, false); 194 195 return 0; 196 } 197 198 static void samsung_timer_resume(void) 199 { 200 /* event timer restart */ 201 samsung_time_setup(pwm.event_id, pwm.clock_count_per_tick - 1); 202 samsung_time_start(pwm.event_id, true); 203 204 /* source timer restart */ 205 samsung_time_setup(pwm.source_id, pwm.tcnt_max); 206 samsung_time_start(pwm.source_id, true); 207 } 208 209 static void samsung_set_mode(enum clock_event_mode mode, 210 struct clock_event_device *evt) 211 { 212 samsung_time_stop(pwm.event_id); 213 214 switch (mode) { 215 case CLOCK_EVT_MODE_PERIODIC: 216 samsung_time_setup(pwm.event_id, pwm.clock_count_per_tick - 1); 217 samsung_time_start(pwm.event_id, true); 218 break; 219 220 case CLOCK_EVT_MODE_ONESHOT: 221 break; 222 223 case CLOCK_EVT_MODE_UNUSED: 224 case CLOCK_EVT_MODE_SHUTDOWN: 225 break; 226 227 case CLOCK_EVT_MODE_RESUME: 228 samsung_timer_resume(); 229 break; 230 } 231 } 232 233 static struct clock_event_device time_event_device = { 234 .name = "samsung_event_timer", 235 .features = CLOCK_EVT_FEAT_PERIODIC | CLOCK_EVT_FEAT_ONESHOT, 236 .rating = 200, 237 .set_next_event = samsung_set_next_event, 238 .set_mode = samsung_set_mode, 239 }; 240 241 static irqreturn_t samsung_clock_event_isr(int irq, void *dev_id) 242 { 243 struct clock_event_device *evt = dev_id; 244 245 if (pwm.variant.has_tint_cstat) { 246 u32 mask = (1 << pwm.event_id); 247 writel(mask | (mask << 5), pwm.base + REG_TINT_CSTAT); 248 } 249 250 evt->event_handler(evt); 251 252 return IRQ_HANDLED; 253 } 254 255 static struct irqaction samsung_clock_event_irq = { 256 .name = "samsung_time_irq", 257 .flags = IRQF_DISABLED | IRQF_TIMER | IRQF_IRQPOLL, 258 .handler = samsung_clock_event_isr, 259 .dev_id = &time_event_device, 260 }; 261 262 static void __init samsung_clockevent_init(void) 263 { 264 unsigned long pclk; 265 unsigned long clock_rate; 266 unsigned int irq_number; 267 268 pclk = clk_get_rate(pwm.timerclk); 269 270 samsung_timer_set_prescale(pwm.event_id, pwm.tscaler_div); 271 samsung_timer_set_divisor(pwm.event_id, pwm.tdiv); 272 273 clock_rate = pclk / (pwm.tscaler_div * pwm.tdiv); 274 pwm.clock_count_per_tick = clock_rate / HZ; 275 276 time_event_device.cpumask = cpumask_of(0); 277 clockevents_config_and_register(&time_event_device, 278 clock_rate, 1, pwm.tcnt_max); 279 280 irq_number = pwm.irq[pwm.event_id]; 281 setup_irq(irq_number, &samsung_clock_event_irq); 282 283 if (pwm.variant.has_tint_cstat) { 284 u32 mask = (1 << pwm.event_id); 285 writel(mask | (mask << 5), pwm.base + REG_TINT_CSTAT); 286 } 287 } 288 289 static void __iomem *samsung_timer_reg(void) 290 { 291 switch (pwm.source_id) { 292 case 0: 293 case 1: 294 case 2: 295 case 3: 296 return pwm.base + pwm.source_id * 0x0c + 0x14; 297 298 case 4: 299 return pwm.base + 0x40; 300 301 default: 302 BUG(); 303 } 304 } 305 306 /* 307 * Override the global weak sched_clock symbol with this 308 * local implementation which uses the clocksource to get some 309 * better resolution when scheduling the kernel. We accept that 310 * this wraps around for now, since it is just a relative time 311 * stamp. (Inspired by U300 implementation.) 312 */ 313 static u32 notrace samsung_read_sched_clock(void) 314 { 315 void __iomem *reg = samsung_timer_reg(); 316 317 if (!reg) 318 return 0; 319 320 return ~__raw_readl(reg); 321 } 322 323 static void __init samsung_clocksource_init(void) 324 { 325 void __iomem *reg = samsung_timer_reg(); 326 unsigned long pclk; 327 unsigned long clock_rate; 328 int ret; 329 330 pclk = clk_get_rate(pwm.timerclk); 331 332 samsung_timer_set_prescale(pwm.source_id, pwm.tscaler_div); 333 samsung_timer_set_divisor(pwm.source_id, pwm.tdiv); 334 335 clock_rate = pclk / (pwm.tscaler_div * pwm.tdiv); 336 337 samsung_time_setup(pwm.source_id, pwm.tcnt_max); 338 samsung_time_start(pwm.source_id, true); 339 340 setup_sched_clock(samsung_read_sched_clock, 341 pwm.variant.bits, clock_rate); 342 343 ret = clocksource_mmio_init(reg, "samsung_clocksource_timer", 344 clock_rate, 250, pwm.variant.bits, 345 clocksource_mmio_readl_down); 346 if (ret) 347 panic("samsung_clocksource_timer: can't register clocksource\n"); 348 } 349 350 static void __init samsung_timer_resources(void) 351 { 352 pwm.timerclk = clk_get(NULL, "timers"); 353 if (IS_ERR(pwm.timerclk)) 354 panic("failed to get timers clock for timer"); 355 356 clk_prepare_enable(pwm.timerclk); 357 358 pwm.tcnt_max = (1UL << pwm.variant.bits) - 1; 359 if (pwm.variant.bits == 16) { 360 pwm.tscaler_div = 25; 361 pwm.tdiv = 2; 362 } else { 363 pwm.tscaler_div = 2; 364 pwm.tdiv = 1; 365 } 366 } 367 368 /* 369 * PWM master driver 370 */ 371 static void __init _samsung_pwm_clocksource_init(void) 372 { 373 u8 mask; 374 int channel; 375 376 mask = ~pwm.variant.output_mask & ((1 << SAMSUNG_PWM_NUM) - 1); 377 channel = fls(mask) - 1; 378 if (channel < 0) 379 panic("failed to find PWM channel for clocksource"); 380 pwm.source_id = channel; 381 382 mask &= ~(1 << channel); 383 channel = fls(mask) - 1; 384 if (channel < 0) 385 panic("failed to find PWM channel for clock event"); 386 pwm.event_id = channel; 387 388 samsung_timer_resources(); 389 samsung_clockevent_init(); 390 samsung_clocksource_init(); 391 } 392 393 void __init samsung_pwm_clocksource_init(void __iomem *base, 394 unsigned int *irqs, struct samsung_pwm_variant *variant) 395 { 396 pwm.base = base; 397 memcpy(&pwm.variant, variant, sizeof(pwm.variant)); 398 memcpy(pwm.irq, irqs, SAMSUNG_PWM_NUM * sizeof(*irqs)); 399 400 _samsung_pwm_clocksource_init(); 401 } 402 403 #ifdef CONFIG_CLKSRC_OF 404 static void __init samsung_pwm_alloc(struct device_node *np, 405 const struct samsung_pwm_variant *variant) 406 { 407 struct resource res; 408 struct property *prop; 409 const __be32 *cur; 410 u32 val; 411 int i; 412 413 memcpy(&pwm.variant, variant, sizeof(pwm.variant)); 414 for (i = 0; i < SAMSUNG_PWM_NUM; ++i) 415 pwm.irq[i] = irq_of_parse_and_map(np, i); 416 417 of_property_for_each_u32(np, "samsung,pwm-outputs", prop, cur, val) { 418 if (val >= SAMSUNG_PWM_NUM) { 419 pr_warning("%s: invalid channel index in samsung,pwm-outputs property\n", 420 __func__); 421 continue; 422 } 423 pwm.variant.output_mask |= 1 << val; 424 } 425 426 of_address_to_resource(np, 0, &res); 427 if (!request_mem_region(res.start, 428 resource_size(&res), "samsung-pwm")) { 429 pr_err("%s: failed to request IO mem region\n", __func__); 430 return; 431 } 432 433 pwm.base = ioremap(res.start, resource_size(&res)); 434 if (!pwm.base) { 435 pr_err("%s: failed to map PWM registers\n", __func__); 436 release_mem_region(res.start, resource_size(&res)); 437 return; 438 } 439 440 _samsung_pwm_clocksource_init(); 441 } 442 443 static const struct samsung_pwm_variant s3c24xx_variant = { 444 .bits = 16, 445 .div_base = 1, 446 .has_tint_cstat = false, 447 .tclk_mask = (1 << 4), 448 }; 449 450 static void __init s3c2410_pwm_clocksource_init(struct device_node *np) 451 { 452 samsung_pwm_alloc(np, &s3c24xx_variant); 453 } 454 CLOCKSOURCE_OF_DECLARE(s3c2410_pwm, "samsung,s3c2410-pwm", s3c2410_pwm_clocksource_init); 455 456 static const struct samsung_pwm_variant s3c64xx_variant = { 457 .bits = 32, 458 .div_base = 0, 459 .has_tint_cstat = true, 460 .tclk_mask = (1 << 7) | (1 << 6) | (1 << 5), 461 }; 462 463 static void __init s3c64xx_pwm_clocksource_init(struct device_node *np) 464 { 465 samsung_pwm_alloc(np, &s3c64xx_variant); 466 } 467 CLOCKSOURCE_OF_DECLARE(s3c6400_pwm, "samsung,s3c6400-pwm", s3c64xx_pwm_clocksource_init); 468 469 static const struct samsung_pwm_variant s5p64x0_variant = { 470 .bits = 32, 471 .div_base = 0, 472 .has_tint_cstat = true, 473 .tclk_mask = 0, 474 }; 475 476 static void __init s5p64x0_pwm_clocksource_init(struct device_node *np) 477 { 478 samsung_pwm_alloc(np, &s5p64x0_variant); 479 } 480 CLOCKSOURCE_OF_DECLARE(s5p6440_pwm, "samsung,s5p6440-pwm", s5p64x0_pwm_clocksource_init); 481 482 static const struct samsung_pwm_variant s5p_variant = { 483 .bits = 32, 484 .div_base = 0, 485 .has_tint_cstat = true, 486 .tclk_mask = (1 << 5), 487 }; 488 489 static void __init s5p_pwm_clocksource_init(struct device_node *np) 490 { 491 samsung_pwm_alloc(np, &s5p_variant); 492 } 493 CLOCKSOURCE_OF_DECLARE(s5pc100_pwm, "samsung,s5pc100-pwm", s5p_pwm_clocksource_init); 494 #endif 495