aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAlexandru M Stan <amstan@chromium.org>2014-11-26 20:30:27 -0500
committerHeiko Stuebner <heiko@sntech.de>2014-11-27 18:44:24 -0500
commit89bf26cbc1a09476c4c4740d16a0ffdfa2192b9c (patch)
tree954a8753ce27fbd5ee9b0cc9fe4adeaa4d9d15bc
parentc1c9f2cced4e3ba22b3fc25603dd3bd2ec7dc173 (diff)
clk: rockchip: Add support for the mmc clock phases using the framework
This patch adds the 2 physical clocks for the mmc (drive and sample). They're mostly there for the phase properties, but they also show the true clock (by dividing by RK3288_MMC_CLKGEN_DIV). The drive and sample phases are generated by dividing an upstream parent clock by 2, this allows us to adjust the phase by 90 deg. There's also an option to have up to 255 delay elements (40-80 picoseconds long). This driver uses those elements (under the assumption that they're 60 ps long) to generate approximate 22.5 degrees options. 67.5 (22.5*3) might be as high as 90 deg if the delay elements are as big as 80 ps, so a finer division (smaller than 22.5) was not picked because the phase might not be monotonic anymore. Suggested-by: Heiko Stuebner <heiko@sntech.de> Signed-off-by: Alexandru M Stan <amstan@chromium.org> Signed-off-by: Heiko Stuebner <heiko@sntech.de>
-rw-r--r--drivers/clk/rockchip/Makefile1
-rw-r--r--drivers/clk/rockchip/clk-mmc-phase.c154
-rw-r--r--drivers/clk/rockchip/clk-rk3288.c12
-rw-r--r--drivers/clk/rockchip/clk.c8
-rw-r--r--drivers/clk/rockchip/clk.h23
5 files changed, 198 insertions, 0 deletions
diff --git a/drivers/clk/rockchip/Makefile b/drivers/clk/rockchip/Makefile
index bd8514d63634..2714097f90db 100644
--- a/drivers/clk/rockchip/Makefile
+++ b/drivers/clk/rockchip/Makefile
@@ -6,6 +6,7 @@ obj-y += clk-rockchip.o
6obj-y += clk.o 6obj-y += clk.o
7obj-y += clk-pll.o 7obj-y += clk-pll.o
8obj-y += clk-cpu.o 8obj-y += clk-cpu.o
9obj-y += clk-mmc-phase.o
9obj-$(CONFIG_RESET_CONTROLLER) += softrst.o 10obj-$(CONFIG_RESET_CONTROLLER) += softrst.o
10 11
11obj-y += clk-rk3188.o 12obj-y += clk-rk3188.o
diff --git a/drivers/clk/rockchip/clk-mmc-phase.c b/drivers/clk/rockchip/clk-mmc-phase.c
new file mode 100644
index 000000000000..c842e3b60f21
--- /dev/null
+++ b/drivers/clk/rockchip/clk-mmc-phase.c
@@ -0,0 +1,154 @@
1/*
2 * Copyright 2014 Google, Inc
3 * Author: Alexandru M Stan <amstan@chromium.org>
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 */
15
16#include <linux/slab.h>
17#include <linux/clk-provider.h>
18#include "clk.h"
19
20struct rockchip_mmc_clock {
21 struct clk_hw hw;
22 void __iomem *reg;
23 int id;
24 int shift;
25};
26
27#define to_mmc_clock(_hw) container_of(_hw, struct rockchip_mmc_clock, hw)
28
29#define RK3288_MMC_CLKGEN_DIV 2
30
31static unsigned long rockchip_mmc_recalc(struct clk_hw *hw,
32 unsigned long parent_rate)
33{
34 return parent_rate / RK3288_MMC_CLKGEN_DIV;
35}
36
37#define ROCKCHIP_MMC_DELAY_SEL BIT(10)
38#define ROCKCHIP_MMC_DEGREE_MASK 0x3
39#define ROCKCHIP_MMC_DELAYNUM_OFFSET 2
40#define ROCKCHIP_MMC_DELAYNUM_MASK (0xff << ROCKCHIP_MMC_DELAYNUM_OFFSET)
41
42#define PSECS_PER_SEC 1000000000000LL
43
44/*
45 * Each fine delay is between 40ps-80ps. Assume each fine delay is 60ps to
46 * simplify calculations. So 45degs could be anywhere between 33deg and 66deg.
47 */
48#define ROCKCHIP_MMC_DELAY_ELEMENT_PSEC 60
49
50static int rockchip_mmc_get_phase(struct clk_hw *hw)
51{
52 struct rockchip_mmc_clock *mmc_clock = to_mmc_clock(hw);
53 unsigned long rate = clk_get_rate(hw->clk);
54 u32 raw_value;
55 u16 degrees;
56 u32 delay_num = 0;
57
58 raw_value = readl(mmc_clock->reg) >> (mmc_clock->shift);
59
60 degrees = (raw_value & ROCKCHIP_MMC_DEGREE_MASK) * 90;
61
62 if (raw_value & ROCKCHIP_MMC_DELAY_SEL) {
63 /* degrees/delaynum * 10000 */
64 unsigned long factor = (ROCKCHIP_MMC_DELAY_ELEMENT_PSEC / 10) *
65 36 * (rate / 1000000);
66
67 delay_num = (raw_value & ROCKCHIP_MMC_DELAYNUM_MASK);
68 delay_num >>= ROCKCHIP_MMC_DELAYNUM_OFFSET;
69 degrees += delay_num * factor / 10000;
70 }
71
72 return degrees % 360;
73}
74
75static int rockchip_mmc_set_phase(struct clk_hw *hw, int degrees)
76{
77 struct rockchip_mmc_clock *mmc_clock = to_mmc_clock(hw);
78 unsigned long rate = clk_get_rate(hw->clk);
79 u8 nineties, remainder;
80 u8 delay_num;
81 u32 raw_value;
82 u64 delay;
83
84 /* allow 22 to be 22.5 */
85 degrees++;
86 /* floor to 22.5 increment */
87 degrees -= ((degrees) * 10 % 225) / 10;
88
89 nineties = degrees / 90;
90 /* 22.5 multiples */
91 remainder = (degrees % 90) / 22;
92
93 delay = PSECS_PER_SEC;
94 do_div(delay, rate);
95 /* / 360 / 22.5 */
96 do_div(delay, 16);
97 do_div(delay, ROCKCHIP_MMC_DELAY_ELEMENT_PSEC);
98
99 delay *= remainder;
100 delay_num = (u8) min(delay, 255ULL);
101
102 raw_value = delay_num ? ROCKCHIP_MMC_DELAY_SEL : 0;
103 raw_value |= delay_num << ROCKCHIP_MMC_DELAYNUM_OFFSET;
104 raw_value |= nineties;
105 writel(HIWORD_UPDATE(raw_value, 0x07ff, mmc_clock->shift), mmc_clock->reg);
106
107 pr_debug("%s->set_phase(%d) delay_nums=%u reg[0x%p]=0x%03x actual_degrees=%d\n",
108 __clk_get_name(hw->clk), degrees, delay_num,
109 mmc_clock->reg, raw_value>>(mmc_clock->shift),
110 rockchip_mmc_get_phase(hw)
111 );
112
113 return 0;
114}
115
116static const struct clk_ops rockchip_mmc_clk_ops = {
117 .recalc_rate = rockchip_mmc_recalc,
118 .get_phase = rockchip_mmc_get_phase,
119 .set_phase = rockchip_mmc_set_phase,
120};
121
122struct clk *rockchip_clk_register_mmc(const char *name,
123 const char **parent_names, u8 num_parents,
124 void __iomem *reg, int shift)
125{
126 struct clk_init_data init;
127 struct rockchip_mmc_clock *mmc_clock;
128 struct clk *clk;
129
130 mmc_clock = kmalloc(sizeof(*mmc_clock), GFP_KERNEL);
131 if (!mmc_clock)
132 return NULL;
133
134 init.num_parents = num_parents;
135 init.parent_names = parent_names;
136 init.ops = &rockchip_mmc_clk_ops;
137
138 mmc_clock->hw.init = &init;
139 mmc_clock->reg = reg;
140 mmc_clock->shift = shift;
141
142 if (name)
143 init.name = name;
144
145 clk = clk_register(NULL, &mmc_clock->hw);
146 if (IS_ERR(clk))
147 goto err_free;
148
149 return clk;
150
151err_free:
152 kfree(mmc_clock);
153 return NULL;
154}
diff --git a/drivers/clk/rockchip/clk-rk3288.c b/drivers/clk/rockchip/clk-rk3288.c
index a43045b05d98..ac6be7c0132d 100644
--- a/drivers/clk/rockchip/clk-rk3288.c
+++ b/drivers/clk/rockchip/clk-rk3288.c
@@ -486,6 +486,18 @@ static struct rockchip_clk_branch rk3288_clk_branches[] __initdata = {
486 RK3288_CLKSEL_CON(12), 14, 2, MFLAGS, 8, 6, DFLAGS, 486 RK3288_CLKSEL_CON(12), 14, 2, MFLAGS, 8, 6, DFLAGS,
487 RK3288_CLKGATE_CON(13), 3, GFLAGS), 487 RK3288_CLKGATE_CON(13), 3, GFLAGS),
488 488
489 MMC(SCLK_SDMMC_DRV, "sdmmc_drv", "sclk_sdmmc", RK3288_SDMMC_CON0, 1),
490 MMC(SCLK_SDMMC_SAMPLE, "sdmmc_sample", "sclk_sdmmc", RK3288_SDMMC_CON1, 0),
491
492 MMC(SCLK_SDIO0_DRV, "sdio0_drv", "sclk_sdio0", RK3288_SDIO0_CON0, 1),
493 MMC(SCLK_SDIO0_SAMPLE, "sdio0_sample", "sclk_sdio0", RK3288_SDIO0_CON1, 0),
494
495 MMC(SCLK_SDIO1_DRV, "sdio1_drv", "sclk_sdio1", RK3288_SDIO1_CON0, 1),
496 MMC(SCLK_SDIO1_SAMPLE, "sdio1_sample", "sclk_sdio1", RK3288_SDIO1_CON1, 0),
497
498 MMC(SCLK_EMMC_DRV, "emmc_drv", "sclk_emmc", RK3288_EMMC_CON0, 1),
499 MMC(SCLK_EMMC_SAMPLE, "emmc_sample", "sclk_emmc", RK3288_EMMC_CON1, 0),
500
489 COMPOSITE(0, "sclk_tspout", mux_tspout_p, 0, 501 COMPOSITE(0, "sclk_tspout", mux_tspout_p, 0,
490 RK3288_CLKSEL_CON(35), 14, 2, MFLAGS, 8, 5, DFLAGS, 502 RK3288_CLKSEL_CON(35), 14, 2, MFLAGS, 8, 5, DFLAGS,
491 RK3288_CLKGATE_CON(4), 11, GFLAGS), 503 RK3288_CLKGATE_CON(4), 11, GFLAGS),
diff --git a/drivers/clk/rockchip/clk.c b/drivers/clk/rockchip/clk.c
index 3b8f26e2cd1a..f6150da97e8b 100644
--- a/drivers/clk/rockchip/clk.c
+++ b/drivers/clk/rockchip/clk.c
@@ -271,6 +271,14 @@ void __init rockchip_clk_register_branches(
271 list->gate_offset, list->gate_shift, 271 list->gate_offset, list->gate_shift,
272 list->gate_flags, flags, &clk_lock); 272 list->gate_flags, flags, &clk_lock);
273 break; 273 break;
274 case branch_mmc:
275 clk = rockchip_clk_register_mmc(
276 list->name,
277 list->parent_names, list->num_parents,
278 reg_base + list->muxdiv_offset,
279 list->div_shift
280 );
281 break;
274 } 282 }
275 283
276 /* none of the cases above matched */ 284 /* none of the cases above matched */
diff --git a/drivers/clk/rockchip/clk.h b/drivers/clk/rockchip/clk.h
index dc9468132ef1..58d2e3bdf22f 100644
--- a/drivers/clk/rockchip/clk.h
+++ b/drivers/clk/rockchip/clk.h
@@ -48,6 +48,14 @@
48#define RK3288_GLB_SRST_SND 0x1b4 48#define RK3288_GLB_SRST_SND 0x1b4
49#define RK3288_SOFTRST_CON(x) (x * 0x4 + 0x1b8) 49#define RK3288_SOFTRST_CON(x) (x * 0x4 + 0x1b8)
50#define RK3288_MISC_CON 0x1e8 50#define RK3288_MISC_CON 0x1e8
51#define RK3288_SDMMC_CON0 0x200
52#define RK3288_SDMMC_CON1 0x204
53#define RK3288_SDIO0_CON0 0x208
54#define RK3288_SDIO0_CON1 0x20c
55#define RK3288_SDIO1_CON0 0x210
56#define RK3288_SDIO1_CON1 0x214
57#define RK3288_EMMC_CON0 0x218
58#define RK3288_EMMC_CON1 0x21c
51 59
52enum rockchip_pll_type { 60enum rockchip_pll_type {
53 pll_rk3066, 61 pll_rk3066,
@@ -170,6 +178,10 @@ struct clk *rockchip_clk_register_cpuclk(const char *name,
170 const struct rockchip_cpuclk_rate_table *rates, 178 const struct rockchip_cpuclk_rate_table *rates,
171 int nrates, void __iomem *reg_base, spinlock_t *lock); 179 int nrates, void __iomem *reg_base, spinlock_t *lock);
172 180
181struct clk *rockchip_clk_register_mmc(const char *name,
182 const char **parent_names, u8 num_parents,
183 void __iomem *reg, int shift);
184
173#define PNAME(x) static const char *x[] __initconst 185#define PNAME(x) static const char *x[] __initconst
174 186
175enum rockchip_clk_branch_type { 187enum rockchip_clk_branch_type {
@@ -178,6 +190,7 @@ enum rockchip_clk_branch_type {
178 branch_divider, 190 branch_divider,
179 branch_fraction_divider, 191 branch_fraction_divider,
180 branch_gate, 192 branch_gate,
193 branch_mmc,
181}; 194};
182 195
183struct rockchip_clk_branch { 196struct rockchip_clk_branch {
@@ -370,6 +383,16 @@ struct rockchip_clk_branch {
370 .gate_flags = gf, \ 383 .gate_flags = gf, \
371 } 384 }
372 385
386#define MMC(_id, cname, pname, offset, shift) \
387 { \
388 .id = _id, \
389 .branch_type = branch_mmc, \
390 .name = cname, \
391 .parent_names = (const char *[]){ pname }, \
392 .num_parents = 1, \
393 .muxdiv_offset = offset, \
394 .div_shift = shift, \
395 }
373 396
374void rockchip_clk_init(struct device_node *np, void __iomem *base, 397void rockchip_clk_init(struct device_node *np, void __iomem *base,
375 unsigned long nr_clks); 398 unsigned long nr_clks);