All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH 0/9] Audio clocks for sun[457]i, SoC revision detection
@ 2014-07-31 21:28 Emilio López
  2014-07-31 21:28 ` [PATCH 1/9] ARM: sunxi: introduce SoC identification support Emilio López
                   ` (8 more replies)
  0 siblings, 9 replies; 36+ messages in thread
From: Emilio López @ 2014-07-31 21:28 UTC (permalink / raw)
  To: linux-arm-kernel

Hi everyone,

As part of my ongoing GSoC project effort, I did some work on the clock
code we're going to need for the audio blocks. This series adds support for
PLL2 on A10, A10S, A13 and A20. It also includes support for the codec
clock as well as module 1 clocks, used in the audio blocks.

To achieve this, we also need to implement SoC revision detection,
as the PLL2 clock is different on A10 revision A. As discussed on the
RFC series, we achieve this by detecting the revision during runtime and
adjusting the compatibles before the devices get probed. As a side effect,
we can also add support for the SoC bus to expose these details to
userspace.

The first two patches introduce the SoC identification support and the
quirk support. The third patch is a fix to a sparse warning that I hit
while building the code. Patches four to six implement the support for PLL2,
codec and module 1 clocks on our clock driver. The last three patches
add the corresponding clock nodes to the device tree.

As usual, all comments and reviews are welcome.

Thanks!

Emilio

Emilio L?pez (9):
  ARM: sunxi: introduce SoC identification support
  ARM: sunxi: quirk support
  ARM: sunxi: make sun6i SMP ops static
  clk: sunxi: PLL2 support for sun4i, sun5i and sun7i
  clk: sunxi: codec clock support
  clk: sunxi: mod1 clock support
  ARM: sunxi: dt: Add PLL2 support
  ARM: sunxi: dt: Add codec clock support
  ARM: sun7i: dt: Add mod1 clock nodes

 Documentation/devicetree/bindings/clock/sunxi.txt |   4 +
 arch/arm/boot/dts/sun4i-a10.dtsi                  |  16 ++
 arch/arm/boot/dts/sun5i-a10s.dtsi                 |  16 ++
 arch/arm/boot/dts/sun5i-a13.dtsi                  |  16 ++
 arch/arm/boot/dts/sun7i-a20.dtsi                  |  56 +++++
 arch/arm/mach-sunxi/Kconfig                       |   1 +
 arch/arm/mach-sunxi/Makefile                      |   2 +-
 arch/arm/mach-sunxi/platsmp.c                     |   2 +-
 arch/arm/mach-sunxi/quirks.c                      |  82 ++++++++
 arch/arm/mach-sunxi/sunxi-soc-id.c                | 226 ++++++++++++++++++++
 arch/arm/mach-sunxi/sunxi-soc-id.h                |   6 +
 drivers/clk/sunxi/Makefile                        |   3 +
 drivers/clk/sunxi/clk-a10-codec.c                 |  41 ++++
 drivers/clk/sunxi/clk-a10-mod1.c                  |  69 ++++++
 drivers/clk/sunxi/clk-a10-pll2.c                  | 243 ++++++++++++++++++++++
 15 files changed, 781 insertions(+), 2 deletions(-)
 create mode 100644 arch/arm/mach-sunxi/quirks.c
 create mode 100644 arch/arm/mach-sunxi/sunxi-soc-id.c
 create mode 100644 arch/arm/mach-sunxi/sunxi-soc-id.h
 create mode 100644 drivers/clk/sunxi/clk-a10-codec.c
 create mode 100644 drivers/clk/sunxi/clk-a10-mod1.c
 create mode 100644 drivers/clk/sunxi/clk-a10-pll2.c

-- 
2.0.3

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

* [PATCH 1/9] ARM: sunxi: introduce SoC identification support
  2014-07-31 21:28 [PATCH 0/9] Audio clocks for sun[457]i, SoC revision detection Emilio López
@ 2014-07-31 21:28 ` Emilio López
  2014-08-03 12:40   ` Maxime Ripard
  2014-07-31 21:28 ` [PATCH 2/9] ARM: sunxi: quirk support Emilio López
                   ` (7 subsequent siblings)
  8 siblings, 1 reply; 36+ messages in thread
From: Emilio López @ 2014-07-31 21:28 UTC (permalink / raw)
  To: linux-arm-kernel

This commit adds SoC bus support on the sunxi platform, and exposes
information such as the hardware revision to userspace and other kernel
clients during init. A message with this information is also printed to
the kernel log to ease future bug triaging.

Signed-off-by: Emilio L?pez <emilio@elopez.com.ar>
---
 arch/arm/mach-sunxi/Kconfig        |   1 +
 arch/arm/mach-sunxi/Makefile       |   2 +-
 arch/arm/mach-sunxi/sunxi-soc-id.c | 226 +++++++++++++++++++++++++++++++++++++
 arch/arm/mach-sunxi/sunxi-soc-id.h |   6 +
 4 files changed, 234 insertions(+), 1 deletion(-)
 create mode 100644 arch/arm/mach-sunxi/sunxi-soc-id.c
 create mode 100644 arch/arm/mach-sunxi/sunxi-soc-id.h

diff --git a/arch/arm/mach-sunxi/Kconfig b/arch/arm/mach-sunxi/Kconfig
index 6434e3b..4a199df 100644
--- a/arch/arm/mach-sunxi/Kconfig
+++ b/arch/arm/mach-sunxi/Kconfig
@@ -5,6 +5,7 @@ menuconfig ARCH_SUNXI
 	select GENERIC_IRQ_CHIP
 	select PINCTRL
 	select PINCTRL_SUNXI
+	select SOC_BUS
 	select SUN4I_TIMER
 
 if ARCH_SUNXI
diff --git a/arch/arm/mach-sunxi/Makefile b/arch/arm/mach-sunxi/Makefile
index 27b168f..589239b 100644
--- a/arch/arm/mach-sunxi/Makefile
+++ b/arch/arm/mach-sunxi/Makefile
@@ -1,2 +1,2 @@
-obj-$(CONFIG_ARCH_SUNXI) += sunxi.o
+obj-$(CONFIG_ARCH_SUNXI) += sunxi.o sunxi-soc-id.o
 obj-$(CONFIG_SMP) += platsmp.o
diff --git a/arch/arm/mach-sunxi/sunxi-soc-id.c b/arch/arm/mach-sunxi/sunxi-soc-id.c
new file mode 100644
index 0000000..c7eff1c
--- /dev/null
+++ b/arch/arm/mach-sunxi/sunxi-soc-id.c
@@ -0,0 +1,226 @@
+/*
+ * SoC revision detection for sunxi SoCs
+ *
+ * Copyright 2014 Emilio L?pez
+ *
+ * Emilio L?pez <emilio@elopez.com.ar>
+ *
+ * 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.
+ */
+
+#include <linux/err.h>
+#include <linux/io.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
+#include <linux/slab.h>
+#include <linux/string.h>
+#include <linux/sys_soc.h>
+
+#include "sunxi-soc-id.h"
+
+/*
+ * On the A10 SoC, we can read the revision information from the timer
+ * block. The detection logic is extracted from similar code on the
+ * Allwinner vendor tree, as this is undocumented on the user manual
+ */
+
+#define TIMER_SOC_REV_REG		0x13c
+#define TIMER_SOC_REV_CLEAR(val)	((val) & ~(0x3 << 6))
+#define TIMER_SOC_REV_GET(val)		(((val) >> 6) & 0x3)
+
+static const struct of_device_id sun4i_timer_compatible[] __initconst = {
+	{ .compatible = "allwinner,sun4i-a10-timer", },
+	{},
+};
+
+static int __init sun4i_get_soc_revision(void)
+{
+	struct device_node *np;
+	void __iomem *base;
+	u32 val;
+	int ret;
+
+	/* Find the timer node */
+	np = of_find_matching_node(NULL, sun4i_timer_compatible);
+	if (!np)
+		return -ENODEV;
+
+	/* Temporarily map it for reading */
+	base = of_iomap(np, 0);
+	if (!base) {
+		of_node_put(np);
+		return -ENOMEM;
+	}
+
+	/* Clear the SoC revision bits and rewrite the register */
+	val = readl(base + TIMER_SOC_REV_REG);
+	val = TIMER_SOC_REV_CLEAR(val);
+	writel(val, base + TIMER_SOC_REV_REG);
+
+	/* Now read it again and see what shows up */
+	val = readl(base + TIMER_SOC_REV_REG);
+	val = TIMER_SOC_REV_GET(val);
+
+	switch (val) {
+	case 0:  /* revision A */
+		ret = 'A';
+	case 3:  /* revision B */
+		ret = 'B';
+	default: /* revision C */
+		ret = 'C';
+	}
+
+	iounmap(base);
+	of_node_put(np);
+
+	return ret;
+}
+
+/*
+ * On the sun5i SoCs (A10S, A13), we can read the revision information
+ * from the first bits in the Security ID. The detection logic is
+ * extracted from similar code on the Allwinner vendor tree, as this
+ * is undocumented on the user manual.
+ */
+
+static const struct of_device_id sun5i_sid_compatible[] __initconst = {
+	{ .compatible = "allwinner,sun4i-a10-sid", },
+	{},
+};
+
+static int __init sun5i_get_soc_revision(void)
+{
+	struct device_node *np;
+	void __iomem *sid;
+	u32 val;
+	int ret;
+
+	/* Find the SID node */
+	np = of_find_matching_node(NULL, sun5i_sid_compatible);
+	if (!np)
+		return -ENODEV;
+
+	/* Temporarily map it for reading */
+	sid = of_iomap(np, 0);
+	if (!sid) {
+		of_node_put(np);
+		return -ENOMEM;
+	}
+
+	/* Read and extract the chip revision from the SID */
+	val = readl(sid);
+	val = (val >> 8) & 0xffffff;
+
+	switch (val) {
+	case 0:        /* A10S/A13 rev A */
+	case 0x162541: /* A10S/A13 rev A */
+	case 0x162565: /* A13 rev A */
+		ret = 'A';
+		break;
+	case 0x162542: /* A10S/A13 rev B */
+		ret = 'B';
+		break;
+	default:       /* Unknown chip revision */
+		ret = -ENODATA;
+	}
+
+	iounmap(sid);
+	of_node_put(np);
+
+	return ret;
+}
+
+int __init sunxi_soc_revision(void)
+{
+	static int revision = -ENODEV;
+
+	/* Try to query the hardware just once */
+	if (!IS_ERR_VALUE(revision))
+		return revision;
+
+	if (of_machine_is_compatible("allwinner,sun4i-a10")) {
+		revision = sun4i_get_soc_revision();
+	} else if (of_machine_is_compatible("allwinner,sun5i-a10s") ||
+		   of_machine_is_compatible("allwinner,sun5i-a13")) {
+		revision = sun5i_get_soc_revision();
+	}
+
+	return revision;
+}
+
+/* Matches for the sunxi SoCs we know of */
+static const struct of_device_id soc_matches[] __initconst = {
+	{ .compatible = "allwinner,sun4i-a10", .data = "A10 (sun4i)" },
+	{ .compatible = "allwinner,sun5i-a13", .data = "A13 (sun5i)" },
+	{ .compatible = "allwinner,sun5i-a10s", .data = "A10S (sun5i)" },
+	{ .compatible = "allwinner,sun6i-a31", .data = "A31 (sun6i)" },
+	{ .compatible = "allwinner,sun7i-a20", .data = "A20 (sun7i)" },
+	{ .compatible = "allwinner,sun8i-a23", .data = "A23 (sun8i)" },
+	{ /* sentinel */ },
+};
+
+static int __init sunxi_register_soc_device(void)
+{
+	struct soc_device_attribute *soc_dev_attr;
+	struct soc_device *soc_dev;
+	const struct of_device_id *match;
+	struct device_node *root;
+	int revision;
+
+	/* Only run on sunxi SoCs that we know of */
+	root = of_find_node_by_path("/");
+	match = of_match_node(soc_matches, root);
+	if (!match)
+		goto exit;
+
+	soc_dev_attr = kzalloc(sizeof(*soc_dev_attr), GFP_KERNEL);
+	if (!soc_dev_attr)
+		goto exit;
+
+	/* Read the machine name if available */
+	of_property_read_string(root, "model", &soc_dev_attr->machine);
+
+	soc_dev_attr->family = kstrdup("Allwinner A Series", GFP_KERNEL);
+	soc_dev_attr->soc_id = kstrdup(match->data, GFP_KERNEL);
+
+	/* Revision may not always be available */
+	revision = sunxi_soc_revision();
+	if (IS_ERR_VALUE(revision))
+		soc_dev_attr->revision = kstrdup("Unknown", GFP_KERNEL);
+	else
+		soc_dev_attr->revision = kasprintf(GFP_KERNEL, "%c", revision);
+
+	soc_dev = soc_device_register(soc_dev_attr);
+	if (IS_ERR(soc_dev))
+		goto free_struct;
+
+	/*
+	 * Print an informational line mentioning the hardware details.
+	 * It may come in handy during bug reports, as some early SoC
+	 * revisions have hardware quirks and do not get much testing.
+	 */
+	pr_info("SoC bus registered, running %s %s, revision %s\n",
+		soc_dev_attr->family, soc_dev_attr->soc_id,
+		soc_dev_attr->revision);
+
+	return 0;
+
+free_struct:
+	kfree(soc_dev_attr->family);
+	kfree(soc_dev_attr->soc_id);
+	kfree(soc_dev_attr->revision);
+	kfree(soc_dev_attr);
+exit:
+	of_node_put(root);
+
+	return 0;
+}
+postcore_initcall(sunxi_register_soc_device)
diff --git a/arch/arm/mach-sunxi/sunxi-soc-id.h b/arch/arm/mach-sunxi/sunxi-soc-id.h
new file mode 100644
index 0000000..d49c245
--- /dev/null
+++ b/arch/arm/mach-sunxi/sunxi-soc-id.h
@@ -0,0 +1,6 @@
+#ifndef _SUNXI_SOC_ID_H
+#define _SUNXI_SOC_ID_H
+
+int __init sunxi_soc_revision(void);
+
+#endif /* _SUNXI_SOC_ID_H */
-- 
2.0.3

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

* [PATCH 2/9] ARM: sunxi: quirk support
  2014-07-31 21:28 [PATCH 0/9] Audio clocks for sun[457]i, SoC revision detection Emilio López
  2014-07-31 21:28 ` [PATCH 1/9] ARM: sunxi: introduce SoC identification support Emilio López
@ 2014-07-31 21:28 ` Emilio López
  2014-08-03 12:42   ` Maxime Ripard
  2014-07-31 21:28 ` [PATCH 3/9] ARM: sunxi: make sun6i SMP ops static Emilio López
                   ` (6 subsequent siblings)
  8 siblings, 1 reply; 36+ messages in thread
From: Emilio López @ 2014-07-31 21:28 UTC (permalink / raw)
  To: linux-arm-kernel

Currently, some hardware revisions of sunxi SoCs need special care on
some blocks because of hardware differences and/or bugs. Unfortunately,
it is unfeasible to account for these issues directly when writing the
device tree, as SoC revision can vary between different units of the
same device. This commit introduces a place to adjust DT compatibles
as needed to work around said issues before devices are probed. To
demonstrate usage, two quirks are added for the PLL2 and audio codec
on sun4i.

Signed-off-by: Emilio L?pez <emilio@elopez.com.ar>
---
 arch/arm/mach-sunxi/Makefile |  2 +-
 arch/arm/mach-sunxi/quirks.c | 82 ++++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 83 insertions(+), 1 deletion(-)
 create mode 100644 arch/arm/mach-sunxi/quirks.c

diff --git a/arch/arm/mach-sunxi/Makefile b/arch/arm/mach-sunxi/Makefile
index 589239b..7c13f99 100644
--- a/arch/arm/mach-sunxi/Makefile
+++ b/arch/arm/mach-sunxi/Makefile
@@ -1,2 +1,2 @@
-obj-$(CONFIG_ARCH_SUNXI) += sunxi.o sunxi-soc-id.o
+obj-$(CONFIG_ARCH_SUNXI) += sunxi.o sunxi-soc-id.o quirks.o
 obj-$(CONFIG_SMP) += platsmp.o
diff --git a/arch/arm/mach-sunxi/quirks.c b/arch/arm/mach-sunxi/quirks.c
new file mode 100644
index 0000000..99cdaa0
--- /dev/null
+++ b/arch/arm/mach-sunxi/quirks.c
@@ -0,0 +1,82 @@
+/*
+ * Runtime quirk handling for sunxi SoCs
+ *
+ * Copyright 2014 Emilio L?pez
+ *
+ * Emilio L?pez <emilio@elopez.com.ar>
+ *
+ * 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.
+ */
+
+#include <linux/of.h>
+#include <linux/slab.h>
+#include <linux/string.h>
+
+#include "sunxi-soc-id.h"
+
+static int __init update_compatible_string(const char *oldc, const char *newc)
+{
+	int count = 0;
+	struct property *newprop;
+	size_t newlen = strlen(newc);
+	struct device_node *np = NULL;
+
+	for_each_compatible_node(np, NULL, oldc) {
+		newprop = kzalloc(sizeof(*newprop), GFP_KERNEL);
+		if (!newprop)
+			return -ENOMEM;
+
+		newprop->name = kstrdup("compatible", GFP_KERNEL);
+		newprop->value = kstrdup(newc, GFP_KERNEL);
+		newprop->length = newlen;
+
+		if (!newprop->name || !newprop->value) {
+			kfree(newprop);
+			return -ENOMEM;
+		}
+
+		of_update_property(np, newprop);
+		count++;
+	}
+
+	return count;
+}
+
+static void __init sun4i_pll2_quirk(void)
+{
+	/* Only revision A is affected */
+	if (sunxi_soc_revision() != 'A')
+		return;
+
+	WARN_ON(!update_compatible_string("allwinner,sun4i-a10-b-pll2",
+					  "allwinner,sun4i-a10-a-pll2"));
+}
+
+static void __init sun4i_codec_quirk(void)
+{
+	/* Only revision A is affected */
+	if (sunxi_soc_revision() != 'A')
+		return;
+
+	WARN_ON(!update_compatible_string("allwinner,sun4i-a10-b-codec",
+					  "allwinner,sun4i-a10-a-codec"));
+}
+
+static int __init sunxi_apply_quirks(void)
+{
+	if (of_machine_is_compatible("allwinner,sun4i-a10")) {
+		sun4i_pll2_quirk();
+		sun4i_codec_quirk();
+	}
+
+	return 0;
+}
+postcore_initcall(sunxi_apply_quirks)
-- 
2.0.3

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

* [PATCH 3/9] ARM: sunxi: make sun6i SMP ops static
  2014-07-31 21:28 [PATCH 0/9] Audio clocks for sun[457]i, SoC revision detection Emilio López
  2014-07-31 21:28 ` [PATCH 1/9] ARM: sunxi: introduce SoC identification support Emilio López
  2014-07-31 21:28 ` [PATCH 2/9] ARM: sunxi: quirk support Emilio López
@ 2014-07-31 21:28 ` Emilio López
  2014-08-03 12:41   ` Maxime Ripard
  2014-07-31 21:28 ` [PATCH 4/9] clk: sunxi: PLL2 support for sun4i, sun5i and sun7i Emilio López
                   ` (5 subsequent siblings)
  8 siblings, 1 reply; 36+ messages in thread
From: Emilio López @ 2014-07-31 21:28 UTC (permalink / raw)
  To: linux-arm-kernel

The sun6i SMP ops are currently not marked as static, as reported by
sparse. Let's mark it as such.

Signed-off-by: Emilio L?pez <emilio@elopez.com.ar>
---
 arch/arm/mach-sunxi/platsmp.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/arch/arm/mach-sunxi/platsmp.c b/arch/arm/mach-sunxi/platsmp.c
index c53077b..e44d028 100644
--- a/arch/arm/mach-sunxi/platsmp.c
+++ b/arch/arm/mach-sunxi/platsmp.c
@@ -116,7 +116,7 @@ static int sun6i_smp_boot_secondary(unsigned int cpu,
 	return 0;
 }
 
-struct smp_operations sun6i_smp_ops __initdata = {
+static struct smp_operations sun6i_smp_ops __initdata = {
 	.smp_prepare_cpus	= sun6i_smp_prepare_cpus,
 	.smp_boot_secondary	= sun6i_smp_boot_secondary,
 };
-- 
2.0.3

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

* [PATCH 4/9] clk: sunxi: PLL2 support for sun4i, sun5i and sun7i
  2014-07-31 21:28 [PATCH 0/9] Audio clocks for sun[457]i, SoC revision detection Emilio López
                   ` (2 preceding siblings ...)
  2014-07-31 21:28 ` [PATCH 3/9] ARM: sunxi: make sun6i SMP ops static Emilio López
@ 2014-07-31 21:28 ` Emilio López
  2014-08-03 12:44   ` Maxime Ripard
                     ` (2 more replies)
  2014-07-31 21:28 ` [PATCH 5/9] clk: sunxi: codec clock support Emilio López
                   ` (4 subsequent siblings)
  8 siblings, 3 replies; 36+ messages in thread
From: Emilio López @ 2014-07-31 21:28 UTC (permalink / raw)
  To: linux-arm-kernel

This patch adds support for PLL2 and derivates on sun4i, sun5i and
sun7i SoCs. As this PLL is only used for audio and requires good
accuracy, we only support two known good rates.

Signed-off-by: Emilio L?pez <emilio@elopez.com.ar>
---

Changes from RFC:
 * Add support for A10 rev. A
 * Document compatibles
 * Use fixed factors

 Documentation/devicetree/bindings/clock/sunxi.txt |   2 +
 drivers/clk/sunxi/Makefile                        |   1 +
 drivers/clk/sunxi/clk-a10-pll2.c                  | 243 ++++++++++++++++++++++
 3 files changed, 246 insertions(+)
 create mode 100644 drivers/clk/sunxi/clk-a10-pll2.c

diff --git a/Documentation/devicetree/bindings/clock/sunxi.txt b/Documentation/devicetree/bindings/clock/sunxi.txt
index d3a5c3c..41ada31 100644
--- a/Documentation/devicetree/bindings/clock/sunxi.txt
+++ b/Documentation/devicetree/bindings/clock/sunxi.txt
@@ -10,6 +10,8 @@ Required properties:
 	"allwinner,sun4i-a10-pll1-clk" - for the main PLL clock and PLL4
 	"allwinner,sun6i-a31-pll1-clk" - for the main PLL clock on A31
 	"allwinner,sun8i-a23-pll1-clk" - for the main PLL clock on A23
+	"allwinner,sun4i-a10-a-pll2-clk" - for the PLL2 clock on A10 rev. A
+	"allwinner,sun4i-a10-b-pll2-clk" - for the PLL2 clock on A10 rev. B
 	"allwinner,sun4i-a10-pll5-clk" - for the PLL5 clock
 	"allwinner,sun4i-a10-pll6-clk" - for the PLL6 clock
 	"allwinner,sun6i-a31-pll6-clk" - for the PLL6 clock on A31
diff --git a/drivers/clk/sunxi/Makefile b/drivers/clk/sunxi/Makefile
index 6850cba..dcd5709 100644
--- a/drivers/clk/sunxi/Makefile
+++ b/drivers/clk/sunxi/Makefile
@@ -4,6 +4,7 @@
 
 obj-y += clk-sunxi.o clk-factors.o
 obj-y += clk-a10-hosc.o
+obj-y += clk-a10-pll2.o
 obj-y += clk-a20-gmac.o
 
 obj-$(CONFIG_MFD_SUN6I_PRCM) += \
diff --git a/drivers/clk/sunxi/clk-a10-pll2.c b/drivers/clk/sunxi/clk-a10-pll2.c
new file mode 100644
index 0000000..bcf7d0b
--- /dev/null
+++ b/drivers/clk/sunxi/clk-a10-pll2.c
@@ -0,0 +1,243 @@
+/*
+ * Copyright 2014 Emilio L?pez
+ *
+ * Emilio L?pez <emilio@elopez.com.ar>
+ *
+ * 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.
+ */
+
+#include <linux/clk-provider.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
+#include <linux/slab.h>
+
+#define SUN4I_PLL2_ENABLE		31
+#define SUN4I_PLL2_A_VCOBIAS		0
+#define SUN4I_PLL2_A_VCOBIAS_MASK	0x1F
+#define SUN4I_PLL2_A_N			7
+#define SUN4I_PLL2_A_N_MASK		0x7F
+#define SUN4I_PLL2_B_POST_DIV		26
+#define SUN4I_PLL2_B_POST_DIV_MASK	0xF
+#define SUN4I_PLL2_B_N			8
+#define SUN4I_PLL2_B_N_MASK		0x7F
+#define SUN4I_PLL2_B_PRE_DIV		0
+#define SUN4I_PLL2_B_PRE_DIV_MASK	0x1F
+
+#define SUN4I_PLL2_OUTPUTS		4
+
+struct sun4i_pll2_clk {
+	struct clk_hw hw;
+	void __iomem *reg;
+};
+
+static inline struct sun4i_pll2_clk *to_sun4i_pll2_clk(struct clk_hw *hw)
+{
+	return container_of(hw, struct sun4i_pll2_clk, hw);
+}
+
+static unsigned long sun4i_pll2_recalc_rate_a(struct clk_hw *hw,
+					      unsigned long parent_rate)
+{
+	struct sun4i_pll2_clk *clk = to_sun4i_pll2_clk(hw);
+	int vcobias, n;
+	u32 val;
+
+	val = readl(clk->reg);
+	vcobias = (val >> SUN4I_PLL2_A_VCOBIAS) & SUN4I_PLL2_A_VCOBIAS_MASK;
+	n = (val >> SUN4I_PLL2_A_N) & SUN4I_PLL2_A_N_MASK;
+
+	if (vcobias == 10 && n == 94)
+		return 22579200;
+	else if (vcobias == 9 && n == 83)
+		return 24576000;
+
+	/*
+	 * Unfortunately we don't really have much documentation on how
+	 * these factors relate mathematically
+	 */
+	return 0;
+}
+
+static unsigned long sun4i_pll2_recalc_rate_b(struct clk_hw *hw,
+					      unsigned long parent_rate)
+{
+	struct sun4i_pll2_clk *clk = to_sun4i_pll2_clk(hw);
+	int n, prediv, postdiv;
+	u32 val;
+
+	val = readl(clk->reg);
+	n = (val >> SUN4I_PLL2_B_N) & SUN4I_PLL2_B_N_MASK;
+	prediv = (val >> SUN4I_PLL2_B_PRE_DIV) & SUN4I_PLL2_B_PRE_DIV_MASK;
+	postdiv = (val >> SUN4I_PLL2_B_POST_DIV) & SUN4I_PLL2_B_POST_DIV_MASK;
+
+	/* 0 is a special case and means 1 */
+	if (n == 0)
+		n = 1;
+	if (prediv == 0)
+		prediv = 1;
+	if (postdiv == 0)
+		postdiv = 1;
+
+	return ((parent_rate * n) / prediv) / postdiv;
+}
+
+static long sun4i_pll2_round_rate(struct clk_hw *hw, unsigned long rate,
+				  unsigned long *parent_rate)
+{
+	/*
+	 * There is only two interesting rates for the audio PLL, the
+	 * rest isn't really usable due to accuracy concerns. Therefore,
+	 * we specifically round to those rates here
+	 */
+	if (rate < 22579200)
+		return -EINVAL;
+
+	if (rate >= 22579200 && rate < 24576000)
+		return 22579200;
+
+	return 24576000;
+}
+
+static int sun4i_pll2_set_rate_a(struct clk_hw *hw, unsigned long rate,
+				 unsigned long parent_rate)
+{
+	struct sun4i_pll2_clk *clk = to_sun4i_pll2_clk(hw);
+	u32 val = readl(clk->reg);
+
+	val &= ~(SUN4I_PLL2_A_VCOBIAS_MASK << SUN4I_PLL2_A_VCOBIAS);
+	val &= ~(SUN4I_PLL2_A_N_MASK << SUN4I_PLL2_A_N);
+
+	if (rate == 22579200)
+		val |= (10 << SUN4I_PLL2_A_VCOBIAS) | (94 << SUN4I_PLL2_A_N);
+	else if (rate == 24576000)
+		val |= (9 << SUN4I_PLL2_A_VCOBIAS) | (83 << SUN4I_PLL2_A_N);
+	else
+		return -EINVAL;
+
+	writel(val, clk->reg);
+
+	return 0;
+}
+
+static int sun4i_pll2_set_rate_b(struct clk_hw *hw, unsigned long rate,
+				 unsigned long parent_rate)
+{
+	struct sun4i_pll2_clk *clk = to_sun4i_pll2_clk(hw);
+	u32 val = readl(clk->reg);
+
+	val &= ~(SUN4I_PLL2_B_N_MASK << SUN4I_PLL2_B_N);
+	val &= ~(SUN4I_PLL2_B_PRE_DIV_MASK << SUN4I_PLL2_B_PRE_DIV);
+	val &= ~(SUN4I_PLL2_B_POST_DIV_MASK << SUN4I_PLL2_B_POST_DIV);
+
+	val |= (21 << SUN4I_PLL2_B_PRE_DIV) | (4 << SUN4I_PLL2_B_POST_DIV);
+
+	if (rate == 22579200)
+		val |= (79 << SUN4I_PLL2_B_N);
+	else if (rate == 24576000)
+		val |= (86 << SUN4I_PLL2_B_N);
+	else
+		return -EINVAL;
+
+	writel(val, clk->reg);
+
+	return 0;
+}
+
+static const struct clk_ops sun4i_pll2_ops_a = {
+	.recalc_rate = sun4i_pll2_recalc_rate_a,
+	.round_rate = sun4i_pll2_round_rate,
+	.set_rate = sun4i_pll2_set_rate_a,
+};
+
+
+static const struct clk_ops sun4i_pll2_ops_b = {
+	.recalc_rate = sun4i_pll2_recalc_rate_b,
+	.round_rate = sun4i_pll2_round_rate,
+	.set_rate = sun4i_pll2_set_rate_b,
+};
+
+static const struct of_device_id pll2_matches[] __initconst = {
+	{ .compatible = "allwinner,sun4i-a10-a-pll2-clk", .data = &sun4i_pll2_ops_a },
+	{ .compatible = "allwinner,sun4i-a10-b-pll2-clk", .data = &sun4i_pll2_ops_b },
+	{ /* sentinel */ },
+};
+
+static void __init sun4i_pll2_setup(struct device_node *np)
+{
+	const char *clk_name = np->name, *parent;
+	const struct of_device_id *match;
+	struct clk_onecell_data *clk_data;
+	const struct clk_ops *pll2_ops;
+	struct sun4i_pll2_clk *pll2;
+	struct clk_gate *gate;
+	struct clk **clks;
+	void __iomem *reg;
+
+	/* Choose the correct ops for pll2 */
+	match = of_match_node(pll2_matches, np);
+	pll2_ops = match->data;
+
+	pll2 = kzalloc(sizeof(*pll2), GFP_KERNEL);
+	gate = kzalloc(sizeof(*gate), GFP_KERNEL);
+	clk_data = kzalloc(sizeof(*clk_data), GFP_KERNEL);
+	clks = kcalloc(SUN4I_PLL2_OUTPUTS, sizeof(*clks), GFP_KERNEL);
+	if (!pll2 || !gate || !clk_data || !clks)
+		goto free_mem;
+
+	reg = of_iomap(np, 0);
+	parent = of_clk_get_parent_name(np, 0);
+	of_property_read_string_index(np, "clock-output-names", 0, &clk_name);
+
+	pll2->reg = reg;
+	gate->reg = reg;
+	gate->bit_idx = SUN4I_PLL2_ENABLE;
+
+	/* PLL2, also known as PLL2x1 */
+	of_property_read_string_index(np, "clock-output-names", 0, &clk_name);
+	clks[0] = clk_register_composite(NULL, clk_name, &parent, 1, NULL, NULL,
+					 &pll2->hw, pll2_ops,
+					 &gate->hw, &clk_gate_ops, 0);
+	WARN_ON(IS_ERR(clks[0]));
+	clk_set_rate(clks[0], 22579200);
+	parent = clk_name;
+
+	/* PLL2x2, 1/4 the rate of PLL2x8 */
+	of_property_read_string_index(np, "clock-output-names", 1, &clk_name);
+	clks[1] = clk_register_fixed_factor(NULL, clk_name, parent,
+					    CLK_SET_RATE_PARENT, 2, 1);
+	WARN_ON(IS_ERR(clks[1]));
+
+	/* PLL2x4, 1/2 the rate of PLL2x8 */
+	of_property_read_string_index(np, "clock-output-names", 2, &clk_name);
+	clks[2] = clk_register_fixed_factor(NULL, clk_name, parent,
+					    CLK_SET_RATE_PARENT, 4, 1);
+	WARN_ON(IS_ERR(clks[2]));
+
+	/* PLL2x8, double of PLL2 without the post divisor */
+	of_property_read_string_index(np, "clock-output-names", 3, &clk_name);
+	clks[3] = clk_register_fixed_factor(NULL, clk_name, parent,
+					    CLK_SET_RATE_PARENT, 2 * 4, 1);
+	WARN_ON(IS_ERR(clks[3]));
+
+	clk_data->clks = clks;
+	clk_data->clk_num = SUN4I_PLL2_OUTPUTS;
+	of_clk_add_provider(np, of_clk_src_onecell_get, clk_data);
+
+	return;
+
+free_mem:
+	kfree(pll2);
+	kfree(gate);
+	kfree(clk_data);
+	kfree(clks);
+}
+CLK_OF_DECLARE(sun4i_pll2_a, "allwinner,sun4i-a10-a-pll2-clk", sun4i_pll2_setup);
+CLK_OF_DECLARE(sun4i_pll2_b, "allwinner,sun4i-a10-b-pll2-clk", sun4i_pll2_setup);
-- 
2.0.3

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

* [PATCH 5/9] clk: sunxi: codec clock support
  2014-07-31 21:28 [PATCH 0/9] Audio clocks for sun[457]i, SoC revision detection Emilio López
                   ` (3 preceding siblings ...)
  2014-07-31 21:28 ` [PATCH 4/9] clk: sunxi: PLL2 support for sun4i, sun5i and sun7i Emilio López
@ 2014-07-31 21:28 ` Emilio López
  2014-07-31 21:28 ` [PATCH 6/9] clk: sunxi: mod1 " Emilio López
                   ` (3 subsequent siblings)
  8 siblings, 0 replies; 36+ messages in thread
From: Emilio López @ 2014-07-31 21:28 UTC (permalink / raw)
  To: linux-arm-kernel

The codec clock on sun4i, sun5i and sun7i is a simple gate with PLL2 as
parent.

Signed-off-by: Emilio L?pez <emilio@elopez.com.ar>
Acked-by: Maxime Ripard <maxime.ripard@free-electrons.com>
---

Changes from RFC:
 * Document the compatible used
 * Collect ack from Maxime

 Documentation/devicetree/bindings/clock/sunxi.txt |  1 +
 drivers/clk/sunxi/Makefile                        |  1 +
 drivers/clk/sunxi/clk-a10-codec.c                 | 41 +++++++++++++++++++++++
 3 files changed, 43 insertions(+)
 create mode 100644 drivers/clk/sunxi/clk-a10-codec.c

diff --git a/Documentation/devicetree/bindings/clock/sunxi.txt b/Documentation/devicetree/bindings/clock/sunxi.txt
index 41ada31..d18b89b 100644
--- a/Documentation/devicetree/bindings/clock/sunxi.txt
+++ b/Documentation/devicetree/bindings/clock/sunxi.txt
@@ -48,6 +48,7 @@ Required properties:
 	"allwinner,sun6i-a31-apb2-div-clk" - for the APB2 gates on A31
 	"allwinner,sun6i-a31-apb2-gates-clk" - for the APB2 gates on A31
 	"allwinner,sun8i-a23-apb2-gates-clk" - for the APB2 gates on A23
+	"allwinner,sun4i-a10-codec-clk" - for the codec clock on A10
 	"allwinner,sun4i-a10-mod0-clk" - for the module 0 family of clocks
 	"allwinner,sun7i-a20-out-clk" - for the external output clocks
 	"allwinner,sun7i-a20-gmac-clk" - for the GMAC clock module on A20/A31
diff --git a/drivers/clk/sunxi/Makefile b/drivers/clk/sunxi/Makefile
index dcd5709..a7a96f8 100644
--- a/drivers/clk/sunxi/Makefile
+++ b/drivers/clk/sunxi/Makefile
@@ -3,6 +3,7 @@
 #
 
 obj-y += clk-sunxi.o clk-factors.o
+obj-y += clk-a10-codec.o
 obj-y += clk-a10-hosc.o
 obj-y += clk-a10-pll2.o
 obj-y += clk-a20-gmac.o
diff --git a/drivers/clk/sunxi/clk-a10-codec.c b/drivers/clk/sunxi/clk-a10-codec.c
new file mode 100644
index 0000000..ab4d948
--- /dev/null
+++ b/drivers/clk/sunxi/clk-a10-codec.c
@@ -0,0 +1,41 @@
+/*
+ * Copyright 2014 Emilio L?pez
+ *
+ * Emilio L?pez <emilio@elopez.com.ar>
+ *
+ * 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.
+ */
+
+#include <linux/clk-provider.h>
+#include <linux/clkdev.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
+
+#define SUN4I_CODEC_GATE	31
+
+static void __init sun4i_codec_clk_setup(struct device_node *node)
+{
+	struct clk *clk;
+	const char *clk_name = node->name, *parent_name;
+	void __iomem *reg;
+
+	of_property_read_string(node, "clock-output-names", &clk_name);
+	parent_name = of_clk_get_parent_name(node, 0);
+	reg = of_iomap(node, 0);
+
+	clk = clk_register_gate(NULL, clk_name, parent_name,
+				CLK_SET_RATE_PARENT, reg,
+				SUN4I_CODEC_GATE, 0, NULL);
+
+	if (!IS_ERR(clk))
+		of_clk_add_provider(node, of_clk_src_simple_get, clk);
+}
+CLK_OF_DECLARE(sun4i_codec, "allwinner,sun4i-a10-codec-clk", sun4i_codec_clk_setup);
-- 
2.0.3

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

* [PATCH 6/9] clk: sunxi: mod1 clock support
  2014-07-31 21:28 [PATCH 0/9] Audio clocks for sun[457]i, SoC revision detection Emilio López
                   ` (4 preceding siblings ...)
  2014-07-31 21:28 ` [PATCH 5/9] clk: sunxi: codec clock support Emilio López
@ 2014-07-31 21:28 ` Emilio López
  2014-08-03 12:47   ` Maxime Ripard
  2014-07-31 21:28 ` [PATCH 7/9] ARM: sunxi: dt: Add PLL2 support Emilio López
                   ` (2 subsequent siblings)
  8 siblings, 1 reply; 36+ messages in thread
From: Emilio López @ 2014-07-31 21:28 UTC (permalink / raw)
  To: linux-arm-kernel

The module 1 type of clocks consist of a gate and a mux and are used on
the audio blocks to mux and gate the PLL2 outputs for AC97, IIS or
SPDIF. This commit adds support for them on the sunxi clock driver.

Not-signed-off-by: Emilio L?pez <emilio@elopez.com.ar>
---

Changes from RFC:
 * Document compatible used

 Documentation/devicetree/bindings/clock/sunxi.txt |  1 +
 drivers/clk/sunxi/Makefile                        |  1 +
 drivers/clk/sunxi/clk-a10-mod1.c                  | 69 +++++++++++++++++++++++
 3 files changed, 71 insertions(+)
 create mode 100644 drivers/clk/sunxi/clk-a10-mod1.c

diff --git a/Documentation/devicetree/bindings/clock/sunxi.txt b/Documentation/devicetree/bindings/clock/sunxi.txt
index d18b89b..b028ee2 100644
--- a/Documentation/devicetree/bindings/clock/sunxi.txt
+++ b/Documentation/devicetree/bindings/clock/sunxi.txt
@@ -50,6 +50,7 @@ Required properties:
 	"allwinner,sun8i-a23-apb2-gates-clk" - for the APB2 gates on A23
 	"allwinner,sun4i-a10-codec-clk" - for the codec clock on A10
 	"allwinner,sun4i-a10-mod0-clk" - for the module 0 family of clocks
+	"allwinner,sun4i-a10-mod1-clk" - for the module 1 family of clocks
 	"allwinner,sun7i-a20-out-clk" - for the external output clocks
 	"allwinner,sun7i-a20-gmac-clk" - for the GMAC clock module on A20/A31
 	"allwinner,sun4i-a10-usb-clk" - for usb gates + resets on A10 / A20
diff --git a/drivers/clk/sunxi/Makefile b/drivers/clk/sunxi/Makefile
index a7a96f8..75d113d 100644
--- a/drivers/clk/sunxi/Makefile
+++ b/drivers/clk/sunxi/Makefile
@@ -5,6 +5,7 @@
 obj-y += clk-sunxi.o clk-factors.o
 obj-y += clk-a10-codec.o
 obj-y += clk-a10-hosc.o
+obj-y += clk-a10-mod1.o
 obj-y += clk-a10-pll2.o
 obj-y += clk-a20-gmac.o
 
diff --git a/drivers/clk/sunxi/clk-a10-mod1.c b/drivers/clk/sunxi/clk-a10-mod1.c
new file mode 100644
index 0000000..09afd54
--- /dev/null
+++ b/drivers/clk/sunxi/clk-a10-mod1.c
@@ -0,0 +1,69 @@
+/*
+ * Copyright 2014 Emilio L?pez
+ *
+ * Emilio L?pez <emilio@elopez.com.ar>
+ *
+ * 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.
+ */
+
+#include <linux/clk-provider.h>
+#include <linux/clkdev.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
+
+static DEFINE_SPINLOCK(mod1_lock);
+
+#define SUN4I_MOD1_ENABLE	31
+#define SUN4I_MOD1_MUX		16
+#define SUN4I_MOD1_MUX_WIDTH	2
+#define SUN4I_MOD1_MAX_PARENTS	4
+
+static void __init sun4i_mod1_clk_setup(struct device_node *node)
+{
+	struct clk *clk;
+	struct clk_mux *mux;
+	struct clk_gate *gate;
+	const char *parents[4];
+	const char *clk_name = node->name;
+	void __iomem *reg;
+	int i = 0;
+
+	mux = kzalloc(sizeof(*mux), GFP_KERNEL);
+	gate = kzalloc(sizeof(*gate), GFP_KERNEL);
+	if (!mux || !gate) {
+		kfree(mux);
+		kfree(gate);
+		return;
+	}
+
+	of_property_read_string(node, "clock-output-names", &clk_name);
+	reg = of_iomap(node, 0);
+
+	while (i < SUN4I_MOD1_MAX_PARENTS &&
+	       (parents[i] = of_clk_get_parent_name(node, i)) != NULL)
+		i++;
+
+	gate->reg = reg;
+	gate->bit_idx = SUN4I_MOD1_ENABLE;
+	gate->lock = &mod1_lock;
+	mux->reg = reg;
+	mux->shift = SUN4I_MOD1_MUX;
+	mux->mask = BIT(SUN4I_MOD1_MUX_WIDTH) - 1;
+	mux->lock = &mod1_lock;
+
+	clk = clk_register_composite(NULL, clk_name, parents, i,
+				     &mux->hw, &clk_mux_ops,
+				     NULL, NULL,
+				     &gate->hw, &clk_gate_ops, 0);
+	if (!IS_ERR(clk))
+		of_clk_add_provider(node, of_clk_src_simple_get, clk);
+}
+CLK_OF_DECLARE(sun4i_mod1, "allwinner,sun4i-a10-mod1-clk", sun4i_mod1_clk_setup);
-- 
2.0.3

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

* [PATCH 7/9] ARM: sunxi: dt: Add PLL2 support
  2014-07-31 21:28 [PATCH 0/9] Audio clocks for sun[457]i, SoC revision detection Emilio López
                   ` (5 preceding siblings ...)
  2014-07-31 21:28 ` [PATCH 6/9] clk: sunxi: mod1 " Emilio López
@ 2014-07-31 21:28 ` Emilio López
  2014-07-31 21:46   ` jonsmirl at gmail.com
  2014-07-31 21:28 ` [PATCH 8/9] ARM: sunxi: dt: Add codec clock support Emilio López
  2014-07-31 21:28 ` [PATCH 9/9] ARM: sun7i: dt: Add mod1 clock nodes Emilio López
  8 siblings, 1 reply; 36+ messages in thread
From: Emilio López @ 2014-07-31 21:28 UTC (permalink / raw)
  To: linux-arm-kernel

This commit adds the PLL2 definition to the sun4i, sun5i and sun7i
device trees. PLL2 is used to clock audio devices.

Signed-off-by: Emilio L?pez <emilio@elopez.com.ar>
---

Changes from RFC:
 * A10 rev A is now supported via the quirk code

 arch/arm/boot/dts/sun4i-a10.dtsi  | 8 ++++++++
 arch/arm/boot/dts/sun5i-a10s.dtsi | 8 ++++++++
 arch/arm/boot/dts/sun5i-a13.dtsi  | 8 ++++++++
 arch/arm/boot/dts/sun7i-a20.dtsi  | 8 ++++++++
 4 files changed, 32 insertions(+)

diff --git a/arch/arm/boot/dts/sun4i-a10.dtsi b/arch/arm/boot/dts/sun4i-a10.dtsi
index 459cb63..c4d4c73 100644
--- a/arch/arm/boot/dts/sun4i-a10.dtsi
+++ b/arch/arm/boot/dts/sun4i-a10.dtsi
@@ -81,6 +81,14 @@
 			clock-output-names = "pll1";
 		};
 
+		pll2: clk at 01c20008 {
+			#clock-cells = <1>;
+			compatible = "allwinner,sun4i-a10-b-pll2-clk";
+			reg = <0x01c20008 0x4>;
+			clocks = <&osc24M>;
+			clock-output-names = "pll2", "pll2x2", "pll2x4", "pll2x8";
+		};
+
 		pll4: clk at 01c20018 {
 			#clock-cells = <0>;
 			compatible = "allwinner,sun4i-a10-pll1-clk";
diff --git a/arch/arm/boot/dts/sun5i-a10s.dtsi b/arch/arm/boot/dts/sun5i-a10s.dtsi
index 24b0ad3..f35aa4d 100644
--- a/arch/arm/boot/dts/sun5i-a10s.dtsi
+++ b/arch/arm/boot/dts/sun5i-a10s.dtsi
@@ -74,6 +74,14 @@
 			clock-output-names = "pll1";
 		};
 
+		pll2: clk at 01c20008 {
+			#clock-cells = <1>;
+			compatible = "allwinner,sun4i-a10-b-pll2-clk";
+			reg = <0x01c20008 0x4>;
+			clocks = <&osc24M>;
+			clock-output-names = "pll2", "pll2x2", "pll2x4", "pll2x8";
+		};
+
 		pll4: clk at 01c20018 {
 			#clock-cells = <0>;
 			compatible = "allwinner,sun4i-a10-pll1-clk";
diff --git a/arch/arm/boot/dts/sun5i-a13.dtsi b/arch/arm/boot/dts/sun5i-a13.dtsi
index bf86e65..38b2d77 100644
--- a/arch/arm/boot/dts/sun5i-a13.dtsi
+++ b/arch/arm/boot/dts/sun5i-a13.dtsi
@@ -75,6 +75,14 @@
 			clock-output-names = "pll1";
 		};
 
+		pll2: clk at 01c20008 {
+			#clock-cells = <1>;
+			compatible = "allwinner,sun4i-a10-b-pll2-clk";
+			reg = <0x01c20008 0x4>;
+			clocks = <&osc24M>;
+			clock-output-names = "pll2", "pll2x2", "pll2x4", "pll2x8";
+		};
+
 		pll4: clk at 01c20018 {
 			#clock-cells = <0>;
 			compatible = "allwinner,sun4i-a10-pll1-clk";
diff --git a/arch/arm/boot/dts/sun7i-a20.dtsi b/arch/arm/boot/dts/sun7i-a20.dtsi
index 4011628..7a6a18c 100644
--- a/arch/arm/boot/dts/sun7i-a20.dtsi
+++ b/arch/arm/boot/dts/sun7i-a20.dtsi
@@ -91,6 +91,14 @@
 			clock-output-names = "pll1";
 		};
 
+		pll2: clk at 01c20008 {
+			#clock-cells = <1>;
+			compatible = "allwinner,sun4i-a10-b-pll2-clk";
+			reg = <0x01c20008 0x4>;
+			clocks = <&osc24M>;
+			clock-output-names = "pll2", "pll2x2", "pll2x4", "pll2x8";
+		};
+
 		pll4: clk at 01c20018 {
 			#clock-cells = <0>;
 			compatible = "allwinner,sun7i-a20-pll4-clk";
-- 
2.0.3

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

* [PATCH 8/9] ARM: sunxi: dt: Add codec clock support
  2014-07-31 21:28 [PATCH 0/9] Audio clocks for sun[457]i, SoC revision detection Emilio López
                   ` (6 preceding siblings ...)
  2014-07-31 21:28 ` [PATCH 7/9] ARM: sunxi: dt: Add PLL2 support Emilio López
@ 2014-07-31 21:28 ` Emilio López
  2014-07-31 21:28 ` [PATCH 9/9] ARM: sun7i: dt: Add mod1 clock nodes Emilio López
  8 siblings, 0 replies; 36+ messages in thread
From: Emilio López @ 2014-07-31 21:28 UTC (permalink / raw)
  To: linux-arm-kernel

This commit adds the codec clock definition to the sun4i, sun5i and
sun7i device trees. The codec clock is used in the analog codec block.

Signed-off-by: Emilio L?pez <emilio@elopez.com.ar>
---
 arch/arm/boot/dts/sun4i-a10.dtsi  | 8 ++++++++
 arch/arm/boot/dts/sun5i-a10s.dtsi | 8 ++++++++
 arch/arm/boot/dts/sun5i-a13.dtsi  | 8 ++++++++
 arch/arm/boot/dts/sun7i-a20.dtsi  | 8 ++++++++
 4 files changed, 32 insertions(+)

diff --git a/arch/arm/boot/dts/sun4i-a10.dtsi b/arch/arm/boot/dts/sun4i-a10.dtsi
index c4d4c73..215cd1c 100644
--- a/arch/arm/boot/dts/sun4i-a10.dtsi
+++ b/arch/arm/boot/dts/sun4i-a10.dtsi
@@ -339,6 +339,14 @@
 			clocks = <&osc24M>, <&pll6 1>, <&pll5 1>;
 			clock-output-names = "spi3";
 		};
+
+		codec_clk: clk at 01c20140 {
+			#clock-cells = <0>;
+			compatible = "allwinner,sun4i-a10-codec-clk";
+			reg = <0x01c20140 0x4>;
+			clocks = <&pll2 0>;
+			clock-output-names = "codec";
+		};
 	};
 
 	soc at 01c00000 {
diff --git a/arch/arm/boot/dts/sun5i-a10s.dtsi b/arch/arm/boot/dts/sun5i-a10s.dtsi
index f35aa4d..289c754 100644
--- a/arch/arm/boot/dts/sun5i-a10s.dtsi
+++ b/arch/arm/boot/dts/sun5i-a10s.dtsi
@@ -293,6 +293,14 @@
 			clock-output-names = "usb_ohci0", "usb_phy";
 		};
 
+		codec_clk: clk at 01c20140 {
+			#clock-cells = <0>;
+			compatible = "allwinner,sun4i-a10-codec-clk";
+			reg = <0x01c20140 0x4>;
+			clocks = <&pll2 0>;
+			clock-output-names = "codec";
+		};
+
 		mbus_clk: clk at 01c2015c {
 			#clock-cells = <0>;
 			compatible = "allwinner,sun4i-a10-mod0-clk";
diff --git a/arch/arm/boot/dts/sun5i-a13.dtsi b/arch/arm/boot/dts/sun5i-a13.dtsi
index 38b2d77..f82b2fd 100644
--- a/arch/arm/boot/dts/sun5i-a13.dtsi
+++ b/arch/arm/boot/dts/sun5i-a13.dtsi
@@ -291,6 +291,14 @@
 			clock-output-names = "usb_ohci0", "usb_phy";
 		};
 
+		codec_clk: clk at 01c20140 {
+			#clock-cells = <0>;
+			compatible = "allwinner,sun4i-a10-codec-clk";
+			reg = <0x01c20140 0x4>;
+			clocks = <&pll2 0>;
+			clock-output-names = "codec";
+		};
+
 		mbus_clk: clk at 01c2015c {
 			#clock-cells = <0>;
 			compatible = "allwinner,sun4i-a10-mod0-clk";
diff --git a/arch/arm/boot/dts/sun7i-a20.dtsi b/arch/arm/boot/dts/sun7i-a20.dtsi
index 7a6a18c..c38ac5e 100644
--- a/arch/arm/boot/dts/sun7i-a20.dtsi
+++ b/arch/arm/boot/dts/sun7i-a20.dtsi
@@ -352,6 +352,14 @@
 			clock-output-names = "spi3";
 		};
 
+		codec_clk: clk at 01c20140 {
+			#clock-cells = <0>;
+			compatible = "allwinner,sun4i-a10-codec-clk";
+			reg = <0x01c20140 0x4>;
+			clocks = <&pll2 0>;
+			clock-output-names = "codec";
+		};
+
 		mbus_clk: clk at 01c2015c {
 			#clock-cells = <0>;
 			compatible = "allwinner,sun4i-a10-mod0-clk";
-- 
2.0.3

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

* [PATCH 9/9] ARM: sun7i: dt: Add mod1 clock nodes
  2014-07-31 21:28 [PATCH 0/9] Audio clocks for sun[457]i, SoC revision detection Emilio López
                   ` (7 preceding siblings ...)
  2014-07-31 21:28 ` [PATCH 8/9] ARM: sunxi: dt: Add codec clock support Emilio López
@ 2014-07-31 21:28 ` Emilio López
  8 siblings, 0 replies; 36+ messages in thread
From: Emilio López @ 2014-07-31 21:28 UTC (permalink / raw)
  To: linux-arm-kernel

This commit adds all the mod1 clocks available on A20 to its device
tree. This list was created by looking at the A20 user manual.

Not-signed-off-by: Emilio L?pez <emilio@elopez.com.ar>
---

Changes from RFC:
 * iis -> i2s
 * Reordered parent clocks as reported by Chen-Yu

 arch/arm/boot/dts/sun7i-a20.dtsi | 40 ++++++++++++++++++++++++++++++++++++++++
 1 file changed, 40 insertions(+)

diff --git a/arch/arm/boot/dts/sun7i-a20.dtsi b/arch/arm/boot/dts/sun7i-a20.dtsi
index c38ac5e..a6db15d 100644
--- a/arch/arm/boot/dts/sun7i-a20.dtsi
+++ b/arch/arm/boot/dts/sun7i-a20.dtsi
@@ -335,6 +335,30 @@
 			clock-output-names = "ir1";
 		};
 
+		i2s0_clk: clk at 01c200b8 {
+			#clock-cells = <0>;
+			compatible = "allwinner,sun4i-a10-mod1-clk";
+			reg = <0x01c200b8 0x4>;
+			clocks = <&pll2 3>, <&pll2 2>, <&pll2 1>, <&pll2 0>;
+			clock-output-names = "i2s0";
+		};
+
+		ac97_clk: clk at 01c200bc {
+			#clock-cells = <0>;
+			compatible = "allwinner,sun4i-a10-mod1-clk";
+			reg = <0x01c200bc 0x4>;
+			clocks = <&pll2 3>, <&pll2 2>, <&pll2 1>, <&pll2 0>;
+			clock-output-names = "ac97";
+		};
+
+		spdif_clk: clk at 01c200c0 {
+			#clock-cells = <0>;
+			compatible = "allwinner,sun4i-a10-mod1-clk";
+			reg = <0x01c200c0 0x4>;
+			clocks = <&pll2 3>, <&pll2 2>, <&pll2 1>, <&pll2 0>;
+			clock-output-names = "spdif";
+		};
+
 		usb_clk: clk at 01c200cc {
 			#clock-cells = <1>;
 		        #reset-cells = <1>;
@@ -352,6 +376,22 @@
 			clock-output-names = "spi3";
 		};
 
+		i2s1_clk: clk at 01c200d8 {
+			#clock-cells = <0>;
+			compatible = "allwinner,sun4i-a10-mod1-clk";
+			reg = <0x01c200d8 0x4>;
+			clocks = <&pll2 3>, <&pll2 2>, <&pll2 1>, <&pll2 0>;
+			clock-output-names = "i2s1";
+		};
+
+		i2s2_clk: clk at 01c200dc {
+			#clock-cells = <0>;
+			compatible = "allwinner,sun4i-a10-mod1-clk";
+			reg = <0x01c200dc 0x4>;
+			clocks = <&pll2 3>, <&pll2 2>, <&pll2 1>, <&pll2 0>;
+			clock-output-names = "i2s2";
+		};
+
 		codec_clk: clk at 01c20140 {
 			#clock-cells = <0>;
 			compatible = "allwinner,sun4i-a10-codec-clk";
-- 
2.0.3

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

* [PATCH 7/9] ARM: sunxi: dt: Add PLL2 support
  2014-07-31 21:28 ` [PATCH 7/9] ARM: sunxi: dt: Add PLL2 support Emilio López
@ 2014-07-31 21:46   ` jonsmirl at gmail.com
  2014-08-03 12:50     ` Maxime Ripard
  0 siblings, 1 reply; 36+ messages in thread
From: jonsmirl at gmail.com @ 2014-07-31 21:46 UTC (permalink / raw)
  To: linux-arm-kernel

Would it be better to name this "allwinner,sun4i-a10-pll2-clk" instead
of "allwinner,sun4i-a10-b-pll2-clk"? By encoding the b in it everyone
is going to wonder what to do on the 'c' revision which is the most
common revision.

The revision based rename would then be from
"allwinner,sun4i-a10-pll2-clk" to "allwinner,sun4i-a10-a-pll2-clk".

On Thu, Jul 31, 2014 at 5:28 PM, Emilio L?pez <emilio@elopez.com.ar> wrote:
> This commit adds the PLL2 definition to the sun4i, sun5i and sun7i
> device trees. PLL2 is used to clock audio devices.
>
> Signed-off-by: Emilio L?pez <emilio@elopez.com.ar>
> ---
>
> Changes from RFC:
>  * A10 rev A is now supported via the quirk code
>
>  arch/arm/boot/dts/sun4i-a10.dtsi  | 8 ++++++++
>  arch/arm/boot/dts/sun5i-a10s.dtsi | 8 ++++++++
>  arch/arm/boot/dts/sun5i-a13.dtsi  | 8 ++++++++
>  arch/arm/boot/dts/sun7i-a20.dtsi  | 8 ++++++++
>  4 files changed, 32 insertions(+)
>
> diff --git a/arch/arm/boot/dts/sun4i-a10.dtsi b/arch/arm/boot/dts/sun4i-a10.dtsi
> index 459cb63..c4d4c73 100644
> --- a/arch/arm/boot/dts/sun4i-a10.dtsi
> +++ b/arch/arm/boot/dts/sun4i-a10.dtsi
> @@ -81,6 +81,14 @@
>                         clock-output-names = "pll1";
>                 };
>
> +               pll2: clk at 01c20008 {
> +                       #clock-cells = <1>;
> +                       compatible = "allwinner,sun4i-a10-b-pll2-clk";
> +                       reg = <0x01c20008 0x4>;
> +                       clocks = <&osc24M>;
> +                       clock-output-names = "pll2", "pll2x2", "pll2x4", "pll2x8";
> +               };
> +
>                 pll4: clk at 01c20018 {
>                         #clock-cells = <0>;
>                         compatible = "allwinner,sun4i-a10-pll1-clk";
> diff --git a/arch/arm/boot/dts/sun5i-a10s.dtsi b/arch/arm/boot/dts/sun5i-a10s.dtsi
> index 24b0ad3..f35aa4d 100644
> --- a/arch/arm/boot/dts/sun5i-a10s.dtsi
> +++ b/arch/arm/boot/dts/sun5i-a10s.dtsi
> @@ -74,6 +74,14 @@
>                         clock-output-names = "pll1";
>                 };
>
> +               pll2: clk at 01c20008 {
> +                       #clock-cells = <1>;
> +                       compatible = "allwinner,sun4i-a10-b-pll2-clk";
> +                       reg = <0x01c20008 0x4>;
> +                       clocks = <&osc24M>;
> +                       clock-output-names = "pll2", "pll2x2", "pll2x4", "pll2x8";
> +               };
> +
>                 pll4: clk at 01c20018 {
>                         #clock-cells = <0>;
>                         compatible = "allwinner,sun4i-a10-pll1-clk";
> diff --git a/arch/arm/boot/dts/sun5i-a13.dtsi b/arch/arm/boot/dts/sun5i-a13.dtsi
> index bf86e65..38b2d77 100644
> --- a/arch/arm/boot/dts/sun5i-a13.dtsi
> +++ b/arch/arm/boot/dts/sun5i-a13.dtsi
> @@ -75,6 +75,14 @@
>                         clock-output-names = "pll1";
>                 };
>
> +               pll2: clk at 01c20008 {
> +                       #clock-cells = <1>;
> +                       compatible = "allwinner,sun4i-a10-b-pll2-clk";
> +                       reg = <0x01c20008 0x4>;
> +                       clocks = <&osc24M>;
> +                       clock-output-names = "pll2", "pll2x2", "pll2x4", "pll2x8";
> +               };
> +
>                 pll4: clk at 01c20018 {
>                         #clock-cells = <0>;
>                         compatible = "allwinner,sun4i-a10-pll1-clk";
> diff --git a/arch/arm/boot/dts/sun7i-a20.dtsi b/arch/arm/boot/dts/sun7i-a20.dtsi
> index 4011628..7a6a18c 100644
> --- a/arch/arm/boot/dts/sun7i-a20.dtsi
> +++ b/arch/arm/boot/dts/sun7i-a20.dtsi
> @@ -91,6 +91,14 @@
>                         clock-output-names = "pll1";
>                 };
>
> +               pll2: clk at 01c20008 {
> +                       #clock-cells = <1>;
> +                       compatible = "allwinner,sun4i-a10-b-pll2-clk";
> +                       reg = <0x01c20008 0x4>;
> +                       clocks = <&osc24M>;
> +                       clock-output-names = "pll2", "pll2x2", "pll2x4", "pll2x8";
> +               };
> +
>                 pll4: clk at 01c20018 {
>                         #clock-cells = <0>;
>                         compatible = "allwinner,sun7i-a20-pll4-clk";
> --
> 2.0.3



-- 
Jon Smirl
jonsmirl at gmail.com

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

* [PATCH 1/9] ARM: sunxi: introduce SoC identification support
  2014-07-31 21:28 ` [PATCH 1/9] ARM: sunxi: introduce SoC identification support Emilio López
@ 2014-08-03 12:40   ` Maxime Ripard
  2014-08-03 21:45     ` Emilio López
  0 siblings, 1 reply; 36+ messages in thread
From: Maxime Ripard @ 2014-08-03 12:40 UTC (permalink / raw)
  To: linux-arm-kernel

Hi Emilio,

On Thu, Jul 31, 2014 at 06:28:04PM -0300, Emilio L?pez wrote:
> This commit adds SoC bus support on the sunxi platform, and exposes
> information such as the hardware revision to userspace and other kernel
> clients during init. A message with this information is also printed to
> the kernel log to ease future bug triaging.
> 
> Signed-off-by: Emilio L?pez <emilio@elopez.com.ar>
> ---
>  arch/arm/mach-sunxi/Kconfig        |   1 +
>  arch/arm/mach-sunxi/Makefile       |   2 +-
>  arch/arm/mach-sunxi/sunxi-soc-id.c | 226 +++++++++++++++++++++++++++++++++++++
>  arch/arm/mach-sunxi/sunxi-soc-id.h |   6 +
>  4 files changed, 234 insertions(+), 1 deletion(-)
>  create mode 100644 arch/arm/mach-sunxi/sunxi-soc-id.c
>  create mode 100644 arch/arm/mach-sunxi/sunxi-soc-id.h
> 
> diff --git a/arch/arm/mach-sunxi/Kconfig b/arch/arm/mach-sunxi/Kconfig
> index 6434e3b..4a199df 100644
> --- a/arch/arm/mach-sunxi/Kconfig
> +++ b/arch/arm/mach-sunxi/Kconfig
> @@ -5,6 +5,7 @@ menuconfig ARCH_SUNXI
>  	select GENERIC_IRQ_CHIP
>  	select PINCTRL
>  	select PINCTRL_SUNXI
> +	select SOC_BUS
>  	select SUN4I_TIMER
>  
>  if ARCH_SUNXI
> diff --git a/arch/arm/mach-sunxi/Makefile b/arch/arm/mach-sunxi/Makefile
> index 27b168f..589239b 100644
> --- a/arch/arm/mach-sunxi/Makefile
> +++ b/arch/arm/mach-sunxi/Makefile
> @@ -1,2 +1,2 @@
> -obj-$(CONFIG_ARCH_SUNXI) += sunxi.o
> +obj-$(CONFIG_ARCH_SUNXI) += sunxi.o sunxi-soc-id.o
>  obj-$(CONFIG_SMP) += platsmp.o
> diff --git a/arch/arm/mach-sunxi/sunxi-soc-id.c b/arch/arm/mach-sunxi/sunxi-soc-id.c
> new file mode 100644
> index 0000000..c7eff1c
> --- /dev/null
> +++ b/arch/arm/mach-sunxi/sunxi-soc-id.c
> @@ -0,0 +1,226 @@
> +/*
> + * SoC revision detection for sunxi SoCs
> + *
> + * Copyright 2014 Emilio L?pez
> + *
> + * Emilio L?pez <emilio@elopez.com.ar>
> + *
> + * 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.
> + */
> +
> +#include <linux/err.h>
> +#include <linux/io.h>
> +#include <linux/of.h>
> +#include <linux/of_address.h>
> +#include <linux/slab.h>
> +#include <linux/string.h>
> +#include <linux/sys_soc.h>
> +
> +#include "sunxi-soc-id.h"
> +
> +/*
> + * On the A10 SoC, we can read the revision information from the timer
> + * block. The detection logic is extracted from similar code on the
> + * Allwinner vendor tree, as this is undocumented on the user manual
> + */
> +
> +#define TIMER_SOC_REV_REG		0x13c
> +#define TIMER_SOC_REV_CLEAR(val)	((val) & ~(0x3 << 6))
> +#define TIMER_SOC_REV_GET(val)		(((val) >> 6) & 0x3)
> +
> +static const struct of_device_id sun4i_timer_compatible[] __initconst = {
> +	{ .compatible = "allwinner,sun4i-a10-timer", },
> +	{},
> +};
> +
> +static int __init sun4i_get_soc_revision(void)
> +{
> +	struct device_node *np;
> +	void __iomem *base;
> +	u32 val;
> +	int ret;
> +
> +	/* Find the timer node */
> +	np = of_find_matching_node(NULL, sun4i_timer_compatible);
> +	if (!np)
> +		return -ENODEV;
> +
> +	/* Temporarily map it for reading */
> +	base = of_iomap(np, 0);
> +	if (!base) {
> +		of_node_put(np);
> +		return -ENOMEM;
> +	}
> +
> +	/* Clear the SoC revision bits and rewrite the register */
> +	val = readl(base + TIMER_SOC_REV_REG);
> +	val = TIMER_SOC_REV_CLEAR(val);
> +	writel(val, base + TIMER_SOC_REV_REG);
> +
> +	/* Now read it again and see what shows up */
> +	val = readl(base + TIMER_SOC_REV_REG);
> +	val = TIMER_SOC_REV_GET(val);
> +
> +	switch (val) {
> +	case 0:  /* revision A */
> +		ret = 'A';
> +	case 3:  /* revision B */
> +		ret = 'B';
> +	default: /* revision C */
> +		ret = 'C';

What's programmed in there in the case of the rev C?

> +	}
> +
> +	iounmap(base);
> +	of_node_put(np);
> +
> +	return ret;
> +}
> +
> +/*
> + * On the sun5i SoCs (A10S, A13), we can read the revision information
> + * from the first bits in the Security ID. The detection logic is
> + * extracted from similar code on the Allwinner vendor tree, as this
> + * is undocumented on the user manual.
> + */
> +
> +static const struct of_device_id sun5i_sid_compatible[] __initconst = {
> +	{ .compatible = "allwinner,sun4i-a10-sid", },
> +	{},
> +};
> +
> +static int __init sun5i_get_soc_revision(void)
> +{
> +	struct device_node *np;
> +	void __iomem *sid;
> +	u32 val;
> +	int ret;
> +
> +	/* Find the SID node */
> +	np = of_find_matching_node(NULL, sun5i_sid_compatible);
> +	if (!np)
> +		return -ENODEV;
> +
> +	/* Temporarily map it for reading */
> +	sid = of_iomap(np, 0);
> +	if (!sid) {
> +		of_node_put(np);
> +		return -ENOMEM;
> +	}
> +
> +	/* Read and extract the chip revision from the SID */
> +	val = readl(sid);
> +	val = (val >> 8) & 0xffffff;
> +
> +	switch (val) {
> +	case 0:        /* A10S/A13 rev A */
> +	case 0x162541: /* A10S/A13 rev A */
> +	case 0x162565: /* A13 rev A */

Some defines would be nice.

Also, isn't the SID supposed to identify any SoC, not just the sun5i?

> +		ret = 'A';
> +		break;
> +	case 0x162542: /* A10S/A13 rev B */
> +		ret = 'B';
> +		break;
> +	default:       /* Unknown chip revision */
> +		ret = -ENODATA;
> +	}
> +
> +	iounmap(sid);
> +	of_node_put(np);
> +
> +	return ret;
> +}
> +
> +int __init sunxi_soc_revision(void)
> +{
> +	static int revision = -ENODEV;
> +
> +	/* Try to query the hardware just once */
> +	if (!IS_ERR_VALUE(revision))
> +		return revision;
> +
> +	if (of_machine_is_compatible("allwinner,sun4i-a10")) {
> +		revision = sun4i_get_soc_revision();
> +	} else if (of_machine_is_compatible("allwinner,sun5i-a10s") ||
> +		   of_machine_is_compatible("allwinner,sun5i-a13")) {
> +		revision = sun5i_get_soc_revision();
> +	}
> +
> +	return revision;
> +}
> +
> +/* Matches for the sunxi SoCs we know of */
> +static const struct of_device_id soc_matches[] __initconst = {
> +	{ .compatible = "allwinner,sun4i-a10", .data = "A10 (sun4i)" },
> +	{ .compatible = "allwinner,sun5i-a13", .data = "A13 (sun5i)" },
> +	{ .compatible = "allwinner,sun5i-a10s", .data = "A10S (sun5i)" },
> +	{ .compatible = "allwinner,sun6i-a31", .data = "A31 (sun6i)" },
> +	{ .compatible = "allwinner,sun7i-a20", .data = "A20 (sun7i)" },
> +	{ .compatible = "allwinner,sun8i-a23", .data = "A23 (sun8i)" },
> +	{ /* sentinel */ },
> +};

Hmmm, no, either auto-detect it, or don't, but that's useless. We can
already have the same information from the DT directly.

> +static int __init sunxi_register_soc_device(void)
> +{
> +	struct soc_device_attribute *soc_dev_attr;
> +	struct soc_device *soc_dev;
> +	const struct of_device_id *match;
> +	struct device_node *root;
> +	int revision;
> +
> +	/* Only run on sunxi SoCs that we know of */
> +	root = of_find_node_by_path("/");
> +	match = of_match_node(soc_matches, root);
> +	if (!match)
> +		goto exit;
> +
> +	soc_dev_attr = kzalloc(sizeof(*soc_dev_attr), GFP_KERNEL);
> +	if (!soc_dev_attr)
> +		goto exit;
> +
> +	/* Read the machine name if available */
> +	of_property_read_string(root, "model", &soc_dev_attr->machine);
> +
> +	soc_dev_attr->family = kstrdup("Allwinner A Series", GFP_KERNEL);

I think the family should be sun*i

> +	soc_dev_attr->soc_id = kstrdup(match->data, GFP_KERNEL);

And soc_id would be just the name of the SoC.

> +
> +	/* Revision may not always be available */
> +	revision = sunxi_soc_revision();
> +	if (IS_ERR_VALUE(revision))
> +		soc_dev_attr->revision = kstrdup("Unknown", GFP_KERNEL);
> +	else
> +		soc_dev_attr->revision = kasprintf(GFP_KERNEL, "%c", revision);
> +
> +	soc_dev = soc_device_register(soc_dev_attr);
> +	if (IS_ERR(soc_dev))
> +		goto free_struct;
> +
> +	/*
> +	 * Print an informational line mentioning the hardware details.
> +	 * It may come in handy during bug reports, as some early SoC
> +	 * revisions have hardware quirks and do not get much testing.
> +	 */
> +	pr_info("SoC bus registered, running %s %s, revision %s\n",
> +		soc_dev_attr->family, soc_dev_attr->soc_id,
> +		soc_dev_attr->revision);

Maybe a pr_fmt would be nice here.


> +
> +	return 0;
> +
> +free_struct:
> +	kfree(soc_dev_attr->family);
> +	kfree(soc_dev_attr->soc_id);
> +	kfree(soc_dev_attr->revision);
> +	kfree(soc_dev_attr);
> +exit:
> +	of_node_put(root);
> +
> +	return 0;
> +}
> +postcore_initcall(sunxi_register_soc_device)

I'm kind of reluctant to this. That would mean that it will run for
every SoC.

Can't you tie it to the system controller, and have it probed as
usual?

-- 
Maxime Ripard, Free Electrons
Embedded Linux, Kernel and Android engineering
http://free-electrons.com
-------------- next part --------------
A non-text attachment was scrubbed...
Name: signature.asc
Type: application/pgp-signature
Size: 819 bytes
Desc: Digital signature
URL: <http://lists.infradead.org/pipermail/linux-arm-kernel/attachments/20140803/2a956bf9/attachment-0001.sig>

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

* [PATCH 3/9] ARM: sunxi: make sun6i SMP ops static
  2014-07-31 21:28 ` [PATCH 3/9] ARM: sunxi: make sun6i SMP ops static Emilio López
@ 2014-08-03 12:41   ` Maxime Ripard
  0 siblings, 0 replies; 36+ messages in thread
From: Maxime Ripard @ 2014-08-03 12:41 UTC (permalink / raw)
  To: linux-arm-kernel

On Thu, Jul 31, 2014 at 06:28:06PM -0300, Emilio L?pez wrote:
> The sun6i SMP ops are currently not marked as static, as reported by
> sparse. Let's mark it as such.
> 
> Signed-off-by: Emilio L?pez <emilio@elopez.com.ar>

That patch has nothing to do in this set. Please send it separately.

Maxime

-- 
Maxime Ripard, Free Electrons
Embedded Linux, Kernel and Android engineering
http://free-electrons.com
-------------- next part --------------
A non-text attachment was scrubbed...
Name: signature.asc
Type: application/pgp-signature
Size: 819 bytes
Desc: Digital signature
URL: <http://lists.infradead.org/pipermail/linux-arm-kernel/attachments/20140803/9fd0e1c1/attachment.sig>

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

* [PATCH 2/9] ARM: sunxi: quirk support
  2014-07-31 21:28 ` [PATCH 2/9] ARM: sunxi: quirk support Emilio López
@ 2014-08-03 12:42   ` Maxime Ripard
  2014-08-03 21:37     ` Emilio López
  0 siblings, 1 reply; 36+ messages in thread
From: Maxime Ripard @ 2014-08-03 12:42 UTC (permalink / raw)
  To: linux-arm-kernel

On Thu, Jul 31, 2014 at 06:28:05PM -0300, Emilio L?pez wrote:
> Currently, some hardware revisions of sunxi SoCs need special care on
> some blocks because of hardware differences and/or bugs. Unfortunately,
> it is unfeasible to account for these issues directly when writing the
> device tree, as SoC revision can vary between different units of the
> same device. This commit introduces a place to adjust DT compatibles
> as needed to work around said issues before devices are probed. To
> demonstrate usage, two quirks are added for the PLL2 and audio codec
> on sun4i.
> 
> Signed-off-by: Emilio L?pez <emilio@elopez.com.ar>
> ---
>  arch/arm/mach-sunxi/Makefile |  2 +-
>  arch/arm/mach-sunxi/quirks.c | 82 ++++++++++++++++++++++++++++++++++++++++++++
>  2 files changed, 83 insertions(+), 1 deletion(-)
>  create mode 100644 arch/arm/mach-sunxi/quirks.c
> 
> diff --git a/arch/arm/mach-sunxi/Makefile b/arch/arm/mach-sunxi/Makefile
> index 589239b..7c13f99 100644
> --- a/arch/arm/mach-sunxi/Makefile
> +++ b/arch/arm/mach-sunxi/Makefile
> @@ -1,2 +1,2 @@
> -obj-$(CONFIG_ARCH_SUNXI) += sunxi.o sunxi-soc-id.o
> +obj-$(CONFIG_ARCH_SUNXI) += sunxi.o sunxi-soc-id.o quirks.o
>  obj-$(CONFIG_SMP) += platsmp.o
> diff --git a/arch/arm/mach-sunxi/quirks.c b/arch/arm/mach-sunxi/quirks.c
> new file mode 100644
> index 0000000..99cdaa0
> --- /dev/null
> +++ b/arch/arm/mach-sunxi/quirks.c
> @@ -0,0 +1,82 @@
> +/*
> + * Runtime quirk handling for sunxi SoCs
> + *
> + * Copyright 2014 Emilio L?pez
> + *
> + * Emilio L?pez <emilio@elopez.com.ar>
> + *
> + * 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.
> + */
> +
> +#include <linux/of.h>
> +#include <linux/slab.h>
> +#include <linux/string.h>
> +
> +#include "sunxi-soc-id.h"
> +
> +static int __init update_compatible_string(const char *oldc, const char *newc)
> +{
> +	int count = 0;
> +	struct property *newprop;
> +	size_t newlen = strlen(newc);
> +	struct device_node *np = NULL;
> +
> +	for_each_compatible_node(np, NULL, oldc) {
> +		newprop = kzalloc(sizeof(*newprop), GFP_KERNEL);
> +		if (!newprop)
> +			return -ENOMEM;
> +
> +		newprop->name = kstrdup("compatible", GFP_KERNEL);
> +		newprop->value = kstrdup(newc, GFP_KERNEL);
> +		newprop->length = newlen;
> +
> +		if (!newprop->name || !newprop->value) {
> +			kfree(newprop);
> +			return -ENOMEM;
> +		}
> +
> +		of_update_property(np, newprop);
> +		count++;
> +	}
> +
> +	return count;
> +}
> +
> +static void __init sun4i_pll2_quirk(void)
> +{
> +	/* Only revision A is affected */
> +	if (sunxi_soc_revision() != 'A')
> +		return;
> +
> +	WARN_ON(!update_compatible_string("allwinner,sun4i-a10-b-pll2",
> +					  "allwinner,sun4i-a10-a-pll2"));
> +}
> +
> +static void __init sun4i_codec_quirk(void)
> +{
> +	/* Only revision A is affected */
> +	if (sunxi_soc_revision() != 'A')
> +		return;
> +
> +	WARN_ON(!update_compatible_string("allwinner,sun4i-a10-b-codec",
> +					  "allwinner,sun4i-a10-a-codec"));
> +}
> +
> +static int __init sunxi_apply_quirks(void)
> +{
> +	if (of_machine_is_compatible("allwinner,sun4i-a10")) {
> +		sun4i_pll2_quirk();
> +		sun4i_codec_quirk();
> +	}
> +
> +	return 0;
> +}
> +postcore_initcall(sunxi_apply_quirks)

Have you tested it? My guess is that it wolud have to run *much*
sooner, before of_platform_populate to be effective.

-- 
Maxime Ripard, Free Electrons
Embedded Linux, Kernel and Android engineering
http://free-electrons.com
-------------- next part --------------
A non-text attachment was scrubbed...
Name: signature.asc
Type: application/pgp-signature
Size: 819 bytes
Desc: Digital signature
URL: <http://lists.infradead.org/pipermail/linux-arm-kernel/attachments/20140803/343600df/attachment.sig>

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

* [PATCH 4/9] clk: sunxi: PLL2 support for sun4i, sun5i and sun7i
  2014-07-31 21:28 ` [PATCH 4/9] clk: sunxi: PLL2 support for sun4i, sun5i and sun7i Emilio López
@ 2014-08-03 12:44   ` Maxime Ripard
  2014-08-03 15:58     ` Chen-Yu Tsai
  2014-08-03 22:02     ` Emilio López
  2014-08-06 13:51   ` jonsmirl at gmail.com
  2014-08-06 15:20   ` jonsmirl at gmail.com
  2 siblings, 2 replies; 36+ messages in thread
From: Maxime Ripard @ 2014-08-03 12:44 UTC (permalink / raw)
  To: linux-arm-kernel

On Thu, Jul 31, 2014 at 06:28:07PM -0300, Emilio L?pez wrote:
> This patch adds support for PLL2 and derivates on sun4i, sun5i and
> sun7i SoCs. As this PLL is only used for audio and requires good
> accuracy, we only support two known good rates.
> 
> Signed-off-by: Emilio L?pez <emilio@elopez.com.ar>
> ---
> 
> Changes from RFC:
>  * Add support for A10 rev. A
>  * Document compatibles
>  * Use fixed factors
> 
>  Documentation/devicetree/bindings/clock/sunxi.txt |   2 +
>  drivers/clk/sunxi/Makefile                        |   1 +
>  drivers/clk/sunxi/clk-a10-pll2.c                  | 243 ++++++++++++++++++++++
>  3 files changed, 246 insertions(+)
>  create mode 100644 drivers/clk/sunxi/clk-a10-pll2.c
> 
> diff --git a/Documentation/devicetree/bindings/clock/sunxi.txt b/Documentation/devicetree/bindings/clock/sunxi.txt
> index d3a5c3c..41ada31 100644
> --- a/Documentation/devicetree/bindings/clock/sunxi.txt
> +++ b/Documentation/devicetree/bindings/clock/sunxi.txt
> @@ -10,6 +10,8 @@ Required properties:
>  	"allwinner,sun4i-a10-pll1-clk" - for the main PLL clock and PLL4
>  	"allwinner,sun6i-a31-pll1-clk" - for the main PLL clock on A31
>  	"allwinner,sun8i-a23-pll1-clk" - for the main PLL clock on A23
> +	"allwinner,sun4i-a10-a-pll2-clk" - for the PLL2 clock on A10 rev. A
> +	"allwinner,sun4i-a10-b-pll2-clk" - for the PLL2 clock on A10 rev. B
>  	"allwinner,sun4i-a10-pll5-clk" - for the PLL5 clock
>  	"allwinner,sun4i-a10-pll6-clk" - for the PLL6 clock
>  	"allwinner,sun6i-a31-pll6-clk" - for the PLL6 clock on A31
> diff --git a/drivers/clk/sunxi/Makefile b/drivers/clk/sunxi/Makefile
> index 6850cba..dcd5709 100644
> --- a/drivers/clk/sunxi/Makefile
> +++ b/drivers/clk/sunxi/Makefile
> @@ -4,6 +4,7 @@
>  
>  obj-y += clk-sunxi.o clk-factors.o
>  obj-y += clk-a10-hosc.o
> +obj-y += clk-a10-pll2.o
>  obj-y += clk-a20-gmac.o
>  
>  obj-$(CONFIG_MFD_SUN6I_PRCM) += \
> diff --git a/drivers/clk/sunxi/clk-a10-pll2.c b/drivers/clk/sunxi/clk-a10-pll2.c
> new file mode 100644
> index 0000000..bcf7d0b
> --- /dev/null
> +++ b/drivers/clk/sunxi/clk-a10-pll2.c
> @@ -0,0 +1,243 @@
> +/*
> + * Copyright 2014 Emilio L?pez
> + *
> + * Emilio L?pez <emilio@elopez.com.ar>
> + *
> + * 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.
> + */
> +
> +#include <linux/clk-provider.h>
> +#include <linux/of.h>
> +#include <linux/of_address.h>
> +#include <linux/slab.h>
> +
> +#define SUN4I_PLL2_ENABLE		31
> +#define SUN4I_PLL2_A_VCOBIAS		0
> +#define SUN4I_PLL2_A_VCOBIAS_MASK	0x1F
> +#define SUN4I_PLL2_A_N			7
> +#define SUN4I_PLL2_A_N_MASK		0x7F
> +#define SUN4I_PLL2_B_POST_DIV		26
> +#define SUN4I_PLL2_B_POST_DIV_MASK	0xF
> +#define SUN4I_PLL2_B_N			8
> +#define SUN4I_PLL2_B_N_MASK		0x7F
> +#define SUN4I_PLL2_B_PRE_DIV		0
> +#define SUN4I_PLL2_B_PRE_DIV_MASK	0x1F
> +
> +#define SUN4I_PLL2_OUTPUTS		4
> +
> +struct sun4i_pll2_clk {
> +	struct clk_hw hw;
> +	void __iomem *reg;
> +};
> +
> +static inline struct sun4i_pll2_clk *to_sun4i_pll2_clk(struct clk_hw *hw)
> +{
> +	return container_of(hw, struct sun4i_pll2_clk, hw);
> +}
> +
> +static unsigned long sun4i_pll2_recalc_rate_a(struct clk_hw *hw,
> +					      unsigned long parent_rate)
> +{
> +	struct sun4i_pll2_clk *clk = to_sun4i_pll2_clk(hw);
> +	int vcobias, n;
> +	u32 val;
> +
> +	val = readl(clk->reg);
> +	vcobias = (val >> SUN4I_PLL2_A_VCOBIAS) & SUN4I_PLL2_A_VCOBIAS_MASK;
> +	n = (val >> SUN4I_PLL2_A_N) & SUN4I_PLL2_A_N_MASK;
> +
> +	if (vcobias == 10 && n == 94)
> +		return 22579200;
> +	else if (vcobias == 9 && n == 83)
> +		return 24576000;
> +
> +	/*
> +	 * Unfortunately we don't really have much documentation on how
> +	 * these factors relate mathematically
> +	 */
> +	return 0;
> +}
> +
> +static unsigned long sun4i_pll2_recalc_rate_b(struct clk_hw *hw,
> +					      unsigned long parent_rate)
> +{
> +	struct sun4i_pll2_clk *clk = to_sun4i_pll2_clk(hw);
> +	int n, prediv, postdiv;
> +	u32 val;
> +
> +	val = readl(clk->reg);
> +	n = (val >> SUN4I_PLL2_B_N) & SUN4I_PLL2_B_N_MASK;
> +	prediv = (val >> SUN4I_PLL2_B_PRE_DIV) & SUN4I_PLL2_B_PRE_DIV_MASK;
> +	postdiv = (val >> SUN4I_PLL2_B_POST_DIV) & SUN4I_PLL2_B_POST_DIV_MASK;
> +
> +	/* 0 is a special case and means 1 */
> +	if (n == 0)
> +		n = 1;
> +	if (prediv == 0)
> +		prediv = 1;
> +	if (postdiv == 0)
> +		postdiv = 1;
> +
> +	return ((parent_rate * n) / prediv) / postdiv;
> +}
> +
> +static long sun4i_pll2_round_rate(struct clk_hw *hw, unsigned long rate,
> +				  unsigned long *parent_rate)
> +{
> +	/*
> +	 * There is only two interesting rates for the audio PLL, the
> +	 * rest isn't really usable due to accuracy concerns. Therefore,
> +	 * we specifically round to those rates here
> +	 */
> +	if (rate < 22579200)
> +		return -EINVAL;
> +
> +	if (rate >= 22579200 && rate < 24576000)
> +		return 22579200;
> +
> +	return 24576000;
> +}
> +
> +static int sun4i_pll2_set_rate_a(struct clk_hw *hw, unsigned long rate,
> +				 unsigned long parent_rate)
> +{
> +	struct sun4i_pll2_clk *clk = to_sun4i_pll2_clk(hw);
> +	u32 val = readl(clk->reg);
> +
> +	val &= ~(SUN4I_PLL2_A_VCOBIAS_MASK << SUN4I_PLL2_A_VCOBIAS);
> +	val &= ~(SUN4I_PLL2_A_N_MASK << SUN4I_PLL2_A_N);
> +
> +	if (rate == 22579200)
> +		val |= (10 << SUN4I_PLL2_A_VCOBIAS) | (94 << SUN4I_PLL2_A_N);
> +	else if (rate == 24576000)
> +		val |= (9 << SUN4I_PLL2_A_VCOBIAS) | (83 << SUN4I_PLL2_A_N);
> +	else
> +		return -EINVAL;
> +
> +	writel(val, clk->reg);
> +
> +	return 0;
> +}
> +
> +static int sun4i_pll2_set_rate_b(struct clk_hw *hw, unsigned long rate,
> +				 unsigned long parent_rate)
> +{
> +	struct sun4i_pll2_clk *clk = to_sun4i_pll2_clk(hw);
> +	u32 val = readl(clk->reg);
> +
> +	val &= ~(SUN4I_PLL2_B_N_MASK << SUN4I_PLL2_B_N);
> +	val &= ~(SUN4I_PLL2_B_PRE_DIV_MASK << SUN4I_PLL2_B_PRE_DIV);
> +	val &= ~(SUN4I_PLL2_B_POST_DIV_MASK << SUN4I_PLL2_B_POST_DIV);
> +
> +	val |= (21 << SUN4I_PLL2_B_PRE_DIV) | (4 << SUN4I_PLL2_B_POST_DIV);
> +
> +	if (rate == 22579200)
> +		val |= (79 << SUN4I_PLL2_B_N);
> +	else if (rate == 24576000)
> +		val |= (86 << SUN4I_PLL2_B_N);
> +	else
> +		return -EINVAL;
> +
> +	writel(val, clk->reg);
> +
> +	return 0;
> +}
> +
> +static const struct clk_ops sun4i_pll2_ops_a = {
> +	.recalc_rate = sun4i_pll2_recalc_rate_a,
> +	.round_rate = sun4i_pll2_round_rate,
> +	.set_rate = sun4i_pll2_set_rate_a,
> +};
> +
> +
> +static const struct clk_ops sun4i_pll2_ops_b = {
> +	.recalc_rate = sun4i_pll2_recalc_rate_b,
> +	.round_rate = sun4i_pll2_round_rate,
> +	.set_rate = sun4i_pll2_set_rate_b,
> +};
> +
> +static const struct of_device_id pll2_matches[] __initconst = {
> +	{ .compatible = "allwinner,sun4i-a10-a-pll2-clk", .data = &sun4i_pll2_ops_a },
> +	{ .compatible = "allwinner,sun4i-a10-b-pll2-clk", .data = &sun4i_pll2_ops_b },
> +	{ /* sentinel */ },
> +};
> +
> +static void __init sun4i_pll2_setup(struct device_node *np)
> +{
> +	const char *clk_name = np->name, *parent;
> +	const struct of_device_id *match;
> +	struct clk_onecell_data *clk_data;
> +	const struct clk_ops *pll2_ops;
> +	struct sun4i_pll2_clk *pll2;
> +	struct clk_gate *gate;
> +	struct clk **clks;
> +	void __iomem *reg;
> +
> +	/* Choose the correct ops for pll2 */
> +	match = of_match_node(pll2_matches, np);
> +	pll2_ops = match->data;
> +
> +	pll2 = kzalloc(sizeof(*pll2), GFP_KERNEL);
> +	gate = kzalloc(sizeof(*gate), GFP_KERNEL);
> +	clk_data = kzalloc(sizeof(*clk_data), GFP_KERNEL);
> +	clks = kcalloc(SUN4I_PLL2_OUTPUTS, sizeof(*clks), GFP_KERNEL);
> +	if (!pll2 || !gate || !clk_data || !clks)
> +		goto free_mem;
> +
> +	reg = of_iomap(np, 0);
> +	parent = of_clk_get_parent_name(np, 0);
> +	of_property_read_string_index(np, "clock-output-names", 0, &clk_name);
> +
> +	pll2->reg = reg;
> +	gate->reg = reg;
> +	gate->bit_idx = SUN4I_PLL2_ENABLE;
> +
> +	/* PLL2, also known as PLL2x1 */
> +	of_property_read_string_index(np, "clock-output-names", 0, &clk_name);
> +	clks[0] = clk_register_composite(NULL, clk_name, &parent, 1, NULL, NULL,
> +					 &pll2->hw, pll2_ops,
> +					 &gate->hw, &clk_gate_ops, 0);
> +	WARN_ON(IS_ERR(clks[0]));
> +	clk_set_rate(clks[0], 22579200);
> +	parent = clk_name;
> +
> +	/* PLL2x2, 1/4 the rate of PLL2x8 */
> +	of_property_read_string_index(np, "clock-output-names", 1, &clk_name);
> +	clks[1] = clk_register_fixed_factor(NULL, clk_name, parent,
> +					    CLK_SET_RATE_PARENT, 2, 1);
> +	WARN_ON(IS_ERR(clks[1]));
> +
> +	/* PLL2x4, 1/2 the rate of PLL2x8 */
> +	of_property_read_string_index(np, "clock-output-names", 2, &clk_name);
> +	clks[2] = clk_register_fixed_factor(NULL, clk_name, parent,
> +					    CLK_SET_RATE_PARENT, 4, 1);
> +	WARN_ON(IS_ERR(clks[2]));
> +
> +	/* PLL2x8, double of PLL2 without the post divisor */
> +	of_property_read_string_index(np, "clock-output-names", 3, &clk_name);
> +	clks[3] = clk_register_fixed_factor(NULL, clk_name, parent,
> +					    CLK_SET_RATE_PARENT, 2 * 4, 1);

Why have you declared them here, instead of using fixed factors in the
DT directly, like we have done in the past?

Maxime

-- 
Maxime Ripard, Free Electrons
Embedded Linux, Kernel and Android engineering
http://free-electrons.com
-------------- next part --------------
A non-text attachment was scrubbed...
Name: signature.asc
Type: application/pgp-signature
Size: 819 bytes
Desc: Digital signature
URL: <http://lists.infradead.org/pipermail/linux-arm-kernel/attachments/20140803/78bff9c8/attachment.sig>

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

* [PATCH 6/9] clk: sunxi: mod1 clock support
  2014-07-31 21:28 ` [PATCH 6/9] clk: sunxi: mod1 " Emilio López
@ 2014-08-03 12:47   ` Maxime Ripard
  2014-08-03 22:11     ` Emilio López
  0 siblings, 1 reply; 36+ messages in thread
From: Maxime Ripard @ 2014-08-03 12:47 UTC (permalink / raw)
  To: linux-arm-kernel

On Thu, Jul 31, 2014 at 06:28:09PM -0300, Emilio L?pez wrote:
> The module 1 type of clocks consist of a gate and a mux and are used on
> the audio blocks to mux and gate the PLL2 outputs for AC97, IIS or
> SPDIF. This commit adds support for them on the sunxi clock driver.
> 
> Not-signed-off-by: Emilio L?pez <emilio@elopez.com.ar>

Why so?

It looks quite good.

> ---
> 
> Changes from RFC:
>  * Document compatible used
> 
>  Documentation/devicetree/bindings/clock/sunxi.txt |  1 +
>  drivers/clk/sunxi/Makefile                        |  1 +
>  drivers/clk/sunxi/clk-a10-mod1.c                  | 69 +++++++++++++++++++++++
>  3 files changed, 71 insertions(+)
>  create mode 100644 drivers/clk/sunxi/clk-a10-mod1.c
> 
> diff --git a/Documentation/devicetree/bindings/clock/sunxi.txt b/Documentation/devicetree/bindings/clock/sunxi.txt
> index d18b89b..b028ee2 100644
> --- a/Documentation/devicetree/bindings/clock/sunxi.txt
> +++ b/Documentation/devicetree/bindings/clock/sunxi.txt
> @@ -50,6 +50,7 @@ Required properties:
>  	"allwinner,sun8i-a23-apb2-gates-clk" - for the APB2 gates on A23
>  	"allwinner,sun4i-a10-codec-clk" - for the codec clock on A10
>  	"allwinner,sun4i-a10-mod0-clk" - for the module 0 family of clocks
> +	"allwinner,sun4i-a10-mod1-clk" - for the module 1 family of clocks
>  	"allwinner,sun7i-a20-out-clk" - for the external output clocks
>  	"allwinner,sun7i-a20-gmac-clk" - for the GMAC clock module on A20/A31
>  	"allwinner,sun4i-a10-usb-clk" - for usb gates + resets on A10 / A20
> diff --git a/drivers/clk/sunxi/Makefile b/drivers/clk/sunxi/Makefile
> index a7a96f8..75d113d 100644
> --- a/drivers/clk/sunxi/Makefile
> +++ b/drivers/clk/sunxi/Makefile
> @@ -5,6 +5,7 @@
>  obj-y += clk-sunxi.o clk-factors.o
>  obj-y += clk-a10-codec.o
>  obj-y += clk-a10-hosc.o
> +obj-y += clk-a10-mod1.o
>  obj-y += clk-a10-pll2.o
>  obj-y += clk-a20-gmac.o
>  
> diff --git a/drivers/clk/sunxi/clk-a10-mod1.c b/drivers/clk/sunxi/clk-a10-mod1.c
> new file mode 100644
> index 0000000..09afd54
> --- /dev/null
> +++ b/drivers/clk/sunxi/clk-a10-mod1.c
> @@ -0,0 +1,69 @@
> +/*
> + * Copyright 2014 Emilio L?pez
> + *
> + * Emilio L?pez <emilio@elopez.com.ar>
> + *
> + * 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.
> + */
> +
> +#include <linux/clk-provider.h>
> +#include <linux/clkdev.h>
> +#include <linux/of.h>
> +#include <linux/of_address.h>
> +
> +static DEFINE_SPINLOCK(mod1_lock);
> +
> +#define SUN4I_MOD1_ENABLE	31
> +#define SUN4I_MOD1_MUX		16
> +#define SUN4I_MOD1_MUX_WIDTH	2
> +#define SUN4I_MOD1_MAX_PARENTS	4
> +
> +static void __init sun4i_mod1_clk_setup(struct device_node *node)
> +{
> +	struct clk *clk;
> +	struct clk_mux *mux;
> +	struct clk_gate *gate;
> +	const char *parents[4];
> +	const char *clk_name = node->name;
> +	void __iomem *reg;
> +	int i = 0;
> +
> +	mux = kzalloc(sizeof(*mux), GFP_KERNEL);
> +	gate = kzalloc(sizeof(*gate), GFP_KERNEL);
> +	if (!mux || !gate) {
> +		kfree(mux);
> +		kfree(gate);
> +		return;
> +	}
> +
> +	of_property_read_string(node, "clock-output-names", &clk_name);
> +	reg = of_iomap(node, 0);
> +
> +	while (i < SUN4I_MOD1_MAX_PARENTS &&
> +	       (parents[i] = of_clk_get_parent_name(node, i)) != NULL)
> +		i++;
> +
> +	gate->reg = reg;
> +	gate->bit_idx = SUN4I_MOD1_ENABLE;
> +	gate->lock = &mod1_lock;
> +	mux->reg = reg;
> +	mux->shift = SUN4I_MOD1_MUX;
> +	mux->mask = BIT(SUN4I_MOD1_MUX_WIDTH) - 1;
> +	mux->lock = &mod1_lock;
> +
> +	clk = clk_register_composite(NULL, clk_name, parents, i,
> +				     &mux->hw, &clk_mux_ops,
> +				     NULL, NULL,
> +				     &gate->hw, &clk_gate_ops, 0);
> +	if (!IS_ERR(clk))
> +		of_clk_add_provider(node, of_clk_src_simple_get, clk);
> +}
> +CLK_OF_DECLARE(sun4i_mod1, "allwinner,sun4i-a10-mod1-clk", sun4i_mod1_clk_setup);

Why are you not using clk-sunxi for this? It has pretty much
everything in place to do so already.

Maxime

-- 
Maxime Ripard, Free Electrons
Embedded Linux, Kernel and Android engineering
http://free-electrons.com
-------------- next part --------------
A non-text attachment was scrubbed...
Name: signature.asc
Type: application/pgp-signature
Size: 819 bytes
Desc: Digital signature
URL: <http://lists.infradead.org/pipermail/linux-arm-kernel/attachments/20140803/345affed/attachment.sig>

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

* [PATCH 7/9] ARM: sunxi: dt: Add PLL2 support
  2014-07-31 21:46   ` jonsmirl at gmail.com
@ 2014-08-03 12:50     ` Maxime Ripard
  2014-08-03 15:55       ` Chen-Yu Tsai
  0 siblings, 1 reply; 36+ messages in thread
From: Maxime Ripard @ 2014-08-03 12:50 UTC (permalink / raw)
  To: linux-arm-kernel

On Thu, Jul 31, 2014 at 05:46:09PM -0400, jonsmirl at gmail.com wrote:
> Would it be better to name this "allwinner,sun4i-a10-pll2-clk" instead
> of "allwinner,sun4i-a10-b-pll2-clk"? By encoding the b in it everyone
> is going to wonder what to do on the 'c' revision which is the most
> common revision.

Not really, the way we works usually is that the compatible is the one
from the first SoC that implemented that IP. If the rev C has the same
IP than rev B, then we're using the rev B compatible.

> 
> The revision based rename would then be from
> "allwinner,sun4i-a10-pll2-clk" to "allwinner,sun4i-a10-a-pll2-clk".

Though, I'd agree with you. We should have a single compatible in the
DT, a generic one, that would trigger the auto-detection, and might
change it to the rev A one, but the rev B doesn't make much sense.

Maxime
-- 
Maxime Ripard, Free Electrons
Embedded Linux, Kernel and Android engineering
http://free-electrons.com
-------------- next part --------------
A non-text attachment was scrubbed...
Name: signature.asc
Type: application/pgp-signature
Size: 819 bytes
Desc: Digital signature
URL: <http://lists.infradead.org/pipermail/linux-arm-kernel/attachments/20140803/d43bd6b5/attachment.sig>

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

* [PATCH 7/9] ARM: sunxi: dt: Add PLL2 support
  2014-08-03 12:50     ` Maxime Ripard
@ 2014-08-03 15:55       ` Chen-Yu Tsai
  2014-08-03 22:15         ` Emilio López
  0 siblings, 1 reply; 36+ messages in thread
From: Chen-Yu Tsai @ 2014-08-03 15:55 UTC (permalink / raw)
  To: linux-arm-kernel

On Sun, Aug 3, 2014 at 8:50 PM, Maxime Ripard
<maxime.ripard@free-electrons.com> wrote:
> On Thu, Jul 31, 2014 at 05:46:09PM -0400, jonsmirl at gmail.com wrote:
>> Would it be better to name this "allwinner,sun4i-a10-pll2-clk" instead
>> of "allwinner,sun4i-a10-b-pll2-clk"? By encoding the b in it everyone
>> is going to wonder what to do on the 'c' revision which is the most
>> common revision.
>
> Not really, the way we works usually is that the compatible is the one
> from the first SoC that implemented that IP. If the rev C has the same
> IP than rev B, then we're using the rev B compatible.
>
>>
>> The revision based rename would then be from
>> "allwinner,sun4i-a10-pll2-clk" to "allwinner,sun4i-a10-a-pll2-clk".
>
> Though, I'd agree with you. We should have a single compatible in the
> DT, a generic one, that would trigger the auto-detection, and might
> change it to the rev A one, but the rev B doesn't make much sense.

I agree. The rev B would make people reading the DT wonder what happened
to rev A.

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

* [PATCH 4/9] clk: sunxi: PLL2 support for sun4i, sun5i and sun7i
  2014-08-03 12:44   ` Maxime Ripard
@ 2014-08-03 15:58     ` Chen-Yu Tsai
  2014-08-03 18:48       ` Maxime Ripard
  2014-08-03 22:02     ` Emilio López
  1 sibling, 1 reply; 36+ messages in thread
From: Chen-Yu Tsai @ 2014-08-03 15:58 UTC (permalink / raw)
  To: linux-arm-kernel

On Sun, Aug 3, 2014 at 8:44 PM, Maxime Ripard
<maxime.ripard@free-electrons.com> wrote:
> On Thu, Jul 31, 2014 at 06:28:07PM -0300, Emilio L?pez wrote:
>> This patch adds support for PLL2 and derivates on sun4i, sun5i and
>> sun7i SoCs. As this PLL is only used for audio and requires good
>> accuracy, we only support two known good rates.
>>
>> Signed-off-by: Emilio L?pez <emilio@elopez.com.ar>
>> ---
>>
>> Changes from RFC:
>>  * Add support for A10 rev. A
>>  * Document compatibles
>>  * Use fixed factors
>>
>>  Documentation/devicetree/bindings/clock/sunxi.txt |   2 +
>>  drivers/clk/sunxi/Makefile                        |   1 +
>>  drivers/clk/sunxi/clk-a10-pll2.c                  | 243 ++++++++++++++++++++++
>>  3 files changed, 246 insertions(+)
>>  create mode 100644 drivers/clk/sunxi/clk-a10-pll2.c
>>
>> diff --git a/Documentation/devicetree/bindings/clock/sunxi.txt b/Documentation/devicetree/bindings/clock/sunxi.txt
>> index d3a5c3c..41ada31 100644
>> --- a/Documentation/devicetree/bindings/clock/sunxi.txt
>> +++ b/Documentation/devicetree/bindings/clock/sunxi.txt
>> @@ -10,6 +10,8 @@ Required properties:
>>       "allwinner,sun4i-a10-pll1-clk" - for the main PLL clock and PLL4
>>       "allwinner,sun6i-a31-pll1-clk" - for the main PLL clock on A31
>>       "allwinner,sun8i-a23-pll1-clk" - for the main PLL clock on A23
>> +     "allwinner,sun4i-a10-a-pll2-clk" - for the PLL2 clock on A10 rev. A
>> +     "allwinner,sun4i-a10-b-pll2-clk" - for the PLL2 clock on A10 rev. B
>>       "allwinner,sun4i-a10-pll5-clk" - for the PLL5 clock
>>       "allwinner,sun4i-a10-pll6-clk" - for the PLL6 clock
>>       "allwinner,sun6i-a31-pll6-clk" - for the PLL6 clock on A31
>> diff --git a/drivers/clk/sunxi/Makefile b/drivers/clk/sunxi/Makefile
>> index 6850cba..dcd5709 100644
>> --- a/drivers/clk/sunxi/Makefile
>> +++ b/drivers/clk/sunxi/Makefile
>> @@ -4,6 +4,7 @@
>>
>>  obj-y += clk-sunxi.o clk-factors.o
>>  obj-y += clk-a10-hosc.o
>> +obj-y += clk-a10-pll2.o
>>  obj-y += clk-a20-gmac.o
>>
>>  obj-$(CONFIG_MFD_SUN6I_PRCM) += \
>> diff --git a/drivers/clk/sunxi/clk-a10-pll2.c b/drivers/clk/sunxi/clk-a10-pll2.c
>> new file mode 100644
>> index 0000000..bcf7d0b
>> --- /dev/null
>> +++ b/drivers/clk/sunxi/clk-a10-pll2.c
>> @@ -0,0 +1,243 @@
>> +/*
>> + * Copyright 2014 Emilio L?pez
>> + *
>> + * Emilio L?pez <emilio@elopez.com.ar>
>> + *
>> + * 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.
>> + */
>> +
>> +#include <linux/clk-provider.h>
>> +#include <linux/of.h>
>> +#include <linux/of_address.h>
>> +#include <linux/slab.h>
>> +
>> +#define SUN4I_PLL2_ENABLE            31
>> +#define SUN4I_PLL2_A_VCOBIAS         0
>> +#define SUN4I_PLL2_A_VCOBIAS_MASK    0x1F
>> +#define SUN4I_PLL2_A_N                       7
>> +#define SUN4I_PLL2_A_N_MASK          0x7F
>> +#define SUN4I_PLL2_B_POST_DIV                26
>> +#define SUN4I_PLL2_B_POST_DIV_MASK   0xF
>> +#define SUN4I_PLL2_B_N                       8
>> +#define SUN4I_PLL2_B_N_MASK          0x7F
>> +#define SUN4I_PLL2_B_PRE_DIV         0
>> +#define SUN4I_PLL2_B_PRE_DIV_MASK    0x1F
>> +
>> +#define SUN4I_PLL2_OUTPUTS           4
>> +
>> +struct sun4i_pll2_clk {
>> +     struct clk_hw hw;
>> +     void __iomem *reg;
>> +};
>> +
>> +static inline struct sun4i_pll2_clk *to_sun4i_pll2_clk(struct clk_hw *hw)
>> +{
>> +     return container_of(hw, struct sun4i_pll2_clk, hw);
>> +}
>> +
>> +static unsigned long sun4i_pll2_recalc_rate_a(struct clk_hw *hw,
>> +                                           unsigned long parent_rate)
>> +{
>> +     struct sun4i_pll2_clk *clk = to_sun4i_pll2_clk(hw);
>> +     int vcobias, n;
>> +     u32 val;
>> +
>> +     val = readl(clk->reg);
>> +     vcobias = (val >> SUN4I_PLL2_A_VCOBIAS) & SUN4I_PLL2_A_VCOBIAS_MASK;
>> +     n = (val >> SUN4I_PLL2_A_N) & SUN4I_PLL2_A_N_MASK;
>> +
>> +     if (vcobias == 10 && n == 94)
>> +             return 22579200;
>> +     else if (vcobias == 9 && n == 83)
>> +             return 24576000;
>> +
>> +     /*
>> +      * Unfortunately we don't really have much documentation on how
>> +      * these factors relate mathematically
>> +      */
>> +     return 0;
>> +}
>> +
>> +static unsigned long sun4i_pll2_recalc_rate_b(struct clk_hw *hw,
>> +                                           unsigned long parent_rate)
>> +{
>> +     struct sun4i_pll2_clk *clk = to_sun4i_pll2_clk(hw);
>> +     int n, prediv, postdiv;
>> +     u32 val;
>> +
>> +     val = readl(clk->reg);
>> +     n = (val >> SUN4I_PLL2_B_N) & SUN4I_PLL2_B_N_MASK;
>> +     prediv = (val >> SUN4I_PLL2_B_PRE_DIV) & SUN4I_PLL2_B_PRE_DIV_MASK;
>> +     postdiv = (val >> SUN4I_PLL2_B_POST_DIV) & SUN4I_PLL2_B_POST_DIV_MASK;
>> +
>> +     /* 0 is a special case and means 1 */
>> +     if (n == 0)
>> +             n = 1;
>> +     if (prediv == 0)
>> +             prediv = 1;
>> +     if (postdiv == 0)
>> +             postdiv = 1;
>> +
>> +     return ((parent_rate * n) / prediv) / postdiv;
>> +}
>> +
>> +static long sun4i_pll2_round_rate(struct clk_hw *hw, unsigned long rate,
>> +                               unsigned long *parent_rate)
>> +{
>> +     /*
>> +      * There is only two interesting rates for the audio PLL, the
>> +      * rest isn't really usable due to accuracy concerns. Therefore,
>> +      * we specifically round to those rates here
>> +      */
>> +     if (rate < 22579200)
>> +             return -EINVAL;
>> +
>> +     if (rate >= 22579200 && rate < 24576000)
>> +             return 22579200;
>> +
>> +     return 24576000;
>> +}
>> +
>> +static int sun4i_pll2_set_rate_a(struct clk_hw *hw, unsigned long rate,
>> +                              unsigned long parent_rate)
>> +{
>> +     struct sun4i_pll2_clk *clk = to_sun4i_pll2_clk(hw);
>> +     u32 val = readl(clk->reg);
>> +
>> +     val &= ~(SUN4I_PLL2_A_VCOBIAS_MASK << SUN4I_PLL2_A_VCOBIAS);
>> +     val &= ~(SUN4I_PLL2_A_N_MASK << SUN4I_PLL2_A_N);
>> +
>> +     if (rate == 22579200)
>> +             val |= (10 << SUN4I_PLL2_A_VCOBIAS) | (94 << SUN4I_PLL2_A_N);
>> +     else if (rate == 24576000)
>> +             val |= (9 << SUN4I_PLL2_A_VCOBIAS) | (83 << SUN4I_PLL2_A_N);
>> +     else
>> +             return -EINVAL;
>> +
>> +     writel(val, clk->reg);
>> +
>> +     return 0;
>> +}
>> +
>> +static int sun4i_pll2_set_rate_b(struct clk_hw *hw, unsigned long rate,
>> +                              unsigned long parent_rate)
>> +{
>> +     struct sun4i_pll2_clk *clk = to_sun4i_pll2_clk(hw);
>> +     u32 val = readl(clk->reg);
>> +
>> +     val &= ~(SUN4I_PLL2_B_N_MASK << SUN4I_PLL2_B_N);
>> +     val &= ~(SUN4I_PLL2_B_PRE_DIV_MASK << SUN4I_PLL2_B_PRE_DIV);
>> +     val &= ~(SUN4I_PLL2_B_POST_DIV_MASK << SUN4I_PLL2_B_POST_DIV);
>> +
>> +     val |= (21 << SUN4I_PLL2_B_PRE_DIV) | (4 << SUN4I_PLL2_B_POST_DIV);
>> +
>> +     if (rate == 22579200)
>> +             val |= (79 << SUN4I_PLL2_B_N);
>> +     else if (rate == 24576000)
>> +             val |= (86 << SUN4I_PLL2_B_N);
>> +     else
>> +             return -EINVAL;
>> +
>> +     writel(val, clk->reg);
>> +
>> +     return 0;
>> +}
>> +
>> +static const struct clk_ops sun4i_pll2_ops_a = {
>> +     .recalc_rate = sun4i_pll2_recalc_rate_a,
>> +     .round_rate = sun4i_pll2_round_rate,
>> +     .set_rate = sun4i_pll2_set_rate_a,
>> +};
>> +
>> +
>> +static const struct clk_ops sun4i_pll2_ops_b = {
>> +     .recalc_rate = sun4i_pll2_recalc_rate_b,
>> +     .round_rate = sun4i_pll2_round_rate,
>> +     .set_rate = sun4i_pll2_set_rate_b,
>> +};
>> +
>> +static const struct of_device_id pll2_matches[] __initconst = {
>> +     { .compatible = "allwinner,sun4i-a10-a-pll2-clk", .data = &sun4i_pll2_ops_a },
>> +     { .compatible = "allwinner,sun4i-a10-b-pll2-clk", .data = &sun4i_pll2_ops_b },
>> +     { /* sentinel */ },
>> +};
>> +
>> +static void __init sun4i_pll2_setup(struct device_node *np)
>> +{
>> +     const char *clk_name = np->name, *parent;
>> +     const struct of_device_id *match;
>> +     struct clk_onecell_data *clk_data;
>> +     const struct clk_ops *pll2_ops;
>> +     struct sun4i_pll2_clk *pll2;
>> +     struct clk_gate *gate;
>> +     struct clk **clks;
>> +     void __iomem *reg;
>> +
>> +     /* Choose the correct ops for pll2 */
>> +     match = of_match_node(pll2_matches, np);
>> +     pll2_ops = match->data;
>> +
>> +     pll2 = kzalloc(sizeof(*pll2), GFP_KERNEL);
>> +     gate = kzalloc(sizeof(*gate), GFP_KERNEL);
>> +     clk_data = kzalloc(sizeof(*clk_data), GFP_KERNEL);
>> +     clks = kcalloc(SUN4I_PLL2_OUTPUTS, sizeof(*clks), GFP_KERNEL);
>> +     if (!pll2 || !gate || !clk_data || !clks)
>> +             goto free_mem;
>> +
>> +     reg = of_iomap(np, 0);
>> +     parent = of_clk_get_parent_name(np, 0);
>> +     of_property_read_string_index(np, "clock-output-names", 0, &clk_name);
>> +
>> +     pll2->reg = reg;
>> +     gate->reg = reg;
>> +     gate->bit_idx = SUN4I_PLL2_ENABLE;
>> +
>> +     /* PLL2, also known as PLL2x1 */
>> +     of_property_read_string_index(np, "clock-output-names", 0, &clk_name);
>> +     clks[0] = clk_register_composite(NULL, clk_name, &parent, 1, NULL, NULL,
>> +                                      &pll2->hw, pll2_ops,
>> +                                      &gate->hw, &clk_gate_ops, 0);
>> +     WARN_ON(IS_ERR(clks[0]));
>> +     clk_set_rate(clks[0], 22579200);
>> +     parent = clk_name;
>> +
>> +     /* PLL2x2, 1/4 the rate of PLL2x8 */
>> +     of_property_read_string_index(np, "clock-output-names", 1, &clk_name);
>> +     clks[1] = clk_register_fixed_factor(NULL, clk_name, parent,
>> +                                         CLK_SET_RATE_PARENT, 2, 1);
>> +     WARN_ON(IS_ERR(clks[1]));
>> +
>> +     /* PLL2x4, 1/2 the rate of PLL2x8 */
>> +     of_property_read_string_index(np, "clock-output-names", 2, &clk_name);
>> +     clks[2] = clk_register_fixed_factor(NULL, clk_name, parent,
>> +                                         CLK_SET_RATE_PARENT, 4, 1);
>> +     WARN_ON(IS_ERR(clks[2]));
>> +
>> +     /* PLL2x8, double of PLL2 without the post divisor */
>> +     of_property_read_string_index(np, "clock-output-names", 3, &clk_name);
>> +     clks[3] = clk_register_fixed_factor(NULL, clk_name, parent,
>> +                                         CLK_SET_RATE_PARENT, 2 * 4, 1);
>
> Why have you declared them here, instead of using fixed factors in the
> DT directly, like we have done in the past?

IIRC, We have not done so for the PLL clocks. This is how it was, though
it was with the more complicated divs clocks.


ChenYu

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

* [PATCH 4/9] clk: sunxi: PLL2 support for sun4i, sun5i and sun7i
  2014-08-03 15:58     ` Chen-Yu Tsai
@ 2014-08-03 18:48       ` Maxime Ripard
  0 siblings, 0 replies; 36+ messages in thread
From: Maxime Ripard @ 2014-08-03 18:48 UTC (permalink / raw)
  To: linux-arm-kernel

On Sun, Aug 03, 2014 at 11:58:16PM +0800, Chen-Yu Tsai wrote:
> >> +     /* PLL2, also known as PLL2x1 */
> >> +     of_property_read_string_index(np, "clock-output-names", 0, &clk_name);
> >> +     clks[0] = clk_register_composite(NULL, clk_name, &parent, 1, NULL, NULL,
> >> +                                      &pll2->hw, pll2_ops,
> >> +                                      &gate->hw, &clk_gate_ops, 0);
> >> +     WARN_ON(IS_ERR(clks[0]));
> >> +     clk_set_rate(clks[0], 22579200);
> >> +     parent = clk_name;
> >> +
> >> +     /* PLL2x2, 1/4 the rate of PLL2x8 */
> >> +     of_property_read_string_index(np, "clock-output-names", 1, &clk_name);
> >> +     clks[1] = clk_register_fixed_factor(NULL, clk_name, parent,
> >> +                                         CLK_SET_RATE_PARENT, 2, 1);
> >> +     WARN_ON(IS_ERR(clks[1]));
> >> +
> >> +     /* PLL2x4, 1/2 the rate of PLL2x8 */
> >> +     of_property_read_string_index(np, "clock-output-names", 2, &clk_name);
> >> +     clks[2] = clk_register_fixed_factor(NULL, clk_name, parent,
> >> +                                         CLK_SET_RATE_PARENT, 4, 1);
> >> +     WARN_ON(IS_ERR(clks[2]));
> >> +
> >> +     /* PLL2x8, double of PLL2 without the post divisor */
> >> +     of_property_read_string_index(np, "clock-output-names", 3, &clk_name);
> >> +     clks[3] = clk_register_fixed_factor(NULL, clk_name, parent,
> >> +                                         CLK_SET_RATE_PARENT, 2 * 4, 1);
> >
> > Why have you declared them here, instead of using fixed factors in the
> > DT directly, like we have done in the past?
> 
> IIRC, We have not done so for the PLL clocks. This is how it was, though
> it was with the more complicated divs clocks.

I don't see any obvious multipliers in the clock-output-names.

-- 
Maxime Ripard, Free Electrons
Embedded Linux, Kernel and Android engineering
http://free-electrons.com
-------------- next part --------------
A non-text attachment was scrubbed...
Name: signature.asc
Type: application/pgp-signature
Size: 819 bytes
Desc: Digital signature
URL: <http://lists.infradead.org/pipermail/linux-arm-kernel/attachments/20140803/b4cfdba1/attachment.sig>

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

* [PATCH 2/9] ARM: sunxi: quirk support
  2014-08-03 12:42   ` Maxime Ripard
@ 2014-08-03 21:37     ` Emilio López
  2014-08-04 19:32       ` Maxime Ripard
  0 siblings, 1 reply; 36+ messages in thread
From: Emilio López @ 2014-08-03 21:37 UTC (permalink / raw)
  To: linux-arm-kernel

Hi Maxime,

El 03/08/14 a las 09:42, Maxime Ripard escibi?:
> On Thu, Jul 31, 2014 at 06:28:05PM -0300, Emilio L?pez wrote:
>> Currently, some hardware revisions of sunxi SoCs need special care on
>> some blocks because of hardware differences and/or bugs. Unfortunately,
>> it is unfeasible to account for these issues directly when writing the
>> device tree, as SoC revision can vary between different units of the
>> same device. This commit introduces a place to adjust DT compatibles
>> as needed to work around said issues before devices are probed. To
>> demonstrate usage, two quirks are added for the PLL2 and audio codec
>> on sun4i.
>>
>> Signed-off-by: Emilio L?pez <emilio@elopez.com.ar>
>> ---
(...)
>> +static int __init sunxi_apply_quirks(void)
>> +{
>> +	if (of_machine_is_compatible("allwinner,sun4i-a10")) {
>> +		sun4i_pll2_quirk();
>> +		sun4i_codec_quirk();
>> +	}
>> +
>> +	return 0;
>> +}
>> +postcore_initcall(sunxi_apply_quirks)
>
> Have you tested it? My guess is that it wolud have to run *much*
> sooner, before of_platform_populate to be effective.

I have, I was able to break my UART with it :)

of_platform_populate() is called from arch/arm/kernel/setup.c on 
arch_initcall. From include/linux/init.h

...
#define postcore_initcall(fn)           __define_initcall(fn, 2)
#define postcore_initcall_sync(fn)      __define_initcall(fn, 2s)
#define arch_initcall(fn)               __define_initcall(fn, 3)
...

So it should be fine.

Cheers,

Emilio

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

* [PATCH 1/9] ARM: sunxi: introduce SoC identification support
  2014-08-03 12:40   ` Maxime Ripard
@ 2014-08-03 21:45     ` Emilio López
  2014-08-04  8:08       ` Lee Jones
  2014-08-04 19:48       ` Maxime Ripard
  0 siblings, 2 replies; 36+ messages in thread
From: Emilio López @ 2014-08-03 21:45 UTC (permalink / raw)
  To: linux-arm-kernel

Hi Maxime,

Thanks for taking the time to review this :)

El 03/08/14 a las 09:40, Maxime Ripard escibi?:
> Hi Emilio,
>
> On Thu, Jul 31, 2014 at 06:28:04PM -0300, Emilio L?pez wrote:
>> This commit adds SoC bus support on the sunxi platform, and exposes
>> information such as the hardware revision to userspace and other kernel
>> clients during init. A message with this information is also printed to
>> the kernel log to ease future bug triaging.
>>
>> Signed-off-by: Emilio L?pez <emilio@elopez.com.ar>
>> ---
>>   arch/arm/mach-sunxi/Kconfig        |   1 +
>>   arch/arm/mach-sunxi/Makefile       |   2 +-
>>   arch/arm/mach-sunxi/sunxi-soc-id.c | 226 +++++++++++++++++++++++++++++++++++++
>>   arch/arm/mach-sunxi/sunxi-soc-id.h |   6 +
>>   4 files changed, 234 insertions(+), 1 deletion(-)
>>   create mode 100644 arch/arm/mach-sunxi/sunxi-soc-id.c
>>   create mode 100644 arch/arm/mach-sunxi/sunxi-soc-id.h
>>
>> diff --git a/arch/arm/mach-sunxi/Kconfig b/arch/arm/mach-sunxi/Kconfig
>> index 6434e3b..4a199df 100644
>> --- a/arch/arm/mach-sunxi/Kconfig
>> +++ b/arch/arm/mach-sunxi/Kconfig
>> @@ -5,6 +5,7 @@ menuconfig ARCH_SUNXI
>>   	select GENERIC_IRQ_CHIP
>>   	select PINCTRL
>>   	select PINCTRL_SUNXI
>> +	select SOC_BUS
>>   	select SUN4I_TIMER
>>
>>   if ARCH_SUNXI
>> diff --git a/arch/arm/mach-sunxi/Makefile b/arch/arm/mach-sunxi/Makefile
>> index 27b168f..589239b 100644
>> --- a/arch/arm/mach-sunxi/Makefile
>> +++ b/arch/arm/mach-sunxi/Makefile
>> @@ -1,2 +1,2 @@
>> -obj-$(CONFIG_ARCH_SUNXI) += sunxi.o
>> +obj-$(CONFIG_ARCH_SUNXI) += sunxi.o sunxi-soc-id.o
>>   obj-$(CONFIG_SMP) += platsmp.o
>> diff --git a/arch/arm/mach-sunxi/sunxi-soc-id.c b/arch/arm/mach-sunxi/sunxi-soc-id.c
>> new file mode 100644
>> index 0000000..c7eff1c
>> --- /dev/null
>> +++ b/arch/arm/mach-sunxi/sunxi-soc-id.c
>> @@ -0,0 +1,226 @@
>> +/*
>> + * SoC revision detection for sunxi SoCs
>> + *
>> + * Copyright 2014 Emilio L?pez
>> + *
>> + * Emilio L?pez <emilio@elopez.com.ar>
>> + *
>> + * 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.
>> + */
>> +
>> +#include <linux/err.h>
>> +#include <linux/io.h>
>> +#include <linux/of.h>
>> +#include <linux/of_address.h>
>> +#include <linux/slab.h>
>> +#include <linux/string.h>
>> +#include <linux/sys_soc.h>
>> +
>> +#include "sunxi-soc-id.h"
>> +
>> +/*
>> + * On the A10 SoC, we can read the revision information from the timer
>> + * block. The detection logic is extracted from similar code on the
>> + * Allwinner vendor tree, as this is undocumented on the user manual
>> + */
>> +
>> +#define TIMER_SOC_REV_REG		0x13c
>> +#define TIMER_SOC_REV_CLEAR(val)	((val) & ~(0x3 << 6))
>> +#define TIMER_SOC_REV_GET(val)		(((val) >> 6) & 0x3)
>> +
>> +static const struct of_device_id sun4i_timer_compatible[] __initconst = {
>> +	{ .compatible = "allwinner,sun4i-a10-timer", },
>> +	{},
>> +};
>> +
>> +static int __init sun4i_get_soc_revision(void)
>> +{
>> +	struct device_node *np;
>> +	void __iomem *base;
>> +	u32 val;
>> +	int ret;
>> +
>> +	/* Find the timer node */
>> +	np = of_find_matching_node(NULL, sun4i_timer_compatible);
>> +	if (!np)
>> +		return -ENODEV;
>> +
>> +	/* Temporarily map it for reading */
>> +	base = of_iomap(np, 0);
>> +	if (!base) {
>> +		of_node_put(np);
>> +		return -ENOMEM;
>> +	}
>> +
>> +	/* Clear the SoC revision bits and rewrite the register */
>> +	val = readl(base + TIMER_SOC_REV_REG);
>> +	val = TIMER_SOC_REV_CLEAR(val);
>> +	writel(val, base + TIMER_SOC_REV_REG);
>> +
>> +	/* Now read it again and see what shows up */
>> +	val = readl(base + TIMER_SOC_REV_REG);
>> +	val = TIMER_SOC_REV_GET(val);
>> +
>> +	switch (val) {
>> +	case 0:  /* revision A */
>> +		ret = 'A';
>> +	case 3:  /* revision B */
>> +		ret = 'B';
>> +	default: /* revision C */
>> +		ret = 'C';
>
> What's programmed in there in the case of the rev C?

Something that's not a 3 or a 0 - I don't have any more specifics than 
what the Allwinner code does :(

https://github.com/linux-sunxi/linux-sunxi/blob/lichee-dev/arch/arm/mach-sun4i/core.c#L418

I just noticed that I forgot the breaks while reworking this from return 
to a variable, so everything is rev C right now - I'll fix that.

>> +	}
>> +
>> +	iounmap(base);
>> +	of_node_put(np);
>> +
>> +	return ret;
>> +}
>> +
>> +/*
>> + * On the sun5i SoCs (A10S, A13), we can read the revision information
>> + * from the first bits in the Security ID. The detection logic is
>> + * extracted from similar code on the Allwinner vendor tree, as this
>> + * is undocumented on the user manual.
>> + */
>> +
>> +static const struct of_device_id sun5i_sid_compatible[] __initconst = {
>> +	{ .compatible = "allwinner,sun4i-a10-sid", },
>> +	{},
>> +};
>> +
>> +static int __init sun5i_get_soc_revision(void)
>> +{
>> +	struct device_node *np;
>> +	void __iomem *sid;
>> +	u32 val;
>> +	int ret;
>> +
>> +	/* Find the SID node */
>> +	np = of_find_matching_node(NULL, sun5i_sid_compatible);
>> +	if (!np)
>> +		return -ENODEV;
>> +
>> +	/* Temporarily map it for reading */
>> +	sid = of_iomap(np, 0);
>> +	if (!sid) {
>> +		of_node_put(np);
>> +		return -ENOMEM;
>> +	}
>> +
>> +	/* Read and extract the chip revision from the SID */
>> +	val = readl(sid);
>> +	val = (val >> 8) & 0xffffff;
>> +
>> +	switch (val) {
>> +	case 0:        /* A10S/A13 rev A */
>> +	case 0x162541: /* A10S/A13 rev A */
>> +	case 0x162565: /* A13 rev A */
>
> Some defines would be nice.

Ok

> Also, isn't the SID supposed to identify any SoC, not just the sun5i?

The Allwinner code I've seen uses the SS to detect the SoC (which 
luckily we don't need, as we have DT) and then reads the timer for sun4i 
or the SID for sun5i.

>> +		ret = 'A';
>> +		break;
>> +	case 0x162542: /* A10S/A13 rev B */
>> +		ret = 'B';
>> +		break;
>> +	default:       /* Unknown chip revision */
>> +		ret = -ENODATA;
>> +	}
>> +
>> +	iounmap(sid);
>> +	of_node_put(np);
>> +
>> +	return ret;
>> +}
>> +
>> +int __init sunxi_soc_revision(void)
>> +{
>> +	static int revision = -ENODEV;
>> +
>> +	/* Try to query the hardware just once */
>> +	if (!IS_ERR_VALUE(revision))
>> +		return revision;
>> +
>> +	if (of_machine_is_compatible("allwinner,sun4i-a10")) {
>> +		revision = sun4i_get_soc_revision();
>> +	} else if (of_machine_is_compatible("allwinner,sun5i-a10s") ||
>> +		   of_machine_is_compatible("allwinner,sun5i-a13")) {
>> +		revision = sun5i_get_soc_revision();
>> +	}
>> +
>> +	return revision;
>> +}
>> +
>> +/* Matches for the sunxi SoCs we know of */
>> +static const struct of_device_id soc_matches[] __initconst = {
>> +	{ .compatible = "allwinner,sun4i-a10", .data = "A10 (sun4i)" },
>> +	{ .compatible = "allwinner,sun5i-a13", .data = "A13 (sun5i)" },
>> +	{ .compatible = "allwinner,sun5i-a10s", .data = "A10S (sun5i)" },
>> +	{ .compatible = "allwinner,sun6i-a31", .data = "A31 (sun6i)" },
>> +	{ .compatible = "allwinner,sun7i-a20", .data = "A20 (sun7i)" },
>> +	{ .compatible = "allwinner,sun8i-a23", .data = "A23 (sun8i)" },
>> +	{ /* sentinel */ },
>> +};
>
> Hmmm, no, either auto-detect it, or don't, but that's useless. We can
> already have the same information from the DT directly.

I don't quite get what you're trying to say here. This is here to
a) get a fancy string saying what the SoC is (AFAIK that's not directly 
available on the DT)
b) only run in the listed SoCs

>> +static int __init sunxi_register_soc_device(void)
>> +{
>> +	struct soc_device_attribute *soc_dev_attr;
>> +	struct soc_device *soc_dev;
>> +	const struct of_device_id *match;
>> +	struct device_node *root;
>> +	int revision;
>> +
>> +	/* Only run on sunxi SoCs that we know of */
>> +	root = of_find_node_by_path("/");
>> +	match = of_match_node(soc_matches, root);
>> +	if (!match)
>> +		goto exit;

See here

>> +
>> +	soc_dev_attr = kzalloc(sizeof(*soc_dev_attr), GFP_KERNEL);
>> +	if (!soc_dev_attr)
>> +		goto exit;
>> +
>> +	/* Read the machine name if available */
>> +	of_property_read_string(root, "model", &soc_dev_attr->machine);
>> +
>> +	soc_dev_attr->family = kstrdup("Allwinner A Series", GFP_KERNEL);
>
> I think the family should be sun*i

Here is a list of the ones currently in use for family

"Cirrus Logic EP93xx"
"Freescale i.MX"
"Integrator"
"Marvell"
"Freescale MXS Family"
"Tegra"
"Xilinx Zynq"

There does not seem to be a real consensus on what these mean. There's a 
binding document on Documentation/ABI/testing/sysfs-devices-soc but it's 
not really detailed.

>
>> +	soc_dev_attr->soc_id = kstrdup(match->data, GFP_KERNEL);
>
> And soc_id would be just the name of the SoC.

According to the binding this is a serial number. Lee, could you help us 
decide what these fields should look like?

I'm now inclining to have eg. family=sun5i, machine=A13, keep revision 
the same and drop soc_id.

>> +
>> +	/* Revision may not always be available */
>> +	revision = sunxi_soc_revision();
>> +	if (IS_ERR_VALUE(revision))
>> +		soc_dev_attr->revision = kstrdup("Unknown", GFP_KERNEL);
>> +	else
>> +		soc_dev_attr->revision = kasprintf(GFP_KERNEL, "%c", revision);
>> +
>> +	soc_dev = soc_device_register(soc_dev_attr);
>> +	if (IS_ERR(soc_dev))
>> +		goto free_struct;
>> +
>> +	/*
>> +	 * Print an informational line mentioning the hardware details.
>> +	 * It may come in handy during bug reports, as some early SoC
>> +	 * revisions have hardware quirks and do not get much testing.
>> +	 */
>> +	pr_info("SoC bus registered, running %s %s, revision %s\n",
>> +		soc_dev_attr->family, soc_dev_attr->soc_id,
>> +		soc_dev_attr->revision);
>
> Maybe a pr_fmt would be nice here.

With something like "Allwinner SoC bus"?

>> +
>> +	return 0;
>> +
>> +free_struct:
>> +	kfree(soc_dev_attr->family);
>> +	kfree(soc_dev_attr->soc_id);
>> +	kfree(soc_dev_attr->revision);
>> +	kfree(soc_dev_attr);
>> +exit:
>> +	of_node_put(root);
>> +
>> +	return 0;
>> +}
>> +postcore_initcall(sunxi_register_soc_device)
>
> I'm kind of reluctant to this. That would mean that it will run for
> every SoC.

The overhead should be negligible - on non-sunxi, it will only check if 
the machine is compatible by looking at the root node and bail out as 
it's not the case.

> Can't you tie it to the system controller, and have it probed as
> usual?

I considered tying this to init_machine, right before the DT starts to 
get probed. That would mean actually writing init_machine hooks calling 
into this for some of our machines, and getting us further from the 
generic DT machine nirvana though. As the quirk bits do not directly 
depend on this, we can probably get away with making this a normal
driver compatible with all our SoCs. Let me know which way do you 
prefer, and I'll be happy to oblige.

Cheers!

Emilio

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

* [PATCH 4/9] clk: sunxi: PLL2 support for sun4i, sun5i and sun7i
  2014-08-03 12:44   ` Maxime Ripard
  2014-08-03 15:58     ` Chen-Yu Tsai
@ 2014-08-03 22:02     ` Emilio López
  2014-08-04 20:02       ` Maxime Ripard
  1 sibling, 1 reply; 36+ messages in thread
From: Emilio López @ 2014-08-03 22:02 UTC (permalink / raw)
  To: linux-arm-kernel

Hi,

El 03/08/14 a las 09:44, Maxime Ripard escibi?:
> On Thu, Jul 31, 2014 at 06:28:07PM -0300, Emilio L?pez wrote:
>> This patch adds support for PLL2 and derivates on sun4i, sun5i and
>> sun7i SoCs. As this PLL is only used for audio and requires good
>> accuracy, we only support two known good rates.
>>
>> Signed-off-by: Emilio L?pez <emilio@elopez.com.ar>
>> ---
>>
(...)
>> +	/* PLL2x8, double of PLL2 without the post divisor */
>> +	of_property_read_string_index(np, "clock-output-names", 3, &clk_name);
>> +	clks[3] = clk_register_fixed_factor(NULL, clk_name, parent,
>> +					    CLK_SET_RATE_PARENT, 2 * 4, 1);
>
> Why have you declared them here, instead of using fixed factors in the
> DT directly, like we have done in the past?

I'd prefer to see one clock with four outputs represented as one clock 
with four outputs rather than one clock with one output and other three 
weirdly named nodes floating around. I can only see four uses of 
fixed-factor-clock in our DTSI, three of which are "pass through" clocks 
for ahb and ar100 on sun6/8i, and a fourth one to implement the 32k that 
results from reducing the 24M osc on sun7i.

As a second reason, implementing it as fixed factors would be slightly 
incorrect, as it's only "x8" when you use the limited set of factors 
that Allwinner and us are using for audio. If we were ever to support 
the full range of the PLL. the DT binding would suddenly be off. By 
using a single binding, the meaning is self-contained in the driver.

As a third reason, of_fixed_factor_clk_setup() does not set 
CLK_SET_RATE_PARENT, which we'd need to propagate the rate change upstream.

Cheers,

Emilio

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

* [PATCH 6/9] clk: sunxi: mod1 clock support
  2014-08-03 12:47   ` Maxime Ripard
@ 2014-08-03 22:11     ` Emilio López
  2014-08-04 19:52       ` Maxime Ripard
  0 siblings, 1 reply; 36+ messages in thread
From: Emilio López @ 2014-08-03 22:11 UTC (permalink / raw)
  To: linux-arm-kernel

Hi,

El 03/08/14 a las 09:47, Maxime Ripard escibi?:
> On Thu, Jul 31, 2014 at 06:28:09PM -0300, Emilio L?pez wrote:
>> The module 1 type of clocks consist of a gate and a mux and are used on
>> the audio blocks to mux and gate the PLL2 outputs for AC97, IIS or
>> SPDIF. This commit adds support for them on the sunxi clock driver.
>>
>> Not-signed-off-by: Emilio L?pez <emilio@elopez.com.ar>
>
> Why so?
>
> It looks quite good.

Because I have not tested this with any actual mod1 clients. Once Jon or 
Marcus report it's ok I'll be happy to s/Not-s/S/; same with the DT 
bindings.

>> ---
>>
>> Changes from RFC:
>>   * Document compatible used
>>
>>   Documentation/devicetree/bindings/clock/sunxi.txt |  1 +
>>   drivers/clk/sunxi/Makefile                        |  1 +
>>   drivers/clk/sunxi/clk-a10-mod1.c                  | 69 +++++++++++++++++++++++
>>   3 files changed, 71 insertions(+)
>>   create mode 100644 drivers/clk/sunxi/clk-a10-mod1.c
>>
>> diff --git a/Documentation/devicetree/bindings/clock/sunxi.txt b/Documentation/devicetree/bindings/clock/sunxi.txt
>> index d18b89b..b028ee2 100644
>> --- a/Documentation/devicetree/bindings/clock/sunxi.txt
>> +++ b/Documentation/devicetree/bindings/clock/sunxi.txt
>> @@ -50,6 +50,7 @@ Required properties:
>>   	"allwinner,sun8i-a23-apb2-gates-clk" - for the APB2 gates on A23
>>   	"allwinner,sun4i-a10-codec-clk" - for the codec clock on A10
>>   	"allwinner,sun4i-a10-mod0-clk" - for the module 0 family of clocks
>> +	"allwinner,sun4i-a10-mod1-clk" - for the module 1 family of clocks
>>   	"allwinner,sun7i-a20-out-clk" - for the external output clocks
>>   	"allwinner,sun7i-a20-gmac-clk" - for the GMAC clock module on A20/A31
>>   	"allwinner,sun4i-a10-usb-clk" - for usb gates + resets on A10 / A20
>> diff --git a/drivers/clk/sunxi/Makefile b/drivers/clk/sunxi/Makefile
>> index a7a96f8..75d113d 100644
>> --- a/drivers/clk/sunxi/Makefile
>> +++ b/drivers/clk/sunxi/Makefile
>> @@ -5,6 +5,7 @@
>>   obj-y += clk-sunxi.o clk-factors.o
>>   obj-y += clk-a10-codec.o
>>   obj-y += clk-a10-hosc.o
>> +obj-y += clk-a10-mod1.o
>>   obj-y += clk-a10-pll2.o
>>   obj-y += clk-a20-gmac.o
>>
>> diff --git a/drivers/clk/sunxi/clk-a10-mod1.c b/drivers/clk/sunxi/clk-a10-mod1.c
>> new file mode 100644
>> index 0000000..09afd54
>> --- /dev/null
>> +++ b/drivers/clk/sunxi/clk-a10-mod1.c
>> @@ -0,0 +1,69 @@
>> +/*
>> + * Copyright 2014 Emilio L?pez
>> + *
>> + * Emilio L?pez <emilio@elopez.com.ar>
>> + *
>> + * 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.
>> + */
>> +
>> +#include <linux/clk-provider.h>
>> +#include <linux/clkdev.h>
>> +#include <linux/of.h>
>> +#include <linux/of_address.h>
>> +
>> +static DEFINE_SPINLOCK(mod1_lock);
>> +
>> +#define SUN4I_MOD1_ENABLE	31
>> +#define SUN4I_MOD1_MUX		16
>> +#define SUN4I_MOD1_MUX_WIDTH	2
>> +#define SUN4I_MOD1_MAX_PARENTS	4
>> +
>> +static void __init sun4i_mod1_clk_setup(struct device_node *node)
>> +{
>> +	struct clk *clk;
>> +	struct clk_mux *mux;
>> +	struct clk_gate *gate;
>> +	const char *parents[4];
>> +	const char *clk_name = node->name;
>> +	void __iomem *reg;
>> +	int i = 0;
>> +
>> +	mux = kzalloc(sizeof(*mux), GFP_KERNEL);
>> +	gate = kzalloc(sizeof(*gate), GFP_KERNEL);
>> +	if (!mux || !gate) {
>> +		kfree(mux);
>> +		kfree(gate);
>> +		return;
>> +	}
>> +
>> +	of_property_read_string(node, "clock-output-names", &clk_name);
>> +	reg = of_iomap(node, 0);
>> +
>> +	while (i < SUN4I_MOD1_MAX_PARENTS &&
>> +	       (parents[i] = of_clk_get_parent_name(node, i)) != NULL)
>> +		i++;
>> +
>> +	gate->reg = reg;
>> +	gate->bit_idx = SUN4I_MOD1_ENABLE;
>> +	gate->lock = &mod1_lock;
>> +	mux->reg = reg;
>> +	mux->shift = SUN4I_MOD1_MUX;
>> +	mux->mask = BIT(SUN4I_MOD1_MUX_WIDTH) - 1;
>> +	mux->lock = &mod1_lock;
>> +
>> +	clk = clk_register_composite(NULL, clk_name, parents, i,
>> +				     &mux->hw, &clk_mux_ops,
>> +				     NULL, NULL,
>> +				     &gate->hw, &clk_gate_ops, 0);

We'll probably need SET_RATE_PARENT here as well

>> +	if (!IS_ERR(clk))
>> +		of_clk_add_provider(node, of_clk_src_simple_get, clk);
>> +}
>> +CLK_OF_DECLARE(sun4i_mod1, "allwinner,sun4i-a10-mod1-clk", sun4i_mod1_clk_setup);
>
> Why are you not using clk-sunxi for this? It has pretty much
> everything in place to do so already.

If this were to have a rate component we could reuse the factors 
infrastructure, but it doesn't, as it's just a gate and a mux.

Cheers,

Emilio

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

* [PATCH 7/9] ARM: sunxi: dt: Add PLL2 support
  2014-08-03 15:55       ` Chen-Yu Tsai
@ 2014-08-03 22:15         ` Emilio López
  2014-08-04 19:53           ` Maxime Ripard
  0 siblings, 1 reply; 36+ messages in thread
From: Emilio López @ 2014-08-03 22:15 UTC (permalink / raw)
  To: linux-arm-kernel

Hi,

El 03/08/14 a las 12:55, Chen-Yu Tsai escibi?:
> On Sun, Aug 3, 2014 at 8:50 PM, Maxime Ripard
> <maxime.ripard@free-electrons.com> wrote:
>> On Thu, Jul 31, 2014 at 05:46:09PM -0400, jonsmirl at gmail.com wrote:
>>> Would it be better to name this "allwinner,sun4i-a10-pll2-clk" instead
>>> of "allwinner,sun4i-a10-b-pll2-clk"? By encoding the b in it everyone
>>> is going to wonder what to do on the 'c' revision which is the most
>>> common revision.
>>
>> Not really, the way we works usually is that the compatible is the one
>> from the first SoC that implemented that IP. If the rev C has the same
>> IP than rev B, then we're using the rev B compatible.
>>
>>>
>>> The revision based rename would then be from
>>> "allwinner,sun4i-a10-pll2-clk" to "allwinner,sun4i-a10-a-pll2-clk".
>>
>> Though, I'd agree with you. We should have a single compatible in the
>> DT, a generic one, that would trigger the auto-detection, and might
>> change it to the rev A one, but the rev B doesn't make much sense.
>
> I agree. The rev B would make people reading the DT wonder what happened
> to rev A.

Would you like to see "allwinner,sun4i-a10-pll2-clk" on the DT then? 
Should we document it as a magic property triggering an autodetect on 
the clock binding document?

Cheers!

Emilio

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

* [PATCH 1/9] ARM: sunxi: introduce SoC identification support
  2014-08-03 21:45     ` Emilio López
@ 2014-08-04  8:08       ` Lee Jones
  2014-08-04 19:48       ` Maxime Ripard
  1 sibling, 0 replies; 36+ messages in thread
From: Lee Jones @ 2014-08-04  8:08 UTC (permalink / raw)
  To: linux-arm-kernel

On Sun, 03 Aug 2014, Emilio L?pez wrote:
> El 03/08/14 a las 09:40, Maxime Ripard escibi?:
> >On Thu, Jul 31, 2014 at 06:28:04PM -0300, Emilio L?pez wrote:
> >>This commit adds SoC bus support on the sunxi platform, and exposes
> >>information such as the hardware revision to userspace and other kernel
> >>clients during init. A message with this information is also printed to
> >>the kernel log to ease future bug triaging.
> >>
> >>Signed-off-by: Emilio L?pez <emilio@elopez.com.ar>
> >>---
> >>  arch/arm/mach-sunxi/Kconfig        |   1 +
> >>  arch/arm/mach-sunxi/Makefile       |   2 +-
> >>  arch/arm/mach-sunxi/sunxi-soc-id.c | 226 +++++++++++++++++++++++++++++++++++++
> >>  arch/arm/mach-sunxi/sunxi-soc-id.h |   6 +
> >>  4 files changed, 234 insertions(+), 1 deletion(-)
> >>  create mode 100644 arch/arm/mach-sunxi/sunxi-soc-id.c
> >>  create mode 100644 arch/arm/mach-sunxi/sunxi-soc-id.h

[...]

> >>+	soc_dev_attr = kzalloc(sizeof(*soc_dev_attr), GFP_KERNEL);
> >>+	if (!soc_dev_attr)
> >>+		goto exit;
> >>+
> >>+	/* Read the machine name if available */
> >>+	of_property_read_string(root, "model", &soc_dev_attr->machine);
> >>+
> >>+	soc_dev_attr->family = kstrdup("Allwinner A Series", GFP_KERNEL);
> >
> >I think the family should be sun*i
> 
> Here is a list of the ones currently in use for family
> 
> "Cirrus Logic EP93xx"
> "Freescale i.MX"
> "Integrator"
> "Marvell"
> "Freescale MXS Family"
> "Tegra"
> "Xilinx Zynq"
> 
> There does not seem to be a real consensus on what these mean.
> There's a binding document on
> Documentation/ABI/testing/sysfs-devices-soc but it's not really
> detailed.
> 
> >
> >>+	soc_dev_attr->soc_id = kstrdup(match->data, GFP_KERNEL);
> >
> >And soc_id would be just the name of the SoC.
> 
> According to the binding this is a serial number. Lee, could you
> help us decide what these fields should look like?
> 
> I'm now inclining to have eg. family=sun5i, machine=A13, keep
> revision the same and drop soc_id.

Voila: Documentation/ABI/testing/sysfs-devices-soc

-- 
Lee Jones
Linaro STMicroelectronics Landing Team Lead
Linaro.org ? Open source software for ARM SoCs
Follow Linaro: Facebook | Twitter | Blog

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

* [PATCH 2/9] ARM: sunxi: quirk support
  2014-08-03 21:37     ` Emilio López
@ 2014-08-04 19:32       ` Maxime Ripard
  0 siblings, 0 replies; 36+ messages in thread
From: Maxime Ripard @ 2014-08-04 19:32 UTC (permalink / raw)
  To: linux-arm-kernel

On Sun, Aug 03, 2014 at 06:37:31PM -0300, Emilio L?pez wrote:
> Hi Maxime,
> 
> El 03/08/14 a las 09:42, Maxime Ripard escibi?:
> >On Thu, Jul 31, 2014 at 06:28:05PM -0300, Emilio L?pez wrote:
> >>Currently, some hardware revisions of sunxi SoCs need special care on
> >>some blocks because of hardware differences and/or bugs. Unfortunately,
> >>it is unfeasible to account for these issues directly when writing the
> >>device tree, as SoC revision can vary between different units of the
> >>same device. This commit introduces a place to adjust DT compatibles
> >>as needed to work around said issues before devices are probed. To
> >>demonstrate usage, two quirks are added for the PLL2 and audio codec
> >>on sun4i.
> >>
> >>Signed-off-by: Emilio L?pez <emilio@elopez.com.ar>
> >>---
> (...)
> >>+static int __init sunxi_apply_quirks(void)
> >>+{
> >>+	if (of_machine_is_compatible("allwinner,sun4i-a10")) {
> >>+		sun4i_pll2_quirk();
> >>+		sun4i_codec_quirk();
> >>+	}
> >>+
> >>+	return 0;
> >>+}
> >>+postcore_initcall(sunxi_apply_quirks)
> >
> >Have you tested it? My guess is that it wolud have to run *much*
> >sooner, before of_platform_populate to be effective.
> 
> I have, I was able to break my UART with it :)
> 
> of_platform_populate() is called from arch/arm/kernel/setup.c on
> arch_initcall. From include/linux/init.h
> 
> ...
> #define postcore_initcall(fn)           __define_initcall(fn, 2)
> #define postcore_initcall_sync(fn)      __define_initcall(fn, 2s)
> #define arch_initcall(fn)               __define_initcall(fn, 3)
> ...
> 
> So it should be fine.

Hmmm, right.

I was under the impression it was done sooner, but I was wrong,
obviously.

Still, the machine init callback has the advantage of being run only
on the current machine, not in every case.

Maxime

-- 
Maxime Ripard, Free Electrons
Embedded Linux, Kernel and Android engineering
http://free-electrons.com
-------------- next part --------------
A non-text attachment was scrubbed...
Name: signature.asc
Type: application/pgp-signature
Size: 819 bytes
Desc: Digital signature
URL: <http://lists.infradead.org/pipermail/linux-arm-kernel/attachments/20140804/6657bbdd/attachment.sig>

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

* [PATCH 1/9] ARM: sunxi: introduce SoC identification support
  2014-08-03 21:45     ` Emilio López
  2014-08-04  8:08       ` Lee Jones
@ 2014-08-04 19:48       ` Maxime Ripard
  1 sibling, 0 replies; 36+ messages in thread
From: Maxime Ripard @ 2014-08-04 19:48 UTC (permalink / raw)
  To: linux-arm-kernel

Hi,

On Sun, Aug 03, 2014 at 06:45:37PM -0300, Emilio L?pez wrote:
> Hi Maxime,
> 
> Thanks for taking the time to review this :)

You're welcome

> 
> El 03/08/14 a las 09:40, Maxime Ripard escibi?:
> >Hi Emilio,
> >
> >On Thu, Jul 31, 2014 at 06:28:04PM -0300, Emilio L?pez wrote:
> >>This commit adds SoC bus support on the sunxi platform, and exposes
> >>information such as the hardware revision to userspace and other kernel
> >>clients during init. A message with this information is also printed to
> >>the kernel log to ease future bug triaging.
> >>
> >>Signed-off-by: Emilio L?pez <emilio@elopez.com.ar>
> >>---
> >>  arch/arm/mach-sunxi/Kconfig        |   1 +
> >>  arch/arm/mach-sunxi/Makefile       |   2 +-
> >>  arch/arm/mach-sunxi/sunxi-soc-id.c | 226 +++++++++++++++++++++++++++++++++++++
> >>  arch/arm/mach-sunxi/sunxi-soc-id.h |   6 +
> >>  4 files changed, 234 insertions(+), 1 deletion(-)
> >>  create mode 100644 arch/arm/mach-sunxi/sunxi-soc-id.c
> >>  create mode 100644 arch/arm/mach-sunxi/sunxi-soc-id.h
> >>
> >>diff --git a/arch/arm/mach-sunxi/Kconfig b/arch/arm/mach-sunxi/Kconfig
> >>index 6434e3b..4a199df 100644
> >>--- a/arch/arm/mach-sunxi/Kconfig
> >>+++ b/arch/arm/mach-sunxi/Kconfig
> >>@@ -5,6 +5,7 @@ menuconfig ARCH_SUNXI
> >>  	select GENERIC_IRQ_CHIP
> >>  	select PINCTRL
> >>  	select PINCTRL_SUNXI
> >>+	select SOC_BUS
> >>  	select SUN4I_TIMER
> >>
> >>  if ARCH_SUNXI
> >>diff --git a/arch/arm/mach-sunxi/Makefile b/arch/arm/mach-sunxi/Makefile
> >>index 27b168f..589239b 100644
> >>--- a/arch/arm/mach-sunxi/Makefile
> >>+++ b/arch/arm/mach-sunxi/Makefile
> >>@@ -1,2 +1,2 @@
> >>-obj-$(CONFIG_ARCH_SUNXI) += sunxi.o
> >>+obj-$(CONFIG_ARCH_SUNXI) += sunxi.o sunxi-soc-id.o
> >>  obj-$(CONFIG_SMP) += platsmp.o
> >>diff --git a/arch/arm/mach-sunxi/sunxi-soc-id.c b/arch/arm/mach-sunxi/sunxi-soc-id.c
> >>new file mode 100644
> >>index 0000000..c7eff1c
> >>--- /dev/null
> >>+++ b/arch/arm/mach-sunxi/sunxi-soc-id.c
> >>@@ -0,0 +1,226 @@
> >>+/*
> >>+ * SoC revision detection for sunxi SoCs
> >>+ *
> >>+ * Copyright 2014 Emilio L?pez
> >>+ *
> >>+ * Emilio L?pez <emilio@elopez.com.ar>
> >>+ *
> >>+ * 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.
> >>+ */
> >>+
> >>+#include <linux/err.h>
> >>+#include <linux/io.h>
> >>+#include <linux/of.h>
> >>+#include <linux/of_address.h>
> >>+#include <linux/slab.h>
> >>+#include <linux/string.h>
> >>+#include <linux/sys_soc.h>
> >>+
> >>+#include "sunxi-soc-id.h"
> >>+
> >>+/*
> >>+ * On the A10 SoC, we can read the revision information from the timer
> >>+ * block. The detection logic is extracted from similar code on the
> >>+ * Allwinner vendor tree, as this is undocumented on the user manual
> >>+ */
> >>+
> >>+#define TIMER_SOC_REV_REG		0x13c
> >>+#define TIMER_SOC_REV_CLEAR(val)	((val) & ~(0x3 << 6))
> >>+#define TIMER_SOC_REV_GET(val)		(((val) >> 6) & 0x3)
> >>+
> >>+static const struct of_device_id sun4i_timer_compatible[] __initconst = {
> >>+	{ .compatible = "allwinner,sun4i-a10-timer", },
> >>+	{},
> >>+};
> >>+
> >>+static int __init sun4i_get_soc_revision(void)
> >>+{
> >>+	struct device_node *np;
> >>+	void __iomem *base;
> >>+	u32 val;
> >>+	int ret;
> >>+
> >>+	/* Find the timer node */
> >>+	np = of_find_matching_node(NULL, sun4i_timer_compatible);
> >>+	if (!np)
> >>+		return -ENODEV;
> >>+
> >>+	/* Temporarily map it for reading */
> >>+	base = of_iomap(np, 0);
> >>+	if (!base) {
> >>+		of_node_put(np);
> >>+		return -ENOMEM;
> >>+	}
> >>+
> >>+	/* Clear the SoC revision bits and rewrite the register */
> >>+	val = readl(base + TIMER_SOC_REV_REG);
> >>+	val = TIMER_SOC_REV_CLEAR(val);
> >>+	writel(val, base + TIMER_SOC_REV_REG);
> >>+
> >>+	/* Now read it again and see what shows up */
> >>+	val = readl(base + TIMER_SOC_REV_REG);
> >>+	val = TIMER_SOC_REV_GET(val);
> >>+
> >>+	switch (val) {
> >>+	case 0:  /* revision A */
> >>+		ret = 'A';
> >>+	case 3:  /* revision B */
> >>+		ret = 'B';
> >>+	default: /* revision C */
> >>+		ret = 'C';
> >
> >What's programmed in there in the case of the rev C?
> 
> Something that's not a 3 or a 0 - I don't have any more specifics
> than what the Allwinner code does :(
> 
> https://github.com/linux-sunxi/linux-sunxi/blob/lichee-dev/arch/arm/mach-sun4i/core.c#L418

It would be interesting to see if there's a value programmed.

> I just noticed that I forgot the breaks while reworking this from
> return to a variable, so everything is rev C right now - I'll fix
> that.
> 
> >>+	}
> >>+
> >>+	iounmap(base);
> >>+	of_node_put(np);
> >>+
> >>+	return ret;
> >>+}
> >>+
> >>+/*
> >>+ * On the sun5i SoCs (A10S, A13), we can read the revision information
> >>+ * from the first bits in the Security ID. The detection logic is
> >>+ * extracted from similar code on the Allwinner vendor tree, as this
> >>+ * is undocumented on the user manual.
> >>+ */
> >>+
> >>+static const struct of_device_id sun5i_sid_compatible[] __initconst = {
> >>+	{ .compatible = "allwinner,sun4i-a10-sid", },
> >>+	{},
> >>+};
> >>+
> >>+static int __init sun5i_get_soc_revision(void)
> >>+{
> >>+	struct device_node *np;
> >>+	void __iomem *sid;
> >>+	u32 val;
> >>+	int ret;
> >>+
> >>+	/* Find the SID node */
> >>+	np = of_find_matching_node(NULL, sun5i_sid_compatible);
> >>+	if (!np)
> >>+		return -ENODEV;
> >>+
> >>+	/* Temporarily map it for reading */
> >>+	sid = of_iomap(np, 0);
> >>+	if (!sid) {
> >>+		of_node_put(np);
> >>+		return -ENOMEM;
> >>+	}
> >>+
> >>+	/* Read and extract the chip revision from the SID */
> >>+	val = readl(sid);
> >>+	val = (val >> 8) & 0xffffff;
> >>+
> >>+	switch (val) {
> >>+	case 0:        /* A10S/A13 rev A */
> >>+	case 0x162541: /* A10S/A13 rev A */
> >>+	case 0x162565: /* A13 rev A */
> >
> >Some defines would be nice.
> 
> Ok
> 
> >Also, isn't the SID supposed to identify any SoC, not just the sun5i?
> 
> The Allwinner code I've seen uses the SS to detect the SoC (which
> luckily we don't need, as we have DT) and then reads the timer for
> sun4i or the SID for sun5i.

SS being security system or system controller ? :)

> 
> >>+		ret = 'A';
> >>+		break;
> >>+	case 0x162542: /* A10S/A13 rev B */
> >>+		ret = 'B';
> >>+		break;
> >>+	default:       /* Unknown chip revision */
> >>+		ret = -ENODATA;
> >>+	}
> >>+
> >>+	iounmap(sid);
> >>+	of_node_put(np);
> >>+
> >>+	return ret;
> >>+}
> >>+
> >>+int __init sunxi_soc_revision(void)
> >>+{
> >>+	static int revision = -ENODEV;
> >>+
> >>+	/* Try to query the hardware just once */
> >>+	if (!IS_ERR_VALUE(revision))
> >>+		return revision;
> >>+
> >>+	if (of_machine_is_compatible("allwinner,sun4i-a10")) {
> >>+		revision = sun4i_get_soc_revision();
> >>+	} else if (of_machine_is_compatible("allwinner,sun5i-a10s") ||
> >>+		   of_machine_is_compatible("allwinner,sun5i-a13")) {
> >>+		revision = sun5i_get_soc_revision();
> >>+	}
> >>+
> >>+	return revision;
> >>+}
> >>+
> >>+/* Matches for the sunxi SoCs we know of */
> >>+static const struct of_device_id soc_matches[] __initconst = {
> >>+	{ .compatible = "allwinner,sun4i-a10", .data = "A10 (sun4i)" },
> >>+	{ .compatible = "allwinner,sun5i-a13", .data = "A13 (sun5i)" },
> >>+	{ .compatible = "allwinner,sun5i-a10s", .data = "A10S (sun5i)" },
> >>+	{ .compatible = "allwinner,sun6i-a31", .data = "A31 (sun6i)" },
> >>+	{ .compatible = "allwinner,sun7i-a20", .data = "A20 (sun7i)" },
> >>+	{ .compatible = "allwinner,sun8i-a23", .data = "A23 (sun8i)" },
> >>+	{ /* sentinel */ },
> >>+};
> >
> >Hmmm, no, either auto-detect it, or don't, but that's useless. We can
> >already have the same information from the DT directly.
> 
> I don't quite get what you're trying to say here. This is here to
> a) get a fancy string saying what the SoC is (AFAIK that's not
> directly available on the DT)
> b) only run in the listed SoCs

I'm saying that the point of this code is to auto-detect the SoC
ID/rev, so taking that info from the DT brings nothing compared to
using of_machine_is_compatible.

> >>+static int __init sunxi_register_soc_device(void)
> >>+{
> >>+	struct soc_device_attribute *soc_dev_attr;
> >>+	struct soc_device *soc_dev;
> >>+	const struct of_device_id *match;
> >>+	struct device_node *root;
> >>+	int revision;
> >>+
> >>+	/* Only run on sunxi SoCs that we know of */
> >>+	root = of_find_node_by_path("/");
> >>+	match = of_match_node(soc_matches, root);
> >>+	if (!match)
> >>+		goto exit;
> 
> See here
> 
> >>+
> >>+	soc_dev_attr = kzalloc(sizeof(*soc_dev_attr), GFP_KERNEL);
> >>+	if (!soc_dev_attr)
> >>+		goto exit;
> >>+
> >>+	/* Read the machine name if available */
> >>+	of_property_read_string(root, "model", &soc_dev_attr->machine);
> >>+
> >>+	soc_dev_attr->family = kstrdup("Allwinner A Series", GFP_KERNEL);
> >
> >I think the family should be sun*i
> 
> Here is a list of the ones currently in use for family
> 
> "Cirrus Logic EP93xx"
> "Freescale i.MX"
> "Integrator"
> "Marvell"
> "Freescale MXS Family"
> "Tegra"
> "Xilinx Zynq"
> 
> There does not seem to be a real consensus on what these mean.
> There's a binding document on
> Documentation/ABI/testing/sysfs-devices-soc but it's not really
> detailed.
> 
> >
> >>+	soc_dev_attr->soc_id = kstrdup(match->data, GFP_KERNEL);
> >
> >And soc_id would be just the name of the SoC.
> 
> According to the binding this is a serial number. Lee, could you
> help us decide what these fields should look like?
> 
> I'm now inclining to have eg. family=sun5i, machine=A13, keep
> revision the same and drop soc_id.

Ah, my guess would have been that the soc_id would be the AW16XX.

> 
> >>+
> >>+	/* Revision may not always be available */
> >>+	revision = sunxi_soc_revision();
> >>+	if (IS_ERR_VALUE(revision))
> >>+		soc_dev_attr->revision = kstrdup("Unknown", GFP_KERNEL);
> >>+	else
> >>+		soc_dev_attr->revision = kasprintf(GFP_KERNEL, "%c", revision);
> >>+
> >>+	soc_dev = soc_device_register(soc_dev_attr);
> >>+	if (IS_ERR(soc_dev))
> >>+		goto free_struct;
> >>+
> >>+	/*
> >>+	 * Print an informational line mentioning the hardware details.
> >>+	 * It may come in handy during bug reports, as some early SoC
> >>+	 * revisions have hardware quirks and do not get much testing.
> >>+	 */
> >>+	pr_info("SoC bus registered, running %s %s, revision %s\n",
> >>+		soc_dev_attr->family, soc_dev_attr->soc_id,
> >>+		soc_dev_attr->revision);
> >
> >Maybe a pr_fmt would be nice here.
> 
> With something like "Allwinner SoC bus"?

"sunxi" would be enough.

> 
> >>+
> >>+	return 0;
> >>+
> >>+free_struct:
> >>+	kfree(soc_dev_attr->family);
> >>+	kfree(soc_dev_attr->soc_id);
> >>+	kfree(soc_dev_attr->revision);
> >>+	kfree(soc_dev_attr);
> >>+exit:
> >>+	of_node_put(root);
> >>+
> >>+	return 0;
> >>+}
> >>+postcore_initcall(sunxi_register_soc_device)
> >
> >I'm kind of reluctant to this. That would mean that it will run for
> >every SoC.
> 
> The overhead should be negligible - on non-sunxi, it will only check
> if the machine is compatible by looking at the root node and bail
> out as it's not the case.
> 
> >Can't you tie it to the system controller, and have it probed as
> >usual?
> 
> I considered tying this to init_machine, right before the DT starts
> to get probed. That would mean actually writing init_machine hooks
> calling into this for some of our machines, and getting us further
> from the generic DT machine nirvana though. As the quirk bits do not
> directly depend on this, we can probably get away with making this a
> normal
> driver compatible with all our SoCs. Let me know which way do you
> prefer, and I'll be happy to oblige.

Isn't one of the point of the soc_device_register to be that the
registered soc_device would be the parent device of all the SoC
devices? (There's probably too many devices and soc words in that
sentence though).

That would make the registration of the soc_device before the call to
of_platform_populate mandatory.

If not, we can see it two-folds then:
  - First, setup the quirks, possibly caching any relevant values
  - Then, use a regular driver to register the soc_device with the
    infos you gathered from the cache

Maxime

-- 
Maxime Ripard, Free Electrons
Embedded Linux, Kernel and Android engineering
http://free-electrons.com
-------------- next part --------------
A non-text attachment was scrubbed...
Name: signature.asc
Type: application/pgp-signature
Size: 819 bytes
Desc: Digital signature
URL: <http://lists.infradead.org/pipermail/linux-arm-kernel/attachments/20140804/b70c2ead/attachment-0001.sig>

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

* [PATCH 6/9] clk: sunxi: mod1 clock support
  2014-08-03 22:11     ` Emilio López
@ 2014-08-04 19:52       ` Maxime Ripard
  0 siblings, 0 replies; 36+ messages in thread
From: Maxime Ripard @ 2014-08-04 19:52 UTC (permalink / raw)
  To: linux-arm-kernel

On Sun, Aug 03, 2014 at 07:11:36PM -0300, Emilio L?pez wrote:
> Hi,
> 
> El 03/08/14 a las 09:47, Maxime Ripard escibi?:
> >On Thu, Jul 31, 2014 at 06:28:09PM -0300, Emilio L?pez wrote:
> >>The module 1 type of clocks consist of a gate and a mux and are used on
> >>the audio blocks to mux and gate the PLL2 outputs for AC97, IIS or
> >>SPDIF. This commit adds support for them on the sunxi clock driver.
> >>
> >>Not-signed-off-by: Emilio L?pez <emilio@elopez.com.ar>
> >
> >Why so?
> >
> >It looks quite good.
> 
> Because I have not tested this with any actual mod1 clients. Once
> Jon or Marcus report it's ok I'll be happy to s/Not-s/S/; same with
> the DT bindings.

Ok.

> 
> >>---
> >>
> >>Changes from RFC:
> >>  * Document compatible used
> >>
> >>  Documentation/devicetree/bindings/clock/sunxi.txt |  1 +
> >>  drivers/clk/sunxi/Makefile                        |  1 +
> >>  drivers/clk/sunxi/clk-a10-mod1.c                  | 69 +++++++++++++++++++++++
> >>  3 files changed, 71 insertions(+)
> >>  create mode 100644 drivers/clk/sunxi/clk-a10-mod1.c
> >>
> >>diff --git a/Documentation/devicetree/bindings/clock/sunxi.txt b/Documentation/devicetree/bindings/clock/sunxi.txt
> >>index d18b89b..b028ee2 100644
> >>--- a/Documentation/devicetree/bindings/clock/sunxi.txt
> >>+++ b/Documentation/devicetree/bindings/clock/sunxi.txt
> >>@@ -50,6 +50,7 @@ Required properties:
> >>  	"allwinner,sun8i-a23-apb2-gates-clk" - for the APB2 gates on A23
> >>  	"allwinner,sun4i-a10-codec-clk" - for the codec clock on A10
> >>  	"allwinner,sun4i-a10-mod0-clk" - for the module 0 family of clocks
> >>+	"allwinner,sun4i-a10-mod1-clk" - for the module 1 family of clocks
> >>  	"allwinner,sun7i-a20-out-clk" - for the external output clocks
> >>  	"allwinner,sun7i-a20-gmac-clk" - for the GMAC clock module on A20/A31
> >>  	"allwinner,sun4i-a10-usb-clk" - for usb gates + resets on A10 / A20
> >>diff --git a/drivers/clk/sunxi/Makefile b/drivers/clk/sunxi/Makefile
> >>index a7a96f8..75d113d 100644
> >>--- a/drivers/clk/sunxi/Makefile
> >>+++ b/drivers/clk/sunxi/Makefile
> >>@@ -5,6 +5,7 @@
> >>  obj-y += clk-sunxi.o clk-factors.o
> >>  obj-y += clk-a10-codec.o
> >>  obj-y += clk-a10-hosc.o
> >>+obj-y += clk-a10-mod1.o
> >>  obj-y += clk-a10-pll2.o
> >>  obj-y += clk-a20-gmac.o
> >>
> >>diff --git a/drivers/clk/sunxi/clk-a10-mod1.c b/drivers/clk/sunxi/clk-a10-mod1.c
> >>new file mode 100644
> >>index 0000000..09afd54
> >>--- /dev/null
> >>+++ b/drivers/clk/sunxi/clk-a10-mod1.c
> >>@@ -0,0 +1,69 @@
> >>+/*
> >>+ * Copyright 2014 Emilio L?pez
> >>+ *
> >>+ * Emilio L?pez <emilio@elopez.com.ar>
> >>+ *
> >>+ * 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.
> >>+ */
> >>+
> >>+#include <linux/clk-provider.h>
> >>+#include <linux/clkdev.h>
> >>+#include <linux/of.h>
> >>+#include <linux/of_address.h>
> >>+
> >>+static DEFINE_SPINLOCK(mod1_lock);
> >>+
> >>+#define SUN4I_MOD1_ENABLE	31
> >>+#define SUN4I_MOD1_MUX		16
> >>+#define SUN4I_MOD1_MUX_WIDTH	2
> >>+#define SUN4I_MOD1_MAX_PARENTS	4
> >>+
> >>+static void __init sun4i_mod1_clk_setup(struct device_node *node)
> >>+{
> >>+	struct clk *clk;
> >>+	struct clk_mux *mux;
> >>+	struct clk_gate *gate;
> >>+	const char *parents[4];
> >>+	const char *clk_name = node->name;
> >>+	void __iomem *reg;
> >>+	int i = 0;
> >>+
> >>+	mux = kzalloc(sizeof(*mux), GFP_KERNEL);
> >>+	gate = kzalloc(sizeof(*gate), GFP_KERNEL);
> >>+	if (!mux || !gate) {
> >>+		kfree(mux);
> >>+		kfree(gate);
> >>+		return;
> >>+	}
> >>+
> >>+	of_property_read_string(node, "clock-output-names", &clk_name);
> >>+	reg = of_iomap(node, 0);
> >>+
> >>+	while (i < SUN4I_MOD1_MAX_PARENTS &&
> >>+	       (parents[i] = of_clk_get_parent_name(node, i)) != NULL)
> >>+		i++;
> >>+
> >>+	gate->reg = reg;
> >>+	gate->bit_idx = SUN4I_MOD1_ENABLE;
> >>+	gate->lock = &mod1_lock;
> >>+	mux->reg = reg;
> >>+	mux->shift = SUN4I_MOD1_MUX;
> >>+	mux->mask = BIT(SUN4I_MOD1_MUX_WIDTH) - 1;
> >>+	mux->lock = &mod1_lock;
> >>+
> >>+	clk = clk_register_composite(NULL, clk_name, parents, i,
> >>+				     &mux->hw, &clk_mux_ops,
> >>+				     NULL, NULL,
> >>+				     &gate->hw, &clk_gate_ops, 0);
> 
> We'll probably need SET_RATE_PARENT here as well
> 
> >>+	if (!IS_ERR(clk))
> >>+		of_clk_add_provider(node, of_clk_src_simple_get, clk);
> >>+}
> >>+CLK_OF_DECLARE(sun4i_mod1, "allwinner,sun4i-a10-mod1-clk", sun4i_mod1_clk_setup);
> >
> >Why are you not using clk-sunxi for this? It has pretty much
> >everything in place to do so already.
> 
> If this were to have a rate component we could reuse the factors
> infrastructure, but it doesn't, as it's just a gate and a mux.

Ah, yes, we have nothing that combines mux and gate. My bad :)

Maxime

-- 
Maxime Ripard, Free Electrons
Embedded Linux, Kernel and Android engineering
http://free-electrons.com
-------------- next part --------------
A non-text attachment was scrubbed...
Name: signature.asc
Type: application/pgp-signature
Size: 819 bytes
Desc: Digital signature
URL: <http://lists.infradead.org/pipermail/linux-arm-kernel/attachments/20140804/cf72b97f/attachment.sig>

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

* [PATCH 7/9] ARM: sunxi: dt: Add PLL2 support
  2014-08-03 22:15         ` Emilio López
@ 2014-08-04 19:53           ` Maxime Ripard
  0 siblings, 0 replies; 36+ messages in thread
From: Maxime Ripard @ 2014-08-04 19:53 UTC (permalink / raw)
  To: linux-arm-kernel

On Sun, Aug 03, 2014 at 07:15:00PM -0300, Emilio L?pez wrote:
> Hi,
> 
> El 03/08/14 a las 12:55, Chen-Yu Tsai escibi?:
> >On Sun, Aug 3, 2014 at 8:50 PM, Maxime Ripard
> ><maxime.ripard@free-electrons.com> wrote:
> >>On Thu, Jul 31, 2014 at 05:46:09PM -0400, jonsmirl at gmail.com wrote:
> >>>Would it be better to name this "allwinner,sun4i-a10-pll2-clk" instead
> >>>of "allwinner,sun4i-a10-b-pll2-clk"? By encoding the b in it everyone
> >>>is going to wonder what to do on the 'c' revision which is the most
> >>>common revision.
> >>
> >>Not really, the way we works usually is that the compatible is the one
> >>from the first SoC that implemented that IP. If the rev C has the same
> >>IP than rev B, then we're using the rev B compatible.
> >>
> >>>
> >>>The revision based rename would then be from
> >>>"allwinner,sun4i-a10-pll2-clk" to "allwinner,sun4i-a10-a-pll2-clk".
> >>
> >>Though, I'd agree with you. We should have a single compatible in the
> >>DT, a generic one, that would trigger the auto-detection, and might
> >>change it to the rev A one, but the rev B doesn't make much sense.
> >
> >I agree. The rev B would make people reading the DT wonder what happened
> >to rev A.
> 
> Would you like to see "allwinner,sun4i-a10-pll2-clk" on the DT then?
> Should we document it as a magic property triggering an autodetect
> on the clock binding document?

Yes, and the rev-a one as really only to be used with a rev A SoC,
with a statement saying that we strongly discourage it, I guess.

Maxime

-- 
Maxime Ripard, Free Electrons
Embedded Linux, Kernel and Android engineering
http://free-electrons.com
-------------- next part --------------
A non-text attachment was scrubbed...
Name: signature.asc
Type: application/pgp-signature
Size: 819 bytes
Desc: Digital signature
URL: <http://lists.infradead.org/pipermail/linux-arm-kernel/attachments/20140804/57c4e425/attachment.sig>

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

* [PATCH 4/9] clk: sunxi: PLL2 support for sun4i, sun5i and sun7i
  2014-08-03 22:02     ` Emilio López
@ 2014-08-04 20:02       ` Maxime Ripard
  2014-08-04 20:23         ` Emilio López
  0 siblings, 1 reply; 36+ messages in thread
From: Maxime Ripard @ 2014-08-04 20:02 UTC (permalink / raw)
  To: linux-arm-kernel

On Sun, Aug 03, 2014 at 07:02:41PM -0300, Emilio L?pez wrote:
> Hi,
> 
> El 03/08/14 a las 09:44, Maxime Ripard escibi?:
> >On Thu, Jul 31, 2014 at 06:28:07PM -0300, Emilio L?pez wrote:
> >>This patch adds support for PLL2 and derivates on sun4i, sun5i and
> >>sun7i SoCs. As this PLL is only used for audio and requires good
> >>accuracy, we only support two known good rates.
> >>
> >>Signed-off-by: Emilio L?pez <emilio@elopez.com.ar>
> >>---
> >>
> (...)
> >>+	/* PLL2x8, double of PLL2 without the post divisor */
> >>+	of_property_read_string_index(np, "clock-output-names", 3, &clk_name);
> >>+	clks[3] = clk_register_fixed_factor(NULL, clk_name, parent,
> >>+					    CLK_SET_RATE_PARENT, 2 * 4, 1);
> >
> >Why have you declared them here, instead of using fixed factors in the
> >DT directly, like we have done in the past?
> 
> I'd prefer to see one clock with four outputs represented as one
> clock with four outputs rather than one clock with one output and
> other three weirdly named nodes floating around.

Except that you have no control over these 3 other outputs, that
directly derive from the only one you can control.

Plus, their name wouldn't be weirded than the one you gave in
clock-output-names ;)

> I can only see four uses of fixed-factor-clock in our DTSI, three of
> which are "pass through" clocks for ahb and ar100 on sun6/8i, and a
> fourth one to implement the 32k that results from reducing the 24M
> osc on sun7i.

And PLLx2, PLL2x4, PLL2x8 are really just pass-through clocks
too. Once PLL2 is controlled, those will just be enabled.

> As a second reason, implementing it as fixed factors would be
> slightly incorrect, as it's only "x8" when you use the limited set
> of factors that Allwinner and us are using for audio.

Hu? What do you mean by that?

> If we were ever to support the full range of the PLL. the DT binding
> would suddenly be off. By using a single binding, the meaning is
> self-contained in the driver.
> 
> As a third reason, of_fixed_factor_clk_setup() does not set
> CLK_SET_RATE_PARENT, which we'd need to propagate the rate change
> upstream.

That would be easy to fix.

Maxime

-- 
Maxime Ripard, Free Electrons
Embedded Linux, Kernel and Android engineering
http://free-electrons.com
-------------- next part --------------
A non-text attachment was scrubbed...
Name: signature.asc
Type: application/pgp-signature
Size: 819 bytes
Desc: Digital signature
URL: <http://lists.infradead.org/pipermail/linux-arm-kernel/attachments/20140804/8661cf34/attachment.sig>

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

* [PATCH 4/9] clk: sunxi: PLL2 support for sun4i, sun5i and sun7i
  2014-08-04 20:02       ` Maxime Ripard
@ 2014-08-04 20:23         ` Emilio López
  2014-08-07 11:23           ` Maxime Ripard
  0 siblings, 1 reply; 36+ messages in thread
From: Emilio López @ 2014-08-04 20:23 UTC (permalink / raw)
  To: linux-arm-kernel

El 04/08/14 a las 17:02, Maxime Ripard escibi?:
> On Sun, Aug 03, 2014 at 07:02:41PM -0300, Emilio L?pez wrote:
>> Hi,
>>
>> El 03/08/14 a las 09:44, Maxime Ripard escibi?:
>>> On Thu, Jul 31, 2014 at 06:28:07PM -0300, Emilio L?pez wrote:
>>>> This patch adds support for PLL2 and derivates on sun4i, sun5i and
>>>> sun7i SoCs. As this PLL is only used for audio and requires good
>>>> accuracy, we only support two known good rates.
>>>>
>>>> Signed-off-by: Emilio L?pez <emilio@elopez.com.ar>
>>>> ---
>>>>
>> (...)
>>>> +	/* PLL2x8, double of PLL2 without the post divisor */
>>>> +	of_property_read_string_index(np, "clock-output-names", 3, &clk_name);
>>>> +	clks[3] = clk_register_fixed_factor(NULL, clk_name, parent,
>>>> +					    CLK_SET_RATE_PARENT, 2 * 4, 1);
>>>
>>> Why have you declared them here, instead of using fixed factors in the
>>> DT directly, like we have done in the past?
>>
>> I'd prefer to see one clock with four outputs represented as one
>> clock with four outputs rather than one clock with one output and
>> other three weirdly named nodes floating around.
>
> Except that you have no control over these 3 other outputs, that
> directly derive from the only one you can control.

You do have one degree of control, see below.

> Plus, their name wouldn't be weirded than the one you gave in
> clock-output-names ;)

Allwinner chose those names on the user manual, not me :)

>> I can only see four uses of fixed-factor-clock in our DTSI, three of
>> which are "pass through" clocks for ahb and ar100 on sun6/8i, and a
>> fourth one to implement the 32k that results from reducing the 24M
>> osc on sun7i.
>
> And PLLx2, PLL2x4, PLL2x8 are really just pass-through clocks
> too. Once PLL2 is controlled, those will just be enabled.

Yes and no.

>> As a second reason, implementing it as fixed factors would be
>> slightly incorrect, as it's only "x8" when you use the limited set
>> of factors that Allwinner and us are using for audio.
>
> Hu? What do you mean by that?

PLL2 (aka PLL2x1) is 24 * N / pre / post
PLL2x8 is 24 * 2 * N / pre

So PLL2 * 8 == PLL2x8 only if post == 4, which holds true on our 
software implementation as well as AW's, but may not always hold true 
hardware wise.

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

* [PATCH 4/9] clk: sunxi: PLL2 support for sun4i, sun5i and sun7i
  2014-07-31 21:28 ` [PATCH 4/9] clk: sunxi: PLL2 support for sun4i, sun5i and sun7i Emilio López
  2014-08-03 12:44   ` Maxime Ripard
@ 2014-08-06 13:51   ` jonsmirl at gmail.com
  2014-08-06 15:20   ` jonsmirl at gmail.com
  2 siblings, 0 replies; 36+ messages in thread
From: jonsmirl at gmail.com @ 2014-08-06 13:51 UTC (permalink / raw)
  To: linux-arm-kernel

On Thu, Jul 31, 2014 at 5:28 PM, Emilio L?pez <emilio@elopez.com.ar> wrote:
> +CLK_OF_DECLARE(sun4i_pll2_a, "allwinner,sun4i-a10-a-pll2-clk", sun4i_pll2_setup);
> +CLK_OF_DECLARE(sun4i_pll2_b, "allwinner,sun4i-a10-b-pll2-clk", sun4i_pll2_setup);


This second one should not encode the 'b'.
CLK_OF_DECLARE(sun4i_pll2_b, "allwinner,sun4i-a10-pll2-clk", sun4i_pll2_setup);

The generic DTDs should only contain compatibles that don't have a
chip revision in them.

Then there are two choices:
1) Identify your chip revision and load a DTD with the specific
revisions in the compatible strings.
2) Rely on the OS to identify the revision and modify the compatible at boot.

The strings in the generic DTD should never contain a specific
revision simply because it is unknown in a generic DTD.

> --
> 2.0.3



-- 
Jon Smirl
jonsmirl at gmail.com

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

* [PATCH 4/9] clk: sunxi: PLL2 support for sun4i, sun5i and sun7i
  2014-07-31 21:28 ` [PATCH 4/9] clk: sunxi: PLL2 support for sun4i, sun5i and sun7i Emilio López
  2014-08-03 12:44   ` Maxime Ripard
  2014-08-06 13:51   ` jonsmirl at gmail.com
@ 2014-08-06 15:20   ` jonsmirl at gmail.com
  2014-08-08  0:03     ` jonsmirl at gmail.com
  2 siblings, 1 reply; 36+ messages in thread
From: jonsmirl at gmail.com @ 2014-08-06 15:20 UTC (permalink / raw)
  To: linux-arm-kernel

On Thu, Jul 31, 2014 at 5:28 PM, Emilio L?pez <emilio@elopez.com.ar> wrote:
> This patch adds support for PLL2 and derivates on sun4i, sun5i and
> sun7i SoCs. As this PLL is only used for audio and requires good
> accuracy, we only support two known good rates.
>
> Signed-off-by: Emilio L?pez <emilio@elopez.com.ar>
> ---
>
> Changes from RFC:
>  * Add support for A10 rev. A
>  * Document compatibles
>  * Use fixed factors
>
>  Documentation/devicetree/bindings/clock/sunxi.txt |   2 +
>  drivers/clk/sunxi/Makefile                        |   1 +
>  drivers/clk/sunxi/clk-a10-pll2.c                  | 243 ++++++++++++++++++++++
>  3 files changed, 246 insertions(+)
>  create mode 100644 drivers/clk/sunxi/clk-a10-pll2.c
>
> diff --git a/Documentation/devicetree/bindings/clock/sunxi.txt b/Documentation/devicetree/bindings/clock/sunxi.txt
> index d3a5c3c..41ada31 100644
> --- a/Documentation/devicetree/bindings/clock/sunxi.txt
> +++ b/Documentation/devicetree/bindings/clock/sunxi.txt
> @@ -10,6 +10,8 @@ Required properties:
>         "allwinner,sun4i-a10-pll1-clk" - for the main PLL clock and PLL4
>         "allwinner,sun6i-a31-pll1-clk" - for the main PLL clock on A31
>         "allwinner,sun8i-a23-pll1-clk" - for the main PLL clock on A23
> +       "allwinner,sun4i-a10-a-pll2-clk" - for the PLL2 clock on A10 rev. A
> +       "allwinner,sun4i-a10-b-pll2-clk" - for the PLL2 clock on A10 rev. B
>         "allwinner,sun4i-a10-pll5-clk" - for the PLL5 clock
>         "allwinner,sun4i-a10-pll6-clk" - for the PLL6 clock
>         "allwinner,sun6i-a31-pll6-clk" - for the PLL6 clock on A31
> diff --git a/drivers/clk/sunxi/Makefile b/drivers/clk/sunxi/Makefile
> index 6850cba..dcd5709 100644
> --- a/drivers/clk/sunxi/Makefile
> +++ b/drivers/clk/sunxi/Makefile
> @@ -4,6 +4,7 @@
>
>  obj-y += clk-sunxi.o clk-factors.o
>  obj-y += clk-a10-hosc.o
> +obj-y += clk-a10-pll2.o
>  obj-y += clk-a20-gmac.o
>
>  obj-$(CONFIG_MFD_SUN6I_PRCM) += \
> diff --git a/drivers/clk/sunxi/clk-a10-pll2.c b/drivers/clk/sunxi/clk-a10-pll2.c
> new file mode 100644
> index 0000000..bcf7d0b
> --- /dev/null
> +++ b/drivers/clk/sunxi/clk-a10-pll2.c
> @@ -0,0 +1,243 @@
> +/*
> + * Copyright 2014 Emilio L?pez
> + *
> + * Emilio L?pez <emilio@elopez.com.ar>
> + *
> + * 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.
> + */
> +
> +#include <linux/clk-provider.h>
> +#include <linux/of.h>
> +#include <linux/of_address.h>
> +#include <linux/slab.h>
> +
> +#define SUN4I_PLL2_ENABLE              31
> +#define SUN4I_PLL2_A_VCOBIAS           0
> +#define SUN4I_PLL2_A_VCOBIAS_MASK      0x1F
> +#define SUN4I_PLL2_A_N                 7
> +#define SUN4I_PLL2_A_N_MASK            0x7F
> +#define SUN4I_PLL2_B_POST_DIV          26
> +#define SUN4I_PLL2_B_POST_DIV_MASK     0xF
> +#define SUN4I_PLL2_B_N                 8
> +#define SUN4I_PLL2_B_N_MASK            0x7F
> +#define SUN4I_PLL2_B_PRE_DIV           0
> +#define SUN4I_PLL2_B_PRE_DIV_MASK      0x1F
> +
> +#define SUN4I_PLL2_OUTPUTS             4
> +
> +struct sun4i_pll2_clk {
> +       struct clk_hw hw;
> +       void __iomem *reg;
> +};
> +
> +static inline struct sun4i_pll2_clk *to_sun4i_pll2_clk(struct clk_hw *hw)
> +{
> +       return container_of(hw, struct sun4i_pll2_clk, hw);
> +}
> +
> +static unsigned long sun4i_pll2_recalc_rate_a(struct clk_hw *hw,
> +                                             unsigned long parent_rate)
> +{
> +       struct sun4i_pll2_clk *clk = to_sun4i_pll2_clk(hw);
> +       int vcobias, n;
> +       u32 val;
> +
> +       val = readl(clk->reg);
> +       vcobias = (val >> SUN4I_PLL2_A_VCOBIAS) & SUN4I_PLL2_A_VCOBIAS_MASK;
> +       n = (val >> SUN4I_PLL2_A_N) & SUN4I_PLL2_A_N_MASK;
> +
> +       if (vcobias == 10 && n == 94)
> +               return 22579200;
> +       else if (vcobias == 9 && n == 83)
> +               return 24576000;
> +
> +       /*
> +        * Unfortunately we don't really have much documentation on how
> +        * these factors relate mathematically
> +        */
> +       return 0;
> +}
> +
> +static unsigned long sun4i_pll2_recalc_rate_b(struct clk_hw *hw,
> +                                             unsigned long parent_rate)
> +{
> +       struct sun4i_pll2_clk *clk = to_sun4i_pll2_clk(hw);
> +       int n, prediv, postdiv;
> +       u32 val;
> +
> +       val = readl(clk->reg);
> +       n = (val >> SUN4I_PLL2_B_N) & SUN4I_PLL2_B_N_MASK;
> +       prediv = (val >> SUN4I_PLL2_B_PRE_DIV) & SUN4I_PLL2_B_PRE_DIV_MASK;
> +       postdiv = (val >> SUN4I_PLL2_B_POST_DIV) & SUN4I_PLL2_B_POST_DIV_MASK;
> +
> +       /* 0 is a special case and means 1 */
> +       if (n == 0)
> +               n = 1;
> +       if (prediv == 0)
> +               prediv = 1;
> +       if (postdiv == 0)
> +               postdiv = 1;
> +
> +       return ((parent_rate * n) / prediv) / postdiv;
> +}
> +
> +static long sun4i_pll2_round_rate(struct clk_hw *hw, unsigned long rate,
> +                                 unsigned long *parent_rate)
> +{
> +       /*
> +        * There is only two interesting rates for the audio PLL, the
> +        * rest isn't really usable due to accuracy concerns. Therefore,
> +        * we specifically round to those rates here
> +        */
> +       if (rate < 22579200)
> +               return -EINVAL;
> +
> +       if (rate >= 22579200 && rate < 24576000)
> +               return 22579200;
> +
> +       return 24576000;
> +}

These should return the actual computed rate osc*n / (pre * post) so
that the error column is displayed correctly.

   pll2                                  1            1    22571428          0
       i2s0                               1            1    22571428          0
          mclk0                           0            0    22571428          0

At 22.5 the error is 0.03%, 24.5 error is 0.01%.

So when you ask for 24.576 it returns 24.571428 and 22.5792 returns 22.571428.




> +
> +static int sun4i_pll2_set_rate_a(struct clk_hw *hw, unsigned long rate,
> +                                unsigned long parent_rate)
> +{
> +       struct sun4i_pll2_clk *clk = to_sun4i_pll2_clk(hw);
> +       u32 val = readl(clk->reg);
> +
> +       val &= ~(SUN4I_PLL2_A_VCOBIAS_MASK << SUN4I_PLL2_A_VCOBIAS);
> +       val &= ~(SUN4I_PLL2_A_N_MASK << SUN4I_PLL2_A_N);
> +
> +       if (rate == 22579200)
> +               val |= (10 << SUN4I_PLL2_A_VCOBIAS) | (94 << SUN4I_PLL2_A_N);
> +       else if (rate == 24576000)
> +               val |= (9 << SUN4I_PLL2_A_VCOBIAS) | (83 << SUN4I_PLL2_A_N);
> +       else
> +               return -EINVAL;
> +
> +       writel(val, clk->reg);
> +
> +       return 0;
> +}
> +
> +static int sun4i_pll2_set_rate_b(struct clk_hw *hw, unsigned long rate,
> +                                unsigned long parent_rate)
> +{
> +       struct sun4i_pll2_clk *clk = to_sun4i_pll2_clk(hw);
> +       u32 val = readl(clk->reg);
> +
> +       val &= ~(SUN4I_PLL2_B_N_MASK << SUN4I_PLL2_B_N);
> +       val &= ~(SUN4I_PLL2_B_PRE_DIV_MASK << SUN4I_PLL2_B_PRE_DIV);
> +       val &= ~(SUN4I_PLL2_B_POST_DIV_MASK << SUN4I_PLL2_B_POST_DIV);
> +
> +       val |= (21 << SUN4I_PLL2_B_PRE_DIV) | (4 << SUN4I_PLL2_B_POST_DIV);
> +
> +       if (rate == 22579200)
> +               val |= (79 << SUN4I_PLL2_B_N);
> +       else if (rate == 24576000)
> +               val |= (86 << SUN4I_PLL2_B_N);
> +       else
> +               return -EINVAL;
> +
> +       writel(val, clk->reg);
> +
> +       return 0;
> +}
> +
> +static const struct clk_ops sun4i_pll2_ops_a = {
> +       .recalc_rate = sun4i_pll2_recalc_rate_a,
> +       .round_rate = sun4i_pll2_round_rate,
> +       .set_rate = sun4i_pll2_set_rate_a,
> +};
> +
> +
> +static const struct clk_ops sun4i_pll2_ops_b = {
> +       .recalc_rate = sun4i_pll2_recalc_rate_b,
> +       .round_rate = sun4i_pll2_round_rate,
> +       .set_rate = sun4i_pll2_set_rate_b,
> +};
> +
> +static const struct of_device_id pll2_matches[] __initconst = {
> +       { .compatible = "allwinner,sun4i-a10-a-pll2-clk", .data = &sun4i_pll2_ops_a },
> +       { .compatible = "allwinner,sun4i-a10-b-pll2-clk", .data = &sun4i_pll2_ops_b },
> +       { /* sentinel */ },
> +};
> +
> +static void __init sun4i_pll2_setup(struct device_node *np)
> +{
> +       const char *clk_name = np->name, *parent;
> +       const struct of_device_id *match;
> +       struct clk_onecell_data *clk_data;
> +       const struct clk_ops *pll2_ops;
> +       struct sun4i_pll2_clk *pll2;
> +       struct clk_gate *gate;
> +       struct clk **clks;
> +       void __iomem *reg;
> +
> +       /* Choose the correct ops for pll2 */
> +       match = of_match_node(pll2_matches, np);
> +       pll2_ops = match->data;
> +
> +       pll2 = kzalloc(sizeof(*pll2), GFP_KERNEL);
> +       gate = kzalloc(sizeof(*gate), GFP_KERNEL);
> +       clk_data = kzalloc(sizeof(*clk_data), GFP_KERNEL);
> +       clks = kcalloc(SUN4I_PLL2_OUTPUTS, sizeof(*clks), GFP_KERNEL);
> +       if (!pll2 || !gate || !clk_data || !clks)
> +               goto free_mem;
> +
> +       reg = of_iomap(np, 0);
> +       parent = of_clk_get_parent_name(np, 0);
> +       of_property_read_string_index(np, "clock-output-names", 0, &clk_name);
> +
> +       pll2->reg = reg;
> +       gate->reg = reg;
> +       gate->bit_idx = SUN4I_PLL2_ENABLE;
> +
> +       /* PLL2, also known as PLL2x1 */
> +       of_property_read_string_index(np, "clock-output-names", 0, &clk_name);
> +       clks[0] = clk_register_composite(NULL, clk_name, &parent, 1, NULL, NULL,
> +                                        &pll2->hw, pll2_ops,
> +                                        &gate->hw, &clk_gate_ops, 0);
> +       WARN_ON(IS_ERR(clks[0]));
> +       clk_set_rate(clks[0], 22579200);
> +       parent = clk_name;
> +
> +       /* PLL2x2, 1/4 the rate of PLL2x8 */
> +       of_property_read_string_index(np, "clock-output-names", 1, &clk_name);
> +       clks[1] = clk_register_fixed_factor(NULL, clk_name, parent,
> +                                           CLK_SET_RATE_PARENT, 2, 1);
> +       WARN_ON(IS_ERR(clks[1]));
> +
> +       /* PLL2x4, 1/2 the rate of PLL2x8 */
> +       of_property_read_string_index(np, "clock-output-names", 2, &clk_name);
> +       clks[2] = clk_register_fixed_factor(NULL, clk_name, parent,
> +                                           CLK_SET_RATE_PARENT, 4, 1);
> +       WARN_ON(IS_ERR(clks[2]));
> +
> +       /* PLL2x8, double of PLL2 without the post divisor */
> +       of_property_read_string_index(np, "clock-output-names", 3, &clk_name);
> +       clks[3] = clk_register_fixed_factor(NULL, clk_name, parent,
> +                                           CLK_SET_RATE_PARENT, 2 * 4, 1);
> +       WARN_ON(IS_ERR(clks[3]));
> +
> +       clk_data->clks = clks;
> +       clk_data->clk_num = SUN4I_PLL2_OUTPUTS;
> +       of_clk_add_provider(np, of_clk_src_onecell_get, clk_data);
> +
> +       return;
> +
> +free_mem:
> +       kfree(pll2);
> +       kfree(gate);
> +       kfree(clk_data);
> +       kfree(clks);
> +}
> +CLK_OF_DECLARE(sun4i_pll2_a, "allwinner,sun4i-a10-a-pll2-clk", sun4i_pll2_setup);
> +CLK_OF_DECLARE(sun4i_pll2_b, "allwinner,sun4i-a10-b-pll2-clk", sun4i_pll2_setup);
> --
> 2.0.3



-- 
Jon Smirl
jonsmirl at gmail.com

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

* [PATCH 4/9] clk: sunxi: PLL2 support for sun4i, sun5i and sun7i
  2014-08-04 20:23         ` Emilio López
@ 2014-08-07 11:23           ` Maxime Ripard
  0 siblings, 0 replies; 36+ messages in thread
From: Maxime Ripard @ 2014-08-07 11:23 UTC (permalink / raw)
  To: linux-arm-kernel

On Mon, Aug 04, 2014 at 05:23:44PM -0300, Emilio L?pez wrote:
> El 04/08/14 a las 17:02, Maxime Ripard escibi?:
> >On Sun, Aug 03, 2014 at 07:02:41PM -0300, Emilio L?pez wrote:
> >>Hi,
> >>
> >>El 03/08/14 a las 09:44, Maxime Ripard escibi?:
> >>>On Thu, Jul 31, 2014 at 06:28:07PM -0300, Emilio L?pez wrote:
> >>>>This patch adds support for PLL2 and derivates on sun4i, sun5i and
> >>>>sun7i SoCs. As this PLL is only used for audio and requires good
> >>>>accuracy, we only support two known good rates.
> >>>>
> >>>>Signed-off-by: Emilio L?pez <emilio@elopez.com.ar>
> >>>>---
> >>>>
> >>(...)
> >>>>+	/* PLL2x8, double of PLL2 without the post divisor */
> >>>>+	of_property_read_string_index(np, "clock-output-names", 3, &clk_name);
> >>>>+	clks[3] = clk_register_fixed_factor(NULL, clk_name, parent,
> >>>>+					    CLK_SET_RATE_PARENT, 2 * 4, 1);
> >>>
> >>>Why have you declared them here, instead of using fixed factors in the
> >>>DT directly, like we have done in the past?
> >>
> >>I'd prefer to see one clock with four outputs represented as one
> >>clock with four outputs rather than one clock with one output and
> >>other three weirdly named nodes floating around.
> >
> >Except that you have no control over these 3 other outputs, that
> >directly derive from the only one you can control.
> 
> You do have one degree of control, see below.
> 
> >Plus, their name wouldn't be weirded than the one you gave in
> >clock-output-names ;)
> 
> Allwinner chose those names on the user manual, not me :)
> 
> >>I can only see four uses of fixed-factor-clock in our DTSI, three of
> >>which are "pass through" clocks for ahb and ar100 on sun6/8i, and a
> >>fourth one to implement the 32k that results from reducing the 24M
> >>osc on sun7i.
> >
> >And PLLx2, PLL2x4, PLL2x8 are really just pass-through clocks
> >too. Once PLL2 is controlled, those will just be enabled.
> 
> Yes and no.
> 
> >>As a second reason, implementing it as fixed factors would be
> >>slightly incorrect, as it's only "x8" when you use the limited set
> >>of factors that Allwinner and us are using for audio.
> >
> >Hu? What do you mean by that?
> 
> PLL2 (aka PLL2x1) is 24 * N / pre / post
> PLL2x8 is 24 * 2 * N / pre
> 
> So PLL2 * 8 == PLL2x8 only if post == 4, which holds true on our
> software implementation as well as AW's, but may not always hold
> true hardware wise.

Ah, right. I was looking at the wrong datasheet for this, my bad.

Then, at least document the bindings properly :)

Maxime

-- 
Maxime Ripard, Free Electrons
Embedded Linux, Kernel and Android engineering
http://free-electrons.com
-------------- next part --------------
A non-text attachment was scrubbed...
Name: signature.asc
Type: application/pgp-signature
Size: 819 bytes
Desc: Digital signature
URL: <http://lists.infradead.org/pipermail/linux-arm-kernel/attachments/20140807/f4332e60/attachment.sig>

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

* [PATCH 4/9] clk: sunxi: PLL2 support for sun4i, sun5i and sun7i
  2014-08-06 15:20   ` jonsmirl at gmail.com
@ 2014-08-08  0:03     ` jonsmirl at gmail.com
  0 siblings, 0 replies; 36+ messages in thread
From: jonsmirl at gmail.com @ 2014-08-08  0:03 UTC (permalink / raw)
  To: linux-arm-kernel

On Wed, Aug 6, 2014 at 11:20 AM, jonsmirl at gmail.com <jonsmirl@gmail.com> wrote:
> On Thu, Jul 31, 2014 at 5:28 PM, Emilio L?pez <emilio@elopez.com.ar> wrote:
>> This patch adds support for PLL2 and derivates on sun4i, sun5i and
>> sun7i SoCs. As this PLL is only used for audio and requires good
>> accuracy, we only support two known good rates.
>>
>> Signed-off-by: Emilio L?pez <emilio@elopez.com.ar>
>> ---
>>
>> Changes from RFC:
>>  * Add support for A10 rev. A
>>  * Document compatibles
>>  * Use fixed factors
>>
>>  Documentation/devicetree/bindings/clock/sunxi.txt |   2 +
>>  drivers/clk/sunxi/Makefile                        |   1 +
>>  drivers/clk/sunxi/clk-a10-pll2.c                  | 243 ++++++++++++++++++++++
>>  3 files changed, 246 insertions(+)
>>  create mode 100644 drivers/clk/sunxi/clk-a10-pll2.c
>>
>> diff --git a/Documentation/devicetree/bindings/clock/sunxi.txt b/Documentation/devicetree/bindings/clock/sunxi.txt
>> index d3a5c3c..41ada31 100644
>> --- a/Documentation/devicetree/bindings/clock/sunxi.txt
>> +++ b/Documentation/devicetree/bindings/clock/sunxi.txt
>> @@ -10,6 +10,8 @@ Required properties:
>>         "allwinner,sun4i-a10-pll1-clk" - for the main PLL clock and PLL4
>>         "allwinner,sun6i-a31-pll1-clk" - for the main PLL clock on A31
>>         "allwinner,sun8i-a23-pll1-clk" - for the main PLL clock on A23
>> +       "allwinner,sun4i-a10-a-pll2-clk" - for the PLL2 clock on A10 rev. A
>> +       "allwinner,sun4i-a10-b-pll2-clk" - for the PLL2 clock on A10 rev. B
>>         "allwinner,sun4i-a10-pll5-clk" - for the PLL5 clock
>>         "allwinner,sun4i-a10-pll6-clk" - for the PLL6 clock
>>         "allwinner,sun6i-a31-pll6-clk" - for the PLL6 clock on A31
>> diff --git a/drivers/clk/sunxi/Makefile b/drivers/clk/sunxi/Makefile
>> index 6850cba..dcd5709 100644
>> --- a/drivers/clk/sunxi/Makefile
>> +++ b/drivers/clk/sunxi/Makefile
>> @@ -4,6 +4,7 @@
>>
>>  obj-y += clk-sunxi.o clk-factors.o
>>  obj-y += clk-a10-hosc.o
>> +obj-y += clk-a10-pll2.o
>>  obj-y += clk-a20-gmac.o
>>
>>  obj-$(CONFIG_MFD_SUN6I_PRCM) += \
>> diff --git a/drivers/clk/sunxi/clk-a10-pll2.c b/drivers/clk/sunxi/clk-a10-pll2.c
>> new file mode 100644
>> index 0000000..bcf7d0b
>> --- /dev/null
>> +++ b/drivers/clk/sunxi/clk-a10-pll2.c
>> @@ -0,0 +1,243 @@
>> +/*
>> + * Copyright 2014 Emilio L?pez
>> + *
>> + * Emilio L?pez <emilio@elopez.com.ar>
>> + *
>> + * 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.
>> + */
>> +
>> +#include <linux/clk-provider.h>
>> +#include <linux/of.h>
>> +#include <linux/of_address.h>
>> +#include <linux/slab.h>
>> +
>> +#define SUN4I_PLL2_ENABLE              31
>> +#define SUN4I_PLL2_A_VCOBIAS           0
>> +#define SUN4I_PLL2_A_VCOBIAS_MASK      0x1F
>> +#define SUN4I_PLL2_A_N                 7
>> +#define SUN4I_PLL2_A_N_MASK            0x7F
>> +#define SUN4I_PLL2_B_POST_DIV          26
>> +#define SUN4I_PLL2_B_POST_DIV_MASK     0xF
>> +#define SUN4I_PLL2_B_N                 8
>> +#define SUN4I_PLL2_B_N_MASK            0x7F
>> +#define SUN4I_PLL2_B_PRE_DIV           0
>> +#define SUN4I_PLL2_B_PRE_DIV_MASK      0x1F
>> +
>> +#define SUN4I_PLL2_OUTPUTS             4
>> +
>> +struct sun4i_pll2_clk {
>> +       struct clk_hw hw;
>> +       void __iomem *reg;
>> +};
>> +
>> +static inline struct sun4i_pll2_clk *to_sun4i_pll2_clk(struct clk_hw *hw)
>> +{
>> +       return container_of(hw, struct sun4i_pll2_clk, hw);
>> +}
>> +
>> +static unsigned long sun4i_pll2_recalc_rate_a(struct clk_hw *hw,
>> +                                             unsigned long parent_rate)
>> +{
>> +       struct sun4i_pll2_clk *clk = to_sun4i_pll2_clk(hw);
>> +       int vcobias, n;
>> +       u32 val;
>> +
>> +       val = readl(clk->reg);
>> +       vcobias = (val >> SUN4I_PLL2_A_VCOBIAS) & SUN4I_PLL2_A_VCOBIAS_MASK;
>> +       n = (val >> SUN4I_PLL2_A_N) & SUN4I_PLL2_A_N_MASK;
>> +
>> +       if (vcobias == 10 && n == 94)
>> +               return 22579200;
>> +       else if (vcobias == 9 && n == 83)
>> +               return 24576000;
>> +
>> +       /*
>> +        * Unfortunately we don't really have much documentation on how
>> +        * these factors relate mathematically
>> +        */
>> +       return 0;
>> +}
>> +
>> +static unsigned long sun4i_pll2_recalc_rate_b(struct clk_hw *hw,
>> +                                             unsigned long parent_rate)
>> +{
>> +       struct sun4i_pll2_clk *clk = to_sun4i_pll2_clk(hw);
>> +       int n, prediv, postdiv;
>> +       u32 val;
>> +
>> +       val = readl(clk->reg);
>> +       n = (val >> SUN4I_PLL2_B_N) & SUN4I_PLL2_B_N_MASK;
>> +       prediv = (val >> SUN4I_PLL2_B_PRE_DIV) & SUN4I_PLL2_B_PRE_DIV_MASK;
>> +       postdiv = (val >> SUN4I_PLL2_B_POST_DIV) & SUN4I_PLL2_B_POST_DIV_MASK;
>> +
>> +       /* 0 is a special case and means 1 */
>> +       if (n == 0)
>> +               n = 1;
>> +       if (prediv == 0)
>> +               prediv = 1;
>> +       if (postdiv == 0)
>> +               postdiv = 1;
>> +
>> +       return ((parent_rate * n) / prediv) / postdiv;
>> +}
>> +
>> +static long sun4i_pll2_round_rate(struct clk_hw *hw, unsigned long rate,
>> +                                 unsigned long *parent_rate)
>> +{
>> +       /*
>> +        * There is only two interesting rates for the audio PLL, the
>> +        * rest isn't really usable due to accuracy concerns. Therefore,
>> +        * we specifically round to those rates here
>> +        */
>> +       if (rate < 22579200)
>> +               return -EINVAL;
>> +
>> +       if (rate >= 22579200 && rate < 24576000)
>> +               return 22579200;
>> +
>> +       return 24576000;
>> +}
>
> These should return the actual computed rate osc*n / (pre * post) so
> that the error column is displayed correctly.
>
>    pll2                                  1            1    22571428          0
>        i2s0                               1            1    22571428          0
>           mclk0                           0            0    22571428          0
>
> At 22.5 the error is 0.03%, 24.5 error is 0.01%.
>
> So when you ask for 24.576 it returns 24.571428 and 22.5792 returns 22.571428.
>
>

This is more complicated that it looks. If you set 22.5792Mhz and the
clock doesn't return  22.5792Mhz it throws off the math inside of
ALSA. For example 22579200 / 44100 = 512fs. But if you use  22571428 /
44100 = 511.8 = 511 because of truncation. Now the checks looking for
512 don't trigger. I just hit this bug.

So I think the clock is doing the right thing when you set 22.5792Mhz
and it returns the actual number 22.571428Mhz.

It's the ALSA code that needs to be more tolerant and use some round()
functions to allow for a slight error in these frequencies.


>
>
>> +
>> +static int sun4i_pll2_set_rate_a(struct clk_hw *hw, unsigned long rate,
>> +                                unsigned long parent_rate)
>> +{
>> +       struct sun4i_pll2_clk *clk = to_sun4i_pll2_clk(hw);
>> +       u32 val = readl(clk->reg);
>> +
>> +       val &= ~(SUN4I_PLL2_A_VCOBIAS_MASK << SUN4I_PLL2_A_VCOBIAS);
>> +       val &= ~(SUN4I_PLL2_A_N_MASK << SUN4I_PLL2_A_N);
>> +
>> +       if (rate == 22579200)
>> +               val |= (10 << SUN4I_PLL2_A_VCOBIAS) | (94 << SUN4I_PLL2_A_N);
>> +       else if (rate == 24576000)
>> +               val |= (9 << SUN4I_PLL2_A_VCOBIAS) | (83 << SUN4I_PLL2_A_N);
>> +       else
>> +               return -EINVAL;
>> +
>> +       writel(val, clk->reg);
>> +
>> +       return 0;
>> +}
>> +
>> +static int sun4i_pll2_set_rate_b(struct clk_hw *hw, unsigned long rate,
>> +                                unsigned long parent_rate)
>> +{
>> +       struct sun4i_pll2_clk *clk = to_sun4i_pll2_clk(hw);
>> +       u32 val = readl(clk->reg);
>> +
>> +       val &= ~(SUN4I_PLL2_B_N_MASK << SUN4I_PLL2_B_N);
>> +       val &= ~(SUN4I_PLL2_B_PRE_DIV_MASK << SUN4I_PLL2_B_PRE_DIV);
>> +       val &= ~(SUN4I_PLL2_B_POST_DIV_MASK << SUN4I_PLL2_B_POST_DIV);
>> +
>> +       val |= (21 << SUN4I_PLL2_B_PRE_DIV) | (4 << SUN4I_PLL2_B_POST_DIV);
>> +
>> +       if (rate == 22579200)
>> +               val |= (79 << SUN4I_PLL2_B_N);
>> +       else if (rate == 24576000)
>> +               val |= (86 << SUN4I_PLL2_B_N);
>> +       else
>> +               return -EINVAL;
>> +
>> +       writel(val, clk->reg);
>> +
>> +       return 0;
>> +}
>> +
>> +static const struct clk_ops sun4i_pll2_ops_a = {
>> +       .recalc_rate = sun4i_pll2_recalc_rate_a,
>> +       .round_rate = sun4i_pll2_round_rate,
>> +       .set_rate = sun4i_pll2_set_rate_a,
>> +};
>> +
>> +
>> +static const struct clk_ops sun4i_pll2_ops_b = {
>> +       .recalc_rate = sun4i_pll2_recalc_rate_b,
>> +       .round_rate = sun4i_pll2_round_rate,
>> +       .set_rate = sun4i_pll2_set_rate_b,
>> +};
>> +
>> +static const struct of_device_id pll2_matches[] __initconst = {
>> +       { .compatible = "allwinner,sun4i-a10-a-pll2-clk", .data = &sun4i_pll2_ops_a },
>> +       { .compatible = "allwinner,sun4i-a10-b-pll2-clk", .data = &sun4i_pll2_ops_b },
>> +       { /* sentinel */ },
>> +};
>> +
>> +static void __init sun4i_pll2_setup(struct device_node *np)
>> +{
>> +       const char *clk_name = np->name, *parent;
>> +       const struct of_device_id *match;
>> +       struct clk_onecell_data *clk_data;
>> +       const struct clk_ops *pll2_ops;
>> +       struct sun4i_pll2_clk *pll2;
>> +       struct clk_gate *gate;
>> +       struct clk **clks;
>> +       void __iomem *reg;
>> +
>> +       /* Choose the correct ops for pll2 */
>> +       match = of_match_node(pll2_matches, np);
>> +       pll2_ops = match->data;
>> +
>> +       pll2 = kzalloc(sizeof(*pll2), GFP_KERNEL);
>> +       gate = kzalloc(sizeof(*gate), GFP_KERNEL);
>> +       clk_data = kzalloc(sizeof(*clk_data), GFP_KERNEL);
>> +       clks = kcalloc(SUN4I_PLL2_OUTPUTS, sizeof(*clks), GFP_KERNEL);
>> +       if (!pll2 || !gate || !clk_data || !clks)
>> +               goto free_mem;
>> +
>> +       reg = of_iomap(np, 0);
>> +       parent = of_clk_get_parent_name(np, 0);
>> +       of_property_read_string_index(np, "clock-output-names", 0, &clk_name);
>> +
>> +       pll2->reg = reg;
>> +       gate->reg = reg;
>> +       gate->bit_idx = SUN4I_PLL2_ENABLE;
>> +
>> +       /* PLL2, also known as PLL2x1 */
>> +       of_property_read_string_index(np, "clock-output-names", 0, &clk_name);
>> +       clks[0] = clk_register_composite(NULL, clk_name, &parent, 1, NULL, NULL,
>> +                                        &pll2->hw, pll2_ops,
>> +                                        &gate->hw, &clk_gate_ops, 0);
>> +       WARN_ON(IS_ERR(clks[0]));
>> +       clk_set_rate(clks[0], 22579200);
>> +       parent = clk_name;
>> +
>> +       /* PLL2x2, 1/4 the rate of PLL2x8 */
>> +       of_property_read_string_index(np, "clock-output-names", 1, &clk_name);
>> +       clks[1] = clk_register_fixed_factor(NULL, clk_name, parent,
>> +                                           CLK_SET_RATE_PARENT, 2, 1);
>> +       WARN_ON(IS_ERR(clks[1]));
>> +
>> +       /* PLL2x4, 1/2 the rate of PLL2x8 */
>> +       of_property_read_string_index(np, "clock-output-names", 2, &clk_name);
>> +       clks[2] = clk_register_fixed_factor(NULL, clk_name, parent,
>> +                                           CLK_SET_RATE_PARENT, 4, 1);
>> +       WARN_ON(IS_ERR(clks[2]));
>> +
>> +       /* PLL2x8, double of PLL2 without the post divisor */
>> +       of_property_read_string_index(np, "clock-output-names", 3, &clk_name);
>> +       clks[3] = clk_register_fixed_factor(NULL, clk_name, parent,
>> +                                           CLK_SET_RATE_PARENT, 2 * 4, 1);
>> +       WARN_ON(IS_ERR(clks[3]));
>> +
>> +       clk_data->clks = clks;
>> +       clk_data->clk_num = SUN4I_PLL2_OUTPUTS;
>> +       of_clk_add_provider(np, of_clk_src_onecell_get, clk_data);
>> +
>> +       return;
>> +
>> +free_mem:
>> +       kfree(pll2);
>> +       kfree(gate);
>> +       kfree(clk_data);
>> +       kfree(clks);
>> +}
>> +CLK_OF_DECLARE(sun4i_pll2_a, "allwinner,sun4i-a10-a-pll2-clk", sun4i_pll2_setup);
>> +CLK_OF_DECLARE(sun4i_pll2_b, "allwinner,sun4i-a10-b-pll2-clk", sun4i_pll2_setup);
>> --
>> 2.0.3
>
>
>
> --
> Jon Smirl
> jonsmirl at gmail.com



-- 
Jon Smirl
jonsmirl at gmail.com

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

end of thread, other threads:[~2014-08-08  0:03 UTC | newest]

Thread overview: 36+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2014-07-31 21:28 [PATCH 0/9] Audio clocks for sun[457]i, SoC revision detection Emilio López
2014-07-31 21:28 ` [PATCH 1/9] ARM: sunxi: introduce SoC identification support Emilio López
2014-08-03 12:40   ` Maxime Ripard
2014-08-03 21:45     ` Emilio López
2014-08-04  8:08       ` Lee Jones
2014-08-04 19:48       ` Maxime Ripard
2014-07-31 21:28 ` [PATCH 2/9] ARM: sunxi: quirk support Emilio López
2014-08-03 12:42   ` Maxime Ripard
2014-08-03 21:37     ` Emilio López
2014-08-04 19:32       ` Maxime Ripard
2014-07-31 21:28 ` [PATCH 3/9] ARM: sunxi: make sun6i SMP ops static Emilio López
2014-08-03 12:41   ` Maxime Ripard
2014-07-31 21:28 ` [PATCH 4/9] clk: sunxi: PLL2 support for sun4i, sun5i and sun7i Emilio López
2014-08-03 12:44   ` Maxime Ripard
2014-08-03 15:58     ` Chen-Yu Tsai
2014-08-03 18:48       ` Maxime Ripard
2014-08-03 22:02     ` Emilio López
2014-08-04 20:02       ` Maxime Ripard
2014-08-04 20:23         ` Emilio López
2014-08-07 11:23           ` Maxime Ripard
2014-08-06 13:51   ` jonsmirl at gmail.com
2014-08-06 15:20   ` jonsmirl at gmail.com
2014-08-08  0:03     ` jonsmirl at gmail.com
2014-07-31 21:28 ` [PATCH 5/9] clk: sunxi: codec clock support Emilio López
2014-07-31 21:28 ` [PATCH 6/9] clk: sunxi: mod1 " Emilio López
2014-08-03 12:47   ` Maxime Ripard
2014-08-03 22:11     ` Emilio López
2014-08-04 19:52       ` Maxime Ripard
2014-07-31 21:28 ` [PATCH 7/9] ARM: sunxi: dt: Add PLL2 support Emilio López
2014-07-31 21:46   ` jonsmirl at gmail.com
2014-08-03 12:50     ` Maxime Ripard
2014-08-03 15:55       ` Chen-Yu Tsai
2014-08-03 22:15         ` Emilio López
2014-08-04 19:53           ` Maxime Ripard
2014-07-31 21:28 ` [PATCH 8/9] ARM: sunxi: dt: Add codec clock support Emilio López
2014-07-31 21:28 ` [PATCH 9/9] ARM: sun7i: dt: Add mod1 clock nodes Emilio López

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.