mirror of
https://github.com/AetherDroid/android_kernel_samsung_on5xelte.git
synced 2025-09-08 17:18:05 -04:00
190 lines
5.5 KiB
C
190 lines
5.5 KiB
C
/*
|
|
* Copyright (c) 2013 Samsung Electronics Co., Ltd.
|
|
* Author: Hyunki Koo <hyunki00.koo@samsung.com>
|
|
*
|
|
* This program is free software; you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License version 2 as
|
|
* published by the Free Software Foundation.
|
|
*
|
|
* Common Clock Framework support for pwm timer Clock Controller.
|
|
*/
|
|
|
|
#include <linux/clkdev.h>
|
|
#include <linux/io.h>
|
|
#include <linux/clk-provider.h>
|
|
#include <linux/of_address.h>
|
|
|
|
#include "clk.h"
|
|
|
|
/* 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 (tin)
|
|
* tclk 0 ------------------------------/
|
|
*
|
|
* prescaled 0 ---- [ div 2,4,8,16 ] ---\
|
|
* [mux] -> timer 1 (tin)
|
|
* tclk 0 ------------------------------/
|
|
*
|
|
*
|
|
* prescaled 1 ---- [ div 2,4,8,16 ] ---\
|
|
* [mux] -> timer 2 (tin)
|
|
* tclk 1 ------------------------------/
|
|
*
|
|
* prescaled 1 ---- [ div 2,4,8,16 ] ---\
|
|
* [mux] -> timer 3 (tin)
|
|
* tclk 1 ------------------------------/
|
|
*
|
|
* prescaled 1 ---- [ div 2,4,8, 16 ] --\
|
|
* [mux] -> timer 4 (tin)
|
|
* 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 -------------------------/
|
|
*
|
|
* tclk is deprecated in exynos
|
|
*
|
|
*/
|
|
|
|
static DEFINE_SPINLOCK(lock);
|
|
static struct clk **clk_table;
|
|
static struct clk_onecell_data clk_data;
|
|
|
|
#define REG_TCFG0 0x00
|
|
#define REG_TCFG1 0x04
|
|
#define REG_TCON 0x08
|
|
#define REG_TINT_CSTAT 0x44
|
|
#define MASK_TCFG0_PRESCALE0 0x00FF
|
|
#define MASK_TCFG0_PRESCALE1 0xFF00
|
|
|
|
enum exynos_pwm_clks {
|
|
pwm_clock = 0,
|
|
pwm_scaler0,
|
|
pwm_scaler1,
|
|
pwm_tclk0,
|
|
pwm_tclk1,
|
|
pwm_tdiv0 = 5,
|
|
pwm_tdiv1,
|
|
pwm_tdiv2,
|
|
pwm_tdiv3,
|
|
pwm_tdiv4,
|
|
pwm_tin0 = 10,
|
|
pwm_tin1,
|
|
pwm_tin2,
|
|
pwm_tin3,
|
|
pwm_tin4,
|
|
exynos_pwm_max_clks,
|
|
};
|
|
|
|
static const char *pwm_tin0_p[] = { "pwm-tdiv0", "pwm-tclk" };
|
|
static const char *pwm_tin1_p[] = { "pwm-tdiv1", "pwm-tclk" };
|
|
static const char *pwm_tin2_p[] = { "pwm-tdiv2", "pwm-tclk" };
|
|
static const char *pwm_tin3_p[] = { "pwm-tdiv3", "pwm-tclk" };
|
|
static const char *pwm_tin4_p[] = { "pwm-tdiv4", "pwm-tclk" };
|
|
|
|
static const struct clk_div_table pwm_div_table[5] = {
|
|
/* { val, div } */
|
|
{ 0, 1 },
|
|
{ 1, 2 },
|
|
{ 2, 4 },
|
|
{ 3, 8 },
|
|
{ 4, 16 },
|
|
};
|
|
|
|
/* register exynos_pwm clocks */
|
|
void __init exynos_pwm_clk_init(struct device_node *np)
|
|
{
|
|
static void __iomem *reg_base;
|
|
unsigned int reg_tcfg0;
|
|
|
|
reg_base = of_iomap(np, 0);
|
|
|
|
if (!reg_base) {
|
|
pr_err("%s: failed to map pwm registers\n", __func__);
|
|
return;
|
|
}
|
|
|
|
clk_table = kzalloc(sizeof(struct clk *) * exynos_pwm_max_clks,
|
|
GFP_KERNEL);
|
|
if (!clk_table) {
|
|
pr_err("%s: could not allocate clk lookup table\n", __func__);
|
|
return;
|
|
}
|
|
|
|
clk_data.clks = clk_table;
|
|
clk_data.clk_num = exynos_pwm_max_clks;
|
|
of_clk_add_provider(np, of_clk_src_onecell_get, &clk_data);
|
|
|
|
reg_tcfg0 = __raw_readl(reg_base + REG_TCFG0);
|
|
reg_tcfg0 &= ~(MASK_TCFG0_PRESCALE0 | MASK_TCFG0_PRESCALE1);
|
|
__raw_writel(reg_tcfg0, reg_base + REG_TCFG0);
|
|
__raw_writel(0, reg_base + REG_TCFG1);
|
|
|
|
clk_table[pwm_scaler0] = clk_register_divider(NULL, "pwm-scaler0",
|
|
"pwm-clock", 0, reg_base + REG_TCFG0, 0, 8,
|
|
CLK_DIVIDER_ALLOW_ZERO, &lock);
|
|
clk_table[pwm_scaler1] = clk_register_divider(NULL, "pwm-scaler1",
|
|
"pwm-clock", 0, reg_base + REG_TCFG0, 8, 8,
|
|
CLK_DIVIDER_ALLOW_ZERO, &lock);
|
|
|
|
clk_table[pwm_tdiv0] = clk_register_divider_table(NULL, "pwm-tdiv0",
|
|
"pwm-scaler0", 0, reg_base + REG_TCFG1, 0, 4,
|
|
CLK_DIVIDER_ALLOW_ZERO, pwm_div_table, &lock);
|
|
|
|
clk_table[pwm_tdiv1] = clk_register_divider_table(NULL, "pwm-tdiv1",
|
|
"pwm-scaler0", 0, reg_base + REG_TCFG1, 4, 4,
|
|
CLK_DIVIDER_ALLOW_ZERO, pwm_div_table, &lock);
|
|
|
|
clk_table[pwm_tdiv2] = clk_register_divider_table(NULL, "pwm-tdiv2",
|
|
"pwm-scaler1", 0, reg_base + REG_TCFG1, 8, 4,
|
|
CLK_DIVIDER_ALLOW_ZERO, pwm_div_table, &lock);
|
|
|
|
clk_table[pwm_tdiv3] = clk_register_divider_table(NULL, "pwm-tdiv3",
|
|
"pwm-scaler1", 0, reg_base + REG_TCFG1, 12, 4,
|
|
CLK_DIVIDER_ALLOW_ZERO, pwm_div_table, &lock);
|
|
|
|
clk_table[pwm_tdiv4] = clk_register_divider_table(NULL, "pwm-tdiv4",
|
|
"pwm-scaler1", 0, reg_base + REG_TCFG1, 16, 4,
|
|
CLK_DIVIDER_ALLOW_ZERO, pwm_div_table, &lock);
|
|
|
|
clk_table[pwm_tin0] = clk_register_mux(NULL, "pwm-tin0",
|
|
pwm_tin0_p, ARRAY_SIZE(pwm_tin0_p), 0,
|
|
reg_base + REG_TCFG1, 3, 0, 0, &lock);
|
|
|
|
clk_table[pwm_tin1] = clk_register_mux(NULL, "pwm-tin1",
|
|
pwm_tin1_p, ARRAY_SIZE(pwm_tin1_p), 0,
|
|
reg_base + REG_TCFG1, 7, 0, 0, &lock);
|
|
|
|
clk_table[pwm_tin2] = clk_register_mux(NULL, "pwm-tin2",
|
|
pwm_tin2_p, ARRAY_SIZE(pwm_tin2_p), 0,
|
|
reg_base + REG_TCFG1, 11, 0, 0, &lock);
|
|
|
|
clk_table[pwm_tin3] = clk_register_mux(NULL, "pwm-tin3",
|
|
pwm_tin3_p, ARRAY_SIZE(pwm_tin3_p), 0,
|
|
reg_base + REG_TCFG1, 15, 0, 0, &lock);
|
|
|
|
clk_table[pwm_tin4] = clk_register_mux(NULL, "pwm-tin4",
|
|
pwm_tin4_p, ARRAY_SIZE(pwm_tin4_p), 0,
|
|
reg_base + REG_TCFG1, 19, 0, 0, &lock);
|
|
|
|
pr_info("Exynos: pwm: clock setup completed\n");
|
|
}
|
|
CLK_OF_DECLARE(exynos_pwm_clk, "samsung,exynos-pwm-clock",
|
|
exynos_pwm_clk_init);
|