[PATCH RFC 1/3] clk: berlin: add clock tree driver for BG2/BG2CD

Sebastian Hesselbarth sebastian.hesselbarth at gmail.com
Thu May 8 13:16:50 PDT 2014


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 at gmail.com>
---
Cc: Mike Turquette <mturquette at linaro.org>
Cc: Alexandre Belloni <alexandre.belloni at free-electrons.com>
Cc: Antoine Tenart <antoine.tenart at free-electrons.com>
Cc: Jason Cooper <jason at lakedaemon.net>
Cc: Andrew Lunn <andrew at lunn.ch>
Cc: Gregory Clement <gregory.clement at free-electrons.com>
Cc: Thomas Petazzoni <thomas.petazzoni at free-electrons.com>
Cc: devicetree at vger.kernel.org
Cc: linux-arm-kernel at lists.infradead.org
Cc: linux-kernel at vger.kernel.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 at free-electrons.com>
+ * Sebastian Hesselbarth <sebastian.hesselbarth at gmail.com>
+ *
+ * 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




More information about the linux-arm-kernel mailing list