/* linux/arch/arm/plat-s3c24xx/pwm-clock.c * * Copyright (c) 2007 Simtec Electronics * Copyright (c) 2007, 2008 Ben Dooks * Ben Dooks * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include /* Each of the timers 0 through 5 go through the following * clock tree, with the inputs depending on the timers. * * pclk ---- [ prescaler 0 ] -+---> timer 0 * +---> timer 1 * * pclk ---- [ prescaler 1 ] -+---> timer 2 * +---> timer 3 * \---> timer 4 * * Which are fed into the timers as so: * * prescaled 0 ---- [ div 2,4,8,16 ] ---\ * [mux] -> timer 0 * tclk 0 ------------------------------/ * * prescaled 0 ---- [ div 2,4,8,16 ] ---\ * [mux] -> timer 1 * tclk 0 ------------------------------/ * * * prescaled 1 ---- [ div 2,4,8,16 ] ---\ * [mux] -> timer 2 * tclk 1 ------------------------------/ * * prescaled 1 ---- [ div 2,4,8,16 ] ---\ * [mux] -> timer 3 * tclk 1 ------------------------------/ * * prescaled 1 ---- [ div 2,4,8, 16 ] --\ * [mux] -> timer 4 * tclk 1 ------------------------------/ * * Since the mux and the divider are tied together in the * same register space, it is impossible to set the parent * and the rate at the same time. To avoid this, we add an * intermediate 'prescaled-and-divided' clock to select * as the parent for the timer input clock called tdiv. * * prescaled clk --> pwm-tdiv ---\ * [ mux ] --> timer X * tclk -------------------------/ */ static struct clk clk_timer_scaler[]; static unsigned long clk_pwm_scaler_get_rate(struct clk *clk) { unsigned long tcfg0 = __raw_readl(S3C2410_TCFG0); if (clk == &clk_timer_scaler[1]) { tcfg0 &= S3C2410_TCFG_PRESCALER1_MASK; tcfg0 >>= S3C2410_TCFG_PRESCALER1_SHIFT; } else { tcfg0 &= S3C2410_TCFG_PRESCALER0_MASK; } return clk_get_rate(clk->parent) / (tcfg0 + 1); } static unsigned long clk_pwm_scaler_round_rate(struct clk *clk, unsigned long rate) { unsigned long parent_rate = clk_get_rate(clk->parent); unsigned long divisor = parent_rate / rate; if (divisor > 256) divisor = 256; else if (divisor < 2) divisor = 2; return parent_rate / divisor; } static int clk_pwm_scaler_set_rate(struct clk *clk, unsigned long rate) { unsigned long round = clk_pwm_scaler_round_rate(clk, rate); unsigned long tcfg0; unsigned long divisor; unsigned long flags; divisor = clk_get_rate(clk->parent) / round; divisor--; local_irq_save(flags); tcfg0 = __raw_readl(S3C2410_TCFG0); if (clk == &clk_timer_scaler[1]) { tcfg0 &= ~S3C2410_TCFG_PRESCALER1_MASK; tcfg0 |= divisor << S3C2410_TCFG_PRESCALER1_SHIFT; } else { tcfg0 &= ~S3C2410_TCFG_PRESCALER0_MASK; tcfg0 |= divisor; } __raw_writel(tcfg0, S3C2410_TCFG0); local_irq_restore(flags); return 0; } static struct clk_ops clk_pwm_scaler_ops = { .get_rate = clk_pwm_scaler_get_rate, .set_rate = clk_pwm_scaler_set_rate, .round_rate = clk_pwm_scaler_round_rate, }; static struct clk clk_timer_scaler[] = { [0] = { .name = "pwm-scaler0", .id = -1, .ops = &clk_pwm_scaler_ops, }, [1] = { .name = "pwm-scaler1", .id = -1, .ops = &clk_pwm_scaler_ops, }, }; static struct clk clk_timer_tclk[] = { [0] = { .name = "pwm-tclk0", .id = -1, }, [1] = { .name = "pwm-tclk1", .id = -1, }, }; struct pwm_tdiv_clk { struct clk clk; unsigned int divisor; }; static inline struct pwm_tdiv_clk *to_tdiv(struct clk *clk) { return container_of(clk, struct pwm_tdiv_clk, clk); } static unsigned long clk_pwm_tdiv_get_rate(struct clk *clk) { unsigned long tcfg1 = __raw_readl(S3C2410_TCFG1); unsigned int divisor; tcfg1 >>= S3C2410_TCFG1_SHIFT(clk->id); tcfg1 &= S3C2410_TCFG1_MUX_MASK; if (pwm_cfg_src_is_tclk(tcfg1)) divisor = to_tdiv(clk)->divisor; else divisor = tcfg_to_divisor(tcfg1); return clk_get_rate(clk->parent) / divisor; } static unsigned long clk_pwm_tdiv_round_rate(struct clk *clk, unsigned long rate) { unsigned long parent_rate; unsigned long divisor; parent_rate = clk_get_rate(clk->parent); divisor = parent_rate / rate; if (divisor <= 1 && pwm_tdiv_has_div1()) divisor = 1; else if (divisor <= 2) divisor = 2; else if (divisor <= 4) divisor = 4; else if (divisor <= 8) divisor = 8; else divisor = 16; return parent_rate / divisor; } static unsigned long clk_pwm_tdiv_bits(struct pwm_tdiv_clk *divclk)