* [PATCH RFC 1/3] clk: berlin: add clock tree driver for BG2/BG2CD
[not found] ` <1399580212-30183-1-git-send-email-sebastian.hesselbarth-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org>
@ 2014-05-08 20:16 ` Sebastian Hesselbarth
2014-05-08 20:16 ` [PATCH RFC 3/3] clk: berlin: add driver for BG2x simple PLLs Sebastian Hesselbarth
2014-05-10 16:13 ` [PATCH RFC 0/3] Berlin SoC clock tree - DT or not DT? Sebastian Hesselbarth
2 siblings, 0 replies; 4+ messages in thread
From: Sebastian Hesselbarth @ 2014-05-08 20:16 UTC (permalink / raw)
To: Sebastian Hesselbarth
Cc: Mike Turquette, Alexandre Belloni, Antoine Tenart, Jason Cooper,
Andrew Lunn, Gregory Clement, Thomas Petazzoni,
devicetree-u79uwXL29TY76Z2rM5mHXA,
linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
linux-kernel-u79uwXL29TY76Z2rM5mHXA
This is a driver beast dealing with the clock tree of Berlin BG2
and BG2CD SoCs.
The include already contains structs for PLL and DIV, send in the
two following patches.
Signed-off-by: Sebastian Hesselbarth <sebastian.hesselbarth-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org>
---
Cc: Mike Turquette <mturquette-QSEj5FYQhm4dnm+yROfE0A@public.gmane.org>
Cc: Alexandre Belloni <alexandre.belloni-wi1+55ScJUtKEb57/3fJTNBPR1lH4CV8@public.gmane.org>
Cc: Antoine Tenart <antoine.tenart-wi1+55ScJUtKEb57/3fJTNBPR1lH4CV8@public.gmane.org>
Cc: Jason Cooper <jason-NLaQJdtUoK4Be96aLqz0jA@public.gmane.org>
Cc: Andrew Lunn <andrew-g2DYL2Zd6BY@public.gmane.org>
Cc: Gregory Clement <gregory.clement-wi1+55ScJUtKEb57/3fJTNBPR1lH4CV8@public.gmane.org>
Cc: Thomas Petazzoni <thomas.petazzoni-wi1+55ScJUtKEb57/3fJTNBPR1lH4CV8@public.gmane.org>
Cc: devicetree-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
Cc: linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r@public.gmane.org
Cc: linux-kernel-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
---
drivers/clk/berlin/bg2.c | 615 ++++++++++++++++++++++++++++++++++++++++++++
drivers/clk/berlin/common.h | 124 +++++++++
2 files changed, 739 insertions(+)
create mode 100644 drivers/clk/berlin/bg2.c
create mode 100644 drivers/clk/berlin/common.h
diff --git a/drivers/clk/berlin/bg2.c b/drivers/clk/berlin/bg2.c
new file mode 100644
index 000000000000..c6eb4e180ca7
--- /dev/null
+++ b/drivers/clk/berlin/bg2.c
@@ -0,0 +1,615 @@
+/*
+ * Copyright (c) 2014 Marvell Technology Group Ltd.
+ *
+ * Alexandre Belloni <alexandre.belloni-wi1+55ScJUtKEb57/3fJTNBPR1lH4CV8@public.gmane.org>
+ * Sebastian Hesselbarth <sebastian.hesselbarth-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope 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.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <linux/clk.h>
+#include <linux/clk-provider.h>
+#include <linux/io.h>
+#include <linux/kernel.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
+#include <linux/slab.h>
+
+#include "common.h"
+
+/*
+ * BG2/BG2CD SoCs have the following audio/video I/O units:
+ *
+ * audiohd: HDMI TX audio
+ * audio0: 7.1ch TX
+ * audio1: 2ch TX
+ * audio2: 2ch RX
+ * audio3: SPDIF TX
+ * video0: HDMI video
+ * video1: Secondary video
+ * video2: SD auxiliary video
+ *
+ * There are no external audio clocks (ACLKI0, ACLKI1) and
+ * only one external video clock (VCLKI0).
+ *
+ * Currently missing bits and pieces:
+ * - audio_fast_pll is unknown
+ * - audiohd_pll is unknown
+ * - video0_pll is unknown
+ * - audio[023], audiohd parent pll is assumed to be audio_fast_pll
+ * - pll bypass switches are ignored
+ *
+ * To do:
+ * - avpll clock driver
+ *
+ */
+
+/* PLL/Clock registers start at 0x0014 of Global Register base */
+#define GLOBAL_OFFSET(x) ((x) - 0x0014)
+
+#define REG_SYSPLL GLOBAL_OFFSET(0x0014)
+#define REG_MEMPLL GLOBAL_OFFSET(0x0028)
+#define REG_CPUPLL GLOBAL_OFFSET(0x003c)
+
+#define REG_CLKENABLE GLOBAL_OFFSET(0x0150)
+#define REG_CLKSELECT0 GLOBAL_OFFSET(0x0154)
+#define REG_CLKSELECT1 GLOBAL_OFFSET(0x0158)
+#define REG_CLKSELECT2 GLOBAL_OFFSET(0x015c)
+#define REG_CLKSELECT3 GLOBAL_OFFSET(0x0160)
+#define REG_CLKSWITCH0 GLOBAL_OFFSET(0x0164)
+#define REG_CLKSWITCH1 GLOBAL_OFFSET(0x0168)
+
+#define REG_GFX3D_CORE GLOBAL_OFFSET(0x022c)
+#define REG_GFX3D_SYS GLOBAL_OFFSET(0x0230)
+#define REG_ARC GLOBAL_OFFSET(0x0234)
+#define REG_VIP GLOBAL_OFFSET(0x0238)
+#define REG_SDIO0XIN GLOBAL_OFFSET(0x023c)
+#define REG_SDIO1XIN GLOBAL_OFFSET(0x0240)
+#define REG_GFX3D_EXTRA GLOBAL_OFFSET(0x0244)
+
+#define REG_GC360 GLOBAL_OFFSET(0x024c)
+#define REG_SDIO_DLLMST GLOBAL_OFFSET(0x0250)
+
+static const struct berlin2_pll_map bg2_pll_map __initdata = {
+ .vcodiv = {10, 15, 20, 25, 30, 40, 50, 60, 80},
+ .mult = 10,
+ .fbdiv_shift = 6,
+ .rfdiv_shift = 1,
+ .divsel_shift = 7,
+};
+
+static const struct berlin2_pll_data bg2_plls[] __initdata = {
+ { .name = "syspll", .offset = REG_SYSPLL, },
+ { .name = "mempll", .offset = REG_MEMPLL, },
+ { .name = "cpupll", .offset = REG_CPUPLL, },
+};
+
+static const char *audio1_pll_mux[] __initconst = { "avpll_b3", "avpll_a3" };
+static const char *video1_pll_mux[] __initconst = { "avpll_a2", "avpll_b2" };
+static const char *video2_pll_mux[] __initconst = { "avpll_b1", "avpll_a5" };
+
+static const char *video0_in_mux[] __initconst = { "video0_pll", "video_ext0" };
+static const char *video1_in_mux[] __initconst = { "video1_pll", "video_ext0" };
+static const char *video2_in_mux[] __initconst = { "video2_pll", "video_ext0" };
+
+static const struct berlin2_mux_data bg2_muxes[] __initdata = {
+ /*
+ * CLKSELECT2
+ *
+ * AUDIO_FAST_EXT, bit 15: 0 = ACLKI0, 1 = ACLKI1
+ * AUDIO_FAST_IN, bit 16: 0 = Audio Fast PLL, 1 = Audio Fast Ext
+ * -> always selects Audio Fast PLL because of missing external clocks
+ *
+ * AUDIOHD_EXT, bit 26: 0 = ACLKI0, 1 = ACLKI1
+ * AUDIOHD_IN, bit 27: 0 = AudioHD PLL, 1 = AudioHD Ext
+ * -> always selects AudioHD PLL because of missing external clocks
+ *
+ * AUDIO1_EXT, bit 28: 0 = ACLKI0, 1 = ACLKI1
+ * AUDIO1_IN, bit 30: 0 = Audio1 PLL, 1 = Audio1 Ext
+ * -> always selects Audio1 PLL because of missing external clocks
+ */
+ {
+ .name = "audio1_pll",
+ .offset = REG_CLKSELECT2, .shift = 29, .width = 1,
+ .parent_names = audio1_pll_mux,
+ .num_parents = ARRAY_SIZE(audio1_pll_mux),
+ },
+ /*
+ * CLKSELECT3
+ *
+ * VIDEO0_EXT, bit 3: 0 = VCLKI0, 1 = VCLKI1
+ * VIDEO0_IN, bit 4: 0 = Video0 PLL, 1 = Video0 Ext
+ * -> always selects Video0 PLL or VCLKI0 because of missing VCLKI1
+ *
+ * VIDEO1_EXT, bit 5: 0 = VCLKI0, 1 = VCLKI1
+ * VIDEO1_IN, bit 6: 0 = Video1 PLL, 1 = Video1 Ext
+ * -> always selects Video1 PLL or VCLKI0 because of missing VCLKI1
+ *
+ * VIDEO2_EXT, bit 8: 0 = VCLKI0, 1 = VCLKI1
+ * VIDEO2_IN, bit 9: 0 = Video2 PLL, 1 = Video2 Ext
+ * -> always selects Video2 PLL or VCLKI0 because of missing VCLKI1
+ *
+ */
+ {
+ .name = "video0_in",
+ .offset = REG_CLKSELECT3, .shift = 4, .width = 1,
+ .parent_names = video0_in_mux,
+ .num_parents = ARRAY_SIZE(video0_in_mux),
+ },
+ {
+ .name = "video1_in",
+ .offset = REG_CLKSELECT3, .shift = 6, .width = 1,
+ .parent_names = video1_in_mux,
+ .num_parents = ARRAY_SIZE(video1_in_mux),
+ },
+ {
+ .name = "video1_pll",
+ .offset = REG_CLKSELECT3, .shift = 7, .width = 1,
+ .parent_names = video1_pll_mux,
+ .num_parents = ARRAY_SIZE(video1_pll_mux),
+ },
+ {
+ .name = "video2_in",
+ .offset = REG_CLKSELECT3, .shift = 9, .width = 1,
+ .parent_names = video2_in_mux,
+ .num_parents = ARRAY_SIZE(video2_in_mux),
+ },
+ {
+ .name = "video2_pll",
+ .offset = REG_CLKSELECT3, .shift = 10, .width = 1,
+ .parent_names = video2_pll_mux,
+ .num_parents = ARRAY_SIZE(video2_pll_mux),
+ },
+};
+
+static const char *default_div_sel[] __initconst = {
+ "syspll", "avpll_b4", "avpll_b5", "avpll_b6", "avpll_b7",
+};
+
+static const char *cpu_div_sel[] __initconst = {
+ "cpupll", "avpll_b4", "avpll_b5", "avpll_b6", "avpll_b7", "mempll",
+};
+
+static const char *audio_fast_div_sel[] __initconst = { "audio_fast_pll" };
+static const char *audio1_div_sel[] __initconst = { "audio1_pll" };
+
+static const struct berlin2_div_data bg2_divs[] __initdata = {
+ {
+ .name = "sys",
+ .parent_names = default_div_sel,
+ .num_parents = ARRAY_SIZE(default_div_sel),
+ .map = {
+ BERLIN2_DIV_GATE(REG_CLKENABLE, 0),
+ BERLIN2_PLL_SELECT(REG_CLKSELECT0, 0),
+ BERLIN2_DIV_SELECT(REG_CLKSELECT0, 3),
+ BERLIN2_PLL_SWITCH(REG_CLKSWITCH0, 3),
+ BERLIN2_DIV_SWITCH(REG_CLKSWITCH0, 4),
+ BERLIN2_DIV_D3SWITCH(REG_CLKSWITCH0, 5),
+ },
+ .flags = CLK_IGNORE_UNUSED,
+ },
+ {
+ .name = "cpu",
+ .parent_names = cpu_div_sel,
+ .num_parents = ARRAY_SIZE(cpu_div_sel),
+ .map = {
+ BERLIN2_PLL_SELECT(REG_CLKSELECT0, 6),
+ BERLIN2_DIV_SELECT(REG_CLKSELECT0, 9),
+ BERLIN2_PLL_SWITCH(REG_CLKSWITCH0, 6),
+ BERLIN2_DIV_SWITCH(REG_CLKSWITCH0, 7),
+ BERLIN2_DIV_D3SWITCH(REG_CLKSWITCH0, 8),
+ },
+ },
+ {
+ .name = "drmfigo",
+ .parent_names = default_div_sel,
+ .num_parents = ARRAY_SIZE(default_div_sel),
+ .map = {
+ BERLIN2_DIV_GATE(REG_CLKENABLE, 16),
+ BERLIN2_PLL_SELECT(REG_CLKSELECT0, 17),
+ BERLIN2_DIV_SELECT(REG_CLKSELECT0, 20),
+ BERLIN2_PLL_SWITCH(REG_CLKSWITCH0, 12),
+ BERLIN2_DIV_SWITCH(REG_CLKSWITCH0, 13),
+ BERLIN2_DIV_D3SWITCH(REG_CLKSWITCH0, 14),
+ },
+ },
+ {
+ .name = "cfg",
+ .parent_names = default_div_sel,
+ .num_parents = ARRAY_SIZE(default_div_sel),
+ .map = {
+ BERLIN2_DIV_GATE(REG_CLKENABLE, 1),
+ BERLIN2_PLL_SELECT(REG_CLKSELECT0, 23),
+ BERLIN2_DIV_SELECT(REG_CLKSELECT0, 26),
+ BERLIN2_PLL_SWITCH(REG_CLKSWITCH0, 15),
+ BERLIN2_DIV_SWITCH(REG_CLKSWITCH0, 16),
+ BERLIN2_DIV_D3SWITCH(REG_CLKSWITCH0, 17),
+ },
+ },
+ {
+ .name = "gfx",
+ .parent_names = default_div_sel,
+ .num_parents = ARRAY_SIZE(default_div_sel),
+ .map = {
+ BERLIN2_DIV_GATE(REG_CLKENABLE, 4),
+ BERLIN2_PLL_SELECT(REG_CLKSELECT0, 29),
+ BERLIN2_DIV_SELECT(REG_CLKSELECT1, 0),
+ BERLIN2_PLL_SWITCH(REG_CLKSWITCH0, 18),
+ BERLIN2_DIV_SWITCH(REG_CLKSWITCH0, 19),
+ BERLIN2_DIV_D3SWITCH(REG_CLKSWITCH0, 20),
+ },
+ },
+ {
+ .name = "zsp",
+ .parent_names = default_div_sel,
+ .num_parents = ARRAY_SIZE(default_div_sel),
+ .map = {
+ BERLIN2_DIV_GATE(REG_CLKENABLE, 5),
+ BERLIN2_PLL_SELECT(REG_CLKSELECT1, 3),
+ BERLIN2_DIV_SELECT(REG_CLKSELECT1, 6),
+ BERLIN2_PLL_SWITCH(REG_CLKSWITCH0, 21),
+ BERLIN2_DIV_SWITCH(REG_CLKSWITCH0, 22),
+ BERLIN2_DIV_D3SWITCH(REG_CLKSWITCH0, 23),
+ },
+ },
+ {
+ .name = "perif",
+ .parent_names = default_div_sel,
+ .num_parents = ARRAY_SIZE(default_div_sel),
+ .map = {
+ BERLIN2_DIV_GATE(REG_CLKENABLE, 6),
+ BERLIN2_PLL_SELECT(REG_CLKSELECT1, 9),
+ BERLIN2_DIV_SELECT(REG_CLKSELECT1, 12),
+ BERLIN2_PLL_SWITCH(REG_CLKSWITCH0, 24),
+ BERLIN2_DIV_SWITCH(REG_CLKSWITCH0, 25),
+ BERLIN2_DIV_D3SWITCH(REG_CLKSWITCH0, 26),
+ },
+ .flags = CLK_IGNORE_UNUSED,
+ },
+ {
+ .name = "pcube",
+ .parent_names = default_div_sel,
+ .num_parents = ARRAY_SIZE(default_div_sel),
+ .map = {
+ BERLIN2_DIV_GATE(REG_CLKENABLE, 2),
+ BERLIN2_PLL_SELECT(REG_CLKSELECT1, 15),
+ BERLIN2_DIV_SELECT(REG_CLKSELECT1, 18),
+ BERLIN2_PLL_SWITCH(REG_CLKSWITCH0, 27),
+ BERLIN2_DIV_SWITCH(REG_CLKSWITCH0, 28),
+ BERLIN2_DIV_D3SWITCH(REG_CLKSWITCH0, 29),
+ },
+ },
+ {
+ .name = "vscope",
+ .parent_names = default_div_sel,
+ .num_parents = ARRAY_SIZE(default_div_sel),
+ .map = {
+ BERLIN2_DIV_GATE(REG_CLKENABLE, 3),
+ BERLIN2_PLL_SELECT(REG_CLKSELECT1, 21),
+ BERLIN2_DIV_SELECT(REG_CLKSELECT1, 24),
+ BERLIN2_PLL_SWITCH(REG_CLKSWITCH0, 30),
+ BERLIN2_DIV_SWITCH(REG_CLKSWITCH0, 31),
+ BERLIN2_DIV_D3SWITCH(REG_CLKSWITCH1, 0),
+ },
+ },
+ {
+ .name = "nfc_ecc",
+ .parent_names = default_div_sel,
+ .num_parents = ARRAY_SIZE(default_div_sel),
+ .map = {
+ BERLIN2_DIV_GATE(REG_CLKENABLE, 18),
+ BERLIN2_PLL_SELECT(REG_CLKSELECT1, 27),
+ BERLIN2_DIV_SELECT(REG_CLKSELECT2, 0),
+ BERLIN2_PLL_SWITCH(REG_CLKSWITCH1, 1),
+ BERLIN2_DIV_SWITCH(REG_CLKSWITCH1, 2),
+ BERLIN2_DIV_D3SWITCH(REG_CLKSWITCH1, 3),
+ },
+ },
+ {
+ .name = "vpp",
+ .parent_names = default_div_sel,
+ .num_parents = ARRAY_SIZE(default_div_sel),
+ .map = {
+ BERLIN2_DIV_GATE(REG_CLKENABLE, 21),
+ BERLIN2_PLL_SELECT(REG_CLKSELECT2, 3),
+ BERLIN2_DIV_SELECT(REG_CLKSELECT2, 6),
+ BERLIN2_PLL_SWITCH(REG_CLKSWITCH1, 4),
+ BERLIN2_DIV_SWITCH(REG_CLKSWITCH1, 5),
+ BERLIN2_DIV_D3SWITCH(REG_CLKSWITCH1, 6),
+ },
+ },
+ {
+ .name = "app",
+ .parent_names = default_div_sel,
+ .num_parents = ARRAY_SIZE(default_div_sel),
+ .map = {
+ BERLIN2_DIV_GATE(REG_CLKENABLE, 20),
+ BERLIN2_PLL_SELECT(REG_CLKSELECT2, 9),
+ BERLIN2_DIV_SELECT(REG_CLKSELECT2, 12),
+ BERLIN2_PLL_SWITCH(REG_CLKSWITCH1, 7),
+ BERLIN2_DIV_SWITCH(REG_CLKSWITCH1, 8),
+ BERLIN2_DIV_D3SWITCH(REG_CLKSWITCH1, 9),
+ },
+ },
+ {
+ .name = "audio0",
+ .parent_names = audio_fast_div_sel,
+ .num_parents = ARRAY_SIZE(audio_fast_div_sel),
+ .map = {
+ BERLIN2_DIV_GATE(REG_CLKENABLE, 22),
+ BERLIN2_DIV_SELECT(REG_CLKSELECT2, 17),
+ BERLIN2_DIV_SWITCH(REG_CLKSWITCH1, 10),
+ BERLIN2_DIV_D3SWITCH(REG_CLKSWITCH1, 11),
+ },
+ },
+ {
+ .name = "audio2",
+ .parent_names = audio_fast_div_sel,
+ .num_parents = ARRAY_SIZE(audio_fast_div_sel),
+ .map = {
+ BERLIN2_DIV_GATE(REG_CLKENABLE, 24),
+ BERLIN2_DIV_SELECT(REG_CLKSELECT2, 20),
+ BERLIN2_DIV_SWITCH(REG_CLKSWITCH1, 14),
+ BERLIN2_DIV_D3SWITCH(REG_CLKSWITCH1, 15),
+ },
+ },
+ {
+ .name = "audio3",
+ .parent_names = audio_fast_div_sel,
+ .num_parents = ARRAY_SIZE(audio_fast_div_sel),
+ .map = {
+ BERLIN2_DIV_GATE(REG_CLKENABLE, 25),
+ BERLIN2_DIV_SELECT(REG_CLKSELECT2, 23),
+ BERLIN2_DIV_SWITCH(REG_CLKSWITCH1, 16),
+ BERLIN2_DIV_D3SWITCH(REG_CLKSWITCH1, 17),
+ },
+ },
+ {
+ .name = "audio1",
+ .parent_names = audio1_div_sel,
+ .num_parents = ARRAY_SIZE(audio1_div_sel),
+ .map = {
+ BERLIN2_DIV_GATE(REG_CLKENABLE, 23),
+ BERLIN2_DIV_SELECT(REG_CLKSELECT3, 0),
+ BERLIN2_DIV_SWITCH(REG_CLKSWITCH1, 12),
+ BERLIN2_DIV_D3SWITCH(REG_CLKSWITCH1, 13),
+ },
+ },
+ {
+ .name = "gfx3d_core",
+ .parent_names = default_div_sel,
+ .num_parents = ARRAY_SIZE(default_div_sel),
+ .map = { BERLIN2_SINGLE_DIV(REG_GFX3D_CORE) },
+ },
+ {
+ .name = "gfx3d_sys",
+ .parent_names = default_div_sel,
+ .num_parents = ARRAY_SIZE(default_div_sel),
+ .map = { BERLIN2_SINGLE_DIV(REG_GFX3D_SYS) },
+ },
+ {
+ .name = "arc",
+ .parent_names = default_div_sel,
+ .num_parents = ARRAY_SIZE(default_div_sel),
+ .map = { BERLIN2_SINGLE_DIV(REG_ARC) },
+ },
+ {
+ .name = "vip",
+ .parent_names = default_div_sel,
+ .num_parents = ARRAY_SIZE(default_div_sel),
+ .map = { BERLIN2_SINGLE_DIV(REG_VIP) },
+ },
+ {
+ .name = "sdio0xin",
+ .parent_names = default_div_sel,
+ .num_parents = ARRAY_SIZE(default_div_sel),
+ .map = { BERLIN2_SINGLE_DIV(REG_SDIO0XIN) },
+ },
+ {
+ .name = "sdio1xin",
+ .parent_names = default_div_sel,
+ .num_parents = ARRAY_SIZE(default_div_sel),
+ .map = { BERLIN2_SINGLE_DIV(REG_SDIO1XIN) },
+ },
+ {
+ .name = "gfx3d_extra",
+ .parent_names = default_div_sel,
+ .num_parents = ARRAY_SIZE(default_div_sel),
+ .map = { BERLIN2_SINGLE_DIV(REG_GFX3D_EXTRA) },
+ },
+ {
+ .name = "gc360",
+ .parent_names = default_div_sel,
+ .num_parents = ARRAY_SIZE(default_div_sel),
+ .map = { BERLIN2_SINGLE_DIV(REG_GC360) },
+ },
+ {
+ .name = "sdio_dllmst",
+ .parent_names = default_div_sel,
+ .num_parents = ARRAY_SIZE(default_div_sel),
+ .map = { BERLIN2_SINGLE_DIV(REG_SDIO_DLLMST) },
+ },
+};
+
+static const struct berlin2_gate_data bg2_gates[] __initdata = {
+ { "geth0", "perif", 7 },
+ { "geth1", "perif", 8 },
+ { "sata", "perif", 9 },
+ { "ahbapb", "perif", 10, CLK_IGNORE_UNUSED },
+ { "usb0", "perif", 11 },
+ { "usb1", "perif", 12 },
+ { "pbridge", "perif", 13, CLK_IGNORE_UNUSED },
+ { "sdio0", "perif", 14 },
+ { "sdio1", "perif", 15 },
+ { "nfc", "perif", 17 },
+ { "smemc", "perif", 19 },
+ { "audiohd", "audiohd_pll", 26 },
+ { "video0", "video0_in", 27 },
+ { "video1", "video1_in", 28 },
+ { "video2", "video2_in", 29 },
+};
+
+static const char *bg2_clocks[] __initconst = {
+ [0] = "ahbapb",
+ [1] = "app",
+ [2] = "arc",
+ [3] = "audio0",
+ [4] = "audio1",
+ [5] = "audio2",
+ [6] = "audio3",
+ [7] = "audiohd",
+ [8] = "cfg",
+ [9] = "cpu",
+ [10] = "drmfigo",
+ [11] = "gc360",
+ [12] = "geth0",
+ [13] = "geth1",
+ [14] = "gfx",
+ [15] = "gfx3d_core",
+ [16] = "gfx3d_extra",
+ [17] = "gfx3d_sys",
+ [18] = "nfc",
+ [19] = "nfc_ecc",
+ [20] = "pbridge",
+ [21] = "perif",
+ [22] = "pcube",
+ [23] = "sata",
+ [24] = "sdio_dllmst",
+ [25] = "sdio0",
+ [26] = "sdio0xin",
+ [27] = "sdio1",
+ [28] = "sdio1xin",
+ [29] = "smemc",
+ [30] = "sys",
+ [31] = "usb0",
+ [32] = "usb1",
+ [33] = "video0",
+ [34] = "video1",
+ [35] = "video2",
+ [36] = "vip",
+ [37] = "vpp",
+ [38] = "vscope",
+ [39] = "zsp",
+};
+
+static spinlock_t lock;
+static struct clk_onecell_data clk_data;
+
+static void __init bg2_clock_tree_setup(struct device_node *np)
+{
+ void __iomem *base;
+ struct clk *iclk;
+ const char *refclk_name;
+ int n;
+
+ clk_data.clk_num = ARRAY_SIZE(bg2_clocks);
+ clk_data.clks = kzalloc(clk_data.clk_num * sizeof(struct clk *),
+ GFP_KERNEL);
+ if (!clk_data.clks)
+ return;
+
+ base = of_iomap(np, 0);
+ if (!base) {
+ pr_err("Unable to map clock tree register base\n");
+ kfree(clk_data.clks);
+ return;
+ }
+
+ iclk = of_clk_get_by_name(np, "refclk");
+ if (IS_ERR(iclk)) {
+ pr_err("Missing reference clock\n");
+ iounmap(base);
+ kfree(clk_data.clks);
+ return;
+ }
+
+ refclk_name = __clk_get_name(iclk);
+ clk_put(iclk);
+ spin_lock_init(&lock);
+
+ /* register clk alias for optional external video clock */
+ iclk = of_clk_get_by_name(np, "video_ext0");
+ if (!IS_ERR(iclk)) {
+ clk_add_alias(__clk_get_name(iclk), NULL, "video_ext0", NULL);
+ clk_put(iclk);
+ }
+
+ /* Standard PLLs */
+ for (n = 0; n < ARRAY_SIZE(bg2_plls); n++) {
+ const struct berlin2_pll_data *data = &bg2_plls[n];
+ struct clk *pll;
+
+ pll = berlin2_pll_register(&bg2_pll_map, base + data->offset,
+ data->name, refclk_name, data->flags);
+ if (IS_ERR(pll))
+ pr_err("Failed to register pll %s\n", data->name);
+ }
+
+ /* AV PLL */
+ clk_register_fixed_rate(NULL, "avpll_a2", NULL, 0, 648000000);
+ clk_register_fixed_rate(NULL, "avpll_a3", NULL, 0, 648000000);
+ clk_register_fixed_rate(NULL, "avpll_a5", NULL, 0, 648000000);
+
+ clk_register_fixed_rate(NULL, "avpll_b1", NULL, 0, 648000000);
+ clk_register_fixed_rate(NULL, "avpll_b2", NULL, 0, 648000000);
+ clk_register_fixed_rate(NULL, "avpll_b3", NULL, 0, 648000000);
+ clk_register_fixed_rate(NULL, "avpll_b4", NULL, 0, 648000000);
+ clk_register_fixed_rate(NULL, "avpll_b5", NULL, 0, 648000000);
+ clk_register_fixed_rate(NULL, "avpll_b6", NULL, 0, 648000000);
+ clk_register_fixed_rate(NULL, "avpll_b7", NULL, 0, 648000000);
+
+ /* clock mux cells */
+ for (n = 0; n < ARRAY_SIZE(bg2_muxes); n++) {
+ const struct berlin2_mux_data *data = &bg2_muxes[n];
+ struct clk *mux;
+
+ mux = clk_register_mux(NULL, data->name, data->parent_names,
+ data->num_parents, data->flags,
+ base + data->offset, data->shift,
+ data->width, 0, &lock);
+ if (IS_ERR(mux))
+ pr_err("Failed to register mux %s\n", data->name);
+ }
+
+ /* clock divider cells */
+ for (n = 0; n < ARRAY_SIZE(bg2_divs); n++) {
+ const struct berlin2_div_data *data = &bg2_divs[n];
+ struct clk *div;
+
+ div = berlin2_div_register(&data->map, base, data->name,
+ data->parent_names, data->num_parents,
+ data->flags, &lock);
+ if (IS_ERR(div))
+ pr_err("Failed to register div %s\n", data->name);
+ }
+
+ /* clock gate cells */
+ for (n = 0; n < ARRAY_SIZE(bg2_gates); n++) {
+ const struct berlin2_gate_data *data = &bg2_gates[n];
+ struct clk *gate;
+
+ gate = clk_register_gate(NULL, data->name, data->parent_name,
+ data->flags, base + REG_CLKENABLE,
+ data->bit_idx, 0, &lock);
+ if (IS_ERR(gate))
+ pr_err("Failed to register gate %s\n", data->name);
+ }
+
+ /* register clk-provider */
+ for (n = 0; n < ARRAY_SIZE(bg2_clocks); n++)
+ clk_data.clks[n] = __clk_lookup(bg2_clocks[n]);
+
+ of_clk_add_provider(np, of_clk_src_onecell_get, &clk_data);
+}
+CLK_OF_DECLARE(bg2_clktree, "marvell,berlin2-clock-tree",
+ bg2_clock_tree_setup);
diff --git a/drivers/clk/berlin/common.h b/drivers/clk/berlin/common.h
new file mode 100644
index 000000000000..1d8f179d9690
--- /dev/null
+++ b/drivers/clk/berlin/common.h
@@ -0,0 +1,124 @@
+/*
+ * Copyright (c) 2014 Marvell Technology Group Ltd.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope 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.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+#ifndef __BERLIN_CLK_COMMON_H
+#define __BERLIN_CLK_COMMON_H
+
+#include <linux/clk.h>
+
+struct berlin2_pll_map {
+ const u8 vcodiv[16];
+ u8 mult;
+ u8 fbdiv_shift;
+ u8 rfdiv_shift;
+ u8 divsel_shift;
+};
+
+struct berlin2_pll_data {
+ const char *name;
+ u32 offset;
+ unsigned long flags;
+};
+
+struct berlin2_div_map {
+ u16 pll_select_offs;
+ u16 pll_switch_offs;
+ u16 div_select_offs;
+ u16 div_switch_offs;
+ u16 div3_switch_offs;
+ u16 gate_offs;
+ u8 pll_select_shift;
+ u8 pll_switch_shift;
+ u8 div_select_shift;
+ u8 div_switch_shift;
+ u8 div3_switch_shift;
+ u8 gate_shift;
+ u8 has_pll_select:1;
+ u8 has_pll_switch:1;
+ u8 has_gate:1;
+};
+
+struct berlin2_div_data {
+ const char *name;
+ const char **parent_names;
+ int num_parents;
+ struct berlin2_div_map map;
+ unsigned long flags;
+};
+
+struct berlin2_mux_data {
+ const char *name;
+ const char **parent_names;
+ int num_parents;
+ u16 offset;
+ u8 shift;
+ u8 width;
+ unsigned long flags;
+};
+
+struct berlin2_gate_data {
+ const char *name;
+ const char *parent_name;
+ u8 bit_idx;
+ unsigned long flags;
+};
+
+#define BERLIN2_PLL_SELECT(_off, _sh) \
+ .pll_select_offs = _off, \
+ .pll_select_shift = _sh, \
+ .has_pll_select = 1
+
+#define BERLIN2_PLL_SWITCH(_off, _sh) \
+ .pll_switch_offs = _off, \
+ .pll_switch_shift = _sh, \
+ .has_pll_switch = 1
+
+#define BERLIN2_DIV_SELECT(_off, _sh) \
+ .div_select_offs = _off, \
+ .div_select_shift = _sh \
+
+#define BERLIN2_DIV_SWITCH(_off, _sh) \
+ .div_switch_offs = _off, \
+ .div_switch_shift = _sh
+
+#define BERLIN2_DIV_D3SWITCH(_off, _sh) \
+ .div3_switch_offs = _off, \
+ .div3_switch_shift = _sh
+
+#define BERLIN2_DIV_GATE(_off, _sh) \
+ .gate_offs = _off, \
+ .gate_shift = _sh, \
+ .has_gate = 1
+
+#define BERLIN2_SINGLE_DIV(_reg) \
+ BERLIN2_DIV_GATE(_reg, 0), \
+ BERLIN2_PLL_SELECT(_reg, 1), \
+ BERLIN2_PLL_SWITCH(_reg, 4), \
+ BERLIN2_DIV_SWITCH(_reg, 5), \
+ BERLIN2_DIV_D3SWITCH(_reg, 6), \
+ BERLIN2_DIV_SELECT(_reg, 7)
+
+struct clk * __init
+berlin2_pll_register(const struct berlin2_pll_map *map,
+ void __iomem *base, const char *name,
+ const char *parent_name, unsigned long flags);
+
+struct clk * __init
+berlin2_div_register(const struct berlin2_div_map *map,
+ void __iomem *base, const char *name,
+ const char **parent_names, int num_parents,
+ unsigned long flags, spinlock_t *lock);
+
+#endif /* BERLIN_CLK_COMMON_H */
--
1.9.1
--
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 related [flat|nested] 4+ messages in thread
* [PATCH RFC 2/3] clk: berlin: add driver for BG2x complex divider cells
[not found] <1399580212-30183-1-git-send-email-sebastian.hesselbarth@gmail.com>
@ 2014-05-08 20:16 ` Sebastian Hesselbarth
[not found] ` <1399580212-30183-1-git-send-email-sebastian.hesselbarth-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org>
1 sibling, 0 replies; 4+ messages in thread
From: Sebastian Hesselbarth @ 2014-05-08 20:16 UTC (permalink / raw)
To: Sebastian Hesselbarth
Cc: Mike Turquette, Alexandre Belloni, Antoine Tenart, Jason Cooper,
Andrew Lunn, Gregory Clement, Thomas Petazzoni, devicetree,
linux-arm-kernel, linux-kernel
There is some awkward ASCII-art describing the basic structure
of the cell.
Signed-off-by: Sebastian Hesselbarth <sebastian.hesselbarth@gmail.com>
---
Cc: Mike Turquette <mturquette@linaro.org>
Cc: Alexandre Belloni <alexandre.belloni@free-electrons.com>
Cc: Antoine Tenart <antoine.tenart@free-electrons.com>
Cc: Jason Cooper <jason@lakedaemon.net>
Cc: Andrew Lunn <andrew@lunn.ch>
Cc: Gregory Clement <gregory.clement@free-electrons.com>
Cc: Thomas Petazzoni <thomas.petazzoni@free-electrons.com>
Cc: devicetree@vger.kernel.org
Cc: linux-arm-kernel@lists.infradead.org
Cc: linux-kernel@vger.kernel.org
---
drivers/clk/berlin/berlin2-div.c | 261 +++++++++++++++++++++++++++++++++++++++
1 file changed, 261 insertions(+)
create mode 100644 drivers/clk/berlin/berlin2-div.c
diff --git a/drivers/clk/berlin/berlin2-div.c b/drivers/clk/berlin/berlin2-div.c
new file mode 100644
index 000000000000..ef8f7583a78f
--- /dev/null
+++ b/drivers/clk/berlin/berlin2-div.c
@@ -0,0 +1,261 @@
+/*
+ * Copyright (c) 2014 Marvell Technology Group Ltd.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope 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.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+#include <linux/bitops.h>
+#include <linux/clk-provider.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
+#include <linux/slab.h>
+#include <linux/spinlock.h>
+
+#include "common.h"
+
+/*
+ * Clock dividers in Berlin2 SoCs comprise a complex cell to select
+ * input pll and divider. The virtual structure as it is used in Marvell
+ * BSP code can be seen as:
+ *
+ * +---+
+ * pll0 --------------->| 0 | +---+
+ * +---+ |(B)|--+--------------->| 0 | +---+
+ * pll1.0 -->| 0 | +-->| 1 | | +--------+ |(E)|----->| 0 | +---+
+ * pll1.1 -->| 1 | | +---+ +-->|(C) 1:M |-->| 1 | |(F)|-->|(G)|->
+ * ... -->|(A)|--+ | +--------+ +---+ +-->| 1 | +---+
+ * ... -->| | +-->|(D) 1:3 |----------+ +---+
+ * pll1.N -->| N | +---------
+ * +---+
+ *
+ * (A) input pll clock mux controlled by <PllSelect[1:n]>
+ * (B) input pll bypass mux controlled by <PllSwitch>
+ * (C) programmable clock divider controlled by <Select[1:n]>
+ * (D) constant div-by-3 clock divider
+ * (E) programmable clock divider bypass controlled by <Switch>
+ * (F) constant div-by-3 clock mux controlled by <D3Switch>
+ * (G) clock gate controlled by <Enable>
+ *
+ * For whatever reason, above control signals come in two flavors:
+ * - single register dividers with all bits in one register
+ * - shared register dividers with bits spread over multiple registers
+ * (including signals for the same cell spread over consecutive registers)
+ *
+ * Also, clock gate and pll mux is not available on every div cell, so
+ * we have to deal with those, too.
+ */
+
+#define PLL_SELECT_MASK 0x7
+#define DIV_SELECT_MASK 0x7
+
+struct berlin2_div {
+ struct clk_hw hw;
+ void __iomem *base;
+ struct berlin2_div_map map;
+ spinlock_t *lock;
+};
+
+#define to_berlin2_div(hw) container_of(hw, struct berlin2_div, hw)
+
+static u8 clk_div[] = { 1, 2, 4, 6, 8, 12, 1, 1 };
+
+static int berlin2_div_is_enabled(struct clk_hw *hw)
+{
+ struct berlin2_div *div = to_berlin2_div(hw);
+ struct berlin2_div_map *map = &div->map;
+ u32 reg;
+
+ spin_lock(div->lock);
+
+ reg = readl_relaxed(div->base + map->gate_offs);
+ reg >>= map->gate_shift;
+
+ spin_unlock(div->lock);
+
+ return (reg & 0x1);
+}
+
+static int berlin2_div_enable(struct clk_hw *hw)
+{
+ struct berlin2_div *div = to_berlin2_div(hw);
+ struct berlin2_div_map *map = &div->map;
+ u32 reg;
+
+ spin_lock(div->lock);
+
+ reg = readl_relaxed(div->base + map->gate_offs);
+ reg |= BIT(map->gate_shift);
+ writel_relaxed(reg, div->base + map->gate_offs);
+
+ spin_unlock(div->lock);
+
+ return 0;
+}
+
+static void berlin2_div_disable(struct clk_hw *hw)
+{
+ struct berlin2_div *div = to_berlin2_div(hw);
+ struct berlin2_div_map *map = &div->map;
+ u32 reg;
+
+ spin_lock(div->lock);
+
+ reg = readl_relaxed(div->base + map->gate_offs);
+ reg &= ~BIT(map->gate_shift);
+ writel_relaxed(reg, div->base + map->gate_offs);
+
+ spin_unlock(div->lock);
+}
+
+static int berlin2_div_set_parent(struct clk_hw *hw, u8 index)
+{
+ struct berlin2_div *div = to_berlin2_div(hw);
+ struct berlin2_div_map *map = &div->map;
+ u32 reg;
+
+ spin_lock(div->lock);
+
+ /* index == 0 is PLL_SWITCH */
+ reg = readl_relaxed(div->base + map->pll_switch_offs);
+ if (index == 0)
+ reg &= ~BIT(map->pll_switch_shift);
+ else
+ reg |= BIT(map->pll_switch_shift);
+ writel_relaxed(reg, div->base + map->pll_switch_offs);
+
+ /* index > 0 is PLL_SELECT */
+ if (index > 0) {
+ reg = readl_relaxed(div->base + map->pll_select_offs);
+ reg &= ~(PLL_SELECT_MASK << map->pll_select_shift);
+ reg |= (index - 1) << map->pll_select_shift;
+ writel_relaxed(reg, div->base + map->pll_select_offs);
+ }
+
+ spin_unlock(div->lock);
+
+ return 0;
+}
+
+static u8 berlin2_div_get_parent(struct clk_hw *hw)
+{
+ struct berlin2_div *div = to_berlin2_div(hw);
+ struct berlin2_div_map *map = &div->map;
+ u32 reg;
+ u8 index = 0;
+
+ spin_lock(div->lock);
+
+ /* PLL_SWITCH == 0 is index 0 */
+ reg = readl_relaxed(div->base + map->pll_switch_offs);
+ reg &= BIT(map->pll_switch_shift);
+ if (reg) {
+ reg = readl_relaxed(div->base + map->pll_select_offs);
+ reg >>= map->pll_select_shift;
+ reg &= PLL_SELECT_MASK;
+ index = 1 + reg;
+ }
+
+ spin_unlock(div->lock);
+
+ return index;
+}
+
+static unsigned long berlin2_div_recalc_rate(struct clk_hw *hw,
+ unsigned long parent_rate)
+{
+ struct berlin2_div *div = to_berlin2_div(hw);
+ struct berlin2_div_map *map = &div->map;
+ u32 divsw, div3sw, divider = 1;
+
+ spin_lock(div->lock);
+
+ divsw = readl_relaxed(div->base + map->div_switch_offs) &
+ (1 << map->div_switch_shift);
+ div3sw = readl_relaxed(div->base + map->div3_switch_offs) &
+ (1 << map->div3_switch_shift);
+
+ /* constant divide-by-3 (dominant) */
+ if (div3sw == 1) {
+ divider = 3;
+ /* divider can be bypassed with DIV_SWITCH == 0 */
+ } else if (divsw == 0) {
+ divider = 1;
+ /* clock divider determined by DIV_SELECT */
+ } else {
+ u32 reg;
+ reg = readl_relaxed(div->base + map->div_select_offs);
+ reg >>= map->div_select_shift;
+ reg &= DIV_SELECT_MASK;
+ divider = clk_div[reg];
+ }
+
+ spin_unlock(div->lock);
+
+ return parent_rate / divider;
+}
+
+static const struct clk_ops berlin2_div_full_ops = {
+ .is_enabled = berlin2_div_is_enabled,
+ .enable = berlin2_div_enable,
+ .disable = berlin2_div_disable,
+ .recalc_rate = berlin2_div_recalc_rate,
+ .set_parent = berlin2_div_set_parent,
+ .get_parent = berlin2_div_get_parent,
+};
+
+static const struct clk_ops berlin2_div_no_gate_ops = {
+ .recalc_rate = berlin2_div_recalc_rate,
+ .set_parent = berlin2_div_set_parent,
+ .get_parent = berlin2_div_get_parent,
+};
+
+static const struct clk_ops berlin2_div_no_pll_mux_ops = {
+ .is_enabled = berlin2_div_is_enabled,
+ .enable = berlin2_div_enable,
+ .disable = berlin2_div_disable,
+ .recalc_rate = berlin2_div_recalc_rate,
+};
+
+struct clk * __init
+berlin2_div_register(const struct berlin2_div_map *map,
+ void __iomem *base, const char *name,
+ const char **parent_names, int num_parents,
+ unsigned long flags, spinlock_t *lock)
+{
+ struct clk_init_data init;
+ struct berlin2_div *div;
+
+ div = kzalloc(sizeof(*div), GFP_KERNEL);
+ if (!div)
+ return ERR_PTR(-ENOMEM);
+
+ /* copy div_map to allow __initconst */
+ memcpy(&div->map, map, sizeof(*map));
+
+ init.name = name;
+ init.parent_names = parent_names;
+ init.num_parents = num_parents;
+ init.flags = flags;
+
+ if (!map->has_gate)
+ init.ops = &berlin2_div_no_gate_ops;
+ else if (!map->has_pll_switch || !map->has_pll_select)
+ init.ops = &berlin2_div_no_pll_mux_ops;
+ else
+ init.ops = &berlin2_div_full_ops;
+
+ div->base = base;
+ div->lock = lock;
+ div->hw.init = &init;
+
+ return clk_register(NULL, &div->hw);
+}
--
1.9.1
^ permalink raw reply related [flat|nested] 4+ messages in thread
* [PATCH RFC 3/3] clk: berlin: add driver for BG2x simple PLLs
[not found] ` <1399580212-30183-1-git-send-email-sebastian.hesselbarth-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org>
2014-05-08 20:16 ` [PATCH RFC 1/3] clk: berlin: add clock tree driver for BG2/BG2CD Sebastian Hesselbarth
@ 2014-05-08 20:16 ` Sebastian Hesselbarth
2014-05-10 16:13 ` [PATCH RFC 0/3] Berlin SoC clock tree - DT or not DT? Sebastian Hesselbarth
2 siblings, 0 replies; 4+ messages in thread
From: Sebastian Hesselbarth @ 2014-05-08 20:16 UTC (permalink / raw)
To: Sebastian Hesselbarth
Cc: Mike Turquette, Alexandre Belloni, Antoine Tenart, Jason Cooper,
Andrew Lunn, Gregory Clement, Thomas Petazzoni,
devicetree-u79uwXL29TY76Z2rM5mHXA,
linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
linux-kernel-u79uwXL29TY76Z2rM5mHXA
This is a clock driver for the simple PLLs found on Berlin SoCs.
With repect to PLL registers and features, BG2/BG2CD and BG2Q are
slightly different, e.g. different allowed VCO dividers and bit
shifts.
Signed-off-by: Sebastian Hesselbarth <sebastian.hesselbarth-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org>
---
Cc: Mike Turquette <mturquette-QSEj5FYQhm4dnm+yROfE0A@public.gmane.org>
Cc: Alexandre Belloni <alexandre.belloni-wi1+55ScJUtKEb57/3fJTNBPR1lH4CV8@public.gmane.org>
Cc: Antoine Tenart <antoine.tenart-wi1+55ScJUtKEb57/3fJTNBPR1lH4CV8@public.gmane.org>
Cc: Jason Cooper <jason-NLaQJdtUoK4Be96aLqz0jA@public.gmane.org>
Cc: Andrew Lunn <andrew-g2DYL2Zd6BY@public.gmane.org>
Cc: Gregory Clement <gregory.clement-wi1+55ScJUtKEb57/3fJTNBPR1lH4CV8@public.gmane.org>
Cc: Thomas Petazzoni <thomas.petazzoni-wi1+55ScJUtKEb57/3fJTNBPR1lH4CV8@public.gmane.org>
Cc: devicetree-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
Cc: linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r@public.gmane.org
Cc: linux-kernel-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
---
drivers/clk/berlin/berlin2-pll.c | 104 +++++++++++++++++++++++++++++++++++++++
1 file changed, 104 insertions(+)
create mode 100644 drivers/clk/berlin/berlin2-pll.c
diff --git a/drivers/clk/berlin/berlin2-pll.c b/drivers/clk/berlin/berlin2-pll.c
new file mode 100644
index 000000000000..aef4ddd8db10
--- /dev/null
+++ b/drivers/clk/berlin/berlin2-pll.c
@@ -0,0 +1,104 @@
+/*
+ * Copyright (c) 2014 Marvell Technology Group Ltd.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope 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.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+#include <linux/clk-provider.h>
+#include <linux/io.h>
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <asm/div64.h>
+
+#include "common.h"
+
+struct berlin2_pll {
+ struct clk_hw hw;
+ void __iomem *base;
+ struct berlin2_pll_map map;
+};
+
+#define to_berlin2_pll(hw) container_of(hw, struct berlin2_pll, hw)
+
+#define SPLL_CTRL0 0x00
+#define SPLL_CTRL1 0x04
+#define SPLL_CTRL2 0x08
+#define SPLL_CTRL3 0x0c
+#define SPLL_CTRL4 0x10
+
+#define FBDIV_MASK 0x1ff
+#define RFDIV_MASK 0x1f
+#define DIVSEL_MASK 0xf
+
+/*
+ * The output frequency formula for the pll is:
+ * clkout = fbdiv / refdiv * parent / vcodiv
+ */
+static unsigned long
+berlin2_pll_recalc_rate(struct clk_hw *hw, unsigned long parent_rate)
+{
+ struct berlin2_pll *pll = to_berlin2_pll(hw);
+ struct berlin2_pll_map *map = &pll->map;
+ u32 val, fbdiv, rfdiv, vcodivsel, vcodiv;
+ u64 rate = parent_rate;
+
+ val = readl_relaxed(pll->base + SPLL_CTRL0);
+ fbdiv = (val >> map->fbdiv_shift) & FBDIV_MASK;
+ rfdiv = (val >> map->rfdiv_shift) & RFDIV_MASK;
+ if (rfdiv == 0) {
+ pr_warn("%s has zero rfdiv\n", __clk_get_name(hw->clk));
+ rfdiv = 1;
+ }
+
+ val = readl_relaxed(pll->base + SPLL_CTRL1);
+ vcodivsel = (val >> map->divsel_shift) & DIVSEL_MASK;
+ vcodiv = map->vcodiv[vcodivsel];
+ if (vcodiv == 0) {
+ pr_warn("%s has zero vcodiv (index %d)\n",
+ __clk_get_name(hw->clk), vcodivsel);
+ vcodiv = 1;
+ }
+
+ rate *= fbdiv * map->mult;
+ do_div(rate, rfdiv * vcodiv);
+
+ return (unsigned long)rate;
+}
+
+static const struct clk_ops berlin2_pll_ops = {
+ .recalc_rate = berlin2_pll_recalc_rate,
+};
+
+struct clk * __init
+berlin2_pll_register(const struct berlin2_pll_map *map,
+ void __iomem *base, const char *name,
+ const char *parent_name, unsigned long flags)
+{
+ struct clk_init_data init;
+ struct berlin2_pll *pll;
+
+ pll = kzalloc(sizeof(*pll), GFP_KERNEL);
+ if (!pll)
+ return ERR_PTR(-ENOMEM);
+
+ /* copy pll_map to allow __initconst */
+ memcpy(&pll->map, map, sizeof(*map));
+ pll->base = base;
+ pll->hw.init = &init;
+ init.name = name;
+ init.ops = &berlin2_pll_ops;
+ init.parent_names = &parent_name;
+ init.num_parents = 1;
+ init.flags = flags;
+
+ return clk_register(NULL, &pll->hw);
+}
--
1.9.1
--
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 related [flat|nested] 4+ messages in thread
* Re: [PATCH RFC 0/3] Berlin SoC clock tree - DT or not DT?
[not found] ` <1399580212-30183-1-git-send-email-sebastian.hesselbarth-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org>
2014-05-08 20:16 ` [PATCH RFC 1/3] clk: berlin: add clock tree driver for BG2/BG2CD Sebastian Hesselbarth
2014-05-08 20:16 ` [PATCH RFC 3/3] clk: berlin: add driver for BG2x simple PLLs Sebastian Hesselbarth
@ 2014-05-10 16:13 ` Sebastian Hesselbarth
2 siblings, 0 replies; 4+ messages in thread
From: Sebastian Hesselbarth @ 2014-05-10 16:13 UTC (permalink / raw)
To: Sebastian Hesselbarth
Cc: Mike Turquette, Alexandre Belloni, Antoine Tenart, Jason Cooper,
Andrew Lunn, Gregory Clement, Thomas Petazzoni,
devicetree-u79uwXL29TY76Z2rM5mHXA@public.gmane.org,
linux-arm-kernel, LKML
Sorry for the noise,
I just noticed that I didn't send the cover letter itself to the
list. Anyway, I decided to review the whole clock mess again and
came up with a quite nice set of patches which split clocks between
DT- and code-registered clocks.
Actually, I need Alexandre to (re-)add BG2Q support first, but if he
is busy, I'll send the preliminary version on Sunday or Monday.
@Mike: I know it is late in the cycle, but I'd love to see this in
for the next release already. There are functional bugs for sure,
but actually Berlin SoC support is so minimal, there is nothing we
can really break. I'll put that in the real patch set's cover letter
again, but when I send it, I will have prepared a topic branch of it
and put it into linux-next to make sure, it doesn't break others.
If you NAK taking it for the next release, we would have to squeeze
in some DT tweaks with fixed-clocks as placeholders for the missing
clocks. Antonine and Alexandre are doing a great job adding
functionality to Berlin SoCs but, of course, lack clocks here and
there. Using DT fixed-clocks would be ok now, but proper clock nodes
even if the corresponding drivers are buggy would be best.
I have compared driver generated frequencies with Marvell's BSP
u-boot clock report and they match, so it isn't too buggy :P
@Alexandre, Antoine:
The latest set of patches is at
https://github.com/shesselba/linux-berlin.git topic/clk
Please have a look at it and prepare BG2Q patches that I can
add/squeeze to it. Also, if you have comments already, do not
hesitate to send them directly to me.
Sebastian
On 05/08/2014 10:16 PM, Sebastian Hesselbarth wrote:
> This is a RFC providing a counter-approach to some patches set by
> Alexandre Belloni [1][2] to bring clock support for Berlin SoCs.
> It is based on those and final patches will include Alexandre's
> Signed-off again, I just dropped it for the RFC.
>
> I am really unsure what is the best way to represent this in DT
> and/or code, i.e. where to split this up. I's appreciate any
> valuable discussion about it.
>
> On Berlin SoCs, clock registers reside in global registers together
> with e.g. pin ctrl, pad ctrl, reset, and general purpose regs used
> for secondary boot address.
>
> Actually, the situation isn't as bad as I initially thought, i.e.
> regarding clocks there is only two types of PLLs, some
> mux/divider/gate cells, and some separate muxes and gates.
>
> What bothers me most is, there is *always* one that is different from
> the others, e.g.
>
> - mux/divider/gate cells either have some bits in registers shared
> with other cells *or* have their own register
> - some cells are missing the gate, some have no mux
> - some m/d/g cells have their shared bits in the same register, some
> in consecutive registers
>
> To find a good representation in either code or DT that fits all,
> quickly becomes messy. Anyway, we somehow have to deal with it, so
> here is what I think are the three options:
>
> a) One single "Berlin2 Clock Tree" DT node (this RFC)
>
> We basically hide all the gory details of the register mess above
> behind a single clock provider and index each leaf-clock by some
> number. The pro is that DT doesn't get messed up, the con is that
> there is a bunch of structs that each MULTIARCH kernel has to carry
> around (currently ~13k). I tried to put it all in __initconst/data
> so at least it can be free'd later.
>
> b) Represent each PLL, M/D/G cell, and the single muxes/gates in DT
>
> Well, the opposite of (a) i.e. exposing all the different register
> offsets and shifts as properties. The pro is that most of the code
> will move to DT which is only used by Berlin SoCs. The con is
> definitely that we have to link all the clocks in DT and therefore
> quickly get up to ~30 clock providers with a lot of clocks each.
>
> c) Find the way in the middle
>
> After looking long enough on the register mess, there is some blocks
> that can be separated from each other. Basically it is each of the
> PLLs, the single register dividers, and the rest. This still requires
> a bunch of DT clocks but has far less clock providers. We still need
> to provide some offset/shift properties as BG2/BG2CD's and BG2Q's cells
> are slightly different.
>
> I know, (c) sounds like the best option. But given the pain I went
> through reading this register set includes (no datasheet available
> of course), I really want some opinions about all three alternatives
> first before touching this code ever again.
>
> I'd appreciate any review and comments about the three patches attached
> to this RFC *and* to the general approaches above. I just sent the
> drivers and left out anything else, e.g. DT binding docs, Makefile/
> Kconfigs, aso. Also, the patches are not in the correct (bisectable)
> order but sorted by "controversity".
>
> Sebastian
>
> [1] https://lkml.org/lkml/2014/4/23/831
> [2] https://lkml.org/lkml/2014/4/24/624
>
> Sebastian Hesselbarth (3):
> clk: berlin: add clock tree driver for BG2/BG2CD
> clk: berlin: add driver for BG2x complex divider cells
> clk: berlin: add driver for BG2x simple PLLs
>
> drivers/clk/berlin/berlin2-div.c | 261 +++++++++++++++++
> drivers/clk/berlin/berlin2-pll.c | 104 +++++++
> drivers/clk/berlin/bg2.c | 615 +++++++++++++++++++++++++++++++++++++++
> drivers/clk/berlin/common.h | 124 ++++++++
> 4 files changed, 1104 insertions(+)
> create mode 100644 drivers/clk/berlin/berlin2-div.c
> create mode 100644 drivers/clk/berlin/berlin2-pll.c
> create mode 100644 drivers/clk/berlin/bg2.c
> create mode 100644 drivers/clk/berlin/common.h
>
--
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] 4+ messages in thread
end of thread, other threads:[~2014-05-10 16:13 UTC | newest]
Thread overview: 4+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
[not found] <1399580212-30183-1-git-send-email-sebastian.hesselbarth@gmail.com>
2014-05-08 20:16 ` [PATCH RFC 2/3] clk: berlin: add driver for BG2x complex divider cells Sebastian Hesselbarth
[not found] ` <1399580212-30183-1-git-send-email-sebastian.hesselbarth-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org>
2014-05-08 20:16 ` [PATCH RFC 1/3] clk: berlin: add clock tree driver for BG2/BG2CD Sebastian Hesselbarth
2014-05-08 20:16 ` [PATCH RFC 3/3] clk: berlin: add driver for BG2x simple PLLs Sebastian Hesselbarth
2014-05-10 16:13 ` [PATCH RFC 0/3] Berlin SoC clock tree - DT or not DT? Sebastian Hesselbarth
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).