* [PATCH v2 14/20] drm/sun4i: tcon: multiply the vtotal when not in interlace
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>
It appears that the total vertical resolution needs to be doubled when
we're not in interlaced. Make sure that is the case.
Signed-off-by: Maxime Ripard <maxime.ripard-wi1+55ScJUtKEb57/3fJTNBPR1lH4CV8@public.gmane.org>
---
drivers/gpu/drm/sun4i/sun4i_tcon.c | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/drivers/gpu/drm/sun4i/sun4i_tcon.c b/drivers/gpu/drm/sun4i/sun4i_tcon.c
index 0f91ec8a4b26..efa079c1a3f5 100644
--- a/drivers/gpu/drm/sun4i/sun4i_tcon.c
+++ b/drivers/gpu/drm/sun4i/sun4i_tcon.c
@@ -272,9 +272,9 @@ void sun4i_tcon1_mode_set(struct sun4i_tcon *tcon,
/* Set vertical display timings */
bp = mode->crtc_vtotal - mode->crtc_vsync_start;
DRM_DEBUG_DRIVER("Setting vertical total %d, backporch %d\n",
- mode->vtotal, bp);
+ mode->crtc_vtotal, bp);
regmap_write(tcon->regs, SUN4I_TCON1_BASIC4_REG,
- SUN4I_TCON1_BASIC4_V_TOTAL(mode->vtotal) |
+ SUN4I_TCON1_BASIC4_V_TOTAL(mode->crtc_vtotal * 2) |
SUN4I_TCON1_BASIC4_V_BACKPORCH(bp));
/* Set Hsync and Vsync length */
--
git-series 0.8.11
^ permalink raw reply related
* [PATCH v2 13/20] drm/sun4i: tcon: Change vertical total size computation inconsistency
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>
Both TCON channels need to have the resolution doubled, since the size the
hardware is going to use is whatever we put in the register divided by two.
However, we handle it differently for the two channels: in the channel 0,
our register access macro does the multiplication of the value passed as
paremeter, while in the channel 1, the macro doesn't do this, and we need
to do it before calling it.
Make this consistent by aligning the channel 0 with the channel 1
behaviour.
Signed-off-by: Maxime Ripard <maxime.ripard-wi1+55ScJUtKEb57/3fJTNBPR1lH4CV8@public.gmane.org>
---
drivers/gpu/drm/sun4i/sun4i_tcon.c | 2 +-
drivers/gpu/drm/sun4i/sun4i_tcon.h | 2 +-
2 files changed, 2 insertions(+), 2 deletions(-)
diff --git a/drivers/gpu/drm/sun4i/sun4i_tcon.c b/drivers/gpu/drm/sun4i/sun4i_tcon.c
index 86a806fae9fb..0f91ec8a4b26 100644
--- a/drivers/gpu/drm/sun4i/sun4i_tcon.c
+++ b/drivers/gpu/drm/sun4i/sun4i_tcon.c
@@ -190,7 +190,7 @@ void sun4i_tcon0_mode_set(struct sun4i_tcon *tcon,
/* Set vertical display timings */
regmap_write(tcon->regs, SUN4I_TCON0_BASIC2_REG,
- SUN4I_TCON0_BASIC2_V_TOTAL(mode->crtc_vtotal) |
+ SUN4I_TCON0_BASIC2_V_TOTAL(mode->crtc_vtotal * 2) |
SUN4I_TCON0_BASIC2_V_BACKPORCH(bp));
/* Set Hsync and Vsync length */
diff --git a/drivers/gpu/drm/sun4i/sun4i_tcon.h b/drivers/gpu/drm/sun4i/sun4i_tcon.h
index 0350936b413c..3517122ee679 100644
--- a/drivers/gpu/drm/sun4i/sun4i_tcon.h
+++ b/drivers/gpu/drm/sun4i/sun4i_tcon.h
@@ -51,7 +51,7 @@
#define SUN4I_TCON0_BASIC1_H_BACKPORCH(bp) (((bp) - 1) & 0xfff)
#define SUN4I_TCON0_BASIC2_REG 0x50
-#define SUN4I_TCON0_BASIC2_V_TOTAL(total) ((((total) * 2) & 0x1fff) << 16)
+#define SUN4I_TCON0_BASIC2_V_TOTAL(total) (((total) & 0x1fff) << 16)
#define SUN4I_TCON0_BASIC2_V_BACKPORCH(bp) (((bp) - 1) & 0xfff)
#define SUN4I_TCON0_BASIC3_REG 0x54
--
git-series 0.8.11
^ permalink raw reply related
* [PATCH v2 12/20] drm/sun4i: tcon: Fix tcon channel 1 backporch calculation
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>
It seems like what's called a backporch in the datasheet is actually the
backporch plus the sync period. Fix that in our driver.
Acked-by: Chen-Yu Tsai <wens-jdAy2FN1RRM@public.gmane.org>
Signed-off-by: Maxime Ripard <maxime.ripard-wi1+55ScJUtKEb57/3fJTNBPR1lH4CV8@public.gmane.org>
---
drivers/gpu/drm/sun4i/sun4i_tcon.c | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/drivers/gpu/drm/sun4i/sun4i_tcon.c b/drivers/gpu/drm/sun4i/sun4i_tcon.c
index 1c609d808b86..86a806fae9fb 100644
--- a/drivers/gpu/drm/sun4i/sun4i_tcon.c
+++ b/drivers/gpu/drm/sun4i/sun4i_tcon.c
@@ -262,7 +262,7 @@ void sun4i_tcon1_mode_set(struct sun4i_tcon *tcon,
SUN4I_TCON1_BASIC2_Y(mode->crtc_vdisplay));
/* Set horizontal display timings */
- bp = mode->crtc_htotal - mode->crtc_hsync_end;
+ bp = mode->crtc_htotal - mode->crtc_hsync_start;
DRM_DEBUG_DRIVER("Setting horizontal total %d, backporch %d\n",
mode->htotal, bp);
regmap_write(tcon->regs, SUN4I_TCON1_BASIC3_REG,
@@ -270,7 +270,7 @@ void sun4i_tcon1_mode_set(struct sun4i_tcon *tcon,
SUN4I_TCON1_BASIC3_H_BACKPORCH(bp));
/* Set vertical display timings */
- bp = mode->crtc_vtotal - mode->crtc_vsync_end;
+ bp = mode->crtc_vtotal - mode->crtc_vsync_start;
DRM_DEBUG_DRIVER("Setting vertical total %d, backporch %d\n",
mode->vtotal, bp);
regmap_write(tcon->regs, SUN4I_TCON1_BASIC4_REG,
--
git-series 0.8.11
^ permalink raw reply related
* [PATCH v2 11/20] drm/sun4i: tcon: Switch mux on only for composite
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>
Even though that mux is undocumented, it seems like it needs to be set to 1
when using composite, and 0 when using HDMI.
Signed-off-by: Maxime Ripard <maxime.ripard-wi1+55ScJUtKEb57/3fJTNBPR1lH4CV8@public.gmane.org>
---
drivers/gpu/drm/sun4i/sun4i_tcon.c | 9 ++++++++-
1 file changed, 8 insertions(+), 1 deletion(-)
diff --git a/drivers/gpu/drm/sun4i/sun4i_tcon.c b/drivers/gpu/drm/sun4i/sun4i_tcon.c
index 0204d9fadb66..1c609d808b86 100644
--- a/drivers/gpu/drm/sun4i/sun4i_tcon.c
+++ b/drivers/gpu/drm/sun4i/sun4i_tcon.c
@@ -111,16 +111,23 @@ EXPORT_SYMBOL(sun4i_tcon_enable_vblank);
void sun4i_tcon_set_mux(struct sun4i_tcon *tcon, int channel,
struct drm_encoder *encoder)
{
+ u32 val;
+
if (!tcon->quirks->has_unknown_mux)
return;
if (channel != 1)
return;
+ if (encoder->encoder_type == DRM_MODE_ENCODER_TVDAC)
+ val = 1;
+ else
+ val = 0;
+
/*
* FIXME: Undocumented bits
*/
- regmap_write(tcon->regs, SUN4I_TCON_MUX_CTRL_REG, 1);
+ regmap_write(tcon->regs, SUN4I_TCON_MUX_CTRL_REG, val);
}
EXPORT_SYMBOL(sun4i_tcon_set_mux);
--
git-series 0.8.11
^ permalink raw reply related
* [PATCH v2 10/20] drm/sun4i: tcon: Move the muxing out of the mode set function
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 muxing can actually happen on both channels on some SoCs, so it makes
more sense to just move it out of the sun4i_tcon1_mode_set function and
create a separate function that needs to be called by the encoders.
Let's do that and convert the existing drivers.
Signed-off-by: Maxime Ripard <maxime.ripard-wi1+55ScJUtKEb57/3fJTNBPR1lH4CV8@public.gmane.org>
---
drivers/gpu/drm/sun4i/sun4i_rgb.c | 1 +
drivers/gpu/drm/sun4i/sun4i_tcon.c | 22 ++++++++++++++++------
drivers/gpu/drm/sun4i/sun4i_tcon.h | 2 ++
drivers/gpu/drm/sun4i/sun4i_tv.c | 1 +
4 files changed, 20 insertions(+), 6 deletions(-)
diff --git a/drivers/gpu/drm/sun4i/sun4i_rgb.c b/drivers/gpu/drm/sun4i/sun4i_rgb.c
index 67f0b91a99de..3003d290c635 100644
--- a/drivers/gpu/drm/sun4i/sun4i_rgb.c
+++ b/drivers/gpu/drm/sun4i/sun4i_rgb.c
@@ -175,6 +175,7 @@ static void sun4i_rgb_encoder_mode_set(struct drm_encoder *encoder,
struct sun4i_tcon *tcon = rgb->tcon;
sun4i_tcon0_mode_set(tcon, mode);
+ sun4i_tcon_set_mux(tcon, 0, encoder);
clk_set_rate(tcon->dclk, mode->crtc_clock * 1000);
diff --git a/drivers/gpu/drm/sun4i/sun4i_tcon.c b/drivers/gpu/drm/sun4i/sun4i_tcon.c
index 66a5bb9b85e9..0204d9fadb66 100644
--- a/drivers/gpu/drm/sun4i/sun4i_tcon.c
+++ b/drivers/gpu/drm/sun4i/sun4i_tcon.c
@@ -108,6 +108,22 @@ void sun4i_tcon_enable_vblank(struct sun4i_tcon *tcon, bool enable)
}
EXPORT_SYMBOL(sun4i_tcon_enable_vblank);
+void sun4i_tcon_set_mux(struct sun4i_tcon *tcon, int channel,
+ struct drm_encoder *encoder)
+{
+ if (!tcon->quirks->has_unknown_mux)
+ return;
+
+ if (channel != 1)
+ return;
+
+ /*
+ * FIXME: Undocumented bits
+ */
+ regmap_write(tcon->regs, SUN4I_TCON_MUX_CTRL_REG, 1);
+}
+EXPORT_SYMBOL(sun4i_tcon_set_mux);
+
static int sun4i_tcon_get_clk_delay(struct drm_display_mode *mode,
int channel)
{
@@ -266,12 +282,6 @@ void sun4i_tcon1_mode_set(struct sun4i_tcon *tcon,
regmap_update_bits(tcon->regs, SUN4I_TCON_GCTL_REG,
SUN4I_TCON_GCTL_IOMAP_MASK,
SUN4I_TCON_GCTL_IOMAP_TCON1);
-
- /*
- * FIXME: Undocumented bits
- */
- if (tcon->quirks->has_unknown_mux)
- regmap_write(tcon->regs, SUN4I_TCON_MUX_CTRL_REG, 1);
}
EXPORT_SYMBOL(sun4i_tcon1_mode_set);
diff --git a/drivers/gpu/drm/sun4i/sun4i_tcon.h b/drivers/gpu/drm/sun4i/sun4i_tcon.h
index f636343a935d..0350936b413c 100644
--- a/drivers/gpu/drm/sun4i/sun4i_tcon.h
+++ b/drivers/gpu/drm/sun4i/sun4i_tcon.h
@@ -190,6 +190,8 @@ void sun4i_tcon_enable_vblank(struct sun4i_tcon *tcon, bool enable);
/* Mode Related Controls */
void sun4i_tcon_switch_interlace(struct sun4i_tcon *tcon,
bool enable);
+void sun4i_tcon_set_mux(struct sun4i_tcon *tcon, int channel,
+ struct drm_encoder *encoder);
void sun4i_tcon0_mode_set(struct sun4i_tcon *tcon,
struct drm_display_mode *mode);
void sun4i_tcon1_mode_set(struct sun4i_tcon *tcon,
diff --git a/drivers/gpu/drm/sun4i/sun4i_tv.c b/drivers/gpu/drm/sun4i/sun4i_tv.c
index 49c49431a053..03c494b8159c 100644
--- a/drivers/gpu/drm/sun4i/sun4i_tv.c
+++ b/drivers/gpu/drm/sun4i/sun4i_tv.c
@@ -393,6 +393,7 @@ static void sun4i_tv_mode_set(struct drm_encoder *encoder,
const struct tv_mode *tv_mode = sun4i_tv_find_tv_by_mode(mode);
sun4i_tcon1_mode_set(tcon, mode);
+ sun4i_tcon_set_mux(tcon, 1, encoder);
/* Enable and map the DAC to the output */
regmap_update_bits(tv->regs, SUN4I_TVE_EN_REG,
--
git-series 0.8.11
^ permalink raw reply related
* [PATCH v2 9/20] drm/sun4i: tcon: Add channel debug
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>
While all functions have debug logs, the channel enable and disable are not
logged. Make sure this is the case.
Acked-by: Chen-Yu Tsai <wens-jdAy2FN1RRM@public.gmane.org>
Signed-off-by: Maxime Ripard <maxime.ripard-wi1+55ScJUtKEb57/3fJTNBPR1lH4CV8@public.gmane.org>
---
drivers/gpu/drm/sun4i/sun4i_tcon.c | 4 ++++
1 file changed, 4 insertions(+), 0 deletions(-)
diff --git a/drivers/gpu/drm/sun4i/sun4i_tcon.c b/drivers/gpu/drm/sun4i/sun4i_tcon.c
index 9a83a85529ac..66a5bb9b85e9 100644
--- a/drivers/gpu/drm/sun4i/sun4i_tcon.c
+++ b/drivers/gpu/drm/sun4i/sun4i_tcon.c
@@ -54,6 +54,8 @@ EXPORT_SYMBOL(sun4i_tcon_enable);
void sun4i_tcon_channel_disable(struct sun4i_tcon *tcon, int channel)
{
+ DRM_DEBUG_DRIVER("Disabling TCON channel %d\n", channel);
+
/* Disable the TCON's channel */
if (channel == 0) {
regmap_update_bits(tcon->regs, SUN4I_TCON0_CTL_REG,
@@ -71,6 +73,8 @@ EXPORT_SYMBOL(sun4i_tcon_channel_disable);
void sun4i_tcon_channel_enable(struct sun4i_tcon *tcon, int channel)
{
+ DRM_DEBUG_DRIVER("Enabling TCON channel %d\n", channel);
+
/* Enable the TCON's channel */
if (channel == 0) {
regmap_update_bits(tcon->regs, SUN4I_TCON0_CTL_REG,
--
git-series 0.8.11
^ permalink raw reply related
* [PATCH v2 8/20] clk: sunxi-ng: sun5i: Export video PLLs
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 video PLLs are used directly by the HDMI controller. Export them so
that we can use them in our DT node.
Signed-off-by: Maxime Ripard <maxime.ripard-wi1+55ScJUtKEb57/3fJTNBPR1lH4CV8@public.gmane.org>
---
drivers/clk/sunxi-ng/ccu-sun5i.h | 6 ++++--
include/dt-bindings/clock/sun5i-ccu.h | 3 +++
2 files changed, 7 insertions(+), 2 deletions(-)
diff --git a/drivers/clk/sunxi-ng/ccu-sun5i.h b/drivers/clk/sunxi-ng/ccu-sun5i.h
index 8144487eb7ca..93a275fbd9a9 100644
--- a/drivers/clk/sunxi-ng/ccu-sun5i.h
+++ b/drivers/clk/sunxi-ng/ccu-sun5i.h
@@ -28,15 +28,17 @@
#define CLK_PLL_AUDIO_4X 6
#define CLK_PLL_AUDIO_8X 7
#define CLK_PLL_VIDEO0 8
-#define CLK_PLL_VIDEO0_2X 9
+
+/* The PLL_VIDEO0_2X is exported for HDMI */
+
#define CLK_PLL_VE 10
#define CLK_PLL_DDR_BASE 11
#define CLK_PLL_DDR 12
#define CLK_PLL_DDR_OTHER 13
#define CLK_PLL_PERIPH 14
#define CLK_PLL_VIDEO1 15
-#define CLK_PLL_VIDEO1_2X 16
+/* The PLL_VIDEO1_2X is exported for HDMI */
/* The CPU clock is exported */
#define CLK_AXI 18
diff --git a/include/dt-bindings/clock/sun5i-ccu.h b/include/dt-bindings/clock/sun5i-ccu.h
index aeb2e2f781fb..81f34d477aeb 100644
--- a/include/dt-bindings/clock/sun5i-ccu.h
+++ b/include/dt-bindings/clock/sun5i-ccu.h
@@ -19,6 +19,9 @@
#define CLK_HOSC 1
+#define CLK_PLL_VIDEO0_2X 9
+
+#define CLK_PLL_VIDEO1_2X 16
#define CLK_CPU 17
#define CLK_AHB_OTG 23
--
git-series 0.8.11
^ permalink raw reply related
* [PATCH v2 7/20] clk: sunxi-ng: mux: Re-adjust parent 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>
Currently, the parent rate given back to the clock framework in our
request is the original parent rate we calculated before trying to round
the rate of our clock.
This works fine unless our clock also changes its parent rate, in which
case we will simply ignore that change and still use the previous parent
rate.
Create a new function to re-adjust the parent rate to take the pre-dividers
into account, and give that back to the clock framework.
Signed-off-by: Maxime Ripard <maxime.ripard-wi1+55ScJUtKEb57/3fJTNBPR1lH4CV8@public.gmane.org>
---
drivers/clk/sunxi-ng/ccu_mux.c | 33 ++++++++++++++++++++++++++++-----
1 file changed, 28 insertions(+), 5 deletions(-)
diff --git a/drivers/clk/sunxi-ng/ccu_mux.c b/drivers/clk/sunxi-ng/ccu_mux.c
index c33210972581..1ce62cc23f8a 100644
--- a/drivers/clk/sunxi-ng/ccu_mux.c
+++ b/drivers/clk/sunxi-ng/ccu_mux.c
@@ -64,6 +64,14 @@ unsigned long ccu_mux_helper_apply_prediv(struct ccu_common *common,
return parent_rate / ccu_mux_get_prediv(common, cm, parent_index);
}
+unsigned long ccu_mux_helper_unapply_prediv(struct ccu_common *common,
+ struct ccu_mux_internal *cm,
+ int parent_index,
+ unsigned long parent_rate)
+{
+ return parent_rate * ccu_mux_get_prediv(common, cm, parent_index);
+}
+
int ccu_mux_helper_determine_rate(struct ccu_common *common,
struct ccu_mux_internal *cm,
struct clk_rate_request *req,
@@ -89,22 +97,37 @@ int ccu_mux_helper_determine_rate(struct ccu_common *common,
best_rate = round(cm, best_parent, &adj_parent_rate,
req->rate, data);
+ /*
+ * parent_rate might have been modified by our clock.
+ * Re-apply the pre-divider if there's one, and give
+ * the actual frequency the parent needs to run at.
+ */
+ best_parent_rate = ccu_mux_helper_unapply_prediv(common, cm, -1,
+ adj_parent_rate);
+
goto out;
}
for (i = 0; i < clk_hw_get_num_parents(hw); i++) {
- unsigned long tmp_rate, parent_rate, adj_parent_rate;
+ unsigned long tmp_rate, parent_rate;
struct clk_hw *parent;
parent = clk_hw_get_parent_by_index(hw, i);
if (!parent)
continue;
- parent_rate = clk_hw_get_rate(parent);
- adj_parent_rate = ccu_mux_helper_apply_prediv(common, cm, i,
- parent_rate);
+ parent_rate = ccu_mux_helper_apply_prediv(common, cm, i,
+ clk_hw_get_rate(parent));
+
+ tmp_rate = round(cm, parent, &parent_rate, req->rate, data);
- tmp_rate = round(cm, parent, &adj_parent_rate, req->rate, data);
+ /*
+ * parent_rate might have been modified by our clock.
+ * Re-apply the pre-divider if there's one, and give
+ * the actual frequency the parent needs to run at.
+ */
+ parent_rate = ccu_mux_helper_unapply_prediv(common, cm, i,
+ parent_rate);
if (tmp_rate == req->rate) {
best_parent = parent;
best_parent_rate = parent_rate;
--
git-series 0.8.11
^ permalink raw reply related
* [PATCH v2 6/20] clk: sunxi-ng: mux: Change pre-divider application function prototype
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 current function name is a bit confusing, and doesn't really allow to
create an explicit function to reverse the operation.
We also for now change the parent rate through a pointer, while we don't
return anything.
In order to be less confusing, and easier to use for downstream users,
change the function name to something hopefully clearer, and return the
adjusted rate instead of changing the pointer.
Signed-off-by: Maxime Ripard <maxime.ripard-wi1+55ScJUtKEb57/3fJTNBPR1lH4CV8@public.gmane.org>
---
drivers/clk/sunxi-ng/ccu_div.c | 8 ++++----
drivers/clk/sunxi-ng/ccu_mp.c | 8 ++++----
drivers/clk/sunxi-ng/ccu_mult.c | 8 ++++----
drivers/clk/sunxi-ng/ccu_mux.c | 29 ++++++++++++-----------------
drivers/clk/sunxi-ng/ccu_mux.h | 8 ++++----
5 files changed, 28 insertions(+), 33 deletions(-)
diff --git a/drivers/clk/sunxi-ng/ccu_div.c b/drivers/clk/sunxi-ng/ccu_div.c
index 419463375bc1..c0e5c10d0091 100644
--- a/drivers/clk/sunxi-ng/ccu_div.c
+++ b/drivers/clk/sunxi-ng/ccu_div.c
@@ -59,8 +59,8 @@ static unsigned long ccu_div_recalc_rate(struct clk_hw *hw,
val = reg >> cd->div.shift;
val &= (1 << cd->div.width) - 1;
- ccu_mux_helper_adjust_parent_for_prediv(&cd->common, &cd->mux, -1,
- &parent_rate);
+ parent_rate = ccu_mux_helper_apply_prediv(&cd->common, &cd->mux, -1,
+ parent_rate);
return divider_recalc_rate(hw, parent_rate, val, cd->div.table,
cd->div.flags);
@@ -83,8 +83,8 @@ static int ccu_div_set_rate(struct clk_hw *hw, unsigned long rate,
unsigned long val;
u32 reg;
- ccu_mux_helper_adjust_parent_for_prediv(&cd->common, &cd->mux, -1,
- &parent_rate);
+ parent_rate = ccu_mux_helper_apply_prediv(&cd->common, &cd->mux, -1,
+ parent_rate);
val = divider_get_val(rate, parent_rate, cd->div.table, cd->div.width,
cd->div.flags);
diff --git a/drivers/clk/sunxi-ng/ccu_mp.c b/drivers/clk/sunxi-ng/ccu_mp.c
index de02e6c386d8..b917ad7a386c 100644
--- a/drivers/clk/sunxi-ng/ccu_mp.c
+++ b/drivers/clk/sunxi-ng/ccu_mp.c
@@ -87,8 +87,8 @@ static unsigned long ccu_mp_recalc_rate(struct clk_hw *hw,
u32 reg;
/* Adjust parent_rate according to pre-dividers */
- ccu_mux_helper_adjust_parent_for_prediv(&cmp->common, &cmp->mux,
- -1, &parent_rate);
+ parent_rate = ccu_mux_helper_apply_prediv(&cmp->common, &cmp->mux, -1,
+ parent_rate);
reg = readl(cmp->common.base + cmp->common.reg);
@@ -123,8 +123,8 @@ static int ccu_mp_set_rate(struct clk_hw *hw, unsigned long rate,
u32 reg;
/* Adjust parent_rate according to pre-dividers */
- ccu_mux_helper_adjust_parent_for_prediv(&cmp->common, &cmp->mux,
- -1, &parent_rate);
+ parent_rate = ccu_mux_helper_apply_prediv(&cmp->common, &cmp->mux, -1,
+ parent_rate);
max_m = cmp->m.max ?: 1 << cmp->m.width;
max_p = cmp->p.max ?: 1 << ((1 << cmp->p.width) - 1);
diff --git a/drivers/clk/sunxi-ng/ccu_mult.c b/drivers/clk/sunxi-ng/ccu_mult.c
index 6ee7ba0738fb..20d0300867f2 100644
--- a/drivers/clk/sunxi-ng/ccu_mult.c
+++ b/drivers/clk/sunxi-ng/ccu_mult.c
@@ -88,8 +88,8 @@ static unsigned long ccu_mult_recalc_rate(struct clk_hw *hw,
val = reg >> cm->mult.shift;
val &= (1 << cm->mult.width) - 1;
- ccu_mux_helper_adjust_parent_for_prediv(&cm->common, &cm->mux, -1,
- &parent_rate);
+ parent_rate = ccu_mux_helper_apply_prediv(&cm->common, &cm->mux, -1,
+ parent_rate);
return parent_rate * (val + cm->mult.offset);
}
@@ -116,8 +116,8 @@ static int ccu_mult_set_rate(struct clk_hw *hw, unsigned long rate,
else
ccu_frac_helper_disable(&cm->common, &cm->frac);
- ccu_mux_helper_adjust_parent_for_prediv(&cm->common, &cm->mux, -1,
- &parent_rate);
+ parent_rate = ccu_mux_helper_apply_prediv(&cm->common, &cm->mux, -1,
+ parent_rate);
_cm.min = cm->mult.min;
diff --git a/drivers/clk/sunxi-ng/ccu_mux.c b/drivers/clk/sunxi-ng/ccu_mux.c
index 3eb23d4e6534..c33210972581 100644
--- a/drivers/clk/sunxi-ng/ccu_mux.c
+++ b/drivers/clk/sunxi-ng/ccu_mux.c
@@ -56,13 +56,12 @@ static u16 ccu_mux_get_prediv(struct ccu_common *common,
return prediv;
}
-void ccu_mux_helper_adjust_parent_for_prediv(struct ccu_common *common,
- struct ccu_mux_internal *cm,
- int parent_index,
- unsigned long *parent_rate)
+unsigned long ccu_mux_helper_apply_prediv(struct ccu_common *common,
+ struct ccu_mux_internal *cm,
+ int parent_index,
+ unsigned long parent_rate)
{
- *parent_rate = *parent_rate / ccu_mux_get_prediv(common, cm,
- parent_index);
+ return parent_rate / ccu_mux_get_prediv(common, cm, parent_index);
}
int ccu_mux_helper_determine_rate(struct ccu_common *common,
@@ -84,10 +83,8 @@ int ccu_mux_helper_determine_rate(struct ccu_common *common,
best_parent = clk_hw_get_parent(hw);
best_parent_rate = clk_hw_get_rate(best_parent);
-
- adj_parent_rate = best_parent_rate;
- ccu_mux_helper_adjust_parent_for_prediv(common, cm, -1,
- &adj_parent_rate);
+ adj_parent_rate = ccu_mux_helper_apply_prediv(common, cm, -1,
+ best_parent_rate);
best_rate = round(cm, best_parent, &adj_parent_rate,
req->rate, data);
@@ -103,9 +100,9 @@ int ccu_mux_helper_determine_rate(struct ccu_common *common,
if (!parent)
continue;
- adj_parent_rate = parent_rate = clk_hw_get_rate(parent);
- ccu_mux_helper_adjust_parent_for_prediv(common, cm, i,
- &adj_parent_rate);
+ parent_rate = clk_hw_get_rate(parent);
+ adj_parent_rate = ccu_mux_helper_apply_prediv(common, cm, i,
+ parent_rate);
tmp_rate = round(cm, parent, &adj_parent_rate, req->rate, data);
if (tmp_rate == req->rate) {
@@ -215,10 +212,8 @@ static unsigned long ccu_mux_recalc_rate(struct clk_hw *hw,
{
struct ccu_mux *cm = hw_to_ccu_mux(hw);
- ccu_mux_helper_adjust_parent_for_prediv(&cm->common, &cm->mux, -1,
- &parent_rate);
-
- return parent_rate;
+ return ccu_mux_helper_apply_prediv(&cm->common, &cm->mux, -1,
+ parent_rate);
}
const struct clk_ops ccu_mux_ops = {
diff --git a/drivers/clk/sunxi-ng/ccu_mux.h b/drivers/clk/sunxi-ng/ccu_mux.h
index 4be56eee2bfd..dba12c76cf54 100644
--- a/drivers/clk/sunxi-ng/ccu_mux.h
+++ b/drivers/clk/sunxi-ng/ccu_mux.h
@@ -78,10 +78,10 @@ static inline struct ccu_mux *hw_to_ccu_mux(struct clk_hw *hw)
extern const struct clk_ops ccu_mux_ops;
-void ccu_mux_helper_adjust_parent_for_prediv(struct ccu_common *common,
- struct ccu_mux_internal *cm,
- int parent_index,
- unsigned long *parent_rate);
+unsigned long ccu_mux_helper_apply_prediv(struct ccu_common *common,
+ struct ccu_mux_internal *cm,
+ int parent_index,
+ unsigned long parent_rate);
int ccu_mux_helper_determine_rate(struct ccu_common *common,
struct ccu_mux_internal *cm,
struct clk_rate_request *req,
--
git-series 0.8.11
^ permalink raw reply related
* [PATCH v2 5/20] clk: sunxi-ng: mux: split out the pre-divider computation code
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 pre-divider retrieval code was merged into the function to apply the
current pre-divider onto the parent clock rate so that we can use that
adjusted value to do our factors computation.
However, since we'll need to do the reverse operation, we need to split out
that code into a function that will be shared.
Signed-off-by: Maxime Ripard <maxime.ripard-wi1+55ScJUtKEb57/3fJTNBPR1lH4CV8@public.gmane.org>
---
drivers/clk/sunxi-ng/ccu_mux.c | 32 ++++++++++++++++++++------------
1 file changed, 20 insertions(+), 12 deletions(-)
diff --git a/drivers/clk/sunxi-ng/ccu_mux.c b/drivers/clk/sunxi-ng/ccu_mux.c
index 58b6e349a0ed..3eb23d4e6534 100644
--- a/drivers/clk/sunxi-ng/ccu_mux.c
+++ b/drivers/clk/sunxi-ng/ccu_mux.c
@@ -15,24 +15,20 @@
#include "ccu_gate.h"
#include "ccu_mux.h"
-void ccu_mux_helper_adjust_parent_for_prediv(struct ccu_common *common,
- struct ccu_mux_internal *cm,
- int parent_index,
- unsigned long *parent_rate)
+static u16 ccu_mux_get_prediv(struct ccu_common *common,
+ struct ccu_mux_internal *cm,
+ int parent_index)
{
u16 prediv = 1;
u32 reg;
- int i;
if (!((common->features & CCU_FEATURE_FIXED_PREDIV) ||
(common->features & CCU_FEATURE_VARIABLE_PREDIV) ||
(common->features & CCU_FEATURE_ALL_PREDIV)))
- return;
+ return 1;
- if (common->features & CCU_FEATURE_ALL_PREDIV) {
- *parent_rate = *parent_rate / common->prediv;
- return;
- }
+ if (common->features & CCU_FEATURE_ALL_PREDIV)
+ return common->prediv;
reg = readl(common->base + common->reg);
if (parent_index < 0) {
@@ -40,10 +36,13 @@ void ccu_mux_helper_adjust_parent_for_prediv(struct ccu_common *common,
parent_index &= (1 << cm->width) - 1;
}
- if (common->features & CCU_FEATURE_FIXED_PREDIV)
+ if (common->features & CCU_FEATURE_FIXED_PREDIV) {
+ int i;
+
for (i = 0; i < cm->n_predivs; i++)
if (parent_index == cm->fixed_predivs[i].index)
prediv = cm->fixed_predivs[i].div;
+ }
if (common->features & CCU_FEATURE_VARIABLE_PREDIV)
if (parent_index == cm->variable_prediv.index) {
@@ -54,7 +53,16 @@ void ccu_mux_helper_adjust_parent_for_prediv(struct ccu_common *common,
prediv = div + 1;
}
- *parent_rate = *parent_rate / prediv;
+ return prediv;
+}
+
+void ccu_mux_helper_adjust_parent_for_prediv(struct ccu_common *common,
+ struct ccu_mux_internal *cm,
+ int parent_index,
+ unsigned long *parent_rate)
+{
+ *parent_rate = *parent_rate / ccu_mux_get_prediv(common, cm,
+ parent_index);
}
int ccu_mux_helper_determine_rate(struct ccu_common *common,
--
git-series 0.8.11
^ permalink raw reply related
* [PATCH v2 4/20] clk: sunxi-ng: mux: Don't just rely on the parent for CLK_SET_RATE_PARENT
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 current code only rely on the parent to change its rate in the case
where CLK_SET_RATE_PARENT is set.
However, some clock rates might be obtained only through a modification of
the parent and the clock divider. Just rely on the round rate of the clocks
to give us the best computation that might be achieved for a given rate.
round_rate functions now need to honor CLK_SET_RATE_PARENT, but either the
functions already do that if they modify the parent, or don't modify the
praents at all.
Signed-off-by: Maxime Ripard <maxime.ripard-wi1+55ScJUtKEb57/3fJTNBPR1lH4CV8@public.gmane.org>
---
drivers/clk/sunxi-ng/ccu_mux.c | 14 +-------------
1 file changed, 1 insertion(+), 13 deletions(-)
diff --git a/drivers/clk/sunxi-ng/ccu_mux.c b/drivers/clk/sunxi-ng/ccu_mux.c
index bae735e252b6..58b6e349a0ed 100644
--- a/drivers/clk/sunxi-ng/ccu_mux.c
+++ b/drivers/clk/sunxi-ng/ccu_mux.c
@@ -95,19 +95,7 @@ int ccu_mux_helper_determine_rate(struct ccu_common *common,
if (!parent)
continue;
- if (clk_hw_get_flags(hw) & CLK_SET_RATE_PARENT) {
- struct clk_rate_request parent_req = *req;
- int ret = __clk_determine_rate(parent, &parent_req);
-
- if (ret)
- continue;
-
- parent_rate = parent_req.rate;
- } else {
- parent_rate = clk_hw_get_rate(parent);
- }
-
- adj_parent_rate = parent_rate;
+ adj_parent_rate = parent_rate = clk_hw_get_rate(parent);
ccu_mux_helper_adjust_parent_for_prediv(common, cm, i,
&adj_parent_rate);
--
git-series 0.8.11
^ permalink raw reply related
* [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
page: next (older) | prev (newer) | latest
- recent:[subjects (threaded)|topics (new)|topics (active)]
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox