Devicetree
 help / color / mirror / Atom feed
* [PATCH v2 3/20] clk: sunxi-ng: div: Switch to divider_round_rate
From: Maxime Ripard @ 2017-05-03 11:59 UTC (permalink / raw)
  To: Mike Turquette, Stephen Boyd, Chen-Yu Tsai, Maxime Ripard
  Cc: Daniel Vetter, David Airlie,
	dri-devel-PD4FTy7X32lNgt0PjOBp9y5qC8QIuHrW, Mark Rutland,
	Rob Herring, devicetree-u79uwXL29TY76Z2rM5mHXA,
	linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
	linux-clk-u79uwXL29TY76Z2rM5mHXA,
	linux-kernel-u79uwXL29TY76Z2rM5mHXA,
	linux-sunxi-/JYPxA39Uh5TLH3MbocFFw
In-Reply-To: <cover.d697f34bb7b0605c5e76cbc4167a64453e5e0984.1493812478.git-series.maxime.ripard-wi1+55ScJUtKEb57/3fJTNBPR1lH4CV8@public.gmane.org>

divider_round_rate already evaluates changing the parent rate if
CLK_SET_RATE_PARENT is set. Now that we can do that on muxes too, let's
just use it.

Signed-off-by: Maxime Ripard <maxime.ripard-wi1+55ScJUtKEb57/3fJTNBPR1lH4CV8@public.gmane.org>
---
 drivers/clk/sunxi-ng/ccu_div.c | 27 ++++-----------------------
 1 file changed, 4 insertions(+), 23 deletions(-)

diff --git a/drivers/clk/sunxi-ng/ccu_div.c b/drivers/clk/sunxi-ng/ccu_div.c
index a489f18a3c01..419463375bc1 100644
--- a/drivers/clk/sunxi-ng/ccu_div.c
+++ b/drivers/clk/sunxi-ng/ccu_div.c
@@ -20,18 +20,11 @@ static unsigned long ccu_div_round_rate(struct ccu_mux_internal *mux,
 					void *data)
 {
 	struct ccu_div *cd = data;
-	unsigned long val;
-
-	/*
-	 * We can't use divider_round_rate that assumes that there's
-	 * several parents, while we might be called to evaluate
-	 * several different parents.
-	 */
-	val = divider_get_val(rate, *parent_rate, cd->div.table, cd->div.width,
-			      cd->div.flags);
 
-	return divider_recalc_rate(&cd->common.hw, *parent_rate, val,
-				   cd->div.table, cd->div.flags);
+	return divider_round_rate_parent(&cd->common.hw, parent,
+					 rate, parent_rate,
+					 cd->div.table, cd->div.width,
+					 cd->div.flags);
 }
 
 static void ccu_div_disable(struct clk_hw *hw)
@@ -78,18 +71,6 @@ static int ccu_div_determine_rate(struct clk_hw *hw,
 {
 	struct ccu_div *cd = hw_to_ccu_div(hw);
 
-	if (clk_hw_get_num_parents(hw) == 1) {
-		req->rate = divider_round_rate(hw, req->rate,
-					       &req->best_parent_rate,
-					       cd->div.table,
-					       cd->div.width,
-					       cd->div.flags);
-
-		req->best_parent_hw = clk_hw_get_parent(hw);
-
-		return 0;
-	}
-
 	return ccu_mux_helper_determine_rate(&cd->common, &cd->mux,
 					     req, ccu_div_round_rate, cd);
 }
-- 
git-series 0.8.11

^ permalink raw reply related

* [PATCH v2 2/20] clk: sunxi-ng: Pass the parent and a pointer to the clocks round rate
From: Maxime Ripard @ 2017-05-03 11:59 UTC (permalink / raw)
  To: Mike Turquette, Stephen Boyd, Chen-Yu Tsai, Maxime Ripard
  Cc: Daniel Vetter, David Airlie,
	dri-devel-PD4FTy7X32lNgt0PjOBp9y5qC8QIuHrW, Mark Rutland,
	Rob Herring, devicetree-u79uwXL29TY76Z2rM5mHXA,
	linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
	linux-clk-u79uwXL29TY76Z2rM5mHXA,
	linux-kernel-u79uwXL29TY76Z2rM5mHXA,
	linux-sunxi-/JYPxA39Uh5TLH3MbocFFw
In-Reply-To: <cover.d697f34bb7b0605c5e76cbc4167a64453e5e0984.1493812478.git-series.maxime.ripard-wi1+55ScJUtKEb57/3fJTNBPR1lH4CV8@public.gmane.org>

The clocks might need to modify their parent clocks. In order to make that
possible, give them access to the parent clock being evaluated, and to a
pointer to the parent rate so that they can modify it if needed.

Signed-off-by: Maxime Ripard <maxime.ripard-wi1+55ScJUtKEb57/3fJTNBPR1lH4CV8@public.gmane.org>
---
 drivers/clk/sunxi-ng/ccu_div.c  |  7 ++++---
 drivers/clk/sunxi-ng/ccu_mp.c   |  7 ++++---
 drivers/clk/sunxi-ng/ccu_mult.c | 11 ++++++-----
 drivers/clk/sunxi-ng/ccu_mux.c  |  8 +++++---
 drivers/clk/sunxi-ng/ccu_mux.h  |  3 ++-
 drivers/clk/sunxi-ng/ccu_nkm.c  |  7 ++++---
 6 files changed, 25 insertions(+), 18 deletions(-)

diff --git a/drivers/clk/sunxi-ng/ccu_div.c b/drivers/clk/sunxi-ng/ccu_div.c
index 4057e6021aa9..a489f18a3c01 100644
--- a/drivers/clk/sunxi-ng/ccu_div.c
+++ b/drivers/clk/sunxi-ng/ccu_div.c
@@ -14,7 +14,8 @@
 #include "ccu_div.h"
 
 static unsigned long ccu_div_round_rate(struct ccu_mux_internal *mux,
-					unsigned long parent_rate,
+					struct clk_hw *parent,
+					unsigned long *parent_rate,
 					unsigned long rate,
 					void *data)
 {
@@ -26,10 +27,10 @@ static unsigned long ccu_div_round_rate(struct ccu_mux_internal *mux,
 	 * several parents, while we might be called to evaluate
 	 * several different parents.
 	 */
-	val = divider_get_val(rate, parent_rate, cd->div.table, cd->div.width,
+	val = divider_get_val(rate, *parent_rate, cd->div.table, cd->div.width,
 			      cd->div.flags);
 
-	return divider_recalc_rate(&cd->common.hw, parent_rate, val,
+	return divider_recalc_rate(&cd->common.hw, *parent_rate, val,
 				   cd->div.table, cd->div.flags);
 }
 
diff --git a/drivers/clk/sunxi-ng/ccu_mp.c b/drivers/clk/sunxi-ng/ccu_mp.c
index b583f186a804..de02e6c386d8 100644
--- a/drivers/clk/sunxi-ng/ccu_mp.c
+++ b/drivers/clk/sunxi-ng/ccu_mp.c
@@ -41,7 +41,8 @@ static void ccu_mp_find_best(unsigned long parent, unsigned long rate,
 }
 
 static unsigned long ccu_mp_round_rate(struct ccu_mux_internal *mux,
-				       unsigned long parent_rate,
+				       struct clk_hw *hw,
+				       unsigned long *parent_rate,
 				       unsigned long rate,
 				       void *data)
 {
@@ -52,9 +53,9 @@ static unsigned long ccu_mp_round_rate(struct ccu_mux_internal *mux,
 	max_m = cmp->m.max ?: 1 << cmp->m.width;
 	max_p = cmp->p.max ?: 1 << ((1 << cmp->p.width) - 1);
 
-	ccu_mp_find_best(parent_rate, rate, max_m, max_p, &m, &p);
+	ccu_mp_find_best(*parent_rate, rate, max_m, max_p, &m, &p);
 
-	return parent_rate / p / m;
+	return *parent_rate / p / m;
 }
 
 static void ccu_mp_disable(struct clk_hw *hw)
diff --git a/drivers/clk/sunxi-ng/ccu_mult.c b/drivers/clk/sunxi-ng/ccu_mult.c
index 671141359895..6ee7ba0738fb 100644
--- a/drivers/clk/sunxi-ng/ccu_mult.c
+++ b/drivers/clk/sunxi-ng/ccu_mult.c
@@ -33,9 +33,10 @@ static void ccu_mult_find_best(unsigned long parent, unsigned long rate,
 }
 
 static unsigned long ccu_mult_round_rate(struct ccu_mux_internal *mux,
-					unsigned long parent_rate,
-					unsigned long rate,
-					void *data)
+					 struct clk_hw *parent,
+					 unsigned long *parent_rate,
+					 unsigned long rate,
+					 void *data)
 {
 	struct ccu_mult *cm = data;
 	struct _ccu_mult _cm;
@@ -47,9 +48,9 @@ static unsigned long ccu_mult_round_rate(struct ccu_mux_internal *mux,
 	else
 		_cm.max = (1 << cm->mult.width) + cm->mult.offset - 1;
 
-	ccu_mult_find_best(parent_rate, rate, &_cm);
+	ccu_mult_find_best(*parent_rate, rate, &_cm);
 
-	return parent_rate * _cm.mult;
+	return *parent_rate * _cm.mult;
 }
 
 static void ccu_mult_disable(struct clk_hw *hw)
diff --git a/drivers/clk/sunxi-ng/ccu_mux.c b/drivers/clk/sunxi-ng/ccu_mux.c
index c6bb1f523232..bae735e252b6 100644
--- a/drivers/clk/sunxi-ng/ccu_mux.c
+++ b/drivers/clk/sunxi-ng/ccu_mux.c
@@ -61,7 +61,8 @@ int ccu_mux_helper_determine_rate(struct ccu_common *common,
 				  struct ccu_mux_internal *cm,
 				  struct clk_rate_request *req,
 				  unsigned long (*round)(struct ccu_mux_internal *,
-							 unsigned long,
+							 struct clk_hw *,
+							 unsigned long *,
 							 unsigned long,
 							 void *),
 				  void *data)
@@ -80,7 +81,8 @@ int ccu_mux_helper_determine_rate(struct ccu_common *common,
 		ccu_mux_helper_adjust_parent_for_prediv(common, cm, -1,
 							&adj_parent_rate);
 
-		best_rate = round(cm, adj_parent_rate, req->rate, data);
+		best_rate = round(cm, best_parent, &adj_parent_rate,
+				  req->rate, data);
 
 		goto out;
 	}
@@ -109,7 +111,7 @@ int ccu_mux_helper_determine_rate(struct ccu_common *common,
 		ccu_mux_helper_adjust_parent_for_prediv(common, cm, i,
 							&adj_parent_rate);
 
-		tmp_rate = round(cm, adj_parent_rate, req->rate, data);
+		tmp_rate = round(cm, parent, &adj_parent_rate, req->rate, data);
 		if (tmp_rate == req->rate) {
 			best_parent = parent;
 			best_parent_rate = parent_rate;
diff --git a/drivers/clk/sunxi-ng/ccu_mux.h b/drivers/clk/sunxi-ng/ccu_mux.h
index 47aba3a48245..4be56eee2bfd 100644
--- a/drivers/clk/sunxi-ng/ccu_mux.h
+++ b/drivers/clk/sunxi-ng/ccu_mux.h
@@ -86,7 +86,8 @@ int ccu_mux_helper_determine_rate(struct ccu_common *common,
 				  struct ccu_mux_internal *cm,
 				  struct clk_rate_request *req,
 				  unsigned long (*round)(struct ccu_mux_internal *,
-							 unsigned long,
+							 struct clk_hw *,
+							 unsigned long *,
 							 unsigned long,
 							 void *),
 				  void *data);
diff --git a/drivers/clk/sunxi-ng/ccu_nkm.c b/drivers/clk/sunxi-ng/ccu_nkm.c
index cba84afe1cf1..44b16dc8fea6 100644
--- a/drivers/clk/sunxi-ng/ccu_nkm.c
+++ b/drivers/clk/sunxi-ng/ccu_nkm.c
@@ -102,7 +102,8 @@ static unsigned long ccu_nkm_recalc_rate(struct clk_hw *hw,
 }
 
 static unsigned long ccu_nkm_round_rate(struct ccu_mux_internal *mux,
-					unsigned long parent_rate,
+					struct clk_hw *hw,
+					unsigned long *parent_rate,
 					unsigned long rate,
 					void *data)
 {
@@ -116,9 +117,9 @@ static unsigned long ccu_nkm_round_rate(struct ccu_mux_internal *mux,
 	_nkm.min_m = 1;
 	_nkm.max_m = nkm->m.max ?: 1 << nkm->m.width;
 
-	ccu_nkm_find_best(parent_rate, rate, &_nkm);
+	ccu_nkm_find_best(*parent_rate, rate, &_nkm);
 
-	return parent_rate * _nkm.n * _nkm.k / _nkm.m;
+	return *parent_rate * _nkm.n * _nkm.k / _nkm.m;
 }
 
 static int ccu_nkm_determine_rate(struct clk_hw *hw,
-- 
git-series 0.8.11

^ permalink raw reply related

* [PATCH v2 1/20] clk: divider: Make divider_round_rate take the parent clock
From: Maxime Ripard @ 2017-05-03 11:59 UTC (permalink / raw)
  To: Mike Turquette, Stephen Boyd, Chen-Yu Tsai, Maxime Ripard
  Cc: Mark Rutland, devicetree, linux-kernel, dri-devel, linux-sunxi,
	Rob Herring, Daniel Vetter, linux-clk, linux-arm-kernel
In-Reply-To: <cover.d697f34bb7b0605c5e76cbc4167a64453e5e0984.1493812478.git-series.maxime.ripard@free-electrons.com>

So far, divider_round_rate only considers the parent clock returned by
clk_hw_get_parent.

This works fine on clocks that have a single parents, this doesn't work on
muxes, since we will only consider the first parent, while other parents
may totally be able to provide a better combination.

Clocks in that case cannot use divider_round_rate, so would have to come up
with a very similar logic to work around it. Instead of having to do
something like this, and duplicate that logic everywhere, create a
divider_round_rate parent to allow caller to give an additional parameter
for the parent clock to consider.

Signed-off-by: Maxime Ripard <maxime.ripard@free-electrons.com>
---
 drivers/clk/clk-divider.c    | 26 ++++++++++++++++++--------
 include/linux/clk-provider.h |  4 ++++
 2 files changed, 22 insertions(+), 8 deletions(-)

diff --git a/drivers/clk/clk-divider.c b/drivers/clk/clk-divider.c
index 96386ffc8483..48750439b1cd 100644
--- a/drivers/clk/clk-divider.c
+++ b/drivers/clk/clk-divider.c
@@ -275,7 +275,8 @@ static int _next_div(const struct clk_div_table *table, int div,
 	return div;
 }
 
-static int clk_divider_bestdiv(struct clk_hw *hw, unsigned long rate,
+static int clk_divider_bestdiv(struct clk_hw *hw, struct clk_hw *parent,
+			       unsigned long rate,
 			       unsigned long *best_parent_rate,
 			       const struct clk_div_table *table, u8 width,
 			       unsigned long flags)
@@ -314,8 +315,7 @@ static int clk_divider_bestdiv(struct clk_hw *hw, unsigned long rate,
 			*best_parent_rate = parent_rate_saved;
 			return i;
 		}
-		parent_rate = clk_hw_round_rate(clk_hw_get_parent(hw),
-					       rate * i);
+		parent_rate = clk_hw_round_rate(parent, rate * i);
 		now = DIV_ROUND_UP_ULL((u64)parent_rate, i);
 		if (_is_best_div(rate, now, best, flags)) {
 			bestdiv = i;
@@ -326,22 +326,32 @@ static int clk_divider_bestdiv(struct clk_hw *hw, unsigned long rate,
 
 	if (!bestdiv) {
 		bestdiv = _get_maxdiv(table, width, flags);
-		*best_parent_rate = clk_hw_round_rate(clk_hw_get_parent(hw), 1);
+		*best_parent_rate = clk_hw_round_rate(parent, 1);
 	}
 
 	return bestdiv;
 }
 
-long divider_round_rate(struct clk_hw *hw, unsigned long rate,
-			unsigned long *prate, const struct clk_div_table *table,
-			u8 width, unsigned long flags)
+long divider_round_rate_parent(struct clk_hw *hw, struct clk_hw *parent,
+			       unsigned long rate, unsigned long *prate,
+			       const struct clk_div_table *table,
+			       u8 width, unsigned long flags)
 {
 	int div;
 
-	div = clk_divider_bestdiv(hw, rate, prate, table, width, flags);
+	div = clk_divider_bestdiv(hw, parent, rate, prate, table, width, flags);
 
 	return DIV_ROUND_UP_ULL((u64)*prate, div);
 }
+EXPORT_SYMBOL_GPL(divider_round_rate_parent);
+
+long divider_round_rate(struct clk_hw *hw, unsigned long rate,
+			unsigned long *prate, const struct clk_div_table *table,
+			u8 width, unsigned long flags)
+{
+	return divider_round_rate_parent(hw, clk_hw_get_parent(hw), rate, prate,
+					 table, width, flags);
+}
 EXPORT_SYMBOL_GPL(divider_round_rate);
 
 static long clk_divider_round_rate(struct clk_hw *hw, unsigned long rate,
diff --git a/include/linux/clk-provider.h b/include/linux/clk-provider.h
index a428aec36ace..14102f783f64 100644
--- a/include/linux/clk-provider.h
+++ b/include/linux/clk-provider.h
@@ -412,6 +412,10 @@ extern const struct clk_ops clk_divider_ro_ops;
 unsigned long divider_recalc_rate(struct clk_hw *hw, unsigned long parent_rate,
 		unsigned int val, const struct clk_div_table *table,
 		unsigned long flags);
+long divider_round_rate_parent(struct clk_hw *hw, struct clk_hw *parent,
+			       unsigned long rate, unsigned long *prate,
+			       const struct clk_div_table *table,
+			       u8 width, unsigned long flags);
 long divider_round_rate(struct clk_hw *hw, unsigned long rate,
 		unsigned long *prate, const struct clk_div_table *table,
 		u8 width, unsigned long flags);
-- 
git-series 0.8.11
_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/dri-devel

^ permalink raw reply related

* [PATCH v2 0/20] drm: sun4i: Add support for the HDMI controller
From: Maxime Ripard @ 2017-05-03 11:59 UTC (permalink / raw)
  To: Mike Turquette, Stephen Boyd, Chen-Yu Tsai, Maxime Ripard
  Cc: Daniel Vetter, David Airlie,
	dri-devel-PD4FTy7X32lNgt0PjOBp9y5qC8QIuHrW, Mark Rutland,
	Rob Herring, devicetree-u79uwXL29TY76Z2rM5mHXA,
	linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
	linux-clk-u79uwXL29TY76Z2rM5mHXA,
	linux-kernel-u79uwXL29TY76Z2rM5mHXA,
	linux-sunxi-/JYPxA39Uh5TLH3MbocFFw

Hi,

Here is an attempt at getting the HDMI controller running.

This HDMI controller is found on a number of old Allwinner SoCs (A10, A10s,
A20, A31).

This driver only supports for now the A10s because it was an easy target,
being very close to the A13 that is already supported by our DRM driver.

There's nothing out of the extraordinary there, except maybe the clock
setup. All the internal clocks (TMDS, DDC) have been modeled using the
common clock framework, the TMDS clock being the parent of the DDC one.

While this might sound overkill, other SoC have a different, external
source for the DDC clock, which will be easier to support through the clock
framework.

The IP also supports audio (through an already supported i2s controller,
and some missing configuration in the HDMI controller) and CEC. Both will
come eventually.

Let me know what you think!
Maxime

Changes from v1:
  - Fixed typos in the CCU header and the HDMI code
  - Reintroduced the comment for the backporch timings
  - Renamed the hdmi node to hdmi, instead of hdmi0
  - Added support for hdmi-connector
  - Added a separate Kconfig option for the HDMI support
  - Changed the TCON muxing configuration for an explicit call in the
    TCON's "clients"
  - Fixed the initialisation sequence that was clearing the clocks bits
  - Constified the HDMI's structures and removed whitespaces errors
  - Fixed an issue in the sunxi-ng code that was not reporting the proper
    parent clock rate if it was modified
  - Removed unused headers
  - Removed CLK_SET_RATE_PARENT for the DDC clock
  - Used the DDC address defines
  - Removed the interlace flag that wasn't supported at the moment
  - Moved most of the HDMI encoder init to the bind function like we do for
    the other encoders
  - Switched to drm_of_find_possible_crtcs
  - Removed the extra printk that were still in my code
  - Rebased on top of linux-next
  - Removed the patch changing the divider_round_rate prototype to
    introduce a new function instead that takes the parent clock to
    evaluate
  - Added a clk_set_rate for the hdmi module clock
  - Fixed the V_TOTAL TCON ch0 calculation to be consistent with ch1's
  - Defined all registers, and remove the TODOs
  - Fixed the EDID issues by increasing the timeout.
  - Added an atomic_check to prevent the DBLCLK modes to be used, as it is
    not supported yet
  - Updated the binding to add the interrupts and DMA channels

Maxime Ripard (20):
  clk: divider: Make divider_round_rate take the parent clock
  clk: sunxi-ng: Pass the parent and a pointer to the clocks round rate
  clk: sunxi-ng: div: Switch to divider_round_rate
  clk: sunxi-ng: mux: Don't just rely on the parent for CLK_SET_RATE_PARENT
  clk: sunxi-ng: mux: split out the pre-divider computation code
  clk: sunxi-ng: mux: Change pre-divider application function prototype
  clk: sunxi-ng: mux: Re-adjust parent rate
  clk: sunxi-ng: sun5i: Export video PLLs
  drm/sun4i: tcon: Add channel debug
  drm/sun4i: tcon: Move the muxing out of the mode set function
  drm/sun4i: tcon: Switch mux on only for composite
  drm/sun4i: tcon: Fix tcon channel 1 backporch calculation
  drm/sun4i: tcon: Change vertical total size computation inconsistency
  drm/sun4i: tcon: multiply the vtotal when not in interlace
  drm/sun4i: Ignore the generic connectors for components
  dt-bindings: display: sun4i: Add HDMI display bindings
  dt-bindings: display: sun4i: Add allwinner,tcon-channel property
  drm/sun4i: Add HDMI support
  ARM: sun5i: a10s: Add the HDMI controller node
  ARM: sun5i: a10s-olinuxino: Enable HDMI

 Documentation/devicetree/bindings/display/sunxi/sun4i-drm.txt |  90 +-
 arch/arm/boot/dts/sun5i-a10s-olinuxino-micro.dts              |  29 +-
 arch/arm/boot/dts/sun5i-a10s.dtsi                             |  50 +-
 arch/arm/boot/dts/sun5i.dtsi                                  |   1 +-
 drivers/clk/clk-divider.c                                     |  26 +-
 drivers/clk/sunxi-ng/ccu-sun5i.h                              |   6 +-
 drivers/clk/sunxi-ng/ccu_div.c                                |  38 +-
 drivers/clk/sunxi-ng/ccu_mp.c                                 |  15 +-
 drivers/clk/sunxi-ng/ccu_mult.c                               |  19 +-
 drivers/clk/sunxi-ng/ccu_mux.c                                |  90 +-
 drivers/clk/sunxi-ng/ccu_mux.h                                |  11 +-
 drivers/clk/sunxi-ng/ccu_nkm.c                                |   7 +-
 drivers/gpu/drm/sun4i/Kconfig                                 |   9 +-
 drivers/gpu/drm/sun4i/Makefile                                |   6 +-
 drivers/gpu/drm/sun4i/sun4i_drv.c                             |   8 +-
 drivers/gpu/drm/sun4i/sun4i_hdmi.h                            | 157 ++-
 drivers/gpu/drm/sun4i/sun4i_hdmi_ddc_clk.c                    | 127 ++-
 drivers/gpu/drm/sun4i/sun4i_hdmi_enc.c                        | 493 +++++++-
 drivers/gpu/drm/sun4i/sun4i_hdmi_tmds_clk.c                   | 225 +++-
 drivers/gpu/drm/sun4i/sun4i_rgb.c                             |   1 +-
 drivers/gpu/drm/sun4i/sun4i_tcon.c                            |  43 +-
 drivers/gpu/drm/sun4i/sun4i_tcon.h                            |   4 +-
 drivers/gpu/drm/sun4i/sun4i_tv.c                              |   1 +-
 include/dt-bindings/clock/sun5i-ccu.h                         |   3 +-
 include/linux/clk-provider.h                                  |   4 +-
 25 files changed, 1347 insertions(+), 116 deletions(-)
 create mode 100644 drivers/gpu/drm/sun4i/sun4i_hdmi.h
 create mode 100644 drivers/gpu/drm/sun4i/sun4i_hdmi_ddc_clk.c
 create mode 100644 drivers/gpu/drm/sun4i/sun4i_hdmi_enc.c
 create mode 100644 drivers/gpu/drm/sun4i/sun4i_hdmi_tmds_clk.c

base-commit: ee91aaf669ac4fbb09283958f69d57dcad5c4963
-- 
git-series 0.8.11

^ permalink raw reply

* Re: [PATCH V6 1/9] PM / OPP: Introduce "power-domain-opp" property
From: Sudeep Holla @ 2017-05-03 11:29 UTC (permalink / raw)
  To: Rob Herring, Viresh Kumar
  Cc: Sudeep Holla, Rafael Wysocki, ulf.hansson, Kevin Hilman,
	Viresh Kumar, Nishanth Menon, Stephen Boyd, linaro-kernel,
	linux-pm, linux-kernel, Vincent Guittot, lina.iyer, rnayak,
	devicetree
In-Reply-To: <20170428204803.plu467okibtxga4d@rob-hp-laptop>



On 28/04/17 21:48, Rob Herring wrote:
> On Wed, Apr 26, 2017 at 04:27:05PM +0530, Viresh Kumar wrote:
>> Power-domains need to express their active states in DT and the devices
>> within the power-domain need to express their dependency on those active
>> states. The power-domains can use the OPP tables without any
>> modifications to the bindings.
>>
>> Add a new property "power-domain-opp", which will contain phandle to the
>> OPP node of the parent power domain. This is required for devices which
>> have dependency on the configured active state of the power domain for
>> their working.
>>
>> For some platforms the actual frequency and voltages of the power
>> domains are managed by the firmware and are so hidden from the high
>> level operating system. The "opp-hz" property is relaxed a bit to
>> contain indexes instead of actual frequency values to support such
>> platforms.
>>
>> Signed-off-by: Viresh Kumar <viresh.kumar@linaro.org>
>> ---
>>  Documentation/devicetree/bindings/opp/opp.txt | 74 ++++++++++++++++++++++++++-
>>  1 file changed, 73 insertions(+), 1 deletion(-)
>>
>> diff --git a/Documentation/devicetree/bindings/opp/opp.txt b/Documentation/devicetree/bindings/opp/opp.txt
>> index 63725498bd20..6e30cae2a936 100644
>> --- a/Documentation/devicetree/bindings/opp/opp.txt
>> +++ b/Documentation/devicetree/bindings/opp/opp.txt
>> @@ -77,7 +77,10 @@ This defines voltage-current-frequency combinations along with other related
>>  properties.
>>  
>>  Required properties:
>> -- opp-hz: Frequency in Hz, expressed as a 64-bit big-endian integer.
>> +- opp-hz: Frequency in Hz, expressed as a 64-bit big-endian integer. In some
>> +  cases the exact frequency in Hz may be hidden from the OS by the firmware and
>> +  this field may contain values that represent the frequency in a firmware
>> +  dependent way, for example an index of an array in the firmware.
> 
> Not really sure OPP binding makes sense here. What about all the other 
> properties. We expose voltage, but not freq?
> 

I completely agree with that and I have been pushing this to be
represented as just regulators[0]. Mark B seem to dislike that
idea [1]

-- 
Regards,
Sudeep

[0] http://www.spinics.net/lists/devicetree/msg174725.html
[1] http://www.spinics.net/lists/devicetree/msg175113.html

^ permalink raw reply

* Re: [PATCH V4 1/9] PM / OPP: Allow OPP table to be used for power-domains
From: Sudeep Holla @ 2017-05-03 11:21 UTC (permalink / raw)
  To: Mark Brown, robh+dt
  Cc: Sudeep Holla, Rajendra Nayak, Viresh Kumar, Rafael Wysocki,
	ulf.hansson, Kevin Hilman, Viresh Kumar, Nishanth Menon,
	Stephen Boyd, linaro-kernel, linux-pm, linux-kernel,
	Vincent Guittot, lina.iyer, devicetree
In-Reply-To: <20170430124917.4m3d237pik7q47f3@sirena.org.uk>



On 30/04/17 13:49, Mark Brown wrote:
> On Thu, Apr 27, 2017 at 10:42:49AM +0100, Sudeep Holla wrote:
>> On 26/04/17 14:55, Mark Brown wrote:
> 
>>> As I'm getting fed up of saying: if the values you are setting are not
>>> voltages and do not behave like voltages then the hardware should not be
>>> represented as a voltage regulator since if they are represented as
>>> voltage regulators things will expect to be able to control them as
>>> voltage regulators.  This hardware is quite clearly providing OPPs
>>> directly, I would expect this to be handled in the OPP code somehow.
> 
>> I agree with you that we need to be absolutely sure on what it actually
>> represents.
> 
>> But as more and more platform are pushing such power controls to
>> dedicated M3 or similar processors, we need abstraction. Though we are
>> controlling hardware, we do so indirectly. Since there were discussions
>> around device tree representing hardware vs platform, I tend to think,
>> we are moving towards platform(something similar to ACPI).
> 
> I don't think there's a meaningful hardware/platform distinction here -
> in terms of what DT is describing the platform bit is just what the
> hardware (the microcontrollers) happen to do, 
> 

Yes agreed. It's similar to PSCI or any other platform firmware IMO.

The question is how do we deal with such controls that needs to be done
via the firmware ? We generally plug-in to the existing framework in
Linux using the existing bindings. Most of the time, much simpler
bindings than the one that present complete hardware description.

> DT doesn't much care about that though.

No sure about that, may be doesn't care about the internals, but we need
to care about interface, no ?

-- 
Regards,
Sudeep

^ permalink raw reply

* Re: [PATCH 2/2] [media] platform: add video-multiplexer subdevice driver
From: Peter Rosin @ 2017-05-03 11:13 UTC (permalink / raw)
  To: Philipp Zabel
  Cc: linux-media, Mark Rutland, devicetree, Steve Longerbeam, kernel,
	Sascha Hauer, Rob Herring, Sakari Ailus, Pavel Machek,
	Steve Longerbeam, Vladimir Zapolskiy
In-Reply-To: <1493800556.3599.15.camel@pengutronix.de>

On 2017-05-03 10:35, Philipp Zabel wrote:
> On Tue, 2017-05-02 at 19:42 +0200, Peter Rosin wrote:
>> On 2017-05-02 17:21, Philipp Zabel wrote:
>>> Thank you, I've resent a version with a mutex lock around vmux->active.
>>
>> I had a bunch of ifs in the above message, so I'm not sure it's needed.
>> I would expect there to be a lock outside somewhere in the media layer.
>> A cursory look gets me to media-entity.c and media_entity_setup_link()
>> which does have a mutex. But I'm no media expert, so maybe there are other
>> ways of getting to video_mux_link_setup that I'm not aware of?
> 
> link_setup is always called under the graph mutex of the /dev/media
> device. That is why I didn't think about locking too hard. In fact, I
> initially wrote this expecting mux_control_get_exclusive to exist and
> the mux select/deselect not to be locked at all.
> 
> But set_format is called from an unlocked ioctl on a /dev/v4l-subdev
> device. Until your comments I didn't notice that it would be possible to
> let link_setup set active = -1 in the middle of the set_format call,
> causing it to return garbage.

Obviously, now that you point it out. I completely missed set_format...

Cheers,
peda

^ permalink raw reply

* [PATCHv2 2/2] ARM: dts: omap4-droid4: Add vibrator
From: Sebastian Reichel @ 2017-05-03 11:11 UTC (permalink / raw)
  To: Sebastian Reichel, Dmitry Torokhov, Tony Lindgren
  Cc: Rob Herring, linux-input, linux-omap, devicetree, linux-kernel,
	Sebastian Reichel
In-Reply-To: <20170503111128.15385-1-sebastian.reichel@collabora.co.uk>

Add vibrator to Droid4's device tree.

Signed-off-by: Sebastian Reichel <sebastian.reichel@collabora.co.uk>
---
 arch/arm/boot/dts/omap4-droid4-xt894.dts | 38 ++++++++++++++++++++++++++++++++
 1 file changed, 38 insertions(+)

diff --git a/arch/arm/boot/dts/omap4-droid4-xt894.dts b/arch/arm/boot/dts/omap4-droid4-xt894.dts
index 89eb607f4a9e..67d286a53368 100644
--- a/arch/arm/boot/dts/omap4-droid4-xt894.dts
+++ b/arch/arm/boot/dts/omap4-droid4-xt894.dts
@@ -116,6 +116,32 @@
 
 		};
 	};
+
+	pwm8: dmtimer-pwm-8 {
+		pinctrl-names = "default";
+		pinctrl-0 = <&vibrator_direction_pin>;
+
+		compatible = "ti,omap-dmtimer-pwm";
+		#pwm-cells = <3>;
+		ti,timers = <&timer8>;
+		ti,clock-source = <0x01>;
+	};
+
+	pwm9: dmtimer-pwm-9 {
+		pinctrl-names = "default";
+		pinctrl-0 = <&vibrator_enable_pin>;
+
+		compatible = "ti,omap-dmtimer-pwm";
+		#pwm-cells = <3>;
+		ti,timers = <&timer9>;
+		ti,clock-source = <0x01>;
+	};
+
+	vibrator {
+		compatible = "motorola,mapphone-pwm-vibrator";
+		pwms = <&pwm9 0 1000000000 0>, <&pwm8 0 1000000000 0>;
+		pwm-names = "enable", "direction";
+	};
 };
 
 &dss {
@@ -453,6 +479,18 @@
 		OMAP4_IOPAD(0x1c8, PIN_INPUT_PULLUP | MUX_MODE7)
 		>;
 	};
+
+	vibrator_direction_pin: pinmux_vibrator_direction_pin {
+		pinctrl-single,pins = <
+			OMAP4_IOPAD(0x1ce, PIN_OUTPUT | MUX_MODE1)	/* dmtimer8_pwm_evt (gpio_27) */
+		>;
+	};
+
+	vibrator_enable_pin: pinmux_vibrator_enable_pin {
+		pinctrl-single,pins = <
+			OMAP4_IOPAD(0X1d0, PIN_OUTPUT | MUX_MODE1)	/* dmtimer9_pwm_evt (gpio_28) */
+		>;
+	};
 };
 
 &omap4_pmx_wkup {
-- 
2.11.0


^ permalink raw reply related

* [PATCHv2 1/2] Input: pwm-vibra: new driver
From: Sebastian Reichel @ 2017-05-03 11:11 UTC (permalink / raw)
  To: Sebastian Reichel, Dmitry Torokhov, Tony Lindgren
  Cc: Rob Herring, linux-input-u79uwXL29TY76Z2rM5mHXA,
	linux-omap-u79uwXL29TY76Z2rM5mHXA,
	devicetree-u79uwXL29TY76Z2rM5mHXA,
	linux-kernel-u79uwXL29TY76Z2rM5mHXA, Sebastian Reichel
In-Reply-To: <20170503111128.15385-1-sebastian.reichel-ZGY8ohtN/8pPYcu2f3hruQ@public.gmane.org>

Provide a simple driver for PWM controllable vibrators. It
will be used by Motorola Droid 4.

Signed-off-by: Sebastian Reichel <sebastian.reichel-ZGY8ohtN/8pPYcu2f3hruQ@public.gmane.org>
---
Changes since PATCHv1:
 - move driver removal code to input->close function
 - mark PM functions __maybe_unused and drop #ifdef CONFIG_PM_SLEEP
 - remove duplicate NULL check for vibrator in probe function
 - cancel work in suspend function
---
 .../devicetree/bindings/input/pwm-vibrator.txt     |  60 ++++
 drivers/input/misc/Kconfig                         |  11 +
 drivers/input/misc/Makefile                        |   1 +
 drivers/input/misc/pwm-vibra.c                     | 343 +++++++++++++++++++++
 4 files changed, 415 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/input/pwm-vibrator.txt
 create mode 100644 drivers/input/misc/pwm-vibra.c

diff --git a/Documentation/devicetree/bindings/input/pwm-vibrator.txt b/Documentation/devicetree/bindings/input/pwm-vibrator.txt
new file mode 100644
index 000000000000..c35be4691366
--- /dev/null
+++ b/Documentation/devicetree/bindings/input/pwm-vibrator.txt
@@ -0,0 +1,60 @@
+* PWM vibrator device tree bindings
+
+Registers a PWM device as vibrator.
+
+Required properties:
+- compatible: should be
+  * "pwm-vibrator"
+    For vibrators controlled using the PWM channel's duty cycle (higher duty means
+    the vibrator becomes stronger).
+  * "motorola,mapphone-pwm-vibrator"
+     For vibrator found in Motorola Droid 4. This vibrator generates a pulse for
+     every rising edge, so its controlled using a duty cycle of 50% and changing
+     the period time.
+- pwm-names: Should contain "enable" and optionally "direction"
+- pwms: Should contain a PWM handle for each entry in pwm-names
+
+Example from Motorola Droid 4:
+
+&omap4_pmx_core {
+	vibrator_direction_pin: pinmux_vibrator_direction_pin {
+		pinctrl-single,pins = <
+		OMAP4_IOPAD(0x1ce, PIN_OUTPUT | MUX_MODE1) /* dmtimer8_pwm_evt (gpio_27) */
+		>;
+	};
+
+	vibrator_enable_pin: pinmux_vibrator_enable_pin {
+		pinctrl-single,pins = <
+		OMAP4_IOPAD(0X1d0, PIN_OUTPUT | MUX_MODE1) /* dmtimer9_pwm_evt (gpio_28) */
+		>;
+	};
+};
+
+/ {
+	pwm8: dmtimer-pwm {
+		pinctrl-names = "default";
+		pinctrl-0 = <&vibrator_direction_pin>;
+
+		compatible = "ti,omap-dmtimer-pwm";
+		#pwm-cells = <3>;
+		ti,timers = <&timer8>;
+		ti,clock-source = <0x01>;
+	};
+
+	pwm9: dmtimer-pwm {
+		pinctrl-names = "default";
+		pinctrl-0 = <&vibrator_enable_pin>;
+
+		compatible = "ti,omap-dmtimer-pwm";
+		#pwm-cells = <3>;
+		ti,timers = <&timer9>;
+		ti,clock-source = <0x01>;
+	};
+
+	vibrator {
+		compatible = "pwm-vibrator";
+		pwms = <&pwm8 0 1000000000 0>,
+		       <&pwm9 0 1000000000 0>;
+		pwm-names = "enable", "direction";
+	};
+};
diff --git a/drivers/input/misc/Kconfig b/drivers/input/misc/Kconfig
index 5b6c52210d20..d8e0b25eb217 100644
--- a/drivers/input/misc/Kconfig
+++ b/drivers/input/misc/Kconfig
@@ -571,6 +571,17 @@ config INPUT_PWM_BEEPER
 	  To compile this driver as a module, choose M here: the module will be
 	  called pwm-beeper.
 
+config INPUT_PWM_VIBRA
+	tristate "PWM vibrator support"
+	depends on PWM
+	help
+	  Say Y here to get support for PWM based vibrator devices.
+
+	  If unsure, say N.
+
+	  To compile this driver as a module, choose M here: the module will be
+	  called pwm-vibra.
+
 config INPUT_GPIO_ROTARY_ENCODER
 	tristate "Rotary encoders connected to GPIO pins"
 	depends on GPIOLIB || COMPILE_TEST
diff --git a/drivers/input/misc/Makefile b/drivers/input/misc/Makefile
index b10523f2878e..9a6517f5458c 100644
--- a/drivers/input/misc/Makefile
+++ b/drivers/input/misc/Makefile
@@ -58,6 +58,7 @@ obj-$(CONFIG_INPUT_PM8XXX_VIBRATOR)	+= pm8xxx-vibrator.o
 obj-$(CONFIG_INPUT_PMIC8XXX_PWRKEY)	+= pmic8xxx-pwrkey.o
 obj-$(CONFIG_INPUT_POWERMATE)		+= powermate.o
 obj-$(CONFIG_INPUT_PWM_BEEPER)		+= pwm-beeper.o
+obj-$(CONFIG_INPUT_PWM_VIBRA)		+= pwm-vibra.o
 obj-$(CONFIG_INPUT_RB532_BUTTON)	+= rb532_button.o
 obj-$(CONFIG_INPUT_REGULATOR_HAPTIC)	+= regulator-haptic.o
 obj-$(CONFIG_INPUT_RETU_PWRBUTTON)	+= retu-pwrbutton.o
diff --git a/drivers/input/misc/pwm-vibra.c b/drivers/input/misc/pwm-vibra.c
new file mode 100644
index 000000000000..878a483f93ff
--- /dev/null
+++ b/drivers/input/misc/pwm-vibra.c
@@ -0,0 +1,343 @@
+/*
+ *  PWM vibrator driver
+ *
+ *  Copyright (C) 2017 Collabora Ltd.
+ *
+ *  Based on previous work from:
+ *  Copyright (C) 2012 Dmitry Torokhov <dmitry.torokhov-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org>
+ *
+ *  Based on PWM beeper driver:
+ *  Copyright (C) 2010, Lars-Peter Clausen <lars-Qo5EllUWu/uELgA04lAiVw@public.gmane.org>
+ *
+ *  This program is free software; you can redistribute it and/or modify it
+ *  under  the terms of the GNU General  Public License as published by the
+ *  Free Software Foundation;  either version 2 of the License, or (at your
+ *  option) any later version.
+ */
+
+#define DEBUG
+
+#include <linux/input.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/of_device.h>
+#include <linux/platform_device.h>
+#include <linux/pwm.h>
+#include <linux/regulator/consumer.h>
+#include <linux/slab.h>
+
+/**
+ * Motorola Droid 4 (also known as mapphone), has a vibrator, which pulses
+ * 1x on rising edge. Increasing the pwm period results in more pulses per
+ * second, but reduces intensity. There is also a second channel to control
+ * the vibrator's rotation direction to increase effect. The following
+ * numbers were determined manually. Going below 12.5 Hz means, clearly
+ * noticeable pauses and at 30 Hz the vibration is just barely noticable
+ * anymore.
+ */
+#define MAPPHONE_MIN_FREQ 125 /* 12.5 Hz */
+#define MAPPHONE_MAX_FREQ 300 /* 30.0 Hz */
+
+struct pwm_vibrator_hw {
+	void (*setup_pwm)(u16 level, struct pwm_state *);
+	void (*setup_pwm_dir)(u16 level, struct pwm_state *);
+};
+
+struct pwm_vibrator {
+	struct input_dev *input;
+	struct pwm_device *pwm;
+	struct pwm_device *pwm_dir;
+	struct regulator *vcc;
+
+	struct work_struct play_work;
+	u16 level;
+
+	const struct pwm_vibrator_hw *hw;
+};
+
+static void pwm_vibrator_setup_generic(u16 level, struct pwm_state *state)
+{
+	/* period is configured by platform, duty cycle controls strength */
+	pwm_set_relative_duty_cycle(state, level, 0xffff);
+}
+
+static void pwm_vibrator_setup_dir_generic(u16 level, struct pwm_state *state)
+{
+	/* period is configured by platform, duty cycle controls strength */
+	pwm_set_relative_duty_cycle(state, 50, 100);
+}
+
+static struct pwm_vibrator_hw pwm_vib_hw_generic = {
+	.setup_pwm = pwm_vibrator_setup_generic,
+	.setup_pwm_dir = pwm_vibrator_setup_dir_generic,
+};
+
+static void pwm_vibrator_setup_mapphone(u16 level, struct pwm_state *state)
+{
+	unsigned int freq;
+
+	/* convert [0, 0xffff] -> [MAPPHONE_MAX_FREQ, MAPPHONE_MIN_FREQ] */
+	freq = 0xffff - level;
+	freq *= MAPPHONE_MAX_FREQ - MAPPHONE_MIN_FREQ;
+	freq /= 0xffff;
+	freq += MAPPHONE_MIN_FREQ;
+
+	state->period = DIV_ROUND_CLOSEST_ULL((u64) NSEC_PER_SEC * 10, freq);
+	pwm_set_relative_duty_cycle(state, 50, 100);
+}
+
+static struct pwm_vibrator_hw pwm_vib_hw_mapphone = {
+	.setup_pwm = pwm_vibrator_setup_mapphone,
+	.setup_pwm_dir = pwm_vibrator_setup_mapphone,
+};
+
+static int pwm_vibrator_start(struct pwm_vibrator *vibrator)
+{
+	struct device *pdev = vibrator->input->dev.parent;
+	struct pwm_state state;
+	int err;
+
+	dev_dbg(pdev, "start vibrator with level=0x%04x", vibrator->level);
+
+	err = regulator_enable(vibrator->vcc);
+	if (err) {
+		dev_err(pdev, "failed to enable regulator: %d", err);
+		return err;
+	}
+
+	pwm_get_state(vibrator->pwm, &state);
+	state.enabled = true;
+
+	vibrator->hw->setup_pwm(vibrator->level, &state);
+	dev_dbg(pdev, "period=%u", state.period);
+
+	err = pwm_apply_state(vibrator->pwm, &state);
+	if (err) {
+		dev_err(pdev, "failed to apply pwm state: %d", err);
+		return err;
+	}
+
+	if (vibrator->pwm_dir) {
+		pwm_get_state(vibrator->pwm_dir, &state);
+		state.enabled = true;
+
+		/* always control via period */
+		vibrator->hw->setup_pwm_dir(vibrator->level, &state);
+
+		err = pwm_apply_state(vibrator->pwm_dir, &state);
+		if (err) {
+			dev_err(pdev, "failed to apply dir-pwm state: %d", err);
+			pwm_disable(vibrator->pwm);
+			return err;
+		}
+	}
+
+	return 0;
+}
+
+static void pwm_vibrator_stop(struct pwm_vibrator *vibrator)
+{
+	struct device *pdev = vibrator->input->dev.parent;
+
+	dev_dbg(pdev, "stop vibrator");
+
+	regulator_disable(vibrator->vcc);
+
+	if (vibrator->pwm_dir)
+		pwm_disable(vibrator->pwm_dir);
+	pwm_disable(vibrator->pwm);
+}
+
+static void vibra_play_work(struct work_struct *work)
+{
+	struct pwm_vibrator *vibrator = container_of(work,
+					struct pwm_vibrator, play_work);
+
+	if (vibrator->level)
+		pwm_vibrator_start(vibrator);
+	else
+		pwm_vibrator_stop(vibrator);
+}
+
+static int pwm_vibrator_play_effect(struct input_dev *dev, void *data,
+				    struct ff_effect *effect)
+{
+	struct pwm_vibrator *vibrator = input_get_drvdata(dev);
+
+	vibrator->level = effect->u.rumble.strong_magnitude;
+	if (!vibrator->level)
+		vibrator->level = effect->u.rumble.weak_magnitude;
+
+	schedule_work(&vibrator->play_work);
+
+	return 0;
+}
+
+static void pwm_vibrator_close(struct input_dev *input)
+{
+	struct pwm_vibrator *vibrator = input_get_drvdata(input);
+
+	cancel_work_sync(&vibrator->play_work);
+	pwm_vibrator_stop(vibrator);
+}
+
+static int pwm_vibrator_probe(struct platform_device *pdev)
+{
+	struct pwm_vibrator *vibrator;
+	struct input_dev *input;
+	struct pwm_state state;
+	int err;
+
+	vibrator = devm_kzalloc(&pdev->dev, sizeof(*vibrator), GFP_KERNEL);
+	if (!vibrator)
+		return -ENOMEM;
+
+	input = devm_input_allocate_device(&pdev->dev);
+	if (!input)
+		return -ENOMEM;
+
+	vibrator->input = input;
+
+	vibrator->vcc = devm_regulator_get(&pdev->dev, "vcc");
+	err = PTR_ERR_OR_ZERO(vibrator->vcc);
+	if (err) {
+		if (err != -EPROBE_DEFER)
+			dev_err(&pdev->dev, "Failed to request regulator: %d",
+				err);
+		return err;
+	}
+
+	vibrator->pwm = devm_pwm_get(&pdev->dev, "enable");
+	err = PTR_ERR_OR_ZERO(vibrator->pwm);
+	if (err) {
+		if (err != -EPROBE_DEFER)
+			dev_err(&pdev->dev, "Failed to request main pwm: %d",
+				err);
+		return err;
+	}
+
+	INIT_WORK(&vibrator->play_work, vibra_play_work);
+
+	/* Sync up PWM state and ensure it is off. */
+	pwm_init_state(vibrator->pwm, &state);
+	state.enabled = false;
+	err = pwm_apply_state(vibrator->pwm, &state);
+	if (err) {
+		dev_err(&pdev->dev, "failed to apply initial PWM state: %d",
+			err);
+		return err;
+	}
+
+	vibrator->pwm_dir = devm_pwm_get(&pdev->dev, "direction");
+	err = PTR_ERR_OR_ZERO(vibrator->pwm_dir);
+	if (err == -ENODATA) {
+		vibrator->pwm_dir = NULL;
+	} else if (err == -EPROBE_DEFER) {
+		return err;
+	} else if (err) {
+		dev_err(&pdev->dev, "Failed to request direction pwm: %d", err);
+		return err;
+	} else {
+		/* Sync up PWM state and ensure it is off. */
+		pwm_init_state(vibrator->pwm_dir, &state);
+		state.enabled = false;
+		err = pwm_apply_state(vibrator->pwm_dir, &state);
+		if (err) {
+			dev_err(&pdev->dev, "failed to apply initial PWM state: %d",
+				err);
+			return err;
+		}
+	}
+
+	vibrator->hw = of_device_get_match_data(&pdev->dev);
+	if (!vibrator->hw)
+		vibrator->hw = &pwm_vib_hw_generic;
+
+	input->name = "pwm-vibrator";
+	input->id.bustype = BUS_HOST;
+	input->dev.parent = &pdev->dev;
+	input->close = pwm_vibrator_close;
+
+	input_set_drvdata(input, vibrator);
+	input_set_capability(input, EV_FF, FF_RUMBLE);
+
+	err = input_ff_create_memless(input, NULL, pwm_vibrator_play_effect);
+	if (err) {
+		dev_err(&pdev->dev, "Couldn't create FF dev: %d", err);
+		return err;
+	}
+
+	err = input_register_device(input);
+	if (err) {
+		dev_err(&pdev->dev, "Couldn't register input dev: %d", err);
+		return err;
+	}
+
+	platform_set_drvdata(pdev, vibrator);
+
+	return 0;
+}
+
+static int __maybe_unused pwm_vibrator_suspend(struct device *dev)
+{
+	struct platform_device *pdev = to_platform_device(dev);
+	struct pwm_vibrator *vibrator = platform_get_drvdata(pdev);
+	struct input_dev *input = vibrator->input;
+	unsigned long flags;
+
+	spin_lock_irqsave(&input->event_lock, flags);
+	cancel_work_sync(&vibrator->play_work);
+	if (vibrator->level)
+		pwm_vibrator_stop(vibrator);
+	spin_unlock_irqrestore(&input->event_lock, flags);
+
+	return 0;
+}
+
+static int __maybe_unused pwm_vibrator_resume(struct device *dev)
+{
+	struct platform_device *pdev = to_platform_device(dev);
+	struct pwm_vibrator *vibrator = platform_get_drvdata(pdev);
+	struct input_dev *input = vibrator->input;
+	unsigned long flags;
+
+	spin_lock_irqsave(&input->event_lock, flags);
+	if (vibrator->level)
+		pwm_vibrator_start(vibrator);
+	spin_unlock_irqrestore(&input->event_lock, flags);
+
+	return 0;
+}
+
+static SIMPLE_DEV_PM_OPS(pwm_vibrator_pm_ops,
+			 pwm_vibrator_suspend, pwm_vibrator_resume);
+
+#ifdef CONFIG_OF
+
+#define PWM_VIB_COMPAT(of_compatible, cfg) {			\
+			.compatible = of_compatible,		\
+			.data = &cfg,	\
+}
+
+static const struct of_device_id pwm_vibra_dt_match_table[] = {
+	PWM_VIB_COMPAT("pwm-vibrator", pwm_vib_hw_generic),
+	PWM_VIB_COMPAT("motorola,mapphone-pwm-vibrator", pwm_vib_hw_mapphone),
+	{},
+};
+MODULE_DEVICE_TABLE(of, pwm_vibra_dt_match_table);
+#endif
+
+static struct platform_driver pwm_vibrator_driver = {
+	.probe	= pwm_vibrator_probe,
+	.driver	= {
+		.name	= "pwm-vibrator",
+		.pm	= &pwm_vibrator_pm_ops,
+		.of_match_table = of_match_ptr(pwm_vibra_dt_match_table),
+	},
+};
+module_platform_driver(pwm_vibrator_driver);
+
+MODULE_AUTHOR("Sebastian Reichel <sre-DgEjT+Ai2ygdnm+yROfE0A@public.gmane.org>");
+MODULE_DESCRIPTION("PWM vibrator driver");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:pwm-vibrator");
-- 
2.11.0

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

^ permalink raw reply related

* [PATCHv2 0/2] PWM Vibrator driver
From: Sebastian Reichel @ 2017-05-03 11:11 UTC (permalink / raw)
  To: Sebastian Reichel, Dmitry Torokhov, Tony Lindgren
  Cc: Rob Herring, linux-input, linux-omap, devicetree, linux-kernel,
	Sebastian Reichel

Hi,

The Motorola Droid 4 has a vibrator, that is connected
to two GPIOs. Motorola's stock kernel names them vib_en
and vib_dir, which probably stand for vibrator_enable
and vibrator_direction. In their stock kernel both GPIOs
are toggled using a hrtimer and a custom vibrator "misc"
device is provided to userspace.

Thankfully the hardware designers the used GPIOs can
also be used from OMAP's dmtimers, so that they can
be driven as PWM output instead saving some CPU cycles
(and code).

The driver is loosely based on an old patch from Dmitry,
that I found in the internet(tm) [0]. Note, that I did
not check the generic vibrator stuff. I just kept it in
the driver, since it's probably what other people expect
from a pwm-vibra driver :)

Also I wrote a small tool to test the vibrator running
at different strength levels, since fftest(1) used a
fixed one.

[0] https://lkml.org/lkml/2012/4/10/41
[1] https://git.collabora.com/cgit/user/sre/rumble-test.git/plain/rumble-test.c

-- Sebastian

Sebastian Reichel (2):
  Input: pwm-vibra: new driver
  ARM: dts: omap4-droid4: Add vibrator

 .../devicetree/bindings/input/pwm-vibrator.txt     |  60 ++++
 arch/arm/boot/dts/omap4-droid4-xt894.dts           |  38 +++
 drivers/input/misc/Kconfig                         |  11 +
 drivers/input/misc/Makefile                        |   1 +
 drivers/input/misc/pwm-vibra.c                     | 343 +++++++++++++++++++++
 5 files changed, 453 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/input/pwm-vibrator.txt
 create mode 100644 drivers/input/misc/pwm-vibra.c

-- 
2.11.0


^ permalink raw reply

* Re: [PATCH] drivers/of_iommu: ignore SMMU DT nodes with status 'disabled'
From: Ard Biesheuvel @ 2017-05-03 10:58 UTC (permalink / raw)
  To: Robin Murphy
  Cc: Will Deacon,
	linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r@public.gmane.org,
	iommu-cunTk1MwBs9QetFLy7KEm3xJsTq8ys+cHZ5vskTnxNA,
	joro-zLv9SwRftAIdnm+yROfE0A,
	devicetree-u79uwXL29TY76Z2rM5mHXA@public.gmane.org, Mark Rutland
In-Reply-To: <1c0463e6-f82f-1179-4f54-b2d3de63dc87-5wv7dgnIgG8@public.gmane.org>


> On 3 May 2017, at 11:32, Robin Murphy <robin.murphy-5wv7dgnIgG8@public.gmane.org> wrote:
> 
>> On 28/04/17 14:22, Ard Biesheuvel wrote:
>>> On 28 April 2017 at 14:17, Will Deacon <will.deacon-5wv7dgnIgG8@public.gmane.org> wrote:
>>>> On Fri, Apr 28, 2017 at 02:14:49PM +0100, Ard Biesheuvel wrote:
>>>>> On 28 April 2017 at 14:11, Will Deacon <will.deacon-5wv7dgnIgG8@public.gmane.org> wrote:
>>>>> Hi Ard,
>>>>> 
>>>>> [+ devicetree@]
>>>>> 
>>>>>> On Fri, Apr 14, 2017 at 01:43:15PM +0100, Ard Biesheuvel wrote:
>>>>>> DT nodes may have a status property, and if they do, such nodes should
>>>>>> only be considered present if the status property is set to 'okay'.
>>>>>> 
>>>>>> Currently, we call the init function of IOMMUs described by the device
>>>>>> tree without taking this into account, which may result in the output
>>>>>> below on systems where some SMMUs may be legally disabled.
>>>>>> 
>>>>>> Failed to initialise IOMMU /smb/smmu@e0200000
>>>>>> Failed to initialise IOMMU /smb/smmu@e0c00000
>>>>>> arm-smmu e0a00000.smmu: probing hardware configuration...
>>>>>> arm-smmu e0a00000.smmu: SMMUv1 with:
>>>>>> arm-smmu e0a00000.smmu:  stage 2 translation
>>>>>> arm-smmu e0a00000.smmu:  coherent table walk
>>>>>> arm-smmu e0a00000.smmu:  stream matching with 32 register groups, mask 0x7fff
>>>>>> arm-smmu e0a00000.smmu:  8 context banks (8 stage-2 only)
>>>>>> arm-smmu e0a00000.smmu:  Supported page sizes: 0x60211000
>>>>>> arm-smmu e0a00000.smmu:  Stage-2: 40-bit IPA -> 40-bit PA
>>>>>> Failed to initialise IOMMU /smb/smmu@e0600000
>>>>>> Failed to initialise IOMMU /smb/smmu@e0800000
>>>>>> 
>>>>>> Since this is not an error condition, only call the init function if
>>>>>> the device is enabled, which also inhibits the spurious error messages.
>>>>>> 
>>>>>> Signed-off-by: Ard Biesheuvel <ard.biesheuvel-QSEj5FYQhm4dnm+yROfE0A@public.gmane.org>
>>>>>> ---
>>>>>> drivers/iommu/of_iommu.c | 2 +-
>>>>>> 1 file changed, 1 insertion(+), 1 deletion(-)
>>>>>> 
>>>>>> diff --git a/drivers/iommu/of_iommu.c b/drivers/iommu/of_iommu.c
>>>>>> index 2683e9fc0dcf..2dd1206e6c0d 100644
>>>>>> --- a/drivers/iommu/of_iommu.c
>>>>>> +++ b/drivers/iommu/of_iommu.c
>>>>>> @@ -183,7 +183,7 @@ static int __init of_iommu_init(void)
>>>>>>      for_each_matching_node_and_match(np, matches, &match) {
>>>>>>              const of_iommu_init_fn init_fn = match->data;
>>>>>> 
>>>>>> -             if (init_fn(np))
>>>>>> +             if (of_device_is_available(np) && init_fn(np))
>>>>>>                      pr_err("Failed to initialise IOMMU %s\n",
>>>>>>                              of_node_full_name(np));
>>>>>>      }
>>>>> 
>>>>> Is there a definition of what status = "disabled" is supposed to mean for an
>>>>> IOMMU? For example, that could mean that the firmware has pre-programmed the
>>>>> SMMU with particular translations or memory attributes (a bit like the
>>>>> CCA=1, CPM=1, DACS=0 case in ACPI IORT), or even disabled DMA traffic
>>>>> altogether.
>>>>> 
>>>>> So I think we'd need an update to the generic IOMMU binding text to say
>>>>> exactly what the semantics are supposed to be here.
>>>>> 
>>>> 
>>>> I agree that it might make sense to describe the behavior of the IOMMU
>>>> when it is left in the state we found it in. But that is not the same
>>>> as status=disabled.
>>>> 
>>>> The DTS subtree contains loads and loads of boilerplate
>>>> configurations, where only some pieces are enabled in the final image
>>>> by setting status=okay. So a node that has status 'disabled' should be
>>>> treated as 'not present', not as 'present but can be ignored under
>>>> assumptions such and such'
>>>> 
>>>> In other words, I think we are talking about two different issues here.
>>> 
>>> I'm not so sure... if we have a master device that has an iommus= property
>>> pointing to an IOMMU with status="disabled", I really don't know whether we
>>> should:
>>> 
>>>  1. Assume the master can do DMA with a 1:1 mapping of memory and no
>>>     changes to memory attributes
>>> 
>>>  2. Assume the master can do DMA with a 1:1 mapping of memory, but
>>>     potentially with changes to the attributes
>>> 
>>>  3. Assume the master can do DMA, but with some pre-existing translation
>>>     (what?)
>>> 
>>>  4. Assume the master can't do DMA
>>> 
>>> and I also don't know whether the "dma-coherent" property remains valid.
>>> 
>> 
>> Ah yes. Good point.
>> 
>> So indeed, there should be some IOMMU specific status property that
>> can convey all of the above, or 1. and 4. at the minimum
> 
> FWIW, the underlying issue being addressed here should be going away now
> anyway, since the now-queued probe deferral series obviates the init_fn
> early-device-creation bodge. I've been deliberately ignoring it for some
> time for precisely that reason ;)
> 


Ok. I have also updated the Seattle firmware to remove the smmu nodes and the associated iommus/iommu-map properties entirely when disabling SMMU support in the firmware, which should address Will's concern regarding unspecified behavior of a disabled SMMU.

IOW, this patch can be disregarded. Thanks.

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

^ permalink raw reply

* Re: [PATCH 1/2] extcon: usb-gpio: Add level trigger support
From: Baolin Wang @ 2017-05-03 10:56 UTC (permalink / raw)
  To: Rob Herring
  Cc: Chanwoo Choi, MyungJoo Ham, Mark Rutland, Mark Brown,
	Linaro Kernel Mailman List, LKML,
	devicetree-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
In-Reply-To: <CAMz4kuLi0rP7UP93hGZoO2WKTDTA08LQ3jYgL14qoaudV6rW+g-JsoAwUIsXosN+BqQ9rBEUg@public.gmane.org>

Hi Rob,

On 29 March 2017 at 14:08, Baolin Wang <baolin.wang-QSEj5FYQhm4dnm+yROfE0A@public.gmane.org> wrote:
> Hi,
>
> On 24 March 2017 at 20:41, Rob Herring <robh+dt-DgEjT+Ai2ygdnm+yROfE0A@public.gmane.org> wrote:
>> On Fri, Mar 24, 2017 at 6:56 AM, Baolin Wang <baolin.wang-QSEj5FYQhm4dnm+yROfE0A@public.gmane.org> wrote:
>>> Hi,
>>>
>>> On 24 March 2017 at 19:08, Chanwoo Choi <cw00.choi-Sze3O3UU22JBDgjK7y7TUQ@public.gmane.org> wrote:
>>>> Hi,
>>>>
>>>> On 2017년 03월 20일 16:59, Baolin Wang wrote:
>>>>> Now extcon-usb-gpio only supports for GPIO egdge trigger, but VBUS/ID
>>>>> gpios' detection can be triggered by the level trigger on some platforms.
>>>>> Thus intoduce one property 'extcon-gpio,level-trigger' to identify this
>>>>> situation.
>>>>>
>>>>> Signed-off-by: Baolin Wang <baolin.wang-QSEj5FYQhm4dnm+yROfE0A@public.gmane.org>
>>>>> ---
>>>>>  .../devicetree/bindings/extcon/extcon-usb-gpio.txt |    3 +++
>>>>>  1 file changed, 3 insertions(+)
>>>>>
>>>>> diff --git a/Documentation/devicetree/bindings/extcon/extcon-usb-gpio.txt b/Documentation/devicetree/bindings/extcon/extcon-usb-gpio.txt
>>>>> index dfc14f7..191504b 100644
>>>>> --- a/Documentation/devicetree/bindings/extcon/extcon-usb-gpio.txt
>>>>> +++ b/Documentation/devicetree/bindings/extcon/extcon-usb-gpio.txt
>>>>> @@ -9,6 +9,9 @@ Required properties:
>>>>>  Either one of id-gpio or vbus-gpio must be present. Both can be present as well.
>>>>>  - id-gpio: gpio for USB ID pin. See gpio binding.
>>>>>  - vbus-gpio: gpio for USB VBUS pin.
>>>>> +- extcon-gpio,level-trigger: Boolean, set this gpio's interrupt flag to
>>>>
>>>> It is 'extcon-usb-gpio' instead of 'extcon-gpio'.
>>>
>>> Sure.
>>
>> "extcon-gpio" is not a vendor, so don't make it a vendor prefix.
>
> OK.
>
>>
>>>>> +level trigger. If not specified defaults to false, gpio's interrupt flag
>>>>> +defaults to edge trigger.
>>>>
>>>> I understand why you need the new flag.
>>>> But, I'm not sure it is right way to add the new flag for interrupt flag.
>>>>
>>>> IMHO, I think that we need to find the more proper way to get the interrupt flag
>>>> or maybe, we may need to implement the new helper api to get the interrupt
>>>> flag for gpio pin when we use the gpio as the interrupt source,
>>>
>>> Yes, I agree with you. We already have helper functions to get
>>> interrupt flag from device tree, but we can not get the irq number of
>>> GPIO to configure in device tree, then I introduce one new flag to
>>> indicate the trigger type. But I like to change the patch if there are
>>> any good suggestion. Thanks.
>>
>> The extcon binding needs an overhaul in general. I'm not going to take
>> extensions to a broken binding. What's needed is a USB connector
>> binding like we have for HDMI and other video connectors. And for
>> USB-C, those need to be combined.

Could you elaborate on how to make an overhaul for the extcon binding?
I can help to do that and then we can consider to modify my patch.
Thanks.

>
> I am not sure I understand your points correctly, could you elaborate
> on how to modify for extcon? Thanks.
>
> --
> Baolin.wang
> Best Regards



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

^ permalink raw reply

* Re: [PATCH] drivers/of_iommu: ignore SMMU DT nodes with status 'disabled'
From: Robin Murphy @ 2017-05-03 10:32 UTC (permalink / raw)
  To: Ard Biesheuvel, Will Deacon
  Cc: Mark Rutland, devicetree-u79uwXL29TY76Z2rM5mHXA@public.gmane.org,
	iommu-cunTk1MwBs9QetFLy7KEm3xJsTq8ys+cHZ5vskTnxNA,
	linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r@public.gmane.org
In-Reply-To: <CAKv+Gu8L1iyzBitt8zzvWDDEXk_ysGfPRxHJbQQK5o8NMWF5MA-JsoAwUIsXosN+BqQ9rBEUg@public.gmane.org>

On 28/04/17 14:22, Ard Biesheuvel wrote:
> On 28 April 2017 at 14:17, Will Deacon <will.deacon-5wv7dgnIgG8@public.gmane.org> wrote:
>> On Fri, Apr 28, 2017 at 02:14:49PM +0100, Ard Biesheuvel wrote:
>>> On 28 April 2017 at 14:11, Will Deacon <will.deacon-5wv7dgnIgG8@public.gmane.org> wrote:
>>>> Hi Ard,
>>>>
>>>> [+ devicetree@]
>>>>
>>>> On Fri, Apr 14, 2017 at 01:43:15PM +0100, Ard Biesheuvel wrote:
>>>>> DT nodes may have a status property, and if they do, such nodes should
>>>>> only be considered present if the status property is set to 'okay'.
>>>>>
>>>>> Currently, we call the init function of IOMMUs described by the device
>>>>> tree without taking this into account, which may result in the output
>>>>> below on systems where some SMMUs may be legally disabled.
>>>>>
>>>>>  Failed to initialise IOMMU /smb/smmu@e0200000
>>>>>  Failed to initialise IOMMU /smb/smmu@e0c00000
>>>>>  arm-smmu e0a00000.smmu: probing hardware configuration...
>>>>>  arm-smmu e0a00000.smmu: SMMUv1 with:
>>>>>  arm-smmu e0a00000.smmu:  stage 2 translation
>>>>>  arm-smmu e0a00000.smmu:  coherent table walk
>>>>>  arm-smmu e0a00000.smmu:  stream matching with 32 register groups, mask 0x7fff
>>>>>  arm-smmu e0a00000.smmu:  8 context banks (8 stage-2 only)
>>>>>  arm-smmu e0a00000.smmu:  Supported page sizes: 0x60211000
>>>>>  arm-smmu e0a00000.smmu:  Stage-2: 40-bit IPA -> 40-bit PA
>>>>>  Failed to initialise IOMMU /smb/smmu@e0600000
>>>>>  Failed to initialise IOMMU /smb/smmu@e0800000
>>>>>
>>>>> Since this is not an error condition, only call the init function if
>>>>> the device is enabled, which also inhibits the spurious error messages.
>>>>>
>>>>> Signed-off-by: Ard Biesheuvel <ard.biesheuvel-QSEj5FYQhm4dnm+yROfE0A@public.gmane.org>
>>>>> ---
>>>>>  drivers/iommu/of_iommu.c | 2 +-
>>>>>  1 file changed, 1 insertion(+), 1 deletion(-)
>>>>>
>>>>> diff --git a/drivers/iommu/of_iommu.c b/drivers/iommu/of_iommu.c
>>>>> index 2683e9fc0dcf..2dd1206e6c0d 100644
>>>>> --- a/drivers/iommu/of_iommu.c
>>>>> +++ b/drivers/iommu/of_iommu.c
>>>>> @@ -183,7 +183,7 @@ static int __init of_iommu_init(void)
>>>>>       for_each_matching_node_and_match(np, matches, &match) {
>>>>>               const of_iommu_init_fn init_fn = match->data;
>>>>>
>>>>> -             if (init_fn(np))
>>>>> +             if (of_device_is_available(np) && init_fn(np))
>>>>>                       pr_err("Failed to initialise IOMMU %s\n",
>>>>>                               of_node_full_name(np));
>>>>>       }
>>>>
>>>> Is there a definition of what status = "disabled" is supposed to mean for an
>>>> IOMMU? For example, that could mean that the firmware has pre-programmed the
>>>> SMMU with particular translations or memory attributes (a bit like the
>>>> CCA=1, CPM=1, DACS=0 case in ACPI IORT), or even disabled DMA traffic
>>>> altogether.
>>>>
>>>> So I think we'd need an update to the generic IOMMU binding text to say
>>>> exactly what the semantics are supposed to be here.
>>>>
>>>
>>> I agree that it might make sense to describe the behavior of the IOMMU
>>> when it is left in the state we found it in. But that is not the same
>>> as status=disabled.
>>>
>>> The DTS subtree contains loads and loads of boilerplate
>>> configurations, where only some pieces are enabled in the final image
>>> by setting status=okay. So a node that has status 'disabled' should be
>>> treated as 'not present', not as 'present but can be ignored under
>>> assumptions such and such'
>>>
>>> In other words, I think we are talking about two different issues here.
>>
>> I'm not so sure... if we have a master device that has an iommus= property
>> pointing to an IOMMU with status="disabled", I really don't know whether we
>> should:
>>
>>   1. Assume the master can do DMA with a 1:1 mapping of memory and no
>>      changes to memory attributes
>>
>>   2. Assume the master can do DMA with a 1:1 mapping of memory, but
>>      potentially with changes to the attributes
>>
>>   3. Assume the master can do DMA, but with some pre-existing translation
>>      (what?)
>>
>>   4. Assume the master can't do DMA
>>
>> and I also don't know whether the "dma-coherent" property remains valid.
>>
> 
> Ah yes. Good point.
> 
> So indeed, there should be some IOMMU specific status property that
> can convey all of the above, or 1. and 4. at the minimum

FWIW, the underlying issue being addressed here should be going away now
anyway, since the now-queued probe deferral series obviates the init_fn
early-device-creation bodge. I've been deliberately ignoring it for some
time for precisely that reason ;)

Robin.

^ permalink raw reply

* [PATCH v3 1/1] of: Move OF property and graph API from base.c to property.c
From: Sakari Ailus @ 2017-05-03 10:21 UTC (permalink / raw)
  To: devicetree, robh, frowand.list
  Cc: linux-acpi, sudeep.holla, lorenzo.pieralisi, mika.westerberg,
	rafael, mark.rutland, broonie, ahs3

base.c contains both core OF functions and increasingly other
functionality such as accessing properties and graphs, including
convenience functions. In the near future this would also include OF
specific implementation of the fwnode property and graph APIs.

Create driver/of/property.c to contain procedures for accessing and
interpreting device tree properties. The procedures are moved from
drivers/of/base.c, with no changes other than copying only the includes
required by the moved procedures.

Signed-off-by: Sakari Ailus <sakari.ailus@linux.intel.com>
---
Hi Rob and Frank,

This is a pre-dependency for further fwnode property cleanup. The patch
appears to be slightly more conflict-prone than the rest of the patchset:

<URL:http://www.spinics.net/lists/linux-acpi/msg72647.html>

Would it be possible to merge it separately from the rest?

since v2:

- Also move of_property_read_u64_index() added by patch
  "drivers/of/base.c: Add of_property_read_u64_index" found in linux-next

 drivers/of/Makefile   |   2 +-
 drivers/of/base.c     | 733 ------------------------------------------------
 drivers/of/property.c | 763 ++++++++++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 764 insertions(+), 734 deletions(-)
 create mode 100644 drivers/of/property.c

Regards,
Sakari

diff --git a/drivers/of/Makefile b/drivers/of/Makefile
index d7efd9d..97dc01c 100644
--- a/drivers/of/Makefile
+++ b/drivers/of/Makefile
@@ -1,4 +1,4 @@
-obj-y = base.o device.o platform.o
+obj-y = base.o device.o platform.o property.o
 obj-$(CONFIG_OF_DYNAMIC) += dynamic.o
 obj-$(CONFIG_OF_FLATTREE) += fdt.o
 obj-$(CONFIG_OF_EARLY_FLATTREE) += fdt_address.o
diff --git a/drivers/of/base.c b/drivers/of/base.c
index 0ea16bd..d57188d 100644
--- a/drivers/of/base.c
+++ b/drivers/of/base.c
@@ -1113,458 +1113,6 @@ struct device_node *of_find_node_by_phandle(phandle handle)
 }
 EXPORT_SYMBOL(of_find_node_by_phandle);
 
-/**
- * of_property_count_elems_of_size - Count the number of elements in a property
- *
- * @np:		device node from which the property value is to be read.
- * @propname:	name of the property to be searched.
- * @elem_size:	size of the individual element
- *
- * Search for a property in a device node and count the number of elements of
- * size elem_size in it. Returns number of elements on sucess, -EINVAL if the
- * property does not exist or its length does not match a multiple of elem_size
- * and -ENODATA if the property does not have a value.
- */
-int of_property_count_elems_of_size(const struct device_node *np,
-				const char *propname, int elem_size)
-{
-	struct property *prop = of_find_property(np, propname, NULL);
-
-	if (!prop)
-		return -EINVAL;
-	if (!prop->value)
-		return -ENODATA;
-
-	if (prop->length % elem_size != 0) {
-		pr_err("size of %s in node %s is not a multiple of %d\n",
-		       propname, np->full_name, elem_size);
-		return -EINVAL;
-	}
-
-	return prop->length / elem_size;
-}
-EXPORT_SYMBOL_GPL(of_property_count_elems_of_size);
-
-/**
- * of_find_property_value_of_size
- *
- * @np:		device node from which the property value is to be read.
- * @propname:	name of the property to be searched.
- * @min:	minimum allowed length of property value
- * @max:	maximum allowed length of property value (0 means unlimited)
- * @len:	if !=NULL, actual length is written to here
- *
- * Search for a property in a device node and valid the requested size.
- * Returns the property value on success, -EINVAL if the property does not
- *  exist, -ENODATA if property does not have a value, and -EOVERFLOW if the
- * property data is too small or too large.
- *
- */
-static void *of_find_property_value_of_size(const struct device_node *np,
-			const char *propname, u32 min, u32 max, size_t *len)
-{
-	struct property *prop = of_find_property(np, propname, NULL);
-
-	if (!prop)
-		return ERR_PTR(-EINVAL);
-	if (!prop->value)
-		return ERR_PTR(-ENODATA);
-	if (prop->length < min)
-		return ERR_PTR(-EOVERFLOW);
-	if (max && prop->length > max)
-		return ERR_PTR(-EOVERFLOW);
-
-	if (len)
-		*len = prop->length;
-
-	return prop->value;
-}
-
-/**
- * of_property_read_u32_index - Find and read a u32 from a multi-value property.
- *
- * @np:		device node from which the property value is to be read.
- * @propname:	name of the property to be searched.
- * @index:	index of the u32 in the list of values
- * @out_value:	pointer to return value, modified only if no error.
- *
- * Search for a property in a device node and read nth 32-bit value from
- * it. Returns 0 on success, -EINVAL if the property does not exist,
- * -ENODATA if property does not have a value, and -EOVERFLOW if the
- * property data isn't large enough.
- *
- * The out_value is modified only if a valid u32 value can be decoded.
- */
-int of_property_read_u32_index(const struct device_node *np,
-				       const char *propname,
-				       u32 index, u32 *out_value)
-{
-	const u32 *val = of_find_property_value_of_size(np, propname,
-					((index + 1) * sizeof(*out_value)),
-					0,
-					NULL);
-
-	if (IS_ERR(val))
-		return PTR_ERR(val);
-
-	*out_value = be32_to_cpup(((__be32 *)val) + index);
-	return 0;
-}
-EXPORT_SYMBOL_GPL(of_property_read_u32_index);
-
-/**
- * of_property_read_u64_index - Find and read a u64 from a multi-value property.
- *
- * @np:		device node from which the property value is to be read.
- * @propname:	name of the property to be searched.
- * @index:	index of the u64 in the list of values
- * @out_value:	pointer to return value, modified only if no error.
- *
- * Search for a property in a device node and read nth 64-bit value from
- * it. Returns 0 on success, -EINVAL if the property does not exist,
- * -ENODATA if property does not have a value, and -EOVERFLOW if the
- * property data isn't large enough.
- *
- * The out_value is modified only if a valid u64 value can be decoded.
- */
-int of_property_read_u64_index(const struct device_node *np,
-				       const char *propname,
-				       u32 index, u64 *out_value)
-{
-	const u64 *val = of_find_property_value_of_size(np, propname,
-					((index + 1) * sizeof(*out_value)),
-					0, NULL);
-
-	if (IS_ERR(val))
-		return PTR_ERR(val);
-
-	*out_value = be64_to_cpup(((__be64 *)val) + index);
-	return 0;
-}
-EXPORT_SYMBOL_GPL(of_property_read_u64_index);
-
-/**
- * of_property_read_variable_u8_array - Find and read an array of u8 from a
- * property, with bounds on the minimum and maximum array size.
- *
- * @np:		device node from which the property value is to be read.
- * @propname:	name of the property to be searched.
- * @out_values:	pointer to return value, modified only if return value is 0.
- * @sz_min:	minimum number of array elements to read
- * @sz_max:	maximum number of array elements to read, if zero there is no
- *		upper limit on the number of elements in the dts entry but only
- *		sz_min will be read.
- *
- * Search for a property in a device node and read 8-bit value(s) from
- * it. Returns number of elements read on success, -EINVAL if the property
- * does not exist, -ENODATA if property does not have a value, and -EOVERFLOW
- * if the property data is smaller than sz_min or longer than sz_max.
- *
- * dts entry of array should be like:
- *	property = /bits/ 8 <0x50 0x60 0x70>;
- *
- * The out_values is modified only if a valid u8 value can be decoded.
- */
-int of_property_read_variable_u8_array(const struct device_node *np,
-					const char *propname, u8 *out_values,
-					size_t sz_min, size_t sz_max)
-{
-	size_t sz, count;
-	const u8 *val = of_find_property_value_of_size(np, propname,
-						(sz_min * sizeof(*out_values)),
-						(sz_max * sizeof(*out_values)),
-						&sz);
-
-	if (IS_ERR(val))
-		return PTR_ERR(val);
-
-	if (!sz_max)
-		sz = sz_min;
-	else
-		sz /= sizeof(*out_values);
-
-	count = sz;
-	while (count--)
-		*out_values++ = *val++;
-
-	return sz;
-}
-EXPORT_SYMBOL_GPL(of_property_read_variable_u8_array);
-
-/**
- * of_property_read_variable_u16_array - Find and read an array of u16 from a
- * property, with bounds on the minimum and maximum array size.
- *
- * @np:		device node from which the property value is to be read.
- * @propname:	name of the property to be searched.
- * @out_values:	pointer to return value, modified only if return value is 0.
- * @sz_min:	minimum number of array elements to read
- * @sz_max:	maximum number of array elements to read, if zero there is no
- *		upper limit on the number of elements in the dts entry but only
- *		sz_min will be read.
- *
- * Search for a property in a device node and read 16-bit value(s) from
- * it. Returns number of elements read on success, -EINVAL if the property
- * does not exist, -ENODATA if property does not have a value, and -EOVERFLOW
- * if the property data is smaller than sz_min or longer than sz_max.
- *
- * dts entry of array should be like:
- *	property = /bits/ 16 <0x5000 0x6000 0x7000>;
- *
- * The out_values is modified only if a valid u16 value can be decoded.
- */
-int of_property_read_variable_u16_array(const struct device_node *np,
-					const char *propname, u16 *out_values,
-					size_t sz_min, size_t sz_max)
-{
-	size_t sz, count;
-	const __be16 *val = of_find_property_value_of_size(np, propname,
-						(sz_min * sizeof(*out_values)),
-						(sz_max * sizeof(*out_values)),
-						&sz);
-
-	if (IS_ERR(val))
-		return PTR_ERR(val);
-
-	if (!sz_max)
-		sz = sz_min;
-	else
-		sz /= sizeof(*out_values);
-
-	count = sz;
-	while (count--)
-		*out_values++ = be16_to_cpup(val++);
-
-	return sz;
-}
-EXPORT_SYMBOL_GPL(of_property_read_variable_u16_array);
-
-/**
- * of_property_read_variable_u32_array - Find and read an array of 32 bit
- * integers from a property, with bounds on the minimum and maximum array size.
- *
- * @np:		device node from which the property value is to be read.
- * @propname:	name of the property to be searched.
- * @out_values:	pointer to return value, modified only if return value is 0.
- * @sz_min:	minimum number of array elements to read
- * @sz_max:	maximum number of array elements to read, if zero there is no
- *		upper limit on the number of elements in the dts entry but only
- *		sz_min will be read.
- *
- * Search for a property in a device node and read 32-bit value(s) from
- * it. Returns number of elements read on success, -EINVAL if the property
- * does not exist, -ENODATA if property does not have a value, and -EOVERFLOW
- * if the property data is smaller than sz_min or longer than sz_max.
- *
- * The out_values is modified only if a valid u32 value can be decoded.
- */
-int of_property_read_variable_u32_array(const struct device_node *np,
-			       const char *propname, u32 *out_values,
-			       size_t sz_min, size_t sz_max)
-{
-	size_t sz, count;
-	const __be32 *val = of_find_property_value_of_size(np, propname,
-						(sz_min * sizeof(*out_values)),
-						(sz_max * sizeof(*out_values)),
-						&sz);
-
-	if (IS_ERR(val))
-		return PTR_ERR(val);
-
-	if (!sz_max)
-		sz = sz_min;
-	else
-		sz /= sizeof(*out_values);
-
-	count = sz;
-	while (count--)
-		*out_values++ = be32_to_cpup(val++);
-
-	return sz;
-}
-EXPORT_SYMBOL_GPL(of_property_read_variable_u32_array);
-
-/**
- * of_property_read_u64 - Find and read a 64 bit integer from a property
- * @np:		device node from which the property value is to be read.
- * @propname:	name of the property to be searched.
- * @out_value:	pointer to return value, modified only if return value is 0.
- *
- * Search for a property in a device node and read a 64-bit value from
- * it. Returns 0 on success, -EINVAL if the property does not exist,
- * -ENODATA if property does not have a value, and -EOVERFLOW if the
- * property data isn't large enough.
- *
- * The out_value is modified only if a valid u64 value can be decoded.
- */
-int of_property_read_u64(const struct device_node *np, const char *propname,
-			 u64 *out_value)
-{
-	const __be32 *val = of_find_property_value_of_size(np, propname,
-						sizeof(*out_value),
-						0,
-						NULL);
-
-	if (IS_ERR(val))
-		return PTR_ERR(val);
-
-	*out_value = of_read_number(val, 2);
-	return 0;
-}
-EXPORT_SYMBOL_GPL(of_property_read_u64);
-
-/**
- * of_property_read_variable_u64_array - Find and read an array of 64 bit
- * integers from a property, with bounds on the minimum and maximum array size.
- *
- * @np:		device node from which the property value is to be read.
- * @propname:	name of the property to be searched.
- * @out_values:	pointer to return value, modified only if return value is 0.
- * @sz_min:	minimum number of array elements to read
- * @sz_max:	maximum number of array elements to read, if zero there is no
- *		upper limit on the number of elements in the dts entry but only
- *		sz_min will be read.
- *
- * Search for a property in a device node and read 64-bit value(s) from
- * it. Returns number of elements read on success, -EINVAL if the property
- * does not exist, -ENODATA if property does not have a value, and -EOVERFLOW
- * if the property data is smaller than sz_min or longer than sz_max.
- *
- * The out_values is modified only if a valid u64 value can be decoded.
- */
-int of_property_read_variable_u64_array(const struct device_node *np,
-			       const char *propname, u64 *out_values,
-			       size_t sz_min, size_t sz_max)
-{
-	size_t sz, count;
-	const __be32 *val = of_find_property_value_of_size(np, propname,
-						(sz_min * sizeof(*out_values)),
-						(sz_max * sizeof(*out_values)),
-						&sz);
-
-	if (IS_ERR(val))
-		return PTR_ERR(val);
-
-	if (!sz_max)
-		sz = sz_min;
-	else
-		sz /= sizeof(*out_values);
-
-	count = sz;
-	while (count--) {
-		*out_values++ = of_read_number(val, 2);
-		val += 2;
-	}
-
-	return sz;
-}
-EXPORT_SYMBOL_GPL(of_property_read_variable_u64_array);
-
-/**
- * of_property_read_string - Find and read a string from a property
- * @np:		device node from which the property value is to be read.
- * @propname:	name of the property to be searched.
- * @out_string:	pointer to null terminated return string, modified only if
- *		return value is 0.
- *
- * Search for a property in a device tree node and retrieve a null
- * terminated string value (pointer to data, not a copy). Returns 0 on
- * success, -EINVAL if the property does not exist, -ENODATA if property
- * does not have a value, and -EILSEQ if the string is not null-terminated
- * within the length of the property data.
- *
- * The out_string pointer is modified only if a valid string can be decoded.
- */
-int of_property_read_string(const struct device_node *np, const char *propname,
-				const char **out_string)
-{
-	const struct property *prop = of_find_property(np, propname, NULL);
-	if (!prop)
-		return -EINVAL;
-	if (!prop->value)
-		return -ENODATA;
-	if (strnlen(prop->value, prop->length) >= prop->length)
-		return -EILSEQ;
-	*out_string = prop->value;
-	return 0;
-}
-EXPORT_SYMBOL_GPL(of_property_read_string);
-
-/**
- * of_property_match_string() - Find string in a list and return index
- * @np: pointer to node containing string list property
- * @propname: string list property name
- * @string: pointer to string to search for in string list
- *
- * This function searches a string list property and returns the index
- * of a specific string value.
- */
-int of_property_match_string(const struct device_node *np, const char *propname,
-			     const char *string)
-{
-	const struct property *prop = of_find_property(np, propname, NULL);
-	size_t l;
-	int i;
-	const char *p, *end;
-
-	if (!prop)
-		return -EINVAL;
-	if (!prop->value)
-		return -ENODATA;
-
-	p = prop->value;
-	end = p + prop->length;
-
-	for (i = 0; p < end; i++, p += l) {
-		l = strnlen(p, end - p) + 1;
-		if (p + l > end)
-			return -EILSEQ;
-		pr_debug("comparing %s with %s\n", string, p);
-		if (strcmp(string, p) == 0)
-			return i; /* Found it; return index */
-	}
-	return -ENODATA;
-}
-EXPORT_SYMBOL_GPL(of_property_match_string);
-
-/**
- * of_property_read_string_helper() - Utility helper for parsing string properties
- * @np:		device node from which the property value is to be read.
- * @propname:	name of the property to be searched.
- * @out_strs:	output array of string pointers.
- * @sz:		number of array elements to read.
- * @skip:	Number of strings to skip over at beginning of list.
- *
- * Don't call this function directly. It is a utility helper for the
- * of_property_read_string*() family of functions.
- */
-int of_property_read_string_helper(const struct device_node *np,
-				   const char *propname, const char **out_strs,
-				   size_t sz, int skip)
-{
-	const struct property *prop = of_find_property(np, propname, NULL);
-	int l = 0, i = 0;
-	const char *p, *end;
-
-	if (!prop)
-		return -EINVAL;
-	if (!prop->value)
-		return -ENODATA;
-	p = prop->value;
-	end = p + prop->length;
-
-	for (i = 0; p < end && (!out_strs || i < skip + sz); i++, p += l) {
-		l = strnlen(p, end - p) + 1;
-		if (p + l > end)
-			return -EILSEQ;
-		if (out_strs && i >= skip)
-			*out_strs++ = p;
-	}
-	i -= skip;
-	return i <= 0 ? -ENODATA : i;
-}
-EXPORT_SYMBOL_GPL(of_property_read_string_helper);
-
 void of_print_phandle_args(const char *msg, const struct of_phandle_args *args)
 {
 	int i;
@@ -2211,47 +1759,6 @@ int of_alias_get_highest_id(const char *stem)
 }
 EXPORT_SYMBOL_GPL(of_alias_get_highest_id);
 
-const __be32 *of_prop_next_u32(struct property *prop, const __be32 *cur,
-			       u32 *pu)
-{
-	const void *curv = cur;
-
-	if (!prop)
-		return NULL;
-
-	if (!cur) {
-		curv = prop->value;
-		goto out_val;
-	}
-
-	curv += sizeof(*cur);
-	if (curv >= prop->value + prop->length)
-		return NULL;
-
-out_val:
-	*pu = be32_to_cpup(curv);
-	return curv;
-}
-EXPORT_SYMBOL_GPL(of_prop_next_u32);
-
-const char *of_prop_next_string(struct property *prop, const char *cur)
-{
-	const void *curv = cur;
-
-	if (!prop)
-		return NULL;
-
-	if (!cur)
-		return prop->value;
-
-	curv += strlen(cur) + 1;
-	if (curv >= prop->value + prop->length)
-		return NULL;
-
-	return curv;
-}
-EXPORT_SYMBOL_GPL(of_prop_next_string);
-
 /**
  * of_console_check() - Test and setup console for DT setup
  * @dn - Pointer to device node
@@ -2326,243 +1833,3 @@ int of_find_last_cache_level(unsigned int cpu)
 
 	return cache_level;
 }
-
-/**
- * of_graph_parse_endpoint() - parse common endpoint node properties
- * @node: pointer to endpoint device_node
- * @endpoint: pointer to the OF endpoint data structure
- *
- * The caller should hold a reference to @node.
- */
-int of_graph_parse_endpoint(const struct device_node *node,
-			    struct of_endpoint *endpoint)
-{
-	struct device_node *port_node = of_get_parent(node);
-
-	WARN_ONCE(!port_node, "%s(): endpoint %s has no parent node\n",
-		  __func__, node->full_name);
-
-	memset(endpoint, 0, sizeof(*endpoint));
-
-	endpoint->local_node = node;
-	/*
-	 * It doesn't matter whether the two calls below succeed.
-	 * If they don't then the default value 0 is used.
-	 */
-	of_property_read_u32(port_node, "reg", &endpoint->port);
-	of_property_read_u32(node, "reg", &endpoint->id);
-
-	of_node_put(port_node);
-
-	return 0;
-}
-EXPORT_SYMBOL(of_graph_parse_endpoint);
-
-/**
- * of_graph_get_port_by_id() - get the port matching a given id
- * @parent: pointer to the parent device node
- * @id: id of the port
- *
- * Return: A 'port' node pointer with refcount incremented. The caller
- * has to use of_node_put() on it when done.
- */
-struct device_node *of_graph_get_port_by_id(struct device_node *parent, u32 id)
-{
-	struct device_node *node, *port;
-
-	node = of_get_child_by_name(parent, "ports");
-	if (node)
-		parent = node;
-
-	for_each_child_of_node(parent, port) {
-		u32 port_id = 0;
-
-		if (of_node_cmp(port->name, "port") != 0)
-			continue;
-		of_property_read_u32(port, "reg", &port_id);
-		if (id == port_id)
-			break;
-	}
-
-	of_node_put(node);
-
-	return port;
-}
-EXPORT_SYMBOL(of_graph_get_port_by_id);
-
-/**
- * of_graph_get_next_endpoint() - get next endpoint node
- * @parent: pointer to the parent device node
- * @prev: previous endpoint node, or NULL to get first
- *
- * Return: An 'endpoint' node pointer with refcount incremented. Refcount
- * of the passed @prev node is decremented.
- */
-struct device_node *of_graph_get_next_endpoint(const struct device_node *parent,
-					struct device_node *prev)
-{
-	struct device_node *endpoint;
-	struct device_node *port;
-
-	if (!parent)
-		return NULL;
-
-	/*
-	 * Start by locating the port node. If no previous endpoint is specified
-	 * search for the first port node, otherwise get the previous endpoint
-	 * parent port node.
-	 */
-	if (!prev) {
-		struct device_node *node;
-
-		node = of_get_child_by_name(parent, "ports");
-		if (node)
-			parent = node;
-
-		port = of_get_child_by_name(parent, "port");
-		of_node_put(node);
-
-		if (!port) {
-			pr_err("graph: no port node found in %s\n",
-			       parent->full_name);
-			return NULL;
-		}
-	} else {
-		port = of_get_parent(prev);
-		if (WARN_ONCE(!port, "%s(): endpoint %s has no parent node\n",
-			      __func__, prev->full_name))
-			return NULL;
-	}
-
-	while (1) {
-		/*
-		 * Now that we have a port node, get the next endpoint by
-		 * getting the next child. If the previous endpoint is NULL this
-		 * will return the first child.
-		 */
-		endpoint = of_get_next_child(port, prev);
-		if (endpoint) {
-			of_node_put(port);
-			return endpoint;
-		}
-
-		/* No more endpoints under this port, try the next one. */
-		prev = NULL;
-
-		do {
-			port = of_get_next_child(parent, port);
-			if (!port)
-				return NULL;
-		} while (of_node_cmp(port->name, "port"));
-	}
-}
-EXPORT_SYMBOL(of_graph_get_next_endpoint);
-
-/**
- * of_graph_get_endpoint_by_regs() - get endpoint node of specific identifiers
- * @parent: pointer to the parent device node
- * @port_reg: identifier (value of reg property) of the parent port node
- * @reg: identifier (value of reg property) of the endpoint node
- *
- * Return: An 'endpoint' node pointer which is identified by reg and at the same
- * is the child of a port node identified by port_reg. reg and port_reg are
- * ignored when they are -1.
- */
-struct device_node *of_graph_get_endpoint_by_regs(
-	const struct device_node *parent, int port_reg, int reg)
-{
-	struct of_endpoint endpoint;
-	struct device_node *node = NULL;
-
-	for_each_endpoint_of_node(parent, node) {
-		of_graph_parse_endpoint(node, &endpoint);
-		if (((port_reg == -1) || (endpoint.port == port_reg)) &&
-			((reg == -1) || (endpoint.id == reg)))
-			return node;
-	}
-
-	return NULL;
-}
-EXPORT_SYMBOL(of_graph_get_endpoint_by_regs);
-
-/**
- * of_graph_get_remote_port_parent() - get remote port's parent node
- * @node: pointer to a local endpoint device_node
- *
- * Return: Remote device node associated with remote endpoint node linked
- *	   to @node. Use of_node_put() on it when done.
- */
-struct device_node *of_graph_get_remote_port_parent(
-			       const struct device_node *node)
-{
-	struct device_node *np;
-	unsigned int depth;
-
-	/* Get remote endpoint node. */
-	np = of_parse_phandle(node, "remote-endpoint", 0);
-
-	/* Walk 3 levels up only if there is 'ports' node. */
-	for (depth = 3; depth && np; depth--) {
-		np = of_get_next_parent(np);
-		if (depth == 2 && of_node_cmp(np->name, "ports"))
-			break;
-	}
-	return np;
-}
-EXPORT_SYMBOL(of_graph_get_remote_port_parent);
-
-/**
- * of_graph_get_remote_port() - get remote port node
- * @node: pointer to a local endpoint device_node
- *
- * Return: Remote port node associated with remote endpoint node linked
- *	   to @node. Use of_node_put() on it when done.
- */
-struct device_node *of_graph_get_remote_port(const struct device_node *node)
-{
-	struct device_node *np;
-
-	/* Get remote endpoint node. */
-	np = of_parse_phandle(node, "remote-endpoint", 0);
-	if (!np)
-		return NULL;
-	return of_get_next_parent(np);
-}
-EXPORT_SYMBOL(of_graph_get_remote_port);
-
-/**
- * of_graph_get_remote_node() - get remote parent device_node for given port/endpoint
- * @node: pointer to parent device_node containing graph port/endpoint
- * @port: identifier (value of reg property) of the parent port node
- * @endpoint: identifier (value of reg property) of the endpoint node
- *
- * Return: Remote device node associated with remote endpoint node linked
- *	   to @node. Use of_node_put() on it when done.
- */
-struct device_node *of_graph_get_remote_node(const struct device_node *node,
-					     u32 port, u32 endpoint)
-{
-	struct device_node *endpoint_node, *remote;
-
-	endpoint_node = of_graph_get_endpoint_by_regs(node, port, endpoint);
-	if (!endpoint_node) {
-		pr_debug("no valid endpoint (%d, %d) for node %s\n",
-			 port, endpoint, node->full_name);
-		return NULL;
-	}
-
-	remote = of_graph_get_remote_port_parent(endpoint_node);
-	of_node_put(endpoint_node);
-	if (!remote) {
-		pr_debug("no valid remote node\n");
-		return NULL;
-	}
-
-	if (!of_device_is_available(remote)) {
-		pr_debug("not available for remote node\n");
-		return NULL;
-	}
-
-	return remote;
-}
-EXPORT_SYMBOL(of_graph_get_remote_node);
diff --git a/drivers/of/property.c b/drivers/of/property.c
new file mode 100644
index 0000000..e5d3e1f
--- /dev/null
+++ b/drivers/of/property.c
@@ -0,0 +1,763 @@
+/*
+ * drivers/of/property.c - Procedures for accessing and interpreting
+ *			   Devicetree properties and graphs.
+ *
+ * Initially created by copying procedures from drivers/of/base.c. This
+ * file contains the OF property as well as the OF graph interface
+ * functions.
+ *
+ * Paul Mackerras	August 1996.
+ * Copyright (C) 1996-2005 Paul Mackerras.
+ *
+ *  Adapted for 64bit PowerPC by Dave Engebretsen and Peter Bergner.
+ *    {engebret|bergner}@us.ibm.com
+ *
+ *  Adapted for sparc and sparc64 by David S. Miller davem@davemloft.net
+ *
+ *  Reconsolidated from arch/x/kernel/prom.c by Stephen Rothwell and
+ *  Grant Likely.
+ *
+ *      This program is free software; you can redistribute it and/or
+ *      modify it under the terms of the GNU General Public License
+ *      as published by the Free Software Foundation; either version
+ *      2 of the License, or (at your option) any later version.
+ */
+
+#include <linux/of.h>
+#include <linux/of_device.h>
+#include <linux/of_graph.h>
+#include <linux/string.h>
+
+#include "of_private.h"
+/**
+ * of_property_count_elems_of_size - Count the number of elements in a property
+ *
+ * @np:		device node from which the property value is to be read.
+ * @propname:	name of the property to be searched.
+ * @elem_size:	size of the individual element
+ *
+ * Search for a property in a device node and count the number of elements of
+ * size elem_size in it. Returns number of elements on sucess, -EINVAL if the
+ * property does not exist or its length does not match a multiple of elem_size
+ * and -ENODATA if the property does not have a value.
+ */
+int of_property_count_elems_of_size(const struct device_node *np,
+				const char *propname, int elem_size)
+{
+	struct property *prop = of_find_property(np, propname, NULL);
+
+	if (!prop)
+		return -EINVAL;
+	if (!prop->value)
+		return -ENODATA;
+
+	if (prop->length % elem_size != 0) {
+		pr_err("size of %s in node %s is not a multiple of %d\n",
+		       propname, np->full_name, elem_size);
+		return -EINVAL;
+	}
+
+	return prop->length / elem_size;
+}
+EXPORT_SYMBOL_GPL(of_property_count_elems_of_size);
+
+/**
+ * of_find_property_value_of_size
+ *
+ * @np:		device node from which the property value is to be read.
+ * @propname:	name of the property to be searched.
+ * @min:	minimum allowed length of property value
+ * @max:	maximum allowed length of property value (0 means unlimited)
+ * @len:	if !=NULL, actual length is written to here
+ *
+ * Search for a property in a device node and valid the requested size.
+ * Returns the property value on success, -EINVAL if the property does not
+ *  exist, -ENODATA if property does not have a value, and -EOVERFLOW if the
+ * property data is too small or too large.
+ *
+ */
+static void *of_find_property_value_of_size(const struct device_node *np,
+			const char *propname, u32 min, u32 max, size_t *len)
+{
+	struct property *prop = of_find_property(np, propname, NULL);
+
+	if (!prop)
+		return ERR_PTR(-EINVAL);
+	if (!prop->value)
+		return ERR_PTR(-ENODATA);
+	if (prop->length < min)
+		return ERR_PTR(-EOVERFLOW);
+	if (max && prop->length > max)
+		return ERR_PTR(-EOVERFLOW);
+
+	if (len)
+		*len = prop->length;
+
+	return prop->value;
+}
+
+/**
+ * of_property_read_u32_index - Find and read a u32 from a multi-value property.
+ *
+ * @np:		device node from which the property value is to be read.
+ * @propname:	name of the property to be searched.
+ * @index:	index of the u32 in the list of values
+ * @out_value:	pointer to return value, modified only if no error.
+ *
+ * Search for a property in a device node and read nth 32-bit value from
+ * it. Returns 0 on success, -EINVAL if the property does not exist,
+ * -ENODATA if property does not have a value, and -EOVERFLOW if the
+ * property data isn't large enough.
+ *
+ * The out_value is modified only if a valid u32 value can be decoded.
+ */
+int of_property_read_u32_index(const struct device_node *np,
+				       const char *propname,
+				       u32 index, u32 *out_value)
+{
+	const u32 *val = of_find_property_value_of_size(np, propname,
+					((index + 1) * sizeof(*out_value)),
+					0,
+					NULL);
+
+	if (IS_ERR(val))
+		return PTR_ERR(val);
+
+	*out_value = be32_to_cpup(((__be32 *)val) + index);
+	return 0;
+}
+EXPORT_SYMBOL_GPL(of_property_read_u32_index);
+
+/**
+ * of_property_read_u64_index - Find and read a u64 from a multi-value property.
+ *
+ * @np:		device node from which the property value is to be read.
+ * @propname:	name of the property to be searched.
+ * @index:	index of the u64 in the list of values
+ * @out_value:	pointer to return value, modified only if no error.
+ *
+ * Search for a property in a device node and read nth 64-bit value from
+ * it. Returns 0 on success, -EINVAL if the property does not exist,
+ * -ENODATA if property does not have a value, and -EOVERFLOW if the
+ * property data isn't large enough.
+ *
+ * The out_value is modified only if a valid u64 value can be decoded.
+ */
+int of_property_read_u64_index(const struct device_node *np,
+				       const char *propname,
+				       u32 index, u64 *out_value)
+{
+	const u64 *val = of_find_property_value_of_size(np, propname,
+					((index + 1) * sizeof(*out_value)),
+					0, NULL);
+
+	if (IS_ERR(val))
+		return PTR_ERR(val);
+
+	*out_value = be64_to_cpup(((__be64 *)val) + index);
+	return 0;
+}
+EXPORT_SYMBOL_GPL(of_property_read_u64_index);
+
+/**
+ * of_property_read_variable_u8_array - Find and read an array of u8 from a
+ * property, with bounds on the minimum and maximum array size.
+ *
+ * @np:		device node from which the property value is to be read.
+ * @propname:	name of the property to be searched.
+ * @out_values:	pointer to return value, modified only if return value is 0.
+ * @sz_min:	minimum number of array elements to read
+ * @sz_max:	maximum number of array elements to read, if zero there is no
+ *		upper limit on the number of elements in the dts entry but only
+ *		sz_min will be read.
+ *
+ * Search for a property in a device node and read 8-bit value(s) from
+ * it. Returns number of elements read on success, -EINVAL if the property
+ * does not exist, -ENODATA if property does not have a value, and -EOVERFLOW
+ * if the property data is smaller than sz_min or longer than sz_max.
+ *
+ * dts entry of array should be like:
+ *	property = /bits/ 8 <0x50 0x60 0x70>;
+ *
+ * The out_values is modified only if a valid u8 value can be decoded.
+ */
+int of_property_read_variable_u8_array(const struct device_node *np,
+					const char *propname, u8 *out_values,
+					size_t sz_min, size_t sz_max)
+{
+	size_t sz, count;
+	const u8 *val = of_find_property_value_of_size(np, propname,
+						(sz_min * sizeof(*out_values)),
+						(sz_max * sizeof(*out_values)),
+						&sz);
+
+	if (IS_ERR(val))
+		return PTR_ERR(val);
+
+	if (!sz_max)
+		sz = sz_min;
+	else
+		sz /= sizeof(*out_values);
+
+	count = sz;
+	while (count--)
+		*out_values++ = *val++;
+
+	return sz;
+}
+EXPORT_SYMBOL_GPL(of_property_read_variable_u8_array);
+
+/**
+ * of_property_read_variable_u16_array - Find and read an array of u16 from a
+ * property, with bounds on the minimum and maximum array size.
+ *
+ * @np:		device node from which the property value is to be read.
+ * @propname:	name of the property to be searched.
+ * @out_values:	pointer to return value, modified only if return value is 0.
+ * @sz_min:	minimum number of array elements to read
+ * @sz_max:	maximum number of array elements to read, if zero there is no
+ *		upper limit on the number of elements in the dts entry but only
+ *		sz_min will be read.
+ *
+ * Search for a property in a device node and read 16-bit value(s) from
+ * it. Returns number of elements read on success, -EINVAL if the property
+ * does not exist, -ENODATA if property does not have a value, and -EOVERFLOW
+ * if the property data is smaller than sz_min or longer than sz_max.
+ *
+ * dts entry of array should be like:
+ *	property = /bits/ 16 <0x5000 0x6000 0x7000>;
+ *
+ * The out_values is modified only if a valid u16 value can be decoded.
+ */
+int of_property_read_variable_u16_array(const struct device_node *np,
+					const char *propname, u16 *out_values,
+					size_t sz_min, size_t sz_max)
+{
+	size_t sz, count;
+	const __be16 *val = of_find_property_value_of_size(np, propname,
+						(sz_min * sizeof(*out_values)),
+						(sz_max * sizeof(*out_values)),
+						&sz);
+
+	if (IS_ERR(val))
+		return PTR_ERR(val);
+
+	if (!sz_max)
+		sz = sz_min;
+	else
+		sz /= sizeof(*out_values);
+
+	count = sz;
+	while (count--)
+		*out_values++ = be16_to_cpup(val++);
+
+	return sz;
+}
+EXPORT_SYMBOL_GPL(of_property_read_variable_u16_array);
+
+/**
+ * of_property_read_variable_u32_array - Find and read an array of 32 bit
+ * integers from a property, with bounds on the minimum and maximum array size.
+ *
+ * @np:		device node from which the property value is to be read.
+ * @propname:	name of the property to be searched.
+ * @out_values:	pointer to return value, modified only if return value is 0.
+ * @sz_min:	minimum number of array elements to read
+ * @sz_max:	maximum number of array elements to read, if zero there is no
+ *		upper limit on the number of elements in the dts entry but only
+ *		sz_min will be read.
+ *
+ * Search for a property in a device node and read 32-bit value(s) from
+ * it. Returns number of elements read on success, -EINVAL if the property
+ * does not exist, -ENODATA if property does not have a value, and -EOVERFLOW
+ * if the property data is smaller than sz_min or longer than sz_max.
+ *
+ * The out_values is modified only if a valid u32 value can be decoded.
+ */
+int of_property_read_variable_u32_array(const struct device_node *np,
+			       const char *propname, u32 *out_values,
+			       size_t sz_min, size_t sz_max)
+{
+	size_t sz, count;
+	const __be32 *val = of_find_property_value_of_size(np, propname,
+						(sz_min * sizeof(*out_values)),
+						(sz_max * sizeof(*out_values)),
+						&sz);
+
+	if (IS_ERR(val))
+		return PTR_ERR(val);
+
+	if (!sz_max)
+		sz = sz_min;
+	else
+		sz /= sizeof(*out_values);
+
+	count = sz;
+	while (count--)
+		*out_values++ = be32_to_cpup(val++);
+
+	return sz;
+}
+EXPORT_SYMBOL_GPL(of_property_read_variable_u32_array);
+
+/**
+ * of_property_read_u64 - Find and read a 64 bit integer from a property
+ * @np:		device node from which the property value is to be read.
+ * @propname:	name of the property to be searched.
+ * @out_value:	pointer to return value, modified only if return value is 0.
+ *
+ * Search for a property in a device node and read a 64-bit value from
+ * it. Returns 0 on success, -EINVAL if the property does not exist,
+ * -ENODATA if property does not have a value, and -EOVERFLOW if the
+ * property data isn't large enough.
+ *
+ * The out_value is modified only if a valid u64 value can be decoded.
+ */
+int of_property_read_u64(const struct device_node *np, const char *propname,
+			 u64 *out_value)
+{
+	const __be32 *val = of_find_property_value_of_size(np, propname,
+						sizeof(*out_value),
+						0,
+						NULL);
+
+	if (IS_ERR(val))
+		return PTR_ERR(val);
+
+	*out_value = of_read_number(val, 2);
+	return 0;
+}
+EXPORT_SYMBOL_GPL(of_property_read_u64);
+
+/**
+ * of_property_read_variable_u64_array - Find and read an array of 64 bit
+ * integers from a property, with bounds on the minimum and maximum array size.
+ *
+ * @np:		device node from which the property value is to be read.
+ * @propname:	name of the property to be searched.
+ * @out_values:	pointer to return value, modified only if return value is 0.
+ * @sz_min:	minimum number of array elements to read
+ * @sz_max:	maximum number of array elements to read, if zero there is no
+ *		upper limit on the number of elements in the dts entry but only
+ *		sz_min will be read.
+ *
+ * Search for a property in a device node and read 64-bit value(s) from
+ * it. Returns number of elements read on success, -EINVAL if the property
+ * does not exist, -ENODATA if property does not have a value, and -EOVERFLOW
+ * if the property data is smaller than sz_min or longer than sz_max.
+ *
+ * The out_values is modified only if a valid u64 value can be decoded.
+ */
+int of_property_read_variable_u64_array(const struct device_node *np,
+			       const char *propname, u64 *out_values,
+			       size_t sz_min, size_t sz_max)
+{
+	size_t sz, count;
+	const __be32 *val = of_find_property_value_of_size(np, propname,
+						(sz_min * sizeof(*out_values)),
+						(sz_max * sizeof(*out_values)),
+						&sz);
+
+	if (IS_ERR(val))
+		return PTR_ERR(val);
+
+	if (!sz_max)
+		sz = sz_min;
+	else
+		sz /= sizeof(*out_values);
+
+	count = sz;
+	while (count--) {
+		*out_values++ = of_read_number(val, 2);
+		val += 2;
+	}
+
+	return sz;
+}
+EXPORT_SYMBOL_GPL(of_property_read_variable_u64_array);
+
+/**
+ * of_property_read_string - Find and read a string from a property
+ * @np:		device node from which the property value is to be read.
+ * @propname:	name of the property to be searched.
+ * @out_string:	pointer to null terminated return string, modified only if
+ *		return value is 0.
+ *
+ * Search for a property in a device tree node and retrieve a null
+ * terminated string value (pointer to data, not a copy). Returns 0 on
+ * success, -EINVAL if the property does not exist, -ENODATA if property
+ * does not have a value, and -EILSEQ if the string is not null-terminated
+ * within the length of the property data.
+ *
+ * The out_string pointer is modified only if a valid string can be decoded.
+ */
+int of_property_read_string(const struct device_node *np, const char *propname,
+				const char **out_string)
+{
+	const struct property *prop = of_find_property(np, propname, NULL);
+	if (!prop)
+		return -EINVAL;
+	if (!prop->value)
+		return -ENODATA;
+	if (strnlen(prop->value, prop->length) >= prop->length)
+		return -EILSEQ;
+	*out_string = prop->value;
+	return 0;
+}
+EXPORT_SYMBOL_GPL(of_property_read_string);
+
+/**
+ * of_property_match_string() - Find string in a list and return index
+ * @np: pointer to node containing string list property
+ * @propname: string list property name
+ * @string: pointer to string to search for in string list
+ *
+ * This function searches a string list property and returns the index
+ * of a specific string value.
+ */
+int of_property_match_string(const struct device_node *np, const char *propname,
+			     const char *string)
+{
+	const struct property *prop = of_find_property(np, propname, NULL);
+	size_t l;
+	int i;
+	const char *p, *end;
+
+	if (!prop)
+		return -EINVAL;
+	if (!prop->value)
+		return -ENODATA;
+
+	p = prop->value;
+	end = p + prop->length;
+
+	for (i = 0; p < end; i++, p += l) {
+		l = strnlen(p, end - p) + 1;
+		if (p + l > end)
+			return -EILSEQ;
+		pr_debug("comparing %s with %s\n", string, p);
+		if (strcmp(string, p) == 0)
+			return i; /* Found it; return index */
+	}
+	return -ENODATA;
+}
+EXPORT_SYMBOL_GPL(of_property_match_string);
+
+/**
+ * of_property_read_string_helper() - Utility helper for parsing string properties
+ * @np:		device node from which the property value is to be read.
+ * @propname:	name of the property to be searched.
+ * @out_strs:	output array of string pointers.
+ * @sz:		number of array elements to read.
+ * @skip:	Number of strings to skip over at beginning of list.
+ *
+ * Don't call this function directly. It is a utility helper for the
+ * of_property_read_string*() family of functions.
+ */
+int of_property_read_string_helper(const struct device_node *np,
+				   const char *propname, const char **out_strs,
+				   size_t sz, int skip)
+{
+	const struct property *prop = of_find_property(np, propname, NULL);
+	int l = 0, i = 0;
+	const char *p, *end;
+
+	if (!prop)
+		return -EINVAL;
+	if (!prop->value)
+		return -ENODATA;
+	p = prop->value;
+	end = p + prop->length;
+
+	for (i = 0; p < end && (!out_strs || i < skip + sz); i++, p += l) {
+		l = strnlen(p, end - p) + 1;
+		if (p + l > end)
+			return -EILSEQ;
+		if (out_strs && i >= skip)
+			*out_strs++ = p;
+	}
+	i -= skip;
+	return i <= 0 ? -ENODATA : i;
+}
+EXPORT_SYMBOL_GPL(of_property_read_string_helper);
+
+const __be32 *of_prop_next_u32(struct property *prop, const __be32 *cur,
+			       u32 *pu)
+{
+	const void *curv = cur;
+
+	if (!prop)
+		return NULL;
+
+	if (!cur) {
+		curv = prop->value;
+		goto out_val;
+	}
+
+	curv += sizeof(*cur);
+	if (curv >= prop->value + prop->length)
+		return NULL;
+
+out_val:
+	*pu = be32_to_cpup(curv);
+	return curv;
+}
+EXPORT_SYMBOL_GPL(of_prop_next_u32);
+
+const char *of_prop_next_string(struct property *prop, const char *cur)
+{
+	const void *curv = cur;
+
+	if (!prop)
+		return NULL;
+
+	if (!cur)
+		return prop->value;
+
+	curv += strlen(cur) + 1;
+	if (curv >= prop->value + prop->length)
+		return NULL;
+
+	return curv;
+}
+EXPORT_SYMBOL_GPL(of_prop_next_string);
+
+/**
+ * of_graph_parse_endpoint() - parse common endpoint node properties
+ * @node: pointer to endpoint device_node
+ * @endpoint: pointer to the OF endpoint data structure
+ *
+ * The caller should hold a reference to @node.
+ */
+int of_graph_parse_endpoint(const struct device_node *node,
+			    struct of_endpoint *endpoint)
+{
+	struct device_node *port_node = of_get_parent(node);
+
+	WARN_ONCE(!port_node, "%s(): endpoint %s has no parent node\n",
+		  __func__, node->full_name);
+
+	memset(endpoint, 0, sizeof(*endpoint));
+
+	endpoint->local_node = node;
+	/*
+	 * It doesn't matter whether the two calls below succeed.
+	 * If they don't then the default value 0 is used.
+	 */
+	of_property_read_u32(port_node, "reg", &endpoint->port);
+	of_property_read_u32(node, "reg", &endpoint->id);
+
+	of_node_put(port_node);
+
+	return 0;
+}
+EXPORT_SYMBOL(of_graph_parse_endpoint);
+
+/**
+ * of_graph_get_port_by_id() - get the port matching a given id
+ * @parent: pointer to the parent device node
+ * @id: id of the port
+ *
+ * Return: A 'port' node pointer with refcount incremented. The caller
+ * has to use of_node_put() on it when done.
+ */
+struct device_node *of_graph_get_port_by_id(struct device_node *parent, u32 id)
+{
+	struct device_node *node, *port;
+
+	node = of_get_child_by_name(parent, "ports");
+	if (node)
+		parent = node;
+
+	for_each_child_of_node(parent, port) {
+		u32 port_id = 0;
+
+		if (of_node_cmp(port->name, "port") != 0)
+			continue;
+		of_property_read_u32(port, "reg", &port_id);
+		if (id == port_id)
+			break;
+	}
+
+	of_node_put(node);
+
+	return port;
+}
+EXPORT_SYMBOL(of_graph_get_port_by_id);
+
+/**
+ * of_graph_get_next_endpoint() - get next endpoint node
+ * @parent: pointer to the parent device node
+ * @prev: previous endpoint node, or NULL to get first
+ *
+ * Return: An 'endpoint' node pointer with refcount incremented. Refcount
+ * of the passed @prev node is decremented.
+ */
+struct device_node *of_graph_get_next_endpoint(const struct device_node *parent,
+					struct device_node *prev)
+{
+	struct device_node *endpoint;
+	struct device_node *port;
+
+	if (!parent)
+		return NULL;
+
+	/*
+	 * Start by locating the port node. If no previous endpoint is specified
+	 * search for the first port node, otherwise get the previous endpoint
+	 * parent port node.
+	 */
+	if (!prev) {
+		struct device_node *node;
+
+		node = of_get_child_by_name(parent, "ports");
+		if (node)
+			parent = node;
+
+		port = of_get_child_by_name(parent, "port");
+		of_node_put(node);
+
+		if (!port) {
+			pr_err("graph: no port node found in %s\n",
+			       parent->full_name);
+			return NULL;
+		}
+	} else {
+		port = of_get_parent(prev);
+		if (WARN_ONCE(!port, "%s(): endpoint %s has no parent node\n",
+			      __func__, prev->full_name))
+			return NULL;
+	}
+
+	while (1) {
+		/*
+		 * Now that we have a port node, get the next endpoint by
+		 * getting the next child. If the previous endpoint is NULL this
+		 * will return the first child.
+		 */
+		endpoint = of_get_next_child(port, prev);
+		if (endpoint) {
+			of_node_put(port);
+			return endpoint;
+		}
+
+		/* No more endpoints under this port, try the next one. */
+		prev = NULL;
+
+		do {
+			port = of_get_next_child(parent, port);
+			if (!port)
+				return NULL;
+		} while (of_node_cmp(port->name, "port"));
+	}
+}
+EXPORT_SYMBOL(of_graph_get_next_endpoint);
+
+/**
+ * of_graph_get_endpoint_by_regs() - get endpoint node of specific identifiers
+ * @parent: pointer to the parent device node
+ * @port_reg: identifier (value of reg property) of the parent port node
+ * @reg: identifier (value of reg property) of the endpoint node
+ *
+ * Return: An 'endpoint' node pointer which is identified by reg and at the same
+ * is the child of a port node identified by port_reg. reg and port_reg are
+ * ignored when they are -1.
+ */
+struct device_node *of_graph_get_endpoint_by_regs(
+	const struct device_node *parent, int port_reg, int reg)
+{
+	struct of_endpoint endpoint;
+	struct device_node *node = NULL;
+
+	for_each_endpoint_of_node(parent, node) {
+		of_graph_parse_endpoint(node, &endpoint);
+		if (((port_reg == -1) || (endpoint.port == port_reg)) &&
+			((reg == -1) || (endpoint.id == reg)))
+			return node;
+	}
+
+	return NULL;
+}
+EXPORT_SYMBOL(of_graph_get_endpoint_by_regs);
+
+/**
+ * of_graph_get_remote_port_parent() - get remote port's parent node
+ * @node: pointer to a local endpoint device_node
+ *
+ * Return: Remote device node associated with remote endpoint node linked
+ *	   to @node. Use of_node_put() on it when done.
+ */
+struct device_node *of_graph_get_remote_port_parent(
+			       const struct device_node *node)
+{
+	struct device_node *np;
+	unsigned int depth;
+
+	/* Get remote endpoint node. */
+	np = of_parse_phandle(node, "remote-endpoint", 0);
+
+	/* Walk 3 levels up only if there is 'ports' node. */
+	for (depth = 3; depth && np; depth--) {
+		np = of_get_next_parent(np);
+		if (depth == 2 && of_node_cmp(np->name, "ports"))
+			break;
+	}
+	return np;
+}
+EXPORT_SYMBOL(of_graph_get_remote_port_parent);
+
+/**
+ * of_graph_get_remote_port() - get remote port node
+ * @node: pointer to a local endpoint device_node
+ *
+ * Return: Remote port node associated with remote endpoint node linked
+ *	   to @node. Use of_node_put() on it when done.
+ */
+struct device_node *of_graph_get_remote_port(const struct device_node *node)
+{
+	struct device_node *np;
+
+	/* Get remote endpoint node. */
+	np = of_parse_phandle(node, "remote-endpoint", 0);
+	if (!np)
+		return NULL;
+	return of_get_next_parent(np);
+}
+EXPORT_SYMBOL(of_graph_get_remote_port);
+
+/**
+ * of_graph_get_remote_node() - get remote parent device_node for given port/endpoint
+ * @node: pointer to parent device_node containing graph port/endpoint
+ * @port: identifier (value of reg property) of the parent port node
+ * @endpoint: identifier (value of reg property) of the endpoint node
+ *
+ * Return: Remote device node associated with remote endpoint node linked
+ *	   to @node. Use of_node_put() on it when done.
+ */
+struct device_node *of_graph_get_remote_node(const struct device_node *node,
+					     u32 port, u32 endpoint)
+{
+	struct device_node *endpoint_node, *remote;
+
+	endpoint_node = of_graph_get_endpoint_by_regs(node, port, endpoint);
+	if (!endpoint_node) {
+		pr_debug("no valid endpoint (%d, %d) for node %s\n",
+			 port, endpoint, node->full_name);
+		return NULL;
+	}
+
+	remote = of_graph_get_remote_port_parent(endpoint_node);
+	of_node_put(endpoint_node);
+	if (!remote) {
+		pr_debug("no valid remote node\n");
+		return NULL;
+	}
+
+	if (!of_device_is_available(remote)) {
+		pr_debug("not available for remote node\n");
+		return NULL;
+	}
+
+	return remote;
+}
+EXPORT_SYMBOL(of_graph_get_remote_node);
-- 
2.7.4


^ permalink raw reply related

* [PATCH v5 5/5] ARM: dts: rockchip: enable ARM Mali GPU on rk3288-veyron
From: Guillaume Tucker @ 2017-05-03  9:56 UTC (permalink / raw)
  To: Rob Herring, Mark Rutland, Heiko Stübner, Neil Armstrong
  Cc: Sjoerd Simons, Enric Balletbo i Serra, John Reitan, Wookey,
	devicetree, linux-rockchip, linux-arm-kernel, linux-kernel,
	Guillaume Tucker
In-Reply-To: <cover.1493804968.git.guillaume.tucker@collabora.com>

From: Enric Balletbo i Serra <enric.balletbo@collabora.com>

Add reference to the Mali GPU device tree node on rk3288-veyron.
Tested on Minnie and Jerry boards.

Signed-off-by: Enric Balletbo i Serra <enric.balletbo@collabora.com>
Signed-off-by: Guillaume Tucker <guillaume.tucker@collabora.com>
---
 arch/arm/boot/dts/rk3288-veyron.dtsi | 5 +++++
 1 file changed, 5 insertions(+)

diff --git a/arch/arm/boot/dts/rk3288-veyron.dtsi b/arch/arm/boot/dts/rk3288-veyron.dtsi
index 5d1eb0a25827..9847d5c6db3b 100644
--- a/arch/arm/boot/dts/rk3288-veyron.dtsi
+++ b/arch/arm/boot/dts/rk3288-veyron.dtsi
@@ -447,6 +447,11 @@
 	status = "okay";
 };
 
+&gpu {
+	mali-supply = <&vdd_gpu>;
+	status = "okay";
+};
+
 &wdt {
 	status = "okay";
 };
-- 
2.11.0

^ permalink raw reply related

* [PATCH v5 4/5] ARM: dts: rockchip: enable ARM Mali GPU on rk3288-firefly
From: Guillaume Tucker @ 2017-05-03  9:56 UTC (permalink / raw)
  To: Rob Herring, Mark Rutland, Heiko Stübner, Neil Armstrong
  Cc: devicetree, Guillaume Tucker, Sjoerd Simons, Wookey, linux-kernel,
	linux-rockchip, John Reitan, Enric Balletbo i Serra,
	linux-arm-kernel
In-Reply-To: <cover.1493804968.git.guillaume.tucker@collabora.com>

Add reference to the Mali GPU device tree node on rk3288-firefly.
Tested on Firefly board.

Signed-off-by: Guillaume Tucker <guillaume.tucker@collabora.com>
---
 arch/arm/boot/dts/rk3288-firefly.dtsi | 5 +++++
 1 file changed, 5 insertions(+)

diff --git a/arch/arm/boot/dts/rk3288-firefly.dtsi b/arch/arm/boot/dts/rk3288-firefly.dtsi
index 10793ac18599..f520589493b4 100644
--- a/arch/arm/boot/dts/rk3288-firefly.dtsi
+++ b/arch/arm/boot/dts/rk3288-firefly.dtsi
@@ -594,3 +594,8 @@
 &wdt {
 	status = "okay";
 };
+
+&gpu {
+	mali-supply = <&vdd_gpu>;
+	status = "okay";
+};
-- 
2.11.0

^ permalink raw reply related

* [PATCH v5 3/5] ARM: dts: rockchip: enable ARM Mali GPU on rk3288-rock2-som
From: Guillaume Tucker @ 2017-05-03  9:56 UTC (permalink / raw)
  To: Rob Herring, Mark Rutland, Heiko Stübner, Neil Armstrong
  Cc: devicetree, Guillaume Tucker, Sjoerd Simons, Wookey, linux-kernel,
	linux-rockchip, John Reitan, Enric Balletbo i Serra,
	linux-arm-kernel
In-Reply-To: <cover.1493804968.git.guillaume.tucker@collabora.com>

Add reference to the Mali GPU device tree node on the
rk3288-rock2-som platform.  Tested on a Radxa Rock2 Square board.

Signed-off-by: Guillaume Tucker <guillaume.tucker@collabora.com>
---
 arch/arm/boot/dts/rk3288-rock2-som.dtsi | 5 +++++
 1 file changed, 5 insertions(+)

diff --git a/arch/arm/boot/dts/rk3288-rock2-som.dtsi b/arch/arm/boot/dts/rk3288-rock2-som.dtsi
index 1c0bbc9b928b..f694867fa46a 100644
--- a/arch/arm/boot/dts/rk3288-rock2-som.dtsi
+++ b/arch/arm/boot/dts/rk3288-rock2-som.dtsi
@@ -301,3 +301,8 @@
 &wdt {
 	status = "okay";
 };
+
+&gpu {
+	mali-supply = <&vdd_gpu>;
+	status = "okay";
+};
-- 
2.11.0

^ permalink raw reply related

* [PATCH v5 2/5] ARM: dts: rockchip: add ARM Mali GPU node for rk3288
From: Guillaume Tucker @ 2017-05-03  9:56 UTC (permalink / raw)
  To: Rob Herring, Mark Rutland, Heiko Stübner, Neil Armstrong
  Cc: devicetree, Guillaume Tucker, Sjoerd Simons, Wookey, linux-kernel,
	linux-rockchip, John Reitan, Enric Balletbo i Serra,
	linux-arm-kernel
In-Reply-To: <cover.1493804968.git.guillaume.tucker@collabora.com>

Add Mali GPU device tree node for the rk3288 SoC, with devfreq
opp table.

Tested-by: Enric Balletbo i Serra <enric.balletbo@collabora.com>
Signed-off-by: Guillaume Tucker <guillaume.tucker@collabora.com>
---
 arch/arm/boot/dts/rk3288.dtsi | 43 +++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 43 insertions(+)

diff --git a/arch/arm/boot/dts/rk3288.dtsi b/arch/arm/boot/dts/rk3288.dtsi
index df8a0dbe9d91..35969041eae2 100644
--- a/arch/arm/boot/dts/rk3288.dtsi
+++ b/arch/arm/boot/dts/rk3288.dtsi
@@ -43,6 +43,7 @@
 #include <dt-bindings/interrupt-controller/arm-gic.h>
 #include <dt-bindings/pinctrl/rockchip.h>
 #include <dt-bindings/clock/rk3288-cru.h>
+#include <dt-bindings/power/rk3288-power.h>
 #include <dt-bindings/thermal/thermal.h>
 #include <dt-bindings/power/rk3288-power.h>
 #include <dt-bindings/soc/rockchip,boot-mode.h>
@@ -1117,6 +1118,48 @@
 		};
 	};
 
+	gpu: mali@ffa30000 {
+		compatible = "rockchip,rk3288-mali", "arm,mali-t760", "arm,mali-midgard";
+		reg = <0xffa30000 0x10000>;
+		interrupts = <GIC_SPI 6 IRQ_TYPE_LEVEL_HIGH>,
+			     <GIC_SPI 7 IRQ_TYPE_LEVEL_HIGH>,
+			     <GIC_SPI 8 IRQ_TYPE_LEVEL_HIGH>;
+		interrupt-names = "job", "mmu", "gpu";
+		clocks = <&cru ACLK_GPU>;
+		operating-points-v2 = <&gpu_opp_table>;
+		power-domains = <&power RK3288_PD_GPU>;
+		status = "disabled";
+	};
+
+	gpu_opp_table: opp_table0 {
+		compatible = "operating-points-v2";
+
+		opp@100000000 {
+			opp-hz = /bits/ 64 <100000000>;
+			opp-microvolt = <950000>;
+		};
+		opp@200000000 {
+			opp-hz = /bits/ 64 <200000000>;
+			opp-microvolt = <950000>;
+		};
+		opp@300000000 {
+			opp-hz = /bits/ 64 <300000000>;
+			opp-microvolt = <1000000>;
+		};
+		opp@400000000 {
+			opp-hz = /bits/ 64 <400000000>;
+			opp-microvolt = <1100000>;
+		};
+		opp@500000000 {
+			opp-hz = /bits/ 64 <500000000>;
+			opp-microvolt = <1200000>;
+		};
+		opp@600000000 {
+			opp-hz = /bits/ 64 <600000000>;
+			opp-microvolt = <1250000>;
+		};
+	};
+
 	qos_gpu_r: qos@ffaa0000 {
 		compatible = "syscon";
 		reg = <0xffaa0000 0x20>;
-- 
2.11.0

^ permalink raw reply related

* [PATCH v5 1/5] dt-bindings: gpu: add bindings for the ARM Mali Midgard GPU
From: Guillaume Tucker @ 2017-05-03  9:56 UTC (permalink / raw)
  To: Rob Herring, Mark Rutland, Heiko Stübner, Neil Armstrong
  Cc: Sjoerd Simons, Enric Balletbo i Serra, John Reitan, Wookey,
	devicetree, linux-rockchip, linux-arm-kernel, linux-kernel,
	Guillaume Tucker
In-Reply-To: <cover.1493804968.git.guillaume.tucker@collabora.com>

The ARM Mali Midgard GPU family is present in a number of SoCs
from many different vendors such as Samsung Exynos and Rockchip.

Import the device tree bindings documentation from the r16p0
release of the Mali Midgard GPU kernel driver:

  https://developer.arm.com/-/media/Files/downloads/mali-drivers/kernel/mali-midgard-gpu/TX011-SW-99002-r16p0-00rel0.tgz

Remove the copyright and GPL licence header as deemed not necessary.

Redesign the "compatible" property strings to list all the Mali
Midgard GPU types and add vendor specific ones.

Drop the "clock-names" property as the Mali Midgard GPU uses only one
clock (the driver now needs to call clk_get with NULL).

Convert the "interrupt-names" property values to lower-case: "job",
"mmu" and "gpu".

Replace the deprecated "operating-points" optional property with
"operating-points-v2".

Omit the following optional properties in this initial version as they
are only used in very specific cases:

  * snoop_enable_smc
  * snoop_disable_smc
  * jm_config
  * power_model
  * system-coherency
  * ipa-model

Update the example accordingly to reflect all these changes, based on
rk3288 mali-t760.

CC: John Reitan <john.reitan@arm.com>
Tested-by: Enric Balletbo i Serra <enric.balletbo@collabora.com>
Signed-off-by: Guillaume Tucker <guillaume.tucker@collabora.com>
---
 .../devicetree/bindings/gpu/arm,mali-midgard.txt   | 86 ++++++++++++++++++++++
 1 file changed, 86 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/gpu/arm,mali-midgard.txt

diff --git a/Documentation/devicetree/bindings/gpu/arm,mali-midgard.txt b/Documentation/devicetree/bindings/gpu/arm,mali-midgard.txt
new file mode 100644
index 000000000000..d3b6e1a4713a
--- /dev/null
+++ b/Documentation/devicetree/bindings/gpu/arm,mali-midgard.txt
@@ -0,0 +1,86 @@
+ARM Mali Midgard GPU
+====================
+
+Required properties:
+
+- compatible :
+  * Must contain one of the following:
+    + "arm,mali-t604"
+    + "arm,mali-t624"
+    + "arm,mali-t628"
+    + "arm,mali-t720"
+    + "arm,mali-t760"
+    + "arm,mali-t820"
+    + "arm,mali-t830"
+    + "arm,mali-t860"
+    + "arm,mali-t880"
+  * which must be preceded by one of the following vendor specifics:
+    + "amlogic,meson-gxm-mali"
+    + "rockchip,rk3288-mali"
+
+- reg : Physical base address of the device and length of the register area.
+
+- interrupts : Contains the three IRQ lines required by Mali Midgard devices.
+
+- interrupt-names : Contains the names of IRQ resources in the order they were
+  provided in the interrupts property. Must contain: "job", "mmu", "gpu".
+
+
+Optional properties:
+
+- clocks : Phandle to clock for the Mali Midgard device.
+
+- mali-supply : Phandle to regulator for the Mali device. Refer to
+  Documentation/devicetree/bindings/regulator/regulator.txt for details.
+
+- operating-points-v2 : Refer to Documentation/devicetree/bindings/power/opp.txt
+  for details.
+
+
+Example for a Mali-T760:
+
+gpu@ffa30000 {
+	compatible = "rockchip,rk3288-mali", "arm,mali-t760", "arm,mali-midgard";
+	reg = <0xffa30000 0x10000>;
+	interrupts = <GIC_SPI 6 IRQ_TYPE_LEVEL_HIGH>,
+		     <GIC_SPI 7 IRQ_TYPE_LEVEL_HIGH>,
+		     <GIC_SPI 8 IRQ_TYPE_LEVEL_HIGH>;
+	interrupt-names = "job", "mmu", "gpu";
+	clocks = <&cru ACLK_GPU>;
+	mali-supply = <&vdd_gpu>;
+	operating-points-v2 = <&gpu_opp_table>;
+	power-domains = <&power RK3288_PD_GPU>;
+};
+
+gpu_opp_table: opp_table0 {
+	compatible = "operating-points-v2";
+
+	opp@533000000 {
+		opp-hz = /bits/ 64 <533000000>;
+		opp-microvolt = <1250000>;
+	};
+	opp@450000000 {
+		opp-hz = /bits/ 64 <450000000>;
+		opp-microvolt = <1150000>;
+	};
+	opp@400000000 {
+		opp-hz = /bits/ 64 <400000000>;
+		opp-microvolt = <1125000>;
+	};
+	opp@350000000 {
+		opp-hz = /bits/ 64 <350000000>;
+		opp-microvolt = <1075000>;
+	};
+	opp@266000000 {
+		opp-hz = /bits/ 64 <266000000>;
+		opp-microvolt = <1025000>;
+	};
+	opp@160000000 {
+		opp-hz = /bits/ 64 <160000000>;
+		opp-microvolt = <925000>;
+	};
+	opp@100000000 {
+		opp-hz = /bits/ 64 <100000000>;
+		opp-microvolt = <912500>;
+	};
+};
-- 
2.11.0

^ permalink raw reply related

* [PATCH v5 0/5] Add ARM Mali Midgard device tree bindings and gpu node for rk3288
From: Guillaume Tucker @ 2017-05-03  9:56 UTC (permalink / raw)
  To: Rob Herring, Mark Rutland, Heiko Stübner, Neil Armstrong
  Cc: Sjoerd Simons, Enric Balletbo i Serra, John Reitan, Wookey,
	devicetree-u79uwXL29TY76Z2rM5mHXA,
	linux-rockchip-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
	linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
	linux-kernel-u79uwXL29TY76Z2rM5mHXA, Guillaume Tucker

The ARM Mali Midgard GPU kernel driver is only available
out-of-tree and is not going to be merged in its current form.
However, it would be useful to have its device tree bindings
merged.  In particular, this would enable distributions to create
working driver packages (dkms...) without having to patch the
kernel.

The bindings for the earlier Mali Utgard GPU family have already
been merged, so this is essentially the same scenario but for
newer GPUs (Mali-T604 ~ Mali-T880).

This series of patches first imports the bindings from the latest
driver release with some clean-up then adds a gpu node for the
rk3288 SoC.  This was successfully tested on Radxa Rock2 Square,
Firefly, Veyron Minnie and Jerry boards using Mali kernel driver
r16p0 and r12p0 user-space binary.


Changes since v1:
- enabled gpu on rk3288-veyron boards

Changes since v2:
- removed "clk-names" property and "clk_mali" name
- converted values of "interrupt-names" property to
  lower-case: "job", "mmu" and "gpu"
- replaced dt compatible strings with list of all Midgard GPU variants and
  optional vendors
- cleaned up gpu node example

Changes since v3:
- add "rockchip,rk3288-mali" vendor compatible string
- move gpu node at the right location in rk3288.dtsi
- use operating-points-v2 in documentation and rk3288.dtsi

Changes since v4:
- removed wildcards (mali-t60x, -t62x)
- vendor compatible strings are not optional any more
- example updated accordingly, based on rk3288


Enric Balletbo i Serra (1):
  ARM: dts: rockchip: enable ARM Mali GPU on rk3288-veyron

Guillaume Tucker (4):
  dt-bindings: gpu: add bindings for the ARM Mali Midgard GPU
  ARM: dts: rockchip: add ARM Mali GPU node for rk3288
  ARM: dts: rockchip: enable ARM Mali GPU on rk3288-rock2-som
  ARM: dts: rockchip: enable ARM Mali GPU on rk3288-firefly

 .../devicetree/bindings/gpu/arm,mali-midgard.txt   | 86 ++++++++++++++++++++++
 arch/arm/boot/dts/rk3288-firefly.dtsi              |  5 ++
 arch/arm/boot/dts/rk3288-rock2-som.dtsi            |  5 ++
 arch/arm/boot/dts/rk3288-veyron.dtsi               |  5 ++
 arch/arm/boot/dts/rk3288.dtsi                      | 43 +++++++++++
 5 files changed, 144 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/gpu/arm,mali-midgard.txt

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

^ permalink raw reply

* Re: [PATCH v3 2/4] soc: qcom: Introduce APCS IPC driver
From: Jassi Brar @ 2017-05-03  9:55 UTC (permalink / raw)
  To: Loic PALLARDY
  Cc: Bjorn Andersson, Andy Gross, Rob Herring, Mark Rutland,
	Ohad Ben-Cohen,
	linux-arm-msm-u79uwXL29TY76Z2rM5mHXA@public.gmane.org,
	linux-soc-u79uwXL29TY76Z2rM5mHXA@public.gmane.org,
	devicetree-u79uwXL29TY76Z2rM5mHXA@public.gmane.org,
	linux-kernel-u79uwXL29TY76Z2rM5mHXA@public.gmane.org,
	linux-remoteproc-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
In-Reply-To: <c9b86ecfa6b84ae994da6c30d2840bbf-GGltqRL9kSEdIAuSVraBWEEOCMrvLtNR@public.gmane.org>

Loic, thanks for adding me.

On Wed, May 3, 2017 at 2:58 PM, Loic PALLARDY <loic.pallardy-qxv4g6HH51o@public.gmane.org> wrote:
>
>
>> -----Original Message-----
>> From: linux-remoteproc-owner-u79uwXL29TY76Z2rM5mHXA@public.gmane.org [mailto:linux-remoteproc-
>> owner-u79uwXL29TY76Z2rM5mHXA@public.gmane.org] On Behalf Of Bjorn Andersson
>> Sent: Wednesday, May 03, 2017 7:29 AM
>> To: Andy Gross <andy.gross-QSEj5FYQhm4dnm+yROfE0A@public.gmane.org>; Rob Herring
>> <robh+dt-DgEjT+Ai2ygdnm+yROfE0A@public.gmane.org>; Mark Rutland <mark.rutland-5wv7dgnIgG8@public.gmane.org>; Ohad Ben-
>> Cohen <ohad-Ix1uc/W3ht7QT0dZR+AlfA@public.gmane.org>
>> Cc: linux-arm-msm-u79uwXL29TY76Z2rM5mHXA@public.gmane.org; linux-soc-u79uwXL29TY76Z2rM5mHXA@public.gmane.org;
>> devicetree-u79uwXL29TY76Z2rM5mHXA@public.gmane.org; linux-kernel-u79uwXL29TY76Z2rM5mHXA@public.gmane.org; linux-
>> remoteproc-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
>> Subject: [PATCH v3 2/4] soc: qcom: Introduce APCS IPC driver
>>
>> This implements a driver that exposes the IPC bits found in the APCS Global
>> block in various Qualcomm platforms. The bits are used to signal inter-
>> processor communication signals from the application CPU to other masters.
>>
>> The driver implements the "doorbell" binding and could be used as basis for a
>> new Linux framework, if found useful outside Qualcomm.
>>
> Hi Bjorn,
>
> Even if Qualcom APCS IPC is limited, why don't you rely on existing mailbox framework.
> It is there to gather all IPC management under the same interface.
> No need to create a new one from my pov.
> If you don't provide message data, mailbox framework behaves as doorbell.
>
QCOM RPM reinvented the wheel for what mailbox framework already did,
despite my pointing it out =>
http://lkml.iu.edu/hypermail//linux/kernel/1406.2/03918.html

The driver bypassed mailbox framework and was pushed via another tree.
Same is being attempted now, only now it is more expensive to switch
to generic mailbox framework having spent so much time on QCOM
specific implementation of controller and protocol drivers inside
drivers/soc/qcom/
--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply

* Re: [PATCH 3/4] net: macb: Add hardware PTP support
From: Richard Cochran @ 2017-05-03  9:43 UTC (permalink / raw)
  To: Rafal Ozieblo
  Cc: David Miller,
	nicolas.ferre-AIFe0yeh4nAAvxtiuMwx3w@public.gmane.org,
	netdev-u79uwXL29TY76Z2rM5mHXA@public.gmane.org,
	linux-kernel-u79uwXL29TY76Z2rM5mHXA@public.gmane.org,
	devicetree-u79uwXL29TY76Z2rM5mHXA@public.gmane.org,
	linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r@public.gmane.org,
	harinikatakamlinux-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org,
	harini.katakam-gjFFaj9aHVfQT0dZR+AlfA@public.gmane.org,
	Andrei.Pistirica-UWL1GkI3JZL3oGB3hsPCZA@public.gmane.org
In-Reply-To: <BN3PR07MB2516757CB4EA7367F6623C06C9170-EldUQEzkDQfxGZiqM5fOI+FPX92sqiQdvxpqHgZTriW3zl9H0oFU5g@public.gmane.org>

On Tue, May 02, 2017 at 01:57:15PM +0000, Rafal Ozieblo wrote:
> > What is the point of this wrapper function anyhow?  Please remove it.
> gem_ptp_gettime() is assigned in ptp_clock_info and it has to have 
> ptp_clock_info pointer as first parameter. gem_tsu_get_time() is used in
> the source code but with macb pointer.
> Do you want me to do something like:
> gem_ptp_gettime(macb->ptp, ts);
> and first would be getting macb pointer from ptp ?
> struct macb *bp = container_of(ptp, struct macb, ptp_clock_info);

Yes.  Unless your sub-function is used in more than one place, then it
is wasteful and confusing to wrap the functionality for no apparent
reason.

> > > +	switch (rq->type) {
> > > +	case PTP_CLK_REQ_EXTTS:	/* Toggle TSU match interrupt */
> > > +		if (on)
> > > +			macb_writel(bp, IER, MACB_BIT(TCI));
> > 
> > No locking to protect IER and IDE?
> There is no need.

But what happens when the PTP_CLK_REQ_EXTTS and PTP_CLK_REQ_PPS ioctls
are called at the same time?

You need to ensure that IDR is consistent.  If the bits are write
only, then you should comment this fact.

> > > +		else
> > > +			macb_writel(bp, IDR, MACB_BIT(TCI));
> > > +		break;
> > > +	case PTP_CLK_REQ_PEROUT: /* Toggle Periodic output */
> > > +		return -EOPNOTSUPP;
> > > +		/* break; */
> > > +	case PTP_CLK_REQ_PPS:	/* Toggle TSU periodic (second)
> > interrupt */
> > > +		if (on)
> > > +			macb_writel(bp, IER, MACB_BIT(SRI));
> > > +		else
> > > +			macb_writel(bp, IDR, MACB_BIT(SRI));
> > > +		break;
> > > +	default:
> > > +		break;
> > > +	}
> > > +	return 0;
> > > +}

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

^ permalink raw reply

* RE: [PATCH v3 2/4] soc: qcom: Introduce APCS IPC driver
From: Loic PALLARDY @ 2017-05-03  9:28 UTC (permalink / raw)
  To: Bjorn Andersson, Andy Gross, Rob Herring, Mark Rutland,
	Ohad Ben-Cohen,
	jassisinghbrar-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org
  Cc: linux-arm-msm-u79uwXL29TY76Z2rM5mHXA@public.gmane.org,
	linux-soc-u79uwXL29TY76Z2rM5mHXA@public.gmane.org,
	devicetree-u79uwXL29TY76Z2rM5mHXA@public.gmane.org,
	linux-kernel-u79uwXL29TY76Z2rM5mHXA@public.gmane.org,
	linux-remoteproc-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
In-Reply-To: <20170503052929.17422-2-bjorn.andersson-QSEj5FYQhm4dnm+yROfE0A@public.gmane.org>



> -----Original Message-----
> From: linux-remoteproc-owner-u79uwXL29TY76Z2rM5mHXA@public.gmane.org [mailto:linux-remoteproc-
> owner-u79uwXL29TY76Z2rM5mHXA@public.gmane.org] On Behalf Of Bjorn Andersson
> Sent: Wednesday, May 03, 2017 7:29 AM
> To: Andy Gross <andy.gross-QSEj5FYQhm4dnm+yROfE0A@public.gmane.org>; Rob Herring
> <robh+dt-DgEjT+Ai2ygdnm+yROfE0A@public.gmane.org>; Mark Rutland <mark.rutland-5wv7dgnIgG8@public.gmane.org>; Ohad Ben-
> Cohen <ohad-Ix1uc/W3ht7QT0dZR+AlfA@public.gmane.org>
> Cc: linux-arm-msm-u79uwXL29TY76Z2rM5mHXA@public.gmane.org; linux-soc-u79uwXL29TY76Z2rM5mHXA@public.gmane.org;
> devicetree-u79uwXL29TY76Z2rM5mHXA@public.gmane.org; linux-kernel-u79uwXL29TY76Z2rM5mHXA@public.gmane.org; linux-
> remoteproc-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
> Subject: [PATCH v3 2/4] soc: qcom: Introduce APCS IPC driver
> 
> This implements a driver that exposes the IPC bits found in the APCS Global
> block in various Qualcomm platforms. The bits are used to signal inter-
> processor communication signals from the application CPU to other masters.
> 
> The driver implements the "doorbell" binding and could be used as basis for a
> new Linux framework, if found useful outside Qualcomm.
> 
Hi Bjorn,

Even if Qualcom APCS IPC is limited, why don't you rely on existing mailbox framework.
It is there to gather all IPC management under the same interface.
No need to create a new one from my pov.
If you don't provide message data, mailbox framework behaves as doorbell.

Regards,
Loic

> Signed-off-by: Bjorn Andersson <bjorn.andersson-QSEj5FYQhm4dnm+yROfE0A@public.gmane.org>
> ---
> 
> Changes since v2:
> - New driver
> 
>  drivers/soc/qcom/Kconfig          |   8 ++
>  drivers/soc/qcom/Makefile         |   1 +
>  drivers/soc/qcom/apcs-ipc.c       | 182
> ++++++++++++++++++++++++++++++++++++++
>  include/linux/soc/qcom/apcs_ipc.h |  26 ++++++
>  4 files changed, 217 insertions(+)
>  create mode 100644 drivers/soc/qcom/apcs-ipc.c  create mode 100644
> include/linux/soc/qcom/apcs_ipc.h
> 
> diff --git a/drivers/soc/qcom/Kconfig b/drivers/soc/qcom/Kconfig index
> 78b1bb7bcf20..4113da81d18b 100644
> --- a/drivers/soc/qcom/Kconfig
> +++ b/drivers/soc/qcom/Kconfig
> @@ -1,6 +1,14 @@
>  #
>  # QCOM Soc drivers
>  #
> +config QCOM_APCS_IPC
> +	tristate "Qualcomm APCS IPC driver"
> +	depends on ARCH_QCOM
> +	help
> +	  Say y here to enable support for the APCS IPC doorbell driver,
> +	  providing an interface for invoking the inter-process communication
> +	  signals from the application processor to other masters.
> +
>  config QCOM_GSBI
>          tristate "QCOM General Serial Bus Interface"
>          depends on ARCH_QCOM
> diff --git a/drivers/soc/qcom/Makefile b/drivers/soc/qcom/Makefile index
> 1f30260b06b8..e15b33e5a630 100644
> --- a/drivers/soc/qcom/Makefile
> +++ b/drivers/soc/qcom/Makefile
> @@ -1,3 +1,4 @@
> +obj-$(CONFIG_QCOM_APCS_IPC) +=	apcs-ipc.o
>  obj-$(CONFIG_QCOM_GSBI)	+=	qcom_gsbi.o
>  obj-$(CONFIG_QCOM_MDT_LOADER)	+= mdt_loader.o
>  obj-$(CONFIG_QCOM_PM)	+=	spm.o
> diff --git a/drivers/soc/qcom/apcs-ipc.c b/drivers/soc/qcom/apcs-ipc.c new
> file mode 100644 index 000000000000..ea835cb08657
> --- /dev/null
> +++ b/drivers/soc/qcom/apcs-ipc.c
> @@ -0,0 +1,182 @@
> +/*
> + * Copyright (c) 2017, Linaro Ltd
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License version 2 and
> + * only 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/kernel.h>
> +#include <linux/module.h>
> +#include <linux/io.h>
> +#include <linux/slab.h>
> +#include <linux/of.h>
> +#include <linux/of_platform.h>
> +#include <linux/platform_device.h>
> +
> +static struct platform_driver qcom_apcs_ipc_driver;
> +
> +struct qcom_apcs_ipc {
> +	struct device *dev;
> +
> +	void __iomem *base;
> +	unsigned long offset;
> +};
> +
> +struct qcom_apcs_ipc_bell {
> +	struct qcom_apcs_ipc *apcs;
> +	unsigned int bit;
> +};
> +
> +static void qcom_apcs_ipc_release(struct device *dev, void *res) {
> +	struct qcom_apcs_ipc_bell *bell = res;
> +	struct qcom_apcs_ipc *apcs = bell->apcs;
> +
> +	put_device(apcs->dev);
> +}
> +
> +/**
> + * qcom_apcs_ipc_get() - acquire a handle to a doorbell
> + * @dev:	client device handle
> + * @id:		identifier of the doorbell
> + *
> + * Returns a doorbell reference, or negative errno on failure.
> + */
> +struct qcom_apcs_ipc_bell *devm_qcom_apcs_ipc_get(struct device *dev,
> +						  const char *id)
> +{
> +	struct qcom_apcs_ipc_bell *bell;
> +	struct platform_device *pdev;
> +	struct of_phandle_args args;
> +	int index = 0;
> +	int ret;
> +
> +	if (id) {
> +		index = of_property_match_string(dev->of_node,
> +						 "doorbell-names", id);
> +		if (index < 0)
> +			return ERR_PTR(index);
> +	}
> +
> +	ret = of_parse_phandle_with_args(dev->of_node, "doorbells",
> +					 "#doorbell-cells", index, &args);
> +	if (ret) {
> +		dev_err(dev, "unable to resolve doorbell\n");
> +		return ERR_PTR(-ENODEV);
> +	}
> +
> +	pdev = of_find_device_by_node(args.np);
> +	of_node_put(args.np);
> +
> +	if (!pdev)
> +		return ERR_PTR(-EPROBE_DEFER);
> +
> +	if (args.args[0] >= 32) {
> +		dev_err(dev, "invalid doorbell requested\n");
> +		ret = -EINVAL;
> +		goto release_device;
> +	}
> +
> +	if (pdev->dev.driver != &qcom_apcs_ipc_driver.driver) {
> +		dev_err(dev, "failed to acquire apcs ipc driver\n");
> +		ret = -EINVAL;
> +		goto release_device;
> +	}
> +
> +	bell = devres_alloc(qcom_apcs_ipc_release, sizeof(*bell),
> GFP_KERNEL);
> +	if (!bell) {
> +		ret = -ENOMEM;
> +		goto release_device;
> +	}
> +
> +	bell->apcs = platform_get_drvdata(pdev);
> +	bell->bit = args.args[0];
> +
> +	devres_add(dev, bell);
> +
> +	return bell;
> +
> +release_device:
> +	put_device(&pdev->dev);
> +
> +	return ERR_PTR(ret);
> +
> +}
> +EXPORT_SYMBOL_GPL(devm_qcom_apcs_ipc_get);
> +
> +/**
> + * qcom_apcs_ipc_ring() - ring the doorbell
> + * @bell:	doorbell to ring
> + */
> +void qcom_apcs_ipc_ring(struct qcom_apcs_ipc_bell *bell) {
> +	struct qcom_apcs_ipc *apcs = bell->apcs;
> +
> +	writel(BIT(bell->bit), apcs->base + apcs->offset); }
> +EXPORT_SYMBOL_GPL(qcom_apcs_ipc_ring);
> +
> +static int qcom_apcs_ipc_probe(struct platform_device *pdev) {
> +	struct qcom_apcs_ipc *apcs;
> +	struct resource *res;
> +
> +	apcs = devm_kzalloc(&pdev->dev, sizeof(*apcs), GFP_KERNEL);
> +	if (!apcs)
> +		return -ENOMEM;
> +
> +	apcs->dev = &pdev->dev;
> +	apcs->offset = (unsigned long)of_device_get_match_data(&pdev-
> >dev);
> +
> +	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> +	apcs->base = devm_ioremap_resource(&pdev->dev, res);
> +	if (IS_ERR(apcs->base))
> +		return PTR_ERR(apcs->base);
> +
> +	platform_set_drvdata(pdev, apcs);
> +
> +	return 0;
> +}
> +
> +static int qcom_apcs_ipc_remove(struct platform_device *pdev) {
> +	return 0;
> +}
> +
> +/* .data is the offset of the ipc register within the global block */
> +static const struct of_device_id qcom_apcs_ipc_of_match[] = {
> +	{ .compatible = "qcom,msm8916-apcs-kpss-global", .data = (void *)8
> },
> +	{ .compatible = "qcom,msm8996-apcs-hmss-global", .data = (void
> *)16 },
> +	{}
> +};
> +MODULE_DEVICE_TABLE(of, qcom_apcs_ipc_of_match);
> +
> +static struct platform_driver qcom_apcs_ipc_driver = {
> +	.probe = qcom_apcs_ipc_probe,
> +	.remove = qcom_apcs_ipc_remove,
> +	.driver = {
> +		.name = "qcom_apcs_ipc",
> +		.of_match_table = qcom_apcs_ipc_of_match,
> +	},
> +};
> +
> +static int __init qcom_apcs_ipc_init(void) {
> +	return platform_driver_register(&qcom_apcs_ipc_driver);
> +}
> +postcore_initcall(qcom_apcs_ipc_init);
> +
> +static void __exit qcom_apcs_ipc_exit(void) {
> +	platform_driver_unregister(&qcom_apcs_ipc_driver);
> +}
> +module_exit(qcom_apcs_ipc_exit);
> +
> +MODULE_LICENSE("GPL v2");
> +MODULE_DESCRIPTION("Qualcomm APCS IPC driver");
> diff --git a/include/linux/soc/qcom/apcs_ipc.h
> b/include/linux/soc/qcom/apcs_ipc.h
> new file mode 100644
> index 000000000000..72be77555261
> --- /dev/null
> +++ b/include/linux/soc/qcom/apcs_ipc.h
> @@ -0,0 +1,26 @@
> +#ifndef __QCOM_APCS_IPC_H__
> +#define __QCOM_APCS_IPC_H__
> +
> +#include <linux/err.h>
> +
> +struct device;
> +struct qcom_apcs_ipc_bell;
> +
> +#if IS_ENABLED(CONFIG_QCOM_APCS_IPC)
> +
> +struct qcom_apcs_ipc_bell *devm_qcom_apcs_ipc_get(struct device *dev,
> +						  const char *id);
> +void qcom_apcs_ipc_ring(struct qcom_apcs_ipc_bell *bell);
> +
> +#else
> +
> +static inline struct qcom_apcs_ipc_bell *devm_qcom_apcs_ipc_get(struct
> device *dev,
> +								const char
> *id)
> +{
> +	return ERR_PTR(-EINVAL);
> +}
> +
> +static inline void qcom_apcs_ipc_ring(struct qcom_apcs_ipc_bell *bell)
> +{}
> +
> +#endif
> +#endif
> --
> 2.12.0
> 
> --
> To unsubscribe from this list: send the line "unsubscribe linux-remoteproc" in
> the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org More majordomo
> info at  http://vger.kernel.org/majordomo-info.html
--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply

* Re: [PATCHv2 2/2] iio: adc: add driver for the ti-adc084s021 chip
From: Mårten Lindahl @ 2017-05-03  9:13 UTC (permalink / raw)
  To: Jonathan Cameron
  Cc: Mårten Lindahl, knaack.h-Mmb7MZpHnFY,
	lars-Qo5EllUWu/uELgA04lAiVw, pmeerw-jW+XmwGofnusTnJN9+BGXg,
	linux-iio-u79uwXL29TY76Z2rM5mHXA, robh+dt-DgEjT+Ai2ygdnm+yROfE0A,
	mark.rutland-5wv7dgnIgG8, devicetree-u79uwXL29TY76Z2rM5mHXA
In-Reply-To: <88d6c17d-8eae-6a41-b304-5224a7a2194a-DgEjT+Ai2ygdnm+yROfE0A@public.gmane.org>

On Sun, 2017-04-30 at 16:51 +0100, Jonathan Cameron wrote:
> On 30/04/17 13:53, Mårten Lindahl wrote:
> > From: Mårten Lindahl <martenli-VrBV9hrLPhE@public.gmane.org>
> > 
> > This adds support for the Texas Instruments ADC084S021 ADC chip.
> > 
> > Signed-off-by: Mårten Lindahl <martenli-VrBV9hrLPhE@public.gmane.org>
> A few more bits inline.  Mostly stuff that has come up
> in the V2 changes (and the inevitable bits I missed the
> first time!)
> 
> Jonathan

Hi Jonathan! Please see my comments. I will send v3 today.
Thanks,
Mårten

> > ---
> > Changes in v2:
> > - Removed most #defines in favor of inlines
> > - Corrected channel macro
> > - Removed configuration array with only one item
> > - Updated func adc084s021_adc_conversion to use be16_to_cpu
> > - Added IIO_CHAN_INFO_SCALE to func adc084s021_read_raw
> > - Use iio_device_claim_direct_mode in func adc084s021_read_raw
> > - Removed documentation for standard driver functions
> > - Changed retval to ret everywhere
> > - Removed dynamic alloc for data buffer in trigger handler
> > - Keeping mutex for all iterations in trigger handler
> > - Removed usage of events in this driver
> > - Removed info log in probe
> > - Use spi_message_init_with_transfers for spi message structs
> > - Use preenable and postdisable functions for regulator
> > - Inserted blank line before last return in all functions
> > 
> >  drivers/iio/adc/Kconfig         |  12 ++
> >  drivers/iio/adc/Makefile        |   1 +
> >  drivers/iio/adc/ti-adc084s021.c | 294 ++++++++++++++++++++++++++++++++++++++++
> >  3 files changed, 307 insertions(+)
> >  create mode 100644 drivers/iio/adc/ti-adc084s021.c
> > 
> > diff --git a/drivers/iio/adc/Kconfig b/drivers/iio/adc/Kconfig
> > index dedae7a..13141e5 100644
> > --- a/drivers/iio/adc/Kconfig
> > +++ b/drivers/iio/adc/Kconfig
> > @@ -560,6 +560,18 @@ config TI_ADC0832
> >  	  This driver can also be built as a module. If so, the module will be
> >  	  called ti-adc0832.
> >  
> > +config TI_ADC084S021
> > +	tristate "Texas Instruments ADC084S021"
> > +	depends on SPI
> > +	select IIO_BUFFER
> > +	select IIO_TRIGGERED_BUFFER
> > +	help
> > +	  If you say yes here you get support for Texas Instruments ADC084S021
> > +	  chips.
> > +
> > +	  This driver can also be built as a module. If so, the module will be
> > +	  called ti-adc084s021.
> > +
> >  config TI_ADC12138
> >  	tristate "Texas Instruments ADC12130/ADC12132/ADC12138"
> >  	depends on SPI
> > diff --git a/drivers/iio/adc/Makefile b/drivers/iio/adc/Makefile
> > index d001262..b1a6158 100644
> > --- a/drivers/iio/adc/Makefile
> > +++ b/drivers/iio/adc/Makefile
> > @@ -51,6 +51,7 @@ obj-$(CONFIG_STM32_ADC_CORE) += stm32-adc-core.o
> >  obj-$(CONFIG_STM32_ADC) += stm32-adc.o
> >  obj-$(CONFIG_TI_ADC081C) += ti-adc081c.o
> >  obj-$(CONFIG_TI_ADC0832) += ti-adc0832.o
> > +obj-$(CONFIG_TI_ADC084S021) += ti-adc084s021.o
> >  obj-$(CONFIG_TI_ADC12138) += ti-adc12138.o
> >  obj-$(CONFIG_TI_ADC128S052) += ti-adc128s052.o
> >  obj-$(CONFIG_TI_ADC161S626) += ti-adc161s626.o
> > diff --git a/drivers/iio/adc/ti-adc084s021.c b/drivers/iio/adc/ti-adc084s021.c
> > new file mode 100644
> > index 0000000..2dce257
> > --- /dev/null
> > +++ b/drivers/iio/adc/ti-adc084s021.c
> > @@ -0,0 +1,294 @@
> > +/**
> > + * Copyright (C) 2017 Axis Communications AB
> > + *
> > + * Driver for Texas Instruments' ADC084S021 ADC chip.
> > + * Datasheets can be found here:
> > + * http://www.ti.com/lit/ds/symlink/adc084s021.pdf
> > + *
> > + * 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.
> > + */
> > +
> > +#include <linux/err.h>
> > +#include <linux/spi/spi.h>
> > +#include <linux/module.h>
> > +#include <linux/interrupt.h>
> > +#include <linux/iio/iio.h>
> > +#include <linux/iio/buffer.h>
> > +#include <linux/iio/triggered_buffer.h>
> > +#include <linux/iio/trigger_consumer.h>
> > +#include <linux/regulator/consumer.h>
> > +
> > +#define ADC084S021_DRIVER_NAME "adc084s021"
> > +
> > +struct adc084s021 {
> > +	struct spi_device *spi;
> > +	struct spi_message message;
> > +	struct spi_transfer spi_trans[2];
> > +	struct regulator *reg;
> > +	struct mutex lock;
> > +	/*
> > +	 * DMA (thus cache coherency maintenance) requires the
> > +	 * transfer buffers to live in their own cache lines.
> > +	 */
> > +	union {
> > +		u16 tx_buf;
> > +		__be16 rx_buf;
> > +	} ____cacheline_aligned;
> > +};
> > +
> > +/**
> > + * Channel specification
> > + */
> Comment doesn't add much so I'd drop it.
Fixed in v3.
> > +#define ADC084S021_VOLTAGE_CHANNEL(num)                  \
> > +	{                                                      \
> > +		.type = IIO_VOLTAGE,                                 \
> > +		.channel = (num),                                    \
> > +		.address = (num) << 3,                               \
> This does feel a little pointless as you can just do the shift inline and
> use the channel value instead.  Doesn't really matter though.
Ok, fixed in v3.
> > +		.indexed = 1,                                        \
> > +		.scan_index = (num),                                 \
> > +		.scan_type = {                                       \
> > +			.sign = 'u',                                       \
> > +			.realbits = 8,                                     \
> > +			.storagebits = 16,                                 \
> > +			.shift = 4,                                        \
> > +			.endianness = IIO_BE,                              \
> > +		},                                                   \
> > +		.info_mask_separate = BIT(IIO_CHAN_INFO_RAW),        \
> > +		.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE),\
> > +	}
> > +
> > +static const struct iio_chan_spec adc084s021_channels[] = {
> > +	ADC084S021_VOLTAGE_CHANNEL(0),
> > +	ADC084S021_VOLTAGE_CHANNEL(1),
> > +	ADC084S021_VOLTAGE_CHANNEL(2),
> > +	ADC084S021_VOLTAGE_CHANNEL(3),
> > +	IIO_CHAN_SOFT_TIMESTAMP(4),
> > +};
> > +
> > +/**
> > + * Read an ADC channel and return its value.
> > + *
> > + * @adc: The ADC SPI data.
> > + * @channel: The IIO channel data structure.
> > + */
> > +static int adc084s021_adc_conversion(struct adc084s021 *adc,
> > +			   struct iio_chan_spec const *channel)
> > +{
> > +	u8 value;
> > +	int ret;
> > +
> > +	adc->tx_buf = channel->address;
> > +
> > +	/* Do the transfer */
> > +	ret = spi_sync(adc->spi, &adc->message);
> > +	if (ret < 0)
> > +		return ret;
> > +
> > +	value = (be16_to_cpu(adc->rx_buf) >> channel->scan_type.shift) & 0xff;You want to do this for the read_raw sysfs calls, but not the buffered ones
> (where this shift and mask is made userspace's problem).
Fixed in v3.
> > +
> > +	dev_dbg(&adc->spi->dev, "value 0x%02X on channel %d\n",
> > +			     value, channel->channel);
> > +
> > +	return value;
> > +}
> > +
> > +static int adc084s021_read_raw(struct iio_dev *indio_dev,
> > +			   struct iio_chan_spec const *channel, int *val,
> > +			   int *val2, long mask)
> > +{
> > +	struct adc084s021 *adc = iio_priv(indio_dev);
> > +	int ret;
> > +
> > +	switch (mask) {
> > +	case IIO_CHAN_INFO_RAW:
> > +		ret = iio_device_claim_direct_mode(indio_dev);
> > +		if (ret < 0)
> > +			return ret;
> > +
> Unless I'm dozing off this morning, you don't have any power..
> So you'll need to enable the regulator first in this path.
Fixed in v3.
> 
> Note that the runtime power management autosuspend stuff can
> be good for this as it stops the power cycling if a short burst
> of readings is taken.
I don't have much experience of the pm_runtime system, so I looked
around to the other drivers but I see very few users of it. I can
absolutely look into it if you think it should be applied to this
driver.
> > +		ret = adc084s021_adc_conversion(adc, channel);
> > +		iio_device_release_direct_mode(indio_dev);
> > +		if (ret < 0)
> > +			return ret;
> > +
> > +		*val = ret;
> > +
> > +		return IIO_VAL_INT;
> > +	case IIO_CHAN_INFO_SCALE:
> > +		ret = regulator_get_voltage(adc->reg);
> Given the regulator is currently off as far as this device is concerned
> it's possible the answer will be 0...
Fixed in v3.
> > +		if (ret < 0)
> > +			return ret;
> > +
> > +		*val = ret / 1000;
> > +
> > +		return IIO_VAL_INT;
> > +	default:
> > +		return -EINVAL;
> > +	}
> > +}
> > +
> > +/**
> > + * Read enabled ADC channels and push data to the buffer.
> > + *
> > + * @irq: The interrupt number (not used).
> > + * @pollfunc: Pointer to the poll func.
> > + */
> > +static irqreturn_t adc084s021_buffer_trigger_handler(int irq, void *pollfunc)
> > +{
> > +	struct iio_poll_func *pf = pollfunc;
> > +	struct iio_dev *indio_dev = pf->indio_dev;
> > +	struct adc084s021 *adc = iio_priv(indio_dev);
> > +	__be16 data[8] = {0}; /* 4 * 16-bit words of data + 8 bytes timestamp */
> > +	int scan_index;
> > +	int i = 0;
> > +
> > +	mutex_lock(&adc->lock);
> > +
> > +	for_each_set_bit(scan_index, indio_dev->active_scan_mask,
> > +			 indio_dev->masklength) {
> > +		const struct iio_chan_spec *channel =
> > +			&indio_dev->channels[scan_index];
> > +		data[i++] = adc084s021_adc_conversion(adc, channel);
> This is clearly a rather simplistic way of handling the read outs.
> Perfectly good for an initial version (which may never get improved
> on!) but you could do the spi transfers for a set of channels much
> more efficiently.
> 
> Figure 1 on the datasheet shows how the control register for the next
> read can be written in parallel with the previous read. That would
> mean each scan took N + 1 2 byte transfers rather than 2N as currently.
> 
> I'm not particularly suggesting you do this, but thought I'd just
> comment on the possibility as it is a common situation with spi
> ADCs (sometimes you get a greater lag between setup and read out
> making this even fiddlier!)
> 
> If you do want to do this, the trick is to do your transfer setup
> and creation stuff in preenable so that it can be customised for
> whatever channels are enabled.
Ok, Fixed in v3.
I have looked into this and I think I improved the design according to
your suggestion. Please note: According to the datasheet (section 8.3)
after each power up the ADC will read out last scanned/first channel,
which will result in a very first read of 16 bits that is ignored. Also,
the readings are setup by only one transfer segment now.
> > +	}
> > +
> > +	iio_push_to_buffers_with_timestamp(indio_dev, data,
> > +					   iio_get_time_ns(indio_dev));
> > +	mutex_unlock(&adc->lock);
> > +	iio_trigger_notify_done(indio_dev->trig);
> > +
> > +	return IRQ_HANDLED;
> > +}
> > +
> > +static int adc084s021_buffer_preenable(struct iio_dev *indio_dev)
> > +{
> > +	struct adc084s021 *adc = iio_priv(indio_dev);
> > +
> > +	return regulator_enable(adc->reg);
> > +}
> > +
> > +static int adc084s021_buffer_postdisable(struct iio_dev *indio_dev)
> > +{
> > +	struct adc084s021 *adc = iio_priv(indio_dev);
> > +
> > +	return regulator_disable(adc->reg);
> > +}
> > +
> > +static const struct iio_info adc084s021_info = {
> > +	.read_raw = adc084s021_read_raw,
> > +	.driver_module = THIS_MODULE,
> > +};
> > +
> > +static const struct iio_buffer_setup_ops adc084s021_buffer_setup_ops = {
> > +	.preenable = adc084s021_buffer_preenable,
> > +	.postenable = iio_triggered_buffer_postenable,
> > +	.predisable = iio_triggered_buffer_predisable,
> > +	.postdisable = adc084s021_buffer_postdisable,
> > +};
> > +
> > +static int adc084s021_probe(struct spi_device *spi)
> > +{
> > +	struct iio_dev *indio_dev;
> > +	struct adc084s021 *adc;
> > +	int ret;
> > +
> > +	indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*adc));
> > +	if (!indio_dev) {
> > +		dev_err(&spi->dev, "Failed to allocate IIO device\n");
> > +		return -ENOMEM;
> > +	}
> > +
> > +	adc = iio_priv(indio_dev);
> > +	adc->spi = spi;
> > +	spi->bits_per_word = 8;
> > +
> > +	/* Update the SPI device with config and connect the iio dev */
> > +	ret = spi_setup(spi);
> > +	if (ret) {
> > +		dev_err(&spi->dev, "Failed to update SPI device\n");
> > +		return ret;
> > +	}
> > +	spi_set_drvdata(spi, indio_dev);
> > +
> > +	/* Initiate the Industrial I/O device */
> > +	indio_dev->dev.parent = &spi->dev;
> > +	indio_dev->dev.of_node = spi->dev.of_node;
> > +	indio_dev->name = spi_get_device_id(spi)->name;
> > +	indio_dev->modes = INDIO_DIRECT_MODE;
> > +	indio_dev->info = &adc084s021_info;
> > +	indio_dev->channels = adc084s021_channels;
> > +	indio_dev->num_channels = ARRAY_SIZE(adc084s021_channels);
> > +
> > +	/* Create SPI transfer for channel reads */
> > +	adc->spi_trans[0].tx_buf = &adc->tx_buf;
> > +	adc->spi_trans[0].len = 2;
> > +	adc->spi_trans[0].speed_hz = spi->max_speed_hz;
> You don't need to set these for individual transfers unless they
> are different from how the spi bus has been set up...
> 
> It's handled in __spi_validate in drivers/spi/spi.c
> 
> 	list_for_each_entry(xfer, &message->transfers, transfer_list) {
> 		message->frame_length += xfer->len;
> 		if (!xfer->bits_per_word)
> 			xfer->bits_per_word = spi->bits_per_word;
> 
> 		if (!xfer->speed_hz)
> 			xfer->speed_hz = spi->max_speed_hz;
> ...
> 
> So it will set them spi->... in the core if you haven't overridden.
Fixed in v3.
> 
> > +	adc->spi_trans[0].bits_per_word = spi->bits_per_word;
> > +	adc->spi_trans[1].rx_buf = &adc->rx_buf;
> > +	adc->spi_trans[1].len = 2;
> > +	adc->spi_trans[1].speed_hz = spi->max_speed_hz;
> > +	adc->spi_trans[1].bits_per_word = spi->bits_per_word;
> > +
> > +	spi_message_init_with_transfers(&adc->message, adc->spi_trans, 2);
> > +
> > +	adc->reg = devm_regulator_get(&spi->dev, "vref");
> > +	if (IS_ERR(adc->reg))
> > +		return PTR_ERR(adc->reg);
> > +
> > +	mutex_init(&adc->lock);
> > +
> > +	/* Setup triggered buffer with pollfunction */
> > +	ret = iio_triggered_buffer_setup(indio_dev, NULL,
> > +					    adc084s021_buffer_trigger_handler,
> > +					    &adc084s021_buffer_setup_ops);
> > +	if (ret) {
> > +		dev_err(&spi->dev, "Failed to setup triggered buffer\n");
> > +		return ret;
> > +	}
> > +
> > +	ret = iio_device_register(indio_dev);
> > +	if (ret) {
> > +		dev_err(&spi->dev, "Failed to register IIO device\n");
> > +		iio_triggered_buffer_cleanup(indio_dev);
> With devm usage this won't be needed any more.
Fixed in v3.
> 
> Hmm. We should improve the error message from the core as it'll allow us
> to remove a lot of boiler plate in drivers. Ah well, a project for
> another day.
I removed this message in v3.
> > +	}
> > +
> > +	return ret;
> > +}
> > +
> > +static int adc084s021_remove(struct spi_device *spi)
> > +{
> > +	struct iio_dev *indio_dev = spi_get_drvdata(spi);
> > +
> > +	iio_device_unregister(indio_dev);
> > +	iio_triggered_buffer_cleanup(indio_dev);
> Now you have moved to dynamically enabling the regulator, it makes
> sense to use the devm_ versions of iio_device_register and 
> iio_triggered_buffer setup.
> 
> With those, you'll be able to get rid of the remove callback entirely as
> there will be nothing to do.
Fixed in v3.
> > +
> > +	return 0;
> > +}
> > +
> > +static const struct of_device_id adc084s021_of_match[] = {
> > +	{ .compatible = "ti,adc084s021", },
> > +	{},
> > +};
> > +MODULE_DEVICE_TABLE(of, adc084s021_of_match);
> > +
> > +static const struct spi_device_id adc084s021_id[] = {
> > +	{ ADC084S021_DRIVER_NAME, 0},
> > +	{}
> > +};
> > +MODULE_DEVICE_TABLE(spi, adc084s021_id);
> > +
> > +static struct spi_driver adc084s021_driver = {
> > +	.driver = {
> > +		.name = ADC084S021_DRIVER_NAME,
> > +		.of_match_table = of_match_ptr(adc084s021_of_match),
> > +	},
> > +	.probe = adc084s021_probe,
> > +	.remove = adc084s021_remove,
> > +	.id_table = adc084s021_id,
> > +};
> > +module_spi_driver(adc084s021_driver);
> > +
> > +MODULE_AUTHOR("Mårten Lindahl <martenli-VrBV9hrLPhE@public.gmane.org>");
> > +MODULE_DESCRIPTION("Texas Instruments ADC084S021");
> > +MODULE_LICENSE("GPL v2");
> > +MODULE_VERSION("1.0");
> > 
> 

^ permalink raw reply

* Re: [PATCH v5 03/14] pinctrl: add a pinctrl driver for the Ingenic jz47xx SoCs
From: Paul Cercueil @ 2017-05-03  9:12 UTC (permalink / raw)
  To: Linus Walleij, Alexandre Courbot, Rob Herring, Mark Rutland,
	Ralf Baechle
  Cc: Boris Brezillon, Thierry Reding, Bartlomiej Zolnierkiewicz,
	Maarten ter Huurne, Lars-Peter Clausen, Paul Burton, james.hogan,
	linux-gpio, devicetree, linux-kernel, linux-mips, linux-mmc,
	linux-mtd, linux-pwm, linux-fbdev
In-Reply-To: <20170428200824.10906-4-paul@crapouillou.net>

The dependency on MFD is gone but now I notice I forgot to remove the 
'select MFD_CORE'
in the Kconfig. It'd be great if you can make a quick edit when merging 
this,
otherwise I'll send a v6.

- Paul

Le 2017-04-28 22:08, Paul Cercueil a écrit :
> This driver handles pin configuration and pin muxing for the
> JZ4740 and JZ4780 SoCs from Ingenic.
> 
> Signed-off-by: Paul Cercueil <paul@crapouillou.net>
> ---
>  drivers/pinctrl/Kconfig           |  10 +
>  drivers/pinctrl/Makefile          |   1 +
>  drivers/pinctrl/pinctrl-ingenic.c | 852 
> ++++++++++++++++++++++++++++++++++++++
>  3 files changed, 863 insertions(+)
>  create mode 100644 drivers/pinctrl/pinctrl-ingenic.c
> 
>  v2: Consider it's a new patch. Completely rewritten from v1.
>  v3: 'unsigned' -> 'unsigned int'
>  v4: Completely rewritten from v3.
>  v5: Probe child devices directly instead of using MFD framework
> 
> diff --git a/drivers/pinctrl/Kconfig b/drivers/pinctrl/Kconfig
> index 8f8c2af45781..82ce72fcb8e0 100644
> --- a/drivers/pinctrl/Kconfig
> +++ b/drivers/pinctrl/Kconfig
> @@ -285,6 +285,16 @@ config PINCTRL_ZYNQ
>  	help
>  	  This selects the pinctrl driver for Xilinx Zynq.
> 
> +config PINCTRL_INGENIC
> +	bool "Pinctrl driver for the Ingenic JZ47xx SoCs"
> +	default y
> +	depends on MACH_INGENIC || COMPILE_TEST
> +	select GENERIC_PINCONF
> +	select GENERIC_PINCTRL_GROUPS
> +	select GENERIC_PINMUX_FUNCTIONS
> +	select REGMAP_MMIO
> +	select MFD_CORE
> +
>  source "drivers/pinctrl/aspeed/Kconfig"
>  source "drivers/pinctrl/bcm/Kconfig"
>  source "drivers/pinctrl/berlin/Kconfig"
> diff --git a/drivers/pinctrl/Makefile b/drivers/pinctrl/Makefile
> index a251f439626f..80f327239d4b 100644
> --- a/drivers/pinctrl/Makefile
> +++ b/drivers/pinctrl/Makefile
> @@ -38,6 +38,7 @@ obj-$(CONFIG_PINCTRL_LPC18XX)	+= pinctrl-lpc18xx.o
>  obj-$(CONFIG_PINCTRL_TB10X)	+= pinctrl-tb10x.o
>  obj-$(CONFIG_PINCTRL_ST) 	+= pinctrl-st.o
>  obj-$(CONFIG_PINCTRL_ZYNQ)	+= pinctrl-zynq.o
> +obj-$(CONFIG_PINCTRL_INGENIC)	+= pinctrl-ingenic.o
> 
>  obj-$(CONFIG_ARCH_ASPEED)	+= aspeed/
>  obj-y				+= bcm/
> diff --git a/drivers/pinctrl/pinctrl-ingenic.c
> b/drivers/pinctrl/pinctrl-ingenic.c
> new file mode 100644
> index 000000000000..d8473d929cb1
> --- /dev/null
> +++ b/drivers/pinctrl/pinctrl-ingenic.c
> @@ -0,0 +1,852 @@
> +/*
> + * Ingenic SoCs pinctrl driver
> + *
> + * Copyright (c) 2017 Paul Cercueil <paul@crapouillou.net>
> + *
> + * License terms: GNU General Public License (GPL) version 2
> + */
> +
> +#include <linux/compiler.h>
> +#include <linux/gpio.h>
> +#include <linux/interrupt.h>
> +#include <linux/io.h>
> +#include <linux/of_device.h>
> +#include <linux/of_platform.h>
> +#include <linux/pinctrl/pinctrl.h>
> +#include <linux/pinctrl/pinmux.h>
> +#include <linux/pinctrl/pinconf.h>
> +#include <linux/pinctrl/pinconf-generic.h>
> +#include <linux/platform_device.h>
> +#include <linux/regmap.h>
> +#include <linux/slab.h>
> +
> +#include "core.h"
> +#include "pinconf.h"
> +#include "pinmux.h"
> +
> +#define JZ4740_GPIO_DATA	0x10
> +#define JZ4740_GPIO_PULL_DIS	0x30
> +#define JZ4740_GPIO_FUNC	0x40
> +#define JZ4740_GPIO_SELECT	0x50
> +#define JZ4740_GPIO_DIR		0x60
> +#define JZ4740_GPIO_TRIG	0x70
> +#define JZ4740_GPIO_FLAG	0x80
> +
> +#define JZ4770_GPIO_INT		0x10
> +#define JZ4770_GPIO_MSK		0x20
> +#define JZ4770_GPIO_PAT1	0x30
> +#define JZ4770_GPIO_PAT0	0x40
> +#define JZ4770_GPIO_FLAG	0x50
> +#define JZ4770_GPIO_PEN		0x70
> +
> +#define REG_SET(x) ((x) + 0x4)
> +#define REG_CLEAR(x) ((x) + 0x8)
> +
> +#define PINS_PER_GPIO_CHIP 32
> +
> +enum jz_version {
> +	ID_JZ4740,
> +	ID_JZ4770,
> +	ID_JZ4780,
> +};
> +
> +struct ingenic_chip_info {
> +	unsigned int num_chips;
> +
> +	const struct group_desc *groups;
> +	unsigned int num_groups;
> +
> +	const struct function_desc *functions;
> +	unsigned int num_functions;
> +
> +	const u32 *pull_ups, *pull_downs;
> +};
> +
> +struct ingenic_pinctrl {
> +	struct device *dev;
> +	struct regmap *map;
> +	struct pinctrl_dev *pctl;
> +	struct pinctrl_pin_desc *pdesc;
> +	enum jz_version version;
> +
> +	const struct ingenic_chip_info *info;
> +};
> +
> +static const u32 jz4740_pull_ups[4] = {
> +	0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
> +};
> +
> +static const u32 jz4740_pull_downs[4] = {
> +	0x00000000, 0x00000000, 0x00000000, 0x00000000,
> +};
> +
> +static int jz4740_mmc_1bit_pins[] = { 0x69, 0x68, 0x6a, };
> +static int jz4740_mmc_4bit_pins[] = { 0x6b, 0x6c, 0x6d, };
> +static int jz4740_uart0_data_pins[] = { 0x7a, 0x79, };
> +static int jz4740_uart0_hwflow_pins[] = { 0x7e, 0x7f, };
> +static int jz4740_uart1_data_pins[] = { 0x7e, 0x7f, };
> +static int jz4740_lcd_8bit_pins[] = {
> +	0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x52, 0x53, 0x54,
> +};
> +static int jz4740_lcd_16bit_pins[] = {
> +	0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f, 0x55,
> +};
> +static int jz4740_lcd_18bit_pins[] = { 0x50, 0x51, };
> +static int jz4740_lcd_18bit_tft_pins[] = { 0x56, 0x57, 0x31, 0x32, };
> +static int jz4740_nand_cs1_pins[] = { 0x39, };
> +static int jz4740_nand_cs2_pins[] = { 0x3a, };
> +static int jz4740_nand_cs3_pins[] = { 0x3b, };
> +static int jz4740_nand_cs4_pins[] = { 0x3c, };
> +static int jz4740_pwm_pwm0_pins[] = { 0x77, };
> +static int jz4740_pwm_pwm1_pins[] = { 0x78, };
> +static int jz4740_pwm_pwm2_pins[] = { 0x79, };
> +static int jz4740_pwm_pwm3_pins[] = { 0x7a, };
> +static int jz4740_pwm_pwm4_pins[] = { 0x7b, };
> +static int jz4740_pwm_pwm5_pins[] = { 0x7c, };
> +static int jz4740_pwm_pwm6_pins[] = { 0x7e, };
> +static int jz4740_pwm_pwm7_pins[] = { 0x7f, };
> +
> +static int jz4740_mmc_1bit_funcs[] = { 0, 0, 0, };
> +static int jz4740_mmc_4bit_funcs[] = { 0, 0, 0, };
> +static int jz4740_uart0_data_funcs[] = { 1, 1, };
> +static int jz4740_uart0_hwflow_funcs[] = { 1, 1, };
> +static int jz4740_uart1_data_funcs[] = { 2, 2, };
> +static int jz4740_lcd_8bit_funcs[] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
> 0, };
> +static int jz4740_lcd_16bit_funcs[] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, };
> +static int jz4740_lcd_18bit_funcs[] = { 0, 0, };
> +static int jz4740_lcd_18bit_tft_funcs[] = { 0, 0, 0, 0, };
> +static int jz4740_nand_cs1_funcs[] = { 0, };
> +static int jz4740_nand_cs2_funcs[] = { 0, };
> +static int jz4740_nand_cs3_funcs[] = { 0, };
> +static int jz4740_nand_cs4_funcs[] = { 0, };
> +static int jz4740_pwm_pwm0_funcs[] = { 0, };
> +static int jz4740_pwm_pwm1_funcs[] = { 0, };
> +static int jz4740_pwm_pwm2_funcs[] = { 0, };
> +static int jz4740_pwm_pwm3_funcs[] = { 0, };
> +static int jz4740_pwm_pwm4_funcs[] = { 0, };
> +static int jz4740_pwm_pwm5_funcs[] = { 0, };
> +static int jz4740_pwm_pwm6_funcs[] = { 0, };
> +static int jz4740_pwm_pwm7_funcs[] = { 0, };
> +
> +#define INGENIC_PIN_GROUP(name, id)			\
> +	{						\
> +		name,					\
> +		id##_pins,				\
> +		ARRAY_SIZE(id##_pins),			\
> +		id##_funcs,				\
> +	}
> +
> +static const struct group_desc jz4740_groups[] = {
> +	INGENIC_PIN_GROUP("mmc-1bit", jz4740_mmc_1bit),
> +	INGENIC_PIN_GROUP("mmc-4bit", jz4740_mmc_4bit),
> +	INGENIC_PIN_GROUP("uart0-data", jz4740_uart0_data),
> +	INGENIC_PIN_GROUP("uart0-hwflow", jz4740_uart0_hwflow),
> +	INGENIC_PIN_GROUP("uart1-data", jz4740_uart1_data),
> +	INGENIC_PIN_GROUP("lcd-8bit", jz4740_lcd_8bit),
> +	INGENIC_PIN_GROUP("lcd-16bit", jz4740_lcd_16bit),
> +	INGENIC_PIN_GROUP("lcd-18bit", jz4740_lcd_18bit),
> +	INGENIC_PIN_GROUP("lcd-18bit-tft", jz4740_lcd_18bit_tft),
> +	{ "lcd-no-pins", },
> +	INGENIC_PIN_GROUP("nand-cs1", jz4740_nand_cs1),
> +	INGENIC_PIN_GROUP("nand-cs2", jz4740_nand_cs2),
> +	INGENIC_PIN_GROUP("nand-cs3", jz4740_nand_cs3),
> +	INGENIC_PIN_GROUP("nand-cs4", jz4740_nand_cs4),
> +	INGENIC_PIN_GROUP("pwm0", jz4740_pwm_pwm0),
> +	INGENIC_PIN_GROUP("pwm1", jz4740_pwm_pwm1),
> +	INGENIC_PIN_GROUP("pwm2", jz4740_pwm_pwm2),
> +	INGENIC_PIN_GROUP("pwm3", jz4740_pwm_pwm3),
> +	INGENIC_PIN_GROUP("pwm4", jz4740_pwm_pwm4),
> +	INGENIC_PIN_GROUP("pwm5", jz4740_pwm_pwm5),
> +	INGENIC_PIN_GROUP("pwm6", jz4740_pwm_pwm6),
> +	INGENIC_PIN_GROUP("pwm7", jz4740_pwm_pwm7),
> +};
> +
> +static const char *jz4740_mmc_groups[] = { "mmc-1bit", "mmc-4bit", };
> +static const char *jz4740_uart0_groups[] = { "uart0-data", 
> "uart0-hwflow", };
> +static const char *jz4740_uart1_groups[] = { "uart1-data", };
> +static const char *jz4740_lcd_groups[] = {
> +	"lcd-8bit", "lcd-16bit", "lcd-18bit", "lcd-18bit-tft", "lcd-no-pins",
> +};
> +static const char *jz4740_nand_groups[] = {
> +	"nand-cs1", "nand-cs2", "nand-cs3", "nand-cs4",
> +};
> +static const char *jz4740_pwm0_groups[] = { "pwm0", };
> +static const char *jz4740_pwm1_groups[] = { "pwm1", };
> +static const char *jz4740_pwm2_groups[] = { "pwm2", };
> +static const char *jz4740_pwm3_groups[] = { "pwm3", };
> +static const char *jz4740_pwm4_groups[] = { "pwm4", };
> +static const char *jz4740_pwm5_groups[] = { "pwm5", };
> +static const char *jz4740_pwm6_groups[] = { "pwm6", };
> +static const char *jz4740_pwm7_groups[] = { "pwm7", };
> +
> +static const struct function_desc jz4740_functions[] = {
> +	{ "mmc", jz4740_mmc_groups, ARRAY_SIZE(jz4740_mmc_groups), },
> +	{ "uart0", jz4740_uart0_groups, ARRAY_SIZE(jz4740_uart0_groups), },
> +	{ "uart1", jz4740_uart1_groups, ARRAY_SIZE(jz4740_uart1_groups), },
> +	{ "lcd", jz4740_lcd_groups, ARRAY_SIZE(jz4740_lcd_groups), },
> +	{ "nand", jz4740_nand_groups, ARRAY_SIZE(jz4740_nand_groups), },
> +	{ "pwm0", jz4740_pwm0_groups, ARRAY_SIZE(jz4740_pwm0_groups), },
> +	{ "pwm1", jz4740_pwm1_groups, ARRAY_SIZE(jz4740_pwm1_groups), },
> +	{ "pwm2", jz4740_pwm2_groups, ARRAY_SIZE(jz4740_pwm2_groups), },
> +	{ "pwm3", jz4740_pwm3_groups, ARRAY_SIZE(jz4740_pwm3_groups), },
> +	{ "pwm4", jz4740_pwm4_groups, ARRAY_SIZE(jz4740_pwm4_groups), },
> +	{ "pwm5", jz4740_pwm5_groups, ARRAY_SIZE(jz4740_pwm5_groups), },
> +	{ "pwm6", jz4740_pwm6_groups, ARRAY_SIZE(jz4740_pwm6_groups), },
> +	{ "pwm7", jz4740_pwm7_groups, ARRAY_SIZE(jz4740_pwm7_groups), },
> +};
> +
> +static const struct ingenic_chip_info jz4740_chip_info = {
> +	.num_chips = 4,
> +	.groups = jz4740_groups,
> +	.num_groups = ARRAY_SIZE(jz4740_groups),
> +	.functions = jz4740_functions,
> +	.num_functions = ARRAY_SIZE(jz4740_functions),
> +	.pull_ups = jz4740_pull_ups,
> +	.pull_downs = jz4740_pull_downs,
> +};
> +
> +static const u32 jz4770_pull_ups[6] = {
> +	0x3fffffff, 0xfff0030c, 0xffffffff, 0xffff4fff, 0xfffffb7c, 
> 0xffa7f00f,
> +};
> +
> +static const u32 jz4770_pull_downs[6] = {
> +	0x00000000, 0x000f0c03, 0x00000000, 0x0000b000, 0x00000483, 
> 0x00580ff0,
> +};
> +
> +static int jz4770_uart0_data_pins[] = { 0xa0, 0xa3, };
> +static int jz4770_uart0_hwflow_pins[] = { 0xa1, 0xa2, };
> +static int jz4770_uart1_data_pins[] = { 0x7a, 0x7c, };
> +static int jz4770_uart1_hwflow_pins[] = { 0x7b, 0x7d, };
> +static int jz4770_uart2_data_pins[] = { 0x66, 0x67, };
> +static int jz4770_uart2_hwflow_pins[] = { 0x65, 0x64, };
> +static int jz4770_uart3_data_pins[] = { 0x6c, 0x85, };
> +static int jz4770_uart3_hwflow_pins[] = { 0x88, 0x89, };
> +static int jz4770_uart4_data_pins[] = { 0x54, 0x4a, };
> +static int jz4770_mmc0_8bit_a_pins[] = { 0x04, 0x05, 0x06, 0x07, 0x18, 
> };
> +static int jz4770_mmc0_4bit_a_pins[] = { 0x15, 0x16, 0x17, };
> +static int jz4770_mmc0_1bit_a_pins[] = { 0x12, 0x13, 0x14, };
> +static int jz4770_mmc0_4bit_e_pins[] = { 0x95, 0x96, 0x97, };
> +static int jz4770_mmc0_1bit_e_pins[] = { 0x9c, 0x9d, 0x94, };
> +static int jz4770_mmc1_4bit_d_pins[] = { 0x75, 0x76, 0x77, };
> +static int jz4770_mmc1_1bit_d_pins[] = { 0x78, 0x79, 0x74, };
> +static int jz4770_mmc1_4bit_e_pins[] = { 0x95, 0x96, 0x97, };
> +static int jz4770_mmc1_1bit_e_pins[] = { 0x9c, 0x9d, 0x94, };
> +static int jz4770_nemc_data_pins[] = {
> +	0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
> +};
> +static int jz4770_nemc_cle_ale_pins[] = { 0x20, 0x21, };
> +static int jz4770_nemc_addr_pins[] = { 0x22, 0x23, 0x24, 0x25, };
> +static int jz4770_nemc_rd_we_pins[] = { 0x10, 0x11, };
> +static int jz4770_nemc_frd_fwe_pins[] = { 0x12, 0x13, };
> +static int jz4770_nemc_cs1_pins[] = { 0x15, };
> +static int jz4770_nemc_cs2_pins[] = { 0x16, };
> +static int jz4770_nemc_cs3_pins[] = { 0x17, };
> +static int jz4770_nemc_cs4_pins[] = { 0x18, };
> +static int jz4770_nemc_cs5_pins[] = { 0x19, };
> +static int jz4770_nemc_cs6_pins[] = { 0x1a, };
> +static int jz4770_i2c0_pins[] = { 0x6e, 0x6f, };
> +static int jz4770_i2c1_pins[] = { 0x8e, 0x8f, };
> +static int jz4770_i2c2_pins[] = { 0xb0, 0xb1, };
> +static int jz4770_i2c3_pins[] = { 0x6a, 0x6b, };
> +static int jz4770_i2c4_e_pins[] = { 0x8c, 0x8d, };
> +static int jz4770_i2c4_f_pins[] = { 0xb9, 0xb8, };
> +static int jz4770_cim_pins[] = {
> +	0x26, 0x27, 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, 0x30, 
> 0x31,
> +};
> +static int jz4770_lcd_32bit_pins[] = {
> +	0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47,
> +	0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f,
> +	0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57,
> +	0x58, 0x59, 0x51,
> +};
> +static int jz4770_pwm_pwm0_pins[] = { 0x80, };
> +static int jz4770_pwm_pwm1_pins[] = { 0x81, };
> +static int jz4770_pwm_pwm2_pins[] = { 0x82, };
> +static int jz4770_pwm_pwm3_pins[] = { 0x83, };
> +static int jz4770_pwm_pwm4_pins[] = { 0x84, };
> +static int jz4770_pwm_pwm5_pins[] = { 0x85, };
> +static int jz4770_pwm_pwm6_pins[] = { 0x6a, };
> +static int jz4770_pwm_pwm7_pins[] = { 0x6b, };
> +
> +static int jz4770_uart0_data_funcs[] = { 0, 0, };
> +static int jz4770_uart0_hwflow_funcs[] = { 0, 0, };
> +static int jz4770_uart1_data_funcs[] = { 0, 0, };
> +static int jz4770_uart1_hwflow_funcs[] = { 0, 0, };
> +static int jz4770_uart2_data_funcs[] = { 1, 1, };
> +static int jz4770_uart2_hwflow_funcs[] = { 1, 1, };
> +static int jz4770_uart3_data_funcs[] = { 0, 1, };
> +static int jz4770_uart3_hwflow_funcs[] = { 0, 0, };
> +static int jz4770_uart4_data_funcs[] = { 2, 2, };
> +static int jz4770_mmc0_8bit_a_funcs[] = { 1, 1, 1, 1, 1, };
> +static int jz4770_mmc0_4bit_a_funcs[] = { 1, 1, 1, };
> +static int jz4770_mmc0_1bit_a_funcs[] = { 1, 1, 0, };
> +static int jz4770_mmc0_4bit_e_funcs[] = { 0, 0, 0, };
> +static int jz4770_mmc0_1bit_e_funcs[] = { 0, 0, 0, };
> +static int jz4770_mmc1_4bit_d_funcs[] = { 0, 0, 0, };
> +static int jz4770_mmc1_1bit_d_funcs[] = { 0, 0, 0, };
> +static int jz4770_mmc1_4bit_e_funcs[] = { 1, 1, 1, };
> +static int jz4770_mmc1_1bit_e_funcs[] = { 1, 1, 1, };
> +static int jz4770_nemc_data_funcs[] = { 0, 0, 0, 0, 0, 0, 0, 0, };
> +static int jz4770_nemc_cle_ale_funcs[] = { 0, 0, };
> +static int jz4770_nemc_addr_funcs[] = { 0, 0, 0, 0, };
> +static int jz4770_nemc_rd_we_funcs[] = { 0, 0, };
> +static int jz4770_nemc_frd_fwe_funcs[] = { 0, 0, };
> +static int jz4770_nemc_cs1_funcs[] = { 0, };
> +static int jz4770_nemc_cs2_funcs[] = { 0, };
> +static int jz4770_nemc_cs3_funcs[] = { 0, };
> +static int jz4770_nemc_cs4_funcs[] = { 0, };
> +static int jz4770_nemc_cs5_funcs[] = { 0, };
> +static int jz4770_nemc_cs6_funcs[] = { 0, };
> +static int jz4770_i2c0_funcs[] = { 0, 0, };
> +static int jz4770_i2c1_funcs[] = { 0, 0, };
> +static int jz4770_i2c2_funcs[] = { 2, 2, };
> +static int jz4770_i2c3_funcs[] = { 1, 1, };
> +static int jz4770_i2c4_e_funcs[] = { 1, 1, };
> +static int jz4770_i2c4_f_funcs[] = { 1, 1, };
> +static int jz4770_cim_funcs[] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
> };
> +static int jz4770_lcd_32bit_funcs[] = {
> +	0, 0, 0, 0, 0, 0, 0, 0,
> +	0, 0, 0, 0, 0, 0, 0, 0,
> +	0, 0, 0,
> +};
> +static int jz4770_pwm_pwm0_funcs[] = { 0, };
> +static int jz4770_pwm_pwm1_funcs[] = { 0, };
> +static int jz4770_pwm_pwm2_funcs[] = { 0, };
> +static int jz4770_pwm_pwm3_funcs[] = { 0, };
> +static int jz4770_pwm_pwm4_funcs[] = { 0, };
> +static int jz4770_pwm_pwm5_funcs[] = { 0, };
> +static int jz4770_pwm_pwm6_funcs[] = { 0, };
> +static int jz4770_pwm_pwm7_funcs[] = { 0, };
> +
> +static const struct group_desc jz4770_groups[] = {
> +	INGENIC_PIN_GROUP("uart0-data", jz4770_uart0_data),
> +	INGENIC_PIN_GROUP("uart0-hwflow", jz4770_uart0_hwflow),
> +	INGENIC_PIN_GROUP("uart1-data", jz4770_uart1_data),
> +	INGENIC_PIN_GROUP("uart1-hwflow", jz4770_uart1_hwflow),
> +	INGENIC_PIN_GROUP("uart2-data", jz4770_uart2_data),
> +	INGENIC_PIN_GROUP("uart2-hwflow", jz4770_uart2_hwflow),
> +	INGENIC_PIN_GROUP("uart3-data", jz4770_uart3_data),
> +	INGENIC_PIN_GROUP("uart3-hwflow", jz4770_uart3_hwflow),
> +	INGENIC_PIN_GROUP("uart4-data", jz4770_uart4_data),
> +	INGENIC_PIN_GROUP("mmc0-8bit-a", jz4770_mmc0_8bit_a),
> +	INGENIC_PIN_GROUP("mmc0-4bit-a", jz4770_mmc0_4bit_a),
> +	INGENIC_PIN_GROUP("mmc0-1bit-a", jz4770_mmc0_1bit_a),
> +	INGENIC_PIN_GROUP("mmc0-4bit-e", jz4770_mmc0_4bit_e),
> +	INGENIC_PIN_GROUP("mmc0-1bit-e", jz4770_mmc0_1bit_e),
> +	INGENIC_PIN_GROUP("mmc1-4bit-d", jz4770_mmc1_4bit_d),
> +	INGENIC_PIN_GROUP("mmc1-1bit-d", jz4770_mmc1_1bit_d),
> +	INGENIC_PIN_GROUP("mmc1-4bit-e", jz4770_mmc1_4bit_e),
> +	INGENIC_PIN_GROUP("mmc1-1bit-e", jz4770_mmc1_1bit_e),
> +	INGENIC_PIN_GROUP("nemc-data", jz4770_nemc_data),
> +	INGENIC_PIN_GROUP("nemc-cle-ale", jz4770_nemc_cle_ale),
> +	INGENIC_PIN_GROUP("nemc-addr", jz4770_nemc_addr),
> +	INGENIC_PIN_GROUP("nemc-rd-we", jz4770_nemc_rd_we),
> +	INGENIC_PIN_GROUP("nemc-frd-fwe", jz4770_nemc_frd_fwe),
> +	INGENIC_PIN_GROUP("nemc-cs1", jz4770_nemc_cs1),
> +	INGENIC_PIN_GROUP("nemc-cs2", jz4770_nemc_cs2),
> +	INGENIC_PIN_GROUP("nemc-cs3", jz4770_nemc_cs3),
> +	INGENIC_PIN_GROUP("nemc-cs4", jz4770_nemc_cs4),
> +	INGENIC_PIN_GROUP("nemc-cs5", jz4770_nemc_cs5),
> +	INGENIC_PIN_GROUP("nemc-cs6", jz4770_nemc_cs6),
> +	INGENIC_PIN_GROUP("i2c0-data", jz4770_i2c0),
> +	INGENIC_PIN_GROUP("i2c1-data", jz4770_i2c1),
> +	INGENIC_PIN_GROUP("i2c2-data", jz4770_i2c2),
> +	INGENIC_PIN_GROUP("i2c3-data", jz4770_i2c3),
> +	INGENIC_PIN_GROUP("i2c4-data-e", jz4770_i2c4_e),
> +	INGENIC_PIN_GROUP("i2c4-data-f", jz4770_i2c4_f),
> +	INGENIC_PIN_GROUP("cim-data", jz4770_cim),
> +	INGENIC_PIN_GROUP("lcd-32bit", jz4770_lcd_32bit),
> +	{ "lcd-no-pins", },
> +	INGENIC_PIN_GROUP("pwm0", jz4770_pwm_pwm0),
> +	INGENIC_PIN_GROUP("pwm1", jz4770_pwm_pwm1),
> +	INGENIC_PIN_GROUP("pwm2", jz4770_pwm_pwm2),
> +	INGENIC_PIN_GROUP("pwm3", jz4770_pwm_pwm3),
> +	INGENIC_PIN_GROUP("pwm4", jz4770_pwm_pwm4),
> +	INGENIC_PIN_GROUP("pwm5", jz4770_pwm_pwm5),
> +	INGENIC_PIN_GROUP("pwm6", jz4770_pwm_pwm6),
> +	INGENIC_PIN_GROUP("pwm7", jz4770_pwm_pwm7),
> +};
> +
> +static const char *jz4770_uart0_groups[] = { "uart0-data", 
> "uart0-hwflow", };
> +static const char *jz4770_uart1_groups[] = { "uart1-data", 
> "uart1-hwflow", };
> +static const char *jz4770_uart2_groups[] = { "uart2-data", 
> "uart2-hwflow", };
> +static const char *jz4770_uart3_groups[] = { "uart3-data", 
> "uart3-hwflow", };
> +static const char *jz4770_uart4_groups[] = { "uart4-data", };
> +static const char *jz4770_mmc0_groups[] = {
> +	"mmc0-8bit-a", "mmc0-4bit-a", "mmc0-1bit-a",
> +	"mmc0-1bit-e", "mmc0-4bit-e",
> +};
> +static const char *jz4770_mmc1_groups[] = {
> +	"mmc1-1bit-d", "mmc1-4bit-d", "mmc1-1bit-e", "mmc1-4bit-e",
> +};
> +static const char *jz4770_nemc_groups[] = {
> +	"nemc-data", "nemc-cle-ale", "nemc-addr", "nemc-rd-we", 
> "nemc-frd-fwe",
> +};
> +static const char *jz4770_cs1_groups[] = { "nemc-cs1", };
> +static const char *jz4770_cs6_groups[] = { "nemc-cs6", };
> +static const char *jz4770_i2c0_groups[] = { "i2c0-data", };
> +static const char *jz4770_i2c1_groups[] = { "i2c1-data", };
> +static const char *jz4770_i2c2_groups[] = { "i2c2-data", };
> +static const char *jz4770_i2c3_groups[] = { "i2c3-data", };
> +static const char *jz4770_i2c4_groups[] = { "i2c4-data-e", 
> "i2c4-data-f", };
> +static const char *jz4770_cim_groups[] = { "cim-data", };
> +static const char *jz4770_lcd_groups[] = { "lcd-32bit", "lcd-no-pins", 
> };
> +static const char *jz4770_pwm0_groups[] = { "pwm0", };
> +static const char *jz4770_pwm1_groups[] = { "pwm1", };
> +static const char *jz4770_pwm2_groups[] = { "pwm2", };
> +static const char *jz4770_pwm3_groups[] = { "pwm3", };
> +static const char *jz4770_pwm4_groups[] = { "pwm4", };
> +static const char *jz4770_pwm5_groups[] = { "pwm5", };
> +static const char *jz4770_pwm6_groups[] = { "pwm6", };
> +static const char *jz4770_pwm7_groups[] = { "pwm7", };
> +
> +static const struct function_desc jz4770_functions[] = {
> +	{ "uart0", jz4770_uart0_groups, ARRAY_SIZE(jz4770_uart0_groups), },
> +	{ "uart1", jz4770_uart1_groups, ARRAY_SIZE(jz4770_uart1_groups), },
> +	{ "uart2", jz4770_uart2_groups, ARRAY_SIZE(jz4770_uart2_groups), },
> +	{ "uart3", jz4770_uart3_groups, ARRAY_SIZE(jz4770_uart3_groups), },
> +	{ "uart4", jz4770_uart4_groups, ARRAY_SIZE(jz4770_uart4_groups), },
> +	{ "mmc0", jz4770_mmc0_groups, ARRAY_SIZE(jz4770_mmc0_groups), },
> +	{ "mmc1", jz4770_mmc1_groups, ARRAY_SIZE(jz4770_mmc1_groups), },
> +	{ "nemc", jz4770_nemc_groups, ARRAY_SIZE(jz4770_nemc_groups), },
> +	{ "nemc-cs1", jz4770_cs1_groups, ARRAY_SIZE(jz4770_cs1_groups), },
> +	{ "nemc-cs6", jz4770_cs6_groups, ARRAY_SIZE(jz4770_cs6_groups), },
> +	{ "i2c0", jz4770_i2c0_groups, ARRAY_SIZE(jz4770_i2c0_groups), },
> +	{ "i2c1", jz4770_i2c1_groups, ARRAY_SIZE(jz4770_i2c1_groups), },
> +	{ "i2c2", jz4770_i2c2_groups, ARRAY_SIZE(jz4770_i2c2_groups), },
> +	{ "i2c3", jz4770_i2c3_groups, ARRAY_SIZE(jz4770_i2c3_groups), },
> +	{ "i2c4", jz4770_i2c4_groups, ARRAY_SIZE(jz4770_i2c4_groups), },
> +	{ "cim", jz4770_cim_groups, ARRAY_SIZE(jz4770_cim_groups), },
> +	{ "lcd", jz4770_lcd_groups, ARRAY_SIZE(jz4770_lcd_groups), },
> +	{ "pwm0", jz4770_pwm0_groups, ARRAY_SIZE(jz4770_pwm0_groups), },
> +	{ "pwm1", jz4770_pwm1_groups, ARRAY_SIZE(jz4770_pwm1_groups), },
> +	{ "pwm2", jz4770_pwm2_groups, ARRAY_SIZE(jz4770_pwm2_groups), },
> +	{ "pwm3", jz4770_pwm3_groups, ARRAY_SIZE(jz4770_pwm3_groups), },
> +	{ "pwm4", jz4770_pwm4_groups, ARRAY_SIZE(jz4770_pwm4_groups), },
> +	{ "pwm5", jz4770_pwm5_groups, ARRAY_SIZE(jz4770_pwm5_groups), },
> +	{ "pwm6", jz4770_pwm6_groups, ARRAY_SIZE(jz4770_pwm6_groups), },
> +	{ "pwm7", jz4770_pwm7_groups, ARRAY_SIZE(jz4770_pwm7_groups), },
> +};
> +
> +static const struct ingenic_chip_info jz4770_chip_info = {
> +	.num_chips = 6,
> +	.groups = jz4770_groups,
> +	.num_groups = ARRAY_SIZE(jz4770_groups),
> +	.functions = jz4770_functions,
> +	.num_functions = ARRAY_SIZE(jz4770_functions),
> +	.pull_ups = jz4770_pull_ups,
> +	.pull_downs = jz4770_pull_downs,
> +};
> +
> +static inline void ingenic_config_pin(struct ingenic_pinctrl *jzpc,
> +		unsigned int pin, u8 reg, bool set)
> +{
> +	unsigned int idx = pin % PINS_PER_GPIO_CHIP;
> +	unsigned int offt = pin / PINS_PER_GPIO_CHIP;
> +
> +	regmap_write(jzpc->map, offt * 0x100 +
> +			(set ? REG_SET(reg) : REG_CLEAR(reg)), BIT(idx));
> +}
> +
> +static inline bool ingenic_get_pin_config(struct ingenic_pinctrl 
> *jzpc,
> +		unsigned int pin, u8 reg)
> +{
> +	unsigned int idx = pin % PINS_PER_GPIO_CHIP;
> +	unsigned int offt = pin / PINS_PER_GPIO_CHIP;
> +	unsigned int val;
> +
> +	regmap_read(jzpc->map, offt * 0x100 + reg, &val);
> +
> +	return val & BIT(idx);
> +}
> +
> +static struct pinctrl_ops ingenic_pctlops = {
> +	.get_groups_count = pinctrl_generic_get_group_count,
> +	.get_group_name = pinctrl_generic_get_group_name,
> +	.get_group_pins = pinctrl_generic_get_group_pins,
> +	.dt_node_to_map = pinconf_generic_dt_node_to_map_all,
> +	.dt_free_map = pinconf_generic_dt_free_map,
> +};
> +
> +static int ingenic_pinmux_set_pin_fn(struct ingenic_pinctrl *jzpc,
> +		int pin, int func)
> +{
> +	unsigned int idx = pin % PINS_PER_GPIO_CHIP;
> +	unsigned int offt = pin / PINS_PER_GPIO_CHIP;
> +
> +	dev_dbg(jzpc->dev, "set pin P%c%u to function %u\n",
> +			'A' + offt, idx, func);
> +
> +	if (jzpc->version >= ID_JZ4770) {
> +		ingenic_config_pin(jzpc, pin, JZ4770_GPIO_INT, false);
> +		ingenic_config_pin(jzpc, pin, JZ4770_GPIO_MSK, false);
> +		ingenic_config_pin(jzpc, pin, JZ4770_GPIO_PAT1, func & 0x2);
> +		ingenic_config_pin(jzpc, pin, JZ4770_GPIO_PAT0, func & 0x1);
> +	} else {
> +		ingenic_config_pin(jzpc, pin, JZ4740_GPIO_FUNC, true);
> +		ingenic_config_pin(jzpc, pin, JZ4740_GPIO_TRIG, func & 0x2);
> +		ingenic_config_pin(jzpc, pin, JZ4740_GPIO_SELECT, func > 0);
> +	}
> +
> +	return 0;
> +}
> +
> +static int ingenic_pinmux_set_mux(struct pinctrl_dev *pctldev,
> +		unsigned int selector, unsigned int group)
> +{
> +	struct ingenic_pinctrl *jzpc = pinctrl_dev_get_drvdata(pctldev);
> +	struct function_desc *func;
> +	struct group_desc *grp;
> +	unsigned int i;
> +
> +	func = pinmux_generic_get_function(pctldev, selector);
> +	if (!func)
> +		return -EINVAL;
> +
> +	grp = pinctrl_generic_get_group(pctldev, group);
> +	if (!grp)
> +		return -EINVAL;
> +
> +	dev_dbg(pctldev->dev, "enable function %s group %s\n",
> +		func->name, grp->name);
> +
> +	for (i = 0; i < grp->num_pins; i++) {
> +		int *pin_modes = grp->data;
> +
> +		ingenic_pinmux_set_pin_fn(jzpc, grp->pins[i], pin_modes[i]);
> +	}
> +
> +	return 0;
> +}
> +
> +static int ingenic_pinmux_gpio_set_direction(struct pinctrl_dev 
> *pctldev,
> +		struct pinctrl_gpio_range *range,
> +		unsigned int pin, bool input)
> +{
> +	struct ingenic_pinctrl *jzpc = pinctrl_dev_get_drvdata(pctldev);
> +	unsigned int idx = pin % PINS_PER_GPIO_CHIP;
> +	unsigned int offt = pin / PINS_PER_GPIO_CHIP;
> +
> +	dev_dbg(pctldev->dev, "set pin P%c%u to %sput\n",
> +			'A' + offt, idx, input ? "in" : "out");
> +
> +	if (jzpc->version >= ID_JZ4770) {
> +		ingenic_config_pin(jzpc, pin, JZ4770_GPIO_INT, false);
> +		ingenic_config_pin(jzpc, pin, JZ4770_GPIO_MSK, true);
> +		ingenic_config_pin(jzpc, pin, JZ4770_GPIO_PAT1, input);
> +	} else {
> +		ingenic_config_pin(jzpc, pin, JZ4740_GPIO_SELECT, false);
> +		ingenic_config_pin(jzpc, pin, JZ4740_GPIO_DIR, input);
> +		ingenic_config_pin(jzpc, pin, JZ4740_GPIO_FUNC, false);
> +	}
> +
> +	return 0;
> +}
> +
> +static struct pinmux_ops ingenic_pmxops = {
> +	.get_functions_count = pinmux_generic_get_function_count,
> +	.get_function_name = pinmux_generic_get_function_name,
> +	.get_function_groups = pinmux_generic_get_function_groups,
> +	.set_mux = ingenic_pinmux_set_mux,
> +	.gpio_set_direction = ingenic_pinmux_gpio_set_direction,
> +};
> +
> +static int ingenic_pinconf_get(struct pinctrl_dev *pctldev,
> +		unsigned int pin, unsigned long *config)
> +{
> +	struct ingenic_pinctrl *jzpc = pinctrl_dev_get_drvdata(pctldev);
> +	enum pin_config_param param = pinconf_to_config_param(*config);
> +	unsigned int idx = pin % PINS_PER_GPIO_CHIP;
> +	unsigned int offt = pin / PINS_PER_GPIO_CHIP;
> +	bool pull;
> +
> +	if (jzpc->version >= ID_JZ4770)
> +		pull = !ingenic_get_pin_config(jzpc, pin, JZ4770_GPIO_PEN);
> +	else
> +		pull = !ingenic_get_pin_config(jzpc, pin, JZ4740_GPIO_PULL_DIS);
> +
> +	switch (param) {
> +	case PIN_CONFIG_BIAS_DISABLE:
> +		if (pull)
> +			return -EINVAL;
> +		break;
> +
> +	case PIN_CONFIG_BIAS_PULL_UP:
> +		if (!pull || !(jzpc->info->pull_ups[offt] & BIT(idx)))
> +			return -EINVAL;
> +		break;
> +
> +	case PIN_CONFIG_BIAS_PULL_DOWN:
> +		if (!pull || !(jzpc->info->pull_downs[offt] & BIT(idx)))
> +			return -EINVAL;
> +		break;
> +
> +	default:
> +		return -ENOTSUPP;
> +	}
> +
> +	*config = pinconf_to_config_packed(param, 1);
> +	return 0;
> +}
> +
> +static void ingenic_set_bias(struct ingenic_pinctrl *jzpc,
> +		unsigned int pin, bool enabled)
> +{
> +	if (jzpc->version >= ID_JZ4770)
> +		ingenic_config_pin(jzpc, pin, JZ4770_GPIO_PEN, !enabled);
> +	else
> +		ingenic_config_pin(jzpc, pin, JZ4740_GPIO_PULL_DIS, !enabled);
> +}
> +
> +static int ingenic_pinconf_set(struct pinctrl_dev *pctldev, unsigned 
> int pin,
> +		unsigned long *configs, unsigned int num_configs)
> +{
> +	struct ingenic_pinctrl *jzpc = pinctrl_dev_get_drvdata(pctldev);
> +	unsigned int idx = pin % PINS_PER_GPIO_CHIP;
> +	unsigned int offt = pin / PINS_PER_GPIO_CHIP;
> +	unsigned int cfg;
> +
> +	for (cfg = 0; cfg < num_configs; cfg++) {
> +		switch (pinconf_to_config_param(configs[cfg])) {
> +		case PIN_CONFIG_BIAS_DISABLE:
> +		case PIN_CONFIG_BIAS_PULL_UP:
> +		case PIN_CONFIG_BIAS_PULL_DOWN:
> +			continue;
> +		default:
> +			return -ENOTSUPP;
> +		}
> +	}
> +
> +	for (cfg = 0; cfg < num_configs; cfg++) {
> +		switch (pinconf_to_config_param(configs[cfg])) {
> +		case PIN_CONFIG_BIAS_DISABLE:
> +			dev_dbg(jzpc->dev, "disable pull-over for pin P%c%u\n",
> +					'A' + offt, idx);
> +			ingenic_set_bias(jzpc, pin, false);
> +			break;
> +
> +		case PIN_CONFIG_BIAS_PULL_UP:
> +			if (!(jzpc->info->pull_ups[offt] & BIT(idx)))
> +				return -EINVAL;
> +			dev_dbg(jzpc->dev, "set pull-up for pin P%c%u\n",
> +					'A' + offt, idx);
> +			ingenic_set_bias(jzpc, pin, true);
> +			break;
> +
> +		case PIN_CONFIG_BIAS_PULL_DOWN:
> +			if (!(jzpc->info->pull_downs[offt] & BIT(idx)))
> +				return -EINVAL;
> +			dev_dbg(jzpc->dev, "set pull-down for pin P%c%u\n",
> +					'A' + offt, idx);
> +			ingenic_set_bias(jzpc, pin, true);
> +			break;
> +
> +		default:
> +			unreachable();
> +		}
> +	}
> +
> +	return 0;
> +}
> +
> +static int ingenic_pinconf_group_get(struct pinctrl_dev *pctldev,
> +		unsigned int group, unsigned long *config)
> +{
> +	const unsigned int *pins;
> +	unsigned int i, npins, old = 0;
> +	int ret;
> +
> +	ret = pinctrl_generic_get_group_pins(pctldev, group, &pins, &npins);
> +	if (ret)
> +		return ret;
> +
> +	for (i = 0; i < npins; i++) {
> +		if (ingenic_pinconf_get(pctldev, pins[i], config))
> +			return -ENOTSUPP;
> +
> +		/* configs do not match between two pins */
> +		if (i && (old != *config))
> +			return -ENOTSUPP;
> +
> +		old = *config;
> +	}
> +
> +	return 0;
> +}
> +
> +static int ingenic_pinconf_group_set(struct pinctrl_dev *pctldev,
> +		unsigned int group, unsigned long *configs,
> +		unsigned int num_configs)
> +{
> +	const unsigned int *pins;
> +	unsigned int i, npins;
> +	int ret;
> +
> +	ret = pinctrl_generic_get_group_pins(pctldev, group, &pins, &npins);
> +	if (ret)
> +		return ret;
> +
> +	for (i = 0; i < npins; i++) {
> +		ret = ingenic_pinconf_set(pctldev,
> +				pins[i], configs, num_configs);
> +		if (ret)
> +			return ret;
> +	}
> +
> +	return 0;
> +}
> +
> +static struct pinconf_ops ingenic_confops = {
> +	.is_generic = true,
> +	.pin_config_get = ingenic_pinconf_get,
> +	.pin_config_set = ingenic_pinconf_set,
> +	.pin_config_group_get = ingenic_pinconf_group_get,
> +	.pin_config_group_set = ingenic_pinconf_group_set,
> +};
> +
> +static const struct regmap_config ingenic_pinctrl_regmap_config = {
> +	.reg_bits = 32,
> +	.val_bits = 32,
> +	.reg_stride = 4,
> +};
> +
> +static const struct of_device_id ingenic_pinctrl_of_match[] = {
> +	{ .compatible = "ingenic,jz4740-pinctrl", .data = (void *) ID_JZ4740 
> },
> +	{ .compatible = "ingenic,jz4770-pinctrl", .data = (void *) ID_JZ4770 
> },
> +	{ .compatible = "ingenic,jz4780-pinctrl", .data = (void *) ID_JZ4780 
> },
> +	{},
> +};
> +
> +int ingenic_pinctrl_probe(struct platform_device *pdev)
> +{
> +	struct device *dev = &pdev->dev;
> +	struct ingenic_pinctrl *jzpc;
> +	struct pinctrl_desc *pctl_desc;
> +	void __iomem *base;
> +	const struct platform_device_id *id = platform_get_device_id(pdev);
> +	const struct of_device_id *of_id = of_match_device(
> +			ingenic_pinctrl_of_match, dev);
> +	const struct ingenic_chip_info *chip_info;
> +	unsigned int i;
> +	int err;
> +
> +	jzpc = devm_kzalloc(dev, sizeof(*jzpc), GFP_KERNEL);
> +	if (!jzpc)
> +		return -ENOMEM;
> +
> +	base = devm_ioremap_resource(dev,
> +			platform_get_resource(pdev, IORESOURCE_MEM, 0));
> +	if (IS_ERR(base)) {
> +		dev_err(dev, "Failed to ioremap registers\n");
> +		return PTR_ERR(base);
> +	}
> +
> +	jzpc->map = devm_regmap_init_mmio(dev, base,
> +			&ingenic_pinctrl_regmap_config);
> +	if (IS_ERR(jzpc->map)) {
> +		dev_err(dev, "Failed to create regmap\n");
> +		return PTR_ERR(jzpc->map);
> +	}
> +
> +	jzpc->dev = dev;
> +
> +	if (of_id)
> +		jzpc->version = (enum jz_version)of_id->data;
> +	else
> +		jzpc->version = (enum jz_version)id->driver_data;
> +
> +	if (jzpc->version >= ID_JZ4770)
> +		chip_info = &jz4770_chip_info;
> +	else
> +		chip_info = &jz4740_chip_info;
> +	jzpc->info = chip_info;
> +
> +	pctl_desc = devm_kzalloc(&pdev->dev, sizeof(*pctl_desc), GFP_KERNEL);
> +	if (!pctl_desc)
> +		return -ENOMEM;
> +
> +	/* fill in pinctrl_desc structure */
> +	pctl_desc->name = dev_name(dev);
> +	pctl_desc->owner = THIS_MODULE;
> +	pctl_desc->pctlops = &ingenic_pctlops;
> +	pctl_desc->pmxops = &ingenic_pmxops;
> +	pctl_desc->confops = &ingenic_confops;
> +	pctl_desc->npins = chip_info->num_chips * PINS_PER_GPIO_CHIP;
> +	pctl_desc->pins = jzpc->pdesc = devm_kzalloc(&pdev->dev,
> +			sizeof(*jzpc->pdesc) * pctl_desc->npins, GFP_KERNEL);
> +	if (!jzpc->pdesc)
> +		return -ENOMEM;
> +
> +	for (i = 0; i < pctl_desc->npins; i++) {
> +		jzpc->pdesc[i].number = i;
> +		jzpc->pdesc[i].name = kasprintf(GFP_KERNEL, "P%c%d",
> +						'A' + (i / PINS_PER_GPIO_CHIP),
> +						i % PINS_PER_GPIO_CHIP);
> +	}
> +
> +	jzpc->pctl = devm_pinctrl_register(dev, pctl_desc, jzpc);
> +	if (!jzpc->pctl) {
> +		dev_err(dev, "Failed to register pinctrl\n");
> +		return -EINVAL;
> +	}
> +
> +	for (i = 0; i < chip_info->num_groups; i++) {
> +		const struct group_desc *group = &chip_info->groups[i];
> +
> +		err = pinctrl_generic_add_group(jzpc->pctl, group->name,
> +				group->pins, group->num_pins, group->data);
> +		if (err) {
> +			dev_err(dev, "Failed to register group %s\n",
> +					group->name);
> +			return err;
> +		}
> +	}
> +
> +	for (i = 0; i < chip_info->num_functions; i++) {
> +		const struct function_desc *func = &chip_info->functions[i];
> +
> +		err = pinmux_generic_add_function(jzpc->pctl, func->name,
> +				func->group_names, func->num_group_names,
> +				func->data);
> +		if (err) {
> +			dev_err(dev, "Failed to register function %s\n",
> +					func->name);
> +			return err;
> +		}
> +	}
> +
> +	dev_set_drvdata(dev, jzpc->map);
> +
> +	if (dev->of_node) {
> +		err = of_platform_populate(dev->of_node, NULL, NULL, dev);
> +		if (err) {
> +			dev_err(dev, "Failed to probe GPIO devices\n");
> +			return err;
> +		}
> +	}
> +
> +	return 0;
> +}
> +
> +static const struct platform_device_id ingenic_pinctrl_ids[] = {
> +	{ "jz4740-pinctrl", ID_JZ4740 },
> +	{ "jz4770-pinctrl", ID_JZ4770 },
> +	{ "jz4780-pinctrl", ID_JZ4780 },
> +	{},
> +};
> +
> +static struct platform_driver ingenic_pinctrl_driver = {
> +	.driver = {
> +		.name = "pinctrl-ingenic",
> +		.of_match_table = of_match_ptr(ingenic_pinctrl_of_match),
> +		.suppress_bind_attrs = true,
> +	},
> +	.probe = ingenic_pinctrl_probe,
> +	.id_table = ingenic_pinctrl_ids,
> +};
> +
> +static int __init ingenic_pinctrl_drv_register(void)
> +{
> +	return platform_driver_register(&ingenic_pinctrl_driver);
> +}
> +postcore_initcall(ingenic_pinctrl_drv_register);


^ permalink raw reply


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