Linux-ARM-Kernel Archive on lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH 2/2] mmc: sdhci-iproc: Increase max_blk_size for bcm2835
From: Stefan Wahren @ 2016-12-30 15:24 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <1483111474-29907-1-git-send-email-stefan.wahren@i2se.com>

According to the BCM2835 datasheet the maximum block size for the
eMMC module is restricted to the internal data FIFO which is 1024 byte.
But this is still an improvement to the default of 512 byte.

Signed-off-by: Stefan Wahren <stefan.wahren@i2se.com>
---
 drivers/mmc/host/sdhci-iproc.c |    4 +++-
 1 file changed, 3 insertions(+), 1 deletion(-)

diff --git a/drivers/mmc/host/sdhci-iproc.c b/drivers/mmc/host/sdhci-iproc.c
index 30b3fdf..3275d49 100644
--- a/drivers/mmc/host/sdhci-iproc.c
+++ b/drivers/mmc/host/sdhci-iproc.c
@@ -218,7 +218,9 @@ static void sdhci_iproc_writeb(struct sdhci_host *host, u8 val, int reg)
 
 static const struct sdhci_iproc_data bcm2835_data = {
 	.pdata = &sdhci_bcm2835_pltfm_data,
-	.caps = SDHCI_CAN_VDD_330 |
+	.caps = ((0x1 << SDHCI_MAX_BLOCK_SHIFT)
+			& SDHCI_MAX_BLOCK_MASK) |
+		SDHCI_CAN_VDD_330 |
 		SDHCI_CAN_DO_HISPD,
 	.caps1 = SDHCI_DRIVER_TYPE_A |
 		 SDHCI_DRIVER_TYPE_C,
-- 
1.7.9.5

^ permalink raw reply related

* [PATCH 1/2] mmc: sdhci-iproc: Apply caps from bcm2835-mmc driver
From: Stefan Wahren @ 2016-12-30 15:24 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <1483111474-29907-1-git-send-email-stefan.wahren@i2se.com>

Since the mmc module on bcm2835 neither provide a capabilities register nor 
free documentation we must rely on the downstream implementation [1].

So enable the following capabilities for bcm2835:

MMC_CAP_MMC_HIGHSPEED
MMC_CAP_SD_HIGHSPEED
MMC_CAP_DRIVER_TYPE_A
MMC_CAP_DRIVER_TYPE_C

[1] - https://github.com/raspberrypi/linux/blob/rpi-4.4.y/drivers/mmc/host/bcm2835-mmc.c

Signed-off-by: Stefan Wahren <stefan.wahren@i2se.com>
---
 drivers/mmc/host/sdhci-iproc.c |    9 ++++++---
 1 file changed, 6 insertions(+), 3 deletions(-)

diff --git a/drivers/mmc/host/sdhci-iproc.c b/drivers/mmc/host/sdhci-iproc.c
index d7046d6..30b3fdf 100644
--- a/drivers/mmc/host/sdhci-iproc.c
+++ b/drivers/mmc/host/sdhci-iproc.c
@@ -211,14 +211,17 @@ static void sdhci_iproc_writeb(struct sdhci_host *host, u8 val, int reg)
 static const struct sdhci_pltfm_data sdhci_bcm2835_pltfm_data = {
 	.quirks = SDHCI_QUIRK_BROKEN_CARD_DETECTION |
 		  SDHCI_QUIRK_DATA_TIMEOUT_USES_SDCLK |
-		  SDHCI_QUIRK_MISSING_CAPS,
+		  SDHCI_QUIRK_MISSING_CAPS |
+		  SDHCI_QUIRK_NO_HISPD_BIT,
 	.ops = &sdhci_iproc_32only_ops,
 };
 
 static const struct sdhci_iproc_data bcm2835_data = {
 	.pdata = &sdhci_bcm2835_pltfm_data,
-	.caps = SDHCI_CAN_VDD_330,
-	.caps1 = 0x00000000,
+	.caps = SDHCI_CAN_VDD_330 |
+		SDHCI_CAN_DO_HISPD,
+	.caps1 = SDHCI_DRIVER_TYPE_A |
+		 SDHCI_DRIVER_TYPE_C,
 	.mmc_caps = 0x00000000,
 };
 
-- 
1.7.9.5

^ permalink raw reply related

* [PATCH 0/2] mmc: sdhci-iproc: Improve bcm2835 performance
From: Stefan Wahren @ 2016-12-30 15:24 UTC (permalink / raw)
  To: linux-arm-kernel

The sdhci-iproc waste a lot performance potential on bcm2835 because of
missing capabilities in the platform data. This patch series tries to fix
this.

Raspberry Pi Zero with a class 10 sdhc card

Before:
dd if=/dev/zero conv=fdatasync of=test bs=8k count=10000
81920000 Bytes (82 MB), 11,0044 s, 7,4 MB/s

sudo dd if=/dev/mmcblk0p1 of=/dev/null
62914560 Bytes (63 MB), 5,83784 s, 10,8 MB/s

After:
dd if=/dev/zero conv=fdatasync of=test bs=8k count=10000
81920000 Bytes (82 MB), 9,76938 s, 8,4 MB/s

sudo dd if=/dev/mmcblk0p1 of=/dev/null
62914560 Bytes (63 MB), 4,73651 s, 13,3 MB/s

Raspberry Pi Compute Module

Before:
dd if=/dev/zero conv=fdatasync of=test bs=8k count=10000
81920000 Bytes (82 MB), 27,4257 s, 3,0 MB/s

sudo dd if=/dev/mmcblk0p1 of=/dev/null
66060288 Bytes (66 MB), 21,4365 s, 3,1 MB/s

After:
dd if=/dev/zero conv=fdatasync of=test bs=8k count=10000
81920000 Bytes (82 MB), 7,19661 s, 11,4 MB/s

sudo dd if=/dev/mmcblk0p1 of=/dev/null
66060288 Bytes (66 MB), 4,76453 s, 13,9 MB/s

Any tests with a Raspberry Pi 3 (SD and Wifi over SDIO) are welcome.

Stefan Wahren (2):
  mmc: sdhci-iproc: Apply caps from bcm2835-mmc driver
  mmc: sdhci-iproc: Increase max_blk_size for bcm2835

 drivers/mmc/host/sdhci-iproc.c |   11 ++++++++---
 1 file changed, 8 insertions(+), 3 deletions(-)

-- 
1.7.9.5

^ permalink raw reply

* [PATCH v2 1/6] ARM: mach-mx31_3ds: Remove camera support
From: Magnus Lilja @ 2016-12-30 15:19 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <CAOMZO5BGkPa86cag==6cN2sPr+ukTDsw4DSLyuog22WOCOX1Pw@mail.gmail.com>

On 30 December 2016 at 16:15, Fabio Estevam <festevam@gmail.com> wrote:
> Hi Magnus,
>
> On Fri, Dec 30, 2016 at 1:14 PM, Magnus Lilja <lilja.magnus@gmail.com> wrote:
>
>> v2 also runs nicely on the i.MX31 PDK hardware.
>
> Thanks for testing.
>
> Care to send your Tested-by tag?

Tested-by: Magnus Lilja <lilja.magnus@gmail.com>

/Magnus

^ permalink raw reply

* [PATCH v2 3/4] ARM64: dts: exynos5433: use macros for pinctrl configuration on Exynos5433
From: Krzysztof Kozlowski @ 2016-12-30 15:17 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <CACRpkdaz9AyFJs1Th_SVexqKaP-=iPF0G-1YXOCoSmrQH8ZHDA@mail.gmail.com>

On Fri, Dec 30, 2016 at 02:32:39PM +0100, Linus Walleij wrote:
> On Fri, Dec 30, 2016 at 5:14 AM, Andi Shyti <andi.shyti@samsung.com> wrote:
> 
> > Use the macros defined in include/dt-bindings/pinctrl/samsung.h
> > instead of hardcoded values.
> >
> > Signed-off-by: Andi Shyti <andi.shyti@samsung.com>
> 
> These look fine, but that this and the other DTS patch through ARM SoC.
> 
> If you also need the headerfile patch (patch 2) to go through ARM SoC
> that is fine,
> I can take it out of pinctrl if you want.

Yes, I need the header. It would be much appreciated if you could
provide a tag or stable branch with it.

BTW, Andi, please follow the subject prefix convention (git log
--oneline arch/arm64/boot/dts/exynos).

Best regards,
Krzysztof

^ permalink raw reply

* [PATCH v2 1/6] ARM: mach-mx31_3ds: Remove camera support
From: Fabio Estevam @ 2016-12-30 15:15 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <CAM=E1R71EYzGOxfDVVpXJ1gP-EUZ=mxn60-_PAh9diHhw1thvg@mail.gmail.com>

Hi Magnus,

On Fri, Dec 30, 2016 at 1:14 PM, Magnus Lilja <lilja.magnus@gmail.com> wrote:

> v2 also runs nicely on the i.MX31 PDK hardware.

Thanks for testing.

Care to send your Tested-by tag?

^ permalink raw reply

* [PATCH v2 1/6] ARM: mach-mx31_3ds: Remove camera support
From: Magnus Lilja @ 2016-12-30 15:14 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <1483097613-21481-1-git-send-email-festevam@gmail.com>

On 30 December 2016 at 12:33, Fabio Estevam <festevam@gmail.com> wrote:
> From: Fabio Estevam <fabio.estevam@nxp.com>
>
> Since commit c93cc61475ebbe6e66 ("[media] staging/media: remove deprecated
> mx3 driver") the mx3 camera driver has been removed, so remove the camera
> support from the board file as well.
>
> Signed-off-by: Fabio Estevam <fabio.estevam@nxp.com>
> ---
> Changes since v1:
> - Also remove CSI pins from IOMUX definition

v2 also runs nicely on the i.MX31 PDK hardware.

/Magnus

^ permalink raw reply

* [PATCH v2 1/4] pinctrl: samsung: Fix the width of PINCFG_TYPE_DRV bitfields for Exynos5433
From: Krzysztof Kozlowski @ 2016-12-30 15:09 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <CACRpkdbR3x14h38Gg7qpeWnswwD9qsS0zDUrrXCEJH-AdEB+cQ@mail.gmail.com>

On Fri, Dec 30, 2016 at 02:28:52PM +0100, Linus Walleij wrote:
> On Fri, Dec 30, 2016 at 5:14 AM, Andi Shyti <andi.shyti@samsung.com> wrote:
> 
> > From: Chanwoo Choi <cw00.choi@samsung.com>
> >
> > This patch fixes the wrong width of PINCFG_TYPE_DRV bitfields for Exynos5433
> > because PINCFG_TYPE_DRV of Exynos5433 has 4bit fields in the *_DRV
> > registers. Usually, other Exynos have 2bit field for PINCFG_TYPE_DRV.
> >
> > Fixes: 3c5ecc9ed353 ("pinctrl: exynos: Add support for Exynos5433")
> > Cc: stable at vger.kernel.org
> > Cc: Tomasz Figa <tomasz.figa@gmail.com>
> > Cc: Krzysztof Kozlowski <krzk@kernel.org>
> > Cc: Sylwester Nawrocki <s.nawrocki@samsung.com>
> > Cc: Linus Walleij <linus.walleij@linaro.org>
> > Cc: Kukjin Kim <kgene@kernel.org>
> > Cc: Javier Martinez Canillas <javier@osg.samsung.com>
> > Signed-off-by: Chanwoo Choi <cw00.choi@samsung.com>
> 
> Nominally I think you should sign this off too Andi, as you are in the delivery
> path.
> 
> Patch applied for fixes.

That has to be signed by Andi... otherwise the chain is broken (and
there could be changes added inside).

Best regards,
Krzysztof

^ permalink raw reply

* [PATCH v2 4/5] arm64: dts: exynos5433: Add bus dt node using VDD_INT for Exynos5433
From: Chanwoo Choi @ 2016-12-30 15:08 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <20161230145155.ecobfvtspviehlos@kozik-lap>

2016-12-30 23:51 GMT+09:00 Krzysztof Kozlowski <krzk@kernel.org>:
> On Fri, Dec 30, 2016 at 09:59:17AM +0900, Chanwoo Choi wrote:
>> Hi Krzysztof,
>>
>> On 2016? 12? 09? 02:52, Krzysztof Kozlowski wrote:
>> > On Thu, Dec 08, 2016 at 01:58:10PM +0900, Chanwoo Choi wrote:
>> >> This patch adds the bus nodes using VDD_INT for Exynos5433 SoC.
>> >> Exynos5433 has the following AMBA AXI buses to translate data
>> >> between DRAM and sub-blocks.
>> >>
>> >> Following list specify the detailed correlation between sub-block and clock:
>> >> - CLK_ACLK_G2D_{400|266}  : Bus clock for G2D (2D graphic engine)
>> >> - CLK_ACLK_MSCL_400       : Bus clock for MSCL (Memory to memory Scaler)
>> >> - CLK_ACLK_GSCL_333       : Bus clock for GSCL (General Scaler)
>> >> - CLK_SCLK_JPEG_MSCL      : Bus clock for JPEG
>> >> - CLK_ACLK_MFC_400        : Bus clock for MFC (Multi Format Codec)
>> >> - CLK_ACLK_HEVC_400       : Bus clock for HEVC (High Efficient Video Codec)
>> >> - CLK_ACLK_BUS0_400       : NoC(Network On Chip)'s bus clock for PERIC/PERIS/FSYS/MSCL
>> >> - CLK_ACLK_BUS1_400       : NoC's bus clock for MFC/HEVC/G3D
>> >> - CLK_ACLK_BUS2_400       : NoC's bus clock for GSCL/DISP/G2D/CAM0/CAM1/ISP
>> >>
>> >> Signed-off-by: Chanwoo Choi <cw00.choi@samsung.com>
>> >> ---
>> >>  arch/arm64/boot/dts/exynos/exynos5433-bus.dtsi | 197 +++++++++++++++++++++++++
>> >>  arch/arm64/boot/dts/exynos/exynos5433.dtsi     |   1 +
>> >>  2 files changed, 198 insertions(+)
>> >>  create mode 100644 arch/arm64/boot/dts/exynos/exynos5433-bus.dtsi
>> >
>> > For the reference:
>> > Reviewed-by: Krzysztof Kozlowski <krzk@kernel.org>
>> >
>> > I'll queue it for v4.11, after this merge window.
>>
>> Could you please pick this patch3/4/5?
>> These patches were already reviewed by you.
>
> Not yet. I wanted to apply them few days ago but arm64 build is broken
> in 4.10-rc1 so I cannot auto-build them in my system. The arm64 is fixed
> already so I will apply them on top of 4.10-rc2 (when released).

OK. Thanks for reply.

-- 
Best Regards,
Chanwoo Choi

^ permalink raw reply

* [PATCH 7/9] pinctrl: samsung: Add property to mark pad state as suitable for power down
From: Krzysztof Kozlowski @ 2016-12-30 15:05 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <bad5ef6a-6132-2029-8581-2e8b27f7a2bd@samsung.com>

On Fri, Dec 30, 2016 at 12:55:27PM +0100, Marek Szyprowski wrote:
> Hi Krzysztof,
> 
> On 2016-12-27 16:33, Krzysztof Kozlowski wrote:
> > On Tue, Dec 27, 2016 at 11:30:57AM +0100, Marek Szyprowski wrote:
> > > On 2016-12-25 20:19, Krzysztof Kozlowski wrote:
> > > > On Fri, Dec 23, 2016 at 01:24:47PM +0100, Marek Szyprowski wrote:
> > > > > Add support for special property "samsung,off-state", which indicates a special
> > > > > state suitable for device's "sleep" state. Its pin values/properties should
> > > > > match the configuration in power down mode. It indicates that pin controller
> > > > > can notify runtime power management subsystem, that it is ready for runtime
> > > > > suspend if its all pins are configured for such state. This in turn might
> > > > > allow to turn respective power domain off to reduce power consumption.
> > > > > 
> > > > > Signed-off-by: Marek Szyprowski <m.szyprowski@samsung.com>
> > > > > ---
> > > > >    Documentation/devicetree/bindings/pinctrl/samsung-pinctrl.txt | 8 ++++++++
> > > > >    drivers/pinctrl/samsung/pinctrl-samsung.c                     | 4 ++++
> > > > >    drivers/pinctrl/samsung/pinctrl-samsung.h                     | 1 +
> > > > >    3 files changed, 13 insertions(+)
> > > > > 
> > > > > diff --git a/Documentation/devicetree/bindings/pinctrl/samsung-pinctrl.txt b/Documentation/devicetree/bindings/pinctrl/samsung-pinctrl.txt
> > > > > index b7bd2e12a269..354eea0e7798 100644
> > > > > --- a/Documentation/devicetree/bindings/pinctrl/samsung-pinctrl.txt
> > > > > +++ b/Documentation/devicetree/bindings/pinctrl/samsung-pinctrl.txt
> > > > > @@ -105,6 +105,7 @@ Required Properties:
> > > > >      - samsung,pin-drv: Drive strength configuration.
> > > > >      - samsung,pin-pud-pdn: Pull up/down configuration in power down mode.
> > > > >      - samsung,pin-drv-pdn: Drive strength configuration in power down mode.
> > > > > +  - samsung,off-state: Mark this configuration as suitable for bank power off.
> > > > >      The values specified by these config properties should be derived from the
> > > > >      hardware manual and these values are programmed as-is into the pin
> > > > > @@ -113,6 +114,13 @@ Required Properties:
> > > > >      Note: A child should include atleast a pin function selection property or
> > > > >      pin configuration property (one or more) or both.
> > > > > +  Note: Special property "samsung,off-state" indicates that this state can
> > > > > +  be used for device's "sleep" pins state. Its pin values/properties should
> > > > > +  match the configuration in power down mode.
> > > > Why power down values cannot be used for sleep state? Why you need
> > > > separate pin control state? If pins values should match power down
> > > > configuration, then they could just be added to default state, couldn't
> > > > they?
> > > Separate sleep state is needed because of the pin control bindings and
> > > design.
> > > 
> > > A separate sleep state is required to let pin control client driver (in this
> > > case
> > > Exynos I2S driver) let to choose when it is okay to switch pads into sleep
> > > state and I see no other way to achieve this.
> > Maybe the pinctrl API should be extended for this? Doing this in DTS
> > just for purpose of passing information between drivers (consumer and
> > provider) looks odd.
> 
> Well, I don't know if it is odd or not, but that's how it is used now and I
> see
> no reason to reinvent wheel. Please check it yourself:
> $ git grep \"sleep\" arch/arm/boot/dts | wc -l
> 101

These drivers, at least not all of them, are not using the existence of
sleep mode configuration as a indication of possible runtime sleep. You
are mixing here different ideas.

> 
> > Anyway, you are proposing a new binding so please Cc devicetree mailing
> > list and device tree maintainers.
> 
> I'm just using the generic pinctrl bindings, but it might make some sense to
> add a note to Exynos i2s driver that a sleep pin control state is needed if
> one wants to get power domain to be turned off.

The samsung,off-state is a extension of the existing binding, so please
Cc the devicetree and maintainers. Why you see a problem in it?

Best regards,
Krzysztof

^ permalink raw reply

* [PATCH v2 4/5] arm64: dts: exynos5433: Add bus dt node using VDD_INT for Exynos5433
From: Krzysztof Kozlowski @ 2016-12-30 14:51 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <5865B165.3010203@samsung.com>

On Fri, Dec 30, 2016 at 09:59:17AM +0900, Chanwoo Choi wrote:
> Hi Krzysztof,
> 
> On 2016? 12? 09? 02:52, Krzysztof Kozlowski wrote:
> > On Thu, Dec 08, 2016 at 01:58:10PM +0900, Chanwoo Choi wrote:
> >> This patch adds the bus nodes using VDD_INT for Exynos5433 SoC.
> >> Exynos5433 has the following AMBA AXI buses to translate data
> >> between DRAM and sub-blocks.
> >>
> >> Following list specify the detailed correlation between sub-block and clock:
> >> - CLK_ACLK_G2D_{400|266}  : Bus clock for G2D (2D graphic engine)
> >> - CLK_ACLK_MSCL_400       : Bus clock for MSCL (Memory to memory Scaler)
> >> - CLK_ACLK_GSCL_333       : Bus clock for GSCL (General Scaler)
> >> - CLK_SCLK_JPEG_MSCL      : Bus clock for JPEG
> >> - CLK_ACLK_MFC_400        : Bus clock for MFC (Multi Format Codec)
> >> - CLK_ACLK_HEVC_400       : Bus clock for HEVC (High Efficient Video Codec)
> >> - CLK_ACLK_BUS0_400       : NoC(Network On Chip)'s bus clock for PERIC/PERIS/FSYS/MSCL
> >> - CLK_ACLK_BUS1_400       : NoC's bus clock for MFC/HEVC/G3D
> >> - CLK_ACLK_BUS2_400       : NoC's bus clock for GSCL/DISP/G2D/CAM0/CAM1/ISP
> >>
> >> Signed-off-by: Chanwoo Choi <cw00.choi@samsung.com>
> >> ---
> >>  arch/arm64/boot/dts/exynos/exynos5433-bus.dtsi | 197 +++++++++++++++++++++++++
> >>  arch/arm64/boot/dts/exynos/exynos5433.dtsi     |   1 +
> >>  2 files changed, 198 insertions(+)
> >>  create mode 100644 arch/arm64/boot/dts/exynos/exynos5433-bus.dtsi
> > 
> > For the reference:
> > Reviewed-by: Krzysztof Kozlowski <krzk@kernel.org>
> > 
> > I'll queue it for v4.11, after this merge window.
> 
> Could you please pick this patch3/4/5?
> These patches were already reviewed by you.

Not yet. I wanted to apply them few days ago but arm64 build is broken
in 4.10-rc1 so I cannot auto-build them in my system. The arm64 is fixed
already so I will apply them on top of 4.10-rc2 (when released).

Best regards,
Krzysztof

^ permalink raw reply

* [PATCH v2] ARM64: dts: meson-gx: Add reserved memory zone and usable memory range
From: Heinrich Schuchardt @ 2016-12-30 14:51 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <1483105232-6242-1-git-send-email-narmstrong@baylibre.com>

On 12/30/2016 02:40 PM, Neil Armstrong wrote:
> The Amlogic Meson GXBB/GXL/GXM secure monitor uses part of the memory space,
> this patch adds this reserved zone and redefines the usable memory range.
> 
> Signed-off-by: Neil Armstrong <narmstrong@baylibre.com>
> ---
> Changes since v1 at [1] :
>  - Renamed reg into linux,usable-memory to ovveride u-boot memory
>  - only kept secmon memory zone
> 
>  [1] http://lkml.kernel.org/r/20161212101801.28491-1-narmstrong at baylibre.com
> 
>  arch/arm64/boot/dts/amlogic/meson-gx-p23x-q20x.dtsi       |  2 +-
>  arch/arm64/boot/dts/amlogic/meson-gx.dtsi                 | 12 ++++++++++++
>  arch/arm64/boot/dts/amlogic/meson-gxbb-nexbox-a95x.dts    |  2 +-
>  arch/arm64/boot/dts/amlogic/meson-gxbb-odroidc2.dts       |  2 +-
>  arch/arm64/boot/dts/amlogic/meson-gxbb-p20x.dtsi          |  2 +-
>  arch/arm64/boot/dts/amlogic/meson-gxbb-vega-s95-meta.dts  |  2 +-
>  arch/arm64/boot/dts/amlogic/meson-gxbb-vega-s95-pro.dts   |  2 +-
>  arch/arm64/boot/dts/amlogic/meson-gxbb-vega-s95-telos.dts |  2 +-
>  arch/arm64/boot/dts/amlogic/meson-gxl-nexbox-a95x.dts     |  2 +-
>  arch/arm64/boot/dts/amlogic/meson-gxl-s905x-p212.dts      |  2 +-
>  arch/arm64/boot/dts/amlogic/meson-gxm-nexbox-a1.dts       |  2 +-
>  11 files changed, 22 insertions(+), 10 deletions(-)
> 
> diff --git a/arch/arm64/boot/dts/amlogic/meson-gx-p23x-q20x.dtsi b/arch/arm64/boot/dts/amlogic/meson-gx-p23x-q20x.dtsi
> index 7a078be..ca3c7fa 100644
> --- a/arch/arm64/boot/dts/amlogic/meson-gx-p23x-q20x.dtsi
> +++ b/arch/arm64/boot/dts/amlogic/meson-gx-p23x-q20x.dtsi
> @@ -56,7 +56,7 @@
>  
>  	memory at 0 {
>  		device_type = "memory";
> -		reg = <0x0 0x0 0x0 0x80000000>;
> +		linux,usable-memory = <0x0 0x1000000 0x0 0x7f000000>;
>  	};
>  
>  	vddio_boot: regulator-vddio_boot {
> diff --git a/arch/arm64/boot/dts/amlogic/meson-gx.dtsi b/arch/arm64/boot/dts/amlogic/meson-gx.dtsi
> index eada0b5..7f244b5 100644
> --- a/arch/arm64/boot/dts/amlogic/meson-gx.dtsi
> +++ b/arch/arm64/boot/dts/amlogic/meson-gx.dtsi
> @@ -55,6 +55,18 @@
>  	#address-cells = <2>;
>  	#size-cells = <2>;
>  
> +	reserved-memory {
> +		#address-cells = <2>;
> +		#size-cells = <2>;
> +		ranges;
> +
> +		/* global autoconfigured region for contiguous allocations */

This comment does not make sense here. It is what you would write over a
compatible to "shared-dma-pool" region. Cf. hi6220-hikey.dts

I suggest you use
/* Amlogic Meson GXBB/GXL/GXM secure monitor reserved memory */
instead.

Doesn't firmware/meson/meson_sm.c already reserve a communication area
to secmon with quite a different address range?
So where is this new region secmon set up? And where is it used?

Best regards

Heinrich

> +		secmon: secmon {
> +			reg = <0x0 0x10000000 0x0 0x200000>;
> +			no-map;
> +		};
> +	};
> +
>  	cpus {
>  		#address-cells = <0x2>;
>  		#size-cells = <0x0>;
> diff --git a/arch/arm64/boot/dts/amlogic/meson-gxbb-nexbox-a95x.dts b/arch/arm64/boot/dts/amlogic/meson-gxbb-nexbox-a95x.dts
> index 4cbd626..c7f008a 100644
> --- a/arch/arm64/boot/dts/amlogic/meson-gxbb-nexbox-a95x.dts
> +++ b/arch/arm64/boot/dts/amlogic/meson-gxbb-nexbox-a95x.dts
> @@ -62,7 +62,7 @@
>  
>  	memory at 0 {
>  		device_type = "memory";
> -		reg = <0x0 0x0 0x0 0x40000000>;
> +		linux,usable-memory = <0x0 0x1000000 0x0 0x3f000000>;
>  	};
>  
>  	leds {
> diff --git a/arch/arm64/boot/dts/amlogic/meson-gxbb-odroidc2.dts b/arch/arm64/boot/dts/amlogic/meson-gxbb-odroidc2.dts
> index 238fbea..546cbe4 100644
> --- a/arch/arm64/boot/dts/amlogic/meson-gxbb-odroidc2.dts
> +++ b/arch/arm64/boot/dts/amlogic/meson-gxbb-odroidc2.dts
> @@ -61,7 +61,7 @@
>  
>  	memory at 0 {
>  		device_type = "memory";
> -		reg = <0x0 0x0 0x0 0x80000000>;
> +		linux,usable-memory = <0x0 0x1000000 0x0 0x7f000000>;
>  	};
>  
>  	usb_otg_pwr: regulator-usb-pwrs {
> diff --git a/arch/arm64/boot/dts/amlogic/meson-gxbb-p20x.dtsi b/arch/arm64/boot/dts/amlogic/meson-gxbb-p20x.dtsi
> index 4a96e0f..1fdf6da 100644
> --- a/arch/arm64/boot/dts/amlogic/meson-gxbb-p20x.dtsi
> +++ b/arch/arm64/boot/dts/amlogic/meson-gxbb-p20x.dtsi
> @@ -55,7 +55,7 @@
>  
>  	memory at 0 {
>  		device_type = "memory";
> -		reg = <0x0 0x0 0x0 0x40000000>;
> +		linux,usable-memory = <0x0 0x1000000 0x0 0x3f000000>;
>  	};
>  
>  	usb_pwr: regulator-usb-pwrs {
> diff --git a/arch/arm64/boot/dts/amlogic/meson-gxbb-vega-s95-meta.dts b/arch/arm64/boot/dts/amlogic/meson-gxbb-vega-s95-meta.dts
> index 62fb496..6ac5c89 100644
> --- a/arch/arm64/boot/dts/amlogic/meson-gxbb-vega-s95-meta.dts
> +++ b/arch/arm64/boot/dts/amlogic/meson-gxbb-vega-s95-meta.dts
> @@ -50,6 +50,6 @@
>  
>  	memory at 0 {
>  		device_type = "memory";
> -		reg = <0x0 0x0 0x0 0x80000000>;
> +		linux,usable-memory = <0x0 0x1000000 0x0 0x7f000000>;
>  	};
>  };
> diff --git a/arch/arm64/boot/dts/amlogic/meson-gxbb-vega-s95-pro.dts b/arch/arm64/boot/dts/amlogic/meson-gxbb-vega-s95-pro.dts
> index 9a9663a..58be8b4 100644
> --- a/arch/arm64/boot/dts/amlogic/meson-gxbb-vega-s95-pro.dts
> +++ b/arch/arm64/boot/dts/amlogic/meson-gxbb-vega-s95-pro.dts
> @@ -50,6 +50,6 @@
>  
>  	memory at 0 {
>  		device_type = "memory";
> -		reg = <0x0 0x0 0x0 0x40000000>;
> +		linux,usable-memory = <0x0 0x1000000 0x0 0x3f000000>;
>  	};
>  };
> diff --git a/arch/arm64/boot/dts/amlogic/meson-gxbb-vega-s95-telos.dts b/arch/arm64/boot/dts/amlogic/meson-gxbb-vega-s95-telos.dts
> index 2fe167b..010cb29 100644
> --- a/arch/arm64/boot/dts/amlogic/meson-gxbb-vega-s95-telos.dts
> +++ b/arch/arm64/boot/dts/amlogic/meson-gxbb-vega-s95-telos.dts
> @@ -50,6 +50,6 @@
>  
>  	memory at 0 {
>  		device_type = "memory";
> -		reg = <0x0 0x0 0x0 0x80000000>;
> +		linux,usable-memory = <0x0 0x1000000 0x0 0x7f000000>;
>  	};
>  };
> diff --git a/arch/arm64/boot/dts/amlogic/meson-gxl-nexbox-a95x.dts b/arch/arm64/boot/dts/amlogic/meson-gxl-nexbox-a95x.dts
> index cea4a3e..fb4a89b 100644
> --- a/arch/arm64/boot/dts/amlogic/meson-gxl-nexbox-a95x.dts
> +++ b/arch/arm64/boot/dts/amlogic/meson-gxl-nexbox-a95x.dts
> @@ -60,7 +60,7 @@
>  
>  	memory at 0 {
>  		device_type = "memory";
> -		reg = <0x0 0x0 0x0 0x80000000>;
> +		linux,usable-memory = <0x0 0x1000000 0x0 0x7f000000>;
>  	};
>  
>  	vddio_card: gpio-regulator {
> diff --git a/arch/arm64/boot/dts/amlogic/meson-gxl-s905x-p212.dts b/arch/arm64/boot/dts/amlogic/meson-gxl-s905x-p212.dts
> index 9639f01..908894c 100644
> --- a/arch/arm64/boot/dts/amlogic/meson-gxl-s905x-p212.dts
> +++ b/arch/arm64/boot/dts/amlogic/meson-gxl-s905x-p212.dts
> @@ -59,7 +59,7 @@
>  
>  	memory at 0 {
>  		device_type = "memory";
> -		reg = <0x0 0x0 0x0 0x80000000>;
> +		linux,usable-memory = <0x0 0x1000000 0x0 0x7f000000>;
>  	};
>  };
>  
> diff --git a/arch/arm64/boot/dts/amlogic/meson-gxm-nexbox-a1.dts b/arch/arm64/boot/dts/amlogic/meson-gxm-nexbox-a1.dts
> index 5a337d3..2077385 100644
> --- a/arch/arm64/boot/dts/amlogic/meson-gxm-nexbox-a1.dts
> +++ b/arch/arm64/boot/dts/amlogic/meson-gxm-nexbox-a1.dts
> @@ -62,7 +62,7 @@
>  
>  	memory at 0 {
>  		device_type = "memory";
> -		reg = <0x0 0x0 0x0 0x80000000>;
> +		linux,usable-memory = <0x0 0x1000000 0x0 0x7f000000>;
>  	};
>  
>  	vddio_boot: regulator-vddio-boot {
> 

^ permalink raw reply

* [PATCH 2/2] ARM: dts: Add CPU OPPs for Exynos4412 Prime
From: Krzysztof Kozlowski @ 2016-12-30 14:47 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <1840714.lHhVv9oNko@amdc3058>

On Thu, Dec 29, 2016 at 04:06:20PM +0100, Bartlomiej Zolnierkiewicz wrote:
> On Thursday, December 29, 2016 04:49:08 PM Krzysztof Kozlowski wrote:
> > On Thu, Dec 29, 2016 at 02:36:51PM +0100, Bartlomiej Zolnierkiewicz wrote:
> > > Add CPU operating points for Exynos4412 Prime (it supports
> > > additional 1704MHz & 1600MHz OPPs and 1500MHz OPP is just
> > > a regular non-turbo OPP on this SoC).  Also update relevant
> > > cooling maps to account for new OPPs.
> > > 
> > > ODROID-X2/U2/U3 boards use Exynos4412 Prime SoC version so
> > > update their board files accordingly.
> > > 
> > > Based on Hardkernel's kernel for ODROID-X2/U2/U3 boards.
> > > 
> > > Cc: Doug Anderson <dianders@chromium.org>
> > > Cc: Andreas Faerber <afaerber@suse.de>
> > > Cc: Thomas Abraham <thomas.ab@samsung.com>
> > > Cc: Tobias Jakobi <tjakobi@math.uni-bielefeld.de>
> > > Cc: Ben Gamari <ben@smart-cactus.org>
> > > Signed-off-by: Bartlomiej Zolnierkiewicz <b.zolnierkie@samsung.com>
> > > ---
> > >  arch/arm/boot/dts/exynos4412-odroid-common.dtsi |  4 +--
> > >  arch/arm/boot/dts/exynos4412-odroidu3.dts       |  5 +--
> > >  arch/arm/boot/dts/exynos4412-odroidx2.dts       |  1 +
> > >  arch/arm/boot/dts/exynos4412-prime.dtsi         | 41 +++++++++++++++++++++++++
> > >  arch/arm/boot/dts/exynos4412.dtsi               |  2 +-
> > >  5 files changed, 48 insertions(+), 5 deletions(-)
> > >  create mode 100644 arch/arm/boot/dts/exynos4412-prime.dtsi
> > > 
> > 
> > Looks okay. Is the clock patch needed for this?
> 
> Yep.

Thanks, applied.

Best regards,
Krzysztof

^ permalink raw reply

* [PATCH 2/2] ARM: dts: Add CPU OPPs for Exynos4412 Prime
From: Krzysztof Kozlowski @ 2016-12-30 14:47 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <6892749a-a8bd-cfb3-551c-f0b1ca6d2b84@samsung.com>

On Thu, Dec 29, 2016 at 05:23:34PM +0100, Sylwester Nawrocki wrote:
> On 12/29/2016 04:06 PM, Bartlomiej Zolnierkiewicz wrote:
> >>> Signed-off-by: Bartlomiej Zolnierkiewicz <b.zolnierkie@samsung.com>
> >>> ---
> >>>  arch/arm/boot/dts/exynos4412-odroid-common.dtsi |  4 +--
> >>>  arch/arm/boot/dts/exynos4412-odroidu3.dts       |  5 +--
> >>>  arch/arm/boot/dts/exynos4412-odroidx2.dts       |  1 +
> >>>  arch/arm/boot/dts/exynos4412-prime.dtsi         | 41 +++++++++++++++++++++++++
> >>>  arch/arm/boot/dts/exynos4412.dtsi               |  2 +-
> >>>  5 files changed, 48 insertions(+), 5 deletions(-)
> >>>  create mode 100644 arch/arm/boot/dts/exynos4412-prime.dtsi
> >>>
> >> Looks okay. Is the clock patch needed for this?
> > Yep.
> 
> I applied the clock patch and here is a stable tag if it needs
> to be pulled as a dependency.
>

Thanks, merged!

BR,
Krzysztof

^ permalink raw reply

* [PATCH v9 3/3] iio: adc: add support for Allwinner SoCs ADC
From: Jonathan Cameron @ 2016-12-30 14:40 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <20161213143332.24988-4-quentin.schulz@free-electrons.com>

On 13/12/16 14:33, Quentin Schulz wrote:
> The Allwinner SoCs all have an ADC that can also act as a touchscreen
> controller and a thermal sensor. This patch adds the ADC driver which is
> based on the MFD for the same SoCs ADC.
> 
> This also registers the thermal adc channel in the iio map array so
> iio_hwmon could use it without modifying the Device Tree. This registers
> the driver in the thermal framework.
> 
> The thermal sensor requires the IP to be in touchscreen mode to return
> correct values. Therefore, if the user is continuously reading the ADC
> channel(s), the thermal framework in which the thermal sensor is
> registered will switch the IP in touchscreen mode to get a temperature
> value and requires a delay of 100ms (because of the mode switching),
> then the ADC will switch back to ADC mode and requires also a delay of
> 100ms. If the ADC readings are critical to user and the SoC temperature
> is not, this driver is capable of not registering the thermal sensor in
> the thermal framework and thus, "quicken" the ADC readings.
> 
> This driver probes on three different platform_device_id to take into
> account slight differences (registers bit and temperature computation)
> between Allwinner SoCs ADCs.
> 
> Signed-off-by: Quentin Schulz <quentin.schulz@free-electrons.com>
> Acked-by: Maxime Ripard <maxime.ripard@free-electrons.com>
> Acked-by: Jonathan Cameron <jic23@kernel.org>
> Acked-for-MFD-by: Lee Jones <lee.jones@linaro.org>
One comment inline but not a blocker.

I would ideally like an ack from the thermal side.  The relevant code
is small, but best to be sure and keep them in the loop as well.

It does feel a little convoluted to have both this directly providing
a thermal zone and being able to create one indirectly through hwmon as
well but this solution works for me I think...

Cc'd Zang and Eduardo.

Jonathan
> ---
> 
> v9:
>  - clarify comment on why we have to use the parent node as node for
>  registering in thermal framework, (backward compatibility)
>  - clarify comment on why we can disable CONFIG_THERMAL_OF,
>  - clarify Kconfig help to say that CONFIG_THERMAL_OF can be disabled
>  but should not in most cases,
>  - make return value of devm_thermal_zone_of_sensor_register a local
>  variable of the condition block,
>  - correct scale from _PLUS_MICRO to _PLUS_NANO for ADC raw readings
>  scale,
> 
> v8:
>  - remove Kconfig depends on !TOUCHSCREEN_SUN4I (moved to
> MFD_SUN4I_GPADC),
>  - fix return values of regmap_irq_get_virq and platform_get_irq_byname
> stored in an unsigned int and then check if negative,
>  - fix uninitialized ret value when an error occurs while registering
> the thermal sensor in the framework,
> 
> v7:
>  - add Kconfig depends on !TOUCHSCREEN_SUN4I,
>  - remove Kconfig selects THERMAL_OF,
>  - do not register thermal sensor if CONFIG_THERMAL_OF is disabled,
>  - disable irq in irq_handler rather than in read_raw,
>  - add delay when switching the IP's mode or channel (delay empirically found),
>  - quicken thermal sensor interrupt period,
>  - add masks for channel bits,
>  - fix deadlock in sun4i_gpadc_read if regmap_read/write fails,
>  - move some logic from sun4i_gpadc_read to sun4i_prepare_for_irq,
>  - mark last busy for runtime_pm only on success in sun4i_gpadc_read,
>  - remove cached values,
>  - increase wait_for_completion_timeout timeout to 1s to be sure to not miss the
>  thermal interrupt,
>  - add voltage scale,
>  - use devm_iio_device_register,
> 
> v6:
>  - remove "-mfd" from filenames and variables inside MFD driver,
>  - use DEFINE_RES_IRQ_NAMED instead of setting resources manually,
>  - cosmetic changes,
>  - use IDs and switch over ID to get cells specific to an architecture, instead
>  of using cells direclty, in of_device_id.data,
>  - compute size of mfd_cells array instead of hardcoded one,
> 
> v5:
>  - correct mail address,
> 
> v4:
>  - rename files and variables from sunxi* to sun4i*,
>  - rename defines from SUNXI_* to SUN4I_* or SUN6I_*,
>  - remove TP in defines name,
>  - rename SUNXI_IRQ_* to SUN4I_GPADC_IRQ_* for consistency,
>  - use devm functions for regmap_add_irq_chip and mfd_add_devices,
>  - remove remove functions (now empty thanks to devm functions),
> 
> v3:
>  - use defines in regmap_irq instead of hard coded BITs,
>  - use of_device_id data field to chose which MFD cells to add considering
>    the compatible responsible of the MFD probe,
>  - remove useless initializations,
>  - disable all interrupts before adding them to regmap_irqchip,
>  - add goto error label in probe,
>  - correct wrapping in header license,
>  - move defines from IIO driver to header,
>  - use GENMASK to limit the size of the variable passed to a macro,
>  - prefix register BIT defines with the name of the register,
>  - reorder defines,
> 
> v2:
>  - add license headers,
>  - reorder alphabetically includes,
>  - add SUNXI_GPADC_ prefixes for defines,
> 
>  drivers/iio/adc/Kconfig           |  17 ++
>  drivers/iio/adc/Makefile          |   1 +
>  drivers/iio/adc/sun4i-gpadc-iio.c | 613 ++++++++++++++++++++++++++++++++++++++
>  include/linux/mfd/sun4i-gpadc.h   |   2 +
>  4 files changed, 633 insertions(+)
>  create mode 100644 drivers/iio/adc/sun4i-gpadc-iio.c
> 
> diff --git a/drivers/iio/adc/Kconfig b/drivers/iio/adc/Kconfig
> index 99c0514..6a6d369 100644
> --- a/drivers/iio/adc/Kconfig
> +++ b/drivers/iio/adc/Kconfig
> @@ -434,6 +434,23 @@ config STX104
>  	  The base port addresses for the devices may be configured via the base
>  	  array module parameter.
>  
> +config SUN4I_GPADC
> +	tristate "Support for the Allwinner SoCs GPADC"
> +	depends on IIO
> +	depends on MFD_SUN4I_GPADC
> +	help
> +	  Say yes here to build support for Allwinner (A10, A13 and A31) SoCs
> +	  GPADC. This ADC provides 4 channels which can be used as an ADC or as
> +	  a touchscreen input and one channel for thermal sensor.
> +
> +	  The thermal sensor slows down ADC readings and can be disabled by
> +	  disabling CONFIG_THERMAL_OF. However, the thermal sensor should be
> +	  enabled by default since the SoC temperature is usually more critical
> +	  than ADC readings.
> +
> +	  To compile this driver as a module, choose M here: the module will be
> +	  called sun4i-gpadc-iio.
> +
>  config TI_ADC081C
>  	tristate "Texas Instruments ADC081C/ADC101C/ADC121C family"
>  	depends on I2C
> diff --git a/drivers/iio/adc/Makefile b/drivers/iio/adc/Makefile
> index 7a40c04..18ce8d6 100644
> --- a/drivers/iio/adc/Makefile
> +++ b/drivers/iio/adc/Makefile
> @@ -41,6 +41,7 @@ obj-$(CONFIG_QCOM_SPMI_IADC) += qcom-spmi-iadc.o
>  obj-$(CONFIG_QCOM_SPMI_VADC) += qcom-spmi-vadc.o
>  obj-$(CONFIG_ROCKCHIP_SARADC) += rockchip_saradc.o
>  obj-$(CONFIG_STX104) += stx104.o
> +obj-$(CONFIG_SUN4I_GPADC) += sun4i-gpadc-iio.o
>  obj-$(CONFIG_TI_ADC081C) += ti-adc081c.o
>  obj-$(CONFIG_TI_ADC0832) += ti-adc0832.o
>  obj-$(CONFIG_TI_ADC12138) += ti-adc12138.o
> diff --git a/drivers/iio/adc/sun4i-gpadc-iio.c b/drivers/iio/adc/sun4i-gpadc-iio.c
> new file mode 100644
> index 0000000..a8e134f
> --- /dev/null
> +++ b/drivers/iio/adc/sun4i-gpadc-iio.c
> @@ -0,0 +1,613 @@
> +/* ADC driver for sunxi platforms' (A10, A13 and A31) GPADC
> + *
> + * Copyright (c) 2016 Quentin Schulz <quentin.schulz@free-electrons.com>
> + *
> + * This program is free software; you can redistribute it and/or modify it under
> + * the terms of the GNU General Public License version 2 as published by the
> + * Free Software Foundation.
> + *
> + * The Allwinner SoCs all have an ADC that can also act as a touchscreen
> + * controller and a thermal sensor.
> + * The thermal sensor works only when the ADC acts as a touchscreen controller
> + * and is configured to throw an interrupt every fixed periods of time (let say
> + * every X seconds).
> + * One would be tempted to disable the IP on the hardware side rather than
> + * disabling interrupts to save some power but that resets the internal clock of
> + * the IP, resulting in having to wait X seconds every time we want to read the
> + * value of the thermal sensor.
> + * This is also the reason of using autosuspend in pm_runtime. If there was no
> + * autosuspend, the thermal sensor would need X seconds after every
> + * pm_runtime_get_sync to get a value from the ADC. The autosuspend allows the
> + * thermal sensor to be requested again in a certain time span before it gets
> + * shutdown for not being used.
> + */
> +
> +#include <linux/completion.h>
> +#include <linux/interrupt.h>
> +#include <linux/io.h>
> +#include <linux/module.h>
> +#include <linux/of.h>
> +#include <linux/of_device.h>
> +#include <linux/platform_device.h>
> +#include <linux/pm_runtime.h>
> +#include <linux/regmap.h>
> +#include <linux/thermal.h>
> +#include <linux/delay.h>
> +
> +#include <linux/iio/iio.h>
> +#include <linux/iio/driver.h>
> +#include <linux/iio/machine.h>
> +#include <linux/mfd/sun4i-gpadc.h>
> +
> +static unsigned int sun4i_gpadc_chan_select(unsigned int chan)
> +{
> +	return SUN4I_GPADC_CTRL1_ADC_CHAN_SELECT(chan);
> +}
> +
> +static unsigned int sun6i_gpadc_chan_select(unsigned int chan)
> +{
> +	return SUN6I_GPADC_CTRL1_ADC_CHAN_SELECT(chan);
> +}
> +
> +struct gpadc_data {
> +	int		temp_offset;
> +	int		temp_scale;
> +	unsigned int	tp_mode_en;
> +	unsigned int	tp_adc_select;
> +	unsigned int	(*adc_chan_select)(unsigned int chan);
> +	unsigned int	adc_chan_mask;
> +};
> +
> +static const struct gpadc_data sun4i_gpadc_data = {
> +	.temp_offset = -1932,
> +	.temp_scale = 133,
> +	.tp_mode_en = SUN4I_GPADC_CTRL1_TP_MODE_EN,
> +	.tp_adc_select = SUN4I_GPADC_CTRL1_TP_ADC_SELECT,
> +	.adc_chan_select = &sun4i_gpadc_chan_select,
> +	.adc_chan_mask = SUN4I_GPADC_CTRL1_ADC_CHAN_MASK,
> +};
> +
> +static const struct gpadc_data sun5i_gpadc_data = {
> +	.temp_offset = -1447,
> +	.temp_scale = 100,
> +	.tp_mode_en = SUN4I_GPADC_CTRL1_TP_MODE_EN,
> +	.tp_adc_select = SUN4I_GPADC_CTRL1_TP_ADC_SELECT,
> +	.adc_chan_select = &sun4i_gpadc_chan_select,
> +	.adc_chan_mask = SUN4I_GPADC_CTRL1_ADC_CHAN_MASK,
> +};
> +
> +static const struct gpadc_data sun6i_gpadc_data = {
> +	.temp_offset = -1623,
> +	.temp_scale = 167,
> +	.tp_mode_en = SUN6I_GPADC_CTRL1_TP_MODE_EN,
> +	.tp_adc_select = SUN6I_GPADC_CTRL1_TP_ADC_SELECT,
> +	.adc_chan_select = &sun6i_gpadc_chan_select,
> +	.adc_chan_mask = SUN6I_GPADC_CTRL1_ADC_CHAN_MASK,
> +};
> +
> +struct sun4i_gpadc_iio {
> +	struct iio_dev			*indio_dev;
> +	struct completion		completion;
> +	int				temp_data;
> +	u32				adc_data;
> +	struct regmap			*regmap;
> +	unsigned int			fifo_data_irq;
> +	atomic_t			ignore_fifo_data_irq;
> +	unsigned int			temp_data_irq;
> +	atomic_t			ignore_temp_data_irq;
> +	const struct gpadc_data		*data;
> +	/* prevents concurrent reads of temperature and ADC */
> +	struct mutex			mutex;
> +};
> +
> +#define SUN4I_GPADC_ADC_CHANNEL(_channel, _name) {		\
> +	.type = IIO_VOLTAGE,					\
> +	.indexed = 1,						\
> +	.channel = _channel,					\
> +	.info_mask_separate = BIT(IIO_CHAN_INFO_RAW),		\
> +	.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE),	\
> +	.datasheet_name = _name,				\
> +}
> +
> +static struct iio_map sun4i_gpadc_hwmon_maps[] = {
> +	{
> +		.adc_channel_label = "temp_adc",
> +		.consumer_dev_name = "iio_hwmon.0",
It's theoretically possible we have another one of these which will make
life interesting.  Oh well, no easy way around that at the mo...
> +	},
> +	{ /* sentinel */ },
> +};
> +
> +static const struct iio_chan_spec sun4i_gpadc_channels[] = {
> +	SUN4I_GPADC_ADC_CHANNEL(0, "adc_chan0"),
> +	SUN4I_GPADC_ADC_CHANNEL(1, "adc_chan1"),
> +	SUN4I_GPADC_ADC_CHANNEL(2, "adc_chan2"),
> +	SUN4I_GPADC_ADC_CHANNEL(3, "adc_chan3"),
> +	{
> +		.type = IIO_TEMP,
> +		.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
> +				      BIT(IIO_CHAN_INFO_SCALE) |
> +				      BIT(IIO_CHAN_INFO_OFFSET),
> +		.datasheet_name = "temp_adc",
> +	},
> +};
> +
> +static const struct iio_chan_spec sun4i_gpadc_channels_no_temp[] = {
> +	SUN4I_GPADC_ADC_CHANNEL(0, "adc_chan0"),
> +	SUN4I_GPADC_ADC_CHANNEL(1, "adc_chan1"),
> +	SUN4I_GPADC_ADC_CHANNEL(2, "adc_chan2"),
> +	SUN4I_GPADC_ADC_CHANNEL(3, "adc_chan3"),
> +};
> +
> +static int sun4i_prepare_for_irq(struct iio_dev *indio_dev, int channel,
> +				 unsigned int irq)
> +{
> +	struct sun4i_gpadc_iio *info = iio_priv(indio_dev);
> +	int ret;
> +	u32 reg;
> +
> +	pm_runtime_get_sync(indio_dev->dev.parent);
> +
> +	reinit_completion(&info->completion);
> +
> +	ret = regmap_write(info->regmap, SUN4I_GPADC_INT_FIFOC,
> +			   SUN4I_GPADC_INT_FIFOC_TP_FIFO_TRIG_LEVEL(1) |
> +			   SUN4I_GPADC_INT_FIFOC_TP_FIFO_FLUSH);
> +	if (ret)
> +		return ret;
> +
> +	ret = regmap_read(info->regmap, SUN4I_GPADC_CTRL1, &reg);
> +	if (ret)
> +		return ret;
> +
> +	if (irq == info->fifo_data_irq) {
> +		ret = regmap_write(info->regmap, SUN4I_GPADC_CTRL1,
> +				   info->data->tp_mode_en |
> +				   info->data->tp_adc_select |
> +				   info->data->adc_chan_select(channel));
> +		/*
> +		 * When the IP changes channel, it needs a bit of time to get
> +		 * correct values.
> +		 */
> +		if ((reg & info->data->adc_chan_mask) !=
> +			 info->data->adc_chan_select(channel))
> +			mdelay(10);
> +
> +	} else {
> +		/*
> +		 * The temperature sensor returns valid data only when the ADC
> +		 * operates in touchscreen mode.
> +		 */
> +		ret = regmap_write(info->regmap, SUN4I_GPADC_CTRL1,
> +				   info->data->tp_mode_en);
> +	}
> +
> +	if (ret)
> +		return ret;
> +
> +	/*
> +	 * When the IP changes mode between ADC or touchscreen, it
> +	 * needs a bit of time to get correct values.
> +	 */
> +	if ((reg & info->data->tp_adc_select) != info->data->tp_adc_select)
> +		mdelay(100);
> +
> +	return 0;
> +}
> +
> +static int sun4i_gpadc_read(struct iio_dev *indio_dev, int channel, int *val,
> +			    unsigned int irq)
> +{
> +	struct sun4i_gpadc_iio *info = iio_priv(indio_dev);
> +	int ret;
> +
> +	mutex_lock(&info->mutex);
> +
> +	ret = sun4i_prepare_for_irq(indio_dev, channel, irq);
> +	if (ret)
> +		goto err;
> +
> +	enable_irq(irq);
> +
> +	/*
> +	 * The temperature sensor throws an interruption periodically (currently
> +	 * set at periods of ~0.6s in sun4i_gpadc_runtime_resume). A 1s delay
> +	 * makes sure an interruption occurs in normal conditions. If it doesn't
> +	 * occur, then there is a timeout.
> +	 */
> +	if (!wait_for_completion_timeout(&info->completion,
> +					 msecs_to_jiffies(1000))) {
> +		ret = -ETIMEDOUT;
> +		goto err;
> +	}
> +
> +	if (irq == info->fifo_data_irq)
> +		*val = info->adc_data;
> +	else
> +		*val = info->temp_data;
> +
> +	ret = 0;
> +	pm_runtime_mark_last_busy(indio_dev->dev.parent);
> +
> +err:
> +	pm_runtime_put_autosuspend(indio_dev->dev.parent);
> +	mutex_unlock(&info->mutex);
> +
> +	return ret;
> +}
> +
> +static int sun4i_gpadc_adc_read(struct iio_dev *indio_dev, int channel,
> +				int *val)
> +{
> +	struct sun4i_gpadc_iio *info = iio_priv(indio_dev);
> +
> +	return sun4i_gpadc_read(indio_dev, channel, val, info->fifo_data_irq);
> +}
> +
> +static int sun4i_gpadc_temp_read(struct iio_dev *indio_dev, int *val)
> +{
> +	struct sun4i_gpadc_iio *info = iio_priv(indio_dev);
> +
> +	return sun4i_gpadc_read(indio_dev, 0, val, info->temp_data_irq);
> +}
> +
> +static int sun4i_gpadc_temp_offset(struct iio_dev *indio_dev, int *val)
> +{
> +	struct sun4i_gpadc_iio *info = iio_priv(indio_dev);
> +
> +	*val = info->data->temp_offset;
> +
> +	return 0;
> +}
> +
> +static int sun4i_gpadc_temp_scale(struct iio_dev *indio_dev, int *val)
> +{
> +	struct sun4i_gpadc_iio *info = iio_priv(indio_dev);
> +
> +	*val = info->data->temp_scale;
> +
> +	return 0;
> +}
> +
> +static int sun4i_gpadc_read_raw(struct iio_dev *indio_dev,
> +				struct iio_chan_spec const *chan, int *val,
> +				int *val2, long mask)
> +{
> +	int ret;
> +
> +	switch (mask) {
> +	case IIO_CHAN_INFO_OFFSET:
> +		ret = sun4i_gpadc_temp_offset(indio_dev, val);
> +		if (ret)
> +			return ret;
> +
> +		return IIO_VAL_INT;
> +	case IIO_CHAN_INFO_RAW:
> +		if (chan->type == IIO_VOLTAGE)
> +			ret = sun4i_gpadc_adc_read(indio_dev, chan->channel,
> +						   val);
> +		else
> +			ret = sun4i_gpadc_temp_read(indio_dev, val);
> +
> +		if (ret)
> +			return ret;
> +
> +		return IIO_VAL_INT;
> +	case IIO_CHAN_INFO_SCALE:
> +		if (chan->type == IIO_VOLTAGE) {
> +			/* 3000mV / 4096 * raw */
> +			*val = 0;
> +			*val2 = 732421875;
> +			return IIO_VAL_INT_PLUS_NANO;
> +		}
> +
> +		ret = sun4i_gpadc_temp_scale(indio_dev, val);
> +		if (ret)
> +			return ret;
> +
> +		return IIO_VAL_INT;
> +	default:
> +		return -EINVAL;
> +	}
> +
> +	return -EINVAL;
> +}
> +
> +static const struct iio_info sun4i_gpadc_iio_info = {
> +	.read_raw = sun4i_gpadc_read_raw,
> +	.driver_module = THIS_MODULE,
> +};
> +
> +static irqreturn_t sun4i_gpadc_temp_data_irq_handler(int irq, void *dev_id)
> +{
> +	struct sun4i_gpadc_iio *info = dev_id;
> +
> +	if (atomic_read(&info->ignore_temp_data_irq))
> +		goto out;
> +
> +	if (!regmap_read(info->regmap, SUN4I_GPADC_TEMP_DATA, &info->temp_data))
> +		complete(&info->completion);
> +
> +out:
> +	disable_irq_nosync(info->temp_data_irq);
> +	return IRQ_HANDLED;
> +}
> +
> +static irqreturn_t sun4i_gpadc_fifo_data_irq_handler(int irq, void *dev_id)
> +{
> +	struct sun4i_gpadc_iio *info = dev_id;
> +
> +	if (atomic_read(&info->ignore_fifo_data_irq))
> +		goto out;
> +
> +	if (!regmap_read(info->regmap, SUN4I_GPADC_DATA, &info->adc_data))
> +		complete(&info->completion);
> +
> +out:
> +	disable_irq_nosync(info->fifo_data_irq);
> +	return IRQ_HANDLED;
> +}
> +
> +static int sun4i_gpadc_runtime_suspend(struct device *dev)
> +{
> +	struct sun4i_gpadc_iio *info = iio_priv(dev_get_drvdata(dev));
> +
> +	/* Disable the ADC on IP */
> +	regmap_write(info->regmap, SUN4I_GPADC_CTRL1, 0);
> +	/* Disable temperature sensor on IP */
> +	regmap_write(info->regmap, SUN4I_GPADC_TPR, 0);
> +
> +	return 0;
> +}
> +
> +static int sun4i_gpadc_runtime_resume(struct device *dev)
> +{
> +	struct sun4i_gpadc_iio *info = iio_priv(dev_get_drvdata(dev));
> +
> +	/* clkin = 6MHz */
> +	regmap_write(info->regmap, SUN4I_GPADC_CTRL0,
> +		     SUN4I_GPADC_CTRL0_ADC_CLK_DIVIDER(2) |
> +		     SUN4I_GPADC_CTRL0_FS_DIV(7) |
> +		     SUN4I_GPADC_CTRL0_T_ACQ(63));
> +	regmap_write(info->regmap, SUN4I_GPADC_CTRL1, info->data->tp_mode_en);
> +	regmap_write(info->regmap, SUN4I_GPADC_CTRL3,
> +		     SUN4I_GPADC_CTRL3_FILTER_EN |
> +		     SUN4I_GPADC_CTRL3_FILTER_TYPE(1));
> +	/* period = SUN4I_GPADC_TPR_TEMP_PERIOD * 256 * 16 / clkin; ~0.6s */
> +	regmap_write(info->regmap, SUN4I_GPADC_TPR,
> +		     SUN4I_GPADC_TPR_TEMP_ENABLE |
> +		     SUN4I_GPADC_TPR_TEMP_PERIOD(800));
> +
> +	return 0;
> +}
> +
> +static int sun4i_gpadc_get_temp(void *data, int *temp)
> +{
> +	struct sun4i_gpadc_iio *info = (struct sun4i_gpadc_iio *)data;
> +	int val, scale, offset;
> +
> +	if (sun4i_gpadc_temp_read(info->indio_dev, &val))
> +		return -ETIMEDOUT;
> +
> +	sun4i_gpadc_temp_scale(info->indio_dev, &scale);
> +	sun4i_gpadc_temp_offset(info->indio_dev, &offset);
> +
> +	*temp = (val + offset) * scale;
> +
> +	return 0;
> +}
> +
> +static const struct thermal_zone_of_device_ops sun4i_ts_tz_ops = {
> +	.get_temp = &sun4i_gpadc_get_temp,
> +};
> +
> +static const struct dev_pm_ops sun4i_gpadc_pm_ops = {
> +	.runtime_suspend = &sun4i_gpadc_runtime_suspend,
> +	.runtime_resume = &sun4i_gpadc_runtime_resume,
> +};
> +
> +static int sun4i_irq_init(struct platform_device *pdev, const char *name,
> +			  irq_handler_t handler, const char *devname,
> +			  unsigned int *irq, atomic_t *atomic)
> +{
> +	int ret;
> +	struct sun4i_gpadc_dev *mfd_dev = dev_get_drvdata(pdev->dev.parent);
> +	struct sun4i_gpadc_iio *info = iio_priv(dev_get_drvdata(&pdev->dev));
> +
> +	/*
> +	 * Once the interrupt is activated, the IP continuously performs
> +	 * conversions thus throws interrupts. The interrupt is activated right
> +	 * after being requested but we want to control when these interrupts
> +	 * occur thus we disable it right after being requested. However, an
> +	 * interrupt might occur between these two instructions and we have to
> +	 * make sure that does not happen, by using atomic flags. We set the
> +	 * flag before requesting the interrupt and unset it right after
> +	 * disabling the interrupt. When an interrupt occurs between these two
> +	 * instructions, reading the atomic flag will tell us to ignore the
> +	 * interrupt.
> +	 */
> +	atomic_set(atomic, 1);
> +
> +	ret = platform_get_irq_byname(pdev, name);
> +	if (ret < 0) {
> +		dev_err(&pdev->dev, "no %s interrupt registered\n", name);
> +		return ret;
> +	}
> +
> +	ret = regmap_irq_get_virq(mfd_dev->regmap_irqc, ret);
> +	if (ret < 0) {
> +		dev_err(&pdev->dev, "failed to get virq for irq %s\n", name);
> +		return ret;
> +	}
> +
> +	*irq = ret;
> +	ret = devm_request_any_context_irq(&pdev->dev, *irq, handler, 0,
> +					   devname, info);
> +	if (ret < 0) {
> +		dev_err(&pdev->dev, "could not request %s interrupt: %d\n",
> +			name, ret);
> +		return ret;
> +	}
> +
> +	disable_irq(*irq);
> +	atomic_set(atomic, 0);
> +
> +	return 0;
> +}
> +
> +static int sun4i_gpadc_probe(struct platform_device *pdev)
> +{
> +	struct sun4i_gpadc_iio *info;
> +	struct iio_dev *indio_dev;
> +	int ret;
> +	struct sun4i_gpadc_dev *sun4i_gpadc_dev;
> +
> +	sun4i_gpadc_dev = dev_get_drvdata(pdev->dev.parent);
> +
> +	indio_dev = devm_iio_device_alloc(&pdev->dev, sizeof(*info));
> +	if (!indio_dev)
> +		return -ENOMEM;
> +
> +	info = iio_priv(indio_dev);
> +	platform_set_drvdata(pdev, indio_dev);
> +
> +	mutex_init(&info->mutex);
> +	info->regmap = sun4i_gpadc_dev->regmap;
> +	info->indio_dev = indio_dev;
> +	init_completion(&info->completion);
> +	indio_dev->name = dev_name(&pdev->dev);
> +	indio_dev->dev.parent = &pdev->dev;
> +	indio_dev->dev.of_node = pdev->dev.of_node;
> +	indio_dev->info = &sun4i_gpadc_iio_info;
> +	indio_dev->modes = INDIO_DIRECT_MODE;
> +	indio_dev->num_channels = ARRAY_SIZE(sun4i_gpadc_channels);
> +	indio_dev->channels = sun4i_gpadc_channels;
> +
> +	info->data = (struct gpadc_data *)platform_get_device_id(pdev)->driver_data;
> +
> +	/*
> +	 * Since the controller needs to be in touchscreen mode for its thermal
> +	 * sensor to operate properly, and that switching between the two modes
> +	 * needs a delay, always registering in the thermal framework will
> +	 * significantly slow down the conversion rate of the ADCs.
> +	 *
> +	 * Therefore, instead of depending on THERMAL_OF in Kconfig, we only
> +	 * register the sensor if that option is enabled, eventually leaving
> +	 * that choice to the user.
> +	 */
> +
> +	if (IS_ENABLED(CONFIG_THERMAL_OF)) {
> +		/*
> +		 * This driver is a child of an MFD which has a node in the DT
> +		 * but not its children, because of DT backward compatibility
> +		 * for A10, A13 and A31 SoCs. Therefore, the resulting devices
> +		 * of this driver do not have an of_node variable.
> +		 * However, its parent (the MFD driver) has an of_node variable
> +		 * and since devm_thermal_zone_of_sensor_register uses its first
> +		 * argument to match the phandle defined in the node of the
> +		 * thermal driver with the of_node of the device passed as first
> +		 * argument and the third argument to call ops from
> +		 * thermal_zone_of_device_ops, the solution is to use the parent
> +		 * device as first argument to match the phandle with its
> +		 * of_node, and the device from this driver as third argument to
> +		 * return the temperature.
> +		 */
> +		struct thermal_zone_device *tzd;
> +		tzd = devm_thermal_zone_of_sensor_register(pdev->dev.parent, 0,
> +							   info,
> +							   &sun4i_ts_tz_ops);
> +		if (IS_ERR(tzd)) {
> +			dev_err(&pdev->dev,
> +				"could not register thermal sensor: %ld\n",
> +				PTR_ERR(tzd));
> +			ret = PTR_ERR(tzd);
> +			goto err;
> +		}
> +	} else {
> +		indio_dev->num_channels =
> +			ARRAY_SIZE(sun4i_gpadc_channels_no_temp);
> +		indio_dev->channels = sun4i_gpadc_channels_no_temp;
> +	}
> +
> +	pm_runtime_set_autosuspend_delay(&pdev->dev,
> +					 SUN4I_GPADC_AUTOSUSPEND_DELAY);
> +	pm_runtime_use_autosuspend(&pdev->dev);
> +	pm_runtime_set_suspended(&pdev->dev);
> +	pm_runtime_enable(&pdev->dev);
> +
> +	if (IS_ENABLED(CONFIG_THERMAL_OF)) {
> +		ret = sun4i_irq_init(pdev, "TEMP_DATA_PENDING",
> +				     sun4i_gpadc_temp_data_irq_handler,
> +				     "temp_data", &info->temp_data_irq,
> +				     &info->ignore_temp_data_irq);
> +		if (ret < 0)
> +			goto err;
> +	}
> +
> +	ret = sun4i_irq_init(pdev, "FIFO_DATA_PENDING",
> +			     sun4i_gpadc_fifo_data_irq_handler, "fifo_data",
> +			     &info->fifo_data_irq, &info->ignore_fifo_data_irq);
> +	if (ret < 0)
> +		goto err;
> +
> +	if (IS_ENABLED(CONFIG_THERMAL_OF)) {
> +		ret = iio_map_array_register(indio_dev, sun4i_gpadc_hwmon_maps);
> +		if (ret < 0) {
> +			dev_err(&pdev->dev,
> +				"failed to register iio map array\n");
> +			goto err;
> +		}
> +	}
> +
> +	ret = devm_iio_device_register(&pdev->dev, indio_dev);
> +	if (ret < 0) {
> +		dev_err(&pdev->dev, "could not register the device\n");
> +		goto err_map;
> +	}
> +
> +	return 0;
> +
> +err_map:
> +	if (IS_ENABLED(CONFIG_THERMAL_OF))
> +		iio_map_array_unregister(indio_dev);
> +
> +err:
> +	pm_runtime_put(&pdev->dev);
> +	pm_runtime_disable(&pdev->dev);
> +
> +	return ret;
> +}
> +
> +static int sun4i_gpadc_remove(struct platform_device *pdev)
> +{
> +	struct iio_dev *indio_dev = platform_get_drvdata(pdev);
> +
> +	pm_runtime_put(&pdev->dev);
> +	pm_runtime_disable(&pdev->dev);
> +	if (IS_ENABLED(CONFIG_THERMAL_OF))
> +		iio_map_array_unregister(indio_dev);
> +
> +	return 0;
> +}
> +
> +static const struct platform_device_id sun4i_gpadc_id[] = {
> +	{ "sun4i-a10-gpadc-iio", (kernel_ulong_t)&sun4i_gpadc_data },
> +	{ "sun5i-a13-gpadc-iio", (kernel_ulong_t)&sun5i_gpadc_data },
> +	{ "sun6i-a31-gpadc-iio", (kernel_ulong_t)&sun6i_gpadc_data },
> +	{ /* sentinel */ },
> +};
> +
> +static struct platform_driver sun4i_gpadc_driver = {
> +	.driver = {
> +		.name = "sun4i-gpadc-iio",
> +		.pm = &sun4i_gpadc_pm_ops,
> +	},
> +	.id_table = sun4i_gpadc_id,
> +	.probe = sun4i_gpadc_probe,
> +	.remove = sun4i_gpadc_remove,
> +};
> +
> +module_platform_driver(sun4i_gpadc_driver);
> +
> +MODULE_DESCRIPTION("ADC driver for sunxi platforms");
> +MODULE_AUTHOR("Quentin Schulz <quentin.schulz@free-electrons.com>");
> +MODULE_LICENSE("GPL v2");
> diff --git a/include/linux/mfd/sun4i-gpadc.h b/include/linux/mfd/sun4i-gpadc.h
> index d7a29f2..509e736 100644
> --- a/include/linux/mfd/sun4i-gpadc.h
> +++ b/include/linux/mfd/sun4i-gpadc.h
> @@ -28,6 +28,7 @@
>  #define SUN4I_GPADC_CTRL1_TP_MODE_EN			BIT(4)
>  #define SUN4I_GPADC_CTRL1_TP_ADC_SELECT			BIT(3)
>  #define SUN4I_GPADC_CTRL1_ADC_CHAN_SELECT(x)		(GENMASK(2, 0) & (x))
> +#define SUN4I_GPADC_CTRL1_ADC_CHAN_MASK			GENMASK(2, 0)
>  
>  /* TP_CTRL1 bits for sun6i SOCs */
>  #define SUN6I_GPADC_CTRL1_TOUCH_PAN_CALI_EN		BIT(7)
> @@ -35,6 +36,7 @@
>  #define SUN6I_GPADC_CTRL1_TP_MODE_EN			BIT(5)
>  #define SUN6I_GPADC_CTRL1_TP_ADC_SELECT			BIT(4)
>  #define SUN6I_GPADC_CTRL1_ADC_CHAN_SELECT(x)		(GENMASK(3, 0) & BIT(x))
> +#define SUN6I_GPADC_CTRL1_ADC_CHAN_MASK			GENMASK(3, 0)
>  
>  #define SUN4I_GPADC_CTRL2				0x08
>  
> 

^ permalink raw reply

* [PATCH v7 3/5] ARM: dts: stm32: Add I2C1 support for STM32F429 SoC
From: M'boumba Cedric Madianga @ 2016-12-30 14:36 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <CACRpkdaNyL2iV+risLudW=O55w81-AuEZhMwRu9NLdXpnC2r1w@mail.gmail.com>

 Hi Linus,

2016-12-30 10:07 GMT+01:00 Linus Walleij <linus.walleij@linaro.org>:
> On Fri, Dec 23, 2016 at 2:09 PM, M'boumba Cedric Madianga
> <cedric.madianga@gmail.com> wrote:
>> 2016-12-22 20:11 GMT+01:00 Uwe Kleine-K?nig <u.kleine-koenig@pengutronix.de>:
>>> On Thu, Dec 22, 2016 at 02:35:02PM +0100, M'boumba Cedric Madianga wrote:
>>>> @@ -337,6 +350,16 @@
>>>>                                       slew-rate = <2>;
>>>>                               };
>>>>                       };
>>>> +
>>>> +                     i2c1_pins_b: i2c1 at 0 {
>>>> +                             pins1 {
>>>> +                                     pinmux = <STM32F429_PB9_FUNC_I2C1_SDA>;
>>>> +                                     drive-open-drain;
>>>> +                             };
>>>> +                             pins2 {
>>>> +                                     pinmux = <STM32F429_PB6_FUNC_I2C1_SCL>;
>>>> +                             };
>>>
>>> the second doesn't need the open-drain property? Why?
>>
>> I thought that open-drain was only needed for SDA line.
>> But after double-checking I2C specification, it seems that SDA and SCL
>> lines need open-drain or open-collector to perform the wired-AND
>> function.
>
> I think I2C SDA/SCL must be open drain by definition.
>
> It also requires pull-up resistors, I guess you have these mounted on the board
> so you do not need pull-up from the pin controller?
Yes, I have 1 pull-up resistor of 1,5K ohm for each line (SDA & SCL)
on the board

>
> Yours,
> Linus Walleij

^ permalink raw reply

* [PATCH] ARM: dts: imx6q-utilite-pro: enable 2nd display pipeline
From: Shawn Guo @ 2016-12-30 14:27 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <ec9676f344eb4786a28a3c7b969f0e94@rwthex-s1-b.rwth-ad.de>

On Fri, Dec 02, 2016 at 03:37:22PM +0100, christopher.spinrath at rwth-aachen.de wrote:
> From: Christopher Spinrath <christopher.spinrath@rwth-aachen.de>
> 
> Apart from the already enabled Designware HDMI port, the Utilite Pro
> has a second display pipeline which has the following shape:
> 
>   IPU1 DI0 --> Parallel display --> tfp410 rgb24 to DVI encoder
>                                 --> HDMI connector.
> Enable support for it.
> 
> In addition, since this pipeline is hardwired to IPU1, sever the link
> between IPU1 and the SoC-internal Designware HDMI encoder forcing the
> latter to be connected to IPU2 instead of IPU1. Otherwise, it is not
> possible to drive both displays at high resolution due to the bandwidth
> limitations of a single IPU.
> 
> Signed-off-by: Christopher Spinrath <christopher.spinrath@rwth-aachen.de>

@Philipp, can you help review the changes?

> ---
> 
> Hi all,
> 
> the removal of the link between IPU1 and the Designware HDMI encoder is the
> result of a discussion I had with Philipp Zabel:
> 
>   https://lists.freedesktop.org/archives/dri-devel/2016-November/125399.html .
> 
> Altough it is not possible to connect anything else to IPU1 on the Utilite, this
> approach has at least one disadvantage: if the resolution is low enough such 
> that a single IPU can handle both displays then muxing both displays to IPU1
> would reduce the power consumption.
> 
> However, IMHO omitting the link IPU1 <--> DW HDMI is still the preferrable
> solution since I'm not aware of any OS/driver that is capable of switching IPUs
> or can handle the bandwidth limitation in a sane way. In particular, Linux is
> unusable when both displays are supposed to be driven at high resolution and
> both muxing options for the DW HDMI are available (this is not a userspace
> issue; the system becomes almost unresponsive as soon as the kernel sets the
> initial resolution).
> 
> Cheers,
> Christopher
> 
> P.S.: this patch depends on the tfp410 bridge driver which has recently been
> merged into drm-next.

v4.10-rc1 has the driver, so the dependency is gone now, I guess.

> 
>  arch/arm/boot/dts/imx6q-utilite-pro.dts | 115 ++++++++++++++++++++++++++++++++
>  1 file changed, 115 insertions(+)
> 
> diff --git a/arch/arm/boot/dts/imx6q-utilite-pro.dts b/arch/arm/boot/dts/imx6q-utilite-pro.dts
> index 2200994..69bdd82 100644
> --- a/arch/arm/boot/dts/imx6q-utilite-pro.dts
> +++ b/arch/arm/boot/dts/imx6q-utilite-pro.dts
> @@ -59,6 +59,33 @@
>  		rtc1 = &snvs_rtc;
>  	};
>  
> +	encoder {
> +		compatible = "ti,tfp410";
> +		#address-cells = <1>;
> +		#size-cells = <0>;
> +
> +		ports {
> +			#address-cells = <1>;
> +			#size-cells = <0>;
> +
> +			port at 0 {
> +				reg = <0>;
> +
> +				tfp410_in: endpoint {
> +					remote-endpoint = <&parallel_display_out>;
> +				};
> +			};
> +
> +			port at 1 {
> +				reg = <1>;
> +
> +				tfp410_out: endpoint {
> +					remote-endpoint = <&hdmi_connector_in>;
> +				};
> +			};
> +		};
> +	};
> +
>  	gpio-keys {
>  		compatible = "gpio-keys";
>  		pinctrl-names = "default";
> @@ -72,6 +99,19 @@
>  		};
>  	};
>  
> +	hdmi-connector {
> +		compatible = "hdmi-connector";
> +

The newline is unnecessary.

> +		type = "a";
> +		ddc-i2c-bus = <&i2c_dvi_ddc>;
> +
> +		port {
> +			hdmi_connector_in: endpoint {
> +				remote-endpoint = <&tfp410_out>;
> +			};
> +		};
> +	};
> +
>  	i2cmux {
>  		compatible = "i2c-mux-gpio";
>  		pinctrl-names = "default";
> @@ -105,8 +145,46 @@
>  			#size-cells = <0>;
>  		};
>  	};
> +
> +	parallel-display {
> +		compatible = "fsl,imx-parallel-display";
> +		#address-cells = <1>;
> +		#size-cells = <0>;
> +		pinctrl-names = "default";
> +		pinctrl-0 = <&pinctrl_ipu1>;
> +

Ditto

I can fix them up if I get a Reviewed-by tag from Philipp on this
version.

Shawn

> +		interface-pix-fmt = "rgb24";
> +
> +		port at 0 {
> +			reg = <0>;
> +
> +			parallel_display_in: endpoint {
> +				remote-endpoint = <&ipu1_di0_disp0>;
> +			};
> +		};
> +
> +		port at 1 {
> +			reg = <1>;
> +
> +			parallel_display_out: endpoint {
> +				remote-endpoint = <&tfp410_in>;
> +			};
> +		};
> +	};
>  };
>  
> +/*
> + * A single IPU is not able to drive both display interfaces available on the
> + * Utilite Pro at high resolution due to its bandwidth limitation. Since the
> + * tfp410 encoder is wired up to IPU1, sever the link between IPU1 and the
> + * SoC-internal Designware HDMI encoder forcing the latter to be connected to
> + * IPU2 instead of IPU1.
> + */
> +/delete-node/&ipu1_di0_hdmi;
> +/delete-node/&hdmi_mux_0;
> +/delete-node/&ipu1_di1_hdmi;
> +/delete-node/&hdmi_mux_1;
> +
>  &hdmi {
>  	ddc-i2c-bus = <&i2c2>;
>  	status = "okay";
> @@ -151,6 +229,39 @@
>  		>;
>  	};
>  
> +	pinctrl_ipu1: ipu1grp {
> +		fsl,pins = <
> +			MX6QDL_PAD_DI0_DISP_CLK__IPU1_DI0_DISP_CLK 0x38
> +			MX6QDL_PAD_DI0_PIN15__IPU1_DI0_PIN15       0x38
> +			MX6QDL_PAD_DI0_PIN2__IPU1_DI0_PIN02        0x38
> +			MX6QDL_PAD_DI0_PIN3__IPU1_DI0_PIN03        0x38
> +			MX6QDL_PAD_DISP0_DAT0__IPU1_DISP0_DATA00   0x38
> +			MX6QDL_PAD_DISP0_DAT1__IPU1_DISP0_DATA01   0x38
> +			MX6QDL_PAD_DISP0_DAT2__IPU1_DISP0_DATA02   0x38
> +			MX6QDL_PAD_DISP0_DAT3__IPU1_DISP0_DATA03   0x38
> +			MX6QDL_PAD_DISP0_DAT4__IPU1_DISP0_DATA04   0x38
> +			MX6QDL_PAD_DISP0_DAT5__IPU1_DISP0_DATA05   0x38
> +			MX6QDL_PAD_DISP0_DAT6__IPU1_DISP0_DATA06   0x38
> +			MX6QDL_PAD_DISP0_DAT7__IPU1_DISP0_DATA07   0x38
> +			MX6QDL_PAD_DISP0_DAT8__IPU1_DISP0_DATA08   0x38
> +			MX6QDL_PAD_DISP0_DAT9__IPU1_DISP0_DATA09   0x38
> +			MX6QDL_PAD_DISP0_DAT10__IPU1_DISP0_DATA10  0x38
> +			MX6QDL_PAD_DISP0_DAT11__IPU1_DISP0_DATA11  0x38
> +			MX6QDL_PAD_DISP0_DAT12__IPU1_DISP0_DATA12  0x38
> +			MX6QDL_PAD_DISP0_DAT13__IPU1_DISP0_DATA13  0x38
> +			MX6QDL_PAD_DISP0_DAT14__IPU1_DISP0_DATA14  0x38
> +			MX6QDL_PAD_DISP0_DAT15__IPU1_DISP0_DATA15  0x38
> +			MX6QDL_PAD_DISP0_DAT16__IPU1_DISP0_DATA16  0x38
> +			MX6QDL_PAD_DISP0_DAT17__IPU1_DISP0_DATA17  0x38
> +			MX6QDL_PAD_DISP0_DAT18__IPU1_DISP0_DATA18  0x38
> +			MX6QDL_PAD_DISP0_DAT19__IPU1_DISP0_DATA19  0x38
> +			MX6QDL_PAD_DISP0_DAT20__IPU1_DISP0_DATA20  0x38
> +			MX6QDL_PAD_DISP0_DAT21__IPU1_DISP0_DATA21  0x38
> +			MX6QDL_PAD_DISP0_DAT22__IPU1_DISP0_DATA22  0x38
> +			MX6QDL_PAD_DISP0_DAT23__IPU1_DISP0_DATA23  0x38
> +		>;
> +	};
> +
>  	pinctrl_uart2: uart2grp {
>  		fsl,pins = <
>  			MX6QDL_PAD_GPIO_7__UART2_TX_DATA 0x1b0b1
> @@ -194,6 +305,10 @@
>  	};
>  };
>  
> +&ipu1_di0_disp0 {
> +	remote-endpoint = <&parallel_display_in>;
> +};
> +
>  &pcie {
>  	pcie at 0,0 {
>  		reg = <0x000000 0 0 0 0>;
> -- 
> 2.10.2
> 

^ permalink raw reply

* [PATCH] pinctrl: stricten up generic group code
From: Linus Walleij @ 2016-12-30 14:07 UTC (permalink / raw)
  To: linux-arm-kernel

Rename the symbol PINCTRL_GENERIC to PINCTRL_GENERIC_GROUPS since
it all pertains to groups. Replace everywhere.

ifdef out the radix tree and the struct when not using the
generic groups.

Cc: Tony Lindgren <tony@atomide.com>
Signed-off-by: Linus Walleij <linus.walleij@linaro.org>
---
 drivers/pinctrl/Kconfig |  2 +-
 drivers/pinctrl/core.c  |  6 ++++--
 drivers/pinctrl/core.h  | 32 +++++++++++++++++---------------
 3 files changed, 22 insertions(+), 18 deletions(-)

diff --git a/drivers/pinctrl/Kconfig b/drivers/pinctrl/Kconfig
index b986998409d1..add257f80d76 100644
--- a/drivers/pinctrl/Kconfig
+++ b/drivers/pinctrl/Kconfig
@@ -8,7 +8,7 @@ config PINCTRL
 menu "Pin controllers"
 	depends on PINCTRL
 
-config GENERIC_PINCTRL
+config GENERIC_PINCTRL_GROUPS
 	bool
 
 config PINMUX
diff --git a/drivers/pinctrl/core.c b/drivers/pinctrl/core.c
index 7b7d706f869c..736149d3a33d 100644
--- a/drivers/pinctrl/core.c
+++ b/drivers/pinctrl/core.c
@@ -540,7 +540,7 @@ void pinctrl_remove_gpio_range(struct pinctrl_dev *pctldev,
 }
 EXPORT_SYMBOL_GPL(pinctrl_remove_gpio_range);
 
-#ifdef CONFIG_GENERIC_PINCTRL
+#ifdef CONFIG_GENERIC_PINCTRL_GROUPS
 
 /**
  * pinctrl_generic_get_group_count() - returns the number of pin groups
@@ -714,7 +714,7 @@ static void pinctrl_generic_free_groups(struct pinctrl_dev *pctldev)
 static inline void pinctrl_generic_free_groups(struct pinctrl_dev *pctldev)
 {
 }
-#endif /* CONFIG_GENERIC_PINCTRL */
+#endif /* CONFIG_GENERIC_PINCTRL_GROUPS */
 
 /**
  * pinctrl_get_group_selector() - returns the group selector for a group
@@ -1993,7 +1993,9 @@ struct pinctrl_dev *pinctrl_register(struct pinctrl_desc *pctldesc,
 	pctldev->desc = pctldesc;
 	pctldev->driver_data = driver_data;
 	INIT_RADIX_TREE(&pctldev->pin_desc_tree, GFP_KERNEL);
+#ifdef CONFIG_GENERIC_PINCTRL_GROUPS
 	INIT_RADIX_TREE(&pctldev->pin_group_tree, GFP_KERNEL);
+#endif
 	INIT_LIST_HEAD(&pctldev->gpio_ranges);
 	INIT_DELAYED_WORK(&pctldev->late_init, pinctrl_late_init);
 	pctldev->dev = dev;
diff --git a/drivers/pinctrl/core.h b/drivers/pinctrl/core.h
index af98b6313902..b04c59bf9701 100644
--- a/drivers/pinctrl/core.h
+++ b/drivers/pinctrl/core.h
@@ -43,8 +43,10 @@ struct pinctrl_dev {
 	struct list_head node;
 	struct pinctrl_desc *desc;
 	struct radix_tree_root pin_desc_tree;
+#ifdef CONFIG_GENERIC_PINCTRL_GROUPS
 	struct radix_tree_root pin_group_tree;
 	unsigned int num_groups;
+#endif
 	struct list_head gpio_ranges;
 	struct device *dev;
 	struct module *owner;
@@ -166,6 +168,20 @@ struct pin_desc {
 };
 
 /**
+ * struct pinctrl_maps - a list item containing part of the mapping table
+ * @node: mapping table list node
+ * @maps: array of mapping table entries
+ * @num_maps: the number of entries in @maps
+ */
+struct pinctrl_maps {
+	struct list_head node;
+	struct pinctrl_map const *maps;
+	unsigned num_maps;
+};
+
+#ifdef CONFIG_GENERIC_PINCTRL_GROUPS
+
+/**
  * struct group_desc - generic pin group descriptor
  * @name: name of the pin group
  * @pins: array of pins that belong to the group
@@ -179,20 +195,6 @@ struct group_desc {
 	void *data;
 };
 
-/**
- * struct pinctrl_maps - a list item containing part of the mapping table
- * @node: mapping table list node
- * @maps: array of mapping table entries
- * @num_maps: the number of entries in @maps
- */
-struct pinctrl_maps {
-	struct list_head node;
-	struct pinctrl_map const *maps;
-	unsigned num_maps;
-};
-
-#ifdef CONFIG_GENERIC_PINCTRL
-
 int pinctrl_generic_get_group_count(struct pinctrl_dev *pctldev);
 
 const char *pinctrl_generic_get_group_name(struct pinctrl_dev *pctldev,
@@ -218,7 +220,7 @@ pinctrl_generic_remove_last_group(struct pinctrl_dev *pctldev)
 	return pinctrl_generic_remove_group(pctldev, pctldev->num_groups - 1);
 }
 
-#endif	/* CONFIG_GENERIC_PINCTRL */
+#endif	/* CONFIG_GENERIC_PINCTRL_GROUPS */
 
 struct pinctrl_dev *get_pinctrl_dev_from_devname(const char *dev_name);
 struct pinctrl_dev *get_pinctrl_dev_from_of_node(struct device_node *np);
-- 
2.9.3

^ permalink raw reply related

* [PATCH] pinctrl: add some comments to the hog/late init code
From: Linus Walleij @ 2016-12-30 13:45 UTC (permalink / raw)
  To: linux-arm-kernel

It confused me a bit so it may confuse others. Make it crystal
clear what is going on here for any future readers.

Cc: Tony Lindgren <tony@atomide.com>
Signed-off-by: Linus Walleij <linus.walleij@linaro.org>
---
 drivers/pinctrl/core.c | 12 ++++++++++++
 1 file changed, 12 insertions(+)

diff --git a/drivers/pinctrl/core.c b/drivers/pinctrl/core.c
index d02e8def9506..a9e3a75fb67d 100644
--- a/drivers/pinctrl/core.c
+++ b/drivers/pinctrl/core.c
@@ -1753,6 +1753,12 @@ static void pinctrl_late_init(struct work_struct *work)
 
 	pctldev = container_of(work, struct pinctrl_dev, late_init.work);
 
+	/*
+	 * If the pin controller does NOT have hogs, this will report an
+	 * error and we skip over this entire branch. This is why we can
+	 * call this function directly when we do not have hogs on the
+	 * device.
+	 */
 	pctldev->p = create_pinctrl(pctldev->dev, pctldev);
 	if (!IS_ERR(pctldev->p)) {
 		kref_get(&pctldev->p->users);
@@ -1847,6 +1853,12 @@ struct pinctrl_dev *pinctrl_register(struct pinctrl_desc *pctldesc,
 		goto out_err;
 	}
 
+	/*
+	 * If the device has hogs we want the probe() function of the driver
+	 * to complete before we go in and hog them and add the pin controller
+	 * to the list of controllers. If it has no hogs, we can just complete
+	 * the registration immediately.
+	 */
 	if (pinctrl_dt_has_hogs(pctldev))
 		schedule_delayed_work(&pctldev->late_init, 0);
 	else
-- 
2.9.3

^ permalink raw reply related

* [PATCH v2] ARM64: dts: meson-gx: Add reserved memory zone and usable memory range
From: Neil Armstrong @ 2016-12-30 13:40 UTC (permalink / raw)
  To: linux-arm-kernel

The Amlogic Meson GXBB/GXL/GXM secure monitor uses part of the memory space,
this patch adds this reserved zone and redefines the usable memory range.

Signed-off-by: Neil Armstrong <narmstrong@baylibre.com>
---
Changes since v1 at [1] :
 - Renamed reg into linux,usable-memory to ovveride u-boot memory
 - only kept secmon memory zone

 [1] http://lkml.kernel.org/r/20161212101801.28491-1-narmstrong at baylibre.com

 arch/arm64/boot/dts/amlogic/meson-gx-p23x-q20x.dtsi       |  2 +-
 arch/arm64/boot/dts/amlogic/meson-gx.dtsi                 | 12 ++++++++++++
 arch/arm64/boot/dts/amlogic/meson-gxbb-nexbox-a95x.dts    |  2 +-
 arch/arm64/boot/dts/amlogic/meson-gxbb-odroidc2.dts       |  2 +-
 arch/arm64/boot/dts/amlogic/meson-gxbb-p20x.dtsi          |  2 +-
 arch/arm64/boot/dts/amlogic/meson-gxbb-vega-s95-meta.dts  |  2 +-
 arch/arm64/boot/dts/amlogic/meson-gxbb-vega-s95-pro.dts   |  2 +-
 arch/arm64/boot/dts/amlogic/meson-gxbb-vega-s95-telos.dts |  2 +-
 arch/arm64/boot/dts/amlogic/meson-gxl-nexbox-a95x.dts     |  2 +-
 arch/arm64/boot/dts/amlogic/meson-gxl-s905x-p212.dts      |  2 +-
 arch/arm64/boot/dts/amlogic/meson-gxm-nexbox-a1.dts       |  2 +-
 11 files changed, 22 insertions(+), 10 deletions(-)

diff --git a/arch/arm64/boot/dts/amlogic/meson-gx-p23x-q20x.dtsi b/arch/arm64/boot/dts/amlogic/meson-gx-p23x-q20x.dtsi
index 7a078be..ca3c7fa 100644
--- a/arch/arm64/boot/dts/amlogic/meson-gx-p23x-q20x.dtsi
+++ b/arch/arm64/boot/dts/amlogic/meson-gx-p23x-q20x.dtsi
@@ -56,7 +56,7 @@
 
 	memory at 0 {
 		device_type = "memory";
-		reg = <0x0 0x0 0x0 0x80000000>;
+		linux,usable-memory = <0x0 0x1000000 0x0 0x7f000000>;
 	};
 
 	vddio_boot: regulator-vddio_boot {
diff --git a/arch/arm64/boot/dts/amlogic/meson-gx.dtsi b/arch/arm64/boot/dts/amlogic/meson-gx.dtsi
index eada0b5..7f244b5 100644
--- a/arch/arm64/boot/dts/amlogic/meson-gx.dtsi
+++ b/arch/arm64/boot/dts/amlogic/meson-gx.dtsi
@@ -55,6 +55,18 @@
 	#address-cells = <2>;
 	#size-cells = <2>;
 
+	reserved-memory {
+		#address-cells = <2>;
+		#size-cells = <2>;
+		ranges;
+
+		/* global autoconfigured region for contiguous allocations */
+		secmon: secmon {
+			reg = <0x0 0x10000000 0x0 0x200000>;
+			no-map;
+		};
+	};
+
 	cpus {
 		#address-cells = <0x2>;
 		#size-cells = <0x0>;
diff --git a/arch/arm64/boot/dts/amlogic/meson-gxbb-nexbox-a95x.dts b/arch/arm64/boot/dts/amlogic/meson-gxbb-nexbox-a95x.dts
index 4cbd626..c7f008a 100644
--- a/arch/arm64/boot/dts/amlogic/meson-gxbb-nexbox-a95x.dts
+++ b/arch/arm64/boot/dts/amlogic/meson-gxbb-nexbox-a95x.dts
@@ -62,7 +62,7 @@
 
 	memory at 0 {
 		device_type = "memory";
-		reg = <0x0 0x0 0x0 0x40000000>;
+		linux,usable-memory = <0x0 0x1000000 0x0 0x3f000000>;
 	};
 
 	leds {
diff --git a/arch/arm64/boot/dts/amlogic/meson-gxbb-odroidc2.dts b/arch/arm64/boot/dts/amlogic/meson-gxbb-odroidc2.dts
index 238fbea..546cbe4 100644
--- a/arch/arm64/boot/dts/amlogic/meson-gxbb-odroidc2.dts
+++ b/arch/arm64/boot/dts/amlogic/meson-gxbb-odroidc2.dts
@@ -61,7 +61,7 @@
 
 	memory at 0 {
 		device_type = "memory";
-		reg = <0x0 0x0 0x0 0x80000000>;
+		linux,usable-memory = <0x0 0x1000000 0x0 0x7f000000>;
 	};
 
 	usb_otg_pwr: regulator-usb-pwrs {
diff --git a/arch/arm64/boot/dts/amlogic/meson-gxbb-p20x.dtsi b/arch/arm64/boot/dts/amlogic/meson-gxbb-p20x.dtsi
index 4a96e0f..1fdf6da 100644
--- a/arch/arm64/boot/dts/amlogic/meson-gxbb-p20x.dtsi
+++ b/arch/arm64/boot/dts/amlogic/meson-gxbb-p20x.dtsi
@@ -55,7 +55,7 @@
 
 	memory at 0 {
 		device_type = "memory";
-		reg = <0x0 0x0 0x0 0x40000000>;
+		linux,usable-memory = <0x0 0x1000000 0x0 0x3f000000>;
 	};
 
 	usb_pwr: regulator-usb-pwrs {
diff --git a/arch/arm64/boot/dts/amlogic/meson-gxbb-vega-s95-meta.dts b/arch/arm64/boot/dts/amlogic/meson-gxbb-vega-s95-meta.dts
index 62fb496..6ac5c89 100644
--- a/arch/arm64/boot/dts/amlogic/meson-gxbb-vega-s95-meta.dts
+++ b/arch/arm64/boot/dts/amlogic/meson-gxbb-vega-s95-meta.dts
@@ -50,6 +50,6 @@
 
 	memory at 0 {
 		device_type = "memory";
-		reg = <0x0 0x0 0x0 0x80000000>;
+		linux,usable-memory = <0x0 0x1000000 0x0 0x7f000000>;
 	};
 };
diff --git a/arch/arm64/boot/dts/amlogic/meson-gxbb-vega-s95-pro.dts b/arch/arm64/boot/dts/amlogic/meson-gxbb-vega-s95-pro.dts
index 9a9663a..58be8b4 100644
--- a/arch/arm64/boot/dts/amlogic/meson-gxbb-vega-s95-pro.dts
+++ b/arch/arm64/boot/dts/amlogic/meson-gxbb-vega-s95-pro.dts
@@ -50,6 +50,6 @@
 
 	memory at 0 {
 		device_type = "memory";
-		reg = <0x0 0x0 0x0 0x40000000>;
+		linux,usable-memory = <0x0 0x1000000 0x0 0x3f000000>;
 	};
 };
diff --git a/arch/arm64/boot/dts/amlogic/meson-gxbb-vega-s95-telos.dts b/arch/arm64/boot/dts/amlogic/meson-gxbb-vega-s95-telos.dts
index 2fe167b..010cb29 100644
--- a/arch/arm64/boot/dts/amlogic/meson-gxbb-vega-s95-telos.dts
+++ b/arch/arm64/boot/dts/amlogic/meson-gxbb-vega-s95-telos.dts
@@ -50,6 +50,6 @@
 
 	memory at 0 {
 		device_type = "memory";
-		reg = <0x0 0x0 0x0 0x80000000>;
+		linux,usable-memory = <0x0 0x1000000 0x0 0x7f000000>;
 	};
 };
diff --git a/arch/arm64/boot/dts/amlogic/meson-gxl-nexbox-a95x.dts b/arch/arm64/boot/dts/amlogic/meson-gxl-nexbox-a95x.dts
index cea4a3e..fb4a89b 100644
--- a/arch/arm64/boot/dts/amlogic/meson-gxl-nexbox-a95x.dts
+++ b/arch/arm64/boot/dts/amlogic/meson-gxl-nexbox-a95x.dts
@@ -60,7 +60,7 @@
 
 	memory at 0 {
 		device_type = "memory";
-		reg = <0x0 0x0 0x0 0x80000000>;
+		linux,usable-memory = <0x0 0x1000000 0x0 0x7f000000>;
 	};
 
 	vddio_card: gpio-regulator {
diff --git a/arch/arm64/boot/dts/amlogic/meson-gxl-s905x-p212.dts b/arch/arm64/boot/dts/amlogic/meson-gxl-s905x-p212.dts
index 9639f01..908894c 100644
--- a/arch/arm64/boot/dts/amlogic/meson-gxl-s905x-p212.dts
+++ b/arch/arm64/boot/dts/amlogic/meson-gxl-s905x-p212.dts
@@ -59,7 +59,7 @@
 
 	memory at 0 {
 		device_type = "memory";
-		reg = <0x0 0x0 0x0 0x80000000>;
+		linux,usable-memory = <0x0 0x1000000 0x0 0x7f000000>;
 	};
 };
 
diff --git a/arch/arm64/boot/dts/amlogic/meson-gxm-nexbox-a1.dts b/arch/arm64/boot/dts/amlogic/meson-gxm-nexbox-a1.dts
index 5a337d3..2077385 100644
--- a/arch/arm64/boot/dts/amlogic/meson-gxm-nexbox-a1.dts
+++ b/arch/arm64/boot/dts/amlogic/meson-gxm-nexbox-a1.dts
@@ -62,7 +62,7 @@
 
 	memory at 0 {
 		device_type = "memory";
-		reg = <0x0 0x0 0x0 0x80000000>;
+		linux,usable-memory = <0x0 0x1000000 0x0 0x7f000000>;
 	};
 
 	vddio_boot: regulator-vddio-boot {
-- 
1.9.1

^ permalink raw reply related

* [PATCH 0/5] ARM: dts: boundary: remove hardcoded LVDS bus format
From: Shawn Guo @ 2016-12-30 13:37 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <20161202090843.22613-1-gary.bisson@boundarydevices.com>

On Fri, Dec 02, 2016 at 10:08:38AM +0100, Gary Bisson wrote:
> Gary Bisson (5):
>   ARM: dts: imx6qdl-nit6xlite: remove hardcoded LVDS bus format
>   ARM: dts: imx6qdl-nitrogen6_max: remove hardcoded LVDS bus format
>   ARM: dts: imx6qdl-nitrogen6_som2: remove hardcoded LVDS bus format
>   ARM: dts: imx6qdl-nitrogen6x: remove hardcoded LVDS bus format
>   ARM: dts: imx6qdl-sabrelite: remove hardcoded LVDS bus format

I squashed them and applied, thanks.

Shawn

^ permalink raw reply

* [PATCH v4 4/4] clk: samsung: exynos-audss: Use runtime PM
From: Marek Szyprowski @ 2016-12-30 13:33 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <1483104816-20885-1-git-send-email-m.szyprowski@samsung.com>

This patch adds support for runtime PM to Exynos Audio SubSystem driver to
enable full support for audio power domain on Exynos5 SoCs. The main change
is moving register saving and restoring code from system sleep PM ops to
runtime PM ops and implementing system sleep PM ops with generic
pm_runtime_force_suspend/resume helpers. Runtime PM of the Exynos AudSS
device is managed from clock core depending on the preparation status
of the provided clocks.

Signed-off-by: Marek Szyprowski <m.szyprowski@samsung.com>
---
This patch depends on "clk/samsung: exynos-audss: Replace syscore PM with
platform device PM" patch, which has been already queued for merging:
https://patchwork.kernel.org/patch/9447115/
---
 .../devicetree/bindings/clock/clk-exynos-audss.txt |  6 +++
 drivers/clk/samsung/clk-exynos-audss.c             | 62 +++++++++++++---------
 2 files changed, 43 insertions(+), 25 deletions(-)

diff --git a/Documentation/devicetree/bindings/clock/clk-exynos-audss.txt b/Documentation/devicetree/bindings/clock/clk-exynos-audss.txt
index 0c3d6015868d..f3635d5aeba4 100644
--- a/Documentation/devicetree/bindings/clock/clk-exynos-audss.txt
+++ b/Documentation/devicetree/bindings/clock/clk-exynos-audss.txt
@@ -33,6 +33,12 @@ Required Properties:
 - clock-names: Aliases for the above clocks. They should be "pll_ref",
   "pll_in", "cdclk", "sclk_audio", and "sclk_pcm_in" respectively.
 
+Optional Properties:
+
+  - power-domains: a phandle to respective power domain node as described by
+    generic PM domain bindings (see power/power_domain.txt for more
+    information).
+
 The following is the list of clocks generated by the controller. Each clock is
 assigned an identifier and client nodes use this identifier to specify the
 clock which they consume. Some of the clocks are available only on a particular
diff --git a/drivers/clk/samsung/clk-exynos-audss.c b/drivers/clk/samsung/clk-exynos-audss.c
index cb7df358a27d..25bb569bdd30 100644
--- a/drivers/clk/samsung/clk-exynos-audss.c
+++ b/drivers/clk/samsung/clk-exynos-audss.c
@@ -18,6 +18,7 @@
 #include <linux/syscore_ops.h>
 #include <linux/module.h>
 #include <linux/platform_device.h>
+#include <linux/pm_runtime.h>
 
 #include <dt-bindings/clock/exynos-audss-clk.h>
 
@@ -134,6 +135,7 @@ static int exynos_audss_clk_probe(struct platform_device *pdev)
 	struct clk *pll_ref, *pll_in, *cdclk, *sclk_audio, *sclk_pcm_in;
 	const struct exynos_audss_clk_drvdata *variant;
 	struct resource *res;
+	struct device *dev = &pdev->dev;
 	int i, ret = 0;
 
 	variant = of_device_get_match_data(&pdev->dev);
@@ -141,15 +143,15 @@ static int exynos_audss_clk_probe(struct platform_device *pdev)
 		return -EINVAL;
 
 	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
-	reg_base = devm_ioremap_resource(&pdev->dev, res);
+	reg_base = devm_ioremap_resource(dev, res);
 	if (IS_ERR(reg_base)) {
-		dev_err(&pdev->dev, "failed to map audss registers\n");
+		dev_err(dev, "failed to map audss registers\n");
 		return PTR_ERR(reg_base);
 	}
 
 	epll = ERR_PTR(-ENODEV);
 
-	clk_table = devm_kzalloc(&pdev->dev,
+	clk_table = devm_kzalloc(dev,
 				sizeof(struct clk *) * EXYNOS_AUDSS_MAX_CLKS,
 				GFP_KERNEL);
 	if (!clk_table)
@@ -158,8 +160,8 @@ static int exynos_audss_clk_probe(struct platform_device *pdev)
 	clk_data.clks = clk_table;
 	clk_data.clk_num = variant->num_clks;
 
-	pll_ref = devm_clk_get(&pdev->dev, "pll_ref");
-	pll_in = devm_clk_get(&pdev->dev, "pll_in");
+	pll_ref = devm_clk_get(dev, "pll_ref");
+	pll_in = devm_clk_get(dev, "pll_in");
 	if (!IS_ERR(pll_ref))
 		mout_audss_p[0] = __clk_get_name(pll_ref);
 	if (!IS_ERR(pll_in)) {
@@ -170,81 +172,89 @@ static int exynos_audss_clk_probe(struct platform_device *pdev)
 
 			ret = clk_prepare_enable(epll);
 			if (ret) {
-				dev_err(&pdev->dev,
+				dev_err(dev,
 					"failed to prepare the epll clock\n");
 				return ret;
 			}
 		}
 	}
-	clk_table[EXYNOS_MOUT_AUDSS] = clk_register_mux(NULL, "mout_audss",
+
+	/*
+	 * Enable runtime PM here, so clock core with use runtime PM for all
+	 * registered clocks.
+	 */
+	pm_runtime_set_active(dev);
+	pm_runtime_enable(dev);
+
+	clk_table[EXYNOS_MOUT_AUDSS] = clk_register_mux(dev, "mout_audss",
 				mout_audss_p, ARRAY_SIZE(mout_audss_p),
 				CLK_SET_RATE_NO_REPARENT,
 				reg_base + ASS_CLK_SRC, 0, 1, 0, &lock);
 
-	cdclk = devm_clk_get(&pdev->dev, "cdclk");
-	sclk_audio = devm_clk_get(&pdev->dev, "sclk_audio");
+	cdclk = devm_clk_get(dev, "cdclk");
+	sclk_audio = devm_clk_get(dev, "sclk_audio");
 	if (!IS_ERR(cdclk))
 		mout_i2s_p[1] = __clk_get_name(cdclk);
 	if (!IS_ERR(sclk_audio))
 		mout_i2s_p[2] = __clk_get_name(sclk_audio);
-	clk_table[EXYNOS_MOUT_I2S] = clk_register_mux(NULL, "mout_i2s",
+	clk_table[EXYNOS_MOUT_I2S] = clk_register_mux(dev, "mout_i2s",
 				mout_i2s_p, ARRAY_SIZE(mout_i2s_p),
 				CLK_SET_RATE_NO_REPARENT,
 				reg_base + ASS_CLK_SRC, 2, 2, 0, &lock);
 
-	clk_table[EXYNOS_DOUT_SRP] = clk_register_divider(NULL, "dout_srp",
+	clk_table[EXYNOS_DOUT_SRP] = clk_register_divider(dev, "dout_srp",
 				"mout_audss", 0, reg_base + ASS_CLK_DIV, 0, 4,
 				0, &lock);
 
-	clk_table[EXYNOS_DOUT_AUD_BUS] = clk_register_divider(NULL,
+	clk_table[EXYNOS_DOUT_AUD_BUS] = clk_register_divider(dev,
 				"dout_aud_bus", "dout_srp", 0,
 				reg_base + ASS_CLK_DIV, 4, 4, 0, &lock);
 
-	clk_table[EXYNOS_DOUT_I2S] = clk_register_divider(NULL, "dout_i2s",
+	clk_table[EXYNOS_DOUT_I2S] = clk_register_divider(dev, "dout_i2s",
 				"mout_i2s", 0, reg_base + ASS_CLK_DIV, 8, 4, 0,
 				&lock);
 
-	clk_table[EXYNOS_SRP_CLK] = clk_register_gate(NULL, "srp_clk",
+	clk_table[EXYNOS_SRP_CLK] = clk_register_gate(dev, "srp_clk",
 				"dout_srp", CLK_SET_RATE_PARENT,
 				reg_base + ASS_CLK_GATE, 0, 0, &lock);
 
-	clk_table[EXYNOS_I2S_BUS] = clk_register_gate(NULL, "i2s_bus",
+	clk_table[EXYNOS_I2S_BUS] = clk_register_gate(dev, "i2s_bus",
 				"dout_aud_bus", CLK_SET_RATE_PARENT,
 				reg_base + ASS_CLK_GATE, 2, 0, &lock);
 
-	clk_table[EXYNOS_SCLK_I2S] = clk_register_gate(NULL, "sclk_i2s",
+	clk_table[EXYNOS_SCLK_I2S] = clk_register_gate(dev, "sclk_i2s",
 				"dout_i2s", CLK_SET_RATE_PARENT,
 				reg_base + ASS_CLK_GATE, 3, 0, &lock);
 
-	clk_table[EXYNOS_PCM_BUS] = clk_register_gate(NULL, "pcm_bus",
+	clk_table[EXYNOS_PCM_BUS] = clk_register_gate(dev, "pcm_bus",
 				 "sclk_pcm", CLK_SET_RATE_PARENT,
 				reg_base + ASS_CLK_GATE, 4, 0, &lock);
 
-	sclk_pcm_in = devm_clk_get(&pdev->dev, "sclk_pcm_in");
+	sclk_pcm_in = devm_clk_get(dev, "sclk_pcm_in");
 	if (!IS_ERR(sclk_pcm_in))
 		sclk_pcm_p = __clk_get_name(sclk_pcm_in);
-	clk_table[EXYNOS_SCLK_PCM] = clk_register_gate(NULL, "sclk_pcm",
+	clk_table[EXYNOS_SCLK_PCM] = clk_register_gate(dev, "sclk_pcm",
 				sclk_pcm_p, CLK_SET_RATE_PARENT,
 				reg_base + ASS_CLK_GATE, 5, 0, &lock);
 
 	if (variant->has_adma_clk) {
-		clk_table[EXYNOS_ADMA] = clk_register_gate(NULL, "adma",
+		clk_table[EXYNOS_ADMA] = clk_register_gate(dev, "adma",
 				"dout_srp", CLK_SET_RATE_PARENT,
 				reg_base + ASS_CLK_GATE, 9, 0, &lock);
 	}
 
 	for (i = 0; i < clk_data.clk_num; i++) {
 		if (IS_ERR(clk_table[i])) {
-			dev_err(&pdev->dev, "failed to register clock %d\n", i);
+			dev_err(dev, "failed to register clock %d\n", i);
 			ret = PTR_ERR(clk_table[i]);
 			goto unregister;
 		}
 	}
 
-	ret = of_clk_add_provider(pdev->dev.of_node, of_clk_src_onecell_get,
+	ret = of_clk_add_provider(dev->of_node, of_clk_src_onecell_get,
 					&clk_data);
 	if (ret) {
-		dev_err(&pdev->dev, "failed to add clock provider\n");
+		dev_err(dev, "failed to add clock provider\n");
 		goto unregister;
 	}
 
@@ -272,8 +282,10 @@ static int exynos_audss_clk_remove(struct platform_device *pdev)
 }
 
 static const struct dev_pm_ops exynos_audss_clk_pm_ops = {
-	SET_LATE_SYSTEM_SLEEP_PM_OPS(exynos_audss_clk_suspend,
-				     exynos_audss_clk_resume)
+	SET_RUNTIME_PM_OPS(exynos_audss_clk_suspend, exynos_audss_clk_resume,
+			   NULL)
+	SET_LATE_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend,
+				     pm_runtime_force_resume)
 };
 
 static struct platform_driver exynos_audss_clk_driver = {
-- 
1.9.1

^ permalink raw reply related

* [PATCH v4 3/4] clk: samsung: exynos5433: Add runtime PM support
From: Marek Szyprowski @ 2016-12-30 13:33 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <1483104816-20885-1-git-send-email-m.szyprowski@samsung.com>

Add runtime pm support for all clock controller units (CMU), which belongs
to power domains and require special handling during on/off operations.
Typically special values has to be written to MUX registers to change
internal clocks parents to OSC clock before turning power off. During such
operation all clocks, which enters CMU has to be enabled to let MUX to
stabilize. Also for each CMU there is one special parent clock, which has
to be enabled all the time when any access to CMU registers is done.

This patch solves most of the mysterious external abort and freeze issues
caused by a lack of proper parent CMU clock enabled or incorrect turn off
procedure.

Signed-off-by: Marek Szyprowski <m.szyprowski@samsung.com>
---
 .../devicetree/bindings/clock/exynos5433-clock.txt |  16 +
 drivers/clk/samsung/clk-exynos5433.c               | 415 ++++++++++++++++-----
 drivers/clk/samsung/clk.h                          |   6 +
 3 files changed, 352 insertions(+), 85 deletions(-)

diff --git a/Documentation/devicetree/bindings/clock/exynos5433-clock.txt b/Documentation/devicetree/bindings/clock/exynos5433-clock.txt
index 1dc80f8811fe..5c7dd12e667a 100644
--- a/Documentation/devicetree/bindings/clock/exynos5433-clock.txt
+++ b/Documentation/devicetree/bindings/clock/exynos5433-clock.txt
@@ -168,6 +168,11 @@ Required Properties:
 		- aclk_cam1_400
 		- aclk_cam1_552
 
+Optional properties:
+  - power-domains: a phandle to respective power domain node as described by
+	generic PM domain bindings (see power/power_domain.txt for more
+	information).
+
 Each clock is assigned an identifier and client nodes can use this identifier
 to specify the clock which they consume.
 
@@ -270,6 +275,7 @@ Example 2: Examples of clock controller nodes are listed below.
 		clocks = <&xxti>,
 		       <&cmu_top CLK_ACLK_G2D_266>,
 		       <&cmu_top CLK_ACLK_G2D_400>;
+		power-domains = <&pd_g2d>;
 	};
 
 	cmu_disp: clock-controller at 13b90000 {
@@ -295,6 +301,7 @@ Example 2: Examples of clock controller nodes are listed below.
 		       <&cmu_mif CLK_SCLK_DECON_ECLK_DISP>,
 		       <&cmu_mif CLK_SCLK_DECON_TV_VCLK_DISP>,
 		       <&cmu_mif CLK_ACLK_DISP_333>;
+		power-domains = <&pd_disp>;
 	};
 
 	cmu_aud: clock-controller at 114c0000 {
@@ -304,6 +311,7 @@ Example 2: Examples of clock controller nodes are listed below.
 
 		clock-names = "oscclk", "fout_aud_pll";
 		clocks = <&xxti>, <&cmu_top CLK_FOUT_AUD_PLL>;
+		power-domains = <&pd_aud>;
 	};
 
 	cmu_bus0: clock-controller at 13600000 {
@@ -340,6 +348,7 @@ Example 2: Examples of clock controller nodes are listed below.
 
 		clock-names = "oscclk", "aclk_g3d_400";
 		clocks = <&xxti>, <&cmu_top CLK_ACLK_G3D_400>;
+		power-domains = <&pd_g3d>;
 	};
 
 	cmu_gscl: clock-controller at 13cf0000 {
@@ -353,6 +362,7 @@ Example 2: Examples of clock controller nodes are listed below.
 		clocks = <&xxti>,
 			<&cmu_top CLK_ACLK_GSCL_111>,
 			<&cmu_top CLK_ACLK_GSCL_333>;
+		power-domains = <&pd_gscl>;
 	};
 
 	cmu_apollo: clock-controller at 11900000 {
@@ -384,6 +394,7 @@ Example 2: Examples of clock controller nodes are listed below.
 		clocks = <&xxti>,
 		       <&cmu_top CLK_SCLK_JPEG_MSCL>,
 		       <&cmu_top CLK_ACLK_MSCL_400>;
+		power-domains = <&pd_mscl>;
 	};
 
 	cmu_mfc: clock-controller at 15280000 {
@@ -393,6 +404,7 @@ Example 2: Examples of clock controller nodes are listed below.
 
 		clock-names = "oscclk", "aclk_mfc_400";
 		clocks = <&xxti>, <&cmu_top CLK_ACLK_MFC_400>;
+		power-domains = <&pd_mfc>;
 	};
 
 	cmu_hevc: clock-controller at 14f80000 {
@@ -402,6 +414,7 @@ Example 2: Examples of clock controller nodes are listed below.
 
 		clock-names = "oscclk", "aclk_hevc_400";
 		clocks = <&xxti>, <&cmu_top CLK_ACLK_HEVC_400>;
+		power-domains = <&pd_hevc>;
 	};
 
 	cmu_isp: clock-controller at 146d0000 {
@@ -415,6 +428,7 @@ Example 2: Examples of clock controller nodes are listed below.
 		clocks = <&xxti>,
 		       <&cmu_top CLK_ACLK_ISP_DIS_400>,
 		       <&cmu_top CLK_ACLK_ISP_400>;
+		power-domains = <&pd_isp>;
 	};
 
 	cmu_cam0: clock-controller at 120d0000 {
@@ -430,6 +444,7 @@ Example 2: Examples of clock controller nodes are listed below.
 		       <&cmu_top CLK_ACLK_CAM0_333>,
 		       <&cmu_top CLK_ACLK_CAM0_400>,
 		       <&cmu_top CLK_ACLK_CAM0_552>;
+		power-domains = <&pd_cam0>;
 	};
 
 	cmu_cam1: clock-controller at 145d0000 {
@@ -451,6 +466,7 @@ Example 2: Examples of clock controller nodes are listed below.
 		       <&cmu_top CLK_ACLK_CAM1_333>,
 		       <&cmu_top CLK_ACLK_CAM1_400>,
 		       <&cmu_top CLK_ACLK_CAM1_552>;
+		power-domains = <&pd_cam1>;
 	};
 
 Example 3: UART controller node that consumes the clock generated by the clock
diff --git a/drivers/clk/samsung/clk-exynos5433.c b/drivers/clk/samsung/clk-exynos5433.c
index f096bd7df40c..9b12bc20c455 100644
--- a/drivers/clk/samsung/clk-exynos5433.c
+++ b/drivers/clk/samsung/clk-exynos5433.c
@@ -9,9 +9,14 @@
  * Common Clock Framework support for Exynos5443 SoC.
  */
 
+#include <linux/clk.h>
 #include <linux/clk-provider.h>
 #include <linux/of.h>
 #include <linux/of_address.h>
+#include <linux/of_device.h>
+#include <linux/platform_device.h>
+#include <linux/pm_runtime.h>
+#include <linux/slab.h>
 
 #include <dt-bindings/clock/exynos5433.h>
 
@@ -1989,6 +1994,14 @@ static void __init exynos5433_cmu_peris_init(struct device_node *np)
 	ENABLE_IP_FSYS1,
 };
 
+static const struct samsung_clk_reg_dump fsys_suspend_regs[] = {
+	{ MUX_SEL_FSYS0, 0 },
+	{ MUX_SEL_FSYS1, 0 },
+	{ MUX_SEL_FSYS2, 0 },
+	{ MUX_SEL_FSYS3, 0 },
+	{ MUX_SEL_FSYS4, 0 },
+};
+
 static const struct samsung_fixed_rate_clock fsys_fixed_clks[] __initconst = {
 	/* PHY clocks from USBDRD30_PHY */
 	FRATE(CLK_PHYCLK_USBDRD30_UDRD30_PHYCLOCK_PHY,
@@ -2294,16 +2307,11 @@ static void __init exynos5433_cmu_peris_init(struct device_node *np)
 	.nr_clk_ids		= FSYS_NR_CLK,
 	.clk_regs		= fsys_clk_regs,
 	.nr_clk_regs		= ARRAY_SIZE(fsys_clk_regs),
+	.suspend_regs		= fsys_suspend_regs,
+	.nr_suspend_regs	= ARRAY_SIZE(fsys_suspend_regs),
+	.clk_name		= "aclk_fsys_200",
 };
 
-static void __init exynos5433_cmu_fsys_init(struct device_node *np)
-{
-	samsung_cmu_register_one(np, &fsys_cmu_info);
-}
-
-CLK_OF_DECLARE(exynos5433_cmu_fsys, "samsung,exynos5433-cmu-fsys",
-		exynos5433_cmu_fsys_init);
-
 /*
  * Register offset definitions for CMU_G2D
  */
@@ -2333,6 +2341,10 @@ static void __init exynos5433_cmu_fsys_init(struct device_node *np)
 	DIV_ENABLE_IP_G2D_SECURE_SMMU_G2D,
 };
 
+static const struct samsung_clk_reg_dump g2d_suspend_regs[] = {
+	{ MUX_SEL_G2D0, 0 },
+};
+
 /* list of all parent clock list */
 PNAME(mout_aclk_g2d_266_user_p)		= { "oscclk", "aclk_g2d_266", };
 PNAME(mout_aclk_g2d_400_user_p)		= { "oscclk", "aclk_g2d_400", };
@@ -2418,16 +2430,11 @@ static void __init exynos5433_cmu_fsys_init(struct device_node *np)
 	.nr_clk_ids		= G2D_NR_CLK,
 	.clk_regs		= g2d_clk_regs,
 	.nr_clk_regs		= ARRAY_SIZE(g2d_clk_regs),
+	.suspend_regs		= g2d_suspend_regs,
+	.nr_suspend_regs	= ARRAY_SIZE(g2d_suspend_regs),
+	.clk_name		= "aclk_g2d_400",
 };
 
-static void __init exynos5433_cmu_g2d_init(struct device_node *np)
-{
-	samsung_cmu_register_one(np, &g2d_cmu_info);
-}
-
-CLK_OF_DECLARE(exynos5433_cmu_g2d, "samsung,exynos5433-cmu-g2d",
-		exynos5433_cmu_g2d_init);
-
 /*
  * Register offset definitions for CMU_DISP
  */
@@ -2492,6 +2499,16 @@ static void __init exynos5433_cmu_g2d_init(struct device_node *np)
 	CLKOUT_CMU_DISP_DIV_STAT,
 };
 
+static const struct samsung_clk_reg_dump disp_suspend_regs[] = {
+	/* ignore status of external PHY muxes during suspend to avoid hangs */
+	{ MUX_IGNORE_DISP2, 0x00111111 },
+	{ MUX_SEL_DISP0, 0 },
+	{ MUX_SEL_DISP1, 0 },
+	{ MUX_SEL_DISP2, 0 },
+	{ MUX_SEL_DISP3, 0 },
+	{ MUX_SEL_DISP4, 0 },
+};
+
 /* list of all parent clock list */
 PNAME(mout_disp_pll_p)			= { "oscclk", "fout_disp_pll", };
 PNAME(mout_sclk_dsim1_user_p)		= { "oscclk", "sclk_dsim1_disp", };
@@ -2837,16 +2854,11 @@ static void __init exynos5433_cmu_g2d_init(struct device_node *np)
 	.nr_clk_ids		= DISP_NR_CLK,
 	.clk_regs		= disp_clk_regs,
 	.nr_clk_regs		= ARRAY_SIZE(disp_clk_regs),
+	.suspend_regs		= disp_suspend_regs,
+	.nr_suspend_regs	= ARRAY_SIZE(disp_suspend_regs),
+	.clk_name		= "aclk_disp_333",
 };
 
-static void __init exynos5433_cmu_disp_init(struct device_node *np)
-{
-	samsung_cmu_register_one(np, &disp_cmu_info);
-}
-
-CLK_OF_DECLARE(exynos5433_cmu_disp, "samsung,exynos5433-cmu-disp",
-		exynos5433_cmu_disp_init);
-
 /*
  * Register offset definitions for CMU_AUD
  */
@@ -2881,6 +2893,11 @@ static void __init exynos5433_cmu_disp_init(struct device_node *np)
 	ENABLE_IP_AUD1,
 };
 
+static const struct samsung_clk_reg_dump aud_suspend_regs[] = {
+	{ MUX_SEL_AUD0, 0 },
+	{ MUX_SEL_AUD1, 0 },
+};
+
 /* list of all parent clock list */
 PNAME(mout_aud_pll_user_aud_p)	= { "oscclk", "fout_aud_pll", };
 PNAME(mout_sclk_aud_pcm_p)	= { "mout_aud_pll_user", "ioclk_audiocdclk0",};
@@ -3007,16 +3024,11 @@ static void __init exynos5433_cmu_disp_init(struct device_node *np)
 	.nr_clk_ids		= AUD_NR_CLK,
 	.clk_regs		= aud_clk_regs,
 	.nr_clk_regs		= ARRAY_SIZE(aud_clk_regs),
+	.suspend_regs		= aud_suspend_regs,
+	.nr_suspend_regs	= ARRAY_SIZE(aud_suspend_regs),
+	.clk_name		= "fout_aud_pll",
 };
 
-static void __init exynos5433_cmu_aud_init(struct device_node *np)
-{
-	samsung_cmu_register_one(np, &aud_cmu_info);
-}
-CLK_OF_DECLARE(exynos5433_cmu_aud, "samsung,exynos5433-cmu-aud",
-		exynos5433_cmu_aud_init);
-
-
 /*
  * Register offset definitions for CMU_BUS{0|1|2}
  */
@@ -3218,6 +3230,10 @@ static void __init exynos5433_cmu_aud_init(struct device_node *np)
 	CLK_STOPCTRL,
 };
 
+static const struct samsung_clk_reg_dump g3d_suspend_regs[] = {
+	{ MUX_SEL_G3D, 0 },
+};
+
 /* list of all parent clock list */
 PNAME(mout_aclk_g3d_400_p)	= { "mout_g3d_pll", "aclk_g3d_400", };
 PNAME(mout_g3d_pll_p)		= { "oscclk", "fout_g3d_pll", };
@@ -3291,15 +3307,11 @@ static void __init exynos5433_cmu_aud_init(struct device_node *np)
 	.nr_clk_ids		= G3D_NR_CLK,
 	.clk_regs		= g3d_clk_regs,
 	.nr_clk_regs		= ARRAY_SIZE(g3d_clk_regs),
+	.suspend_regs		= g3d_suspend_regs,
+	.nr_suspend_regs	= ARRAY_SIZE(g3d_suspend_regs),
+	.clk_name		= "aclk_g3d_400",
 };
 
-static void __init exynos5433_cmu_g3d_init(struct device_node *np)
-{
-	samsung_cmu_register_one(np, &g3d_cmu_info);
-}
-CLK_OF_DECLARE(exynos5433_cmu_g3d, "samsung,exynos5433-cmu-g3d",
-		exynos5433_cmu_g3d_init);
-
 /*
  * Register offset definitions for CMU_GSCL
  */
@@ -3338,6 +3350,12 @@ static void __init exynos5433_cmu_g3d_init(struct device_node *np)
 	ENABLE_IP_GSCL_SECURE_SMMU_GSCL2,
 };
 
+static const struct samsung_clk_reg_dump gscl_suspend_regs[] = {
+	{ MUX_SEL_GSCL, 0 },
+	{ ENABLE_ACLK_GSCL, 0xfff },
+	{ ENABLE_PCLK_GSCL, 0xff },
+};
+
 /* list of all parent clock list */
 PNAME(aclk_gscl_111_user_p)	= { "oscclk", "aclk_gscl_111", };
 PNAME(aclk_gscl_333_user_p)	= { "oscclk", "aclk_gscl_333", };
@@ -3432,15 +3450,11 @@ static void __init exynos5433_cmu_g3d_init(struct device_node *np)
 	.nr_clk_ids		= GSCL_NR_CLK,
 	.clk_regs		= gscl_clk_regs,
 	.nr_clk_regs		= ARRAY_SIZE(gscl_clk_regs),
+	.suspend_regs		= gscl_suspend_regs,
+	.nr_suspend_regs	= ARRAY_SIZE(gscl_suspend_regs),
+	.clk_name		= "aclk_gscl_111",
 };
 
-static void __init exynos5433_cmu_gscl_init(struct device_node *np)
-{
-	samsung_cmu_register_one(np, &gscl_cmu_info);
-}
-CLK_OF_DECLARE(exynos5433_cmu_gscl, "samsung,exynos5433-cmu-gscl",
-		exynos5433_cmu_gscl_init);
-
 /*
  * Register offset definitions for CMU_APOLLO
  */
@@ -3966,6 +3980,11 @@ static void __init exynos5433_cmu_atlas_init(struct device_node *np)
 	ENABLE_IP_MSCL_SECURE_SMMU_JPEG,
 };
 
+static const struct samsung_clk_reg_dump mscl_suspend_regs[] = {
+	{ MUX_SEL_MSCL0, 0 },
+	{ MUX_SEL_MSCL1, 0 },
+};
+
 /* list of all parent clock list */
 PNAME(mout_sclk_jpeg_user_p)		= { "oscclk", "sclk_jpeg_mscl", };
 PNAME(mout_aclk_mscl_400_user_p)	= { "oscclk", "aclk_mscl_400", };
@@ -4078,15 +4097,11 @@ static void __init exynos5433_cmu_atlas_init(struct device_node *np)
 	.nr_clk_ids		= MSCL_NR_CLK,
 	.clk_regs		= mscl_clk_regs,
 	.nr_clk_regs		= ARRAY_SIZE(mscl_clk_regs),
+	.suspend_regs		= mscl_suspend_regs,
+	.nr_suspend_regs	= ARRAY_SIZE(mscl_suspend_regs),
+	.clk_name		= "aclk_mscl_400",
 };
 
-static void __init exynos5433_cmu_mscl_init(struct device_node *np)
-{
-	samsung_cmu_register_one(np, &mscl_cmu_info);
-}
-CLK_OF_DECLARE(exynos5433_cmu_mscl, "samsung,exynos5433-cmu-mscl",
-		exynos5433_cmu_mscl_init);
-
 /*
  * Register offset definitions for CMU_MFC
  */
@@ -4116,6 +4131,10 @@ static void __init exynos5433_cmu_mscl_init(struct device_node *np)
 	ENABLE_IP_MFC_SECURE_SMMU_MFC,
 };
 
+static const struct samsung_clk_reg_dump mfc_suspend_regs[] = {
+	{ MUX_SEL_MFC, 0 },
+};
+
 PNAME(mout_aclk_mfc_400_user_p)		= { "oscclk", "aclk_mfc_400", };
 
 static const struct samsung_mux_clock mfc_mux_clks[] __initconst = {
@@ -4186,15 +4205,11 @@ static void __init exynos5433_cmu_mscl_init(struct device_node *np)
 	.nr_clk_ids		= MFC_NR_CLK,
 	.clk_regs		= mfc_clk_regs,
 	.nr_clk_regs		= ARRAY_SIZE(mfc_clk_regs),
+	.suspend_regs		= mfc_suspend_regs,
+	.nr_suspend_regs	= ARRAY_SIZE(mfc_suspend_regs),
+	.clk_name		= "aclk_mfc_400",
 };
 
-static void __init exynos5433_cmu_mfc_init(struct device_node *np)
-{
-	samsung_cmu_register_one(np, &mfc_cmu_info);
-}
-CLK_OF_DECLARE(exynos5433_cmu_mfc, "samsung,exynos5433-cmu-mfc",
-		exynos5433_cmu_mfc_init);
-
 /*
  * Register offset definitions for CMU_HEVC
  */
@@ -4224,6 +4239,10 @@ static void __init exynos5433_cmu_mfc_init(struct device_node *np)
 	ENABLE_IP_HEVC_SECURE_SMMU_HEVC,
 };
 
+static const struct samsung_clk_reg_dump hevc_suspend_regs[] = {
+	{ MUX_SEL_HEVC, 0 },
+};
+
 PNAME(mout_aclk_hevc_400_user_p)	= { "oscclk", "aclk_hevc_400", };
 
 static const struct samsung_mux_clock hevc_mux_clks[] __initconst = {
@@ -4296,15 +4315,11 @@ static void __init exynos5433_cmu_mfc_init(struct device_node *np)
 	.nr_clk_ids		= HEVC_NR_CLK,
 	.clk_regs		= hevc_clk_regs,
 	.nr_clk_regs		= ARRAY_SIZE(hevc_clk_regs),
+	.suspend_regs		= hevc_suspend_regs,
+	.nr_suspend_regs	= ARRAY_SIZE(hevc_suspend_regs),
+	.clk_name		= "aclk_hevc_400",
 };
 
-static void __init exynos5433_cmu_hevc_init(struct device_node *np)
-{
-	samsung_cmu_register_one(np, &hevc_cmu_info);
-}
-CLK_OF_DECLARE(exynos5433_cmu_hevc, "samsung,exynos5433-cmu-hevc",
-		exynos5433_cmu_hevc_init);
-
 /*
  * Register offset definitions for CMU_ISP
  */
@@ -4338,6 +4353,10 @@ static void __init exynos5433_cmu_hevc_init(struct device_node *np)
 	ENABLE_IP_ISP3,
 };
 
+static const struct samsung_clk_reg_dump isp_suspend_regs[] = {
+	{ MUX_SEL_ISP, 0 },
+};
+
 PNAME(mout_aclk_isp_dis_400_user_p)	= { "oscclk", "aclk_isp_dis_400", };
 PNAME(mout_aclk_isp_400_user_p)		= { "oscclk", "aclk_isp_400", };
 
@@ -4549,15 +4568,11 @@ static void __init exynos5433_cmu_hevc_init(struct device_node *np)
 	.nr_clk_ids		= ISP_NR_CLK,
 	.clk_regs		= isp_clk_regs,
 	.nr_clk_regs		= ARRAY_SIZE(isp_clk_regs),
+	.suspend_regs		= isp_suspend_regs,
+	.nr_suspend_regs	= ARRAY_SIZE(isp_suspend_regs),
+	.clk_name		= "aclk_isp_400",
 };
 
-static void __init exynos5433_cmu_isp_init(struct device_node *np)
-{
-	samsung_cmu_register_one(np, &isp_cmu_info);
-}
-CLK_OF_DECLARE(exynos5433_cmu_isp, "samsung,exynos5433-cmu-isp",
-		exynos5433_cmu_isp_init);
-
 /*
  * Register offset definitions for CMU_CAM0
  */
@@ -4621,6 +4636,15 @@ static void __init exynos5433_cmu_isp_init(struct device_node *np)
 	ENABLE_IP_CAM02,
 	ENABLE_IP_CAM03,
 };
+
+static const struct samsung_clk_reg_dump cam0_suspend_regs[] = {
+	{ MUX_SEL_CAM00, 0 },
+	{ MUX_SEL_CAM01, 0 },
+	{ MUX_SEL_CAM02, 0 },
+	{ MUX_SEL_CAM03, 0 },
+	{ MUX_SEL_CAM04, 0 },
+};
+
 PNAME(mout_aclk_cam0_333_user_p)	= { "oscclk", "aclk_cam0_333", };
 PNAME(mout_aclk_cam0_400_user_p)	= { "oscclk", "aclk_cam0_400", };
 PNAME(mout_aclk_cam0_552_user_p)	= { "oscclk", "aclk_cam0_552", };
@@ -5026,15 +5050,11 @@ static void __init exynos5433_cmu_isp_init(struct device_node *np)
 	.nr_clk_ids		= CAM0_NR_CLK,
 	.clk_regs		= cam0_clk_regs,
 	.nr_clk_regs		= ARRAY_SIZE(cam0_clk_regs),
+	.suspend_regs		= cam0_suspend_regs,
+	.nr_suspend_regs	= ARRAY_SIZE(cam0_suspend_regs),
+	.clk_name		= "aclk_cam0_400",
 };
 
-static void __init exynos5433_cmu_cam0_init(struct device_node *np)
-{
-	samsung_cmu_register_one(np, &cam0_cmu_info);
-}
-CLK_OF_DECLARE(exynos5433_cmu_cam0, "samsung,exynos5433-cmu-cam0",
-		exynos5433_cmu_cam0_init);
-
 /*
  * Register offset definitions for CMU_CAM1
  */
@@ -5081,6 +5101,12 @@ static void __init exynos5433_cmu_cam0_init(struct device_node *np)
 	ENABLE_IP_CAM12,
 };
 
+static const struct samsung_clk_reg_dump cam1_suspend_regs[] = {
+	{ MUX_SEL_CAM10, 0 },
+	{ MUX_SEL_CAM11, 0 },
+	{ MUX_SEL_CAM12, 0 },
+};
+
 PNAME(mout_sclk_isp_uart_user_p)	= { "oscclk", "sclk_isp_uart_cam1", };
 PNAME(mout_sclk_isp_spi1_user_p)	= { "oscclk", "sclk_isp_spi1_cam1", };
 PNAME(mout_sclk_isp_spi0_user_p)	= { "oscclk", "sclk_isp_spi0_cam1", };
@@ -5399,11 +5425,230 @@ static void __init exynos5433_cmu_cam0_init(struct device_node *np)
 	.nr_clk_ids		= CAM1_NR_CLK,
 	.clk_regs		= cam1_clk_regs,
 	.nr_clk_regs		= ARRAY_SIZE(cam1_clk_regs),
+	.suspend_regs		= cam1_suspend_regs,
+	.nr_suspend_regs	= ARRAY_SIZE(cam1_suspend_regs),
+	.clk_name		= "aclk_cam1_400",
+};
+
+
+struct exynos5433_cmu_data {
+	struct samsung_clk_provider ctx;
+
+	struct samsung_clk_reg_dump *clk_save;
+	unsigned int nr_clk_save;
+	const struct samsung_clk_reg_dump *clk_suspend;
+	unsigned int nr_clk_suspend;
+
+	struct clk *clk;
+	struct clk **pclks;
+	int nr_pclks;
+};
+
+static int exynos5433_cmu_suspend(struct device *dev)
+{
+	struct exynos5433_cmu_data *data = dev_get_drvdata(dev);
+	int i;
+
+	samsung_clk_save(data->ctx.reg_base, data->clk_save,
+			 data->nr_clk_save);
+
+	for (i = 0; i < data->nr_pclks; i++)
+		clk_enable(data->pclks[i]);
+
+	samsung_clk_restore(data->ctx.reg_base, data->clk_suspend,
+			    data->nr_clk_suspend);
+
+	for (i = 0; i < data->nr_pclks; i++)
+		clk_disable(data->pclks[i]);
+
+	clk_disable(data->clk);
+
+	return 0;
+}
+
+static int exynos5433_cmu_resume(struct device *dev)
+{
+	struct exynos5433_cmu_data *data = dev_get_drvdata(dev);
+	int i;
+
+	clk_enable(data->clk);
+
+	for (i = 0; i < data->nr_pclks; i++)
+		clk_enable(data->pclks[i]);
+
+	samsung_clk_restore(data->ctx.reg_base, data->clk_save,
+			    data->nr_clk_save);
+
+	for (i = 0; i < data->nr_pclks; i++)
+		clk_disable(data->pclks[i]);
+
+	return 0;
+}
+
+static int __init exynos5433_cmu_probe(struct platform_device *pdev)
+{
+	const struct samsung_cmu_info *info;
+	struct exynos5433_cmu_data *data;
+	struct samsung_clk_provider *ctx;
+	struct device *dev = &pdev->dev;
+	struct resource *res;
+	void __iomem *reg_base;
+	struct clk **clk_table;
+	int i;
+
+	info = of_device_get_match_data(dev);
+
+	data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL);
+	if (!data)
+		return -ENOMEM;
+	ctx = &data->ctx;
+
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	reg_base = devm_ioremap_resource(dev, res);
+	if (!reg_base) {
+		dev_err(dev, "failed to map registers\n");
+		return -ENOMEM;
+	}
+
+	clk_table = devm_kcalloc(dev, info->nr_clk_ids, sizeof(struct clk *),
+				 GFP_KERNEL);
+	if (!clk_table)
+		return -ENOMEM;
+
+	for (i = 0; i < info->nr_clk_ids; ++i)
+		clk_table[i] = ERR_PTR(-ENOENT);
+
+	ctx->clk_data.clks = clk_table;
+	ctx->clk_data.clk_num = info->nr_clk_ids;
+	ctx->reg_base = reg_base;
+	ctx->dev = dev;
+	spin_lock_init(&ctx->lock);
+
+	data->clk_save = samsung_clk_alloc_reg_dump(info->clk_regs,
+						    info->nr_clk_regs);
+	data->nr_clk_save = info->nr_clk_regs;
+	data->clk_suspend = info->suspend_regs;
+	data->nr_clk_suspend = info->nr_suspend_regs;
+	data->nr_pclks = of_count_phandle_with_args(dev->of_node, "clocks",
+						    "#clock-cells");
+	if (data->nr_pclks > 0) {
+		data->pclks = devm_kcalloc(dev, sizeof(struct clk *),
+					   data->nr_pclks, GFP_KERNEL);
+
+		for (i = 0; i < data->nr_pclks; i++) {
+			struct clk *clk = of_clk_get(dev->of_node, i);
+
+			if (IS_ERR(clk))
+				return PTR_ERR(clk);
+			data->pclks[i] = clk;
+		}
+	}
+
+	/*
+	 * Prepare all parent clocks here to avoid potential deadlock caused
+	 * by global clock "prepare lock" grabbed by runtime pm callbacks
+	 * from pm workers.
+	 */
+	for (i = 0; i < data->nr_pclks; i++)
+		clk_prepare(data->pclks[i]);
+
+	if (info->clk_name)
+		data->clk = clk_get(dev, info->clk_name);
+	clk_prepare_enable(data->clk);
+
+	platform_set_drvdata(pdev, data);
+
+	/*
+	 * Enable runtime pm here, so clock core with use runtime pm for all
+	 * registered clocks.
+	 */
+	pm_runtime_set_active(dev);
+	pm_runtime_enable(dev);
+
+	if (info->pll_clks)
+		samsung_clk_register_pll(ctx, info->pll_clks, info->nr_pll_clks,
+					 reg_base);
+	if (info->mux_clks)
+		samsung_clk_register_mux(ctx, info->mux_clks,
+					 info->nr_mux_clks);
+	if (info->div_clks)
+		samsung_clk_register_div(ctx, info->div_clks,
+					 info->nr_div_clks);
+	if (info->gate_clks)
+		samsung_clk_register_gate(ctx, info->gate_clks,
+					  info->nr_gate_clks);
+	if (info->fixed_clks)
+		samsung_clk_register_fixed_rate(ctx, info->fixed_clks,
+						info->nr_fixed_clks);
+	if (info->fixed_factor_clks)
+		samsung_clk_register_fixed_factor(ctx, info->fixed_factor_clks,
+						  info->nr_fixed_factor_clks);
+
+	samsung_clk_of_add_provider(dev->of_node, ctx);
+
+	return 0;
+}
+
+static const struct of_device_id exynos5433_cmu_of_match[] = {
+	{
+		.compatible = "samsung,exynos5433-cmu-aud",
+		.data = &aud_cmu_info,
+	}, {
+		.compatible = "samsung,exynos5433-cmu-cam0",
+		.data = &cam0_cmu_info,
+	}, {
+		.compatible = "samsung,exynos5433-cmu-cam1",
+		.data = &cam1_cmu_info,
+	}, {
+		.compatible = "samsung,exynos5433-cmu-disp",
+		.data = &disp_cmu_info,
+	}, {
+		.compatible = "samsung,exynos5433-cmu-g2d",
+		.data = &g2d_cmu_info,
+	}, {
+		.compatible = "samsung,exynos5433-cmu-g3d",
+		.data = &g3d_cmu_info,
+	}, {
+		.compatible = "samsung,exynos5433-cmu-fsys",
+		.data = &fsys_cmu_info,
+	}, {
+		.compatible = "samsung,exynos5433-cmu-gscl",
+		.data = &gscl_cmu_info,
+	}, {
+		.compatible = "samsung,exynos5433-cmu-mfc",
+		.data = &mfc_cmu_info,
+	}, {
+		.compatible = "samsung,exynos5433-cmu-hevc",
+		.data = &hevc_cmu_info,
+	}, {
+		.compatible = "samsung,exynos5433-cmu-isp",
+		.data = &isp_cmu_info,
+	}, {
+		.compatible = "samsung,exynos5433-cmu-mscl",
+		.data = &mscl_cmu_info,
+	}, {
+	},
+};
+
+static const struct dev_pm_ops exynos5433_cmu_pm_ops = {
+	SET_RUNTIME_PM_OPS(exynos5433_cmu_suspend, exynos5433_cmu_resume,
+			   NULL)
+	SET_LATE_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend,
+				     pm_runtime_force_resume)
+};
+
+static struct platform_driver exynos5433_cmu_driver __refdata = {
+	.driver	= {
+		.name = "exynos5433-cmu",
+		.of_match_table = exynos5433_cmu_of_match,
+		.suppress_bind_attrs = true,
+		.pm = &exynos5433_cmu_pm_ops,
+	},
+	.probe = exynos5433_cmu_probe,
 };
 
-static void __init exynos5433_cmu_cam1_init(struct device_node *np)
+static int __init exynos5433_cmu_init(void)
 {
-	samsung_cmu_register_one(np, &cam1_cmu_info);
+	return platform_driver_register(&exynos5433_cmu_driver);
 }
-CLK_OF_DECLARE(exynos5433_cmu_cam1, "samsung,exynos5433-cmu-cam1",
-		exynos5433_cmu_cam1_init);
+core_initcall(exynos5433_cmu_init);
diff --git a/drivers/clk/samsung/clk.h b/drivers/clk/samsung/clk.h
index 9263d8a27c6b..664020cb4794 100644
--- a/drivers/clk/samsung/clk.h
+++ b/drivers/clk/samsung/clk.h
@@ -354,6 +354,12 @@ struct samsung_cmu_info {
 	/* list and number of clocks registers */
 	const unsigned long *clk_regs;
 	unsigned int nr_clk_regs;
+
+	/* list and number of clocks registers to set before suspend */
+	const struct samsung_clk_reg_dump *suspend_regs;
+	unsigned int nr_suspend_regs;
+	/* name of the parent clock needed for CMU register access */
+	const char *clk_name;
 };
 
 extern struct samsung_clk_provider *__init samsung_clk_init(
-- 
1.9.1

^ permalink raw reply related

* [PATCH v4 2/4] clk: samsung: Add support for runtime PM
From: Marek Szyprowski @ 2016-12-30 13:33 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <1483104816-20885-1-git-send-email-m.szyprowski@samsung.com>

This patch adds struct device pointer to samsung_clk_provider and forwarding it
to clk_register_* functions, so drivers can register clocks, which use runtime
pm feature.

Signed-off-by: Marek Szyprowski <m.szyprowski@samsung.com>
---
 drivers/clk/samsung/clk-pll.c |  2 +-
 drivers/clk/samsung/clk.c     | 12 ++++++------
 drivers/clk/samsung/clk.h     |  1 +
 3 files changed, 8 insertions(+), 7 deletions(-)

diff --git a/drivers/clk/samsung/clk-pll.c b/drivers/clk/samsung/clk-pll.c
index 9617825daabb..f5249a7e545d 100644
--- a/drivers/clk/samsung/clk-pll.c
+++ b/drivers/clk/samsung/clk-pll.c
@@ -1345,7 +1345,7 @@ static void __init _samsung_clk_register_pll(struct samsung_clk_provider *ctx,
 	pll->lock_reg = base + pll_clk->lock_offset;
 	pll->con_reg = base + pll_clk->con_offset;
 
-	clk = clk_register(NULL, &pll->hw);
+	clk = clk_register(ctx->dev, &pll->hw);
 	if (IS_ERR(clk)) {
 		pr_err("%s: failed to register pll clock %s : %ld\n",
 			__func__, pll_clk->name, PTR_ERR(clk));
diff --git a/drivers/clk/samsung/clk.c b/drivers/clk/samsung/clk.c
index b7d87d6db9dc..e6923714f024 100644
--- a/drivers/clk/samsung/clk.c
+++ b/drivers/clk/samsung/clk.c
@@ -143,7 +143,7 @@ void __init samsung_clk_register_fixed_rate(struct samsung_clk_provider *ctx,
 	unsigned int idx, ret;
 
 	for (idx = 0; idx < nr_clk; idx++, list++) {
-		clk = clk_register_fixed_rate(NULL, list->name,
+		clk = clk_register_fixed_rate(ctx->dev, list->name,
 			list->parent_name, list->flags, list->fixed_rate);
 		if (IS_ERR(clk)) {
 			pr_err("%s: failed to register clock %s\n", __func__,
@@ -172,7 +172,7 @@ void __init samsung_clk_register_fixed_factor(struct samsung_clk_provider *ctx,
 	unsigned int idx;
 
 	for (idx = 0; idx < nr_clk; idx++, list++) {
-		clk = clk_register_fixed_factor(NULL, list->name,
+		clk = clk_register_fixed_factor(ctx->dev, list->name,
 			list->parent_name, list->flags, list->mult, list->div);
 		if (IS_ERR(clk)) {
 			pr_err("%s: failed to register clock %s\n", __func__,
@@ -193,7 +193,7 @@ void __init samsung_clk_register_mux(struct samsung_clk_provider *ctx,
 	unsigned int idx, ret;
 
 	for (idx = 0; idx < nr_clk; idx++, list++) {
-		clk = clk_register_mux(NULL, list->name, list->parent_names,
+		clk = clk_register_mux(ctx->dev, list->name, list->parent_names,
 			list->num_parents, list->flags,
 			ctx->reg_base + list->offset,
 			list->shift, list->width, list->mux_flags, &ctx->lock);
@@ -226,13 +226,13 @@ void __init samsung_clk_register_div(struct samsung_clk_provider *ctx,
 
 	for (idx = 0; idx < nr_clk; idx++, list++) {
 		if (list->table)
-			clk = clk_register_divider_table(NULL, list->name,
+			clk = clk_register_divider_table(ctx->dev, list->name,
 				list->parent_name, list->flags,
 				ctx->reg_base + list->offset,
 				list->shift, list->width, list->div_flags,
 				list->table, &ctx->lock);
 		else
-			clk = clk_register_divider(NULL, list->name,
+			clk = clk_register_divider(ctx->dev, list->name,
 				list->parent_name, list->flags,
 				ctx->reg_base + list->offset, list->shift,
 				list->width, list->div_flags, &ctx->lock);
@@ -264,7 +264,7 @@ void __init samsung_clk_register_gate(struct samsung_clk_provider *ctx,
 	unsigned int idx, ret;
 
 	for (idx = 0; idx < nr_clk; idx++, list++) {
-		clk = clk_register_gate(NULL, list->name, list->parent_name,
+		clk = clk_register_gate(ctx->dev, list->name, list->parent_name,
 				list->flags, ctx->reg_base + list->offset,
 				list->bit_idx, list->gate_flags, &ctx->lock);
 		if (IS_ERR(clk)) {
diff --git a/drivers/clk/samsung/clk.h b/drivers/clk/samsung/clk.h
index da3bdebabf1e..9263d8a27c6b 100644
--- a/drivers/clk/samsung/clk.h
+++ b/drivers/clk/samsung/clk.h
@@ -26,6 +26,7 @@
  */
 struct samsung_clk_provider {
 	void __iomem *reg_base;
+	struct device *dev;
 	struct clk_onecell_data clk_data;
 	spinlock_t lock;
 };
-- 
1.9.1

^ permalink raw reply related

* [PATCH v4 1/4] clk: Add support for runtime PM
From: Marek Szyprowski @ 2016-12-30 13:33 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <1483104816-20885-1-git-send-email-m.szyprowski@samsung.com>

Registers for some clocks might be located in the SOC area, which are under the
power domain. To enable access to those registers respective domain has to be
turned on. Additionally, registers for such clocks will usually loose its
contents when power domain is turned off, so additional saving and restoring of
them might be needed in the clock controller driver.

This patch adds basic infrastructure in the clocks core to allow implementing
driver for such clocks under power domains. Clock provider can supply a
struct device pointer, which is the used by clock core for tracking and managing
clock's controller runtime pm state. Each clk_prepare() operation
will first call pm_runtime_get_sync() on the supplied device, while
clk_unprepare() will do pm_runtime_put() at the end.

Additional calls to pm_runtime_get/put functions are required to ensure that any
register access (like calculating/changing clock rates and unpreparing/disabling
unused clocks on boot) will be done with clock controller in runtime resumend
state.

When one wants to register clock controller, which make use of this feature, he
has to:
1. Provide a struct device to the core when registering the provider.
2. Ensure to enable runtime PM for that device before registering clocks.
3. Make sure that the runtime PM status of the controller device reflects
   the HW state.

Signed-off-by: Marek Szyprowski <m.szyprowski@samsung.com>
---
 drivers/clk/clk.c | 111 +++++++++++++++++++++++++++++++++++++++++++++++++-----
 1 file changed, 101 insertions(+), 10 deletions(-)

diff --git a/drivers/clk/clk.c b/drivers/clk/clk.c
index 0fb39fe217d1..3cf202ccf5a9 100644
--- a/drivers/clk/clk.c
+++ b/drivers/clk/clk.c
@@ -21,6 +21,7 @@
 #include <linux/of.h>
 #include <linux/device.h>
 #include <linux/init.h>
+#include <linux/pm_runtime.h>
 #include <linux/sched.h>
 #include <linux/clkdev.h>
 
@@ -46,6 +47,7 @@ struct clk_core {
 	const struct clk_ops	*ops;
 	struct clk_hw		*hw;
 	struct module		*owner;
+	struct device		*dev;
 	struct clk_core		*parent;
 	const char		**parent_names;
 	struct clk_core		**parents;
@@ -87,6 +89,26 @@ struct clk {
 	struct hlist_node clks_node;
 };
 
+/***           runtime pm          ***/
+static int clk_pm_runtime_get(struct clk_core *core)
+{
+	int ret = 0;
+
+	if (!core->dev)
+		return 0;
+
+	ret = pm_runtime_get_sync(core->dev);
+	return ret < 0 ? ret : 0;
+}
+
+static void clk_pm_runtime_put(struct clk_core *core)
+{
+	if (!core->dev)
+		return;
+
+	pm_runtime_put(core->dev);
+}
+
 /***           locking             ***/
 static void clk_prepare_lock(void)
 {
@@ -150,6 +172,8 @@ static void clk_enable_unlock(unsigned long flags)
 
 static bool clk_core_is_prepared(struct clk_core *core)
 {
+	bool status;
+
 	/*
 	 * .is_prepared is optional for clocks that can prepare
 	 * fall back to software usage counter if it is missing
@@ -157,11 +181,20 @@ static bool clk_core_is_prepared(struct clk_core *core)
 	if (!core->ops->is_prepared)
 		return core->prepare_count;
 
-	return core->ops->is_prepared(core->hw);
+	if (clk_pm_runtime_get(core) == 0) {
+		status = core->ops->is_prepared(core->hw);
+		clk_pm_runtime_put(core);
+	} else {
+		status = false;
+	}
+
+	return status;
 }
 
 static bool clk_core_is_enabled(struct clk_core *core)
 {
+	bool status;
+
 	/*
 	 * .is_enabled is only mandatory for clocks that gate
 	 * fall back to software usage counter if .is_enabled is missing
@@ -169,7 +202,30 @@ static bool clk_core_is_enabled(struct clk_core *core)
 	if (!core->ops->is_enabled)
 		return core->enable_count;
 
-	return core->ops->is_enabled(core->hw);
+	/*
+	 * Check if clock controller's device is runtime active before
+	 * calling .is_enabled callback. If not, assume that clock is
+	 * disabled, because we might be called from atomic context, from
+	 * which pm_runtime_get() is not allowed.
+	 * This function is called mainly from clk_disable_unused_subtree,
+	 * which ensures proper runtime pm activation of controller before
+	 * taking enable spinlock, but the below check is needed if one tries
+	 * to call it from other places.
+	 */
+	if (core->dev) {
+		pm_runtime_get_noresume(core->dev);
+		if (!pm_runtime_active(core->dev)) {
+			status = false;
+			goto done;
+		}
+	}
+
+	status = core->ops->is_enabled(core->hw);
+done:
+	if (core->dev)
+		pm_runtime_put(core->dev);
+
+	return status;
 }
 
 /***    helper functions   ***/
@@ -489,6 +545,8 @@ static void clk_core_unprepare(struct clk_core *core)
 	if (core->ops->unprepare)
 		core->ops->unprepare(core->hw);
 
+	clk_pm_runtime_put(core);
+
 	trace_clk_unprepare_complete(core);
 	clk_core_unprepare(core->parent);
 }
@@ -530,10 +588,14 @@ static int clk_core_prepare(struct clk_core *core)
 		return 0;
 
 	if (core->prepare_count == 0) {
-		ret = clk_core_prepare(core->parent);
+		ret = clk_pm_runtime_get(core);
 		if (ret)
 			return ret;
 
+		ret = clk_core_prepare(core->parent);
+		if (ret)
+			goto runtime_put;
+
 		trace_clk_prepare(core);
 
 		if (core->ops->prepare)
@@ -541,15 +603,18 @@ static int clk_core_prepare(struct clk_core *core)
 
 		trace_clk_prepare_complete(core);
 
-		if (ret) {
-			clk_core_unprepare(core->parent);
-			return ret;
-		}
+		if (ret)
+			goto unprepare;
 	}
 
 	core->prepare_count++;
 
 	return 0;
+unprepare:
+	clk_core_unprepare(core->parent);
+runtime_put:
+	clk_pm_runtime_put(core);
+	return ret;
 }
 
 static int clk_core_prepare_lock(struct clk_core *core)
@@ -745,6 +810,9 @@ static void clk_unprepare_unused_subtree(struct clk_core *core)
 	if (core->flags & CLK_IGNORE_UNUSED)
 		return;
 
+	if (clk_pm_runtime_get(core))
+		return;
+
 	if (clk_core_is_prepared(core)) {
 		trace_clk_unprepare(core);
 		if (core->ops->unprepare_unused)
@@ -753,6 +821,8 @@ static void clk_unprepare_unused_subtree(struct clk_core *core)
 			core->ops->unprepare(core->hw);
 		trace_clk_unprepare_complete(core);
 	}
+
+	clk_pm_runtime_put(core);
 }
 
 static void clk_disable_unused_subtree(struct clk_core *core)
@@ -768,6 +838,9 @@ static void clk_disable_unused_subtree(struct clk_core *core)
 	if (core->flags & CLK_OPS_PARENT_ENABLE)
 		clk_core_prepare_enable(core->parent);
 
+	if (clk_pm_runtime_get(core))
+		goto unprepare_out;
+
 	flags = clk_enable_lock();
 
 	if (core->enable_count)
@@ -792,6 +865,8 @@ static void clk_disable_unused_subtree(struct clk_core *core)
 
 unlock_out:
 	clk_enable_unlock(flags);
+	clk_pm_runtime_put(core);
+unprepare_out:
 	if (core->flags & CLK_OPS_PARENT_ENABLE)
 		clk_core_disable_unprepare(core->parent);
 }
@@ -1563,6 +1638,7 @@ static int clk_core_set_rate_nolock(struct clk_core *core,
 {
 	struct clk_core *top, *fail_clk;
 	unsigned long rate = req_rate;
+	int ret = 0;
 
 	if (!core)
 		return 0;
@@ -1579,21 +1655,28 @@ static int clk_core_set_rate_nolock(struct clk_core *core,
 	if (!top)
 		return -EINVAL;
 
+	ret = clk_pm_runtime_get(core);
+	if (ret)
+		return ret;
+
 	/* notify that we are about to change rates */
 	fail_clk = clk_propagate_rate_change(top, PRE_RATE_CHANGE);
 	if (fail_clk) {
 		pr_debug("%s: failed to set %s rate\n", __func__,
 				fail_clk->name);
 		clk_propagate_rate_change(top, ABORT_RATE_CHANGE);
-		return -EBUSY;
+		ret = -EBUSY;
+		goto err;
 	}
 
 	/* change the rates */
 	clk_change_rate(top);
 
 	core->req_rate = req_rate;
+err:
+	clk_pm_runtime_put(core);
 
-	return 0;
+	return ret;
 }
 
 /**
@@ -1824,12 +1907,16 @@ static int clk_core_set_parent(struct clk_core *core, struct clk_core *parent)
 		p_rate = parent->rate;
 	}
 
+	ret = clk_pm_runtime_get(core);
+	if (ret)
+		goto out;
+
 	/* propagate PRE_RATE_CHANGE notifications */
 	ret = __clk_speculate_rates(core, p_rate);
 
 	/* abort if a driver objects */
 	if (ret & NOTIFY_STOP_MASK)
-		goto out;
+		goto runtime_put;
 
 	/* do the re-parent */
 	ret = __clk_set_parent(core, parent, p_index);
@@ -1842,6 +1929,8 @@ static int clk_core_set_parent(struct clk_core *core, struct clk_core *parent)
 		__clk_recalc_accuracies(core);
 	}
 
+runtime_put:
+	clk_pm_runtime_put(core);
 out:
 	clk_prepare_unlock();
 
@@ -2549,6 +2638,8 @@ struct clk *clk_register(struct device *dev, struct clk_hw *hw)
 		goto fail_name;
 	}
 	core->ops = hw->init->ops;
+	if (dev && pm_runtime_enabled(dev))
+		core->dev = dev;
 	if (dev && dev->driver)
 		core->owner = dev->driver->owner;
 	core->hw = hw;
-- 
1.9.1

^ permalink raw reply related


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