Linux-ARM-Kernel Archive on lore.kernel.org
 help / color / mirror / Atom feed
* [PATCHv3 07/41] OMAPDSS: add of helpers
From: Tomi Valkeinen @ 2014-01-21 10:56 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <1390301833-24944-1-git-send-email-tomi.valkeinen@ti.com>

Add helpers to get ports and endpoints from DT data.

Signed-off-by: Tomi Valkeinen <tomi.valkeinen@ti.com>
---
 drivers/video/omap2/dss/Makefile |   2 +-
 drivers/video/omap2/dss/dss-of.c | 159 +++++++++++++++++++++++++++++++++++++++
 include/video/omapdss.h          |  14 ++++
 3 files changed, 174 insertions(+), 1 deletion(-)
 create mode 100644 drivers/video/omap2/dss/dss-of.c

diff --git a/drivers/video/omap2/dss/Makefile b/drivers/video/omap2/dss/Makefile
index d3aa91bdd6a8..8aec8bda27cc 100644
--- a/drivers/video/omap2/dss/Makefile
+++ b/drivers/video/omap2/dss/Makefile
@@ -1,7 +1,7 @@
 obj-$(CONFIG_OMAP2_DSS) += omapdss.o
 # Core DSS files
 omapdss-y := core.o dss.o dss_features.o dispc.o dispc_coefs.o display.o \
-	output.o
+	output.o dss-of.o
 # DSS compat layer files
 omapdss-y += manager.o manager-sysfs.o overlay.o overlay-sysfs.o apply.o \
 	dispc-compat.o display-sysfs.o
diff --git a/drivers/video/omap2/dss/dss-of.c b/drivers/video/omap2/dss/dss-of.c
new file mode 100644
index 000000000000..7beb62fb367c
--- /dev/null
+++ b/drivers/video/omap2/dss/dss-of.c
@@ -0,0 +1,159 @@
+/*
+ * Copyright (C) 2013 Texas Instruments
+ * Author: Tomi Valkeinen <tomi.valkeinen@ti.com>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published by
+ * the Free Software Foundation.
+ *
+ * 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/device.h>
+#include <linux/err.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/seq_file.h>
+
+#include <video/omapdss.h>
+
+struct device_node *
+omapdss_of_get_next_port(const struct device_node *parent,
+			 struct device_node *prev)
+{
+	struct device_node *port = NULL;
+
+	if (!parent)
+		return NULL;
+
+	if (!prev) {
+		struct device_node *ports;
+		/*
+		 * It's the first call, we have to find a port subnode
+		 * within this node or within an optional 'ports' node.
+		 */
+		ports = of_get_child_by_name(parent, "ports");
+		if (ports)
+			parent = ports;
+
+		port = of_get_child_by_name(parent, "port");
+
+		/* release the 'ports' node */
+		of_node_put(ports);
+	} else {
+		struct device_node *ports;
+
+		ports = of_get_parent(prev);
+		if (!ports)
+			return NULL;
+
+		do {
+			port = of_get_next_child(ports, prev);
+			if (!port) {
+				of_node_put(ports);
+				return NULL;
+			}
+			prev = port;
+		} while (of_node_cmp(port->name, "port") != 0);
+	}
+
+	return port;
+}
+EXPORT_SYMBOL_GPL(omapdss_of_get_next_port);
+
+struct device_node *
+omapdss_of_get_next_endpoint(const struct device_node *parent,
+			     struct device_node *prev)
+{
+	struct device_node *ep = NULL;
+
+	if (!parent)
+		return NULL;
+
+	do {
+		ep = of_get_next_child(parent, prev);
+		if (!ep)
+			return NULL;
+		prev = ep;
+	} while (of_node_cmp(ep->name, "endpoint") != 0);
+
+	return ep;
+}
+EXPORT_SYMBOL_GPL(omapdss_of_get_next_endpoint);
+
+static struct device_node *
+omapdss_of_get_remote_device_node(const struct device_node *node)
+{
+	struct device_node *np;
+	int i;
+
+	np = of_parse_phandle(node, "remote-endpoint", 0);
+
+	if (!np)
+		return NULL;
+
+	np = of_get_next_parent(np);
+
+	for (i = 0; i < 3 && np; ++i) {
+		struct property *prop;
+
+		prop = of_find_property(np, "compatible", NULL);
+
+		if (prop)
+			return np;
+
+		np = of_get_next_parent(np);
+	}
+
+	return NULL;
+}
+
+struct device_node *
+omapdss_of_get_first_endpoint(const struct device_node *parent)
+{
+	struct device_node *port;
+	struct device_node *ep;
+
+	port = omapdss_of_get_next_port(parent, NULL);
+	if (port) {
+		ep = omapdss_of_get_next_endpoint(port, NULL);
+		of_node_put(port);
+	} else {
+		ep = omapdss_of_get_next_endpoint(parent, NULL);
+	}
+
+	return ep;
+}
+EXPORT_SYMBOL_GPL(omapdss_of_get_first_endpoint);
+
+struct omap_dss_device *
+omapdss_of_find_source_for_first_ep(struct device_node *node)
+{
+	struct device_node *ep;
+	struct device_node *src_node;
+	struct omap_dss_device *src;
+
+	ep = omapdss_of_get_first_endpoint(node);
+	if (!ep)
+		return ERR_PTR(-EINVAL);
+
+	src_node = omapdss_of_get_remote_device_node(ep);
+
+	of_node_put(ep);
+
+	if (!src_node)
+		return ERR_PTR(-EINVAL);
+
+	src = omap_dss_find_output_by_node(src_node);
+
+	of_node_put(src_node);
+
+	if (!src)
+		return ERR_PTR(-EPROBE_DEFER);
+
+	return src;
+}
+EXPORT_SYMBOL_GPL(omapdss_of_find_source_for_first_ep);
diff --git a/include/video/omapdss.h b/include/video/omapdss.h
index 3d7c51a6f9ff..e3cd2a921c2f 100644
--- a/include/video/omapdss.h
+++ b/include/video/omapdss.h
@@ -1019,4 +1019,18 @@ static inline bool omapdss_device_is_enabled(struct omap_dss_device *dssdev)
 	return dssdev->state == OMAP_DSS_DISPLAY_ACTIVE;
 }
 
+struct device_node *
+omapdss_of_get_next_port(const struct device_node *parent,
+			 struct device_node *prev);
+
+struct device_node *
+omapdss_of_get_next_endpoint(const struct device_node *parent,
+			     struct device_node *prev);
+
+struct device_node *
+omapdss_of_get_first_endpoint(const struct device_node *parent);
+
+struct omap_dss_device *
+omapdss_of_find_source_for_first_ep(struct device_node *node);
+
 #endif
-- 
1.8.3.2

^ permalink raw reply related

* [PATCHv3 06/41] OMAPFB: search for default display with DT alias
From: Tomi Valkeinen @ 2014-01-21 10:56 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <1390301833-24944-1-git-send-email-tomi.valkeinen@ti.com>

Improve the search for the default display in two ways:
* compare the given display name to the display's alias
* if no display name is given, look for "display0" DT alias

Signed-off-by: Tomi Valkeinen <tomi.valkeinen@ti.com>
---
 drivers/video/omap2/omapfb/omapfb-main.c | 23 ++++++++++++++++++++++-
 1 file changed, 22 insertions(+), 1 deletion(-)

diff --git a/drivers/video/omap2/omapfb/omapfb-main.c b/drivers/video/omap2/omapfb/omapfb-main.c
index b7469636f15c..f121e87b30be 100644
--- a/drivers/video/omap2/omapfb/omapfb-main.c
+++ b/drivers/video/omap2/omapfb/omapfb-main.c
@@ -2423,7 +2423,10 @@ omapfb_find_default_display(struct omapfb2_device *fbdev)
 	const char *def_name;
 	int i;
 
-	/* search with the display name from the user or the board file */
+	/*
+	 * Search with the display name from the user or the board file,
+	 * comparing to display names and aliases
+	 */
 
 	def_name = omapdss_get_default_display_name();
 
@@ -2435,12 +2438,30 @@ omapfb_find_default_display(struct omapfb2_device *fbdev)
 
 			if (dssdev->name && strcmp(def_name, dssdev->name) == 0)
 				return dssdev;
+
+			if (strcmp(def_name, dssdev->alias) == 0)
+				return dssdev;
 		}
 
 		/* def_name given but not found */
 		return NULL;
 	}
 
+	/* then look for DT alias display0 */
+	for (i = 0; i < fbdev->num_displays; ++i) {
+		struct omap_dss_device *dssdev;
+		int id;
+
+		dssdev = fbdev->displays[i].dssdev;
+
+		if (dssdev->dev->of_node == NULL)
+			continue;
+
+		id = of_alias_get_id(dssdev->dev->of_node, "display");
+		if (id == 0)
+			return dssdev;
+	}
+
 	/* return the first display we have in the list */
 	return fbdev->displays[0].dssdev;
 }
-- 
1.8.3.2

^ permalink raw reply related

* [PATCHv3 05/41] OMAPFB: clean up default display search
From: Tomi Valkeinen @ 2014-01-21 10:56 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <1390301833-24944-1-git-send-email-tomi.valkeinen@ti.com>

Separate the code for finding the default display into a function for
clarity and to make it easier to extend it in the future.

Signed-off-by: Tomi Valkeinen <tomi.valkeinen@ti.com>
---
 drivers/video/omap2/omapfb/omapfb-main.c | 46 ++++++++++++++++++++------------
 1 file changed, 29 insertions(+), 17 deletions(-)

diff --git a/drivers/video/omap2/omapfb/omapfb-main.c b/drivers/video/omap2/omapfb/omapfb-main.c
index fcb9e932d00c..b7469636f15c 100644
--- a/drivers/video/omap2/omapfb/omapfb-main.c
+++ b/drivers/video/omap2/omapfb/omapfb-main.c
@@ -2417,6 +2417,34 @@ static int omapfb_init_connections(struct omapfb2_device *fbdev,
 	return 0;
 }
 
+static struct omap_dss_device *
+omapfb_find_default_display(struct omapfb2_device *fbdev)
+{
+	const char *def_name;
+	int i;
+
+	/* search with the display name from the user or the board file */
+
+	def_name = omapdss_get_default_display_name();
+
+	if (def_name) {
+		for (i = 0; i < fbdev->num_displays; ++i) {
+			struct omap_dss_device *dssdev;
+
+			dssdev = fbdev->displays[i].dssdev;
+
+			if (dssdev->name && strcmp(def_name, dssdev->name) == 0)
+				return dssdev;
+		}
+
+		/* def_name given but not found */
+		return NULL;
+	}
+
+	/* return the first display we have in the list */
+	return fbdev->displays[0].dssdev;
+}
+
 static int omapfb_probe(struct platform_device *pdev)
 {
 	struct omapfb2_device *fbdev = NULL;
@@ -2494,23 +2522,7 @@ static int omapfb_probe(struct platform_device *pdev)
 	for (i = 0; i < fbdev->num_managers; i++)
 		fbdev->managers[i] = omap_dss_get_overlay_manager(i);
 
-	def_display = NULL;
-
-	for (i = 0; i < fbdev->num_displays; ++i) {
-		struct omap_dss_device *dssdev;
-		const char *def_name;
-
-		def_name = omapdss_get_default_display_name();
-
-		dssdev = fbdev->displays[i].dssdev;
-
-		if (def_name == NULL ||
-			(dssdev->name && strcmp(def_name, dssdev->name) == 0)) {
-			def_display = dssdev;
-			break;
-		}
-	}
-
+	def_display = omapfb_find_default_display(fbdev);
 	if (def_display == NULL) {
 		dev_err(fbdev->dev, "failed to find default display\n");
 		r = -EPROBE_DEFER;
-- 
1.8.3.2

^ permalink raw reply related

* [PATCHv3 04/41] OMAPDSS: get dssdev->alias from DT alias
From: Tomi Valkeinen @ 2014-01-21 10:56 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <1390301833-24944-1-git-send-email-tomi.valkeinen@ti.com>

We currently create a "displayX" style alias name for all displays,
using a number that is incremented for each registered display. With the
new DSS device model there is no clear order in which the displays are
registered, and thus the numbering is somewhat random.

This patch improves the behavior for DT case so that if the displays
have been assigned DT aliases, those aliases will be used as DSS
aliases.

This means that "display0" is always the one specified in the DT alias,
and thus display0 can be used as default display in case the user didn't
specify a default display.

Signed-off-by: Tomi Valkeinen <tomi.valkeinen@ti.com>
---
 drivers/video/omap2/dss/display.c | 19 +++++++++++++++++--
 1 file changed, 17 insertions(+), 2 deletions(-)

diff --git a/drivers/video/omap2/dss/display.c b/drivers/video/omap2/dss/display.c
index 012ada38a29d..21080f9dae87 100644
--- a/drivers/video/omap2/dss/display.c
+++ b/drivers/video/omap2/dss/display.c
@@ -134,9 +134,24 @@ static int disp_num_counter;
 int omapdss_register_display(struct omap_dss_device *dssdev)
 {
 	struct omap_dss_driver *drv = dssdev->driver;
+	int id;
 
-	snprintf(dssdev->alias, sizeof(dssdev->alias),
-			"display%d", disp_num_counter++);
+	/*
+	 * Note: this presumes all the displays are either using DT or non-DT,
+	 * which normally should be the case. This also presumes that all
+	 * displays either have an DT alias, or none has.
+	 */
+
+	if (dssdev->dev->of_node) {
+		id = of_alias_get_id(dssdev->dev->of_node, "display");
+
+		if (id < 0)
+			id = disp_num_counter++;
+	} else {
+		id = disp_num_counter++;
+	}
+
+	snprintf(dssdev->alias, sizeof(dssdev->alias), "display%d", id);
 
 	/* Use 'label' property for name, if it exists */
 	if (dssdev->dev->of_node)
-- 
1.8.3.2

^ permalink raw reply related

* [PATCHv3 03/41] OMAPDSS: add 'label' support for DT
From: Tomi Valkeinen @ 2014-01-21 10:56 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <1390301833-24944-1-git-send-email-tomi.valkeinen@ti.com>

Add support to get the label (i.e. a "nickname") for a display from the
DT data. If there is no label defined, use the display's alias (e.g.
'display0') as a name.

Signed-off-by: Tomi Valkeinen <tomi.valkeinen@ti.com>
---
 drivers/video/omap2/dss/display.c | 9 +++++++++
 1 file changed, 9 insertions(+)

diff --git a/drivers/video/omap2/dss/display.c b/drivers/video/omap2/dss/display.c
index 669a81fdf58e..012ada38a29d 100644
--- a/drivers/video/omap2/dss/display.c
+++ b/drivers/video/omap2/dss/display.c
@@ -26,6 +26,7 @@
 #include <linux/module.h>
 #include <linux/jiffies.h>
 #include <linux/platform_device.h>
+#include <linux/of.h>
 
 #include <video/omapdss.h>
 #include "dss.h"
@@ -137,6 +138,14 @@ int omapdss_register_display(struct omap_dss_device *dssdev)
 	snprintf(dssdev->alias, sizeof(dssdev->alias),
 			"display%d", disp_num_counter++);
 
+	/* Use 'label' property for name, if it exists */
+	if (dssdev->dev->of_node)
+		of_property_read_string(dssdev->dev->of_node, "label",
+			&dssdev->name);
+
+	if (dssdev->name == NULL)
+		dssdev->name = dssdev->alias;
+
 	if (drv && drv->get_resolution == NULL)
 		drv->get_resolution = omapdss_default_get_resolution;
 	if (drv && drv->get_recommended_bpp == NULL)
-- 
1.8.3.2

^ permalink raw reply related

* [PATCHv3 02/41] ARM: OMAP2+: DT 'compatible' tweak for displays
From: Tomi Valkeinen @ 2014-01-21 10:56 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <1390301833-24944-1-git-send-email-tomi.valkeinen@ti.com>

As there is no common panel framework in the kernel, we have OMAP
specific panel drivers. However, the DT data should be generic. This
brings the issue that some other platform could use the same panels, and
would need to create a driver with the same 'compatible' string as the
OMAP driver.

In the long run, we have to get a common panel framework. For the time
being, this patch solves the issue:

At early boot time, we go through the DT nodes looking for the panels
the kernel supports for OMAP. For each found node, the 'compatible'
string is prepended with "omapdss,", i.e. "sony,acx565akm" becomes
"omapdss,sony,acx565akm". The OMAP display drivers all have "omapdss,"
at the beginning of their compatible field.

This allows us to have generic DT data, but OMAP specific display
drivers.

Signed-off-by: Tomi Valkeinen <tomi.valkeinen@ti.com>
---
 arch/arm/mach-omap2/board-generic.c |  2 ++
 arch/arm/mach-omap2/common.h        |  1 +
 arch/arm/mach-omap2/display.c       | 56 +++++++++++++++++++++++++++++++++++++
 3 files changed, 59 insertions(+)

diff --git a/arch/arm/mach-omap2/board-generic.c b/arch/arm/mach-omap2/board-generic.c
index 842e4f21ab09..08921d0d63d4 100644
--- a/arch/arm/mach-omap2/board-generic.c
+++ b/arch/arm/mach-omap2/board-generic.c
@@ -35,6 +35,8 @@ static struct of_device_id omap_dt_match_table[] __initdata = {
 
 static void __init omap_generic_init(void)
 {
+	omapdss_early_init_of();
+
 	pdata_quirks_init(omap_dt_match_table);
 
 	omapdss_init_of();
diff --git a/arch/arm/mach-omap2/common.h b/arch/arm/mach-omap2/common.h
index 04a4d360dd20..e21702e2e82e 100644
--- a/arch/arm/mach-omap2/common.h
+++ b/arch/arm/mach-omap2/common.h
@@ -308,6 +308,7 @@ extern int omap_dss_reset(struct omap_hwmod *);
 extern int (*omap_clk_init)(void);
 
 int __init omapdss_init_of(void);
+void __init omapdss_early_init_of(void);
 
 #endif /* __ASSEMBLER__ */
 #endif /* __ARCH_ARM_MACH_OMAP2PLUS_COMMON_H */
diff --git a/arch/arm/mach-omap2/display.c b/arch/arm/mach-omap2/display.c
index 77cd927e5acb..9a729bd3d28b 100644
--- a/arch/arm/mach-omap2/display.c
+++ b/arch/arm/mach-omap2/display.c
@@ -25,6 +25,7 @@
 #include <linux/delay.h>
 #include <linux/of.h>
 #include <linux/of_platform.h>
+#include <linux/slab.h>
 
 #include <video/omapdss.h>
 #include "omap_hwmod.h"
@@ -555,6 +556,61 @@ int omap_dss_reset(struct omap_hwmod *oh)
 	return r;
 }
 
+/* list of 'compatible' nodes to convert to omapdss specific */
+static const char * const dss_compat_conv_list[] __initconst = {
+	"composite-connector",
+	"dvi-connector",
+	"hdmi-connector",
+	"panel-dpi",
+	"panel-dsi-cm",
+	"sony,acx565akm",
+	"svideo-connector",
+	"ti,tfp410",
+	"ti,tpd12s015",
+};
+
+/* prepend compatible string with "omapdss," */
+static __init void omapdss_omapify_node(struct device_node *node,
+	const char *compat)
+{
+	char *new_compat;
+	struct property *prop;
+
+	new_compat = kasprintf(GFP_KERNEL, "omapdss,%s", compat);
+
+	prop = kzalloc(sizeof(*prop), GFP_KERNEL);
+	prop->name = "compatible";
+	prop->value = new_compat;
+	prop->length = strlen(new_compat) + 1;
+
+	of_update_property(node, prop);
+}
+
+/*
+ * As omapdss panel drivers are omapdss specific, but we want to define the
+ * DT-data in generic manner, we convert the compatible strings of the panel
+ * nodes from "panel-foo" to "omapdss,panel-foo". This way we can have both
+ * correct DT data and omapdss specific drivers.
+ *
+ * When we get generic panel drivers to the kernel, this will be removed.
+ */
+void __init omapdss_early_init_of(void)
+{
+	int i;
+
+	for (i = 0; i < ARRAY_SIZE(dss_compat_conv_list); ++i) {
+		const char *compat = dss_compat_conv_list[i];
+		struct device_node *node = NULL;
+
+		while ((node = of_find_compatible_node(node, NULL, compat))) {
+			if (!of_device_is_available(node))
+				continue;
+
+			omapdss_omapify_node(node, compat);
+		}
+	}
+}
+
 int __init omapdss_init_of(void)
 {
 	int r;
-- 
1.8.3.2

^ permalink raw reply related

* [PATCHv3 01/41] ARM: OMAP2+: add omapdss_init_of()
From: Tomi Valkeinen @ 2014-01-21 10:56 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <1390301833-24944-1-git-send-email-tomi.valkeinen@ti.com>

omapdss driver uses a omapdss platform device to pass platform specific
function pointers and DSS hardware version from the arch code to the
driver. This device is needed also when booting with DT.

This patch adds omapdss_init_of() function, called from board-generic at
init time, which creates the omapdss device.

Signed-off-by: Tomi Valkeinen <tomi.valkeinen@ti.com>
---
 arch/arm/mach-omap2/board-generic.c |  2 +
 arch/arm/mach-omap2/common.h        |  2 +
 arch/arm/mach-omap2/display.c       | 76 +++++++++++++++++++++++++++++++++++++
 3 files changed, 80 insertions(+)

diff --git a/arch/arm/mach-omap2/board-generic.c b/arch/arm/mach-omap2/board-generic.c
index 8d972ff18c56..842e4f21ab09 100644
--- a/arch/arm/mach-omap2/board-generic.c
+++ b/arch/arm/mach-omap2/board-generic.c
@@ -36,6 +36,8 @@ static struct of_device_id omap_dt_match_table[] __initdata = {
 static void __init omap_generic_init(void)
 {
 	pdata_quirks_init(omap_dt_match_table);
+
+	omapdss_init_of();
 }
 
 #ifdef CONFIG_SOC_OMAP2420
diff --git a/arch/arm/mach-omap2/common.h b/arch/arm/mach-omap2/common.h
index e30ef6797c63..04a4d360dd20 100644
--- a/arch/arm/mach-omap2/common.h
+++ b/arch/arm/mach-omap2/common.h
@@ -307,5 +307,7 @@ extern int omap_dss_reset(struct omap_hwmod *);
 /* SoC specific clock initializer */
 extern int (*omap_clk_init)(void);
 
+int __init omapdss_init_of(void);
+
 #endif /* __ASSEMBLER__ */
 #endif /* __ARCH_ARM_MACH_OMAP2PLUS_COMMON_H */
diff --git a/arch/arm/mach-omap2/display.c b/arch/arm/mach-omap2/display.c
index 4cf165502b35..77cd927e5acb 100644
--- a/arch/arm/mach-omap2/display.c
+++ b/arch/arm/mach-omap2/display.c
@@ -23,6 +23,8 @@
 #include <linux/clk.h>
 #include <linux/err.h>
 #include <linux/delay.h>
+#include <linux/of.h>
+#include <linux/of_platform.h>
 
 #include <video/omapdss.h>
 #include "omap_hwmod.h"
@@ -552,3 +554,77 @@ int omap_dss_reset(struct omap_hwmod *oh)
 
 	return r;
 }
+
+int __init omapdss_init_of(void)
+{
+	int r;
+	enum omapdss_version ver;
+	struct device_node *node;
+
+	static struct omap_dss_board_info board_data = {
+		.dsi_enable_pads = omap_dsi_enable_pads,
+		.dsi_disable_pads = omap_dsi_disable_pads,
+		.get_context_loss_count = omap_pm_get_dev_context_loss_count,
+		.set_min_bus_tput = omap_dss_set_min_bus_tput,
+	};
+
+	/* only create dss helper devices if dss is enabled in the .dts */
+
+	node = of_find_compatible_node(NULL, NULL, "ti,omap2-dss");
+	if (!node)
+		node = of_find_compatible_node(NULL, NULL, "ti,omap3-dss");
+	if (!node)
+		node = of_find_compatible_node(NULL, NULL, "ti,omap4-dss");
+	if (!node)
+		return 0;
+
+	if (!of_device_is_available(node))
+		return 0;
+
+	ver = omap_display_get_version();
+
+	if (ver == OMAPDSS_VER_UNKNOWN) {
+		pr_err("DSS not supported on this SoC\n");
+		return -ENODEV;
+	}
+
+	board_data.version = ver;
+
+	omap_display_device.dev.platform_data = &board_data;
+
+	r = platform_device_register(&omap_display_device);
+	if (r < 0) {
+		pr_err("Unable to register omapdss device\n");
+		return r;
+	}
+
+	/* create DRM device */
+	r = omap_init_drm();
+	if (r < 0) {
+		pr_err("Unable to register omapdrm device\n");
+		return r;
+	}
+
+	/* create vrfb device */
+	r = omap_init_vrfb();
+	if (r < 0) {
+		pr_err("Unable to register omapvrfb device\n");
+		return r;
+	}
+
+	/* create FB device */
+	r = omap_init_fb();
+	if (r < 0) {
+		pr_err("Unable to register omapfb device\n");
+		return r;
+	}
+
+	/* create V4L2 display device */
+	r = omap_init_vout();
+	if (r < 0) {
+		pr_err("Unable to register omap_vout device\n");
+		return r;
+	}
+
+	return 0;
+}
-- 
1.8.3.2

^ permalink raw reply related

* [PATCHv3 00/41] OMAPDSS: DT support v3
From: Tomi Valkeinen @ 2014-01-21 10:56 UTC (permalink / raw)
  To: linux-arm-kernel

Hi,

Here's version 3 of the DSS DT series.

The previous version can be found from:

v1: http://permalink.gmane.org/gmane.linux.ports.arm.omap/108249
v2: http://permalink.gmane.org/gmane.linux.ports.arm.omap/108866

The main changes to v2 are:

- DT Binding documentation
- OMAP2 DSS support
- Split DSI register space
- DSS nodes disabled by default
- Hack to have generic DT bindings but OMAP specific drivers (for now)

This series can also be found from:

git://git.kernel.org/pub/scm/linux/kernel/git/tomba/linux.git work/dss-dt-review-3

 Tomi

Javier Martinez Canillas (1):
  ARM: omap3-igep0020.dts: add display information

Sebastian Reichel (1):
  OMAPDSS: acx565akm: Add DT support

Tomi Valkeinen (39):
  ARM: OMAP2+: add omapdss_init_of()
  ARM: OMAP2+: DT 'compatible' tweak for displays
  OMAPDSS: add 'label' support for DT
  OMAPDSS: get dssdev->alias from DT alias
  OMAPFB: clean up default display search
  OMAPFB: search for default display with DT alias
  OMAPDSS: add of helpers
  OMAPDSS: Improve regulator names for DT
  OMAPDSS: Add DT support to DSS
  OMAPDSS: Add DT support to DISPC
  OMAPDSS: Add DT support to HDMI
  OMAPDSS: Add DT support to VENC
  OMAPDSS: Add DT support to DSI
  OMAPDSS: panel-dsi-cm: Add DT support
  OMAPDSS: encoder-tfp410: Add DT support
  OMAPDSS: connector-dvi: Add DT support
  OMAPDSS: encoder-tpd12s015: Add DT support
  OMAPDSS: hdmi-connector: Add DT support
  OMAPDSS: panel-dpi: Add DT support
  OMAPDSS: connector-analog-tv: Add DT support
  ARM: omap2.dtsi: add omapdss information
  ARM: omap3.dtsi: add omapdss information
  ARM: omap4.dtsi: add omapdss information
  ARM: omap4-panda.dts: add display information
  ARM: omap4-sdp.dts: add display information
  ARM: omap3-beagle.dts: add display information
  ARM: omap3-beagle-xm.dts: add display information
  ARM: omap3-n900.dts: add display information
  OMAPDSS: remove DT hacks for regulators
  ARM: OMAP2+: remove pdata quirks for displays
  Doc/DT: Add OMAP DSS DT Bindings
  Doc/DT: Add DT binding documentation for Analog TV Connector
  Doc/DT: Add DT binding documentation for DVI Connector
  Doc/DT: Add DT binding documentation for HDMI Connector
  Doc/DT: Add DT binding documentation for MIPI DPI Panel
  Doc/DT: Add DT binding documentation for MIPI DSI CM Panel
  Doc/DT: Add DT binding documentation for Sony acx565akm panel
  Doc/DT: Add DT binding documentation for TFP410 encoder
  Doc/DT: Add DT binding documentation for tpd12s015 encoder

 .../bindings/video/analog-tv-connector.txt         |  23 +++
 .../devicetree/bindings/video/dvi-connector.txt    |  26 +++
 .../devicetree/bindings/video/hdmi-connector.txt   |  23 +++
 .../devicetree/bindings/video/panel-dpi.txt        |  43 ++++
 .../devicetree/bindings/video/panel-dsi-cm.txt     |  26 +++
 .../devicetree/bindings/video/sony,acx565akm.txt   |  28 +++
 .../devicetree/bindings/video/ti,omap-dss.txt      | 197 ++++++++++++++++++
 .../devicetree/bindings/video/ti,omap2-dss.txt     |  54 +++++
 .../devicetree/bindings/video/ti,omap3-dss.txt     |  73 +++++++
 .../devicetree/bindings/video/ti,omap4-dss.txt     |  99 +++++++++
 .../devicetree/bindings/video/ti,tfp410.txt        |  41 ++++
 .../devicetree/bindings/video/ti,tpd12s015.txt     |  44 ++++
 .../devicetree/bindings/video/video-ports.txt      |  22 ++
 arch/arm/boot/dts/omap2.dtsi                       |  31 +++
 arch/arm/boot/dts/omap3-beagle-xm.dts              | 119 +++++++++++
 arch/arm/boot/dts/omap3-beagle.dts                 | 116 +++++++++++
 arch/arm/boot/dts/omap3-igep0020.dts               |  59 +++++-
 arch/arm/boot/dts/omap3-n900.dts                   |  70 ++++++-
 arch/arm/boot/dts/omap3.dtsi                       |  42 ++++
 arch/arm/boot/dts/omap4-panda-common.dtsi          | 119 ++++++++++-
 arch/arm/boot/dts/omap4-sdp.dts                    | 107 +++++++++-
 arch/arm/boot/dts/omap4.dtsi                       |  65 ++++++
 arch/arm/mach-omap2/board-generic.c                |   4 +
 arch/arm/mach-omap2/common.h                       |   3 +
 arch/arm/mach-omap2/display.c                      | 132 ++++++++++++
 arch/arm/mach-omap2/dss-common.c                   | 224 ---------------------
 arch/arm/mach-omap2/pdata-quirks.c                 |   3 -
 .../video/omap2/displays-new/connector-analog-tv.c |  43 +++-
 drivers/video/omap2/displays-new/connector-dvi.c   |  43 ++++
 drivers/video/omap2/displays-new/connector-hdmi.c  |  30 +++
 drivers/video/omap2/displays-new/encoder-tfp410.c  |  43 +++-
 .../video/omap2/displays-new/encoder-tpd12s015.c   |  56 ++++++
 drivers/video/omap2/displays-new/panel-dpi.c       |  64 +++++-
 drivers/video/omap2/displays-new/panel-dsi-cm.c    |  65 +++++-
 .../omap2/displays-new/panel-sony-acx565akm.c      |  33 ++-
 drivers/video/omap2/dss/Makefile                   |   2 +-
 drivers/video/omap2/dss/dispc.c                    |   8 +
 drivers/video/omap2/dss/display.c                  |  28 ++-
 drivers/video/omap2/dss/dpi.c                      |  47 +++++
 drivers/video/omap2/dss/dsi.c                      | 147 +++++++++++++-
 drivers/video/omap2/dss/dss-of.c                   | 159 +++++++++++++++
 drivers/video/omap2/dss/dss.c                      |  64 ++++++
 drivers/video/omap2/dss/dss.h                      |   6 +
 drivers/video/omap2/dss/hdmi4.c                    |  14 +-
 drivers/video/omap2/dss/sdi.c                      |  45 +++++
 drivers/video/omap2/dss/venc.c                     |  66 +++++-
 drivers/video/omap2/omapfb/omapfb-main.c           |  67 ++++--
 include/video/omapdss.h                            |  14 ++
 48 files changed, 2550 insertions(+), 287 deletions(-)
 create mode 100644 Documentation/devicetree/bindings/video/analog-tv-connector.txt
 create mode 100644 Documentation/devicetree/bindings/video/dvi-connector.txt
 create mode 100644 Documentation/devicetree/bindings/video/hdmi-connector.txt
 create mode 100644 Documentation/devicetree/bindings/video/panel-dpi.txt
 create mode 100644 Documentation/devicetree/bindings/video/panel-dsi-cm.txt
 create mode 100644 Documentation/devicetree/bindings/video/sony,acx565akm.txt
 create mode 100644 Documentation/devicetree/bindings/video/ti,omap-dss.txt
 create mode 100644 Documentation/devicetree/bindings/video/ti,omap2-dss.txt
 create mode 100644 Documentation/devicetree/bindings/video/ti,omap3-dss.txt
 create mode 100644 Documentation/devicetree/bindings/video/ti,omap4-dss.txt
 create mode 100644 Documentation/devicetree/bindings/video/ti,tfp410.txt
 create mode 100644 Documentation/devicetree/bindings/video/ti,tpd12s015.txt
 create mode 100644 Documentation/devicetree/bindings/video/video-ports.txt
 create mode 100644 drivers/video/omap2/dss/dss-of.c

-- 
1.8.3.2

^ permalink raw reply

* [PATCH v2 02/15] clocksource: orion: Use atomic access for shared registers
From: Arnd Bergmann @ 2014-01-21 10:54 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <52DE44B9.70308@gmail.com>

On Tuesday 21 January 2014 10:58:17 Sebastian Hesselbarth wrote:
> On 01/21/14 10:46, Arnd Bergmann wrote:
> > On Tuesday 21 January 2014 06:12:28 Ezequiel Garcia wrote:
> >> -/*
> >> - * Thread-safe access to TIMER_CTRL register
> >> - * (shared with watchdog timer)
> >> - */
> >> -void orion_timer_ctrl_clrset(u32 clr, u32 set)
> >> -{
> >> -       spin_lock(&timer_ctrl_lock);
> >> -       writel((readl(timer_base + TIMER_CTRL) & ~clr) | set,
> >> -               timer_base + TIMER_CTRL);
> >> -       spin_unlock(&timer_ctrl_lock);
> >> -}
> >> -EXPORT_SYMBOL(orion_timer_ctrl_clrset);
> >
> > I don't understand what's wrong with this function, it seems like
> > a cleaner approach than touching the register directly from two
> > different drivers. Is this something that would only work on
> > orion but not on armadaxp?
> 
> The real problem with this is that it resides in orion-time.c which
> is fine for Orion SoCs. Armada 370/XP use a different timer and
> therefore the _common_ watchdog driver cannot call this function.

Right, I had missed this part.

> Moreover, Dove (out of Orion) and Armada 370/XP will happily live in
> one V7 kernel with both time-orion and time-armada-370-xp compiled in.

It still wouldn't be too hard to do: both clock drivers could export
functions with different identifiers, and the wdt driver can use
conditional compilation (#ifdef or if(IS_ENABLED())) to only call
those functions when the respective clocksource drivers are enabled.

I think the part where it went wrong is the fact that we have two
device nodes in DT that claim the same register range, which should
not have happened, but seems too hard to fix now without breaking
one part or another.

> The idea of the atomic readl/writel was to have something lightweight
> and _early_ to allow such locking even for timers.
> 
> IIRC, there was some kind of consensus that it is okay to have atomic
> readl/writel here.

Ok, Ezequiel pointed me to that discussion now, seems ok then.

	Arnd

^ permalink raw reply

* [PATCH v2 02/15] clocksource: orion: Use atomic access for shared registers
From: Ezequiel Garcia @ 2014-01-21 10:53 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <52DE4491.2000505@linaro.org>

Hello Daniel,

On Tue, Jan 21, 2014 at 10:57:37AM +0100, Daniel Lezcano wrote:
> On 01/21/2014 10:12 AM, Ezequiel Garcia wrote:
> > Replace the driver-specific thread-safe shared register API
> > by the recently introduced atomic_io_clear_set().
> >
> > Signed-off-by: Ezequiel Garcia <ezequiel.garcia@free-electrons.com>
> 
> in the future, put me in Cc when modifying files in clocksource that 
> will make my life easier to track the changes.
> 

Ouch, sorry about that.

[..]
> >   /*
> >    * Free-running clocksource handling.
> > @@ -68,7 +54,8 @@ static int orion_clkevt_next_event(unsigned long delta,
> >   {
> >   	/* setup and enable one-shot timer */
> >   	writel(delta, timer_base + TIMER1_VAL);
> > -	orion_timer_ctrl_clrset(TIMER1_RELOAD_EN, TIMER1_EN);
> > +	atomic_io_modify(timer_base + TIMER_CTRL,
> > +		TIMER1_RELOAD_EN | TIMER1_EN, TIMER1_EN);
> 
> I am not convinced this change is worth.
> 
> Could you elaborate the race the spinlock should prevent ?
> 

Sure. Take a look to the patchset that adds the atomic I/O (sorry again for
not Ccing you on that one):

  http://www.spinics.net/lists/arm-kernel/msg292775.html

The TIMER_CTRL register provides clocksource and watchdog control, so we
need a safe way to access the register, since we're sharing it on two
completely unrelated drivers.
-- 
Ezequiel Garc?a, Free Electrons
Embedded Linux, Kernel and Android Engineering
http://free-electrons.com

^ permalink raw reply

* [PATCH] ARM: imx: correct usecount of IPG, ARM and MMDC clk on i.mx6sl
From: Anson.Huang at freescale.com @ 2014-01-21 10:52 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <1390298702.8981.2.camel@weser.hi.pengutronix.de>

Hi, Lucas
	Thanks for the comment, already improved the comment in V2 patch.

Best Regards.
Anson huang ???
?
Freescale Semiconductor Shanghai
?????????192?A?2?
201203
Tel:021-28937058


>-----Original Message-----
>From: Lucas Stach [mailto:l.stach at pengutronix.de]
>Sent: Tuesday, January 21, 2014 6:05 PM
>To: Huang Yongcai-B20788
>Cc: shawn.guo at linaro.org; kernel at pengutronix.de; linux-arm-
>kernel at lists.infradead.org
>Subject: Re: [PATCH] ARM: imx: correct usecount of IPG, ARM and MMDC clk on
>i.mx6sl
>
>Am Montag, den 20.01.2014, 18:44 +0800 schrieb Anson Huang:
>> IPG, ARM and MMDC's clock should be enabled during kernel boot up, so
>> we need to maintain their use count, otherwise, they may be disabled
>> unexpectedly if their children's clock are turned off, which is not
>> allowed.
>>
>> Signed-off-by: Anson Huang <b20788@freescale.com>
>> ---
>>  arch/arm/mach-imx/clk-imx6sl.c |   15 +++++++++++++++
>>  1 file changed, 15 insertions(+)
>>
>> diff --git a/arch/arm/mach-imx/clk-imx6sl.c
>> b/arch/arm/mach-imx/clk-imx6sl.c index 78f3bd6..8d720f9 100644
>> --- a/arch/arm/mach-imx/clk-imx6sl.c
>> +++ b/arch/arm/mach-imx/clk-imx6sl.c
>> @@ -291,6 +291,21 @@ static void __init imx6sl_clocks_init(struct
>device_node *ccm_node)
>>  		pr_warn("%s: failed to set AHB clock rate %d!\n",
>>  			__func__, ret);
>>
>> +	/* Correct usecount of IPG clk */
>
>You don't really correct anything here, so this comment is misleading.
>If those are always on clocks, like you suggest in the commit message, please
>put this in the comment instead.
>
>Regards,
>Lucas
>
>> +	ret = clk_prepare_enable(clks[IMX6SL_CLK_IPG]);
>> +	if (ret)
>> +		pr_warn("%s: failed to enable IPG clock %d\n", __func__, ret);
>> +
>> +	/* Correct usecount of ARM clk */
>> +	ret = clk_prepare_enable(clks[IMX6SL_CLK_ARM]);
>> +	if (ret)
>> +		pr_warn("%s: failed to enable ARM clock %d\n", __func__, ret);
>> +
>> +	/* Correct usecount of MMDC clk */
>> +	ret = clk_prepare_enable(clks[IMX6SL_CLK_MMDC_ROOT]);
>> +	if (ret)
>> +		pr_warn("%s: failed to enable MMDC clock %d\n", __func__, ret);
>> +
>>  	if (IS_ENABLED(CONFIG_USB_MXS_PHY)) {
>>  		clk_prepare_enable(clks[IMX6SL_CLK_USBPHY1_GATE]);
>>  		clk_prepare_enable(clks[IMX6SL_CLK_USBPHY2_GATE]);
>
>--
>Pengutronix e.K.                           | Lucas Stach                 |
>Industrial Linux Solutions                 | http://www.pengutronix.de/  |
>Peiner Str. 6-8, 31137 Hildesheim, Germany | Phone: +49-5121-206917-5076 |
>Amtsgericht Hildesheim, HRA 2686           | Fax:   +49-5121-206917-5555 |
>
>

^ permalink raw reply

* [PATCH v7] mmc: moxart: Add MOXA ART SD/MMC driver
From: Jonas Jensen @ 2014-01-21 10:52 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <1389951825-14884-1-git-send-email-jonas.jensen@gmail.com>

Add SD/MMC driver for MOXA ART SoCs.

Signed-off-by: Jonas Jensen <jonas.jensen@gmail.com>
---

Notes:
    v6 was supposed to fix a panic from dmaengine_tx_status() but it
    still happened. This is the sort of panic that lasts between reboots
    but not between kernel reflash.
    
    dmaengine_tx_status() is used only to print DMA error, that's why
    I decided to remove it, instead of risking permanent kernel panic.
    
    Changes since v6:
    
    1. use mmc_of_parse()
    2. remove duplicate defines MSD_*
    3. add reference to bindings/mmc/mmc.txt in DT binding
    4. update DT binding example
       ("coreclk" renamed "clk_apb", DMA channel number is no longer needed)
    5. rename "sdhci-moxart" "moxart-mmc"
    6. remove dmaengine_tx_status() / don't print DMA error
    7. format comments / use correct grammar
    
    Applies to next-20140121

 .../devicetree/bindings/mmc/moxa,moxart-mmc.txt    |  32 +
 drivers/mmc/host/Kconfig                           |   9 +
 drivers/mmc/host/Makefile                          |   1 +
 drivers/mmc/host/moxart-mmc.c                      | 872 +++++++++++++++++++++
 4 files changed, 914 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/mmc/moxa,moxart-mmc.txt
 create mode 100644 drivers/mmc/host/moxart-mmc.c

diff --git a/Documentation/devicetree/bindings/mmc/moxa,moxart-mmc.txt b/Documentation/devicetree/bindings/mmc/moxa,moxart-mmc.txt
new file mode 100644
index 0000000..c2045d1
--- /dev/null
+++ b/Documentation/devicetree/bindings/mmc/moxa,moxart-mmc.txt
@@ -0,0 +1,32 @@
+MOXA ART SD Host Controller Interface
+
+  Inherits from mmc binding[1].
+
+  [1] Documentation/devicetree/bindings/mmc/mmc.txt
+
+Required properties:
+
+- compatible :	Must be "moxa,moxart-mmc"
+- reg :		Should contain registers location and length
+- interrupts :	Should contain the interrupt number
+- clocks :	Should contain phandle for the clock feeding the SDHCI controller
+
+Optional properties:
+
+These are optional but required to enable DMA transfer mode:
+
+- dmas :	Should contain two DMA channels, line request number must be 5 for
+		both channels
+- dma-names :	Must be "tx", "rx"
+
+Example:
+
+	mmc: mmc at 98e00000 {
+		compatible = "moxa,moxart-mmc";
+		reg = <0x98e00000 0x5C>;
+		interrupts = <5 0>;
+		clocks = <&clk_apb>;
+		dmas =  <&dma 5>,
+			<&dma 5>;
+		dma-names = "tx", "rx";
+	};
diff --git a/drivers/mmc/host/Kconfig b/drivers/mmc/host/Kconfig
index 1384f67..15806d6 100644
--- a/drivers/mmc/host/Kconfig
+++ b/drivers/mmc/host/Kconfig
@@ -283,6 +283,15 @@ config MMC_SDHCI_BCM2835
 
 	  If unsure, say N.
 
+config MMC_MOXART
+	tristate "MOXART SD/MMC Host Controller support"
+	depends on ARCH_MOXART && MMC
+	help
+	  This selects support for the MOXART SD/MMC Host Controller.
+	  MOXA provides one multi-functional card reader which can
+	  be found on some embedded hardware such as UC-7112-LX.
+	  If you have a controller with this interface, say Y here.
+
 config MMC_OMAP
 	tristate "TI OMAP Multimedia Card Interface support"
 	depends on ARCH_OMAP
diff --git a/drivers/mmc/host/Makefile b/drivers/mmc/host/Makefile
index 3483b6b..6b90eda 100644
--- a/drivers/mmc/host/Makefile
+++ b/drivers/mmc/host/Makefile
@@ -51,6 +51,7 @@ obj-$(CONFIG_MMC_JZ4740)	+= jz4740_mmc.o
 obj-$(CONFIG_MMC_VUB300)	+= vub300.o
 obj-$(CONFIG_MMC_USHC)		+= ushc.o
 obj-$(CONFIG_MMC_WMT)		+= wmt-sdmmc.o
+obj-$(CONFIG_MMC_MOXART)	+= moxart-mmc.o
 
 obj-$(CONFIG_MMC_REALTEK_PCI)	+= rtsx_pci_sdmmc.o
 
diff --git a/drivers/mmc/host/moxart-mmc.c b/drivers/mmc/host/moxart-mmc.c
new file mode 100644
index 0000000..be12e12
--- /dev/null
+++ b/drivers/mmc/host/moxart-mmc.c
@@ -0,0 +1,872 @@
+/*
+ * MOXA ART MMC host driver.
+ *
+ * Copyright (C) 2013 Jonas Jensen
+ *
+ * Jonas Jensen <jonas.jensen@gmail.com>
+ *
+ * Based on code from
+ * Moxa Technologies Co., Ltd. <www.moxa.com>
+ *
+ * This file is licensed under the terms of the GNU General Public
+ * License version 2.  This program is licensed "as is" without any
+ * warranty of any kind, whether express or implied.
+ */
+
+#include <linux/version.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/ioport.h>
+#include <linux/platform_device.h>
+#include <linux/delay.h>
+#include <linux/interrupt.h>
+#include <linux/blkdev.h>
+#include <linux/dma-mapping.h>
+#include <linux/dmaengine.h>
+#include <linux/mmc/host.h>
+#include <linux/mmc/sd.h>
+#include <linux/mmc/mmc.h>
+#include <linux/sched.h>
+#include <linux/io.h>
+#include <linux/gpio.h>
+#include <linux/sizes.h>
+#include <linux/of_address.h>
+#include <linux/of_irq.h>
+#include <linux/clk.h>
+#include <linux/bitops.h>
+#include <linux/of_dma.h>
+
+#include <asm/dma.h>
+#include <asm/irq.h>
+
+#define MMC_RSP_SHORT		1
+#define MMC_RSP_LONG		2
+#define MMC_RSP_MASK		3
+#define MMC_ERR_NONE		0
+#define MMC_ERR_TIMEOUT		1
+#define MMC_MODE_MMC		0
+#define MMC_MODE_SD		1
+#define MMC_ERR_BADCRC		2
+#define MMC_VDD_360		23
+
+#define MSD_RETRY_COUNT		10
+
+#define REG_COMMAND		0
+#define REG_ARGUMENT		4
+#define REG_RESPONSE0		8
+#define REG_RESPONSE1		12
+#define REG_RESPONSE2		16
+#define REG_RESPONSE3		20
+#define REG_RESPONSE_COMMAND	24
+#define REG_DATA_CONTROL	28
+#define REG_DATA_TIMER		32
+#define REG_DATA_LENGTH		36
+#define REG_STATUS		40
+#define REG_CLEAR		44
+#define REG_INTERRUPT_MASK	48
+#define REG_POWER_CONTROL	52
+#define REG_CLOCK_CONTROL	56
+#define REG_BUS_WIDTH		60
+#define REG_DATA_WINDOW		64
+#define REG_FEATURE		68
+#define REG_REVISION		72
+
+/* REG_COMMAND */
+#define MSD_SDC_RST		BIT(10)
+#define MSD_CMD_EN		BIT(9)
+#define MSD_APP_CMD		BIT(8)
+#define MSD_LONG_RSP		BIT(7)
+#define MSD_NEED_RSP		BIT(6)
+#define MSD_CMD_IDX_MASK	0x3f
+
+/* REG_RESPONSE_COMMAND */
+#define MSD_RSP_CMD_APP		BIT(6)
+#define MSD_RSP_CMD_IDX_MASK	0x3f
+
+/* REG_DATA_CONTROL */
+#define MSD_DATA_EN		BIT(6)
+#define MSD_DMA_EN		BIT(5)
+#define MSD_DATA_WRITE		BIT(4)
+#define MSD_BLK_SIZE_MASK	0x0f
+
+/* REG_DATA_LENGTH */
+#define MSD_DATA_LEN_MASK	0xffffff
+
+/* REG_STATUS */
+#define MSD_WRITE_PROT		BIT(12)
+#define MSD_CARD_DETECT		BIT(11)
+/* 1-10 below can be sent to either registers, interrupt or clear. */
+#define MSD_CARD_CHANGE		BIT(10)
+#define MSD_FIFO_ORUN		BIT(9)
+#define MSD_FIFO_URUN		BIT(8)
+#define MSD_DATA_END		BIT(7)
+#define MSD_CMD_SENT		BIT(6)
+#define MSD_DATA_CRC_OK		BIT(5)
+#define MSD_RSP_CRC_OK		BIT(4)
+#define MSD_DATA_TIMEOUT	BIT(3)
+#define MSD_RSP_TIMEOUT		BIT(2)
+#define MSD_DATA_CRC_FAIL	BIT(1)
+#define MSD_RSP_CRC_FAIL	BIT(0)
+
+/* REG_POWER_CONTROL */
+#define MSD_SD_POWER_ON		BIT(4)
+#define MSD_SD_POWER_MASK	0x0f
+
+/* REG_CLOCK_CONTROL */
+#define MSD_CLK_DIS		BIT(8)
+#define MSD_CLK_SD		BIT(7)
+#define MSD_CLK_DIV_MASK	0x7f
+
+/* REG_BUS_WIDTH */
+#define MSD_WIDE_BUS_SUPPORT	BIT(3)
+#define MSD_WIDE_BUS		BIT(2)	/* 4 bytes */
+#define MSD_SINGLE_BUS		BIT(0)	/* 1 byte */
+
+/* REG_FEATURE */
+#define MSD_CPRM_FUNCTION	BIT(8)
+
+struct moxart_host {
+	spinlock_t			lock;
+	void __iomem			*base;
+	phys_addr_t			reg_phys;
+
+	struct dma_chan			*dma_chan_rx;
+	struct dma_chan			*dma_chan_tx;
+	struct dma_async_tx_descriptor	*tx_desc;
+	unsigned int			dma_direction;
+	bool				have_dma;
+	struct completion		dma_complete;
+	struct completion		pio_complete;
+
+	struct mmc_host			*mmc;
+	struct mmc_request		*mrq;
+
+	struct scatterlist		*cur_sg;
+	unsigned int			num_sg;
+	unsigned int			remain;
+	int				size;
+
+	unsigned int			timeout;
+	long				sysclk;
+	bool				removed;
+};
+
+#define MSD_FIFO_LENW	4	/* 4 words, total 4 * 4 = 16 bytes */
+#define MSD_FIFO_LENB	16
+
+#define DMA_FIFO_LEN_FORCE		0
+#define MIN_POWER (MMC_VDD_360 - MSD_SD_POWER_MASK)
+
+static inline void moxart_init_sg(struct moxart_host *host,
+				  struct mmc_data *data)
+{
+	host->cur_sg = data->sg;
+	host->num_sg = data->sg_len;
+	host->remain = host->cur_sg->length;
+
+	if (host->remain > host->size)
+		host->remain = host->size;
+
+	data->error = MMC_ERR_NONE;
+}
+
+static inline int moxart_next_sg(struct moxart_host *host)
+{
+	int remain;
+	struct mmc_data *data = host->mrq->cmd->data;
+
+	host->cur_sg++;
+	host->num_sg--;
+
+	if (host->num_sg > 0) {
+		host->remain = host->cur_sg->length;
+		remain = host->size - data->bytes_xfered;
+		if (remain > 0 && remain < host->remain)
+			host->remain = remain;
+	}
+
+	return host->num_sg;
+}
+
+static void moxart_send_command(struct moxart_host *host,
+	struct mmc_command *cmd)
+{
+	unsigned int status, cmdctrl;
+	int retry = 0;
+
+	dev_dbg(mmc_dev(host->mmc), "%s: cmd->opcode=%d\n",
+		__func__, cmd->opcode);
+
+	cmd->error = MMC_ERR_TIMEOUT;
+
+	writel(MSD_RSP_TIMEOUT | MSD_RSP_CRC_OK |
+	       MSD_RSP_CRC_FAIL | MSD_CMD_SENT, host->base + REG_CLEAR);
+	writel(cmd->arg, host->base + REG_ARGUMENT);
+
+	cmdctrl = cmd->opcode & MSD_CMD_IDX_MASK;
+	if (cmdctrl == SD_APP_SET_BUS_WIDTH || cmdctrl == SD_APP_OP_COND ||
+	    cmdctrl == SD_APP_SEND_SCR || cmdctrl == SD_APP_SD_STATUS ||
+	    cmdctrl == SD_APP_SEND_NUM_WR_BLKS)
+		cmdctrl |= MSD_APP_CMD;
+
+	if (cmd->flags & MMC_RSP_136)
+		cmdctrl |= (MSD_LONG_RSP | MSD_NEED_RSP);
+	else
+		cmdctrl |= MSD_NEED_RSP;
+
+	writel(cmdctrl | MSD_CMD_EN, host->base + REG_COMMAND);
+
+	while (retry++ < MSD_RETRY_COUNT) {
+		udelay(10);
+		status = readl(host->base + REG_STATUS);
+		if (status & MSD_CARD_DETECT) {
+			dev_dbg(mmc_dev(host->mmc), "%s: MSD_CARD_DETECT\n",
+				__func__);
+			cmd->error = MMC_ERR_TIMEOUT;
+			break;
+		}
+		if (cmdctrl & MSD_NEED_RSP) {
+			dev_dbg(mmc_dev(host->mmc), "%s: MSD_NEED_RSP\n",
+				__func__);
+			if (status & MSD_RSP_TIMEOUT) {
+				dev_dbg(mmc_dev(host->mmc), "%s: MSD_RSP_TIMEOUT\n",
+					__func__);
+				writel(MSD_RSP_TIMEOUT, host->base + REG_CLEAR);
+				cmd->error = MMC_ERR_TIMEOUT;
+				break;
+			}
+			if ((cmd->flags & MMC_RSP_CRC) &&
+				(status & MSD_RSP_CRC_FAIL)) {
+				dev_dbg(mmc_dev(host->mmc), "%s: MSD_RSP_CRC_FAIL\n",
+					__func__);
+				writel(MSD_RSP_CRC_FAIL, host->base +
+				       REG_CLEAR);
+				cmd->error = MMC_ERR_BADCRC;
+				break;
+			}
+			if (status & MSD_RSP_CRC_OK) {
+				dev_dbg(mmc_dev(host->mmc), "%s: MSD_RSP_CRC_OK\n",
+					__func__);
+				writel(MSD_RSP_CRC_OK, host->base + REG_CLEAR);
+
+				if (cmd->flags & MMC_RSP_136) {
+					cmd->resp[3] =
+						readl(host->base +
+						      REG_RESPONSE0);
+					cmd->resp[2] =
+						readl(host->base +
+						      REG_RESPONSE1);
+					cmd->resp[1] =
+						readl(host->base +
+						      REG_RESPONSE2);
+					cmd->resp[0] =
+						readl(host->base +
+						      REG_RESPONSE3);
+				} else {
+					cmd->resp[0] =
+						readl(host->base +
+						      REG_RESPONSE0);
+				}
+
+				cmd->error = MMC_ERR_NONE;
+				break;
+			}
+		} else {
+			dev_dbg(mmc_dev(host->mmc), "%s: !(cmdctrl & MSD_NEED_RSP)\n",
+				__func__);
+			if (status & MSD_CMD_SENT) {
+				writel(MSD_CMD_SENT, host->base + REG_CLEAR);
+				cmd->error = MMC_ERR_NONE;
+				break;
+			}
+		}
+	}
+
+	if (retry >= (MSD_RETRY_COUNT - 1)) {
+		/*
+		 * Seem to happen a lot on or after CMD25
+		 * (MMC_WRITE_MULTIPLE_BLOCK) with more than 4 blocks
+		 * (>4096), possibly because the transfer is too big
+		 * or takes too long to complete.
+		 *
+		 * When this happens the controller is usually rendered
+		 * unresponsive and can only be recovered with reboot.
+		 */
+		dev_dbg(mmc_dev(host->mmc), "%s: WARNING! no valid status found!\n",
+			__func__);
+	}
+}
+
+static void moxart_dma_complete(void *param)
+{
+	struct moxart_host *host = param;
+
+	dev_dbg(mmc_dev(host->mmc), "%s: host=%p\n", __func__, host);
+
+	complete(&host->dma_complete);
+}
+
+static void moxart_transfer_dma(struct mmc_data *data, struct moxart_host *host)
+{
+	unsigned int len, direction_dev;
+	struct dma_async_tx_descriptor *desc = NULL;
+	struct dma_chan *dma_chan_cur;
+
+	if (host->size == data->bytes_xfered)
+		return;
+
+	if (data->flags & MMC_DATA_WRITE) {
+		direction_dev = DMA_MEM_TO_DEV;
+		dma_chan_cur = host->dma_chan_tx;
+	} else {
+		direction_dev = DMA_DEV_TO_MEM;
+		dma_chan_cur = host->dma_chan_rx;
+	}
+
+	len = dma_map_sg(dma_chan_cur->device->dev, data->sg,
+			 data->sg_len, host->dma_direction);
+
+	if (len > 0) {
+		desc = dmaengine_prep_slave_sg(dma_chan_cur, data->sg,
+					       len, direction_dev,
+					       DMA_PREP_INTERRUPT |
+					       DMA_CTRL_ACK);
+	} else {
+		dev_err(mmc_dev(host->mmc),
+			"%s: dma_map_sg returned zero length\n",
+			__func__);
+	}
+
+	if (desc) {
+		host->tx_desc = desc;
+		desc->callback = moxart_dma_complete;
+		desc->callback_param = host;
+		dmaengine_submit(desc);
+		dma_async_issue_pending(dma_chan_cur);
+	}
+
+	data->bytes_xfered += host->remain;
+}
+
+/* When DMA is available this is used only for SD_APP_SEND_SCR. */
+static void moxart_transfer_pio(struct moxart_host *host)
+{
+	unsigned char *buffer;
+	unsigned int wcnt, i;
+	struct mmc_data *data = host->mrq->cmd->data;
+
+	if (host->size == data->bytes_xfered)
+		goto transfer_complete;
+
+	buffer = sg_virt(host->cur_sg);
+	wcnt = host->remain >> 2;
+
+	if (data->flags & MMC_DATA_WRITE) {
+		for (i = 0; i < wcnt; i++, buffer += 4) {
+			writel(*(unsigned int *)buffer,
+			       host->base + REG_DATA_WINDOW);
+			udelay(10);
+		}
+	} else {
+		for (i = 0; i < wcnt; i++, buffer += 4) {
+			/* Byte order is reversed only when reading SCR. */
+			if (data->mrq->cmd->opcode == SD_APP_SEND_SCR) {
+				*(unsigned int *)buffer = ioread32be(
+					host->base + REG_DATA_WINDOW);
+			} else {
+				*(unsigned int *)buffer =
+					readl(host->base + REG_DATA_WINDOW);
+			}
+			udelay(10);
+		}
+	}
+	wcnt <<= 2;
+	host->remain -= wcnt;
+	data->bytes_xfered += wcnt;
+
+	if (host->size != data->bytes_xfered)
+		moxart_next_sg(host);
+	else
+		complete(&host->pio_complete);
+
+transfer_complete:
+	dev_dbg(mmc_dev(host->mmc), "%s: host->size=%d host->remain=%u\n",
+		__func__, host->size, host->remain);
+}
+
+static void moxart_prepare_data(struct moxart_host *host)
+{
+	struct mmc_data *data = host->mrq->cmd->data;
+	unsigned int timeout, datactrl;
+	int blksz_bits;
+
+	dev_dbg(mmc_dev(host->mmc), "%s\n", __func__);
+
+	if (!data)
+		return;
+
+	host->size = data->blocks * data->blksz;
+	blksz_bits = ffs(data->blksz) - 1;
+	BUG_ON(1 << blksz_bits != data->blksz);
+
+	moxart_init_sg(host, data);
+
+	timeout = (host->mmc->f_max / 1000) * (data->timeout_ns / 1000);
+	timeout *= 2;
+
+	datactrl = (blksz_bits & MSD_BLK_SIZE_MASK) | MSD_DATA_EN;
+
+	if (data->flags & MMC_DATA_WRITE)
+		datactrl |= MSD_DATA_WRITE;
+
+	if (((host->size > MSD_FIFO_LENB) || DMA_FIFO_LEN_FORCE)
+	    && host->have_dma) {
+		datactrl |= MSD_DMA_EN;
+	}
+
+	dev_dbg(mmc_dev(host->mmc), "%s: blocks=%d blksz=%d datactrl=0x%08x\n",
+		__func__, data->blocks,	data->blksz, datactrl);
+	dev_dbg(mmc_dev(host->mmc), "%s: timeout=%u timeout_ns=%u\n",
+		__func__, timeout, data->timeout_ns);
+
+	writel(timeout, host->base + REG_DATA_TIMER);
+	writel(host->size, host->base + REG_DATA_LENGTH);
+	writel(datactrl, host->base + REG_DATA_CONTROL);
+}
+
+static void moxart_transfer_check(struct mmc_data *data,
+				  struct moxart_host *host)
+{
+	unsigned int status, count = 0;
+
+	dev_dbg(mmc_dev(host->mmc), "%s\n", __func__);
+
+	while (1) {
+		udelay(10);
+		status = readl(host->base + REG_STATUS);
+		if (status & (MSD_DATA_CRC_OK | MSD_DATA_CRC_FAIL
+			| MSD_DATA_END)	|| count > 10)
+			break;
+		dev_dbg(mmc_dev(host->mmc), "%s: waiting for status=%08x ..\n",
+			__func__, status);
+		count++;
+	}
+	if (status & MSD_DATA_CRC_OK) {
+		dev_dbg(mmc_dev(host->mmc), "%s: MSD_DATA_CRC_OK\n", __func__);
+		writel(MSD_DATA_CRC_OK, host->base + REG_CLEAR);
+	}
+	if (status & MSD_DATA_CRC_FAIL) {
+		dev_dbg(mmc_dev(host->mmc), "%s: MSD_DATA_CRC_FAIL\n",
+			__func__);
+		writel(MSD_DATA_CRC_FAIL, host->base + REG_CLEAR);
+		data->error = MMC_ERR_TIMEOUT;
+	}
+	if (status & MSD_DATA_END) {
+		dev_dbg(mmc_dev(host->mmc), "%s: MSD_DATA_END\n",
+			__func__);
+		writel(MSD_DATA_END, host->base + REG_CLEAR);
+	}
+	if (status & MSD_DATA_TIMEOUT) {
+		dev_dbg(mmc_dev(host->mmc), "%s: MSD_DATA_TIMEOUT\n",
+			__func__);
+		writel(MSD_DATA_TIMEOUT, host->base + REG_CLEAR);
+	}
+}
+
+static void moxart_card_change(struct moxart_host *host)
+{
+	int delay;
+	unsigned long flags;
+
+	spin_lock_irqsave(&host->lock, flags);
+
+	if (readl(host->base + REG_STATUS) & MSD_CARD_DETECT) {
+		dev_dbg(mmc_dev(host->mmc), "%s: card removed\n", __func__);
+		if (host->have_dma && host->size > MSD_FIFO_LENB) {
+			dev_dbg(mmc_dev(host->mmc), "%s: call dmaengine_terminate_all\n",
+				__func__);
+			dmaengine_terminate_all(host->dma_chan_rx);
+			dmaengine_terminate_all(host->dma_chan_tx);
+		}
+		host->removed = true;
+		delay = 0;
+	} else {
+		dev_dbg(mmc_dev(host->mmc), "%s: card inserted\n", __func__);
+		host->removed = false;
+		delay = 500;
+	}
+
+	/*
+	 * Clearing FIFO interrupts here does not stop a follow up
+	 * MSD_FIFO_*RUN after MSD_CARD_CHANGE.
+	 *
+	 * This is why moxart_irq() has a check (host->mrq != NULL),
+	 * as it avoids unnecessary calls to moxart_transfer_pio().
+	 *
+	 * If it's during transfer, the mmc_request pointer
+	 * in moxart_request() should still be valid
+	 * (which is why host->mrq can be set NULL here).
+	 */
+	host->mrq = NULL;
+	writel(MSD_CARD_CHANGE | MSD_FIFO_ORUN | MSD_FIFO_URUN,
+	       host->base + REG_CLEAR);
+	writel(MSD_CARD_CHANGE, host->base + REG_INTERRUPT_MASK);
+
+	spin_unlock_irqrestore(&host->lock, flags);
+	dev_dbg(mmc_dev(host->mmc), "%s: call mmc_detect_change\n", __func__);
+	mmc_detect_change(host->mmc, msecs_to_jiffies(delay));
+}
+
+static void moxart_request(struct mmc_host *mmc, struct mmc_request *mrq)
+{
+	struct moxart_host *host = mmc_priv(mmc);
+	unsigned long dma_time, pio_time, flags;
+	unsigned int status;
+
+	dev_dbg(mmc_dev(host->mmc), "%s\n", __func__);
+
+	spin_lock_irqsave(&host->lock, flags);
+
+	init_completion(&host->dma_complete);
+	init_completion(&host->pio_complete);
+
+	host->mrq = mrq;
+
+	if (readl(host->base + REG_STATUS) & MSD_CARD_DETECT) {
+		mrq->cmd->error = MMC_ERR_TIMEOUT;
+		goto request_done;
+	}
+
+	moxart_prepare_data(host);
+	moxart_send_command(host, host->mrq->cmd);
+
+	if (mrq->cmd->data) {
+		/* Only use DMA/PIO (and wait) when there's data. */
+		if (((host->size > MSD_FIFO_LENB) || DMA_FIFO_LEN_FORCE)
+			&& host->have_dma) {
+
+			writel(MSD_CARD_CHANGE, host->base +
+			       REG_INTERRUPT_MASK);
+
+			spin_unlock_irqrestore(&host->lock, flags);
+
+			host->dma_direction = (mrq->cmd->data->flags
+					      & MMC_DATA_WRITE) ?
+					      DMA_TO_DEVICE : DMA_FROM_DEVICE;
+			moxart_transfer_dma(mrq->cmd->data, host);
+
+			dma_time = wait_for_completion_interruptible_timeout(
+				   &host->dma_complete, host->timeout);
+			dev_dbg(mmc_dev(host->mmc), "%s: dma_time=%lu (DMA wait time)\n",
+				__func__, dma_time);
+
+			dma_unmap_sg(host->dma_chan_tx->device->dev,
+				     mrq->cmd->data->sg, mrq->cmd->data->sg_len,
+				     host->dma_direction);
+
+			spin_lock_irqsave(&host->lock, flags);
+		} else {
+
+			writel(MSD_FIFO_URUN | MSD_FIFO_ORUN | MSD_CARD_CHANGE,
+			       host->base + REG_INTERRUPT_MASK);
+
+			status = readl(host->base + REG_STATUS);
+			dev_dbg(mmc_dev(host->mmc), "%s: status=%08x\n",
+				__func__, status);
+
+			spin_unlock_irqrestore(&host->lock, flags);
+
+			/* PIO transfer will start from interrupt. */
+			pio_time = wait_for_completion_interruptible_timeout(
+				   &host->pio_complete, host->timeout);
+			dev_dbg(mmc_dev(host->mmc), "%s: pio_time=%lu (PIO wait time)\n",
+				__func__, pio_time);
+
+			spin_lock_irqsave(&host->lock, flags);
+		}
+
+		if (host->removed) {
+			dev_dbg(mmc_dev(host->mmc), "%s: host removed during transfer!\n",
+				__func__);
+			mrq->cmd->error = MMC_ERR_TIMEOUT;
+		}
+
+		moxart_transfer_check(mrq->cmd->data, host);
+
+		if (mrq->cmd->data->stop)
+			moxart_send_command(host, mrq->cmd->data->stop);
+	}
+
+request_done:
+	spin_unlock_irqrestore(&host->lock, flags);
+	mmc_request_done(host->mmc, mrq);
+}
+
+static irqreturn_t moxart_irq(int irq, void *devid)
+{
+	struct moxart_host *host = (struct moxart_host *)devid;
+	unsigned int status;
+	unsigned long flags;
+
+	status = readl(host->base + REG_STATUS);
+
+	dev_dbg(mmc_dev(host->mmc), "%s: host=%p status=%08x\n",
+		__func__, host, status);
+
+	if (status & MSD_CARD_CHANGE) {
+		dev_dbg(mmc_dev(host->mmc), "%s: call moxart_card_change\n",
+			__func__);
+		moxart_card_change(host);
+	}
+	if (status & (MSD_FIFO_ORUN | MSD_FIFO_URUN) && host->mrq) {
+		writel(status & (MSD_FIFO_ORUN | MSD_FIFO_URUN),
+		       host->base + REG_CLEAR);
+		dev_dbg(mmc_dev(host->mmc), "%s: call moxart_transfer_pio\n",
+			__func__);
+		spin_lock_irqsave(&host->lock, flags);
+		moxart_transfer_pio(host);
+		spin_unlock_irqrestore(&host->lock, flags);
+	}
+
+	return IRQ_HANDLED;
+}
+
+static void moxart_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
+{
+	struct moxart_host *host = mmc_priv(mmc);
+	unsigned long flags;
+	unsigned short power;
+	int div;
+
+	spin_lock_irqsave(&host->lock, flags);
+	if (ios->clock) {
+		div = (host->sysclk / (host->mmc->f_max * 2)) - 1;
+
+		if (div > MSD_CLK_DIV_MASK)
+			div = MSD_CLK_DIV_MASK;
+		else if (div < 0)
+			div = 0;
+
+		div |= MSD_CLK_SD;
+		writel(div, host->base + REG_CLOCK_CONTROL);
+	} else if (!(readl(host->base + REG_CLOCK_CONTROL) & MSD_CLK_DIS)) {
+		writel(readl(host->base + REG_CLOCK_CONTROL) | MSD_CLK_DIS,
+		       host->base + REG_CLOCK_CONTROL);
+	}
+
+	if (ios->power_mode == MMC_POWER_OFF) {
+		writel(readl(host->base + REG_POWER_CONTROL) & ~MSD_SD_POWER_ON,
+		       host->base + REG_POWER_CONTROL);
+	} else {
+		if (ios->vdd < MIN_POWER)
+			power = 0;
+		else
+			power = ios->vdd - MIN_POWER;
+
+		writel(MSD_SD_POWER_ON | (unsigned int) power,
+		       host->base + REG_POWER_CONTROL);
+	}
+
+	if (ios->bus_width == MMC_BUS_WIDTH_1)
+		writel(MSD_SINGLE_BUS, host->base + REG_BUS_WIDTH);
+	else
+		writel(MSD_WIDE_BUS, host->base + REG_BUS_WIDTH);
+
+	spin_unlock_irqrestore(&host->lock, flags);
+}
+
+
+static int moxart_get_ro(struct mmc_host *mmc)
+{
+	struct moxart_host *host = mmc_priv(mmc);
+
+	return !!(readl(host->base + REG_STATUS) & MSD_WRITE_PROT);
+}
+
+static struct mmc_host_ops moxart_ops = {
+	.request = moxart_request,
+	.set_ios = moxart_set_ios,
+	.get_ro = moxart_get_ro,
+};
+
+static int moxart_probe(struct platform_device *pdev)
+{
+	struct device *dev = &pdev->dev;
+	struct device_node *node = dev->of_node;
+	struct resource res_mmc;
+	struct mmc_host *mmc;
+	struct moxart_host *host = NULL;
+	void __iomem *reg_mmc;
+	dma_cap_mask_t mask;
+	int ret;
+	struct dma_slave_config cfg;
+	unsigned int irq;
+	struct clk *clk;
+
+	mmc = mmc_alloc_host(sizeof(struct moxart_host), dev);
+	if (!mmc) {
+		dev_err(dev, "%s: mmc_alloc_host failed\n", __func__);
+		ret = -ENOMEM;
+		goto out;
+	}
+
+	ret = of_address_to_resource(node, 0, &res_mmc);
+	if (ret) {
+		dev_err(dev, "%s: could not get MMC base resource\n", __func__);
+		goto out;
+	}
+
+	irq = irq_of_parse_and_map(node, 0);
+	if (irq <= 0) {
+		dev_err(dev, "irq_of_parse_and_map failed\n");
+		ret = -EINVAL;
+		goto out;
+	}
+
+	reg_mmc = devm_ioremap_resource(dev, &res_mmc);
+	if (IS_ERR(reg_mmc))
+		return PTR_ERR(reg_mmc);
+
+	mmc_of_parse(mmc);
+
+	mmc->ops = &moxart_ops;
+	mmc->f_min = 400000;
+	mmc->f_max = 25000000;
+	mmc->ocr_avail = 0xffff00;	/* Support 2.0v - 3.6v power. */
+	mmc->max_segs = 32;
+	mmc->max_blk_size = 512;
+	mmc->max_blk_count = mmc->max_req_size / mmc->max_blk_size;
+	mmc->max_seg_size = mmc->max_req_size;
+
+	host = mmc_priv(mmc);
+	host->mmc = mmc;
+	host->base = reg_mmc;
+	host->reg_phys = res_mmc.start;
+	host->timeout = msecs_to_jiffies(1000);
+
+	dma_cap_zero(mask);
+	dma_cap_set(DMA_SLAVE, mask);
+
+	clk = of_clk_get(node, 0);
+	if (IS_ERR(clk)) {
+		dev_err(dev, "%s: of_clk_get failed\n", __func__);
+		return PTR_ERR(clk);
+	}
+	host->sysclk = clk_get_rate(clk);
+
+	spin_lock_init(&host->lock);
+
+	/* Disable interrupts. */
+	writel(0, host->base + REG_INTERRUPT_MASK);
+
+	/* Reset chip. */
+	writel(MSD_SDC_RST, host->base + REG_COMMAND);
+
+	/* Wait for reset finished. */
+	while (readl(host->base + REG_COMMAND) & MSD_SDC_RST)
+		udelay(10);
+
+	host->dma_chan_tx = of_dma_request_slave_channel(node, "tx");
+	host->dma_chan_rx = of_dma_request_slave_channel(node, "rx");
+
+	if (!host->dma_chan_rx || !host->dma_chan_tx) {
+		dev_dbg(dev, "%s: using PIO mode transfer\n", __func__);
+
+		host->have_dma = false;
+		mmc->max_blk_count = 1;
+	} else {
+		dev_dbg(dev, "%s: using 2 DMA channels rx=%p tx=%p\n",
+			__func__, host->dma_chan_rx, host->dma_chan_tx);
+
+		cfg.src_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
+		cfg.dst_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
+
+		cfg.direction = DMA_MEM_TO_DEV;
+		cfg.src_addr = 0;
+		cfg.dst_addr = (unsigned int)host->reg_phys + REG_DATA_WINDOW;
+		dmaengine_slave_config(host->dma_chan_tx, &cfg);
+
+		cfg.direction = DMA_DEV_TO_MEM;
+		cfg.src_addr = (unsigned int)host->reg_phys + REG_DATA_WINDOW;
+		cfg.dst_addr = 0;
+		dmaengine_slave_config(host->dma_chan_rx, &cfg);
+
+		host->have_dma = true;
+		mmc->max_blk_count = 16;
+	}
+
+	ret = devm_request_irq(dev, irq, moxart_irq, 0, "moxart-mmc", host);
+
+	if (ret)
+		goto out;
+
+	dev_set_drvdata(dev, mmc);
+	mmc_add_host(mmc);
+
+	dev_dbg(dev, "%s: IRQ=%d\n", __func__, irq);
+
+	return 0;
+
+out:
+	if (mmc)
+		mmc_free_host(mmc);
+	return ret;
+}
+
+static void moxart_release_dma(struct moxart_host *host)
+{
+	if (host->dma_chan_tx) {
+		struct dma_chan *chan = host->dma_chan_tx;
+		host->dma_chan_tx = NULL;
+		dma_release_channel(chan);
+	}
+	if (host->dma_chan_rx) {
+		struct dma_chan *chan = host->dma_chan_rx;
+		host->dma_chan_rx = NULL;
+		dma_release_channel(chan);
+	}
+}
+
+static int moxart_remove(struct platform_device *pdev)
+{
+	struct mmc_host *mmc = dev_get_drvdata(&pdev->dev);
+	struct moxart_host *host = mmc_priv(mmc);
+
+	dev_set_drvdata(&pdev->dev, NULL);
+
+	if (mmc) {
+		moxart_release_dma(host);
+		mmc_remove_host(mmc);
+		mmc_free_host(mmc);
+
+		writel(0, host->base + REG_INTERRUPT_MASK);
+		writel(0, host->base + REG_POWER_CONTROL);
+		writel(readl(host->base + REG_CLOCK_CONTROL) | MSD_CLK_DIS,
+		       host->base + REG_CLOCK_CONTROL);
+	}
+
+	kfree(host);
+
+	return 0;
+}
+
+static const struct of_device_id moxart_mmc_match[] = {
+	{ .compatible = "moxa,moxart-mmc" },
+	{ }
+};
+
+static struct platform_driver moxart_mmc_driver = {
+	.probe      = moxart_probe,
+	.remove     = moxart_remove,
+	.driver     = {
+		.name		= "mmc-moxart",
+		.owner		= THIS_MODULE,
+		.of_match_table	= moxart_mmc_match,
+	},
+};
+module_platform_driver(moxart_mmc_driver);
+
+MODULE_ALIAS("platform:mmc-moxart");
+MODULE_DESCRIPTION("MOXART SDHCI driver");
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Jonas Jensen <jonas.jensen@gmail.com>");
-- 
1.8.2.1

^ permalink raw reply related

* [PATCH] clk: nomadik: fix multiplatform problem
From: Andrew Lunn @ 2014-01-21 10:52 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <1390291581-5858-1-git-send-email-linus.walleij@linaro.org>

On Tue, Jan 21, 2014 at 09:06:21AM +0100, Linus Walleij wrote:
> The Nomadik debugfs screws up multiplatform boots if debugfs
> is enabled on the multiplatform image, since it's a simple
> initcall that is unconditionally executed and reads from certain
> memory locations.
> 
> Fix this by checking that the driver has been properly
> initialized, so a base offset to the Nomadik SRC controller
> exists, before proceeding to register debugfs files.
> 
> Reported-by: Andrew Lunn <andrew@lunn.ch>
> Signed-off-by: Linus Walleij <linus.walleij@linaro.org>

Hi Linus

Thanks for fixing this so quick. I tested on my kirkwood, no longer
crashes.

Tested-by: Andrew Lunn <andrew@lunn.ch>

Thanks
	Andrew

> ---
>  drivers/clk/clk-nomadik.c | 3 +++
>  1 file changed, 3 insertions(+)
> 
> diff --git a/drivers/clk/clk-nomadik.c b/drivers/clk/clk-nomadik.c
> index 6a934a5296bd..05e04ce0f148 100644
> --- a/drivers/clk/clk-nomadik.c
> +++ b/drivers/clk/clk-nomadik.c
> @@ -494,6 +494,9 @@ static const struct file_operations nomadik_src_clk_debugfs_ops = {
>  
>  static int __init nomadik_src_clk_init_debugfs(void)
>  {
> +	/* Vital for multiplatform */
> +	if (!src_base)
> +		return -ENODEV;
>  	src_pcksr0_boot = readl(src_base + SRC_PCKSR0);
>  	src_pcksr1_boot = readl(src_base + SRC_PCKSR1);
>  	debugfs_create_file("nomadik-src-clk", S_IFREG | S_IRUGO,
> -- 
> 1.8.4.2
> 
> 
> _______________________________________________
> linux-arm-kernel mailing list
> linux-arm-kernel at lists.infradead.org
> http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

^ permalink raw reply

* [PATCH V2] ARM: imx: correct usecount of IPG, ARM and MMDC clk on i.mx6sl
From: Anson Huang @ 2014-01-21 10:50 UTC (permalink / raw)
  To: linux-arm-kernel

IPG, ARM and MMDC's clock should be enabled during kernel boot up,
so we need to maintain their use count, otherwise, they may be
disabled unexpectedly if their children's clock are turned off,
which is not allowed.

Signed-off-by: Anson Huang <b20788@freescale.com>
---
 arch/arm/mach-imx/clk-imx6sl.c |   16 ++++++++++++++++
 1 file changed, 16 insertions(+)

diff --git a/arch/arm/mach-imx/clk-imx6sl.c b/arch/arm/mach-imx/clk-imx6sl.c
index 78f3bd6..047f4ff 100644
--- a/arch/arm/mach-imx/clk-imx6sl.c
+++ b/arch/arm/mach-imx/clk-imx6sl.c
@@ -291,6 +291,22 @@ static void __init imx6sl_clocks_init(struct device_node *ccm_node)
 		pr_warn("%s: failed to set AHB clock rate %d!\n",
 			__func__, ret);
 
+	/*
+	 * Make sure those always on clocks are enabled to maintain the correct
+	 * usecount and enabling/disabling of parent PLLs.
+	 */
+	ret = clk_prepare_enable(clks[IMX6SL_CLK_IPG]);
+	if (ret)
+		pr_warn("%s: failed to enable IPG clock %d\n", __func__, ret);
+
+	ret = clk_prepare_enable(clks[IMX6SL_CLK_ARM]);
+	if (ret)
+		pr_warn("%s: failed to enable ARM clock %d\n", __func__, ret);
+
+	ret = clk_prepare_enable(clks[IMX6SL_CLK_MMDC_ROOT]);
+	if (ret)
+		pr_warn("%s: failed to enable MMDC clock %d\n", __func__, ret);
+
 	if (IS_ENABLED(CONFIG_USB_MXS_PHY)) {
 		clk_prepare_enable(clks[IMX6SL_CLK_USBPHY1_GATE]);
 		clk_prepare_enable(clks[IMX6SL_CLK_USBPHY2_GATE]);
-- 
1.7.9.5

^ permalink raw reply related

* [PATCH v2 01/15] ARM: Introduce atomic MMIO modify
From: Ezequiel Garcia @ 2014-01-21 10:46 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <3512865.jTEvHVvFLA@wuerfel>

On Tue, Jan 21, 2014 at 10:45:21AM +0100, Arnd Bergmann wrote:
> On Tuesday 21 January 2014 06:12:27 Ezequiel Garcia wrote:
> > Some SoC have MMIO regions that are shared across orthogonal
> > subsystems. This commit implements a possible solution for the
> > thread-safe access of such regions through a spinlock-protected API.
> > 
> > Concurrent access is protected with a single spinlock for the
> > entire MMIO address space. While this protects shared-registers,
> > it also serializes access to unrelated/unshared registers.
> > 
> > We add relaxed and non-relaxed variants, by using writel_relaxed and writel,
> > respectively. The rationale for this is that some users may not require
> > register write completion but only thread-safe access to a register.
> > 
> > Signed-off-by: Ezequiel Garcia <ezequiel.garcia@free-electrons.com>
> 
> You add the new atomic mmio interfaces in an ARM global header file,
> but at the same time make them ARM-only. I'm not opposed to having
> interfaces like that, but I'm not convinced they are actually needed
> for this case and if we go there, it needs to be done more carefully
> and should be available for all architectures so that portable drivers
> can use them.
> 

Yes, true. We've discussed about this and even post an arch-generic API:

http://lwn.net/Articles/564709/

In the end it was decided to keep it ARM-specific for now:

http://www.spinics.net/lists/arm-kernel/msg271773.html

Just for reference, the last posted patchset for the atomic I/O API
is this:

http://www.spinics.net/lists/arm-kernel/msg292775.html

> It also seems to duplicate functionality that is already present in
> regmap-mmio.
> 

Yes, this is true. We tried to use regmap-mmio/syscon but we need this
to initialize the clocksource which is too early for that to work:

https://www.mail-archive.com/linux-kernel at vger.kernel.org/msg484535.html

-- 
Ezequiel Garc?a, Free Electrons
Embedded Linux, Kernel and Android Engineering
http://free-electrons.com

^ permalink raw reply

* [PATCH v2 5/7] clk: exynos: use cpu-clock provider type to represent arm clock
From: Lukasz Majewski @ 2014-01-21 10:38 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <CAJuA9ajQWStgtFHDshM8PgRwizndzyTuRqvBNwh6GT4m-Exj6A@mail.gmail.com>

Hi Thomas,

> On Mon, Jan 20, 2014 at 1:17 PM, Lukasz Majewski
> <l.majewski@samsung.com> wrote:
> > Hi Thomas,
> >
> >> From: Thomas Abraham <thomas.ab@samsung.com>
> >>
> >> With the addition of the new Samsung specific cpu-clock type, the
> >> arm clock can be represented as a cpu-clock type and the
> >> independent clock blocks that made up the arm clock can be removed.
> >>
> >> Signed-off-by: Thomas Abraham <thomas.ab@samsung.com>
> >> ---
> >>  .../devicetree/bindings/clock/exynos5250-clock.txt |    1 +
> >>  drivers/clk/samsung/clk-exynos4.c                  |   11
> >> +++++------ drivers/clk/samsung/clk-exynos5250.c
> >> |    8 ++++---- include/dt-bindings/clock/exynos5250.h
> >> |    1 + 4 files changed, 11 insertions(+), 10 deletions(-)
> >>
> >> diff --git
> >> a/Documentation/devicetree/bindings/clock/exynos5250-clock.txt
> >> b/Documentation/devicetree/bindings/clock/exynos5250-clock.txt
> >> index 99eae9c..acf867a 100644 ---
> >> a/Documentation/devicetree/bindings/clock/exynos5250-clock.txt +++
> >> b/Documentation/devicetree/bindings/clock/exynos5250-clock.txt @@
> >> -38,6 +38,7 @@ clock which they consume.
> >> ---------------------------- fin_pll            1
> >> +  armclk             12
> >>
> >>    [Clock Gate for Special Clocks]
> >>
> >> diff --git a/drivers/clk/samsung/clk-exynos4.c
> >> b/drivers/clk/samsung/clk-exynos4.c index 010f071..efcf4a3 100644
> >> --- a/drivers/clk/samsung/clk-exynos4.c
> >> +++ b/drivers/clk/samsung/clk-exynos4.c
> >> @@ -437,8 +437,6 @@ static struct samsung_mux_clock
> >> exynos4x12_mux_clks[] __initdata = {
> >>  /* list of divider clocks supported in all exynos4 soc's */
> >>  static struct samsung_div_clock exynos4_div_clks[] __initdata = {
> >> -     DIV(0, "div_core", "mout_core", DIV_CPU0, 0, 3),
> >> -     DIV(0, "div_core2", "div_core", DIV_CPU0, 28, 3),
> >>       DIV(0, "div_fimc0", "mout_fimc0", DIV_CAM, 0, 4),
> >>       DIV(0, "div_fimc1", "mout_fimc1", DIV_CAM, 4, 4),
> >>       DIV(0, "div_fimc2", "mout_fimc2", DIV_CAM, 8, 4),
> >> @@ -484,8 +482,8 @@ static struct samsung_div_clock
> >> exynos4_div_clks[] __initdata = { DIV(0, "div_spi_pre2",
> >> "div_spi2", DIV_PERIL2, 8, 8), DIV(0, "div_audio1", "mout_audio1",
> >> DIV_PERIL4, 0, 4), DIV(0, "div_audio2", "mout_audio2", DIV_PERIL4,
> >> 16, 4),
> >> -     DIV(CLK_ARM_CLK, "arm_clk", "div_core2", DIV_CPU0, 28, 3),
> >> -     DIV(CLK_SCLK_APLL, "sclk_apll", "mout_apll", DIV_CPU0, 24,
> >> 3),
> >> +     DIV_F(CLK_SCLK_APLL, "sclk_apll", "mout_apll", DIV_CPU0, 24,
> >> 3,
> >> +                     CLK_GET_RATE_NOCACHE, 0),
> >>       DIV_F(0, "div_mipi_pre0", "div_mipi0", DIV_LCD0, 20, 4,
> >>                       CLK_SET_RATE_PARENT, 0),
> >>       DIV_F(0, "div_mmc_pre0", "div_mmc0", DIV_FSYS1, 8, 8,
> >> @@ -870,7 +868,6 @@ static struct samsung_gate_clock
> >> exynos4x12_gate_clks[] __initdata = {
> >>  static struct samsung_clock_alias exynos4_aliases[] __initdata = {
> >>       ALIAS(CLK_MOUT_CORE, NULL, "moutcore"),
> >> -     ALIAS(CLK_ARM_CLK, NULL, "armclk"),
> >>       ALIAS(CLK_SCLK_APLL, NULL, "mout_apll"),
> >>  };
> >>
> >> @@ -1125,12 +1122,14 @@ static void __init exynos4_clk_init(struct
> >> device_node *np, samsung_clk_register_alias(exynos4_aliases,
> >>                       ARRAY_SIZE(exynos4_aliases));
> >>
> >> +     samsung_register_arm_clock(np, CLK_ARM_CLK, "mout_apll",
> >> reg_base); +
> >
> > I've got some doubts about allowing only the "mout_apll" clock to
> > be the only parent for armclk Samsung clock.
> >
> > For the Exynos4412 it is also valid to have SCLK_MPLL_USER_C [*] as
> > a parent for this clock.
> >
> > The problem is that you are reparenting the armclk to [*] with the
> > register modification - no CCF involved.
> 
> The MUX_CORE mux is part of the larger cpu clock type and not
> registered as a separate mux clock with CCF. So I would like to know
> if there are any potential issues you see if this mux clock is
> internally managed within the set_rate of the larger cpu clock type.

So the large opaque clock (armclk) starts with inputs to MUX_CORE (and
embrace this MUX as well)?

If yes, then I don't mind if it is solely managed inside the armclk.

This however allows the armclk to be fed from mout_apll[*] and
sclk_mpll__user_c[**]. And this would be nice if reflected in the code.

> 
> >
> > I just would like to know if this is yours design decision or
> > something, that we have overlooked in the v1 of this patch series.
> 
> I did overlook this one. This will be fixed in the next version by
> ensuring that the dividers for SCLK_HPM clock will be updated only if
> mout_apll is the parent of the MUX_HPM clock mux.

Shall not they be updated if whatever connected to the MUX_HPM
([*] and [**]) changes?

> 
> Thanks,
> Thomas.
> 
> >
> >>       pr_info("%s clocks: sclk_apll = %ld, sclk_mpll = %ld\n"
> >>               "\tsclk_epll = %ld, sclk_vpll = %ld, arm_clk =
> >> %ld\n", exynos4_soc == EXYNOS4210 ? "Exynos4210" : "Exynos4x12",
> >>               _get_rate("sclk_apll"),
> >> _get_rate("sclk_mpll"), _get_rate("sclk_epll"),
> >> _get_rate("sclk_vpll"),
> >> -             _get_rate("arm_clk"));
> >> +             _get_rate("armclk"));
> >>  }
> >>
> >>
> >> diff --git a/drivers/clk/samsung/clk-exynos5250.c
> >> b/drivers/clk/samsung/clk-exynos5250.c index ff4beeb..487be36
> >> 100644 --- a/drivers/clk/samsung/clk-exynos5250.c
> >> +++ b/drivers/clk/samsung/clk-exynos5250.c
> >> @@ -298,9 +298,8 @@ static struct samsung_div_clock
> >> exynos5250_div_clks[] __initdata = { /*
> >>        * CMU_CPU
> >>        */
> >> -     DIV(0, "div_arm", "mout_cpu", DIV_CPU0, 0, 3),
> >> -     DIV(0, "div_apll", "mout_apll", DIV_CPU0, 24, 3),
> >> -     DIV_A(0, "div_arm2", "div_arm", DIV_CPU0, 28, 3, "armclk"),
> >> +     DIV_F(0, "div_apll", "mout_apll", DIV_CPU0, 24, 3,
> >> +                     CLK_GET_RATE_NOCACHE, 0),
> >>
> >>       /*
> >>        * CMU_TOP
> >> @@ -684,8 +683,9 @@ static void __init exynos5250_clk_init(struct
> >> device_node *np) ARRAY_SIZE(exynos5250_div_clks));
> >>       samsung_clk_register_gate(exynos5250_gate_clks,
> >>                       ARRAY_SIZE(exynos5250_gate_clks));
> >> +     samsung_register_arm_clock(np, CLK_ARM_CLK, "mout_apll",
> >> reg_base);
> >>       pr_info("Exynos5250: clock setup completed, armclk=%ld\n",
> >> -                     _get_rate("div_arm2"));
> >> +                     _get_rate("armclk"));
> >>  }
> >>  CLK_OF_DECLARE(exynos5250_clk, "samsung,exynos5250-clock",
> >> exynos5250_clk_init); diff --git
> >> a/include/dt-bindings/clock/exynos5250.h
> >> b/include/dt-bindings/clock/exynos5250.h index 922f2dc..59a10fb
> >> 100644 --- a/include/dt-bindings/clock/exynos5250.h +++
> >> b/include/dt-bindings/clock/exynos5250.h @@ -21,6 +21,7 @@
> >>  #define CLK_FOUT_CPLL                6
> >>  #define CLK_FOUT_EPLL                7
> >>  #define CLK_FOUT_VPLL                8
> >> +#define CLK_ARM_CLK          12
> >>
> >>  /* gate for special clocks (sclk) */
> >>  #define CLK_SCLK_CAM_BAYER   128
> >
> >
> >
> > --
> > Best regards,
> >
> > Lukasz Majewski
> >
> > Samsung R&D Institute Poland (SRPOL) | Linux Platform Group
> 
> _______________________________________________
> linux-arm-kernel mailing list
> linux-arm-kernel at lists.infradead.org
> http://lists.infradead.org/mailman/listinfo/linux-arm-kernel



-- 
Best regards,

Lukasz Majewski

Samsung R&D Institute Poland (SRPOL) | Linux Platform Group

^ permalink raw reply

* [PATCH v2 2/7] clk: samsung: add infrastructure to register cpu clocks
From: Thomas Abraham @ 2014-01-21 10:38 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <20140121112548.247fca7f@amdc2363>

On Tue, Jan 21, 2014 at 3:55 PM, Lukasz Majewski <l.majewski@samsung.com> wrote:
> Hi Thomas,
>
>> Hi Lukasz,
>>
>> On Mon, Jan 20, 2014 at 1:54 PM, Lukasz Majewski
>> <l.majewski@samsung.com> wrote:
>> > Hi Thomas,
>> >
>> >> From: Thomas Abraham <thomas.ab@samsung.com>
>> >>
>> >> The CPU clock provider supplies the clock to the CPU clock domain.
>> >> The composition and organization of the CPU clock provider could
>> >> vary among Exynos SoCs. A CPU clock provider can be composed of
>> >> clock mux, dividers and gates. This patch defines a new clock type
>> >> for CPU clock provider and adds infrastructure to register the CPU
>> >> clock providers for Samsung platforms.
>> >>
>> >> In addition to this, the arm cpu clock provider for Exynos4210 and
>> >> compatible SoCs is instantiated using the new cpu clock type. The
>> >> clock frequency table and the clock configuration data for this
>> >> clock is obtained from device tree. This implementation is
>> >> reusable for Exynos4x12 and Exynos5250 SoCs as well.
>> >>
>> >> Cc: Tomasz Figa <t.figa@samsung.com>
>> >> Cc: Lukasz Majewski <l.majewski@majess.pl>
>> >> Signed-off-by: Thomas Abraham <thomas.ab@samsung.com>
>> >> ---
>> >>  drivers/clk/samsung/Makefile  |    2 +-
>> >>  drivers/clk/samsung/clk-cpu.c |  345
>> >> +++++++++++++++++++++++++++++++++++++++++
>> >> drivers/clk/samsung/clk.h     |    3 + 3 files changed, 349
>> >> insertions(+), 1 deletions(-) create mode 100644
>> >> drivers/clk/samsung/clk-cpu.c
>> >>
>> >> diff --git a/drivers/clk/samsung/Makefile
>> >> b/drivers/clk/samsung/Makefile index 8eb4799..e2b453f 100644
>> >> --- a/drivers/clk/samsung/Makefile
>> >> +++ b/drivers/clk/samsung/Makefile
>> >> @@ -2,7 +2,7 @@
>> >>  # Samsung Clock specific Makefile
>> >>  #
>> >>
>> >> -obj-$(CONFIG_COMMON_CLK)     += clk.o clk-pll.o
>> >> +obj-$(CONFIG_COMMON_CLK)     += clk.o clk-pll.o clk-cpu.o
>> >>  obj-$(CONFIG_ARCH_EXYNOS4)   += clk-exynos4.o
>> >>  obj-$(CONFIG_SOC_EXYNOS5250) += clk-exynos5250.o
>> >>  obj-$(CONFIG_SOC_EXYNOS5420) += clk-exynos5420.o
>> >> diff --git a/drivers/clk/samsung/clk-cpu.c
>> >> b/drivers/clk/samsung/clk-cpu.c new file mode 100644
>> >> index 0000000..92fba45
>> >> --- /dev/null
>> >> +++ b/drivers/clk/samsung/clk-cpu.c
>> >> @@ -0,0 +1,345 @@
>> >> +/*
>> >> + * Copyright (c) 2014 Samsung Electronics Co., Ltd.
>> >> + * Author: Thomas Abraham <thomas.ab@samsung.com>
>> >> + *
>> >> + * This program is free software; you can redistribute it and/or
>> >> modify
>> >> + * it under the terms of the GNU General Public License version 2
>> >> as
>> >> + * published by the Free Software Foundation.
>> >> + *
>> >> + * This file contains the utility functions to register the cpu
>> >> clocks
>> >> + * for samsung platforms.
>> >> +*/
>> >> +
>> >> +#include <linux/errno.h>
>> >> +#include "clk.h"
>> >> +
>> >> +#define SRC_CPU                      0x0
>> >> +#define STAT_CPU             0x200
>> >> +#define DIV_CPU0             0x300
>> >> +#define DIV_CPU1             0x304
>> >> +#define DIV_STAT_CPU0                0x400
>> >> +#define DIV_STAT_CPU1                0x404
>> >> +
>> >> +/**
>> >> + * struct samsung_cpuclk_freq_table: table of frequency supported
>> >> by
>> >> + * a cpu clock and associated data if any.
>> >> + * @freq: points to a table of supported frequencies (in KHz)
>> >> + * @freq_count: number of entries in the frequency table
>> >> + * @data: cpu clock specific data, if any
>> >> + *
>> >> + * This structure holds the frequency options supported by the cpu
>> >> clock in
>> >> + * which this structure is contained. The data pointer is an
>> >> optional data
>> >> + * that can provide any additional configuration options for the
>> >> supported
>> >> + * frequencies. This structure is intended to be reusable for all
>> >> cpu clocks
>> >> + * in Samsung SoC based platforms
>> >> + */
>> >> +struct samsung_cpuclk_freq_table {
>> >> +     const unsigned long     *freq;       /* in KHz */
>> >> +     unsigned long           freq_count;
>> >> +     const void              *data;
>> >> +};
>> >> +
>> >> +/**
>> >> + * struct exynos4210_freq_data: format of auxillary data
>> >> associated with
>> >> + * each frequency supported by the cpu clock for exynos4210.
>> >> + * @parent_freq: The frequency of the parent clock required to
>> >> generate the
>> >> + * supported cpu clock speed.
>> >> + * @div0: value to be programmed in the div_cpu0 register.
>> >> + * @div1: value to be programmed in the div_cpu1 register.
>> >> + *
>> >> + * This structure holds the auxillary configuration data for each
>> >> supported
>> >> + * cpu clock frequency on Exynos4210 and compatible SoCs.
>> >> + */
>> >> +struct exynos4210_freq_data {
>> >> +     unsigned long   parent_freq;
>> >> +     unsigned int    div0;
>> >> +     unsigned int    div1;
>> >> +};
>> >> +
>> >> +/**
>> >> + * struct samsung_cpuclk: information about clock supplied to a
>> >> CPU core.
>> >> + * @hw: handle between ccf and cpu clock.
>> >> + * @ctrl_base: base address of the clock controller.
>> >> + * @offset: offset from the ctrl_base address where the cpu clock
>> >> div/mux
>> >> + *          registers can be accessed.
>> >> + * @parent: clock handle representing the clock output of the
>> >> parent clock.
>> >> + * @freq_table: the frequency table supported by this cpu clock.
>> >> + */
>> >> +struct samsung_cpuclk {
>> >> +     struct clk_hw           hw;
>> >> +     void __iomem            *ctrl_base;
>> >> +     unsigned long           offset;
>> >> +     struct clk              *parent;
>> >> +     const struct samsung_cpuclk_freq_table *freq_table;
>> >> +};
>> >> +
>> >> +#define to_samsung_cpuclk(hw)        container_of(hw, struct
>> >> samsung_cpuclk, hw) +
>> >> +/**
>> >> + * struct samsung_cpuclk_match_data: soc specific data for cpu
>> >> clocks.
>> >> + * @parser: pointer to a function that can parse SoC specific cpu
>> >> clock
>> >> + *   frequency and associated configuration data.
>> >> + * @offset: optional offset from base of clock controller register
>> >> base,
>> >> + *   to be used when accessing clock controller registers
>> >> related to the
>> >> + * cpu clock.
>> >> + * @offset: offset from the ctrl_base address where the cpu clock
>> >> div/mux
>> >> + *   registers can be accessed.
>> >> + */
>> >> +struct samsung_cpuclk_match_data {
>> >> +     int (*parser)(struct device_node *,
>> >> +                     struct samsung_cpuclk_freq_table **);
>> >> +     unsigned int offset;
>> >> +};
>> >> +
>> >> +/* This is a helper function to perform clock rounding for cpu
>> >> clocks. */ +static long samsung_cpuclk_round_rate(struct clk_hw
>> >> *hw,
>> >> +                     unsigned long drate, unsigned long *prate)
>> >> +{
>> >> +     struct samsung_cpuclk *cpuclk = to_samsung_cpuclk(hw);
>> >> +     const struct samsung_cpuclk_freq_table *freq_tbl;
>> >> +     int i;
>> >> +
>> >> +     freq_tbl = cpuclk->freq_table;
>> >> +     drate /= 1000;
>> >> +
>> >> +     for (i = 0; i < freq_tbl->freq_count; i++) {
>> >> +             if (drate >= freq_tbl->freq[i])
>> >> +                     return freq_tbl->freq[i] * 1000;
>> >> +     }
>> >> +     return freq_tbl->freq[i - 1] * 1000;
>> >> +}
>> >> +
>> >> +#define EXYNOS4210_ARM_DIV1(base) ((readl(base + DIV_CPU0) & 0xf)
>> >> + 1) +#define EXYNOS4210_ARM_DIV2(base) (((readl(base + DIV_CPU0)
>> >> >> 28) & 0xf) + 1) +
>> >> +/*
>> >> + * CPU clock speed for Exynos4210 and compatible SoCs is
>> >> + * parent clock speed / core1_ratio / core2_ratio
>> >> + */
>> >> +static unsigned long exynos4210_armclk_recalc_rate(struct clk_hw
>> >> *hw,
>> >> +                             unsigned long parent_rate)
>> >> +{
>> >> +     struct samsung_cpuclk *armclk = to_samsung_cpuclk(hw);
>> >> +     void __iomem *base = armclk->ctrl_base + armclk->offset;
>> >> +
>> >> +     return parent_rate / EXYNOS4210_ARM_DIV1(base) /
>> >> +                             EXYNOS4210_ARM_DIV2(base);
>> >> +}
>> >> +
>> >> +/* set rate callback for cpuclk type on Exynos4210 and similar
>> >> SoCs */ +static int exynos4210_armclk_set_rate(struct clk_hw *hw,
>> >> unsigned long drate,
>> >> +                                     unsigned long prate)
>> >> +{
>> >> +     struct samsung_cpuclk *armclk = to_samsung_cpuclk(hw);
>> >> +     const struct samsung_cpuclk_freq_table *freq_tbl;
>> >> +     const struct exynos4210_freq_data *freq_data;
>> >> +     unsigned long mux_reg, idx;
>> >> +     void __iomem *base;
>> >> +
>> >> +     if (drate == prate)
>> >> +             return 0;
>> >> +
>> >> +     freq_tbl = armclk->freq_table;
>> >> +     freq_data = freq_tbl->data;
>> >> +     base = armclk->ctrl_base + armclk->offset;
>> >> +
>> >> +     for (idx = 0; idx < freq_tbl->freq_count; idx++, freq_data++)
>> >> +             if ((freq_tbl->freq[idx] * 1000) == drate)
>> >> +                     break;
>> >> +
>> >> +     if (drate < prate) {
>> >> +             mux_reg = readl(base + SRC_CPU);
>> >> +             writel(mux_reg | (1 << 16), base + SRC_CPU);
>> >> +             while (((readl(base + STAT_CPU) >> 16) & 0x7) != 2)
>> >> +                     ;
>> >> +
>> >> +             clk_set_rate(armclk->parent, drate);
>> >> +     }
>> >> +
>> >> +     writel(freq_data->div0, base + DIV_CPU0);
>> >> +     while (readl(base + DIV_STAT_CPU0) != 0)
>> >> +             ;
>> >> +     writel(freq_data->div1, base + DIV_CPU1);
>> >> +     while (readl(base + DIV_STAT_CPU1) != 0)
>> >> +             ;
>> >> +
>> >> +     if (drate > prate) {
>> >> +             mux_reg = readl(base + SRC_CPU);
>> >> +             writel(mux_reg | (1 << 16), base + SRC_CPU);
>> >> +             while (((readl(base + STAT_CPU) >> 16) & 0x7) != 2)
>> >> +                     ;
>> >> +
>> >> +             clk_set_rate(armclk->parent, freq_data->parent_freq
>> >> * 1000);
>> >> +     }
>> >> +
>> >> +     mux_reg = readl(base + SRC_CPU);
>> >> +     writel(mux_reg & ~(1 << 16), base + SRC_CPU);
>> >> +     while (((readl(base + STAT_CPU) >> 16) & 0x7) != 1)
>> >> +                     ;
>> >> +     return 0;
>> >> +}
>> >> +
>> >> +/* clock ops for armclk on Exynos4210 and compatible platforms. */
>> >> +static const struct clk_ops exynos4210_armclk_clk_ops = {
>> >> +     .recalc_rate = exynos4210_armclk_recalc_rate,
>> >> +     .round_rate = samsung_cpuclk_round_rate,
>> >> +     .set_rate = exynos4210_armclk_set_rate,
>> >> +};
>> >> +
>> >> +/* helper function to register a cpu clock */
>> >> +static void __init samsung_cpuclk_register(unsigned int lookup_id,
>> >> +             const char *name, const char *parent, const struct
>> >> clk_ops *ops,
>> >> +             const struct samsung_cpuclk_freq_table *freq_tbl,
>> >> +             void __iomem *reg_base,
>> >> +             const struct samsung_cpuclk_match_data *data)
>> >> +{
>> >> +     struct samsung_cpuclk *cpuclk;
>> >> +     struct clk_init_data init;
>> >> +     struct clk *clk;
>> >> +
>> >> +     cpuclk = kzalloc(sizeof(*cpuclk), GFP_KERNEL);
>> >> +     if (!cpuclk) {
>> >> +             pr_err("%s: could not allocate memory for cpuclk
>> >> %s\n",
>> >> +                                     __func__, name);
>> >> +             return;
>> >> +     }
>> >> +
>> >> +     init.name = name;
>> >> +     init.flags = CLK_GET_RATE_NOCACHE;
>> >> +     init.parent_names = &parent;
>> >> +     init.num_parents = 1;
>> >> +     init.ops = ops;
>> >> +
>> >> +     cpuclk->hw.init = &init;
>> >> +     cpuclk->ctrl_base = reg_base;
>> >> +     cpuclk->offset = data->offset;
>> >> +     cpuclk->freq_table = freq_tbl;
>> >> +     cpuclk->parent = __clk_lookup(parent);
>> >> +
>> >> +     clk = clk_register(NULL, &cpuclk->hw);
>> >> +     if (IS_ERR(clk)) {
>> >> +             pr_err("%s: could not register cpuclk %s\n",
>> >> __func__,     name);
>> >> +             kfree(cpuclk);
>> >> +             return;
>> >> +     }
>> >> +     samsung_clk_add_lookup(clk, lookup_id);
>> >> +}
>> >> +
>> >> +#define EXYNOS4210_DIV_CPU01(d0, d1, d2, d3, d4, d5, d6,
>> >> d7)           \
>> >> +             ((d0 << 28) | (d1 << 24) | (d2 << 20) | (d3
>> >> << 16) |      \
>> >> +              (d4 << 12) | (d5 << 8) | (d6 << 4) | (d7 << 0))
>> >> +#define EXYNOS4210_DIV_CPU11(d0, d1,
>> >> d2)                           \
>> >> +             ((d0 << 8) | (d1 << 4) | (d2 << 0))
>> >> +#define EXYNOS4210_CFG_LEN 13
>> >> +
>> >> +/*
>> >> + * parse cpu clock frequency table and auxillary configuration
>> >> data from dt
>> >> + * for exynos4210 and compatible SoC's.
>> >> + */
>> >> +static int exynos4210_armclk_cfg_parser(struct device_node *np,
>> >> +             struct samsung_cpuclk_freq_table **tbl)
>> >> +{
>> >> +     struct samsung_cpuclk_freq_table *freq_tbl;
>> >> +     struct exynos4210_freq_data *fdata, *t_fdata;
>> >> +     unsigned long *freqs, cfg[EXYNOS4210_CFG_LEN];
>> >> +     const struct property *prop;
>> >> +     unsigned int tbl_sz, i, j;
>> >> +     const __be32 *val;
>> >> +     int ret;
>> >> +
>> >> +     prop = of_find_property(np, "arm-frequency-table", NULL);
>> >> +     if (!prop)
>> >> +             return -EINVAL;
>> >> +     if (!prop->value)
>> >> +             return -EINVAL;
>> >> +     if ((prop->length / sizeof(u32)) % EXYNOS4210_CFG_LEN)
>> >
>> > Cannot we have the EXYNOS4210_CFG_LEN parsed from DT as well?
>>
>> As per Rob's suggestion, the clock divider ration table will be
>> removed. So this portion of the code will be reworked.
>
> Ok. Thanks.
>
>>
>> >
>> >> +             return -EINVAL;
>> >> +     tbl_sz = (prop->length / sizeof(u32)) / EXYNOS4210_CFG_LEN;
>> >> +
>> >> +     freq_tbl = kzalloc(sizeof(*freq_tbl), GFP_KERNEL);
>> >> +     if (!freq_tbl)
>> >> +             return -ENOMEM;
>> >> +
>> >> +     freqs = kzalloc(sizeof(u32) * tbl_sz, GFP_KERNEL);
>> >> +     if (!freqs) {
>> >> +             ret = -ENOMEM;
>> >> +             goto free_freq_tbl;
>> >> +     }
>> >> +
>> >> +     fdata = kzalloc(sizeof(*fdata) * tbl_sz, GFP_KERNEL);
>> >> +     if (!fdata) {
>> >> +             ret = -ENOMEM;
>> >> +             goto free_freqs;
>> >> +     }
>> >> +     t_fdata = fdata;
>> >> +
>> >> +     val = prop->value;
>> >> +     for (i = 0; i < tbl_sz; i++, fdata++) {
>> >> +             for (j = 0; j < EXYNOS4210_CFG_LEN; j++)
>> >> +                     cfg[j] = be32_to_cpup(val++);
>> >> +             freqs[i] = cfg[0];
>> >> +             fdata->parent_freq = cfg[1];
>> >
>> > Why do we need the separate parent_freq entry here?
>> >
>> > In the patch 4/7 the freqs (cfg[0]) and parent_freq (cfg[1]) values
>> > are the same for all supported devices (at "arm-frequency-table").
>> >
>> > What is the rationale for having those values duplicated in the DT?
>>
>> The intention was to support frequencies which may not be direct
>> output of the parent PLL clock. For instance, if the PLL supports
>> 200MHz, 400MHz and 600MHz and the CPU clock needs to be set to 300MHz,
>> then 600MHz / 2 is a valid clock output for the cpu clock. So this is
>> an example where the cpu clock speed is different from its parent
>> clock speed.
>
> But shall not this case been handled by CCF internally?

The divider which actually can divide the PLL clock output is now part
of the larger cpu clock type. So it is the implementation of the
set_rate function of the cpu clock that has to handle this division.
So there is no core CCF support to handle this.

>
> Is there any Samsung PLL clock, which has such property?

Yes, this functionality is available on all Exynos SoCs but it is not
a Samsung PLL clock property. The PLL clock output can optionally be
divided and then used as arm clock output.

>
>> If possible, I will try and remove the need for this table in the
>> next version.
>
> Ok.

On second thoughts, it looks possible to get rid of this table and I
am testing with this approach now.

>
>>
>> Thanks,
>> Thomas.
>>
>> >
>> >
>> >> +             fdata->div0 = EXYNOS4210_DIV_CPU01(cfg[9], cfg[8],
>> >> cfg[7],
>> >> +                             cfg[6], cfg[5], cfg[4], cfg[3],
>> >> cfg[2]);
>> >> +             fdata->div1 = EXYNOS4210_DIV_CPU11(cfg[12], cfg[11],
>> >> cfg[10]);
>> >> +     }
>> >> +
>> >> +     freq_tbl->freq = freqs;
>> >> +     freq_tbl->freq_count = tbl_sz;
>> >> +     freq_tbl->data = t_fdata;
>> >> +     *tbl = freq_tbl;
>> >> +     return 0;
>> >> +
>> >> +free_freqs:
>> >> +     kfree(freqs);
>> >> +free_freq_tbl:
>> >> +     kfree(freq_tbl);
>> >> +     return ret;
>> >> +}
>> >> +
>> >> +static struct samsung_cpuclk_match_data
>> >> exynos4210_cpuclk_match_data = {
>> >> +     .parser = exynos4210_armclk_cfg_parser,
>> >> +     .offset = 0x14200,
>> >> +};
>> >> +
>> >> +static struct samsung_cpuclk_match_data
>> >> exynos5250_cpuclk_match_data = {
>> >> +     .parser = exynos4210_armclk_cfg_parser,
>> >> +     .offset = 0x200,
>> >> +};
>> >> +
>> >> +static const struct of_device_id samsung_clock_ids[] = {
>> >> +     { .compatible = "samsung,exynos4210-clock",
>> >> +                     .data = &exynos4210_cpuclk_match_data, },
>> >> +     { .compatible = "samsung,exynos4412-clock",
>> >> +                     .data = &exynos4210_cpuclk_match_data, },
>> >> +     { .compatible = "samsung,exynos5250-clock",
>> >> +                     .data = &exynos5250_cpuclk_match_data, },
>> >> +};
>> >> +
>> >> +int __init samsung_register_arm_clock(struct device_node *np,
>> >> +             unsigned int lookup_id, const char *parent,
>> >> void __iomem *base) +{
>> >> +     const struct of_device_id *match;
>> >> +     struct samsung_cpuclk_freq_table *freq_table;
>> >> +     const struct samsung_cpuclk_match_data *data;
>> >> +     int ret;
>> >> +
>> >> +     match = of_match_node(samsung_clock_ids, np);
>> >> +     if (!match) {
>> >> +             pr_err("%s: could not determine soc type\n",
>> >> __func__);
>> >> +             return -EINVAL;
>> >> +     }
>> >> +
>> >> +     data = match->data;
>> >> +     ret = data->parser(np, &freq_table);
>> >> +     if (ret) {
>> >> +             pr_err("%s: error %d in parsing arm clock freq
>> >> table",
>> >> +                                             __func__, ret);
>> >> +             return -EINVAL;
>> >> +     }
>> >> +
>> >> +     samsung_cpuclk_register(lookup_id, "armclk", parent,
>> >> +             &exynos4210_armclk_clk_ops, freq_table, base, data);
>> >> +
>> >> +     return 0;
>> >> +}
>> >> diff --git a/drivers/clk/samsung/clk.h b/drivers/clk/samsung/clk.h
>> >> index 31b4174..a759330 100644
>> >> --- a/drivers/clk/samsung/clk.h
>> >> +++ b/drivers/clk/samsung/clk.h
>> >> @@ -340,4 +340,7 @@ extern void __init
>> >> samsung_clk_register_pll(struct samsung_pll_clock *pll_list,
>> >>  extern unsigned long _get_rate(const char *clk_name);
>> >>
>> >> +extern int __init samsung_register_arm_clock(struct device_node
>> >> *np,
>> >> +             unsigned int lookup_id, const char *parent, void
>> >> __iomem *base); +
>> >>  #endif /* __SAMSUNG_CLK_H */
>> >
>> >
>> >
>> > --
>> > Best regards,
>> >
>> > Lukasz Majewski
>> >
>> > Samsung R&D Institute Poland (SRPOL) | Linux Platform Group
>
> --
> Best regards,
>
> Lukasz Majewski
>
> Samsung R&D Institute Poland (SRPOL) | Linux Platform Group

^ permalink raw reply

* [PATCH 2/2] tty: uartps: Get rid of Xilinx PS name
From: Michal Simek @ 2014-01-21 10:33 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <7d0c02bf2d7eb816eb505d04c3800112805e2b62.1390300383.git.michal.simek@xilinx.com>

Use zynq name instead.

Signed-off-by: Michal Simek <michal.simek@xilinx.com>
---
 drivers/tty/serial/xilinx_uartps.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/drivers/tty/serial/xilinx_uartps.c b/drivers/tty/serial/xilinx_uartps.c
index b2f929e..528f16a 100644
--- a/drivers/tty/serial/xilinx_uartps.c
+++ b/drivers/tty/serial/xilinx_uartps.c
@@ -1,5 +1,5 @@
 /*
- * Xilinx PS UART driver
+ * Xilinx Zynq UART driver
  *
  * 2011 - 2013 (C) Xilinx Inc.
  *
--
1.8.2.3

-------------- next part --------------
A non-text attachment was scrubbed...
Name: not available
Type: application/pgp-signature
Size: 198 bytes
Desc: not available
URL: <http://lists.infradead.org/pipermail/linux-arm-kernel/attachments/20140121/45360577/attachment.sig>

^ permalink raw reply related

* [PATCH 1/2] tty: uartps: Fix kernel-doc errors in the driver
From: Michal Simek @ 2014-01-21 10:33 UTC (permalink / raw)
  To: linux-arm-kernel

No functional changes.

Signed-off-by: Michal Simek <michal.simek@xilinx.com>
---
./scripts/kernel-doc -v -man drivers/tty/serial/xilinx_uartps.c > /dev/null
---
 drivers/tty/serial/xilinx_uartps.c | 109 +++++++++++++++++--------------------
 1 file changed, 49 insertions(+), 60 deletions(-)

diff --git a/drivers/tty/serial/xilinx_uartps.c b/drivers/tty/serial/xilinx_uartps.c
index f619ad5..b2f929e 100644
--- a/drivers/tty/serial/xilinx_uartps.c
+++ b/drivers/tty/serial/xilinx_uartps.c
@@ -163,11 +163,11 @@ MODULE_PARM_DESC(rx_timeout, "Rx timeout, 1-255");

 /**
  * struct xuartps - device data
- * @port		Pointer to the UART port
- * @refclk		Reference clock
- * @aperclk		APB clock
- * @baud		Current baud rate
- * @clk_rate_change_nb	Notifier block for clock changes
+ * @port:		Pointer to the UART port
+ * @refclk:		Reference clock
+ * @aperclk:		APB clock
+ * @baud:		Current baud rate
+ * @clk_rate_change_nb:	Notifier block for clock changes
  */
 struct xuartps {
 	struct uart_port	*port;
@@ -183,8 +183,8 @@ struct xuartps {
  * @irq: Irq number
  * @dev_id: Id of the port
  *
- * Returns IRQHANDLED
- **/
+ * Return: IRQHANDLED
+ */
 static irqreturn_t xuartps_isr(int irq, void *dev_id)
 {
 	struct uart_port *port = (struct uart_port *)dev_id;
@@ -325,7 +325,7 @@ static irqreturn_t xuartps_isr(int irq, void *dev_id)
  * @rbdiv: BDIV value (return value)
  * @rcd: CD value (return value)
  * @div8: Value for clk_sel bit in mod (return value)
- * Returns baud rate, requested baud when possible, or actual baud when there
+ * Return: baud rate, requested baud when possible, or actual baud when there
  *	was too much error, zero if no valid divisors are found.
  *
  * Formula to obtain baud rate is
@@ -384,7 +384,7 @@ static unsigned int xuartps_calc_baud_divs(unsigned int clk, unsigned int baud,
  * xuartps_set_baud_rate - Calculate and set the baud rate
  * @port: Handle to the uart port structure
  * @baud: Baud rate to set
- * Returns baud rate, requested baud when possible, or actual baud when there
+ * Return: baud rate, requested baud when possible, or actual baud when there
  *	   was too much error, zero if no valid divisors are found.
  */
 static unsigned int xuartps_set_baud_rate(struct uart_port *port,
@@ -419,7 +419,7 @@ static unsigned int xuartps_set_baud_rate(struct uart_port *port,
  * @nb:		Notifier block
  * @event:	Notify event
  * @data:	Notifier data
- * Returns NOTIFY_OK on success, NOTIFY_BAD on error.
+ * Return: NOTIFY_OK on success, NOTIFY_BAD on error.
  */
 static int xuartps_clk_notifier_cb(struct notifier_block *nb,
 		unsigned long event, void *data)
@@ -514,8 +514,7 @@ static int xuartps_clk_notifier_cb(struct notifier_block *nb,
 /**
  * xuartps_start_tx -  Start transmitting bytes
  * @port: Handle to the uart port structure
- *
- **/
+ */
 static void xuartps_start_tx(struct uart_port *port)
 {
 	unsigned int status, numbytes = port->fifosize;
@@ -562,8 +561,7 @@ static void xuartps_start_tx(struct uart_port *port)
 /**
  * xuartps_stop_tx - Stop TX
  * @port: Handle to the uart port structure
- *
- **/
+ */
 static void xuartps_stop_tx(struct uart_port *port)
 {
 	unsigned int regval;
@@ -577,8 +575,7 @@ static void xuartps_stop_tx(struct uart_port *port)
 /**
  * xuartps_stop_rx - Stop RX
  * @port: Handle to the uart port structure
- *
- **/
+ */
 static void xuartps_stop_rx(struct uart_port *port)
 {
 	unsigned int regval;
@@ -593,8 +590,8 @@ static void xuartps_stop_rx(struct uart_port *port)
  * xuartps_tx_empty -  Check whether TX is empty
  * @port: Handle to the uart port structure
  *
- * Returns TIOCSER_TEMT on success, 0 otherwise
- **/
+ * Return: TIOCSER_TEMT on success, 0 otherwise
+ */
 static unsigned int xuartps_tx_empty(struct uart_port *port)
 {
 	unsigned int status;
@@ -608,8 +605,7 @@ static unsigned int xuartps_tx_empty(struct uart_port *port)
  *			transmitting char breaks
  * @port: Handle to the uart port structure
  * @ctl: Value based on which start or stop decision is taken
- *
- **/
+ */
 static void xuartps_break_ctl(struct uart_port *port, int ctl)
 {
 	unsigned int status;
@@ -636,8 +632,7 @@ static void xuartps_break_ctl(struct uart_port *port, int ctl)
  * @port: Handle to the uart port structure
  * @termios: Handle to the input termios structure
  * @old: Values of the previously saved termios structure
- *
- **/
+ */
 static void xuartps_set_termios(struct uart_port *port,
 				struct ktermios *termios, struct ktermios *old)
 {
@@ -761,8 +756,8 @@ static void xuartps_set_termios(struct uart_port *port,
  * xuartps_startup - Called when an application opens a xuartps port
  * @port: Handle to the uart port structure
  *
- * Returns 0 on success, negative error otherwise
- **/
+ * Return: 0 on success, negative error otherwise
+ */
 static int xuartps_startup(struct uart_port *port)
 {
 	unsigned int retval = 0, status = 0;
@@ -824,8 +819,7 @@ static int xuartps_startup(struct uart_port *port)
 /**
  * xuartps_shutdown - Called when an application closes a xuartps port
  * @port: Handle to the uart port structure
- *
- **/
+ */
 static void xuartps_shutdown(struct uart_port *port)
 {
 	int status;
@@ -844,8 +838,8 @@ static void xuartps_shutdown(struct uart_port *port)
  * xuartps_type - Set UART type to xuartps port
  * @port: Handle to the uart port structure
  *
- * Returns string on success, NULL otherwise
- **/
+ * Return: string on success, NULL otherwise
+ */
 static const char *xuartps_type(struct uart_port *port)
 {
 	return port->type == PORT_XUARTPS ? XUARTPS_NAME : NULL;
@@ -856,8 +850,8 @@ static const char *xuartps_type(struct uart_port *port)
  * @port: Handle to the uart port structure
  * @ser: Handle to the structure whose members are compared
  *
- * Returns 0 if success otherwise -EINVAL
- **/
+ * Return: 0 if success otherwise -EINVAL
+ */
 static int xuartps_verify_port(struct uart_port *port,
 					struct serial_struct *ser)
 {
@@ -880,8 +874,8 @@ static int xuartps_verify_port(struct uart_port *port,
  *				uart_add_one_port()
  * @port: Handle to the uart port structure
  *
- * Returns 0, -ENOMEM if request fails
- **/
+ * Return: 0, -ENOMEM if request fails
+ */
 static int xuartps_request_port(struct uart_port *port)
 {
 	if (!request_mem_region(port->mapbase, XUARTPS_REGISTER_SPACE,
@@ -903,8 +897,7 @@ static int xuartps_request_port(struct uart_port *port)
  *				port, called when the driver removes a xuartps
  *				port via uart_remove_one_port().
  * @port: Handle to the uart port structure
- *
- **/
+ */
 static void xuartps_release_port(struct uart_port *port)
 {
 	release_mem_region(port->mapbase, XUARTPS_REGISTER_SPACE);
@@ -917,8 +910,7 @@ static void xuartps_release_port(struct uart_port *port)
  *				xuartps port
  * @port: Handle to the uart port structure
  * @flags: If any
- *
- **/
+ */
 static void xuartps_config_port(struct uart_port *port, int flags)
 {
 	if (flags & UART_CONFIG_TYPE && xuartps_request_port(port) == 0)
@@ -930,9 +922,8 @@ static void xuartps_config_port(struct uart_port *port, int flags)
  *
  * @port: Handle to the uart port structure
  *
- * Returns the modem control state
- *
- **/
+ * Return: the modem control state
+ */
 static unsigned int xuartps_get_mctrl(struct uart_port *port)
 {
 	return TIOCM_CTS | TIOCM_DSR | TIOCM_CAR;
@@ -1040,8 +1031,8 @@ static struct uart_port xuartps_port[2];
  * xuartps_get_port - Configure the port from the platform device resource
  *			info
  *
- * Returns a pointer to a uart_port or NULL for failure
- **/
+ * Return: a pointer to a uart_port or NULL for failure
+ */
 static struct uart_port *xuartps_get_port(void)
 {
 	struct uart_port *port;
@@ -1078,8 +1069,7 @@ static struct uart_port *xuartps_get_port(void)
 /**
  * xuartps_console_wait_tx - Wait for the TX to be full
  * @port: Handle to the uart port structure
- *
- **/
+ */
 static void xuartps_console_wait_tx(struct uart_port *port)
 {
 	while ((xuartps_readl(XUARTPS_SR_OFFSET) & XUARTPS_SR_TXEMPTY)
@@ -1091,8 +1081,7 @@ static void xuartps_console_wait_tx(struct uart_port *port)
  * xuartps_console_putchar - write the character to the FIFO buffer
  * @port: Handle to the uart port structure
  * @ch: Character to be written
- *
- **/
+ */
 static void xuartps_console_putchar(struct uart_port *port, int ch)
 {
 	xuartps_console_wait_tx(port);
@@ -1101,10 +1090,10 @@ static void xuartps_console_putchar(struct uart_port *port, int ch)

 /**
  * xuartps_console_write - perform write operation
- * @port: Handle to the uart port structure
+ * @co: Console handle
  * @s: Pointer to character array
  * @count: No of characters
- **/
+ */
 static void xuartps_console_write(struct console *co, const char *s,
 				unsigned int count)
 {
@@ -1151,8 +1140,8 @@ static void xuartps_console_write(struct console *co, const char *s,
  * @co: Console handle
  * @options: Initial settings of uart
  *
- * Returns 0, -ENODEV if no device
- **/
+ * Return: 0, -ENODEV if no device
+ */
 static int __init xuartps_console_setup(struct console *co, char *options)
 {
 	struct uart_port *port = &xuartps_port[co->index];
@@ -1190,8 +1179,8 @@ static struct console xuartps_console = {
 /**
  * xuartps_console_init - Initialization call
  *
- * Returns 0 on success, negative error otherwise
- **/
+ * Return: 0 on success, negative error otherwise
+ */
 static int __init xuartps_console_init(void)
 {
 	register_console(&xuartps_console);
@@ -1221,7 +1210,7 @@ static struct uart_driver xuartps_uart_driver = {
  * xuartps_suspend - suspend event
  * @device: Pointer to the device structure
  *
- * Returns 0
+ * Return: 0
  */
 static int xuartps_suspend(struct device *device)
 {
@@ -1269,7 +1258,7 @@ static int xuartps_suspend(struct device *device)
  * xuartps_resume - Resume after a previous suspend
  * @device: Pointer to the device structure
  *
- * Returns 0
+ * Return: 0
  */
 static int xuartps_resume(struct device *device)
 {
@@ -1336,8 +1325,8 @@ static SIMPLE_DEV_PM_OPS(xuartps_dev_pm_ops, xuartps_suspend, xuartps_resume);
  * xuartps_probe - Platform driver probe
  * @pdev: Pointer to the platform device structure
  *
- * Returns 0 on success, negative error otherwise
- **/
+ * Return: 0 on success, negative error otherwise
+ */
 static int xuartps_probe(struct platform_device *pdev)
 {
 	int rc;
@@ -1437,8 +1426,8 @@ err_out_clk_dis_aper:
  * xuartps_remove - called when the platform driver is unregistered
  * @pdev: Pointer to the platform device structure
  *
- * Returns 0 on success, negative error otherwise
- **/
+ * Return: 0 on success, negative error otherwise
+ */
 static int xuartps_remove(struct platform_device *pdev)
 {
 	struct uart_port *port = platform_get_drvdata(pdev);
@@ -1481,8 +1470,8 @@ static struct platform_driver xuartps_platform_driver = {
 /**
  * xuartps_init - Initial driver registration call
  *
- * Returns whether the registration was successful or not
- **/
+ * Return: whether the registration was successful or not
+ */
 static int __init xuartps_init(void)
 {
 	int retval = 0;
@@ -1502,7 +1491,7 @@ static int __init xuartps_init(void)

 /**
  * xuartps_exit - Driver unregistration call
- **/
+ */
 static void __exit xuartps_exit(void)
 {
 	/* The order of unregistration is important. Unregister the
--
1.8.2.3

-------------- next part --------------
A non-text attachment was scrubbed...
Name: not available
Type: application/pgp-signature
Size: 198 bytes
Desc: not available
URL: <http://lists.infradead.org/pipermail/linux-arm-kernel/attachments/20140121/f6942f96/attachment.sig>

^ permalink raw reply related

* [PATCH v2 2/7] clk: samsung: add infrastructure to register cpu clocks
From: Lukasz Majewski @ 2014-01-21 10:25 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <CAJuA9ajpcB41b0d09emqPPVLxeXviQAjeEObNjPxnZ3PcZk0GA@mail.gmail.com>

Hi Thomas,

> Hi Lukasz,
> 
> On Mon, Jan 20, 2014 at 1:54 PM, Lukasz Majewski
> <l.majewski@samsung.com> wrote:
> > Hi Thomas,
> >
> >> From: Thomas Abraham <thomas.ab@samsung.com>
> >>
> >> The CPU clock provider supplies the clock to the CPU clock domain.
> >> The composition and organization of the CPU clock provider could
> >> vary among Exynos SoCs. A CPU clock provider can be composed of
> >> clock mux, dividers and gates. This patch defines a new clock type
> >> for CPU clock provider and adds infrastructure to register the CPU
> >> clock providers for Samsung platforms.
> >>
> >> In addition to this, the arm cpu clock provider for Exynos4210 and
> >> compatible SoCs is instantiated using the new cpu clock type. The
> >> clock frequency table and the clock configuration data for this
> >> clock is obtained from device tree. This implementation is
> >> reusable for Exynos4x12 and Exynos5250 SoCs as well.
> >>
> >> Cc: Tomasz Figa <t.figa@samsung.com>
> >> Cc: Lukasz Majewski <l.majewski@majess.pl>
> >> Signed-off-by: Thomas Abraham <thomas.ab@samsung.com>
> >> ---
> >>  drivers/clk/samsung/Makefile  |    2 +-
> >>  drivers/clk/samsung/clk-cpu.c |  345
> >> +++++++++++++++++++++++++++++++++++++++++
> >> drivers/clk/samsung/clk.h     |    3 + 3 files changed, 349
> >> insertions(+), 1 deletions(-) create mode 100644
> >> drivers/clk/samsung/clk-cpu.c
> >>
> >> diff --git a/drivers/clk/samsung/Makefile
> >> b/drivers/clk/samsung/Makefile index 8eb4799..e2b453f 100644
> >> --- a/drivers/clk/samsung/Makefile
> >> +++ b/drivers/clk/samsung/Makefile
> >> @@ -2,7 +2,7 @@
> >>  # Samsung Clock specific Makefile
> >>  #
> >>
> >> -obj-$(CONFIG_COMMON_CLK)     += clk.o clk-pll.o
> >> +obj-$(CONFIG_COMMON_CLK)     += clk.o clk-pll.o clk-cpu.o
> >>  obj-$(CONFIG_ARCH_EXYNOS4)   += clk-exynos4.o
> >>  obj-$(CONFIG_SOC_EXYNOS5250) += clk-exynos5250.o
> >>  obj-$(CONFIG_SOC_EXYNOS5420) += clk-exynos5420.o
> >> diff --git a/drivers/clk/samsung/clk-cpu.c
> >> b/drivers/clk/samsung/clk-cpu.c new file mode 100644
> >> index 0000000..92fba45
> >> --- /dev/null
> >> +++ b/drivers/clk/samsung/clk-cpu.c
> >> @@ -0,0 +1,345 @@
> >> +/*
> >> + * Copyright (c) 2014 Samsung Electronics Co., Ltd.
> >> + * Author: Thomas Abraham <thomas.ab@samsung.com>
> >> + *
> >> + * This program is free software; you can redistribute it and/or
> >> modify
> >> + * it under the terms of the GNU General Public License version 2
> >> as
> >> + * published by the Free Software Foundation.
> >> + *
> >> + * This file contains the utility functions to register the cpu
> >> clocks
> >> + * for samsung platforms.
> >> +*/
> >> +
> >> +#include <linux/errno.h>
> >> +#include "clk.h"
> >> +
> >> +#define SRC_CPU                      0x0
> >> +#define STAT_CPU             0x200
> >> +#define DIV_CPU0             0x300
> >> +#define DIV_CPU1             0x304
> >> +#define DIV_STAT_CPU0                0x400
> >> +#define DIV_STAT_CPU1                0x404
> >> +
> >> +/**
> >> + * struct samsung_cpuclk_freq_table: table of frequency supported
> >> by
> >> + * a cpu clock and associated data if any.
> >> + * @freq: points to a table of supported frequencies (in KHz)
> >> + * @freq_count: number of entries in the frequency table
> >> + * @data: cpu clock specific data, if any
> >> + *
> >> + * This structure holds the frequency options supported by the cpu
> >> clock in
> >> + * which this structure is contained. The data pointer is an
> >> optional data
> >> + * that can provide any additional configuration options for the
> >> supported
> >> + * frequencies. This structure is intended to be reusable for all
> >> cpu clocks
> >> + * in Samsung SoC based platforms
> >> + */
> >> +struct samsung_cpuclk_freq_table {
> >> +     const unsigned long     *freq;       /* in KHz */
> >> +     unsigned long           freq_count;
> >> +     const void              *data;
> >> +};
> >> +
> >> +/**
> >> + * struct exynos4210_freq_data: format of auxillary data
> >> associated with
> >> + * each frequency supported by the cpu clock for exynos4210.
> >> + * @parent_freq: The frequency of the parent clock required to
> >> generate the
> >> + * supported cpu clock speed.
> >> + * @div0: value to be programmed in the div_cpu0 register.
> >> + * @div1: value to be programmed in the div_cpu1 register.
> >> + *
> >> + * This structure holds the auxillary configuration data for each
> >> supported
> >> + * cpu clock frequency on Exynos4210 and compatible SoCs.
> >> + */
> >> +struct exynos4210_freq_data {
> >> +     unsigned long   parent_freq;
> >> +     unsigned int    div0;
> >> +     unsigned int    div1;
> >> +};
> >> +
> >> +/**
> >> + * struct samsung_cpuclk: information about clock supplied to a
> >> CPU core.
> >> + * @hw: handle between ccf and cpu clock.
> >> + * @ctrl_base: base address of the clock controller.
> >> + * @offset: offset from the ctrl_base address where the cpu clock
> >> div/mux
> >> + *          registers can be accessed.
> >> + * @parent: clock handle representing the clock output of the
> >> parent clock.
> >> + * @freq_table: the frequency table supported by this cpu clock.
> >> + */
> >> +struct samsung_cpuclk {
> >> +     struct clk_hw           hw;
> >> +     void __iomem            *ctrl_base;
> >> +     unsigned long           offset;
> >> +     struct clk              *parent;
> >> +     const struct samsung_cpuclk_freq_table *freq_table;
> >> +};
> >> +
> >> +#define to_samsung_cpuclk(hw)        container_of(hw, struct
> >> samsung_cpuclk, hw) +
> >> +/**
> >> + * struct samsung_cpuclk_match_data: soc specific data for cpu
> >> clocks.
> >> + * @parser: pointer to a function that can parse SoC specific cpu
> >> clock
> >> + *   frequency and associated configuration data.
> >> + * @offset: optional offset from base of clock controller register
> >> base,
> >> + *   to be used when accessing clock controller registers
> >> related to the
> >> + * cpu clock.
> >> + * @offset: offset from the ctrl_base address where the cpu clock
> >> div/mux
> >> + *   registers can be accessed.
> >> + */
> >> +struct samsung_cpuclk_match_data {
> >> +     int (*parser)(struct device_node *,
> >> +                     struct samsung_cpuclk_freq_table **);
> >> +     unsigned int offset;
> >> +};
> >> +
> >> +/* This is a helper function to perform clock rounding for cpu
> >> clocks. */ +static long samsung_cpuclk_round_rate(struct clk_hw
> >> *hw,
> >> +                     unsigned long drate, unsigned long *prate)
> >> +{
> >> +     struct samsung_cpuclk *cpuclk = to_samsung_cpuclk(hw);
> >> +     const struct samsung_cpuclk_freq_table *freq_tbl;
> >> +     int i;
> >> +
> >> +     freq_tbl = cpuclk->freq_table;
> >> +     drate /= 1000;
> >> +
> >> +     for (i = 0; i < freq_tbl->freq_count; i++) {
> >> +             if (drate >= freq_tbl->freq[i])
> >> +                     return freq_tbl->freq[i] * 1000;
> >> +     }
> >> +     return freq_tbl->freq[i - 1] * 1000;
> >> +}
> >> +
> >> +#define EXYNOS4210_ARM_DIV1(base) ((readl(base + DIV_CPU0) & 0xf)
> >> + 1) +#define EXYNOS4210_ARM_DIV2(base) (((readl(base + DIV_CPU0)
> >> >> 28) & 0xf) + 1) +
> >> +/*
> >> + * CPU clock speed for Exynos4210 and compatible SoCs is
> >> + * parent clock speed / core1_ratio / core2_ratio
> >> + */
> >> +static unsigned long exynos4210_armclk_recalc_rate(struct clk_hw
> >> *hw,
> >> +                             unsigned long parent_rate)
> >> +{
> >> +     struct samsung_cpuclk *armclk = to_samsung_cpuclk(hw);
> >> +     void __iomem *base = armclk->ctrl_base + armclk->offset;
> >> +
> >> +     return parent_rate / EXYNOS4210_ARM_DIV1(base) /
> >> +                             EXYNOS4210_ARM_DIV2(base);
> >> +}
> >> +
> >> +/* set rate callback for cpuclk type on Exynos4210 and similar
> >> SoCs */ +static int exynos4210_armclk_set_rate(struct clk_hw *hw,
> >> unsigned long drate,
> >> +                                     unsigned long prate)
> >> +{
> >> +     struct samsung_cpuclk *armclk = to_samsung_cpuclk(hw);
> >> +     const struct samsung_cpuclk_freq_table *freq_tbl;
> >> +     const struct exynos4210_freq_data *freq_data;
> >> +     unsigned long mux_reg, idx;
> >> +     void __iomem *base;
> >> +
> >> +     if (drate == prate)
> >> +             return 0;
> >> +
> >> +     freq_tbl = armclk->freq_table;
> >> +     freq_data = freq_tbl->data;
> >> +     base = armclk->ctrl_base + armclk->offset;
> >> +
> >> +     for (idx = 0; idx < freq_tbl->freq_count; idx++, freq_data++)
> >> +             if ((freq_tbl->freq[idx] * 1000) == drate)
> >> +                     break;
> >> +
> >> +     if (drate < prate) {
> >> +             mux_reg = readl(base + SRC_CPU);
> >> +             writel(mux_reg | (1 << 16), base + SRC_CPU);
> >> +             while (((readl(base + STAT_CPU) >> 16) & 0x7) != 2)
> >> +                     ;
> >> +
> >> +             clk_set_rate(armclk->parent, drate);
> >> +     }
> >> +
> >> +     writel(freq_data->div0, base + DIV_CPU0);
> >> +     while (readl(base + DIV_STAT_CPU0) != 0)
> >> +             ;
> >> +     writel(freq_data->div1, base + DIV_CPU1);
> >> +     while (readl(base + DIV_STAT_CPU1) != 0)
> >> +             ;
> >> +
> >> +     if (drate > prate) {
> >> +             mux_reg = readl(base + SRC_CPU);
> >> +             writel(mux_reg | (1 << 16), base + SRC_CPU);
> >> +             while (((readl(base + STAT_CPU) >> 16) & 0x7) != 2)
> >> +                     ;
> >> +
> >> +             clk_set_rate(armclk->parent, freq_data->parent_freq
> >> * 1000);
> >> +     }
> >> +
> >> +     mux_reg = readl(base + SRC_CPU);
> >> +     writel(mux_reg & ~(1 << 16), base + SRC_CPU);
> >> +     while (((readl(base + STAT_CPU) >> 16) & 0x7) != 1)
> >> +                     ;
> >> +     return 0;
> >> +}
> >> +
> >> +/* clock ops for armclk on Exynos4210 and compatible platforms. */
> >> +static const struct clk_ops exynos4210_armclk_clk_ops = {
> >> +     .recalc_rate = exynos4210_armclk_recalc_rate,
> >> +     .round_rate = samsung_cpuclk_round_rate,
> >> +     .set_rate = exynos4210_armclk_set_rate,
> >> +};
> >> +
> >> +/* helper function to register a cpu clock */
> >> +static void __init samsung_cpuclk_register(unsigned int lookup_id,
> >> +             const char *name, const char *parent, const struct
> >> clk_ops *ops,
> >> +             const struct samsung_cpuclk_freq_table *freq_tbl,
> >> +             void __iomem *reg_base,
> >> +             const struct samsung_cpuclk_match_data *data)
> >> +{
> >> +     struct samsung_cpuclk *cpuclk;
> >> +     struct clk_init_data init;
> >> +     struct clk *clk;
> >> +
> >> +     cpuclk = kzalloc(sizeof(*cpuclk), GFP_KERNEL);
> >> +     if (!cpuclk) {
> >> +             pr_err("%s: could not allocate memory for cpuclk
> >> %s\n",
> >> +                                     __func__, name);
> >> +             return;
> >> +     }
> >> +
> >> +     init.name = name;
> >> +     init.flags = CLK_GET_RATE_NOCACHE;
> >> +     init.parent_names = &parent;
> >> +     init.num_parents = 1;
> >> +     init.ops = ops;
> >> +
> >> +     cpuclk->hw.init = &init;
> >> +     cpuclk->ctrl_base = reg_base;
> >> +     cpuclk->offset = data->offset;
> >> +     cpuclk->freq_table = freq_tbl;
> >> +     cpuclk->parent = __clk_lookup(parent);
> >> +
> >> +     clk = clk_register(NULL, &cpuclk->hw);
> >> +     if (IS_ERR(clk)) {
> >> +             pr_err("%s: could not register cpuclk %s\n",
> >> __func__,     name);
> >> +             kfree(cpuclk);
> >> +             return;
> >> +     }
> >> +     samsung_clk_add_lookup(clk, lookup_id);
> >> +}
> >> +
> >> +#define EXYNOS4210_DIV_CPU01(d0, d1, d2, d3, d4, d5, d6,
> >> d7)           \
> >> +             ((d0 << 28) | (d1 << 24) | (d2 << 20) | (d3
> >> << 16) |      \
> >> +              (d4 << 12) | (d5 << 8) | (d6 << 4) | (d7 << 0))
> >> +#define EXYNOS4210_DIV_CPU11(d0, d1,
> >> d2)                           \
> >> +             ((d0 << 8) | (d1 << 4) | (d2 << 0))
> >> +#define EXYNOS4210_CFG_LEN 13
> >> +
> >> +/*
> >> + * parse cpu clock frequency table and auxillary configuration
> >> data from dt
> >> + * for exynos4210 and compatible SoC's.
> >> + */
> >> +static int exynos4210_armclk_cfg_parser(struct device_node *np,
> >> +             struct samsung_cpuclk_freq_table **tbl)
> >> +{
> >> +     struct samsung_cpuclk_freq_table *freq_tbl;
> >> +     struct exynos4210_freq_data *fdata, *t_fdata;
> >> +     unsigned long *freqs, cfg[EXYNOS4210_CFG_LEN];
> >> +     const struct property *prop;
> >> +     unsigned int tbl_sz, i, j;
> >> +     const __be32 *val;
> >> +     int ret;
> >> +
> >> +     prop = of_find_property(np, "arm-frequency-table", NULL);
> >> +     if (!prop)
> >> +             return -EINVAL;
> >> +     if (!prop->value)
> >> +             return -EINVAL;
> >> +     if ((prop->length / sizeof(u32)) % EXYNOS4210_CFG_LEN)
> >
> > Cannot we have the EXYNOS4210_CFG_LEN parsed from DT as well?
> 
> As per Rob's suggestion, the clock divider ration table will be
> removed. So this portion of the code will be reworked.

Ok. Thanks.

> 
> >
> >> +             return -EINVAL;
> >> +     tbl_sz = (prop->length / sizeof(u32)) / EXYNOS4210_CFG_LEN;
> >> +
> >> +     freq_tbl = kzalloc(sizeof(*freq_tbl), GFP_KERNEL);
> >> +     if (!freq_tbl)
> >> +             return -ENOMEM;
> >> +
> >> +     freqs = kzalloc(sizeof(u32) * tbl_sz, GFP_KERNEL);
> >> +     if (!freqs) {
> >> +             ret = -ENOMEM;
> >> +             goto free_freq_tbl;
> >> +     }
> >> +
> >> +     fdata = kzalloc(sizeof(*fdata) * tbl_sz, GFP_KERNEL);
> >> +     if (!fdata) {
> >> +             ret = -ENOMEM;
> >> +             goto free_freqs;
> >> +     }
> >> +     t_fdata = fdata;
> >> +
> >> +     val = prop->value;
> >> +     for (i = 0; i < tbl_sz; i++, fdata++) {
> >> +             for (j = 0; j < EXYNOS4210_CFG_LEN; j++)
> >> +                     cfg[j] = be32_to_cpup(val++);
> >> +             freqs[i] = cfg[0];
> >> +             fdata->parent_freq = cfg[1];
> >
> > Why do we need the separate parent_freq entry here?
> >
> > In the patch 4/7 the freqs (cfg[0]) and parent_freq (cfg[1]) values
> > are the same for all supported devices (at "arm-frequency-table").
> >
> > What is the rationale for having those values duplicated in the DT?
> 
> The intention was to support frequencies which may not be direct
> output of the parent PLL clock. For instance, if the PLL supports
> 200MHz, 400MHz and 600MHz and the CPU clock needs to be set to 300MHz,
> then 600MHz / 2 is a valid clock output for the cpu clock. So this is
> an example where the cpu clock speed is different from its parent
> clock speed.

But shall not this case been handled by CCF internally? 

Is there any Samsung PLL clock, which has such property?

> If possible, I will try and remove the need for this table in the
> next version.

Ok.

> 
> Thanks,
> Thomas.
> 
> >
> >
> >> +             fdata->div0 = EXYNOS4210_DIV_CPU01(cfg[9], cfg[8],
> >> cfg[7],
> >> +                             cfg[6], cfg[5], cfg[4], cfg[3],
> >> cfg[2]);
> >> +             fdata->div1 = EXYNOS4210_DIV_CPU11(cfg[12], cfg[11],
> >> cfg[10]);
> >> +     }
> >> +
> >> +     freq_tbl->freq = freqs;
> >> +     freq_tbl->freq_count = tbl_sz;
> >> +     freq_tbl->data = t_fdata;
> >> +     *tbl = freq_tbl;
> >> +     return 0;
> >> +
> >> +free_freqs:
> >> +     kfree(freqs);
> >> +free_freq_tbl:
> >> +     kfree(freq_tbl);
> >> +     return ret;
> >> +}
> >> +
> >> +static struct samsung_cpuclk_match_data
> >> exynos4210_cpuclk_match_data = {
> >> +     .parser = exynos4210_armclk_cfg_parser,
> >> +     .offset = 0x14200,
> >> +};
> >> +
> >> +static struct samsung_cpuclk_match_data
> >> exynos5250_cpuclk_match_data = {
> >> +     .parser = exynos4210_armclk_cfg_parser,
> >> +     .offset = 0x200,
> >> +};
> >> +
> >> +static const struct of_device_id samsung_clock_ids[] = {
> >> +     { .compatible = "samsung,exynos4210-clock",
> >> +                     .data = &exynos4210_cpuclk_match_data, },
> >> +     { .compatible = "samsung,exynos4412-clock",
> >> +                     .data = &exynos4210_cpuclk_match_data, },
> >> +     { .compatible = "samsung,exynos5250-clock",
> >> +                     .data = &exynos5250_cpuclk_match_data, },
> >> +};
> >> +
> >> +int __init samsung_register_arm_clock(struct device_node *np,
> >> +             unsigned int lookup_id, const char *parent,
> >> void __iomem *base) +{
> >> +     const struct of_device_id *match;
> >> +     struct samsung_cpuclk_freq_table *freq_table;
> >> +     const struct samsung_cpuclk_match_data *data;
> >> +     int ret;
> >> +
> >> +     match = of_match_node(samsung_clock_ids, np);
> >> +     if (!match) {
> >> +             pr_err("%s: could not determine soc type\n",
> >> __func__);
> >> +             return -EINVAL;
> >> +     }
> >> +
> >> +     data = match->data;
> >> +     ret = data->parser(np, &freq_table);
> >> +     if (ret) {
> >> +             pr_err("%s: error %d in parsing arm clock freq
> >> table",
> >> +                                             __func__, ret);
> >> +             return -EINVAL;
> >> +     }
> >> +
> >> +     samsung_cpuclk_register(lookup_id, "armclk", parent,
> >> +             &exynos4210_armclk_clk_ops, freq_table, base, data);
> >> +
> >> +     return 0;
> >> +}
> >> diff --git a/drivers/clk/samsung/clk.h b/drivers/clk/samsung/clk.h
> >> index 31b4174..a759330 100644
> >> --- a/drivers/clk/samsung/clk.h
> >> +++ b/drivers/clk/samsung/clk.h
> >> @@ -340,4 +340,7 @@ extern void __init
> >> samsung_clk_register_pll(struct samsung_pll_clock *pll_list,
> >>  extern unsigned long _get_rate(const char *clk_name);
> >>
> >> +extern int __init samsung_register_arm_clock(struct device_node
> >> *np,
> >> +             unsigned int lookup_id, const char *parent, void
> >> __iomem *base); +
> >>  #endif /* __SAMSUNG_CLK_H */
> >
> >
> >
> > --
> > Best regards,
> >
> > Lukasz Majewski
> >
> > Samsung R&D Institute Poland (SRPOL) | Linux Platform Group

-- 
Best regards,

Lukasz Majewski

Samsung R&D Institute Poland (SRPOL) | Linux Platform Group

^ permalink raw reply

* [PATCH] Add Xilinx AXI Video DMA Engine driver
From: Srikanth Thokala @ 2014-01-21 10:21 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <1389972737.7619.14.camel@smile>

Hi Andy,

On Fri, Jan 17, 2014 at 9:02 PM, Andy Shevchenko
<andriy.shevchenko@linux.intel.com> wrote:
> On Thu, 2014-01-16 at 23:23 +0530, Srikanth Thokala wrote:
>> Hi,
>>
>> This is the driver for Xilinx AXI Video Direct Memory Access Engine.
>> It is a soft IP core, which provides high-bandwidth direct memory
>> access between memory and AXI4-Stream video type target peripherals
>> including peripherals which support AXI4-Stream Video Protocol. The
>> core provides efficient two dimensional DMA operations with independent
>> asynchronous read and write channel operation.
>>
>> For more information on the IP, please refer to
>> http://www.xilinx.com/support/documentation/ip_documentation/axi_vdma/v6_1/pg020_axi_vdma.pdf
>>
>> This patch also provides a test client, which assumes read and write channels
>> of the core are configured in a back-to-back connection.  It transfers
>> data on the write channel, read and verify the data on the read channel.
>>
>>  drivers/dma/xilinx/xilinx_vdma_test.c              |  629 ++++++++
>
> What about to modify dmatest.c accordingly to your needs?
> I see no much sense to have another copy of that in the tree.

I agree with you, we thought it will be good to provide a simple test
case for our driver,
I will drop off this test client from my v2 patch set.

Thanks
Srikanth

>
> --
> Andy Shevchenko <andriy.shevchenko@linux.intel.com>
> Intel Finland Oy
>
> --
> To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
> the body of a message to majordomo at vger.kernel.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html
> Please read the FAQ at  http://www.tux.org/lkml/

^ permalink raw reply

* Question: xilinx_zynq_defconfig
From: Masahiro Yamada @ 2014-01-21 10:17 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <52DE3743.9000109@monstr.eu>

Hello Michal,

> zynq is enabled in multi_v7 defconfig that's why I don't have
> a need to add defconfig just for zynq. Even one defconfig per soc
> is permitted by maintainers.

Thanks!

Best Regards
Masahiro Yamada

^ permalink raw reply

* [PATCH v2 03/15] watchdog: orion: Use atomic access for shared registers
From: Andrew Lunn @ 2014-01-21 10:13 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <3381968.dYvuFs4ZXV@wuerfel>

On Tue, Jan 21, 2014 at 10:54:37AM +0100, Arnd Bergmann wrote:
> On Tuesday 21 January 2014 06:12:29 Ezequiel Garcia wrote:
> >         writel(~WDT_INT_REQ, BRIDGE_CAUSE);
> >  
> >         /* Enable watchdog timer */
> > -       reg = readl(wdt_reg + TIMER_CTRL);
> > -       reg |= WDT_EN;
> > -       writel(reg, wdt_reg + TIMER_CTRL);
> > +       atomic_io_modify(wdt_reg + TIMER_CTRL, WDT_EN, WDT_EN);
> 
> As mentioned in my comment for patch 2, it seems that the exported
> orion_timer_ctrl_clrset() function would be more appropriate for this.

Hi Arnd

As Sebastian points out, Armada 370 XP does not have this function. 

> 
> >         /* Enable reset on watchdog */
> > -       reg = readl(RSTOUTn_MASK);
> > -       reg |= WDT_RESET_OUT_EN;
> > -       writel(reg, RSTOUTn_MASK);
> > +       atomic_io_modify(RSTOUTn_MASK, WDT_RESET_OUT_EN, WDT_RESET_OUT_EN);
> 
> And this register already has an abstraction in
> arch/arm/mach-mvebu/system-controller.c. I would prefer to find a way
> to generalize that. I can see multiple ways to do that:

Please be aware that arch/arm/mach-mvebu/system-controller.c is
currently DT only. We need a solution which works for both with DT and
without DT, since orion5x and mv78xx0 none DT boards are not going
away soon.

     Andrew
 

^ permalink raw reply

* [PATCH v4 2/2] usb: dwc3: adapt dwc3 core to use Generic PHY Framework
From: Kishon Vijay Abraham I @ 2014-01-21 10:11 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <1390299099-14764-1-git-send-email-kishon@ti.com>

Adapted dwc3 core to use the Generic PHY Framework. So for init, exit,
power_on and power_off the following APIs are used phy_init(), phy_exit(),
phy_power_on() and phy_power_off().

However using the old USB phy library wont be removed till the PHYs of all
other SoC's using dwc3 core is adapted to the Generic PHY Framework.

Signed-off-by: Kishon Vijay Abraham I <kishon@ti.com>
---
Changes from v3:
* avoided using quirks

 Documentation/devicetree/bindings/usb/dwc3.txt |    6 ++-
 drivers/usb/dwc3/core.c                        |   60 ++++++++++++++++++++++++
 drivers/usb/dwc3/core.h                        |    7 +++
 3 files changed, 71 insertions(+), 2 deletions(-)

diff --git a/Documentation/devicetree/bindings/usb/dwc3.txt b/Documentation/devicetree/bindings/usb/dwc3.txt
index e807635..471366d 100644
--- a/Documentation/devicetree/bindings/usb/dwc3.txt
+++ b/Documentation/devicetree/bindings/usb/dwc3.txt
@@ -6,11 +6,13 @@ Required properties:
  - compatible: must be "snps,dwc3"
  - reg : Address and length of the register set for the device
  - interrupts: Interrupts used by the dwc3 controller.
+
+Optional properties:
  - usb-phy : array of phandle for the PHY device.  The first element
    in the array is expected to be a handle to the USB2/HS PHY and
    the second element is expected to be a handle to the USB3/SS PHY
-
-Optional properties:
+ - phys: from the *Generic PHY* bindings
+ - phy-names: from the *Generic PHY* bindings
  - tx-fifo-resize: determines if the FIFO *has* to be reallocated.
 
 This is usually a subnode to DWC3 glue to which it is connected.
diff --git a/drivers/usb/dwc3/core.c b/drivers/usb/dwc3/core.c
index e009d4e..036d589 100644
--- a/drivers/usb/dwc3/core.c
+++ b/drivers/usb/dwc3/core.c
@@ -82,6 +82,11 @@ static void dwc3_core_soft_reset(struct dwc3 *dwc)
 
 	usb_phy_init(dwc->usb2_phy);
 	usb_phy_init(dwc->usb3_phy);
+	if (dwc->usb2_generic_phy)
+		phy_init(dwc->usb2_generic_phy);
+	if (dwc->usb3_generic_phy)
+		phy_init(dwc->usb3_generic_phy);
+
 	mdelay(100);
 
 	/* Clear USB3 PHY reset */
@@ -343,6 +348,11 @@ static void dwc3_core_exit(struct dwc3 *dwc)
 {
 	usb_phy_shutdown(dwc->usb2_phy);
 	usb_phy_shutdown(dwc->usb3_phy);
+	if (dwc->usb2_generic_phy)
+		phy_exit(dwc->usb2_generic_phy);
+	if (dwc->usb3_generic_phy)
+		phy_exit(dwc->usb3_generic_phy);
+
 }
 
 #define DWC3_ALIGN_MASK		(16 - 1)
@@ -433,6 +443,32 @@ static int dwc3_probe(struct platform_device *pdev)
 		}
 	}
 
+	dwc->usb2_generic_phy = devm_phy_get(dev, "usb2-phy");
+	if (IS_ERR(dwc->usb2_generic_phy)) {
+		ret = PTR_ERR(dwc->usb2_generic_phy);
+		if (ret == -ENOSYS || ret == -ENODEV) {
+			dwc->usb2_generic_phy = NULL;
+		} else if (ret == -EPROBE_DEFER) {
+			return ret;
+		} else {
+			dev_err(dev, "no usb2 phy configured\n");
+			return ret;
+		}
+	}
+
+	dwc->usb3_generic_phy = devm_phy_get(dev, "usb3-phy");
+	if (IS_ERR(dwc->usb3_generic_phy)) {
+		ret = PTR_ERR(dwc->usb3_generic_phy);
+		if (ret == -ENOSYS || ret == -ENODEV) {
+			dwc->usb3_generic_phy = NULL;
+		} else if (ret == -EPROBE_DEFER) {
+			return ret;
+		} else {
+			dev_err(dev, "no usb3 phy configured\n");
+			return ret;
+		}
+	}
+
 	dwc->xhci_resources[0].start = res->start;
 	dwc->xhci_resources[0].end = dwc->xhci_resources[0].start +
 					DWC3_XHCI_REGS_END;
@@ -482,6 +518,11 @@ static int dwc3_probe(struct platform_device *pdev)
 	usb_phy_set_suspend(dwc->usb2_phy, 0);
 	usb_phy_set_suspend(dwc->usb3_phy, 0);
 
+	if (dwc->usb2_generic_phy)
+		phy_power_on(dwc->usb2_generic_phy);
+	if (dwc->usb3_generic_phy)
+		phy_power_on(dwc->usb3_generic_phy);
+
 	ret = dwc3_event_buffers_setup(dwc);
 	if (ret) {
 		dev_err(dwc->dev, "failed to setup event buffers\n");
@@ -565,6 +606,10 @@ err2:
 err1:
 	usb_phy_set_suspend(dwc->usb2_phy, 1);
 	usb_phy_set_suspend(dwc->usb3_phy, 1);
+	if (dwc->usb2_generic_phy)
+		phy_power_off(dwc->usb2_generic_phy);
+	if (dwc->usb3_generic_phy)
+		phy_power_off(dwc->usb3_generic_phy);
 	dwc3_core_exit(dwc);
 
 err0:
@@ -580,6 +625,11 @@ static int dwc3_remove(struct platform_device *pdev)
 	usb_phy_set_suspend(dwc->usb2_phy, 1);
 	usb_phy_set_suspend(dwc->usb3_phy, 1);
 
+	if (dwc->usb2_generic_phy)
+		phy_power_off(dwc->usb2_generic_phy);
+	if (dwc->usb3_generic_phy)
+		phy_power_off(dwc->usb3_generic_phy);
+
 	pm_runtime_put_sync(&pdev->dev);
 	pm_runtime_disable(&pdev->dev);
 
@@ -677,6 +727,11 @@ static int dwc3_suspend(struct device *dev)
 	usb_phy_shutdown(dwc->usb3_phy);
 	usb_phy_shutdown(dwc->usb2_phy);
 
+	if (dwc->usb2_generic_phy)
+		phy_exit(dwc->usb2_generic_phy);
+	if (dwc->usb3_generic_phy)
+		phy_exit(dwc->usb3_generic_phy);
+
 	return 0;
 }
 
@@ -688,6 +743,11 @@ static int dwc3_resume(struct device *dev)
 	usb_phy_init(dwc->usb3_phy);
 	usb_phy_init(dwc->usb2_phy);
 
+	if (dwc->usb2_generic_phy)
+		phy_init(dwc->usb2_generic_phy);
+	if (dwc->usb3_generic_phy)
+		phy_init(dwc->usb3_generic_phy);
+
 	spin_lock_irqsave(&dwc->lock, flags);
 
 	dwc3_writel(dwc->regs, DWC3_GCTL, dwc->gctl);
diff --git a/drivers/usb/dwc3/core.h b/drivers/usb/dwc3/core.h
index f8af8d4..01ec7d7 100644
--- a/drivers/usb/dwc3/core.h
+++ b/drivers/usb/dwc3/core.h
@@ -31,6 +31,8 @@
 #include <linux/usb/gadget.h>
 #include <linux/usb/otg.h>
 
+#include <linux/phy/phy.h>
+
 /* Global constants */
 #define DWC3_EP0_BOUNCE_SIZE	512
 #define DWC3_ENDPOINTS_NUM	32
@@ -613,6 +615,8 @@ struct dwc3_scratchpad_array {
  * @dr_mode: requested mode of operation
  * @usb2_phy: pointer to USB2 PHY
  * @usb3_phy: pointer to USB3 PHY
+ * @usb2_generic_phy: pointer to USB2 PHY
+ * @usb3_generic_phy: pointer to USB3 PHY
  * @dcfg: saved contents of DCFG register
  * @gctl: saved contents of GCTL register
  * @is_selfpowered: true when we are selfpowered
@@ -665,6 +669,9 @@ struct dwc3 {
 	struct usb_phy		*usb2_phy;
 	struct usb_phy		*usb3_phy;
 
+	struct phy		*usb2_generic_phy;
+	struct phy		*usb3_generic_phy;
+
 	void __iomem		*regs;
 	size_t			regs_size;
 
-- 
1.7.10.4

^ permalink raw reply related

* [PATCH 1/2] usb: dwc3: core: continue probing if usb phy library returns -ENODEV/-ENXIO
From: Kishon Vijay Abraham I @ 2014-01-21 10:11 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <1385373690-12170-1-git-send-email-kishon@ti.com>

Since PHYs for dwc3 is optional (not all SoCs that have DWC3 use PHYs),
do not return from probe if the USB PHY library returns -ENODEV as that
indicates the platform does not have PHY.

Signed-off-by: Kishon Vijay Abraham I <kishon@ti.com>
---
 drivers/usb/dwc3/core.c |   34 ++++++++++++++--------------------
 1 file changed, 14 insertions(+), 20 deletions(-)

diff --git a/drivers/usb/dwc3/core.c b/drivers/usb/dwc3/core.c
index a49217a..e009d4e 100644
--- a/drivers/usb/dwc3/core.c
+++ b/drivers/usb/dwc3/core.c
@@ -411,32 +411,26 @@ static int dwc3_probe(struct platform_device *pdev)
 
 	if (IS_ERR(dwc->usb2_phy)) {
 		ret = PTR_ERR(dwc->usb2_phy);
-
-		/*
-		 * if -ENXIO is returned, it means PHY layer wasn't
-		 * enabled, so it makes no sense to return -EPROBE_DEFER
-		 * in that case, since no PHY driver will ever probe.
-		 */
-		if (ret == -ENXIO)
+		if (ret == -ENXIO || ret == -ENODEV) {
+			dwc->usb2_phy = NULL;
+		} else if (ret == -EPROBE_DEFER) {
 			return ret;
-
-		dev_err(dev, "no usb2 phy configured\n");
-		return -EPROBE_DEFER;
+		} else {
+			dev_err(dev, "no usb2 phy configured\n");
+			return ret;
+		}
 	}
 
 	if (IS_ERR(dwc->usb3_phy)) {
 		ret = PTR_ERR(dwc->usb3_phy);
-
-		/*
-		 * if -ENXIO is returned, it means PHY layer wasn't
-		 * enabled, so it makes no sense to return -EPROBE_DEFER
-		 * in that case, since no PHY driver will ever probe.
-		 */
-		if (ret == -ENXIO)
+		if (ret == -ENXIO || ret == -ENODEV) {
+			dwc->usb3_phy = NULL;
+		} else if (ret == -EPROBE_DEFER) {
 			return ret;
-
-		dev_err(dev, "no usb3 phy configured\n");
-		return -EPROBE_DEFER;
+		} else {
+			dev_err(dev, "no usb3 phy configured\n");
+			return ret;
+		}
 	}
 
 	dwc->xhci_resources[0].start = res->start;
-- 
1.7.10.4

^ permalink raw reply related


This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox