All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH 0/2] For now all I have is the getter and setter for the phase, nothing that uses it
@ 2014-11-14 22:52 ` Alexandru M Stan
  0 siblings, 0 replies; 10+ messages in thread
From: Alexandru M Stan @ 2014-11-14 22:52 UTC (permalink / raw)
  To: Heiko Stuebner, Doug Anderson, addy ke
  Cc: Sonny Rao, Kever Yang, Alexandru M Stan, mark.rutland,
	devicetree, mturquette, pawel.moll, ijc+devicetree, linux-kernel,
	linux-rockchip, robh+dt, galak, mark.yao, linux-arm-kernel

(that is ready). You can test the getter like this:
localhost ~ # cat /sys/kernel/debug/clk/clk_summary|grep sample -C 1
    sclk_sdio1                            0            0    24000000          0 0
       sdio1_sample                       0            0    12000000          0 0
       sdio1_drv                          0            0    12000000          0 90
--
          sclk_sdmmc                      1            1   297000000          0 0
             sdmmc_sample                 0            0   148500000          0 134
             sdmmc_drv                    0            0   148500000          0 90
--
          sclk_sdio0                      1            1   100000000          0 0
             sdio0_sample                 0            0    50000000          0 0
             sdio0_drv                    0            0    50000000          0 90
          sclk_emmc                       1            1   100000000          0 0
             emmc_sample                  0            0    50000000          0 0
             emmc_drv                     0            0    50000000          0 180

Next thing that will come is some dts changes that will make use of these new
clocks, and eventually mmc code will be changed to tune with these clocks.


Alexandru M Stan (2):
  clk: rockchip: add bindings for the mmc clock phases
  clk: rockchip: Add support for the mmc clock phases using the
    framework

 drivers/clk/rockchip/Makefile          |   1 +
 drivers/clk/rockchip/clk-mmc-phase.c   | 150 +++++++++++++++++++++++++++++++++
 drivers/clk/rockchip/clk-rk3288.c      |  12 +++
 drivers/clk/rockchip/clk.c             |   8 ++
 drivers/clk/rockchip/clk.h             |  23 +++++
 include/dt-bindings/clock/rk3288-cru.h |  10 +++
 6 files changed, 204 insertions(+)
 create mode 100644 drivers/clk/rockchip/clk-mmc-phase.c

-- 
2.1.0.rc2.206.gedb03e5


^ permalink raw reply	[flat|nested] 10+ messages in thread

* [PATCH 0/2] For now all I have is the getter and setter for the phase, nothing that uses it
@ 2014-11-14 22:52 ` Alexandru M Stan
  0 siblings, 0 replies; 10+ messages in thread
From: Alexandru M Stan @ 2014-11-14 22:52 UTC (permalink / raw)
  To: Heiko Stuebner, Doug Anderson, addy ke
  Cc: Sonny Rao, Kever Yang, Alexandru M Stan,
	mark.rutland-5wv7dgnIgG8, devicetree-u79uwXL29TY76Z2rM5mHXA,
	mturquette-QSEj5FYQhm4dnm+yROfE0A, pawel.moll-5wv7dgnIgG8,
	ijc+devicetree-KcIKpvwj1kUDXYZnReoRVg,
	linux-kernel-u79uwXL29TY76Z2rM5mHXA,
	linux-rockchip-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
	robh+dt-DgEjT+Ai2ygdnm+yROfE0A, galak-sgV2jX0FEOL9JmXXK+q4OQ,
	mark.yao-TNX95d0MmH7DzftRWevZcw,
	linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r

(that is ready). You can test the getter like this:
localhost ~ # cat /sys/kernel/debug/clk/clk_summary|grep sample -C 1
    sclk_sdio1                            0            0    24000000          0 0
       sdio1_sample                       0            0    12000000          0 0
       sdio1_drv                          0            0    12000000          0 90
--
          sclk_sdmmc                      1            1   297000000          0 0
             sdmmc_sample                 0            0   148500000          0 134
             sdmmc_drv                    0            0   148500000          0 90
--
          sclk_sdio0                      1            1   100000000          0 0
             sdio0_sample                 0            0    50000000          0 0
             sdio0_drv                    0            0    50000000          0 90
          sclk_emmc                       1            1   100000000          0 0
             emmc_sample                  0            0    50000000          0 0
             emmc_drv                     0            0    50000000          0 180

Next thing that will come is some dts changes that will make use of these new
clocks, and eventually mmc code will be changed to tune with these clocks.


Alexandru M Stan (2):
  clk: rockchip: add bindings for the mmc clock phases
  clk: rockchip: Add support for the mmc clock phases using the
    framework

 drivers/clk/rockchip/Makefile          |   1 +
 drivers/clk/rockchip/clk-mmc-phase.c   | 150 +++++++++++++++++++++++++++++++++
 drivers/clk/rockchip/clk-rk3288.c      |  12 +++
 drivers/clk/rockchip/clk.c             |   8 ++
 drivers/clk/rockchip/clk.h             |  23 +++++
 include/dt-bindings/clock/rk3288-cru.h |  10 +++
 6 files changed, 204 insertions(+)
 create mode 100644 drivers/clk/rockchip/clk-mmc-phase.c

-- 
2.1.0.rc2.206.gedb03e5

--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply	[flat|nested] 10+ messages in thread

* [PATCH 0/2] For now all I have is the getter and setter for the phase, nothing that uses it
@ 2014-11-14 22:52 ` Alexandru M Stan
  0 siblings, 0 replies; 10+ messages in thread
From: Alexandru M Stan @ 2014-11-14 22:52 UTC (permalink / raw)
  To: linux-arm-kernel

(that is ready). You can test the getter like this:
localhost ~ # cat /sys/kernel/debug/clk/clk_summary|grep sample -C 1
    sclk_sdio1                            0            0    24000000          0 0
       sdio1_sample                       0            0    12000000          0 0
       sdio1_drv                          0            0    12000000          0 90
--
          sclk_sdmmc                      1            1   297000000          0 0
             sdmmc_sample                 0            0   148500000          0 134
             sdmmc_drv                    0            0   148500000          0 90
--
          sclk_sdio0                      1            1   100000000          0 0
             sdio0_sample                 0            0    50000000          0 0
             sdio0_drv                    0            0    50000000          0 90
          sclk_emmc                       1            1   100000000          0 0
             emmc_sample                  0            0    50000000          0 0
             emmc_drv                     0            0    50000000          0 180

Next thing that will come is some dts changes that will make use of these new
clocks, and eventually mmc code will be changed to tune with these clocks.


Alexandru M Stan (2):
  clk: rockchip: add bindings for the mmc clock phases
  clk: rockchip: Add support for the mmc clock phases using the
    framework

 drivers/clk/rockchip/Makefile          |   1 +
 drivers/clk/rockchip/clk-mmc-phase.c   | 150 +++++++++++++++++++++++++++++++++
 drivers/clk/rockchip/clk-rk3288.c      |  12 +++
 drivers/clk/rockchip/clk.c             |   8 ++
 drivers/clk/rockchip/clk.h             |  23 +++++
 include/dt-bindings/clock/rk3288-cru.h |  10 +++
 6 files changed, 204 insertions(+)
 create mode 100644 drivers/clk/rockchip/clk-mmc-phase.c

-- 
2.1.0.rc2.206.gedb03e5

^ permalink raw reply	[flat|nested] 10+ messages in thread

* [PATCH 1/2] clk: rockchip: add bindings for the mmc clock phases
  2014-11-14 22:52 ` Alexandru M Stan
  (?)
  (?)
@ 2014-11-14 22:52 ` Alexandru M Stan
  2014-11-18 17:44     ` Doug Anderson
  -1 siblings, 1 reply; 10+ messages in thread
From: Alexandru M Stan @ 2014-11-14 22:52 UTC (permalink / raw)
  To: Heiko Stuebner, Doug Anderson, addy ke
  Cc: Sonny Rao, Kever Yang, Alexandru M Stan, robh+dt, pawel.moll,
	mark.rutland, ijc+devicetree, galak, mturquette, mark.yao,
	devicetree, linux-kernel

This will be used in a later patch for clock phase tuning.

Suggested-by: Heiko Stuebner <heiko@sntech.de>
Signed-off-by: Alexandru M Stan <amstan@chromium.org>
---
 include/dt-bindings/clock/rk3288-cru.h | 10 ++++++++++
 1 file changed, 10 insertions(+)

diff --git a/include/dt-bindings/clock/rk3288-cru.h b/include/dt-bindings/clock/rk3288-cru.h
index 100a08c..465d0f6 100644
--- a/include/dt-bindings/clock/rk3288-cru.h
+++ b/include/dt-bindings/clock/rk3288-cru.h
@@ -72,6 +72,16 @@
 #define SCLK_HEVC_CABAC		111
 #define SCLK_HEVC_CORE		112
 
+#define SCLK_SDMMC_DRV_PHASE	113
+#define SCLK_SDIO0_DRV_PHASE	114
+#define SCLK_SDIO1_DRV_PHASE	115
+#define SCLK_EMMC_DRV_PHASE	116
+
+#define SCLK_SDMMC_SAMPLE_PHASE	117
+#define SCLK_SDIO0_SAMPLE_PHASE	118
+#define SCLK_SDIO1_SAMPLE_PHASE	119
+#define SCLK_EMMC_SAMPLE_PHASE	120
+
 #define DCLK_VOP0		190
 #define DCLK_VOP1		191
 
-- 
2.1.0.rc2.206.gedb03e5


^ permalink raw reply related	[flat|nested] 10+ messages in thread

* [PATCH 2/2] clk: rockchip: Add support for the mmc clock phases using the framework
  2014-11-14 22:52 ` Alexandru M Stan
@ 2014-11-14 22:52   ` Alexandru M Stan
  -1 siblings, 0 replies; 10+ messages in thread
From: Alexandru M Stan @ 2014-11-14 22:52 UTC (permalink / raw)
  To: Heiko Stuebner, Doug Anderson, addy ke
  Cc: Sonny Rao, Kever Yang, Alexandru M Stan, mturquette,
	linux-kernel, linux-arm-kernel, linux-rockchip

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 60ps long) to
generate a rough 45deg (which might be from 33deg to 66deg) setting.

Suggested-by: Heiko Stuebner <heiko@sntech.de>
Signed-off-by: Alexandru M Stan <amstan@chromium.org>
---
 drivers/clk/rockchip/Makefile        |   1 +
 drivers/clk/rockchip/clk-mmc-phase.c | 150 +++++++++++++++++++++++++++++++++++
 drivers/clk/rockchip/clk-rk3288.c    |  12 +++
 drivers/clk/rockchip/clk.c           |   8 ++
 drivers/clk/rockchip/clk.h           |  23 ++++++
 5 files changed, 194 insertions(+)
 create mode 100644 drivers/clk/rockchip/clk-mmc-phase.c

diff --git a/drivers/clk/rockchip/Makefile b/drivers/clk/rockchip/Makefile
index bd8514d..2714097 100644
--- a/drivers/clk/rockchip/Makefile
+++ b/drivers/clk/rockchip/Makefile
@@ -6,6 +6,7 @@ obj-y	+= clk-rockchip.o
 obj-y	+= clk.o
 obj-y	+= clk-pll.o
 obj-y	+= clk-cpu.o
+obj-y	+= clk-mmc-phase.o
 obj-$(CONFIG_RESET_CONTROLLER)	+= softrst.o
 
 obj-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 0000000..5c1532a
--- /dev/null
+++ b/drivers/clk/rockchip/clk-mmc-phase.c
@@ -0,0 +1,150 @@
+/*
+ * Copyright 2014 Google, Inc
+ * Author: Alexandru M Stan <amstan@chromium.org>
+ *
+ * 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, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#define DEBUG
+#include <linux/slab.h>
+#include <linux/clk-provider.h>
+#include "clk.h"
+
+struct rockchip_mmc_clock_phase {
+	struct clk_hw	hw;
+	void __iomem	*reg;
+	int		id;
+	int		shift;
+};
+
+#define to_phase(_hw) container_of(_hw, struct rockchip_mmc_clock_phase, hw)
+
+static unsigned long rockchip_mmc_recalc(struct clk_hw *hw,
+					 unsigned long parent_rate)
+{
+	return parent_rate / 2;
+}
+
+#define ROCKCHIP_MMC_DELAY_SEL BIT(10)
+#define ROCKCHIP_MMC_DEGREE_MASK 0x3
+#define ROCKCHIP_MMC_DELAYNUM_OFFSET 2
+#define ROCKCHIP_MMC_DELAYNUM_MASK (0xff << ROCKCHIP_MMC_DELAYNUM_OFFSET)
+
+#define PSECS_PER_SEC 1000000000000LL
+
+/*
+ * Each fine delay is between 40ps-80ps. Assume each fine delay is 60ps to
+ * simplify calculations. So 45degs could be anywhere between 33deg and 66deg.
+ */
+#define DELAY_ELEMENT_PSEC 60
+
+static int rockchip_mmc_get_phase(struct clk_hw *hw)
+{
+	struct rockchip_mmc_clock_phase *phase = to_phase(hw);
+	unsigned long rate = clk_get_rate(hw->clk);
+	u32 raw_value;
+	u16 degrees;
+	u8 delay_num = 0;
+
+	raw_value = readl(phase->reg) >> (phase->shift);
+
+	degrees = (raw_value & ROCKCHIP_MMC_DEGREE_MASK) * 90;
+
+	if (raw_value & ROCKCHIP_MMC_DELAY_SEL) {
+		/* degrees/delaynum * 10000 */
+		unsigned long factor = (DELAY_ELEMENT_PSEC / 10) * 36 *
+					(rate / 1000000);
+
+		delay_num = (raw_value & ROCKCHIP_MMC_DELAYNUM_MASK);
+		delay_num >>= ROCKCHIP_MMC_DELAYNUM_OFFSET;
+		degrees += delay_num * factor / 10000;
+	}
+
+	return degrees % 360;
+}
+
+static int rockchip_mmc_set_phase(struct clk_hw *hw, int degrees)
+{
+	struct rockchip_mmc_clock_phase *phase = to_phase(hw);
+	unsigned long rate = clk_get_rate(hw->clk);
+	u8 nineties, is_fortyfive;
+	u8 delay_num = 0;
+	u32 raw_value;
+
+	degrees -= degrees % 45;
+
+	nineties = degrees / 90;
+	is_fortyfive = (degrees % 90) == 45;
+
+	if (is_fortyfive) {
+		u64 delay;
+
+		delay = PSECS_PER_SEC;
+		do_div(delay, rate);
+		do_div(delay, 360 / 45);
+		do_div(delay, DELAY_ELEMENT_PSEC);
+
+		delay_num = (u8) min(delay, 255ULL);
+	}
+
+	raw_value = ROCKCHIP_MMC_DELAY_SEL;
+	raw_value |= delay_num << ROCKCHIP_MMC_DELAYNUM_OFFSET;
+	raw_value |= nineties;
+	writel(HIWORD_UPDATE(raw_value, 0xffff, phase->shift), phase->reg);
+
+	pr_debug("%s->set_phase(%d) delay_nums=%u reg[0x%p]=0x%x actual_degrees=%d\n",
+		__clk_get_name(hw->clk), degrees, delay_num,
+		phase->reg, raw_value>>phase->shift,
+		rockchip_mmc_get_phase(hw)
+	);
+
+	return 0;
+}
+
+static const struct clk_ops rockchip_phase_clk_ops = {
+	.recalc_rate	= rockchip_mmc_recalc,
+	.get_phase	= rockchip_mmc_get_phase,
+	.set_phase	= rockchip_mmc_set_phase,
+};
+
+struct clk *rockchip_clk_register_mmc_phase(const char *name,
+				const char **parent_names, u8 num_parents,
+				void __iomem *reg, int shift)
+{
+	struct clk_init_data init;
+	struct rockchip_mmc_clock_phase *phase;
+	struct clk *clk;
+
+	phase = kmalloc(sizeof(*phase), GFP_KERNEL);
+	if (!phase)
+		return NULL;
+
+	init.num_parents = num_parents;
+	init.parent_names = parent_names;
+	init.ops = &rockchip_phase_clk_ops;
+
+	phase->hw.init = &init;
+	phase->reg = reg;
+	phase->shift = shift;
+
+	if (name)
+		init.name = name;
+
+	clk = clk_register(NULL, &phase->hw);
+	if (IS_ERR(clk))
+		goto err_free;
+
+	return clk;
+
+err_free:
+	kfree(phase);
+	return NULL;
+}
diff --git a/drivers/clk/rockchip/clk-rk3288.c b/drivers/clk/rockchip/clk-rk3288.c
index 2327829..273d28a 100644
--- a/drivers/clk/rockchip/clk-rk3288.c
+++ b/drivers/clk/rockchip/clk-rk3288.c
@@ -483,6 +483,18 @@ static struct rockchip_clk_branch rk3288_clk_branches[] __initdata = {
 			RK3288_CLKSEL_CON(12), 14, 2, MFLAGS, 8, 6, DFLAGS,
 			RK3288_CLKGATE_CON(13), 3, GFLAGS),
 
+	MMC_PHASE(SCLK_SDMMC_DRV_PHASE,    "sdmmc_drv",    "sclk_sdmmc", RK3288_SDMMC_CON0, 1),
+	MMC_PHASE(SCLK_SDMMC_SAMPLE_PHASE, "sdmmc_sample", "sclk_sdmmc", RK3288_SDMMC_CON1, 0),
+
+	MMC_PHASE(SCLK_SDIO0_DRV_PHASE,    "sdio0_drv",    "sclk_sdio0", RK3288_SDIO0_CON0, 1),
+	MMC_PHASE(SCLK_SDIO0_SAMPLE_PHASE, "sdio0_sample", "sclk_sdio0", RK3288_SDIO0_CON1, 0),
+
+	MMC_PHASE(SCLK_SDIO1_DRV_PHASE,    "sdio1_drv",    "sclk_sdio1", RK3288_SDIO1_CON0, 1),
+	MMC_PHASE(SCLK_SDIO1_SAMPLE_PHASE, "sdio1_sample", "sclk_sdio1", RK3288_SDIO1_CON1, 0),
+
+	MMC_PHASE(SCLK_EMMC_DRV_PHASE,     "emmc_drv",     "sclk_emmc",  RK3288_EMMC_CON0,  1),
+	MMC_PHASE(SCLK_EMMC_SAMPLE_PHASE,  "emmc_sample",  "sclk_emmc",  RK3288_EMMC_CON1,  0),
+
 	COMPOSITE(0, "sclk_tspout", mux_tspout_p, 0,
 			RK3288_CLKSEL_CON(35), 14, 2, MFLAGS, 8, 5, DFLAGS,
 			RK3288_CLKGATE_CON(4), 11, GFLAGS),
diff --git a/drivers/clk/rockchip/clk.c b/drivers/clk/rockchip/clk.c
index 1e68bff..3149905 100644
--- a/drivers/clk/rockchip/clk.c
+++ b/drivers/clk/rockchip/clk.c
@@ -279,6 +279,14 @@ void __init rockchip_clk_register_branches(
 				list->gate_offset, list->gate_shift,
 				list->gate_flags, flags, &clk_lock);
 			break;
+		case branch_mmc_phase:
+			clk = rockchip_clk_register_mmc_phase(
+				list->name,
+				list->parent_names, list->num_parents,
+				reg_base + list->muxdiv_offset,
+				list->div_shift
+			);
+			break;
 		}
 
 		/* none of the cases above matched */
diff --git a/drivers/clk/rockchip/clk.h b/drivers/clk/rockchip/clk.h
index ca009ab..9ff5e2e 100644
--- a/drivers/clk/rockchip/clk.h
+++ b/drivers/clk/rockchip/clk.h
@@ -48,6 +48,14 @@
 #define RK3288_GLB_SRST_SND		0x1b4
 #define RK3288_SOFTRST_CON(x)		(x * 0x4 + 0x1b8)
 #define RK3288_MISC_CON			0x1e8
+#define RK3288_SDMMC_CON0		0x200
+#define RK3288_SDMMC_CON1		0x204
+#define RK3288_SDIO0_CON0		0x208
+#define RK3288_SDIO0_CON1		0x20c
+#define RK3288_SDIO1_CON0		0x210
+#define RK3288_SDIO1_CON1		0x214
+#define RK3288_EMMC_CON0		0x218
+#define RK3288_EMMC_CON1		0x21c
 
 enum rockchip_pll_type {
 	pll_rk3066,
@@ -152,6 +160,10 @@ struct clk *rockchip_clk_register_cpuclk(const char *name,
 			const struct rockchip_cpuclk_rate_table *rates,
 			int nrates, void __iomem *reg_base, spinlock_t *lock);
 
+struct clk *rockchip_clk_register_mmc_phase(const char *name,
+				const char **parent_names, u8 num_parents,
+				void __iomem *reg, int shift);
+
 #define PNAME(x) static const char *x[] __initconst
 
 enum rockchip_clk_branch_type {
@@ -160,6 +172,7 @@ enum rockchip_clk_branch_type {
 	branch_divider,
 	branch_fraction_divider,
 	branch_gate,
+	branch_mmc_phase,
 };
 
 struct rockchip_clk_branch {
@@ -352,6 +365,16 @@ struct rockchip_clk_branch {
 		.gate_flags	= gf,				\
 	}
 
+#define MMC_PHASE(_id, cname, pname, offset, shift)		\
+	{							\
+		.id		= _id,				\
+		.branch_type	= branch_mmc_phase,		\
+		.name		= cname,			\
+		.parent_names	= (const char *[]){ pname },	\
+		.num_parents	= 1,				\
+		.muxdiv_offset	= offset,			\
+		.div_shift	= shift,			\
+	}
 
 void rockchip_clk_init(struct device_node *np, void __iomem *base,
 		       unsigned long nr_clks);
-- 
2.1.0.rc2.206.gedb03e5


^ permalink raw reply related	[flat|nested] 10+ messages in thread

* [PATCH 2/2] clk: rockchip: Add support for the mmc clock phases using the framework
@ 2014-11-14 22:52   ` Alexandru M Stan
  0 siblings, 0 replies; 10+ messages in thread
From: Alexandru M Stan @ 2014-11-14 22:52 UTC (permalink / raw)
  To: linux-arm-kernel

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 60ps long) to
generate a rough 45deg (which might be from 33deg to 66deg) setting.

Suggested-by: Heiko Stuebner <heiko@sntech.de>
Signed-off-by: Alexandru M Stan <amstan@chromium.org>
---
 drivers/clk/rockchip/Makefile        |   1 +
 drivers/clk/rockchip/clk-mmc-phase.c | 150 +++++++++++++++++++++++++++++++++++
 drivers/clk/rockchip/clk-rk3288.c    |  12 +++
 drivers/clk/rockchip/clk.c           |   8 ++
 drivers/clk/rockchip/clk.h           |  23 ++++++
 5 files changed, 194 insertions(+)
 create mode 100644 drivers/clk/rockchip/clk-mmc-phase.c

diff --git a/drivers/clk/rockchip/Makefile b/drivers/clk/rockchip/Makefile
index bd8514d..2714097 100644
--- a/drivers/clk/rockchip/Makefile
+++ b/drivers/clk/rockchip/Makefile
@@ -6,6 +6,7 @@ obj-y	+= clk-rockchip.o
 obj-y	+= clk.o
 obj-y	+= clk-pll.o
 obj-y	+= clk-cpu.o
+obj-y	+= clk-mmc-phase.o
 obj-$(CONFIG_RESET_CONTROLLER)	+= softrst.o
 
 obj-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 0000000..5c1532a
--- /dev/null
+++ b/drivers/clk/rockchip/clk-mmc-phase.c
@@ -0,0 +1,150 @@
+/*
+ * Copyright 2014 Google, Inc
+ * Author: Alexandru M Stan <amstan@chromium.org>
+ *
+ * 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, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#define DEBUG
+#include <linux/slab.h>
+#include <linux/clk-provider.h>
+#include "clk.h"
+
+struct rockchip_mmc_clock_phase {
+	struct clk_hw	hw;
+	void __iomem	*reg;
+	int		id;
+	int		shift;
+};
+
+#define to_phase(_hw) container_of(_hw, struct rockchip_mmc_clock_phase, hw)
+
+static unsigned long rockchip_mmc_recalc(struct clk_hw *hw,
+					 unsigned long parent_rate)
+{
+	return parent_rate / 2;
+}
+
+#define ROCKCHIP_MMC_DELAY_SEL BIT(10)
+#define ROCKCHIP_MMC_DEGREE_MASK 0x3
+#define ROCKCHIP_MMC_DELAYNUM_OFFSET 2
+#define ROCKCHIP_MMC_DELAYNUM_MASK (0xff << ROCKCHIP_MMC_DELAYNUM_OFFSET)
+
+#define PSECS_PER_SEC 1000000000000LL
+
+/*
+ * Each fine delay is between 40ps-80ps. Assume each fine delay is 60ps to
+ * simplify calculations. So 45degs could be anywhere between 33deg and 66deg.
+ */
+#define DELAY_ELEMENT_PSEC 60
+
+static int rockchip_mmc_get_phase(struct clk_hw *hw)
+{
+	struct rockchip_mmc_clock_phase *phase = to_phase(hw);
+	unsigned long rate = clk_get_rate(hw->clk);
+	u32 raw_value;
+	u16 degrees;
+	u8 delay_num = 0;
+
+	raw_value = readl(phase->reg) >> (phase->shift);
+
+	degrees = (raw_value & ROCKCHIP_MMC_DEGREE_MASK) * 90;
+
+	if (raw_value & ROCKCHIP_MMC_DELAY_SEL) {
+		/* degrees/delaynum * 10000 */
+		unsigned long factor = (DELAY_ELEMENT_PSEC / 10) * 36 *
+					(rate / 1000000);
+
+		delay_num = (raw_value & ROCKCHIP_MMC_DELAYNUM_MASK);
+		delay_num >>= ROCKCHIP_MMC_DELAYNUM_OFFSET;
+		degrees += delay_num * factor / 10000;
+	}
+
+	return degrees % 360;
+}
+
+static int rockchip_mmc_set_phase(struct clk_hw *hw, int degrees)
+{
+	struct rockchip_mmc_clock_phase *phase = to_phase(hw);
+	unsigned long rate = clk_get_rate(hw->clk);
+	u8 nineties, is_fortyfive;
+	u8 delay_num = 0;
+	u32 raw_value;
+
+	degrees -= degrees % 45;
+
+	nineties = degrees / 90;
+	is_fortyfive = (degrees % 90) == 45;
+
+	if (is_fortyfive) {
+		u64 delay;
+
+		delay = PSECS_PER_SEC;
+		do_div(delay, rate);
+		do_div(delay, 360 / 45);
+		do_div(delay, DELAY_ELEMENT_PSEC);
+
+		delay_num = (u8) min(delay, 255ULL);
+	}
+
+	raw_value = ROCKCHIP_MMC_DELAY_SEL;
+	raw_value |= delay_num << ROCKCHIP_MMC_DELAYNUM_OFFSET;
+	raw_value |= nineties;
+	writel(HIWORD_UPDATE(raw_value, 0xffff, phase->shift), phase->reg);
+
+	pr_debug("%s->set_phase(%d) delay_nums=%u reg[0x%p]=0x%x actual_degrees=%d\n",
+		__clk_get_name(hw->clk), degrees, delay_num,
+		phase->reg, raw_value>>phase->shift,
+		rockchip_mmc_get_phase(hw)
+	);
+
+	return 0;
+}
+
+static const struct clk_ops rockchip_phase_clk_ops = {
+	.recalc_rate	= rockchip_mmc_recalc,
+	.get_phase	= rockchip_mmc_get_phase,
+	.set_phase	= rockchip_mmc_set_phase,
+};
+
+struct clk *rockchip_clk_register_mmc_phase(const char *name,
+				const char **parent_names, u8 num_parents,
+				void __iomem *reg, int shift)
+{
+	struct clk_init_data init;
+	struct rockchip_mmc_clock_phase *phase;
+	struct clk *clk;
+
+	phase = kmalloc(sizeof(*phase), GFP_KERNEL);
+	if (!phase)
+		return NULL;
+
+	init.num_parents = num_parents;
+	init.parent_names = parent_names;
+	init.ops = &rockchip_phase_clk_ops;
+
+	phase->hw.init = &init;
+	phase->reg = reg;
+	phase->shift = shift;
+
+	if (name)
+		init.name = name;
+
+	clk = clk_register(NULL, &phase->hw);
+	if (IS_ERR(clk))
+		goto err_free;
+
+	return clk;
+
+err_free:
+	kfree(phase);
+	return NULL;
+}
diff --git a/drivers/clk/rockchip/clk-rk3288.c b/drivers/clk/rockchip/clk-rk3288.c
index 2327829..273d28a 100644
--- a/drivers/clk/rockchip/clk-rk3288.c
+++ b/drivers/clk/rockchip/clk-rk3288.c
@@ -483,6 +483,18 @@ static struct rockchip_clk_branch rk3288_clk_branches[] __initdata = {
 			RK3288_CLKSEL_CON(12), 14, 2, MFLAGS, 8, 6, DFLAGS,
 			RK3288_CLKGATE_CON(13), 3, GFLAGS),
 
+	MMC_PHASE(SCLK_SDMMC_DRV_PHASE,    "sdmmc_drv",    "sclk_sdmmc", RK3288_SDMMC_CON0, 1),
+	MMC_PHASE(SCLK_SDMMC_SAMPLE_PHASE, "sdmmc_sample", "sclk_sdmmc", RK3288_SDMMC_CON1, 0),
+
+	MMC_PHASE(SCLK_SDIO0_DRV_PHASE,    "sdio0_drv",    "sclk_sdio0", RK3288_SDIO0_CON0, 1),
+	MMC_PHASE(SCLK_SDIO0_SAMPLE_PHASE, "sdio0_sample", "sclk_sdio0", RK3288_SDIO0_CON1, 0),
+
+	MMC_PHASE(SCLK_SDIO1_DRV_PHASE,    "sdio1_drv",    "sclk_sdio1", RK3288_SDIO1_CON0, 1),
+	MMC_PHASE(SCLK_SDIO1_SAMPLE_PHASE, "sdio1_sample", "sclk_sdio1", RK3288_SDIO1_CON1, 0),
+
+	MMC_PHASE(SCLK_EMMC_DRV_PHASE,     "emmc_drv",     "sclk_emmc",  RK3288_EMMC_CON0,  1),
+	MMC_PHASE(SCLK_EMMC_SAMPLE_PHASE,  "emmc_sample",  "sclk_emmc",  RK3288_EMMC_CON1,  0),
+
 	COMPOSITE(0, "sclk_tspout", mux_tspout_p, 0,
 			RK3288_CLKSEL_CON(35), 14, 2, MFLAGS, 8, 5, DFLAGS,
 			RK3288_CLKGATE_CON(4), 11, GFLAGS),
diff --git a/drivers/clk/rockchip/clk.c b/drivers/clk/rockchip/clk.c
index 1e68bff..3149905 100644
--- a/drivers/clk/rockchip/clk.c
+++ b/drivers/clk/rockchip/clk.c
@@ -279,6 +279,14 @@ void __init rockchip_clk_register_branches(
 				list->gate_offset, list->gate_shift,
 				list->gate_flags, flags, &clk_lock);
 			break;
+		case branch_mmc_phase:
+			clk = rockchip_clk_register_mmc_phase(
+				list->name,
+				list->parent_names, list->num_parents,
+				reg_base + list->muxdiv_offset,
+				list->div_shift
+			);
+			break;
 		}
 
 		/* none of the cases above matched */
diff --git a/drivers/clk/rockchip/clk.h b/drivers/clk/rockchip/clk.h
index ca009ab..9ff5e2e 100644
--- a/drivers/clk/rockchip/clk.h
+++ b/drivers/clk/rockchip/clk.h
@@ -48,6 +48,14 @@
 #define RK3288_GLB_SRST_SND		0x1b4
 #define RK3288_SOFTRST_CON(x)		(x * 0x4 + 0x1b8)
 #define RK3288_MISC_CON			0x1e8
+#define RK3288_SDMMC_CON0		0x200
+#define RK3288_SDMMC_CON1		0x204
+#define RK3288_SDIO0_CON0		0x208
+#define RK3288_SDIO0_CON1		0x20c
+#define RK3288_SDIO1_CON0		0x210
+#define RK3288_SDIO1_CON1		0x214
+#define RK3288_EMMC_CON0		0x218
+#define RK3288_EMMC_CON1		0x21c
 
 enum rockchip_pll_type {
 	pll_rk3066,
@@ -152,6 +160,10 @@ struct clk *rockchip_clk_register_cpuclk(const char *name,
 			const struct rockchip_cpuclk_rate_table *rates,
 			int nrates, void __iomem *reg_base, spinlock_t *lock);
 
+struct clk *rockchip_clk_register_mmc_phase(const char *name,
+				const char **parent_names, u8 num_parents,
+				void __iomem *reg, int shift);
+
 #define PNAME(x) static const char *x[] __initconst
 
 enum rockchip_clk_branch_type {
@@ -160,6 +172,7 @@ enum rockchip_clk_branch_type {
 	branch_divider,
 	branch_fraction_divider,
 	branch_gate,
+	branch_mmc_phase,
 };
 
 struct rockchip_clk_branch {
@@ -352,6 +365,16 @@ struct rockchip_clk_branch {
 		.gate_flags	= gf,				\
 	}
 
+#define MMC_PHASE(_id, cname, pname, offset, shift)		\
+	{							\
+		.id		= _id,				\
+		.branch_type	= branch_mmc_phase,		\
+		.name		= cname,			\
+		.parent_names	= (const char *[]){ pname },	\
+		.num_parents	= 1,				\
+		.muxdiv_offset	= offset,			\
+		.div_shift	= shift,			\
+	}
 
 void rockchip_clk_init(struct device_node *np, void __iomem *base,
 		       unsigned long nr_clks);
-- 
2.1.0.rc2.206.gedb03e5

^ permalink raw reply related	[flat|nested] 10+ messages in thread

* Re: [PATCH 2/2] clk: rockchip: Add support for the mmc clock phases using the framework
  2014-11-14 22:52   ` Alexandru M Stan
@ 2014-11-14 23:43     ` Heiko Stübner
  -1 siblings, 0 replies; 10+ messages in thread
From: Heiko Stübner @ 2014-11-14 23:43 UTC (permalink / raw)
  To: Alexandru M Stan, mturquette
  Cc: Doug Anderson, addy ke, Sonny Rao, Kever Yang, linux-kernel,
	linux-arm-kernel, linux-rockchip

Hi,

Am Freitag, 14. November 2014, 14:52:54 schrieb Alexandru M Stan:
> 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
> 60ps long) to generate a rough 45deg (which might be from 33deg to 66deg)
> setting.
> 
> Suggested-by: Heiko Stuebner <heiko@sntech.de>
> Signed-off-by: Alexandru M Stan <amstan@chromium.org>

apart from the dangling #define DEBUG below, this looks good to me.

But I'd like to get a go ahead from Mike for this bigger chunk of new code.

> ---
>  drivers/clk/rockchip/Makefile        |   1 +
>  drivers/clk/rockchip/clk-mmc-phase.c | 150
> +++++++++++++++++++++++++++++++++++ drivers/clk/rockchip/clk-rk3288.c    | 
> 12 +++
>  drivers/clk/rockchip/clk.c           |   8 ++
>  drivers/clk/rockchip/clk.h           |  23 ++++++
>  5 files changed, 194 insertions(+)
>  create mode 100644 drivers/clk/rockchip/clk-mmc-phase.c
> 
> diff --git a/drivers/clk/rockchip/Makefile b/drivers/clk/rockchip/Makefile
> index bd8514d..2714097 100644
> --- a/drivers/clk/rockchip/Makefile
> +++ b/drivers/clk/rockchip/Makefile
> @@ -6,6 +6,7 @@ obj-y	+= clk-rockchip.o
>  obj-y	+= clk.o
>  obj-y	+= clk-pll.o
>  obj-y	+= clk-cpu.o
> +obj-y	+= clk-mmc-phase.o
>  obj-$(CONFIG_RESET_CONTROLLER)	+= softrst.o
> 
>  obj-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 0000000..5c1532a
> --- /dev/null
> +++ b/drivers/clk/rockchip/clk-mmc-phase.c
> @@ -0,0 +1,150 @@
> +/*
> + * Copyright 2014 Google, Inc
> + * Author: Alexandru M Stan <amstan@chromium.org>
> + *
> + * 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, or
> + * (at your option) any later version.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> + * GNU General Public License for more details.
> + */
> +
> +#define DEBUG

should not be here


Heiko

> +#include <linux/slab.h>
> +#include <linux/clk-provider.h>
> +#include "clk.h"
> +
> +struct rockchip_mmc_clock_phase {
> +	struct clk_hw	hw;
> +	void __iomem	*reg;
> +	int		id;
> +	int		shift;
> +};
> +
> +#define to_phase(_hw) container_of(_hw, struct rockchip_mmc_clock_phase,
> hw) +
> +static unsigned long rockchip_mmc_recalc(struct clk_hw *hw,
> +					 unsigned long parent_rate)
> +{
> +	return parent_rate / 2;
> +}
> +
> +#define ROCKCHIP_MMC_DELAY_SEL BIT(10)
> +#define ROCKCHIP_MMC_DEGREE_MASK 0x3
> +#define ROCKCHIP_MMC_DELAYNUM_OFFSET 2
> +#define ROCKCHIP_MMC_DELAYNUM_MASK (0xff << ROCKCHIP_MMC_DELAYNUM_OFFSET)
> +
> +#define PSECS_PER_SEC 1000000000000LL
> +
> +/*
> + * Each fine delay is between 40ps-80ps. Assume each fine delay is 60ps to
> + * simplify calculations. So 45degs could be anywhere between 33deg and
> 66deg. + */
> +#define DELAY_ELEMENT_PSEC 60
> +
> +static int rockchip_mmc_get_phase(struct clk_hw *hw)
> +{
> +	struct rockchip_mmc_clock_phase *phase = to_phase(hw);
> +	unsigned long rate = clk_get_rate(hw->clk);
> +	u32 raw_value;
> +	u16 degrees;
> +	u8 delay_num = 0;
> +
> +	raw_value = readl(phase->reg) >> (phase->shift);
> +
> +	degrees = (raw_value & ROCKCHIP_MMC_DEGREE_MASK) * 90;
> +
> +	if (raw_value & ROCKCHIP_MMC_DELAY_SEL) {
> +		/* degrees/delaynum * 10000 */
> +		unsigned long factor = (DELAY_ELEMENT_PSEC / 10) * 36 *
> +					(rate / 1000000);
> +
> +		delay_num = (raw_value & ROCKCHIP_MMC_DELAYNUM_MASK);
> +		delay_num >>= ROCKCHIP_MMC_DELAYNUM_OFFSET;
> +		degrees += delay_num * factor / 10000;
> +	}
> +
> +	return degrees % 360;
> +}
> +
> +static int rockchip_mmc_set_phase(struct clk_hw *hw, int degrees)
> +{
> +	struct rockchip_mmc_clock_phase *phase = to_phase(hw);
> +	unsigned long rate = clk_get_rate(hw->clk);
> +	u8 nineties, is_fortyfive;
> +	u8 delay_num = 0;
> +	u32 raw_value;
> +
> +	degrees -= degrees % 45;
> +
> +	nineties = degrees / 90;
> +	is_fortyfive = (degrees % 90) == 45;
> +
> +	if (is_fortyfive) {
> +		u64 delay;
> +
> +		delay = PSECS_PER_SEC;
> +		do_div(delay, rate);
> +		do_div(delay, 360 / 45);
> +		do_div(delay, DELAY_ELEMENT_PSEC);
> +
> +		delay_num = (u8) min(delay, 255ULL);
> +	}
> +
> +	raw_value = ROCKCHIP_MMC_DELAY_SEL;
> +	raw_value |= delay_num << ROCKCHIP_MMC_DELAYNUM_OFFSET;
> +	raw_value |= nineties;
> +	writel(HIWORD_UPDATE(raw_value, 0xffff, phase->shift), phase->reg);
> +
> +	pr_debug("%s->set_phase(%d) delay_nums=%u reg[0x%p]=0x%x
> actual_degrees=%d\n", +		__clk_get_name(hw->clk), degrees, delay_num,
> +		phase->reg, raw_value>>phase->shift,
> +		rockchip_mmc_get_phase(hw)
> +	);
> +
> +	return 0;
> +}
> +
> +static const struct clk_ops rockchip_phase_clk_ops = {
> +	.recalc_rate	= rockchip_mmc_recalc,
> +	.get_phase	= rockchip_mmc_get_phase,
> +	.set_phase	= rockchip_mmc_set_phase,
> +};
> +
> +struct clk *rockchip_clk_register_mmc_phase(const char *name,
> +				const char **parent_names, u8 num_parents,
> +				void __iomem *reg, int shift)
> +{
> +	struct clk_init_data init;
> +	struct rockchip_mmc_clock_phase *phase;
> +	struct clk *clk;
> +
> +	phase = kmalloc(sizeof(*phase), GFP_KERNEL);
> +	if (!phase)
> +		return NULL;
> +
> +	init.num_parents = num_parents;
> +	init.parent_names = parent_names;
> +	init.ops = &rockchip_phase_clk_ops;
> +
> +	phase->hw.init = &init;
> +	phase->reg = reg;
> +	phase->shift = shift;
> +
> +	if (name)
> +		init.name = name;
> +
> +	clk = clk_register(NULL, &phase->hw);
> +	if (IS_ERR(clk))
> +		goto err_free;
> +
> +	return clk;
> +
> +err_free:
> +	kfree(phase);
> +	return NULL;
> +}
> diff --git a/drivers/clk/rockchip/clk-rk3288.c
> b/drivers/clk/rockchip/clk-rk3288.c index 2327829..273d28a 100644
> --- a/drivers/clk/rockchip/clk-rk3288.c
> +++ b/drivers/clk/rockchip/clk-rk3288.c
> @@ -483,6 +483,18 @@ static struct rockchip_clk_branch rk3288_clk_branches[]
> __initdata = { RK3288_CLKSEL_CON(12), 14, 2, MFLAGS, 8, 6, DFLAGS,
>  			RK3288_CLKGATE_CON(13), 3, GFLAGS),
> 
> +	MMC_PHASE(SCLK_SDMMC_DRV_PHASE,    "sdmmc_drv",    "sclk_sdmmc",
> RK3288_SDMMC_CON0, 1), +	MMC_PHASE(SCLK_SDMMC_SAMPLE_PHASE, "sdmmc_sample",
> "sclk_sdmmc", RK3288_SDMMC_CON1, 0), +
> +	MMC_PHASE(SCLK_SDIO0_DRV_PHASE,    "sdio0_drv",    "sclk_sdio0",
> RK3288_SDIO0_CON0, 1), +	MMC_PHASE(SCLK_SDIO0_SAMPLE_PHASE, "sdio0_sample",
> "sclk_sdio0", RK3288_SDIO0_CON1, 0), +
> +	MMC_PHASE(SCLK_SDIO1_DRV_PHASE,    "sdio1_drv",    "sclk_sdio1",
> RK3288_SDIO1_CON0, 1), +	MMC_PHASE(SCLK_SDIO1_SAMPLE_PHASE, "sdio1_sample",
> "sclk_sdio1", RK3288_SDIO1_CON1, 0), +
> +	MMC_PHASE(SCLK_EMMC_DRV_PHASE,     "emmc_drv",     "sclk_emmc", 
> RK3288_EMMC_CON0,  1), +	MMC_PHASE(SCLK_EMMC_SAMPLE_PHASE,  "emmc_sample", 
> "sclk_emmc",  RK3288_EMMC_CON1,  0), +
>  	COMPOSITE(0, "sclk_tspout", mux_tspout_p, 0,
>  			RK3288_CLKSEL_CON(35), 14, 2, MFLAGS, 8, 5, DFLAGS,
>  			RK3288_CLKGATE_CON(4), 11, GFLAGS),
> diff --git a/drivers/clk/rockchip/clk.c b/drivers/clk/rockchip/clk.c
> index 1e68bff..3149905 100644
> --- a/drivers/clk/rockchip/clk.c
> +++ b/drivers/clk/rockchip/clk.c
> @@ -279,6 +279,14 @@ void __init rockchip_clk_register_branches(
>  				list->gate_offset, list->gate_shift,
>  				list->gate_flags, flags, &clk_lock);
>  			break;
> +		case branch_mmc_phase:
> +			clk = rockchip_clk_register_mmc_phase(
> +				list->name,
> +				list->parent_names, list->num_parents,
> +				reg_base + list->muxdiv_offset,
> +				list->div_shift
> +			);
> +			break;
>  		}
> 
>  		/* none of the cases above matched */
> diff --git a/drivers/clk/rockchip/clk.h b/drivers/clk/rockchip/clk.h
> index ca009ab..9ff5e2e 100644
> --- a/drivers/clk/rockchip/clk.h
> +++ b/drivers/clk/rockchip/clk.h
> @@ -48,6 +48,14 @@
>  #define RK3288_GLB_SRST_SND		0x1b4
>  #define RK3288_SOFTRST_CON(x)		(x * 0x4 + 0x1b8)
>  #define RK3288_MISC_CON			0x1e8
> +#define RK3288_SDMMC_CON0		0x200
> +#define RK3288_SDMMC_CON1		0x204
> +#define RK3288_SDIO0_CON0		0x208
> +#define RK3288_SDIO0_CON1		0x20c
> +#define RK3288_SDIO1_CON0		0x210
> +#define RK3288_SDIO1_CON1		0x214
> +#define RK3288_EMMC_CON0		0x218
> +#define RK3288_EMMC_CON1		0x21c
> 
>  enum rockchip_pll_type {
>  	pll_rk3066,
> @@ -152,6 +160,10 @@ struct clk *rockchip_clk_register_cpuclk(const char
> *name, const struct rockchip_cpuclk_rate_table *rates,
>  			int nrates, void __iomem *reg_base, spinlock_t *lock);
> 
> +struct clk *rockchip_clk_register_mmc_phase(const char *name,
> +				const char **parent_names, u8 num_parents,
> +				void __iomem *reg, int shift);
> +
>  #define PNAME(x) static const char *x[] __initconst
> 
>  enum rockchip_clk_branch_type {
> @@ -160,6 +172,7 @@ enum rockchip_clk_branch_type {
>  	branch_divider,
>  	branch_fraction_divider,
>  	branch_gate,
> +	branch_mmc_phase,
>  };
> 
>  struct rockchip_clk_branch {
> @@ -352,6 +365,16 @@ struct rockchip_clk_branch {
>  		.gate_flags	= gf,				\
>  	}
> 
> +#define MMC_PHASE(_id, cname, pname, offset, shift)		\
> +	{							\
> +		.id		= _id,				\
> +		.branch_type	= branch_mmc_phase,		\
> +		.name		= cname,			\
> +		.parent_names	= (const char *[]){ pname },	\
> +		.num_parents	= 1,				\
> +		.muxdiv_offset	= offset,			\
> +		.div_shift	= shift,			\
> +	}
> 
>  void rockchip_clk_init(struct device_node *np, void __iomem *base,
>  		       unsigned long nr_clks);


^ permalink raw reply	[flat|nested] 10+ messages in thread

* [PATCH 2/2] clk: rockchip: Add support for the mmc clock phases using the framework
@ 2014-11-14 23:43     ` Heiko Stübner
  0 siblings, 0 replies; 10+ messages in thread
From: Heiko Stübner @ 2014-11-14 23:43 UTC (permalink / raw)
  To: linux-arm-kernel

Hi,

Am Freitag, 14. November 2014, 14:52:54 schrieb Alexandru M Stan:
> 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
> 60ps long) to generate a rough 45deg (which might be from 33deg to 66deg)
> setting.
> 
> Suggested-by: Heiko Stuebner <heiko@sntech.de>
> Signed-off-by: Alexandru M Stan <amstan@chromium.org>

apart from the dangling #define DEBUG below, this looks good to me.

But I'd like to get a go ahead from Mike for this bigger chunk of new code.

> ---
>  drivers/clk/rockchip/Makefile        |   1 +
>  drivers/clk/rockchip/clk-mmc-phase.c | 150
> +++++++++++++++++++++++++++++++++++ drivers/clk/rockchip/clk-rk3288.c    | 
> 12 +++
>  drivers/clk/rockchip/clk.c           |   8 ++
>  drivers/clk/rockchip/clk.h           |  23 ++++++
>  5 files changed, 194 insertions(+)
>  create mode 100644 drivers/clk/rockchip/clk-mmc-phase.c
> 
> diff --git a/drivers/clk/rockchip/Makefile b/drivers/clk/rockchip/Makefile
> index bd8514d..2714097 100644
> --- a/drivers/clk/rockchip/Makefile
> +++ b/drivers/clk/rockchip/Makefile
> @@ -6,6 +6,7 @@ obj-y	+= clk-rockchip.o
>  obj-y	+= clk.o
>  obj-y	+= clk-pll.o
>  obj-y	+= clk-cpu.o
> +obj-y	+= clk-mmc-phase.o
>  obj-$(CONFIG_RESET_CONTROLLER)	+= softrst.o
> 
>  obj-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 0000000..5c1532a
> --- /dev/null
> +++ b/drivers/clk/rockchip/clk-mmc-phase.c
> @@ -0,0 +1,150 @@
> +/*
> + * Copyright 2014 Google, Inc
> + * Author: Alexandru M Stan <amstan@chromium.org>
> + *
> + * 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, or
> + * (at your option) any later version.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> + * GNU General Public License for more details.
> + */
> +
> +#define DEBUG

should not be here


Heiko

> +#include <linux/slab.h>
> +#include <linux/clk-provider.h>
> +#include "clk.h"
> +
> +struct rockchip_mmc_clock_phase {
> +	struct clk_hw	hw;
> +	void __iomem	*reg;
> +	int		id;
> +	int		shift;
> +};
> +
> +#define to_phase(_hw) container_of(_hw, struct rockchip_mmc_clock_phase,
> hw) +
> +static unsigned long rockchip_mmc_recalc(struct clk_hw *hw,
> +					 unsigned long parent_rate)
> +{
> +	return parent_rate / 2;
> +}
> +
> +#define ROCKCHIP_MMC_DELAY_SEL BIT(10)
> +#define ROCKCHIP_MMC_DEGREE_MASK 0x3
> +#define ROCKCHIP_MMC_DELAYNUM_OFFSET 2
> +#define ROCKCHIP_MMC_DELAYNUM_MASK (0xff << ROCKCHIP_MMC_DELAYNUM_OFFSET)
> +
> +#define PSECS_PER_SEC 1000000000000LL
> +
> +/*
> + * Each fine delay is between 40ps-80ps. Assume each fine delay is 60ps to
> + * simplify calculations. So 45degs could be anywhere between 33deg and
> 66deg. + */
> +#define DELAY_ELEMENT_PSEC 60
> +
> +static int rockchip_mmc_get_phase(struct clk_hw *hw)
> +{
> +	struct rockchip_mmc_clock_phase *phase = to_phase(hw);
> +	unsigned long rate = clk_get_rate(hw->clk);
> +	u32 raw_value;
> +	u16 degrees;
> +	u8 delay_num = 0;
> +
> +	raw_value = readl(phase->reg) >> (phase->shift);
> +
> +	degrees = (raw_value & ROCKCHIP_MMC_DEGREE_MASK) * 90;
> +
> +	if (raw_value & ROCKCHIP_MMC_DELAY_SEL) {
> +		/* degrees/delaynum * 10000 */
> +		unsigned long factor = (DELAY_ELEMENT_PSEC / 10) * 36 *
> +					(rate / 1000000);
> +
> +		delay_num = (raw_value & ROCKCHIP_MMC_DELAYNUM_MASK);
> +		delay_num >>= ROCKCHIP_MMC_DELAYNUM_OFFSET;
> +		degrees += delay_num * factor / 10000;
> +	}
> +
> +	return degrees % 360;
> +}
> +
> +static int rockchip_mmc_set_phase(struct clk_hw *hw, int degrees)
> +{
> +	struct rockchip_mmc_clock_phase *phase = to_phase(hw);
> +	unsigned long rate = clk_get_rate(hw->clk);
> +	u8 nineties, is_fortyfive;
> +	u8 delay_num = 0;
> +	u32 raw_value;
> +
> +	degrees -= degrees % 45;
> +
> +	nineties = degrees / 90;
> +	is_fortyfive = (degrees % 90) == 45;
> +
> +	if (is_fortyfive) {
> +		u64 delay;
> +
> +		delay = PSECS_PER_SEC;
> +		do_div(delay, rate);
> +		do_div(delay, 360 / 45);
> +		do_div(delay, DELAY_ELEMENT_PSEC);
> +
> +		delay_num = (u8) min(delay, 255ULL);
> +	}
> +
> +	raw_value = ROCKCHIP_MMC_DELAY_SEL;
> +	raw_value |= delay_num << ROCKCHIP_MMC_DELAYNUM_OFFSET;
> +	raw_value |= nineties;
> +	writel(HIWORD_UPDATE(raw_value, 0xffff, phase->shift), phase->reg);
> +
> +	pr_debug("%s->set_phase(%d) delay_nums=%u reg[0x%p]=0x%x
> actual_degrees=%d\n", +		__clk_get_name(hw->clk), degrees, delay_num,
> +		phase->reg, raw_value>>phase->shift,
> +		rockchip_mmc_get_phase(hw)
> +	);
> +
> +	return 0;
> +}
> +
> +static const struct clk_ops rockchip_phase_clk_ops = {
> +	.recalc_rate	= rockchip_mmc_recalc,
> +	.get_phase	= rockchip_mmc_get_phase,
> +	.set_phase	= rockchip_mmc_set_phase,
> +};
> +
> +struct clk *rockchip_clk_register_mmc_phase(const char *name,
> +				const char **parent_names, u8 num_parents,
> +				void __iomem *reg, int shift)
> +{
> +	struct clk_init_data init;
> +	struct rockchip_mmc_clock_phase *phase;
> +	struct clk *clk;
> +
> +	phase = kmalloc(sizeof(*phase), GFP_KERNEL);
> +	if (!phase)
> +		return NULL;
> +
> +	init.num_parents = num_parents;
> +	init.parent_names = parent_names;
> +	init.ops = &rockchip_phase_clk_ops;
> +
> +	phase->hw.init = &init;
> +	phase->reg = reg;
> +	phase->shift = shift;
> +
> +	if (name)
> +		init.name = name;
> +
> +	clk = clk_register(NULL, &phase->hw);
> +	if (IS_ERR(clk))
> +		goto err_free;
> +
> +	return clk;
> +
> +err_free:
> +	kfree(phase);
> +	return NULL;
> +}
> diff --git a/drivers/clk/rockchip/clk-rk3288.c
> b/drivers/clk/rockchip/clk-rk3288.c index 2327829..273d28a 100644
> --- a/drivers/clk/rockchip/clk-rk3288.c
> +++ b/drivers/clk/rockchip/clk-rk3288.c
> @@ -483,6 +483,18 @@ static struct rockchip_clk_branch rk3288_clk_branches[]
> __initdata = { RK3288_CLKSEL_CON(12), 14, 2, MFLAGS, 8, 6, DFLAGS,
>  			RK3288_CLKGATE_CON(13), 3, GFLAGS),
> 
> +	MMC_PHASE(SCLK_SDMMC_DRV_PHASE,    "sdmmc_drv",    "sclk_sdmmc",
> RK3288_SDMMC_CON0, 1), +	MMC_PHASE(SCLK_SDMMC_SAMPLE_PHASE, "sdmmc_sample",
> "sclk_sdmmc", RK3288_SDMMC_CON1, 0), +
> +	MMC_PHASE(SCLK_SDIO0_DRV_PHASE,    "sdio0_drv",    "sclk_sdio0",
> RK3288_SDIO0_CON0, 1), +	MMC_PHASE(SCLK_SDIO0_SAMPLE_PHASE, "sdio0_sample",
> "sclk_sdio0", RK3288_SDIO0_CON1, 0), +
> +	MMC_PHASE(SCLK_SDIO1_DRV_PHASE,    "sdio1_drv",    "sclk_sdio1",
> RK3288_SDIO1_CON0, 1), +	MMC_PHASE(SCLK_SDIO1_SAMPLE_PHASE, "sdio1_sample",
> "sclk_sdio1", RK3288_SDIO1_CON1, 0), +
> +	MMC_PHASE(SCLK_EMMC_DRV_PHASE,     "emmc_drv",     "sclk_emmc", 
> RK3288_EMMC_CON0,  1), +	MMC_PHASE(SCLK_EMMC_SAMPLE_PHASE,  "emmc_sample", 
> "sclk_emmc",  RK3288_EMMC_CON1,  0), +
>  	COMPOSITE(0, "sclk_tspout", mux_tspout_p, 0,
>  			RK3288_CLKSEL_CON(35), 14, 2, MFLAGS, 8, 5, DFLAGS,
>  			RK3288_CLKGATE_CON(4), 11, GFLAGS),
> diff --git a/drivers/clk/rockchip/clk.c b/drivers/clk/rockchip/clk.c
> index 1e68bff..3149905 100644
> --- a/drivers/clk/rockchip/clk.c
> +++ b/drivers/clk/rockchip/clk.c
> @@ -279,6 +279,14 @@ void __init rockchip_clk_register_branches(
>  				list->gate_offset, list->gate_shift,
>  				list->gate_flags, flags, &clk_lock);
>  			break;
> +		case branch_mmc_phase:
> +			clk = rockchip_clk_register_mmc_phase(
> +				list->name,
> +				list->parent_names, list->num_parents,
> +				reg_base + list->muxdiv_offset,
> +				list->div_shift
> +			);
> +			break;
>  		}
> 
>  		/* none of the cases above matched */
> diff --git a/drivers/clk/rockchip/clk.h b/drivers/clk/rockchip/clk.h
> index ca009ab..9ff5e2e 100644
> --- a/drivers/clk/rockchip/clk.h
> +++ b/drivers/clk/rockchip/clk.h
> @@ -48,6 +48,14 @@
>  #define RK3288_GLB_SRST_SND		0x1b4
>  #define RK3288_SOFTRST_CON(x)		(x * 0x4 + 0x1b8)
>  #define RK3288_MISC_CON			0x1e8
> +#define RK3288_SDMMC_CON0		0x200
> +#define RK3288_SDMMC_CON1		0x204
> +#define RK3288_SDIO0_CON0		0x208
> +#define RK3288_SDIO0_CON1		0x20c
> +#define RK3288_SDIO1_CON0		0x210
> +#define RK3288_SDIO1_CON1		0x214
> +#define RK3288_EMMC_CON0		0x218
> +#define RK3288_EMMC_CON1		0x21c
> 
>  enum rockchip_pll_type {
>  	pll_rk3066,
> @@ -152,6 +160,10 @@ struct clk *rockchip_clk_register_cpuclk(const char
> *name, const struct rockchip_cpuclk_rate_table *rates,
>  			int nrates, void __iomem *reg_base, spinlock_t *lock);
> 
> +struct clk *rockchip_clk_register_mmc_phase(const char *name,
> +				const char **parent_names, u8 num_parents,
> +				void __iomem *reg, int shift);
> +
>  #define PNAME(x) static const char *x[] __initconst
> 
>  enum rockchip_clk_branch_type {
> @@ -160,6 +172,7 @@ enum rockchip_clk_branch_type {
>  	branch_divider,
>  	branch_fraction_divider,
>  	branch_gate,
> +	branch_mmc_phase,
>  };
> 
>  struct rockchip_clk_branch {
> @@ -352,6 +365,16 @@ struct rockchip_clk_branch {
>  		.gate_flags	= gf,				\
>  	}
> 
> +#define MMC_PHASE(_id, cname, pname, offset, shift)		\
> +	{							\
> +		.id		= _id,				\
> +		.branch_type	= branch_mmc_phase,		\
> +		.name		= cname,			\
> +		.parent_names	= (const char *[]){ pname },	\
> +		.num_parents	= 1,				\
> +		.muxdiv_offset	= offset,			\
> +		.div_shift	= shift,			\
> +	}
> 
>  void rockchip_clk_init(struct device_node *np, void __iomem *base,
>  		       unsigned long nr_clks);

^ permalink raw reply	[flat|nested] 10+ messages in thread

* Re: [PATCH 1/2] clk: rockchip: add bindings for the mmc clock phases
@ 2014-11-18 17:44     ` Doug Anderson
  0 siblings, 0 replies; 10+ messages in thread
From: Doug Anderson @ 2014-11-18 17:44 UTC (permalink / raw)
  To: Alexandru M Stan
  Cc: Heiko Stuebner, addy ke, Sonny Rao, Kever Yang, Rob Herring,
	Pawel Moll, Mark Rutland, Ian Campbell, Kumar Gala,
	Mike Turquette, Mark yao, devicetree, linux-kernel

Hi,

On Fri, Nov 14, 2014 at 2:52 PM, Alexandru M Stan <amstan@chromium.org> wrote:
> This will be used in a later patch for clock phase tuning.
>
> Suggested-by: Heiko Stuebner <heiko@sntech.de>
> Signed-off-by: Alexandru M Stan <amstan@chromium.org>
> ---
>  include/dt-bindings/clock/rk3288-cru.h | 10 ++++++++++
>  1 file changed, 10 insertions(+)
>
> diff --git a/include/dt-bindings/clock/rk3288-cru.h b/include/dt-bindings/clock/rk3288-cru.h
> index 100a08c..465d0f6 100644
> --- a/include/dt-bindings/clock/rk3288-cru.h
> +++ b/include/dt-bindings/clock/rk3288-cru.h
> @@ -72,6 +72,16 @@
>  #define SCLK_HEVC_CABAC                111
>  #define SCLK_HEVC_CORE         112
>
> +#define SCLK_SDMMC_DRV_PHASE   113
> +#define SCLK_SDIO0_DRV_PHASE   114
> +#define SCLK_SDIO1_DRV_PHASE   115
> +#define SCLK_EMMC_DRV_PHASE    116
> +
> +#define SCLK_SDMMC_SAMPLE_PHASE        117
> +#define SCLK_SDIO0_SAMPLE_PHASE        118
> +#define SCLK_SDIO1_SAMPLE_PHASE        119
> +#define SCLK_EMMC_SAMPLE_PHASE 120
> +

Thinking about Mike T's comment, we might want to actually rename
these defines and just remove the "_PHASE".  These clocks _are_ the
drive and sample clocks.  Sure, they happen to have phases as one of
the attributes, but they aren't "phase clocks".

FYI, the TRM shows these clocks as:

- Into "clkgen" you see cclkin (the *2 clock)
- Out of "clkgen" you see:
  --> "cclk_in" (the non *2 clock)
  --> "cclk_in_drv" (the drive clock, which might be shifted)
  --> "cclk_in_sample" (the sample clock, which might be shifted)

Right now we're not modeling the non-shifted, non *2 clock.  I think
that's OK (otherwise we've got a bunch of old device trees to change,
since the old DTS files passed in the *2 clock as the card clock).


-Doug

^ permalink raw reply	[flat|nested] 10+ messages in thread

* Re: [PATCH 1/2] clk: rockchip: add bindings for the mmc clock phases
@ 2014-11-18 17:44     ` Doug Anderson
  0 siblings, 0 replies; 10+ messages in thread
From: Doug Anderson @ 2014-11-18 17:44 UTC (permalink / raw)
  To: Alexandru M Stan
  Cc: Heiko Stuebner, addy ke, Sonny Rao, Kever Yang, Rob Herring,
	Pawel Moll, Mark Rutland, Ian Campbell, Kumar Gala,
	Mike Turquette, Mark yao, devicetree-u79uwXL29TY76Z2rM5mHXA,
	linux-kernel-u79uwXL29TY76Z2rM5mHXA

Hi,

On Fri, Nov 14, 2014 at 2:52 PM, Alexandru M Stan <amstan-F7+t8E8rja9g9hUCZPvPmw@public.gmane.org> wrote:
> This will be used in a later patch for clock phase tuning.
>
> Suggested-by: Heiko Stuebner <heiko-4mtYJXux2i+zQB+pC5nmwQ@public.gmane.org>
> Signed-off-by: Alexandru M Stan <amstan-F7+t8E8rja9g9hUCZPvPmw@public.gmane.org>
> ---
>  include/dt-bindings/clock/rk3288-cru.h | 10 ++++++++++
>  1 file changed, 10 insertions(+)
>
> diff --git a/include/dt-bindings/clock/rk3288-cru.h b/include/dt-bindings/clock/rk3288-cru.h
> index 100a08c..465d0f6 100644
> --- a/include/dt-bindings/clock/rk3288-cru.h
> +++ b/include/dt-bindings/clock/rk3288-cru.h
> @@ -72,6 +72,16 @@
>  #define SCLK_HEVC_CABAC                111
>  #define SCLK_HEVC_CORE         112
>
> +#define SCLK_SDMMC_DRV_PHASE   113
> +#define SCLK_SDIO0_DRV_PHASE   114
> +#define SCLK_SDIO1_DRV_PHASE   115
> +#define SCLK_EMMC_DRV_PHASE    116
> +
> +#define SCLK_SDMMC_SAMPLE_PHASE        117
> +#define SCLK_SDIO0_SAMPLE_PHASE        118
> +#define SCLK_SDIO1_SAMPLE_PHASE        119
> +#define SCLK_EMMC_SAMPLE_PHASE 120
> +

Thinking about Mike T's comment, we might want to actually rename
these defines and just remove the "_PHASE".  These clocks _are_ the
drive and sample clocks.  Sure, they happen to have phases as one of
the attributes, but they aren't "phase clocks".

FYI, the TRM shows these clocks as:

- Into "clkgen" you see cclkin (the *2 clock)
- Out of "clkgen" you see:
  --> "cclk_in" (the non *2 clock)
  --> "cclk_in_drv" (the drive clock, which might be shifted)
  --> "cclk_in_sample" (the sample clock, which might be shifted)

Right now we're not modeling the non-shifted, non *2 clock.  I think
that's OK (otherwise we've got a bunch of old device trees to change,
since the old DTS files passed in the *2 clock as the card clock).


-Doug
--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply	[flat|nested] 10+ messages in thread

end of thread, other threads:[~2014-11-18 17:44 UTC | newest]

Thread overview: 10+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2014-11-14 22:52 [PATCH 0/2] For now all I have is the getter and setter for the phase, nothing that uses it Alexandru M Stan
2014-11-14 22:52 ` Alexandru M Stan
2014-11-14 22:52 ` Alexandru M Stan
2014-11-14 22:52 ` [PATCH 1/2] clk: rockchip: add bindings for the mmc clock phases Alexandru M Stan
2014-11-18 17:44   ` Doug Anderson
2014-11-18 17:44     ` Doug Anderson
2014-11-14 22:52 ` [PATCH 2/2] clk: rockchip: Add support for the mmc clock phases using the framework Alexandru M Stan
2014-11-14 22:52   ` Alexandru M Stan
2014-11-14 23:43   ` Heiko Stübner
2014-11-14 23:43     ` Heiko Stübner

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.