diff mbox

[02/11] pinctrl: mvebu: dove pinctrl driver

Message ID 1344689809-6223-3-git-send-email-sebastian.hesselbarth@gmail.com (mailing list archive)
State New, archived
Headers show

Commit Message

Sebastian Hesselbarth Aug. 11, 2012, 12:56 p.m. UTC
This patch adds a SoC specific pinctrl driver for Marvell Dove SoCs
plus DT binding documentation. This driver will use the mvebu pinctrl
driver core.

Signed-off-by: Sebastian Hesselbarth <sebastian.hesselbarth@gmail.com>
---
Cc: Grant Likely <grant.likely@secretlab.ca>
Cc: Rob Herring <rob.herring@calxeda.com>
Cc: Rob Landley <rob@landley.net>
Cc: Russell King <linux@arm.linux.org.uk>
Cc: Linus Walleij <linus.walleij@linaro.org>
Cc: Lior Amsalem <alior@marvell.com>
Cc: Andrew Lunn <andrew@lunn.ch>
Cc: Gregory CLEMENT <gregory.clement@free-electrons.com>
Cc: Ben Dooks <ben.dooks@codethink.co.uk>
Cc: Sebastian Hesselbarth <sebastian.hesselbarth@gmail.com>
Cc: Thomas Petazzoni <thomas.petazzoni@free-electrons.com>
Cc: devicetree-discuss@lists.ozlabs.org
Cc: linux-doc@vger.kernel.org
Cc: linux-kernel@vger.kernel.org
Cc: linux-arm-kernel@lists.infradead.org
---
 .../bindings/pinctrl/marvell,dove-pinctrl.txt      |   71 +++
 arch/arm/Kconfig                                   |    1 +
 drivers/pinctrl/Kconfig                            |    9 +-
 drivers/pinctrl/Makefile                           |    1 +
 drivers/pinctrl/pinctrl-dove.c                     |  622 ++++++++++++++++++++
 5 files changed, 703 insertions(+), 1 deletion(-)
 create mode 100644 Documentation/devicetree/bindings/pinctrl/marvell,dove-pinctrl.txt
 create mode 100644 drivers/pinctrl/pinctrl-dove.c

Comments

Linus Walleij Aug. 20, 2012, 1:43 p.m. UTC | #1
On Sat, Aug 11, 2012 at 2:56 PM, Sebastian Hesselbarth
<sebastian.hesselbarth@gmail.com> wrote:

(...)
> diff --git a/drivers/pinctrl/Kconfig b/drivers/pinctrl/Kconfig
> @@ -147,7 +147,7 @@ config PINCTRL_COH901
>
>  config PINCTRL_MVEBU
>         bool "Marvell SoC pin controller drivers"
> -       depends on ARCH_MVEBU
> +       depends on ARCH_MVEBU || ARCH_DOVE

Hmmmm.

Shouldn't this thing rather be:

depends on PLAT_ORION

else you end up lining up all Marvell machines here...

>         select PINMUX
>         select PINCONF
>         help
> @@ -155,6 +155,13 @@ config PINCTRL_MVEBU
>           This is only the driver core and additionally needs a SoC specific
>           driver.
>
> +config PINCTRL_DOVE
> +       bool "Support for Marvell Dove SoCs"
> +       depends on PINCTRL_MVEBU

Shouldn't this rather be:

depends on ARCH_DOVE
select PINCTRL_MVEBU

since you call down into the mvebu driver, it definatley needs
to select that.

(...)
> diff --git a/drivers/pinctrl/pinctrl-dove.c b/drivers/pinctrl/pinctrl-dove.c

> +#define  DOVE_AU0_AC97_SEL             (1 << 16)

It is way cleaner to say:

#include <linux/bitops.h>

#define  DOVE_AU0_AC97_SEL             BIT(16)

(...)
> +static int dove_pmu_mpp_ctrl_get(struct mvebu_mpp_ctrl *ctrl,
> +                                unsigned long *config)
> +{
> +       unsigned off = (ctrl->pid / 8) * 4;
> +       unsigned shift = (ctrl->pid % 8) * 4;
> +       unsigned long pmu = readl(DOVE_PMU_MPP_GENERAL_CTRL);
> +       unsigned long mpp = readl(DOVE_MPP_VIRT_BASE + off);
> +
> +       if (pmu & (1 << ctrl->pid))
> +               *config = 0x10;
> +       else
> +               *config = (mpp >> shift) & 0xf;
> +       return 0;
> +}

Same comments about the magic numbers as in the review of the first
patch.

> +static int dove_pmu_mpp_ctrl_set(struct mvebu_mpp_ctrl *ctrl,
> +                                unsigned long config)
> +{
> +       unsigned off = (ctrl->pid / 8) * 4;
> +       unsigned shift = (ctrl->pid % 8) * 4;
> +       unsigned long pmu = readl(DOVE_PMU_MPP_GENERAL_CTRL);
> +       unsigned long mpp = readl(DOVE_MPP_VIRT_BASE + off);
> +
> +       if (config == 0x10)
> +               writel(pmu | (1 << ctrl->pid), DOVE_PMU_MPP_GENERAL_CTRL);
> +       else {
> +               writel(pmu & ~(1 << ctrl->pid), DOVE_PMU_MPP_GENERAL_CTRL);
> +               mpp &= ~(0xf << shift);
> +               mpp |= config << shift;
> +               writel(mpp, DOVE_MPP_VIRT_BASE + off);
> +       }
> +       return 0;
> +}

Dito.

> +static int dove_mpp4_ctrl_get(struct mvebu_mpp_ctrl *ctrl,
> +                             unsigned long *config)
> +{
> +       unsigned long mpp4 = readl(DOVE_MPP_CTRL4_VIRT_BASE);
> +       unsigned long mask;
> +
> +       if (ctrl->pid == 24)
> +               mask = DOVE_CAM_GPIO_SEL;
> +       else if (ctrl->pid == 40)
> +               mask = DOVE_SD0_GPIO_SEL;
> +       else if (ctrl->pid == 46)
> +               mask = DOVE_SD1_GPIO_SEL;
> +       else if (ctrl->pid == 58)
> +               mask = DOVE_SPI_GPIO_SEL;
> +       else if (ctrl->pid == 62)
> +               mask = DOVE_UART1_GPIO_SEL;
> +       else
> +               return -EINVAL;

Magic numbers, atleast provide a comment on what is so special about
these pins, and can you use a switch() statement instead?

(...)
> +static int dove_mpp4_ctrl_set(struct mvebu_mpp_ctrl *ctrl,
> +                             unsigned long config)
> +{
> +       unsigned long mpp4 = readl(DOVE_MPP_CTRL4_VIRT_BASE);
> +       unsigned long mask;
> +
> +       if (ctrl->pid == 24)
> +               mask = DOVE_CAM_GPIO_SEL;
> +       else if (ctrl->pid == 40)
> +               mask = DOVE_SD0_GPIO_SEL;
> +       else if (ctrl->pid == 46)
> +               mask = DOVE_SD1_GPIO_SEL;
> +       else if (ctrl->pid == 58)
> +               mask = DOVE_SPI_GPIO_SEL;
> +       else if (ctrl->pid == 62)
> +               mask = DOVE_UART1_GPIO_SEL;
> +       else
> +               return -EINVAL;

Dito.

(...)
> +static int dove_audio1_ctrl_get(struct mvebu_mpp_ctrl *ctrl,
> +                               unsigned long *config)
> +{
> +       unsigned long mpp4 = readl(DOVE_MPP_CTRL4_VIRT_BASE);
> +       unsigned long sspc1 = readl(DOVE_SSP_CTRL_STATUS_1);
> +       unsigned long gmpp = readl(DOVE_MPP_GENERAL_VIRT_BASE);
> +       unsigned long gcfg2 = readl(DOVE_GLOBAL_CONFIG_2);
> +
> +       *config = 0;
> +       if (mpp4 & DOVE_AU1_GPIO_SEL)
> +               *config |= 0x8;
> +       if (sspc1 & DOVE_SSP_ON_AU1)
> +               *config |= 0x4;
> +       if (gmpp & DOVE_AU1_SPDIFO_GPIO_EN)
> +               *config |= 0x2;
> +       if (gcfg2 & DOVE_TWSI_OPTION3_GPIO)
> +               *config |= 0x1;

More magic, but the flags make it clear what the set
bits are all about, so OK.

But I would have:
#include <linux/bitops.h>

*config |= BIT(0);

etc.

> +       /* SSP/TWSI only if I2S1 not set*/
> +       if ((*config & 0x8) == 0)
> +               *config &= ~(0x4 | 0x1);
> +       /* TWSI only if SPDIFO not set*/
> +       if ((*config & 0x2) == 0)
> +               *config &= ~(0x1);
> +       return 0;

And that applies to these.

> +static int dove_audio1_ctrl_set(struct mvebu_mpp_ctrl *ctrl,
> +                               unsigned long config)
> +{
> +       unsigned long mpp4 = readl(DOVE_MPP_CTRL4_VIRT_BASE);
> +       unsigned long sspc1 = readl(DOVE_SSP_CTRL_STATUS_1);
> +       unsigned long gmpp = readl(DOVE_MPP_GENERAL_VIRT_BASE);
> +       unsigned long gcfg2 = readl(DOVE_GLOBAL_CONFIG_2);
> +
> +       if (config & 0x1)
> +               gcfg2 |= DOVE_TWSI_OPTION3_GPIO;
> +       if (config & 0x2)
> +               gmpp |= DOVE_AU1_SPDIFO_GPIO_EN;
> +       if (config & 0x4)
> +               sspc1 |= DOVE_SSP_ON_AU1;
> +       if (config & 0x8)
> +               mpp4 |= DOVE_AU1_GPIO_SEL;

Dito.

> +static int dove_audio1_ctrl_gpio_req(struct mvebu_mpp_ctrl *ctrl, u8 pid)
> +{
> +       unsigned long config;
> +
> +       dove_audio1_ctrl_get(ctrl, &config);
> +
> +       switch (config) {
> +       /* mode 0x00 i2s1/spdifo : no gpio */
> +       /* mode 0x0c ssp/spdifo  : no gpio */
> +       /* mode 0x0f ssp/twsi    : no gpio */
> +       case 0x00:
> +       case 0x0c:
> +       case 0x0f:
> +               return -ENOTSUPP;

Nice with the comments (also before the function),
now I understand what's happening, this is pretty OK,
I would have put the comments on the same
line as the magic number but whatever.

However you could just delete these lines and let them
fall through to -ENOTSUPP at the bottom.

> +
> +       /* mode 0x02 i2s1 : gpio[56:57] */
> +       /* mode 0x0e ssp  : gpio[56:57] */
> +       case 0x02:
> +       case 0x0e:
> +               if (pid >= 56)
> +                       return 0;
> +
> +       /* mode 0x08 spdifo : gpio[52:55] */
> +       /* mode 0x0b twsi   : gpio[52:55] */
> +       case 0x08:
> +       case 0x0b:
> +               if (pid <= 55)
> +                       return 0;
> +
> +       /* mode 0x0a/gpio : all gpio */
> +       case 0x0a:
> +               return 0;
> +       }
> +
> +       return -ENOTSUPP;
> +}

> +/* mpp[52:57] has gpio pins capable of in and out */
> +static int dove_audio1_ctrl_gpio_dir(struct mvebu_mpp_ctrl *ctrl, u8 pid,
> +                               bool input)
> +{
> +       return 0;
> +}

So should this not return a -ENOTSUPP for
pin < 52 || pin > 57?

(...)
> +static int __devinit dove_pinctrl_probe(struct platform_device *pdev)
> +{
> +       const struct of_device_id *match =
> +               of_match_device(dove_pinctrl_of_match, &pdev->dev);
> +       int ret;
> +
> +       if (match)
> +               pdev->dev.platform_data = match->data;
> +
> +       /*
> +        * General MPP Configuration Register is part of pdma registers.
> +        * grab clk to make sure it is clocked.
> +        */
> +       pdma_clk = clk_get_sys("dove-pdma", NULL);

First there is something really weird with the name of this
clock. Atleast call the variable holding the clk something
apropriate like just "clk".

Second why are you fetching a sys clock instead of using the clkdev
device binding in the clock tree so you could say:

clk = clk_get(&pdev->dev, NULL);

> +       clk_prepare_enable(pdma_clk);

Good!

> +       ret = mvebu_pinctrl_probe(pdev);
> +       if (!ret)
> +               return 0;
> +
> +       clk_disable_unprepare(pdma_clk);
> +       clk_put(pdma_clk);
> +       return ret;
> +}
> +
> +static int __devexit dove_pinctrl_remove(struct platform_device *pdev)
> +{
> +       int ret;
> +
> +       ret = mvebu_pinctrl_remove(pdev);
> +       if (!IS_ERR(pdma_clk)) {
> +               clk_disable_unprepare(pdma_clk);
> +               clk_put(pdma_clk);
> +       }

You don't need this (!IS_ERR()) clause, look in yout probe function:
you bail out if the clock is not found.

The rest looks good... atleast now, but I'm low on coffee FTM.

Yours,
Linus Walleij
Sebastian Hesselbarth Aug. 20, 2012, 2:43 p.m. UTC | #2
On 08/20/2012 03:43 PM, Linus Walleij wrote:
>>   config PINCTRL_MVEBU
>>          bool "Marvell SoC pin controller drivers"
>> -       depends on ARCH_MVEBU
>> +       depends on ARCH_MVEBU || ARCH_DOVE
>
> Hmmmm.
>
> Shouldn't this thing rather be:
> depends on PLAT_ORION
> else you end up lining up all Marvell machines here...
 > (...)
 >> +config PINCTRL_DOVE
 >> +       bool "Support for Marvell Dove SoCs"
 >> +       depends on PINCTRL_MVEBU
 >
 > Shouldn't this rather be:
 >
 > depends on ARCH_DOVE
 > select PINCTRL_MVEBU
 >
 > since you call down into the mvebu driver, it definatley needs
 > to select that.

Yes, this is a bit difficult but PLAT_ORION is basically all existing
Marvell SoCs of the Orion family. Armada 370/XP are two new SoCs
_and_ the approach to combine all into one mach-mvebu. The pinctrl
driver will only be made available to mach-mvebu as I am already close
to have at least mach-dove ported over.

After the review of Andrew, I already changed the Kconfig to have the 
structure you are proposing.

>> +#define  DOVE_AU0_AC97_SEL             (1<<  16)
>
> It is way cleaner to say:
> #include<linux/bitops.h>
> #define  DOVE_AU0_AC97_SEL             BIT(16)

Ok.

> Same comments about the magic numbers as in the review of the first
> patch.

I just agree with all of these comments and will work them in.

>> +       if (ctrl->pid == 24)
>> +               mask = DOVE_CAM_GPIO_SEL;
>> +       else if (ctrl->pid == 40)
>> +               mask = DOVE_SD0_GPIO_SEL;
>> +       else if (ctrl->pid == 46)
>> +               mask = DOVE_SD1_GPIO_SEL;
>> +       else if (ctrl->pid == 58)
>> +               mask = DOVE_SPI_GPIO_SEL;
>> +       else if (ctrl->pid == 62)
>> +               mask = DOVE_UART1_GPIO_SEL;
>> +       else
>> +               return -EINVAL;
>
> Magic numbers, atleast provide a comment on what is so special about
> these pins, and can you use a switch() statement instead?

Sure.

>> +static int dove_audio1_ctrl_gpio_req(struct mvebu_mpp_ctrl *ctrl, u8 pid)
>> +{
>> +       unsigned long config;
>> +
>> +       dove_audio1_ctrl_get(ctrl,&config);
>> +
>> +       switch (config) {
>> +       /* mode 0x00 i2s1/spdifo : no gpio */
>> +       /* mode 0x0c ssp/spdifo  : no gpio */
>> +       /* mode 0x0f ssp/twsi    : no gpio */
>> +       case 0x00:
>> +       case 0x0c:
>> +       case 0x0f:
>> +               return -ENOTSUPP;
>
> Nice with the comments (also before the function),
> now I understand what's happening, this is pretty OK,
> I would have put the comments on the same
> line as the magic number but whatever.
>
> However you could just delete these lines and let them
> fall through to -ENOTSUPP at the bottom.

Ok.

>> +/* mpp[52:57] has gpio pins capable of in and out */
>> +static int dove_audio1_ctrl_gpio_dir(struct mvebu_mpp_ctrl *ctrl, u8 pid,
>> +                               bool input)
>> +{
>> +       return 0;
>> +}
>
> So should this not return a -ENOTSUPP for
> pin<  52 || pin>  57?

Well, as pinctrl-mvebu and pinctrl core are already checking for pin
ranges, I think it's ok not to do it here again. But I don't have a
strong opinion about that.

>> +       /*
>> +        * General MPP Configuration Register is part of pdma registers.
>> +        * grab clk to make sure it is clocked.
>> +        */
>> +       pdma_clk = clk_get_sys("dove-pdma", NULL);
>
> First there is something really weird with the name of this
> clock. Atleast call the variable holding the clk something
> apropriate like just "clk".
>
> Second why are you fetching a sys clock instead of using the clkdev
> device binding in the clock tree so you could say:
>
> clk = clk_get(&pdev->dev, NULL);

pdma is the name of the clock gating control bit used in DS, IIRC it
stands for Peripheral DMA. Moreover, as I already mentioned it is now
using devm_clk_get. I think the time the pinctrl-dove driver patch was
posted there was no clock binding in 3.6.

>> +static int __devexit dove_pinctrl_remove(struct platform_device *pdev)
>> +{
>> +       int ret;
>> +
>> +       ret = mvebu_pinctrl_remove(pdev);
>> +       if (!IS_ERR(pdma_clk)) {
>> +               clk_disable_unprepare(pdma_clk);
>> +               clk_put(pdma_clk);
>> +       }
>
> You don't need this (!IS_ERR()) clause, look in yout probe function:
> you bail out if the clock is not found.

Agree.

Sebastian
Thomas Petazzoni Aug. 20, 2012, 5:16 p.m. UTC | #3
Le Mon, 20 Aug 2012 15:43:11 +0200,
Linus Walleij <linus.walleij@linaro.org> a écrit :

> >  config PINCTRL_MVEBU
> >         bool "Marvell SoC pin controller drivers"
> > -       depends on ARCH_MVEBU
> > +       depends on ARCH_MVEBU || ARCH_DOVE
> 
> Hmmmm.
> 
> Shouldn't this thing rather be:
> 
> depends on PLAT_ORION
> 
> else you end up lining up all Marvell machines here...

When we started working on this, ARCH_MVEBU was not a PLAT_ORION
platform, because PLAT_ORION includes its own custom pinmux code, gpio
code, etc, and what we're trying to achieve with Sebastian is to clean
all this. Ultimately, our goal is to have all Orion, Kirkwood, Dove,
Discovery and Armada 370/XP supported in mach-mvebu, and therefore
plat-orion would most likely disappear.

In the mean time, a patch got accepted that separates PLAT_ORION for
PLAT_ORION_LEGACY, and therefore MACH_MVEBU now selects PLAT_ORION to
access things that are still useful (address decoding code) while all
other platforms select PLAT_ORION_LEGACY to get the old pinmux code,
old gpio code, etc. All this is very much a work in progress.

Best regards,

Thomas
diff mbox

Patch

diff --git a/Documentation/devicetree/bindings/pinctrl/marvell,dove-pinctrl.txt b/Documentation/devicetree/bindings/pinctrl/marvell,dove-pinctrl.txt
new file mode 100644
index 0000000..9e6eea2
--- /dev/null
+++ b/Documentation/devicetree/bindings/pinctrl/marvell,dove-pinctrl.txt
@@ -0,0 +1,71 @@ 
+* Marvell Dove SoC pinctrl driver for mpp
+
+Please refer to marvell,mvebu-pinctrl.txt in this directory for common binding
+part and usage.
+
+Required properties:
+- compatible: "marvell,dove-pinctrl"
+
+Available mpp pins/groups and functions:
+Note: brackets (x) are not part of the mpp name for marvell,function and given
+only for more detailed description in this document.
+
+name          pins     functions
+================================================================================
+mpp0          0        gpio, pmu, uart2(rts), sdio0(cd), lcd0(pwm)
+mpp1          1        gpio, pmu, uart2(cts), sdio0(wp), lcd1(pwm)
+mpp2          2        gpio, pmu, uart2(txd), sdio0(buspwr), sata(prsnt),
+                       uart1(rts)
+mpp3          3        gpio, pmu, uart2(rxd), sdio0(ledctrl), sata(act),
+                       uart1(cts), lcd-spi(cs1)
+mpp4          4        gpio, pmu, uart3(rts), sdio1(cd), spi1(miso)
+mpp5          5        gpio, pmu, uart3(cts), sdio1(wp), spi1(cs)
+mpp6          6        gpio, pmu, uart3(txd), sdio1(buspwr), spi1(mosi)
+mpp7          7        gpio, pmu, uart3(rxd), sdio1(ledctrl), spi1(sck)
+mpp8          8        gpio, pmu, watchdog(rstout)
+mpp9          9        gpio, pmu, pex1(clkreq)
+mpp10         10       gpio, pmu, ssp(sclk)
+mpp11         11       gpio, pmu, sata(prsnt), sata-1(act), sdio0(ledctrl),
+                       sdio1(ledctrl), pex0(clkreq)
+mpp12         12       gpio, pmu, uart2(rts), audio0(extclk), sdio1(cd), sata(act)
+mpp13         13       gpio, pmu, uart2(cts), audio1(extclk), sdio1(wp),
+                       ssp(extclk)
+mpp14         14       gpio, pmu, uart2(txd), sdio1(buspwr), ssp(rxd)
+mpp15         15       gpio, pmu, uart2(rxd), sdio1(ledctrl), ssp(sfrm)
+mpp16         16       gpio, uart3(rts), sdio0(cd), ac97(sdi1), lcd-spi(cs1)
+mpp17         17       gpio, uart3(cts), sdio0(wp), ac97(sdi2), twsi(sda),
+                       ac97-1(sysclko)
+mpp18         18       gpio, uart3(txd), sdio0(buspwr), ac97(sdi3), lcd0(pwm)
+mpp19         19       gpio, uart3(rxd), sdio0(ledctrl), twsi(sck)
+mpp20         20       gpio, sdio0(cd), sdio1(cd), spi1(miso), lcd-spi(miso),
+                       ac97(sysclko)
+mpp21         21       gpio, sdio0(wp), sdio1(wp), spi1(cs), lcd-spi(cs0),
+                       uart1(cts), ssp(sfrm)
+mpp22         22       gpio, sdio0(buspwr), sdio1(buspwr), spi1(mosi),
+                       lcd-spi(mosi), uart1(cts), ssp(txd)
+mpp23         23       gpio, sdio0(ledctrl), sdio1(ledctrl), spi1(sck),
+                       lcd-spi(sck), ssp(sclk)
+mpp_camera    24-39    gpio, camera
+mpp_sdio0     40-45    gpio, sdio0
+mpp_sdio1     46-51    gpio, sdio1
+mpp_audio1    52-57    gpio, i2s1/spdifo, i2s1, spdifo, twsi, ssp/spdifo, ssp,
+                       ssp/twsi
+mpp_spi0      58-61    gpio, spi0
+mpp_uart1     62-63    gpio, uart1
+mpp_nand      64-71    gpo, nand
+audio0        -        i2s, ac97
+twsi          -        none, opt1, opt2, opt3
+
+Notes:
+* group "mpp_audio1" allows the following functions and gpio pins:
+  - gpio          : gpio on pins 52-57
+  - i2s1/spdifo   : audio1 i2s on pins 52-55 and spdifo on 57, no gpios
+  - i2s1          : audio1 i2s on pins 52-55, gpio on pins 56,57
+  - spdifo        : spdifo on pin 57, gpio on pins 52-55
+  - twsi          : twsi on pins 56,57, gpio on pins 52-55
+  - ssp/spdifo    : ssp on pins 52-55, spdifo on pin 57, no gpios
+  - ssp           : ssp on pins 52-55, gpio on pins 56,57
+  - ssp/twsi      : ssp on pins 52-55, twsi on pins 56,57, no gpios
+* group "audio0" internally muxes i2s0 or ac97 controller to the dedicated
+  audio0 pins.
+* group "twsi" internally muxes twsi controller to the dedicated or option pins.
diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig
index e91c7cd..cd3d827 100644
--- a/arch/arm/Kconfig
+++ b/arch/arm/Kconfig
@@ -584,6 +584,7 @@  config ARCH_DOVE
 	select GENERIC_CLOCKEVENTS
 	select NEED_MACH_IO_H
 	select PLAT_ORION
+	select PINCTRL
 	help
 	  Support for the Marvell Dove SoC 88AP510
 
diff --git a/drivers/pinctrl/Kconfig b/drivers/pinctrl/Kconfig
index b968335..e2427eb 100644
--- a/drivers/pinctrl/Kconfig
+++ b/drivers/pinctrl/Kconfig
@@ -147,7 +147,7 @@  config PINCTRL_COH901
 
 config PINCTRL_MVEBU
 	bool "Marvell SoC pin controller drivers"
-	depends on ARCH_MVEBU
+	depends on ARCH_MVEBU || ARCH_DOVE
 	select PINMUX
 	select PINCONF
 	help
@@ -155,6 +155,13 @@  config PINCTRL_MVEBU
 	  This is only the driver core and additionally needs a SoC specific
 	  driver.
 
+config PINCTRL_DOVE
+	bool "Support for Marvell Dove SoCs"
+	depends on PINCTRL_MVEBU
+	help
+	  Say yes here to support pinctrl driver on Marvell Dove SoCs
+	  (88AP510).
+
 source "drivers/pinctrl/spear/Kconfig"
 
 endmenu
diff --git a/drivers/pinctrl/Makefile b/drivers/pinctrl/Makefile
index 007ed32..d1327df 100644
--- a/drivers/pinctrl/Makefile
+++ b/drivers/pinctrl/Makefile
@@ -30,5 +30,6 @@  obj-$(CONFIG_PINCTRL_TEGRA30)	+= pinctrl-tegra30.o
 obj-$(CONFIG_PINCTRL_U300)	+= pinctrl-u300.o
 obj-$(CONFIG_PINCTRL_COH901)	+= pinctrl-coh901.o
 obj-$(CONFIG_PINCTRL_MVEBU)	+= pinctrl-mvebu.o
+obj-$(CONFIG_PINCTRL_DOVE)	+= pinctrl-dove.o
 
 obj-$(CONFIG_PLAT_SPEAR)	+= spear/
diff --git a/drivers/pinctrl/pinctrl-dove.c b/drivers/pinctrl/pinctrl-dove.c
new file mode 100644
index 0000000..b16de42
--- /dev/null
+++ b/drivers/pinctrl/pinctrl-dove.c
@@ -0,0 +1,622 @@ 
+/*
+ * Marvell Dove pinctrl driver based on mvebu pinctrl core
+ *
+ * Author: Sebastian Hesselbarth <sebastian.hesselbarth@gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#include <linux/err.h>
+#include <linux/init.h>
+#include <linux/io.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/clk.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+#include <linux/pinctrl/pinctrl.h>
+
+#include "pinctrl-mvebu.h"
+
+#define DOVE_SB_REGS_VIRT_BASE		0xfde00000
+#define DOVE_MPP_VIRT_BASE		(DOVE_SB_REGS_VIRT_BASE | 0xd0200)
+#define DOVE_PMU_MPP_GENERAL_CTRL	(DOVE_MPP_VIRT_BASE + 0x10)
+#define  DOVE_AU0_AC97_SEL		(1 << 16)
+#define DOVE_GLOBAL_CONFIG_1		(DOVE_SB_REGS_VIRT_BASE | 0xe802C)
+#define  DOVE_TWSI_ENABLE_OPTION1	(1 << 7)
+#define DOVE_GLOBAL_CONFIG_2		(DOVE_SB_REGS_VIRT_BASE | 0xe8030)
+#define  DOVE_TWSI_ENABLE_OPTION2	(1 << 20)
+#define  DOVE_TWSI_ENABLE_OPTION3	(1 << 21)
+#define  DOVE_TWSI_OPTION3_GPIO		(1 << 22)
+#define DOVE_SSP_CTRL_STATUS_1		(DOVE_SB_REGS_VIRT_BASE | 0xe8034)
+#define  DOVE_SSP_ON_AU1		(1 << 0)
+#define DOVE_MPP_GENERAL_VIRT_BASE	(DOVE_SB_REGS_VIRT_BASE | 0xe803c)
+#define  DOVE_AU1_SPDIFO_GPIO_EN	(1 << 1)
+#define  DOVE_NAND_GPIO_EN		(1 << 0)
+#define DOVE_GPIO_LO_VIRT_BASE		(DOVE_SB_REGS_VIRT_BASE | 0xd0400)
+#define DOVE_MPP_CTRL4_VIRT_BASE	(DOVE_GPIO_LO_VIRT_BASE + 0x40)
+#define  DOVE_SPI_GPIO_SEL		(1 << 5)
+#define  DOVE_UART1_GPIO_SEL		(1 << 4)
+#define  DOVE_AU1_GPIO_SEL		(1 << 3)
+#define  DOVE_CAM_GPIO_SEL		(1 << 2)
+#define  DOVE_SD1_GPIO_SEL		(1 << 1)
+#define  DOVE_SD0_GPIO_SEL		(1 << 0)
+
+static int dove_pmu_mpp_ctrl_get(struct mvebu_mpp_ctrl *ctrl,
+				 unsigned long *config)
+{
+	unsigned off = (ctrl->pid / 8) * 4;
+	unsigned shift = (ctrl->pid % 8) * 4;
+	unsigned long pmu = readl(DOVE_PMU_MPP_GENERAL_CTRL);
+	unsigned long mpp = readl(DOVE_MPP_VIRT_BASE + off);
+
+	if (pmu & (1 << ctrl->pid))
+		*config = 0x10;
+	else
+		*config = (mpp >> shift) & 0xf;
+	return 0;
+}
+
+static int dove_pmu_mpp_ctrl_set(struct mvebu_mpp_ctrl *ctrl,
+				 unsigned long config)
+{
+	unsigned off = (ctrl->pid / 8) * 4;
+	unsigned shift = (ctrl->pid % 8) * 4;
+	unsigned long pmu = readl(DOVE_PMU_MPP_GENERAL_CTRL);
+	unsigned long mpp = readl(DOVE_MPP_VIRT_BASE + off);
+
+	if (config == 0x10)
+		writel(pmu | (1 << ctrl->pid), DOVE_PMU_MPP_GENERAL_CTRL);
+	else {
+		writel(pmu & ~(1 << ctrl->pid), DOVE_PMU_MPP_GENERAL_CTRL);
+		mpp &= ~(0xf << shift);
+		mpp |= config << shift;
+		writel(mpp, DOVE_MPP_VIRT_BASE + off);
+	}
+	return 0;
+}
+
+static int dove_mpp4_ctrl_get(struct mvebu_mpp_ctrl *ctrl,
+			      unsigned long *config)
+{
+	unsigned long mpp4 = readl(DOVE_MPP_CTRL4_VIRT_BASE);
+	unsigned long mask;
+
+	if (ctrl->pid == 24)
+		mask = DOVE_CAM_GPIO_SEL;
+	else if (ctrl->pid == 40)
+		mask = DOVE_SD0_GPIO_SEL;
+	else if (ctrl->pid == 46)
+		mask = DOVE_SD1_GPIO_SEL;
+	else if (ctrl->pid == 58)
+		mask = DOVE_SPI_GPIO_SEL;
+	else if (ctrl->pid == 62)
+		mask = DOVE_UART1_GPIO_SEL;
+	else
+		return -EINVAL;
+
+	*config = ((mpp4 & mask) != 0);
+
+	return 0;
+}
+
+static int dove_mpp4_ctrl_set(struct mvebu_mpp_ctrl *ctrl,
+			      unsigned long config)
+{
+	unsigned long mpp4 = readl(DOVE_MPP_CTRL4_VIRT_BASE);
+	unsigned long mask;
+
+	if (ctrl->pid == 24)
+		mask = DOVE_CAM_GPIO_SEL;
+	else if (ctrl->pid == 40)
+		mask = DOVE_SD0_GPIO_SEL;
+	else if (ctrl->pid == 46)
+		mask = DOVE_SD1_GPIO_SEL;
+	else if (ctrl->pid == 58)
+		mask = DOVE_SPI_GPIO_SEL;
+	else if (ctrl->pid == 62)
+		mask = DOVE_UART1_GPIO_SEL;
+	else
+		return -EINVAL;
+
+	mpp4 &= ~mask;
+	if (config)
+		mpp4 |= mask;
+
+	writel(mpp4, DOVE_MPP_CTRL4_VIRT_BASE);
+
+	return 0;
+}
+
+static int dove_nand_ctrl_get(struct mvebu_mpp_ctrl *ctrl,
+			      unsigned long *config)
+{
+	unsigned long gmpp = readl(DOVE_MPP_GENERAL_VIRT_BASE);
+
+	*config = ((gmpp & DOVE_NAND_GPIO_EN) != 0);
+
+	return 0;
+}
+
+static int dove_nand_ctrl_set(struct mvebu_mpp_ctrl *ctrl,
+			      unsigned long config)
+{
+	unsigned long gmpp = readl(DOVE_MPP_GENERAL_VIRT_BASE);
+
+	gmpp &= ~DOVE_NAND_GPIO_EN;
+	if (config)
+		gmpp |= DOVE_NAND_GPIO_EN;
+
+	writel(gmpp, DOVE_MPP_GENERAL_VIRT_BASE);
+
+	return 0;
+}
+
+static int dove_audio0_ctrl_get(struct mvebu_mpp_ctrl *ctrl,
+				unsigned long *config)
+{
+	unsigned long pmu = readl(DOVE_PMU_MPP_GENERAL_CTRL);
+
+	*config = ((pmu & DOVE_AU0_AC97_SEL) != 0);
+
+	return 0;
+}
+
+static int dove_audio0_ctrl_set(struct mvebu_mpp_ctrl *ctrl,
+				unsigned long config)
+{
+	unsigned long pmu = readl(DOVE_PMU_MPP_GENERAL_CTRL);
+
+	pmu &= ~DOVE_AU0_AC97_SEL;
+	if (config)
+		pmu |= DOVE_AU0_AC97_SEL;
+	writel(pmu, DOVE_PMU_MPP_GENERAL_CTRL);
+
+	return 0;
+}
+
+static int dove_audio1_ctrl_get(struct mvebu_mpp_ctrl *ctrl,
+				unsigned long *config)
+{
+	unsigned long mpp4 = readl(DOVE_MPP_CTRL4_VIRT_BASE);
+	unsigned long sspc1 = readl(DOVE_SSP_CTRL_STATUS_1);
+	unsigned long gmpp = readl(DOVE_MPP_GENERAL_VIRT_BASE);
+	unsigned long gcfg2 = readl(DOVE_GLOBAL_CONFIG_2);
+
+	*config = 0;
+	if (mpp4 & DOVE_AU1_GPIO_SEL)
+		*config |= 0x8;
+	if (sspc1 & DOVE_SSP_ON_AU1)
+		*config |= 0x4;
+	if (gmpp & DOVE_AU1_SPDIFO_GPIO_EN)
+		*config |= 0x2;
+	if (gcfg2 & DOVE_TWSI_OPTION3_GPIO)
+		*config |= 0x1;
+
+	/* SSP/TWSI only if I2S1 not set*/
+	if ((*config & 0x8) == 0)
+		*config &= ~(0x4 | 0x1);
+	/* TWSI only if SPDIFO not set*/
+	if ((*config & 0x2) == 0)
+		*config &= ~(0x1);
+	return 0;
+}
+
+static int dove_audio1_ctrl_set(struct mvebu_mpp_ctrl *ctrl,
+				unsigned long config)
+{
+	unsigned long mpp4 = readl(DOVE_MPP_CTRL4_VIRT_BASE);
+	unsigned long sspc1 = readl(DOVE_SSP_CTRL_STATUS_1);
+	unsigned long gmpp = readl(DOVE_MPP_GENERAL_VIRT_BASE);
+	unsigned long gcfg2 = readl(DOVE_GLOBAL_CONFIG_2);
+
+	if (config & 0x1)
+		gcfg2 |= DOVE_TWSI_OPTION3_GPIO;
+	if (config & 0x2)
+		gmpp |= DOVE_AU1_SPDIFO_GPIO_EN;
+	if (config & 0x4)
+		sspc1 |= DOVE_SSP_ON_AU1;
+	if (config & 0x8)
+		mpp4 |= DOVE_AU1_GPIO_SEL;
+
+	writel(mpp4, DOVE_MPP_CTRL4_VIRT_BASE);
+	writel(sspc1, DOVE_SSP_CTRL_STATUS_1);
+	writel(gmpp, DOVE_MPP_GENERAL_VIRT_BASE);
+	writel(gcfg2, DOVE_GLOBAL_CONFIG_2);
+
+	return 0;
+}
+
+/* mpp[52:57] gpio pins depend heavily on current config;
+ * gpio_req does not try to mux in gpio capabilities to not
+ * break other functions. If you require all mpps as gpio
+ * enforce gpio setting by pinctrl mapping.
+ */
+static int dove_audio1_ctrl_gpio_req(struct mvebu_mpp_ctrl *ctrl, u8 pid)
+{
+	unsigned long config;
+
+	dove_audio1_ctrl_get(ctrl, &config);
+
+	switch (config) {
+	/* mode 0x00 i2s1/spdifo : no gpio */
+	/* mode 0x0c ssp/spdifo  : no gpio */
+	/* mode 0x0f ssp/twsi    : no gpio */
+	case 0x00:
+	case 0x0c:
+	case 0x0f:
+		return -ENOTSUPP;
+
+	/* mode 0x02 i2s1 : gpio[56:57] */
+	/* mode 0x0e ssp  : gpio[56:57] */
+	case 0x02:
+	case 0x0e:
+		if (pid >= 56)
+			return 0;
+
+	/* mode 0x08 spdifo : gpio[52:55] */
+	/* mode 0x0b twsi   : gpio[52:55] */
+	case 0x08:
+	case 0x0b:
+		if (pid <= 55)
+			return 0;
+
+	/* mode 0x0a/gpio : all gpio */
+	case 0x0a:
+		return 0;
+	}
+
+	return -ENOTSUPP;
+}
+
+/* mpp[52:57] has gpio pins capable of in and out */
+static int dove_audio1_ctrl_gpio_dir(struct mvebu_mpp_ctrl *ctrl, u8 pid,
+				bool input)
+{
+	return 0;
+}
+
+static int dove_twsi_ctrl_get(struct mvebu_mpp_ctrl *ctrl,
+			      unsigned long *config)
+{
+	unsigned long gcfg1 = readl(DOVE_GLOBAL_CONFIG_1);
+	unsigned long gcfg2 = readl(DOVE_GLOBAL_CONFIG_2);
+
+	*config = 0;
+	if (gcfg1 & DOVE_TWSI_ENABLE_OPTION1)
+		*config = 0x1;
+	else if (gcfg2 & DOVE_TWSI_ENABLE_OPTION2)
+		*config = 0x2;
+	else if (gcfg2 & DOVE_TWSI_ENABLE_OPTION3)
+		*config = 0x3;
+
+	return 0;
+}
+
+static int dove_twsi_ctrl_set(struct mvebu_mpp_ctrl *ctrl,
+				unsigned long config)
+{
+	unsigned long gcfg1 = readl(DOVE_GLOBAL_CONFIG_1);
+	unsigned long gcfg2 = readl(DOVE_GLOBAL_CONFIG_2);
+
+	gcfg1 &= ~DOVE_TWSI_ENABLE_OPTION1;
+	gcfg2 &= ~(DOVE_TWSI_ENABLE_OPTION2 | DOVE_TWSI_ENABLE_OPTION2);
+
+	switch (config) {
+	case 1:
+		gcfg1 |= DOVE_TWSI_ENABLE_OPTION1;
+		break;
+	case 2:
+		gcfg2 |= DOVE_TWSI_ENABLE_OPTION2;
+		break;
+	case 3:
+		gcfg2 |= DOVE_TWSI_ENABLE_OPTION3;
+		break;
+	}
+
+	writel(gcfg1, DOVE_GLOBAL_CONFIG_1);
+	writel(gcfg2, DOVE_GLOBAL_CONFIG_2);
+
+	return 0;
+}
+
+static struct mvebu_mpp_ctrl dove_mpp_controls[] = {
+	MPP_FUNC_CTRL(0, 0, "mpp0", dove_pmu_mpp_ctrl),
+	MPP_FUNC_CTRL(1, 1, "mpp1", dove_pmu_mpp_ctrl),
+	MPP_FUNC_CTRL(2, 2, "mpp2", dove_pmu_mpp_ctrl),
+	MPP_FUNC_CTRL(3, 3, "mpp3", dove_pmu_mpp_ctrl),
+	MPP_FUNC_CTRL(4, 4, "mpp4", dove_pmu_mpp_ctrl),
+	MPP_FUNC_CTRL(5, 5, "mpp5", dove_pmu_mpp_ctrl),
+	MPP_FUNC_CTRL(6, 6, "mpp6", dove_pmu_mpp_ctrl),
+	MPP_FUNC_CTRL(7, 7, "mpp7", dove_pmu_mpp_ctrl),
+	MPP_FUNC_CTRL(8, 8, "mpp8", dove_pmu_mpp_ctrl),
+	MPP_FUNC_CTRL(9, 9, "mpp9", dove_pmu_mpp_ctrl),
+	MPP_FUNC_CTRL(10, 10, "mpp10", dove_pmu_mpp_ctrl),
+	MPP_FUNC_CTRL(11, 11, "mpp11", dove_pmu_mpp_ctrl),
+	MPP_FUNC_CTRL(12, 12, "mpp12", dove_pmu_mpp_ctrl),
+	MPP_FUNC_CTRL(13, 13, "mpp13", dove_pmu_mpp_ctrl),
+	MPP_FUNC_CTRL(14, 14, "mpp14", dove_pmu_mpp_ctrl),
+	MPP_FUNC_CTRL(15, 15, "mpp15", dove_pmu_mpp_ctrl),
+	MPP_REG_CTRL(16, 23),
+	MPP_FUNC_CTRL(24, 39, "mpp_camera", dove_mpp4_ctrl),
+	MPP_FUNC_CTRL(40, 45, "mpp_sdio0", dove_mpp4_ctrl),
+	MPP_FUNC_CTRL(46, 51, "mpp_sdio1", dove_mpp4_ctrl),
+	MPP_FUNC_GPIO_CTRL(52, 57, "mpp_audio1", dove_audio1_ctrl),
+	MPP_FUNC_CTRL(58, 61, "mpp_spi0", dove_mpp4_ctrl),
+	MPP_FUNC_CTRL(62, 63, "mpp_uart1", dove_mpp4_ctrl),
+	MPP_FUNC_CTRL(64, 71, "mpp_nand", dove_nand_ctrl),
+	MPP_FUNC_CTRL(72, 72, "audio0", dove_audio0_ctrl),
+	MPP_FUNC_CTRL(73, 73, "twsi", dove_twsi_ctrl),
+};
+
+static struct mvebu_mpp_mode dove_mpp_modes[] = {
+	MPP_MODE(0,
+		MPP_FUNCTION(0x00, "gpio", NULL),
+		MPP_FUNCTION(0x02, "uart2", "rts"),
+		MPP_FUNCTION(0x03, "sdio0", "cd"),
+		MPP_FUNCTION(0x0f, "lcd0", "pwm"),
+		MPP_FUNCTION(0x10, "pmu", NULL)),
+	MPP_MODE(1,
+		MPP_FUNCTION(0x00, "gpio", NULL),
+		MPP_FUNCTION(0x02, "uart2", "cts"),
+		MPP_FUNCTION(0x03, "sdio0", "wp"),
+		MPP_FUNCTION(0x0f, "lcd1", "pwm"),
+		MPP_FUNCTION(0x10, "pmu", NULL)),
+	MPP_MODE(2,
+		MPP_FUNCTION(0x00, "gpio", NULL),
+		MPP_FUNCTION(0x01, "sata", "prsnt"),
+		MPP_FUNCTION(0x02, "uart2", "txd"),
+		MPP_FUNCTION(0x03, "sdio0", "buspwr"),
+		MPP_FUNCTION(0x04, "uart1", "rts"),
+		MPP_FUNCTION(0x10, "pmu", NULL)),
+	MPP_MODE(3,
+		MPP_FUNCTION(0x00, "gpio", NULL),
+		MPP_FUNCTION(0x01, "sata", "act"),
+		MPP_FUNCTION(0x02, "uart2", "rxd"),
+		MPP_FUNCTION(0x03, "sdio0", "ledctrl"),
+		MPP_FUNCTION(0x04, "uart1", "cts"),
+		MPP_FUNCTION(0x0f, "lcd-spi", "cs1"),
+		MPP_FUNCTION(0x10, "pmu", NULL)),
+	MPP_MODE(4,
+		MPP_FUNCTION(0x00, "gpio", NULL),
+		MPP_FUNCTION(0x02, "uart3", "rts"),
+		MPP_FUNCTION(0x03, "sdio1", "cd"),
+		MPP_FUNCTION(0x04, "spi1", "miso"),
+		MPP_FUNCTION(0x10, "pmu", NULL)),
+	MPP_MODE(5,
+		MPP_FUNCTION(0x00, "gpio", NULL),
+		MPP_FUNCTION(0x02, "uart3", "cts"),
+		MPP_FUNCTION(0x03, "sdio1", "wp"),
+		MPP_FUNCTION(0x04, "spi1", "cs"),
+		MPP_FUNCTION(0x10, "pmu", NULL)),
+	MPP_MODE(6,
+		MPP_FUNCTION(0x00, "gpio", NULL),
+		MPP_FUNCTION(0x02, "uart3", "txd"),
+		MPP_FUNCTION(0x03, "sdio1", "buspwr"),
+		MPP_FUNCTION(0x04, "spi1", "mosi"),
+		MPP_FUNCTION(0x10, "pmu", NULL)),
+	MPP_MODE(7,
+		MPP_FUNCTION(0x00, "gpio", NULL),
+		MPP_FUNCTION(0x02, "uart3", "rxd"),
+		MPP_FUNCTION(0x03, "sdio1", "ledctrl"),
+		MPP_FUNCTION(0x04, "spi1", "sck"),
+		MPP_FUNCTION(0x10, "pmu", NULL)),
+	MPP_MODE(8,
+		MPP_FUNCTION(0x00, "gpio", NULL),
+		MPP_FUNCTION(0x01, "watchdog", "rstout"),
+		MPP_FUNCTION(0x10, "pmu", NULL)),
+	MPP_MODE(9,
+		MPP_FUNCTION(0x00, "gpio", NULL),
+		MPP_FUNCTION(0x05, "pex1", "clkreq"),
+		MPP_FUNCTION(0x10, "pmu", NULL)),
+	MPP_MODE(10,
+		MPP_FUNCTION(0x00, "gpio", NULL),
+		MPP_FUNCTION(0x05, "ssp", "sclk"),
+		MPP_FUNCTION(0x10, "pmu", NULL)),
+	MPP_MODE(11,
+		MPP_FUNCTION(0x00, "gpio", NULL),
+		MPP_FUNCTION(0x01, "sata", "prsnt"),
+		MPP_FUNCTION(0x02, "sata-1", "act"),
+		MPP_FUNCTION(0x03, "sdio0", "ledctrl"),
+		MPP_FUNCTION(0x04, "sdio1", "ledctrl"),
+		MPP_FUNCTION(0x05, "pex0", "clkreq"),
+		MPP_FUNCTION(0x10, "pmu", NULL)),
+	MPP_MODE(12,
+		MPP_FUNCTION(0x00, "gpio", NULL),
+		MPP_FUNCTION(0x01, "sata", "act"),
+		MPP_FUNCTION(0x02, "uart2", "rts"),
+		MPP_FUNCTION(0x03, "audio0", "extclk"),
+		MPP_FUNCTION(0x04, "sdio1", "cd"),
+		MPP_FUNCTION(0x10, "pmu", NULL)),
+	MPP_MODE(13,
+		MPP_FUNCTION(0x00, "gpio", NULL),
+		MPP_FUNCTION(0x02, "uart2", "cts"),
+		MPP_FUNCTION(0x03, "audio1", "extclk"),
+		MPP_FUNCTION(0x04, "sdio1", "wp"),
+		MPP_FUNCTION(0x05, "ssp", "extclk"),
+		MPP_FUNCTION(0x10, "pmu", NULL)),
+	MPP_MODE(14,
+		MPP_FUNCTION(0x00, "gpio", NULL),
+		MPP_FUNCTION(0x02, "uart2", "txd"),
+		MPP_FUNCTION(0x04, "sdio1", "buspwr"),
+		MPP_FUNCTION(0x05, "ssp", "rxd"),
+		MPP_FUNCTION(0x10, "pmu", NULL)),
+	MPP_MODE(15,
+		MPP_FUNCTION(0x00, "gpio", NULL),
+		MPP_FUNCTION(0x02, "uart2", "rxd"),
+		MPP_FUNCTION(0x04, "sdio1", "ledctrl"),
+		MPP_FUNCTION(0x05, "ssp", "sfrm"),
+		MPP_FUNCTION(0x10, "pmu", NULL)),
+	MPP_MODE(16,
+		MPP_FUNCTION(0x00, "gpio", NULL),
+		MPP_FUNCTION(0x02, "uart3", "rts"),
+		MPP_FUNCTION(0x03, "sdio0", "cd"),
+		MPP_FUNCTION(0x04, "lcd-spi", "cs1"),
+		MPP_FUNCTION(0x05, "ac97", "sdi1")),
+	MPP_MODE(17,
+		MPP_FUNCTION(0x00, "gpio", NULL),
+		MPP_FUNCTION(0x01, "ac97-1", "sysclko"),
+		MPP_FUNCTION(0x02, "uart3", "cts"),
+		MPP_FUNCTION(0x03, "sdio0", "wp"),
+		MPP_FUNCTION(0x04, "twsi", "sda"),
+		MPP_FUNCTION(0x05, "ac97", "sdi2")),
+	MPP_MODE(18,
+		MPP_FUNCTION(0x00, "gpio", NULL),
+		MPP_FUNCTION(0x02, "uart3", "txd"),
+		MPP_FUNCTION(0x03, "sdio0", "buspwr"),
+		MPP_FUNCTION(0x04, "lcd0", "pwm"),
+		MPP_FUNCTION(0x05, "ac97", "sdi3")),
+	MPP_MODE(19,
+		MPP_FUNCTION(0x00, "gpio", NULL),
+		MPP_FUNCTION(0x02, "uart3", "rxd"),
+		MPP_FUNCTION(0x03, "sdio0", "ledctrl"),
+		MPP_FUNCTION(0x04, "twsi", "sck")),
+	MPP_MODE(20,
+		MPP_FUNCTION(0x00, "gpio", NULL),
+		MPP_FUNCTION(0x01, "ac97", "sysclko"),
+		MPP_FUNCTION(0x02, "lcd-spi", "miso"),
+		MPP_FUNCTION(0x03, "sdio1", "cd"),
+		MPP_FUNCTION(0x05, "sdio0", "cd"),
+		MPP_FUNCTION(0x06, "spi1", "miso")),
+	MPP_MODE(21,
+		MPP_FUNCTION(0x00, "gpio", NULL),
+		MPP_FUNCTION(0x01, "uart1", "rts"),
+		MPP_FUNCTION(0x02, "lcd-spi", "cs0"),
+		MPP_FUNCTION(0x03, "sdio1", "wp"),
+		MPP_FUNCTION(0x04, "ssp", "sfrm"),
+		MPP_FUNCTION(0x05, "sdio0", "wp"),
+		MPP_FUNCTION(0x06, "spi1", "cs")),
+	MPP_MODE(22,
+		MPP_FUNCTION(0x00, "gpio", NULL),
+		MPP_FUNCTION(0x01, "uart1", "cts"),
+		MPP_FUNCTION(0x02, "lcd-spi", "mosi"),
+		MPP_FUNCTION(0x03, "sdio1", "buspwr"),
+		MPP_FUNCTION(0x04, "ssp", "txd"),
+		MPP_FUNCTION(0x05, "sdio0", "buspwr"),
+		MPP_FUNCTION(0x06, "spi1", "mosi")),
+	MPP_MODE(23,
+		MPP_FUNCTION(0x00, "gpio", NULL),
+		MPP_FUNCTION(0x02, "lcd-spi", "sck"),
+		MPP_FUNCTION(0x03, "sdio1", "ledctrl"),
+		MPP_FUNCTION(0x04, "ssp", "sclk"),
+		MPP_FUNCTION(0x05, "sdio0", "ledctrl"),
+		MPP_FUNCTION(0x06, "spi1", "sck")),
+	MPP_MODE(24,
+		MPP_FUNCTION(0x00, "camera", NULL),
+		MPP_FUNCTION(0x01, "gpio", NULL)),
+	MPP_MODE(40,
+		MPP_FUNCTION(0x00, "sdio0", NULL),
+		MPP_FUNCTION(0x01, "gpio", NULL)),
+	MPP_MODE(46,
+		MPP_FUNCTION(0x00, "sdio1", NULL),
+		MPP_FUNCTION(0x01, "gpio", NULL)),
+	MPP_MODE(52,
+		MPP_FUNCTION(0x00, "i2s1/spdifo", NULL),
+		MPP_FUNCTION(0x02, "i2s1", NULL),
+		MPP_FUNCTION(0x08, "spdifo", NULL),
+		MPP_FUNCTION(0x0a, "gpio", NULL),
+		MPP_FUNCTION(0x0b, "twsi", NULL),
+		MPP_FUNCTION(0x0c, "ssp/spdifo", NULL),
+		MPP_FUNCTION(0x0e, "ssp", NULL),
+		MPP_FUNCTION(0x0f, "ssp/twsi", NULL)),
+	MPP_MODE(58,
+		MPP_FUNCTION(0x00, "spi0", NULL),
+		MPP_FUNCTION(0x01, "gpio", NULL)),
+	MPP_MODE(62,
+		MPP_FUNCTION(0x00, "uart1", NULL),
+		MPP_FUNCTION(0x01, "gpio", NULL)),
+	MPP_MODE(64,
+		MPP_FUNCTION(0x00, "nand", NULL),
+		MPP_FUNCTION(0x01, "gpo", NULL)),
+	MPP_MODE(72,
+		MPP_FUNCTION(0x00, "i2s", NULL),
+		MPP_FUNCTION(0x01, "ac97", NULL)),
+	MPP_MODE(73,
+		MPP_FUNCTION(0x00, "none", NULL),
+		MPP_FUNCTION(0x01, "opt1", NULL),
+		MPP_FUNCTION(0x02, "opt2", NULL),
+		MPP_FUNCTION(0x03, "opt3", NULL)),
+};
+
+static struct pinctrl_gpio_range dove_mpp_gpio_ranges[] = {
+	MPP_GPIO_RANGE(0,  0,  0, 32),
+	MPP_GPIO_RANGE(1, 32, 32, 32),
+	MPP_GPIO_RANGE(2, 64, 64,  8),
+};
+
+static struct mvebu_pinctrl_soc_info dove_pinctrl_info = {
+	.controls = dove_mpp_controls,
+	.ncontrols = ARRAY_SIZE(dove_mpp_controls),
+	.modes = dove_mpp_modes,
+	.nmodes = ARRAY_SIZE(dove_mpp_modes),
+	.gpioranges = dove_mpp_gpio_ranges,
+	.ngpioranges = ARRAY_SIZE(dove_mpp_gpio_ranges),
+	.variant = 0,
+};
+
+static struct clk *pdma_clk;
+
+static struct of_device_id dove_pinctrl_of_match[] __devinitdata = {
+	{ .compatible = "marvell,dove-pinctrl", .data = &dove_pinctrl_info },
+	{ }
+};
+
+static int __devinit dove_pinctrl_probe(struct platform_device *pdev)
+{
+	const struct of_device_id *match =
+		of_match_device(dove_pinctrl_of_match, &pdev->dev);
+	int ret;
+
+	if (match)
+		pdev->dev.platform_data = match->data;
+
+	/*
+	 * General MPP Configuration Register is part of pdma registers.
+	 * grab clk to make sure it is clocked.
+	 */
+	pdma_clk = clk_get_sys("dove-pdma", NULL);
+	if (IS_ERR(pdma_clk)) {
+		dev_err(&pdev->dev, "unable to get pdma clk\n");
+		return -ENODEV;
+	}
+
+	clk_prepare_enable(pdma_clk);
+	ret = mvebu_pinctrl_probe(pdev);
+	if (!ret)
+		return 0;
+
+	clk_disable_unprepare(pdma_clk);
+	clk_put(pdma_clk);
+	return ret;
+}
+
+static int __devexit dove_pinctrl_remove(struct platform_device *pdev)
+{
+	int ret;
+
+	ret = mvebu_pinctrl_remove(pdev);
+	if (!IS_ERR(pdma_clk)) {
+		clk_disable_unprepare(pdma_clk);
+		clk_put(pdma_clk);
+	}
+	return ret;
+}
+
+static struct platform_driver dove_pinctrl_driver = {
+	.driver = {
+		.name = "dove-pinctrl",
+		.owner = THIS_MODULE,
+		.of_match_table = of_match_ptr(dove_pinctrl_of_match),
+	},
+	.probe = dove_pinctrl_probe,
+	.remove = __devexit_p(dove_pinctrl_remove),
+};
+
+module_platform_driver(dove_pinctrl_driver);
+
+MODULE_AUTHOR("Sebastian Hesselbarth <sebastian.hesselbarth@gmail.com>");
+MODULE_DESCRIPTION("Marvell Dove pinctrl driver");
+MODULE_LICENSE("GPL v2");